about summary refs log tree commit diff
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
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
-rw-r--r--src/librustc/driver/driver.rs30
-rw-r--r--src/librustc/driver/session.rs2
-rw-r--r--src/librustc/front/feature_gate.rs16
-rw-r--r--src/librustc/front/test.rs8
-rw-r--r--src/librustc/metadata/creader.rs18
-rw-r--r--src/librustc/metadata/decoder.rs6
-rw-r--r--src/librustc/metadata/encoder.rs27
-rw-r--r--src/librustc/middle/lang_items.rs2
-rw-r--r--src/librustc/middle/lint.rs42
-rw-r--r--src/librustc/middle/ty.rs2
-rw-r--r--src/librustdoc/clean.rs21
-rw-r--r--src/librustdoc/core.rs4
-rw-r--r--src/librustpkg/util.rs12
-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
27 files changed, 336 insertions, 154 deletions
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index 211d60f7e2d..2b552aff717 100644
--- a/src/librustc/driver/driver.rs
+++ b/src/librustc/driver/driver.rs
@@ -44,6 +44,7 @@ use syntax::codemap;
 use syntax::diagnostic;
 use syntax::ext::base::CrateLoader;
 use syntax::parse;
+use syntax::parse::token::InternedString;
 use syntax::parse::token;
 use syntax::print::{pp, pprust};
 use syntax;
@@ -90,22 +91,23 @@ pub fn default_configuration(sess: Session) ->
     };
 
     let fam = match sess.targ_cfg.os {
-        abi::OsWin32 => @"windows",
-        _ => @"unix"
+        abi::OsWin32 => InternedString::new("windows"),
+        _ => InternedString::new("unix")
     };
 
     let mk = attr::mk_name_value_item_str;
     return ~[ // Target bindings.
-         attr::mk_word_item(fam),
-         mk(@"target_os", tos),
-         mk(@"target_family", fam),
-         mk(@"target_arch", arch),
-         mk(@"target_endian", end),
-         mk(@"target_word_size", wordsz),
+         attr::mk_word_item(fam.clone()),
+         mk(InternedString::new("target_os"), tos),
+         mk(InternedString::new("target_family"), fam.get().to_managed()),
+         mk(InternedString::new("target_arch"), arch),
+         mk(InternedString::new("target_endian"), end),
+         mk(InternedString::new("target_word_size"), wordsz),
     ];
 }
 
-pub fn append_configuration(cfg: &mut ast::CrateConfig, name: @str) {
+pub fn append_configuration(cfg: &mut ast::CrateConfig,
+                            name: InternedString) {
     if !cfg.iter().any(|mi| mi.name() == name) {
         cfg.push(attr::mk_word_item(name))
     }
@@ -118,9 +120,15 @@ pub fn build_configuration(sess: Session) ->
     let default_cfg = default_configuration(sess);
     let mut user_cfg = sess.opts.cfg.clone();
     // If the user wants a test runner, then add the test cfg
-    if sess.opts.test { append_configuration(&mut user_cfg, @"test") }
+    if sess.opts.test {
+        append_configuration(&mut user_cfg, InternedString::new("test"))
+    }
     // If the user requested GC, then add the GC cfg
-    append_configuration(&mut user_cfg, if sess.opts.gc { @"gc" } else { @"nogc" });
+    append_configuration(&mut user_cfg, if sess.opts.gc {
+        InternedString::new("gc")
+    } else {
+        InternedString::new("nogc")
+    });
     return vec::append(user_cfg, default_cfg);
 }
 
diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs
index 5cda81836a4..57bc9c32cbd 100644
--- a/src/librustc/driver/session.rs
+++ b/src/librustc/driver/session.rs
@@ -435,7 +435,7 @@ pub fn collect_outputs(session: &Session,
     }
     let mut base = session.opts.outputs.clone();
     let mut iter = attrs.iter().filter_map(|a| {
-        if "crate_type" == a.name() {
+        if a.name().equiv(&("crate_type")) {
             match a.value_str() {
                 Some(n) if "rlib" == n => Some(OutputRlib),
                 Some(n) if "dylib" == n => Some(OutputDylib),
diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs
index bfb27594108..7903ce00dc3 100644
--- a/src/librustc/front/feature_gate.rs
+++ b/src/librustc/front/feature_gate.rs
@@ -135,7 +135,7 @@ impl Visitor<()> for Context {
 
     fn visit_item(&mut self, i: &ast::Item, _:()) {
         for attr in i.attrs.iter() {
-            if "thread_local" == attr.name() {
+            if attr.name().equiv(&("thread_local")) {
                 self.gate_feature("thread_local", i.span,
                                   "`#[thread_local]` is an experimental feature, and does not \
                                   currently handle destructors. There is no corresponding \
@@ -258,7 +258,9 @@ pub fn check_crate(sess: Session, crate: &ast::Crate) {
     };
 
     for attr in crate.attrs.iter() {
-        if "feature" != attr.name() { continue }
+        if !attr.name().equiv(&("feature")) {
+            continue
+        }
 
         match attr.meta_item_list() {
             None => {
@@ -268,14 +270,16 @@ pub fn check_crate(sess: Session, crate: &ast::Crate) {
             Some(list) => {
                 for &mi in list.iter() {
                     let name = match mi.node {
-                        ast::MetaWord(word) => word,
+                        ast::MetaWord(ref word) => (*word).clone(),
                         _ => {
-                            sess.span_err(mi.span, "malformed feature, expected \
-                                                    just one word");
+                            sess.span_err(mi.span,
+                                          "malformed feature, expected just \
+                                           one word");
                             continue
                         }
                     };
-                    match KNOWN_FEATURES.iter().find(|& &(n, _)| n == name) {
+                    match KNOWN_FEATURES.iter()
+                                        .find(|& &(n, _)| name.equiv(&n)) {
                         Some(&(name, Active)) => { cx.features.push(name); }
                         Some(&(_, Removed)) => {
                             sess.span_err(mi.span, "feature has been removed");
diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs
index 0714c1c620b..b32568abeb1 100644
--- a/src/librustc/front/test.rs
+++ b/src/librustc/front/test.rs
@@ -27,6 +27,7 @@ use syntax::ext::base::ExtCtxt;
 use syntax::fold::Folder;
 use syntax::fold;
 use syntax::opt_vec;
+use syntax::parse::token::InternedString;
 use syntax::print::pprust;
 use syntax::{ast, ast_util};
 use syntax::util::small_vector::SmallVector;
@@ -132,7 +133,7 @@ impl<'a> fold::Folder for TestHarnessGenerator<'a> {
             if !cx.sess.building_library.get() {
                 @ast::Item {
                     attrs: item.attrs.iter().filter_map(|attr| {
-                        if "main" != attr.name() {
+                        if attr.name().equiv(&("main")) {
                             Some(*attr)
                         } else {
                             None
@@ -248,7 +249,7 @@ fn is_bench_fn(i: @ast::Item) -> bool {
 fn is_ignored(cx: &TestCtxt, i: @ast::Item) -> bool {
     i.attrs.iter().any(|attr| {
         // check ignore(cfg(foo, bar))
-        "ignore" == attr.name() && match attr.meta_item_list() {
+        attr.name().equiv(&("ignore")) && match attr.meta_item_list() {
             Some(ref cfgs) => attr::test_cfg(cx.config, cfgs.iter().map(|x| *x)),
             None => true
         }
@@ -330,8 +331,9 @@ fn mk_test_module(cx: &TestCtxt) -> @ast::Item {
     let item_ = ast::ItemMod(testmod);
 
     // This attribute tells resolve to let us call unexported functions
+    let resolve_unexported_str = InternedString::new("!resolve_unexported");
     let resolve_unexported_attr =
-        attr::mk_attr(attr::mk_word_item(@"!resolve_unexported"));
+        attr::mk_attr(attr::mk_word_item(resolve_unexported_str));
 
     let item = ast::Item {
         ident: cx.sess.ident_of("__test"),
diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs
index fa2e94b6f8f..4670757b5c3 100644
--- a/src/librustc/metadata/creader.rs
+++ b/src/librustc/metadata/creader.rs
@@ -124,7 +124,7 @@ struct Env {
 fn visit_crate(e: &Env, c: &ast::Crate) {
     let cstore = e.sess.cstore;
 
-    for a in c.attrs.iter().filter(|m| "link_args" == m.name()) {
+    for a in c.attrs.iter().filter(|m| m.name().equiv(&("link_args"))) {
         match a.value_str() {
           Some(ref linkarg) => {
             cstore.add_used_link_args(*linkarg);
@@ -206,7 +206,11 @@ fn visit_item(e: &Env, i: &ast::Item) {
             // First, add all of the custom link_args attributes
             let cstore = e.sess.cstore;
             let link_args = i.attrs.iter()
-                .filter_map(|at| if "link_args" == at.name() {Some(at)} else {None})
+                .filter_map(|at| if at.name().equiv(&("link_args")) {
+                    Some(at)
+                } else {
+                    None
+                })
                 .to_owned_vec();
             for m in link_args.iter() {
                 match m.value_str() {
@@ -220,13 +224,17 @@ fn visit_item(e: &Env, i: &ast::Item) {
             // Next, process all of the #[link(..)]-style arguments
             let cstore = e.sess.cstore;
             let link_args = i.attrs.iter()
-                .filter_map(|at| if "link" == at.name() {Some(at)} else {None})
+                .filter_map(|at| if at.name().equiv(&("link")) {
+                    Some(at)
+                } else {
+                    None
+                })
                 .to_owned_vec();
             for m in link_args.iter() {
                 match m.meta_item_list() {
                     Some(items) => {
                         let kind = items.iter().find(|k| {
-                            "kind" == k.name()
+                            k.name().equiv(&("kind"))
                         }).and_then(|a| a.value_str());
                         let kind = match kind {
                             Some(k) => {
@@ -249,7 +257,7 @@ fn visit_item(e: &Env, i: &ast::Item) {
                             None => cstore::NativeUnknown
                         };
                         let n = items.iter().find(|n| {
-                            "name" == n.name()
+                            n.name().equiv(&("name"))
                         }).and_then(|a| a.value_str());
                         let n = match n {
                             Some(n) => n,
diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs
index 11fab9cced7..ef6bb30cb51 100644
--- a/src/librustc/metadata/decoder.rs
+++ b/src/librustc/metadata/decoder.rs
@@ -1042,14 +1042,14 @@ fn get_meta_items(md: ebml::Doc) -> ~[@ast::MetaItem] {
     let mut items: ~[@ast::MetaItem] = ~[];
     reader::tagged_docs(md, tag_meta_item_word, |meta_item_doc| {
         let nd = reader::get_doc(meta_item_doc, tag_meta_item_name);
-        let n = nd.as_str_slice().to_managed();
+        let n = token::intern_and_get_ident(nd.as_str_slice());
         items.push(attr::mk_word_item(n));
         true
     });
     reader::tagged_docs(md, tag_meta_item_name_value, |meta_item_doc| {
         let nd = reader::get_doc(meta_item_doc, tag_meta_item_name);
         let vd = reader::get_doc(meta_item_doc, tag_meta_item_value);
-        let n = nd.as_str_slice().to_managed();
+        let n = token::intern_and_get_ident(nd.as_str_slice());
         let v = vd.as_str_slice().to_managed();
         // FIXME (#623): Should be able to decode MetaNameValue variants,
         // but currently the encoder just drops them
@@ -1058,7 +1058,7 @@ fn get_meta_items(md: ebml::Doc) -> ~[@ast::MetaItem] {
     });
     reader::tagged_docs(md, tag_meta_item_list, |meta_item_doc| {
         let nd = reader::get_doc(meta_item_doc, tag_meta_item_name);
-        let n = nd.as_str_slice().to_managed();
+        let n = token::intern_and_get_ident(nd.as_str_slice());
         let subitems = get_meta_items(meta_item_doc);
         items.push(attr::mk_list_item(n, subitems));
         true
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index ac3ee78fb86..94a4639975c 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -21,29 +21,28 @@ use middle::ty;
 use middle::typeck;
 use middle;
 
+use extra::serialize::Encodable;
 use std::cast;
 use std::cell::{Cell, RefCell};
 use std::hashmap::{HashMap, HashSet};
 use std::io::MemWriter;
 use std::str;
 use std::vec;
-
-use extra::serialize::Encodable;
-
 use syntax::abi::AbiSet;
 use syntax::ast::*;
 use syntax::ast;
 use syntax::ast_map;
 use syntax::ast_util::*;
-use syntax::attr;
+use syntax::ast_util;
 use syntax::attr::AttrMetaMethods;
+use syntax::attr;
 use syntax::codemap;
 use syntax::diagnostic::SpanHandler;
+use syntax::parse::token::InternedString;
 use syntax::parse::token::special_idents;
-use syntax::ast_util;
+use syntax::parse::token;
 use syntax::visit::Visitor;
 use syntax::visit;
-use syntax::parse::token;
 use syntax;
 use writer = extra::ebml::writer;
 
@@ -1507,19 +1506,19 @@ fn write_i64(writer: &mut MemWriter, &n: &i64) {
 
 fn encode_meta_item(ebml_w: &mut writer::Encoder, mi: @MetaItem) {
     match mi.node {
-      MetaWord(name) => {
+      MetaWord(ref name) => {
         ebml_w.start_tag(tag_meta_item_word);
         ebml_w.start_tag(tag_meta_item_name);
-        ebml_w.writer.write(name.as_bytes());
+        ebml_w.writer.write(name.get().as_bytes());
         ebml_w.end_tag();
         ebml_w.end_tag();
       }
-      MetaNameValue(name, value) => {
+      MetaNameValue(ref name, value) => {
         match value.node {
           LitStr(value, _) => {
             ebml_w.start_tag(tag_meta_item_name_value);
             ebml_w.start_tag(tag_meta_item_name);
-            ebml_w.writer.write(name.as_bytes());
+            ebml_w.writer.write(name.get().as_bytes());
             ebml_w.end_tag();
             ebml_w.start_tag(tag_meta_item_value);
             ebml_w.writer.write(value.as_bytes());
@@ -1529,10 +1528,10 @@ fn encode_meta_item(ebml_w: &mut writer::Encoder, mi: @MetaItem) {
           _ => {/* FIXME (#623): encode other variants */ }
         }
       }
-      MetaList(name, ref items) => {
+      MetaList(ref name, ref items) => {
         ebml_w.start_tag(tag_meta_item_list);
         ebml_w.start_tag(tag_meta_item_name);
-        ebml_w.writer.write(name.as_bytes());
+        ebml_w.writer.write(name.get().as_bytes());
         ebml_w.end_tag();
         for inner_item in items.iter() {
             encode_meta_item(ebml_w, *inner_item);
@@ -1563,13 +1562,13 @@ fn synthesize_crate_attrs(ecx: &EncodeContext,
 
         attr::mk_attr(
             attr::mk_name_value_item_str(
-                @"crate_id",
+                InternedString::new("crate_id"),
                 ecx.link_meta.crateid.to_str().to_managed()))
     }
 
     let mut attrs = ~[];
     for attr in crate.attrs.iter() {
-        if "crate_id" != attr.name()  {
+        if !attr.name().equiv(&("crate_id")) {
             attrs.push(*attr);
         }
     }
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index 82c927a1c2d..34f8bad9666 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -185,7 +185,7 @@ impl LanguageItemCollector {
 pub fn extract(attrs: &[ast::Attribute]) -> Option<@str> {
     for attribute in attrs.iter() {
         match attribute.name_str_pair() {
-            Some((key, value)) if "lang" == key => {
+            Some((ref key, value)) if key.equiv(&("lang")) => {
                 return Some(value);
             }
             Some(..) | None => {}
diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs
index 7cb549d91a8..3dfa186e4a9 100644
--- a/src/librustc/middle/lint.rs
+++ b/src/librustc/middle/lint.rs
@@ -34,18 +34,17 @@
 //! Context itself, span_lint should be used instead of add_lint.
 
 use driver::session;
+use metadata::csearch;
 use middle::dead::DEAD_CODE_LINT_STR;
+use middle::pat_util;
 use middle::privacy;
 use middle::trans::adt; // for `adt::is_ffi_safe`
 use middle::ty;
+use middle::typeck::astconv::{ast_ty_to_ty, AstConv};
+use middle::typeck::infer;
 use middle::typeck;
-use middle::pat_util;
-use metadata::csearch;
-use util::ppaux::{ty_to_str};
 use std::to_str::ToStr;
-
-use middle::typeck::infer;
-use middle::typeck::astconv::{ast_ty_to_ty, AstConv};
+use util::ppaux::{ty_to_str};
 
 use std::cmp;
 use std::hashmap::HashMap;
@@ -59,13 +58,14 @@ use std::u64;
 use std::u8;
 use extra::smallintmap::SmallIntMap;
 use syntax::ast_map;
-use syntax::attr;
+use syntax::ast_util::IdVisitingOperation;
 use syntax::attr::{AttrMetaMethods, AttributeMethods};
+use syntax::attr;
 use syntax::codemap::Span;
+use syntax::parse::token::InternedString;
 use syntax::parse::token;
-use syntax::{ast, ast_util, visit};
-use syntax::ast_util::IdVisitingOperation;
 use syntax::visit::Visitor;
+use syntax::{ast, ast_util, visit};
 
 #[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
 pub enum Lint {
@@ -540,10 +540,16 @@ impl<'a> Context<'a> {
         });
 
         let old_is_doc_hidden = self.is_doc_hidden;
-        self.is_doc_hidden = self.is_doc_hidden ||
-            attrs.iter().any(|attr| ("doc" == attr.name() && match attr.meta_item_list()
-                                     { None => false,
-                                       Some(l) => attr::contains_name(l, "hidden") }));
+        self.is_doc_hidden =
+            self.is_doc_hidden ||
+            attrs.iter()
+                 .any(|attr| {
+                     attr.name().equiv(&("doc")) &&
+                     match attr.meta_item_list() {
+                         None => false,
+                         Some(l) => attr::contains_name(l, "hidden")
+                     }
+                 });
 
         f(self);
 
@@ -569,12 +575,12 @@ impl<'a> Context<'a> {
 // Return true if that's the case. Otherwise return false.
 pub fn each_lint(sess: session::Session,
                  attrs: &[ast::Attribute],
-                 f: |@ast::MetaItem, level, @str| -> bool)
+                 f: |@ast::MetaItem, level, InternedString| -> bool)
                  -> bool {
     let xs = [allow, warn, deny, forbid];
     for &level in xs.iter() {
         let level_name = level_to_str(level);
-        for attr in attrs.iter().filter(|m| level_name == m.name()) {
+        for attr in attrs.iter().filter(|m| m.name().equiv(&level_name)) {
             let meta = attr.node.value;
             let metas = match meta.node {
                 ast::MetaList(_, ref metas) => metas,
@@ -585,8 +591,8 @@ pub fn each_lint(sess: session::Session,
             };
             for meta in metas.iter() {
                 match meta.node {
-                    ast::MetaWord(lintname) => {
-                        if !f(*meta, level, lintname) {
+                    ast::MetaWord(ref lintname) => {
+                        if !f(*meta, level, (*lintname).clone()) {
                             return false;
                         }
                     }
@@ -1314,7 +1320,7 @@ fn check_missing_doc_attrs(cx: &Context,
 
     let has_doc = attrs.iter().any(|a| {
         match a.node.value.node {
-            ast::MetaNameValue(ref name, _) if "doc" == *name => true,
+            ast::MetaNameValue(ref name, _) if name.equiv(&("doc")) => true,
             _ => false
         }
     });
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 9062949b000..ba9dea28cbc 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -4165,7 +4165,7 @@ pub fn each_attr(tcx: ctxt, did: DefId, f: |@MetaItem| -> bool) -> bool {
 pub fn has_attr(tcx: ctxt, did: DefId, attr: &str) -> bool {
     let mut found = false;
     each_attr(tcx, did, |item| {
-        if attr == item.name() {
+        if item.name().equiv(&attr) {
             found = true;
             false
         } else {
diff --git a/src/librustdoc/clean.rs b/src/librustdoc/clean.rs
index e86122fb7d1..564fb465c02 100644
--- a/src/librustdoc/clean.rs
+++ b/src/librustdoc/clean.rs
@@ -20,6 +20,8 @@ use syntax::ast_util;
 use syntax::attr;
 use syntax::attr::AttributeMethods;
 use syntax::codemap::Pos;
+use syntax::parse::token::InternedString;
+use syntax::parse::token;
 
 use rustc::metadata::cstore;
 use rustc::metadata::csearch;
@@ -223,9 +225,13 @@ pub enum Attribute {
 impl Clean<Attribute> for ast::MetaItem {
     fn clean(&self) -> Attribute {
         match self.node {
-            ast::MetaWord(s) => Word(s.to_owned()),
-            ast::MetaList(ref s, ref l) => List(s.to_owned(), l.clean()),
-            ast::MetaNameValue(s, ref v) => NameValue(s.to_owned(), lit_to_str(v))
+            ast::MetaWord(ref s) => Word(s.get().to_owned()),
+            ast::MetaList(ref s, ref l) => {
+                List(s.get().to_owned(), l.clean())
+            }
+            ast::MetaNameValue(ref s, ref v) => {
+                NameValue(s.get().to_owned(), lit_to_str(v))
+            }
         }
     }
 }
@@ -238,10 +244,11 @@ impl Clean<Attribute> for ast::Attribute {
 
 // This is a rough approximation that gets us what we want.
 impl<'a> attr::AttrMetaMethods for &'a Attribute {
-    fn name(&self) -> @str {
+    fn name(&self) -> InternedString {
         match **self {
-            Word(ref n) | List(ref n, _) | NameValue(ref n, _) =>
-                n.to_managed()
+            Word(ref n) | List(ref n, _) | NameValue(ref n, _) => {
+                token::intern_and_get_ident(*n)
+            }
         }
     }
 
@@ -252,7 +259,7 @@ impl<'a> attr::AttrMetaMethods for &'a Attribute {
         }
     }
     fn meta_item_list<'a>(&'a self) -> Option<&'a [@ast::MetaItem]> { None }
-    fn name_str_pair(&self) -> Option<(@str, @str)> { None }
+    fn name_str_pair(&self) -> Option<(InternedString, @str)> { None }
 }
 
 #[deriving(Clone, Encodable, Decodable)]
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index e4260e367a8..0e2d6c972ae 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -15,6 +15,7 @@ use rustc::middle::privacy;
 
 use syntax::ast;
 use syntax::diagnostic;
+use syntax::parse::token;
 use syntax::parse;
 use syntax;
 
@@ -71,7 +72,8 @@ fn get_ast_and_resolve(cpath: &Path,
 
     let mut cfg = build_configuration(sess);
     for cfg_ in cfgs.move_iter() {
-        cfg.push(@dummy_spanned(ast::MetaWord(cfg_.to_managed())));
+        let cfg_ = token::intern_and_get_ident(cfg_);
+        cfg.push(@dummy_spanned(ast::MetaWord(cfg_)));
     }
 
     let crate = phase_1_parse_input(sess, cfg.clone(), &input);
diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs
index 1f8962fbd3a..3f1762009bc 100644
--- a/src/librustpkg/util.rs
+++ b/src/librustpkg/util.rs
@@ -30,6 +30,7 @@ use syntax::ext::base::{ExtCtxt, MacroCrate};
 use syntax::{ast, attr, codemap, diagnostic, fold, visit};
 use syntax::attr::AttrMetaMethods;
 use syntax::fold::Folder;
+use syntax::parse::token::InternedString;
 use syntax::visit::Visitor;
 use syntax::util::small_vector::SmallVector;
 use syntax::crateid::CrateId;
@@ -77,7 +78,7 @@ fn fold_mod(m: &ast::Mod, fold: &mut CrateSetup) -> ast::Mod {
     fn strip_main(item: @ast::Item) -> @ast::Item {
         @ast::Item {
             attrs: item.attrs.iter().filter_map(|attr| {
-                if "main" != attr.name() {
+                if !attr.name().equiv(&("main")) {
                     Some(*attr)
                 } else {
                     None
@@ -101,13 +102,15 @@ fn fold_item(item: @ast::Item, fold: &mut CrateSetup)
     let mut had_pkg_do = false;
 
     for attr in item.attrs.iter() {
-        if "pkg_do" == attr.name() {
+        if attr.name().equiv(&("pkg_do")) {
             had_pkg_do = true;
             match attr.node.value.node {
                 ast::MetaList(_, ref mis) => {
                     for mi in mis.iter() {
                         match mi.node {
-                            ast::MetaWord(cmd) => cmds.push(cmd.to_owned()),
+                            ast::MetaWord(ref cmd) => {
+                                cmds.push(cmd.get().to_owned())
+                            }
                             _ => {}
                         };
                     }
@@ -314,7 +317,8 @@ pub fn compile_input(context: &BuildContext,
     if !attr::contains_name(crate.attrs, "crate_id") {
         // FIXME (#9639): This needs to handle non-utf8 paths
         let crateid_attr =
-            attr::mk_name_value_item_str(@"crate_id", crate_id.to_str().to_managed());
+            attr::mk_name_value_item_str(InternedString::new("crate_id"),
+                                         crate_id.to_str().to_managed());
 
         debug!("crateid attr: {:?}", crateid_attr);
         crate.attrs.push(attr::mk_attr(crateid_attr));
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()