提交 8e1de177 编写于 作者: N Niko Matsakis

Create a new pass to resolve named lifetimes; rscope is not only

used to indicate when anonymous regions (i.e., &T) are permitted
上级 a594a999
......@@ -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(
......
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
* 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<ast::NodeId, ast::DefRegion>;
struct LifetimeContext {
sess: session::Session,
named_region_map: @mut NamedRegionMap,
}
enum ScopeChain<'self> {
ItemScope(&'self OptVec<ast::Lifetime>),
FnScope(ast::NodeId, &'self OptVec<ast::Lifetime>, &'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<ast::Lifetime>) {
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<ast::Lifetime>,
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;
}
......@@ -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<V:'static>() -> @mut HashMap<t, V> {
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,
......
......@@ -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<ast::Lifetime>,
res: Result<ty::Region, RegionError>) -> 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<AC:AstConv,RS:RegionScope + Clone + 'static>(
pub fn opt_ast_region_to_region<AC:AstConv,RS:RegionScope>(
this: &AC,
rscope: &RS,
default_span: Span,
opt_lifetime: &Option<ast::Lifetime>) -> 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<AC:AstConv,RS:RegionScope + Clone + 'static>(
fn ast_path_substs<AC:AstConv,RS:RegionScope>(
this: &AC,
rscope: &RS,
def_id: ast::DefId,
......@@ -200,7 +222,7 @@ fn ast_path_substs<AC:AstConv,RS:RegionScope + Clone + 'static>(
}
pub fn ast_path_to_substs_and_ty<AC:AstConv,
RS:RegionScope + Clone + 'static>(
RS:RegionScope>(
this: &AC,
rscope: &RS,
did: ast::DefId,
......@@ -217,7 +239,7 @@ pub fn ast_path_to_substs_and_ty<AC:AstConv,
ty_param_substs_and_ty { substs: substs, ty: ty }
}
pub fn ast_path_to_trait_ref<AC:AstConv,RS:RegionScope + Clone + 'static>(
pub fn ast_path_to_trait_ref<AC:AstConv,RS:RegionScope>(
this: &AC,
rscope: &RS,
trait_def_id: ast::DefId,
......@@ -240,7 +262,7 @@ pub fn ast_path_to_trait_ref<AC:AstConv,RS:RegionScope + Clone + 'static>(
return trait_ref;
}
pub fn ast_path_to_ty<AC:AstConv,RS:RegionScope + Clone + 'static>(
pub fn ast_path_to_ty<AC:AstConv,RS:RegionScope>(
this: &AC,
rscope: &RS,
did: ast::DefId,
......@@ -262,10 +284,10 @@ pub fn ast_path_to_ty<AC:AstConv,RS:RegionScope + Clone + 'static>(
// 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<AC:AstConv, RS:RegionScope + Clone + 'static>(
pub fn ast_ty_to_ty<AC:AstConv, RS:RegionScope>(
this: &AC, rscope: &RS, ast_ty: &ast::Ty) -> ty::t {
fn ast_mt_to_mt<AC:AstConv, RS:RegionScope + Clone + 'static>(
fn ast_mt_to_mt<AC:AstConv, RS:RegionScope>(
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<AC:AstConv, RS:RegionScope + Clone + 'static>(
// 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<AC:AstConv,RS:RegionScope + Clone + 'static>(
fn mk_pointer<AC:AstConv,RS:RegionScope>(
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<AC:AstConv,
RS:RegionScope + Clone + 'static>(
RS:RegionScope>(
this: &AC,
rscope: &RS,
a: &ast::arg,
......@@ -564,42 +587,12 @@ pub fn ty_of_arg<AC:AstConv,
}
}
pub fn bound_lifetimes<AC:AstConv>(
this: &AC,
ast_lifetimes: &OptVec<ast::Lifetime>) -> OptVec<ast::Ident>
{
/*!
*
* 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<AC:AstConv,RS:RegionScope + Clone + 'static>(
pub fn ty_of_method<AC:AstConv>(
this: &AC,
rscope: &RS,
purity: ast::purity,
......@@ -643,10 +636,7 @@ fn ty_of_method_or_bare_fn<AC:AstConv,RS:RegionScope + Clone + 'static>(
// 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<AC:AstConv,RS:RegionScope + Clone + 'static>(
}
});
fn transform_self_ty<AC:AstConv,RS:RegionScope + Clone + 'static>(
fn transform_self_ty<AC:AstConv,RS:RegionScope>(
this: &AC,
rscope: &RS,
self_info: &SelfInfo) -> Option<ty::t>
......@@ -683,9 +673,9 @@ fn transform_self_ty<AC:AstConv,RS:RegionScope + Clone + 'static>(
}
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<AC:AstConv,RS:RegionScope + Clone + 'static>(
}
}
pub fn ty_of_closure<AC:AstConv,RS:RegionScope + Clone + 'static>(
pub fn ty_of_closure<AC:AstConv,RS:RegionScope>(
this: &AC,
rscope: &RS,
sigil: ast::Sigil,
......@@ -729,8 +719,8 @@ pub fn ty_of_closure<AC:AstConv,RS:RegionScope + Clone + 'static>(
// 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<AC:AstConv,RS:RegionScope + Clone + 'static>(
}
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<AC:AstConv,RS:RegionScope + Clone + 'static>(
// 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| {
......
......@@ -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<ty::Region, RegionError>;
fn self_region(&self, span: Span) -> Result<ty::Region, RegionError>;
fn named_region(&self, span: Span, id: ast::Ident)
-> Result<ty::Region, RegionError>;
}
#[deriving(Clone)]
pub struct EmptyRscope;
impl RegionScope for EmptyRscope {
fn anon_region(&self, _span: Span) -> Result<ty::Region, RegionError> {
result::Err(RegionError {
msg: ~"only 'static is allowed here",
replacement: ty::re_static
})
}
fn self_region(&self, _span: Span) -> Result<ty::Region, RegionError> {
self.anon_region(_span)
}
fn named_region(&self, _span: Span, _id: ast::Ident)
-> Result<ty::Region, RegionError>
{
self.anon_region(_span)
}
}
#[deriving(Clone)]
pub struct RegionParamNames(OptVec<ast::Ident>);
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<ty::Region>)
-> Result<ty::Region, RegionError> {
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<ast::Lifetime>)
-> 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<ty::region_variance>,
generics: &ast::Generics)
-> Option<RegionParameterization> {
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<ty::region_variance>,
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<ty::region_variance>,
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<ty::Region, RegionError> {
result::Err(RegionError {
msg: ~"anonymous lifetimes are not permitted here",
replacement: ty::re_bound(ty::br_self)
})
}
fn self_region(&self, _span: Span) -> Result<ty::Region, RegionError> {
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<ty::Region, RegionError> {
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<RegionParameterization>);
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<ty::Region, RegionError> {
result::Err(RegionError {
msg: ~"anonymous lifetimes are not permitted here",
replacement: self.replacement()
})
}
fn self_region(&self, _span: Span) -> Result<ty::Region, RegionError> {
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<ty::Region, RegionError> {
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<ty::region_variance>)
-> OptVec<ty::Region> {
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<RS:RegionScope + Clone + 'static>(
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<ty::Region, RegionError> {
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<ty::Region, RegionError> {
self.base.self_region(span)
}
fn named_region(&self,
span: Span,
id: ast::Ident) -> Result<ty::Region, RegionError>
{
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<ty::Region> {
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()
}
......@@ -251,6 +251,20 @@ pub enum Def {
DefMethod(DefId /* method */, Option<DefId> /* 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];
......
......@@ -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<Lifetime>,
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<E:Clone, V:Visitor<E>>(visitor: &mut V, crate: &Crate, env: E) {
......@@ -119,6 +144,18 @@ pub fn walk_local<E:Clone, V:Visitor<E>>(visitor: &mut V, local: &Local, env: E)
}
}
fn walk_explicit_self<E:Clone, V:Visitor<E>>(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<E:Clone, V:Visitor<E>>(visitor: &mut V,
trait_ref: &ast::trait_ref,
env: E) {
......@@ -221,8 +258,11 @@ pub fn skip_ty<E, V:Visitor<E>>(_: &mut V, _: &Ty, _: E) {
pub fn walk_ty<E:Clone, V:Visitor<E>>(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<E:Clone, V:Visitor<E>>(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<E:Clone, V:Visitor<E>>(visitor: &mut V, typ: &Ty, env: E) {
}
}
fn walk_lifetime_decls<E:Clone, V:Visitor<E>>(visitor: &mut V,
lifetimes: &OptVec<Lifetime>,
env: E) {
for l in lifetimes.iter() {
visitor.visit_lifetime_decl(l, env.clone());
}
}
pub fn walk_path<E:Clone, V:Visitor<E>>(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<E:Clone, V:Visitor<E>>(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<E:Clone, V:Visitor<E>>(visitor: &mut V,
......@@ -385,18 +445,31 @@ pub fn walk_fn<E:Clone, V:Visitor<E>>(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<E:Clone, V:Visitor<E>>(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())
}
......
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
struct Foo<'a, 'a> { //~ ERROR lifetime name `'a` declared twice
x: &'a int
}
fn main() {}
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
struct Foo<'static> { //~ ERROR illegal lifetime parameter name: `static`
x: &'static int
}
fn main() {}
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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() {}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册