|
10 | 10 |
|
11 | 11 | use super::probe;
|
12 | 12 |
|
13 |
| -use check::{FnCtxt, callee}; |
| 13 | +use check::{FnCtxt, LvalueOp, callee}; |
14 | 14 | use hir::def_id::DefId;
|
15 | 15 | use rustc::ty::subst::Substs;
|
16 | 16 | use rustc::traits;
|
@@ -433,137 +433,95 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
433 | 433 | for (i, &expr) in exprs.iter().rev().enumerate() {
|
434 | 434 | debug!("convert_lvalue_derefs_to_mutable: i={} expr={:?}", i, expr);
|
435 | 435 |
|
436 |
| - // Count autoderefs. We don't need to fix up the autoref - the parent |
437 |
| - // expression will fix them up for us. |
438 |
| - let adjustment = self.tables.borrow().adjustments.get(&expr.id).cloned(); |
439 |
| - match adjustment { |
440 |
| - Some(Adjustment { kind: Adjust::DerefRef { autoderefs, .. }, .. }) => { |
441 |
| - if autoderefs > 0 { |
442 |
| - let mut autoderef = self.autoderef(expr.span, self.node_ty(expr.id)); |
443 |
| - autoderef.nth(autoderefs).unwrap_or_else(|| { |
444 |
| - span_bug!(expr.span, |
445 |
| - "expr was deref-able {} times but now isn't?", |
446 |
| - autoderefs); |
447 |
| - }); |
448 |
| - autoderef.finalize(PreferMutLvalue, expr); |
449 |
| - } |
450 |
| - } |
451 |
| - Some(_) | None => {} |
| 436 | + // Fix up the autoderefs. Autorefs can only occur immediately preceding |
| 437 | + // overloaded lvalue ops, and will be fixed by them in order to get |
| 438 | + // the correct region. |
| 439 | + let autoderefs = match self.tables.borrow().adjustments.get(&expr.id) { |
| 440 | + Some(&Adjustment { kind: Adjust::DerefRef { autoderefs, .. }, .. }) => autoderefs, |
| 441 | + Some(_) | None => 0 |
| 442 | + }; |
| 443 | + |
| 444 | + if autoderefs > 0 { |
| 445 | + let mut autoderef = self.autoderef(expr.span, self.node_ty(expr.id)); |
| 446 | + autoderef.nth(autoderefs).unwrap_or_else(|| { |
| 447 | + span_bug!(expr.span, |
| 448 | + "expr was deref-able {} times but now isn't?", |
| 449 | + autoderefs); |
| 450 | + }); |
| 451 | + autoderef.finalize(PreferMutLvalue, expr); |
452 | 452 | }
|
453 | 453 |
|
454 |
| - // Don't retry the first one or we might infinite loop! |
455 |
| - if i == 0 { |
456 |
| - continue; |
457 |
| - } |
458 | 454 | match expr.node {
|
459 | 455 | hir::ExprIndex(ref base_expr, ref index_expr) => {
|
460 |
| - // If this is an overloaded index, the |
461 |
| - // adjustment will include an extra layer of |
462 |
| - // autoref because the method is an &self/&mut |
463 |
| - // self method. We have to peel it off to get |
464 |
| - // the raw adjustment that `try_index_step` |
465 |
| - // expects. This is annoying and horrible. We |
466 |
| - // ought to recode this routine so it doesn't |
467 |
| - // (ab)use the normal type checking paths. |
468 |
| - let adj = self.tables.borrow_mut().adjustments.remove(&base_expr.id); |
469 |
| - let (autoderefs, unsize, adjusted_base_ty) = match adj { |
470 |
| - Some(Adjustment { |
471 |
| - kind: Adjust::DerefRef { autoderefs, autoref, unsize }, |
472 |
| - target |
473 |
| - }) => { |
474 |
| - match autoref { |
475 |
| - None => { |
476 |
| - assert!(!unsize); |
477 |
| - } |
478 |
| - Some(AutoBorrow::Ref(..)) => {} |
479 |
| - Some(_) => { |
480 |
| - span_bug!(base_expr.span, |
481 |
| - "unexpected adjustment autoref {:?}", |
482 |
| - adj); |
483 |
| - } |
484 |
| - } |
485 |
| - |
486 |
| - (autoderefs, unsize, if unsize { |
487 |
| - target.builtin_deref(false, NoPreference) |
488 |
| - .expect("fixup: AutoBorrow::Ref is not &T") |
489 |
| - .ty |
490 |
| - } else { |
491 |
| - let ty = self.node_ty(base_expr.id); |
492 |
| - let mut ty = self.shallow_resolve(ty); |
493 |
| - let mut method_type = |method_call: ty::MethodCall| { |
494 |
| - self.tables.borrow().method_map.get(&method_call).map(|m| { |
495 |
| - self.resolve_type_vars_if_possible(&m.ty) |
496 |
| - }) |
497 |
| - }; |
498 |
| - |
499 |
| - if !ty.references_error() { |
500 |
| - for i in 0..autoderefs { |
501 |
| - ty = ty.adjust_for_autoderef(self.tcx, |
502 |
| - base_expr.id, |
503 |
| - base_expr.span, |
504 |
| - i as u32, |
505 |
| - &mut method_type); |
506 |
| - } |
507 |
| - } |
508 |
| - |
509 |
| - ty |
510 |
| - }) |
511 |
| - } |
512 |
| - None => (0, false, self.node_ty(base_expr.id)), |
513 |
| - Some(_) => { |
514 |
| - span_bug!(base_expr.span, "unexpected adjustment type"); |
515 |
| - } |
516 |
| - }; |
517 |
| - |
518 | 456 | let index_expr_ty = self.node_ty(index_expr.id);
|
519 |
| - let adjusted_base_ty = self.resolve_type_vars_if_possible(&adjusted_base_ty); |
520 |
| - let index_expr_ty = self.resolve_type_vars_if_possible(&index_expr_ty); |
521 |
| - |
522 |
| - let result = self.try_index_step(ty::MethodCall::expr(expr.id), |
523 |
| - expr, |
524 |
| - &base_expr, |
525 |
| - adjusted_base_ty, |
526 |
| - autoderefs, |
527 |
| - unsize, |
528 |
| - PreferMutLvalue, |
529 |
| - index_expr_ty); |
530 |
| - |
531 |
| - if let Some((input_ty, return_ty)) = result { |
532 |
| - self.demand_suptype(index_expr.span, input_ty, index_expr_ty); |
533 |
| - |
534 |
| - let expr_ty = self.node_ty(expr.id); |
535 |
| - self.demand_suptype(expr.span, expr_ty, return_ty); |
536 |
| - } else { |
537 |
| - // We could not perform a mutable index. Re-apply the |
538 |
| - // immutable index adjustments - borrowck will detect |
539 |
| - // this as an error. |
540 |
| - if let Some(adjustment) = adjustment { |
541 |
| - self.apply_adjustment(expr.id, adjustment); |
542 |
| - } |
543 |
| - self.tcx.sess.delay_span_bug( |
544 |
| - expr.span, "convert_lvalue_derefs_to_mutable failed"); |
545 |
| - } |
| 457 | + self.convert_lvalue_op_to_mutable( |
| 458 | + LvalueOp::Index, expr, base_expr, &[index_expr_ty]); |
546 | 459 | }
|
547 | 460 | hir::ExprUnary(hir::UnDeref, ref base_expr) => {
|
548 |
| - // if this is an overloaded deref, then re-evaluate with |
549 |
| - // a preference for mut |
550 |
| - let method_call = ty::MethodCall::expr(expr.id); |
551 |
| - if self.tables.borrow().method_map.contains_key(&method_call) { |
552 |
| - self.tables.borrow_mut().adjustments.remove(&base_expr.id); |
553 |
| - let method = self.try_overloaded_deref(expr.span, |
554 |
| - Some(&base_expr), |
555 |
| - self.node_ty(base_expr.id), |
556 |
| - PreferMutLvalue); |
557 |
| - let ok = method.expect("re-trying deref failed"); |
558 |
| - let method = self.register_infer_ok_obligations(ok); |
559 |
| - self.tables.borrow_mut().method_map.insert(method_call, method); |
560 |
| - } |
| 461 | + self.convert_lvalue_op_to_mutable( |
| 462 | + LvalueOp::Deref, expr, base_expr, &[]); |
561 | 463 | }
|
562 | 464 | _ => {}
|
563 | 465 | }
|
564 | 466 | }
|
565 | 467 | }
|
566 | 468 |
|
| 469 | + fn convert_lvalue_op_to_mutable(&self, |
| 470 | + op: LvalueOp, |
| 471 | + expr: &hir::Expr, |
| 472 | + base_expr: &hir::Expr, |
| 473 | + arg_tys: &[Ty<'tcx>]) |
| 474 | + { |
| 475 | + debug!("convert_lvalue_op_to_mutable({:?}, {:?}, {:?}, {:?})", |
| 476 | + op, expr, base_expr, arg_tys); |
| 477 | + let method_call = ty::MethodCall::expr(expr.id); |
| 478 | + if !self.tables.borrow().method_map.contains_key(&method_call) { |
| 479 | + debug!("convert_lvalue_op_to_mutable - builtin, nothing to do"); |
| 480 | + return |
| 481 | + } |
| 482 | + |
| 483 | + let base_ty = self.tables.borrow().adjustments.get(&base_expr.id) |
| 484 | + .map_or_else(|| self.node_ty(expr.id), |adj| adj.target); |
| 485 | + let base_ty = self.resolve_type_vars_if_possible(&base_ty); |
| 486 | + |
| 487 | + // Need to deref because overloaded lvalue ops take self by-reference. |
| 488 | + let base_ty = base_ty.builtin_deref(false, NoPreference) |
| 489 | + .expect("lvalue op takes something that is not a ref") |
| 490 | + .ty; |
| 491 | + |
| 492 | + let method = self.try_overloaded_lvalue_op( |
| 493 | + expr.span, None, base_ty, arg_tys, PreferMutLvalue, op); |
| 494 | + let ok = match method { |
| 495 | + Some(method) => method, |
| 496 | + None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed") |
| 497 | + }; |
| 498 | + let method = self.register_infer_ok_obligations(ok); |
| 499 | + debug!("convert_lvalue_op_to_mutable: method={:?}", method); |
| 500 | + self.tables.borrow_mut().method_map.insert(method_call, method); |
| 501 | + |
| 502 | + // Convert the autoref in the base expr to mutable with the correct |
| 503 | + // region and mutability. |
| 504 | + if let Some(&mut Adjustment { |
| 505 | + ref mut target, kind: Adjust::DerefRef { |
| 506 | + autoref: Some(AutoBorrow::Ref(ref mut r, ref mut mutbl)), .. |
| 507 | + } |
| 508 | + }) = self.tables.borrow_mut().adjustments.get_mut(&base_expr.id) { |
| 509 | + debug!("convert_lvalue_op_to_mutable: converting autoref of {:?}", target); |
| 510 | + |
| 511 | + // extract method return type, which will be &mut T; |
| 512 | + // all LB regions should have been instantiated during method lookup |
| 513 | + let method_sig = self.tcx.no_late_bound_regions(&method.ty.fn_sig()).unwrap(); |
| 514 | + |
| 515 | + *target = method_sig.inputs()[0]; |
| 516 | + if let ty::TyRef(r_, mt) = target.sty { |
| 517 | + *r = r_; |
| 518 | + *mutbl = mt.mutbl; |
| 519 | + } else { |
| 520 | + span_bug!(expr.span, "input to lvalue op is not a ref?"); |
| 521 | + } |
| 522 | + } |
| 523 | + } |
| 524 | + |
567 | 525 | ///////////////////////////////////////////////////////////////////////////
|
568 | 526 | // MISCELLANY
|
569 | 527 |
|
|
0 commit comments