auto merge of #18993 : nikomatsakis/rust/hrtb-5, r=pcwalton

Enough said.

Fixes #18639.

r? @pcwalton (or someone else?)

This is a [breaking-change]. In particular, several feature gates related to unboxed closures were consolidated into one (`overloaded_calls`, `unboxed_closure_sugar` => `unboxed_closures`). Otherwise, I think everything that worked before should still work. File a bug and cc @nikomatsakis if you find otherwise. :)
This commit is contained in:
bors 2014-11-18 19:11:43 +00:00
commit c8d6e3b2c2
126 changed files with 3954 additions and 2004 deletions

View file

@ -2513,11 +2513,6 @@ The currently implemented features of the reference compiler are:
closure as `once` is unlikely to be supported going forward. So
they are hidden behind this feature until they are to be removed.
* `overloaded_calls` - Allow implementing the `Fn*` family of traits on user
types, allowing overloading the call operator (`()`).
This feature may still undergo changes before being
stabilized.
* `phase` - Usage of the `#[phase]` attribute allows loading compiler plugins
for custom lints or syntax extensions. The implementation is
considered unwholesome and in need of overhaul, and it is not clear
@ -2560,11 +2555,8 @@ The currently implemented features of the reference compiler are:
* `trace_macros` - Allows use of the `trace_macros` macro, which is a nasty
hack that will certainly be removed.
* `unboxed_closure_sugar` - Allows using `|Foo| -> Bar` as a trait bound
meaning one of the `Fn` traits. Still
experimental.
* `unboxed_closures` - A work in progress feature with many known bugs.
* `unboxed_closures` - Rust's new closure design, which is currently a work in
progress feature with many known bugs.
* `unsafe_destructor` - Allows use of the `#[unsafe_destructor]` attribute,
which is considered wildly unsafe and will be

View file

@ -144,5 +144,6 @@ register_diagnostics!(
E0165,
E0166,
E0167,
E0168
E0168,
E0169
)

View file

@ -294,7 +294,7 @@ fn parse_region(st: &mut PState, conv: conv_did) -> ty::Region {
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), ']');
@ -579,8 +579,6 @@ fn parse_bare_fn_ty(st: &mut PState, conv: conv_did) -> ty::BareFnTy {
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)));
@ -598,8 +596,7 @@ fn parse_sig(st: &mut PState, conv: conv_did) -> ty::FnSig {
}
_ => 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}
}

View file

@ -130,7 +130,7 @@ fn enc_region_substs(w: &mut SeekableMemWriter, cx: &ctxt, substs: &subst::Regio
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, "]");
}
@ -331,7 +331,7 @@ pub fn enc_closure_ty(w: &mut SeekableMemWriter, cx: &ctxt, ft: &ty::ClosureTy)
}
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);
}

View file

@ -483,8 +483,8 @@ impl tr for def::Def {
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)

View file

@ -65,8 +65,8 @@ pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator
impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> {
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
b: &'v Block, s: Span, n: NodeId) {
borrowck_fn(self, fk, fd, b, s, n);
b: &'v Block, s: Span, id: ast::NodeId) {
borrowck_fn(self, fk, fd, b, s, id);
}
fn visit_item(&mut self, item: &ast::Item) {

View file

@ -139,8 +139,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> {
check_local(self, l);
}
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
b: &'v Block, s: Span, _: NodeId) {
check_fn(self, fk, fd, b, s);
b: &'v Block, s: Span, n: NodeId) {
check_fn(self, fk, fd, b, s, n);
}
}
@ -920,7 +920,8 @@ fn check_fn(cx: &mut MatchCheckCtxt,
kind: FnKind,
decl: &FnDecl,
body: &Block,
sp: Span) {
sp: Span,
_: NodeId) {
visit::walk_fn(cx, kind, decl, body, sp);
for input in decl.inputs.iter() {
is_refutable(cx, &*input.pat, |pat| {

View file

@ -187,9 +187,8 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, cx: &ty::ctxt) -> String {
}
impl<'a, 'tcx, 'v> Visitor<'v> for IrMaps<'a, 'tcx> {
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
b: &'v Block, s: Span, n: NodeId) {
visit_fn(self, fk, fd, b, s, n);
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl, b: &'v Block, s: Span, id: ast::NodeId) {
visit_fn(self, fk, fd, b, s, id);
}
fn visit_local(&mut self, l: &ast::Local) { visit_local(self, l); }
fn visit_expr(&mut self, ex: &Expr) { visit_expr(self, ex); }
@ -374,9 +373,8 @@ fn visit_fn(ir: &mut IrMaps,
decl: &FnDecl,
body: &Block,
sp: Span,
id: NodeId) {
debug!("visit_fn: id={}", id);
let _i = ::util::common::indenter();
id: ast::NodeId) {
debug!("visit_fn");
// swap in a new set of IR maps for this function body:
let mut fn_maps = IrMaps::new(ir.tcx);

View file

@ -5038,10 +5038,10 @@ impl<'a> Resolver<'a> {
visit::walk_ty(self, ty);
}
TyPolyTraitRef(ref poly_trait_ref) => {
self.resolve_poly_trait_reference(
TyPolyTraitRef(ref bounds) => {
self.resolve_type_parameter_bounds(
ty.id,
&**poly_trait_ref,
bounds,
TraitObject);
visit::walk_ty(self, ty);
}

View file

@ -21,11 +21,13 @@ pub use self::DefRegion::*;
use self::ScopeChain::*;
use session::Session;
use middle::def;
use middle::resolve::DefMap;
use middle::subst;
use middle::ty;
use std::fmt;
use syntax::ast;
use syntax::codemap::Span;
use syntax::owned_slice::OwnedSlice;
use syntax::parse::token::special_idents;
use syntax::parse::token;
use syntax::print::pprust::{lifetime_to_string};
@ -39,8 +41,7 @@ pub enum DefRegion {
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),
@ -53,16 +54,17 @@ pub type NamedRegionMap = NodeMap<DefRegion>;
struct LifetimeContext<'a> {
sess: &'a Session,
named_region_map: &'a mut NamedRegionMap,
scope: Scope<'a>
scope: Scope<'a>,
def_map: &'a DefMap,
}
enum ScopeChain<'a> {
/// 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>),
@ -73,12 +75,13 @@ type Scope<'a> = &'a ScopeChain<'a>;
static ROOT_SCOPE: ScopeChain<'static> = RootScope;
pub fn krate(sess: &Session, krate: &ast::Crate) -> NamedRegionMap {
pub fn krate(sess: &Session, krate: &ast::Crate, def_map: &DefMap) -> NamedRegionMap {
let mut named_region_map = NodeMap::new();
visit::walk_crate(&mut LifetimeContext {
sess: sess,
named_region_map: &mut named_region_map,
scope: &ROOT_SCOPE
scope: &ROOT_SCOPE,
def_map: def_map,
}, krate);
sess.abort_if_errors();
named_region_map
@ -102,8 +105,7 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
ast::ItemTy(_, ref generics) |
ast::ItemEnum(_, ref generics) |
ast::ItemStruct(_, ref generics) |
ast::ItemTrait(ref generics, _, _, _) |
ast::ItemImpl(ref generics, _, _, _) => {
ast::ItemTrait(ref generics, _, _, _) => {
// These kinds of items have only early bound lifetime parameters.
let lifetimes = &generics.lifetimes;
self.with(EarlyScope(subst::TypeSpace, lifetimes, &ROOT_SCOPE), |this| {
@ -111,16 +113,23 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
visit::walk_item(this, item);
});
}
ast::ItemImpl(ref generics, _, _, _) => {
// Impls have both early- and late-bound lifetimes.
self.visit_early_late(subst::TypeSpace, generics, |this| {
this.check_lifetime_defs(&generics.lifetimes);
visit::walk_item(this, item);
})
}
}
}
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(..) => {
@ -130,21 +139,58 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
}
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)
};
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);
});
}
ast::TyPath(ref path, ref opt_bounds, id) => {
// if this path references a trait, then this will resolve to
// a trait ref, which introduces a binding scope.
match self.def_map.borrow().get(&id) {
Some(&def::DefTrait(..)) => {
self.with(LateScope(&Vec::new(), self.scope), |this| {
this.visit_path(path, id);
});
self.with(LateScope(ty.id, lifetimes, self.scope), |this| {
this.check_lifetime_defs(lifetimes);
visit::walk_ty(this, ty);
});
match *opt_bounds {
Some(ref bounds) => {
visit::walk_ty_param_bounds_helper(self, bounds);
}
None => { }
}
}
_ => {
visit::walk_ty(self, 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))
}
@ -162,7 +208,7 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
fn visit_generics(&mut self, generics: &ast::Generics) {
for ty_param in generics.ty_params.iter() {
self.visit_ty_param_bounds(&ty_param.bounds);
visit::walk_ty_param_bounds_helper(self, &ty_param.bounds);
match ty_param.default {
Some(ref ty) => self.visit_ty(&**ty),
None => {}
@ -170,41 +216,14 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
}
for predicate in generics.where_clause.predicates.iter() {
self.visit_ident(predicate.span, predicate.ident);
self.visit_ty_param_bounds(&predicate.bounds);
}
}
}
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
};
debug!("entering scope {}", this.scope);
f(&mut this);
debug!("exiting scope {}", this.scope);
}
fn visit_ty_param_bounds(&mut self,
bounds: &OwnedSlice<ast::TyParamBound>) {
for bound in bounds.iter() {
match *bound {
ast::TraitTyParamBound(ref trait_ref) => {
self.visit_poly_trait_ref(trait_ref);
}
ast::RegionTyParamBound(ref lifetime) => {
self.visit_lifetime_ref(lifetime);
}
}
visit::walk_ty_param_bounds_helper(self, &predicate.bounds);
}
}
fn visit_poly_trait_ref(&mut self, trait_ref: &ast::PolyTraitRef) {
let ref_id = trait_ref.trait_ref.ref_id;
self.with(LateScope(ref_id, &trait_ref.bound_lifetimes, self.scope), |this| {
debug!("visit_poly_trait_ref trait_ref={}", trait_ref);
self.with(LateScope(&trait_ref.bound_lifetimes, self.scope), |this| {
this.check_lifetime_defs(&trait_ref.bound_lifetimes);
for lifetime in trait_ref.bound_lifetimes.iter() {
this.visit_lifetime_decl(lifetime);
@ -216,11 +235,25 @@ impl<'a> LifetimeContext<'a> {
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|) {
/*!
@ -249,15 +282,14 @@ impl<'a> LifetimeContext<'a> {
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);
});
@ -271,7 +303,7 @@ impl<'a> LifetimeContext<'a> {
// 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 {
@ -291,22 +323,22 @@ impl<'a> LifetimeContext<'a> {
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;
}
}
@ -339,7 +371,7 @@ impl<'a> LifetimeContext<'a> {
}
EarlyScope(_, lifetimes, s) |
LateScope(_, lifetimes, s) => {
LateScope(lifetimes, s) => {
search_result = search_lifetimes(lifetimes, lifetime_ref);
if search_result.is_some() {
break;
@ -467,10 +499,10 @@ fn early_bound_lifetime_names(generics: &ast::Generics) -> Vec<ast::Name> {
FreeLifetimeCollector { early_bound: &mut early_bound,
late_bound: &mut late_bound };
for ty_param in generics.ty_params.iter() {
visit::walk_ty_param_bounds(&mut collector, &ty_param.bounds);
visit::walk_ty_param_bounds_helper(&mut collector, &ty_param.bounds);
}
for predicate in generics.where_clause.predicates.iter() {
visit::walk_ty_param_bounds(&mut collector, &predicate.bounds);
visit::walk_ty_param_bounds_helper(&mut collector, &predicate.bounds);
}
}
@ -517,7 +549,7 @@ impl<'a> fmt::Show for ScopeChain<'a> {
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"),
}

View file

@ -14,8 +14,7 @@ pub use self::ParamSpace::*;
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;
@ -101,6 +100,17 @@ impl Substs {
regions_is_noop && self.types.is_empty()
}
pub fn has_regions_escaping_depth(&self, depth: uint) -> bool {
self.types.iter().any(|&t| ty::type_escapes_depth(t, depth)) || {
match self.regions {
ErasedRegions =>
false,
NonerasedRegions(ref regions) =>
regions.iter().any(|r| r.escapes_depth(depth)),
}
}
}
pub fn self_ty(&self) -> Option<ty::t> {
self.types.get_self().map(|&t| t)
}
@ -166,6 +176,13 @@ impl RegionSubsts {
NonerasedRegions(r) => NonerasedRegions(op(r, a))
}
}
pub fn is_erased(&self) -> bool {
match *self {
ErasedRegions => true,
NonerasedRegions(_) => false,
}
}
}
///////////////////////////////////////////////////////////////////////////
@ -392,6 +409,10 @@ impl<T> VecPerParamSpace<T> {
self.content.iter()
}
pub fn iter_enumerated<'a>(&'a self) -> EnumeratedItems<'a,T> {
EnumeratedItems::new(self)
}
pub fn as_slice(&self) -> &[T] {
self.content.as_slice()
}
@ -421,6 +442,14 @@ impl<T> VecPerParamSpace<T> {
self.assoc_limit)
}
pub fn map_enumerated<U>(&self, pred: |(ParamSpace, uint, &T)| -> U) -> VecPerParamSpace<U> {
let result = self.iter_enumerated().map(pred).collect();
VecPerParamSpace::new_internal(result,
self.type_limit,
self.self_limit,
self.assoc_limit)
}
pub fn map_move<U>(self, pred: |T| -> U) -> VecPerParamSpace<U> {
let SeparateVecsPerParamSpace {
types: t,
@ -457,6 +486,49 @@ impl<T> VecPerParamSpace<T> {
}
}
pub struct EnumeratedItems<'a,T:'a> {
vec: &'a VecPerParamSpace<T>,
space_index: uint,
elem_index: uint
}
impl<'a,T> EnumeratedItems<'a,T> {
fn new(v: &'a VecPerParamSpace<T>) -> EnumeratedItems<'a,T> {
let mut result = EnumeratedItems { vec: v, space_index: 0, elem_index: 0 };
result.adjust_space();
result
}
fn adjust_space(&mut self) {
let spaces = ParamSpace::all();
while
self.space_index < spaces.len() &&
self.elem_index >= self.vec.len(spaces[self.space_index])
{
self.space_index += 1;
self.elem_index = 0;
}
}
}
impl<'a,T> Iterator<(ParamSpace, uint, &'a T)> for EnumeratedItems<'a,T> {
fn next(&mut self) -> Option<(ParamSpace, uint, &'a T)> {
let spaces = ParamSpace::all();
if self.space_index < spaces.len() {
let space = spaces[self.space_index];
let index = self.elem_index;
let item = self.vec.get(space, index);
self.elem_index += 1;
self.adjust_space();
Some((space, index, item))
} else {
None
}
}
}
///////////////////////////////////////////////////////////////////////////
// Public trait `Subst`
//
@ -486,7 +558,8 @@ impl<T:TypeFoldable> Subst for T {
substs: substs,
span: span,
root_ty: None,
ty_stack_depth: 0 };
ty_stack_depth: 0,
region_binders_passed: 0 };
(*self).fold_with(&mut folder)
}
}
@ -506,11 +579,22 @@ struct SubstFolder<'a, 'tcx: 'a> {
// 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
@ -524,7 +608,9 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> {
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(
@ -557,12 +643,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> {
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)
@ -576,30 +657,100 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> {
}
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 \
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());
}
}
}
}
}
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(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)
}
}

View file

@ -17,7 +17,7 @@ use super::util;
use middle::subst;
use middle::subst::Subst;
use middle::ty;
use middle::typeck::infer::InferCtxt;
use middle::typeck::infer::{mod, InferCtxt};
use syntax::ast;
use syntax::codemap::DUMMY_SP;
use util::ppaux::Repr;
@ -38,14 +38,18 @@ pub fn impl_can_satisfy(infcx: &InferCtxt,
util::fresh_substs_for_impl(infcx, DUMMY_SP, impl1_def_id);
let impl1_trait_ref =
ty::impl_trait_ref(infcx.tcx, impl1_def_id).unwrap()
.subst(infcx.tcx, &impl1_substs);
.subst(infcx.tcx, &impl1_substs);
let impl1_trait_ref =
infcx.replace_late_bound_regions_with_fresh_var(DUMMY_SP,
infer::FnCall,
&impl1_trait_ref).0;
// Determine whether `impl2` can provide an implementation for those
// same types.
let param_env = ty::empty_parameter_environment();
let mut selcx = SelectionContext::intercrate(infcx, &param_env, infcx.tcx);
let obligation = Obligation::misc(DUMMY_SP, impl1_trait_ref);
debug!("impl_can_satisfy obligation={}", obligation.repr(infcx.tcx));
debug!("impl_can_satisfy(obligation={})", obligation.repr(infcx.tcx));
selcx.evaluate_impl(impl2_def_id, &obligation)
}

View file

@ -55,6 +55,7 @@ impl FulfillmentContext {
obligation: Obligation)
{
debug!("register_obligation({})", obligation.repr(tcx));
assert!(!obligation.trait_ref.has_escaping_regions());
self.trait_obligations.push(obligation);
}

View file

@ -281,33 +281,28 @@ pub fn overlapping_impls(infcx: &InferCtxt,
coherence::impl_can_satisfy(infcx, impl2_def_id, impl1_def_id)
}
pub fn impl_obligations(tcx: &ty::ctxt,
cause: ObligationCause,
impl_def_id: ast::DefId,
impl_substs: &subst::Substs)
-> subst::VecPerParamSpace<Obligation>
{
let impl_generics = ty::lookup_item_type(tcx, impl_def_id).generics;
obligations_for_generics(tcx, cause, &impl_generics, impl_substs)
}
pub fn obligations_for_generics(tcx: &ty::ctxt,
cause: ObligationCause,
generics: &ty::Generics,
substs: &subst::Substs)
generic_bounds: &ty::GenericBounds,
type_substs: &subst::VecPerParamSpace<ty::t>)
-> subst::VecPerParamSpace<Obligation>
{
/*!
* Given generics for an impl like:
* Given generic bounds from an impl like:
*
* impl<A:Foo, B:Bar+Qux> ...
*
* and a substs vector like `<A=A0, B=B0>`, yields a result like
* along with the bindings for the types `A` and `B` (e.g.,
* `<A=A0, B=B0>`), yields a result like
*
* [[Foo for A0, Bar for B0, Qux for B0], [], []]
*
* Expects that `generic_bounds` have already been fully
* substituted, late-bound regions liberated and so forth,
* so that they are in the same namespace as `type_substs`.
*/
util::obligations_for_generics(tcx, cause, 0, generics, substs)
util::obligations_for_generics(tcx, cause, 0, generic_bounds, type_substs)
}
pub fn obligation_for_builtin_bound(tcx: &ty::ctxt,

View file

@ -31,9 +31,7 @@ use middle::fast_reject;
use middle::mem_categorization::Typer;
use middle::subst::{Subst, Substs, VecPerParamSpace};
use middle::ty;
use middle::typeck::check::regionmanip;
use middle::typeck::infer;
use middle::typeck::infer::LateBoundRegionConversionTime::*;
use middle::typeck::infer::{InferCtxt, TypeSkolemizer};
use middle::ty_fold::TypeFoldable;
use std::cell::RefCell;
@ -211,6 +209,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
*/
debug!("select({})", obligation.repr(self.tcx()));
assert!(!obligation.trait_ref.has_escaping_regions());
let stack = self.push_stack(None, obligation);
match try!(self.candidate_from_obligation(&stack)) {
@ -263,6 +262,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!("evaluate_obligation({})",
obligation.repr(self.tcx()));
assert!(!obligation.trait_ref.has_escaping_regions());
let stack = self.push_stack(None, obligation);
self.evaluate_stack(&stack).may_apply()
@ -747,6 +747,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!("candidate_from_obligation(cache_skol_trait_ref={}, obligation={})",
cache_skol_trait_ref.repr(self.tcx()),
stack.repr(self.tcx()));
assert!(!stack.obligation.trait_ref.has_escaping_regions());
match self.check_candidate_cache(cache_skol_trait_ref.clone()) {
Some(c) => {
@ -1707,27 +1708,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
};
// FIXME(pcwalton): This is a bogus thing to do, but
// it'll do for now until we get the new trait-bound
// region skolemization working.
let (_, new_signature) =
regionmanip::replace_late_bound_regions(
self.tcx(),
closure_type.sig.binder_id,
&closure_type.sig,
|br| self.infcx.next_region_var(
infer::LateBoundRegion(obligation.cause.span, br,
infer::FnCall)));
let arguments_tuple = new_signature.inputs[0];
let closure_sig = &closure_type.sig;
let arguments_tuple = closure_sig.inputs[0];
let substs =
Substs::new_trait(
vec![arguments_tuple.subst(self.tcx(), substs),
closure_sig.output.unwrap().subst(self.tcx(), substs)],
vec![],
vec![],
obligation.self_ty());
let trait_ref = Rc::new(ty::TraitRef {
def_id: obligation.trait_ref.def_id,
substs: Substs::new_trait(
vec![arguments_tuple.subst(self.tcx(), substs),
new_signature.output.unwrap().subst(self.tcx(), substs)],
vec![],
vec![],
obligation.self_ty())
substs: substs,
});
self.confirm(obligation.cause,
@ -2025,10 +2017,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
impl_substs: &Substs)
-> VecPerParamSpace<Obligation>
{
let impl_generics = ty::lookup_item_type(self.tcx(),
impl_def_id).generics;
let impl_generics = ty::lookup_item_type(self.tcx(), impl_def_id).generics;
let bounds = impl_generics.to_bounds(self.tcx(), impl_substs);
util::obligations_for_generics(self.tcx(), cause, recursion_depth,
&impl_generics, impl_substs)
&bounds, &impl_substs.types)
}
}

View file

@ -10,7 +10,7 @@
// except according to those terms.
use middle::subst;
use middle::subst::{ParamSpace, Subst, Substs, VecPerParamSpace};
use middle::subst::{ParamSpace, Substs, VecPerParamSpace};
use middle::typeck::infer::InferCtxt;
use middle::ty;
use std::collections::HashSet;
@ -173,25 +173,25 @@ impl fmt::Show for VtableParamData {
pub fn obligations_for_generics(tcx: &ty::ctxt,
cause: ObligationCause,
recursion_depth: uint,
generics: &ty::Generics,
substs: &Substs)
generic_bounds: &ty::GenericBounds,
type_substs: &VecPerParamSpace<ty::t>)
-> VecPerParamSpace<Obligation>
{
/*! See `super::obligations_for_generics` */
debug!("obligations_for_generics(generics={}, substs={})",
generics.repr(tcx), substs.repr(tcx));
debug!("obligations_for_generics(generic_bounds={}, type_substs={})",
generic_bounds.repr(tcx), type_substs.repr(tcx));
let mut obligations = VecPerParamSpace::empty();
for def in generics.types.iter() {
for (space, index, bounds) in generic_bounds.types.iter_enumerated() {
push_obligations_for_param_bounds(tcx,
cause,
recursion_depth,
def.space,
def.index,
&def.bounds,
substs,
space,
index,
bounds,
type_substs,
&mut obligations);
}
@ -207,11 +207,10 @@ fn push_obligations_for_param_bounds(
space: subst::ParamSpace,
index: uint,
param_bounds: &ty::ParamBounds,
param_substs: &Substs,
param_type_substs: &VecPerParamSpace<ty::t>,
obligations: &mut VecPerParamSpace<Obligation>)
{
let param_ty = *param_substs.types.get(space, index);
let param_ty = *param_type_substs.get(space, index);
for builtin_bound in param_bounds.builtin_bounds.iter() {
let obligation = obligation_for_builtin_bound(tcx,
cause,
@ -225,12 +224,11 @@ fn push_obligations_for_param_bounds(
}
for bound_trait_ref in param_bounds.trait_bounds.iter() {
let bound_trait_ref = bound_trait_ref.subst(tcx, param_substs);
obligations.push(
space,
Obligation { cause: cause,
recursion_depth: recursion_depth,
trait_ref: bound_trait_ref });
trait_ref: (*bound_trait_ref).clone() });
}
}

View file

@ -53,7 +53,7 @@ use middle::subst::{mod, Subst, Substs, VecPerParamSpace};
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};
@ -609,13 +609,14 @@ pub struct ctxt<'tcx> {
// 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,
}
}
@ -626,6 +627,9 @@ pub type t_box = &'static t_box_;
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 {
@ -670,6 +674,50 @@ pub fn type_needs_infer(t: t) -> bool {
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,
@ -706,17 +754,16 @@ impl FnOutput {
* 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
@ -729,6 +776,54 @@ pub struct ParamTy {
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 {
@ -741,9 +836,8 @@ 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
@ -885,12 +979,19 @@ pub type UpvarBorrowMap = FnvHashMap<UpvarId, UpvarBorrow>;
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)]
@ -928,6 +1029,7 @@ mod primitives {
pub static $name: t_box_ = t_box_ {
sty: $sty,
flags: super::NO_TYPE_FLAGS,
region_depth: 0,
};
)
)
@ -950,6 +1052,7 @@ mod primitives {
pub static TY_ERR: t_box_ = t_box_ {
sty: super::ty_err,
flags: super::HAS_TY_ERR,
region_depth: 0,
};
}
@ -1002,12 +1105,46 @@ pub struct TyTrait {
pub bounds: ExistentialBounds
}
/**
* A complete reference to a trait. These take numerous guises in syntax,
* but perhaps the most recognizable form is in a where clause:
*
* T : Foo<U>
*
* This would be represented by a trait-reference where the def-id is the
* def-id for the trait `Foo` and the substs defines `T` as parameter 0 in the
* `SelfSpace` and `U` as parameter 0 in the `TypeSpace`.
*
* Trait references also appear in object types like `Foo<U>`, but in
* that case the `Self` parameter is absent from the substitutions.
*
* Note that a `TraitRef` introduces a level of region binding, to
* account for higher-ranked trait bounds like `T : for<'a> Foo<&'a
* U>` or higher-ranked object types.
*/
#[deriving(Clone, PartialEq, Eq, Hash, Show)]
pub struct TraitRef {
pub def_id: DefId,
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),
@ -1272,6 +1409,52 @@ impl Generics {
pub fn has_region_params(&self, space: subst::ParamSpace) -> bool {
!self.regions.is_empty_in(space)
}
pub fn to_bounds(&self, tcx: &ty::ctxt, substs: &Substs) -> GenericBounds {
GenericBounds {
types: self.types.map(|d| d.bounds.subst(tcx, substs)),
regions: self.regions.map(|d| d.bounds.subst(tcx, substs)),
}
}
}
/**
* Represents the bounds declared on a particular set of type
* parameters. Should eventually be generalized into a flag list of
* where clauses. You can obtain a `GenericBounds` list from a
* `Generics` by using the `to_bounds` method. Note that this method
* reflects an important semantic invariant of `GenericBounds`: while
* the bounds in a `Generics` are expressed in terms of the bound type
* parameters of the impl/trait/whatever, a `GenericBounds` instance
* represented a set of bounds for some particular instantiation,
* meaning that the generic parameters have been substituted with
* their values.
*
* Example:
*
* struct Foo<T,U:Bar<T>> { ... }
*
* Here, the `Generics` for `Foo` would contain a list of bounds like
* `[[], [U:Bar<T>]]`. Now if there were some particular reference
* like `Foo<int,uint>`, then the `GenericBounds` would be `[[],
* [uint:Bar<int>]]`.
*/
#[deriving(Clone, Show)]
pub struct GenericBounds {
pub types: VecPerParamSpace<ParamBounds>,
pub regions: VecPerParamSpace<Vec<Region>>,
}
impl GenericBounds {
pub fn empty() -> GenericBounds {
GenericBounds { types: VecPerParamSpace::empty(),
regions: VecPerParamSpace::empty() }
}
pub fn has_escaping_regions(&self) -> bool {
self.types.any(|pb| pb.trait_bounds.iter().any(|tr| tr.has_escaping_regions())) ||
self.regions.any(|rs| rs.iter().any(|r| r.escapes_depth(0)))
}
}
impl TraitRef {
@ -1290,6 +1473,14 @@ impl TraitRef {
// associated types.
self.substs.types.as_slice()
}
pub fn has_escaping_regions(&self) -> bool {
self.substs.has_regions_escaping_depth(1)
}
pub fn has_bound_regions(&self) -> bool {
self.substs.has_regions_escaping_depth(0)
}
}
/// When type checking, we use the `ParameterEnvironment` to track
@ -1597,99 +1788,12 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t {
_ => ()
}
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;
@ -1705,6 +1809,188 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t {
}
}
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 }) => {
let mut computation = FlagComputation::new();
computation.add_substs(&principal.substs);
self.add_bound_computation(&computation);
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 {
@ -1855,7 +2141,6 @@ pub fn mk_bare_fn(cx: &ctxt, fty: BareFnTy) -> t {
}
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();
@ -1864,7 +2149,6 @@ pub fn mk_ctor_fn(cx: &ctxt,
fn_style: ast::NormalFn,
abi: abi::Rust,
sig: FnSig {
binder_id: binder_id,
inputs: input_args,
output: ty::FnConverging(output),
variadic: false
@ -4498,9 +4782,99 @@ pub fn bounds_for_trait_ref(tcx: &ctxt,
-> ty::ParamBounds
{
let trait_def = lookup_trait_def(tcx, trait_ref.def_id);
debug!("bounds_for_trait_ref(trait_def={}, trait_ref={})",
trait_def.repr(tcx), trait_ref.repr(tcx));
trait_def.bounds.subst(tcx, &trait_ref.substs)
// The interaction between HRTB and supertraits is not entirely
// obvious. Let me walk you (and myself) through an example.
//
// Let's start with an easy case. Consider two traits:
//
// trait Foo<'a> : Bar<'a,'a> { }
// trait Bar<'b,'c> { }
//
// Now, if we have a trait reference `for<'x> T : Foo<'x>`, then
// we can deduce that `for<'x> T : Bar<'x,'x>`. Basically, if we
// knew that `Foo<'x>` (for any 'x) then we also know that
// `Bar<'x,'x>` (for any 'x). This more-or-less falls out from
// normal substitution.
//
// In terms of why this is sound, the idea is that whenever there
// is an impl of `T:Foo<'a>`, it must show that `T:Bar<'a,'a>`
// holds. So if there is an impl of `T:Foo<'a>` that applies to
// all `'a`, then we must know that `T:Bar<'a,'a>` holds for all
// `'a`.
//
// Another example to be careful of is this:
//
// trait Foo1<'a> : for<'b> Bar1<'a,'b> { }
// trait Bar1<'b,'c> { }
//
// Here, if we have `for<'x> T : Foo1<'x>`, then what do we know?
// The answer is that we know `for<'x,'b> T : Bar1<'x,'b>`. The
// reason is similar to the previous example: any impl of
// `T:Foo1<'x>` must show that `for<'b> T : Bar1<'x, 'b>`. So
// basically we would want to collapse the bound lifetimes from
// the input (`trait_ref`) and the supertraits.
//
// To achieve this in practice is fairly straightforward. Let's
// consider the more complicated scenario:
//
// - We start out with `for<'x> T : Foo1<'x>`. In this case, `'x`
// has a De Bruijn index of 1. We want to produce `for<'x,'b> T : Bar1<'x,'b>`,
// where both `'x` and `'b` would have a DB index of 1.
// The substitution from the input trait-ref is therefore going to be
// `'a => 'x` (where `'x` has a DB index of 1).
// - The super-trait-ref is `for<'b> Bar1<'a,'b>`, where `'a` is an
// early-bound parameter and `'b' is a late-bound parameter with a
// DB index of 1.
// - If we replace `'a` with `'x` from the input, it too will have
// a DB index of 1, and thus we'll have `for<'x,'b> Bar1<'x,'b>`
// just as we wanted.
//
// There is only one catch. If we just apply the substitution `'a
// => 'x` to `for<'b> Bar1<'a,'b>`, the substitution code will
// adjust the DB index because we substituting into a binder (it
// tries to be so smart...) resulting in `for<'x> for<'b>
// Bar1<'x,'b>` (we have no syntax for this, so use your
// imagination). Basically the 'x will have DB index of 2 and 'b
// will have DB index of 1. Not quite what we want. So we apply
// the substitution to the *contents* of the trait reference,
// rather than the trait reference itself (put another way, the
// substitution code expects equal binding levels in the values
// from the substitution and the value being substituted into, and
// this trick achieves that).
// Carefully avoid the binder introduced by each trait-ref by
// substituting over the substs, not the trait-refs themselves,
// thus achieving the "collapse" described in the big comment
// above.
let trait_bounds: Vec<_> =
trait_def.bounds.trait_bounds
.iter()
.map(|bound_trait_ref| {
ty::TraitRef::new(bound_trait_ref.def_id,
bound_trait_ref.substs.subst(tcx, &trait_ref.substs))
})
.map(|bound_trait_ref| Rc::new(bound_trait_ref))
.collect();
debug!("bounds_for_trait_ref: trait_bounds={}",
trait_bounds.repr(tcx));
// The region bounds and builtin bounds do not currently introduce
// binders so we can just substitute in a straightforward way here.
let region_bounds =
trait_def.bounds.region_bounds.subst(tcx, &trait_ref.substs);
let builtin_bounds =
trait_def.bounds.builtin_bounds.subst(tcx, &trait_ref.substs);
ty::ParamBounds {
trait_bounds: trait_bounds,
region_bounds: region_bounds,
builtin_bounds: builtin_bounds,
}
}
/// Iterate over attributes of a definition.
@ -4783,13 +5157,12 @@ pub fn normalize_ty(cx: &ctxt, t: t) -> t {
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,
@ -5305,11 +5678,13 @@ pub fn construct_parameter_environment(
// Compute the bounds on Self and the type parameters.
//
let mut bounds = VecPerParamSpace::empty();
for &space in subst::ParamSpace::all().iter() {
push_bounds_from_defs(tcx, &mut bounds, space, &free_substs,
generics.types.get_slice(space));
}
let bounds = generics.to_bounds(tcx, &free_substs);
let bounds = liberate_late_bound_regions(tcx, free_id, &bind(bounds)).value;
let obligations = traits::obligations_for_generics(tcx,
traits::ObligationCause::misc(span),
&bounds,
&free_substs.types);
let type_bounds = bounds.types.subst(tcx, &free_substs);
//
// Compute region bounds. For now, these relations are stored in a
@ -5318,24 +5693,20 @@ pub fn construct_parameter_environment(
//
for &space in subst::ParamSpace::all().iter() {
record_region_bounds_from_defs(tcx, space, &free_substs,
generics.regions.get_slice(space));
record_region_bounds(tcx, space, &free_substs, bounds.regions.get_slice(space));
}
debug!("construct_parameter_environment: free_id={} \
free_subst={} \
bounds={}",
debug!("construct_parameter_environment: free_id={} free_subst={} \
obligations={} type_bounds={}",
free_id,
free_substs.repr(tcx),
bounds.repr(tcx));
let obligations = traits::obligations_for_generics(tcx, traits::ObligationCause::misc(span),
generics, &free_substs);
obligations.repr(tcx),
type_bounds.repr(tcx));
return ty::ParameterEnvironment {
free_substs: free_substs,
bounds: bounds,
bounds: bounds.types,
implicit_region_bound: ty::ReScope(free_id),
caller_obligations: obligations,
selection_cache: traits::SelectionCache::new(),
@ -5366,28 +5737,16 @@ pub fn construct_parameter_environment(
}
}
fn push_bounds_from_defs(tcx: &ty::ctxt,
bounds: &mut subst::VecPerParamSpace<ParamBounds>,
space: subst::ParamSpace,
free_substs: &subst::Substs,
defs: &[TypeParameterDef]) {
for def in defs.iter() {
let b = def.bounds.subst(tcx, free_substs);
bounds.push(space, b);
}
}
fn record_region_bounds_from_defs(tcx: &ty::ctxt,
space: subst::ParamSpace,
free_substs: &subst::Substs,
defs: &[RegionParameterDef]) {
for (subst_region, def) in
fn record_region_bounds(tcx: &ty::ctxt,
space: subst::ParamSpace,
free_substs: &Substs,
bound_sets: &[Vec<ty::Region>]) {
for (subst_region, bound_set) in
free_substs.regions().get_slice(space).iter().zip(
defs.iter())
bound_sets.iter())
{
// For each region parameter 'subst...
let bounds = def.bounds.subst(tcx, free_substs);
for bound_region in bounds.iter() {
for bound_region in bound_set.iter() {
// Which is declared with a bound like 'subst:'bound...
match (subst_region, bound_region) {
(&ty::ReFree(subst_fr), &ty::ReFree(bound_fr)) => {
@ -5398,7 +5757,7 @@ pub fn construct_parameter_environment(
_ => {
// All named regions are instantiated with free regions.
tcx.sess.bug(
format!("push_region_bounds_from_defs: \
format!("record_region_bounds: \
non free region: {} / {}",
subst_region.repr(tcx),
bound_region.repr(tcx)).as_slice());
@ -5589,3 +5948,86 @@ impl AutoDerefRef {
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 but also method lookup and a few other places
* where precise region relationships are not required.
*/
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 }
}
}

View file

@ -42,7 +42,6 @@ use middle::ty;
use middle::traits;
use middle::typeck;
use std::rc::Rc;
use syntax::ast;
use syntax::owned_slice::OwnedSlice;
use util::ppaux::Repr;
@ -63,6 +62,17 @@ pub trait TypeFoldable {
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)
}
@ -85,10 +95,10 @@ pub trait TypeFolder<'tcx> {
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,
@ -153,6 +163,12 @@ impl TypeFoldable for () {
}
}
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))
@ -171,6 +187,15 @@ impl<T:TypeFoldable> TypeFoldable for Vec<T> {
}
}
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()
@ -179,7 +204,24 @@ impl<T:TypeFoldable> TypeFoldable for OwnedSlice<T> {
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
}
}
@ -221,7 +263,7 @@ impl TypeFoldable for ty::FnOutput {
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)
}
}
@ -368,6 +410,15 @@ impl TypeFoldable for ty::Generics {
}
}
impl TypeFoldable for ty::GenericBounds {
fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::GenericBounds {
ty::GenericBounds {
types: self.types.fold_with(folder),
regions: self.regions.fold_with(folder),
}
}
}
impl TypeFoldable for ty::UnsizeKind {
fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::UnsizeKind {
match *self {
@ -434,6 +485,7 @@ impl TypeFoldable for traits::VtableParamData {
// "super" routines: these are the default implementations for TypeFolder.
//
// They should invoke `foo.fold_with()` to do recursive folding.
pub fn super_fold_ty<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
t: ty::t)
-> ty::t {
@ -457,11 +509,21 @@ pub fn super_fold_substs<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
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 }
}
@ -497,9 +559,21 @@ pub fn super_fold_closure_ty<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
abi: fty.abi,
}
}
pub fn super_fold_trait_ref<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
t: &ty::TraitRef)
-> ty::TraitRef {
-> ty::TraitRef
{
this.enter_region_binder();
let result = super_fold_trait_ref_contents(this, t);
this.exit_region_binder();
result
}
pub fn super_fold_trait_ref_contents<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
t: &ty::TraitRef)
-> ty::TraitRef
{
ty::TraitRef {
def_id: t.def_id,
substs: t.substs.fold_with(this),
@ -621,6 +695,42 @@ pub fn super_fold_obligation<'tcx, T:TypeFolder<'tcx>>(this: &mut T,
}
}
///////////////////////////////////////////////////////////////////////////
// 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 HigherRankedFoldable for ty::TraitRef {
fn fold_contents<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::TraitRef {
super_fold_trait_ref_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))
}
}
impl<T:HigherRankedFoldable> HigherRankedFoldable for Rc<T> {
fn fold_contents<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Rc<T> {
Rc::new((**self).fold_contents(folder))
}
}
///////////////////////////////////////////////////////////////////////////
// Some sample folders
@ -655,77 +765,43 @@ impl<'a, 'tcx> TypeFolder<'tcx> for BottomUpFolder<'a, 'tcx> {
/// 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 => {}
}
fn enter_region_binder(&mut self) {
self.current_depth += 1;
}
let t1 = super_fold_ty(self, ty);
let ret = (self.fld_t)(t1);
if opt_binder_id.is_some() {
self.within_binder_ids.pop();
}
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)
}
}
}
@ -755,3 +831,33 @@ impl<'a, 'tcx> TypeFolder<'tcx> for RegionEraser<'a, 'tcx> {
}
}
}
///////////////////////////////////////////////////////////////////////////
// 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)
}))
}

View file

@ -55,11 +55,10 @@ use middle::subst::{FnSpace, TypeSpace, AssocSpace, SelfSpace, Subst, Substs};
use middle::subst::{VecPerParamSpace};
use middle::ty;
use middle::typeck::lookup_def_tcx;
use middle::typeck::infer;
use middle::typeck::rscope::{UnelidableRscope, RegionScope, SpecificRscope, BindingRscope};
use middle::typeck::rscope::{UnelidableRscope, RegionScope, SpecificRscope,
ShiftedRscope, BindingRscope};
use middle::typeck::rscope;
use middle::typeck::TypeAndSubsts;
use middle::typeck;
use util::nodemap::DefIdMap;
use util::ppaux::{Repr, UserString};
@ -106,9 +105,8 @@ pub fn ast_region_to_region(tcx: &ty::ctxt, lifetime: &ast::Lifetime)
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)) => {
@ -203,15 +201,14 @@ pub fn opt_ast_region_to_region<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
r
}
fn ast_path_substs<'tcx,AC,RS>(
fn ast_path_substs_for_ty<'tcx,AC,RS>(
this: &AC,
rscope: &RS,
decl_def_id: ast::DefId,
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
{
@ -235,12 +232,35 @@ fn ast_path_substs<'tcx,AC,RS>(
assert!(decl_generics.types.all(|d| d.space != FnSpace));
let (regions, types) = match path.segments.last().unwrap().parameters {
ast::AngleBracketedParameters(ref data) =>
angle_bracketed_parameters(this, rscope, data),
ast::ParenthesizedParameters(ref data) =>
parenthesized_parameters(this, binder_id, data),
ast::AngleBracketedParameters(ref data) => {
convert_angle_bracketed_parameters(this, rscope, data)
}
ast::ParenthesizedParameters(ref data) => {
span_err!(tcx.sess, path.span, E0169,
"parenthesized parameters may only be used with a trait");
(Vec::new(), convert_parenthesized_parameters(this, data))
}
};
create_substs_for_ast_path(this, rscope, path.span, decl_def_id,
decl_generics, self_ty, types, regions, associated_ty)
}
fn create_substs_for_ast_path<'tcx,AC,RS>(
this: &AC,
rscope: &RS,
span: Span,
decl_def_id: ast::DefId,
decl_generics: &ty::Generics,
self_ty: Option<ty::t>,
types: Vec<ty::t>,
regions: Vec<ty::Region>,
associated_ty: Option<ty::t>)
-> Substs
where AC: AstConv<'tcx>, RS: RegionScope
{
let tcx = this.tcx();
// If the type is parameterized by the this region, then replace this
// region with the current anon region binding (in other words,
// whatever & would get replaced with).
@ -250,10 +270,10 @@ fn ast_path_substs<'tcx,AC,RS>(
regions
} else {
let anon_regions =
rscope.anon_regions(path.span, expected_num_region_params);
rscope.anon_regions(span, expected_num_region_params);
if supplied_num_region_params != 0 || anon_regions.is_err() {
span_err!(tcx.sess, path.span, E0107,
span_err!(tcx.sess, span, E0107,
"wrong number of lifetime parameters: expected {}, found {}",
expected_num_region_params, supplied_num_region_params);
}
@ -285,7 +305,7 @@ fn ast_path_substs<'tcx,AC,RS>(
} else {
"expected"
};
this.tcx().sess.span_fatal(path.span,
this.tcx().sess.span_fatal(span,
format!("wrong number of type arguments: {} {}, found {}",
expected,
required_ty_param_count,
@ -296,7 +316,7 @@ fn ast_path_substs<'tcx,AC,RS>(
} else {
"expected"
};
this.tcx().sess.span_fatal(path.span,
this.tcx().sess.span_fatal(span,
format!("wrong number of type arguments: {} {}, found {}",
expected,
formal_ty_param_count,
@ -305,9 +325,9 @@ fn ast_path_substs<'tcx,AC,RS>(
if supplied_ty_param_count > required_ty_param_count
&& !this.tcx().sess.features.borrow().default_type_params {
span_err!(this.tcx().sess, path.span, E0108,
span_err!(this.tcx().sess, span, E0108,
"default type parameters are experimental and possibly buggy");
span_help!(this.tcx().sess, path.span,
span_help!(this.tcx().sess, span,
"add #![feature(default_type_params)] to the crate attributes to enable");
}
@ -333,12 +353,11 @@ fn ast_path_substs<'tcx,AC,RS>(
// This is a default type parameter.
let default = default.subst_spanned(tcx,
&substs,
Some(path.span));
Some(span));
substs.types.push(TypeSpace, default);
}
None => {
tcx.sess.span_bug(path.span,
"extra parameter without default");
tcx.sess.span_bug(span, "extra parameter without default");
}
}
}
@ -346,53 +365,64 @@ fn ast_path_substs<'tcx,AC,RS>(
for param in decl_generics.types.get_slice(AssocSpace).iter() {
substs.types.push(
AssocSpace,
this.associated_type_binding(path.span,
this.associated_type_binding(span,
associated_ty,
decl_def_id,
param.def_id))
}
return substs;
}
fn angle_bracketed_parameters<'tcx, AC, RS>(this: &AC,
rscope: &RS,
data: &ast::AngleBracketedParameterData)
-> (Vec<ty::Region>, Vec<ty::t>)
where AC: AstConv<'tcx>, RS: RegionScope
{
let regions: Vec<_> =
data.lifetimes.iter()
.map(|l| ast_region_to_region(this.tcx(), l))
.collect();
fn convert_angle_bracketed_parameters<'tcx, AC, RS>(this: &AC,
rscope: &RS,
data: &ast::AngleBracketedParameterData)
-> (Vec<ty::Region>, Vec<ty::t>)
where AC: AstConv<'tcx>, RS: RegionScope
{
let regions: Vec<_> =
data.lifetimes.iter()
.map(|l| ast_region_to_region(this.tcx(), l))
.collect();
let types: Vec<_> =
data.types.iter()
.map(|t| ast_ty_to_ty(this, rscope, &**t))
.collect();
let types: Vec<_> =
data.types.iter()
.map(|t| ast_ty_to_ty(this, rscope, &**t))
.collect();
(regions, types)
}
(regions, types)
}
fn parenthesized_parameters<'tcx,AC>(this: &AC,
binder_id: ast::NodeId,
data: &ast::ParenthesizedParameterData)
-> (Vec<ty::Region>, Vec<ty::t>)
where AC: AstConv<'tcx>
{
let binding_rscope = BindingRscope::new(binder_id);
fn convert_parenthesized_parameters<'tcx,AC>(this: &AC,
data: &ast::ParenthesizedParameterData)
-> Vec<ty::t>
where AC: AstConv<'tcx>
{
let binding_rscope = BindingRscope::new();
let inputs = data.inputs.iter()
.map(|a_t| ast_ty_to_ty(this, &binding_rscope, &**a_t))
.collect();
let input_ty = ty::mk_tup(this.tcx(), inputs);
let inputs = data.inputs.iter()
.map(|a_t| ast_ty_to_ty(this, &binding_rscope, &**a_t))
.collect();
let input_ty = ty::mk_tup(this.tcx(), inputs);
let output = match data.output {
Some(ref output_ty) => ast_ty_to_ty(this, &binding_rscope, &**output_ty),
None => ty::mk_nil(this.tcx())
};
let output = match data.output {
Some(ref output_ty) => ast_ty_to_ty(this, &binding_rscope, &**output_ty),
None => ty::mk_nil(this.tcx()),
};
(Vec::new(), vec![input_ty, output])
}
vec![input_ty, output]
}
pub fn instantiate_poly_trait_ref<'tcx,AC,RS>(
this: &AC,
rscope: &RS,
ast_trait_ref: &ast::PolyTraitRef,
self_ty: Option<ty::t>,
associated_type: Option<ty::t>)
-> Rc<ty::TraitRef>
where AC: AstConv<'tcx>, RS: RegionScope
{
instantiate_trait_ref(this, rscope, &ast_trait_ref.trait_ref, self_ty, associated_type)
}
pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC,
@ -413,16 +443,9 @@ pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC,
match lookup_def_tcx(this.tcx(),
ast_trait_ref.path.span,
ast_trait_ref.ref_id) {
def::DefTrait(trait_did) => {
let trait_ref =
Rc::new(ast_path_to_trait_ref(this,
rscope,
trait_did,
self_ty,
associated_type,
&ast_trait_ref.path,
ast_trait_ref.ref_id));
def::DefTrait(trait_def_id) => {
let trait_ref = Rc::new(ast_path_to_trait_ref(this, rscope, trait_def_id, self_ty,
associated_type, &ast_trait_ref.path));
this.tcx().trait_refs.borrow_mut().insert(ast_trait_ref.ref_id,
trait_ref.clone());
trait_ref
@ -435,36 +458,52 @@ pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC,
}
}
pub fn ast_path_to_trait_ref<'tcx,AC,RS>(this: &AC,
rscope: &RS,
trait_def_id: ast::DefId,
self_ty: Option<ty::t>,
associated_type: Option<ty::t>,
path: &ast::Path,
binder_id: ast::NodeId)
-> ty::TraitRef
where AC: AstConv<'tcx>,
RS: RegionScope {
fn ast_path_to_trait_ref<'tcx,AC,RS>(
this: &AC,
rscope: &RS,
trait_def_id: ast::DefId,
self_ty: Option<ty::t>,
associated_type: Option<ty::t>,
path: &ast::Path)
-> ty::TraitRef
where AC: AstConv<'tcx>, RS: RegionScope
{
let trait_def = this.get_trait_def(trait_def_id);
ty::TraitRef {
def_id: trait_def_id,
substs: ast_path_substs(this,
rscope,
trait_def_id,
&trait_def.generics,
self_ty,
associated_type,
path,
binder_id)
}
// the trait reference introduces a binding level here, so
// we need to shift the `rscope`. It'd be nice if we could
// do away with this rscope stuff and work this knowledge
// into resolve_lifetimes, as we do with non-omitted
// lifetimes. Oh well, not there yet.
let shifted_rscope = ShiftedRscope::new(rscope);
let (regions, types) = match path.segments.last().unwrap().parameters {
ast::AngleBracketedParameters(ref data) => {
convert_angle_bracketed_parameters(this, &shifted_rscope, data)
}
ast::ParenthesizedParameters(ref data) => {
(Vec::new(), convert_parenthesized_parameters(this, data))
}
};
let substs = create_substs_for_ast_path(this,
&shifted_rscope,
path.span,
trait_def_id,
&trait_def.generics,
self_ty,
types,
regions,
associated_type);
ty::TraitRef::new(trait_def_id, substs)
}
pub fn ast_path_to_ty<'tcx, 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();
@ -473,14 +512,13 @@ pub fn ast_path_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
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 }
}
@ -494,8 +532,7 @@ pub fn ast_path_to_ty_relaxed<'tcx,AC,RS>(
this: &AC,
rscope: &RS,
did: ast::DefId,
path: &ast::Path,
binder_id: ast::NodeId)
path: &ast::Path)
-> TypeAndSubsts
where AC : AstConv<'tcx>, RS : RegionScope
{
@ -521,7 +558,7 @@ pub fn ast_path_to_ty_relaxed<'tcx,AC,RS>(
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);
@ -628,7 +665,7 @@ pub fn ast_ty_to_builtin_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
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);
@ -689,8 +726,7 @@ fn mk_pointer<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
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() };
@ -752,12 +788,7 @@ fn associated_ty_to_ty<'tcx,AC,RS>(this: &AC,
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()));
@ -830,8 +861,7 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
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
@ -842,7 +872,6 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
[].as_slice(),
f.bounds.as_slice());
let fn_decl = ty_of_closure(this,
ast_ty.id,
f.fn_style,
f.onceness,
bounds,
@ -863,7 +892,6 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
f.bounds.as_slice());
let fn_decl = ty_of_closure(this,
ast_ty.id,
f.fn_style,
f.onceness,
bounds,
@ -874,15 +902,8 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
ty::mk_closure(tcx, fn_decl)
}
ast::TyPolyTraitRef(ref data) => {
// FIXME(#18639) this is just a placeholder for code to come
let principal = instantiate_trait_ref(this, rscope, &data.trait_ref, None, None);
let bounds = conv_existential_bounds(this,
rscope,
ast_ty.span,
&[principal.clone()],
&[]);
ty::mk_trait(tcx, (*principal).clone(), bounds)
ast::TyPolyTraitRef(ref bounds) => {
conv_ty_poly_trait_ref(this, rscope, ast_ty.span, bounds.as_slice())
}
ast::TyPath(ref path, ref bounds, id) => {
let a_def = match tcx.def_map.borrow().get(&id) {
@ -910,8 +931,7 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
trait_def_id,
None,
None,
path,
id);
path);
let empty_bounds: &[ast::TyParamBound] = &[];
let ast_bounds = match *bounds {
Some(ref b) => b.as_slice(),
@ -922,12 +942,12 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
ast_ty.span,
&[Rc::new(result.clone())],
ast_bounds);
ty::mk_trait(tcx,
result,
bounds)
let result_ty = ty::mk_trait(tcx, result, bounds);
debug!("ast_ty_to_ty: result_ty={}", result_ty.repr(this.tcx()));
result_ty
}
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);
@ -1056,7 +1076,6 @@ struct SelfInfo<'a> {
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,
@ -1069,7 +1088,6 @@ pub fn ty_of_method<'tcx, AC: AstConv<'tcx>>(
});
let (bare_fn_ty, optional_explicit_self_category) =
ty_of_method_or_bare_fn(this,
id,
fn_style,
abi,
self_info,
@ -1077,17 +1095,14 @@ pub fn ty_of_method<'tcx, AC: AstConv<'tcx>>(
(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>,
@ -1098,7 +1113,7 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>(
// 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
@ -1114,7 +1129,9 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>(
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)
}
@ -1205,7 +1222,6 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>(
fn_style: fn_style,
abi: abi,
sig: ty::FnSig {
binder_id: id,
inputs: self_and_input_tys,
output: output_ty,
variadic: decl.variadic
@ -1218,8 +1234,9 @@ fn determine_explicit_self_category<'tcx, AC: AstConv<'tcx>,
this: &AC,
rscope: &RS,
self_info: &SelfInfo)
-> ty::ExplicitSelfCategory {
match self_info.explicit_self.node {
-> ty::ExplicitSelfCategory
{
return match self_info.explicit_self.node {
ast::SelfStatic => ty::StaticExplicitSelfCategory,
ast::SelfValue(_) => ty::ByValueExplicitSelfCategory,
ast::SelfRegion(ref lifetime, mutability, _) => {
@ -1233,64 +1250,69 @@ fn determine_explicit_self_category<'tcx, AC: AstConv<'tcx>,
ast::SelfExplicit(ref ast_type, _) => {
let explicit_type = ast_ty_to_ty(this, rscope, &**ast_type);
{
let inference_context = infer::new_infer_ctxt(this.tcx());
let expected_self = self_info.untransformed_self_ty;
let actual_self = explicit_type;
let result = infer::mk_eqty(
&inference_context,
false,
infer::Misc(self_info.explicit_self.span),
expected_self,
actual_self);
match result {
Ok(_) => {
inference_context.resolve_regions_and_report_errors();
return ty::ByValueExplicitSelfCategory
}
Err(_) => {}
}
}
// We wish to (for now) categorize an explicit self
// declaration like `self: SomeType` into either `self`,
// `&self`, `&mut self`, or `Box<self>`. We do this here
// by some simple pattern matching. A more precise check
// is done later in `check_method_self_type()`.
//
// Examples:
//
// ```
// impl Foo for &T {
// // Legal declarations:
// fn method1(self: &&T); // ByReferenceExplicitSelfCategory
// fn method2(self: &T); // ByValueExplicitSelfCategory
// fn method3(self: Box<&T>); // ByBoxExplicitSelfCategory
//
// // Invalid cases will be caught later by `check_method_self_type`:
// fn method_err1(self: &mut T); // ByReferenceExplicitSelfCategory
// }
// ```
//
// To do the check we just count the number of "modifiers"
// on each type and compare them. If they are the same or
// the impl has more, we call it "by value". Otherwise, we
// look at the outermost modifier on the method decl and
// call it by-ref, by-box as appropriate. For method1, for
// example, the impl type has one modifier, but the method
// type has two, so we end up with
// ByReferenceExplicitSelfCategory.
match ty::get(explicit_type).sty {
ty::ty_rptr(region, tm) => {
typeck::require_same_types(
this.tcx(),
None,
false,
self_info.explicit_self.span,
self_info.untransformed_self_ty,
tm.ty,
|| "not a valid type for `self`".to_string());
return ty::ByReferenceExplicitSelfCategory(region,
tm.mutbl)
}
ty::ty_uniq(typ) => {
typeck::require_same_types(
this.tcx(),
None,
false,
self_info.explicit_self.span,
self_info.untransformed_self_ty,
typ,
|| "not a valid type for `self`".to_string());
return ty::ByBoxExplicitSelfCategory
}
_ => {
this.tcx()
.sess
.span_err(self_info.explicit_self.span,
"not a valid type for `self`");
return ty::ByValueExplicitSelfCategory
let impl_modifiers = count_modifiers(self_info.untransformed_self_ty);
let method_modifiers = count_modifiers(explicit_type);
debug!("determine_explicit_self_category(self_info.untransformed_self_ty={} \
explicit_type={} \
modifiers=({},{})",
self_info.untransformed_self_ty.repr(this.tcx()),
explicit_type.repr(this.tcx()),
impl_modifiers,
method_modifiers);
if impl_modifiers >= method_modifiers {
ty::ByValueExplicitSelfCategory
} else {
match ty::get(explicit_type).sty {
ty::ty_rptr(r, mt) => ty::ByReferenceExplicitSelfCategory(r, mt.mutbl),
ty::ty_uniq(_) => ty::ByBoxExplicitSelfCategory,
_ => ty::ByValueExplicitSelfCategory,
}
}
}
};
fn count_modifiers(ty: ty::t) -> uint {
match ty::get(ty).sty {
ty::ty_rptr(_, mt) => count_modifiers(mt.ty) + 1,
ty::ty_uniq(t) => count_modifiers(t) + 1,
_ => 0,
}
}
}
pub fn ty_of_closure<'tcx, AC: AstConv<'tcx>>(
this: &AC,
id: ast::NodeId,
fn_style: ast::FnStyle,
onceness: ast::Onceness,
bounds: ty::ExistentialBounds,
@ -1300,13 +1322,14 @@ pub fn ty_of_closure<'tcx, AC: AstConv<'tcx>>(
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
@ -1331,14 +1354,16 @@ pub fn ty_of_closure<'tcx, AC: AstConv<'tcx>>(
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}
}
@ -1365,15 +1390,66 @@ pub fn conv_existential_bounds<'tcx, AC: AstConv<'tcx>, RS:RegionScope>(
let ast_bound_refs: Vec<&ast::TyParamBound> =
ast_bounds.iter().collect();
let partitioned_bounds =
partition_bounds(this.tcx(), span, ast_bound_refs.as_slice());
conv_existential_bounds_from_partitioned_bounds(
this, rscope, span, main_trait_refs, partitioned_bounds)
}
fn conv_ty_poly_trait_ref<'tcx, AC, RS>(
this: &AC,
rscope: &RS,
span: Span,
ast_bounds: &[ast::TyParamBound])
-> ty::t
where AC: AstConv<'tcx>, RS:RegionScope
{
let ast_bounds: Vec<&ast::TyParamBound> = ast_bounds.iter().collect();
let mut partitioned_bounds = partition_bounds(this.tcx(), span, ast_bounds[]);
let main_trait_bound = match partitioned_bounds.trait_bounds.remove(0) {
Some(trait_bound) => {
Some(instantiate_poly_trait_ref(this, rscope, trait_bound, None, None))
}
None => {
this.tcx().sess.span_err(
span,
"at least one non-builtin trait is required for an object type");
None
}
};
let bounds = conv_existential_bounds_from_partitioned_bounds(this,
rscope,
span,
main_trait_bound.as_slice(),
partitioned_bounds);
match main_trait_bound {
None => ty::mk_err(),
Some(principal) => ty::mk_trait(this.tcx(), (*principal).clone(), bounds)
}
}
pub fn conv_existential_bounds_from_partitioned_bounds<'tcx, AC, RS>(
this: &AC,
rscope: &RS,
span: Span,
main_trait_refs: &[Rc<ty::TraitRef>],
partitioned_bounds: PartitionedBounds)
-> ty::ExistentialBounds
where AC: AstConv<'tcx>, RS:RegionScope
{
let PartitionedBounds { builtin_bounds,
trait_bounds,
region_bounds } =
partition_bounds(this.tcx(), span, ast_bound_refs.as_slice());
partitioned_bounds;
if !trait_bounds.is_empty() {
let b = &trait_bounds[0];
this.tcx().sess.span_err(
b.path.span,
b.trait_ref.path.span,
format!("only the builtin traits can be used \
as closure or object bounds").as_slice());
}
@ -1505,7 +1581,7 @@ fn compute_region_bound<'tcx, AC: AstConv<'tcx>, RS:RegionScope>(
pub struct PartitionedBounds<'a> {
pub builtin_bounds: ty::BuiltinBounds,
pub trait_bounds: Vec<&'a ast::TraitRef>,
pub trait_bounds: Vec<&'a ast::PolyTraitRef>,
pub region_bounds: Vec<&'a ast::Lifetime>,
}
@ -1527,8 +1603,7 @@ pub fn partition_bounds<'a>(tcx: &ty::ctxt,
for &ast_bound in ast_bounds.iter() {
match *ast_bound {
ast::TraitTyParamBound(ref b) => {
let b = &b.trait_ref; // FIXME
match lookup_def_tcx(tcx, b.path.span, b.ref_id) {
match lookup_def_tcx(tcx, b.trait_ref.path.span, b.trait_ref.ref_id) {
def::DefTrait(trait_did) => {
match trait_def_ids.get(&trait_did) {
// Already seen this trait. We forbid
@ -1536,10 +1611,10 @@ pub fn partition_bounds<'a>(tcx: &ty::ctxt,
// reason).
Some(span) => {
span_err!(
tcx.sess, b.path.span, E0127,
tcx.sess, b.trait_ref.path.span, E0127,
"trait `{}` already appears in the \
list of bounds",
b.path.user_string(tcx));
b.trait_ref.path.user_string(tcx));
tcx.sess.span_note(
*span,
"previous appearance is here");
@ -1550,7 +1625,7 @@ pub fn partition_bounds<'a>(tcx: &ty::ctxt,
None => { }
}
trait_def_ids.insert(trait_did, b.path.span);
trait_def_ids.insert(trait_did, b.trait_ref.path.span);
if ty::try_add_builtin_trait(tcx,
trait_did,

View file

@ -20,6 +20,7 @@ use middle::typeck::{MethodCall, MethodCallee, MethodObject, MethodOrigin,
MethodParam, MethodStatic, MethodTraitObject, MethodTypeParam};
use middle::typeck::infer;
use middle::typeck::infer::InferCtxt;
use middle::ty_fold::HigherRankedFoldable;
use syntax::ast;
use syntax::codemap::Span;
use std::rc::Rc;
@ -32,6 +33,27 @@ struct ConfirmContext<'a, 'tcx:'a> {
self_expr: &'a ast::Expr,
}
struct InstantiatedMethodSig {
/// Function signature of the method being invoked. The 0th
/// argument is the receiver.
method_sig: ty::FnSig,
/// Substitutions for all types/early-bound-regions declared on
/// the method.
all_substs: subst::Substs,
/// Substitution to use when adding obligations from the method
/// bounds. Normally equal to `all_substs` except for object
/// receivers. See FIXME in instantiate_method_sig() for
/// explanation.
method_bounds_substs: subst::Substs,
/// Generic bounds on the method's parameters which must be added
/// as pending obligations.
method_bounds: ty::GenericBounds,
}
pub fn confirm(fcx: &FnCtxt,
span: Span,
self_expr: &ast::Expr,
@ -79,14 +101,16 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
debug!("all_substs={}", all_substs.repr(self.tcx()));
// Create the final signature for the method, replacing late-bound regions.
let method_sig = self.instantiate_method_sig(&pick, &all_substs);
let InstantiatedMethodSig {
method_sig, all_substs, method_bounds_substs, method_bounds
} = self.instantiate_method_sig(&pick, all_substs);
let method_self_ty = method_sig.inputs[0];
// Unify the (adjusted) self type with what the method expects.
self.unify_receivers(self_ty, method_self_ty);
// Add any trait/regions obligations specified on the method's type parameters.
self.add_obligations(&pick, &all_substs);
self.add_obligations(&pick, &method_bounds_substs, &method_bounds);
// Create the final `MethodCallee`.
let fty = ty::mk_bare_fn(self.tcx(), ty::BareFnTy {
@ -176,6 +200,10 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
* where all type and region parameters are instantiated with
* fresh variables. This substitution does not include any
* parameters declared on the method itself.
*
* Note that this substitution may include late-bound regions
* from the impl level. If so, these are instantiated later in
* the `instantiate_method_sig` routine.
*/
match pick.kind {
@ -354,20 +382,34 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
fn instantiate_method_sig(&mut self,
pick: &probe::Pick,
all_substs: &subst::Substs)
-> ty::FnSig
all_substs: subst::Substs)
-> InstantiatedMethodSig
{
let ref bare_fn_ty = pick.method_ty.fty;
let fn_sig = bare_fn_ty.sig.subst(self.tcx(), all_substs);
self.infcx().replace_late_bound_regions_with_fresh_var(fn_sig.binder_id,
self.span,
infer::FnCall,
&fn_sig).0
}
// If this method comes from an impl (as opposed to a trait),
// it may have late-bound regions from the impl that appear in
// the substitutions, method signature, and
// bounds. Instantiate those at this point. (If it comes from
// a trait, this step has no effect, as there are no
// late-bound regions to instantiate.)
//
// The binder level here corresponds to the impl.
let (all_substs, (method_sig, method_generics)) =
self.replace_late_bound_regions_with_fresh_var(
&ty::bind((all_substs,
(pick.method_ty.fty.sig.clone(),
pick.method_ty.generics.clone())))).value;
fn add_obligations(&mut self,
pick: &probe::Pick,
all_substs: &subst::Substs) {
debug!("late-bound lifetimes from impl instantiated, \
all_substs={} method_sig={} method_generics={}",
all_substs.repr(self.tcx()),
method_sig.repr(self.tcx()),
method_generics.repr(self.tcx()));
// Instantiate the bounds on the method with the
// type/early-bound-regions substitutions performed. The only
// late-bound-regions that can appear in bounds are from the
// impl, and those were already instantiated above.
//
// FIXME(DST). Super hack. For a method on a trait object
// `Trait`, the generic signature requires that
// `Self:Trait`. Since, for an object, we bind `Self` to the
@ -381,24 +423,54 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
// obligations. This causes us to generate the obligation
// `err:Trait`, and the error type is considered to implement
// all traits, so we're all good. Hack hack hack.
match pick.kind {
let method_bounds_substs = match pick.kind {
probe::ObjectPick(..) => {
let mut temp_substs = all_substs.clone();
temp_substs.types.get_mut_slice(subst::SelfSpace)[0] = ty::mk_err();
self.fcx.add_obligations_for_parameters(
traits::ObligationCause::misc(self.span),
&temp_substs,
&pick.method_ty.generics);
temp_substs
}
_ => {
self.fcx.add_obligations_for_parameters(
traits::ObligationCause::misc(self.span),
all_substs,
&pick.method_ty.generics);
all_substs.clone()
}
};
let method_bounds =
method_generics.to_bounds(self.tcx(), &method_bounds_substs);
debug!("method_bounds after subst = {}",
method_bounds.repr(self.tcx()));
// Substitute the type/early-bound-regions into the method
// signature. In addition, the method signature may bind
// late-bound regions, so instantiate those.
let method_sig = method_sig.subst(self.tcx(), &all_substs);
let method_sig = self.replace_late_bound_regions_with_fresh_var(&method_sig);
debug!("late-bound lifetimes from method instantiated, method_sig={}",
method_sig.repr(self.tcx()));
InstantiatedMethodSig {
method_sig: method_sig,
all_substs: all_substs,
method_bounds_substs: method_bounds_substs,
method_bounds: method_bounds,
}
}
fn add_obligations(&mut self,
pick: &probe::Pick,
method_bounds_substs: &subst::Substs,
method_bounds: &ty::GenericBounds) {
debug!("add_obligations: pick={} method_bounds_substs={} method_bounds={}",
pick.repr(self.tcx()),
method_bounds_substs.repr(self.tcx()),
method_bounds.repr(self.tcx()));
self.fcx.add_obligations_for_parameters(
traits::ObligationCause::misc(self.span),
method_bounds_substs,
method_bounds);
}
///////////////////////////////////////////////////////////////////////////
// RECONCILIATION
@ -591,6 +663,13 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
source_trait_ref.repr(self.tcx()),
target_trait_def_id.repr(self.tcx()))[]);
}
fn replace_late_bound_regions_with_fresh_var<T>(&self, value: &T) -> T
where T : HigherRankedFoldable
{
self.infcx().replace_late_bound_regions_with_fresh_var(
self.span, infer::FnCall, value).0
}
}
fn wrap_autoref(mut deref: ty::AutoDerefRef,

View file

@ -200,10 +200,12 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>,
// Substitute the trait parameters into the method type and
// instantiate late-bound regions to get the actual method type.
//
// Note that as the method comes from a trait, it can only have
// late-bound regions from the fn itself, not the impl.
let ref bare_fn_ty = method_ty.fty;
let fn_sig = bare_fn_ty.sig.subst(tcx, &trait_ref.substs);
let fn_sig = fcx.infcx().replace_late_bound_regions_with_fresh_var(fn_sig.binder_id,
span,
let fn_sig = fcx.infcx().replace_late_bound_regions_with_fresh_var(span,
infer::FnCall,
&fn_sig).0;
let transformed_self_ty = fn_sig.inputs[0];
@ -222,10 +224,15 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &'a FnCtxt<'a, 'tcx>,
// so this also effectively registers `obligation` as well. (We
// used to register `obligation` explicitly, but that resulted in
// double error messages being reported.)
//
// Note that as the method comes from a trait, it should not have
// any late-bound regions appearing in its bounds.
let method_bounds = method_ty.generics.to_bounds(fcx.tcx(), &trait_ref.substs);
assert!(!method_bounds.has_escaping_regions());
fcx.add_obligations_for_parameters(
traits::ObligationCause::misc(span),
&trait_ref.substs,
&method_ty.generics);
&method_bounds);
// FIXME(#18653) -- Try to resolve obligations, giving us more
// typing information, which can sometimes be needed to avoid

View file

@ -17,6 +17,7 @@ use middle::subst;
use middle::subst::Subst;
use middle::traits;
use middle::ty;
use middle::ty_fold::HigherRankedFoldable;
use middle::typeck::check;
use middle::typeck::check::{FnCtxt, NoPreference};
use middle::typeck::{MethodObject};
@ -257,29 +258,28 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
ty::populate_implementations_for_type_if_necessary(self.tcx(), def_id);
for impl_infos in self.tcx().inherent_impls.borrow().get(&def_id).iter() {
for &impl_did in impl_infos.iter() {
self.assemble_inherent_impl_probe(impl_did);
for &impl_def_id in impl_infos.iter() {
self.assemble_inherent_impl_probe(impl_def_id);
}
}
}
fn assemble_inherent_impl_probe(&mut self, impl_did: ast::DefId) {
if !self.impl_dups.insert(impl_did) {
fn assemble_inherent_impl_probe(&mut self, impl_def_id: ast::DefId) {
if !self.impl_dups.insert(impl_def_id) {
return; // already visited
}
let method = match impl_method(self.tcx(), impl_did, self.method_name) {
let method = match impl_method(self.tcx(), impl_def_id, self.method_name) {
Some(m) => m,
None => { return; } // No method with correct name on this impl
};
if !self.has_applicable_self(&*method) {
// No receiver declared. Not a candidate.
return self.record_static_candidate(ImplSource(impl_did));
return self.record_static_candidate(ImplSource(impl_def_id));
}
let impl_pty = check::impl_self_ty(self.fcx, self.span, impl_did);
let impl_substs = impl_pty.substs;
let impl_substs = self.impl_substs(impl_def_id);
// Determine the receiver type that the method itself expects.
let xform_self_ty =
@ -288,7 +288,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
self.inherent_candidates.push(Candidate {
xform_self_ty: xform_self_ty,
method_ty: method,
kind: InherentImplCandidate(impl_did, impl_substs)
kind: InherentImplCandidate(impl_def_id, impl_substs)
});
}
@ -496,8 +496,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
continue;
}
let impl_pty = check::impl_self_ty(self.fcx, self.span, impl_def_id);
let impl_substs = impl_pty.substs;
let impl_substs = self.impl_substs(impl_def_id);
debug!("impl_substs={}", impl_substs.repr(self.tcx()));
@ -675,7 +674,9 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
mk_autoref_ty: |ast::Mutability, ty::Region| -> ty::t)
-> Option<PickResult>
{
let region = self.infcx().next_region_var(infer::Autoref(self.span));
// In general, during probing we erase regions. See
// `impl_self_ty()` for an explanation.
let region = ty::ReStatic;
// Search through mutabilities in order to find one where pick works:
[ast::MutImmutable, ast::MutMutable]
@ -746,6 +747,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
probe.repr(self.tcx()));
self.infcx().probe(|| {
// First check that the self type can be related.
match self.make_sub_ty(self_ty, probe.xform_self_ty) {
Ok(()) => { }
Err(_) => {
@ -754,23 +756,34 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
}
}
// If so, impls may carry other conditions (e.g., where
// clauses) that must be considered. Make sure that those
// match as well (or at least may match, sometimes we
// don't have enough information to fully evaluate).
match probe.kind {
InherentImplCandidate(impl_def_id, ref substs) |
ExtensionImplCandidate(impl_def_id, _, ref substs, _) => {
// Check whether the impl imposes obligations we have to worry about.
let impl_generics = ty::lookup_item_type(self.tcx(), impl_def_id).generics;
let impl_bounds = impl_generics.to_bounds(self.tcx(), substs);
// Erase any late-bound regions bound in the impl
// which appear in the bounds.
let impl_bounds = self.erase_late_bound_regions(&ty::bind(impl_bounds)).value;
// Convert the bounds into obligations.
let obligations =
traits::impl_obligations(
traits::obligations_for_generics(
self.tcx(),
traits::ObligationCause::misc(self.span),
impl_def_id,
substs);
&impl_bounds,
&substs.types);
debug!("impl_obligations={}", obligations.repr(self.tcx()));
// Evaluate those obligations to see if they might possibly hold.
let mut selcx = traits::SelectionContext::new(self.infcx(),
&self.fcx.inh.param_env,
self.fcx);
obligations.all(|o| selcx.evaluate_obligation(o))
}
@ -883,20 +896,78 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
self.infcx().next_ty_vars(
method.generics.types.len(subst::FnSpace));
// In general, during probe we erase regions. See
// `impl_self_ty()` for an explanation.
let method_regions =
self.infcx().region_vars_for_defs(
self.span,
method.generics.regions.get_slice(subst::FnSpace));
method.generics.regions.get_slice(subst::FnSpace)
.iter()
.map(|_| ty::ReStatic)
.collect();
placeholder = (*substs).clone().with_method(method_types, method_regions);
substs = &placeholder;
}
// Replace early-bound regions and types.
let xform_self_ty = method.fty.sig.inputs[0].subst(self.tcx(), substs);
self.infcx().replace_late_bound_regions_with_fresh_var(method.fty.sig.binder_id,
self.span,
infer::FnCall,
&xform_self_ty).0
// Replace late-bound regions bound in the impl or
// where-clause (2 levels of binding).
let xform_self_ty =
self.erase_late_bound_regions(&ty::bind(ty::bind(xform_self_ty))).value.value;
// Replace late-bound regions bound in the method (1 level of binding).
self.erase_late_bound_regions(&ty::bind(xform_self_ty)).value
}
fn impl_substs(&self,
impl_def_id: ast::DefId)
-> subst::Substs
{
let impl_pty = ty::lookup_item_type(self.tcx(), impl_def_id);
let type_vars =
impl_pty.generics.types.map(
|_| self.infcx().next_ty_var());
let region_placeholders =
impl_pty.generics.regions.map(
|_| ty::ReStatic); // see erase_late_bound_regions() for an expl of why 'static
subst::Substs::new(type_vars, region_placeholders)
}
fn erase_late_bound_regions<T>(&self, value: &T) -> T
where T : HigherRankedFoldable
{
/*!
* Replace late-bound-regions bound by `value` with `'static`
* using `ty::erase_late_bound_regions`.
*
* This is only a reasonable thing to do during the *probe*
* phase, not the *confirm* phase, of method matching. It is
* reasonable during the probe phase because we don't consider
* region relationships at all. Therefore, we can just replace
* all the region variables with 'static rather than creating
* fresh region variables. This is nice for two reasons:
*
* 1. Because the numbers of the region variables would
* otherwise be fairly unique to this particular method
* call, it winds up creating fewer types overall, which
* helps for memory usage. (Admittedly, this is a rather
* small effect, though measureable.)
*
* 2. It makes it easier to deal with higher-ranked trait
* bounds, because we can replace any late-bound regions
* with 'static. Otherwise, if we were going to replace
* late-bound regions with actual region variables as is
* proper, we'd have to ensure that the same region got
* replaced with the same variable, which requires a bit
* more coordination and/or tracking the substitution and
* so forth.
*/
ty::erase_late_bound_regions(self.tcx(), value)
}
}

View file

@ -97,12 +97,12 @@ use middle::ty::{FnSig, VariantInfo};
use middle::ty::{Polytype};
use middle::ty::{Disr, ParamTy, ParameterEnvironment};
use middle::ty;
use middle::ty::{replace_late_bound_regions, liberate_late_bound_regions};
use middle::ty_fold::TypeFolder;
use middle::typeck::astconv::AstConv;
use middle::typeck::astconv::{ast_region_to_region, ast_ty_to_ty};
use middle::typeck::astconv;
use middle::typeck::check::_match::pat_ctxt;
use middle::typeck::check::regionmanip::replace_late_bound_regions;
use middle::typeck::CrateCtxt;
use middle::typeck::infer;
use middle::typeck::rscope::RegionScope;
@ -528,9 +528,7 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
// 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;
@ -740,6 +738,11 @@ fn check_method_body(ccx: &CrateCtxt,
let param_env = ParameterEnvironment::for_item(ccx.tcx, method.id);
let fty = ty::node_id_to_type(ccx.tcx, method.id);
debug!("fty (raw): {}", fty.repr(ccx.tcx));
let body_id = method.pe_body().id;
let fty = liberate_late_bound_regions(ccx.tcx, body_id, &ty::bind(fty)).value;
debug!("fty (liberated): {}", fty.repr(ccx.tcx));
check_bare_fn(ccx,
&*method.pe_fn_decl(),
@ -784,7 +787,7 @@ fn check_impl_items_against_trait(ccx: &CrateCtxt,
impl_method.span,
impl_method.pe_body().id,
&**trait_method_ty,
&impl_trait_ref.substs);
impl_trait_ref);
}
_ => {
// This is span_bug as it should have already been
@ -929,11 +932,36 @@ fn compare_impl_method(tcx: &ty::ctxt,
impl_m_span: Span,
impl_m_body_id: ast::NodeId,
trait_m: &ty::Method,
trait_to_impl_substs: &subst::Substs) {
debug!("compare_impl_method(trait_to_impl_substs={})",
trait_to_impl_substs.repr(tcx));
impl_trait_ref: &ty::TraitRef) {
debug!("compare_impl_method(impl_trait_ref={})",
impl_trait_ref.repr(tcx));
// The impl's trait ref may bind late-bound regions from the impl.
// Liberate them and assign them the scope of the method body.
//
// An example would be:
//
// impl<'a> Foo<&'a T> for &'a U { ... }
//
// Here, the region parameter `'a` is late-bound, so the
// trait reference associated with the impl will be
//
// for<'a> Foo<&'a T>
//
// liberating will convert this into:
//
// Foo<&'A T>
//
// where `'A` is the `ReFree` version of `'a`.
let impl_trait_ref = liberate_late_bound_regions(tcx, impl_m_body_id, impl_trait_ref);
debug!("impl_trait_ref (liberated) = {}",
impl_trait_ref.repr(tcx));
let infcx = infer::new_infer_ctxt(tcx);
let trait_to_impl_substs = &impl_trait_ref.substs;
// Try to give more informative error messages about self typing
// mismatches. Note that any mismatch will also be detected
// below, where we construct a canonical function type that
@ -997,22 +1025,23 @@ fn compare_impl_method(tcx: &ty::ctxt,
// This code is best explained by example. Consider a trait:
//
// trait Trait<T> {
// fn method<'a,M>(t: T, m: &'a M) -> Self;
// trait Trait<'t,T> {
// fn method<'a,M>(t: &'t T, m: &'a M) -> Self;
// }
//
// And an impl:
//
// impl<'i, U> Trait<&'i U> for Foo {
// fn method<'b,N>(t: &'i U, m: &'b N) -> Foo;
// impl<'i, 'j, U> Trait<'j, &'i U> for Foo {
// fn method<'b,N>(t: &'j &'i U, m: &'b N) -> Foo;
// }
//
// We wish to decide if those two method types are compatible.
//
// We start out with trait_to_impl_substs, that maps the trait type
// parameters to impl type parameters:
// We start out with trait_to_impl_substs, that maps the trait
// type parameters to impl type parameters. This is taken from the
// impl trait reference:
//
// trait_to_impl_substs = {T => &'i U, Self => Foo}
// trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo}
//
// We create a mapping `dummy_substs` that maps from the impl type
// parameters to fresh types and regions. For type parameters,
@ -1067,6 +1096,7 @@ fn compare_impl_method(tcx: &ty::ctxt,
if !check_region_bounds_on_impl_method(tcx,
impl_m_span,
impl_m,
impl_m_body_id,
&trait_m.generics,
&impl_m.generics,
&trait_to_skol_substs,
@ -1074,15 +1104,50 @@ fn compare_impl_method(tcx: &ty::ctxt,
return;
}
// Check bounds.
let it = trait_m.generics.types.get_slice(subst::FnSpace).iter()
.zip(impl_m.generics.types.get_slice(subst::FnSpace).iter());
for (i, (trait_param_def, impl_param_def)) in it.enumerate() {
// Check bounds. Note that the bounds from the impl may reference
// late-bound regions declared on the impl, so liberate those.
// This requires two artificial binding scopes -- one for the impl,
// and one for the method.
//
// An example would be:
//
// trait Foo<T> { fn method<U:Bound<T>>() { ... } }
//
// impl<'a> Foo<&'a T> for &'a U {
// fn method<U:Bound<&'a T>>() { ... }
// }
//
// Here, the region parameter `'a` is late-bound, so in the bound
// `Bound<&'a T>`, the lifetime `'a` will be late-bound with a
// depth of 3 (it is nested within 3 binders: the impl, method,
// and trait-ref itself). So when we do the liberation, we have
// two introduce two `ty::bind` scopes, one for the impl and one
// the method.
//
// The only late-bounded regions that can possibly appear here are
// from the impl, not the method. This is because region
// parameters declared on the method which appear in a type bound
// would be early bound. On the trait side, there can be no
// late-bound lifetimes because trait definitions do not introduce
// a late region binder.
let trait_bounds =
trait_m.generics.types.get_slice(subst::FnSpace).iter()
.map(|trait_param_def| &trait_param_def.bounds);
let impl_bounds =
impl_m.generics.types.get_slice(subst::FnSpace).iter()
.map(|impl_param_def|
liberate_late_bound_regions(
tcx,
impl_m_body_id,
&ty::bind(ty::bind(impl_param_def.bounds.clone()))).value.value);
for (i, (trait_param_bounds, impl_param_bounds)) in
trait_bounds.zip(impl_bounds).enumerate()
{
// Check that the impl does not require any builtin-bounds
// that the trait does not guarantee:
let extra_bounds =
impl_param_def.bounds.builtin_bounds -
trait_param_def.bounds.builtin_bounds;
impl_param_bounds.builtin_bounds -
trait_param_bounds.builtin_bounds;
if !extra_bounds.is_empty() {
span_err!(tcx.sess, impl_m_span, E0051,
"in method `{}`, type parameter {} requires `{}`, \
@ -1099,31 +1164,32 @@ fn compare_impl_method(tcx: &ty::ctxt,
//
// FIXME(pcwalton): We could be laxer here regarding sub- and super-
// traits, but I doubt that'll be wanted often, so meh.
for impl_trait_bound in impl_param_def.bounds.trait_bounds.iter() {
for impl_trait_bound in impl_param_bounds.trait_bounds.iter() {
debug!("compare_impl_method(): impl-trait-bound subst");
let impl_trait_bound =
impl_trait_bound.subst(tcx, &impl_to_skol_substs);
let mut ok = false;
for trait_bound in trait_param_def.bounds.trait_bounds.iter() {
debug!("compare_impl_method(): trait-bound subst");
let trait_bound =
trait_bound.subst(tcx, &trait_to_skol_substs);
let infcx = infer::new_infer_ctxt(tcx);
match infer::mk_sub_trait_refs(&infcx,
true,
infer::Misc(impl_m_span),
trait_bound,
impl_trait_bound.clone()) {
Ok(_) => {
ok = true;
break
}
Err(_) => continue,
}
}
// There may be late-bound regions from the impl in the
// impl's bound, so "liberate" those. Note that the
// trait_to_skol_substs is derived from the impl's
// trait-ref, and the late-bound regions appearing there
// have already been liberated, so the result should match
// up.
if !ok {
let found_match_in_trait =
trait_param_bounds.trait_bounds.iter().any(|trait_bound| {
debug!("compare_impl_method(): trait-bound subst");
let trait_bound =
trait_bound.subst(tcx, &trait_to_skol_substs);
let infcx = infer::new_infer_ctxt(tcx);
infer::mk_sub_trait_refs(&infcx,
true,
infer::Misc(impl_m_span),
trait_bound,
impl_trait_bound.clone()).is_ok()
});
if !found_match_in_trait {
span_err!(tcx.sess, impl_m_span, E0052,
"in method `{}`, type parameter {} requires bound `{}`, which is not \
required by the corresponding type parameter in the trait declaration",
@ -1134,9 +1200,11 @@ fn compare_impl_method(tcx: &ty::ctxt,
}
}
// Compute skolemized form of impl and trait method tys.
// Compute skolemized form of impl and trait method tys. Note
// that we must liberate the late-bound regions from the impl.
let impl_fty = ty::mk_bare_fn(tcx, impl_m.fty.clone());
let impl_fty = impl_fty.subst(tcx, &impl_to_skol_substs);
let impl_fty = liberate_late_bound_regions(tcx, impl_m_body_id, &ty::bind(impl_fty)).value;
let trait_fty = ty::mk_bare_fn(tcx, trait_m.fty.clone());
let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs);
@ -1171,6 +1239,7 @@ fn compare_impl_method(tcx: &ty::ctxt,
fn check_region_bounds_on_impl_method(tcx: &ty::ctxt,
span: Span,
impl_m: &ty::Method,
impl_m_body_id: ast::NodeId,
trait_generics: &ty::Generics,
impl_generics: &ty::Generics,
trait_to_skol_substs: &Substs,
@ -1216,9 +1285,13 @@ fn compare_impl_method(tcx: &ty::ctxt,
debug!("check_region_bounds_on_impl_method: \
trait_generics={} \
impl_generics={}",
impl_generics={} \
trait_to_skol_substs={} \
impl_to_skol_substs={}",
trait_generics.repr(tcx),
impl_generics.repr(tcx));
impl_generics.repr(tcx),
trait_to_skol_substs.repr(tcx),
impl_to_skol_substs.repr(tcx));
// Must have same number of early-bound lifetime parameters.
// Unfortunately, if the user screws up the bounds, then this
@ -1249,6 +1322,18 @@ fn compare_impl_method(tcx: &ty::ctxt,
let impl_bounds =
impl_param.bounds.subst(tcx, impl_to_skol_substs);
// The bounds may reference late-bound regions from the
// impl declaration. In that case, we want to replace
// those with the liberated variety so as to match the
// versions appearing in the `trait_to_skol_substs`.
// There are two-levels of binder to be aware of: the
// impl, and the method.
let impl_bounds =
ty::liberate_late_bound_regions(
tcx,
impl_m_body_id,
&ty::bind(ty::bind(impl_bounds))).value.value;
debug!("check_region_bounds_on_impl_method: \
trait_param={} \
impl_param={} \
@ -1603,15 +1688,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
pub fn write_ty_substs(&self,
node_id: ast::NodeId,
ty: ty::t,
substs: ty::ItemSubsts) {
let ty = ty.subst(self.tcx(), &substs.substs);
self.write_ty(node_id, ty);
self.write_substs(node_id, substs);
}
pub fn write_autoderef_adjustment(&self,
node_id: ast::NodeId,
span: Span,
@ -1709,17 +1785,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
pub fn instantiate_item_type(&self,
span: Span,
def_id: ast::DefId)
-> TypeAndSubsts
pub fn instantiate_type(&self,
span: Span,
def_id: ast::DefId)
-> TypeAndSubsts
{
/*!
* Returns the type of `def_id` with all generics replaced by
* by fresh type/region variables. Also returns the
* substitution from the type parameters on `def_id` to the
* fresh variables. Registers any trait obligations specified
* fresh variables. Registers any trait obligations specified
* on `def_id` at the same time.
*
* Note that function is only intended to be used with types
* (notably, not impls). This is because it doesn't do any
* instantiation of late-bound regions.
*/
let polytype =
@ -1728,12 +1808,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.infcx().fresh_substs_for_generics(
span,
&polytype.generics);
let bounds =
polytype.generics.to_bounds(self.tcx(), &substs);
self.add_obligations_for_parameters(
traits::ObligationCause::new(
span,
traits::ItemObligation(def_id)),
&substs,
&polytype.generics);
&bounds);
let monotype =
polytype.ty.subst(self.tcx(), &substs);
@ -1958,8 +2040,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut region_obligations = self.inh.region_obligations.borrow_mut();
let region_obligation = RegionObligation { sub_region: r,
sup_type: ty,
origin: origin };
sup_type: ty,
origin: origin };
match region_obligations.entry(self.body_id) {
Vacant(entry) => { entry.set(vec![region_obligation]); },
@ -1970,12 +2052,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn add_obligations_for_parameters(&self,
cause: traits::ObligationCause,
substs: &Substs,
generics: &ty::Generics)
generic_bounds: &ty::GenericBounds)
{
/*!
* Given a set of generic parameter definitions (`generics`)
* and the values provided for each of them (`substs`),
* creates and registers suitable region obligations.
* Given a fully substituted set of bounds (`generic_bounds`),
* and the values with which each type/region parameter was
* instantiated (`substs`), creates and registers suitable
* trait/region obligations.
*
* For example, if there is a function:
*
@ -1991,60 +2074,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
* locally.
*/
debug!("add_obligations_for_parameters(substs={}, generics={})",
substs.repr(self.tcx()),
generics.repr(self.tcx()));
assert!(!generic_bounds.has_escaping_regions());
self.add_trait_obligations_for_generics(cause, substs, generics);
self.add_region_obligations_for_generics(cause, substs, generics);
debug!("add_obligations_for_parameters(substs={}, generic_bounds={})",
substs.repr(self.tcx()),
generic_bounds.repr(self.tcx()));
self.add_trait_obligations_for_generics(cause, substs, generic_bounds);
self.add_region_obligations_for_generics(cause, substs, generic_bounds);
}
fn add_trait_obligations_for_generics(&self,
cause: traits::ObligationCause,
substs: &Substs,
generics: &ty::Generics) {
generic_bounds: &ty::GenericBounds) {
assert!(!generic_bounds.has_escaping_regions());
assert!(!substs.has_regions_escaping_depth(0));
let obligations =
traits::obligations_for_generics(self.tcx(),
cause,
generics,
substs);
generic_bounds,
&substs.types);
obligations.map_move(|o| self.register_obligation(o));
}
fn add_region_obligations_for_generics(&self,
cause: traits::ObligationCause,
substs: &Substs,
generics: &ty::Generics)
generic_bounds: &ty::GenericBounds)
{
assert_eq!(generics.types.iter().len(),
substs.types.iter().len());
for (type_def, &type_param) in
generics.types.iter().zip(
assert!(!generic_bounds.has_escaping_regions());
assert_eq!(generic_bounds.types.iter().len(), substs.types.iter().len());
for (type_bounds, &type_param) in
generic_bounds.types.iter().zip(
substs.types.iter())
{
let param_ty = ty::ParamTy { space: type_def.space,
idx: type_def.index,
def_id: type_def.def_id };
let bounds = type_def.bounds.subst(self.tcx(), substs);
self.add_region_obligations_for_type_parameter(
cause.span, param_ty, &bounds, type_param);
cause.span, type_bounds, type_param);
}
assert_eq!(generics.regions.iter().len(),
assert_eq!(generic_bounds.regions.iter().len(),
substs.regions().iter().len());
for (region_def, &region_param) in
generics.regions.iter().zip(
for (region_bounds, &region_param) in
generic_bounds.regions.iter().zip(
substs.regions().iter())
{
let bounds = region_def.bounds.subst(self.tcx(), substs);
self.add_region_obligations_for_region_parameter(
cause.span, bounds.as_slice(), region_param);
cause.span, region_bounds.as_slice(), region_param);
}
}
fn add_region_obligations_for_type_parameter(&self,
span: Span,
param_ty: ty::ParamTy,
param_bound: &ty::ParamBounds,
ty: ty::t)
{
@ -2056,7 +2139,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
param_bound.builtin_bounds,
param_bound.trait_bounds.as_slice());
for &r in region_bounds.iter() {
let origin = infer::RelateParamBound(span, param_ty, ty);
let origin = infer::RelateParamBound(span, ty);
self.register_region_obligation(origin, ty, r);
}
}
@ -2181,11 +2264,11 @@ fn try_overloaded_call<'a>(fcx: &FnCtxt,
fcx.inh.method_map.borrow_mut().insert(method_call, method_callee);
write_call(fcx, call_expression, output_type);
if !fcx.tcx().sess.features.borrow().overloaded_calls {
if !fcx.tcx().sess.features.borrow().unboxed_closures {
span_err!(fcx.tcx().sess, call_expression.span, E0056,
"overloaded calls are experimental");
span_help!(fcx.tcx().sess, call_expression.span,
"add `#![feature(overloaded_calls)]` to \
"add `#![feature(unboxed_closures)]` to \
the crate attributes to enable");
}
@ -3031,7 +3114,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
// 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
@ -3051,11 +3133,9 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
// 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,
@ -3437,7 +3517,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
body: &ast::Block) {
let mut fn_ty = astconv::ty_of_closure(
fcx,
expr.id,
ast::NormalFn,
ast::Many,
@ -3508,6 +3587,10 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
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.
@ -3517,10 +3600,11 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
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:
@ -3561,7 +3645,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
// construct the function type
let fn_ty = astconv::ty_of_closure(fcx,
expr.id,
ast::NormalFn,
expected_onceness,
expected_bounds,
@ -3818,7 +3901,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
let TypeAndSubsts {
ty: mut struct_type,
substs: struct_substs
} = fcx.instantiate_item_type(span, class_id);
} = fcx.instantiate_type(span, class_id);
// Look up and check the fields.
let class_fields = ty::lookup_struct_fields(tcx, class_id);
@ -3860,7 +3943,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
let TypeAndSubsts {
ty: enum_type,
substs: substitutions
} = fcx.instantiate_item_type(span, enum_id);
} = fcx.instantiate_type(span, enum_id);
// Look up and check the enum variant fields.
let variant_fields = ty::lookup_struct_fields(tcx, variant_id);
@ -4454,8 +4537,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
let type_and_substs = astconv::ast_path_to_ty_relaxed(fcx,
fcx.infcx(),
struct_id,
path,
expr.id);
path);
match fcx.mk_subty(false,
infer::Misc(path.span),
actual_structure_type,
@ -5339,14 +5421,39 @@ pub fn instantiate_path(fcx: &FnCtxt,
assert_eq!(substs.regions().len(space), region_defs.len(space));
}
// The things we are substituting into the type should not contain
// escaping late-bound regions.
assert!(!substs.has_regions_escaping_depth(0));
// In the case of static items taken from impls, there may be
// late-bound regions associated with the impl (not declared on
// the fn itself). Those should be replaced with fresh variables
// now. These can appear either on the type being referenced, or
// on the associated bounds.
let bounds = polytype.generics.to_bounds(fcx.tcx(), &substs);
let (ty_late_bound, bounds) =
fcx.infcx().replace_late_bound_regions_with_fresh_var(
span,
infer::FnCall,
&ty::bind((polytype.ty, bounds))).0.value;
debug!("after late-bounds have been replaced: ty_late_bound={}", ty_late_bound.repr(fcx.tcx()));
debug!("after late-bounds have been replaced: bounds={}", bounds.repr(fcx.tcx()));
fcx.add_obligations_for_parameters(
traits::ObligationCause::new(span, traits::ItemObligation(def.def_id())),
&substs,
&polytype.generics);
&bounds);
fcx.write_ty_substs(node_id, polytype.ty, ty::ItemSubsts {
substs: substs,
});
// Substitute the values for the type parameters into the type of
// the referenced item.
let ty_substituted = ty_late_bound.subst(fcx.tcx(), &substs);
debug!("ty_substituted: ty_substituted={}", ty_substituted.repr(fcx.tcx()));
fcx.write_ty(node_id, ty_substituted);
fcx.write_substs(node_id, ty::ItemSubsts { substs: substs });
return;
fn report_error_if_segment_contains_type_parameters(
fcx: &FnCtxt,
@ -5739,7 +5846,8 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
"move_val_init" => {
(1u,
vec!(
ty::mk_mut_rptr(tcx, ty::ReLateBound(it.id, ty::BrAnon(0)), param(ccx, 0)),
ty::mk_mut_rptr(tcx, ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrAnon(0)),
param(ccx, 0)),
param(ccx, 0u)
),
ty::mk_nil(tcx))
@ -5943,7 +6051,6 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
fn_style: ast::UnsafeFn,
abi: abi::RustIntrinsic,
sig: FnSig {
binder_id: it.id,
inputs: inputs,
output: output,
variadic: false,

View file

@ -14,47 +14,14 @@ pub use self::WfConstraint::*;
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),

View file

@ -300,14 +300,14 @@ pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) {
}
fn resolve_trait_ref(fcx: &FnCtxt, obligation: &Obligation)
-> (ty::TraitRef, ty::t)
-> (Rc<ty::TraitRef>, ty::t)
{
let trait_ref =
fcx.infcx().resolve_type_vars_in_trait_ref_if_possible(
&*obligation.trait_ref);
let self_ty =
trait_ref.substs.self_ty().unwrap();
(trait_ref, self_ty)
(Rc::new(trait_ref), self_ty)
}
pub fn report_fulfillment_errors(fcx: &FnCtxt,

View file

@ -12,10 +12,10 @@ use middle::subst;
use middle::subst::{Subst};
use middle::traits;
use middle::ty;
use middle::ty::liberate_late_bound_regions;
use middle::ty_fold::{TypeFolder, TypeFoldable};
use middle::typeck::astconv::AstConv;
use middle::typeck::check::{FnCtxt, Inherited, blank_fn_ctxt, vtable, regionck};
use middle::typeck::check::regionmanip::replace_late_bound_regions;
use middle::typeck::CrateCtxt;
use util::ppaux::Repr;
@ -166,16 +166,24 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
let mut bounds_checker = BoundsChecker::new(fcx, item.span,
item.id, Some(&mut this.cache));
// Find the impl self type as seen from the "inside" --
// that is, with all type parameters converted from bound
// to free, and any late-bound regions on the impl
// liberated.
let self_ty = ty::node_id_to_type(fcx.tcx(), item.id);
let self_ty = self_ty.subst(fcx.tcx(), &fcx.inh.param_env.free_substs);
let self_ty = liberate_late_bound_regions(fcx.tcx(), item.id, &ty::bind(self_ty)).value;
bounds_checker.check_traits_in_ty(self_ty);
// Similarly, obtain an "inside" reference to the trait
// that the impl implements.
let trait_ref = match ty::impl_trait_ref(fcx.tcx(), local_def(item.id)) {
None => { return; }
Some(t) => { t }
};
let trait_ref = (*trait_ref).subst(fcx.tcx(), &fcx.inh.param_env.free_substs);
let trait_ref = liberate_late_bound_regions(fcx.tcx(), item.id, &trait_ref);
// There are special rules that apply to drop.
if
@ -215,7 +223,6 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
// FIXME -- This is a bit ill-factored. There is very similar
// code in traits::util::obligations_for_generics.
fcx.add_region_obligations_for_type_parameter(item.span,
ty::ParamTy::for_self(trait_ref.def_id),
&trait_def.bounds,
trait_ref.self_ty());
for builtin_bound in trait_def.bounds.builtin_bounds.iter() {
@ -280,12 +287,13 @@ impl<'cx,'tcx> BoundsChecker<'cx,'tcx> {
let trait_def = ty::lookup_trait_def(self.fcx.tcx(), trait_ref.def_id);
let bounds = trait_def.generics.to_bounds(self.tcx(), &trait_ref.substs);
self.fcx.add_obligations_for_parameters(
traits::ObligationCause::new(
self.span,
traits::ItemObligation(trait_ref.def_id)),
&trait_ref.substs,
&trait_def.generics);
&bounds);
for &ty in trait_ref.substs.types.iter() {
self.check_traits_in_ty(ty);
@ -335,7 +343,7 @@ impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> {
traits::ObligationCause::new(self.span,
traits::ItemObligation(type_id)),
substs,
&polytype.generics);
&polytype.generics.to_bounds(self.tcx(), substs));
} else {
// There are two circumstances in which we ignore
// region obligations.
@ -363,7 +371,7 @@ impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> {
traits::ObligationCause::new(self.span,
traits::ItemObligation(type_id)),
substs,
&polytype.generics);
&polytype.generics.to_bounds(self.tcx(), substs));
}
self.fold_substs(substs);
@ -372,16 +380,12 @@ impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> {
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;
}

View file

@ -214,12 +214,11 @@ pub fn get_enum_variant_types(ccx: &CrateCtxt,
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(_) => {
@ -403,7 +402,6 @@ fn collect_trait_methods(ccx: &CrateCtxt,
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,
@ -588,7 +586,6 @@ fn convert_methods<'a,I>(ccx: &CrateCtxt,
method_generics: &m_ty_generics,
};
astconv::ty_of_method(&imcx,
m.id,
m.pe_fn_style(),
untransformed_rcvr_ty,
m.pe_explicit_self(),
@ -603,7 +600,6 @@ fn convert_methods<'a,I>(ccx: &CrateCtxt,
method_generics: &m_ty_generics,
};
astconv::ty_of_method(&tmcx,
m.id,
m.pe_fn_style(),
untransformed_rcvr_ty,
m.pe_explicit_self(),
@ -1116,10 +1112,12 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::Item) {
for impl_item in impl_items.iter() {
match *impl_item {
ast::MethodImplItem(ref method) => {
let body_id = method.pe_body().id;
check_method_self_type(ccx,
&BindingRscope::new(method.id),
&BindingRscope::new(),
selfty,
method.pe_explicit_self());
method.pe_explicit_self(),
body_id);
methods.push(&**method);
}
ast::TypeImplItem(ref typedef) => {
@ -1174,17 +1172,19 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::Item) {
local_def(it.id));
match *trait_method {
ast::RequiredMethod(ref type_method) => {
let rscope = BindingRscope::new(type_method.id);
let rscope = BindingRscope::new();
check_method_self_type(ccx,
&rscope,
self_type,
&type_method.explicit_self)
&type_method.explicit_self,
it.id)
}
ast::ProvidedMethod(ref method) => {
check_method_self_type(ccx,
&BindingRscope::new(method.id),
&BindingRscope::new(),
self_type,
method.pe_explicit_self())
method.pe_explicit_self(),
it.id)
}
ast::TypeTraitItem(ref associated_type) => {
convert_associated_type(ccx,
@ -1294,7 +1294,6 @@ pub fn convert_struct(ccx: &CrateCtxt,
|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);
@ -1465,11 +1464,7 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: &ast::Item)
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,
@ -2015,12 +2010,12 @@ fn conv_param_bounds<'tcx,AC>(this: &AC,
astconv::partition_bounds(this.tcx(), span, all_bounds.as_slice());
let trait_bounds: Vec<Rc<ty::TraitRef>> =
trait_bounds.into_iter()
.map(|b| {
astconv::instantiate_trait_ref(this,
&ExplicitRscope,
b,
Some(param_ty.to_ty(this.tcx())),
Some(param_ty.to_ty(this.tcx())))
.map(|bound| {
astconv::instantiate_poly_trait_ref(this,
&ExplicitRscope,
bound,
Some(param_ty.to_ty(this.tcx())),
Some(param_ty.to_ty(this.tcx())))
})
.collect();
let region_bounds: Vec<ty::Region> =
@ -2091,7 +2086,7 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt,
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))
@ -2109,8 +2104,7 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt,
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}
});
@ -2142,10 +2136,12 @@ pub fn mk_item_substs(ccx: &CrateCtxt,
/// Verifies that the explicit self type of a method matches the impl or
/// trait.
fn check_method_self_type<RS:RegionScope>(
crate_context: &CrateCtxt,
rs: &RS,
required_type: ty::t,
explicit_self: &ast::ExplicitSelf) {
crate_context: &CrateCtxt,
rs: &RS,
required_type: ty::t,
explicit_self: &ast::ExplicitSelf,
body_id: ast::NodeId)
{
match explicit_self.node {
ast::SelfExplicit(ref ast_type, _) => {
let typ = crate_context.to_ty(rs, &**ast_type);
@ -2154,13 +2150,44 @@ fn check_method_self_type<RS:RegionScope>(
ty::ty_uniq(typ) => typ,
_ => typ,
};
// "Required type" comes from the trait definition. It may
// contain late-bound regions from the method, but not the
// trait (since traits only have early-bound region
// parameters).
assert!(!ty::type_escapes_depth(required_type, 1));
let required_type_free =
ty::liberate_late_bound_regions(
crate_context.tcx,
body_id,
&ty::bind(required_type)).value;
// The "base type" comes from the impl. It may have late-bound
// regions from the impl or the method.
let base_type_free = // liberate impl regions:
ty::liberate_late_bound_regions(
crate_context.tcx,
body_id,
&ty::bind(ty::bind(base_type))).value.value;
let base_type_free = // liberate method regions:
ty::liberate_late_bound_regions(
crate_context.tcx,
body_id,
&ty::bind(base_type_free)).value;
debug!("required_type={} required_type_free={} \
base_type={} base_type_free={}",
required_type.repr(crate_context.tcx),
required_type_free.repr(crate_context.tcx),
base_type.repr(crate_context.tcx),
base_type_free.repr(crate_context.tcx));
let infcx = infer::new_infer_ctxt(crate_context.tcx);
drop(typeck::require_same_types(crate_context.tcx,
Some(&infcx),
false,
explicit_self.span,
base_type,
required_type,
base_type_free,
required_type_free,
|| {
format!("mismatched self type: expected `{}`",
ppaux::ty_to_string(crate_context.tcx, required_type))

View file

@ -59,6 +59,7 @@ use syntax::codemap::Span;
pub trait Combine<'tcx> {
fn infcx<'a>(&'a self) -> &'a InferCtxt<'a, 'tcx>;
fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { self.infcx().tcx }
fn tag(&self) -> String;
fn a_is_expected(&self) -> bool;
fn trace(&self) -> TypeTrace;
@ -296,26 +297,14 @@ pub trait Combine<'tcx> {
Err(ty::terr_trait_stores_differ(vk, expected_found(self, a, b)))
}
}
}
fn trait_refs(&self,
a: &ty::TraitRef,
b: &ty::TraitRef)
-> cres<ty::TraitRef> {
// Different traits cannot be related
// - NOTE in the future, expand out subtraits!
if a.def_id != b.def_id {
Err(ty::terr_traits(
expected_found(self, a.def_id, b.def_id)))
} else {
let substs = try!(self.substs(a.def_id, &a.substs, &b.substs));
Ok(ty::TraitRef { def_id: a.def_id,
substs: substs })
}
}
-> cres<ty::TraitRef>;
// this must be overridden to do correctly, so as to account for higher-ranked
// behavior
}
#[deriving(Clone)]
@ -334,48 +323,6 @@ pub fn expected_found<'tcx, C: Combine<'tcx>, T>(
}
}
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;

View file

@ -137,4 +137,9 @@ impl<'f, 'tcx> Combine<'tcx> for Equate<'f, 'tcx> {
try!(self.sub().fn_sigs(a, b));
self.sub().fn_sigs(b, a)
}
fn trait_refs(&self, a: &ty::TraitRef, b: &ty::TraitRef) -> cres<ty::TraitRef> {
try!(self.sub().trait_refs(a, b));
self.sub().trait_refs(b, a)
}
}

View file

@ -650,14 +650,12 @@ impl<'a, 'tcx> ErrorReporting for InferCtxt<'a, 'tcx> {
sup,
"");
}
infer::RelateParamBound(span, param_ty, ty) => {
infer::RelateParamBound(span, ty) => {
self.tcx.sess.span_err(
span,
format!("the type `{}` (provided as the value of \
the parameter `{}`) does not fulfill the \
format!("the type `{}` does not fulfill the \
required lifetime",
self.ty_to_string(ty),
param_ty.user_string(self.tcx)).as_slice());
self.ty_to_string(ty)).as_slice());
note_and_explain_region(self.tcx,
"type must outlive ",
sub,
@ -1460,8 +1458,8 @@ impl<'a, 'tcx> ErrorReportingHelpers for InferCtxt<'a, 'tcx> {
format!(" for {}in function call",
bound_region_to_string(self.tcx, "lifetime parameter ", true, br))
}
infer::LateBoundRegion(_, br, infer::FnType) => {
format!(" for {}in function type",
infer::LateBoundRegion(_, br, infer::HigherRankedType) => {
format!(" for {}in generic type",
bound_region_to_string(self.tcx, "lifetime parameter ", true, br))
}
infer::EarlyBoundRegion(_, name) => {
@ -1651,13 +1649,11 @@ impl<'a, 'tcx> ErrorReportingHelpers for InferCtxt<'a, 'tcx> {
does not outlive the data it points at",
self.ty_to_string(ty)).as_slice());
}
infer::RelateParamBound(span, param_ty, t) => {
infer::RelateParamBound(span, t) => {
self.tcx.sess.span_note(
span,
format!("...so that the parameter `{}`, \
when instantiated with `{}`, \
will meet its declared lifetime bounds.",
param_ty.user_string(self.tcx),
format!("...so that the type `{}` \
will meet the declared lifetime bounds.",
self.ty_to_string(t)).as_slice());
}
infer::RelateDefaultParamBound(span, t) => {

View file

@ -10,23 +10,18 @@
use middle::ty::{BuiltinBounds};
use middle::ty::RegionVid;
use middle::ty;
use middle::typeck::infer::combine::*;
use middle::typeck::infer::lattice::*;
use middle::typeck::infer::equate::Equate;
use middle::typeck::infer::fold_regions_in_sig;
use middle::typeck::infer::LateBoundRegionConversionTime::FnType;
use middle::typeck::infer::higher_ranked::HigherRankedRelations;
use middle::typeck::infer::lub::Lub;
use middle::typeck::infer::region_inference::RegionMark;
use middle::typeck::infer::sub::Sub;
use middle::typeck::infer::{cres, InferCtxt};
use middle::typeck::infer::{TypeTrace, Subtype};
use syntax::ast::{Many, Once, MutImmutable, MutMutable};
use syntax::ast::{NormalFn, UnsafeFn, NodeId};
use syntax::ast::{NormalFn, UnsafeFn};
use syntax::ast::{Onceness, FnStyle};
use util::common::{indenter};
use util::nodemap::FnvHashMap;
use util::ppaux::mt_to_string;
use util::ppaux::Repr;
@ -128,139 +123,10 @@ impl<'f, 'tcx> Combine<'tcx> for Glb<'f, 'tcx> {
}
fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
// Note: this is a subtle algorithm. For a full explanation,
// please see the large comment in `region_inference.rs`.
self.higher_ranked_glb(a, b)
}
debug!("{}.fn_sigs({}, {})",
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.fields.infcx.region_vars.mark();
// Instantiate each bound region with a fresh region variable.
let (a_with_fresh, a_map) =
self.fields.infcx.replace_late_bound_regions_with_fresh_var(
a.binder_id, self.trace().span(), FnType, a);
let a_vars = var_ids(self, &a_map);
let (b_with_fresh, b_map) =
self.fields.infcx.replace_late_bound_regions_with_fresh_var(
b.binder_id, self.trace().span(), FnType, b);
let b_vars = var_ids(self, &b_map);
// Collect constraints.
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.fields.infcx.region_vars.vars_created_since_mark(mark);
let sig1 =
fold_regions_in_sig(
self.fields.infcx.tcx,
&sig0,
|r| {
generalize_region(self,
mark,
new_vars.as_slice(),
sig0.binder_id,
&a_map,
a_vars.as_slice(),
b_vars.as_slice(),
r)
});
debug!("sig1 = {}", sig1.repr(self.fields.infcx.tcx));
return Ok(sig1);
fn generalize_region(this: &Glb,
mark: RegionMark,
new_vars: &[RegionVid],
new_binder_id: NodeId,
a_map: &FnvHashMap<ty::BoundRegion, ty::Region>,
a_vars: &[RegionVid],
b_vars: &[RegionVid],
r0: ty::Region) -> ty::Region {
if !is_var_in_set(new_vars, r0) {
assert!(!r0.is_bound());
return r0;
}
let tainted = this.fields.infcx.region_vars.tainted(mark, r0);
let mut a_r = None;
let mut b_r = None;
let mut only_new_vars = true;
for r in tainted.iter() {
if is_var_in_set(a_vars, *r) {
if a_r.is_some() {
return fresh_bound_variable(this, new_binder_id);
} else {
a_r = Some(*r);
}
} else if is_var_in_set(b_vars, *r) {
if b_r.is_some() {
return fresh_bound_variable(this, new_binder_id);
} else {
b_r = Some(*r);
}
} else if !is_var_in_set(new_vars, *r) {
only_new_vars = false;
}
}
// NB---I do not believe this algorithm computes
// (necessarily) the GLB. As written it can
// spuriously fail. In particular, if there is a case
// like: |fn(&a)| and fn(fn(&b)), where a and b are
// free, it will return fn(&c) where c = GLB(a,b). If
// however this GLB is not defined, then the result is
// an error, even though something like
// "fn<X>(fn(&X))" where X is bound would be a
// subtype of both of those.
//
// The problem is that if we were to return a bound
// variable, we'd be computing a lower-bound, but not
// necessarily the *greatest* lower-bound.
//
// Unfortunately, this problem is non-trivial to solve,
// because we do not know at the time of computing the GLB
// whether a GLB(a,b) exists or not, because we haven't
// run region inference (or indeed, even fully computed
// the region hierarchy!). The current algorithm seems to
// works ok in practice.
if a_r.is_some() && b_r.is_some() && only_new_vars {
// Related to exactly one bound variable from each fn:
return rev_lookup(this, a_map, new_binder_id, a_r.unwrap());
} else if a_r.is_none() && b_r.is_none() {
// Not related to bound variables from either fn:
assert!(!r0.is_bound());
return r0;
} else {
// Other:
return fresh_bound_variable(this, new_binder_id);
}
}
fn rev_lookup(this: &Glb,
a_map: &FnvHashMap<ty::BoundRegion, ty::Region>,
new_binder_id: NodeId,
r: ty::Region) -> ty::Region
{
for (a_br, a_r) in a_map.iter() {
if *a_r == r {
return ty::ReLateBound(new_binder_id, *a_br);
}
}
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.fields.infcx.region_vars.new_bound(binder_id)
}
fn trait_refs(&self, a: &ty::TraitRef, b: &ty::TraitRef) -> cres<ty::TraitRef> {
self.higher_ranked_glb(a, b)
}
}

View file

@ -0,0 +1,415 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
# Skolemization and functions
One of the trickiest and most subtle aspects of regions is dealing
with higher-ranked things which include bound region variables, such
as function types. I strongly suggest that if you want to understand
the situation, you read this paper (which is, admittedly, very long,
but you don't have to read the whole thing):
http://research.microsoft.com/en-us/um/people/simonpj/papers/higher-rank/
Although my explanation will never compete with SPJ's (for one thing,
his is approximately 100 pages), I will attempt to explain the basic
problem and also how we solve it. Note that the paper only discusses
subtyping, not the computation of LUB/GLB.
The problem we are addressing is that there is a kind of subtyping
between functions with bound region parameters. Consider, for
example, whether the following relation holds:
for<'a> fn(&'a int) <: for<'b> fn(&'b int)? (Yes, a => b)
The answer is that of course it does. These two types are basically
the same, except that in one we used the name `a` and one we used
the name `b`.
In the examples that follow, it becomes very important to know whether
a lifetime is bound in a function type (that is, is a lifetime
parameter) or appears free (is defined in some outer scope).
Therefore, from now on I will always write the bindings explicitly,
using the Rust syntax `for<'a> fn(&'a int)` to indicate that `a` is a
lifetime parameter.
Now let's consider two more function types. Here, we assume that the
`'b` lifetime is defined somewhere outside and hence is not a lifetime
parameter bound by the function type (it "appears free"):
for<'a> fn(&'a int) <: fn(&'b int)? (Yes, a => b)
This subtyping relation does in fact hold. To see why, you have to
consider what subtyping means. One way to look at `T1 <: T2` is to
say that it means that it is always ok to treat an instance of `T1` as
if it had the type `T2`. So, with our functions, it is always ok to
treat a function that can take pointers with any lifetime as if it
were a function that can only take a pointer with the specific
lifetime `'b`. After all, `'b` is a lifetime, after all, and
the function can take values of any lifetime.
You can also look at subtyping as the *is a* relationship. This amounts
to the same thing: a function that accepts pointers with any lifetime
*is a* function that accepts pointers with some specific lifetime.
So, what if we reverse the order of the two function types, like this:
fn(&'b int) <: for<'a> fn(&'a int)? (No)
Does the subtyping relationship still hold? The answer of course is
no. In this case, the function accepts *only the lifetime `'b`*,
so it is not reasonable to treat it as if it were a function that
accepted any lifetime.
What about these two examples:
for<'a,'b> fn(&'a int, &'b int) <: for<'a> fn(&'a int, &'a int)? (Yes)
for<'a> fn(&'a int, &'a int) <: for<'a,'b> fn(&'a int, &'b int)? (No)
Here, it is true that functions which take two pointers with any two
lifetimes can be treated as if they only accepted two pointers with
the same lifetime, but not the reverse.
## The algorithm
Here is the algorithm we use to perform the subtyping check:
1. Replace all bound regions in the subtype with new variables
2. Replace all bound regions in the supertype with skolemized
equivalents. A "skolemized" region is just a new fresh region
name.
3. Check that the parameter and return types match as normal
4. Ensure that no skolemized regions 'leak' into region variables
visible from "the outside"
Let's walk through some examples and see how this algorithm plays out.
#### First example
We'll start with the first example, which was:
1. for<'a> fn(&'a T) <: for<'b> fn(&'b T)? Yes: a -> b
After steps 1 and 2 of the algorithm we will have replaced the types
like so:
1. fn(&'A T) <: fn(&'x T)?
Here the upper case `&A` indicates a *region variable*, that is, a
region whose value is being inferred by the system. I also replaced
`&b` with `&x`---I'll use letters late in the alphabet (`x`, `y`, `z`)
to indicate skolemized region names. We can assume they don't appear
elsewhere. Note that neither the sub- nor the supertype bind any
region names anymore (as indicated by the absence of `<` and `>`).
The next step is to check that the parameter types match. Because
parameters are contravariant, this means that we check whether:
&'x T <: &'A T
Region pointers are contravariant so this implies that
&A <= &x
must hold, where `<=` is the subregion relationship. Processing
*this* constrain simply adds a constraint into our graph that `&A <=
&x` and is considered successful (it can, for example, be satisfied by
choosing the value `&x` for `&A`).
So far we have encountered no error, so the subtype check succeeds.
#### The third example
Now let's look first at the third example, which was:
3. fn(&'a T) <: for<'b> fn(&'b T)? No!
After steps 1 and 2 of the algorithm we will have replaced the types
like so:
3. fn(&'a T) <: fn(&'x T)?
This looks pretty much the same as before, except that on the LHS
`'a` was not bound, and hence was left as-is and not replaced with
a variable. The next step is again to check that the parameter types
match. This will ultimately require (as before) that `'a` <= `&x`
must hold: but this does not hold. `self` and `x` are both distinct
free regions. So the subtype check fails.
#### Checking for skolemization leaks
You may be wondering about that mysterious last step in the algorithm.
So far it has not been relevant. The purpose of that last step is to
catch something like *this*:
for<'a> fn() -> fn(&'a T) <: fn() -> for<'b> fn(&'b T)? No.
Here the function types are the same but for where the binding occurs.
The subtype returns a function that expects a value in precisely one
region. The supertype returns a function that expects a value in any
region. If we allow an instance of the subtype to be used where the
supertype is expected, then, someone could call the fn and think that
the return value has type `fn<b>(&'b T)` when it really has type
`fn(&'a T)` (this is case #3, above). Bad.
So let's step through what happens when we perform this subtype check.
We first replace the bound regions in the subtype (the supertype has
no bound regions). This gives us:
fn() -> fn(&'A T) <: fn() -> for<'b> fn(&'b T)?
Now we compare the return types, which are covariant, and hence we have:
fn(&'A T) <: for<'b> fn(&'b T)?
Here we skolemize the bound region in the supertype to yield:
fn(&'A T) <: fn(&'x T)?
And then proceed to compare the argument types:
&'x T <: &'A T
'A <= 'x
Finally, this is where it gets interesting! This is where an error
*should* be reported. But in fact this will not happen. The reason why
is that `A` is a variable: we will infer that its value is the fresh
region `x` and think that everything is happy. In fact, this behavior
is *necessary*, it was key to the first example we walked through.
The difference between this example and the first one is that the variable
`A` already existed at the point where the skolemization occurred. In
the first example, you had two functions:
for<'a> fn(&'a T) <: for<'b> fn(&'b T)
and hence `&A` and `&x` were created "together". In general, the
intention of the skolemized names is that they are supposed to be
fresh names that could never be equal to anything from the outside.
But when inference comes into play, we might not be respecting this
rule.
So the way we solve this is to add a fourth step that examines the
constraints that refer to skolemized names. Basically, consider a
non-directed verison of the constraint graph. Let `Tainted(x)` be the
set of all things reachable from a skolemized variable `x`.
`Tainted(x)` should not contain any regions that existed before the
step at which the skolemization was performed. So this case here
would fail because `&x` was created alone, but is relatable to `&A`.
## Computing the LUB and GLB
The paper I pointed you at is written for Haskell. It does not
therefore considering subtyping and in particular does not consider
LUB or GLB computation. We have to consider this. Here is the
algorithm I implemented.
First though, let's discuss what we are trying to compute in more
detail. The LUB is basically the "common supertype" and the GLB is
"common subtype"; one catch is that the LUB should be the
*most-specific* common supertype and the GLB should be *most general*
common subtype (as opposed to any common supertype or any common
subtype).
Anyway, to help clarify, here is a table containing some function
pairs and their LUB/GLB (for conciseness, in this table, I'm just
including the lifetimes here, not the rest of the types, and I'm
writing `fn<>` instead of `for<> fn`):
```
Type 1 Type 2 LUB GLB
fn<'a>('a) fn('X) fn('X) fn<'a>('a)
fn('a) fn('X) -- fn<'a>('a)
fn<'a,'b>('a, 'b) fn<'x>('x, 'x) fn<'a>('a, 'a) fn<'a,'b>('a, 'b)
fn<'a,'b>('a, 'b, 'a) fn<'x,'y>('x, 'y, 'y) fn<'a>('a, 'a, 'a) fn<'a,'b,'c>('a,'b,'c)
```
### Conventions
I use lower-case letters (e.g., `&a`) for bound regions and upper-case
letters for free regions (`&A`). Region variables written with a
dollar-sign (e.g., `$a`). I will try to remember to enumerate the
bound-regions on the fn type as well (e.g., `for<'a> fn(&a)`).
### High-level summary
Both the LUB and the GLB algorithms work in a similar fashion. They
begin by replacing all bound regions (on both sides) with fresh region
inference variables. Therefore, both functions are converted to types
that contain only free regions. We can then compute the LUB/GLB in a
straightforward way, as described in `combine.rs`. This results in an
interim type T. The algorithms then examine the regions that appear
in T and try to, in some cases, replace them with bound regions to
yield the final result.
To decide whether to replace a region `R` that appears in `T` with a
bound region, the algorithms make use of two bits of information.
First is a set `V` that contains all region variables created as part
of the LUB/GLB computation. `V` will contain the region variables
created to replace the bound regions in the input types, but it also
contains 'intermediate' variables created to represent the LUB/GLB of
individual regions. Basically, when asked to compute the LUB/GLB of a
region variable with another region, the inferencer cannot oblige
immediately since the values of that variables are not known.
Therefore, it creates a new variable that is related to the two
regions. For example, the LUB of two variables `$x` and `$y` is a
fresh variable `$z` that is constrained such that `$x <= $z` and `$y
<= $z`. So `V` will contain these intermediate variables as well.
The other important factor in deciding how to replace a region in T is
the function `Tainted($r)` which, for a region variable, identifies
all regions that the region variable is related to in some way
(`Tainted()` made an appearance in the subtype computation as well).
### LUB
The LUB algorithm proceeds in three steps:
1. Replace all bound regions (on both sides) with fresh region
inference variables.
2. Compute the LUB "as normal", meaning compute the GLB of each
pair of argument types and the LUB of the return types and
so forth. Combine those to a new function type `F`.
3. Replace each region `R` that appears in `F` as follows:
- Let `V` be the set of variables created during the LUB
computational steps 1 and 2, as described in the previous section.
- If `R` is not in `V`, replace `R` with itself.
- If `Tainted(R)` contains a region that is not in `V`,
replace `R` with itself.
- Otherwise, select the earliest variable in `Tainted(R)` that originates
from the left-hand side and replace `R` with the bound region that
this variable was a replacement for.
So, let's work through the simplest example: `fn(&A)` and `for<'a> fn(&a)`.
In this case, `&a` will be replaced with `$a` and the interim LUB type
`fn($b)` will be computed, where `$b=GLB(&A,$a)`. Therefore, `V =
{$a, $b}` and `Tainted($b) = { $b, $a, &A }`. When we go to replace
`$b`, we find that since `&A \in Tainted($b)` is not a member of `V`,
we leave `$b` as is. When region inference happens, `$b` will be
resolved to `&A`, as we wanted.
Let's look at a more complex one: `fn(&a, &b)` and `fn(&x, &x)`. In
this case, we'll end up with a (pre-replacement) LUB type of `fn(&g,
&h)` and a graph that looks like:
```
$a $b *--$x
\ \ / /
\ $h-* /
$g-----------*
```
Here `$g` and `$h` are fresh variables that are created to represent
the LUB/GLB of things requiring inference. This means that `V` and
`Tainted` will look like:
```
V = {$a, $b, $g, $h, $x}
Tainted($g) = Tainted($h) = { $a, $b, $h, $g, $x }
```
Therefore we replace both `$g` and `$h` with `$a`, and end up
with the type `fn(&a, &a)`.
### GLB
The procedure for computing the GLB is similar. The difference lies
in computing the replacements for the various variables. For each
region `R` that appears in the type `F`, we again compute `Tainted(R)`
and examine the results:
1. If `R` is not in `V`, it is not replaced.
2. Else, if `Tainted(R)` contains only variables in `V`, and it
contains exactly one variable from the LHS and one variable from
the RHS, then `R` can be mapped to the bound version of the
variable from the LHS.
3. Else, if `Tainted(R)` contains no variable from the LHS and no
variable from the RHS, then `R` can be mapped to itself.
4. Else, `R` is mapped to a fresh bound variable.
These rules are pretty complex. Let's look at some examples to see
how they play out.
Out first example was `fn(&a)` and `fn(&X)`. In this case, `&a` will
be replaced with `$a` and we will ultimately compute a
(pre-replacement) GLB type of `fn($g)` where `$g=LUB($a,&X)`.
Therefore, `V={$a,$g}` and `Tainted($g)={$g,$a,&X}. To find the
replacement for `$g` we consult the rules above:
- Rule (1) does not apply because `$g \in V`
- Rule (2) does not apply because `&X \in Tainted($g)`
- Rule (3) does not apply because `$a \in Tainted($g)`
- Hence, by rule (4), we replace `$g` with a fresh bound variable `&z`.
So our final result is `fn(&z)`, which is correct.
The next example is `fn(&A)` and `fn(&Z)`. In this case, we will again
have a (pre-replacement) GLB of `fn(&g)`, where `$g = LUB(&A,&Z)`.
Therefore, `V={$g}` and `Tainted($g) = {$g, &A, &Z}`. In this case,
by rule (3), `$g` is mapped to itself, and hence the result is
`fn($g)`. This result is correct (in this case, at least), but it is
indicative of a case that *can* lead us into concluding that there is
no GLB when in fact a GLB does exist. See the section "Questionable
Results" below for more details.
The next example is `fn(&a, &b)` and `fn(&c, &c)`. In this case, as
before, we'll end up with `F=fn($g, $h)` where `Tainted($g) =
Tainted($h) = {$g, $h, $a, $b, $c}`. Only rule (4) applies and hence
we'll select fresh bound variables `y` and `z` and wind up with
`fn(&y, &z)`.
For the last example, let's consider what may seem trivial, but is
not: `fn(&a, &a)` and `fn(&b, &b)`. In this case, we'll get `F=fn($g,
$h)` where `Tainted($g) = {$g, $a, $x}` and `Tainted($h) = {$h, $a,
$x}`. Both of these sets contain exactly one bound variable from each
side, so we'll map them both to `&a`, resulting in `fn(&a, &a)`, which
is the desired result.
### Shortcomings and correctness
You may be wondering whether this algorithm is correct. The answer is
"sort of". There are definitely cases where they fail to compute a
result even though a correct result exists. I believe, though, that
if they succeed, then the result is valid, and I will attempt to
convince you. The basic argument is that the "pre-replacement" step
computes a set of constraints. The replacements, then, attempt to
satisfy those constraints, using bound identifiers where needed.
For now I will briefly go over the cases for LUB/GLB and identify
their intent:
- LUB:
- The region variables that are substituted in place of bound regions
are intended to collect constraints on those bound regions.
- If Tainted(R) contains only values in V, then this region is unconstrained
and can therefore be generalized, otherwise it cannot.
- GLB:
- The region variables that are substituted in place of bound regions
are intended to collect constraints on those bound regions.
- If Tainted(R) contains exactly one variable from each side, and
only variables in V, that indicates that those two bound regions
must be equated.
- Otherwise, if Tainted(R) references any variables from left or right
side, then it is trying to combine a bound region with a free one or
multiple bound regions, so we need to select fresh bound regions.
Sorry this is more of a shorthand to myself. I will try to write up something
more convincing in the future.
#### Where are the algorithms wrong?
- The pre-replacement computation can fail even though using a
bound-region would have succeeded.
- We will compute GLB(fn(fn($a)), fn(fn($b))) as fn($c) where $c is the
GLB of $a and $b. But if inference finds that $a and $b must be mapped
to regions without a GLB, then this is effectively a failure to compute
the GLB. However, the result `fn<$c>(fn($c))` is a valid GLB.
*/

View file

@ -0,0 +1,445 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* Helper routines for higher-ranked things. See the `doc` module at
* the end of the file for details.
*/
use middle::ty;
use middle::ty::replace_late_bound_regions;
use middle::typeck::infer::{mod, combine, cres, InferCtxt};
use middle::typeck::infer::combine::Combine;
use middle::typeck::infer::region_inference::{RegionMark};
use middle::ty_fold::{mod, HigherRankedFoldable, TypeFoldable};
use syntax::codemap::Span;
use util::nodemap::FnvHashMap;
use util::ppaux::{bound_region_to_string, Repr};
pub trait HigherRankedCombineable : HigherRankedFoldable + TypeFoldable + Repr {
fn super_combine<'tcx,C:Combine<'tcx>>(combiner: &C, a: &Self, b: &Self) -> cres<Self>;
}
pub trait HigherRankedRelations {
fn higher_ranked_sub<T>(&self, a: &T, b: &T) -> cres<T>
where T : HigherRankedCombineable;
fn higher_ranked_lub<T>(&self, a: &T, b: &T) -> cres<T>
where T : HigherRankedCombineable;
fn higher_ranked_glb<T>(&self, a: &T, b: &T) -> cres<T>
where T : HigherRankedCombineable;
}
impl<'tcx,C> HigherRankedRelations for C
where C : Combine<'tcx>
{
fn higher_ranked_sub<T>(&self, a: &T, b: &T) -> cres<T>
where T : HigherRankedCombineable
{
debug!("higher_ranked_sub(a={}, b={})",
a.repr(self.tcx()), b.repr(self.tcx()));
// Rather than checking the subtype relationship between `a` and `b`
// as-is, we need to do some extra work here in order to make sure
// that function subtyping works correctly with respect to regions
//
// Note: this is a subtle algorithm. For a full explanation,
// please see the large comment at the end of the file in the (inlined) module
// `doc`.
// Make a mark so we can examine "all bindings that were
// created as part of this type comparison".
let mark = self.infcx().region_vars.mark();
// First, we instantiate each bound region in the subtype with a fresh
// region variable.
let (a_prime, _) =
self.infcx().replace_late_bound_regions_with_fresh_var(
self.trace().origin.span(),
infer::HigherRankedType,
a);
// Second, we instantiate each bound region in the supertype with a
// fresh concrete region.
let (b_prime, skol_map) = {
replace_late_bound_regions(self.tcx(), b, |br, _| {
let skol = self.infcx().region_vars.new_skolemized(br);
debug!("Bound region {} skolemized to {}",
bound_region_to_string(self.tcx(), "", false, br),
skol);
skol
})
};
debug!("a_prime={}", a_prime.repr(self.tcx()));
debug!("b_prime={}", b_prime.repr(self.tcx()));
// Compare types now that bound regions have been replaced.
let result = try!(HigherRankedCombineable::super_combine(self, &a_prime, &b_prime));
// Presuming type comparison succeeds, we need to check
// that the skolemized regions do not "leak".
let new_vars =
self.infcx().region_vars.vars_created_since_mark(mark);
for (&skol_br, &skol) in skol_map.iter() {
let tainted = self.infcx().region_vars.tainted(mark, skol);
for tainted_region in tainted.iter() {
// Each skolemized should only be relatable to itself
// or new variables:
match *tainted_region {
ty::ReInfer(ty::ReVar(ref vid)) => {
if new_vars.iter().any(|x| x == vid) { continue; }
}
_ => {
if *tainted_region == skol { continue; }
}
};
// A is not as polymorphic as B:
if self.a_is_expected() {
debug!("Not as polymorphic!");
return Err(ty::terr_regions_insufficiently_polymorphic(
skol_br, *tainted_region));
} else {
debug!("Overly polymorphic!");
return Err(ty::terr_regions_overly_polymorphic(
skol_br, *tainted_region));
}
}
}
debug!("higher_ranked_sub: OK result={}",
result.repr(self.tcx()));
return Ok(result);
}
fn higher_ranked_lub<T>(&self, a: &T, b: &T) -> cres<T>
where T : HigherRankedCombineable
{
// Make a mark so we can examine "all bindings that were
// created as part of this type comparison".
let mark = self.infcx().region_vars.mark();
// Instantiate each bound region with a fresh region variable.
let span = self.trace().origin.span();
let (a_with_fresh, a_map) =
self.infcx().replace_late_bound_regions_with_fresh_var(
span, infer::HigherRankedType, a);
let (b_with_fresh, _) =
self.infcx().replace_late_bound_regions_with_fresh_var(
span, infer::HigherRankedType, b);
// Collect constraints.
let result0 =
try!(HigherRankedCombineable::super_combine(self, &a_with_fresh, &b_with_fresh));
debug!("lub result0 = {}", result0.repr(self.tcx()));
// Generalize the regions appearing in result0 if possible
let new_vars = self.infcx().region_vars.vars_created_since_mark(mark);
let span = self.trace().origin.span();
let result1 =
fold_regions_in(
self.tcx(),
&result0,
|r, debruijn| generalize_region(self.infcx(), span, mark, debruijn,
new_vars.as_slice(), &a_map, r));
debug!("lub({},{}) = {}",
a.repr(self.tcx()),
b.repr(self.tcx()),
result1.repr(self.tcx()));
return Ok(result1);
fn generalize_region(infcx: &InferCtxt,
span: Span,
mark: RegionMark,
debruijn: ty::DebruijnIndex,
new_vars: &[ty::RegionVid],
a_map: &FnvHashMap<ty::BoundRegion, ty::Region>,
r0: ty::Region)
-> ty::Region {
// Regions that pre-dated the LUB computation stay as they are.
if !is_var_in_set(new_vars, r0) {
assert!(!r0.is_bound());
debug!("generalize_region(r0={}): not new variable", r0);
return r0;
}
let tainted = infcx.region_vars.tainted(mark, r0);
// Variables created during LUB computation which are
// *related* to regions that pre-date the LUB computation
// stay as they are.
if !tainted.iter().all(|r| is_var_in_set(new_vars, *r)) {
debug!("generalize_region(r0={}): \
non-new-variables found in {}",
r0, tainted);
assert!(!r0.is_bound());
return r0;
}
// Otherwise, the variable must be associated with at
// least one of the variables representing bound regions
// in both A and B. Replace the variable with the "first"
// bound region from A that we find it to be associated
// with.
for (a_br, a_r) in a_map.iter() {
if tainted.iter().any(|x| x == a_r) {
debug!("generalize_region(r0={}): \
replacing with {}, tainted={}",
r0, *a_br, tainted);
return ty::ReLateBound(debruijn, *a_br);
}
}
infcx.tcx.sess.span_bug(
span,
format!("region {} is not associated with \
any bound region from A!",
r0).as_slice())
}
}
fn higher_ranked_glb<T>(&self, a: &T, b: &T) -> cres<T>
where T : HigherRankedCombineable
{
debug!("{}.higher_ranked_glb({}, {})",
self.tag(), a.repr(self.tcx()), b.repr(self.tcx()));
// Make a mark so we can examine "all bindings that were
// created as part of this type comparison".
let mark = self.infcx().region_vars.mark();
// Instantiate each bound region with a fresh region variable.
let (a_with_fresh, a_map) =
self.infcx().replace_late_bound_regions_with_fresh_var(
self.trace().origin.span(), infer::HigherRankedType, a);
let (b_with_fresh, b_map) =
self.infcx().replace_late_bound_regions_with_fresh_var(
self.trace().origin.span(), infer::HigherRankedType, b);
let a_vars = var_ids(self, &a_map);
let b_vars = var_ids(self, &b_map);
// Collect constraints.
let result0 =
try!(HigherRankedCombineable::super_combine(self, &a_with_fresh, &b_with_fresh));
debug!("glb result0 = {}", result0.repr(self.tcx()));
// Generalize the regions appearing in fn_ty0 if possible
let new_vars = self.infcx().region_vars.vars_created_since_mark(mark);
let span = self.trace().origin.span();
let result1 =
fold_regions_in(
self.tcx(),
&result0,
|r, debruijn| generalize_region(self.infcx(), span, mark, debruijn,
new_vars.as_slice(),
&a_map, a_vars.as_slice(), b_vars.as_slice(),
r));
debug!("glb({},{}) = {}",
a.repr(self.tcx()),
b.repr(self.tcx()),
result1.repr(self.tcx()));
return Ok(result1);
fn generalize_region(infcx: &InferCtxt,
span: Span,
mark: RegionMark,
debruijn: ty::DebruijnIndex,
new_vars: &[ty::RegionVid],
a_map: &FnvHashMap<ty::BoundRegion, ty::Region>,
a_vars: &[ty::RegionVid],
b_vars: &[ty::RegionVid],
r0: ty::Region) -> ty::Region {
if !is_var_in_set(new_vars, r0) {
assert!(!r0.is_bound());
return r0;
}
let tainted = infcx.region_vars.tainted(mark, r0);
let mut a_r = None;
let mut b_r = None;
let mut only_new_vars = true;
for r in tainted.iter() {
if is_var_in_set(a_vars, *r) {
if a_r.is_some() {
return fresh_bound_variable(infcx, debruijn);
} else {
a_r = Some(*r);
}
} else if is_var_in_set(b_vars, *r) {
if b_r.is_some() {
return fresh_bound_variable(infcx, debruijn);
} else {
b_r = Some(*r);
}
} else if !is_var_in_set(new_vars, *r) {
only_new_vars = false;
}
}
// NB---I do not believe this algorithm computes
// (necessarily) the GLB. As written it can
// spuriously fail. In particular, if there is a case
// like: |fn(&a)| and fn(fn(&b)), where a and b are
// free, it will return fn(&c) where c = GLB(a,b). If
// however this GLB is not defined, then the result is
// an error, even though something like
// "fn<X>(fn(&X))" where X is bound would be a
// subtype of both of those.
//
// The problem is that if we were to return a bound
// variable, we'd be computing a lower-bound, but not
// necessarily the *greatest* lower-bound.
//
// Unfortunately, this problem is non-trivial to solve,
// because we do not know at the time of computing the GLB
// whether a GLB(a,b) exists or not, because we haven't
// run region inference (or indeed, even fully computed
// the region hierarchy!). The current algorithm seems to
// works ok in practice.
if a_r.is_some() && b_r.is_some() && only_new_vars {
// Related to exactly one bound variable from each fn:
return rev_lookup(infcx, span, a_map, a_r.unwrap());
} else if a_r.is_none() && b_r.is_none() {
// Not related to bound variables from either fn:
assert!(!r0.is_bound());
return r0;
} else {
// Other:
return fresh_bound_variable(infcx, debruijn);
}
}
fn rev_lookup(infcx: &InferCtxt,
span: Span,
a_map: &FnvHashMap<ty::BoundRegion, ty::Region>,
r: ty::Region) -> ty::Region
{
for (a_br, a_r) in a_map.iter() {
if *a_r == r {
return ty::ReLateBound(ty::DebruijnIndex::new(1), *a_br);
}
}
infcx.tcx.sess.span_bug(
span,
format!("could not find original bound region for {}", r)[]);
}
fn fresh_bound_variable(infcx: &InferCtxt, debruijn: ty::DebruijnIndex) -> ty::Region {
infcx.region_vars.new_bound(debruijn)
}
}
}
impl HigherRankedCombineable for ty::FnSig {
fn super_combine<'tcx,C:Combine<'tcx>>(combiner: &C, a: &ty::FnSig, b: &ty::FnSig)
-> cres<ty::FnSig>
{
if a.variadic != b.variadic {
return Err(ty::terr_variadic_mismatch(
combine::expected_found(combiner, a.variadic, b.variadic)));
}
let inputs = try!(argvecs(combiner,
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!(combiner.tys(a_ty, b_ty)))),
(ty::FnDiverging, ty::FnDiverging) =>
Ok(ty::FnDiverging),
(a, b) =>
Err(ty::terr_convergence_mismatch(
combine::expected_found(combiner, a != ty::FnDiverging, b != ty::FnDiverging))),
});
return Ok(ty::FnSig {inputs: inputs,
output: output,
variadic: a.variadic});
fn argvecs<'tcx, C: Combine<'tcx>>(combiner: &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)| combiner.args(*a, *b)).collect()
} else {
Err(ty::terr_arg_count)
}
}
}
}
impl HigherRankedCombineable for ty::TraitRef {
fn super_combine<'tcx,C:Combine<'tcx>>(combiner: &C, a: &ty::TraitRef, b: &ty::TraitRef)
-> cres<ty::TraitRef>
{
// Different traits cannot be related
if a.def_id != b.def_id {
Err(ty::terr_traits(
combine::expected_found(combiner, a.def_id, b.def_id)))
} else {
let substs = try!(combiner.substs(a.def_id, &a.substs, &b.substs));
Ok(ty::TraitRef { def_id: a.def_id,
substs: substs })
}
}
}
fn var_ids<'tcx, T: Combine<'tcx>>(combiner: &T,
map: &FnvHashMap<ty::BoundRegion, ty::Region>)
-> Vec<ty::RegionVid> {
map.iter().map(|(_, r)| match *r {
ty::ReInfer(ty::ReVar(r)) => { r }
r => {
combiner.infcx().tcx.sess.span_bug(
combiner.trace().origin.span(),
format!("found non-region-vid: {}", r).as_slice());
}
}).collect()
}
fn is_var_in_set(new_vars: &[ty::RegionVid], r: ty::Region) -> bool {
match r {
ty::ReInfer(ty::ReVar(ref v)) => new_vars.iter().any(|x| x == v),
_ => false
}
}
fn fold_regions_in<T:HigherRankedFoldable>(tcx: &ty::ctxt,
value: &T,
fldr: |ty::Region, ty::DebruijnIndex| -> ty::Region)
-> T
{
value.fold_contents(&mut ty_fold::RegionFolder::new(tcx, |region, current_depth| {
// we should only be encountering "escaping" late-bound regions here,
// because the ones at the current level should have been replaced
// with fresh variables
assert!(match region {
ty::ReLateBound(..) => false,
_ => true
});
fldr(region, ty::DebruijnIndex::new(current_depth))
}))
}

View file

@ -31,13 +31,12 @@
* a lattice.
*/
use middle::ty::{RegionVid, TyVar};
use middle::ty::{TyVar};
use middle::ty;
use middle::typeck::infer::*;
use middle::typeck::infer::combine::*;
use middle::typeck::infer::glb::Glb;
use middle::typeck::infer::lub::Lub;
use util::nodemap::FnvHashMap;
use util::ppaux::Repr;
pub trait LatticeDir {
@ -101,27 +100,3 @@ pub fn super_lattice_tys<'tcx, L:LatticeDir+Combine<'tcx>>(this: &L,
}
}
}
///////////////////////////////////////////////////////////////////////////
// Random utility functions used by LUB/GLB when computing LUB/GLB of
// fn types
pub fn var_ids<'tcx, T: Combine<'tcx>>(this: &T,
map: &FnvHashMap<ty::BoundRegion, ty::Region>)
-> Vec<RegionVid> {
map.iter().map(|(_, r)| match *r {
ty::ReInfer(ty::ReVar(r)) => { r }
r => {
this.infcx().tcx.sess.span_bug(
this.trace().origin.span(),
format!("found non-region-vid: {}", r).as_slice());
}
}).collect()
}
pub fn is_var_in_set(new_vars: &[RegionVid], r: ty::Region) -> bool {
match r {
ty::ReInfer(ty::ReVar(ref v)) => new_vars.iter().any(|x| x == v),
_ => false
}
}

View file

@ -9,23 +9,19 @@
// except according to those terms.
use middle::ty::{BuiltinBounds};
use middle::ty::RegionVid;
use middle::ty;
use middle::typeck::infer::combine::*;
use middle::typeck::infer::equate::Equate;
use middle::typeck::infer::glb::Glb;
use middle::typeck::infer::higher_ranked::HigherRankedRelations;
use middle::typeck::infer::lattice::*;
use middle::typeck::infer::LateBoundRegionConversionTime::FnType;
use middle::typeck::infer::sub::Sub;
use middle::typeck::infer::{cres, InferCtxt};
use middle::typeck::infer::fold_regions_in_sig;
use middle::typeck::infer::{TypeTrace, Subtype};
use middle::typeck::infer::region_inference::RegionMark;
use syntax::ast::{Many, Once, NodeId};
use syntax::ast::{Many, Once};
use syntax::ast::{NormalFn, UnsafeFn};
use syntax::ast::{Onceness, FnStyle};
use syntax::ast::{MutMutable, MutImmutable};
use util::nodemap::FnvHashMap;
use util::ppaux::mt_to_string;
use util::ppaux::Repr;
@ -51,7 +47,7 @@ impl<'f, 'tcx> Combine<'tcx> for Lub<'f, 'tcx> {
fn glb<'a>(&'a self) -> Glb<'a, 'tcx> { Glb(self.fields.clone()) }
fn mts(&self, a: &ty::mt, b: &ty::mt) -> cres<ty::mt> {
let tcx = self.fields.infcx.tcx;
let tcx = self.tcx();
debug!("{}.mts({}, {})",
self.tag(),
@ -111,93 +107,21 @@ impl<'f, 'tcx> Combine<'tcx> for Lub<'f, 'tcx> {
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
debug!("{}.regions({}, {})",
self.tag(),
a.repr(self.fields.infcx.tcx),
b.repr(self.fields.infcx.tcx));
a.repr(self.tcx()),
b.repr(self.tcx()));
Ok(self.fields.infcx.region_vars.lub_regions(Subtype(self.trace()), a, b))
Ok(self.infcx().region_vars.lub_regions(Subtype(self.trace()), a, b))
}
fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
// Note: this is a subtle algorithm. For a full explanation,
// please see the large comment in `region_inference.rs`.
// Make a mark so we can examine "all bindings that were
// created as part of this type comparison".
let mark = self.fields.infcx.region_vars.mark();
// Instantiate each bound region with a fresh region variable.
let (a_with_fresh, a_map) =
self.fields.infcx.replace_late_bound_regions_with_fresh_var(
a.binder_id, self.trace().span(), FnType, a);
let (b_with_fresh, _) =
self.fields.infcx.replace_late_bound_regions_with_fresh_var(
b.binder_id, self.trace().span(), FnType, b);
// Collect constraints.
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.fields.infcx.region_vars.vars_created_since_mark(mark);
let sig1 =
fold_regions_in_sig(
self.fields.infcx.tcx,
&sig0,
|r| generalize_region(self, mark, new_vars.as_slice(),
sig0.binder_id, &a_map, r));
return Ok(sig1);
fn generalize_region(this: &Lub,
mark: RegionMark,
new_vars: &[RegionVid],
new_scope: NodeId,
a_map: &FnvHashMap<ty::BoundRegion, ty::Region>,
r0: ty::Region)
-> ty::Region {
// Regions that pre-dated the LUB computation stay as they are.
if !is_var_in_set(new_vars, r0) {
assert!(!r0.is_bound());
debug!("generalize_region(r0={}): not new variable", r0);
return 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
// stay as they are.
if !tainted.iter().all(|r| is_var_in_set(new_vars, *r)) {
debug!("generalize_region(r0={}): \
non-new-variables found in {}",
r0, tainted);
assert!(!r0.is_bound());
return r0;
}
// Otherwise, the variable must be associated with at
// least one of the variables representing bound regions
// in both A and B. Replace the variable with the "first"
// bound region from A that we find it to be associated
// with.
for (a_br, a_r) in a_map.iter() {
if tainted.iter().any(|x| x == a_r) {
debug!("generalize_region(r0={}): \
replacing with {}, tainted={}",
r0, *a_br, tainted);
return ty::ReLateBound(new_scope, *a_br);
}
}
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())
}
self.higher_ranked_lub(a, b)
}
fn tys(&self, a: ty::t, b: ty::t) -> cres<ty::t> {
super_lattice_tys(self, a, b)
}
fn trait_refs(&self, a: &ty::TraitRef, b: &ty::TraitRef) -> cres<ty::TraitRef> {
self.higher_ranked_lub(a, b)
}
}

View file

@ -31,10 +31,9 @@ pub use self::skolemize::TypeSkolemizer;
use middle::subst;
use middle::subst::Substs;
use middle::ty::{TyVid, IntVid, FloatVid, RegionVid};
use middle::ty::replace_late_bound_regions;
use middle::ty;
use middle::ty_fold;
use middle::ty_fold::{TypeFolder, TypeFoldable};
use middle::typeck::check::regionmanip::replace_late_bound_regions;
use middle::ty_fold::{HigherRankedFoldable, TypeFolder, TypeFoldable};
use std::cell::{RefCell};
use std::rc::Rc;
use syntax::ast;
@ -61,6 +60,7 @@ pub mod doc;
pub mod equate;
pub mod error_reporting;
pub mod glb;
pub mod higher_ranked;
pub mod lattice;
pub mod lub;
pub mod region_inference;
@ -184,9 +184,9 @@ pub enum SubregionOrigin {
// type of the variable outlives the lifetime bound.
RelateProcBound(Span, ast::NodeId, ty::t),
// The given type parameter was instantiated with the given type,
// Some type parameter was instantiated with the given type,
// and that type must outlive some region.
RelateParamBound(Span, ty::ParamTy, ty::t),
RelateParamBound(Span, ty::t),
// The given region parameter was instantiated with a region
// that must outlive some other region.
@ -233,8 +233,8 @@ pub enum LateBoundRegionConversionTime {
/// when a fn is called
FnCall,
/// when two fn types are compared
FnType,
/// when two higher-ranked types are compared
HigherRankedType,
}
/// Reasons to create a region inference variable
@ -796,8 +796,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
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) {
@ -815,8 +815,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
format!("({})", tstrs.connect(", "))
}
pub fn trait_ref_to_string(&self, t: &ty::TraitRef) -> String {
let t = self.resolve_type_vars_in_trait_ref_if_possible(t);
pub fn trait_ref_to_string(&self, t: &Rc<ty::TraitRef>) -> String {
let t = self.resolve_type_vars_in_trait_ref_if_possible(&**t);
trait_ref_to_string(self.tcx, &t)
}
@ -967,30 +967,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
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()
@ -1073,7 +1062,7 @@ impl SubregionOrigin {
IndexSlice(a) => a,
RelateObjectBound(a) => a,
RelateProcBound(a, _, _) => a,
RelateParamBound(a, _, _) => a,
RelateParamBound(a, _) => a,
RelateRegionParamBound(a) => a,
RelateDefaultParamBound(a, _) => a,
Reborrow(a) => a,
@ -1123,11 +1112,10 @@ impl Repr for SubregionOrigin {
b,
c.repr(tcx))
}
RelateParamBound(a, b, c) => {
format!("RelateParamBound({},{},{})",
RelateParamBound(a, b) => {
format!("RelateParamBound({},{})",
a.repr(tcx),
b.repr(tcx),
c.repr(tcx))
b.repr(tcx))
}
RelateRegionParamBound(a) => {
format!("RelateRegionParamBound({})",

View file

@ -370,404 +370,9 @@ address this problem somehow and make region inference somewhat more
efficient. Note that this is solely a matter of performance, not
expressiveness.
# Skolemization and functions
### Skolemization
One of the trickiest and most subtle aspects of regions is dealing
with the fact that region variables are bound in function types. I
strongly suggest that if you want to understand the situation, you
read this paper (which is, admittedly, very long, but you don't have
to read the whole thing):
http://research.microsoft.com/en-us/um/people/simonpj/papers/higher-rank/
Although my explanation will never compete with SPJ's (for one thing,
his is approximately 100 pages), I will attempt to explain the basic
problem and also how we solve it. Note that the paper only discusses
subtyping, not the computation of LUB/GLB.
The problem we are addressing is that there is a kind of subtyping
between functions with bound region parameters. Consider, for
example, whether the following relation holds:
fn(&'a int) <: |&'b int|? (Yes, a => b)
The answer is that of course it does. These two types are basically
the same, except that in one we used the name `a` and one we used
the name `b`.
In the examples that follow, it becomes very important to know whether
a lifetime is bound in a function type (that is, is a lifetime
parameter) or appears free (is defined in some outer scope).
Therefore, from now on I will write the bindings explicitly, using a
notation like `fn<a>(&'a int)` to indicate that `a` is a lifetime
parameter.
Now let's consider two more function types. Here, we assume that the
`self` lifetime is defined somewhere outside and hence is not a
lifetime parameter bound by the function type (it "appears free"):
fn<a>(&'a int) <: |&'a int|? (Yes, a => self)
This subtyping relation does in fact hold. To see why, you have to
consider what subtyping means. One way to look at `T1 <: T2` is to
say that it means that it is always ok to treat an instance of `T1` as
if it had the type `T2`. So, with our functions, it is always ok to
treat a function that can take pointers with any lifetime as if it
were a function that can only take a pointer with the specific
lifetime `&self`. After all, `&self` is a lifetime, after all, and
the function can take values of any lifetime.
You can also look at subtyping as the *is a* relationship. This amounts
to the same thing: a function that accepts pointers with any lifetime
*is a* function that accepts pointers with some specific lifetime.
So, what if we reverse the order of the two function types, like this:
fn(&'a int) <: <a>|&'a int|? (No)
Does the subtyping relationship still hold? The answer of course is
no. In this case, the function accepts *only the lifetime `&self`*,
so it is not reasonable to treat it as if it were a function that
accepted any lifetime.
What about these two examples:
fn<a,b>(&'a int, &'b int) <: <a>|&'a int, &'a int|? (Yes)
fn<a>(&'a int, &'a int) <: <a,b>|&'a int, &'b int|? (No)
Here, it is true that functions which take two pointers with any two
lifetimes can be treated as if they only accepted two pointers with
the same lifetime, but not the reverse.
## The algorithm
Here is the algorithm we use to perform the subtyping check:
1. Replace all bound regions in the subtype with new variables
2. Replace all bound regions in the supertype with skolemized
equivalents. A "skolemized" region is just a new fresh region
name.
3. Check that the parameter and return types match as normal
4. Ensure that no skolemized regions 'leak' into region variables
visible from "the outside"
Let's walk through some examples and see how this algorithm plays out.
#### First example
We'll start with the first example, which was:
1. fn<a>(&'a T) <: <b>|&'b T|? Yes: a -> b
After steps 1 and 2 of the algorithm we will have replaced the types
like so:
1. fn(&'A T) <: |&'x T|?
Here the upper case `&A` indicates a *region variable*, that is, a
region whose value is being inferred by the system. I also replaced
`&b` with `&x`---I'll use letters late in the alphabet (`x`, `y`, `z`)
to indicate skolemized region names. We can assume they don't appear
elsewhere. Note that neither the sub- nor the supertype bind any
region names anymore (as indicated by the absence of `<` and `>`).
The next step is to check that the parameter types match. Because
parameters are contravariant, this means that we check whether:
&'x T <: &'A T
Region pointers are contravariant so this implies that
&A <= &x
must hold, where `<=` is the subregion relationship. Processing
*this* constrain simply adds a constraint into our graph that `&A <=
&x` and is considered successful (it can, for example, be satisfied by
choosing the value `&x` for `&A`).
So far we have encountered no error, so the subtype check succeeds.
#### The third example
Now let's look first at the third example, which was:
3. fn(&'a T) <: <b>|&'b T|? No!
After steps 1 and 2 of the algorithm we will have replaced the types
like so:
3. fn(&'a T) <: |&'x T|?
This looks pretty much the same as before, except that on the LHS
`&self` was not bound, and hence was left as-is and not replaced with
a variable. The next step is again to check that the parameter types
match. This will ultimately require (as before) that `&self` <= `&x`
must hold: but this does not hold. `self` and `x` are both distinct
free regions. So the subtype check fails.
#### Checking for skolemization leaks
You may be wondering about that mysterious last step in the algorithm.
So far it has not been relevant. The purpose of that last step is to
catch something like *this*:
fn<a>() -> fn(&'a T) <: || -> fn<b>(&'b T)? No.
Here the function types are the same but for where the binding occurs.
The subtype returns a function that expects a value in precisely one
region. The supertype returns a function that expects a value in any
region. If we allow an instance of the subtype to be used where the
supertype is expected, then, someone could call the fn and think that
the return value has type `fn<b>(&'b T)` when it really has type
`fn(&'a T)` (this is case #3, above). Bad.
So let's step through what happens when we perform this subtype check.
We first replace the bound regions in the subtype (the supertype has
no bound regions). This gives us:
fn() -> fn(&'A T) <: || -> fn<b>(&'b T)?
Now we compare the return types, which are covariant, and hence we have:
fn(&'A T) <: <b>|&'b T|?
Here we skolemize the bound region in the supertype to yield:
fn(&'A T) <: |&'x T|?
And then proceed to compare the argument types:
&'x T <: &'A T
&A <= &x
Finally, this is where it gets interesting! This is where an error
*should* be reported. But in fact this will not happen. The reason why
is that `A` is a variable: we will infer that its value is the fresh
region `x` and think that everything is happy. In fact, this behavior
is *necessary*, it was key to the first example we walked through.
The difference between this example and the first one is that the variable
`A` already existed at the point where the skolemization occurred. In
the first example, you had two functions:
fn<a>(&'a T) <: <b>|&'b T|
and hence `&A` and `&x` were created "together". In general, the
intention of the skolemized names is that they are supposed to be
fresh names that could never be equal to anything from the outside.
But when inference comes into play, we might not be respecting this
rule.
So the way we solve this is to add a fourth step that examines the
constraints that refer to skolemized names. Basically, consider a
non-directed verison of the constraint graph. Let `Tainted(x)` be the
set of all things reachable from a skolemized variable `x`.
`Tainted(x)` should not contain any regions that existed before the
step at which the skolemization was performed. So this case here
would fail because `&x` was created alone, but is relatable to `&A`.
## Computing the LUB and GLB
The paper I pointed you at is written for Haskell. It does not
therefore considering subtyping and in particular does not consider
LUB or GLB computation. We have to consider this. Here is the
algorithm I implemented.
First though, let's discuss what we are trying to compute in more
detail. The LUB is basically the "common supertype" and the GLB is
"common subtype"; one catch is that the LUB should be the
*most-specific* common supertype and the GLB should be *most general*
common subtype (as opposed to any common supertype or any common
subtype).
Anyway, to help clarify, here is a table containing some
function pairs and their LUB/GLB:
```
Type 1 Type 2 LUB GLB
fn<a>(&a) fn(&X) fn(&X) fn<a>(&a)
fn(&A) fn(&X) -- fn<a>(&a)
fn<a,b>(&a, &b) fn<x>(&x, &x) fn<a>(&a, &a) fn<a,b>(&a, &b)
fn<a,b>(&a, &b, &a) fn<x,y>(&x, &y, &y) fn<a>(&a, &a, &a) fn<a,b,c>(&a,&b,&c)
```
### Conventions
I use lower-case letters (e.g., `&a`) for bound regions and upper-case
letters for free regions (`&A`). Region variables written with a
dollar-sign (e.g., `$a`). I will try to remember to enumerate the
bound-regions on the fn type as well (e.g., `fn<a>(&a)`).
### High-level summary
Both the LUB and the GLB algorithms work in a similar fashion. They
begin by replacing all bound regions (on both sides) with fresh region
inference variables. Therefore, both functions are converted to types
that contain only free regions. We can then compute the LUB/GLB in a
straightforward way, as described in `combine.rs`. This results in an
interim type T. The algorithms then examine the regions that appear
in T and try to, in some cases, replace them with bound regions to
yield the final result.
To decide whether to replace a region `R` that appears in `T` with a
bound region, the algorithms make use of two bits of information.
First is a set `V` that contains all region variables created as part
of the LUB/GLB computation. `V` will contain the region variables
created to replace the bound regions in the input types, but it also
contains 'intermediate' variables created to represent the LUB/GLB of
individual regions. Basically, when asked to compute the LUB/GLB of a
region variable with another region, the inferencer cannot oblige
immediately since the values of that variables are not known.
Therefore, it creates a new variable that is related to the two
regions. For example, the LUB of two variables `$x` and `$y` is a
fresh variable `$z` that is constrained such that `$x <= $z` and `$y
<= $z`. So `V` will contain these intermediate variables as well.
The other important factor in deciding how to replace a region in T is
the function `Tainted($r)` which, for a region variable, identifies
all regions that the region variable is related to in some way
(`Tainted()` made an appearance in the subtype computation as well).
### LUB
The LUB algorithm proceeds in three steps:
1. Replace all bound regions (on both sides) with fresh region
inference variables.
2. Compute the LUB "as normal", meaning compute the GLB of each
pair of argument types and the LUB of the return types and
so forth. Combine those to a new function type `F`.
3. Replace each region `R` that appears in `F` as follows:
- Let `V` be the set of variables created during the LUB
computational steps 1 and 2, as described in the previous section.
- If `R` is not in `V`, replace `R` with itself.
- If `Tainted(R)` contains a region that is not in `V`,
replace `R` with itself.
- Otherwise, select the earliest variable in `Tainted(R)` that originates
from the left-hand side and replace `R` with the bound region that
this variable was a replacement for.
So, let's work through the simplest example: `fn(&A)` and `fn<a>(&a)`.
In this case, `&a` will be replaced with `$a` and the interim LUB type
`fn($b)` will be computed, where `$b=GLB(&A,$a)`. Therefore, `V =
{$a, $b}` and `Tainted($b) = { $b, $a, &A }`. When we go to replace
`$b`, we find that since `&A \in Tainted($b)` is not a member of `V`,
we leave `$b` as is. When region inference happens, `$b` will be
resolved to `&A`, as we wanted.
Let's look at a more complex one: `fn(&a, &b)` and `fn(&x, &x)`. In
this case, we'll end up with a (pre-replacement) LUB type of `fn(&g,
&h)` and a graph that looks like:
```
$a $b *--$x
\ \ / /
\ $h-* /
$g-----------*
```
Here `$g` and `$h` are fresh variables that are created to represent
the LUB/GLB of things requiring inference. This means that `V` and
`Tainted` will look like:
```
V = {$a, $b, $g, $h, $x}
Tainted($g) = Tainted($h) = { $a, $b, $h, $g, $x }
```
Therefore we replace both `$g` and `$h` with `$a`, and end up
with the type `fn(&a, &a)`.
### GLB
The procedure for computing the GLB is similar. The difference lies
in computing the replacements for the various variables. For each
region `R` that appears in the type `F`, we again compute `Tainted(R)`
and examine the results:
1. If `R` is not in `V`, it is not replaced.
2. Else, if `Tainted(R)` contains only variables in `V`, and it
contains exactly one variable from the LHS and one variable from
the RHS, then `R` can be mapped to the bound version of the
variable from the LHS.
3. Else, if `Tainted(R)` contains no variable from the LHS and no
variable from the RHS, then `R` can be mapped to itself.
4. Else, `R` is mapped to a fresh bound variable.
These rules are pretty complex. Let's look at some examples to see
how they play out.
Out first example was `fn(&a)` and `fn(&X)`. In this case, `&a` will
be replaced with `$a` and we will ultimately compute a
(pre-replacement) GLB type of `fn($g)` where `$g=LUB($a,&X)`.
Therefore, `V={$a,$g}` and `Tainted($g)={$g,$a,&X}. To find the
replacement for `$g` we consult the rules above:
- Rule (1) does not apply because `$g \in V`
- Rule (2) does not apply because `&X \in Tainted($g)`
- Rule (3) does not apply because `$a \in Tainted($g)`
- Hence, by rule (4), we replace `$g` with a fresh bound variable `&z`.
So our final result is `fn(&z)`, which is correct.
The next example is `fn(&A)` and `fn(&Z)`. In this case, we will again
have a (pre-replacement) GLB of `fn(&g)`, where `$g = LUB(&A,&Z)`.
Therefore, `V={$g}` and `Tainted($g) = {$g, &A, &Z}`. In this case,
by rule (3), `$g` is mapped to itself, and hence the result is
`fn($g)`. This result is correct (in this case, at least), but it is
indicative of a case that *can* lead us into concluding that there is
no GLB when in fact a GLB does exist. See the section "Questionable
Results" below for more details.
The next example is `fn(&a, &b)` and `fn(&c, &c)`. In this case, as
before, we'll end up with `F=fn($g, $h)` where `Tainted($g) =
Tainted($h) = {$g, $h, $a, $b, $c}`. Only rule (4) applies and hence
we'll select fresh bound variables `y` and `z` and wind up with
`fn(&y, &z)`.
For the last example, let's consider what may seem trivial, but is
not: `fn(&a, &a)` and `fn(&b, &b)`. In this case, we'll get `F=fn($g,
$h)` where `Tainted($g) = {$g, $a, $x}` and `Tainted($h) = {$h, $a,
$x}`. Both of these sets contain exactly one bound variable from each
side, so we'll map them both to `&a`, resulting in `fn(&a, &a)`, which
is the desired result.
### Shortcomings and correctness
You may be wondering whether this algorithm is correct. The answer is
"sort of". There are definitely cases where they fail to compute a
result even though a correct result exists. I believe, though, that
if they succeed, then the result is valid, and I will attempt to
convince you. The basic argument is that the "pre-replacement" step
computes a set of constraints. The replacements, then, attempt to
satisfy those constraints, using bound identifiers where needed.
For now I will briefly go over the cases for LUB/GLB and identify
their intent:
- LUB:
- The region variables that are substituted in place of bound regions
are intended to collect constraints on those bound regions.
- If Tainted(R) contains only values in V, then this region is unconstrained
and can therefore be generalized, otherwise it cannot.
- GLB:
- The region variables that are substituted in place of bound regions
are intended to collect constraints on those bound regions.
- If Tainted(R) contains exactly one variable from each side, and
only variables in V, that indicates that those two bound regions
must be equated.
- Otherwise, if Tainted(R) references any variables from left or right
side, then it is trying to combine a bound region with a free one or
multiple bound regions, so we need to select fresh bound regions.
Sorry this is more of a shorthand to myself. I will try to write up something
more convincing in the future.
#### Where are the algorithms wrong?
- The pre-replacement computation can fail even though using a
bound-region would have succeeded.
- We will compute GLB(fn(fn($a)), fn(fn($b))) as fn($c) where $c is the
GLB of $a and $b. But if inference finds that $a and $b must be mapped
to regions without a GLB, then this is effectively a failure to compute
the GLB. However, the result `fn<$c>(fn($c))` is a valid GLB.
For a discussion on skolemization and higher-ranked subtyping, please
see the module `middle::typeck::infer::higher_ranked::doc`.
*/

View file

@ -332,7 +332,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
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.
@ -358,7 +358,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
self.tcx.sess.bug("rollover in RegionInference new_bound()");
}
ReLateBound(binder_id, BrFresh(sc))
ReLateBound(debruijn, BrFresh(sc))
}
fn values_are_none(&self) -> bool {

View file

@ -12,18 +12,16 @@
use middle::ty::{BuiltinBounds};
use middle::ty;
use middle::ty::TyVar;
use middle::typeck::check::regionmanip::replace_late_bound_regions;
use middle::typeck::infer::combine::*;
use middle::typeck::infer::{cres, CresCompare};
use middle::typeck::infer::equate::Equate;
use middle::typeck::infer::LateBoundRegionConversionTime::FnType;
use middle::typeck::infer::glb::Glb;
use middle::typeck::infer::higher_ranked::HigherRankedRelations;
use middle::typeck::infer::InferCtxt;
use middle::typeck::infer::lub::Lub;
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 util::ppaux::{Repr};
use syntax::ast::{Onceness, FnStyle, MutImmutable, MutMutable};
@ -65,16 +63,16 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> {
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
debug!("{}.regions({}, {})",
self.tag(),
a.repr(self.fields.infcx.tcx),
b.repr(self.fields.infcx.tcx));
self.fields.infcx.region_vars.make_subregion(Subtype(self.trace()), a, b);
a.repr(self.tcx()),
b.repr(self.tcx()));
self.infcx().region_vars.make_subregion(Subtype(self.trace()), a, b);
Ok(a)
}
fn mts(&self, a: &ty::mt, b: &ty::mt) -> cres<ty::mt> {
debug!("mts({} <: {})",
a.repr(self.fields.infcx.tcx),
b.repr(self.fields.infcx.tcx));
a.repr(self.tcx()),
b.repr(self.tcx()));
if a.mutbl != b.mutbl {
return Err(ty::terr_mutability);
@ -123,7 +121,7 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> {
fn tys(&self, a: ty::t, b: ty::t) -> cres<ty::t> {
debug!("{}.tys({}, {})", self.tag(),
a.repr(self.fields.infcx.tcx), b.repr(self.fields.infcx.tcx));
a.repr(self.tcx()), b.repr(self.tcx()));
if a == b { return Ok(a); }
let infcx = self.fields.infcx;
@ -158,77 +156,11 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> {
}
fn fn_sigs(&self, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
debug!("fn_sigs(a={}, b={})",
a.repr(self.fields.infcx.tcx), b.repr(self.fields.infcx.tcx));
let _indenter = indenter();
self.higher_ranked_sub(a, b)
}
// Rather than checking the subtype relationship between `a` and `b`
// as-is, we need to do some extra work here in order to make sure
// that function subtyping works correctly with respect to regions
//
// Note: this is a subtle algorithm. For a full explanation,
// please see the large comment in `region_inference.rs`.
// Make a mark so we can examine "all bindings that were
// created as part of this type comparison".
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.fields.infcx.replace_late_bound_regions_with_fresh_var(
a.binder_id, self.trace().span(), FnType, a);
// Second, we instantiate each bound region in the supertype with a
// fresh concrete region.
let (skol_map, b_sig) = {
replace_late_bound_regions(self.fields.infcx.tcx, b.binder_id, b, |br| {
let skol = self.fields.infcx.region_vars.new_skolemized(br);
debug!("Bound region {} skolemized to {}",
bound_region_to_string(self.fields.infcx.tcx, "", false, br),
skol);
skol
})
};
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 = 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.fields.infcx.region_vars.vars_created_since_mark(mark);
for (&skol_br, &skol) in skol_map.iter() {
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:
match *tainted_region {
ty::ReInfer(ty::ReVar(ref vid)) => {
if new_vars.iter().any(|x| x == vid) { continue; }
}
_ => {
if *tainted_region == skol { continue; }
}
};
// A is not as polymorphic as B:
if self.a_is_expected() {
debug!("Not as polymorphic!");
return Err(ty::terr_regions_insufficiently_polymorphic(
skol_br, *tainted_region));
} else {
debug!("Overly polymorphic!");
return Err(ty::terr_regions_overly_polymorphic(
skol_br, *tainted_region));
}
}
}
return Ok(sig);
fn trait_refs(&self, a: &ty::TraitRef, b: &ty::TraitRef) -> cres<ty::TraitRef> {
self.higher_ranked_sub(a, b)
}
}

View file

@ -383,7 +383,6 @@ fn check_main_fn_ty(ccx: &CrateCtxt,
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
@ -432,7 +431,6 @@ fn check_start_fn_ty(ccx: &CrateCtxt,
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()))

View file

@ -10,9 +10,9 @@
use middle::ty;
use middle::ty_fold;
use std::cell::Cell;
use syntax::ast;
use syntax::codemap::Span;
/// Defines strategies for handling regions that are omitted. For
@ -104,14 +104,12 @@ impl RegionScope for SpecificRscope {
/// 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),
}
}
@ -119,7 +117,7 @@ impl BindingRscope {
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))
}
}
@ -138,3 +136,40 @@ impl RegionScope for BindingRscope {
}
}
/// A scope which simply shifts the Debruijn index of other scopes
/// to account for binding levels.
pub struct ShiftedRscope<'r> {
base_scope: &'r RegionScope+'r
}
impl<'r> ShiftedRscope<'r> {
pub fn new(base_scope: &'r RegionScope+'r) -> ShiftedRscope<'r> {
ShiftedRscope { base_scope: base_scope }
}
}
impl<'r> RegionScope for ShiftedRscope<'r> {
fn default_region_bound(&self, span: Span) -> Option<ty::Region>
{
self.base_scope.default_region_bound(span)
.map(|r| ty_fold::shift_region(r, 1))
}
fn anon_regions(&self,
span: Span,
count: uint)
-> Result<Vec<ty::Region>, Option<Vec<(String, uint)>>>
{
match self.base_scope.anon_regions(span, count) {
Ok(mut v) => {
for r in v.iter_mut() {
*r = ty_fold::shift_region(*r, 1);
}
Ok(v)
}
Err(errs) => {
Err(errs)
}
}
}
}

View file

@ -252,8 +252,7 @@ pub fn vec_map_to_string<T>(ts: &[T], f: |t: &T| -> String) -> String {
}
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 {
@ -262,11 +261,11 @@ 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 => {}
@ -732,6 +731,9 @@ impl Repr for ty::ParamBounds {
impl Repr for ty::TraitRef {
fn repr(&self, tcx: &ctxt) -> String {
// when printing out the debug representation, we don't need
// to enumerate the `for<...>` etc because the debruijn index
// tells you everything you need to know.
let base = ty::item_path_str(tcx, self.def_id);
let trait_def = ty::lookup_trait_def(tcx, self.def_id);
format!("<{} : {}>",
@ -922,6 +924,14 @@ impl Repr for ty::Generics {
}
}
impl Repr for ty::GenericBounds {
fn repr(&self, tcx: &ctxt) -> String {
format!("GenericBounds(types: {}, regions: {})",
self.types.repr(tcx),
self.regions.repr(tcx))
}
}
impl Repr for ty::ItemVariances {
fn repr(&self, tcx: &ctxt) -> String {
format!("ItemVariances(types={}, \
@ -1140,9 +1150,41 @@ impl UserString for ty::BuiltinBounds {
impl UserString for ty::TraitRef {
fn user_string(&self, tcx: &ctxt) -> String {
let base = ty::item_path_str(tcx, self.def_id);
// Replace any anonymous late-bound regions with named
// variants, using gensym'd identifiers, so that we can
// clearly differentiate between named and unnamed regions in
// the output. We'll probably want to tweak this over time to
// decide just how much information to give.
let mut names = Vec::new();
let (trait_ref, _) = ty::replace_late_bound_regions(tcx, self, |br, debruijn| {
ty::ReLateBound(debruijn, match br {
ty::BrNamed(_, name) => {
names.push(token::get_name(name));
br
}
ty::BrAnon(_) |
ty::BrFresh(_) |
ty::BrEnv => {
let name = token::gensym("r");
names.push(token::get_name(name));
ty::BrNamed(ast_util::local_def(ast::DUMMY_NODE_ID), name)
}
})
});
let names: Vec<_> = names.iter().map(|s| s.get()).collect();
// Let the base string be either `SomeTrait` for `for<'a,'b> SomeTrait`,
// depending on whether there are bound regions.
let path_str = ty::item_path_str(tcx, self.def_id);
let base =
if names.is_empty() {
path_str
} else {
format!("for<{}> {}", names.connect(","), path_str)
};
let trait_def = ty::lookup_trait_def(tcx, self.def_id);
parameterized(tcx, base.as_slice(), &self.substs, &trait_def.generics)
parameterized(tcx, base.as_slice(), &trait_ref.substs, &trait_def.generics)
}
}
@ -1301,3 +1343,8 @@ impl<A:Repr,B:Repr> Repr for (A,B) {
}
}
impl<T:Repr> Repr for ty::Binder<T> {
fn repr(&self, tcx: &ctxt) -> String {
format!("Binder({})", self.value.repr(tcx))
}
}

View file

@ -385,7 +385,7 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session,
syntax::ext::mtwt::clear_tables();
let named_region_map = time(time_passes, "lifetime resolution", (),
|_| middle::resolve_lifetime::krate(&sess, krate));
|_| middle::resolve_lifetime::krate(&sess, krate, &def_map));
time(time_passes, "looking for entry point", (),
|_| middle::entry::find_entry_point(&sess, &ast_map));

View file

@ -1095,7 +1095,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> {
fd: &'v ast::FnDecl,
b: &'v ast::Block,
s: Span,
_: NodeId) {
_: ast::NodeId) {
if generated_code(s) {
return;
}

View file

@ -14,9 +14,6 @@
*/
// This is only used by tests, hence allow dead code.
#![allow(dead_code)]
use driver::diagnostic;
use driver::diagnostic::Emitter;
use driver::driver;
@ -25,17 +22,20 @@ use middle::region;
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;
@ -97,12 +97,12 @@ fn errors(msgs: &[&str]) -> (Box<Emitter+Send>, uint) {
(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 =
@ -125,7 +125,7 @@ fn test_env(_test_name: &str,
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();
@ -164,6 +164,7 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
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,
@ -237,14 +238,6 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
}
}
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);
@ -255,36 +248,91 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
}
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_int(&self) -> ty::t {
ty::mk_int()
pub fn t_nil(&self) -> ty::t {
ty::mk_nil(self.infcx.tcx)
}
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_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_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_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 {
@ -301,10 +349,6 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
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,
@ -345,31 +389,11 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
}
}
}
/// 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);
@ -381,8 +405,7 @@ fn contravariant_region_ptr_ok() {
#[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();
@ -398,114 +421,273 @@ fn contravariant_region_ptr_err() {
#[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);
})
}

View file

@ -519,8 +519,9 @@ pub fn get_res_dtor(ccx: &CrateContext,
let name = csearch::get_symbol(&ccx.sess().cstore, did);
let class_ty = ty::lookup_item_type(tcx, parent_id).ty.subst(tcx, substs);
let llty = type_of_dtor(ccx, class_ty);
let dtor_ty = ty::mk_ctor_fn(ccx.tcx(), ast::DUMMY_NODE_ID,
&[glue::get_drop_glue_type(ccx, t)], ty::mk_nil(ccx.tcx()));
let dtor_ty = ty::mk_ctor_fn(ccx.tcx(),
&[glue::get_drop_glue_type(ccx, t)],
ty::mk_nil(ccx.tcx()));
get_extern_fn(ccx,
&mut *ccx.externs().borrow_mut(),
name.as_slice(),

View file

@ -269,7 +269,6 @@ pub fn trans_unboxing_shim(bcx: Block,
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
@ -294,7 +293,6 @@ pub fn trans_unboxing_shim(bcx: Block,
// 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),
@ -434,6 +432,8 @@ pub fn trans_fn_ref_with_substs(
substs.repr(tcx));
assert!(substs.types.all(|t| !ty::type_needs_infer(*t)));
assert!(substs.types.all(|t| !ty::type_has_escaping_regions(*t)));
let substs = substs.erase_regions();
// Load the info for the appropriate trait if necessary.
match ty::trait_of_item(tcx, def_id) {
@ -467,13 +467,13 @@ pub fn trans_fn_ref_with_substs(
let impl_or_trait_item = ty::impl_or_trait_item(tcx, source_id);
match impl_or_trait_item {
ty::MethodTraitItem(method) => {
let trait_ref = ty::impl_trait_ref(tcx, impl_id)
.expect("could not find trait_ref for impl with \
default methods");
let trait_ref = ty::impl_trait_ref(tcx, impl_id).unwrap();
let trait_ref = ty::erase_late_bound_regions(tcx, &trait_ref);
// Compute the first substitution
let first_subst = make_substs_for_receiver_types(
tcx, &*trait_ref, &*method);
let first_subst =
make_substs_for_receiver_types(tcx, &*trait_ref, &*method)
.erase_regions();
// And compose them
let new_substs = first_subst.subst(tcx, &substs);
@ -663,7 +663,7 @@ pub fn trans_lang_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
trans_fn_ref_with_substs_to_callee(bcx,
did,
0,
subst::Substs::empty())
subst::Substs::trans_empty())
},
ArgVals(args),
dest)

View file

@ -486,7 +486,7 @@ pub fn trans_unboxed_closure<'blk, 'tcx>(
let llfn = get_or_create_declaration_if_unboxed_closure(
bcx,
closure_id,
&bcx.fcx.param_substs.substs).unwrap();
bcx.fcx.param_substs.substs()).unwrap();
let function_type = (*bcx.tcx().unboxed_closures.borrow())[closure_id]
.closure_type

View file

@ -191,10 +191,21 @@ pub type ExternMap = FnvHashMap<String, ValueRef>;
// Here `self_ty` is the real type of the self parameter to this method. It
// will only be set in the case of default methods.
pub struct param_substs {
pub substs: subst::Substs,
substs: subst::Substs,
}
impl param_substs {
pub fn new(substs: subst::Substs) -> param_substs {
assert!(substs.types.all(|t| !ty::type_needs_infer(*t)));
assert!(substs.types.all(|t| !ty::type_has_params(*t)));
assert!(substs.types.all(|t| !ty::type_has_escaping_regions(*t)));
param_substs { substs: substs.erase_regions() }
}
pub fn substs(&self) -> &subst::Substs {
&self.substs
}
pub fn empty() -> param_substs {
param_substs {
substs: subst::Substs::trans_empty(),
@ -822,6 +833,8 @@ pub fn fulfill_obligation(ccx: &CrateContext,
None => { }
}
debug!("trans fulfill_obligation: trait_ref={}", trait_ref.repr(ccx.tcx()));
ty::populate_implementations_for_trait_if_necessary(tcx, trait_ref.def_id);
let infcx = infer::new_infer_ctxt(tcx);

View file

@ -1410,7 +1410,7 @@ pub fn create_function_debug_context(cx: &CrateContext,
file_metadata: DIFile,
name_to_append_suffix_to: &mut String)
-> DIArray {
let self_type = param_substs.substs.self_ty();
let self_type = param_substs.substs().self_ty();
// Only true for static default methods:
let has_self_type = self_type.is_some();
@ -1467,7 +1467,7 @@ pub fn create_function_debug_context(cx: &CrateContext,
}
// Handle other generic parameters
let actual_types = param_substs.substs.types.get_slice(subst::FnSpace);
let actual_types = param_substs.substs().types.get_slice(subst::FnSpace);
for (index, &ast::TyParam{ ident, .. }) in generics.ty_params.iter().enumerate() {
let actual_type = actual_types[index];
// Add actual type name to <...> clause of function name

View file

@ -329,12 +329,12 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
bcx.ty_to_string(unsized_ty)).as_slice())
},
&ty::UnsizeVtable(ty::TyTrait { ref principal, .. }, _) => {
let substs = principal.substs.with_self_ty(unsized_ty);
let substs = principal.substs.with_self_ty(unsized_ty).erase_regions();
let trait_ref =
Rc::new(ty::TraitRef { def_id: principal.def_id,
substs: substs });
let trait_ref =
trait_ref.subst(bcx.tcx(), &bcx.fcx.param_substs.substs);
trait_ref.subst(bcx.tcx(), bcx.fcx.param_substs.substs());
let box_ty = mk_ty(unsized_ty);
PointerCast(bcx,
meth::get_vtable(bcx, box_ty, trait_ref),
@ -1122,7 +1122,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
.map(|t| (*t).clone())
.unwrap();
let trait_ref =
trait_ref.subst(bcx.tcx(), &bcx.fcx.param_substs.substs);
trait_ref.subst(bcx.tcx(), bcx.fcx.param_substs.substs());
let datum = unpack_datum!(bcx, trans(bcx, &**val));
meth::trans_trait_cast(bcx, datum, expr.id,
trait_ref, dest)

View file

@ -536,7 +536,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
let _icx = push_ctxt("foreign::build_foreign_fn");
let fnty = ty::node_id_to_type(ccx.tcx(), id);
let mty = fnty.subst(ccx.tcx(), &param_substs.substs);
let mty = fnty.subst(ccx.tcx(), param_substs.substs());
let tys = foreign_types_for_fn_ty(ccx, mty);
unsafe { // unsafe because we call LLVM operations
@ -558,7 +558,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
let _icx = push_ctxt("foreign::foreign::build_rust_fn");
let tcx = ccx.tcx();
let t = ty::node_id_to_type(tcx, id).subst(
ccx.tcx(), &param_substs.substs);
ccx.tcx(), param_substs.substs());
let ps = ccx.tcx().map.with_path(id, |path| {
let abi = Some(ast_map::PathName(special_idents::clownshoe_abi.name));

View file

@ -287,8 +287,9 @@ fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
val, *ty);
}
let dtor_ty = ty::mk_ctor_fn(variant_cx.tcx(), ast::DUMMY_NODE_ID,
&[get_drop_glue_type(bcx.ccx(), t)], ty::mk_nil(bcx.tcx()));
let dtor_ty = ty::mk_ctor_fn(bcx.tcx(),
&[get_drop_glue_type(bcx.ccx(), t)],
ty::mk_nil(bcx.tcx()));
let (_, variant_cx) = invoke(variant_cx, dtor_addr, args, dtor_ty, None, false);
variant_cx.fcx.pop_and_trans_custom_cleanup_scope(variant_cx, field_scope);

View file

@ -137,8 +137,11 @@ pub fn trans_method_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}) => {
let trait_ref =
Rc::new(trait_ref.subst(bcx.tcx(),
&bcx.fcx.param_substs.substs));
bcx.fcx.param_substs.substs()));
let span = bcx.tcx().map.span(method_call.expr_id);
debug!("method_call={} trait_ref={}",
method_call,
trait_ref.repr(bcx.tcx()));
let origin = fulfill_obligation(bcx.ccx(),
span,
(*trait_ref).clone());
@ -609,9 +612,6 @@ pub fn get_vtable(bcx: Block,
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,

View file

@ -63,9 +63,8 @@ pub fn monomorphic_fn(ccx: &CrateContext,
None => ()
}
let psubsts = param_substs {
substs: (*real_substs).clone(),
};
debug!("creating param_substs with real_substs={}", real_substs.repr(ccx.tcx()));
let psubsts = param_substs::new((*real_substs).clone());
debug!("monomorphic_fn(\
fn_id={}, \

View file

@ -1154,7 +1154,7 @@ pub enum Ty_ {
/// Type parameters are stored in the Path itself
TyPath(Path, Option<TyParamBounds>, NodeId), // for #7264; see above
/// A type like `for<'a> Foo<&'a Bar>`
TyPolyTraitRef(P<PolyTraitRef>),
TyPolyTraitRef(TyParamBounds),
/// A "qualified path", e.g. `<Vec<T> as SomeTrait>::SomeType`
TyQPath(P<QPath>),
/// No-op; kept solely so that we can pretty-print faithfully

View file

@ -494,10 +494,10 @@ impl<'a, 'v, O: IdVisitingOperation> Visitor<'v> for IdVisitor<'a, O> {
}
visit::walk_fn(self,
function_kind,
function_declaration,
block,
span);
function_kind,
function_declaration,
block,
span);
if !self.pass_through_items {
match function_kind {

View file

@ -58,8 +58,6 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
("quote", Active),
("linkage", Active),
("struct_inherit", Removed),
("overloaded_calls", Active),
("unboxed_closure_sugar", Active),
("quad_precision_float", Removed),
@ -102,7 +100,7 @@ enum Status {
/// A set of features to be used by later passes.
pub struct Features {
pub default_type_params: bool,
pub overloaded_calls: bool,
pub unboxed_closures: bool,
pub rustc_diagnostic_macros: bool,
pub import_shadowing: bool,
pub visible_private_types: bool,
@ -113,7 +111,7 @@ impl Features {
pub fn new() -> Features {
Features {
default_type_params: false,
overloaded_calls: false,
unboxed_closures: false,
rustc_diagnostic_macros: false,
import_shadowing: false,
visible_private_types: false,
@ -381,7 +379,7 @@ impl<'a, 'v> Visitor<'v> for Context<'a> {
fn_decl: &'v ast::FnDecl,
block: &'v ast::Block,
span: Span,
_: NodeId) {
_node_id: NodeId) {
match fn_kind {
visit::FkItemFn(_, _, _, abi) if abi == RustIntrinsic => {
self.gate_feature("intrinsics",
@ -392,6 +390,19 @@ impl<'a, 'v> Visitor<'v> for Context<'a> {
}
visit::walk_fn(self, fn_kind, fn_decl, block, span);
}
fn visit_path_parameters(&mut self, path_span: Span, parameters: &'v ast::PathParameters) {
match *parameters {
ast::ParenthesizedParameters(..) => {
self.gate_feature("unboxed_closures",
path_span,
"parenthetical parameter notation is subject to change");
}
ast::AngleBracketedParameters(..) => { }
}
visit::walk_path_parameters(self, path_span, parameters)
}
}
pub fn check_crate(span_handler: &SpanHandler, krate: &ast::Crate) -> (Features, Vec<Span>) {
@ -446,7 +457,7 @@ pub fn check_crate(span_handler: &SpanHandler, krate: &ast::Crate) -> (Features,
(Features {
default_type_params: cx.has_feature("default_type_params"),
overloaded_calls: cx.has_feature("overloaded_calls"),
unboxed_closures: cx.has_feature("unboxed_closures"),
rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"),
import_shadowing: cx.has_feature("import_shadowing"),
visible_private_types: cx.has_feature("visible_private_types"),

View file

@ -445,10 +445,12 @@ pub fn noop_fold_ty<T: Folder>(t: P<Ty>, fld: &mut T) -> P<Ty> {
TyFixedLengthVec(ty, e) => {
TyFixedLengthVec(fld.fold_ty(ty), fld.fold_expr(e))
}
TyTypeof(expr) => TyTypeof(fld.fold_expr(expr)),
TyPolyTraitRef(poly_trait_ref) => {
TyPolyTraitRef(poly_trait_ref.map(|p| fld.fold_poly_trait_ref(p)))
},
TyTypeof(expr) => {
TyTypeof(fld.fold_expr(expr))
}
TyPolyTraitRef(bounds) => {
TyPolyTraitRef(bounds.move_map(|b| fld.fold_ty_param_bound(b)))
}
},
span: fld.new_span(span)
})

View file

@ -1023,10 +1023,21 @@ impl<'a> Parser<'a> {
self.parse_ty_bare_fn_or_ty_closure(lifetime_defs)
} else if self.token == token::ModSep ||
self.token.is_ident() ||
self.token.is_path() {
self.token.is_path()
{
let trait_ref = self.parse_trait_ref();
TyPolyTraitRef(P(PolyTraitRef { bound_lifetimes: lifetime_defs,
trait_ref: trait_ref }))
let poly_trait_ref = ast::PolyTraitRef { bound_lifetimes: lifetime_defs,
trait_ref: trait_ref };
let other_bounds = if self.eat(&token::BinOp(token::Plus)) {
self.parse_ty_param_bounds()
} else {
OwnedSlice::empty()
};
let all_bounds =
Some(TraitTyParamBound(poly_trait_ref)).into_iter()
.chain(other_bounds.into_vec().into_iter())
.collect();
ast::TyPolyTraitRef(all_bounds)
} else {
self.parse_ty_closure(lifetime_defs)
}

View file

@ -739,8 +739,8 @@ impl<'a> State<'a> {
ast::TyPath(ref path, ref bounds, _) => {
try!(self.print_bounded_path(path, bounds));
}
ast::TyPolyTraitRef(ref poly_trait_ref) => {
try!(self.print_poly_trait_ref(&**poly_trait_ref));
ast::TyPolyTraitRef(ref bounds) => {
try!(self.print_bounds("", bounds));
}
ast::TyQPath(ref qpath) => {
try!(word(&mut self.s, "<"));

View file

@ -78,6 +78,9 @@ pub trait Visitor<'v> {
fn visit_ty_method(&mut self, t: &'v TypeMethod) { walk_ty_method(self, t) }
fn visit_trait_item(&mut self, t: &'v TraitItem) { walk_trait_item(self, t) }
fn visit_trait_ref(&mut self, t: &'v TraitRef) { walk_trait_ref(self, t) }
fn visit_ty_param_bound(&mut self, bounds: &'v TyParamBound) {
walk_ty_param_bound(self, bounds)
}
fn visit_poly_trait_ref(&mut self, t: &'v PolyTraitRef) {
walk_poly_trait_ref(self, t)
}
@ -119,6 +122,12 @@ pub trait Visitor<'v> {
fn visit_path(&mut self, path: &'v Path, _id: ast::NodeId) {
walk_path(self, path)
}
fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v PathSegment) {
walk_path_segment(self, path_span, path_segment)
}
fn visit_path_parameters(&mut self, path_span: Span, path_parameters: &'v PathParameters) {
walk_path_parameters(self, path_span, path_parameters)
}
fn visit_attribute(&mut self, _attr: &'v Attribute) {}
}
@ -170,7 +179,7 @@ pub fn walk_view_item<'v, V: Visitor<'v>>(visitor: &mut V, vi: &'v ViewItem) {
ViewPathGlob(ref path, id) => {
visitor.visit_path(path, id);
}
ViewPathList(ref path, ref list, _) => {
ViewPathList(ref prefix, ref list, _) => {
for id in list.iter() {
match id.node {
PathListIdent { name, .. } => {
@ -179,7 +188,10 @@ pub fn walk_view_item<'v, V: Visitor<'v>>(visitor: &mut V, vi: &'v ViewItem) {
PathListMod { .. } => ()
}
}
walk_path(visitor, path);
// Note that the `prefix` here is not a complete
// path, so we don't use `visit_path`.
walk_path(visitor, prefix);
}
}
}
@ -212,7 +224,7 @@ pub fn walk_poly_trait_ref<'v, V>(visitor: &mut V,
trait_ref: &'v PolyTraitRef)
where V: Visitor<'v>
{
walk_lifetime_decls(visitor, &trait_ref.bound_lifetimes);
walk_lifetime_decls_helper(visitor, &trait_ref.bound_lifetimes);
visitor.visit_trait_ref(&trait_ref.trait_ref);
}
@ -290,7 +302,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
}
ItemTrait(ref generics, _, ref bounds, ref methods) => {
visitor.visit_generics(generics);
walk_ty_param_bounds(visitor, bounds);
walk_ty_param_bounds_helper(visitor, bounds);
for method in methods.iter() {
visitor.visit_trait_item(method)
}
@ -363,29 +375,29 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
visitor.visit_ty(&*argument.ty)
}
walk_fn_ret_ty(visitor, &function_declaration.decl.output);
walk_ty_param_bounds(visitor, &function_declaration.bounds);
walk_lifetime_decls(visitor, &function_declaration.lifetimes);
walk_ty_param_bounds_helper(visitor, &function_declaration.bounds);
walk_lifetime_decls_helper(visitor, &function_declaration.lifetimes);
}
TyProc(ref function_declaration) => {
for argument in function_declaration.decl.inputs.iter() {
visitor.visit_ty(&*argument.ty)
}
walk_fn_ret_ty(visitor, &function_declaration.decl.output);
walk_ty_param_bounds(visitor, &function_declaration.bounds);
walk_lifetime_decls(visitor, &function_declaration.lifetimes);
walk_ty_param_bounds_helper(visitor, &function_declaration.bounds);
walk_lifetime_decls_helper(visitor, &function_declaration.lifetimes);
}
TyBareFn(ref function_declaration) => {
for argument in function_declaration.decl.inputs.iter() {
visitor.visit_ty(&*argument.ty)
}
walk_fn_ret_ty(visitor, &function_declaration.decl.output);
walk_lifetime_decls(visitor, &function_declaration.lifetimes);
walk_lifetime_decls_helper(visitor, &function_declaration.lifetimes);
}
TyPath(ref path, ref opt_bounds, id) => {
visitor.visit_path(path, id);
match *opt_bounds {
Some(ref bounds) => {
walk_ty_param_bounds(visitor, bounds);
walk_ty_param_bounds_helper(visitor, bounds);
}
None => { }
}
@ -399,8 +411,8 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
visitor.visit_ty(&**ty);
visitor.visit_expr(&**expression)
}
TyPolyTraitRef(ref poly_trait_ref) => {
visitor.visit_poly_trait_ref(&**poly_trait_ref)
TyPolyTraitRef(ref bounds) => {
walk_ty_param_bounds_helper(visitor, bounds)
}
TyTypeof(ref expression) => {
visitor.visit_expr(&**expression)
@ -409,8 +421,8 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
}
}
fn walk_lifetime_decls<'v, V: Visitor<'v>>(visitor: &mut V,
lifetimes: &'v Vec<LifetimeDef>) {
pub fn walk_lifetime_decls_helper<'v, V: Visitor<'v>>(visitor: &mut V,
lifetimes: &'v Vec<LifetimeDef>) {
for l in lifetimes.iter() {
visitor.visit_lifetime_decl(l);
}
@ -418,24 +430,35 @@ fn walk_lifetime_decls<'v, V: Visitor<'v>>(visitor: &mut V,
pub fn walk_path<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path) {
for segment in path.segments.iter() {
visitor.visit_ident(path.span, segment.identifier);
visitor.visit_path_segment(path.span, segment);
}
}
match segment.parameters {
ast::AngleBracketedParameters(ref data) => {
for typ in data.types.iter() {
visitor.visit_ty(&**typ);
}
for lifetime in data.lifetimes.iter() {
visitor.visit_lifetime_ref(lifetime);
}
pub fn walk_path_segment<'v, V: Visitor<'v>>(visitor: &mut V,
path_span: Span,
segment: &'v PathSegment) {
visitor.visit_ident(path_span, segment.identifier);
visitor.visit_path_parameters(path_span, &segment.parameters);
}
pub fn walk_path_parameters<'v, V: Visitor<'v>>(visitor: &mut V,
_path_span: Span,
path_parameters: &'v PathParameters) {
match *path_parameters {
ast::AngleBracketedParameters(ref data) => {
for typ in data.types.iter() {
visitor.visit_ty(&**typ);
}
ast::ParenthesizedParameters(ref data) => {
for typ in data.inputs.iter() {
visitor.visit_ty(&**typ);
}
for typ in data.output.iter() {
visitor.visit_ty(&**typ);
}
for lifetime in data.lifetimes.iter() {
visitor.visit_lifetime_ref(lifetime);
}
}
ast::ParenthesizedParameters(ref data) => {
for typ in data.inputs.iter() {
visitor.visit_ty(&**typ);
}
for typ in data.output.iter() {
visitor.visit_ty(&**typ);
}
}
}
@ -511,32 +534,37 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V,
}
}
pub fn walk_ty_param_bounds<'v, V: Visitor<'v>>(visitor: &mut V,
bounds: &'v OwnedSlice<TyParamBound>) {
pub fn walk_ty_param_bounds_helper<'v, V: Visitor<'v>>(visitor: &mut V,
bounds: &'v OwnedSlice<TyParamBound>) {
for bound in bounds.iter() {
match *bound {
TraitTyParamBound(ref typ) => {
visitor.visit_poly_trait_ref(typ)
}
RegionTyParamBound(ref lifetime) => {
visitor.visit_lifetime_ref(lifetime);
}
visitor.visit_ty_param_bound(bound)
}
}
pub fn walk_ty_param_bound<'v, V: Visitor<'v>>(visitor: &mut V,
bound: &'v TyParamBound) {
match *bound {
TraitTyParamBound(ref typ) => {
visitor.visit_poly_trait_ref(typ);
}
RegionTyParamBound(ref lifetime) => {
visitor.visit_lifetime_ref(lifetime);
}
}
}
pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics) {
for type_parameter in generics.ty_params.iter() {
walk_ty_param_bounds(visitor, &type_parameter.bounds);
walk_ty_param_bounds_helper(visitor, &type_parameter.bounds);
match type_parameter.default {
Some(ref ty) => visitor.visit_ty(&**ty),
None => {}
}
}
walk_lifetime_decls(visitor, &generics.lifetimes);
walk_lifetime_decls_helper(visitor, &generics.lifetimes);
for predicate in generics.where_clause.predicates.iter() {
visitor.visit_ident(predicate.span, predicate.ident);
walk_ty_param_bounds(visitor, &predicate.bounds);
walk_ty_param_bounds_helper(visitor, &predicate.bounds);
}
}

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(unboxed_closures, overloaded_calls)]
#![feature(unboxed_closures)]
#![crate_type = "rlib"]
pub fn inner<F>(f: F) -> F {

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(unboxed_closures, overloaded_calls)]
#![feature(unboxed_closures)]
#[inline]
pub fn has_closures() -> uint {

View file

@ -40,7 +40,7 @@
// ignore-android see #10393 #13206
#![feature(slicing_syntax, unboxed_closures, overloaded_calls)]
#![feature(slicing_syntax, unboxed_closures)]
extern crate libc;

View file

@ -41,7 +41,7 @@
// no-pretty-expanded FIXME #15189
#![allow(non_snake_case)]
#![feature(unboxed_closures, overloaded_calls)]
#![feature(unboxed_closures)]
use std::iter::AdditiveIterator;
use std::mem;

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(overloaded_calls)]
#![feature(unboxed_closures)]
use std::ops::{Fn, FnMut, FnOnce};

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(overloaded_calls)]
#![feature(overloaded_calls, unboxed_closures)]
fn a<F:Fn(int, int) -> int>(mut f: F) {
let g = &mut f;

View file

@ -14,12 +14,9 @@ struct Foo<'a,'b> {
}
impl<'a,'b> Foo<'a,'b> {
// The number of errors is related to the way invariance works.
fn bar(self: Foo<'b,'a>) {}
//~^ ERROR mismatched types: expected `Foo<'a, 'b>`, found `Foo<'b, 'a>`
//~^^ ERROR mismatched types: expected `Foo<'a, 'b>`, found `Foo<'b, 'a>`
//~^^^ ERROR mismatched types: expected `Foo<'b, 'a>`, found `Foo<'a, 'b>`
//~^^^^ ERROR mismatched types: expected `Foo<'b, 'a>`, found `Foo<'a, 'b>`
}
fn main() {}

View file

@ -0,0 +1,60 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test HRTB supertraits with several levels of expansion required.
trait Foo<'tcx>
{
fn foo(&'tcx self) -> &'tcx int;
}
trait Bar<'ccx>
: for<'tcx> Foo<'tcx>
{
fn bar(&'ccx self) -> &'ccx int;
}
trait Baz
: for<'ccx> Bar<'ccx>
{
fn dummy(&self);
}
trait Qux
: Bar<'static>
{
fn dummy(&self);
}
fn want_foo_for_any_tcx<F>(f: &F)
where F : for<'tcx> Foo<'tcx>
{
}
fn want_bar_for_any_ccx<B>(b: &B)
where B : for<'ccx> Bar<'ccx>
{
}
fn want_baz<B>(b: &B)
where B : Baz
{
want_foo_for_any_tcx(b);
want_bar_for_any_ccx(b);
}
fn want_qux<B>(b: &B)
where B : Qux
{
want_foo_for_any_tcx(b);
want_bar_for_any_ccx(b); //~ ERROR not implemented
}
fn main() {}

View file

@ -0,0 +1,58 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test a trait (`Bar`) with a higher-ranked supertrait.
trait Foo<'tcx>
{
fn foo(&'tcx self) -> &'tcx int;
}
trait Bar<'ccx>
: for<'tcx> Foo<'tcx>
{
fn bar(&'ccx self) -> &'ccx int;
}
fn want_foo_for_some_tcx<'x,F>(f: &'x F)
where F : Foo<'x>
{
want_foo_for_some_tcx(f);
want_foo_for_any_tcx(f); //~ ERROR not implemented
}
fn want_foo_for_any_tcx<F>(f: &F)
where F : for<'tcx> Foo<'tcx>
{
want_foo_for_some_tcx(f);
want_foo_for_any_tcx(f);
}
fn want_bar_for_some_ccx<'x,B>(b: &B)
where B : Bar<'x>
{
want_foo_for_some_tcx(b);
want_foo_for_any_tcx(b);
want_bar_for_some_ccx(b);
want_bar_for_any_ccx(b); //~ ERROR not implemented
}
fn want_bar_for_any_ccx<B>(b: &B)
where B : for<'ccx> Bar<'ccx>
{
want_foo_for_some_tcx(b);
want_foo_for_any_tcx(b);
want_bar_for_some_ccx(b);
want_bar_for_any_ccx(b);
}
fn main() {}

View file

@ -0,0 +1,35 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that the `'a` in the where clause correctly links the region
// of the output to the region of the input.
trait FnLike<A,R> {
fn call(&self, arg: A) -> R;
}
fn call_repeatedly<F>(f: F)
where F : for<'a> FnLike<&'a int, &'a int>
{
// Result is stored: cannot re-assign `x`
let mut x = 3;
let y = f.call(&x);
x = 5; //~ ERROR cannot assign
// Result is not stored: can re-assign `x`
let mut x = 3;
f.call(&x);
f.call(&x);
f.call(&x);
x = 5;
}
fn main() {
}

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(overloaded_calls)]
#![feature(unboxed_closures)]
use std::{fmt, ops};

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(overloaded_calls)]
#![feature(overloaded_calls, unboxed_closures)]
// Make sure we don't ICE when making an overloaded call with the
// wrong arity.

View file

@ -12,7 +12,7 @@
// when a type error or unconstrained type variable propagates
// into it.
#![feature(overloaded_calls)]
#![feature(unboxed_closures)]
fn main() {
(return)((),());

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(overloaded_calls)]
#![feature(unboxed_closures)]
use std::ops::FnMut;

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(overloaded_calls)]
#![feature(unboxed_closures)]
use std::ops::FnMut;

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(unboxed_closure_sugar, unboxed_closures, overloaded_calls)]
#![feature(unboxed_closures, overloaded_calls)]
use std::ops::FnMut;

View file

@ -44,6 +44,9 @@ fn bar<'a>(x: &'a int) {
// &'a CAN be declared on functions and used then:
fn g<'a>(a: &'a int) { } // OK
fn h(a: for<'a>|&'a int|) { } // OK
// But not in the bound of a closure, it's not in scope *there*
fn i(a: for<'a>|&int|:'a) { } //~ ERROR undeclared lifetime
}
// Test nesting of lifetimes in fn type declarations

View file

@ -0,0 +1,43 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// A zero-dependency test that covers some basic traits, default
// methods, etc. When mucking about with basic type system stuff I
// often encounter problems in the iterator trait, so it's useful to
// have hanging around. -nmatsakis
// error-pattern: requires `start` lang_item
#![no_std]
#![feature(lang_items)]
#[lang = "sized"]
pub trait Sized for Sized? {
// Empty.
}
pub mod std {
pub mod clone {
pub trait Clone {
fn clone(&self) -> Self;
}
}
}
pub struct ContravariantLifetime<'a>;
impl <'a> ::std::clone::Clone for ContravariantLifetime<'a> {
#[inline]
fn clone(&self) -> ContravariantLifetime<'a> {
match *self { ContravariantLifetime => ContravariantLifetime, }
}
}
fn main() { }

View file

@ -0,0 +1,40 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// A zero-dependency test that covers some basic traits, default
// methods, etc. When mucking about with basic type system stuff I
// often encounter problems in the iterator trait, so it's useful to
// have hanging around. -nmatsakis
// error-pattern: requires `start` lang_item
#![no_std]
#![feature(lang_items)]
#[lang = "sized"]
pub trait Sized for Sized? {
// Empty.
}
#[unstable = "Definition may change slightly after trait reform"]
pub trait PartialEq for Sized? {
/// This method tests for `self` and `other` values to be equal, and is used by `==`.
fn eq(&self, other: &Self) -> bool;
}
#[cfg(not(stage0))] // NOTE(stage0): remove cfg after a snapshot
#[unstable = "Trait is unstable."]
impl<'a, Sized? T: PartialEq> PartialEq for &'a T {
#[inline]
fn eq(&self, other: & &'a T) -> bool { PartialEq::eq(*self, *other) }
}
fn main() { }

View file

@ -14,7 +14,6 @@ struct Foo {
impl Foo {
fn foo(self: int, x: int) -> int { //~ ERROR mismatched self type
//~^ ERROR not a valid type for `self`
self.f + x
}
}
@ -25,15 +24,26 @@ struct Bar<T> {
impl<T> Bar<T> {
fn foo(self: Bar<int>, x: int) -> int { //~ ERROR mismatched self type
//~^ ERROR not a valid type for `self`
x
}
fn bar(self: &Bar<uint>, x: int) -> int { //~ ERROR mismatched self type
//~^ ERROR not a valid type for `self`
x
}
}
trait SomeTrait {
fn dummy1(&self);
fn dummy2(&self);
fn dummy3(&self);
}
impl<'a, T> SomeTrait for &'a Bar<T> {
fn dummy1(self: &&'a Bar<T>) { }
fn dummy2(self: &Bar<T>) {} //~ ERROR mismatched self type
fn dummy3(self: &&Bar<T>) {} //~ ERROR lifetime mismatch
//~^ ERROR lifetime mismatch
}
fn main() {
let foo = box Foo {
f: 1,

View file

@ -11,16 +11,16 @@
// Test interaction between unboxed closure sugar and default type
// parameters (should be exactly as if angle brackets were used).
#![feature(default_type_params)]
#![feature(default_type_params, unboxed_closures)]
#![allow(dead_code)]
struct Foo<T,U,V=T> {
t: T, u: U
trait Foo<T,U,V=T> {
fn dummy(&self, t: T, u: U, v: V);
}
trait Eq<X> { }
impl<X> Eq<X> for X { }
fn eq<A,B:Eq<A>>() { }
trait Eq<Sized? X> for Sized? { }
impl<Sized? X> Eq<X> for X { }
fn eq<Sized? A,Sized? B>() where A : Eq<B> { }
fn test<'a,'b>() {
// Parens are equivalent to omitting default in angle.

View file

@ -13,15 +13,16 @@
// angle brackets. This test covers only simple types and in
// particular doesn't test bound regions.
#![feature(unboxed_closures)]
#![allow(dead_code)]
struct Foo<T,U> {
t: T, u: U
trait Foo<T,U> {
fn dummy(&self, t: T, u: U);
}
trait Eq<X> { }
impl<X> Eq<X> for X { }
fn eq<A,B:Eq<A>>() { }
trait Eq<Sized? X> for Sized? { }
impl<Sized? X> Eq<X> for X { }
fn eq<Sized? A,Sized? B:Eq<A>>() { }
fn test<'a,'b>() {
// No errors expected:
@ -31,6 +32,22 @@ fn test<'a,'b>() {
eq::< Foo<(int,uint),uint>, Foo(int,uint) -> uint >();
eq::< Foo<(&'a int,&'b uint),uint>, Foo(&'a int,&'b uint) -> uint >();
// Test that anonymous regions in `()` form are equivalent
// to fresh bound regions, and that we can intermingle
// named and anonymous as we choose:
eq::< for<'a,'b> Foo<(&'a int,&'b uint),uint>,
for<'a,'b> Foo(&'a int,&'b uint) -> uint >();
eq::< for<'a,'b> Foo<(&'a int,&'b uint),uint>,
for<'a> Foo(&'a int,&uint) -> uint >();
eq::< for<'a,'b> Foo<(&'a int,&'b uint),uint>,
for<'b> Foo(&int,&'b uint) -> uint >();
eq::< for<'a,'b> Foo<(&'a int,&'b uint),uint>,
Foo(&int,&uint) -> uint >();
// FIXME(#18992) Test lifetime elision in `()` form:
// eq::< for<'a,'b> Foo<(&'a int,), &'a int>,
// Foo(&int) -> &int >();
// Errors expected:
eq::< Foo<(),()>, Foo(char) >();
//~^ ERROR not implemented

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(unboxed_closures)]
fn f<F:Nonexist(int) -> int>(x: F) {} //~ ERROR nonexistent trait `Nonexist`
type Typedef = int;

View file

@ -12,20 +12,19 @@
// parameters (should be exactly as if angle brackets were used
// and regions omitted).
#![feature(default_type_params)]
#![feature(default_type_params, unboxed_closures)]
#![allow(dead_code)]
use std::kinds::marker;
struct Foo<'a,T,U> {
t: T,
u: U,
m: marker::InvariantLifetime<'a>
trait Foo<'a,T,U> {
fn dummy(&'a self) -> &'a (T,U);
}
trait Eq<X> { }
impl<X> Eq<X> for X { }
fn eq<A,B:Eq<A>>() { }
trait Eq<Sized? X> for Sized? { }
impl<Sized? X> Eq<X> for X { }
fn eq<Sized? A,Sized? B:Eq<A>>() { }
fn same_type<A,B:Eq<A>>(a: A, b: B) { }
fn test<'a,'b>() {
@ -34,10 +33,10 @@ fn test<'a,'b>() {
// Here we specify 'static explicitly in angle-bracket version.
// Parenthesized winds up getting inferred.
eq::< Foo<'static, (int,),()>, Foo(int) >();
eq::< Foo<'static, (int,),()>, Foo(int) >();
}
fn test2(x: Foo<(int,),()>, y: Foo(int)) {
fn test2(x: &Foo<(int,),()>, y: &Foo(int)) {
// Here, the omitted lifetimes are expanded to distinct things.
same_type(x, y) //~ ERROR cannot infer
}

View file

@ -8,9 +8,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
struct One<A>;
#![feature(unboxed_closures)]
fn foo(_: One()) //~ ERROR wrong number of type arguments
trait One<A> { fn foo(&self) -> A; }
fn foo(_: &One()) //~ ERROR wrong number of type arguments
{}
fn main() { }

View file

@ -8,9 +8,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
struct Three<A,B,C>;
#![feature(unboxed_closures)]
fn foo(_: Three()) //~ ERROR wrong number of type arguments
trait Three<A,B,C> { fn dummy(&self) -> (A,B,C); }
fn foo(_: &Three()) //~ ERROR wrong number of type arguments
{}
fn main() { }

View file

@ -8,7 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
struct Zero;
#![feature(unboxed_closures)]
trait Zero { fn dummy(&self); }
fn foo(_: Zero()) //~ ERROR wrong number of type arguments
{}

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(unboxed_closures)]
trait Trait {}
fn f<F:Trait(int) -> int>(x: F) {}

View file

@ -11,7 +11,7 @@
// Checks that the Fn trait hierarchy rules do not permit
// Fn to be used where FnMut is implemented.
#![feature(unboxed_closure_sugar)]
#![feature(unboxed_closures)]
#![feature(overloaded_calls)]
use std::ops::{Fn,FnMut,FnOnce};

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(overloaded_calls)]
#![feature(unboxed_closures)]
use std::ops::FnMut;

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(overloaded_calls, unboxed_closures)]
#![feature(unboxed_closures)]
fn each<'a,T,F:FnMut(&'a T)>(x: &'a [T], mut f: F) {
for val in x.iter() {

View file

@ -9,7 +9,7 @@
// except according to those terms.
#![allow(dead_code)]
#![feature(unboxed_closures, unboxed_closure_sugar)]
#![feature(unboxed_closures)]
// compile-flags:-g

View file

@ -0,0 +1,34 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we handle binder levels in object types correctly.
// Initially, the reference to `'tcx` in the object type
// `&Typer<'tcx>` was getting an incorrect binder level, yielding
// weird compilation ICEs and so forth.
trait Typer<'tcx> {
fn method(&self, data: &'tcx int) -> &'tcx int { data }
}
struct Tcx<'tcx> {
fields: &'tcx int
}
impl<'tcx> Typer<'tcx> for Tcx<'tcx> {
}
fn g<'tcx>(typer: &Typer<'tcx>) {
}
fn check_static_type<'x>(tcx: &Tcx<'x>) {
g(tcx)
}
fn main() { }

View file

@ -0,0 +1,23 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
trait Typer<'tcx> {
fn method(&self, data: &'tcx int) -> &'tcx int { data }
fn dummy(&self) { }
}
fn g(_: |&Typer|) {
}
fn h() {
g(|typer| typer.dummy())
}
fn main() { }

View file

@ -0,0 +1,35 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// A basic test of using a higher-ranked trait bound.
trait FnLike<A,R> {
fn call(&self, arg: A) -> R;
}
type FnObject<'b> = for<'a> FnLike<&'a int, &'a int> + 'b;
struct Identity;
impl<'a, T> FnLike<&'a T, &'a T> for Identity {
fn call(&self, arg: &'a T) -> &'a T {
arg
}
}
fn call_repeatedly(f: &FnObject) {
let x = 3;
let y = f.call(&x);
assert_eq!(3, *y);
}
fn main() {
call_repeatedly(&Identity);
}

View file

@ -0,0 +1,35 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// A basic test of using a higher-ranked trait bound.
trait FnLike<A,R> {
fn call(&self, arg: A) -> R;
}
struct Identity;
impl<'a, T> FnLike<&'a T, &'a T> for Identity {
fn call(&self, arg: &'a T) -> &'a T {
arg
}
}
fn call_repeatedly<F>(f: F)
where F : for<'a> FnLike<&'a int, &'a int>
{
let x = 3;
let y = f.call(&x);
assert_eq!(3, *y);
}
fn main() {
call_repeatedly(Identity);
}

View file

@ -11,6 +11,7 @@
// Test that we can parse all the various places that a `for` keyword
// can appear representing universal quantification.
#![feature(unboxed_closures)]
#![allow(unused_variables)]
#![allow(dead_code)]

View file

@ -0,0 +1,20 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// A basic test of using a higher-ranked trait bound.
trait FnLike<A,R> {
fn call(&self, arg: A) -> R;
}
type FnObject<'b> = for<'a> FnLike<&'a int, &'a int> + 'b;
fn main() {
}

Some files were not shown because too many files have changed in this diff Show more