Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend two-phase borrows to apply to method receiver autorefs #49348

Merged
merged 4 commits into from
Apr 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/librustc/ich/impls_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ for ty::adjustment::Adjust<'gcx> {
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_stable_hash_for!(enum ty::adjustment::AllowTwoPhase {
Yes,
No
});

impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::adjustment::AutoBorrowMutability {
fn hash_stable<W: StableHasherResult>(&self,
Expand Down
20 changes: 19 additions & 1 deletion src/librustc/ty/adjustment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,27 @@ impl<'a, 'gcx, 'tcx> OverloadedDeref<'tcx> {
}
}

/// At least for initial deployment, we want to limit two-phase borrows to
/// only a few specific cases. Right now, those mostly "things that desugar"
/// into method calls
/// - using x.some_method() syntax, where some_method takes &mut self
/// - using Foo::some_method(&mut x, ...) syntax
/// - binary assignment operators (+=, -=, *=, etc.)
/// Anything else should be rejected until generalized two phase borrow support
/// is implemented. Right now, dataflow can't handle the general case where there
/// is more than one use of a mutable borrow, and we don't want to accept too much
/// new code via two-phase borrows, so we try to limit where we create two-phase
/// capable mutable borrows.
/// See #49434 for tracking.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lovin' the comments <3

#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable)]
pub enum AllowTwoPhase {
Yes,
No
}

#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable)]
pub enum AutoBorrowMutability {
Mutable { allow_two_phase_borrow: bool },
Mutable { allow_two_phase_borrow: AllowTwoPhase },
Immutable,
}

Expand Down
6 changes: 5 additions & 1 deletion src/librustc_mir/hair/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -662,9 +662,13 @@ trait ToBorrowKind { fn to_borrow_kind(&self) -> BorrowKind; }

impl ToBorrowKind for AutoBorrowMutability {
fn to_borrow_kind(&self) -> BorrowKind {
use rustc::ty::adjustment::AllowTwoPhase;
match *self {
AutoBorrowMutability::Mutable { allow_two_phase_borrow } =>
BorrowKind::Mut { allow_two_phase_borrow },
BorrowKind::Mut { allow_two_phase_borrow: match allow_two_phase_borrow {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we should propagate the AllowTwoPhase type here, instead of converting to bool?

AllowTwoPhase::Yes => true,
AllowTwoPhase::No => false
}},
AutoBorrowMutability::Immutable =>
BorrowKind::Shared,
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_typeck/check/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, AutoBorrowMutability};
use rustc::ty::adjustment::{Adjustment, Adjust, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use syntax::abi;
use syntax::symbol::Symbol;
use syntax_pos::Span;
Expand Down Expand Up @@ -182,7 +182,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// For initial two-phase borrow
// deployment, conservatively omit
// overloaded function call ops.
allow_two_phase_borrow: false,
allow_two_phase_borrow: AllowTwoPhase::No,
}
};
autoref = Some(Adjustment {
Expand Down
6 changes: 4 additions & 2 deletions src/librustc_typeck/check/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ use rustc::hir;
use rustc::session::Session;
use rustc::traits;
use rustc::ty::{self, Ty, TypeFoldable};
use rustc::ty::adjustment::AllowTwoPhase;
use rustc::ty::cast::{CastKind, CastTy};
use rustc::ty::subst::Substs;
use rustc::middle::lang_items;
Expand Down Expand Up @@ -434,7 +435,8 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
let f = self.expr_ty.fn_sig(fcx.tcx);
let res = fcx.try_coerce(self.expr,
self.expr_ty,
fcx.tcx.mk_fn_ptr(f));
fcx.tcx.mk_fn_ptr(f),
AllowTwoPhase::No);
if !res.is_ok() {
return Err(CastError::NonScalar);
}
Expand Down Expand Up @@ -616,7 +618,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
}

fn try_coercion_cast(&self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> bool {
fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty).is_ok()
fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty, AllowTwoPhase::No).is_ok()
}
}

Expand Down
42 changes: 30 additions & 12 deletions src/librustc_typeck/check/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ use rustc::hir::def_id::DefId;
use rustc::infer::{Coercion, InferResult, InferOk};
use rustc::infer::type_variable::TypeVariableOrigin;
use rustc::traits::{self, ObligationCause, ObligationCauseCode};
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability};
use rustc::ty::adjustment::{Adjustment, Adjust, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc::ty::{self, TypeAndMut, Ty, ClosureSubsts};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::error::TypeError;
Expand All @@ -84,6 +84,13 @@ struct Coerce<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
cause: ObligationCause<'tcx>,
use_lub: bool,
/// Determines whether or not allow_two_phase_borrow is set on any
/// autoref adjustments we create while coercing. We don't want to
/// allow deref coercions to create two-phase borrows, at least initially,
/// but we do need two-phase borrows for function argument reborrows.
/// See #47489 and #48598
/// See docs on the "AllowTwoPhase" type for a more detailed discussion
allow_two_phase: AllowTwoPhase,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, lovin' the comments

}

impl<'a, 'gcx, 'tcx> Deref for Coerce<'a, 'gcx, 'tcx> {
Expand Down Expand Up @@ -123,10 +130,13 @@ fn success<'tcx>(adj: Vec<Adjustment<'tcx>>,
}

impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
fn new(fcx: &'f FnCtxt<'f, 'gcx, 'tcx>, cause: ObligationCause<'tcx>) -> Self {
fn new(fcx: &'f FnCtxt<'f, 'gcx, 'tcx>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: these days, I tend to move over to rustfmt style when changing a sig anyway

fn new(
    fcx: ...
    cause: ...
    allow_two_phase: ...,
) -> Self {

cause: ObligationCause<'tcx>,
allow_two_phase: AllowTwoPhase) -> Self {
Coerce {
fcx,
cause,
allow_two_phase,
use_lub: false,
}
}
Expand Down Expand Up @@ -424,10 +434,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
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,
allow_two_phase_borrow: self.allow_two_phase,
}
};
adjustments.push(Adjustment {
Expand Down Expand Up @@ -473,7 +480,10 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
let mutbl = match mt_b.mutbl {
hir::MutImmutable => AutoBorrowMutability::Immutable,
hir::MutMutable => AutoBorrowMutability::Mutable {
allow_two_phase_borrow: false,
// We don't allow two-phase borrows here, at least for initial
// implementation. If it happens that this coercion is a function argument,
// the reborrow in coerce_borrowed_ptr will pick it up.
allow_two_phase_borrow: AllowTwoPhase::No,
}
};
Some((Adjustment {
Expand Down Expand Up @@ -751,13 +761,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn try_coerce(&self,
expr: &hir::Expr,
expr_ty: Ty<'tcx>,
target: Ty<'tcx>)
target: Ty<'tcx>,
allow_two_phase: AllowTwoPhase)
-> RelateResult<'tcx, Ty<'tcx>> {
let source = self.resolve_type_vars_with_obligations(expr_ty);
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);

let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable);
let coerce = Coerce::new(self, cause);
let coerce = Coerce::new(self, cause, allow_two_phase);
let ok = self.commit_if_ok(|_| coerce.coerce(source, target))?;

let (adjustments, _) = self.register_infer_ok_obligations(ok);
Expand All @@ -771,7 +782,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
debug!("coercion::can({:?} -> {:?})", source, target);

let cause = self.cause(syntax_pos::DUMMY_SP, ObligationCauseCode::ExprAssignable);
let coerce = Coerce::new(self, cause);
// We don't ever need two-phase here since we throw out the result of the coercion
let coerce = Coerce::new(self, cause, AllowTwoPhase::No);
self.probe(|_| coerce.coerce(source, target)).is_ok()
}

Expand Down Expand Up @@ -840,7 +852,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
return Ok(fn_ptr);
}

let mut coerce = Coerce::new(self, cause.clone());
// Configure a Coerce instance to compute the LUB.
// We don't allow two-phase borrows on any autorefs this creates since we
// probably aren't processing function arguments here and even if we were,
// they're going to get autorefed again anyway and we can apply 2-phase borrows
// at that time.
let mut coerce = Coerce::new(self, cause.clone(), AllowTwoPhase::No);
coerce.use_lub = true;

// First try to coerce the new expression to the type of the previous ones,
Expand Down Expand Up @@ -1106,7 +1123,8 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
if self.pushed == 0 {
// Special-case the first expression we are coercing.
// To be honest, I'm not entirely sure why we do this.
fcx.try_coerce(expression, expression_ty, self.expected_ty)
// We don't allow two-phase borrows, see comment in try_find_coercion_lub for why
fcx.try_coerce(expression, expression_ty, self.expected_ty, AllowTwoPhase::No)
} else {
match self.expressions {
Expressions::Dynamic(ref exprs) =>
Expand Down
11 changes: 7 additions & 4 deletions src/librustc_typeck/check/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use rustc::hir;
use rustc::hir::print;
use rustc::hir::def::Def;
use rustc::ty::{self, Ty, AssociatedItem};
use rustc::ty::adjustment::AllowTwoPhase;
use errors::{DiagnosticBuilder, CodeMapper};

use super::method::probe;
Expand Down Expand Up @@ -79,9 +80,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn demand_coerce(&self,
expr: &hir::Expr,
checked_ty: Ty<'tcx>,
expected: Ty<'tcx>)
expected: Ty<'tcx>,
allow_two_phase: AllowTwoPhase)
-> Ty<'tcx> {
let (ty, err) = self.demand_coerce_diag(expr, checked_ty, expected);
let (ty, err) = self.demand_coerce_diag(expr, checked_ty, expected, allow_two_phase);
if let Some(mut err) = err {
err.emit();
}
Expand All @@ -96,11 +98,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn demand_coerce_diag(&self,
expr: &hir::Expr,
checked_ty: Ty<'tcx>,
expected: Ty<'tcx>)
expected: Ty<'tcx>,
allow_two_phase: AllowTwoPhase)
-> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx>>) {
let expected = self.resolve_type_vars_with_obligations(expected);

let e = match self.try_coerce(expr, checked_ty, expected) {
let e = match self.try_coerce(expr, checked_ty, expected, allow_two_phase) {
Ok(ty) => return (ty, None),
Err(e) => e
};
Expand Down
7 changes: 4 additions & 3 deletions src/librustc_typeck/check/method/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ 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, AutoBorrowMutability, OverloadedDeref};
use rustc::ty::adjustment::{Adjustment, Adjust, OverloadedDeref};
use rustc::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc::ty::fold::TypeFoldable;
use rustc::infer::{self, InferOk};
use syntax_pos::Span;
Expand Down Expand Up @@ -170,7 +171,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
hir::MutMutable => AutoBorrowMutability::Mutable {
// Method call receivers are the primary use case
// for two-phase borrows.
allow_two_phase_borrow: true,
allow_two_phase_borrow: AllowTwoPhase::Yes,
}
};
adjustments.push(Adjustment {
Expand Down Expand Up @@ -544,7 +545,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
// For initial two-phase borrow
// deployment, conservatively omit
// overloaded operators.
allow_two_phase_borrow: false,
allow_two_phase_borrow: AllowTwoPhase::No,
}
};
adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
Expand Down
28 changes: 17 additions & 11 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ use rustc::mir::interpret::{GlobalId};
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, AutoBorrowMutability};
use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::maps::Providers;
use rustc::ty::util::{Representability, IntTypeExt, Discr};
Expand Down Expand Up @@ -2341,12 +2341,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
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,
// Indexing can be desugared to a method call,
// so maybe we could use two-phase here.
// See the documentation of AllowTwoPhase for why that's
// not the case today.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you just referring to our general desire for caution? (I didn't see any specific notes about indexing)

allow_two_phase_borrow: AllowTwoPhase::No,
}
};
adjustments.push(Adjustment {
Expand Down Expand Up @@ -2649,7 +2648,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// to, which is `expected_ty` if `rvalue_hint` returns an
// `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise.
let coerce_ty = expected.and_then(|e| e.only_has_type(self));
self.demand_coerce(&arg, checked_ty, coerce_ty.unwrap_or(formal_ty));
// We're processing function arguments so we definitely want to use
// two-phase borrows.
self.demand_coerce(&arg,
checked_ty,
coerce_ty.unwrap_or(formal_ty),
AllowTwoPhase::Yes);

// 3. Relate the expected type and the formal one,
// if the expected type was used for the coercion.
Expand Down Expand Up @@ -2812,7 +2816,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
expr,
ExpectHasType(expected),
needs);
self.demand_coerce(expr, ty, expected)
// checks don't need two phase
self.demand_coerce(expr, ty, expected, AllowTwoPhase::No)
}

fn check_expr_with_hint(&self, expr: &'gcx hir::Expr,
Expand Down Expand Up @@ -3645,7 +3650,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// (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,
allow_two_phase_borrow: AllowTwoPhase::No,
}
};
self.apply_adjustments(oprnd, vec![Adjustment {
Expand Down Expand Up @@ -4112,7 +4117,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let base_t = self.structurally_resolved_type(expr.span, base_t);
match self.lookup_indexing(expr, base, base_t, idx_t, needs) {
Some((index_ty, element_ty)) => {
self.demand_coerce(idx, idx_t, index_ty);
// two-phase not needed because index_ty is never mutable
self.demand_coerce(idx, idx_t, index_ty, AllowTwoPhase::No);
element_ty
}
None => {
Expand Down
6 changes: 3 additions & 3 deletions src/librustc_typeck/check/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, TyAdt};
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability};
use rustc::ty::adjustment::{Adjustment, Adjust, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc::infer::type_variable::TypeVariableOrigin;
use errors;
use syntax_pos::Span;
Expand Down Expand Up @@ -206,7 +206,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
hir::MutMutable => AutoBorrowMutability::Mutable {
// Allow two-phase borrows for binops in initial deployment
// since they desugar to methods
allow_two_phase_borrow: true,
allow_two_phase_borrow: AllowTwoPhase::Yes,
}
};
let autoref = Adjustment {
Expand All @@ -223,7 +223,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
hir::MutMutable => AutoBorrowMutability::Mutable {
// Allow two-phase borrows for binops in initial deployment
// since they desugar to methods
allow_two_phase_borrow: true,
allow_two_phase_borrow: AllowTwoPhase::Yes,
}
};
let autoref = Adjustment {
Expand Down
Loading