]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir/borrow_check/nll/region_infer/values.rs
New upstream version 1.26.0+dfsg1
[rustc.git] / src / librustc_mir / borrow_check / nll / region_infer / values.rs
CommitLineData
ff7c6d11
XL
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
11use std::rc::Rc;
0531ce1d 12use rustc_data_structures::bitvec::SparseBitMatrix;
ff7c6d11
XL
13use rustc_data_structures::fx::FxHashMap;
14use rustc_data_structures::indexed_vec::Idx;
15use rustc_data_structures::indexed_vec::IndexVec;
16use rustc::mir::{BasicBlock, Location, Mir};
17use rustc::ty::RegionVid;
18use syntax::codemap::Span;
19
20use super::{Cause, CauseExt, TrackCauses};
21
22/// Maps between the various kinds of elements of a region value to
23/// the internal indices that w use.
24pub(super) struct RegionValueElements {
25 /// For each basic block, how many points are contained within?
26 statements_before_block: IndexVec<BasicBlock, usize>,
27 num_points: usize,
28 num_universal_regions: usize,
29}
30
31impl RegionValueElements {
32 pub(super) fn new(mir: &Mir<'_>, num_universal_regions: usize) -> Self {
33 let mut num_points = 0;
34 let statements_before_block = mir.basic_blocks()
35 .iter()
36 .map(|block_data| {
37 let v = num_points;
38 num_points += block_data.statements.len() + 1;
39 v
40 })
41 .collect();
42
43 debug!(
44 "RegionValueElements(num_universal_regions={:?})",
45 num_universal_regions
46 );
47 debug!(
48 "RegionValueElements: statements_before_block={:#?}",
49 statements_before_block
50 );
51 debug!("RegionValueElements: num_points={:#?}", num_points);
52
53 Self {
54 statements_before_block,
55 num_universal_regions,
56 num_points,
57 }
58 }
59
60 /// Total number of element indices that exist.
61 pub(super) fn num_elements(&self) -> usize {
62 self.num_points + self.num_universal_regions
63 }
64
65 /// Converts an element of a region value into a `RegionElementIndex`.
66 pub(super) fn index<T: ToElementIndex>(&self, elem: T) -> RegionElementIndex {
67 elem.to_element_index(self)
68 }
69
70 /// Iterates over the `RegionElementIndex` for all points in the CFG.
71 pub(super) fn all_point_indices<'a>(&'a self) -> impl Iterator<Item = RegionElementIndex> + 'a {
0531ce1d 72 (0..self.num_points).map(move |i| RegionElementIndex::new(i + self.num_universal_regions))
ff7c6d11
XL
73 }
74
75 /// Iterates over the `RegionElementIndex` for all points in the CFG.
76 pub(super) fn all_universal_region_indices(&self) -> impl Iterator<Item = RegionElementIndex> {
77 (0..self.num_universal_regions).map(move |i| RegionElementIndex::new(i))
78 }
79
80 /// Converts a particular `RegionElementIndex` to the `RegionElement` it represents.
81 pub(super) fn to_element(&self, i: RegionElementIndex) -> RegionElement {
82 debug!("to_element(i={:?})", i);
83
84 if let Some(r) = self.to_universal_region(i) {
85 RegionElement::UniversalRegion(r)
86 } else {
87 let point_index = i.index() - self.num_universal_regions;
88
89 // Find the basic block. We have a vector with the
90 // starting index of the statement in each block. Imagine
91 // we have statement #22, and we have a vector like:
92 //
93 // [0, 10, 20]
94 //
95 // In that case, this represents point_index 2 of
96 // basic block BB2. We know this because BB0 accounts for
97 // 0..10, BB1 accounts for 11..20, and BB2 accounts for
98 // 20...
99 //
100 // To compute this, we could do a binary search, but
101 // because I am lazy we instead iterate through to find
102 // the last point where the "first index" (0, 10, or 20)
103 // was less than the statement index (22). In our case, this will
104 // be (BB2, 20).
105 //
106 // Nit: we could do a binary search here but I'm too lazy.
107 let (block, &first_index) = self.statements_before_block
108 .iter_enumerated()
109 .filter(|(_, first_index)| **first_index <= point_index)
110 .last()
111 .unwrap();
112
113 RegionElement::Location(Location {
114 block,
115 statement_index: point_index - first_index,
116 })
117 }
118 }
119
120 /// Converts a particular `RegionElementIndex` to a universal
121 /// region, if that is what it represents. Returns `None`
122 /// otherwise.
123 pub(super) fn to_universal_region(&self, i: RegionElementIndex) -> Option<RegionVid> {
124 if i.index() < self.num_universal_regions {
125 Some(RegionVid::new(i.index()))
126 } else {
127 None
128 }
129 }
130}
131
132/// A newtype for the integers that represent one of the possible
0531ce1d 133/// elements in a region. These are the rows in the `SparseBitMatrix` that
ff7c6d11
XL
134/// is used to store the values of all regions. They have the following
135/// convention:
136///
137/// - The first N indices represent free regions (where N = universal_regions.len()).
138/// - The remainder represent the points in the CFG (see `point_indices` map).
139///
140/// You can convert a `RegionElementIndex` into a `RegionElement`
141/// using the `to_region_elem` method.
142newtype_index!(RegionElementIndex { DEBUG_FORMAT = "RegionElementIndex({})" });
143
144/// An individual element in a region value -- the value of a
145/// particular region variable consists of a set of these elements.
146#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
147pub(super) enum RegionElement {
148 /// A point in the control-flow graph.
149 Location(Location),
150
0531ce1d 151 /// An in-scope, universally quantified region (e.g., a lifetime parameter).
ff7c6d11
XL
152 UniversalRegion(RegionVid),
153}
154
ff7c6d11
XL
155pub(super) trait ToElementIndex {
156 fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex;
157}
158
159impl ToElementIndex for Location {
160 fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex {
161 let Location {
162 block,
163 statement_index,
164 } = self;
165 let start_index = elements.statements_before_block[block];
166 RegionElementIndex::new(elements.num_universal_regions + start_index + statement_index)
167 }
168}
169
170impl ToElementIndex for RegionVid {
171 fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex {
172 assert!(self.index() < elements.num_universal_regions);
173 RegionElementIndex::new(self.index())
174 }
175}
176
177impl ToElementIndex for RegionElementIndex {
178 fn to_element_index(self, _elements: &RegionValueElements) -> RegionElementIndex {
179 self
180 }
181}
182
183/// Stores the values for a set of regions. These are stored in a
0531ce1d 184/// compact `SparseBitMatrix` representation, with one row per region
ff7c6d11
XL
185/// variable. The columns consist of either universal regions or
186/// points in the CFG.
ff7c6d11
XL
187pub(super) struct RegionValues {
188 elements: Rc<RegionValueElements>,
0531ce1d 189 matrix: SparseBitMatrix<RegionVid, RegionElementIndex>,
ff7c6d11
XL
190
191 /// If cause tracking is enabled, maps from a pair (r, e)
192 /// consisting of a region `r` that contains some element `e` to
193 /// the reason that the element is contained. There should be an
0531ce1d 194 /// entry for every bit set to 1 in `SparseBitMatrix`.
ff7c6d11
XL
195 causes: Option<CauseMap>,
196}
197
198type CauseMap = FxHashMap<(RegionVid, RegionElementIndex), Rc<Cause>>;
199
200impl RegionValues {
0531ce1d
XL
201 /// Creates a new set of "region values" that tracks causal information.
202 /// Each of the regions in num_region_variables will be initialized with an
203 /// empty set of points and no causal information.
204 pub(super) fn new(elements: &Rc<RegionValueElements>, num_region_variables: usize) -> Self {
ff7c6d11
XL
205 assert!(
206 elements.num_universal_regions <= num_region_variables,
207 "universal regions are a subset of the region variables"
208 );
209
210 Self {
211 elements: elements.clone(),
0531ce1d
XL
212 matrix: SparseBitMatrix::new(
213 RegionVid::new(num_region_variables),
214 RegionElementIndex::new(elements.num_elements()),
215 ),
216 causes: Some(CauseMap::default()),
217 }
218 }
219
220 /// Duplicates the region values. If track_causes is false, then the
221 /// resulting value will not track causal information (and any existing
222 /// causal information is dropped). Otherwise, the causal information is
223 /// preserved and maintained. Tracking the causal information makes region
224 /// propagation significantly slower, so we prefer not to do it until an
225 /// error is reported.
226 pub(super) fn duplicate(&self, track_causes: TrackCauses) -> Self {
227 Self {
228 elements: self.elements.clone(),
229 matrix: self.matrix.clone(),
ff7c6d11 230 causes: if track_causes.0 {
0531ce1d 231 self.causes.clone()
ff7c6d11
XL
232 } else {
233 None
234 },
235 }
236 }
237
238 /// Adds the given element to the value for the given region. Returns true if
239 /// the element is newly added (i.e., was not already present).
240 pub(super) fn add<E: ToElementIndex>(&mut self, r: RegionVid, elem: E, cause: &Cause) -> bool {
241 let i = self.elements.index(elem);
242 self.add_internal(r, i, |_| cause.clone())
243 }
244
245 /// Internal method to add an element to a region.
246 ///
247 /// Takes a "lazy" cause -- this function will return the cause, but it will only
248 /// be invoked if cause tracking is enabled.
249 fn add_internal<F>(&mut self, r: RegionVid, i: RegionElementIndex, make_cause: F) -> bool
250 where
251 F: FnOnce(&CauseMap) -> Cause,
252 {
0531ce1d 253 if self.matrix.add(r, i) {
ff7c6d11
XL
254 debug!("add(r={:?}, i={:?})", r, self.elements.to_element(i));
255
256 if let Some(causes) = &mut self.causes {
257 let cause = Rc::new(make_cause(causes));
258 causes.insert((r, i), cause);
259 }
260
261 true
262 } else {
263 if let Some(causes) = &mut self.causes {
264 let cause = make_cause(causes);
265 let old_cause = causes.get_mut(&(r, i)).unwrap();
266 if cause < **old_cause {
267 *old_cause = Rc::new(cause);
268 return true;
269 }
270 }
271
272 false
273 }
274 }
275
276 /// Adds `elem` to `to_region` because of a relation:
277 ///
278 /// to_region: from_region @ constraint_location
279 ///
280 /// that was added by the cod at `constraint_span`.
281 pub(super) fn add_due_to_outlives<T: ToElementIndex>(
282 &mut self,
283 from_region: RegionVid,
284 to_region: RegionVid,
285 elem: T,
286 constraint_location: Location,
287 constraint_span: Span,
288 ) -> bool {
289 let elem = self.elements.index(elem);
290 self.add_internal(to_region, elem, |causes| {
291 causes[&(from_region, elem)].outlives(constraint_location, constraint_span)
292 })
293 }
294
295 /// Adds all the universal regions outlived by `from_region` to
296 /// `to_region`.
297 pub(super) fn add_universal_regions_outlived_by(
298 &mut self,
299 from_region: RegionVid,
300 to_region: RegionVid,
301 constraint_location: Location,
302 constraint_span: Span,
303 ) -> bool {
0531ce1d 304 // We could optimize this by improving `SparseBitMatrix::merge` so
ff7c6d11
XL
305 // it does not always merge an entire row. That would
306 // complicate causal tracking though.
307 debug!(
308 "add_universal_regions_outlived_by(from_region={:?}, to_region={:?})",
0531ce1d 309 from_region, to_region
ff7c6d11
XL
310 );
311 let mut changed = false;
312 for elem in self.elements.all_universal_region_indices() {
313 if self.contains(from_region, elem) {
314 changed |= self.add_due_to_outlives(
315 from_region,
316 to_region,
317 elem,
318 constraint_location,
319 constraint_span,
320 );
321 }
322 }
323 changed
324 }
325
326 /// True if the region `r` contains the given element.
327 pub(super) fn contains<E: ToElementIndex>(&self, r: RegionVid, elem: E) -> bool {
328 let i = self.elements.index(elem);
0531ce1d 329 self.matrix.contains(r, i)
ff7c6d11
XL
330 }
331
332 /// Iterate over the value of the region `r`, yielding up element
333 /// indices. You may prefer `universal_regions_outlived_by` or
334 /// `elements_contained_in`.
335 pub(super) fn element_indices_contained_in<'a>(
336 &'a self,
337 r: RegionVid,
338 ) -> impl Iterator<Item = RegionElementIndex> + 'a {
0531ce1d 339 self.matrix.iter(r).map(move |i| i)
ff7c6d11
XL
340 }
341
342 /// Returns just the universal regions that are contained in a given region's value.
343 pub(super) fn universal_regions_outlived_by<'a>(
344 &'a self,
345 r: RegionVid,
346 ) -> impl Iterator<Item = RegionVid> + 'a {
347 self.element_indices_contained_in(r)
348 .map(move |i| self.elements.to_universal_region(i))
349 .take_while(move |v| v.is_some()) // universal regions are a prefix
350 .map(move |v| v.unwrap())
351 }
352
353 /// Returns all the elements contained in a given region's value.
354 pub(super) fn elements_contained_in<'a>(
355 &'a self,
356 r: RegionVid,
357 ) -> impl Iterator<Item = RegionElement> + 'a {
358 self.element_indices_contained_in(r)
359 .map(move |r| self.elements.to_element(r))
360 }
361
362 /// Returns a "pretty" string value of the region. Meant for debugging.
363 pub(super) fn region_value_str(&self, r: RegionVid) -> String {
364 let mut result = String::new();
365 result.push_str("{");
366
367 // Set to Some(l1, l2) when we have observed all the locations
368 // from l1..=l2 (inclusive) but not yet printed them. This
369 // gets extended if we then see l3 where l3 is the successor
370 // to l2.
371 let mut open_location: Option<(Location, Location)> = None;
372
373 let mut sep = "";
374 let mut push_sep = |s: &mut String| {
375 s.push_str(sep);
376 sep = ", ";
377 };
378
379 for element in self.elements_contained_in(r) {
380 match element {
381 RegionElement::Location(l) => {
382 if let Some((location1, location2)) = open_location {
383 if location2.block == l.block
384 && location2.statement_index == l.statement_index - 1
385 {
386 open_location = Some((location1, l));
387 continue;
388 }
389
390 push_sep(&mut result);
391 Self::push_location_range(&mut result, location1, location2);
392 }
393
394 open_location = Some((l, l));
395 }
396
397 RegionElement::UniversalRegion(fr) => {
398 if let Some((location1, location2)) = open_location {
399 push_sep(&mut result);
400 Self::push_location_range(&mut result, location1, location2);
401 open_location = None;
402 }
403
404 push_sep(&mut result);
405 result.push_str(&format!("{:?}", fr));
406 }
407 }
408 }
409
410 if let Some((location1, location2)) = open_location {
411 push_sep(&mut result);
412 Self::push_location_range(&mut result, location1, location2);
413 }
414
415 result.push_str("}");
416
417 result
418 }
419
420 fn push_location_range(str: &mut String, location1: Location, location2: Location) {
421 if location1 == location2 {
422 str.push_str(&format!("{:?}", location1));
423 } else {
424 assert_eq!(location1.block, location2.block);
425 str.push_str(&format!(
426 "{:?}[{}..={}]",
0531ce1d 427 location1.block, location1.statement_index, location2.statement_index
ff7c6d11
XL
428 ));
429 }
430 }
431
432 /// Given a region `r` that contains the element `elem`, returns the `Cause`
433 /// that tells us *why* `elem` is found in that region.
434 ///
435 /// Returns None if cause tracking is disabled or `elem` is not
436 /// actually found in `r`.
437 pub(super) fn cause<T: ToElementIndex>(&self, r: RegionVid, elem: T) -> Option<Rc<Cause>> {
438 let index = self.elements.index(elem);
439 if let Some(causes) = &self.causes {
440 causes.get(&(r, index)).cloned()
441 } else {
442 None
443 }
444 }
445}