|
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