@@ -15,6 +15,8 @@ use middle::typeck::infer::lattice::*;
15
15
use middle:: typeck:: infer:: sub:: Sub ;
16
16
use middle:: typeck:: infer:: to_str:: ToStr ;
17
17
18
+ use std:: list;
19
+
18
20
use syntax:: ast:: { Many , Once } ;
19
21
20
22
enum Glb = combine_fields; // "greatest lower bound" (common subtype)
@@ -23,6 +25,7 @@ impl Glb: combine {
23
25
fn infcx ( ) -> infer_ctxt { self . infcx }
24
26
fn tag ( ) -> ~str { ~"glb" }
25
27
fn a_is_expected ( ) -> bool { self . a_is_expected }
28
+ fn span ( ) -> span { self . span }
26
29
27
30
fn sub ( ) -> Sub { Sub ( * self ) }
28
31
fn lub ( ) -> Lub { Lub ( * self ) }
@@ -144,7 +147,122 @@ impl Glb: combine {
144
147
}
145
148
146
149
fn fns ( a : & ty:: FnTy , b : & ty:: FnTy ) -> cres < ty:: FnTy > {
147
- super_fns ( & self , a, b)
150
+ // Note: this is a subtle algorithm. For a full explanation,
151
+ // please see the large comment in `region_inference.rs`.
152
+
153
+ debug ! ( "%s.fns(%?, %?)" ,
154
+ self . tag( ) , a. to_str( self . infcx) , b. to_str( self . infcx) ) ;
155
+ let _indenter = indenter ( ) ;
156
+
157
+ // Take a snapshot. We'll never roll this back, but in later
158
+ // phases we do want to be able to examine "all bindings that
159
+ // were created as part of this type comparison", and making a
160
+ // snapshot is a convenient way to do that.
161
+ let snapshot = self . infcx . region_vars . start_snapshot ( ) ;
162
+
163
+ // Instantiate each bound region with a fresh region variable.
164
+ let ( a_with_fresh, a_isr) =
165
+ self . infcx . replace_bound_regions_with_fresh_regions (
166
+ self . span , a) ;
167
+ let a_vars = var_ids ( & self , a_isr) ;
168
+ let ( b_with_fresh, b_isr) =
169
+ self . infcx . replace_bound_regions_with_fresh_regions (
170
+ self . span , b) ;
171
+ let b_vars = var_ids ( & self , b_isr) ;
172
+
173
+ // Collect constraints.
174
+ let fn_ty0 = if_ok ! ( super_fns( & self , & a_with_fresh, & b_with_fresh) ) ;
175
+ debug ! ( "fn_ty0 = %s" , fn_ty0. to_str( self . infcx) ) ;
176
+
177
+ // Generalize the regions appearing in fn_ty0 if possible
178
+ let new_vars =
179
+ self . infcx . region_vars . vars_created_since_snapshot ( snapshot) ;
180
+ let fn_ty1 =
181
+ self . infcx . fold_regions_in_sig (
182
+ & fn_ty0,
183
+ |r, _in_fn| generalize_region ( & self , snapshot,
184
+ new_vars, a_isr, a_vars, b_vars,
185
+ r) ) ;
186
+ debug ! ( "fn_ty1 = %s" , fn_ty1. to_str( self . infcx) ) ;
187
+ return Ok ( move fn_ty1) ;
188
+
189
+ fn generalize_region ( self : & Glb ,
190
+ snapshot : uint ,
191
+ new_vars : & [ RegionVid ] ,
192
+ a_isr : isr_alist ,
193
+ a_vars : & [ RegionVid ] ,
194
+ b_vars : & [ RegionVid ] ,
195
+ r0 : ty:: Region ) -> ty:: Region {
196
+ if !is_var_in_set ( new_vars, r0) {
197
+ return r0;
198
+ }
199
+
200
+ let tainted = self . infcx . region_vars . tainted ( snapshot, r0) ;
201
+
202
+ let mut a_r = None , b_r = None , only_new_vars = true ;
203
+ for tainted. each |r| {
204
+ if is_var_in_set ( a_vars, * r) {
205
+ if a_r. is_some ( ) {
206
+ return fresh_bound_variable ( self ) ;
207
+ } else {
208
+ a_r = Some ( * r) ;
209
+ }
210
+ } else if is_var_in_set ( b_vars, * r) {
211
+ if b_r. is_some ( ) {
212
+ return fresh_bound_variable ( self ) ;
213
+ } else {
214
+ b_r = Some ( * r) ;
215
+ }
216
+ } else if !is_var_in_set ( new_vars, * r) {
217
+ only_new_vars = false ;
218
+ }
219
+ }
220
+
221
+ // NB---I do not believe this algorithm computes
222
+ // (necessarily) the GLB. As written it can
223
+ // spuriously fail. In particular, if there is a case
224
+ // like: fn(fn(&a)) and fn(fn(&b)), where a and b are
225
+ // free, it will return fn(&c) where c = GLB(a,b). If
226
+ // however this GLB is not defined, then the result is
227
+ // an error, even though something like
228
+ // "fn<X>(fn(&X))" where X is bound would be a
229
+ // subtype of both of those.
230
+ //
231
+ // The problem is that if we were to return a bound
232
+ // variable, we'd be computing a lower-bound, but not
233
+ // necessarily the *greatest* lower-bound.
234
+
235
+ if a_r. is_some ( ) && b_r. is_some ( ) && only_new_vars {
236
+ // Related to exactly one bound variable from each fn:
237
+ return rev_lookup ( self , a_isr, a_r. get ( ) ) ;
238
+ } else if a_r. is_none ( ) && b_r. is_none ( ) {
239
+ // Not related to bound variables from either fn:
240
+ return r0;
241
+ } else {
242
+ // Other:
243
+ return fresh_bound_variable ( self ) ;
244
+ }
245
+ }
246
+
247
+ fn rev_lookup ( self : & Glb ,
248
+ a_isr : isr_alist ,
249
+ r : ty:: Region ) -> ty:: Region
250
+ {
251
+ for list:: each( a_isr) |pair| {
252
+ let ( a_br, a_r) = * pair;
253
+ if a_r == r {
254
+ return ty:: re_bound ( a_br) ;
255
+ }
256
+ }
257
+
258
+ self . infcx . tcx . sess . span_bug (
259
+ self . span ,
260
+ fmt ! ( "could not find original bound region for %?" , r) ) ;
261
+ }
262
+
263
+ fn fresh_bound_variable ( self : & Glb ) -> ty:: Region {
264
+ self . infcx . region_vars . new_bound ( )
265
+ }
148
266
}
149
267
150
268
fn fn_metas ( a : & ty:: FnMeta , b : & ty:: FnMeta ) -> cres < ty:: FnMeta > {
0 commit comments