Skip to content
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

Implement least-upper-bound for fn types #4015

Closed
wants to merge 2 commits into from
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
25 changes: 25 additions & 0 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ 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 apply_op_on_t_to_ty_fn;
export fold_regions_and_ty, walk_regions_and_ty;
export field;
export field_idx, field_idx_strict;
Expand Down Expand Up @@ -1476,6 +1477,30 @@ fn fold_regions_and_ty(
}
}

/* A little utility: it often happens that I have a `fn_ty`,
* but I want to use some function like `fold_regions_and_ty()`
* that is defined over all types. This utility converts to
* a full type and back. It's not the best way to do this (somewhat
* inefficient to do the conversion), it would be better to refactor
* all this folding business. However, I've been waiting on that
* until trait support is improved. */
fn apply_op_on_t_to_ty_fn(
cx: ctxt,
f: &FnTy,
t_op: fn(t) -> t) -> FnTy
{
let t0 = ty::mk_fn(cx, *f);
let t1 = t_op(t0);
match ty::get(t1).sty {
ty::ty_fn(copy f) => {
move f
}
_ => {
cx.sess.bug(~"`t_op` did not return a function type");
}
}
}

// n.b. this function is intended to eventually replace fold_region() below,
// that is why its name is so similar.
fn fold_regions(
Expand Down
23 changes: 23 additions & 0 deletions src/librustc/middle/typeck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,29 @@ fn require_same_types(
}
}

// a list of mapping from in-scope-region-names ("isr") to the
// corresponding ty::Region
type isr_alist = @List<(ty::bound_region, ty::Region)>;

trait get_and_find_region {
fn get(br: ty::bound_region) -> ty::Region;
fn find(br: ty::bound_region) -> Option<ty::Region>;
}

impl isr_alist: get_and_find_region {
fn get(br: ty::bound_region) -> ty::Region {
self.find(br).get()
}

fn find(br: ty::bound_region) -> Option<ty::Region> {
for list::each(self) |isr| {
let (isr_br, isr_r) = *isr;
if isr_br == br { return Some(isr_r); }
}
return None;
}
}

fn arg_is_argv_ty(tcx: ty::ctxt, a: ty::arg) -> bool {
match ty::resolved_mode(tcx, a.mode) {
ast::by_val => { /*ok*/ }
Expand Down
23 changes: 0 additions & 23 deletions src/librustc/middle/typeck/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,29 +166,6 @@ fn blank_fn_ctxt(ccx: @crate_ctxt, rty: ty::t,
}
}

// a list of mapping from in-scope-region-names ("isr") to the
// corresponding ty::Region
type isr_alist = @List<(ty::bound_region, ty::Region)>;

trait get_and_find_region {
fn get(br: ty::bound_region) -> ty::Region;
fn find(br: ty::bound_region) -> Option<ty::Region>;
}

impl isr_alist: get_and_find_region {
fn get(br: ty::bound_region) -> ty::Region {
self.find(br).get()
}

fn find(br: ty::bound_region) -> Option<ty::Region> {
for list::each(self) |isr| {
let (isr_br, isr_r) = *isr;
if isr_br == br { return Some(isr_r); }
}
return None;
}
}

fn check_item_types(ccx: @crate_ctxt, crate: @ast::crate) {
let visit = visit::mk_simple_visitor(@{
visit_item: |a| check_item(ccx, a),
Expand Down
19 changes: 19 additions & 0 deletions src/librustc/middle/typeck/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,5 +684,24 @@ impl infer_ctxt {
result::Err(_) => typ
}
}

fn replace_bound_regions_with_fresh_regions(
&self, span: span,
fty: &ty::FnTy) -> (ty::FnTy, isr_alist)
{
let {fn_ty, isr, _} =
replace_bound_regions_in_fn_ty(self.tcx, @Nil, None, fty, |br| {
// N.B.: The name of the bound region doesn't have anything to
// do with the region variable that's created for it. The
// only thing we're doing with `br` here is using it in the
// debug message.
let rvar = self.next_region_var_nb(span);
debug!("Bound region %s maps to %?",
bound_region_to_str(self.tcx, br),
rvar);
rvar
});
(fn_ty, isr)
}
}

5 changes: 3 additions & 2 deletions src/librustc/middle/typeck/infer/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,9 @@ fn super_args<C:combine>(

fn super_vstores<C:combine>(
self: &C, vk: ty::terr_vstore_kind,
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore> {
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore>
{
debug!("%s.super_vstores(a=%?, b=%?)", self.tag(), a, b);

match (a, b) {
(ty::vstore_slice(a_r), ty::vstore_slice(b_r)) => {
Expand Down Expand Up @@ -517,4 +519,3 @@ fn super_tys<C:combine>(
_ => Err(ty::terr_sorts(expected_found(self, a, b)))
}
}

108 changes: 96 additions & 12 deletions src/librustc/middle/typeck/infer/lub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use lattice::*;
use to_str::ToStr;
use syntax::ast::{Many, Once};

fn macros() { include!("macros.rs"); } // FIXME(#3114): Macro import/export.

enum Lub = combine_fields; // "subtype", "subregion" etc

impl Lub: combine {
Expand Down Expand Up @@ -102,6 +104,100 @@ impl Lub: combine {
}
}

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

// 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 (b_with_fresh, _) =
self.infcx.replace_bound_regions_with_fresh_regions(
self.span, b);

// 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 =
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)));
return Ok(move fn_ty1);

fn generalize_region(self: &Lub,
snapshot: uint,
new_vars: &[RegionVid],
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) {
debug!("generalize_region(r0=%?): not new variable", r0);
return r0;
}

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

// 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)) {
debug!("generalize_region(r0=%?): \
non-new-variables found in %?",
r0, tainted);
return r0;
}

// Otherwise, the variable must be associated with at
// least one of the variables representing bound regions
// in both A and B. Replace the variable with the "first"
// bound region from A that we find it to be associated
// with.
for list::each(a_isr) |pair| {
let (a_br, a_r) = *pair;
if tainted.contains(&a_r) {
debug!("generalize_region(r0=%?): \
replacing with %?, tainted=%?",
r0, a_br, tainted);
return ty::re_bound(a_br);
}
}

self.infcx.tcx.sess.span_bug(
self.span,
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> {
super_fn_metas(&self, a, b)
}

fn fn_sigs(a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
super_fn_sigs(&self, a, b)
}

// Traits please (FIXME: #2794):

fn tys(a: ty::t, b: ty::t) -> cres<ty::t> {
Expand All @@ -125,18 +221,6 @@ impl Lub: combine {
super_args(&self, a, b)
}

fn fns(a: &ty::FnTy, b: &ty::FnTy) -> cres<ty::FnTy> {
super_fns(&self, a, b)
}

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

fn fn_sigs(a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
super_fn_sigs(&self, a, b)
}

fn substs(did: ast::def_id,
as_: &ty::substs,
bs: &ty::substs) -> cres<ty::substs> {
Expand Down
Loading