11//! Logic for lowering higher-kinded outlives constraints
22//! (with placeholders and universes) and turn them into regular
33//! outlives constraints.
4+ use core:: cmp;
45
56use rustc_data_structures:: frozen:: Frozen ;
67use rustc_data_structures:: fx:: FxIndexMap ;
@@ -10,7 +11,7 @@ use rustc_index::IndexVec;
1011use rustc_infer:: infer:: RegionVariableOrigin ;
1112use rustc_middle:: mir:: ConstraintCategory ;
1213use rustc_middle:: ty:: { RegionVid , UniverseIndex } ;
13- use tracing:: debug;
14+ use tracing:: { debug, trace } ;
1415
1516use crate :: constraints:: { ConstraintSccIndex , OutlivesConstraintSet } ;
1617use crate :: consumers:: OutlivesConstraint ;
@@ -62,84 +63,147 @@ impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
6263 type SccIdx = ConstraintSccIndex ;
6364}
6465
66+ #[ derive( Copy , Debug , Clone , PartialEq , Eq ) ]
67+ enum PlaceholderReachability {
68+ /// This SCC reaches no placeholders.
69+ NoPlaceholders ,
70+ /// This SCC reaches at least one placeholder.
71+ Placeholders {
72+ /// The largest-universed placeholder we can reach
73+ max_universe : ( UniverseIndex , RegionVid ) ,
74+
75+ /// The placeholder with the smallest ID
76+ min_placeholder : RegionVid ,
77+
78+ /// The placeholder with the largest ID
79+ max_placeholder : RegionVid ,
80+ } ,
81+ }
82+
83+ impl PlaceholderReachability {
84+ /// Merge the reachable placeholders of two graph components.
85+ fn merge ( self , other : PlaceholderReachability ) -> PlaceholderReachability {
86+ use PlaceholderReachability :: * ;
87+ match ( self , other) {
88+ ( NoPlaceholders , NoPlaceholders ) => NoPlaceholders ,
89+ ( NoPlaceholders , p @ Placeholders { .. } )
90+ | ( p @ Placeholders { .. } , NoPlaceholders ) => p,
91+ (
92+ Placeholders {
93+ min_placeholder : min_pl,
94+ max_placeholder : max_pl,
95+ max_universe : max_u,
96+ } ,
97+ Placeholders { min_placeholder, max_placeholder, max_universe } ,
98+ ) => Placeholders {
99+ min_placeholder : cmp:: min ( min_pl, min_placeholder) ,
100+ max_placeholder : cmp:: max ( max_pl, max_placeholder) ,
101+ max_universe : cmp:: max ( max_u, max_universe) ,
102+ } ,
103+ }
104+ }
105+
106+ fn max_universe ( & self ) -> Option < ( UniverseIndex , RegionVid ) > {
107+ match self {
108+ Self :: NoPlaceholders => None ,
109+ Self :: Placeholders { max_universe, .. } => Some ( * max_universe) ,
110+ }
111+ }
112+
113+ /// If we have reached placeholders, determine if they can
114+ /// be named from this universe.
115+ fn can_be_named_by ( & self , from : UniverseIndex ) -> bool {
116+ self . max_universe ( )
117+ . is_none_or ( |( max_placeholder_universe, _) | from. can_name ( max_placeholder_universe) )
118+ }
119+ }
120+
65121/// An annotation for region graph SCCs that tracks
66122/// the values of its elements. This annotates a single SCC.
67123#[ derive( Copy , Debug , Clone ) ]
68124pub ( crate ) struct RegionTracker {
69- /// The largest universe of a placeholder reached from this SCC.
70- /// This includes placeholders within this SCC.
71- max_placeholder_universe_reached : UniverseIndex ,
125+ reachable_placeholders : PlaceholderReachability ,
72126
73127 /// The largest universe nameable from this SCC.
74128 /// It is the smallest nameable universes of all
75- /// existential regions reachable from it.
76- max_nameable_universe : UniverseIndex ,
129+ /// existential regions reachable from it. Small Rvids are preferred.
130+ max_nameable_universe : ( UniverseIndex , RegionVid ) ,
77131
78132 /// The representative Region Variable Id for this SCC.
79133 pub ( crate ) representative : Representative ,
80134}
81135
82136impl RegionTracker {
83137 pub ( crate ) fn new ( rvid : RegionVid , definition : & RegionDefinition < ' _ > ) -> Self {
84- let placeholder_universe =
138+ let reachable_placeholders =
85139 if matches ! ( definition. origin, NllRegionVariableOrigin :: Placeholder ( _) ) {
86- definition. universe
140+ PlaceholderReachability :: Placeholders {
141+ max_universe : ( definition. universe , rvid) ,
142+ min_placeholder : rvid,
143+ max_placeholder : rvid,
144+ }
87145 } else {
88- UniverseIndex :: ROOT
146+ PlaceholderReachability :: NoPlaceholders
89147 } ;
90148
91149 Self {
92- max_placeholder_universe_reached : placeholder_universe ,
93- max_nameable_universe : definition. universe ,
150+ reachable_placeholders ,
151+ max_nameable_universe : ( definition. universe , rvid ) ,
94152 representative : Representative :: new ( rvid, definition) ,
95153 }
96154 }
97155
98156 /// The largest universe this SCC can name. It's the smallest
99- /// largest nameable uninverse of any reachable region.
157+ /// largest nameable universe of any reachable region, or
158+ /// `max_nameable(r) = min (max_nameable(r') for r' reachable from r)`
100159 pub ( crate ) fn max_nameable_universe ( self ) -> UniverseIndex {
101- self . max_nameable_universe
160+ self . max_nameable_universe . 0
102161 }
103162
104163 pub ( crate ) fn max_placeholder_universe_reached ( self ) -> UniverseIndex {
105- self . max_placeholder_universe_reached
106- }
107-
108- fn merge_min_max_seen ( & mut self , other : & Self ) {
109- self . max_placeholder_universe_reached = std:: cmp:: max (
110- self . max_placeholder_universe_reached ,
111- other. max_placeholder_universe_reached ,
112- ) ;
113-
114- self . max_nameable_universe =
115- std:: cmp:: min ( self . max_nameable_universe , other. max_nameable_universe ) ;
116- }
117-
118- /// Returns `true` if during the annotated SCC reaches a placeholder
119- /// with a universe larger than the smallest nameable universe of any
120- /// reachable existential region.
121- pub ( crate ) fn has_incompatible_universes ( & self ) -> bool {
122- self . max_nameable_universe ( ) . cannot_name ( self . max_placeholder_universe_reached )
164+ if let Some ( ( universe, _) ) = self . reachable_placeholders . max_universe ( ) {
165+ universe
166+ } else {
167+ UniverseIndex :: ROOT
168+ }
123169 }
124170
125171 /// Determine if the tracked universes of the two SCCs are compatible.
126172 pub ( crate ) fn universe_compatible_with ( & self , other : Self ) -> bool {
173+ // HACK: We first check whether we can name the highest existential universe
174+ // of `other`. This only exists to avoid errors in case that scc already
175+ // depends on a placeholder it cannot name itself.
127176 self . max_nameable_universe ( ) . can_name ( other. max_nameable_universe ( ) )
128- || self . max_nameable_universe ( ) . can_name ( other. max_placeholder_universe_reached )
177+ || other. reachable_placeholders . can_be_named_by ( self . max_nameable_universe ( ) )
178+ }
179+
180+ /// If this SCC reaches a placeholder it can't name, return it.
181+ fn unnameable_placeholder ( & self ) -> Option < ( UniverseIndex , RegionVid ) > {
182+ self . reachable_placeholders . max_universe ( ) . filter ( |& ( placeholder_universe, _) | {
183+ !self . max_nameable_universe ( ) . can_name ( placeholder_universe)
184+ } )
129185 }
130186}
131187
132188impl scc:: Annotation for RegionTracker {
133- fn merge_scc ( mut self , other : Self ) -> Self {
134- self . representative = self . representative . merge_scc ( other. representative ) ;
135- self . merge_min_max_seen ( & other) ;
136- self
189+ fn merge_scc ( self , other : Self ) -> Self {
190+ trace ! ( "{:?} << {:?}" , self . representative, other. representative) ;
191+
192+ Self {
193+ representative : self . representative . merge_scc ( other. representative ) ,
194+ ..self . merge_reached ( other)
195+ }
137196 }
138197
139- fn merge_reached ( mut self , other : Self ) -> Self {
140- // No update to in-component values, only add seen values.
141- self . merge_min_max_seen ( & other) ;
142- self
198+ fn merge_reached ( self , other : Self ) -> Self {
199+ Self {
200+ max_nameable_universe : cmp:: min (
201+ self . max_nameable_universe ,
202+ other. max_nameable_universe ,
203+ ) ,
204+ reachable_placeholders : self . reachable_placeholders . merge ( other. reachable_placeholders ) ,
205+ representative : self . representative ,
206+ }
143207 }
144208}
145209
@@ -310,28 +374,52 @@ fn rewrite_placeholder_outlives<'tcx>(
310374
311375 let annotation = annotations[ scc] ;
312376
313- // If this SCC participates in a universe violation,
314- // e.g. if it reaches a region with a universe smaller than
315- // the largest region reached, add a requirement that it must
316- // outlive `'static`.
317- if annotation. has_incompatible_universes ( ) {
318- // Optimisation opportunity: this will add more constraints than
319- // needed for correctness, since an SCC upstream of another with
320- // a universe violation will "infect" its downstream SCCs to also
321- // outlive static.
322- let scc_representative_outlives_static = OutlivesConstraint {
323- sup : annotation. representative . rvid ( ) ,
324- sub : fr_static,
325- category : ConstraintCategory :: IllegalUniverse ,
326- locations : Locations :: All ( rustc_span:: DUMMY_SP ) ,
327- span : rustc_span:: DUMMY_SP ,
328- variance_info : VarianceDiagInfo :: None ,
329- from_closure : false ,
330- } ;
331- outlives_constraints. push ( scc_representative_outlives_static) ;
332- added_constraints = true ;
333- debug ! ( "Added {:?}: 'static!" , annotation. representative. rvid( ) ) ;
334- }
377+ let Some ( ( max_u, max_u_rvid) ) = annotation. unnameable_placeholder ( ) else {
378+ continue ;
379+ } ;
380+
381+ debug ! (
382+ "Placeholder universe {max_u:?} is too large for its SCC, represented by {:?}" ,
383+ annotation. representative
384+ ) ;
385+
386+ // We only add one `r: 'static` constraint per SCC, where `r` is the SCC representative.
387+ // That constraint is annotated with some placeholder `unnameable` where
388+ // `unnameable` is unnameable from `r` and there is a path in the constraint graph
389+ // between them.
390+ //
391+ // There is one exception; if some other region in this SCC can't name `'r`, then
392+ // we pick the region with the smallest universe in the SCC, so that a path can
393+ // always start in `'r` to find a motivation that isn't cyclic.
394+ let blame_to = if annotation. representative . rvid ( ) == max_u_rvid {
395+ // Assertion: the region that lowered our universe is an existential one and we are a placeholder!
396+
397+ // The SCC's representative is not nameable from some region
398+ // that ends up in the SCC.
399+ let small_universed_rvid = annotation. max_nameable_universe . 1 ;
400+ debug ! (
401+ "{small_universed_rvid:?} lowered our universe to {:?}" ,
402+ annotation. max_nameable_universe( )
403+ ) ;
404+ small_universed_rvid
405+ } else {
406+ // `max_u_rvid` is not nameable by the SCC's representative.
407+ max_u_rvid
408+ } ;
409+
410+ // FIXME: if we can extract a useful blame span here, future error
411+ // reporting and constraint search can be simplified.
412+
413+ added_constraints = true ;
414+ outlives_constraints. push ( OutlivesConstraint {
415+ sup : annotation. representative . rvid ( ) ,
416+ sub : fr_static,
417+ category : ConstraintCategory :: OutlivesUnnameablePlaceholder ( blame_to) ,
418+ locations : Locations :: All ( rustc_span:: DUMMY_SP ) ,
419+ span : rustc_span:: DUMMY_SP ,
420+ variance_info : VarianceDiagInfo :: None ,
421+ from_closure : false ,
422+ } ) ;
335423 }
336424 added_constraints
337425}
0 commit comments