Skip to content

Commit ad91f19

Browse files
committed
typeck: Support multiple expressions getting coerced at the same type.
1 parent 3ff4c34 commit ad91f19

File tree

6 files changed

+225
-216
lines changed

6 files changed

+225
-216
lines changed

src/librustc_typeck/check/callee.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
8282
autoderef(fcx,
8383
callee_expr.span,
8484
original_callee_ty,
85-
Some(callee_expr),
85+
|| Some(callee_expr),
8686
UnresolvedTypeAction::Error,
8787
LvaluePreference::NoPreference,
8888
|adj_ty, idx| {

src/librustc_typeck/check/coercion.rs

Lines changed: 66 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ struct Coerce<'a, 'tcx: 'a> {
8787
type CoerceResult<'tcx> = RelateResult<'tcx, Option<AutoAdjustment<'tcx>>>;
8888

8989
impl<'f, 'tcx> Coerce<'f, 'tcx> {
90+
fn new(fcx: &'a FnCtxt<'a, 'tcx>, origin: TypeOrigin) -> Self {
91+
Coerce {
92+
fcx: fcx,
93+
origin: origin,
94+
unsizing_obligations: RefCell::new(vec![])
95+
}
96+
}
97+
9098
fn tcx(&self) -> &TyCtxt<'tcx> {
9199
self.fcx.tcx()
92100
}
@@ -96,16 +104,17 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
96104
Ok(None) // No coercion required.
97105
}
98106

99-
fn coerce(&self,
100-
expr_a: &hir::Expr,
101-
a: Ty<'tcx>,
102-
b: Ty<'tcx>)
103-
-> CoerceResult<'tcx> {
104-
debug!("Coerce.tys({:?} => {:?})",
105-
a,
106-
b);
107+
fn coerce<'a, E, I>(&self,
108+
exprs: &E,
109+
a: Ty<'tcx>,
110+
b: Ty<'tcx>)
111+
-> CoerceResult<'tcx>
112+
// FIXME(eddyb) use copyable iterators when that becomes ergonomic.
113+
where E: Fn() -> I,
114+
I: IntoIterator<Item=&'a hir::Expr> {
107115

108116
let a = self.fcx.infcx().shallow_resolve(a);
117+
debug!("Coerce.tys({:?} => {:?})", a, b);
109118

110119
// Just ignore error types.
111120
if a.references_error() || b.references_error() {
@@ -156,15 +165,18 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
156165
/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
157166
/// To match `A` with `B`, autoderef will be performed,
158167
/// calling `deref`/`deref_mut` where necessary.
159-
fn coerce_borrowed_pointer(&self,
160-
expr_a: &hir::Expr,
161-
a: Ty<'tcx>,
162-
b: Ty<'tcx>,
163-
mutbl_b: hir::Mutability)
164-
-> CoerceResult<'tcx> {
165-
debug!("coerce_borrowed_pointer(a={:?}, b={:?})",
166-
a,
167-
b);
168+
fn coerce_borrowed_pointer<'a, E, I>(&self,
169+
span: Span,
170+
exprs: &E,
171+
a: Ty<'tcx>,
172+
b: Ty<'tcx>,
173+
mutbl_b: hir::Mutability)
174+
-> CoerceResult<'tcx>
175+
// FIXME(eddyb) use copyable iterators when that becomes ergonomic.
176+
where E: Fn() -> I,
177+
I: IntoIterator<Item=&'a hir::Expr> {
178+
179+
debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
168180

169181
// If we have a parameter of type `&M T_a` and the value
170182
// provided is `expr`, we will be adding an implicit borrow,
@@ -179,17 +191,15 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
179191
_ => return self.subtype(a, b)
180192
}
181193

182-
let coercion = Coercion(self.origin.span());
194+
let span = self.origin.span();
195+
let coercion = Coercion(span);
183196
let r_borrow = self.fcx.infcx().next_region_var(coercion);
184197
let r_borrow = self.tcx().mk_region(r_borrow);
185198
let autoref = Some(AutoPtr(r_borrow, mutbl_b));
186199

187200
let lvalue_pref = LvaluePreference::from_mutbl(mutbl_b);
188201
let mut first_error = None;
189-
let (_, autoderefs, success) = autoderef(self.fcx,
190-
expr_a.span,
191-
a,
192-
Some(expr_a),
202+
let (_, autoderefs, success) = autoderef(self.fcx, span, a, exprs,
193203
UnresolvedTypeAction::Ignore,
194204
lvalue_pref,
195205
|inner_ty, autoderef| {
@@ -323,9 +333,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
323333
}
324334
}
325335

326-
let mut obligations = self.unsizing_obligations.borrow_mut();
327-
assert!(obligations.is_empty());
328-
*obligations = leftover_predicates;
336+
*self.unsizing_obligations.borrow_mut() = leftover_predicates;
329337

330338
let adjustment = AutoDerefRef {
331339
autoderefs: if reborrow.is_some() { 1 } else { 0 },
@@ -425,39 +433,48 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
425433
}
426434
}
427435

428-
pub fn try<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
429-
expr: &hir::Expr,
430-
a: Ty<'tcx>,
431-
b: Ty<'tcx>)
432-
-> RelateResult<'tcx, ()> {
433-
debug!("coercion::try({:?} -> {:?})", a, b);
434-
let mut unsizing_obligations = vec![];
435-
let adjustment = try!(indent(|| {
436-
fcx.infcx().commit_if_ok(|_| {
437-
let coerce = Coerce {
438-
fcx: fcx,
439-
origin: TypeOrigin::ExprAssignable(expr.span),
440-
unsizing_obligations: RefCell::new(vec![])
441-
};
442-
let adjustment = try!(coerce.coerce(expr, a, b));
443-
unsizing_obligations = coerce.unsizing_obligations.into_inner();
444-
Ok(adjustment)
445-
})
446-
}));
436+
fn apply<'a, 'b, 'tcx, E, I>(coerce: &mut Coerce<'a, 'tcx>,
437+
exprs: &E,
438+
a: Ty<'tcx>,
439+
b: Ty<'tcx>)
440+
-> RelateResult<'tcx, Ty<'tcx>>
441+
where E: Fn() -> I,
442+
I: IntoIterator<Item=&'b hir::Expr> {
443+
444+
let (ty, adjustment) = try!(indent(|| coerce.coerce(exprs, a, b)));
447445

448-
if let Some(AdjustDerefRef(auto)) = adjustment {
446+
let fcx = coerce.fcx;
447+
if let AdjustDerefRef(auto) = adjustment {
449448
if auto.unsize.is_some() {
450-
for obligation in unsizing_obligations {
449+
for obligation in coerce.unsizing_obligations.borrow_mut().drain() {
451450
fcx.register_predicate(obligation);
452451
}
453452
}
454453
}
455454

456-
if let Some(adjustment) = adjustment {
455+
if !adjustment.is_identity() {
457456
debug!("Success, coerced with {:?}", adjustment);
458-
fcx.write_adjustment(expr.id, adjustment);
457+
for expr in exprs() {
458+
assert!(!fcx.inh.tables.borrow().adjustments.contains(&expr.id));
459+
fcx.write_adjustment(expr.id, adjustment);
460+
}
459461
}
460-
Ok(())
462+
Ok(ty)
463+
}
464+
465+
/// Attempt to coerce an expression from a type (a) to another type (b).
466+
/// Adjustments are only recorded if the coercion was successful.
467+
/// The expressions *must not* have any pre-existing adjustments.
468+
pub fn try<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
469+
expr: &hir::Expr,
470+
a: Ty<'tcx>,
471+
b: Ty<'tcx>)
472+
-> RelateResult<'tcx, ()> {
473+
debug!("coercion::try({:?} -> {:?})", a, b);
474+
let mut coerce = Coerce::new(fcx, TypeOrigin::ExprAssignable(expr.span));
475+
fcx.infcx().commit_if_ok(|_| {
476+
apply(&mut coerce, &|| Some(expr), a, b)
477+
}).map(|_| ())
461478
}
462479

463480
fn coerce_mutbls<'tcx>(from_mutbl: hir::Mutability,

src/librustc_typeck/check/method/confirm.rs

Lines changed: 83 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
158158
let (autoderefd_ty, n, result) = check::autoderef(self.fcx,
159159
self.span,
160160
unadjusted_self_ty,
161-
Some(self.self_expr),
161+
|| Some(self.self_expr),
162162
UnresolvedTypeAction::Error,
163163
NoPreference,
164164
|_, n| {
@@ -287,7 +287,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
287287
let (_, _, result) = check::autoderef(self.fcx,
288288
self.span,
289289
self_ty,
290-
None,
290+
|| None,
291291
UnresolvedTypeAction::Error,
292292
NoPreference,
293293
|ty, _| {
@@ -509,7 +509,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
509509
check::autoderef(self.fcx,
510510
expr.span,
511511
self.fcx.expr_ty(expr),
512-
Some(expr),
512+
|| Some(expr),
513513
UnresolvedTypeAction::Error,
514514
PreferMutLvalue,
515515
|_, autoderefs| {
@@ -522,92 +522,94 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
522522
}
523523

524524
// Don't retry the first one or we might infinite loop!
525-
if i != 0 {
526-
match expr.node {
527-
hir::ExprIndex(ref base_expr, ref index_expr) => {
528-
// If this is an overloaded index, the
529-
// adjustment will include an extra layer of
530-
// autoref because the method is an &self/&mut
531-
// self method. We have to peel it off to get
532-
// the raw adjustment that `try_index_step`
533-
// expects. This is annoying and horrible. We
534-
// ought to recode this routine so it doesn't
535-
// (ab)use the normal type checking paths.
536-
let adj = self.fcx.inh.tables.borrow().adjustments.get(&base_expr.id)
537-
.cloned();
538-
let (autoderefs, unsize) = match adj {
539-
Some(AdjustDerefRef(adr)) => match adr.autoref {
540-
None => {
541-
assert!(adr.unsize.is_none());
542-
(adr.autoderefs, None)
543-
}
544-
Some(AutoPtr(_, _)) => {
545-
(adr.autoderefs, adr.unsize.map(|target| {
546-
target.builtin_deref(false, NoPreference)
547-
.expect("fixup: AutoPtr is not &T").ty
548-
}))
549-
}
550-
Some(_) => {
551-
self.tcx().sess.span_bug(
552-
base_expr.span,
553-
&format!("unexpected adjustment autoref {:?}",
554-
adr));
555-
}
556-
},
557-
None => (0, None),
525+
if i == 0 {
526+
continue;
527+
}
528+
match expr.node {
529+
hir::ExprIndex(ref base_expr, ref index_expr) => {
530+
// If this is an overloaded index, the
531+
// adjustment will include an extra layer of
532+
// autoref because the method is an &self/&mut
533+
// self method. We have to peel it off to get
534+
// the raw adjustment that `try_index_step`
535+
// expects. This is annoying and horrible. We
536+
// ought to recode this routine so it doesn't
537+
// (ab)use the normal type checking paths.
538+
let adj = self.fcx.inh.tables.borrow().adjustments.get(&base_expr.id)
539+
.cloned();
540+
let (autoderefs, unsize) = match adj {
541+
Some(AdjustDerefRef(adr)) => match adr.autoref {
542+
None => {
543+
assert!(adr.unsize.is_none());
544+
(adr.autoderefs, None)
545+
}
546+
Some(AutoPtr(_, _)) => {
547+
(adr.autoderefs, adr.unsize.map(|target| {
548+
target.builtin_deref(false, NoPreference)
549+
.expect("fixup: AutoPtr is not &T").ty
550+
}))
551+
}
558552
Some(_) => {
559553
self.tcx().sess.span_bug(
560554
base_expr.span,
561-
"unexpected adjustment type");
555+
&format!("unexpected adjustment autoref {:?}",
556+
adr));
562557
}
563-
};
564-
565-
let (adjusted_base_ty, unsize) = if let Some(target) = unsize {
566-
(target, true)
567-
} else {
568-
(self.fcx.adjust_expr_ty(base_expr,
569-
Some(&AdjustDerefRef(AutoDerefRef {
570-
autoderefs: autoderefs,
571-
autoref: None,
572-
unsize: None
573-
}))), false)
574-
};
575-
let index_expr_ty = self.fcx.expr_ty(&index_expr);
576-
577-
let result = check::try_index_step(
578-
self.fcx,
579-
ty::MethodCall::expr(expr.id),
580-
expr,
581-
&base_expr,
582-
adjusted_base_ty,
583-
autoderefs,
584-
unsize,
585-
PreferMutLvalue,
586-
index_expr_ty);
587-
588-
if let Some((input_ty, return_ty)) = result {
589-
demand::suptype(self.fcx, index_expr.span, input_ty, index_expr_ty);
590-
591-
let expr_ty = self.fcx.expr_ty(&expr);
592-
demand::suptype(self.fcx, expr.span, expr_ty, return_ty);
558+
},
559+
None => (0, None),
560+
Some(_) => {
561+
self.tcx().sess.span_bug(
562+
base_expr.span,
563+
"unexpected adjustment type");
593564
}
565+
};
566+
567+
let (adjusted_base_ty, unsize) = if let Some(target) = unsize {
568+
(target, true)
569+
} else {
570+
(self.fcx.adjust_expr_ty(base_expr,
571+
Some(&AdjustDerefRef(AutoDerefRef {
572+
autoderefs: autoderefs,
573+
autoref: None,
574+
unsize: None
575+
}))), false)
576+
};
577+
let index_expr_ty = self.fcx.expr_ty(&index_expr);
578+
579+
let result = check::try_index_step(
580+
self.fcx,
581+
ty::MethodCall::expr(expr.id),
582+
expr,
583+
&base_expr,
584+
adjusted_base_ty,
585+
autoderefs,
586+
unsize,
587+
PreferMutLvalue,
588+
index_expr_ty);
589+
590+
if let Some((input_ty, return_ty)) = result {
591+
demand::suptype(self.fcx, index_expr.span, input_ty, index_expr_ty);
592+
593+
let expr_ty = self.fcx.expr_ty(&expr);
594+
demand::suptype(self.fcx, expr.span, expr_ty, return_ty);
594595
}
595-
hir::ExprUnary(hir::UnDeref, ref base_expr) => {
596-
// if this is an overloaded deref, then re-evaluate with
597-
// a preference for mut
598-
let method_call = ty::MethodCall::expr(expr.id);
599-
if self.fcx.inh.tables.borrow().method_map.contains_key(&method_call) {
600-
check::try_overloaded_deref(
601-
self.fcx,
602-
expr.span,
603-
Some(method_call),
604-
Some(&base_expr),
605-
self.fcx.expr_ty(&base_expr),
606-
PreferMutLvalue);
607-
}
596+
}
597+
hir::ExprUnary(hir::UnDeref, ref base_expr) => {
598+
// if this is an overloaded deref, then re-evaluate with
599+
// a preference for mut
600+
let method_call = ty::MethodCall::expr(expr.id);
601+
if self.fcx.inh.tables.borrow().method_map.contains_key(&method_call) {
602+
let method = check::try_overloaded_deref(
603+
self.fcx,
604+
expr.span,
605+
Some(&base_expr),
606+
self.fcx.expr_ty(&base_expr),
607+
PreferMutLvalue);
608+
let method = method.expect("re-trying deref failed");
609+
self.fcx.inh.tables.borrow_mut().method_map.insert(method_call, method);
608610
}
609-
_ => {}
610611
}
612+
_ => {}
611613
}
612614
}
613615
}

src/librustc_typeck/check/method/probe.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ fn create_steps<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
200200
let (final_ty, dereferences, _) = check::autoderef(fcx,
201201
span,
202202
self_ty,
203-
None,
203+
|| None,
204204
UnresolvedTypeAction::Error,
205205
NoPreference,
206206
|t, d| {

0 commit comments

Comments
 (0)