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

Attributes on generic formals #34764

Merged
merged 3 commits into from
Oct 1, 2016
Merged
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
2 changes: 2 additions & 0 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ impl fmt::Debug for Lifetime {
/// A lifetime definition, e.g. `'a: 'b+'c+'d`
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct LifetimeDef {
pub attrs: ThinVec<Attribute>,
pub lifetime: Lifetime,
pub bounds: Vec<Lifetime>
}
Expand Down Expand Up @@ -370,6 +371,7 @@ pub type TyParamBounds = P<[TyParamBound]>;

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct TyParam {
pub attrs: ThinVec<Attribute>,
pub ident: Ident,
pub id: NodeId,
pub bounds: TyParamBounds,
Expand Down
6 changes: 6 additions & 0 deletions src/libsyntax/ext/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ pub trait AstBuilder {
fn typaram(&self,
span: Span,
id: ast::Ident,
attrs: Vec<ast::Attribute>,
bounds: ast::TyParamBounds,
default: Option<P<ast::Ty>>) -> ast::TyParam;

Expand All @@ -83,6 +84,7 @@ pub trait AstBuilder {
fn lifetime_def(&self,
span: Span,
name: ast::Name,
attrs: Vec<ast::Attribute>,
bounds: Vec<ast::Lifetime>)
-> ast::LifetimeDef;

Expand Down Expand Up @@ -452,11 +454,13 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
fn typaram(&self,
span: Span,
id: ast::Ident,
attrs: Vec<ast::Attribute>,
bounds: ast::TyParamBounds,
default: Option<P<ast::Ty>>) -> ast::TyParam {
ast::TyParam {
ident: id,
id: ast::DUMMY_NODE_ID,
attrs: attrs.into(),
bounds: bounds,
default: default,
span: span
Expand Down Expand Up @@ -503,9 +507,11 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
fn lifetime_def(&self,
span: Span,
name: ast::Name,
attrs: Vec<ast::Attribute>,
bounds: Vec<ast::Lifetime>)
-> ast::LifetimeDef {
ast::LifetimeDef {
attrs: attrs.into(),
lifetime: self.lifetime(span, name),
bounds: bounds
}
Expand Down
21 changes: 21 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,9 @@ declare_features! (
// Used to identify the `compiler_builtins` crate
// rustc internal
(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! (
Expand Down Expand Up @@ -1208,6 +1211,24 @@ impl<'a> Visitor for PostExpansionVisitor<'a> {

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 {
Expand Down
12 changes: 11 additions & 1 deletion src/libsyntax/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
let TyParam {id, ident, bounds, default, span} = tp;
let TyParam {attrs, id, ident, bounds, default, span} = tp;
let attrs: Vec<_> = attrs.into();
TyParam {
attrs: attrs.into_iter()
.flat_map(|x| fld.fold_attribute(x).into_iter())
.collect::<Vec<_>>()
.into(),
id: fld.new_id(id),
ident: ident,
bounds: fld.fold_bounds(bounds),
Expand All @@ -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)
-> LifetimeDef {
let attrs: Vec<_> = l.attrs.into();
LifetimeDef {
attrs: attrs.into_iter()
.flat_map(|x| fld.fold_attribute(x).into_iter())
.collect::<Vec<_>>()
.into(),
lifetime: fld.fold_lifetime(l.lifetime),
bounds: fld.fold_lifetimes(l.bounds),
}
Expand Down
62 changes: 53 additions & 9 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1184,7 +1184,7 @@ impl<'a> Parser<'a> {
let lo = self.span.lo;

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)?;
(ident, TraitItemKind::Type(bounds, default))
} else if self.is_const_item() {
Expand Down Expand Up @@ -1923,10 +1923,22 @@ impl<'a> Parser<'a> {

/// Parses `lifetime_defs = [ lifetime_defs { ',' lifetime_defs } ]` where `lifetime_def =
/// 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();
loop {
let attrs = self.parse_outer_attributes()?;
match self.token {
token::Lifetime(_) => {
let lifetime = self.parse_lifetime()?;
Expand All @@ -1936,11 +1948,20 @@ impl<'a> Parser<'a> {
} else {
Vec::new()
};
res.push(ast::LifetimeDef { lifetime: lifetime,
res.push(ast::LifetimeDef { attrs: attrs.into(),
lifetime: lifetime,
bounds: bounds });
}

_ => {
if let Some(recv) = followed_by_ty_params {
assert!(recv.is_empty());
*recv = attrs;
} else {
let msg = "trailing attribute after lifetime parameters";
return Err(self.fatal(msg));
}
debug!("parse_lifetime_defs ret {:?}", res);
return Ok(res);
}
}
Expand Down Expand Up @@ -4238,7 +4259,7 @@ impl<'a> Parser<'a> {
}

/// 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 ident = self.parse_ident()?;

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

Ok(TyParam {
attrs: preceding_attrs.into(),
ident: ident,
id: ast::DUMMY_NODE_ID,
bounds: bounds,
Expand All @@ -4272,11 +4294,27 @@ impl<'a> Parser<'a> {
let span_lo = self.span.lo;

if self.eat(&token::Lt) {
let lifetime_defs = self.parse_lifetime_defs()?;
// Upon encountering attribute in generics list, we do not
// know if it is attached to lifetime or to type param.
//
// Solution: 1. eagerly parse attributes in tandem with
// lifetime defs, 2. store last set of parsed (and unused)
// attributes in `attrs`, and 3. pass in those attributes
// when parsing formal type param after lifetime defs.
let mut attrs = vec![];
let lifetime_defs = self.parse_lifetime_defs(Some(&mut attrs))?;
let mut seen_default = false;
let mut post_lifetime_attrs = Some(attrs);
let ty_params = self.parse_seq_to_gt(Some(token::Comma), |p| {
p.forbid_lifetime()?;
let ty_param = p.parse_ty_param()?;
// Move out of `post_lifetime_attrs` if present. O/w
// not first type param: parse attributes anew.
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() {
seen_default = true;
} else if seen_default {
Expand All @@ -4286,6 +4324,12 @@ impl<'a> Parser<'a> {
}
Ok(ty_param)
})?;
if let Some(attrs) = post_lifetime_attrs {
if !attrs.is_empty() {
self.span_err(attrs[0].span,
"trailing attribute after lifetime parameters");
}
}
Ok(ast::Generics {
lifetimes: lifetime_defs,
ty_params: ty_params,
Expand Down Expand Up @@ -4433,7 +4477,7 @@ impl<'a> Parser<'a> {
let bound_lifetimes = if self.eat_keyword(keywords::For) {
// Higher ranked constraint.
self.expect(&token::Lt)?;
let lifetime_defs = self.parse_lifetime_defs()?;
let lifetime_defs = self.parse_lifetime_defs(None)?;
self.expect_gt()?;
lifetime_defs
} else {
Expand Down Expand Up @@ -5006,7 +5050,7 @@ impl<'a> Parser<'a> {
fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec<ast::LifetimeDef>> {
if self.eat_keyword(keywords::For) {
self.expect(&token::Lt)?;
let lifetime_defs = self.parse_lifetime_defs()?;
let lifetime_defs = self.parse_lifetime_defs(None)?;
self.expect_gt()?;
Ok(lifetime_defs)
} else {
Expand Down
2 changes: 2 additions & 0 deletions src/libsyntax/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
visitor.visit_lifetime(&lifetime_def.lifetime);
walk_list!(visitor, visit_lifetime, &lifetime_def.bounds);
walk_list!(visitor, visit_attribute, &*lifetime_def.attrs);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why deref here, but not with the bounds?

Copy link
Member Author

Choose a reason for hiding this comment

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

Its necessary (here and below) because &bounds is a &Vec while &attrs is a &ThinVec.

Right now, you cannot iterate over a &ThinVec directly; you need to deref the ThinVec to turn it into a [T] slice that you can then iterate over.

}

pub fn walk_poly_trait_ref<V>(visitor: &mut V, trait_ref: &PolyTraitRef, _: &TraitBoundModifier)
Expand Down Expand Up @@ -474,6 +475,7 @@ pub fn walk_generics<V: Visitor>(visitor: &mut V, generics: &Generics) {
visitor.visit_ident(param.span, param.ident);
walk_list!(visitor, visit_ty_param_bound, &param.bounds);
walk_list!(visitor, visit_ty, &param.default);
walk_list!(visitor, visit_attribute, &*param.attrs);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why deref here, but not with the bounds?

}
walk_list!(visitor, visit_lifetime_def, &generics.lifetimes);
for predicate in &generics.where_clause.predicates {
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax_ext/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ impl<'a> TraitDef<'a> {
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
Expand Down
7 changes: 4 additions & 3 deletions src/libsyntax_ext/deriving/generic/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ impl<'a> Ty<'a> {
fn mk_ty_param(cx: &ExtCtxt,
span: Span,
name: &str,
attrs: &[ast::Attribute],
bounds: &[Path],
self_ident: Ident,
self_generics: &Generics)
Expand All @@ -204,7 +205,7 @@ fn mk_ty_param(cx: &ExtCtxt,
cx.typarambound(path)
})
.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)
Expand Down Expand Up @@ -246,15 +247,15 @@ impl<'a> LifetimeBounds<'a> {
let bounds = bounds.iter()
.map(|b| cx.lifetime(span, cx.ident_of(*b).name))
.collect();
cx.lifetime_def(span, cx.ident_of(*lt).name, bounds)
cx.lifetime_def(span, cx.ident_of(*lt).name, vec![], bounds)
})
.collect();
let ty_params = self.bounds
.iter()
.map(|t| {
match *t {
(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)
}
}
})
Expand Down
75 changes: 75 additions & 0 deletions src/test/compile-fail/attr-on-generic-formals-are-visited.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2016 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.

// This test ensures that attributes on formals in generic parameter
// lists are included when we are checking for unstable attributes.
//
// Note that feature(generic_param_attrs) *is* enabled here. We are
// checking feature-gating of the attributes themselves, not the
// capability to parse such attributes in that context.

#![feature(generic_param_attrs)]
#![allow(dead_code)]

struct StLt<#[lt_struct] 'a>(&'a u32);
//~^ ERROR The attribute `lt_struct` is currently unknown to the compiler
struct StTy<#[ty_struct] I>(I);
//~^ ERROR The attribute `ty_struct` is currently unknown to the compiler

enum EnLt<#[lt_enum] 'b> { A(&'b u32), B }
//~^ ERROR The attribute `lt_enum` is currently unknown to the compiler
enum EnTy<#[ty_enum] J> { A(J), B }
//~^ ERROR The attribute `ty_enum` is currently unknown to the compiler

trait TrLt<#[lt_trait] 'c> { fn foo(&self, _: &'c [u32]) -> &'c u32; }
//~^ ERROR The attribute `lt_trait` is currently unknown to the compiler
trait TrTy<#[ty_trait] K> { fn foo(&self, _: K); }
//~^ ERROR The attribute `ty_trait` is currently unknown to the compiler

type TyLt<#[lt_type] 'd> = &'d u32;
//~^ ERROR The attribute `lt_type` is currently unknown to the compiler
type TyTy<#[ty_type] L> = (L, );
//~^ ERROR The attribute `ty_type` is currently unknown to the compiler

impl<#[lt_inherent] 'e> StLt<'e> { }
//~^ ERROR The attribute `lt_inherent` is currently unknown to the compiler
impl<#[ty_inherent] M> StTy<M> { }
//~^ ERROR The attribute `ty_inherent` is currently unknown to the compiler

impl<#[lt_impl_for] 'f> TrLt<'f> for StLt<'f> {
//~^ ERROR The attribute `lt_impl_for` is currently unknown to the compiler
fn foo(&self, _: &'f [u32]) -> &'f u32 { loop { } }
}
impl<#[ty_impl_for] N> TrTy<N> for StTy<N> {
//~^ ERROR The attribute `ty_impl_for` is currently unknown to the compiler
fn foo(&self, _: N) { }
}

fn f_lt<#[lt_fn] 'g>(_: &'g [u32]) -> &'g u32 { loop { } }
//~^ ERROR The attribute `lt_fn` is currently unknown to the compiler
fn f_ty<#[ty_fn] O>(_: O) { }
//~^ ERROR The attribute `ty_fn` is currently unknown to the compiler

impl<I> StTy<I> {
fn m_lt<#[lt_meth] 'h>(_: &'h [u32]) -> &'h u32 { loop { } }
//~^ ERROR The attribute `lt_meth` is currently unknown to the compiler
fn m_ty<#[ty_meth] P>(_: P) { }
//~^ ERROR The attribute `ty_meth` is currently unknown to the compiler
}

fn hof_lt<Q>(_: Q)
where Q: for <#[lt_hof] 'i> Fn(&'i [u32]) -> &'i u32
//~^ ERROR The attribute `lt_hof` is currently unknown to the compiler
{
}

fn main() {

}
Loading