diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 9b3cc2b6a0a..ebce1a2a69b 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -136,6 +136,7 @@ pub mod util { pub mod common; pub mod ppaux; pub mod nodemap; + pub mod snapshot_vec; } pub mod lib { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index c2d0d27be6d..6f2dd6e0992 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -30,7 +30,6 @@ use middle::subst::{Subst, Substs, VecPerParamSpace}; use middle::subst; use middle::ty; use middle::typeck; -use middle::typeck::MethodCall; use middle::ty_fold; use middle::ty_fold::{TypeFoldable,TypeFolder}; use middle; diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 72f33a2f984..f54e650a173 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -1381,8 +1381,8 @@ fn link_by_ref(rcx: &Rcx, expr.repr(tcx), callee_scope); let mc = mc::MemCategorizationContext::new(rcx); let expr_cmt = ignore_err!(mc.cat_expr(expr)); - let region_min = ty::ReScope(callee_scope); - link_region(rcx, expr.span, region_min, ty::ImmBorrow, expr_cmt); + let borrow_region = ty::ReScope(callee_scope); + link_region(rcx, expr.span, borrow_region, ty::ImmBorrow, expr_cmt); } fn link_region_from_node_type(rcx: &Rcx, @@ -1408,102 +1408,54 @@ fn link_region_from_node_type(rcx: &Rcx, fn link_region(rcx: &Rcx, span: Span, - region_min: ty::Region, - kind: ty::BorrowKind, - cmt_borrowed: mc::cmt) { + borrow_region: ty::Region, + borrow_kind: ty::BorrowKind, + borrow_cmt: mc::cmt) { /*! - * Informs the inference engine that a borrow of `cmt` - * must have the borrow kind `kind` and lifetime `region_min`. - * If `cmt` is a deref of a region pointer with - * lifetime `r_borrowed`, this will add the constraint that - * `region_min <= r_borrowed`. + * Informs the inference engine that `borrow_cmt` is being + * borrowed with kind `borrow_kind` and lifetime `borrow_region`. + * In order to ensure borrowck is satisfied, this may create + * constraints between regions, as explained in + * `link_reborrowed_region()`. */ - // Iterate through all the things that must be live at least - // for the lifetime `region_min` for the borrow to be valid: - let mut cmt_borrowed = cmt_borrowed; + let mut borrow_cmt = borrow_cmt; + let mut borrow_kind = borrow_kind; + loop { - debug!("link_region(region_min={}, kind={}, cmt_borrowed={})", - region_min.repr(rcx.tcx()), - kind.repr(rcx.tcx()), - cmt_borrowed.repr(rcx.tcx())); - match cmt_borrowed.cat.clone() { - mc::cat_deref(base, _, mc::BorrowedPtr(_, r_borrowed)) | - mc::cat_deref(base, _, mc::Implicit(_, r_borrowed)) => { - // References to an upvar `x` are translated to - // `*x`, since that is what happens in the - // underlying machine. We detect such references - // and treat them slightly differently, both to - // offer better error messages and because we need - // to infer the kind of borrow (mut, const, etc) - // to use for each upvar. - let cause = match base.cat { - mc::cat_upvar(ref upvar_id, _) => { - match rcx.fcx.inh.upvar_borrow_map.borrow_mut() - .find_mut(upvar_id) { - Some(upvar_borrow) => { - debug!("link_region: {} <= {}", - region_min.repr(rcx.tcx()), - upvar_borrow.region.repr(rcx.tcx())); - adjust_upvar_borrow_kind_for_loan( - *upvar_id, - upvar_borrow, - kind); - infer::ReborrowUpvar(span, *upvar_id) - } - None => { - rcx.tcx().sess.span_bug( - span, - format!("Illegal upvar id: {}", - upvar_id.repr( - rcx.tcx())).as_slice()); - } - } + debug!("link_region(borrow_region={}, borrow_kind={}, borrow_cmt={})", + borrow_region.repr(rcx.tcx()), + borrow_kind.repr(rcx.tcx()), + borrow_cmt.repr(rcx.tcx())); + match borrow_cmt.cat.clone() { + mc::cat_deref(ref_cmt, _, + mc::Implicit(ref_kind, ref_region)) | + mc::cat_deref(ref_cmt, _, + mc::BorrowedPtr(ref_kind, ref_region)) => { + match link_reborrowed_region(rcx, span, + borrow_region, borrow_kind, + ref_cmt, ref_region, ref_kind) { + Some((c, k)) => { + borrow_cmt = c; + borrow_kind = k; } - - _ => { - infer::Reborrow(span) + None => { + return; } - }; - - debug!("link_region: {} <= {}", - region_min.repr(rcx.tcx()), - r_borrowed.repr(rcx.tcx())); - rcx.fcx.mk_subr(cause, region_min, r_borrowed); - - if kind != ty::ImmBorrow { - // If this is a mutable borrow, then the thing - // being borrowed will have to be unique. - // In user code, this means it must be an `&mut` - // 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 - // if m == ast::m_mutbl { - // cmt_borrowed = cmt_base; - // } else { - // return; - // } - 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, _) => { - // Interior or owned data requires its base to be valid - cmt_borrowed = 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(..) | @@ -1519,6 +1471,154 @@ fn link_region(rcx: &Rcx, } } +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, _) => { + let mut upvar_borrow_map = + rcx.fcx.inh.upvar_borrow_map.borrow_mut(); + match upvar_borrow_map.find_mut(upvar_id) { + Some(upvar_borrow) => { + // Adjust mutability that we infer for the upvar + // so it can accommodate being borrowed with + // mutability `kind`: + adjust_upvar_borrow_kind_for_loan(*upvar_id, + upvar_borrow, + borrow_kind); + + infer::ReborrowUpvar(span, *upvar_id) + } + None => { + rcx.tcx().sess.span_bug( + span, + format!("Illegal upvar id: {}", + upvar_id.repr( + rcx.tcx())).as_slice()); + } + } + } + + _ => { + infer::Reborrow(span) + } + }; + + debug!("link_reborrowed_region: {} <= {}", + borrow_region.repr(rcx.tcx()), + ref_region.repr(rcx.tcx())); + rcx.fcx.mk_subr(cause, borrow_region, ref_region); + + // Decide whether we need to recurse and link any regions within + // the `ref_cmt`. This is concerned for the case where the value + // being reborrowed is in fact a borrowed pointer found within + // another borrowed pointer. For example: + // + // let p: &'b &'a mut T = ...; + // ... + // &'z **p + // + // What makes this case particularly tricky is that, if the data + // being borrowed is a `&mut` or `&uniq` borrow, borrowck requires + // not only that `'z <= 'a`, (as before) but also `'z <= 'b` + // (otherwise the user might mutate through the `&mut T` reference + // after `'b` expires and invalidate the borrow we are looking at + // now). + // + // So let's re-examine our parameters in light of this more + // complicated (possible) scenario: + // + // A borrow of: `& 'z bk * * p` where `p` has type `&'b bk & 'a bk T` + // borrow_region ^~ ref_region ^~ + // borrow_kind ^~ ref_kind ^~ + // ref_cmt ^~~ + // + // (Note that since we have not examined `ref_cmt.cat`, we don't + // know whether this scenario has occurred; but I wanted to show + // how all the types get adjusted.) + match ref_kind { + ty::ImmBorrow => { + // The reference being reborrowed is a sharable ref of + // 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)); + } + } +} + fn adjust_borrow_kind_for_assignment_lhs(rcx: &Rcx, lhs: &ast::Expr) { /*! @@ -1534,6 +1634,12 @@ fn adjust_borrow_kind_for_assignment_lhs(rcx: &Rcx, fn adjust_upvar_borrow_kind_for_mut(rcx: &Rcx, 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; loop { debug!("adjust_upvar_borrow_kind_for_mut(cmt={})", diff --git a/src/librustc/middle/typeck/infer/coercion.rs b/src/librustc/middle/typeck/infer/coercion.rs index abf36638113..9013b468d3f 100644 --- a/src/librustc/middle/typeck/infer/coercion.rs +++ b/src/librustc/middle/typeck/infer/coercion.rs @@ -247,7 +247,7 @@ impl<'f> Coerce<'f> { let a_borrowed = ty::mk_rptr(self.get_ref().infcx.tcx, r_borrow, mt {ty: inner_ty, mutbl: mutbl_b}); - if_ok!(sub.tys(a_borrowed, b)); + try!(sub.tys(a_borrowed, b)); Ok(Some(AutoDerefRef(AutoDerefRef { autoderefs: 1, @@ -273,7 +273,7 @@ impl<'f> Coerce<'f> { let r_borrow = self.get_ref().infcx.next_region_var(coercion); let unsized_ty = ty::mk_slice(self.get_ref().infcx.tcx, r_borrow, 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 { autoderefs: 0, autoref: Some(ty::AutoPtr(r_borrow, @@ -316,7 +316,7 @@ impl<'f> Coerce<'f> { let ty = ty::mk_rptr(self.get_ref().infcx.tcx, r_borrow, 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, \ AutoPtr(AutoUnsize({:?})))", kind); Ok(Some(AutoDerefRef(AutoDerefRef { @@ -334,7 +334,7 @@ impl<'f> Coerce<'f> { match self.unsize_ty(sty_a, t_b) { Some((ty, kind)) => { 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, \ AutoUnsizeUniq({:?}))", kind); 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 { autoderefs: 1, autoref: Some(AutoPtr(r_a, b_mutbl, None)) @@ -512,7 +512,7 @@ impl<'f> Coerce<'f> { sig: fn_ty_a.sig.clone(), .. *fn_ty_b }); - if_ok!(self.subtype(a_closure, b)); + try!(self.subtype(a_closure, b)); Ok(Some(adj)) }) } @@ -536,7 +536,7 @@ impl<'f> Coerce<'f> { // check that the types which they point at are compatible 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 // representation, we still register an AutoDerefRef so that diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs index 47085877ad7..2a44ef9de2b 100644 --- a/src/librustc/middle/typeck/infer/combine.rs +++ b/src/librustc/middle/typeck/infer/combine.rs @@ -8,43 +8,29 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ______________________________________________________________________ -// Type combining +/////////////////////////////////////////////////////////////////////////// +// # Type combining // -// There are three type combiners: sub, lub, and glb. Each implements -// the trait `Combine` and contains methods for combining two -// instances of various things and yielding a new instance. These -// combiner methods always yield a `result`---failure is propagated -// upward using `and_then()` methods. There is a lot of common code for -// these operations, implemented as default methods on the `Combine` -// trait. +// There are four type combiners: equate, sub, lub, and glb. Each +// implements the trait `Combine` and contains methods for combining +// two instances of various things and yielding a new instance. These +// combiner methods always yield a `Result`. There is a lot of +// common code for these operations, implemented as default methods on +// the `Combine` trait. // -// In reality, the sub operation is rather different from lub/glb, but -// they are combined into one trait to avoid duplication (they used to -// be separate but there were many bugs because there were two copies -// of most routines). +// Each operation may have side-effects on the inference context, +// though these can be unrolled using snapshots. On success, the +// LUB/GLB operations return the appropriate bound. The Eq and Sub +// operations generally return the first operand. // -// The differences are: -// -// - 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 +// ## Contravariance // // When you are relating two things which have a contravariant // relationship, you should use `contratys()` or `contraregions()`, // rather than inversing the order of arguments! This is necessary // 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 -// terms of error reporting, although we do not do that properly right -// now. +// terms of error reporting. use middle::subst; @@ -53,14 +39,16 @@ use middle::ty::{FloatVar, FnSig, IntVar, TyVar}; use middle::ty::{IntType, UintType}; use middle::ty::{BuiltinBounds}; use middle::ty; -use middle::typeck::infer::{ToUres}; +use middle::typeck::infer::equate::Equate; use middle::typeck::infer::glb::Glb; use middle::typeck::infer::lub::Lub; use middle::typeck::infer::sub::Sub; use middle::typeck::infer::unify::InferCtxtMethodsForSimplyUnifiableTypes; -use middle::typeck::infer::{InferCtxt, cres, ures}; -use middle::typeck::infer::{TypeTrace}; -use util::common::indent; +use middle::typeck::infer::{InferCtxt, cres}; +use middle::typeck::infer::{MiscVariable, TypeTrace}; +use middle::typeck::infer::type_variable::{RelationDir, EqTo, + SubtypeOf, SupertypeOf}; +use middle::ty_fold::{RegionFolder, TypeFoldable}; use util::ppaux::Repr; use std::result; @@ -75,6 +63,7 @@ pub trait Combine { fn a_is_expected(&self) -> bool; fn trace(&self) -> TypeTrace; + fn equate<'a>(&'a self) -> Equate<'a>; fn sub<'a>(&'a self) -> Sub<'a>; fn lub<'a>(&'a self) -> Lub<'a>; fn glb<'a>(&'a self) -> Glb<'a>; @@ -101,7 +90,7 @@ pub trait Combine { try!(result::fold_(as_ .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_)) } @@ -121,7 +110,7 @@ pub trait Combine { for &space in subst::ParamSpace::all().iter() { let a_tps = a_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 b_regions = b_subst.regions().get_slice(space); @@ -137,11 +126,11 @@ pub trait Combine { } }; - let regions = if_ok!(relate_region_params(self, - item_def_id, - r_variances, - a_regions, - b_regions)); + let regions = try!(relate_region_params(self, + item_def_id, + r_variances, + a_regions, + b_regions)); substs.types.replace(space, tps); substs.mut_regions().replace(space, regions); @@ -177,15 +166,12 @@ pub trait Combine { let b_r = b_rs[i]; let variance = variances[i]; let r = match variance { - ty::Invariant => { - eq_regions(this, a_r, b_r) - .and_then(|()| Ok(a_r)) - } + ty::Invariant => this.equate().regions(a_r, b_r), ty::Covariant => this.regions(a_r, b_r), ty::Contravariant => this.contraregions(a_r, b_r), ty::Bivariant => Ok(a_r), }; - rs.push(if_ok!(r)); + rs.push(try!(r)); } Ok(rs) } @@ -193,9 +179,9 @@ pub trait Combine { fn bare_fn_tys(&self, a: &ty::BareFnTy, b: &ty::BareFnTy) -> cres { - let fn_style = if_ok!(self.fn_styles(a.fn_style, b.fn_style)); - let abi = if_ok!(self.abi(a.abi, b.abi)); - let sig = if_ok!(self.fn_sigs(&a.sig, &b.sig)); + let fn_style = try!(self.fn_styles(a.fn_style, b.fn_style)); + let abi = try!(self.abi(a.abi, b.abi)); + let sig = try!(self.fn_sigs(&a.sig, &b.sig)); Ok(ty::BareFnTy {fn_style: fn_style, abi: abi, sig: sig}) @@ -207,7 +193,7 @@ pub trait Combine { let store = match (a.store, b.store) { (ty::RegionTraitStore(a_r, a_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) } @@ -219,11 +205,11 @@ pub trait Combine { 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 onceness = if_ok!(self.oncenesses(a.onceness, b.onceness)); - let bounds = if_ok!(self.existential_bounds(a.bounds, b.bounds)); - let sig = if_ok!(self.fn_sigs(&a.sig, &b.sig)); - let abi = if_ok!(self.abi(a.abi, b.abi)); + let fn_style = try!(self.fn_styles(a.fn_style, b.fn_style)); + let onceness = try!(self.oncenesses(a.onceness, b.onceness)); + let bounds = try!(self.existential_bounds(a.bounds, b.bounds)); + let sig = try!(self.fn_sigs(&a.sig, &b.sig)); + let abi = try!(self.abi(a.abi, b.abi)); Ok(ty::ClosureTy { fn_style: fn_style, onceness: onceness, @@ -311,7 +297,7 @@ pub trait Combine { Err(ty::terr_traits( expected_found(self, a.def_id, b.def_id))) } 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, substs: substs }) } @@ -334,34 +320,6 @@ pub fn expected_found( } } -pub fn eq_tys(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(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(this: &C, a: &ty::FnSig, b: &ty::FnSig) -> cres { fn argvecs(this: &C, a_args: &[ty::t], b_args: &[ty::t]) -> cres > { @@ -377,10 +335,10 @@ pub fn super_fn_sigs(this: &C, a: &ty::FnSig, b: &ty::FnSig) -> cres< 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(), 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, inputs: inputs, output: output, @@ -430,7 +388,7 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { // Relate integral variables to other types (&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)); Ok(a) } @@ -453,8 +411,7 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { // Relate floating-point variables to other types (&ty::ty_infer(FloatVar(a_id)), &ty::ty_infer(FloatVar(b_id))) => { - if_ok!(this.infcx().simple_vars(this.a_is_expected(), - a_id, b_id)); + try!(this.infcx().simple_vars(this.a_is_expected(), a_id, b_id)); Ok(a) } (&ty::ty_infer(FloatVar(v_id)), &ty::ty_float(v)) => { @@ -469,7 +426,8 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { (&ty::ty_bool, _) | (&ty::ty_int(_), _) | (&ty::ty_uint(_), _) | - (&ty::ty_float(_), _) => { + (&ty::ty_float(_), _) | + (&ty::ty_err, _) => { if ty::get(a).sty == ty::get(b).sty { Ok(a) } else { @@ -485,7 +443,7 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { (&ty::ty_enum(a_id, ref a_substs), &ty::ty_enum(b_id, ref b_substs)) if a_id == b_id => { - let substs = if_ok!(this.substs(a_id, + let substs = try!(this.substs(a_id, a_substs, b_substs)); Ok(ty::mk_enum(tcx, a_id, substs)) @@ -495,8 +453,8 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { &ty::ty_trait(ref b_)) if a_.def_id == b_.def_id => { debug!("Trying to match traits {:?} and {:?}", a, b); - let substs = if_ok!(this.substs(a_.def_id, &a_.substs, &b_.substs)); - let bounds = if_ok!(this.existential_bounds(a_.bounds, b_.bounds)); + let substs = try!(this.substs(a_.def_id, &a_.substs, &b_.substs)); + let bounds = try!(this.existential_bounds(a_.bounds, b_.bounds)); Ok(ty::mk_trait(tcx, a_.def_id, substs.clone(), @@ -505,14 +463,17 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { (&ty::ty_struct(a_id, ref a_substs), &ty::ty_struct(b_id, ref b_substs)) 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)) } (&ty::ty_unboxed_closure(a_id, a_region), &ty::ty_unboxed_closure(b_id, b_region)) 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)) } @@ -521,27 +482,27 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { } (&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)) } (&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)) } (&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 // used to use covariant subtyping. I have preserved this behaviour, // even though it is probably incorrect. So don't go down the usual // path which would require invariance. 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 => { - 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 } } - _ => 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)) } @@ -592,7 +553,7 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { vid: ty::IntVid, val: ty::IntVarValue) -> cres { - 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 { IntType(v) => Ok(ty::mk_mach_int(v)), UintType(v) => Ok(ty::mk_mach_uint(v)) @@ -605,7 +566,122 @@ pub fn super_tys(this: &C, a: ty::t, b: ty::t) -> cres { vid: ty::FloatVid, val: ast::FloatTy) -> cres { - 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)) } } + +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)))) + } +} diff --git a/src/librustc/middle/typeck/infer/equate.rs b/src/librustc/middle/typeck/infer/equate.rs new file mode 100644 index 00000000000..391027f9c4b --- /dev/null +++ b/src/librustc/middle/typeck/infer/equate.rs @@ -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 or the MIT license +// , 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 { + self.tys(a, b) + } + + fn contraregions(&self, a: ty::Region, b: ty::Region) -> cres { + self.regions(a, b) + } + + fn regions(&self, a: ty::Region, b: ty::Region) -> cres { + 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 { + 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 { + 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 { + 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 + { + // 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 { + 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 { + try!(self.sub().fn_sigs(a, b)); + self.sub().fn_sigs(b, a) + } +} diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index 2883a960df9..bd355d2f580 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -583,6 +583,19 @@ impl<'a> ErrorReporting for InferCtxt<'a> { 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) => { self.tcx.sess.span_err(span, "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)) } infer::EarlyBoundRegion(_, name) => { - format!(" for lifetime parameter `{}", + format!(" for lifetime parameter `{}`", token::get_name(name).get()) } infer::BoundRegionInCoherence(name) => { - format!(" for lifetime parameter `{} in coherence check", + format!(" for lifetime parameter `{}` in coherence check", token::get_name(name).get()) } infer::UpvarRegion(ref upvar_id, _) => { @@ -1528,6 +1541,15 @@ impl<'a> ErrorReportingHelpers for InferCtxt<'a> { self.tcx, 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) => { self.tcx.sess.span_note( span, @@ -1571,8 +1593,8 @@ impl<'a> ErrorReportingHelpers for InferCtxt<'a> { infer::AutoBorrow(span) => { self.tcx.sess.span_note( span, - "...so that reference is valid \ - at the time of implicit borrow"); + "...so that auto-reference is valid \ + at the time of borrow"); } infer::ExprTypeIsNotInScope(t, span) => { self.tcx.sess.span_note( diff --git a/src/librustc/middle/typeck/infer/glb.rs b/src/librustc/middle/typeck/infer/glb.rs index 00eaa4d235b..68b8031a04b 100644 --- a/src/librustc/middle/typeck/infer/glb.rs +++ b/src/librustc/middle/typeck/infer/glb.rs @@ -12,9 +12,9 @@ use middle::ty::{BuiltinBounds}; use middle::ty::RegionVid; use middle::ty; -use middle::typeck::infer::then; use middle::typeck::infer::combine::*; use middle::typeck::infer::lattice::*; +use middle::typeck::infer::equate::Equate; use middle::typeck::infer::lub::Lub; use middle::typeck::infer::sub::Sub; use middle::typeck::infer::{cres, InferCtxt}; @@ -29,24 +29,29 @@ use util::common::{indenter}; use util::ppaux::mt_to_string; 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> { - pub fn get_ref<'a>(&'a self) -> &'a CombineFields<'f> { let Glb(ref v) = *self; v } +#[allow(non_snake_case_functions)] +pub fn Glb<'f>(cf: CombineFields<'f>) -> Glb<'f> { + Glb { fields: cf } } 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 a_is_expected(&self) -> bool { self.get_ref().a_is_expected } - fn trace(&self) -> TypeTrace { self.get_ref().trace.clone() } + fn a_is_expected(&self) -> bool { self.fields.a_is_expected } + fn trace(&self) -> TypeTrace { self.fields.trace.clone() } - fn sub<'a>(&'a self) -> Sub<'a> { Sub(self.get_ref().clone()) } - fn lub<'a>(&'a self) -> Lub<'a> { Lub(self.get_ref().clone()) } - fn glb<'a>(&'a self) -> Glb<'a> { Glb(self.get_ref().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 mts(&self, a: &ty::mt, b: &ty::mt) -> cres { - let tcx = self.get_ref().infcx.tcx; + let tcx = self.fields.infcx.tcx; debug!("{}.mts({}, {})", self.tag(), @@ -54,27 +59,25 @@ impl<'f> Combine for Glb<'f> { mt_to_string(tcx, b)); match (a.mutbl, b.mutbl) { - // If one side or both is mut, then the GLB must use - // the precise type from the mut side. - (MutMutable, MutMutable) => { - eq_tys(self, a.ty, b.ty).then(|| { - Ok(ty::mt {ty: a.ty, mutbl: MutMutable}) - }) - } + // If one side or both is mut, then the GLB must use + // the precise type from the mut side. + (MutMutable, MutMutable) => { + let t = try!(self.equate().tys(a.ty, b.ty)); + Ok(ty::mt {ty: t, mutbl: MutMutable}) + } - // If one side or both is immutable, we can use the GLB of - // both sides but mutbl must be `MutImmutable`. - (MutImmutable, MutImmutable) => { - self.tys(a.ty, b.ty).and_then(|t| { + // If one side or both is immutable, we can use the GLB of + // both sides but mutbl must be `MutImmutable`. + (MutImmutable, MutImmutable) => { + let t = try!(self.tys(a.ty, b.ty)); Ok(ty::mt {ty: t, mutbl: MutImmutable}) - }) - } + } - // There is no mutual subtype of these combinations. - (MutMutable, MutImmutable) | - (MutImmutable, MutMutable) => { - Err(ty::terr_mutability) - } + // There is no mutual subtype of these combinations. + (MutMutable, MutImmutable) | + (MutImmutable, MutMutable) => { + Err(ty::terr_mutability) + } } } @@ -108,10 +111,10 @@ impl<'f> Combine for Glb<'f> { fn regions(&self, a: ty::Region, b: ty::Region) -> cres { debug!("{}.regions({:?}, {:?})", 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)); - 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) @@ -128,33 +131,33 @@ impl<'f> Combine for Glb<'f> { // please see the large comment in `region_inference.rs`. 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(); // Make a mark so we can examine "all bindings that were // created as part of this type comparison". - let mark = self.get_ref().infcx.region_vars.mark(); + let mark = self.fields.infcx.region_vars.mark(); // Instantiate each bound region with a fresh region variable. 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); let a_vars = var_ids(self, &a_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); let b_vars = var_ids(self, &b_map); // Collect constraints. - let sig0 = if_ok!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh)); - debug!("sig0 = {}", sig0.repr(self.get_ref().infcx.tcx)); + let sig0 = try!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh)); + debug!("sig0 = {}", sig0.repr(self.fields.infcx.tcx)); // Generalize the regions appearing in fn_ty0 if possible 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 = fold_regions_in_sig( - self.get_ref().infcx.tcx, + self.fields.infcx.tcx, &sig0, |r| { generalize_region(self, @@ -166,7 +169,7 @@ impl<'f> Combine for Glb<'f> { b_vars.as_slice(), r) }); - debug!("sig1 = {}", sig1.repr(self.get_ref().infcx.tcx)); + debug!("sig1 = {}", sig1.repr(self.fields.infcx.tcx)); return Ok(sig1); fn generalize_region(this: &Glb, @@ -182,7 +185,7 @@ impl<'f> Combine for Glb<'f> { 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 b_r = None; @@ -249,14 +252,14 @@ impl<'f> Combine for Glb<'f> { return ty::ReLateBound(new_binder_id, *a_br); } } - this.get_ref().infcx.tcx.sess.span_bug( - this.get_ref().trace.origin.span(), + this.fields.infcx.tcx.sess.span_bug( + this.fields.trace.origin.span(), format!("could not find original bound region for {:?}", r).as_slice()) } 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) } } } diff --git a/src/librustc/middle/typeck/infer/lattice.rs b/src/librustc/middle/typeck/infer/lattice.rs index 708eb498f84..f09773d30b5 100644 --- a/src/librustc/middle/typeck/infer/lattice.rs +++ b/src/librustc/middle/typeck/infer/lattice.rs @@ -9,7 +9,6 @@ // except according to those terms. /*! - * * # Lattice Variables * * This file contains generic code for operating on inference variables @@ -34,346 +33,55 @@ use middle::ty::{RegionVid, TyVar}; use middle::ty; -use middle::typeck::infer::{ToUres}; use middle::typeck::infer::*; use middle::typeck::infer::combine::*; use middle::typeck::infer::glb::Glb; use middle::typeck::infer::lub::Lub; -use middle::typeck::infer::unify::*; -use middle::typeck::infer::sub::Sub; use util::ppaux::Repr; 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; - fn glb(cf: CombineFields, a: &Self, b: &Self) -> cres; +pub trait LatticeDir { + // Relates the bottom type to `t` and returns LUB(t, _|_) or + // GLB(t, _|_) as appropriate. + fn ty_bot(&self, t: ty::t) -> cres; + + // 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<()>; } -pub type LatticeOp<'a, T> = - |cf: CombineFields, a: &T, b: &T|: 'a -> cres; - -impl LatticeValue for ty::t { - fn sub(cf: CombineFields, a: &ty::t, b: &ty::t) -> ures { - Sub(cf).tys(*a, *b).to_ures() +impl<'a> LatticeDir for Lub<'a> { + fn ty_bot(&self, t: ty::t) -> cres { + Ok(t) } - fn lub(cf: CombineFields, a: &ty::t, b: &ty::t) -> cres { - Lub(cf).tys(*a, *b) - } - - fn glb(cf: CombineFields, a: &ty::t, b: &ty::t) -> cres { - Glb(cf).tys(*a, *b) - } -} - -pub trait CombineFieldsLatticeMethods>> { - /// 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, - b: &Bounds, - rank: uint) - -> ures; -} - -pub trait CombineFieldsLatticeMethods2 { - fn merge_bnd(&self, - a: &Bound, - b: &Bound, - lattice_op: LatticeOp) - -> cres>; - - fn bnds(&self, a: &Bound, b: &Bound) -> ures; -} - -impl<'f,T:LatticeValue, K:UnifyKey>> - CombineFieldsLatticeMethods 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, - b: &Bounds, - 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)); + fn relate_bound<'a>(&'a self, v: ty::t, a: ty::t, b: ty::t) -> cres<()> { + let sub = self.sub(); + try!(sub.tys(a, v)); + try!(sub.tys(b, v)); Ok(()) } } -impl<'f,T:LatticeValue> - CombineFieldsLatticeMethods2 for CombineFields<'f> -{ - fn merge_bnd(&self, - a: &Bound, - b: &Bound, - lattice_op: LatticeOp) - -> cres> - { - /*! - * 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, - b: &Bound) - -> 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 { - fn combine_fields<'a>(&'a self) -> CombineFields<'a>; - fn bnd(&self, b: &Bounds) -> Option; - fn with_bnd(&self, b: &Bounds, t: T) -> Bounds; -} - -pub trait TyLatticeDir { - fn ty_bot(&self, t: ty::t) -> cres; -} - -impl<'f> LatticeDir for Lub<'f> { - fn combine_fields<'a>(&'a self) -> CombineFields<'a> { self.get_ref().clone() } - fn bnd(&self, b: &Bounds) -> Option { b.ub.clone() } - fn with_bnd(&self, b: &Bounds, t: T) -> Bounds { - Bounds { ub: Some(t), ..(*b).clone() } - } -} - -impl<'f> TyLatticeDir for Lub<'f> { - fn ty_bot(&self, t: ty::t) -> cres { - Ok(t) - } -} - -impl<'f> LatticeDir for Glb<'f> { - fn combine_fields<'a>(&'a self) -> CombineFields<'a> { self.get_ref().clone() } - fn bnd(&self, b: &Bounds) -> Option { b.lb.clone() } - fn with_bnd(&self, b: &Bounds, t: T) -> Bounds { - Bounds { lb: Some(t), ..(*b).clone() } - } -} - -impl<'f> TyLatticeDir for Glb<'f> { - fn ty_bot(&self, _t: ty::t) -> cres { +impl<'a> LatticeDir for Glb<'a> { + fn ty_bot(&self, _: ty::t) -> cres { 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(this: &L, - a: ty::t, - b: ty::t) - -> cres { +pub fn super_lattice_tys(this: &L, + a: ty::t, + b: ty::t) + -> cres +{ debug!("{}.lattice_tys({}, {})", this.tag(), a.repr(this.infcx().tcx), @@ -383,156 +91,27 @@ pub fn super_lattice_tys(this: &L, 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) { - (&ty::ty_bot, _) => { return this.ty_bot(b); } - (_, &ty::ty_bot) => { return this.ty_bot(a); } + (&ty::ty_bot, _) => { this.ty_bot(b) } + (_, &ty::ty_bot) => { this.ty_bot(a) } - (&ty::ty_infer(TyVar(a_id)), &ty::ty_infer(TyVar(b_id))) => { - let r = if_ok!(lattice_vars(this, a_id, b_id, - |x, y| this.tys(*x, *y))); - return match r { - VarResult(v) => Ok(ty::mk_var(tcx, 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)); + (&ty::ty_infer(TyVar(..)), _) | + (_, &ty::ty_infer(TyVar(..))) => { + let v = infcx.next_ty_var(); + try!(this.relate_bound(v, a, b)); + Ok(v) } _ => { - return super_tys(this, a, b); + super_tys(this, a, b) } } } -pub type LatticeDirOp<'a, T> = |a: &T, b: &T|: 'a -> cres; - -#[deriving(Clone)] -pub enum LatticeVarResult { - 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>>( - this: &L, // defines whether we want LUB or GLB - a_vid: K, // first variable - b_vid: K, // second variable - lattice_dir_op: LatticeDirOp) // LUB or GLB operation on types - -> cres> -{ - 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>>( - this: &L, - a_id: K, - b: &T, - lattice_dir_op: LatticeDirOp) - -> cres -{ - 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 // fn types diff --git a/src/librustc/middle/typeck/infer/lub.rs b/src/librustc/middle/typeck/infer/lub.rs index 8707efc622b..9c6c0763ad4 100644 --- a/src/librustc/middle/typeck/infer/lub.rs +++ b/src/librustc/middle/typeck/infer/lub.rs @@ -11,8 +11,8 @@ use middle::ty::{BuiltinBounds}; use middle::ty::RegionVid; use middle::ty; -use middle::typeck::infer::then; use middle::typeck::infer::combine::*; +use middle::typeck::infer::equate::Equate; use middle::typeck::infer::glb::Glb; use middle::typeck::infer::lattice::*; use middle::typeck::infer::sub::Sub; @@ -28,24 +28,29 @@ use syntax::ast::{MutMutable, MutImmutable}; use util::ppaux::mt_to_string; 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> { - pub fn get_ref<'a>(&'a self) -> &'a CombineFields<'f> { let Lub(ref v) = *self; v } +#[allow(non_snake_case_functions)] +pub fn Lub<'f>(cf: CombineFields<'f>) -> Lub<'f> { + Lub { fields: cf } } 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 a_is_expected(&self) -> bool { self.get_ref().a_is_expected } - fn trace(&self) -> TypeTrace { self.get_ref().trace.clone() } + fn a_is_expected(&self) -> bool { self.fields.a_is_expected } + fn trace(&self) -> TypeTrace { self.fields.trace.clone() } - fn sub<'a>(&'a self) -> Sub<'a> { Sub(self.get_ref().clone()) } - fn lub<'a>(&'a self) -> Lub<'a> { Lub(self.get_ref().clone()) } - fn glb<'a>(&'a self) -> Glb<'a> { Glb(self.get_ref().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 mts(&self, a: &ty::mt, b: &ty::mt) -> cres { - let tcx = self.get_ref().infcx.tcx; + let tcx = self.fields.infcx.tcx; debug!("{}.mts({}, {})", self.tag(), @@ -58,17 +63,15 @@ impl<'f> Combine for Lub<'f> { let m = a.mutbl; match m { - MutImmutable => { - self.tys(a.ty, b.ty).and_then(|t| Ok(ty::mt {ty: t, mutbl: m}) ) - } + MutImmutable => { + let t = try!(self.tys(a.ty, b.ty)); + Ok(ty::mt {ty: t, mutbl: m}) + } - MutMutable => { - self.get_ref().infcx.try(|| { - eq_tys(self, a.ty, b.ty).then(|| { - Ok(ty::mt {ty: a.ty, mutbl: m}) - }) - }).or_else(|e| Err(e)) - } + MutMutable => { + let t = try!(self.equate().tys(a.ty, b.ty)); + Ok(ty::mt {ty: t, mutbl: m}) + } } } @@ -107,10 +110,10 @@ impl<'f> Combine for Lub<'f> { fn regions(&self, a: ty::Region, b: ty::Region) -> cres { debug!("{}.regions({}, {})", 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)); - 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 { @@ -119,26 +122,26 @@ impl<'f> Combine for Lub<'f> { // Make a mark so we can examine "all bindings that were // created as part of this type comparison". - let mark = self.get_ref().infcx.region_vars.mark(); + let mark = self.fields.infcx.region_vars.mark(); // Instantiate each bound region with a fresh region variable. 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); 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); // Collect constraints. - let sig0 = if_ok!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh)); - debug!("sig0 = {}", sig0.repr(self.get_ref().infcx.tcx)); + let sig0 = try!(super_fn_sigs(self, &a_with_fresh, &b_with_fresh)); + debug!("sig0 = {}", sig0.repr(self.fields.infcx.tcx)); // Generalize the regions appearing in sig0 if possible 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 = fold_regions_in_sig( - self.get_ref().infcx.tcx, + self.fields.infcx.tcx, &sig0, |r| generalize_region(self, mark, new_vars.as_slice(), sig0.binder_id, &a_map, r)); @@ -158,7 +161,7 @@ impl<'f> Combine for Lub<'f> { 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 // *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.get_ref().trace.origin.span(), + this.fields.infcx.tcx.sess.span_bug( + this.fields.trace.origin.span(), format!("region {:?} is not associated with \ any bound region from A!", r0).as_slice()) diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index ed96effdd83..f86857f97f6 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -29,13 +29,14 @@ use middle::ty_fold; use middle::ty_fold::TypeFolder; use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig; use middle::typeck::infer::coercion::Coerce; -use middle::typeck::infer::combine::{Combine, CombineFields, eq_tys}; -use middle::typeck::infer::region_inference::{RegionSnapshot}; -use middle::typeck::infer::region_inference::{RegionVarBindings}; +use middle::typeck::infer::combine::{Combine, CombineFields}; +use middle::typeck::infer::region_inference::{RegionVarBindings, + RegionSnapshot}; use middle::typeck::infer::resolve::{resolver}; +use middle::typeck::infer::equate::Equate; use middle::typeck::infer::sub::Sub; 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 std::cell::{RefCell}; use std::collections::HashMap; @@ -46,19 +47,20 @@ use syntax::codemap::Span; use util::common::indent; use util::ppaux::{bound_region_to_string, ty_to_string, trait_ref_to_string, Repr}; -pub mod doc; -pub mod macros; +pub mod coercion; pub mod combine; +pub mod doc; +pub mod equate; +pub mod error_reporting; pub mod glb; pub mod lattice; pub mod lub; pub mod region_inference; pub mod resolve; pub mod sub; -pub mod unify; -pub mod coercion; -pub mod error_reporting; pub mod test; +pub mod type_variable; +pub mod unify; pub type Bound = Option; @@ -79,8 +81,7 @@ pub struct InferCtxt<'a> { // We instantiate UnificationTable with bounds because the // types that might instantiate a general type variable have an // order, represented by its upper and lower bounds. - type_unification_table: - RefCell>>, + type_variables: RefCell, // Map from integral variable to the kind of integer it represents int_unification_table: @@ -161,6 +162,9 @@ pub enum SubregionOrigin { // Closure bound must not outlive captured free variables FreeVariable(Span, ast::NodeId), + // Proc upvars must be 'static + ProcCapture(Span, ast::NodeId), + // Index into slice must be within its lifetime 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> { InferCtxt { tcx: tcx, - type_unification_table: RefCell::new(UnificationTable::new()), + type_variables: RefCell::new(type_variable::TypeVariableTable::new()), int_unification_table: RefCell::new(UnificationTable::new()), float_unification_table: RefCell::new(UnificationTable::new()), region_vars: RegionVarBindings::new(tcx), @@ -392,8 +396,8 @@ pub fn mk_eqty(cx: &InferCtxt, origin: origin, values: Types(expected_found(a_is_expected, a, b)) }; - let suber = cx.sub(a_is_expected, trace); - eq_tys(&suber, a, b) + try!(cx.equate(a_is_expected, trace).tys(a, b)); + Ok(()) }) } @@ -508,9 +512,9 @@ pub fn uok() -> ures { } pub struct CombinedSnapshot { - type_snapshot: Snapshot, - int_snapshot: Snapshot, - float_snapshot: Snapshot, + type_snapshot: type_variable::Snapshot, + int_snapshot: unify::Snapshot, + float_snapshot: unify::Snapshot, region_vars_snapshot: RegionSnapshot, } @@ -522,6 +526,10 @@ impl<'a> InferCtxt<'a> { 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> { Sub(self.combine_fields(a_is_expected, trace)) } @@ -530,13 +538,9 @@ impl<'a> InferCtxt<'a> { Lub(self.combine_fields(a_is_expected, trace)) } - pub fn in_snapshot(&self) -> bool { - self.region_vars.in_snapshot() - } - fn start_snapshot(&self) -> 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(), float_snapshot: self.float_unification_table.borrow_mut().snapshot(), region_vars_snapshot: self.region_vars.start_snapshot(), @@ -550,15 +554,15 @@ impl<'a> InferCtxt<'a> { float_snapshot, region_vars_snapshot } = snapshot; - self.type_unification_table + self.type_variables .borrow_mut() - .rollback_to(self.tcx, type_snapshot); + .rollback_to(type_snapshot); self.int_unification_table .borrow_mut() - .rollback_to(self.tcx, int_snapshot); + .rollback_to(int_snapshot); self.float_unification_table .borrow_mut() - .rollback_to(self.tcx, float_snapshot); + .rollback_to(float_snapshot); self.region_vars .rollback_to(region_vars_snapshot); } @@ -570,7 +574,7 @@ impl<'a> InferCtxt<'a> { float_snapshot, region_vars_snapshot } = snapshot; - self.type_unification_table + self.type_variables .borrow_mut() .commit(type_snapshot); self.int_unification_table @@ -633,9 +637,9 @@ impl<'a> InferCtxt<'a> { impl<'a> InferCtxt<'a> { pub fn next_ty_var_id(&self) -> TyVid { - self.type_unification_table + self.type_variables .borrow_mut() - .new_key(Bounds { lb: None, ub: None }) + .new_var() } pub fn next_ty_var(&self) -> ty::t { @@ -933,6 +937,7 @@ impl SubregionOrigin { InvokeClosure(a) => a, DerefPointer(a) => a, FreeVariable(a, _) => a, + ProcCapture(a, _) => a, IndexSlice(a) => a, RelateObjectBound(a) => a, RelateProcBound(a, _, _) => a, @@ -972,6 +977,9 @@ impl Repr for SubregionOrigin { FreeVariable(a, b) => { format!("FreeVariable({}, {})", a.repr(tcx), b) } + ProcCapture(a, b) => { + format!("ProcCapture({}, {})", a.repr(tcx), b) + } IndexSlice(a) => { format!("IndexSlice({})", a.repr(tcx)) } diff --git a/src/librustc/middle/typeck/infer/region_inference/mod.rs b/src/librustc/middle/typeck/infer/region_inference/mod.rs index 7c8d10dd994..f34894346f6 100644 --- a/src/librustc/middle/typeck/infer/region_inference/mod.rs +++ b/src/librustc/middle/typeck/infer/region_inference/mod.rs @@ -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 } @@ -253,7 +253,7 @@ impl<'a> RegionVarBindings<'a> { } 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().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, origin: SubregionOrigin, sub: Region, diff --git a/src/librustc/middle/typeck/infer/resolve.rs b/src/librustc/middle/typeck/infer/resolve.rs index 2ae95309d41..f9742c522da 100644 --- a/src/librustc/middle/typeck/infer/resolve.rs +++ b/src/librustc/middle/typeck/infer/resolve.rs @@ -48,12 +48,11 @@ 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_fold; -use middle::typeck::infer::{Bounds, cyclic_ty, fixup_err, fres, InferCtxt}; -use middle::typeck::infer::{unresolved_float_ty, unresolved_int_ty}; -use middle::typeck::infer::{unresolved_ty}; +use middle::typeck::infer::{cyclic_ty, fixup_err, fres, InferCtxt}; +use middle::typeck::infer::{unresolved_int_ty,unresolved_float_ty,unresolved_ty}; use syntax::codemap::Span; use util::common::indent; use util::ppaux::{Repr, ty_to_string}; @@ -132,8 +131,8 @@ impl<'a> ResolveState<'a> { assert!(self.v_seen.is_empty()); match self.err { None => { - debug!("Resolved to {} + {} (modes={:x})", - ty_to_string(self.infcx.tcx, rty), + debug!("Resolved {} to {} (modes={:x})", + ty_to_string(self.infcx.tcx, typ), ty_to_string(self.infcx.tcx, rty), self.modes); return Ok(rty); @@ -219,21 +218,16 @@ impl<'a> ResolveState<'a> { // tend to carry more restrictions or higher // perf. penalties, so it pays to know more. - let node = - self.infcx.type_unification_table.borrow_mut().get(tcx, vid); - let t1 = match node.value { - Bounds { ub:_, lb:Some(t) } if !type_is_bot(t) => { - self.resolve_type(t) - } - Bounds { ub:Some(t), lb:_ } | Bounds { ub:_, lb:Some(t) } => { - self.resolve_type(t) - } - Bounds { ub:None, lb:None } => { - if self.should(force_tvar) { - self.err = Some(unresolved_ty(vid)); + let t1 = match self.infcx.type_variables.borrow().probe(vid) { + Some(t) => { + self.resolve_type(t) + } + None => { + if self.should(force_tvar) { + self.err = Some(unresolved_ty(vid)); + } + ty::mk_var(tcx, vid) } - ty::mk_var(tcx, vid) - } }; self.v_seen.pop().unwrap(); return t1; diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/typeck/infer/sub.rs index a9e8d1e8603..cc3abc279bf 100644 --- a/src/librustc/middle/typeck/infer/sub.rs +++ b/src/librustc/middle/typeck/infer/sub.rs @@ -15,79 +15,83 @@ use middle::ty::TyVar; use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig; use middle::typeck::infer::combine::*; use middle::typeck::infer::{cres, CresCompare}; +use middle::typeck::infer::equate::Equate; use middle::typeck::infer::glb::Glb; use middle::typeck::infer::InferCtxt; -use middle::typeck::infer::lattice::CombineFieldsLatticeMethods; use middle::typeck::infer::lub::Lub; -use middle::typeck::infer::then; use middle::typeck::infer::{TypeTrace, Subtype}; +use middle::typeck::infer::type_variable::{SubtypeOf, SupertypeOf}; use util::common::{indenter}; use util::ppaux::{bound_region_to_string, Repr}; use syntax::ast::{Onceness, FnStyle, MutImmutable, MutMutable}; -pub struct Sub<'f>(pub CombineFields<'f>); // "subtype", "subregion" etc -impl<'f> Sub<'f> { - pub fn get_ref<'a>(&'a self) -> &'a CombineFields<'f> { let Sub(ref v) = *self; v } +/// "Greatest lower bound" (common subtype) +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> { - 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 a_is_expected(&self) -> bool { self.get_ref().a_is_expected } - fn trace(&self) -> TypeTrace { self.get_ref().trace.clone() } + fn a_is_expected(&self) -> bool { self.fields.a_is_expected } + fn trace(&self) -> TypeTrace { self.fields.trace.clone() } - fn sub<'a>(&'a self) -> Sub<'a> { Sub(self.get_ref().clone()) } - fn lub<'a>(&'a self) -> Lub<'a> { Lub(self.get_ref().clone()) } - fn glb<'a>(&'a self) -> Glb<'a> { Glb(self.get_ref().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 { - let opp = CombineFields { - a_is_expected: !self.get_ref().a_is_expected, - ..self.get_ref().clone() - }; - Sub(opp).tys(b, a) + Sub(self.fields.switch_expected()).tys(b, a) } fn contraregions(&self, a: ty::Region, b: ty::Region) - -> cres { - let opp = CombineFields { - a_is_expected: !self.get_ref().a_is_expected, - ..self.get_ref().clone() - }; - Sub(opp).regions(b, a) - } + -> cres { + let opp = CombineFields { + a_is_expected: !self.fields.a_is_expected, + ..self.fields.clone() + }; + Sub(opp).regions(b, a) + } fn regions(&self, a: ty::Region, b: ty::Region) -> cres { debug!("{}.regions({}, {})", self.tag(), - a.repr(self.get_ref().infcx.tcx), - b.repr(self.get_ref().infcx.tcx)); - self.get_ref().infcx.region_vars.make_subregion(Subtype(self.trace()), a, b); + a.repr(self.fields.infcx.tcx), + b.repr(self.fields.infcx.tcx)); + self.fields.infcx.region_vars.make_subregion(Subtype(self.trace()), a, b); Ok(a) } fn mts(&self, a: &ty::mt, b: &ty::mt) -> cres { debug!("mts({} <: {})", - 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.mutbl != b.mutbl { return Err(ty::terr_mutability); } match b.mutbl { - MutMutable => { - // If supertype is mut, subtype must match exactly - // (i.e., invariant if mut): - eq_tys(self, a.ty, b.ty).then(|| Ok(*a)) - } - MutImmutable => { - // Otherwise we can be covariant: - self.tys(a.ty, b.ty).and_then(|_t| Ok(*a) ) - } + MutMutable => { + // If supertype is mut, subtype must match exactly + // (i.e., invariant if mut): + try!(self.equate().tys(a.ty, b.ty)); + } + MutImmutable => { + // Otherwise we can be covariant: + 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 { @@ -118,16 +122,21 @@ impl<'f> Combine for Sub<'f> { fn tys(&self, a: ty::t, b: ty::t) -> cres { 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); } - 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) { (&ty::ty_bot, _) => { Ok(a) } (&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) } // 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))) } (&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) } @@ -148,7 +159,7 @@ impl<'f> Combine for Sub<'f> { Err(ty::terr_sorts(expected_found(self, a, b))) } (_, &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) } @@ -164,7 +175,7 @@ impl<'f> Combine for Sub<'f> { fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres { 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(); // 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 // 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 // region variable. 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); // Second, we instantiate each bound region in the supertype with a // fresh concrete region. let (skol_map, b_sig) = { - replace_late_bound_regions_in_fn_sig(self.get_ref().infcx.tcx, b, |br| { - let skol = self.get_ref().infcx.region_vars.new_skolemized(br); + replace_late_bound_regions_in_fn_sig(self.fields.infcx.tcx, b, |br| { + let skol = self.fields.infcx.region_vars.new_skolemized(br); 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 }) }; - debug!("a_sig={}", a_sig.repr(self.get_ref().infcx.tcx)); - debug!("b_sig={}", b_sig.repr(self.get_ref().infcx.tcx)); + debug!("a_sig={}", a_sig.repr(self.fields.infcx.tcx)); + debug!("b_sig={}", b_sig.repr(self.fields.infcx.tcx)); // 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 // that the skolemized regions do not "leak". 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() { - 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() { // Each skolemized should only be relatable to itself // or new variables: @@ -224,16 +235,16 @@ impl<'f> Combine for Sub<'f> { if self.a_is_expected() { debug!("Not as polymorphic!"); return Err(ty::terr_regions_insufficiently_polymorphic( - skol_br, *tainted_region)); + skol_br, *tainted_region)); } else { debug!("Overly polymorphic!"); return Err(ty::terr_regions_overly_polymorphic( - skol_br, *tainted_region)); + skol_br, *tainted_region)); } } } return Ok(sig); } - } + diff --git a/src/librustc/middle/typeck/infer/type_variable.rs b/src/librustc/middle/typeck/infer/type_variable.rs new file mode 100644 index 00000000000..5f67f8a048a --- /dev/null +++ b/src/librustc/middle/typeck/infer/type_variable.rs @@ -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 or the MIT license +// , 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, +} + +struct TypeVariableData { + value: TypeVariableValue +} + +enum TypeVariableValue { + Known(ty::t), + Bounded(Vec), +} + +pub struct Snapshot { + snapshot: sv::Snapshot +} + +enum UndoEntry { + // The type of the var was specified. + SpecifyVar(ty::TyVid, Vec), + 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 { + 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 { + 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 for Delegate { + fn reverse(&mut self, + values: &mut Vec, + 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 { + match v.value { + Known(_) => fail!("var_sub_var: variable is known"), + Bounded(ref mut relations) => relations + } +} + diff --git a/src/librustc/middle/typeck/infer/unify.rs b/src/librustc/middle/typeck/infer/unify.rs index 33bdded5234..adf0a25ce40 100644 --- a/src/librustc/middle/typeck/infer/unify.rs +++ b/src/librustc/middle/typeck/infer/unify.rs @@ -12,23 +12,23 @@ use std::kinds::marker; use middle::ty::{expected_found, IntVarValue}; use middle::ty; -use middle::typeck::infer::{Bounds, uok, ures}; +use middle::typeck::infer::{uok, ures}; use middle::typeck::infer::InferCtxt; use std::cell::RefCell; use std::fmt::Show; -use std::mem; use syntax::ast; use util::ppaux::Repr; +use util::snapshot_vec as sv; /** * This trait is implemented by any type that can serve as a type * variable. We call such variables *unification keys*. For example, - * this trait is implemented by `TyVid`, which represents normal - * type variables, and `IntVid`, which represents integral variables. + * this trait is implemented by `IntVid`, which represents integral + * variables. * - * Each key type has an associated value type `V`. For example, - * for `TyVid`, this is `Bounds`, representing a pair of - * upper- and lower-bound types. + * Each key type has an associated value type `V`. For example, for + * `IntVid`, this is `Option`, representing some + * (possibly not yet known) sort of integer. * * Implementations of this trait are at the end of this file. */ @@ -48,11 +48,10 @@ pub trait UnifyKey : Clone + Show + PartialEq + Repr { } /** - * Trait for valid types that a type variable can be set to. Note - * that this is typically not the end type that the value will - * take on, but rather some wrapper: for example, for normal type - * variables, the associated type is not `ty::t` but rather - * `Bounds`. + * Trait for valid types that a type variable can be set to. Note that + * this is typically not the end type that the value will take on, but + * rather an `Option` wrapper (where `None` represents a variable + * whose value is not yet set). * * Implementations of this trait are at the end of this file. */ @@ -82,13 +81,8 @@ pub struct UnificationTable { /** * Indicates the current value of each key. */ - values: Vec>, - /** - * When a snapshot is active, logs each change made to the table - * so that they can be unrolled. - */ - undo_log: Vec>, + values: sv::SnapshotVec,(),Delegate>, } /** @@ -96,29 +90,9 @@ pub struct UnificationTable { * made during the snapshot may either be *committed* or *rolled back*. */ pub struct Snapshot { - // Ensure that this snapshot is keyed to the table type. - marker1: marker::CovariantType, - - // 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 { - /// 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), + // Link snapshot to the key type `K` of the table. + marker: marker::CovariantType, + snapshot: sv::Snapshot, } /** @@ -131,85 +105,36 @@ pub struct Node { pub rank: uint, } +pub struct Delegate; + // We can't use V:LatticeValue, much as I would like to, -// because frequently the pattern is that V=Bounds for some +// because frequently the pattern is that V=Option for some // other type parameter U, and we have no way to say -// Bounds: +// Option:LatticeValue. impl> UnificationTable { pub fn new() -> UnificationTable { UnificationTable { - values: Vec::new(), - undo_log: Vec::new() + values: sv::SnapshotVec::new(Delegate), } } - pub fn in_snapshot(&self) -> bool { - /*! True if a snapshot has been started. */ - - self.undo_log.len() > 0 - } - /** * Starts a new snapshot. Each snapshot must be either * rolled back or committed in a "LIFO" (stack) order. */ pub fn snapshot(&mut self) -> Snapshot { - let length = self.undo_log.len(); - debug!("{}: snapshot at length {}", - UnifyKey::tag(None::), - length); - self.undo_log.push(OpenSnapshot); - Snapshot { length: length, - marker1: marker::CovariantType, - marker2: 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!(*self.undo_log.get(snapshot.length) == OpenSnapshot); + Snapshot { marker: marker::CovariantType::, + snapshot: self.values.start_snapshot() } } /** * Reverses all changes since the last snapshot. Also * removes any keys that have been created since then. */ - pub fn rollback_to(&mut self, tcx: &ty::ctxt, snapshot: Snapshot) { - debug!("{}: rollback_to({})", - UnifyKey::tag(None::), - 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); + pub fn rollback_to(&mut self, snapshot: Snapshot) { + debug!("{}: rollback_to()", UnifyKey::tag(None::)); + self.values.rollback_to(snapshot.snapshot); } /** @@ -217,28 +142,12 @@ impl> UnificationTable { * can still be undone if there is a snapshot further out. */ pub fn commit(&mut self, snapshot: Snapshot) { - debug!("{}: commit({})", - UnifyKey::tag(None::), - 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; - } + debug!("{}: commit()", UnifyKey::tag(None::)); + self.values.commit(snapshot.snapshot); } pub fn new_key(&mut self, value: V) -> K { - let index = self.values.len(); - - if self.in_snapshot() { - self.undo_log.push(NewVar(index)); - } - - self.values.push(Root(value, 0)); + let index = self.values.push(Root(value, 0)); let k = UnifyKey::from_index(index); debug!("{}: created new key: {}", UnifyKey::tag(None::), @@ -246,20 +155,6 @@ impl> UnificationTable { k } - fn swap_value(&mut self, - index: uint, - new_value: VarValue) - -> VarValue - { - /*! - * 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 { /*! * Find the root node for `vid`. This uses the standard @@ -274,15 +169,7 @@ impl> UnificationTable { let node: Node = self.get(tcx, redirect.clone()); if node.key != redirect { // Path compression - let old_value = - self.swap_value(index, Redirect(node.key.clone())); - - // If we are in a snapshot, record this compression, - // because it's possible that the unification which - // caused it will be rolled back later. - if self.in_snapshot() { - self.undo_log.push(SetVar(index, old_value)); - } + self.values.set(index, Redirect(node.key.clone())); } node } @@ -310,15 +197,12 @@ impl> UnificationTable { */ assert!(self.is_root(&key)); - assert!(self.in_snapshot()); debug!("Updating variable {} to {}", key.repr(tcx), new_value.repr(tcx)); - let index = key.index(); - let old_value = self.swap_value(index, new_value); - self.undo_log.push(SetVar(index, old_value)); + self.values.set(key.index(), new_value); } pub fn unify(&mut self, @@ -359,6 +243,12 @@ impl> UnificationTable { } } +impl sv::SnapshotVecDelegate,()> for Delegate { + fn reverse(&mut self, _: &mut Vec>, _: ()) { + fail!("Nothing to reverse"); + } +} + /////////////////////////////////////////////////////////////////////////// // Code to handle simple keys like ints, floats---anything that // doesn't have a subtyping relationship we need to worry about. @@ -373,7 +263,8 @@ pub trait SimplyUnifiable : Clone + PartialEq + Repr { pub fn err(a_is_expected: bool, a_t: V, - b_t: V) -> ures { + b_t: V) + -> ures { if a_is_expected { Err(SimplyUnifiable::to_type_err( ty::expected_found {expected: a_t, found: b_t})) @@ -483,26 +374,6 @@ impl<'tcx,V:SimplyUnifiable,K:UnifyKey>> /////////////////////////////////////////////////////////////////////////// -// General type keys - -impl UnifyKey> 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>> - { - return &infcx.type_unification_table; - } - - fn tag(_: Option) -> &'static str { - "TyVid" - } -} - -impl UnifyValue for Bounds { } - // Integral type keys impl UnifyKey> for ty::IntVid { diff --git a/src/librustc/util/snapshot_vec.rs b/src/librustc/util/snapshot_vec.rs new file mode 100644 index 00000000000..60e50c84c61 --- /dev/null +++ b/src/librustc/util/snapshot_vec.rs @@ -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 or the MIT license +// , 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 { + /// 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 { + values: Vec, + undo_log: Vec>, + 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 { + fn reverse(&mut self, values: &mut Vec, action: U); +} + +impl> SnapshotVec { + pub fn new(delegate: D) -> SnapshotVec { + 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; + } + } +} diff --git a/src/test/compile-fail/borrowck-closures-mut-of-imm.rs b/src/test/compile-fail/borrowck-closures-mut-of-imm.rs index cdfb569762d..6360a913500 100644 --- a/src/test/compile-fail/borrowck-closures-mut-of-imm.rs +++ b/src/test/compile-fail/borrowck-closures-mut-of-imm.rs @@ -23,8 +23,7 @@ fn a(x: &int) { let c1 = || set(&mut *x); //~^ ERROR cannot borrow let c2 = || set(&mut *x); - //~^ ERROR closure requires unique access to `x` - //~^^ ERROR cannot borrow + //~^ ERROR cannot borrow } fn main() { diff --git a/src/test/compile-fail/borrowck-reborrow-from-shorter-lived-andmut.rs b/src/test/compile-fail/borrowck-reborrow-from-shorter-lived-andmut.rs index 0e1c4758c1b..05cadfd5365 100644 --- a/src/test/compile-fail/borrowck-reborrow-from-shorter-lived-andmut.rs +++ b/src/test/compile-fail/borrowck-reborrow-from-shorter-lived-andmut.rs @@ -17,7 +17,7 @@ struct S<'a> { fn copy_borrowed_ptr<'a,'b>(p: &'a mut S<'b>) -> S<'b> { 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() { diff --git a/src/test/compile-fail/issue-10291.rs b/src/test/compile-fail/issue-10291.rs index 8ae20dfde91..995ae7b3d44 100644 --- a/src/test/compile-fail/issue-10291.rs +++ b/src/test/compile-fail/issue-10291.rs @@ -9,7 +9,7 @@ // except according to those terms. fn test<'x>(x: &'x int) { - drop::< <'z>|&'z int| -> &'z int>(|z| { + drop::< <'z>|&'z int| -> &'z int >(|z| { x //~^ ERROR cannot infer an appropriate lifetime }); diff --git a/src/test/compile-fail/issue-13482.rs b/src/test/compile-fail/issue-13482.rs index 7159aa17623..2d745894426 100644 --- a/src/test/compile-fail/issue-13482.rs +++ b/src/test/compile-fail/issue-13482.rs @@ -12,7 +12,7 @@ fn main() { let x = [1,2]; let y = match x { [] => None, -//~^ ERROR expected `[, .. 2]`, found a fixed vector pattern of size 0 +//~^ ERROR expected `[, .. 2]`, found a fixed vector pattern of size 0 [a,_] => Some(a) }; } diff --git a/src/test/compile-fail/issue-16338.rs b/src/test/compile-fail/issue-16338.rs index d4b31066e5a..305b1fe2ad7 100644 --- a/src/test/compile-fail/issue-16338.rs +++ b/src/test/compile-fail/issue-16338.rs @@ -12,6 +12,6 @@ use std::raw::Slice; fn main() { 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 } diff --git a/src/test/compile-fail/issue-2149.rs b/src/test/compile-fail/issue-2149.rs index afb413584a4..5d07472afbb 100644 --- a/src/test/compile-fail/issue-2149.rs +++ b/src/test/compile-fail/issue-2149.rs @@ -22,5 +22,5 @@ impl vec_monad for Vec { } fn main() { ["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` } diff --git a/src/test/compile-fail/kindck-inherited-copy-bound.rs b/src/test/compile-fail/kindck-inherited-copy-bound.rs new file mode 100644 index 00000000000..2520ed215d5 --- /dev/null +++ b/src/test/compile-fail/kindck-inherited-copy-bound.rs @@ -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 or the MIT license +// , 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 Foo for T { +} + +fn take_param(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` +} diff --git a/src/test/compile-fail/kindck-send-object1.rs b/src/test/compile-fail/kindck-send-object1.rs new file mode 100644 index 00000000000..6a90fd55356 --- /dev/null +++ b/src/test/compile-fail/kindck-send-object1.rs @@ -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 or the MIT license +// , 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() { } +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::>(); +} + +// closure and object types can have lifetime bounds which make +// them not ok +fn test_70<'a>() { + assert_send::(); //~ ERROR does not fulfill the required lifetime +} + +fn test_71<'a>() { + assert_send::>(); //~ ERROR does not fulfill the required lifetime +} + +fn main() { } diff --git a/src/test/compile-fail/kindck-send-object2.rs b/src/test/compile-fail/kindck-send-object2.rs new file mode 100644 index 00000000000..75006477837 --- /dev/null +++ b/src/test/compile-fail/kindck-send-object2.rs @@ -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 or the MIT license +// , 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() { } +trait Dummy { } + +fn test50() { + assert_send::<&'static Dummy>(); //~ ERROR does not fulfill `Send` +} + +fn test53() { + assert_send::>(); //~ ERROR does not fulfill `Send` +} + +// ...unless they are properly bounded +fn test60() { + assert_send::<&'static Dummy+Send>(); +} +fn test61() { + assert_send::>(); +} + +fn main() { } diff --git a/src/test/compile-fail/kindck-send-owned.rs b/src/test/compile-fail/kindck-send-owned.rs new file mode 100644 index 00000000000..0eed05692b9 --- /dev/null +++ b/src/test/compile-fail/kindck-send-owned.rs @@ -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 or the MIT license +// , 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() { } + +// owned content are ok +fn test30() { assert_send::>(); } +fn test31() { assert_send::(); } +fn test32() { assert_send:: >(); } + +// but not if they own a bad thing +fn test40<'a>(_: &'a int) { + assert_send::>(); //~ ERROR does not fulfill the required lifetime +} + +fn main() { } diff --git a/src/test/compile-fail/kindck-send-region-pointers.rs b/src/test/compile-fail/kindck-send-region-pointers.rs new file mode 100644 index 00000000000..cc46d7f4de9 --- /dev/null +++ b/src/test/compile-fail/kindck-send-region-pointers.rs @@ -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 or the MIT license +// , 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() { } + +// 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() { } diff --git a/src/librustc/middle/typeck/infer/macros.rs b/src/test/compile-fail/kindck-send-unsafe.rs similarity index 55% rename from src/librustc/middle/typeck/infer/macros.rs rename to src/test/compile-fail/kindck-send-unsafe.rs index d3e81f07f7d..a9bbfcfa262 100644 --- a/src/librustc/middle/typeck/infer/macros.rs +++ b/src/test/compile-fail/kindck-send-unsafe.rs @@ -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 // http://rust-lang.org/COPYRIGHT. // @@ -8,13 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![macro_escape] +fn assert_send() { } -macro_rules! if_ok( - ($inp: expr) => ( - match $inp { - Ok(v) => { v } - Err(e) => { return Err(e); } - } - ) -) +// unsafe ptrs are ok unless they point at unsendable things +fn test70() { + assert_send::<*mut int>(); +} +fn test71<'a>() { + assert_send::<*mut &'a int>(); //~ ERROR does not fulfill the required lifetime +} + +fn main() { +} diff --git a/src/test/compile-fail/regions-bounds.rs b/src/test/compile-fail/regions-bounds.rs index 68e198ea5b7..e13a6b211a5 100644 --- a/src/test/compile-fail/regions-bounds.rs +++ b/src/test/compile-fail/regions-bounds.rs @@ -17,12 +17,10 @@ struct a_class<'a> { x:&'a int } 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>` - //~^ ERROR cannot infer } 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>` - //~^ ERROR cannot infer } fn main() { } diff --git a/src/test/compile-fail/regions-escape-bound-fn-2.rs b/src/test/compile-fail/regions-escape-bound-fn-2.rs index d752bc97cac..66103eb9588 100644 --- a/src/test/compile-fail/regions-escape-bound-fn-2.rs +++ b/src/test/compile-fail/regions-escape-bound-fn-2.rs @@ -15,7 +15,6 @@ fn with_int(f: |x: &int|) { fn main() { 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)); + //~^ ERROR cannot infer } diff --git a/src/test/compile-fail/regions-escape-bound-fn.rs b/src/test/compile-fail/regions-escape-bound-fn.rs index eb72b4b0c53..fee84cf9656 100644 --- a/src/test/compile-fail/regions-escape-bound-fn.rs +++ b/src/test/compile-fail/regions-escape-bound-fn.rs @@ -14,6 +14,6 @@ fn with_int(f: |x: &int|) { } fn main() { - let mut x: Option<&int> = None; //~ ERROR cannot infer - with_int(|y| x = Some(y)); + let mut x: Option<&int> = None; + with_int(|y| x = Some(y)); //~ ERROR cannot infer } diff --git a/src/test/compile-fail/regions-escape-via-trait-or-not.rs b/src/test/compile-fail/regions-escape-via-trait-or-not.rs index adef1f901fd..b73b5e0649f 100644 --- a/src/test/compile-fail/regions-escape-via-trait-or-not.rs +++ b/src/test/compile-fail/regions-escape-via-trait-or-not.rs @@ -27,7 +27,7 @@ fn with(f: |x: &int| -> R) -> int { } fn return_it() -> int { - with(|o| o) //~ ERROR cannot infer an appropriate lifetime + with(|o| o) //~ ERROR cannot infer } fn main() { diff --git a/src/test/compile-fail/regions-infer-at-fn-not-param.rs b/src/test/compile-fail/regions-infer-at-fn-not-param.rs index e5444aadc1c..8af341e3ace 100644 --- a/src/test/compile-fail/regions-infer-at-fn-not-param.rs +++ b/src/test/compile-fail/regions-infer-at-fn-not-param.rs @@ -22,7 +22,6 @@ struct not_parameterized2 { fn take1<'a>(p: parameterized1) -> parameterized1<'a> { p } //~^ ERROR mismatched types -//~^^ ERROR cannot infer fn take3(p: not_parameterized1) -> not_parameterized1 { p } fn take4(p: not_parameterized2) -> not_parameterized2 { p } diff --git a/src/test/compile-fail/regions-infer-contravariance-due-to-decl.rs b/src/test/compile-fail/regions-infer-contravariance-due-to-decl.rs index d3e9c1f6ea8..4dd028b7884 100644 --- a/src/test/compile-fail/regions-infer-contravariance-due-to-decl.rs +++ b/src/test/compile-fail/regions-infer-contravariance-due-to-decl.rs @@ -33,7 +33,6 @@ fn use_<'short,'long>(c: Contravariant<'short>, // covariant with respect to its parameter 'a. let _: Contravariant<'long> = c; //~ ERROR mismatched types - //~^ ERROR cannot infer an appropriate lifetime } fn main() {} diff --git a/src/test/compile-fail/regions-infer-covariance-due-to-decl.rs b/src/test/compile-fail/regions-infer-covariance-due-to-decl.rs index 2d3ca173012..93c06aecd30 100644 --- a/src/test/compile-fail/regions-infer-covariance-due-to-decl.rs +++ b/src/test/compile-fail/regions-infer-covariance-due-to-decl.rs @@ -30,7 +30,6 @@ fn use_<'short,'long>(c: Covariant<'long>, // contravariant with respect to its parameter 'a. let _: Covariant<'short> = c; //~ ERROR mismatched types - //~^ ERROR cannot infer an appropriate lifetime } fn main() {} diff --git a/src/test/compile-fail/regions-infer-not-param.rs b/src/test/compile-fail/regions-infer-not-param.rs index cadf66c3286..b84f13ec37f 100644 --- a/src/test/compile-fail/regions-infer-not-param.rs +++ b/src/test/compile-fail/regions-infer-not-param.rs @@ -23,11 +23,9 @@ struct indirect2<'a> { } 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_indirect2<'a,'b>(p: indirect2<'a>) -> indirect2<'b> { p } //~ ERROR mismatched types -//~^ ERROR cannot infer fn main() {} diff --git a/src/test/compile-fail/regions-infer-paramd-indirect.rs b/src/test/compile-fail/regions-infer-paramd-indirect.rs index 519223e9753..e862b36dcd1 100644 --- a/src/test/compile-fail/regions-infer-paramd-indirect.rs +++ b/src/test/compile-fail/regions-infer-paramd-indirect.rs @@ -22,18 +22,17 @@ struct c<'a> { } trait set_f<'a> { - fn set_f_ok(&self, b: Gc>); - fn set_f_bad(&self, b: Gc); + fn set_f_ok(&mut self, b: Gc>); + fn set_f_bad(&mut self, b: Gc); } impl<'a> set_f<'a> for c<'a> { - fn set_f_ok(&self, b: Gc>) { + fn set_f_ok(&mut self, b: Gc>) { self.f = b; } - fn set_f_bad(&self, b: Gc) { + fn set_f_bad(&mut self, b: Gc) { self.f = b; //~ ERROR mismatched types: expected `Gc>`, found `Gc>` - //~^ ERROR cannot infer } } diff --git a/src/test/compile-fail/regions-infer-proc-static-upvar.rs b/src/test/compile-fail/regions-infer-proc-static-upvar.rs new file mode 100644 index 00000000000..7fe85290da0 --- /dev/null +++ b/src/test/compile-fail/regions-infer-proc-static-upvar.rs @@ -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 or the MIT license +// , 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() { } diff --git a/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref-mut-ref.rs b/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref-mut-ref.rs index 50ea8b1f2ed..783009f6dcb 100644 --- a/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref-mut-ref.rs +++ b/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref-mut-ref.rs @@ -11,7 +11,7 @@ // 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 { - &mut ***p //~ ERROR lifetime of `p` is too short to guarantee its contents + &mut ***p //~ ERROR cannot infer } fn main() { diff --git a/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref.rs b/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref.rs index 385bc11d1a9..4b1c7a2928b 100644 --- a/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref.rs +++ b/src/test/compile-fail/regions-reborrow-from-shorter-mut-ref.rs @@ -13,7 +13,7 @@ // for `'a` (which must be a sublifetime of `'b`). 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() { diff --git a/src/test/compile-fail/regions-ret-borrowed-1.rs b/src/test/compile-fail/regions-ret-borrowed-1.rs index aac81a2af6b..6d9b2619171 100644 --- a/src/test/compile-fail/regions-ret-borrowed-1.rs +++ b/src/test/compile-fail/regions-ret-borrowed-1.rs @@ -19,8 +19,6 @@ fn with(f: <'a>|x: &'a int| -> R) -> R { fn return_it<'a>() -> &'a int { with(|o| o) //~^ ERROR cannot infer - //~^^ ERROR not valid during the expression - //~^^^ ERROR not valid at this point } fn main() { diff --git a/src/test/compile-fail/regions-ret-borrowed.rs b/src/test/compile-fail/regions-ret-borrowed.rs index dd9421ee2ef..465f4410fbb 100644 --- a/src/test/compile-fail/regions-ret-borrowed.rs +++ b/src/test/compile-fail/regions-ret-borrowed.rs @@ -22,8 +22,6 @@ fn with(f: |x: &int| -> R) -> R { fn return_it<'a>() -> &'a int { with(|o| o) //~^ ERROR cannot infer - //~^^ ERROR not valid during the expression - //~^^^ ERROR not valid at this point } fn main() { diff --git a/src/test/compile-fail/regions-variance-contravariant-use-covariant-in-second-position.rs b/src/test/compile-fail/regions-variance-contravariant-use-covariant-in-second-position.rs index 77a54fec7bf..14ead8da158 100644 --- a/src/test/compile-fail/regions-variance-contravariant-use-covariant-in-second-position.rs +++ b/src/test/compile-fail/regions-variance-contravariant-use-covariant-in-second-position.rs @@ -33,7 +33,6 @@ fn use_<'short,'long>(c: S<'long, 'short>, // covariant with respect to its parameter 'a. let _: S<'long, 'long> = c; //~ ERROR mismatched types - //~^ ERROR cannot infer an appropriate lifetime } fn main() {} diff --git a/src/test/compile-fail/regions-variance-contravariant-use-covariant.rs b/src/test/compile-fail/regions-variance-contravariant-use-covariant.rs index 0fab8926460..3fc58071d2c 100644 --- a/src/test/compile-fail/regions-variance-contravariant-use-covariant.rs +++ b/src/test/compile-fail/regions-variance-contravariant-use-covariant.rs @@ -31,7 +31,6 @@ fn use_<'short,'long>(c: Contravariant<'short>, // covariant with respect to its parameter 'a. let _: Contravariant<'long> = c; //~ ERROR mismatched types - //~^ ERROR cannot infer an appropriate lifetime } fn main() {} diff --git a/src/test/compile-fail/regions-variance-covariant-use-contravariant.rs b/src/test/compile-fail/regions-variance-covariant-use-contravariant.rs index 7dcdc9875e3..844c8151a64 100644 --- a/src/test/compile-fail/regions-variance-covariant-use-contravariant.rs +++ b/src/test/compile-fail/regions-variance-covariant-use-contravariant.rs @@ -31,7 +31,6 @@ fn use_<'short,'long>(c: Covariant<'long>, // contravariant with respect to its parameter 'a. let _: Covariant<'short> = c; //~ ERROR mismatched types - //~^ ERROR cannot infer an appropriate lifetime } fn main() {} diff --git a/src/test/run-pass/regions-infer-reborrow-ref-mut-recurse.rs b/src/test/run-pass/regions-infer-reborrow-ref-mut-recurse.rs new file mode 100644 index 00000000000..efe3994dbb7 --- /dev/null +++ b/src/test/run-pass/regions-infer-reborrow-ref-mut-recurse.rs @@ -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 or the MIT license +// , 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. */ +} diff --git a/src/test/run-pass/regions-infer-static-from-proc.rs b/src/test/run-pass/regions-infer-static-from-proc.rs new file mode 100644 index 00000000000..823644ddfb5 --- /dev/null +++ b/src/test/run-pass/regions-infer-static-from-proc.rs @@ -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 or the MIT license +// , 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); + }); +} diff --git a/src/test/run-pass/regions-scope-chain-example.rs b/src/test/run-pass/regions-scope-chain-example.rs new file mode 100644 index 00000000000..0eacb27a600 --- /dev/null +++ b/src/test/run-pass/regions-scope-chain-example.rs @@ -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 or the MIT license +// , 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() { }