Skip to content

Implement GLB algorithm. (Issue #2263) #4168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/librustc/metadata/tyencode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ fn enc_bound_region(w: io::Writer, cx: @ctxt, br: ty::bound_region) {
w.write_char('|');
enc_bound_region(w, cx, *br);
}
ty::br_fresh(id) => {
w.write_uint(id);
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/librustc/middle/astencode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,8 @@ impl ty::Region: tr {
impl ty::bound_region: tr {
fn tr(xcx: extended_decode_ctxt) -> ty::bound_region {
match self {
ty::br_anon(_) | ty::br_named(_) | ty::br_self => self,
ty::br_anon(_) | ty::br_named(_) | ty::br_self |
ty::br_fresh(_) => self,
ty::br_cap_avoid(id, br) => ty::br_cap_avoid(xcx.tr_id(id),
@br.tr(xcx))
}
Expand Down
33 changes: 28 additions & 5 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export expr_ty_params_and_ty;
export expr_is_lval, expr_kind;
export ExprKind, LvalueExpr, RvalueDatumExpr, RvalueDpsExpr, RvalueStmtExpr;
export field_ty;
export fold_ty, fold_sty_to_ty, fold_region, fold_regions;
export fold_ty, fold_sty_to_ty, fold_region, fold_regions, fold_sig;
export apply_op_on_t_to_ty_fn;
export fold_regions_and_ty, walk_regions_and_ty;
export field;
Expand Down Expand Up @@ -126,7 +126,7 @@ export ty_struct;
export Region, bound_region, encl_region;
export re_bound, re_free, re_scope, re_static, re_infer;
export ReVar, ReSkolemized;
export br_self, br_anon, br_named, br_cap_avoid;
export br_self, br_anon, br_named, br_cap_avoid, br_fresh;
export get, type_has_params, type_needs_infer, type_has_regions;
export type_is_region_ptr;
export type_id;
Expand Down Expand Up @@ -585,6 +585,9 @@ enum bound_region {
/// Named region parameters for functions (a in &a/T)
br_named(ast::ident),

/// Fresh bound identifiers created during GLB computations.
br_fresh(uint),

/**
* Handles capture-avoiding substitution in a rather subtle case. If you
* have a closure whose argument types are being inferred based on the
Expand Down Expand Up @@ -1285,6 +1288,17 @@ fn fold_sty_to_ty(tcx: ty::ctxt, sty: &sty, foldop: fn(t) -> t) -> t {
mk_t(tcx, fold_sty(sty, foldop))
}

fn fold_sig(sig: &FnSig, fldop: fn(t) -> t) -> FnSig {
let args = do sig.inputs.map |arg| {
{ mode: arg.mode, ty: fldop(arg.ty) }
};

FnSig {
inputs: move args,
output: fldop(sig.output)
}
}

fn fold_sty(sty: &sty, fldop: fn(t) -> t) -> sty {
fn fold_substs(substs: &substs, fldop: fn(t) -> t) -> substs {
{self_r: substs.self_r,
Expand Down Expand Up @@ -1463,8 +1477,8 @@ fn apply_op_on_t_to_ty_fn(
fn fold_regions(
cx: ctxt,
ty: t,
fldr: fn(r: Region, in_fn: bool) -> Region) -> t {

fldr: fn(r: Region, in_fn: bool) -> Region) -> t
{
fn do_fold(cx: ctxt, ty: t, in_fn: bool,
fldr: fn(Region, bool) -> Region) -> t {
if !type_has_regions(ty) { return ty; }
Expand Down Expand Up @@ -2690,7 +2704,10 @@ impl bound_region : to_bytes::IterBytes {
to_bytes::iter_bytes_2(&2u8, ident, lsb0, f),

ty::br_cap_avoid(ref id, ref br) =>
to_bytes::iter_bytes_3(&3u8, id, br, lsb0, f)
to_bytes::iter_bytes_3(&3u8, id, br, lsb0, f),

ty::br_fresh(ref x) =>
to_bytes::iter_bytes_2(&4u8, x, lsb0, f)
}
}
}
Expand Down Expand Up @@ -4452,6 +4469,12 @@ impl bound_region : cmp::Eq {
_ => false
}
}
br_fresh(e0a) => {
match (*other) {
br_fresh(e0b) => e0a == e0b,
_ => false
}
}
}
}
pure fn ne(&self, other: &bound_region) -> bool { !(*self).eq(other) }
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/typeck/infer/assignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ enum Assign = combine_fields;

impl Assign {
fn tys(a: ty::t, b: ty::t) -> ares {
debug!("Assign.tys(%s -> %s)",
debug!("Assign.tys(%s => %s)",
a.to_str(self.infcx),
b.to_str(self.infcx));
let _r = indenter();
Expand Down Expand Up @@ -124,7 +124,7 @@ priv impl Assign {
a: ty::t, b: ty::t,
+a_bnd: Option<ty::t>, +b_bnd: Option<ty::t>) -> ares {

debug!("Assign.assign_tys_or_sub(%s -> %s, %s -> %s)",
debug!("Assign.assign_tys_or_sub(%s => %s, %s => %s)",
a.to_str(self.infcx), b.to_str(self.infcx),
a_bnd.to_str(self.infcx), b_bnd.to_str(self.infcx));
let _r = indenter();
Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/typeck/infer/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ trait combine {
fn infcx() -> infer_ctxt;
fn tag() -> ~str;
fn a_is_expected() -> bool;
fn span() -> span;

fn sub() -> Sub;
fn lub() -> Lub;
Expand Down
118 changes: 117 additions & 1 deletion src/librustc/middle/typeck/infer/glb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ impl Glb: combine {
fn infcx() -> infer_ctxt { self.infcx }
fn tag() -> ~str { ~"glb" }
fn a_is_expected() -> bool { self.a_is_expected }
fn span() -> span { self.span }

fn sub() -> Sub { Sub(*self) }
fn lub() -> Lub { Lub(*self) }
Expand Down Expand Up @@ -152,7 +153,122 @@ impl Glb: combine {
}

fn fns(a: &ty::FnTy, b: &ty::FnTy) -> cres<ty::FnTy> {
super_fns(&self, a, b)
// Note: this is a subtle algorithm. For a full explanation,
// please see the large comment in `region_inference.rs`.

debug!("%s.fns(%?, %?)",
self.tag(), a.to_str(self.infcx), b.to_str(self.infcx));
let _indenter = indenter();

// Take a snapshot. We'll never roll this back, but in later
// phases we do want to be able to examine "all bindings that
// were created as part of this type comparison", and making a
// snapshot is a convenient way to do that.
let snapshot = self.infcx.region_vars.start_snapshot();

// Instantiate each bound region with a fresh region variable.
let (a_with_fresh, a_isr) =
self.infcx.replace_bound_regions_with_fresh_regions(
self.span, a);
let a_vars = var_ids(&self, a_isr);
let (b_with_fresh, b_isr) =
self.infcx.replace_bound_regions_with_fresh_regions(
self.span, b);
let b_vars = var_ids(&self, b_isr);

// Collect constraints.
let fn_ty0 = if_ok!(super_fns(&self, &a_with_fresh, &b_with_fresh));
debug!("fn_ty0 = %s", fn_ty0.to_str(self.infcx));

// Generalize the regions appearing in fn_ty0 if possible
let new_vars =
self.infcx.region_vars.vars_created_since_snapshot(snapshot);
let fn_ty1 =
self.infcx.fold_regions_in_sig(
&fn_ty0,
|r, _in_fn| generalize_region(&self, snapshot,
new_vars, a_isr, a_vars, b_vars,
r));
debug!("fn_ty1 = %s", fn_ty1.to_str(self.infcx));
return Ok(move fn_ty1);

fn generalize_region(self: &Glb,
snapshot: uint,
new_vars: &[RegionVid],
a_isr: isr_alist,
a_vars: &[RegionVid],
b_vars: &[RegionVid],
r0: ty::Region) -> ty::Region {
if !is_var_in_set(new_vars, r0) {
return r0;
}

let tainted = self.infcx.region_vars.tainted(snapshot, r0);

let mut a_r = None, b_r = None, only_new_vars = true;
for tainted.each |r| {
if is_var_in_set(a_vars, *r) {
if a_r.is_some() {
return fresh_bound_variable(self);
} else {
a_r = Some(*r);
}
} else if is_var_in_set(b_vars, *r) {
if b_r.is_some() {
return fresh_bound_variable(self);
} else {
b_r = Some(*r);
}
} else if !is_var_in_set(new_vars, *r) {
only_new_vars = false;
}
}

// NB---I do not believe this algorithm computes
// (necessarily) the GLB. As written it can
// spuriously fail. In particular, if there is a case
// like: fn(fn(&a)) and fn(fn(&b)), where a and b are
// free, it will return fn(&c) where c = GLB(a,b). If
// however this GLB is not defined, then the result is
// an error, even though something like
// "fn<X>(fn(&X))" where X is bound would be a
// subtype of both of those.
//
// The problem is that if we were to return a bound
// variable, we'd be computing a lower-bound, but not
// necessarily the *greatest* lower-bound.

if a_r.is_some() && b_r.is_some() && only_new_vars {
// Related to exactly one bound variable from each fn:
return rev_lookup(self, a_isr, a_r.get());
} else if a_r.is_none() && b_r.is_none() {
// Not related to bound variables from either fn:
return r0;
} else {
// Other:
return fresh_bound_variable(self);
}
}

fn rev_lookup(self: &Glb,
a_isr: isr_alist,
r: ty::Region) -> ty::Region
{
for list::each(a_isr) |pair| {
let (a_br, a_r) = *pair;
if a_r == r {
return ty::re_bound(a_br);
}
}

self.infcx.tcx.sess.span_bug(
self.span,
fmt!("could not find original bound region for %?", r));
}

fn fresh_bound_variable(self: &Glb) -> ty::Region {
self.infcx.region_vars.new_bound()
}
}

fn fn_metas(a: &ty::FnMeta, b: &ty::FnMeta) -> cres<ty::FnMeta> {
Expand Down
26 changes: 26 additions & 0 deletions src/librustc/middle/typeck/infer/lattice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,29 @@ fn lattice_var_and_t<L:lattice_ops combine>(
}
}
}

// ___________________________________________________________________________
// Random utility functions used by LUB/GLB when computing LUB/GLB of
// fn types

fn var_ids<T: combine>(self: &T, isr: isr_alist) -> ~[RegionVid] {
let mut result = ~[];
for list::each(isr) |pair| {
match pair.second() {
ty::re_infer(ty::ReVar(r)) => { result.push(r); }
r => {
self.infcx().tcx.sess.span_bug(
self.span(),
fmt!("Found non-region-vid: %?", r));
}
}
}
return result;
}

fn is_var_in_set(new_vars: &[RegionVid], r: ty::Region) -> bool {
match r {
ty::re_infer(ty::ReVar(ref v)) => new_vars.contains(v),
_ => false
}
}
22 changes: 7 additions & 15 deletions src/librustc/middle/typeck/infer/lub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ impl Lub: combine {
fn infcx() -> infer_ctxt { self.infcx }
fn tag() -> ~str { ~"lub" }
fn a_is_expected() -> bool { self.a_is_expected }
fn span() -> span { self.span }

fn sub() -> Sub { Sub(*self) }
fn lub() -> Lub { Lub(*self) }
Expand Down Expand Up @@ -142,12 +143,10 @@ impl Lub: combine {
let new_vars =
self.infcx.region_vars.vars_created_since_snapshot(snapshot);
let fn_ty1 =
ty::apply_op_on_t_to_ty_fn(
self.infcx.tcx, &fn_ty0,
|t| ty::fold_regions(
self.infcx.tcx, t,
|r, _in_fn| generalize_region(&self, snapshot,
new_vars, a_isr, r)));
self.infcx.fold_regions_in_sig(
&fn_ty0,
|r, _in_fn| generalize_region(&self, snapshot, new_vars,
a_isr, r));
return Ok(move fn_ty1);

fn generalize_region(self: &Lub,
Expand All @@ -156,7 +155,7 @@ impl Lub: combine {
a_isr: isr_alist,
r0: ty::Region) -> ty::Region {
// Regions that pre-dated the LUB computation stay as they are.
if !is_new_var(new_vars, r0) {
if !is_var_in_set(new_vars, r0) {
debug!("generalize_region(r0=%?): not new variable", r0);
return r0;
}
Expand All @@ -166,7 +165,7 @@ impl Lub: combine {
// Variables created during LUB computation which are
// *related* to regions that pre-date the LUB computation
// stay as they are.
if !tainted.all(|r| is_new_var(new_vars, *r)) {
if !tainted.all(|r| is_var_in_set(new_vars, *r)) {
debug!("generalize_region(r0=%?): \
non-new-variables found in %?",
r0, tainted);
Expand All @@ -193,13 +192,6 @@ impl Lub: combine {
fmt!("Region %? is not associated with \
any bound region from A!", r0));
}

fn is_new_var(new_vars: &[RegionVid], r: ty::Region) -> bool {
match r {
ty::re_infer(ty::ReVar(ref v)) => new_vars.contains(v),
_ => false
}
}
}

fn fn_metas(a: &ty::FnMeta, b: &ty::FnMeta) -> cres<ty::FnMeta> {
Expand Down
11 changes: 11 additions & 0 deletions src/librustc/middle/typeck/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -776,5 +776,16 @@ impl infer_ctxt {
(fn_ty, isr)
}

fn fold_regions_in_sig(
&self,
fn_ty: &ty::FnTy,
fldr: &fn(r: ty::Region, in_fn: bool) -> ty::Region) -> ty::FnTy
{
let sig = do ty::fold_sig(&fn_ty.sig) |t| {
ty::fold_regions(self.tcx, t, fldr)
};
ty::FnTyBase {meta: fn_ty.meta, sig: sig}
}

}

Loading