diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index c57cd134e01b665900652876a8d8ae308976ecbe..380991266822ca217a6e12979b9736443b86de41 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -239,6 +239,9 @@ pub fn phase_3_run_analysis_passes(sess: Session, time(time_passes, "resolution", (), |_| middle::resolve::resolve_crate(sess, lang_items, crate)); + let named_region_map = time(time_passes, "lifetime resolution", (), + |_| middle::resolve_lifetime::crate(sess, crate)); + time(time_passes, "looking for entry point", (), |_| middle::entry::find_entry_point(sess, crate, ast_map)); @@ -251,8 +254,8 @@ pub fn phase_3_run_analysis_passes(sess: Session, let rp_set = time(time_passes, "region parameterization inference", (), |_| middle::region::determine_rp_in_crate(sess, ast_map, def_map, crate)); - let ty_cx = ty::mk_ctxt(sess, def_map, ast_map, freevars, - region_map, rp_set, lang_items); + let ty_cx = ty::mk_ctxt(sess, def_map, named_region_map, ast_map, freevars, + region_map, lang_items); // passes are timed inside typeck let (method_map, vtable_map) = typeck::check_crate( diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs new file mode 100644 index 0000000000000000000000000000000000000000..98260be99023012e2c52e4305b98411f810c84e0 --- /dev/null +++ b/src/librustc/middle/resolve_lifetime.rs @@ -0,0 +1,321 @@ +// Copyright 2012-2013 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. + +/*! + * Name resolution for lifetimes. + * + * Name resolution for lifetimes follows MUCH simpler rules than the + * full resolve. For example, lifetime names are never exported or + * used between functions, and they operate in a purely top-down + * way. Therefore we break lifetime name resolution into a separate pass. + */ + +use driver::session; +use std::hashmap::HashMap; +use syntax::ast; +use syntax::codemap::Span; +use syntax::opt_vec::OptVec; +use syntax::parse::token::special_idents; +use syntax::print::pprust::{lifetime_to_str}; +use syntax::visit; +use syntax::visit::Visitor; + +// maps the id of each lifetime reference to the lifetime decl +// that it corresponds to +pub type NamedRegionMap = HashMap; + +struct LifetimeContext { + sess: session::Session, + named_region_map: @mut NamedRegionMap, +} + +enum ScopeChain<'self> { + ItemScope(&'self OptVec), + FnScope(ast::NodeId, &'self OptVec, &'self ScopeChain<'self>), + BlockScope(ast::NodeId, &'self ScopeChain<'self>), + RootScope +} + +pub fn crate(sess: session::Session, + crate: &ast::Crate) + -> @mut NamedRegionMap { + let mut ctxt = LifetimeContext { + sess: sess, + named_region_map: @mut HashMap::new() + }; + visit::walk_crate(&mut ctxt, crate, &RootScope); + sess.abort_if_errors(); + ctxt.named_region_map +} + +impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext { + fn visit_item(&mut self, + item: @ast::item, + _: &'self ScopeChain<'self>) { + let scope = match item.node { + ast::item_fn(*) | // fn lifetimes get added in visit_fn below + ast::item_mod(*) | + ast::item_mac(*) | + ast::item_foreign_mod(*) | + ast::item_static(*) => { + RootScope + } + ast::item_ty(_, ref generics) | + ast::item_enum(_, ref generics) | + ast::item_struct(_, ref generics) | + ast::item_impl(ref generics, _, _, _) | + ast::item_trait(ref generics, _, _) => { + self.check_lifetime_names(&generics.lifetimes); + ItemScope(&generics.lifetimes) + } + }; + debug!("entering scope {:?}", scope); + visit::walk_item(self, item, &scope); + debug!("exiting scope {:?}", scope); + } + + fn visit_fn(&mut self, + fk: &visit::fn_kind, + fd: &ast::fn_decl, + b: &ast::Block, + s: Span, + n: ast::NodeId, + scope: &'self ScopeChain<'self>) { + match *fk { + visit::fk_item_fn(_, generics, _, _) | + visit::fk_method(_, generics, _) => { + let scope1 = FnScope(n, &generics.lifetimes, scope); + self.check_lifetime_names(&generics.lifetimes); + debug!("pushing fn scope id={} due to item/method", n); + visit::walk_fn(self, fk, fd, b, s, n, &scope1); + debug!("popping fn scope id={} due to item/method", n); + } + visit::fk_anon(*) | visit::fk_fn_block(*) => { + visit::walk_fn(self, fk, fd, b, s, n, scope); + } + } + } + + fn visit_ty(&mut self, + ty: &ast::Ty, + scope: &'self ScopeChain<'self>) { + match ty.node { + ast::ty_closure(@ast::TyClosure { lifetimes: ref lifetimes, _ }) | + ast::ty_bare_fn(@ast::TyBareFn { lifetimes: ref lifetimes, _ }) => { + let scope1 = FnScope(ty.id, lifetimes, scope); + self.check_lifetime_names(lifetimes); + debug!("pushing fn scope id={} due to type", ty.id); + visit::walk_ty(self, ty, &scope1); + debug!("popping fn scope id={} due to type", ty.id); + } + _ => { + visit::walk_ty(self, ty, scope); + } + } + } + + fn visit_ty_method(&mut self, + m: &ast::TypeMethod, + scope: &'self ScopeChain<'self>) { + let scope1 = FnScope(m.id, &m.generics.lifetimes, scope); + self.check_lifetime_names(&m.generics.lifetimes); + debug!("pushing fn scope id={} due to ty_method", m.id); + visit::walk_ty_method(self, m, &scope1); + debug!("popping fn scope id={} due to ty_method", m.id); + } + + fn visit_block(&mut self, + b: &ast::Block, + scope: &'self ScopeChain<'self>) { + let scope1 = BlockScope(b.id, scope); + debug!("pushing block scope {}", b.id); + visit::walk_block(self, b, &scope1); + debug!("popping block scope {}", b.id); + } + + fn visit_lifetime_ref(&mut self, + lifetime_ref: &ast::Lifetime, + scope: &'self ScopeChain<'self>) { + if lifetime_ref.ident == special_idents::statik { + self.insert_lifetime(lifetime_ref, ast::DefStaticRegion); + return; + } + self.resolve_lifetime_ref(lifetime_ref, scope); + } +} + +impl LifetimeContext { + fn resolve_lifetime_ref(&self, + lifetime_ref: &ast::Lifetime, + scope: &ScopeChain) { + // Walk up the scope chain, tracking the number of fn scopes + // that we pass through, until we find a lifetime with the + // given name or we run out of scopes. If we encounter a code + // 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 scope = scope; + loop { + match *scope { + BlockScope(id, s) => { + return self.resolve_free_lifetime_ref(id, lifetime_ref, s); + } + + RootScope => { + break; + } + + ItemScope(lifetimes) => { + match search_lifetimes(lifetimes, lifetime_ref) { + Some((index, decl_id)) => { + let def = ast::DefTypeBoundRegion(index, decl_id); + self.insert_lifetime(lifetime_ref, def); + return; + } + None => { + break; + } + } + } + + FnScope(id, lifetimes, s) => { + match search_lifetimes(lifetimes, lifetime_ref) { + Some((_index, decl_id)) => { + let def = ast::DefFnBoundRegion(id, depth, decl_id); + self.insert_lifetime(lifetime_ref, def); + return; + } + + None => { + depth += 1; + scope = s; + } + } + } + } + } + + self.unresolved_lifetime_ref(lifetime_ref); + } + + fn resolve_free_lifetime_ref(&self, + scope_id: ast::NodeId, + lifetime_ref: &ast::Lifetime, + scope: &ScopeChain) { + // Walk up the scope chain, tracking the outermost free scope, + // until we encounter a scope that contains the named lifetime + // or we run out of scopes. + let mut scope_id = scope_id; + let mut scope = scope; + let mut search_result = None; + loop { + match *scope { + BlockScope(id, s) => { + scope_id = id; + scope = s; + } + + RootScope => { + break; + } + + ItemScope(lifetimes) => { + search_result = search_lifetimes(lifetimes, lifetime_ref); + break; + } + + FnScope(_, lifetimes, s) => { + search_result = search_lifetimes(lifetimes, lifetime_ref); + if search_result.is_some() { + break; + } + scope = s; + } + } + } + + match search_result { + Some((_depth, decl_id)) => { + let def = ast::DefFreeRegion(scope_id, decl_id); + self.insert_lifetime(lifetime_ref, def); + } + + None => { + self.unresolved_lifetime_ref(lifetime_ref); + } + } + + } + + fn unresolved_lifetime_ref(&self, + lifetime_ref: &ast::Lifetime) { + self.sess.span_err( + lifetime_ref.span, + format!("use of undeclared lifetime name `'{}`", + self.sess.str_of(lifetime_ref.ident))); + } + + fn check_lifetime_names(&self, lifetimes: &OptVec) { + for i in range(0, lifetimes.len()) { + let lifetime_i = lifetimes.get(i); + + let special_idents = [special_idents::statik]; + for lifetime in lifetimes.iter() { + if special_idents.iter().any(|&i| i == lifetime.ident) { + self.sess.span_err( + lifetime.span, + format!("illegal lifetime parameter name: `{}`", + self.sess.str_of(lifetime.ident))); + } + } + + for j in range(i + 1, lifetimes.len()) { + let lifetime_j = lifetimes.get(j); + + if lifetime_i.ident == lifetime_j.ident { + self.sess.span_err( + lifetime_j.span, + format!("lifetime name `'{}` declared twice in \ + the same scope", + self.sess.str_of(lifetime_j.ident))); + } + } + } + } + + fn insert_lifetime(&self, + lifetime_ref: &ast::Lifetime, + def: ast::DefRegion) { + if lifetime_ref.id == ast::DUMMY_NODE_ID { + self.sess.span_bug(lifetime_ref.span, + "Lifetime reference not renumbered, \ + probably a bug in syntax::fold"); + } + + debug!("lifetime_ref={} id={} resolved to {:?}", + lifetime_to_str(lifetime_ref, + self.sess.intr()), + lifetime_ref.id, + def); + self.named_region_map.insert(lifetime_ref.id, def); + } +} + +fn search_lifetimes(lifetimes: &OptVec, + lifetime_ref: &ast::Lifetime) + -> Option<(uint, ast::NodeId)> { + for (i, lifetime_decl) in lifetimes.iter().enumerate() { + if lifetime_decl.ident == lifetime_ref.ident { + return Some((i, lifetime_decl.id)); + } + } + return None; +} diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 96bb2897e0e2610b2b764c7e5bc80b50e39347bf..09f24b327f43949d91eab1e20aef4c89fd684482 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -17,6 +17,7 @@ use middle::lang_items::OpaqueStructLangItem; use middle::freevars; use middle::resolve; +use middle::resolve_lifetime; use middle::ty; use middle::subst::Subst; use middle::typeck; @@ -258,6 +259,8 @@ struct ctxt_ { sess: session::Session, def_map: resolve::DefMap, + named_region_map: @mut resolve_lifetime::NamedRegionMap, + region_maps: @mut middle::region::RegionMaps, region_paramd_items: middle::region::region_paramd_items, @@ -919,6 +922,7 @@ pub fn new_ty_hash() -> @mut HashMap { pub fn mk_ctxt(s: session::Session, dm: resolve::DefMap, + named_region_map: @mut resolve_lifetime::NamedRegionMap, amap: ast_map::map, freevars: freevars::freevar_map, region_maps: @mut middle::region::RegionMaps, @@ -926,6 +930,7 @@ pub fn mk_ctxt(s: session::Session, lang_items: middle::lang_items::LanguageItems) -> ctxt { @ctxt_ { + named_region_map: named_region_map, diag: s.diagnostic(), interner: @mut HashMap::new(), next_id: @mut primitives::LAST_PRIMITIVE_ID, diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index 45a6d709b04fb7fcac13ad26ef00608a7d1942c2..11d995762ee72083c59815c1f35ee0773b97e837 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -23,13 +23,10 @@ * In the check phase, when the @FnCtxt is used as the `AstConv`, * `get_item_ty()` just looks up the item type in `tcx.tcache`. * - * The `RegionScope` trait controls how region references are - * handled. It has two methods which are used to resolve anonymous - * region references (e.g., `&T`) and named region references (e.g., - * `&a.T`). There are numerous region scopes that can be used, but most - * commonly you want either `EmptyRscope`, which permits only the static - * region, or `TypeRscope`, which permits the self region if the type in - * question is parameterized by a region. + * The `RegionScope` trait controls what happens when the user does + * not specify a region in some location where a region is required + * (e.g., if the user writes `&Foo` as a type rather than `&'a Foo`). + * See the `rscope` module for more details. * * Unlike the `AstConv` trait, the region scope can change as we descend * the type. This is to accommodate the fact that (a) fn types are binding @@ -57,20 +54,17 @@ use middle::ty::{substs}; use middle::ty::{ty_param_substs_and_ty}; use middle::ty; -use middle::typeck::rscope::in_binding_rscope; -use middle::typeck::rscope::{RegionScope, RegionError}; -use middle::typeck::rscope::RegionParamNames; +use middle::typeck::rscope; +use middle::typeck::rscope::{RegionScope}; use middle::typeck::lookup_def_tcx; -use std::result; +use std::vec; use syntax::abi::AbiSet; use syntax::{ast, ast_util}; use syntax::codemap::Span; use syntax::opt_vec::OptVec; use syntax::opt_vec; use syntax::print::pprust::{lifetime_to_str, path_to_str}; -use syntax::parse::token::special_idents; -use util::common::indenter; pub trait AstConv { fn tcx(&self) -> ty::ctxt; @@ -81,55 +75,83 @@ pub trait AstConv { fn ty_infer(&self, span: Span) -> ty::t; } -pub fn get_region_reporting_err( +pub fn ast_region_to_region( tcx: ty::ctxt, - span: Span, - a_r: &Option, - res: Result) -> ty::Region + lifetime: &ast::Lifetime) + -> ty::Region { - match res { - result::Ok(r) => r, - result::Err(ref e) => { - let descr = match a_r { - &None => ~"anonymous lifetime", - &Some(ref a) => format!("lifetime {}", - lifetime_to_str(a, tcx.sess.intr())) - }; - tcx.sess.span_err( - span, - format!("Illegal {}: {}", - descr, e.msg)); - e.replacement + let r = match tcx.named_region_map.find(&lifetime.id) { + None => { + // should have been recorded by the `resolve_lifetime` pass + tcx.sess.span_bug(lifetime.span, "unresolved lifetime"); } - } + + Some(&ast::DefStaticRegion) => { + ty::re_static + } + + Some(&ast::DefFnBoundRegion(binder_id, _, id)) => { + ty::re_fn_bound(binder_id, ty::br_named(ast_util::local_def(id), + lifetime.ident)) + } + + Some(&ast::DefTypeBoundRegion(index, id)) => { + ty::re_type_bound(id, index, lifetime.ident) + } + + Some(&ast::DefFreeRegion(scope_id, id)) => { + ty::re_free(ty::FreeRegion { + scope_id: scope_id, + bound_region: ty::br_named(ast_util::local_def(id), + lifetime.ident) + }) + } + }; + + debug!("ast_region_to_region(lifetime={} id={}) yields {}", + lifetime_to_str(lifetime, tcx.sess.intr()), + lifetime.id, + r.repr(tcx)); + + r } -pub fn ast_region_to_region( +pub fn opt_ast_region_to_region( this: &AC, rscope: &RS, default_span: Span, opt_lifetime: &Option) -> ty::Region { - let (span, res) = match opt_lifetime { - &None => { - (default_span, rscope.anon_region(default_span)) - } - &Some(ref lifetime) if lifetime.ident == special_idents::statik => { - (lifetime.span, Ok(ty::re_static)) + let r = match *opt_lifetime { + Some(ref lifetime) => { + ast_region_to_region(this.tcx(), lifetime) } - &Some(ref lifetime) if lifetime.ident == special_idents::self_ => { - (lifetime.span, rscope.self_region(lifetime.span)) - } - &Some(ref lifetime) => { - (lifetime.span, rscope.named_region(lifetime.span, - lifetime.ident)) + + None => { + match rscope.anon_regions(default_span, 1) { + None => { + debug!("optional region in illegal location"); + this.tcx().sess.span_err( + default_span, "missing lifetime specifier"); + ty::re_static + } + + Some(rs) => { + rs[0] + } + } } }; - get_region_reporting_err(this.tcx(), span, opt_lifetime, res) + debug!("opt_ast_region_to_region(opt_lifetime={:?}) yields {}", + opt_lifetime.as_ref().map( + |e| lifetime_to_str(e, this.tcx().sess.intr())), + r.repr(this.tcx())); + + r } -fn ast_path_substs( +fn ast_path_substs( this: &AC, rscope: &RS, def_id: ast::DefId, @@ -200,7 +222,7 @@ fn ast_path_substs( } pub fn ast_path_to_substs_and_ty( + RS:RegionScope>( this: &AC, rscope: &RS, did: ast::DefId, @@ -217,7 +239,7 @@ pub fn ast_path_to_substs_and_ty( +pub fn ast_path_to_trait_ref( this: &AC, rscope: &RS, trait_def_id: ast::DefId, @@ -240,7 +262,7 @@ pub fn ast_path_to_trait_ref( return trait_ref; } -pub fn ast_path_to_ty( +pub fn ast_path_to_ty( this: &AC, rscope: &RS, did: ast::DefId, @@ -262,10 +284,10 @@ pub fn ast_path_to_ty( // Parses the programmer's textual representation of a type into our // internal notion of a type. `getter` is a function that returns the type // corresponding to a definition ID: -pub fn ast_ty_to_ty( +pub fn ast_ty_to_ty( this: &AC, rscope: &RS, ast_ty: &ast::Ty) -> ty::t { - fn ast_mt_to_mt( + fn ast_mt_to_mt( this: &AC, rscope: &RS, mt: &ast::mt) -> ty::mt { ty::mt {ty: ast_ty_to_ty(this, rscope, mt.ty), mutbl: mt.mutbl} @@ -274,7 +296,7 @@ fn ast_mt_to_mt( // Handle @, ~, and & being able to mean estrs and evecs. // If a_seq_ty is a str or a vec, make it an estr/evec. // Also handle first-class trait types. - fn mk_pointer( + fn mk_pointer( this: &AC, rscope: &RS, a_seq_ty: &ast::mt, @@ -387,7 +409,8 @@ fn check_path_args(tcx: ty::ctxt, ty::mk_ptr(tcx, ast_mt_to_mt(this, rscope, mt)) } ast::ty_rptr(ref region, ref mt) => { - let r = ast_region_to_region(this, rscope, ast_ty.span, region); + let r = opt_ast_region_to_region(this, rscope, ast_ty.span, region); + debug!("ty_rptr r={}", r.repr(this.tcx())); mk_pointer(this, rscope, mt, ty::vstore_slice(r), |tmt| ty::mk_rptr(tcx, r, tmt)) } @@ -551,7 +574,7 @@ fn check_path_args(tcx: ty::ctxt, } pub fn ty_of_arg( + RS:RegionScope>( this: &AC, rscope: &RS, a: &ast::arg, @@ -564,42 +587,12 @@ pub fn ty_of_arg( - this: &AC, - ast_lifetimes: &OptVec) -> OptVec -{ - /*! - * - * Converts a list of lifetimes into a list of bound identifier - * names. Does not permit special names like 'static or 'this to - * be bound. Note that this function is for use in closures, - * methods, and fn definitions. It is legal to bind 'this in a - * type. Eventually this distinction should go away and the same - * rules should apply everywhere ('this would not be a special name - * at that point). - */ - - let special_idents = [special_idents::statik, special_idents::self_]; - let mut bound_lifetime_names = opt_vec::Empty; - ast_lifetimes.map_to_vec(|ast_lifetime| { - if special_idents.iter().any(|&i| i == ast_lifetime.ident) { - this.tcx().sess.span_err( - ast_lifetime.span, - format!("illegal lifetime parameter name: `{}`", - lifetime_to_str(ast_lifetime, this.tcx().sess.intr()))); - } else { - bound_lifetime_names.push(ast_lifetime.ident); - } - }); - bound_lifetime_names -} - struct SelfInfo { untransformed_self_ty: ty::t, explicit_self: ast::explicit_self } -pub fn ty_of_method( +pub fn ty_of_method( this: &AC, rscope: &RS, purity: ast::purity, @@ -643,10 +636,7 @@ fn ty_of_method_or_bare_fn( // new region names that appear inside of the fn decl are bound to // that function type - let bound_lifetime_names = bound_lifetimes(this, lifetimes); - let rb = - in_binding_rscope(rscope, - RegionParamNames(bound_lifetime_names.clone())); + let rb = rscope::BindingRscope::new(id); let opt_transformed_self_ty = do opt_self_info.map |self_info| { transform_self_ty(this, &rb, self_info) @@ -671,7 +661,7 @@ fn ty_of_method_or_bare_fn( } }); - fn transform_self_ty( + fn transform_self_ty( this: &AC, rscope: &RS, self_info: &SelfInfo) -> Option @@ -683,9 +673,9 @@ fn transform_self_ty( } ast::sty_region(ref lifetime, mutability) => { let region = - ast_region_to_region(this, rscope, - self_info.explicit_self.span, - lifetime); + opt_ast_region_to_region(this, rscope, + self_info.explicit_self.span, + lifetime); Some(ty::mk_rptr(this.tcx(), region, ty::mt {ty: self_info.untransformed_self_ty, mutbl: mutability})) @@ -704,7 +694,7 @@ fn transform_self_ty( } } -pub fn ty_of_closure( +pub fn ty_of_closure( this: &AC, rscope: &RS, sigil: ast::Sigil, @@ -729,8 +719,8 @@ pub fn ty_of_closure( // resolve the function bound region in the original region // scope `rscope`, not the scope of the function parameters let bound_region = match opt_lifetime { - &Some(_) => { - ast_region_to_region(this, rscope, span, opt_lifetime) + &Some(ref lifetime) => { + ast_region_to_region(this.tcx(), lifetime) } &None => { match sigil { @@ -741,7 +731,7 @@ pub fn ty_of_closure( } ast::BorrowedSigil => { // &fn() defaults as normal for an omitted lifetime: - ast_region_to_region(this, rscope, span, opt_lifetime) + opt_ast_region_to_region(this, rscope, span, opt_lifetime) } } } @@ -749,10 +739,7 @@ pub fn ty_of_closure( // new region names that appear inside of the fn decl are bound to // that function type - let bound_lifetime_names = bound_lifetimes(this, lifetimes); - let rb = - in_binding_rscope(rscope, - RegionParamNames(bound_lifetime_names.clone())); + let rb = rscope::BindingRscope::new(id); let input_tys = do decl.inputs.iter().enumerate().map |(i, a)| { let expected_arg_ty = do expected_sig.as_ref().and_then |e| { diff --git a/src/librustc/middle/typeck/rscope.rs b/src/librustc/middle/typeck/rscope.rs index 1967122745dad5ebfaa98895dbebd991fd0fa8fd..2c48eb04ee6dc83481b3457f58b55fcafaa9c8ae 100644 --- a/src/librustc/middle/typeck/rscope.rs +++ b/src/librustc/middle/typeck/rscope.rs @@ -11,312 +11,59 @@ use middle::ty; -use std::result; +use std::vec; use syntax::ast; use syntax::codemap::Span; use syntax::opt_vec::OptVec; -use syntax::opt_vec; -use syntax::parse::token::special_idents; - -#[deriving(ToStr)] -pub struct RegionError { - msg: ~str, - replacement: ty::Region -} pub trait RegionScope { - fn anon_region(&self, span: Span) -> Result; - fn self_region(&self, span: Span) -> Result; - fn named_region(&self, span: Span, id: ast::Ident) - -> Result; -} - -#[deriving(Clone)] -pub struct EmptyRscope; -impl RegionScope for EmptyRscope { - fn anon_region(&self, _span: Span) -> Result { - result::Err(RegionError { - msg: ~"only 'static is allowed here", - replacement: ty::re_static - }) - } - fn self_region(&self, _span: Span) -> Result { - self.anon_region(_span) - } - fn named_region(&self, _span: Span, _id: ast::Ident) - -> Result - { - self.anon_region(_span) - } -} - -#[deriving(Clone)] -pub struct RegionParamNames(OptVec); - -impl RegionParamNames { - fn has_self(&self) -> bool { - self.has_ident(special_idents::self_) - } - - fn has_ident(&self, ident: ast::Ident) -> bool { - for region_param_name in self.iter() { - if *region_param_name == ident { - return true; - } - } - false - } - - pub fn add_generics(&mut self, generics: &ast::Generics) { - match generics.lifetimes { - opt_vec::Empty => {} - opt_vec::Vec(ref new_lifetimes) => { - match **self { - opt_vec::Empty => { - *self = RegionParamNames( - opt_vec::Vec(new_lifetimes.map(|lt| lt.ident))); - } - opt_vec::Vec(ref mut existing_lifetimes) => { - for new_lifetime in new_lifetimes.iter() { - existing_lifetimes.push(new_lifetime.ident); - } - } - } - } - } - } - - // Convenience function to produce the error for an unresolved name. The - // optional argument specifies a custom replacement. - pub fn undeclared_name(custom_replacement: Option) - -> Result { - let replacement = match custom_replacement { - None => ty::re_bound(ty::br_self), - Some(custom_replacement) => custom_replacement - }; - Err(RegionError { - msg: ~"this lifetime must be declared", - replacement: replacement - }) - } - - pub fn from_generics(generics: &ast::Generics) -> RegionParamNames { - match generics.lifetimes { - opt_vec::Empty => RegionParamNames(opt_vec::Empty), - opt_vec::Vec(ref lifetimes) => { - RegionParamNames(opt_vec::Vec(lifetimes.map(|lt| lt.ident))) - } - } - } - - pub fn from_lifetimes(lifetimes: &opt_vec::OptVec) - -> RegionParamNames { - match *lifetimes { - opt_vec::Empty => RegionParamNames::new(), - opt_vec::Vec(ref v) => { - RegionParamNames(opt_vec::Vec(v.map(|lt| lt.ident))) - } - } - } - - fn new() -> RegionParamNames { - RegionParamNames(opt_vec::Empty) - } -} - -#[deriving(Clone)] -struct RegionParameterization { - variance: ty::region_variance, - region_param_names: RegionParamNames, -} - -impl RegionParameterization { - pub fn from_variance_and_generics(variance: Option, - generics: &ast::Generics) - -> Option { - match variance { - None => None, - Some(variance) => { - Some(RegionParameterization { - variance: variance, - region_param_names: - RegionParamNames::from_generics(generics), - }) - } - } - } -} - -#[deriving(Clone)] -pub struct MethodRscope { - explicit_self: ast::explicit_self_, - variance: Option, - region_param_names: RegionParamNames, -} - -impl MethodRscope { - // `generics` here refers to the generics of the outer item (impl or - // trait). - pub fn new(explicit_self: ast::explicit_self_, - variance: Option, - rcvr_generics: &ast::Generics) - -> MethodRscope { - let region_param_names = - RegionParamNames::from_generics(rcvr_generics); - MethodRscope { - explicit_self: explicit_self, - variance: variance, - region_param_names: region_param_names - } - } - - pub fn region_param_names(&self) -> RegionParamNames { - self.region_param_names.clone() - } -} - -impl RegionScope for MethodRscope { - fn anon_region(&self, _span: Span) -> Result { - result::Err(RegionError { - msg: ~"anonymous lifetimes are not permitted here", - replacement: ty::re_bound(ty::br_self) - }) - } - fn self_region(&self, _span: Span) -> Result { - assert!(self.variance.is_some()); - match self.variance { - None => {} // must be borrowed self, so this is OK - Some(_) => { - if !self.region_param_names.has_self() { - return Err(RegionError { - msg: ~"the `self` lifetime must be declared", - replacement: ty::re_bound(ty::br_self) - }) - } - } - } - result::Ok(ty::re_bound(ty::br_self)) - } - fn named_region(&self, span: Span, id: ast::Ident) - -> Result { - if !self.region_param_names.has_ident(id) { - return RegionParamNames::undeclared_name(None); - } - do EmptyRscope.named_region(span, id).or_else |_e| { - result::Err(RegionError { - msg: ~"lifetime is not in scope", - replacement: ty::re_bound(ty::br_self) - }) - } - } + fn anon_regions(&self, + span: Span, + count: uint) + -> Option<~[ty::Region]>; } -#[deriving(Clone)] -pub struct TypeRscope(Option); - -impl TypeRscope { - fn replacement(&self) -> ty::Region { - if self.is_some() { - ty::re_bound(ty::br_self) - } else { - ty::re_static - } - } -} -impl RegionScope for TypeRscope { - fn anon_region(&self, _span: Span) -> Result { - result::Err(RegionError { - msg: ~"anonymous lifetimes are not permitted here", - replacement: self.replacement() - }) - } - fn self_region(&self, _span: Span) -> Result { - match **self { - None => { - // if the self region is used, region parameterization should - // have inferred that this type is RP - fail!("region parameterization should have inferred that \ - this type is RP"); - } - Some(ref region_parameterization) => { - if !region_parameterization.region_param_names.has_self() { - return Err(RegionError { - msg: ~"the `self` lifetime must be declared", - replacement: ty::re_bound(ty::br_self) - }) - } - } - } - result::Ok(ty::re_bound(ty::br_self)) - } - fn named_region(&self, span: Span, id: ast::Ident) - -> Result { - do EmptyRscope.named_region(span, id).or_else |_e| { - result::Err(RegionError { - msg: ~"only 'self is allowed as part of a type declaration", - replacement: self.replacement() - }) - } - } -} +// A scope in which all regions must be explicitly named +pub struct ExplicitRscope; -pub fn bound_self_region(rp: Option) - -> OptVec { - match rp { - Some(_) => opt_vec::with(ty::re_bound(ty::br_self)), - None => opt_vec::Empty +impl RegionScope for ExplicitRscope { + fn anon_regions(&self, + _span: Span, + _count: uint) + -> Option<~[ty::Region]> { + None } } pub struct BindingRscope { - base: @RegionScope, - anon_bindings: @mut uint, - region_param_names: RegionParamNames, + binder_id: ast::NodeId, + anon_bindings: @mut uint } -impl Clone for BindingRscope { - fn clone(&self) -> BindingRscope { +impl BindingRscope { + pub fn new(binder_id: ast::NodeId) -> BindingRscope { BindingRscope { - base: self.base, - anon_bindings: self.anon_bindings, - region_param_names: self.region_param_names.clone(), + binder_id: binder_id, + anon_bindings: @mut 0 } } } -pub fn in_binding_rscope( - this: &RS, - region_param_names: RegionParamNames) - -> BindingRscope { - let base = @(*this).clone(); - let base = base as @RegionScope; - BindingRscope { - base: base, - anon_bindings: @mut 0, - region_param_names: region_param_names, - } -} - impl RegionScope for BindingRscope { - fn anon_region(&self, _span: Span) -> Result { + fn anon_regions(&self, + _: Span, + count: uint) + -> Option<~[ty::Region]> { let idx = *self.anon_bindings; - *self.anon_bindings += 1; - result::Ok(ty::re_bound(ty::br_anon(idx))) - } - fn self_region(&self, span: Span) -> Result { - self.base.self_region(span) - } - fn named_region(&self, - span: Span, - id: ast::Ident) -> Result - { - do self.base.named_region(span, id).or_else |_e| { - let result = ty::re_bound(ty::br_named(id)); - if self.region_param_names.has_ident(id) { - result::Ok(result) - } else { - RegionParamNames::undeclared_name(Some(result)) - } - } + *self.anon_bindings += count; + Some(vec::from_fn(count, |i| ty::re_fn_bound(self.binder_id, + ty::br_anon(idx + i)))) } } + +pub fn bound_type_regions(defs: &[ty::RegionParameterDef]) + -> OptVec { + assert!(defs.iter().all(|def| def.def_id.crate == ast::LOCAL_CRATE)); + defs.iter().enumerate().map( + |(i, def)| ty::re_type_bound(def.def_id.node, i, def.ident)).collect() +} diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 2dd2629048bd37635aaa4b203e9c222e0121a657..c0c2e2822f1621331d699bb5284b7b0cae19ef6c 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -251,6 +251,20 @@ pub enum Def { DefMethod(DefId /* method */, Option /* trait */), } +#[deriving(Clone, Eq, IterBytes, Encodable, Decodable, ToStr)] +pub enum DefRegion { + DefStaticRegion, + DefTypeBoundRegion(/* index */ uint, /* lifetime decl */ NodeId), + DefFnBoundRegion(/* binder_id */ NodeId, /* depth */ uint, /* lifetime decl */ NodeId), + DefFreeRegion(/* block scope */ NodeId, /* lifetime decl */ NodeId), +} + +#[deriving(Clone, Eq, IterBytes, Encodable, Decodable, ToStr)] +pub struct DefNamedRegion { + node_id: NodeId, + depth: uint, +} + // The set of MetaItems that define the compilation environment of the crate, // used to drive conditional compilation pub type CrateConfig = ~[@MetaItem]; diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 09ee87d17014808e382196d7d5dd75f127fce008..60127be87b6ef7d48ee96d75b8119eb402fdff97 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -90,7 +90,32 @@ fn visit_struct_def(&mut self, s:@struct_def, i:Ident, g:&Generics, n:NodeId, e: walk_struct_def(self, s, i, g, n, e) } fn visit_struct_field(&mut self, s:@struct_field, e:E) { walk_struct_field(self, s, e) } - fn visit_mac(&mut self, m:&mac, e:E) { walk_mac(self, m, e); } + fn visit_opt_lifetime_ref(&mut self, + _span: Span, + opt_lifetime: &Option, + env: E) { + /*! + * Visits an optional reference to a lifetime. The `span` is + * the span of some surrounding reference should opt_lifetime + * be None. + */ + match *opt_lifetime { + Some(ref l) => self.visit_lifetime_ref(l, env), + None => () + } + } + fn visit_lifetime_ref(&mut self, _lifetime: &Lifetime, _e: E) { + /*! Visits a reference to a lifetime */ + } + fn visit_lifetime_decl(&mut self, _lifetime: &Lifetime, _e: E) { + /*! Visits a declaration of a lifetime */ + } + fn visit_explicit_self(&mut self, es: &explicit_self, e: E) { + walk_explicit_self(self, es, e) + } + fn visit_mac(&mut self, macro:&mac, e:E) { + walk_mac(self, macro, e) + } } pub fn walk_crate>(visitor: &mut V, crate: &Crate, env: E) { @@ -119,6 +144,18 @@ pub fn walk_local>(visitor: &mut V, local: &Local, env: E) } } +fn walk_explicit_self>(visitor: &mut V, + explicit_self: &explicit_self, + env: E) { + match explicit_self.node { + sty_static | sty_value(_) | sty_box(_) | sty_uniq(_) => { + } + sty_region(ref lifetime, _) => { + visitor.visit_opt_lifetime_ref(explicit_self.span, lifetime, env) + } + } +} + fn walk_trait_ref>(visitor: &mut V, trait_ref: &ast::trait_ref, env: E) { @@ -221,8 +258,11 @@ pub fn skip_ty>(_: &mut V, _: &Ty, _: E) { pub fn walk_ty>(visitor: &mut V, typ: &Ty, env: E) { match typ.node { ty_box(ref mutable_type) | ty_uniq(ref mutable_type) | - ty_vec(ref mutable_type) | ty_ptr(ref mutable_type) | - ty_rptr(_, ref mutable_type) => { + ty_vec(ref mutable_type) | ty_ptr(ref mutable_type) => { + visitor.visit_ty(mutable_type.ty, env) + } + ty_rptr(ref lifetime, ref mutable_type) => { + visitor.visit_opt_lifetime_ref(typ.span, lifetime, env.clone()); visitor.visit_ty(mutable_type.ty, env) } ty_tup(ref tuple_element_types) => { @@ -231,19 +271,27 @@ pub fn walk_ty>(visitor: &mut V, typ: &Ty, env: E) { } } ty_closure(ref function_declaration) => { - for argument in function_declaration.decl.inputs.iter() { + for argument in function_declaration.decl.inputs.iter() { visitor.visit_ty(&argument.ty, env.clone()) - } - visitor.visit_ty(&function_declaration.decl.output, env.clone()); - for bounds in function_declaration.bounds.iter() { + } + visitor.visit_ty(&function_declaration.decl.output, env.clone()); + for bounds in function_declaration.bounds.iter() { walk_ty_param_bounds(visitor, bounds, env.clone()) - } + } + visitor.visit_opt_lifetime_ref( + typ.span, + &function_declaration.region, + env.clone()); + walk_lifetime_decls(visitor, &function_declaration.lifetimes, + env.clone()); } ty_bare_fn(ref function_declaration) => { for argument in function_declaration.decl.inputs.iter() { visitor.visit_ty(&argument.ty, env.clone()) } - visitor.visit_ty(&function_declaration.decl.output, env.clone()) + visitor.visit_ty(&function_declaration.decl.output, env.clone()); + walk_lifetime_decls(visitor, &function_declaration.lifetimes, + env.clone()); } ty_path(ref path, ref bounds, _) => { walk_path(visitor, path, env.clone()); @@ -262,10 +310,21 @@ pub fn walk_ty>(visitor: &mut V, typ: &Ty, env: E) { } } +fn walk_lifetime_decls>(visitor: &mut V, + lifetimes: &OptVec, + env: E) { + for l in lifetimes.iter() { + visitor.visit_lifetime_decl(l, env.clone()); + } +} + pub fn walk_path>(visitor: &mut V, path: &Path, env: E) { for segment in path.segments.iter() { for typ in segment.types.iter() { - visitor.visit_ty(typ, env.clone()) + visitor.visit_ty(typ, env.clone()); + } + for lifetime in segment.lifetimes.iter() { + visitor.visit_lifetime_ref(lifetime, env.clone()); } } } @@ -354,6 +413,7 @@ pub fn walk_generics>(visitor: &mut V, for type_parameter in generics.ty_params.iter() { walk_ty_param_bounds(visitor, &type_parameter.bounds, env.clone()) } + walk_lifetime_decls(visitor, &generics.lifetimes, env); } pub fn walk_fn_decl>(visitor: &mut V, @@ -385,18 +445,31 @@ pub fn walk_fn>(visitor: &mut V, function_kind: &fn_kind, function_declaration: &fn_decl, function_body: &Block, - _: Span, + _span: Span, _: NodeId, env: E) { walk_fn_decl(visitor, function_declaration, env.clone()); - let generics = generics_of_fn(function_kind); - visitor.visit_generics(&generics, env.clone()); + + match *function_kind { + fk_item_fn(_, generics, _, _) => { + visitor.visit_generics(generics, env.clone()); + } + fk_method(_, generics, method) => { + visitor.visit_generics(generics, env.clone()); + + visitor.visit_explicit_self(&method.explicit_self, env.clone()); + } + fk_anon(*) | fk_fn_block(*) => { + } + } + visitor.visit_block(function_body, env) } pub fn walk_ty_method>(visitor: &mut V, - method_type: &TypeMethod, - env: E) { + method_type: &TypeMethod, + env: E) { + visitor.visit_explicit_self(&method_type.explicit_self, env.clone()); for argument_type in method_type.decl.inputs.iter() { visitor.visit_ty(&argument_type.ty, env.clone()) } diff --git a/src/test/compile-fail/regions-name-duplicated.rs b/src/test/compile-fail/regions-name-duplicated.rs new file mode 100644 index 0000000000000000000000000000000000000000..518fe0b00b6ce7c743de9f0ba701bb644bafe24e --- /dev/null +++ b/src/test/compile-fail/regions-name-duplicated.rs @@ -0,0 +1,15 @@ +// Copyright 2012-2013 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. + +struct Foo<'a, 'a> { //~ ERROR lifetime name `'a` declared twice + x: &'a int +} + +fn main() {} diff --git a/src/test/compile-fail/regions-name-static.rs b/src/test/compile-fail/regions-name-static.rs new file mode 100644 index 0000000000000000000000000000000000000000..c1170654dd294c7b00e43bed3e0c6e8b04eb333d --- /dev/null +++ b/src/test/compile-fail/regions-name-static.rs @@ -0,0 +1,15 @@ +// Copyright 2012-2013 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. + +struct Foo<'static> { //~ ERROR illegal lifetime parameter name: `static` + x: &'static int +} + +fn main() {} diff --git a/src/test/compile-fail/regions-name-undeclared.rs b/src/test/compile-fail/regions-name-undeclared.rs new file mode 100644 index 0000000000000000000000000000000000000000..abff33e05149126f18c4da94dfc48d7234b26fa0 --- /dev/null +++ b/src/test/compile-fail/regions-name-undeclared.rs @@ -0,0 +1,60 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Check that lifetime resolver enforces the lifetime name scoping +// rules correctly in various scenarios. + +struct Foo<'a> { + x: &'a int +} + +impl<'a> Foo<'a> { + // &'a is inherited: + fn m1(&self, arg: &'a int) { } + fn m2(&'a self) { } + fn m3(&self, arg: Foo<'a>) { } + + // &'b is not: + fn m4(&self, arg: &'b int) { } //~ ERROR undeclared lifetime + fn m5(&'b self) { } //~ ERROR undeclared lifetime + fn m6(&self, arg: Foo<'b>) { } //~ ERROR undeclared lifetime +} + +fn bar<'a>(x: &'a int) { + // &'a is visible to code: + let y: &'a int = x; + + // &'a is not visible to *items*: + type X = Option<&'a int>; //~ ERROR undeclared lifetime + enum E { + E1(&'a int) //~ ERROR undeclared lifetime + } + struct S { + f: &'a int //~ ERROR undeclared lifetime + } + fn f(a: &'a int) { } //~ ERROR undeclared lifetime + + // &'a CAN be declared on functions and used then: + fn g<'a>(a: &'a int) { } // OK + fn h(a: &fn<'a>(&'a int)) { } // OK +} + +// Test nesting of lifetimes in fn type declarations +fn fn_types(a: &'a int, //~ ERROR undeclared lifetime + b: &fn<'a>(a: &'a int, + b: &'b int, //~ ERROR undeclared lifetime + c: &fn<'b>(a: &'a int, + b: &'b int), + d: &'b int), //~ ERROR undeclared lifetime + c: &'a int) //~ ERROR undeclared lifetime +{ +} + +pub fn main() {}