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
- //! This logic is provisional and should be removed once the trait
6
- //! solver can handle this kind of constraint.
4
+
7
5
use rustc_data_structures:: frozen:: Frozen ;
8
- use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap } ;
6
+ use rustc_data_structures:: fx:: FxIndexMap ;
9
7
use rustc_data_structures:: graph:: scc;
10
8
use rustc_data_structures:: graph:: scc:: Sccs ;
11
9
use rustc_index:: IndexVec ;
10
+ use rustc_infer:: infer:: RegionVariableOrigin ;
12
11
use rustc_middle:: mir:: ConstraintCategory ;
13
12
use rustc_middle:: ty:: { RegionVid , UniverseIndex } ;
14
13
use tracing:: debug;
@@ -18,7 +17,7 @@ use crate::consumers::OutlivesConstraint;
18
17
use crate :: diagnostics:: UniverseInfo ;
19
18
use crate :: member_constraints:: MemberConstraintSet ;
20
19
use crate :: region_infer:: values:: { LivenessValues , PlaceholderIndices } ;
21
- use crate :: region_infer:: { ConstraintSccs , RegionDefinition , TypeTest } ;
20
+ use crate :: region_infer:: { ConstraintSccs , RegionDefinition , Representative , TypeTest } ;
22
21
use crate :: ty:: VarianceDiagInfo ;
23
22
use crate :: type_check:: free_region_relations:: UniversalRegionRelations ;
24
23
use crate :: type_check:: { Locations , MirTypeckRegionConstraints } ;
@@ -32,7 +31,7 @@ pub(crate) struct LoweredConstraints<'tcx> {
32
31
pub ( crate ) definitions : Frozen < IndexVec < RegionVid , RegionDefinition < ' tcx > > > ,
33
32
pub ( crate ) scc_annotations : IndexVec < ConstraintSccIndex , RegionTracker > ,
34
33
pub ( crate ) member_constraints : MemberConstraintSet < ' tcx , RegionVid > ,
35
- pub ( crate ) outlives_constraints : OutlivesConstraintSet < ' tcx > ,
34
+ pub ( crate ) outlives_constraints : Frozen < OutlivesConstraintSet < ' tcx > > ,
36
35
pub ( crate ) type_tests : Vec < TypeTest < ' tcx > > ,
37
36
pub ( crate ) liveness_constraints : LivenessValues ,
38
37
pub ( crate ) universe_causes : FxIndexMap < UniverseIndex , UniverseInfo < ' tcx > > ,
@@ -73,45 +72,35 @@ pub(crate) struct RegionTracker {
73
72
/// This includes placeholders within this SCC.
74
73
max_placeholder_universe_reached : UniverseIndex ,
75
74
76
- /// The smallest universe index reachable form the nodes of this SCC.
77
- min_reachable_universe : UniverseIndex ,
78
-
79
- /// The representative Region Variable Id for this SCC. We prefer
80
- /// placeholders over existentially quantified variables, otherwise
81
- /// it's the one with the smallest Region Variable ID.
82
- pub ( crate ) representative : RegionVid ,
83
-
84
- /// Is the current representative a placeholder?
85
- representative_is_placeholder : bool ,
75
+ /// The largest universe nameable from this SCC.
76
+ /// It is the smallest nameable universes of all
77
+ /// existential regions reachable from it.
78
+ max_nameable_universe : UniverseIndex ,
86
79
87
- /// Is the current representative existentially quantified?
88
- representative_is_existential : bool ,
80
+ /// The representative Region Variable Id for this SCC.
81
+ pub ( crate ) representative : Representative ,
89
82
}
90
83
91
84
impl RegionTracker {
92
85
pub ( crate ) fn new ( rvid : RegionVid , definition : & RegionDefinition < ' _ > ) -> Self {
93
- let ( representative_is_placeholder, representative_is_existential) = match definition. origin
94
- {
95
- NllRegionVariableOrigin :: FreeRegion => ( false , false ) ,
96
- NllRegionVariableOrigin :: Placeholder ( _) => ( true , false ) ,
97
- NllRegionVariableOrigin :: Existential { .. } => ( false , true ) ,
98
- } ;
99
-
100
86
let placeholder_universe =
101
- if representative_is_placeholder { definition. universe } else { UniverseIndex :: ROOT } ;
87
+ if matches ! ( definition. origin, NllRegionVariableOrigin :: Placeholder ( _) ) {
88
+ definition. universe
89
+ } else {
90
+ UniverseIndex :: ROOT
91
+ } ;
102
92
103
93
Self {
104
94
max_placeholder_universe_reached : placeholder_universe,
105
- min_reachable_universe : definition. universe ,
106
- representative : rvid,
107
- representative_is_placeholder,
108
- representative_is_existential,
95
+ max_nameable_universe : definition. universe ,
96
+ representative : Representative :: new ( rvid, definition) ,
109
97
}
110
98
}
111
99
112
- /// The smallest-indexed universe reachable from and/or in this SCC.
113
- pub ( crate ) fn min_universe ( self ) -> UniverseIndex {
114
- self . min_reachable_universe
100
+ /// The largest universe this SCC can name. It's the smallest
101
+ /// largest nameable uninverse of any reachable region.
102
+ pub ( crate ) fn max_nameable_universe ( self ) -> UniverseIndex {
103
+ self . max_nameable_universe
115
104
}
116
105
117
106
fn merge_min_max_seen ( & mut self , other : & Self ) {
@@ -120,40 +109,29 @@ impl RegionTracker {
120
109
other. max_placeholder_universe_reached ,
121
110
) ;
122
111
123
- self . min_reachable_universe =
124
- std:: cmp:: min ( self . min_reachable_universe , other. min_reachable_universe ) ;
112
+ self . max_nameable_universe =
113
+ std:: cmp:: min ( self . max_nameable_universe , other. max_nameable_universe ) ;
125
114
}
126
115
127
116
/// Returns `true` if during the annotated SCC reaches a placeholder
128
- /// with a universe larger than the smallest reachable one, `false` otherwise.
117
+ /// with a universe larger than the smallest nameable universe of any
118
+ /// reachable existential region.
129
119
pub ( crate ) fn has_incompatible_universes ( & self ) -> bool {
130
- self . min_universe ( ) . cannot_name ( self . max_placeholder_universe_reached )
120
+ self . max_nameable_universe ( ) . cannot_name ( self . max_placeholder_universe_reached )
131
121
}
132
122
133
- /// Determine if the tracked universes of the two SCCs
134
- /// are compatible.
123
+ /// Determine if the tracked universes of the two SCCs are compatible.
135
124
pub ( crate ) fn universe_compatible_with ( & self , other : Self ) -> bool {
136
- self . min_universe ( ) . can_name ( other. min_universe ( ) )
137
- || self . min_universe ( ) . can_name ( other. max_placeholder_universe_reached )
125
+ self . max_nameable_universe ( ) . can_name ( other. max_nameable_universe ( ) )
126
+ || self . max_nameable_universe ( ) . can_name ( other. max_placeholder_universe_reached )
138
127
}
139
128
}
140
129
141
130
impl scc:: Annotation for RegionTracker {
142
- fn merge_scc ( mut self , mut other : Self ) -> Self {
143
- // Prefer any placeholder over any existential
144
- if other. representative_is_placeholder && self . representative_is_existential {
145
- other. merge_min_max_seen ( & self ) ;
146
- return other;
147
- }
148
-
149
- if self . representative_is_placeholder && other. representative_is_existential
150
- || ( self . representative <= other. representative )
151
- {
152
- self . merge_min_max_seen ( & other) ;
153
- return self ;
154
- }
155
- other. merge_min_max_seen ( & self ) ;
156
- other
131
+ fn merge_scc ( mut self , other : Self ) -> Self {
132
+ self . representative = self . representative . merge_scc ( other. representative ) ;
133
+ self . merge_min_max_seen ( & other) ;
134
+ self
157
135
}
158
136
159
137
fn merge_reached ( mut self , other : Self ) -> Self {
@@ -164,7 +142,7 @@ impl scc::Annotation for RegionTracker {
164
142
}
165
143
166
144
/// Determines if the region variable definitions contain
167
- /// placeholers , and compute them for later use.
145
+ /// placeholders , and compute them for later use.
168
146
fn region_definitions < ' tcx > (
169
147
universal_regions : & UniversalRegions < ' tcx > ,
170
148
infcx : & BorrowckInferCtxt < ' tcx > ,
@@ -177,42 +155,39 @@ fn region_definitions<'tcx>(
177
155
let mut has_placeholders = false ;
178
156
179
157
for info in var_infos. iter ( ) {
180
- let definition = RegionDefinition :: new ( info) ;
181
- has_placeholders |= matches ! ( definition. origin, NllRegionVariableOrigin :: Placeholder ( _) ) ;
158
+ let origin = match info. origin {
159
+ RegionVariableOrigin :: Nll ( origin) => origin,
160
+ _ => NllRegionVariableOrigin :: Existential { from_forall : false } ,
161
+ } ;
162
+
163
+ let definition = RegionDefinition { origin, universe : info. universe , external_name : None } ;
164
+
165
+ has_placeholders |= matches ! ( origin, NllRegionVariableOrigin :: Placeholder ( _) ) ;
182
166
definitions. push ( definition) ;
183
167
}
184
168
185
169
// Add external names from universal regions in fun function definitions.
170
+ // FIXME: this two-step method is annoying, but I don't know how to avoid it.
186
171
for ( external_name, variable) in universal_regions. named_universal_regions_iter ( ) {
187
172
debug ! ( "region {:?} has external name {:?}" , variable, external_name) ;
188
173
definitions[ variable] . external_name = Some ( external_name) ;
189
174
}
190
175
( Frozen :: freeze ( definitions) , has_placeholders)
191
176
}
192
177
193
- /// This method handles Universe errors by rewriting the constraint
178
+ /// This method handles placeholders by rewriting the constraint
194
179
/// graph. For each strongly connected component in the constraint
195
180
/// graph such that there is a series of constraints
196
181
/// A: B: C: ... : X where
197
- /// A's universe is smaller than X's and A is a placeholder ,
182
+ /// A contains a placeholder whose universe cannot be named by X ,
198
183
/// add a constraint that A: 'static. This is a safe upper bound
199
184
/// in the face of borrow checker/trait solver limitations that will
200
185
/// eventually go away.
201
186
///
202
187
/// For a more precise definition, see the documentation for
203
- /// [`RegionTracker`] and its methods!.
204
- ///
205
- /// Since universes can also be involved in errors (if one placeholder
206
- /// transitively outlives another), this function also flags those.
207
- ///
208
- /// Additionally, it similarly rewrites type-tests.
209
- ///
210
- /// This edge case used to be handled during constraint propagation
211
- /// by iterating over the strongly connected components in the constraint
212
- /// graph while maintaining a set of bookkeeping mappings similar
213
- /// to what is stored in `RegionTracker` and manually adding 'sttaic as
214
- /// needed.
188
+ /// [`RegionTracker`] and its methods!
215
189
///
190
+ /// This edge case used to be handled during constraint propagation.
216
191
/// It was rewritten as part of the Polonius project with the goal of moving
217
192
/// higher-kindedness concerns out of the path of the borrow checker,
218
193
/// for two reasons:
@@ -228,7 +203,7 @@ fn region_definitions<'tcx>(
228
203
/// This code is a stop-gap measure in preparation for the future trait solver.
229
204
///
230
205
/// Every constraint added by this method is an internal `IllegalUniverse` constraint.
231
- pub ( crate ) fn rewrite_higher_kinded_outlives_as_constraints < ' tcx > (
206
+ pub ( crate ) fn compute_sccs_applying_placeholder_outlives_constraints < ' tcx > (
232
207
constraints : MirTypeckRegionConstraints < ' tcx > ,
233
208
universal_region_relations : & Frozen < UniversalRegionRelations < ' tcx > > ,
234
209
infcx : & BorrowckInferCtxt < ' tcx > ,
@@ -267,36 +242,37 @@ pub(crate) fn rewrite_higher_kinded_outlives_as_constraints<'tcx>(
267
242
)
268
243
} ;
269
244
245
+ let mut scc_annotations = SccAnnotations :: init ( & definitions) ;
246
+ let constraint_sccs = compute_sccs ( & outlives_constraints, & mut scc_annotations) ;
247
+
270
248
// This code structure is a bit convoluted because it allows for a planned
271
249
// future change where the early return here has a different type of annotation
272
250
// that does much less work.
273
251
if !has_placeholders {
274
252
debug ! ( "No placeholder regions found; skipping rewriting logic!" ) ;
275
- let mut scc_annotations = SccAnnotations :: init ( & definitions) ;
276
- let constraint_sccs = compute_sccs ( & outlives_constraints, & mut scc_annotations) ;
277
253
278
254
return LoweredConstraints {
279
255
type_tests,
280
256
member_constraints,
281
257
constraint_sccs,
282
258
scc_annotations : scc_annotations. scc_to_annotation ,
283
259
definitions,
284
- outlives_constraints,
260
+ outlives_constraints : Frozen :: freeze ( outlives_constraints ) ,
285
261
liveness_constraints,
286
262
universe_causes,
287
263
placeholder_indices,
288
264
} ;
289
265
}
290
266
debug ! ( "Placeholders present; activating placeholder handling logic!" ) ;
291
267
292
- let mut annotations = SccAnnotations :: init ( & definitions) ;
293
- let sccs = compute_sccs ( & outlives_constraints, & mut annotations) ;
294
-
295
- let outlives_static =
296
- rewrite_outlives ( & sccs, & annotations, fr_static, & mut outlives_constraints) ;
268
+ let added_constraints = rewrite_placeholder_outlives (
269
+ & constraint_sccs,
270
+ & scc_annotations,
271
+ fr_static,
272
+ & mut outlives_constraints,
273
+ ) ;
297
274
298
- let ( sccs, scc_annotations) = if !outlives_static. is_empty ( ) {
299
- debug ! ( "The following SCCs had :'static constraints added: {:?}" , outlives_static) ;
275
+ let ( constraint_sccs, scc_annotations) = if added_constraints {
300
276
let mut annotations = SccAnnotations :: init ( & definitions) ;
301
277
302
278
// We changed the constraint set and so must recompute SCCs.
@@ -307,31 +283,31 @@ pub(crate) fn rewrite_higher_kinded_outlives_as_constraints<'tcx>(
307
283
} else {
308
284
// If we didn't add any back-edges; no more work needs doing
309
285
debug ! ( "No constraints rewritten!" ) ;
310
- ( sccs , annotations . scc_to_annotation )
286
+ ( constraint_sccs , scc_annotations . scc_to_annotation )
311
287
} ;
312
288
313
289
LoweredConstraints {
314
- constraint_sccs : sccs ,
290
+ constraint_sccs,
315
291
definitions,
316
292
scc_annotations,
317
293
member_constraints,
318
- outlives_constraints,
294
+ outlives_constraints : Frozen :: freeze ( outlives_constraints ) ,
319
295
type_tests,
320
296
liveness_constraints,
321
297
universe_causes,
322
298
placeholder_indices,
323
299
}
324
300
}
325
301
326
- fn rewrite_outlives < ' tcx > (
302
+ fn rewrite_placeholder_outlives < ' tcx > (
327
303
sccs : & Sccs < RegionVid , ConstraintSccIndex > ,
328
304
annotations : & SccAnnotations < ' _ , ' _ , RegionTracker > ,
329
305
fr_static : RegionVid ,
330
306
outlives_constraints : & mut OutlivesConstraintSet < ' tcx > ,
331
- ) -> FxHashSet < ConstraintSccIndex > {
332
- // Changed to `true` if we added any constraints to `self` and need to
307
+ ) -> bool {
308
+ // Changed to `true` if we added any constraints and need to
333
309
// recompute SCCs.
334
- let mut outlives_static = FxHashSet :: default ( ) ;
310
+ let mut added_constraints = false ;
335
311
336
312
let annotations = & annotations. scc_to_annotation ;
337
313
@@ -354,9 +330,8 @@ fn rewrite_outlives<'tcx>(
354
330
// needed for correctness, since an SCC upstream of another with
355
331
// a universe violation will "infect" its downstream SCCs to also
356
332
// outlive static.
357
- outlives_static. insert ( scc) ;
358
333
let scc_representative_outlives_static = OutlivesConstraint {
359
- sup : annotation. representative ,
334
+ sup : annotation. representative . rvid ( ) ,
360
335
sub : fr_static,
361
336
category : ConstraintCategory :: IllegalUniverse ,
362
337
locations : Locations :: All ( rustc_span:: DUMMY_SP ) ,
@@ -365,7 +340,9 @@ fn rewrite_outlives<'tcx>(
365
340
from_closure : false ,
366
341
} ;
367
342
outlives_constraints. push ( scc_representative_outlives_static) ;
343
+ added_constraints = true ;
344
+ debug ! ( "Added {:?}: 'static!" , annotation. representative. rvid( ) ) ;
368
345
}
369
346
}
370
- outlives_static
347
+ added_constraints
371
348
}
0 commit comments