@@ -17,7 +17,7 @@ use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
17
17
use rustc:: infer:: InferCtxt ;
18
18
use rustc:: mir:: { self , Location , Mir , Place , Rvalue , StatementKind , TerminatorKind } ;
19
19
use rustc:: ty:: RegionVid ;
20
- use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
20
+ use rustc_data_structures:: fx:: FxHashSet ;
21
21
use rustc_data_structures:: indexed_vec:: IndexVec ;
22
22
use std:: fmt;
23
23
use syntax_pos:: Span ;
@@ -48,140 +48,107 @@ impl fmt::Display for ConstraintCategory {
48
48
}
49
49
50
50
impl < ' tcx > RegionInferenceContext < ' tcx > {
51
- /// When reporting an error, it is useful to be able to determine which constraints influenced
52
- /// the region being reported as an error. This function finds all of the paths from the
51
+ /// Walks the graph of constraints (where `'a: 'b` is considered
52
+ /// an edge `'b -> 'a`) to find all paths from `from_region` to
53
+ /// `to_region`. The paths are accumulated into the vector
54
+ /// `results`. The paths are stored as a series of
55
+ /// `ConstraintIndex` values -- in other words, a list of *edges*.
56
+ ///
57
+ /// # Parameters
58
+ ///
59
+ /// - `from_region`
60
+ /// When reporting an error, it is useful to be able to determine
61
+ /// which constraints influenced the region being reported as an
62
+ /// error. This function finds all of the paths from the
53
63
/// constraint.
54
- fn find_constraint_paths_from_region ( & self , r0 : RegionVid ) -> Vec < Vec < ConstraintIndex > > {
55
- let constraints = self . constraints . clone ( ) ;
56
-
57
- // Mapping of regions to the previous region and constraint index that led to it.
58
- let mut previous = FxHashMap ( ) ;
59
- // Regions yet to be visited.
60
- let mut next = vec ! [ r0] ;
61
- // Regions that have been visited.
62
- let mut visited = FxHashSet ( ) ;
63
- // Ends of paths.
64
- let mut end_regions = FxHashSet ( ) ;
65
-
66
- // When we've still got points to visit...
67
- while let Some ( current) = next. pop ( ) {
68
- // ...take the next point...
69
- debug ! (
70
- "find_constraint_paths_from_region: current={:?} visited={:?} next={:?}" ,
71
- current, visited, next
72
- ) ;
73
- // ...but make sure not to visit this point again...
74
- visited. insert ( current) ;
75
-
76
- // ...find the edges containing it...
77
- let mut upcoming = Vec :: new ( ) ;
78
- for ( index, constraint) in constraints. iter_enumerated ( ) {
79
- if constraint. sub == current {
80
- // ...add the regions that join us with to the path we've taken...
81
- debug ! (
82
- "find_constraint_paths_from_region: index={:?} constraint={:?}" ,
83
- index, constraint
84
- ) ;
85
- let next_region = constraint. sup . clone ( ) ;
86
-
87
- // ...unless we've visited it since this was added...
88
- if visited. contains ( & next_region) {
89
- debug ! ( "find_constraint_paths_from_region: skipping as visited" ) ;
90
- continue ;
91
- }
64
+ fn find_constraint_paths_between_regions (
65
+ & self ,
66
+ from_region : RegionVid ,
67
+ to_region : RegionVid ,
68
+ ) -> Vec < Vec < ConstraintIndex > > {
69
+ let mut results = vec ! [ ] ;
70
+ self . find_constraint_paths_between_regions_helper (
71
+ from_region,
72
+ from_region,
73
+ to_region,
74
+ & mut FxHashSet :: default ( ) ,
75
+ & mut vec ! [ ] ,
76
+ & mut results,
77
+ ) ;
78
+ results
79
+ }
92
80
93
- previous. insert ( next_region, ( index, Some ( current) ) ) ;
94
- upcoming. push ( next_region) ;
95
- }
96
- }
81
+ /// Helper for `find_constraint_paths_between_regions`.
82
+ fn find_constraint_paths_between_regions_helper (
83
+ & self ,
84
+ from_region : RegionVid ,
85
+ current_region : RegionVid ,
86
+ to_region : RegionVid ,
87
+ visited : & mut FxHashSet < RegionVid > ,
88
+ stack : & mut Vec < ConstraintIndex > ,
89
+ results : & mut Vec < Vec < ConstraintIndex > > ,
90
+ ) {
91
+ let dependency_map = self . dependency_map . as_ref ( ) . unwrap ( ) ;
97
92
98
- if upcoming. is_empty ( ) {
99
- // If we didn't find any edges then this is the end of a path...
100
- debug ! (
101
- "find_constraint_paths_from_region: new end region current={:?}" ,
102
- current
103
- ) ;
104
- end_regions. insert ( current) ;
105
- } else {
106
- // ...but, if we did find edges, then add these to the regions yet to visit.
107
- debug ! (
108
- "find_constraint_paths_from_region: extend next upcoming={:?}" ,
109
- upcoming
110
- ) ;
111
- next. extend ( upcoming) ;
112
- }
93
+ // Check if we already visited this region.
94
+ if !visited. insert ( current_region) {
95
+ return ;
113
96
}
114
97
115
- // Now we've visited each point, compute the final paths.
116
- let mut paths: Vec < Vec < ConstraintIndex > > = Vec :: new ( ) ;
117
- debug ! (
118
- "find_constraint_paths_from_region: end_regions={:?}" ,
119
- end_regions
120
- ) ;
121
- for end_region in end_regions {
122
- debug ! (
123
- "find_constraint_paths_from_region: end_region={:?}" ,
124
- end_region
125
- ) ;
98
+ // Check if we reached the region we were looking for.
99
+ if current_region == to_region {
100
+ // Unless we started out searching for `'a ~> 'a`, which shouldn't have caused
101
+ // en error, then we must have traversed at least *some* constraint:
102
+ assert ! ( !stack. is_empty( ) ) ;
126
103
127
- // Get the constraint and region that led to this end point.
128
- // We can unwrap as we know if end_point was in the vector that it
129
- // must also be in our previous map.
130
- let ( mut index, mut region) = previous. get ( & end_region) . unwrap ( ) ;
131
- debug ! (
132
- "find_constraint_paths_from_region: index={:?} region={:?}" ,
133
- index, region
134
- ) ;
135
-
136
- // Keep track of the indices.
137
- let mut path: Vec < ConstraintIndex > = vec ! [ index] ;
138
-
139
- while region. is_some ( ) && region != Some ( r0) {
140
- let p = previous. get ( & region. unwrap ( ) ) . unwrap ( ) ;
141
- index = p. 0 ;
142
- region = p. 1 ;
104
+ // The first constraint should be like `X: from_region`.
105
+ assert_eq ! ( self . constraints[ stack[ 0 ] ] . sub, from_region) ;
143
106
144
- debug ! (
145
- "find_constraint_paths_from_region: index={:?} region={:?}" ,
146
- index, region
147
- ) ;
148
- path. push ( index) ;
149
- }
107
+ // The last constraint should be like `to_region: Y`.
108
+ assert_eq ! ( self . constraints[ * stack. last( ) . unwrap( ) ] . sup, to_region) ;
150
109
151
- // Add to our paths.
152
- paths . push ( path ) ;
110
+ results . push ( stack . clone ( ) ) ;
111
+ return ;
153
112
}
154
113
155
- debug ! ( "find_constraint_paths_from_region: paths={:?}" , paths) ;
156
- paths
114
+ self . constraints
115
+ . each_affected_by_dirty ( dependency_map[ current_region] , |constraint| {
116
+ assert_eq ! ( self . constraints[ constraint] . sub, current_region) ;
117
+ stack. push ( constraint) ;
118
+ self . find_constraint_paths_between_regions_helper (
119
+ from_region,
120
+ self . constraints [ constraint] . sup ,
121
+ to_region,
122
+ visited,
123
+ stack,
124
+ results,
125
+ ) ;
126
+ stack. pop ( ) ;
127
+ } ) ;
157
128
}
158
129
159
130
/// This function will return true if a constraint is interesting and false if a constraint
160
131
/// is not. It is useful in filtering constraint paths to only interesting points.
161
- fn constraint_is_interesting ( & self , index : & ConstraintIndex ) -> bool {
162
- self . constraints
163
- . get ( * index)
164
- . filter ( |constraint| {
165
- debug ! (
166
- "constraint_is_interesting: locations={:?} constraint={:?}" ,
167
- constraint. locations, constraint
168
- ) ;
169
- if let Locations :: Interesting ( _) = constraint. locations {
170
- true
171
- } else {
172
- false
173
- }
174
- } )
175
- . is_some ( )
132
+ fn constraint_is_interesting ( & self , index : ConstraintIndex ) -> bool {
133
+ let constraint = self . constraints [ index] ;
134
+ debug ! (
135
+ "constraint_is_interesting: locations={:?} constraint={:?}" ,
136
+ constraint. locations, constraint
137
+ ) ;
138
+ if let Locations :: Interesting ( _) = constraint. locations {
139
+ true
140
+ } else {
141
+ false
142
+ }
176
143
}
177
144
178
145
/// This function classifies a constraint from a location.
179
146
fn classify_constraint (
180
147
& self ,
181
- index : & ConstraintIndex ,
148
+ index : ConstraintIndex ,
182
149
mir : & Mir < ' tcx > ,
183
150
) -> Option < ( ConstraintCategory , Span ) > {
184
- let constraint = self . constraints . get ( * index) ? ;
151
+ let constraint = self . constraints [ index] ;
185
152
let span = constraint. locations . span ( mir) ;
186
153
let location = constraint. locations . from_location ( ) ?;
187
154
@@ -238,7 +205,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
238
205
outlived_fr : RegionVid ,
239
206
blame_span : Span ,
240
207
) {
241
- // Obviously uncool error reporting.
208
+ debug ! ( "report_error(fr={:?}, outlived_fr={:?})" , fr , outlived_fr ) ;
242
209
243
210
let fr_name = self . to_error_region ( fr) ;
244
211
let outlived_fr_name = self . to_error_region ( outlived_fr) ;
@@ -261,19 +228,26 @@ impl<'tcx> RegionInferenceContext<'tcx> {
261
228
None => format ! ( "free region `{:?}`" , outlived_fr) ,
262
229
} ;
263
230
264
- let constraints = self . find_constraint_paths_from_region ( fr. clone ( ) ) ;
265
- let path = constraints. iter ( ) . min_by_key ( |p| p. len ( ) ) . unwrap ( ) ;
231
+ // Find all paths
232
+ let constraint_paths = self . find_constraint_paths_between_regions ( outlived_fr, fr) ;
233
+ debug ! ( "report_error: constraint_paths={:#?}" , constraint_paths) ;
234
+
235
+ // Find the shortest such path.
236
+ let path = constraint_paths. iter ( ) . min_by_key ( |p| p. len ( ) ) . unwrap ( ) ;
266
237
debug ! ( "report_error: shortest_path={:?}" , path) ;
267
238
268
- let mut categorized_path = path. iter ( )
269
- . filter_map ( |index| self . classify_constraint ( index, mir) )
270
- . collect :: < Vec < ( ConstraintCategory , Span ) > > ( ) ;
239
+ // Classify each of the constraints along the path.
240
+ let mut categorized_path: Vec < ( ConstraintCategory , Span ) > = path. iter ( )
241
+ . filter_map ( |& index| self . classify_constraint ( index, mir) )
242
+ . collect ( ) ;
271
243
debug ! ( "report_error: categorized_path={:?}" , categorized_path) ;
272
244
245
+ // Find what appears to be the most interesting path to report to the user.
273
246
categorized_path. sort_by ( |p0, p1| p0. 0 . cmp ( & p1. 0 ) ) ;
274
247
debug ! ( "report_error: sorted_path={:?}" , categorized_path) ;
275
248
276
- if let Some ( ( category, span) ) = & categorized_path. first ( ) {
249
+ // If we found something, cite that as the main cause of the problem.
250
+ if let Some ( ( category, span) ) = categorized_path. first ( ) {
277
251
let mut diag = infcx. tcx . sess . struct_span_err (
278
252
* span,
279
253
& format ! (
0 commit comments