match next(st) {
'b' => {
assert_eq!(next(st), '[');
- let id = parse_uint(st) as ast::NodeId;
+ let id = ty::DebruijnIndex::new(parse_uint(st));
assert_eq!(next(st), '|');
let br = parse_bound_region(st, |x,y| conv(x,y));
assert_eq!(next(st), ']');
fn parse_sig(st: &mut PState, conv: conv_did) -> ty::FnSig {
assert_eq!(next(st), '[');
- let id = parse_uint(st) as ast::NodeId;
- assert_eq!(next(st), '|');
let mut inputs = Vec::new();
while peek(st) != ']' {
inputs.push(parse_ty(st, |x,y| conv(x,y)));
}
_ => ty::FnConverging(parse_ty(st, |x,y| conv(x,y)))
};
- ty::FnSig {binder_id: id,
- inputs: inputs,
+ ty::FnSig {inputs: inputs,
output: output,
variadic: variadic}
}
pub fn enc_region(w: &mut SeekableMemWriter, cx: &ctxt, r: ty::Region) {
match r {
ty::ReLateBound(id, br) => {
- mywrite!(w, "b[{}|", id);
+ mywrite!(w, "b[{}|", id.depth);
enc_bound_region(w, cx, br);
mywrite!(w, "]");
}
}
fn enc_fn_sig(w: &mut SeekableMemWriter, cx: &ctxt, fsig: &ty::FnSig) {
- mywrite!(w, "[{}|", fsig.binder_id);
+ mywrite!(w, "[");
for ty in fsig.inputs.iter() {
enc_ty(w, cx, *ty);
}
impl tr for ty::Region {
fn tr(&self, dcx: &DecodeContext) -> ty::Region {
match *self {
- ty::ReLateBound(id, br) => {
- ty::ReLateBound(dcx.tr_id(id), br.tr(dcx))
+ ty::ReLateBound(debruijn, br) => {
+ ty::ReLateBound(debruijn, br.tr(dcx))
}
ty::ReEarlyBound(id, space, index, ident) => {
ty::ReEarlyBound(dcx.tr_id(id), space, index, ident)
DefEarlyBoundRegion(/* space */ subst::ParamSpace,
/* index */ uint,
/* lifetime decl */ ast::NodeId),
- DefLateBoundRegion(/* binder_id */ ast::NodeId,
- /* depth */ uint,
+ DefLateBoundRegion(ty::DebruijnIndex,
/* lifetime decl */ ast::NodeId),
DefFreeRegion(/* block scope */ ast::NodeId,
/* lifetime decl */ ast::NodeId),
/// EarlyScope(i, ['a, 'b, ...], s) extends s with early-bound
/// lifetimes, assigning indexes 'a => i, 'b => i+1, ... etc.
EarlyScope(subst::ParamSpace, &'a Vec<ast::LifetimeDef>, Scope<'a>),
- /// LateScope(binder_id, ['a, 'b, ...], s) extends s with late-bound
+ /// LateScope(['a, 'b, ...], s) extends s with late-bound
/// lifetimes introduced by the declaration binder_id.
- LateScope(ast::NodeId, &'a Vec<ast::LifetimeDef>, Scope<'a>),
+ LateScope(&'a Vec<ast::LifetimeDef>, Scope<'a>),
/// lifetimes introduced by items within a code block are scoped
/// to that block.
BlockScope(ast::NodeId, Scope<'a>),
}
fn visit_fn(&mut self, fk: visit::FnKind<'v>, fd: &'v ast::FnDecl,
- b: &'v ast::Block, s: Span, n: ast::NodeId) {
+ b: &'v ast::Block, s: Span, _: ast::NodeId) {
match fk {
visit::FkItemFn(_, generics, _, _) |
visit::FkMethod(_, generics, _) => {
self.visit_early_late(
- subst::FnSpace, n, generics,
+ subst::FnSpace, generics,
|this| visit::walk_fn(this, fk, fd, b, s))
}
visit::FkFnBlock(..) => {
}
fn visit_ty(&mut self, ty: &ast::Ty) {
- let lifetimes = match ty.node {
- ast::TyClosure(ref c) | ast::TyProc(ref c) => &c.lifetimes,
- ast::TyBareFn(ref c) => &c.lifetimes,
- _ => return visit::walk_ty(self, ty)
- };
-
- self.with(LateScope(ty.id, lifetimes, self.scope), |this| {
- this.check_lifetime_defs(lifetimes);
- visit::walk_ty(this, ty);
- });
+ match ty.node {
+ ast::TyClosure(ref c) | ast::TyProc(ref c) => {
+ // Careful, the bounds on a closure/proc are *not* within its binder.
+ visit::walk_ty_param_bounds_helper(self, &c.bounds);
+ visit::walk_lifetime_decls_helper(self, &c.lifetimes);
+ self.with(LateScope(&c.lifetimes, self.scope), |this| {
+ this.check_lifetime_defs(&c.lifetimes);
+ for argument in c.decl.inputs.iter() {
+ this.visit_ty(&*argument.ty)
+ }
+ visit::walk_fn_ret_ty(this, &c.decl.output);
+ });
+ }
+ ast::TyBareFn(ref c) => {
+ visit::walk_lifetime_decls_helper(self, &c.lifetimes);
+ self.with(LateScope(&c.lifetimes, self.scope), |this| {
+ // a bare fn has no bounds, so everything
+ // contained within is scoped within its binder.
+ this.check_lifetime_defs(&c.lifetimes);
+ visit::walk_ty(this, ty);
+ });
+ }
+ _ => {
+ visit::walk_ty(self, ty)
+ }
+ }
}
fn visit_ty_method(&mut self, m: &ast::TypeMethod) {
self.visit_early_late(
- subst::FnSpace, m.id, &m.generics,
+ subst::FnSpace, &m.generics,
|this| visit::walk_ty_method(this, m))
}
fn visit_trait_ref(&mut self, trait_ref: &ast::TraitRef) {
self.visit_path(&trait_ref.path, trait_ref.ref_id);
}
+}
+
+impl<'a> LifetimeContext<'a> {
+ fn with(&mut self, wrap_scope: ScopeChain, f: |&mut LifetimeContext|) {
+ let LifetimeContext {sess, ref mut named_region_map, ..} = *self;
+ let mut this = LifetimeContext {
+ sess: sess,
+ named_region_map: *named_region_map,
+ scope: &wrap_scope,
+ def_map: self.def_map,
+ };
+ debug!("entering scope {}", this.scope);
+ f(&mut this);
+ debug!("exiting scope {}", this.scope);
+ }
/// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
fn visit_early_late(&mut self,
early_space: subst::ParamSpace,
- binder_id: ast::NodeId,
generics: &ast::Generics,
walk: |&mut LifetimeContext|) {
/*!
let referenced_idents = early_bound_lifetime_names(generics);
- debug!("visit_early_late: binder_id={} referenced_idents={}",
- binder_id,
+ debug!("visit_early_late: referenced_idents={}",
referenced_idents);
let (early, late) = generics.lifetimes.clone().partition(
|l| referenced_idents.iter().any(|&i| i == l.lifetime.name));
self.with(EarlyScope(early_space, &early, self.scope), |this| {
- this.with(LateScope(binder_id, &late, this.scope), |this| {
+ this.with(LateScope(&late, this.scope), |this| {
this.check_lifetime_defs(&generics.lifetimes);
walk(this);
});
// block, then the lifetime is not bound but free, so switch
// over to `resolve_free_lifetime_ref()` to complete the
// search.
- let mut depth = 0;
+ let mut late_depth = 0;
let mut scope = self.scope;
loop {
match *scope {
return;
}
None => {
- depth += 1;
scope = s;
}
}
}
- LateScope(binder_id, lifetimes, s) => {
+ LateScope(lifetimes, s) => {
match search_lifetimes(lifetimes, lifetime_ref) {
Some((_index, decl_id)) => {
- let def = DefLateBoundRegion(binder_id, depth, decl_id);
+ let debruijn = ty::DebruijnIndex::new(late_depth + 1);
+ let def = DefLateBoundRegion(debruijn, decl_id);
self.insert_lifetime(lifetime_ref, def);
return;
}
None => {
- depth += 1;
+ late_depth += 1;
scope = s;
}
}
}
EarlyScope(_, lifetimes, s) |
- LateScope(_, lifetimes, s) => {
+ LateScope(lifetimes, s) => {
search_result = search_lifetimes(lifetimes, lifetime_ref);
if search_result.is_some() {
break;
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
EarlyScope(space, defs, _) => write!(fmt, "EarlyScope({}, {})", space, defs),
- LateScope(id, defs, _) => write!(fmt, "LateScope({}, {})", id, defs),
+ LateScope(defs, _) => write!(fmt, "LateScope({})", defs),
BlockScope(id, _) => write!(fmt, "BlockScope({})", id),
RootScope => write!(fmt, "RootScope"),
}
pub use self::RegionSubsts::*;
use middle::ty;
-use middle::ty_fold;
-use middle::ty_fold::{TypeFoldable, TypeFolder};
+use middle::ty_fold::{mod, TypeFoldable, TypeFolder};
use util::ppaux::Repr;
use std::fmt;
// Depth of type stack
ty_stack_depth: uint,
+
+ // Number of region binders we have passed through while doing the substitution
+ region_binders_passed: uint,
}
impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> {
fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { self.tcx }
+ fn enter_region_binder(&mut self) {
+ self.region_binders_passed += 1;
+ }
+
+ fn exit_region_binder(&mut self) {
+ self.region_binders_passed -= 1;
+ }
+
fn fold_region(&mut self, r: ty::Region) -> ty::Region {
// Note: This routine only handles regions that are bound on
// type declarations and other outer declarations, not those
ErasedRegions => ty::ReStatic,
NonerasedRegions(ref regions) =>
match regions.opt_get(space, i) {
- Some(t) => *t,
+ Some(&r) => {
+ self.shift_region_through_binders(r)
+ }
None => {
let span = self.span.unwrap_or(DUMMY_SP);
self.tcx().sess.span_bug(
let t1 = match ty::get(t).sty {
ty::ty_param(p) => {
- check(self,
- p,
- t,
- self.substs.types.opt_get(p.space, p.idx),
- p.space,
- p.idx)
+ self.ty_for_param(p, t)
}
_ => {
ty_fold::super_fold_ty(self, t)
}
return t1;
+ }
+}
- fn check(this: &SubstFolder,
- p: ty::ParamTy,
- source_ty: ty::t,
- opt_ty: Option<&ty::t>,
- space: ParamSpace,
- index: uint)
- -> ty::t {
- match opt_ty {
- Some(t) => *t,
- None => {
- let span = this.span.unwrap_or(DUMMY_SP);
- this.tcx().sess.span_bug(
- span,
- format!("Type parameter `{}` ({}/{}/{}) out of range \
+impl<'a,'tcx> SubstFolder<'a,'tcx> {
+ fn ty_for_param(&self, p: ty::ParamTy, source_ty: ty::t) -> ty::t {
+ // Look up the type in the substitutions. It really should be in there.
+ let opt_ty = self.substs.types.opt_get(p.space, p.idx);
+ let ty = match opt_ty {
+ Some(t) => *t,
+ None => {
+ let span = self.span.unwrap_or(DUMMY_SP);
+ self.tcx().sess.span_bug(
+ span,
+ format!("Type parameter `{}` ({}/{}/{}) out of range \
when substituting (root type={}) substs={}",
- p.repr(this.tcx()),
- source_ty.repr(this.tcx()),
- space,
- index,
- this.root_ty.repr(this.tcx()),
- this.substs.repr(this.tcx())).as_slice());
- }
+ p.repr(self.tcx()),
+ source_ty.repr(self.tcx()),
+ p.space,
+ p.idx,
+ self.root_ty.repr(self.tcx()),
+ self.substs.repr(self.tcx())).as_slice());
}
+ };
+
+ self.shift_regions_through_binders(ty)
+ }
+
+ fn shift_regions_through_binders(&self, ty: ty::t) -> ty::t {
+ /*!
+ * It is sometimes necessary to adjust the debruijn indices
+ * during substitution. This occurs when we are substituting a
+ * type with escaping regions into a context where we have
+ * passed through region binders. That's quite a
+ * mouthful. Let's see an example:
+ *
+ * ```
+ * type Func<A> = fn(A);
+ * type MetaFunc = for<'a> fn(Func<&'a int>)
+ * ```
+ *
+ * The type `MetaFunc`, when fully expanded, will be
+ *
+ * for<'a> fn(fn(&'a int))
+ * ^~ ^~ ^~~
+ * | | |
+ * | | DebruijnIndex of 2
+ * Binders
+ *
+ * Here the `'a` lifetime is bound in the outer function, but
+ * appears as an argument of the inner one. Therefore, that
+ * appearance will have a DebruijnIndex of 2, because we must
+ * skip over the inner binder (remember that we count Debruijn
+ * indices from 1). However, in the definition of `MetaFunc`,
+ * the binder is not visible, so the type `&'a int` will have
+ * a debruijn index of 1. It's only during the substitution
+ * that we can see we must increase the depth by 1 to account
+ * for the binder that we passed through.
+ *
+ * As a second example, consider this twist:
+ *
+ * ```
+ * type FuncTuple<A> = (A,fn(A));
+ * type MetaFuncTuple = for<'a> fn(FuncTuple<&'a int>)
+ * ```
+ *
+ * Here the final type will be:
+ *
+ * for<'a> fn((&'a int, fn(&'a int)))
+ * ^~~ ^~~
+ * | |
+ * DebruijnIndex of 1 |
+ * DebruijnIndex of 2
+ *
+ * As indicated in the diagram, here the same type `&'a int`
+ * is substituted once, but in the first case we do not
+ * increase the Debruijn index and in the second case we
+ * do. The reason is that only in the second case have we
+ * passed through a fn binder.
+ */
+
+ debug!("shift_regions(ty={}, region_binders_passed={}, type_has_escaping_regions={})",
+ ty.repr(self.tcx()), self.region_binders_passed, ty::type_has_escaping_regions(ty));
+
+ if self.region_binders_passed == 0 || !ty::type_has_escaping_regions(ty) {
+ return ty;
}
+
+ let result = ty_fold::shift_regions(self.tcx(), self.region_binders_passed, &ty);
+ debug!("shift_regions: shifted result = {}", result.repr(self.tcx()));
+
+ result
+ }
+
+ fn shift_region_through_binders(&self, region: ty::Region) -> ty::Region {
+ ty_fold::shift_region(region, self.region_binders_passed)
}
}
use middle::traits;
use middle::ty;
use middle::typeck;
-use middle::ty_fold::{mod, TypeFoldable,TypeFolder};
+use middle::ty_fold::{mod, TypeFoldable, TypeFolder, HigherRankedFoldable};
use middle;
use util::ppaux::{note_and_explain_region, bound_region_ptr_to_string};
use util::ppaux::{trait_store_to_string, ty_to_string};
// recursing over the type itself.
bitflags! {
flags TypeFlags: u32 {
- const NO_TYPE_FLAGS = 0b0,
- const HAS_PARAMS = 0b1,
- const HAS_SELF = 0b10,
- const HAS_TY_INFER = 0b100,
- const HAS_RE_INFER = 0b1000,
- const HAS_REGIONS = 0b10000,
- const HAS_TY_ERR = 0b100000,
+ const NO_TYPE_FLAGS = 0b0,
+ const HAS_PARAMS = 0b1,
+ const HAS_SELF = 0b10,
+ const HAS_TY_INFER = 0b100,
+ const HAS_RE_INFER = 0b1000,
+ const HAS_RE_LATE_BOUND = 0b10000,
+ const HAS_REGIONS = 0b100000,
+ const HAS_TY_ERR = 0b1000000,
const NEEDS_SUBST = HAS_PARAMS.bits | HAS_SELF.bits | HAS_REGIONS.bits,
}
}
pub struct t_box_ {
pub sty: sty,
pub flags: TypeFlags,
+
+ // the maximal depth of any bound regions appearing in this type.
+ region_depth: uint,
}
impl fmt::Show for TypeFlags {
tbox_has_flag(get(t), HAS_TY_INFER | HAS_RE_INFER)
}
+pub fn type_has_late_bound_regions(ty: t) -> bool {
+ get(ty).flags.intersects(HAS_RE_LATE_BOUND)
+}
+
+pub fn type_has_escaping_regions(t: t) -> bool {
+ /*!
+ * An "escaping region" is a bound region whose binder is not part of `t`.
+ *
+ * So, for example, consider a type like the following, which has two
+ * binders:
+ *
+ * for<'a> fn(x: for<'b> fn(&'a int, &'b int))
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope
+ * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ inner scope
+ *
+ * This type has *bound regions* (`'a`, `'b`), but it does not
+ * have escaping regions, because the binders of both `'a` and
+ * `'b` are part of the type itself. However, if we consider the
+ * *inner fn type*, that type has an escaping region: `'a`.
+ *
+ * Note that what I'm calling an "escaping region" is often just
+ * called a "free region". However, we already use the term "free
+ * region". It refers to the regions that we use to represent
+ * bound regions on a fn definition while we are typechecking its
+ * body.
+ *
+ * To clarify, conceptually there is no particular difference
+ * between an "escaping" region and a "free" region. However,
+ * there is a big difference in practice. Basically, when
+ * "entering" a binding level, one is generally required to do
+ * some sort of processing to a bound region, such as replacing it
+ * with a fresh/skolemized region, or making an entry in the
+ * environment to represent the scope to which it is attached,
+ * etc. An escaping region represents a bound region for which
+ * this processing has not yet been done.
+ */
+
+ type_escapes_depth(t, 0)
+}
+
+pub fn type_escapes_depth(t: t, depth: uint) -> bool {
+ get(t).region_depth > depth
+}
+
#[deriving(Clone, PartialEq, Eq, Hash, Show)]
pub struct BareFnTy {
pub fn_style: ast::FnStyle,
* Signature of a function type, which I have arbitrarily
* decided to use to refer to the input/output types.
*
- * - `binder_id` is the node id where this fn type appeared;
- * it is used to identify all the bound regions appearing
- * in the input/output types that are bound by this fn type
- * (vs some enclosing or enclosed fn type)
* - `inputs` is the list of arguments and their modes.
* - `output` is the return type.
* - `variadic` indicates whether this is a varidic function. (only true for foreign fns)
+ *
+ * Note that a `FnSig` introduces a level of region binding, to
+ * account for late-bound parameters that appear in the types of the
+ * fn's arguments or the fn's return type.
*/
#[deriving(Clone, PartialEq, Eq, Hash)]
pub struct FnSig {
- pub binder_id: ast::NodeId,
pub inputs: Vec<t>,
pub output: FnOutput,
pub variadic: bool
pub def_id: DefId
}
+/**
+ * A [De Bruijn index][dbi] is a standard means of representing
+ * regions (and perhaps later types) in a higher-ranked setting. In
+ * particular, imagine a type like this:
+ *
+ * for<'a> fn(for<'b> fn(&'b int, &'a int), &'a char)
+ * ^ ^ | | |
+ * | | | | |
+ * | +------------+ 1 | |
+ * | | |
+ * +--------------------------------+ 2 |
+ * | |
+ * +------------------------------------------+ 1
+ *
+ * In this type, there are two binders (the outer fn and the inner
+ * fn). We need to be able to determine, for any given region, which
+ * fn type it is bound by, the inner or the outer one. There are
+ * various ways you can do this, but a De Bruijn index is one of the
+ * more convenient and has some nice properties. The basic idea is to
+ * count the number of binders, inside out. Some examples should help
+ * clarify what I mean.
+ *
+ * Let's start with the reference type `&'b int` that is the first
+ * argument to the inner function. This region `'b` is assigned a De
+ * Bruijn index of 1, meaning "the innermost binder" (in this case, a
+ * fn). The region `'a` that appears in the second argument type (`&'a
+ * int`) would then be assigned a De Bruijn index of 2, meaning "the
+ * second-innermost binder". (These indices are written on the arrays
+ * in the diagram).
+ *
+ * What is interesting is that De Bruijn index attached to a particular
+ * variable will vary depending on where it appears. For example,
+ * the final type `&'a char` also refers to the region `'a` declared on
+ * the outermost fn. But this time, this reference is not nested within
+ * any other binders (i.e., it is not an argument to the inner fn, but
+ * rather the outer one). Therefore, in this case, it is assigned a
+ * De Bruijn index of 1, because the innermost binder in that location
+ * is the outer fn.
+ *
+ * [dbi]: http://en.wikipedia.org/wiki/De_Bruijn_index
+ */
+#[deriving(Clone, PartialEq, Eq, Hash, Encodable, Decodable, Show)]
+pub struct DebruijnIndex {
+ // We maintain the invariant that this is never 0. So 1 indicates
+ // the innermost binder. To ensure this, create with `DebruijnIndex::new`.
+ pub depth: uint,
+}
+
/// Representation of regions:
#[deriving(Clone, PartialEq, Eq, Hash, Encodable, Decodable, Show)]
pub enum Region {
ast::Name),
// Region bound in a function scope, which will be substituted when the
- // function is called. The first argument must be the `binder_id` of
- // some enclosing function signature.
- ReLateBound(/* binder_id */ ast::NodeId, BoundRegion),
+ // function is called.
+ ReLateBound(DebruijnIndex, BoundRegion),
/// When checking a function body, the types of all arguments and so forth
/// that refer to bound region parameters are modified to refer to free
impl Region {
pub fn is_bound(&self) -> bool {
- match self {
- &ty::ReEarlyBound(..) => true,
- &ty::ReLateBound(..) => true,
+ match *self {
+ ty::ReEarlyBound(..) => true,
+ ty::ReLateBound(..) => true,
_ => false
}
}
+
+ pub fn escapes_depth(&self, depth: uint) -> bool {
+ match *self {
+ ty::ReLateBound(debruijn, _) => debruijn.depth > depth,
+ _ => false,
+ }
+ }
}
#[deriving(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Encodable, Decodable, Show)]
pub static $name: t_box_ = t_box_ {
sty: $sty,
flags: super::NO_TYPE_FLAGS,
+ region_depth: 0,
};
)
)
pub static TY_ERR: t_box_ = t_box_ {
sty: super::ty_err,
flags: super::HAS_TY_ERR,
+ region_depth: 0,
};
}
pub substs: Substs,
}
+/**
+ * Binder serves as a synthetic binder for lifetimes. It is used when
+ * we wish to replace the escaping higher-ranked lifetimes in a type
+ * or something else that is not itself a binder (this is because the
+ * `replace_late_bound_regions` function replaces all lifetimes bound
+ * by the binder supplied to it; but a type is not a binder, so you
+ * must introduce an artificial one).
+ */
+#[deriving(Clone, PartialEq, Eq, Hash, Show)]
+pub struct Binder<T> {
+ pub value: T
+}
+
+pub fn bind<T>(value: T) -> Binder<T> {
+ Binder { value: value }
+}
+
#[deriving(Clone, PartialEq)]
pub enum IntVarValue {
IntType(ast::IntTy),
_ => ()
}
- let mut flags = NO_TYPE_FLAGS;
- fn rflags(r: Region) -> TypeFlags {
- HAS_REGIONS | {
- match r {
- ty::ReInfer(_) => HAS_RE_INFER,
- _ => NO_TYPE_FLAGS,
- }
- }
- }
- fn sflags(substs: &Substs) -> TypeFlags {
- let mut f = NO_TYPE_FLAGS;
- let mut i = substs.types.iter();
- for tt in i {
- f = f | get(*tt).flags;
- }
- match substs.regions {
- subst::ErasedRegions => {}
- subst::NonerasedRegions(ref regions) => {
- for r in regions.iter() {
- f = f | rflags(*r)
- }
- }
- }
- return f;
- }
- fn flags_for_bounds(bounds: &ExistentialBounds) -> TypeFlags {
- rflags(bounds.region_bound)
- }
- match &st {
- &ty_bool | &ty_char | &ty_int(_) | &ty_float(_) | &ty_uint(_) |
- &ty_str => {}
- // You might think that we could just return ty_err for
- // any type containing ty_err as a component, and get
- // rid of the HAS_TY_ERR flag -- likewise for ty_bot (with
- // the exception of function types that return bot).
- // But doing so caused sporadic memory corruption, and
- // neither I (tjc) nor nmatsakis could figure out why,
- // so we're doing it this way.
- &ty_err => flags = flags | HAS_TY_ERR,
- &ty_param(ref p) => {
- if p.space == subst::SelfSpace {
- flags = flags | HAS_SELF;
- } else {
- flags = flags | HAS_PARAMS;
- }
- }
- &ty_unboxed_closure(_, ref region, ref substs) => {
- flags = flags | rflags(*region);
- flags = flags | sflags(substs);
- }
- &ty_infer(_) => flags = flags | HAS_TY_INFER,
- &ty_enum(_, ref substs) | &ty_struct(_, ref substs) => {
- flags = flags | sflags(substs);
- }
- &ty_trait(box TyTrait { ref principal, ref bounds }) => {
- flags = flags | sflags(&principal.substs);
- flags = flags | flags_for_bounds(bounds);
- }
- &ty_uniq(tt) | &ty_vec(tt, _) | &ty_open(tt) => {
- flags = flags | get(tt).flags
- }
- &ty_ptr(ref m) => {
- flags = flags | get(m.ty).flags;
- }
- &ty_rptr(r, ref m) => {
- flags = flags | rflags(r);
- flags = flags | get(m.ty).flags;
- }
- &ty_tup(ref ts) => for tt in ts.iter() { flags = flags | get(*tt).flags; },
- &ty_bare_fn(ref f) => {
- for a in f.sig.inputs.iter() { flags = flags | get(*a).flags; }
- if let ty::FnConverging(output) = f.sig.output {
- flags = flags | get(output).flags;
- }
- }
- &ty_closure(ref f) => {
- match f.store {
- RegionTraitStore(r, _) => {
- flags = flags | rflags(r);
- }
- _ => {}
- }
- for a in f.sig.inputs.iter() { flags = flags | get(*a).flags; }
- if let ty::FnConverging(output) = f.sig.output {
- flags = flags | get(output).flags;
- }
- flags = flags | flags_for_bounds(&f.bounds);
- }
- }
+ let flags = FlagComputation::for_sty(&st);
let t = cx.type_arena.alloc(t_box_ {
sty: st,
- flags: flags,
+ flags: flags.flags,
+ region_depth: flags.depth,
});
let sty_ptr = &t.sty as *const sty;
}
}
+struct FlagComputation {
+ flags: TypeFlags,
+
+ // maximum depth of any bound region that we have seen thus far
+ depth: uint,
+}
+
+impl FlagComputation {
+ fn new() -> FlagComputation {
+ FlagComputation { flags: NO_TYPE_FLAGS, depth: 0 }
+ }
+
+ fn for_sty(st: &sty) -> FlagComputation {
+ let mut result = FlagComputation::new();
+ result.add_sty(st);
+ result
+ }
+
+ fn add_flags(&mut self, flags: TypeFlags) {
+ self.flags = self.flags | flags;
+ }
+
+ fn add_depth(&mut self, depth: uint) {
+ if depth > self.depth {
+ self.depth = depth;
+ }
+ }
+
+ fn add_bound_computation(&mut self, computation: &FlagComputation) {
+ /*!
+ * Adds the flags/depth from a set of types that appear within
+ * the current type, but within a region binder.
+ */
+
+ self.add_flags(computation.flags);
+
+ // The types that contributed to `computation` occured within
+ // a region binder, so subtract one from the region depth
+ // within when adding the depth to `self`.
+ let depth = computation.depth;
+ if depth > 0 {
+ self.add_depth(depth - 1);
+ }
+ }
+
+ fn add_sty(&mut self, st: &sty) {
+ match st {
+ &ty_bool |
+ &ty_char |
+ &ty_int(_) |
+ &ty_float(_) |
+ &ty_uint(_) |
+ &ty_str => {
+ }
+
+ // You might think that we could just return ty_err for
+ // any type containing ty_err as a component, and get
+ // rid of the HAS_TY_ERR flag -- likewise for ty_bot (with
+ // the exception of function types that return bot).
+ // But doing so caused sporadic memory corruption, and
+ // neither I (tjc) nor nmatsakis could figure out why,
+ // so we're doing it this way.
+ &ty_err => {
+ self.add_flags(HAS_TY_ERR)
+ }
+
+ &ty_param(ref p) => {
+ if p.space == subst::SelfSpace {
+ self.add_flags(HAS_SELF);
+ } else {
+ self.add_flags(HAS_PARAMS);
+ }
+ }
+
+ &ty_unboxed_closure(_, ref region, ref substs) => {
+ self.add_region(*region);
+ self.add_substs(substs);
+ }
+
+ &ty_infer(_) => {
+ self.add_flags(HAS_TY_INFER)
+ }
+
+ &ty_enum(_, ref substs) | &ty_struct(_, ref substs) => {
+ self.add_substs(substs);
+ }
+
+ &ty_trait(box TyTrait { ref principal, ref bounds }) => {
+ self.add_substs(&principal.substs);
+ self.add_bounds(bounds);
+ }
+
+ &ty_uniq(tt) | &ty_vec(tt, _) | &ty_open(tt) => {
+ self.add_ty(tt)
+ }
+
+ &ty_ptr(ref m) => {
+ self.add_ty(m.ty);
+ }
+
+ &ty_rptr(r, ref m) => {
+ self.add_region(r);
+ self.add_ty(m.ty);
+ }
+
+ &ty_tup(ref ts) => {
+ self.add_tys(ts[]);
+ }
+
+ &ty_bare_fn(ref f) => {
+ self.add_fn_sig(&f.sig);
+ }
+
+ &ty_closure(ref f) => {
+ match f.store {
+ RegionTraitStore(r, _) => {
+ self.add_region(r);
+ }
+ _ => {}
+ }
+ self.add_fn_sig(&f.sig);
+ self.add_bounds(&f.bounds);
+ }
+ }
+ }
+
+ fn add_ty(&mut self, t: t) {
+ let t_box = get(t);
+ self.add_flags(t_box.flags);
+ self.add_depth(t_box.region_depth);
+ }
+
+ fn add_tys(&mut self, tys: &[t]) {
+ for &ty in tys.iter() {
+ self.add_ty(ty);
+ }
+ }
+
+ fn add_fn_sig(&mut self, fn_sig: &FnSig) {
+ let mut computation = FlagComputation::new();
+
+ computation.add_tys(fn_sig.inputs[]);
+
+ if let ty::FnConverging(output) = fn_sig.output {
+ computation.add_ty(output);
+ }
+
+ self.add_bound_computation(&computation);
+ }
+
+ fn add_region(&mut self, r: Region) {
+ self.add_flags(HAS_REGIONS);
+ match r {
+ ty::ReInfer(_) => { self.add_flags(HAS_RE_INFER); }
+ ty::ReLateBound(debruijn, _) => {
+ self.add_flags(HAS_RE_LATE_BOUND);
+ self.add_depth(debruijn.depth);
+ }
+ _ => { }
+ }
+ }
+
+ fn add_substs(&mut self, substs: &Substs) {
+ self.add_tys(substs.types.as_slice());
+ match substs.regions {
+ subst::ErasedRegions => {}
+ subst::NonerasedRegions(ref regions) => {
+ for &r in regions.iter() {
+ self.add_region(r);
+ }
+ }
+ }
+ }
+
+ fn add_bounds(&mut self, bounds: &ExistentialBounds) {
+ self.add_region(bounds.region_bound);
+ }
+}
+
#[inline]
pub fn mk_prim_t(primitive: &'static t_box_) -> t {
unsafe {
}
pub fn mk_ctor_fn(cx: &ctxt,
- binder_id: ast::NodeId,
input_tys: &[ty::t],
output: ty::t) -> t {
let input_args = input_tys.iter().map(|t| *t).collect();
fn_style: ast::NormalFn,
abi: abi::Rust,
sig: FnSig {
- binder_id: binder_id,
inputs: input_args,
output: ty::FnConverging(output),
variadic: false
types: substs.types.fold_with(self) }
}
- fn fold_sig(&mut self,
- sig: &ty::FnSig)
- -> ty::FnSig {
+ fn fold_fn_sig(&mut self,
+ sig: &ty::FnSig)
+ -> ty::FnSig {
// The binder-id is only relevant to bound regions, which
// are erased at trans time.
ty::FnSig {
- binder_id: ast::DUMMY_NODE_ID,
inputs: sig.inputs.fold_with(self),
output: sig.output.fold_with(self),
variadic: sig.variadic,
self.autoderefs == 0 && self.autoref.is_none()
}
}
+
+pub fn liberate_late_bound_regions<HR>(
+ tcx: &ty::ctxt,
+ scope_id: ast::NodeId,
+ value: &HR)
+ -> HR
+ where HR : HigherRankedFoldable
+{
+ /*!
+ * Replace any late-bound regions bound in `value` with free variants
+ * attached to scope-id `scope_id`.
+ */
+
+ replace_late_bound_regions(
+ tcx, value,
+ |br, _| ty::ReFree(ty::FreeRegion{scope_id: scope_id, bound_region: br})).0
+}
+
+pub fn erase_late_bound_regions<HR>(
+ tcx: &ty::ctxt,
+ value: &HR)
+ -> HR
+ where HR : HigherRankedFoldable
+{
+ /*!
+ * Replace any late-bound regions bound in `value` with `'static`.
+ * Useful in trans.
+ */
+
+ replace_late_bound_regions(tcx, value, |_, _| ty::ReStatic).0
+}
+
+pub fn replace_late_bound_regions<HR>(
+ tcx: &ty::ctxt,
+ value: &HR,
+ mapf: |BoundRegion, DebruijnIndex| -> ty::Region)
+ -> (HR, FnvHashMap<ty::BoundRegion,ty::Region>)
+ where HR : HigherRankedFoldable
+{
+ /*!
+ * Replaces the late-bound-regions in `value` that are bound by `value`.
+ */
+
+ debug!("replace_late_bound_regions({})", value.repr(tcx));
+
+ let mut map = FnvHashMap::new();
+ let value = {
+ let mut f = ty_fold::RegionFolder::new(tcx, |region, current_depth| {
+ debug!("region={}", region.repr(tcx));
+ match region {
+ ty::ReLateBound(debruijn, br) if debruijn.depth == current_depth => {
+ * match map.entry(br) {
+ Vacant(entry) => entry.set(mapf(br, debruijn)),
+ Occupied(entry) => entry.into_mut(),
+ }
+ }
+ _ => {
+ region
+ }
+ }
+ });
+
+ // Note: use `fold_contents` not `fold_with`. If we used
+ // `fold_with`, it would consider the late-bound regions bound
+ // by `value` to be bound, but we want to consider them as
+ // `free`.
+ value.fold_contents(&mut f)
+ };
+ debug!("resulting map: {} value: {}", map, value.repr(tcx));
+ (value, map)
+}
+
+impl DebruijnIndex {
+ pub fn new(depth: uint) -> DebruijnIndex {
+ assert!(depth > 0);
+ DebruijnIndex { depth: depth }
+ }
+
+ pub fn shifted(&self, amount: uint) -> DebruijnIndex {
+ DebruijnIndex { depth: self.depth + amount }
+ }
+}
pub trait TypeFolder<'tcx> {
fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>;
+ /// Invoked by the `super_*` routines when we enter a region
+ /// binding level (for example, when entering a function
+ /// signature). This is used by clients that want to track the
+ /// Debruijn index nesting level.
+ fn enter_region_binder(&mut self) { }
+
+ /// Invoked by the `super_*` routines when we exit a region
+ /// binding level. This is used by clients that want to
+ /// track the Debruijn index nesting level.
+ fn exit_region_binder(&mut self) { }
+
fn fold_ty(&mut self, t: ty::t) -> ty::t {
super_fold_ty(self, t)
}
super_fold_substs(self, substs)
}
- fn fold_sig(&mut self,
+ fn fold_fn_sig(&mut self,
sig: &ty::FnSig)
-> ty::FnSig {
- super_fold_sig(self, sig)
+ super_fold_fn_sig(self, sig)
}
fn fold_output(&mut self,
}
}
+impl<T:TypeFoldable,U:TypeFoldable> TypeFoldable for (T, U) {
+ fn fold_with<'tcx, F:TypeFolder<'tcx>>(&self, folder: &mut F) -> (T, U) {
+ (self.0.fold_with(folder), self.1.fold_with(folder))
+ }
+}
+
impl<T:TypeFoldable> TypeFoldable for Option<T> {
fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Option<T> {
self.as_ref().map(|t| t.fold_with(folder))
}
}
+impl<T:TypeFoldable> TypeFoldable for ty::Binder<T> {
+ fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::Binder<T> {
+ folder.enter_region_binder();
+ let result = ty::bind(self.value.fold_with(folder));
+ folder.exit_region_binder();
+ result
+ }
+}
+
impl<T:TypeFoldable> TypeFoldable for OwnedSlice<T> {
fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> OwnedSlice<T> {
self.iter().map(|t| t.fold_with(folder)).collect()
impl<T:TypeFoldable> TypeFoldable for VecPerParamSpace<T> {
fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> VecPerParamSpace<T> {
- self.map(|t| t.fold_with(folder))
+
+ // Things in the Fn space take place under an additional level
+ // of region binding relative to the other spaces. This is
+ // because those entries are attached to a method, and methods
+ // always introduce a level of region binding.
+
+ let result = self.map_enumerated(|(space, index, elem)| {
+ if space == subst::FnSpace && index == 0 {
+ // enter new level when/if we reach the first thing in fn space
+ folder.enter_region_binder();
+ }
+ elem.fold_with(folder)
+ });
+ if result.len(subst::FnSpace) > 0 {
+ // if there was anything in fn space, exit the region binding level
+ folder.exit_region_binder();
+ }
+ result
}
}
impl TypeFoldable for ty::FnSig {
fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::FnSig {
- folder.fold_sig(self)
+ folder.fold_fn_sig(self)
}
}
types: substs.types.fold_with(this) }
}
-pub fn super_fold_sig<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
- sig: &ty::FnSig)
- -> ty::FnSig {
- ty::FnSig { binder_id: sig.binder_id,
- inputs: sig.inputs.fold_with(this),
+pub fn super_fold_fn_sig<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
+ sig: &ty::FnSig)
+ -> ty::FnSig
+{
+ this.enter_region_binder();
+ let result = super_fold_fn_sig_contents(this, sig);
+ this.exit_region_binder();
+ result
+}
+
+pub fn super_fold_fn_sig_contents<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
+ sig: &ty::FnSig)
+ -> ty::FnSig
+{
+ ty::FnSig { inputs: sig.inputs.fold_with(this),
output: sig.output.fold_with(this),
variadic: sig.variadic }
}
}
///////////////////////////////////////////////////////////////////////////
+// Higher-ranked things
+
+/**
+ * Designates a "binder" for late-bound regions.
+ */
+pub trait HigherRankedFoldable : Repr {
+ /// Folds the contents of `self`, ignoring the region binder created
+ /// by `self`.
+ fn fold_contents<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self;
+}
+
+impl HigherRankedFoldable for ty::FnSig {
+ fn fold_contents<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::FnSig {
+ super_fold_fn_sig_contents(folder, self)
+ }
+}
+impl<T:TypeFoldable+Repr> HigherRankedFoldable for ty::Binder<T> {
+ fn fold_contents<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::Binder<T> {
+ ty::bind(self.value.fold_with(folder))
+ }
+}
// Some sample folders
pub struct BottomUpFolder<'a, 'tcx: 'a> {
/// current position of the fold.)
pub struct RegionFolder<'a, 'tcx: 'a> {
tcx: &'a ty::ctxt<'tcx>,
- fld_t: |ty::t|: 'a -> ty::t,
- fld_r: |ty::Region|: 'a -> ty::Region,
- within_binder_ids: Vec<ast::NodeId>,
+ current_depth: uint,
+ fld_r: |ty::Region, uint|: 'a -> ty::Region,
}
impl<'a, 'tcx> RegionFolder<'a, 'tcx> {
- pub fn general(tcx: &'a ty::ctxt<'tcx>,
- fld_r: |ty::Region|: 'a -> ty::Region,
- fld_t: |ty::t|: 'a -> ty::t)
- -> RegionFolder<'a, 'tcx> {
+ pub fn new(tcx: &'a ty::ctxt<'tcx>, fld_r: |ty::Region, uint|: 'a -> ty::Region)
+ -> RegionFolder<'a, 'tcx> {
RegionFolder {
tcx: tcx,
- fld_t: fld_t,
+ current_depth: 1,
fld_r: fld_r,
- within_binder_ids: vec![],
}
}
-
- pub fn regions(tcx: &'a ty::ctxt<'tcx>, fld_r: |ty::Region|: 'a -> ty::Region)
- -> RegionFolder<'a, 'tcx> {
- fn noop(t: ty::t) -> ty::t { t }
-
- RegionFolder {
- tcx: tcx,
- fld_t: noop,
- fld_r: fld_r,
- within_binder_ids: vec![],
- }
- }
-}
-
-/// If `ty` has `FnSig` (i.e. closure or fn), return its binder_id;
-/// else None.
-fn opt_binder_id_of_function(t: ty::t) -> Option<ast::NodeId> {
- match ty::get(t).sty {
- ty::ty_closure(ref f) => Some(f.sig.binder_id),
- ty::ty_bare_fn(ref f) => Some(f.sig.binder_id),
- _ => None,
- }
}
impl<'a, 'tcx> TypeFolder<'tcx> for RegionFolder<'a, 'tcx> {
fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { self.tcx }
- fn fold_ty(&mut self, ty: ty::t) -> ty::t {
- debug!("RegionFolder.fold_ty({})", ty.repr(self.tcx()));
- let opt_binder_id = opt_binder_id_of_function(ty);
- match opt_binder_id {
- Some(binder_id) => self.within_binder_ids.push(binder_id),
- None => {}
- }
-
- let t1 = super_fold_ty(self, ty);
- let ret = (self.fld_t)(t1);
-
- if opt_binder_id.is_some() {
- self.within_binder_ids.pop();
- }
+ fn enter_region_binder(&mut self) {
+ self.current_depth += 1;
+ }
- ret
+ fn exit_region_binder(&mut self) {
+ self.current_depth -= 1;
}
fn fold_region(&mut self, r: ty::Region) -> ty::Region {
match r {
- ty::ReLateBound(binder_id, _) if self.within_binder_ids.contains(&binder_id) => {
- debug!("RegionFolder.fold_region({}) skipped bound region", r.repr(self.tcx()));
+ ty::ReLateBound(debruijn, _) if debruijn.depth < self.current_depth => {
+ debug!("RegionFolder.fold_region({}) skipped bound region (current depth={})",
+ r.repr(self.tcx()), self.current_depth);
r
}
_ => {
- debug!("RegionFolder.fold_region({}) folding free region", r.repr(self.tcx()));
- (self.fld_r)(r)
+ debug!("RegionFolder.fold_region({}) folding free region (current_depth={})",
+ r.repr(self.tcx()), self.current_depth);
+ (self.fld_r)(r, self.current_depth)
}
}
}
}
}
}
+
+///////////////////////////////////////////////////////////////////////////
+// Region shifter
+//
+// Shifts the De Bruijn indices on all escaping bound regions by a
+// fixed amount. Useful in substitution or when otherwise introducing
+// a binding level that is not intended to capture the existing bound
+// regions. See comment on `shift_regions_through_binders` method in
+// `subst.rs` for more details.
+
+pub fn shift_region(region: ty::Region, amount: uint) -> ty::Region {
+ match region {
+ ty::ReLateBound(debruijn, br) => {
+ ty::ReLateBound(debruijn.shifted(amount), br)
+ }
+ _ => {
+ region
+ }
+ }
+}
+
+pub fn shift_regions<T:TypeFoldable+Repr>(tcx: &ty::ctxt, amount: uint, value: &T) -> T {
+ debug!("shift_regions(value={}, amount={})",
+ value.repr(tcx), amount);
+
+ value.fold_with(&mut RegionFolder::new(tcx, |region, _current_depth| {
+ shift_region(region, amount)
+ }))
+}
+
ty::ReStatic
}
- Some(&rl::DefLateBoundRegion(binder_id, _, id)) => {
- ty::ReLateBound(binder_id, ty::BrNamed(ast_util::local_def(id),
- lifetime.name))
+ Some(&rl::DefLateBoundRegion(debruijn, id)) => {
+ ty::ReLateBound(debruijn, ty::BrNamed(ast_util::local_def(id), lifetime.name))
}
Some(&rl::DefEarlyBoundRegion(space, index, id)) => {
decl_generics: &ty::Generics,
self_ty: Option<ty::t>,
associated_ty: Option<ty::t>,
- path: &ast::Path,
- binder_id: ast::NodeId)
+ path: &ast::Path)
-> Substs
where AC: AstConv<'tcx>, RS: RegionScope
{
this: &AC,
rscope: &RS,
did: ast::DefId,
- path: &ast::Path,
- binder_id: ast::NodeId)
+ path: &ast::Path)
-> TypeAndSubsts
{
let tcx = this.tcx();
ty: decl_ty
} = this.get_item_ty(did);
- let substs = ast_path_substs(this,
- rscope,
- did,
- &generics,
- None,
- None,
- path,
- binder_id);
+ let substs = ast_path_substs_for_ty(this,
+ rscope,
+ did,
+ &generics,
+ None,
+ None,
+ path);
let ty = decl_ty.subst(tcx, &substs);
TypeAndSubsts { substs: substs, ty: ty }
}
this: &AC,
rscope: &RS,
did: ast::DefId,
- path: &ast::Path,
- binder_id: ast::NodeId)
+ path: &ast::Path)
-> TypeAndSubsts
where AC : AstConv<'tcx>, RS : RegionScope
{
Substs::new(VecPerParamSpace::params_from_type(type_params),
VecPerParamSpace::params_from_type(region_params))
} else {
- ast_path_substs(this, rscope, did, &generics, None, None, path, binder_id)
+ ast_path_substs_for_ty(this, rscope, did, &generics, None, None, path)
};
let ty = decl_ty.subst(tcx, &substs);
match a_def {
def::DefTy(did, _) |
def::DefStruct(did) if Some(did) == this.tcx().lang_items.owned_box() => {
- let ty = ast_path_to_ty(this, rscope, did, path, id).ty;
+ let ty = ast_path_to_ty(this, rscope, did, path).ty;
match ty::get(ty).sty {
ty::ty_struct(struct_def_id, ref substs) => {
assert_eq!(struct_def_id, did);
trait_def_id,
None,
None,
- path,
- id);
+ path);
let empty_vec = [];
let bounds = match *opt_bounds { None => empty_vec.as_slice(),
Some(ref bounds) => bounds.as_slice() };
trait_did,
None,
Some(for_type),
- trait_path,
- ast::DUMMY_NODE_ID); // *see below
-
- // * The trait in a qualified path cannot be "higher-ranked" and
- // hence cannot use the parenthetical sugar, so the binder-id is
- // irrelevant.
+ trait_path);
debug!("associated_ty_to_ty(trait_ref={})",
trait_ref.repr(this.tcx()));
tcx.sess.span_err(ast_ty.span,
"variadic function must have C calling convention");
}
- ty::mk_bare_fn(tcx, ty_of_bare_fn(this, ast_ty.id, bf.fn_style,
- bf.abi, &*bf.decl))
+ ty::mk_bare_fn(tcx, ty_of_bare_fn(this, bf.fn_style, bf.abi, &*bf.decl))
}
ast::TyClosure(ref f) => {
// Use corresponding trait store to figure out default bounds
[].as_slice(),
f.bounds.as_slice());
let fn_decl = ty_of_closure(this,
- ast_ty.id,
f.fn_style,
f.onceness,
bounds,
f.bounds.as_slice());
let fn_decl = ty_of_closure(this,
- ast_ty.id,
f.fn_style,
f.onceness,
bounds,
trait_def_id,
None,
None,
- path,
- id);
+ path);
let empty_bounds: &[ast::TyParamBound] = &[];
let ast_bounds = match *bounds {
Some(ref b) => b.as_slice(),
bounds)
}
def::DefTy(did, _) | def::DefStruct(did) => {
- ast_path_to_ty(this, rscope, did, path, id).ty
+ ast_path_to_ty(this, rscope, did, path).ty
}
def::DefTyParam(space, id, n) => {
check_path_args(tcx, path, NO_TPS | NO_REGIONS);
pub fn ty_of_method<'tcx, AC: AstConv<'tcx>>(
this: &AC,
- id: ast::NodeId,
fn_style: ast::FnStyle,
untransformed_self_ty: ty::t,
explicit_self: &ast::ExplicitSelf,
});
let (bare_fn_ty, optional_explicit_self_category) =
ty_of_method_or_bare_fn(this,
- id,
fn_style,
abi,
self_info,
(bare_fn_ty, optional_explicit_self_category.unwrap())
}
-pub fn ty_of_bare_fn<'tcx, AC: AstConv<'tcx>>(this: &AC, id: ast::NodeId,
- fn_style: ast::FnStyle, abi: abi::Abi,
+pub fn ty_of_bare_fn<'tcx, AC: AstConv<'tcx>>(this: &AC, fn_style: ast::FnStyle, abi: abi::Abi,
decl: &ast::FnDecl) -> ty::BareFnTy {
- let (bare_fn_ty, _) =
- ty_of_method_or_bare_fn(this, id, fn_style, abi, None, decl);
+ let (bare_fn_ty, _) = ty_of_method_or_bare_fn(this, fn_style, abi, None, decl);
bare_fn_ty
}
fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>(
this: &AC,
- id: ast::NodeId,
fn_style: ast::FnStyle,
abi: abi::Abi,
opt_self_info: Option<SelfInfo>,
// New region names that appear inside of the arguments of the function
// declaration are bound to that function type.
- let rb = rscope::BindingRscope::new(id);
+ let rb = rscope::BindingRscope::new();
// `implied_output_region` is the region that will be assumed for any
// region parameters in the return type. In accordance with the rules for
determine_explicit_self_category(this, &rb, &self_info);
explicit_self_category_result = Some(explicit_self_category);
match explicit_self_category {
- ty::StaticExplicitSelfCategory => (None, None),
+ ty::StaticExplicitSelfCategory => {
+ (None, None)
+ }
ty::ByValueExplicitSelfCategory => {
(Some(self_info.untransformed_self_ty), None)
}
fn_style: fn_style,
abi: abi,
sig: ty::FnSig {
- binder_id: id,
inputs: self_and_input_tys,
output: output_ty,
variadic: decl.variadic
pub fn ty_of_closure<'tcx, AC: AstConv<'tcx>>(
this: &AC,
- id: ast::NodeId,
fn_style: ast::FnStyle,
onceness: ast::Onceness,
bounds: ty::ExistentialBounds,
expected_sig: Option<ty::FnSig>)
-> ty::ClosureTy
{
- debug!("ty_of_fn_decl");
+ debug!("ty_of_closure(expected_sig={})",
+ expected_sig.repr(this.tcx()));
// new region names that appear inside of the fn decl are bound to
// that function type
- let rb = rscope::BindingRscope::new(id);
+ let rb = rscope::BindingRscope::new();
- let input_tys = decl.inputs.iter().enumerate().map(|(i, a)| {
+ let input_tys: Vec<_> = decl.inputs.iter().enumerate().map(|(i, a)| {
let expected_arg_ty = expected_sig.as_ref().and_then(|e| {
// no guarantee that the correct number of expected args
// were supplied
ast::NoReturn(_) => ty::FnDiverging
};
+ debug!("ty_of_closure: input_tys={}", input_tys.repr(this.tcx()));
+ debug!("ty_of_closure: output_ty={}", output_ty.repr(this.tcx()));
+
ty::ClosureTy {
fn_style: fn_style,
onceness: onceness,
store: store,
bounds: bounds,
abi: abi,
- sig: ty::FnSig {binder_id: id,
- inputs: input_tys,
+ sig: ty::FnSig {inputs: input_tys,
output: output_ty,
variadic: decl.variadic}
}
// First, we have to replace any bound regions in the fn type with free ones.
// The free region references will be bound the node_id of the body block.
- let (_, fn_sig) = replace_late_bound_regions(tcx, fn_sig.binder_id, fn_sig, |br| {
- ty::ReFree(ty::FreeRegion {scope_id: body.id, bound_region: br})
- });
+ let fn_sig = liberate_late_bound_regions(tcx, body.id, fn_sig);
let arg_tys = fn_sig.inputs.as_slice();
let ret_ty = fn_sig.output;
// In that case, we check each argument against "error" in order to
// set up all the node type bindings.
let error_fn_sig = FnSig {
- binder_id: ast::CRATE_NODE_ID,
inputs: err_args(args.len()),
output: ty::FnConverging(ty::mk_err()),
variadic: false
// Replace any bound regions that appear in the function
// signature with region variables
let fn_sig =
- fcx.infcx().replace_late_bound_regions_with_fresh_var(
- fn_sig.binder_id,
- call_expr.span,
- infer::FnCall,
- fn_sig).0;
+ fcx.infcx().replace_late_bound_regions_with_fresh_var(call_expr.span,
+ infer::FnCall,
+ fn_sig).0;
// Call the generic checker.
check_argument_types(fcx,
body: &ast::Block) {
let mut fn_ty = astconv::ty_of_closure(
fcx,
- expr.id,
ast::NormalFn,
ast::Many,
expected: Expectation) {
let tcx = fcx.ccx.tcx;
+ debug!("check_expr_fn(expr={}, expected={})",
+ expr.repr(tcx),
+ expected.repr(tcx));
+
// Find the expected input/output types (if any). Substitute
// fresh bound regions for any bound regions we find in the
// expected types so as to avoid capture.
expected_bounds) = {
match expected_sty {
Some(ty::ty_closure(ref cenv)) => {
- let (_, sig) =
+ let (sig, _) =
replace_late_bound_regions(
- tcx, cenv.sig.binder_id, &cenv.sig,
- |_| fcx.inh.infcx.fresh_bound_region(expr.id));
+ tcx,
+ &cenv.sig,
+ |_, debruijn| fcx.inh.infcx.fresh_bound_region(debruijn));
let onceness = match (&store, &cenv.store) {
// As the closure type and onceness go, only three
// combinations are legit:
// construct the function type
let fn_ty = astconv::ty_of_closure(fcx,
- expr.id,
ast::NormalFn,
expected_onceness,
expected_bounds,
fn_style: ast::UnsafeFn,
abi: abi::RustIntrinsic,
sig: FnSig {
- binder_id: it.id,
inputs: inputs,
output: output,
variadic: false,
use middle::subst::{ParamSpace, Subst, Substs};
use middle::ty;
-use middle::ty_fold;
-use middle::ty_fold::{TypeFolder, TypeFoldable};
+use middle::ty_fold::{TypeFolder};
use syntax::ast;
-use std::collections::hash_map::{Occupied, Vacant};
-use util::nodemap::FnvHashMap;
use util::ppaux::Repr;
// Helper functions related to manipulating region types.
-pub fn replace_late_bound_regions<T>(
- tcx: &ty::ctxt,
- binder_id: ast::NodeId,
- value: &T,
- map_fn: |ty::BoundRegion| -> ty::Region)
- -> (FnvHashMap<ty::BoundRegion,ty::Region>, T)
- where T : TypeFoldable + Repr
-{
- debug!("replace_late_bound_regions(binder_id={}, value={})",
- binder_id, value.repr(tcx));
-
- let mut map = FnvHashMap::new();
- let new_value = {
- let mut folder = ty_fold::RegionFolder::regions(tcx, |r| {
- match r {
- ty::ReLateBound(s, br) if s == binder_id => {
- match map.entry(br) {
- Vacant(entry) => *entry.set(map_fn(br)),
- Occupied(entry) => *entry.into_mut(),
- }
- }
- _ => r
- }
- });
- value.fold_with(&mut folder)
- };
- debug!("resulting map: {}", map);
- (map, new_value)
-}
-
pub enum WfConstraint {
RegionSubRegionConstraint(Option<ty::t>, ty::Region, ty::Region),
RegionSubParamConstraint(Option<ty::t>, ty::Region, ty::ParamTy),
ty::ty_closure(box ty::ClosureTy{sig: ref fn_sig, ..}) => {
self.binding_count += 1;
- let (_, fn_sig) =
- replace_late_bound_regions(
- self.fcx.tcx(), fn_sig.binder_id, fn_sig,
- |br| ty::ReFree(ty::FreeRegion{scope_id: self.scope_id,
- bound_region: br}));
+ let fn_sig = liberate_late_bound_regions(self.fcx.tcx(), self.scope_id, fn_sig);
debug!("late-bound regions replaced: {}",
fn_sig.repr(self.tcx()));
- self.fold_sig(&fn_sig);
+ self.fold_fn_sig(&fn_sig);
self.binding_count -= 1;
}
for variant in variants.iter() {
// Nullary enum constructors get turned into constants; n-ary enum
// constructors get turned into functions.
- let scope = variant.node.id;
let result_ty = match variant.node.kind {
ast::TupleVariantKind(ref args) if args.len() > 0 => {
let rs = ExplicitRscope;
let input_tys: Vec<_> = args.iter().map(|va| ccx.to_ty(&rs, &*va.ty)).collect();
- ty::mk_ctor_fn(tcx, scope, input_tys.as_slice(), enum_ty)
+ ty::mk_ctor_fn(tcx, input_tys.as_slice(), enum_ty)
}
ast::TupleVariantKind(_) => {
let trait_self_ty = ty::mk_self_type(tmcx.tcx(),
local_def(trait_id));
astconv::ty_of_method(&tmcx,
- *m_id,
*m_fn_style,
trait_self_ty,
m_explicit_self,
method_generics: &m_ty_generics,
};
astconv::ty_of_method(&imcx,
- m.id,
m.pe_fn_style(),
untransformed_rcvr_ty,
m.pe_explicit_self(),
method_generics: &m_ty_generics,
};
astconv::ty_of_method(&tmcx,
- m.id,
m.pe_fn_style(),
untransformed_rcvr_ty,
m.pe_explicit_self(),
|field| (*tcx.tcache.borrow())[
local_def(field.node.id)].ty).collect();
let ctor_fn_ty = ty::mk_ctor_fn(tcx,
- ctor_id,
inputs.as_slice(),
selfty);
write_ty_to_tcx(tcx, ctor_id, ctor_fn_ty);
ccx: ccx,
generics: &ty_generics,
};
- astconv::ty_of_bare_fn(&fcx,
- it.id,
- fn_style,
- abi,
- &**decl)
+ astconv::ty_of_bare_fn(&fcx, fn_style, abi, &**decl)
};
let pty = Polytype {
generics: ty_generics,
ast_generics,
ty::Generics::empty(),
DontCreateTypeParametersForAssociatedTypes);
- let rb = BindingRscope::new(def_id.node);
+ let rb = BindingRscope::new();
let input_tys = decl.inputs
.iter()
.map(|a| ty_of_arg(ccx, &rb, a, None))
ty::BareFnTy {
abi: abi,
fn_style: ast::UnsafeFn,
- sig: ty::FnSig {binder_id: def_id.node,
- inputs: input_tys,
+ sig: ty::FnSig {inputs: input_tys,
output: output,
variadic: decl.variadic}
});
}
}
-pub fn super_fn_sigs<'tcx, C: Combine<'tcx>>(this: &C,
- a: &ty::FnSig,
- b: &ty::FnSig)
- -> cres<ty::FnSig> {
-
- fn argvecs<'tcx, C: Combine<'tcx>>(this: &C,
- a_args: &[ty::t],
- b_args: &[ty::t])
- -> cres<Vec<ty::t>> {
- if a_args.len() == b_args.len() {
- a_args.iter().zip(b_args.iter())
- .map(|(a, b)| this.args(*a, *b)).collect()
- } else {
- Err(ty::terr_arg_count)
- }
- }
-
- if a.variadic != b.variadic {
- return Err(ty::terr_variadic_mismatch(expected_found(this, a.variadic, b.variadic)));
- }
-
- let inputs = try!(argvecs(this,
- a.inputs.as_slice(),
- b.inputs.as_slice()));
-
- let output = try!(match (a.output, b.output) {
- (ty::FnConverging(a_ty), ty::FnConverging(b_ty)) =>
- Ok(ty::FnConverging(try!(this.tys(a_ty, b_ty)))),
- (ty::FnDiverging, ty::FnDiverging) =>
- Ok(ty::FnDiverging),
- (a, b) =>
- Err(ty::terr_convergence_mismatch(
- expected_found(this, a != ty::FnDiverging, b != ty::FnDiverging)
- )),
- });
-
- Ok(FnSig {binder_id: a.binder_id,
- inputs: inputs,
- output: output,
- variadic: a.variadic})
-}
-
pub fn super_tys<'tcx, C: Combine<'tcx>>(this: &C, a: ty::t, b: ty::t) -> cres<ty::t> {
let tcx = this.infcx().tcx;
fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
self.higher_ranked_glb(a, b)
}
+
+ fn trait_refs(&self, a: &ty::TraitRef, b: &ty::TraitRef) -> cres<ty::TraitRef> {
+ self.higher_ranked_glb(a, b)
+ }
}
subst::Substs::new_trait(type_parameters, regions, assoc_type_parameters, self_ty)
}
- pub fn fresh_bound_region(&self, binder_id: ast::NodeId) -> ty::Region {
- self.region_vars.new_bound(binder_id)
+ pub fn fresh_bound_region(&self, debruijn: ty::DebruijnIndex) -> ty::Region {
+ self.region_vars.new_bound(debruijn)
}
pub fn resolve_regions_and_report_errors(&self) {
pub fn replace_late_bound_regions_with_fresh_var<T>(
&self,
- binder_id: ast::NodeId,
span: Span,
lbrct: LateBoundRegionConversionTime,
value: &T)
-> (T, FnvHashMap<ty::BoundRegion,ty::Region>)
- where T : TypeFoldable + Repr
+ where T : HigherRankedFoldable
{
- let (map, value) =
- replace_late_bound_regions(
- self.tcx,
- binder_id,
- value,
- |br| self.next_region_var(LateBoundRegion(span, br, lbrct)));
- (value, map)
+ ty::replace_late_bound_regions(
+ self.tcx,
+ value,
+ |br, _| self.next_region_var(LateBoundRegion(span, br, lbrct)))
}
}
-pub fn fold_regions_in_sig(tcx: &ty::ctxt,
- fn_sig: &ty::FnSig,
- fldr: |r: ty::Region| -> ty::Region)
- -> ty::FnSig {
- ty_fold::RegionFolder::regions(tcx, fldr).fold_sig(fn_sig)
-}
-
impl TypeTrace {
pub fn span(&self) -> Span {
self.origin.span()
ReInfer(ReSkolemized(sc, br))
}
- pub fn new_bound(&self, binder_id: ast::NodeId) -> Region {
+ pub fn new_bound(&self, debruijn: ty::DebruijnIndex) -> Region {
// Creates a fresh bound variable for use in GLB computations.
// See discussion of GLB computation in the large comment at
// the top of this file for more details.
self.tcx.sess.bug("rollover in RegionInference new_bound()");
}
- ReLateBound(binder_id, BrFresh(sc))
+ ReLateBound(debruijn, BrFresh(sc))
}
fn values_are_none(&self) -> bool {
fn_style: ast::NormalFn,
abi: abi::Rust,
sig: ty::FnSig {
- binder_id: main_id,
inputs: Vec::new(),
output: ty::FnConverging(ty::mk_nil(tcx)),
variadic: false
fn_style: ast::NormalFn,
abi: abi::Rust,
sig: ty::FnSig {
- binder_id: start_id,
inputs: vec!(
ty::mk_int(),
ty::mk_imm_ptr(tcx, ty::mk_imm_ptr(tcx, ty::mk_u8()))
/// A scope in which we generate anonymous, late-bound regions for
/// omitted regions. This occurs in function signatures.
pub struct BindingRscope {
- binder_id: ast::NodeId,
anon_bindings: Cell<uint>,
}
impl BindingRscope {
- pub fn new(binder_id: ast::NodeId) -> BindingRscope {
+ pub fn new() -> BindingRscope {
BindingRscope {
- binder_id: binder_id,
anon_bindings: Cell::new(0),
}
}
fn next_region(&self) -> ty::Region {
let idx = self.anon_bindings.get();
self.anon_bindings.set(idx + 1);
- ty::ReLateBound(self.binder_id, ty::BrAnon(idx))
+ ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrAnon(idx))
}
}
}
pub fn fn_sig_to_string(cx: &ctxt, typ: &ty::FnSig) -> String {
- format!("fn{}{} -> {}", typ.binder_id, typ.inputs.repr(cx),
- typ.output.repr(cx))
+ format!("fn{} -> {}", typ.inputs.repr(cx), typ.output.repr(cx))
}
pub fn trait_ref_to_string(cx: &ctxt, trait_ref: &ty::TraitRef) -> String {
pub fn ty_to_string(cx: &ctxt, typ: t) -> String {
fn bare_fn_to_string(cx: &ctxt,
- fn_style: ast::FnStyle,
- abi: abi::Abi,
- ident: Option<ast::Ident>,
- sig: &ty::FnSig)
- -> String {
+ fn_style: ast::FnStyle,
+ abi: abi::Abi,
+ ident: Option<ast::Ident>,
+ sig: &ty::FnSig)
+ -> String {
let mut s = String::new();
match fn_style {
ast::NormalFn => {}
}
}
+impl<T:Repr> Repr for ty::Binder<T> {
+ fn repr(&self, tcx: &ctxt) -> String {
+ format!("Binder({})", self.value.repr(tcx))
+ }
+}
*/
-// This is only used by tests, hence allow dead code.
-#![allow(dead_code)]
-
use driver::diagnostic;
use driver::diagnostic::Emitter;
use driver::driver;
use middle::resolve;
use middle::resolve_lifetime;
use middle::stability;
+use middle::subst;
+use middle::subst::Subst;
use middle::ty;
use middle::typeck::infer::combine::Combine;
use middle::typeck::infer;
use middle::typeck::infer::lub::Lub;
use middle::typeck::infer::glb::Glb;
-use session::{mod, config};
+use session::{mod,config};
+use syntax::{abi, ast, ast_map, ast_util};
use syntax::codemap;
use syntax::codemap::{Span, CodeMap, DUMMY_SP};
use syntax::diagnostic::{Level, RenderSpan, Bug, Fatal, Error, Warning, Note, Help};
-use syntax::{ast, ast_map};
-use util::ppaux::{ty_to_string, UserString};
+use syntax::parse::token;
+use util::ppaux::{ty_to_string, Repr, UserString};
use arena::TypedArena;
(box ExpectErrorEmitter { messages: v } as Box<Emitter+Send>, msgs.len())
}
-fn test_env(_test_name: &str,
- source_string: &str,
+fn test_env(source_string: &str,
(emitter, expected_err_count): (Box<Emitter+Send>, uint),
body: |Env|) {
- let options =
+ let mut options =
config::basic_options();
+ options.debugging_opts |= config::VERBOSE;
let codemap =
CodeMap::new();
let diagnostic_handler =
let lang_items = lang_items::collect_language_items(krate, &sess);
let resolve::CrateMap { def_map, freevars, capture_mode_map, .. } =
resolve::resolve_crate(&sess, &lang_items, krate);
- let named_region_map = resolve_lifetime::krate(&sess, krate);
+ let named_region_map = resolve_lifetime::krate(&sess, krate, &def_map);
let region_map = region::resolve_crate(&sess, krate);
let stability_index = stability::Index::build(krate);
let type_arena = TypedArena::new();
sub: &[]}]});
}
+ #[allow(dead_code)] // this seems like it could be useful, even if we don't use it now
pub fn lookup_item(&self, names: &[String]) -> ast::NodeId {
return match search_mod(self, &self.infcx.tcx.map.krate().module, 0, names) {
Some(id) => id,
}
}
- pub fn assert_not_subtype(&self, a: ty::t, b: ty::t) {
- if self.is_subtype(a, b) {
- panic!("{} is a subtype of {}, but it shouldn't be",
- self.ty_to_string(a),
- self.ty_to_string(b));
- }
- }
-
pub fn assert_eq(&self, a: ty::t, b: ty::t) {
self.assert_subtype(a, b);
self.assert_subtype(b, a);
}
pub fn t_fn(&self,
- binder_id: ast::NodeId,
input_tys: &[ty::t],
output_ty: ty::t)
-> ty::t
{
- ty::mk_ctor_fn(self.infcx.tcx, binder_id, input_tys, output_ty)
+ ty::mk_ctor_fn(self.infcx.tcx, input_tys, output_ty)
+ }
+
+ pub fn t_nil(&self) -> ty::t {
+ ty::mk_nil(self.infcx.tcx)
+ }
+
+ pub fn t_pair(&self, ty1: ty::t, ty2: ty::t) -> ty::t
+ {
+ ty::mk_tup(self.infcx.tcx, vec![ty1, ty2])
+ }
+
+ pub fn t_closure(&self,
+ input_tys: &[ty::t],
+ output_ty: ty::t,
+ region_bound: ty::Region)
+ -> ty::t
+ {
+ ty::mk_closure(self.infcx.tcx, ty::ClosureTy {
+ fn_style: ast::NormalFn,
+ onceness: ast::Many,
+ store: ty::RegionTraitStore(region_bound, ast::MutMutable),
+ bounds: ty::region_existential_bound(region_bound),
+ sig: ty::FnSig {
+ inputs: input_tys.to_vec(),
+ output: ty::FnConverging(output_ty),
+ variadic: false,
+ },
+ abi: abi::Rust,
+ })
+ }
+
+ pub fn t_param(&self, space: subst::ParamSpace, index: uint) -> ty::t {
+ ty::mk_param(self.infcx.tcx, space, index, ast_util::local_def(ast::DUMMY_NODE_ID))
+ }
+
+ pub fn re_early_bound(&self,
+ space: subst::ParamSpace,
+ index: uint,
+ name: &'static str)
+ -> ty::Region
+ {
+ let name = token::intern(name);
+ ty::ReEarlyBound(ast::DUMMY_NODE_ID, space, index, name)
+ }
+
+ pub fn re_late_bound_with_debruijn(&self, id: uint, debruijn: ty::DebruijnIndex) -> ty::Region {
+ ty::ReLateBound(debruijn, ty::BrAnon(id))
+ }
+
+ pub fn t_rptr(&self, r: ty::Region) -> ty::t {
+ ty::mk_imm_rptr(self.infcx.tcx, r, ty::mk_int())
}
- pub fn t_int(&self) -> ty::t {
- ty::mk_int()
+ pub fn t_rptr_late_bound(&self, id: uint) -> ty::t {
+ ty::mk_imm_rptr(self.infcx.tcx,
+ self.re_late_bound_with_debruijn(id, ty::DebruijnIndex::new(1)),
+ ty::mk_int())
}
- pub fn t_rptr_late_bound(&self, binder_id: ast::NodeId, id: uint) -> ty::t {
- ty::mk_imm_rptr(self.infcx.tcx, ty::ReLateBound(binder_id, ty::BrAnon(id)),
- self.t_int())
+ pub fn t_rptr_late_bound_with_debruijn(&self, id: uint, debruijn: ty::DebruijnIndex) -> ty::t {
+ ty::mk_imm_rptr(self.infcx.tcx,
+ self.re_late_bound_with_debruijn(id, debruijn),
+ ty::mk_int())
}
pub fn t_rptr_scope(&self, id: ast::NodeId) -> ty::t {
- ty::mk_imm_rptr(self.infcx.tcx, ty::ReScope(id), self.t_int())
+ ty::mk_imm_rptr(self.infcx.tcx, ty::ReScope(id), ty::mk_int())
+ }
+
+ pub fn re_free(&self, nid: ast::NodeId, id: uint) -> ty::Region {
+ ty::ReFree(ty::FreeRegion {scope_id: nid,
+ bound_region: ty::BrAnon(id)})
}
pub fn t_rptr_free(&self, nid: ast::NodeId, id: uint) -> ty::t {
- ty::mk_imm_rptr(self.infcx.tcx,
- ty::ReFree(ty::FreeRegion {scope_id: nid,
- bound_region: ty::BrAnon(id)}),
- self.t_int())
+ ty::mk_imm_rptr(self.infcx.tcx, self.re_free(nid, id), ty::mk_int())
}
pub fn t_rptr_static(&self) -> ty::t {
- ty::mk_imm_rptr(self.infcx.tcx, ty::ReStatic, self.t_int())
+ ty::mk_imm_rptr(self.infcx.tcx, ty::ReStatic, ty::mk_int())
}
pub fn dummy_type_trace(&self) -> infer::TypeTrace {
Glb(self.infcx.combine_fields(true, trace))
}
- pub fn resolve_regions(&self) {
- self.infcx.resolve_regions_and_report_errors();
- }
-
pub fn make_lub_ty(&self, t1: ty::t, t2: ty::t) -> ty::t {
match self.lub().tys(t1, t2) {
Ok(t) => t,
}
}
}
-
- /// Checks that `LUB(t1,t2)` is undefined
- pub fn check_no_lub(&self, t1: ty::t, t2: ty::t) {
- match self.lub().tys(t1, t2) {
- Err(_) => {}
- Ok(t) => {
- panic!("unexpected success computing LUB: {}", self.ty_to_string(t))
- }
- }
- }
-
- /// Checks that `GLB(t1,t2)` is undefined
- pub fn check_no_glb(&self, t1: ty::t, t2: ty::t) {
- match self.glb().tys(t1, t2) {
- Err(_) => {}
- Ok(t) => {
- panic!("unexpected success computing GLB: {}", self.ty_to_string(t))
- }
- }
- }
}
#[test]
fn contravariant_region_ptr_ok() {
- test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
+ test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
env.create_simple_region_hierarchy();
let t_rptr1 = env.t_rptr_scope(1);
let t_rptr10 = env.t_rptr_scope(10);
#[test]
fn contravariant_region_ptr_err() {
- test_env("contravariant_region_ptr",
- EMPTY_SOURCE_STR,
+ test_env(EMPTY_SOURCE_STR,
errors(&["lifetime mismatch"]),
|env| {
env.create_simple_region_hierarchy();
#[test]
fn lub_bound_bound() {
- test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
- let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
- let t_rptr_bound2 = env.t_rptr_late_bound(22, 2);
- env.check_lub(env.t_fn(22, &[t_rptr_bound1], env.t_int()),
- env.t_fn(22, &[t_rptr_bound2], env.t_int()),
- env.t_fn(22, &[t_rptr_bound1], env.t_int()));
+ test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+ let t_rptr_bound1 = env.t_rptr_late_bound(1);
+ let t_rptr_bound2 = env.t_rptr_late_bound(2);
+ env.check_lub(env.t_fn(&[t_rptr_bound1], ty::mk_int()),
+ env.t_fn(&[t_rptr_bound2], ty::mk_int()),
+ env.t_fn(&[t_rptr_bound1], ty::mk_int()));
})
}
#[test]
fn lub_bound_free() {
- test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
- let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
+ test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+ let t_rptr_bound1 = env.t_rptr_late_bound(1);
let t_rptr_free1 = env.t_rptr_free(0, 1);
- env.check_lub(env.t_fn(22, &[t_rptr_bound1], env.t_int()),
- env.t_fn(22, &[t_rptr_free1], env.t_int()),
- env.t_fn(22, &[t_rptr_free1], env.t_int()));
+ env.check_lub(env.t_fn(&[t_rptr_bound1], ty::mk_int()),
+ env.t_fn(&[t_rptr_free1], ty::mk_int()),
+ env.t_fn(&[t_rptr_free1], ty::mk_int()));
})
}
#[test]
fn lub_bound_static() {
- test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
- let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
+ test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+ let t_rptr_bound1 = env.t_rptr_late_bound(1);
let t_rptr_static = env.t_rptr_static();
- env.check_lub(env.t_fn(22, &[t_rptr_bound1], env.t_int()),
- env.t_fn(22, &[t_rptr_static], env.t_int()),
- env.t_fn(22, &[t_rptr_static], env.t_int()));
+ env.check_lub(env.t_fn(&[t_rptr_bound1], ty::mk_int()),
+ env.t_fn(&[t_rptr_static], ty::mk_int()),
+ env.t_fn(&[t_rptr_static], ty::mk_int()));
})
}
#[test]
fn lub_bound_bound_inverse_order() {
- test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
- let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
- let t_rptr_bound2 = env.t_rptr_late_bound(22, 2);
- env.check_lub(env.t_fn(22, &[t_rptr_bound1, t_rptr_bound2], t_rptr_bound1),
- env.t_fn(22, &[t_rptr_bound2, t_rptr_bound1], t_rptr_bound1),
- env.t_fn(22, &[t_rptr_bound1, t_rptr_bound1], t_rptr_bound1));
+ test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+ let t_rptr_bound1 = env.t_rptr_late_bound(1);
+ let t_rptr_bound2 = env.t_rptr_late_bound(2);
+ env.check_lub(env.t_fn(&[t_rptr_bound1, t_rptr_bound2], t_rptr_bound1),
+ env.t_fn(&[t_rptr_bound2, t_rptr_bound1], t_rptr_bound1),
+ env.t_fn(&[t_rptr_bound1, t_rptr_bound1], t_rptr_bound1));
})
}
#[test]
fn lub_free_free() {
- test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
+ test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
let t_rptr_free1 = env.t_rptr_free(0, 1);
let t_rptr_free2 = env.t_rptr_free(0, 2);
let t_rptr_static = env.t_rptr_static();
- env.check_lub(env.t_fn(22, &[t_rptr_free1], env.t_int()),
- env.t_fn(22, &[t_rptr_free2], env.t_int()),
- env.t_fn(22, &[t_rptr_static], env.t_int()));
+ env.check_lub(env.t_fn(&[t_rptr_free1], ty::mk_int()),
+ env.t_fn(&[t_rptr_free2], ty::mk_int()),
+ env.t_fn(&[t_rptr_static], ty::mk_int()));
})
}
#[test]
fn lub_returning_scope() {
- test_env("contravariant_region_ptr", EMPTY_SOURCE_STR,
+ test_env(EMPTY_SOURCE_STR,
errors(&["cannot infer an appropriate lifetime"]), |env| {
let t_rptr_scope10 = env.t_rptr_scope(10);
let t_rptr_scope11 = env.t_rptr_scope(11);
// this should generate an error when regions are resolved
- env.make_lub_ty(env.t_fn(22, &[], t_rptr_scope10),
- env.t_fn(22, &[], t_rptr_scope11));
+ env.make_lub_ty(env.t_fn(&[], t_rptr_scope10),
+ env.t_fn(&[], t_rptr_scope11));
})
}
#[test]
fn glb_free_free_with_common_scope() {
- test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
+ test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
let t_rptr_free1 = env.t_rptr_free(0, 1);
let t_rptr_free2 = env.t_rptr_free(0, 2);
let t_rptr_scope = env.t_rptr_scope(0);
- env.check_glb(env.t_fn(22, &[t_rptr_free1], env.t_int()),
- env.t_fn(22, &[t_rptr_free2], env.t_int()),
- env.t_fn(22, &[t_rptr_scope], env.t_int()));
+ env.check_glb(env.t_fn(&[t_rptr_free1], ty::mk_int()),
+ env.t_fn(&[t_rptr_free2], ty::mk_int()),
+ env.t_fn(&[t_rptr_scope], ty::mk_int()));
})
}
#[test]
fn glb_bound_bound() {
- test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
- let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
- let t_rptr_bound2 = env.t_rptr_late_bound(22, 2);
- env.check_glb(env.t_fn(22, &[t_rptr_bound1], env.t_int()),
- env.t_fn(22, &[t_rptr_bound2], env.t_int()),
- env.t_fn(22, &[t_rptr_bound1], env.t_int()));
+ test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+ let t_rptr_bound1 = env.t_rptr_late_bound(1);
+ let t_rptr_bound2 = env.t_rptr_late_bound(2);
+ env.check_glb(env.t_fn(&[t_rptr_bound1], ty::mk_int()),
+ env.t_fn(&[t_rptr_bound2], ty::mk_int()),
+ env.t_fn(&[t_rptr_bound1], ty::mk_int()));
})
}
#[test]
fn glb_bound_free() {
- test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
- let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
+ test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+ let t_rptr_bound1 = env.t_rptr_late_bound(1);
let t_rptr_free1 = env.t_rptr_free(0, 1);
- env.check_glb(env.t_fn(22, &[t_rptr_bound1], env.t_int()),
- env.t_fn(22, &[t_rptr_free1], env.t_int()),
- env.t_fn(22, &[t_rptr_bound1], env.t_int()));
+ env.check_glb(env.t_fn(&[t_rptr_bound1], ty::mk_int()),
+ env.t_fn(&[t_rptr_free1], ty::mk_int()),
+ env.t_fn(&[t_rptr_bound1], ty::mk_int()));
})
}
#[test]
fn glb_bound_static() {
- test_env("contravariant_region_ptr", EMPTY_SOURCE_STR, errors(&[]), |env| {
- let t_rptr_bound1 = env.t_rptr_late_bound(22, 1);
+ test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
+ let t_rptr_bound1 = env.t_rptr_late_bound(1);
let t_rptr_static = env.t_rptr_static();
- env.check_glb(env.t_fn(22, &[t_rptr_bound1], env.t_int()),
- env.t_fn(22, &[t_rptr_static], env.t_int()),
- env.t_fn(22, &[t_rptr_bound1], env.t_int()));
+ env.check_glb(env.t_fn(&[t_rptr_bound1], ty::mk_int()),
+ env.t_fn(&[t_rptr_static], ty::mk_int()),
+ env.t_fn(&[t_rptr_bound1], ty::mk_int()));
})
}
+
+#[test]
+fn subst_ty_renumber_bound() {
+ /*!
+ * Test substituting a bound region into a function, which introduces another
+ * level of binding. This requires adjusting the Debruijn index.
+ */
+
+ test_env(EMPTY_SOURCE_STR, errors([]), |env| {
+ // Situation:
+ // Theta = [A -> &'a foo]
+
+ let t_rptr_bound1 = env.t_rptr_late_bound(1);
+
+ // t_source = fn(A)
+ let t_source = {
+ let t_param = env.t_param(subst::TypeSpace, 0);
+ env.t_fn([t_param], env.t_nil())
+ };
+
+ let substs = subst::Substs::new_type(vec![t_rptr_bound1], vec![]);
+ let t_substituted = t_source.subst(env.infcx.tcx, &substs);
+
+ // t_expected = fn(&'a int)
+ let t_expected = {
+ let t_ptr_bound2 = env.t_rptr_late_bound_with_debruijn(1, ty::DebruijnIndex::new(2));
+ env.t_fn([t_ptr_bound2], env.t_nil())
+ };
+
+ debug!("subst_bound: t_source={} substs={} t_substituted={} t_expected={}",
+ t_source.repr(env.infcx.tcx),
+ substs.repr(env.infcx.tcx),
+ t_substituted.repr(env.infcx.tcx),
+ t_expected.repr(env.infcx.tcx));
+
+ assert_eq!(t_substituted, t_expected);
+ })
+}
+
+#[test]
+fn subst_ty_renumber_some_bounds() {
+ /*!
+ * Test substituting a bound region into a function, which introduces another
+ * level of binding. This requires adjusting the Debruijn index.
+ */
+
+ test_env(EMPTY_SOURCE_STR, errors([]), |env| {
+ // Situation:
+ // Theta = [A -> &'a foo]
+
+ let t_rptr_bound1 = env.t_rptr_late_bound(1);
+
+ // t_source = (A, fn(A))
+ let t_source = {
+ let t_param = env.t_param(subst::TypeSpace, 0);
+ env.t_pair(t_param, env.t_fn([t_param], env.t_nil()))
+ };
+
+ let substs = subst::Substs::new_type(vec![t_rptr_bound1], vec![]);
+ let t_substituted = t_source.subst(env.infcx.tcx, &substs);
+
+ // t_expected = (&'a int, fn(&'a int))
+ //
+ // but not that the Debruijn index is different in the different cases.
+ let t_expected = {
+ let t_rptr_bound2 = env.t_rptr_late_bound_with_debruijn(1, ty::DebruijnIndex::new(2));
+ env.t_pair(t_rptr_bound1, env.t_fn([t_rptr_bound2], env.t_nil()))
+ };
+
+ debug!("subst_bound: t_source={} substs={} t_substituted={} t_expected={}",
+ t_source.repr(env.infcx.tcx),
+ substs.repr(env.infcx.tcx),
+ t_substituted.repr(env.infcx.tcx),
+ t_expected.repr(env.infcx.tcx));
+
+ assert_eq!(t_substituted, t_expected);
+ })
+}
+
+#[test]
+fn escaping() {
+ /*!
+ * Test that we correctly compute whether a type has escaping
+ * regions or not.
+ */
+
+ test_env(EMPTY_SOURCE_STR, errors([]), |env| {
+ // Situation:
+ // Theta = [A -> &'a foo]
+
+ assert!(!ty::type_has_escaping_regions(env.t_nil()));
+
+ let t_rptr_free1 = env.t_rptr_free(0, 1);
+ assert!(!ty::type_has_escaping_regions(t_rptr_free1));
+
+ let t_rptr_bound1 = env.t_rptr_late_bound_with_debruijn(1, ty::DebruijnIndex::new(1));
+ assert!(ty::type_has_escaping_regions(t_rptr_bound1));
+
+ let t_rptr_bound2 = env.t_rptr_late_bound_with_debruijn(1, ty::DebruijnIndex::new(2));
+ assert!(ty::type_has_escaping_regions(t_rptr_bound2));
+
+ // t_fn = fn(A)
+ let t_param = env.t_param(subst::TypeSpace, 0);
+ assert!(!ty::type_has_escaping_regions(t_param));
+ let t_fn = env.t_fn([t_param], env.t_nil());
+ assert!(!ty::type_has_escaping_regions(t_fn));
+
+ // t_fn = |&int|+'a
+ let t_fn = env.t_closure([t_rptr_bound1], env.t_nil(), env.re_free(0, 1));
+ assert!(!ty::type_has_escaping_regions(t_fn));
+
+ // t_fn = |&int|+'a (where &int has depth 2)
+ let t_fn = env.t_closure([t_rptr_bound2], env.t_nil(), env.re_free(0, 1));
+ assert!(ty::type_has_escaping_regions(t_fn));
+
+ // t_fn = |&int|+&int
+ let t_fn = env.t_closure([t_rptr_bound1], env.t_nil(),
+ env.re_late_bound_with_debruijn(1, ty::DebruijnIndex::new(1)));
+ assert!(ty::type_has_escaping_regions(t_fn));
+ })
+}
+
+#[test]
+fn subst_region_renumber_region() {
+ /*!
+ * Test applying a substitution where the value being substituted
+ * for an early-bound region is a late-bound region.
+ */
+
+ test_env(EMPTY_SOURCE_STR, errors([]), |env| {
+ let re_bound1 = env.re_late_bound_with_debruijn(1, ty::DebruijnIndex::new(1));
+
+ // type t_source<'a> = fn(&'a int)
+ let t_source = {
+ let re_early = env.re_early_bound(subst::TypeSpace, 0, "'a");
+ env.t_fn([env.t_rptr(re_early)], env.t_nil())
+ };
+
+ let substs = subst::Substs::new_type(vec![], vec![re_bound1]);
+ let t_substituted = t_source.subst(env.infcx.tcx, &substs);
+
+ // t_expected = fn(&'a int)
+ //
+ // but not that the Debruijn index is different in the different cases.
+ let t_expected = {
+ let t_rptr_bound2 = env.t_rptr_late_bound_with_debruijn(1, ty::DebruijnIndex::new(2));
+ env.t_fn([t_rptr_bound2], env.t_nil())
+ };
+
+ debug!("subst_bound: t_source={} substs={} t_substituted={} t_expected={}",
+ t_source.repr(env.infcx.tcx),
+ substs.repr(env.infcx.tcx),
+ t_substituted.repr(env.infcx.tcx),
+ t_expected.repr(env.infcx.tcx));
+
+ assert_eq!(t_substituted, t_expected);
+ })
+}
+
let self_type = fty.sig.inputs[0];
let boxed_self_type = ty::mk_uniq(tcx, self_type);
let boxed_function_type = ty::FnSig {
- binder_id: fty.sig.binder_id,
inputs: fty.sig.inputs.iter().enumerate().map(|(i, typ)| {
if i == 0 {
boxed_self_type
// RustCall so the untupled arguments can be passed
// through verbatim. This is kind of ugly.
let fake_ty = ty::FnSig {
- binder_id: fty.sig.binder_id,
inputs: type_of::untuple_arguments_if_necessary(ccx,
fty.sig.inputs.as_slice(),
fty.abi),
fn_style: closure_info.closure_type.fn_style,
abi: Rust,
sig: ty::FnSig {
- binder_id: closure_info.closure_type
- .sig
- .binder_id,
inputs: new_inputs,
output: new_output,
variadic: false,