autoderef(fcx,
callee_expr.span,
original_callee_ty,
- Some(callee_expr),
+ || Some(callee_expr),
UnresolvedTypeAction::Error,
LvaluePreference::NoPreference,
|adj_ty, idx| {
type CoerceResult<'tcx> = RelateResult<'tcx, Option<AutoAdjustment<'tcx>>>;
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()
}
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<Item=&'a hir::Expr> {
let a = self.fcx.infcx().shallow_resolve(a);
+ debug!("Coerce.tys({:?} => {:?})", a, b);
// Just ignore error types.
if a.references_error() || b.references_error() {
/// 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<Item=&'a hir::Expr> {
+
+ 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,
_ => 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| {
}
}
- 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 },
}
}
-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)
- })
- }));
+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<Item=&'b hir::Expr> {
+
+ let (ty, adjustment) = try!(indent(|| coerce.coerce(exprs, a, b)));
- if let Some(AdjustDerefRef(auto)) = adjustment {
+ let fcx = coerce.fcx;
+ if let AdjustDerefRef(auto) = adjustment {
if auto.unsize.is_some() {
- for obligation in unsizing_obligations {
+ for obligation in coerce.unsizing_obligations.borrow_mut().drain() {
fcx.register_predicate(obligation);
}
}
}
- if let Some(adjustment) = adjustment {
+ if !adjustment.is_identity() {
debug!("Success, coerced with {:?}", adjustment);
- fcx.write_adjustment(expr.id, adjustment);
+ for expr in exprs() {
+ assert!(!fcx.inh.tables.borrow().adjustments.contains(&expr.id));
+ fcx.write_adjustment(expr.id, adjustment);
+ }
}
- Ok(())
+ 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 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,
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| {
let (_, _, result) = check::autoderef(self.fcx,
self.span,
self_ty,
- None,
+ || None,
UnresolvedTypeAction::Error,
NoPreference,
|ty, _| {
check::autoderef(self.fcx,
expr.span,
self.fcx.expr_ty(expr),
- Some(expr),
+ || Some(expr),
UnresolvedTypeAction::Error,
PreferMutLvalue,
|_, autoderefs| {
}
// 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);
}
- _ => {}
}
+ _ => {}
}
}
}
let (final_ty, dereferences, _) = check::autoderef(fcx,
span,
self_ty,
- None,
+ || None,
UnresolvedTypeAction::Error,
NoPreference,
|t, d| {
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) {
///
/// 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<T>)
- where F: FnMut(Ty<'tcx>, usize) -> Option<T>,
+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<T>)
+ // FIXME(eddyb) use copyable iterators when that becomes ergonomic.
+ where E: Fn() -> I,
+ I: IntoIterator<Item=&'b hir::Expr>,
+ F: FnMut(Ty<'tcx>, usize) -> Option<T>,
{
- 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() {
}
// 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;
}
}
fn try_overloaded_deref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
span: Span,
- method_call: Option<MethodCall>,
base_expr: Option<&hir::Expr>,
base_ty: Ty<'tcx>,
lvalue_pref: LvaluePreference)
- -> Option<ty::TypeAndMut<'tcx>>
+ -> Option<MethodCallee<'tcx>>
{
// Try DerefMut first, if preferred.
let method = match (lvalue_pref, fcx.tcx().lang_items.deref_mut_trait()) {
(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<MethodCall>,
- method: Option<MethodCallee<'tcx>>)
- -> Option<ty::TypeAndMut<'tcx>>
+/// 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();
-
- if let Some(method_call) = method_call {
- fcx.inh.tables.borrow_mut().method_map.insert(method_call, 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 = tcx.no_late_bound_regions(&ret_ty).unwrap().unwrap();
- // 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>,
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| {
// 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)
})
}
let (_, autoderefs, field_ty) = autoderef(fcx,
expr.span,
expr_t,
- Some(base),
+ || Some(base),
UnresolvedTypeAction::Error,
lvalue_pref,
|base_t, _| {
let (_, autoderefs, field_ty) = autoderef(fcx,
expr.span,
expr_t,
- Some(base),
+ || Some(base),
UnresolvedTypeAction::Error,
lvalue_pref,
|base_t, _| {
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,