Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 5121 fns with early lifetime params #10556

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 167 additions & 36 deletions src/librustc/middle/resolve_lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use driver::session;
use std::hashmap::HashMap;
use syntax::ast;
use syntax::codemap::Span;
use syntax::opt_vec;
use syntax::opt_vec::OptVec;
use syntax::parse::token::special_idents;
use syntax::print::pprust::{lifetime_to_str};
Expand All @@ -37,12 +38,14 @@ struct LifetimeContext {
}

enum ScopeChain<'self> {
ItemScope(&'self OptVec<ast::Lifetime>),
FnScope(ast::NodeId, &'self OptVec<ast::Lifetime>, &'self ScopeChain<'self>),
BlockScope(ast::NodeId, &'self ScopeChain<'self>),
EarlyScope(uint, &'self OptVec<ast::Lifetime>, Scope<'self>),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document the first tuple component of EarlyScope. (Or, alternatively, provide a pointer, such as a function name, to which part of the code you'd recommend reading to find out what they are.)

LateScope(ast::NodeId, &'self OptVec<ast::Lifetime>, Scope<'self>),
BlockScope(ast::NodeId, Scope<'self>),
RootScope
}

type Scope<'self> = &'self ScopeChain<'self>;

pub fn crate(sess: session::Session,
crate: &ast::Crate)
-> @mut NamedRegionMap {
Expand All @@ -55,10 +58,10 @@ pub fn crate(sess: session::Session,
ctxt.named_region_map
}

impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext {
impl<'self> Visitor<Scope<'self>> for LifetimeContext {
fn visit_item(&mut self,
item: @ast::item,
_: &'self ScopeChain<'self>) {
_: Scope<'self>) {
let scope = match item.node {
ast::item_fn(*) | // fn lifetimes get added in visit_fn below
ast::item_mod(*) |
Expand All @@ -73,7 +76,7 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext {
ast::item_impl(ref generics, _, _, _) |
ast::item_trait(ref generics, _, _) => {
self.check_lifetime_names(&generics.lifetimes);
ItemScope(&generics.lifetimes)
EarlyScope(0, &generics.lifetimes, &RootScope)
}
};
debug!("entering scope {:?}", scope);
Expand All @@ -87,33 +90,32 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext {
b: &ast::Block,
s: Span,
n: ast::NodeId,
scope: &'self ScopeChain<'self>) {
scope: Scope<'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);
self.visit_fn_decl(
n, generics, scope,
|this, scope1| visit::walk_fn(this, fk, fd, b,
s, n, scope1))
}
visit::fk_anon(*) | visit::fk_fn_block(*) => {
visit::walk_fn(self, fk, fd, b, s, n, scope);
visit::walk_fn(self, fk, fd, b, s, n, scope)
}
}
}

fn visit_ty(&mut self,
ty: &ast::Ty,
scope: &'self ScopeChain<'self>) {
scope: Scope<'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);
let scope1 = LateScope(ty.id, lifetimes, scope);
self.check_lifetime_names(lifetimes);
debug!("pushing fn scope id={} due to type", ty.id);
debug!("pushing fn ty scope id={} due to type", ty.id);
visit::walk_ty(self, ty, &scope1);
debug!("popping fn scope id={} due to type", ty.id);
debug!("popping fn ty scope id={} due to type", ty.id);
}
_ => {
visit::walk_ty(self, ty, scope);
Expand All @@ -123,17 +125,15 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext {

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);
scope: Scope<'self>) {
self.visit_fn_decl(
m.id, &m.generics, scope,
|this, scope1| visit::walk_ty_method(this, m, scope1))
}

fn visit_block(&mut self,
b: &ast::Block,
scope: &'self ScopeChain<'self>) {
scope: Scope<'self>) {
let scope1 = BlockScope(b.id, scope);
debug!("pushing block scope {}", b.id);
visit::walk_block(self, b, &scope1);
Expand All @@ -142,7 +142,7 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext {

fn visit_lifetime_ref(&mut self,
lifetime_ref: &ast::Lifetime,
scope: &'self ScopeChain<'self>) {
scope: Scope<'self>) {
if lifetime_ref.ident == special_idents::statik {
self.insert_lifetime(lifetime_ref, ast::DefStaticRegion);
return;
Expand All @@ -151,7 +151,93 @@ impl<'self> Visitor<&'self ScopeChain<'self>> for LifetimeContext {
}
}

impl<'self> ScopeChain<'self> {
fn count_early_params(&self) -> uint {
/*!
* Counts the number of early parameters that are in scope.
* Used when checking methods so that we assign the early
* lifetime parameters declared on the method indices that
* come after those from the type (e.g., if there is something
* like `impl<'a> Foo { ... fn bar<'b>(...) }` then `'a` would
* have index 0 and `'b` would have index 1.
*/

match *self {
RootScope => 0,
EarlyScope(base, lifetimes, _) => base + lifetimes.len(),
LateScope(_, _, s) => s.count_early_params(),
BlockScope(_, _) => 0,
}
}
}

impl LifetimeContext {
fn visit_fn_decl(&mut self,
n: ast::NodeId,
generics: &ast::Generics,
scope: Scope,
walk: |&mut LifetimeContext, Scope|) {
/*!
* Handles visiting fns and methods. These are a bit
* complicated because we must distinguish early- vs late-bound
* lifetime parameters. We do this by checking which lifetimes
* appear within type bounds; those are early bound lifetimes,
* and the rest are late bound.
*
* For example:
*
* fn foo<'a,'b,'c,T:Trait<'b>>(...)
*
* Here `'a` and `'c` are late bound but `'b` is early
* bound. Note that early- and late-bound lifetimes may be
* interspersed together.
*
* If early bound lifetimes are present, we separate them into
* their own list (and likewise for late bound). They will be
* numbered sequentially, starting from the lowest index that
* is already in scope (for a fn item, that will be 0, but for
* a method it might not be). Late bound lifetimes are
* resolved by name and associated with a binder id (`n`), so
* the ordering is not important there.
*/

self.check_lifetime_names(&generics.lifetimes);

let early_count = scope.count_early_params();
let referenced_idents =
free_lifetimes(&generics.ty_params);
debug!("pushing fn scope id={} due to item/method \
referenced_idents={:?} \
early_count={}",
n,
referenced_idents.map(|&i| self.sess.str_of(i)),
early_count);
if referenced_idents.is_empty() {
let scope1 = LateScope(n, &generics.lifetimes, scope);

walk(self, &scope1)
} else {
let early: OptVec<ast::Lifetime> =
generics.lifetimes.iter()
.filter(|l| referenced_idents.iter().any(|i| i == &l.ident))
.map(|l| *l)
.collect();
let scope1 = EarlyScope(early_count, &early, scope);
debug!("early names = {:?}",
early.map(|l| self.sess.str_of(l.ident)));

let late: OptVec<ast::Lifetime> =
generics.lifetimes.iter()
.filter(|l| !referenced_idents.iter().any(|i| i == &l.ident))
.map(|l| *l)
.collect();
let scope2 = LateScope(n, &late, &scope1);

walk(self, &scope2)
}
debug!("popping fn scope id={} due to item/method", n);
}

fn resolve_lifetime_ref(&self,
lifetime_ref: &ast::Lifetime,
scope: &ScopeChain) {
Expand All @@ -173,23 +259,25 @@ impl LifetimeContext {
break;
}

ItemScope(lifetimes) => {
EarlyScope(base, lifetimes, s) => {
match search_lifetimes(lifetimes, lifetime_ref) {
Some((index, decl_id)) => {
Some((offset, decl_id)) => {
let index = base + offset;
let def = ast::DefEarlyBoundRegion(index, decl_id);
self.insert_lifetime(lifetime_ref, def);
return;
}
None => {
break;
depth += 1;
scope = s;
}
}
}

FnScope(id, lifetimes, s) => {
LateScope(binder_id, lifetimes, s) => {
match search_lifetimes(lifetimes, lifetime_ref) {
Some((_index, decl_id)) => {
let def = ast::DefLateBoundRegion(id, depth, decl_id);
let def = ast::DefLateBoundRegion(binder_id, depth, decl_id);
self.insert_lifetime(lifetime_ref, def);
return;
}
Expand Down Expand Up @@ -227,12 +315,7 @@ impl LifetimeContext {
break;
}

ItemScope(lifetimes) => {
search_result = search_lifetimes(lifetimes, lifetime_ref);
break;
}

FnScope(_, lifetimes, s) => {
EarlyScope(_, lifetimes, s) | LateScope(_, lifetimes, s) => {
search_result = search_lifetimes(lifetimes, lifetime_ref);
if search_result.is_some() {
break;
Expand Down Expand Up @@ -319,3 +402,51 @@ fn search_lifetimes(lifetimes: &OptVec<ast::Lifetime>,
}
return None;
}

///////////////////////////////////////////////////////////////////////////

pub fn early_bound_lifetimes<'a>(generics: &'a ast::Generics) -> OptVec<ast::Lifetime> {
let referenced_idents = free_lifetimes(&generics.ty_params);
if referenced_idents.is_empty() {
return opt_vec::Empty;
}

generics.lifetimes.iter()
.filter(|l| referenced_idents.iter().any(|i| i == &l.ident))
.map(|l| *l)
.collect()
}

pub fn free_lifetimes(ty_params: &OptVec<ast::TyParam>) -> OptVec<ast::Ident> {
/*!
* Gathers up and returns the names of any lifetimes that appear
* free in `ty_params`. Of course, right now, all lifetimes appear
* free, since we don't have any binders in type parameter
* declarations, but I just to be forwards compatible for future
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"I just to be ..." is not grammatical.

* extensions with my terminology. =)
*/

let mut collector = FreeLifetimeCollector { names: opt_vec::Empty };
for ty_param in ty_params.iter() {
visit::walk_ty_param_bounds(&mut collector, &ty_param.bounds, ());
}
return collector.names;

struct FreeLifetimeCollector {
names: OptVec<ast::Ident>,
}

impl Visitor<()> for FreeLifetimeCollector {
fn visit_ty(&mut self, t:&ast::Ty, _:()) {
// for some weird reason visitor doesn't descend into
// types by default
visit::walk_ty(self, t, ());
}

fn visit_lifetime_ref(&mut self,
lifetime_ref: &ast::Lifetime,
_: ()) {
self.names.push(lifetime_ref.ident);
}
}
}
35 changes: 29 additions & 6 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -882,7 +882,8 @@ pub struct Generics {
/// List of type parameters declared on the item.
type_param_defs: @~[TypeParameterDef],

/// List of region parameters declared on the item.
/// List of region parameters declared on the item. In the
/// case of a fn or method, only includes *early-bound* lifetimes.
region_param_defs: @[RegionParameterDef],
}

Expand Down Expand Up @@ -4739,6 +4740,7 @@ pub fn construct_parameter_environment(
item_type_params: &[TypeParameterDef],
method_type_params: &[TypeParameterDef],
item_region_params: &[RegionParameterDef],
method_region_params: &[RegionParameterDef],
free_id: ast::NodeId)
-> ParameterEnvironment
{
Expand Down Expand Up @@ -4766,11 +4768,23 @@ pub fn construct_parameter_environment(
});

// map bound 'a => free 'a
let region_params = item_region_params.iter().
map(|r| ty::ReFree(ty::FreeRegion {
scope_id: free_id,
bound_region: ty::BrNamed(r.def_id, r.ident)})).
collect();
let region_params = {
fn push_region_params(accum: OptVec<ty::Region>,
free_id: ast::NodeId,
region_params: &[RegionParameterDef])
-> OptVec<ty::Region> {
let mut accum = accum;
for r in region_params.iter() {
accum.push(
ty::ReFree(ty::FreeRegion {
scope_id: free_id,
bound_region: ty::BrNamed(r.def_id, r.ident)}));
}
accum
}
let t = push_region_params(opt_vec::Empty, free_id, item_region_params);
push_region_params(t, free_id, method_region_params)
};

let free_substs = substs {
self_ty: self_ty,
Expand All @@ -4792,6 +4806,15 @@ pub fn construct_parameter_environment(
}
});

debug!("construct_parameter_environment: free_id={} \
free_substs={} \
self_param_bound={} \
type_param_bounds={}",
free_id,
free_substs.repr(tcx),
self_bound_substd.repr(tcx),
type_param_bounds_substd.repr(tcx));

ty::ParameterEnvironment {
free_substs: free_substs,
self_param_bound: self_bound_substd,
Expand Down
Loading