11//! Logic for lowering higher-kinded outlives constraints
22//! (with placeholders and universes) and turn them into regular
33//! outlives constraints.
4- //!
5- //! This logic is provisional and should be removed once the trait
6- //! solver can handle this kind of constraint.
4+
75use rustc_data_structures:: frozen:: Frozen ;
8- use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap } ;
6+ use rustc_data_structures:: fx:: FxIndexMap ;
97use rustc_data_structures:: graph:: scc;
108use rustc_data_structures:: graph:: scc:: Sccs ;
119use rustc_index:: IndexVec ;
10+ use rustc_infer:: infer:: RegionVariableOrigin ;
1211use rustc_middle:: mir:: ConstraintCategory ;
1312use rustc_middle:: ty:: { RegionVid , UniverseIndex } ;
1413use tracing:: debug;
@@ -18,7 +17,7 @@ use crate::consumers::OutlivesConstraint;
1817use crate :: diagnostics:: UniverseInfo ;
1918use crate :: member_constraints:: MemberConstraintSet ;
2019use crate :: region_infer:: values:: { LivenessValues , PlaceholderIndices } ;
21- use crate :: region_infer:: { ConstraintSccs , RegionDefinition , TypeTest } ;
20+ use crate :: region_infer:: { ConstraintSccs , RegionDefinition , Representative , TypeTest } ;
2221use crate :: ty:: VarianceDiagInfo ;
2322use crate :: type_check:: free_region_relations:: UniversalRegionRelations ;
2423use crate :: type_check:: { Locations , MirTypeckRegionConstraints } ;
@@ -32,7 +31,7 @@ pub(crate) struct LoweredConstraints<'tcx> {
3231 pub ( crate ) definitions : Frozen < IndexVec < RegionVid , RegionDefinition < ' tcx > > > ,
3332 pub ( crate ) scc_annotations : IndexVec < ConstraintSccIndex , RegionTracker > ,
3433 pub ( crate ) member_constraints : MemberConstraintSet < ' tcx , RegionVid > ,
35- pub ( crate ) outlives_constraints : OutlivesConstraintSet < ' tcx > ,
34+ pub ( crate ) outlives_constraints : Frozen < OutlivesConstraintSet < ' tcx > > ,
3635 pub ( crate ) type_tests : Vec < TypeTest < ' tcx > > ,
3736 pub ( crate ) liveness_constraints : LivenessValues ,
3837 pub ( crate ) universe_causes : FxIndexMap < UniverseIndex , UniverseInfo < ' tcx > > ,
@@ -73,45 +72,35 @@ pub(crate) struct RegionTracker {
7372 /// This includes placeholders within this SCC.
7473 max_placeholder_universe_reached : UniverseIndex ,
7574
76- /// The smallest universe index reachable form the nodes of this SCC.
77- min_reachable_universe : UniverseIndex ,
78-
79- /// The representative Region Variable Id for this SCC. We prefer
80- /// placeholders over existentially quantified variables, otherwise
81- /// it's the one with the smallest Region Variable ID.
82- pub ( crate ) representative : RegionVid ,
83-
84- /// Is the current representative a placeholder?
85- representative_is_placeholder : bool ,
75+ /// The largest universe nameable from this SCC.
76+ /// It is the smallest nameable universes of all
77+ /// existential regions reachable from it.
78+ max_nameable_universe : UniverseIndex ,
8679
87- /// Is the current representative existentially quantified?
88- representative_is_existential : bool ,
80+ /// The representative Region Variable Id for this SCC.
81+ pub ( crate ) representative : Representative ,
8982}
9083
9184impl RegionTracker {
9285 pub ( crate ) fn new ( rvid : RegionVid , definition : & RegionDefinition < ' _ > ) -> Self {
93- let ( representative_is_placeholder, representative_is_existential) = match definition. origin
94- {
95- NllRegionVariableOrigin :: FreeRegion => ( false , false ) ,
96- NllRegionVariableOrigin :: Placeholder ( _) => ( true , false ) ,
97- NllRegionVariableOrigin :: Existential { .. } => ( false , true ) ,
98- } ;
99-
10086 let placeholder_universe =
101- if representative_is_placeholder { definition. universe } else { UniverseIndex :: ROOT } ;
87+ if matches ! ( definition. origin, NllRegionVariableOrigin :: Placeholder ( _) ) {
88+ definition. universe
89+ } else {
90+ UniverseIndex :: ROOT
91+ } ;
10292
10393 Self {
10494 max_placeholder_universe_reached : placeholder_universe,
105- min_reachable_universe : definition. universe ,
106- representative : rvid,
107- representative_is_placeholder,
108- representative_is_existential,
95+ max_nameable_universe : definition. universe ,
96+ representative : Representative :: new ( rvid, definition) ,
10997 }
11098 }
11199
112- /// The smallest-indexed universe reachable from and/or in this SCC.
113- pub ( crate ) fn min_universe ( self ) -> UniverseIndex {
114- self . min_reachable_universe
100+ /// The largest universe this SCC can name. It's the smallest
101+ /// largest nameable uninverse of any reachable region.
102+ pub ( crate ) fn max_nameable_universe ( self ) -> UniverseIndex {
103+ self . max_nameable_universe
115104 }
116105
117106 fn merge_min_max_seen ( & mut self , other : & Self ) {
@@ -120,40 +109,29 @@ impl RegionTracker {
120109 other. max_placeholder_universe_reached ,
121110 ) ;
122111
123- self . min_reachable_universe =
124- std:: cmp:: min ( self . min_reachable_universe , other. min_reachable_universe ) ;
112+ self . max_nameable_universe =
113+ std:: cmp:: min ( self . max_nameable_universe , other. max_nameable_universe ) ;
125114 }
126115
127116 /// Returns `true` if during the annotated SCC reaches a placeholder
128- /// with a universe larger than the smallest reachable one, `false` otherwise.
117+ /// with a universe larger than the smallest nameable universe of any
118+ /// reachable existential region.
129119 pub ( crate ) fn has_incompatible_universes ( & self ) -> bool {
130- self . min_universe ( ) . cannot_name ( self . max_placeholder_universe_reached )
120+ self . max_nameable_universe ( ) . cannot_name ( self . max_placeholder_universe_reached )
131121 }
132122
133- /// Determine if the tracked universes of the two SCCs
134- /// are compatible.
123+ /// Determine if the tracked universes of the two SCCs are compatible.
135124 pub ( crate ) fn universe_compatible_with ( & self , other : Self ) -> bool {
136- self . min_universe ( ) . can_name ( other. min_universe ( ) )
137- || self . min_universe ( ) . can_name ( other. max_placeholder_universe_reached )
125+ self . max_nameable_universe ( ) . can_name ( other. max_nameable_universe ( ) )
126+ || self . max_nameable_universe ( ) . can_name ( other. max_placeholder_universe_reached )
138127 }
139128}
140129
141130impl scc:: Annotation for RegionTracker {
142- fn merge_scc ( mut self , mut other : Self ) -> Self {
143- // Prefer any placeholder over any existential
144- if other. representative_is_placeholder && self . representative_is_existential {
145- other. merge_min_max_seen ( & self ) ;
146- return other;
147- }
148-
149- if self . representative_is_placeholder && other. representative_is_existential
150- || ( self . representative <= other. representative )
151- {
152- self . merge_min_max_seen ( & other) ;
153- return self ;
154- }
155- other. merge_min_max_seen ( & self ) ;
156- other
131+ fn merge_scc ( mut self , other : Self ) -> Self {
132+ self . representative = self . representative . merge_scc ( other. representative ) ;
133+ self . merge_min_max_seen ( & other) ;
134+ self
157135 }
158136
159137 fn merge_reached ( mut self , other : Self ) -> Self {
@@ -164,7 +142,7 @@ impl scc::Annotation for RegionTracker {
164142}
165143
166144/// Determines if the region variable definitions contain
167- /// placeholers , and compute them for later use.
145+ /// placeholders , and compute them for later use.
168146fn region_definitions < ' tcx > (
169147 universal_regions : & UniversalRegions < ' tcx > ,
170148 infcx : & BorrowckInferCtxt < ' tcx > ,
@@ -177,42 +155,39 @@ fn region_definitions<'tcx>(
177155 let mut has_placeholders = false ;
178156
179157 for info in var_infos. iter ( ) {
180- let definition = RegionDefinition :: new ( info) ;
181- has_placeholders |= matches ! ( definition. origin, NllRegionVariableOrigin :: Placeholder ( _) ) ;
158+ let origin = match info. origin {
159+ RegionVariableOrigin :: Nll ( origin) => origin,
160+ _ => NllRegionVariableOrigin :: Existential { from_forall : false } ,
161+ } ;
162+
163+ let definition = RegionDefinition { origin, universe : info. universe , external_name : None } ;
164+
165+ has_placeholders |= matches ! ( origin, NllRegionVariableOrigin :: Placeholder ( _) ) ;
182166 definitions. push ( definition) ;
183167 }
184168
185169 // Add external names from universal regions in fun function definitions.
170+ // FIXME: this two-step method is annoying, but I don't know how to avoid it.
186171 for ( external_name, variable) in universal_regions. named_universal_regions_iter ( ) {
187172 debug ! ( "region {:?} has external name {:?}" , variable, external_name) ;
188173 definitions[ variable] . external_name = Some ( external_name) ;
189174 }
190175 ( Frozen :: freeze ( definitions) , has_placeholders)
191176}
192177
193- /// This method handles Universe errors by rewriting the constraint
178+ /// This method handles placeholders by rewriting the constraint
194179/// graph. For each strongly connected component in the constraint
195180/// graph such that there is a series of constraints
196181/// A: B: C: ... : X where
197- /// A's universe is smaller than X's and A is a placeholder ,
182+ /// A contains a placeholder whose universe cannot be named by X ,
198183/// add a constraint that A: 'static. This is a safe upper bound
199184/// in the face of borrow checker/trait solver limitations that will
200185/// eventually go away.
201186///
202187/// For a more precise definition, see the documentation for
203- /// [`RegionTracker`] and its methods!.
204- ///
205- /// Since universes can also be involved in errors (if one placeholder
206- /// transitively outlives another), this function also flags those.
207- ///
208- /// Additionally, it similarly rewrites type-tests.
209- ///
210- /// This edge case used to be handled during constraint propagation
211- /// by iterating over the strongly connected components in the constraint
212- /// graph while maintaining a set of bookkeeping mappings similar
213- /// to what is stored in `RegionTracker` and manually adding 'sttaic as
214- /// needed.
188+ /// [`RegionTracker`] and its methods!
215189///
190+ /// This edge case used to be handled during constraint propagation.
216191/// It was rewritten as part of the Polonius project with the goal of moving
217192/// higher-kindedness concerns out of the path of the borrow checker,
218193/// for two reasons:
@@ -228,7 +203,7 @@ fn region_definitions<'tcx>(
228203/// This code is a stop-gap measure in preparation for the future trait solver.
229204///
230205/// Every constraint added by this method is an internal `IllegalUniverse` constraint.
231- pub ( crate ) fn rewrite_higher_kinded_outlives_as_constraints < ' tcx > (
206+ pub ( crate ) fn compute_sccs_applying_placeholder_outlives_constraints < ' tcx > (
232207 constraints : MirTypeckRegionConstraints < ' tcx > ,
233208 universal_region_relations : & Frozen < UniversalRegionRelations < ' tcx > > ,
234209 infcx : & BorrowckInferCtxt < ' tcx > ,
@@ -267,36 +242,37 @@ pub(crate) fn rewrite_higher_kinded_outlives_as_constraints<'tcx>(
267242 )
268243 } ;
269244
245+ let mut scc_annotations = SccAnnotations :: init ( & definitions) ;
246+ let constraint_sccs = compute_sccs ( & outlives_constraints, & mut scc_annotations) ;
247+
270248 // This code structure is a bit convoluted because it allows for a planned
271249 // future change where the early return here has a different type of annotation
272250 // that does much less work.
273251 if !has_placeholders {
274252 debug ! ( "No placeholder regions found; skipping rewriting logic!" ) ;
275- let mut scc_annotations = SccAnnotations :: init ( & definitions) ;
276- let constraint_sccs = compute_sccs ( & outlives_constraints, & mut scc_annotations) ;
277253
278254 return LoweredConstraints {
279255 type_tests,
280256 member_constraints,
281257 constraint_sccs,
282258 scc_annotations : scc_annotations. scc_to_annotation ,
283259 definitions,
284- outlives_constraints,
260+ outlives_constraints : Frozen :: freeze ( outlives_constraints ) ,
285261 liveness_constraints,
286262 universe_causes,
287263 placeholder_indices,
288264 } ;
289265 }
290266 debug ! ( "Placeholders present; activating placeholder handling logic!" ) ;
291267
292- let mut annotations = SccAnnotations :: init ( & definitions) ;
293- let sccs = compute_sccs ( & outlives_constraints, & mut annotations) ;
294-
295- let outlives_static =
296- rewrite_outlives ( & sccs, & annotations, fr_static, & mut outlives_constraints) ;
268+ let added_constraints = rewrite_placeholder_outlives (
269+ & constraint_sccs,
270+ & scc_annotations,
271+ fr_static,
272+ & mut outlives_constraints,
273+ ) ;
297274
298- let ( sccs, scc_annotations) = if !outlives_static. is_empty ( ) {
299- debug ! ( "The following SCCs had :'static constraints added: {:?}" , outlives_static) ;
275+ let ( constraint_sccs, scc_annotations) = if added_constraints {
300276 let mut annotations = SccAnnotations :: init ( & definitions) ;
301277
302278 // We changed the constraint set and so must recompute SCCs.
@@ -307,31 +283,31 @@ pub(crate) fn rewrite_higher_kinded_outlives_as_constraints<'tcx>(
307283 } else {
308284 // If we didn't add any back-edges; no more work needs doing
309285 debug ! ( "No constraints rewritten!" ) ;
310- ( sccs , annotations . scc_to_annotation )
286+ ( constraint_sccs , scc_annotations . scc_to_annotation )
311287 } ;
312288
313289 LoweredConstraints {
314- constraint_sccs : sccs ,
290+ constraint_sccs,
315291 definitions,
316292 scc_annotations,
317293 member_constraints,
318- outlives_constraints,
294+ outlives_constraints : Frozen :: freeze ( outlives_constraints ) ,
319295 type_tests,
320296 liveness_constraints,
321297 universe_causes,
322298 placeholder_indices,
323299 }
324300}
325301
326- fn rewrite_outlives < ' tcx > (
302+ fn rewrite_placeholder_outlives < ' tcx > (
327303 sccs : & Sccs < RegionVid , ConstraintSccIndex > ,
328304 annotations : & SccAnnotations < ' _ , ' _ , RegionTracker > ,
329305 fr_static : RegionVid ,
330306 outlives_constraints : & mut OutlivesConstraintSet < ' tcx > ,
331- ) -> FxHashSet < ConstraintSccIndex > {
332- // Changed to `true` if we added any constraints to `self` and need to
307+ ) -> bool {
308+ // Changed to `true` if we added any constraints and need to
333309 // recompute SCCs.
334- let mut outlives_static = FxHashSet :: default ( ) ;
310+ let mut added_constraints = false ;
335311
336312 let annotations = & annotations. scc_to_annotation ;
337313
@@ -354,9 +330,8 @@ fn rewrite_outlives<'tcx>(
354330 // needed for correctness, since an SCC upstream of another with
355331 // a universe violation will "infect" its downstream SCCs to also
356332 // outlive static.
357- outlives_static. insert ( scc) ;
358333 let scc_representative_outlives_static = OutlivesConstraint {
359- sup : annotation. representative ,
334+ sup : annotation. representative . rvid ( ) ,
360335 sub : fr_static,
361336 category : ConstraintCategory :: IllegalUniverse ,
362337 locations : Locations :: All ( rustc_span:: DUMMY_SP ) ,
@@ -365,7 +340,9 @@ fn rewrite_outlives<'tcx>(
365340 from_closure : false ,
366341 } ;
367342 outlives_constraints. push ( scc_representative_outlives_static) ;
343+ added_constraints = true ;
344+ debug ! ( "Added {:?}: 'static!" , annotation. representative. rvid( ) ) ;
368345 }
369346 }
370- outlives_static
347+ added_constraints
371348}
0 commit comments