1
Fork 0

rustc: keep overloaded autoderef MethodCallee's in Adjust.

This commit is contained in:
Eduard-Mihai Burtescu 2017-05-17 00:54:04 +03:00
parent 9a53c3e904
commit b4988f0792
26 changed files with 513 additions and 700 deletions

View file

@ -412,8 +412,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
pred: CFGIndex, pred: CFGIndex,
func_or_rcvr: &hir::Expr, func_or_rcvr: &hir::Expr,
args: I) -> CFGIndex { args: I) -> CFGIndex {
let method_call = ty::MethodCall::expr(call_expr.id); let fn_ty = match self.tables.method_map.get(&call_expr.id) {
let fn_ty = match self.tables.method_map.get(&method_call) {
Some(method) => method.ty, Some(method) => method.ty,
None => self.tables.expr_ty_adjusted(func_or_rcvr), None => self.tables.expr_ty_adjusted(func_or_rcvr),
}; };

View file

@ -102,7 +102,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a, 'tcx>> for ty::adjustment::Ad
ty::adjustment::Adjust::UnsafeFnPointer | ty::adjustment::Adjust::UnsafeFnPointer |
ty::adjustment::Adjust::ClosureFnPointer | ty::adjustment::Adjust::ClosureFnPointer |
ty::adjustment::Adjust::MutToConstPointer => {} ty::adjustment::Adjust::MutToConstPointer => {}
ty::adjustment::Adjust::DerefRef { autoderefs, ref autoref, unsize } => { ty::adjustment::Adjust::DerefRef { ref autoderefs, ref autoref, unsize } => {
autoderefs.hash_stable(hcx, hasher); autoderefs.hash_stable(hcx, hasher);
autoref.hash_stable(hcx, hasher); autoref.hash_stable(hcx, hasher);
unsize.hash_stable(hcx, hasher); unsize.hash_stable(hcx, hasher);
@ -112,7 +112,6 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a, 'tcx>> for ty::adjustment::Ad
} }
impl_stable_hash_for!(struct ty::adjustment::Adjustment<'tcx> { kind, target }); impl_stable_hash_for!(struct ty::adjustment::Adjustment<'tcx> { kind, target });
impl_stable_hash_for!(struct ty::MethodCall { expr_id, autoderef });
impl_stable_hash_for!(struct ty::MethodCallee<'tcx> { def_id, ty, substs }); impl_stable_hash_for!(struct ty::MethodCallee<'tcx> { def_id, ty, substs });
impl_stable_hash_for!(struct ty::UpvarId { var_id, closure_expr_id }); impl_stable_hash_for!(struct ty::UpvarId { var_id, closure_expr_id });
impl_stable_hash_for!(struct ty::UpvarBorrow<'tcx> { kind, region }); impl_stable_hash_for!(struct ty::UpvarBorrow<'tcx> { kind, region });
@ -626,17 +625,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a, 'tcx>> for ty::TypeckTables<'
ich::hash_stable_nodemap(hcx, hasher, node_types); ich::hash_stable_nodemap(hcx, hasher, node_types);
ich::hash_stable_nodemap(hcx, hasher, item_substs); ich::hash_stable_nodemap(hcx, hasher, item_substs);
ich::hash_stable_nodemap(hcx, hasher, adjustments); ich::hash_stable_nodemap(hcx, hasher, adjustments);
ich::hash_stable_nodemap(hcx, hasher, method_map);
ich::hash_stable_hashmap(hcx, hasher, method_map, |hcx, method_call| {
let ty::MethodCall {
expr_id,
autoderef
} = *method_call;
let def_id = hcx.tcx().hir.local_def_id(expr_id);
(hcx.def_path_hash(def_id), autoderef)
});
ich::hash_stable_hashmap(hcx, hasher, upvar_capture_map, |hcx, up_var_id| { ich::hash_stable_hashmap(hcx, hasher, upvar_capture_map, |hcx, up_var_id| {
let ty::UpvarId { let ty::UpvarId {
var_id, var_id,

View file

@ -564,13 +564,14 @@ impl<'tcx, T> InferOk<'tcx, T> {
} }
#[must_use = "once you start a snapshot, you should always consume it"] #[must_use = "once you start a snapshot, you should always consume it"]
pub struct CombinedSnapshot { pub struct CombinedSnapshot<'a, 'tcx:'a> {
projection_cache_snapshot: traits::ProjectionCacheSnapshot, projection_cache_snapshot: traits::ProjectionCacheSnapshot,
type_snapshot: type_variable::Snapshot, type_snapshot: type_variable::Snapshot,
int_snapshot: unify::Snapshot<ty::IntVid>, int_snapshot: unify::Snapshot<ty::IntVid>,
float_snapshot: unify::Snapshot<ty::FloatVid>, float_snapshot: unify::Snapshot<ty::FloatVid>,
region_vars_snapshot: RegionSnapshot, region_vars_snapshot: RegionSnapshot,
was_in_snapshot: bool, was_in_snapshot: bool,
_in_progress_tables: Option<Ref<'a, ty::TypeckTables<'tcx>>>,
} }
/// Helper trait for shortening the lifetimes inside a /// Helper trait for shortening the lifetimes inside a
@ -888,7 +889,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
result result
} }
fn start_snapshot(&self) -> CombinedSnapshot { fn start_snapshot<'b>(&'b self) -> CombinedSnapshot<'b, 'tcx> {
debug!("start_snapshot()"); debug!("start_snapshot()");
let in_snapshot = self.in_snapshot.get(); let in_snapshot = self.in_snapshot.get();
@ -901,6 +902,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
float_snapshot: self.float_unification_table.borrow_mut().snapshot(), float_snapshot: self.float_unification_table.borrow_mut().snapshot(),
region_vars_snapshot: self.region_vars.start_snapshot(), region_vars_snapshot: self.region_vars.start_snapshot(),
was_in_snapshot: in_snapshot, was_in_snapshot: in_snapshot,
// Borrow tables "in progress" (i.e. during typeck)
// to ban writes from within a snapshot to them.
_in_progress_tables: match self.tables {
InferTables::InProgress(ref tables) => tables.try_borrow().ok(),
_ => None
}
} }
} }
@ -911,7 +918,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
int_snapshot, int_snapshot,
float_snapshot, float_snapshot,
region_vars_snapshot, region_vars_snapshot,
was_in_snapshot } = snapshot; was_in_snapshot,
_in_progress_tables } = snapshot;
self.in_snapshot.set(was_in_snapshot); self.in_snapshot.set(was_in_snapshot);
@ -938,7 +946,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
int_snapshot, int_snapshot,
float_snapshot, float_snapshot,
region_vars_snapshot, region_vars_snapshot,
was_in_snapshot } = snapshot; was_in_snapshot,
_in_progress_tables } = snapshot;
self.in_snapshot.set(was_in_snapshot); self.in_snapshot.set(was_in_snapshot);
@ -1645,29 +1654,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
!traits::type_known_to_meet_bound(self, ty, copy_def_id, span) !traits::type_known_to_meet_bound(self, ty, copy_def_id, span)
} }
pub fn node_method_ty(&self, method_call: ty::MethodCall)
-> Option<Ty<'tcx>> {
self.tables
.borrow()
.method_map
.get(&method_call)
.map(|method| method.ty)
.map(|ty| self.resolve_type_vars_if_possible(&ty))
}
pub fn node_method_id(&self, method_call: ty::MethodCall)
-> Option<DefId> {
self.tables
.borrow()
.method_map
.get(&method_call)
.map(|method| method.def_id)
}
pub fn is_method_call(&self, id: ast::NodeId) -> bool {
self.tables.borrow().method_map.contains_key(&ty::MethodCall::expr(id))
}
pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarCapture<'tcx>> { pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarCapture<'tcx>> {
self.tables.borrow().upvar_capture_map.get(&upvar_id).cloned() self.tables.borrow().upvar_capture_map.get(&upvar_id).cloned()
} }

View file

@ -95,9 +95,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
} }
fn lookup_and_handle_method(&mut self, id: ast::NodeId) { fn lookup_and_handle_method(&mut self, id: ast::NodeId) {
let method_call = ty::MethodCall::expr(id); self.check_def_id(self.tables.method_map[&id].def_id);
let method = self.tables.method_map[&method_call];
self.check_def_id(method.def_id);
} }
fn handle_field_access(&mut self, lhs: &hir::Expr, name: ast::Name) { fn handle_field_access(&mut self, lhs: &hir::Expr, name: ast::Name) {

View file

@ -13,7 +13,6 @@
use self::RootUnsafeContext::*; use self::RootUnsafeContext::*;
use ty::{self, Ty, TyCtxt}; use ty::{self, Ty, TyCtxt};
use ty::MethodCall;
use lint; use lint;
use syntax::ast; use syntax::ast;
@ -174,8 +173,7 @@ impl<'a, 'tcx> Visitor<'tcx> for EffectCheckVisitor<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx hir::Expr) { fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
match expr.node { match expr.node {
hir::ExprMethodCall(..) => { hir::ExprMethodCall(..) => {
let method_call = MethodCall::expr(expr.id); let base_type = self.tables.method_map[&expr.id].ty;
let base_type = self.tables.method_map[&method_call].ty;
debug!("effect: method call case, base type is {:?}", debug!("effect: method call case, base type is {:?}",
base_type); base_type);
if type_is_unsafe_function(base_type) { if type_is_unsafe_function(base_type) {

View file

@ -563,19 +563,8 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
} }
ty::TyError => { } ty::TyError => { }
_ => { _ => {
let overloaded_call_type = let method = self.mc.infcx.tables.borrow().method_map[&call.id];
match self.mc.infcx.node_method_id(ty::MethodCall::expr(call.id)) { match OverloadedCallType::from_method_id(self.tcx(), method.def_id) {
Some(method_id) => {
OverloadedCallType::from_method_id(self.tcx(), method_id)
}
None => {
span_bug!(
callee.span,
"unexpected callee type {}",
callee_ty)
}
};
match overloaded_call_type {
FnMutOverloadedCall => { FnMutOverloadedCall => {
let call_scope_r = self.tcx().node_scope_region(call.id); let call_scope_r = self.tcx().node_scope_region(call.id);
self.borrow_expr(callee, self.borrow_expr(callee,
@ -717,7 +706,9 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
fn walk_adjustment(&mut self, expr: &hir::Expr) { fn walk_adjustment(&mut self, expr: &hir::Expr) {
let infcx = self.mc.infcx; let infcx = self.mc.infcx;
//NOTE(@jroesch): mixed RefCell borrow causes crash //NOTE(@jroesch): mixed RefCell borrow causes crash
let adj = infcx.tables.borrow().adjustments.get(&expr.id).map(|x| x.clone()); let adj = infcx.tables.borrow().adjustments.get(&expr.id).cloned();
let cmt_unadjusted =
return_if_err!(self.mc.cat_expr_unadjusted(expr));
if let Some(adjustment) = adj { if let Some(adjustment) = adj {
match adjustment.kind { match adjustment.kind {
adjustment::Adjust::NeverToAny | adjustment::Adjust::NeverToAny |
@ -728,17 +719,13 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
// Creating a closure/fn-pointer or unsizing consumes // Creating a closure/fn-pointer or unsizing consumes
// the input and stores it into the resulting rvalue. // the input and stores it into the resulting rvalue.
debug!("walk_adjustment: trivial adjustment"); debug!("walk_adjustment: trivial adjustment");
let cmt_unadjusted =
return_if_err!(self.mc.cat_expr_unadjusted(expr));
self.delegate_consume(expr.id, expr.span, cmt_unadjusted); self.delegate_consume(expr.id, expr.span, cmt_unadjusted);
} }
adjustment::Adjust::DerefRef { autoderefs, autoref, unsize } => { adjustment::Adjust::DerefRef { ref autoderefs, autoref, unsize } => {
debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment); debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment);
self.walk_autoderefs(expr, autoderefs);
let cmt_derefd = let cmt_derefd =
return_if_err!(self.mc.cat_expr_autoderefd(expr, autoderefs)); return_if_err!(self.walk_autoderefs(expr, cmt_unadjusted, autoderefs));
let cmt_refd = let cmt_refd =
self.walk_autoref(expr, cmt_derefd, autoref); self.walk_autoref(expr, cmt_derefd, autoref);
@ -757,30 +744,30 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
/// `deref()` is declared with `&self`, this is an autoref of `x`. /// `deref()` is declared with `&self`, this is an autoref of `x`.
fn walk_autoderefs(&mut self, fn walk_autoderefs(&mut self,
expr: &hir::Expr, expr: &hir::Expr,
autoderefs: usize) { mut cmt: mc::cmt<'tcx>,
debug!("walk_autoderefs expr={:?} autoderefs={}", expr, autoderefs); autoderefs: &[Option<ty::MethodCallee<'tcx>>])
-> mc::McResult<mc::cmt<'tcx>> {
for i in 0..autoderefs { debug!("walk_autoderefs expr={:?} autoderefs={:?}", expr, autoderefs);
let deref_id = ty::MethodCall::autoderef(expr.id, i as u32);
if let Some(method_ty) = self.mc.infcx.node_method_ty(deref_id) {
let cmt = return_if_err!(self.mc.cat_expr_autoderefd(expr, i));
for &overloaded in autoderefs {
if let Some(method) = overloaded {
// the method call infrastructure should have // the method call infrastructure should have
// replaced all late-bound regions with variables: // replaced all late-bound regions with variables:
let self_ty = method_ty.fn_sig().input(0); let self_ty = method.ty.fn_sig().input(0);
let self_ty = self.mc.infcx.resolve_type_vars_if_possible(&self_ty);
let self_ty = self.tcx().no_late_bound_regions(&self_ty).unwrap(); let self_ty = self.tcx().no_late_bound_regions(&self_ty).unwrap();
let (m, r) = match self_ty.sty { let (m, r) = match self_ty.sty {
ty::TyRef(r, ref m) => (m.mutbl, r), ty::TyRef(r, ref m) => (m.mutbl, r),
_ => span_bug!(expr.span, _ => span_bug!(expr.span, "bad overloaded deref type {:?}", self_ty)
"bad overloaded deref type {:?}",
method_ty)
}; };
let bk = ty::BorrowKind::from_mutbl(m); let bk = ty::BorrowKind::from_mutbl(m);
self.delegate.borrow(expr.id, expr.span, cmt, self.delegate.borrow(expr.id, expr.span, cmt.clone(),
r, bk, AutoRef); r, bk, AutoRef);
} }
cmt = self.mc.cat_deref(expr, cmt, overloaded)?;
} }
Ok(cmt)
} }
/// Walks the autoref `opt_autoref` applied to the autoderef'd /// Walks the autoref `opt_autoref` applied to the autoderef'd
@ -863,7 +850,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
pass_args: PassArgs) pass_args: PassArgs)
-> bool -> bool
{ {
if !self.mc.infcx.is_method_call(expr.id) { if !self.mc.infcx.tables.borrow().is_method_call(expr.id) {
return false; return false;
} }

View file

@ -1084,8 +1084,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
} }
hir::ExprMethodCall(.., ref args) => { hir::ExprMethodCall(.., ref args) => {
let method_call = ty::MethodCall::expr(expr.id); let method_ty = self.tables.method_map[&expr.id].ty;
let method_ty = self.tables.method_map[&method_call].ty;
// FIXME(canndrew): This is_never should really be an is_uninhabited // FIXME(canndrew): This is_never should really be an is_uninhabited
let succ = if method_ty.fn_ret().0.is_never() { let succ = if method_ty.fn_ret().0.is_never() {
self.s.exit_ln self.s.exit_ln

View file

@ -480,14 +480,21 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
} }
Some(adjustment) => { Some(adjustment) => {
debug!("cat_expr({:?}): {:?}", adjustment, expr);
match adjustment.kind { match adjustment.kind {
adjustment::Adjust::DerefRef { adjustment::Adjust::DerefRef {
autoderefs, ref autoderefs,
autoref: None, autoref: None,
unsize: false unsize: false
} => { } => {
// Equivalent to *expr or something similar. // Equivalent to *expr or something similar.
self.cat_expr_autoderefd(expr, autoderefs) let mut cmt = self.cat_expr_unadjusted(expr)?;
debug!("cat_expr: autoderefs={:?}, cmt={:?}",
autoderefs, cmt);
for &overloaded in autoderefs {
cmt = self.cat_deref(expr, cmt, overloaded)?;
}
return Ok(cmt);
} }
adjustment::Adjust::NeverToAny | adjustment::Adjust::NeverToAny |
@ -496,9 +503,6 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
adjustment::Adjust::ClosureFnPointer | adjustment::Adjust::ClosureFnPointer |
adjustment::Adjust::MutToConstPointer | adjustment::Adjust::MutToConstPointer |
adjustment::Adjust::DerefRef {..} => { adjustment::Adjust::DerefRef {..} => {
debug!("cat_expr({:?}): {:?}",
adjustment,
expr);
// Result is an rvalue. // Result is an rvalue.
let expr_ty = self.expr_ty_adjusted(expr)?; let expr_ty = self.expr_ty_adjusted(expr)?;
Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)) Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty))
@ -508,20 +512,6 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
} }
} }
pub fn cat_expr_autoderefd(&self,
expr: &hir::Expr,
autoderefs: usize)
-> McResult<cmt<'tcx>> {
let mut cmt = self.cat_expr_unadjusted(expr)?;
debug!("cat_expr_autoderefd: autoderefs={}, cmt={:?}",
autoderefs,
cmt);
for deref in 1..autoderefs + 1 {
cmt = self.cat_deref(expr, cmt, deref)?;
}
return Ok(cmt);
}
pub fn cat_expr_unadjusted(&self, expr: &hir::Expr) -> McResult<cmt<'tcx>> { pub fn cat_expr_unadjusted(&self, expr: &hir::Expr) -> McResult<cmt<'tcx>> {
debug!("cat_expr: id={} expr={:?}", expr.id, expr); debug!("cat_expr: id={} expr={:?}", expr.id, expr);
@ -529,7 +519,9 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
match expr.node { match expr.node {
hir::ExprUnary(hir::UnDeref, ref e_base) => { hir::ExprUnary(hir::UnDeref, ref e_base) => {
let base_cmt = self.cat_expr(&e_base)?; let base_cmt = self.cat_expr(&e_base)?;
self.cat_deref(expr, base_cmt, 0) let method = self.infcx.tables.borrow().method_map
.get(&expr.id).cloned();
self.cat_deref(expr, base_cmt, method)
} }
hir::ExprField(ref base, f_name) => { hir::ExprField(ref base, f_name) => {
@ -547,12 +539,12 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
} }
hir::ExprIndex(ref base, _) => { hir::ExprIndex(ref base, _) => {
let method_call = ty::MethodCall::expr(expr.id()); let method = self.infcx.tables.borrow().method_map.get(&expr.id()).cloned();
match self.infcx.node_method_ty(method_call) { match method {
Some(method_ty) => { Some(method) => {
// If this is an index implemented by a method call, then it // If this is an index implemented by a method call, then it
// will include an implicit deref of the result. // will include an implicit deref of the result.
let ret_ty = self.overloaded_method_return_ty(method_ty); let ret_ty = self.overloaded_method_return_ty(method);
// The index method always returns an `&T`, so // The index method always returns an `&T`, so
// dereference it to find the result type. // dereference it to find the result type.
@ -932,24 +924,16 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
ret ret
} }
fn cat_deref<N:ast_node>(&self, pub fn cat_deref<N:ast_node>(&self,
node: &N, node: &N,
base_cmt: cmt<'tcx>, base_cmt: cmt<'tcx>,
deref_cnt: usize) overloaded: Option<ty::MethodCallee<'tcx>>)
-> McResult<cmt<'tcx>> { -> McResult<cmt<'tcx>> {
let method_call = ty::MethodCall { debug!("cat_deref: overloaded={:?}", overloaded);
expr_id: node.id(),
autoderef: deref_cnt as u32
};
let method_ty = self.infcx.node_method_ty(method_call);
debug!("cat_deref: method_call={:?} method_ty={:?}", let base_cmt = match overloaded {
method_call, method_ty.map(|ty| ty)); Some(method) => {
let ref_ty = self.overloaded_method_return_ty(method);
let base_cmt = match method_ty {
Some(method_ty) => {
let ref_ty =
self.tcx().no_late_bound_regions(&method_ty.fn_ret()).unwrap();
self.cat_rvalue_node(node.id(), node.span(), ref_ty) self.cat_rvalue_node(node.id(), node.span(), ref_ty)
} }
None => base_cmt None => base_cmt
@ -1020,12 +1004,10 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
//! - `elt`: the AST node being indexed //! - `elt`: the AST node being indexed
//! - `base_cmt`: the cmt of `elt` //! - `base_cmt`: the cmt of `elt`
let method_call = ty::MethodCall::expr(elt.id()); let method = self.infcx.tables.borrow().method_map.get(&elt.id()).cloned();
let method_ty = self.infcx.node_method_ty(method_call); let (element_ty, element_kind) = match method {
Some(method) => {
let (element_ty, element_kind) = match method_ty { let ref_ty = self.overloaded_method_return_ty(method);
Some(method_ty) => {
let ref_ty = self.overloaded_method_return_ty(method_ty);
base_cmt = self.cat_rvalue_node(elt.id(), elt.span(), ref_ty); base_cmt = self.cat_rvalue_node(elt.id(), elt.span(), ref_ty);
(ref_ty.builtin_deref(false, ty::NoPreference).unwrap().ty, (ref_ty.builtin_deref(false, ty::NoPreference).unwrap().ty,
@ -1234,7 +1216,9 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
// box p1, &p1, &mut p1. we can ignore the mutability of // box p1, &p1, &mut p1. we can ignore the mutability of
// PatKind::Ref since that information is already contained // PatKind::Ref since that information is already contained
// in the type. // in the type.
let subcmt = self.cat_deref(pat, cmt, 0)?; let method = self.infcx.tables.borrow().method_map
.get(&pat.id).cloned();
let subcmt = self.cat_deref(pat, cmt, method)?;
self.cat_pattern_(subcmt, &subpat, op)?; self.cat_pattern_(subcmt, &subpat, op)?;
} }
@ -1262,7 +1246,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
} }
fn overloaded_method_return_ty(&self, fn overloaded_method_return_ty(&self,
method_ty: Ty<'tcx>) method: ty::MethodCallee<'tcx>)
-> Ty<'tcx> -> Ty<'tcx>
{ {
// When we process an overloaded `*` or `[]` etc, we often // When we process an overloaded `*` or `[]` etc, we often
@ -1270,8 +1254,9 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
// types are generated by method resolution and always have // types are generated by method resolution and always have
// all late-bound regions fully instantiated, so we just want // all late-bound regions fully instantiated, so we just want
// to skip past the binder. // to skip past the binder.
self.tcx().no_late_bound_regions(&method_ty.fn_ret()) let ret_ty = method.ty.fn_ret();
.unwrap() let ret_ty = self.infcx.resolve_type_vars_if_possible(&ret_ty);
self.tcx().no_late_bound_regions(&ret_ty).unwrap()
} }
} }

View file

@ -110,8 +110,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ReachableContext<'a, 'tcx> {
Some(self.tables.qpath_def(qpath, expr.id)) Some(self.tables.qpath_def(qpath, expr.id))
} }
hir::ExprMethodCall(..) => { hir::ExprMethodCall(..) => {
let method_call = ty::MethodCall::expr(expr.id); let def_id = self.tables.method_map[&expr.id].def_id;
let def_id = self.tables.method_map[&method_call].def_id;
Some(Def::Method(def_id)) Some(Def::Method(def_id))
} }
_ => None _ => None

View file

@ -9,10 +9,6 @@
// except according to those terms. // except according to those terms.
use ty::{self, Ty, TyCtxt, TypeAndMut}; use ty::{self, Ty, TyCtxt, TypeAndMut};
use ty::LvaluePreference::{NoPreference};
use syntax::ast;
use syntax_pos::Span;
use hir; use hir;
@ -43,10 +39,10 @@ pub enum Adjust<'tcx> {
/// here means either or both of raw vs borrowed vs unique and fat vs thin. /// here means either or both of raw vs borrowed vs unique and fat vs thin.
/// ///
/// We transform pointers by following the following steps in order: /// We transform pointers by following the following steps in order:
/// 1. Deref the pointer `self.autoderefs` times (may be 0). /// 1. Deref the pointer through `self.autoderefs` steps (may be no steps).
/// 2. If `autoref` is `Some(_)`, then take the address and produce either a /// 2. If `autoref` is `Some(_)`, then take the address and produce either a
/// `&` or `*` pointer. /// `&` or `*` pointer.
/// 3. If `unsize` is `Some(_)`, then apply the unsize transformation, /// 3. If `unsize` is `true`, then apply the unsize transformation,
/// which will do things like convert thin pointers to fat /// which will do things like convert thin pointers to fat
/// pointers, or convert structs containing thin pointers to /// pointers, or convert structs containing thin pointers to
/// structs containing fat pointers, or convert between fat /// structs containing fat pointers, or convert between fat
@ -61,23 +57,26 @@ pub enum Adjust<'tcx> {
/// 1. The simplest cases are where the pointer is not adjusted fat vs thin. /// 1. The simplest cases are where the pointer is not adjusted fat vs thin.
/// Here the pointer will be dereferenced N times (where a dereference can /// Here the pointer will be dereferenced N times (where a dereference can
/// happen to raw or borrowed pointers or any smart pointer which implements /// happen to raw or borrowed pointers or any smart pointer which implements
/// Deref, including Box<_>). The number of dereferences is given by /// Deref, including Box<_>). The types of dereferences is given by
/// `autoderefs`. It can then be auto-referenced zero or one times, indicated /// `autoderefs`. It can then be auto-referenced zero or one times, indicated
/// by `autoref`, to either a raw or borrowed pointer. In these cases unsize is /// by `autoref`, to either a raw or borrowed pointer. In these cases unsize is
/// None. /// `false`.
/// ///
/// 2. A thin-to-fat coercon involves unsizing the underlying data. We start /// 2. A thin-to-fat coercon involves unsizing the underlying data. We start
/// with a thin pointer, deref a number of times, unsize the underlying data, /// with a thin pointer, deref a number of times, unsize the underlying data,
/// then autoref. The 'unsize' phase may change a fixed length array to a /// then autoref. The 'unsize' phase may change a fixed length array to a
/// dynamically sized one, a concrete object to a trait object, or statically /// dynamically sized one, a concrete object to a trait object, or statically
/// sized struct to a dyncamically sized one. E.g., &[i32; 4] -> &[i32] is /// sized struct to a dynamically sized one. E.g., &[i32; 4] -> &[i32] is
/// represented by: /// represented by:
/// ///
/// ``` /// ```
/// Adjust::DerefRef { /// Adjustment {
/// autoderefs: 1, // &[i32; 4] -> [i32; 4] /// kind: Adjust::DerefRef {
/// autoref: Some(AutoBorrow::Ref), // [i32] -> &[i32] /// autoderefs: vec![None], // &[i32; 4] -> [i32; 4]
/// unsize: Some([i32]), // [i32; 4] -> [i32] /// autoref: Some(AutoBorrow::Ref), // [i32; 4] -> &[i32; 4]
/// unsize: true, // &[i32; 4] -> &[i32]
/// },
/// target: `[i32]`,
/// } /// }
/// ``` /// ```
/// ///
@ -95,15 +94,18 @@ pub enum Adjust<'tcx> {
/// Box<[i32]> is represented by: /// Box<[i32]> is represented by:
/// ///
/// ``` /// ```
/// Adjust::DerefRef { /// Adjustment {
/// autoderefs: 0, /// Adjust::DerefRef {
/// autoref: None, /// autoderefs: vec![],
/// unsize: Some(Box<[i32]>), /// autoref: None,
/// unsize: true,
/// },
/// target: `Box<[i32]>`,
/// } /// }
/// ``` /// ```
DerefRef { DerefRef {
/// Step 1. Apply a number of dereferences, producing an lvalue. /// Step 1. Apply a number of dereferences, producing an lvalue.
autoderefs: usize, autoderefs: Vec<Option<ty::MethodCallee<'tcx>>>,
/// Step 2. Optionally produce a pointer/reference from the value. /// Step 2. Optionally produce a pointer/reference from the value.
autoref: Option<AutoBorrow<'tcx>>, autoref: Option<AutoBorrow<'tcx>>,
@ -119,7 +121,11 @@ impl<'tcx> Adjustment<'tcx> {
match self.kind { match self.kind {
Adjust::NeverToAny => self.target.is_never(), Adjust::NeverToAny => self.target.is_never(),
Adjust::DerefRef { autoderefs: 0, autoref: None, unsize: false } => true, Adjust::DerefRef {
ref autoderefs,
autoref: None,
unsize: false
} if autoderefs.is_empty() => true,
Adjust::ReifyFnPointer | Adjust::ReifyFnPointer |
Adjust::UnsafeFnPointer | Adjust::UnsafeFnPointer |
@ -161,35 +167,6 @@ pub enum CustomCoerceUnsized {
} }
impl<'a, 'gcx, 'tcx> ty::TyS<'tcx> { impl<'a, 'gcx, 'tcx> ty::TyS<'tcx> {
pub fn adjust_for_autoderef<F>(&'tcx self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
expr_id: ast::NodeId,
expr_span: Span,
autoderef: u32, // how many autoderefs so far?
mut method_type: F)
-> Ty<'tcx> where
F: FnMut(ty::MethodCall) -> Option<Ty<'tcx>>,
{
let method_call = ty::MethodCall::autoderef(expr_id, autoderef);
let mut adjusted_ty = self;
if let Some(method_ty) = method_type(method_call) {
// Method calls always have all late-bound regions
// fully instantiated.
adjusted_ty = tcx.no_late_bound_regions(&method_ty.fn_ret()).unwrap();
}
match adjusted_ty.builtin_deref(true, NoPreference) {
Some(mt) => mt.ty,
None => {
span_bug!(
expr_span,
"the {}th autoderef for {} failed: {}",
autoderef,
expr_id,
adjusted_ty);
}
}
}
pub fn adjust_for_autoref(&'tcx self, tcx: TyCtxt<'a, 'gcx, 'tcx>, pub fn adjust_for_autoref(&'tcx self, tcx: TyCtxt<'a, 'gcx, 'tcx>,
autoref: Option<AutoBorrow<'tcx>>) autoref: Option<AutoBorrow<'tcx>>)
-> Ty<'tcx> { -> Ty<'tcx> {

View file

@ -222,7 +222,7 @@ pub struct TypeckTables<'tcx> {
pub adjustments: NodeMap<ty::adjustment::Adjustment<'tcx>>, pub adjustments: NodeMap<ty::adjustment::Adjustment<'tcx>>,
pub method_map: ty::MethodMap<'tcx>, pub method_map: NodeMap<ty::MethodCallee<'tcx>>,
/// Borrows /// Borrows
pub upvar_capture_map: ty::UpvarCaptureMap<'tcx>, pub upvar_capture_map: ty::UpvarCaptureMap<'tcx>,
@ -358,11 +358,7 @@ impl<'tcx> TypeckTables<'tcx> {
} }
pub fn is_method_call(&self, expr_id: NodeId) -> bool { pub fn is_method_call(&self, expr_id: NodeId) -> bool {
self.method_map.contains_key(&ty::MethodCall::expr(expr_id)) self.method_map.contains_key(&expr_id)
}
pub fn is_overloaded_autoderef(&self, expr_id: NodeId, autoderefs: u32) -> bool {
self.method_map.contains_key(&ty::MethodCall::autoderef(expr_id, autoderefs))
} }
pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarCapture<'tcx>> { pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarCapture<'tcx>> {

View file

@ -398,44 +398,6 @@ pub struct MethodCallee<'tcx> {
pub substs: &'tcx Substs<'tcx> pub substs: &'tcx Substs<'tcx>
} }
/// With method calls, we store some extra information in
/// side tables (i.e method_map). We use
/// MethodCall as a key to index into these tables instead of
/// just directly using the expression's NodeId. The reason
/// for this being that we may apply adjustments (coercions)
/// with the resulting expression also needing to use the
/// side tables. The problem with this is that we don't
/// assign a separate NodeId to this new expression
/// and so it would clash with the base expression if both
/// needed to add to the side tables. Thus to disambiguate
/// we also keep track of whether there's an adjustment in
/// our key.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
pub struct MethodCall {
pub expr_id: NodeId,
pub autoderef: u32
}
impl MethodCall {
pub fn expr(id: NodeId) -> MethodCall {
MethodCall {
expr_id: id,
autoderef: 0
}
}
pub fn autoderef(expr_id: NodeId, autoderef: u32) -> MethodCall {
MethodCall {
expr_id: expr_id,
autoderef: 1 + autoderef
}
}
}
// maps from an expression id that corresponds to a method call to the details
// of the method to be invoked
pub type MethodMap<'tcx> = FxHashMap<MethodCall, MethodCallee<'tcx>>;
// Contains information needed to resolve types and (in the future) look up // Contains information needed to resolve types and (in the future) look up
// the types of AST nodes. // the types of AST nodes.
#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[derive(Copy, Clone, PartialEq, Eq, Hash)]

View file

@ -882,19 +882,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion {
use rustc::ty::adjustment::*; use rustc::ty::adjustment::*;
// Check for method calls and overloaded operators. // Check for method calls and overloaded operators.
let opt_m = cx.tables.method_map.get(&ty::MethodCall::expr(id)).cloned(); if let Some(m) = cx.tables.method_map.get(&id).cloned() {
if let Some(m) = opt_m {
if method_call_refers_to_method(cx.tcx, method, m.def_id, m.substs, id) { if method_call_refers_to_method(cx.tcx, method, m.def_id, m.substs, id) {
return true; return true;
} }
} }
// Check for overloaded autoderef method calls. // Check for overloaded autoderef method calls.
let opt_adj = cx.tables.adjustments.get(&id).cloned(); if let Some(Adjustment {
if let Some(Adjustment { kind: Adjust::DerefRef { autoderefs, .. }, .. }) = opt_adj { kind: Adjust::DerefRef { ref autoderefs, .. }, ..
for i in 0..autoderefs { }) = cx.tables.adjustments.get(&id).cloned() {
let method_call = ty::MethodCall::autoderef(id, i as u32); for &overloaded in autoderefs {
if let Some(m) = cx.tables.method_map.get(&method_call).cloned() { if let Some(m) = overloaded {
if method_call_refers_to_method(cx.tcx, method, m.def_id, m.substs, id) { if method_call_refers_to_method(cx.tcx, method, m.def_id, m.substs, id) {
return true; return true;
} }

View file

@ -31,16 +31,16 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
debug!("Expr::make_mirror(): id={}, span={:?}", self.id, self.span); debug!("Expr::make_mirror(): id={}, span={:?}", self.id, self.span);
let mut expr = make_mirror_unadjusted(cx, self); let mut expr = make_mirror_unadjusted(cx, self);
let adj = cx.tables().adjustments.get(&self.id).cloned(); let adj = cx.tables().adjustments.get(&self.id);
debug!("make_mirror: unadjusted-expr={:?} applying adjustments={:?}", debug!("make_mirror: unadjusted-expr={:?} applying adjustments={:?}",
expr, expr,
adj); adj);
// Now apply adjustments, if any. // Now apply adjustments, if any.
match adj.map(|adj| (adj.kind, adj.target)) { match adj.map(|adj| (&adj.kind, adj.target)) {
None => {} None => {}
Some((ty::adjustment::Adjust::ReifyFnPointer, adjusted_ty)) => { Some((&ty::adjustment::Adjust::ReifyFnPointer, adjusted_ty)) => {
expr = Expr { expr = Expr {
temp_lifetime: temp_lifetime, temp_lifetime: temp_lifetime,
temp_lifetime_was_shrunk: was_shrunk, temp_lifetime_was_shrunk: was_shrunk,
@ -49,7 +49,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
kind: ExprKind::ReifyFnPointer { source: expr.to_ref() }, kind: ExprKind::ReifyFnPointer { source: expr.to_ref() },
}; };
} }
Some((ty::adjustment::Adjust::UnsafeFnPointer, adjusted_ty)) => { Some((&ty::adjustment::Adjust::UnsafeFnPointer, adjusted_ty)) => {
expr = Expr { expr = Expr {
temp_lifetime: temp_lifetime, temp_lifetime: temp_lifetime,
temp_lifetime_was_shrunk: was_shrunk, temp_lifetime_was_shrunk: was_shrunk,
@ -58,7 +58,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
kind: ExprKind::UnsafeFnPointer { source: expr.to_ref() }, kind: ExprKind::UnsafeFnPointer { source: expr.to_ref() },
}; };
} }
Some((ty::adjustment::Adjust::ClosureFnPointer, adjusted_ty)) => { Some((&ty::adjustment::Adjust::ClosureFnPointer, adjusted_ty)) => {
expr = Expr { expr = Expr {
temp_lifetime: temp_lifetime, temp_lifetime: temp_lifetime,
temp_lifetime_was_shrunk: was_shrunk, temp_lifetime_was_shrunk: was_shrunk,
@ -67,7 +67,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
kind: ExprKind::ClosureFnPointer { source: expr.to_ref() }, kind: ExprKind::ClosureFnPointer { source: expr.to_ref() },
}; };
} }
Some((ty::adjustment::Adjust::NeverToAny, adjusted_ty)) => { Some((&ty::adjustment::Adjust::NeverToAny, adjusted_ty)) => {
expr = Expr { expr = Expr {
temp_lifetime: temp_lifetime, temp_lifetime: temp_lifetime,
temp_lifetime_was_shrunk: was_shrunk, temp_lifetime_was_shrunk: was_shrunk,
@ -76,7 +76,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
kind: ExprKind::NeverToAny { source: expr.to_ref() }, kind: ExprKind::NeverToAny { source: expr.to_ref() },
}; };
} }
Some((ty::adjustment::Adjust::MutToConstPointer, adjusted_ty)) => { Some((&ty::adjustment::Adjust::MutToConstPointer, adjusted_ty)) => {
expr = Expr { expr = Expr {
temp_lifetime: temp_lifetime, temp_lifetime: temp_lifetime,
temp_lifetime_was_shrunk: was_shrunk, temp_lifetime_was_shrunk: was_shrunk,
@ -85,25 +85,18 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
kind: ExprKind::Cast { source: expr.to_ref() }, kind: ExprKind::Cast { source: expr.to_ref() },
}; };
} }
Some((ty::adjustment::Adjust::DerefRef { autoderefs, autoref, unsize }, Some((&ty::adjustment::Adjust::DerefRef { ref autoderefs, autoref, unsize },
adjusted_ty)) => { adjusted_ty)) => {
for i in 0..autoderefs { for &overloaded in autoderefs {
let i = i as u32; let mut ref_ty = expr.ty;
let adjusted_ty = let kind = if let Some(method) = overloaded {
expr.ty.adjust_for_autoderef(cx.tcx, self.id, self.span, i, |mc| { debug!("make_mirror: overloaded autoderef (method={:?})", method);
cx.tables().method_map.get(&mc).map(|m| m.ty)
});
debug!("make_mirror: autoderef #{}, adjusted_ty={:?}",
i,
adjusted_ty);
let method_key = ty::MethodCall::autoderef(self.id, i);
let meth_ty = cx.tables().method_map.get(&method_key).map(|m| m.ty);
let kind = if let Some(meth_ty) = meth_ty {
debug!("make_mirror: overloaded autoderef (meth_ty={:?})", meth_ty);
let ref_ty = cx.tcx.no_late_bound_regions(&meth_ty.fn_ret()); // Method calls always have all late-bound regions
let (region, mutbl) = match ref_ty { // fully instantiated.
Some(&ty::TyS { sty: ty::TyRef(region, mt), .. }) => (region, mt.mutbl), ref_ty = cx.tcx.no_late_bound_regions(&method.ty.fn_ret()).unwrap();
let (region, mutbl) = match ref_ty.sty {
ty::TyRef(region, mt) => (region, mt.mutbl),
_ => span_bug!(expr.span, "autoderef returned bad type"), _ => span_bug!(expr.span, "autoderef returned bad type"),
}; };
@ -125,7 +118,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
overloaded_lvalue(cx, overloaded_lvalue(cx,
self, self,
method_key, method,
PassArgs::ByRef, PassArgs::ByRef,
expr.to_ref(), expr.to_ref(),
vec![]) vec![])
@ -133,6 +126,14 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
debug!("make_mirror: built-in autoderef"); debug!("make_mirror: built-in autoderef");
ExprKind::Deref { arg: expr.to_ref() } ExprKind::Deref { arg: expr.to_ref() }
}; };
let adjusted_ty = match ref_ty.builtin_deref(true,
ty::LvaluePreference::NoPreference) {
Some(mt) => mt.ty,
None => {
span_bug!(self.span, "autoderef for {} failed: {}", self.id, ref_ty);
}
};
debug!("make_mirror: autoderef adjusted_ty={:?}", adjusted_ty);
expr = Expr { expr = Expr {
temp_lifetime: temp_lifetime, temp_lifetime: temp_lifetime,
temp_lifetime_was_shrunk: was_shrunk, temp_lifetime_was_shrunk: was_shrunk,
@ -243,7 +244,8 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
// Here comes the interesting stuff: // Here comes the interesting stuff:
hir::ExprMethodCall(.., ref args) => { hir::ExprMethodCall(.., ref args) => {
// Rewrite a.b(c) into UFCS form like Trait::b(a, c) // Rewrite a.b(c) into UFCS form like Trait::b(a, c)
let expr = method_callee(cx, expr, ty::MethodCall::expr(expr.id)); let method = cx.tables().method_map[&expr.id];
let expr = method_callee(cx, expr, method);
let args = args.iter() let args = args.iter()
.map(|e| e.to_ref()) .map(|e| e.to_ref())
.collect(); .collect();
@ -255,7 +257,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
} }
hir::ExprCall(ref fun, ref args) => { hir::ExprCall(ref fun, ref args) => {
if cx.tables().is_method_call(expr.id) { if let Some(&method) = cx.tables().method_map.get(&expr.id) {
// The callee is something implementing Fn, FnMut, or FnOnce. // The callee is something implementing Fn, FnMut, or FnOnce.
// Find the actual method implementation being called and // Find the actual method implementation being called and
// build the appropriate UFCS call expression with the // build the appropriate UFCS call expression with the
@ -263,7 +265,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
// rewrite f(u, v) into FnOnce::call_once(f, (u, v)) // rewrite f(u, v) into FnOnce::call_once(f, (u, v))
let method = method_callee(cx, expr, ty::MethodCall::expr(expr.id)); let method = method_callee(cx, expr, method);
let sig = method.ty.fn_sig(); let sig = method.ty.fn_sig();
@ -352,7 +354,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
} }
hir::ExprAssignOp(op, ref lhs, ref rhs) => { hir::ExprAssignOp(op, ref lhs, ref rhs) => {
if cx.tables().is_method_call(expr.id) { if let Some(&method) = cx.tables().method_map.get(&expr.id) {
let pass_args = if op.node.is_by_value() { let pass_args = if op.node.is_by_value() {
PassArgs::ByValue PassArgs::ByValue
} else { } else {
@ -360,7 +362,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
}; };
overloaded_operator(cx, overloaded_operator(cx,
expr, expr,
ty::MethodCall::expr(expr.id), method,
pass_args, pass_args,
lhs.to_ref(), lhs.to_ref(),
vec![rhs]) vec![rhs])
@ -376,7 +378,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
hir::ExprLit(..) => ExprKind::Literal { literal: cx.const_eval_literal(expr) }, hir::ExprLit(..) => ExprKind::Literal { literal: cx.const_eval_literal(expr) },
hir::ExprBinary(op, ref lhs, ref rhs) => { hir::ExprBinary(op, ref lhs, ref rhs) => {
if cx.tables().is_method_call(expr.id) { if let Some(&method) = cx.tables().method_map.get(&expr.id) {
let pass_args = if op.node.is_by_value() { let pass_args = if op.node.is_by_value() {
PassArgs::ByValue PassArgs::ByValue
} else { } else {
@ -384,7 +386,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
}; };
overloaded_operator(cx, overloaded_operator(cx,
expr, expr,
ty::MethodCall::expr(expr.id), method,
pass_args, pass_args,
lhs.to_ref(), lhs.to_ref(),
vec![rhs]) vec![rhs])
@ -436,10 +438,10 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
} }
hir::ExprIndex(ref lhs, ref index) => { hir::ExprIndex(ref lhs, ref index) => {
if cx.tables().is_method_call(expr.id) { if let Some(&method) = cx.tables().method_map.get(&expr.id) {
overloaded_lvalue(cx, overloaded_lvalue(cx,
expr, expr,
ty::MethodCall::expr(expr.id), method,
PassArgs::ByValue, PassArgs::ByValue,
lhs.to_ref(), lhs.to_ref(),
vec![index]) vec![index])
@ -452,10 +454,10 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
} }
hir::ExprUnary(hir::UnOp::UnDeref, ref arg) => { hir::ExprUnary(hir::UnOp::UnDeref, ref arg) => {
if cx.tables().is_method_call(expr.id) { if let Some(&method) = cx.tables().method_map.get(&expr.id) {
overloaded_lvalue(cx, overloaded_lvalue(cx,
expr, expr,
ty::MethodCall::expr(expr.id), method,
PassArgs::ByValue, PassArgs::ByValue,
arg.to_ref(), arg.to_ref(),
vec![]) vec![])
@ -465,10 +467,10 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
} }
hir::ExprUnary(hir::UnOp::UnNot, ref arg) => { hir::ExprUnary(hir::UnOp::UnNot, ref arg) => {
if cx.tables().is_method_call(expr.id) { if let Some(&method) = cx.tables().method_map.get(&expr.id) {
overloaded_operator(cx, overloaded_operator(cx,
expr, expr,
ty::MethodCall::expr(expr.id), method,
PassArgs::ByValue, PassArgs::ByValue,
arg.to_ref(), arg.to_ref(),
vec![]) vec![])
@ -481,10 +483,10 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
} }
hir::ExprUnary(hir::UnOp::UnNeg, ref arg) => { hir::ExprUnary(hir::UnOp::UnNeg, ref arg) => {
if cx.tables().is_method_call(expr.id) { if let Some(&method) = cx.tables().method_map.get(&expr.id) {
overloaded_operator(cx, overloaded_operator(cx,
expr, expr,
ty::MethodCall::expr(expr.id), method,
PassArgs::ByValue, PassArgs::ByValue,
arg.to_ref(), arg.to_ref(),
vec![]) vec![])
@ -703,9 +705,8 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
fn method_callee<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, fn method_callee<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
expr: &hir::Expr, expr: &hir::Expr,
method_call: ty::MethodCall) callee: ty::MethodCallee<'tcx>)
-> Expr<'tcx> { -> Expr<'tcx> {
let callee = cx.tables().method_map[&method_call];
let (temp_lifetime, was_shrunk) = cx.region_maps.temporary_scope2(expr.id); let (temp_lifetime, was_shrunk) = cx.region_maps.temporary_scope2(expr.id);
Expr { Expr {
temp_lifetime: temp_lifetime, temp_lifetime: temp_lifetime,
@ -948,7 +949,7 @@ enum PassArgs {
fn overloaded_operator<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, fn overloaded_operator<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
expr: &'tcx hir::Expr, expr: &'tcx hir::Expr,
method_call: ty::MethodCall, method: ty::MethodCallee<'tcx>,
pass_args: PassArgs, pass_args: PassArgs,
receiver: ExprRef<'tcx>, receiver: ExprRef<'tcx>,
args: Vec<&'tcx P<hir::Expr>>) args: Vec<&'tcx P<hir::Expr>>)
@ -991,7 +992,7 @@ fn overloaded_operator<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
} }
// now create the call itself // now create the call itself
let fun = method_callee(cx, expr, method_call); let fun = method_callee(cx, expr, method);
ExprKind::Call { ExprKind::Call {
ty: fun.ty, ty: fun.ty,
fun: fun.to_ref(), fun: fun.to_ref(),
@ -1001,7 +1002,7 @@ fn overloaded_operator<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
fn overloaded_lvalue<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, fn overloaded_lvalue<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
expr: &'tcx hir::Expr, expr: &'tcx hir::Expr,
method_call: ty::MethodCall, method: ty::MethodCallee<'tcx>,
pass_args: PassArgs, pass_args: PassArgs,
receiver: ExprRef<'tcx>, receiver: ExprRef<'tcx>,
args: Vec<&'tcx P<hir::Expr>>) args: Vec<&'tcx P<hir::Expr>>)
@ -1011,14 +1012,14 @@ fn overloaded_lvalue<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
// line up (this is because `*x` and `x[y]` represent lvalues): // line up (this is because `*x` and `x[y]` represent lvalues):
// to find the type &T of the content returned by the method; // to find the type &T of the content returned by the method;
let ref_ty = cx.tables().method_map[&method_call].ty.fn_ret(); let ref_ty = method.ty.fn_ret();
let ref_ty = cx.tcx.no_late_bound_regions(&ref_ty).unwrap(); let ref_ty = cx.tcx.no_late_bound_regions(&ref_ty).unwrap();
// callees always have all late-bound regions fully instantiated, // callees always have all late-bound regions fully instantiated,
// construct the complete expression `foo()` for the overloaded call, // construct the complete expression `foo()` for the overloaded call,
// which will yield the &T type // which will yield the &T type
let (temp_lifetime, was_shrunk) = cx.region_maps.temporary_scope2(expr.id); let (temp_lifetime, was_shrunk) = cx.region_maps.temporary_scope2(expr.id);
let ref_kind = overloaded_operator(cx, expr, method_call, pass_args, receiver, args); let ref_kind = overloaded_operator(cx, expr, method, pass_args, receiver, args);
let ref_expr = Expr { let ref_expr = Expr {
temp_lifetime: temp_lifetime, temp_lifetime: temp_lifetime,
temp_lifetime_was_shrunk: was_shrunk, temp_lifetime_was_shrunk: was_shrunk,

View file

@ -280,11 +280,10 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node
_ => {} _ => {}
} }
let method_call = ty::MethodCall::expr(e.id);
match e.node { match e.node {
hir::ExprUnary(..) | hir::ExprUnary(..) |
hir::ExprBinary(..) | hir::ExprBinary(..) |
hir::ExprIndex(..) if v.tables.method_map.contains_key(&method_call) => { hir::ExprIndex(..) if v.tables.method_map.contains_key(&e.id) => {
v.promotable = false; v.promotable = false;
} }
hir::ExprBox(_) => { hir::ExprBox(_) => {
@ -381,7 +380,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node
} }
} }
hir::ExprMethodCall(..) => { hir::ExprMethodCall(..) => {
let method = v.tables.method_map[&method_call]; let method = v.tables.method_map[&e.id];
match v.tcx.associated_item(method.def_id).container { match v.tcx.associated_item(method.def_id).container {
ty::ImplContainer(_) => v.handle_const_fn_call(method.def_id, node_ty), ty::ImplContainer(_) => v.handle_const_fn_call(method.def_id, node_ty),
ty::TraitContainer(_) => v.promotable = false ty::TraitContainer(_) => v.promotable = false
@ -450,9 +449,8 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp
Some(&Adjust::ClosureFnPointer) | Some(&Adjust::ClosureFnPointer) |
Some(&Adjust::MutToConstPointer) => {} Some(&Adjust::MutToConstPointer) => {}
Some(&Adjust::DerefRef { autoderefs, .. }) => { Some(&Adjust::DerefRef { ref autoderefs, .. }) => {
if (0..autoderefs as u32) if autoderefs.iter().any(|overloaded| overloaded.is_some()) {
.any(|autoderef| v.tables.is_overloaded_autoderef(e.id, autoderef)) {
v.promotable = false; v.promotable = false;
} }
} }

View file

@ -564,8 +564,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
} }
} }
ast::ExprKind::MethodCall(..) => { ast::ExprKind::MethodCall(..) => {
let method_call = ty::MethodCall::expr(expr.id); let method_id = self.tables.method_map[&expr.id].def_id;
let method_id = self.tables.method_map[&method_call].def_id;
let (def_id, decl_id) = match self.tcx.associated_item(method_id).container { let (def_id, decl_id) = match self.tcx.associated_item(method_id).container {
ty::ImplContainer(_) => (Some(method_id), None), ty::ImplContainer(_) => (Some(method_id), None),
ty::TraitContainer(_) => (None, Some(method_id)), ty::TraitContainer(_) => (None, Some(method_id)),

View file

@ -12,14 +12,12 @@ use astconv::AstConv;
use super::{FnCtxt, LvalueOp}; use super::{FnCtxt, LvalueOp};
use check::coercion::AsCoercionSite;
use rustc::infer::InferOk; use rustc::infer::InferOk;
use rustc::traits; 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::{LvaluePreference, NoPreference}; use rustc::ty::{LvaluePreference, NoPreference};
use rustc::hir; use rustc::ty::adjustment::AutoBorrow;
use syntax_pos::Span; use syntax_pos::Span;
use syntax::symbol::Symbol; use syntax::symbol::Symbol;
@ -149,52 +147,45 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
self.fcx.resolve_type_vars_if_possible(&self.cur_ty) self.fcx.resolve_type_vars_if_possible(&self.cur_ty)
} }
pub fn finalize(self, pref: LvaluePreference, expr: &hir::Expr) { pub fn step_count(&self) -> usize {
let fcx = self.fcx; self.steps.len()
fcx.register_infer_ok_obligations(self.finalize_as_infer_ok(pref, &[expr]));
} }
pub fn finalize_as_infer_ok<E>(self, pref: LvaluePreference, exprs: &[E]) /// Returns the steps required in adjustments (overloaded deref calls).
-> InferOk<'tcx, ()> pub fn adjust_steps(&self, pref: LvaluePreference)
where E: AsCoercionSite -> Vec<Option<ty::MethodCallee<'tcx>>> {
{ self.fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(pref))
let Autoderef { fcx, span, mut obligations, steps, .. } = self; }
let methods: Vec<_> = steps
.iter()
.map(|&(ty, kind)| {
if let AutoderefKind::Overloaded = kind {
fcx.try_overloaded_deref(span, None, ty, pref)
.map(|InferOk { value, obligations: o }| {
obligations.extend(o);
value
})
} else {
None
}
})
.collect();
debug!("finalize({:?}) - {:?},{:?}", pub fn adjust_steps_as_infer_ok(&self, pref: LvaluePreference)
pref, -> InferOk<'tcx, Vec<Option<ty::MethodCallee<'tcx>>>> {
methods, let mut obligations = vec![];
obligations); let steps: Vec<_> = self.steps.iter().map(|&(ty, kind)| {
if let AutoderefKind::Overloaded = kind {
for expr in exprs { self.fcx.try_overloaded_deref(self.span, ty, pref)
let expr = expr.as_coercion_site(); .map(|InferOk { value: (_, method), obligations: o }| {
debug!("finalize - finalizing #{} - {:?}", expr.id, expr); obligations.extend(o);
for (n, method) in methods.iter().enumerate() { method
if let &Some(method) = method { })
let method_call = MethodCall::autoderef(expr.id, n as u32); } else {
fcx.tables.borrow_mut().method_map.insert(method_call, method); None
}
} }
} }).collect();
InferOk { InferOk {
value: (), obligations,
obligations value: steps
} }
} }
pub fn finalize(self) {
let fcx = self.fcx;
fcx.register_predicates(self.into_obligations());
}
pub fn into_obligations(self) -> Vec<traits::PredicateObligation<'tcx>> {
self.obligations
}
} }
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
@ -211,14 +202,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn try_overloaded_deref(&self, pub fn try_overloaded_deref(&self,
span: Span, span: Span,
base_expr: Option<&hir::Expr>,
base_ty: Ty<'tcx>, base_ty: Ty<'tcx>,
pref: LvaluePreference) pref: LvaluePreference)
-> Option<InferOk<'tcx, MethodCallee<'tcx>>> { -> Option<InferOk<'tcx,
let rcvr = base_expr.map(|base_expr| super::AdjustedRcvr { (Option<AutoBorrow<'tcx>>,
rcvr_expr: base_expr, autoderefs: 0, unsize: false ty::MethodCallee<'tcx>)>> {
}); self.try_overloaded_lvalue_op(span, base_ty, &[], pref, LvalueOp::Deref)
self.try_overloaded_lvalue_op(span, rcvr, base_ty, &[], pref, LvalueOp::Deref)
} }
} }

View file

@ -8,13 +8,15 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use super::{DeferredCallResolution, Expectation, FnCtxt, TupleArgumentsFlag}; use super::{Expectation, FnCtxt, TupleArgumentsFlag};
use super::autoderef::Autoderef;
use hir::def::Def; use hir::def::Def;
use hir::def_id::{DefId, LOCAL_CRATE}; use hir::def_id::{DefId, LOCAL_CRATE};
use rustc::{infer, traits}; use rustc::{infer, traits};
use rustc::ty::{self, TyCtxt, LvaluePreference, Ty}; use rustc::ty::{self, TyCtxt, LvaluePreference, Ty};
use rustc::ty::subst::Subst; use rustc::ty::subst::Subst;
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
use syntax::abi; use syntax::abi;
use syntax::symbol::Symbol; use syntax::symbol::Symbol;
use syntax_pos::Span; use syntax_pos::Span;
@ -33,7 +35,7 @@ pub fn check_legal_trait_for_method_call(tcx: TyCtxt, span: Span, trait_id: DefI
} }
enum CallStep<'tcx> { enum CallStep<'tcx> {
Builtin, Builtin(Ty<'tcx>),
DeferredClosure(ty::FnSig<'tcx>), DeferredClosure(ty::FnSig<'tcx>),
Overloaded(ty::MethodCallee<'tcx>), Overloaded(ty::MethodCallee<'tcx>),
} }
@ -49,13 +51,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let expr_ty = self.structurally_resolved_type(call_expr.span, original_callee_ty); let expr_ty = self.structurally_resolved_type(call_expr.span, original_callee_ty);
let mut autoderef = self.autoderef(callee_expr.span, expr_ty); let mut autoderef = self.autoderef(callee_expr.span, expr_ty);
let result = autoderef.by_ref() let mut result = None;
.flat_map(|(adj_ty, idx)| { while result.is_none() && autoderef.next().is_some() {
self.try_overloaded_call_step(call_expr, callee_expr, adj_ty, idx) result = self.try_overloaded_call_step(call_expr, callee_expr, &autoderef);
}) }
.next(); autoderef.finalize();
let callee_ty = autoderef.unambiguous_final_ty();
autoderef.finalize(LvaluePreference::NoPreference, callee_expr);
let output = match result { let output = match result {
None => { None => {
@ -63,7 +63,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.confirm_builtin_call(call_expr, original_callee_ty, arg_exprs, expected) self.confirm_builtin_call(call_expr, original_callee_ty, arg_exprs, expected)
} }
Some(CallStep::Builtin) => { Some(CallStep::Builtin(callee_ty)) => {
self.confirm_builtin_call(call_expr, callee_ty, arg_exprs, expected) self.confirm_builtin_call(call_expr, callee_ty, arg_exprs, expected)
} }
@ -89,19 +89,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn try_overloaded_call_step(&self, fn try_overloaded_call_step(&self,
call_expr: &'gcx hir::Expr, call_expr: &'gcx hir::Expr,
callee_expr: &'gcx hir::Expr, callee_expr: &'gcx hir::Expr,
adjusted_ty: Ty<'tcx>, autoderef: &Autoderef<'a, 'gcx, 'tcx>)
autoderefs: usize)
-> Option<CallStep<'tcx>> { -> Option<CallStep<'tcx>> {
debug!("try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?}, autoderefs={})", let adjusted_ty = autoderef.unambiguous_final_ty();
debug!("try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?})",
call_expr, call_expr,
adjusted_ty, adjusted_ty);
autoderefs);
// If the callee is a bare function or a closure, then we're all set. // If the callee is a bare function or a closure, then we're all set.
match self.structurally_resolved_type(callee_expr.span, adjusted_ty).sty { match adjusted_ty.sty {
ty::TyFnDef(..) | ty::TyFnPtr(_) => { ty::TyFnDef(..) | ty::TyFnPtr(_) => {
let autoderefs = autoderef.adjust_steps(LvaluePreference::NoPreference);
self.apply_autoderef_adjustment(callee_expr.id, autoderefs, adjusted_ty); self.apply_autoderef_adjustment(callee_expr.id, autoderefs, adjusted_ty);
return Some(CallStep::Builtin); return Some(CallStep::Builtin(adjusted_ty));
} }
ty::TyClosure(def_id, substs) => { ty::TyClosure(def_id, substs) => {
@ -116,15 +116,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
infer::FnCall, infer::FnCall,
&closure_ty) &closure_ty)
.0; .0;
self.record_deferred_call_resolution(def_id, let autoderefs = autoderef.adjust_steps(LvaluePreference::NoPreference);
Box::new(CallResolution { self.record_deferred_call_resolution(def_id, DeferredCallResolution {
call_expr: call_expr, call_expr: call_expr,
callee_expr: callee_expr, callee_expr: callee_expr,
adjusted_ty: adjusted_ty, adjusted_ty: adjusted_ty,
autoderefs: autoderefs, autoderefs: autoderefs,
fn_sig: fn_sig.clone(), fn_sig: fn_sig.clone(),
closure_def_id: def_id, closure_def_id: def_id,
})); });
return Some(CallStep::DeferredClosure(fn_sig)); return Some(CallStep::DeferredClosure(fn_sig));
} }
} }
@ -137,23 +137,32 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// over the top. The simplest fix by far is to just ignore // over the top. The simplest fix by far is to just ignore
// this case and deref again, so we wind up with // this case and deref again, so we wind up with
// `FnMut::call_mut(&mut *x, ())`. // `FnMut::call_mut(&mut *x, ())`.
ty::TyRef(..) if autoderefs == 0 => { ty::TyRef(..) if autoderef.step_count() == 0 => {
return None; return None;
} }
_ => {} _ => {}
} }
self.try_overloaded_call_traits(call_expr, callee_expr, adjusted_ty, autoderefs) self.try_overloaded_call_traits(call_expr, adjusted_ty).map(|(autoref, method)| {
.map(|method_callee| CallStep::Overloaded(method_callee)) let autoderefs = autoderef.adjust_steps(LvaluePreference::NoPreference);
self.apply_adjustment(callee_expr.id, Adjustment {
kind: Adjust::DerefRef {
autoderefs,
autoref,
unsize: false
},
target: *method.ty.fn_sig().input(0).skip_binder()
});
CallStep::Overloaded(method)
})
} }
fn try_overloaded_call_traits(&self, fn try_overloaded_call_traits(&self,
call_expr: &hir::Expr, call_expr: &hir::Expr,
callee_expr: &hir::Expr, adjusted_ty: Ty<'tcx>)
adjusted_ty: Ty<'tcx>, -> Option<(Option<AutoBorrow<'tcx>>,
autoderefs: usize) ty::MethodCallee<'tcx>)> {
-> Option<ty::MethodCallee<'tcx>> {
// Try the options that are least restrictive on the caller first. // Try the options that are least restrictive on the caller first.
for &(opt_trait_def_id, method_name) in for &(opt_trait_def_id, method_name) in
&[(self.tcx.lang_items.fn_trait(), Symbol::intern("call")), &[(self.tcx.lang_items.fn_trait(), Symbol::intern("call")),
@ -165,20 +174,12 @@ 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(super::AdjustedRcvr {
rcvr_expr: callee_expr,
autoderefs,
unsize: false
}),
method_name, method_name,
trait_def_id, trait_def_id,
adjusted_ty, adjusted_ty,
None) { None) {
None => continue, None => continue,
Some(ok) => { Some(ok) => return Some(self.register_infer_ok_obligations(ok))
let method_callee = self.register_infer_ok_obligations(ok);
return Some(method_callee);
}
} }
} }
@ -313,30 +314,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
TupleArgumentsFlag::TupleArguments, TupleArgumentsFlag::TupleArguments,
expected); expected);
self.write_overloaded_call_method_map(call_expr, method_callee); self.tables.borrow_mut().method_map.insert(call_expr.id, method_callee);
output_type output_type
} }
fn write_overloaded_call_method_map(&self,
call_expr: &hir::Expr,
method_callee: ty::MethodCallee<'tcx>) {
let method_call = ty::MethodCall::expr(call_expr.id);
self.tables.borrow_mut().method_map.insert(method_call, method_callee);
}
} }
#[derive(Debug)] #[derive(Debug)]
struct CallResolution<'gcx: 'tcx, 'tcx> { pub struct DeferredCallResolution<'gcx: 'tcx, 'tcx> {
call_expr: &'gcx hir::Expr, call_expr: &'gcx hir::Expr,
callee_expr: &'gcx hir::Expr, callee_expr: &'gcx hir::Expr,
adjusted_ty: Ty<'tcx>, adjusted_ty: Ty<'tcx>,
autoderefs: usize, autoderefs: Vec<Option<ty::MethodCallee<'tcx>>>,
fn_sig: ty::FnSig<'tcx>, fn_sig: ty::FnSig<'tcx>,
closure_def_id: DefId, closure_def_id: DefId,
} }
impl<'gcx, 'tcx> DeferredCallResolution<'gcx, 'tcx> for CallResolution<'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> DeferredCallResolution<'gcx, 'tcx> {
fn resolve<'a>(&mut self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) { pub fn resolve(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) {
debug!("DeferredCallResolution::resolve() {:?}", self); debug!("DeferredCallResolution::resolve() {:?}", self);
// we should not be invoked until the closure kind has been // we should not be invoked until the closure kind has been
@ -345,10 +339,8 @@ impl<'gcx, 'tcx> DeferredCallResolution<'gcx, 'tcx> for CallResolution<'gcx, 'tc
// We may now know enough to figure out fn vs fnmut etc. // We may now know enough to figure out fn vs fnmut etc.
match fcx.try_overloaded_call_traits(self.call_expr, match fcx.try_overloaded_call_traits(self.call_expr,
self.callee_expr, self.adjusted_ty) {
self.adjusted_ty, Some((autoref, method_callee)) => {
self.autoderefs) {
Some(method_callee) => {
// One problem is that when we get here, we are going // One problem is that when we get here, we are going
// to have a newly instantiated function signature // to have a newly instantiated function signature
// from the call trait. This has to be reconciled with // from the call trait. This has to be reconciled with
@ -370,7 +362,16 @@ impl<'gcx, 'tcx> DeferredCallResolution<'gcx, 'tcx> for CallResolution<'gcx, 'tc
fcx.demand_eqtype(self.call_expr.span, method_sig.output(), self.fn_sig.output()); fcx.demand_eqtype(self.call_expr.span, method_sig.output(), self.fn_sig.output());
fcx.write_overloaded_call_method_map(self.call_expr, method_callee); fcx.apply_adjustment(self.callee_expr.id, Adjustment {
kind: Adjust::DerefRef {
autoderefs: self.autoderefs,
autoref,
unsize: false
},
target: method_sig.inputs()[0]
});
fcx.tables.borrow_mut().method_map.insert(self.call_expr.id, method_callee);
} }
None => { None => {
span_bug!(self.call_expr.span, span_bug!(self.call_expr.span,

View file

@ -110,7 +110,7 @@ fn coerce_mutbls<'tcx>(from_mutbl: hir::Mutability,
fn identity<'tcx>() -> Adjust<'tcx> { fn identity<'tcx>() -> Adjust<'tcx> {
Adjust::DerefRef { Adjust::DerefRef {
autoderefs: 0, autoderefs: vec![],
autoref: None, autoref: None,
unsize: false, unsize: false,
} }
@ -157,13 +157,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
}) })
} }
fn coerce<E>(&self, fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
exprs: &[E],
a: Ty<'tcx>,
b: Ty<'tcx>)
-> CoerceResult<'tcx>
where E: AsCoercionSite
{
let a = self.shallow_resolve(a); let a = self.shallow_resolve(a);
debug!("Coerce.tys({:?} => {:?})", a, b); debug!("Coerce.tys({:?} => {:?})", a, b);
@ -210,7 +204,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
} }
ty::TyRef(r_b, mt_b) => { ty::TyRef(r_b, mt_b) => {
return self.coerce_borrowed_pointer(exprs, a, b, r_b, mt_b); return self.coerce_borrowed_pointer(a, b, r_b, mt_b);
} }
_ => {} _ => {}
@ -245,15 +239,12 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`. /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
/// To match `A` with `B`, autoderef will be performed, /// To match `A` with `B`, autoderef will be performed,
/// calling `deref`/`deref_mut` where necessary. /// calling `deref`/`deref_mut` where necessary.
fn coerce_borrowed_pointer<E>(&self, fn coerce_borrowed_pointer(&self,
exprs: &[E], a: Ty<'tcx>,
a: Ty<'tcx>, b: Ty<'tcx>,
b: Ty<'tcx>, r_b: ty::Region<'tcx>,
r_b: ty::Region<'tcx>, mt_b: TypeAndMut<'tcx>)
mt_b: TypeAndMut<'tcx>) -> CoerceResult<'tcx> {
-> CoerceResult<'tcx>
where E: AsCoercionSite
{
debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b); debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
@ -375,7 +366,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
}); });
match self.unify(derefd_ty_a, b) { match self.unify(derefd_ty_a, b) {
Ok(ok) => { Ok(ok) => {
found = Some((ok, autoderefs)); found = Some(ok);
break; break;
} }
Err(err) => { Err(err) => {
@ -391,7 +382,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
// (e.g., in example above, the failure from relating `Vec<T>` // (e.g., in example above, the failure from relating `Vec<T>`
// to the target type), since that should be the least // to the target type), since that should be the least
// confusing. // confusing.
let (InferOk { value: ty, mut obligations }, autoderefs) = match found { let InferOk { value: ty, mut obligations } = match found {
Some(d) => d, Some(d) => d,
None => { None => {
let err = first_error.expect("coerce_borrowed_pointer had no error"); let err = first_error.expect("coerce_borrowed_pointer had no error");
@ -400,7 +391,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
} }
}; };
if ty == a && mt_a.mutbl == hir::MutImmutable && autoderefs == 1 { if ty == a && mt_a.mutbl == hir::MutImmutable && autoderef.step_count() == 1 {
// As a special case, if we would produce `&'a *x`, that's // As a special case, if we would produce `&'a *x`, that's
// a total no-op. We end up with the type `&'a T` just as // a total no-op. We end up with the type `&'a T` just as
// we started with. In that case, just skip it // we started with. In that case, just skip it
@ -423,17 +414,21 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
_ => span_bug!(span, "expected a ref type, got {:?}", ty), _ => span_bug!(span, "expected a ref type, got {:?}", ty),
}; };
let autoref = Some(AutoBorrow::Ref(r_borrow, mt_b.mutbl)); let autoref = Some(AutoBorrow::Ref(r_borrow, mt_b.mutbl));
let pref = LvaluePreference::from_mutbl(mt_b.mutbl);
let InferOk { value: autoderefs, obligations: o }
= autoderef.adjust_steps_as_infer_ok(pref);
obligations.extend(o);
obligations.extend(autoderef.into_obligations());
debug!("coerce_borrowed_pointer: succeeded ty={:?} autoderefs={:?} autoref={:?}", debug!("coerce_borrowed_pointer: succeeded ty={:?} autoderefs={:?} autoref={:?}",
ty, ty,
autoderefs, autoderefs,
autoref); autoref);
let pref = LvaluePreference::from_mutbl(mt_b.mutbl);
obligations.extend(autoderef.finalize_as_infer_ok(pref, exprs).obligations);
success(Adjust::DerefRef { success(Adjust::DerefRef {
autoderefs: autoderefs, autoderefs,
autoref: autoref, autoref,
unsize: false, unsize: false,
}, ty, obligations) }, ty, obligations)
} }
@ -477,7 +472,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
let coerce_source = source.adjust_for_autoref(self.tcx, reborrow); let coerce_source = source.adjust_for_autoref(self.tcx, reborrow);
let adjust = Adjust::DerefRef { let adjust = Adjust::DerefRef {
autoderefs: if reborrow.is_some() { 1 } else { 0 }, autoderefs: if reborrow.is_some() { vec![None] } else { vec![] },
autoref: reborrow, autoref: reborrow,
unsize: true, unsize: true,
}; };
@ -668,7 +663,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
// regionck knows that the region for `a` must be valid here. // regionck knows that the region for `a` must be valid here.
self.unify_and(a_unsafe, b, if is_ref { self.unify_and(a_unsafe, b, if is_ref {
Adjust::DerefRef { Adjust::DerefRef {
autoderefs: 1, autoderefs: vec![None],
autoref: Some(AutoBorrow::RawPtr(mutbl_b)), autoref: Some(AutoBorrow::RawPtr(mutbl_b)),
unsize: false, unsize: false,
} }
@ -703,7 +698,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable); let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable);
let coerce = Coerce::new(self, cause); let coerce = Coerce::new(self, cause);
let ok = self.commit_if_ok(|_| coerce.coerce(&[expr], source, target))?; let ok = self.commit_if_ok(|_| coerce.coerce(source, target))?;
let adjustment = self.register_infer_ok_obligations(ok); let adjustment = self.register_infer_ok_obligations(ok);
self.apply_adjustment(expr.id, adjustment); self.apply_adjustment(expr.id, adjustment);
@ -721,7 +716,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let cause = self.cause(syntax_pos::DUMMY_SP, ObligationCauseCode::ExprAssignable); let cause = self.cause(syntax_pos::DUMMY_SP, ObligationCauseCode::ExprAssignable);
let coerce = Coerce::new(self, cause); let coerce = Coerce::new(self, cause);
self.probe(|_| coerce.coerce::<hir::Expr>(&[], source, target)).is_ok() self.probe(|_| coerce.coerce(source, target)).is_ok()
} }
/// Given some expressions, their known unified type and another expression, /// Given some expressions, their known unified type and another expression,
@ -796,7 +791,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// but only if the new expression has no coercion already applied to it. // but only if the new expression has no coercion already applied to it.
let mut first_error = None; let mut first_error = None;
if !self.tables.borrow().adjustments.contains_key(&new.id) { if !self.tables.borrow().adjustments.contains_key(&new.id) {
let result = self.commit_if_ok(|_| coerce.coerce(&[new], new_ty, prev_ty)); let result = self.commit_if_ok(|_| coerce.coerce(new_ty, prev_ty));
match result { match result {
Ok(ok) => { Ok(ok) => {
let adjustment = self.register_infer_ok_obligations(ok); let adjustment = self.register_infer_ok_obligations(ok);
@ -815,10 +810,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let expr = expr.as_coercion_site(); let expr = expr.as_coercion_site();
let noop = match self.tables.borrow().adjustments.get(&expr.id).map(|adj| &adj.kind) { let noop = match self.tables.borrow().adjustments.get(&expr.id).map(|adj| &adj.kind) {
Some(&Adjust::DerefRef { Some(&Adjust::DerefRef {
autoderefs: 1, ref autoderefs,
autoref: Some(AutoBorrow::Ref(_, mutbl_adj)), autoref: Some(AutoBorrow::Ref(_, mutbl_adj)),
unsize: false unsize: false
}) => { }) if autoderefs.len() == 1 => {
match self.node_ty(expr.id).sty { match self.node_ty(expr.id).sty {
ty::TyRef(_, mt_orig) => { ty::TyRef(_, mt_orig) => {
// Reborrow that we can safely ignore, because // Reborrow that we can safely ignore, because
@ -842,7 +837,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
} }
} }
match self.commit_if_ok(|_| coerce.coerce(&exprs, prev_ty, new_ty)) { match self.commit_if_ok(|_| coerce.coerce(prev_ty, new_ty)) {
Err(_) => { Err(_) => {
// Avoid giving strange errors on failed attempts. // Avoid giving strange errors on failed attempts.
if let Some(e) = first_error { if let Some(e) = first_error {

View file

@ -136,8 +136,10 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
let (autoderefd_ty, n) = autoderef.nth(pick.autoderefs).unwrap(); let (autoderefd_ty, n) = autoderef.nth(pick.autoderefs).unwrap();
assert_eq!(n, pick.autoderefs); assert_eq!(n, pick.autoderefs);
let autoderefs = autoderef.adjust_steps(LvaluePreference::NoPreference);
autoderef.unambiguous_final_ty(); autoderef.unambiguous_final_ty();
autoderef.finalize(LvaluePreference::NoPreference, self.self_expr); autoderef.finalize();
let target = pick.unsize.unwrap_or(autoderefd_ty); let target = pick.unsize.unwrap_or(autoderefd_ty);
let target = target.adjust_for_autoref(self.tcx, autoref); let target = target.adjust_for_autoref(self.tcx, autoref);
@ -145,8 +147,8 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
// Write out the final adjustment. // Write out the final adjustment.
self.apply_adjustment(self.self_expr.id, Adjustment { self.apply_adjustment(self.self_expr.id, Adjustment {
kind: Adjust::DerefRef { kind: Adjust::DerefRef {
autoderefs: pick.autoderefs, autoderefs,
autoref: autoref, autoref,
unsize: pick.unsize.is_some(), unsize: pick.unsize.is_some(),
}, },
target: target target: target
@ -436,19 +438,18 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
// Fix up the autoderefs. Autorefs can only occur immediately preceding // Fix up the autoderefs. Autorefs can only occur immediately preceding
// overloaded lvalue ops, and will be fixed by them in order to get // overloaded lvalue ops, and will be fixed by them in order to get
// the correct region. // the correct region.
let autoderefs = match self.tables.borrow().adjustments.get(&expr.id) { let expr_ty = self.node_ty(expr.id);
Some(&Adjustment { kind: Adjust::DerefRef { autoderefs, .. }, .. }) => autoderefs, if let Some(adj) = self.tables.borrow_mut().adjustments.get_mut(&expr.id) {
Some(_) | None => 0 if let Adjust::DerefRef { ref mut autoderefs, .. } = adj.kind {
}; let mut autoderef = self.autoderef(expr.span, expr_ty);
autoderef.nth(autoderefs.len()).unwrap_or_else(|| {
if autoderefs > 0 { span_bug!(expr.span,
let mut autoderef = self.autoderef(expr.span, self.node_ty(expr.id)); "expr was deref-able as {:?} but now isn't?",
autoderef.nth(autoderefs).unwrap_or_else(|| { autoderefs);
span_bug!(expr.span, });
"expr was deref-able {} times but now isn't?", *autoderefs = autoderef.adjust_steps(LvaluePreference::PreferMutLvalue);
autoderefs); autoderef.finalize();
}); }
autoderef.finalize(PreferMutLvalue, expr);
} }
match expr.node { match expr.node {
@ -474,8 +475,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
{ {
debug!("convert_lvalue_op_to_mutable({:?}, {:?}, {:?}, {:?})", debug!("convert_lvalue_op_to_mutable({:?}, {:?}, {:?}, {:?})",
op, expr, base_expr, arg_tys); op, expr, base_expr, arg_tys);
let method_call = ty::MethodCall::expr(expr.id); if !self.tables.borrow().method_map.contains_key(&expr.id) {
if !self.tables.borrow().method_map.contains_key(&method_call) {
debug!("convert_lvalue_op_to_mutable - builtin, nothing to do"); debug!("convert_lvalue_op_to_mutable - builtin, nothing to do");
return return
} }
@ -490,14 +490,14 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
.ty; .ty;
let method = self.try_overloaded_lvalue_op( let method = self.try_overloaded_lvalue_op(
expr.span, None, base_ty, arg_tys, PreferMutLvalue, op); expr.span, base_ty, arg_tys, PreferMutLvalue, op);
let ok = match method { let ok = match method {
Some(method) => method, Some(method) => method,
None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed") None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed")
}; };
let method = self.register_infer_ok_obligations(ok); let (_, method) = self.register_infer_ok_obligations(ok);
debug!("convert_lvalue_op_to_mutable: method={:?}", method); debug!("convert_lvalue_op_to_mutable: method={:?}", method);
self.tables.borrow_mut().method_map.insert(method_call, method); self.tables.borrow_mut().method_map.insert(expr.id, method);
// Convert the autoref in the base expr to mutable with the correct // Convert the autoref in the base expr to mutable with the correct
// region and mutability. // region and mutability.

View file

@ -10,13 +10,13 @@
//! Method lookup: the secret sauce of Rust. See `README.md`. //! Method lookup: the secret sauce of Rust. See `README.md`.
use check::{FnCtxt, AdjustedRcvr}; use check::FnCtxt;
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;
use rustc::traits; use rustc::traits;
use rustc::ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, TypeFoldable}; use rustc::ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, TypeFoldable};
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow}; use rustc::ty::adjustment::AutoBorrow;
use rustc::ty::subst::Subst; use rustc::ty::subst::Subst;
use rustc::infer::{self, InferOk}; use rustc::infer::{self, InferOk};
@ -166,16 +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_info: Option<AdjustedRcvr>,
m_name: ast::Name, m_name: ast::Name,
trait_def_id: DefId, trait_def_id: DefId,
self_ty: ty::Ty<'tcx>, self_ty: ty::Ty<'tcx>,
opt_input_types: Option<Vec<ty::Ty<'tcx>>>) opt_input_types: Option<&[ty::Ty<'tcx>]>)
-> Option<InferOk<'tcx, ty::MethodCallee<'tcx>>> { -> Option<InferOk<'tcx,
debug!("lookup_in_trait_adjusted(self_ty={:?}, self_info={:?}, \ (Option<AutoBorrow<'tcx>>,
ty::MethodCallee<'tcx>)>> {
debug!("lookup_in_trait_adjusted(self_ty={:?}, \
m_name={}, trait_def_id={:?})", m_name={}, trait_def_id={:?})",
self_ty, self_ty,
self_info,
m_name, m_name,
trait_def_id); trait_def_id);
@ -237,7 +237,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
value value
} }
}; };
let transformed_self_ty = fn_sig.inputs()[0];
let method_ty = tcx.mk_fn_def(def_id, substs, ty::Binder(fn_sig)); let method_ty = tcx.mk_fn_def(def_id, substs, ty::Binder(fn_sig));
debug!("lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}", debug!("lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}",
@ -267,36 +266,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// Also add an obligation for the method type being well-formed. // Also add an obligation for the method type being well-formed.
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). let autoref = match (&original_method_ty.fn_sig().input(0).skip_binder().sty,
if let Some(AdjustedRcvr { rcvr_expr, autoderefs, unsize }) = self_info { &method_ty.fn_sig().input(0).skip_binder().sty) {
debug!("lookup_in_trait_adjusted: inserting adjustment if needed \ (&ty::TyRef(..), &ty::TyRef(region, ty::TypeAndMut { mutbl, ty: _ })) => {
(self-id={}, autoderefs={}, unsize={}, fty={:?})", // Trait method is fn(&self) or fn(&mut self), need an
rcvr_expr.id, autoderefs, unsize, original_method_ty); // autoref. Pull the region etc out of the type of first argument.
Some(AutoBorrow::Ref(region, mutbl))
let original_sig = original_method_ty.fn_sig(); }
let autoref = match (&original_sig.input(0).skip_binder().sty, _ => {
&transformed_self_ty.sty) { // Trait method is fn(self), no transformation needed.
(&ty::TyRef(..), &ty::TyRef(region, ty::TypeAndMut { mutbl, ty: _ })) => { None
// Trait method is fn(&self) or fn(&mut self), need an }
// autoref. Pull the region etc out of the type of first argument. };
Some(AutoBorrow::Ref(region, mutbl))
}
_ => {
// Trait method is fn(self), no transformation needed.
assert!(!unsize);
None
}
};
self.apply_adjustment(rcvr_expr.id, Adjustment {
kind: Adjust::DerefRef {
autoderefs: autoderefs,
autoref: autoref,
unsize: unsize
},
target: transformed_self_ty
});
}
let callee = ty::MethodCallee { let callee = ty::MethodCallee {
def_id: def_id, def_id: def_id,
@ -308,7 +289,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
Some(InferOk { Some(InferOk {
obligations, obligations,
value: callee value: (autoref, callee)
}) })
} }

View file

@ -77,6 +77,8 @@ type parameter).
*/ */
pub use self::Expectation::*; pub use self::Expectation::*;
use self::autoderef::Autoderef;
use self::callee::DeferredCallResolution;
use self::coercion::{CoerceMany, DynamicCoerceMany}; use self::coercion::{CoerceMany, DynamicCoerceMany};
pub use self::compare_method::{compare_impl_method, compare_const_impl}; pub use self::compare_method::{compare_impl_method, compare_const_impl};
use self::TupleArgumentsFlag::*; use self::TupleArgumentsFlag::*;
@ -93,7 +95,7 @@ use rustc::ty::subst::{Kind, Subst, Substs};
use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode, Reveal}; use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode, Reveal};
use rustc::ty::{ParamTy, LvaluePreference, NoPreference, PreferMutLvalue}; use rustc::ty::{ParamTy, LvaluePreference, NoPreference, PreferMutLvalue};
use rustc::ty::{self, Ty, TyCtxt, Visibility}; use rustc::ty::{self, Ty, TyCtxt, Visibility};
use rustc::ty::{MethodCall, MethodCallee}; use rustc::ty::{MethodCallee};
use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
use rustc::ty::fold::{BottomUpFolder, TypeFoldable}; use rustc::ty::fold::{BottomUpFolder, TypeFoldable};
use rustc::ty::maps::Providers; use rustc::ty::maps::Providers;
@ -168,7 +170,7 @@ pub struct Inherited<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
// decision. We keep these deferred resolutions grouped by the // decision. We keep these deferred resolutions grouped by the
// def-id of the closure, so that once we decide, we can easily go // def-id of the closure, so that once we decide, we can easily go
// back and process them. // back and process them.
deferred_call_resolutions: RefCell<DefIdMap<Vec<DeferredCallResolutionHandler<'gcx, 'tcx>>>>, deferred_call_resolutions: RefCell<DefIdMap<Vec<DeferredCallResolution<'gcx, 'tcx>>>>,
deferred_cast_checks: RefCell<Vec<cast::CastCheck<'tcx>>>, deferred_cast_checks: RefCell<Vec<cast::CastCheck<'tcx>>>,
@ -194,12 +196,6 @@ impl<'a, 'gcx, 'tcx> Deref for Inherited<'a, 'gcx, 'tcx> {
} }
} }
trait DeferredCallResolution<'gcx, 'tcx> {
fn resolve<'a>(&mut self, fcx: &FnCtxt<'a, 'gcx, 'tcx>);
}
type DeferredCallResolutionHandler<'gcx, 'tcx> = Box<DeferredCallResolution<'gcx, 'tcx>+'tcx>;
/// When type-checking an expression, we propagate downward /// When type-checking an expression, we propagate downward
/// whatever type hint we are able in the form of an `Expectation`. /// whatever type hint we are able in the form of an `Expectation`.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@ -375,13 +371,6 @@ pub enum LvalueOp {
Index 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
@ -1729,17 +1718,17 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn record_deferred_call_resolution(&self, fn record_deferred_call_resolution(&self,
closure_def_id: DefId, closure_def_id: DefId,
r: DeferredCallResolutionHandler<'gcx, 'tcx>) { r: DeferredCallResolution<'gcx, 'tcx>) {
let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut(); let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
deferred_call_resolutions.entry(closure_def_id).or_insert(vec![]).push(r); deferred_call_resolutions.entry(closure_def_id).or_insert(vec![]).push(r);
} }
fn remove_deferred_call_resolutions(&self, fn remove_deferred_call_resolutions(&self,
closure_def_id: DefId) closure_def_id: DefId)
-> Vec<DeferredCallResolutionHandler<'gcx, 'tcx>> -> Vec<DeferredCallResolution<'gcx, 'tcx>>
{ {
let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut(); let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut();
deferred_call_resolutions.remove(&closure_def_id).unwrap_or(Vec::new()) deferred_call_resolutions.remove(&closure_def_id).unwrap_or(vec![])
} }
pub fn tag(&self) -> String { pub fn tag(&self) -> String {
@ -1782,11 +1771,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn apply_autoderef_adjustment(&self, pub fn apply_autoderef_adjustment(&self,
node_id: ast::NodeId, node_id: ast::NodeId,
derefs: usize, autoderefs: Vec<Option<ty::MethodCallee<'tcx>>>,
adjusted_ty: Ty<'tcx>) { adjusted_ty: Ty<'tcx>) {
self.apply_adjustment(node_id, Adjustment { self.apply_adjustment(node_id, Adjustment {
kind: Adjust::DerefRef { kind: Adjust::DerefRef {
autoderefs: derefs, autoderefs,
autoref: None, autoref: None,
unsize: false unsize: false
}, },
@ -1805,18 +1794,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
Entry::Vacant(entry) => { entry.insert(adj); }, Entry::Vacant(entry) => { entry.insert(adj); },
Entry::Occupied(mut entry) => { Entry::Occupied(mut entry) => {
debug!(" - composing on top of {:?}", entry.get()); debug!(" - composing on top of {:?}", entry.get());
let composed_kind = match (&entry.get().kind, &adj.kind) { match (&entry.get().kind, &adj.kind) {
// Applying any adjustment on top of a NeverToAny // Applying any adjustment on top of a NeverToAny
// is a valid NeverToAny adjustment, because it can't // is a valid NeverToAny adjustment, because it can't
// be reached. // be reached.
(&Adjust::NeverToAny, _) => Adjust::NeverToAny, (&Adjust::NeverToAny, _) => return,
(&Adjust::DerefRef { (&Adjust::DerefRef {
autoderefs: 1, autoderefs: ref old,
autoref: Some(AutoBorrow::Ref(..)), autoref: Some(AutoBorrow::Ref(..)),
unsize: false unsize: false
}, &Adjust::DerefRef { autoderefs, .. }) if autoderefs > 0 => { }, &Adjust::DerefRef {
autoderefs: ref new, ..
}) if old.len() == 1 && new.len() >= 1 => {
// A reborrow has no effect before a dereference. // A reborrow has no effect before a dereference.
adj.kind
} }
// FIXME: currently we never try to compose autoderefs // FIXME: currently we never try to compose autoderefs
// and ReifyFnPointer/UnsafeFnPointer, but we could. // and ReifyFnPointer/UnsafeFnPointer, but we could.
@ -1824,10 +1814,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
bug!("while adjusting {}, can't compose {:?} and {:?}", bug!("while adjusting {}, can't compose {:?} and {:?}",
node_id, entry.get(), adj) node_id, entry.get(), adj)
}; };
*entry.get_mut() = Adjustment { *entry.get_mut() = adj;
kind: composed_kind,
target: adj.target
};
} }
} }
} }
@ -2189,32 +2176,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// consolidated. // consolidated.
let mut autoderef = self.autoderef(base_expr.span, base_ty); let mut autoderef = self.autoderef(base_expr.span, base_ty);
let mut result = None;
while let Some((adj_ty, autoderefs)) = autoderef.next() { while result.is_none() && autoderef.next().is_some() {
if let Some(final_mt) = self.try_index_step( result = self.try_index_step(expr, base_expr, &autoderef, lvalue_pref, idx_ty);
MethodCall::expr(expr.id), expr, Some(AdjustedRcvr {
rcvr_expr: base_expr,
autoderefs,
unsize: false
}), base_expr.span, adj_ty, lvalue_pref, idx_ty)
{
autoderef.finalize(lvalue_pref, base_expr);
return Some(final_mt);
}
if let ty::TyArray(element_ty, _) = adj_ty.sty {
autoderef.finalize(lvalue_pref, base_expr);
let adj_ty = self.tcx.mk_slice(element_ty);
return self.try_index_step(
MethodCall::expr(expr.id), expr, Some(AdjustedRcvr {
rcvr_expr: base_expr,
autoderefs,
unsize: true
}), base_expr.span, adj_ty, lvalue_pref, idx_ty)
}
} }
autoderef.unambiguous_final_ty(); autoderef.finalize();
None result
} }
/// To type-check `base_expr[index_expr]`, we progressively autoderef /// To type-check `base_expr[index_expr]`, we progressively autoderef
@ -2223,16 +2190,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
/// This loop implements one step in that search; the autoderef loop /// This loop implements one step in that search; the autoderef loop
/// is implemented by `lookup_indexing`. /// is implemented by `lookup_indexing`.
fn try_index_step(&self, fn try_index_step(&self,
method_call: MethodCall,
expr: &hir::Expr, expr: &hir::Expr,
base_expr: Option<AdjustedRcvr>, base_expr: &hir::Expr,
base_span: Span, autoderef: &Autoderef<'a, 'gcx, 'tcx>,
adjusted_ty: Ty<'tcx>,
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 mut adjusted_ty = autoderef.unambiguous_final_ty();
debug!("try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \ debug!("try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
index_ty={:?})", index_ty={:?})",
expr, expr,
@ -2240,35 +2205,59 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
adjusted_ty, adjusted_ty,
index_ty); index_ty);
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]`. let autoderefs = autoderef.adjust_steps(lvalue_pref);
if let Some(base_expr) = base_expr { self.apply_autoderef_adjustment(
assert!(!base_expr.unsize); base_expr.id, autoderefs, adjusted_ty);
self.apply_autoderef_adjustment( return Some((self.tcx.types.usize, ty));
base_expr.rcvr_expr.id, base_expr.autoderefs, adjusted_ty);
}
return Some((tcx.types.usize, ty));
} }
_ => {} _ => {}
} }
// If some lookup succeeds, write callee into table and extract index/element for &unsize in &[false, true] {
// type from the method signature. if unsize {
// If some lookup succeeded, install method in table // We only unsize arrays here.
let method = self.try_overloaded_lvalue_op( if let ty::TyArray(element_ty, _) = adjusted_ty.sty {
expr.span, base_expr, adjusted_ty, &[input_ty], lvalue_pref, LvalueOp::Index); adjusted_ty = self.tcx.mk_slice(element_ty);
} else {
continue;
}
}
method.map(|ok| { // If some lookup succeeds, write callee into table and extract index/element
debug!("try_index_step: success, using overloaded indexing"); // type from the method signature.
let method = self.register_infer_ok_obligations(ok); // If some lookup succeeded, install method in table
self.tables.borrow_mut().method_map.insert(method_call, method); let input_ty = self.next_ty_var(TypeVariableOrigin::AutoDeref(base_expr.span));
(input_ty, self.make_overloaded_lvalue_return_type(method).ty) let method = self.try_overloaded_lvalue_op(
}) expr.span, adjusted_ty, &[input_ty], lvalue_pref, LvalueOp::Index);
let result = method.map(|ok| {
debug!("try_index_step: success, using overloaded indexing");
let (autoref, method) = self.register_infer_ok_obligations(ok);
let autoderefs = autoderef.adjust_steps(lvalue_pref);
self.apply_adjustment(base_expr.id, Adjustment {
kind: Adjust::DerefRef {
autoderefs,
autoref,
unsize
},
target: *method.ty.fn_sig().input(0).skip_binder()
});
self.tables.borrow_mut().method_map.insert(expr.id, method);
(input_ty, self.make_overloaded_lvalue_return_type(method).ty)
});
if result.is_some() {
return result;
}
}
None
} }
fn resolve_lvalue_op(&self, op: LvalueOp, is_mut: bool) -> (Option<DefId>, Symbol) { fn resolve_lvalue_op(&self, op: LvalueOp, is_mut: bool) -> (Option<DefId>, Symbol) {
@ -2287,16 +2276,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn try_overloaded_lvalue_op(&self, fn try_overloaded_lvalue_op(&self,
span: Span, span: Span,
base_expr: Option<AdjustedRcvr>,
base_ty: Ty<'tcx>, base_ty: Ty<'tcx>,
arg_tys: &[Ty<'tcx>], arg_tys: &[Ty<'tcx>],
lvalue_pref: LvaluePreference, lvalue_pref: LvaluePreference,
op: LvalueOp) op: LvalueOp)
-> Option<InferOk<'tcx, MethodCallee<'tcx>>> -> Option<InferOk<'tcx,
(Option<AutoBorrow<'tcx>>,
ty::MethodCallee<'tcx>)>>
{ {
debug!("try_overloaded_lvalue_op({:?},{:?},{:?},{:?},{:?})", debug!("try_overloaded_lvalue_op({:?},{:?},{:?},{:?})",
span, span,
base_expr,
base_ty, base_ty,
lvalue_pref, lvalue_pref,
op); op);
@ -2306,11 +2295,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let method = match (lvalue_pref, mut_tr) { let method = match (lvalue_pref, mut_tr) {
(PreferMutLvalue, Some(trait_did)) => { (PreferMutLvalue, Some(trait_did)) => {
self.lookup_method_in_trait_adjusted(span, self.lookup_method_in_trait_adjusted(span,
base_expr,
mut_op, mut_op,
trait_did, trait_did,
base_ty, base_ty,
Some(arg_tys.to_owned())) Some(arg_tys))
} }
_ => None, _ => None,
}; };
@ -2320,11 +2308,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let method = match (method, imm_tr) { let method = match (method, imm_tr) {
(None, Some(trait_did)) => { (None, Some(trait_did)) => {
self.lookup_method_in_trait_adjusted(span, self.lookup_method_in_trait_adjusted(span,
base_expr,
imm_op, imm_op,
trait_did, trait_did,
base_ty, base_ty,
Some(arg_tys.to_owned())) Some(arg_tys))
} }
(method, _) => method, (method, _) => method,
}; };
@ -2802,10 +2789,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
expr, expr,
rcvr) { rcvr) {
Ok(method) => { Ok(method) => {
let method_ty = method.ty; self.tables.borrow_mut().method_map.insert(expr.id, method);
let method_call = MethodCall::expr(expr.id); method.ty
self.tables.borrow_mut().method_map.insert(method_call, method);
method_ty
} }
Err(error) => { Err(error) => {
if method_name.node != keywords::Invalid.name() { if method_name.node != keywords::Invalid.name() {
@ -2912,7 +2897,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
expr_t); expr_t);
let mut private_candidate = None; let mut private_candidate = None;
let mut autoderef = self.autoderef(expr.span, expr_t); let mut autoderef = self.autoderef(expr.span, expr_t);
while let Some((base_t, autoderefs)) = autoderef.next() { while let Some((base_t, _)) = autoderef.next() {
match base_t.sty { match base_t.sty {
ty::TyAdt(base_def, substs) if !base_def.is_enum() => { ty::TyAdt(base_def, substs) if !base_def.is_enum() => {
debug!("struct named {:?}", base_t); debug!("struct named {:?}", base_t);
@ -2922,8 +2907,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
if let Some(field) = fields.iter().find(|f| f.name.to_ident() == ident) { if let Some(field) = fields.iter().find(|f| f.name.to_ident() == ident) {
let field_ty = self.field_ty(expr.span, field, substs); let field_ty = self.field_ty(expr.span, field, substs);
if field.vis.is_accessible_from(def_scope, self.tcx) { if field.vis.is_accessible_from(def_scope, self.tcx) {
autoderef.finalize(lvalue_pref, base); let autoderefs = autoderef.adjust_steps(lvalue_pref);
self.apply_autoderef_adjustment(base.id, autoderefs, base_t); self.apply_autoderef_adjustment(base.id, autoderefs, base_t);
autoderef.finalize();
self.tcx.check_stability(field.did, expr.id, expr.span); self.tcx.check_stability(field.did, expr.id, expr.span);
@ -3020,7 +3006,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let mut private_candidate = None; let mut private_candidate = None;
let mut tuple_like = false; let mut tuple_like = false;
let mut autoderef = self.autoderef(expr.span, expr_t); let mut autoderef = self.autoderef(expr.span, expr_t);
while let Some((base_t, autoderefs)) = autoderef.next() { while let Some((base_t, _)) = autoderef.next() {
let field = match base_t.sty { let field = match base_t.sty {
ty::TyAdt(base_def, substs) if base_def.is_struct() => { ty::TyAdt(base_def, substs) if base_def.is_struct() => {
tuple_like = base_def.struct_variant().ctor_kind == CtorKind::Fn; tuple_like = base_def.struct_variant().ctor_kind == CtorKind::Fn;
@ -3055,8 +3041,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}; };
if let Some(field_ty) = field { if let Some(field_ty) = field {
autoderef.finalize(lvalue_pref, base); let autoderefs = autoderef.adjust_steps(lvalue_pref);
self.apply_autoderef_adjustment(base.id, autoderefs, base_t); self.apply_autoderef_adjustment(base.id, autoderefs, base_t);
autoderef.finalize();
return field_ty; return field_ty;
} }
} }
@ -3470,11 +3457,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
if let Some(mt) = oprnd_t.builtin_deref(true, NoPreference) { if let Some(mt) = oprnd_t.builtin_deref(true, NoPreference) {
oprnd_t = mt.ty; oprnd_t = mt.ty;
} else if let Some(ok) = self.try_overloaded_deref( } else if let Some(ok) = self.try_overloaded_deref(
expr.span, Some(&oprnd), oprnd_t, lvalue_pref) { expr.span, oprnd_t, lvalue_pref) {
let method = self.register_infer_ok_obligations(ok); let (autoref, method) = self.register_infer_ok_obligations(ok);
self.apply_adjustment(oprnd.id, Adjustment {
kind: Adjust::DerefRef {
autoderefs: vec![],
autoref,
unsize: false
},
target: *method.ty.fn_sig().input(0).skip_binder()
});
oprnd_t = self.make_overloaded_lvalue_return_type(method).ty; oprnd_t = self.make_overloaded_lvalue_return_type(method).ty;
self.tables.borrow_mut().method_map.insert(MethodCall::expr(expr.id), self.tables.borrow_mut().method_map.insert(expr.id, method);
method);
} else { } else {
self.type_error_message(expr.span, |actual| { self.type_error_message(expr.span, |actual| {
format!("type `{}` cannot be \ format!("type `{}` cannot be \

View file

@ -14,6 +14,7 @@ use super::FnCtxt;
use hir::def_id::DefId; use hir::def_id::DefId;
use rustc::ty::{Ty, TypeFoldable, PreferMutLvalue, TypeVariants}; use rustc::ty::{Ty, TypeFoldable, PreferMutLvalue, TypeVariants};
use rustc::ty::TypeVariants::{TyStr, TyRef}; use rustc::ty::TypeVariants::{TyStr, TyRef};
use rustc::ty::adjustment::{Adjustment, Adjust};
use rustc::infer::type_variable::TypeVariableOrigin; use rustc::infer::type_variable::TypeVariableOrigin;
use errors; use errors;
use syntax::ast; use syntax::ast;
@ -184,7 +185,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// particularly for things like `String + &String`. // particularly for things like `String + &String`.
let rhs_ty_var = self.next_ty_var(TypeVariableOrigin::MiscVariable(rhs_expr.span)); let rhs_ty_var = self.next_ty_var(TypeVariableOrigin::MiscVariable(rhs_expr.span));
let return_ty = self.lookup_op_method(expr, lhs_ty, vec![rhs_ty_var], let return_ty = self.lookup_op_method(expr, lhs_ty, &[rhs_ty_var],
Symbol::intern(name), trait_def_id, Symbol::intern(name), trait_def_id,
lhs_expr); lhs_expr);
@ -214,7 +215,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
if let TypeVariants::TyRef(_, ref ty_mut) = lhs_ty.sty { if let TypeVariants::TyRef(_, ref ty_mut) = lhs_ty.sty {
if !self.infcx.type_moves_by_default(ty_mut.ty, lhs_expr.span) && if !self.infcx.type_moves_by_default(ty_mut.ty, lhs_expr.span) &&
self.lookup_op_method(expr, ty_mut.ty, vec![rhs_ty], self.lookup_op_method(expr, ty_mut.ty, &[rhs_ty],
Symbol::intern(name), trait_def_id, Symbol::intern(name), trait_def_id,
lhs_expr).is_ok() { lhs_expr).is_ok() {
err.note( err.note(
@ -313,7 +314,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
{ {
assert!(op.is_by_value()); assert!(op.is_by_value());
let mname = Symbol::intern(mname); let mname = Symbol::intern(mname);
match self.lookup_op_method(ex, operand_ty, vec![], mname, trait_did, operand_expr) { match self.lookup_op_method(ex, operand_ty, &[], mname, trait_did, operand_expr) {
Ok(t) => t, Ok(t) => t,
Err(()) => { Err(()) => {
let actual = self.resolve_type_vars_if_possible(&operand_ty); let actual = self.resolve_type_vars_if_possible(&operand_ty);
@ -382,7 +383,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn lookup_op_method(&self, fn lookup_op_method(&self,
expr: &'gcx hir::Expr, expr: &'gcx hir::Expr,
lhs_ty: Ty<'tcx>, lhs_ty: Ty<'tcx>,
other_tys: Vec<Ty<'tcx>>, other_tys: &[Ty<'tcx>],
opname: ast::Name, opname: ast::Name,
trait_did: Option<DefId>, trait_did: Option<DefId>,
lhs_expr: &'a hir::Expr) lhs_expr: &'a hir::Expr)
@ -398,11 +399,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let method = match trait_did { let method = match trait_did {
Some(trait_did) => { Some(trait_did) => {
let lhs_expr = Some(super::AdjustedRcvr {
rcvr_expr: lhs_expr, autoderefs: 0, unsize: false
});
self.lookup_method_in_trait_adjusted(expr.span, self.lookup_method_in_trait_adjusted(expr.span,
lhs_expr,
opname, opname,
trait_did, trait_did,
lhs_ty, lhs_ty,
@ -413,18 +410,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
match method { match method {
Some(ok) => { Some(ok) => {
let method = self.register_infer_ok_obligations(ok); let (autoref, method) = self.register_infer_ok_obligations(ok);
self.select_obligations_where_possible(); self.select_obligations_where_possible();
let method_ty = method.ty; self.apply_adjustment(lhs_expr.id, Adjustment {
kind: Adjust::DerefRef {
// HACK(eddyb) Fully qualified path to work around a resolve bug. autoderefs: vec![],
let method_call = ::rustc::ty::MethodCall::expr(expr.id); autoref,
self.tables.borrow_mut().method_map.insert(method_call, method); unsize: false
},
target: *method.ty.fn_sig().input(0).skip_binder()
});
self.tables.borrow_mut().method_map.insert(expr.id, method);
// extract return type for method; all late bound regions // extract return type for method; all late bound regions
// should have been instantiated by now // should have been instantiated by now
let ret_ty = method_ty.fn_ret(); let ret_ty = method.ty.fn_ret();
Ok(self.tcx.no_late_bound_regions(&ret_ty).unwrap()) Ok(self.tcx.no_late_bound_regions(&ret_ty).unwrap())
} }
None => { None => {

View file

@ -91,7 +91,7 @@ use middle::region::{CodeExtent, RegionMaps};
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
use rustc::ty::subst::Substs; use rustc::ty::subst::Substs;
use rustc::traits; use rustc::traits;
use rustc::ty::{self, Ty, MethodCall, TypeFoldable}; use rustc::ty::{self, Ty, TypeFoldable};
use rustc::infer::{self, GenericKind, SubregionOrigin, VerifyBound}; use rustc::infer::{self, GenericKind, SubregionOrigin, VerifyBound};
use rustc::ty::adjustment; use rustc::ty::adjustment;
use rustc::ty::wf::ImpliedBound; use rustc::ty::wf::ImpliedBound;
@ -520,8 +520,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> {
self.type_must_outlive(infer::ExprTypeIsNotInScope(expr_ty, expr.span), self.type_must_outlive(infer::ExprTypeIsNotInScope(expr_ty, expr.span),
expr_ty, expr_region); expr_ty, expr_region);
let method_call = MethodCall::expr(expr.id); let opt_method_callee = self.tables.borrow().method_map.get(&expr.id).cloned();
let opt_method_callee = self.tables.borrow().method_map.get(&method_call).cloned();
let has_method_map = opt_method_callee.is_some(); let has_method_map = opt_method_callee.is_some();
// If we are calling a method (either explicitly or via an // If we are calling a method (either explicitly or via an
@ -548,11 +547,10 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> {
if let Some(adjustment) = adjustment { if let Some(adjustment) = adjustment {
debug!("adjustment={:?}", adjustment); debug!("adjustment={:?}", adjustment);
match adjustment.kind { match adjustment.kind {
adjustment::Adjust::DerefRef { autoderefs, ref autoref, .. } => { adjustment::Adjust::DerefRef { ref autoderefs, ref autoref, .. } => {
let expr_ty = self.resolve_node_type(expr.id); let cmt = ignore_err!(self.constrain_autoderefs(expr, autoderefs));
self.constrain_autoderefs(expr, autoderefs, expr_ty);
if let Some(ref autoref) = *autoref { if let Some(ref autoref) = *autoref {
self.link_autoref(expr, autoderefs, autoref); self.link_autoref(expr, cmt, autoref);
// Require that the resulting region encompasses // Require that the resulting region encompasses
// the current node. // the current node.
@ -690,8 +688,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> {
hir::ExprUnary(hir::UnDeref, ref base) => { hir::ExprUnary(hir::UnDeref, ref base) => {
// For *a, the lifetime of a must enclose the deref // For *a, the lifetime of a must enclose the deref
let method_call = MethodCall::expr(expr.id); let base_ty = match self.tables.borrow().method_map.get(&expr.id) {
let base_ty = match self.tables.borrow().method_map.get(&method_call) {
Some(method) => { Some(method) => {
self.constrain_call(expr, Some(&base), self.constrain_call(expr, Some(&base),
None::<hir::Expr>.iter(), true); None::<hir::Expr>.iter(), true);
@ -914,79 +911,68 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
/// dereferenced, the lifetime of the pointer includes the deref expr. /// dereferenced, the lifetime of the pointer includes the deref expr.
fn constrain_autoderefs(&mut self, fn constrain_autoderefs(&mut self,
deref_expr: &hir::Expr, deref_expr: &hir::Expr,
derefs: usize, autoderefs: &[Option<ty::MethodCallee<'tcx>>])
mut derefd_ty: Ty<'tcx>) -> mc::McResult<mc::cmt<'tcx>>
{ {
debug!("constrain_autoderefs(deref_expr={:?}, derefs={}, derefd_ty={:?})", debug!("constrain_autoderefs(deref_expr={:?}, autoderefs={:?})",
deref_expr, deref_expr,
derefs, autoderefs);
derefd_ty);
let mut cmt = {
let mc = mc::MemCategorizationContext::new(self, &self.region_maps);
mc.cat_expr_unadjusted(deref_expr)?
};
let r_deref_expr = self.tcx.node_scope_region(deref_expr.id); let r_deref_expr = self.tcx.node_scope_region(deref_expr.id);
for i in 0..derefs { for &overloaded in autoderefs {
let method_call = MethodCall::autoderef(deref_expr.id, i as u32); if let Some(method) = overloaded {
debug!("constrain_autoderefs: method_call={:?} (of {:?} total)", method_call, derefs); debug!("constrain_autoderefs: overloaded, method={:?}", method);
let method = self.tables.borrow().method_map.get(&method_call).map(|m| m.clone()); let origin = infer::ParameterOrigin::OverloadedDeref;
self.substs_wf_in_scope(origin, method.substs, deref_expr.span, r_deref_expr);
derefd_ty = match method { // Treat overloaded autoderefs as if an AutoBorrow adjustment
Some(method) => { // was applied on the base type, as that is always the case.
debug!("constrain_autoderefs: #{} is overloaded, method={:?}", let fn_sig = method.ty.fn_sig();
i, method); let fn_sig = // late-bound regions should have been instantiated
self.tcx.no_late_bound_regions(&fn_sig).unwrap();
let origin = infer::ParameterOrigin::OverloadedDeref; let self_ty = fn_sig.inputs()[0];
self.substs_wf_in_scope(origin, method.substs, deref_expr.span, r_deref_expr); let (m, r) = match self_ty.sty {
ty::TyRef(r, ref m) => (m.mutbl, r),
// Treat overloaded autoderefs as if an AutoBorrow adjustment _ => {
// was applied on the base type, as that is always the case. span_bug!(
let fn_sig = method.ty.fn_sig(); deref_expr.span,
let fn_sig = // late-bound regions should have been instantiated "bad overloaded deref type {:?}",
self.tcx.no_late_bound_regions(&fn_sig).unwrap(); method.ty)
let self_ty = fn_sig.inputs()[0];
let (m, r) = match self_ty.sty {
ty::TyRef(r, ref m) => (m.mutbl, r),
_ => {
span_bug!(
deref_expr.span,
"bad overloaded deref type {:?}",
method.ty)
}
};
debug!("constrain_autoderefs: receiver r={:?} m={:?}",
r, m);
{
let mc = mc::MemCategorizationContext::new(self, &self.region_maps);
let self_cmt = ignore_err!(mc.cat_expr_autoderefd(deref_expr, i));
debug!("constrain_autoderefs: self_cmt={:?}",
self_cmt);
self.link_region(deref_expr.span, r,
ty::BorrowKind::from_mutbl(m), self_cmt);
} }
};
// Specialized version of constrain_call. debug!("constrain_autoderefs: receiver r={:?} m={:?}",
self.type_must_outlive(infer::CallRcvr(deref_expr.span), r, m);
self_ty, r_deref_expr);
self.type_must_outlive(infer::CallReturn(deref_expr.span),
fn_sig.output(), r_deref_expr);
fn_sig.output()
}
None => derefd_ty
};
if let ty::TyRef(r_ptr, _) = derefd_ty.sty { debug!("constrain_autoderefs: self_cmt={:?}", cmt);
self.link_region(deref_expr.span, r,
ty::BorrowKind::from_mutbl(m), cmt.clone());
// Specialized version of constrain_call.
self.type_must_outlive(infer::CallRcvr(deref_expr.span),
self_ty, r_deref_expr);
self.type_must_outlive(infer::CallReturn(deref_expr.span),
fn_sig.output(), r_deref_expr);
}
{
let mc = mc::MemCategorizationContext::new(self, &self.region_maps);
cmt = mc.cat_deref(deref_expr, cmt, overloaded)?;
}
if let Categorization::Deref(_, mc::BorrowedPtr(_, r_ptr)) = cmt.cat {
self.mk_subregion_due_to_dereference(deref_expr.span, self.mk_subregion_due_to_dereference(deref_expr.span,
r_deref_expr, r_ptr); r_deref_expr, r_ptr);
} }
match derefd_ty.builtin_deref(true, ty::NoPreference) {
Some(mt) => derefd_ty = mt.ty,
/* if this type can't be dereferenced, then there's already an error
in the session saying so. Just bail out for now */
None => break
}
} }
Ok(cmt)
} }
pub fn mk_subregion_due_to_dereference(&mut self, pub fn mk_subregion_due_to_dereference(&mut self,
@ -1151,13 +1137,10 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
/// autoref'd. /// autoref'd.
fn link_autoref(&self, fn link_autoref(&self,
expr: &hir::Expr, expr: &hir::Expr,
autoderefs: usize, expr_cmt: mc::cmt<'tcx>,
autoref: &adjustment::AutoBorrow<'tcx>) autoref: &adjustment::AutoBorrow<'tcx>)
{ {
debug!("link_autoref(autoderefs={}, autoref={:?})", autoderefs, autoref); debug!("link_autoref(autoref={:?}, expr_cmt={:?})", autoref, expr_cmt);
let mc = mc::MemCategorizationContext::new(self, &self.region_maps);
let expr_cmt = ignore_err!(mc.cat_expr_autoderefd(expr, autoderefs));
debug!("expr_cmt={:?}", expr_cmt);
match *autoref { match *autoref {
adjustment::AutoBorrow::Ref(r, m) => { adjustment::AutoBorrow::Ref(r, m) => {

View file

@ -216,9 +216,9 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
let closure_def_id = self.fcx.tcx.hir.local_def_id(id); let closure_def_id = self.fcx.tcx.hir.local_def_id(id);
debug!("closure_kind({:?}) = {:?}", closure_def_id, kind); debug!("closure_kind({:?}) = {:?}", closure_def_id, kind);
let mut deferred_call_resolutions = let deferred_call_resolutions =
self.fcx.remove_deferred_call_resolutions(closure_def_id); self.fcx.remove_deferred_call_resolutions(closure_def_id);
for deferred_call_resolution in &mut deferred_call_resolutions { for deferred_call_resolution in deferred_call_resolutions {
deferred_call_resolution.resolve(self.fcx); deferred_call_resolution.resolve(self.fcx);
} }
} }

View file

@ -16,7 +16,7 @@ use check::FnCtxt;
use rustc::hir; use rustc::hir;
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
use rustc::infer::{InferCtxt}; use rustc::infer::{InferCtxt};
use rustc::ty::{self, Ty, TyCtxt, MethodCall, MethodCallee}; use rustc::ty::{self, Ty, TyCtxt, MethodCallee};
use rustc::ty::adjustment; use rustc::ty::adjustment;
use rustc::ty::fold::{TypeFolder,TypeFoldable}; use rustc::ty::fold::{TypeFolder,TypeFoldable};
use rustc::util::nodemap::DefIdSet; use rustc::util::nodemap::DefIdSet;
@ -106,7 +106,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
let inner_ty = self.fcx.resolve_type_vars_if_possible(&inner_ty); let inner_ty = self.fcx.resolve_type_vars_if_possible(&inner_ty);
if inner_ty.is_scalar() { if inner_ty.is_scalar() {
self.fcx.tables.borrow_mut().method_map.remove(&MethodCall::expr(e.id)); self.fcx.tables.borrow_mut().method_map.remove(&e.id);
} }
} }
hir::ExprBinary(ref op, ref lhs, ref rhs) | hir::ExprBinary(ref op, ref lhs, ref rhs) |
@ -118,7 +118,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
let rhs_ty = self.fcx.resolve_type_vars_if_possible(&rhs_ty); let rhs_ty = self.fcx.resolve_type_vars_if_possible(&rhs_ty);
if lhs_ty.is_scalar() && rhs_ty.is_scalar() { if lhs_ty.is_scalar() && rhs_ty.is_scalar() {
self.fcx.tables.borrow_mut().method_map.remove(&MethodCall::expr(e.id)); self.fcx.tables.borrow_mut().method_map.remove(&e.id);
// weird but true: the by-ref binops put an // weird but true: the by-ref binops put an
// adjustment on the lhs but not the rhs; the // adjustment on the lhs but not the rhs; the
@ -164,7 +164,7 @@ impl<'cx, 'gcx, 'tcx> Visitor<'gcx> for WritebackCx<'cx, 'gcx, 'tcx> {
self.fix_scalar_builtin_expr(e); self.fix_scalar_builtin_expr(e);
self.visit_node_id(e.span, e.id); self.visit_node_id(e.span, e.id);
self.visit_method_map_entry(e.span, MethodCall::expr(e.id)); self.visit_method_map_entry(e.span, e.id);
if let hir::ExprClosure(_, _, body, _) = e.node { if let hir::ExprClosure(_, _, body, _) = e.node {
let body = self.fcx.tcx.hir.body(body); let body = self.fcx.tcx.hir.body(body);
@ -335,13 +335,16 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
} }
adjustment::Adjust::DerefRef { autoderefs, autoref, unsize } => { adjustment::Adjust::DerefRef { autoderefs, autoref, unsize } => {
for autoderef in 0..autoderefs {
let method_call = MethodCall::autoderef(node_id, autoderef as u32);
self.visit_method_map_entry(span, method_call);
}
adjustment::Adjust::DerefRef { adjustment::Adjust::DerefRef {
autoderefs: autoderefs, autoderefs: autoderefs.iter().map(|overloaded| {
overloaded.map(|method| {
MethodCallee {
def_id: method.def_id,
ty: self.resolve(&method.ty, &span),
substs: self.resolve(&method.substs, &span),
}
})
}).collect(),
autoref: self.resolve(&autoref, &span), autoref: self.resolve(&autoref, &span),
unsize: unsize, unsize: unsize,
} }
@ -359,27 +362,22 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
fn visit_method_map_entry(&mut self, fn visit_method_map_entry(&mut self,
method_span: Span, method_span: Span,
method_call: MethodCall) { node_id: ast::NodeId) {
// Resolve any method map entry // Resolve any method map entry
let new_method = match self.fcx.tables.borrow_mut().method_map.remove(&method_call) { let new_method = match self.fcx.tables.borrow_mut().method_map.remove(&node_id) {
Some(method) => { Some(method) => {
debug!("writeback::resolve_method_map_entry(call={:?}, entry={:?})", Some(MethodCallee {
method_call,
method);
let new_method = MethodCallee {
def_id: method.def_id, def_id: method.def_id,
ty: self.resolve(&method.ty, &method_span), ty: self.resolve(&method.ty, &method_span),
substs: self.resolve(&method.substs, &method_span), substs: self.resolve(&method.substs, &method_span),
}; })
Some(new_method)
} }
None => None None => None
}; };
//NB(jroesch): We need to match twice to avoid a double borrow which would cause an ICE //NB(jroesch): We need to match twice to avoid a double borrow which would cause an ICE
if let Some(method) = new_method { if let Some(method) = new_method {
self.tables.method_map.insert(method_call, method); self.tables.method_map.insert(node_id, method);
} }
} }