1965 lines
60 KiB
Rust
1965 lines
60 KiB
Rust
import std::smallintmap;
|
|
import std::smallintmap::smallintmap;
|
|
import std::smallintmap::map;
|
|
import std::map::hashmap;
|
|
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;
|
|
import util::common::{indent, indenter};
|
|
|
|
export infer_ctxt;
|
|
export new_infer_ctxt;
|
|
export mk_subty;
|
|
export mk_subr;
|
|
export mk_eqty;
|
|
export mk_assignty;
|
|
export resolve_shallow;
|
|
export resolve_deep;
|
|
export resolve_deep_var;
|
|
export compare_tys;
|
|
export fixup_err, fixup_err_to_str;
|
|
|
|
// Extra information needed to perform an assignment that may borrow.
|
|
// The `expr_id` is the is of the expression whose type is being
|
|
// assigned, and `borrow_scope` is the region scope to use if the
|
|
// value should be borrowed.
|
|
type assignment = {
|
|
expr_id: ast::node_id,
|
|
borrow_scope: ast::node_id
|
|
};
|
|
|
|
type bound<T:copy> = option<T>;
|
|
|
|
type bounds<T:copy> = {lb: bound<T>, ub: bound<T>};
|
|
|
|
enum var_value<V:copy, T:copy> {
|
|
redirect(V),
|
|
bounded(bounds<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::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, fixup_err>;
|
|
|
|
fn new_infer_ctxt(tcx: ty::ctxt) -> infer_ctxt {
|
|
infer_ctxt(@{tcx: tcx,
|
|
vb: {vals: smallintmap::mk(), mut bindings: []},
|
|
rb: {vals: smallintmap::mk(), mut bindings: []}})
|
|
}
|
|
|
|
fn mk_subty(cx: infer_ctxt, a: ty::t, b: ty::t) -> ures {
|
|
#debug["mk_subty(%s <: %s)", a.to_str(cx), b.to_str(cx)];
|
|
indent {|| cx.commit {|| sub(cx).tys(a, b) } }.to_ures()
|
|
}
|
|
|
|
fn mk_subr(cx: infer_ctxt, a: ty::region, b: ty::region) -> ures {
|
|
#debug["mk_subr(%s <: %s)", a.to_str(cx), b.to_str(cx)];
|
|
indent {|| cx.commit {|| sub(cx).regions(a, b) } }.to_ures()
|
|
}
|
|
|
|
fn mk_eqty(cx: infer_ctxt, a: ty::t, b: ty::t) -> ures {
|
|
#debug["mk_eqty(%s <: %s)", a.to_str(cx), b.to_str(cx)];
|
|
indent {|| cx.commit {|| cx.eq_tys(a, b) } }.to_ures()
|
|
}
|
|
|
|
fn mk_assignty(cx: infer_ctxt, anmnt: assignment,
|
|
a: ty::t, b: ty::t) -> ures {
|
|
#debug["mk_assignty(%? / %s <: %s)",
|
|
anmnt, a.to_str(cx), b.to_str(cx)];
|
|
indent {|| cx.commit {||
|
|
cx.assign_tys(anmnt, a, b)
|
|
} }.to_ures()
|
|
}
|
|
|
|
fn compare_tys(tcx: ty::ctxt, a: ty::t, b: ty::t) -> ures {
|
|
let infcx = new_infer_ctxt(tcx);
|
|
mk_eqty(infcx, a, b)
|
|
}
|
|
|
|
// See comment on the type `resolve_state` below
|
|
fn resolve_shallow(cx: infer_ctxt, a: ty::t,
|
|
force_vars: bool) -> fres<ty::t> {
|
|
resolver(cx, false, force_vars).resolve(a)
|
|
}
|
|
|
|
// See comment on the type `resolve_state` below
|
|
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))
|
|
}
|
|
|
|
// See comment on the type `resolve_state` below
|
|
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 {
|
|
fn then<T:copy>(f: fn() -> result<T,ty::type_err>)
|
|
-> result<T,ty::type_err> {
|
|
self.chain() {|_i| f() }
|
|
}
|
|
}
|
|
|
|
impl methods<T:copy> for cres<T> {
|
|
fn to_ures() -> ures {
|
|
alt self {
|
|
ok(_v) { ok(()) }
|
|
err(e) { err(e) }
|
|
}
|
|
}
|
|
|
|
fn compare(t: T, f: fn() -> ty::type_err) -> cres<T> {
|
|
self.chain {|s|
|
|
if s == t {
|
|
self
|
|
} else {
|
|
err(f())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
iface to_str {
|
|
fn to_str(cx: infer_ctxt) -> str;
|
|
}
|
|
|
|
impl of to_str for ty::t {
|
|
fn to_str(cx: infer_ctxt) -> str {
|
|
ty_to_str(cx.tcx, self)
|
|
}
|
|
}
|
|
|
|
impl of to_str for ty::mt {
|
|
fn to_str(cx: infer_ctxt) -> str {
|
|
mt_to_str(cx.tcx, self)
|
|
}
|
|
}
|
|
|
|
impl of to_str for ty::region {
|
|
fn to_str(cx: infer_ctxt) -> str {
|
|
util::ppaux::region_to_str(cx.tcx, self)
|
|
}
|
|
}
|
|
|
|
impl<V:copy to_str> of to_str for bound<V> {
|
|
fn to_str(cx: infer_ctxt) -> str {
|
|
alt self {
|
|
some(v) { v.to_str(cx) }
|
|
none { "none " }
|
|
}
|
|
}
|
|
}
|
|
|
|
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),
|
|
self.ub.to_str(cx)]
|
|
}
|
|
}
|
|
|
|
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(vid) { #fmt("redirect(%s)", vid.to_str()) }
|
|
bounded(bnds) { #fmt("bounded(%s)", bnds.to_str(cx)) }
|
|
}
|
|
}
|
|
}
|
|
|
|
iface st {
|
|
fn sub(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 sub(infcx: infer_ctxt, b: ty::t) -> ures {
|
|
sub(infcx).tys(self, b).to_ures()
|
|
}
|
|
|
|
fn lub(infcx: infer_ctxt, b: ty::t) -> cres<ty::t> {
|
|
lub(infcx).tys(self, b)
|
|
}
|
|
|
|
fn glb(infcx: infer_ctxt, b: ty::t) -> cres<ty::t> {
|
|
glb(infcx).tys(self, b)
|
|
}
|
|
}
|
|
|
|
impl of st for ty::region {
|
|
fn sub(infcx: infer_ctxt, b: ty::region) -> ures {
|
|
sub(infcx).regions(self, b).chain {|_r| ok(()) }
|
|
}
|
|
|
|
fn lub(infcx: infer_ctxt, b: ty::region) -> cres<ty::region> {
|
|
lub(infcx).regions(self, b)
|
|
}
|
|
|
|
fn glb(infcx: infer_ctxt, b: ty::region) -> cres<ty::region> {
|
|
glb(infcx).regions(self, b)
|
|
}
|
|
}
|
|
|
|
fn uok() -> ures {
|
|
ok(())
|
|
}
|
|
|
|
impl methods for infer_ctxt {
|
|
fn commit<T,E>(f: fn() -> result<T,E>) -> result<T,E> {
|
|
|
|
assert self.vb.bindings.len() == 0u;
|
|
assert self.rb.bindings.len() == 0u;
|
|
|
|
let r <- self.try(f);
|
|
|
|
// TODO---could use a vec::clear() that ran destructors but kept
|
|
// the vec at its currently allocated length
|
|
self.vb.bindings = [];
|
|
self.rb.bindings = [];
|
|
|
|
ret r;
|
|
}
|
|
|
|
fn try<T,E>(f: fn() -> result<T,E>) -> result<T,E> {
|
|
|
|
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.to_uint(), old_v);
|
|
}
|
|
}
|
|
|
|
let vbl = self.vb.bindings.len();
|
|
let rbl = self.rb.bindings.len();
|
|
#debug["try(vbl=%u, rbl=%u)", vbl, rbl];
|
|
let r <- f();
|
|
alt r {
|
|
result::ok(_) { #debug["try--ok"]; }
|
|
result::err(_) {
|
|
#debug["try--rollback"];
|
|
rollback_to(self.vb, vbl);
|
|
rollback_to(self.rb, rbl);
|
|
}
|
|
}
|
|
ret r;
|
|
}
|
|
}
|
|
|
|
impl unify_methods for infer_ctxt {
|
|
|
|
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.to_uint());
|
|
vec::push(vb.bindings, (vid, old_v));
|
|
vb.vals.insert(vid.to_uint(), new_v);
|
|
|
|
#debug["Updating variable %s from %s to %s",
|
|
vid.to_str(), old_v.to_str(self), new_v.to_str(self)];
|
|
}
|
|
|
|
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.to_uint()) {
|
|
none {
|
|
let bnds = {lb: none, ub: none};
|
|
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.to_uint(), redirect(root));
|
|
}
|
|
{root: root, bounds: bounds}
|
|
}
|
|
some(bounded(bounds)) {
|
|
{root: vid, bounds: bounds}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Combines the two bounds into a more general bound.
|
|
fn merge_bnd<V:copy to_str>(
|
|
a: bound<V>, b: bound<V>,
|
|
merge_op: fn(V,V) -> cres<V>) -> cres<bound<V>> {
|
|
|
|
#debug["merge_bnd(%s,%s)", a.to_str(self), b.to_str(self)];
|
|
let _r = indenter();
|
|
|
|
alt (a, b) {
|
|
(none, none) {
|
|
ok(none)
|
|
}
|
|
(some(_), none) {
|
|
ok(a)
|
|
}
|
|
(none, some(_)) {
|
|
ok(b)
|
|
}
|
|
(some(v_a), some(v_b)) {
|
|
merge_op(v_a, v_b).chain {|v|
|
|
ok(some(v))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn merge_bnds<V:copy to_str>(
|
|
a: bounds<V>, b: bounds<V>,
|
|
lub: fn(V,V) -> cres<V>,
|
|
glb: fn(V,V) -> cres<V>) -> cres<bounds<V>> {
|
|
|
|
let _r = indenter();
|
|
self.merge_bnd(a.ub, b.ub, glb).chain {|ub|
|
|
#debug["glb of ubs %s and %s is %s",
|
|
a.ub.to_str(self), b.ub.to_str(self),
|
|
ub.to_str(self)];
|
|
self.merge_bnd(a.lb, b.lb, lub).chain {|lb|
|
|
#debug["lub of lbs %s and %s is %s",
|
|
a.lb.to_str(self), b.lb.to_str(self),
|
|
lb.to_str(self)];
|
|
ok({lb: lb, ub: ub})
|
|
}
|
|
}
|
|
}
|
|
|
|
// Updates the bounds for the variable `v_id` to be the intersection
|
|
// of `a` and `b`. That is, the new bounds for `v_id` will be
|
|
// a bounds c such that:
|
|
// c.ub <: a.ub
|
|
// c.ub <: b.ub
|
|
// a.lb <: c.lb
|
|
// b.lb <: c.lb
|
|
// If this cannot be achieved, the result is failure.
|
|
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
|
|
// can swap A/B in these pictures):
|
|
//
|
|
// A A
|
|
// / \ / \
|
|
// / B \ / B \
|
|
// / / \ \ / / \ \
|
|
// * * * * * / * *
|
|
// \ \ / / \ / /
|
|
// \ B / / \ / /
|
|
// \ / * \ /
|
|
// A \ / A
|
|
// B
|
|
|
|
#debug["merge(%s,%s,%s)",
|
|
v_id.to_str(),
|
|
a.to_str(self),
|
|
b.to_str(self)];
|
|
|
|
// First, relate the lower/upper bounds of A and B.
|
|
// Note that these relations *must* hold for us to
|
|
// to be able to merge A and B at all, and relating
|
|
// them explicitly gives the type inferencer more
|
|
// information and helps to produce tighter bounds
|
|
// when necessary.
|
|
indent {||
|
|
self.bnds(a.lb, b.ub).then {||
|
|
self.bnds(b.lb, a.ub).then {||
|
|
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(vb, v_id, bounded(bnds));
|
|
uok()
|
|
}
|
|
}}}}}
|
|
}
|
|
|
|
fn vars<V:copy vid, T:copy to_str st>(
|
|
vb: vals_and_bindings<V, T>,
|
|
a_id: V, b_id: V) -> ures {
|
|
|
|
// 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 uok(); }
|
|
|
|
// If both A's UB and B's LB have already been bound to types,
|
|
// 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 {|| a_ub.sub(self, b_lb) };
|
|
alt r {
|
|
ok(()) { ret result::ok(()); }
|
|
err(_) { /*fallthrough */ }
|
|
}
|
|
}
|
|
_ { /*fallthrough*/ }
|
|
}
|
|
|
|
// For max perf, we should consider the rank here. But for now,
|
|
// we always make b redirect to a.
|
|
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_var_to_merged_bounds(vb, a_id, a_bounds, b_bounds).then {||
|
|
uok()
|
|
}
|
|
}
|
|
|
|
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_var_to_merged_bounds(vb, a_id, a_bounds, b_bounds)
|
|
}
|
|
|
|
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(vb, b_id);
|
|
#debug["tvar(%s <: %s=%s)",
|
|
a.to_str(self),
|
|
b_id.to_str(), b_bounds.to_str(self)];
|
|
self.set_var_to_merged_bounds(vb, b_id, a_bounds, b_bounds)
|
|
}
|
|
|
|
fn constrs(
|
|
expected: @ty::type_constr,
|
|
actual_constr: @ty::type_constr) -> ures {
|
|
|
|
let err_res =
|
|
err(ty::terr_constr_mismatch(expected, actual_constr));
|
|
|
|
if expected.node.id != actual_constr.node.id { ret err_res; }
|
|
let expected_arg_len = vec::len(expected.node.args);
|
|
let actual_arg_len = vec::len(actual_constr.node.args);
|
|
if expected_arg_len != actual_arg_len { ret err_res; }
|
|
let mut i = 0u;
|
|
for expected.node.args.each {|a|
|
|
let actual = actual_constr.node.args[i];
|
|
alt a.node {
|
|
ast::carg_base {
|
|
alt actual.node {
|
|
ast::carg_base { }
|
|
_ { ret err_res; }
|
|
}
|
|
}
|
|
ast::carg_lit(l) {
|
|
alt actual.node {
|
|
ast::carg_lit(m) {
|
|
if l != m { ret err_res; }
|
|
}
|
|
_ { ret err_res; }
|
|
}
|
|
}
|
|
ast::carg_ident(p) {
|
|
alt actual.node {
|
|
ast::carg_ident(q) {
|
|
if p.idents != q.idents { ret err_res; }
|
|
}
|
|
_ { ret err_res; }
|
|
}
|
|
}
|
|
}
|
|
i += 1u;
|
|
}
|
|
ret uok();
|
|
}
|
|
|
|
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));
|
|
indent {||
|
|
alt (a, b) {
|
|
(none, none) |
|
|
(some(_), none) |
|
|
(none, some(_)) {
|
|
uok()
|
|
}
|
|
(some(t_a), some(t_b)) {
|
|
t_a.sub(self, t_b)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn constrvecs(
|
|
as: [@ty::type_constr], bs: [@ty::type_constr]) -> ures {
|
|
|
|
if check vec::same_length(as, bs) {
|
|
iter2(as, bs) {|a,b|
|
|
self.constrs(a, b)
|
|
}
|
|
} else {
|
|
err(ty::terr_constr_len(bs.len(), as.len()))
|
|
}
|
|
}
|
|
|
|
fn sub_tys(a: ty::t, b: ty::t) -> ures {
|
|
sub(self).tys(a, b).chain {|_t| ok(()) }
|
|
}
|
|
|
|
fn sub_regions(a: ty::region, b: ty::region) -> ures {
|
|
sub(self).regions(a, b).chain {|_t| ok(()) }
|
|
}
|
|
|
|
fn eq_tys(a: ty::t, b: ty::t) -> ures {
|
|
self.sub_tys(a, b).then {||
|
|
self.sub_tys(b, a)
|
|
}
|
|
}
|
|
|
|
fn eq_regions(a: ty::region, b: ty::region) -> ures {
|
|
#debug["eq_regions(%s, %s)",
|
|
a.to_str(self), b.to_str(self)];
|
|
indent {||
|
|
self.sub_regions(a, b).then {||
|
|
self.sub_regions(b, a)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 it is true,
|
|
// then unconstrained variables result in an error.
|
|
|
|
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;
|
|
|
|
#debug["Resolving %s (deep=%b, force_vars=%b)",
|
|
ty_to_str(self.infcx.tcx, typ),
|
|
self.deep,
|
|
self.force_vars];
|
|
|
|
// 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 = indent {|| self.resolve1(typ) };
|
|
assert vec::is_empty(self.v_seen) && vec::is_empty(self.r_seen);
|
|
alt self.err {
|
|
none {
|
|
#debug["Resolved to %s (deep=%b, force_vars=%b)",
|
|
ty_to_str(self.infcx.tcx, rty),
|
|
self.deep,
|
|
self.force_vars];
|
|
ret ok(rty);
|
|
}
|
|
some(e) { ret err(e); }
|
|
}
|
|
}
|
|
|
|
fn resolve1(typ: ty::t) -> ty::t {
|
|
#debug("Resolve1(%s)", typ.to_str(self.infcx));
|
|
indent(fn&() -> ty::t {
|
|
if !ty::type_needs_infer(typ) { ret typ; }
|
|
|
|
alt ty::get(typ).struct {
|
|
ty::ty_var(vid) {
|
|
self.resolve_ty_var(vid)
|
|
}
|
|
_ if !ty::type_has_regions(typ) && !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 {
|
|
#debug("Resolve_if_deep(%s)", typ.to_str(self.infcx));
|
|
if !self.deep {typ} else {self.resolve1(typ)}
|
|
}
|
|
|
|
fn resolve_region(orig: ty::region) -> ty::region {
|
|
#debug("Resolve_region(%s)", orig.to_str(self.infcx));
|
|
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 {root:_, bounds} = self.infcx.get(self.infcx.rb, rid);
|
|
let r1 = alt bounds {
|
|
{ ub:_, lb:some(t) } { self.resolve_region(t) }
|
|
{ ub:some(t), lb:_ } { self.resolve_region(t) }
|
|
{ ub:none, lb:none } {
|
|
if self.force_vars {
|
|
self.err = some(unresolved_region(rid));
|
|
}
|
|
ty::re_var(rid)
|
|
}
|
|
};
|
|
vec::pop(self.r_seen);
|
|
ret r1;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
// Nonobvious: prefer the most specific type
|
|
// (i.e., the lower bound) to the more general
|
|
// one. More general types in Rust (e.g., fn())
|
|
// tend to carry more restrictions or higher
|
|
// perf. penalties, so it pays to know more.
|
|
|
|
let {root:_, bounds} = self.infcx.get(self.infcx.vb, vid);
|
|
let t1 = alt bounds {
|
|
{ ub:_, lb:some(t) } if !type_is_bot(t) { self.resolve1(t) }
|
|
{ ub:some(t), lb:_ } { self.resolve1(t) }
|
|
{ ub:_, lb:some(t) } { self.resolve1(t) }
|
|
{ ub:none, lb:none } {
|
|
if self.force_vars {
|
|
self.err = some(unresolved_ty(vid));
|
|
}
|
|
ty::mk_var(tcx, vid)
|
|
}
|
|
};
|
|
vec::pop(self.v_seen);
|
|
ret t1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ______________________________________________________________________
|
|
// Type assignment
|
|
//
|
|
// True if rvalues of type `a` can be assigned to lvalues of type `b`.
|
|
// This may cause borrowing to the region scope enclosing `a_node_id`.
|
|
//
|
|
// The strategy here is somewhat non-obvious. The problem is
|
|
// that the constraint we wish to contend with is not a subtyping
|
|
// constraint. Currently, for variables, we only track what it
|
|
// must be a subtype of, not what types it must be assignable to
|
|
// (or from). Possibly, we should track that, but I leave that
|
|
// refactoring for another day.
|
|
//
|
|
// Instead, we look at each variable involved and try to extract
|
|
// *some* sort of bound. Typically, the type a is the argument
|
|
// supplied to a call; it typically has a *lower bound* (which
|
|
// comes from having been assigned a value). What we'd actually
|
|
// *like* here is an upper-bound, but we generally don't have
|
|
// one. The type b is the expected type and it typically has a
|
|
// lower-bound too, which is good.
|
|
//
|
|
// The way we deal with the fact that we often don't have the
|
|
// bounds we need is to be a bit careful. We try to get *some*
|
|
// bound from each side, preferring the upper from a and the
|
|
// lower from b. If we fail to get a bound from both sides, then
|
|
// we just fall back to requiring that a <: b.
|
|
//
|
|
// Assuming we have a bound from both sides, we will then examine
|
|
// these bounds and see if they have the form (@MT_a, &rb.MT_b)
|
|
// (resp. ~MT_a). If they do not, we fall back to subtyping.
|
|
//
|
|
// If they *do*, then we know that the two types could never be
|
|
// subtypes of one another. We will then construct a type @MT_b and
|
|
// ensure that type a is a subtype of that. This allows for the
|
|
// possibility of assigning from a type like (say) @[mut T1] to a type
|
|
// &[const T2] where T1 <: T2. Basically we would require that @[mut
|
|
// T1] <: @[const T2]. Next we require that the region for the
|
|
// enclosing scope be a superregion of the region r. These two checks
|
|
// together guarantee that the type A would be a subtype of the type B
|
|
// if the @ were converted to a region r.
|
|
//
|
|
// You might wonder why we don't just make the type &e.MT_a where e is
|
|
// the enclosing region and check that &e.MT_a <: B. The reason is
|
|
// that the type @MT_a is (generally) just a *lower-bound*, so this
|
|
// would be imposing @MT_a also as the upper-bound on type A. But
|
|
// this upper-bound might be stricter than what is truly needed.
|
|
|
|
impl assignment for infer_ctxt {
|
|
fn assign_tys(anmnt: assignment, a: ty::t, b: ty::t) -> ures {
|
|
|
|
fn select(fst: option<ty::t>, snd: option<ty::t>) -> option<ty::t> {
|
|
alt fst {
|
|
some(t) { some(t) }
|
|
none {
|
|
alt snd {
|
|
some(t) { some(t) }
|
|
none { none }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#debug["assign_tys(anmnt=%?, %s -> %s)",
|
|
anmnt, a.to_str(self), b.to_str(self)];
|
|
let _r = indenter();
|
|
|
|
alt (ty::get(a).struct, ty::get(b).struct) {
|
|
(ty::ty_bot, _) {
|
|
uok()
|
|
}
|
|
|
|
(ty::ty_var(a_id), ty::ty_var(b_id)) {
|
|
let {root:_, bounds: a_bounds} = self.get(self.vb, a_id);
|
|
let {root:_, bounds: b_bounds} = self.get(self.vb, b_id);
|
|
let a_bnd = select(a_bounds.ub, a_bounds.lb);
|
|
let b_bnd = select(b_bounds.lb, b_bounds.ub);
|
|
self.assign_tys_or_sub(anmnt, a, b, a_bnd, b_bnd)
|
|
}
|
|
|
|
(ty::ty_var(a_id), _) {
|
|
let {root:_, bounds:a_bounds} = self.get(self.vb, a_id);
|
|
let a_bnd = select(a_bounds.ub, a_bounds.lb);
|
|
self.assign_tys_or_sub(anmnt, a, b, a_bnd, some(b))
|
|
}
|
|
|
|
(_, ty::ty_var(b_id)) {
|
|
let {root:_, bounds: b_bounds} = self.get(self.vb, b_id);
|
|
let b_bnd = select(b_bounds.lb, b_bounds.ub);
|
|
self.assign_tys_or_sub(anmnt, a, b, some(a), b_bnd)
|
|
}
|
|
|
|
(_, _) {
|
|
self.assign_tys_or_sub(anmnt, a, b, some(a), some(b))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn assign_tys_or_sub(
|
|
anmnt: assignment,
|
|
a: ty::t, b: ty::t,
|
|
a_bnd: option<ty::t>, b_bnd: option<ty::t>) -> ures {
|
|
|
|
#debug["assign_tys_or_sub(anmnt=%?, %s -> %s, %s -> %s)",
|
|
anmnt, a.to_str(self), b.to_str(self),
|
|
a_bnd.to_str(self), b_bnd.to_str(self)];
|
|
let _r = indenter();
|
|
|
|
fn is_borrowable(v: ty::vstore) -> bool {
|
|
alt v {
|
|
ty::vstore_fixed(_) | ty::vstore_uniq | ty::vstore_box { true }
|
|
ty::vstore_slice(_) { false }
|
|
}
|
|
}
|
|
|
|
alt (a_bnd, b_bnd) {
|
|
(some(a_bnd), some(b_bnd)) {
|
|
alt (ty::get(a_bnd).struct, ty::get(b_bnd).struct) {
|
|
(ty::ty_box(mt_a), ty::ty_rptr(r_b, mt_b)) {
|
|
let nr_b = ty::mk_box(self.tcx, mt_b);
|
|
self.crosspolinate(anmnt, a, nr_b, r_b)
|
|
}
|
|
(ty::ty_uniq(mt_a), ty::ty_rptr(r_b, mt_b)) {
|
|
let nr_b = ty::mk_uniq(self.tcx, mt_b);
|
|
self.crosspolinate(anmnt, a, nr_b, r_b)
|
|
}
|
|
(ty::ty_estr(vs_a),
|
|
ty::ty_estr(ty::vstore_slice(r_b)))
|
|
if is_borrowable(vs_a) {
|
|
let nr_b = ty::mk_estr(self.tcx, vs_a);
|
|
self.crosspolinate(anmnt, a, nr_b, r_b)
|
|
}
|
|
(ty::ty_str,
|
|
ty::ty_estr(ty::vstore_slice(r_b))) {
|
|
let nr_b = ty::mk_str(self.tcx);
|
|
self.crosspolinate(anmnt, a, nr_b, r_b)
|
|
}
|
|
|
|
(ty::ty_evec(mt_a, vs_a),
|
|
ty::ty_evec(mt_b, ty::vstore_slice(r_b)))
|
|
if is_borrowable(vs_a) {
|
|
let nr_b = ty::mk_evec(self.tcx, mt_b, vs_a);
|
|
self.crosspolinate(anmnt, a, nr_b, r_b)
|
|
}
|
|
(ty::ty_vec(mt_a),
|
|
ty::ty_evec(mt_b, ty::vstore_slice(r_b))) {
|
|
let nr_b = ty::mk_vec(self.tcx, mt_b);
|
|
self.crosspolinate(anmnt, a, nr_b, r_b)
|
|
}
|
|
_ {
|
|
self.sub_tys(a, b)
|
|
}
|
|
}
|
|
}
|
|
_ {
|
|
self.sub_tys(a, b)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn crosspolinate(anmnt: assignment,
|
|
a: ty::t,
|
|
nr_b: ty::t,
|
|
r_b: ty::region) -> ures {
|
|
|
|
#debug["crosspolinate(anmnt=%?, a=%s, nr_b=%s, r_b=%s)",
|
|
anmnt, a.to_str(self), nr_b.to_str(self),
|
|
r_b.to_str(self)];
|
|
|
|
indent {||
|
|
self.sub_tys(a, nr_b).then {||
|
|
let r_a = ty::re_scope(anmnt.borrow_scope);
|
|
#debug["anmnt=%?", anmnt];
|
|
sub(self).contraregions(r_a, r_b).chain {|_r|
|
|
// if successful, add an entry indicating that
|
|
// borrowing occurred
|
|
#debug["borrowing expression #%?", anmnt];
|
|
self.tcx.borrowings.insert(anmnt.expr_id,
|
|
anmnt.borrow_scope);
|
|
uok()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ______________________________________________________________________
|
|
// Type combining
|
|
//
|
|
// There are three type combiners, sub, lub, and gub. `sub` is a bit
|
|
// different from the other two: it takes two types and makes the first
|
|
// a subtype of the other, or fails if this cannot be done. It does
|
|
// return a new type but its return value is meaningless---it is only
|
|
// there to allow for greater code reuse.
|
|
//
|
|
// `lub` and `glb` compute the Least Upper Bound and Greatest Lower
|
|
// Bound respectively of two types `a` and `b`. The LUB is a mutual
|
|
// supertype type `c` where `a <: c` and `b <: c`. As the name
|
|
// implies, it tries to pick the most precise `c` possible. The GLB
|
|
// is a mutual subtype, aiming for the most general such type
|
|
// possible. Both computations may fail.
|
|
//
|
|
// There is a lot of common code for these operations, which is
|
|
// abstracted out into functions named `super_X()` which take a combiner
|
|
// instance as the first parameter. This would be better implemented
|
|
// using traits.
|
|
//
|
|
// The `super_X()` top-level items work for *sub, lub, and glb*: any
|
|
// operation which varies will be dynamically dispatched using a
|
|
// `self.Y()` operation.
|
|
|
|
type cres<T> = result<T,ty::type_err>;
|
|
|
|
iface combine {
|
|
fn infcx() -> infer_ctxt;
|
|
fn tag() -> str;
|
|
|
|
fn mts(a: ty::mt, b: ty::mt) -> cres<ty::mt>;
|
|
fn contratys(a: ty::t, b: ty::t) -> cres<ty::t>;
|
|
fn tys(a: ty::t, b: ty::t) -> cres<ty::t>;
|
|
fn tps(as: [ty::t], bs: [ty::t]) -> cres<[ty::t]>;
|
|
fn self_tys(a: option<ty::t>, b: option<ty::t>) -> cres<option<ty::t>>;
|
|
fn substs(as: ty::substs, bs: ty::substs) -> cres<ty::substs>;
|
|
fn fns(a: ty::fn_ty, b: ty::fn_ty) -> cres<ty::fn_ty>;
|
|
fn flds(a: ty::field, b: ty::field) -> cres<ty::field>;
|
|
fn modes(a: ast::mode, b: ast::mode) -> cres<ast::mode>;
|
|
fn args(a: ty::arg, b: ty::arg) -> cres<ty::arg>;
|
|
fn protos(p1: ast::proto, p2: ast::proto) -> cres<ast::proto>;
|
|
fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style>;
|
|
fn contraregions(a: ty::region, b: ty::region) -> cres<ty::region>;
|
|
fn regions(a: ty::region, b: ty::region) -> cres<ty::region>;
|
|
fn vstores(vk: ty::terr_vstore_kind,
|
|
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore>;
|
|
}
|
|
|
|
enum sub = infer_ctxt; // "subtype", "subregion" etc
|
|
enum lub = infer_ctxt; // "least upper bound" (common supertype)
|
|
enum glb = infer_ctxt; // "greatest lower bound" (common subtype)
|
|
|
|
fn super_substs<C:combine>(
|
|
self: C, a: ty::substs, b: ty::substs) -> cres<ty::substs> {
|
|
|
|
fn eq_opt_regions(infcx: infer_ctxt,
|
|
a: option<ty::region>,
|
|
b: option<ty::region>) -> cres<option<ty::region>> {
|
|
alt (a, b) {
|
|
(none, none) {
|
|
ok(none)
|
|
}
|
|
(some(a), some(b)) {
|
|
infcx.eq_regions(a, b).then {||
|
|
ok(some(a))
|
|
}
|
|
}
|
|
(_, _) {
|
|
// If these two substitutions are for the same type (and
|
|
// they should be), then the type should either
|
|
// consistenly have a region parameter or not have a
|
|
// region parameter.
|
|
infcx.tcx.sess.bug(
|
|
#fmt["substitution a had opt_region %s and \
|
|
b had opt_region %s",
|
|
a.to_str(infcx),
|
|
b.to_str(infcx)]);
|
|
}
|
|
}
|
|
}
|
|
|
|
self.tps(a.tps, b.tps).chain { |tps|
|
|
self.self_tys(a.self_ty, b.self_ty).chain { |self_ty|
|
|
eq_opt_regions(self.infcx(), a.self_r, b.self_r).chain { |self_r|
|
|
ok({self_r: self_r, self_ty: self_ty, tps: tps})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn super_tps<C:combine>(
|
|
self: C, as: [ty::t], bs: [ty::t]) -> cres<[ty::t]> {
|
|
|
|
// Note: type parameters are always treated as *invariant*
|
|
// (otherwise the type system would be unsound). In the
|
|
// future we could allow type parameters to declare a
|
|
// variance.
|
|
|
|
if check vec::same_length(as, bs) {
|
|
iter2(as, bs) {|a, b| self.infcx().eq_tys(a, b) }.then {||
|
|
ok(as)
|
|
}
|
|
} else {
|
|
err(ty::terr_ty_param_size(bs.len(), as.len()))
|
|
}
|
|
}
|
|
|
|
fn super_self_tys<C:combine>(
|
|
self: C, a: option<ty::t>, b: option<ty::t>) -> cres<option<ty::t>> {
|
|
|
|
// Note: the self type parameter is (currently) always treated as
|
|
// *invariant* (otherwise the type system would be unsound).
|
|
|
|
alt (a, b) {
|
|
(none, none) {
|
|
ok(none)
|
|
}
|
|
(some(a), some(b)) {
|
|
self.infcx().eq_tys(a, b).then {||
|
|
ok(some(a))
|
|
}
|
|
}
|
|
(none, some(_)) |
|
|
(some(_), none) {
|
|
// I think it should never happen that we unify two substs and
|
|
// one of them has a self_ty and one doesn't...? I could be
|
|
// wrong about this.
|
|
err(ty::terr_self_substs)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn super_flds<C:combine>(
|
|
self: C, a: ty::field, b: ty::field) -> cres<ty::field> {
|
|
|
|
if a.ident == b.ident {
|
|
self.mts(a.mt, b.mt).chain {|mt|
|
|
ok({ident: a.ident, mt: mt})
|
|
}.chain_err {|e|
|
|
err(ty::terr_in_field(@e, a.ident))
|
|
}
|
|
} else {
|
|
err(ty::terr_record_fields(b.ident, a.ident))
|
|
}
|
|
}
|
|
|
|
fn super_modes<C:combine>(
|
|
self: C, a: ast::mode, b: ast::mode)
|
|
-> cres<ast::mode> {
|
|
|
|
let tcx = self.infcx().tcx;
|
|
ty::unify_mode(tcx, a, b)
|
|
}
|
|
|
|
fn super_args<C:combine>(
|
|
self: C, a: ty::arg, b: ty::arg)
|
|
-> cres<ty::arg> {
|
|
|
|
self.modes(a.mode, b.mode).chain {|m|
|
|
self.contratys(a.ty, b.ty).chain {|t|
|
|
ok({mode: m, ty: t})
|
|
}
|
|
}
|
|
}
|
|
|
|
fn super_vstores<C:combine>(
|
|
self: C, vk: ty::terr_vstore_kind,
|
|
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore> {
|
|
|
|
alt (a, b) {
|
|
(ty::vstore_slice(a_r), ty::vstore_slice(b_r)) {
|
|
self.contraregions(a_r, b_r).chain {|r|
|
|
ok(ty::vstore_slice(r))
|
|
}
|
|
}
|
|
|
|
_ if a == b {
|
|
ok(a)
|
|
}
|
|
|
|
_ {
|
|
err(ty::terr_vstores_differ(vk, b, a))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn super_fns<C:combine>(
|
|
self: C, a_f: ty::fn_ty, b_f: ty::fn_ty) -> cres<ty::fn_ty> {
|
|
|
|
fn argvecs<C:combine>(
|
|
self: C, a_args: [ty::arg], b_args: [ty::arg]) -> cres<[ty::arg]> {
|
|
|
|
if check vec::same_length(a_args, b_args) {
|
|
map2(a_args, b_args) {|a, b| self.args(a, b) }
|
|
} else {
|
|
err(ty::terr_arg_count)
|
|
}
|
|
}
|
|
|
|
self.protos(a_f.proto, b_f.proto).chain {|p|
|
|
self.ret_styles(a_f.ret_style, b_f.ret_style).chain {|rs|
|
|
argvecs(self, a_f.inputs, b_f.inputs).chain {|inputs|
|
|
self.tys(a_f.output, b_f.output).chain {|output|
|
|
//FIXME self.infcx().constrvecs(a_f.constraints,
|
|
//FIXME b_f.constraints).then {||
|
|
ok({proto: p,
|
|
inputs: inputs,
|
|
output: output,
|
|
ret_style: rs,
|
|
constraints: a_f.constraints})
|
|
//FIXME }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn super_tys<C:combine>(
|
|
self: C, a: ty::t, b: ty::t) -> cres<ty::t> {
|
|
|
|
let tcx = self.infcx().tcx;
|
|
alt (ty::get(a).struct, ty::get(b).struct) {
|
|
// The "subtype" ought to be handling cases involving bot or var:
|
|
(ty::ty_bot, _) |
|
|
(_, ty::ty_bot) |
|
|
(ty::ty_var(_), _) |
|
|
(_, ty::ty_var(_)) {
|
|
tcx.sess.bug(
|
|
#fmt["%s: bot and var types should have been handled (%s,%s)",
|
|
self.tag(),
|
|
a.to_str(self.infcx()),
|
|
b.to_str(self.infcx())]);
|
|
}
|
|
|
|
(ty::ty_nil, _) |
|
|
(ty::ty_bool, _) |
|
|
(ty::ty_int(_), _) |
|
|
(ty::ty_uint(_), _) |
|
|
(ty::ty_float(_), _) |
|
|
(ty::ty_str, _) {
|
|
let cfg = tcx.sess.targ_cfg;
|
|
if ty::mach_sty(cfg, a) == ty::mach_sty(cfg, b) {
|
|
ok(a)
|
|
} else {
|
|
err(ty::terr_sorts(b, a))
|
|
}
|
|
}
|
|
|
|
(ty::ty_param(a_n, _), ty::ty_param(b_n, _)) if a_n == b_n {
|
|
ok(a)
|
|
}
|
|
|
|
(ty::ty_enum(a_id, a_substs), ty::ty_enum(b_id, b_substs))
|
|
if a_id == b_id {
|
|
self.substs(a_substs, b_substs).chain {|tps|
|
|
ok(ty::mk_enum(tcx, a_id, tps))
|
|
}
|
|
}
|
|
|
|
(ty::ty_iface(a_id, a_substs), ty::ty_iface(b_id, b_substs))
|
|
if a_id == b_id {
|
|
self.substs(a_substs, b_substs).chain {|substs|
|
|
ok(ty::mk_iface(tcx, a_id, substs))
|
|
}
|
|
}
|
|
|
|
(ty::ty_class(a_id, a_substs), ty::ty_class(b_id, b_substs))
|
|
if a_id == b_id {
|
|
self.substs(a_substs, b_substs).chain {|substs|
|
|
ok(ty::mk_class(tcx, a_id, substs))
|
|
}
|
|
}
|
|
|
|
(ty::ty_box(a_mt), ty::ty_box(b_mt)) {
|
|
self.mts(a_mt, b_mt).chain {|mt|
|
|
ok(ty::mk_box(tcx, mt))
|
|
}
|
|
}
|
|
|
|
(ty::ty_uniq(a_mt), ty::ty_uniq(b_mt)) {
|
|
self.mts(a_mt, b_mt).chain {|mt|
|
|
ok(ty::mk_uniq(tcx, mt))
|
|
}
|
|
}
|
|
|
|
(ty::ty_vec(a_mt), ty::ty_vec(b_mt)) {
|
|
self.mts(a_mt, b_mt).chain {|mt|
|
|
ok(ty::mk_vec(tcx, mt))
|
|
}
|
|
}
|
|
|
|
(ty::ty_ptr(a_mt), ty::ty_ptr(b_mt)) {
|
|
self.mts(a_mt, b_mt).chain {|mt|
|
|
ok(ty::mk_ptr(tcx, mt))
|
|
}
|
|
}
|
|
|
|
(ty::ty_rptr(a_r, a_mt), ty::ty_rptr(b_r, b_mt)) {
|
|
self.contraregions(a_r, b_r).chain {|r|
|
|
self.mts(a_mt, b_mt).chain {|mt|
|
|
ok(ty::mk_rptr(tcx, r, mt))
|
|
}
|
|
}
|
|
}
|
|
|
|
(ty::ty_evec(a_mt, vs_a), ty::ty_evec(b_mt, vs_b)) {
|
|
self.mts(a_mt, b_mt).chain {|mt|
|
|
self.vstores(ty::terr_vec, vs_a, vs_b).chain {|vs|
|
|
ok(ty::mk_evec(tcx, mt, vs))
|
|
}
|
|
}
|
|
}
|
|
|
|
(ty::ty_estr(vs_a), ty::ty_estr(vs_b)) {
|
|
self.vstores(ty::terr_str, vs_a, vs_b).chain {|vs|
|
|
ok(ty::mk_estr(tcx,vs))
|
|
}
|
|
}
|
|
|
|
(ty::ty_res(a_id, a_t, a_substs),
|
|
ty::ty_res(b_id, b_t, b_substs))
|
|
if a_id == b_id {
|
|
self.tys(a_t, b_t).chain {|t|
|
|
self.substs(a_substs, b_substs).chain {|substs|
|
|
ok(ty::mk_res(tcx, a_id, t, substs))
|
|
}
|
|
}
|
|
}
|
|
|
|
(ty::ty_rec(as), ty::ty_rec(bs)) {
|
|
if check vec::same_length(as, bs) {
|
|
map2(as, bs) {|a,b| self.flds(a, b) }.chain {|flds|
|
|
ok(ty::mk_rec(tcx, flds))
|
|
}
|
|
} else {
|
|
err(ty::terr_record_size(bs.len(), as.len()))
|
|
}
|
|
}
|
|
|
|
(ty::ty_tup(as), ty::ty_tup(bs)) {
|
|
if check vec::same_length(as, bs) {
|
|
map2(as, bs) {|a, b| self.tys(a, b) }.chain {|ts|
|
|
ok(ty::mk_tup(tcx, ts))
|
|
}
|
|
} else {
|
|
err(ty::terr_tuple_size(bs.len(), as.len()))
|
|
}
|
|
}
|
|
|
|
(ty::ty_fn(a_fty), ty::ty_fn(b_fty)) {
|
|
self.fns(a_fty, b_fty).chain {|fty|
|
|
ok(ty::mk_fn(tcx, fty))
|
|
}
|
|
}
|
|
|
|
(ty::ty_constr(a_t, a_constrs), ty::ty_constr(b_t, b_constrs)) {
|
|
self.tys(a_t, b_t).chain {|t|
|
|
self.infcx().constrvecs(a_constrs, b_constrs).then {||
|
|
ok(ty::mk_constr(tcx, t, a_constrs))
|
|
}
|
|
}
|
|
}
|
|
|
|
_ { err(ty::terr_sorts(b, a)) }
|
|
}
|
|
}
|
|
|
|
impl of combine for sub {
|
|
fn infcx() -> infer_ctxt { *self }
|
|
fn tag() -> str { "sub" }
|
|
|
|
fn lub() -> lub { lub(*self) }
|
|
|
|
fn contratys(a: ty::t, b: ty::t) -> cres<ty::t> {
|
|
self.tys(b, a)
|
|
}
|
|
|
|
fn contraregions(a: ty::region, b: ty::region) -> cres<ty::region> {
|
|
self.regions(b, a)
|
|
}
|
|
|
|
fn regions(a: ty::region, b: ty::region) -> cres<ty::region> {
|
|
#debug["%s.regions(%s, %s)",
|
|
self.tag(),
|
|
a.to_str(self.infcx()),
|
|
b.to_str(self.infcx())];
|
|
indent {||
|
|
alt (a, b) {
|
|
(ty::re_var(a_id), ty::re_var(b_id)) {
|
|
self.infcx().vars(self.rb, a_id, b_id).then {||
|
|
ok(a)
|
|
}
|
|
}
|
|
(ty::re_var(a_id), _) {
|
|
self.infcx().vart(self.rb, a_id, b).then {||
|
|
ok(a)
|
|
}
|
|
}
|
|
(_, ty::re_var(b_id)) {
|
|
self.infcx().tvar(self.rb, a, b_id).then {||
|
|
ok(a)
|
|
}
|
|
}
|
|
_ {
|
|
self.lub().regions(a, b).compare(b) {||
|
|
ty::terr_regions_differ(b, a)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn mts(a: ty::mt, b: ty::mt) -> cres<ty::mt> {
|
|
#debug("mts(%s <: %s)", a.to_str(*self), b.to_str(*self));
|
|
|
|
if a.mutbl != b.mutbl && b.mutbl != ast::m_const {
|
|
ret err(ty::terr_mutability);
|
|
}
|
|
|
|
alt b.mutbl {
|
|
ast::m_mutbl {
|
|
// If supertype is mut, subtype must match exactly
|
|
// (i.e., invariant if mut):
|
|
self.infcx().eq_tys(a.ty, b.ty).then {|| ok(a) }
|
|
}
|
|
ast::m_imm | ast::m_const {
|
|
// Otherwise we can be covariant:
|
|
self.tys(a.ty, b.ty).chain {|_t| ok(a) }
|
|
}
|
|
}
|
|
}
|
|
|
|
fn protos(a: ast::proto, b: ast::proto) -> cres<ast::proto> {
|
|
self.lub().protos(a, b).compare(b) {||
|
|
ty::terr_proto_mismatch(b, a)
|
|
}
|
|
}
|
|
|
|
fn ret_styles(a: ret_style, b: ret_style) -> cres<ret_style> {
|
|
self.lub().ret_styles(a, b).compare(b) {||
|
|
ty::terr_ret_style_mismatch(b, a)
|
|
}
|
|
}
|
|
|
|
fn tys(a: ty::t, b: ty::t) -> cres<ty::t> {
|
|
#debug("%s.tys(%s, %s)", self.tag(),
|
|
a.to_str(*self), b.to_str(*self));
|
|
if a == b { ret ok(a); }
|
|
indent {||
|
|
alt (ty::get(a).struct, ty::get(b).struct) {
|
|
(ty::ty_bot, _) {
|
|
ok(a)
|
|
}
|
|
(ty::ty_var(a_id), ty::ty_var(b_id)) {
|
|
self.infcx().vars(self.vb, a_id, b_id).then {|| ok(a) }
|
|
}
|
|
(ty::ty_var(a_id), _) {
|
|
self.infcx().vart(self.vb, a_id, b).then {|| ok(a) }
|
|
}
|
|
(_, ty::ty_var(b_id)) {
|
|
self.infcx().tvar(self.vb, a, b_id).then {|| ok(a) }
|
|
}
|
|
(_, ty::ty_bot) {
|
|
err(ty::terr_sorts(b, a))
|
|
}
|
|
_ {
|
|
super_tys(self, a, b)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Traits please:
|
|
|
|
fn flds(a: ty::field, b: ty::field) -> cres<ty::field> {
|
|
super_flds(self, a, b)
|
|
}
|
|
|
|
fn vstores(vk: ty::terr_vstore_kind,
|
|
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore> {
|
|
super_vstores(self, vk, a, b)
|
|
}
|
|
|
|
fn modes(a: ast::mode, b: ast::mode) -> cres<ast::mode> {
|
|
super_modes(self, a, b)
|
|
}
|
|
|
|
fn args(a: ty::arg, b: ty::arg) -> cres<ty::arg> {
|
|
super_args(self, a, b)
|
|
}
|
|
|
|
fn fns(a: ty::fn_ty, b: ty::fn_ty) -> cres<ty::fn_ty> {
|
|
super_fns(self, a, b)
|
|
}
|
|
|
|
fn substs(as: ty::substs, bs: ty::substs) -> cres<ty::substs> {
|
|
super_substs(self, as, bs)
|
|
}
|
|
|
|
fn tps(as: [ty::t], bs: [ty::t]) -> cres<[ty::t]> {
|
|
super_tps(self, as, bs)
|
|
}
|
|
|
|
fn self_tys(a: option<ty::t>, b: option<ty::t>) -> cres<option<ty::t>> {
|
|
super_self_tys(self, a, b)
|
|
}
|
|
}
|
|
|
|
impl of combine for lub {
|
|
fn infcx() -> infer_ctxt { *self }
|
|
fn tag() -> str { "lub" }
|
|
|
|
fn bot_ty(b: ty::t) -> cres<ty::t> { ok(b) }
|
|
fn ty_bot(b: ty::t) -> cres<ty::t> { self.bot_ty(b) } // commutative
|
|
|
|
fn mts(a: ty::mt, b: ty::mt) -> cres<ty::mt> {
|
|
let tcx = self.infcx().tcx;
|
|
|
|
#debug("%s.mts(%s, %s)",
|
|
self.tag(),
|
|
mt_to_str(tcx, a),
|
|
mt_to_str(tcx, b));
|
|
|
|
let m = if a.mutbl == b.mutbl {
|
|
a.mutbl
|
|
} else {
|
|
ast::m_const
|
|
};
|
|
|
|
alt m {
|
|
ast::m_imm | ast::m_const {
|
|
self.tys(a.ty, b.ty).chain {|t|
|
|
ok({ty: t, mutbl: m})
|
|
}
|
|
}
|
|
|
|
ast::m_mutbl {
|
|
self.infcx().try {||
|
|
self.infcx().eq_tys(a.ty, b.ty).then {||
|
|
ok({ty: a.ty, mutbl: m})
|
|
}
|
|
}.chain_err {|_e|
|
|
self.tys(a.ty, b.ty).chain {|t|
|
|
ok({ty: t, mutbl: ast::m_const})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn contratys(a: ty::t, b: ty::t) -> cres<ty::t> {
|
|
glb(self.infcx()).tys(a, b)
|
|
}
|
|
|
|
fn protos(p1: ast::proto, p2: ast::proto) -> cres<ast::proto> {
|
|
if p1 == ast::proto_bare {
|
|
ok(p2)
|
|
} else if p2 == ast::proto_bare {
|
|
ok(p1)
|
|
} else if p1 == p2 {
|
|
ok(p1)
|
|
} else {
|
|
ok(ast::proto_any)
|
|
}
|
|
}
|
|
|
|
fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style> {
|
|
alt (r1, r2) {
|
|
(ast::return_val, _) |
|
|
(_, ast::return_val) {
|
|
ok(ast::return_val)
|
|
}
|
|
(ast::noreturn, ast::noreturn) {
|
|
ok(ast::noreturn)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn contraregions(a: ty::region, b: ty::region) -> cres<ty::region> {
|
|
ret glb(self.infcx()).regions(a, b);
|
|
}
|
|
|
|
fn regions(a: ty::region, b: ty::region) -> cres<ty::region> {
|
|
#debug["%s.regions(%?, %?)",
|
|
self.tag(),
|
|
a.to_str(self.infcx()),
|
|
b.to_str(self.infcx())];
|
|
|
|
indent {||
|
|
alt (a, b) {
|
|
(ty::re_static, _) | (_, ty::re_static) {
|
|
ok(ty::re_static) // nothing lives longer than static
|
|
}
|
|
|
|
(ty::re_var(_), _) | (_, ty::re_var(_)) {
|
|
lattice_rvars(self, a, b)
|
|
}
|
|
|
|
(f @ ty::re_free(f_id, _), ty::re_scope(s_id)) |
|
|
(ty::re_scope(s_id), f @ ty::re_free(f_id, _)) {
|
|
// For LUB, generally the scope is within the fn and
|
|
// the free region is a parameter to the fn. In that case,
|
|
// the free region will always live as long as the fn,
|
|
// which is longer than the scope.
|
|
//
|
|
// However, with nested fns, it can happen that the
|
|
// scope surrounds the fn itself. In that case, we do
|
|
// not know which will live longer---it depends on the
|
|
// value provided for the free region in any given
|
|
// call. And so we must just back off to re_static as
|
|
// the LUB.
|
|
let rm = self.infcx().tcx.region_map;
|
|
alt region::nearest_common_ancestor(rm, f_id, s_id) {
|
|
some(r_id) if r_id == f_id { ok(f) }
|
|
_ { ok(ty::re_static) }
|
|
}
|
|
}
|
|
|
|
(ty::re_scope(a_id), ty::re_scope(b_id)) {
|
|
// 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)) }
|
|
_ { ok(ty::re_static) }
|
|
}
|
|
}
|
|
|
|
// 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 {
|
|
ok(a)
|
|
} else {
|
|
ok(ty::re_static)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Traits please:
|
|
|
|
fn tys(a: ty::t, b: ty::t) -> cres<ty::t> {
|
|
lattice_tys(self, a, b)
|
|
}
|
|
|
|
fn flds(a: ty::field, b: ty::field) -> cres<ty::field> {
|
|
super_flds(self, a, b)
|
|
}
|
|
|
|
fn vstores(vk: ty::terr_vstore_kind,
|
|
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore> {
|
|
super_vstores(self, vk, a, b)
|
|
}
|
|
|
|
fn modes(a: ast::mode, b: ast::mode) -> cres<ast::mode> {
|
|
super_modes(self, a, b)
|
|
}
|
|
|
|
fn args(a: ty::arg, b: ty::arg) -> cres<ty::arg> {
|
|
super_args(self, a, b)
|
|
}
|
|
|
|
fn fns(a: ty::fn_ty, b: ty::fn_ty) -> cres<ty::fn_ty> {
|
|
super_fns(self, a, b)
|
|
}
|
|
|
|
fn substs(as: ty::substs, bs: ty::substs) -> cres<ty::substs> {
|
|
super_substs(self, as, bs)
|
|
}
|
|
|
|
fn tps(as: [ty::t], bs: [ty::t]) -> cres<[ty::t]> {
|
|
super_tps(self, as, bs)
|
|
}
|
|
|
|
fn self_tys(a: option<ty::t>, b: option<ty::t>) -> cres<option<ty::t>> {
|
|
super_self_tys(self, a, b)
|
|
}
|
|
}
|
|
|
|
impl of combine for glb {
|
|
fn infcx() -> infer_ctxt { *self }
|
|
fn tag() -> str { "glb" }
|
|
|
|
fn mts(a: ty::mt, b: ty::mt) -> cres<ty::mt> {
|
|
let tcx = self.infcx().tcx;
|
|
|
|
#debug("%s.mts(%s, %s)",
|
|
self.tag(),
|
|
mt_to_str(tcx, a),
|
|
mt_to_str(tcx, b));
|
|
|
|
alt (a.mutbl, b.mutbl) {
|
|
// If one side or both is mut, then the GLB must use
|
|
// the precise type from the mut side.
|
|
(ast::m_mutbl, ast::m_const) {
|
|
sub(*self).tys(a.ty, b.ty).chain {|_t|
|
|
ok({ty: a.ty, mutbl: ast::m_mutbl})
|
|
}
|
|
}
|
|
(ast::m_const, ast::m_mutbl) {
|
|
sub(*self).tys(b.ty, a.ty).chain {|_t|
|
|
ok({ty: b.ty, mutbl: ast::m_mutbl})
|
|
}
|
|
}
|
|
(ast::m_mutbl, ast::m_mutbl) {
|
|
self.infcx().eq_tys(a.ty, b.ty).then {||
|
|
ok({ty: a.ty, mutbl: ast::m_mutbl})
|
|
}
|
|
}
|
|
|
|
// If one side or both is immutable, we can use the GLB of
|
|
// both sides but mutbl must be `m_imm`.
|
|
(ast::m_imm, ast::m_const) |
|
|
(ast::m_const, ast::m_imm) |
|
|
(ast::m_imm, ast::m_imm) {
|
|
self.tys(a.ty, b.ty).chain {|t|
|
|
ok({ty: t, mutbl: ast::m_imm})
|
|
}
|
|
}
|
|
|
|
// If both sides are const, then we can use GLB of both
|
|
// sides and mutbl of only `m_const`.
|
|
(ast::m_const, ast::m_const) {
|
|
self.tys(a.ty, b.ty).chain {|t|
|
|
ok({ty: t, mutbl: ast::m_const})
|
|
}
|
|
}
|
|
|
|
// There is no mutual subtype of these combinations.
|
|
(ast::m_mutbl, ast::m_imm) |
|
|
(ast::m_imm, ast::m_mutbl) {
|
|
err(ty::terr_mutability)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn contratys(a: ty::t, b: ty::t) -> cres<ty::t> {
|
|
lub(self.infcx()).tys(a, b)
|
|
}
|
|
|
|
fn protos(p1: ast::proto, p2: ast::proto) -> cres<ast::proto> {
|
|
if p1 == ast::proto_any {
|
|
ok(p2)
|
|
} else if p2 == ast::proto_any {
|
|
ok(p1)
|
|
} else if p1 == p2 {
|
|
ok(p1)
|
|
} else {
|
|
ok(ast::proto_bare)
|
|
}
|
|
}
|
|
|
|
fn ret_styles(r1: ret_style, r2: ret_style) -> cres<ret_style> {
|
|
alt (r1, r2) {
|
|
(ast::return_val, ast::return_val) {
|
|
ok(ast::return_val)
|
|
}
|
|
(ast::noreturn, _) |
|
|
(_, ast::noreturn) {
|
|
ok(ast::noreturn)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn regions(a: ty::region, b: ty::region) -> cres<ty::region> {
|
|
#debug["%s.regions(%?, %?)",
|
|
self.tag(),
|
|
a.to_str(self.infcx()),
|
|
b.to_str(self.infcx())];
|
|
|
|
indent {||
|
|
alt (a, b) {
|
|
(ty::re_static, r) | (r, ty::re_static) {
|
|
// static lives longer than everything else
|
|
ok(r)
|
|
}
|
|
|
|
(ty::re_var(_), _) | (_, ty::re_var(_)) {
|
|
lattice_rvars(self, a, b)
|
|
}
|
|
|
|
(ty::re_free(f_id, _), s @ ty::re_scope(s_id)) |
|
|
(s @ ty::re_scope(s_id), ty::re_free(f_id, _)) {
|
|
// For GLB, generally the scope is within the fn and
|
|
// the free region is a parameter to the fn. In that case,
|
|
// the scope is always shorter than the free region.
|
|
//
|
|
// However, with nested fns, it can happen that the
|
|
// scope surrounds the fn itself. In that case, we do
|
|
// not know which will live longer---it depends on the
|
|
// value provided for the free region in any given
|
|
// call. And so we cannot give a GLB.
|
|
let rm = self.infcx().tcx.region_map;
|
|
alt region::nearest_common_ancestor(rm, f_id, s_id) {
|
|
some(r_id) if r_id == f_id { ok(s) }
|
|
_ { err(ty::terr_regions_differ(b, a)) }
|
|
}
|
|
}
|
|
|
|
(ty::re_scope(a_id), ty::re_scope(b_id)) {
|
|
// 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(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 {
|
|
ok(a)
|
|
} else {
|
|
err(ty::terr_regions_differ(b, a))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn contraregions(a: ty::region, b: ty::region) -> cres<ty::region> {
|
|
lub(self.infcx()).regions(a, b)
|
|
}
|
|
|
|
fn tys(a: ty::t, b: ty::t) -> cres<ty::t> {
|
|
lattice_tys(self, a, b)
|
|
}
|
|
|
|
// Traits please:
|
|
|
|
fn flds(a: ty::field, b: ty::field) -> cres<ty::field> {
|
|
super_flds(self, a, b)
|
|
}
|
|
|
|
fn vstores(vk: ty::terr_vstore_kind,
|
|
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore> {
|
|
super_vstores(self, vk, a, b)
|
|
}
|
|
|
|
fn modes(a: ast::mode, b: ast::mode) -> cres<ast::mode> {
|
|
super_modes(self, a, b)
|
|
}
|
|
|
|
fn args(a: ty::arg, b: ty::arg) -> cres<ty::arg> {
|
|
super_args(self, a, b)
|
|
}
|
|
|
|
fn fns(a: ty::fn_ty, b: ty::fn_ty) -> cres<ty::fn_ty> {
|
|
super_fns(self, a, b)
|
|
}
|
|
|
|
fn substs(as: ty::substs, bs: ty::substs) -> cres<ty::substs> {
|
|
super_substs(self, as, bs)
|
|
}
|
|
|
|
fn tps(as: [ty::t], bs: [ty::t]) -> cres<[ty::t]> {
|
|
super_tps(self, as, bs)
|
|
}
|
|
|
|
fn self_tys(a: option<ty::t>, b: option<ty::t>) -> cres<option<ty::t>> {
|
|
super_self_tys(self, a, b)
|
|
}
|
|
}
|
|
|
|
// ______________________________________________________________________
|
|
// Lattice operations on variables
|
|
//
|
|
// This is common code used by both LUB and GLB to compute the LUB/GLB
|
|
// for pairs of variables or for variables and values.
|
|
|
|
iface lattice_ops {
|
|
fn bnd<T:copy>(b: bounds<T>) -> option<T>;
|
|
fn with_bnd<T:copy>(b: bounds<T>, t: T) -> bounds<T>;
|
|
fn ty_bot(t: ty::t) -> cres<ty::t>;
|
|
}
|
|
|
|
impl of lattice_ops for lub {
|
|
fn bnd<T:copy>(b: bounds<T>) -> option<T> { b.ub }
|
|
fn with_bnd<T:copy>(b: bounds<T>, t: T) -> bounds<T> {
|
|
{ub: some(t) with b}
|
|
}
|
|
fn ty_bot(t: ty::t) -> cres<ty::t> {
|
|
ok(t)
|
|
}
|
|
}
|
|
|
|
impl of lattice_ops for glb {
|
|
fn bnd<T:copy>(b: bounds<T>) -> option<T> { b.lb }
|
|
fn with_bnd<T:copy>(b: bounds<T>, t: T) -> bounds<T> {
|
|
{lb: some(t) with b}
|
|
}
|
|
fn ty_bot(_t: ty::t) -> cres<ty::t> {
|
|
ok(ty::mk_bot(self.infcx().tcx))
|
|
}
|
|
}
|
|
|
|
fn lattice_tys<L:lattice_ops combine>(
|
|
self: L, a: ty::t, b: ty::t) -> cres<ty::t> {
|
|
|
|
#debug("%s.tys(%s, %s)", self.tag(),
|
|
a.to_str(self.infcx()),
|
|
b.to_str(self.infcx()));
|
|
if a == b { ret ok(a); }
|
|
indent {||
|
|
alt (ty::get(a).struct, ty::get(b).struct) {
|
|
(ty::ty_bot, _) { self.ty_bot(b) }
|
|
(_, ty::ty_bot) { self.ty_bot(a) }
|
|
|
|
(ty::ty_var(a_id), ty::ty_var(b_id)) {
|
|
lattice_vars(self, self.infcx().vb,
|
|
a, a_id, b_id,
|
|
{|x, y| self.tys(x, y) })
|
|
}
|
|
|
|
(ty::ty_var(a_id), _) {
|
|
lattice_var_t(self, self.infcx().vb, a_id, b,
|
|
{|x, y| self.tys(x, y) })
|
|
}
|
|
|
|
(_, ty::ty_var(b_id)) {
|
|
lattice_var_t(self, self.infcx().vb, b_id, a,
|
|
{|x, y| self.tys(x, y) })
|
|
}
|
|
|
|
_ {
|
|
super_tys(self, a, b)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pull out some common code from LUB/GLB for handling region vars:
|
|
fn lattice_rvars<L:lattice_ops combine>(
|
|
self: L, a: ty::region, b: ty::region) -> cres<ty::region> {
|
|
|
|
alt (a, b) {
|
|
(ty::re_var(a_id), ty::re_var(b_id)) {
|
|
lattice_vars(self, self.infcx().rb,
|
|
a, a_id, b_id,
|
|
{|x, y| self.regions(x, y) })
|
|
}
|
|
|
|
(ty::re_var(v_id), r) | (r, ty::re_var(v_id)) {
|
|
lattice_var_t(self, self.infcx().rb,
|
|
v_id, r,
|
|
{|x, y| self.regions(x, y) })
|
|
}
|
|
|
|
_ {
|
|
self.infcx().tcx.sess.bug(
|
|
#fmt["%s: lattice_rvars invoked with a=%s and b=%s, \
|
|
neither of which are region variables",
|
|
self.tag(),
|
|
a.to_str(self.infcx()),
|
|
b.to_str(self.infcx())]);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn lattice_vars<V:copy vid, T:copy to_str st, L:lattice_ops combine>(
|
|
self: L, 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_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.lattice_vars(%s=%s <: %s=%s)",
|
|
self.tag(),
|
|
a_vid.to_str(), a_bounds.to_str(self.infcx()),
|
|
b_vid.to_str(), b_bounds.to_str(self.infcx())];
|
|
|
|
if a_vid == b_vid {
|
|
ret ok(a_t);
|
|
}
|
|
|
|
// 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 {|| c_ts(a_ty, b_ty) } {
|
|
ok(t) { ret ok(t); }
|
|
err(_) { /*fallthrough */ }
|
|
}
|
|
}
|
|
_ {/*fallthrough*/}
|
|
}
|
|
|
|
// Otherwise, we need to merge A and B into one variable. We can
|
|
// then use either variable as an upper bound:
|
|
self.infcx().vars(vb, a_vid, b_vid).then {||
|
|
ok(a_t)
|
|
}
|
|
}
|
|
|
|
fn lattice_var_t<V:copy vid, T:copy to_str st, L:lattice_ops combine>(
|
|
self: L, vb: vals_and_bindings<V, T>,
|
|
a_id: V, b: T,
|
|
c_ts: fn(T, T) -> cres<T>) -> cres<T> {
|
|
|
|
let {root: a_id, bounds: a_bounds} = self.infcx().get(vb, a_id);
|
|
|
|
// 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.lattice_vart(%s=%s <: %s)",
|
|
self.tag(),
|
|
a_id.to_str(), a_bounds.to_str(self.infcx()),
|
|
b.to_str(self.infcx())];
|
|
|
|
alt self.bnd(a_bounds) {
|
|
some(a_bnd) {
|
|
// If a has an upper bound, return it.
|
|
#debug["bnd=some(%s)", a_bnd.to_str(self.infcx())];
|
|
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.
|
|
#debug["bnd=none"];
|
|
let a_bounds = self.with_bnd(a_bounds, b);
|
|
self.infcx().bnds(a_bounds.lb, a_bounds.ub).then {||
|
|
self.infcx().set(vb, a_id, bounded(a_bounds));
|
|
ok(b)
|
|
}
|
|
}
|
|
}
|
|
}
|