If you define lang items in your crate, add `#[feature(lang_items)]`.
If you define intrinsics (`extern "rust-intrinsic"`), add
`#[feature(intrinsics)]`.
Closes #12858.
[breaking-change]
r? @brson
}
#[deriving(Clone, PartialEq, Eq, Hash)]
-pub struct TyVid(pub uint);
+pub struct TyVid {
+ pub index: uint
+}
#[deriving(Clone, PartialEq, Eq, Hash)]
-pub struct IntVid(pub uint);
+pub struct IntVid {
+ pub index: uint
+}
#[deriving(Clone, PartialEq, Eq, Hash)]
-pub struct FloatVid(pub uint);
+pub struct FloatVid {
+ pub index: uint
+}
#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)]
pub struct RegionVid {
- pub id: uint
+ pub index: uint
}
#[deriving(Clone, PartialEq, Eq, Hash)]
}
}
-pub trait Vid {
- fn to_uint(&self) -> uint;
-}
-
-impl Vid for TyVid {
- fn to_uint(&self) -> uint { let TyVid(v) = *self; v }
-}
-
impl fmt::Show for TyVid {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result{
- write!(f, "<generic #{}>", self.to_uint())
+ write!(f, "<generic #{}>", self.index)
}
}
-impl Vid for IntVid {
- fn to_uint(&self) -> uint { let IntVid(v) = *self; v }
-}
-
impl fmt::Show for IntVid {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "<generic integer #{}>", self.to_uint())
+ write!(f, "<generic integer #{}>", self.index)
}
}
-impl Vid for FloatVid {
- fn to_uint(&self) -> uint { let FloatVid(v) = *self; v }
-}
-
impl fmt::Show for FloatVid {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "<generic float #{}>", self.to_uint())
+ write!(f, "<generic float #{}>", self.index)
}
}
-impl Vid for RegionVid {
- fn to_uint(&self) -> uint { self.id }
-}
-
impl fmt::Show for RegionVid {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.id.fmt(f)
+ write!(f, "'<generic lifetime #{}>", self.index)
}
}
fn can_unify_universally_quantified<'a>(&self,
a: &'a UniversalQuantificationResult,
b: &'a UniversalQuantificationResult)
- -> bool {
+ -> bool
+ {
infer::can_mk_subty(&self.inference_context,
a.monotype,
b.monotype).is_ok()
use middle::typeck::infer::{CoerceResult, resolve_type, Coercion};
use middle::typeck::infer::combine::{CombineFields, Combine};
use middle::typeck::infer::sub::Sub;
-use middle::typeck::infer::to_str::InferStr;
use middle::typeck::infer::resolve::try_resolve_tvar_shallow;
use util::common::indenter;
+use util::ppaux::Repr;
use syntax::abi;
use syntax::ast::MutImmutable;
pub fn tys(&self, a: ty::t, b: ty::t) -> CoerceResult {
debug!("Coerce.tys({} => {})",
- a.inf_str(self.get_ref().infcx),
- b.inf_str(self.get_ref().infcx));
+ a.repr(self.get_ref().infcx.tcx),
+ b.repr(self.get_ref().infcx.tcx));
let _indent = indenter();
// Examine the supertype and consider auto-borrowing.
mt_b: ty::mt)
-> CoerceResult {
debug!("coerce_borrowed_pointer(a={}, sty_a={:?}, b={}, mt_b={:?})",
- a.inf_str(self.get_ref().infcx), sty_a,
- b.inf_str(self.get_ref().infcx), mt_b);
+ a.repr(self.get_ref().infcx.tcx), sty_a,
+ b.repr(self.get_ref().infcx.tcx), mt_b);
// If we have a parameter of type `&M T_a` and the value
// provided is `expr`, we will be adding an implicit borrow,
b: ty::t)
-> CoerceResult {
debug!("coerce_borrowed_string(a={}, sty_a={:?}, b={})",
- a.inf_str(self.get_ref().infcx), sty_a,
- b.inf_str(self.get_ref().infcx));
+ a.repr(self.get_ref().infcx.tcx), sty_a,
+ b.repr(self.get_ref().infcx.tcx));
match *sty_a {
ty::ty_uniq(t) => match ty::get(t).sty {
mutbl_b: ast::Mutability)
-> CoerceResult {
debug!("coerce_borrowed_vector(a={}, sty_a={:?}, b={})",
- a.inf_str(self.get_ref().infcx), sty_a,
- b.inf_str(self.get_ref().infcx));
+ a.repr(self.get_ref().infcx.tcx), sty_a,
+ b.repr(self.get_ref().infcx.tcx));
let sub = Sub(self.get_ref().clone());
let coercion = Coercion(self.get_ref().trace.clone());
b_mutbl: ast::Mutability) -> CoerceResult
{
debug!("coerce_borrowed_object(a={}, sty_a={:?}, b={})",
- a.inf_str(self.get_ref().infcx), sty_a,
- b.inf_str(self.get_ref().infcx));
+ a.repr(self.get_ref().infcx.tcx), sty_a,
+ b.repr(self.get_ref().infcx.tcx));
let tcx = self.get_ref().infcx.tcx;
let coercion = Coercion(self.get_ref().trace.clone());
b: ty::t)
-> CoerceResult {
debug!("coerce_borrowed_fn(a={}, sty_a={:?}, b={})",
- a.inf_str(self.get_ref().infcx), sty_a,
- b.inf_str(self.get_ref().infcx));
+ a.repr(self.get_ref().infcx.tcx), sty_a,
+ b.repr(self.get_ref().infcx.tcx));
match *sty_a {
ty::ty_bare_fn(ref f) => {
self.unpack_actual_value(b, |sty_b| {
debug!("coerce_from_bare_fn(a={}, b={})",
- a.inf_str(self.get_ref().infcx), b.inf_str(self.get_ref().infcx));
+ a.repr(self.get_ref().infcx.tcx), b.repr(self.get_ref().infcx.tcx));
if fn_ty_a.abi != abi::Rust || fn_ty_a.fn_style != ast::NormalFn {
return self.subtype(a, b);
mt_b: ty::mt)
-> CoerceResult {
debug!("coerce_unsafe_ptr(a={}, sty_a={:?}, b={})",
- a.inf_str(self.get_ref().infcx), sty_a,
- b.inf_str(self.get_ref().infcx));
+ a.repr(self.get_ref().infcx.tcx), sty_a,
+ b.repr(self.get_ref().infcx.tcx));
let mt_a = match *sty_a {
ty::ty_rptr(_, mt) => mt,
bounds: ty::BuiltinBounds) -> CoerceResult {
debug!("coerce_object(a={}, sty_a={:?}, b={})",
- a.inf_str(self.get_ref().infcx), sty_a,
- b.inf_str(self.get_ref().infcx));
+ a.repr(self.get_ref().infcx.tcx), sty_a,
+ b.repr(self.get_ref().infcx.tcx));
Ok(Some(ty::AutoObject(trait_store, bounds,
trait_def_id, trait_substs.clone())))
use middle::typeck::infer::glb::Glb;
use middle::typeck::infer::lub::Lub;
use middle::typeck::infer::sub::Sub;
-use middle::typeck::infer::to_str::InferStr;
-use middle::typeck::infer::unify::InferCtxtMethods;
+use middle::typeck::infer::unify::InferCtxtMethodsForSimplyUnifiableTypes;
use middle::typeck::infer::{InferCtxt, cres, ures};
use middle::typeck::infer::{TypeTrace};
use util::common::indent;
a: ty::TraitStore,
b: ty::TraitStore)
-> cres<ty::TraitStore> {
- debug!("{}.trait_stores(a={:?}, b={:?})", self.tag(), a, b);
+ debug!("{}.trait_stores(a={}, b={})", self.tag(), a, b);
match (a, b) {
(ty::RegionTraitStore(a_r, a_m),
tcx.sess.bug(
format!("{}: bot and var types should have been handled ({},{})",
this.tag(),
- a.inf_str(this.infcx()),
- b.inf_str(this.infcx())).as_slice());
+ a.repr(this.infcx().tcx),
+ b.repr(this.infcx().tcx)).as_slice());
}
// Relate integral variables to other types
use middle::typeck::infer::lattice::*;
use middle::typeck::infer::lub::Lub;
use middle::typeck::infer::sub::Sub;
-use middle::typeck::infer::to_str::InferStr;
use middle::typeck::infer::{cres, InferCtxt};
use middle::typeck::infer::{TypeTrace, Subtype};
use middle::typeck::infer::fold_regions_in_sig;
+use middle::typeck::infer::region_inference::RegionMark;
use syntax::ast::{Many, Once, MutImmutable, MutMutable};
use syntax::ast::{NormalFn, UnsafeFn, NodeId};
use syntax::ast::{Onceness, FnStyle};
use std::collections::HashMap;
use util::common::{indenter};
use util::ppaux::mt_to_str;
+use util::ppaux::Repr;
pub struct Glb<'f>(pub CombineFields<'f>); // "greatest lower bound" (common subtype)
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
debug!("{}.regions({:?}, {:?})",
self.tag(),
- a.inf_str(self.get_ref().infcx),
- b.inf_str(self.get_ref().infcx));
+ a.repr(self.get_ref().infcx.tcx),
+ b.repr(self.get_ref().infcx.tcx));
Ok(self.get_ref().infcx.region_vars.glb_regions(Subtype(self.trace()), a, b))
}
// please see the large comment in `region_inference.rs`.
debug!("{}.fn_sigs({:?}, {:?})",
- self.tag(), a.inf_str(self.get_ref().infcx), b.inf_str(self.get_ref().infcx));
+ self.tag(), a.repr(self.get_ref().infcx.tcx), b.repr(self.get_ref().infcx.tcx));
let _indenter = indenter();
- // Take a snapshot. We'll never roll this back, but in later
- // phases we do want to be able to examine "all bindings that
- // were created as part of this type comparison", and making a
- // snapshot is a convenient way to do that.
- let snapshot = self.get_ref().infcx.region_vars.start_snapshot();
+ // Make a mark so we can examine "all bindings that were
+ // created as part of this type comparison".
+ let mark = self.get_ref().infcx.region_vars.mark();
// Instantiate each bound region with a fresh region variable.
let (a_with_fresh, a_map) =
// Collect constraints.
let sig0 = if_ok!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh));
- debug!("sig0 = {}", sig0.inf_str(self.get_ref().infcx));
+ debug!("sig0 = {}", sig0.repr(self.get_ref().infcx.tcx));
// Generalize the regions appearing in fn_ty0 if possible
let new_vars =
- self.get_ref().infcx.region_vars.vars_created_since_snapshot(snapshot);
+ self.get_ref().infcx.region_vars.vars_created_since_mark(mark);
let sig1 =
fold_regions_in_sig(
self.get_ref().infcx.tcx,
&sig0,
|r| {
generalize_region(self,
- snapshot,
+ mark,
new_vars.as_slice(),
sig0.binder_id,
&a_map,
b_vars.as_slice(),
r)
});
- debug!("sig1 = {}", sig1.inf_str(self.get_ref().infcx));
+ debug!("sig1 = {}", sig1.repr(self.get_ref().infcx.tcx));
return Ok(sig1);
fn generalize_region(this: &Glb,
- snapshot: uint,
+ mark: RegionMark,
new_vars: &[RegionVid],
new_binder_id: NodeId,
a_map: &HashMap<ty::BoundRegion, ty::Region>,
return r0;
}
- let tainted = this.get_ref().infcx.region_vars.tainted(snapshot, r0);
+ let tainted = this.get_ref().infcx.region_vars.tainted(mark, r0);
let mut a_r = None;
let mut b_r = None;
* a lattice.
*/
-
-use middle::ty::{RegionVid, TyVar, Vid};
+use middle::ty::{RegionVid, TyVar};
use middle::ty;
-use middle::typeck::infer::{then, ToUres};
+use middle::typeck::infer::{ToUres};
use middle::typeck::infer::*;
use middle::typeck::infer::combine::*;
use middle::typeck::infer::glb::Glb;
use middle::typeck::infer::lub::Lub;
-use middle::typeck::infer::unify::*;
+use middle::typeck::infer::unify::{Root, UnifyKey};
use middle::typeck::infer::sub::Sub;
-use middle::typeck::infer::to_str::InferStr;
-use util::common::indenter;
+use util::ppaux::Repr;
use std::collections::HashMap;
-trait LatticeValue {
+trait LatticeValue : Clone + Repr + PartialEq {
fn sub(cf: CombineFields, a: &Self, b: &Self) -> ures;
fn lub(cf: CombineFields, a: &Self, b: &Self) -> cres<Self>;
fn glb(cf: CombineFields, a: &Self, b: &Self) -> cres<Self>;
}
}
-pub trait CombineFieldsLatticeMethods {
- fn var_sub_var<T:Clone + InferStr + LatticeValue,
- V:Clone + PartialEq + ToStr + Vid + UnifyVid<Bounds<T>>>(&self,
- a_id: V,
- b_id: V)
- -> ures;
+pub trait CombineFieldsLatticeMethods<T:LatticeValue, K:UnifyKey<Bounds<T>>> {
+ /// make variable a subtype of variable
+ fn var_sub_var(&self,
+ a_id: K,
+ b_id: K)
+ -> ures;
+
/// make variable a subtype of T
- fn var_sub_t<T:Clone + InferStr + LatticeValue,
- V:Clone + PartialEq + ToStr + Vid + UnifyVid<Bounds<T>>>(
- &self,
- a_id: V,
+ fn var_sub_t(&self,
+ a_id: K,
b: T)
-> ures;
- fn t_sub_var<T:Clone + InferStr + LatticeValue,
- V:Clone + PartialEq + ToStr + Vid + UnifyVid<Bounds<T>>>(
- &self,
+
+ /// make T a subtype of variable
+ fn t_sub_var(&self,
a: T,
- b_id: V)
+ b_id: K)
-> ures;
- fn merge_bnd<T:Clone + InferStr + LatticeValue>(
- &self,
- a: &Bound<T>,
- b: &Bound<T>,
- lattice_op: LatticeOp<T>)
- -> cres<Bound<T>>;
- fn set_var_to_merged_bounds<T:Clone + InferStr + LatticeValue,
- V:Clone+PartialEq+ToStr+Vid+UnifyVid<Bounds<T>>>(
- &self,
- v_id: V,
+
+ fn set_var_to_merged_bounds(&self,
+ v_id: K,
a: &Bounds<T>,
b: &Bounds<T>,
rank: uint)
-> ures;
- fn bnds<T:Clone + InferStr + LatticeValue>(
- &self,
- a: &Bound<T>,
- b: &Bound<T>)
- -> ures;
}
-impl<'f> CombineFieldsLatticeMethods for CombineFields<'f> {
- fn var_sub_var<T:Clone + InferStr + LatticeValue,
- V:Clone + PartialEq + ToStr + Vid + UnifyVid<Bounds<T>>>(
- &self,
- a_id: V,
- b_id: V)
- -> ures {
+pub trait CombineFieldsLatticeMethods2<T:LatticeValue> {
+ fn merge_bnd(&self,
+ a: &Bound<T>,
+ b: &Bound<T>,
+ lattice_op: LatticeOp<T>)
+ -> cres<Bound<T>>;
+
+ fn bnds(&self, a: &Bound<T>, b: &Bound<T>) -> ures;
+}
+
+impl<'f,T:LatticeValue, K:UnifyKey<Bounds<T>>>
+ CombineFieldsLatticeMethods<T,K> for CombineFields<'f>
+{
+ fn var_sub_var(&self,
+ a_id: K,
+ b_id: K)
+ -> ures
+ {
/*!
- *
* Make one variable a subtype of another variable. This is a
* subtle and tricky process, as described in detail at the
- * top of infer.rs*/
+ * top of infer.rs.
+ */
+
+ let tcx = self.infcx.tcx;
+ let table = UnifyKey::unification_table(self.infcx);
// Need to make sub_id a subtype of sup_id.
- let node_a = self.infcx.get(a_id);
- let node_b = self.infcx.get(b_id);
- let a_id = node_a.root.clone();
- let b_id = node_b.root.clone();
- let a_bounds = node_a.possible_types.clone();
- let b_bounds = node_b.possible_types.clone();
+ let node_a = table.borrow_mut().get(tcx, a_id);
+ let node_b = table.borrow_mut().get(tcx, b_id);
+ let a_id = node_a.key.clone();
+ let b_id = node_b.key.clone();
+ let a_bounds = node_a.value.clone();
+ let b_bounds = node_b.value.clone();
debug!("vars({}={} <: {}={})",
- a_id.to_str(), a_bounds.inf_str(self.infcx),
- b_id.to_str(), b_bounds.inf_str(self.infcx));
+ a_id, a_bounds.repr(tcx),
+ b_id, b_bounds.repr(tcx));
- if a_id == b_id { return uok(); }
+ if a_id == b_id { return Ok(()); }
// If both A's UB and B's LB have already been bound to types,
// see if we can make those types subtypes.
// A remains a subtype of B. Actually, there are other options,
// but that's the route we choose to take.
- let (new_root, new_rank) = self.infcx.unify(&node_a, &node_b);
+ let (new_root, new_rank) =
+ table.borrow_mut().unify(tcx, &node_a, &node_b);
self.set_var_to_merged_bounds(new_root,
&a_bounds, &b_bounds,
new_rank)
}
/// make variable a subtype of T
- fn var_sub_t<T:Clone + InferStr + LatticeValue,
- V:Clone + PartialEq + ToStr + Vid + UnifyVid<Bounds<T>>>(
- &self,
- a_id: V,
+ fn var_sub_t(&self,
+ a_id: K,
b: T)
- -> ures {
+ -> ures
+ {
/*!
- *
- * Make a variable (`a_id`) a subtype of the concrete type `b` */
-
- let node_a = self.infcx.get(a_id);
- let a_id = node_a.root.clone();
- let a_bounds = &node_a.possible_types;
+ * Make a variable (`a_id`) a subtype of the concrete type `b`.
+ */
+
+ let tcx = self.infcx.tcx;
+ let table = UnifyKey::unification_table(self.infcx);
+ let node_a = table.borrow_mut().get(tcx, a_id);
+ let a_id = node_a.key.clone();
+ let a_bounds = &node_a.value;
let b_bounds = &Bounds { lb: None, ub: Some(b.clone()) };
debug!("var_sub_t({}={} <: {})",
- a_id.to_str(),
- a_bounds.inf_str(self.infcx),
- b.inf_str(self.infcx));
+ a_id,
+ a_bounds.repr(self.infcx.tcx),
+ b.repr(self.infcx.tcx));
self.set_var_to_merged_bounds(
a_id, a_bounds, b_bounds, node_a.rank)
}
- fn t_sub_var<T:Clone + InferStr + LatticeValue,
- V:Clone + PartialEq + ToStr + Vid + UnifyVid<Bounds<T>>>(
- &self,
+ fn t_sub_var(&self,
a: T,
- b_id: V)
- -> ures {
+ b_id: K)
+ -> ures
+ {
/*!
- *
- * Make a concrete type (`a`) a subtype of the variable `b_id` */
+ * Make a concrete type (`a`) a subtype of the variable `b_id`
+ */
+ let tcx = self.infcx.tcx;
+ let table = UnifyKey::unification_table(self.infcx);
let a_bounds = &Bounds { lb: Some(a.clone()), ub: None };
- let node_b = self.infcx.get(b_id);
- let b_id = node_b.root.clone();
- let b_bounds = &node_b.possible_types;
+ let node_b = table.borrow_mut().get(tcx, b_id);
+ let b_id = node_b.key.clone();
+ let b_bounds = &node_b.value;
debug!("t_sub_var({} <: {}={})",
- a.inf_str(self.infcx),
- b_id.to_str(),
- b_bounds.inf_str(self.infcx));
+ a.repr(self.infcx.tcx),
+ b_id,
+ b_bounds.repr(self.infcx.tcx));
self.set_var_to_merged_bounds(
b_id, a_bounds, b_bounds, node_b.rank)
}
- fn merge_bnd<T:Clone + InferStr + LatticeValue>(
- &self,
- a: &Bound<T>,
- b: &Bound<T>,
- lattice_op: LatticeOp<T>)
- -> cres<Bound<T>> {
- /*!
- *
- * Combines two bounds into a more general bound. */
-
- debug!("merge_bnd({},{})",
- a.inf_str(self.infcx),
- b.inf_str(self.infcx));
- let _r = indenter();
-
- match (a, b) {
- (&None, &None) => Ok(None),
- (&Some(_), &None) => Ok((*a).clone()),
- (&None, &Some(_)) => Ok((*b).clone()),
- (&Some(ref v_a), &Some(ref v_b)) => {
- lattice_op(self.clone(), v_a, v_b).and_then(|v| Ok(Some(v)))
- }
- }
- }
-
- fn set_var_to_merged_bounds<T:Clone + InferStr + LatticeValue,
- V:Clone+PartialEq+ToStr+Vid+UnifyVid<Bounds<T>>>(
- &self,
- v_id: V,
+ fn set_var_to_merged_bounds(&self,
+ v_id: K,
a: &Bounds<T>,
b: &Bounds<T>,
rank: uint)
- -> ures {
+ -> ures
+ {
/*!
- *
* 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 <: b.ub
* a.lb <: c.lb
* b.lb <: c.lb
- * If this cannot be achieved, the result is failure. */
+ * If this cannot be achieved, the result is failure.
+ */
// Think of the two diamonds, we want to find the
// intersection. There are basically four possibilities (you
// A \ / A
// B
+ let tcx = self.infcx.tcx;
+ let table = UnifyKey::unification_table(self.infcx);
+
debug!("merge({},{},{})",
- v_id.to_str(),
- a.inf_str(self.infcx),
- b.inf_str(self.infcx));
- let _indent = indenter();
+ v_id,
+ a.repr(self.infcx.tcx),
+ b.repr(self.infcx.tcx));
// First, relate the lower/upper bounds of A and B.
// Note that these relations *must* hold for us
let lb = if_ok!(self.merge_bnd(&a.lb, &b.lb, LatticeValue::lub));
let bounds = Bounds { lb: lb, ub: ub };
debug!("merge({}): bounds={}",
- v_id.to_str(),
- bounds.inf_str(self.infcx));
+ v_id,
+ bounds.repr(self.infcx.tcx));
// the new bounds must themselves
// be relatable:
let () = if_ok!(self.bnds(&bounds.lb, &bounds.ub));
- self.infcx.set(v_id, Root(bounds, rank));
- uok()
+ table.borrow_mut().set(tcx, v_id, Root(bounds, rank));
+ Ok(())
}
+}
- fn bnds<T:Clone + InferStr + LatticeValue>(&self,
- a: &Bound<T>,
- b: &Bound<T>)
- -> ures {
- debug!("bnds({} <: {})", a.inf_str(self.infcx),
- b.inf_str(self.infcx));
- let _r = indenter();
+impl<'f,T:LatticeValue>
+ CombineFieldsLatticeMethods2<T> for CombineFields<'f>
+{
+ fn merge_bnd(&self,
+ a: &Bound<T>,
+ b: &Bound<T>,
+ lattice_op: LatticeOp<T>)
+ -> cres<Bound<T>>
+ {
+ /*!
+ * Combines two bounds into a more general bound.
+ */
+
+ debug!("merge_bnd({},{})",
+ a.repr(self.infcx.tcx),
+ b.repr(self.infcx.tcx));
+ match (a, b) {
+ (&None, &None) => Ok(None),
+ (&Some(_), &None) => Ok((*a).clone()),
+ (&None, &Some(_)) => Ok((*b).clone()),
+ (&Some(ref v_a), &Some(ref v_b)) => {
+ lattice_op(self.clone(), v_a, v_b).and_then(|v| Ok(Some(v)))
+ }
+ }
+ }
+
+ fn bnds(&self,
+ a: &Bound<T>,
+ b: &Bound<T>)
+ -> ures
+ {
+ debug!("bnds({} <: {})",
+ a.repr(self.infcx.tcx),
+ b.repr(self.infcx.tcx));
match (a, b) {
(&None, &None) |
(&Some(_), &None) |
(&None, &Some(_)) => {
- uok()
+ Ok(())
}
(&Some(ref t_a), &Some(ref t_b)) => {
LatticeValue::sub(self.clone(), t_a, t_b)
a: ty::t,
b: ty::t)
-> cres<ty::t> {
- debug!("{}.lattice_tys({}, {})", this.tag(),
- a.inf_str(this.infcx()),
- b.inf_str(this.infcx()));
+ debug!("{}.lattice_tys({}, {})",
+ this.tag(),
+ a.repr(this.infcx().tcx),
+ b.repr(this.infcx().tcx));
if a == b {
return Ok(a);
pub type LatticeDirOp<'a, T> = |a: &T, b: &T|: 'a -> cres<T>;
#[deriving(Clone)]
-pub enum LatticeVarResult<V,T> {
- VarResult(V),
+pub enum LatticeVarResult<K,T> {
+ VarResult(K),
ValueResult(T)
}
* - If the variables do not both have an upper bound, we will unify
* the variables and return the unified variable, in which case the
* result is a variable. This is indicated with a `VarResult`
- * return. */
-pub fn lattice_vars<L:LatticeDir + Combine,
- T:Clone + InferStr + LatticeValue,
- V:Clone + PartialEq + ToStr + Vid + UnifyVid<Bounds<T>>>(
+ * return.
+ */
+pub fn lattice_vars<L:LatticeDir+Combine,
+ T:LatticeValue,
+ K:UnifyKey<Bounds<T>>>(
this: &L, // defines whether we want LUB or GLB
- a_vid: V, // first variable
- b_vid: V, // second variable
+ a_vid: K, // first variable
+ b_vid: K, // second variable
lattice_dir_op: LatticeDirOp<T>) // LUB or GLB operation on types
- -> cres<LatticeVarResult<V,T>> {
- let nde_a = this.infcx().get(a_vid);
- let nde_b = this.infcx().get(b_vid);
- let a_vid = nde_a.root.clone();
- let b_vid = nde_b.root.clone();
- let a_bounds = &nde_a.possible_types;
- let b_bounds = &nde_b.possible_types;
+ -> cres<LatticeVarResult<K,T>>
+{
+ let tcx = this.infcx().tcx;
+ let table = UnifyKey::unification_table(this.infcx());
+
+ let node_a = table.borrow_mut().get(tcx, a_vid);
+ let node_b = table.borrow_mut().get(tcx, b_vid);
+ let a_vid = node_a.key.clone();
+ let b_vid = node_b.key.clone();
+ let a_bounds = &node_a.value;
+ let b_bounds = &node_b.value;
debug!("{}.lattice_vars({}={} <: {}={})",
this.tag(),
- a_vid.to_str(), a_bounds.inf_str(this.infcx()),
- b_vid.to_str(), b_bounds.inf_str(this.infcx()));
+ a_vid, a_bounds.repr(tcx),
+ b_vid, b_bounds.repr(tcx));
// Same variable: the easy case.
if a_vid == b_vid {
// Otherwise, we need to merge A and B into one variable. We can
// then use either variable as an upper bound:
let cf = this.combine_fields();
- cf.var_sub_var(a_vid.clone(), b_vid.clone()).then(|| {
- Ok(VarResult(a_vid.clone()))
- })
+ let () = try!(cf.var_sub_var(a_vid.clone(), b_vid.clone()));
+ Ok(VarResult(a_vid.clone()))
}
-pub fn lattice_var_and_t<L:LatticeDir + Combine,
- T:Clone + InferStr + LatticeValue,
- V:Clone + PartialEq + ToStr + Vid + UnifyVid<Bounds<T>>>(
+pub fn lattice_var_and_t<L:LatticeDir+Combine,
+ T:LatticeValue,
+ K:UnifyKey<Bounds<T>>>(
this: &L,
- a_id: V,
+ a_id: K,
b: &T,
lattice_dir_op: LatticeDirOp<T>)
- -> cres<T> {
- let nde_a = this.infcx().get(a_id);
- let a_id = nde_a.root.clone();
- let a_bounds = &nde_a.possible_types;
+ -> cres<T>
+{
+ let tcx = this.infcx().tcx;
+ let table = UnifyKey::unification_table(this.infcx());
+
+ let node_a = table.borrow_mut().get(tcx, a_id);
+ let a_id = node_a.key.clone();
+ let a_bounds = &node_a.value;
// 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!("{}.lattice_var_and_t({}={} <: {})",
this.tag(),
- a_id.to_str(),
- a_bounds.inf_str(this.infcx()),
- b.inf_str(this.infcx()));
+ a_id,
+ a_bounds.repr(this.infcx().tcx),
+ b.repr(this.infcx().tcx));
match this.bnd(a_bounds) {
Some(ref a_bnd) => {
// If a has an upper bound, return the LUB(a.ub, b)
- debug!("bnd=Some({})", a_bnd.inf_str(this.infcx()));
+ debug!("bnd=Some({})", a_bnd.repr(this.infcx().tcx));
lattice_dir_op(a_bnd, b)
}
None => {
// and then return b.
debug!("bnd=None");
let a_bounds = this.with_bnd(a_bounds, (*b).clone());
- this.combine_fields().bnds(&a_bounds.lb, &a_bounds.ub).then(|| {
- this.infcx().set(a_id.clone(),
- Root(a_bounds.clone(), nde_a.rank));
- Ok((*b).clone())
- })
+ let () = try!(this.combine_fields().bnds(&a_bounds.lb,
+ &a_bounds.ub));
+ table.borrow_mut().set(tcx,
+ a_id.clone(),
+ Root(a_bounds.clone(), node_a.rank));
+ Ok((*b).clone())
}
}
}
use middle::typeck::infer::glb::Glb;
use middle::typeck::infer::lattice::*;
use middle::typeck::infer::sub::Sub;
-use middle::typeck::infer::to_str::InferStr;
use middle::typeck::infer::{cres, InferCtxt};
use middle::typeck::infer::fold_regions_in_sig;
use middle::typeck::infer::{TypeTrace, Subtype};
+use middle::typeck::infer::region_inference::RegionMark;
use std::collections::HashMap;
use syntax::ast::{Many, Once, NodeId};
use syntax::ast::{NormalFn, UnsafeFn};
use syntax::ast::{Onceness, FnStyle};
use syntax::ast::{MutMutable, MutImmutable};
use util::ppaux::mt_to_str;
+use util::ppaux::Repr;
pub struct Lub<'f>(pub CombineFields<'f>); // least-upper-bound: common supertype
}
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
- debug!("{}.regions({:?}, {:?})",
+ debug!("{}.regions({}, {})",
self.tag(),
- a.inf_str(self.get_ref().infcx),
- b.inf_str(self.get_ref().infcx));
+ a.repr(self.get_ref().infcx.tcx),
+ b.repr(self.get_ref().infcx.tcx));
Ok(self.get_ref().infcx.region_vars.lub_regions(Subtype(self.trace()), a, b))
}
// Note: this is a subtle algorithm. For a full explanation,
// please see the large comment in `region_inference.rs`.
- // Take a snapshot. We'll never roll this back, but in later
- // phases we do want to be able to examine "all bindings that
- // were created as part of this type comparison", and making a
- // snapshot is a convenient way to do that.
- let snapshot = self.get_ref().infcx.region_vars.start_snapshot();
+ // Make a mark so we can examine "all bindings that were
+ // created as part of this type comparison".
+ let mark = self.get_ref().infcx.region_vars.mark();
// Instantiate each bound region with a fresh region variable.
let (a_with_fresh, a_map) =
// Collect constraints.
let sig0 = if_ok!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh));
- debug!("sig0 = {}", sig0.inf_str(self.get_ref().infcx));
+ debug!("sig0 = {}", sig0.repr(self.get_ref().infcx.tcx));
// Generalize the regions appearing in sig0 if possible
let new_vars =
- self.get_ref().infcx.region_vars.vars_created_since_snapshot(snapshot);
+ self.get_ref().infcx.region_vars.vars_created_since_mark(mark);
let sig1 =
fold_regions_in_sig(
self.get_ref().infcx.tcx,
&sig0,
- |r| generalize_region(self, snapshot, new_vars.as_slice(),
+ |r| generalize_region(self, mark, new_vars.as_slice(),
sig0.binder_id, &a_map, r));
return Ok(sig1);
fn generalize_region(this: &Lub,
- snapshot: uint,
+ mark: RegionMark,
new_vars: &[RegionVid],
new_scope: NodeId,
a_map: &HashMap<ty::BoundRegion, ty::Region>,
return r0;
}
- let tainted = this.get_ref().infcx.region_vars.tainted(snapshot, r0);
+ let tainted = this.get_ref().infcx.region_vars.tainted(mark, r0);
// Variables created during LUB computation which are
// *related* to regions that pre-date the LUB computation
use middle::subst;
use middle::subst::Substs;
-use middle::ty::{TyVid, IntVid, FloatVid, RegionVid, Vid};
+use middle::ty::{TyVid, IntVid, FloatVid, RegionVid};
use middle::ty;
use middle::ty_fold;
use middle::ty_fold::TypeFolder;
use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig;
use middle::typeck::infer::coercion::Coerce;
use middle::typeck::infer::combine::{Combine, CombineFields, eq_tys};
-use middle::typeck::infer::region_inference::{RegionVarBindings};
+use middle::typeck::infer::region_inference::{RegionVarBindings,
+ RegionSnapshot};
use middle::typeck::infer::resolve::{resolver};
use middle::typeck::infer::sub::Sub;
use middle::typeck::infer::lub::Lub;
-use middle::typeck::infer::to_str::InferStr;
-use middle::typeck::infer::unify::{ValsAndBindings, Root};
+use middle::typeck::infer::unify::{UnificationTable, Snapshot};
use middle::typeck::infer::error_reporting::ErrorReporting;
-use std::cell::{Cell, RefCell};
+use std::cell::{RefCell};
use std::collections::HashMap;
use std::rc::Rc;
use syntax::ast;
pub mod region_inference;
pub mod resolve;
pub mod sub;
-pub mod to_str;
pub mod unify;
pub mod coercion;
pub mod error_reporting;
+pub mod test;
pub type Bound<T> = Option<T>;
-#[deriving(Clone)]
+#[deriving(PartialEq,Clone)]
pub struct Bounds<T> {
- lb: Bound<T>,
- ub: Bound<T>
+ pub lb: Bound<T>,
+ pub ub: Bound<T>
}
pub type cres<T> = Result<T,ty::type_err>; // "combine result"
pub struct InferCtxt<'a> {
pub tcx: &'a ty::ctxt,
- // We instantiate ValsAndBindings with bounds<ty::t> because the
+ // We instantiate UnificationTable with bounds<ty::t> because the
// types that might instantiate a general type variable have an
// order, represented by its upper and lower bounds.
- pub ty_var_bindings: RefCell<ValsAndBindings<ty::TyVid, Bounds<ty::t>>>,
- pub ty_var_counter: Cell<uint>,
+ type_unification_table:
+ RefCell<UnificationTable<ty::TyVid, Bounds<ty::t>>>,
// Map from integral variable to the kind of integer it represents
- pub int_var_bindings: RefCell<ValsAndBindings<ty::IntVid,
- Option<IntVarValue>>>,
- pub int_var_counter: Cell<uint>,
+ int_unification_table:
+ RefCell<UnificationTable<ty::IntVid, Option<IntVarValue>>>,
// Map from floating variable to the kind of float it represents
- pub float_var_bindings: RefCell<ValsAndBindings<ty::FloatVid,
- Option<ast::FloatTy>>>,
- pub float_var_counter: Cell<uint>,
+ float_unification_table:
+ RefCell<UnificationTable<ty::FloatVid, Option<ast::FloatTy>>>,
// For region variables.
- pub region_vars: RegionVarBindings<'a>,
+ region_vars:
+ RegionVarBindings<'a>,
}
/// Why did we require that the two types be related?
pub fn new_infer_ctxt<'a>(tcx: &'a ty::ctxt) -> InferCtxt<'a> {
InferCtxt {
tcx: tcx,
-
- ty_var_bindings: RefCell::new(ValsAndBindings::new()),
- ty_var_counter: Cell::new(0),
-
- int_var_bindings: RefCell::new(ValsAndBindings::new()),
- int_var_counter: Cell::new(0),
-
- float_var_bindings: RefCell::new(ValsAndBindings::new()),
- float_var_counter: Cell::new(0),
-
+ type_unification_table: RefCell::new(UnificationTable::new()),
+ int_unification_table: RefCell::new(UnificationTable::new()),
+ float_unification_table: RefCell::new(UnificationTable::new()),
region_vars: RegionVarBindings::new(tcx),
}
}
a_is_expected: bool,
a: ty::t,
b: ty::t)
- -> ty::t {
+ -> ty::t
+{
/*!
* Computes the least upper-bound of `a` and `b`. If this is
* not possible, reports an error and returns ty::err.
*/
- debug!("common_supertype({}, {})", a.inf_str(cx), b.inf_str(cx));
+ debug!("common_supertype({}, {})",
+ a.repr(cx.tcx), b.repr(cx.tcx));
let trace = TypeTrace {
origin: origin,
values: Types(expected_found(a_is_expected, a, b))
};
- let result = cx.commit(|| cx.lub(a_is_expected, trace.clone()).tys(a, b));
+ let result =
+ cx.commit_if_ok(|| cx.lub(a_is_expected, trace.clone()).tys(a, b));
match result {
Ok(t) => t,
Err(ref err) => {
a: ty::t,
b: ty::t)
-> ures {
- debug!("mk_subty({} <: {})", a.inf_str(cx), b.inf_str(cx));
+ debug!("mk_subty({} <: {})", a.repr(cx.tcx), b.repr(cx.tcx));
indent(|| {
- cx.commit(|| {
+ cx.commit_if_ok(|| {
let trace = TypeTrace {
origin: origin,
values: Types(expected_found(a_is_expected, a, b))
}
pub fn can_mk_subty(cx: &InferCtxt, a: ty::t, b: ty::t) -> ures {
- debug!("can_mk_subty({} <: {})", a.inf_str(cx), b.inf_str(cx));
- indent(|| {
- cx.probe(|| {
- let trace = TypeTrace {
- origin: Misc(codemap::DUMMY_SP),
- values: Types(expected_found(true, a, b))
- };
- cx.sub(true, trace).tys(a, b)
- })
+ debug!("can_mk_subty({} <: {})", a.repr(cx.tcx), b.repr(cx.tcx));
+ cx.probe(|| {
+ let trace = TypeTrace {
+ origin: Misc(codemap::DUMMY_SP),
+ values: Types(expected_found(true, a, b))
+ };
+ cx.sub(true, trace).tys(a, b)
}).to_ures()
}
origin: SubregionOrigin,
a: ty::Region,
b: ty::Region) {
- debug!("mk_subr({} <: {})", a.inf_str(cx), b.inf_str(cx));
- cx.region_vars.start_snapshot();
+ debug!("mk_subr({} <: {})", a.repr(cx.tcx), b.repr(cx.tcx));
+ let snapshot = cx.region_vars.start_snapshot();
cx.region_vars.make_subregion(origin, a, b);
- cx.region_vars.commit();
+ cx.region_vars.commit(snapshot);
}
pub fn mk_eqty(cx: &InferCtxt,
origin: TypeOrigin,
a: ty::t,
b: ty::t)
- -> ures {
- debug!("mk_eqty({} <: {})", a.inf_str(cx), b.inf_str(cx));
- indent(|| {
- cx.commit(|| {
- let trace = TypeTrace {
- origin: origin,
- values: Types(expected_found(a_is_expected, a, b))
- };
- let suber = cx.sub(a_is_expected, trace);
- eq_tys(&suber, a, b)
- })
- }).to_ures()
+ -> ures
+{
+ debug!("mk_eqty({} <: {})", a.repr(cx.tcx), b.repr(cx.tcx));
+ cx.commit_if_ok(|| {
+ let trace = TypeTrace {
+ origin: origin,
+ values: Types(expected_found(a_is_expected, a, b))
+ };
+ let suber = cx.sub(a_is_expected, trace);
+ eq_tys(&suber, a, b)
+ })
}
pub fn mk_sub_trait_refs(cx: &InferCtxt,
-> ures
{
debug!("mk_sub_trait_refs({} <: {})",
- a.inf_str(cx), b.inf_str(cx));
+ a.repr(cx.tcx), b.repr(cx.tcx));
indent(|| {
- cx.commit(|| {
+ cx.commit_if_ok(|| {
let trace = TypeTrace {
origin: origin,
values: TraitRefs(expected_found(a_is_expected, a.clone(), b.clone()))
a: ty::t,
b: ty::t)
-> CoerceResult {
- debug!("mk_coercety({} -> {})", a.inf_str(cx), b.inf_str(cx));
+ debug!("mk_coercety({} -> {})", a.repr(cx.tcx), b.repr(cx.tcx));
indent(|| {
- cx.commit(|| {
+ cx.commit_if_ok(|| {
let trace = TypeTrace {
origin: origin,
values: Types(expected_found(a_is_expected, a, b))
pub fn resolve_type(cx: &InferCtxt,
a: ty::t,
modes: uint)
- -> fres<ty::t> {
+ -> fres<ty::t>
+{
let mut resolver = resolver(cx, modes);
- resolver.resolve_type_chk(a)
+ cx.commit_unconditionally(|| resolver.resolve_type_chk(a))
}
pub fn resolve_region(cx: &InferCtxt, r: ty::Region, modes: uint)
- -> fres<ty::Region> {
+ -> fres<ty::Region>
+{
let mut resolver = resolver(cx, modes);
resolver.resolve_region_chk(r)
}
Ok(())
}
-fn rollback_to<V:Clone + Vid,T:Clone>(vb: &mut ValsAndBindings<V, T>,
- len: uint) {
- while vb.bindings.len() != len {
- let (vid, old_v) = vb.bindings.pop().unwrap();
- vb.vals.insert(vid.to_uint(), old_v);
- }
-}
-
-pub struct Snapshot {
- ty_var_bindings_len: uint,
- int_var_bindings_len: uint,
- float_var_bindings_len: uint,
- region_vars_snapshot: uint,
+pub struct CombinedSnapshot {
+ type_snapshot: Snapshot<ty::TyVid>,
+ int_snapshot: Snapshot<ty::IntVid>,
+ float_snapshot: Snapshot<ty::FloatVid>,
+ region_vars_snapshot: RegionSnapshot,
}
impl<'a> InferCtxt<'a> {
self.region_vars.in_snapshot()
}
- pub fn start_snapshot(&self) -> Snapshot {
- Snapshot {
- ty_var_bindings_len: self.ty_var_bindings.borrow().bindings.len(),
- int_var_bindings_len: self.int_var_bindings.borrow().bindings.len(),
- float_var_bindings_len: self.float_var_bindings.borrow().bindings.len(),
+ fn start_snapshot(&self) -> CombinedSnapshot {
+ CombinedSnapshot {
+ type_snapshot: self.type_unification_table.borrow_mut().snapshot(),
+ int_snapshot: self.int_unification_table.borrow_mut().snapshot(),
+ float_snapshot: self.float_unification_table.borrow_mut().snapshot(),
region_vars_snapshot: self.region_vars.start_snapshot(),
}
}
- pub fn rollback_to(&self, snapshot: &Snapshot) {
+ fn rollback_to(&self, snapshot: CombinedSnapshot) {
debug!("rollback!");
- rollback_to(&mut *self.ty_var_bindings.borrow_mut(),
- snapshot.ty_var_bindings_len);
- rollback_to(&mut *self.int_var_bindings.borrow_mut(),
- snapshot.int_var_bindings_len);
- rollback_to(&mut *self.float_var_bindings.borrow_mut(),
- snapshot.float_var_bindings_len);
-
- self.region_vars.rollback_to(snapshot.region_vars_snapshot);
+ let CombinedSnapshot { type_snapshot,
+ int_snapshot,
+ float_snapshot,
+ region_vars_snapshot } = snapshot;
+
+ self.type_unification_table
+ .borrow_mut()
+ .rollback_to(self.tcx, type_snapshot);
+ self.int_unification_table
+ .borrow_mut()
+ .rollback_to(self.tcx, int_snapshot);
+ self.float_unification_table
+ .borrow_mut()
+ .rollback_to(self.tcx, float_snapshot);
+ self.region_vars
+ .rollback_to(region_vars_snapshot);
+ }
+
+ fn commit_from(&self, snapshot: CombinedSnapshot) {
+ debug!("commit_from!");
+ let CombinedSnapshot { type_snapshot,
+ int_snapshot,
+ float_snapshot,
+ region_vars_snapshot } = snapshot;
+
+ self.type_unification_table
+ .borrow_mut()
+ .commit(type_snapshot);
+ self.int_unification_table
+ .borrow_mut()
+ .commit(int_snapshot);
+ self.float_unification_table
+ .borrow_mut()
+ .commit(float_snapshot);
+ self.region_vars
+ .commit(region_vars_snapshot);
+ }
+
+ /// Execute `f` and commit the bindings
+ pub fn commit_unconditionally<R>(&self, f: || -> R) -> R {
+ debug!("commit()");
+ let snapshot = self.start_snapshot();
+ let r = f();
+ self.commit_from(snapshot);
+ r
}
/// Execute `f` and commit the bindings if successful
- pub fn commit<T,E>(&self, f: || -> Result<T,E>) -> Result<T,E> {
- assert!(!self.in_snapshot());
-
- debug!("commit()");
- indent(|| {
- let r = self.try(|| f());
-
- self.ty_var_bindings.borrow_mut().bindings.truncate(0);
- self.int_var_bindings.borrow_mut().bindings.truncate(0);
- self.region_vars.commit();
- r
- })
+ pub fn commit_if_ok<T,E>(&self, f: || -> Result<T,E>) -> Result<T,E> {
+ self.commit_unconditionally(|| self.try(|| f()))
}
/// Execute `f`, unroll bindings on failure
debug!("try()");
let snapshot = self.start_snapshot();
let r = f();
+ debug!("try() -- r.is_ok() = {}", r.is_ok());
match r {
- Ok(_) => { debug!("success"); }
- Err(ref e) => {
- debug!("error: {:?}", *e);
- self.rollback_to(&snapshot)
+ Ok(_) => {
+ self.commit_from(snapshot);
+ }
+ Err(_) => {
+ self.rollback_to(snapshot);
}
}
r
/// Execute `f` then unroll any bindings it creates
pub fn probe<T,E>(&self, f: || -> Result<T,E>) -> Result<T,E> {
debug!("probe()");
- indent(|| {
- let snapshot = self.start_snapshot();
- let r = f();
- self.rollback_to(&snapshot);
- r
- })
+ let snapshot = self.start_snapshot();
+ let r = f();
+ self.rollback_to(snapshot);
+ r
}
}
-fn next_simple_var<V:Clone,T:Clone>(counter: &mut uint,
- bindings: &mut ValsAndBindings<V,
- Option<T>>)
- -> uint {
- let id = *counter;
- *counter += 1;
- bindings.vals.insert(id, Root(None, 0));
- return id;
-}
-
impl<'a> InferCtxt<'a> {
pub fn next_ty_var_id(&self) -> TyVid {
- let id = self.ty_var_counter.get();
- self.ty_var_counter.set(id + 1);
- {
- let mut ty_var_bindings = self.ty_var_bindings.borrow_mut();
- let vals = &mut ty_var_bindings.vals;
- vals.insert(id, Root(Bounds { lb: None, ub: None }, 0u));
- }
- debug!("created type variable {}", TyVid(id));
- return TyVid(id);
+ self.type_unification_table
+ .borrow_mut()
+ .new_key(Bounds { lb: None, ub: None })
}
pub fn next_ty_var(&self) -> ty::t {
}
pub fn next_int_var_id(&self) -> IntVid {
- let mut int_var_counter = self.int_var_counter.get();
- let mut int_var_bindings = self.int_var_bindings.borrow_mut();
- let result = IntVid(next_simple_var(&mut int_var_counter,
- &mut *int_var_bindings));
- self.int_var_counter.set(int_var_counter);
- result
+ self.int_unification_table
+ .borrow_mut()
+ .new_key(None)
}
pub fn next_float_var_id(&self) -> FloatVid {
- let mut float_var_counter = self.float_var_counter.get();
- let mut float_var_bindings = self.float_var_bindings.borrow_mut();
- let result = FloatVid(next_simple_var(&mut float_var_counter,
- &mut *float_var_bindings));
- self.float_var_counter.set(float_var_counter);
- result
+ self.float_unification_table
+ .borrow_mut()
+ .new_key(None)
}
pub fn next_region_var(&self, origin: RegionVariableOrigin) -> ty::Region {
use middle::ty;
-use middle::ty::{BoundRegion, FreeRegion, Region, RegionVid, Vid};
+use middle::ty::{BoundRegion, FreeRegion, Region, RegionVid};
use middle::ty::{ReEmpty, ReStatic, ReInfer, ReFree, ReEarlyBound,
ReLateBound};
use middle::ty::{ReScope, ReVar, ReSkolemized, BrFresh};
b: Region,
}
+#[deriving(PartialEq)]
pub enum UndoLogEntry {
- Snapshot,
+ OpenSnapshot,
+ CommitedSnapshot,
+ Mark,
AddVar(RegionVid),
AddConstraint(Constraint),
AddCombination(CombineMapType, TwoRegions)
}
+#[deriving(PartialEq)]
pub enum CombineMapType {
Lub, Glb
}
// The undo log records actions that might later be undone.
//
// Note: when the undo_log is empty, we are not actively
- // snapshotting. When the `start_snapshot()` method is called, we
- // push a Snapshot entry onto the list to indicate that we are now
- // actively snapshotting. The reason for this is that otherwise
- // we end up adding entries for things like the lower bound on
- // a variable and so forth, which can never be rolled back.
- undo_log: RefCell<Vec<UndoLogEntry> >,
+ // snapshotting. When the `start_snapshot()` method is called, we
+ // push an OpenSnapshot entry onto the list to indicate that we
+ // are now actively snapshotting. The reason for this is that
+ // otherwise we end up adding entries for things like the lower
+ // bound on a variable and so forth, which can never be rolled
+ // back.
+ undo_log: RefCell<Vec<UndoLogEntry>>,
// This contains the results of inference. It begins as an empty
// option and only acquires a value after inference is complete.
- values: RefCell<Option<Vec<VarValue> >>,
+ values: RefCell<Option<Vec<VarValue>>>,
+}
+
+#[deriving(Show)]
+pub struct RegionSnapshot {
+ length: uint
+}
+
+#[deriving(Show)]
+pub struct RegionMark {
+ length: uint
}
impl<'a> RegionVarBindings<'a> {
self.undo_log.borrow().len() > 0
}
- pub fn start_snapshot(&self) -> uint {
- debug!("RegionVarBindings: start_snapshot()");
- if self.in_snapshot() {
- self.undo_log.borrow().len()
- } else {
- self.undo_log.borrow_mut().push(Snapshot);
- 0
- }
+ pub fn start_snapshot(&self) -> RegionSnapshot {
+ let length = self.undo_log.borrow().len();
+ debug!("RegionVarBindings: start_snapshot({})", length);
+ self.undo_log.borrow_mut().push(OpenSnapshot);
+ RegionSnapshot { length: length }
+ }
+
+ pub fn mark(&self) -> RegionMark {
+ let length = self.undo_log.borrow().len();
+ debug!("RegionVarBindings: mark({})", length);
+ self.undo_log.borrow_mut().push(Mark);
+ RegionMark { length: length }
}
- pub fn commit(&self) {
+ pub fn commit(&self, snapshot: RegionSnapshot) {
debug!("RegionVarBindings: commit()");
+ assert!(self.undo_log.borrow().len() > snapshot.length);
+ assert!(*self.undo_log.borrow().get(snapshot.length) == OpenSnapshot);
+
let mut undo_log = self.undo_log.borrow_mut();
- while undo_log.len() > 0 {
- undo_log.pop().unwrap();
+ if snapshot.length == 0 {
+ undo_log.truncate(0);
+ } else {
+ *undo_log.get_mut(snapshot.length) = CommitedSnapshot;
}
}
- pub fn rollback_to(&self, snapshot: uint) {
+ pub fn rollback_to(&self, snapshot: RegionSnapshot) {
debug!("RegionVarBindings: rollback_to({})", snapshot);
let mut undo_log = self.undo_log.borrow_mut();
- while undo_log.len() > snapshot {
- let undo_item = undo_log.pop().unwrap();
- debug!("undo_item={:?}", undo_item);
- match undo_item {
- Snapshot => {}
- AddVar(vid) => {
- let mut var_origins = self.var_origins.borrow_mut();
- assert_eq!(var_origins.len(), vid.to_uint() + 1);
- var_origins.pop().unwrap();
- }
- AddConstraint(ref constraint) => {
- self.constraints.borrow_mut().remove(constraint);
- }
- AddCombination(Glb, ref regions) => {
- self.glbs.borrow_mut().remove(regions);
- }
- AddCombination(Lub, ref regions) => {
- self.lubs.borrow_mut().remove(regions);
- }
+ assert!(undo_log.len() > snapshot.length);
+ assert!(*undo_log.get(snapshot.length) == OpenSnapshot);
+ while undo_log.len() > snapshot.length + 1 {
+ match undo_log.pop().unwrap() {
+ OpenSnapshot => {
+ fail!("Failure to observe stack discipline");
+ }
+ Mark | CommitedSnapshot => { }
+ AddVar(vid) => {
+ let mut var_origins = self.var_origins.borrow_mut();
+ assert_eq!(var_origins.len(), vid.index + 1);
+ var_origins.pop().unwrap();
+ }
+ AddConstraint(ref constraint) => {
+ self.constraints.borrow_mut().remove(constraint);
+ }
+ AddCombination(Glb, ref regions) => {
+ self.glbs.borrow_mut().remove(regions);
+ }
+ AddCombination(Lub, ref regions) => {
+ self.lubs.borrow_mut().remove(regions);
+ }
}
}
+ let c = undo_log.pop().unwrap();
+ assert!(c == OpenSnapshot);
}
pub fn num_vars(&self) -> uint {
pub fn new_region_var(&self, origin: RegionVariableOrigin) -> RegionVid {
let id = self.num_vars();
self.var_origins.borrow_mut().push(origin.clone());
- let vid = RegionVid { id: id };
+ let vid = RegionVid { index: id };
if self.in_snapshot() {
self.undo_log.borrow_mut().push(AddVar(vid));
}
let v = match *self.values.borrow() {
None => {
self.tcx.sess.span_bug(
- self.var_origins.borrow().get(rid.to_uint()).span(),
+ self.var_origins.borrow().get(rid.index).span(),
"attempt to resolve region variable before values have \
been computed!")
}
- Some(ref values) => *values.get(rid.to_uint())
+ Some(ref values) => *values.get(rid.index)
};
debug!("RegionVarBindings: resolve_var({:?}={})={:?}",
- rid, rid.to_uint(), v);
+ rid, rid.index, v);
match v {
Value(r) => r,
ReInfer(ReVar(c))
}
- pub fn vars_created_since_snapshot(&self, snapshot: uint)
- -> Vec<RegionVid> {
- self.undo_log.borrow().slice_from(snapshot).iter()
+ pub fn vars_created_since_mark(&self, mark: RegionMark)
+ -> Vec<RegionVid>
+ {
+ self.undo_log.borrow()
+ .slice_from(mark.length)
+ .iter()
.filter_map(|&elt| match elt {
AddVar(vid) => Some(vid),
_ => None
.collect()
}
- pub fn tainted(&self, snapshot: uint, r0: Region) -> Vec<Region> {
+ pub fn tainted(&self, mark: RegionMark, r0: Region) -> Vec<Region> {
/*!
* Computes all regions that have been related to `r0` in any
- * way since the snapshot `snapshot` was taken---`r0` itself
- * will be the first entry. This is used when checking whether
+ * way since the mark `mark` was made---`r0` itself will be
+ * the first entry. This is used when checking whether
* skolemized regions are being improperly related to other
* regions.
*/
- debug!("tainted(snapshot={}, r0={:?})", snapshot, r0);
+ debug!("tainted(mark={}, r0={})", mark, r0.repr(self.tcx));
let _indenter = indenter();
- let undo_len = self.undo_log.borrow().len();
-
// `result_set` acts as a worklist: we explore all outgoing
// edges and add any new regions we find to result_set. This
// is not a terribly efficient implementation.
while result_index < result_set.len() {
// nb: can't use uint::range() here because result_set grows
let r = *result_set.get(result_index);
-
debug!("result_index={}, r={:?}", result_index, r);
- let mut undo_index = snapshot;
- while undo_index < undo_len {
- // nb: can't use uint::range() here as we move result_set
- let regs = match self.undo_log.borrow().get(undo_index) {
+ for undo_entry in
+ self.undo_log.borrow().slice_from(mark.length).iter()
+ {
+ let regs = match undo_entry {
&AddConstraint(ConstrainVarSubVar(ref a, ref b)) => {
- Some((ReInfer(ReVar(*a)),
- ReInfer(ReVar(*b))))
+ Some((ReInfer(ReVar(*a)), ReInfer(ReVar(*b))))
}
&AddConstraint(ConstrainRegSubVar(ref a, ref b)) => {
Some((*a, ReInfer(ReVar(*b))))
&AddConstraint(ConstrainRegSubReg(a, b)) => {
Some((a, b))
}
- _ => {
+ &AddCombination(..) |
+ &Mark |
+ &AddVar(..) |
+ &OpenSnapshot |
+ &CommitedSnapshot => {
None
}
};
consider_adding_edge(result_set, r, r2, r1);
}
}
-
- undo_index += 1;
}
result_index += 1;
(ReInfer(ReVar(v_id)), _) | (_, ReInfer(ReVar(v_id))) => {
self.tcx.sess.span_bug(
- self.var_origins.borrow().get(v_id.to_uint()).span(),
+ self.var_origins.borrow().get(v_id.index).span(),
format!("lub_concrete_regions invoked with \
non-concrete regions: {:?}, {:?}",
a,
(ReInfer(ReVar(v_id)), _) |
(_, ReInfer(ReVar(v_id))) => {
self.tcx.sess.span_bug(
- self.var_origins.borrow().get(v_id.to_uint()).span(),
+ self.var_origins.borrow().get(v_id.index).span(),
format!("glb_concrete_regions invoked with \
non-concrete regions: {:?}, {:?}",
a,
self.iterate_until_fixed_point("Expansion", |constraint| {
match *constraint {
ConstrainRegSubVar(a_region, b_vid) => {
- let b_data = &mut var_data[b_vid.to_uint()];
+ let b_data = &mut var_data[b_vid.index];
self.expand_node(a_region, b_vid, b_data)
}
ConstrainVarSubVar(a_vid, b_vid) => {
- match var_data[a_vid.to_uint()].value {
+ match var_data[a_vid.index].value {
NoValue | ErrorValue => false,
Value(a_region) => {
- let b_node = &mut var_data[b_vid.to_uint()];
+ let b_node = &mut var_data[b_vid.index];
self.expand_node(a_region, b_vid, b_node)
}
}
false
}
ConstrainVarSubVar(a_vid, b_vid) => {
- match var_data[b_vid.to_uint()].value {
+ match var_data[b_vid.index].value {
NoValue | ErrorValue => false,
Value(b_region) => {
- let a_data = &mut var_data[a_vid.to_uint()];
+ let a_data = &mut var_data[a_vid.index];
self.contract_node(a_vid, a_data, b_region)
}
}
}
ConstrainVarSubReg(a_vid, b_region) => {
- let a_data = &mut var_data[a_vid.to_uint()];
+ let a_data = &mut var_data[a_vid.index];
self.contract_node(a_vid, a_data, b_region)
}
ConstrainRegSubReg(..) => {
}
let graph = opt_graph.get_ref();
- let node_vid = RegionVid { id: idx };
+ let node_vid = RegionVid { index: idx };
match var_data[idx].classification {
Expanding => {
self.collect_error_for_expanding_node(
for (constraint, _) in constraints.iter() {
match *constraint {
ConstrainVarSubVar(a_id, b_id) => {
- graph.add_edge(NodeIndex(a_id.to_uint()),
- NodeIndex(b_id.to_uint()),
+ graph.add_edge(NodeIndex(a_id.index),
+ NodeIndex(b_id.index),
*constraint);
}
ConstrainRegSubVar(_, b_id) => {
graph.add_edge(dummy_idx,
- NodeIndex(b_id.to_uint()),
+ NodeIndex(b_id.index),
*constraint);
}
ConstrainVarSubReg(a_id, _) => {
- graph.add_edge(NodeIndex(a_id.to_uint()),
+ graph.add_edge(NodeIndex(a_id.index),
dummy_idx,
*constraint);
}
if !self.is_subregion_of(lower_bound.region,
upper_bound.region) {
errors.push(SubSupConflict(
- self.var_origins.borrow().get(node_idx.to_uint()).clone(),
+ self.var_origins.borrow().get(node_idx.index).clone(),
lower_bound.origin.clone(),
lower_bound.region,
upper_bound.origin.clone(),
}
self.tcx.sess.span_bug(
- self.var_origins.borrow().get(node_idx.to_uint()).span(),
+ self.var_origins.borrow().get(node_idx.index).span(),
format!("collect_error_for_expanding_node() could not find error \
for var {:?}, lower_bounds={}, upper_bounds={}",
node_idx,
Ok(_) => {}
Err(_) => {
errors.push(SupSupConflict(
- self.var_origins.borrow().get(node_idx.to_uint()).clone(),
+ self.var_origins.borrow().get(node_idx.index).clone(),
upper_bound_1.origin.clone(),
upper_bound_1.region,
upper_bound_2.origin.clone(),
}
self.tcx.sess.span_bug(
- self.var_origins.borrow().get(node_idx.to_uint()).span(),
+ self.var_origins.borrow().get(node_idx.index).span(),
format!("collect_error_for_contracting_node() could not find error \
for var {:?}, upper_bounds={}",
node_idx,
while !state.stack.is_empty() {
let node_idx = state.stack.pop().unwrap();
- let classification = var_data[node_idx.to_uint()].classification;
+ let classification = var_data[node_idx.index].classification;
// check whether we've visited this node on some previous walk
- if dup_vec[node_idx.to_uint()] == uint::MAX {
- dup_vec[node_idx.to_uint()] = orig_node_idx.to_uint();
- } else if dup_vec[node_idx.to_uint()] != orig_node_idx.to_uint() {
+ if dup_vec[node_idx.index] == uint::MAX {
+ dup_vec[node_idx.index] = orig_node_idx.index;
+ } else if dup_vec[node_idx.index] != orig_node_idx.index {
state.dup_found = true;
}
dir: Direction) {
debug!("process_edges(source_vid={:?}, dir={:?})", source_vid, dir);
- let source_node_index = NodeIndex(source_vid.to_uint());
+ let source_node_index = NodeIndex(source_vid.index);
graph.each_adjacent_edge(source_node_index, dir, |_, edge| {
match edge.data {
ConstrainVarSubVar(from_vid, to_vid) => {
use middle::ty_fold;
use middle::typeck::infer::{Bounds, cyclic_ty, fixup_err, fres, InferCtxt};
use middle::typeck::infer::unresolved_ty;
-use middle::typeck::infer::to_str::InferStr;
-use middle::typeck::infer::unify::{Root, UnifyInferCtxtMethods};
-use util::common::{indent, indenter};
-use util::ppaux::ty_to_str;
+use middle::typeck::infer::unify::Root;
+use util::common::{indent};
+use util::ppaux::{ty_to_str, Repr};
use syntax::ast;
}
pub fn resolve_type(&mut self, typ: ty::t) -> ty::t {
- debug!("resolve_type({})", typ.inf_str(self.infcx));
- let _i = indenter();
+ debug!("resolve_type({})", typ.repr(self.infcx.tcx));
if !ty::type_needs_infer(typ) {
return typ;
}
pub fn resolve_region(&mut self, orig: ty::Region) -> ty::Region {
- debug!("Resolve_region({})", orig.inf_str(self.infcx));
+ debug!("Resolve_region({})", orig.repr(self.infcx.tcx));
match orig {
ty::ReInfer(ty::ReVar(rid)) => self.resolve_region_var(rid),
_ => orig
// tend to carry more restrictions or higher
// perf. penalties, so it pays to know more.
- let nde = self.infcx.get(vid);
- let bounds = nde.possible_types;
-
- let t1 = match bounds {
- Bounds { ub:_, lb:Some(t) } if !type_is_bot(t)
- => self.resolve_type(t),
- Bounds { ub:Some(t), lb:_ } => self.resolve_type(t),
- Bounds { ub:_, lb:Some(t) } => self.resolve_type(t),
+ let node =
+ self.infcx.type_unification_table.borrow_mut().get(tcx, vid);
+ let t1 = match node.value {
+ Bounds { ub:_, lb:Some(t) } if !type_is_bot(t) => {
+ self.resolve_type(t)
+ }
+ Bounds { ub:Some(t), lb:_ } | Bounds { ub:_, lb:Some(t) } => {
+ self.resolve_type(t)
+ }
Bounds { ub:None, lb:None } => {
if self.should(force_tvar) {
self.err = Some(unresolved_ty(vid));
return ty::mk_int_var(self.infcx.tcx, vid);
}
- let node = self.infcx.get(vid);
- match node.possible_types {
+ let tcx = self.infcx.tcx;
+ let table = &self.infcx.int_unification_table;
+ let node = table.borrow_mut().get(tcx, vid);
+ match node.value {
Some(IntType(t)) => ty::mk_mach_int(t),
Some(UintType(t)) => ty::mk_mach_uint(t),
None => {
if self.should(force_ivar) {
// As a last resort, default to int.
let ty = ty::mk_int();
- self.infcx.set(vid, Root(Some(IntType(ast::TyI)), node.rank));
+ table.borrow_mut().set(
+ tcx, node.key, Root(Some(IntType(ast::TyI)), node.rank));
ty
} else {
ty::mk_int_var(self.infcx.tcx, vid)
return ty::mk_float_var(self.infcx.tcx, vid);
}
- let node = self.infcx.get(vid);
- match node.possible_types {
+ let tcx = self.infcx.tcx;
+ let table = &self.infcx.float_unification_table;
+ let node = table.borrow_mut().get(tcx, vid);
+ match node.value {
Some(t) => ty::mk_mach_float(t),
None => {
if self.should(force_fvar) {
// As a last resort, default to f64.
let ty = ty::mk_f64();
- self.infcx.set(vid, Root(Some(ast::TyF64), node.rank));
+ table.borrow_mut().set(
+ tcx, node.key, Root(Some(ast::TyF64), node.rank));
ty
} else {
ty::mk_float_var(self.infcx.tcx, vid)
use middle::typeck::infer::lattice::CombineFieldsLatticeMethods;
use middle::typeck::infer::lub::Lub;
use middle::typeck::infer::then;
-use middle::typeck::infer::to_str::InferStr;
use middle::typeck::infer::{TypeTrace, Subtype};
use util::common::{indenter};
-use util::ppaux::bound_region_to_str;
+use util::ppaux::{bound_region_to_str, Repr};
use syntax::ast::{Onceness, FnStyle, MutImmutable, MutMutable};
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
debug!("{}.regions({}, {})",
self.tag(),
- a.inf_str(self.get_ref().infcx),
- b.inf_str(self.get_ref().infcx));
+ a.repr(self.get_ref().infcx.tcx),
+ b.repr(self.get_ref().infcx.tcx));
self.get_ref().infcx.region_vars.make_subregion(Subtype(self.trace()), a, b);
Ok(a)
}
fn mts(&self, a: &ty::mt, b: &ty::mt) -> cres<ty::mt> {
- debug!("mts({} <: {})", a.inf_str(self.get_ref().infcx), b.inf_str(self.get_ref().infcx));
+ debug!("mts({} <: {})",
+ a.repr(self.get_ref().infcx.tcx),
+ b.repr(self.get_ref().infcx.tcx));
if a.mutbl != b.mutbl {
return Err(ty::terr_mutability);
fn tys(&self, a: ty::t, b: ty::t) -> cres<ty::t> {
debug!("{}.tys({}, {})", self.tag(),
- a.inf_str(self.get_ref().infcx), b.inf_str(self.get_ref().infcx));
+ a.repr(self.get_ref().infcx.tcx), b.repr(self.get_ref().infcx.tcx));
if a == b { return Ok(a); }
let _indenter = indenter();
match (&ty::get(a).sty, &ty::get(b).sty) {
fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
debug!("fn_sigs(a={}, b={})",
- a.inf_str(self.get_ref().infcx), b.inf_str(self.get_ref().infcx));
+ a.repr(self.get_ref().infcx.tcx), b.repr(self.get_ref().infcx.tcx));
let _indenter = indenter();
// Rather than checking the subtype relationship between `a` and `b`
// Note: this is a subtle algorithm. For a full explanation,
// please see the large comment in `region_inference.rs`.
- // Take a snapshot. We'll never roll this back, but in later
- // phases we do want to be able to examine "all bindings that
- // were created as part of this type comparison", and making a
- // snapshot is a convenient way to do that.
- let snapshot = self.get_ref().infcx.region_vars.start_snapshot();
+ // Make a mark so we can examine "all bindings that were
+ // created as part of this type comparison".
+ let mark = self.get_ref().infcx.region_vars.mark();
// First, we instantiate each bound region in the subtype with a fresh
// region variable.
})
};
- debug!("a_sig={}", a_sig.inf_str(self.get_ref().infcx));
- debug!("b_sig={}", b_sig.inf_str(self.get_ref().infcx));
+ debug!("a_sig={}", a_sig.repr(self.get_ref().infcx.tcx));
+ debug!("b_sig={}", b_sig.repr(self.get_ref().infcx.tcx));
// Compare types now that bound regions have been replaced.
let sig = if_ok!(super_fn_sigs(self, &a_sig, &b_sig));
// Presuming type comparison succeeds, we need to check
// that the skolemized regions do not "leak".
let new_vars =
- self.get_ref().infcx.region_vars.vars_created_since_snapshot(snapshot);
+ self.get_ref().infcx.region_vars.vars_created_since_mark(mark);
for (&skol_br, &skol) in skol_map.iter() {
- let tainted = self.get_ref().infcx.region_vars.tainted(snapshot, skol);
+ let tainted = self.get_ref().infcx.region_vars.tainted(mark, skol);
for tainted_region in tainted.iter() {
// Each skolemized should only be relatable to itself
// or new variables:
// A is not as polymorphic as B:
if self.a_is_expected() {
+ debug!("Not as polymorphic!");
return Err(ty::terr_regions_insufficiently_polymorphic(
skol_br, *tainted_region));
} else {
+ debug!("Overly polymorphic!");
return Err(ty::terr_regions_overly_polymorphic(
skol_br, *tainted_region));
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-/**
+/*!
# Standalone Tests for the Inference Module
-Note: This module is only compiled when doing unit testing.
-
*/
+// This is only used by tests, hence allow dead code.
+#![allow(dead_code)]
+
+use driver::config;
use driver::diagnostic;
-use driver::driver::{optgroups, build_session_options, build_session};
-use driver::driver::{str_input, build_configuration};
-use middle::lang_items::{LanguageItems, language_items};
-use middle::ty::{FnTyBase, FnMeta, FnSig};
-use util::ppaux::ty_to_str;
-
-use extra::oldmap::HashMap;
-use getopts::{optopt, optmulti, optflag, optflagopt, getopts};
-use getopts::opt_present;
-use syntax::codemap::DUMMY_SP;
-use syntax::parse::parse_crate_from_source_str;
-use syntax::{ast, attr, parse};
-
-struct Env {
- krate: @ast::Crate,
- tcx: ty::ctxt,
- infcx: infer::infer_ctxt,
- err_messages: @DVec<String>
+use driver::diagnostic::Emitter;
+use driver::driver;
+use driver::session;
+use middle::freevars;
+use middle::lang_items;
+use middle::region;
+use middle::resolve;
+use middle::resolve_lifetime;
+use middle::stability;
+use middle::ty;
+use middle::typeck::infer::combine::Combine;
+use middle::typeck::infer;
+use middle::typeck::infer::lub::Lub;
+use middle::typeck::infer::glb::Glb;
+use syntax::codemap;
+use syntax::codemap::{Span, CodeMap, DUMMY_SP};
+use syntax::diagnostic::{Level, RenderSpan, Bug, Fatal, Error, Warning, Note};
+use syntax::ast;
+use syntax::crateid::CrateId;
+use util::ppaux::{ty_to_str, UserString};
+
+struct Env<'a> {
+ krate: ast::Crate,
+ tcx: &'a ty::ctxt,
+ infcx: &'a infer::InferCtxt<'a>,
+}
+
+struct RH<'a> {
+ id: ast::NodeId,
+ sub: &'a [RH<'a>]
}
-struct RH {
- id: ast::node_id,
- sub: &[RH]
+static EMPTY_SOURCE_STR: &'static str = "#![no_std]";
+
+struct ExpectErrorEmitter {
+ messages: Vec<String>
}
-static EMPTY_SOURCE_STR: &str = "/* Hello, world! */";
-
-fn setup_env(test_name: &str, source_string: &str) -> Env {
- let messages = @DVec();
- let matches = getopts(vec!("-Z".to_string(), "verbose".to_string()), optgroups()).get();
- let diag = diagnostic::collect(messages);
- let sessopts = build_session_options("rustc".to_string(), &matches, diag);
- let sess = build_session(sessopts, None, diag);
- let cfg = build_configuration(sess, "whatever".to_string(), str_input("".to_string()));
- let dm = HashMap();
- let amap = HashMap();
- let freevars = HashMap();
- let region_paramd_items = HashMap();
- let region_map = HashMap();
- let lang_items = LanguageItems::new();
-
- let parse_sess = parse::new_parse_sess(None);
- let krate = parse_crate_from_source_str(
- test_name.to_str(), @source_string.to_str(),
- cfg, parse_sess);
-
- let tcx = ty::mk_ctxt(sess, dm, amap, freevars, region_map,
- region_paramd_items, lang_items);
-
- let infcx = infer::new_infer_ctxt(tcx);
-
- return Env {krate: krate,
- tcx: tcx,
- infcx: infcx,
- err_messages: messages};
+fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) {
+ match lvl {
+ Bug | Fatal | Error => { }
+ Warning | Note => { return; }
+ }
+
+ debug!("Error: {}", msg);
+ match e.messages.iter().position(|m| msg.contains(m.as_slice())) {
+ Some(i) => {
+ e.messages.remove(i);
+ }
+ None => {
+ fail!("Unexpected error: {} Expected: {}",
+ msg, e.messages);
+ }
+ }
}
-impl Env {
+impl Emitter for ExpectErrorEmitter {
+ fn emit(&mut self,
+ _cmsp: Option<(&codemap::CodeMap, Span)>,
+ msg: &str,
+ lvl: Level)
+ {
+ remove_message(self, msg, lvl);
+ }
+
+ fn custom_emit(&mut self,
+ _cm: &codemap::CodeMap,
+ _sp: RenderSpan,
+ msg: &str,
+ lvl: Level)
+ {
+ remove_message(self, msg, lvl);
+ }
+}
+
+fn errors(msgs: &[&str]) -> (Box<Emitter+Send>, uint) {
+ let v = Vec::from_fn(msgs.len(), |i| msgs[i].to_owned());
+ (box ExpectErrorEmitter { messages: v } as Box<Emitter+Send>, msgs.len())
+}
+
+fn test_env(_test_name: &str,
+ source_string: &str,
+ (emitter, expected_err_count): (Box<Emitter+Send>, uint),
+ body: |Env|) {
+ let options =
+ config::basic_options();
+ let codemap =
+ CodeMap::new();
+ let diagnostic_handler =
+ diagnostic::mk_handler(emitter);
+ let span_diagnostic_handler =
+ diagnostic::mk_span_handler(diagnostic_handler, codemap);
+
+ let sess = session::build_session_(options, None, span_diagnostic_handler);
+ let krate_config = Vec::new();
+ let input = driver::StrInput(source_string.to_owned());
+ let krate = driver::phase_1_parse_input(&sess, krate_config, &input);
+ let krate_id = CrateId { path: "test".to_owned(),
+ name: "test".to_owned(),
+ version: None };
+ let (krate, ast_map) =
+ driver::phase_2_configure_and_expand(&sess, krate, &krate_id);
+
+ // run just enough stuff to build a tcx:
+ let lang_items = lang_items::collect_language_items(&krate, &sess);
+ let resolve::CrateMap { def_map: def_map, .. } =
+ resolve::resolve_crate(&sess, &lang_items, &krate);
+ let freevars_map = freevars::annotate_freevars(&def_map, &krate);
+ let named_region_map = resolve_lifetime::krate(&sess, &krate);
+ let region_map = region::resolve_crate(&sess, &krate);
+ let stability_index = stability::Index::build(&krate);
+ let tcx = ty::mk_ctxt(sess, def_map, named_region_map, ast_map,
+ freevars_map, region_map, lang_items, stability_index);
+ let infcx = infer::new_infer_ctxt(&tcx);
+ let env = Env {krate: krate,
+ tcx: &tcx,
+ infcx: &infcx};
+ body(env);
+ infcx.resolve_regions_and_report_errors();
+ assert_eq!(tcx.sess.err_count(), expected_err_count);
+}
+
+impl<'a> Env<'a> {
pub fn create_region_hierarchy(&self, rh: &RH) {
for child_rh in rh.sub.iter() {
self.create_region_hierarchy(child_rh);
- self.tcx.region_map.insert(child_rh.id, rh.id);
+ self.tcx.region_maps.record_encl_scope(child_rh.id, rh.id);
}
}
sub: &[]}]});
}
- pub fn lookup_item(&self, names: &[String]) -> ast::node_id {
- return match search_mod(self, &self.krate.node.module, 0, names) {
+ pub fn lookup_item(&self, names: &[String]) -> ast::NodeId {
+ return match search_mod(self, &self.krate.module, 0, names) {
Some(id) => id,
None => {
- fail!("no item found: `%s`", names.connect("::"));
+ fail!("no item found: `{}`", names.connect("::"));
}
};
- fn search_mod(self: &Env,
+ fn search_mod(this: &Env,
m: &ast::Mod,
idx: uint,
- names: &[String]) -> Option<ast::node_id> {
+ names: &[String])
+ -> Option<ast::NodeId> {
assert!(idx < names.len());
for item in m.items.iter() {
- if self.tcx.sess.str_of(item.ident) == names[idx] {
- return search(self, *item, idx+1, names);
+ if item.ident.user_string(this.tcx) == names[idx] {
+ return search(this, *item, idx+1, names);
}
}
return None;
}
- fn search(self: &Env,
- it: @ast::Item,
+ fn search(this: &Env,
+ it: &ast::Item,
idx: uint,
- names: &[String]) -> Option<ast::node_id> {
+ names: &[String])
+ -> Option<ast::NodeId> {
if idx == names.len() {
return Some(it.id);
}
return match it.node {
- ast::ItemConst(..) | ast::ItemFn(..) |
+ ast::ItemStatic(..) | ast::ItemFn(..) |
ast::ItemForeignMod(..) | ast::ItemTy(..) => {
None
}
}
ast::ItemMod(ref m) => {
- search_mod(self, m, idx, names)
+ search_mod(this, m, idx, names)
}
};
}
}
+ pub fn make_subtype(&self, a: ty::t, b: ty::t) -> bool {
+ match infer::mk_subty(self.infcx, true, infer::Misc(DUMMY_SP), a, b) {
+ Ok(_) => true,
+ Err(ref e) => fail!("Encountered error: {}",
+ ty::type_err_to_str(self.tcx, e))
+ }
+ }
+
pub fn is_subtype(&self, a: ty::t, b: ty::t) -> bool {
match infer::can_mk_subty(self.infcx, a, b) {
Ok(_) => true,
pub fn assert_subtype(&self, a: ty::t, b: ty::t) {
if !self.is_subtype(a, b) {
- fail!("%s is not a subtype of %s, but it should be",
+ fail!("{} is not a subtype of {}, but it should be",
self.ty_to_str(a),
self.ty_to_str(b));
}
pub fn assert_not_subtype(&self, a: ty::t, b: ty::t) {
if self.is_subtype(a, b) {
- fail!("%s is a subtype of %s, but it shouldn't be",
+ fail!("{} is a subtype of {}, but it shouldn't be",
self.ty_to_str(a),
self.ty_to_str(b));
}
}
- pub fn assert_strict_subtype(&self, a: ty::t, b: ty::t) {
- self.assert_subtype(a, b);
- self.assert_not_subtype(b, a);
- }
-
pub fn assert_eq(&self, a: ty::t, b: ty::t) {
self.assert_subtype(a, b);
self.assert_subtype(b, a);
ty_to_str(self.tcx, a)
}
- pub fn t_fn(&self, input_tys: &[ty::t], output_ty: ty::t) -> ty::t {
- let inputs = input_tys.map(|t| {mode: ast::expl(ast::by_copy),
- ty: *t});
- ty::mk_fn(self.tcx, FnTyBase {
- meta: FnMeta {fn_style: ast::NormalFn,
- proto: ast::ProtoBare,
- onceness: ast::Many,
- region: ty::ReStatic,
- bounds: @Vec::new()},
- sig: FnSig {
- inputs: inputs,
- output: output_ty,
- variadic: false
- }
- })
+ pub fn t_fn(&self,
+ binder_id: ast::NodeId,
+ input_tys: &[ty::t],
+ output_ty: ty::t)
+ -> ty::t
+ {
+ ty::mk_ctor_fn(self.tcx, binder_id, input_tys, output_ty)
}
pub fn t_int(&self) -> ty::t {
- ty::mk_int(self.tcx)
+ ty::mk_int()
}
- pub fn t_rptr_bound(&self, id: uint) -> ty::t {
- ty::mk_imm_rptr(self.tcx, ty::re_bound(ty::BrAnon(id)), self.t_int())
+ pub fn t_rptr_late_bound(&self, binder_id: ast::NodeId, id: uint) -> ty::t {
+ ty::mk_imm_rptr(self.tcx, ty::ReLateBound(binder_id, ty::BrAnon(id)),
+ self.t_int())
}
- pub fn t_rptr_scope(&self, id: ast::node_id) -> ty::t {
+ pub fn t_rptr_scope(&self, id: ast::NodeId) -> ty::t {
ty::mk_imm_rptr(self.tcx, ty::ReScope(id), self.t_int())
}
- pub fn t_rptr_free(&self, nid: ast::node_id, id: uint) -> ty::t {
+ pub fn t_rptr_free(&self, nid: ast::NodeId, id: uint) -> ty::t {
ty::mk_imm_rptr(self.tcx,
ty::ReFree(ty::FreeRegion {scope_id: nid,
bound_region: ty::BrAnon(id)}),
ty::mk_imm_rptr(self.tcx, ty::ReStatic, self.t_int())
}
- pub fn lub() -> Lub { Lub(self.infcx.combine_fields(true, DUMMY_SP)) }
+ pub fn dummy_type_trace(&self) -> infer::TypeTrace {
+ infer::TypeTrace {
+ origin: infer::Misc(DUMMY_SP),
+ values: infer::Types(ty::expected_found {
+ expected: ty::mk_err(),
+ found: ty::mk_err(),
+ })
+ }
+ }
+
+ pub fn lub(&self) -> Lub<'a> {
+ let trace = self.dummy_type_trace();
+ Lub(self.infcx.combine_fields(true, trace))
+ }
- pub fn glb() -> Glb { Glb(self.infcx.combine_fields(true, DUMMY_SP)) }
+ pub fn glb(&self) -> Glb<'a> {
+ let trace = self.dummy_type_trace();
+ Glb(self.infcx.combine_fields(true, trace))
+ }
- pub fn resolve_regions(exp_count: uint) {
- debug!("resolve_regions(%u)", exp_count);
+ pub fn resolve_regions(&self) {
+ self.infcx.resolve_regions_and_report_errors();
+ }
- self.infcx.resolve_regions();
- if self.err_messages.len() != exp_count {
- for msg in self.err_messages.iter() {
- debug!("Error encountered: %s", *msg);
- }
- format!("resolving regions encountered %u errors but expected %u!",
- self.err_messages.len(),
- exp_count);
+ pub fn make_lub_ty(&self, t1: ty::t, t2: ty::t) -> ty::t {
+ match self.lub().tys(t1, t2) {
+ Ok(t) => t,
+ Err(ref e) => fail!("unexpected error computing LUB: {:?}",
+ ty::type_err_to_str(self.tcx, e))
}
}
/// Checks that `LUB(t1,t2) == t_lub`
pub fn check_lub(&self, t1: ty::t, t2: ty::t, t_lub: ty::t) {
match self.lub().tys(t1, t2) {
- Err(e) => {
- fail!("unexpected error computing LUB: %?", e)
- }
Ok(t) => {
self.assert_eq(t, t_lub);
-
- // sanity check for good measure:
- self.assert_subtype(t1, t);
- self.assert_subtype(t2, t);
-
- self.resolve_regions(0);
+ }
+ Err(ref e) => {
+ fail!("unexpected error in LUB: {}",
+ ty::type_err_to_str(self.tcx, e))
}
}
}
/// Checks that `GLB(t1,t2) == t_glb`
pub fn check_glb(&self, t1: ty::t, t2: ty::t, t_glb: ty::t) {
- debug!("check_glb(t1=%s, t2=%s, t_glb=%s)",
+ debug!("check_glb(t1={}, t2={}, t_glb={})",
self.ty_to_str(t1),
self.ty_to_str(t2),
self.ty_to_str(t_glb));
match self.glb().tys(t1, t2) {
Err(e) => {
- fail!("unexpected error computing LUB: %?", e)
+ fail!("unexpected error computing LUB: {:?}", e)
}
Ok(t) => {
self.assert_eq(t, t_glb);
// sanity check for good measure:
self.assert_subtype(t, t1);
self.assert_subtype(t, t2);
-
- self.resolve_regions(0);
}
}
}
match self.lub().tys(t1, t2) {
Err(_) => {}
Ok(t) => {
- fail!("unexpected success computing LUB: %?", self.ty_to_str(t))
+ fail!("unexpected success computing LUB: {}", self.ty_to_str(t))
}
}
}
match self.glb().tys(t1, t2) {
Err(_) => {}
Ok(t) => {
- fail!("unexpected success computing GLB: %?", self.ty_to_str(t))
+ fail!("unexpected success computing GLB: {}", self.ty_to_str(t))
}
}
}
}
#[test]
-fn contravariant_region_ptr() {
- let env = setup_env("contravariant_region_ptr", EMPTY_SOURCE_STR);
- env.create_simple_region_hierarchy();
- let t_rptr1 = env.t_rptr_scope(1);
- let t_rptr10 = env.t_rptr_scope(10);
- env.assert_eq(t_rptr1, t_rptr1);
- env.assert_eq(t_rptr10, t_rptr10);
- env.assert_strict_subtype(t_rptr1, t_rptr10);
+fn contravariant_region_ptr_ok() {
+ test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors([]), |env| {
+ env.create_simple_region_hierarchy();
+ let t_rptr1 = env.t_rptr_scope(1);
+ let t_rptr10 = env.t_rptr_scope(10);
+ env.assert_eq(t_rptr1, t_rptr1);
+ env.assert_eq(t_rptr10, t_rptr10);
+ env.make_subtype(t_rptr1, t_rptr10);
+ })
+}
+
+#[test]
+fn contravariant_region_ptr_err() {
+ test_env("contravariant_region_ptr",
+ EMPTY_SOURCE_STR,
+ errors(["lifetime mismatch"]),
+ |env| {
+ env.create_simple_region_hierarchy();
+ let t_rptr1 = env.t_rptr_scope(1);
+ let t_rptr10 = env.t_rptr_scope(10);
+ env.assert_eq(t_rptr1, t_rptr1);
+ env.assert_eq(t_rptr10, t_rptr10);
+
+ // will cause an error when regions are resolved
+ env.make_subtype(t_rptr10, t_rptr1);
+ })
}
#[test]
fn lub_bound_bound() {
- let env = setup_env("lub_bound_bound", EMPTY_SOURCE_STR);
- let t_rptr_bound1 = env.t_rptr_bound(1);
- let t_rptr_bound2 = env.t_rptr_bound(2);
- env.check_lub(env.t_fn([t_rptr_bound1], env.t_int()),
- env.t_fn([t_rptr_bound2], env.t_int()),
- env.t_fn([t_rptr_bound1], env.t_int()));
+ test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors([]), |env| {
+ let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
+ let t_rptr_bound2 = env.t_rptr_late_bound(22, 2);
+ env.check_lub(env.t_fn(22, [t_rptr_bound1], env.t_int()),
+ env.t_fn(22, [t_rptr_bound2], env.t_int()),
+ env.t_fn(22, [t_rptr_bound1], env.t_int()));
+ })
}
#[test]
fn lub_bound_free() {
- let env = setup_env("lub_bound_free", EMPTY_SOURCE_STR);
- let t_rptr_bound1 = env.t_rptr_bound(1);
- let t_rptr_free1 = env.t_rptr_free(0, 1);
- env.check_lub(env.t_fn([t_rptr_bound1], env.t_int()),
- env.t_fn([t_rptr_free1], env.t_int()),
- env.t_fn([t_rptr_free1], env.t_int()));
+ test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors([]), |env| {
+ let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
+ let t_rptr_free1 = env.t_rptr_free(0, 1);
+ env.check_lub(env.t_fn(22, [t_rptr_bound1], env.t_int()),
+ env.t_fn(22, [t_rptr_free1], env.t_int()),
+ env.t_fn(22, [t_rptr_free1], env.t_int()));
+ })
}
#[test]
fn lub_bound_static() {
- let env = setup_env("lub_bound_static", EMPTY_SOURCE_STR);
- let t_rptr_bound1 = env.t_rptr_bound(1);
- let t_rptr_static = env.t_rptr_static();
- env.check_lub(env.t_fn([t_rptr_bound1], env.t_int()),
- env.t_fn([t_rptr_static], env.t_int()),
- env.t_fn([t_rptr_static], env.t_int()));
+ test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors([]), |env| {
+ let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
+ let t_rptr_static = env.t_rptr_static();
+ env.check_lub(env.t_fn(22, [t_rptr_bound1], env.t_int()),
+ env.t_fn(22, [t_rptr_static], env.t_int()),
+ env.t_fn(22, [t_rptr_static], env.t_int()));
+ })
}
#[test]
fn lub_bound_bound_inverse_order() {
- let env = setup_env("lub_bound_bound_inverse_order", EMPTY_SOURCE_STR);
- let t_rptr_bound1 = env.t_rptr_bound(1);
- let t_rptr_bound2 = env.t_rptr_bound(2);
- env.check_lub(env.t_fn([t_rptr_bound1, t_rptr_bound2], t_rptr_bound1),
- env.t_fn([t_rptr_bound2, t_rptr_bound1], t_rptr_bound1),
- env.t_fn([t_rptr_bound1, t_rptr_bound1], t_rptr_bound1));
+ test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors([]), |env| {
+ let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
+ let t_rptr_bound2 = env.t_rptr_late_bound(22, 2);
+ env.check_lub(env.t_fn(22, [t_rptr_bound1, t_rptr_bound2], t_rptr_bound1),
+ env.t_fn(22, [t_rptr_bound2, t_rptr_bound1], t_rptr_bound1),
+ env.t_fn(22, [t_rptr_bound1, t_rptr_bound1], t_rptr_bound1));
+ })
}
#[test]
fn lub_free_free() {
- let env = setup_env("lub_free_free", EMPTY_SOURCE_STR);
- let t_rptr_free1 = env.t_rptr_free(0, 1);
- let t_rptr_free2 = env.t_rptr_free(0, 2);
- let t_rptr_static = env.t_rptr_static();
- env.check_lub(env.t_fn([t_rptr_free1], env.t_int()),
- env.t_fn([t_rptr_free2], env.t_int()),
- env.t_fn([t_rptr_static], env.t_int()));
+ test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors([]), |env| {
+ let t_rptr_free1 = env.t_rptr_free(0, 1);
+ let t_rptr_free2 = env.t_rptr_free(0, 2);
+ let t_rptr_static = env.t_rptr_static();
+ env.check_lub(env.t_fn(22, [t_rptr_free1], env.t_int()),
+ env.t_fn(22, [t_rptr_free2], env.t_int()),
+ env.t_fn(22, [t_rptr_static], env.t_int()));
+ })
}
#[test]
fn lub_returning_scope() {
- let env = setup_env("lub_returning_scope", EMPTY_SOURCE_STR);
- let t_rptr_scope10 = env.t_rptr_scope(10);
- let t_rptr_scope11 = env.t_rptr_scope(11);
- env.check_no_lub(env.t_fn([], t_rptr_scope10),
- env.t_fn([], t_rptr_scope11));
+ test_env("contravariant_region_ptr", EMPTY_SOURCE_STR,
+ errors(["cannot infer an appropriate lifetime"]), |env| {
+ let t_rptr_scope10 = env.t_rptr_scope(10);
+ let t_rptr_scope11 = env.t_rptr_scope(11);
+
+ // this should generate an error when regions are resolved
+ env.make_lub_ty(env.t_fn(22, [], t_rptr_scope10),
+ env.t_fn(22, [], t_rptr_scope11));
+ })
}
#[test]
fn glb_free_free_with_common_scope() {
- let env = setup_env("glb_free_free", EMPTY_SOURCE_STR);
- let t_rptr_free1 = env.t_rptr_free(0, 1);
- let t_rptr_free2 = env.t_rptr_free(0, 2);
- let t_rptr_scope = env.t_rptr_scope(0);
- env.check_glb(env.t_fn([t_rptr_free1], env.t_int()),
- env.t_fn([t_rptr_free2], env.t_int()),
- env.t_fn([t_rptr_scope], env.t_int()));
+ test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors([]), |env| {
+ let t_rptr_free1 = env.t_rptr_free(0, 1);
+ let t_rptr_free2 = env.t_rptr_free(0, 2);
+ let t_rptr_scope = env.t_rptr_scope(0);
+ env.check_glb(env.t_fn(22, [t_rptr_free1], env.t_int()),
+ env.t_fn(22, [t_rptr_free2], env.t_int()),
+ env.t_fn(22, [t_rptr_scope], env.t_int()));
+ })
}
#[test]
fn glb_bound_bound() {
- let env = setup_env("glb_bound_bound", EMPTY_SOURCE_STR);
- let t_rptr_bound1 = env.t_rptr_bound(1);
- let t_rptr_bound2 = env.t_rptr_bound(2);
- env.check_glb(env.t_fn([t_rptr_bound1], env.t_int()),
- env.t_fn([t_rptr_bound2], env.t_int()),
- env.t_fn([t_rptr_bound1], env.t_int()));
+ test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors([]), |env| {
+ let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
+ let t_rptr_bound2 = env.t_rptr_late_bound(22, 2);
+ env.check_glb(env.t_fn(22, [t_rptr_bound1], env.t_int()),
+ env.t_fn(22, [t_rptr_bound2], env.t_int()),
+ env.t_fn(22, [t_rptr_bound1], env.t_int()));
+ })
}
#[test]
fn glb_bound_free() {
- let env = setup_env("glb_bound_free", EMPTY_SOURCE_STR);
- let t_rptr_bound1 = env.t_rptr_bound(1);
- let t_rptr_free1 = env.t_rptr_free(0, 1);
- env.check_glb(env.t_fn([t_rptr_bound1], env.t_int()),
- env.t_fn([t_rptr_free1], env.t_int()),
- env.t_fn([t_rptr_bound1], env.t_int()));
+ test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors([]), |env| {
+ let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
+ let t_rptr_free1 = env.t_rptr_free(0, 1);
+ env.check_glb(env.t_fn(22, [t_rptr_bound1], env.t_int()),
+ env.t_fn(22, [t_rptr_free1], env.t_int()),
+ env.t_fn(22, [t_rptr_bound1], env.t_int()));
+ })
}
#[test]
fn glb_bound_static() {
- let env = setup_env("glb_bound_static", EMPTY_SOURCE_STR);
- let t_rptr_bound1 = env.t_rptr_bound(1);
- let t_rptr_static = env.t_rptr_static();
- env.check_glb(env.t_fn([t_rptr_bound1], env.t_int()),
- env.t_fn([t_rptr_static], env.t_int()),
- env.t_fn([t_rptr_bound1], env.t_int()));
+ test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors([]), |env| {
+ let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
+ let t_rptr_static = env.t_rptr_static();
+ env.check_glb(env.t_fn(22, [t_rptr_bound1], env.t_int()),
+ env.t_fn(22, [t_rptr_static], env.t_int()),
+ env.t_fn(22, [t_rptr_bound1], env.t_int()));
+ })
}
+++ /dev/null
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-
-use middle::ty::{FnSig, Vid};
-use middle::ty::IntVarValue;
-use middle::ty;
-use middle::typeck::infer::{Bound, Bounds};
-use middle::typeck::infer::InferCtxt;
-use middle::typeck::infer::unify::{Redirect, Root, VarValue};
-use util::ppaux::{mt_to_str, ty_to_str, trait_ref_to_str};
-
-use syntax::ast;
-
-pub trait InferStr {
- fn inf_str(&self, cx: &InferCtxt) -> String;
-}
-
-impl InferStr for ty::t {
- fn inf_str(&self, cx: &InferCtxt) -> String {
- ty_to_str(cx.tcx, *self)
- }
-}
-
-impl InferStr for FnSig {
- fn inf_str(&self, cx: &InferCtxt) -> String {
- format!("({}) -> {}",
- self.inputs.iter()
- .map(|a| a.inf_str(cx))
- .collect::<Vec<String>>().connect(", "),
- self.output.inf_str(cx))
- }
-}
-
-impl InferStr for ty::mt {
- fn inf_str(&self, cx: &InferCtxt) -> String {
- mt_to_str(cx.tcx, self)
- }
-}
-
-impl InferStr for ty::Region {
- fn inf_str(&self, _cx: &InferCtxt) -> String {
- format!("{:?}", *self)
- }
-}
-
-impl<V:InferStr> InferStr for Bound<V> {
- fn inf_str(&self, cx: &InferCtxt) -> String {
- match *self {
- Some(ref v) => v.inf_str(cx),
- None => "none".to_string()
- }
- }
-}
-
-impl<T:InferStr> InferStr for Bounds<T> {
- fn inf_str(&self, cx: &InferCtxt) -> String {
- format!("{{{} <: {}}}", self.lb.inf_str(cx), self.ub.inf_str(cx))
- }
-}
-
-impl<V:Vid + ToStr,T:InferStr> InferStr for VarValue<V, T> {
- fn inf_str(&self, cx: &InferCtxt) -> String {
- match *self {
- Redirect(ref vid) => format!("Redirect({})", vid.to_str()),
- Root(ref pt, rk) => {
- format!("Root({}, {})", pt.inf_str(cx), rk)
- }
- }
- }
-}
-
-impl InferStr for IntVarValue {
- fn inf_str(&self, _cx: &InferCtxt) -> String {
- self.to_str()
- }
-}
-
-impl InferStr for ast::FloatTy {
- fn inf_str(&self, _cx: &InferCtxt) -> String {
- self.to_str()
- }
-}
-
-impl InferStr for ty::TraitRef {
- fn inf_str(&self, cx: &InferCtxt) -> String {
- trait_ref_to_str(cx.tcx, self)
- }
-}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+use std::kinds::marker;
-use std::collections::SmallIntMap;
-
-use middle::ty::{Vid, expected_found, IntVarValue};
+use middle::ty::{expected_found, IntVarValue};
use middle::ty;
use middle::typeck::infer::{Bounds, uok, ures};
use middle::typeck::infer::InferCtxt;
-use middle::typeck::infer::to_str::InferStr;
use std::cell::RefCell;
+use std::fmt::Show;
+use std::mem;
use syntax::ast;
+use util::ppaux::Repr;
+
+/**
+ * This trait is implemented by any type that can serve as a type
+ * variable. We call such variables *unification keys*. For example,
+ * this trait is implemented by `TyVid`, which represents normal
+ * type variables, and `IntVid`, which represents integral variables.
+ *
+ * Each key type has an associated value type `V`. For example,
+ * for `TyVid`, this is `Bounds<ty::t>`, representing a pair of
+ * upper- and lower-bound types.
+ *
+ * Implementations of this trait are at the end of this file.
+ */
+pub trait UnifyKey<V> : Clone + Show + PartialEq + Repr {
+ fn index(&self) -> uint;
+
+ fn from_index(u: uint) -> Self;
+
+ /**
+ * Given an inference context, returns the unification table
+ * appropriate to this key type.
+ */
+ fn unification_table<'v>(infcx: &'v InferCtxt)
+ -> &'v RefCell<UnificationTable<Self,V>>;
+
+ fn tag(k: Option<Self>) -> &'static str;
+}
-#[deriving(Clone)]
-pub enum VarValue<V, T> {
- Redirect(V),
- Root(T, uint),
+/**
+ * Trait for valid types that a type variable can be set to. Note
+ * that this is typically not the end type that the value will
+ * take on, but rather some wrapper: for example, for normal type
+ * variables, the associated type is not `ty::t` but rather
+ * `Bounds<ty::t>`.
+ *
+ * Implementations of this trait are at the end of this file.
+ */
+pub trait UnifyValue : Clone + Repr + PartialEq {
}
-pub struct ValsAndBindings<V, T> {
- pub vals: SmallIntMap<VarValue<V, T>>,
- pub bindings: Vec<(V, VarValue<V, T>)> ,
+/**
+ * Value of a unification key. We implement Tarjan's union-find
+ * algorithm: when two keys are unified, one of them is converted
+ * into a "redirect" pointing at the other. These redirects form a
+ * DAG: the roots of the DAG (nodes that are not redirected) are each
+ * associated with a value of type `V` and a rank. The rank is used
+ * to keep the DAG relatively balanced, which helps keep the running
+ * time of the algorithm under control. For more information, see
+ * <http://en.wikipedia.org/wiki/Disjoint-set_data_structure>.
+ */
+#[deriving(PartialEq,Clone)]
+pub enum VarValue<K,V> {
+ Redirect(K),
+ Root(V, uint),
}
-impl<V:Clone, T:Clone> ValsAndBindings<V, T> {
- pub fn new() -> ValsAndBindings<V, T> {
- ValsAndBindings {
- vals: SmallIntMap::new(),
- bindings: Vec::new()
- }
- }
+/**
+ * Table of unification keys and their values.
+ */
+pub struct UnificationTable<K,V> {
+ /**
+ * Indicates the current value of each key.
+ */
+ values: Vec<VarValue<K,V>>,
+
+ /**
+ * When a snapshot is active, logs each change made to the table
+ * so that they can be unrolled.
+ */
+ undo_log: Vec<UndoLog<K,V>>,
}
-pub struct Node<V, T> {
- pub root: V,
- pub possible_types: T,
- pub rank: uint,
+/**
+ * At any time, users may snapshot a unification table. The changes
+ * made during the snapshot may either be *commited* or *rolled back*.
+ */
+pub struct Snapshot<K> {
+ // Ensure that this snapshot is keyed to the table type.
+ marker1: marker::CovariantType<K>,
+
+ // Snapshots are tokens that should be created/consumed linearly.
+ marker2: marker::NoCopy,
+
+ // Length of the undo log at the time the snapshot was taken.
+ length: uint,
}
-pub trait UnifyVid<T> {
- fn appropriate_vals_and_bindings<'v>(infcx: &'v InferCtxt)
- -> &'v RefCell<ValsAndBindings<Self, T>>;
+#[deriving(PartialEq)]
+enum UndoLog<K,V> {
+ /// Indicates where a snapshot started.
+ OpenSnapshot,
+
+ /// Indicates a snapshot that has been committed.
+ CommittedSnapshot,
+
+ /// New variable with given index was created.
+ NewVar(uint),
+
+ /// Variable with given index was changed *from* the given value.
+ SetVar(uint, VarValue<K,V>),
}
-pub trait UnifyInferCtxtMethods {
- fn get<T:Clone,
- V:Clone + PartialEq + Vid + UnifyVid<T>>(
- &self,
- vid: V)
- -> Node<V, T>;
- fn set<T:Clone + InferStr,
- V:Clone + Vid + ToStr + UnifyVid<T>>(
- &self,
- vid: V,
- new_v: VarValue<V, T>);
- fn unify<T:Clone + InferStr,
- V:Clone + Vid + ToStr + UnifyVid<T>>(
- &self,
- node_a: &Node<V, T>,
- node_b: &Node<V, T>)
- -> (V, uint);
+/**
+ * Internal type used to represent the result of a `get()` operation.
+ * Conveys the current root and value of the key.
+ */
+pub struct Node<K,V> {
+ pub key: K,
+ pub value: V,
+ pub rank: uint,
}
-impl<'a> UnifyInferCtxtMethods for InferCtxt<'a> {
- fn get<T:Clone,
- V:Clone + PartialEq + Vid + UnifyVid<T>>(
- &self,
- vid: V)
- -> Node<V, T> {
+// We can't use V:LatticeValue, much as I would like to,
+// because frequently the pattern is that V=Bounds<U> for some
+// other type parameter U, and we have no way to say
+// Bounds<U>:
+
+impl<V:PartialEq+Clone+Repr,K:UnifyKey<V>> UnificationTable<K,V> {
+ pub fn new() -> UnificationTable<K,V> {
+ UnificationTable {
+ values: Vec::new(),
+ undo_log: Vec::new()
+ }
+ }
+
+ pub fn in_snapshot(&self) -> bool {
+ /*! True if a snapshot has been started. */
+
+ self.undo_log.len() > 0
+ }
+
+ /**
+ * Starts a new snapshot. Each snapshot must be either
+ * rolled back or commited in a "LIFO" (stack) order.
+ */
+ pub fn snapshot(&mut self) -> Snapshot<K> {
+ let length = self.undo_log.len();
+ debug!("{}: snapshot at length {}",
+ UnifyKey::tag(None::<K>),
+ length);
+ self.undo_log.push(OpenSnapshot);
+ Snapshot { length: length,
+ marker1: marker::CovariantType,
+ marker2: marker::NoCopy }
+ }
+
+ fn assert_open_snapshot(&self, snapshot: &Snapshot<K>) {
+ // Or else there was a failure to follow a stack discipline:
+ assert!(self.undo_log.len() > snapshot.length);
+
+ // Invariant established by start_snapshot():
+ assert!(*self.undo_log.get(snapshot.length) == OpenSnapshot);
+ }
+
+ /**
+ * Reverses all changes since the last snapshot. Also
+ * removes any keys that have been created since then.
+ */
+ pub fn rollback_to(&mut self, tcx: &ty::ctxt, snapshot: Snapshot<K>) {
+ debug!("{}: rollback_to({})",
+ UnifyKey::tag(None::<K>),
+ snapshot.length);
+
+ self.assert_open_snapshot(&snapshot);
+
+ while self.undo_log.len() > snapshot.length + 1 {
+ match self.undo_log.pop().unwrap() {
+ OpenSnapshot => {
+ // This indicates a failure to obey the stack discipline.
+ tcx.sess.bug("Cannot rollback an uncommited snapshot");
+ }
+
+ CommittedSnapshot => {
+ // This occurs when there are nested snapshots and
+ // the inner is commited but outer is rolled back.
+ }
+
+ NewVar(i) => {
+ assert!(self.values.len() == i);
+ self.values.pop();
+ }
+
+ SetVar(i, v) => {
+ *self.values.get_mut(i) = v;
+ }
+ }
+ }
+
+ let v = self.undo_log.pop().unwrap();
+ assert!(v == OpenSnapshot);
+ assert!(self.undo_log.len() == snapshot.length);
+ }
+
+ /**
+ * Commits all changes since the last snapshot. Of course, they
+ * can still be undone if there is a snapshot further out.
+ */
+ pub fn commit(&mut self, snapshot: Snapshot<K>) {
+ debug!("{}: commit({})",
+ UnifyKey::tag(None::<K>),
+ snapshot.length);
+
+ self.assert_open_snapshot(&snapshot);
+
+ if snapshot.length == 0 {
+ // The root snapshot.
+ self.undo_log.truncate(0);
+ } else {
+ *self.undo_log.get_mut(snapshot.length) = CommittedSnapshot;
+ }
+ }
+
+ pub fn new_key(&mut self, value: V) -> K {
+ let index = self.values.len();
+
+ if self.in_snapshot() {
+ self.undo_log.push(NewVar(index));
+ }
+
+ self.values.push(Root(value, 0));
+ let k = UnifyKey::from_index(index);
+ debug!("{}: created new key: {}",
+ UnifyKey::tag(None::<K>),
+ k);
+ k
+ }
+
+ fn swap_value(&mut self,
+ index: uint,
+ new_value: VarValue<K,V>)
+ -> VarValue<K,V>
+ {
+ /*!
+ * Primitive operation to swap a value in the var array.
+ * Caller should update the undo log if we are in a snapshot.
+ */
+
+ let loc = self.values.get_mut(index);
+ mem::replace(loc, new_value)
+ }
+
+ pub fn get(&mut self, tcx: &ty::ctxt, vid: K) -> Node<K,V> {
/*!
- *
* Find the root node for `vid`. This uses the standard
* union-find algorithm with path compression:
* http://en.wikipedia.org/wiki/Disjoint-set_data_structure
*/
- let tcx = self.tcx;
- let vb = UnifyVid::appropriate_vals_and_bindings(self);
- return helper(tcx, &mut *vb.borrow_mut(), vid);
-
- fn helper<T:Clone, V:Clone+PartialEq+Vid>(
- tcx: &ty::ctxt,
- vb: &mut ValsAndBindings<V,T>,
- vid: V) -> Node<V, T>
- {
- let vid_u = vid.to_uint();
- let var_val = match vb.vals.find(&vid_u) {
- Some(&ref var_val) => (*var_val).clone(),
- None => {
- tcx.sess.bug(format!(
- "failed lookup of vid `{}`", vid_u).as_slice());
- }
- };
- match var_val {
- Redirect(vid) => {
- let node: Node<V,T> = helper(tcx, vb, vid.clone());
- if node.root != vid {
- // Path compression
- vb.vals.insert(vid.to_uint(),
- Redirect(node.root.clone()));
+ let index = vid.index();
+ let value = (*self.values.get(index)).clone();
+ match value {
+ Redirect(redirect) => {
+ let node: Node<K,V> = self.get(tcx, redirect.clone());
+ if node.key != redirect {
+ // Path compression
+ let old_value =
+ self.swap_value(index, Redirect(node.key.clone()));
+
+ // If we are in a snapshot, record this compression,
+ // because it's possible that the unification which
+ // caused it will be rolled back later.
+ if self.in_snapshot() {
+ self.undo_log.push(SetVar(index, old_value));
}
- node
- }
- Root(pt, rk) => {
- Node {root: vid, possible_types: pt, rank: rk}
}
+ node
}
+ Root(value, rank) => {
+ Node { key: vid, value: value, rank: rank }
+ }
+ }
+ }
+
+ fn is_root(&self, key: &K) -> bool {
+ match *self.values.get(key.index()) {
+ Redirect(..) => false,
+ Root(..) => true,
}
}
- fn set<T:Clone + InferStr,
- V:Clone + Vid + ToStr + UnifyVid<T>>(
- &self,
- vid: V,
- new_v: VarValue<V, T>) {
+ pub fn set(&mut self,
+ tcx: &ty::ctxt,
+ key: K,
+ new_value: VarValue<K,V>)
+ {
/*!
- *
- * Sets the value for `vid` to `new_v`. `vid` MUST be a root node!
+ * Sets the value for `vid` to `new_value`. `vid` MUST be a
+ * root node! Also, we must be in the middle of a snapshot.
*/
+ assert!(self.is_root(&key));
+ assert!(self.in_snapshot());
+
debug!("Updating variable {} to {}",
- vid.to_str(), new_v.inf_str(self));
+ key.repr(tcx),
+ new_value.repr(tcx));
- let vb = UnifyVid::appropriate_vals_and_bindings(self);
- let mut vb = vb.borrow_mut();
- let old_v = (*vb.vals.get(&vid.to_uint())).clone();
- vb.bindings.push((vid.clone(), old_v));
- vb.vals.insert(vid.to_uint(), new_v);
+ let index = key.index();
+ let old_value = self.swap_value(index, new_value);
+ self.undo_log.push(SetVar(index, old_value));
}
- fn unify<T:Clone + InferStr,
- V:Clone + Vid + ToStr + UnifyVid<T>>(
- &self,
- node_a: &Node<V, T>,
- node_b: &Node<V, T>)
- -> (V, uint) {
- // Rank optimization: if you don't know what it is, check
- // out <http://en.wikipedia.org/wiki/Disjoint-set_data_structure>
+ pub fn unify(&mut self,
+ tcx: &ty::ctxt,
+ node_a: &Node<K,V>,
+ node_b: &Node<K,V>)
+ -> (K, uint)
+ {
+ /*!
+ * Either redirects node_a to node_b or vice versa, depending
+ * on the relative rank. Returns the new root and rank. You
+ * should then update the value of the new root to something
+ * suitable.
+ */
- debug!("unify(node_a(id={:?}, rank={:?}), \
- node_b(id={:?}, rank={:?}))",
- node_a.root, node_a.rank,
- node_b.root, node_b.rank);
+ debug!("unify(node_a(id={}, rank={}), node_b(id={}, rank={}))",
+ node_a.key.repr(tcx),
+ node_a.rank,
+ node_b.key.repr(tcx),
+ node_b.rank);
if node_a.rank > node_b.rank {
// a has greater rank, so a should become b's parent,
// i.e., b should redirect to a.
- self.set(node_b.root.clone(), Redirect(node_a.root.clone()));
- (node_a.root.clone(), node_a.rank)
+ self.set(tcx, node_b.key.clone(), Redirect(node_a.key.clone()));
+ (node_a.key.clone(), node_a.rank)
} else if node_a.rank < node_b.rank {
// b has greater rank, so a should redirect to b.
- self.set(node_a.root.clone(), Redirect(node_b.root.clone()));
- (node_b.root.clone(), node_b.rank)
+ self.set(tcx, node_a.key.clone(), Redirect(node_b.key.clone()));
+ (node_b.key.clone(), node_b.rank)
} else {
// If equal, redirect one to the other and increment the
// other's rank.
assert_eq!(node_a.rank, node_b.rank);
- self.set(node_b.root.clone(), Redirect(node_a.root.clone()));
- (node_a.root.clone(), node_a.rank + 1)
+ self.set(tcx, node_b.key.clone(), Redirect(node_a.key.clone()));
+ (node_a.key.clone(), node_a.rank + 1)
}
}
-
}
-// ______________________________________________________________________
-// Code to handle simple variables like ints, floats---anything that
+///////////////////////////////////////////////////////////////////////////
+// Code to handle simple keys like ints, floats---anything that
// doesn't have a subtyping relationship we need to worry about.
-pub trait SimplyUnifiable {
+/**
+ * Indicates a type that does not have any kind of subtyping
+ * relationship.
+ */
+pub trait SimplyUnifiable : Clone + PartialEq + Repr {
fn to_type_err(expected_found<Self>) -> ty::type_err;
}
-pub fn mk_err<T:SimplyUnifiable>(a_is_expected: bool,
- a_t: T,
- b_t: T) -> ures {
+pub fn err<V:SimplyUnifiable>(a_is_expected: bool,
+ a_t: V,
+ b_t: V) -> ures {
if a_is_expected {
Err(SimplyUnifiable::to_type_err(
ty::expected_found {expected: a_t, found: b_t}))
}
}
-pub trait InferCtxtMethods {
- fn simple_vars<T:Clone + PartialEq + InferStr + SimplyUnifiable,
- V:Clone + PartialEq + Vid + ToStr + UnifyVid<Option<T>>>(
- &self,
+pub trait InferCtxtMethodsForSimplyUnifiableTypes<V:SimplyUnifiable,
+ K:UnifyKey<Option<V>>> {
+ fn simple_vars(&self,
a_is_expected: bool,
- a_id: V,
- b_id: V)
+ a_id: K,
+ b_id: K)
-> ures;
- fn simple_var_t<T:Clone + PartialEq + InferStr + SimplyUnifiable,
- V:Clone + PartialEq + Vid + ToStr + UnifyVid<Option<T>>>(
- &self,
+ fn simple_var_t(&self,
a_is_expected: bool,
- a_id: V,
- b: T)
+ a_id: K,
+ b: V)
-> ures;
}
-impl<'a> InferCtxtMethods for InferCtxt<'a> {
- fn simple_vars<T:Clone + PartialEq + InferStr + SimplyUnifiable,
- V:Clone + PartialEq + Vid + ToStr + UnifyVid<Option<T>>>(
- &self,
+impl<'tcx,V:SimplyUnifiable,K:UnifyKey<Option<V>>>
+ InferCtxtMethodsForSimplyUnifiableTypes<V,K> for InferCtxt<'tcx>
+{
+ fn simple_vars(&self,
a_is_expected: bool,
- a_id: V,
- b_id: V)
- -> ures {
+ a_id: K,
+ b_id: K)
+ -> ures
+ {
/*!
- *
- * Unifies two simple variables. Because simple variables do
- * not have any subtyping relationships, if both variables
+ * Unifies two simple keys. Because simple keys do
+ * not have any subtyping relationships, if both keys
* have already been associated with a value, then those two
- * values must be the same. */
+ * values must be the same.
+ */
- let node_a = self.get(a_id);
- let node_b = self.get(b_id);
- let a_id = node_a.root.clone();
- let b_id = node_b.root.clone();
+ let tcx = self.tcx;
+ let table = UnifyKey::unification_table(self);
+ let node_a = table.borrow_mut().get(tcx, a_id);
+ let node_b = table.borrow_mut().get(tcx, b_id);
+ let a_id = node_a.key.clone();
+ let b_id = node_b.key.clone();
if a_id == b_id { return uok(); }
- let combined = match (&node_a.possible_types, &node_b.possible_types)
- {
- (&None, &None) => None,
- (&Some(ref v), &None) | (&None, &Some(ref v)) => {
- Some((*v).clone())
- }
- (&Some(ref v1), &Some(ref v2)) => {
- if *v1 != *v2 {
- return mk_err(a_is_expected, (*v1).clone(), (*v2).clone())
+ let combined = {
+ match (&node_a.value, &node_b.value) {
+ (&None, &None) => {
+ None
+ }
+ (&Some(ref v), &None) | (&None, &Some(ref v)) => {
+ Some((*v).clone())
+ }
+ (&Some(ref v1), &Some(ref v2)) => {
+ if *v1 != *v2 {
+ return err(a_is_expected, (*v1).clone(), (*v2).clone())
+ }
+ Some((*v1).clone())
}
- Some((*v1).clone())
}
};
- let (new_root, new_rank) = self.unify(&node_a, &node_b);
- self.set(new_root, Root(combined, new_rank));
- return uok();
+ let (new_root, new_rank) = table.borrow_mut().unify(tcx,
+ &node_a,
+ &node_b);
+ table.borrow_mut().set(tcx, new_root, Root(combined, new_rank));
+ return Ok(())
}
- fn simple_var_t<T:Clone + PartialEq + InferStr + SimplyUnifiable,
- V:Clone + PartialEq + Vid + ToStr + UnifyVid<Option<T>>>(
- &self,
+ fn simple_var_t(&self,
a_is_expected: bool,
- a_id: V,
- b: T)
- -> ures {
+ a_id: K,
+ b: V)
+ -> ures
+ {
/*!
- *
- * Sets the value of the variable `a_id` to `b`. Because
- * simple variables do not have any subtyping relationships,
+ * Sets the value of the key `a_id` to `b`. Because
+ * simple keys do not have any subtyping relationships,
* if `a_id` already has a value, it must be the same as
- * `b`. */
+ * `b`.
+ */
- let node_a = self.get(a_id);
- let a_id = node_a.root.clone();
+ let tcx = self.tcx;
+ let table = UnifyKey::unification_table(self);
+ let node_a = table.borrow_mut().get(tcx, a_id);
+ let a_id = node_a.key.clone();
- match node_a.possible_types {
+ match node_a.value {
None => {
- self.set(a_id, Root(Some(b), node_a.rank));
- return uok();
+ table.borrow_mut().set(tcx, a_id, Root(Some(b), node_a.rank));
+ return Ok(());
}
Some(ref a_t) => {
if *a_t == b {
- return uok();
+ return Ok(());
} else {
- return mk_err(a_is_expected, (*a_t).clone(), b);
+ return err(a_is_expected, (*a_t).clone(), b);
}
}
}
}
}
-// ______________________________________________________________________
+///////////////////////////////////////////////////////////////////////////
+
+// General type keys
+
+impl UnifyKey<Bounds<ty::t>> for ty::TyVid {
+ fn index(&self) -> uint { self.index }
+
+ fn from_index(i: uint) -> ty::TyVid { ty::TyVid { index: i } }
-impl UnifyVid<Bounds<ty::t>> for ty::TyVid {
- fn appropriate_vals_and_bindings<'v>(infcx: &'v InferCtxt)
- -> &'v RefCell<ValsAndBindings<ty::TyVid, Bounds<ty::t>>> {
- return &infcx.ty_var_bindings;
+ fn unification_table<'v>(infcx: &'v InferCtxt)
+ -> &'v RefCell<UnificationTable<ty::TyVid, Bounds<ty::t>>>
+ {
+ return &infcx.type_unification_table;
+ }
+
+ fn tag(_: Option<ty::TyVid>) -> &'static str {
+ "TyVid"
}
}
-impl UnifyVid<Option<IntVarValue>> for ty::IntVid {
- fn appropriate_vals_and_bindings<'v>(infcx: &'v InferCtxt)
- -> &'v RefCell<ValsAndBindings<ty::IntVid, Option<IntVarValue>>> {
- return &infcx.int_var_bindings;
+impl UnifyValue for Bounds<ty::t> { }
+
+// Integral type keys
+
+impl UnifyKey<Option<IntVarValue>> for ty::IntVid {
+ fn index(&self) -> uint { self.index }
+
+ fn from_index(i: uint) -> ty::IntVid { ty::IntVid { index: i } }
+
+ fn unification_table<'v>(infcx: &'v InferCtxt)
+ -> &'v RefCell<UnificationTable<ty::IntVid, Option<IntVarValue>>>
+ {
+ return &infcx.int_unification_table;
+ }
+
+ fn tag(_: Option<ty::IntVid>) -> &'static str {
+ "IntVid"
}
}
}
}
-impl UnifyVid<Option<ast::FloatTy>> for ty::FloatVid {
- fn appropriate_vals_and_bindings<'v>(infcx: &'v InferCtxt)
- -> &'v RefCell<ValsAndBindings<ty::FloatVid, Option<ast::FloatTy>>> {
- return &infcx.float_var_bindings;
+impl UnifyValue for Option<IntVarValue> { }
+
+// Floating point type keys
+
+impl UnifyKey<Option<ast::FloatTy>> for ty::FloatVid {
+ fn index(&self) -> uint { self.index }
+
+ fn from_index(i: uint) -> ty::FloatVid { ty::FloatVid { index: i } }
+
+ fn unification_table<'v>(infcx: &'v InferCtxt)
+ -> &'v RefCell<UnificationTable<ty::FloatVid, Option<ast::FloatTy>>>
+ {
+ return &infcx.float_unification_table;
+ }
+
+ fn tag(_: Option<ty::FloatVid>) -> &'static str {
+ "FloatVid"
}
}
+impl UnifyValue for Option<ast::FloatTy> {
+}
+
impl SimplyUnifiable for ast::FloatTy {
fn to_type_err(err: expected_found<ast::FloatTy>) -> ty::type_err {
return ty::terr_float_mismatch(err);
use middle::ty::{ty_uniq, ty_trait, ty_int, ty_uint, ty_infer};
use middle::ty;
use middle::typeck;
+use middle::typeck::infer;
+use middle::typeck::infer::unify;
+use VV = middle::typeck::infer::unify::VarValue;
+use middle::typeck::infer::region_inference;
use std::rc::Rc;
use std::gc::Gc;
}
}
+impl Repr for ty::mt {
+ fn repr(&self, tcx: &ctxt) -> String {
+ mt_to_str(tcx, self)
+ }
+}
+
impl Repr for subst::Substs {
fn repr(&self, tcx: &ctxt) -> String {
format!("Substs[types={}, regions={}]",
}
ty::ReInfer(ReVar(ref vid)) => {
- format!("ReInfer({})", vid.id)
+ format!("ReInfer({})", vid.index)
}
ty::ReInfer(ReSkolemized(id, ref bound_region)) => {
}
}
-
-impl Repr for ty::RegionVid {
- fn repr(&self, _tcx: &ctxt) -> String {
- format!("{:?}", *self)
- }
-}
-
impl Repr for ty::TraitStore {
fn repr(&self, tcx: &ctxt) -> String {
trait_store_to_str(tcx, *self)
self.region.repr(tcx))
}
}
+
+impl Repr for ty::IntVid {
+ fn repr(&self, _tcx: &ctxt) -> String {
+ format!("{}", self)
+ }
+}
+
+impl Repr for ty::FloatVid {
+ fn repr(&self, _tcx: &ctxt) -> String {
+ format!("{}", self)
+ }
+}
+
+impl Repr for ty::RegionVid {
+ fn repr(&self, _tcx: &ctxt) -> String {
+ format!("{}", self)
+ }
+}
+
+impl Repr for ty::TyVid {
+ fn repr(&self, _tcx: &ctxt) -> String {
+ format!("{}", self)
+ }
+}
+
+impl Repr for ty::IntVarValue {
+ fn repr(&self, _tcx: &ctxt) -> String {
+ format!("{:?}", *self)
+ }
+}
+
+impl Repr for ast::IntTy {
+ fn repr(&self, _tcx: &ctxt) -> String {
+ format!("{:?}", *self)
+ }
+}
+
+impl Repr for ast::UintTy {
+ fn repr(&self, _tcx: &ctxt) -> String {
+ format!("{:?}", *self)
+ }
+}
+
+impl Repr for ast::FloatTy {
+ fn repr(&self, _tcx: &ctxt) -> String {
+ format!("{:?}", *self)
+ }
+}
+
+impl<T:Repr> Repr for infer::Bounds<T> {
+ fn repr(&self, tcx: &ctxt) -> String {
+ format!("({} <= {})",
+ self.lb.repr(tcx),
+ self.ub.repr(tcx))
+ }
+}
+
+impl<K:Repr,V:Repr> Repr for VV<K,V> {
+ fn repr(&self, tcx: &ctxt) -> String {
+ match *self {
+ unify::Redirect(ref k) =>
+ format!("Redirect({})", k.repr(tcx)),
+ unify::Root(ref v, r) =>
+ format!("Root({}, {})", v.repr(tcx), r)
+ }
+ }
+}
+
+impl Repr for region_inference::VarValue {
+ fn repr(&self, tcx: &ctxt) -> String {
+ match *self {
+ infer::region_inference::NoValue =>
+ format!("NoValue"),
+ infer::region_inference::Value(r) =>
+ format!("Value({})", r.repr(tcx)),
+ infer::region_inference::ErrorValue =>
+ format!("ErrorValue"),
+ }
+ }
+}