1- use rustc_middle:: mir:: { Body , Location } ;
1+ use rustc_data_structures:: fx:: FxHashSet ;
2+ use rustc_middle:: mir:: { Body , Location , Statement , StatementKind , Terminator , TerminatorKind } ;
3+ use rustc_middle:: ty:: { TyCtxt , TypeVisitable } ;
4+ use rustc_mir_dataflow:: points:: PointIndex ;
25
36use super :: { LocalizedOutlivesConstraint , LocalizedOutlivesConstraintSet } ;
47use crate :: constraints:: OutlivesConstraint ;
58use crate :: region_infer:: values:: LivenessValues ;
69use crate :: type_check:: Locations ;
10+ use crate :: universal_regions:: UniversalRegions ;
711
812/// Propagate loans throughout the subset graph at a given point (with some subtleties around the
913/// location where effects start to be visible).
1014pub ( super ) fn convert_typeck_constraints < ' tcx > (
15+ tcx : TyCtxt < ' tcx > ,
1116 body : & Body < ' tcx > ,
1217 liveness : & LivenessValues ,
1318 outlives_constraints : impl Iterator < Item = OutlivesConstraint < ' tcx > > ,
19+ universal_regions : & UniversalRegions < ' tcx > ,
1420 localized_outlives_constraints : & mut LocalizedOutlivesConstraintSet ,
1521) {
1622 for outlives_constraint in outlives_constraints {
@@ -35,7 +41,191 @@ pub(super) fn convert_typeck_constraints<'tcx>(
3541 }
3642 }
3743
38- _ => { }
44+ Locations :: Single ( location) => {
45+ // This constraint is marked as holding at one location, we localize it to that
46+ // location or its successor, depending on the corresponding MIR
47+ // statement/terminator. Unfortunately, they all show up from typeck as coming "on
48+ // entry", so for now we modify them to take effects that should apply "on exit"
49+ // into account.
50+ //
51+ // FIXME: this approach is subtle, complicated, and hard to test, so we should track
52+ // this information better in MIR typeck instead, for example with a new `Locations`
53+ // variant that contains which node is crossing over between entry and exit.
54+ let point = liveness. point_from_location ( location) ;
55+ let ( from, to) = if let Some ( stmt) =
56+ body[ location. block ] . statements . get ( location. statement_index )
57+ {
58+ localize_statement_constraint (
59+ tcx,
60+ body,
61+ stmt,
62+ liveness,
63+ & outlives_constraint,
64+ location,
65+ point,
66+ universal_regions,
67+ )
68+ } else {
69+ assert_eq ! ( location. statement_index, body[ location. block] . statements. len( ) ) ;
70+ let terminator = body[ location. block ] . terminator ( ) ;
71+ localize_terminator_constraint (
72+ tcx,
73+ body,
74+ terminator,
75+ liveness,
76+ & outlives_constraint,
77+ point,
78+ universal_regions,
79+ )
80+ } ;
81+ localized_outlives_constraints. push ( LocalizedOutlivesConstraint {
82+ source : outlives_constraint. sup ,
83+ from,
84+ target : outlives_constraint. sub ,
85+ to,
86+ } ) ;
87+ }
88+ }
89+ }
90+ }
91+
92+ /// For a given outlives constraint arising from a MIR statement, computes the CFG `from`-`to`
93+ /// intra-block nodes to localize the constraint.
94+ fn localize_statement_constraint < ' tcx > (
95+ tcx : TyCtxt < ' tcx > ,
96+ body : & Body < ' tcx > ,
97+ stmt : & Statement < ' tcx > ,
98+ liveness : & LivenessValues ,
99+ outlives_constraint : & OutlivesConstraint < ' tcx > ,
100+ current_location : Location ,
101+ current_point : PointIndex ,
102+ universal_regions : & UniversalRegions < ' tcx > ,
103+ ) -> ( PointIndex , PointIndex ) {
104+ match & stmt. kind {
105+ StatementKind :: Assign ( box ( lhs, rhs) ) => {
106+ // To create localized outlives constraints without midpoints, we rely on the property
107+ // that no input regions from the RHS of the assignment will flow into themselves: they
108+ // should not appear in the output regions in the LHS. We believe this to be true by
109+ // construction of the MIR, via temporaries, and assert it here.
110+ //
111+ // We think we don't need midpoints because:
112+ // - every LHS Place has a unique set of regions that don't appear elsewhere
113+ // - this implies that for them to be part of the RHS, the same Place must be read and
114+ // written
115+ // - and that should be impossible in MIR
116+ //
117+ // When we have a more complete implementation in the future, tested with crater, etc,
118+ // we can relax this to a debug assert instead, or remove it.
119+ assert ! (
120+ {
121+ let mut lhs_regions = FxHashSet :: default ( ) ;
122+ tcx. for_each_free_region( lhs, |region| {
123+ let region = universal_regions. to_region_vid( region) ;
124+ lhs_regions. insert( region) ;
125+ } ) ;
126+
127+ let mut rhs_regions = FxHashSet :: default ( ) ;
128+ tcx. for_each_free_region( rhs, |region| {
129+ let region = universal_regions. to_region_vid( region) ;
130+ rhs_regions. insert( region) ;
131+ } ) ;
132+
133+ // The intersection between LHS and RHS regions should be empty.
134+ lhs_regions. is_disjoint( & rhs_regions)
135+ } ,
136+ "there should be no common regions between the LHS and RHS of an assignment"
137+ ) ;
138+
139+ // As mentioned earlier, we should be tracking these better upstream but: we want to
140+ // relate the types on entry to the type of the place on exit. That is, outlives
141+ // constraints on the RHS are on entry, and outlives constraints to/from the LHS are on
142+ // exit (i.e. on entry to the successor location).
143+ let lhs_ty = body. local_decls [ lhs. local ] . ty ;
144+ let successor_location = Location {
145+ block : current_location. block ,
146+ statement_index : current_location. statement_index + 1 ,
147+ } ;
148+ let successor_point = liveness. point_from_location ( successor_location) ;
149+ compute_constraint_direction (
150+ tcx,
151+ outlives_constraint,
152+ & lhs_ty,
153+ current_point,
154+ successor_point,
155+ universal_regions,
156+ )
157+ }
158+ _ => {
159+ // For the other cases, we localize an outlives constraint to where it arises.
160+ ( current_point, current_point)
39161 }
40162 }
41163}
164+
165+ /// For a given outlives constraint arising from a MIR terminator, computes the CFG `from`-`to`
166+ /// inter-block nodes to localize the constraint.
167+ fn localize_terminator_constraint < ' tcx > (
168+ tcx : TyCtxt < ' tcx > ,
169+ body : & Body < ' tcx > ,
170+ terminator : & Terminator < ' tcx > ,
171+ liveness : & LivenessValues ,
172+ outlives_constraint : & OutlivesConstraint < ' tcx > ,
173+ current_point : PointIndex ,
174+ universal_regions : & UniversalRegions < ' tcx > ,
175+ ) -> ( PointIndex , PointIndex ) {
176+ // FIXME: check if other terminators need the same handling as `Call`s, in particular
177+ // Assert/Yield/Drop. A handful of tests are failing with Drop related issues, as well as some
178+ // coroutine tests, and that may be why.
179+ match & terminator. kind {
180+ // FIXME: also handle diverging calls.
181+ TerminatorKind :: Call { destination, target : Some ( target) , .. } => {
182+ // Calls are similar to assignments, and thus follow the same pattern. If there is a
183+ // target for the call we also relate what flows into the destination here to entry to
184+ // that successor.
185+ let destination_ty = destination. ty ( & body. local_decls , tcx) ;
186+ let successor_location = Location { block : * target, statement_index : 0 } ;
187+ let successor_point = liveness. point_from_location ( successor_location) ;
188+ compute_constraint_direction (
189+ tcx,
190+ outlives_constraint,
191+ & destination_ty,
192+ current_point,
193+ successor_point,
194+ universal_regions,
195+ )
196+ }
197+ _ => {
198+ // Typeck constraints guide loans between regions at the current point, so we do that in
199+ // the general case, and liveness will take care of making them flow to the terminator's
200+ // successors.
201+ ( current_point, current_point)
202+ }
203+ }
204+ }
205+
206+ /// For a given constraint, returns the `from`-`to` edge according to whether the constraint flows
207+ /// to or from a free region in the given `value`, some kind of result for an effectful operation,
208+ /// like the LHS of an assignment.
209+ fn compute_constraint_direction < ' tcx > (
210+ tcx : TyCtxt < ' tcx > ,
211+ outlives_constraint : & OutlivesConstraint < ' tcx > ,
212+ value : & impl TypeVisitable < TyCtxt < ' tcx > > ,
213+ current_point : PointIndex ,
214+ successor_point : PointIndex ,
215+ universal_regions : & UniversalRegions < ' tcx > ,
216+ ) -> ( PointIndex , PointIndex ) {
217+ let mut to = current_point;
218+ let mut from = current_point;
219+ tcx. for_each_free_region ( value, |region| {
220+ let region = universal_regions. to_region_vid ( region) ;
221+ if region == outlives_constraint. sub {
222+ // This constraint flows into the result, its effects start becoming visible on exit.
223+ to = successor_point;
224+ } else if region == outlives_constraint. sup {
225+ // This constraint flows from the result, its effects start becoming visible on exit.
226+ from = successor_point;
227+ }
228+ } ) ;
229+
230+ ( from, to)
231+ }
0 commit comments