diff --git a/src/libcore/clone.rs b/src/libcore/clone.rs
index 69355c6c6cc0b..0b800cacfc19a 100644
--- a/src/libcore/clone.rs
+++ b/src/libcore/clone.rs
@@ -113,10 +113,23 @@ pub trait Clone : Sized {
     }
 }
 
-// FIXME(aburka): this method is used solely by #[derive] to
-// assert that every component of a type implements Clone.
+// FIXME(aburka): these structs are used solely by #[derive] to
+// assert that every component of a type implements Clone or Copy.
 //
-// This should never be called by user code.
+// These structs should never appear in user code.
+#[doc(hidden)]
+#[allow(missing_debug_implementations)]
+#[unstable(feature = "derive_clone_copy",
+           reason = "deriving hack, should not be public",
+           issue = "0")]
+pub struct AssertParamIsClone<T: Clone + ?Sized> { _field: ::marker::PhantomData<T> }
+#[doc(hidden)]
+#[allow(missing_debug_implementations)]
+#[unstable(feature = "derive_clone_copy",
+           reason = "deriving hack, should not be public",
+           issue = "0")]
+pub struct AssertParamIsCopy<T: Copy + ?Sized> { _field: ::marker::PhantomData<T> }
+#[cfg(stage0)]
 #[doc(hidden)]
 #[inline(always)]
 #[unstable(feature = "derive_clone_copy",
diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs
index 670978a2d49af..f990a27e52b31 100644
--- a/src/libcore/cmp.rs
+++ b/src/libcore/cmp.rs
@@ -129,7 +129,7 @@ pub trait PartialEq<Rhs: ?Sized = Self> {
 /// This trait can be used with `#[derive]`. When `derive`d, because `Eq` has
 /// no extra methods, it is only informing the compiler that this is an
 /// equivalence relation rather than a partial equivalence relation. Note that
-/// the `derive` strategy requires all fields are `PartialEq`, which isn't
+/// the `derive` strategy requires all fields are `Eq`, which isn't
 /// always desired.
 ///
 /// ## How can I implement `Eq`?
@@ -165,6 +165,17 @@ pub trait Eq: PartialEq<Self> {
     fn assert_receiver_is_total_eq(&self) {}
 }
 
+// FIXME: this struct is used solely by #[derive] to
+// assert that every component of a type implements Eq.
+//
+// This struct should never appear in user code.
+#[doc(hidden)]
+#[allow(missing_debug_implementations)]
+#[unstable(feature = "derive_eq",
+           reason = "deriving hack, should not be public",
+           issue = "0")]
+pub struct AssertParamIsEq<T: Eq + ?Sized> { _field: ::marker::PhantomData<T> }
+
 /// An `Ordering` is the result of a comparison between two values.
 ///
 /// # Examples
diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs
index 7b147faccd20f..d1df56905df24 100644
--- a/src/libcore/slice.rs
+++ b/src/libcore/slice.rs
@@ -520,8 +520,8 @@ impl<T> ops::Index<usize> for [T] {
     type Output = T;
 
     fn index(&self, index: usize) -> &T {
-        assert!(index < self.len());
-        unsafe { self.get_unchecked(index) }
+        // NB built-in indexing
+        &(*self)[index]
     }
 }
 
@@ -530,8 +530,8 @@ impl<T> ops::Index<usize> for [T] {
 impl<T> ops::IndexMut<usize> for [T] {
     #[inline]
     fn index_mut(&mut self, index: usize) -> &mut T {
-        assert!(index < self.len());
-        unsafe { self.get_unchecked_mut(index) }
+        // NB built-in indexing
+        &mut (*self)[index]
     }
 }
 
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 59431f3f02dce..39fc50666a8ce 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -830,6 +830,33 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         result.map(move |t| InferOk { value: t, obligations: fields.obligations })
     }
 
+    // Clear the "obligations in snapshot" flag, invoke the closure,
+    // then restore the flag to its original value. This flag is a
+    // debugging measure designed to detect cases where we start a
+    // snapshot, create type variables, register obligations involving
+    // those type variables in the fulfillment cx, and then have to
+    // unroll the snapshot, leaving "dangling type variables" behind.
+    // In such cases, the flag will be set by the fulfillment cx, and
+    // an assertion will fail when rolling the snapshot back.  Very
+    // useful, much better than grovelling through megabytes of
+    // RUST_LOG output.
+    //
+    // HOWEVER, in some cases the flag is wrong. In particular, we
+    // sometimes create a "mini-fulfilment-cx" in which we enroll
+    // obligations. As long as this fulfillment cx is fully drained
+    // before we return, this is not a problem, as there won't be any
+    // escaping obligations in the main cx. In those cases, you can
+    // use this function.
+    pub fn save_and_restore_obligations_in_snapshot_flag<F, R>(&self, func: F) -> R
+        where F: FnOnce(&Self) -> R
+    {
+        let flag = self.obligations_in_snapshot.get();
+        self.obligations_in_snapshot.set(false);
+        let result = func(self);
+        self.obligations_in_snapshot.set(flag);
+        result
+    }
+
     fn start_snapshot(&self) -> CombinedSnapshot {
         debug!("start_snapshot()");
 
diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs
index b8465e63b1c8a..b2c293a290ab3 100644
--- a/src/librustc/middle/cstore.rs
+++ b/src/librustc/middle/cstore.rs
@@ -39,6 +39,7 @@ use std::rc::Rc;
 use std::path::PathBuf;
 use syntax::ast;
 use syntax::attr;
+use syntax::ext::base::LoadedMacro;
 use syntax::ptr::P;
 use syntax::parse::token::InternedString;
 use syntax_pos::Span;
@@ -492,6 +493,9 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
     fn metadata_encoding_version(&self) -> &[u8] { bug!("metadata_encoding_version") }
 }
 
+pub trait MacroLoader {
+     fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<LoadedMacro>;
+}
 
 /// Metadata encoding and decoding can make use of thread-local encoding and
 /// decoding contexts. These allow implementers of serialize::Encodable and
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index 3477ec6f99af1..49686d63ee43b 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -21,7 +21,7 @@ use util::nodemap::{NodeMap, FnvHashMap};
 use util::common::duration_to_secs_str;
 use mir::transform as mir_pass;
 
-use syntax::ast::{NodeId, Name};
+use syntax::ast::NodeId;
 use errors::{self, DiagnosticBuilder};
 use errors::emitter::{Emitter, EmitterWriter};
 use syntax::json::JsonEmitter;
@@ -39,7 +39,7 @@ use llvm;
 
 use std::path::{Path, PathBuf};
 use std::cell::{self, Cell, RefCell};
-use std::collections::{HashMap, HashSet};
+use std::collections::HashMap;
 use std::env;
 use std::ffi::CString;
 use std::rc::Rc;
@@ -96,10 +96,6 @@ pub struct Session {
     pub injected_allocator: Cell<Option<ast::CrateNum>>,
     pub injected_panic_runtime: Cell<Option<ast::CrateNum>>,
 
-    /// Names of all bang-style macros and syntax extensions
-    /// available in this crate
-    pub available_macros: RefCell<HashSet<Name>>,
-
     /// Map from imported macro spans (which consist of
     /// the localized span for the macro body) to the
     /// macro name and defintion span in the source crate.
@@ -552,7 +548,6 @@ pub fn build_session_(sopts: config::Options,
         next_node_id: Cell::new(1),
         injected_allocator: Cell::new(None),
         injected_panic_runtime: Cell::new(None),
-        available_macros: RefCell::new(HashSet::new()),
         imported_macro_spans: RefCell::new(HashMap::new()),
         incr_comp_session: RefCell::new(IncrCompSession::NotInitialized),
         perf_stats: PerfStats {
diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs
index f3ba4d16eb0b2..2f63526bf6c27 100644
--- a/src/librustc/traits/specialize/mod.rs
+++ b/src/librustc/traits/specialize/mod.rs
@@ -203,32 +203,34 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
     // attempt to prove all of the predicates for impl2 given those for impl1
     // (which are packed up in penv)
 
-    let mut fulfill_cx = FulfillmentContext::new();
-    for oblig in obligations.into_iter() {
-        fulfill_cx.register_predicate_obligation(&infcx, oblig);
-    }
-    match fulfill_cx.select_all_or_error(infcx) {
-        Err(errors) => {
-            // no dice!
-            debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \
-                    {:?}",
-                   source_trait_ref,
-                   target_trait_ref,
-                   errors,
-                   infcx.parameter_environment.caller_bounds);
-            Err(())
+    infcx.save_and_restore_obligations_in_snapshot_flag(|infcx| {
+        let mut fulfill_cx = FulfillmentContext::new();
+        for oblig in obligations.into_iter() {
+            fulfill_cx.register_predicate_obligation(&infcx, oblig);
         }
+        match fulfill_cx.select_all_or_error(infcx) {
+            Err(errors) => {
+                // no dice!
+                debug!("fulfill_implication: for impls on {:?} and {:?}, \
+                        could not fulfill: {:?} given {:?}",
+                       source_trait_ref,
+                       target_trait_ref,
+                       errors,
+                       infcx.parameter_environment.caller_bounds);
+                Err(())
+            }
 
-        Ok(()) => {
-            debug!("fulfill_implication: an impl for {:?} specializes {:?}",
-                   source_trait_ref,
-                   target_trait_ref);
+            Ok(()) => {
+                debug!("fulfill_implication: an impl for {:?} specializes {:?}",
+                       source_trait_ref,
+                       target_trait_ref);
 
-            // Now resolve the *substitution* we built for the target earlier, replacing
-            // the inference variables inside with whatever we got from fulfillment.
-            Ok(infcx.resolve_type_vars_if_possible(&target_substs))
+                // Now resolve the *substitution* we built for the target earlier, replacing
+                // the inference variables inside with whatever we got from fulfillment.
+                Ok(infcx.resolve_type_vars_if_possible(&target_substs))
+            }
         }
-    }
+    })
 }
 
 pub struct SpecializesCache {
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 20601493d68f3..6d7a2d6cba1c7 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -1303,7 +1303,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     }
 
     pub fn mk_trait(self, mut obj: TraitObject<'tcx>) -> Ty<'tcx> {
-        obj.projection_bounds.sort_by(|a, b| a.sort_key().cmp(&b.sort_key()));
+        obj.projection_bounds.sort_by_key(|b| b.sort_key(self));
         self.mk_ty(TyTrait(box obj))
     }
 
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index f634c8e37d7bd..14eb2fb7914c3 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1018,10 +1018,6 @@ impl<'tcx> PolyProjectionPredicate<'tcx> {
     pub fn item_name(&self) -> Name {
         self.0.projection_ty.item_name // safe to skip the binder to access a name
     }
-
-    pub fn sort_key(&self) -> (DefId, Name) {
-        self.0.projection_ty.sort_key()
-    }
 }
 
 pub trait ToPolyTraitRef<'tcx> {
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index a755dd056cd84..5fdc7abc0af5b 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -23,7 +23,7 @@ use std::mem;
 use std::ops;
 use syntax::abi;
 use syntax::ast::{self, Name};
-use syntax::parse::token::keywords;
+use syntax::parse::token::{keywords, InternedString};
 
 use serialize::{Decodable, Decoder, Encodable, Encoder};
 
@@ -440,12 +440,6 @@ pub struct ProjectionTy<'tcx> {
     pub item_name: Name,
 }
 
-impl<'tcx> ProjectionTy<'tcx> {
-    pub fn sort_key(&self) -> (DefId, Name) {
-        (self.trait_ref.def_id, self.item_name)
-    }
-}
-
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub struct BareFnTy<'tcx> {
     pub unsafety: hir::Unsafety,
@@ -738,8 +732,17 @@ impl<'a, 'tcx, 'gcx> PolyExistentialProjection<'tcx> {
         self.0.item_name // safe to skip the binder to access a name
     }
 
-    pub fn sort_key(&self) -> (DefId, Name) {
-        (self.0.trait_ref.def_id, self.0.item_name)
+    pub fn sort_key(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> (u64, InternedString) {
+        // We want something here that is stable across crate boundaries.
+        // The DefId isn't but the `deterministic_hash` of the corresponding
+        // DefPath is.
+        let trait_def = tcx.lookup_trait_def(self.0.trait_ref.def_id);
+        let def_path_hash = trait_def.def_path_hash;
+
+        // An `ast::Name` is also not stable (it's just an index into an
+        // interning table), so map to the corresponding `InternedString`.
+        let item_name = self.0.item_name.as_str();
+        (def_path_hash, item_name)
     }
 
     pub fn with_self_ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>,
diff --git a/src/librustc/ty/trait_def.rs b/src/librustc/ty/trait_def.rs
index 61285e8f8b0a5..268b2fcaa4adb 100644
--- a/src/librustc/ty/trait_def.rs
+++ b/src/librustc/ty/trait_def.rs
@@ -70,7 +70,11 @@ pub struct TraitDef<'tcx> {
     pub specialization_graph: RefCell<traits::specialization_graph::Graph>,
 
     /// Various flags
-    pub flags: Cell<TraitFlags>
+    pub flags: Cell<TraitFlags>,
+
+    /// The ICH of this trait's DefPath, cached here so it doesn't have to be
+    /// recomputed all the time.
+    pub def_path_hash: u64,
 }
 
 impl<'a, 'gcx, 'tcx> TraitDef<'tcx> {
@@ -78,7 +82,8 @@ impl<'a, 'gcx, 'tcx> TraitDef<'tcx> {
                paren_sugar: bool,
                generics: &'tcx ty::Generics<'tcx>,
                trait_ref: ty::TraitRef<'tcx>,
-               associated_type_names: Vec<Name>)
+               associated_type_names: Vec<Name>,
+               def_path_hash: u64)
                -> TraitDef<'tcx> {
         TraitDef {
             paren_sugar: paren_sugar,
@@ -90,6 +95,7 @@ impl<'a, 'gcx, 'tcx> TraitDef<'tcx> {
             blanket_impls: RefCell::new(vec![]),
             flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS),
             specialization_graph: RefCell::new(traits::specialization_graph::Graph::new()),
+            def_path_hash: def_path_hash,
         }
     }
 
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index 6b3ebaa895fa3..d34fdaa7d71cd 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -411,15 +411,11 @@ impl<'a, 'gcx, 'tcx> TypeIdHasher<'a, 'gcx, 'tcx> {
     }
 
     fn def_id(&mut self, did: DefId) {
-        // Hash the crate identification information.
-        let name = self.tcx.crate_name(did.krate);
-        let disambiguator = self.tcx.crate_disambiguator(did.krate);
-        self.hash((name, disambiguator));
-
-        // Hash the item path within that crate.
-        // FIXME(#35379) This should use a deterministic
-        // DefPath hashing mechanism, not the DefIndex.
-        self.hash(did.index);
+        // Hash the DefPath corresponding to the DefId, which is independent
+        // of compiler internal state.
+        let tcx = self.tcx;
+        let def_path = tcx.def_path(did);
+        def_path.deterministic_hash_to(tcx, &mut self.state);
     }
 }
 
@@ -445,33 +441,8 @@ impl<'a, 'gcx, 'tcx> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx> {
                 self.hash(f.sig.variadic());
             }
             TyTrait(ref data) => {
-                // Trait objects have a list of projection bounds
-                // that are not guaranteed to be sorted in an order
-                // that gets preserved across crates, so we need
-                // to sort them again by the name, in string form.
-
-                // Hash the whole principal trait ref.
                 self.def_id(data.principal.def_id());
-                data.principal.visit_with(self);
-
-                // Hash region and builtin bounds.
-                data.region_bound.visit_with(self);
                 self.hash(data.builtin_bounds);
-
-                // Only projection bounds are left, sort and hash them.
-                let mut projection_bounds: Vec<_> = data.projection_bounds
-                                                        .iter()
-                                                        .map(|b| (b.item_name().as_str(), b))
-                                                        .collect();
-                projection_bounds.sort_by_key(|&(ref name, _)| name.clone());
-                for (name, bound) in projection_bounds {
-                    self.def_id(bound.0.trait_ref.def_id);
-                    self.hash(name);
-                    bound.visit_with(self);
-                }
-
-                // Bypass super_visit_with, we've visited everything.
-                return false;
             }
             TyTuple(tys) => {
                 self.hash(tys.len());
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index ba6c4b9b84c37..36e9fccdf5fd8 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -50,6 +50,7 @@ use std::io::{self, Write};
 use std::path::{Path, PathBuf};
 use syntax::{ast, diagnostics, visit};
 use syntax::attr;
+use syntax::ext::base::ExtCtxt;
 use syntax::parse::{self, PResult, token};
 use syntax::util::node_count::NodeCounter;
 use syntax;
@@ -638,6 +639,13 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
     }
     sess.track_errors(|| sess.lint_store.borrow_mut().process_command_line(sess))?;
 
+    let mut macro_loader =
+        macro_import::MacroLoader::new(sess, &cstore, crate_name, krate.config.clone());
+
+    let resolver_arenas = Resolver::arenas();
+    let mut resolver = Resolver::new(sess, make_glob_map, &mut macro_loader, &resolver_arenas);
+    syntax_ext::register_builtins(&mut resolver, sess.features.borrow().quote);
+
     krate = time(time_passes, "expansion", || {
         // Windows dlls do not have rpaths, so they don't know how to find their
         // dependencies. It's up to us to tell the system where to find all the
@@ -672,25 +680,17 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
             trace_mac: sess.opts.debugging_opts.trace_macros,
             should_test: sess.opts.test,
         };
-        let mut loader = macro_import::MacroLoader::new(sess,
-                                                        &cstore,
-                                                        crate_name,
-                                                        krate.config.clone());
-        let mut ecx = syntax::ext::base::ExtCtxt::new(&sess.parse_sess,
-                                                      krate.config.clone(),
-                                                      cfg,
-                                                      &mut loader);
-        syntax_ext::register_builtins(&mut ecx.syntax_env);
+        let mut ecx = ExtCtxt::new(&sess.parse_sess, krate.config.clone(), cfg, &mut resolver);
         let ret = syntax::ext::expand::expand_crate(&mut ecx, syntax_exts, krate);
         if cfg!(windows) {
             env::set_var("PATH", &old_path);
         }
-        *sess.available_macros.borrow_mut() = ecx.syntax_env.names;
         ret
     });
 
     krate = time(time_passes, "maybe building test harness", || {
         syntax::test::modify_for_testing(&sess.parse_sess,
+                                         &mut resolver,
                                          sess.opts.test,
                                          krate,
                                          sess.diagnostic())
@@ -701,6 +701,7 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
         let is_rustc_macro_crate = crate_types.contains(&config::CrateTypeRustcMacro);
         let num_crate_types = crate_types.len();
         syntax_ext::rustc_macro_registrar::modify(&sess.parse_sess,
+                                                  &mut resolver,
                                                   krate,
                                                   is_rustc_macro_crate,
                                                   num_crate_types,
@@ -708,11 +709,6 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
                                                   &sess.features.borrow())
     });
 
-    let resolver_arenas = Resolver::arenas();
-    let mut resolver = Resolver::new(sess, make_glob_map, &resolver_arenas);
-
-    let krate = time(sess.time_passes(), "assigning node ids", || resolver.assign_node_ids(krate));
-
     if sess.opts.debugging_opts.input_stats {
         println!("Post-expansion node count: {}", count_nodes(&krate));
     }
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index a6049acdb10d4..e8d9e90456efc 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -569,7 +569,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
 
             ty::TyTuple(_) => {
                 FfiUnsafe("found Rust tuple type in foreign module; \
-                           consider using a struct instead`")
+                           consider using a struct instead")
             }
 
             ty::TyRawPtr(ref m) | ty::TyRef(_, ref m) => {
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index d2840fbe4fe46..624bffb7e0369 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -385,12 +385,14 @@ pub fn get_trait_def<'a, 'tcx>(cdata: Cmd,
     let unsafety = parse_unsafety(item_doc);
     let associated_type_names = parse_associated_type_names(item_doc);
     let paren_sugar = parse_paren_sugar(item_doc);
+    let def_path = def_path(cdata, item_id).unwrap();
 
     ty::TraitDef::new(unsafety,
                       paren_sugar,
                       generics,
                       item_trait_ref(item_doc, tcx, cdata),
-                      associated_type_names)
+                      associated_type_names,
+                      def_path.deterministic_hash(tcx))
 }
 
 pub fn get_adt_def<'a, 'tcx>(cdata: Cmd,
diff --git a/src/librustc_metadata/macro_import.rs b/src/librustc_metadata/macro_import.rs
index 22691975050e5..e41f076d64a80 100644
--- a/src/librustc_metadata/macro_import.rs
+++ b/src/librustc_metadata/macro_import.rs
@@ -18,6 +18,7 @@ use creader::{CrateReader, Macros};
 use cstore::CStore;
 
 use rustc::hir::def_id::DefIndex;
+use rustc::middle;
 use rustc::session::Session;
 use rustc::util::nodemap::FnvHashMap;
 use rustc_back::dynamic_lib::DynamicLibrary;
@@ -26,7 +27,6 @@ use rustc_macro::__internal::Registry;
 use syntax::ast;
 use syntax::attr;
 use syntax::ext::base::LoadedMacro;
-use syntax::ext;
 use syntax::parse::token;
 use syntax_ext::deriving::custom::CustomDerive;
 use syntax_pos::Span;
@@ -55,7 +55,7 @@ pub fn call_bad_macro_reexport(a: &Session, b: Span) {
 
 pub type MacroSelection = FnvHashMap<token::InternedString, Span>;
 
-impl<'a> ext::base::MacroLoader for MacroLoader<'a> {
+impl<'a> middle::cstore::MacroLoader for MacroLoader<'a> {
     fn load_crate(&mut self,
                   extern_crate: &ast::Item,
                   allows_macros: bool) -> Vec<LoadedMacro> {
diff --git a/src/librustc_metadata/tyencode.rs b/src/librustc_metadata/tyencode.rs
index 8030abf6330e7..dbefd3eacc24a 100644
--- a/src/librustc_metadata/tyencode.rs
+++ b/src/librustc_metadata/tyencode.rs
@@ -104,14 +104,7 @@ pub fn enc_ty<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx
 
             enc_region(w, cx, obj.region_bound);
 
-            // Encode projection_bounds in a stable order
-            let mut projection_bounds: Vec<_> = obj.projection_bounds
-                                                .iter()
-                                                .map(|b| (b.item_name().as_str(), b))
-                                                .collect();
-            projection_bounds.sort_by_key(|&(ref name, _)| name.clone());
-
-            for tp in projection_bounds.iter().map(|&(_, tp)| tp) {
+            for tp in &obj.projection_bounds {
                 write!(w, "P");
                 enc_existential_projection(w, cx, &tp.0);
             }
diff --git a/src/librustc_resolve/assign_ids.rs b/src/librustc_resolve/assign_ids.rs
deleted file mode 100644
index a9e3c6ffe9ed8..0000000000000
--- a/src/librustc_resolve/assign_ids.rs
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use Resolver;
-use rustc::session::Session;
-use rustc::util::nodemap::FnvHashMap;
-use syntax::ast;
-use syntax::ext::hygiene::Mark;
-use syntax::fold::{self, Folder};
-use syntax::ptr::P;
-use syntax::util::move_map::MoveMap;
-use syntax::util::small_vector::SmallVector;
-
-use std::mem;
-
-impl<'a> Resolver<'a> {
-    pub fn assign_node_ids(&mut self, krate: ast::Crate) -> ast::Crate {
-        NodeIdAssigner {
-            sess: self.session,
-            macros_at_scope: &mut self.macros_at_scope,
-        }.fold_crate(krate)
-    }
-}
-
-struct NodeIdAssigner<'a> {
-    sess: &'a Session,
-    macros_at_scope: &'a mut FnvHashMap<ast::NodeId, Vec<Mark>>,
-}
-
-impl<'a> Folder for NodeIdAssigner<'a> {
-    fn new_id(&mut self, old_id: ast::NodeId) -> ast::NodeId {
-        assert_eq!(old_id, ast::DUMMY_NODE_ID);
-        self.sess.next_node_id()
-    }
-
-    fn fold_block(&mut self, block: P<ast::Block>) -> P<ast::Block> {
-        block.map(|mut block| {
-            block.id = self.new_id(block.id);
-
-            let stmt = block.stmts.pop();
-            let mut macros = Vec::new();
-            block.stmts = block.stmts.move_flat_map(|stmt| {
-                if let ast::StmtKind::Item(ref item) = stmt.node {
-                    if let ast::ItemKind::Mac(..) = item.node {
-                        macros.push(item.ident.ctxt.data().outer_mark);
-                        return None;
-                    }
-                }
-
-                let stmt = self.fold_stmt(stmt).pop().unwrap();
-                if !macros.is_empty() {
-                    self.macros_at_scope.insert(stmt.id, mem::replace(&mut macros, Vec::new()));
-                }
-                Some(stmt)
-            });
-
-            stmt.and_then(|mut stmt| {
-                // Avoid wasting a node id on a trailing expression statement,
-                // which shares a HIR node with the expression itself.
-                if let ast::StmtKind::Expr(expr) = stmt.node {
-                    let expr = self.fold_expr(expr);
-                    stmt.id = expr.id;
-                    stmt.node = ast::StmtKind::Expr(expr);
-                    Some(stmt)
-                } else {
-                    self.fold_stmt(stmt).pop()
-                }
-            }).map(|stmt| {
-                if !macros.is_empty() {
-                    self.macros_at_scope.insert(stmt.id, mem::replace(&mut macros, Vec::new()));
-                }
-                block.stmts.push(stmt);
-            });
-
-            block
-        })
-    }
-
-    fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
-        match item.node {
-            ast::ItemKind::Mac(..) => SmallVector::zero(),
-            _ => fold::noop_fold_item(item, self),
-        }
-    }
-}
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index c5b505fba38e9..912b39cafff36 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -45,6 +45,7 @@ use self::ParentLink::*;
 
 use rustc::hir::map::Definitions;
 use rustc::hir::{self, PrimTy, TyBool, TyChar, TyFloat, TyInt, TyUint, TyStr};
+use rustc::middle::cstore::MacroLoader;
 use rustc::session::Session;
 use rustc::lint;
 use rustc::hir::def::*;
@@ -79,10 +80,10 @@ use resolve_imports::{ImportDirective, NameResolution};
 // registered before they are used.
 mod diagnostics;
 
+mod macros;
 mod check_unused;
 mod build_reduced_graph;
 mod resolve_imports;
-mod assign_ids;
 
 enum SuggestionType {
     Macro(String),
@@ -247,7 +248,7 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>,
                                            "method `{}` is not a member of trait `{}`",
                                            method,
                                            trait_);
-            err.span_label(span, &format!("not a member of `{}`", trait_));
+            err.span_label(span, &format!("not a member of trait `{}`", trait_));
             err
         }
         ResolutionError::TypeNotMemberOfTrait(type_, trait_) => {
@@ -257,7 +258,7 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>,
                              "type `{}` is not a member of trait `{}`",
                              type_,
                              trait_);
-            err.span_label(span, &format!("not a member of trait `Foo`"));
+            err.span_label(span, &format!("not a member of trait `{}`", trait_));
             err
         }
         ResolutionError::ConstNotMemberOfTrait(const_, trait_) => {
@@ -267,7 +268,7 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>,
                              "const `{}` is not a member of trait `{}`",
                              const_,
                              trait_);
-            err.span_label(span, &format!("not a member of trait `Foo`"));
+            err.span_label(span, &format!("not a member of trait `{}`", trait_));
             err
         }
         ResolutionError::VariableNotBoundInPattern(variable_name, from, to) => {
@@ -1068,6 +1069,12 @@ pub struct Resolver<'a> {
     arenas: &'a ResolverArenas<'a>,
     dummy_binding: &'a NameBinding<'a>,
     new_import_semantics: bool, // true if `#![feature(item_like_imports)]`
+
+    macro_loader: &'a mut MacroLoader,
+    macro_names: FnvHashSet<Name>,
+
+    // Maps the `Mark` of an expansion to its containing module or block.
+    expansion_data: Vec<macros::ExpansionData>,
 }
 
 pub struct ResolverArenas<'a> {
@@ -1166,7 +1173,10 @@ impl Named for hir::PathSegment {
 }
 
 impl<'a> Resolver<'a> {
-    pub fn new(session: &'a Session, make_glob_map: MakeGlobMap, arenas: &'a ResolverArenas<'a>)
+    pub fn new(session: &'a Session,
+               make_glob_map: MakeGlobMap,
+               macro_loader: &'a mut MacroLoader,
+               arenas: &'a ResolverArenas<'a>)
                -> Resolver<'a> {
         let root_def_id = DefId::local(CRATE_DEF_INDEX);
         let graph_root =
@@ -1227,6 +1237,10 @@ impl<'a> Resolver<'a> {
                 vis: ty::Visibility::Public,
             }),
             new_import_semantics: session.features.borrow().item_like_imports,
+
+            macro_loader: macro_loader,
+            macro_names: FnvHashSet(),
+            expansion_data: vec![macros::ExpansionData::default()],
         }
     }
 
@@ -2768,8 +2782,7 @@ impl<'a> Resolver<'a> {
     }
 
     fn find_best_match(&mut self, name: &str) -> SuggestionType {
-        if let Some(macro_name) = self.session.available_macros
-                                  .borrow().iter().find(|n| n.as_str() == name) {
+        if let Some(macro_name) = self.macro_names.iter().find(|n| n.as_str() == name) {
             return SuggestionType::Macro(format!("{}!", macro_name));
         }
 
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
new file mode 100644
index 0000000000000..67ee4c307d3c3
--- /dev/null
+++ b/src/librustc_resolve/macros.rs
@@ -0,0 +1,217 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use Resolver;
+use rustc::util::nodemap::FnvHashMap;
+use std::cell::RefCell;
+use std::mem;
+use std::rc::Rc;
+use syntax::ast::{self, Name};
+use syntax::errors::DiagnosticBuilder;
+use syntax::ext::base::{self, LoadedMacro, MultiModifier, MultiDecorator};
+use syntax::ext::base::{NormalTT, SyntaxExtension};
+use syntax::ext::expand::{Expansion, Invocation, InvocationKind};
+use syntax::ext::hygiene::Mark;
+use syntax::parse::token::intern;
+use syntax::util::lev_distance::find_best_match_for_name;
+use syntax::visit::{self, Visitor};
+
+#[derive(Clone, Default)]
+pub struct ExpansionData {
+    module: Rc<ModuleData>,
+}
+
+// FIXME(jseyfried): merge with `::ModuleS`.
+#[derive(Default)]
+struct ModuleData {
+    parent: Option<Rc<ModuleData>>,
+    macros: RefCell<FnvHashMap<Name, Rc<SyntaxExtension>>>,
+    macros_escape: bool,
+}
+
+impl<'a> base::Resolver for Resolver<'a> {
+    fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<LoadedMacro> {
+        self.macro_loader.load_crate(extern_crate, allows_macros)
+    }
+
+    fn next_node_id(&mut self) -> ast::NodeId {
+        self.session.next_node_id()
+    }
+
+    fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion) {
+        expansion.visit_with(&mut ExpansionVisitor {
+            current_module: self.expansion_data[mark.as_u32() as usize].module.clone(),
+            resolver: self,
+        });
+    }
+
+    fn add_macro(&mut self, scope: Mark, ident: ast::Ident, ext: Rc<SyntaxExtension>) {
+        if let NormalTT(..) = *ext {
+            self.macro_names.insert(ident.name);
+        }
+
+        let mut module = self.expansion_data[scope.as_u32() as usize].module.clone();
+        while module.macros_escape {
+            module = module.parent.clone().unwrap();
+        }
+        module.macros.borrow_mut().insert(ident.name, ext);
+    }
+
+    fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec<Mark>) {
+        self.macros_at_scope.insert(id, macros);
+    }
+
+    fn find_attr_invoc(&mut self, attrs: &mut Vec<ast::Attribute>) -> Option<ast::Attribute> {
+        for i in 0..attrs.len() {
+            let name = intern(&attrs[i].name());
+            match self.expansion_data[0].module.macros.borrow().get(&name) {
+                Some(ext) => match **ext {
+                    MultiModifier(..) | MultiDecorator(..) => return Some(attrs.remove(i)),
+                    _ => {}
+                },
+                None => {}
+            }
+        }
+        None
+    }
+
+    fn resolve_invoc(&mut self, invoc: &Invocation) -> Option<Rc<SyntaxExtension>> {
+        let (name, span) = match invoc.kind {
+            InvocationKind::Bang { ref mac, .. } => {
+                let path = &mac.node.path;
+                if path.segments.len() > 1 || path.global ||
+                   !path.segments[0].parameters.is_empty() {
+                    self.session.span_err(path.span,
+                                          "expected macro name without module separators");
+                    return None;
+                }
+                (path.segments[0].identifier.name, path.span)
+            }
+            InvocationKind::Attr { ref attr, .. } => (intern(&*attr.name()), attr.span),
+        };
+
+        let mut module = self.expansion_data[invoc.mark().as_u32() as usize].module.clone();
+        loop {
+            if let Some(ext) = module.macros.borrow().get(&name) {
+                return Some(ext.clone());
+            }
+            match module.parent.clone() {
+                Some(parent) => module = parent,
+                None => break,
+            }
+        }
+
+        let mut err =
+            self.session.struct_span_err(span, &format!("macro undefined: '{}!'", name));
+        self.suggest_macro_name(&name.as_str(), &mut err);
+        err.emit();
+        None
+    }
+}
+
+impl<'a> Resolver<'a> {
+    fn suggest_macro_name(&mut self, name: &str, err: &mut DiagnosticBuilder<'a>) {
+        if let Some(suggestion) = find_best_match_for_name(self.macro_names.iter(), name, None) {
+            if suggestion != name {
+                err.help(&format!("did you mean `{}!`?", suggestion));
+            } else {
+                err.help(&format!("have you added the `#[macro_use]` on the module/import?"));
+            }
+        }
+    }
+}
+
+struct ExpansionVisitor<'b, 'a: 'b> {
+    resolver: &'b mut Resolver<'a>,
+    current_module: Rc<ModuleData>,
+}
+
+impl<'a, 'b> ExpansionVisitor<'a, 'b> {
+    fn visit_invoc(&mut self, id: ast::NodeId) {
+        assert_eq!(id, self.resolver.expansion_data.len() as u32);
+        self.resolver.expansion_data.push(ExpansionData {
+            module: self.current_module.clone(),
+        });
+    }
+
+    // does this attribute list contain "macro_use"?
+    fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool {
+        for attr in attrs {
+            if attr.check_name("macro_escape") {
+                let msg = "macro_escape is a deprecated synonym for macro_use";
+                let mut err = self.resolver.session.struct_span_warn(attr.span, msg);
+                if let ast::AttrStyle::Inner = attr.node.style {
+                    err.help("consider an outer attribute, #[macro_use] mod ...").emit();
+                } else {
+                    err.emit();
+                }
+            } else if !attr.check_name("macro_use") {
+                continue;
+            }
+
+            if !attr.is_word() {
+                self.resolver.session.span_err(attr.span,
+                                               "arguments to macro_use are not allowed here");
+            }
+            return true;
+        }
+
+        false
+    }
+}
+
+macro_rules! method {
+    ($visit:ident: $ty:ty, $invoc:path, $walk:ident) => {
+        fn $visit(&mut self, node: &$ty) {
+            match node.node {
+                $invoc(..) => self.visit_invoc(node.id),
+                _ => visit::$walk(self, node),
+            }
+        }
+    }
+}
+
+impl<'a, 'b> Visitor for ExpansionVisitor<'a, 'b>  {
+    method!(visit_trait_item: ast::TraitItem, ast::TraitItemKind::Macro, walk_trait_item);
+    method!(visit_impl_item:  ast::ImplItem,  ast::ImplItemKind::Macro,  walk_impl_item);
+    method!(visit_stmt:       ast::Stmt,      ast::StmtKind::Mac,        walk_stmt);
+    method!(visit_expr:       ast::Expr,      ast::ExprKind::Mac,        walk_expr);
+    method!(visit_pat:        ast::Pat,       ast::PatKind::Mac,         walk_pat);
+    method!(visit_ty:         ast::Ty,        ast::TyKind::Mac,          walk_ty);
+
+    fn visit_item(&mut self, item: &ast::Item) {
+        match item.node {
+            ast::ItemKind::Mac(..) if item.id == ast::DUMMY_NODE_ID => {} // Scope placeholder
+            ast::ItemKind::Mac(..) => self.visit_invoc(item.id),
+            ast::ItemKind::Mod(..) => {
+                let module_data = ModuleData {
+                    parent: Some(self.current_module.clone()),
+                    macros: RefCell::new(FnvHashMap()),
+                    macros_escape: self.contains_macro_use(&item.attrs),
+                };
+                let orig_module = mem::replace(&mut self.current_module, Rc::new(module_data));
+                visit::walk_item(self, item);
+                self.current_module = orig_module;
+            }
+            _ => visit::walk_item(self, item),
+        }
+    }
+
+    fn visit_block(&mut self, block: &ast::Block) {
+        let module_data = ModuleData {
+            parent: Some(self.current_module.clone()),
+            macros: RefCell::new(FnvHashMap()),
+            macros_escape: false,
+        };
+        let orig_module = mem::replace(&mut self.current_module, Rc::new(module_data));
+        visit::walk_block(self, block);
+        self.current_module = orig_module;
+    }
+}
diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs
index 6b48c6ae26dac..3073b1dbfaeeb 100644
--- a/src/librustc_trans/glue.rs
+++ b/src/librustc_trans/glue.rs
@@ -296,6 +296,7 @@ fn trans_custom_dtor<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         sized_args = [v0];
         &sized_args
     } else {
+        // FIXME(#36457) -- we should pass unsized values to drop glue as two arguments
         unsized_args = [
             Load(bcx, get_dataptr(bcx, v0)),
             Load(bcx, get_meta(bcx, v0))
@@ -440,7 +441,9 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
     }
 }
 
-fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueKind<'tcx>)
+fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
+                              v0: ValueRef,
+                              g: DropGlueKind<'tcx>)
                               -> Block<'blk, 'tcx> {
     let t = g.ty();
 
@@ -463,6 +466,7 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK
                 let llval = get_dataptr(bcx, v0);
                 let llbox = Load(bcx, llval);
                 let bcx = drop_ty(bcx, v0, content_ty, DebugLoc::None);
+                // FIXME(#36457) -- we should pass unsized values to drop glue as two arguments
                 let info = get_meta(bcx, v0);
                 let info = Load(bcx, info);
                 let (llsize, llalign) =
@@ -488,6 +492,7 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK
             // No support in vtable for distinguishing destroying with
             // versus without calling Drop::drop. Assert caller is
             // okay with always calling the Drop impl, if any.
+            // FIXME(#36457) -- we should pass unsized values to drop glue as two arguments
             assert!(!skip_dtor);
             let data_ptr = get_dataptr(bcx, v0);
             let vtable_ptr = Load(bcx, get_meta(bcx, v0));
@@ -543,6 +548,7 @@ fn drop_structural_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
     let value = if type_is_sized(cx.tcx(), t) {
         adt::MaybeSizedValue::sized(av)
     } else {
+        // FIXME(#36457) -- we should pass unsized values as two arguments
         let data = Load(cx, get_dataptr(cx, av));
         let info = Load(cx, get_meta(cx, av));
         adt::MaybeSizedValue::unsized_(data, info)
@@ -586,6 +592,7 @@ fn drop_structural_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
                     let val = if type_is_sized(cx.tcx(), field_ty) {
                         llfld_a
                     } else {
+                        // FIXME(#36457) -- we should pass unsized values as two arguments
                         let scratch = alloc_ty(cx, field_ty, "__fat_ptr_iter");
                         Store(cx, llfld_a, get_dataptr(cx, scratch));
                         Store(cx, value.meta, get_meta(cx, scratch));
diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs
index 694db035e282c..0d919cb775701 100644
--- a/src/librustc_trans/intrinsic.rs
+++ b/src/librustc_trans/intrinsic.rs
@@ -194,6 +194,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
             let ptr = if is_sized {
                 llargs[0]
             } else {
+                // FIXME(#36457) -- we should pass unsized values as two arguments
                 let scratch = alloc_ty(bcx, tp_ty, "drop");
                 call_lifetime_start(bcx, scratch);
                 Store(bcx, llargs[0], get_dataptr(bcx, scratch));
diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs
index fbd04d7b38029..baeafbe3e346f 100644
--- a/src/librustc_trans/mir/block.rs
+++ b/src/librustc_trans/mir/block.rs
@@ -242,10 +242,28 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 let lvalue = self.trans_lvalue(&bcx, location);
                 let drop_fn = glue::get_drop_glue(bcx.ccx(), ty);
                 let drop_ty = glue::get_drop_glue_type(bcx.tcx(), ty);
-                let llvalue = if drop_ty != ty {
-                    bcx.pointercast(lvalue.llval, type_of::type_of(bcx.ccx(), drop_ty).ptr_to())
+                let is_sized = common::type_is_sized(bcx.tcx(), ty);
+                let llvalue = if is_sized {
+                    if drop_ty != ty {
+                        bcx.pointercast(lvalue.llval, type_of::type_of(bcx.ccx(), drop_ty).ptr_to())
+                    } else {
+                        lvalue.llval
+                    }
                 } else {
-                    lvalue.llval
+                    // FIXME(#36457) Currently drop glue takes sized
+                    // values as a `*(data, meta)`, but elsewhere in
+                    // MIR we pass `(data, meta)` as two separate
+                    // arguments. It would be better to fix drop glue,
+                    // but I am shooting for a quick fix to #35546
+                    // here that can be cleanly backported to beta, so
+                    // I want to avoid touching all of trans.
+                    bcx.with_block(|bcx| {
+                        let scratch = base::alloc_ty(bcx, ty, "drop");
+                        base::call_lifetime_start(bcx, scratch);
+                        build::Store(bcx, lvalue.llval, base::get_dataptr(bcx, scratch));
+                        build::Store(bcx, lvalue.llextra, base::get_meta(bcx, scratch));
+                        scratch
+                    })
                 };
                 if let Some(unwind) = unwind {
                     bcx.invoke(drop_fn,
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 7d111cdc4156f..04aca8c0947ca 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -1290,12 +1290,15 @@ fn trait_def_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
         }
     }).collect();
 
+    let def_path_hash = tcx.def_path(def_id).deterministic_hash(tcx);
+
     let trait_ref = ty::TraitRef::new(def_id, substs);
     let trait_def = ty::TraitDef::new(unsafety,
                                       paren_sugar,
                                       ty_generics,
                                       trait_ref,
-                                      associated_type_names);
+                                      associated_type_names,
+                                      def_path_hash);
 
     tcx.intern_trait_def(trait_def)
 }
diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs
index 79bbe5e7daa45..246c57ab23871 100644
--- a/src/libstd/time/duration.rs
+++ b/src/libstd/time/duration.rs
@@ -97,6 +97,146 @@ impl Duration {
     #[stable(feature = "duration", since = "1.3.0")]
     #[inline]
     pub fn subsec_nanos(&self) -> u32 { self.nanos }
+
+    /// Checked duration addition. Computes `self + other`, returning `None`
+    /// if overflow occurred.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// #![feature(duration_checked_ops)]
+    ///
+    /// use std::time::Duration;
+    ///
+    /// assert_eq!(Duration::new(0, 0).checked_add(Duration::new(0, 1)), Some(Duration::new(0, 1)));
+    /// assert_eq!(Duration::new(1, 0).checked_add(Duration::new(std::u64::MAX, 0)), None);
+    /// ```
+    #[unstable(feature = "duration_checked_ops", issue = "35774")]
+    #[inline]
+    pub fn checked_add(self, rhs: Duration) -> Option<Duration> {
+        if let Some(mut secs) = self.secs.checked_add(rhs.secs) {
+            let mut nanos = self.nanos + rhs.nanos;
+            if nanos >= NANOS_PER_SEC {
+                nanos -= NANOS_PER_SEC;
+                if let Some(new_secs) = secs.checked_add(1) {
+                    secs = new_secs;
+                } else {
+                    return None;
+                }
+            }
+            debug_assert!(nanos < NANOS_PER_SEC);
+            Some(Duration {
+                secs: secs,
+                nanos: nanos,
+            })
+        } else {
+            None
+        }
+    }
+
+    /// Checked duration subtraction. Computes `self + other`, returning `None`
+    /// if the result would be negative or if underflow occurred.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// #![feature(duration_checked_ops)]
+    ///
+    /// use std::time::Duration;
+    ///
+    /// assert_eq!(Duration::new(0, 1).checked_sub(Duration::new(0, 0)), Some(Duration::new(0, 1)));
+    /// assert_eq!(Duration::new(0, 0).checked_sub(Duration::new(0, 1)), None);
+    /// ```
+    #[unstable(feature = "duration_checked_ops", issue = "35774")]
+    #[inline]
+    pub fn checked_sub(self, rhs: Duration) -> Option<Duration> {
+        if let Some(mut secs) = self.secs.checked_sub(rhs.secs) {
+            let nanos = if self.nanos >= rhs.nanos {
+                self.nanos - rhs.nanos
+            } else {
+                if let Some(sub_secs) = secs.checked_sub(1) {
+                    secs = sub_secs;
+                    self.nanos + NANOS_PER_SEC - rhs.nanos
+                } else {
+                    return None;
+                }
+            };
+            debug_assert!(nanos < NANOS_PER_SEC);
+            Some(Duration { secs: secs, nanos: nanos })
+        } else {
+            None
+        }
+    }
+
+    /// Checked duration multiplication. Computes `self * other`, returning
+    /// `None` if underflow or overflow occurred.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// #![feature(duration_checked_ops)]
+    ///
+    /// use std::time::Duration;
+    ///
+    /// assert_eq!(Duration::new(0, 500_000_001).checked_mul(2), Some(Duration::new(1, 2)));
+    /// assert_eq!(Duration::new(std::u64::MAX - 1, 0).checked_mul(2), None);
+    /// ```
+    #[unstable(feature = "duration_checked_ops", issue = "35774")]
+    #[inline]
+    pub fn checked_mul(self, rhs: u32) -> Option<Duration> {
+        // Multiply nanoseconds as u64, because it cannot overflow that way.
+        let total_nanos = self.nanos as u64 * rhs as u64;
+        let extra_secs = total_nanos / (NANOS_PER_SEC as u64);
+        let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32;
+        if let Some(secs) = self.secs
+            .checked_mul(rhs as u64)
+            .and_then(|s| s.checked_add(extra_secs)) {
+            debug_assert!(nanos < NANOS_PER_SEC);
+            Some(Duration {
+                secs: secs,
+                nanos: nanos,
+            })
+        } else {
+            None
+        }
+    }
+
+    /// Checked duration division. Computes `self / other`, returning `None`
+    /// if `other == 0` or the operation results in underflow or overflow.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// #![feature(duration_checked_ops)]
+    ///
+    /// use std::time::Duration;
+    ///
+    /// assert_eq!(Duration::new(2, 0).checked_div(2), Some(Duration::new(1, 0)));
+    /// assert_eq!(Duration::new(1, 0).checked_div(2), Some(Duration::new(0, 500_000_000)));
+    /// assert_eq!(Duration::new(2, 0).checked_div(0), None);
+    /// ```
+    #[unstable(feature = "duration_checked_ops", issue = "35774")]
+    #[inline]
+    pub fn checked_div(self, rhs: u32) -> Option<Duration> {
+        if rhs != 0 {
+            let secs = self.secs / (rhs as u64);
+            let carry = self.secs - secs * (rhs as u64);
+            let extra_nanos = carry * (NANOS_PER_SEC as u64) / (rhs as u64);
+            let nanos = self.nanos / rhs + (extra_nanos as u32);
+            debug_assert!(nanos < NANOS_PER_SEC);
+            Some(Duration { secs: secs, nanos: nanos })
+        } else {
+            None
+        }
+    }
 }
 
 #[stable(feature = "duration", since = "1.3.0")]
@@ -104,15 +244,7 @@ impl Add for Duration {
     type Output = Duration;
 
     fn add(self, rhs: Duration) -> Duration {
-        let mut secs = self.secs.checked_add(rhs.secs)
-                           .expect("overflow when adding durations");
-        let mut nanos = self.nanos + rhs.nanos;
-        if nanos >= NANOS_PER_SEC {
-            nanos -= NANOS_PER_SEC;
-            secs = secs.checked_add(1).expect("overflow when adding durations");
-        }
-        debug_assert!(nanos < NANOS_PER_SEC);
-        Duration { secs: secs, nanos: nanos }
+        self.checked_add(rhs).expect("overflow when adding durations")
     }
 }
 
@@ -128,17 +260,7 @@ impl Sub for Duration {
     type Output = Duration;
 
     fn sub(self, rhs: Duration) -> Duration {
-        let mut secs = self.secs.checked_sub(rhs.secs)
-                           .expect("overflow when subtracting durations");
-        let nanos = if self.nanos >= rhs.nanos {
-            self.nanos - rhs.nanos
-        } else {
-            secs = secs.checked_sub(1)
-                       .expect("overflow when subtracting durations");
-            self.nanos + NANOS_PER_SEC - rhs.nanos
-        };
-        debug_assert!(nanos < NANOS_PER_SEC);
-        Duration { secs: secs, nanos: nanos }
+        self.checked_sub(rhs).expect("overflow when subtracting durations")
     }
 }
 
@@ -154,15 +276,7 @@ impl Mul<u32> for Duration {
     type Output = Duration;
 
     fn mul(self, rhs: u32) -> Duration {
-        // Multiply nanoseconds as u64, because it cannot overflow that way.
-        let total_nanos = self.nanos as u64 * rhs as u64;
-        let extra_secs = total_nanos / (NANOS_PER_SEC as u64);
-        let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32;
-        let secs = self.secs.checked_mul(rhs as u64)
-                       .and_then(|s| s.checked_add(extra_secs))
-                       .expect("overflow when multiplying duration");
-        debug_assert!(nanos < NANOS_PER_SEC);
-        Duration { secs: secs, nanos: nanos }
+        self.checked_mul(rhs).expect("overflow when multiplying duration by scalar")
     }
 }
 
@@ -178,12 +292,7 @@ impl Div<u32> for Duration {
     type Output = Duration;
 
     fn div(self, rhs: u32) -> Duration {
-        let secs = self.secs / (rhs as u64);
-        let carry = self.secs - secs * (rhs as u64);
-        let extra_nanos = carry * (NANOS_PER_SEC as u64) / (rhs as u64);
-        let nanos = self.nanos / rhs + (extra_nanos as u32);
-        debug_assert!(nanos < NANOS_PER_SEC);
-        Duration { secs: secs, nanos: nanos }
+        self.checked_div(rhs).expect("divide by zero error when dividing duration by scalar")
     }
 }
 
@@ -234,6 +343,15 @@ mod tests {
                    Duration::new(1, 1));
     }
 
+    #[test]
+    fn checked_add() {
+        assert_eq!(Duration::new(0, 0).checked_add(Duration::new(0, 1)),
+                   Some(Duration::new(0, 1)));
+        assert_eq!(Duration::new(0, 500_000_000).checked_add(Duration::new(0, 500_000_001)),
+                   Some(Duration::new(1, 1)));
+        assert_eq!(Duration::new(1, 0).checked_add(Duration::new(::u64::MAX, 0)), None);
+    }
+
     #[test]
     fn sub() {
         assert_eq!(Duration::new(0, 1) - Duration::new(0, 0),
@@ -244,6 +362,18 @@ mod tests {
                    Duration::new(0, 999_999_999));
     }
 
+    #[test]
+    fn checked_sub() {
+        let zero = Duration::new(0, 0);
+        let one_nano = Duration::new(0, 1);
+        let one_sec = Duration::new(1, 0);
+        assert_eq!(one_nano.checked_sub(zero), Some(Duration::new(0, 1)));
+        assert_eq!(one_sec.checked_sub(one_nano),
+                   Some(Duration::new(0, 999_999_999)));
+        assert_eq!(zero.checked_sub(one_nano), None);
+        assert_eq!(zero.checked_sub(one_sec), None);
+    }
+
     #[test] #[should_panic]
     fn sub_bad1() {
         Duration::new(0, 0) - Duration::new(0, 1);
@@ -263,6 +393,16 @@ mod tests {
                    Duration::new(2000, 4000));
     }
 
+    #[test]
+    fn checked_mul() {
+        assert_eq!(Duration::new(0, 1).checked_mul(2), Some(Duration::new(0, 2)));
+        assert_eq!(Duration::new(1, 1).checked_mul(3), Some(Duration::new(3, 3)));
+        assert_eq!(Duration::new(0, 500_000_001).checked_mul(4), Some(Duration::new(2, 4)));
+        assert_eq!(Duration::new(0, 500_000_001).checked_mul(4000),
+                   Some(Duration::new(2000, 4000)));
+        assert_eq!(Duration::new(::u64::MAX - 1, 0).checked_mul(2), None);
+    }
+
     #[test]
     fn div() {
         assert_eq!(Duration::new(0, 1) / 2, Duration::new(0, 0));
@@ -270,4 +410,11 @@ mod tests {
         assert_eq!(Duration::new(99, 999_999_000) / 100,
                    Duration::new(0, 999_999_990));
     }
+
+    #[test]
+    fn checked_div() {
+        assert_eq!(Duration::new(2, 0).checked_div(2), Some(Duration::new(1, 0)));
+        assert_eq!(Duration::new(1, 0).checked_div(2), Some(Duration::new(0, 500_000_000)));
+        assert_eq!(Duration::new(2, 0).checked_div(0), None);
+    }
 }
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index edd38ea23e2fd..fb4816d3847ed 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -10,27 +10,25 @@
 
 pub use self::SyntaxExtension::*;
 
-use ast;
-use ast::{Name, PatKind};
+use ast::{self, Attribute, Name, PatKind};
 use attr::HasAttrs;
-use codemap::{self, CodeMap, ExpnInfo};
+use codemap::{self, CodeMap, ExpnInfo, Spanned, respan};
 use syntax_pos::{Span, ExpnId, NO_EXPANSION};
 use errors::DiagnosticBuilder;
-use ext;
-use ext::expand;
+use ext::expand::{self, Invocation, Expansion};
+use ext::hygiene::Mark;
 use ext::tt::macro_rules;
 use parse;
 use parse::parser;
 use parse::token;
-use parse::token::{InternedString, intern, str_to_ident};
+use parse::token::{InternedString, str_to_ident};
 use ptr::P;
 use std_inject;
 use util::small_vector::SmallVector;
-use util::lev_distance::find_best_match_for_name;
 use fold::Folder;
 use feature_gate;
 
-use std::collections::{HashMap, HashSet};
+use std::collections::HashMap;
 use std::path::PathBuf;
 use std::rc::Rc;
 use tokenstream;
@@ -44,7 +42,7 @@ pub enum Annotatable {
 }
 
 impl HasAttrs for Annotatable {
-    fn attrs(&self) -> &[ast::Attribute] {
+    fn attrs(&self) -> &[Attribute] {
         match *self {
             Annotatable::Item(ref item) => &item.attrs,
             Annotatable::TraitItem(ref trait_item) => &trait_item.attrs,
@@ -52,7 +50,7 @@ impl HasAttrs for Annotatable {
         }
     }
 
-    fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self {
+    fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
         match self {
             Annotatable::Item(item) => Annotatable::Item(item.map_attrs(f)),
             Annotatable::TraitItem(trait_item) => Annotatable::TraitItem(trait_item.map_attrs(f)),
@@ -464,91 +462,16 @@ pub enum SyntaxExtension {
 
 pub type NamedSyntaxExtension = (Name, SyntaxExtension);
 
-/// The base map of methods for expanding syntax extension
-/// AST nodes into full ASTs
-fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
-                                        -> SyntaxEnv {
-    // utility function to simplify creating NormalTT syntax extensions
-    fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension {
-        NormalTT(Box::new(f), None, false)
-    }
-
-    let mut syntax_expanders = SyntaxEnv::new();
-    syntax_expanders.insert(intern("macro_rules"), MacroRulesTT);
-
-    if ecfg.enable_quotes() {
-        // Quasi-quoting expanders
-        syntax_expanders.insert(intern("quote_tokens"),
-                           builtin_normal_expander(
-                                ext::quote::expand_quote_tokens));
-        syntax_expanders.insert(intern("quote_expr"),
-                           builtin_normal_expander(
-                                ext::quote::expand_quote_expr));
-        syntax_expanders.insert(intern("quote_ty"),
-                           builtin_normal_expander(
-                                ext::quote::expand_quote_ty));
-        syntax_expanders.insert(intern("quote_item"),
-                           builtin_normal_expander(
-                                ext::quote::expand_quote_item));
-        syntax_expanders.insert(intern("quote_pat"),
-                           builtin_normal_expander(
-                                ext::quote::expand_quote_pat));
-        syntax_expanders.insert(intern("quote_arm"),
-                           builtin_normal_expander(
-                                ext::quote::expand_quote_arm));
-        syntax_expanders.insert(intern("quote_stmt"),
-                           builtin_normal_expander(
-                                ext::quote::expand_quote_stmt));
-        syntax_expanders.insert(intern("quote_matcher"),
-                           builtin_normal_expander(
-                                ext::quote::expand_quote_matcher));
-        syntax_expanders.insert(intern("quote_attr"),
-                           builtin_normal_expander(
-                                ext::quote::expand_quote_attr));
-        syntax_expanders.insert(intern("quote_arg"),
-                           builtin_normal_expander(
-                                ext::quote::expand_quote_arg));
-        syntax_expanders.insert(intern("quote_block"),
-                           builtin_normal_expander(
-                                ext::quote::expand_quote_block));
-        syntax_expanders.insert(intern("quote_meta_item"),
-                           builtin_normal_expander(
-                                ext::quote::expand_quote_meta_item));
-        syntax_expanders.insert(intern("quote_path"),
-                           builtin_normal_expander(
-                                ext::quote::expand_quote_path));
-    }
-
-    syntax_expanders.insert(intern("line"),
-                            builtin_normal_expander(
-                                    ext::source_util::expand_line));
-    syntax_expanders.insert(intern("column"),
-                            builtin_normal_expander(
-                                    ext::source_util::expand_column));
-    syntax_expanders.insert(intern("file"),
-                            builtin_normal_expander(
-                                    ext::source_util::expand_file));
-    syntax_expanders.insert(intern("stringify"),
-                            builtin_normal_expander(
-                                    ext::source_util::expand_stringify));
-    syntax_expanders.insert(intern("include"),
-                            builtin_normal_expander(
-                                    ext::source_util::expand_include));
-    syntax_expanders.insert(intern("include_str"),
-                            builtin_normal_expander(
-                                    ext::source_util::expand_include_str));
-    syntax_expanders.insert(intern("include_bytes"),
-                            builtin_normal_expander(
-                                    ext::source_util::expand_include_bytes));
-    syntax_expanders.insert(intern("module_path"),
-                            builtin_normal_expander(
-                                    ext::source_util::expand_mod));
-    syntax_expanders
-}
-
-pub trait MacroLoader {
-    fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool)
-                  -> Vec<LoadedMacro>;
+pub trait Resolver {
+    fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<LoadedMacro>;
+    fn next_node_id(&mut self) -> ast::NodeId;
+
+    fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion);
+    fn add_macro(&mut self, scope: Mark, ident: ast::Ident, ext: Rc<SyntaxExtension>);
+    fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec<Mark>);
+
+    fn find_attr_invoc(&mut self, attrs: &mut Vec<Attribute>) -> Option<Attribute>;
+    fn resolve_invoc(&mut self, invoc: &Invocation) -> Option<Rc<SyntaxExtension>>;
 }
 
 pub enum LoadedMacro {
@@ -556,11 +479,35 @@ pub enum LoadedMacro {
     CustomDerive(String, Box<MultiItemModifier>),
 }
 
-pub struct DummyMacroLoader;
-impl MacroLoader for DummyMacroLoader {
-    fn load_crate(&mut self, _: &ast::Item, _: bool) -> Vec<LoadedMacro> {
+pub struct DummyResolver;
+
+impl Resolver for DummyResolver {
+    fn load_crate(&mut self, _extern_crate: &ast::Item, _allows_macros: bool) -> Vec<LoadedMacro> {
         Vec::new()
     }
+    fn next_node_id(&mut self) -> ast::NodeId { ast::DUMMY_NODE_ID }
+
+    fn visit_expansion(&mut self, _invoc: Mark, _expansion: &Expansion) {}
+    fn add_macro(&mut self, _scope: Mark, _ident: ast::Ident, _ext: Rc<SyntaxExtension>) {}
+    fn add_expansions_at_stmt(&mut self, _id: ast::NodeId, _macros: Vec<Mark>) {}
+
+    fn find_attr_invoc(&mut self, _attrs: &mut Vec<Attribute>) -> Option<Attribute> { None }
+    fn resolve_invoc(&mut self, _invoc: &Invocation) -> Option<Rc<SyntaxExtension>> { None }
+}
+
+#[derive(Clone)]
+pub struct ModuleData {
+    pub mod_path: Vec<ast::Ident>,
+    pub directory: PathBuf,
+}
+
+#[derive(Clone)]
+pub struct ExpansionData {
+    pub mark: Mark,
+    pub depth: usize,
+    pub backtrace: ExpnId,
+    pub module: Rc<ModuleData>,
+    pub in_block: bool,
 }
 
 /// One of these is made during expansion and incrementally updated as we go;
@@ -569,63 +516,68 @@ impl MacroLoader for DummyMacroLoader {
 pub struct ExtCtxt<'a> {
     pub parse_sess: &'a parse::ParseSess,
     pub cfg: ast::CrateConfig,
-    pub backtrace: ExpnId,
     pub ecfg: expand::ExpansionConfig<'a>,
     pub crate_root: Option<&'static str>,
-    pub loader: &'a mut MacroLoader,
-
+    pub resolver: &'a mut Resolver,
     pub exported_macros: Vec<ast::MacroDef>,
-
-    pub syntax_env: SyntaxEnv,
     pub derive_modes: HashMap<InternedString, Box<MultiItemModifier>>,
-    pub recursion_count: usize,
+    pub current_expansion: ExpansionData,
 }
 
 impl<'a> ExtCtxt<'a> {
     pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig,
                ecfg: expand::ExpansionConfig<'a>,
-               loader: &'a mut MacroLoader)
+               resolver: &'a mut Resolver)
                -> ExtCtxt<'a> {
         ExtCtxt {
-            syntax_env: initial_syntax_expander_table(&ecfg),
             parse_sess: parse_sess,
             cfg: cfg,
-            backtrace: NO_EXPANSION,
             ecfg: ecfg,
             crate_root: None,
             exported_macros: Vec::new(),
-            loader: loader,
+            resolver: resolver,
             derive_modes: HashMap::new(),
-            recursion_count: 0,
+            current_expansion: ExpansionData {
+                mark: Mark::root(),
+                depth: 0,
+                backtrace: NO_EXPANSION,
+                module: Rc::new(ModuleData { mod_path: Vec::new(), directory: PathBuf::new() }),
+                in_block: false,
+            },
         }
     }
 
     /// Returns a `Folder` for deeply expanding all macros in an AST node.
     pub fn expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> {
-        expand::MacroExpander::new(self, false, false)
+        expand::MacroExpander::new(self, false)
+    }
+
+    /// Returns a `Folder` that deeply expands all macros and assigns all node ids in an AST node.
+    /// Once node ids are assigned, the node may not be expanded, removed, or otherwise modified.
+    pub fn monotonic_expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> {
+        expand::MacroExpander::new(self, true)
     }
 
     pub fn new_parser_from_tts(&self, tts: &[tokenstream::TokenTree])
         -> parser::Parser<'a> {
         parse::tts_to_parser(self.parse_sess, tts.to_vec(), self.cfg())
     }
-
     pub fn codemap(&self) -> &'a CodeMap { self.parse_sess.codemap() }
     pub fn parse_sess(&self) -> &'a parse::ParseSess { self.parse_sess }
     pub fn cfg(&self) -> ast::CrateConfig { self.cfg.clone() }
     pub fn call_site(&self) -> Span {
-        self.codemap().with_expn_info(self.backtrace, |ei| match ei {
+        self.codemap().with_expn_info(self.backtrace(), |ei| match ei {
             Some(expn_info) => expn_info.call_site,
             None => self.bug("missing top span")
         })
     }
-    pub fn backtrace(&self) -> ExpnId { self.backtrace }
+    pub fn backtrace(&self) -> ExpnId { self.current_expansion.backtrace }
 
     /// Returns span for the macro which originally caused the current expansion to happen.
     ///
     /// Stops backtracing at include! boundary.
     pub fn expansion_cause(&self) -> Span {
-        let mut expn_id = self.backtrace;
+        let mut expn_id = self.backtrace();
         let mut last_macro = None;
         loop {
             if self.codemap().with_expn_info(expn_id, |info| {
@@ -646,15 +598,15 @@ impl<'a> ExtCtxt<'a> {
     }
 
     pub fn bt_push(&mut self, ei: ExpnInfo) {
-        if self.recursion_count > self.ecfg.recursion_limit {
+        if self.current_expansion.depth > self.ecfg.recursion_limit {
             self.span_fatal(ei.call_site,
                             &format!("recursion limit reached while expanding the macro `{}`",
                                     ei.callee.name()));
         }
 
         let mut call_site = ei.call_site;
-        call_site.expn_id = self.backtrace;
-        self.backtrace = self.codemap().record_expansion(ExpnInfo {
+        call_site.expn_id = self.backtrace();
+        self.current_expansion.backtrace = self.codemap().record_expansion(ExpnInfo {
             call_site: call_site,
             callee: ei.callee
         });
@@ -667,14 +619,11 @@ impl<'a> ExtCtxt<'a> {
         }
         if def.use_locally {
             let ext = macro_rules::compile(self, &def);
-            self.syntax_env.insert(def.ident.name, ext);
+            self.resolver.add_macro(self.current_expansion.mark, def.ident, Rc::new(ext));
         }
     }
 
-    pub fn insert_custom_derive(&mut self,
-                                name: &str,
-                                ext: Box<MultiItemModifier>,
-                                sp: Span) {
+    pub fn insert_custom_derive(&mut self, name: &str, ext: Box<MultiItemModifier>, sp: Span) {
         if !self.ecfg.enable_rustc_macro() {
             feature_gate::emit_feature_err(&self.parse_sess.span_diagnostic,
                                            "rustc_macro",
@@ -685,8 +634,7 @@ impl<'a> ExtCtxt<'a> {
         }
         let name = token::intern_and_get_ident(name);
         if self.derive_modes.insert(name.clone(), ext).is_some() {
-            self.span_err(sp, &format!("cannot shadow existing derive mode `{}`",
-                                       name));
+            self.span_err(sp, &format!("cannot shadow existing derive mode `{}`", name));
         }
     }
 
@@ -765,20 +713,6 @@ impl<'a> ExtCtxt<'a> {
         token::intern(st)
     }
 
-    pub fn suggest_macro_name(&mut self,
-                              name: &str,
-                              err: &mut DiagnosticBuilder<'a>) {
-        let names = &self.syntax_env.names;
-        if let Some(suggestion) = find_best_match_for_name(names.iter(), name, None) {
-            if suggestion != name {
-                err.help(&format!("did you mean `{}!`?", suggestion));
-            } else {
-                err.help(&format!("have you added the `#[macro_use]` on the \
-                                   module/import?"));
-            }
-        }
-    }
-
     pub fn initialize(&mut self, user_exts: Vec<NamedSyntaxExtension>, krate: &ast::Crate) {
         if std_inject::no_core(&krate) {
             self.crate_root = None;
@@ -789,27 +723,27 @@ impl<'a> ExtCtxt<'a> {
         }
 
         for (name, extension) in user_exts {
-            self.syntax_env.insert(name, extension);
+            let ident = ast::Ident::with_empty_ctxt(name);
+            self.resolver.add_macro(Mark::root(), ident, Rc::new(extension));
         }
 
-        self.syntax_env.current_module = Module(0);
-        let mut paths = ModulePaths {
+        let mut module = ModuleData {
             mod_path: vec![token::str_to_ident(&self.ecfg.crate_name)],
             directory: PathBuf::from(self.parse_sess.codemap().span_to_filename(krate.span)),
         };
-        paths.directory.pop();
-        self.syntax_env.module_data[0].paths = Rc::new(paths);
+        module.directory.pop();
+        self.current_expansion.module = Rc::new(module);
     }
 }
 
 /// Extract a string literal from the macro expanded version of `expr`,
 /// emitting `err_msg` if `expr` is not a string literal. This does not stop
 /// compilation on error, merely emits a non-fatal error and returns None.
-pub fn expr_to_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &str)
-                      -> Option<(InternedString, ast::StrStyle)> {
+pub fn expr_to_spanned_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &str)
+                              -> Option<Spanned<(InternedString, ast::StrStyle)>> {
     // Update `expr.span`'s expn_id now in case expr is an `include!` macro invocation.
     let expr = expr.map(|mut expr| {
-        expr.span.expn_id = cx.backtrace;
+        expr.span.expn_id = cx.backtrace();
         expr
     });
 
@@ -817,7 +751,7 @@ pub fn expr_to_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &str)
     let expr = cx.expander().fold_expr(expr);
     match expr.node {
         ast::ExprKind::Lit(ref l) => match l.node {
-            ast::LitKind::Str(ref s, style) => return Some(((*s).clone(), style)),
+            ast::LitKind::Str(ref s, style) => return Some(respan(expr.span, (s.clone(), style))),
             _ => cx.span_err(l.span, err_msg)
         },
         _ => cx.span_err(expr.span, err_msg)
@@ -825,6 +759,11 @@ pub fn expr_to_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &str)
     None
 }
 
+pub fn expr_to_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &str)
+                      -> Option<(InternedString, ast::StrStyle)> {
+    expr_to_spanned_string(cx, expr, err_msg).map(|s| s.node)
+}
+
 /// Non-fatally assert that `tts` is empty. Note that this function
 /// returns even when `tts` is non-empty, macros that *need* to stop
 /// compilation should call
@@ -851,7 +790,7 @@ pub fn get_single_str_from_tts(cx: &mut ExtCtxt,
         cx.span_err(sp, &format!("{} takes 1 argument", name));
         return None
     }
-    let ret = cx.expander().fold_expr(panictry!(p.parse_expr()));
+    let ret = panictry!(p.parse_expr());
     if p.token != token::Eof {
         cx.span_err(sp, &format!("{} takes 1 argument", name));
     }
@@ -879,104 +818,3 @@ pub fn get_exprs_from_tts(cx: &mut ExtCtxt,
     }
     Some(es)
 }
-
-/// In order to have some notion of scoping for macros,
-/// we want to implement the notion of a transformation
-/// environment.
-///
-/// This environment maps Names to SyntaxExtensions.
-pub struct SyntaxEnv {
-    module_data: Vec<ModuleData>,
-    pub current_module: Module,
-
-    /// All bang-style macro/extension names
-    /// encountered so far; to be used for diagnostics in resolve
-    pub names: HashSet<Name>,
-}
-
-#[derive(Copy, Clone, PartialEq, Eq)]
-pub struct Module(u32);
-
-struct ModuleData {
-    parent: Module,
-    paths: Rc<ModulePaths>,
-    macros: HashMap<Name, Rc<SyntaxExtension>>,
-    macros_escape: bool,
-    in_block: bool,
-}
-
-#[derive(Clone)]
-pub struct ModulePaths {
-    pub mod_path: Vec<ast::Ident>,
-    pub directory: PathBuf,
-}
-
-impl SyntaxEnv {
-    fn new() -> SyntaxEnv {
-        let mut env = SyntaxEnv {
-            current_module: Module(0),
-            module_data: Vec::new(),
-            names: HashSet::new(),
-        };
-        let paths = Rc::new(ModulePaths { mod_path: Vec::new(), directory: PathBuf::new() });
-        env.add_module(false, false, paths);
-        env
-    }
-
-    fn data(&self, module: Module) -> &ModuleData {
-        &self.module_data[module.0 as usize]
-    }
-
-    pub fn paths(&self) -> Rc<ModulePaths> {
-        self.data(self.current_module).paths.clone()
-    }
-
-    pub fn in_block(&self) -> bool {
-        self.data(self.current_module).in_block
-    }
-
-    pub fn add_module(&mut self, macros_escape: bool, in_block: bool, paths: Rc<ModulePaths>)
-                      -> Module {
-        let data = ModuleData {
-            parent: self.current_module,
-            paths: paths,
-            macros: HashMap::new(),
-            macros_escape: macros_escape,
-            in_block: in_block,
-        };
-
-        self.module_data.push(data);
-        Module(self.module_data.len() as u32 - 1)
-    }
-
-    pub fn find(&self, name: Name) -> Option<Rc<SyntaxExtension>> {
-        let mut module = self.current_module;
-        let mut module_data;
-        loop {
-            module_data = self.data(module);
-            if let Some(ext) = module_data.macros.get(&name) {
-                return Some(ext.clone());
-            }
-            if module == module_data.parent {
-                return None;
-            }
-            module = module_data.parent;
-        }
-    }
-
-    pub fn insert(&mut self, name: Name, ext: SyntaxExtension) {
-        if let NormalTT(..) = ext {
-            self.names.insert(name);
-        }
-
-        let mut module = self.current_module;
-        while self.data(module).macros_escape {
-            module = self.data(module).parent;
-        }
-        self.module_data[module.0 as usize].macros.insert(name, Rc::new(ext));
-    }
-
-    pub fn is_crate_root(&mut self) -> bool {
-        self.current_module == Module(0)
-    }
-}
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 3dcdbc8909627..b81d95a6998c3 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -97,6 +97,7 @@ pub trait AstBuilder {
                       typ: P<ast::Ty>,
                       ex: P<ast::Expr>)
                       -> P<ast::Stmt>;
+    fn stmt_let_type_only(&self, span: Span, ty: P<ast::Ty>) -> ast::Stmt;
     fn stmt_item(&self, sp: Span, item: P<ast::Item>) -> ast::Stmt;
 
     // blocks
@@ -577,6 +578,23 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
         })
     }
 
+    // Generate `let _: Type;`, usually used for type assertions.
+    fn stmt_let_type_only(&self, span: Span, ty: P<ast::Ty>) -> ast::Stmt {
+        let local = P(ast::Local {
+            pat: self.pat_wild(span),
+            ty: Some(ty),
+            init: None,
+            id: ast::DUMMY_NODE_ID,
+            span: span,
+            attrs: ast::ThinVec::new(),
+        });
+        ast::Stmt {
+            id: ast::DUMMY_NODE_ID,
+            node: ast::StmtKind::Local(local),
+            span: span,
+        }
+    }
+
     fn stmt_item(&self, sp: Span, item: P<ast::Item>) -> ast::Stmt {
         ast::Stmt {
             id: ast::DUMMY_NODE_ID,
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 4715eda837490..62e299684b760 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -25,6 +25,7 @@ use parse::token::{intern, keywords};
 use ptr::P;
 use tokenstream::TokenTree;
 use util::small_vector::SmallVector;
+use visit::Visitor;
 
 use std::mem;
 use std::path::PathBuf;
@@ -32,7 +33,8 @@ use std::rc::Rc;
 
 macro_rules! expansions {
     ($($kind:ident: $ty:ty [$($vec:ident, $ty_elt:ty)*], $kind_name:expr, .$make:ident,
-            $(.$fold:ident)*  $(lift .$fold_elt:ident)*;)*) => {
+            $(.$fold:ident)*  $(lift .$fold_elt:ident)*,
+            $(.$visit:ident)*  $(lift .$visit_elt:ident)*;)*) => {
         #[derive(Copy, Clone)]
         pub enum ExpansionKind { OptExpr, $( $kind, )*  }
         pub enum Expansion { OptExpr(Option<P<ast::Expr>>), $( $kind($ty), )* }
@@ -77,6 +79,17 @@ macro_rules! expansions {
                     }, )*)*
                 }
             }
+
+            pub fn visit_with<V: Visitor>(&self, visitor: &mut V) {
+                match *self {
+                    Expansion::OptExpr(Some(ref expr)) => visitor.visit_expr(expr),
+                    Expansion::OptExpr(None) => {}
+                    $($( Expansion::$kind(ref ast) => visitor.$visit(ast), )*)*
+                    $($( Expansion::$kind(ref ast) => for ast in ast.as_slice() {
+                        visitor.$visit_elt(ast);
+                    }, )*)*
+                }
+            }
         }
 
         impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
@@ -94,17 +107,17 @@ macro_rules! expansions {
 }
 
 expansions! {
-    Expr: P<ast::Expr> [], "expression", .make_expr, .fold_expr;
-    Pat: P<ast::Pat>   [], "pattern",    .make_pat,  .fold_pat;
-    Ty: P<ast::Ty>     [], "type",       .make_ty,   .fold_ty;
+    Expr: P<ast::Expr> [], "expression", .make_expr, .fold_expr, .visit_expr;
+    Pat: P<ast::Pat>   [], "pattern",    .make_pat,  .fold_pat,  .visit_pat;
+    Ty: P<ast::Ty>     [], "type",       .make_ty,   .fold_ty,   .visit_ty;
     Stmts: SmallVector<ast::Stmt> [SmallVector, ast::Stmt],
-        "statement",  .make_stmts,       lift .fold_stmt;
+        "statement",  .make_stmts,       lift .fold_stmt, lift .visit_stmt;
     Items: SmallVector<P<ast::Item>> [SmallVector, P<ast::Item>],
-        "item",       .make_items,       lift .fold_item;
+        "item",       .make_items,       lift .fold_item, lift .visit_item;
     TraitItems: SmallVector<ast::TraitItem> [SmallVector, ast::TraitItem],
-        "trait item", .make_trait_items, lift .fold_trait_item;
+        "trait item", .make_trait_items, lift .fold_trait_item, lift .visit_trait_item;
     ImplItems: SmallVector<ast::ImplItem> [SmallVector, ast::ImplItem],
-        "impl item",  .make_impl_items,  lift .fold_impl_item;
+        "impl item",  .make_impl_items,  lift .fold_impl_item,  lift .visit_impl_item;
 }
 
 impl ExpansionKind {
@@ -127,15 +140,12 @@ impl ExpansionKind {
 }
 
 pub struct Invocation {
-    kind: InvocationKind,
+    pub kind: InvocationKind,
     expansion_kind: ExpansionKind,
-    mark: Mark,
-    module: Module,
-    backtrace: ExpnId,
-    depth: usize,
+    expansion_data: ExpansionData,
 }
 
-enum InvocationKind {
+pub enum InvocationKind {
     Bang {
         attrs: Vec<ast::Attribute>,
         mac: ast::Mac,
@@ -148,29 +158,53 @@ enum InvocationKind {
     },
 }
 
+impl Invocation {
+    fn span(&self) -> Span {
+        match self.kind {
+            InvocationKind::Bang { span, .. } => span,
+            InvocationKind::Attr { ref attr, .. } => attr.span,
+        }
+    }
+
+    pub fn mark(&self) -> Mark {
+        self.expansion_data.mark
+    }
+}
+
 pub struct MacroExpander<'a, 'b:'a> {
     pub cx: &'a mut ExtCtxt<'b>,
     pub single_step: bool,
     pub keep_macs: bool,
+    monotonic: bool, // c.f. `cx.monotonic_expander()`
 }
 
 impl<'a, 'b> MacroExpander<'a, 'b> {
-    pub fn new(cx: &'a mut ExtCtxt<'b>,
-               single_step: bool,
-               keep_macs: bool) -> MacroExpander<'a, 'b> {
+    pub fn new(cx: &'a mut ExtCtxt<'b>, monotonic: bool) -> Self {
         MacroExpander {
             cx: cx,
-            single_step: single_step,
-            keep_macs: keep_macs
+            monotonic: monotonic,
+            single_step: false,
+            keep_macs: false,
         }
     }
 
     fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate {
         let err_count = self.cx.parse_sess.span_diagnostic.err_count();
 
-        let items = Expansion::Items(SmallVector::many(krate.module.items));
-        krate.module.items = self.expand(items).make_items().into();
-        krate.exported_macros = self.cx.exported_macros.clone();
+        let mut krate_item = placeholder(ExpansionKind::Items, ast::DUMMY_NODE_ID)
+            .make_items().pop().unwrap().unwrap();
+        krate_item.node = ast::ItemKind::Mod(krate.module);
+        let krate_item = Expansion::Items(SmallVector::one(P(krate_item)));
+
+        krate.module = match self.expand(krate_item).make_items().pop().unwrap().unwrap().node {
+            ast::ItemKind::Mod(module) => module,
+            _ => unreachable!(),
+        };
+        krate.exported_macros = mem::replace(&mut self.cx.exported_macros, Vec::new());
+
+        for def in &mut krate.exported_macros {
+            def.id = self.cx.resolver.next_node_id()
+        }
 
         if self.cx.parse_sess.span_diagnostic.err_count() > err_count {
             self.cx.parse_sess.span_diagnostic.abort_if_errors();
@@ -181,21 +215,23 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
 
     // Fully expand all the invocations in `expansion`.
     fn expand(&mut self, expansion: Expansion) -> Expansion {
-        self.cx.recursion_count = 0;
+        let orig_expansion_data = self.cx.current_expansion.clone();
+        self.cx.current_expansion.depth = 0;
+
         let (expansion, mut invocations) = self.collect_invocations(expansion);
         invocations.reverse();
 
         let mut expansions = vec![vec![(0, expansion)]];
         while let Some(invoc) = invocations.pop() {
-            let Invocation { mark, module, depth, backtrace, .. } = invoc;
-            self.cx.syntax_env.current_module = module;
-            self.cx.recursion_count = depth;
-            self.cx.backtrace = backtrace;
+            let ExpansionData { depth, mark, .. } = invoc.expansion_data;
+            self.cx.current_expansion = invoc.expansion_data.clone();
 
-            let expansion = self.expand_invoc(invoc);
+            let expansion = match self.cx.resolver.resolve_invoc(&invoc) {
+                Some(ext) => self.expand_invoc(invoc, ext),
+                None => invoc.expansion_kind.dummy(invoc.span()),
+            };
 
-            self.cx.syntax_env.current_module = module;
-            self.cx.recursion_count = depth + 1;
+            self.cx.current_expansion.depth = depth + 1;
             let (expansion, new_invocations) = self.collect_invocations(expansion);
 
             if expansions.len() == depth {
@@ -207,7 +243,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             }
         }
 
-        let mut placeholder_expander = PlaceholderExpander::new();
+        self.cx.current_expansion = orig_expansion_data;
+
+        let mut placeholder_expander = PlaceholderExpander::new(self.cx, self.monotonic);
         while let Some(expansions) = expansions.pop() {
             for (mark, expansion) in expansions.into_iter().rev() {
                 let expansion = expansion.fold_with(&mut placeholder_expander);
@@ -230,33 +268,31 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 },
                 cx: self.cx,
                 invocations: Vec::new(),
+                monotonic: self.monotonic,
             };
             (expansion.fold_with(&mut collector), collector.invocations)
         };
-
         self.cx.cfg = crate_config;
+
+        let mark = self.cx.current_expansion.mark;
+        self.cx.resolver.visit_expansion(mark, &result.0);
         result
     }
 
-    fn expand_invoc(&mut self, invoc: Invocation) -> Expansion {
+    fn expand_invoc(&mut self, invoc: Invocation, ext: Rc<SyntaxExtension>) -> Expansion {
         match invoc.kind {
-            InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc),
-            InvocationKind::Attr { .. } => self.expand_attr_invoc(invoc),
+            InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc, ext),
+            InvocationKind::Attr { .. } => self.expand_attr_invoc(invoc, ext),
         }
     }
 
-    fn expand_attr_invoc(&mut self, invoc: Invocation) -> Expansion {
+    fn expand_attr_invoc(&mut self, invoc: Invocation, ext: Rc<SyntaxExtension>) -> Expansion {
         let Invocation { expansion_kind: kind, .. } = invoc;
         let (attr, item) = match invoc.kind {
             InvocationKind::Attr { attr, item } => (attr, item),
             _ => unreachable!(),
         };
 
-        let extension = match self.cx.syntax_env.find(intern(&attr.name())) {
-            Some(extension) => extension,
-            None => unreachable!(),
-        };
-
         attr::mark_used(&attr);
         self.cx.bt_push(ExpnInfo {
             call_site: attr.span,
@@ -267,7 +303,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             }
         });
 
-        match *extension {
+        match *ext {
             MultiModifier(ref mac) => {
                 let item = mac.expand(self.cx, attr.span, &attr.node.value, item);
                 kind.expect_from_annotatables(item)
@@ -284,8 +320,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
     }
 
     /// Expand a macro invocation. Returns the result of expansion.
-    fn expand_bang_invoc(&mut self, invoc: Invocation) -> Expansion {
-        let Invocation { mark, expansion_kind: kind, .. } = invoc;
+    fn expand_bang_invoc(&mut self, invoc: Invocation, ext: Rc<SyntaxExtension>) -> Expansion {
+        let (mark, kind) = (invoc.mark(), invoc.expansion_kind);
         let (attrs, mac, ident, span) = match invoc.kind {
             InvocationKind::Bang { attrs, mac, ident, span } => (attrs, mac, ident, span),
             _ => unreachable!(),
@@ -306,19 +342,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         }
 
         let extname = path.segments[0].identifier.name;
-        let extension = if let Some(extension) = self.cx.syntax_env.find(extname) {
-            extension
-        } else {
-            let mut err =
-                self.cx.struct_span_err(path.span, &format!("macro undefined: '{}!'", &extname));
-            self.cx.suggest_macro_name(&extname.as_str(), &mut err);
-            err.emit();
-            return kind.dummy(span);
-        };
-
         let ident = ident.unwrap_or(keywords::Invalid.ident());
         let marked_tts = mark_tts(&tts, mark);
-        let opt_expanded = match *extension {
+        let opt_expanded = match *ext {
             NormalTT(ref expandfun, exp_span, allow_internal_unstable) => {
                 if ident.name != keywords::Invalid.name() {
                     let msg =
@@ -425,6 +451,7 @@ struct InvocationCollector<'a, 'b: 'a> {
     cx: &'a mut ExtCtxt<'b>,
     cfg: StripUnconfigured<'a>,
     invocations: Vec<Invocation>,
+    monotonic: bool,
 }
 
 macro_rules! fully_configure {
@@ -442,10 +469,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
         self.invocations.push(Invocation {
             kind: kind,
             expansion_kind: expansion_kind,
-            mark: mark,
-            module: self.cx.syntax_env.current_module,
-            backtrace: self.cx.backtrace,
-            depth: self.cx.recursion_count,
+            expansion_data: ExpansionData { mark: mark, ..self.cx.current_expansion.clone() },
         });
         placeholder(expansion_kind, mark.as_u32())
     }
@@ -462,50 +486,15 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
     }
 
     // If `item` is an attr invocation, remove and return the macro attribute.
-    fn classify_item<T: HasAttrs>(&self, mut item: T) -> (T, Option<ast::Attribute>) {
+    fn classify_item<T: HasAttrs>(&mut self, mut item: T) -> (T, Option<ast::Attribute>) {
         let mut attr = None;
         item = item.map_attrs(|mut attrs| {
-            for i in 0..attrs.len() {
-                if let Some(extension) = self.cx.syntax_env.find(intern(&attrs[i].name())) {
-                    match *extension {
-                        MultiModifier(..) | MultiDecorator(..) => {
-                            attr = Some(attrs.remove(i));
-                            break;
-                        }
-                        _ => {}
-                    }
-                }
-            }
+            attr = self.cx.resolver.find_attr_invoc(&mut attrs);
             attrs
         });
         (item, attr)
     }
 
-    // does this attribute list contain "macro_use" ?
-    fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool {
-        for attr in attrs {
-            let mut is_use = attr.check_name("macro_use");
-            if attr.check_name("macro_escape") {
-                let msg = "macro_escape is a deprecated synonym for macro_use";
-                let mut err = self.cx.struct_span_warn(attr.span, msg);
-                is_use = true;
-                if let ast::AttrStyle::Inner = attr.node.style {
-                    err.help("consider an outer attribute, #[macro_use] mod ...").emit();
-                } else {
-                    err.emit();
-                }
-            };
-
-            if is_use {
-                if !attr.is_word() {
-                    self.cx.span_err(attr.span, "arguments to macro_use are not allowed here");
-                }
-                return true;
-            }
-        }
-        false
-    }
-
     fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
         self.cfg.configure(node)
     }
@@ -554,9 +543,14 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
             None => return SmallVector::zero(),
         };
 
-        let (mac, style, attrs) = match stmt.node {
-            StmtKind::Mac(mac) => mac.unwrap(),
-            _ => return noop_fold_stmt(stmt, self),
+        let (mac, style, attrs) = if let StmtKind::Mac(mac) = stmt.node {
+            mac.unwrap()
+        } else {
+            // The placeholder expander gives ids to statements, so we avoid folding the id here.
+            let ast::Stmt { id, node, span } = stmt;
+            return noop_fold_stmt_kind(node, self).into_iter().map(|node| {
+                ast::Stmt { id: id, node: node, span: span }
+            }).collect()
         };
 
         let mut placeholder =
@@ -574,11 +568,9 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
     }
 
     fn fold_block(&mut self, block: P<Block>) -> P<Block> {
-        let paths = self.cx.syntax_env.paths();
-        let module = self.cx.syntax_env.add_module(false, true, paths);
-        let orig_module = mem::replace(&mut self.cx.syntax_env.current_module, module);
+        let orig_in_block = mem::replace(&mut self.cx.current_expansion.in_block, true);
         let result = noop_fold_block(block, self);
-        self.cx.syntax_env.current_module = orig_module;
+        self.cx.current_expansion.in_block = orig_in_block;
         result
     }
 
@@ -613,8 +605,12 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
                 })
             }
             ast::ItemKind::Mod(ast::Mod { inner, .. }) => {
-                let mut paths = (*self.cx.syntax_env.paths()).clone();
-                paths.mod_path.push(item.ident);
+                if item.ident == keywords::Invalid.ident() {
+                    return noop_fold_item(item, self);
+                }
+
+                let mut module = (*self.cx.current_expansion.module).clone();
+                module.mod_path.push(item.ident);
 
                 // Detect if this is an inline module (`mod m { ... }` as opposed to `mod m;`).
                 // In the non-inline case, `inner` is never the dummy span (c.f. `parse_item_mod`).
@@ -622,29 +618,27 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
                 let inline_module = item.span.contains(inner) || inner == syntax_pos::DUMMY_SP;
 
                 if inline_module {
-                    paths.directory.push(&*{
+                    module.directory.push(&*{
                         ::attr::first_attr_value_str_by_name(&item.attrs, "path")
                             .unwrap_or(item.ident.name.as_str())
                     });
                 } else {
-                    paths.directory =
+                    module.directory =
                         PathBuf::from(self.cx.parse_sess.codemap().span_to_filename(inner));
-                    paths.directory.pop();
+                    module.directory.pop();
                 }
 
-                let macro_use = self.contains_macro_use(&item.attrs);
-                let in_block = self.cx.syntax_env.in_block();
-                let module = self.cx.syntax_env.add_module(macro_use, in_block, Rc::new(paths));
-                let module = mem::replace(&mut self.cx.syntax_env.current_module, module);
+                let orig_module =
+                    mem::replace(&mut self.cx.current_expansion.module, Rc::new(module));
                 let result = noop_fold_item(item, self);
-                self.cx.syntax_env.current_module = module;
-                result
-            },
+                self.cx.current_expansion.module = orig_module;
+                return result;
+            }
             ast::ItemKind::ExternCrate(..) => {
                 // We need to error on `#[macro_use] extern crate` when it isn't at the
                 // crate root, because `$crate` won't work properly.
-                let is_crate_root = self.cx.syntax_env.is_crate_root();
-                for def in self.cx.loader.load_crate(&*item, is_crate_root) {
+                let is_crate_root = self.cx.current_expansion.module.mod_path.len() == 1;
+                for def in self.cx.resolver.load_crate(&*item, is_crate_root) {
                     match def {
                         LoadedMacro::Def(def) => self.cx.insert_macro(def),
                         LoadedMacro::CustomDerive(name, ext) => {
@@ -652,7 +646,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
                         }
                     }
                 }
-                SmallVector::one(item)
+                noop_fold_item(item, self)
             },
             _ => noop_fold_item(item, self),
         }
@@ -715,6 +709,15 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
     fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
         noop_fold_item_kind(self.cfg.configure_item_kind(item), self)
     }
+
+    fn new_id(&mut self, id: ast::NodeId) -> ast::NodeId {
+        if self.monotonic {
+            assert_eq!(id, ast::DUMMY_NODE_ID);
+            self.cx.resolver.next_node_id()
+        } else {
+            id
+        }
+    }
 }
 
 pub struct ExpansionConfig<'feat> {
@@ -766,7 +769,7 @@ pub fn expand_crate(cx: &mut ExtCtxt,
                     user_exts: Vec<NamedSyntaxExtension>,
                     c: Crate) -> Crate {
     cx.initialize(user_exts, &c);
-    cx.expander().expand_crate(c)
+    cx.monotonic_expander().expand_crate(c)
 }
 
 // Expands crate using supplied MacroExpander - allows for
@@ -803,110 +806,3 @@ impl Folder for Marker {
 fn mark_tts(tts: &[TokenTree], m: Mark) -> Vec<TokenTree> {
     noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None})
 }
-
-
-#[cfg(test)]
-mod tests {
-    use super::{expand_crate, ExpansionConfig};
-    use ast;
-    use ext::base::{ExtCtxt, DummyMacroLoader};
-    use parse;
-    use util::parser_testing::{string_to_parser};
-    use visit;
-    use visit::Visitor;
-
-    // a visitor that extracts the paths
-    // from a given thingy and puts them in a mutable
-    // array (passed in to the traversal)
-    #[derive(Clone)]
-    struct PathExprFinderContext {
-        path_accumulator: Vec<ast::Path> ,
-    }
-
-    impl Visitor for PathExprFinderContext {
-        fn visit_expr(&mut self, expr: &ast::Expr) {
-            if let ast::ExprKind::Path(None, ref p) = expr.node {
-                self.path_accumulator.push(p.clone());
-            }
-            visit::walk_expr(self, expr);
-        }
-    }
-
-    // these following tests are quite fragile, in that they don't test what
-    // *kind* of failure occurs.
-
-    fn test_ecfg() -> ExpansionConfig<'static> {
-        ExpansionConfig::default("test".to_string())
-    }
-
-    // make sure that macros can't escape fns
-    #[should_panic]
-    #[test] fn macros_cant_escape_fns_test () {
-        let src = "fn bogus() {macro_rules! z (() => (3+4));}\
-                   fn inty() -> i32 { z!() }".to_string();
-        let sess = parse::ParseSess::new();
-        let crate_ast = parse::parse_crate_from_source_str(
-            "<test>".to_string(),
-            src,
-            Vec::new(), &sess).unwrap();
-        // should fail:
-        let mut loader = DummyMacroLoader;
-        let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
-        expand_crate(&mut ecx, vec![], crate_ast);
-    }
-
-    // make sure that macros can't escape modules
-    #[should_panic]
-    #[test] fn macros_cant_escape_mods_test () {
-        let src = "mod foo {macro_rules! z (() => (3+4));}\
-                   fn inty() -> i32 { z!() }".to_string();
-        let sess = parse::ParseSess::new();
-        let crate_ast = parse::parse_crate_from_source_str(
-            "<test>".to_string(),
-            src,
-            Vec::new(), &sess).unwrap();
-        let mut loader = DummyMacroLoader;
-        let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
-        expand_crate(&mut ecx, vec![], crate_ast);
-    }
-
-    // macro_use modules should allow macros to escape
-    #[test] fn macros_can_escape_flattened_mods_test () {
-        let src = "#[macro_use] mod foo {macro_rules! z (() => (3+4));}\
-                   fn inty() -> i32 { z!() }".to_string();
-        let sess = parse::ParseSess::new();
-        let crate_ast = parse::parse_crate_from_source_str(
-            "<test>".to_string(),
-            src,
-            Vec::new(), &sess).unwrap();
-        let mut loader = DummyMacroLoader;
-        let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
-        expand_crate(&mut ecx, vec![], crate_ast);
-    }
-
-    fn expand_crate_str(crate_str: String) -> ast::Crate {
-        let ps = parse::ParseSess::new();
-        let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod());
-        // the cfg argument actually does matter, here...
-        let mut loader = DummyMacroLoader;
-        let mut ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut loader);
-        expand_crate(&mut ecx, vec![], crate_ast)
-    }
-
-    #[test] fn macro_tokens_should_match(){
-        expand_crate_str(
-            "macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string());
-    }
-
-    // should be able to use a bound identifier as a literal in a macro definition:
-    #[test] fn self_macro_parsing(){
-        expand_crate_str(
-            "macro_rules! foo ((zz) => (287;));
-            fn f(zz: i32) {foo!(zz);}".to_string()
-            );
-    }
-
-    // create a really evil test case where a $x appears inside a binding of $x
-    // but *shouldn't* bind because it was inserted by a different macro....
-    // can't write this test case until we have macro-generating macros.
-}
diff --git a/src/libsyntax/ext/hygiene.rs b/src/libsyntax/ext/hygiene.rs
index 27e8eab62e114..34126fac4ac78 100644
--- a/src/libsyntax/ext/hygiene.rs
+++ b/src/libsyntax/ext/hygiene.rs
@@ -29,7 +29,7 @@ pub struct SyntaxContextData {
     pub prev_ctxt: SyntaxContext,
 }
 
-/// A mark represents a unique id associated with a macro expansion.
+/// A mark is a unique id associated with a macro expansion.
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
 pub struct Mark(u32);
 
@@ -41,6 +41,11 @@ impl Mark {
         })
     }
 
+    /// The mark of the theoretical expansion that generates freshly parsed, unexpanded AST.
+    pub fn root() -> Self {
+        Mark(0)
+    }
+
     pub fn as_u32(&self) -> u32 {
         self.0
     }
@@ -56,8 +61,8 @@ impl HygieneData {
     fn new() -> Self {
         HygieneData {
             syntax_contexts: vec![SyntaxContextData {
-                outer_mark: Mark(0), // the null mark
-                prev_ctxt: SyntaxContext(0), // the empty context
+                outer_mark: Mark::root(),
+                prev_ctxt: SyntaxContext::empty(),
             }],
             markings: HashMap::new(),
             next_mark: Mark(1),
diff --git a/src/libsyntax/ext/placeholders.rs b/src/libsyntax/ext/placeholders.rs
index abadcf867b146..47f366a88768e 100644
--- a/src/libsyntax/ext/placeholders.rs
+++ b/src/libsyntax/ext/placeholders.rs
@@ -10,13 +10,16 @@
 
 use ast;
 use codemap::{DUMMY_SP, dummy_spanned};
+use ext::base::ExtCtxt;
 use ext::expand::{Expansion, ExpansionKind};
 use fold::*;
 use parse::token::keywords;
 use ptr::P;
+use util::move_map::MoveMap;
 use util::small_vector::SmallVector;
 
 use std::collections::HashMap;
+use std::mem;
 
 pub fn placeholder(kind: ExpansionKind, id: ast::NodeId) -> Expansion {
     fn mac_placeholder() -> ast::Mac {
@@ -69,14 +72,18 @@ pub fn macro_scope_placeholder() -> Expansion {
     placeholder(ExpansionKind::Items, ast::DUMMY_NODE_ID)
 }
 
-pub struct PlaceholderExpander {
+pub struct PlaceholderExpander<'a, 'b: 'a> {
     expansions: HashMap<ast::NodeId, Expansion>,
+    cx: &'a mut ExtCtxt<'b>,
+    monotonic: bool,
 }
 
-impl PlaceholderExpander {
-    pub fn new() -> Self {
+impl<'a, 'b> PlaceholderExpander<'a, 'b> {
+    pub fn new(cx: &'a mut ExtCtxt<'b>, monotonic: bool) -> Self {
         PlaceholderExpander {
+            cx: cx,
             expansions: HashMap::new(),
+            monotonic: monotonic,
         }
     }
 
@@ -89,7 +96,7 @@ impl PlaceholderExpander {
     }
 }
 
-impl Folder for PlaceholderExpander {
+impl<'a, 'b> Folder for PlaceholderExpander<'a, 'b> {
     fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
         match item.node {
             // Scope placeholder
@@ -155,6 +162,56 @@ impl Folder for PlaceholderExpander {
             _ => noop_fold_ty(ty, self),
         }
     }
+
+    fn fold_block(&mut self, block: P<ast::Block>) -> P<ast::Block> {
+        noop_fold_block(block, self).map(|mut block| {
+            let mut macros = Vec::new();
+            let mut remaining_stmts = block.stmts.len();
+
+            block.stmts = block.stmts.move_flat_map(|mut stmt| {
+                remaining_stmts -= 1;
+
+                // Scope placeholder
+                if let ast::StmtKind::Item(ref item) = stmt.node {
+                    if let ast::ItemKind::Mac(..) = item.node {
+                        macros.push(item.ident.ctxt.data().outer_mark);
+                        return None;
+                    }
+                }
+
+                match stmt.node {
+                    // Avoid wasting a node id on a trailing expression statement,
+                    // which shares a HIR node with the expression itself.
+                    ast::StmtKind::Expr(ref expr) if remaining_stmts == 0 => stmt.id = expr.id,
+
+                    _ if self.monotonic => {
+                        assert_eq!(stmt.id, ast::DUMMY_NODE_ID);
+                        stmt.id = self.cx.resolver.next_node_id();
+                    }
+
+                    _ => {}
+                }
+
+                if self.monotonic && !macros.is_empty() {
+                    let macros = mem::replace(&mut macros, Vec::new());
+                    self.cx.resolver.add_expansions_at_stmt(stmt.id, macros);
+                }
+
+                Some(stmt)
+            });
+
+            block
+        })
+    }
+
+    fn fold_mod(&mut self, module: ast::Mod) -> ast::Mod {
+        let mut module = noop_fold_mod(module, self);
+        module.items = module.items.move_flat_map(|item| match item.node {
+            ast::ItemKind::Mac(_) => None, // remove scope placeholders from modules
+            _ => Some(item),
+        });
+        module
+    }
 }
 
 pub fn reconstructed_macro_rules(def: &ast::MacroDef, path: &ast::Path) -> Expansion {
diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs
index 105b226111738..e75e41d0c2d4b 100644
--- a/src/libsyntax/ext/source_util.rs
+++ b/src/libsyntax/ext/source_util.rs
@@ -74,8 +74,8 @@ pub fn expand_stringify(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTre
 pub fn expand_mod(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
                   -> Box<base::MacResult+'static> {
     base::check_zero_tts(cx, sp, tts, "module_path!");
-    let paths = cx.syntax_env.paths();
-    let string = paths.mod_path.iter().map(|x| x.to_string()).collect::<Vec<String>>().join("::");
+    let mod_path = &cx.current_expansion.module.mod_path;
+    let string = mod_path.iter().map(|x| x.to_string()).collect::<Vec<String>>().join("::");
 
     base::MacEager::expr(cx.expr_str(
             sp,
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index ed80ec9cbc49e..51ef45b97be6f 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -211,8 +211,8 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
                                            imported_from,
                                            rhs);
                 let mut p = Parser::new(cx.parse_sess(), cx.cfg(), Box::new(trncbr));
-                p.directory = cx.syntax_env.paths().directory.clone();
-                p.restrictions = match cx.syntax_env.in_block() {
+                p.directory = cx.current_expansion.module.directory.clone();
+                p.restrictions = match cx.current_expansion.in_block {
                     true => Restrictions::NO_NONINLINE_MOD,
                     false => Restrictions::empty(),
                 };
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 7500bfe9caa80..9fb4d0203f41e 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -1320,51 +1320,27 @@ pub fn noop_fold_exprs<T: Folder>(es: Vec<P<Expr>>, folder: &mut T) -> Vec<P<Exp
     es.move_flat_map(|e| folder.fold_opt_expr(e))
 }
 
-pub fn noop_fold_stmt<T: Folder>(Stmt {node, span, id}: Stmt, folder: &mut T)
-                                 -> SmallVector<Stmt> {
+pub fn noop_fold_stmt<T: Folder>(Stmt {node, span, id}: Stmt, folder: &mut T) -> SmallVector<Stmt> {
     let id = folder.new_id(id);
     let span = folder.new_span(span);
+    noop_fold_stmt_kind(node, folder).into_iter().map(|node| {
+        Stmt { id: id, node: node, span: span }
+    }).collect()
+}
 
+pub fn noop_fold_stmt_kind<T: Folder>(node: StmtKind, folder: &mut T) -> SmallVector<StmtKind> {
     match node {
-        StmtKind::Local(local) => SmallVector::one(Stmt {
-            id: id,
-            node: StmtKind::Local(folder.fold_local(local)),
-            span: span,
-        }),
-        StmtKind::Item(item) => folder.fold_item(item).into_iter().map(|item| Stmt {
-            id: id,
-            node: StmtKind::Item(item),
-            span: span,
-        }).collect(),
+        StmtKind::Local(local) => SmallVector::one(StmtKind::Local(folder.fold_local(local))),
+        StmtKind::Item(item) => folder.fold_item(item).into_iter().map(StmtKind::Item).collect(),
         StmtKind::Expr(expr) => {
-            if let Some(expr) = folder.fold_opt_expr(expr) {
-                SmallVector::one(Stmt {
-                    id: id,
-                    node: StmtKind::Expr(expr),
-                    span: span,
-                })
-            } else {
-                SmallVector::zero()
-            }
+            folder.fold_opt_expr(expr).into_iter().map(StmtKind::Expr).collect()
         }
         StmtKind::Semi(expr) => {
-            if let Some(expr) = folder.fold_opt_expr(expr) {
-                SmallVector::one(Stmt {
-                    id: id,
-                    node: StmtKind::Semi(expr),
-                    span: span,
-                })
-            } else {
-                SmallVector::zero()
-            }
+            folder.fold_opt_expr(expr).into_iter().map(StmtKind::Semi).collect()
         }
-        StmtKind::Mac(mac) => SmallVector::one(Stmt {
-            id: id,
-            node: StmtKind::Mac(mac.map(|(mac, semi, attrs)| {
-                (folder.fold_mac(mac), semi, fold_attrs(attrs.into(), folder).into())
-            })),
-            span: span,
-        })
+        StmtKind::Mac(mac) => SmallVector::one(StmtKind::Mac(mac.map(|(mac, semi, attrs)| {
+            (folder.fold_mac(mac), semi, fold_attrs(attrs.into(), folder).into())
+        }))),
     }
 }
 
diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs
index 3108296e778a2..6327e8f71bcd5 100644
--- a/src/libsyntax/test.rs
+++ b/src/libsyntax/test.rs
@@ -28,7 +28,7 @@ use errors;
 use errors::snippet::{SnippetData};
 use config;
 use entry::{self, EntryPointType};
-use ext::base::{ExtCtxt, DummyMacroLoader};
+use ext::base::{ExtCtxt, Resolver};
 use ext::build::AstBuilder;
 use ext::expand::ExpansionConfig;
 use fold::Folder;
@@ -70,6 +70,7 @@ struct TestCtxt<'a> {
 // Traverse the crate, collecting all the test functions, eliding any
 // existing main functions, and synthesizing a main test harness
 pub fn modify_for_testing(sess: &ParseSess,
+                          resolver: &mut Resolver,
                           should_test: bool,
                           krate: ast::Crate,
                           span_diagnostic: &errors::Handler) -> ast::Crate {
@@ -82,7 +83,7 @@ pub fn modify_for_testing(sess: &ParseSess,
                                            "reexport_test_harness_main");
 
     if should_test {
-        generate_test_harness(sess, reexport_test_harness_main, krate, span_diagnostic)
+        generate_test_harness(sess, resolver, reexport_test_harness_main, krate, span_diagnostic)
     } else {
         krate
     }
@@ -248,27 +249,28 @@ fn mk_reexport_mod(cx: &mut TestCtxt, tests: Vec<ast::Ident>,
     }).chain(tested_submods.into_iter().map(|(r, sym)| {
         let path = cx.ext_cx.path(DUMMY_SP, vec![super_, r, sym]);
         cx.ext_cx.item_use_simple_(DUMMY_SP, ast::Visibility::Public, r, path)
-    }));
+    })).collect();
 
     let reexport_mod = ast::Mod {
         inner: DUMMY_SP,
-        items: items.collect(),
+        items: items,
     };
 
     let sym = token::gensym_ident("__test_reexports");
-    let it = P(ast::Item {
+    let it = cx.ext_cx.monotonic_expander().fold_item(P(ast::Item {
         ident: sym.clone(),
         attrs: Vec::new(),
         id: ast::DUMMY_NODE_ID,
         node: ast::ItemKind::Mod(reexport_mod),
         vis: ast::Visibility::Public,
         span: DUMMY_SP,
-    });
+    })).pop().unwrap();
 
     (it, sym)
 }
 
 fn generate_test_harness(sess: &ParseSess,
+                         resolver: &mut Resolver,
                          reexport_test_harness_main: Option<InternedString>,
                          krate: ast::Crate,
                          sd: &errors::Handler) -> ast::Crate {
@@ -276,13 +278,10 @@ fn generate_test_harness(sess: &ParseSess,
     let mut cleaner = EntryPointCleaner { depth: 0 };
     let krate = cleaner.fold_crate(krate);
 
-    let mut loader = DummyMacroLoader;
     let mut cx: TestCtxt = TestCtxt {
         sess: sess,
         span_diagnostic: sd,
-        ext_cx: ExtCtxt::new(sess, vec![],
-                             ExpansionConfig::default("test".to_string()),
-                             &mut loader),
+        ext_cx: ExtCtxt::new(sess, vec![], ExpansionConfig::default("test".to_string()), resolver),
         path: Vec::new(),
         testfns: Vec::new(),
         reexport_test_harness_main: reexport_test_harness_main,
@@ -511,16 +510,17 @@ fn mk_test_module(cx: &mut TestCtxt) -> (P<ast::Item>, Option<P<ast::Item>>) {
         items: vec![import, mainfn, tests],
     };
     let item_ = ast::ItemKind::Mod(testmod);
-
     let mod_ident = token::gensym_ident("__test");
-    let item = P(ast::Item {
+
+    let mut expander = cx.ext_cx.monotonic_expander();
+    let item = expander.fold_item(P(ast::Item {
         id: ast::DUMMY_NODE_ID,
         ident: mod_ident,
         attrs: vec![],
         node: item_,
         vis: ast::Visibility::Public,
         span: DUMMY_SP,
-    });
+    })).pop().unwrap();
     let reexport = cx.reexport_test_harness_main.as_ref().map(|s| {
         // building `use <ident> = __test::main`
         let reexport_ident = token::str_to_ident(&s);
@@ -529,14 +529,14 @@ fn mk_test_module(cx: &mut TestCtxt) -> (P<ast::Item>, Option<P<ast::Item>>) {
             nospan(ast::ViewPathSimple(reexport_ident,
                                        path_node(vec![mod_ident, token::str_to_ident("main")])));
 
-        P(ast::Item {
+        expander.fold_item(P(ast::Item {
             id: ast::DUMMY_NODE_ID,
             ident: keywords::Invalid.ident(),
             attrs: vec![],
             node: ast::ItemKind::Use(P(use_path)),
             vis: ast::Visibility::Inherited,
             span: DUMMY_SP
-        })
+        })).pop().unwrap()
     });
 
     debug!("Synthetic test module:\n{}\n", pprust::item_to_string(&item));
diff --git a/src/libsyntax_ext/deriving/clone.rs b/src/libsyntax_ext/deriving/clone.rs
index c7afaaf4796a4..d7bc2a6faeeb9 100644
--- a/src/libsyntax_ext/deriving/clone.rs
+++ b/src/libsyntax_ext/deriving/clone.rs
@@ -11,20 +11,14 @@
 use deriving::generic::*;
 use deriving::generic::ty::*;
 
-use syntax::ast::{Expr, Generics, ItemKind, MetaItem, VariantData};
+use syntax::ast::{self, Expr, Generics, ItemKind, MetaItem, VariantData};
 use syntax::attr;
 use syntax::ext::base::{Annotatable, ExtCtxt};
 use syntax::ext::build::AstBuilder;
-use syntax::parse::token::InternedString;
+use syntax::parse::token::{keywords, InternedString};
 use syntax::ptr::P;
 use syntax_pos::Span;
 
-#[derive(PartialEq)]
-enum Mode {
-    Deep,
-    Shallow,
-}
-
 pub fn expand_deriving_clone(cx: &mut ExtCtxt,
                              span: Span,
                              mitem: &MetaItem,
@@ -40,29 +34,38 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
     //      if we used the short form with generics, we'd have to bound the generics with
     //      Clone + Copy, and then there'd be no Clone impl at all if the user fills in something
     //      that is Clone but not Copy. and until specialization we can't write both impls.
+    // - the item is a union with Copy fields
+    //      Unions with generic parameters still can derive Clone because they require Copy
+    //      for deriving, Clone alone is not enough.
+    //      Whever Clone is implemented for fields is irrelevant so we don't assert it.
     let bounds;
-    let unify_fieldless_variants;
     let substructure;
+    let is_shallow;
     match *item {
         Annotatable::Item(ref annitem) => {
             match annitem.node {
                 ItemKind::Struct(_, Generics { ref ty_params, .. }) |
                 ItemKind::Enum(_, Generics { ref ty_params, .. })
-                    if ty_params.is_empty() &&
-                       attr::contains_name(&annitem.attrs, "rustc_copy_clone_marker") => {
-
+                        if attr::contains_name(&annitem.attrs, "rustc_copy_clone_marker") &&
+                           ty_params.is_empty() => {
+                    bounds = vec![];
+                    is_shallow = true;
+                    substructure = combine_substructure(Box::new(|c, s, sub| {
+                        cs_clone_shallow("Clone", c, s, sub, false)
+                    }));
+                }
+                ItemKind::Union(..) => {
                     bounds = vec![Literal(path_std!(cx, core::marker::Copy))];
-                    unify_fieldless_variants = true;
+                    is_shallow = true;
                     substructure = combine_substructure(Box::new(|c, s, sub| {
-                        cs_clone("Clone", c, s, sub, Mode::Shallow)
+                        cs_clone_shallow("Clone", c, s, sub, true)
                     }));
                 }
-
                 _ => {
                     bounds = vec![];
-                    unify_fieldless_variants = false;
+                    is_shallow = false;
                     substructure = combine_substructure(Box::new(|c, s, sub| {
-                        cs_clone("Clone", c, s, sub, Mode::Deep)
+                        cs_clone("Clone", c, s, sub)
                     }));
                 }
             }
@@ -80,7 +83,7 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
         additional_bounds: bounds,
         generics: LifetimeBounds::empty(),
         is_unsafe: false,
-        supports_unions: false,
+        supports_unions: true,
         methods: vec![MethodDef {
                           name: "clone",
                           generics: LifetimeBounds::empty(),
@@ -89,37 +92,72 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
                           ret_ty: Self_,
                           attributes: attrs,
                           is_unsafe: false,
-                          unify_fieldless_variants: unify_fieldless_variants,
+                          unify_fieldless_variants: false,
                           combine_substructure: substructure,
                       }],
         associated_types: Vec::new(),
     };
 
-    trait_def.expand(cx, mitem, item, push)
+    trait_def.expand_ext(cx, mitem, item, push, is_shallow)
+}
+
+fn cs_clone_shallow(name: &str,
+                    cx: &mut ExtCtxt,
+                    trait_span: Span,
+                    substr: &Substructure,
+                    is_union: bool)
+                    -> P<Expr> {
+    fn assert_ty_bounds(cx: &mut ExtCtxt, stmts: &mut Vec<ast::Stmt>,
+                        ty: P<ast::Ty>, span: Span, helper_name: &str) {
+        // Generate statement `let _: helper_name<ty>;`,
+        // set the expn ID so we can use the unstable struct.
+        let span = super::allow_unstable(cx, span, "derive(Clone)");
+        let assert_path = cx.path_all(span, true,
+                                        cx.std_path(&["clone", helper_name]),
+                                        vec![], vec![ty], vec![]);
+        stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path)));
+    }
+    fn process_variant(cx: &mut ExtCtxt, stmts: &mut Vec<ast::Stmt>, variant: &VariantData) {
+        for field in variant.fields() {
+            // let _: AssertParamIsClone<FieldTy>;
+            assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsClone");
+        }
+    }
+
+    let mut stmts = Vec::new();
+    if is_union {
+        // let _: AssertParamIsCopy<Self>;
+        let self_ty = cx.ty_path(cx.path_ident(trait_span, keywords::SelfType.ident()));
+        assert_ty_bounds(cx, &mut stmts, self_ty, trait_span, "AssertParamIsCopy");
+    } else {
+        match *substr.fields {
+            StaticStruct(vdata, ..) => {
+                process_variant(cx, &mut stmts, vdata);
+            }
+            StaticEnum(enum_def, ..) => {
+                for variant in &enum_def.variants {
+                    process_variant(cx, &mut stmts, &variant.node.data);
+                }
+            }
+            _ => cx.span_bug(trait_span, &format!("unexpected substructure in \
+                                                    shallow `derive({})`", name))
+        }
+    }
+    stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span))));
+    cx.expr_block(cx.block(trait_span, stmts))
 }
 
 fn cs_clone(name: &str,
             cx: &mut ExtCtxt,
             trait_span: Span,
-            substr: &Substructure,
-            mode: Mode)
+            substr: &Substructure)
             -> P<Expr> {
     let ctor_path;
     let all_fields;
-    let fn_path = match mode {
-        Mode::Shallow => cx.std_path(&["clone", "assert_receiver_is_clone"]),
-        Mode::Deep => cx.std_path(&["clone", "Clone", "clone"]),
-    };
+    let fn_path = cx.std_path(&["clone", "Clone", "clone"]);
     let subcall = |cx: &mut ExtCtxt, field: &FieldInfo| {
         let args = vec![cx.expr_addr_of(field.span, field.self_.clone())];
-
-        let span = if mode == Mode::Shallow {
-            // set the expn ID so we can call the unstable method
-            super::allow_unstable(cx, field.span, "derive(Clone)")
-        } else {
-            field.span
-        };
-        cx.expr_call_global(span, fn_path.clone(), args)
+        cx.expr_call_global(field.span, fn_path.clone(), args)
     };
 
     let vdata;
@@ -145,43 +183,31 @@ fn cs_clone(name: &str,
         }
     }
 
-    match mode {
-        Mode::Shallow => {
-            let mut stmts = all_fields.iter().map(|f| {
-                let call = subcall(cx, f);
-                cx.stmt_expr(call)
-            }).collect::<Vec<_>>();
-            stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span))));
-            cx.expr_block(cx.block(trait_span, stmts))
-        }
-        Mode::Deep => {
-            match *vdata {
-                VariantData::Struct(..) => {
-                    let fields = all_fields.iter()
-                        .map(|field| {
-                            let ident = match field.name {
-                                Some(i) => i,
-                                None => {
-                                    cx.span_bug(trait_span,
-                                                &format!("unnamed field in normal struct in \
-                                                     `derive({})`",
-                                                         name))
-                                }
-                            };
-                            let call = subcall(cx, field);
-                            cx.field_imm(field.span, ident, call)
-                        })
-                        .collect::<Vec<_>>();
+    match *vdata {
+        VariantData::Struct(..) => {
+            let fields = all_fields.iter()
+                .map(|field| {
+                    let ident = match field.name {
+                        Some(i) => i,
+                        None => {
+                            cx.span_bug(trait_span,
+                                        &format!("unnamed field in normal struct in \
+                                                `derive({})`",
+                                                    name))
+                        }
+                    };
+                    let call = subcall(cx, field);
+                    cx.field_imm(field.span, ident, call)
+                })
+                .collect::<Vec<_>>();
 
-                    cx.expr_struct(trait_span, ctor_path, fields)
-                }
-                VariantData::Tuple(..) => {
-                    let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect();
-                    let path = cx.expr_path(ctor_path);
-                    cx.expr_call(trait_span, path, subcalls)
-                }
-                VariantData::Unit(..) => cx.expr_path(ctor_path),
-            }
+            cx.expr_struct(trait_span, ctor_path, fields)
+        }
+        VariantData::Tuple(..) => {
+            let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect();
+            let path = cx.expr_path(ctor_path);
+            cx.expr_call(trait_span, path, subcalls)
         }
+        VariantData::Unit(..) => cx.expr_path(ctor_path),
     }
 }
diff --git a/src/libsyntax_ext/deriving/cmp/eq.rs b/src/libsyntax_ext/deriving/cmp/eq.rs
index 425a47a991bc4..fa0fb2492c551 100644
--- a/src/libsyntax_ext/deriving/cmp/eq.rs
+++ b/src/libsyntax_ext/deriving/cmp/eq.rs
@@ -11,7 +11,7 @@
 use deriving::generic::*;
 use deriving::generic::ty::*;
 
-use syntax::ast::{Expr, MetaItem};
+use syntax::ast::{self, Expr, MetaItem};
 use syntax::ext::base::{Annotatable, ExtCtxt};
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token::InternedString;
@@ -23,22 +23,6 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt,
                           mitem: &MetaItem,
                           item: &Annotatable,
                           push: &mut FnMut(Annotatable)) {
-    fn cs_total_eq_assert(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
-        cs_same_method(|cx, span, exprs| {
-            // create `a.<method>(); b.<method>(); c.<method>(); ...`
-            // (where method is `assert_receiver_is_total_eq`)
-            let stmts = exprs.into_iter().map(|e| cx.stmt_expr(e)).collect();
-            let block = cx.block(span, stmts);
-            cx.expr_block(block)
-        },
-                       Box::new(|cx, sp, _, _| {
-                           cx.span_bug(sp, "non matching enums in derive(Eq)?")
-                       }),
-                       cx,
-                       span,
-                       substr)
-    }
-
     let inline = cx.meta_word(span, InternedString::new("inline"));
     let hidden = cx.meta_list_item_word(span, InternedString::new("hidden"));
     let doc = cx.meta_list(span, InternedString::new("doc"), vec![hidden]);
@@ -50,7 +34,7 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt,
         additional_bounds: Vec::new(),
         generics: LifetimeBounds::empty(),
         is_unsafe: false,
-        supports_unions: false,
+        supports_unions: true,
         methods: vec![MethodDef {
                           name: "assert_receiver_is_total_eq",
                           generics: LifetimeBounds::empty(),
@@ -66,5 +50,38 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt,
                       }],
         associated_types: Vec::new(),
     };
-    trait_def.expand(cx, mitem, item, push)
+    trait_def.expand_ext(cx, mitem, item, push, true)
+}
+
+fn cs_total_eq_assert(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
+    fn assert_ty_bounds(cx: &mut ExtCtxt, stmts: &mut Vec<ast::Stmt>,
+                        ty: P<ast::Ty>, span: Span, helper_name: &str) {
+        // Generate statement `let _: helper_name<ty>;`,
+        // set the expn ID so we can use the unstable struct.
+        let span = super::allow_unstable(cx, span, "derive(Eq)");
+        let assert_path = cx.path_all(span, true,
+                                        cx.std_path(&["cmp", helper_name]),
+                                        vec![], vec![ty], vec![]);
+        stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path)));
+    }
+    fn process_variant(cx: &mut ExtCtxt, stmts: &mut Vec<ast::Stmt>, variant: &ast::VariantData) {
+        for field in variant.fields() {
+            // let _: AssertParamIsEq<FieldTy>;
+            assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsEq");
+        }
+    }
+
+    let mut stmts = Vec::new();
+    match *substr.fields {
+        StaticStruct(vdata, ..) => {
+            process_variant(cx, &mut stmts, vdata);
+        }
+        StaticEnum(enum_def, ..) => {
+            for variant in &enum_def.variants {
+                process_variant(cx, &mut stmts, &variant.node.data);
+            }
+        }
+        _ => cx.span_bug(trait_span, "unexpected substructure in `derive(Eq)`")
+    }
+    cx.expr_block(cx.block(trait_span, stmts))
 }
diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs
index 600f5d335c472..339a6c477ccd5 100644
--- a/src/libsyntax_ext/deriving/generic/mod.rs
+++ b/src/libsyntax_ext/deriving/generic/mod.rs
@@ -401,18 +401,29 @@ impl<'a> TraitDef<'a> {
                   mitem: &ast::MetaItem,
                   item: &'a Annotatable,
                   push: &mut FnMut(Annotatable)) {
+        self.expand_ext(cx, mitem, item, push, false);
+    }
+
+    pub fn expand_ext(&self,
+                      cx: &mut ExtCtxt,
+                      mitem: &ast::MetaItem,
+                      item: &'a Annotatable,
+                      push: &mut FnMut(Annotatable),
+                      from_scratch: bool) {
         match *item {
             Annotatable::Item(ref item) => {
                 let newitem = match item.node {
                     ast::ItemKind::Struct(ref struct_def, ref generics) => {
-                        self.expand_struct_def(cx, &struct_def, item.ident, generics)
+                        self.expand_struct_def(cx, &struct_def, item.ident, generics, from_scratch)
                     }
                     ast::ItemKind::Enum(ref enum_def, ref generics) => {
-                        self.expand_enum_def(cx, enum_def, &item.attrs, item.ident, generics)
+                        self.expand_enum_def(cx, enum_def, &item.attrs,
+                                             item.ident, generics, from_scratch)
                     }
                     ast::ItemKind::Union(ref struct_def, ref generics) => {
                         if self.supports_unions {
-                            self.expand_struct_def(cx, &struct_def, item.ident, generics)
+                            self.expand_struct_def(cx, &struct_def, item.ident,
+                                                   generics, from_scratch)
                         } else {
                             cx.span_err(mitem.span,
                                         "this trait cannot be derived for unions");
@@ -661,7 +672,8 @@ impl<'a> TraitDef<'a> {
                          cx: &mut ExtCtxt,
                          struct_def: &'a VariantData,
                          type_ident: Ident,
-                         generics: &Generics)
+                         generics: &Generics,
+                         from_scratch: bool)
                          -> P<ast::Item> {
         let field_tys: Vec<P<ast::Ty>> = struct_def.fields()
             .iter()
@@ -674,7 +686,7 @@ impl<'a> TraitDef<'a> {
                 let (explicit_self, self_args, nonself_args, tys) =
                     method_def.split_self_nonself_args(cx, self, type_ident, generics);
 
-                let body = if method_def.is_static() {
+                let body = if from_scratch || method_def.is_static() {
                     method_def.expand_static_struct_method_body(cx,
                                                                 self,
                                                                 struct_def,
@@ -709,7 +721,8 @@ impl<'a> TraitDef<'a> {
                        enum_def: &'a EnumDef,
                        type_attrs: &[ast::Attribute],
                        type_ident: Ident,
-                       generics: &Generics)
+                       generics: &Generics,
+                       from_scratch: bool)
                        -> P<ast::Item> {
         let mut field_tys = Vec::new();
 
@@ -727,7 +740,7 @@ impl<'a> TraitDef<'a> {
                 let (explicit_self, self_args, nonself_args, tys) =
                     method_def.split_self_nonself_args(cx, self, type_ident, generics);
 
-                let body = if method_def.is_static() {
+                let body = if from_scratch || method_def.is_static() {
                     method_def.expand_static_enum_method_body(cx,
                                                               self,
                                                               enum_def,
diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs
index fcbce36389082..6162beb80eccc 100644
--- a/src/libsyntax_ext/deriving/mod.rs
+++ b/src/libsyntax_ext/deriving/mod.rs
@@ -11,8 +11,7 @@
 //! The compiler code necessary to implement the `#[derive]` extensions.
 
 use syntax::ast::{self, MetaItem};
-use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxEnv};
-use syntax::ext::base::MultiModifier;
+use syntax::ext::base::{Annotatable, ExtCtxt};
 use syntax::ext::build::AstBuilder;
 use syntax::feature_gate;
 use syntax::codemap;
@@ -89,7 +88,7 @@ fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span {
     }
 }
 
-fn expand_derive(cx: &mut ExtCtxt,
+pub fn expand_derive(cx: &mut ExtCtxt,
                  span: Span,
                  mitem: &MetaItem,
                  annotatable: Annotatable)
@@ -243,10 +242,6 @@ fn expand_derive(cx: &mut ExtCtxt,
 
 macro_rules! derive_traits {
     ($( $name:expr => $func:path, )+) => {
-        pub fn register_all(env: &mut SyntaxEnv) {
-            env.insert(intern("derive"), MultiModifier(Box::new(expand_derive)));
-        }
-
         pub fn is_builtin_trait(name: &str) -> bool {
             match name {
                 $( $name )|+ => true,
diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs
index 06b16095d1963..892ebcfa76129 100644
--- a/src/libsyntax_ext/format.rs
+++ b/src/libsyntax_ext/format.rs
@@ -17,7 +17,6 @@ use syntax::ast;
 use syntax::ext::base::*;
 use syntax::ext::base;
 use syntax::ext::build::AstBuilder;
-use syntax::fold::Folder;
 use syntax::parse::token::{self, keywords};
 use syntax::ptr::P;
 use syntax_pos::{Span, DUMMY_SP};
@@ -702,10 +701,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
     let arg_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect();
     let arg_unique_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect();
     let macsp = ecx.call_site();
-    // Expand the format literal so that efmt.span will have a backtrace. This
-    // is essential for locating a bug when the format literal is generated in
-    // a macro. (e.g. println!("{}"), which uses concat!($fmt, "\n")).
-    let efmt = ecx.expander().fold_expr(efmt);
+    let msg = "format argument must be a string literal.";
+    let fmt = match expr_to_spanned_string(ecx, efmt, msg) {
+        Some(fmt) => fmt,
+        None => return DummyResult::raw_expr(sp),
+    };
+
     let mut cx = Context {
         ecx: ecx,
         args: args,
@@ -723,14 +724,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
         str_pieces: Vec::new(),
         all_pieces_simple: true,
         macsp: macsp,
-        fmtsp: efmt.span,
-    };
-    let fmt = match expr_to_string(cx.ecx, efmt, "format argument must be a string literal.") {
-        Some((fmt, _)) => fmt,
-        None => return DummyResult::raw_expr(sp),
+        fmtsp: fmt.span,
     };
 
-    let mut parser = parse::Parser::new(&fmt);
+    let mut parser = parse::Parser::new(&fmt.node.0);
     let mut pieces = vec![];
 
     loop {
diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs
index 2065d92fd6ed7..3a6212e5445ce 100644
--- a/src/libsyntax_ext/lib.rs
+++ b/src/libsyntax_ext/lib.rs
@@ -34,11 +34,6 @@ extern crate syntax_pos;
 extern crate rustc_macro;
 extern crate rustc_errors as errors;
 
-use syntax::ext::base::{MacroExpanderFn, NormalTT};
-use syntax::ext::base::{SyntaxEnv, SyntaxExtension};
-use syntax::parse::token::intern;
-
-
 mod asm;
 mod cfg;
 mod concat;
@@ -53,28 +48,67 @@ pub mod rustc_macro_registrar;
 // for custom_derive
 pub mod deriving;
 
-pub fn register_builtins(env: &mut SyntaxEnv) {
-    // utility function to simplify creating NormalTT syntax extensions
-    fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension {
-        NormalTT(Box::new(f), None, false)
+use std::rc::Rc;
+use syntax::ast;
+use syntax::ext::base::{MacroExpanderFn, MacroRulesTT, NormalTT, MultiModifier};
+use syntax::ext::hygiene::Mark;
+use syntax::parse::token::intern;
+
+pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, enable_quotes: bool) {
+    let mut register = |name, ext| {
+        resolver.add_macro(Mark::root(), ast::Ident::with_empty_ctxt(intern(name)), Rc::new(ext));
+    };
+
+    register("macro_rules", MacroRulesTT);
+
+    macro_rules! register {
+        ($( $name:ident: $f:expr, )*) => { $(
+            register(stringify!($name), NormalTT(Box::new($f as MacroExpanderFn), None, false));
+        )* }
     }
 
-    env.insert(intern("asm"), builtin_normal_expander(asm::expand_asm));
-    env.insert(intern("cfg"), builtin_normal_expander(cfg::expand_cfg));
-    env.insert(intern("concat"),
-               builtin_normal_expander(concat::expand_syntax_ext));
-    env.insert(intern("concat_idents"),
-               builtin_normal_expander(concat_idents::expand_syntax_ext));
-    env.insert(intern("env"), builtin_normal_expander(env::expand_env));
-    env.insert(intern("option_env"),
-               builtin_normal_expander(env::expand_option_env));
-    env.insert(intern("format_args"),
-               // format_args uses `unstable` things internally.
-               NormalTT(Box::new(format::expand_format_args), None, true));
-    env.insert(intern("log_syntax"),
-               builtin_normal_expander(log_syntax::expand_syntax_ext));
-    env.insert(intern("trace_macros"),
-               builtin_normal_expander(trace_macros::expand_trace_macros));
-
-    deriving::register_all(env);
+    if enable_quotes {
+        use syntax::ext::quote::*;
+        register! {
+            quote_tokens: expand_quote_tokens,
+            quote_expr: expand_quote_expr,
+            quote_ty: expand_quote_ty,
+            quote_item: expand_quote_item,
+            quote_pat: expand_quote_pat,
+            quote_arm: expand_quote_arm,
+            quote_stmt: expand_quote_stmt,
+            quote_matcher: expand_quote_matcher,
+            quote_attr: expand_quote_attr,
+            quote_arg: expand_quote_arg,
+            quote_block: expand_quote_block,
+            quote_meta_item: expand_quote_meta_item,
+            quote_path: expand_quote_path,
+        }
+    }
+
+    use syntax::ext::source_util::*;
+    register! {
+        line: expand_line,
+        column: expand_column,
+        file: expand_file,
+        stringify: expand_stringify,
+        include: expand_include,
+        include_str: expand_include_str,
+        include_bytes: expand_include_bytes,
+        module_path: expand_mod,
+
+        asm: asm::expand_asm,
+        cfg: cfg::expand_cfg,
+        concat: concat::expand_syntax_ext,
+        concat_idents: concat_idents::expand_syntax_ext,
+        env: env::expand_env,
+        option_env: env::expand_option_env,
+        log_syntax: log_syntax::expand_syntax_ext,
+        trace_macros: trace_macros::expand_trace_macros,
+    }
+
+    // format_args uses `unstable` things internally.
+    register("format_args", NormalTT(Box::new(format::expand_format_args), None, true));
+
+    register("derive", MultiModifier(Box::new(deriving::expand_derive)));
 }
diff --git a/src/libsyntax_ext/rustc_macro_registrar.rs b/src/libsyntax_ext/rustc_macro_registrar.rs
index 7693e2416f4b0..ce3e53cdf97f4 100644
--- a/src/libsyntax_ext/rustc_macro_registrar.rs
+++ b/src/libsyntax_ext/rustc_macro_registrar.rs
@@ -13,12 +13,13 @@ use std::mem;
 use errors;
 use syntax::ast::{self, Ident, NodeId};
 use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute};
-use syntax::ext::base::{ExtCtxt, DummyMacroLoader};
+use syntax::ext::base::ExtCtxt;
 use syntax::ext::build::AstBuilder;
 use syntax::ext::expand::ExpansionConfig;
 use syntax::parse::ParseSess;
 use syntax::parse::token::{self, InternedString};
 use syntax::feature_gate::Features;
+use syntax::fold::Folder;
 use syntax::ptr::P;
 use syntax_pos::{Span, DUMMY_SP};
 use syntax::visit::{self, Visitor};
@@ -39,16 +40,14 @@ struct CollectCustomDerives<'a> {
 }
 
 pub fn modify(sess: &ParseSess,
+              resolver: &mut ::syntax::ext::base::Resolver,
               mut krate: ast::Crate,
               is_rustc_macro_crate: bool,
               num_crate_types: usize,
               handler: &errors::Handler,
               features: &Features) -> ast::Crate {
-    let mut loader = DummyMacroLoader;
-    let mut cx = ExtCtxt::new(sess,
-                              Vec::new(),
-                              ExpansionConfig::default("rustc_macro".to_string()),
-                              &mut loader);
+    let ecfg = ExpansionConfig::default("rustc_macro".to_string());
+    let mut cx = ExtCtxt::new(sess, Vec::new(), ecfg, resolver);
 
     let mut collect = CollectCustomDerives {
         derives: Vec::new(),
@@ -268,13 +267,11 @@ fn mk_registrar(cx: &mut ExtCtxt,
         i.vis = ast::Visibility::Public;
         i
     });
-    let module = cx.item_mod(span,
-                             span,
-                             ast::Ident::with_empty_ctxt(token::gensym("registrar")),
-                             Vec::new(),
-                             vec![krate, func]);
-    module.map(|mut i| {
+    let ident = ast::Ident::with_empty_ctxt(token::gensym("registrar"));
+    let module = cx.item_mod(span, span, ident, Vec::new(), vec![krate, func]).map(|mut i| {
         i.vis = ast::Visibility::Public;
         i
-    })
+    });
+
+    cx.monotonic_expander().fold_item(module).pop().unwrap()
 }
diff --git a/src/test/compile-fail-fulldeps/qquote.rs b/src/test/compile-fail-fulldeps/qquote.rs
index e29ded8a052c6..3e5d17e2ffb17 100644
--- a/src/test/compile-fail-fulldeps/qquote.rs
+++ b/src/test/compile-fail-fulldeps/qquote.rs
@@ -22,11 +22,11 @@ use syntax_pos::DUMMY_SP;
 
 fn main() {
     let ps = syntax::parse::ParseSess::new();
-    let mut loader = syntax::ext::base::DummyMacroLoader;
+    let mut resolver = syntax::ext::base::DummyResolver;
     let mut cx = syntax::ext::base::ExtCtxt::new(
         &ps, vec![],
         syntax::ext::expand::ExpansionConfig::default("qquote".to_string()),
-        &mut loader);
+        &mut resolver);
     cx.bt_push(syntax::codemap::ExpnInfo {
         call_site: DUMMY_SP,
         callee: syntax::codemap::NameAndSpan {
diff --git a/src/test/compile-fail/E0407.rs b/src/test/compile-fail/E0407.rs
index 2a150b7451210..c207dbfca5565 100644
--- a/src/test/compile-fail/E0407.rs
+++ b/src/test/compile-fail/E0407.rs
@@ -18,7 +18,7 @@ impl Foo for Bar {
     fn a() {}
     fn b() {}
     //~^ ERROR E0407
-    //~| NOTE not a member of `Foo`
+    //~| NOTE not a member of trait `Foo`
 }
 
 fn main() {
diff --git a/src/test/compile-fail/E0438.rs b/src/test/compile-fail/E0438.rs
index f549d62aebfea..2e2df4bee5a35 100644
--- a/src/test/compile-fail/E0438.rs
+++ b/src/test/compile-fail/E0438.rs
@@ -10,11 +10,11 @@
 
 #![feature(associated_consts)]
 
-trait Foo {}
+trait Bar {}
 
-impl Foo for i32 {
+impl Bar for i32 {
     const BAR: bool = true; //~ ERROR E0438
-        //~| NOTE not a member of trait `Foo`
+        //~| NOTE not a member of trait `Bar`
 }
 
 fn main () {
diff --git a/src/test/compile-fail/borrowck/borrowck-union-borrow-nested.rs b/src/test/compile-fail/borrowck/borrowck-union-borrow-nested.rs
index 19975d79b60be..8b6b8d9ecb08e 100644
--- a/src/test/compile-fail/borrowck/borrowck-union-borrow-nested.rs
+++ b/src/test/compile-fail/borrowck/borrowck-union-borrow-nested.rs
@@ -18,16 +18,12 @@ struct S {
     b: u16,
 }
 
+#[derive(Clone, Copy)]
 union U {
     s: S,
     c: u32,
 }
 
-impl Clone for U {
-    fn clone(&self) -> Self { *self }
-}
-impl Copy for U {}
-
 fn main() {
     unsafe {
         {
diff --git a/src/test/compile-fail/borrowck/borrowck-union-borrow.rs b/src/test/compile-fail/borrowck/borrowck-union-borrow.rs
index e8989a3c2d499..ecc698acc317f 100644
--- a/src/test/compile-fail/borrowck/borrowck-union-borrow.rs
+++ b/src/test/compile-fail/borrowck/borrowck-union-borrow.rs
@@ -12,16 +12,12 @@
 
 #![feature(untagged_unions)]
 
+#[derive(Clone, Copy)]
 union U {
     a: u8,
     b: u64,
 }
 
-impl Clone for U {
-    fn clone(&self) -> Self { *self }
-}
-impl Copy for U {}
-
 fn main() {
     unsafe {
         let mut u = U { b: 0 };
diff --git a/src/test/compile-fail/issue-36053-2.rs b/src/test/compile-fail/issue-36053-2.rs
new file mode 100644
index 0000000000000..7da529487aa86
--- /dev/null
+++ b/src/test/compile-fail/issue-36053-2.rs
@@ -0,0 +1,21 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Regression test for #36053. ICE was caused due to obligations
+// being added to a special, dedicated fulfillment cx during
+// a probe.
+
+use std::iter::once;
+fn main() {
+    once::<&str>("str").fuse().filter(|a: &str| true).count();
+    //~^ ERROR no method named `count`
+    //~| ERROR E0281
+    //~| ERROR E0281
+}
diff --git a/src/test/compile-fail/macro-expansion-tests.rs b/src/test/compile-fail/macro-expansion-tests.rs
new file mode 100644
index 0000000000000..a064e69bc6d59
--- /dev/null
+++ b/src/test/compile-fail/macro-expansion-tests.rs
@@ -0,0 +1,46 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+mod macros_cant_escape_fns {
+    fn f() {
+        macro_rules! m { () => { 3 + 4 } }
+    }
+    fn g() -> i32 { m!() } //~ ERROR macro undefined
+}
+
+mod macros_cant_escape_mods {
+    mod f {
+        macro_rules! m { () => { 3 + 4 } }
+    }
+    fn g() -> i32 { m!() } //~ ERROR macro undefined
+}
+
+mod macros_can_escape_flattened_mods_test {
+    #[macro_use]
+    mod f {
+        macro_rules! m { () => { 3 + 4 } }
+    }
+    fn g() -> i32 { m!() }
+}
+
+fn macro_tokens_should_match() {
+    macro_rules! m { (a) => { 13 } }
+    m!(a);
+}
+
+// should be able to use a bound identifier as a literal in a macro definition:
+fn self_macro_parsing() {
+    macro_rules! foo { (zz) => { 287; } }
+    fn f(zz: i32) {
+        foo!(zz);
+    }
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/union/union-copy.rs b/src/test/compile-fail/union/union-copy.rs
index 6e08ae0074d48..9014b3f2956b7 100644
--- a/src/test/compile-fail/union/union-copy.rs
+++ b/src/test/compile-fail/union/union-copy.rs
@@ -10,16 +10,16 @@
 
 #![feature(untagged_unions)]
 
+#[derive(Clone)]
 union U {
     a: u8
 }
 
+#[derive(Clone)]
 union W {
     a: String
 }
 
-impl Clone for U { fn clone(&self) { panic!(); } }
-impl Clone for W { fn clone(&self) { panic!(); } }
 impl Copy for U {} // OK
 impl Copy for W {} //~ ERROR the trait `Copy` may not be implemented for this type
 
diff --git a/src/test/compile-fail/union/union-derive-clone.rs b/src/test/compile-fail/union/union-derive-clone.rs
new file mode 100644
index 0000000000000..6e226d7d79f9f
--- /dev/null
+++ b/src/test/compile-fail/union/union-derive-clone.rs
@@ -0,0 +1,41 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+#[derive(Clone)] //~ ERROR the trait bound `U1: std::marker::Copy` is not satisfied
+union U1 {
+    a: u8,
+}
+
+#[derive(Clone)]
+union U2 {
+    a: u8, // OK
+}
+
+impl Copy for U2 {}
+
+#[derive(Clone, Copy)]
+union U3 {
+    a: u8, // OK
+}
+
+#[derive(Clone, Copy)]
+union U4<T> {
+    a: T, // OK
+}
+
+#[derive(Clone)]
+struct CloneNoCopy;
+
+fn main() {
+    let u = U4 { a: CloneNoCopy };
+    let w = u.clone(); //~ ERROR no method named `clone` found for type `U4<CloneNoCopy>`
+}
diff --git a/src/test/compile-fail/union/union-derive-eq.rs b/src/test/compile-fail/union/union-derive-eq.rs
new file mode 100644
index 0000000000000..9dfec288c1572
--- /dev/null
+++ b/src/test/compile-fail/union/union-derive-eq.rs
@@ -0,0 +1,30 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+#[derive(Eq)] // OK
+union U1 {
+    a: u8,
+}
+
+impl PartialEq for U1 { fn eq(&self, rhs: &Self) -> bool { true } }
+
+#[derive(PartialEq)]
+struct PartialEqNotEq;
+
+#[derive(Eq)]
+union U2 {
+    a: PartialEqNotEq, //~ ERROR the trait bound `PartialEqNotEq: std::cmp::Eq` is not satisfied
+}
+
+impl PartialEq for U2 { fn eq(&self, rhs: &Self) -> bool { true } }
+
+fn main() {}
diff --git a/src/test/compile-fail/union/union-derive.rs b/src/test/compile-fail/union/union-derive.rs
index 0f78e96f640c7..26dbdfd0b4118 100644
--- a/src/test/compile-fail/union/union-derive.rs
+++ b/src/test/compile-fail/union/union-derive.rs
@@ -13,9 +13,7 @@
 #![feature(untagged_unions)]
 
 #[derive(
-    Clone, //~ ERROR this trait cannot be derived for unions
     PartialEq, //~ ERROR this trait cannot be derived for unions
-    Eq, //~ ERROR this trait cannot be derived for unions
     PartialOrd, //~ ERROR this trait cannot be derived for unions
     Ord, //~ ERROR this trait cannot be derived for unions
     Hash, //~ ERROR this trait cannot be derived for unions
diff --git a/src/test/run-fail-fulldeps/qquote.rs b/src/test/run-fail-fulldeps/qquote.rs
index 47e97abbbaa47..1458583ff5830 100644
--- a/src/test/run-fail-fulldeps/qquote.rs
+++ b/src/test/run-fail-fulldeps/qquote.rs
@@ -25,11 +25,11 @@ use syntax_pos::DUMMY_SP;
 
 fn main() {
     let ps = syntax::parse::ParseSess::new();
-    let mut loader = syntax::ext::base::DummyMacroLoader;
+    let mut resolver = syntax::ext::base::DummyResolver;
     let mut cx = syntax::ext::base::ExtCtxt::new(
         &ps, vec![],
         syntax::ext::expand::ExpansionConfig::default("qquote".to_string()),
-        &mut loader);
+        &mut resolver);
     cx.bt_push(syntax::codemap::ExpnInfo {
         call_site: DUMMY_SP,
         callee: syntax::codemap::NameAndSpan {
diff --git a/src/test/run-fail/bounds-check-no-overflow.rs b/src/test/run-fail/bounds-check-no-overflow.rs
index 4d502cb2106b1..3d1cbb446e848 100644
--- a/src/test/run-fail/bounds-check-no-overflow.rs
+++ b/src/test/run-fail/bounds-check-no-overflow.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:assertion failed: index < self.len()
+// error-pattern:index out of bounds
 
 use std::usize;
 use std::mem::size_of;
diff --git a/src/test/run-pass-fulldeps/qquote.rs b/src/test/run-pass-fulldeps/qquote.rs
index a4f0e35cc5ac7..2a53a62a5ab60 100644
--- a/src/test/run-pass-fulldeps/qquote.rs
+++ b/src/test/run-pass-fulldeps/qquote.rs
@@ -21,11 +21,11 @@ use syntax_pos::DUMMY_SP;
 
 fn main() {
     let ps = syntax::parse::ParseSess::new();
-    let mut loader = syntax::ext::base::DummyMacroLoader;
+    let mut resolver = syntax::ext::base::DummyResolver;
     let mut cx = syntax::ext::base::ExtCtxt::new(
         &ps, vec![],
         syntax::ext::expand::ExpansionConfig::default("qquote".to_string()),
-        &mut loader);
+        &mut resolver);
     cx.bt_push(syntax::codemap::ExpnInfo {
         call_site: DUMMY_SP,
         callee: syntax::codemap::NameAndSpan {
diff --git a/src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs b/src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs
index 42c0da6286bdc..10e315f269f97 100644
--- a/src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs
+++ b/src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs
@@ -22,6 +22,8 @@ pub type F = Option<isize>;
 pub type G = usize;
 pub type H = &'static str;
 pub type I = Box<Fn()>;
+pub type I32Iterator = Iterator<Item=i32>;
+pub type U32Iterator = Iterator<Item=u32>;
 
 pub fn id_A() -> TypeId { TypeId::of::<A>() }
 pub fn id_B() -> TypeId { TypeId::of::<B>() }
@@ -34,3 +36,6 @@ pub fn id_H() -> TypeId { TypeId::of::<H>() }
 pub fn id_I() -> TypeId { TypeId::of::<I>() }
 
 pub fn foo<T: Any>() -> TypeId { TypeId::of::<T>() }
+
+pub fn id_i32_iterator() -> TypeId { TypeId::of::<I32Iterator>() }
+pub fn id_u32_iterator() -> TypeId { TypeId::of::<U32Iterator>() }
diff --git a/src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs b/src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs
index 42c0da6286bdc..10e315f269f97 100644
--- a/src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs
+++ b/src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs
@@ -22,6 +22,8 @@ pub type F = Option<isize>;
 pub type G = usize;
 pub type H = &'static str;
 pub type I = Box<Fn()>;
+pub type I32Iterator = Iterator<Item=i32>;
+pub type U32Iterator = Iterator<Item=u32>;
 
 pub fn id_A() -> TypeId { TypeId::of::<A>() }
 pub fn id_B() -> TypeId { TypeId::of::<B>() }
@@ -34,3 +36,6 @@ pub fn id_H() -> TypeId { TypeId::of::<H>() }
 pub fn id_I() -> TypeId { TypeId::of::<I>() }
 
 pub fn foo<T: Any>() -> TypeId { TypeId::of::<T>() }
+
+pub fn id_i32_iterator() -> TypeId { TypeId::of::<I32Iterator>() }
+pub fn id_u32_iterator() -> TypeId { TypeId::of::<U32Iterator>() }
diff --git a/src/test/run-pass/issue-35546.rs b/src/test/run-pass/issue-35546.rs
new file mode 100644
index 0000000000000..e8d14f1d42146
--- /dev/null
+++ b/src/test/run-pass/issue-35546.rs
@@ -0,0 +1,28 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Regression test for #35546. Check that we are able to codegen
+// this. Before we had problems because of the drop glue signature
+// around dropping a trait object (specifically, when dropping the
+// `value` field of `Node<Send>`).
+
+struct Node<T: ?Sized + Send> {
+    next: Option<Box<Node<Send>>>,
+    value: T,
+}
+
+fn clear(head: &mut Option<Box<Node<Send>>>) {
+    match head.take() {
+        Some(node) => *head = node.next,
+        None => (),
+    }
+}
+
+fn main() {}
diff --git a/src/test/run-pass/issue-36053.rs b/src/test/run-pass/issue-36053.rs
new file mode 100644
index 0000000000000..2411996cf054b
--- /dev/null
+++ b/src/test/run-pass/issue-36053.rs
@@ -0,0 +1,32 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Regression test for #36053. ICE was caused due to obligations being
+// added to a special, dedicated fulfillment cx during a
+// probe. Problem seems to be related to the particular definition of
+// `FusedIterator` in std but I was not able to isolate that into an
+// external crate.
+
+#![feature(fused)]
+use std::iter::FusedIterator;
+
+struct Thing<'a>(&'a str);
+impl<'a> Iterator for Thing<'a> {
+    type Item = &'a str;
+    fn next(&mut self) -> Option<&'a str> {
+        None
+    }
+}
+
+impl<'a> FusedIterator for Thing<'a> {}
+
+fn main() {
+    Thing("test").fuse().filter(|_| true).count();
+}
diff --git a/src/test/run-pass/typeid-intrinsic.rs b/src/test/run-pass/typeid-intrinsic.rs
index e99a5f69af40f..36650368d57be 100644
--- a/src/test/run-pass/typeid-intrinsic.rs
+++ b/src/test/run-pass/typeid-intrinsic.rs
@@ -78,4 +78,13 @@ pub fn main() {
     b.hash(&mut s2);
 
     assert_eq!(s1.finish(), s2.finish());
+
+    // Check projections
+
+    assert_eq!(TypeId::of::<other1::I32Iterator>(), other1::id_i32_iterator());
+    assert_eq!(TypeId::of::<other1::U32Iterator>(), other1::id_u32_iterator());
+    assert_eq!(other1::id_i32_iterator(), other2::id_i32_iterator());
+    assert_eq!(other1::id_u32_iterator(), other2::id_u32_iterator());
+    assert!(other1::id_i32_iterator() != other1::id_u32_iterator());
+    assert!(TypeId::of::<other1::I32Iterator>() != TypeId::of::<other1::U32Iterator>());
 }
diff --git a/src/test/run-pass/union/union-c-interop.rs b/src/test/run-pass/union/union-c-interop.rs
index a9f97620ebd46..bea4d5f923e21 100644
--- a/src/test/run-pass/union/union-c-interop.rs
+++ b/src/test/run-pass/union/union-c-interop.rs
@@ -10,14 +10,14 @@
 
 #![feature(untagged_unions)]
 
-#[derive(Copy)]
+#[derive(Clone, Copy)]
 #[repr(C)]
 struct LARGE_INTEGER_U {
     LowPart: u32,
     HighPart: u32,
 }
 
-#[derive(Copy)]
+#[derive(Clone, Copy)]
 #[repr(C)]
 union LARGE_INTEGER {
   __unnamed__: LARGE_INTEGER_U,
@@ -25,9 +25,6 @@ union LARGE_INTEGER {
   QuadPart: u64,
 }
 
-impl Clone for LARGE_INTEGER_U { fn clone(&self) -> Self { *self } }
-impl Clone for LARGE_INTEGER { fn clone(&self) -> Self { *self } }
-
 #[link(name = "rust_test_helpers")]
 extern "C" {
     fn increment_all_parts(_: LARGE_INTEGER) -> LARGE_INTEGER;
diff --git a/src/test/run-pass/union/union-derive.rs b/src/test/run-pass/union/union-derive.rs
index b71c23990a474..8ff6f17394f9e 100644
--- a/src/test/run-pass/union/union-derive.rs
+++ b/src/test/run-pass/union/union-derive.rs
@@ -14,18 +14,34 @@
 
 #[derive(
     Copy,
+    Clone,
+    Eq,
 )]
 union U {
     a: u8,
     b: u16,
 }
 
-impl Clone for U {
-    fn clone(&self) -> Self { *self }
+impl PartialEq for U { fn eq(&self, rhs: &Self) -> bool { true } }
+
+#[derive(
+    Clone,
+    Copy,
+    Eq
+)]
+union W<T> {
+    a: T,
 }
 
+impl<T> PartialEq for W<T> { fn eq(&self, rhs: &Self) -> bool { true } }
+
 fn main() {
     let u = U { b: 0 };
     let u1 = u;
     let u2 = u.clone();
+    assert!(u1 == u2);
+
+    let w = W { a: 0 };
+    let w1 = w.clone();
+    assert!(w == w1);
 }