Skip to content

Commit 8661b3d

Browse files
committed
Auto merge of #21971 - pnkfelix:fsk-restrict-fixdsz-array-moves, r=nikomatsakis
Revised version of PR #21930. Restrictions on moves into and out-from fixed-length arrays. (There was only one use of this "feature" in the compiler source.) Note 1: the change to the error message in tests/compile-fail/borrowck-use-in-index-lvalue.rs, where we now report that *w is uninitialized (rather than w), was unintended fallout from the implementation strategy used here. The change appears harmless to me, but I welcome advice on how to bring back the old message, which was slightly cleaner (i.e. less unintelligible) since that the syntactic form *w does not actually appear in the source text. Note 2: the move out-from restriction to only apply to expr[i], and not destructuring bind (e.g. f([a, b, c]: Array) { ... }) since the latter is compatible with nonzeroing drop, AFAICT. [breaking-change]
2 parents 80627cd + 4583272 commit 8661b3d

17 files changed

+276
-108
lines changed

src/librustc/middle/mem_categorization.rs

+56-21
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ pub enum PointerKind {
128128
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
129129
pub enum InteriorKind {
130130
InteriorField(FieldName),
131-
InteriorElement(ElementKind),
131+
InteriorElement(InteriorOffsetKind, ElementKind),
132132
}
133133

134134
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
@@ -137,6 +137,12 @@ pub enum FieldName {
137137
PositionalField(uint)
138138
}
139139

140+
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
141+
pub enum InteriorOffsetKind {
142+
Index, // e.g. `array_expr[index_expr]`
143+
Pattern, // e.g. `fn foo([_, a, _, _]: [A; 4]) { ... }`
144+
}
145+
140146
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
141147
pub enum ElementKind {
142148
VecElement,
@@ -196,10 +202,12 @@ pub enum deref_kind {
196202
deref_interior(InteriorKind),
197203
}
198204

205+
type DerefKindContext = Option<InteriorOffsetKind>;
206+
199207
// Categorizes a derefable type. Note that we include vectors and strings as
200208
// derefable (we model an index as the combination of a deref and then a
201209
// pointer adjustment).
202-
pub fn deref_kind(t: Ty) -> McResult<deref_kind> {
210+
fn deref_kind(t: Ty, context: DerefKindContext) -> McResult<deref_kind> {
203211
match t.sty {
204212
ty::ty_uniq(_) => {
205213
Ok(deref_ptr(Unique))
@@ -220,7 +228,12 @@ pub fn deref_kind(t: Ty) -> McResult<deref_kind> {
220228
}
221229

222230
ty::ty_vec(_, _) | ty::ty_str => {
223-
Ok(deref_interior(InteriorElement(element_kind(t))))
231+
// no deref of indexed content without supplying InteriorOffsetKind
232+
if let Some(context) = context {
233+
Ok(deref_interior(InteriorElement(context, element_kind(t))))
234+
} else {
235+
Err(())
236+
}
224237
}
225238

226239
_ => Err(()),
@@ -455,7 +468,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
455468
autoderefs,
456469
cmt.repr(self.tcx()));
457470
for deref in 1..autoderefs + 1 {
458-
cmt = try!(self.cat_deref(expr, cmt, deref));
471+
cmt = try!(self.cat_deref(expr, cmt, deref, None));
459472
}
460473
return Ok(cmt);
461474
}
@@ -467,7 +480,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
467480
match expr.node {
468481
ast::ExprUnary(ast::UnDeref, ref e_base) => {
469482
let base_cmt = try!(self.cat_expr(&**e_base));
470-
self.cat_deref(expr, base_cmt, 0)
483+
self.cat_deref(expr, base_cmt, 0, None)
471484
}
472485

473486
ast::ExprField(ref base, f_name) => {
@@ -486,6 +499,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
486499

487500
ast::ExprIndex(ref base, _) => {
488501
let method_call = ty::MethodCall::expr(expr.id());
502+
let context = InteriorOffsetKind::Index;
489503
match self.typer.node_method_ty(method_call) {
490504
Some(method_ty) => {
491505
// If this is an index implemented by a method call, then it
@@ -507,10 +521,10 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
507521
// is an rvalue. That is what we will be
508522
// dereferencing.
509523
let base_cmt = self.cat_rvalue_node(expr.id(), expr.span(), ret_ty);
510-
self.cat_deref_common(expr, base_cmt, 1, elem_ty, true)
524+
self.cat_deref_common(expr, base_cmt, 1, elem_ty, Some(context), true)
511525
}
512526
None => {
513-
self.cat_index(expr, try!(self.cat_expr(&**base)))
527+
self.cat_index(expr, try!(self.cat_expr(&**base)), context)
514528
}
515529
}
516530
}
@@ -854,7 +868,8 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
854868
fn cat_deref<N:ast_node>(&self,
855869
node: &N,
856870
base_cmt: cmt<'tcx>,
857-
deref_cnt: uint)
871+
deref_cnt: uint,
872+
deref_context: DerefKindContext)
858873
-> McResult<cmt<'tcx>> {
859874
let adjustment = match self.typer.adjustments().borrow().get(&node.id()) {
860875
Some(adj) if ty::adjust_is_object(adj) => ty::AutoObject,
@@ -882,7 +897,9 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
882897
};
883898
let base_cmt_ty = base_cmt.ty;
884899
match ty::deref(base_cmt_ty, true) {
885-
Some(mt) => self.cat_deref_common(node, base_cmt, deref_cnt, mt.ty,
900+
Some(mt) => self.cat_deref_common(node, base_cmt, deref_cnt,
901+
mt.ty,
902+
deref_context,
886903
/* implicit: */ false),
887904
None => {
888905
debug!("Explicit deref of non-derefable type: {}",
@@ -897,10 +914,11 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
897914
base_cmt: cmt<'tcx>,
898915
deref_cnt: uint,
899916
deref_ty: Ty<'tcx>,
917+
deref_context: DerefKindContext,
900918
implicit: bool)
901919
-> McResult<cmt<'tcx>>
902920
{
903-
let (m, cat) = match try!(deref_kind(base_cmt.ty)) {
921+
let (m, cat) = match try!(deref_kind(base_cmt.ty, deref_context)) {
904922
deref_ptr(ptr) => {
905923
let ptr = if implicit {
906924
match ptr {
@@ -932,7 +950,8 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
932950

933951
pub fn cat_index<N:ast_node>(&self,
934952
elt: &N,
935-
mut base_cmt: cmt<'tcx>)
953+
mut base_cmt: cmt<'tcx>,
954+
context: InteriorOffsetKind)
936955
-> McResult<cmt<'tcx>> {
937956
//! Creates a cmt for an indexing operation (`[]`).
938957
//!
@@ -974,18 +993,21 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
974993
};
975994

976995
let m = base_cmt.mutbl.inherit();
977-
return Ok(interior(elt, base_cmt.clone(), base_cmt.ty, m, element_ty));
996+
return Ok(interior(elt, base_cmt.clone(), base_cmt.ty,
997+
m, context, element_ty));
978998

979999
fn interior<'tcx, N: ast_node>(elt: &N,
9801000
of_cmt: cmt<'tcx>,
9811001
vec_ty: Ty<'tcx>,
9821002
mutbl: MutabilityCategory,
1003+
context: InteriorOffsetKind,
9831004
element_ty: Ty<'tcx>) -> cmt<'tcx>
9841005
{
1006+
let interior_elem = InteriorElement(context, element_kind(vec_ty));
9851007
Rc::new(cmt_ {
9861008
id:elt.id(),
9871009
span:elt.span(),
988-
cat:cat_interior(of_cmt, InteriorElement(element_kind(vec_ty))),
1010+
cat:cat_interior(of_cmt, interior_elem),
9891011
mutbl:mutbl,
9901012
ty:element_ty,
9911013
note: NoteNone
@@ -997,10 +1019,11 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
9971019
// underlying vec.
9981020
fn deref_vec<N:ast_node>(&self,
9991021
elt: &N,
1000-
base_cmt: cmt<'tcx>)
1022+
base_cmt: cmt<'tcx>,
1023+
context: InteriorOffsetKind)
10011024
-> McResult<cmt<'tcx>>
10021025
{
1003-
match try!(deref_kind(base_cmt.ty)) {
1026+
match try!(deref_kind(base_cmt.ty, Some(context))) {
10041027
deref_ptr(ptr) => {
10051028
// for unique ptrs, we inherit mutability from the
10061029
// owning reference.
@@ -1041,7 +1064,9 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
10411064
let (slice_mutbl, slice_r) = vec_slice_info(self.tcx(),
10421065
slice_pat,
10431066
slice_ty);
1044-
let cmt_slice = try!(self.cat_index(slice_pat, try!(self.deref_vec(slice_pat, vec_cmt))));
1067+
let context = InteriorOffsetKind::Pattern;
1068+
let cmt_vec = try!(self.deref_vec(slice_pat, vec_cmt, context));
1069+
let cmt_slice = try!(self.cat_index(slice_pat, cmt_vec, context));
10451070
return Ok((cmt_slice, slice_mutbl, slice_r));
10461071

10471072
/// In a pattern like [a, b, ..c], normally `c` has slice type, but if you have [a, b,
@@ -1253,12 +1278,14 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
12531278
// box p1, &p1, &mut p1. we can ignore the mutability of
12541279
// PatRegion since that information is already contained
12551280
// in the type.
1256-
let subcmt = try!(self.cat_deref(pat, cmt, 0));
1281+
let subcmt = try!(self.cat_deref(pat, cmt, 0, None));
12571282
try!(self.cat_pattern_(subcmt, &**subpat, op));
12581283
}
12591284

12601285
ast::PatVec(ref before, ref slice, ref after) => {
1261-
let elt_cmt = try!(self.cat_index(pat, try!(self.deref_vec(pat, cmt))));
1286+
let context = InteriorOffsetKind::Pattern;
1287+
let vec_cmt = try!(self.deref_vec(pat, cmt, context));
1288+
let elt_cmt = try!(self.cat_index(pat, vec_cmt, context));
12621289
for before_pat in before {
12631290
try!(self.cat_pattern_(elt_cmt.clone(), &**before_pat, op));
12641291
}
@@ -1455,10 +1482,18 @@ impl<'tcx> cmt_<'tcx> {
14551482
cat_interior(_, InteriorField(PositionalField(_))) => {
14561483
"anonymous field".to_string()
14571484
}
1458-
cat_interior(_, InteriorElement(VecElement)) |
1459-
cat_interior(_, InteriorElement(OtherElement)) => {
1485+
cat_interior(_, InteriorElement(InteriorOffsetKind::Index,
1486+
VecElement)) |
1487+
cat_interior(_, InteriorElement(InteriorOffsetKind::Index,
1488+
OtherElement)) => {
14601489
"indexed content".to_string()
14611490
}
1491+
cat_interior(_, InteriorElement(InteriorOffsetKind::Pattern,
1492+
VecElement)) |
1493+
cat_interior(_, InteriorElement(InteriorOffsetKind::Pattern,
1494+
OtherElement)) => {
1495+
"pattern-bound indexed content".to_string()
1496+
}
14621497
cat_upvar(ref var) => {
14631498
var.user_string(tcx)
14641499
}
@@ -1546,7 +1581,7 @@ impl<'tcx> Repr<'tcx> for InteriorKind {
15461581
token::get_name(fld).to_string()
15471582
}
15481583
InteriorField(PositionalField(i)) => format!("#{}", i),
1549-
InteriorElement(_) => "[]".to_string(),
1584+
InteriorElement(..) => "[]".to_string(),
15501585
}
15511586
}
15521587
}

src/librustc_borrowck/borrowck/check_loans.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use self::UseError::*;
2020

2121
use borrowck::*;
22+
use borrowck::InteriorKind::{InteriorElement, InteriorField};
2223
use rustc::middle::expr_use_visitor as euv;
2324
use rustc::middle::mem_categorization as mc;
2425
use rustc::middle::region;
@@ -743,15 +744,16 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
743744
self.check_if_assigned_path_is_moved(id, span,
744745
use_kind, lp_base);
745746
}
746-
LpExtend(ref lp_base, _, LpInterior(_)) => {
747+
LpExtend(ref lp_base, _, LpInterior(InteriorField(_))) => {
747748
// assigning to `P.f` is ok if assigning to `P` is ok
748749
self.check_if_assigned_path_is_moved(id, span,
749750
use_kind, lp_base);
750751
}
752+
LpExtend(ref lp_base, _, LpInterior(InteriorElement(..))) |
751753
LpExtend(ref lp_base, _, LpDeref(_)) => {
752-
// assigning to `(*P)` requires that `P` be initialized
753-
self.check_if_path_is_moved(id, span,
754-
use_kind, lp_base);
754+
// assigning to `P[i]` requires `P` is initialized
755+
// assigning to `(*P)` requires `P` is initialized
756+
self.check_if_path_is_moved(id, span, use_kind, lp_base);
755757
}
756758
}
757759
}

src/librustc_borrowck/borrowck/fragments.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
1515
use self::Fragment::*;
1616

17+
use borrowck::InteriorKind::{InteriorField, InteriorElement};
1718
use borrowck::{LoanPath};
1819
use borrowck::LoanPathKind::{LpVar, LpUpvar, LpDowncast, LpExtend};
1920
use borrowck::LoanPathElem::{LpDeref, LpInterior};
@@ -300,20 +301,24 @@ fn add_fragment_siblings<'tcx>(this: &MoveData<'tcx>,
300301
LpExtend(_, _, LpDeref(mc::Implicit(..))) |
301302
LpExtend(_, _, LpDeref(mc::BorrowedPtr(..))) => {}
302303

303-
// FIXME(pnkfelix): LV[j] should be tracked, at least in the
304+
// FIXME (pnkfelix): LV[j] should be tracked, at least in the
304305
// sense of we will track the remaining drop obligation of the
305306
// rest of the array.
306307
//
307-
// LV[j] is not tracked precisely
308-
LpExtend(_, _, LpInterior(mc::InteriorElement(_))) => {
308+
// Well, either that or LV[j] should be made illegal.
309+
// But even then, we will need to deal with destructuring
310+
// bind.
311+
//
312+
// Anyway, for now: LV[j] is not tracked precisely
313+
LpExtend(_, _, LpInterior(InteriorElement(..))) => {
309314
let mp = this.move_path(tcx, lp.clone());
310315
gathered_fragments.push(AllButOneFrom(mp));
311316
}
312317

313318
// field access LV.x and tuple access LV#k are the cases
314319
// we are interested in
315320
LpExtend(ref loan_parent, mc,
316-
LpInterior(mc::InteriorField(ref field_name))) => {
321+
LpInterior(InteriorField(ref field_name))) => {
317322
let enum_variant_info = match loan_parent.kind {
318323
LpDowncast(ref loan_parent_2, variant_def_id) =>
319324
Some((variant_def_id, loan_parent_2.clone())),
@@ -452,7 +457,7 @@ fn add_fragment_sibling_core<'tcx>(this: &MoveData<'tcx>,
452457
LpVar(..) | LpUpvar(..) | LpExtend(..) => enum_variant_did,
453458
};
454459

455-
let loan_path_elem = LpInterior(mc::InteriorField(new_field_name));
460+
let loan_path_elem = LpInterior(InteriorField(new_field_name));
456461
let new_lp_type = match new_field_name {
457462
mc::NamedField(ast_name) =>
458463
ty::named_element_ty(tcx, parent.to_type(), ast_name, opt_variant_did),

src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use borrowck::gather_loans::move_error::{MoveError, MoveErrorCollector};
1616
use borrowck::move_data::*;
1717
use rustc::middle::expr_use_visitor as euv;
1818
use rustc::middle::mem_categorization as mc;
19+
use rustc::middle::mem_categorization::Typer;
20+
use rustc::middle::mem_categorization::InteriorOffsetKind as Kind;
1921
use rustc::middle::ty;
2022
use rustc::util::ppaux::Repr;
2123
use std::rc::Rc;
@@ -156,6 +158,7 @@ pub fn gather_assignment<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
156158
mode);
157159
}
158160

161+
// (keep in sync with move_error::report_cannot_move_out_of )
159162
fn check_and_get_illegal_move_origin<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
160163
cmt: &mc::cmt<'tcx>)
161164
-> Option<mc::cmt<'tcx>> {
@@ -174,7 +177,8 @@ fn check_and_get_illegal_move_origin<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
174177
}
175178

176179
mc::cat_downcast(ref b, _) |
177-
mc::cat_interior(ref b, _) => {
180+
mc::cat_interior(ref b, mc::InteriorField(_)) |
181+
mc::cat_interior(ref b, mc::InteriorElement(Kind::Pattern, _)) => {
178182
match b.ty.sty {
179183
ty::ty_struct(did, _) | ty::ty_enum(did, _) => {
180184
if ty::has_dtor(bccx.tcx, did) {
@@ -189,6 +193,11 @@ fn check_and_get_illegal_move_origin<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
189193
}
190194
}
191195

196+
mc::cat_interior(_, mc::InteriorElement(Kind::Index, _)) => {
197+
// Forbid move of arr[i] for arr: [T; 3]; see RFC 533.
198+
Some(cmt.clone())
199+
}
200+
192201
mc::cat_deref(ref b, _, mc::Unique) => {
193202
check_and_get_illegal_move_origin(bccx, b)
194203
}

src/librustc_borrowck/borrowck/gather_loans/move_error.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
use borrowck::BorrowckCtxt;
1212
use rustc::middle::mem_categorization as mc;
13+
use rustc::middle::mem_categorization::Typer;
14+
use rustc::middle::mem_categorization::InteriorOffsetKind as Kind;
1315
use rustc::middle::ty;
1416
use rustc::util::ppaux::UserString;
1517
use std::cell::RefCell;
@@ -110,6 +112,7 @@ fn group_errors_with_same_origin<'tcx>(errors: &Vec<MoveError<'tcx>>)
110112
}
111113
}
112114

115+
// (keep in sync with gather_moves::check_and_get_illegal_move_origin )
113116
fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
114117
move_from: mc::cmt<'tcx>) {
115118
match move_from.cat {
@@ -121,8 +124,18 @@ fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
121124
move_from.descriptive_string(bccx.tcx))[]);
122125
}
123126

127+
mc::cat_interior(ref b, mc::InteriorElement(Kind::Index, _)) => {
128+
let expr = bccx.tcx.map.expect_expr(move_from.id);
129+
if let ast::ExprIndex(..) = expr.node {
130+
bccx.span_err(move_from.span,
131+
&format!("cannot move out of type `{}`, \
132+
a non-copy fixed-size array",
133+
b.ty.user_string(bccx.tcx))[]);
134+
}
135+
}
136+
124137
mc::cat_downcast(ref b, _) |
125-
mc::cat_interior(ref b, _) => {
138+
mc::cat_interior(ref b, mc::InteriorField(_)) => {
126139
match b.ty.sty {
127140
ty::ty_struct(did, _) |
128141
ty::ty_enum(did, _) if ty::has_dtor(bccx.tcx, did) => {

src/librustc_borrowck/borrowck/gather_loans/restrictions.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ use rustc::middle::ty;
1919
use rustc::util::ppaux::Repr;
2020
use syntax::codemap::Span;
2121

22+
use borrowck::ToInteriorKind;
23+
2224
use std::rc::Rc;
2325

2426
#[derive(Debug)]
@@ -96,7 +98,7 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
9698
// the memory, so no additional restrictions are
9799
// needed.
98100
let result = self.restrict(cmt_base);
99-
self.extend(result, &cmt, LpInterior(i))
101+
self.extend(result, &cmt, LpInterior(i.cleaned()))
100102
}
101103

102104
mc::cat_static_item(..) => {

0 commit comments

Comments
 (0)