Skip to content

Commit 117e5e3

Browse files
committed
Implement LUB algorithm and add new unit-testing infrastructure for infer.
r=brson
1 parent dc34fb9 commit 117e5e3

File tree

12 files changed

+727
-119
lines changed

12 files changed

+727
-119
lines changed

src/librustc/middle/ty.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export expr_is_lval, expr_kind;
4141
export ExprKind, LvalueExpr, RvalueDatumExpr, RvalueDpsExpr, RvalueStmtExpr;
4242
export field_ty;
4343
export fold_ty, fold_sty_to_ty, fold_region, fold_regions;
44+
export apply_op_on_t_to_ty_fn;
4445
export fold_regions_and_ty, walk_regions_and_ty;
4546
export field;
4647
export field_idx, field_idx_strict;
@@ -1482,6 +1483,30 @@ fn fold_regions_and_ty(
14821483
}
14831484
}
14841485

1486+
/* A little utility: it often happens that I have a `fn_ty`,
1487+
* but I want to use some function like `fold_regions_and_ty()`
1488+
* that is defined over all types. This utility converts to
1489+
* a full type and back. It's not the best way to do this (somewhat
1490+
* inefficient to do the conversion), it would be better to refactor
1491+
* all this folding business. However, I've been waiting on that
1492+
* until trait support is improved. */
1493+
fn apply_op_on_t_to_ty_fn(
1494+
cx: ctxt,
1495+
f: &FnTy,
1496+
t_op: fn(t) -> t) -> FnTy
1497+
{
1498+
let t0 = ty::mk_fn(cx, *f);
1499+
let t1 = t_op(t0);
1500+
match ty::get(t1).sty {
1501+
ty::ty_fn(copy f) => {
1502+
move f
1503+
}
1504+
_ => {
1505+
cx.sess.bug(~"`t_op` did not return a function type");
1506+
}
1507+
}
1508+
}
1509+
14851510
// n.b. this function is intended to eventually replace fold_region() below,
14861511
// that is why its name is so similar.
14871512
fn fold_regions(

src/librustc/middle/typeck.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,29 @@ fn require_same_types(
251251
}
252252
}
253253
254+
// a list of mapping from in-scope-region-names ("isr") to the
255+
// corresponding ty::Region
256+
type isr_alist = @List<(ty::bound_region, ty::Region)>;
257+
258+
trait get_and_find_region {
259+
fn get(br: ty::bound_region) -> ty::Region;
260+
fn find(br: ty::bound_region) -> Option<ty::Region>;
261+
}
262+
263+
impl isr_alist: get_and_find_region {
264+
fn get(br: ty::bound_region) -> ty::Region {
265+
self.find(br).get()
266+
}
267+
268+
fn find(br: ty::bound_region) -> Option<ty::Region> {
269+
for list::each(self) |isr| {
270+
let (isr_br, isr_r) = *isr;
271+
if isr_br == br { return Some(isr_r); }
272+
}
273+
return None;
274+
}
275+
}
276+
254277
fn arg_is_argv_ty(tcx: ty::ctxt, a: ty::arg) -> bool {
255278
match ty::resolved_mode(tcx, a.mode) {
256279
ast::by_val => { /*ok*/ }

src/librustc/middle/typeck/check.rs

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -166,29 +166,6 @@ fn blank_fn_ctxt(ccx: @crate_ctxt, rty: ty::t,
166166
}
167167
}
168168

169-
// a list of mapping from in-scope-region-names ("isr") to the
170-
// corresponding ty::Region
171-
type isr_alist = @List<(ty::bound_region, ty::Region)>;
172-
173-
trait get_and_find_region {
174-
fn get(br: ty::bound_region) -> ty::Region;
175-
fn find(br: ty::bound_region) -> Option<ty::Region>;
176-
}
177-
178-
impl isr_alist: get_and_find_region {
179-
fn get(br: ty::bound_region) -> ty::Region {
180-
self.find(br).get()
181-
}
182-
183-
fn find(br: ty::bound_region) -> Option<ty::Region> {
184-
for list::each(self) |isr| {
185-
let (isr_br, isr_r) = *isr;
186-
if isr_br == br { return Some(isr_r); }
187-
}
188-
return None;
189-
}
190-
}
191-
192169
fn check_item_types(ccx: @crate_ctxt, crate: @ast::crate) {
193170
let visit = visit::mk_simple_visitor(@{
194171
visit_item: |a| check_item(ccx, a),

src/librustc/middle/typeck/infer.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,5 +722,24 @@ impl infer_ctxt {
722722
self.type_error_message(sp, mk_msg, a, Some(err));
723723
}
724724

725+
fn replace_bound_regions_with_fresh_regions(
726+
&self, span: span,
727+
fty: &ty::FnTy) -> (ty::FnTy, isr_alist)
728+
{
729+
let {fn_ty, isr, _} =
730+
replace_bound_regions_in_fn_ty(self.tcx, @Nil, None, fty, |br| {
731+
// N.B.: The name of the bound region doesn't have anything to
732+
// do with the region variable that's created for it. The
733+
// only thing we're doing with `br` here is using it in the
734+
// debug message.
735+
let rvar = self.next_region_var_nb(span);
736+
debug!("Bound region %s maps to %?",
737+
bound_region_to_str(self.tcx, br),
738+
rvar);
739+
rvar
740+
});
741+
(fn_ty, isr)
742+
}
743+
725744
}
726745

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,9 @@ fn super_args<C:combine>(
290290

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

295297
match (a, b) {
296298
(ty::vstore_slice(a_r), ty::vstore_slice(b_r)) => {
@@ -517,4 +519,3 @@ fn super_tys<C:combine>(
517519
_ => Err(ty::terr_sorts(expected_found(self, a, b)))
518520
}
519521
}
520-

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

Lines changed: 96 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use lattice::*;
33
use to_str::ToStr;
44
use syntax::ast::{Many, Once};
55

6+
fn macros() { include!("macros.rs"); } // FIXME(#3114): Macro import/export.
7+
68
enum Lub = combine_fields; // "subtype", "subregion" etc
79

810
impl Lub: combine {
@@ -102,6 +104,100 @@ impl Lub: combine {
102104
}
103105
}
104106

107+
fn fns(a: &ty::FnTy, b: &ty::FnTy) -> cres<ty::FnTy> {
108+
// Note: this is a subtle algorithm. For a full explanation,
109+
// please see the large comment in `region_inference.rs`.
110+
111+
// Take a snapshot. We'll never roll this back, but in later
112+
// phases we do want to be able to examine "all bindings that
113+
// were created as part of this type comparison", and making a
114+
// snapshot is a convenient way to do that.
115+
let snapshot = self.infcx.region_vars.start_snapshot();
116+
117+
// Instantiate each bound region with a fresh region variable.
118+
let (a_with_fresh, a_isr) =
119+
self.infcx.replace_bound_regions_with_fresh_regions(
120+
self.span, a);
121+
let (b_with_fresh, _) =
122+
self.infcx.replace_bound_regions_with_fresh_regions(
123+
self.span, b);
124+
125+
// Collect constraints.
126+
let fn_ty0 = if_ok!(super_fns(&self, &a_with_fresh, &b_with_fresh));
127+
debug!("fn_ty0 = %s", fn_ty0.to_str(self.infcx));
128+
129+
// Generalize the regions appearing in fn_ty0 if possible
130+
let new_vars =
131+
self.infcx.region_vars.vars_created_since_snapshot(snapshot);
132+
let fn_ty1 =
133+
ty::apply_op_on_t_to_ty_fn(
134+
self.infcx.tcx, &fn_ty0,
135+
|t| ty::fold_regions(
136+
self.infcx.tcx, t,
137+
|r, _in_fn| generalize_region(&self, snapshot,
138+
new_vars, a_isr, r)));
139+
return Ok(move fn_ty1);
140+
141+
fn generalize_region(self: &Lub,
142+
snapshot: uint,
143+
new_vars: &[RegionVid],
144+
a_isr: isr_alist,
145+
r0: ty::Region) -> ty::Region {
146+
// Regions that pre-dated the LUB computation stay as they are.
147+
if !is_new_var(new_vars, r0) {
148+
debug!("generalize_region(r0=%?): not new variable", r0);
149+
return r0;
150+
}
151+
152+
let tainted = self.infcx.region_vars.tainted(snapshot, r0);
153+
154+
// Variables created during LUB computation which are
155+
// *related* to regions that pre-date the LUB computation
156+
// stay as they are.
157+
if !tainted.all(|r| is_new_var(new_vars, *r)) {
158+
debug!("generalize_region(r0=%?): \
159+
non-new-variables found in %?",
160+
r0, tainted);
161+
return r0;
162+
}
163+
164+
// Otherwise, the variable must be associated with at
165+
// least one of the variables representing bound regions
166+
// in both A and B. Replace the variable with the "first"
167+
// bound region from A that we find it to be associated
168+
// with.
169+
for list::each(a_isr) |pair| {
170+
let (a_br, a_r) = *pair;
171+
if tainted.contains(&a_r) {
172+
debug!("generalize_region(r0=%?): \
173+
replacing with %?, tainted=%?",
174+
r0, a_br, tainted);
175+
return ty::re_bound(a_br);
176+
}
177+
}
178+
179+
self.infcx.tcx.sess.span_bug(
180+
self.span,
181+
fmt!("Region %? is not associated with \
182+
any bound region from A!", r0));
183+
}
184+
185+
fn is_new_var(new_vars: &[RegionVid], r: ty::Region) -> bool {
186+
match r {
187+
ty::re_infer(ty::ReVar(ref v)) => new_vars.contains(v),
188+
_ => false
189+
}
190+
}
191+
}
192+
193+
fn fn_metas(a: &ty::FnMeta, b: &ty::FnMeta) -> cres<ty::FnMeta> {
194+
super_fn_metas(&self, a, b)
195+
}
196+
197+
fn fn_sigs(a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
198+
super_fn_sigs(&self, a, b)
199+
}
200+
105201
// Traits please (FIXME: #2794):
106202

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

128-
fn fns(a: &ty::FnTy, b: &ty::FnTy) -> cres<ty::FnTy> {
129-
super_fns(&self, a, b)
130-
}
131-
132-
fn fn_metas(a: &ty::FnMeta, b: &ty::FnMeta) -> cres<ty::FnMeta> {
133-
super_fn_metas(&self, a, b)
134-
}
135-
136-
fn fn_sigs(a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
137-
super_fn_sigs(&self, a, b)
138-
}
139-
140224
fn substs(did: ast::def_id,
141225
as_: &ty::substs,
142226
bs: &ty::substs) -> cres<ty::substs> {

0 commit comments

Comments
 (0)