wip: refactor repr of regions

- we now distinguish bound/free parameters (see region-param
  test case for why this is necessary)
- we also track bounds on region variables
- also, restructure fold_ty() to have multiple variants without
  duplication instead of one overloaded folder.  This also allows
  for using block functions.
This commit is contained in:
Niko Matsakis 2012-04-01 14:28:30 -07:00
parent d961f054c5
commit c0d61795de
12 changed files with 1349 additions and 958 deletions

View File

@ -265,7 +265,9 @@ fn parse_ty(st: @pstate, conv: conv_did) -> ty::t {
st.pos = st.pos + 1u;
ret ty::mk_res(st.tcx, def, inner, params);
}
'X' { ret ty::mk_var(st.tcx, parse_int(st)); }
'X' {
ret ty::mk_var(st.tcx, ty::ty_vid(parse_int(st) as uint));
}
'Y' { ret ty::mk_type(st.tcx); }
'C' {
let ck = alt check next(st) {

View File

@ -5,6 +5,7 @@ import std::map::hashmap;
import syntax::ast::*;
import driver::session::session;
import middle::ty;
import middle::ty::vid;
import syntax::print::pprust::*;
import middle::trans::reachable;
@ -99,23 +100,46 @@ fn enc_mt(w: io::writer, cx: @ctxt, mt: ty::mt) {
}
enc_ty(w, cx, mt.ty);
}
fn enc_bound_region(w: io::writer, br: ty::bound_region) {
alt br {
ty::br_self { w.write_char('s') }
ty::br_anon { w.write_char('a') }
ty::br_param(id, s) {
w.write_char('[');
w.write_uint(id);
w.write_char('|');
w.write_str(s);
w.write_char(']')
}
}
}
fn enc_region(w: io::writer, r: ty::region) {
alt r {
ty::re_block(nid) {
w.write_char('b'); w.write_int(nid); w.write_char('|');
}
ty::re_self {
w.write_char('s');
}
ty::re_inferred {
w.write_char('i');
}
ty::re_param(id) {
w.write_char('p'); w.write_uint(id); w.write_char('|');
}
ty::re_var(id) {
w.write_char('v'); w.write_uint(id); w.write_char('|');
}
ty::re_bound(br) {
w.write_char('b');
enc_bound_region(w, br);
}
ty::re_free(id, br) {
w.write_char('f');
w.write_char('[');
w.write_int(id);
w.write_char('|');
enc_bound_region(w, br);
w.write_char(']');
}
ty::re_scope(nid) {
w.write_char('s');
w.write_int(nid);
w.write_char('|');
}
ty::re_default {
w.write_char('i');
}
ty::re_var(id) {
w.write_char('v');
w.write_uint(id.to_uint());
w.write_char('|');
}
}
}
fn enc_sty(w: io::writer, cx: @ctxt, st: ty::sty) {
@ -199,7 +223,10 @@ fn enc_sty(w: io::writer, cx: @ctxt, st: ty::sty) {
for t: ty::t in tps { enc_ty(w, cx, t); }
w.write_char(']');
}
ty::ty_var(id) { w.write_char('X'); w.write_str(int::str(id)); }
ty::ty_var(id) {
w.write_char('X');
w.write_uint(id.to_uint());
}
ty::ty_param(id, did) {
w.write_char('p');
w.write_str(cx.ds(did));

View File

@ -2,11 +2,13 @@ import std::smallintmap;
import std::smallintmap::smallintmap;
import std::smallintmap::map;
import middle::ty;
import middle::ty::{ty_vid, region_vid, vid};
import syntax::ast;
import syntax::ast::{ret_style};
import util::ppaux::{ty_to_str, mt_to_str};
import result::{result, extensions, ok, err, map, map2, iter2};
import ty::type_is_bot;
import driver::session::session;
export infer_ctxt;
export new_infer_ctxt;
@ -16,29 +18,46 @@ export resolve_type_structure;
export fixup_vars;
export resolve_var;
export compare_tys;
export fixup_err, fixup_err_to_str;
type bound<T:copy> = option<T>;
type bounds<T:copy> = {lb: bound<T>, ub: bound<T>};
enum var_value<T:copy> {
redirect(uint),
enum var_value<V:copy, T:copy> {
redirect(V),
bounded(bounds<T>)
}
type vals_and_bindings<T:copy> = {
vals: smallintmap<var_value<T>>,
mut bindings: [(uint, var_value<T>)]
type vals_and_bindings<V:copy, T:copy> = {
vals: smallintmap<var_value<V, T>>,
mut bindings: [(V, var_value<V, T>)]
};
enum infer_ctxt = @{
tcx: ty::ctxt,
vb: vals_and_bindings<ty::t>,
rb: vals_and_bindings<ty::region>,
vb: vals_and_bindings<ty::ty_vid, ty::t>,
rb: vals_and_bindings<ty::region_vid, ty::region>,
};
enum fixup_err {
unresolved_ty(ty_vid),
cyclic_ty(ty_vid),
unresolved_region(region_vid),
cyclic_region(region_vid)
}
fn fixup_err_to_str(f: fixup_err) -> str {
alt f {
unresolved_ty(_) { "unconstrained type" }
cyclic_ty(_) { "cyclic type of infinite size" }
unresolved_region(_) { "unconstrained region" }
cyclic_region(_) { "cyclic region" }
}
}
type ures = result::result<(), ty::type_err>;
type fres<T> = result::result<T,int>;
type fres<T> = result::result<T, fixup_err>;
fn new_infer_ctxt(tcx: ty::ctxt) -> infer_ctxt {
infer_ctxt(@{tcx: tcx,
@ -74,12 +93,12 @@ fn resolve_type_structure(cx: infer_ctxt, a: ty::t) -> fres<ty::t> {
cx.resolve_ty(a)
}
fn resolve_var(cx: infer_ctxt, vid: int) -> fres<ty::t> {
cx.fixup_vars(ty::mk_var(cx.tcx, vid))
fn resolve_var(cx: infer_ctxt, vid: ty_vid) -> fres<ty::t> {
cx.fixup_ty(ty::mk_var(cx.tcx, vid))
}
fn fixup_vars(cx: infer_ctxt, a: ty::t) -> fres<ty::t> {
cx.fixup_vars(a)
cx.fixup_ty(a)
}
impl methods for ures {
@ -120,7 +139,7 @@ impl<V:copy to_str> of to_str for bound<V> {
}
}
impl<V:copy to_str> of to_str for bounds<V> {
impl<T:copy to_str> of to_str for bounds<T> {
fn to_str(cx: infer_ctxt) -> str {
#fmt["{%s <: %s}",
self.lb.to_str(cx),
@ -128,15 +147,49 @@ impl<V:copy to_str> of to_str for bounds<V> {
}
}
impl<V:copy to_str> of to_str for var_value<V> {
impl<V:copy vid, T:copy to_str> of to_str for var_value<V,T> {
fn to_str(cx: infer_ctxt) -> str {
alt self {
redirect(id) { #fmt("redirect(%u)", id) }
redirect(vid) { #fmt("redirect(%s)", vid.to_str()) }
bounded(bnds) { #fmt("bounded(%s)", bnds.to_str(cx)) }
}
}
}
iface st {
fn st(infcx: infer_ctxt, b: self) -> ures;
fn lub(infcx: infer_ctxt, b: self) -> cres<self>;
fn glb(infcx: infer_ctxt, b: self) -> cres<self>;
}
impl of st for ty::t {
fn st(infcx: infer_ctxt, b: ty::t) -> ures {
infcx.tys(self, b)
}
fn lub(infcx: infer_ctxt, b: ty::t) -> cres<ty::t> {
lub(infcx).c_tys(self, b)
}
fn glb(infcx: infer_ctxt, b: ty::t) -> cres<ty::t> {
glb(infcx).c_tys(self, b)
}
}
impl of st for ty::region {
fn st(infcx: infer_ctxt, b: ty::region) -> ures {
infcx.regions(self, b)
}
fn lub(infcx: infer_ctxt, b: ty::region) -> cres<ty::region> {
lub(infcx).c_regions(self, b)
}
fn glb(infcx: infer_ctxt, b: ty::region) -> cres<ty::region> {
glb(infcx).c_regions(self, b)
}
}
// Most of these methods, like tys() and so forth, take two parameters
// a and b and they are tasked with "ensuring that a is a subtype of
// b". They return success or failure. They make changes in-place to
@ -157,31 +210,20 @@ impl unify_methods for infer_ctxt {
err(e)
}
fn set<T:copy to_str>(
vb: vals_and_bindings<T>, vid: uint,
+new_v: var_value<T>) {
fn set<V:copy vid, T:copy to_str>(
vb: vals_and_bindings<V, T>, vid: V,
+new_v: var_value<V, T>) {
let old_v = vb.vals.get(vid);
let old_v = vb.vals.get(vid.to_uint());
vec::push(vb.bindings, (vid, old_v));
vb.vals.insert(vid, new_v);
vb.vals.insert(vid.to_uint(), new_v);
#debug["Updating variable <%u> from %s to %s",
vid, old_v.to_str(self), new_v.to_str(self)];
#debug["Updating variable %s from %s to %s",
vid.to_str(), old_v.to_str(self), new_v.to_str(self)];
}
fn set_ty(vid: uint, +new_v: var_value<ty::t>) {
let old_v = self.vb.vals.get(vid);
fn set_ty(vid: ty_vid, +new_v: var_value<ty_vid, ty::t>) {
self.set(self.vb, vid, new_v);
#debug["Updating variable <T%u> from %s to %s",
vid, old_v.to_str(self), new_v.to_str(self)];
}
fn rollback_to<T:copy>(vb: vals_and_bindings<T>, len: uint) {
while vb.bindings.len() != len {
let (vid, old_v) = vec::pop(vb.bindings);
vb.vals.insert(vid, old_v);
}
}
fn commit<T:copy,E:copy>(f: fn() -> result<T,E>) -> result<T,E> {
@ -203,10 +245,12 @@ impl unify_methods for infer_ctxt {
fn try<T:copy,E:copy>(f: fn() -> result<T,E>) -> result<T,E> {
fn rollback_to<T:copy>(vb: vals_and_bindings<T>, len: uint) {
fn rollback_to<V:copy vid, T:copy>(
vb: vals_and_bindings<V, T>, len: uint) {
while vb.bindings.len() != len {
let (vid, old_v) = vec::pop(vb.bindings);
vb.vals.insert(vid, old_v);
vb.vals.insert(vid.to_uint(), old_v);
}
}
@ -225,19 +269,20 @@ impl unify_methods for infer_ctxt {
ret r;
}
fn get<T:copy>(vb: vals_and_bindings<T>, vid: uint)
-> {root: uint, bounds:bounds<T>} {
fn get<V:copy vid, T:copy>(
vb: vals_and_bindings<V, T>, vid: V)
-> {root: V, bounds:bounds<T>} {
alt vb.vals.find(vid) {
alt vb.vals.find(vid.to_uint()) {
none {
let bnds = {lb: none, ub: none};
vb.vals.insert(vid, bounded(bnds));
vb.vals.insert(vid.to_uint(), bounded(bnds));
{root: vid, bounds: bnds}
}
some(redirect(vid)) {
let {root, bounds} = self.get(vb, vid);
if root != vid {
vb.vals.insert(vid, redirect(root));
vb.vals.insert(vid.to_uint(), redirect(root));
}
{root: root, bounds: bounds}
}
@ -247,11 +292,15 @@ impl unify_methods for infer_ctxt {
}
}
fn get_var(vid: uint) -> {root: uint, bounds:bounds<ty::t>} {
fn get_var(vid: ty_vid)
-> {root: ty_vid, bounds:bounds<ty::t>} {
ret self.get(self.vb, vid);
}
fn get_region(rid: uint) -> {root: uint, bounds:bounds<ty::region>} {
fn get_region(rid: region_vid)
-> {root: region_vid, bounds:bounds<ty::region>} {
ret self.get(self.rb, rid);
}
@ -304,8 +353,9 @@ impl unify_methods for infer_ctxt {
// a.lb <: c.lb
// b.lb <: c.lb
// If this cannot be achieved, the result is failure.
fn set_ty_var_to_merged_bounds(
v_id: uint, a: bounds<ty::t>, b: bounds<ty::t>) -> ures {
fn set_var_to_merged_bounds<V:copy vid, T:copy to_str st>(
vb: vals_and_bindings<V, T>,
v_id: V, a: bounds<T>, b: bounds<T>) -> ures {
// Think of the two diamonds, we want to find the
// intersection. There are basically four possibilities (you
@ -322,8 +372,8 @@ impl unify_methods for infer_ctxt {
// A \ / A
// B
#debug["merge(<T%u>,%s,%s)",
v_id,
#debug["merge(%s,%s,%s)",
v_id.to_str(),
a.to_str(self),
b.to_str(self)];
@ -335,33 +385,33 @@ impl unify_methods for infer_ctxt {
// when necessary.
self.bnds(a.lb, b.ub).then {||
self.bnds(b.lb, a.ub).then {||
self.merge_bnds(
a, b,
{|a_ty, b_ty| lub(self).c_tys(a_ty, b_ty) },
{|a_ty, b_ty| glb(self).c_tys(a_ty, b_ty) }).chain {|bnds|
#debug["merge(<T%u>): bnds=%s",
v_id,
self.merge_bnd(a.ub, b.ub, {|x, y| x.glb(self, y)}).chain {|ub|
self.merge_bnd(a.lb, b.lb, {|x, y| x.lub(self, y)}).chain {|lb|
let bnds = {lb: lb, ub: ub};
#debug["merge(%s): bnds=%s",
v_id.to_str(),
bnds.to_str(self)];
// the new bounds must themselves
// be relatable:
self.bnds(bnds.lb, bnds.ub).then {||
self.set_ty(v_id, bounded(bnds));
self.set(vb, v_id, bounded(bnds));
self.uok()
}
}}}
}}}}
}
// TODO: Generalize to regions.
fn vars(a_id: uint, b_id: uint) -> ures {
// Need to make sub_id a subtype of sup_id.
let {root: a_id, bounds: a_bounds} = self.get(self.vb, a_id);
let {root: b_id, bounds: b_bounds} = self.get(self.vb, b_id);
fn vars<V:copy vid, T:copy to_str st>(
vb: vals_and_bindings<V, T>,
a_id: V, b_id: V) -> ures {
#debug["vars(<T%u>=%s <: <T%u>=%s)",
a_id, a_bounds.to_str(self),
b_id, b_bounds.to_str(self)];
// Need to make sub_id a subtype of sup_id.
let {root: a_id, bounds: a_bounds} = self.get(vb, a_id);
let {root: b_id, bounds: b_bounds} = self.get(vb, b_id);
#debug["vars(%s=%s <: %s=%s)",
a_id.to_str(), a_bounds.to_str(self),
b_id.to_str(), b_bounds.to_str(self)];
if a_id == b_id { ret self.uok(); }
@ -369,7 +419,7 @@ impl unify_methods for infer_ctxt {
// see if we can make those types subtypes.
alt (a_bounds.ub, b_bounds.lb) {
(some(a_ub), some(b_lb)) {
let r = self.try {|| self.tys(a_ub, b_lb) };
let r = self.try {|| a_ub.st(self, b_lb) };
alt r {
ok(()) { ret result::ok(()); }
err(_) { /*fallthrough */ }
@ -380,38 +430,81 @@ impl unify_methods for infer_ctxt {
// For max perf, we should consider the rank here. But for now,
// we always make b redirect to a.
self.set_ty(b_id, redirect(a_id));
self.set(vb, b_id, redirect(a_id));
// Otherwise, we need to merge A and B so as to guarantee that
// A remains a subtype of B. Actually, there are other options,
// but that's the route we choose to take.
self.set_ty_var_to_merged_bounds(a_id, a_bounds, b_bounds).then {||
self.set_var_to_merged_bounds(vb, a_id, a_bounds, b_bounds).then {||
self.uok()
}
}
fn varty(a_id: uint, b: ty::t) -> ures {
let {root: a_id, bounds: a_bounds} = self.get(self.vb, a_id);
#debug["varty(<T%u>=%s <: %s)",
a_id, a_bounds.to_str(self),
fn vart<V: copy vid, T: copy to_str st>(
vb: vals_and_bindings<V, T>,
a_id: V, b: T) -> ures {
let {root: a_id, bounds: a_bounds} = self.get(vb, a_id);
#debug["vart(%s=%s <: %s)",
a_id.to_str(), a_bounds.to_str(self),
b.to_str(self)];
let b_bounds = {lb: none, ub: some(b)};
self.set_ty_var_to_merged_bounds(a_id, a_bounds, b_bounds)
self.set_var_to_merged_bounds(vb, a_id, a_bounds, b_bounds)
}
fn tyvar(a: ty::t, b_id: uint) -> ures {
fn tvar<V: copy vid, T: copy to_str st>(
vb: vals_and_bindings<V, T>,
a: T, b_id: V) -> ures {
let a_bounds = {lb: some(a), ub: none};
let {root: b_id, bounds: b_bounds} = self.get(self.vb, b_id);
#debug["tyvar(%s <: <T%u>=%s)",
let {root: b_id, bounds: b_bounds} = self.get(vb, b_id);
#debug["tvar(%s <: %s=%s)",
a.to_str(self),
b_id, b_bounds.to_str(self)];
self.set_ty_var_to_merged_bounds(b_id, a_bounds, b_bounds)
b_id.to_str(), b_bounds.to_str(self)];
self.set_var_to_merged_bounds(vb, b_id, a_bounds, b_bounds)
}
fn regions(a: ty::region, b: ty::region) -> ures {
alt combine_or_unify_regions(self.tcx, a, b, false) {
ok(_) { self.uok() }
err(e) { self.uerr(e) }
alt (a, b) { // XXX
(ty::re_var(a_id), ty::re_var(b_id)) {
self.vars(self.rb, a_id, b_id)
}
(ty::re_var(a_id), _) {
self.vart(self.rb, a_id, b)
}
(_, ty::re_var(b_id)) {
self.tvar(self.rb, a, b_id)
}
(ty::re_free(a_id, _), ty::re_scope(b_id)) |
(ty::re_scope(a_id), ty::re_free(b_id, _)) |
(ty::re_scope(a_id), ty::re_scope(b_id)) {
let rm = self.tcx.region_map;
alt region::nearest_common_ancestor(rm, a_id, b_id) {
some(r_id) if r_id == a_id { self.uok() }
_ { err(ty::terr_regions_differ(false, b, a)) }
}
}
// For these types, we cannot define any additional relationship:
(ty::re_free(_, _), ty::re_free(_, _)) |
(ty::re_bound(_), ty::re_bound(_)) |
(ty::re_bound(_), ty::re_free(_, _)) |
(ty::re_bound(_), ty::re_scope(_)) |
(ty::re_free(_, _), ty::re_bound(_)) |
(ty::re_scope(_), ty::re_bound(_)) {
if a == b {
self.uok()
} else {
err(ty::terr_regions_differ(false, b, a))
}
}
(ty::re_default, _) |
(_, ty::re_default) {
// actually a compiler bug, I think.
err(ty::terr_regions_differ(false, b, a))
}
}
}
@ -557,11 +650,10 @@ impl unify_methods for infer_ctxt {
ret self.uok();
}
// TODO: Generalize this.
fn bnds(a: bound<ty::t>, b: bound<ty::t>) -> ures {
#debug("bnds(%s <: %s)",
a.to_str(self),
b.to_str(self));
fn bnds<T:copy to_str st>(
a: bound<T>, b: bound<T>) -> ures {
#debug("bnds(%s <: %s)", a.to_str(self), b.to_str(self));
alt (a, b) {
(none, none) |
@ -570,7 +662,7 @@ impl unify_methods for infer_ctxt {
self.uok()
}
(some(t_a), some(t_b)) {
self.tys(t_a, t_b)
t_a.st(self, t_b)
}
}
}
@ -605,13 +697,13 @@ impl unify_methods for infer_ctxt {
(ty::ty_bot, _) { self.uok() }
(ty::ty_var(a_id), ty::ty_var(b_id)) {
self.vars(a_id as uint, b_id as uint)
self.vars(self.vb, a_id, b_id)
}
(ty::ty_var(a_id), _) {
self.varty(a_id as uint, b)
self.vart(self.vb, a_id, b)
}
(_, ty::ty_var(b_id)) {
self.tyvar(a, b_id as uint)
self.tvar(self.vb, a, b_id)
}
(ty::ty_nil, _) |
@ -700,19 +792,19 @@ impl resolve_methods for infer_ctxt {
ok(t)
}
fn rerr<T>(v: int) -> fres<T> {
fn rerr<T>(v: fixup_err) -> fres<T> {
#debug["Resolve error: %?", v];
err(v)
}
fn resolve_var<T:copy to_str>(
vb: vals_and_bindings<T>, bot_guard: fn(T)->bool,
vid: int, unbound: fn() -> fres<T>) -> fres<T> {
fn resolve_var<V: copy vid, T:copy to_str>(
vb: vals_and_bindings<V, T>, bot_guard: fn(T)->bool,
vid: V, unbound: fn() -> fres<T>) -> fres<T> {
let {root:_, bounds} = self.get(vb, vid as uint);
let {root:_, bounds} = self.get(vb, vid);
#debug["resolve_var(%d) bounds=%s",
vid, bounds.to_str(self)];
#debug["resolve_var(%s) bounds=%s",
vid.to_str(), bounds.to_str(self)];
// Nonobvious: prefer the most specific type
// (i.e., the lower bound) to the more general
@ -728,7 +820,7 @@ impl resolve_methods for infer_ctxt {
}
}
fn resolve_ty_var(vid: int) -> fres<ty::t> {
fn resolve_ty_var(vid: ty_vid) -> fres<ty::t> {
ret self.resolve_var(
self.vb,
{|t| type_is_bot(t) },
@ -736,19 +828,19 @@ impl resolve_methods for infer_ctxt {
{|| ok(ty::mk_bot(self.tcx)) });
}
fn resolve_region_var(rid: int) -> fres<ty::region> {
fn resolve_region_var(rid: region_vid) -> fres<ty::region> {
ret self.resolve_var(
self.rb,
{|_t| false },
rid,
{|| err(rid) });
{|| err(unresolved_region(rid)) });
}
fn resolve_ty(typ: ty::t) -> fres<ty::t> {
alt ty::get(typ).struct {
ty::ty_var(vid) { self.resolve_ty_var(vid) }
ty::ty_rptr(ty::re_var(rid), base_ty) {
alt self.resolve_region_var(rid as int) {
alt self.resolve_region_var(rid) {
err(terr) { err(terr) }
ok(region) {
self.rok(ty::mk_rptr(self.tcx, region, base_ty))
@ -759,95 +851,76 @@ impl resolve_methods for infer_ctxt {
}
}
fn subst_vars(unresolved: @mut option<int>,
vars_seen: std::list::list<int>,
vid: int) -> ty::t {
// Should really return a fixup_result instead of a t, but fold_ty
// doesn't allow returning anything but a t.
alt self.resolve_ty_var(vid) {
err(vid) {
*unresolved = some(vid);
ret ty::mk_var(self.tcx, vid);
fn fixup_region(r: ty::region,
&r_seen: [region_vid],
err: @mut option<fixup_err>) -> ty::region {
alt r {
ty::re_var(rid) if vec::contains(r_seen, rid) {
*err = some(cyclic_region(rid)); r
}
ok(rt) {
let mut give_up = false;
std::list::iter(vars_seen) {|v|
if v == vid {
*unresolved = some(-1); // hack: communicate inf ty
give_up = true;
}
}
// Return the type unchanged, so we can error out
// downstream
if give_up { ret rt; }
ret ty::fold_ty(self.tcx,
ty::fm_var(
self.subst_vars(
unresolved,
std::list::cons(vid, @vars_seen),
_)),
rt);
}
}
}
fn fixup_vars(typ: ty::t) -> fres<ty::t> {
let unresolved = @mut none::<int>;
let rty =
ty::fold_ty(self.tcx,
ty::fm_var(
self.subst_vars(
unresolved,
std::list::nil,
_)),
typ);
let ur = *unresolved;
alt ur {
none { ret self.rok(rty); }
some(var_id) { ret self.rerr(var_id); }
}
}
fn subst_regions(unresolved: @mut option<int>,
regions_seen: std::list::list<int>,
rid: int) -> ty::region {
// Should really return a fixup_result instead of a t, but fold_ty
// doesn't allow returning anything but a t.
alt self.resolve_region_var(rid) {
err(rid) {
*unresolved = some(rid);
ret ty::re_var(rid as uint);
}
ok(rr) {
let mut give_up = false;
std::list::iter(regions_seen) {|r|
if r == rid {
*unresolved = some(-1); // hack: communicate inf region
give_up = true;
}
}
ret rr;
}
}
}
fn fixup_regions(typ: ty::t) -> fres<ty::t> {
let unresolved = @mut none::<int>;
let rty = ty::fold_ty(self.tcx, ty::fm_rptr({ |region, _under_rptr|
alt region {
ty::re_var(rid) {
self.subst_regions(unresolved, std::list::nil, rid as int)
ty::re_var(rid) {
alt self.resolve_region_var(rid) {
result::ok(r1) {
vec::push(r_seen, rid);
let r2 = self.fixup_region(r1, r_seen, err);
vec::pop(r_seen);
ret r2;
}
_ { region }
result::err(e) { *err = some(e); r }
}
}, false), typ);
}
let ur = *unresolved;
alt ur {
_ { r }
}
}
fn fixup_ty1(ty: ty::t,
&ty_seen: [ty_vid],
&r_seen: [region_vid],
err: @mut option<fixup_err>) -> ty::t {
let tb = ty::get(ty);
if !tb.has_vars { ret ty; }
alt tb.struct {
ty::ty_var(vid) if vec::contains(ty_seen, vid) {
*err = some(cyclic_ty(vid)); ty
}
ty::ty_var(vid) {
alt self.resolve_ty_var(vid) {
result::err(e) { *err = some(e); ty }
result::ok(ty1) {
vec::push(ty_seen, vid);
let ty2 = self.fixup_ty1(ty1, ty_seen, r_seen, err);
vec::pop(ty_seen);
ret ty2;
}
}
}
ty::ty_rptr(r, {ty: base_ty, mutbl: m}) {
let base_ty1 = self.fixup_ty1(base_ty, ty_seen, r_seen, err);
let r1 = self.fixup_region(r, r_seen, err);
ret ty::mk_rptr(self.tcx, r1, {ty: base_ty1, mutbl: m});
}
sty {
ty::fold_sty_to_ty(self.tcx, sty) {|t|
self.fixup_ty1(t, ty_seen, r_seen, err)
}
}
}
}
fn fixup_ty(typ: ty::t) -> fres<ty::t> {
#debug["fixup_ty(%s)", ty_to_str(self.tcx, typ)];
let mut ty_seen = [];
let mut r_seen = [];
let unresolved = @mut none;
let rty = self.fixup_ty1(typ, ty_seen, r_seen, unresolved);
alt *unresolved {
none { ret self.rok(rty); }
some(var_id) { ret self.rerr(var_id); }
some(e) { ret self.rerr(e); }
}
}
}
@ -868,6 +941,10 @@ impl resolve_methods for infer_ctxt {
// instance as the first parameter. This would be better implemented
// using traits.
//
// The `c_X()` top-level items work for *both LUB and GLB*: any
// operation which varies between LUB and GLB will be dynamically
// dispatched using a `self.c_Y()` operation.
//
// In principle, the subtyping relation computed above could be built
// on the combine framework---this would result in less code but would
// be less efficient. There is a significant performance gain from
@ -884,42 +961,55 @@ iface combine {
fn bnd<V:copy>(b: bounds<V>) -> option<V>;
fn with_bnd<V:copy>(b: bounds<V>, v: V) -> bounds<V>;
fn c_bot(b: ty::t) -> cres<ty::t>;
fn c_regions(a: ty::region, b: ty::region) -> cres<ty::region>;
fn c_mts(a: ty::mt, b: ty::mt) -> cres<ty::mt>;
fn c_contratys(t1: ty::t, t2: ty::t) -> cres<ty::t>;
fn c_tys(t1: ty::t, t2: ty::t) -> cres<ty::t>;
fn c_protos(p1: ast::proto, p2: ast::proto) -> cres<ast::proto>;
fn c_ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style>;
// Combining regions (along with some specific cases that are
// different for LUB/GLB):
fn c_regions(
a: ty::region, b: ty::region) -> cres<ty::region>;
fn c_regions_scope_scope(
a: ty::region, a_id: ast::node_id,
b: ty::region, b_id: ast::node_id) -> cres<ty::region>;
fn c_regions_free_scope(
a: ty::region, a_id: ast::node_id, a_br: ty::bound_region,
b: ty::region, b_id: ast::node_id) -> cres<ty::region>;
}
enum lub = infer_ctxt;
enum glb = infer_ctxt;
fn c_ty_vars<C:combine>(self: C, a_id: uint, b_id: uint) -> cres<ty::t> {
fn c_vars<V:copy vid, C:combine, T:copy to_str st>(
self: C, vb: vals_and_bindings<V, T>,
a_t: T, a_vid: V, b_vid: V,
c_ts: fn(T, T) -> cres<T>) -> cres<T> {
// The comments in this function are written for LUB and types,
// but they apply equally well to GLB and regions if you inverse
// upper/lower/sub/super/etc.
// Need to find a type that is a supertype of both a and b:
let {root: a_id, bounds: a_bounds} = self.infcx().get_var(a_id);
let {root: b_id, bounds: b_bounds} = self.infcx().get_var(b_id);
let {root: a_vid, bounds: a_bounds} = self.infcx().get(vb, a_vid);
let {root: b_vid, bounds: b_bounds} = self.infcx().get(vb, b_vid);
#debug["%s.c_ty_vars(<T%u>=%s <: <T%u>=%s)",
#debug["%s.c_vars(%s=%s <: %s=%s)",
self.tag(),
a_id, a_bounds.to_str(self.infcx()),
b_id, b_bounds.to_str(self.infcx())];
a_vid.to_str(), a_bounds.to_str(self.infcx()),
b_vid.to_str(), b_bounds.to_str(self.infcx())];
let tcx = self.infcx().tcx;
if a_id == b_id {
ret ok(ty::mk_var(tcx, a_id as int));
if a_vid == b_vid {
ret ok(a_t);
}
// The comments in this function are written for LUB, but they
// apply equally well to GLB if you inverse upper/lower/sub/super/etc.
// If both A and B have an UB type, then we can just compute the
// LUB of those types:
let a_bnd = self.bnd(a_bounds), b_bnd = self.bnd(b_bounds);
alt (a_bnd, b_bnd) {
(some(a_ty), some(b_ty)) {
alt self.infcx().try {|| self.c_tys(a_ty, b_ty) } {
alt self.infcx().try {|| c_ts(a_ty, b_ty) } {
ok(t) { ret ok(t); }
err(_) { /*fallthrough */ }
}
@ -929,33 +1019,37 @@ fn c_ty_vars<C:combine>(self: C, a_id: uint, b_id: uint) -> cres<ty::t> {
// Otherwise, we need to merge A and B into one variable. We can
// then use either variable as an upper bound:
self.infcx().vars(a_id, b_id).then {||
ok(ty::mk_var(tcx, a_id as int))
self.infcx().vars(vb, a_vid, b_vid).then {||
ok(a_t)
}
}
fn c_ty_var_ty<C:combine>(self: C, a_id: uint, b: ty::t) -> cres<ty::t> {
let {root: a_id, bounds: a_bounds} = self.infcx().get_var(a_id);
fn c_var_t<V:copy vid, C:combine, T:copy to_str st>(
self: C, vb: vals_and_bindings<V, T>,
a_vid: V, b: T,
c_ts: fn(T, T) -> cres<T>) -> cres<T> {
let {root: a_id, bounds: a_bounds} = self.infcx().get(vb, a_vid);
// The comments in this function are written for LUB, but they
// apply equally well to GLB if you inverse upper/lower/sub/super/etc.
#debug["%s.c_ty_var_ty(<T%u>=%s <: %s)",
#debug["%s.c_var_ty(%s=%s <: %s)",
self.tag(),
a_id, a_bounds.to_str(self.infcx()),
a_id.to_str(), a_bounds.to_str(self.infcx()),
b.to_str(self.infcx())];
alt self.bnd(a_bounds) {
some(a_ty) {
some(a_bnd) {
// If a has an upper bound, return it.
ret self.c_tys(a_ty, b);
ret c_ts(a_bnd, b);
}
none {
// If a does not have an upper bound, make b the upper bound of a
// and then return b.
let a_bounds = self.with_bnd(a_bounds, b);
self.infcx().bnds(a_bounds.lb, a_bounds.ub).then {||
self.infcx().set_ty(a_id, bounded(a_bounds));
self.infcx().set(vb, a_id, bounded(a_bounds));
ok(b)
}
}
@ -1070,15 +1164,21 @@ fn c_tys<C:combine>(
(_, ty::ty_bot) { self.c_bot(b) }
(ty::ty_var(a_id), ty::ty_var(b_id)) {
c_ty_vars(self, a_id as uint, b_id as uint)
c_vars(self, self.infcx().vb,
a, a_id, b_id,
{|x, y| self.c_tys(x, y) })
}
// Note that the LUB/GLB operations are commutative:
(ty::ty_var(a_id), _) {
c_ty_var_ty(self, a_id as uint, b)
(ty::ty_var(v_id), _) {
c_var_t(self, self.infcx().vb,
v_id, b,
{|x, y| self.c_tys(x, y) })
}
(_, ty::ty_var(b_id)) {
c_ty_var_ty(self, b_id as uint, a)
(_, ty::ty_var(v_id)) {
c_var_t(self, self.infcx().vb,
v_id, a,
{|x, y| self.c_tys(x, y) })
}
(ty::ty_nil, _) |
@ -1192,43 +1292,61 @@ fn c_tys<C:combine>(
}
}
fn combine_or_unify_regions(tcx: ty::ctxt,
a: ty::region,
b: ty::region,
contravariant_combine: bool) -> cres<ty::region> {
fn c_regions<C:combine>(
self: C, a: ty::region, b: ty::region) -> cres<ty::region> {
#debug["%s.c_regions(%?, %?)",
self.tag(),
a.to_str(self.infcx()),
b.to_str(self.infcx())];
alt (a, b) {
(ty::re_var(_), _) | (_, ty::re_var(_)) {
ok(a) // FIXME: We need region variables!
(ty::re_var(a_id), ty::re_var(b_id)) {
c_vars(self, self.infcx().rb,
a, a_id, b_id,
{|x, y| self.c_regions(x, y) })
}
(ty::re_inferred, _) | (_, ty::re_inferred) {
fail "tried to combine or unify inferred regions"
(ty::re_var(v_id), r) |
(r, ty::re_var(v_id)) {
c_var_t(self, self.infcx().rb,
v_id, r,
{|x, y| self.c_regions(x, y) })
}
(ty::re_param(_), ty::re_param(_)) |
(ty::re_self, ty::re_self) {
(f @ ty::re_free(f_id, f_br), s @ ty::re_scope(s_id)) |
(s @ ty::re_scope(s_id), f @ ty::re_free(f_id, f_br)) {
self.c_regions_free_scope(f, f_id, f_br, s, s_id)
}
(ty::re_scope(a_id), ty::re_scope(b_id)) {
self.c_regions_scope_scope(a, a_id, b, b_id)
}
// For these types, we cannot define any additional relationship:
(ty::re_free(_, _), ty::re_free(_, _)) |
(ty::re_bound(_), ty::re_bound(_)) |
(ty::re_bound(_), ty::re_free(_, _)) |
(ty::re_bound(_), ty::re_scope(_)) |
(ty::re_free(_, _), ty::re_bound(_)) |
(ty::re_scope(_), ty::re_bound(_)) {
if a == b {
#debug["... yes, %s == %s.",
a.to_str(self.infcx()),
b.to_str(self.infcx())];
ok(a)
} else {
err(ty::terr_regions_differ(false, a, b))
#debug["... no, %s != %s.",
a.to_str(self.infcx()),
b.to_str(self.infcx())];
err(ty::terr_regions_differ(false, b, a))
}
}
(ty::re_param(_), ty::re_block(_)) |
(ty::re_self, ty::re_block(_)) {
ok(a)
}
(ty::re_block(_), ty::re_param(_)) |
(ty::re_block(_), ty::re_self) {
err(ty::terr_regions_differ(false, a, b))
}
(ty::re_block(block_a), ty::re_block(block_b)) {
// The region corresponding to an outer block is a subtype of the
// region corresponding to an inner block.
let rm = tcx.region_map;
let nca_opt = region::nearest_common_ancestor(rm, block_a, block_b);
alt nca_opt {
some(nca) if nca == block_b { ok(a) }
some(nca) if contravariant_combine { ok(ty::re_block(nca)) }
_ { err(ty::terr_regions_differ(false, a, b)) }
}
(ty::re_default, _) |
(_, ty::re_default) {
// actually a compiler bug, I think.
err(ty::terr_regions_differ(false, b, a))
}
}
}
@ -1319,7 +1437,29 @@ impl of combine for lub {
}
fn c_regions(a: ty::region, b: ty::region) -> cres<ty::region> {
ret combine_or_unify_regions(self.tcx, a, b, true);
ret c_regions(self, a, b);
}
fn c_regions_free_scope(
a: ty::region, _a_id: ast::node_id, _a_br: ty::bound_region,
_b: ty::region, _b_id: ast::node_id) -> cres<ty::region> {
// for LUB, the scope is within the function and the free
// region is always a parameter to the method.
ret ok(a); // NDM--not so for nested functions
}
fn c_regions_scope_scope(a: ty::region, a_id: ast::node_id,
b: ty::region, b_id: ast::node_id)
-> cres<ty::region> {
// The region corresponding to an outer block is a subtype of the
// region corresponding to an inner block.
let rm = self.infcx().tcx.region_map;
alt region::nearest_common_ancestor(rm, a_id, b_id) {
some(r_id) { ok(ty::re_scope(r_id)) }
_ { err(ty::terr_regions_differ(false, b, a)) }
}
}
}
@ -1427,6 +1567,31 @@ impl of combine for glb {
}
fn c_regions(a: ty::region, b: ty::region) -> cres<ty::region> {
ret combine_or_unify_regions(self.tcx, a, b, false);
ret c_regions(self, a, b);
}
fn c_regions_free_scope(
_a: ty::region, _a_id: ast::node_id, _a_br: ty::bound_region,
b: ty::region, _b_id: ast::node_id) -> cres<ty::region> {
// for GLB, the scope is within the function and the free
// region is always a parameter to the method. So the GLB
// must be the scope.
ret ok(b); // NDM--not so for nested functions
}
fn c_regions_scope_scope(a: ty::region, a_id: ast::node_id,
b: ty::region, b_id: ast::node_id)
-> cres<ty::region> {
// We want to generate a region that is contained by both of
// these: so, if one of these scopes is a subscope of the
// other, return it. Otherwise fail.
let rm = self.infcx().tcx.region_map;
alt region::nearest_common_ancestor(rm, a_id, b_id) {
some(r_id) if a_id == r_id { ok(b) }
some(r_id) if b_id == r_id { ok(a) }
_ { err(ty::terr_regions_differ(false, b, a)) }
}
}
}

View File

@ -1,11 +1,141 @@
/*
* Region resolution. This pass runs before typechecking and resolves region
* names to the appropriate block.
*/
Region resolution. This pass runs before typechecking and resolves region
names to the appropriate block.
This seems to be as good a place as any to explain in detail how
region naming, representation, and type check works.
### Naming and so forth
We really want regions to be very lightweight to use. Therefore,
unlike other named things, the scopes for regions are not explicitly
declared: instead, they are implicitly defined. Functions declare new
scopes: if the function is not a bare function, then as always it
inherits the names in scope from the outer scope. Within a function
declaration, new names implicitly declare new region variables. Outside
of function declarations, new names are illegal. To make this more
concrete, here is an example:
fn foo(s: &a.S, t: &b.T) {
let s1: &a.S = s; // a refers to the same a as in the decl
let t1: &c.T = t; // illegal: cannot introduce new name here
}
The code in this file is what actually handles resolving these names.
It creates a couple of maps that map from the AST node representing a
region ptr type to the resolved form of its region parameter. If new
names are introduced where they shouldn't be, then an error is
reported.
If regions are not given an explicit name, then the behavior depends
a bit on the context. Within a function declaration, all unnamed regions
are mapped to a single, anonymous parameter. That is, a function like:
fn foo(s: &S) -> &S { s }
is equivalent to a declaration like:
fn foo(s: &a.S) -> &a.S { s }
Within a function body or other non-binding context, an unnamed region
reference is mapped to a fresh region variable whose value can be
inferred as normal.
The resolved form of regions is `ty::region`. Before I can explain
why this type is setup the way it is, I have to digress a little bit
into some ill-explained type theory.
### Universal Quantification
Regions are more complex than type parameters because, unlike type
parameters, they can be universally quantified within a type. To put
it another way, you cannot (at least at the time of this writing) have
a variable `x` of type `fn<T>(T) -> T`. You can have an *item* of
type `fn<T>(T) - T`, but whenever it is referenced within a method,
that type parameter `T` is replaced with a concrete type *variable*
`$T`. To make this more concrete, imagine this code:
fn identity<T>(x: T) -> T { x }
let f = identity; // f has type fn($T) -> $T
f(3u); // $T is bound to uint
f(3); // Type error
You can see here that a type error will result because the type of `f`
(as opposed to the type of `identity`) is not universally quantified
over `$T`. That's fancy math speak for saying that the type variable
`$T` refers to a specific type that may not yet be known, unlike the
type parameter `T` which refers to some type which will never be
known.
Anyway, regions work differently. If you have an item of type
`fn(&a.T) -> &a.T` and you reference it, its type remains the same:
only when the function *is called* is `&a` instantiated with a
concrete region variable. This means you could call it twice and give
different values for `&a` each time.
This more general form is possible for regions because they do not
impact code generation. We do not need to monomorphize functions
differently just because they contain region pointers. In fact, we
don't really do *anything* differently.
### Representing regions; or, why do I care about all that?
The point of this discussion is that the representation of regions
must distinguish between a *bound* reference to a region and a *free*
reference. A bound reference is one which will be replaced with a
fresh type variable when the function is called, like the type
parameter `T` in `identity`. They can only appear within function
types. A free reference is a region that may not yet be concretely
known, like the variable `$T`.
To see why we must distinguish them carefully, consider this program:
fn item1(s: &a.S) {
let choose = fn@(s1: &a.S) -> &a.S {
if some_cond { s } else { s1 }
};
}
Here, the variable `s1: &a.S` that appears within the `fn@` is a free
reference to `a`. That is, when you call `choose()`, you don't
replace `&a` with a fresh region variable, but rather you expect `s1`
to be in the same region as the parameter `s`.
But in this program, this is not the case at all:
fn item2() {
let identity = fn@(s1: &a.S) -> &a.S { s1 };
}
To distinguish between these two cases, `ty::region` contains two
variants: `re_bound` and `re_free`. In `item1()`, the outer reference
to `&a` would be `re_bound(rid_param("a", 0u))`, and the inner reference
would be `re_free(rid_param("a", 0u))`. In `item2()`, the inner reference
would be `re_bound(rid_param("a", 0u))`.
#### Impliciations for typeck
In typeck, whenever we call a function, we must go over and replace
all references to `re_bound()` regions within its parameters with
fresh type variables (we do not, however, replace bound regions within
nested function types, as those nested functions have not yet been
called).
Also, when we typecheck the *body* of an item, we must replace all
`re_bound` references with `re_free` references. This means that the
region in the type of the argument `s` in `item1()` *within `item1()`*
is not `re_bound(re_param("a", 0u))` but rather `re_free(re_param("a",
0u))`. This is because, for any particular *invocation of `item1()`*,
`&a` will be bound to some specific region, and hence it is no longer
bound.
*/
import driver::session::session;
import middle::ty;
import syntax::{ast, visit};
import syntax::codemap::span;
import util::common::new_def_hash;
import std::list;
@ -23,10 +153,9 @@ enum parent {
}
/* Records the parameter ID of a region name. */
type binding = {
name: str,
id: uint
};
type binding = {node_id: ast::node_id,
name: str,
br: ty::bound_region};
type region_map = {
/* Mapping from a block/function expression to its parent. */
@ -44,11 +173,105 @@ type region_map = {
rvalue_to_block: hashmap<ast::node_id,ast::node_id>
};
type region_scope = @{
node_id: ast::node_id,
kind: region_scope_kind
};
enum region_scope_kind {
rsk_root,
rsk_body(region_scope),
rsk_self(region_scope),
rsk_binding(region_scope, @mut [binding])
}
fn root_scope(node_id: ast::node_id) -> region_scope {
@{node_id: node_id, kind: rsk_root}
}
impl methods for region_scope {
fn body_subscope(node_id: ast::node_id) -> region_scope {
@{node_id: node_id, kind: rsk_body(self)}
}
fn binding_subscope(node_id: ast::node_id) -> region_scope {
@{node_id: node_id, kind: rsk_binding(self, @mut [])}
}
fn self_subscope(node_id: ast::node_id) -> region_scope {
@{node_id: node_id, kind: rsk_self(self)}
}
fn find(nm: str) -> option<binding> {
alt self.kind {
rsk_root { none }
rsk_body(parent) { parent.find(nm) }
rsk_self(parent) { parent.find(nm) }
rsk_binding(parent, bs) {
alt (*bs).find({|b| b.name == nm }) {
none { parent.find(nm) }
some(b) { some(b) }
}
}
}
}
// fn resolve_anon() -> option<ty::region> {
// alt self.kind {
// rsk_root { none }
// rsk_body(_) { none }
// rsk_self(_) { none }
// rsk_binding(_, _) { ty::re_bound(ty::br_anon) }
// }
// }
fn resolve_self_helper(bound: bool) -> option<ty::region> {
alt self.kind {
rsk_root { none }
rsk_self(_) if bound { some(ty::re_bound(ty::br_self)) }
rsk_self(_) { some(ty::re_free(self.node_id, ty::br_self)) }
rsk_binding(p, _) { p.resolve_self_helper(bound) }
rsk_body(p) { p.resolve_self_helper(false) }
}
}
fn resolve_self() -> option<ty::region> {
self.resolve_self_helper(true)
}
fn resolve_ident(nm: str) -> option<ty::region> {
alt self.find(nm) {
some(b) if b.node_id == self.node_id {
some(ty::re_bound(b.br))
}
some(b) {
some(ty::re_free(b.node_id, b.br))
}
none {
alt self.kind {
rsk_self(_) | rsk_root | rsk_body(_) { none }
rsk_binding(_, bs) {
let idx = (*bs).len();
let br = ty::br_param(idx, nm);
vec::push(*bs, {node_id: self.node_id,
name: nm,
br: br});
some(ty::re_bound(br))
}
}
}
}
}
}
type ctxt = {
sess: session,
def_map: resolve::def_map,
region_map: @region_map,
mut bindings: @list<binding>,
scope: region_scope,
/*
* A list of local IDs that will be parented to the next block we
@ -63,9 +286,6 @@ type ctxt = {
/* True if we're within the pattern part of an alt, false otherwise. */
in_alt: bool,
/* True if we're within a typeclass implementation, false otherwise. */
in_typeclass: bool,
/* The next parameter ID. */
mut next_param_id: uint
};
@ -108,16 +328,28 @@ fn nearest_common_ancestor(region_map: @region_map, scope_a: ast::node_id,
let b_ancestors = ancestors_of(region_map, scope_b);
let mut a_index = vec::len(a_ancestors) - 1u;
let mut b_index = vec::len(b_ancestors) - 1u;
while a_ancestors[a_index] == b_ancestors[b_index] {
// Here, [ab]_ancestors is a vector going from narrow to broad.
// The end of each vector will be the item where the scope is
// defined; if there are any common ancestors, then the tails of
// the vector will be the same. So basically we want to walk
// backwards from the tail of each vector and find the first point
// where they diverge. If one vector is a suffix of the other,
// then the corresponding scope is a superscope of the other.
loop {
if a_ancestors[a_index] != b_ancestors[b_index] {
if a_index == a_ancestors.len() {
ret none;
} else {
ret some(a_ancestors[a_index + 1u]);
}
}
if a_index == 0u { ret some(scope_a); }
if b_index == 0u { ret some(scope_b); }
a_index -= 1u;
b_index -= 1u;
}
if a_index == vec::len(a_ancestors) {
ret none;
}
ret some(a_ancestors[a_index + 1u]);
}
fn get_inferred_region(cx: ctxt, sp: syntax::codemap::span) -> ty::region {
@ -127,14 +359,51 @@ fn get_inferred_region(cx: ctxt, sp: syntax::codemap::span) -> ty::region {
// TODO: What do we do if we're in an alt?
ret alt cx.parent {
pa_fn_item(_) | pa_nested_fn(_) {
let id = cx.next_param_id;
cx.next_param_id += 1u;
ty::re_param(id)
pa_fn_item(_) | pa_nested_fn(_) { ty::re_bound(ty::br_anon) }
pa_block(block_id) { ty::re_scope(block_id) }
pa_item(_) { ty::re_bound(ty::br_anon) }
pa_crate { cx.sess.span_bug(sp, "inferred region at crate level?!"); }
}
}
fn resolve_region_binding(cx: ctxt, span: span, region: ast::region) {
let id = region.id;
let rm = cx.region_map;
alt region.node {
ast::re_inferred {
// option::may(cx.scope.resolve_anon()) {|r|
// rm.ast_type_to_region.insert(id, r);
// }
}
ast::re_named(ident) {
alt cx.scope.resolve_ident(ident) {
some(r) {
rm.ast_type_to_region.insert(id, r);
}
none {
cx.sess.span_err(
span,
#fmt["the region `%s` is not declared", ident]);
}
}
pa_block(block_id) { ty::re_block(block_id) }
pa_item(_) { ty::re_param(0u) }
pa_crate { cx.sess.span_bug(sp, "inferred region at crate level?!"); }
}
ast::re_self {
alt cx.scope.resolve_self() {
some(r) {
rm.ast_type_to_region.insert(id, r);
}
none {
cx.sess.span_err(
span,
"the `self` region is not allowed here");
}
}
}
}
}
@ -143,65 +412,10 @@ fn resolve_ty(ty: @ast::ty, cx: ctxt, visitor: visit::vt<ctxt>) {
cx.region_map.ast_type_to_inferred_region.insert(ty.id, inferred_region);
alt ty.node {
ast::ty_rptr({id: region_id, node: node}, _) {
alt node {
ast::re_inferred { /* no-op */ }
ast::re_self {
if cx.in_typeclass {
let r = ty::re_self;
let rm = cx.region_map;
rm.ast_type_to_region.insert(region_id, r);
} else {
cx.sess.span_err(ty.span,
"the `self` region is not allowed \
here");
}
}
ast::re_named(ident) {
// If at item scope, introduce or reuse a binding. If at
// block scope, require that the binding be introduced.
let bindings = cx.bindings;
let mut region;
alt list::find(*bindings, {|b| ident == b.name}) {
some(binding) { region = ty::re_param(binding.id); }
none {
let id = cx.next_param_id;
let binding = {name: ident, id: id};
cx.next_param_id += 1u;
cx.bindings = @list::cons(binding, cx.bindings);
region = ty::re_param(id);
alt cx.parent {
pa_fn_item(fn_id) | pa_nested_fn(fn_id) {
/* ok */
}
pa_item(_) {
cx.sess.span_err(ty.span,
"named region not " +
"allowed in this " +
"context");
}
pa_block(_) {
cx.sess.span_err(ty.span,
"unknown region `" +
ident + "`");
}
pa_crate {
cx.sess.span_bug(ty.span, "named " +
"region at crate " +
"level?!");
}
}
}
}
let ast_type_to_region = cx.region_map.ast_type_to_region;
ast_type_to_region.insert(region_id, region);
}
}
}
_ { /* nothing to do */ }
ast::ty_rptr(r, _) {
resolve_region_binding(cx, ty.span, r);
}
_ { /* nothing to do */ }
}
visit::visit_ty(ty, cx, visitor);
@ -230,6 +444,7 @@ fn resolve_block(blk: ast::blk, cx: ctxt, visitor: visit::vt<ctxt>) {
// Descend.
let new_cx: ctxt = {parent: pa_block(blk.node.id),
scope: cx.scope.body_subscope(blk.node.id),
mut queued_locals: [],
in_alt: false with cx};
visit::visit_block(blk, new_cx, visitor);
@ -282,6 +497,7 @@ fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
ast::expr_fn(_, _, _, _) | ast::expr_fn_block(_, _) {
record_parent(cx, expr.id);
let new_cx = {parent: pa_nested_fn(expr.id),
scope: cx.scope.binding_subscope(expr.id),
in_alt: false with cx};
visit::visit_expr(expr, new_cx, visitor);
}
@ -312,27 +528,26 @@ fn resolve_local(local: @ast::local, cx: ctxt, visitor: visit::vt<ctxt>) {
fn resolve_item(item: @ast::item, cx: ctxt, visitor: visit::vt<ctxt>) {
// Items create a new outer block scope as far as we're concerned.
let mut parent;
let mut in_typeclass;
alt item.node {
ast::item_fn(_, _, _) | ast::item_enum(_, _) {
parent = pa_fn_item(item.id);
in_typeclass = false;
}
ast::item_impl(_, _, _, _) {
parent = pa_item(item.id);
in_typeclass = true;
}
_ {
parent = pa_item(item.id);
in_typeclass = false;
let {parent, scope} = {
alt item.node {
ast::item_fn(_, _, _) | ast::item_enum(_, _) {
{parent: pa_fn_item(item.id),
scope: cx.scope.binding_subscope(item.id)}
}
ast::item_impl(_, _, _, _) | ast::item_class(_, _, _) {
{parent: pa_item(item.id),
scope: cx.scope.self_subscope(item.id)}
}
_ {
{parent: pa_item(item.id),
scope: root_scope(item.id)}
}
}
};
let new_cx: ctxt = {bindings: @list::nil,
parent: parent,
let new_cx: ctxt = {parent: parent,
scope: scope,
in_alt: false,
in_typeclass: in_typeclass,
mut next_param_id: 0u
with cx};
@ -349,11 +564,10 @@ fn resolve_crate(sess: session, def_map: resolve::def_map, crate: @ast::crate)
ast_type_to_inferred_region:
map::int_hash(),
rvalue_to_block: map::int_hash()},
mut bindings: @list::nil,
scope: root_scope(0),
mut queued_locals: [],
parent: pa_crate,
in_alt: false,
in_typeclass: false,
mut next_param_id: 0u};
let visitor = visit::mk_vt(@{
visit_block: resolve_block,

View File

@ -26,38 +26,38 @@ fn check_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
if ty::type_has_rptrs(t) {
ty::walk_ty(t) { |t|
alt ty::get(t).struct {
ty::ty_rptr(region, _) {
alt region {
ty::re_self | ty::re_inferred | ty::re_param(_) {
/* ok */
}
ty::re_block(rbi) {
let referent_block_id = rbi;
let enclosing_block_id = alt cx.enclosing_block {
none {
cx.tcx.sess.span_bug(expr.span,
"block region " +
"type outside a " +
"block?!");
}
some(eb) { eb }
};
ty::ty_rptr(region, _) {
alt region {
ty::re_bound(_) | ty::re_free(_, _) {
/* ok */
}
ty::re_scope(rbi) {
let referent_block_id = rbi;
let enclosing_block_id = alt cx.enclosing_block {
none {
cx.tcx.sess.span_bug(expr.span,
"block region " +
"type outside a " +
"block?!");
}
some(eb) { eb }
};
if !region::scope_contains(cx.tcx.region_map,
referent_block_id,
enclosing_block_id) {
if !region::scope_contains(cx.tcx.region_map,
referent_block_id,
enclosing_block_id) {
cx.tcx.sess.span_err(expr.span, "reference " +
"escapes its block");
}
}
ty::re_var(_) {
cx.tcx.sess.span_bug(expr.span,
"unresolved region");
}
cx.tcx.sess.span_err(expr.span, "reference " +
"escapes its block");
}
}
ty::re_default | ty::re_var(_) {
cx.tcx.sess.span_bug(expr.span,
"unresolved region");
}
}
_ { /* no-op */ }
}
_ { /* no-op */ }
}
}
}

View File

@ -641,7 +641,7 @@ fn simplify_type(tcx: ty::ctxt, typ: ty::t) -> ty::t {
_ { typ }
}
}
ty::fold_ty(tcx, ty::fm_general(bind simplifier(tcx, _)), typ)
ty::fold_ty(tcx, typ) {|t| simplifier(tcx, t) }
}
// Given a tag type `ty`, returns the offset of the payload.

View File

@ -15,6 +15,8 @@ import util::ppaux::ty_to_str;
import util::ppaux::ty_constr_to_str;
import syntax::print::pprust::*;
export ty_vid, region_vid, vid;
export br_hashmap;
export is_instantiable;
export node_id_to_type;
export node_id_to_type_params;
@ -33,12 +35,11 @@ export expr_ty;
export expr_ty_params_and_ty;
export expr_is_lval;
export field_ty;
export fold_ty;
export fold_ty, fold_sty_to_ty, fold_region, fold_ty_var;
export field;
export field_idx;
export get_field;
export get_fields;
export fm_var, fm_general, fm_rptr;
export get_element_type;
export is_binopable;
export is_pred_ty;
@ -93,7 +94,7 @@ export ty_uint, mk_uint, mk_mach_uint;
export ty_uniq, mk_uniq, mk_imm_uniq, type_is_unique_box;
export ty_var, mk_var;
export ty_self, mk_self;
export region, re_block, re_param, re_var;
export region, bound_region;
export get, type_has_params, type_has_vars, type_has_rptrs, type_id;
export ty_var_id;
export ty_to_def_id;
@ -127,7 +128,6 @@ export resolved_mode;
export arg_mode;
export unify_mode;
export set_default_mode;
export unify;
export variant_info;
export walk_ty, maybe_walk_ty;
export occurs_check;
@ -261,20 +261,25 @@ type fn_ty = {proto: ast::proto,
ret_style: ret_style,
constraints: [@constr]};
// See discussion at head of region.rs
enum region {
// The region of a block.
re_block(node_id),
// The self region. Only valid inside classes and typeclass
// implementations.
re_self,
// The inferred region, which also corresponds to &self in typedefs.
re_inferred,
re_bound(bound_region),
re_free(node_id, bound_region),
re_scope(node_id),
re_var(region_vid),
re_default
}
// A region parameter.
re_param(uint),
enum bound_region {
// The `self` region for a given class/impl/iface. The defining item may
// appear in another crate.
br_self,
// A region variable.
re_var(uint)
// The anonymous region parameter for a given function.
br_anon,
// A named region parameter.
br_param(uint, str)
}
// NB: If you change this, you'll probably want to change the corresponding
@ -300,7 +305,7 @@ enum sty {
ty_res(def_id, t, [t]),
ty_tup([t]),
ty_var(int), // type variable during typechecking
ty_var(ty_vid), // type variable during typechecking
ty_param(uint, def_id), // type parameter
ty_self([t]), // interface method self type
@ -344,6 +349,24 @@ enum param_bound {
bound_iface(t),
}
enum ty_vid = uint;
enum region_vid = uint;
iface vid {
fn to_uint() -> uint;
fn to_str() -> str;
}
impl of vid for ty_vid {
fn to_uint() -> uint { *self }
fn to_str() -> str { #fmt["<V%u>", self.to_uint()] }
}
impl of vid for region_vid {
fn to_uint() -> uint { *self }
fn to_str() -> str { #fmt["<R%u>", self.to_uint()] }
}
fn param_bounds_to_kind(bounds: param_bounds) -> kind {
let mut kind = kind_noncopyable;
for bound in *bounds {
@ -443,7 +466,11 @@ fn mk_t_with_id(cx: ctxt, st: sty, o_def_id: option<ast::def_id>) -> t {
ty_box(m) | ty_uniq(m) | ty_vec(m) | ty_ptr(m) {
derive_flags(has_params, has_vars, has_rptrs, m.ty);
}
ty_rptr(_, m) {
ty_rptr(r, m) {
alt r {
ty::re_var(_) { has_vars = true; }
_ { }
}
has_rptrs = true;
derive_flags(has_params, has_vars, has_rptrs, m.ty);
}
@ -555,7 +582,7 @@ fn mk_res(cx: ctxt, did: ast::def_id, inner: t, tps: [t]) -> t {
mk_t(cx, ty_res(did, inner, tps))
}
fn mk_var(cx: ctxt, v: int) -> t { mk_t(cx, ty_var(v)) }
fn mk_var(cx: ctxt, v: ty_vid) -> t { mk_t(cx, ty_var(v)) }
fn mk_self(cx: ctxt, tps: [t]) -> t { mk_t(cx, ty_self(tps)) }
@ -622,146 +649,124 @@ fn maybe_walk_ty(ty: t, f: fn(t) -> bool) {
}
}
enum fold_mode {
fm_var(fn@(int) -> t),
fm_param(fn@(uint, def_id) -> t),
fm_rptr(fn@(region, bool /* under & */) -> region,
bool /* descend into outermost fn */),
fm_general(fn@(t) -> t),
fn fold_sty_to_ty(tcx: ty::ctxt, sty: sty, foldop: fn(t) -> t) -> t {
mk_t(tcx, fold_sty(sty, foldop))
}
fn fold_ty(cx: ctxt, fld: fold_mode, ty_0: t) -> t {
fn do_fold(cx: ctxt, fld: fold_mode, ty_0: t, under_rptr: bool) -> t {
let mut ty = ty_0;
fn fold_sty(sty: sty, fldop: fn(t) -> t) -> sty {
alt sty {
ty_box(tm) {
ty_box({ty: fldop(tm.ty), mutbl: tm.mutbl})
}
ty_uniq(tm) {
ty_uniq({ty: fldop(tm.ty), mutbl: tm.mutbl})
}
ty_ptr(tm) {
ty_ptr({ty: fldop(tm.ty), mutbl: tm.mutbl})
}
ty_vec(tm) {
ty_vec({ty: fldop(tm.ty), mutbl: tm.mutbl})
}
ty_enum(tid, subtys) {
ty_enum(tid, vec::map(subtys) {|t| fldop(t) })
}
ty_iface(did, subtys) {
ty_iface(did, vec::map(subtys) {|t| fldop(t) })
}
ty_self(subtys) {
ty_self(vec::map(subtys) {|t| fldop(t) })
}
ty_rec(fields) {
let new_fields = vec::map(fields) {|fl|
let new_ty = fldop(fl.mt.ty);
let new_mt = {ty: new_ty, mutbl: fl.mt.mutbl};
{ident: fl.ident, mt: new_mt}
};
ty_rec(new_fields)
}
ty_tup(ts) {
let new_ts = vec::map(ts) {|tt| fldop(tt) };
ty_tup(new_ts)
}
ty_fn(f) {
let new_args = vec::map(f.inputs) {|a|
let new_ty = fldop(a.ty);
{mode: a.mode, ty: new_ty}
};
let new_output = fldop(f.output);
ty_fn({inputs: new_args, output: new_output with f})
}
ty_res(did, subty, tps) {
let new_tps = vec::map(tps) {|ty| fldop(ty) };
ty_res(did, fldop(subty), new_tps)
}
ty_rptr(r, tm) {
ty_rptr(r, {ty: fldop(tm.ty), mutbl: tm.mutbl})
}
ty_constr(subty, cs) {
ty_constr(fldop(subty), cs)
}
ty_class(did, tps) {
let new_tps = vec::map(tps) {|ty| fldop(ty) };
ty_class(did, new_tps)
}
ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
ty_str | ty_type | ty_opaque_closure_ptr(_) |
ty_opaque_box | ty_var(_) | ty_param(_, _) {
sty
}
}
}
let tb = get(ty);
alt fld {
fm_var(_) { if !tb.has_vars { ret ty; } }
fm_param(_) { if !tb.has_params { ret ty; } }
fm_rptr(_,_) { if !tb.has_rptrs { ret ty; } }
fm_general(_) {/* no fast path */ }
}
// Folds types from the bottom up.
fn fold_ty(cx: ctxt, t0: t, fldop: fn(t) -> t) -> t {
let sty = fold_sty(get(t0).struct) {|t| fold_ty(cx, t, fldop) };
fldop(mk_t(cx, sty))
}
fn fold_ty_var(cx: ctxt, t0: t, fldop: fn(ty_vid) -> t) -> t {
let tb = get(t0);
if !tb.has_vars { ret t0; }
alt tb.struct {
ty_var(id) { fldop(id) }
sty { fold_sty_to_ty(cx, sty) {|t| fold_ty_var(cx, t, fldop) } }
}
}
fn fold_region(cx: ctxt, t0: t, fldop: fn(region, bool) -> region) -> t {
fn do_fold(cx: ctxt, t0: t, under_r: bool,
fldop: fn(region, bool) -> region) -> t {
let tb = get(t0);
if !tb.has_rptrs { ret t0; }
alt tb.struct {
ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
ty_str | ty_type | ty_opaque_closure_ptr(_) |
ty_opaque_box {}
ty_box(tm) {
ty = mk_box(cx, {ty: do_fold(cx, fld, tm.ty, under_rptr),
mutbl: tm.mutbl});
ty_rptr(r, {ty: t1, mutbl: m}) {
let m_r = fldop(r, under_r);
let m_t1 = do_fold(cx, t1, true, fldop);
ty::mk_rptr(cx, m_r, {ty: m_t1, mutbl: m})
}
ty_uniq(tm) {
ty = mk_uniq(cx, {ty: do_fold(cx, fld, tm.ty, under_rptr),
mutbl: tm.mutbl});
ty_fn(_) {
// do not recurse into functions, which introduce fresh bindings
t0
}
ty_ptr(tm) {
ty = mk_ptr(cx, {ty: do_fold(cx, fld, tm.ty, under_rptr),
mutbl: tm.mutbl});
}
ty_vec(tm) {
ty = mk_vec(cx, {ty: do_fold(cx, fld, tm.ty, under_rptr),
mutbl: tm.mutbl});
}
ty_enum(tid, subtys) {
ty = mk_enum(cx, tid,
vec::map(subtys, {|t|
do_fold(cx, fld, t, under_rptr)
}));
}
ty_iface(did, subtys) {
ty = mk_iface(cx, did,
vec::map(subtys, {|t|
do_fold(cx, fld, t, under_rptr)
}));
}
ty_self(subtys) {
ty = mk_self(cx, vec::map(subtys, {|t|
do_fold(cx, fld, t, under_rptr)
}));
}
ty_rec(fields) {
let mut new_fields: [field] = [];
for fl: field in fields {
let new_ty = do_fold(cx, fld, fl.mt.ty, under_rptr);
let new_mt = {ty: new_ty, mutbl: fl.mt.mutbl};
new_fields += [{ident: fl.ident, mt: new_mt}];
sty {
fold_sty_to_ty(cx, sty) {|t|
do_fold(cx, t, under_r, fldop)
}
ty = mk_rec(cx, new_fields);
}
ty_tup(ts) {
let mut new_ts = [];
for tt in ts { new_ts += [do_fold(cx, fld, tt, under_rptr)]; }
ty = mk_tup(cx, new_ts);
}
ty_fn(f) {
let mut new_fld;
alt fld {
fm_rptr(_, false) {
// Don't recurse into functions, because regions are
// universally quantified, well, universally, at function
// boundaries.
ret ty;
}
fm_rptr(f, true) {
new_fld = fm_rptr(f, false);
}
_ { new_fld = fld; }
}
let mut new_args: [arg] = [];
for a: arg in f.inputs {
let new_ty = do_fold(cx, new_fld, a.ty, under_rptr);
new_args += [{mode: a.mode, ty: new_ty}];
}
let new_output = do_fold(cx, new_fld, f.output, under_rptr);
ty = mk_fn(cx, {inputs: new_args, output: new_output with f});
}
ty_res(did, subty, tps) {
let mut new_tps = [];
for tp: t in tps {
new_tps += [do_fold(cx, fld, tp, under_rptr)];
}
ty = mk_res(cx, did, do_fold(cx, fld, subty, under_rptr),
new_tps);
}
ty_var(id) {
alt fld { fm_var(folder) { ty = folder(id); } _ {/* no-op */ } }
}
ty_param(id, did) {
alt fld { fm_param(folder) { ty = folder(id, did); } _ {} }
}
ty_rptr(r, tm) {
let region = alt fld {
fm_rptr(folder, _) { folder(r, under_rptr) }
_ { r }
};
ty = mk_rptr(cx, region,
{ty: do_fold(cx, fld, tm.ty, true),
mutbl: tm.mutbl});
}
ty_constr(subty, cs) {
ty = mk_constr(cx, do_fold(cx, fld, subty, under_rptr), cs);
}
ty_class(did, ts) {
ty = mk_class(cx, did, vec::map(ts, {|t|
do_fold(cx, fld, t, under_rptr)}));
}
_ {
cx.sess.bug("unsupported sort of type in fold_ty");
}
}
alt tb.o_def_id {
some(did) { ty = mk_t_with_id(cx, get(ty).struct, some(did)); }
_ {}
}
// If this is a general type fold, then we need to run it now.
alt fld { fm_general(folder) { ret folder(ty); } _ { ret ty; } }
}
}
ret do_fold(cx, fld, ty_0, false);
do_fold(cx, t0, false, fldop)
}
fn substitute_type_params(cx: ctxt, substs: [ty::t], typ: t) -> t {
let tb = get(typ);
alt tb.struct {
ty_param(idx, _) { substs[idx] }
_ if !tb.has_params { typ }
s { mk_t(cx, fold_sty(s) {|t| substitute_type_params(cx, substs, t)}) }
}
}
// Type utilities
@ -1392,7 +1397,7 @@ fn type_param(ty: t) -> option<uint> {
// Returns a vec of all the type variables
// occurring in t. It may contain duplicates.
fn vars_in_type(ty: t) -> [int] {
fn vars_in_type(ty: t) -> [ty_vid] {
let mut rslt = [];
walk_ty(ty) {|ty|
alt get(ty).struct { ty_var(v) { rslt += [v]; } _ { } }
@ -1421,6 +1426,19 @@ fn type_autoderef(cx: ctxt, t: t) -> t {
ret t1;
}
fn hash_bound_region(br: bound_region) -> uint {
alt br { // no idea if this is any good
br_self { 0u }
br_anon { 1u }
br_param(id, _) { id }
}
}
fn br_hashmap<V:copy>() -> hashmap<bound_region, V> {
map::hashmap(hash_bound_region,
{|&&a: bound_region, &&b: bound_region| a == b })
}
// Type hashing.
fn hash_type_structure(st: sty) -> uint {
fn hash_uint(id: uint, n: uint) -> uint { (id << 2u) + n }
@ -1448,12 +1466,13 @@ fn hash_type_structure(st: sty) -> uint {
h
}
fn hash_region(r: region) -> uint {
alt r {
re_block(_) { 0u }
re_self { 1u }
re_inferred { 2u }
re_param(_) { 3u }
re_var(_) { 4u }
alt r { // no idea if this is any good
re_bound(br) { (hash_bound_region(br)) << 2u | 0u }
re_free(id, br) { ((id as uint) << 4u) |
(hash_bound_region(br)) << 2u | 1u }
re_scope(id) { ((id as uint) << 2u) | 2u }
re_var(id) { (id.to_uint() << 2u) | 3u }
re_default { 4u }
}
}
alt st {
@ -1492,7 +1511,7 @@ fn hash_type_structure(st: sty) -> uint {
for a in f.inputs { h = hash_subty(h, a.ty); }
hash_subty(h, f.output)
}
ty_var(v) { hash_uint(30u, v as uint) }
ty_var(v) { hash_uint(30u, v.to_uint()) }
ty_param(pid, did) { hash_def(hash_uint(31u, pid), did) }
ty_self(ts) {
let mut h = 28u;
@ -1638,7 +1657,7 @@ fn is_pred_ty(fty: t) -> bool {
is_fn_ty(fty) && type_is_bool(ty_fn_ret(fty))
}
fn ty_var_id(typ: t) -> int {
fn ty_var_id(typ: t) -> ty_vid {
alt get(typ).struct {
ty_var(vid) { ret vid; }
_ { #error("ty_var_id called on non-var ty"); fail; }
@ -1727,7 +1746,7 @@ fn sort_methods(meths: [method]) -> [method] {
ret std::sort::merge_sort(bind method_lteq(_, _), meths);
}
fn occurs_check(tcx: ctxt, sp: span, vid: int, rt: t) {
fn occurs_check(tcx: ctxt, sp: span, vid: ty_vid, rt: t) {
// Fast path
if !type_has_vars(rt) { ret; }
@ -1816,115 +1835,6 @@ fn set_default_mode(cx: ctxt, m: ast::mode, m_def: ast::rmode) {
}
}
// Type unification via Robinson's algorithm (Robinson 1965). Implemented as
// described in Hoder and Voronkov:
//
// http://www.cs.man.ac.uk/~hoderk/ubench/unification_full.pdf
mod unify {
import result::{result, ok, err, chain, map, map2};
export mk_region_bindings;
export region_bindings;
export precise, in_region_bindings;
type ures<T> = result<T,type_err>;
type region_bindings =
{sets: ufind::ufind, regions: smallintmap::smallintmap<region>};
enum unify_style {
precise,
in_region_bindings(@region_bindings)
}
type uctxt = {st: unify_style, tcx: ctxt};
fn mk_region_bindings() -> @region_bindings {
ret @{sets: ufind::make(), regions: smallintmap::mk::<region>()};
}
// Unifies two region sets.
//
// FIXME: This is a straight copy of the code above. We should use
// polymorphism to make this better.
fn union_region_sets<T:copy>(
cx: @uctxt, set_a: uint, set_b: uint,
nxt: fn() -> ures<T>) -> ures<T> {
let rb = alt cx.st {
in_region_bindings(rb) { rb }
precise {
cx.tcx.sess.bug("attempted to unify two region sets without \
a set of region bindings present");
}
};
ufind::grow(rb.sets, uint::max(set_a, set_b) + 1u);
let root_a = ufind::find(rb.sets, set_a);
let root_b = ufind::find(rb.sets, set_b);
let replace_region = (
fn@(rb: @region_bindings, r: region) {
ufind::union(rb.sets, set_a, set_b);
let root_c: uint = ufind::find(rb.sets, set_a);
smallintmap::insert(rb.regions, root_c, r);
}
);
alt smallintmap::find(rb.regions, root_a) {
none {
alt smallintmap::find(rb.regions, root_b) {
none { ufind::union(rb.sets, set_a, set_b); ret nxt(); }
some(r_b) { replace_region(rb, r_b); ret nxt(); }
}
}
some(r_a) {
alt smallintmap::find(rb.regions, root_b) {
none { replace_region(rb, r_a); ret nxt(); }
some(r_b) {
ret unify_regions(cx, r_a, r_b) {|r_c|
replace_region(rb, r_c);
nxt()
};
}
}
}
}
}
fn record_region_binding<T:copy>(
cx: @uctxt, key: uint,
r: region,
nxt: fn(region) -> ures<T>) -> ures<T> {
let rb = alt cx.st {
in_region_bindings(rb) { rb }
precise { fail; }
};
ufind::grow(rb.sets, key + 1u);
let root = ufind::find(rb.sets, key);
let mut result_region = r;
alt smallintmap::find(rb.regions, root) {
some(old_region) {
alt unify_regions(cx, old_region, r, {|v| ok(v)}) {
ok(unified_region) { result_region = unified_region; }
err(e) { ret err(e); }
}
}
none {/* fall through */ }
}
smallintmap::insert(rb.regions, root, result_region);
// FIXME: This should be re_var instead.
ret nxt(re_param(key));
}
fn unify_regions<T:copy>(
_cx: @uctxt, _e_region: region, _a_region: region,
_nxt: fn(region) -> ures<T>) -> ures<T> {
fail; // unused
}
}
fn type_err_to_str(cx: ctxt, err: type_err) -> str {
alt err {
terr_mismatch { ret "types differ"; }
@ -1995,14 +1905,6 @@ fn type_err_to_str(cx: ctxt, err: type_err) -> str {
}
}
// Replaces type parameters in the given type using the given list of
// substitions.
fn substitute_type_params(cx: ctxt, substs: [ty::t], typ: t) -> t {
if !type_has_params(typ) { ret typ; }
// Precondition? idx < vec::len(substs)
fold_ty(cx, fm_param({|idx, _id| substs[idx]}), typ)
}
fn def_has_ty_params(def: ast::def) -> bool {
alt def {
ast::def_fn(_, _) | ast::def_variant(_, _) | ast::def_class(_)

File diff suppressed because it is too large Load Diff

View File

@ -10,21 +10,32 @@ import syntax::{ast, ast_util};
import middle::ast_map;
import driver::session::session;
fn bound_region_to_str(_cx: ctxt, br: bound_region) -> str {
alt br {
br_anon { "&" }
br_param(_, str) { #fmt["&%s.", str] }
br_self { "&self." }
}
}
fn region_to_str(cx: ctxt, region: region) -> str {
alt region {
re_block(node_id) {
re_scope(node_id) {
alt cx.items.get(node_id) {
ast_map::node_block(blk) {
#fmt("<block at %s>", codemap::span_to_str(blk.span,
#fmt("&<block at %s>.", codemap::span_to_str(blk.span,
cx.sess.codemap))
}
_ { cx.sess.bug("re_block refers to non-block") }
}
}
re_self { "self" }
re_inferred { "" }
re_param(id) { #fmt("<P%u>", id) } // TODO: do better than this
re_var(id) { #fmt("<R%u>", id) } // TODO: do better than this
re_bound(br) { bound_region_to_str(cx, br) }
re_free(id, br) { #fmt["{%d} %s", id, bound_region_to_str(cx, br)] }
// These two should not be seen by end-users (very often, anyhow):
re_var(id) { #fmt("&%s.", id.to_str()) }
re_default { "&(default)." }
}
}
@ -122,7 +133,7 @@ fn ty_to_str(cx: ctxt, typ: t) -> str {
ty_box(tm) { "@" + mt_to_str(cx, tm) }
ty_uniq(tm) { "~" + mt_to_str(cx, tm) }
ty_ptr(tm) { "*" + mt_to_str(cx, tm) }
ty_rptr(r, tm) { "&" + region_to_str(cx, r) + "." + mt_to_str(cx, tm) }
ty_rptr(r, tm) { region_to_str(cx, r) + mt_to_str(cx, tm) }
ty_vec(tm) { "[" + mt_to_str(cx, tm) + "]" }
ty_type { "type" }
ty_rec(elems) {
@ -139,7 +150,7 @@ fn ty_to_str(cx: ctxt, typ: t) -> str {
fn_to_str(cx, f.proto, none, f.inputs, f.output, f.ret_style,
f.constraints)
}
ty_var(v) { "<T" + int::str(v) + ">" }
ty_var(v) { v.to_str() }
ty_param(id, _) {
"'" + str::from_bytes([('a' as u8) + (id as u8)])
}

View File

@ -1,4 +1,4 @@
fn main() {
let f; //! ERROR this local variable has a type of infinite size
let f; //! ERROR cyclic type of infinite size
f = @f;
}

View File

@ -0,0 +1,15 @@
fn region_identity(x: &r.uint) -> &r.uint { x }
fn apply<T>(t: T, f: fn(T) -> T) -> T { f(t) }
fn parameterized(x: &uint) -> uint {
let z = apply(x) {|y|
region_identity(y)
};
*z
}
fn main() {
let x = 3u;
assert parameterized(&x) == 3u;
}

View File

@ -1,3 +1,6 @@
// xfail-test
// ^ handling of self is currently broken
type clam = { chowder: &int };
impl clam for clam {