about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-12-13 11:09:55 +0000
committerbors <bors@rust-lang.org>2017-12-13 11:09:55 +0000
commit3dfbc88a626625be01e112da11ec367e2fc71bb3 (patch)
tree3123349d2f450ac5317944d5f5803b20c3eca65b /src
parent61100840e5c978a99b0489e8eaa922da06c05f65 (diff)
parent85d19b33357897c51d80727a4208f46b19c5c5a6 (diff)
downloadrust-3dfbc88a626625be01e112da11ec367e2fc71bb3.tar.gz
rust-3dfbc88a626625be01e112da11ec367e2fc71bb3.zip
Auto merge of #46550 - jseyfried:cleanup_builtin_hygiene, r=nrc
macros: hygienize use of `core`/`std` in builtin macros

Today, if a builtin macro wants to access an item from `core` or `std` (depending `#![no_std]`), it generates `::core::path::to::item` or `::std::path::to::item` respectively (c.f. `fn std_path()` in `libsyntax/ext/base.rs`).

This PR refactors the builtin macros to instead always emit `$crate::path::to::item` here. That is, the def site of builtin macros is taken to be in `extern crate core;` or `extern crate std;`. Since builtin macros are macros 1.0 (i.e. mostly unhygienic), changing the def site can only effect the resolution of `$crate`.

r? @nrc
Diffstat (limited to 'src')
-rw-r--r--src/librustc/hir/lowering.rs2
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs8
-rw-r--r--src/librustc_resolve/lib.rs3
-rw-r--r--src/librustc_resolve/macros.rs11
-rw-r--r--src/libsyntax/ext/base.rs15
-rw-r--r--src/libsyntax/ext/build.rs5
-rw-r--r--src/libsyntax/ext/expand.rs2
-rw-r--r--src/libsyntax/print/pprust.rs20
-rw-r--r--src/libsyntax/std_inject.rs26
-rw-r--r--src/libsyntax/test.rs3
-rw-r--r--src/libsyntax_ext/deriving/bounds.rs9
-rw-r--r--src/libsyntax_ext/deriving/clone.rs5
-rw-r--r--src/libsyntax_ext/deriving/cmp/eq.rs3
-rw-r--r--src/libsyntax_ext/deriving/cmp/ord.rs5
-rw-r--r--src/libsyntax_ext/deriving/cmp/partial_eq.rs3
-rw-r--r--src/libsyntax_ext/deriving/cmp/partial_ord.rs9
-rw-r--r--src/libsyntax_ext/deriving/debug.rs7
-rw-r--r--src/libsyntax_ext/deriving/decodable.rs20
-rw-r--r--src/libsyntax_ext/deriving/default.rs3
-rw-r--r--src/libsyntax_ext/deriving/encodable.rs24
-rw-r--r--src/libsyntax_ext/deriving/generic/ty.rs38
-rw-r--r--src/libsyntax_ext/deriving/hash.rs6
-rw-r--r--src/libsyntax_ext/deriving/mod.rs30
-rw-r--r--src/libsyntax_ext/lib.rs1
-rw-r--r--src/libsyntax_pos/hygiene.rs42
-rw-r--r--src/test/pretty/issue-4264.pp2
-rw-r--r--src/test/run-pass-fulldeps/auxiliary/custom_derive_partial_eq.rs2
-rw-r--r--src/test/run-pass-fulldeps/auxiliary/custom_derive_plugin.rs2
-rw-r--r--src/test/run-pass-fulldeps/auxiliary/custom_derive_plugin_attr.rs2
-rw-r--r--src/test/run-pass-fulldeps/derive-no-std-not-supported.rs (renamed from src/test/compile-fail-fulldeps/derive-no-std-not-supported.rs)6
30 files changed, 174 insertions, 140 deletions
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index de69fad6dd7..e3a5e835bb9 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -175,7 +175,7 @@ pub fn lower_crate(sess: &Session,
     let _ignore = dep_graph.in_ignore();
 
     LoweringContext {
-        crate_root: std_inject::injected_crate_name(krate),
+        crate_root: std_inject::injected_crate_name(),
         sess,
         cstore,
         parent_def: None,
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index 33fe432f067..8df6458b72e 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -40,6 +40,7 @@ use syntax::ext::base::Determinacy::Undetermined;
 use syntax::ext::hygiene::Mark;
 use syntax::ext::tt::macro_rules;
 use syntax::parse::token::{self, Token};
+use syntax::std_inject::injected_crate_name;
 use syntax::symbol::keywords;
 use syntax::symbol::Symbol;
 use syntax::visit::{self, Visitor};
@@ -262,6 +263,10 @@ impl<'a> Resolver<'a> {
                 let module =
                     self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX });
                 self.populate_module_if_necessary(module);
+                if injected_crate_name().map_or(false, |name| item.ident.name == name) {
+                    self.injected_crate = Some(module);
+                }
+
                 let used = self.process_legacy_macro_imports(item, module, expansion);
                 let binding =
                     (module, ty::Visibility::Public, sp, expansion).to_name_binding(self.arenas);
@@ -558,8 +563,7 @@ impl<'a> Resolver<'a> {
         if let Some(id) = self.definitions.as_local_node_id(def_id) {
             self.local_macro_def_scopes[&id]
         } else if def_id.krate == BUILTIN_MACROS_CRATE {
-            // FIXME(jseyfried): This happens when `include!()`ing a `$crate::` path, c.f, #40469.
-            self.graph_root
+            self.injected_crate.unwrap_or(self.graph_root)
         } else {
             let module_def_id = ty::DefIdTree::parent(&*self, def_id).unwrap();
             self.get_module(module_def_id)
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 30b808fb9d7..33e57b2d180 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -1338,6 +1338,8 @@ pub struct Resolver<'a> {
 
     // Only used for better errors on `fn(): fn()`
     current_type_ascription: Vec<Span>,
+
+    injected_crate: Option<Module<'a>>,
 }
 
 pub struct ResolverArenas<'a> {
@@ -1537,6 +1539,7 @@ impl<'a> Resolver<'a> {
             found_unresolved_macro: false,
             unused_macros: FxHashSet(),
             current_type_ascription: Vec::new(),
+            injected_crate: None,
         }
     }
 
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index f2162f2321f..260a0cd7cd7 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -25,7 +25,7 @@ use syntax::errors::DiagnosticBuilder;
 use syntax::ext::base::{self, Annotatable, Determinacy, MultiModifier, MultiDecorator};
 use syntax::ext::base::{MacroKind, SyntaxExtension, Resolver as SyntaxResolver};
 use syntax::ext::expand::{Expansion, ExpansionKind, Invocation, InvocationKind, find_attr_invoc};
-use syntax::ext::hygiene::Mark;
+use syntax::ext::hygiene::{Mark, MarkKind};
 use syntax::ext::placeholders::placeholder;
 use syntax::ext::tt::macro_rules;
 use syntax::feature_gate::{self, emit_feature_err, GateIssue};
@@ -297,16 +297,19 @@ impl<'a> base::Resolver for Resolver<'a> {
             InvocationKind::Attr { attr: None, .. } => return Ok(None),
             _ => self.resolve_invoc_to_def(invoc, scope, force)?,
         };
+        let def_id = def.def_id();
 
-        self.macro_defs.insert(invoc.expansion_data.mark, def.def_id());
+        self.macro_defs.insert(invoc.expansion_data.mark, def_id);
         let normal_module_def_id =
             self.macro_def_scope(invoc.expansion_data.mark).normal_ancestor_id;
         self.definitions.add_macro_def_scope(invoc.expansion_data.mark, normal_module_def_id);
 
-        self.unused_macros.remove(&def.def_id());
+        self.unused_macros.remove(&def_id);
         let ext = self.get_macro(def);
         if ext.is_modern() {
-            invoc.expansion_data.mark.set_modern();
+            invoc.expansion_data.mark.set_kind(MarkKind::Modern);
+        } else if def_id.krate == BUILTIN_MACROS_CRATE {
+            invoc.expansion_data.mark.set_kind(MarkKind::Builtin);
         }
         Ok(Some(ext))
     }
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 6c96692f719..bb1b7da7dba 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -21,10 +21,11 @@ use fold::{self, Folder};
 use parse::{self, parser, DirectoryOwnership};
 use parse::token;
 use ptr::P;
-use symbol::Symbol;
+use symbol::{keywords, Ident, Symbol};
 use util::small_vector::SmallVector;
 
 use std::collections::HashMap;
+use std::iter;
 use std::path::PathBuf;
 use std::rc::Rc;
 use std::default::Default;
@@ -664,7 +665,6 @@ pub struct ExpansionData {
 pub struct ExtCtxt<'a> {
     pub parse_sess: &'a parse::ParseSess,
     pub ecfg: expand::ExpansionConfig<'a>,
-    pub crate_root: Option<&'static str>,
     pub root_path: PathBuf,
     pub resolver: &'a mut Resolver,
     pub resolve_err_count: usize,
@@ -680,7 +680,6 @@ impl<'a> ExtCtxt<'a> {
         ExtCtxt {
             parse_sess,
             ecfg,
-            crate_root: None,
             root_path: PathBuf::new(),
             resolver,
             resolve_err_count: 0,
@@ -822,12 +821,10 @@ impl<'a> ExtCtxt<'a> {
         ast::Ident::from_str(st)
     }
     pub fn std_path(&self, components: &[&str]) -> Vec<ast::Ident> {
-        let mut v = Vec::new();
-        if let Some(s) = self.crate_root {
-            v.push(self.ident_of(s));
-        }
-        v.extend(components.iter().map(|s| self.ident_of(s)));
-        v
+        let def_site = SyntaxContext::empty().apply_mark(self.current_expansion.mark);
+        iter::once(Ident { ctxt: def_site, ..keywords::DollarCrate.ident() })
+            .chain(components.iter().map(|s| self.ident_of(s)))
+            .collect()
     }
     pub fn name_of(&self, st: &str) -> ast::Name {
         Symbol::intern(st)
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 25eef6db930..82e7747b014 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -319,9 +319,12 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
                 types: Vec<P<ast::Ty>>,
                 bindings: Vec<ast::TypeBinding> )
                 -> ast::Path {
+        use syntax::parse::token;
+
         let last_identifier = idents.pop().unwrap();
         let mut segments: Vec<ast::PathSegment> = Vec::new();
-        if global {
+        if global &&
+           !idents.first().map_or(false, |&ident| token::Ident(ident).is_path_segment_keyword()) {
             segments.push(ast::PathSegment::crate_root(span));
         }
 
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 0d1b1c65a29..ecb396f259f 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -25,7 +25,6 @@ use parse::{DirectoryOwnership, PResult};
 use parse::token::{self, Token};
 use parse::parser::Parser;
 use ptr::P;
-use std_inject;
 use symbol::Symbol;
 use symbol::keywords;
 use syntax_pos::{Span, DUMMY_SP};
@@ -219,7 +218,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
     }
 
     pub fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate {
-        self.cx.crate_root = std_inject::injected_crate_name(&krate);
         let mut module = ModuleData {
             mod_path: vec![Ident::from_str(&self.cx.ecfg.crate_name)],
             directory: self.cx.codemap().span_to_unmapped_path(krate.span),
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index a2d3ed4deb6..17f37d0f2c0 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -18,6 +18,7 @@ use util::parser::{self, AssocOp, Fixity};
 use attr;
 use codemap::{self, CodeMap};
 use syntax_pos::{self, BytePos};
+use syntax_pos::hygiene::{Mark, MarkKind, SyntaxContext};
 use parse::token::{self, BinOpToken, Token};
 use parse::lexer::comments;
 use parse::{self, ParseSess};
@@ -93,7 +94,7 @@ pub fn print_crate<'a>(cm: &'a CodeMap,
                        is_expanded: bool) -> io::Result<()> {
     let mut s = State::new_from_input(cm, sess, filename, input, out, ann, is_expanded);
 
-    if is_expanded && !std_inject::injected_crate_name(krate).is_none() {
+    if is_expanded && !std_inject::injected_crate_name().is_none() {
         // We need to print `#![no_std]` (and its feature gate) so that
         // compiling pretty-printed source won't inject libstd again.
         // However we don't want these attributes in the AST because
@@ -734,6 +735,8 @@ pub trait PrintState<'a> {
                     if segment.identifier.name != keywords::CrateRoot.name() &&
                        segment.identifier.name != keywords::DollarCrate.name() {
                         self.writer().word(&segment.identifier.name.as_str())?;
+                    } else if segment.identifier.name == keywords::DollarCrate.name() {
+                        self.print_dollar_crate(segment.identifier.ctxt)?;
                     }
                 }
                 self.writer().space()?;
@@ -822,6 +825,19 @@ pub trait PrintState<'a> {
     }
 
     fn nbsp(&mut self) -> io::Result<()> { self.writer().word(" ") }
+
+    fn print_dollar_crate(&mut self, mut ctxt: SyntaxContext) -> io::Result<()> {
+        if let Some(mark) = ctxt.adjust(Mark::root()) {
+            // Make a best effort to print something that complies
+            if mark.kind() == MarkKind::Builtin {
+                if let Some(name) = std_inject::injected_crate_name() {
+                    self.writer().word("::")?;
+                    self.writer().word(name)?;
+                }
+            }
+        }
+        Ok(())
+    }
 }
 
 impl<'a> PrintState<'a> for State<'a> {
@@ -2411,6 +2427,8 @@ impl<'a> State<'a> {
             if let Some(ref parameters) = segment.parameters {
                 self.print_path_parameters(parameters, colons_before_params)?;
             }
+        } else if segment.identifier.name == keywords::DollarCrate.name() {
+            self.print_dollar_crate(segment.identifier.ctxt)?;
         }
         Ok(())
     }
diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs
index ae22230198f..00546400bb5 100644
--- a/src/libsyntax/std_inject.rs
+++ b/src/libsyntax/std_inject.rs
@@ -10,6 +10,7 @@
 
 use ast;
 use attr;
+use std::cell::Cell;
 use ext::hygiene::{Mark, SyntaxContext};
 use symbol::{Symbol, keywords};
 use syntax_pos::{DUMMY_SP, Span};
@@ -34,22 +35,25 @@ fn ignored_span(sp: Span) -> Span {
     sp.with_ctxt(SyntaxContext::empty().apply_mark(mark))
 }
 
-pub fn injected_crate_name(krate: &ast::Crate) -> Option<&'static str> {
-    if attr::contains_name(&krate.attrs, "no_core") {
-        None
-    } else if attr::contains_name(&krate.attrs, "no_std") {
-        Some("core")
-    } else {
-        Some("std")
-    }
+pub fn injected_crate_name() -> Option<&'static str> {
+    INJECTED_CRATE_NAME.with(|name| name.get())
+}
+
+thread_local! {
+    static INJECTED_CRATE_NAME: Cell<Option<&'static str>> = Cell::new(None);
 }
 
 pub fn maybe_inject_crates_ref(mut krate: ast::Crate, alt_std_name: Option<String>) -> ast::Crate {
-    let name = match injected_crate_name(&krate) {
-        Some(name) => name,
-        None => return krate,
+    let name = if attr::contains_name(&krate.attrs, "no_core") {
+        return krate;
+    } else if attr::contains_name(&krate.attrs, "no_std") {
+        "core"
+    } else {
+        "std"
     };
 
+    INJECTED_CRATE_NAME.with(|opt_name| opt_name.set(Some(name)));
+
     let crate_name = Symbol::intern(&alt_std_name.unwrap_or_else(|| name.to_string()));
 
     krate.module.items.insert(0, P(ast::Item {
diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs
index a4ac5826f99..9f097169d97 100644
--- a/src/libsyntax/test.rs
+++ b/src/libsyntax/test.rs
@@ -272,7 +272,7 @@ fn generate_test_harness(sess: &ParseSess,
 
     let mark = Mark::fresh(Mark::root());
 
-    let mut cx: TestCtxt = TestCtxt {
+    let cx = TestCtxt {
         span_diagnostic: sd,
         ext_cx: ExtCtxt::new(sess, ExpansionConfig::default("test".to_string()), resolver),
         path: Vec::new(),
@@ -283,7 +283,6 @@ fn generate_test_harness(sess: &ParseSess,
         toplevel_reexport: None,
         ctxt: SyntaxContext::empty().apply_mark(mark),
     };
-    cx.ext_cx.crate_root = Some("std");
 
     mark.set_expn_info(ExpnInfo {
         call_site: DUMMY_SP,
diff --git a/src/libsyntax_ext/deriving/bounds.rs b/src/libsyntax_ext/deriving/bounds.rs
index 0bc802283fb..7f03001d9c6 100644
--- a/src/libsyntax_ext/deriving/bounds.rs
+++ b/src/libsyntax_ext/deriving/bounds.rs
@@ -8,9 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use deriving::path_std;
 use deriving::generic::*;
 use deriving::generic::ty::*;
-
 use syntax::ast::MetaItem;
 use syntax::ext::base::{Annotatable, ExtCtxt};
 use syntax_pos::Span;
@@ -28,15 +28,10 @@ pub fn expand_deriving_copy(cx: &mut ExtCtxt,
                             mitem: &MetaItem,
                             item: &Annotatable,
                             push: &mut FnMut(Annotatable)) {
-    let mut v = cx.crate_root.map(|s| vec![s]).unwrap_or(Vec::new());
-    v.push("marker");
-    v.push("Copy");
-    let path = Path::new(v);
-
     let trait_def = TraitDef {
         span,
         attributes: Vec::new(),
-        path,
+        path: path_std!(cx, marker::Copy),
         additional_bounds: Vec::new(),
         generics: LifetimeBounds::empty(),
         is_unsafe: false,
diff --git a/src/libsyntax_ext/deriving/clone.rs b/src/libsyntax_ext/deriving/clone.rs
index 5d93c2a5f72..35def632fc1 100644
--- a/src/libsyntax_ext/deriving/clone.rs
+++ b/src/libsyntax_ext/deriving/clone.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use deriving::path_std;
 use deriving::generic::*;
 use deriving::generic::ty::*;
 
@@ -55,7 +56,7 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
                     }));
                 }
                 ItemKind::Union(..) => {
-                    bounds = vec![Literal(path_std!(cx, core::marker::Copy))];
+                    bounds = vec![Literal(path_std!(cx, marker::Copy))];
                     is_shallow = true;
                     substructure = combine_substructure(Box::new(|c, s, sub| {
                         cs_clone_shallow("Clone", c, s, sub, true)
@@ -79,7 +80,7 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
     let trait_def = TraitDef {
         span,
         attributes: Vec::new(),
-        path: path_std!(cx, core::clone::Clone),
+        path: path_std!(cx, clone::Clone),
         additional_bounds: bounds,
         generics: LifetimeBounds::empty(),
         is_unsafe: false,
diff --git a/src/libsyntax_ext/deriving/cmp/eq.rs b/src/libsyntax_ext/deriving/cmp/eq.rs
index a282ff5bd04..237c8654edf 100644
--- a/src/libsyntax_ext/deriving/cmp/eq.rs
+++ b/src/libsyntax_ext/deriving/cmp/eq.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use deriving::path_std;
 use deriving::generic::*;
 use deriving::generic::ty::*;
 
@@ -30,7 +31,7 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt,
     let trait_def = TraitDef {
         span,
         attributes: Vec::new(),
-        path: path_std!(cx, core::cmp::Eq),
+        path: path_std!(cx, cmp::Eq),
         additional_bounds: Vec::new(),
         generics: LifetimeBounds::empty(),
         is_unsafe: false,
diff --git a/src/libsyntax_ext/deriving/cmp/ord.rs b/src/libsyntax_ext/deriving/cmp/ord.rs
index 9b057aacece..1a392ac3765 100644
--- a/src/libsyntax_ext/deriving/cmp/ord.rs
+++ b/src/libsyntax_ext/deriving/cmp/ord.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use deriving::path_std;
 use deriving::generic::*;
 use deriving::generic::ty::*;
 
@@ -28,7 +29,7 @@ pub fn expand_deriving_ord(cx: &mut ExtCtxt,
     let trait_def = TraitDef {
         span,
         attributes: Vec::new(),
-        path: path_std!(cx, core::cmp::Ord),
+        path: path_std!(cx, cmp::Ord),
         additional_bounds: Vec::new(),
         generics: LifetimeBounds::empty(),
         is_unsafe: false,
@@ -38,7 +39,7 @@ pub fn expand_deriving_ord(cx: &mut ExtCtxt,
                           generics: LifetimeBounds::empty(),
                           explicit_self: borrowed_explicit_self(),
                           args: vec![borrowed_self()],
-                          ret_ty: Literal(path_std!(cx, core::cmp::Ordering)),
+                          ret_ty: Literal(path_std!(cx, cmp::Ordering)),
                           attributes: attrs,
                           is_unsafe: false,
                           unify_fieldless_variants: true,
diff --git a/src/libsyntax_ext/deriving/cmp/partial_eq.rs b/src/libsyntax_ext/deriving/cmp/partial_eq.rs
index e635c6bebcd..75db7cc1e4c 100644
--- a/src/libsyntax_ext/deriving/cmp/partial_eq.rs
+++ b/src/libsyntax_ext/deriving/cmp/partial_eq.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use deriving::{path_local, path_std};
 use deriving::generic::*;
 use deriving::generic::ty::*;
 
@@ -93,7 +94,7 @@ pub fn expand_deriving_partial_eq(cx: &mut ExtCtxt,
     let trait_def = TraitDef {
         span,
         attributes: Vec::new(),
-        path: path_std!(cx, core::cmp::PartialEq),
+        path: path_std!(cx, cmp::PartialEq),
         additional_bounds: Vec::new(),
         generics: LifetimeBounds::empty(),
         is_unsafe: false,
diff --git a/src/libsyntax_ext/deriving/cmp/partial_ord.rs b/src/libsyntax_ext/deriving/cmp/partial_ord.rs
index e7e1c108760..92183c58eb2 100644
--- a/src/libsyntax_ext/deriving/cmp/partial_ord.rs
+++ b/src/libsyntax_ext/deriving/cmp/partial_ord.rs
@@ -10,6 +10,7 @@
 
 pub use self::OrderingOp::*;
 
+use deriving::{path_local, pathvec_std, path_std};
 use deriving::generic::*;
 use deriving::generic::ty::*;
 
@@ -45,11 +46,11 @@ pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt,
         } }
     }
 
-    let ordering_ty = Literal(path_std!(cx, core::cmp::Ordering));
-    let ret_ty = Literal(Path::new_(pathvec_std!(cx, core::option::Option),
+    let ordering_ty = Literal(path_std!(cx, cmp::Ordering));
+    let ret_ty = Literal(Path::new_(pathvec_std!(cx, option::Option),
                                     None,
                                     vec![Box::new(ordering_ty)],
-                                    true));
+                                    PathKind::Std));
 
     let inline = cx.meta_word(span, Symbol::intern("inline"));
     let attrs = vec![cx.attribute(span, inline)];
@@ -84,7 +85,7 @@ pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt,
     let trait_def = TraitDef {
         span,
         attributes: vec![],
-        path: path_std!(cx, core::cmp::PartialOrd),
+        path: path_std!(cx, cmp::PartialOrd),
         additional_bounds: vec![],
         generics: LifetimeBounds::empty(),
         is_unsafe: false,
diff --git a/src/libsyntax_ext/deriving/debug.rs b/src/libsyntax_ext/deriving/debug.rs
index ab6dd04520c..82fc09fca69 100644
--- a/src/libsyntax_ext/deriving/debug.rs
+++ b/src/libsyntax_ext/deriving/debug.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use deriving::path_std;
 use deriving::generic::*;
 use deriving::generic::ty::*;
 
@@ -24,13 +25,13 @@ pub fn expand_deriving_debug(cx: &mut ExtCtxt,
                              item: &Annotatable,
                              push: &mut FnMut(Annotatable)) {
     // &mut ::std::fmt::Formatter
-    let fmtr = Ptr(Box::new(Literal(path_std!(cx, core::fmt::Formatter))),
+    let fmtr = Ptr(Box::new(Literal(path_std!(cx, fmt::Formatter))),
                    Borrowed(None, ast::Mutability::Mutable));
 
     let trait_def = TraitDef {
         span,
         attributes: Vec::new(),
-        path: path_std!(cx, core::fmt::Debug),
+        path: path_std!(cx, fmt::Debug),
         additional_bounds: Vec::new(),
         generics: LifetimeBounds::empty(),
         is_unsafe: false,
@@ -40,7 +41,7 @@ pub fn expand_deriving_debug(cx: &mut ExtCtxt,
                           generics: LifetimeBounds::empty(),
                           explicit_self: borrowed_explicit_self(),
                           args: vec![fmtr],
-                          ret_ty: Literal(path_std!(cx, core::fmt::Result)),
+                          ret_ty: Literal(path_std!(cx, fmt::Result)),
                           attributes: Vec::new(),
                           is_unsafe: false,
                           unify_fieldless_variants: false,
diff --git a/src/libsyntax_ext/deriving/decodable.rs b/src/libsyntax_ext/deriving/decodable.rs
index 517221af1d4..46dada256b8 100644
--- a/src/libsyntax_ext/deriving/decodable.rs
+++ b/src/libsyntax_ext/deriving/decodable.rs
@@ -10,7 +10,7 @@
 
 //! The compiler code necessary for `#[derive(Decodable)]`. See encodable.rs for more.
 
-use deriving;
+use deriving::{self, pathvec_std};
 use deriving::generic::*;
 use deriving::generic::ty::*;
 use deriving::warn_if_deprecated;
@@ -46,20 +46,12 @@ fn expand_deriving_decodable_imp(cx: &mut ExtCtxt,
                                  item: &Annotatable,
                                  push: &mut FnMut(Annotatable),
                                  krate: &'static str) {
-    if cx.crate_root != Some("std") {
-        // FIXME(#21880): lift this requirement.
-        cx.span_err(span,
-                    "this trait cannot be derived with #![no_std] \
-                           or #![no_core]");
-        return;
-    }
-
     let typaram = &*deriving::hygienic_type_parameter(item, "__D");
 
     let trait_def = TraitDef {
         span,
         attributes: Vec::new(),
-        path: Path::new_(vec![krate, "Decodable"], None, vec![], true),
+        path: Path::new_(vec![krate, "Decodable"], None, vec![], PathKind::Global),
         additional_bounds: Vec::new(),
         generics: LifetimeBounds::empty(),
         is_unsafe: false,
@@ -72,18 +64,18 @@ fn expand_deriving_decodable_imp(cx: &mut ExtCtxt,
                                             vec![Path::new_(vec![krate, "Decoder"],
                                                             None,
                                                             vec![],
-                                                            true)])],
+                                                            PathKind::Global)])],
                           },
                           explicit_self: None,
                           args: vec![Ptr(Box::new(Literal(Path::new_local(typaram))),
                                          Borrowed(None, Mutability::Mutable))],
                           ret_ty:
-                              Literal(Path::new_(pathvec_std!(cx, core::result::Result),
+                              Literal(Path::new_(pathvec_std!(cx, result::Result),
                                                  None,
                                                  vec![Box::new(Self_), Box::new(Literal(Path::new_(
-                        vec![typaram, "Error"], None, vec![], false
+                        vec![typaram, "Error"], None, vec![], PathKind::Local
                     )))],
-                                                 true)),
+                                                 PathKind::Std)),
                           attributes: Vec::new(),
                           is_unsafe: false,
                           unify_fieldless_variants: false,
diff --git a/src/libsyntax_ext/deriving/default.rs b/src/libsyntax_ext/deriving/default.rs
index 0c8e3c39395..99e7bb4baef 100644
--- a/src/libsyntax_ext/deriving/default.rs
+++ b/src/libsyntax_ext/deriving/default.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use deriving::path_std;
 use deriving::generic::*;
 use deriving::generic::ty::*;
 
@@ -28,7 +29,7 @@ pub fn expand_deriving_default(cx: &mut ExtCtxt,
     let trait_def = TraitDef {
         span,
         attributes: Vec::new(),
-        path: path_std!(cx, core::default::Default),
+        path: path_std!(cx, default::Default),
         additional_bounds: Vec::new(),
         generics: LifetimeBounds::empty(),
         is_unsafe: false,
diff --git a/src/libsyntax_ext/deriving/encodable.rs b/src/libsyntax_ext/deriving/encodable.rs
index c2c862f043f..0e6e96438d8 100644
--- a/src/libsyntax_ext/deriving/encodable.rs
+++ b/src/libsyntax_ext/deriving/encodable.rs
@@ -92,7 +92,7 @@
 //! }
 //! ```
 
-use deriving;
+use deriving::{self, pathvec_std};
 use deriving::generic::*;
 use deriving::generic::ty::*;
 use deriving::warn_if_deprecated;
@@ -127,20 +127,12 @@ fn expand_deriving_encodable_imp(cx: &mut ExtCtxt,
                                  item: &Annotatable,
                                  push: &mut FnMut(Annotatable),
                                  krate: &'static str) {
-    if cx.crate_root != Some("std") {
-        // FIXME(#21880): lift this requirement.
-        cx.span_err(span,
-                    "this trait cannot be derived with #![no_std] \
-                           or #![no_core]");
-        return;
-    }
-
     let typaram = &*deriving::hygienic_type_parameter(item, "__S");
 
     let trait_def = TraitDef {
         span,
         attributes: Vec::new(),
-        path: Path::new_(vec![krate, "Encodable"], None, vec![], true),
+        path: Path::new_(vec![krate, "Encodable"], None, vec![], PathKind::Global),
         additional_bounds: Vec::new(),
         generics: LifetimeBounds::empty(),
         is_unsafe: false,
@@ -150,19 +142,21 @@ fn expand_deriving_encodable_imp(cx: &mut ExtCtxt,
                 name: "encode",
                 generics: LifetimeBounds {
                     lifetimes: Vec::new(),
-                    bounds: vec![(typaram,
-                                  vec![Path::new_(vec![krate, "Encoder"], None, vec![], true)])]
+                    bounds: vec![
+                        (typaram,
+                         vec![Path::new_(vec![krate, "Encoder"], None, vec![], PathKind::Global)])
+                    ],
                 },
                 explicit_self: borrowed_explicit_self(),
                 args: vec![Ptr(Box::new(Literal(Path::new_local(typaram))),
                            Borrowed(None, Mutability::Mutable))],
                 ret_ty: Literal(Path::new_(
-                    pathvec_std!(cx, core::result::Result),
+                    pathvec_std!(cx, result::Result),
                     None,
                     vec![Box::new(Tuple(Vec::new())), Box::new(Literal(Path::new_(
-                        vec![typaram, "Error"], None, vec![], false
+                        vec![typaram, "Error"], None, vec![], PathKind::Local
                     )))],
-                    true
+                    PathKind::Std
                 )),
                 attributes: Vec::new(),
                 is_unsafe: false,
diff --git a/src/libsyntax_ext/deriving/generic/ty.rs b/src/libsyntax_ext/deriving/generic/ty.rs
index 47b5f40832a..e4faf652389 100644
--- a/src/libsyntax_ext/deriving/generic/ty.rs
+++ b/src/libsyntax_ext/deriving/generic/ty.rs
@@ -21,6 +21,8 @@ use syntax::ext::build::AstBuilder;
 use syntax::codemap::respan;
 use syntax::ptr::P;
 use syntax_pos::Span;
+use syntax_pos::hygiene::SyntaxContext;
+use syntax_pos::symbol::keywords;
 
 /// The types of pointers
 #[derive(Clone, Eq, PartialEq)]
@@ -36,29 +38,36 @@ pub enum PtrTy<'a> {
 /// for type parameters and a lifetime.
 #[derive(Clone, Eq, PartialEq)]
 pub struct Path<'a> {
-    pub path: Vec<&'a str>,
-    pub lifetime: Option<&'a str>,
-    pub params: Vec<Box<Ty<'a>>>,
-    pub global: bool,
+    path: Vec<&'a str>,
+    lifetime: Option<&'a str>,
+    params: Vec<Box<Ty<'a>>>,
+    kind: PathKind,
+}
+
+#[derive(Clone, Eq, PartialEq)]
+pub enum PathKind {
+    Local,
+    Global,
+    Std,
 }
 
 impl<'a> Path<'a> {
     pub fn new<'r>(path: Vec<&'r str>) -> Path<'r> {
-        Path::new_(path, None, Vec::new(), true)
+        Path::new_(path, None, Vec::new(), PathKind::Std)
     }
     pub fn new_local<'r>(path: &'r str) -> Path<'r> {
-        Path::new_(vec![path], None, Vec::new(), false)
+        Path::new_(vec![path], None, Vec::new(), PathKind::Local)
     }
     pub fn new_<'r>(path: Vec<&'r str>,
                     lifetime: Option<&'r str>,
                     params: Vec<Box<Ty<'r>>>,
-                    global: bool)
+                    kind: PathKind)
                     -> Path<'r> {
         Path {
             path,
             lifetime,
             params,
-            global,
+            kind,
         }
     }
 
@@ -76,11 +85,20 @@ impl<'a> Path<'a> {
                    self_ty: Ident,
                    self_generics: &Generics)
                    -> ast::Path {
-        let idents = self.path.iter().map(|s| cx.ident_of(*s)).collect();
+        let mut idents = self.path.iter().map(|s| cx.ident_of(*s)).collect();
         let lt = mk_lifetimes(cx, span, &self.lifetime);
         let tys = self.params.iter().map(|t| t.to_ty(cx, span, self_ty, self_generics)).collect();
 
-        cx.path_all(span, self.global, idents, lt, tys, Vec::new())
+        match self.kind {
+            PathKind::Global => cx.path_all(span, true, idents, lt, tys, Vec::new()),
+            PathKind::Local => cx.path_all(span, false, idents, lt, tys, Vec::new()),
+            PathKind::Std => {
+                let def_site = SyntaxContext::empty().apply_mark(cx.current_expansion.mark);
+                idents.insert(0, Ident { ctxt: def_site, ..keywords::DollarCrate.ident() });
+                cx.path_all(span, false, idents, lt, tys, Vec::new())
+            }
+        }
+
     }
 }
 
diff --git a/src/libsyntax_ext/deriving/hash.rs b/src/libsyntax_ext/deriving/hash.rs
index 6c78eea186d..b192ab2527e 100644
--- a/src/libsyntax_ext/deriving/hash.rs
+++ b/src/libsyntax_ext/deriving/hash.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use deriving;
+use deriving::{self, pathvec_std, path_std};
 use deriving::generic::*;
 use deriving::generic::ty::*;
 
@@ -24,7 +24,7 @@ pub fn expand_deriving_hash(cx: &mut ExtCtxt,
                             item: &Annotatable,
                             push: &mut FnMut(Annotatable)) {
 
-    let path = Path::new_(pathvec_std!(cx, core::hash::Hash), None, vec![], true);
+    let path = Path::new_(pathvec_std!(cx, hash::Hash), None, vec![], PathKind::Std);
 
     let typaram = &*deriving::hygienic_type_parameter(item, "__H");
 
@@ -41,7 +41,7 @@ pub fn expand_deriving_hash(cx: &mut ExtCtxt,
                           name: "hash",
                           generics: LifetimeBounds {
                               lifetimes: Vec::new(),
-                              bounds: vec![(typaram, vec![path_std!(cx, core::hash::Hasher)])],
+                              bounds: vec![(typaram, vec![path_std!(cx, hash::Hasher)])],
                           },
                           explicit_self: borrowed_explicit_self(),
                           args: vec![Ptr(Box::new(Literal(arg)),
diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs
index ccf3d550234..a6696b53369 100644
--- a/src/libsyntax_ext/deriving/mod.rs
+++ b/src/libsyntax_ext/deriving/mod.rs
@@ -19,32 +19,16 @@ use syntax::ptr::P;
 use syntax::symbol::Symbol;
 use syntax_pos::Span;
 
-macro_rules! pathvec {
-    ($($x:ident)::+) => (
-        vec![ $( stringify!($x) ),+ ]
-    )
+macro path_local($x:ident) {
+    generic::ty::Path::new_local(stringify!($x))
 }
 
-macro_rules! path_local {
-    ($x:ident) => (
-        ::deriving::generic::ty::Path::new_local(stringify!($x))
-    )
-}
-
-macro_rules! pathvec_std {
-    ($cx:expr, $first:ident :: $($rest:ident)::+) => ({
-        let mut v = pathvec![$($rest)::+];
-        if let Some(s) = $cx.crate_root {
-            v.insert(0, s);
-        }
-        v
-    })
-}
+macro pathvec_std($cx:expr, $($rest:ident)::+) {{
+    vec![ $( stringify!($rest) ),+ ]
+}}
 
-macro_rules! path_std {
-    ($($x:tt)*) => (
-        ::deriving::generic::ty::Path::new( pathvec_std!( $($x)* ) )
-    )
+macro path_std($($x:tt)*) {
+    generic::ty::Path::new( pathvec_std!( $($x)* ) )
 }
 
 pub mod bounds;
diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs
index 42bbb4ae0cb..82d6ee5afa0 100644
--- a/src/libsyntax_ext/lib.rs
+++ b/src/libsyntax_ext/lib.rs
@@ -16,6 +16,7 @@
 #![deny(warnings)]
 
 #![feature(proc_macro_internals)]
+#![feature(decl_macro)]
 
 extern crate fmt_macros;
 #[macro_use]
diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs
index 9358e654a9f..ab6c3f7d62d 100644
--- a/src/libsyntax_pos/hygiene.rs
+++ b/src/libsyntax_pos/hygiene.rs
@@ -27,7 +27,7 @@ use std::fmt;
 #[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
 pub struct SyntaxContext(pub(super) u32);
 
-#[derive(Copy, Clone, Default)]
+#[derive(Copy, Clone)]
 pub struct SyntaxContextData {
     pub outer_mark: Mark,
     pub prev_ctxt: SyntaxContext,
@@ -35,20 +35,26 @@ pub struct SyntaxContextData {
 }
 
 /// A mark is a unique id associated with a macro expansion.
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default, RustcEncodable, RustcDecodable)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
 pub struct Mark(u32);
 
-#[derive(Default)]
 struct MarkData {
     parent: Mark,
-    modern: bool,
+    kind: MarkKind,
     expn_info: Option<ExpnInfo>,
 }
 
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub enum MarkKind {
+    Modern,
+    Builtin,
+    Legacy,
+}
+
 impl Mark {
     pub fn fresh(parent: Mark) -> Self {
         HygieneData::with(|data| {
-            data.marks.push(MarkData { parent: parent, modern: false, expn_info: None });
+            data.marks.push(MarkData { parent: parent, kind: MarkKind::Legacy, expn_info: None });
             Mark(data.marks.len() as u32 - 1)
         })
     }
@@ -77,7 +83,7 @@ impl Mark {
     pub fn modern(mut self) -> Mark {
         HygieneData::with(|data| {
             loop {
-                if self == Mark::root() || data.marks[self.0 as usize].modern {
+                if self == Mark::root() || data.marks[self.0 as usize].kind == MarkKind::Modern {
                     return self;
                 }
                 self = data.marks[self.0 as usize].parent;
@@ -85,12 +91,12 @@ impl Mark {
         })
     }
 
-    pub fn is_modern(self) -> bool {
-        HygieneData::with(|data| data.marks[self.0 as usize].modern)
+    pub fn kind(self) -> MarkKind {
+        HygieneData::with(|data| data.marks[self.0 as usize].kind)
     }
 
-    pub fn set_modern(self) {
-        HygieneData::with(|data| data.marks[self.0 as usize].modern = true)
+    pub fn set_kind(self, kind: MarkKind) {
+        HygieneData::with(|data| data.marks[self.0 as usize].kind = kind)
     }
 
     pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
@@ -116,8 +122,16 @@ struct HygieneData {
 impl HygieneData {
     fn new() -> Self {
         HygieneData {
-            marks: vec![MarkData::default()],
-            syntax_contexts: vec![SyntaxContextData::default()],
+            marks: vec![MarkData {
+                parent: Mark::root(),
+                kind: MarkKind::Builtin,
+                expn_info: None,
+            }],
+            syntax_contexts: vec![SyntaxContextData {
+                outer_mark: Mark::root(),
+                prev_ctxt: SyntaxContext(0),
+                modern: SyntaxContext(0),
+            }],
             markings: HashMap::new(),
             gensym_to_ctxt: HashMap::new(),
         }
@@ -150,7 +164,7 @@ impl SyntaxContext {
         HygieneData::with(|data| {
             data.marks.push(MarkData {
                 parent: Mark::root(),
-                modern: false,
+                kind: MarkKind::Legacy,
                 expn_info: Some(expansion_info)
             });
 
@@ -170,7 +184,7 @@ impl SyntaxContext {
         HygieneData::with(|data| {
             let syntax_contexts = &mut data.syntax_contexts;
             let mut modern = syntax_contexts[self.0 as usize].modern;
-            if data.marks[mark.0 as usize].modern {
+            if data.marks[mark.0 as usize].kind == MarkKind::Modern {
                 modern = *data.markings.entry((modern, mark)).or_insert_with(|| {
                     let len = syntax_contexts.len() as u32;
                     syntax_contexts.push(SyntaxContextData {
diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp
index 02b8425d88b..81518b0b872 100644
--- a/src/test/pretty/issue-4264.pp
+++ b/src/test/pretty/issue-4264.pp
@@ -40,7 +40,7 @@ pub fn bar() ({
 
 
                   ((::fmt::format as
-                       for<'r> fn(std::fmt::Arguments<'r>) -> std::string::String {std::fmt::format})(((<::std::fmt::Arguments>::new_v1
+                       for<'r> fn(std::fmt::Arguments<'r>) -> std::string::String {std::fmt::format})(((<::fmt::Arguments>::new_v1
                                                                                                            as
                                                                                                            fn(&[&str], &[std::fmt::ArgumentV1<'_>]) -> std::fmt::Arguments<'_> {std::fmt::Arguments<'_>::new_v1})((&([("test"
                                                                                                                                                                                                                           as
diff --git a/src/test/run-pass-fulldeps/auxiliary/custom_derive_partial_eq.rs b/src/test/run-pass-fulldeps/auxiliary/custom_derive_partial_eq.rs
index 63dbd4d5bed..f485982e2d3 100644
--- a/src/test/run-pass-fulldeps/auxiliary/custom_derive_partial_eq.rs
+++ b/src/test/run-pass-fulldeps/auxiliary/custom_derive_partial_eq.rs
@@ -69,7 +69,7 @@ fn expand_deriving_partial_eq(cx: &mut ExtCtxt, span: Span, mitem: &MetaItem, it
     let trait_def = TraitDef {
         span: span,
         attributes: Vec::new(),
-        path: deriving::generic::ty::Path::new(vec!["std", "cmp", "PartialEq"]),
+        path: deriving::generic::ty::Path::new(vec!["cmp", "PartialEq"]),
         additional_bounds: Vec::new(),
         generics: LifetimeBounds::empty(),
         is_unsafe: false,
diff --git a/src/test/run-pass-fulldeps/auxiliary/custom_derive_plugin.rs b/src/test/run-pass-fulldeps/auxiliary/custom_derive_plugin.rs
index 16856d30417..449cd29ada3 100644
--- a/src/test/run-pass-fulldeps/auxiliary/custom_derive_plugin.rs
+++ b/src/test/run-pass-fulldeps/auxiliary/custom_derive_plugin.rs
@@ -50,7 +50,7 @@ fn expand(cx: &mut ExtCtxt,
     let trait_def = TraitDef {
         span: span,
         attributes: vec![],
-        path: Path::new(vec!["TotalSum"]),
+        path: Path::new_local("TotalSum"),
         additional_bounds: vec![],
         generics: LifetimeBounds::empty(),
         associated_types: vec![],
diff --git a/src/test/run-pass-fulldeps/auxiliary/custom_derive_plugin_attr.rs b/src/test/run-pass-fulldeps/auxiliary/custom_derive_plugin_attr.rs
index 50b16a0e26f..1a9358f22bf 100644
--- a/src/test/run-pass-fulldeps/auxiliary/custom_derive_plugin_attr.rs
+++ b/src/test/run-pass-fulldeps/auxiliary/custom_derive_plugin_attr.rs
@@ -46,7 +46,7 @@ fn expand(cx: &mut ExtCtxt,
     let trait_def = TraitDef {
         span: span,
         attributes: vec![],
-        path: Path::new(vec!["TotalSum"]),
+        path: Path::new_local("TotalSum"),
         additional_bounds: vec![],
         generics: LifetimeBounds::empty(),
         associated_types: vec![],
diff --git a/src/test/compile-fail-fulldeps/derive-no-std-not-supported.rs b/src/test/run-pass-fulldeps/derive-no-std-not-supported.rs
index 1e97cb07f89..a0747e0fbf5 100644
--- a/src/test/compile-fail-fulldeps/derive-no-std-not-supported.rs
+++ b/src/test/run-pass-fulldeps/derive-no-std-not-supported.rs
@@ -8,22 +8,22 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(rustc_private)]
 #![no_std]
 
 extern crate serialize as rustc_serialize;
 
-#[derive(RustcEncodable)]  //~ ERROR this trait cannot be derived
+#[derive(RustcEncodable)]
 struct Bar {
     x: u32,
 }
 
-#[derive(RustcDecodable)]  //~ ERROR this trait cannot be derived
+#[derive(RustcDecodable)]
 struct Baz {
     x: u32,
 }
 
 fn main() {
-    Foo { x: 0 };
     Bar { x: 0 };
     Baz { x: 0 };
 }