1
Fork 0

Add attribute support to generic lifetime and type parameters.

I am using `ThinAttributes` rather than a vector for attributes
attached to generics, since I expect almost all lifetime and types
parameters to not carry any attributes.
This commit is contained in:
Felix S. Klock II 2016-05-17 18:51:45 +02:00
parent f2c53ea66b
commit 4c37ad6607
8 changed files with 85 additions and 14 deletions

View file

@ -121,6 +121,7 @@ impl fmt::Debug for Lifetime {
/// A lifetime definition, e.g. `'a: 'b+'c+'d` /// A lifetime definition, e.g. `'a: 'b+'c+'d`
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct LifetimeDef { pub struct LifetimeDef {
pub attrs: ThinVec<Attribute>,
pub lifetime: Lifetime, pub lifetime: Lifetime,
pub bounds: Vec<Lifetime> pub bounds: Vec<Lifetime>
} }
@ -370,6 +371,7 @@ pub type TyParamBounds = P<[TyParamBound]>;
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct TyParam { pub struct TyParam {
pub attrs: ThinVec<Attribute>,
pub ident: Ident, pub ident: Ident,
pub id: NodeId, pub id: NodeId,
pub bounds: TyParamBounds, pub bounds: TyParamBounds,

View file

@ -73,6 +73,7 @@ pub trait AstBuilder {
fn typaram(&self, fn typaram(&self,
span: Span, span: Span,
id: ast::Ident, id: ast::Ident,
attrs: Vec<ast::Attribute>,
bounds: ast::TyParamBounds, bounds: ast::TyParamBounds,
default: Option<P<ast::Ty>>) -> ast::TyParam; default: Option<P<ast::Ty>>) -> ast::TyParam;
@ -83,6 +84,7 @@ pub trait AstBuilder {
fn lifetime_def(&self, fn lifetime_def(&self,
span: Span, span: Span,
name: ast::Name, name: ast::Name,
attrs: Vec<ast::Attribute>,
bounds: Vec<ast::Lifetime>) bounds: Vec<ast::Lifetime>)
-> ast::LifetimeDef; -> ast::LifetimeDef;
@ -452,11 +454,13 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
fn typaram(&self, fn typaram(&self,
span: Span, span: Span,
id: ast::Ident, id: ast::Ident,
attrs: Vec<ast::Attribute>,
bounds: ast::TyParamBounds, bounds: ast::TyParamBounds,
default: Option<P<ast::Ty>>) -> ast::TyParam { default: Option<P<ast::Ty>>) -> ast::TyParam {
ast::TyParam { ast::TyParam {
ident: id, ident: id,
id: ast::DUMMY_NODE_ID, id: ast::DUMMY_NODE_ID,
attrs: attrs.into(),
bounds: bounds, bounds: bounds,
default: default, default: default,
span: span span: span
@ -503,9 +507,11 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
fn lifetime_def(&self, fn lifetime_def(&self,
span: Span, span: Span,
name: ast::Name, name: ast::Name,
attrs: Vec<ast::Attribute>,
bounds: Vec<ast::Lifetime>) bounds: Vec<ast::Lifetime>)
-> ast::LifetimeDef { -> ast::LifetimeDef {
ast::LifetimeDef { ast::LifetimeDef {
attrs: attrs.into(),
lifetime: self.lifetime(span, name), lifetime: self.lifetime(span, name),
bounds: bounds bounds: bounds
} }

View file

@ -302,6 +302,9 @@ declare_features! (
// Used to identify the `compiler_builtins` crate // Used to identify the `compiler_builtins` crate
// rustc internal // rustc internal
(active, compiler_builtins, "1.13.0", None), (active, compiler_builtins, "1.13.0", None),
// Allows attributes on lifetime/type formal parameters in generics (RFC 1327)
(active, generic_param_attrs, "1.11.0", Some(34761)),
); );
declare_features! ( declare_features! (
@ -1208,6 +1211,24 @@ impl<'a> Visitor for PostExpansionVisitor<'a> {
visit::walk_vis(self, vis) visit::walk_vis(self, vis)
} }
fn visit_generics(&mut self, g: &ast::Generics) {
for t in &g.ty_params {
if !t.attrs.is_empty() {
gate_feature_post!(&self, generic_param_attrs, t.attrs[0].span,
"attributes on type parameter bindings are experimental");
}
}
visit::walk_generics(self, g)
}
fn visit_lifetime_def(&mut self, lifetime_def: &ast::LifetimeDef) {
if !lifetime_def.attrs.is_empty() {
gate_feature_post!(&self, generic_param_attrs, lifetime_def.attrs[0].span,
"attributes on lifetime bindings are experimental");
}
visit::walk_lifetime_def(self, lifetime_def)
}
} }
pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> Features { pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> Features {

View file

@ -662,8 +662,13 @@ pub fn noop_fold_ty_param_bound<T>(tpb: TyParamBound, fld: &mut T)
} }
pub fn noop_fold_ty_param<T: Folder>(tp: TyParam, fld: &mut T) -> TyParam { pub fn noop_fold_ty_param<T: Folder>(tp: TyParam, fld: &mut T) -> TyParam {
let TyParam {id, ident, bounds, default, span} = tp; let TyParam {attrs, id, ident, bounds, default, span} = tp;
let attrs: Vec<_> = attrs.into();
TyParam { TyParam {
attrs: attrs.into_iter()
.flat_map(|x| fld.fold_attribute(x).into_iter())
.collect::<Vec<_>>()
.into(),
id: fld.new_id(id), id: fld.new_id(id),
ident: ident, ident: ident,
bounds: fld.fold_bounds(bounds), bounds: fld.fold_bounds(bounds),
@ -687,7 +692,12 @@ pub fn noop_fold_lifetime<T: Folder>(l: Lifetime, fld: &mut T) -> Lifetime {
pub fn noop_fold_lifetime_def<T: Folder>(l: LifetimeDef, fld: &mut T) pub fn noop_fold_lifetime_def<T: Folder>(l: LifetimeDef, fld: &mut T)
-> LifetimeDef { -> LifetimeDef {
let attrs: Vec<_> = l.attrs.into();
LifetimeDef { LifetimeDef {
attrs: attrs.into_iter()
.flat_map(|x| fld.fold_attribute(x).into_iter())
.collect::<Vec<_>>()
.into(),
lifetime: fld.fold_lifetime(l.lifetime), lifetime: fld.fold_lifetime(l.lifetime),
bounds: fld.fold_lifetimes(l.bounds), bounds: fld.fold_lifetimes(l.bounds),
} }

View file

@ -1184,7 +1184,7 @@ impl<'a> Parser<'a> {
let lo = self.span.lo; let lo = self.span.lo;
let (name, node) = if self.eat_keyword(keywords::Type) { let (name, node) = if self.eat_keyword(keywords::Type) {
let TyParam {ident, bounds, default, ..} = self.parse_ty_param()?; let TyParam {ident, bounds, default, ..} = self.parse_ty_param(vec![])?;
self.expect(&token::Semi)?; self.expect(&token::Semi)?;
(ident, TraitItemKind::Type(bounds, default)) (ident, TraitItemKind::Type(bounds, default))
} else if self.is_const_item() { } else if self.is_const_item() {
@ -1923,10 +1923,22 @@ impl<'a> Parser<'a> {
/// Parses `lifetime_defs = [ lifetime_defs { ',' lifetime_defs } ]` where `lifetime_def = /// Parses `lifetime_defs = [ lifetime_defs { ',' lifetime_defs } ]` where `lifetime_def =
/// lifetime [':' lifetimes]` /// lifetime [':' lifetimes]`
pub fn parse_lifetime_defs(&mut self) -> PResult<'a, Vec<ast::LifetimeDef>> { ///
/// If `followed_by_ty_params` is None, then we are in a context
/// where only lifetime parameters are allowed, and thus we should
/// error if we encounter attributes after the bound lifetimes.
///
/// If `followed_by_ty_params` is Some(r), then there may be type
/// parameter bindings after the lifetimes, so we should pass
/// along the parsed attributes to be attached to the first such
/// type parmeter.
pub fn parse_lifetime_defs(&mut self,
followed_by_ty_params: Option<&mut Vec<ast::Attribute>>)
-> PResult<'a, Vec<ast::LifetimeDef>>
{
let mut res = Vec::new(); let mut res = Vec::new();
loop { loop {
let attrs = self.parse_outer_attributes()?;
match self.token { match self.token {
token::Lifetime(_) => { token::Lifetime(_) => {
let lifetime = self.parse_lifetime()?; let lifetime = self.parse_lifetime()?;
@ -1936,11 +1948,20 @@ impl<'a> Parser<'a> {
} else { } else {
Vec::new() Vec::new()
}; };
res.push(ast::LifetimeDef { lifetime: lifetime, res.push(ast::LifetimeDef { attrs: attrs.into(),
lifetime: lifetime,
bounds: bounds }); bounds: bounds });
} }
_ => { _ => {
if let Some(recv) = followed_by_ty_params {
assert!(recv.is_empty());
*recv = attrs;
} else {
let msg = "encountered trailing attributes after lifetime parameters";
return Err(self.fatal(msg));
}
debug!("parse_lifetime_defs ret {:?}", res);
return Ok(res); return Ok(res);
} }
} }
@ -4238,7 +4259,7 @@ impl<'a> Parser<'a> {
} }
/// Matches typaram = IDENT (`?` unbound)? optbounds ( EQ ty )? /// Matches typaram = IDENT (`?` unbound)? optbounds ( EQ ty )?
fn parse_ty_param(&mut self) -> PResult<'a, TyParam> { fn parse_ty_param(&mut self, preceding_attrs: Vec<ast::Attribute>) -> PResult<'a, TyParam> {
let span = self.span; let span = self.span;
let ident = self.parse_ident()?; let ident = self.parse_ident()?;
@ -4252,6 +4273,7 @@ impl<'a> Parser<'a> {
}; };
Ok(TyParam { Ok(TyParam {
attrs: preceding_attrs.into(),
ident: ident, ident: ident,
id: ast::DUMMY_NODE_ID, id: ast::DUMMY_NODE_ID,
bounds: bounds, bounds: bounds,
@ -4272,11 +4294,18 @@ impl<'a> Parser<'a> {
let span_lo = self.span.lo; let span_lo = self.span.lo;
if self.eat(&token::Lt) { if self.eat(&token::Lt) {
let lifetime_defs = self.parse_lifetime_defs()?; let mut attrs = vec![];
let lifetime_defs = self.parse_lifetime_defs(Some(&mut attrs))?;
let mut seen_default = false; let mut seen_default = false;
let mut post_lifetime_attrs = Some(attrs);
let ty_params = self.parse_seq_to_gt(Some(token::Comma), |p| { let ty_params = self.parse_seq_to_gt(Some(token::Comma), |p| {
p.forbid_lifetime()?; p.forbid_lifetime()?;
let ty_param = p.parse_ty_param()?; let attrs = match post_lifetime_attrs.as_mut() {
None => p.parse_outer_attributes()?,
Some(attrs) => mem::replace(attrs, vec![]),
};
post_lifetime_attrs = None;
let ty_param = p.parse_ty_param(attrs)?;
if ty_param.default.is_some() { if ty_param.default.is_some() {
seen_default = true; seen_default = true;
} else if seen_default { } else if seen_default {
@ -4433,7 +4462,7 @@ impl<'a> Parser<'a> {
let bound_lifetimes = if self.eat_keyword(keywords::For) { let bound_lifetimes = if self.eat_keyword(keywords::For) {
// Higher ranked constraint. // Higher ranked constraint.
self.expect(&token::Lt)?; self.expect(&token::Lt)?;
let lifetime_defs = self.parse_lifetime_defs()?; let lifetime_defs = self.parse_lifetime_defs(None)?;
self.expect_gt()?; self.expect_gt()?;
lifetime_defs lifetime_defs
} else { } else {
@ -5006,7 +5035,7 @@ impl<'a> Parser<'a> {
fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec<ast::LifetimeDef>> { fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec<ast::LifetimeDef>> {
if self.eat_keyword(keywords::For) { if self.eat_keyword(keywords::For) {
self.expect(&token::Lt)?; self.expect(&token::Lt)?;
let lifetime_defs = self.parse_lifetime_defs()?; let lifetime_defs = self.parse_lifetime_defs(None)?;
self.expect_gt()?; self.expect_gt()?;
Ok(lifetime_defs) Ok(lifetime_defs)
} else { } else {

View file

@ -201,6 +201,7 @@ pub fn walk_lifetime<V: Visitor>(visitor: &mut V, lifetime: &Lifetime) {
pub fn walk_lifetime_def<V: Visitor>(visitor: &mut V, lifetime_def: &LifetimeDef) { pub fn walk_lifetime_def<V: Visitor>(visitor: &mut V, lifetime_def: &LifetimeDef) {
visitor.visit_lifetime(&lifetime_def.lifetime); visitor.visit_lifetime(&lifetime_def.lifetime);
walk_list!(visitor, visit_lifetime, &lifetime_def.bounds); walk_list!(visitor, visit_lifetime, &lifetime_def.bounds);
walk_list!(visitor, visit_attribute, &*lifetime_def.attrs);
} }
pub fn walk_poly_trait_ref<V>(visitor: &mut V, trait_ref: &PolyTraitRef, _: &TraitBoundModifier) pub fn walk_poly_trait_ref<V>(visitor: &mut V, trait_ref: &PolyTraitRef, _: &TraitBoundModifier)
@ -474,6 +475,7 @@ pub fn walk_generics<V: Visitor>(visitor: &mut V, generics: &Generics) {
visitor.visit_ident(param.span, param.ident); visitor.visit_ident(param.span, param.ident);
walk_list!(visitor, visit_ty_param_bound, &param.bounds); walk_list!(visitor, visit_ty_param_bound, &param.bounds);
walk_list!(visitor, visit_ty, &param.default); walk_list!(visitor, visit_ty, &param.default);
walk_list!(visitor, visit_attribute, &*param.attrs);
} }
walk_list!(visitor, visit_lifetime_def, &generics.lifetimes); walk_list!(visitor, visit_lifetime_def, &generics.lifetimes);
for predicate in &generics.where_clause.predicates { for predicate in &generics.where_clause.predicates {

View file

@ -536,7 +536,7 @@ impl<'a> TraitDef<'a> {
bounds.push((*declared_bound).clone()); bounds.push((*declared_bound).clone());
} }
cx.typaram(self.span, ty_param.ident, P::from_vec(bounds), None) cx.typaram(self.span, ty_param.ident, vec![], P::from_vec(bounds), None)
})); }));
// and similarly for where clauses // and similarly for where clauses

View file

@ -194,6 +194,7 @@ impl<'a> Ty<'a> {
fn mk_ty_param(cx: &ExtCtxt, fn mk_ty_param(cx: &ExtCtxt,
span: Span, span: Span,
name: &str, name: &str,
attrs: &[ast::Attribute],
bounds: &[Path], bounds: &[Path],
self_ident: Ident, self_ident: Ident,
self_generics: &Generics) self_generics: &Generics)
@ -204,7 +205,7 @@ fn mk_ty_param(cx: &ExtCtxt,
cx.typarambound(path) cx.typarambound(path)
}) })
.collect(); .collect();
cx.typaram(span, cx.ident_of(name), bounds, None) cx.typaram(span, cx.ident_of(name), attrs.to_owned(), bounds, None)
} }
fn mk_generics(lifetimes: Vec<ast::LifetimeDef>, ty_params: Vec<ast::TyParam>, span: Span) fn mk_generics(lifetimes: Vec<ast::LifetimeDef>, ty_params: Vec<ast::TyParam>, span: Span)
@ -246,7 +247,7 @@ impl<'a> LifetimeBounds<'a> {
let bounds = bounds.iter() let bounds = bounds.iter()
.map(|b| cx.lifetime(span, cx.ident_of(*b).name)) .map(|b| cx.lifetime(span, cx.ident_of(*b).name))
.collect(); .collect();
cx.lifetime_def(span, cx.ident_of(*lt).name, bounds) cx.lifetime_def(span, cx.ident_of(*lt).name, vec![], bounds)
}) })
.collect(); .collect();
let ty_params = self.bounds let ty_params = self.bounds
@ -254,7 +255,7 @@ impl<'a> LifetimeBounds<'a> {
.map(|t| { .map(|t| {
match *t { match *t {
(ref name, ref bounds) => { (ref name, ref bounds) => {
mk_ty_param(cx, span, *name, bounds, self_ty, self_generics) mk_ty_param(cx, span, *name, &[], bounds, self_ty, self_generics)
} }
} }
}) })