Skip to content

Commit c3a74d8

Browse files
committed
Implement GLB algorithm. (Issue #2263)
r=brson
1 parent 3b71d14 commit c3a74d8

File tree

15 files changed

+517
-107
lines changed

15 files changed

+517
-107
lines changed

src/librustc/metadata/tyencode.rs

+3
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ fn enc_bound_region(w: io::Writer, cx: @ctxt, br: ty::bound_region) {
188188
w.write_char('|');
189189
enc_bound_region(w, cx, *br);
190190
}
191+
ty::br_fresh(id) => {
192+
w.write_uint(id);
193+
}
191194
}
192195
}
193196

src/librustc/middle/astencode.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,8 @@ impl ty::Region: tr {
418418
impl ty::bound_region: tr {
419419
fn tr(xcx: extended_decode_ctxt) -> ty::bound_region {
420420
match self {
421-
ty::br_anon(_) | ty::br_named(_) | ty::br_self => self,
421+
ty::br_anon(_) | ty::br_named(_) | ty::br_self |
422+
ty::br_fresh(_) => self,
422423
ty::br_cap_avoid(id, br) => ty::br_cap_avoid(xcx.tr_id(id),
423424
@br.tr(xcx))
424425
}

src/librustc/middle/ty.rs

+28-5
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export expr_ty_params_and_ty;
7272
export expr_is_lval, expr_kind;
7373
export ExprKind, LvalueExpr, RvalueDatumExpr, RvalueDpsExpr, RvalueStmtExpr;
7474
export field_ty;
75-
export fold_ty, fold_sty_to_ty, fold_region, fold_regions;
75+
export fold_ty, fold_sty_to_ty, fold_region, fold_regions, fold_sig;
7676
export apply_op_on_t_to_ty_fn;
7777
export fold_regions_and_ty, walk_regions_and_ty;
7878
export field;
@@ -145,7 +145,7 @@ export ty_struct;
145145
export Region, bound_region, encl_region;
146146
export re_bound, re_free, re_scope, re_static, re_infer;
147147
export ReVar, ReSkolemized;
148-
export br_self, br_anon, br_named, br_cap_avoid;
148+
export br_self, br_anon, br_named, br_cap_avoid, br_fresh;
149149
export get, type_has_params, type_needs_infer, type_has_regions;
150150
export type_is_region_ptr;
151151
export type_id;
@@ -606,6 +606,9 @@ enum bound_region {
606606
/// Named region parameters for functions (a in &a/T)
607607
br_named(ast::ident),
608608

609+
/// Fresh bound identifiers created during GLB computations.
610+
br_fresh(uint),
611+
609612
/**
610613
* Handles capture-avoiding substitution in a rather subtle case. If you
611614
* have a closure whose argument types are being inferred based on the
@@ -1311,6 +1314,17 @@ fn fold_sty_to_ty(tcx: ty::ctxt, sty: &sty, foldop: fn(t) -> t) -> t {
13111314
mk_t(tcx, fold_sty(sty, foldop))
13121315
}
13131316

1317+
fn fold_sig(sig: &FnSig, fldop: fn(t) -> t) -> FnSig {
1318+
let args = do sig.inputs.map |arg| {
1319+
{ mode: arg.mode, ty: fldop(arg.ty) }
1320+
};
1321+
1322+
FnSig {
1323+
inputs: move args,
1324+
output: fldop(sig.output)
1325+
}
1326+
}
1327+
13141328
fn fold_sty(sty: &sty, fldop: fn(t) -> t) -> sty {
13151329
fn fold_substs(substs: &substs, fldop: fn(t) -> t) -> substs {
13161330
{self_r: substs.self_r,
@@ -1489,8 +1503,8 @@ fn apply_op_on_t_to_ty_fn(
14891503
fn fold_regions(
14901504
cx: ctxt,
14911505
ty: t,
1492-
fldr: fn(r: Region, in_fn: bool) -> Region) -> t {
1493-
1506+
fldr: fn(r: Region, in_fn: bool) -> Region) -> t
1507+
{
14941508
fn do_fold(cx: ctxt, ty: t, in_fn: bool,
14951509
fldr: fn(Region, bool) -> Region) -> t {
14961510
if !type_has_regions(ty) { return ty; }
@@ -2744,7 +2758,10 @@ impl bound_region : to_bytes::IterBytes {
27442758
to_bytes::iter_bytes_2(&2u8, ident, lsb0, f),
27452759
27462760
ty::br_cap_avoid(ref id, ref br) =>
2747-
to_bytes::iter_bytes_3(&3u8, id, br, lsb0, f)
2761+
to_bytes::iter_bytes_3(&3u8, id, br, lsb0, f),
2762+
2763+
ty::br_fresh(ref x) =>
2764+
to_bytes::iter_bytes_2(&4u8, x, lsb0, f)
27482765
}
27492766
}
27502767
}
@@ -4493,6 +4510,12 @@ impl bound_region : cmp::Eq {
44934510
_ => false
44944511
}
44954512
}
4513+
br_fresh(e0a) => {
4514+
match (*other) {
4515+
br_fresh(e0b) => e0a == e0b,
4516+
_ => false
4517+
}
4518+
}
44964519
}
44974520
}
44984521
pure fn ne(&self, other: &bound_region) -> bool { !(*self).eq(other) }

src/librustc/middle/typeck/infer/assignment.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ enum Assign = combine_fields;
8080

8181
impl Assign {
8282
fn tys(a: ty::t, b: ty::t) -> ares {
83-
debug!("Assign.tys(%s -> %s)",
83+
debug!("Assign.tys(%s => %s)",
8484
a.to_str(self.infcx),
8585
b.to_str(self.infcx));
8686
let _r = indenter();
@@ -139,7 +139,7 @@ priv impl Assign {
139139
a: ty::t, b: ty::t,
140140
+a_bnd: Option<ty::t>, +b_bnd: Option<ty::t>) -> ares {
141141

142-
debug!("Assign.assign_tys_or_sub(%s -> %s, %s -> %s)",
142+
debug!("Assign.assign_tys_or_sub(%s => %s, %s => %s)",
143143
a.to_str(self.infcx), b.to_str(self.infcx),
144144
a_bnd.to_str(self.infcx), b_bnd.to_str(self.infcx));
145145
let _r = indenter();

src/librustc/middle/typeck/infer/combine.rs

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ trait combine {
6969
fn infcx() -> infer_ctxt;
7070
fn tag() -> ~str;
7171
fn a_is_expected() -> bool;
72+
fn span() -> span;
7273

7374
fn sub() -> Sub;
7475
fn lub() -> Lub;

src/librustc/middle/typeck/infer/glb.rs

+119-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ use middle::typeck::infer::lattice::*;
1515
use middle::typeck::infer::sub::Sub;
1616
use middle::typeck::infer::to_str::ToStr;
1717

18+
use std::list;
19+
1820
use syntax::ast::{Many, Once};
1921

2022
enum Glb = combine_fields; // "greatest lower bound" (common subtype)
@@ -23,6 +25,7 @@ impl Glb: combine {
2325
fn infcx() -> infer_ctxt { self.infcx }
2426
fn tag() -> ~str { ~"glb" }
2527
fn a_is_expected() -> bool { self.a_is_expected }
28+
fn span() -> span { self.span }
2629

2730
fn sub() -> Sub { Sub(*self) }
2831
fn lub() -> Lub { Lub(*self) }
@@ -144,7 +147,122 @@ impl Glb: combine {
144147
}
145148

146149
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+
}
148266
}
149267

150268
fn fn_metas(a: &ty::FnMeta, b: &ty::FnMeta) -> cres<ty::FnMeta> {

src/librustc/middle/typeck/infer/lattice.rs

+28
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use middle::typeck::infer::combine::*;
1414
use middle::typeck::infer::unify::*;
1515
use middle::typeck::infer::to_str::ToStr;
1616

17+
use std::list;
18+
1719
// ______________________________________________________________________
1820
// Lattice operations on variables
1921
//
@@ -158,3 +160,29 @@ fn lattice_var_and_t<L:lattice_ops combine>(
158160
}
159161
}
160162
}
163+
164+
// ___________________________________________________________________________
165+
// Random utility functions used by LUB/GLB when computing LUB/GLB of
166+
// fn types
167+
168+
fn var_ids<T: combine>(self: &T, isr: isr_alist) -> ~[RegionVid] {
169+
let mut result = ~[];
170+
for list::each(isr) |pair| {
171+
match pair.second() {
172+
ty::re_infer(ty::ReVar(r)) => { result.push(r); }
173+
r => {
174+
self.infcx().tcx.sess.span_bug(
175+
self.span(),
176+
fmt!("Found non-region-vid: %?", r));
177+
}
178+
}
179+
}
180+
return result;
181+
}
182+
183+
fn is_var_in_set(new_vars: &[RegionVid], r: ty::Region) -> bool {
184+
match r {
185+
ty::re_infer(ty::ReVar(ref v)) => new_vars.contains(v),
186+
_ => false
187+
}
188+
}

src/librustc/middle/typeck/infer/lub.rs

+7-15
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ impl Lub: combine {
3131
fn infcx() -> infer_ctxt { self.infcx }
3232
fn tag() -> ~str { ~"lub" }
3333
fn a_is_expected() -> bool { self.a_is_expected }
34+
fn span() -> span { self.span }
3435

3536
fn sub() -> Sub { Sub(*self) }
3637
fn lub() -> Lub { Lub(*self) }
@@ -139,12 +140,10 @@ impl Lub: combine {
139140
let new_vars =
140141
self.infcx.region_vars.vars_created_since_snapshot(snapshot);
141142
let fn_ty1 =
142-
ty::apply_op_on_t_to_ty_fn(
143-
self.infcx.tcx, &fn_ty0,
144-
|t| ty::fold_regions(
145-
self.infcx.tcx, t,
146-
|r, _in_fn| generalize_region(&self, snapshot,
147-
new_vars, a_isr, r)));
143+
self.infcx.fold_regions_in_sig(
144+
&fn_ty0,
145+
|r, _in_fn| generalize_region(&self, snapshot, new_vars,
146+
a_isr, r));
148147
return Ok(move fn_ty1);
149148

150149
fn generalize_region(self: &Lub,
@@ -153,7 +152,7 @@ impl Lub: combine {
153152
a_isr: isr_alist,
154153
r0: ty::Region) -> ty::Region {
155154
// Regions that pre-dated the LUB computation stay as they are.
156-
if !is_new_var(new_vars, r0) {
155+
if !is_var_in_set(new_vars, r0) {
157156
debug!("generalize_region(r0=%?): not new variable", r0);
158157
return r0;
159158
}
@@ -163,7 +162,7 @@ impl Lub: combine {
163162
// Variables created during LUB computation which are
164163
// *related* to regions that pre-date the LUB computation
165164
// stay as they are.
166-
if !tainted.all(|r| is_new_var(new_vars, *r)) {
165+
if !tainted.all(|r| is_var_in_set(new_vars, *r)) {
167166
debug!("generalize_region(r0=%?): \
168167
non-new-variables found in %?",
169168
r0, tainted);
@@ -190,13 +189,6 @@ impl Lub: combine {
190189
fmt!("Region %? is not associated with \
191190
any bound region from A!", r0));
192191
}
193-
194-
fn is_new_var(new_vars: &[RegionVid], r: ty::Region) -> bool {
195-
match r {
196-
ty::re_infer(ty::ReVar(ref v)) => new_vars.contains(v),
197-
_ => false
198-
}
199-
}
200192
}
201193

202194
fn fn_metas(a: &ty::FnMeta, b: &ty::FnMeta) -> cres<ty::FnMeta> {

src/librustc/middle/typeck/infer/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -799,5 +799,16 @@ impl infer_ctxt {
799799
(fn_ty, isr)
800800
}
801801

802+
fn fold_regions_in_sig(
803+
&self,
804+
fn_ty: &ty::FnTy,
805+
fldr: &fn(r: ty::Region, in_fn: bool) -> ty::Region) -> ty::FnTy
806+
{
807+
let sig = do ty::fold_sig(&fn_ty.sig) |t| {
808+
ty::fold_regions(self.tcx, t, fldr)
809+
};
810+
ty::FnTyBase {meta: fn_ty.meta, sig: sig}
811+
}
812+
802813
}
803814

0 commit comments

Comments
 (0)