]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2012-2014 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 | //! Helper routines used for fragmenting structural paths due to moves for | |
12 | //! tracking drop obligations. Please see the extensive comments in the | |
c34b1796 | 13 | //! section "Structural fragments" in `README.md`. |
1a4d82fc JJ |
14 | |
15 | use self::Fragment::*; | |
16 | ||
85aaf69f | 17 | use borrowck::InteriorKind::{InteriorField, InteriorElement}; |
c1a9b12d | 18 | use borrowck::{self, LoanPath}; |
1a4d82fc JJ |
19 | use borrowck::LoanPathKind::{LpVar, LpUpvar, LpDowncast, LpExtend}; |
20 | use borrowck::LoanPathElem::{LpDeref, LpInterior}; | |
c34b1796 | 21 | use borrowck::move_data::InvalidMovePathIndex; |
1a4d82fc | 22 | use borrowck::move_data::{MoveData, MovePathIndex}; |
54a0048b SL |
23 | use rustc::hir::def_id::{DefId}; |
24 | use rustc::ty::{self, TyCtxt}; | |
1a4d82fc | 25 | use rustc::middle::mem_categorization as mc; |
62682a34 | 26 | |
1a4d82fc JJ |
27 | use std::mem; |
28 | use std::rc::Rc; | |
29 | use syntax::ast; | |
54a0048b | 30 | use syntax::codemap::{Span, DUMMY_SP}; |
b039eaaf | 31 | use syntax::attr::AttrMetaMethods; |
1a4d82fc JJ |
32 | |
33 | #[derive(PartialEq, Eq, PartialOrd, Ord)] | |
34 | enum Fragment { | |
35 | // This represents the path described by the move path index | |
36 | Just(MovePathIndex), | |
37 | ||
38 | // This represents the collection of all but one of the elements | |
39 | // from an array at the path described by the move path index. | |
40 | // Note that attached MovePathIndex should have mem_categorization | |
85aaf69f | 41 | // of InteriorElement (i.e. array dereference `&foo[..]`). |
1a4d82fc JJ |
42 | AllButOneFrom(MovePathIndex), |
43 | } | |
44 | ||
45 | impl Fragment { | |
62682a34 SL |
46 | fn loan_path_repr(&self, move_data: &MoveData) -> String { |
47 | let lp = |mpi| move_data.path_loan_path(mpi); | |
1a4d82fc | 48 | match *self { |
62682a34 SL |
49 | Just(mpi) => format!("{:?}", lp(mpi)), |
50 | AllButOneFrom(mpi) => format!("$(allbutone {:?})", lp(mpi)), | |
1a4d82fc JJ |
51 | } |
52 | } | |
53 | ||
62682a34 SL |
54 | fn loan_path_user_string(&self, move_data: &MoveData) -> String { |
55 | let lp = |mpi| move_data.path_loan_path(mpi); | |
1a4d82fc | 56 | match *self { |
62682a34 SL |
57 | Just(mpi) => lp(mpi).to_string(), |
58 | AllButOneFrom(mpi) => format!("$(allbutone {})", lp(mpi)), | |
1a4d82fc JJ |
59 | } |
60 | } | |
61 | } | |
62 | ||
c1a9b12d SL |
63 | pub fn build_unfragmented_map(this: &mut borrowck::BorrowckCtxt, |
64 | move_data: &MoveData, | |
65 | id: ast::NodeId) { | |
66 | let fr = &move_data.fragments.borrow(); | |
67 | ||
68 | // For now, don't care about other kinds of fragments; the precise | |
69 | // classfication of all paths for non-zeroing *drop* needs them, | |
70 | // but the loose approximation used by non-zeroing moves does not. | |
71 | let moved_leaf_paths = fr.moved_leaf_paths(); | |
72 | let assigned_leaf_paths = fr.assigned_leaf_paths(); | |
73 | ||
74 | let mut fragment_infos = Vec::with_capacity(moved_leaf_paths.len()); | |
75 | ||
76 | let find_var_id = |move_path_index: MovePathIndex| -> Option<ast::NodeId> { | |
77 | let lp = move_data.path_loan_path(move_path_index); | |
78 | match lp.kind { | |
79 | LpVar(var_id) => Some(var_id), | |
80 | LpUpvar(ty::UpvarId { var_id, closure_expr_id }) => { | |
81 | // The `var_id` is unique *relative to* the current function. | |
82 | // (Check that we are indeed talking about the same function.) | |
83 | assert_eq!(id, closure_expr_id); | |
84 | Some(var_id) | |
85 | } | |
86 | LpDowncast(..) | LpExtend(..) => { | |
87 | // This simple implementation of non-zeroing move does | |
88 | // not attempt to deal with tracking substructure | |
89 | // accurately in the general case. | |
90 | None | |
91 | } | |
92 | } | |
93 | }; | |
94 | ||
95 | let moves = move_data.moves.borrow(); | |
96 | for &move_path_index in moved_leaf_paths { | |
97 | let var_id = match find_var_id(move_path_index) { | |
98 | None => continue, | |
99 | Some(var_id) => var_id, | |
100 | }; | |
101 | ||
102 | move_data.each_applicable_move(move_path_index, |move_index| { | |
103 | let info = ty::FragmentInfo::Moved { | |
104 | var: var_id, | |
105 | move_expr: moves[move_index.get()].id, | |
106 | }; | |
107 | debug!("fragment_infos push({:?} \ | |
108 | due to move_path_index: {} move_index: {}", | |
109 | info, move_path_index.get(), move_index.get()); | |
110 | fragment_infos.push(info); | |
111 | true | |
112 | }); | |
113 | } | |
114 | ||
115 | for &move_path_index in assigned_leaf_paths { | |
116 | let var_id = match find_var_id(move_path_index) { | |
117 | None => continue, | |
118 | Some(var_id) => var_id, | |
119 | }; | |
120 | ||
121 | let var_assigns = move_data.var_assignments.borrow(); | |
122 | for var_assign in var_assigns.iter() | |
123 | .filter(|&assign| assign.path == move_path_index) | |
124 | { | |
125 | let info = ty::FragmentInfo::Assigned { | |
126 | var: var_id, | |
127 | assign_expr: var_assign.id, | |
128 | assignee_id: var_assign.assignee_id, | |
129 | }; | |
130 | debug!("fragment_infos push({:?} due to var_assignment", info); | |
131 | fragment_infos.push(info); | |
132 | } | |
133 | } | |
134 | ||
135 | let mut fraginfo_map = this.tcx.fragment_infos.borrow_mut(); | |
b039eaaf | 136 | let fn_did = this.tcx.map.local_def_id(id); |
c1a9b12d SL |
137 | let prev = fraginfo_map.insert(fn_did, fragment_infos); |
138 | assert!(prev.is_none()); | |
139 | } | |
140 | ||
1a4d82fc JJ |
141 | pub struct FragmentSets { |
142 | /// During move_data construction, `moved_leaf_paths` tracks paths | |
143 | /// that have been used directly by being moved out of. When | |
144 | /// move_data construction has been completed, `moved_leaf_paths` | |
145 | /// tracks such paths that are *leaf fragments* (e.g. `a.j` if we | |
146 | /// never move out any child like `a.j.x`); any parent paths | |
147 | /// (e.g. `a` for the `a.j` example) are moved over to | |
148 | /// `parents_of_fragments`. | |
149 | moved_leaf_paths: Vec<MovePathIndex>, | |
150 | ||
151 | /// `assigned_leaf_paths` tracks paths that have been used | |
152 | /// directly by being overwritten, but is otherwise much like | |
153 | /// `moved_leaf_paths`. | |
154 | assigned_leaf_paths: Vec<MovePathIndex>, | |
155 | ||
156 | /// `parents_of_fragments` tracks paths that are definitely | |
157 | /// parents of paths that have been moved. | |
158 | /// | |
159 | /// FIXME(pnkfelix) probably do not want/need | |
160 | /// `parents_of_fragments` at all, if we can avoid it. | |
161 | /// | |
b039eaaf | 162 | /// Update: I do not see a way to avoid it. Maybe just remove |
1a4d82fc JJ |
163 | /// above fixme, or at least document why doing this may be hard. |
164 | parents_of_fragments: Vec<MovePathIndex>, | |
165 | ||
166 | /// During move_data construction (specifically the | |
167 | /// fixup_fragment_sets call), `unmoved_fragments` tracks paths | |
168 | /// that have been "left behind" after a sibling has been moved or | |
169 | /// assigned. When move_data construction has been completed, | |
170 | /// `unmoved_fragments` tracks paths that were *only* results of | |
171 | /// being left-behind, and never directly moved themselves. | |
172 | unmoved_fragments: Vec<Fragment>, | |
173 | } | |
174 | ||
175 | impl FragmentSets { | |
176 | pub fn new() -> FragmentSets { | |
177 | FragmentSets { | |
178 | unmoved_fragments: Vec::new(), | |
179 | moved_leaf_paths: Vec::new(), | |
180 | assigned_leaf_paths: Vec::new(), | |
181 | parents_of_fragments: Vec::new(), | |
182 | } | |
183 | } | |
184 | ||
c1a9b12d SL |
185 | pub fn moved_leaf_paths(&self) -> &[MovePathIndex] { |
186 | &self.moved_leaf_paths | |
187 | } | |
188 | ||
189 | pub fn assigned_leaf_paths(&self) -> &[MovePathIndex] { | |
190 | &self.assigned_leaf_paths | |
191 | } | |
192 | ||
1a4d82fc JJ |
193 | pub fn add_move(&mut self, path_index: MovePathIndex) { |
194 | self.moved_leaf_paths.push(path_index); | |
195 | } | |
196 | ||
197 | pub fn add_assignment(&mut self, path_index: MovePathIndex) { | |
198 | self.assigned_leaf_paths.push(path_index); | |
199 | } | |
200 | } | |
201 | ||
202 | pub fn instrument_move_fragments<'tcx>(this: &MoveData<'tcx>, | |
54a0048b | 203 | tcx: &TyCtxt<'tcx>, |
1a4d82fc JJ |
204 | sp: Span, |
205 | id: ast::NodeId) { | |
c34b1796 AL |
206 | let span_err = tcx.map.attrs(id).iter() |
207 | .any(|a| a.check_name("rustc_move_fragments")); | |
208 | let print = tcx.sess.opts.debugging_opts.print_move_fragments; | |
1a4d82fc JJ |
209 | |
210 | if !span_err && !print { return; } | |
211 | ||
85aaf69f | 212 | let instrument_all_paths = |kind, vec_rc: &Vec<MovePathIndex>| { |
1a4d82fc | 213 | for (i, mpi) in vec_rc.iter().enumerate() { |
62682a34 | 214 | let lp = || this.path_loan_path(*mpi); |
1a4d82fc | 215 | if span_err { |
62682a34 | 216 | tcx.sess.span_err(sp, &format!("{}: `{}`", kind, lp())); |
1a4d82fc JJ |
217 | } |
218 | if print { | |
62682a34 | 219 | println!("id:{} {}[{}] `{}`", id, kind, i, lp()); |
1a4d82fc JJ |
220 | } |
221 | } | |
222 | }; | |
223 | ||
85aaf69f | 224 | let instrument_all_fragments = |kind, vec_rc: &Vec<Fragment>| { |
1a4d82fc | 225 | for (i, f) in vec_rc.iter().enumerate() { |
62682a34 | 226 | let render = || f.loan_path_user_string(this); |
1a4d82fc | 227 | if span_err { |
c34b1796 | 228 | tcx.sess.span_err(sp, &format!("{}: `{}`", kind, render())); |
1a4d82fc JJ |
229 | } |
230 | if print { | |
231 | println!("id:{} {}[{}] `{}`", id, kind, i, render()); | |
232 | } | |
233 | } | |
234 | }; | |
235 | ||
236 | let fragments = this.fragments.borrow(); | |
237 | instrument_all_paths("moved_leaf_path", &fragments.moved_leaf_paths); | |
238 | instrument_all_fragments("unmoved_fragment", &fragments.unmoved_fragments); | |
239 | instrument_all_paths("parent_of_fragments", &fragments.parents_of_fragments); | |
240 | instrument_all_paths("assigned_leaf_path", &fragments.assigned_leaf_paths); | |
241 | } | |
242 | ||
243 | /// Normalizes the fragment sets in `this`; i.e., removes duplicate entries, constructs the set of | |
244 | /// parents, and constructs the left-over fragments. | |
245 | /// | |
246 | /// Note: "left-over fragments" means paths that were not directly referenced in moves nor | |
247 | /// assignments, but must nonetheless be tracked as potential drop obligations. | |
54a0048b | 248 | pub fn fixup_fragment_sets<'tcx>(this: &MoveData<'tcx>, tcx: &TyCtxt<'tcx>) { |
1a4d82fc JJ |
249 | |
250 | let mut fragments = this.fragments.borrow_mut(); | |
251 | ||
252 | // Swap out contents of fragments so that we can modify the fields | |
253 | // without borrowing the common fragments. | |
254 | let mut unmoved = mem::replace(&mut fragments.unmoved_fragments, vec![]); | |
255 | let mut parents = mem::replace(&mut fragments.parents_of_fragments, vec![]); | |
256 | let mut moved = mem::replace(&mut fragments.moved_leaf_paths, vec![]); | |
257 | let mut assigned = mem::replace(&mut fragments.assigned_leaf_paths, vec![]); | |
258 | ||
85aaf69f | 259 | let path_lps = |mpis: &[MovePathIndex]| -> Vec<String> { |
62682a34 | 260 | mpis.iter().map(|mpi| format!("{:?}", this.path_loan_path(*mpi))).collect() |
1a4d82fc JJ |
261 | }; |
262 | ||
85aaf69f | 263 | let frag_lps = |fs: &[Fragment]| -> Vec<String> { |
62682a34 | 264 | fs.iter().map(|f| f.loan_path_repr(this)).collect() |
1a4d82fc JJ |
265 | }; |
266 | ||
267 | // First, filter out duplicates | |
268 | moved.sort(); | |
269 | moved.dedup(); | |
85aaf69f | 270 | debug!("fragments 1 moved: {:?}", path_lps(&moved[..])); |
1a4d82fc JJ |
271 | |
272 | assigned.sort(); | |
273 | assigned.dedup(); | |
85aaf69f | 274 | debug!("fragments 1 assigned: {:?}", path_lps(&assigned[..])); |
1a4d82fc JJ |
275 | |
276 | // Second, build parents from the moved and assigned. | |
85aaf69f | 277 | for m in &moved { |
1a4d82fc JJ |
278 | let mut p = this.path_parent(*m); |
279 | while p != InvalidMovePathIndex { | |
280 | parents.push(p); | |
281 | p = this.path_parent(p); | |
282 | } | |
283 | } | |
85aaf69f | 284 | for a in &assigned { |
1a4d82fc JJ |
285 | let mut p = this.path_parent(*a); |
286 | while p != InvalidMovePathIndex { | |
287 | parents.push(p); | |
288 | p = this.path_parent(p); | |
289 | } | |
290 | } | |
291 | ||
292 | parents.sort(); | |
293 | parents.dedup(); | |
85aaf69f | 294 | debug!("fragments 2 parents: {:?}", path_lps(&parents[..])); |
1a4d82fc JJ |
295 | |
296 | // Third, filter the moved and assigned fragments down to just the non-parents | |
85aaf69f SL |
297 | moved.retain(|f| non_member(*f, &parents[..])); |
298 | debug!("fragments 3 moved: {:?}", path_lps(&moved[..])); | |
1a4d82fc | 299 | |
85aaf69f SL |
300 | assigned.retain(|f| non_member(*f, &parents[..])); |
301 | debug!("fragments 3 assigned: {:?}", path_lps(&assigned[..])); | |
1a4d82fc JJ |
302 | |
303 | // Fourth, build the leftover from the moved, assigned, and parents. | |
85aaf69f | 304 | for m in &moved { |
1a4d82fc JJ |
305 | let lp = this.path_loan_path(*m); |
306 | add_fragment_siblings(this, tcx, &mut unmoved, lp, None); | |
307 | } | |
85aaf69f | 308 | for a in &assigned { |
1a4d82fc JJ |
309 | let lp = this.path_loan_path(*a); |
310 | add_fragment_siblings(this, tcx, &mut unmoved, lp, None); | |
311 | } | |
85aaf69f | 312 | for p in &parents { |
1a4d82fc JJ |
313 | let lp = this.path_loan_path(*p); |
314 | add_fragment_siblings(this, tcx, &mut unmoved, lp, None); | |
315 | } | |
316 | ||
317 | unmoved.sort(); | |
318 | unmoved.dedup(); | |
85aaf69f | 319 | debug!("fragments 4 unmoved: {:?}", frag_lps(&unmoved[..])); |
1a4d82fc JJ |
320 | |
321 | // Fifth, filter the leftover fragments down to its core. | |
322 | unmoved.retain(|f| match *f { | |
323 | AllButOneFrom(_) => true, | |
85aaf69f SL |
324 | Just(mpi) => non_member(mpi, &parents[..]) && |
325 | non_member(mpi, &moved[..]) && | |
326 | non_member(mpi, &assigned[..]) | |
1a4d82fc | 327 | }); |
85aaf69f | 328 | debug!("fragments 5 unmoved: {:?}", frag_lps(&unmoved[..])); |
1a4d82fc JJ |
329 | |
330 | // Swap contents back in. | |
331 | fragments.unmoved_fragments = unmoved; | |
332 | fragments.parents_of_fragments = parents; | |
333 | fragments.moved_leaf_paths = moved; | |
334 | fragments.assigned_leaf_paths = assigned; | |
335 | ||
336 | return; | |
337 | ||
338 | fn non_member(elem: MovePathIndex, set: &[MovePathIndex]) -> bool { | |
339 | match set.binary_search(&elem) { | |
340 | Ok(_) => false, | |
341 | Err(_) => true, | |
342 | } | |
343 | } | |
344 | } | |
345 | ||
346 | /// Adds all of the precisely-tracked siblings of `lp` as potential move paths of interest. For | |
347 | /// example, if `lp` represents `s.x.j`, then adds moves paths for `s.x.i` and `s.x.k`, the | |
348 | /// siblings of `s.x.j`. | |
349 | fn add_fragment_siblings<'tcx>(this: &MoveData<'tcx>, | |
54a0048b | 350 | tcx: &TyCtxt<'tcx>, |
1a4d82fc JJ |
351 | gathered_fragments: &mut Vec<Fragment>, |
352 | lp: Rc<LoanPath<'tcx>>, | |
353 | origin_id: Option<ast::NodeId>) { | |
354 | match lp.kind { | |
355 | LpVar(_) | LpUpvar(..) => {} // Local variables have no siblings. | |
356 | ||
357 | // Consuming a downcast is like consuming the original value, so propage inward. | |
358 | LpDowncast(ref loan_parent, _) => { | |
359 | add_fragment_siblings(this, tcx, gathered_fragments, loan_parent.clone(), origin_id); | |
360 | } | |
361 | ||
362 | // *LV for Unique consumes the contents of the box (at | |
363 | // least when it is non-copy...), so propagate inward. | |
364 | LpExtend(ref loan_parent, _, LpDeref(mc::Unique)) => { | |
365 | add_fragment_siblings(this, tcx, gathered_fragments, loan_parent.clone(), origin_id); | |
366 | } | |
367 | ||
368 | // *LV for unsafe and borrowed pointers do not consume their loan path, so stop here. | |
369 | LpExtend(_, _, LpDeref(mc::UnsafePtr(..))) | | |
370 | LpExtend(_, _, LpDeref(mc::Implicit(..))) | | |
371 | LpExtend(_, _, LpDeref(mc::BorrowedPtr(..))) => {} | |
372 | ||
85aaf69f | 373 | // FIXME (pnkfelix): LV[j] should be tracked, at least in the |
1a4d82fc JJ |
374 | // sense of we will track the remaining drop obligation of the |
375 | // rest of the array. | |
376 | // | |
85aaf69f SL |
377 | // Well, either that or LV[j] should be made illegal. |
378 | // But even then, we will need to deal with destructuring | |
379 | // bind. | |
380 | // | |
381 | // Anyway, for now: LV[j] is not tracked precisely | |
9cc50fc6 | 382 | LpExtend(_, _, LpInterior(_, InteriorElement(..))) => { |
1a4d82fc JJ |
383 | let mp = this.move_path(tcx, lp.clone()); |
384 | gathered_fragments.push(AllButOneFrom(mp)); | |
385 | } | |
386 | ||
387 | // field access LV.x and tuple access LV#k are the cases | |
388 | // we are interested in | |
389 | LpExtend(ref loan_parent, mc, | |
9cc50fc6 | 390 | LpInterior(_, InteriorField(ref field_name))) => { |
1a4d82fc JJ |
391 | let enum_variant_info = match loan_parent.kind { |
392 | LpDowncast(ref loan_parent_2, variant_def_id) => | |
393 | Some((variant_def_id, loan_parent_2.clone())), | |
394 | LpExtend(..) | LpVar(..) | LpUpvar(..) => | |
395 | None, | |
396 | }; | |
397 | add_fragment_siblings_for_extension( | |
398 | this, | |
399 | tcx, | |
400 | gathered_fragments, | |
401 | loan_parent, mc, field_name, &lp, origin_id, enum_variant_info); | |
402 | } | |
403 | } | |
404 | } | |
405 | ||
406 | /// We have determined that `origin_lp` destructures to LpExtend(parent, original_field_name). | |
407 | /// Based on this, add move paths for all of the siblings of `origin_lp`. | |
408 | fn add_fragment_siblings_for_extension<'tcx>(this: &MoveData<'tcx>, | |
54a0048b | 409 | tcx: &TyCtxt<'tcx>, |
1a4d82fc JJ |
410 | gathered_fragments: &mut Vec<Fragment>, |
411 | parent_lp: &Rc<LoanPath<'tcx>>, | |
412 | mc: mc::MutabilityCategory, | |
413 | origin_field_name: &mc::FieldName, | |
414 | origin_lp: &Rc<LoanPath<'tcx>>, | |
415 | origin_id: Option<ast::NodeId>, | |
e9174d1e | 416 | enum_variant_info: Option<(DefId, |
1a4d82fc JJ |
417 | Rc<LoanPath<'tcx>>)>) { |
418 | let parent_ty = parent_lp.to_type(); | |
419 | ||
85aaf69f | 420 | let mut add_fragment_sibling_local = |field_name, variant_did| { |
1a4d82fc JJ |
421 | add_fragment_sibling_core( |
422 | this, tcx, gathered_fragments, parent_lp.clone(), mc, field_name, origin_lp, | |
423 | variant_did); | |
424 | }; | |
425 | ||
426 | match (&parent_ty.sty, enum_variant_info) { | |
62682a34 | 427 | (&ty::TyTuple(ref v), None) => { |
1a4d82fc JJ |
428 | let tuple_idx = match *origin_field_name { |
429 | mc::PositionalField(tuple_idx) => tuple_idx, | |
430 | mc::NamedField(_) => | |
54a0048b SL |
431 | bug!("tuple type {:?} should not have named fields.", |
432 | parent_ty), | |
1a4d82fc JJ |
433 | }; |
434 | let tuple_len = v.len(); | |
85aaf69f | 435 | for i in 0..tuple_len { |
1a4d82fc JJ |
436 | if i == tuple_idx { continue } |
437 | let field_name = mc::PositionalField(i); | |
438 | add_fragment_sibling_local(field_name, None); | |
439 | } | |
440 | } | |
441 | ||
e9174d1e | 442 | (&ty::TyStruct(def, _), None) => { |
1a4d82fc JJ |
443 | match *origin_field_name { |
444 | mc::NamedField(ast_name) => { | |
e9174d1e | 445 | for f in &def.struct_variant().fields { |
1a4d82fc JJ |
446 | if f.name == ast_name { |
447 | continue; | |
448 | } | |
449 | let field_name = mc::NamedField(f.name); | |
450 | add_fragment_sibling_local(field_name, None); | |
451 | } | |
452 | } | |
453 | mc::PositionalField(tuple_idx) => { | |
e9174d1e | 454 | for (i, _f) in def.struct_variant().fields.iter().enumerate() { |
1a4d82fc JJ |
455 | if i == tuple_idx { |
456 | continue | |
457 | } | |
458 | let field_name = mc::PositionalField(i); | |
459 | add_fragment_sibling_local(field_name, None); | |
460 | } | |
461 | } | |
462 | } | |
463 | } | |
464 | ||
e9174d1e SL |
465 | (&ty::TyEnum(def, _), ref enum_variant_info) => { |
466 | let variant = match *enum_variant_info { | |
467 | Some((vid, ref _lp2)) => def.variant_with_id(vid), | |
468 | None => { | |
469 | assert!(def.is_univariant()); | |
470 | &def.variants[0] | |
1a4d82fc JJ |
471 | } |
472 | }; | |
473 | match *origin_field_name { | |
474 | mc::NamedField(ast_name) => { | |
e9174d1e SL |
475 | for field in &variant.fields { |
476 | if field.name == ast_name { | |
1a4d82fc JJ |
477 | continue; |
478 | } | |
e9174d1e SL |
479 | let field_name = mc::NamedField(field.name); |
480 | add_fragment_sibling_local(field_name, Some(variant.did)); | |
1a4d82fc JJ |
481 | } |
482 | } | |
483 | mc::PositionalField(tuple_idx) => { | |
e9174d1e | 484 | for (i, _f) in variant.fields.iter().enumerate() { |
1a4d82fc JJ |
485 | if tuple_idx == i { |
486 | continue; | |
487 | } | |
488 | let field_name = mc::PositionalField(i); | |
489 | add_fragment_sibling_local(field_name, None); | |
490 | } | |
491 | } | |
492 | } | |
493 | } | |
494 | ||
495 | ref sty_and_variant_info => { | |
1a4d82fc | 496 | let opt_span = origin_id.and_then(|id|tcx.map.opt_span(id)); |
54a0048b SL |
497 | span_bug!(opt_span.unwrap_or(DUMMY_SP), |
498 | "type {:?} ({:?}) is not fragmentable", | |
499 | parent_ty, | |
500 | sty_and_variant_info); | |
1a4d82fc JJ |
501 | } |
502 | } | |
503 | } | |
504 | ||
505 | /// Adds the single sibling `LpExtend(parent, new_field_name)` of `origin_lp` (the original | |
506 | /// loan-path). | |
507 | fn add_fragment_sibling_core<'tcx>(this: &MoveData<'tcx>, | |
54a0048b | 508 | tcx: &TyCtxt<'tcx>, |
1a4d82fc JJ |
509 | gathered_fragments: &mut Vec<Fragment>, |
510 | parent: Rc<LoanPath<'tcx>>, | |
511 | mc: mc::MutabilityCategory, | |
512 | new_field_name: mc::FieldName, | |
513 | origin_lp: &Rc<LoanPath<'tcx>>, | |
e9174d1e | 514 | enum_variant_did: Option<DefId>) -> MovePathIndex { |
1a4d82fc JJ |
515 | let opt_variant_did = match parent.kind { |
516 | LpDowncast(_, variant_did) => Some(variant_did), | |
517 | LpVar(..) | LpUpvar(..) | LpExtend(..) => enum_variant_did, | |
518 | }; | |
519 | ||
9cc50fc6 | 520 | let loan_path_elem = LpInterior(opt_variant_did, InteriorField(new_field_name)); |
1a4d82fc JJ |
521 | let new_lp_type = match new_field_name { |
522 | mc::NamedField(ast_name) => | |
c1a9b12d | 523 | tcx.named_element_ty(parent.to_type(), ast_name, opt_variant_did), |
1a4d82fc | 524 | mc::PositionalField(idx) => |
c1a9b12d | 525 | tcx.positional_element_ty(parent.to_type(), idx, opt_variant_did), |
1a4d82fc JJ |
526 | }; |
527 | let new_lp_variant = LpExtend(parent, mc, loan_path_elem); | |
528 | let new_lp = LoanPath::new(new_lp_variant, new_lp_type.unwrap()); | |
62682a34 SL |
529 | debug!("add_fragment_sibling_core(new_lp={:?}, origin_lp={:?})", |
530 | new_lp, origin_lp); | |
1a4d82fc JJ |
531 | let mp = this.move_path(tcx, Rc::new(new_lp)); |
532 | ||
533 | // Do not worry about checking for duplicates here; we will sort | |
534 | // and dedup after all are added. | |
535 | gathered_fragments.push(Just(mp)); | |
536 | ||
537 | mp | |
538 | } |