diff --git a/src/doc/reference.md b/src/doc/reference.md index 171c39a4cbc39..62e0f5e4f1f48 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -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 diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index b5dafdb180766..afbb18faa0b9f 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -144,5 +144,6 @@ register_diagnostics!( E0165, E0166, E0167, - E0168 + E0168, + E0169 ) diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index e1b0797e98253..69be2e3491526 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -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} } diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index 3242d3961467a..a53f5fa187df2 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -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); } diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 20dcf094b6620..ff6965574be92 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -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) diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 44206343c1081..d7925177c29db 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -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) { diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index d67c5b0dece29..c733084e9818a 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -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| { diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index c773467552a75..8604c3967a926 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -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); diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 94b52532a946d..baf53cc34ba94 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -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); } diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 4077629f76dad..a1257caf47ffa 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -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; 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, 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, Scope<'a>), + LateScope(&'a Vec, 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); - visit::walk_ty(this, ty); - }); + match ty.node { + ast::TyClosure(ref c) | ast::TyProc(ref c) => { + // Careful, the bounds on a closure/proc are *not* within its binder. + visit::walk_ty_param_bounds_helper(self, &c.bounds); + visit::walk_lifetime_decls_helper(self, &c.lifetimes); + self.with(LateScope(&c.lifetimes, self.scope), |this| { + this.check_lifetime_defs(&c.lifetimes); + for argument in c.decl.inputs.iter() { + this.visit_ty(&*argument.ty) + } + visit::walk_fn_ret_ty(this, &c.decl.output); + }); + } + ast::TyBareFn(ref c) => { + visit::walk_lifetime_decls_helper(self, &c.lifetimes); + self.with(LateScope(&c.lifetimes, self.scope), |this| { + // a bare fn has no bounds, so everything + // contained within is scoped within its binder. + this.check_lifetime_defs(&c.lifetimes); + visit::walk_ty(this, ty); + }); + } + 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) { - 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 { 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"), } diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index 520209257f5b8..4fabdabf3db4b 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -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 { 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 VecPerParamSpace { 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 VecPerParamSpace { self.assoc_limit) } + pub fn map_enumerated(&self, pred: |(ParamSpace, uint, &T)| -> U) -> VecPerParamSpace { + 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(self, pred: |T| -> U) -> VecPerParamSpace { let SeparateVecsPerParamSpace { types: t, @@ -457,6 +486,49 @@ impl VecPerParamSpace { } } +pub struct EnumeratedItems<'a,T:'a> { + vec: &'a VecPerParamSpace, + space_index: uint, + elem_index: uint +} + +impl<'a,T> EnumeratedItems<'a,T> { + fn new(v: &'a VecPerParamSpace) -> 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 Subst for T { substs: substs, span: span, root_ty: None, - ty_stack_depth: 0 }; + ty_stack_depth: 0, + region_binders_passed: 0 }; (*self).fold_with(&mut folder) } } @@ -506,11 +579,22 @@ struct SubstFolder<'a, 'tcx: 'a> { // Depth of type stack ty_stack_depth: uint, + + // Number of region binders we have passed through while doing the substitution + region_binders_passed: uint, } impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { self.tcx } + fn enter_region_binder(&mut self) { + self.region_binders_passed += 1; + } + + fn exit_region_binder(&mut self) { + self.region_binders_passed -= 1; + } + fn fold_region(&mut self, r: ty::Region) -> ty::Region { // Note: This routine only handles regions that are bound on // type declarations and other outer declarations, not those @@ -524,7 +608,9 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { ErasedRegions => ty::ReStatic, NonerasedRegions(ref regions) => match regions.opt_get(space, i) { - Some(t) => *t, + Some(&r) => { + self.shift_region_through_binders(r) + } None => { let span = self.span.unwrap_or(DUMMY_SP); self.tcx().sess.span_bug( @@ -557,12 +643,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { let t1 = match ty::get(t).sty { ty::ty_param(p) => { - check(self, - p, - t, - self.substs.types.opt_get(p.space, p.idx), - p.space, - p.idx) + self.ty_for_param(p, t) } _ => { ty_fold::super_fold_ty(self, t) @@ -576,30 +657,100 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { } return t1; + } +} - fn check(this: &SubstFolder, - p: ty::ParamTy, - source_ty: ty::t, - opt_ty: Option<&ty::t>, - space: ParamSpace, - index: uint) - -> ty::t { - match opt_ty { - Some(t) => *t, - None => { - let span = this.span.unwrap_or(DUMMY_SP); - this.tcx().sess.span_bug( - span, - format!("Type parameter `{}` ({}/{}/{}) out of range \ +impl<'a,'tcx> SubstFolder<'a,'tcx> { + fn ty_for_param(&self, p: ty::ParamTy, source_ty: ty::t) -> ty::t { + // Look up the type in the substitutions. It really should be in there. + let opt_ty = self.substs.types.opt_get(p.space, p.idx); + let ty = match opt_ty { + Some(t) => *t, + None => { + let span = self.span.unwrap_or(DUMMY_SP); + self.tcx().sess.span_bug( + span, + format!("Type parameter `{}` ({}/{}/{}) out of range \ when substituting (root type={}) substs={}", - p.repr(this.tcx()), - source_ty.repr(this.tcx()), - space, - index, - this.root_ty.repr(this.tcx()), - this.substs.repr(this.tcx())).as_slice()); - } + p.repr(self.tcx()), + source_ty.repr(self.tcx()), + p.space, + p.idx, + self.root_ty.repr(self.tcx()), + self.substs.repr(self.tcx())).as_slice()); } + }; + + self.shift_regions_through_binders(ty) + } + + fn shift_regions_through_binders(&self, ty: ty::t) -> ty::t { + /*! + * It is sometimes necessary to adjust the debruijn indices + * during substitution. This occurs when we are substituting a + * type with escaping regions into a context where we have + * passed through region binders. That's quite a + * mouthful. Let's see an example: + * + * ``` + * type Func = 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,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) } } diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs index be5a007c1ebde..405f6509e596d 100644 --- a/src/librustc/middle/traits/coherence.rs +++ b/src/librustc/middle/traits/coherence.rs @@ -17,7 +17,7 @@ use super::util; use middle::subst; use middle::subst::Subst; use middle::ty; -use middle::typeck::infer::InferCtxt; +use middle::typeck::infer::{mod, InferCtxt}; use syntax::ast; use syntax::codemap::DUMMY_SP; use util::ppaux::Repr; @@ -38,14 +38,18 @@ pub fn impl_can_satisfy(infcx: &InferCtxt, util::fresh_substs_for_impl(infcx, DUMMY_SP, impl1_def_id); let impl1_trait_ref = ty::impl_trait_ref(infcx.tcx, impl1_def_id).unwrap() - .subst(infcx.tcx, &impl1_substs); + .subst(infcx.tcx, &impl1_substs); + let impl1_trait_ref = + infcx.replace_late_bound_regions_with_fresh_var(DUMMY_SP, + infer::FnCall, + &impl1_trait_ref).0; // Determine whether `impl2` can provide an implementation for those // same types. let param_env = ty::empty_parameter_environment(); let mut selcx = SelectionContext::intercrate(infcx, ¶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) } diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs index 6baf4e6c04890..ff5d80c8a16a2 100644 --- a/src/librustc/middle/traits/fulfill.rs +++ b/src/librustc/middle/traits/fulfill.rs @@ -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); } diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index 9e0abb897f709..c9c9e3bd4ffdc 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -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 -{ - 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) -> subst::VecPerParamSpace { /*! - * Given generics for an impl like: + * Given generic bounds from an impl like: * * impl ... * - * and a substs vector like ``, yields a result like + * along with the bindings for the types `A` and `B` (e.g., + * ``), 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, diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index bb0db874b675f..a941d2b079e21 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -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 trait_ref = Rc::new(ty::TraitRef { - def_id: obligation.trait_ref.def_id, - substs: Substs::new_trait( + 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), - new_signature.output.unwrap().subst(self.tcx(), substs)], + closure_sig.output.unwrap().subst(self.tcx(), substs)], vec![], vec![], - obligation.self_ty()) + obligation.self_ty()); + let trait_ref = Rc::new(ty::TraitRef { + def_id: obligation.trait_ref.def_id, + substs: substs, }); self.confirm(obligation.cause, @@ -2025,10 +2017,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { impl_substs: &Substs) -> VecPerParamSpace { - 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) } } diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs index d8043fc2aab41..8f8203f0281b1 100644 --- a/src/librustc/middle/traits/util.rs +++ b/src/librustc/middle/traits/util.rs @@ -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) -> VecPerParamSpace { /*! 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, obligations: &mut VecPerParamSpace) { - 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() }); } } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 5ec89ffb9e7e2..9f90afa37490c 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -53,7 +53,7 @@ use middle::subst::{mod, Subst, Substs, VecPerParamSpace}; use middle::traits; use middle::ty; use middle::typeck; -use middle::ty_fold::{mod, TypeFoldable,TypeFolder}; +use middle::ty_fold::{mod, TypeFoldable, TypeFolder, HigherRankedFoldable}; use middle; use util::ppaux::{note_and_explain_region, bound_region_ptr_to_string}; use util::ppaux::{trait_store_to_string, ty_to_string}; @@ -609,13 +609,14 @@ pub struct ctxt<'tcx> { // recursing over the type itself. bitflags! { flags TypeFlags: u32 { - const NO_TYPE_FLAGS = 0b0, - const HAS_PARAMS = 0b1, - const HAS_SELF = 0b10, - const HAS_TY_INFER = 0b100, - const HAS_RE_INFER = 0b1000, - const HAS_REGIONS = 0b10000, - const HAS_TY_ERR = 0b100000, + const NO_TYPE_FLAGS = 0b0, + const HAS_PARAMS = 0b1, + const HAS_SELF = 0b10, + const HAS_TY_INFER = 0b100, + const HAS_RE_INFER = 0b1000, + const HAS_RE_LATE_BOUND = 0b10000, + const HAS_REGIONS = 0b100000, + const HAS_TY_ERR = 0b1000000, const NEEDS_SUBST = HAS_PARAMS.bits | HAS_SELF.bits | HAS_REGIONS.bits, } } @@ -626,6 +627,9 @@ pub type t_box = &'static t_box_; pub struct t_box_ { pub sty: sty, pub flags: TypeFlags, + + // the maximal depth of any bound regions appearing in this type. + region_depth: uint, } impl fmt::Show for TypeFlags { @@ -670,6 +674,50 @@ pub fn type_needs_infer(t: t) -> bool { tbox_has_flag(get(t), HAS_TY_INFER | HAS_RE_INFER) } +pub fn type_has_late_bound_regions(ty: t) -> bool { + get(ty).flags.intersects(HAS_RE_LATE_BOUND) +} + +pub fn type_has_escaping_regions(t: t) -> bool { + /*! + * An "escaping region" is a bound region whose binder is not part of `t`. + * + * So, for example, consider a type like the following, which has two + * binders: + * + * for<'a> fn(x: for<'b> fn(&'a int, &'b int)) + * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope + * ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ inner scope + * + * This type has *bound regions* (`'a`, `'b`), but it does not + * have escaping regions, because the binders of both `'a` and + * `'b` are part of the type itself. However, if we consider the + * *inner fn type*, that type has an escaping region: `'a`. + * + * Note that what I'm calling an "escaping region" is often just + * called a "free region". However, we already use the term "free + * region". It refers to the regions that we use to represent + * bound regions on a fn definition while we are typechecking its + * body. + * + * To clarify, conceptually there is no particular difference + * between an "escaping" region and a "free" region. However, + * there is a big difference in practice. Basically, when + * "entering" a binding level, one is generally required to do + * some sort of processing to a bound region, such as replacing it + * with a fresh/skolemized region, or making an entry in the + * environment to represent the scope to which it is attached, + * etc. An escaping region represents a bound region for which + * this processing has not yet been done. + */ + + type_escapes_depth(t, 0) +} + +pub fn type_escapes_depth(t: t, depth: uint) -> bool { + get(t).region_depth > depth +} + #[deriving(Clone, PartialEq, Eq, Hash, Show)] pub struct BareFnTy { pub fn_style: ast::FnStyle, @@ -706,17 +754,16 @@ impl FnOutput { * Signature of a function type, which I have arbitrarily * decided to use to refer to the input/output types. * - * - `binder_id` is the node id where this fn type appeared; - * it is used to identify all the bound regions appearing - * in the input/output types that are bound by this fn type - * (vs some enclosing or enclosed fn type) * - `inputs` is the list of arguments and their modes. * - `output` is the return type. * - `variadic` indicates whether this is a varidic function. (only true for foreign fns) + * + * Note that a `FnSig` introduces a level of region binding, to + * account for late-bound parameters that appear in the types of the + * fn's arguments or the fn's return type. */ #[deriving(Clone, PartialEq, Eq, Hash)] pub struct FnSig { - pub binder_id: ast::NodeId, pub inputs: Vec, 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; 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 + * + * 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`, 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 { + pub value: T +} + +pub fn bind(value: T) -> Binder { + 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> { ... } + * + * Here, the `Generics` for `Foo` would contain a list of bounds like + * `[[], [U:Bar]]`. Now if there were some particular reference + * like `Foo`, then the `GenericBounds` would be `[[], + * [uint:Bar]]`. + */ +#[deriving(Clone, Show)] +pub struct GenericBounds { + pub types: VecPerParamSpace, + pub regions: VecPerParamSpace>, +} + +impl GenericBounds { + pub fn empty() -> GenericBounds { + GenericBounds { types: VecPerParamSpace::empty(), + regions: VecPerParamSpace::empty() } + } + + pub fn has_escaping_regions(&self) -> bool { + self.types.any(|pb| pb.trait_bounds.iter().any(|tr| tr.has_escaping_regions())) || + self.regions.any(|rs| rs.iter().any(|r| r.escapes_depth(0))) + } } impl TraitRef { @@ -1290,6 +1473,14 @@ impl TraitRef { // associated types. self.substs.types.as_slice() } + + pub fn has_escaping_regions(&self) -> bool { + self.substs.has_regions_escaping_depth(1) + } + + pub fn has_bound_regions(&self) -> bool { + self.substs.has_regions_escaping_depth(0) + } } /// When type checking, we use the `ParameterEnvironment` to track @@ -1597,99 +1788,12 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t { _ => () } - let mut flags = NO_TYPE_FLAGS; - fn rflags(r: Region) -> TypeFlags { - HAS_REGIONS | { - match r { - ty::ReInfer(_) => HAS_RE_INFER, - _ => NO_TYPE_FLAGS, - } - } - } - fn sflags(substs: &Substs) -> TypeFlags { - let mut f = NO_TYPE_FLAGS; - let mut i = substs.types.iter(); - for tt in i { - f = f | get(*tt).flags; - } - match substs.regions { - subst::ErasedRegions => {} - subst::NonerasedRegions(ref regions) => { - for r in regions.iter() { - f = f | rflags(*r) - } - } - } - return f; - } - fn flags_for_bounds(bounds: &ExistentialBounds) -> TypeFlags { - rflags(bounds.region_bound) - } - match &st { - &ty_bool | &ty_char | &ty_int(_) | &ty_float(_) | &ty_uint(_) | - &ty_str => {} - // You might think that we could just return ty_err for - // any type containing ty_err as a component, and get - // rid of the HAS_TY_ERR flag -- likewise for ty_bot (with - // the exception of function types that return bot). - // But doing so caused sporadic memory corruption, and - // neither I (tjc) nor nmatsakis could figure out why, - // so we're doing it this way. - &ty_err => flags = flags | HAS_TY_ERR, - &ty_param(ref p) => { - if p.space == subst::SelfSpace { - flags = flags | HAS_SELF; - } else { - flags = flags | HAS_PARAMS; - } - } - &ty_unboxed_closure(_, ref region, ref substs) => { - flags = flags | rflags(*region); - flags = flags | sflags(substs); - } - &ty_infer(_) => flags = flags | HAS_TY_INFER, - &ty_enum(_, ref substs) | &ty_struct(_, ref substs) => { - flags = flags | sflags(substs); - } - &ty_trait(box TyTrait { ref principal, ref bounds }) => { - flags = flags | sflags(&principal.substs); - flags = flags | flags_for_bounds(bounds); - } - &ty_uniq(tt) | &ty_vec(tt, _) | &ty_open(tt) => { - flags = flags | get(tt).flags - } - &ty_ptr(ref m) => { - flags = flags | get(m.ty).flags; - } - &ty_rptr(r, ref m) => { - flags = flags | rflags(r); - flags = flags | get(m.ty).flags; - } - &ty_tup(ref ts) => for tt in ts.iter() { flags = flags | get(*tt).flags; }, - &ty_bare_fn(ref f) => { - for a in f.sig.inputs.iter() { flags = flags | get(*a).flags; } - if let ty::FnConverging(output) = f.sig.output { - flags = flags | get(output).flags; - } - } - &ty_closure(ref f) => { - match f.store { - RegionTraitStore(r, _) => { - flags = flags | rflags(r); - } - _ => {} - } - for a in f.sig.inputs.iter() { flags = flags | get(*a).flags; } - if let ty::FnConverging(output) = f.sig.output { - flags = flags | get(output).flags; - } - flags = flags | flags_for_bounds(&f.bounds); - } - } + let flags = FlagComputation::for_sty(&st); let t = cx.type_arena.alloc(t_box_ { sty: st, - flags: flags, + flags: flags.flags, + region_depth: flags.depth, }); let sty_ptr = &t.sty as *const sty; @@ -1705,6 +1809,188 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t { } } +struct FlagComputation { + flags: TypeFlags, + + // maximum depth of any bound region that we have seen thus far + depth: uint, +} + +impl FlagComputation { + fn new() -> FlagComputation { + FlagComputation { flags: NO_TYPE_FLAGS, depth: 0 } + } + + fn for_sty(st: &sty) -> FlagComputation { + let mut result = FlagComputation::new(); + result.add_sty(st); + result + } + + fn add_flags(&mut self, flags: TypeFlags) { + self.flags = self.flags | flags; + } + + fn add_depth(&mut self, depth: uint) { + if depth > self.depth { + self.depth = depth; + } + } + + fn add_bound_computation(&mut self, computation: &FlagComputation) { + /*! + * Adds the flags/depth from a set of types that appear within + * the current type, but within a region binder. + */ + + self.add_flags(computation.flags); + + // The types that contributed to `computation` occured within + // a region binder, so subtract one from the region depth + // within when adding the depth to `self`. + let depth = computation.depth; + if depth > 0 { + self.add_depth(depth - 1); + } + } + + fn add_sty(&mut self, st: &sty) { + match st { + &ty_bool | + &ty_char | + &ty_int(_) | + &ty_float(_) | + &ty_uint(_) | + &ty_str => { + } + + // You might think that we could just return ty_err for + // any type containing ty_err as a component, and get + // rid of the HAS_TY_ERR flag -- likewise for ty_bot (with + // the exception of function types that return bot). + // But doing so caused sporadic memory corruption, and + // neither I (tjc) nor nmatsakis could figure out why, + // so we're doing it this way. + &ty_err => { + self.add_flags(HAS_TY_ERR) + } + + &ty_param(ref p) => { + if p.space == subst::SelfSpace { + self.add_flags(HAS_SELF); + } else { + self.add_flags(HAS_PARAMS); + } + } + + &ty_unboxed_closure(_, ref region, ref substs) => { + self.add_region(*region); + self.add_substs(substs); + } + + &ty_infer(_) => { + self.add_flags(HAS_TY_INFER) + } + + &ty_enum(_, ref substs) | &ty_struct(_, ref substs) => { + self.add_substs(substs); + } + + &ty_trait(box TyTrait { ref principal, ref bounds }) => { + let mut computation = FlagComputation::new(); + computation.add_substs(&principal.substs); + self.add_bound_computation(&computation); + + self.add_bounds(bounds); + } + + &ty_uniq(tt) | &ty_vec(tt, _) | &ty_open(tt) => { + self.add_ty(tt) + } + + &ty_ptr(ref m) => { + self.add_ty(m.ty); + } + + &ty_rptr(r, ref m) => { + self.add_region(r); + self.add_ty(m.ty); + } + + &ty_tup(ref ts) => { + self.add_tys(ts[]); + } + + &ty_bare_fn(ref f) => { + self.add_fn_sig(&f.sig); + } + + &ty_closure(ref f) => { + match f.store { + RegionTraitStore(r, _) => { + self.add_region(r); + } + _ => {} + } + self.add_fn_sig(&f.sig); + self.add_bounds(&f.bounds); + } + } + } + + fn add_ty(&mut self, t: t) { + let t_box = get(t); + self.add_flags(t_box.flags); + self.add_depth(t_box.region_depth); + } + + fn add_tys(&mut self, tys: &[t]) { + for &ty in tys.iter() { + self.add_ty(ty); + } + } + + fn add_fn_sig(&mut self, fn_sig: &FnSig) { + let mut computation = FlagComputation::new(); + + computation.add_tys(fn_sig.inputs[]); + + if let ty::FnConverging(output) = fn_sig.output { + computation.add_ty(output); + } + + self.add_bound_computation(&computation); + } + + fn add_region(&mut self, r: Region) { + self.add_flags(HAS_REGIONS); + match r { + ty::ReInfer(_) => { self.add_flags(HAS_RE_INFER); } + ty::ReLateBound(debruijn, _) => { + self.add_flags(HAS_RE_LATE_BOUND); + self.add_depth(debruijn.depth); + } + _ => { } + } + } + + fn add_substs(&mut self, substs: &Substs) { + self.add_tys(substs.types.as_slice()); + match substs.regions { + subst::ErasedRegions => {} + subst::NonerasedRegions(ref regions) => { + for &r in regions.iter() { + self.add_region(r); + } + } + } + } + + fn add_bounds(&mut self, bounds: &ExistentialBounds) { + self.add_region(bounds.region_bound); + } +} + #[inline] pub fn mk_prim_t(primitive: &'static t_box_) -> t { unsafe { @@ -1855,7 +2141,6 @@ pub fn mk_bare_fn(cx: &ctxt, fty: BareFnTy) -> t { } pub fn mk_ctor_fn(cx: &ctxt, - binder_id: ast::NodeId, input_tys: &[ty::t], output: ty::t) -> t { let input_args = input_tys.iter().map(|t| *t).collect(); @@ -1864,7 +2149,6 @@ pub fn mk_ctor_fn(cx: &ctxt, fn_style: ast::NormalFn, abi: abi::Rust, sig: FnSig { - binder_id: binder_id, inputs: input_args, output: ty::FnConverging(output), variadic: false @@ -4498,9 +4782,99 @@ pub fn bounds_for_trait_ref(tcx: &ctxt, -> ty::ParamBounds { let trait_def = lookup_trait_def(tcx, trait_ref.def_id); + debug!("bounds_for_trait_ref(trait_def={}, trait_ref={})", trait_def.repr(tcx), trait_ref.repr(tcx)); - trait_def.bounds.subst(tcx, &trait_ref.substs) + + // The interaction between HRTB and supertraits is not entirely + // obvious. Let me walk you (and myself) through an example. + // + // Let's start with an easy case. Consider two traits: + // + // trait Foo<'a> : Bar<'a,'a> { } + // trait Bar<'b,'c> { } + // + // Now, if we have a trait reference `for<'x> T : Foo<'x>`, then + // we can deduce that `for<'x> T : Bar<'x,'x>`. Basically, if we + // knew that `Foo<'x>` (for any 'x) then we also know that + // `Bar<'x,'x>` (for any 'x). This more-or-less falls out from + // normal substitution. + // + // In terms of why this is sound, the idea is that whenever there + // is an impl of `T:Foo<'a>`, it must show that `T:Bar<'a,'a>` + // holds. So if there is an impl of `T:Foo<'a>` that applies to + // all `'a`, then we must know that `T:Bar<'a,'a>` holds for all + // `'a`. + // + // Another example to be careful of is this: + // + // trait Foo1<'a> : for<'b> Bar1<'a,'b> { } + // trait Bar1<'b,'c> { } + // + // Here, if we have `for<'x> T : Foo1<'x>`, then what do we know? + // The answer is that we know `for<'x,'b> T : Bar1<'x,'b>`. The + // reason is similar to the previous example: any impl of + // `T:Foo1<'x>` must show that `for<'b> T : Bar1<'x, 'b>`. So + // basically we would want to collapse the bound lifetimes from + // the input (`trait_ref`) and the supertraits. + // + // To achieve this in practice is fairly straightforward. Let's + // consider the more complicated scenario: + // + // - We start out with `for<'x> T : Foo1<'x>`. In this case, `'x` + // has a De Bruijn index of 1. We want to produce `for<'x,'b> T : Bar1<'x,'b>`, + // where both `'x` and `'b` would have a DB index of 1. + // The substitution from the input trait-ref is therefore going to be + // `'a => 'x` (where `'x` has a DB index of 1). + // - The super-trait-ref is `for<'b> Bar1<'a,'b>`, where `'a` is an + // early-bound parameter and `'b' is a late-bound parameter with a + // DB index of 1. + // - If we replace `'a` with `'x` from the input, it too will have + // a DB index of 1, and thus we'll have `for<'x,'b> Bar1<'x,'b>` + // just as we wanted. + // + // There is only one catch. If we just apply the substitution `'a + // => 'x` to `for<'b> Bar1<'a,'b>`, the substitution code will + // adjust the DB index because we substituting into a binder (it + // tries to be so smart...) resulting in `for<'x> for<'b> + // Bar1<'x,'b>` (we have no syntax for this, so use your + // imagination). Basically the 'x will have DB index of 2 and 'b + // will have DB index of 1. Not quite what we want. So we apply + // the substitution to the *contents* of the trait reference, + // rather than the trait reference itself (put another way, the + // substitution code expects equal binding levels in the values + // from the substitution and the value being substituted into, and + // this trick achieves that). + + // Carefully avoid the binder introduced by each trait-ref by + // substituting over the substs, not the trait-refs themselves, + // thus achieving the "collapse" described in the big comment + // above. + let trait_bounds: Vec<_> = + trait_def.bounds.trait_bounds + .iter() + .map(|bound_trait_ref| { + ty::TraitRef::new(bound_trait_ref.def_id, + bound_trait_ref.substs.subst(tcx, &trait_ref.substs)) + }) + .map(|bound_trait_ref| Rc::new(bound_trait_ref)) + .collect(); + + debug!("bounds_for_trait_ref: trait_bounds={}", + trait_bounds.repr(tcx)); + + // The region bounds and builtin bounds do not currently introduce + // binders so we can just substitute in a straightforward way here. + let region_bounds = + trait_def.bounds.region_bounds.subst(tcx, &trait_ref.substs); + let builtin_bounds = + trait_def.bounds.builtin_bounds.subst(tcx, &trait_ref.substs); + + ty::ParamBounds { + trait_bounds: trait_bounds, + region_bounds: region_bounds, + builtin_bounds: builtin_bounds, + } } /// Iterate over attributes of a definition. @@ -4783,13 +5157,12 @@ pub fn normalize_ty(cx: &ctxt, t: t) -> t { types: substs.types.fold_with(self) } } - fn fold_sig(&mut self, - sig: &ty::FnSig) - -> ty::FnSig { + fn fold_fn_sig(&mut self, + sig: &ty::FnSig) + -> ty::FnSig { // The binder-id is only relevant to bound regions, which // are erased at trans time. ty::FnSig { - binder_id: ast::DUMMY_NODE_ID, inputs: sig.inputs.fold_with(self), output: sig.output.fold_with(self), variadic: sig.variadic, @@ -5305,11 +5678,13 @@ pub fn construct_parameter_environment( // Compute the bounds on Self and the type parameters. // - let mut bounds = VecPerParamSpace::empty(); - for &space in subst::ParamSpace::all().iter() { - push_bounds_from_defs(tcx, &mut bounds, space, &free_substs, - generics.types.get_slice(space)); - } + let bounds = generics.to_bounds(tcx, &free_substs); + let bounds = liberate_late_bound_regions(tcx, free_id, &bind(bounds)).value; + let obligations = traits::obligations_for_generics(tcx, + traits::ObligationCause::misc(span), + &bounds, + &free_substs.types); + let type_bounds = bounds.types.subst(tcx, &free_substs); // // Compute region bounds. For now, these relations are stored in a @@ -5318,24 +5693,20 @@ pub fn construct_parameter_environment( // for &space in subst::ParamSpace::all().iter() { - record_region_bounds_from_defs(tcx, space, &free_substs, - generics.regions.get_slice(space)); + record_region_bounds(tcx, space, &free_substs, bounds.regions.get_slice(space)); } - debug!("construct_parameter_environment: free_id={} \ - free_subst={} \ - bounds={}", + debug!("construct_parameter_environment: free_id={} free_subst={} \ + obligations={} type_bounds={}", free_id, free_substs.repr(tcx), - bounds.repr(tcx)); - - let obligations = traits::obligations_for_generics(tcx, traits::ObligationCause::misc(span), - generics, &free_substs); + obligations.repr(tcx), + type_bounds.repr(tcx)); return ty::ParameterEnvironment { free_substs: free_substs, - bounds: bounds, + bounds: bounds.types, implicit_region_bound: ty::ReScope(free_id), caller_obligations: obligations, selection_cache: traits::SelectionCache::new(), @@ -5366,28 +5737,16 @@ pub fn construct_parameter_environment( } } - fn push_bounds_from_defs(tcx: &ty::ctxt, - bounds: &mut subst::VecPerParamSpace, - space: subst::ParamSpace, - free_substs: &subst::Substs, - defs: &[TypeParameterDef]) { - for def in defs.iter() { - let b = def.bounds.subst(tcx, free_substs); - bounds.push(space, b); - } - } - - fn record_region_bounds_from_defs(tcx: &ty::ctxt, - space: subst::ParamSpace, - free_substs: &subst::Substs, - defs: &[RegionParameterDef]) { - for (subst_region, def) in + fn record_region_bounds(tcx: &ty::ctxt, + space: subst::ParamSpace, + free_substs: &Substs, + bound_sets: &[Vec]) { + 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
( + 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
( + 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
( + tcx: &ty::ctxt, + value: &HR, + mapf: |BoundRegion, DebruijnIndex| -> ty::Region) + -> (HR, FnvHashMap) + 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 } + } +} diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index 6d8d03aa0ab7a..4dfee673bca16 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -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 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 TypeFoldable for Option { fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Option { self.as_ref().map(|t| t.fold_with(folder)) @@ -171,6 +187,15 @@ impl TypeFoldable for Vec { } } +impl TypeFoldable for ty::Binder { + fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::Binder { + folder.enter_region_binder(); + let result = ty::bind(self.value.fold_with(folder)); + folder.exit_region_binder(); + result + } +} + impl TypeFoldable for OwnedSlice { fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> OwnedSlice { self.iter().map(|t| t.fold_with(folder)).collect() @@ -179,7 +204,24 @@ impl TypeFoldable for OwnedSlice { impl TypeFoldable for VecPerParamSpace { fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> VecPerParamSpace { - self.map(|t| t.fold_with(folder)) + + // Things in the Fn space take place under an additional level + // of region binding relative to the other spaces. This is + // because those entries are attached to a method, and methods + // always introduce a level of region binding. + + let result = self.map_enumerated(|(space, index, elem)| { + if space == subst::FnSpace && index == 0 { + // enter new level when/if we reach the first thing in fn space + folder.enter_region_binder(); + } + elem.fold_with(folder) + }); + if result.len(subst::FnSpace) > 0 { + // if there was anything in fn space, exit the region binding level + folder.exit_region_binder(); + } + result } } @@ -221,7 +263,7 @@ impl TypeFoldable for ty::FnOutput { impl TypeFoldable for ty::FnSig { fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::FnSig { - folder.fold_sig(self) + folder.fold_fn_sig(self) } } @@ -368,6 +410,15 @@ impl TypeFoldable for ty::Generics { } } +impl TypeFoldable for ty::GenericBounds { + fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::GenericBounds { + ty::GenericBounds { + types: self.types.fold_with(folder), + regions: self.regions.fold_with(folder), + } + } +} + impl TypeFoldable for ty::UnsizeKind { fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::UnsizeKind { match *self { @@ -434,6 +485,7 @@ impl TypeFoldable for traits::VtableParamData { // "super" routines: these are the default implementations for TypeFolder. // // They should invoke `foo.fold_with()` to do recursive folding. + pub fn super_fold_ty<'tcx, T: TypeFolder<'tcx>>(this: &mut T, t: ty::t) -> ty::t { @@ -457,11 +509,21 @@ pub fn super_fold_substs<'tcx, T: TypeFolder<'tcx>>(this: &mut T, types: substs.types.fold_with(this) } } -pub fn super_fold_sig<'tcx, T: TypeFolder<'tcx>>(this: &mut T, - sig: &ty::FnSig) - -> ty::FnSig { - ty::FnSig { binder_id: sig.binder_id, - inputs: sig.inputs.fold_with(this), +pub fn super_fold_fn_sig<'tcx, T: TypeFolder<'tcx>>(this: &mut T, + sig: &ty::FnSig) + -> ty::FnSig +{ + this.enter_region_binder(); + let result = super_fold_fn_sig_contents(this, sig); + this.exit_region_binder(); + result +} + +pub fn super_fold_fn_sig_contents<'tcx, T: TypeFolder<'tcx>>(this: &mut T, + sig: &ty::FnSig) + -> ty::FnSig +{ + ty::FnSig { inputs: sig.inputs.fold_with(this), output: sig.output.fold_with(this), variadic: sig.variadic } } @@ -497,9 +559,21 @@ pub fn super_fold_closure_ty<'tcx, T: TypeFolder<'tcx>>(this: &mut T, abi: fty.abi, } } + pub fn super_fold_trait_ref<'tcx, T: TypeFolder<'tcx>>(this: &mut T, t: &ty::TraitRef) - -> ty::TraitRef { + -> ty::TraitRef +{ + this.enter_region_binder(); + let result = super_fold_trait_ref_contents(this, t); + this.exit_region_binder(); + result +} + +pub fn super_fold_trait_ref_contents<'tcx, T: TypeFolder<'tcx>>(this: &mut T, + t: &ty::TraitRef) + -> ty::TraitRef +{ ty::TraitRef { def_id: t.def_id, substs: t.substs.fold_with(this), @@ -621,6 +695,42 @@ pub fn super_fold_obligation<'tcx, T:TypeFolder<'tcx>>(this: &mut T, } } +/////////////////////////////////////////////////////////////////////////// +// Higher-ranked things + +/** + * Designates a "binder" for late-bound regions. + */ +pub trait HigherRankedFoldable : Repr { + /// Folds the contents of `self`, ignoring the region binder created + /// by `self`. + fn fold_contents<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self; +} + +impl HigherRankedFoldable for ty::FnSig { + fn fold_contents<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::FnSig { + super_fold_fn_sig_contents(folder, self) + } +} + +impl HigherRankedFoldable for ty::TraitRef { + fn fold_contents<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::TraitRef { + super_fold_trait_ref_contents(folder, self) + } +} + +impl HigherRankedFoldable for ty::Binder { + fn fold_contents<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::Binder { + ty::bind(self.value.fold_with(folder)) + } +} + +impl HigherRankedFoldable for Rc { + fn fold_contents<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Rc { + 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, + current_depth: uint, + fld_r: |ty::Region, uint|: 'a -> ty::Region, } impl<'a, 'tcx> RegionFolder<'a, 'tcx> { - pub fn general(tcx: &'a ty::ctxt<'tcx>, - fld_r: |ty::Region|: 'a -> ty::Region, - fld_t: |ty::t|: 'a -> ty::t) - -> RegionFolder<'a, 'tcx> { + pub fn new(tcx: &'a ty::ctxt<'tcx>, fld_r: |ty::Region, uint|: 'a -> ty::Region) + -> RegionFolder<'a, 'tcx> { RegionFolder { tcx: tcx, - fld_t: fld_t, + current_depth: 1, fld_r: fld_r, - within_binder_ids: vec![], - } - } - - pub fn regions(tcx: &'a ty::ctxt<'tcx>, fld_r: |ty::Region|: 'a -> ty::Region) - -> RegionFolder<'a, 'tcx> { - fn noop(t: ty::t) -> ty::t { t } - - RegionFolder { - tcx: tcx, - fld_t: noop, - fld_r: fld_r, - within_binder_ids: vec![], } } } -/// If `ty` has `FnSig` (i.e. closure or fn), return its binder_id; -/// else None. -fn opt_binder_id_of_function(t: ty::t) -> Option { - match ty::get(t).sty { - ty::ty_closure(ref f) => Some(f.sig.binder_id), - ty::ty_bare_fn(ref f) => Some(f.sig.binder_id), - _ => None, - } -} - impl<'a, 'tcx> TypeFolder<'tcx> for RegionFolder<'a, 'tcx> { fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { self.tcx } - fn fold_ty(&mut self, ty: ty::t) -> ty::t { - debug!("RegionFolder.fold_ty({})", ty.repr(self.tcx())); - let opt_binder_id = opt_binder_id_of_function(ty); - match opt_binder_id { - Some(binder_id) => self.within_binder_ids.push(binder_id), - None => {} - } - - let t1 = super_fold_ty(self, ty); - let ret = (self.fld_t)(t1); - - if opt_binder_id.is_some() { - self.within_binder_ids.pop(); - } + fn enter_region_binder(&mut self) { + self.current_depth += 1; + } - ret + fn exit_region_binder(&mut self) { + self.current_depth -= 1; } fn fold_region(&mut self, r: ty::Region) -> ty::Region { match r { - ty::ReLateBound(binder_id, _) if self.within_binder_ids.contains(&binder_id) => { - debug!("RegionFolder.fold_region({}) skipped bound region", r.repr(self.tcx())); + ty::ReLateBound(debruijn, _) if debruijn.depth < self.current_depth => { + debug!("RegionFolder.fold_region({}) skipped bound region (current depth={})", + r.repr(self.tcx()), self.current_depth); r } _ => { - debug!("RegionFolder.fold_region({}) folding free region", r.repr(self.tcx())); - (self.fld_r)(r) + debug!("RegionFolder.fold_region({}) folding free region (current_depth={})", + r.repr(self.tcx()), self.current_depth); + (self.fld_r)(r, self.current_depth) } } } @@ -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(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) + })) +} + diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index 3ef9a59d8adb4..6867749264961 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -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, associated_ty: Option, - 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, + types: Vec, + regions: Vec, + associated_ty: Option) + -> Substs + where AC: AstConv<'tcx>, RS: RegionScope +{ + let tcx = this.tcx(); + // If the type is parameterized by the this region, then replace this // region with the current anon region binding (in other words, // whatever & would get replaced with). @@ -250,10 +270,10 @@ fn ast_path_substs<'tcx,AC,RS>( regions } else { let anon_regions = - rscope.anon_regions(path.span, expected_num_region_params); + rscope.anon_regions(span, expected_num_region_params); if supplied_num_region_params != 0 || anon_regions.is_err() { - span_err!(tcx.sess, path.span, E0107, + span_err!(tcx.sess, span, E0107, "wrong number of lifetime parameters: expected {}, found {}", expected_num_region_params, supplied_num_region_params); } @@ -285,7 +305,7 @@ fn ast_path_substs<'tcx,AC,RS>( } else { "expected" }; - this.tcx().sess.span_fatal(path.span, + this.tcx().sess.span_fatal(span, format!("wrong number of type arguments: {} {}, found {}", expected, required_ty_param_count, @@ -296,7 +316,7 @@ fn ast_path_substs<'tcx,AC,RS>( } else { "expected" }; - this.tcx().sess.span_fatal(path.span, + this.tcx().sess.span_fatal(span, format!("wrong number of type arguments: {} {}, found {}", expected, formal_ty_param_count, @@ -305,9 +325,9 @@ fn ast_path_substs<'tcx,AC,RS>( if supplied_ty_param_count > required_ty_param_count && !this.tcx().sess.features.borrow().default_type_params { - span_err!(this.tcx().sess, path.span, E0108, + span_err!(this.tcx().sess, span, E0108, "default type parameters are experimental and possibly buggy"); - span_help!(this.tcx().sess, path.span, + span_help!(this.tcx().sess, span, "add #![feature(default_type_params)] to the crate attributes to enable"); } @@ -333,12 +353,11 @@ fn ast_path_substs<'tcx,AC,RS>( // This is a default type parameter. let default = default.subst_spanned(tcx, &substs, - Some(path.span)); + Some(span)); substs.types.push(TypeSpace, default); } None => { - tcx.sess.span_bug(path.span, - "extra parameter without default"); + tcx.sess.span_bug(span, "extra parameter without default"); } } } @@ -346,53 +365,64 @@ fn ast_path_substs<'tcx,AC,RS>( for param in decl_generics.types.get_slice(AssocSpace).iter() { substs.types.push( AssocSpace, - this.associated_type_binding(path.span, + this.associated_type_binding(span, associated_ty, decl_def_id, param.def_id)) } return substs; +} - fn angle_bracketed_parameters<'tcx, AC, RS>(this: &AC, - rscope: &RS, - data: &ast::AngleBracketedParameterData) - -> (Vec, Vec) - where AC: AstConv<'tcx>, RS: RegionScope - { - let regions: Vec<_> = - data.lifetimes.iter() - .map(|l| ast_region_to_region(this.tcx(), l)) - .collect(); - - let types: Vec<_> = - data.types.iter() - .map(|t| ast_ty_to_ty(this, rscope, &**t)) - .collect(); - - (regions, types) - } +fn convert_angle_bracketed_parameters<'tcx, AC, RS>(this: &AC, + rscope: &RS, + data: &ast::AngleBracketedParameterData) + -> (Vec, Vec) + where AC: AstConv<'tcx>, RS: RegionScope +{ + let regions: Vec<_> = + data.lifetimes.iter() + .map(|l| ast_region_to_region(this.tcx(), l)) + .collect(); - fn parenthesized_parameters<'tcx,AC>(this: &AC, - binder_id: ast::NodeId, - data: &ast::ParenthesizedParameterData) - -> (Vec, Vec) - where AC: AstConv<'tcx> - { - let binding_rscope = BindingRscope::new(binder_id); - - let inputs = data.inputs.iter() - .map(|a_t| ast_ty_to_ty(this, &binding_rscope, &**a_t)) - .collect(); - let input_ty = ty::mk_tup(this.tcx(), inputs); - - let output = match data.output { - Some(ref output_ty) => ast_ty_to_ty(this, &binding_rscope, &**output_ty), - None => ty::mk_nil(this.tcx()) - }; + let types: Vec<_> = + data.types.iter() + .map(|t| ast_ty_to_ty(this, rscope, &**t)) + .collect(); - (Vec::new(), vec![input_ty, output]) - } + (regions, types) +} + +fn convert_parenthesized_parameters<'tcx,AC>(this: &AC, + data: &ast::ParenthesizedParameterData) + -> Vec + where AC: AstConv<'tcx> +{ + let binding_rscope = BindingRscope::new(); + + let inputs = data.inputs.iter() + .map(|a_t| ast_ty_to_ty(this, &binding_rscope, &**a_t)) + .collect(); + let input_ty = ty::mk_tup(this.tcx(), inputs); + + let output = match data.output { + Some(ref output_ty) => ast_ty_to_ty(this, &binding_rscope, &**output_ty), + None => ty::mk_nil(this.tcx()), + }; + + 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, + associated_type: Option) + -> Rc + where AC: AstConv<'tcx>, RS: RegionScope +{ + instantiate_trait_ref(this, rscope, &ast_trait_ref.trait_ref, self_ty, associated_type) } pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC, @@ -413,16 +443,9 @@ pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC, match lookup_def_tcx(this.tcx(), ast_trait_ref.path.span, ast_trait_ref.ref_id) { - def::DefTrait(trait_did) => { - let trait_ref = - Rc::new(ast_path_to_trait_ref(this, - rscope, - trait_did, - self_ty, - associated_type, - &ast_trait_ref.path, - ast_trait_ref.ref_id)); - + def::DefTrait(trait_def_id) => { + let trait_ref = Rc::new(ast_path_to_trait_ref(this, rscope, trait_def_id, self_ty, + associated_type, &ast_trait_ref.path)); this.tcx().trait_refs.borrow_mut().insert(ast_trait_ref.ref_id, trait_ref.clone()); trait_ref @@ -435,36 +458,52 @@ pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC, } } -pub fn ast_path_to_trait_ref<'tcx,AC,RS>(this: &AC, - rscope: &RS, - trait_def_id: ast::DefId, - self_ty: Option, - associated_type: Option, - path: &ast::Path, - binder_id: ast::NodeId) - -> ty::TraitRef - where AC: AstConv<'tcx>, - RS: RegionScope { +fn ast_path_to_trait_ref<'tcx,AC,RS>( + this: &AC, + rscope: &RS, + trait_def_id: ast::DefId, + self_ty: Option, + associated_type: Option, + path: &ast::Path) + -> ty::TraitRef + where AC: AstConv<'tcx>, RS: RegionScope +{ let trait_def = this.get_trait_def(trait_def_id); - ty::TraitRef { - def_id: trait_def_id, - substs: ast_path_substs(this, - rscope, - trait_def_id, - &trait_def.generics, - self_ty, - associated_type, - path, - binder_id) - } + + // the trait reference introduces a binding level here, so + // we need to shift the `rscope`. It'd be nice if we could + // do away with this rscope stuff and work this knowledge + // into resolve_lifetimes, as we do with non-omitted + // lifetimes. Oh well, not there yet. + let shifted_rscope = ShiftedRscope::new(rscope); + + let (regions, types) = match path.segments.last().unwrap().parameters { + ast::AngleBracketedParameters(ref data) => { + convert_angle_bracketed_parameters(this, &shifted_rscope, data) + } + ast::ParenthesizedParameters(ref data) => { + (Vec::new(), convert_parenthesized_parameters(this, data)) + } + }; + + let substs = create_substs_for_ast_path(this, + &shifted_rscope, + path.span, + trait_def_id, + &trait_def.generics, + self_ty, + types, + regions, + associated_type); + + ty::TraitRef::new(trait_def_id, substs) } pub fn ast_path_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( this: &AC, rscope: &RS, did: ast::DefId, - path: &ast::Path, - binder_id: ast::NodeId) + path: &ast::Path) -> TypeAndSubsts { let tcx = this.tcx(); @@ -473,14 +512,13 @@ pub fn ast_path_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( ty: decl_ty } = this.get_item_ty(did); - let substs = ast_path_substs(this, - rscope, - did, - &generics, - None, - None, - path, - binder_id); + let substs = ast_path_substs_for_ty(this, + rscope, + did, + &generics, + None, + None, + path); let ty = decl_ty.subst(tcx, &substs); TypeAndSubsts { substs: substs, ty: ty } } @@ -494,8 +532,7 @@ pub fn ast_path_to_ty_relaxed<'tcx,AC,RS>( this: &AC, rscope: &RS, did: ast::DefId, - path: &ast::Path, - binder_id: ast::NodeId) + path: &ast::Path) -> TypeAndSubsts where AC : AstConv<'tcx>, RS : RegionScope { @@ -521,7 +558,7 @@ pub fn ast_path_to_ty_relaxed<'tcx,AC,RS>( Substs::new(VecPerParamSpace::params_from_type(type_params), VecPerParamSpace::params_from_type(region_params)) } else { - ast_path_substs(this, rscope, did, &generics, None, None, path, binder_id) + ast_path_substs_for_ty(this, rscope, did, &generics, None, None, path) }; let ty = decl_ty.subst(tcx, &substs); @@ -628,7 +665,7 @@ pub fn ast_ty_to_builtin_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( match a_def { def::DefTy(did, _) | def::DefStruct(did) if Some(did) == this.tcx().lang_items.owned_box() => { - let ty = ast_path_to_ty(this, rscope, did, path, id).ty; + let ty = ast_path_to_ty(this, rscope, did, path).ty; match ty::get(ty).sty { ty::ty_struct(struct_def_id, ref substs) => { assert_eq!(struct_def_id, did); @@ -689,8 +726,7 @@ fn mk_pointer<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( trait_def_id, None, None, - path, - id); + path); let empty_vec = []; let bounds = match *opt_bounds { None => empty_vec.as_slice(), Some(ref bounds) => bounds.as_slice() }; @@ -752,12 +788,7 @@ fn associated_ty_to_ty<'tcx,AC,RS>(this: &AC, trait_did, None, Some(for_type), - trait_path, - ast::DUMMY_NODE_ID); // *see below - - // * The trait in a qualified path cannot be "higher-ranked" and - // hence cannot use the parenthetical sugar, so the binder-id is - // irrelevant. + trait_path); debug!("associated_ty_to_ty(trait_ref={})", trait_ref.repr(this.tcx())); @@ -830,8 +861,7 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( tcx.sess.span_err(ast_ty.span, "variadic function must have C calling convention"); } - ty::mk_bare_fn(tcx, ty_of_bare_fn(this, ast_ty.id, bf.fn_style, - bf.abi, &*bf.decl)) + ty::mk_bare_fn(tcx, ty_of_bare_fn(this, bf.fn_style, bf.abi, &*bf.decl)) } ast::TyClosure(ref f) => { // Use corresponding trait store to figure out default bounds @@ -842,7 +872,6 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( [].as_slice(), f.bounds.as_slice()); let fn_decl = ty_of_closure(this, - ast_ty.id, f.fn_style, f.onceness, bounds, @@ -863,7 +892,6 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( f.bounds.as_slice()); let fn_decl = ty_of_closure(this, - ast_ty.id, f.fn_style, f.onceness, bounds, @@ -874,15 +902,8 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( ty::mk_closure(tcx, fn_decl) } - ast::TyPolyTraitRef(ref data) => { - // FIXME(#18639) this is just a placeholder for code to come - let principal = instantiate_trait_ref(this, rscope, &data.trait_ref, None, None); - let bounds = conv_existential_bounds(this, - rscope, - ast_ty.span, - &[principal.clone()], - &[]); - ty::mk_trait(tcx, (*principal).clone(), bounds) + ast::TyPolyTraitRef(ref bounds) => { + conv_ty_poly_trait_ref(this, rscope, ast_ty.span, bounds.as_slice()) } ast::TyPath(ref path, ref bounds, id) => { let a_def = match tcx.def_map.borrow().get(&id) { @@ -910,8 +931,7 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( trait_def_id, None, None, - path, - id); + path); let empty_bounds: &[ast::TyParamBound] = &[]; let ast_bounds = match *bounds { Some(ref b) => b.as_slice(), @@ -922,12 +942,12 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( ast_ty.span, &[Rc::new(result.clone())], ast_bounds); - ty::mk_trait(tcx, - result, - bounds) + let result_ty = ty::mk_trait(tcx, result, bounds); + debug!("ast_ty_to_ty: result_ty={}", result_ty.repr(this.tcx())); + result_ty } def::DefTy(did, _) | def::DefStruct(did) => { - ast_path_to_ty(this, rscope, did, path, id).ty + ast_path_to_ty(this, rscope, did, path).ty } def::DefTyParam(space, id, n) => { check_path_args(tcx, path, NO_TPS | NO_REGIONS); @@ -1056,7 +1076,6 @@ struct SelfInfo<'a> { pub fn ty_of_method<'tcx, AC: AstConv<'tcx>>( this: &AC, - id: ast::NodeId, fn_style: ast::FnStyle, untransformed_self_ty: ty::t, explicit_self: &ast::ExplicitSelf, @@ -1069,7 +1088,6 @@ pub fn ty_of_method<'tcx, AC: AstConv<'tcx>>( }); let (bare_fn_ty, optional_explicit_self_category) = ty_of_method_or_bare_fn(this, - id, fn_style, abi, self_info, @@ -1077,17 +1095,14 @@ pub fn ty_of_method<'tcx, AC: AstConv<'tcx>>( (bare_fn_ty, optional_explicit_self_category.unwrap()) } -pub fn ty_of_bare_fn<'tcx, AC: AstConv<'tcx>>(this: &AC, id: ast::NodeId, - fn_style: ast::FnStyle, abi: abi::Abi, +pub fn ty_of_bare_fn<'tcx, AC: AstConv<'tcx>>(this: &AC, fn_style: ast::FnStyle, abi: abi::Abi, decl: &ast::FnDecl) -> ty::BareFnTy { - let (bare_fn_ty, _) = - ty_of_method_or_bare_fn(this, id, fn_style, abi, None, decl); + let (bare_fn_ty, _) = ty_of_method_or_bare_fn(this, fn_style, abi, None, decl); bare_fn_ty } fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>( this: &AC, - id: ast::NodeId, fn_style: ast::FnStyle, abi: abi::Abi, opt_self_info: Option, @@ -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`. 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(r, mt) => ty::ByReferenceExplicitSelfCategory(r, mt.mutbl), + ty::ty_uniq(_) => ty::ByBoxExplicitSelfCategory, + _ => ty::ByValueExplicitSelfCategory, } } + } + }; - 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 - } - } + 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::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], + 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, diff --git a/src/librustc/middle/typeck/check/method/confirm.rs b/src/librustc/middle/typeck/check/method/confirm.rs index ba64a1e23a70f..b4d22f117d4b7 100644 --- a/src/librustc/middle/typeck/check/method/confirm.rs +++ b/src/librustc/middle/typeck/check/method/confirm.rs @@ -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 - } - - fn add_obligations(&mut self, - pick: &probe::Pick, - all_substs: &subst::Substs) { + // 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; + + debug!("late-bound lifetimes from impl instantiated, \ + all_substs={} method_sig={} method_generics={}", + all_substs.repr(self.tcx()), + method_sig.repr(self.tcx()), + method_generics.repr(self.tcx())); + + // Instantiate the bounds on the method with the + // type/early-bound-regions substitutions performed. The only + // late-bound-regions that can appear in bounds are from the + // impl, and those were already instantiated above. + // // FIXME(DST). Super hack. For a method on a trait object // `Trait`, the generic signature requires that // `Self:Trait`. Since, for an object, we bind `Self` to the @@ -381,24 +423,54 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { // obligations. This causes us to generate the obligation // `err:Trait`, and the error type is considered to implement // all traits, so we're all good. Hack hack hack. - match pick.kind { + let method_bounds_substs = match pick.kind { probe::ObjectPick(..) => { let mut temp_substs = all_substs.clone(); temp_substs.types.get_mut_slice(subst::SelfSpace)[0] = ty::mk_err(); - self.fcx.add_obligations_for_parameters( - traits::ObligationCause::misc(self.span), - &temp_substs, - &pick.method_ty.generics); + temp_substs } _ => { - self.fcx.add_obligations_for_parameters( - traits::ObligationCause::misc(self.span), - all_substs, - &pick.method_ty.generics); + all_substs.clone() } + }; + let method_bounds = + method_generics.to_bounds(self.tcx(), &method_bounds_substs); + + debug!("method_bounds after subst = {}", + method_bounds.repr(self.tcx())); + + // Substitute the type/early-bound-regions into the method + // signature. In addition, the method signature may bind + // late-bound regions, so instantiate those. + let method_sig = method_sig.subst(self.tcx(), &all_substs); + let method_sig = self.replace_late_bound_regions_with_fresh_var(&method_sig); + + debug!("late-bound lifetimes from method instantiated, method_sig={}", + method_sig.repr(self.tcx())); + + InstantiatedMethodSig { + method_sig: method_sig, + all_substs: all_substs, + method_bounds_substs: method_bounds_substs, + method_bounds: method_bounds, } } + fn add_obligations(&mut self, + pick: &probe::Pick, + method_bounds_substs: &subst::Substs, + method_bounds: &ty::GenericBounds) { + debug!("add_obligations: pick={} method_bounds_substs={} method_bounds={}", + pick.repr(self.tcx()), + method_bounds_substs.repr(self.tcx()), + method_bounds.repr(self.tcx())); + + self.fcx.add_obligations_for_parameters( + traits::ObligationCause::misc(self.span), + method_bounds_substs, + method_bounds); + } + /////////////////////////////////////////////////////////////////////////// // RECONCILIATION @@ -591,6 +663,13 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { source_trait_ref.repr(self.tcx()), target_trait_def_id.repr(self.tcx()))[]); } + + fn replace_late_bound_regions_with_fresh_var(&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, diff --git a/src/librustc/middle/typeck/check/method/mod.rs b/src/librustc/middle/typeck/check/method/mod.rs index d4f1d4defa3d7..6c7df2cd07ed2 100644 --- a/src/librustc/middle/typeck/check/method/mod.rs +++ b/src/librustc/middle/typeck/check/method/mod.rs @@ -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 diff --git a/src/librustc/middle/typeck/check/method/probe.rs b/src/librustc/middle/typeck/check/method/probe.rs index 2e5397e768e33..f9c3fa86752ea 100644 --- a/src/librustc/middle/typeck/check/method/probe.rs +++ b/src/librustc/middle/typeck/check/method/probe.rs @@ -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 { - 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(&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) } } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 33705343dabe7..8ae5c3a0a9597 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -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 { - // 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 { fn method>() { ... } } + // + // impl<'a> Foo<&'a T> for &'a U { + // fn method>() { ... } + // } + // + // Here, the region parameter `'a` is late-bound, so in the bound + // `Bound<&'a T>`, the lifetime `'a` will be late-bound with a + // depth of 3 (it is nested within 3 binders: the impl, method, + // and trait-ref itself). So when we do the liberation, we have + // two introduce two `ty::bind` scopes, one for the impl and one + // the method. + // + // The only late-bounded regions that can possibly appear here are + // from the impl, not the method. This is because region + // parameters declared on the method which appear in a type bound + // would be early bound. On the trait side, there can be no + // late-bound lifetimes because trait definitions do not introduce + // a late region binder. + let trait_bounds = + trait_m.generics.types.get_slice(subst::FnSpace).iter() + .map(|trait_param_def| &trait_param_def.bounds); + let impl_bounds = + impl_m.generics.types.get_slice(subst::FnSpace).iter() + .map(|impl_param_def| + liberate_late_bound_regions( + tcx, + impl_m_body_id, + &ty::bind(ty::bind(impl_param_def.bounds.clone()))).value.value); + for (i, (trait_param_bounds, impl_param_bounds)) in + trait_bounds.zip(impl_bounds).enumerate() + { // Check that the impl does not require any builtin-bounds // that the trait does not guarantee: let extra_bounds = - impl_param_def.bounds.builtin_bounds - - trait_param_def.bounds.builtin_bounds; + impl_param_bounds.builtin_bounds - + trait_param_bounds.builtin_bounds; if !extra_bounds.is_empty() { span_err!(tcx.sess, impl_m_span, E0051, "in method `{}`, type parameter {} requires `{}`, \ @@ -1099,31 +1164,32 @@ fn compare_impl_method(tcx: &ty::ctxt, // // FIXME(pcwalton): We could be laxer here regarding sub- and super- // traits, but I doubt that'll be wanted often, so meh. - for impl_trait_bound in impl_param_def.bounds.trait_bounds.iter() { + for impl_trait_bound in impl_param_bounds.trait_bounds.iter() { debug!("compare_impl_method(): impl-trait-bound subst"); let impl_trait_bound = impl_trait_bound.subst(tcx, &impl_to_skol_substs); - let mut ok = false; - for trait_bound in trait_param_def.bounds.trait_bounds.iter() { - debug!("compare_impl_method(): trait-bound subst"); - let trait_bound = - trait_bound.subst(tcx, &trait_to_skol_substs); - let infcx = infer::new_infer_ctxt(tcx); - match infer::mk_sub_trait_refs(&infcx, - true, - infer::Misc(impl_m_span), - trait_bound, - impl_trait_bound.clone()) { - Ok(_) => { - ok = true; - break - } - Err(_) => continue, - } - } + // There may be late-bound regions from the impl in the + // impl's bound, so "liberate" those. Note that the + // trait_to_skol_substs is derived from the impl's + // trait-ref, and the late-bound regions appearing there + // have already been liberated, so the result should match + // up. + + let found_match_in_trait = + trait_param_bounds.trait_bounds.iter().any(|trait_bound| { + debug!("compare_impl_method(): trait-bound subst"); + let trait_bound = + trait_bound.subst(tcx, &trait_to_skol_substs); + let infcx = infer::new_infer_ctxt(tcx); + infer::mk_sub_trait_refs(&infcx, + true, + infer::Misc(impl_m_span), + trait_bound, + impl_trait_bound.clone()).is_ok() + }); - if !ok { + if !found_match_in_trait { span_err!(tcx.sess, impl_m_span, E0052, "in method `{}`, type parameter {} requires bound `{}`, which is not \ required by the corresponding type parameter in the trait declaration", @@ -1134,9 +1200,11 @@ fn compare_impl_method(tcx: &ty::ctxt, } } - // Compute skolemized form of impl and trait method tys. + // Compute skolemized form of impl and trait method tys. Note + // that we must liberate the late-bound regions from the impl. let impl_fty = ty::mk_bare_fn(tcx, impl_m.fty.clone()); let impl_fty = impl_fty.subst(tcx, &impl_to_skol_substs); + let impl_fty = liberate_late_bound_regions(tcx, impl_m_body_id, &ty::bind(impl_fty)).value; let trait_fty = ty::mk_bare_fn(tcx, trait_m.fty.clone()); let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs); @@ -1171,6 +1239,7 @@ fn compare_impl_method(tcx: &ty::ctxt, fn check_region_bounds_on_impl_method(tcx: &ty::ctxt, span: Span, impl_m: &ty::Method, + impl_m_body_id: ast::NodeId, trait_generics: &ty::Generics, impl_generics: &ty::Generics, trait_to_skol_substs: &Substs, @@ -1216,9 +1285,13 @@ fn compare_impl_method(tcx: &ty::ctxt, debug!("check_region_bounds_on_impl_method: \ trait_generics={} \ - impl_generics={}", + impl_generics={} \ + trait_to_skol_substs={} \ + impl_to_skol_substs={}", trait_generics.repr(tcx), - impl_generics.repr(tcx)); + impl_generics.repr(tcx), + trait_to_skol_substs.repr(tcx), + impl_to_skol_substs.repr(tcx)); // Must have same number of early-bound lifetime parameters. // Unfortunately, if the user screws up the bounds, then this @@ -1249,6 +1322,18 @@ fn compare_impl_method(tcx: &ty::ctxt, let impl_bounds = impl_param.bounds.subst(tcx, impl_to_skol_substs); + // The bounds may reference late-bound regions from the + // impl declaration. In that case, we want to replace + // those with the liberated variety so as to match the + // versions appearing in the `trait_to_skol_substs`. + // There are two-levels of binder to be aware of: the + // impl, and the method. + let impl_bounds = + ty::liberate_late_bound_regions( + tcx, + impl_m_body_id, + &ty::bind(ty::bind(impl_bounds))).value.value; + debug!("check_region_bounds_on_impl_method: \ trait_param={} \ impl_param={} \ @@ -1603,15 +1688,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub fn write_ty_substs(&self, - node_id: ast::NodeId, - ty: ty::t, - substs: ty::ItemSubsts) { - let ty = ty.subst(self.tcx(), &substs.substs); - self.write_ty(node_id, ty); - self.write_substs(node_id, substs); - } - pub fn write_autoderef_adjustment(&self, node_id: ast::NodeId, span: Span, @@ -1709,17 +1785,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub fn instantiate_item_type(&self, - span: Span, - def_id: ast::DefId) - -> TypeAndSubsts + pub fn instantiate_type(&self, + span: Span, + def_id: ast::DefId) + -> TypeAndSubsts { /*! * Returns the type of `def_id` with all generics replaced by * by fresh type/region variables. Also returns the * substitution from the type parameters on `def_id` to the - * fresh variables. Registers any trait obligations specified + * fresh variables. Registers any trait obligations specified * on `def_id` at the same time. + * + * Note that function is only intended to be used with types + * (notably, not impls). This is because it doesn't do any + * instantiation of late-bound regions. */ let polytype = @@ -1728,12 +1808,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.infcx().fresh_substs_for_generics( span, &polytype.generics); + let bounds = + polytype.generics.to_bounds(self.tcx(), &substs); self.add_obligations_for_parameters( traits::ObligationCause::new( span, traits::ItemObligation(def_id)), &substs, - &polytype.generics); + &bounds); let monotype = polytype.ty.subst(self.tcx(), &substs); @@ -1958,8 +2040,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut region_obligations = self.inh.region_obligations.borrow_mut(); let region_obligation = RegionObligation { sub_region: r, - sup_type: ty, - origin: origin }; + sup_type: ty, + origin: origin }; match region_obligations.entry(self.body_id) { Vacant(entry) => { entry.set(vec![region_obligation]); }, @@ -1970,12 +2052,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn add_obligations_for_parameters(&self, cause: traits::ObligationCause, substs: &Substs, - generics: &ty::Generics) + generic_bounds: &ty::GenericBounds) { /*! - * Given a set of generic parameter definitions (`generics`) - * and the values provided for each of them (`substs`), - * creates and registers suitable region obligations. + * Given a fully substituted set of bounds (`generic_bounds`), + * and the values with which each type/region parameter was + * instantiated (`substs`), creates and registers suitable + * trait/region obligations. * * For example, if there is a function: * @@ -1991,60 +2074,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { * locally. */ - debug!("add_obligations_for_parameters(substs={}, generics={})", + assert!(!generic_bounds.has_escaping_regions()); + + debug!("add_obligations_for_parameters(substs={}, generic_bounds={})", substs.repr(self.tcx()), - generics.repr(self.tcx())); + generic_bounds.repr(self.tcx())); - self.add_trait_obligations_for_generics(cause, substs, generics); - self.add_region_obligations_for_generics(cause, substs, generics); + 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,11 +3133,9 @@ fn check_expr_with_unifier(fcx: &FnCtxt, // Replace any bound regions that appear in the function // signature with region variables let fn_sig = - fcx.infcx().replace_late_bound_regions_with_fresh_var( - fn_sig.binder_id, - call_expr.span, - infer::FnCall, - fn_sig).0; + fcx.infcx().replace_late_bound_regions_with_fresh_var(call_expr.span, + infer::FnCall, + fn_sig).0; // Call the generic checker. check_argument_types(fcx, @@ -3437,7 +3517,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt, body: &ast::Block) { let mut fn_ty = astconv::ty_of_closure( fcx, - expr.id, ast::NormalFn, ast::Many, @@ -3508,6 +3587,10 @@ fn check_expr_with_unifier(fcx: &FnCtxt, expected: Expectation) { let tcx = fcx.ccx.tcx; + debug!("check_expr_fn(expr={}, expected={})", + expr.repr(tcx), + expected.repr(tcx)); + // Find the expected input/output types (if any). Substitute // fresh bound regions for any bound regions we find in the // expected types so as to avoid capture. @@ -3517,10 +3600,11 @@ fn check_expr_with_unifier(fcx: &FnCtxt, expected_bounds) = { match expected_sty { Some(ty::ty_closure(ref cenv)) => { - let (_, sig) = + let (sig, _) = replace_late_bound_regions( - tcx, cenv.sig.binder_id, &cenv.sig, - |_| fcx.inh.infcx.fresh_bound_region(expr.id)); + tcx, + &cenv.sig, + |_, debruijn| fcx.inh.infcx.fresh_bound_region(debruijn)); let onceness = match (&store, &cenv.store) { // As the closure type and onceness go, only three // combinations are legit: @@ -3561,7 +3645,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt, // construct the function type let fn_ty = astconv::ty_of_closure(fcx, - expr.id, ast::NormalFn, expected_onceness, expected_bounds, @@ -3818,7 +3901,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, let TypeAndSubsts { ty: mut struct_type, substs: struct_substs - } = fcx.instantiate_item_type(span, class_id); + } = fcx.instantiate_type(span, class_id); // Look up and check the fields. let class_fields = ty::lookup_struct_fields(tcx, class_id); @@ -3860,7 +3943,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, let TypeAndSubsts { ty: enum_type, substs: substitutions - } = fcx.instantiate_item_type(span, enum_id); + } = fcx.instantiate_type(span, enum_id); // Look up and check the enum variant fields. let variant_fields = ty::lookup_struct_fields(tcx, variant_id); @@ -4454,8 +4537,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, let type_and_substs = astconv::ast_path_to_ty_relaxed(fcx, fcx.infcx(), struct_id, - path, - expr.id); + path); match fcx.mk_subty(false, infer::Misc(path.span), actual_structure_type, @@ -5339,14 +5421,39 @@ pub fn instantiate_path(fcx: &FnCtxt, assert_eq!(substs.regions().len(space), region_defs.len(space)); } + // The things we are substituting into the type should not contain + // escaping late-bound regions. + assert!(!substs.has_regions_escaping_depth(0)); + + // In the case of static items taken from impls, there may be + // late-bound regions associated with the impl (not declared on + // the fn itself). Those should be replaced with fresh variables + // now. These can appear either on the type being referenced, or + // on the associated bounds. + let bounds = polytype.generics.to_bounds(fcx.tcx(), &substs); + let (ty_late_bound, bounds) = + fcx.infcx().replace_late_bound_regions_with_fresh_var( + span, + infer::FnCall, + &ty::bind((polytype.ty, bounds))).0.value; + + debug!("after late-bounds have been replaced: ty_late_bound={}", ty_late_bound.repr(fcx.tcx())); + debug!("after late-bounds have been replaced: bounds={}", bounds.repr(fcx.tcx())); + fcx.add_obligations_for_parameters( traits::ObligationCause::new(span, traits::ItemObligation(def.def_id())), &substs, - &polytype.generics); + &bounds); - fcx.write_ty_substs(node_id, polytype.ty, ty::ItemSubsts { - substs: substs, - }); + // Substitute the values for the type parameters into the type of + // the referenced item. + let ty_substituted = ty_late_bound.subst(fcx.tcx(), &substs); + + debug!("ty_substituted: ty_substituted={}", ty_substituted.repr(fcx.tcx())); + + fcx.write_ty(node_id, ty_substituted); + fcx.write_substs(node_id, ty::ItemSubsts { substs: substs }); + return; fn report_error_if_segment_contains_type_parameters( fcx: &FnCtxt, @@ -5739,7 +5846,8 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) { "move_val_init" => { (1u, vec!( - ty::mk_mut_rptr(tcx, ty::ReLateBound(it.id, ty::BrAnon(0)), param(ccx, 0)), + ty::mk_mut_rptr(tcx, ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrAnon(0)), + param(ccx, 0)), param(ccx, 0u) ), ty::mk_nil(tcx)) @@ -5943,7 +6051,6 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) { fn_style: ast::UnsafeFn, abi: abi::RustIntrinsic, sig: FnSig { - binder_id: it.id, inputs: inputs, output: output, variadic: false, diff --git a/src/librustc/middle/typeck/check/regionmanip.rs b/src/librustc/middle/typeck/check/regionmanip.rs index b7710ab7bf9b9..2e727a8ef9aa5 100644 --- a/src/librustc/middle/typeck/check/regionmanip.rs +++ b/src/librustc/middle/typeck/check/regionmanip.rs @@ -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( - tcx: &ty::ctxt, - binder_id: ast::NodeId, - value: &T, - map_fn: |ty::BoundRegion| -> ty::Region) - -> (FnvHashMap, 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::Region, ty::Region), RegionSubParamConstraint(Option, ty::Region, ty::ParamTy), diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index 498594716e7ab..041d21a8baf36 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -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::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, diff --git a/src/librustc/middle/typeck/check/wf.rs b/src/librustc/middle/typeck/check/wf.rs index b3449d658f6d6..8e02f9f7bfd57 100644 --- a/src/librustc/middle/typeck/check/wf.rs +++ b/src/librustc/middle/typeck/check/wf.rs @@ -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; } diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index a5ca5179f08b0..a0b198a59c266 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -214,12 +214,11 @@ pub fn get_enum_variant_types(ccx: &CrateCtxt, for variant in variants.iter() { // Nullary enum constructors get turned into constants; n-ary enum // constructors get turned into functions. - let scope = variant.node.id; let result_ty = match variant.node.kind { ast::TupleVariantKind(ref args) if args.len() > 0 => { let rs = ExplicitRscope; let input_tys: Vec<_> = args.iter().map(|va| ccx.to_ty(&rs, &*va.ty)).collect(); - ty::mk_ctor_fn(tcx, scope, input_tys.as_slice(), enum_ty) + ty::mk_ctor_fn(tcx, input_tys.as_slice(), enum_ty) } ast::TupleVariantKind(_) => { @@ -403,7 +402,6 @@ fn collect_trait_methods(ccx: &CrateCtxt, let trait_self_ty = ty::mk_self_type(tmcx.tcx(), local_def(trait_id)); astconv::ty_of_method(&tmcx, - *m_id, *m_fn_style, trait_self_ty, m_explicit_self, @@ -588,7 +586,6 @@ fn convert_methods<'a,I>(ccx: &CrateCtxt, method_generics: &m_ty_generics, }; astconv::ty_of_method(&imcx, - m.id, m.pe_fn_style(), untransformed_rcvr_ty, m.pe_explicit_self(), @@ -603,7 +600,6 @@ fn convert_methods<'a,I>(ccx: &CrateCtxt, method_generics: &m_ty_generics, }; astconv::ty_of_method(&tmcx, - m.id, m.pe_fn_style(), untransformed_rcvr_ty, m.pe_explicit_self(), @@ -1116,10 +1112,12 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::Item) { for impl_item in impl_items.iter() { match *impl_item { ast::MethodImplItem(ref method) => { + let body_id = method.pe_body().id; check_method_self_type(ccx, - &BindingRscope::new(method.id), + &BindingRscope::new(), selfty, - method.pe_explicit_self()); + method.pe_explicit_self(), + body_id); methods.push(&**method); } ast::TypeImplItem(ref typedef) => { @@ -1174,17 +1172,19 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::Item) { local_def(it.id)); match *trait_method { ast::RequiredMethod(ref type_method) => { - let rscope = BindingRscope::new(type_method.id); + let rscope = BindingRscope::new(); check_method_self_type(ccx, &rscope, self_type, - &type_method.explicit_self) + &type_method.explicit_self, + it.id) } ast::ProvidedMethod(ref method) => { check_method_self_type(ccx, - &BindingRscope::new(method.id), + &BindingRscope::new(), self_type, - method.pe_explicit_self()) + method.pe_explicit_self(), + it.id) } ast::TypeTraitItem(ref associated_type) => { convert_associated_type(ccx, @@ -1294,7 +1294,6 @@ pub fn convert_struct(ccx: &CrateCtxt, |field| (*tcx.tcache.borrow())[ local_def(field.node.id)].ty).collect(); let ctor_fn_ty = ty::mk_ctor_fn(tcx, - ctor_id, inputs.as_slice(), selfty); write_ty_to_tcx(tcx, ctor_id, ctor_fn_ty); @@ -1465,11 +1464,7 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: &ast::Item) ccx: ccx, generics: &ty_generics, }; - astconv::ty_of_bare_fn(&fcx, - it.id, - fn_style, - abi, - &**decl) + astconv::ty_of_bare_fn(&fcx, fn_style, abi, &**decl) }; let pty = Polytype { generics: ty_generics, @@ -2015,12 +2010,12 @@ fn conv_param_bounds<'tcx,AC>(this: &AC, astconv::partition_bounds(this.tcx(), span, all_bounds.as_slice()); let trait_bounds: Vec> = trait_bounds.into_iter() - .map(|b| { - astconv::instantiate_trait_ref(this, - &ExplicitRscope, - b, - Some(param_ty.to_ty(this.tcx())), - Some(param_ty.to_ty(this.tcx()))) + .map(|bound| { + astconv::instantiate_poly_trait_ref(this, + &ExplicitRscope, + bound, + Some(param_ty.to_ty(this.tcx())), + Some(param_ty.to_ty(this.tcx()))) }) .collect(); let region_bounds: Vec = @@ -2091,7 +2086,7 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt, ast_generics, ty::Generics::empty(), DontCreateTypeParametersForAssociatedTypes); - let rb = BindingRscope::new(def_id.node); + let rb = BindingRscope::new(); let input_tys = decl.inputs .iter() .map(|a| ty_of_arg(ccx, &rb, a, None)) @@ -2109,8 +2104,7 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt, ty::BareFnTy { abi: abi, fn_style: ast::UnsafeFn, - sig: ty::FnSig {binder_id: def_id.node, - inputs: input_tys, + sig: ty::FnSig {inputs: input_tys, output: output, variadic: decl.variadic} }); @@ -2142,10 +2136,12 @@ pub fn mk_item_substs(ccx: &CrateCtxt, /// Verifies that the explicit self type of a method matches the impl or /// trait. fn check_method_self_type( - crate_context: &CrateCtxt, - rs: &RS, - required_type: ty::t, - explicit_self: &ast::ExplicitSelf) { + crate_context: &CrateCtxt, + rs: &RS, + required_type: ty::t, + explicit_self: &ast::ExplicitSelf, + body_id: ast::NodeId) +{ match explicit_self.node { ast::SelfExplicit(ref ast_type, _) => { let typ = crate_context.to_ty(rs, &**ast_type); @@ -2154,13 +2150,44 @@ fn check_method_self_type( 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)) diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs index 078a2c10bcb35..0f9554cd417f7 100644 --- a/src/librustc/middle/typeck/infer/combine.rs +++ b/src/librustc/middle/typeck/infer/combine.rs @@ -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 { - // 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; + // 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 { - - fn argvecs<'tcx, C: Combine<'tcx>>(this: &C, - a_args: &[ty::t], - b_args: &[ty::t]) - -> cres> { - 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 { let tcx = this.infcx().tcx; diff --git a/src/librustc/middle/typeck/infer/equate.rs b/src/librustc/middle/typeck/infer/equate.rs index 97453dc86efd4..3874f5fc5e4aa 100644 --- a/src/librustc/middle/typeck/infer/equate.rs +++ b/src/librustc/middle/typeck/infer/equate.rs @@ -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 { + try!(self.sub().trait_refs(a, b)); + self.sub().trait_refs(b, a) + } } diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index 8c55872d96217..65bd21b14e025 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -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) => { diff --git a/src/librustc/middle/typeck/infer/glb.rs b/src/librustc/middle/typeck/infer/glb.rs index 4ff4857ea06bd..287a5cfba9e34 100644 --- a/src/librustc/middle/typeck/infer/glb.rs +++ b/src/librustc/middle/typeck/infer/glb.rs @@ -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 { - // 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, - a_vars: &[RegionVid], - b_vars: &[RegionVid], - r0: ty::Region) -> ty::Region { - if !is_var_in_set(new_vars, r0) { - assert!(!r0.is_bound()); - return r0; - } - - let tainted = this.fields.infcx.region_vars.tainted(mark, r0); - - let mut a_r = None; - let mut b_r = None; - let mut only_new_vars = true; - for r in tainted.iter() { - if is_var_in_set(a_vars, *r) { - if a_r.is_some() { - return fresh_bound_variable(this, new_binder_id); - } else { - a_r = Some(*r); - } - } else if is_var_in_set(b_vars, *r) { - if b_r.is_some() { - return fresh_bound_variable(this, new_binder_id); - } else { - b_r = Some(*r); - } - } else if !is_var_in_set(new_vars, *r) { - only_new_vars = false; - } - } - - // NB---I do not believe this algorithm computes - // (necessarily) the GLB. As written it can - // spuriously fail. In particular, if there is a case - // like: |fn(&a)| and fn(fn(&b)), where a and b are - // free, it will return fn(&c) where c = GLB(a,b). If - // however this GLB is not defined, then the result is - // an error, even though something like - // "fn(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, - 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()) - } + self.higher_ranked_glb(a, b) + } - 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 { + self.higher_ranked_glb(a, b) } } diff --git a/src/librustc/middle/typeck/infer/higher_ranked/doc.rs b/src/librustc/middle/typeck/infer/higher_ranked/doc.rs new file mode 100644 index 0000000000000..4c4452ac89236 --- /dev/null +++ b/src/librustc/middle/typeck/infer/higher_ranked/doc.rs @@ -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 or the MIT license +// , 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 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. + + */ diff --git a/src/librustc/middle/typeck/infer/higher_ranked/mod.rs b/src/librustc/middle/typeck/infer/higher_ranked/mod.rs new file mode 100644 index 0000000000000..09f0bbb2254ad --- /dev/null +++ b/src/librustc/middle/typeck/infer/higher_ranked/mod.rs @@ -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 or the MIT license +// , 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; +} + +pub trait HigherRankedRelations { + fn higher_ranked_sub(&self, a: &T, b: &T) -> cres + where T : HigherRankedCombineable; + + fn higher_ranked_lub(&self, a: &T, b: &T) -> cres + where T : HigherRankedCombineable; + + fn higher_ranked_glb(&self, a: &T, b: &T) -> cres + where T : HigherRankedCombineable; +} + +impl<'tcx,C> HigherRankedRelations for C + where C : Combine<'tcx> +{ + fn higher_ranked_sub(&self, a: &T, b: &T) -> cres + 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(&self, a: &T, b: &T) -> cres + 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, + 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(&self, a: &T, b: &T) -> cres + 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, + 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(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, + 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 + { + 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> + { + 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 + { + // 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) + -> Vec { + 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(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)) + })) +} + diff --git a/src/librustc/middle/typeck/infer/lattice.rs b/src/librustc/middle/typeck/infer/lattice.rs index 5dbcaadf0df67..f7e6cef99af93 100644 --- a/src/librustc/middle/typeck/infer/lattice.rs +++ b/src/librustc/middle/typeck/infer/lattice.rs @@ -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) - -> Vec { - 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 - } -} diff --git a/src/librustc/middle/typeck/infer/lub.rs b/src/librustc/middle/typeck/infer/lub.rs index aaad3d573be55..8856f42d1f5ab 100644 --- a/src/librustc/middle/typeck/infer/lub.rs +++ b/src/librustc/middle/typeck/infer/lub.rs @@ -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 { - 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 { 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 { - // 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, - 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 { super_lattice_tys(self, a, b) } + + fn trait_refs(&self, a: &ty::TraitRef, b: &ty::TraitRef) -> cres { + self.higher_ranked_lub(a, b) + } } diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index a75e3a78e4b25..e69bd2157662e 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -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) -> 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( &self, - binder_id: ast::NodeId, span: Span, lbrct: LateBoundRegionConversionTime, value: &T) -> (T, FnvHashMap) - where T : TypeFoldable + Repr + where T : HigherRankedFoldable { - let (map, value) = - replace_late_bound_regions( - self.tcx, - binder_id, - value, - |br| self.next_region_var(LateBoundRegion(span, br, lbrct))); - (value, map) + ty::replace_late_bound_regions( + self.tcx, + value, + |br, _| self.next_region_var(LateBoundRegion(span, br, lbrct))) } } -pub fn fold_regions_in_sig(tcx: &ty::ctxt, - fn_sig: &ty::FnSig, - fldr: |r: ty::Region| -> ty::Region) - -> ty::FnSig { - ty_fold::RegionFolder::regions(tcx, fldr).fold_sig(fn_sig) -} - impl TypeTrace { pub fn span(&self) -> Span { self.origin.span() @@ -1073,7 +1062,7 @@ impl SubregionOrigin { IndexSlice(a) => a, RelateObjectBound(a) => a, RelateProcBound(a, _, _) => a, - RelateParamBound(a, _, _) => a, + RelateParamBound(a, _) => a, RelateRegionParamBound(a) => a, RelateDefaultParamBound(a, _) => a, Reborrow(a) => a, @@ -1123,11 +1112,10 @@ impl Repr for SubregionOrigin { b, c.repr(tcx)) } - RelateParamBound(a, b, c) => { - format!("RelateParamBound({},{},{})", + RelateParamBound(a, b) => { + format!("RelateParamBound({},{})", a.repr(tcx), - b.repr(tcx), - c.repr(tcx)) + b.repr(tcx)) } RelateRegionParamBound(a) => { format!("RelateRegionParamBound({})", diff --git a/src/librustc/middle/typeck/infer/region_inference/doc.rs b/src/librustc/middle/typeck/infer/region_inference/doc.rs index a64f7896cfbfd..40b41deeb2b66 100644 --- a/src/librustc/middle/typeck/infer/region_inference/doc.rs +++ b/src/librustc/middle/typeck/infer/region_inference/doc.rs @@ -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 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 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 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 int, &'b int) <: |&'a int, &'a int|? (Yes) - fn(&'a int, &'a int) <: |&'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 T) <: |&'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 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() -> fn(&'a T) <: || -> 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 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 T)? - -Now we compare the return types, which are covariant, and hence we have: - - fn(&'A T) <: |&'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 T) <: |&'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) fn(&X) fn(&X) fn(&a) -fn(&A) fn(&X) -- fn(&a) -fn(&a, &b) fn(&x, &x) fn(&a, &a) fn(&a, &b) -fn(&a, &b, &a) fn(&x, &y, &y) fn(&a, &a, &a) fn(&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)`). - -### 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)`. -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`. */ diff --git a/src/librustc/middle/typeck/infer/region_inference/mod.rs b/src/librustc/middle/typeck/infer/region_inference/mod.rs index c65a930195c7f..54fb7872f3b03 100644 --- a/src/librustc/middle/typeck/infer/region_inference/mod.rs +++ b/src/librustc/middle/typeck/infer/region_inference/mod.rs @@ -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 { diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/typeck/infer/sub.rs index 2763132024b3c..f85cb85ff21a2 100644 --- a/src/librustc/middle/typeck/infer/sub.rs +++ b/src/librustc/middle/typeck/infer/sub.rs @@ -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 { 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 { 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 { 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 { - 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 { + self.higher_ranked_sub(a, b) } } diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs index 634b153a9ce34..24d11b25a60f2 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc/middle/typeck/mod.rs @@ -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())) diff --git a/src/librustc/middle/typeck/rscope.rs b/src/librustc/middle/typeck/rscope.rs index 2845e3954b5c6..2f72d3cf50db1 100644 --- a/src/librustc/middle/typeck/rscope.rs +++ b/src/librustc/middle/typeck/rscope.rs @@ -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, } 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 + { + self.base_scope.default_region_bound(span) + .map(|r| ty_fold::shift_region(r, 1)) + } + + fn anon_regions(&self, + span: Span, + count: uint) + -> Result, Option>> + { + 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) + } + } + } +} diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index e6015bfc074ec..4ce783b37b737 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -252,8 +252,7 @@ pub fn vec_map_to_string(ts: &[T], f: |t: &T| -> String) -> String { } pub fn fn_sig_to_string(cx: &ctxt, typ: &ty::FnSig) -> String { - format!("fn{}{} -> {}", typ.binder_id, typ.inputs.repr(cx), - typ.output.repr(cx)) + format!("fn{} -> {}", typ.inputs.repr(cx), typ.output.repr(cx)) } pub fn trait_ref_to_string(cx: &ctxt, trait_ref: &ty::TraitRef) -> String { @@ -262,11 +261,11 @@ pub fn trait_ref_to_string(cx: &ctxt, trait_ref: &ty::TraitRef) -> String { pub fn ty_to_string(cx: &ctxt, typ: t) -> String { fn bare_fn_to_string(cx: &ctxt, - fn_style: ast::FnStyle, - abi: abi::Abi, - ident: Option, - sig: &ty::FnSig) - -> String { + fn_style: ast::FnStyle, + abi: abi::Abi, + ident: Option, + sig: &ty::FnSig) + -> String { let mut s = String::new(); match fn_style { ast::NormalFn => {} @@ -732,6 +731,9 @@ impl Repr for ty::ParamBounds { impl Repr for ty::TraitRef { fn repr(&self, tcx: &ctxt) -> String { + // when printing out the debug representation, we don't need + // to enumerate the `for<...>` etc because the debruijn index + // tells you everything you need to know. let base = ty::item_path_str(tcx, self.def_id); let trait_def = ty::lookup_trait_def(tcx, self.def_id); format!("<{} : {}>", @@ -922,6 +924,14 @@ impl Repr for ty::Generics { } } +impl Repr for ty::GenericBounds { + fn repr(&self, tcx: &ctxt) -> String { + format!("GenericBounds(types: {}, regions: {})", + self.types.repr(tcx), + self.regions.repr(tcx)) + } +} + impl Repr for ty::ItemVariances { fn repr(&self, tcx: &ctxt) -> String { format!("ItemVariances(types={}, \ @@ -1140,9 +1150,41 @@ impl UserString for ty::BuiltinBounds { impl UserString for ty::TraitRef { fn user_string(&self, tcx: &ctxt) -> String { - let base = ty::item_path_str(tcx, self.def_id); + // Replace any anonymous late-bound regions with named + // variants, using gensym'd identifiers, so that we can + // clearly differentiate between named and unnamed regions in + // the output. We'll probably want to tweak this over time to + // decide just how much information to give. + let mut names = Vec::new(); + let (trait_ref, _) = ty::replace_late_bound_regions(tcx, self, |br, debruijn| { + ty::ReLateBound(debruijn, match br { + ty::BrNamed(_, name) => { + names.push(token::get_name(name)); + br + } + ty::BrAnon(_) | + ty::BrFresh(_) | + ty::BrEnv => { + let name = token::gensym("r"); + names.push(token::get_name(name)); + ty::BrNamed(ast_util::local_def(ast::DUMMY_NODE_ID), name) + } + }) + }); + let names: Vec<_> = names.iter().map(|s| s.get()).collect(); + + // Let the base string be either `SomeTrait` for `for<'a,'b> SomeTrait`, + // depending on whether there are bound regions. + let path_str = ty::item_path_str(tcx, self.def_id); + let base = + if names.is_empty() { + path_str + } else { + format!("for<{}> {}", names.connect(","), path_str) + }; + let trait_def = ty::lookup_trait_def(tcx, self.def_id); - parameterized(tcx, base.as_slice(), &self.substs, &trait_def.generics) + parameterized(tcx, base.as_slice(), &trait_ref.substs, &trait_def.generics) } } @@ -1301,3 +1343,8 @@ impl Repr for (A,B) { } } +impl Repr for ty::Binder { + fn repr(&self, tcx: &ctxt) -> String { + format!("Binder({})", self.value.repr(tcx)) + } +} diff --git a/src/librustc_trans/driver/driver.rs b/src/librustc_trans/driver/driver.rs index ca70a37bf3d21..a0e2bf07b830f 100644 --- a/src/librustc_trans/driver/driver.rs +++ b/src/librustc_trans/driver/driver.rs @@ -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)); diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 38bc8c99c0fe1..c9410d753ae6a 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -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; } diff --git a/src/librustc_trans/test.rs b/src/librustc_trans/test.rs index 4648021cc5fd7..a17702ac1dcaa 100644 --- a/src/librustc_trans/test.rs +++ b/src/librustc_trans/test.rs @@ -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, uint) { (box ExpectErrorEmitter { messages: v } as Box, msgs.len()) } -fn test_env(_test_name: &str, - source_string: &str, +fn test_env(source_string: &str, (emitter, expected_err_count): (Box, 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_nil(&self) -> ty::t { + ty::mk_nil(self.infcx.tcx) + } + + pub fn t_pair(&self, ty1: ty::t, ty2: ty::t) -> ty::t + { + ty::mk_tup(self.infcx.tcx, vec![ty1, ty2]) + } + + pub fn t_closure(&self, + input_tys: &[ty::t], + output_ty: ty::t, + region_bound: ty::Region) + -> ty::t + { + ty::mk_closure(self.infcx.tcx, ty::ClosureTy { + fn_style: ast::NormalFn, + onceness: ast::Many, + store: ty::RegionTraitStore(region_bound, ast::MutMutable), + bounds: ty::region_existential_bound(region_bound), + sig: ty::FnSig { + inputs: input_tys.to_vec(), + output: ty::FnConverging(output_ty), + variadic: false, + }, + abi: abi::Rust, + }) + } + + pub fn t_param(&self, space: subst::ParamSpace, index: uint) -> ty::t { + ty::mk_param(self.infcx.tcx, space, index, ast_util::local_def(ast::DUMMY_NODE_ID)) + } + + pub fn re_early_bound(&self, + space: subst::ParamSpace, + index: uint, + name: &'static str) + -> ty::Region + { + let name = token::intern(name); + ty::ReEarlyBound(ast::DUMMY_NODE_ID, space, index, name) + } + + pub fn re_late_bound_with_debruijn(&self, id: uint, debruijn: ty::DebruijnIndex) -> ty::Region { + ty::ReLateBound(debruijn, ty::BrAnon(id)) + } + + pub fn t_rptr(&self, r: ty::Region) -> ty::t { + ty::mk_imm_rptr(self.infcx.tcx, r, ty::mk_int()) } - pub fn t_int(&self) -> ty::t { - ty::mk_int() + pub fn t_rptr_late_bound(&self, id: uint) -> ty::t { + ty::mk_imm_rptr(self.infcx.tcx, + self.re_late_bound_with_debruijn(id, ty::DebruijnIndex::new(1)), + ty::mk_int()) } - pub fn t_rptr_late_bound(&self, binder_id: ast::NodeId, id: uint) -> ty::t { - ty::mk_imm_rptr(self.infcx.tcx, ty::ReLateBound(binder_id, ty::BrAnon(id)), - self.t_int()) + pub fn t_rptr_late_bound_with_debruijn(&self, id: uint, debruijn: ty::DebruijnIndex) -> ty::t { + ty::mk_imm_rptr(self.infcx.tcx, + self.re_late_bound_with_debruijn(id, debruijn), + ty::mk_int()) } pub fn t_rptr_scope(&self, id: ast::NodeId) -> ty::t { - ty::mk_imm_rptr(self.infcx.tcx, ty::ReScope(id), self.t_int()) + ty::mk_imm_rptr(self.infcx.tcx, ty::ReScope(id), ty::mk_int()) + } + + pub fn re_free(&self, nid: ast::NodeId, id: uint) -> ty::Region { + ty::ReFree(ty::FreeRegion {scope_id: nid, + bound_region: ty::BrAnon(id)}) } pub fn t_rptr_free(&self, nid: ast::NodeId, id: uint) -> ty::t { - ty::mk_imm_rptr(self.infcx.tcx, - ty::ReFree(ty::FreeRegion {scope_id: nid, - bound_region: ty::BrAnon(id)}), - self.t_int()) + ty::mk_imm_rptr(self.infcx.tcx, self.re_free(nid, id), ty::mk_int()) } pub fn t_rptr_static(&self) -> ty::t { - ty::mk_imm_rptr(self.infcx.tcx, ty::ReStatic, self.t_int()) + ty::mk_imm_rptr(self.infcx.tcx, ty::ReStatic, ty::mk_int()) } pub fn dummy_type_trace(&self) -> infer::TypeTrace { @@ -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); + }) +} + diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 39474a99f6d1e..9c4a532790dde 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -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(), diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index 0544c9d43a811..de49754fe7f9d 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -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) diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs index 139087507ff96..3cb823aec34c1 100644 --- a/src/librustc_trans/trans/closure.rs +++ b/src/librustc_trans/trans/closure.rs @@ -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 diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index d06cfa4a02717..21cf313866144 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -191,10 +191,21 @@ pub type ExternMap = FnvHashMap; // 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); diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs index e3e36ee53fd4a..65fd95667608a 100644 --- a/src/librustc_trans/trans/debuginfo.rs +++ b/src/librustc_trans/trans/debuginfo.rs @@ -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 diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 2b7c3d0c24ff9..40a4d6047aa53 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -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) diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs index 3ca37f9e35565..f86a0994bf95c 100644 --- a/src/librustc_trans/trans/foreign.rs +++ b/src/librustc_trans/trans/foreign.rs @@ -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)); diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs index cceea96d4c1a8..5188ca773503a 100644 --- a/src/librustc_trans/trans/glue.rs +++ b/src/librustc_trans/trans/glue.rs @@ -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); diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 55ecd8a6554a6..90777afff7e72 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -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, diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs index 077c1337a44d6..52aa81fa427da 100644 --- a/src/librustc_trans/trans/monomorphize.rs +++ b/src/librustc_trans/trans/monomorphize.rs @@ -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={}, \ diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 0cb80d4c15397..15e14902727f5 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1154,7 +1154,7 @@ pub enum Ty_ { /// Type parameters are stored in the Path itself TyPath(Path, Option, NodeId), // for #7264; see above /// A type like `for<'a> Foo<&'a Bar>` - TyPolyTraitRef(P), + TyPolyTraitRef(TyParamBounds), /// A "qualified path", e.g. ` as SomeTrait>::SomeType` TyQPath(P), /// No-op; kept solely so that we can pretty-print faithfully diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 2e3a15bfd4b4b..30cdecbc85199 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -494,10 +494,10 @@ impl<'a, 'v, O: IdVisitingOperation> Visitor<'v> for IdVisitor<'a, O> { } visit::walk_fn(self, - function_kind, - function_declaration, - block, - span); + function_kind, + function_declaration, + block, + span); if !self.pass_through_items { match function_kind { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 0c31e9ae01d75..ebdcf27893402 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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) { @@ -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"), diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 56d912824374d..b3137ff5f7e52 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -445,10 +445,12 @@ pub fn noop_fold_ty(t: P, fld: &mut T) -> P { 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) }) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 98479d65cbb2f..40c4ac9f8c044 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -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) } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 0543b80f208b1..e6e0c33a42dbd 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -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, "<")); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 2960c28a8b71d..efe1e18eda928 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -78,6 +78,9 @@ pub trait Visitor<'v> { fn visit_ty_method(&mut self, t: &'v TypeMethod) { walk_ty_method(self, t) } fn visit_trait_item(&mut self, t: &'v TraitItem) { walk_trait_item(self, t) } fn visit_trait_ref(&mut self, t: &'v TraitRef) { walk_trait_ref(self, t) } + fn visit_ty_param_bound(&mut self, bounds: &'v TyParamBound) { + walk_ty_param_bound(self, bounds) + } fn visit_poly_trait_ref(&mut self, t: &'v PolyTraitRef) { walk_poly_trait_ref(self, t) } @@ -119,6 +122,12 @@ pub trait Visitor<'v> { fn visit_path(&mut self, path: &'v Path, _id: ast::NodeId) { walk_path(self, path) } + fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v PathSegment) { + walk_path_segment(self, path_span, path_segment) + } + fn visit_path_parameters(&mut self, path_span: Span, path_parameters: &'v PathParameters) { + walk_path_parameters(self, path_span, path_parameters) + } fn visit_attribute(&mut self, _attr: &'v Attribute) {} } @@ -170,7 +179,7 @@ pub fn walk_view_item<'v, V: Visitor<'v>>(visitor: &mut V, vi: &'v ViewItem) { ViewPathGlob(ref path, id) => { visitor.visit_path(path, id); } - ViewPathList(ref path, ref list, _) => { + ViewPathList(ref prefix, ref list, _) => { for id in list.iter() { match id.node { PathListIdent { name, .. } => { @@ -179,7 +188,10 @@ pub fn walk_view_item<'v, V: Visitor<'v>>(visitor: &mut V, vi: &'v ViewItem) { PathListMod { .. } => () } } - walk_path(visitor, path); + + // Note that the `prefix` here is not a complete + // path, so we don't use `visit_path`. + walk_path(visitor, prefix); } } } @@ -212,7 +224,7 @@ pub fn walk_poly_trait_ref<'v, V>(visitor: &mut V, trait_ref: &'v PolyTraitRef) where V: Visitor<'v> { - walk_lifetime_decls(visitor, &trait_ref.bound_lifetimes); + walk_lifetime_decls_helper(visitor, &trait_ref.bound_lifetimes); visitor.visit_trait_ref(&trait_ref.trait_ref); } @@ -290,7 +302,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) { } ItemTrait(ref generics, _, ref bounds, ref methods) => { visitor.visit_generics(generics); - walk_ty_param_bounds(visitor, bounds); + walk_ty_param_bounds_helper(visitor, bounds); for method in methods.iter() { visitor.visit_trait_item(method) } @@ -363,29 +375,29 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) { visitor.visit_ty(&*argument.ty) } walk_fn_ret_ty(visitor, &function_declaration.decl.output); - walk_ty_param_bounds(visitor, &function_declaration.bounds); - walk_lifetime_decls(visitor, &function_declaration.lifetimes); + walk_ty_param_bounds_helper(visitor, &function_declaration.bounds); + walk_lifetime_decls_helper(visitor, &function_declaration.lifetimes); } TyProc(ref function_declaration) => { for argument in function_declaration.decl.inputs.iter() { visitor.visit_ty(&*argument.ty) } walk_fn_ret_ty(visitor, &function_declaration.decl.output); - walk_ty_param_bounds(visitor, &function_declaration.bounds); - walk_lifetime_decls(visitor, &function_declaration.lifetimes); + walk_ty_param_bounds_helper(visitor, &function_declaration.bounds); + walk_lifetime_decls_helper(visitor, &function_declaration.lifetimes); } TyBareFn(ref function_declaration) => { for argument in function_declaration.decl.inputs.iter() { visitor.visit_ty(&*argument.ty) } walk_fn_ret_ty(visitor, &function_declaration.decl.output); - walk_lifetime_decls(visitor, &function_declaration.lifetimes); + walk_lifetime_decls_helper(visitor, &function_declaration.lifetimes); } TyPath(ref path, ref opt_bounds, id) => { visitor.visit_path(path, id); match *opt_bounds { Some(ref bounds) => { - walk_ty_param_bounds(visitor, bounds); + walk_ty_param_bounds_helper(visitor, bounds); } None => { } } @@ -399,8 +411,8 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) { visitor.visit_ty(&**ty); visitor.visit_expr(&**expression) } - TyPolyTraitRef(ref poly_trait_ref) => { - visitor.visit_poly_trait_ref(&**poly_trait_ref) + TyPolyTraitRef(ref bounds) => { + walk_ty_param_bounds_helper(visitor, bounds) } TyTypeof(ref expression) => { visitor.visit_expr(&**expression) @@ -409,8 +421,8 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) { } } -fn walk_lifetime_decls<'v, V: Visitor<'v>>(visitor: &mut V, - lifetimes: &'v Vec) { +pub fn walk_lifetime_decls_helper<'v, V: Visitor<'v>>(visitor: &mut V, + lifetimes: &'v Vec) { for l in lifetimes.iter() { visitor.visit_lifetime_decl(l); } @@ -418,24 +430,35 @@ fn walk_lifetime_decls<'v, V: Visitor<'v>>(visitor: &mut V, pub fn walk_path<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path) { for segment in path.segments.iter() { - visitor.visit_ident(path.span, segment.identifier); + visitor.visit_path_segment(path.span, segment); + } +} - match segment.parameters { - ast::AngleBracketedParameters(ref data) => { - for typ in data.types.iter() { - visitor.visit_ty(&**typ); - } - for lifetime in data.lifetimes.iter() { - visitor.visit_lifetime_ref(lifetime); - } +pub fn walk_path_segment<'v, V: Visitor<'v>>(visitor: &mut V, + path_span: Span, + segment: &'v PathSegment) { + visitor.visit_ident(path_span, segment.identifier); + visitor.visit_path_parameters(path_span, &segment.parameters); +} + +pub fn walk_path_parameters<'v, V: Visitor<'v>>(visitor: &mut V, + _path_span: Span, + path_parameters: &'v PathParameters) { + match *path_parameters { + ast::AngleBracketedParameters(ref data) => { + for typ in data.types.iter() { + visitor.visit_ty(&**typ); } - ast::ParenthesizedParameters(ref data) => { - for typ in data.inputs.iter() { - visitor.visit_ty(&**typ); - } - for typ in data.output.iter() { - visitor.visit_ty(&**typ); - } + for lifetime in data.lifetimes.iter() { + visitor.visit_lifetime_ref(lifetime); + } + } + ast::ParenthesizedParameters(ref data) => { + for typ in data.inputs.iter() { + visitor.visit_ty(&**typ); + } + for typ in data.output.iter() { + visitor.visit_ty(&**typ); } } } @@ -511,32 +534,37 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V, } } -pub fn walk_ty_param_bounds<'v, V: Visitor<'v>>(visitor: &mut V, - bounds: &'v OwnedSlice) { +pub fn walk_ty_param_bounds_helper<'v, V: Visitor<'v>>(visitor: &mut V, + bounds: &'v OwnedSlice) { for bound in bounds.iter() { - match *bound { - TraitTyParamBound(ref typ) => { - visitor.visit_poly_trait_ref(typ) - } - RegionTyParamBound(ref lifetime) => { - visitor.visit_lifetime_ref(lifetime); - } + visitor.visit_ty_param_bound(bound) + } +} + +pub fn walk_ty_param_bound<'v, V: Visitor<'v>>(visitor: &mut V, + bound: &'v TyParamBound) { + match *bound { + TraitTyParamBound(ref typ) => { + visitor.visit_poly_trait_ref(typ); + } + RegionTyParamBound(ref lifetime) => { + visitor.visit_lifetime_ref(lifetime); } } } pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics) { for type_parameter in generics.ty_params.iter() { - walk_ty_param_bounds(visitor, &type_parameter.bounds); + walk_ty_param_bounds_helper(visitor, &type_parameter.bounds); match type_parameter.default { Some(ref ty) => visitor.visit_ty(&**ty), None => {} } } - walk_lifetime_decls(visitor, &generics.lifetimes); + walk_lifetime_decls_helper(visitor, &generics.lifetimes); for predicate in generics.where_clause.predicates.iter() { visitor.visit_ident(predicate.span, predicate.ident); - walk_ty_param_bounds(visitor, &predicate.bounds); + walk_ty_param_bounds_helper(visitor, &predicate.bounds); } } diff --git a/src/test/auxiliary/issue-18711.rs b/src/test/auxiliary/issue-18711.rs index 454e67b8bd5cc..54f1595780db7 100644 --- a/src/test/auxiliary/issue-18711.rs +++ b/src/test/auxiliary/issue-18711.rs @@ -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 { diff --git a/src/test/auxiliary/unboxed-closures-cross-crate.rs b/src/test/auxiliary/unboxed-closures-cross-crate.rs index d04829bb808c5..9a6a2c7495b79 100644 --- a/src/test/auxiliary/unboxed-closures-cross-crate.rs +++ b/src/test/auxiliary/unboxed-closures-cross-crate.rs @@ -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 { diff --git a/src/test/bench/shootout-reverse-complement.rs b/src/test/bench/shootout-reverse-complement.rs index d7d8e94c8a7db..ffe5739e0bb5e 100644 --- a/src/test/bench/shootout-reverse-complement.rs +++ b/src/test/bench/shootout-reverse-complement.rs @@ -40,7 +40,7 @@ // ignore-android see #10393 #13206 -#![feature(slicing_syntax, unboxed_closures, overloaded_calls)] +#![feature(slicing_syntax, unboxed_closures)] extern crate libc; diff --git a/src/test/bench/shootout-spectralnorm.rs b/src/test/bench/shootout-spectralnorm.rs index c5f2fbb189017..acb289aa3ade8 100644 --- a/src/test/bench/shootout-spectralnorm.rs +++ b/src/test/bench/shootout-spectralnorm.rs @@ -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; diff --git a/src/test/compile-fail/borrowck-overloaded-call.rs b/src/test/compile-fail/borrowck-overloaded-call.rs index f134c2aa09bd5..938fc53d61054 100644 --- a/src/test/compile-fail/borrowck-overloaded-call.rs +++ b/src/test/compile-fail/borrowck-overloaded-call.rs @@ -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}; diff --git a/src/test/compile-fail/borrowck-unboxed-closures.rs b/src/test/compile-fail/borrowck-unboxed-closures.rs index 5f9dd72f5bf14..cca3dcb8b34db 100644 --- a/src/test/compile-fail/borrowck-unboxed-closures.rs +++ b/src/test/compile-fail/borrowck-unboxed-closures.rs @@ -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 int>(mut f: F) { let g = &mut f; diff --git a/src/test/compile-fail/explicit-self-lifetime-mismatch.rs b/src/test/compile-fail/explicit-self-lifetime-mismatch.rs index d1ae535d83097..9b2264b8902a3 100644 --- a/src/test/compile-fail/explicit-self-lifetime-mismatch.rs +++ b/src/test/compile-fail/explicit-self-lifetime-mismatch.rs @@ -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() {} diff --git a/src/test/compile-fail/hrtb-higher-ranker-supertraits-transitive.rs b/src/test/compile-fail/hrtb-higher-ranker-supertraits-transitive.rs new file mode 100644 index 0000000000000..4199deee7b80e --- /dev/null +++ b/src/test/compile-fail/hrtb-higher-ranker-supertraits-transitive.rs @@ -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 or the MIT license +// , 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) + where F : for<'tcx> Foo<'tcx> +{ +} + +fn want_bar_for_any_ccx(b: &B) + where B : for<'ccx> Bar<'ccx> +{ +} + +fn want_baz(b: &B) + where B : Baz +{ + want_foo_for_any_tcx(b); + want_bar_for_any_ccx(b); +} + +fn want_qux(b: &B) + where B : Qux +{ + want_foo_for_any_tcx(b); + want_bar_for_any_ccx(b); //~ ERROR not implemented +} + +fn main() {} diff --git a/src/test/compile-fail/hrtb-higher-ranker-supertraits.rs b/src/test/compile-fail/hrtb-higher-ranker-supertraits.rs new file mode 100644 index 0000000000000..108ca1b82e0df --- /dev/null +++ b/src/test/compile-fail/hrtb-higher-ranker-supertraits.rs @@ -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 or the MIT license +// , 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) + 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) + 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() {} diff --git a/src/test/compile-fail/hrtb-identity-fn-borrows.rs b/src/test/compile-fail/hrtb-identity-fn-borrows.rs new file mode 100644 index 0000000000000..733a5b2a85af1 --- /dev/null +++ b/src/test/compile-fail/hrtb-identity-fn-borrows.rs @@ -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 or the MIT license +// , 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 { + fn call(&self, arg: A) -> R; +} + +fn call_repeatedly(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() { +} diff --git a/src/test/compile-fail/issue-15094.rs b/src/test/compile-fail/issue-15094.rs index c9e47b74d515d..dd3c88b8a10b4 100644 --- a/src/test/compile-fail/issue-15094.rs +++ b/src/test/compile-fail/issue-15094.rs @@ -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}; diff --git a/src/test/compile-fail/issue-16939.rs b/src/test/compile-fail/issue-16939.rs index e7d3d15e5a938..7ec3fef5c878e 100644 --- a/src/test/compile-fail/issue-16939.rs +++ b/src/test/compile-fail/issue-16939.rs @@ -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. diff --git a/src/test/compile-fail/issue-18532.rs b/src/test/compile-fail/issue-18532.rs index a67f4c851bf8a..943d326182f89 100644 --- a/src/test/compile-fail/issue-18532.rs +++ b/src/test/compile-fail/issue-18532.rs @@ -12,7 +12,7 @@ // when a type error or unconstrained type variable propagates // into it. -#![feature(overloaded_calls)] +#![feature(unboxed_closures)] fn main() { (return)((),()); diff --git a/src/test/compile-fail/overloaded-calls-bad.rs b/src/test/compile-fail/overloaded-calls-bad.rs index cc01c0b3c9f1c..b3a4c3125891f 100644 --- a/src/test/compile-fail/overloaded-calls-bad.rs +++ b/src/test/compile-fail/overloaded-calls-bad.rs @@ -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; diff --git a/src/test/compile-fail/overloaded-calls-nontuple.rs b/src/test/compile-fail/overloaded-calls-nontuple.rs index ee2fe3522470a..396a809c2e12f 100644 --- a/src/test/compile-fail/overloaded-calls-nontuple.rs +++ b/src/test/compile-fail/overloaded-calls-nontuple.rs @@ -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; diff --git a/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs b/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs index e046b5c68addd..75e9e55138e06 100644 --- a/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs +++ b/src/test/compile-fail/regionck-unboxed-closure-lifetimes.rs @@ -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; diff --git a/src/test/compile-fail/regions-name-undeclared.rs b/src/test/compile-fail/regions-name-undeclared.rs index e9e585e84d0ef..ffd1501075e94 100644 --- a/src/test/compile-fail/regions-name-undeclared.rs +++ b/src/test/compile-fail/regions-name-undeclared.rs @@ -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 diff --git a/src/test/compile-fail/stage0-clone-contravariant-lifetime.rs b/src/test/compile-fail/stage0-clone-contravariant-lifetime.rs new file mode 100644 index 0000000000000..1d1b244ab5aeb --- /dev/null +++ b/src/test/compile-fail/stage0-clone-contravariant-lifetime.rs @@ -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 or the MIT license +// , 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() { } diff --git a/src/test/compile-fail/stage0-cmp.rs b/src/test/compile-fail/stage0-cmp.rs new file mode 100644 index 0000000000000..2c0772b1414bd --- /dev/null +++ b/src/test/compile-fail/stage0-cmp.rs @@ -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 or the MIT license +// , 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() { } diff --git a/src/test/compile-fail/ufcs-explicit-self-bad.rs b/src/test/compile-fail/ufcs-explicit-self-bad.rs index b639af617574c..8d3610affdfb9 100644 --- a/src/test/compile-fail/ufcs-explicit-self-bad.rs +++ b/src/test/compile-fail/ufcs-explicit-self-bad.rs @@ -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 { impl Bar { fn foo(self: Bar, x: int) -> int { //~ ERROR mismatched self type -//~^ ERROR not a valid type for `self` x } fn bar(self: &Bar, 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 { + fn dummy1(self: &&'a Bar) { } + fn dummy2(self: &Bar) {} //~ ERROR mismatched self type + fn dummy3(self: &&Bar) {} //~ ERROR lifetime mismatch + //~^ ERROR lifetime mismatch +} + fn main() { let foo = box Foo { f: 1, diff --git a/src/test/compile-fail/unboxed-closure-sugar-default.rs b/src/test/compile-fail/unboxed-closure-sugar-default.rs index 9866a20004527..06a934063927a 100644 --- a/src/test/compile-fail/unboxed-closure-sugar-default.rs +++ b/src/test/compile-fail/unboxed-closure-sugar-default.rs @@ -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: T, u: U +trait Foo { + fn dummy(&self, t: T, u: U, v: V); } -trait Eq { } -impl Eq for X { } -fn eq>() { } +trait Eq for Sized? { } +impl Eq for X { } +fn eq() where A : Eq { } fn test<'a,'b>() { // Parens are equivalent to omitting default in angle. diff --git a/src/test/compile-fail/unboxed-closure-sugar-equiv.rs b/src/test/compile-fail/unboxed-closure-sugar-equiv.rs index c38010c1ee260..6f875efdef7ea 100644 --- a/src/test/compile-fail/unboxed-closure-sugar-equiv.rs +++ b/src/test/compile-fail/unboxed-closure-sugar-equiv.rs @@ -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: T, u: U +trait Foo { + fn dummy(&self, t: T, u: U); } -trait Eq { } -impl Eq for X { } -fn eq>() { } +trait Eq for Sized? { } +impl Eq for X { } +fn eq>() { } 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 diff --git a/src/test/compile-fail/unboxed-closure-sugar-nonexistent-trait.rs b/src/test/compile-fail/unboxed-closure-sugar-nonexistent-trait.rs index d89c3802508c5..23e2d2f4365af 100644 --- a/src/test/compile-fail/unboxed-closure-sugar-nonexistent-trait.rs +++ b/src/test/compile-fail/unboxed-closure-sugar-nonexistent-trait.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(unboxed_closures)] + fn f int>(x: F) {} //~ ERROR nonexistent trait `Nonexist` type Typedef = int; diff --git a/src/test/compile-fail/unboxed-closure-sugar-region.rs b/src/test/compile-fail/unboxed-closure-sugar-region.rs index 962e233dea696..a938f126c1607 100644 --- a/src/test/compile-fail/unboxed-closure-sugar-region.rs +++ b/src/test/compile-fail/unboxed-closure-sugar-region.rs @@ -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 { } -impl Eq for X { } -fn eq>() { } +trait Eq for Sized? { } +impl Eq for X { } +fn eq>() { } + fn same_type>(a: A, b: B) { } fn test<'a,'b>() { @@ -34,10 +33,10 @@ fn test<'a,'b>() { // Here we specify 'static explicitly in angle-bracket version. // Parenthesized winds up getting inferred. - eq::< Foo<'static, (int,),()>, Foo(int) >(); + eq::< Foo<'static, (int,),()>, Foo(int) >(); } -fn test2(x: Foo<(int,),()>, y: Foo(int)) { +fn test2(x: &Foo<(int,),()>, y: &Foo(int)) { // Here, the omitted lifetimes are expanded to distinct things. same_type(x, y) //~ ERROR cannot infer } diff --git a/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-1.rs b/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-1.rs index e122b87b1e0f0..d9efab974d83f 100644 --- a/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-1.rs +++ b/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-1.rs @@ -8,9 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct One; +#![feature(unboxed_closures)] -fn foo(_: One()) //~ ERROR wrong number of type arguments +trait One { fn foo(&self) -> A; } + +fn foo(_: &One()) //~ ERROR wrong number of type arguments {} fn main() { } diff --git a/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs b/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs index 7a66abb39df58..dcfcb7d47728c 100644 --- a/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs +++ b/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs @@ -8,9 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct Three; +#![feature(unboxed_closures)] -fn foo(_: Three()) //~ ERROR wrong number of type arguments +trait Three { fn dummy(&self) -> (A,B,C); } + +fn foo(_: &Three()) //~ ERROR wrong number of type arguments {} fn main() { } diff --git a/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters.rs b/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters.rs index e265a3d56b871..a8ac62444aafc 100644 --- a/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters.rs +++ b/src/test/compile-fail/unboxed-closure-sugar-wrong-number-number-type-parameters.rs @@ -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 {} diff --git a/src/test/compile-fail/unboxed-closure-sugar-wrong-trait.rs b/src/test/compile-fail/unboxed-closure-sugar-wrong-trait.rs index 1394f8fa65fcc..ba1e931ac6434 100644 --- a/src/test/compile-fail/unboxed-closure-sugar-wrong-trait.rs +++ b/src/test/compile-fail/unboxed-closure-sugar-wrong-trait.rs @@ -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 int>(x: F) {} diff --git a/src/test/compile-fail/unboxed-closures-fnmut-as-fn.rs b/src/test/compile-fail/unboxed-closures-fnmut-as-fn.rs index 20d7262432f0c..9fbb8a18ae93a 100644 --- a/src/test/compile-fail/unboxed-closures-fnmut-as-fn.rs +++ b/src/test/compile-fail/unboxed-closures-fnmut-as-fn.rs @@ -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}; diff --git a/src/test/run-pass/bare-fn-implements-fn-mut.rs b/src/test/run-pass/bare-fn-implements-fn-mut.rs index 4962e9d0f2259..9d104afd6464e 100644 --- a/src/test/run-pass/bare-fn-implements-fn-mut.rs +++ b/src/test/run-pass/bare-fn-implements-fn-mut.rs @@ -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; diff --git a/src/test/run-pass/capture-clauses-unboxed-closures.rs b/src/test/run-pass/capture-clauses-unboxed-closures.rs index 9f333fd043f07..cd40e2a784366 100644 --- a/src/test/run-pass/capture-clauses-unboxed-closures.rs +++ b/src/test/run-pass/capture-clauses-unboxed-closures.rs @@ -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() { diff --git a/src/test/run-pass/closure-syntax.rs b/src/test/run-pass/closure-syntax.rs index 9d98a7ac12f9b..6716c3468d0f8 100644 --- a/src/test/run-pass/closure-syntax.rs +++ b/src/test/run-pass/closure-syntax.rs @@ -9,7 +9,7 @@ // except according to those terms. #![allow(dead_code)] -#![feature(unboxed_closures, unboxed_closure_sugar)] +#![feature(unboxed_closures)] // compile-flags:-g diff --git a/src/test/run-pass/hrtb-binder-levels-in-object-types.rs b/src/test/run-pass/hrtb-binder-levels-in-object-types.rs new file mode 100644 index 0000000000000..5a793f7065a41 --- /dev/null +++ b/src/test/run-pass/hrtb-binder-levels-in-object-types.rs @@ -0,0 +1,34 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that 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() { } diff --git a/src/test/run-pass/hrtb-debruijn-object-types-in-closures.rs b/src/test/run-pass/hrtb-debruijn-object-types-in-closures.rs new file mode 100644 index 0000000000000..5bdfa3cafd747 --- /dev/null +++ b/src/test/run-pass/hrtb-debruijn-object-types-in-closures.rs @@ -0,0 +1,23 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +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() { } diff --git a/src/test/run-pass/hrtb-fn-like-trait-object.rs b/src/test/run-pass/hrtb-fn-like-trait-object.rs new file mode 100644 index 0000000000000..c8992afe36ac7 --- /dev/null +++ b/src/test/run-pass/hrtb-fn-like-trait-object.rs @@ -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 or the MIT license +// , 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 { + 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); +} diff --git a/src/test/run-pass/hrtb-fn-like-trait.rs b/src/test/run-pass/hrtb-fn-like-trait.rs new file mode 100644 index 0000000000000..4067b922cfd6b --- /dev/null +++ b/src/test/run-pass/hrtb-fn-like-trait.rs @@ -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 or the MIT license +// , 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 { + 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) + 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); +} diff --git a/src/test/run-pass/hrtb-parse.rs b/src/test/run-pass/hrtb-parse.rs index 080523f0060a7..02d3bc120da8c 100644 --- a/src/test/run-pass/hrtb-parse.rs +++ b/src/test/run-pass/hrtb-parse.rs @@ -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)] diff --git a/src/test/run-pass/hrtb-resolve-lifetime.rs b/src/test/run-pass/hrtb-resolve-lifetime.rs new file mode 100644 index 0000000000000..9b37b8e49ef3c --- /dev/null +++ b/src/test/run-pass/hrtb-resolve-lifetime.rs @@ -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 or the MIT license +// , 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 { + fn call(&self, arg: A) -> R; +} + +type FnObject<'b> = for<'a> FnLike<&'a int, &'a int> + 'b; + +fn main() { +} diff --git a/src/test/run-pass/hrtb-trait-object-paren-notation.rs b/src/test/run-pass/hrtb-trait-object-paren-notation.rs new file mode 100644 index 0000000000000..e17e0ae2189d3 --- /dev/null +++ b/src/test/run-pass/hrtb-trait-object-paren-notation.rs @@ -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 or the MIT license +// , 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 { + 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); +} diff --git a/src/test/run-pass/hrtb-trait-object-passed-to-closure.rs b/src/test/run-pass/hrtb-trait-object-passed-to-closure.rs new file mode 100644 index 0000000000000..076b9c7684ec4 --- /dev/null +++ b/src/test/run-pass/hrtb-trait-object-passed-to-closure.rs @@ -0,0 +1,31 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that `&PrinterSupport`, which is really short for `&'a +// PrinterSupport<'b>`, gets properly expanded when it appears in a +// closure type. This used to result in messed up De Bruijn indices. + +trait PrinterSupport<'ast> { + fn ast_map(&self) -> Option<&'ast uint> { None } +} + +struct NoAnn<'ast> { + f: Option<&'ast uint> +} + +impl<'ast> PrinterSupport<'ast> for NoAnn<'ast> { +} + +fn foo<'ast> (f: Option<&'ast uint>, g: |&PrinterSupport|) { + let annotation = NoAnn { f: f }; + g(&annotation) +} + +fn main() {} diff --git a/src/test/run-pass/hrtb-unboxed-closure-trait.rs b/src/test/run-pass/hrtb-unboxed-closure-trait.rs new file mode 100644 index 0000000000000..fea628177da41 --- /dev/null +++ b/src/test/run-pass/hrtb-unboxed-closure-trait.rs @@ -0,0 +1,22 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test HRTB used with the `Fn` trait. + +#![feature(unboxed_closures)] + +fn foo(f: F) { + let x = 22; + f(&x); +} + +fn main() { + foo(|&: x: &int| println!("{}", *x)); +} diff --git a/src/test/run-pass/issue-14958.rs b/src/test/run-pass/issue-14958.rs index c2bd8c5b3e50c..7f3321e0b3eed 100644 --- a/src/test/run-pass/issue-14958.rs +++ b/src/test/run-pass/issue-14958.rs @@ -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)] trait Foo {} diff --git a/src/test/run-pass/issue-14959.rs b/src/test/run-pass/issue-14959.rs index 5429c5195928e..6cc5ab4d6cbaa 100644 --- a/src/test/run-pass/issue-14959.rs +++ b/src/test/run-pass/issue-14959.rs @@ -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; diff --git a/src/test/run-pass/issue-16774.rs b/src/test/run-pass/issue-16774.rs index f996f9309c135..ebc879d82fbb1 100644 --- a/src/test/run-pass/issue-16774.rs +++ b/src/test/run-pass/issue-16774.rs @@ -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)] struct X(Box); diff --git a/src/test/run-pass/issue-18652.rs b/src/test/run-pass/issue-18652.rs index 5ca091000608b..ef2c15c748ca3 100644 --- a/src/test/run-pass/issue-18652.rs +++ b/src/test/run-pass/issue-18652.rs @@ -12,7 +12,7 @@ // once closure as an optimization by trans. This used to hit an // incorrect assert. -#![feature(unboxed_closures, overloaded_calls)] +#![feature(unboxed_closures)] fn main() { let x = 2u8; diff --git a/src/test/run-pass/issue-18685.rs b/src/test/run-pass/issue-18685.rs index 1f74e2f847497..be6dd583132ec 100644 --- a/src/test/run-pass/issue-18685.rs +++ b/src/test/run-pass/issue-18685.rs @@ -11,7 +11,7 @@ // Test that the self param space is not used in a conflicting // manner by unboxed closures within a default method on a trait -#![feature(unboxed_closures, overloaded_calls)] +#![feature(unboxed_closures)] trait Tr { fn foo(&self); diff --git a/src/test/run-pass/issue-18711.rs b/src/test/run-pass/issue-18711.rs index 2d16aa7f0c307..6a04e68af0cb3 100644 --- a/src/test/run-pass/issue-18711.rs +++ b/src/test/run-pass/issue-18711.rs @@ -11,7 +11,7 @@ // Test that we don't panic on a RefCell borrow conflict in certain // code paths involving unboxed closures. -#![feature(unboxed_closures, overloaded_calls)] +#![feature(unboxed_closures)] // aux-build:issue-18711.rs extern crate "issue-18711" as issue; diff --git a/src/test/run-pass/overloaded-calls-param-vtables.rs b/src/test/run-pass/overloaded-calls-param-vtables.rs index 6f870f0afd52c..d0dbee39ae095 100644 --- a/src/test/run-pass/overloaded-calls-param-vtables.rs +++ b/src/test/run-pass/overloaded-calls-param-vtables.rs @@ -10,7 +10,7 @@ // Tests that nested vtables work with overloaded calls. -#![feature(overloaded_calls)] +#![feature(unboxed_closures)] use std::ops::Fn; diff --git a/src/test/run-pass/overloaded-calls-simple.rs b/src/test/run-pass/overloaded-calls-simple.rs index 76c7e60116f80..b0a40f74ff973 100644 --- a/src/test/run-pass/overloaded-calls-simple.rs +++ b/src/test/run-pass/overloaded-calls-simple.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(lang_items, overloaded_calls)] +#![feature(lang_items, unboxed_closures)] use std::ops::{Fn, FnMut, FnOnce}; diff --git a/src/test/run-pass/overloaded-calls-zero-args.rs b/src/test/run-pass/overloaded-calls-zero-args.rs index b868c8c96b5fb..809a251fe8059 100644 --- a/src/test/run-pass/overloaded-calls-zero-args.rs +++ b/src/test/run-pass/overloaded-calls-zero-args.rs @@ -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}; diff --git a/src/test/run-pass/unboxed-closures-all-traits.rs b/src/test/run-pass/unboxed-closures-all-traits.rs index 508d1e46f7e18..635e1670aada1 100644 --- a/src/test/run-pass/unboxed-closures-all-traits.rs +++ b/src/test/run-pass/unboxed-closures-all-traits.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(lang_items, overloaded_calls, unboxed_closures)] +#![feature(lang_items, unboxed_closures)] fn a int>(f: F) -> int { f(1, 2) diff --git a/src/test/run-pass/unboxed-closures-by-ref.rs b/src/test/run-pass/unboxed-closures-by-ref.rs index 70d41a5c68909..be955486dac33 100644 --- a/src/test/run-pass/unboxed-closures-by-ref.rs +++ b/src/test/run-pass/unboxed-closures-by-ref.rs @@ -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)] // Test by-ref capture of environment in unboxed closure types diff --git a/src/test/run-pass/unboxed-closures-direct-sugary-call.rs b/src/test/run-pass/unboxed-closures-direct-sugary-call.rs index c77ee9914ef2e..2854d64f6637b 100644 --- a/src/test/run-pass/unboxed-closures-direct-sugary-call.rs +++ b/src/test/run-pass/unboxed-closures-direct-sugary-call.rs @@ -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)] fn main() { let mut unboxed = |&mut:| {}; diff --git a/src/test/run-pass/unboxed-closures-drop.rs b/src/test/run-pass/unboxed-closures-drop.rs index 00bf5fac095cd..8d4d7b4ecb503 100644 --- a/src/test/run-pass/unboxed-closures-drop.rs +++ b/src/test/run-pass/unboxed-closures-drop.rs @@ -11,7 +11,7 @@ // A battery of tests to ensure destructors of unboxed closure environments // run at the right times. -#![feature(overloaded_calls, unboxed_closures)] +#![feature(unboxed_closures)] static mut DROP_COUNT: uint = 0; diff --git a/src/test/run-pass/unboxed-closures-extern-fn.rs b/src/test/run-pass/unboxed-closures-extern-fn.rs index 82d51ba1f1645..2628bd90eef0e 100644 --- a/src/test/run-pass/unboxed-closures-extern-fn.rs +++ b/src/test/run-pass/unboxed-closures-extern-fn.rs @@ -10,8 +10,8 @@ // Checks that extern fn points implement the full range of Fn traits. -#![feature(unboxed_closure_sugar)] -#![feature(overloaded_calls)] +#![feature(unboxed_closures)] +#![feature(unboxed_closures)] use std::ops::{Fn,FnMut,FnOnce}; diff --git a/src/test/run-pass/unboxed-closures-fn-as-fnmut-and-fnonce.rs b/src/test/run-pass/unboxed-closures-fn-as-fnmut-and-fnonce.rs index 90272636bc59d..77d41ae190778 100644 --- a/src/test/run-pass/unboxed-closures-fn-as-fnmut-and-fnonce.rs +++ b/src/test/run-pass/unboxed-closures-fn-as-fnmut-and-fnonce.rs @@ -11,8 +11,8 @@ // Checks that the Fn trait hierarchy rules permit // any Fn trait to be used where Fn is implemented. -#![feature(unboxed_closure_sugar)] -#![feature(overloaded_calls)] +#![feature(unboxed_closures)] +#![feature(unboxed_closures)] use std::ops::{Fn,FnMut,FnOnce}; diff --git a/src/test/run-pass/unboxed-closures-fnmut-as-fnonce.rs b/src/test/run-pass/unboxed-closures-fnmut-as-fnonce.rs index bd01910a210ab..02395624cd123 100644 --- a/src/test/run-pass/unboxed-closures-fnmut-as-fnonce.rs +++ b/src/test/run-pass/unboxed-closures-fnmut-as-fnonce.rs @@ -11,8 +11,8 @@ // Checks that the Fn trait hierarchy rules permit // FnMut or FnOnce to be used where FnMut is implemented. -#![feature(unboxed_closure_sugar)] -#![feature(overloaded_calls)] +#![feature(unboxed_closures)] +#![feature(unboxed_closures)] use std::ops::{FnMut,FnOnce}; diff --git a/src/test/run-pass/unboxed-closures-manual-impl.rs b/src/test/run-pass/unboxed-closures-manual-impl.rs index 8f6cfe0499703..3a750dadb911c 100644 --- a/src/test/run-pass/unboxed-closures-manual-impl.rs +++ b/src/test/run-pass/unboxed-closures-manual-impl.rs @@ -9,7 +9,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(unboxed_closure_sugar)] +#![feature(unboxed_closures)] use std::ops::FnMut; diff --git a/src/test/run-pass/unboxed-closures-prelude.rs b/src/test/run-pass/unboxed-closures-prelude.rs index f9d2ba02123c4..6f672f2f2828c 100644 --- a/src/test/run-pass/unboxed-closures-prelude.rs +++ b/src/test/run-pass/unboxed-closures-prelude.rs @@ -10,7 +10,7 @@ // Tests that the reexports of `FnOnce` et al from the prelude work. -#![feature(unboxed_closures, unboxed_closure_sugar)] +#![feature(unboxed_closures)] fn main() { let task: Box int> = box |: x| x; diff --git a/src/test/run-pass/unboxed-closures-single-word-env.rs b/src/test/run-pass/unboxed-closures-single-word-env.rs index 4239cfdd8cf14..61ceb5e140e32 100644 --- a/src/test/run-pass/unboxed-closures-single-word-env.rs +++ b/src/test/run-pass/unboxed-closures-single-word-env.rs @@ -11,7 +11,7 @@ // Ensures that single-word environments work right in unboxed closures. // These take a different path in codegen. -#![feature(overloaded_calls, unboxed_closures)] +#![feature(unboxed_closures)] fn a int>(f: F) -> int { f(1, 2) diff --git a/src/test/run-pass/unboxed-closures-sugar-1.rs b/src/test/run-pass/unboxed-closures-sugar-1.rs deleted file mode 100644 index b358e7ce28833..0000000000000 --- a/src/test/run-pass/unboxed-closures-sugar-1.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Test that the unboxed closure sugar can be used with an arbitrary -// struct type and that it is equivalent to the same syntax using -// angle brackets. This test covers only simple types and in -// particular doesn't test bound regions. - -#![allow(dead_code)] - -struct Foo { - t: T, u: U -} - -trait Eq { } -impl Eq for X { } -fn eq>() { } - -fn test<'a,'b>() { - eq::< Foo<(),()>, Foo() >(); - eq::< Foo<(int,),()>, Foo(int) >(); - eq::< Foo<(int,uint),()>, Foo(int,uint) >(); - eq::< Foo<(int,uint),uint>, Foo(int,uint) -> uint >(); - eq::< Foo<(&'a int,&'b uint),uint>, Foo(&'a int,&'b uint) -> uint >(); -} - -fn main() { } diff --git a/src/test/run-pass/unboxed-closures-sugar-object.rs b/src/test/run-pass/unboxed-closures-sugar-object.rs index 3b38f72432f17..d65de438514f5 100644 --- a/src/test/run-pass/unboxed-closures-sugar-object.rs +++ b/src/test/run-pass/unboxed-closures-sugar-object.rs @@ -11,6 +11,7 @@ // Test unboxed closure sugar used in object types. #![allow(dead_code)] +#![feature(unboxed_closures)] struct Foo { t: T, u: U diff --git a/src/test/run-pass/unboxed-closures-unboxing-shim.rs b/src/test/run-pass/unboxed-closures-unboxing-shim.rs index 426352cadd87f..c41aeaa673f80 100644 --- a/src/test/run-pass/unboxed-closures-unboxing-shim.rs +++ b/src/test/run-pass/unboxed-closures-unboxing-shim.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(unboxed_closures, unboxed_closure_sugar)] +#![feature(unboxed_closures)] use std::ops::FnOnce; diff --git a/src/test/run-pass/unboxed-closures-unique-type-id.rs b/src/test/run-pass/unboxed-closures-unique-type-id.rs index 43ab16c106f6f..4fdfb8cf02a71 100644 --- a/src/test/run-pass/unboxed-closures-unique-type-id.rs +++ b/src/test/run-pass/unboxed-closures-unique-type-id.rs @@ -19,7 +19,7 @@ // // compile-flags: -g -#![feature(unboxed_closures, overloaded_calls)] +#![feature(unboxed_closures)] use std::ptr;