11//! Logic for lowering higher-kinded outlives constraints
22//! (with placeholders and universes) and turn them into regular
33//! outlives constraints.
4-
54use rustc_data_structures:: frozen:: Frozen ;
65use rustc_data_structures:: fx:: FxIndexMap ;
76use rustc_data_structures:: graph:: scc;
@@ -10,7 +9,7 @@ use rustc_index::IndexVec;
109use rustc_infer:: infer:: RegionVariableOrigin ;
1110use rustc_middle:: mir:: ConstraintCategory ;
1211use rustc_middle:: ty:: { RegionVid , UniverseIndex } ;
13- use tracing:: debug;
12+ use tracing:: { debug, trace } ;
1413
1514use crate :: constraints:: { ConstraintSccIndex , OutlivesConstraintSet } ;
1615use crate :: consumers:: OutlivesConstraint ;
@@ -62,84 +61,145 @@ impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
6261 type SccIdx = ConstraintSccIndex ;
6362}
6463
64+ #[ derive( Copy , Debug , Clone , PartialEq , Eq ) ]
65+ enum PlaceholderReachability {
66+ /// This SCC reaches no placeholders.
67+ NoPlaceholders ,
68+ /// This SCC reaches at least one placeholder.
69+ Placeholders {
70+ /// The largest-universed placeholder we can reach
71+ max_universe : ( UniverseIndex , RegionVid ) ,
72+
73+ /// The placeholder with the smallest ID
74+ min_placeholder : RegionVid ,
75+
76+ /// The placeholder with the largest ID
77+ max_placeholder : RegionVid ,
78+ } ,
79+ }
80+
81+ impl PlaceholderReachability {
82+ /// Merge the reachable placeholders of two graph components.
83+ fn merge ( self , other : PlaceholderReachability ) -> PlaceholderReachability {
84+ use PlaceholderReachability :: * ;
85+ match ( self , other) {
86+ ( NoPlaceholders , NoPlaceholders ) => NoPlaceholders ,
87+ ( NoPlaceholders , p @ Placeholders { .. } )
88+ | ( p @ Placeholders { .. } , NoPlaceholders ) => p,
89+ (
90+ Placeholders {
91+ min_placeholder : min_pl,
92+ max_placeholder : max_pl,
93+ max_universe : max_u,
94+ } ,
95+ Placeholders { min_placeholder, max_placeholder, max_universe } ,
96+ ) => Placeholders {
97+ min_placeholder : min_pl. min ( min_placeholder) ,
98+ max_placeholder : max_pl. max ( max_placeholder) ,
99+ max_universe : max_u. max ( max_universe) ,
100+ } ,
101+ }
102+ }
103+
104+ fn max_universe ( & self ) -> Option < ( UniverseIndex , RegionVid ) > {
105+ match self {
106+ Self :: NoPlaceholders => None ,
107+ Self :: Placeholders { max_universe, .. } => Some ( * max_universe) ,
108+ }
109+ }
110+
111+ /// If we have reached placeholders, determine if they can
112+ /// be named from this universe.
113+ fn can_be_named_by ( & self , from : UniverseIndex ) -> bool {
114+ self . max_universe ( )
115+ . is_none_or ( |( max_placeholder_universe, _) | from. can_name ( max_placeholder_universe) )
116+ }
117+ }
118+
65119/// An annotation for region graph SCCs that tracks
66120/// the values of its elements. This annotates a single SCC.
67121#[ derive( Copy , Debug , Clone ) ]
68122pub ( 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 ,
123+ reachable_placeholders : PlaceholderReachability ,
72124
73125 /// The largest universe nameable from this SCC.
74126 /// It is the smallest nameable universes of all
75- /// existential regions reachable from it.
76- max_nameable_universe : UniverseIndex ,
127+ /// existential regions reachable from it. Small Rvids are preferred.
128+ max_nameable_universe : ( UniverseIndex , RegionVid ) ,
77129
78130 /// The representative Region Variable Id for this SCC.
79131 pub ( crate ) representative : Representative ,
80132}
81133
82134impl RegionTracker {
83135 pub ( crate ) fn new ( rvid : RegionVid , definition : & RegionDefinition < ' _ > ) -> Self {
84- let placeholder_universe =
136+ let reachable_placeholders =
85137 if matches ! ( definition. origin, NllRegionVariableOrigin :: Placeholder ( _) ) {
86- definition. universe
138+ PlaceholderReachability :: Placeholders {
139+ max_universe : ( definition. universe , rvid) ,
140+ min_placeholder : rvid,
141+ max_placeholder : rvid,
142+ }
87143 } else {
88- UniverseIndex :: ROOT
144+ PlaceholderReachability :: NoPlaceholders
89145 } ;
90146
91147 Self {
92- max_placeholder_universe_reached : placeholder_universe ,
93- max_nameable_universe : definition. universe ,
148+ reachable_placeholders ,
149+ max_nameable_universe : ( definition. universe , rvid ) ,
94150 representative : Representative :: new ( rvid, definition) ,
95151 }
96152 }
97153
98154 /// The largest universe this SCC can name. It's the smallest
99- /// largest nameable uninverse of any reachable region.
155+ /// largest nameable universe of any reachable region, or
156+ /// `max_nameable(r) = min (max_nameable(r') for r' reachable from r)`
100157 pub ( crate ) fn max_nameable_universe ( self ) -> UniverseIndex {
101- self . max_nameable_universe
158+ self . max_nameable_universe . 0
102159 }
103160
104161 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 )
162+ if let Some ( ( universe, _) ) = self . reachable_placeholders . max_universe ( ) {
163+ universe
164+ } else {
165+ UniverseIndex :: ROOT
166+ }
123167 }
124168
125169 /// Determine if the tracked universes of the two SCCs are compatible.
126170 pub ( crate ) fn universe_compatible_with ( & self , other : Self ) -> bool {
171+ // HACK: We first check whether we can name the highest existential universe
172+ // of `other`. This only exists to avoid errors in case that scc already
173+ // depends on a placeholder it cannot name itself.
127174 self . max_nameable_universe ( ) . can_name ( other. max_nameable_universe ( ) )
128- || self . max_nameable_universe ( ) . can_name ( other. max_placeholder_universe_reached )
175+ || other. reachable_placeholders . can_be_named_by ( self . max_nameable_universe ( ) )
176+ }
177+
178+ /// If this SCC reaches a placeholder it can't name, return it.
179+ fn unnameable_placeholder ( & self ) -> Option < ( UniverseIndex , RegionVid ) > {
180+ self . reachable_placeholders . max_universe ( ) . filter ( |& ( placeholder_universe, _) | {
181+ !self . max_nameable_universe ( ) . can_name ( placeholder_universe)
182+ } )
129183 }
130184}
131185
132186impl 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
187+ fn merge_scc ( self , other : Self ) -> Self {
188+ trace ! ( "{:?} << {:?}" , self . representative, other. representative) ;
189+
190+ Self {
191+ representative : self . representative . min ( other. representative ) ,
192+ max_nameable_universe : self . max_nameable_universe . min ( other. max_nameable_universe ) ,
193+ reachable_placeholders : self . reachable_placeholders . merge ( other. reachable_placeholders ) ,
194+ }
137195 }
138196
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
197+ fn merge_reached ( self , other : Self ) -> Self {
198+ Self {
199+ max_nameable_universe : self . max_nameable_universe . min ( other. max_nameable_universe ) ,
200+ reachable_placeholders : self . reachable_placeholders . merge ( other. reachable_placeholders ) ,
201+ representative : self . representative ,
202+ }
143203 }
144204}
145205
@@ -310,28 +370,52 @@ fn rewrite_placeholder_outlives<'tcx>(
310370
311371 let annotation = annotations[ scc] ;
312372
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- }
373+ let Some ( ( max_u, max_u_rvid) ) = annotation. unnameable_placeholder ( ) else {
374+ continue ;
375+ } ;
376+
377+ debug ! (
378+ "Placeholder universe {max_u:?} is too large for its SCC, represented by {:?}" ,
379+ annotation. representative
380+ ) ;
381+
382+ // We only add one `r: 'static` constraint per SCC, where `r` is the SCC representative.
383+ // That constraint is annotated with some placeholder `unnameable` where
384+ // `unnameable` is unnameable from `r` and there is a path in the constraint graph
385+ // between them.
386+ //
387+ // There is one exception; if some other region in this SCC can't name `'r`, then
388+ // we pick the region with the smallest universe in the SCC, so that a path can
389+ // always start in `'r` to find a motivation that isn't cyclic.
390+ let blame_to = if annotation. representative . rvid ( ) == max_u_rvid {
391+ // Assertion: the region that lowered our universe is an existential one and we are a placeholder!
392+
393+ // The SCC's representative is not nameable from some region
394+ // that ends up in the SCC.
395+ let small_universed_rvid = annotation. max_nameable_universe . 1 ;
396+ debug ! (
397+ "{small_universed_rvid:?} lowered our universe to {:?}" ,
398+ annotation. max_nameable_universe( )
399+ ) ;
400+ small_universed_rvid
401+ } else {
402+ // `max_u_rvid` is not nameable by the SCC's representative.
403+ max_u_rvid
404+ } ;
405+
406+ // FIXME: if we can extract a useful blame span here, future error
407+ // reporting and constraint search can be simplified.
408+
409+ added_constraints = true ;
410+ outlives_constraints. push ( OutlivesConstraint {
411+ sup : annotation. representative . rvid ( ) ,
412+ sub : fr_static,
413+ category : ConstraintCategory :: OutlivesUnnameablePlaceholder ( blame_to) ,
414+ locations : Locations :: All ( rustc_span:: DUMMY_SP ) ,
415+ span : rustc_span:: DUMMY_SP ,
416+ variance_info : VarianceDiagInfo :: None ,
417+ from_closure : false ,
418+ } ) ;
335419 }
336420 added_constraints
337421}
0 commit comments