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
+ use core:: cmp;
4
5
5
6
use rustc_data_structures:: frozen:: Frozen ;
6
7
use rustc_data_structures:: fx:: FxIndexMap ;
@@ -10,7 +11,7 @@ use rustc_index::IndexVec;
10
11
use rustc_infer:: infer:: RegionVariableOrigin ;
11
12
use rustc_middle:: mir:: ConstraintCategory ;
12
13
use rustc_middle:: ty:: { RegionVid , UniverseIndex } ;
13
- use tracing:: debug;
14
+ use tracing:: { debug, trace } ;
14
15
15
16
use crate :: constraints:: { ConstraintSccIndex , OutlivesConstraintSet } ;
16
17
use crate :: consumers:: OutlivesConstraint ;
@@ -64,84 +65,147 @@ impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
64
65
type SccIdx = ConstraintSccIndex ;
65
66
}
66
67
68
+ #[ derive( Copy , Debug , Clone , PartialEq , Eq ) ]
69
+ enum PlaceholderReachability {
70
+ /// This SCC reaches no placeholders.
71
+ NoPlaceholders ,
72
+ /// This SCC reaches at least one placeholder.
73
+ Placeholders {
74
+ /// The largest-universed placeholder we can reach
75
+ max_universe : ( UniverseIndex , RegionVid ) ,
76
+
77
+ /// The placeholder with the smallest ID
78
+ min_placeholder : RegionVid ,
79
+
80
+ /// The placeholder with the largest ID
81
+ max_placeholder : RegionVid ,
82
+ } ,
83
+ }
84
+
85
+ impl PlaceholderReachability {
86
+ /// Merge the reachable placeholders of two graph components.
87
+ fn merge ( self , other : PlaceholderReachability ) -> PlaceholderReachability {
88
+ use PlaceholderReachability :: * ;
89
+ match ( self , other) {
90
+ ( NoPlaceholders , NoPlaceholders ) => NoPlaceholders ,
91
+ ( NoPlaceholders , p @ Placeholders { .. } )
92
+ | ( p @ Placeholders { .. } , NoPlaceholders ) => p,
93
+ (
94
+ Placeholders {
95
+ min_placeholder : min_pl,
96
+ max_placeholder : max_pl,
97
+ max_universe : max_u,
98
+ } ,
99
+ Placeholders { min_placeholder, max_placeholder, max_universe } ,
100
+ ) => Placeholders {
101
+ min_placeholder : cmp:: min ( min_pl, min_placeholder) ,
102
+ max_placeholder : cmp:: max ( max_pl, max_placeholder) ,
103
+ max_universe : cmp:: max ( max_u, max_universe) ,
104
+ } ,
105
+ }
106
+ }
107
+
108
+ fn max_universe ( & self ) -> Option < ( UniverseIndex , RegionVid ) > {
109
+ match self {
110
+ Self :: NoPlaceholders => None ,
111
+ Self :: Placeholders { max_universe, .. } => Some ( * max_universe) ,
112
+ }
113
+ }
114
+
115
+ /// If we have reached placeholders, determine if they can
116
+ /// be named from this universe.
117
+ fn can_be_named_by ( & self , from : UniverseIndex ) -> bool {
118
+ self . max_universe ( )
119
+ . is_none_or ( |( max_placeholder_universe, _) | from. can_name ( max_placeholder_universe) )
120
+ }
121
+ }
122
+
67
123
/// An annotation for region graph SCCs that tracks
68
124
/// the values of its elements. This annotates a single SCC.
69
125
#[ derive( Copy , Debug , Clone ) ]
70
126
pub ( crate ) struct RegionTracker {
71
- /// The largest universe of a placeholder reached from this SCC.
72
- /// This includes placeholders within this SCC.
73
- max_placeholder_universe_reached : UniverseIndex ,
127
+ reachable_placeholders : PlaceholderReachability ,
74
128
75
129
/// The largest universe nameable from this SCC.
76
130
/// It is the smallest nameable universes of all
77
- /// existential regions reachable from it.
78
- max_nameable_universe : UniverseIndex ,
131
+ /// existential regions reachable from it. Small Rvids are preferred.
132
+ max_nameable_universe : ( UniverseIndex , RegionVid ) ,
79
133
80
134
/// The representative Region Variable Id for this SCC.
81
135
pub ( crate ) representative : Representative ,
82
136
}
83
137
84
138
impl RegionTracker {
85
139
pub ( crate ) fn new ( rvid : RegionVid , definition : & RegionDefinition < ' _ > ) -> Self {
86
- let placeholder_universe =
140
+ let reachable_placeholders =
87
141
if matches ! ( definition. origin, NllRegionVariableOrigin :: Placeholder ( _) ) {
88
- definition. universe
142
+ PlaceholderReachability :: Placeholders {
143
+ max_universe : ( definition. universe , rvid) ,
144
+ min_placeholder : rvid,
145
+ max_placeholder : rvid,
146
+ }
89
147
} else {
90
- UniverseIndex :: ROOT
148
+ PlaceholderReachability :: NoPlaceholders
91
149
} ;
92
150
93
151
Self {
94
- max_placeholder_universe_reached : placeholder_universe ,
95
- max_nameable_universe : definition. universe ,
152
+ reachable_placeholders ,
153
+ max_nameable_universe : ( definition. universe , rvid ) ,
96
154
representative : Representative :: new ( rvid, definition) ,
97
155
}
98
156
}
99
157
100
158
/// The largest universe this SCC can name. It's the smallest
101
- /// largest nameable uninverse of any reachable region.
159
+ /// largest nameable universe of any reachable region, or
160
+ /// `max_nameable(r) = min (max_nameable(r') for r' reachable from r)`
102
161
pub ( crate ) fn max_nameable_universe ( self ) -> UniverseIndex {
103
- self . max_nameable_universe
162
+ self . max_nameable_universe . 0
104
163
}
105
164
106
165
pub ( crate ) fn max_placeholder_universe_reached ( self ) -> UniverseIndex {
107
- self . max_placeholder_universe_reached
108
- }
109
-
110
- fn merge_min_max_seen ( & mut self , other : & Self ) {
111
- self . max_placeholder_universe_reached = std:: cmp:: max (
112
- self . max_placeholder_universe_reached ,
113
- other. max_placeholder_universe_reached ,
114
- ) ;
115
-
116
- self . max_nameable_universe =
117
- std:: cmp:: min ( self . max_nameable_universe , other. max_nameable_universe ) ;
118
- }
119
-
120
- /// Returns `true` if during the annotated SCC reaches a placeholder
121
- /// with a universe larger than the smallest nameable universe of any
122
- /// reachable existential region.
123
- pub ( crate ) fn has_incompatible_universes ( & self ) -> bool {
124
- self . max_nameable_universe ( ) . cannot_name ( self . max_placeholder_universe_reached )
166
+ if let Some ( ( universe, _) ) = self . reachable_placeholders . max_universe ( ) {
167
+ universe
168
+ } else {
169
+ UniverseIndex :: ROOT
170
+ }
125
171
}
126
172
127
173
/// Determine if the tracked universes of the two SCCs are compatible.
128
174
pub ( crate ) fn universe_compatible_with ( & self , other : Self ) -> bool {
175
+ // HACK: We first check whether we can name the highest existential universe
176
+ // of `other`. This only exists to avoid errors in case that scc already
177
+ // depends on a placeholder it cannot name itself.
129
178
self . max_nameable_universe ( ) . can_name ( other. max_nameable_universe ( ) )
130
- || self . max_nameable_universe ( ) . can_name ( other. max_placeholder_universe_reached )
179
+ || other. reachable_placeholders . can_be_named_by ( self . max_nameable_universe ( ) )
180
+ }
181
+
182
+ /// If this SCC reaches a placeholder it can't name, return it.
183
+ fn unnameable_placeholder ( & self ) -> Option < ( UniverseIndex , RegionVid ) > {
184
+ self . reachable_placeholders . max_universe ( ) . filter ( |& ( placeholder_universe, _) | {
185
+ !self . max_nameable_universe ( ) . can_name ( placeholder_universe)
186
+ } )
131
187
}
132
188
}
133
189
134
190
impl scc:: Annotation for RegionTracker {
135
- fn merge_scc ( mut self , other : Self ) -> Self {
136
- self . representative = self . representative . merge_scc ( other. representative ) ;
137
- self . merge_min_max_seen ( & other) ;
138
- self
191
+ fn merge_scc ( self , other : Self ) -> Self {
192
+ trace ! ( "{:?} << {:?}" , self . representative, other. representative) ;
193
+
194
+ Self {
195
+ representative : self . representative . merge_scc ( other. representative ) ,
196
+ ..self . merge_reached ( other)
197
+ }
139
198
}
140
199
141
- fn merge_reached ( mut self , other : Self ) -> Self {
142
- // No update to in-component values, only add seen values.
143
- self . merge_min_max_seen ( & other) ;
144
- self
200
+ fn merge_reached ( self , other : Self ) -> Self {
201
+ Self {
202
+ max_nameable_universe : cmp:: min (
203
+ self . max_nameable_universe ,
204
+ other. max_nameable_universe ,
205
+ ) ,
206
+ reachable_placeholders : self . reachable_placeholders . merge ( other. reachable_placeholders ) ,
207
+ representative : self . representative ,
208
+ }
145
209
}
146
210
}
147
211
@@ -314,28 +378,52 @@ fn rewrite_placeholder_outlives<'tcx>(
314
378
315
379
let annotation = annotations[ scc] ;
316
380
317
- // If this SCC participates in a universe violation,
318
- // e.g. if it reaches a region with a universe smaller than
319
- // the largest region reached, add a requirement that it must
320
- // outlive `'static`.
321
- if annotation. has_incompatible_universes ( ) {
322
- // Optimisation opportunity: this will add more constraints than
323
- // needed for correctness, since an SCC upstream of another with
324
- // a universe violation will "infect" its downstream SCCs to also
325
- // outlive static.
326
- let scc_representative_outlives_static = OutlivesConstraint {
327
- sup : annotation. representative . rvid ( ) ,
328
- sub : fr_static,
329
- category : ConstraintCategory :: IllegalUniverse ,
330
- locations : Locations :: All ( rustc_span:: DUMMY_SP ) ,
331
- span : rustc_span:: DUMMY_SP ,
332
- variance_info : VarianceDiagInfo :: None ,
333
- from_closure : false ,
334
- } ;
335
- outlives_constraints. push ( scc_representative_outlives_static) ;
336
- added_constraints = true ;
337
- debug ! ( "Added {:?}: 'static!" , annotation. representative. rvid( ) ) ;
338
- }
381
+ let Some ( ( max_u, max_u_rvid) ) = annotation. unnameable_placeholder ( ) else {
382
+ continue ;
383
+ } ;
384
+
385
+ debug ! (
386
+ "Placeholder universe {max_u:?} is too large for its SCC, represented by {:?}" ,
387
+ annotation. representative
388
+ ) ;
389
+
390
+ // We only add one `r: 'static` constraint per SCC, where `r` is the SCC representative.
391
+ // That constraint is annotated with some placeholder `unnameable` where
392
+ // `unnameable` is unnameable from `r` and there is a path in the constraint graph
393
+ // between them.
394
+ //
395
+ // There is one exception; if some other region in this SCC can't name `'r`, then
396
+ // we pick the region with the smallest universe in the SCC, so that a path can
397
+ // always start in `'r` to find a motivation that isn't cyclic.
398
+ let blame_to = if annotation. representative . rvid ( ) == max_u_rvid {
399
+ // Assertion: the region that lowered our universe is an existential one and we are a placeholder!
400
+
401
+ // The SCC's representative is not nameable from some region
402
+ // that ends up in the SCC.
403
+ let small_universed_rvid = annotation. max_nameable_universe . 1 ;
404
+ debug ! (
405
+ "{small_universed_rvid:?} lowered our universe to {:?}" ,
406
+ annotation. max_nameable_universe( )
407
+ ) ;
408
+ small_universed_rvid
409
+ } else {
410
+ // `max_u_rvid` is not nameable by the SCC's representative.
411
+ max_u_rvid
412
+ } ;
413
+
414
+ // FIXME: if we can extract a useful blame span here, future error
415
+ // reporting and constraint search can be simplified.
416
+
417
+ added_constraints = true ;
418
+ outlives_constraints. push ( OutlivesConstraint {
419
+ sup : annotation. representative . rvid ( ) ,
420
+ sub : fr_static,
421
+ category : ConstraintCategory :: OutlivesUnnameablePlaceholder ( blame_to) ,
422
+ locations : Locations :: All ( rustc_span:: DUMMY_SP ) ,
423
+ span : rustc_span:: DUMMY_SP ,
424
+ variance_info : VarianceDiagInfo :: None ,
425
+ from_closure : false ,
426
+ } ) ;
339
427
}
340
428
added_constraints
341
429
}
0 commit comments