Skip to content

Commit b12af86

Browse files
committed
Auto merge of #49348 - bobtwinkles:extend_2pb, r=nikomatsakis
Extend two-phase borrows to apply to method receiver autorefs Fixes #48598 by permitting two-phase borrows on the autorefs created when functions and methods.
2 parents 577d29c + d8352af commit b12af86

File tree

12 files changed

+155
-57
lines changed

12 files changed

+155
-57
lines changed

src/librustc/ich/impls_ty.rs

+4
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,10 @@ for ty::adjustment::Adjust<'gcx> {
188188
impl_stable_hash_for!(struct ty::adjustment::Adjustment<'tcx> { kind, target });
189189
impl_stable_hash_for!(struct ty::adjustment::OverloadedDeref<'tcx> { region, mutbl });
190190
impl_stable_hash_for!(struct ty::UpvarBorrow<'tcx> { kind, region });
191+
impl_stable_hash_for!(enum ty::adjustment::AllowTwoPhase {
192+
Yes,
193+
No
194+
});
191195

192196
impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::adjustment::AutoBorrowMutability {
193197
fn hash_stable<W: StableHasherResult>(&self,

src/librustc/ty/adjustment.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,27 @@ impl<'a, 'gcx, 'tcx> OverloadedDeref<'tcx> {
119119
}
120120
}
121121

122+
/// At least for initial deployment, we want to limit two-phase borrows to
123+
/// only a few specific cases. Right now, those mostly "things that desugar"
124+
/// into method calls
125+
/// - using x.some_method() syntax, where some_method takes &mut self
126+
/// - using Foo::some_method(&mut x, ...) syntax
127+
/// - binary assignment operators (+=, -=, *=, etc.)
128+
/// Anything else should be rejected until generalized two phase borrow support
129+
/// is implemented. Right now, dataflow can't handle the general case where there
130+
/// is more than one use of a mutable borrow, and we don't want to accept too much
131+
/// new code via two-phase borrows, so we try to limit where we create two-phase
132+
/// capable mutable borrows.
133+
/// See #49434 for tracking.
134+
#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable)]
135+
pub enum AllowTwoPhase {
136+
Yes,
137+
No
138+
}
139+
122140
#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable)]
123141
pub enum AutoBorrowMutability {
124-
Mutable { allow_two_phase_borrow: bool },
142+
Mutable { allow_two_phase_borrow: AllowTwoPhase },
125143
Immutable,
126144
}
127145

src/librustc_mir/hair/cx/expr.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -662,9 +662,13 @@ trait ToBorrowKind { fn to_borrow_kind(&self) -> BorrowKind; }
662662

663663
impl ToBorrowKind for AutoBorrowMutability {
664664
fn to_borrow_kind(&self) -> BorrowKind {
665+
use rustc::ty::adjustment::AllowTwoPhase;
665666
match *self {
666667
AutoBorrowMutability::Mutable { allow_two_phase_borrow } =>
667-
BorrowKind::Mut { allow_two_phase_borrow },
668+
BorrowKind::Mut { allow_two_phase_borrow: match allow_two_phase_borrow {
669+
AllowTwoPhase::Yes => true,
670+
AllowTwoPhase::No => false
671+
}},
668672
AutoBorrowMutability::Immutable =>
669673
BorrowKind::Shared,
670674
}

src/librustc_typeck/check/callee.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use hir::def::Def;
1616
use hir::def_id::{DefId, LOCAL_CRATE};
1717
use rustc::{infer, traits};
1818
use rustc::ty::{self, TyCtxt, TypeFoldable, Ty};
19-
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability};
19+
use rustc::ty::adjustment::{Adjustment, Adjust, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
2020
use syntax::abi;
2121
use syntax::symbol::Symbol;
2222
use syntax_pos::Span;
@@ -182,7 +182,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
182182
// For initial two-phase borrow
183183
// deployment, conservatively omit
184184
// overloaded function call ops.
185-
allow_two_phase_borrow: false,
185+
allow_two_phase_borrow: AllowTwoPhase::No,
186186
}
187187
};
188188
autoref = Some(Adjustment {

src/librustc_typeck/check/cast.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ use rustc::hir;
4747
use rustc::session::Session;
4848
use rustc::traits;
4949
use rustc::ty::{self, Ty, TypeFoldable};
50+
use rustc::ty::adjustment::AllowTwoPhase;
5051
use rustc::ty::cast::{CastKind, CastTy};
5152
use rustc::ty::subst::Substs;
5253
use rustc::middle::lang_items;
@@ -434,7 +435,8 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
434435
let f = self.expr_ty.fn_sig(fcx.tcx);
435436
let res = fcx.try_coerce(self.expr,
436437
self.expr_ty,
437-
fcx.tcx.mk_fn_ptr(f));
438+
fcx.tcx.mk_fn_ptr(f),
439+
AllowTwoPhase::No);
438440
if !res.is_ok() {
439441
return Err(CastError::NonScalar);
440442
}
@@ -616,7 +618,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
616618
}
617619

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

src/librustc_typeck/check/coercion.rs

+30-12
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ use rustc::hir::def_id::DefId;
6767
use rustc::infer::{Coercion, InferResult, InferOk};
6868
use rustc::infer::type_variable::TypeVariableOrigin;
6969
use rustc::traits::{self, ObligationCause, ObligationCauseCode};
70-
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability};
70+
use rustc::ty::adjustment::{Adjustment, Adjust, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
7171
use rustc::ty::{self, TypeAndMut, Ty, ClosureSubsts};
7272
use rustc::ty::fold::TypeFoldable;
7373
use rustc::ty::error::TypeError;
@@ -84,6 +84,13 @@ struct Coerce<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
8484
fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
8585
cause: ObligationCause<'tcx>,
8686
use_lub: bool,
87+
/// Determines whether or not allow_two_phase_borrow is set on any
88+
/// autoref adjustments we create while coercing. We don't want to
89+
/// allow deref coercions to create two-phase borrows, at least initially,
90+
/// but we do need two-phase borrows for function argument reborrows.
91+
/// See #47489 and #48598
92+
/// See docs on the "AllowTwoPhase" type for a more detailed discussion
93+
allow_two_phase: AllowTwoPhase,
8794
}
8895

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

125132
impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
126-
fn new(fcx: &'f FnCtxt<'f, 'gcx, 'tcx>, cause: ObligationCause<'tcx>) -> Self {
133+
fn new(fcx: &'f FnCtxt<'f, 'gcx, 'tcx>,
134+
cause: ObligationCause<'tcx>,
135+
allow_two_phase: AllowTwoPhase) -> Self {
127136
Coerce {
128137
fcx,
129138
cause,
139+
allow_two_phase,
130140
use_lub: false,
131141
}
132142
}
@@ -423,10 +433,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
423433
let mutbl = match mt_b.mutbl {
424434
hir::MutImmutable => AutoBorrowMutability::Immutable,
425435
hir::MutMutable => AutoBorrowMutability::Mutable {
426-
// Deref-coercion is a case where we deliberately
427-
// disallow two-phase borrows in its initial
428-
// deployment; see discussion on PR #47489.
429-
allow_two_phase_borrow: false,
436+
allow_two_phase_borrow: self.allow_two_phase,
430437
}
431438
};
432439
adjustments.push(Adjustment {
@@ -472,7 +479,10 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
472479
let mutbl = match mt_b.mutbl {
473480
hir::MutImmutable => AutoBorrowMutability::Immutable,
474481
hir::MutMutable => AutoBorrowMutability::Mutable {
475-
allow_two_phase_borrow: false,
482+
// We don't allow two-phase borrows here, at least for initial
483+
// implementation. If it happens that this coercion is a function argument,
484+
// the reborrow in coerce_borrowed_ptr will pick it up.
485+
allow_two_phase_borrow: AllowTwoPhase::No,
476486
}
477487
};
478488
Some((Adjustment {
@@ -750,13 +760,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
750760
pub fn try_coerce(&self,
751761
expr: &hir::Expr,
752762
expr_ty: Ty<'tcx>,
753-
target: Ty<'tcx>)
763+
target: Ty<'tcx>,
764+
allow_two_phase: AllowTwoPhase)
754765
-> RelateResult<'tcx, Ty<'tcx>> {
755766
let source = self.resolve_type_vars_with_obligations(expr_ty);
756767
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
757768

758769
let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable);
759-
let coerce = Coerce::new(self, cause);
770+
let coerce = Coerce::new(self, cause, allow_two_phase);
760771
let ok = self.commit_if_ok(|_| coerce.coerce(source, target))?;
761772

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

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

@@ -839,7 +851,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
839851
return Ok(fn_ptr);
840852
}
841853

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

845862
// First try to coerce the new expression to the type of the previous ones,
@@ -1105,7 +1122,8 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
11051122
if self.pushed == 0 {
11061123
// Special-case the first expression we are coercing.
11071124
// To be honest, I'm not entirely sure why we do this.
1108-
fcx.try_coerce(expression, expression_ty, self.expected_ty)
1125+
// We don't allow two-phase borrows, see comment in try_find_coercion_lub for why
1126+
fcx.try_coerce(expression, expression_ty, self.expected_ty, AllowTwoPhase::No)
11091127
} else {
11101128
match self.expressions {
11111129
Expressions::Dynamic(ref exprs) =>

src/librustc_typeck/check/demand.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use rustc::hir::def::Def;
2222
use rustc::hir::map::NodeItem;
2323
use rustc::hir::{Item, ItemConst, print};
2424
use rustc::ty::{self, Ty, AssociatedItem};
25+
use rustc::ty::adjustment::AllowTwoPhase;
2526
use errors::{DiagnosticBuilder, CodeMapper};
2627

2728
use super::method::probe;
@@ -80,9 +81,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
8081
pub fn demand_coerce(&self,
8182
expr: &hir::Expr,
8283
checked_ty: Ty<'tcx>,
83-
expected: Ty<'tcx>)
84+
expected: Ty<'tcx>,
85+
allow_two_phase: AllowTwoPhase)
8486
-> Ty<'tcx> {
85-
let (ty, err) = self.demand_coerce_diag(expr, checked_ty, expected);
87+
let (ty, err) = self.demand_coerce_diag(expr, checked_ty, expected, allow_two_phase);
8688
if let Some(mut err) = err {
8789
err.emit();
8890
}
@@ -97,11 +99,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
9799
pub fn demand_coerce_diag(&self,
98100
expr: &hir::Expr,
99101
checked_ty: Ty<'tcx>,
100-
expected: Ty<'tcx>)
102+
expected: Ty<'tcx>,
103+
allow_two_phase: AllowTwoPhase)
101104
-> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx>>) {
102105
let expected = self.resolve_type_vars_with_obligations(expected);
103106

104-
let e = match self.try_coerce(expr, checked_ty, expected) {
107+
let e = match self.try_coerce(expr, checked_ty, expected, allow_two_phase) {
105108
Ok(ty) => return (ty, None),
106109
Err(e) => e
107110
};

src/librustc_typeck/check/method/confirm.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ use rustc::ty::subst::Substs;
1717
use rustc::traits;
1818
use rustc::ty::{self, Ty};
1919
use rustc::ty::subst::Subst;
20-
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability, OverloadedDeref};
20+
use rustc::ty::adjustment::{Adjustment, Adjust, OverloadedDeref};
21+
use rustc::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
2122
use rustc::ty::fold::TypeFoldable;
2223
use rustc::infer::{self, InferOk};
2324
use syntax_pos::Span;
@@ -170,7 +171,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
170171
hir::MutMutable => AutoBorrowMutability::Mutable {
171172
// Method call receivers are the primary use case
172173
// for two-phase borrows.
173-
allow_two_phase_borrow: true,
174+
allow_two_phase_borrow: AllowTwoPhase::Yes,
174175
}
175176
};
176177
adjustments.push(Adjustment {
@@ -544,7 +545,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
544545
// For initial two-phase borrow
545546
// deployment, conservatively omit
546547
// overloaded operators.
547-
allow_two_phase_borrow: false,
548+
allow_two_phase_borrow: AllowTwoPhase::No,
548549
}
549550
};
550551
adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));

src/librustc_typeck/check/mod.rs

+17-11
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ use rustc::mir::interpret::{GlobalId};
9797
use rustc::ty::subst::{Kind, Subst, Substs};
9898
use rustc::traits::{self, ObligationCause, ObligationCauseCode, TraitEngine};
9999
use rustc::ty::{self, Ty, TyCtxt, Visibility, ToPredicate};
100-
use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
100+
use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
101101
use rustc::ty::fold::TypeFoldable;
102102
use rustc::ty::maps::Providers;
103103
use rustc::ty::util::{Representability, IntTypeExt, Discr};
@@ -2377,12 +2377,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
23772377
let mutbl = match mt.mutbl {
23782378
hir::MutImmutable => AutoBorrowMutability::Immutable,
23792379
hir::MutMutable => AutoBorrowMutability::Mutable {
2380-
// FIXME (#46747): arguably indexing is
2381-
// "just another kind of call"; perhaps it
2382-
// would be more consistent to allow
2383-
// two-phase borrows for .index()
2384-
// receivers here.
2385-
allow_two_phase_borrow: false,
2380+
// Indexing can be desugared to a method call,
2381+
// so maybe we could use two-phase here.
2382+
// See the documentation of AllowTwoPhase for why that's
2383+
// not the case today.
2384+
allow_two_phase_borrow: AllowTwoPhase::No,
23862385
}
23872386
};
23882387
adjustments.push(Adjustment {
@@ -2685,7 +2684,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
26852684
// to, which is `expected_ty` if `rvalue_hint` returns an
26862685
// `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise.
26872686
let coerce_ty = expected.and_then(|e| e.only_has_type(self));
2688-
self.demand_coerce(&arg, checked_ty, coerce_ty.unwrap_or(formal_ty));
2687+
// We're processing function arguments so we definitely want to use
2688+
// two-phase borrows.
2689+
self.demand_coerce(&arg,
2690+
checked_ty,
2691+
coerce_ty.unwrap_or(formal_ty),
2692+
AllowTwoPhase::Yes);
26892693

26902694
// 3. Relate the expected type and the formal one,
26912695
// if the expected type was used for the coercion.
@@ -2847,7 +2851,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
28472851
expr,
28482852
ExpectHasType(expected),
28492853
needs);
2850-
self.demand_coerce(expr, ty, expected)
2854+
// checks don't need two phase
2855+
self.demand_coerce(expr, ty, expected, AllowTwoPhase::No)
28512856
}
28522857

28532858
fn check_expr_with_hint(&self, expr: &'gcx hir::Expr,
@@ -3674,7 +3679,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
36743679
// (It shouldn't actually matter for unary ops whether
36753680
// we enable two-phase borrows or not, since a unary
36763681
// op has no additional operands.)
3677-
allow_two_phase_borrow: false,
3682+
allow_two_phase_borrow: AllowTwoPhase::No,
36783683
}
36793684
};
36803685
self.apply_adjustments(oprnd, vec![Adjustment {
@@ -4138,7 +4143,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
41384143
let base_t = self.structurally_resolved_type(expr.span, base_t);
41394144
match self.lookup_indexing(expr, base, base_t, idx_t, needs) {
41404145
Some((index_ty, element_ty)) => {
4141-
self.demand_coerce(idx, idx_t, index_ty);
4146+
// two-phase not needed because index_ty is never mutable
4147+
self.demand_coerce(idx, idx_t, index_ty, AllowTwoPhase::No);
41424148
element_ty
41434149
}
41444150
None => {

src/librustc_typeck/check/op.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use super::{FnCtxt, Needs};
1414
use super::method::MethodCallee;
1515
use rustc::ty::{self, Ty, TypeFoldable, TypeVariants};
1616
use rustc::ty::TypeVariants::{TyStr, TyRef, TyAdt};
17-
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability};
17+
use rustc::ty::adjustment::{Adjustment, Adjust, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
1818
use rustc::infer::type_variable::TypeVariableOrigin;
1919
use errors;
2020
use syntax_pos::Span;
@@ -203,7 +203,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
203203
hir::MutMutable => AutoBorrowMutability::Mutable {
204204
// Allow two-phase borrows for binops in initial deployment
205205
// since they desugar to methods
206-
allow_two_phase_borrow: true,
206+
allow_two_phase_borrow: AllowTwoPhase::Yes,
207207
}
208208
};
209209
let autoref = Adjustment {
@@ -220,7 +220,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
220220
hir::MutMutable => AutoBorrowMutability::Mutable {
221221
// Allow two-phase borrows for binops in initial deployment
222222
// since they desugar to methods
223-
allow_two_phase_borrow: true,
223+
allow_two_phase_borrow: AllowTwoPhase::Yes,
224224
}
225225
};
226226
let autoref = Adjustment {

0 commit comments

Comments
 (0)