rewrite the resolution infrastructure; but it's still grody

This commit is contained in:
Niko Matsakis 2012-04-20 15:46:57 -07:00
parent 68f8812511
commit 0d3658bb43
4 changed files with 230 additions and 159 deletions

View file

@ -142,7 +142,9 @@ checking for overflow:
assert incd == [2u, 3u, 4u];
}
"]
fn map<T,U:copy,V:copy>(ts: [T], op: fn(T) -> result<V,U>) -> result<[V],U> {
fn map<T,U:copy,V:copy>(
ts: [T], op: fn(T) -> result<V,U>) -> result<[V],U> {
let mut vs: [V] = [];
vec::reserve(vs, vec::len(ts));
for vec::each(ts) {|t|
@ -154,6 +156,20 @@ fn map<T,U:copy,V:copy>(ts: [T], op: fn(T) -> result<V,U>) -> result<[V],U> {
ret ok(vs);
}
fn map_opt<T,U:copy,V:copy>(
o_t: option<T>, op: fn(T) -> result<V,U>) -> result<option<V>,U> {
alt o_t {
none { ok(none) }
some(t) {
alt op(t) {
ok(v) { ok(some(v)) }
err(e) { err(e) }
}
}
}
}
#[doc = "Same as map, but it operates over two parallel vectors.
A precondition is used here to ensure that the vectors are the same

View file

@ -17,9 +17,9 @@ export new_infer_ctxt;
export mk_subty;
export mk_eqty;
export mk_assignty;
export resolve_type_structure;
export fixup_vars;
export resolve_var;
export resolve_shallow;
export resolve_deep;
export resolve_deep_var;
export compare_tys;
export fixup_err, fixup_err_to_str;
@ -92,16 +92,23 @@ fn compare_tys(tcx: ty::ctxt, a: ty::t, b: ty::t) -> ures {
mk_eqty(infcx, a, b)
}
fn resolve_type_structure(cx: infer_ctxt, a: ty::t) -> fres<ty::t> {
cx.resolve_ty(a)
// Resolves one level of type structure but not any type variables
// that may be nested within.
fn resolve_shallow(cx: infer_ctxt, a: ty::t) -> fres<ty::t> {
resolver(cx, false, false).resolve(a)
}
fn resolve_var(cx: infer_ctxt, vid: ty_vid) -> fres<ty::t> {
cx.fixup_ty(ty::mk_var(cx.tcx, vid))
// see resolve_deep()
fn resolve_deep_var(cx: infer_ctxt, vid: ty_vid,
force_vars: bool) -> fres<ty::t> {
resolver(cx, true, force_vars).resolve(ty::mk_var(cx.tcx, vid))
}
fn fixup_vars(cx: infer_ctxt, a: ty::t) -> fres<ty::t> {
cx.fixup_ty(a)
// Resolves all levels of type structure. If `force_vars` is true,
// then we will resolve unconstrained type/region variables to
// something arbitrary. Otherwise, we preserve them as variables.
fn resolve_deep(cx: infer_ctxt, a: ty::t, force_vars: bool) -> fres<ty::t> {
resolver(cx, true, force_vars).resolve(a)
}
impl methods for ures {
@ -555,25 +562,137 @@ impl unify_methods for infer_ctxt {
}
}
impl resolve_methods for infer_ctxt {
fn rok(t: ty::t) -> fres<ty::t> {
#debug["Resolve OK: %s", t.to_str(self)];
ok(t)
// Resolution is the process of removing type variables and replacing
// them with their inferred values. There are several "modes" for
// resolution. The first is a shallow resolution: this only resolves
// one layer, but does not resolve any nested variables. So, for
// example, if we have two variables A and B, and the constraint that
// A <: [B] and B <: int, then shallow resolution on A would yield
// [B]. Deep resolution, on the other hand, would yield [int].
//
// But there is one more knob: the force_vars variable controls the
// behavior in the face of unconstrained variables. If we have A, B
// and only the constraint that A <: B, then the result is [_|_] if
// force_vars is true and [B] otherwise. We use force_vars == true
// when resolving types after typeck, but false otherwise (for
// example, when pretty-printing them for errors).
type resolve_state = @{
infcx: infer_ctxt,
deep: bool,
force_vars: bool,
mut err: option<fixup_err>,
mut r_seen: [region_vid],
mut v_seen: [ty_vid]
};
fn resolver(infcx: infer_ctxt, deep: bool, fvars: bool) -> resolve_state {
@{infcx: infcx,
deep: deep,
force_vars: fvars,
mut err: none,
mut r_seen: [],
mut v_seen: []}
}
impl methods for resolve_state {
fn resolve(typ: ty::t) -> fres<ty::t> {
self.err = none;
// n.b. This is a hokey mess because the current fold doesn't
// allow us to pass back errors in any useful way.
assert vec::is_empty(self.v_seen) && vec::is_empty(self.r_seen);
let rty = self.resolve1(typ);
assert vec::is_empty(self.v_seen) && vec::is_empty(self.r_seen);
alt self.err {
none {
#debug["Resolved %s to %s (deep=%b, force_vars=%b)",
ty_to_str(self.infcx.tcx, typ),
ty_to_str(self.infcx.tcx, rty),
self.deep,
self.force_vars];
ret ok(rty);
}
some(e) { ret err(e); }
}
}
fn rerr<T>(v: fixup_err) -> fres<T> {
#debug["Resolve error: %?", v];
err(v)
fn resolve1(typ: ty::t) -> ty::t {
let tb = ty::get(typ);
alt tb.struct {
ty::ty_var(vid) { self.resolve_ty_var(vid) }
_ if !tb.has_regions && !self.deep { typ }
_ {
ty::fold_regions_and_ty(
self.infcx.tcx, typ,
{ |r| self.resolve_region(r) },
{ |t| self.resolve_if_deep(t) },
{ |t| self.resolve_if_deep(t) })
}
}
}
fn resolve_if_deep(typ: ty::t) -> ty::t {
if !self.deep {typ} else {self.resolve1(typ)}
}
fn resolve_region(orig: ty::region) -> ty::region {
alt orig {
ty::re_var(rid) { self.resolve_region_var(rid) }
_ { orig }
}
}
fn resolve_region_var(rid: region_vid) -> ty::region {
if vec::contains(self.r_seen, rid) {
self.err = some(cyclic_region(rid));
ret ty::re_var(rid);
} else {
vec::push(self.r_seen, rid);
let r = self.resolve_var(
self.infcx.rb,
{|_t| false },
rid,
{||
if self.force_vars {ty::re_static}
else {ty::re_var(rid)}
});
vec::pop(self.r_seen);
ret r;
}
}
fn resolve_ty_var(vid: ty_vid) -> ty::t {
if vec::contains(self.v_seen, vid) {
self.err = some(cyclic_ty(vid));
ret ty::mk_var(self.infcx.tcx, vid);
} else {
vec::push(self.v_seen, vid);
let tcx = self.infcx.tcx;
let t0 = self.resolve_var(
self.infcx.vb,
{|t| type_is_bot(t) },
vid,
{||
if self.force_vars {ty::mk_bot(tcx)}
else {ty::mk_var(tcx, vid)}
});
let t1 = self.resolve1(t0);
vec::pop(self.v_seen);
ret t1;
}
}
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> {
vid: V, unbound: fn() -> T) -> T {
let {root:_, bounds} = self.get(vb, vid);
let {root:_, bounds} = self.infcx.get(vb, vid);
#debug["resolve_var(%s) bounds=%s",
vid.to_str(), bounds.to_str(self)];
vid.to_str(),
bounds.to_str(self.infcx)];
// Nonobvious: prefer the most specific type
// (i.e., the lower bound) to the more general
@ -582,116 +701,12 @@ impl resolve_methods for infer_ctxt {
// perf. penalties, so it pays to know more.
alt bounds {
{ ub:_, lb:some(t) } if !bot_guard(t) { ok(t) }
{ ub:some(t), lb:_ } { ok(t) }
{ ub:_, lb:some(t) } { ok(t) }
{ ub:_, lb:some(t) } if !bot_guard(t) { t }
{ ub:some(t), lb:_ } { t }
{ ub:_, lb:some(t) } { t }
{ ub:none, lb:none } { unbound() }
}
}
fn resolve_ty_var(vid: ty_vid) -> fres<ty::t> {
ret self.resolve_var(
self.vb,
{|t| type_is_bot(t) },
vid,
{|| ok(ty::mk_bot(self.tcx)) });
}
fn resolve_region_var(rid: region_vid) -> fres<ty::region> {
ret self.resolve_var(
self.rb,
{|_t| false },
rid,
{|| ok(ty::re_static) });
}
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) {
err(terr) { err(terr) }
ok(region) {
self.rok(ty::mk_rptr(self.tcx, region, base_ty))
}
}
}
_ { self.rok(typ) }
}
}
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
}
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;
}
result::err(e) { *err = some(e); r }
}
}
_ { 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(e) { ret self.rerr(e); }
}
}
}
// ______________________________________________________________________

View file

@ -37,6 +37,7 @@ export expr_ty_params_and_ty;
export expr_is_lval;
export field_ty;
export fold_ty, fold_sty_to_ty, fold_region, fold_regions, fold_ty_var;
export fold_regions_and_ty;
export field;
export field_idx;
export get_field;
@ -147,6 +148,7 @@ export item_path_str;
export ast_ty_to_ty_cache_entry;
export atttce_unresolved, atttce_resolved;
export mach_sty;
export ty_sort_str;
// Data types

View file

@ -12,7 +12,8 @@ import middle::ty;
import middle::ty::{arg, field, node_type_table, mk_nil,
ty_param_bounds_and_ty, lookup_public_fields};
import middle::ty::{ty_vid, region_vid, vid};
import util::ppaux::{ty_to_str, region_to_str};
import util::ppaux::{ty_to_str, region_to_str,
bound_region_to_str, vstore_to_str};
import std::smallintmap;
import std::smallintmap::map;
import std::map;
@ -257,7 +258,7 @@ fn instantiate_path(fcx: @fn_ctxt,
// Type tests
fn structurally_resolved_type(fcx: @fn_ctxt, sp: span, tp: ty::t) -> ty::t {
alt infer::resolve_type_structure(fcx.infcx, tp) {
alt infer::resolve_shallow(fcx.infcx, tp) {
// note: the bot type doesn't count as resolved; it's what we use when
// there is no information about a variable.
result::ok(t_s) if !ty::type_is_bot(t_s) { ret t_s; }
@ -274,17 +275,6 @@ fn structure_of(fcx: @fn_ctxt, sp: span, typ: ty::t) -> ty::sty {
ty::get(structurally_resolved_type(fcx, sp, typ)).struct
}
// Returns the one-level-deep structure of the given type or none if it
// is not known yet.
fn structure_of_maybe(fcx: @fn_ctxt, _sp: span, typ: ty::t) ->
option<ty::sty> {
let r = infer::resolve_type_structure(fcx.infcx, typ);
alt r {
result::ok(typ_s) { some(ty::get(typ_s).struct) }
result::err(_) { none }
}
}
fn type_is_integral(fcx: @fn_ctxt, sp: span, typ: ty::t) -> bool {
let typ_s = structurally_resolved_type(fcx, sp, typ);
ret ty::type_is_integral(typ_s);
@ -523,18 +513,60 @@ fn ast_ty_to_ty<AC: ast_conv, RS: region_scope copy>(
ret ty;
}
fn mk_vstore<AC: ast_conv, RS: region_scope copy>(
fn mk_bounded<AC: ast_conv, RS: region_scope copy>(
self: AC, rscope: RS, a_seq_ty: @ast::ty, vst: ty::vstore) -> ty::t {
let tcx = self.tcx();
let seq_ty = ast_ty_to_ty(self, rscope, a_seq_ty);
alt ty::get(seq_ty).struct {
ty::ty_vec(mt) { ty::mk_evec(tcx, mt, vst) }
ty::ty_str { ty::mk_estr(tcx, vst) }
ty::ty_vec(mt) {
ret ty::mk_evec(tcx, mt, vst);
}
ty::ty_str {
ret ty::mk_estr(tcx, vst);
}
ty::ty_enum(_, subst) |
ty::ty_class(_, subst) |
ty::ty_res(_, _, subst) {
// n.b.: This is a hacky abuse of the vstore terminology to also
// make it work for region bounds. The idea is to allow a type
// Id/&r where Id is an enum, class, or resource, but not Id/@
// etc. We also do not want to allow Id/&r if the given
// enum/class/resource does not define a region parameter.
//
// Really, these "/&r" bounds ought to be part of the path, like
// type parameters. (In fact, we could generalize to allowing
// multiple such bounds someday)
alt (subst.self_r, vst) {
(some(_), ty::vstore_slice(_)) { /* ok */ }
(none, ty::vstore_slice(_)) {
tcx.sess.span_err(
a_seq_ty.span,
#fmt["inappropriate bound for %s, \
which is not declared as containing \
region pointers",
ty::ty_sort_str(tcx, seq_ty)]);
}
(_, _) {
tcx.sess.span_err(
a_seq_ty.span,
#fmt["a %s bound is not appropriate for %s",
vstore_to_str(tcx, vst),
ty::ty_sort_str(tcx, seq_ty)]);
}
}
ret seq_ty;
}
_ {
tcx.sess.span_bug(a_seq_ty.span,
"found sequence storage modifier \
on non-sequence type")
tcx.sess.span_err(
a_seq_ty.span,
#fmt["Bound not allowed on a %s.",
ty::ty_sort_str(tcx, seq_ty)]);
ret seq_ty;
}
}
}
@ -633,21 +665,21 @@ fn ast_ty_to_ty<AC: ast_conv, RS: region_scope copy>(
}
ast::ty_vstore(a_t, ast::vstore_slice(a_r)) {
let r = ast_region_to_region(self, rscope, ast_ty.span, a_r);
mk_vstore(self, in_anon_rscope(rscope, r), a_t, ty::vstore_slice(r))
mk_bounded(self, in_anon_rscope(rscope, r), a_t, ty::vstore_slice(r))
}
ast::ty_vstore(a_t, ast::vstore_uniq) {
mk_vstore(self, rscope, a_t, ty::vstore_uniq)
mk_bounded(self, rscope, a_t, ty::vstore_uniq)
}
ast::ty_vstore(a_t, ast::vstore_box) {
mk_vstore(self, rscope, a_t, ty::vstore_box)
mk_bounded(self, rscope, a_t, ty::vstore_box)
}
ast::ty_vstore(a_t, ast::vstore_fixed(some(u))) {
mk_vstore(self, rscope, a_t, ty::vstore_fixed(u))
mk_bounded(self, rscope, a_t, ty::vstore_fixed(u))
}
ast::ty_vstore(_, ast::vstore_fixed(none)) {
tcx.sess.span_bug(
ast_ty.span,
"implied fixed length in ast_ty_vstore_to_vstore");
"implied fixed length for bound");
}
ast::ty_constr(t, cs) {
let mut out_cs = [];
@ -1656,7 +1688,7 @@ fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
}
fn resolve_type_vars_if_possible(fcx: @fn_ctxt, typ: ty::t) -> ty::t {
alt infer::fixup_vars(fcx.infcx, typ) {
alt infer::resolve_deep(fcx.infcx, typ, false) {
result::ok(new_type) { ret new_type; }
result::err(_) { ret typ; }
}
@ -1731,7 +1763,7 @@ mod writeback {
fn resolve_type_vars_in_type(fcx: @fn_ctxt, sp: span, typ: ty::t) ->
option<ty::t> {
if !ty::type_has_vars(typ) { ret some(typ); }
alt infer::fixup_vars(fcx.infcx, typ) {
alt infer::resolve_deep(fcx.infcx, typ, true) {
result::ok(new_type) { ret some(new_type); }
result::err(e) {
if !fcx.ccx.tcx.sess.has_errors() {
@ -1852,7 +1884,7 @@ mod writeback {
fn visit_local(l: @ast::local, wbcx: wb_ctxt, v: wb_vt) {
if !wbcx.success { ret; }
let var_id = lookup_local(wbcx.fcx, l.span, l.node.id);
alt infer::resolve_var(wbcx.fcx.infcx, var_id) {
alt infer::resolve_deep_var(wbcx.fcx.infcx, var_id, true) {
result::ok(lty) {
#debug["Type for local %s (id %d) resolved to %s",
pat_to_str(l.node.pat), l.node.id,
@ -1998,12 +2030,18 @@ fn universally_quantify_from_sty(fcx: @fn_ctxt,
bound_tys.map {|x| fcx.ty_to_str(x) }];
indent {||
let tcx = fcx.tcx();
let isr = collect_bound_regions_in_tys(tcx, @nil, bound_tys) { |_id|
fcx.next_region_var()
let isr = collect_bound_regions_in_tys(tcx, @nil, bound_tys) { |br|
let rvar = fcx.next_region_var();
#debug["Bound region %s maps to %s",
bound_region_to_str(fcx.ccx.tcx, br),
region_to_str(fcx.ccx.tcx, rvar)];
rvar
};
ty::fold_sty_to_ty(fcx.ccx.tcx, sty) { |t|
let t_res = ty::fold_sty_to_ty(fcx.ccx.tcx, sty) { |t|
replace_bound_regions(tcx, span, isr, t)
}
};
#debug["Result of universal quant. is %s", fcx.ty_to_str(t_res)];
t_res
}
}
@ -4477,7 +4515,7 @@ mod vtable {
fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t {
let tcx = fcx.ccx.tcx;
alt infer::fixup_vars(fcx.infcx, ty) {
alt infer::resolve_deep(fcx.infcx, ty, true) {
result::ok(new_type) { new_type }
result::err(e) {
tcx.sess.span_fatal(