rustc_borrowck/constraints/
mod.rs

1use std::fmt;
2use std::ops::Index;
3
4use rustc_index::{IndexSlice, IndexVec};
5use rustc_middle::mir::ConstraintCategory;
6use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo};
7use rustc_span::Span;
8use tracing::{debug, instrument};
9
10use crate::region_infer::{ConstraintSccs, RegionDefinition, RegionTracker};
11use crate::type_check::Locations;
12use crate::universal_regions::UniversalRegions;
13
14pub(crate) mod graph;
15
16/// A set of NLL region constraints. These include "outlives"
17/// constraints of the form `R1: R2`. Each constraint is identified by
18/// a unique `OutlivesConstraintIndex` and you can index into the set
19/// (`constraint_set[i]`) to access the constraint details.
20#[derive(Clone, Debug, Default)]
21pub(crate) struct OutlivesConstraintSet<'tcx> {
22    outlives: IndexVec<OutlivesConstraintIndex, OutlivesConstraint<'tcx>>,
23}
24
25impl<'tcx> OutlivesConstraintSet<'tcx> {
26    pub(crate) fn push(&mut self, constraint: OutlivesConstraint<'tcx>) {
27        debug!("OutlivesConstraintSet::push({:?})", constraint);
28        if constraint.sup == constraint.sub {
29            // 'a: 'a is pretty uninteresting
30            return;
31        }
32        self.outlives.push(constraint);
33    }
34
35    /// Constructs a "normal" graph from the constraint set; the graph makes it
36    /// easy to find the constraints affecting a particular region.
37    ///
38    /// N.B., this graph contains a "frozen" view of the current
39    /// constraints. Any new constraints added to the `OutlivesConstraintSet`
40    /// after the graph is built will not be present in the graph.
41    pub(crate) fn graph(&self, num_region_vars: usize) -> graph::NormalConstraintGraph {
42        graph::ConstraintGraph::new(graph::Normal, self, num_region_vars)
43    }
44
45    /// Like `graph`, but constraints a reverse graph where `R1: R2`
46    /// represents an edge `R2 -> R1`.
47    pub(crate) fn reverse_graph(&self, num_region_vars: usize) -> graph::ReverseConstraintGraph {
48        graph::ConstraintGraph::new(graph::Reverse, self, num_region_vars)
49    }
50
51    pub(crate) fn outlives(
52        &self,
53    ) -> &IndexSlice<OutlivesConstraintIndex, OutlivesConstraint<'tcx>> {
54        &self.outlives
55    }
56
57    /// Computes cycles (SCCs) in the graph of regions. In particular,
58    /// find all regions R1, R2 such that R1: R2 and R2: R1 and group
59    /// them into an SCC, and find the relationships between SCCs.
60    pub(crate) fn compute_sccs(
61        &self,
62        static_region: RegionVid,
63        definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
64    ) -> ConstraintSccs {
65        let constraint_graph = self.graph(definitions.len());
66        let region_graph = &constraint_graph.region_graph(self, static_region);
67        ConstraintSccs::new_with_annotation(&region_graph, |r| {
68            RegionTracker::new(r, &definitions[r])
69        })
70    }
71
72    /// This method handles Universe errors by rewriting the constraint
73    /// graph. For each strongly connected component in the constraint
74    /// graph such that there is a series of constraints
75    ///    A: B: C: ... : X  where
76    /// A's universe is smaller than X's and A is a placeholder,
77    /// add a constraint that A: 'static. This is a safe upper bound
78    /// in the face of borrow checker/trait solver limitations that will
79    /// eventually go away.
80    ///
81    /// For a more precise definition, see the documentation for
82    /// [`RegionTracker::has_incompatible_universes()`].
83    ///
84    /// This edge case used to be handled during constraint propagation
85    /// by iterating over the strongly connected components in the constraint
86    /// graph while maintaining a set of bookkeeping mappings similar
87    /// to what is stored in `RegionTracker` and manually adding 'sttaic as
88    /// needed.
89    ///
90    /// It was rewritten as part of the Polonius project with the goal of moving
91    /// higher-kindedness concerns out of the path of the borrow checker,
92    /// for two reasons:
93    ///
94    /// 1. Implementing Polonius is difficult enough without also
95    ///     handling them.
96    /// 2. The long-term goal is to handle higher-kinded concerns
97    ///     in the trait solver, where they belong. This avoids
98    ///     logic duplication and allows future trait solvers
99    ///     to compute better bounds than for example our
100    ///     "must outlive 'static" here.
101    ///
102    /// This code is a stop-gap measure in preparation for the future trait solver.
103    ///
104    /// Every constraint added by this method is an
105    /// internal `IllegalUniverse` constraint.
106    #[instrument(skip(self, universal_regions, definitions))]
107    pub(crate) fn add_outlives_static(
108        &mut self,
109        universal_regions: &UniversalRegions<'tcx>,
110        definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
111    ) -> ConstraintSccs {
112        let fr_static = universal_regions.fr_static;
113        let sccs = self.compute_sccs(fr_static, definitions);
114
115        // Changed to `true` if we added any constraints to `self` and need to
116        // recompute SCCs.
117        let mut added_constraints = false;
118
119        for scc in sccs.all_sccs() {
120            // No point in adding 'static: 'static!
121            // This micro-optimisation makes somewhat sense
122            // because static outlives *everything*.
123            if scc == sccs.scc(fr_static) {
124                continue;
125            }
126
127            let annotation = sccs.annotation(scc);
128
129            // If this SCC participates in a universe violation,
130            // e.g. if it reaches a region with a universe smaller than
131            // the largest region reached, add a requirement that it must
132            // outlive `'static`.
133            if annotation.has_incompatible_universes() {
134                // Optimisation opportunity: this will add more constraints than
135                // needed for correctness, since an SCC upstream of another with
136                // a universe violation will "infect" its downstream SCCs to also
137                // outlive static.
138                added_constraints = true;
139                let scc_representative_outlives_static = OutlivesConstraint {
140                    sup: annotation.representative,
141                    sub: fr_static,
142                    category: ConstraintCategory::IllegalUniverse,
143                    locations: Locations::All(rustc_span::DUMMY_SP),
144                    span: rustc_span::DUMMY_SP,
145                    variance_info: VarianceDiagInfo::None,
146                    from_closure: false,
147                };
148                self.push(scc_representative_outlives_static);
149            }
150        }
151
152        if added_constraints {
153            // We changed the constraint set and so must recompute SCCs.
154            self.compute_sccs(fr_static, definitions)
155        } else {
156            // If we didn't add any back-edges; no more work needs doing
157            sccs
158        }
159    }
160}
161
162impl<'tcx> Index<OutlivesConstraintIndex> for OutlivesConstraintSet<'tcx> {
163    type Output = OutlivesConstraint<'tcx>;
164
165    fn index(&self, i: OutlivesConstraintIndex) -> &Self::Output {
166        &self.outlives[i]
167    }
168}
169
170#[derive(Copy, Clone, PartialEq, Eq)]
171pub struct OutlivesConstraint<'tcx> {
172    // NB. The ordering here is not significant for correctness, but
173    // it is for convenience. Before we dump the constraints in the
174    // debugging logs, we sort them, and we'd like the "super region"
175    // to be first, etc. (In particular, span should remain last.)
176    /// The region SUP must outlive SUB...
177    pub sup: RegionVid,
178
179    /// Region that must be outlived.
180    pub sub: RegionVid,
181
182    /// Where did this constraint arise?
183    pub locations: Locations,
184
185    /// The `Span` associated with the creation of this constraint.
186    /// This should be used in preference to obtaining the span from
187    /// `locations`, since the `locations` may give a poor span
188    /// in some cases (e.g. converting a constraint from a promoted).
189    pub span: Span,
190
191    /// What caused this constraint?
192    pub category: ConstraintCategory<'tcx>,
193
194    /// Variance diagnostic information
195    pub variance_info: VarianceDiagInfo<TyCtxt<'tcx>>,
196
197    /// If this constraint is promoted from closure requirements.
198    pub from_closure: bool,
199}
200
201impl<'tcx> fmt::Debug for OutlivesConstraint<'tcx> {
202    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
203        write!(
204            formatter,
205            "({:?}: {:?}) due to {:?} ({:?}) ({:?})",
206            self.sup, self.sub, self.locations, self.variance_info, self.category,
207        )
208    }
209}
210
211rustc_index::newtype_index! {
212    #[debug_format = "OutlivesConstraintIndex({})"]
213    pub(crate) struct OutlivesConstraintIndex {}
214}
215
216rustc_index::newtype_index! {
217    #[orderable]
218    #[debug_format = "ConstraintSccIndex({})"]
219    pub struct ConstraintSccIndex {}
220}