Skip to content

Commit e357178

Browse files
committed
Auto merge of #38814 - Ralith:cfg-fields, r=jseyfried
syntax: enable attributes and cfg on struct fields This enables conditional compilation of field initializers in a struct literal, simplifying construction of structs whose fields are themselves conditionally present. For example, the intializer for the constant in the following becomes legal, and has the intuitive effect: ```rust struct Foo { #[cfg(unix)] bar: (), } const FOO: Foo = Foo { #[cfg(unix)] bar: (), }; ``` It's not clear to me whether this calls for the full RFC process, but the implementation was simple enough that I figured I'd begin the conversation with code.
2 parents 27b9e6d + 7972c19 commit e357178

File tree

12 files changed

+164
-19
lines changed

12 files changed

+164
-19
lines changed

src/libsyntax/ast.rs

+2
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,7 @@ pub struct FieldPat {
538538
/// The pattern the field is destructured to
539539
pub pat: P<Pat>,
540540
pub is_shorthand: bool,
541+
pub attrs: ThinVec<Attribute>,
541542
}
542543

543544
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
@@ -815,6 +816,7 @@ pub struct Field {
815816
pub expr: P<Expr>,
816817
pub span: Span,
817818
pub is_shorthand: bool,
819+
pub attrs: ThinVec<Attribute>,
818820
}
819821

820822
pub type SpannedIdent = Spanned<Ident>;

src/libsyntax/attr.rs

+22-10
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use ast;
1818
use ast::{AttrId, Attribute, Name};
1919
use ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind};
2020
use ast::{Lit, Expr, Item, Local, Stmt, StmtKind};
21-
use codemap::{spanned, dummy_spanned, mk_sp};
21+
use codemap::{Spanned, spanned, dummy_spanned, mk_sp};
2222
use syntax_pos::{Span, BytePos, DUMMY_SP};
2323
use errors::Handler;
2424
use feature_gate::{Features, GatedCfg};
@@ -959,6 +959,13 @@ pub trait HasAttrs: Sized {
959959
fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self;
960960
}
961961

962+
impl<T: HasAttrs> HasAttrs for Spanned<T> {
963+
fn attrs(&self) -> &[ast::Attribute] { self.node.attrs() }
964+
fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self {
965+
Spanned { node: self.node.map_attrs(f), span: self.span }
966+
}
967+
}
968+
962969
impl HasAttrs for Vec<Attribute> {
963970
fn attrs(&self) -> &[Attribute] {
964971
&self
@@ -1012,26 +1019,31 @@ impl HasAttrs for StmtKind {
10121019
}
10131020
}
10141021

1015-
macro_rules! derive_has_attrs_from_field {
1016-
($($ty:path),*) => { derive_has_attrs_from_field!($($ty: .attrs),*); };
1017-
($($ty:path : $(.$field:ident)*),*) => { $(
1022+
impl HasAttrs for Stmt {
1023+
fn attrs(&self) -> &[ast::Attribute] { self.node.attrs() }
1024+
fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self {
1025+
Stmt { id: self.id, node: self.node.map_attrs(f), span: self.span }
1026+
}
1027+
}
1028+
1029+
macro_rules! derive_has_attrs {
1030+
($($ty:path),*) => { $(
10181031
impl HasAttrs for $ty {
10191032
fn attrs(&self) -> &[Attribute] {
1020-
self $(.$field)* .attrs()
1033+
&self.attrs
10211034
}
10221035

10231036
fn map_attrs<F>(mut self, f: F) -> Self
10241037
where F: FnOnce(Vec<Attribute>) -> Vec<Attribute>,
10251038
{
1026-
self $(.$field)* = self $(.$field)* .map_attrs(f);
1039+
self.attrs = self.attrs.map_attrs(f);
10271040
self
10281041
}
10291042
}
10301043
)* }
10311044
}
10321045

1033-
derive_has_attrs_from_field! {
1034-
Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, ast::Arm
1046+
derive_has_attrs! {
1047+
Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, ast::Arm,
1048+
ast::Field, ast::FieldPat, ast::Variant_
10351049
}
1036-
1037-
derive_has_attrs_from_field! { Stmt: .node, ast::Variant: .node.attrs }

src/libsyntax/config.rs

+64-5
Original file line numberDiff line numberDiff line change
@@ -221,11 +221,21 @@ impl<'a> StripUnconfigured<'a> {
221221
}
222222

223223
pub fn configure_expr_kind(&mut self, expr_kind: ast::ExprKind) -> ast::ExprKind {
224-
if let ast::ExprKind::Match(m, arms) = expr_kind {
225-
let arms = arms.into_iter().filter_map(|a| self.configure(a)).collect();
226-
ast::ExprKind::Match(m, arms)
227-
} else {
228-
expr_kind
224+
match expr_kind {
225+
ast::ExprKind::Match(m, arms) => {
226+
let arms = arms.into_iter().filter_map(|a| self.configure(a)).collect();
227+
ast::ExprKind::Match(m, arms)
228+
}
229+
ast::ExprKind::Struct(path, fields, base) => {
230+
let fields = fields.into_iter()
231+
.filter_map(|field| {
232+
self.visit_struct_field_attrs(field.attrs());
233+
self.configure(field)
234+
})
235+
.collect();
236+
ast::ExprKind::Struct(path, fields, base)
237+
}
238+
_ => expr_kind,
229239
}
230240
}
231241

@@ -250,6 +260,51 @@ impl<'a> StripUnconfigured<'a> {
250260
pub fn configure_stmt(&mut self, stmt: ast::Stmt) -> Option<ast::Stmt> {
251261
self.configure(stmt)
252262
}
263+
264+
pub fn configure_struct_expr_field(&mut self, field: ast::Field) -> Option<ast::Field> {
265+
if !self.features.map(|features| features.struct_field_attributes).unwrap_or(true) {
266+
if !field.attrs.is_empty() {
267+
let mut err = feature_err(&self.sess,
268+
"struct_field_attributes",
269+
field.span,
270+
GateIssue::Language,
271+
"attributes on struct literal fields are unstable");
272+
err.emit();
273+
}
274+
}
275+
276+
self.configure(field)
277+
}
278+
279+
pub fn configure_pat(&mut self, pattern: P<ast::Pat>) -> P<ast::Pat> {
280+
pattern.map(|mut pattern| {
281+
if let ast::PatKind::Struct(path, fields, etc) = pattern.node {
282+
let fields = fields.into_iter()
283+
.filter_map(|field| {
284+
self.visit_struct_field_attrs(field.attrs());
285+
self.configure(field)
286+
})
287+
.collect();
288+
pattern.node = ast::PatKind::Struct(path, fields, etc);
289+
}
290+
pattern
291+
})
292+
}
293+
294+
fn visit_struct_field_attrs(&mut self, attrs: &[ast::Attribute]) {
295+
// flag the offending attributes
296+
for attr in attrs.iter() {
297+
if !self.features.map(|features| features.struct_field_attributes).unwrap_or(true) {
298+
let mut err = feature_err(
299+
&self.sess,
300+
"struct_field_attributes",
301+
attr.span,
302+
GateIssue::Language,
303+
"attributes on struct pattern or literal fields are unstable");
304+
err.emit();
305+
}
306+
}
307+
}
253308
}
254309

255310
impl<'a> fold::Folder for StripUnconfigured<'a> {
@@ -299,6 +354,10 @@ impl<'a> fold::Folder for StripUnconfigured<'a> {
299354
// Interpolated AST will get configured once the surrounding tokens are parsed.
300355
mac
301356
}
357+
358+
fn fold_pat(&mut self, pattern: P<ast::Pat>) -> P<ast::Pat> {
359+
fold::noop_fold_pat(self.configure_pat(pattern), self)
360+
}
302361
}
303362

304363
fn is_cfg(attr: &ast::Attribute) -> bool {

src/libsyntax/ext/build.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,13 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
699699
self.expr(b.span, ast::ExprKind::Block(b))
700700
}
701701
fn field_imm(&self, span: Span, name: Ident, e: P<ast::Expr>) -> ast::Field {
702-
ast::Field { ident: respan(span, name), expr: e, span: span, is_shorthand: false }
702+
ast::Field {
703+
ident: respan(span, name),
704+
expr: e,
705+
span: span,
706+
is_shorthand: false,
707+
attrs: ast::ThinVec::new(),
708+
}
703709
}
704710
fn expr_struct(&self, span: Span, path: ast::Path, fields: Vec<ast::Field>) -> P<ast::Expr> {
705711
self.expr(span, ast::ExprKind::Struct(path, fields, None))

src/libsyntax/ext/expand.rs

+1
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
679679
}
680680

681681
fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
682+
let pat = self.cfg.configure_pat(pat);
682683
match pat.node {
683684
PatKind::Mac(_) => {}
684685
_ => return noop_fold_pat(pat, self),

src/libsyntax/feature_gate.rs

+3
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,9 @@ declare_features! (
324324

325325
// The `unadjusted` ABI. Perma unstable.
326326
(active, abi_unadjusted, "1.16.0", None),
327+
328+
// Allows attributes on struct literal fields.
329+
(active, struct_field_attributes, "1.16.0", Some(38814)),
327330
);
328331

329332
declare_features! (

src/libsyntax/fold.rs

+2
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,7 @@ pub fn noop_fold_field<T: Folder>(f: Field, folder: &mut T) -> Field {
830830
expr: folder.fold_expr(f.expr),
831831
span: folder.new_span(f.span),
832832
is_shorthand: f.is_shorthand,
833+
attrs: fold_thin_attrs(f.attrs, folder),
833834
}
834835
}
835836

@@ -1089,6 +1090,7 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
10891090
ident: folder.fold_ident(f.node.ident),
10901091
pat: folder.fold_pat(f.node.pat),
10911092
is_shorthand: f.node.is_shorthand,
1093+
attrs: fold_attrs(f.node.attrs.into(), folder).into()
10921094
}}
10931095
});
10941096
PatKind::Struct(pth, fs, etc)

src/libsyntax/parse/parser.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -1946,6 +1946,7 @@ impl<'a> Parser<'a> {
19461946

19471947
/// Parse ident (COLON expr)?
19481948
pub fn parse_field(&mut self) -> PResult<'a, Field> {
1949+
let attrs = self.parse_outer_attributes()?;
19491950
let lo = self.span.lo;
19501951
let hi;
19511952

@@ -1968,6 +1969,7 @@ impl<'a> Parser<'a> {
19681969
span: mk_sp(lo, expr.span.hi),
19691970
expr: expr,
19701971
is_shorthand: is_shorthand,
1972+
attrs: attrs.into(),
19711973
})
19721974
}
19731975

@@ -3436,6 +3438,7 @@ impl<'a> Parser<'a> {
34363438
if self.check(&token::CloseDelim(token::Brace)) { break }
34373439
}
34383440

3441+
let attrs = self.parse_outer_attributes()?;
34393442
let lo = self.span.lo;
34403443
let hi;
34413444

@@ -3493,9 +3496,13 @@ impl<'a> Parser<'a> {
34933496
};
34943497

34953498
fields.push(codemap::Spanned { span: mk_sp(lo, hi),
3496-
node: ast::FieldPat { ident: fieldname,
3497-
pat: subpat,
3498-
is_shorthand: is_shorthand }});
3499+
node: ast::FieldPat {
3500+
ident: fieldname,
3501+
pat: subpat,
3502+
is_shorthand: is_shorthand,
3503+
attrs: attrs.into(),
3504+
}
3505+
});
34993506
}
35003507
return Ok((fields, etc));
35013508
}

src/libsyntax/visit.rs

+2
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
427427
PatKind::Struct(ref path, ref fields, _) => {
428428
visitor.visit_path(path, pattern.id);
429429
for field in fields {
430+
walk_list!(visitor, visit_attribute, field.node.attrs.iter());
430431
visitor.visit_ident(field.span, field.node.ident);
431432
visitor.visit_pat(&field.node.pat)
432433
}
@@ -659,6 +660,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
659660
ExprKind::Struct(ref path, ref fields, ref optional_base) => {
660661
visitor.visit_path(path, expression.id);
661662
for field in fields {
663+
walk_list!(visitor, visit_attribute, field.attrs.iter());
662664
visitor.visit_ident(field.ident.span, field.ident.node);
663665
visitor.visit_expr(&field.expr)
664666
}

src/libsyntax_ext/deriving/generic/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1550,6 +1550,7 @@ impl<'a> TraitDef<'a> {
15501550
ident: ident.unwrap(),
15511551
pat: pat,
15521552
is_shorthand: false,
1553+
attrs: ast::ThinVec::new(),
15531554
},
15541555
}
15551556
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct Foo {
12+
present: (),
13+
}
14+
15+
fn main() {
16+
let foo = Foo { #[cfg(all())] present: () };
17+
//~^ ERROR attributes on struct pattern or literal fields are unstable
18+
let Foo { #[cfg(all())] present: () } = foo;
19+
//~^ ERROR attributes on struct pattern or literal fields are unstable
20+
}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(struct_field_attributes)]
12+
13+
struct Foo {
14+
present: (),
15+
}
16+
17+
fn main() {
18+
let foo = Foo { #[cfg(all())] present: () };
19+
let _ = Foo { #[cfg(any())] present: () };
20+
//~^ ERROR missing field `present` in initializer of `Foo`
21+
let _ = Foo { present: (), #[cfg(any())] absent: () };
22+
let _ = Foo { present: (), #[cfg(all())] absent: () };
23+
//~^ ERROR struct `Foo` has no field named `absent`
24+
let Foo { #[cfg(all())] present: () } = foo;
25+
let Foo { #[cfg(any())] present: () } = foo;
26+
//~^ ERROR pattern does not mention field `present`
27+
let Foo { present: (), #[cfg(any())] absent: () } = foo;
28+
let Foo { present: (), #[cfg(all())] absent: () } = foo;
29+
//~^ ERROR struct `Foo` does not have a field named `absent`
30+
}

0 commit comments

Comments
 (0)