1
1
//! Logic for lowering higher-kinded outlives constraints
2
2
//! (with placeholders and universes) and turn them into regular
3
3
//! outlives constraints.
4
-
5
4
use rustc_data_structures:: frozen:: Frozen ;
6
5
use rustc_data_structures:: fx:: FxIndexMap ;
7
6
use rustc_data_structures:: graph:: scc;
@@ -10,7 +9,7 @@ use rustc_index::IndexVec;
10
9
use rustc_infer:: infer:: RegionVariableOrigin ;
11
10
use rustc_middle:: mir:: ConstraintCategory ;
12
11
use rustc_middle:: ty:: { RegionVid , UniverseIndex } ;
13
- use tracing:: debug;
12
+ use tracing:: { debug, trace } ;
14
13
15
14
use crate :: constraints:: { ConstraintSccIndex , OutlivesConstraintSet } ;
16
15
use crate :: consumers:: OutlivesConstraint ;
@@ -62,84 +61,145 @@ impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
62
61
type SccIdx = ConstraintSccIndex ;
63
62
}
64
63
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
+
65
119
/// An annotation for region graph SCCs that tracks
66
120
/// the values of its elements. This annotates a single SCC.
67
121
#[ derive( Copy , Debug , Clone ) ]
68
122
pub ( 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 ,
72
124
73
125
/// The largest universe nameable from this SCC.
74
126
/// 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 ) ,
77
129
78
130
/// The representative Region Variable Id for this SCC.
79
131
pub ( crate ) representative : Representative ,
80
132
}
81
133
82
134
impl RegionTracker {
83
135
pub ( crate ) fn new ( rvid : RegionVid , definition : & RegionDefinition < ' _ > ) -> Self {
84
- let placeholder_universe =
136
+ let reachable_placeholders =
85
137
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
+ }
87
143
} else {
88
- UniverseIndex :: ROOT
144
+ PlaceholderReachability :: NoPlaceholders
89
145
} ;
90
146
91
147
Self {
92
- max_placeholder_universe_reached : placeholder_universe ,
93
- max_nameable_universe : definition. universe ,
148
+ reachable_placeholders ,
149
+ max_nameable_universe : ( definition. universe , rvid ) ,
94
150
representative : Representative :: new ( rvid, definition) ,
95
151
}
96
152
}
97
153
98
154
/// 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)`
100
157
pub ( crate ) fn max_nameable_universe ( self ) -> UniverseIndex {
101
- self . max_nameable_universe
158
+ self . max_nameable_universe . 0
102
159
}
103
160
104
161
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
+ }
123
167
}
124
168
125
169
/// Determine if the tracked universes of the two SCCs are compatible.
126
170
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.
127
174
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
+ } )
129
183
}
130
184
}
131
185
132
186
impl 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
+ }
137
195
}
138
196
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
+ }
143
203
}
144
204
}
145
205
@@ -310,28 +370,52 @@ fn rewrite_placeholder_outlives<'tcx>(
310
370
311
371
let annotation = annotations[ scc] ;
312
372
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
+ } ) ;
335
419
}
336
420
added_constraints
337
421
}
0 commit comments