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:
commit
c8d6e3b2c2
126 changed files with 3954 additions and 2004 deletions
|
@ -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
|
||||
|
|
|
@ -144,5 +144,6 @@ register_diagnostics!(
|
|||
E0165,
|
||||
E0166,
|
||||
E0167,
|
||||
E0168
|
||||
E0168,
|
||||
E0169
|
||||
)
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
|
||||
self.with(LateScope(ty.id, lifetimes, self.scope), |this| {
|
||||
this.check_lifetime_defs(lifetimes);
|
||||
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);
|
||||
});
|
||||
|
||||
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"),
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
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 = this.span.unwrap_or(DUMMY_SP);
|
||||
this.tcx().sess.span_bug(
|
||||
let span = self.span.unwrap_or(DUMMY_SP);
|
||||
self.tcx().sess.span_bug(
|
||||
span,
|
||||
format!("Type parameter `{}` ({}/{}/{}) out of range \
|
||||
when substituting (root type={}) substs={}",
|
||||
p.repr(this.tcx()),
|
||||
source_ty.repr(this.tcx()),
|
||||
space,
|
||||
index,
|
||||
this.root_ty.repr(this.tcx()),
|
||||
this.substs.repr(this.tcx())).as_slice());
|
||||
p.repr(self.tcx()),
|
||||
source_ty.repr(self.tcx()),
|
||||
p.space,
|
||||
p.idx,
|
||||
self.root_ty.repr(self.tcx()),
|
||||
self.substs.repr(self.tcx())).as_slice());
|
||||
}
|
||||
};
|
||||
|
||||
self.shift_regions_through_binders(ty)
|
||||
}
|
||||
|
||||
fn shift_regions_through_binders(&self, ty: ty::t) -> ty::t {
|
||||
/*!
|
||||
* It is sometimes necessary to adjust the debruijn indices
|
||||
* during substitution. This occurs when we are substituting a
|
||||
* type with escaping regions into a context where we have
|
||||
* passed through region binders. That's quite a
|
||||
* mouthful. Let's see an example:
|
||||
*
|
||||
* ```
|
||||
* type Func<A> = fn(A);
|
||||
* type MetaFunc = for<'a> fn(Func<&'a int>)
|
||||
* ```
|
||||
*
|
||||
* The type `MetaFunc`, when fully expanded, will be
|
||||
*
|
||||
* for<'a> fn(fn(&'a int))
|
||||
* ^~ ^~ ^~~
|
||||
* | | |
|
||||
* | | DebruijnIndex of 2
|
||||
* Binders
|
||||
*
|
||||
* Here the `'a` lifetime is bound in the outer function, but
|
||||
* appears as an argument of the inner one. Therefore, that
|
||||
* appearance will have a DebruijnIndex of 2, because we must
|
||||
* skip over the inner binder (remember that we count Debruijn
|
||||
* indices from 1). However, in the definition of `MetaFunc`,
|
||||
* the binder is not visible, so the type `&'a int` will have
|
||||
* a debruijn index of 1. It's only during the substitution
|
||||
* that we can see we must increase the depth by 1 to account
|
||||
* for the binder that we passed through.
|
||||
*
|
||||
* As a second example, consider this twist:
|
||||
*
|
||||
* ```
|
||||
* type FuncTuple<A> = (A,fn(A));
|
||||
* type MetaFuncTuple = for<'a> fn(FuncTuple<&'a int>)
|
||||
* ```
|
||||
*
|
||||
* Here the final type will be:
|
||||
*
|
||||
* for<'a> fn((&'a int, fn(&'a int)))
|
||||
* ^~~ ^~~
|
||||
* | |
|
||||
* DebruijnIndex of 1 |
|
||||
* DebruijnIndex of 2
|
||||
*
|
||||
* As indicated in the diagram, here the same type `&'a int`
|
||||
* is substituted once, but in the first case we do not
|
||||
* increase the Debruijn index and in the second case we
|
||||
* do. The reason is that only in the second case have we
|
||||
* passed through a fn binder.
|
||||
*/
|
||||
|
||||
debug!("shift_regions(ty={}, region_binders_passed={}, type_has_escaping_regions={})",
|
||||
ty.repr(self.tcx()), self.region_binders_passed, ty::type_has_escaping_regions(ty));
|
||||
|
||||
if self.region_binders_passed == 0 || !ty::type_has_escaping_regions(ty) {
|
||||
return ty;
|
||||
}
|
||||
|
||||
let result = ty_fold::shift_regions(self.tcx(), self.region_binders_passed, &ty);
|
||||
debug!("shift_regions: shifted result = {}", result.repr(self.tcx()));
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn shift_region_through_binders(&self, region: ty::Region) -> ty::Region {
|
||||
ty_fold::shift_region(region, self.region_binders_passed)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -39,13 +39,17 @@ pub fn impl_can_satisfy(infcx: &InferCtxt,
|
|||
let impl1_trait_ref =
|
||||
ty::impl_trait_ref(infcx.tcx, impl1_def_id).unwrap()
|
||||
.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, ¶m_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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
@ -614,8 +614,9 @@ bitflags! {
|
|||
const HAS_SELF = 0b10,
|
||||
const HAS_TY_INFER = 0b100,
|
||||
const HAS_RE_INFER = 0b1000,
|
||||
const HAS_REGIONS = 0b10000,
|
||||
const HAS_TY_ERR = 0b100000,
|
||||
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,
|
||||
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>,
|
||||
fn record_region_bounds(tcx: &ty::ctxt,
|
||||
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
|
||||
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 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
pub fn super_fold_fn_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),
|
||||
-> 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)
|
||||
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)
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
|
@ -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,20 +365,21 @@ 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,
|
||||
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))
|
||||
|
@ -371,15 +391,14 @@ fn ast_path_substs<'tcx,AC,RS>(
|
|||
.collect();
|
||||
|
||||
(regions, types)
|
||||
}
|
||||
}
|
||||
|
||||
fn parenthesized_parameters<'tcx,AC>(this: &AC,
|
||||
binder_id: ast::NodeId,
|
||||
fn convert_parenthesized_parameters<'tcx,AC>(this: &AC,
|
||||
data: &ast::ParenthesizedParameterData)
|
||||
-> (Vec<ty::Region>, Vec<ty::t>)
|
||||
-> Vec<ty::t>
|
||||
where AC: AstConv<'tcx>
|
||||
{
|
||||
let binding_rscope = BindingRscope::new(binder_id);
|
||||
{
|
||||
let binding_rscope = BindingRscope::new();
|
||||
|
||||
let inputs = data.inputs.iter()
|
||||
.map(|a_t| ast_ty_to_ty(this, &binding_rscope, &**a_t))
|
||||
|
@ -388,11 +407,22 @@ fn ast_path_substs<'tcx,AC,RS>(
|
|||
|
||||
let output = match data.output {
|
||||
Some(ref output_ty) => ast_ty_to_ty(this, &binding_rscope, &**output_ty),
|
||||
None => ty::mk_nil(this.tcx())
|
||||
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,
|
||||
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)
|
||||
path: &ast::Path)
|
||||
-> ty::TraitRef
|
||||
where AC: AstConv<'tcx>,
|
||||
RS: RegionScope {
|
||||
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,
|
||||
|
||||
// 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,
|
||||
associated_type,
|
||||
path,
|
||||
binder_id)
|
||||
}
|
||||
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,
|
||||
let substs = ast_path_substs_for_ty(this,
|
||||
rscope,
|
||||
did,
|
||||
&generics,
|
||||
None,
|
||||
None,
|
||||
path,
|
||||
binder_id);
|
||||
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.
|
||||
|
||||
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(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
|
||||
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,
|
||||
|
|
|
@ -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,22 +423,52 @@ 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
|
||||
}
|
||||
_ => {
|
||||
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),
|
||||
all_substs,
|
||||
&pick.method_ty.generics);
|
||||
}
|
||||
}
|
||||
method_bounds_substs,
|
||||
method_bounds);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
// 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.
|
||||
|
||||
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);
|
||||
match infer::mk_sub_trait_refs(&infcx,
|
||||
infer::mk_sub_trait_refs(&infcx,
|
||||
true,
|
||||
infer::Misc(impl_m_span),
|
||||
trait_bound,
|
||||
impl_trait_bound.clone()) {
|
||||
Ok(_) => {
|
||||
ok = true;
|
||||
break
|
||||
}
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
impl_trait_bound.clone()).is_ok()
|
||||
});
|
||||
|
||||
if !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,7 +1785,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn instantiate_item_type(&self,
|
||||
pub fn instantiate_type(&self,
|
||||
span: Span,
|
||||
def_id: ast::DefId)
|
||||
-> TypeAndSubsts
|
||||
|
@ -1720,6 +1796,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
* substitution from the type parameters on `def_id` to the
|
||||
* 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);
|
||||
|
||||
|
@ -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, ®ion_param) in
|
||||
generics.regions.iter().zip(
|
||||
for (region_bounds, ®ion_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,9 +3133,7 @@ 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,
|
||||
fcx.infcx().replace_late_bound_regions_with_fresh_var(call_expr.span,
|
||||
infer::FnCall,
|
||||
fn_sig).0;
|
||||
|
||||
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,10 +2010,10 @@ 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,
|
||||
.map(|bound| {
|
||||
astconv::instantiate_poly_trait_ref(this,
|
||||
&ExplicitRscope,
|
||||
b,
|
||||
bound,
|
||||
Some(param_ty.to_ty(this.tcx())),
|
||||
Some(param_ty.to_ty(this.tcx())))
|
||||
})
|
||||
|
@ -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}
|
||||
});
|
||||
|
@ -2145,7 +2139,9 @@ fn check_method_self_type<RS:RegionScope>(
|
|||
crate_context: &CrateCtxt,
|
||||
rs: &RS,
|
||||
required_type: ty::t,
|
||||
explicit_self: &ast::ExplicitSelf) {
|
||||
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))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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`.
|
||||
|
||||
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;
|
||||
self.higher_ranked_glb(a, b)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
415
src/librustc/middle/typeck/infer/higher_ranked/doc.rs
Normal file
415
src/librustc/middle/typeck/infer/higher_ranked/doc.rs
Normal 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.
|
||||
|
||||
*/
|
445
src/librustc/middle/typeck/infer/higher_ranked/mod.rs
Normal file
445
src/librustc/middle/typeck/infer/higher_ranked/mod.rs
Normal 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))
|
||||
}))
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
ty::replace_late_bound_regions(
|
||||
self.tcx,
|
||||
binder_id,
|
||||
value,
|
||||
|br| self.next_region_var(LateBoundRegion(span, br, lbrct)));
|
||||
(value, map)
|
||||
|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({})",
|
||||
|
|
|
@ -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`.
|
||||
|
||||
*/
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
self.higher_ranked_sub(a, b)
|
||||
}
|
||||
|
||||
return Ok(sig);
|
||||
fn trait_refs(&self, a: &ty::TraitRef, b: &ty::TraitRef) -> cres<ty::TraitRef> {
|
||||
self.higher_ranked_sub(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(), ¶m_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(), ¶m_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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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={}, \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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, "<"));
|
||||
|
|
|
@ -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,7 +421,7 @@ 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,
|
||||
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,9 +430,21 @@ 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 {
|
||||
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);
|
||||
|
@ -438,7 +462,6 @@ pub fn walk_path<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
|
||||
|
@ -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,
|
||||
pub fn walk_ty_param_bounds_helper<'v, V: Visitor<'v>>(visitor: &mut V,
|
||||
bounds: &'v OwnedSlice<TyParamBound>) {
|
||||
for bound in bounds.iter() {
|
||||
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)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
// ignore-android see #10393 #13206
|
||||
|
||||
#![feature(slicing_syntax, unboxed_closures, overloaded_calls)]
|
||||
#![feature(slicing_syntax, unboxed_closures)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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() {}
|
58
src/test/compile-fail/hrtb-higher-ranker-supertraits.rs
Normal file
58
src/test/compile-fail/hrtb-higher-ranker-supertraits.rs
Normal 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() {}
|
35
src/test/compile-fail/hrtb-identity-fn-borrows.rs
Normal file
35
src/test/compile-fail/hrtb-identity-fn-borrows.rs
Normal 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() {
|
||||
}
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// when a type error or unconstrained type variable propagates
|
||||
// into it.
|
||||
|
||||
#![feature(overloaded_calls)]
|
||||
#![feature(unboxed_closures)]
|
||||
|
||||
fn main() {
|
||||
(return)((),());
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
43
src/test/compile-fail/stage0-clone-contravariant-lifetime.rs
Normal file
43
src/test/compile-fail/stage0-clone-contravariant-lifetime.rs
Normal 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() { }
|
40
src/test/compile-fail/stage0-cmp.rs
Normal file
40
src/test/compile-fail/stage0-cmp.rs
Normal 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() { }
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>() {
|
||||
|
@ -37,7 +36,7 @@ fn test<'a,'b>() {
|
|||
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
|
||||
}
|
||||
|
|
|
@ -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() { }
|
||||
|
|
|
@ -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() { }
|
||||
|
|
|
@ -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
|
||||
{}
|
||||
|
|
|
@ -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) {}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
// except according to those terms.
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![feature(unboxed_closures, unboxed_closure_sugar)]
|
||||
#![feature(unboxed_closures)]
|
||||
|
||||
// compile-flags:-g
|
||||
|
||||
|
|
34
src/test/run-pass/hrtb-binder-levels-in-object-types.rs
Normal file
34
src/test/run-pass/hrtb-binder-levels-in-object-types.rs
Normal 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() { }
|
23
src/test/run-pass/hrtb-debruijn-object-types-in-closures.rs
Normal file
23
src/test/run-pass/hrtb-debruijn-object-types-in-closures.rs
Normal 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() { }
|
35
src/test/run-pass/hrtb-fn-like-trait-object.rs
Normal file
35
src/test/run-pass/hrtb-fn-like-trait-object.rs
Normal 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);
|
||||
}
|
35
src/test/run-pass/hrtb-fn-like-trait.rs
Normal file
35
src/test/run-pass/hrtb-fn-like-trait.rs
Normal 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);
|
||||
}
|
|
@ -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)]
|
||||
|
||||
|
|
20
src/test/run-pass/hrtb-resolve-lifetime.rs
Normal file
20
src/test/run-pass/hrtb-resolve-lifetime.rs
Normal 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() {
|
||||
}
|
37
src/test/run-pass/hrtb-trait-object-paren-notation.rs
Normal file
37
src/test/run-pass/hrtb-trait-object-paren-notation.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
// 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.
|
||||
|
||||
#![feature(unboxed_closures)]
|
||||
|
||||
// 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);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue