typeck: Support multiple expressions getting coerced at the same type.
This commit is contained in:
parent
3ff4c347c7
commit
ad91f19957
@ -82,7 +82,7 @@ pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
|||||||
autoderef(fcx,
|
autoderef(fcx,
|
||||||
callee_expr.span,
|
callee_expr.span,
|
||||||
original_callee_ty,
|
original_callee_ty,
|
||||||
Some(callee_expr),
|
|| Some(callee_expr),
|
||||||
UnresolvedTypeAction::Error,
|
UnresolvedTypeAction::Error,
|
||||||
LvaluePreference::NoPreference,
|
LvaluePreference::NoPreference,
|
||||||
|adj_ty, idx| {
|
|adj_ty, idx| {
|
||||||
|
@ -87,6 +87,14 @@ struct Coerce<'a, 'tcx: 'a> {
|
|||||||
type CoerceResult<'tcx> = RelateResult<'tcx, Option<AutoAdjustment<'tcx>>>;
|
type CoerceResult<'tcx> = RelateResult<'tcx, Option<AutoAdjustment<'tcx>>>;
|
||||||
|
|
||||||
impl<'f, 'tcx> Coerce<'f, '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> {
|
fn tcx(&self) -> &TyCtxt<'tcx> {
|
||||||
self.fcx.tcx()
|
self.fcx.tcx()
|
||||||
}
|
}
|
||||||
@ -96,16 +104,17 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||||||
Ok(None) // No coercion required.
|
Ok(None) // No coercion required.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coerce(&self,
|
fn coerce<'a, E, I>(&self,
|
||||||
expr_a: &hir::Expr,
|
exprs: &E,
|
||||||
a: Ty<'tcx>,
|
a: Ty<'tcx>,
|
||||||
b: Ty<'tcx>)
|
b: Ty<'tcx>)
|
||||||
-> CoerceResult<'tcx> {
|
-> CoerceResult<'tcx>
|
||||||
debug!("Coerce.tys({:?} => {:?})",
|
// FIXME(eddyb) use copyable iterators when that becomes ergonomic.
|
||||||
a,
|
where E: Fn() -> I,
|
||||||
b);
|
I: IntoIterator<Item=&'a hir::Expr> {
|
||||||
|
|
||||||
let a = self.fcx.infcx().shallow_resolve(a);
|
let a = self.fcx.infcx().shallow_resolve(a);
|
||||||
|
debug!("Coerce.tys({:?} => {:?})", a, b);
|
||||||
|
|
||||||
// Just ignore error types.
|
// Just ignore error types.
|
||||||
if a.references_error() || b.references_error() {
|
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`.
|
/// 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(&self,
|
fn coerce_borrowed_pointer<'a, E, I>(&self,
|
||||||
expr_a: &hir::Expr,
|
span: Span,
|
||||||
a: Ty<'tcx>,
|
exprs: &E,
|
||||||
b: Ty<'tcx>,
|
a: Ty<'tcx>,
|
||||||
mutbl_b: hir::Mutability)
|
b: Ty<'tcx>,
|
||||||
-> CoerceResult<'tcx> {
|
mutbl_b: hir::Mutability)
|
||||||
debug!("coerce_borrowed_pointer(a={:?}, b={:?})",
|
-> CoerceResult<'tcx>
|
||||||
a,
|
// FIXME(eddyb) use copyable iterators when that becomes ergonomic.
|
||||||
b);
|
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
|
// If we have a parameter of type `&M T_a` and the value
|
||||||
// provided is `expr`, we will be adding an implicit borrow,
|
// 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)
|
_ => 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.fcx.infcx().next_region_var(coercion);
|
||||||
let r_borrow = self.tcx().mk_region(r_borrow);
|
let r_borrow = self.tcx().mk_region(r_borrow);
|
||||||
let autoref = Some(AutoPtr(r_borrow, mutbl_b));
|
let autoref = Some(AutoPtr(r_borrow, mutbl_b));
|
||||||
|
|
||||||
let lvalue_pref = LvaluePreference::from_mutbl(mutbl_b);
|
let lvalue_pref = LvaluePreference::from_mutbl(mutbl_b);
|
||||||
let mut first_error = None;
|
let mut first_error = None;
|
||||||
let (_, autoderefs, success) = autoderef(self.fcx,
|
let (_, autoderefs, success) = autoderef(self.fcx, span, a, exprs,
|
||||||
expr_a.span,
|
|
||||||
a,
|
|
||||||
Some(expr_a),
|
|
||||||
UnresolvedTypeAction::Ignore,
|
UnresolvedTypeAction::Ignore,
|
||||||
lvalue_pref,
|
lvalue_pref,
|
||||||
|inner_ty, autoderef| {
|
|inner_ty, autoderef| {
|
||||||
@ -323,9 +333,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut obligations = self.unsizing_obligations.borrow_mut();
|
*self.unsizing_obligations.borrow_mut() = leftover_predicates;
|
||||||
assert!(obligations.is_empty());
|
|
||||||
*obligations = leftover_predicates;
|
|
||||||
|
|
||||||
let adjustment = AutoDerefRef {
|
let adjustment = AutoDerefRef {
|
||||||
autoderefs: if reborrow.is_some() { 1 } else { 0 },
|
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<Item=&'b hir::Expr> {
|
||||||
|
|
||||||
|
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>,
|
pub fn try<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
||||||
expr: &hir::Expr,
|
expr: &hir::Expr,
|
||||||
a: Ty<'tcx>,
|
a: Ty<'tcx>,
|
||||||
b: Ty<'tcx>)
|
b: Ty<'tcx>)
|
||||||
-> RelateResult<'tcx, ()> {
|
-> RelateResult<'tcx, ()> {
|
||||||
debug!("coercion::try({:?} -> {:?})", a, b);
|
debug!("coercion::try({:?} -> {:?})", a, b);
|
||||||
let mut unsizing_obligations = vec![];
|
let mut coerce = Coerce::new(fcx, TypeOrigin::ExprAssignable(expr.span));
|
||||||
let adjustment = try!(indent(|| {
|
fcx.infcx().commit_if_ok(|_| {
|
||||||
fcx.infcx().commit_if_ok(|_| {
|
apply(&mut coerce, &|| Some(expr), a, b)
|
||||||
let coerce = Coerce {
|
}).map(|_| ())
|
||||||
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(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coerce_mutbls<'tcx>(from_mutbl: hir::Mutability,
|
fn coerce_mutbls<'tcx>(from_mutbl: hir::Mutability,
|
||||||
|
@ -158,7 +158,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
|
|||||||
let (autoderefd_ty, n, result) = check::autoderef(self.fcx,
|
let (autoderefd_ty, n, result) = check::autoderef(self.fcx,
|
||||||
self.span,
|
self.span,
|
||||||
unadjusted_self_ty,
|
unadjusted_self_ty,
|
||||||
Some(self.self_expr),
|
|| Some(self.self_expr),
|
||||||
UnresolvedTypeAction::Error,
|
UnresolvedTypeAction::Error,
|
||||||
NoPreference,
|
NoPreference,
|
||||||
|_, n| {
|
|_, n| {
|
||||||
@ -287,7 +287,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
|
|||||||
let (_, _, result) = check::autoderef(self.fcx,
|
let (_, _, result) = check::autoderef(self.fcx,
|
||||||
self.span,
|
self.span,
|
||||||
self_ty,
|
self_ty,
|
||||||
None,
|
|| None,
|
||||||
UnresolvedTypeAction::Error,
|
UnresolvedTypeAction::Error,
|
||||||
NoPreference,
|
NoPreference,
|
||||||
|ty, _| {
|
|ty, _| {
|
||||||
@ -509,7 +509,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
|
|||||||
check::autoderef(self.fcx,
|
check::autoderef(self.fcx,
|
||||||
expr.span,
|
expr.span,
|
||||||
self.fcx.expr_ty(expr),
|
self.fcx.expr_ty(expr),
|
||||||
Some(expr),
|
|| Some(expr),
|
||||||
UnresolvedTypeAction::Error,
|
UnresolvedTypeAction::Error,
|
||||||
PreferMutLvalue,
|
PreferMutLvalue,
|
||||||
|_, autoderefs| {
|
|_, autoderefs| {
|
||||||
@ -522,92 +522,94 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Don't retry the first one or we might infinite loop!
|
// Don't retry the first one or we might infinite loop!
|
||||||
if i != 0 {
|
if i == 0 {
|
||||||
match expr.node {
|
continue;
|
||||||
hir::ExprIndex(ref base_expr, ref index_expr) => {
|
}
|
||||||
// If this is an overloaded index, the
|
match expr.node {
|
||||||
// adjustment will include an extra layer of
|
hir::ExprIndex(ref base_expr, ref index_expr) => {
|
||||||
// autoref because the method is an &self/&mut
|
// If this is an overloaded index, the
|
||||||
// self method. We have to peel it off to get
|
// adjustment will include an extra layer of
|
||||||
// the raw adjustment that `try_index_step`
|
// autoref because the method is an &self/&mut
|
||||||
// expects. This is annoying and horrible. We
|
// self method. We have to peel it off to get
|
||||||
// ought to recode this routine so it doesn't
|
// the raw adjustment that `try_index_step`
|
||||||
// (ab)use the normal type checking paths.
|
// expects. This is annoying and horrible. We
|
||||||
let adj = self.fcx.inh.tables.borrow().adjustments.get(&base_expr.id)
|
// ought to recode this routine so it doesn't
|
||||||
.cloned();
|
// (ab)use the normal type checking paths.
|
||||||
let (autoderefs, unsize) = match adj {
|
let adj = self.fcx.inh.tables.borrow().adjustments.get(&base_expr.id)
|
||||||
Some(AdjustDerefRef(adr)) => match adr.autoref {
|
.cloned();
|
||||||
None => {
|
let (autoderefs, unsize) = match adj {
|
||||||
assert!(adr.unsize.is_none());
|
Some(AdjustDerefRef(adr)) => match adr.autoref {
|
||||||
(adr.autoderefs, None)
|
None => {
|
||||||
}
|
assert!(adr.unsize.is_none());
|
||||||
Some(AutoPtr(_, _)) => {
|
(adr.autoderefs, None)
|
||||||
(adr.autoderefs, adr.unsize.map(|target| {
|
}
|
||||||
target.builtin_deref(false, NoPreference)
|
Some(AutoPtr(_, _)) => {
|
||||||
.expect("fixup: AutoPtr is not &T").ty
|
(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),
|
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
self.tcx().sess.span_bug(
|
self.tcx().sess.span_bug(
|
||||||
base_expr.span,
|
base_expr.span,
|
||||||
"unexpected adjustment type");
|
&format!("unexpected adjustment autoref {:?}",
|
||||||
|
adr));
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
None => (0, None),
|
||||||
let (adjusted_base_ty, unsize) = if let Some(target) = unsize {
|
Some(_) => {
|
||||||
(target, true)
|
self.tcx().sess.span_bug(
|
||||||
} else {
|
base_expr.span,
|
||||||
(self.fcx.adjust_expr_ty(base_expr,
|
"unexpected adjustment type");
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,7 +200,7 @@ fn create_steps<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
|||||||
let (final_ty, dereferences, _) = check::autoderef(fcx,
|
let (final_ty, dereferences, _) = check::autoderef(fcx,
|
||||||
span,
|
span,
|
||||||
self_ty,
|
self_ty,
|
||||||
None,
|
|| None,
|
||||||
UnresolvedTypeAction::Error,
|
UnresolvedTypeAction::Error,
|
||||||
NoPreference,
|
NoPreference,
|
||||||
|t, d| {
|
|t, d| {
|
||||||
|
@ -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));
|
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,
|
check::UnresolvedTypeAction::Ignore, ty::NoPreference,
|
||||||
|ty, _| {
|
|ty, _| {
|
||||||
if is_local(ty) {
|
if is_local(ty) {
|
||||||
|
@ -2053,20 +2053,21 @@ pub enum UnresolvedTypeAction {
|
|||||||
///
|
///
|
||||||
/// Note: this method does not modify the adjustments table. The caller is responsible for
|
/// 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.
|
/// inserting an AutoAdjustment record into the `fcx` using one of the suitable methods.
|
||||||
pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
|
pub fn autoderef<'a, 'b, 'tcx, E, I, T, F>(fcx: &FnCtxt<'a, 'tcx>,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
base_ty: Ty<'tcx>,
|
base_ty: Ty<'tcx>,
|
||||||
opt_expr: Option<&hir::Expr>,
|
maybe_exprs: E,
|
||||||
unresolved_type_action: UnresolvedTypeAction,
|
unresolved_type_action: UnresolvedTypeAction,
|
||||||
mut lvalue_pref: LvaluePreference,
|
mut lvalue_pref: LvaluePreference,
|
||||||
mut should_stop: F)
|
mut should_stop: F)
|
||||||
-> (Ty<'tcx>, usize, Option<T>)
|
-> (Ty<'tcx>, usize, Option<T>)
|
||||||
where F: FnMut(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={:?})",
|
debug!("autoderef(base_ty={:?}, lvalue_pref={:?})",
|
||||||
base_ty,
|
base_ty, lvalue_pref);
|
||||||
opt_expr,
|
|
||||||
lvalue_pref);
|
|
||||||
|
|
||||||
let mut t = base_ty;
|
let mut t = base_ty;
|
||||||
for autoderefs in 0..fcx.tcx().sess.recursion_limit.get() {
|
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:
|
// 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
|
// Super subtle: it might seem as though we should
|
||||||
// pass `opt_expr` to `try_overloaded_deref`, so that
|
// pass `opt_expr` to `try_overloaded_deref`, so that
|
||||||
// the (implicit) autoref of using an overloaded deref
|
// the (implicit) autoref of using an overloaded deref
|
||||||
// would get added to the adjustment table. However we
|
// would get added to the adjustment table. However we
|
||||||
// do not do that, because it's kind of a
|
// do not do that, because it's kind of a
|
||||||
// "meta-adjustment" -- instead, we just leave it
|
// "meta-adjustment" -- instead, we just leave it
|
||||||
// unrecorded and know that there "will be" an
|
// unrecorded and know that there "will be" an
|
||||||
// autoref. regionck and other bits of the code base,
|
// autoref. regionck and other bits of the code base,
|
||||||
// when they encounter an overloaded autoderef, have
|
// when they encounter an overloaded autoderef, have
|
||||||
// to do some reconstructive surgery. This is a pretty
|
// to do some reconstructive surgery. This is a pretty
|
||||||
// complex mess that is begging for a proper MIR.
|
// complex mess that is begging for a proper MIR.
|
||||||
try_overloaded_deref(fcx, sp, method_call, None, resolved_t, lvalue_pref)
|
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;
|
||||||
t = mt.ty;
|
if mt.mutbl == hir::MutImmutable {
|
||||||
if mt.mutbl == hir::MutImmutable {
|
lvalue_pref = NoPreference;
|
||||||
lvalue_pref = NoPreference;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => return (resolved_t, autoderefs, None)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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>,
|
fn try_overloaded_deref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
method_call: Option<MethodCall>,
|
|
||||||
base_expr: Option<&hir::Expr>,
|
base_expr: Option<&hir::Expr>,
|
||||||
base_ty: Ty<'tcx>,
|
base_ty: Ty<'tcx>,
|
||||||
lvalue_pref: LvaluePreference)
|
lvalue_pref: LvaluePreference)
|
||||||
-> Option<ty::TypeAndMut<'tcx>>
|
-> Option<MethodCallee<'tcx>>
|
||||||
{
|
{
|
||||||
// Try DerefMut first, if preferred.
|
// Try DerefMut first, if preferred.
|
||||||
let method = match (lvalue_pref, fcx.tcx().lang_items.deref_mut_trait()) {
|
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
|
(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
|
/// 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
|
/// 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.
|
/// type by one layer to yield `T`.
|
||||||
fn make_overloaded_lvalue_return_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
fn make_overloaded_lvalue_return_type<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||||
method_call: Option<MethodCall>,
|
method: MethodCallee<'tcx>)
|
||||||
method: Option<MethodCallee<'tcx>>)
|
-> ty::TypeAndMut<'tcx>
|
||||||
-> Option<ty::TypeAndMut<'tcx>>
|
|
||||||
{
|
{
|
||||||
match method {
|
// extract method return type, which will be &T;
|
||||||
Some(method) => {
|
// all LB regions should have been instantiated during method lookup
|
||||||
// extract method return type, which will be &T;
|
let ret_ty = method.ty.fn_ret();
|
||||||
// all LB regions should have been instantiated during method lookup
|
let ret_ty = tcx.no_late_bound_regions(&ret_ty).unwrap().unwrap();
|
||||||
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 {
|
// method returns &T, but the type as visible to user is T, so deref
|
||||||
fcx.inh.tables.borrow_mut().method_map.insert(method_call, method);
|
ret_ty.builtin_deref(true, NoPreference).unwrap()
|
||||||
}
|
|
||||||
|
|
||||||
// method returns &T, but the type as visible to user is T, so deref
|
|
||||||
ret_ty.builtin_deref(true, NoPreference)
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookup_indexing<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
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,
|
let (ty, autoderefs, final_mt) = autoderef(fcx,
|
||||||
base_expr.span,
|
base_expr.span,
|
||||||
base_ty,
|
base_ty,
|
||||||
Some(base_expr),
|
|| Some(base_expr),
|
||||||
UnresolvedTypeAction::Error,
|
UnresolvedTypeAction::Error,
|
||||||
lvalue_pref,
|
lvalue_pref,
|
||||||
|adj_ty, idx| {
|
|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
|
// If some lookup succeeds, write callee into table and extract index/element
|
||||||
// type from the method signature.
|
// type from the method signature.
|
||||||
// If some lookup succeeded, install method in table
|
// If some lookup succeeded, install method in table
|
||||||
method.and_then(|method| {
|
method.map(|method| {
|
||||||
debug!("try_index_step: success, using overloaded indexing");
|
debug!("try_index_step: success, using overloaded indexing");
|
||||||
make_overloaded_lvalue_return_type(fcx, Some(method_call), Some(method)).
|
fcx.inh.tables.borrow_mut().method_map.insert(method_call, method);
|
||||||
map(|ret| (input_ty, ret.ty))
|
(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,
|
let (_, autoderefs, field_ty) = autoderef(fcx,
|
||||||
expr.span,
|
expr.span,
|
||||||
expr_t,
|
expr_t,
|
||||||
Some(base),
|
|| Some(base),
|
||||||
UnresolvedTypeAction::Error,
|
UnresolvedTypeAction::Error,
|
||||||
lvalue_pref,
|
lvalue_pref,
|
||||||
|base_t, _| {
|
|base_t, _| {
|
||||||
@ -3005,7 +2995,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
|
|||||||
let (_, autoderefs, field_ty) = autoderef(fcx,
|
let (_, autoderefs, field_ty) = autoderef(fcx,
|
||||||
expr.span,
|
expr.span,
|
||||||
expr_t,
|
expr_t,
|
||||||
Some(base),
|
|| Some(base),
|
||||||
UnresolvedTypeAction::Error,
|
UnresolvedTypeAction::Error,
|
||||||
lvalue_pref,
|
lvalue_pref,
|
||||||
|base_t, _| {
|
|base_t, _| {
|
||||||
@ -3253,21 +3243,21 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
|
|||||||
match unop {
|
match unop {
|
||||||
hir::UnDeref => {
|
hir::UnDeref => {
|
||||||
oprnd_t = structurally_resolved_type(fcx, expr.span, oprnd_t);
|
oprnd_t = structurally_resolved_type(fcx, expr.span, oprnd_t);
|
||||||
oprnd_t = match oprnd_t.builtin_deref(true, NoPreference) {
|
|
||||||
Some(mt) => mt.ty,
|
if let Some(mt) = oprnd_t.builtin_deref(true, NoPreference) {
|
||||||
None => match try_overloaded_deref(fcx, expr.span,
|
oprnd_t = mt.ty;
|
||||||
Some(MethodCall::expr(expr.id)),
|
} else if let Some(method) = try_overloaded_deref(
|
||||||
Some(&oprnd), oprnd_t, lvalue_pref) {
|
fcx, expr.span, Some(&oprnd), oprnd_t, lvalue_pref) {
|
||||||
Some(mt) => mt.ty,
|
oprnd_t = make_overloaded_lvalue_return_type(tcx, method).ty;
|
||||||
None => {
|
fcx.inh.tables.borrow_mut().method_map.insert(MethodCall::expr(expr.id),
|
||||||
fcx.type_error_message(expr.span, |actual| {
|
method);
|
||||||
format!("type `{}` cannot be \
|
} else {
|
||||||
dereferenced", actual)
|
fcx.type_error_message(expr.span, |actual| {
|
||||||
}, oprnd_t, None);
|
format!("type `{}` cannot be \
|
||||||
tcx.types.err
|
dereferenced", actual)
|
||||||
}
|
}, oprnd_t, None);
|
||||||
}
|
oprnd_t = tcx.types.err;
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
hir::UnNot => {
|
hir::UnNot => {
|
||||||
oprnd_t = structurally_resolved_type(fcx, oprnd.span,
|
oprnd_t = structurally_resolved_type(fcx, oprnd.span,
|
||||||
|
Loading…
Reference in New Issue
Block a user