55//! This logic is provisional and should be removed once the trait
66//! solver can handle this kind of constraint.
77
8+ use std:: collections:: VecDeque ;
9+
810use rustc_data_structures:: fx:: FxHashSet ;
911use rustc_data_structures:: graph:: scc:: { self , Sccs } ;
1012use rustc_index:: IndexVec ;
@@ -13,6 +15,7 @@ use rustc_infer::infer::region_constraints::{VarInfos, VerifyBound};
1315use rustc_middle:: ty:: { Region , RegionVid , TyCtxt , UniverseIndex } ;
1416use tracing:: { debug, instrument} ;
1517
18+ use crate :: constraints:: graph:: { ConstraintGraph , Normal } ;
1619use crate :: constraints:: { ConstraintSccIndex , OutlivesConstraintSet } ;
1720use crate :: diagnostics:: RegionErrorKind ;
1821use crate :: member_constraints:: MemberConstraintSet ;
@@ -105,9 +108,15 @@ struct RegionTracker {
105108
106109impl scc:: Annotation for RegionTracker {
107110 fn merge_scc ( self , other : Self ) -> Self {
111+ debug ! ( "{:?} << {:?}" , self . representative, other. representative) ;
112+ let min_universe = if other. min_universe . 0 < self . min_universe . 0 {
113+ other. min_universe
114+ } else {
115+ self . min_universe
116+ } ;
108117 Self {
109118 reachable_placeholders : self . reachable_placeholders . merge ( other. reachable_placeholders ) ,
110- min_universe : self . min_universe . min ( other . min_universe ) ,
119+ min_universe,
111120 representative : self . representative . merge_scc ( other. representative ) ,
112121 }
113122 }
@@ -150,39 +159,6 @@ impl RegionTracker {
150159 self . min_universe . 0
151160 }
152161
153- /// Figure out if there is a universe violation going on.
154- /// This can happen in two cases: either one of our placeholders
155- /// had its universe lowered from reaching a region with a lower universe,
156- /// (in which case we blame the lower universe's region), or because we reached
157- /// a larger universe (in which case we blame the larger universe's region).
158- fn universe_violation ( & self ) -> Option < RegionVid > {
159- let PlaceholderReachability :: Placeholders { max_universe : ( max_u, max_u_rvid) , .. } =
160- self . reachable_placeholders
161- else {
162- return None ;
163- } ;
164-
165- let ( min_u, min_u_rvid) = self . min_universe ;
166-
167- if min_u. can_name ( max_u) {
168- return None ;
169- }
170-
171- debug ! ( "Universe {max_u:?} is too large for its SCC!" ) ;
172- let to_blame = if self . representative . rvid ( ) == max_u_rvid {
173- // We originally had a large enough universe to fit all our reachable
174- // placeholders, but had it lowered because we also reached something
175- // small-universed. In this case, that's to blame!
176- debug ! ( "{min_u_rvid:?} lowered our universe to {min_u:?}" ) ;
177- min_u_rvid
178- } else {
179- // The problem is that we, who have a small universe, reach a large one.
180- max_u_rvid
181- } ;
182-
183- Some ( to_blame)
184- }
185-
186162 /// Determine if this SCC reaches a placeholder that isn't `placeholder_rvid`,
187163 /// returning it if that is the case. This prefers the placeholder with the
188164 /// smallest region variable ID.
@@ -250,6 +226,7 @@ impl scc::Annotations<RegionVid, ConstraintSccIndex, Representative>
250226 }
251227}
252228
229+ #[ instrument( skip( definitions, sccs, annotations) , ret, level = "debug" ) ]
253230fn find_placeholder_mismatch_errors < ' tcx > (
254231 definitions : & IndexVec < RegionVid , RegionDefinition < ' tcx > > ,
255232 sccs : & Sccs < RegionVid , ConstraintSccIndex > ,
@@ -397,7 +374,7 @@ pub(crate) fn rewrite_higher_kinded_outlives_as_constraints<'tcx>(
397374 let sccs = outlives_constraints. compute_sccs ( fr_static, definitions. len ( ) , & mut annotations) ;
398375
399376 let outlives_static =
400- rewrite_outlives ( & sccs, & annotations, fr_static, & mut outlives_constraints) ;
377+ rewrite_outlives ( & sccs, & annotations, fr_static, & mut outlives_constraints, & definitions ) ;
401378
402379 let placeholder_errors = find_placeholder_mismatch_errors ( & definitions, & sccs, & annotations) ;
403380
@@ -467,35 +444,112 @@ fn rewrite_outlives<'tcx>(
467444 annotations : & SccAnnotations < ' _ , ' _ , RegionTracker > ,
468445 fr_static : RegionVid ,
469446 outlives_constraints : & mut OutlivesConstraintSet < ' tcx > ,
447+ definitions : & IndexVec < RegionVid , RegionDefinition < ' tcx > > ,
470448) -> FxHashSet < ConstraintSccIndex > {
471449 // Is this SCC already outliving static directly or transitively?
472450 let mut outlives_static = FxHashSet :: default ( ) ;
473451
452+ let mut memoised_constraint_graph: Option < ConstraintGraph < Normal > > = None ;
453+
474454 for scc in sccs. all_sccs ( ) {
475455 let annotation: RegionTracker = annotations. scc_to_annotation [ scc] ;
476456 if scc == sccs. scc ( fr_static) {
477457 // No use adding 'static: 'static.
478458 continue ;
479459 }
480460
481- // If this SCC participates in a universe violation
482- // e.g. if it reaches a region with a universe smaller than
483- // the largest region reached, add a requirement that it must
484- // outlive `'static`. Here we get to know which reachable region
485- // caused the violation.
486- if let Some ( to) = annotation. universe_violation ( ) {
487- outlives_static. insert ( scc) ;
488- outlives_constraints. add_placeholder_violation_constraint (
489- annotation. representative_rvid ( ) ,
490- annotation. representative_rvid ( ) ,
491- to,
461+ // Figure out if there is a universe violation in this SCC.
462+ // This can happen in two cases: either one of our placeholders
463+ // had its universe lowered from reaching a region with a lower universe,
464+ // (in which case we blame the lower universe's region), or because we reached
465+ // a larger universe (in which case we blame the larger universe's region).
466+ let PlaceholderReachability :: Placeholders { max_universe : ( max_u, max_u_rvid) , .. } =
467+ annotation. reachable_placeholders
468+ else {
469+ continue ;
470+ } ;
471+
472+ let ( min_u, _) = annotation. min_universe ;
473+
474+ if min_u. can_name ( max_u) {
475+ continue ;
476+ }
477+
478+ debug ! ( "Universe {max_u:?} is too large for its SCC!" ) ;
479+ let blame_to = if annotation. representative . rvid ( ) == max_u_rvid {
480+ // We originally had a large enough universe to fit all our reachable
481+ // placeholders, but had it lowered because we also absorbed something
482+ // small-universed. In this case, that's to blame!
483+ let small_universed_rvid = find_region (
484+ outlives_constraints,
485+ memoised_constraint_graph
486+ . get_or_insert_with ( || outlives_constraints. graph ( definitions. len ( ) ) ) ,
487+ definitions,
488+ max_u_rvid,
489+ |r : RegionVid | definitions[ r] . universe == min_u,
492490 fr_static,
493491 ) ;
494- }
492+ debug ! ( "{small_universed_rvid:?} lowered our universe to {min_u:?}" ) ;
493+ small_universed_rvid
494+ } else {
495+ // The problem is that we, who have a small universe, reach a large one.
496+ max_u_rvid
497+ } ;
498+
499+ outlives_static. insert ( scc) ;
500+ outlives_constraints. add_placeholder_violation_constraint (
501+ annotation. representative_rvid ( ) ,
502+ annotation. representative_rvid ( ) ,
503+ blame_to,
504+ fr_static,
505+ ) ;
495506 }
496507 outlives_static
497508}
498509
510+ /// Find a region matching a predicate in a set of constraints, using BFS.
511+ // FIXME(amandasystems) this is at least partially duplicated code to the constraint search in `region_infer`.
512+ // It's probably also very expensive.
513+ fn find_region < ' tcx > (
514+ constraints : & OutlivesConstraintSet < ' tcx > ,
515+ graph : & ConstraintGraph < Normal > ,
516+ definitions : & IndexVec < RegionVid , RegionDefinition < ' tcx > > ,
517+ start_region : RegionVid ,
518+ target_test : impl Fn ( RegionVid ) -> bool ,
519+ fr_static : RegionVid ,
520+ ) -> RegionVid {
521+ #[ derive( Clone , PartialEq , Eq , Debug ) ]
522+ enum Trace {
523+ StartRegion ,
524+ NotVisited ,
525+ Visited ,
526+ }
527+
528+ let mut context = IndexVec :: from_elem ( Trace :: NotVisited , definitions) ;
529+ context[ start_region] = Trace :: StartRegion ;
530+
531+ let mut deque = VecDeque :: new ( ) ;
532+ deque. push_back ( start_region) ;
533+
534+ while let Some ( r) = deque. pop_front ( ) {
535+ if target_test ( r) {
536+ return r;
537+ }
538+
539+ let outgoing_edges_from_graph = graph. outgoing_edges ( r, constraints, Some ( fr_static) ) ;
540+
541+ for constraint in outgoing_edges_from_graph {
542+ debug_assert_eq ! ( constraint. sup, r) ;
543+ let sub_region = constraint. sub ;
544+ if let Trace :: NotVisited = context[ sub_region] {
545+ context[ sub_region] = Trace :: Visited ;
546+ deque. push_back ( sub_region) ;
547+ }
548+ }
549+ }
550+ unreachable ! ( "Should have found something!" ) ;
551+ }
552+
499553#[ instrument( skip( sccs, scc_annotations, universal_regions) , ret) ]
500554fn bound_has_universe_violation < ' t > (
501555 bound : & VerifyBound < ' t > ,
0 commit comments