Skip to content

Commit d37a7ab

Browse files
Extend two-phase borrows to apply to method receiver autorefs
This is required to compile things like src/test/ui/borrowck/two-phase-method-receivers.rs
1 parent a04b88d commit d37a7ab

File tree

6 files changed

+100
-37
lines changed

6 files changed

+100
-37
lines changed

src/librustc_typeck/check/cast.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,8 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
434434
let f = self.expr_ty.fn_sig(fcx.tcx);
435435
let res = fcx.try_coerce(self.expr,
436436
self.expr_ty,
437-
fcx.tcx.mk_fn_ptr(f));
437+
fcx.tcx.mk_fn_ptr(f),
438+
false);
438439
if !res.is_ok() {
439440
return Err(CastError::NonScalar);
440441
}
@@ -616,7 +617,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
616617
}
617618

618619
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()
620+
fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty, false).is_ok()
620621
}
621622
}
622623

src/librustc_typeck/check/coercion.rs

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ 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+
allow_two_phase: bool,
8793
}
8894

8995
impl<'a, 'gcx, 'tcx> Deref for Coerce<'a, 'gcx, 'tcx> {
@@ -123,10 +129,13 @@ fn success<'tcx>(adj: Vec<Adjustment<'tcx>>,
123129
}
124130

125131
impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
126-
fn new(fcx: &'f FnCtxt<'f, 'gcx, 'tcx>, cause: ObligationCause<'tcx>) -> Self {
132+
fn new(fcx: &'f FnCtxt<'f, 'gcx, 'tcx>,
133+
cause: ObligationCause<'tcx>,
134+
allow_two_phase: bool) -> Self {
127135
Coerce {
128136
fcx,
129137
cause,
138+
allow_two_phase,
130139
use_lub: false,
131140
}
132141
}
@@ -424,10 +433,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
424433
let mutbl = match mt_b.mutbl {
425434
hir::MutImmutable => AutoBorrowMutability::Immutable,
426435
hir::MutMutable => AutoBorrowMutability::Mutable {
427-
// Deref-coercion is a case where we deliberately
428-
// disallow two-phase borrows in its initial
429-
// deployment; see discussion on PR #47489.
430-
allow_two_phase_borrow: false,
436+
allow_two_phase_borrow: self.allow_two_phase,
431437
}
432438
};
433439
adjustments.push(Adjustment {
@@ -473,6 +479,9 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
473479
let mutbl = match mt_b.mutbl {
474480
hir::MutImmutable => AutoBorrowMutability::Immutable,
475481
hir::MutMutable => AutoBorrowMutability::Mutable {
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.
476485
allow_two_phase_borrow: false,
477486
}
478487
};
@@ -751,13 +760,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
751760
pub fn try_coerce(&self,
752761
expr: &hir::Expr,
753762
expr_ty: Ty<'tcx>,
754-
target: Ty<'tcx>)
763+
target: Ty<'tcx>,
764+
allow_two_phase: bool)
755765
-> RelateResult<'tcx, Ty<'tcx>> {
756766
let source = self.resolve_type_vars_with_obligations(expr_ty);
757767
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
758768

759769
let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable);
760-
let coerce = Coerce::new(self, cause);
770+
let coerce = Coerce::new(self, cause, allow_two_phase);
761771
let ok = self.commit_if_ok(|_| coerce.coerce(source, target))?;
762772

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

773783
let cause = self.cause(syntax_pos::DUMMY_SP, ObligationCauseCode::ExprAssignable);
774-
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, false);
775786
self.probe(|_| coerce.coerce(source, target)).is_ok()
776787
}
777788

@@ -840,7 +851,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
840851
return Ok(fn_ptr);
841852
}
842853

843-
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(), false);
844860
coerce.use_lub = true;
845861

846862
// First try to coerce the new expression to the type of the previous ones,
@@ -1106,7 +1122,8 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
11061122
if self.pushed == 0 {
11071123
// Special-case the first expression we are coercing.
11081124
// To be honest, I'm not entirely sure why we do this.
1109-
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, false)
11101127
} else {
11111128
match self.expressions {
11121129
Expressions::Dynamic(ref exprs) =>

src/librustc_typeck/check/demand.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
7979
pub fn demand_coerce(&self,
8080
expr: &hir::Expr,
8181
checked_ty: Ty<'tcx>,
82-
expected: Ty<'tcx>)
82+
expected: Ty<'tcx>,
83+
allow_two_phase: bool)
8384
-> Ty<'tcx> {
84-
let (ty, err) = self.demand_coerce_diag(expr, checked_ty, expected);
85+
let (ty, err) = self.demand_coerce_diag(expr, checked_ty, expected, allow_two_phase);
8586
if let Some(mut err) = err {
8687
err.emit();
8788
}
@@ -96,11 +97,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
9697
pub fn demand_coerce_diag(&self,
9798
expr: &hir::Expr,
9899
checked_ty: Ty<'tcx>,
99-
expected: Ty<'tcx>)
100+
expected: Ty<'tcx>,
101+
allow_two_phase: bool)
100102
-> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx>>) {
101103
let expected = self.resolve_type_vars_with_obligations(expected);
102104

103-
let e = match self.try_coerce(expr, checked_ty, expected) {
105+
let e = match self.try_coerce(expr, checked_ty, expected, allow_two_phase) {
104106
Ok(ty) => return (ty, None),
105107
Err(e) => e
106108
};

src/librustc_typeck/check/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2649,7 +2649,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
26492649
// to, which is `expected_ty` if `rvalue_hint` returns an
26502650
// `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise.
26512651
let coerce_ty = expected.and_then(|e| e.only_has_type(self));
2652-
self.demand_coerce(&arg, checked_ty, coerce_ty.unwrap_or(formal_ty));
2652+
self.demand_coerce(&arg, checked_ty, coerce_ty.unwrap_or(formal_ty), true);
26532653

26542654
// 3. Relate the expected type and the formal one,
26552655
// if the expected type was used for the coercion.
@@ -2812,7 +2812,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
28122812
expr,
28132813
ExpectHasType(expected),
28142814
needs);
2815-
self.demand_coerce(expr, ty, expected)
2815+
self.demand_coerce(expr, ty, expected, false)
28162816
}
28172817

28182818
fn check_expr_with_hint(&self, expr: &'gcx hir::Expr,
@@ -4112,7 +4112,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
41124112
let base_t = self.structurally_resolved_type(expr.span, base_t);
41134113
match self.lookup_indexing(expr, base, base_t, idx_t, needs) {
41144114
Some((index_ty, element_ty)) => {
4115-
self.demand_coerce(idx, idx_t, index_ty);
4115+
// two-phase not needed because index_ty is never mutable
4116+
self.demand_coerce(idx, idx_t, index_ty, false);
41164117
element_ty
41174118
}
41184119
None => {

src/test/compile-fail/borrowck/two-phase-nonrecv-autoref.rs

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// revisions: lxl nll
11+
// revisions: ast lxl nll
12+
//[ast]compile-flags:
1213
//[lxl]compile-flags: -Z borrowck=mir -Z two-phase-borrows
1314
//[nll]compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z nll
1415

@@ -33,17 +34,14 @@
3334

3435
use std::ops::{Index, IndexMut};
3536

36-
// This is case outlined by Niko that we want to ensure we reject
37-
// (at least initially).
38-
3937
fn foo(x: &mut u32, y: u32) {
4038
*x += y;
4139
}
4240

4341
fn deref_coercion(x: &mut u32) {
4442
foo(x, *x);
45-
//[lxl]~^ ERROR cannot use `*x` because it was mutably borrowed [E0503]
46-
//[nll]~^^ ERROR cannot use `*x` because it was mutably borrowed [E0503]
43+
//[ast]~^ ERROR cannot use `*x` because it was mutably borrowed [E0503]
44+
// Above error is a known limitation of AST borrowck
4745
}
4846

4947
// While adding a flag to adjustments (indicating whether they
@@ -74,22 +72,25 @@ fn overloaded_call_traits() {
7472
//[lxl]~^ ERROR cannot borrow `*f` as mutable more than once at a time
7573
//[nll]~^^ ERROR cannot borrow `*f` as mutable more than once at a time
7674
//[g2p]~^^^ ERROR cannot borrow `*f` as mutable more than once at a time
75+
//[ast]~^^^^ ERROR cannot borrow `*f` as mutable more than once at a time
7776
}
7877
fn twice_ten_si<F: Fn(i32) -> i32>(f: &mut F) {
7978
f(f(10));
8079
}
8180
fn twice_ten_so<F: FnOnce(i32) -> i32>(f: Box<F>) {
8281
f(f(10));
83-
//[lxl]~^ ERROR use of moved value: `*f`
84-
//[nll]~^^ ERROR use of moved value: `*f`
85-
//[g2p]~^^^ ERROR use of moved value: `*f`
82+
//[lxl]~^ ERROR use of moved value: `*f`
83+
//[nll]~^^ ERROR use of moved value: `*f`
84+
//[g2p]~^^^ ERROR use of moved value: `*f`
85+
//[ast]~^^^^ ERROR use of moved value: `*f`
8686
}
8787

8888
fn twice_ten_om(f: &mut FnMut(i32) -> i32) {
8989
f(f(10));
90-
//[lxl]~^ ERROR cannot borrow `*f` as mutable more than once at a time
90+
//[lxl]~^ ERROR cannot borrow `*f` as mutable more than once at a time
9191
//[nll]~^^ ERROR cannot borrow `*f` as mutable more than once at a time
92-
//[g2p]~^^^ ERROR cannot borrow `*f` as mutable more than once at a time
92+
//[g2p]~^^^ ERROR cannot borrow `*f` as mutable more than once at a time
93+
//[ast]~^^^^ ERROR cannot borrow `*f` as mutable more than once at a time
9394
}
9495
fn twice_ten_oi(f: &mut Fn(i32) -> i32) {
9596
f(f(10));
@@ -105,6 +106,7 @@ fn overloaded_call_traits() {
105106
//[g2p]~^^^^^^^ ERROR cannot move a value of type
106107
//[g2p]~^^^^^^^^ ERROR cannot move a value of type
107108
//[g2p]~^^^^^^^^^ ERROR use of moved value: `*f`
109+
//[ast]~^^^^^^^^^^ ERROR use of moved value: `*f`
108110
}
109111

110112
twice_ten_sm(&mut |x| x + 1);
@@ -142,12 +144,15 @@ fn coerce_unsized() {
142144

143145
// This is not okay.
144146
double_access(&mut a, &a);
145-
//[lxl]~^ ERROR cannot borrow `a` as immutable because it is also borrowed as mutable [E0502]
146-
//[nll]~^^ ERROR cannot borrow `a` as immutable because it is also borrowed as mutable [E0502]
147-
//[g2p]~^^^ ERROR cannot borrow `a` as immutable because it is also borrowed as mutable [E0502]
147+
//[lxl]~^ ERROR cannot borrow `a` as immutable because it is also borrowed as mutable [E0502]
148+
//[nll]~^^ ERROR cannot borrow `a` as immutable because it is also borrowed as mutable [E0502]
149+
//[g2p]~^^^ ERROR cannot borrow `a` as immutable because it is also borrowed as mutable [E0502]
150+
//[ast]~^^^^ ERROR cannot borrow `a` as immutable because it is also borrowed as mutable [E0502]
148151

149152
// But this is okay.
150153
a.m(a.i(10));
154+
//[ast]~^ ERROR cannot borrow `a` as immutable because it is also borrowed as mutable [E0502]
155+
// Above error is an expected limitation of AST borrowck
151156
}
152157

153158
struct I(i32);
@@ -168,21 +173,25 @@ impl IndexMut<i32> for I {
168173
fn coerce_index_op() {
169174
let mut i = I(10);
170175
i[i[3]] = 4;
171-
//[lxl]~^ ERROR cannot borrow `i` as immutable because it is also borrowed as mutable [E0502]
172-
//[nll]~^^ ERROR cannot borrow `i` as immutable because it is also borrowed as mutable [E0502]
176+
//[lxl]~^ ERROR cannot borrow `i` as immutable because it is also borrowed as mutable [E0502]
177+
//[nll]~^^ ERROR cannot borrow `i` as immutable because it is also borrowed as mutable [E0502]
178+
//[ast]~^^^ ERROR cannot borrow `i` as immutable because it is also borrowed as mutable [E0502]
173179

174180
i[3] = i[4];
175181

176182
i[i[3]] = i[4];
177-
//[lxl]~^ ERROR cannot borrow `i` as immutable because it is also borrowed as mutable [E0502]
178-
//[nll]~^^ ERROR cannot borrow `i` as immutable because it is also borrowed as mutable [E0502]
183+
//[lxl]~^ ERROR cannot borrow `i` as immutable because it is also borrowed as mutable [E0502]
184+
//[nll]~^^ ERROR cannot borrow `i` as immutable because it is also borrowed as mutable [E0502]
185+
//[ast]~^^^ ERROR cannot borrow `i` as immutable because it is also borrowed as mutable [E0502]
179186
}
180187

181188
fn main() {
182189

183190
// As a reminder, this is the basic case we want to ensure we handle.
184191
let mut v = vec![1, 2, 3];
185192
v.push(v.len());
193+
//[ast]~^ ERROR cannot borrow `v` as immutable because it is also borrowed as mutable [E0502]
194+
// Error above is an expected limitation of AST borrowck
186195

187196
// (as a rule, pnkfelix does not like to write tests with dead code.)
188197

@@ -192,9 +201,13 @@ fn main() {
192201

193202
let mut s = S;
194203
s.m(s.i(10));
204+
//[ast]~^ ERROR cannot borrow `s` as immutable because it is also borrowed as mutable [E0502]
205+
// Error above is an expected limitation of AST borrowck
195206

196207
let mut t = T;
197208
t.m(t.i(10));
209+
//[ast]~^ ERROR cannot borrow `t` as immutable because it is also borrowed as mutable [E0502]
210+
// Error above is an expected limitation of AST borrowck
198211

199212
coerce_unsized();
200213
coerce_index_op();
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// revisions: lxl nll
12+
//[lxl]compile-flags: -Z borrowck=mir -Z two-phase-borrows
13+
//[nll]compile-flags: -Z borrowck=mir -Z two-phase-borrows -Z nll
14+
15+
// run-pass
16+
17+
struct Foo<'a> {
18+
x: &'a i32
19+
}
20+
21+
impl<'a> Foo<'a> {
22+
fn method(&mut self, _: &i32) {
23+
}
24+
}
25+
26+
fn main() {
27+
let a = &mut Foo { x: &22 };
28+
Foo::method(a, a.x);
29+
}

0 commit comments

Comments
 (0)