about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorSergio Benitez <sb@sergio.bz>2016-08-19 18:58:14 -0700
committerSergio Benitez <sb@sergio.bz>2016-08-25 13:25:22 -0700
commit8250a26b5bcea9190ac63e756c35d8a54bf9da0c (patch)
treef95baa037c52c307de53d605ad752afd4880ffa4 /src/libsyntax
parent528c6f3ed6a23a374dc5a40582d1bea2f2cfda65 (diff)
downloadrust-8250a26b5bcea9190ac63e756c35d8a54bf9da0c.tar.gz
rust-8250a26b5bcea9190ac63e756c35d8a54bf9da0c.zip
Implement RFC#1559: allow all literals in attributes.
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ast.rs58
-rw-r--r--src/libsyntax/attr.rs330
-rw-r--r--src/libsyntax/config.rs37
-rw-r--r--src/libsyntax/diagnostic_list.rs18
-rw-r--r--src/libsyntax/ext/build.rs13
-rw-r--r--src/libsyntax/feature_gate.rs51
-rw-r--r--src/libsyntax/fold.rs19
-rw-r--r--src/libsyntax/parse/attr.rs64
-rw-r--r--src/libsyntax/print/pprust.rs19
-rw-r--r--src/libsyntax/test.rs9
10 files changed, 459 insertions, 159 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index f8a5cb0b04a..63fd2e7686f 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -439,6 +439,22 @@ pub struct Crate {
     pub exported_macros: Vec<MacroDef>,
 }
 
+/// A spanned compile-time attribute list item.
+pub type NestedMetaItem = Spanned<NestedMetaItemKind>;
+
+/// Possible values inside of compile-time attribute lists.
+///
+/// E.g. the '..' in `#[name(..)]`.
+#[derive(Clone, Eq, RustcEncodable, RustcDecodable, Hash, Debug, PartialEq)]
+pub enum NestedMetaItemKind {
+    /// A full MetaItem, for recursive meta items.
+    MetaItem(P<MetaItem>),
+    /// A literal.
+    ///
+    /// E.g. "foo", 64, true
+    Literal(Lit),
+}
+
 /// A spanned compile-time attribute item.
 ///
 /// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
@@ -456,7 +472,7 @@ pub enum MetaItemKind {
     /// List meta item.
     ///
     /// E.g. `derive(..)` as in `#[derive(..)]`
-    List(InternedString, Vec<P<MetaItem>>),
+    List(InternedString, Vec<NestedMetaItem>),
     /// Name value meta item.
     ///
     /// E.g. `feature = "foo"` as in `#[feature = "foo"]`
@@ -472,19 +488,21 @@ impl PartialEq for MetaItemKind {
                 Word(ref no) => (*ns) == (*no),
                 _ => false
             },
+            List(ref ns, ref miss) => match *other {
+                List(ref no, ref miso) => {
+                    ns == no &&
+                        miss.iter().all(|mi| {
+                            miso.iter().any(|x| x.node == mi.node)
+                        })
+                }
+                _ => false
+            },
             NameValue(ref ns, ref vs) => match *other {
                 NameValue(ref no, ref vo) => {
                     (*ns) == (*no) && vs.node == vo.node
                 }
                 _ => false
             },
-            List(ref ns, ref miss) => match *other {
-                List(ref no, ref miso) => {
-                    ns == no &&
-                        miss.iter().all(|mi| miso.iter().any(|x| x.node == mi.node))
-                }
-                _ => false
-            }
         }
     }
 }
@@ -1105,6 +1123,30 @@ impl LitKind {
             _ => false,
         }
     }
+
+    /// Returns true if this literal has no suffix. Note: this will return true
+    /// for literals with prefixes such as raw strings and byte strings.
+    pub fn is_unsuffixed(&self) -> bool {
+        match *self {
+            // unsuffixed variants
+            LitKind::Str(..) => true,
+            LitKind::ByteStr(..) => true,
+            LitKind::Byte(..) => true,
+            LitKind::Char(..) => true,
+            LitKind::Int(_, LitIntType::Unsuffixed) => true,
+            LitKind::FloatUnsuffixed(..) => true,
+            LitKind::Bool(..) => true,
+            // suffixed variants
+            LitKind::Int(_, LitIntType::Signed(..)) => false,
+            LitKind::Int(_, LitIntType::Unsigned(..)) => false,
+            LitKind::Float(..) => false,
+        }
+    }
+
+    /// Returns true if this literal has a suffix.
+    pub fn is_suffixed(&self) -> bool {
+        !self.is_unsuffixed()
+    }
 }
 
 // NB: If you change this, you'll probably want to change the corresponding
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index b622f6861b3..4897425f2c0 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -15,9 +15,10 @@ pub use self::ReprAttr::*;
 pub use self::IntType::*;
 
 use ast;
-use ast::{AttrId, Attribute, Attribute_, MetaItem, MetaItemKind};
-use ast::{Expr, Item, Local, Stmt, StmtKind};
-use codemap::{respan, spanned, dummy_spanned, Spanned};
+use ast::{AttrId, Attribute, Attribute_};
+use ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind};
+use ast::{Lit, Expr, Item, Local, Stmt, StmtKind};
+use codemap::{respan, spanned, dummy_spanned};
 use syntax_pos::{Span, BytePos, DUMMY_SP};
 use errors::Handler;
 use feature_gate::{Features, GatedCfg};
@@ -40,6 +41,7 @@ enum AttrError {
     MissingSince,
     MissingFeature,
     MultipleStabilityLevels,
+    UnsupportedLiteral
 }
 
 fn handle_errors(diag: &Handler, span: Span, error: AttrError) {
@@ -52,10 +54,12 @@ fn handle_errors(diag: &Handler, span: Span, error: AttrError) {
         AttrError::MissingFeature => span_err!(diag, span, E0546, "missing 'feature'"),
         AttrError::MultipleStabilityLevels => span_err!(diag, span, E0544,
                                                         "multiple stability levels"),
+        AttrError::UnsupportedLiteral => span_err!(diag, span, E0565, "unsupported literal"),
     }
 }
 
 pub fn mark_used(attr: &Attribute) {
+    debug!("Marking {:?} as used.", attr);
     let AttrId(id) = attr.node.id;
     USED_ATTRS.with(|slot| {
         let idx = (id / 64) as usize;
@@ -77,6 +81,93 @@ pub fn is_used(attr: &Attribute) -> bool {
     })
 }
 
+pub trait AttrNestedMetaItemMethods {
+    /// Returns true if this list item is a MetaItem with a name of `name`.
+    fn check_name(&self, name: &str) -> bool {
+        self.meta_item().map_or(false, |meta_item| meta_item.check_name(name))
+    }
+
+    /// Returns the name of the meta item, e.g. `foo` in `#[foo]`,
+    /// `#[foo="bar"]` and `#[foo(bar)]`, if self is a MetaItem
+    fn name(&self) -> Option<InternedString> {
+        self.meta_item().and_then(|meta_item| Some(meta_item.name()))
+    }
+
+    /// Returns the MetaItem if self is a NestedMetaItemKind::MetaItem.
+    fn meta_item(&self) -> Option<&P<MetaItem>>;
+
+    /// Returns the Lit if self is a NestedMetaItemKind::Literal.
+    fn literal(&self) -> Option<&Lit>;
+
+    /// Gets the string value if self is a MetaItem and the MetaItem is a
+    /// MetaItemKind::NameValue variant containing a string, otherwise None.
+    fn value_str(&self) -> Option<InternedString> {
+        self.meta_item().and_then(|meta_item| meta_item.value_str())
+    }
+
+    /// Returns a MetaItem if self is a MetaItem with Kind Word.
+    fn word(&self) -> Option<&P<MetaItem>> {
+        self.meta_item().and_then(|meta_item| if meta_item.is_word() {
+            Some(meta_item)
+        } else {
+            None
+        })
+    }
+
+    /// Gets a list of inner meta items from a list MetaItem type.
+    fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
+        self.meta_item().and_then(|meta_item| meta_item.meta_item_list())
+    }
+
+    /// Returns `true` if the variant is MetaItem.
+    fn is_meta_item(&self) -> bool {
+        self.meta_item().is_some()
+    }
+
+    /// Returns `true` if the variant is Literal.
+    fn is_literal(&self) -> bool {
+        self.literal().is_some()
+    }
+
+    /// Returns `true` if self is a MetaItem and the meta item is a word.
+    fn is_word(&self) -> bool {
+        self.word().is_some()
+    }
+
+    /// Returns `true` if self is a MetaItem and the meta item is a ValueString.
+    fn is_value_str(&self) -> bool {
+        self.value_str().is_some()
+    }
+
+    /// Returns `true` if self is a MetaItem and the meta item is a list.
+    fn is_meta_item_list(&self) -> bool {
+        self.meta_item_list().is_some()
+    }
+
+    /// Returns the Span for `self`.
+    fn span(&self) -> Span;
+}
+
+impl AttrNestedMetaItemMethods for NestedMetaItem {
+    fn meta_item(&self) -> Option<&P<MetaItem>> {
+        match self.node {
+            NestedMetaItemKind::MetaItem(ref item) => Some(&item),
+            _ => None
+        }
+    }
+
+    fn literal(&self) -> Option<&Lit> {
+        match self.node {
+            NestedMetaItemKind::Literal(ref lit) => Some(&lit),
+            _ => None
+        }
+    }
+
+    fn span(&self) -> Span {
+        self.span
+    }
+}
+
 pub trait AttrMetaMethods {
     fn check_name(&self, name: &str) -> bool {
         name == &self.name()[..]
@@ -89,8 +180,9 @@ pub trait AttrMetaMethods {
     /// Gets the string value if self is a MetaItemKind::NameValue variant
     /// containing a string, otherwise None.
     fn value_str(&self) -> Option<InternedString>;
+
     /// Gets a list of inner meta items from a list MetaItem type.
-    fn meta_item_list(&self) -> Option<&[P<MetaItem>]>;
+    fn meta_item_list(&self) -> Option<&[NestedMetaItem]>;
 
     /// Indicates if the attribute is a Word.
     fn is_word(&self) -> bool;
@@ -116,11 +208,14 @@ impl AttrMetaMethods for Attribute {
         }
         matches
     }
+
     fn name(&self) -> InternedString { self.meta().name() }
+
     fn value_str(&self) -> Option<InternedString> {
         self.meta().value_str()
     }
-    fn meta_item_list(&self) -> Option<&[P<MetaItem>]> {
+
+    fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
         self.meta().meta_item_list()
     }
 
@@ -150,7 +245,7 @@ impl AttrMetaMethods for MetaItem {
         }
     }
 
-    fn meta_item_list(&self) -> Option<&[P<MetaItem>]> {
+    fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
         match self.node {
             MetaItemKind::List(_, ref l) => Some(&l[..]),
             _ => None
@@ -171,7 +266,7 @@ impl AttrMetaMethods for MetaItem {
 impl AttrMetaMethods for P<MetaItem> {
     fn name(&self) -> InternedString { (**self).name() }
     fn value_str(&self) -> Option<InternedString> { (**self).value_str() }
-    fn meta_item_list(&self) -> Option<&[P<MetaItem>]> {
+    fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
         (**self).meta_item_list()
     }
     fn is_word(&self) -> bool { (**self).is_word() }
@@ -229,10 +324,14 @@ pub fn mk_name_value_item(name: InternedString, value: ast::Lit)
     mk_spanned_name_value_item(DUMMY_SP, name, value)
 }
 
-pub fn mk_list_item(name: InternedString, items: Vec<P<MetaItem>>) -> P<MetaItem> {
+pub fn mk_list_item(name: InternedString, items: Vec<NestedMetaItem>) -> P<MetaItem> {
     mk_spanned_list_item(DUMMY_SP, name, items)
 }
 
+pub fn mk_list_word_item(name: InternedString) -> ast::NestedMetaItem {
+    dummy_spanned(NestedMetaItemKind::MetaItem(mk_spanned_word_item(DUMMY_SP, name)))
+}
+
 pub fn mk_word_item(name: InternedString) -> P<MetaItem> {
     mk_spanned_word_item(DUMMY_SP, name)
 }
@@ -242,7 +341,7 @@ pub fn mk_spanned_name_value_item(sp: Span, name: InternedString, value: ast::Li
     P(respan(sp, MetaItemKind::NameValue(name, value)))
 }
 
-pub fn mk_spanned_list_item(sp: Span, name: InternedString, items: Vec<P<MetaItem>>)
+pub fn mk_spanned_list_item(sp: Span, name: InternedString, items: Vec<NestedMetaItem>)
                             -> P<MetaItem> {
     P(respan(sp, MetaItemKind::List(name, items)))
 }
@@ -332,6 +431,14 @@ pub fn contains(haystack: &[P<MetaItem>], needle: &MetaItem) -> bool {
     })
 }
 
+pub fn list_contains_name<AM: AttrNestedMetaItemMethods>(items: &[AM], name: &str) -> bool {
+    debug!("attr::list_contains_name (name={})", name);
+    items.iter().any(|item| {
+        debug!("  testing: {:?}", item.name());
+        item.check_name(name)
+    })
+}
+
 pub fn contains_name<AM: AttrMetaMethods>(metas: &[AM], name: &str) -> bool {
     debug!("attr::contains_name (name={})", name);
     metas.iter().any(|item| {
@@ -357,27 +464,6 @@ pub fn last_meta_item_value_str_by_name(items: &[P<MetaItem>], name: &str)
 
 /* Higher-level applications */
 
-pub fn sort_meta_items(items: Vec<P<MetaItem>>) -> Vec<P<MetaItem>> {
-    // This is sort of stupid here, but we need to sort by
-    // human-readable strings.
-    let mut v = items.into_iter()
-        .map(|mi| (mi.name(), mi))
-        .collect::<Vec<(InternedString, P<MetaItem>)>>();
-
-    v.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
-
-    // There doesn't seem to be a more optimal way to do this
-    v.into_iter().map(|(_, m)| m.map(|Spanned {node, span}| {
-        Spanned {
-            node: match node {
-                MetaItemKind::List(n, mis) => MetaItemKind::List(n, sort_meta_items(mis)),
-                _ => node
-            },
-            span: span
-        }
-    })).collect()
-}
-
 pub fn find_crate_name(attrs: &[Attribute]) -> Option<InternedString> {
     first_attr_value_str_by_name(attrs, "crate_name")
 }
@@ -427,14 +513,15 @@ pub fn find_inline_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> In
                 if items.len() != 1 {
                     diagnostic.map(|d|{ span_err!(d, attr.span, E0534, "expected one argument"); });
                     InlineAttr::None
-                } else if contains_name(&items[..], "always") {
+                } else if list_contains_name(&items[..], "always") {
                     InlineAttr::Always
-                } else if contains_name(&items[..], "never") {
+                } else if list_contains_name(&items[..], "never") {
                     InlineAttr::Never
                 } else {
                     diagnostic.map(|d| {
-                        span_err!(d, (*items[0]).span, E0535, "invalid argument");
+                        span_err!(d, items[0].span, E0535, "invalid argument");
                     });
+
                     InlineAttr::None
                 }
             }
@@ -453,27 +540,44 @@ pub fn requests_inline(attrs: &[Attribute]) -> bool {
 
 /// Tests if a cfg-pattern matches the cfg set
 pub fn cfg_matches(cfgs: &[P<MetaItem>], cfg: &ast::MetaItem,
-                   sess: &ParseSess, features: Option<&Features>)
+                   sess: &ParseSess,
+                   features: Option<&Features>)
                    -> bool {
     match cfg.node {
-        ast::MetaItemKind::List(ref pred, ref mis) if &pred[..] == "any" =>
-            mis.iter().any(|mi| cfg_matches(cfgs, &mi, sess, features)),
-        ast::MetaItemKind::List(ref pred, ref mis) if &pred[..] == "all" =>
-            mis.iter().all(|mi| cfg_matches(cfgs, &mi, sess, features)),
-        ast::MetaItemKind::List(ref pred, ref mis) if &pred[..] == "not" => {
-            if mis.len() != 1 {
-                span_err!(sess.span_diagnostic, cfg.span, E0536, "expected 1 cfg-pattern");
-                return false;
+        ast::MetaItemKind::List(ref pred, ref mis) => {
+            for mi in mis.iter() {
+                if !mi.is_meta_item() {
+                    handle_errors(&sess.span_diagnostic, mi.span, AttrError::UnsupportedLiteral);
+                    return false;
+                }
+            }
+
+            // The unwraps below may look dangerous, but we've already asserted
+            // that they won't fail with the loop above.
+            match &pred[..] {
+                "any" => mis.iter().any(|mi| {
+                    cfg_matches(cfgs, mi.meta_item().unwrap(), sess, features)
+                }),
+                "all" => mis.iter().all(|mi| {
+                    cfg_matches(cfgs, mi.meta_item().unwrap(), sess, features)
+                }),
+                "not" => {
+                    if mis.len() != 1 {
+                        span_err!(sess.span_diagnostic, cfg.span, E0536, "expected 1 cfg-pattern");
+                        return false;
+                    }
+
+                    !cfg_matches(cfgs, mis[0].meta_item().unwrap(), sess, features)
+                },
+                p => {
+                    span_err!(sess.span_diagnostic, cfg.span, E0537, "invalid predicate `{}`", p);
+                    false
+                }
             }
-            !cfg_matches(cfgs, &mis[0], sess, features)
-        }
-        ast::MetaItemKind::List(ref pred, _) => {
-            span_err!(sess.span_diagnostic, cfg.span, E0537, "invalid predicate `{}`", pred);
-            false
         },
         ast::MetaItemKind::Word(_) | ast::MetaItemKind::NameValue(..) => {
-            if let (Some(features), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) {
-                gated_cfg.check_and_emit(sess, features);
+            if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) {
+                gated_cfg.check_and_emit(sess, feats);
             }
             contains(cfgs, cfg)
         }
@@ -557,14 +661,19 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
                     let mut since = None;
                     let mut reason = None;
                     for meta in metas {
-                        match &*meta.name() {
-                            "since" => if !get(meta, &mut since) { continue 'outer },
-                            "reason" => if !get(meta, &mut reason) { continue 'outer },
-                            _ => {
-                                handle_errors(diagnostic, meta.span,
-                                              AttrError::UnknownMetaItem(meta.name()));
-                                continue 'outer
+                        if let Some(mi) = meta.meta_item() {
+                            match &*mi.name() {
+                                "since" => if !get(mi, &mut since) { continue 'outer },
+                                "reason" => if !get(mi, &mut reason) { continue 'outer },
+                                _ => {
+                                    handle_errors(diagnostic, mi.span,
+                                                  AttrError::UnknownMetaItem(mi.name()));
+                                    continue 'outer
+                                }
                             }
+                        } else {
+                            handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral);
+                            continue 'outer
                         }
                     }
 
@@ -595,15 +704,20 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
                     let mut reason = None;
                     let mut issue = None;
                     for meta in metas {
-                        match &*meta.name() {
-                            "feature" => if !get(meta, &mut feature) { continue 'outer },
-                            "reason" => if !get(meta, &mut reason) { continue 'outer },
-                            "issue" => if !get(meta, &mut issue) { continue 'outer },
-                            _ => {
-                                handle_errors(diagnostic, meta.span,
-                                              AttrError::UnknownMetaItem(meta.name()));
-                                continue 'outer
+                        if let Some(mi) = meta.meta_item() {
+                            match &*mi.name() {
+                                "feature" => if !get(mi, &mut feature) { continue 'outer },
+                                "reason" => if !get(mi, &mut reason) { continue 'outer },
+                                "issue" => if !get(mi, &mut issue) { continue 'outer },
+                                _ => {
+                                    handle_errors(diagnostic, meta.span,
+                                                  AttrError::UnknownMetaItem(mi.name()));
+                                    continue 'outer
+                                }
                             }
+                        } else {
+                            handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral);
+                            continue 'outer
                         }
                     }
 
@@ -645,14 +759,19 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
                     let mut feature = None;
                     let mut since = None;
                     for meta in metas {
-                        match &*meta.name() {
-                            "feature" => if !get(meta, &mut feature) { continue 'outer },
-                            "since" => if !get(meta, &mut since) { continue 'outer },
-                            _ => {
-                                handle_errors(diagnostic, meta.span,
-                                              AttrError::UnknownMetaItem(meta.name()));
-                                continue 'outer
+                        if let NestedMetaItemKind::MetaItem(ref mi) = meta.node {
+                            match &*mi.name() {
+                                "feature" => if !get(mi, &mut feature) { continue 'outer },
+                                "since" => if !get(mi, &mut since) { continue 'outer },
+                                _ => {
+                                    handle_errors(diagnostic, meta.span,
+                                                  AttrError::UnknownMetaItem(mi.name()));
+                                    continue 'outer
+                                }
                             }
+                        } else {
+                            handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral);
+                            continue 'outer
                         }
                     }
 
@@ -739,14 +858,19 @@ fn find_deprecation_generic<'a, I>(diagnostic: &Handler,
             let mut since = None;
             let mut note = None;
             for meta in metas {
-                match &*meta.name() {
-                    "since" => if !get(meta, &mut since) { continue 'outer },
-                    "note" => if !get(meta, &mut note) { continue 'outer },
-                    _ => {
-                        handle_errors(diagnostic, meta.span,
-                                      AttrError::UnknownMetaItem(meta.name()));
-                        continue 'outer
+                if let NestedMetaItemKind::MetaItem(ref mi) = meta.node {
+                    match &*mi.name() {
+                        "since" => if !get(mi, &mut since) { continue 'outer },
+                        "note" => if !get(mi, &mut note) { continue 'outer },
+                        _ => {
+                            handle_errors(diagnostic, meta.span,
+                                          AttrError::UnknownMetaItem(mi.name()));
+                            continue 'outer
+                        }
                     }
+                } else {
+                    handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral);
+                    continue 'outer
                 }
             }
 
@@ -796,32 +920,36 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr>
         ast::MetaItemKind::List(ref s, ref items) if s == "repr" => {
             mark_used(attr);
             for item in items {
-                match item.node {
-                    ast::MetaItemKind::Word(ref word) => {
-                        let hint = match &word[..] {
-                            // Can't use "extern" because it's not a lexical identifier.
-                            "C" => Some(ReprExtern),
-                            "packed" => Some(ReprPacked),
-                            "simd" => Some(ReprSimd),
-                            _ => match int_type_of_word(&word) {
-                                Some(ity) => Some(ReprInt(item.span, ity)),
-                                None => {
-                                    // Not a word we recognize
-                                    span_err!(diagnostic, item.span, E0552,
-                                              "unrecognized representation hint");
-                                    None
-                                }
-                            }
-                        };
+                if !item.is_meta_item() {
+                    handle_errors(diagnostic, item.span, AttrError::UnsupportedLiteral);
+                    continue
+                }
 
-                        match hint {
-                            Some(h) => acc.push(h),
-                            None => { }
+                if let Some(mi) = item.word() {
+                    let word = &*mi.name();
+                    let hint = match word {
+                        // Can't use "extern" because it's not a lexical identifier.
+                        "C" => Some(ReprExtern),
+                        "packed" => Some(ReprPacked),
+                        "simd" => Some(ReprSimd),
+                        _ => match int_type_of_word(word) {
+                            Some(ity) => Some(ReprInt(item.span, ity)),
+                            None => {
+                                // Not a word we recognize
+                                span_err!(diagnostic, item.span, E0552,
+                                          "unrecognized representation hint");
+                                None
+                            }
                         }
+                    };
+
+                    match hint {
+                        Some(h) => acc.push(h),
+                        None => { }
                     }
-                    // Not a word:
-                    _ => span_err!(diagnostic, item.span, E0553,
-                                   "unrecognized enum representation hint"),
+                } else {
+                    span_err!(diagnostic, item.span, E0553,
+                              "unrecognized enum representation hint");
                 }
             }
         }
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index a825cf866a8..4663143f4b1 100644
--- a/src/libsyntax/config.rs
+++ b/src/libsyntax/config.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use attr::{AttrMetaMethods, HasAttrs};
+use attr::{AttrMetaMethods, AttrNestedMetaItemMethods, HasAttrs};
 use feature_gate::{emit_feature_err, EXPLAIN_STMT_ATTR_SYNTAX, Features, get_features, GateIssue};
 use fold::Folder;
 use {fold, attr};
@@ -52,6 +52,7 @@ impl<'a> StripUnconfigured<'a> {
                 return None;
             }
         };
+
         let (cfg, mi) = match (attr_list.len(), attr_list.get(0), attr_list.get(1)) {
             (2, Some(cfg), Some(mi)) => (cfg, mi),
             _ => {
@@ -61,15 +62,24 @@ impl<'a> StripUnconfigured<'a> {
             }
         };
 
-        if attr::cfg_matches(self.config, &cfg, self.sess, self.features) {
-            self.process_cfg_attr(respan(mi.span, ast::Attribute_ {
-                id: attr::mk_attr_id(),
-                style: attr.node.style,
-                value: mi.clone(),
-                is_sugared_doc: false,
-            }))
-        } else {
-            None
+        use attr::cfg_matches;
+        match (cfg.meta_item(), mi.meta_item()) {
+            (Some(cfg), Some(mi)) =>
+                if cfg_matches(self.config, &cfg, self.sess, self.features) {
+                    self.process_cfg_attr(respan(mi.span, ast::Attribute_ {
+                        id: attr::mk_attr_id(),
+                        style: attr.node.style,
+                        value: mi.clone(),
+                        is_sugared_doc: false,
+                    }))
+                } else {
+                    None
+                },
+            _ => {
+                let msg = "unexpected literal(s) in `#[cfg_attr(<cfg pattern>, <attr>)]`";
+                self.sess.span_diagnostic.span_err(attr.span, msg);
+                None
+            }
         }
     }
 
@@ -91,7 +101,12 @@ impl<'a> StripUnconfigured<'a> {
                 return true;
             }
 
-            attr::cfg_matches(self.config, &mis[0], self.sess, self.features)
+            if !mis[0].is_meta_item() {
+                self.sess.span_diagnostic.span_err(mis[0].span, "unexpected literal");
+                return true;
+            }
+
+            attr::cfg_matches(self.config, mis[0].meta_item().unwrap(), self.sess, self.features)
         })
     }
 
diff --git a/src/libsyntax/diagnostic_list.rs b/src/libsyntax/diagnostic_list.rs
index 010b1d638e6..9110e989a8a 100644
--- a/src/libsyntax/diagnostic_list.rs
+++ b/src/libsyntax/diagnostic_list.rs
@@ -161,6 +161,24 @@ fn main() {}
 ```
 "##,
 
+E0565: r##"
+A literal was used in an attribute that doesn't support literals.
+
+Erroneous code example:
+
+```compile_fail,E0565
+#[inline("always")] // error: unsupported literal
+pub fn something() {}
+```
+
+Literals in attributes are new and largely unsupported. Work to support literals
+where appropriate is ongoing. Try using an unquoted name instead:
+
+```
+#[inline(always)]
+pub fn something() {}
+```
+"##,
 }
 
 register_diagnostics! {
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 5d6429f7bdf..1d3939df6fb 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -277,10 +277,13 @@ pub trait AstBuilder {
     fn attribute(&self, sp: Span, mi: P<ast::MetaItem>) -> ast::Attribute;
 
     fn meta_word(&self, sp: Span, w: InternedString) -> P<ast::MetaItem>;
+
+    fn meta_list_item_word(&self, sp: Span, w: InternedString) -> ast::NestedMetaItem;
+
     fn meta_list(&self,
                  sp: Span,
                  name: InternedString,
-                 mis: Vec<P<ast::MetaItem>> )
+                 mis: Vec<ast::NestedMetaItem> )
                  -> P<ast::MetaItem>;
     fn meta_name_value(&self,
                        sp: Span,
@@ -1141,10 +1144,16 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     fn meta_word(&self, sp: Span, w: InternedString) -> P<ast::MetaItem> {
         attr::mk_spanned_word_item(sp, w)
     }
-    fn meta_list(&self, sp: Span, name: InternedString, mis: Vec<P<ast::MetaItem>>)
+
+    fn meta_list_item_word(&self, sp: Span, w: InternedString) -> ast::NestedMetaItem {
+        respan(sp, ast::NestedMetaItemKind::MetaItem(attr::mk_spanned_word_item(sp, w)))
+    }
+
+    fn meta_list(&self, sp: Span, name: InternedString, mis: Vec<ast::NestedMetaItem>)
                  -> P<ast::MetaItem> {
         attr::mk_spanned_list_item(sp, name, mis)
     }
+
     fn meta_name_value(&self, sp: Span, name: InternedString, value: ast::LitKind)
                        -> P<ast::MetaItem> {
         attr::mk_spanned_name_value_item(sp, name, respan(sp, value))
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index d746f8e2114..df1d5c4d9ca 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -26,10 +26,8 @@ use self::AttributeType::*;
 use self::AttributeGate::*;
 
 use abi::Abi;
-use ast::{NodeId, PatKind};
-use ast;
-use attr;
-use attr::AttrMetaMethods;
+use ast::{self, NodeId, PatKind};
+use attr::{self, AttrMetaMethods, AttrNestedMetaItemMethods};
 use codemap::CodeMap;
 use syntax_pos::Span;
 use errors::Handler;
@@ -283,7 +281,10 @@ declare_features! (
     (active, relaxed_adts, "1.12.0", Some(35626)),
 
     // The `!` type
-    (active, never_type, "1.13.0", Some(35121))
+    (active, never_type, "1.13.0", Some(35121)),
+
+    // Allows all literals in attribute lists and values of key-value pairs.
+    (active, attr_literals, "1.13.0", Some(34981))
 );
 
 declare_features! (
@@ -831,11 +832,34 @@ impl<'a> PostExpansionVisitor<'a> {
     }
 }
 
+fn contains_novel_literal(item: &ast::MetaItem) -> bool {
+    use ast::MetaItemKind::*;
+    use ast::NestedMetaItemKind::*;
+
+    match item.node {
+        Word(..) => false,
+        NameValue(_, ref lit) => !lit.node.is_str(),
+        List(_, ref list) => list.iter().any(|li| {
+            match li.node {
+                MetaItem(ref mi) => contains_novel_literal(&**mi),
+                Literal(_) => true,
+            }
+        }),
+    }
+}
+
 impl<'a> Visitor for PostExpansionVisitor<'a> {
     fn visit_attribute(&mut self, attr: &ast::Attribute) {
         if !self.context.cm.span_allows_unstable(attr.span) {
+            // check for gated attributes
             self.context.check_attribute(attr, false);
         }
+
+        if contains_novel_literal(&*(attr.node.value)) {
+            gate_feature_post!(&self, attr_literals, attr.span,
+                               "non-string literals in attributes, or string \
+                               literals in top-level positions, are experimental");
+        }
     }
 
     fn visit_name(&mut self, sp: Span, name: ast::Name) {
@@ -895,7 +919,7 @@ impl<'a> Visitor for PostExpansionVisitor<'a> {
                 for attr in &i.attrs {
                     if attr.name() == "repr" {
                         for item in attr.meta_item_list().unwrap_or(&[]) {
-                            if item.name() == "simd" {
+                            if item.check_name("simd") {
                                 gate_feature_post!(&self, repr_simd, i.span,
                                                    "SIMD types are experimental \
                                                     and possibly buggy");
@@ -1155,13 +1179,14 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> F
             }
             Some(list) => {
                 for mi in list {
-                    let name = if mi.is_word() {
-                                   mi.name()
-                               } else {
-                                   span_err!(span_handler, mi.span, E0556,
-                                             "malformed feature, expected just one word");
-                                   continue
-                               };
+                    let name = if let Some(word) = mi.word() {
+                        word.name()
+                    } else {
+                        span_err!(span_handler, mi.span, E0556,
+                                  "malformed feature, expected just one word");
+                        continue
+                    };
+
                     if let Some(&(_, _, _, setter)) = ACTIVE_FEATURES.iter()
                         .find(|& &(n, _, _, _)| name == n) {
                         *(setter(&mut features)) = true;
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index b257ab98987..b361a856dbe 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -47,6 +47,10 @@ pub trait Folder : Sized {
         noop_fold_meta_items(meta_items, self)
     }
 
+    fn fold_meta_list_item(&mut self, list_item: NestedMetaItem) -> NestedMetaItem {
+        noop_fold_meta_list_item(list_item, self)
+    }
+
     fn fold_meta_item(&mut self, meta_item: P<MetaItem>) -> P<MetaItem> {
         noop_fold_meta_item(meta_item, self)
     }
@@ -513,12 +517,25 @@ pub fn noop_fold_mac<T: Folder>(Spanned {node, span}: Mac, fld: &mut T) -> Mac {
     }
 }
 
+pub fn noop_fold_meta_list_item<T: Folder>(li: NestedMetaItem, fld: &mut T)
+    -> NestedMetaItem {
+    Spanned {
+        node: match li.node {
+            NestedMetaItemKind::MetaItem(mi) =>  {
+                NestedMetaItemKind::MetaItem(fld.fold_meta_item(mi))
+            },
+            NestedMetaItemKind::Literal(lit) => NestedMetaItemKind::Literal(lit)
+        },
+        span: fld.new_span(li.span)
+    }
+}
+
 pub fn noop_fold_meta_item<T: Folder>(mi: P<MetaItem>, fld: &mut T) -> P<MetaItem> {
     mi.map(|Spanned {node, span}| Spanned {
         node: match node {
             MetaItemKind::Word(id) => MetaItemKind::Word(id),
             MetaItemKind::List(id, mis) => {
-                MetaItemKind::List(id, mis.move_map(|e| fld.fold_meta_item(e)))
+                MetaItemKind::List(id, mis.move_map(|e| fld.fold_meta_list_item(e)))
             }
             MetaItemKind::NameValue(id, s) => MetaItemKind::NameValue(id, s)
         },
diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs
index 2ae3236cd5a..27dd055cd3a 100644
--- a/src/libsyntax/parse/attr.rs
+++ b/src/libsyntax/parse/attr.rs
@@ -193,9 +193,26 @@ impl<'a> Parser<'a> {
         Ok(attrs)
     }
 
-    /// matches meta_item = IDENT
-    /// | IDENT = lit
-    /// | IDENT meta_seq
+    fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
+        let lit = self.parse_lit()?;
+        debug!("Checking if {:?} is unusuffixed.", lit);
+
+        if !lit.node.is_unsuffixed() {
+            let msg = "suffixed literals are not allowed in attributes";
+            self.diagnostic().struct_span_err(lit.span, msg)
+                             .help("instead of using a suffixed literal \
+                                    (1u8, 1.0f32, etc.), use an unsuffixed version \
+                                    (1, 1.0, etc.).")
+                             .emit()
+        }
+
+        Ok(lit)
+    }
+
+    /// Per RFC#1559, matches the following grammar:
+    ///
+    /// meta_item : IDENT ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ;
+    /// meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ;
     pub fn parse_meta_item(&mut self) -> PResult<'a, P<ast::MetaItem>> {
         let nt_meta = match self.token {
             token::Interpolated(token::NtMeta(ref e)) => Some(e.clone()),
@@ -213,16 +230,7 @@ impl<'a> Parser<'a> {
         match self.token {
             token::Eq => {
                 self.bump();
-                let lit = self.parse_lit()?;
-                // FIXME #623 Non-string meta items are not serialized correctly;
-                // just forbid them for now
-                match lit.node {
-                    ast::LitKind::Str(..) => {}
-                    _ => {
-                        self.span_err(lit.span,
-                                      "non-string literals are not allowed in meta-items");
-                    }
-                }
+                let lit = self.parse_unsuffixed_lit()?;
                 let hi = self.span.hi;
                 Ok(P(spanned(lo, hi, ast::MetaItemKind::NameValue(name, lit))))
             }
@@ -238,11 +246,35 @@ impl<'a> Parser<'a> {
         }
     }
 
-    /// matches meta_seq = ( COMMASEP(meta_item) )
-    fn parse_meta_seq(&mut self) -> PResult<'a, Vec<P<ast::MetaItem>>> {
+    /// matches meta_item_inner : (meta_item | UNSUFFIXED_LIT) ;
+    fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> {
+        let sp = self.span;
+        let lo = self.span.lo;
+
+        match self.parse_unsuffixed_lit() {
+            Ok(lit) => {
+                return Ok(spanned(lo, self.span.hi, ast::NestedMetaItemKind::Literal(lit)))
+            }
+            Err(ref mut err) => self.diagnostic().cancel(err)
+        }
+
+        match self.parse_meta_item() {
+            Ok(mi) => {
+                return Ok(spanned(lo, self.span.hi, ast::NestedMetaItemKind::MetaItem(mi)))
+            }
+            Err(ref mut err) => self.diagnostic().cancel(err)
+        }
+
+        let found = self.this_token_to_string();
+        let msg = format!("expected unsuffixed literal or identifier, found {}", found);
+        Err(self.diagnostic().struct_span_err(sp, &msg))
+    }
+
+    /// matches meta_seq = ( COMMASEP(meta_item_inner) )
+    fn parse_meta_seq(&mut self) -> PResult<'a, Vec<ast::NestedMetaItem>> {
         self.parse_unspanned_seq(&token::OpenDelim(token::Paren),
                                  &token::CloseDelim(token::Paren),
                                  SeqSep::trailing_allowed(token::Comma),
-                                 |p: &mut Parser<'a>| p.parse_meta_item())
+                                 |p: &mut Parser<'a>| p.parse_meta_item_inner())
     }
 }
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index a77c678248b..562cc896aef 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -120,7 +120,7 @@ pub fn print_crate<'a>(cm: &'a CodeMap,
         // of the feature gate, so we fake them up here.
 
         // #![feature(prelude_import)]
-        let prelude_import_meta = attr::mk_word_item(InternedString::new("prelude_import"));
+        let prelude_import_meta = attr::mk_list_word_item(InternedString::new("prelude_import"));
         let list = attr::mk_list_item(InternedString::new("feature"),
                                       vec![prelude_import_meta]);
         let fake_attr = attr::mk_attr_inner(attr::mk_attr_id(), list);
@@ -406,6 +406,10 @@ pub fn block_to_string(blk: &ast::Block) -> String {
     })
 }
 
+pub fn meta_list_item_to_string(li: &ast::NestedMetaItem) -> String {
+    to_string(|s| s.print_meta_list_item(li))
+}
+
 pub fn meta_item_to_string(mi: &ast::MetaItem) -> String {
     to_string(|s| s.print_meta_item(mi))
 }
@@ -764,6 +768,17 @@ pub trait PrintState<'a> {
         }
     }
 
+    fn print_meta_list_item(&mut self, item: &ast::NestedMetaItem) -> io::Result<()> {
+        match item.node {
+            ast::NestedMetaItemKind::MetaItem(ref mi) => {
+                self.print_meta_item(mi)
+            },
+            ast::NestedMetaItemKind::Literal(ref lit) => {
+                self.print_literal(lit)
+            }
+        }
+    }
+
     fn print_meta_item(&mut self, item: &ast::MetaItem) -> io::Result<()> {
         try!(self.ibox(INDENT_UNIT));
         match item.node {
@@ -780,7 +795,7 @@ pub trait PrintState<'a> {
                 try!(self.popen());
                 try!(self.commasep(Consistent,
                               &items[..],
-                              |s, i| s.print_meta_item(&i)));
+                              |s, i| s.print_meta_list_item(&i)));
                 try!(self.pclose());
             }
         }
diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs
index faf6a17a150..ce917f248e1 100644
--- a/src/libsyntax/test.rs
+++ b/src/libsyntax/test.rs
@@ -19,8 +19,7 @@ use std::iter;
 use std::slice;
 use std::mem;
 use std::vec;
-use attr::AttrMetaMethods;
-use attr;
+use attr::{self, AttrMetaMethods, AttrNestedMetaItemMethods};
 use syntax_pos::{self, DUMMY_SP, NO_EXPANSION, Span, FileMap, BytePos};
 use std::rc::Rc;
 
@@ -210,9 +209,8 @@ impl fold::Folder for EntryPointCleaner {
                 folded.map(|ast::Item {id, ident, attrs, node, vis, span}| {
                     let allow_str = InternedString::new("allow");
                     let dead_code_str = InternedString::new("dead_code");
-                    let allow_dead_code_item =
-                        attr::mk_list_item(allow_str,
-                                           vec![attr::mk_word_item(dead_code_str)]);
+                    let word_vec = vec![attr::mk_list_word_item(dead_code_str)];
+                    let allow_dead_code_item = attr::mk_list_item(allow_str, word_vec);
                     let allow_dead_code = attr::mk_attr_outer(attr::mk_attr_id(),
                                                               allow_dead_code_item);
 
@@ -413,6 +411,7 @@ fn should_panic(i: &ast::Item) -> ShouldPanic {
         Some(attr) => {
             let msg = attr.meta_item_list()
                 .and_then(|list| list.iter().find(|mi| mi.check_name("expected")))
+                .and_then(|li| li.meta_item())
                 .and_then(|mi| mi.value_str());
             ShouldPanic::Yes(msg)
         }