about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2014-01-08 10:35:15 -0800
committerHuon Wilson <dbau.pp+github@gmail.com>2014-02-02 01:44:47 +1100
commit70c5a0fbf784d6a89b1c2c50f9fe83093bd21abc (patch)
treec0d73d05918545051a9e1d10f5496ee588df3693 /src/libsyntax
parent1d494198bbb9701b6336febcf9d0ceb39e4b7975 (diff)
downloadrust-70c5a0fbf784d6a89b1c2c50f9fe83093bd21abc.tar.gz
rust-70c5a0fbf784d6a89b1c2c50f9fe83093bd21abc.zip
libsyntax: Introduce an `InternedString` type to reduce `@str` in the
compiler and use it for attributes
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ast.rs9
-rw-r--r--src/libsyntax/attr.rs83
-rw-r--r--src/libsyntax/ext/build.rs28
-rw-r--r--src/libsyntax/ext/cfg.rs7
-rw-r--r--src/libsyntax/ext/deriving/generic.rs12
-rw-r--r--src/libsyntax/ext/deriving/mod.rs8
-rw-r--r--src/libsyntax/ext/expand.rs4
-rw-r--r--src/libsyntax/ext/format.rs16
-rw-r--r--src/libsyntax/fold.rs11
-rw-r--r--src/libsyntax/parse/attr.rs2
-rw-r--r--src/libsyntax/parse/parser.rs12
-rw-r--r--src/libsyntax/parse/token.rs87
-rw-r--r--src/libsyntax/print/pprust.rs10
-rw-r--r--src/libsyntax/util/interner.rs11
14 files changed, 221 insertions, 79 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 1513946e401..f7e474f666c 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -14,7 +14,8 @@ use codemap::{Span, Spanned, DUMMY_SP};
 use abi::AbiSet;
 use ast_util;
 use opt_vec::OptVec;
-use parse::token::{interner_get, str_to_ident, special_idents};
+use parse::token::{InternedString, interner_get, special_idents};
+use parse::token::{str_to_ident};
 
 use std::cell::RefCell;
 use std::hashmap::HashMap;
@@ -295,9 +296,9 @@ pub type MetaItem = Spanned<MetaItem_>;
 
 #[deriving(Clone, Encodable, Decodable, IterBytes)]
 pub enum MetaItem_ {
-    MetaWord(@str),
-    MetaList(@str, ~[@MetaItem]),
-    MetaNameValue(@str, Lit),
+    MetaWord(InternedString),
+    MetaList(InternedString, ~[@MetaItem]),
+    MetaNameValue(InternedString, Lit),
 }
 
 // can't be derived because the MetaList requires an unordered comparison
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index c44861bd7d7..6c6f47bae8a 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -16,18 +16,19 @@ use codemap::{Span, Spanned, spanned, dummy_spanned};
 use codemap::BytePos;
 use diagnostic::SpanHandler;
 use parse::comments::{doc_comment_style, strip_doc_comment_decoration};
+use parse::token::InternedString;
 use crateid::CrateId;
 
 use std::hashmap::HashSet;
 
 pub trait AttrMetaMethods {
-    // This could be changed to `fn check_name(&self, name: @str) ->
+    // This could be changed to `fn check_name(&self, name: InternedString) ->
     // bool` which would facilitate a side table recording which
     // attributes/meta items are used/unused.
 
     /// Retrieve the name of the meta item, e.g. foo in #[foo],
     /// #[foo="bar"] and #[foo(bar)]
-    fn name(&self) -> @str;
+    fn name(&self) -> InternedString;
 
     /**
      * Gets the string value if self is a MetaNameValue variant
@@ -41,24 +42,26 @@ pub trait AttrMetaMethods {
      * If the meta item is a name-value type with a string value then returns
      * a tuple containing the name and string value, otherwise `None`
      */
-    fn name_str_pair(&self) -> Option<(@str, @str)>;
+    fn name_str_pair(&self) -> Option<(InternedString, @str)>;
 }
 
 impl AttrMetaMethods for Attribute {
-    fn name(&self) -> @str { self.meta().name() }
+    fn name(&self) -> InternedString { self.meta().name() }
     fn value_str(&self) -> Option<@str> { self.meta().value_str() }
     fn meta_item_list<'a>(&'a self) -> Option<&'a [@MetaItem]> {
         self.node.value.meta_item_list()
     }
-    fn name_str_pair(&self) -> Option<(@str, @str)> { self.meta().name_str_pair() }
+    fn name_str_pair(&self) -> Option<(InternedString, @str)> {
+        self.meta().name_str_pair()
+    }
 }
 
 impl AttrMetaMethods for MetaItem {
-    fn name(&self) -> @str {
+    fn name(&self) -> InternedString {
         match self.node {
-            MetaWord(n) => n,
-            MetaNameValue(n, _) => n,
-            MetaList(n, _) => n
+            MetaWord(ref n) => (*n).clone(),
+            MetaNameValue(ref n, _) => (*n).clone(),
+            MetaList(ref n, _) => (*n).clone(),
         }
     }
 
@@ -81,19 +84,21 @@ impl AttrMetaMethods for MetaItem {
         }
     }
 
-    fn name_str_pair(&self) -> Option<(@str, @str)> {
+    fn name_str_pair(&self) -> Option<(InternedString, @str)> {
         self.value_str().map(|s| (self.name(), s))
     }
 }
 
 // Annoying, but required to get test_cfg to work
 impl AttrMetaMethods for @MetaItem {
-    fn name(&self) -> @str { (**self).name() }
+    fn name(&self) -> InternedString { (**self).name() }
     fn value_str(&self) -> Option<@str> { (**self).value_str() }
     fn meta_item_list<'a>(&'a self) -> Option<&'a [@MetaItem]> {
         (**self).meta_item_list()
     }
-    fn name_str_pair(&self) -> Option<(@str, @str)> { (**self).name_str_pair() }
+    fn name_str_pair(&self) -> Option<(InternedString, @str)> {
+        (**self).name_str_pair()
+    }
 }
 
 
@@ -114,7 +119,7 @@ impl AttributeMethods for Attribute {
     fn desugar_doc(&self) -> Attribute {
         if self.node.is_sugared_doc {
             let comment = self.value_str().unwrap();
-            let meta = mk_name_value_item_str(@"doc",
+            let meta = mk_name_value_item_str(InternedString::new("doc"),
                                               strip_doc_comment_decoration(comment).to_managed());
             mk_attr(meta)
         } else {
@@ -125,20 +130,22 @@ impl AttributeMethods for Attribute {
 
 /* Constructors */
 
-pub fn mk_name_value_item_str(name: @str, value: @str) -> @MetaItem {
+pub fn mk_name_value_item_str(name: InternedString, value: @str)
+                              -> @MetaItem {
     let value_lit = dummy_spanned(ast::LitStr(value, ast::CookedStr));
     mk_name_value_item(name, value_lit)
 }
 
-pub fn mk_name_value_item(name: @str, value: ast::Lit) -> @MetaItem {
+pub fn mk_name_value_item(name: InternedString, value: ast::Lit)
+                          -> @MetaItem {
     @dummy_spanned(MetaNameValue(name, value))
 }
 
-pub fn mk_list_item(name: @str, items: ~[@MetaItem]) -> @MetaItem {
+pub fn mk_list_item(name: InternedString, items: ~[@MetaItem]) -> @MetaItem {
     @dummy_spanned(MetaList(name, items))
 }
 
-pub fn mk_word_item(name: @str) -> @MetaItem {
+pub fn mk_word_item(name: InternedString) -> @MetaItem {
     @dummy_spanned(MetaWord(name))
 }
 
@@ -155,7 +162,8 @@ pub fn mk_sugared_doc_attr(text: @str, lo: BytePos, hi: BytePos) -> Attribute {
     let lit = spanned(lo, hi, ast::LitStr(text, ast::CookedStr));
     let attr = Attribute_ {
         style: style,
-        value: @spanned(lo, hi, MetaNameValue(@"doc", lit)),
+        value: @spanned(lo, hi, MetaNameValue(InternedString::new("doc"),
+                                              lit)),
         is_sugared_doc: true
     };
     spanned(lo, hi, attr)
@@ -178,20 +186,22 @@ pub fn contains_name<AM: AttrMetaMethods>(metas: &[AM], name: &str) -> bool {
     debug!("attr::contains_name (name={})", name);
     metas.iter().any(|item| {
         debug!("  testing: {}", item.name());
-        name == item.name()
+        item.name().equiv(&name)
     })
 }
 
 pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: &str)
                                  -> Option<@str> {
     attrs.iter()
-        .find(|at| name == at.name())
+        .find(|at| at.name().equiv(&name))
         .and_then(|at| at.value_str())
 }
 
 pub fn last_meta_item_value_str_by_name(items: &[@MetaItem], name: &str)
                                      -> Option<@str> {
-    items.rev_iter().find(|mi| name == mi.name()).and_then(|i| i.value_str())
+    items.rev_iter()
+         .find(|mi| mi.name().equiv(&name))
+         .and_then(|i| i.value_str())
 }
 
 /* Higher-level applications */
@@ -201,16 +211,16 @@ pub fn sort_meta_items(items: &[@MetaItem]) -> ~[@MetaItem] {
     // human-readable strings.
     let mut v = items.iter()
         .map(|&mi| (mi.name(), mi))
-        .collect::<~[(@str, @MetaItem)]>();
+        .collect::<~[(InternedString, @MetaItem)]>();
 
-    v.sort_by(|&(a, _), &(b, _)| a.cmp(&b));
+    v.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
 
     // There doesn't seem to be a more optimal way to do this
     v.move_iter().map(|(_, m)| {
         match m.node {
-            MetaList(n, ref mis) => {
+            MetaList(ref n, ref mis) => {
                 @Spanned {
-                    node: MetaList(n, sort_meta_items(*mis)),
+                    node: MetaList((*n).clone(), sort_meta_items(*mis)),
                     .. /*bad*/ (*m).clone()
                 }
             }
@@ -225,7 +235,7 @@ pub fn sort_meta_items(items: &[@MetaItem]) -> ~[@MetaItem] {
  */
 pub fn find_linkage_metas(attrs: &[Attribute]) -> ~[@MetaItem] {
     let mut result = ~[];
-    for attr in attrs.iter().filter(|at| "link" == at.name()) {
+    for attr in attrs.iter().filter(|at| at.name().equiv(&("link"))) {
         match attr.meta().node {
             MetaList(_, ref items) => result.push_all(*items),
             _ => ()
@@ -254,8 +264,8 @@ pub fn find_inline_attr(attrs: &[Attribute]) -> InlineAttr {
     // FIXME (#2809)---validate the usage of #[inline] and #[inline]
     attrs.iter().fold(InlineNone, |ia,attr| {
         match attr.node.value.node {
-          MetaWord(n) if "inline" == n => InlineHint,
-          MetaList(n, ref items) if "inline" == n => {
+          MetaWord(ref n) if n.equiv(&("inline")) => InlineHint,
+          MetaList(ref n, ref items) if n.equiv(&("inline")) => {
             if contains_name(*items, "always") {
                 InlineAlways
             } else if contains_name(*items, "never") {
@@ -284,7 +294,7 @@ pub fn test_cfg<AM: AttrMetaMethods, It: Iterator<AM>>
     // this doesn't work.
     let some_cfg_matches = metas.any(|mi| {
         debug!("testing name: {}", mi.name());
-        if "cfg" == mi.name() { // it is a #[cfg()] attribute
+        if mi.name().equiv(&("cfg")) { // it is a #[cfg()] attribute
             debug!("is cfg");
             no_cfgs = false;
              // only #[cfg(...)] ones are understood.
@@ -294,7 +304,8 @@ pub fn test_cfg<AM: AttrMetaMethods, It: Iterator<AM>>
                     cfg_meta.iter().all(|cfg_mi| {
                         debug!("cfg({}[...])", cfg_mi.name());
                         match cfg_mi.node {
-                            ast::MetaList(s, ref not_cfgs) if "not" == s => {
+                            ast::MetaList(ref s, ref not_cfgs)
+                            if s.equiv(&("not")) => {
                                 debug!("not!");
                                 // inside #[cfg(not(...))], so these need to all
                                 // not match.
@@ -337,7 +348,7 @@ pub enum StabilityLevel {
 /// Find the first stability attribute. `None` if none exists.
 pub fn find_stability<AM: AttrMetaMethods, It: Iterator<AM>>(mut metas: It) -> Option<Stability> {
     for m in metas {
-        let level = match m.name().as_slice() {
+        let level = match m.name().get() {
             "deprecated" => Deprecated,
             "experimental" => Experimental,
             "unstable" => Unstable,
@@ -360,7 +371,7 @@ pub fn require_unique_names(diagnostic: @SpanHandler, metas: &[@MetaItem]) {
     for meta in metas.iter() {
         let name = meta.name();
 
-        if !set.insert(name) {
+        if !set.insert(name.clone()) {
             diagnostic.span_fatal(meta.span,
                                   format!("duplicate meta item `{}`", name));
         }
@@ -384,14 +395,14 @@ pub fn find_repr_attr(diagnostic: @SpanHandler, attr: @ast::MetaItem, acc: ReprA
     -> ReprAttr {
     let mut acc = acc;
     match attr.node {
-        ast::MetaList(s, ref items) if "repr" == s => {
+        ast::MetaList(ref s, ref items) if s.equiv(&("repr")) => {
             for item in items.iter() {
                 match item.node {
-                    ast::MetaWord(word) => {
-                        let hint = match word.as_slice() {
+                    ast::MetaWord(ref word) => {
+                        let hint = match word.get() {
                             // Can't use "extern" because it's not a lexical identifier.
                             "C" => ReprExtern,
-                            _ => match int_type_of_word(word) {
+                            _ => match int_type_of_word(word.get()) {
                                 Some(ity) => ReprInt(item.span, ity),
                                 None => {
                                     // Not a word we recognize
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 9ad4f4f7fac..884bd831ce8 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -228,9 +228,17 @@ pub trait AstBuilder {
 
     fn attribute(&self, sp: Span, mi: @ast::MetaItem) -> ast::Attribute;
 
-    fn meta_word(&self, sp: Span, w: @str) -> @ast::MetaItem;
-    fn meta_list(&self, sp: Span, name: @str, mis: ~[@ast::MetaItem]) -> @ast::MetaItem;
-    fn meta_name_value(&self, sp: Span, name: @str, value: ast::Lit_) -> @ast::MetaItem;
+    fn meta_word(&self, sp: Span, w: InternedString) -> @ast::MetaItem;
+    fn meta_list(&self,
+                 sp: Span,
+                 name: InternedString,
+                 mis: ~[@ast::MetaItem])
+                 -> @ast::MetaItem;
+    fn meta_name_value(&self,
+                       sp: Span,
+                       name: InternedString,
+                       value: ast::Lit_)
+                       -> @ast::MetaItem;
 
     fn view_use(&self, sp: Span,
                 vis: ast::Visibility, vp: ~[@ast::ViewPath]) -> ast::ViewItem;
@@ -866,13 +874,21 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
         })
     }
 
-    fn meta_word(&self, sp: Span, w: @str) -> @ast::MetaItem {
+    fn meta_word(&self, sp: Span, w: InternedString) -> @ast::MetaItem {
         @respan(sp, ast::MetaWord(w))
     }
-    fn meta_list(&self, sp: Span, name: @str, mis: ~[@ast::MetaItem]) -> @ast::MetaItem {
+    fn meta_list(&self,
+                 sp: Span,
+                 name: InternedString,
+                 mis: ~[@ast::MetaItem])
+                 -> @ast::MetaItem {
         @respan(sp, ast::MetaList(name, mis))
     }
-    fn meta_name_value(&self, sp: Span, name: @str, value: ast::Lit_) -> @ast::MetaItem {
+    fn meta_name_value(&self,
+                       sp: Span,
+                       name: InternedString,
+                       value: ast::Lit_)
+                       -> @ast::MetaItem {
         @respan(sp, ast::MetaNameValue(name, respan(sp, value)))
     }
 
diff --git a/src/libsyntax/ext/cfg.rs b/src/libsyntax/ext/cfg.rs
index 9af295c0b11..295c456c9d0 100644
--- a/src/libsyntax/ext/cfg.rs
+++ b/src/libsyntax/ext/cfg.rs
@@ -21,9 +21,10 @@ use ext::base;
 use ext::build::AstBuilder;
 use attr;
 use attr::*;
-use parse;
-use parse::token;
 use parse::attr::ParserAttr;
+use parse::token::InternedString;
+use parse::token;
+use parse;
 
 pub fn expand_cfg(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> base::MacResult {
     let mut p = parse::new_parser_from_tts(cx.parse_sess(),
@@ -39,7 +40,7 @@ pub fn expand_cfg(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> base::M
     }
 
     // test_cfg searches for meta items looking like `cfg(foo, ...)`
-    let in_cfg = &[cx.meta_list(sp, @"cfg", cfgs)];
+    let in_cfg = &[cx.meta_list(sp, InternedString::new("cfg"), cfgs)];
 
     let matches_cfg = attr::test_cfg(cx.cfg(), in_cfg.iter().map(|&x| x));
     let e = cx.expr_bool(sp, matches_cfg);
diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs
index 6449d0aab5e..20a515692e1 100644
--- a/src/libsyntax/ext/deriving/generic.rs
+++ b/src/libsyntax/ext/deriving/generic.rs
@@ -184,6 +184,7 @@ use ext::build::AstBuilder;
 use codemap;
 use codemap::Span;
 use opt_vec;
+use parse::token::InternedString;
 
 use std::vec;
 
@@ -396,7 +397,7 @@ impl<'a> TraitDef<'a> {
         let doc_attr = cx.attribute(
             self.span,
             cx.meta_name_value(self.span,
-                               @"doc",
+                               InternedString::new("doc"),
                                ast::LitStr(@"Automatically derived.", ast::CookedStr)));
         cx.item(
             self.span,
@@ -567,7 +568,14 @@ impl<'a> MethodDef<'a> {
         let body_block = trait_.cx.block_expr(body);
 
         let attrs = if self.inline {
-            ~[trait_.cx.attribute(trait_.span, trait_.cx.meta_word(trait_.span, @"inline"))]
+            ~[
+                trait_.cx
+                      .attribute(trait_.span,
+                                 trait_.cx
+                                       .meta_word(trait_.span,
+                                                  InternedString::new(
+                                                      "inline")))
+            ]
         } else {
             ~[]
         };
diff --git a/src/libsyntax/ext/deriving/mod.rs b/src/libsyntax/ext/deriving/mod.rs
index 652f5ebe6c7..9c487146639 100644
--- a/src/libsyntax/ext/deriving/mod.rs
+++ b/src/libsyntax/ext/deriving/mod.rs
@@ -75,12 +75,12 @@ pub fn expand_meta_deriving(cx: &ExtCtxt,
         MetaList(_, ref titems) => {
             titems.rev_iter().fold(in_items, |in_items, &titem| {
                 match titem.node {
-                    MetaNameValue(tname, _) |
-                    MetaList(tname, _) |
-                    MetaWord(tname) => {
+                    MetaNameValue(ref tname, _) |
+                    MetaList(ref tname, _) |
+                    MetaWord(ref tname) => {
                         macro_rules! expand(($func:path) => ($func(cx, titem.span,
                                                                    titem, in_items)));
-                        match tname.as_slice() {
+                        match tname.get() {
                             "Clone" => expand!(clone::expand_deriving_clone),
                             "DeepClone" => expand!(clone::expand_deriving_deep_clone),
 
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 1ffff03a80f..d98a549725c 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -221,12 +221,12 @@ pub fn expand_mod_items(module_: &ast::Mod, fld: &mut MacroExpander) -> ast::Mod
         item.attrs.rev_iter().fold(~[*item], |items, attr| {
             let mname = attr.name();
 
-            match fld.extsbox.find(&intern(mname)) {
+            match fld.extsbox.find(&intern(mname.get())) {
               Some(&ItemDecorator(dec_fn)) => {
                   fld.cx.bt_push(ExpnInfo {
                       call_site: attr.span,
                       callee: NameAndSpan {
-                          name: mname,
+                          name: mname.get().to_managed(),
                           format: MacroAttribute,
                           span: None
                       }
diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs
index bbf6f7fff7f..5730d435c15 100644
--- a/src/libsyntax/ext/format.rs
+++ b/src/libsyntax/ext/format.rs
@@ -14,9 +14,10 @@ use codemap::{Span, respan};
 use ext::base::*;
 use ext::base;
 use ext::build::AstBuilder;
-use rsparse = parse;
-use parse::token;
 use opt_vec;
+use parse::token::InternedString;
+use parse::token;
+use rsparse = parse;
 use std::fmt::parse;
 use std::hashmap::{HashMap, HashSet};
 use std::vec;
@@ -333,13 +334,18 @@ impl<'a> Context<'a> {
     fn static_attrs(&self) -> ~[ast::Attribute] {
         // Flag statics as `address_insignificant` so LLVM can merge duplicate
         // globals as much as possible (which we're generating a whole lot of).
-        let unnamed = self.ecx.meta_word(self.fmtsp, @"address_insignificant");
+        let unnamed = self.ecx
+                          .meta_word(self.fmtsp,
+                                     InternedString::new(
+                                         "address_insignificant"));
         let unnamed = self.ecx.attribute(self.fmtsp, unnamed);
 
         // Do not warn format string as dead code
-        let dead_code = self.ecx.meta_word(self.fmtsp, @"dead_code");
+        let dead_code = self.ecx.meta_word(self.fmtsp,
+                                           InternedString::new("dead_code"));
         let allow_dead_code = self.ecx.meta_list(self.fmtsp,
-                                                 @"allow", ~[dead_code]);
+                                                 InternedString::new("allow"),
+                                                 ~[dead_code]);
         let allow_dead_code = self.ecx.attribute(self.fmtsp, allow_dead_code);
         return ~[unnamed, allow_dead_code];
     }
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 8dac13f1e31..c2d54a4f368 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -321,15 +321,12 @@ fn fold_meta_item_<T: Folder>(mi: @MetaItem, fld: &mut T) -> @MetaItem {
     @Spanned {
         node:
             match mi.node {
-                MetaWord(id) => MetaWord(id),
-                MetaList(id, ref mis) => {
+                MetaWord(ref id) => MetaWord((*id).clone()),
+                MetaList(ref id, ref mis) => {
                     let fold_meta_item = |x| fold_meta_item_(x, fld);
-                    MetaList(
-                        id,
-                        mis.map(|e| fold_meta_item(*e))
-                    )
+                    MetaList((*id).clone(), mis.map(|e| fold_meta_item(*e)))
                 }
-                MetaNameValue(id, s) => MetaNameValue(id, s)
+                MetaNameValue(ref id, s) => MetaNameValue((*id).clone(), s)
             },
         span: fld.new_span(mi.span) }
 }
diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs
index e7630a66855..bc9f7e4b195 100644
--- a/src/libsyntax/parse/attr.rs
+++ b/src/libsyntax/parse/attr.rs
@@ -157,7 +157,7 @@ impl ParserAttr for Parser {
     fn parse_meta_item(&mut self) -> @ast::MetaItem {
         let lo = self.span.lo;
         let ident = self.parse_ident();
-        let name = self.id_to_str(ident);
+        let name = self.id_to_interned_str(ident);
         match self.token {
             token::EQ => {
                 self.bump();
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 642624adfb2..93264f5a6c6 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -71,10 +71,10 @@ use parse::common::{seq_sep_trailing_disallowed, seq_sep_trailing_allowed};
 use parse::lexer::Reader;
 use parse::lexer::TokenAndSpan;
 use parse::obsolete::*;
-use parse::token::{can_begin_expr, get_ident_interner, ident_to_str, is_ident};
-use parse::token::{is_ident_or_path};
-use parse::token::{is_plain_ident, INTERPOLATED, keywords, special_idents};
-use parse::token::{token_to_binop};
+use parse::token::{INTERPOLATED, InternedString, can_begin_expr, get_ident};
+use parse::token::{get_ident_interner, ident_to_str, is_ident};
+use parse::token::{is_ident_or_path, is_plain_ident, keywords};
+use parse::token::{special_idents, token_to_binop};
 use parse::token;
 use parse::{new_sub_parser_from_file, ParseSess};
 use opt_vec;
@@ -806,6 +806,10 @@ impl Parser {
         get_ident_interner().get(id.name)
     }
 
+    pub fn id_to_interned_str(&mut self, id: Ident) -> InternedString {
+        get_ident(id.name)
+    }
+
     // Is the current token one of the keywords that signals a bare function
     // type?
     pub fn token_is_bare_fn_keyword(&mut self) -> bool {
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index 68e2f44ebb1..97b9b4d53a4 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -15,8 +15,10 @@ use parse::token;
 use util::interner::StrInterner;
 use util::interner;
 
+use extra::serialize::{Decodable, Decoder, Encodable, Encoder};
 use std::cast;
 use std::char;
+use std::fmt;
 use std::local_data;
 
 #[allow(non_camel_case_types)]
@@ -525,6 +527,90 @@ pub fn get_ident_interner() -> @IdentInterner {
     }
 }
 
+/// Represents a string stored in the task-local interner. Because the
+/// interner lives for the life of the task, this can be safely treated as an
+/// immortal string, as long as it never crosses between tasks.
+///
+/// XXX(pcwalton): You must be careful about what you do in the destructors of
+/// objects stored in TLS, because they may run after the interner is
+/// destroyed. In particular, they must not access string contents. This can
+/// be fixed in the future by just leaking all strings until task death
+/// somehow.
+#[no_send]
+#[deriving(Clone, Eq, IterBytes, TotalEq, TotalOrd)]
+pub struct InternedString {
+    priv string: @str,
+}
+
+#[unsafe_destructor]
+impl Drop for InternedString {
+    fn drop(&mut self) {
+        // No-op just to make this not implicitly copyable.
+    }
+}
+
+impl InternedString {
+    #[inline]
+    pub fn new(string: &'static str) -> InternedString {
+        InternedString {
+            string: string.to_managed(),
+        }
+    }
+
+    // NB: Do not make this public. We are trying to remove `@str`.
+    #[inline]
+    fn new_from_at_str(string: @str) -> InternedString {
+        InternedString {
+            string: string,
+        }
+    }
+
+    #[inline]
+    pub fn get<'a>(&'a self) -> &'a str {
+        self.string.as_slice()
+    }
+}
+
+impl fmt::Default for InternedString {
+    fn fmt(obj: &InternedString, f: &mut fmt::Formatter) {
+        write!(f.buf, "{}", obj.string);
+    }
+}
+
+impl<'a> Equiv<&'a str> for InternedString {
+    fn equiv(&self, other: & &'a str) -> bool {
+        (*other) == self.string.as_slice()
+    }
+}
+
+impl<D:Decoder> Decodable<D> for InternedString {
+    fn decode(d: &mut D) -> InternedString {
+        let interner = get_ident_interner();
+        get_ident(interner.intern(d.read_str()))
+    }
+}
+
+impl<E:Encoder> Encodable<E> for InternedString {
+    fn encode(&self, e: &mut E) {
+        e.emit_str(self.string)
+    }
+}
+
+/// Returns the string contents of an identifier, using the task-local
+/// interner.
+#[inline]
+pub fn get_ident(idx: Name) -> InternedString {
+    let interner = get_ident_interner();
+    InternedString::new_from_at_str(interner.get(idx))
+}
+
+/// Interns and returns the string contents of an identifier, using the
+/// task-local interner.
+#[inline]
+pub fn intern_and_get_ident(s: &str) -> InternedString {
+    get_ident(intern(s))
+}
+
 /* for when we don't care about the contents; doesn't interact with TLD or
    serialization */
 pub fn mk_fake_ident_interner() -> @IdentInterner {
@@ -532,6 +618,7 @@ pub fn mk_fake_ident_interner() -> @IdentInterner {
 }
 
 // maps a string to its interned representation
+#[inline]
 pub fn intern(str : &str) -> Name {
     let interner = get_ident_interner();
     interner.intern(str)
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 2783284ea8b..89d8173f7e7 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1930,14 +1930,14 @@ pub fn print_generics(s: &mut State, generics: &ast::Generics) {
 pub fn print_meta_item(s: &mut State, item: &ast::MetaItem) {
     ibox(s, indent_unit);
     match item.node {
-      ast::MetaWord(name) => word(&mut s.s, name),
-      ast::MetaNameValue(name, value) => {
-        word_space(s, name);
+      ast::MetaWord(ref name) => word(&mut s.s, name.get()),
+      ast::MetaNameValue(ref name, value) => {
+        word_space(s, name.get());
         word_space(s, "=");
         print_literal(s, &value);
       }
-      ast::MetaList(name, ref items) => {
-        word(&mut s.s, name);
+      ast::MetaList(ref name, ref items) => {
+        word(&mut s.s, name.get());
         popen(s);
         commasep(s,
                  Consistent,
diff --git a/src/libsyntax/util/interner.rs b/src/libsyntax/util/interner.rs
index fdc54f1f140..c0fe19ede01 100644
--- a/src/libsyntax/util/interner.rs
+++ b/src/libsyntax/util/interner.rs
@@ -14,6 +14,7 @@
 
 use ast::Name;
 
+use std::cast;
 use std::cell::RefCell;
 use std::cmp::Equiv;
 use std::hashmap::HashMap;
@@ -151,6 +152,16 @@ impl StrInterner {
         vect.get()[idx]
     }
 
+    /// Returns this string with lifetime tied to the interner. Since
+    /// strings may never be removed from the interner, this is safe.
+    pub fn get_ref<'a>(&'a self, idx: Name) -> &'a str {
+        let vect = self.vect.borrow();
+        let s: &str = vect.get()[idx];
+        unsafe {
+            cast::transmute(s)
+        }
+    }
+
     pub fn len(&self) -> uint {
         let vect = self.vect.borrow();
         vect.get().len()