]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir/borrow_check/nll/region_infer/values.rs
New upstream version 1.27.1+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};
83c7162d 17use rustc::ty::{self, RegionVid};
ff7c6d11
XL
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();
83c7162d
XL
266 // #49998: compare using root cause alone to avoid
267 // useless traffic from similar outlives chains.
268
269 let overwrite = if ty::tls::with(|tcx| {
270 tcx.sess.opts.debugging_opts.nll_subminimal_causes
271 }) {
272 cause.root_cause() < old_cause.root_cause()
273 } else {
274 cause < **old_cause
275 };
276 if overwrite {
ff7c6d11
XL
277 *old_cause = Rc::new(cause);
278 return true;
279 }
280 }
281
282 false
283 }
284 }
285
286 /// Adds `elem` to `to_region` because of a relation:
287 ///
288 /// to_region: from_region @ constraint_location
289 ///
290 /// that was added by the cod at `constraint_span`.
291 pub(super) fn add_due_to_outlives<T: ToElementIndex>(
292 &mut self,
293 from_region: RegionVid,
294 to_region: RegionVid,
295 elem: T,
296 constraint_location: Location,
297 constraint_span: Span,
298 ) -> bool {
299 let elem = self.elements.index(elem);
300 self.add_internal(to_region, elem, |causes| {
301 causes[&(from_region, elem)].outlives(constraint_location, constraint_span)
302 })
303 }
304
305 /// Adds all the universal regions outlived by `from_region` to
306 /// `to_region`.
307 pub(super) fn add_universal_regions_outlived_by(
308 &mut self,
309 from_region: RegionVid,
310 to_region: RegionVid,
311 constraint_location: Location,
312 constraint_span: Span,
313 ) -> bool {
0531ce1d 314 // We could optimize this by improving `SparseBitMatrix::merge` so
ff7c6d11
XL
315 // it does not always merge an entire row. That would
316 // complicate causal tracking though.
317 debug!(
318 "add_universal_regions_outlived_by(from_region={:?}, to_region={:?})",
0531ce1d 319 from_region, to_region
ff7c6d11
XL
320 );
321 let mut changed = false;
322 for elem in self.elements.all_universal_region_indices() {
323 if self.contains(from_region, elem) {
324 changed |= self.add_due_to_outlives(
325 from_region,
326 to_region,
327 elem,
328 constraint_location,
329 constraint_span,
330 );
331 }
332 }
333 changed
334 }
335
336 /// True if the region `r` contains the given element.
337 pub(super) fn contains<E: ToElementIndex>(&self, r: RegionVid, elem: E) -> bool {
338 let i = self.elements.index(elem);
0531ce1d 339 self.matrix.contains(r, i)
ff7c6d11
XL
340 }
341
342 /// Iterate over the value of the region `r`, yielding up element
343 /// indices. You may prefer `universal_regions_outlived_by` or
344 /// `elements_contained_in`.
345 pub(super) fn element_indices_contained_in<'a>(
346 &'a self,
347 r: RegionVid,
348 ) -> impl Iterator<Item = RegionElementIndex> + 'a {
0531ce1d 349 self.matrix.iter(r).map(move |i| i)
ff7c6d11
XL
350 }
351
352 /// Returns just the universal regions that are contained in a given region's value.
353 pub(super) fn universal_regions_outlived_by<'a>(
354 &'a self,
355 r: RegionVid,
356 ) -> impl Iterator<Item = RegionVid> + 'a {
357 self.element_indices_contained_in(r)
358 .map(move |i| self.elements.to_universal_region(i))
359 .take_while(move |v| v.is_some()) // universal regions are a prefix
360 .map(move |v| v.unwrap())
361 }
362
363 /// Returns all the elements contained in a given region's value.
364 pub(super) fn elements_contained_in<'a>(
365 &'a self,
366 r: RegionVid,
367 ) -> impl Iterator<Item = RegionElement> + 'a {
368 self.element_indices_contained_in(r)
369 .map(move |r| self.elements.to_element(r))
370 }
371
372 /// Returns a "pretty" string value of the region. Meant for debugging.
373 pub(super) fn region_value_str(&self, r: RegionVid) -> String {
374 let mut result = String::new();
375 result.push_str("{");
376
377 // Set to Some(l1, l2) when we have observed all the locations
378 // from l1..=l2 (inclusive) but not yet printed them. This
379 // gets extended if we then see l3 where l3 is the successor
380 // to l2.
381 let mut open_location: Option<(Location, Location)> = None;
382
383 let mut sep = "";
384 let mut push_sep = |s: &mut String| {
385 s.push_str(sep);
386 sep = ", ";
387 };
388
389 for element in self.elements_contained_in(r) {
390 match element {
391 RegionElement::Location(l) => {
392 if let Some((location1, location2)) = open_location {
393 if location2.block == l.block
394 && location2.statement_index == l.statement_index - 1
395 {
396 open_location = Some((location1, l));
397 continue;
398 }
399
400 push_sep(&mut result);
401 Self::push_location_range(&mut result, location1, location2);
402 }
403
404 open_location = Some((l, l));
405 }
406
407 RegionElement::UniversalRegion(fr) => {
408 if let Some((location1, location2)) = open_location {
409 push_sep(&mut result);
410 Self::push_location_range(&mut result, location1, location2);
411 open_location = None;
412 }
413
414 push_sep(&mut result);
415 result.push_str(&format!("{:?}", fr));
416 }
417 }
418 }
419
420 if let Some((location1, location2)) = open_location {
421 push_sep(&mut result);
422 Self::push_location_range(&mut result, location1, location2);
423 }
424
425 result.push_str("}");
426
427 result
428 }
429
430 fn push_location_range(str: &mut String, location1: Location, location2: Location) {
431 if location1 == location2 {
432 str.push_str(&format!("{:?}", location1));
433 } else {
434 assert_eq!(location1.block, location2.block);
435 str.push_str(&format!(
436 "{:?}[{}..={}]",
0531ce1d 437 location1.block, location1.statement_index, location2.statement_index
ff7c6d11
XL
438 ));
439 }
440 }
441
442 /// Given a region `r` that contains the element `elem`, returns the `Cause`
443 /// that tells us *why* `elem` is found in that region.
444 ///
445 /// Returns None if cause tracking is disabled or `elem` is not
446 /// actually found in `r`.
447 pub(super) fn cause<T: ToElementIndex>(&self, r: RegionVid, elem: T) -> Option<Rc<Cause>> {
448 let index = self.elements.index(elem);
449 if let Some(causes) = &self.causes {
450 causes.get(&(r, index)).cloned()
451 } else {
452 None
453 }
454 }
455}