From c8041dd8ac780425f880e5e4e00d055e3bd0bece Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 23 Jan 2018 13:31:11 +0100 Subject: [PATCH] Add `AutoBorrowMutability`; its like `hir::Mutability` but w/ two-phase borrow info too. Namely, the mutable borrows also carries a flag indicating whether they should support two-phase borrows. This allows us to thread down, from the point of the borrow's introduction, whether the particular adjustment that created it is one that yields two-phase mutable borrows. --- src/librustc/ich/impls_ty.rs | 14 +++++++++ src/librustc/middle/expr_use_visitor.rs | 2 +- src/librustc/ty/adjustment.rs | 17 ++++++++++- src/librustc_lint/unused.rs | 6 ++-- src/librustc_mir/hair/cx/expr.rs | 33 +++++++++++++++------ src/librustc_typeck/check/callee.rs | 13 ++++++-- src/librustc_typeck/check/coercion.rs | 22 ++++++++++++-- src/librustc_typeck/check/method/confirm.rs | 23 ++++++++++++-- src/librustc_typeck/check/mod.rs | 26 ++++++++++++++-- src/librustc_typeck/check/op.rs | 24 +++++++++++++-- src/librustc_typeck/check/regionck.rs | 2 +- 11 files changed, 154 insertions(+), 28 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 107779ec3fa..d1e431597e7 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -163,6 +163,20 @@ impl_stable_hash_for!(struct ty::adjustment::Adjustment<'tcx> { kind, target }); impl_stable_hash_for!(struct ty::adjustment::OverloadedDeref<'tcx> { region, mutbl }); impl_stable_hash_for!(struct ty::UpvarBorrow<'tcx> { kind, region }); +impl<'gcx> HashStable> for ty::adjustment::AutoBorrowMutability { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + mem::discriminant(self).hash_stable(hcx, hasher); + match *self { + ty::adjustment::AutoBorrowMutability::Mutable { ref allow_two_phase_borrow } => { + allow_two_phase_borrow.hash_stable(hcx, hasher); + } + ty::adjustment::AutoBorrowMutability::Immutable => {} + } + } +} + impl_stable_hash_for!(struct ty::UpvarId { var_id, closure_expr_id }); impl_stable_hash_for!(enum ty::BorrowKind { diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index c69005101c6..7db75a51668 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -760,7 +760,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { expr.span, cmt_base, r, - ty::BorrowKind::from_mutbl(m), + ty::BorrowKind::from_mutbl(m.into()), AutoRef); } diff --git a/src/librustc/ty/adjustment.rs b/src/librustc/ty/adjustment.rs index 96d69b4fba2..7579d95a8fe 100644 --- a/src/librustc/ty/adjustment.rs +++ b/src/librustc/ty/adjustment.rs @@ -119,10 +119,25 @@ impl<'a, 'gcx, 'tcx> OverloadedDeref<'tcx> { } } +#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable)] +pub enum AutoBorrowMutability { + Mutable { allow_two_phase_borrow: bool }, + Immutable, +} + +impl From for hir::Mutability { + fn from(m: AutoBorrowMutability) -> Self { + match m { + AutoBorrowMutability::Mutable { .. } => hir::MutMutable, + AutoBorrowMutability::Immutable => hir::MutImmutable, + } + } +} + #[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable)] pub enum AutoBorrow<'tcx> { /// Convert from T to &T. - Ref(ty::Region<'tcx>, hir::Mutability), + Ref(ty::Region<'tcx>, AutoBorrowMutability), /// Convert from T to *T. RawPtr(hir::Mutability), diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index 56f863ab3aa..439533fae49 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -437,8 +437,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAllocation { for adj in cx.tables.expr_adjustments(e) { if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind { let msg = match m { - hir::MutImmutable => "unnecessary allocation, use & instead", - hir::MutMutable => "unnecessary allocation, use &mut instead" + adjustment::AutoBorrowMutability::Immutable => + "unnecessary allocation, use & instead", + adjustment::AutoBorrowMutability::Mutable { .. }=> + "unnecessary allocation, use &mut instead" }; cx.span_lint(UNUSED_ALLOCATION, e.span, msg); } diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index e33147a915b..00ab2e45995 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -17,7 +17,7 @@ use hair::cx::to_ref::ToRef; use rustc::hir::def::{Def, CtorKind}; use rustc::middle::const_val::ConstVal; use rustc::ty::{self, AdtKind, VariantDef, Ty}; -use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow}; +use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability}; use rustc::ty::cast::CastKind as TyCastKind; use rustc::hir; use rustc::hir::def_id::LocalDefId; @@ -112,7 +112,7 @@ fn apply_adjustment<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, span, kind: ExprKind::Borrow { region: deref.region, - borrow_kind: to_borrow_kind(deref.mutbl, true), + borrow_kind: deref.mutbl.to_borrow_kind(), arg: expr.to_ref(), }, }; @@ -122,7 +122,7 @@ fn apply_adjustment<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, Adjust::Borrow(AutoBorrow::Ref(r, m)) => { ExprKind::Borrow { region: r, - borrow_kind: to_borrow_kind(m, true), + borrow_kind: m.to_borrow_kind(), arg: expr.to_ref(), } } @@ -142,7 +142,7 @@ fn apply_adjustment<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, span, kind: ExprKind::Borrow { region, - borrow_kind: to_borrow_kind(m, true), + borrow_kind: m.to_borrow_kind(), arg: expr.to_ref(), }, }; @@ -288,7 +288,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, }; ExprKind::Borrow { region, - borrow_kind: to_borrow_kind(mutbl, false), + borrow_kind: mutbl.to_borrow_kind(), arg: expr.to_ref(), } } @@ -643,10 +643,25 @@ fn method_callee<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, } } -fn to_borrow_kind(m: hir::Mutability, allow_two_phase_borrow: bool) -> BorrowKind { - match m { - hir::MutMutable => BorrowKind::Mut { allow_two_phase_borrow }, - hir::MutImmutable => BorrowKind::Shared, +trait ToBorrowKind { fn to_borrow_kind(&self) -> BorrowKind; } + +impl ToBorrowKind for AutoBorrowMutability { + fn to_borrow_kind(&self) -> BorrowKind { + match *self { + AutoBorrowMutability::Mutable { allow_two_phase_borrow } => + BorrowKind::Mut { allow_two_phase_borrow }, + AutoBorrowMutability::Immutable => + BorrowKind::Shared, + } + } +} + +impl ToBorrowKind for hir::Mutability { + fn to_borrow_kind(&self) -> BorrowKind { + match *self { + hir::MutMutable => BorrowKind::Mut { allow_two_phase_borrow: false }, + hir::MutImmutable => BorrowKind::Shared, + } } } diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 76df9be4838..3d61ffe3933 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -16,7 +16,7 @@ use hir::def::Def; use hir::def_id::{DefId, LOCAL_CRATE}; use rustc::{infer, traits}; use rustc::ty::{self, TyCtxt, TypeFoldable, Ty}; -use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow}; +use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability}; use syntax::abi; use syntax::symbol::Symbol; use syntax_pos::Span; @@ -176,8 +176,17 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mut autoref = None; if borrow { if let ty::TyRef(region, mt) = method.sig.inputs()[0].sty { + let mutbl = match mt.mutbl { + hir::MutImmutable => AutoBorrowMutability::Immutable, + hir::MutMutable => AutoBorrowMutability::Mutable { + // For initial two-phase borrow + // deployment, conservatively omit + // overloaded function call ops. + allow_two_phase_borrow: false, + } + }; autoref = Some(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, mt.mutbl)), + kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), target: method.sig.inputs()[0] }); } diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index d0280bf0b30..47e4b0272be 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -68,7 +68,7 @@ use rustc::infer::{Coercion, InferResult, InferOk}; use rustc::infer::type_variable::TypeVariableOrigin; use rustc::lint; use rustc::traits::{self, ObligationCause, ObligationCauseCode}; -use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow}; +use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability}; use rustc::ty::{self, TypeAndMut, Ty, ClosureSubsts}; use rustc::ty::fold::TypeFoldable; use rustc::ty::error::TypeError; @@ -421,8 +421,17 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> { ty::TyRef(r_borrow, _) => r_borrow, _ => span_bug!(span, "expected a ref type, got {:?}", ty), }; + let mutbl = match mt_b.mutbl { + hir::MutImmutable => AutoBorrowMutability::Immutable, + hir::MutMutable => AutoBorrowMutability::Mutable { + // Deref-coercion is a case where we deliberately + // disallow two-phase borrows in its initial + // deployment; see discussion on PR #47489. + allow_two_phase_borrow: false, + } + }; adjustments.push(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(r_borrow, mt_b.mutbl)), + kind: Adjust::Borrow(AutoBorrow::Ref(r_borrow, mutbl)), target: ty }); @@ -461,11 +470,17 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> { let coercion = Coercion(self.cause.span); let r_borrow = self.next_region_var(coercion); + let mutbl = match mt_b.mutbl { + hir::MutImmutable => AutoBorrowMutability::Immutable, + hir::MutMutable => AutoBorrowMutability::Mutable { + allow_two_phase_borrow: false, + } + }; Some((Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }, Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(r_borrow, mt_b.mutbl)), + kind: Adjust::Borrow(AutoBorrow::Ref(r_borrow, mutbl)), target: self.tcx.mk_ref(r_borrow, ty::TypeAndMut { mutbl: mt_b.mutbl, ty: mt_a.ty @@ -871,6 +886,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ] => { match self.node_ty(expr.hir_id).sty { ty::TyRef(_, mt_orig) => { + let mutbl_adj: hir::Mutability = mutbl_adj.into(); // Reborrow that we can safely ignore, because // the next adjustment can only be a Deref // which will be merged into it. diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 7f5b353f79e..20d58991496 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -17,7 +17,7 @@ use rustc::ty::subst::Substs; use rustc::traits; use rustc::ty::{self, Ty}; use rustc::ty::subst::Subst; -use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, OverloadedDeref}; +use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability, OverloadedDeref}; use rustc::ty::fold::TypeFoldable; use rustc::infer::{self, InferOk}; use syntax_pos::Span; @@ -165,6 +165,14 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { mutbl, ty: target }); + let mutbl = match mutbl { + hir::MutImmutable => AutoBorrowMutability::Immutable, + hir::MutMutable => AutoBorrowMutability::Mutable { + // Method call receivers are the primary use case + // for two-phase borrows. + allow_two_phase_borrow: true, + } + }; adjustments.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), target @@ -172,7 +180,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { if let Some(unsize_target) = pick.unsize { target = self.tcx.mk_ref(region, ty::TypeAndMut { - mutbl, + mutbl: mutbl.into(), ty: unsize_target }); adjustments.push(Adjustment { @@ -530,10 +538,19 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> { for adjustment in &mut adjustments[..] { if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind { debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment); + let mutbl = match mutbl { + hir::MutImmutable => AutoBorrowMutability::Immutable, + hir::MutMutable => AutoBorrowMutability::Mutable { + // For initial two-phase borrow + // deployment, conservatively omit + // overloaded operators. + allow_two_phase_borrow: false, + } + }; adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl)); adjustment.target = self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, - mutbl + mutbl: mutbl.into(), }); } source = adjustment.target; diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 363d4a9dc0c..47bf085c9c0 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -96,7 +96,7 @@ use rustc::middle::region; use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode}; use rustc::ty::{self, Ty, TyCtxt, Visibility, ToPredicate}; -use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; +use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc::ty::fold::TypeFoldable; use rustc::ty::maps::Providers; use rustc::ty::util::{Representability, IntTypeExt}; @@ -2357,8 +2357,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mut adjustments = autoderef.adjust_steps(needs); if let ty::TyRef(region, mt) = method.sig.inputs()[0].sty { + let mutbl = match mt.mutbl { + hir::MutImmutable => AutoBorrowMutability::Immutable, + hir::MutMutable => AutoBorrowMutability::Mutable { + // FIXME (#46747): arguably indexing is + // "just another kind of call"; perhaps it + // would be more consistent to allow + // two-phase borrows for .index() + // receivers here. + allow_two_phase_borrow: false, + } + }; adjustments.push(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, mt.mutbl)), + kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), target: self.tcx.mk_ref(region, ty::TypeAndMut { mutbl: mt.mutbl, ty: adjusted_ty @@ -3646,8 +3657,17 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { expr.span, oprnd_t, needs) { let method = self.register_infer_ok_obligations(ok); if let ty::TyRef(region, mt) = method.sig.inputs()[0].sty { + let mutbl = match mt.mutbl { + hir::MutImmutable => AutoBorrowMutability::Immutable, + hir::MutMutable => AutoBorrowMutability::Mutable { + // (It shouldn't actually matter for unary ops whether + // we enable two-phase borrows or not, since a unary + // op has no additional operands.) + allow_two_phase_borrow: false, + } + }; self.apply_adjustments(oprnd, vec![Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, mt.mutbl)), + kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), target: method.sig.inputs()[0] }]); } diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index 0698e3ecb6e..a6776a0fe86 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -14,7 +14,7 @@ use super::{FnCtxt, Needs}; use super::method::MethodCallee; use rustc::ty::{self, Ty, TypeFoldable, TypeVariants}; use rustc::ty::TypeVariants::{TyStr, TyRef}; -use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow}; +use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability}; use rustc::infer::type_variable::TypeVariableOrigin; use errors; use syntax_pos::Span; @@ -198,8 +198,17 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let by_ref_binop = !op.node.is_by_value(); if is_assign == IsAssign::Yes || by_ref_binop { if let ty::TyRef(region, mt) = method.sig.inputs()[0].sty { + let mutbl = match mt.mutbl { + hir::MutImmutable => AutoBorrowMutability::Immutable, + hir::MutMutable => AutoBorrowMutability::Mutable { + // For initial two-phase borrow + // deployment, conservatively omit + // overloaded binary ops. + allow_two_phase_borrow: false, + } + }; let autoref = Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, mt.mutbl)), + kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), target: method.sig.inputs()[0] }; self.apply_adjustments(lhs_expr, vec![autoref]); @@ -207,8 +216,17 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } if by_ref_binop { if let ty::TyRef(region, mt) = method.sig.inputs()[1].sty { + let mutbl = match mt.mutbl { + hir::MutImmutable => AutoBorrowMutability::Immutable, + hir::MutMutable => AutoBorrowMutability::Mutable { + // For initial two-phase borrow + // deployment, conservatively omit + // overloaded binary ops. + allow_two_phase_borrow: false, + } + }; let autoref = Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, mt.mutbl)), + kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), target: method.sig.inputs()[1] }; // HACK(eddyb) Bypass checks due to reborrows being in diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 64063ec5bed..b5bf59fef9a 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -1063,7 +1063,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { match *autoref { adjustment::AutoBorrow::Ref(r, m) => { self.link_region(expr.span, r, - ty::BorrowKind::from_mutbl(m), expr_cmt); + ty::BorrowKind::from_mutbl(m.into()), expr_cmt); } adjustment::AutoBorrow::RawPtr(m) => {