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 ;
@@ -62,84 +63,147 @@ impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
62
63
type SccIdx = ConstraintSccIndex ;
63
64
}
64
65
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
+
65
121
/// An annotation for region graph SCCs that tracks
66
122
/// the values of its elements. This annotates a single SCC.
67
123
#[ derive( Copy , Debug , Clone ) ]
68
124
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 ,
125
+ reachable_placeholders : PlaceholderReachability ,
72
126
73
127
/// The largest universe nameable from this SCC.
74
128
/// 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 ) ,
77
131
78
132
/// The representative Region Variable Id for this SCC.
79
133
pub ( crate ) representative : Representative ,
80
134
}
81
135
82
136
impl RegionTracker {
83
137
pub ( crate ) fn new ( rvid : RegionVid , definition : & RegionDefinition < ' _ > ) -> Self {
84
- let placeholder_universe =
138
+ let reachable_placeholders =
85
139
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
+ }
87
145
} else {
88
- UniverseIndex :: ROOT
146
+ PlaceholderReachability :: NoPlaceholders
89
147
} ;
90
148
91
149
Self {
92
- max_placeholder_universe_reached : placeholder_universe ,
93
- max_nameable_universe : definition. universe ,
150
+ reachable_placeholders ,
151
+ max_nameable_universe : ( definition. universe , rvid ) ,
94
152
representative : Representative :: new ( rvid, definition) ,
95
153
}
96
154
}
97
155
98
156
/// 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)`
100
159
pub ( crate ) fn max_nameable_universe ( self ) -> UniverseIndex {
101
- self . max_nameable_universe
160
+ self . max_nameable_universe . 0
102
161
}
103
162
104
163
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
+ }
123
169
}
124
170
125
171
/// Determine if the tracked universes of the two SCCs are compatible.
126
172
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.
127
176
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
+ } )
129
185
}
130
186
}
131
187
132
188
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
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
+ }
137
196
}
138
197
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
+ }
143
207
}
144
208
}
145
209
@@ -310,28 +374,52 @@ fn rewrite_placeholder_outlives<'tcx>(
310
374
311
375
let annotation = annotations[ scc] ;
312
376
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
+ } ) ;
335
423
}
336
424
added_constraints
337
425
}
0 commit comments