Skip to content

Commit 4c37ad6

Browse files
committed
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.
1 parent f2c53ea commit 4c37ad6

File tree

8 files changed

+85
-14
lines changed

8 files changed

+85
-14
lines changed

src/libsyntax/ast.rs

+2
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ impl fmt::Debug for Lifetime {
121121
/// A lifetime definition, e.g. `'a: 'b+'c+'d`
122122
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
123123
pub struct LifetimeDef {
124+
pub attrs: ThinVec<Attribute>,
124125
pub lifetime: Lifetime,
125126
pub bounds: Vec<Lifetime>
126127
}
@@ -370,6 +371,7 @@ pub type TyParamBounds = P<[TyParamBound]>;
370371

371372
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
372373
pub struct TyParam {
374+
pub attrs: ThinVec<Attribute>,
373375
pub ident: Ident,
374376
pub id: NodeId,
375377
pub bounds: TyParamBounds,

src/libsyntax/ext/build.rs

+6
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub trait AstBuilder {
7373
fn typaram(&self,
7474
span: Span,
7575
id: ast::Ident,
76+
attrs: Vec<ast::Attribute>,
7677
bounds: ast::TyParamBounds,
7778
default: Option<P<ast::Ty>>) -> ast::TyParam;
7879

@@ -83,6 +84,7 @@ pub trait AstBuilder {
8384
fn lifetime_def(&self,
8485
span: Span,
8586
name: ast::Name,
87+
attrs: Vec<ast::Attribute>,
8688
bounds: Vec<ast::Lifetime>)
8789
-> ast::LifetimeDef;
8890

@@ -452,11 +454,13 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
452454
fn typaram(&self,
453455
span: Span,
454456
id: ast::Ident,
457+
attrs: Vec<ast::Attribute>,
455458
bounds: ast::TyParamBounds,
456459
default: Option<P<ast::Ty>>) -> ast::TyParam {
457460
ast::TyParam {
458461
ident: id,
459462
id: ast::DUMMY_NODE_ID,
463+
attrs: attrs.into(),
460464
bounds: bounds,
461465
default: default,
462466
span: span
@@ -503,9 +507,11 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
503507
fn lifetime_def(&self,
504508
span: Span,
505509
name: ast::Name,
510+
attrs: Vec<ast::Attribute>,
506511
bounds: Vec<ast::Lifetime>)
507512
-> ast::LifetimeDef {
508513
ast::LifetimeDef {
514+
attrs: attrs.into(),
509515
lifetime: self.lifetime(span, name),
510516
bounds: bounds
511517
}

src/libsyntax/feature_gate.rs

+21
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,9 @@ declare_features! (
302302
// Used to identify the `compiler_builtins` crate
303303
// rustc internal
304304
(active, compiler_builtins, "1.13.0", None),
305+
306+
// Allows attributes on lifetime/type formal parameters in generics (RFC 1327)
307+
(active, generic_param_attrs, "1.11.0", Some(34761)),
305308
);
306309

307310
declare_features! (
@@ -1208,6 +1211,24 @@ impl<'a> Visitor for PostExpansionVisitor<'a> {
12081211

12091212
visit::walk_vis(self, vis)
12101213
}
1214+
1215+
fn visit_generics(&mut self, g: &ast::Generics) {
1216+
for t in &g.ty_params {
1217+
if !t.attrs.is_empty() {
1218+
gate_feature_post!(&self, generic_param_attrs, t.attrs[0].span,
1219+
"attributes on type parameter bindings are experimental");
1220+
}
1221+
}
1222+
visit::walk_generics(self, g)
1223+
}
1224+
1225+
fn visit_lifetime_def(&mut self, lifetime_def: &ast::LifetimeDef) {
1226+
if !lifetime_def.attrs.is_empty() {
1227+
gate_feature_post!(&self, generic_param_attrs, lifetime_def.attrs[0].span,
1228+
"attributes on lifetime bindings are experimental");
1229+
}
1230+
visit::walk_lifetime_def(self, lifetime_def)
1231+
}
12111232
}
12121233

12131234
pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> Features {

src/libsyntax/fold.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -662,8 +662,13 @@ pub fn noop_fold_ty_param_bound<T>(tpb: TyParamBound, fld: &mut T)
662662
}
663663

664664
pub fn noop_fold_ty_param<T: Folder>(tp: TyParam, fld: &mut T) -> TyParam {
665-
let TyParam {id, ident, bounds, default, span} = tp;
665+
let TyParam {attrs, id, ident, bounds, default, span} = tp;
666+
let attrs: Vec<_> = attrs.into();
666667
TyParam {
668+
attrs: attrs.into_iter()
669+
.flat_map(|x| fld.fold_attribute(x).into_iter())
670+
.collect::<Vec<_>>()
671+
.into(),
667672
id: fld.new_id(id),
668673
ident: ident,
669674
bounds: fld.fold_bounds(bounds),
@@ -687,7 +692,12 @@ pub fn noop_fold_lifetime<T: Folder>(l: Lifetime, fld: &mut T) -> Lifetime {
687692

688693
pub fn noop_fold_lifetime_def<T: Folder>(l: LifetimeDef, fld: &mut T)
689694
-> LifetimeDef {
695+
let attrs: Vec<_> = l.attrs.into();
690696
LifetimeDef {
697+
attrs: attrs.into_iter()
698+
.flat_map(|x| fld.fold_attribute(x).into_iter())
699+
.collect::<Vec<_>>()
700+
.into(),
691701
lifetime: fld.fold_lifetime(l.lifetime),
692702
bounds: fld.fold_lifetimes(l.bounds),
693703
}

src/libsyntax/parse/parser.rs

+38-9
Original file line numberDiff line numberDiff line change
@@ -1184,7 +1184,7 @@ impl<'a> Parser<'a> {
11841184
let lo = self.span.lo;
11851185

11861186
let (name, node) = if self.eat_keyword(keywords::Type) {
1187-
let TyParam {ident, bounds, default, ..} = self.parse_ty_param()?;
1187+
let TyParam {ident, bounds, default, ..} = self.parse_ty_param(vec![])?;
11881188
self.expect(&token::Semi)?;
11891189
(ident, TraitItemKind::Type(bounds, default))
11901190
} else if self.is_const_item() {
@@ -1923,10 +1923,22 @@ impl<'a> Parser<'a> {
19231923

19241924
/// Parses `lifetime_defs = [ lifetime_defs { ',' lifetime_defs } ]` where `lifetime_def =
19251925
/// lifetime [':' lifetimes]`
1926-
pub fn parse_lifetime_defs(&mut self) -> PResult<'a, Vec<ast::LifetimeDef>> {
1927-
1926+
///
1927+
/// If `followed_by_ty_params` is None, then we are in a context
1928+
/// where only lifetime parameters are allowed, and thus we should
1929+
/// error if we encounter attributes after the bound lifetimes.
1930+
///
1931+
/// If `followed_by_ty_params` is Some(r), then there may be type
1932+
/// parameter bindings after the lifetimes, so we should pass
1933+
/// along the parsed attributes to be attached to the first such
1934+
/// type parmeter.
1935+
pub fn parse_lifetime_defs(&mut self,
1936+
followed_by_ty_params: Option<&mut Vec<ast::Attribute>>)
1937+
-> PResult<'a, Vec<ast::LifetimeDef>>
1938+
{
19281939
let mut res = Vec::new();
19291940
loop {
1941+
let attrs = self.parse_outer_attributes()?;
19301942
match self.token {
19311943
token::Lifetime(_) => {
19321944
let lifetime = self.parse_lifetime()?;
@@ -1936,11 +1948,20 @@ impl<'a> Parser<'a> {
19361948
} else {
19371949
Vec::new()
19381950
};
1939-
res.push(ast::LifetimeDef { lifetime: lifetime,
1951+
res.push(ast::LifetimeDef { attrs: attrs.into(),
1952+
lifetime: lifetime,
19401953
bounds: bounds });
19411954
}
19421955

19431956
_ => {
1957+
if let Some(recv) = followed_by_ty_params {
1958+
assert!(recv.is_empty());
1959+
*recv = attrs;
1960+
} else {
1961+
let msg = "encountered trailing attributes after lifetime parameters";
1962+
return Err(self.fatal(msg));
1963+
}
1964+
debug!("parse_lifetime_defs ret {:?}", res);
19441965
return Ok(res);
19451966
}
19461967
}
@@ -4238,7 +4259,7 @@ impl<'a> Parser<'a> {
42384259
}
42394260

42404261
/// Matches typaram = IDENT (`?` unbound)? optbounds ( EQ ty )?
4241-
fn parse_ty_param(&mut self) -> PResult<'a, TyParam> {
4262+
fn parse_ty_param(&mut self, preceding_attrs: Vec<ast::Attribute>) -> PResult<'a, TyParam> {
42424263
let span = self.span;
42434264
let ident = self.parse_ident()?;
42444265

@@ -4252,6 +4273,7 @@ impl<'a> Parser<'a> {
42524273
};
42534274

42544275
Ok(TyParam {
4276+
attrs: preceding_attrs.into(),
42554277
ident: ident,
42564278
id: ast::DUMMY_NODE_ID,
42574279
bounds: bounds,
@@ -4272,11 +4294,18 @@ impl<'a> Parser<'a> {
42724294
let span_lo = self.span.lo;
42734295

42744296
if self.eat(&token::Lt) {
4275-
let lifetime_defs = self.parse_lifetime_defs()?;
4297+
let mut attrs = vec![];
4298+
let lifetime_defs = self.parse_lifetime_defs(Some(&mut attrs))?;
42764299
let mut seen_default = false;
4300+
let mut post_lifetime_attrs = Some(attrs);
42774301
let ty_params = self.parse_seq_to_gt(Some(token::Comma), |p| {
42784302
p.forbid_lifetime()?;
4279-
let ty_param = p.parse_ty_param()?;
4303+
let attrs = match post_lifetime_attrs.as_mut() {
4304+
None => p.parse_outer_attributes()?,
4305+
Some(attrs) => mem::replace(attrs, vec![]),
4306+
};
4307+
post_lifetime_attrs = None;
4308+
let ty_param = p.parse_ty_param(attrs)?;
42804309
if ty_param.default.is_some() {
42814310
seen_default = true;
42824311
} else if seen_default {
@@ -4433,7 +4462,7 @@ impl<'a> Parser<'a> {
44334462
let bound_lifetimes = if self.eat_keyword(keywords::For) {
44344463
// Higher ranked constraint.
44354464
self.expect(&token::Lt)?;
4436-
let lifetime_defs = self.parse_lifetime_defs()?;
4465+
let lifetime_defs = self.parse_lifetime_defs(None)?;
44374466
self.expect_gt()?;
44384467
lifetime_defs
44394468
} else {
@@ -5006,7 +5035,7 @@ impl<'a> Parser<'a> {
50065035
fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec<ast::LifetimeDef>> {
50075036
if self.eat_keyword(keywords::For) {
50085037
self.expect(&token::Lt)?;
5009-
let lifetime_defs = self.parse_lifetime_defs()?;
5038+
let lifetime_defs = self.parse_lifetime_defs(None)?;
50105039
self.expect_gt()?;
50115040
Ok(lifetime_defs)
50125041
} else {

src/libsyntax/visit.rs

+2
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ pub fn walk_lifetime<V: Visitor>(visitor: &mut V, lifetime: &Lifetime) {
201201
pub fn walk_lifetime_def<V: Visitor>(visitor: &mut V, lifetime_def: &LifetimeDef) {
202202
visitor.visit_lifetime(&lifetime_def.lifetime);
203203
walk_list!(visitor, visit_lifetime, &lifetime_def.bounds);
204+
walk_list!(visitor, visit_attribute, &*lifetime_def.attrs);
204205
}
205206

206207
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) {
474475
visitor.visit_ident(param.span, param.ident);
475476
walk_list!(visitor, visit_ty_param_bound, &param.bounds);
476477
walk_list!(visitor, visit_ty, &param.default);
478+
walk_list!(visitor, visit_attribute, &*param.attrs);
477479
}
478480
walk_list!(visitor, visit_lifetime_def, &generics.lifetimes);
479481
for predicate in &generics.where_clause.predicates {

src/libsyntax_ext/deriving/generic/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ impl<'a> TraitDef<'a> {
536536
bounds.push((*declared_bound).clone());
537537
}
538538

539-
cx.typaram(self.span, ty_param.ident, P::from_vec(bounds), None)
539+
cx.typaram(self.span, ty_param.ident, vec![], P::from_vec(bounds), None)
540540
}));
541541

542542
// and similarly for where clauses

src/libsyntax_ext/deriving/generic/ty.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ impl<'a> Ty<'a> {
194194
fn mk_ty_param(cx: &ExtCtxt,
195195
span: Span,
196196
name: &str,
197+
attrs: &[ast::Attribute],
197198
bounds: &[Path],
198199
self_ident: Ident,
199200
self_generics: &Generics)
@@ -204,7 +205,7 @@ fn mk_ty_param(cx: &ExtCtxt,
204205
cx.typarambound(path)
205206
})
206207
.collect();
207-
cx.typaram(span, cx.ident_of(name), bounds, None)
208+
cx.typaram(span, cx.ident_of(name), attrs.to_owned(), bounds, None)
208209
}
209210

210211
fn mk_generics(lifetimes: Vec<ast::LifetimeDef>, ty_params: Vec<ast::TyParam>, span: Span)
@@ -246,15 +247,15 @@ impl<'a> LifetimeBounds<'a> {
246247
let bounds = bounds.iter()
247248
.map(|b| cx.lifetime(span, cx.ident_of(*b).name))
248249
.collect();
249-
cx.lifetime_def(span, cx.ident_of(*lt).name, bounds)
250+
cx.lifetime_def(span, cx.ident_of(*lt).name, vec![], bounds)
250251
})
251252
.collect();
252253
let ty_params = self.bounds
253254
.iter()
254255
.map(|t| {
255256
match *t {
256257
(ref name, ref bounds) => {
257-
mk_ty_param(cx, span, *name, bounds, self_ty, self_generics)
258+
mk_ty_param(cx, span, *name, &[], bounds, self_ty, self_generics)
258259
}
259260
}
260261
})

0 commit comments

Comments
 (0)