From ad91f19957b62bb19fce7d400cf2c94e33da153d Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Mon, 22 Feb 2016 14:06:19 +0200 Subject: [PATCH] typeck: Support multiple expressions getting coerced at the same type. --- src/librustc_typeck/check/callee.rs | 2 +- src/librustc_typeck/check/coercion.rs | 121 ++++++++------ src/librustc_typeck/check/method/confirm.rs | 166 ++++++++++---------- src/librustc_typeck/check/method/probe.rs | 2 +- src/librustc_typeck/check/method/suggest.rs | 2 +- src/librustc_typeck/check/mod.rs | 154 +++++++++--------- 6 files changed, 228 insertions(+), 219 deletions(-) diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index fde2283d6e2..bf60f435a22 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -82,7 +82,7 @@ pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, autoderef(fcx, callee_expr.span, original_callee_ty, - Some(callee_expr), + || Some(callee_expr), UnresolvedTypeAction::Error, LvaluePreference::NoPreference, |adj_ty, idx| { diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 880d50eddd6..42aaea9db5f 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -87,6 +87,14 @@ struct Coerce<'a, 'tcx: 'a> { type CoerceResult<'tcx> = RelateResult<'tcx, Option>>; impl<'f, 'tcx> Coerce<'f, 'tcx> { + fn new(fcx: &'a FnCtxt<'a, 'tcx>, origin: TypeOrigin) -> Self { + Coerce { + fcx: fcx, + origin: origin, + unsizing_obligations: RefCell::new(vec![]) + } + } + fn tcx(&self) -> &TyCtxt<'tcx> { self.fcx.tcx() } @@ -96,16 +104,17 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { Ok(None) // No coercion required. } - fn coerce(&self, - expr_a: &hir::Expr, - a: Ty<'tcx>, - b: Ty<'tcx>) - -> CoerceResult<'tcx> { - debug!("Coerce.tys({:?} => {:?})", - a, - b); + fn coerce<'a, E, I>(&self, + exprs: &E, + a: Ty<'tcx>, + b: Ty<'tcx>) + -> CoerceResult<'tcx> + // FIXME(eddyb) use copyable iterators when that becomes ergonomic. + where E: Fn() -> I, + I: IntoIterator { let a = self.fcx.infcx().shallow_resolve(a); + debug!("Coerce.tys({:?} => {:?})", a, b); // Just ignore error types. if a.references_error() || b.references_error() { @@ -156,15 +165,18 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`. /// To match `A` with `B`, autoderef will be performed, /// calling `deref`/`deref_mut` where necessary. - fn coerce_borrowed_pointer(&self, - expr_a: &hir::Expr, - a: Ty<'tcx>, - b: Ty<'tcx>, - mutbl_b: hir::Mutability) - -> CoerceResult<'tcx> { - debug!("coerce_borrowed_pointer(a={:?}, b={:?})", - a, - b); + fn coerce_borrowed_pointer<'a, E, I>(&self, + span: Span, + exprs: &E, + a: Ty<'tcx>, + b: Ty<'tcx>, + mutbl_b: hir::Mutability) + -> CoerceResult<'tcx> + // FIXME(eddyb) use copyable iterators when that becomes ergonomic. + where E: Fn() -> I, + I: IntoIterator { + + debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b); // If we have a parameter of type `&M T_a` and the value // provided is `expr`, we will be adding an implicit borrow, @@ -179,17 +191,15 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { _ => return self.subtype(a, b) } - let coercion = Coercion(self.origin.span()); + let span = self.origin.span(); + let coercion = Coercion(span); let r_borrow = self.fcx.infcx().next_region_var(coercion); let r_borrow = self.tcx().mk_region(r_borrow); let autoref = Some(AutoPtr(r_borrow, mutbl_b)); let lvalue_pref = LvaluePreference::from_mutbl(mutbl_b); let mut first_error = None; - let (_, autoderefs, success) = autoderef(self.fcx, - expr_a.span, - a, - Some(expr_a), + let (_, autoderefs, success) = autoderef(self.fcx, span, a, exprs, UnresolvedTypeAction::Ignore, lvalue_pref, |inner_ty, autoderef| { @@ -323,9 +333,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } - let mut obligations = self.unsizing_obligations.borrow_mut(); - assert!(obligations.is_empty()); - *obligations = leftover_predicates; + *self.unsizing_obligations.borrow_mut() = leftover_predicates; let adjustment = AutoDerefRef { autoderefs: if reborrow.is_some() { 1 } else { 0 }, @@ -425,39 +433,48 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } +fn apply<'a, 'b, 'tcx, E, I>(coerce: &mut Coerce<'a, 'tcx>, + exprs: &E, + a: Ty<'tcx>, + b: Ty<'tcx>) + -> RelateResult<'tcx, Ty<'tcx>> + where E: Fn() -> I, + I: IntoIterator { + + let (ty, adjustment) = try!(indent(|| coerce.coerce(exprs, a, b))); + + let fcx = coerce.fcx; + if let AdjustDerefRef(auto) = adjustment { + if auto.unsize.is_some() { + for obligation in coerce.unsizing_obligations.borrow_mut().drain() { + fcx.register_predicate(obligation); + } + } + } + + if !adjustment.is_identity() { + debug!("Success, coerced with {:?}", adjustment); + for expr in exprs() { + assert!(!fcx.inh.tables.borrow().adjustments.contains(&expr.id)); + fcx.write_adjustment(expr.id, adjustment); + } + } + Ok(ty) +} + +/// Attempt to coerce an expression from a type (a) to another type (b). +/// Adjustments are only recorded if the coercion was successful. +/// The expressions *must not* have any pre-existing adjustments. pub fn try<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr: &hir::Expr, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { debug!("coercion::try({:?} -> {:?})", a, b); - let mut unsizing_obligations = vec![]; - let adjustment = try!(indent(|| { - fcx.infcx().commit_if_ok(|_| { - let coerce = Coerce { - fcx: fcx, - origin: TypeOrigin::ExprAssignable(expr.span), - unsizing_obligations: RefCell::new(vec![]) - }; - let adjustment = try!(coerce.coerce(expr, a, b)); - unsizing_obligations = coerce.unsizing_obligations.into_inner(); - Ok(adjustment) - }) - })); - - if let Some(AdjustDerefRef(auto)) = adjustment { - if auto.unsize.is_some() { - for obligation in unsizing_obligations { - fcx.register_predicate(obligation); - } - } - } - - if let Some(adjustment) = adjustment { - debug!("Success, coerced with {:?}", adjustment); - fcx.write_adjustment(expr.id, adjustment); - } - Ok(()) + let mut coerce = Coerce::new(fcx, TypeOrigin::ExprAssignable(expr.span)); + fcx.infcx().commit_if_ok(|_| { + apply(&mut coerce, &|| Some(expr), a, b) + }).map(|_| ()) } fn coerce_mutbls<'tcx>(from_mutbl: hir::Mutability, diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 7b709b93ed8..f4268deee37 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -158,7 +158,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { let (autoderefd_ty, n, result) = check::autoderef(self.fcx, self.span, unadjusted_self_ty, - Some(self.self_expr), + || Some(self.self_expr), UnresolvedTypeAction::Error, NoPreference, |_, n| { @@ -287,7 +287,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { let (_, _, result) = check::autoderef(self.fcx, self.span, self_ty, - None, + || None, UnresolvedTypeAction::Error, NoPreference, |ty, _| { @@ -509,7 +509,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { check::autoderef(self.fcx, expr.span, self.fcx.expr_ty(expr), - Some(expr), + || Some(expr), UnresolvedTypeAction::Error, PreferMutLvalue, |_, autoderefs| { @@ -522,92 +522,94 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { } // Don't retry the first one or we might infinite loop! - if i != 0 { - match expr.node { - hir::ExprIndex(ref base_expr, ref index_expr) => { - // If this is an overloaded index, the - // adjustment will include an extra layer of - // autoref because the method is an &self/&mut - // self method. We have to peel it off to get - // the raw adjustment that `try_index_step` - // expects. This is annoying and horrible. We - // ought to recode this routine so it doesn't - // (ab)use the normal type checking paths. - let adj = self.fcx.inh.tables.borrow().adjustments.get(&base_expr.id) - .cloned(); - let (autoderefs, unsize) = match adj { - Some(AdjustDerefRef(adr)) => match adr.autoref { - None => { - assert!(adr.unsize.is_none()); - (adr.autoderefs, None) - } - Some(AutoPtr(_, _)) => { - (adr.autoderefs, adr.unsize.map(|target| { - target.builtin_deref(false, NoPreference) - .expect("fixup: AutoPtr is not &T").ty - })) - } - Some(_) => { - self.tcx().sess.span_bug( - base_expr.span, - &format!("unexpected adjustment autoref {:?}", - adr)); - } - }, - None => (0, None), + if i == 0 { + continue; + } + match expr.node { + hir::ExprIndex(ref base_expr, ref index_expr) => { + // If this is an overloaded index, the + // adjustment will include an extra layer of + // autoref because the method is an &self/&mut + // self method. We have to peel it off to get + // the raw adjustment that `try_index_step` + // expects. This is annoying and horrible. We + // ought to recode this routine so it doesn't + // (ab)use the normal type checking paths. + let adj = self.fcx.inh.tables.borrow().adjustments.get(&base_expr.id) + .cloned(); + let (autoderefs, unsize) = match adj { + Some(AdjustDerefRef(adr)) => match adr.autoref { + None => { + assert!(adr.unsize.is_none()); + (adr.autoderefs, None) + } + Some(AutoPtr(_, _)) => { + (adr.autoderefs, adr.unsize.map(|target| { + target.builtin_deref(false, NoPreference) + .expect("fixup: AutoPtr is not &T").ty + })) + } Some(_) => { self.tcx().sess.span_bug( base_expr.span, - "unexpected adjustment type"); + &format!("unexpected adjustment autoref {:?}", + adr)); } - }; - - let (adjusted_base_ty, unsize) = if let Some(target) = unsize { - (target, true) - } else { - (self.fcx.adjust_expr_ty(base_expr, - Some(&AdjustDerefRef(AutoDerefRef { - autoderefs: autoderefs, - autoref: None, - unsize: None - }))), false) - }; - let index_expr_ty = self.fcx.expr_ty(&index_expr); - - let result = check::try_index_step( - self.fcx, - ty::MethodCall::expr(expr.id), - expr, - &base_expr, - adjusted_base_ty, - autoderefs, - unsize, - PreferMutLvalue, - index_expr_ty); - - if let Some((input_ty, return_ty)) = result { - demand::suptype(self.fcx, index_expr.span, input_ty, index_expr_ty); - - let expr_ty = self.fcx.expr_ty(&expr); - demand::suptype(self.fcx, expr.span, expr_ty, return_ty); + }, + None => (0, None), + Some(_) => { + self.tcx().sess.span_bug( + base_expr.span, + "unexpected adjustment type"); } + }; + + let (adjusted_base_ty, unsize) = if let Some(target) = unsize { + (target, true) + } else { + (self.fcx.adjust_expr_ty(base_expr, + Some(&AdjustDerefRef(AutoDerefRef { + autoderefs: autoderefs, + autoref: None, + unsize: None + }))), false) + }; + let index_expr_ty = self.fcx.expr_ty(&index_expr); + + let result = check::try_index_step( + self.fcx, + ty::MethodCall::expr(expr.id), + expr, + &base_expr, + adjusted_base_ty, + autoderefs, + unsize, + PreferMutLvalue, + index_expr_ty); + + if let Some((input_ty, return_ty)) = result { + demand::suptype(self.fcx, index_expr.span, input_ty, index_expr_ty); + + let expr_ty = self.fcx.expr_ty(&expr); + demand::suptype(self.fcx, expr.span, expr_ty, return_ty); } - hir::ExprUnary(hir::UnDeref, ref base_expr) => { - // if this is an overloaded deref, then re-evaluate with - // a preference for mut - let method_call = ty::MethodCall::expr(expr.id); - if self.fcx.inh.tables.borrow().method_map.contains_key(&method_call) { - check::try_overloaded_deref( - self.fcx, - expr.span, - Some(method_call), - Some(&base_expr), - self.fcx.expr_ty(&base_expr), - PreferMutLvalue); - } - } - _ => {} } + hir::ExprUnary(hir::UnDeref, ref base_expr) => { + // if this is an overloaded deref, then re-evaluate with + // a preference for mut + let method_call = ty::MethodCall::expr(expr.id); + if self.fcx.inh.tables.borrow().method_map.contains_key(&method_call) { + let method = check::try_overloaded_deref( + self.fcx, + expr.span, + Some(&base_expr), + self.fcx.expr_ty(&base_expr), + PreferMutLvalue); + let method = method.expect("re-trying deref failed"); + self.fcx.inh.tables.borrow_mut().method_map.insert(method_call, method); + } + } + _ => {} } } } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 4a8a893cc46..d11a07cb41f 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -200,7 +200,7 @@ fn create_steps<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, let (final_ty, dereferences, _) = check::autoderef(fcx, span, self_ty, - None, + || None, UnresolvedTypeAction::Error, NoPreference, |t, d| { diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index e6336696663..7dc9d46c303 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -351,7 +351,7 @@ fn type_derefs_to_local<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, return is_local(fcx.resolve_type_vars_if_possible(rcvr_ty)); } - check::autoderef(fcx, span, rcvr_ty, None, + check::autoderef(fcx, span, rcvr_ty, || None, check::UnresolvedTypeAction::Ignore, ty::NoPreference, |ty, _| { if is_local(ty) { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 1c0430f0b7c..311b6a9772c 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2053,20 +2053,21 @@ pub enum UnresolvedTypeAction { /// /// Note: this method does not modify the adjustments table. The caller is responsible for /// inserting an AutoAdjustment record into the `fcx` using one of the suitable methods. -pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>, - sp: Span, - base_ty: Ty<'tcx>, - opt_expr: Option<&hir::Expr>, - unresolved_type_action: UnresolvedTypeAction, - mut lvalue_pref: LvaluePreference, - mut should_stop: F) - -> (Ty<'tcx>, usize, Option) - where F: FnMut(Ty<'tcx>, usize) -> Option, +pub fn autoderef<'a, 'b, 'tcx, E, I, T, F>(fcx: &FnCtxt<'a, 'tcx>, + sp: Span, + base_ty: Ty<'tcx>, + maybe_exprs: E, + unresolved_type_action: UnresolvedTypeAction, + mut lvalue_pref: LvaluePreference, + mut should_stop: F) + -> (Ty<'tcx>, usize, Option) + // FIXME(eddyb) use copyable iterators when that becomes ergonomic. + where E: Fn() -> I, + I: IntoIterator, + F: FnMut(Ty<'tcx>, usize) -> Option, { - debug!("autoderef(base_ty={:?}, opt_expr={:?}, lvalue_pref={:?})", - base_ty, - opt_expr, - lvalue_pref); + debug!("autoderef(base_ty={:?}, lvalue_pref={:?})", + base_ty, lvalue_pref); let mut t = base_ty; for autoderefs in 0..fcx.tcx().sess.recursion_limit.get() { @@ -2092,34 +2093,34 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>, } // Otherwise, deref if type is derefable: - let mt = match resolved_t.builtin_deref(false, lvalue_pref) { - Some(mt) => Some(mt), - None => { - let method_call = - opt_expr.map(|expr| MethodCall::autoderef(expr.id, autoderefs as u32)); - // Super subtle: it might seem as though we should - // pass `opt_expr` to `try_overloaded_deref`, so that - // the (implicit) autoref of using an overloaded deref - // would get added to the adjustment table. However we - // do not do that, because it's kind of a - // "meta-adjustment" -- instead, we just leave it - // unrecorded and know that there "will be" an - // autoref. regionck and other bits of the code base, - // when they encounter an overloaded autoderef, have - // to do some reconstructive surgery. This is a pretty - // complex mess that is begging for a proper MIR. - try_overloaded_deref(fcx, sp, method_call, None, resolved_t, lvalue_pref) + // Super subtle: it might seem as though we should + // pass `opt_expr` to `try_overloaded_deref`, so that + // the (implicit) autoref of using an overloaded deref + // would get added to the adjustment table. However we + // do not do that, because it's kind of a + // "meta-adjustment" -- instead, we just leave it + // unrecorded and know that there "will be" an + // autoref. regionck and other bits of the code base, + // when they encounter an overloaded autoderef, have + // to do some reconstructive surgery. This is a pretty + // complex mess that is begging for a proper MIR. + let mt = if let Some(mt) = resolved_t.builtin_deref(false, lvalue_pref) { + mt + } else if let Some(method) = try_overloaded_deref(fcx, sp, None, + resolved_t, lvalue_pref) { + for expr in maybe_exprs() { + let method_call = MethodCall::autoderef(expr.id, autoderefs as u32); + fcx.inh.tables.borrow_mut().method_map.insert(method_call, method); } + make_overloaded_lvalue_return_type(fcx.tcx(), method) + } else { + return (resolved_t, autoderefs, None); }; - match mt { - Some(mt) => { - t = mt.ty; - if mt.mutbl == hir::MutImmutable { - lvalue_pref = NoPreference; - } - } - None => return (resolved_t, autoderefs, None) + + t = mt.ty; + if mt.mutbl == hir::MutImmutable { + lvalue_pref = NoPreference; } } @@ -2132,11 +2133,10 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>, fn try_overloaded_deref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, span: Span, - method_call: Option, base_expr: Option<&hir::Expr>, base_ty: Ty<'tcx>, lvalue_pref: LvaluePreference) - -> Option> + -> Option> { // Try DerefMut first, if preferred. let method = match (lvalue_pref, fcx.tcx().lang_items.deref_mut_trait()) { @@ -2158,33 +2158,23 @@ fn try_overloaded_deref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, (method, _) => method }; - make_overloaded_lvalue_return_type(fcx, method_call, method) + method } /// For the overloaded lvalue expressions (`*x`, `x[3]`), the trait returns a type of `&T`, but the /// actual type we assign to the *expression* is `T`. So this function just peels off the return -/// type by one layer to yield `T`. It also inserts the `method-callee` into the method map. -fn make_overloaded_lvalue_return_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - method_call: Option, - method: Option>) - -> Option> +/// type by one layer to yield `T`. +fn make_overloaded_lvalue_return_type<'tcx>(tcx: &ty::ctxt<'tcx>, + method: MethodCallee<'tcx>) + -> ty::TypeAndMut<'tcx> { - match method { - Some(method) => { - // extract method return type, which will be &T; - // all LB regions should have been instantiated during method lookup - let ret_ty = method.ty.fn_ret(); - let ret_ty = fcx.tcx().no_late_bound_regions(&ret_ty).unwrap().unwrap(); + // extract method return type, which will be &T; + // all LB regions should have been instantiated during method lookup + let ret_ty = method.ty.fn_ret(); + let ret_ty = tcx.no_late_bound_regions(&ret_ty).unwrap().unwrap(); - if let Some(method_call) = method_call { - fcx.inh.tables.borrow_mut().method_map.insert(method_call, method); - } - - // method returns &T, but the type as visible to user is T, so deref - ret_ty.builtin_deref(true, NoPreference) - } - None => None, - } + // method returns &T, but the type as visible to user is T, so deref + ret_ty.builtin_deref(true, NoPreference).unwrap() } fn lookup_indexing<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, @@ -2202,7 +2192,7 @@ fn lookup_indexing<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, let (ty, autoderefs, final_mt) = autoderef(fcx, base_expr.span, base_ty, - Some(base_expr), + || Some(base_expr), UnresolvedTypeAction::Error, lvalue_pref, |adj_ty, idx| { @@ -2299,10 +2289,10 @@ fn try_index_step<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // If some lookup succeeds, write callee into table and extract index/element // type from the method signature. // If some lookup succeeded, install method in table - method.and_then(|method| { + method.map(|method| { debug!("try_index_step: success, using overloaded indexing"); - make_overloaded_lvalue_return_type(fcx, Some(method_call), Some(method)). - map(|ret| (input_ty, ret.ty)) + fcx.inh.tables.borrow_mut().method_map.insert(method_call, method); + (input_ty, make_overloaded_lvalue_return_type(fcx.tcx(), method).ty) }) } @@ -2907,7 +2897,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, let (_, autoderefs, field_ty) = autoderef(fcx, expr.span, expr_t, - Some(base), + || Some(base), UnresolvedTypeAction::Error, lvalue_pref, |base_t, _| { @@ -3005,7 +2995,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, let (_, autoderefs, field_ty) = autoderef(fcx, expr.span, expr_t, - Some(base), + || Some(base), UnresolvedTypeAction::Error, lvalue_pref, |base_t, _| { @@ -3253,21 +3243,21 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, match unop { hir::UnDeref => { oprnd_t = structurally_resolved_type(fcx, expr.span, oprnd_t); - oprnd_t = match oprnd_t.builtin_deref(true, NoPreference) { - Some(mt) => mt.ty, - None => match try_overloaded_deref(fcx, expr.span, - Some(MethodCall::expr(expr.id)), - Some(&oprnd), oprnd_t, lvalue_pref) { - Some(mt) => mt.ty, - None => { - fcx.type_error_message(expr.span, |actual| { - format!("type `{}` cannot be \ - dereferenced", actual) - }, oprnd_t, None); - tcx.types.err - } - } - }; + + if let Some(mt) = oprnd_t.builtin_deref(true, NoPreference) { + oprnd_t = mt.ty; + } else if let Some(method) = try_overloaded_deref( + fcx, expr.span, Some(&oprnd), oprnd_t, lvalue_pref) { + oprnd_t = make_overloaded_lvalue_return_type(tcx, method).ty; + fcx.inh.tables.borrow_mut().method_map.insert(MethodCall::expr(expr.id), + method); + } else { + fcx.type_error_message(expr.span, |actual| { + format!("type `{}` cannot be \ + dereferenced", actual) + }, oprnd_t, None); + oprnd_t = tcx.types.err; + } } hir::UnNot => { oprnd_t = structurally_resolved_type(fcx, oprnd.span,