auto merge of #15955 : nikomatsakis/rust/issue-5527-new-inference-scheme, r=pcwalton
The inference scheme proposed in <http://smallcultfollowing.com/babysteps/blog/2014/07/09/an-experimental-new-type-inference-scheme-for-rust/>. This is theoretically a [breaking-change]. It is possible that you may encounter type checking errors, particularly related to closures or functions with higher-ranked lifetimes or object types. Adding more explicit type annotations should help the problem. However, I have not been able to make an example that *actually* successfully compiles with the older scheme and fails with the newer scheme. f? @pcwalton, @pnkfelix
This commit is contained in:
commit
bd159d3867
|
@ -136,6 +136,7 @@ pub mod util {
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod ppaux;
|
pub mod ppaux;
|
||||||
pub mod nodemap;
|
pub mod nodemap;
|
||||||
|
pub mod snapshot_vec;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod lib {
|
pub mod lib {
|
||||||
|
|
|
@ -30,7 +30,6 @@ use middle::subst::{Subst, Substs, VecPerParamSpace};
|
||||||
use middle::subst;
|
use middle::subst;
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use middle::typeck;
|
use middle::typeck;
|
||||||
use middle::typeck::MethodCall;
|
|
||||||
use middle::ty_fold;
|
use middle::ty_fold;
|
||||||
use middle::ty_fold::{TypeFoldable,TypeFolder};
|
use middle::ty_fold::{TypeFoldable,TypeFolder};
|
||||||
use middle;
|
use middle;
|
||||||
|
|
|
@ -1381,8 +1381,8 @@ fn link_by_ref(rcx: &Rcx,
|
||||||
expr.repr(tcx), callee_scope);
|
expr.repr(tcx), callee_scope);
|
||||||
let mc = mc::MemCategorizationContext::new(rcx);
|
let mc = mc::MemCategorizationContext::new(rcx);
|
||||||
let expr_cmt = ignore_err!(mc.cat_expr(expr));
|
let expr_cmt = ignore_err!(mc.cat_expr(expr));
|
||||||
let region_min = ty::ReScope(callee_scope);
|
let borrow_region = ty::ReScope(callee_scope);
|
||||||
link_region(rcx, expr.span, region_min, ty::ImmBorrow, expr_cmt);
|
link_region(rcx, expr.span, borrow_region, ty::ImmBorrow, expr_cmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn link_region_from_node_type(rcx: &Rcx,
|
fn link_region_from_node_type(rcx: &Rcx,
|
||||||
|
@ -1408,47 +1408,135 @@ fn link_region_from_node_type(rcx: &Rcx,
|
||||||
|
|
||||||
fn link_region(rcx: &Rcx,
|
fn link_region(rcx: &Rcx,
|
||||||
span: Span,
|
span: Span,
|
||||||
region_min: ty::Region,
|
borrow_region: ty::Region,
|
||||||
kind: ty::BorrowKind,
|
borrow_kind: ty::BorrowKind,
|
||||||
cmt_borrowed: mc::cmt) {
|
borrow_cmt: mc::cmt) {
|
||||||
/*!
|
/*!
|
||||||
* Informs the inference engine that a borrow of `cmt`
|
* Informs the inference engine that `borrow_cmt` is being
|
||||||
* must have the borrow kind `kind` and lifetime `region_min`.
|
* borrowed with kind `borrow_kind` and lifetime `borrow_region`.
|
||||||
* If `cmt` is a deref of a region pointer with
|
* In order to ensure borrowck is satisfied, this may create
|
||||||
* lifetime `r_borrowed`, this will add the constraint that
|
* constraints between regions, as explained in
|
||||||
* `region_min <= r_borrowed`.
|
* `link_reborrowed_region()`.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Iterate through all the things that must be live at least
|
let mut borrow_cmt = borrow_cmt;
|
||||||
// for the lifetime `region_min` for the borrow to be valid:
|
let mut borrow_kind = borrow_kind;
|
||||||
let mut cmt_borrowed = cmt_borrowed;
|
|
||||||
loop {
|
loop {
|
||||||
debug!("link_region(region_min={}, kind={}, cmt_borrowed={})",
|
debug!("link_region(borrow_region={}, borrow_kind={}, borrow_cmt={})",
|
||||||
region_min.repr(rcx.tcx()),
|
borrow_region.repr(rcx.tcx()),
|
||||||
kind.repr(rcx.tcx()),
|
borrow_kind.repr(rcx.tcx()),
|
||||||
cmt_borrowed.repr(rcx.tcx()));
|
borrow_cmt.repr(rcx.tcx()));
|
||||||
match cmt_borrowed.cat.clone() {
|
match borrow_cmt.cat.clone() {
|
||||||
mc::cat_deref(base, _, mc::BorrowedPtr(_, r_borrowed)) |
|
mc::cat_deref(ref_cmt, _,
|
||||||
mc::cat_deref(base, _, mc::Implicit(_, r_borrowed)) => {
|
mc::Implicit(ref_kind, ref_region)) |
|
||||||
// References to an upvar `x` are translated to
|
mc::cat_deref(ref_cmt, _,
|
||||||
// `*x`, since that is what happens in the
|
mc::BorrowedPtr(ref_kind, ref_region)) => {
|
||||||
// underlying machine. We detect such references
|
match link_reborrowed_region(rcx, span,
|
||||||
// and treat them slightly differently, both to
|
borrow_region, borrow_kind,
|
||||||
// offer better error messages and because we need
|
ref_cmt, ref_region, ref_kind) {
|
||||||
// to infer the kind of borrow (mut, const, etc)
|
Some((c, k)) => {
|
||||||
// to use for each upvar.
|
borrow_cmt = c;
|
||||||
let cause = match base.cat {
|
borrow_kind = k;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mc::cat_discr(cmt_base, _) |
|
||||||
|
mc::cat_downcast(cmt_base) |
|
||||||
|
mc::cat_deref(cmt_base, _, mc::GcPtr(..)) |
|
||||||
|
mc::cat_deref(cmt_base, _, mc::OwnedPtr) |
|
||||||
|
mc::cat_interior(cmt_base, _) => {
|
||||||
|
// Borrowing interior or owned data requires the base
|
||||||
|
// to be valid and borrowable in the same fashion.
|
||||||
|
borrow_cmt = cmt_base;
|
||||||
|
borrow_kind = borrow_kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
mc::cat_deref(_, _, mc::UnsafePtr(..)) |
|
||||||
|
mc::cat_static_item |
|
||||||
|
mc::cat_copied_upvar(..) |
|
||||||
|
mc::cat_local(..) |
|
||||||
|
mc::cat_arg(..) |
|
||||||
|
mc::cat_upvar(..) |
|
||||||
|
mc::cat_rvalue(..) => {
|
||||||
|
// These are all "base cases" with independent lifetimes
|
||||||
|
// that are not subject to inference
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn link_reborrowed_region(rcx: &Rcx,
|
||||||
|
span: Span,
|
||||||
|
borrow_region: ty::Region,
|
||||||
|
borrow_kind: ty::BorrowKind,
|
||||||
|
ref_cmt: mc::cmt,
|
||||||
|
ref_region: ty::Region,
|
||||||
|
ref_kind: ty::BorrowKind)
|
||||||
|
-> Option<(mc::cmt, ty::BorrowKind)>
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
* This is the most complicated case: the path being borrowed is
|
||||||
|
* itself the referent of a borrowed pointer. Let me give an
|
||||||
|
* example fragment of code to make clear(er) the situation:
|
||||||
|
*
|
||||||
|
* let r: &'a mut T = ...; // the original reference "r" has lifetime 'a
|
||||||
|
* ...
|
||||||
|
* &'z *r // the reborrow has lifetime 'z
|
||||||
|
*
|
||||||
|
* Now, in this case, our primary job is to add the inference
|
||||||
|
* constraint that `'z <= 'a`. Given this setup, let's clarify the
|
||||||
|
* parameters in (roughly) terms of the example:
|
||||||
|
*
|
||||||
|
* A borrow of: `& 'z bk * r` where `r` has type `& 'a bk T`
|
||||||
|
* borrow_region ^~ ref_region ^~
|
||||||
|
* borrow_kind ^~ ref_kind ^~
|
||||||
|
* ref_cmt ^
|
||||||
|
*
|
||||||
|
* Here `bk` stands for some borrow-kind (e.g., `mut`, `uniq`, etc).
|
||||||
|
*
|
||||||
|
* Unfortunately, there are some complications beyond the simple
|
||||||
|
* scenario I just painted:
|
||||||
|
*
|
||||||
|
* 1. The reference `r` might in fact be a "by-ref" upvar. In that
|
||||||
|
* case, we have two jobs. First, we are inferring whether this reference
|
||||||
|
* should be an `&T`, `&mut T`, or `&uniq T` reference, and we must
|
||||||
|
* adjust that based on this borrow (e.g., if this is an `&mut` borrow,
|
||||||
|
* then `r` must be an `&mut` reference). Second, whenever we link
|
||||||
|
* two regions (here, `'z <= 'a`), we supply a *cause*, and in this
|
||||||
|
* case we adjust the cause to indicate that the reference being
|
||||||
|
* "reborrowed" is itself an upvar. This provides a nicer error message
|
||||||
|
* should something go wrong.
|
||||||
|
*
|
||||||
|
* 2. There may in fact be more levels of reborrowing. In the
|
||||||
|
* example, I said the borrow was like `&'z *r`, but it might
|
||||||
|
* in fact be a borrow like `&'z **q` where `q` has type `&'a
|
||||||
|
* &'b mut T`. In that case, we want to ensure that `'z <= 'a`
|
||||||
|
* and `'z <= 'b`. This is explained more below.
|
||||||
|
*
|
||||||
|
* The return value of this function indicates whether we need to
|
||||||
|
* recurse and process `ref_cmt` (see case 2 above).
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Detect references to an upvar `x`:
|
||||||
|
let cause = match ref_cmt.cat {
|
||||||
mc::cat_upvar(ref upvar_id, _) => {
|
mc::cat_upvar(ref upvar_id, _) => {
|
||||||
match rcx.fcx.inh.upvar_borrow_map.borrow_mut()
|
let mut upvar_borrow_map =
|
||||||
.find_mut(upvar_id) {
|
rcx.fcx.inh.upvar_borrow_map.borrow_mut();
|
||||||
|
match upvar_borrow_map.find_mut(upvar_id) {
|
||||||
Some(upvar_borrow) => {
|
Some(upvar_borrow) => {
|
||||||
debug!("link_region: {} <= {}",
|
// Adjust mutability that we infer for the upvar
|
||||||
region_min.repr(rcx.tcx()),
|
// so it can accommodate being borrowed with
|
||||||
upvar_borrow.region.repr(rcx.tcx()));
|
// mutability `kind`:
|
||||||
adjust_upvar_borrow_kind_for_loan(
|
adjust_upvar_borrow_kind_for_loan(*upvar_id,
|
||||||
*upvar_id,
|
|
||||||
upvar_borrow,
|
upvar_borrow,
|
||||||
kind);
|
borrow_kind);
|
||||||
|
|
||||||
infer::ReborrowUpvar(span, *upvar_id)
|
infer::ReborrowUpvar(span, *upvar_id)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
@ -1466,55 +1554,67 @@ fn link_region(rcx: &Rcx,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("link_region: {} <= {}",
|
debug!("link_reborrowed_region: {} <= {}",
|
||||||
region_min.repr(rcx.tcx()),
|
borrow_region.repr(rcx.tcx()),
|
||||||
r_borrowed.repr(rcx.tcx()));
|
ref_region.repr(rcx.tcx()));
|
||||||
rcx.fcx.mk_subr(cause, region_min, r_borrowed);
|
rcx.fcx.mk_subr(cause, borrow_region, ref_region);
|
||||||
|
|
||||||
if kind != ty::ImmBorrow {
|
// Decide whether we need to recurse and link any regions within
|
||||||
// If this is a mutable borrow, then the thing
|
// the `ref_cmt`. This is concerned for the case where the value
|
||||||
// being borrowed will have to be unique.
|
// being reborrowed is in fact a borrowed pointer found within
|
||||||
// In user code, this means it must be an `&mut`
|
// another borrowed pointer. For example:
|
||||||
// borrow, but for an upvar, we might opt
|
|
||||||
// for an immutable-unique borrow.
|
|
||||||
adjust_upvar_borrow_kind_for_unique(rcx, base);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Borrowing an `&mut` pointee for `region_min` is
|
|
||||||
// only valid if the pointer resides in a unique
|
|
||||||
// location which is itself valid for
|
|
||||||
// `region_min`. We don't care about the unique
|
|
||||||
// part, but we may need to influence the
|
|
||||||
// inference to ensure that the location remains
|
|
||||||
// valid.
|
|
||||||
//
|
//
|
||||||
// FIXME(#8624) fixing borrowck will require this
|
// let p: &'b &'a mut T = ...;
|
||||||
// if m == ast::m_mutbl {
|
// ...
|
||||||
// cmt_borrowed = cmt_base;
|
// &'z **p
|
||||||
// } else {
|
//
|
||||||
// return;
|
// What makes this case particularly tricky is that, if the data
|
||||||
// }
|
// being borrowed is a `&mut` or `&uniq` borrow, borrowck requires
|
||||||
return;
|
// not only that `'z <= 'a`, (as before) but also `'z <= 'b`
|
||||||
}
|
// (otherwise the user might mutate through the `&mut T` reference
|
||||||
mc::cat_discr(cmt_base, _) |
|
// after `'b` expires and invalidate the borrow we are looking at
|
||||||
mc::cat_downcast(cmt_base) |
|
// now).
|
||||||
mc::cat_deref(cmt_base, _, mc::GcPtr(..)) |
|
//
|
||||||
mc::cat_deref(cmt_base, _, mc::OwnedPtr) |
|
// So let's re-examine our parameters in light of this more
|
||||||
mc::cat_interior(cmt_base, _) => {
|
// complicated (possible) scenario:
|
||||||
// Interior or owned data requires its base to be valid
|
//
|
||||||
cmt_borrowed = cmt_base;
|
// A borrow of: `& 'z bk * * p` where `p` has type `&'b bk & 'a bk T`
|
||||||
}
|
// borrow_region ^~ ref_region ^~
|
||||||
mc::cat_deref(_, _, mc::UnsafePtr(..)) |
|
// borrow_kind ^~ ref_kind ^~
|
||||||
mc::cat_static_item |
|
// ref_cmt ^~~
|
||||||
mc::cat_copied_upvar(..) |
|
//
|
||||||
mc::cat_local(..) |
|
// (Note that since we have not examined `ref_cmt.cat`, we don't
|
||||||
mc::cat_arg(..) |
|
// know whether this scenario has occurred; but I wanted to show
|
||||||
mc::cat_upvar(..) |
|
// how all the types get adjusted.)
|
||||||
mc::cat_rvalue(..) => {
|
match ref_kind {
|
||||||
// These are all "base cases" with independent lifetimes
|
ty::ImmBorrow => {
|
||||||
// that are not subject to inference
|
// The reference being reborrowed is a sharable ref of
|
||||||
return;
|
// type `&'a T`. In this case, it doesn't matter where we
|
||||||
|
// *found* the `&T` pointer, the memory it references will
|
||||||
|
// be valid and immutable for `'a`. So we can stop here.
|
||||||
|
//
|
||||||
|
// (Note that the `borrow_kind` must also be ImmBorrow or
|
||||||
|
// else the user is borrowed imm memory as mut memory,
|
||||||
|
// which means they'll get an error downstream in borrowck
|
||||||
|
// anyhow.)
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ty::MutBorrow | ty::UniqueImmBorrow => {
|
||||||
|
// The reference being reborrowed is either an `&mut T` or
|
||||||
|
// `&uniq T`. This is the case where recursion is needed.
|
||||||
|
//
|
||||||
|
// One interesting twist is that we can weaken the borrow
|
||||||
|
// kind when we recurse: to reborrow an `&mut` referent as
|
||||||
|
// mutable, borrowck requires a unique path to the `&mut`
|
||||||
|
// reference but not necessarily a *mutable* path.
|
||||||
|
let new_borrow_kind = match borrow_kind {
|
||||||
|
ty::ImmBorrow =>
|
||||||
|
ty::ImmBorrow,
|
||||||
|
ty::MutBorrow | ty::UniqueImmBorrow =>
|
||||||
|
ty::UniqueImmBorrow
|
||||||
|
};
|
||||||
|
return Some((ref_cmt, new_borrow_kind));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1534,6 +1634,12 @@ fn adjust_borrow_kind_for_assignment_lhs(rcx: &Rcx,
|
||||||
|
|
||||||
fn adjust_upvar_borrow_kind_for_mut(rcx: &Rcx,
|
fn adjust_upvar_borrow_kind_for_mut(rcx: &Rcx,
|
||||||
cmt: mc::cmt) {
|
cmt: mc::cmt) {
|
||||||
|
/*!
|
||||||
|
* Indicates that `cmt` is being directly mutated (e.g., assigned
|
||||||
|
* to). If cmt contains any by-ref upvars, this implies that
|
||||||
|
* those upvars must be borrowed using an `&mut` borow.
|
||||||
|
*/
|
||||||
|
|
||||||
let mut cmt = cmt;
|
let mut cmt = cmt;
|
||||||
loop {
|
loop {
|
||||||
debug!("adjust_upvar_borrow_kind_for_mut(cmt={})",
|
debug!("adjust_upvar_borrow_kind_for_mut(cmt={})",
|
||||||
|
|
|
@ -247,7 +247,7 @@ impl<'f> Coerce<'f> {
|
||||||
let a_borrowed = ty::mk_rptr(self.get_ref().infcx.tcx,
|
let a_borrowed = ty::mk_rptr(self.get_ref().infcx.tcx,
|
||||||
r_borrow,
|
r_borrow,
|
||||||
mt {ty: inner_ty, mutbl: mutbl_b});
|
mt {ty: inner_ty, mutbl: mutbl_b});
|
||||||
if_ok!(sub.tys(a_borrowed, b));
|
try!(sub.tys(a_borrowed, b));
|
||||||
|
|
||||||
Ok(Some(AutoDerefRef(AutoDerefRef {
|
Ok(Some(AutoDerefRef(AutoDerefRef {
|
||||||
autoderefs: 1,
|
autoderefs: 1,
|
||||||
|
@ -273,7 +273,7 @@ impl<'f> Coerce<'f> {
|
||||||
let r_borrow = self.get_ref().infcx.next_region_var(coercion);
|
let r_borrow = self.get_ref().infcx.next_region_var(coercion);
|
||||||
let unsized_ty = ty::mk_slice(self.get_ref().infcx.tcx, r_borrow,
|
let unsized_ty = ty::mk_slice(self.get_ref().infcx.tcx, r_borrow,
|
||||||
mt {ty: t_a, mutbl: mutbl_b});
|
mt {ty: t_a, mutbl: mutbl_b});
|
||||||
if_ok!(self.get_ref().infcx.try(|| sub.tys(unsized_ty, b)));
|
try!(self.get_ref().infcx.try(|| sub.tys(unsized_ty, b)));
|
||||||
Ok(Some(AutoDerefRef(AutoDerefRef {
|
Ok(Some(AutoDerefRef(AutoDerefRef {
|
||||||
autoderefs: 0,
|
autoderefs: 0,
|
||||||
autoref: Some(ty::AutoPtr(r_borrow,
|
autoref: Some(ty::AutoPtr(r_borrow,
|
||||||
|
@ -316,7 +316,7 @@ impl<'f> Coerce<'f> {
|
||||||
let ty = ty::mk_rptr(self.get_ref().infcx.tcx,
|
let ty = ty::mk_rptr(self.get_ref().infcx.tcx,
|
||||||
r_borrow,
|
r_borrow,
|
||||||
ty::mt{ty: ty, mutbl: mt_b.mutbl});
|
ty::mt{ty: ty, mutbl: mt_b.mutbl});
|
||||||
if_ok!(self.get_ref().infcx.try(|| sub.tys(ty, b)));
|
try!(self.get_ref().infcx.try(|| sub.tys(ty, b)));
|
||||||
debug!("Success, coerced with AutoDerefRef(1, \
|
debug!("Success, coerced with AutoDerefRef(1, \
|
||||||
AutoPtr(AutoUnsize({:?})))", kind);
|
AutoPtr(AutoUnsize({:?})))", kind);
|
||||||
Ok(Some(AutoDerefRef(AutoDerefRef {
|
Ok(Some(AutoDerefRef(AutoDerefRef {
|
||||||
|
@ -334,7 +334,7 @@ impl<'f> Coerce<'f> {
|
||||||
match self.unsize_ty(sty_a, t_b) {
|
match self.unsize_ty(sty_a, t_b) {
|
||||||
Some((ty, kind)) => {
|
Some((ty, kind)) => {
|
||||||
let ty = ty::mk_uniq(self.get_ref().infcx.tcx, ty);
|
let ty = ty::mk_uniq(self.get_ref().infcx.tcx, ty);
|
||||||
if_ok!(self.get_ref().infcx.try(|| sub.tys(ty, b)));
|
try!(self.get_ref().infcx.try(|| sub.tys(ty, b)));
|
||||||
debug!("Success, coerced with AutoDerefRef(1, \
|
debug!("Success, coerced with AutoDerefRef(1, \
|
||||||
AutoUnsizeUniq({:?}))", kind);
|
AutoUnsizeUniq({:?}))", kind);
|
||||||
Ok(Some(AutoDerefRef(AutoDerefRef {
|
Ok(Some(AutoDerefRef(AutoDerefRef {
|
||||||
|
@ -458,7 +458,7 @@ impl<'f> Coerce<'f> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if_ok!(self.subtype(a_borrowed, b));
|
try!(self.subtype(a_borrowed, b));
|
||||||
Ok(Some(AutoDerefRef(AutoDerefRef {
|
Ok(Some(AutoDerefRef(AutoDerefRef {
|
||||||
autoderefs: 1,
|
autoderefs: 1,
|
||||||
autoref: Some(AutoPtr(r_a, b_mutbl, None))
|
autoref: Some(AutoPtr(r_a, b_mutbl, None))
|
||||||
|
@ -512,7 +512,7 @@ impl<'f> Coerce<'f> {
|
||||||
sig: fn_ty_a.sig.clone(),
|
sig: fn_ty_a.sig.clone(),
|
||||||
.. *fn_ty_b
|
.. *fn_ty_b
|
||||||
});
|
});
|
||||||
if_ok!(self.subtype(a_closure, b));
|
try!(self.subtype(a_closure, b));
|
||||||
Ok(Some(adj))
|
Ok(Some(adj))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -536,7 +536,7 @@ impl<'f> Coerce<'f> {
|
||||||
|
|
||||||
// check that the types which they point at are compatible
|
// check that the types which they point at are compatible
|
||||||
let a_unsafe = ty::mk_ptr(self.get_ref().infcx.tcx, mt_a);
|
let a_unsafe = ty::mk_ptr(self.get_ref().infcx.tcx, mt_a);
|
||||||
if_ok!(self.subtype(a_unsafe, b));
|
try!(self.subtype(a_unsafe, b));
|
||||||
|
|
||||||
// although references and unsafe ptrs have the same
|
// although references and unsafe ptrs have the same
|
||||||
// representation, we still register an AutoDerefRef so that
|
// representation, we still register an AutoDerefRef so that
|
||||||
|
|
|
@ -8,43 +8,29 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
// ______________________________________________________________________
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Type combining
|
// # Type combining
|
||||||
//
|
//
|
||||||
// There are three type combiners: sub, lub, and glb. Each implements
|
// There are four type combiners: equate, sub, lub, and glb. Each
|
||||||
// the trait `Combine` and contains methods for combining two
|
// implements the trait `Combine` and contains methods for combining
|
||||||
// instances of various things and yielding a new instance. These
|
// two instances of various things and yielding a new instance. These
|
||||||
// combiner methods always yield a `result<T>`---failure is propagated
|
// combiner methods always yield a `Result<T>`. There is a lot of
|
||||||
// upward using `and_then()` methods. There is a lot of common code for
|
// common code for these operations, implemented as default methods on
|
||||||
// these operations, implemented as default methods on the `Combine`
|
// the `Combine` trait.
|
||||||
// trait.
|
|
||||||
//
|
//
|
||||||
// In reality, the sub operation is rather different from lub/glb, but
|
// Each operation may have side-effects on the inference context,
|
||||||
// they are combined into one trait to avoid duplication (they used to
|
// though these can be unrolled using snapshots. On success, the
|
||||||
// be separate but there were many bugs because there were two copies
|
// LUB/GLB operations return the appropriate bound. The Eq and Sub
|
||||||
// of most routines).
|
// operations generally return the first operand.
|
||||||
//
|
//
|
||||||
// The differences are:
|
// ## Contravariance
|
||||||
//
|
|
||||||
// - when making two things have a sub relationship, the order of the
|
|
||||||
// arguments is significant (a <: b) and the return value of the
|
|
||||||
// combine functions is largely irrelevant. The important thing is
|
|
||||||
// whether the action succeeds or fails. If it succeeds, then side
|
|
||||||
// effects have been committed into the type variables.
|
|
||||||
//
|
|
||||||
// - for GLB/LUB, the order of arguments is not significant (GLB(a,b) ==
|
|
||||||
// GLB(b,a)) and the return value is important (it is the GLB). Of
|
|
||||||
// course GLB/LUB may also have side effects.
|
|
||||||
//
|
|
||||||
// Contravariance
|
|
||||||
//
|
//
|
||||||
// When you are relating two things which have a contravariant
|
// When you are relating two things which have a contravariant
|
||||||
// relationship, you should use `contratys()` or `contraregions()`,
|
// relationship, you should use `contratys()` or `contraregions()`,
|
||||||
// rather than inversing the order of arguments! This is necessary
|
// rather than inversing the order of arguments! This is necessary
|
||||||
// because the order of arguments is not relevant for LUB and GLB. It
|
// because the order of arguments is not relevant for LUB and GLB. It
|
||||||
// is also useful to track which value is the "expected" value in
|
// is also useful to track which value is the "expected" value in
|
||||||
// terms of error reporting, although we do not do that properly right
|
// terms of error reporting.
|
||||||
// now.
|
|
||||||
|
|
||||||
|
|
||||||
use middle::subst;
|
use middle::subst;
|
||||||
|
@ -53,14 +39,16 @@ use middle::ty::{FloatVar, FnSig, IntVar, TyVar};
|
||||||
use middle::ty::{IntType, UintType};
|
use middle::ty::{IntType, UintType};
|
||||||
use middle::ty::{BuiltinBounds};
|
use middle::ty::{BuiltinBounds};
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use middle::typeck::infer::{ToUres};
|
use middle::typeck::infer::equate::Equate;
|
||||||
use middle::typeck::infer::glb::Glb;
|
use middle::typeck::infer::glb::Glb;
|
||||||
use middle::typeck::infer::lub::Lub;
|
use middle::typeck::infer::lub::Lub;
|
||||||
use middle::typeck::infer::sub::Sub;
|
use middle::typeck::infer::sub::Sub;
|
||||||
use middle::typeck::infer::unify::InferCtxtMethodsForSimplyUnifiableTypes;
|
use middle::typeck::infer::unify::InferCtxtMethodsForSimplyUnifiableTypes;
|
||||||
use middle::typeck::infer::{InferCtxt, cres, ures};
|
use middle::typeck::infer::{InferCtxt, cres};
|
||||||
use middle::typeck::infer::{TypeTrace};
|
use middle::typeck::infer::{MiscVariable, TypeTrace};
|
||||||
use util::common::indent;
|
use middle::typeck::infer::type_variable::{RelationDir, EqTo,
|
||||||
|
SubtypeOf, SupertypeOf};
|
||||||
|
use middle::ty_fold::{RegionFolder, TypeFoldable};
|
||||||
use util::ppaux::Repr;
|
use util::ppaux::Repr;
|
||||||
|
|
||||||
use std::result;
|
use std::result;
|
||||||
|
@ -75,6 +63,7 @@ pub trait Combine {
|
||||||
fn a_is_expected(&self) -> bool;
|
fn a_is_expected(&self) -> bool;
|
||||||
fn trace(&self) -> TypeTrace;
|
fn trace(&self) -> TypeTrace;
|
||||||
|
|
||||||
|
fn equate<'a>(&'a self) -> Equate<'a>;
|
||||||
fn sub<'a>(&'a self) -> Sub<'a>;
|
fn sub<'a>(&'a self) -> Sub<'a>;
|
||||||
fn lub<'a>(&'a self) -> Lub<'a>;
|
fn lub<'a>(&'a self) -> Lub<'a>;
|
||||||
fn glb<'a>(&'a self) -> Glb<'a>;
|
fn glb<'a>(&'a self) -> Glb<'a>;
|
||||||
|
@ -101,7 +90,7 @@ pub trait Combine {
|
||||||
try!(result::fold_(as_
|
try!(result::fold_(as_
|
||||||
.iter()
|
.iter()
|
||||||
.zip(bs.iter())
|
.zip(bs.iter())
|
||||||
.map(|(a, b)| eq_tys(self, *a, *b))));
|
.map(|(a, b)| self.equate().tys(*a, *b))));
|
||||||
Ok(Vec::from_slice(as_))
|
Ok(Vec::from_slice(as_))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +110,7 @@ pub trait Combine {
|
||||||
for &space in subst::ParamSpace::all().iter() {
|
for &space in subst::ParamSpace::all().iter() {
|
||||||
let a_tps = a_subst.types.get_slice(space);
|
let a_tps = a_subst.types.get_slice(space);
|
||||||
let b_tps = b_subst.types.get_slice(space);
|
let b_tps = b_subst.types.get_slice(space);
|
||||||
let tps = if_ok!(self.tps(space, a_tps, b_tps));
|
let tps = try!(self.tps(space, a_tps, b_tps));
|
||||||
|
|
||||||
let a_regions = a_subst.regions().get_slice(space);
|
let a_regions = a_subst.regions().get_slice(space);
|
||||||
let b_regions = b_subst.regions().get_slice(space);
|
let b_regions = b_subst.regions().get_slice(space);
|
||||||
|
@ -137,7 +126,7 @@ pub trait Combine {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let regions = if_ok!(relate_region_params(self,
|
let regions = try!(relate_region_params(self,
|
||||||
item_def_id,
|
item_def_id,
|
||||||
r_variances,
|
r_variances,
|
||||||
a_regions,
|
a_regions,
|
||||||
|
@ -177,15 +166,12 @@ pub trait Combine {
|
||||||
let b_r = b_rs[i];
|
let b_r = b_rs[i];
|
||||||
let variance = variances[i];
|
let variance = variances[i];
|
||||||
let r = match variance {
|
let r = match variance {
|
||||||
ty::Invariant => {
|
ty::Invariant => this.equate().regions(a_r, b_r),
|
||||||
eq_regions(this, a_r, b_r)
|
|
||||||
.and_then(|()| Ok(a_r))
|
|
||||||
}
|
|
||||||
ty::Covariant => this.regions(a_r, b_r),
|
ty::Covariant => this.regions(a_r, b_r),
|
||||||
ty::Contravariant => this.contraregions(a_r, b_r),
|
ty::Contravariant => this.contraregions(a_r, b_r),
|
||||||
ty::Bivariant => Ok(a_r),
|
ty::Bivariant => Ok(a_r),
|
||||||
};
|
};
|
||||||
rs.push(if_ok!(r));
|
rs.push(try!(r));
|
||||||
}
|
}
|
||||||
Ok(rs)
|
Ok(rs)
|
||||||
}
|
}
|
||||||
|
@ -193,9 +179,9 @@ pub trait Combine {
|
||||||
|
|
||||||
fn bare_fn_tys(&self, a: &ty::BareFnTy,
|
fn bare_fn_tys(&self, a: &ty::BareFnTy,
|
||||||
b: &ty::BareFnTy) -> cres<ty::BareFnTy> {
|
b: &ty::BareFnTy) -> cres<ty::BareFnTy> {
|
||||||
let fn_style = if_ok!(self.fn_styles(a.fn_style, b.fn_style));
|
let fn_style = try!(self.fn_styles(a.fn_style, b.fn_style));
|
||||||
let abi = if_ok!(self.abi(a.abi, b.abi));
|
let abi = try!(self.abi(a.abi, b.abi));
|
||||||
let sig = if_ok!(self.fn_sigs(&a.sig, &b.sig));
|
let sig = try!(self.fn_sigs(&a.sig, &b.sig));
|
||||||
Ok(ty::BareFnTy {fn_style: fn_style,
|
Ok(ty::BareFnTy {fn_style: fn_style,
|
||||||
abi: abi,
|
abi: abi,
|
||||||
sig: sig})
|
sig: sig})
|
||||||
|
@ -207,7 +193,7 @@ pub trait Combine {
|
||||||
let store = match (a.store, b.store) {
|
let store = match (a.store, b.store) {
|
||||||
(ty::RegionTraitStore(a_r, a_m),
|
(ty::RegionTraitStore(a_r, a_m),
|
||||||
ty::RegionTraitStore(b_r, b_m)) if a_m == b_m => {
|
ty::RegionTraitStore(b_r, b_m)) if a_m == b_m => {
|
||||||
let r = if_ok!(self.contraregions(a_r, b_r));
|
let r = try!(self.contraregions(a_r, b_r));
|
||||||
ty::RegionTraitStore(r, a_m)
|
ty::RegionTraitStore(r, a_m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,11 +205,11 @@ pub trait Combine {
|
||||||
return Err(ty::terr_sigil_mismatch(expected_found(self, a.store, b.store)))
|
return Err(ty::terr_sigil_mismatch(expected_found(self, a.store, b.store)))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let fn_style = if_ok!(self.fn_styles(a.fn_style, b.fn_style));
|
let fn_style = try!(self.fn_styles(a.fn_style, b.fn_style));
|
||||||
let onceness = if_ok!(self.oncenesses(a.onceness, b.onceness));
|
let onceness = try!(self.oncenesses(a.onceness, b.onceness));
|
||||||
let bounds = if_ok!(self.existential_bounds(a.bounds, b.bounds));
|
let bounds = try!(self.existential_bounds(a.bounds, b.bounds));
|
||||||
let sig = if_ok!(self.fn_sigs(&a.sig, &b.sig));
|
let sig = try!(self.fn_sigs(&a.sig, &b.sig));
|
||||||
let abi = if_ok!(self.abi(a.abi, b.abi));
|
let abi = try!(self.abi(a.abi, b.abi));
|
||||||
Ok(ty::ClosureTy {
|
Ok(ty::ClosureTy {
|
||||||
fn_style: fn_style,
|
fn_style: fn_style,
|
||||||
onceness: onceness,
|
onceness: onceness,
|
||||||
|
@ -311,7 +297,7 @@ pub trait Combine {
|
||||||
Err(ty::terr_traits(
|
Err(ty::terr_traits(
|
||||||
expected_found(self, a.def_id, b.def_id)))
|
expected_found(self, a.def_id, b.def_id)))
|
||||||
} else {
|
} else {
|
||||||
let substs = if_ok!(self.substs(a.def_id, &a.substs, &b.substs));
|
let substs = try!(self.substs(a.def_id, &a.substs, &b.substs));
|
||||||
Ok(ty::TraitRef { def_id: a.def_id,
|
Ok(ty::TraitRef { def_id: a.def_id,
|
||||||
substs: substs })
|
substs: substs })
|
||||||
}
|
}
|
||||||
|
@ -334,34 +320,6 @@ pub fn expected_found<C:Combine,T>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eq_tys<C:Combine>(this: &C, a: ty::t, b: ty::t) -> ures {
|
|
||||||
let suber = this.sub();
|
|
||||||
this.infcx().try(|| {
|
|
||||||
suber.tys(a, b).and_then(|_ok| suber.contratys(a, b)).to_ures()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn eq_regions<C:Combine>(this: &C, a: ty::Region, b: ty::Region)
|
|
||||||
-> ures {
|
|
||||||
debug!("eq_regions({}, {})",
|
|
||||||
a.repr(this.infcx().tcx),
|
|
||||||
b.repr(this.infcx().tcx));
|
|
||||||
let sub = this.sub();
|
|
||||||
indent(|| {
|
|
||||||
this.infcx().try(|| {
|
|
||||||
sub.regions(a, b).and_then(|_r| sub.contraregions(a, b))
|
|
||||||
}).or_else(|e| {
|
|
||||||
// substitute a better error, but use the regions
|
|
||||||
// found in the original error
|
|
||||||
match e {
|
|
||||||
ty::terr_regions_does_not_outlive(a1, b1) =>
|
|
||||||
Err(ty::terr_regions_not_same(a1, b1)),
|
|
||||||
_ => Err(e)
|
|
||||||
}
|
|
||||||
}).to_ures()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn super_fn_sigs<C:Combine>(this: &C, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
|
pub fn super_fn_sigs<C:Combine>(this: &C, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
|
||||||
|
|
||||||
fn argvecs<C:Combine>(this: &C, a_args: &[ty::t], b_args: &[ty::t]) -> cres<Vec<ty::t> > {
|
fn argvecs<C:Combine>(this: &C, a_args: &[ty::t], b_args: &[ty::t]) -> cres<Vec<ty::t> > {
|
||||||
|
@ -377,10 +335,10 @@ pub fn super_fn_sigs<C:Combine>(this: &C, a: &ty::FnSig, b: &ty::FnSig) -> cres<
|
||||||
return Err(ty::terr_variadic_mismatch(expected_found(this, a.variadic, b.variadic)));
|
return Err(ty::terr_variadic_mismatch(expected_found(this, a.variadic, b.variadic)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let inputs = if_ok!(argvecs(this,
|
let inputs = try!(argvecs(this,
|
||||||
a.inputs.as_slice(),
|
a.inputs.as_slice(),
|
||||||
b.inputs.as_slice()));
|
b.inputs.as_slice()));
|
||||||
let output = if_ok!(this.tys(a.output, b.output));
|
let output = try!(this.tys(a.output, b.output));
|
||||||
Ok(FnSig {binder_id: a.binder_id,
|
Ok(FnSig {binder_id: a.binder_id,
|
||||||
inputs: inputs,
|
inputs: inputs,
|
||||||
output: output,
|
output: output,
|
||||||
|
@ -430,7 +388,7 @@ pub fn super_tys<C:Combine>(this: &C, a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||||
|
|
||||||
// Relate integral variables to other types
|
// Relate integral variables to other types
|
||||||
(&ty::ty_infer(IntVar(a_id)), &ty::ty_infer(IntVar(b_id))) => {
|
(&ty::ty_infer(IntVar(a_id)), &ty::ty_infer(IntVar(b_id))) => {
|
||||||
if_ok!(this.infcx().simple_vars(this.a_is_expected(),
|
try!(this.infcx().simple_vars(this.a_is_expected(),
|
||||||
a_id, b_id));
|
a_id, b_id));
|
||||||
Ok(a)
|
Ok(a)
|
||||||
}
|
}
|
||||||
|
@ -453,8 +411,7 @@ pub fn super_tys<C:Combine>(this: &C, a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||||
|
|
||||||
// Relate floating-point variables to other types
|
// Relate floating-point variables to other types
|
||||||
(&ty::ty_infer(FloatVar(a_id)), &ty::ty_infer(FloatVar(b_id))) => {
|
(&ty::ty_infer(FloatVar(a_id)), &ty::ty_infer(FloatVar(b_id))) => {
|
||||||
if_ok!(this.infcx().simple_vars(this.a_is_expected(),
|
try!(this.infcx().simple_vars(this.a_is_expected(), a_id, b_id));
|
||||||
a_id, b_id));
|
|
||||||
Ok(a)
|
Ok(a)
|
||||||
}
|
}
|
||||||
(&ty::ty_infer(FloatVar(v_id)), &ty::ty_float(v)) => {
|
(&ty::ty_infer(FloatVar(v_id)), &ty::ty_float(v)) => {
|
||||||
|
@ -469,7 +426,8 @@ pub fn super_tys<C:Combine>(this: &C, a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||||
(&ty::ty_bool, _) |
|
(&ty::ty_bool, _) |
|
||||||
(&ty::ty_int(_), _) |
|
(&ty::ty_int(_), _) |
|
||||||
(&ty::ty_uint(_), _) |
|
(&ty::ty_uint(_), _) |
|
||||||
(&ty::ty_float(_), _) => {
|
(&ty::ty_float(_), _) |
|
||||||
|
(&ty::ty_err, _) => {
|
||||||
if ty::get(a).sty == ty::get(b).sty {
|
if ty::get(a).sty == ty::get(b).sty {
|
||||||
Ok(a)
|
Ok(a)
|
||||||
} else {
|
} else {
|
||||||
|
@ -485,7 +443,7 @@ pub fn super_tys<C:Combine>(this: &C, a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||||
(&ty::ty_enum(a_id, ref a_substs),
|
(&ty::ty_enum(a_id, ref a_substs),
|
||||||
&ty::ty_enum(b_id, ref b_substs))
|
&ty::ty_enum(b_id, ref b_substs))
|
||||||
if a_id == b_id => {
|
if a_id == b_id => {
|
||||||
let substs = if_ok!(this.substs(a_id,
|
let substs = try!(this.substs(a_id,
|
||||||
a_substs,
|
a_substs,
|
||||||
b_substs));
|
b_substs));
|
||||||
Ok(ty::mk_enum(tcx, a_id, substs))
|
Ok(ty::mk_enum(tcx, a_id, substs))
|
||||||
|
@ -495,8 +453,8 @@ pub fn super_tys<C:Combine>(this: &C, a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||||
&ty::ty_trait(ref b_))
|
&ty::ty_trait(ref b_))
|
||||||
if a_.def_id == b_.def_id => {
|
if a_.def_id == b_.def_id => {
|
||||||
debug!("Trying to match traits {:?} and {:?}", a, b);
|
debug!("Trying to match traits {:?} and {:?}", a, b);
|
||||||
let substs = if_ok!(this.substs(a_.def_id, &a_.substs, &b_.substs));
|
let substs = try!(this.substs(a_.def_id, &a_.substs, &b_.substs));
|
||||||
let bounds = if_ok!(this.existential_bounds(a_.bounds, b_.bounds));
|
let bounds = try!(this.existential_bounds(a_.bounds, b_.bounds));
|
||||||
Ok(ty::mk_trait(tcx,
|
Ok(ty::mk_trait(tcx,
|
||||||
a_.def_id,
|
a_.def_id,
|
||||||
substs.clone(),
|
substs.clone(),
|
||||||
|
@ -505,14 +463,17 @@ pub fn super_tys<C:Combine>(this: &C, a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||||
|
|
||||||
(&ty::ty_struct(a_id, ref a_substs), &ty::ty_struct(b_id, ref b_substs))
|
(&ty::ty_struct(a_id, ref a_substs), &ty::ty_struct(b_id, ref b_substs))
|
||||||
if a_id == b_id => {
|
if a_id == b_id => {
|
||||||
let substs = if_ok!(this.substs(a_id, a_substs, b_substs));
|
let substs = try!(this.substs(a_id, a_substs, b_substs));
|
||||||
Ok(ty::mk_struct(tcx, a_id, substs))
|
Ok(ty::mk_struct(tcx, a_id, substs))
|
||||||
}
|
}
|
||||||
|
|
||||||
(&ty::ty_unboxed_closure(a_id, a_region),
|
(&ty::ty_unboxed_closure(a_id, a_region),
|
||||||
&ty::ty_unboxed_closure(b_id, b_region))
|
&ty::ty_unboxed_closure(b_id, b_region))
|
||||||
if a_id == b_id => {
|
if a_id == b_id => {
|
||||||
let region = if_ok!(this.regions(a_region, b_region));
|
// All ty_unboxed_closure types with the same id represent
|
||||||
|
// the (anonymous) type of the same closure expression. So
|
||||||
|
// all of their regions should be equated.
|
||||||
|
let region = try!(this.equate().regions(a_region, b_region));
|
||||||
Ok(ty::mk_unboxed_closure(tcx, a_id, region))
|
Ok(ty::mk_unboxed_closure(tcx, a_id, region))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,27 +482,27 @@ pub fn super_tys<C:Combine>(this: &C, a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||||
}
|
}
|
||||||
|
|
||||||
(&ty::ty_uniq(a_inner), &ty::ty_uniq(b_inner)) => {
|
(&ty::ty_uniq(a_inner), &ty::ty_uniq(b_inner)) => {
|
||||||
let typ = if_ok!(this.tys(a_inner, b_inner));
|
let typ = try!(this.tys(a_inner, b_inner));
|
||||||
check_ptr_to_unsized(this, a, b, a_inner, b_inner, ty::mk_uniq(tcx, typ))
|
check_ptr_to_unsized(this, a, b, a_inner, b_inner, ty::mk_uniq(tcx, typ))
|
||||||
}
|
}
|
||||||
|
|
||||||
(&ty::ty_ptr(ref a_mt), &ty::ty_ptr(ref b_mt)) => {
|
(&ty::ty_ptr(ref a_mt), &ty::ty_ptr(ref b_mt)) => {
|
||||||
let mt = if_ok!(this.mts(a_mt, b_mt));
|
let mt = try!(this.mts(a_mt, b_mt));
|
||||||
check_ptr_to_unsized(this, a, b, a_mt.ty, b_mt.ty, ty::mk_ptr(tcx, mt))
|
check_ptr_to_unsized(this, a, b, a_mt.ty, b_mt.ty, ty::mk_ptr(tcx, mt))
|
||||||
}
|
}
|
||||||
|
|
||||||
(&ty::ty_rptr(a_r, ref a_mt), &ty::ty_rptr(b_r, ref b_mt)) => {
|
(&ty::ty_rptr(a_r, ref a_mt), &ty::ty_rptr(b_r, ref b_mt)) => {
|
||||||
let r = if_ok!(this.contraregions(a_r, b_r));
|
let r = try!(this.contraregions(a_r, b_r));
|
||||||
// FIXME(14985) If we have mutable references to trait objects, we
|
// FIXME(14985) If we have mutable references to trait objects, we
|
||||||
// used to use covariant subtyping. I have preserved this behaviour,
|
// used to use covariant subtyping. I have preserved this behaviour,
|
||||||
// even though it is probably incorrect. So don't go down the usual
|
// even though it is probably incorrect. So don't go down the usual
|
||||||
// path which would require invariance.
|
// path which would require invariance.
|
||||||
let mt = match (&ty::get(a_mt.ty).sty, &ty::get(b_mt.ty).sty) {
|
let mt = match (&ty::get(a_mt.ty).sty, &ty::get(b_mt.ty).sty) {
|
||||||
(&ty::ty_trait(..), &ty::ty_trait(..)) if a_mt.mutbl == b_mt.mutbl => {
|
(&ty::ty_trait(..), &ty::ty_trait(..)) if a_mt.mutbl == b_mt.mutbl => {
|
||||||
let ty = if_ok!(this.tys(a_mt.ty, b_mt.ty));
|
let ty = try!(this.tys(a_mt.ty, b_mt.ty));
|
||||||
ty::mt { ty: ty, mutbl: a_mt.mutbl }
|
ty::mt { ty: ty, mutbl: a_mt.mutbl }
|
||||||
}
|
}
|
||||||
_ => if_ok!(this.mts(a_mt, b_mt))
|
_ => try!(this.mts(a_mt, b_mt))
|
||||||
};
|
};
|
||||||
check_ptr_to_unsized(this, a, b, a_mt.ty, b_mt.ty, ty::mk_rptr(tcx, r, mt))
|
check_ptr_to_unsized(this, a, b, a_mt.ty, b_mt.ty, ty::mk_rptr(tcx, r, mt))
|
||||||
}
|
}
|
||||||
|
@ -592,7 +553,7 @@ pub fn super_tys<C:Combine>(this: &C, a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||||
vid: ty::IntVid,
|
vid: ty::IntVid,
|
||||||
val: ty::IntVarValue) -> cres<ty::t>
|
val: ty::IntVarValue) -> cres<ty::t>
|
||||||
{
|
{
|
||||||
if_ok!(this.infcx().simple_var_t(vid_is_expected, vid, val));
|
try!(this.infcx().simple_var_t(vid_is_expected, vid, val));
|
||||||
match val {
|
match val {
|
||||||
IntType(v) => Ok(ty::mk_mach_int(v)),
|
IntType(v) => Ok(ty::mk_mach_int(v)),
|
||||||
UintType(v) => Ok(ty::mk_mach_uint(v))
|
UintType(v) => Ok(ty::mk_mach_uint(v))
|
||||||
|
@ -605,7 +566,122 @@ pub fn super_tys<C:Combine>(this: &C, a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||||
vid: ty::FloatVid,
|
vid: ty::FloatVid,
|
||||||
val: ast::FloatTy) -> cres<ty::t>
|
val: ast::FloatTy) -> cres<ty::t>
|
||||||
{
|
{
|
||||||
if_ok!(this.infcx().simple_var_t(vid_is_expected, vid, val));
|
try!(this.infcx().simple_var_t(vid_is_expected, vid, val));
|
||||||
Ok(ty::mk_mach_float(val))
|
Ok(ty::mk_mach_float(val))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'f> CombineFields<'f> {
|
||||||
|
pub fn switch_expected(&self) -> CombineFields<'f> {
|
||||||
|
CombineFields {
|
||||||
|
a_is_expected: !self.a_is_expected,
|
||||||
|
..(*self).clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn equate(&self) -> Equate<'f> {
|
||||||
|
Equate((*self).clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub(&self) -> Sub<'f> {
|
||||||
|
Sub((*self).clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn instantiate(&self,
|
||||||
|
a_ty: ty::t,
|
||||||
|
dir: RelationDir,
|
||||||
|
b_vid: ty::TyVid)
|
||||||
|
-> cres<()>
|
||||||
|
{
|
||||||
|
let tcx = self.infcx.tcx;
|
||||||
|
let mut stack = Vec::new();
|
||||||
|
stack.push((a_ty, dir, b_vid));
|
||||||
|
loop {
|
||||||
|
// For each turn of the loop, we extract a tuple
|
||||||
|
//
|
||||||
|
// (a_ty, dir, b_vid)
|
||||||
|
//
|
||||||
|
// to relate. Here dir is either SubtypeOf or
|
||||||
|
// SupertypeOf. The idea is that we should ensure that
|
||||||
|
// the type `a_ty` is a subtype or supertype (respectively) of the
|
||||||
|
// type to which `b_vid` is bound.
|
||||||
|
//
|
||||||
|
// If `b_vid` has not yet been instantiated with a type
|
||||||
|
// (which is always true on the first iteration, but not
|
||||||
|
// necessarily true on later iterations), we will first
|
||||||
|
// instantiate `b_vid` with a *generalized* version of
|
||||||
|
// `a_ty`. Generalization introduces other inference
|
||||||
|
// variables wherever subtyping could occur (at time of
|
||||||
|
// this writing, this means replacing free regions with
|
||||||
|
// region variables).
|
||||||
|
let (a_ty, dir, b_vid) = match stack.pop() {
|
||||||
|
None => break,
|
||||||
|
Some(e) => e,
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!("instantiate(a_ty={} dir={} b_vid={})",
|
||||||
|
a_ty.repr(tcx),
|
||||||
|
dir,
|
||||||
|
b_vid.repr(tcx));
|
||||||
|
|
||||||
|
// Check whether `vid` has been instantiated yet. If not,
|
||||||
|
// make a generalized form of `ty` and instantiate with
|
||||||
|
// that.
|
||||||
|
let b_ty = self.infcx.type_variables.borrow().probe(b_vid);
|
||||||
|
let b_ty = match b_ty {
|
||||||
|
Some(t) => t, // ...already instantiated.
|
||||||
|
None => { // ...not yet instantiated:
|
||||||
|
// Generalize type if necessary.
|
||||||
|
let generalized_ty = match dir {
|
||||||
|
EqTo => a_ty,
|
||||||
|
SupertypeOf | SubtypeOf => self.generalize(a_ty)
|
||||||
|
};
|
||||||
|
debug!("instantiate(a_ty={}, dir={}, \
|
||||||
|
b_vid={}, generalized_ty={})",
|
||||||
|
a_ty.repr(tcx), dir, b_vid.repr(tcx),
|
||||||
|
generalized_ty.repr(tcx));
|
||||||
|
self.infcx.type_variables
|
||||||
|
.borrow_mut()
|
||||||
|
.instantiate_and_push(
|
||||||
|
b_vid, generalized_ty, &mut stack);
|
||||||
|
generalized_ty
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The original triple was `(a_ty, dir, b_vid)` -- now we have
|
||||||
|
// resolved `b_vid` to `b_ty`, so apply `(a_ty, dir, b_ty)`:
|
||||||
|
//
|
||||||
|
// FIXME(#16847): This code is non-ideal because all these subtype
|
||||||
|
// relations wind up attributed to the same spans. We need
|
||||||
|
// to associate causes/spans with each of the relations in
|
||||||
|
// the stack to get this right.
|
||||||
|
match dir {
|
||||||
|
EqTo => {
|
||||||
|
try!(self.equate().tys(a_ty, b_ty));
|
||||||
|
}
|
||||||
|
|
||||||
|
SubtypeOf => {
|
||||||
|
try!(self.sub().tys(a_ty, b_ty));
|
||||||
|
}
|
||||||
|
|
||||||
|
SupertypeOf => {
|
||||||
|
try!(self.sub().contratys(a_ty, b_ty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generalize(&self, t: ty::t) -> ty::t {
|
||||||
|
// FIXME(#16847): This is non-ideal because we don't give a
|
||||||
|
// very descriptive origin for this region variable.
|
||||||
|
|
||||||
|
let infcx = self.infcx;
|
||||||
|
let span = self.trace.origin.span();
|
||||||
|
t.fold_with(
|
||||||
|
&mut RegionFolder::regions(
|
||||||
|
self.infcx.tcx,
|
||||||
|
|_| infcx.next_region_var(MiscVariable(span))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
// Copyright 2014 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::{BuiltinBounds};
|
||||||
|
use middle::ty;
|
||||||
|
use middle::ty::TyVar;
|
||||||
|
use middle::typeck::infer::combine::*;
|
||||||
|
use middle::typeck::infer::{cres};
|
||||||
|
use middle::typeck::infer::glb::Glb;
|
||||||
|
use middle::typeck::infer::InferCtxt;
|
||||||
|
use middle::typeck::infer::lub::Lub;
|
||||||
|
use middle::typeck::infer::sub::Sub;
|
||||||
|
use middle::typeck::infer::{TypeTrace, Subtype};
|
||||||
|
use middle::typeck::infer::type_variable::{EqTo};
|
||||||
|
use util::ppaux::{Repr};
|
||||||
|
|
||||||
|
use syntax::ast::{Onceness, FnStyle};
|
||||||
|
|
||||||
|
pub struct Equate<'f> {
|
||||||
|
fields: CombineFields<'f>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case_functions)]
|
||||||
|
pub fn Equate<'f>(cf: CombineFields<'f>) -> Equate<'f> {
|
||||||
|
Equate { fields: cf }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'f> Combine for Equate<'f> {
|
||||||
|
fn infcx<'a>(&'a self) -> &'a InferCtxt<'a> { self.fields.infcx }
|
||||||
|
fn tag(&self) -> String { "eq".to_string() }
|
||||||
|
fn a_is_expected(&self) -> bool { self.fields.a_is_expected }
|
||||||
|
fn trace(&self) -> TypeTrace { self.fields.trace.clone() }
|
||||||
|
|
||||||
|
fn equate<'a>(&'a self) -> Equate<'a> { Equate(self.fields.clone()) }
|
||||||
|
fn sub<'a>(&'a self) -> Sub<'a> { Sub(self.fields.clone()) }
|
||||||
|
fn lub<'a>(&'a self) -> Lub<'a> { Lub(self.fields.clone()) }
|
||||||
|
fn glb<'a>(&'a self) -> Glb<'a> { Glb(self.fields.clone()) }
|
||||||
|
|
||||||
|
fn contratys(&self, a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||||
|
self.tys(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contraregions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
|
||||||
|
self.regions(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
|
||||||
|
debug!("{}.regions({}, {})",
|
||||||
|
self.tag(),
|
||||||
|
a.repr(self.fields.infcx.tcx),
|
||||||
|
b.repr(self.fields.infcx.tcx));
|
||||||
|
self.infcx().region_vars.make_eqregion(Subtype(self.trace()), a, b);
|
||||||
|
Ok(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mts(&self, a: &ty::mt, b: &ty::mt) -> cres<ty::mt> {
|
||||||
|
debug!("mts({} <: {})",
|
||||||
|
a.repr(self.fields.infcx.tcx),
|
||||||
|
b.repr(self.fields.infcx.tcx));
|
||||||
|
|
||||||
|
if a.mutbl != b.mutbl { return Err(ty::terr_mutability); }
|
||||||
|
let t = try!(self.tys(a.ty, b.ty));
|
||||||
|
Ok(ty::mt { mutbl: a.mutbl, ty: t })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn_styles(&self, a: FnStyle, b: FnStyle) -> cres<FnStyle> {
|
||||||
|
if a != b {
|
||||||
|
Err(ty::terr_fn_style_mismatch(expected_found(self, a, b)))
|
||||||
|
} else {
|
||||||
|
Ok(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn oncenesses(&self, a: Onceness, b: Onceness) -> cres<Onceness> {
|
||||||
|
if a != b {
|
||||||
|
Err(ty::terr_onceness_mismatch(expected_found(self, a, b)))
|
||||||
|
} else {
|
||||||
|
Ok(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn builtin_bounds(&self,
|
||||||
|
a: BuiltinBounds,
|
||||||
|
b: BuiltinBounds)
|
||||||
|
-> cres<BuiltinBounds>
|
||||||
|
{
|
||||||
|
// More bounds is a subtype of fewer bounds.
|
||||||
|
//
|
||||||
|
// e.g., fn:Copy() <: fn(), because the former is a function
|
||||||
|
// that only closes over copyable things, but the latter is
|
||||||
|
// any function at all.
|
||||||
|
if a != b {
|
||||||
|
Err(ty::terr_builtin_bounds(expected_found(self, a, b)))
|
||||||
|
} else {
|
||||||
|
Ok(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tys(&self, a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||||
|
debug!("{}.tys({}, {})", self.tag(),
|
||||||
|
a.repr(self.fields.infcx.tcx), b.repr(self.fields.infcx.tcx));
|
||||||
|
if a == b { return Ok(a); }
|
||||||
|
|
||||||
|
let infcx = self.fields.infcx;
|
||||||
|
let a = infcx.type_variables.borrow().replace_if_possible(a);
|
||||||
|
let b = infcx.type_variables.borrow().replace_if_possible(b);
|
||||||
|
match (&ty::get(a).sty, &ty::get(b).sty) {
|
||||||
|
(&ty::ty_bot, &ty::ty_bot) => {
|
||||||
|
Ok(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
(&ty::ty_bot, _) |
|
||||||
|
(_, &ty::ty_bot) => {
|
||||||
|
Err(ty::terr_sorts(expected_found(self, a, b)))
|
||||||
|
}
|
||||||
|
|
||||||
|
(&ty::ty_infer(TyVar(a_id)), &ty::ty_infer(TyVar(b_id))) => {
|
||||||
|
infcx.type_variables.borrow_mut().relate_vars(a_id, EqTo, b_id);
|
||||||
|
Ok(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
(&ty::ty_infer(TyVar(a_id)), _) => {
|
||||||
|
try!(self.fields.instantiate(b, EqTo, a_id));
|
||||||
|
Ok(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
(_, &ty::ty_infer(TyVar(b_id))) => {
|
||||||
|
try!(self.fields.instantiate(a, EqTo, b_id));
|
||||||
|
Ok(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
super_tys(self, a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
|
||||||
|
try!(self.sub().fn_sigs(a, b));
|
||||||
|
self.sub().fn_sigs(b, a)
|
||||||
|
}
|
||||||
|
}
|
|
@ -583,6 +583,19 @@ impl<'a> ErrorReporting for InferCtxt<'a> {
|
||||||
sub,
|
sub,
|
||||||
"");
|
"");
|
||||||
}
|
}
|
||||||
|
infer::ProcCapture(span, id) => {
|
||||||
|
self.tcx.sess.span_err(
|
||||||
|
span,
|
||||||
|
format!("captured variable `{}` must be 'static \
|
||||||
|
to be captured in a proc",
|
||||||
|
ty::local_var_name_str(self.tcx, id).get())
|
||||||
|
.as_slice());
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
"captured variable is only valid for ",
|
||||||
|
sup,
|
||||||
|
"");
|
||||||
|
}
|
||||||
infer::IndexSlice(span) => {
|
infer::IndexSlice(span) => {
|
||||||
self.tcx.sess.span_err(span,
|
self.tcx.sess.span_err(span,
|
||||||
"index of slice outside its lifetime");
|
"index of slice outside its lifetime");
|
||||||
|
@ -1423,11 +1436,11 @@ impl<'a> ErrorReportingHelpers for InferCtxt<'a> {
|
||||||
bound_region_to_string(self.tcx, "lifetime parameter ", true, br))
|
bound_region_to_string(self.tcx, "lifetime parameter ", true, br))
|
||||||
}
|
}
|
||||||
infer::EarlyBoundRegion(_, name) => {
|
infer::EarlyBoundRegion(_, name) => {
|
||||||
format!(" for lifetime parameter `{}",
|
format!(" for lifetime parameter `{}`",
|
||||||
token::get_name(name).get())
|
token::get_name(name).get())
|
||||||
}
|
}
|
||||||
infer::BoundRegionInCoherence(name) => {
|
infer::BoundRegionInCoherence(name) => {
|
||||||
format!(" for lifetime parameter `{} in coherence check",
|
format!(" for lifetime parameter `{}` in coherence check",
|
||||||
token::get_name(name).get())
|
token::get_name(name).get())
|
||||||
}
|
}
|
||||||
infer::UpvarRegion(ref upvar_id, _) => {
|
infer::UpvarRegion(ref upvar_id, _) => {
|
||||||
|
@ -1528,6 +1541,15 @@ impl<'a> ErrorReportingHelpers for InferCtxt<'a> {
|
||||||
self.tcx,
|
self.tcx,
|
||||||
id).get().to_string()).as_slice());
|
id).get().to_string()).as_slice());
|
||||||
}
|
}
|
||||||
|
infer::ProcCapture(span, id) => {
|
||||||
|
self.tcx.sess.span_note(
|
||||||
|
span,
|
||||||
|
format!("...so that captured variable `{}` \
|
||||||
|
is 'static",
|
||||||
|
ty::local_var_name_str(
|
||||||
|
self.tcx,
|
||||||
|
id).get()).as_slice());
|
||||||
|
}
|
||||||
infer::IndexSlice(span) => {
|
infer::IndexSlice(span) => {
|
||||||
self.tcx.sess.span_note(
|
self.tcx.sess.span_note(
|
||||||
span,
|
span,
|
||||||
|
@ -1571,8 +1593,8 @@ impl<'a> ErrorReportingHelpers for InferCtxt<'a> {
|
||||||
infer::AutoBorrow(span) => {
|
infer::AutoBorrow(span) => {
|
||||||
self.tcx.sess.span_note(
|
self.tcx.sess.span_note(
|
||||||
span,
|
span,
|
||||||
"...so that reference is valid \
|
"...so that auto-reference is valid \
|
||||||
at the time of implicit borrow");
|
at the time of borrow");
|
||||||
}
|
}
|
||||||
infer::ExprTypeIsNotInScope(t, span) => {
|
infer::ExprTypeIsNotInScope(t, span) => {
|
||||||
self.tcx.sess.span_note(
|
self.tcx.sess.span_note(
|
||||||
|
|
|
@ -12,9 +12,9 @@
|
||||||
use middle::ty::{BuiltinBounds};
|
use middle::ty::{BuiltinBounds};
|
||||||
use middle::ty::RegionVid;
|
use middle::ty::RegionVid;
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use middle::typeck::infer::then;
|
|
||||||
use middle::typeck::infer::combine::*;
|
use middle::typeck::infer::combine::*;
|
||||||
use middle::typeck::infer::lattice::*;
|
use middle::typeck::infer::lattice::*;
|
||||||
|
use middle::typeck::infer::equate::Equate;
|
||||||
use middle::typeck::infer::lub::Lub;
|
use middle::typeck::infer::lub::Lub;
|
||||||
use middle::typeck::infer::sub::Sub;
|
use middle::typeck::infer::sub::Sub;
|
||||||
use middle::typeck::infer::{cres, InferCtxt};
|
use middle::typeck::infer::{cres, InferCtxt};
|
||||||
|
@ -29,24 +29,29 @@ use util::common::{indenter};
|
||||||
use util::ppaux::mt_to_string;
|
use util::ppaux::mt_to_string;
|
||||||
use util::ppaux::Repr;
|
use util::ppaux::Repr;
|
||||||
|
|
||||||
pub struct Glb<'f>(pub CombineFields<'f>); // "greatest lower bound" (common subtype)
|
/// "Greatest lower bound" (common subtype)
|
||||||
|
pub struct Glb<'f> {
|
||||||
|
fields: CombineFields<'f>
|
||||||
|
}
|
||||||
|
|
||||||
impl<'f> Glb<'f> {
|
#[allow(non_snake_case_functions)]
|
||||||
pub fn get_ref<'a>(&'a self) -> &'a CombineFields<'f> { let Glb(ref v) = *self; v }
|
pub fn Glb<'f>(cf: CombineFields<'f>) -> Glb<'f> {
|
||||||
|
Glb { fields: cf }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'f> Combine for Glb<'f> {
|
impl<'f> Combine for Glb<'f> {
|
||||||
fn infcx<'a>(&'a self) -> &'a InferCtxt<'a> { self.get_ref().infcx }
|
fn infcx<'a>(&'a self) -> &'a InferCtxt<'a> { self.fields.infcx }
|
||||||
fn tag(&self) -> String { "glb".to_string() }
|
fn tag(&self) -> String { "glb".to_string() }
|
||||||
fn a_is_expected(&self) -> bool { self.get_ref().a_is_expected }
|
fn a_is_expected(&self) -> bool { self.fields.a_is_expected }
|
||||||
fn trace(&self) -> TypeTrace { self.get_ref().trace.clone() }
|
fn trace(&self) -> TypeTrace { self.fields.trace.clone() }
|
||||||
|
|
||||||
fn sub<'a>(&'a self) -> Sub<'a> { Sub(self.get_ref().clone()) }
|
fn equate<'a>(&'a self) -> Equate<'a> { Equate(self.fields.clone()) }
|
||||||
fn lub<'a>(&'a self) -> Lub<'a> { Lub(self.get_ref().clone()) }
|
fn sub<'a>(&'a self) -> Sub<'a> { Sub(self.fields.clone()) }
|
||||||
fn glb<'a>(&'a self) -> Glb<'a> { Glb(self.get_ref().clone()) }
|
fn lub<'a>(&'a self) -> Lub<'a> { Lub(self.fields.clone()) }
|
||||||
|
fn glb<'a>(&'a self) -> Glb<'a> { Glb(self.fields.clone()) }
|
||||||
|
|
||||||
fn mts(&self, a: &ty::mt, b: &ty::mt) -> cres<ty::mt> {
|
fn mts(&self, a: &ty::mt, b: &ty::mt) -> cres<ty::mt> {
|
||||||
let tcx = self.get_ref().infcx.tcx;
|
let tcx = self.fields.infcx.tcx;
|
||||||
|
|
||||||
debug!("{}.mts({}, {})",
|
debug!("{}.mts({}, {})",
|
||||||
self.tag(),
|
self.tag(),
|
||||||
|
@ -57,17 +62,15 @@ impl<'f> Combine for Glb<'f> {
|
||||||
// If one side or both is mut, then the GLB must use
|
// If one side or both is mut, then the GLB must use
|
||||||
// the precise type from the mut side.
|
// the precise type from the mut side.
|
||||||
(MutMutable, MutMutable) => {
|
(MutMutable, MutMutable) => {
|
||||||
eq_tys(self, a.ty, b.ty).then(|| {
|
let t = try!(self.equate().tys(a.ty, b.ty));
|
||||||
Ok(ty::mt {ty: a.ty, mutbl: MutMutable})
|
Ok(ty::mt {ty: t, mutbl: MutMutable})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If one side or both is immutable, we can use the GLB of
|
// If one side or both is immutable, we can use the GLB of
|
||||||
// both sides but mutbl must be `MutImmutable`.
|
// both sides but mutbl must be `MutImmutable`.
|
||||||
(MutImmutable, MutImmutable) => {
|
(MutImmutable, MutImmutable) => {
|
||||||
self.tys(a.ty, b.ty).and_then(|t| {
|
let t = try!(self.tys(a.ty, b.ty));
|
||||||
Ok(ty::mt {ty: t, mutbl: MutImmutable})
|
Ok(ty::mt {ty: t, mutbl: MutImmutable})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// There is no mutual subtype of these combinations.
|
// There is no mutual subtype of these combinations.
|
||||||
|
@ -108,10 +111,10 @@ impl<'f> Combine for Glb<'f> {
|
||||||
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
|
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
|
||||||
debug!("{}.regions({:?}, {:?})",
|
debug!("{}.regions({:?}, {:?})",
|
||||||
self.tag(),
|
self.tag(),
|
||||||
a.repr(self.get_ref().infcx.tcx),
|
a.repr(self.fields.infcx.tcx),
|
||||||
b.repr(self.get_ref().infcx.tcx));
|
b.repr(self.fields.infcx.tcx));
|
||||||
|
|
||||||
Ok(self.get_ref().infcx.region_vars.glb_regions(Subtype(self.trace()), a, b))
|
Ok(self.fields.infcx.region_vars.glb_regions(Subtype(self.trace()), a, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contraregions(&self, a: ty::Region, b: ty::Region)
|
fn contraregions(&self, a: ty::Region, b: ty::Region)
|
||||||
|
@ -128,33 +131,33 @@ impl<'f> Combine for Glb<'f> {
|
||||||
// please see the large comment in `region_inference.rs`.
|
// please see the large comment in `region_inference.rs`.
|
||||||
|
|
||||||
debug!("{}.fn_sigs({:?}, {:?})",
|
debug!("{}.fn_sigs({:?}, {:?})",
|
||||||
self.tag(), a.repr(self.get_ref().infcx.tcx), b.repr(self.get_ref().infcx.tcx));
|
self.tag(), a.repr(self.fields.infcx.tcx), b.repr(self.fields.infcx.tcx));
|
||||||
let _indenter = indenter();
|
let _indenter = indenter();
|
||||||
|
|
||||||
// Make a mark so we can examine "all bindings that were
|
// Make a mark so we can examine "all bindings that were
|
||||||
// created as part of this type comparison".
|
// created as part of this type comparison".
|
||||||
let mark = self.get_ref().infcx.region_vars.mark();
|
let mark = self.fields.infcx.region_vars.mark();
|
||||||
|
|
||||||
// Instantiate each bound region with a fresh region variable.
|
// Instantiate each bound region with a fresh region variable.
|
||||||
let (a_with_fresh, a_map) =
|
let (a_with_fresh, a_map) =
|
||||||
self.get_ref().infcx.replace_late_bound_regions_with_fresh_regions(
|
self.fields.infcx.replace_late_bound_regions_with_fresh_regions(
|
||||||
self.trace(), a);
|
self.trace(), a);
|
||||||
let a_vars = var_ids(self, &a_map);
|
let a_vars = var_ids(self, &a_map);
|
||||||
let (b_with_fresh, b_map) =
|
let (b_with_fresh, b_map) =
|
||||||
self.get_ref().infcx.replace_late_bound_regions_with_fresh_regions(
|
self.fields.infcx.replace_late_bound_regions_with_fresh_regions(
|
||||||
self.trace(), b);
|
self.trace(), b);
|
||||||
let b_vars = var_ids(self, &b_map);
|
let b_vars = var_ids(self, &b_map);
|
||||||
|
|
||||||
// Collect constraints.
|
// Collect constraints.
|
||||||
let sig0 = if_ok!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh));
|
let sig0 = try!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh));
|
||||||
debug!("sig0 = {}", sig0.repr(self.get_ref().infcx.tcx));
|
debug!("sig0 = {}", sig0.repr(self.fields.infcx.tcx));
|
||||||
|
|
||||||
// Generalize the regions appearing in fn_ty0 if possible
|
// Generalize the regions appearing in fn_ty0 if possible
|
||||||
let new_vars =
|
let new_vars =
|
||||||
self.get_ref().infcx.region_vars.vars_created_since_mark(mark);
|
self.fields.infcx.region_vars.vars_created_since_mark(mark);
|
||||||
let sig1 =
|
let sig1 =
|
||||||
fold_regions_in_sig(
|
fold_regions_in_sig(
|
||||||
self.get_ref().infcx.tcx,
|
self.fields.infcx.tcx,
|
||||||
&sig0,
|
&sig0,
|
||||||
|r| {
|
|r| {
|
||||||
generalize_region(self,
|
generalize_region(self,
|
||||||
|
@ -166,7 +169,7 @@ impl<'f> Combine for Glb<'f> {
|
||||||
b_vars.as_slice(),
|
b_vars.as_slice(),
|
||||||
r)
|
r)
|
||||||
});
|
});
|
||||||
debug!("sig1 = {}", sig1.repr(self.get_ref().infcx.tcx));
|
debug!("sig1 = {}", sig1.repr(self.fields.infcx.tcx));
|
||||||
return Ok(sig1);
|
return Ok(sig1);
|
||||||
|
|
||||||
fn generalize_region(this: &Glb,
|
fn generalize_region(this: &Glb,
|
||||||
|
@ -182,7 +185,7 @@ impl<'f> Combine for Glb<'f> {
|
||||||
return r0;
|
return r0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let tainted = this.get_ref().infcx.region_vars.tainted(mark, r0);
|
let tainted = this.fields.infcx.region_vars.tainted(mark, r0);
|
||||||
|
|
||||||
let mut a_r = None;
|
let mut a_r = None;
|
||||||
let mut b_r = None;
|
let mut b_r = None;
|
||||||
|
@ -249,14 +252,14 @@ impl<'f> Combine for Glb<'f> {
|
||||||
return ty::ReLateBound(new_binder_id, *a_br);
|
return ty::ReLateBound(new_binder_id, *a_br);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.get_ref().infcx.tcx.sess.span_bug(
|
this.fields.infcx.tcx.sess.span_bug(
|
||||||
this.get_ref().trace.origin.span(),
|
this.fields.trace.origin.span(),
|
||||||
format!("could not find original bound region for {:?}",
|
format!("could not find original bound region for {:?}",
|
||||||
r).as_slice())
|
r).as_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fresh_bound_variable(this: &Glb, binder_id: NodeId) -> ty::Region {
|
fn fresh_bound_variable(this: &Glb, binder_id: NodeId) -> ty::Region {
|
||||||
this.get_ref().infcx.region_vars.new_bound(binder_id)
|
this.fields.infcx.region_vars.new_bound(binder_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
*
|
|
||||||
* # Lattice Variables
|
* # Lattice Variables
|
||||||
*
|
*
|
||||||
* This file contains generic code for operating on inference variables
|
* This file contains generic code for operating on inference variables
|
||||||
|
@ -34,346 +33,55 @@
|
||||||
|
|
||||||
use middle::ty::{RegionVid, TyVar};
|
use middle::ty::{RegionVid, TyVar};
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use middle::typeck::infer::{ToUres};
|
|
||||||
use middle::typeck::infer::*;
|
use middle::typeck::infer::*;
|
||||||
use middle::typeck::infer::combine::*;
|
use middle::typeck::infer::combine::*;
|
||||||
use middle::typeck::infer::glb::Glb;
|
use middle::typeck::infer::glb::Glb;
|
||||||
use middle::typeck::infer::lub::Lub;
|
use middle::typeck::infer::lub::Lub;
|
||||||
use middle::typeck::infer::unify::*;
|
|
||||||
use middle::typeck::infer::sub::Sub;
|
|
||||||
use util::ppaux::Repr;
|
use util::ppaux::Repr;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
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 type LatticeOp<'a, T> =
|
|
||||||
|cf: CombineFields, a: &T, b: &T|: 'a -> cres<T>;
|
|
||||||
|
|
||||||
impl LatticeValue for ty::t {
|
|
||||||
fn sub(cf: CombineFields, a: &ty::t, b: &ty::t) -> ures {
|
|
||||||
Sub(cf).tys(*a, *b).to_ures()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lub(cf: CombineFields, a: &ty::t, b: &ty::t) -> cres<ty::t> {
|
|
||||||
Lub(cf).tys(*a, *b)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn glb(cf: CombineFields, a: &ty::t, b: &ty::t) -> cres<ty::t> {
|
|
||||||
Glb(cf).tys(*a, *b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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(&self,
|
|
||||||
a_id: K,
|
|
||||||
b: T)
|
|
||||||
-> ures;
|
|
||||||
|
|
||||||
/// make T a subtype of variable
|
|
||||||
fn t_sub_var(&self,
|
|
||||||
a: T,
|
|
||||||
b_id: K)
|
|
||||||
-> ures;
|
|
||||||
|
|
||||||
fn set_var_to_merged_bounds(&self,
|
|
||||||
v_id: K,
|
|
||||||
a: &Bounds<T>,
|
|
||||||
b: &Bounds<T>,
|
|
||||||
rank: uint)
|
|
||||||
-> 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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 = 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, a_bounds.repr(tcx),
|
|
||||||
b_id, b_bounds.repr(tcx));
|
|
||||||
|
|
||||||
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.
|
|
||||||
match (&a_bounds.ub, &b_bounds.lb) {
|
|
||||||
(&Some(ref a_ub), &Some(ref b_lb)) => {
|
|
||||||
let r = self.infcx.try(
|
|
||||||
|| LatticeValue::sub(self.clone(), a_ub, b_lb));
|
|
||||||
match r {
|
|
||||||
Ok(()) => {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
Err(_) => { /*fallthrough */ }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => { /*fallthrough*/ }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, we need to merge A and B so as to guarantee that
|
|
||||||
// A remains a subtype of B. Actually, there are other options,
|
|
||||||
// but that's the route we choose to take.
|
|
||||||
|
|
||||||
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(&self,
|
|
||||||
a_id: K,
|
|
||||||
b: T)
|
|
||||||
-> ures
|
|
||||||
{
|
|
||||||
/*!
|
|
||||||
* 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,
|
|
||||||
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(&self,
|
|
||||||
a: T,
|
|
||||||
b_id: K)
|
|
||||||
-> ures
|
|
||||||
{
|
|
||||||
/*!
|
|
||||||
* 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 = 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.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 set_var_to_merged_bounds(&self,
|
|
||||||
v_id: K,
|
|
||||||
a: &Bounds<T>,
|
|
||||||
b: &Bounds<T>,
|
|
||||||
rank: uint)
|
|
||||||
-> 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 <: a.ub
|
|
||||||
* c.ub <: b.ub
|
|
||||||
* a.lb <: c.lb
|
|
||||||
* b.lb <: c.lb
|
|
||||||
* 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
|
|
||||||
// can swap A/B in these pictures):
|
|
||||||
//
|
|
||||||
// A A
|
|
||||||
// / \ / \
|
|
||||||
// / B \ / B \
|
|
||||||
// / / \ \ / / \ \
|
|
||||||
// * * * * * / * *
|
|
||||||
// \ \ / / \ / /
|
|
||||||
// \ B / / \ / /
|
|
||||||
// \ / * \ /
|
|
||||||
// A \ / A
|
|
||||||
// B
|
|
||||||
|
|
||||||
let tcx = self.infcx.tcx;
|
|
||||||
let table = UnifyKey::unification_table(self.infcx);
|
|
||||||
|
|
||||||
debug!("merge({},{},{})",
|
|
||||||
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
|
|
||||||
// to be able to merge A and B at all, and relating
|
|
||||||
// them explicitly gives the type inferencer more
|
|
||||||
// information and helps to produce tighter bounds
|
|
||||||
// when necessary.
|
|
||||||
let () = if_ok!(self.bnds(&a.lb, &b.ub));
|
|
||||||
let () = if_ok!(self.bnds(&b.lb, &a.ub));
|
|
||||||
let ub = if_ok!(self.merge_bnd(&a.ub, &b.ub, LatticeValue::glb));
|
|
||||||
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,
|
|
||||||
bounds.repr(self.infcx.tcx));
|
|
||||||
|
|
||||||
// the new bounds must themselves
|
|
||||||
// be relatable:
|
|
||||||
let () = if_ok!(self.bnds(&bounds.lb, &bounds.ub));
|
|
||||||
table.borrow_mut().set(tcx, v_id, Root(bounds, rank));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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(_)) => {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
(&Some(ref t_a), &Some(ref t_b)) => {
|
|
||||||
LatticeValue::sub(self.clone(), t_a, t_b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ______________________________________________________________________
|
|
||||||
// Lattice operations on variables
|
|
||||||
//
|
|
||||||
// This is common code used by both LUB and GLB to compute the LUB/GLB
|
|
||||||
// for pairs of variables or for variables and values.
|
|
||||||
|
|
||||||
pub trait LatticeDir {
|
pub trait LatticeDir {
|
||||||
fn combine_fields<'a>(&'a self) -> CombineFields<'a>;
|
// Relates the bottom type to `t` and returns LUB(t, _|_) or
|
||||||
fn bnd<T:Clone>(&self, b: &Bounds<T>) -> Option<T>;
|
// GLB(t, _|_) as appropriate.
|
||||||
fn with_bnd<T:Clone>(&self, b: &Bounds<T>, t: T) -> Bounds<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait TyLatticeDir {
|
|
||||||
fn ty_bot(&self, t: ty::t) -> cres<ty::t>;
|
fn ty_bot(&self, t: ty::t) -> cres<ty::t>;
|
||||||
|
|
||||||
|
// Relates the type `v` to `a` and `b` such that `v` represents
|
||||||
|
// the LUB/GLB of `a` and `b` as appropriate.
|
||||||
|
fn relate_bound<'a>(&'a self, v: ty::t, a: ty::t, b: ty::t) -> cres<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'f> LatticeDir for Lub<'f> {
|
impl<'a> LatticeDir for Lub<'a> {
|
||||||
fn combine_fields<'a>(&'a self) -> CombineFields<'a> { self.get_ref().clone() }
|
|
||||||
fn bnd<T:Clone>(&self, b: &Bounds<T>) -> Option<T> { b.ub.clone() }
|
|
||||||
fn with_bnd<T:Clone>(&self, b: &Bounds<T>, t: T) -> Bounds<T> {
|
|
||||||
Bounds { ub: Some(t), ..(*b).clone() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'f> TyLatticeDir for Lub<'f> {
|
|
||||||
fn ty_bot(&self, t: ty::t) -> cres<ty::t> {
|
fn ty_bot(&self, t: ty::t) -> cres<ty::t> {
|
||||||
Ok(t)
|
Ok(t)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'f> LatticeDir for Glb<'f> {
|
fn relate_bound<'a>(&'a self, v: ty::t, a: ty::t, b: ty::t) -> cres<()> {
|
||||||
fn combine_fields<'a>(&'a self) -> CombineFields<'a> { self.get_ref().clone() }
|
let sub = self.sub();
|
||||||
fn bnd<T:Clone>(&self, b: &Bounds<T>) -> Option<T> { b.lb.clone() }
|
try!(sub.tys(a, v));
|
||||||
fn with_bnd<T:Clone>(&self, b: &Bounds<T>, t: T) -> Bounds<T> {
|
try!(sub.tys(b, v));
|
||||||
Bounds { lb: Some(t), ..(*b).clone() }
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'f> TyLatticeDir for Glb<'f> {
|
impl<'a> LatticeDir for Glb<'a> {
|
||||||
fn ty_bot(&self, _t: ty::t) -> cres<ty::t> {
|
fn ty_bot(&self, _: ty::t) -> cres<ty::t> {
|
||||||
Ok(ty::mk_bot())
|
Ok(ty::mk_bot())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn relate_bound<'a>(&'a self, v: ty::t, a: ty::t, b: ty::t) -> cres<()> {
|
||||||
|
let sub = self.sub();
|
||||||
|
try!(sub.tys(v, a));
|
||||||
|
try!(sub.tys(v, b));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn super_lattice_tys<L:LatticeDir+TyLatticeDir+Combine>(this: &L,
|
pub fn super_lattice_tys<L:LatticeDir+Combine>(this: &L,
|
||||||
a: ty::t,
|
a: ty::t,
|
||||||
b: ty::t)
|
b: ty::t)
|
||||||
-> cres<ty::t> {
|
-> cres<ty::t>
|
||||||
|
{
|
||||||
debug!("{}.lattice_tys({}, {})",
|
debug!("{}.lattice_tys({}, {})",
|
||||||
this.tag(),
|
this.tag(),
|
||||||
a.repr(this.infcx().tcx),
|
a.repr(this.infcx().tcx),
|
||||||
|
@ -383,156 +91,27 @@ pub fn super_lattice_tys<L:LatticeDir+TyLatticeDir+Combine>(this: &L,
|
||||||
return Ok(a);
|
return Ok(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
let tcx = this.infcx().tcx;
|
let infcx = this.infcx();
|
||||||
|
let a = infcx.type_variables.borrow().replace_if_possible(a);
|
||||||
|
let b = infcx.type_variables.borrow().replace_if_possible(b);
|
||||||
match (&ty::get(a).sty, &ty::get(b).sty) {
|
match (&ty::get(a).sty, &ty::get(b).sty) {
|
||||||
(&ty::ty_bot, _) => { return this.ty_bot(b); }
|
(&ty::ty_bot, _) => { this.ty_bot(b) }
|
||||||
(_, &ty::ty_bot) => { return this.ty_bot(a); }
|
(_, &ty::ty_bot) => { this.ty_bot(a) }
|
||||||
|
|
||||||
(&ty::ty_infer(TyVar(a_id)), &ty::ty_infer(TyVar(b_id))) => {
|
(&ty::ty_infer(TyVar(..)), _) |
|
||||||
let r = if_ok!(lattice_vars(this, a_id, b_id,
|
(_, &ty::ty_infer(TyVar(..))) => {
|
||||||
|x, y| this.tys(*x, *y)));
|
let v = infcx.next_ty_var();
|
||||||
return match r {
|
try!(this.relate_bound(v, a, b));
|
||||||
VarResult(v) => Ok(ty::mk_var(tcx, v)),
|
Ok(v)
|
||||||
ValueResult(t) => Ok(t)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
(&ty::ty_infer(TyVar(a_id)), _) => {
|
|
||||||
return lattice_var_and_t(this, a_id, &b,
|
|
||||||
|x, y| this.tys(*x, *y));
|
|
||||||
}
|
|
||||||
|
|
||||||
(_, &ty::ty_infer(TyVar(b_id))) => {
|
|
||||||
return lattice_var_and_t(this, b_id, &a,
|
|
||||||
|x, y| this.tys(*x, *y));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
return super_tys(this, a, b);
|
super_tys(this, a, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type LatticeDirOp<'a, T> = |a: &T, b: &T|: 'a -> cres<T>;
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[deriving(Clone)]
|
|
||||||
pub enum LatticeVarResult<K,T> {
|
|
||||||
VarResult(K),
|
|
||||||
ValueResult(T)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes the LUB or GLB of two bounded variables. These could be any
|
|
||||||
* sort of variables, but in the comments on this function I'll assume
|
|
||||||
* we are doing an LUB on two type variables.
|
|
||||||
*
|
|
||||||
* This computation can be done in one of two ways:
|
|
||||||
*
|
|
||||||
* - If both variables have an upper bound, we may just compute the
|
|
||||||
* LUB of those bounds and return that, in which case we are
|
|
||||||
* returning a type. This is indicated with a `ValueResult` return.
|
|
||||||
*
|
|
||||||
* - 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:LatticeValue,
|
|
||||||
K:UnifyKey<Bounds<T>>>(
|
|
||||||
this: &L, // defines whether we want LUB or GLB
|
|
||||||
a_vid: K, // first variable
|
|
||||||
b_vid: K, // second variable
|
|
||||||
lattice_dir_op: LatticeDirOp<T>) // LUB or GLB operation on 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, a_bounds.repr(tcx),
|
|
||||||
b_vid, b_bounds.repr(tcx));
|
|
||||||
|
|
||||||
// Same variable: the easy case.
|
|
||||||
if a_vid == b_vid {
|
|
||||||
return Ok(VarResult(a_vid));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If both A and B have an UB type, then we can just compute the
|
|
||||||
// LUB of those types:
|
|
||||||
let (a_bnd, b_bnd) = (this.bnd(a_bounds), this.bnd(b_bounds));
|
|
||||||
match (a_bnd, b_bnd) {
|
|
||||||
(Some(ref a_ty), Some(ref b_ty)) => {
|
|
||||||
match this.infcx().try(|| lattice_dir_op(a_ty, b_ty) ) {
|
|
||||||
Ok(t) => return Ok(ValueResult(t)),
|
|
||||||
Err(_) => { /*fallthrough */ }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {/*fallthrough*/}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, we need to merge A and B into one variable. We can
|
|
||||||
// then use either variable as an upper bound:
|
|
||||||
let cf = this.combine_fields();
|
|
||||||
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:LatticeValue,
|
|
||||||
K:UnifyKey<Bounds<T>>>(
|
|
||||||
this: &L,
|
|
||||||
a_id: K,
|
|
||||||
b: &T,
|
|
||||||
lattice_dir_op: LatticeDirOp<T>)
|
|
||||||
-> 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,
|
|
||||||
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.repr(this.infcx().tcx));
|
|
||||||
lattice_dir_op(a_bnd, b)
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// If a does not have an upper bound, make b the upper bound of a
|
|
||||||
// and then return b.
|
|
||||||
debug!("bnd=None");
|
|
||||||
let a_bounds = this.with_bnd(a_bounds, (*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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ___________________________________________________________________________
|
|
||||||
// Random utility functions used by LUB/GLB when computing LUB/GLB of
|
// Random utility functions used by LUB/GLB when computing LUB/GLB of
|
||||||
// fn types
|
// fn types
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
use middle::ty::{BuiltinBounds};
|
use middle::ty::{BuiltinBounds};
|
||||||
use middle::ty::RegionVid;
|
use middle::ty::RegionVid;
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use middle::typeck::infer::then;
|
|
||||||
use middle::typeck::infer::combine::*;
|
use middle::typeck::infer::combine::*;
|
||||||
|
use middle::typeck::infer::equate::Equate;
|
||||||
use middle::typeck::infer::glb::Glb;
|
use middle::typeck::infer::glb::Glb;
|
||||||
use middle::typeck::infer::lattice::*;
|
use middle::typeck::infer::lattice::*;
|
||||||
use middle::typeck::infer::sub::Sub;
|
use middle::typeck::infer::sub::Sub;
|
||||||
|
@ -28,24 +28,29 @@ use syntax::ast::{MutMutable, MutImmutable};
|
||||||
use util::ppaux::mt_to_string;
|
use util::ppaux::mt_to_string;
|
||||||
use util::ppaux::Repr;
|
use util::ppaux::Repr;
|
||||||
|
|
||||||
pub struct Lub<'f>(pub CombineFields<'f>); // least-upper-bound: common supertype
|
/// "Least upper bound" (common supertype)
|
||||||
|
pub struct Lub<'f> {
|
||||||
|
fields: CombineFields<'f>
|
||||||
|
}
|
||||||
|
|
||||||
impl<'f> Lub<'f> {
|
#[allow(non_snake_case_functions)]
|
||||||
pub fn get_ref<'a>(&'a self) -> &'a CombineFields<'f> { let Lub(ref v) = *self; v }
|
pub fn Lub<'f>(cf: CombineFields<'f>) -> Lub<'f> {
|
||||||
|
Lub { fields: cf }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'f> Combine for Lub<'f> {
|
impl<'f> Combine for Lub<'f> {
|
||||||
fn infcx<'a>(&'a self) -> &'a InferCtxt<'a> { self.get_ref().infcx }
|
fn infcx<'a>(&'a self) -> &'a InferCtxt<'a> { self.fields.infcx }
|
||||||
fn tag(&self) -> String { "lub".to_string() }
|
fn tag(&self) -> String { "lub".to_string() }
|
||||||
fn a_is_expected(&self) -> bool { self.get_ref().a_is_expected }
|
fn a_is_expected(&self) -> bool { self.fields.a_is_expected }
|
||||||
fn trace(&self) -> TypeTrace { self.get_ref().trace.clone() }
|
fn trace(&self) -> TypeTrace { self.fields.trace.clone() }
|
||||||
|
|
||||||
fn sub<'a>(&'a self) -> Sub<'a> { Sub(self.get_ref().clone()) }
|
fn equate<'a>(&'a self) -> Equate<'a> { Equate(self.fields.clone()) }
|
||||||
fn lub<'a>(&'a self) -> Lub<'a> { Lub(self.get_ref().clone()) }
|
fn sub<'a>(&'a self) -> Sub<'a> { Sub(self.fields.clone()) }
|
||||||
fn glb<'a>(&'a self) -> Glb<'a> { Glb(self.get_ref().clone()) }
|
fn lub<'a>(&'a self) -> Lub<'a> { Lub(self.fields.clone()) }
|
||||||
|
fn glb<'a>(&'a self) -> Glb<'a> { Glb(self.fields.clone()) }
|
||||||
|
|
||||||
fn mts(&self, a: &ty::mt, b: &ty::mt) -> cres<ty::mt> {
|
fn mts(&self, a: &ty::mt, b: &ty::mt) -> cres<ty::mt> {
|
||||||
let tcx = self.get_ref().infcx.tcx;
|
let tcx = self.fields.infcx.tcx;
|
||||||
|
|
||||||
debug!("{}.mts({}, {})",
|
debug!("{}.mts({}, {})",
|
||||||
self.tag(),
|
self.tag(),
|
||||||
|
@ -59,15 +64,13 @@ impl<'f> Combine for Lub<'f> {
|
||||||
let m = a.mutbl;
|
let m = a.mutbl;
|
||||||
match m {
|
match m {
|
||||||
MutImmutable => {
|
MutImmutable => {
|
||||||
self.tys(a.ty, b.ty).and_then(|t| Ok(ty::mt {ty: t, mutbl: m}) )
|
let t = try!(self.tys(a.ty, b.ty));
|
||||||
|
Ok(ty::mt {ty: t, mutbl: m})
|
||||||
}
|
}
|
||||||
|
|
||||||
MutMutable => {
|
MutMutable => {
|
||||||
self.get_ref().infcx.try(|| {
|
let t = try!(self.equate().tys(a.ty, b.ty));
|
||||||
eq_tys(self, a.ty, b.ty).then(|| {
|
Ok(ty::mt {ty: t, mutbl: m})
|
||||||
Ok(ty::mt {ty: a.ty, mutbl: m})
|
|
||||||
})
|
|
||||||
}).or_else(|e| Err(e))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,10 +110,10 @@ impl<'f> Combine for Lub<'f> {
|
||||||
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
|
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
|
||||||
debug!("{}.regions({}, {})",
|
debug!("{}.regions({}, {})",
|
||||||
self.tag(),
|
self.tag(),
|
||||||
a.repr(self.get_ref().infcx.tcx),
|
a.repr(self.fields.infcx.tcx),
|
||||||
b.repr(self.get_ref().infcx.tcx));
|
b.repr(self.fields.infcx.tcx));
|
||||||
|
|
||||||
Ok(self.get_ref().infcx.region_vars.lub_regions(Subtype(self.trace()), a, b))
|
Ok(self.fields.infcx.region_vars.lub_regions(Subtype(self.trace()), a, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
|
fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
|
||||||
|
@ -119,26 +122,26 @@ impl<'f> Combine for Lub<'f> {
|
||||||
|
|
||||||
// Make a mark so we can examine "all bindings that were
|
// Make a mark so we can examine "all bindings that were
|
||||||
// created as part of this type comparison".
|
// created as part of this type comparison".
|
||||||
let mark = self.get_ref().infcx.region_vars.mark();
|
let mark = self.fields.infcx.region_vars.mark();
|
||||||
|
|
||||||
// Instantiate each bound region with a fresh region variable.
|
// Instantiate each bound region with a fresh region variable.
|
||||||
let (a_with_fresh, a_map) =
|
let (a_with_fresh, a_map) =
|
||||||
self.get_ref().infcx.replace_late_bound_regions_with_fresh_regions(
|
self.fields.infcx.replace_late_bound_regions_with_fresh_regions(
|
||||||
self.trace(), a);
|
self.trace(), a);
|
||||||
let (b_with_fresh, _) =
|
let (b_with_fresh, _) =
|
||||||
self.get_ref().infcx.replace_late_bound_regions_with_fresh_regions(
|
self.fields.infcx.replace_late_bound_regions_with_fresh_regions(
|
||||||
self.trace(), b);
|
self.trace(), b);
|
||||||
|
|
||||||
// Collect constraints.
|
// Collect constraints.
|
||||||
let sig0 = if_ok!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh));
|
let sig0 = try!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh));
|
||||||
debug!("sig0 = {}", sig0.repr(self.get_ref().infcx.tcx));
|
debug!("sig0 = {}", sig0.repr(self.fields.infcx.tcx));
|
||||||
|
|
||||||
// Generalize the regions appearing in sig0 if possible
|
// Generalize the regions appearing in sig0 if possible
|
||||||
let new_vars =
|
let new_vars =
|
||||||
self.get_ref().infcx.region_vars.vars_created_since_mark(mark);
|
self.fields.infcx.region_vars.vars_created_since_mark(mark);
|
||||||
let sig1 =
|
let sig1 =
|
||||||
fold_regions_in_sig(
|
fold_regions_in_sig(
|
||||||
self.get_ref().infcx.tcx,
|
self.fields.infcx.tcx,
|
||||||
&sig0,
|
&sig0,
|
||||||
|r| generalize_region(self, mark, new_vars.as_slice(),
|
|r| generalize_region(self, mark, new_vars.as_slice(),
|
||||||
sig0.binder_id, &a_map, r));
|
sig0.binder_id, &a_map, r));
|
||||||
|
@ -158,7 +161,7 @@ impl<'f> Combine for Lub<'f> {
|
||||||
return r0;
|
return r0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let tainted = this.get_ref().infcx.region_vars.tainted(mark, r0);
|
let tainted = this.fields.infcx.region_vars.tainted(mark, r0);
|
||||||
|
|
||||||
// Variables created during LUB computation which are
|
// Variables created during LUB computation which are
|
||||||
// *related* to regions that pre-date the LUB computation
|
// *related* to regions that pre-date the LUB computation
|
||||||
|
@ -185,8 +188,8 @@ impl<'f> Combine for Lub<'f> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.get_ref().infcx.tcx.sess.span_bug(
|
this.fields.infcx.tcx.sess.span_bug(
|
||||||
this.get_ref().trace.origin.span(),
|
this.fields.trace.origin.span(),
|
||||||
format!("region {:?} is not associated with \
|
format!("region {:?} is not associated with \
|
||||||
any bound region from A!",
|
any bound region from A!",
|
||||||
r0).as_slice())
|
r0).as_slice())
|
||||||
|
|
|
@ -29,13 +29,14 @@ use middle::ty_fold;
|
||||||
use middle::ty_fold::TypeFolder;
|
use middle::ty_fold::TypeFolder;
|
||||||
use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig;
|
use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig;
|
||||||
use middle::typeck::infer::coercion::Coerce;
|
use middle::typeck::infer::coercion::Coerce;
|
||||||
use middle::typeck::infer::combine::{Combine, CombineFields, eq_tys};
|
use middle::typeck::infer::combine::{Combine, CombineFields};
|
||||||
use middle::typeck::infer::region_inference::{RegionSnapshot};
|
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::resolve::{resolver};
|
||||||
|
use middle::typeck::infer::equate::Equate;
|
||||||
use middle::typeck::infer::sub::Sub;
|
use middle::typeck::infer::sub::Sub;
|
||||||
use middle::typeck::infer::lub::Lub;
|
use middle::typeck::infer::lub::Lub;
|
||||||
use middle::typeck::infer::unify::{UnificationTable, Snapshot};
|
use middle::typeck::infer::unify::{UnificationTable};
|
||||||
use middle::typeck::infer::error_reporting::ErrorReporting;
|
use middle::typeck::infer::error_reporting::ErrorReporting;
|
||||||
use std::cell::{RefCell};
|
use std::cell::{RefCell};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -46,19 +47,20 @@ use syntax::codemap::Span;
|
||||||
use util::common::indent;
|
use util::common::indent;
|
||||||
use util::ppaux::{bound_region_to_string, ty_to_string, trait_ref_to_string, Repr};
|
use util::ppaux::{bound_region_to_string, ty_to_string, trait_ref_to_string, Repr};
|
||||||
|
|
||||||
pub mod doc;
|
pub mod coercion;
|
||||||
pub mod macros;
|
|
||||||
pub mod combine;
|
pub mod combine;
|
||||||
|
pub mod doc;
|
||||||
|
pub mod equate;
|
||||||
|
pub mod error_reporting;
|
||||||
pub mod glb;
|
pub mod glb;
|
||||||
pub mod lattice;
|
pub mod lattice;
|
||||||
pub mod lub;
|
pub mod lub;
|
||||||
pub mod region_inference;
|
pub mod region_inference;
|
||||||
pub mod resolve;
|
pub mod resolve;
|
||||||
pub mod sub;
|
pub mod sub;
|
||||||
pub mod unify;
|
|
||||||
pub mod coercion;
|
|
||||||
pub mod error_reporting;
|
|
||||||
pub mod test;
|
pub mod test;
|
||||||
|
pub mod type_variable;
|
||||||
|
pub mod unify;
|
||||||
|
|
||||||
pub type Bound<T> = Option<T>;
|
pub type Bound<T> = Option<T>;
|
||||||
|
|
||||||
|
@ -79,8 +81,7 @@ pub struct InferCtxt<'a> {
|
||||||
// We instantiate UnificationTable 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
|
// types that might instantiate a general type variable have an
|
||||||
// order, represented by its upper and lower bounds.
|
// order, represented by its upper and lower bounds.
|
||||||
type_unification_table:
|
type_variables: RefCell<type_variable::TypeVariableTable>,
|
||||||
RefCell<UnificationTable<ty::TyVid, Bounds<ty::t>>>,
|
|
||||||
|
|
||||||
// Map from integral variable to the kind of integer it represents
|
// Map from integral variable to the kind of integer it represents
|
||||||
int_unification_table:
|
int_unification_table:
|
||||||
|
@ -161,6 +162,9 @@ pub enum SubregionOrigin {
|
||||||
// Closure bound must not outlive captured free variables
|
// Closure bound must not outlive captured free variables
|
||||||
FreeVariable(Span, ast::NodeId),
|
FreeVariable(Span, ast::NodeId),
|
||||||
|
|
||||||
|
// Proc upvars must be 'static
|
||||||
|
ProcCapture(Span, ast::NodeId),
|
||||||
|
|
||||||
// Index into slice must be within its lifetime
|
// Index into slice must be within its lifetime
|
||||||
IndexSlice(Span),
|
IndexSlice(Span),
|
||||||
|
|
||||||
|
@ -290,7 +294,7 @@ pub fn fixup_err_to_string(f: fixup_err) -> String {
|
||||||
pub fn new_infer_ctxt<'a>(tcx: &'a ty::ctxt) -> InferCtxt<'a> {
|
pub fn new_infer_ctxt<'a>(tcx: &'a ty::ctxt) -> InferCtxt<'a> {
|
||||||
InferCtxt {
|
InferCtxt {
|
||||||
tcx: tcx,
|
tcx: tcx,
|
||||||
type_unification_table: RefCell::new(UnificationTable::new()),
|
type_variables: RefCell::new(type_variable::TypeVariableTable::new()),
|
||||||
int_unification_table: RefCell::new(UnificationTable::new()),
|
int_unification_table: RefCell::new(UnificationTable::new()),
|
||||||
float_unification_table: RefCell::new(UnificationTable::new()),
|
float_unification_table: RefCell::new(UnificationTable::new()),
|
||||||
region_vars: RegionVarBindings::new(tcx),
|
region_vars: RegionVarBindings::new(tcx),
|
||||||
|
@ -392,8 +396,8 @@ pub fn mk_eqty(cx: &InferCtxt,
|
||||||
origin: origin,
|
origin: origin,
|
||||||
values: Types(expected_found(a_is_expected, a, b))
|
values: Types(expected_found(a_is_expected, a, b))
|
||||||
};
|
};
|
||||||
let suber = cx.sub(a_is_expected, trace);
|
try!(cx.equate(a_is_expected, trace).tys(a, b));
|
||||||
eq_tys(&suber, a, b)
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,9 +512,9 @@ pub fn uok() -> ures {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CombinedSnapshot {
|
pub struct CombinedSnapshot {
|
||||||
type_snapshot: Snapshot<ty::TyVid>,
|
type_snapshot: type_variable::Snapshot,
|
||||||
int_snapshot: Snapshot<ty::IntVid>,
|
int_snapshot: unify::Snapshot<ty::IntVid>,
|
||||||
float_snapshot: Snapshot<ty::FloatVid>,
|
float_snapshot: unify::Snapshot<ty::FloatVid>,
|
||||||
region_vars_snapshot: RegionSnapshot,
|
region_vars_snapshot: RegionSnapshot,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -522,6 +526,10 @@ impl<'a> InferCtxt<'a> {
|
||||||
trace: trace}
|
trace: trace}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn equate<'a>(&'a self, a_is_expected: bool, trace: TypeTrace) -> Equate<'a> {
|
||||||
|
Equate(self.combine_fields(a_is_expected, trace))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sub<'a>(&'a self, a_is_expected: bool, trace: TypeTrace) -> Sub<'a> {
|
pub fn sub<'a>(&'a self, a_is_expected: bool, trace: TypeTrace) -> Sub<'a> {
|
||||||
Sub(self.combine_fields(a_is_expected, trace))
|
Sub(self.combine_fields(a_is_expected, trace))
|
||||||
}
|
}
|
||||||
|
@ -530,13 +538,9 @@ impl<'a> InferCtxt<'a> {
|
||||||
Lub(self.combine_fields(a_is_expected, trace))
|
Lub(self.combine_fields(a_is_expected, trace))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn in_snapshot(&self) -> bool {
|
|
||||||
self.region_vars.in_snapshot()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start_snapshot(&self) -> CombinedSnapshot {
|
fn start_snapshot(&self) -> CombinedSnapshot {
|
||||||
CombinedSnapshot {
|
CombinedSnapshot {
|
||||||
type_snapshot: self.type_unification_table.borrow_mut().snapshot(),
|
type_snapshot: self.type_variables.borrow_mut().snapshot(),
|
||||||
int_snapshot: self.int_unification_table.borrow_mut().snapshot(),
|
int_snapshot: self.int_unification_table.borrow_mut().snapshot(),
|
||||||
float_snapshot: self.float_unification_table.borrow_mut().snapshot(),
|
float_snapshot: self.float_unification_table.borrow_mut().snapshot(),
|
||||||
region_vars_snapshot: self.region_vars.start_snapshot(),
|
region_vars_snapshot: self.region_vars.start_snapshot(),
|
||||||
|
@ -550,15 +554,15 @@ impl<'a> InferCtxt<'a> {
|
||||||
float_snapshot,
|
float_snapshot,
|
||||||
region_vars_snapshot } = snapshot;
|
region_vars_snapshot } = snapshot;
|
||||||
|
|
||||||
self.type_unification_table
|
self.type_variables
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.rollback_to(self.tcx, type_snapshot);
|
.rollback_to(type_snapshot);
|
||||||
self.int_unification_table
|
self.int_unification_table
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.rollback_to(self.tcx, int_snapshot);
|
.rollback_to(int_snapshot);
|
||||||
self.float_unification_table
|
self.float_unification_table
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.rollback_to(self.tcx, float_snapshot);
|
.rollback_to(float_snapshot);
|
||||||
self.region_vars
|
self.region_vars
|
||||||
.rollback_to(region_vars_snapshot);
|
.rollback_to(region_vars_snapshot);
|
||||||
}
|
}
|
||||||
|
@ -570,7 +574,7 @@ impl<'a> InferCtxt<'a> {
|
||||||
float_snapshot,
|
float_snapshot,
|
||||||
region_vars_snapshot } = snapshot;
|
region_vars_snapshot } = snapshot;
|
||||||
|
|
||||||
self.type_unification_table
|
self.type_variables
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.commit(type_snapshot);
|
.commit(type_snapshot);
|
||||||
self.int_unification_table
|
self.int_unification_table
|
||||||
|
@ -633,9 +637,9 @@ impl<'a> InferCtxt<'a> {
|
||||||
|
|
||||||
impl<'a> InferCtxt<'a> {
|
impl<'a> InferCtxt<'a> {
|
||||||
pub fn next_ty_var_id(&self) -> TyVid {
|
pub fn next_ty_var_id(&self) -> TyVid {
|
||||||
self.type_unification_table
|
self.type_variables
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.new_key(Bounds { lb: None, ub: None })
|
.new_var()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_ty_var(&self) -> ty::t {
|
pub fn next_ty_var(&self) -> ty::t {
|
||||||
|
@ -933,6 +937,7 @@ impl SubregionOrigin {
|
||||||
InvokeClosure(a) => a,
|
InvokeClosure(a) => a,
|
||||||
DerefPointer(a) => a,
|
DerefPointer(a) => a,
|
||||||
FreeVariable(a, _) => a,
|
FreeVariable(a, _) => a,
|
||||||
|
ProcCapture(a, _) => a,
|
||||||
IndexSlice(a) => a,
|
IndexSlice(a) => a,
|
||||||
RelateObjectBound(a) => a,
|
RelateObjectBound(a) => a,
|
||||||
RelateProcBound(a, _, _) => a,
|
RelateProcBound(a, _, _) => a,
|
||||||
|
@ -972,6 +977,9 @@ impl Repr for SubregionOrigin {
|
||||||
FreeVariable(a, b) => {
|
FreeVariable(a, b) => {
|
||||||
format!("FreeVariable({}, {})", a.repr(tcx), b)
|
format!("FreeVariable({}, {})", a.repr(tcx), b)
|
||||||
}
|
}
|
||||||
|
ProcCapture(a, b) => {
|
||||||
|
format!("ProcCapture({}, {})", a.repr(tcx), b)
|
||||||
|
}
|
||||||
IndexSlice(a) => {
|
IndexSlice(a) => {
|
||||||
format!("IndexSlice({})", a.repr(tcx))
|
format!("IndexSlice({})", a.repr(tcx))
|
||||||
}
|
}
|
||||||
|
|
|
@ -234,7 +234,7 @@ impl<'a> RegionVarBindings<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn in_snapshot(&self) -> bool {
|
fn in_snapshot(&self) -> bool {
|
||||||
self.undo_log.borrow().len() > 0
|
self.undo_log.borrow().len() > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,7 +253,7 @@ impl<'a> RegionVarBindings<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn commit(&self, snapshot: RegionSnapshot) {
|
pub fn commit(&self, snapshot: RegionSnapshot) {
|
||||||
debug!("RegionVarBindings: commit()");
|
debug!("RegionVarBindings: commit({})", snapshot.length);
|
||||||
assert!(self.undo_log.borrow().len() > snapshot.length);
|
assert!(self.undo_log.borrow().len() > snapshot.length);
|
||||||
assert!(*self.undo_log.borrow().get(snapshot.length) == OpenSnapshot);
|
assert!(*self.undo_log.borrow().get(snapshot.length) == OpenSnapshot);
|
||||||
|
|
||||||
|
@ -406,6 +406,18 @@ impl<'a> RegionVarBindings<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn make_eqregion(&self,
|
||||||
|
origin: SubregionOrigin,
|
||||||
|
sub: Region,
|
||||||
|
sup: Region) {
|
||||||
|
if sub != sup {
|
||||||
|
// Eventually, it would be nice to add direct support for
|
||||||
|
// equating regions.
|
||||||
|
self.make_subregion(origin.clone(), sub, sup);
|
||||||
|
self.make_subregion(origin, sup, sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn make_subregion(&self,
|
pub fn make_subregion(&self,
|
||||||
origin: SubregionOrigin,
|
origin: SubregionOrigin,
|
||||||
sub: Region,
|
sub: Region,
|
||||||
|
|
|
@ -48,12 +48,11 @@
|
||||||
|
|
||||||
|
|
||||||
use middle::ty::{FloatVar, FloatVid, IntVar, IntVid, RegionVid, TyVar, TyVid};
|
use middle::ty::{FloatVar, FloatVid, IntVar, IntVid, RegionVid, TyVar, TyVid};
|
||||||
use middle::ty::{type_is_bot, IntType, UintType};
|
use middle::ty::{IntType, UintType};
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use middle::ty_fold;
|
use middle::ty_fold;
|
||||||
use middle::typeck::infer::{Bounds, cyclic_ty, fixup_err, fres, InferCtxt};
|
use middle::typeck::infer::{cyclic_ty, fixup_err, fres, InferCtxt};
|
||||||
use middle::typeck::infer::{unresolved_float_ty, unresolved_int_ty};
|
use middle::typeck::infer::{unresolved_int_ty,unresolved_float_ty,unresolved_ty};
|
||||||
use middle::typeck::infer::{unresolved_ty};
|
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
use util::common::indent;
|
use util::common::indent;
|
||||||
use util::ppaux::{Repr, ty_to_string};
|
use util::ppaux::{Repr, ty_to_string};
|
||||||
|
@ -132,8 +131,8 @@ impl<'a> ResolveState<'a> {
|
||||||
assert!(self.v_seen.is_empty());
|
assert!(self.v_seen.is_empty());
|
||||||
match self.err {
|
match self.err {
|
||||||
None => {
|
None => {
|
||||||
debug!("Resolved to {} + {} (modes={:x})",
|
debug!("Resolved {} to {} (modes={:x})",
|
||||||
ty_to_string(self.infcx.tcx, rty),
|
ty_to_string(self.infcx.tcx, typ),
|
||||||
ty_to_string(self.infcx.tcx, rty),
|
ty_to_string(self.infcx.tcx, rty),
|
||||||
self.modes);
|
self.modes);
|
||||||
return Ok(rty);
|
return Ok(rty);
|
||||||
|
@ -219,16 +218,11 @@ impl<'a> ResolveState<'a> {
|
||||||
// tend to carry more restrictions or higher
|
// tend to carry more restrictions or higher
|
||||||
// perf. penalties, so it pays to know more.
|
// perf. penalties, so it pays to know more.
|
||||||
|
|
||||||
let node =
|
let t1 = match self.infcx.type_variables.borrow().probe(vid) {
|
||||||
self.infcx.type_unification_table.borrow_mut().get(tcx, vid);
|
Some(t) => {
|
||||||
let t1 = match node.value {
|
|
||||||
Bounds { ub:_, lb:Some(t) } if !type_is_bot(t) => {
|
|
||||||
self.resolve_type(t)
|
self.resolve_type(t)
|
||||||
}
|
}
|
||||||
Bounds { ub:Some(t), lb:_ } | Bounds { ub:_, lb:Some(t) } => {
|
None => {
|
||||||
self.resolve_type(t)
|
|
||||||
}
|
|
||||||
Bounds { ub:None, lb:None } => {
|
|
||||||
if self.should(force_tvar) {
|
if self.should(force_tvar) {
|
||||||
self.err = Some(unresolved_ty(vid));
|
self.err = Some(unresolved_ty(vid));
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,46 +15,48 @@ use middle::ty::TyVar;
|
||||||
use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig;
|
use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig;
|
||||||
use middle::typeck::infer::combine::*;
|
use middle::typeck::infer::combine::*;
|
||||||
use middle::typeck::infer::{cres, CresCompare};
|
use middle::typeck::infer::{cres, CresCompare};
|
||||||
|
use middle::typeck::infer::equate::Equate;
|
||||||
use middle::typeck::infer::glb::Glb;
|
use middle::typeck::infer::glb::Glb;
|
||||||
use middle::typeck::infer::InferCtxt;
|
use middle::typeck::infer::InferCtxt;
|
||||||
use middle::typeck::infer::lattice::CombineFieldsLatticeMethods;
|
|
||||||
use middle::typeck::infer::lub::Lub;
|
use middle::typeck::infer::lub::Lub;
|
||||||
use middle::typeck::infer::then;
|
|
||||||
use middle::typeck::infer::{TypeTrace, Subtype};
|
use middle::typeck::infer::{TypeTrace, Subtype};
|
||||||
|
use middle::typeck::infer::type_variable::{SubtypeOf, SupertypeOf};
|
||||||
use util::common::{indenter};
|
use util::common::{indenter};
|
||||||
use util::ppaux::{bound_region_to_string, Repr};
|
use util::ppaux::{bound_region_to_string, Repr};
|
||||||
|
|
||||||
use syntax::ast::{Onceness, FnStyle, MutImmutable, MutMutable};
|
use syntax::ast::{Onceness, FnStyle, MutImmutable, MutMutable};
|
||||||
|
|
||||||
pub struct Sub<'f>(pub CombineFields<'f>); // "subtype", "subregion" etc
|
|
||||||
|
|
||||||
impl<'f> Sub<'f> {
|
/// "Greatest lower bound" (common subtype)
|
||||||
pub fn get_ref<'a>(&'a self) -> &'a CombineFields<'f> { let Sub(ref v) = *self; v }
|
pub struct Sub<'f> {
|
||||||
|
fields: CombineFields<'f>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case_functions)]
|
||||||
|
pub fn Sub<'f>(cf: CombineFields<'f>) -> Sub<'f> {
|
||||||
|
Sub { fields: cf }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'f> Combine for Sub<'f> {
|
impl<'f> Combine for Sub<'f> {
|
||||||
fn infcx<'a>(&'a self) -> &'a InferCtxt<'a> { self.get_ref().infcx }
|
fn infcx<'a>(&'a self) -> &'a InferCtxt<'a> { self.fields.infcx }
|
||||||
fn tag(&self) -> String { "sub".to_string() }
|
fn tag(&self) -> String { "sub".to_string() }
|
||||||
fn a_is_expected(&self) -> bool { self.get_ref().a_is_expected }
|
fn a_is_expected(&self) -> bool { self.fields.a_is_expected }
|
||||||
fn trace(&self) -> TypeTrace { self.get_ref().trace.clone() }
|
fn trace(&self) -> TypeTrace { self.fields.trace.clone() }
|
||||||
|
|
||||||
fn sub<'a>(&'a self) -> Sub<'a> { Sub(self.get_ref().clone()) }
|
fn equate<'a>(&'a self) -> Equate<'a> { Equate(self.fields.clone()) }
|
||||||
fn lub<'a>(&'a self) -> Lub<'a> { Lub(self.get_ref().clone()) }
|
fn sub<'a>(&'a self) -> Sub<'a> { Sub(self.fields.clone()) }
|
||||||
fn glb<'a>(&'a self) -> Glb<'a> { Glb(self.get_ref().clone()) }
|
fn lub<'a>(&'a self) -> Lub<'a> { Lub(self.fields.clone()) }
|
||||||
|
fn glb<'a>(&'a self) -> Glb<'a> { Glb(self.fields.clone()) }
|
||||||
|
|
||||||
fn contratys(&self, a: ty::t, b: ty::t) -> cres<ty::t> {
|
fn contratys(&self, a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||||
let opp = CombineFields {
|
Sub(self.fields.switch_expected()).tys(b, a)
|
||||||
a_is_expected: !self.get_ref().a_is_expected,
|
|
||||||
..self.get_ref().clone()
|
|
||||||
};
|
|
||||||
Sub(opp).tys(b, a)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contraregions(&self, a: ty::Region, b: ty::Region)
|
fn contraregions(&self, a: ty::Region, b: ty::Region)
|
||||||
-> cres<ty::Region> {
|
-> cres<ty::Region> {
|
||||||
let opp = CombineFields {
|
let opp = CombineFields {
|
||||||
a_is_expected: !self.get_ref().a_is_expected,
|
a_is_expected: !self.fields.a_is_expected,
|
||||||
..self.get_ref().clone()
|
..self.fields.clone()
|
||||||
};
|
};
|
||||||
Sub(opp).regions(b, a)
|
Sub(opp).regions(b, a)
|
||||||
}
|
}
|
||||||
|
@ -62,16 +64,16 @@ impl<'f> Combine for Sub<'f> {
|
||||||
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
|
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
|
||||||
debug!("{}.regions({}, {})",
|
debug!("{}.regions({}, {})",
|
||||||
self.tag(),
|
self.tag(),
|
||||||
a.repr(self.get_ref().infcx.tcx),
|
a.repr(self.fields.infcx.tcx),
|
||||||
b.repr(self.get_ref().infcx.tcx));
|
b.repr(self.fields.infcx.tcx));
|
||||||
self.get_ref().infcx.region_vars.make_subregion(Subtype(self.trace()), a, b);
|
self.fields.infcx.region_vars.make_subregion(Subtype(self.trace()), a, b);
|
||||||
Ok(a)
|
Ok(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mts(&self, a: &ty::mt, b: &ty::mt) -> cres<ty::mt> {
|
fn mts(&self, a: &ty::mt, b: &ty::mt) -> cres<ty::mt> {
|
||||||
debug!("mts({} <: {})",
|
debug!("mts({} <: {})",
|
||||||
a.repr(self.get_ref().infcx.tcx),
|
a.repr(self.fields.infcx.tcx),
|
||||||
b.repr(self.get_ref().infcx.tcx));
|
b.repr(self.fields.infcx.tcx));
|
||||||
|
|
||||||
if a.mutbl != b.mutbl {
|
if a.mutbl != b.mutbl {
|
||||||
return Err(ty::terr_mutability);
|
return Err(ty::terr_mutability);
|
||||||
|
@ -81,13 +83,15 @@ impl<'f> Combine for Sub<'f> {
|
||||||
MutMutable => {
|
MutMutable => {
|
||||||
// If supertype is mut, subtype must match exactly
|
// If supertype is mut, subtype must match exactly
|
||||||
// (i.e., invariant if mut):
|
// (i.e., invariant if mut):
|
||||||
eq_tys(self, a.ty, b.ty).then(|| Ok(*a))
|
try!(self.equate().tys(a.ty, b.ty));
|
||||||
}
|
}
|
||||||
MutImmutable => {
|
MutImmutable => {
|
||||||
// Otherwise we can be covariant:
|
// Otherwise we can be covariant:
|
||||||
self.tys(a.ty, b.ty).and_then(|_t| Ok(*a) )
|
try!(self.tys(a.ty, b.ty));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(*a) // return is meaningless in sub, just return *a
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fn_styles(&self, a: FnStyle, b: FnStyle) -> cres<FnStyle> {
|
fn fn_styles(&self, a: FnStyle, b: FnStyle) -> cres<FnStyle> {
|
||||||
|
@ -118,16 +122,21 @@ impl<'f> Combine for Sub<'f> {
|
||||||
|
|
||||||
fn tys(&self, a: ty::t, b: ty::t) -> cres<ty::t> {
|
fn tys(&self, a: ty::t, b: ty::t) -> cres<ty::t> {
|
||||||
debug!("{}.tys({}, {})", self.tag(),
|
debug!("{}.tys({}, {})", self.tag(),
|
||||||
a.repr(self.get_ref().infcx.tcx), b.repr(self.get_ref().infcx.tcx));
|
a.repr(self.fields.infcx.tcx), b.repr(self.fields.infcx.tcx));
|
||||||
if a == b { return Ok(a); }
|
if a == b { return Ok(a); }
|
||||||
let _indenter = indenter();
|
|
||||||
|
let infcx = self.fields.infcx;
|
||||||
|
let a = infcx.type_variables.borrow().replace_if_possible(a);
|
||||||
|
let b = infcx.type_variables.borrow().replace_if_possible(b);
|
||||||
match (&ty::get(a).sty, &ty::get(b).sty) {
|
match (&ty::get(a).sty, &ty::get(b).sty) {
|
||||||
(&ty::ty_bot, _) => {
|
(&ty::ty_bot, _) => {
|
||||||
Ok(a)
|
Ok(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
(&ty::ty_infer(TyVar(a_id)), &ty::ty_infer(TyVar(b_id))) => {
|
(&ty::ty_infer(TyVar(a_id)), &ty::ty_infer(TyVar(b_id))) => {
|
||||||
if_ok!(self.get_ref().var_sub_var(a_id, b_id));
|
infcx.type_variables
|
||||||
|
.borrow_mut()
|
||||||
|
.relate_vars(a_id, SubtypeOf, b_id);
|
||||||
Ok(a)
|
Ok(a)
|
||||||
}
|
}
|
||||||
// The vec/str check here and below is so that we don't unify
|
// The vec/str check here and below is so that we don't unify
|
||||||
|
@ -139,7 +148,9 @@ impl<'f> Combine for Sub<'f> {
|
||||||
Err(ty::terr_sorts(expected_found(self, a, b)))
|
Err(ty::terr_sorts(expected_found(self, a, b)))
|
||||||
}
|
}
|
||||||
(&ty::ty_infer(TyVar(a_id)), _) => {
|
(&ty::ty_infer(TyVar(a_id)), _) => {
|
||||||
if_ok!(self.get_ref().var_sub_t(a_id, b));
|
try!(self.fields
|
||||||
|
.switch_expected()
|
||||||
|
.instantiate(b, SupertypeOf, a_id));
|
||||||
Ok(a)
|
Ok(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +159,7 @@ impl<'f> Combine for Sub<'f> {
|
||||||
Err(ty::terr_sorts(expected_found(self, a, b)))
|
Err(ty::terr_sorts(expected_found(self, a, b)))
|
||||||
}
|
}
|
||||||
(_, &ty::ty_infer(TyVar(b_id))) => {
|
(_, &ty::ty_infer(TyVar(b_id))) => {
|
||||||
if_ok!(self.get_ref().t_sub_var(a, b_id));
|
try!(self.fields.instantiate(a, SubtypeOf, b_id));
|
||||||
Ok(a)
|
Ok(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +175,7 @@ impl<'f> Combine for Sub<'f> {
|
||||||
|
|
||||||
fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
|
fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
|
||||||
debug!("fn_sigs(a={}, b={})",
|
debug!("fn_sigs(a={}, b={})",
|
||||||
a.repr(self.get_ref().infcx.tcx), b.repr(self.get_ref().infcx.tcx));
|
a.repr(self.fields.infcx.tcx), b.repr(self.fields.infcx.tcx));
|
||||||
let _indenter = indenter();
|
let _indenter = indenter();
|
||||||
|
|
||||||
// Rather than checking the subtype relationship between `a` and `b`
|
// Rather than checking the subtype relationship between `a` and `b`
|
||||||
|
@ -176,38 +187,38 @@ impl<'f> Combine for Sub<'f> {
|
||||||
|
|
||||||
// Make a mark so we can examine "all bindings that were
|
// Make a mark so we can examine "all bindings that were
|
||||||
// created as part of this type comparison".
|
// created as part of this type comparison".
|
||||||
let mark = self.get_ref().infcx.region_vars.mark();
|
let mark = self.fields.infcx.region_vars.mark();
|
||||||
|
|
||||||
// First, we instantiate each bound region in the subtype with a fresh
|
// First, we instantiate each bound region in the subtype with a fresh
|
||||||
// region variable.
|
// region variable.
|
||||||
let (a_sig, _) =
|
let (a_sig, _) =
|
||||||
self.get_ref().infcx.replace_late_bound_regions_with_fresh_regions(
|
self.fields.infcx.replace_late_bound_regions_with_fresh_regions(
|
||||||
self.trace(), a);
|
self.trace(), a);
|
||||||
|
|
||||||
// Second, we instantiate each bound region in the supertype with a
|
// Second, we instantiate each bound region in the supertype with a
|
||||||
// fresh concrete region.
|
// fresh concrete region.
|
||||||
let (skol_map, b_sig) = {
|
let (skol_map, b_sig) = {
|
||||||
replace_late_bound_regions_in_fn_sig(self.get_ref().infcx.tcx, b, |br| {
|
replace_late_bound_regions_in_fn_sig(self.fields.infcx.tcx, b, |br| {
|
||||||
let skol = self.get_ref().infcx.region_vars.new_skolemized(br);
|
let skol = self.fields.infcx.region_vars.new_skolemized(br);
|
||||||
debug!("Bound region {} skolemized to {:?}",
|
debug!("Bound region {} skolemized to {:?}",
|
||||||
bound_region_to_string(self.get_ref().infcx.tcx, "", false, br),
|
bound_region_to_string(self.fields.infcx.tcx, "", false, br),
|
||||||
skol);
|
skol);
|
||||||
skol
|
skol
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("a_sig={}", a_sig.repr(self.get_ref().infcx.tcx));
|
debug!("a_sig={}", a_sig.repr(self.fields.infcx.tcx));
|
||||||
debug!("b_sig={}", b_sig.repr(self.get_ref().infcx.tcx));
|
debug!("b_sig={}", b_sig.repr(self.fields.infcx.tcx));
|
||||||
|
|
||||||
// Compare types now that bound regions have been replaced.
|
// Compare types now that bound regions have been replaced.
|
||||||
let sig = if_ok!(super_fn_sigs(self, &a_sig, &b_sig));
|
let sig = try!(super_fn_sigs(self, &a_sig, &b_sig));
|
||||||
|
|
||||||
// Presuming type comparison succeeds, we need to check
|
// Presuming type comparison succeeds, we need to check
|
||||||
// that the skolemized regions do not "leak".
|
// that the skolemized regions do not "leak".
|
||||||
let new_vars =
|
let new_vars =
|
||||||
self.get_ref().infcx.region_vars.vars_created_since_mark(mark);
|
self.fields.infcx.region_vars.vars_created_since_mark(mark);
|
||||||
for (&skol_br, &skol) in skol_map.iter() {
|
for (&skol_br, &skol) in skol_map.iter() {
|
||||||
let tainted = self.get_ref().infcx.region_vars.tainted(mark, skol);
|
let tainted = self.fields.infcx.region_vars.tainted(mark, skol);
|
||||||
for tainted_region in tainted.iter() {
|
for tainted_region in tainted.iter() {
|
||||||
// Each skolemized should only be relatable to itself
|
// Each skolemized should only be relatable to itself
|
||||||
// or new variables:
|
// or new variables:
|
||||||
|
@ -235,5 +246,5 @@ impl<'f> Combine for Sub<'f> {
|
||||||
|
|
||||||
return Ok(sig);
|
return Ok(sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
// Copyright 2014 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;
|
||||||
|
use std::mem;
|
||||||
|
use util::snapshot_vec as sv;
|
||||||
|
|
||||||
|
pub struct TypeVariableTable {
|
||||||
|
values: sv::SnapshotVec<TypeVariableData,UndoEntry,Delegate>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TypeVariableData {
|
||||||
|
value: TypeVariableValue
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TypeVariableValue {
|
||||||
|
Known(ty::t),
|
||||||
|
Bounded(Vec<Relation>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Snapshot {
|
||||||
|
snapshot: sv::Snapshot
|
||||||
|
}
|
||||||
|
|
||||||
|
enum UndoEntry {
|
||||||
|
// The type of the var was specified.
|
||||||
|
SpecifyVar(ty::TyVid, Vec<Relation>),
|
||||||
|
Relate(ty::TyVid, ty::TyVid),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Delegate;
|
||||||
|
|
||||||
|
type Relation = (RelationDir, ty::TyVid);
|
||||||
|
|
||||||
|
#[deriving(PartialEq,Show)]
|
||||||
|
pub enum RelationDir {
|
||||||
|
SubtypeOf, SupertypeOf, EqTo
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelationDir {
|
||||||
|
fn opposite(self) -> RelationDir {
|
||||||
|
match self {
|
||||||
|
SubtypeOf => SupertypeOf,
|
||||||
|
SupertypeOf => SubtypeOf,
|
||||||
|
EqTo => EqTo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeVariableTable {
|
||||||
|
pub fn new() -> TypeVariableTable {
|
||||||
|
TypeVariableTable { values: sv::SnapshotVec::new(Delegate) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn relations<'a>(&'a mut self, a: ty::TyVid) -> &'a mut Vec<Relation> {
|
||||||
|
relations(self.values.get_mut(a.index))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn relate_vars(&mut self, a: ty::TyVid, dir: RelationDir, b: ty::TyVid) {
|
||||||
|
/*!
|
||||||
|
* Records that `a <: b`, `a :> b`, or `a == b`, depending on `dir`.
|
||||||
|
*
|
||||||
|
* Precondition: neither `a` nor `b` are known.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if a != b {
|
||||||
|
self.relations(a).push((dir, b));
|
||||||
|
self.relations(b).push((dir.opposite(), a));
|
||||||
|
self.values.record(Relate(a, b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn instantiate_and_push(
|
||||||
|
&mut self,
|
||||||
|
vid: ty::TyVid,
|
||||||
|
ty: ty::t,
|
||||||
|
stack: &mut Vec<(ty::t, RelationDir, ty::TyVid)>)
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
* Instantiates `vid` with the type `ty` and then pushes an
|
||||||
|
* entry onto `stack` for each of the relations of `vid` to
|
||||||
|
* other variables. The relations will have the form `(ty,
|
||||||
|
* dir, vid1)` where `vid1` is some other variable id.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let old_value = {
|
||||||
|
let value_ptr = &mut self.values.get_mut(vid.index).value;
|
||||||
|
mem::replace(value_ptr, Known(ty))
|
||||||
|
};
|
||||||
|
|
||||||
|
let relations = match old_value {
|
||||||
|
Bounded(b) => b,
|
||||||
|
Known(_) => fail!("Asked to instantiate variable that is \
|
||||||
|
already instantiated")
|
||||||
|
};
|
||||||
|
|
||||||
|
for &(dir, vid) in relations.iter() {
|
||||||
|
stack.push((ty, dir, vid));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.values.record(SpecifyVar(vid, relations));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_var(&mut self) -> ty::TyVid {
|
||||||
|
let index =
|
||||||
|
self.values.push(
|
||||||
|
TypeVariableData { value: Bounded(Vec::new()) });
|
||||||
|
ty::TyVid { index: index }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn probe(&self, vid: ty::TyVid) -> Option<ty::t> {
|
||||||
|
match self.values.get(vid.index).value {
|
||||||
|
Bounded(..) => None,
|
||||||
|
Known(t) => Some(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn replace_if_possible(&self, t: ty::t) -> ty::t {
|
||||||
|
match ty::get(t).sty {
|
||||||
|
ty::ty_infer(ty::TyVar(v)) => {
|
||||||
|
match self.probe(v) {
|
||||||
|
None => t,
|
||||||
|
Some(u) => u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn snapshot(&mut self) -> Snapshot {
|
||||||
|
Snapshot { snapshot: self.values.start_snapshot() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rollback_to(&mut self, s: Snapshot) {
|
||||||
|
self.values.rollback_to(s.snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn commit(&mut self, s: Snapshot) {
|
||||||
|
self.values.commit(s.snapshot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl sv::SnapshotVecDelegate<TypeVariableData,UndoEntry> for Delegate {
|
||||||
|
fn reverse(&mut self,
|
||||||
|
values: &mut Vec<TypeVariableData>,
|
||||||
|
action: UndoEntry) {
|
||||||
|
match action {
|
||||||
|
SpecifyVar(vid, relations) => {
|
||||||
|
values.get_mut(vid.index).value = Bounded(relations);
|
||||||
|
}
|
||||||
|
|
||||||
|
Relate(a, b) => {
|
||||||
|
relations(values.get_mut(a.index)).pop();
|
||||||
|
relations(values.get_mut(b.index)).pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn relations<'a>(v: &'a mut TypeVariableData) -> &'a mut Vec<Relation> {
|
||||||
|
match v.value {
|
||||||
|
Known(_) => fail!("var_sub_var: variable is known"),
|
||||||
|
Bounded(ref mut relations) => relations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,23 +12,23 @@ use std::kinds::marker;
|
||||||
|
|
||||||
use middle::ty::{expected_found, IntVarValue};
|
use middle::ty::{expected_found, IntVarValue};
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use middle::typeck::infer::{Bounds, uok, ures};
|
use middle::typeck::infer::{uok, ures};
|
||||||
use middle::typeck::infer::InferCtxt;
|
use middle::typeck::infer::InferCtxt;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt::Show;
|
use std::fmt::Show;
|
||||||
use std::mem;
|
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use util::ppaux::Repr;
|
use util::ppaux::Repr;
|
||||||
|
use util::snapshot_vec as sv;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This trait is implemented by any type that can serve as a type
|
* This trait is implemented by any type that can serve as a type
|
||||||
* variable. We call such variables *unification keys*. For example,
|
* variable. We call such variables *unification keys*. For example,
|
||||||
* this trait is implemented by `TyVid`, which represents normal
|
* this trait is implemented by `IntVid`, which represents integral
|
||||||
* type variables, and `IntVid`, which represents integral variables.
|
* variables.
|
||||||
*
|
*
|
||||||
* Each key type has an associated value type `V`. For example,
|
* Each key type has an associated value type `V`. For example, for
|
||||||
* for `TyVid`, this is `Bounds<ty::t>`, representing a pair of
|
* `IntVid`, this is `Option<IntVarValue>`, representing some
|
||||||
* upper- and lower-bound types.
|
* (possibly not yet known) sort of integer.
|
||||||
*
|
*
|
||||||
* Implementations of this trait are at the end of this file.
|
* Implementations of this trait are at the end of this file.
|
||||||
*/
|
*/
|
||||||
|
@ -48,11 +48,10 @@ pub trait UnifyKey<V> : Clone + Show + PartialEq + Repr {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trait for valid types that a type variable can be set to. Note
|
* Trait for valid types that a type variable can be set to. Note that
|
||||||
* that this is typically not the end type that the value will
|
* this is typically not the end type that the value will take on, but
|
||||||
* take on, but rather some wrapper: for example, for normal type
|
* rather an `Option` wrapper (where `None` represents a variable
|
||||||
* variables, the associated type is not `ty::t` but rather
|
* whose value is not yet set).
|
||||||
* `Bounds<ty::t>`.
|
|
||||||
*
|
*
|
||||||
* Implementations of this trait are at the end of this file.
|
* Implementations of this trait are at the end of this file.
|
||||||
*/
|
*/
|
||||||
|
@ -82,13 +81,8 @@ pub struct UnificationTable<K,V> {
|
||||||
/**
|
/**
|
||||||
* Indicates the current value of each key.
|
* Indicates the current value of each key.
|
||||||
*/
|
*/
|
||||||
values: Vec<VarValue<K,V>>,
|
|
||||||
|
|
||||||
/**
|
values: sv::SnapshotVec<VarValue<K,V>,(),Delegate>,
|
||||||
* When a snapshot is active, logs each change made to the table
|
|
||||||
* so that they can be unrolled.
|
|
||||||
*/
|
|
||||||
undo_log: Vec<UndoLog<K,V>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,29 +90,9 @@ pub struct UnificationTable<K,V> {
|
||||||
* made during the snapshot may either be *committed* or *rolled back*.
|
* made during the snapshot may either be *committed* or *rolled back*.
|
||||||
*/
|
*/
|
||||||
pub struct Snapshot<K> {
|
pub struct Snapshot<K> {
|
||||||
// Ensure that this snapshot is keyed to the table type.
|
// Link snapshot to the key type `K` of the table.
|
||||||
marker1: marker::CovariantType<K>,
|
marker: marker::CovariantType<K>,
|
||||||
|
snapshot: sv::Snapshot,
|
||||||
// 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,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -131,85 +105,36 @@ pub struct Node<K,V> {
|
||||||
pub rank: uint,
|
pub rank: uint,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Delegate;
|
||||||
|
|
||||||
// We can't use V:LatticeValue, much as I would like to,
|
// We can't use V:LatticeValue, much as I would like to,
|
||||||
// because frequently the pattern is that V=Bounds<U> for some
|
// because frequently the pattern is that V=Option<U> for some
|
||||||
// other type parameter U, and we have no way to say
|
// other type parameter U, and we have no way to say
|
||||||
// Bounds<U>:
|
// Option<U>:LatticeValue.
|
||||||
|
|
||||||
impl<V:PartialEq+Clone+Repr,K:UnifyKey<V>> UnificationTable<K,V> {
|
impl<V:PartialEq+Clone+Repr,K:UnifyKey<V>> UnificationTable<K,V> {
|
||||||
pub fn new() -> UnificationTable<K,V> {
|
pub fn new() -> UnificationTable<K,V> {
|
||||||
UnificationTable {
|
UnificationTable {
|
||||||
values: Vec::new(),
|
values: sv::SnapshotVec::new(Delegate),
|
||||||
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
|
* Starts a new snapshot. Each snapshot must be either
|
||||||
* rolled back or committed in a "LIFO" (stack) order.
|
* rolled back or committed in a "LIFO" (stack) order.
|
||||||
*/
|
*/
|
||||||
pub fn snapshot(&mut self) -> Snapshot<K> {
|
pub fn snapshot(&mut self) -> Snapshot<K> {
|
||||||
let length = self.undo_log.len();
|
Snapshot { marker: marker::CovariantType::<K>,
|
||||||
debug!("{}: snapshot at length {}",
|
snapshot: self.values.start_snapshot() }
|
||||||
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
|
* Reverses all changes since the last snapshot. Also
|
||||||
* removes any keys that have been created since then.
|
* removes any keys that have been created since then.
|
||||||
*/
|
*/
|
||||||
pub fn rollback_to(&mut self, tcx: &ty::ctxt, snapshot: Snapshot<K>) {
|
pub fn rollback_to(&mut self, snapshot: Snapshot<K>) {
|
||||||
debug!("{}: rollback_to({})",
|
debug!("{}: rollback_to()", UnifyKey::tag(None::<K>));
|
||||||
UnifyKey::tag(None::<K>),
|
self.values.rollback_to(snapshot.snapshot);
|
||||||
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 uncommitted snapshot");
|
|
||||||
}
|
|
||||||
|
|
||||||
CommittedSnapshot => {
|
|
||||||
// This occurs when there are nested snapshots and
|
|
||||||
// the inner is committed 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -217,28 +142,12 @@ impl<V:PartialEq+Clone+Repr,K:UnifyKey<V>> UnificationTable<K,V> {
|
||||||
* can still be undone if there is a snapshot further out.
|
* can still be undone if there is a snapshot further out.
|
||||||
*/
|
*/
|
||||||
pub fn commit(&mut self, snapshot: Snapshot<K>) {
|
pub fn commit(&mut self, snapshot: Snapshot<K>) {
|
||||||
debug!("{}: commit({})",
|
debug!("{}: commit()", UnifyKey::tag(None::<K>));
|
||||||
UnifyKey::tag(None::<K>),
|
self.values.commit(snapshot.snapshot);
|
||||||
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 {
|
pub fn new_key(&mut self, value: V) -> K {
|
||||||
let index = self.values.len();
|
let index = self.values.push(Root(value, 0));
|
||||||
|
|
||||||
if self.in_snapshot() {
|
|
||||||
self.undo_log.push(NewVar(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.values.push(Root(value, 0));
|
|
||||||
let k = UnifyKey::from_index(index);
|
let k = UnifyKey::from_index(index);
|
||||||
debug!("{}: created new key: {}",
|
debug!("{}: created new key: {}",
|
||||||
UnifyKey::tag(None::<K>),
|
UnifyKey::tag(None::<K>),
|
||||||
|
@ -246,20 +155,6 @@ impl<V:PartialEq+Clone+Repr,K:UnifyKey<V>> UnificationTable<K,V> {
|
||||||
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> {
|
pub fn get(&mut self, tcx: &ty::ctxt, vid: K) -> Node<K,V> {
|
||||||
/*!
|
/*!
|
||||||
* Find the root node for `vid`. This uses the standard
|
* Find the root node for `vid`. This uses the standard
|
||||||
|
@ -274,15 +169,7 @@ impl<V:PartialEq+Clone+Repr,K:UnifyKey<V>> UnificationTable<K,V> {
|
||||||
let node: Node<K,V> = self.get(tcx, redirect.clone());
|
let node: Node<K,V> = self.get(tcx, redirect.clone());
|
||||||
if node.key != redirect {
|
if node.key != redirect {
|
||||||
// Path compression
|
// Path compression
|
||||||
let old_value =
|
self.values.set(index, Redirect(node.key.clone()));
|
||||||
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
|
node
|
||||||
}
|
}
|
||||||
|
@ -310,15 +197,12 @@ impl<V:PartialEq+Clone+Repr,K:UnifyKey<V>> UnificationTable<K,V> {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
assert!(self.is_root(&key));
|
assert!(self.is_root(&key));
|
||||||
assert!(self.in_snapshot());
|
|
||||||
|
|
||||||
debug!("Updating variable {} to {}",
|
debug!("Updating variable {} to {}",
|
||||||
key.repr(tcx),
|
key.repr(tcx),
|
||||||
new_value.repr(tcx));
|
new_value.repr(tcx));
|
||||||
|
|
||||||
let index = key.index();
|
self.values.set(key.index(), new_value);
|
||||||
let old_value = self.swap_value(index, new_value);
|
|
||||||
self.undo_log.push(SetVar(index, old_value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unify(&mut self,
|
pub fn unify(&mut self,
|
||||||
|
@ -359,6 +243,12 @@ impl<V:PartialEq+Clone+Repr,K:UnifyKey<V>> UnificationTable<K,V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<K,V> sv::SnapshotVecDelegate<VarValue<K,V>,()> for Delegate {
|
||||||
|
fn reverse(&mut self, _: &mut Vec<VarValue<K,V>>, _: ()) {
|
||||||
|
fail!("Nothing to reverse");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Code to handle simple keys 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.
|
// doesn't have a subtyping relationship we need to worry about.
|
||||||
|
@ -373,7 +263,8 @@ pub trait SimplyUnifiable : Clone + PartialEq + Repr {
|
||||||
|
|
||||||
pub fn err<V:SimplyUnifiable>(a_is_expected: bool,
|
pub fn err<V:SimplyUnifiable>(a_is_expected: bool,
|
||||||
a_t: V,
|
a_t: V,
|
||||||
b_t: V) -> ures {
|
b_t: V)
|
||||||
|
-> ures {
|
||||||
if a_is_expected {
|
if a_is_expected {
|
||||||
Err(SimplyUnifiable::to_type_err(
|
Err(SimplyUnifiable::to_type_err(
|
||||||
ty::expected_found {expected: a_t, found: b_t}))
|
ty::expected_found {expected: a_t, found: b_t}))
|
||||||
|
@ -483,26 +374,6 @@ impl<'tcx,V:SimplyUnifiable,K:UnifyKey<Option<V>>>
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// 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 } }
|
|
||||||
|
|
||||||
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 UnifyValue for Bounds<ty::t> { }
|
|
||||||
|
|
||||||
// Integral type keys
|
// Integral type keys
|
||||||
|
|
||||||
impl UnifyKey<Option<IntVarValue>> for ty::IntVid {
|
impl UnifyKey<Option<IntVarValue>> for ty::IntVid {
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* A utility class for implementing "snapshottable" things; a
|
||||||
|
* snapshottable data structure permits you to take a snapshot (via
|
||||||
|
* `start_snapshot`) and then, after making some changes, elect either
|
||||||
|
* to rollback to the start of the snapshot or commit those changes.
|
||||||
|
*
|
||||||
|
* This vector is intended to be used as part of an abstraction, not
|
||||||
|
* serve as a complete abstraction on its own. As such, while it will
|
||||||
|
* roll back most changes on its own, it also supports a `get_mut`
|
||||||
|
* operation that gives you an abitrary mutable pointer into the
|
||||||
|
* vector. To ensure that any changes you make this with this pointer
|
||||||
|
* are rolled back, you must invoke `record` to record any changes you
|
||||||
|
* make and also supplying a delegate capable of reversing those
|
||||||
|
* changes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::kinds::marker;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
#[deriving(PartialEq)]
|
||||||
|
enum UndoLog<T,U> {
|
||||||
|
/// Indicates where a snapshot started.
|
||||||
|
OpenSnapshot,
|
||||||
|
|
||||||
|
/// Indicates a snapshot that has been committed.
|
||||||
|
CommittedSnapshot,
|
||||||
|
|
||||||
|
/// New variable with given index was created.
|
||||||
|
NewElem(uint),
|
||||||
|
|
||||||
|
/// Variable with given index was changed *from* the given value.
|
||||||
|
SetElem(uint, T),
|
||||||
|
|
||||||
|
/// Extensible set of actions
|
||||||
|
Other(U)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SnapshotVec<T,U,D> {
|
||||||
|
values: Vec<T>,
|
||||||
|
undo_log: Vec<UndoLog<T,U>>,
|
||||||
|
delegate: D
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Snapshot {
|
||||||
|
// Snapshots are tokens that should be created/consumed linearly.
|
||||||
|
marker: marker::NoCopy,
|
||||||
|
|
||||||
|
// Length of the undo log at the time the snapshot was taken.
|
||||||
|
length: uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SnapshotVecDelegate<T,U> {
|
||||||
|
fn reverse(&mut self, values: &mut Vec<T>, action: U);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T,U,D:SnapshotVecDelegate<T,U>> SnapshotVec<T,U,D> {
|
||||||
|
pub fn new(delegate: D) -> SnapshotVec<T,U,D> {
|
||||||
|
SnapshotVec {
|
||||||
|
values: Vec::new(),
|
||||||
|
undo_log: Vec::new(),
|
||||||
|
delegate: delegate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn in_snapshot(&self) -> bool {
|
||||||
|
!self.undo_log.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn record(&mut self, action: U) {
|
||||||
|
if self.in_snapshot() {
|
||||||
|
self.undo_log.push(Other(action));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, elem: T) -> uint {
|
||||||
|
let len = self.values.len();
|
||||||
|
self.values.push(elem);
|
||||||
|
|
||||||
|
if self.in_snapshot() {
|
||||||
|
self.undo_log.push(NewElem(len));
|
||||||
|
}
|
||||||
|
|
||||||
|
len
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get<'a>(&'a self, index: uint) -> &'a T {
|
||||||
|
self.values.get(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut<'a>(&'a mut self, index: uint) -> &'a mut T {
|
||||||
|
/*!
|
||||||
|
* Returns a mutable pointer into the vec; whatever changes
|
||||||
|
* you make here cannot be undone automatically, so you should
|
||||||
|
* be sure call `record()` with some sort of suitable undo
|
||||||
|
* action.
|
||||||
|
*/
|
||||||
|
|
||||||
|
self.values.get_mut(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, index: uint, new_elem: T) {
|
||||||
|
/*!
|
||||||
|
* Updates the element at the given index. The old value will
|
||||||
|
* saved (and perhaps restored) if a snapshot is active.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let old_elem = mem::replace(self.values.get_mut(index), new_elem);
|
||||||
|
if self.in_snapshot() {
|
||||||
|
self.undo_log.push(SetElem(index, old_elem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_snapshot(&mut self) -> Snapshot {
|
||||||
|
let length = self.undo_log.len();
|
||||||
|
self.undo_log.push(OpenSnapshot);
|
||||||
|
Snapshot { length: length,
|
||||||
|
marker: marker::NoCopy }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_open_snapshot(&self, snapshot: &Snapshot) {
|
||||||
|
// Or else there was a failure to follow a stack discipline:
|
||||||
|
assert!(self.undo_log.len() > snapshot.length);
|
||||||
|
|
||||||
|
// Invariant established by start_snapshot():
|
||||||
|
assert!(
|
||||||
|
match *self.undo_log.get(snapshot.length) {
|
||||||
|
OpenSnapshot => true,
|
||||||
|
_ => false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rollback_to(&mut self, snapshot: Snapshot) {
|
||||||
|
debug!("rollback_to({})", 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.
|
||||||
|
fail!("Cannot rollback an uncommited snapshot");
|
||||||
|
}
|
||||||
|
|
||||||
|
CommittedSnapshot => {
|
||||||
|
// This occurs when there are nested snapshots and
|
||||||
|
// the inner is commited but outer is rolled back.
|
||||||
|
}
|
||||||
|
|
||||||
|
NewElem(i) => {
|
||||||
|
self.values.pop();
|
||||||
|
assert!(self.values.len() == i);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetElem(i, v) => {
|
||||||
|
*self.values.get_mut(i) = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
Other(u) => {
|
||||||
|
self.delegate.reverse(&mut self.values, u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let v = self.undo_log.pop().unwrap();
|
||||||
|
assert!(match v { OpenSnapshot => true, _ => false });
|
||||||
|
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) {
|
||||||
|
debug!("commit({})", 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,8 +23,7 @@ fn a(x: &int) {
|
||||||
let c1 = || set(&mut *x);
|
let c1 = || set(&mut *x);
|
||||||
//~^ ERROR cannot borrow
|
//~^ ERROR cannot borrow
|
||||||
let c2 = || set(&mut *x);
|
let c2 = || set(&mut *x);
|
||||||
//~^ ERROR closure requires unique access to `x`
|
//~^ ERROR cannot borrow
|
||||||
//~^^ ERROR cannot borrow
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -17,7 +17,7 @@ struct S<'a> {
|
||||||
|
|
||||||
fn copy_borrowed_ptr<'a,'b>(p: &'a mut S<'b>) -> S<'b> {
|
fn copy_borrowed_ptr<'a,'b>(p: &'a mut S<'b>) -> S<'b> {
|
||||||
S { pointer: &mut *p.pointer }
|
S { pointer: &mut *p.pointer }
|
||||||
//~^ ERROR lifetime of `p` is too short to guarantee its contents can be safely reborrowed
|
//~^ ERROR cannot infer
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -12,7 +12,7 @@ fn main() {
|
||||||
let x = [1,2];
|
let x = [1,2];
|
||||||
let y = match x {
|
let y = match x {
|
||||||
[] => None,
|
[] => None,
|
||||||
//~^ ERROR expected `[<generic integer #1>, .. 2]`, found a fixed vector pattern of size 0
|
//~^ ERROR expected `[<generic integer #0>, .. 2]`, found a fixed vector pattern of size 0
|
||||||
[a,_] => Some(a)
|
[a,_] => Some(a)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,6 @@ use std::raw::Slice;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let Slice { data: data, len: len } = "foo";
|
let Slice { data: data, len: len } = "foo";
|
||||||
//~^ ERROR mismatched types: expected `&'static str`, found a structure pattern
|
//~^ ERROR mismatched types: expected `&str`, found a structure pattern
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,5 +22,5 @@ impl<A> vec_monad<A> for Vec<A> {
|
||||||
}
|
}
|
||||||
fn main() {
|
fn main() {
|
||||||
["hi"].bind(|x| [x] );
|
["hi"].bind(|x| [x] );
|
||||||
//~^ ERROR type `[&'static str, .. 1]` does not implement any method in scope named `bind`
|
//~^ ERROR type `[&str, .. 1]` does not implement any method in scope named `bind`
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
// Test that Copy bounds inherited by trait are checked.
|
||||||
|
|
||||||
|
use std::any::Any;
|
||||||
|
use std::any::AnyRefExt;
|
||||||
|
|
||||||
|
trait Foo : Copy {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T:Copy> Foo for T {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_param<T:Foo>(foo: &T) { }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = box 3i;
|
||||||
|
take_param(&x); //~ ERROR does not fulfill `Copy`
|
||||||
|
|
||||||
|
let y = &x;
|
||||||
|
let z = &x as &Foo; //~ ERROR does not fulfill `Copy`
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
// Test which object types are considered sendable. This test
|
||||||
|
// is broken into two parts because some errors occur in distinct
|
||||||
|
// phases in the compiler. See kindck-send-object2.rs as well!
|
||||||
|
|
||||||
|
fn assert_send<T:Send>() { }
|
||||||
|
trait Dummy { }
|
||||||
|
|
||||||
|
// careful with object types, who knows what they close over...
|
||||||
|
fn test51<'a>() {
|
||||||
|
assert_send::<&'a Dummy>(); //~ ERROR does not fulfill the required lifetime
|
||||||
|
}
|
||||||
|
fn test52<'a>() {
|
||||||
|
assert_send::<&'a Dummy+Send>(); //~ ERROR does not fulfill the required lifetime
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...unless they are properly bounded
|
||||||
|
fn test60() {
|
||||||
|
assert_send::<&'static Dummy+Send>();
|
||||||
|
}
|
||||||
|
fn test61() {
|
||||||
|
assert_send::<Box<Dummy+Send>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// closure and object types can have lifetime bounds which make
|
||||||
|
// them not ok
|
||||||
|
fn test_70<'a>() {
|
||||||
|
assert_send::<proc():'a>(); //~ ERROR does not fulfill the required lifetime
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_71<'a>() {
|
||||||
|
assert_send::<Box<Dummy+'a>>(); //~ ERROR does not fulfill the required lifetime
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() { }
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
// Continue kindck-send-object1.rs.
|
||||||
|
|
||||||
|
fn assert_send<T:Send>() { }
|
||||||
|
trait Dummy { }
|
||||||
|
|
||||||
|
fn test50() {
|
||||||
|
assert_send::<&'static Dummy>(); //~ ERROR does not fulfill `Send`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test53() {
|
||||||
|
assert_send::<Box<Dummy>>(); //~ ERROR does not fulfill `Send`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...unless they are properly bounded
|
||||||
|
fn test60() {
|
||||||
|
assert_send::<&'static Dummy+Send>();
|
||||||
|
}
|
||||||
|
fn test61() {
|
||||||
|
assert_send::<Box<Dummy+Send>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() { }
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
// Test which of the builtin types are considered sendable.
|
||||||
|
|
||||||
|
fn assert_send<T:Send>() { }
|
||||||
|
|
||||||
|
// owned content are ok
|
||||||
|
fn test30() { assert_send::<Box<int>>(); }
|
||||||
|
fn test31() { assert_send::<String>(); }
|
||||||
|
fn test32() { assert_send::<Vec<int> >(); }
|
||||||
|
|
||||||
|
// but not if they own a bad thing
|
||||||
|
fn test40<'a>(_: &'a int) {
|
||||||
|
assert_send::<Box<&'a int>>(); //~ ERROR does not fulfill the required lifetime
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() { }
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
// Test that borrowed pointers are not sendable unless 'static.
|
||||||
|
|
||||||
|
fn assert_send<T:Send>() { }
|
||||||
|
|
||||||
|
// lifetime pointers with 'static lifetime are ok
|
||||||
|
fn test01() { assert_send::<&'static int>(); }
|
||||||
|
fn test02() { assert_send::<&'static str>(); }
|
||||||
|
fn test03() { assert_send::<&'static [int]>(); }
|
||||||
|
|
||||||
|
// whether or not they are mutable
|
||||||
|
fn test10() { assert_send::<&'static mut int>(); }
|
||||||
|
|
||||||
|
// otherwise lifetime pointers are not ok
|
||||||
|
fn test20<'a>(_: &'a int) {
|
||||||
|
assert_send::<&'a int>(); //~ ERROR does not fulfill the required lifetime
|
||||||
|
}
|
||||||
|
fn test21<'a>(_: &'a int) {
|
||||||
|
assert_send::<&'a str>(); //~ ERROR does not fulfill the required lifetime
|
||||||
|
}
|
||||||
|
fn test22<'a>(_: &'a int) {
|
||||||
|
assert_send::<&'a [int]>(); //~ ERROR does not fulfill the required lifetime
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() { }
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||||
// file at the top-level directory of this distribution and at
|
// file at the top-level directory of this distribution and at
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
//
|
//
|
||||||
|
@ -8,13 +8,15 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
#![macro_escape]
|
fn assert_send<T:Send>() { }
|
||||||
|
|
||||||
macro_rules! if_ok(
|
// unsafe ptrs are ok unless they point at unsendable things
|
||||||
($inp: expr) => (
|
fn test70() {
|
||||||
match $inp {
|
assert_send::<*mut int>();
|
||||||
Ok(v) => { v }
|
}
|
||||||
Err(e) => { return Err(e); }
|
fn test71<'a>() {
|
||||||
|
assert_send::<*mut &'a int>(); //~ ERROR does not fulfill the required lifetime
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
}
|
}
|
||||||
)
|
|
||||||
)
|
|
|
@ -17,12 +17,10 @@ struct a_class<'a> { x:&'a int }
|
||||||
|
|
||||||
fn a_fn1<'a,'b>(e: an_enum<'a>) -> an_enum<'b> {
|
fn a_fn1<'a,'b>(e: an_enum<'a>) -> an_enum<'b> {
|
||||||
return e; //~ ERROR mismatched types: expected `an_enum<'b>`, found `an_enum<'a>`
|
return e; //~ ERROR mismatched types: expected `an_enum<'b>`, found `an_enum<'a>`
|
||||||
//~^ ERROR cannot infer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn a_fn3<'a,'b>(e: a_class<'a>) -> a_class<'b> {
|
fn a_fn3<'a,'b>(e: a_class<'a>) -> a_class<'b> {
|
||||||
return e; //~ ERROR mismatched types: expected `a_class<'b>`, found `a_class<'a>`
|
return e; //~ ERROR mismatched types: expected `a_class<'b>`, found `a_class<'a>`
|
||||||
//~^ ERROR cannot infer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() { }
|
fn main() { }
|
||||||
|
|
|
@ -15,7 +15,6 @@ fn with_int(f: |x: &int|) {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut x = None;
|
let mut x = None;
|
||||||
//~^ ERROR lifetime of variable does not enclose its declaration
|
|
||||||
//~^^ ERROR type of expression contains references that are not valid during the expression
|
|
||||||
with_int(|y| x = Some(y));
|
with_int(|y| x = Some(y));
|
||||||
|
//~^ ERROR cannot infer
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,6 @@ fn with_int(f: |x: &int|) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut x: Option<&int> = None; //~ ERROR cannot infer
|
let mut x: Option<&int> = None;
|
||||||
with_int(|y| x = Some(y));
|
with_int(|y| x = Some(y)); //~ ERROR cannot infer
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ fn with<R:Deref>(f: |x: &int| -> R) -> int {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_it() -> int {
|
fn return_it() -> int {
|
||||||
with(|o| o) //~ ERROR cannot infer an appropriate lifetime
|
with(|o| o) //~ ERROR cannot infer
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -22,7 +22,6 @@ struct not_parameterized2 {
|
||||||
|
|
||||||
fn take1<'a>(p: parameterized1) -> parameterized1<'a> { p }
|
fn take1<'a>(p: parameterized1) -> parameterized1<'a> { p }
|
||||||
//~^ ERROR mismatched types
|
//~^ ERROR mismatched types
|
||||||
//~^^ ERROR cannot infer
|
|
||||||
|
|
||||||
fn take3(p: not_parameterized1) -> not_parameterized1 { p }
|
fn take3(p: not_parameterized1) -> not_parameterized1 { p }
|
||||||
fn take4(p: not_parameterized2) -> not_parameterized2 { p }
|
fn take4(p: not_parameterized2) -> not_parameterized2 { p }
|
||||||
|
|
|
@ -33,7 +33,6 @@ fn use_<'short,'long>(c: Contravariant<'short>,
|
||||||
// covariant with respect to its parameter 'a.
|
// covariant with respect to its parameter 'a.
|
||||||
|
|
||||||
let _: Contravariant<'long> = c; //~ ERROR mismatched types
|
let _: Contravariant<'long> = c; //~ ERROR mismatched types
|
||||||
//~^ ERROR cannot infer an appropriate lifetime
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -30,7 +30,6 @@ fn use_<'short,'long>(c: Covariant<'long>,
|
||||||
// contravariant with respect to its parameter 'a.
|
// contravariant with respect to its parameter 'a.
|
||||||
|
|
||||||
let _: Covariant<'short> = c; //~ ERROR mismatched types
|
let _: Covariant<'short> = c; //~ ERROR mismatched types
|
||||||
//~^ ERROR cannot infer an appropriate lifetime
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -23,11 +23,9 @@ struct indirect2<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_direct<'a,'b>(p: direct<'a>) -> direct<'b> { p } //~ ERROR mismatched types
|
fn take_direct<'a,'b>(p: direct<'a>) -> direct<'b> { p } //~ ERROR mismatched types
|
||||||
//~^ ERROR cannot infer
|
|
||||||
|
|
||||||
fn take_indirect1(p: indirect1) -> indirect1 { p }
|
fn take_indirect1(p: indirect1) -> indirect1 { p }
|
||||||
|
|
||||||
fn take_indirect2<'a,'b>(p: indirect2<'a>) -> indirect2<'b> { p } //~ ERROR mismatched types
|
fn take_indirect2<'a,'b>(p: indirect2<'a>) -> indirect2<'b> { p } //~ ERROR mismatched types
|
||||||
//~^ ERROR cannot infer
|
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -22,18 +22,17 @@ struct c<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
trait set_f<'a> {
|
trait set_f<'a> {
|
||||||
fn set_f_ok(&self, b: Gc<b<'a>>);
|
fn set_f_ok(&mut self, b: Gc<b<'a>>);
|
||||||
fn set_f_bad(&self, b: Gc<b>);
|
fn set_f_bad(&mut self, b: Gc<b>);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> set_f<'a> for c<'a> {
|
impl<'a> set_f<'a> for c<'a> {
|
||||||
fn set_f_ok(&self, b: Gc<b<'a>>) {
|
fn set_f_ok(&mut self, b: Gc<b<'a>>) {
|
||||||
self.f = b;
|
self.f = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_f_bad(&self, b: Gc<b>) {
|
fn set_f_bad(&mut self, b: Gc<b>) {
|
||||||
self.f = b; //~ ERROR mismatched types: expected `Gc<Gc<&'a int>>`, found `Gc<Gc<&int>>`
|
self.f = b; //~ ERROR mismatched types: expected `Gc<Gc<&'a int>>`, found `Gc<Gc<&int>>`
|
||||||
//~^ ERROR cannot infer
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
// Test that, when a variable of type `&T` is captured inside a proc,
|
||||||
|
// we correctly infer/require that its lifetime is 'static.
|
||||||
|
|
||||||
|
fn foo(_p: proc():'static) { }
|
||||||
|
|
||||||
|
static i: int = 3;
|
||||||
|
|
||||||
|
fn capture_local() {
|
||||||
|
let x = 3i;
|
||||||
|
let y = &x; //~ ERROR `x` does not live long enough
|
||||||
|
foo(proc() {
|
||||||
|
let _a = *y;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn capture_static() {
|
||||||
|
// Legal because &i can have static lifetime:
|
||||||
|
let y = &i;
|
||||||
|
foo(proc() {
|
||||||
|
let _a = *y;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() { }
|
|
@ -11,7 +11,7 @@
|
||||||
// Issue #8624. Test for reborrowing with 3 levels, not just two.
|
// Issue #8624. Test for reborrowing with 3 levels, not just two.
|
||||||
|
|
||||||
fn copy_borrowed_ptr<'a, 'b, 'c>(p: &'a mut &'b mut &'c mut int) -> &'b mut int {
|
fn copy_borrowed_ptr<'a, 'b, 'c>(p: &'a mut &'b mut &'c mut int) -> &'b mut int {
|
||||||
&mut ***p //~ ERROR lifetime of `p` is too short to guarantee its contents
|
&mut ***p //~ ERROR cannot infer
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
// for `'a` (which must be a sublifetime of `'b`).
|
// for `'a` (which must be a sublifetime of `'b`).
|
||||||
|
|
||||||
fn copy_borrowed_ptr<'a, 'b>(p: &'a mut &'b mut int) -> &'b mut int {
|
fn copy_borrowed_ptr<'a, 'b>(p: &'a mut &'b mut int) -> &'b mut int {
|
||||||
&mut **p //~ ERROR lifetime of `p` is too short
|
&mut **p //~ ERROR cannot infer
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -19,8 +19,6 @@ fn with<R>(f: <'a>|x: &'a int| -> R) -> R {
|
||||||
fn return_it<'a>() -> &'a int {
|
fn return_it<'a>() -> &'a int {
|
||||||
with(|o| o)
|
with(|o| o)
|
||||||
//~^ ERROR cannot infer
|
//~^ ERROR cannot infer
|
||||||
//~^^ ERROR not valid during the expression
|
|
||||||
//~^^^ ERROR not valid at this point
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -22,8 +22,6 @@ fn with<R>(f: |x: &int| -> R) -> R {
|
||||||
fn return_it<'a>() -> &'a int {
|
fn return_it<'a>() -> &'a int {
|
||||||
with(|o| o)
|
with(|o| o)
|
||||||
//~^ ERROR cannot infer
|
//~^ ERROR cannot infer
|
||||||
//~^^ ERROR not valid during the expression
|
|
||||||
//~^^^ ERROR not valid at this point
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -33,7 +33,6 @@ fn use_<'short,'long>(c: S<'long, 'short>,
|
||||||
// covariant with respect to its parameter 'a.
|
// covariant with respect to its parameter 'a.
|
||||||
|
|
||||||
let _: S<'long, 'long> = c; //~ ERROR mismatched types
|
let _: S<'long, 'long> = c; //~ ERROR mismatched types
|
||||||
//~^ ERROR cannot infer an appropriate lifetime
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -31,7 +31,6 @@ fn use_<'short,'long>(c: Contravariant<'short>,
|
||||||
// covariant with respect to its parameter 'a.
|
// covariant with respect to its parameter 'a.
|
||||||
|
|
||||||
let _: Contravariant<'long> = c; //~ ERROR mismatched types
|
let _: Contravariant<'long> = c; //~ ERROR mismatched types
|
||||||
//~^ ERROR cannot infer an appropriate lifetime
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -31,7 +31,6 @@ fn use_<'short,'long>(c: Covariant<'long>,
|
||||||
// contravariant with respect to its parameter 'a.
|
// contravariant with respect to its parameter 'a.
|
||||||
|
|
||||||
let _: Covariant<'short> = c; //~ ERROR mismatched types
|
let _: Covariant<'short> = c; //~ ERROR mismatched types
|
||||||
//~^ ERROR cannot infer an appropriate lifetime
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
// Test an edge case in region inference: the lifetime of the borrow
|
||||||
|
// of `*x` must be extended to at least 'a.
|
||||||
|
|
||||||
|
fn foo<'a,'b>(x: &'a &'b mut int) -> &'a int {
|
||||||
|
let y = &*x; // should be inferred to have type &'a &'b mut int...
|
||||||
|
|
||||||
|
// ...because if we inferred, say, &'x &'b mut int where 'x <= 'a,
|
||||||
|
// this reborrow would be illegal:
|
||||||
|
&**y
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
/* Just want to know that it compiles. */
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
// Check that the 'static bound on a proc influences lifetimes of
|
||||||
|
// region variables contained within (otherwise, region inference will
|
||||||
|
// give `x` a very short lifetime).
|
||||||
|
|
||||||
|
static i: uint = 3;
|
||||||
|
fn foo(_: proc():'static) {}
|
||||||
|
fn read(_: uint) { }
|
||||||
|
pub fn main() {
|
||||||
|
let x = &i;
|
||||||
|
foo(proc() {
|
||||||
|
read(*x);
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
// This is an example where the older inference algorithm failed. The
|
||||||
|
// specifics of why it failed are somewhat, but not entirely, tailed
|
||||||
|
// to the algorithm. Ultimately the problem is that when computing the
|
||||||
|
// mutual supertype of both sides of the `if` it would be faced with a
|
||||||
|
// choice of tightening bounds or unifying variables and it took the
|
||||||
|
// wrong path. The new algorithm avoids this problem and hence this
|
||||||
|
// example typechecks correctly.
|
||||||
|
|
||||||
|
enum ScopeChain<'a> {
|
||||||
|
Link(Scope<'a>),
|
||||||
|
End
|
||||||
|
}
|
||||||
|
|
||||||
|
type Scope<'a> = &'a ScopeChain<'a>;
|
||||||
|
|
||||||
|
struct OuterContext;
|
||||||
|
|
||||||
|
struct Context<'a> {
|
||||||
|
foo: &'a OuterContext
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Context<'a> {
|
||||||
|
fn foo(&mut self, scope: Scope) {
|
||||||
|
let link = if 1i < 2 {
|
||||||
|
let l = Link(scope);
|
||||||
|
self.take_scope(&l);
|
||||||
|
l
|
||||||
|
} else {
|
||||||
|
Link(scope)
|
||||||
|
};
|
||||||
|
self.take_scope(&link);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_scope(&mut self, x: Scope) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() { }
|
Loading…
Reference in New Issue