mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 23:34:48 +00:00
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:
parent
d961f054c5
commit
c0d61795de
@ -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) {
|
||||
|
@ -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));
|
||||
|
@ -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)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
@ -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)])
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
15
src/test/run-pass/region-param.rs
Normal file
15
src/test/run-pass/region-param.rs
Normal 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;
|
||||
}
|
@ -1,3 +1,6 @@
|
||||
// xfail-test
|
||||
// ^ handling of self is currently broken
|
||||
|
||||
type clam = { chowder: &int };
|
||||
|
||||
impl clam for clam {
|
||||
|
Loading…
Reference in New Issue
Block a user