about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--src/libcore/mem/maybe_uninit.rs3
-rw-r--r--src/libcore/mem/mod.rs3
-rw-r--r--src/libcore/slice/mod.rs11
-rw-r--r--src/librustc/middle/cstore.rs4
-rw-r--r--src/librustc/mir/interpret/error.rs2
-rw-r--r--src/librustc/ty/layout.rs4
-rw-r--r--src/librustc/ty/mod.rs7
-rw-r--r--src/librustc/ty/sty.rs14
-rw-r--r--src/librustc/ty/util.rs8
-rw-r--r--src/librustc/ty/walk.rs4
-rw-r--r--src/librustc_codegen_ssa/lib.rs1
-rw-r--r--src/librustc_codegen_ssa/mir/block.rs6
-rw-r--r--src/librustc_lint/builtin.rs91
-rw-r--r--src/librustc_lint/lib.rs1
-rw-r--r--src/librustc_mir/interpret/eval_context.rs10
-rw-r--r--src/librustc_mir/interpret/snapshot.rs4
-rw-r--r--src/librustc_mir/interpret/terminator.rs8
-rw-r--r--src/librustc_mir/shim.rs2
-rw-r--r--src/librustc_mir/util/elaborate_drops.rs4
-rw-r--r--src/librustdoc/Cargo.toml1
-rw-r--r--src/librustdoc/clean/blanket_impl.rs3
-rw-r--r--src/librustdoc/clean/inline.rs17
-rw-r--r--src/librustdoc/clean/mod.rs26
-rw-r--r--src/librustdoc/config.rs18
-rw-r--r--src/librustdoc/core.rs47
-rw-r--r--src/librustdoc/doctree.rs42
-rw-r--r--src/librustdoc/externalfiles.rs48
-rw-r--r--src/librustdoc/fold.rs8
-rw-r--r--src/librustdoc/html/markdown.rs259
-rw-r--r--src/librustdoc/html/markdown/tests.rs12
-rw-r--r--src/librustdoc/html/render.rs52
-rw-r--r--src/librustdoc/lib.rs16
-rw-r--r--src/librustdoc/markdown.rs12
-rw-r--r--src/librustdoc/passes/collapse_docs.rs4
-rw-r--r--src/librustdoc/passes/collect_trait_impls.rs4
-rw-r--r--src/librustdoc/passes/mod.rs59
-rw-r--r--src/librustdoc/visit_ast.rs28
-rw-r--r--src/libstd/ffi/c_str.rs6
-rw-r--r--src/libstd/ffi/os_str.rs6
-rw-r--r--src/libstd/path.rs12
-rw-r--r--src/libstd/sys_common/os_str_bytes.rs6
-rw-r--r--src/libsyntax_pos/symbol.rs3
-rw-r--r--src/test/ui/consts/uninhabited-const-issue-61744.rs4
-rw-r--r--src/test/ui/consts/uninhabited-const-issue-61744.stderr57
-rw-r--r--src/test/ui/lint/uninitialized-zeroed.rs58
-rw-r--r--src/test/ui/lint/uninitialized-zeroed.stderr169
-rw-r--r--src/test/ui/panic-uninitialized-zeroed.rs2
m---------src/tools/clippy16
-rw-r--r--src/tools/error_index_generator/main.rs13
50 files changed, 802 insertions, 394 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 52cfa2cb1f8..ab6731e4d43 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3252,7 +3252,6 @@ name = "rustdoc"
 version = "0.0.0"
 dependencies = [
  "minifier 0.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
- "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "pulldown-cmark 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-rayon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/src/libcore/mem/maybe_uninit.rs b/src/libcore/mem/maybe_uninit.rs
index 8d064de6f47..1bbea02e0c7 100644
--- a/src/libcore/mem/maybe_uninit.rs
+++ b/src/libcore/mem/maybe_uninit.rs
@@ -13,6 +13,7 @@ use crate::mem::ManuallyDrop;
 /// ever gets used to access memory:
 ///
 /// ```rust,no_run
+/// # #![allow(invalid_value)]
 /// use std::mem::{self, MaybeUninit};
 ///
 /// let x: &i32 = unsafe { mem::zeroed() }; // undefined behavior!
@@ -27,6 +28,7 @@ use crate::mem::ManuallyDrop;
 /// always be `true` or `false`. Hence, creating an uninitialized `bool` is undefined behavior:
 ///
 /// ```rust,no_run
+/// # #![allow(invalid_value)]
 /// use std::mem::{self, MaybeUninit};
 ///
 /// let b: bool = unsafe { mem::uninitialized() }; // undefined behavior!
@@ -40,6 +42,7 @@ use crate::mem::ManuallyDrop;
 /// which otherwise can hold any *fixed* bit pattern:
 ///
 /// ```rust,no_run
+/// # #![allow(invalid_value)]
 /// use std::mem::{self, MaybeUninit};
 ///
 /// let x: i32 = unsafe { mem::uninitialized() }; // undefined behavior!
diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs
index 2b462f08d42..2534400b833 100644
--- a/src/libcore/mem/mod.rs
+++ b/src/libcore/mem/mod.rs
@@ -445,7 +445,8 @@ pub const fn needs_drop<T>() -> bool {
 ///
 /// *Incorrect* usage of this function: initializing a reference with zero.
 ///
-/// ```no_run
+/// ```rust,no_run
+/// # #![allow(invalid_value)]
 /// use std::mem;
 ///
 /// let _x: &i32 = unsafe { mem::zeroed() }; // Undefined behavior!
diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs
index d5a34ea2bd5..ce5af13d4ca 100644
--- a/src/libcore/slice/mod.rs
+++ b/src/libcore/slice/mod.rs
@@ -1364,6 +1364,17 @@ impl<T> [T] {
     /// let r = s.binary_search(&1);
     /// assert!(match r { Ok(1..=4) => true, _ => false, });
     /// ```
+    ///
+    /// If you want to insert an item to a sorted vector, while maintaining
+    /// sort order:
+    ///
+    /// ```
+    /// let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+    /// let num = 42;
+    /// let idx = s.binary_search(&num).unwrap_or_else(|x| x);
+    /// s.insert(idx, num);
+    /// assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
+    /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn binary_search(&self, x: &T) -> Result<usize, usize>
         where T: Ord
diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs
index 5a580dfa420..d37b2367ae7 100644
--- a/src/librustc/middle/cstore.rs
+++ b/src/librustc/middle/cstore.rs
@@ -87,7 +87,7 @@ pub enum LinkagePreference {
     RequireStatic,
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash,
          RustcEncodable, RustcDecodable, HashStable)]
 pub enum NativeLibraryKind {
     /// native static library (.a archive)
@@ -100,7 +100,7 @@ pub enum NativeLibraryKind {
     NativeUnknown,
 }
 
-#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
 pub struct NativeLibrary {
     pub kind: NativeLibraryKind,
     pub name: Option<Symbol>,
diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs
index f53d2ffb6df..ef0e2051848 100644
--- a/src/librustc/mir/interpret/error.rs
+++ b/src/librustc/mir/interpret/error.rs
@@ -217,7 +217,7 @@ fn print_backtrace(backtrace: &mut Backtrace) {
 
 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
     fn from(kind: InterpError<'tcx>) -> Self {
-        let backtrace = match env::var("RUST_CTFE_BACKTRACE") {
+        let backtrace = match env::var("RUSTC_CTFE_BACKTRACE") {
             // Matching `RUST_BACKTRACE` -- we treat "0" the same as "not present".
             Ok(ref val) if val != "0" => {
                 let mut backtrace = Backtrace::new_unresolved();
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index a9d1fd1fffc..19c753bc304 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -2518,7 +2518,7 @@ where
         + HasTyCtxt<'tcx>
         + HasParamEnv<'tcx>,
 {
-    fn of_instance(cx: &C, instance: &ty::Instance<'tcx>) -> Self;
+    fn of_instance(cx: &C, instance: ty::Instance<'tcx>) -> Self;
     fn new(cx: &C, sig: ty::FnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self;
     fn new_vtable(cx: &C, sig: ty::FnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self;
     fn new_internal(
@@ -2538,7 +2538,7 @@ where
         + HasTyCtxt<'tcx>
         + HasParamEnv<'tcx>,
 {
-    fn of_instance(cx: &C, instance: &ty::Instance<'tcx>) -> Self {
+    fn of_instance(cx: &C, instance: ty::Instance<'tcx>) -> Self {
         let sig = instance.fn_sig(cx.tcx());
         let sig = cx
             .tcx()
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index a10578b0a43..9d563e290de 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1842,7 +1842,8 @@ pub struct VariantDef {
     pub ctor_kind: CtorKind,
     /// Flags of the variant (e.g. is field list non-exhaustive)?
     flags: VariantFlags,
-    /// Recovered?
+    /// Variant is obtained as part of recovering from a syntactic error.
+    /// May be incomplete or bogus.
     pub recovered: bool,
 }
 
@@ -1949,7 +1950,7 @@ pub struct FieldDef {
 pub struct AdtDef {
     /// `DefId` of the struct, enum or union item.
     pub did: DefId,
-    /// Variants of the ADT. If this is a struct or enum, then there will be a single variant.
+    /// Variants of the ADT. If this is a struct or union, then there will be a single variant.
     pub variants: IndexVec<self::layout::VariantIdx, VariantDef>,
     /// Flags of the ADT (e.g. is this a struct? is this non-exhaustive?)
     flags: AdtFlags,
@@ -2565,6 +2566,8 @@ impl<'tcx> AdtDef {
 }
 
 impl<'tcx> FieldDef {
+    /// Returns the type of this field. The `subst` is typically obtained
+    /// via the second field of `TyKind::AdtDef`.
     pub fn ty(&self, tcx: TyCtxt<'tcx>, subst: SubstsRef<'tcx>) -> Ty<'tcx> {
         tcx.type_of(self.did).subst(tcx, subst)
     }
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index 064c374de2b..129ea9b5b67 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -171,6 +171,7 @@ pub enum TyKind<'tcx> {
     Never,
 
     /// A tuple type. For example, `(i32, bool)`.
+    /// Use `TyS::tuple_fields` to iterate over the field types.
     Tuple(SubstsRef<'tcx>),
 
     /// The projection of an associated type. For example,
@@ -1723,8 +1724,8 @@ impl<'tcx> TyS<'tcx> {
                     })
                 })
             }
-            ty::Tuple(tys) => tys.iter().any(|ty| {
-                ty.expect_ty().conservative_is_privately_uninhabited(tcx)
+            ty::Tuple(..) => self.tuple_fields().any(|ty| {
+                ty.conservative_is_privately_uninhabited(tcx)
             }),
             ty::Array(ty, len) => {
                 match len.try_eval_usize(tcx, ParamEnv::empty()) {
@@ -2103,6 +2104,15 @@ impl<'tcx> TyS<'tcx> {
         }
     }
 
+    /// Iterates over tuple fields.
+    /// Panics when called on anything but a tuple.
+    pub fn tuple_fields(&self) -> impl DoubleEndedIterator<Item=Ty<'tcx>> {
+        match self.sty {
+            Tuple(substs) => substs.iter().map(|field| field.expect_ty()),
+            _ => bug!("tuple_fields called on non-tuple"),
+        }
+    }
+
     /// If the type contains variants, returns the valid range of variant indices.
     /// FIXME This requires the optimized MIR in the case of generators.
     #[inline]
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index c3ecc04b12e..96e16efd130 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -845,15 +845,15 @@ impl<'tcx> ty::TyS<'tcx> {
             ty: Ty<'tcx>,
         ) -> Representability {
             match ty.sty {
-                Tuple(ref ts) => {
+                Tuple(..) => {
                     // Find non representable
-                    fold_repr(ts.iter().map(|ty| {
+                    fold_repr(ty.tuple_fields().map(|ty| {
                         is_type_structurally_recursive(
                             tcx,
                             sp,
                             seen,
                             representable_cache,
-                            ty.expect_ty(),
+                            ty,
                         )
                     }))
                 }
@@ -1095,7 +1095,7 @@ fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>
         // state transformation pass
         ty::Generator(..) => true,
 
-        ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).any(needs_drop),
+        ty::Tuple(..) => ty.tuple_fields().any(needs_drop),
 
         // unions don't have destructors because of the child types,
         // only if they manually implement `Drop` (handled above).
diff --git a/src/librustc/ty/walk.rs b/src/librustc/ty/walk.rs
index c74511cf0fd..8c3110792a8 100644
--- a/src/librustc/ty/walk.rs
+++ b/src/librustc/ty/walk.rs
@@ -119,8 +119,8 @@ fn push_subtypes<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent_ty: Ty<'tcx>) {
         ty::GeneratorWitness(ts) => {
             stack.extend(ts.skip_binder().iter().cloned().rev());
         }
-        ty::Tuple(ts) => {
-            stack.extend(ts.iter().map(|k| k.expect_ty()).rev());
+        ty::Tuple(..) => {
+            stack.extend(parent_ty.tuple_fields().rev());
         }
         ty::FnDef(_, substs) => {
             stack.extend(substs.types().rev());
diff --git a/src/librustc_codegen_ssa/lib.rs b/src/librustc_codegen_ssa/lib.rs
index 0e3c3a77b28..68640abb043 100644
--- a/src/librustc_codegen_ssa/lib.rs
+++ b/src/librustc_codegen_ssa/lib.rs
@@ -128,6 +128,7 @@ bitflags::bitflags! {
 }
 
 /// Misc info we load from metadata to persist beyond the tcx.
+#[derive(Debug)]
 pub struct CrateInfo {
     pub panic_runtime: Option<CrateNum>,
     pub compiler_builtins: Option<CrateNum>,
diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs
index 006ebcbdec6..ce98979cc0c 100644
--- a/src/librustc_codegen_ssa/mir/block.rs
+++ b/src/librustc_codegen_ssa/mir/block.rs
@@ -337,7 +337,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             }
             _ => {
                 (bx.get_fn(drop_fn),
-                 FnType::of_instance(&bx, &drop_fn))
+                 FnType::of_instance(&bx, drop_fn))
             }
         };
         helper.do_call(self, &mut bx, fn_ty, drop_fn, args,
@@ -435,7 +435,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         // Obtain the panic entry point.
         let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item);
         let instance = ty::Instance::mono(bx.tcx(), def_id);
-        let fn_ty = FnType::of_instance(&bx, &instance);
+        let fn_ty = FnType::of_instance(&bx, instance);
         let llfn = bx.get_fn(instance);
 
         // Codegen the actual panic invoke/call.
@@ -552,7 +552,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 let def_id =
                     common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem);
                 let instance = ty::Instance::mono(bx.tcx(), def_id);
-                let fn_ty = FnType::of_instance(&bx, &instance);
+                let fn_ty = FnType::of_instance(&bx, instance);
                 let llfn = bx.get_fn(instance);
 
                 // Codegen the actual panic invoke/call.
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index c9153f285ff..13ec27aa1ab 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -23,7 +23,7 @@
 
 use rustc::hir::def::{Res, DefKind};
 use rustc::hir::def_id::{DefId, LOCAL_CRATE};
-use rustc::ty::{self, Ty, TyCtxt};
+use rustc::ty::{self, Ty, TyCtxt, layout::VariantIdx};
 use rustc::{lint, util};
 use hir::Node;
 use util::nodemap::HirIdSet;
@@ -1862,3 +1862,92 @@ impl EarlyLintPass for IncompleteFeatures {
             });
     }
 }
+
+declare_lint! {
+    pub INVALID_VALUE,
+    Warn,
+    "an invalid value is being created (such as a NULL reference)"
+}
+
+declare_lint_pass!(InvalidValue => [INVALID_VALUE]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
+    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &hir::Expr) {
+
+        const ZEROED_PATH: &[Symbol] = &[sym::core, sym::mem, sym::zeroed];
+        const UININIT_PATH: &[Symbol] = &[sym::core, sym::mem, sym::uninitialized];
+
+        /// Return `false` only if we are sure this type does *not*
+        /// allow zero initialization.
+        fn ty_maybe_allows_zero_init<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
+            use rustc::ty::TyKind::*;
+            match ty.sty {
+                // Primitive types that don't like 0 as a value.
+                Ref(..) | FnPtr(..) | Never => false,
+                Adt(..) if ty.is_box() => false,
+                // Recurse for some compound types.
+                Adt(adt_def, substs) if !adt_def.is_union() => {
+                    match adt_def.variants.len() {
+                        0 => false, // Uninhabited enum!
+                        1 => {
+                            // Struct, or enum with exactly one variant.
+                            // Proceed recursively, check all fields.
+                            let variant = &adt_def.variants[VariantIdx::from_u32(0)];
+                            variant.fields.iter().all(|field| {
+                                ty_maybe_allows_zero_init(
+                                    tcx,
+                                    field.ty(tcx, substs),
+                                )
+                            })
+                        }
+                        _ => true, // Conservative fallback for multi-variant enum.
+                    }
+                }
+                Tuple(..) => {
+                    // Proceed recursively, check all fields.
+                    ty.tuple_fields().all(|field| ty_maybe_allows_zero_init(tcx, field))
+                }
+                // FIXME: Would be nice to also warn for `NonNull`/`NonZero*`.
+                // FIXME: *Only for `mem::uninitialized`*, we could also warn for `bool`,
+                //        `char`, and any multivariant enum.
+                // Conservative fallback.
+                _ => true,
+            }
+        }
+
+        if let hir::ExprKind::Call(ref path_expr, ref _args) = expr.node {
+            if let hir::ExprKind::Path(ref qpath) = path_expr.node {
+                if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id() {
+                    if cx.match_def_path(def_id, &ZEROED_PATH) ||
+                        cx.match_def_path(def_id, &UININIT_PATH)
+                    {
+                        // This conjures an instance of a type out of nothing,
+                        // using zeroed or uninitialized memory.
+                        // We are extremely conservative with what we warn about.
+                        let conjured_ty = cx.tables.expr_ty(expr);
+
+                        if !ty_maybe_allows_zero_init(cx.tcx, conjured_ty) {
+                            cx.struct_span_lint(
+                                INVALID_VALUE,
+                                expr.span,
+                                &format!(
+                                    "the type `{}` does not permit {}",
+                                    conjured_ty,
+                                    if cx.match_def_path(def_id, &ZEROED_PATH) {
+                                        "zero-initialization"
+                                    } else {
+                                        "being left uninitialized"
+                                    }
+                                ),
+                            )
+                            .note("this means that this code causes undefined behavior \
+                                when executed")
+                            .help("use `MaybeUninit` instead")
+                            .emit();
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs
index 78bc164ba1a..3a540fdf4b9 100644
--- a/src/librustc_lint/lib.rs
+++ b/src/librustc_lint/lib.rs
@@ -177,6 +177,7 @@ macro_rules! late_lint_mod_passes {
             UnreachablePub: UnreachablePub,
 
             ExplicitOutlivesRequirements: ExplicitOutlivesRequirements,
+            InvalidValue: InvalidValue,
         ]);
     )
 }
diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs
index 1f23d8c017c..6f4227ed34c 100644
--- a/src/librustc_mir/interpret/eval_context.rs
+++ b/src/librustc_mir/interpret/eval_context.rs
@@ -385,15 +385,19 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         local: mir::Local,
         layout: Option<TyLayout<'tcx>>,
     ) -> InterpResult<'tcx, TyLayout<'tcx>> {
-        match frame.locals[local].layout.get() {
+        // `const_prop` runs into this with an invalid (empty) frame, so we
+        // have to support that case (mostly by skipping all caching).
+        match frame.locals.get(local).and_then(|state| state.layout.get()) {
             None => {
                 let layout = crate::interpret::operand::from_known_layout(layout, || {
                     let local_ty = frame.body.local_decls[local].ty;
                     let local_ty = self.monomorphize_with_substs(local_ty, frame.instance.substs)?;
                     self.layout_of(local_ty)
                 })?;
-                // Layouts of locals are requested a lot, so we cache them.
-                frame.locals[local].layout.set(Some(layout));
+                if let Some(state) = frame.locals.get(local) {
+                    // Layouts of locals are requested a lot, so we cache them.
+                    state.layout.set(Some(layout));
+                }
                 Ok(layout)
             }
             Some(layout) => Ok(layout),
diff --git a/src/librustc_mir/interpret/snapshot.rs b/src/librustc_mir/interpret/snapshot.rs
index fad9fafbb08..47289064f4d 100644
--- a/src/librustc_mir/interpret/snapshot.rs
+++ b/src/librustc_mir/interpret/snapshot.rs
@@ -304,7 +304,7 @@ impl_stable_hash_for!(enum crate::interpret::eval_context::StackPopCleanup {
 
 #[derive(Eq, PartialEq)]
 struct FrameSnapshot<'a, 'tcx> {
-    instance: &'a ty::Instance<'tcx>,
+    instance: ty::Instance<'tcx>,
     span: Span,
     return_to_block: &'a StackPopCleanup,
     return_place: Option<Place<(), AllocIdSnapshot<'a>>>,
@@ -344,7 +344,7 @@ impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx>
         } = self;
 
         FrameSnapshot {
-            instance,
+            instance: *instance,
             span: *span,
             return_to_block,
             block,
diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs
index a0c6fb026dd..1d6b48e9da4 100644
--- a/src/librustc_mir/interpret/terminator.rs
+++ b/src/librustc_mir/interpret/terminator.rs
@@ -405,9 +405,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                         }
                     } else {
                         let local = mir::RETURN_PLACE;
-                        let ty = self.frame().body.local_decls[local].ty;
-                        if !self.tcx.is_ty_uninhabited_from_any_module(ty) {
-                            throw_unsup!(FunctionRetMismatch(self.tcx.types.never, ty))
+                        let callee_layout = self.layout_of_local(self.frame(), local, None)?;
+                        if !callee_layout.abi.is_uninhabited() {
+                            throw_unsup!(FunctionRetMismatch(
+                                self.tcx.types.never, callee_layout.ty
+                            ))
                         }
                     }
                     Ok(())
diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs
index 42945c79ddf..33447eba749 100644
--- a/src/librustc_mir/shim.rs
+++ b/src/librustc_mir/shim.rs
@@ -324,7 +324,7 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -
                 substs.upvar_tys(def_id, tcx)
             )
         }
-        ty::Tuple(tys) => builder.tuple_like_shim(dest, src, tys.iter().map(|k| k.expect_ty())),
+        ty::Tuple(..) => builder.tuple_like_shim(dest, src, self_ty.tuple_fields()),
         _ => {
             bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty)
         }
diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs
index d17dcaafc04..52fd645e38e 100644
--- a/src/librustc_mir/util/elaborate_drops.rs
+++ b/src/librustc_mir/util/elaborate_drops.rs
@@ -804,8 +804,8 @@ where
                 let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx()).collect();
                 self.open_drop_for_tuple(&tys)
             }
-            ty::Tuple(tys) => {
-                let tys: Vec<_> = tys.iter().map(|k| k.expect_ty()).collect();
+            ty::Tuple(..) => {
+                let tys: Vec<_> = ty.tuple_fields().collect();
                 self.open_drop_for_tuple(&tys)
             }
             ty::Adt(def, substs) => {
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index 334dc74c6c8..0eb8b73016d 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -13,4 +13,3 @@ pulldown-cmark = { version = "0.5.3", default-features = false }
 minifier = "0.0.33"
 rayon = { version = "0.2.0", package = "rustc-rayon" }
 tempfile = "3"
-parking_lot = "0.7"
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index 64cffaec2ea..490d4107c51 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -3,6 +3,7 @@ use rustc::traits;
 use rustc::ty::ToPredicate;
 use rustc::ty::subst::Subst;
 use rustc::infer::InferOk;
+use rustc::hir::def_id::LOCAL_CRATE;
 use syntax_pos::DUMMY_SP;
 
 use super::*;
@@ -27,7 +28,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
 
         debug!("get_blanket_impls({:?})", ty);
         let mut impls = Vec::new();
-        for &trait_def_id in self.cx.all_traits.iter() {
+        for &trait_def_id in self.cx.tcx.all_traits(LOCAL_CRATE).iter() {
             if !self.cx.renderinfo.borrow().access_levels.is_public(trait_def_id) ||
                self.cx.generated_synthetics
                       .borrow_mut()
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index d2a6dcf19bc..6f93c95edef 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -163,10 +163,7 @@ pub fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> {
 /// These names are used later on by HTML rendering to generate things like
 /// source links back to the original item.
 pub fn record_extern_fqn(cx: &DocContext<'_>, did: DefId, kind: clean::TypeKind) {
-    let mut crate_name = cx.tcx.crate_name(did.krate).to_string();
-    if did.is_local() {
-        crate_name = cx.crate_name.clone().unwrap_or(crate_name);
-    }
+    let crate_name = cx.tcx.crate_name(did.krate).to_string();
 
     let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| {
         // extern blocks have an empty name
@@ -577,22 +574,18 @@ pub fn record_extern_trait(cx: &DocContext<'_>, did: DefId) {
     }
 
     {
-        let external_traits = cx.external_traits.lock();
-        if external_traits.borrow().contains_key(&did) ||
+        if cx.external_traits.borrow().contains_key(&did) ||
             cx.active_extern_traits.borrow().contains(&did)
         {
             return;
         }
     }
 
-    cx.active_extern_traits.borrow_mut().push(did);
+    cx.active_extern_traits.borrow_mut().insert(did);
 
     debug!("record_extern_trait: {:?}", did);
     let trait_ = build_external_trait(cx, did);
 
-    {
-        let external_traits = cx.external_traits.lock();
-        external_traits.borrow_mut().insert(did, trait_);
-    }
-    cx.active_extern_traits.borrow_mut().remove_item(&did);
+    cx.external_traits.borrow_mut().insert(did, trait_);
+    cx.active_extern_traits.borrow_mut().remove(&did);
 }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 6f33bdd7f4d..b281505956d 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -39,14 +39,12 @@ use std::fmt;
 use std::hash::{Hash, Hasher};
 use std::default::Default;
 use std::{mem, slice, vec};
-use std::iter::{FromIterator, once};
+use std::iter::FromIterator;
 use std::rc::Rc;
 use std::cell::RefCell;
 use std::sync::Arc;
 use std::u32;
 
-use parking_lot::ReentrantMutex;
-
 use crate::core::{self, DocContext};
 use crate::doctree;
 use crate::html::render::{cache, ExternalLocation};
@@ -133,8 +131,9 @@ pub struct Crate {
     pub primitives: Vec<(DefId, PrimitiveType, Attributes)>,
     // These are later on moved into `CACHEKEY`, leaving the map empty.
     // Only here so that they can be filtered through the rustdoc passes.
-    pub external_traits: Arc<ReentrantMutex<RefCell<FxHashMap<DefId, Trait>>>>,
+    pub external_traits: Rc<RefCell<FxHashMap<DefId, Trait>>>,
     pub masked_crates: FxHashSet<CrateNum>,
+    pub collapsed: bool,
 }
 
 impl Clean<Crate> for hir::Crate {
@@ -223,6 +222,7 @@ impl Clean<Crate> for hir::Crate {
             primitives,
             external_traits: cx.external_traits.clone(),
             masked_crates,
+            collapsed: false,
         }
     }
 }
@@ -4398,24 +4398,6 @@ impl Clean<TypeBindingKind> for hir::TypeBindingKind {
     }
 }
 
-pub fn def_id_to_path(
-    cx: &DocContext<'_>,
-    did: DefId,
-    name: Option<String>
-) -> Vec<String> {
-    let crate_name = name.unwrap_or_else(|| cx.tcx.crate_name(did.krate).to_string());
-    let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| {
-        // extern blocks have an empty name
-        let s = elem.data.to_string();
-        if !s.is_empty() {
-            Some(s)
-        } else {
-            None
-        }
-    });
-    once(crate_name).chain(relative).collect()
-}
-
 pub fn enter_impl_trait<F, R>(cx: &DocContext<'_>, f: F) -> R
 where
     F: FnOnce() -> R,
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index db90bb4524d..98ab957ecbb 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -220,22 +220,22 @@ impl Options {
                 println!("{:>20} - {}", pass.name, pass.description);
             }
             println!("\nDefault passes for rustdoc:");
-            for &name in passes::DEFAULT_PASSES {
-                println!("{:>20}", name);
+            for pass in passes::DEFAULT_PASSES {
+                println!("{:>20}", pass.name);
             }
             println!("\nPasses run with `--document-private-items`:");
-            for &name in passes::DEFAULT_PRIVATE_PASSES {
-                println!("{:>20}", name);
+            for pass in passes::DEFAULT_PRIVATE_PASSES {
+                println!("{:>20}", pass.name);
             }
 
             if nightly_options::is_nightly_build() {
                 println!("\nPasses run with `--show-coverage`:");
-                for &name in passes::DEFAULT_COVERAGE_PASSES {
-                    println!("{:>20}", name);
+                for pass in passes::DEFAULT_COVERAGE_PASSES {
+                    println!("{:>20}", pass.name);
                 }
                 println!("\nPasses run with `--show-coverage --document-private-items`:");
-                for &name in passes::PRIVATE_COVERAGE_PASSES {
-                    println!("{:>20}", name);
+                for pass in passes::PRIVATE_COVERAGE_PASSES {
+                    println!("{:>20}", pass.name);
                 }
             }
 
@@ -378,7 +378,7 @@ impl Options {
                 &matches.opt_strs("html-after-content"),
                 &matches.opt_strs("markdown-before-content"),
                 &matches.opt_strs("markdown-after-content"),
-                &diag, &mut id_map, edition) {
+                &diag, &mut id_map, edition, &None) {
             Some(eh) => eh,
             None => return Err(3),
         };
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 5138e4a23a4..87381f224d0 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -22,12 +22,10 @@ use syntax::json::JsonEmitter;
 use syntax::symbol::sym;
 use errors;
 use errors::emitter::{Emitter, EmitterWriter};
-use parking_lot::ReentrantMutex;
 
 use std::cell::RefCell;
 use std::mem;
 use rustc_data_structures::sync::{self, Lrc};
-use std::sync::Arc;
 use std::rc::Rc;
 
 use crate::config::{Options as RustdocOptions, RenderOptions};
@@ -46,16 +44,14 @@ pub struct DocContext<'tcx> {
 
     pub tcx: TyCtxt<'tcx>,
     pub resolver: Rc<RefCell<interface::BoxedResolver>>,
-    /// The stack of module NodeIds up till this point
-    pub crate_name: Option<String>,
     pub cstore: Lrc<CStore>,
     /// Later on moved into `html::render::CACHE_KEY`
     pub renderinfo: RefCell<RenderInfo>,
     /// Later on moved through `clean::Crate` into `html::render::CACHE_KEY`
-    pub external_traits: Arc<ReentrantMutex<RefCell<FxHashMap<DefId, clean::Trait>>>>,
+    pub external_traits: Rc<RefCell<FxHashMap<DefId, clean::Trait>>>,
     /// Used while populating `external_traits` to ensure we don't process the same trait twice at
     /// the same time.
-    pub active_extern_traits: RefCell<Vec<DefId>>,
+    pub active_extern_traits: RefCell<FxHashSet<DefId>>,
     // The current set of type and lifetime substitutions,
     // for expanding type aliases at the HIR level:
 
@@ -72,7 +68,6 @@ pub struct DocContext<'tcx> {
     /// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`.
     // FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set.
     pub generated_synthetics: RefCell<FxHashSet<(Ty<'tcx>, DefId)>>,
-    pub all_traits: Vec<DefId>,
     pub auto_traits: Vec<DefId>,
 }
 
@@ -227,7 +222,7 @@ pub fn new_handler(error_format: ErrorOutputType,
     )
 }
 
-pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOptions, Vec<String>) {
+pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOptions) {
     // Parse, resolve, and typecheck the given crate.
 
     let RustdocOptions {
@@ -332,7 +327,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
         file_loader: None,
         diagnostic_output: DiagnosticOutput::Default,
         stderr: None,
-        crate_name: crate_name.clone(),
+        crate_name,
         lint_caps,
     };
 
@@ -368,11 +363,9 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
             let mut renderinfo = RenderInfo::default();
             renderinfo.access_levels = access_levels;
 
-            let all_traits = tcx.all_traits(LOCAL_CRATE).to_vec();
             let ctxt = DocContext {
                 tcx,
                 resolver,
-                crate_name,
                 cstore: compiler.cstore().clone(),
                 external_traits: Default::default(),
                 active_extern_traits: Default::default(),
@@ -384,10 +377,9 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
                 fake_def_ids: Default::default(),
                 all_fake_def_ids: Default::default(),
                 generated_synthetics: Default::default(),
-                auto_traits: all_traits.iter().cloned().filter(|trait_def_id| {
+                auto_traits: tcx.all_traits(LOCAL_CRATE).iter().cloned().filter(|trait_def_id| {
                     tcx.trait_is_auto(*trait_def_id)
                 }).collect(),
-                all_traits,
             };
             debug!("crate: {:?}", tcx.hir().krate());
 
@@ -432,8 +424,8 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
                         },
                         _ => continue,
                     };
-                    for p in value.as_str().split_whitespace() {
-                        sink.push(p.to_string());
+                    for name in value.as_str().split_whitespace() {
+                        sink.push(name.to_string());
                     }
                 }
 
@@ -444,25 +436,26 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
                 }
             }
 
-            let mut passes: Vec<String> =
-                passes::defaults(default_passes).iter().map(|p| p.to_string()).collect();
-            passes.extend(manual_passes);
+            let passes = passes::defaults(default_passes).iter().chain(manual_passes.into_iter()
+                .flat_map(|name| {
+                    if let Some(pass) = passes::find_pass(&name) {
+                        Some(pass)
+                    } else {
+                        error!("unknown pass {}, skipping", name);
+                        None
+                    }
+                }));
 
             info!("Executing passes");
 
-            for pass_name in &passes {
-                match passes::find_pass(pass_name).map(|p| p.pass) {
-                    Some(pass) => {
-                        debug!("running pass {}", pass_name);
-                        krate = pass(krate, &ctxt);
-                    }
-                    None => error!("unknown pass {}, skipping", *pass_name),
-                }
+            for pass in passes {
+                debug!("running pass {}", pass.name);
+                krate = (pass.pass)(krate, &ctxt);
             }
 
             ctxt.sess().abort_if_errors();
 
-            (krate, ctxt.renderinfo.into_inner(), render_options, passes)
+            (krate, ctxt.renderinfo.into_inner(), render_options)
         })
     })
 }
diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs
index 90dcf1be76c..6e453561f6d 100644
--- a/src/librustdoc/doctree.rs
+++ b/src/librustdoc/doctree.rs
@@ -13,7 +13,7 @@ use rustc::hir::ptr::P;
 
 pub struct Module<'hir> {
     pub name: Option<Name>,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub where_outer: Span,
     pub where_inner: Span,
     pub extern_crates: Vec<ExternCrate<'hir>>,
@@ -41,7 +41,7 @@ pub struct Module<'hir> {
 impl Module<'hir> {
     pub fn new(
         name: Option<Name>,
-        attrs: &'hir hir::HirVec<ast::Attribute>,
+        attrs: &'hir [ast::Attribute],
         vis: &'hir hir::Visibility,
     ) -> Module<'hir> {
         Module {
@@ -89,7 +89,7 @@ pub struct Struct<'hir> {
     pub struct_type: StructType,
     pub name: Name,
     pub generics: &'hir hir::Generics,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub fields: &'hir [hir::StructField],
     pub whence: Span,
 }
@@ -100,7 +100,7 @@ pub struct Union<'hir> {
     pub struct_type: StructType,
     pub name: Name,
     pub generics: &'hir hir::Generics,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub fields: &'hir [hir::StructField],
     pub whence: Span,
 }
@@ -109,7 +109,7 @@ pub struct Enum<'hir> {
     pub vis: &'hir hir::Visibility,
     pub variants: Vec<Variant<'hir>>,
     pub generics: &'hir hir::Generics,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub id: hir::HirId,
     pub whence: Span,
     pub name: Name,
@@ -118,14 +118,14 @@ pub struct Enum<'hir> {
 pub struct Variant<'hir> {
     pub name: Name,
     pub id: hir::HirId,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub def: &'hir hir::VariantData,
     pub whence: Span,
 }
 
 pub struct Function<'hir> {
     pub decl: &'hir hir::FnDecl,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub id: hir::HirId,
     pub name: Name,
     pub vis: &'hir hir::Visibility,
@@ -140,7 +140,7 @@ pub struct Typedef<'hir> {
     pub gen: &'hir hir::Generics,
     pub name: Name,
     pub id: hir::HirId,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub whence: Span,
     pub vis: &'hir hir::Visibility,
 }
@@ -149,7 +149,7 @@ pub struct OpaqueTy<'hir> {
     pub opaque_ty: &'hir hir::OpaqueTy,
     pub name: Name,
     pub id: hir::HirId,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub whence: Span,
     pub vis: &'hir hir::Visibility,
 }
@@ -160,7 +160,7 @@ pub struct Static<'hir> {
     pub mutability: hir::Mutability,
     pub expr: hir::BodyId,
     pub name: Name,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub vis: &'hir hir::Visibility,
     pub id: hir::HirId,
     pub whence: Span,
@@ -170,7 +170,7 @@ pub struct Constant<'hir> {
     pub type_: &'hir P<hir::Ty>,
     pub expr: hir::BodyId,
     pub name: Name,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub vis: &'hir hir::Visibility,
     pub id: hir::HirId,
     pub whence: Span,
@@ -182,8 +182,8 @@ pub struct Trait<'hir> {
     pub name: Name,
     pub items: Vec<&'hir hir::TraitItem>,
     pub generics: &'hir hir::Generics,
-    pub bounds: &'hir hir::HirVec<hir::GenericBound>,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub bounds: &'hir [hir::GenericBound],
+    pub attrs: &'hir [ast::Attribute],
     pub id: hir::HirId,
     pub whence: Span,
     pub vis: &'hir hir::Visibility,
@@ -192,8 +192,8 @@ pub struct Trait<'hir> {
 pub struct TraitAlias<'hir> {
     pub name: Name,
     pub generics: &'hir hir::Generics,
-    pub bounds: &'hir hir::HirVec<hir::GenericBound>,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub bounds: &'hir [hir::GenericBound],
+    pub attrs: &'hir [ast::Attribute],
     pub id: hir::HirId,
     pub whence: Span,
     pub vis: &'hir hir::Visibility,
@@ -208,7 +208,7 @@ pub struct Impl<'hir> {
     pub trait_: &'hir Option<hir::TraitRef>,
     pub for_: &'hir P<hir::Ty>,
     pub items: Vec<&'hir hir::ImplItem>,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub whence: Span,
     pub vis: &'hir hir::Visibility,
     pub id: hir::HirId,
@@ -219,7 +219,7 @@ pub struct ForeignItem<'hir> {
     pub id: hir::HirId,
     pub name: Name,
     pub kind: &'hir hir::ForeignItemKind,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub whence: Span,
 }
 
@@ -229,7 +229,7 @@ pub struct Macro<'hir> {
     pub name: Name,
     pub hid: hir::HirId,
     pub def_id: hir::def_id::DefId,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub whence: Span,
     pub matchers: hir::HirVec<Span>,
     pub imported_from: Option<Name>,
@@ -240,7 +240,7 @@ pub struct ExternCrate<'hir> {
     pub cnum: CrateNum,
     pub path: Option<String>,
     pub vis: &'hir hir::Visibility,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub whence: Span,
 }
 
@@ -248,7 +248,7 @@ pub struct Import<'hir> {
     pub name: Name,
     pub id: hir::HirId,
     pub vis: &'hir hir::Visibility,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub path: &'hir hir::Path,
     pub glob: bool,
     pub whence: Span,
@@ -259,7 +259,7 @@ pub struct ProcMacro<'hir> {
     pub id: hir::HirId,
     pub kind: MacroKind,
     pub helpers: Vec<Name>,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub whence: Span,
 }
 
diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs
index d604ba11d41..56f1191feed 100644
--- a/src/librustdoc/externalfiles.rs
+++ b/src/librustdoc/externalfiles.rs
@@ -4,9 +4,7 @@ use std::str;
 use errors;
 use crate::syntax::feature_gate::UnstableFeatures;
 use crate::syntax::edition::Edition;
-use crate::html::markdown::{IdMap, ErrorCodes, Markdown};
-
-use std::cell::RefCell;
+use crate::html::markdown::{IdMap, ErrorCodes, Markdown, Playground};
 
 #[derive(Clone, Debug)]
 pub struct ExternalHtml {
@@ -24,37 +22,23 @@ pub struct ExternalHtml {
 impl ExternalHtml {
     pub fn load(in_header: &[String], before_content: &[String], after_content: &[String],
                 md_before_content: &[String], md_after_content: &[String], diag: &errors::Handler,
-                id_map: &mut IdMap, edition: Edition)
+                id_map: &mut IdMap, edition: Edition, playground: &Option<Playground>)
             -> Option<ExternalHtml> {
         let codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
-        load_external_files(in_header, diag)
-            .and_then(|ih|
-                load_external_files(before_content, diag)
-                    .map(|bc| (ih, bc))
-            )
-            .and_then(|(ih, bc)|
-                load_external_files(md_before_content, diag)
-                    .map(|m_bc| (ih,
-                            format!("{}{}", bc, Markdown(&m_bc, &[], RefCell::new(id_map),
-                                    codes, edition))))
-            )
-            .and_then(|(ih, bc)|
-                load_external_files(after_content, diag)
-                    .map(|ac| (ih, bc, ac))
-            )
-            .and_then(|(ih, bc, ac)|
-                load_external_files(md_after_content, diag)
-                    .map(|m_ac| (ih, bc,
-                            format!("{}{}", ac, Markdown(&m_ac, &[], RefCell::new(id_map),
-                                    codes, edition))))
-            )
-            .map(|(ih, bc, ac)|
-                ExternalHtml {
-                    in_header: ih,
-                    before_content: bc,
-                    after_content: ac,
-                }
-            )
+        let ih = load_external_files(in_header, diag)?;
+        let bc = load_external_files(before_content, diag)?;
+        let m_bc = load_external_files(md_before_content, diag)?;
+        let bc = format!("{}{}", bc, Markdown(&m_bc, &[], id_map,
+                                    codes, edition, playground).to_string());
+        let ac = load_external_files(after_content, diag)?;
+        let m_ac = load_external_files(md_after_content, diag)?;
+        let ac = format!("{}{}", ac, Markdown(&m_ac, &[], id_map,
+                                    codes, edition, playground).to_string());
+        Some(ExternalHtml {
+            in_header: ih,
+            before_content: bc,
+            after_content: ac,
+        })
     }
 }
 
diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs
index cfa22bc27b7..5482239c7ce 100644
--- a/src/librustdoc/fold.rs
+++ b/src/librustdoc/fold.rs
@@ -105,12 +105,12 @@ pub trait DocFolder : Sized {
         c.module = c.module.take().and_then(|module| self.fold_item(module));
 
         {
-            let guard = c.external_traits.lock();
-            let traits = guard.replace(Default::default());
-            guard.borrow_mut().extend(traits.into_iter().map(|(k, mut v)| {
+            let mut guard = c.external_traits.borrow_mut();
+            let external_traits = std::mem::replace(&mut *guard, Default::default());
+            *guard = external_traits.into_iter().map(|(k, mut v)| {
                 v.items = v.items.into_iter().filter_map(|i| self.fold_item(i)).collect();
                 (k, v)
-            }));
+            }).collect();
         }
         c
     }
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index ef52ce62875..5a7deb651b0 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -1,9 +1,6 @@
 //! Markdown formatting for rustdoc.
 //!
-//! This module implements markdown formatting through the pulldown-cmark
-//! rust-library. This module exposes all of the
-//! functionality through a unit struct, `Markdown`, which has an implementation
-//! of `fmt::Display`. Example usage:
+//! This module implements markdown formatting through the pulldown-cmark library.
 //!
 //! ```
 //! #![feature(rustc_private)]
@@ -12,12 +9,11 @@
 //!
 //! use syntax::edition::Edition;
 //! use rustdoc::html::markdown::{IdMap, Markdown, ErrorCodes};
-//! use std::cell::RefCell;
 //!
 //! let s = "My *markdown* _text_";
 //! let mut id_map = IdMap::new();
-//! let html = format!("{}", Markdown(s, &[], RefCell::new(&mut id_map),
-//!                                   ErrorCodes::Yes, Edition::Edition2015));
+//! let md = Markdown(s, &[], &mut id_map, ErrorCodes::Yes, Edition::Edition2015, &None);
+//! let html = md.to_string();
 //! // ... something using html
 //! ```
 
@@ -27,7 +23,7 @@ use rustc_data_structures::fx::FxHashMap;
 use std::cell::RefCell;
 use std::collections::VecDeque;
 use std::default::Default;
-use std::fmt::{self, Write};
+use std::fmt::Write;
 use std::borrow::Cow;
 use std::ops::Range;
 use std::str;
@@ -46,29 +42,36 @@ fn opts() -> Options {
     Options::ENABLE_TABLES | Options::ENABLE_FOOTNOTES
 }
 
-/// A tuple struct that has the `fmt::Display` trait implemented.
-/// When formatted, this struct will emit the HTML corresponding to the rendered
-/// version of the contained markdown string.
+/// When `to_string` is called, this struct will emit the HTML corresponding to
+/// the rendered version of the contained markdown string.
 pub struct Markdown<'a>(
     pub &'a str,
     /// A list of link replacements.
     pub &'a [(String, String)],
     /// The current list of used header IDs.
-    pub RefCell<&'a mut IdMap>,
+    pub &'a mut IdMap,
     /// Whether to allow the use of explicit error codes in doctest lang strings.
     pub ErrorCodes,
     /// Default edition to use when parsing doctests (to add a `fn main`).
     pub Edition,
+    pub &'a Option<Playground>,
 );
 /// A tuple struct like `Markdown` that renders the markdown with a table of contents.
 pub struct MarkdownWithToc<'a>(
     pub &'a str,
-    pub RefCell<&'a mut IdMap>,
+    pub &'a mut IdMap,
     pub ErrorCodes,
     pub Edition,
+    pub &'a Option<Playground>,
 );
 /// A tuple struct like `Markdown` that renders the markdown escaping HTML tags.
-pub struct MarkdownHtml<'a>(pub &'a str, pub RefCell<&'a mut IdMap>, pub ErrorCodes, pub Edition);
+pub struct MarkdownHtml<'a>(
+    pub &'a str,
+    pub &'a mut IdMap,
+    pub ErrorCodes,
+    pub Edition,
+    pub &'a Option<Playground>,
+);
 /// A tuple struct like `Markdown` that renders only the first paragraph.
 pub struct MarkdownSummaryLine<'a>(pub &'a str, pub &'a [(String, String)]);
 
@@ -155,30 +158,39 @@ fn slugify(c: char) -> Option<char> {
     }
 }
 
-// Information about the playground if a URL has been specified, containing an
-// optional crate name and the URL.
-thread_local!(pub static PLAYGROUND: RefCell<Option<(Option<String>, String)>> = {
-    RefCell::new(None)
-});
+#[derive(Clone, Debug)]
+pub struct Playground {
+    pub crate_name: Option<String>,
+    pub url: String,
+}
 
 /// Adds syntax highlighting and playground Run buttons to Rust code blocks.
-struct CodeBlocks<'a, I: Iterator<Item = Event<'a>>> {
+struct CodeBlocks<'p, 'a, I: Iterator<Item = Event<'a>>> {
     inner: I,
     check_error_codes: ErrorCodes,
     edition: Edition,
+    // Information about the playground if a URL has been specified, containing an
+    // optional crate name and the URL.
+    playground: &'p Option<Playground>,
 }
 
-impl<'a, I: Iterator<Item = Event<'a>>> CodeBlocks<'a, I> {
-    fn new(iter: I, error_codes: ErrorCodes, edition: Edition) -> Self {
+impl<'p, 'a, I: Iterator<Item = Event<'a>>> CodeBlocks<'p, 'a, I> {
+    fn new(
+        iter: I,
+        error_codes: ErrorCodes,
+        edition: Edition,
+        playground: &'p Option<Playground>,
+    ) -> Self {
         CodeBlocks {
             inner: iter,
             check_error_codes: error_codes,
             edition,
+            playground,
         }
     }
 }
 
-impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
+impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
     type Item = Event<'a>;
 
     fn next(&mut self) -> Option<Self::Item> {
@@ -213,86 +225,86 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
         }
         let lines = origtext.lines().filter_map(|l| map_line(l).for_html());
         let text = lines.collect::<Vec<Cow<'_, str>>>().join("\n");
-        PLAYGROUND.with(|play| {
-            // insert newline to clearly separate it from the
-            // previous block so we can shorten the html output
-            let mut s = String::from("\n");
-            let playground_button = play.borrow().as_ref().and_then(|&(ref krate, ref url)| {
-                if url.is_empty() {
-                    return None;
-                }
-                let test = origtext.lines()
-                    .map(|l| map_line(l).for_code())
-                    .collect::<Vec<Cow<'_, str>>>().join("\n");
-                let krate = krate.as_ref().map(|s| &**s);
-                let (test, _) = test::make_test(&test, krate, false,
-                                           &Default::default(), edition);
-                let channel = if test.contains("#![feature(") {
-                    "&amp;version=nightly"
-                } else {
-                    ""
-                };
-
-                let edition_string = format!("&amp;edition={}", edition);
-
-                // These characters don't need to be escaped in a URI.
-                // FIXME: use a library function for percent encoding.
-                fn dont_escape(c: u8) -> bool {
-                    (b'a' <= c && c <= b'z') ||
-                    (b'A' <= c && c <= b'Z') ||
-                    (b'0' <= c && c <= b'9') ||
-                    c == b'-' || c == b'_' || c == b'.' ||
-                    c == b'~' || c == b'!' || c == b'\'' ||
-                    c == b'(' || c == b')' || c == b'*'
-                }
-                let mut test_escaped = String::new();
-                for b in test.bytes() {
-                    if dont_escape(b) {
-                        test_escaped.push(char::from(b));
-                    } else {
-                        write!(test_escaped, "%{:02X}", b).unwrap();
-                    }
-                }
-                Some(format!(
-                    r#"<a class="test-arrow" target="_blank" href="{}?code={}{}{}">Run</a>"#,
-                    url, test_escaped, channel, edition_string
-                ))
-            });
-
-            let tooltip = if ignore {
-                Some(("This example is not tested".to_owned(), "ignore"))
-            } else if compile_fail {
-                Some(("This example deliberately fails to compile".to_owned(), "compile_fail"))
-            } else if explicit_edition {
-                Some((format!("This code runs with edition {}", edition), "edition"))
+        // insert newline to clearly separate it from the
+        // previous block so we can shorten the html output
+        let mut s = String::from("\n");
+        let playground_button = self.playground.as_ref().and_then(|playground| {
+            let krate = &playground.crate_name;
+            let url = &playground.url;
+            if url.is_empty() {
+                return None;
+            }
+            let test = origtext.lines()
+                .map(|l| map_line(l).for_code())
+                .collect::<Vec<Cow<'_, str>>>().join("\n");
+            let krate = krate.as_ref().map(|s| &**s);
+            let (test, _) = test::make_test(&test, krate, false,
+                                        &Default::default(), edition);
+            let channel = if test.contains("#![feature(") {
+                "&amp;version=nightly"
             } else {
-                None
+                ""
             };
 
-            if let Some((s1, s2)) = tooltip {
-                s.push_str(&highlight::render_with_highlighting(
-                    &text,
-                    Some(&format!("rust-example-rendered{}",
-                                  if ignore { " ignore" }
-                                  else if compile_fail { " compile_fail" }
-                                  else if explicit_edition { " edition " }
-                                  else { "" })),
-                    playground_button.as_ref().map(String::as_str),
-                    Some((s1.as_str(), s2))));
-                Some(Event::Html(s.into()))
-            } else {
-                s.push_str(&highlight::render_with_highlighting(
-                    &text,
-                    Some(&format!("rust-example-rendered{}",
-                                  if ignore { " ignore" }
-                                  else if compile_fail { " compile_fail" }
-                                  else if explicit_edition { " edition " }
-                                  else { "" })),
-                    playground_button.as_ref().map(String::as_str),
-                    None));
-                Some(Event::Html(s.into()))
+            let edition_string = format!("&amp;edition={}", edition);
+
+            // These characters don't need to be escaped in a URI.
+            // FIXME: use a library function for percent encoding.
+            fn dont_escape(c: u8) -> bool {
+                (b'a' <= c && c <= b'z') ||
+                (b'A' <= c && c <= b'Z') ||
+                (b'0' <= c && c <= b'9') ||
+                c == b'-' || c == b'_' || c == b'.' ||
+                c == b'~' || c == b'!' || c == b'\'' ||
+                c == b'(' || c == b')' || c == b'*'
+            }
+            let mut test_escaped = String::new();
+            for b in test.bytes() {
+                if dont_escape(b) {
+                    test_escaped.push(char::from(b));
+                } else {
+                    write!(test_escaped, "%{:02X}", b).unwrap();
+                }
             }
-        })
+            Some(format!(
+                r#"<a class="test-arrow" target="_blank" href="{}?code={}{}{}">Run</a>"#,
+                url, test_escaped, channel, edition_string
+            ))
+        });
+
+        let tooltip = if ignore {
+            Some(("This example is not tested".to_owned(), "ignore"))
+        } else if compile_fail {
+            Some(("This example deliberately fails to compile".to_owned(), "compile_fail"))
+        } else if explicit_edition {
+            Some((format!("This code runs with edition {}", edition), "edition"))
+        } else {
+            None
+        };
+
+        if let Some((s1, s2)) = tooltip {
+            s.push_str(&highlight::render_with_highlighting(
+                &text,
+                Some(&format!("rust-example-rendered{}",
+                                if ignore { " ignore" }
+                                else if compile_fail { " compile_fail" }
+                                else if explicit_edition { " edition " }
+                                else { "" })),
+                playground_button.as_ref().map(String::as_str),
+                Some((s1.as_str(), s2))));
+            Some(Event::Html(s.into()))
+        } else {
+            s.push_str(&highlight::render_with_highlighting(
+                &text,
+                Some(&format!("rust-example-rendered{}",
+                                if ignore { " ignore" }
+                                else if compile_fail { " compile_fail" }
+                                else if explicit_edition { " edition " }
+                                else { "" })),
+                playground_button.as_ref().map(String::as_str),
+                None));
+            Some(Event::Html(s.into()))
+        }
     }
 }
 
@@ -674,13 +686,12 @@ impl LangString {
     }
 }
 
-impl<'a> fmt::Display for Markdown<'a> {
-    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Markdown(md, links, ref ids, codes, edition) = *self;
-        let mut ids = ids.borrow_mut();
+impl Markdown<'_> {
+    pub fn to_string(self) -> String {
+        let Markdown(md, links, mut ids, codes, edition, playground) = self;
 
         // This is actually common enough to special-case
-        if md.is_empty() { return Ok(()) }
+        if md.is_empty() { return String::new(); }
         let replacer = |_: &str, s: &str| {
             if let Some(&(_, ref replace)) = links.into_iter().find(|link| &*link.0 == s) {
                 Some((replace.clone(), s.to_owned()))
@@ -695,18 +706,17 @@ impl<'a> fmt::Display for Markdown<'a> {
 
         let p = HeadingLinks::new(p, None, &mut ids);
         let p = LinkReplacer::new(p, links);
-        let p = CodeBlocks::new(p, codes, edition);
+        let p = CodeBlocks::new(p, codes, edition, playground);
         let p = Footnotes::new(p);
         html::push_html(&mut s, p);
 
-        fmt.write_str(&s)
+        s
     }
 }
 
-impl<'a> fmt::Display for MarkdownWithToc<'a> {
-    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let MarkdownWithToc(md, ref ids, codes, edition) = *self;
-        let mut ids = ids.borrow_mut();
+impl MarkdownWithToc<'_> {
+    pub fn to_string(self) -> String {
+        let MarkdownWithToc(md, mut ids, codes, edition, playground) = self;
 
         let p = Parser::new_ext(md, opts());
 
@@ -716,24 +726,21 @@ impl<'a> fmt::Display for MarkdownWithToc<'a> {
 
         {
             let p = HeadingLinks::new(p, Some(&mut toc), &mut ids);
-            let p = CodeBlocks::new(p, codes, edition);
+            let p = CodeBlocks::new(p, codes, edition, playground);
             let p = Footnotes::new(p);
             html::push_html(&mut s, p);
         }
 
-        write!(fmt, "<nav id=\"TOC\">{}</nav>", toc.into_toc())?;
-
-        fmt.write_str(&s)
+        format!("<nav id=\"TOC\">{}</nav>{}", toc.into_toc(), s)
     }
 }
 
-impl<'a> fmt::Display for MarkdownHtml<'a> {
-    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let MarkdownHtml(md, ref ids, codes, edition) = *self;
-        let mut ids = ids.borrow_mut();
+impl MarkdownHtml<'_> {
+    pub fn to_string(self) -> String {
+        let MarkdownHtml(md, mut ids, codes, edition, playground) = self;
 
         // This is actually common enough to special-case
-        if md.is_empty() { return Ok(()) }
+        if md.is_empty() { return String::new(); }
         let p = Parser::new_ext(md, opts());
 
         // Treat inline HTML as plain text.
@@ -745,19 +752,19 @@ impl<'a> fmt::Display for MarkdownHtml<'a> {
         let mut s = String::with_capacity(md.len() * 3 / 2);
 
         let p = HeadingLinks::new(p, None, &mut ids);
-        let p = CodeBlocks::new(p, codes, edition);
+        let p = CodeBlocks::new(p, codes, edition, playground);
         let p = Footnotes::new(p);
         html::push_html(&mut s, p);
 
-        fmt.write_str(&s)
+        s
     }
 }
 
-impl<'a> fmt::Display for MarkdownSummaryLine<'a> {
-    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let MarkdownSummaryLine(md, links) = *self;
+impl MarkdownSummaryLine<'_> {
+    pub fn to_string(self) -> String {
+        let MarkdownSummaryLine(md, links) = self;
         // This is actually common enough to special-case
-        if md.is_empty() { return Ok(()) }
+        if md.is_empty() { return String::new(); }
 
         let replacer = |_: &str, s: &str| {
             if let Some(&(_, ref replace)) = links.into_iter().find(|link| &*link.0 == s) {
@@ -773,7 +780,7 @@ impl<'a> fmt::Display for MarkdownSummaryLine<'a> {
 
         html::push_html(&mut s, LinkReplacer::new(SummaryLine::new(p), links));
 
-        fmt.write_str(&s)
+        s
     }
 }
 
diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs
index 681f363544a..a95c29038d4 100644
--- a/src/librustdoc/html/markdown/tests.rs
+++ b/src/librustdoc/html/markdown/tests.rs
@@ -73,8 +73,8 @@ fn test_lang_string_parse() {
 fn test_header() {
     fn t(input: &str, expect: &str) {
         let mut map = IdMap::new();
-        let output = Markdown(input, &[], RefCell::new(&mut map),
-                              ErrorCodes::Yes, DEFAULT_EDITION).to_string();
+        let output = Markdown(
+            input, &[], &mut map, ErrorCodes::Yes, DEFAULT_EDITION, &None).to_string();
         assert_eq!(output, expect, "original: {}", input);
     }
 
@@ -96,8 +96,8 @@ fn test_header() {
 fn test_header_ids_multiple_blocks() {
     let mut map = IdMap::new();
     fn t(map: &mut IdMap, input: &str, expect: &str) {
-        let output = Markdown(input, &[], RefCell::new(map),
-                              ErrorCodes::Yes, DEFAULT_EDITION).to_string();
+        let output = Markdown(input, &[], map,
+                              ErrorCodes::Yes, DEFAULT_EDITION, &None).to_string();
         assert_eq!(output, expect, "original: {}", input);
     }
 
@@ -134,8 +134,8 @@ fn test_plain_summary_line() {
 fn test_markdown_html_escape() {
     fn t(input: &str, expect: &str) {
         let mut idmap = IdMap::new();
-        let output = MarkdownHtml(input, RefCell::new(&mut idmap),
-                                  ErrorCodes::Yes, DEFAULT_EDITION).to_string();
+        let output = MarkdownHtml(input, &mut idmap,
+                                  ErrorCodes::Yes, DEFAULT_EDITION, &None).to_string();
         assert_eq!(output, expect, "original: {}", input);
     }
 
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index b1cee018205..ea97cea9428 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -170,6 +170,7 @@ struct Context {
     /// The map used to ensure all generated 'id=' attributes are unique.
     id_map: Rc<RefCell<IdMap>>,
     pub shared: Arc<SharedContext>,
+    playground: Option<markdown::Playground>,
 }
 
 struct SharedContext {
@@ -185,8 +186,8 @@ struct SharedContext {
     pub include_sources: bool,
     /// The local file sources we've emitted and their respective url-paths.
     pub local_sources: FxHashMap<PathBuf, String>,
-    /// All the passes that were run on this crate.
-    pub passes: FxHashSet<String>,
+    /// Whether the collapsed pass ran
+    pub collapsed: bool,
     /// The base-URL of the issue tracker for when an item has been tagged with
     /// an issue number.
     pub issue_tracker_base_url: Option<String>,
@@ -229,15 +230,10 @@ impl SharedContext {
 }
 
 impl SharedContext {
-    /// Returns `true` if the `collapse-docs` pass was run on this crate.
-    pub fn was_collapsed(&self) -> bool {
-        self.passes.contains("collapse-docs")
-    }
-
     /// Based on whether the `collapse-docs` pass was run, return either the `doc_value` or the
     /// `collapsed_doc_value` of the given item.
     pub fn maybe_collapsed_doc_value<'a>(&self, item: &'a clean::Item) -> Option<Cow<'a, str>> {
-        if self.was_collapsed() {
+        if self.collapsed {
             item.collapsed_doc_value().map(|s| s.into())
         } else {
             item.doc_value().map(|s| s.into())
@@ -526,7 +522,6 @@ pub fn initial_ids() -> Vec<String> {
 /// Generates the documentation for `crate` into the directory `dst`
 pub fn run(mut krate: clean::Crate,
            options: RenderOptions,
-           passes: FxHashSet<String>,
            renderinfo: RenderInfo,
            diag: &errors::Handler,
            edition: Edition) -> Result<(), Error> {
@@ -557,8 +552,8 @@ pub fn run(mut krate: clean::Crate,
     };
     let mut errors = Arc::new(ErrorStorage::new());
     let mut scx = SharedContext {
+        collapsed: krate.collapsed,
         src_root,
-        passes,
         include_sources: true,
         local_sources: Default::default(),
         issue_tracker_base_url: None,
@@ -580,9 +575,11 @@ pub fn run(mut krate: clean::Crate,
     };
 
     // If user passed in `--playground-url` arg, we fill in crate name here
+    let mut playground = None;
     if let Some(url) = playground_url {
-        markdown::PLAYGROUND.with(|slot| {
-            *slot.borrow_mut() = Some((Some(krate.name.clone()), url));
+        playground = Some(markdown::Playground {
+            crate_name: Some(krate.name.clone()),
+            url,
         });
     }
 
@@ -598,9 +595,9 @@ pub fn run(mut krate: clean::Crate,
                     scx.layout.logo = s.to_string();
                 }
                 (sym::html_playground_url, Some(s)) => {
-                    markdown::PLAYGROUND.with(|slot| {
-                        let name = krate.name.clone();
-                        *slot.borrow_mut() = Some((Some(name), s.to_string()));
+                    playground = Some(markdown::Playground {
+                        crate_name: Some(krate.name.clone()),
+                        url: s.to_string(),
                     });
                 }
                 (sym::issue_tracker_base_url, Some(s)) => {
@@ -624,6 +621,7 @@ pub fn run(mut krate: clean::Crate,
         edition,
         id_map: Rc::new(RefCell::new(id_map)),
         shared: Arc::new(scx),
+        playground,
     };
 
     // Crawl the crate to build various caches used for the output
@@ -659,7 +657,7 @@ pub fn run(mut krate: clean::Crate,
         crate_version: krate.version.take(),
         orphan_impl_items: Vec::new(),
         orphan_trait_impls: Vec::new(),
-        traits: krate.external_traits.lock().replace(Default::default()),
+        traits: krate.external_traits.replace(Default::default()),
         deref_trait_did,
         deref_mut_trait_did,
         owned_box_did,
@@ -2597,8 +2595,8 @@ fn render_markdown(w: &mut fmt::Formatter<'_>,
     write!(w, "<div class='docblock{}'>{}{}</div>",
            if is_hidden { " hidden" } else { "" },
            prefix,
-           Markdown(md_text, &links, RefCell::new(&mut ids),
-           cx.codes, cx.edition))
+           Markdown(md_text, &links, &mut ids,
+           cx.codes, cx.edition, &cx.playground).to_string())
 }
 
 fn document_short(
@@ -2868,7 +2866,7 @@ fn item_module(w: &mut fmt::Formatter<'_>, cx: &Context,
                        </tr>",
                        name = *myitem.name.as_ref().unwrap(),
                        stab_tags = stability_tags(myitem),
-                       docs = MarkdownSummaryLine(doc_value, &myitem.links()),
+                       docs = MarkdownSummaryLine(doc_value, &myitem.links()).to_string(),
                        class = myitem.type_(),
                        add = add,
                        stab = stab.unwrap_or_else(|| String::new()),
@@ -2963,8 +2961,8 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> {
 
         if let Some(note) = note {
             let mut ids = cx.id_map.borrow_mut();
-            let html = MarkdownHtml(&note, RefCell::new(&mut ids), error_codes, cx.edition);
-            message.push_str(&format!(": {}", html));
+            let html = MarkdownHtml(&note, &mut ids, error_codes, cx.edition, &cx.playground);
+            message.push_str(&format!(": {}", html.to_string()));
         }
         stability.push(format!("<div class='stab deprecated'>{}</div>", message));
     }
@@ -3012,7 +3010,13 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> {
             message = format!(
                 "<details><summary>{}</summary>{}</details>",
                 message,
-                MarkdownHtml(&unstable_reason, RefCell::new(&mut ids), error_codes, cx.edition)
+                MarkdownHtml(
+                    &unstable_reason,
+                    &mut ids,
+                    error_codes,
+                    cx.edition,
+                    &cx.playground,
+                ).to_string()
             );
         }
 
@@ -4242,8 +4246,8 @@ fn render_impl(w: &mut fmt::Formatter<'_>, cx: &Context, i: &Impl, link: AssocIt
         if let Some(ref dox) = cx.shared.maybe_collapsed_doc_value(&i.impl_item) {
             let mut ids = cx.id_map.borrow_mut();
             write!(w, "<div class='docblock'>{}</div>",
-                   Markdown(&*dox, &i.impl_item.links(), RefCell::new(&mut ids),
-                            cx.codes, cx.edition))?;
+                   Markdown(&*dox, &i.impl_item.links(), &mut ids,
+                            cx.codes, cx.edition, &cx.playground).to_string())?;
         }
     }
 
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 799b5d923ae..e30b35937db 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -44,7 +44,6 @@ use std::default::Default;
 use std::env;
 use std::panic;
 use std::process;
-use std::sync::mpsc::channel;
 
 use rustc::session::{early_warn, early_error};
 use rustc::session::config::{ErrorOutputType, RustcOptGroup};
@@ -80,7 +79,6 @@ struct Output {
     krate: clean::Crate,
     renderinfo: html::render::RenderInfo,
     renderopts: config::RenderOptions,
-    passes: Vec<String>,
 }
 
 pub fn main() {
@@ -419,14 +417,13 @@ fn main_options(options: config::Options) -> i32 {
             return rustc_driver::EXIT_SUCCESS;
         }
 
-        let Output { krate, passes, renderinfo, renderopts } = out;
+        let Output { krate, renderinfo, renderopts } = out;
         info!("going to format");
         let (error_format, treat_err_as_bug, ui_testing, edition) = diag_opts;
         let diag = core::new_handler(error_format, None, treat_err_as_bug, ui_testing);
         match html::render::run(
             krate,
             renderopts,
-            passes.into_iter().collect(),
             renderinfo,
             &diag,
             edition,
@@ -454,12 +451,10 @@ where R: 'static + Send,
     // First, parse the crate and extract all relevant information.
     info!("starting to run rustc");
 
-    let (tx, rx) = channel();
-
     let result = rustc_driver::report_ices_to_stderr_if_any(move || {
         let crate_name = options.crate_name.clone();
         let crate_version = options.crate_version.clone();
-        let (mut krate, renderinfo, renderopts, passes) = core::run_core(options);
+        let (mut krate, renderinfo, renderopts) = core::run_core(options);
 
         info!("finished with rustc");
 
@@ -469,16 +464,15 @@ where R: 'static + Send,
 
         krate.version = crate_version;
 
-        tx.send(f(Output {
+        f(Output {
             krate: krate,
             renderinfo: renderinfo,
             renderopts,
-            passes: passes
-        })).unwrap();
+        })
     });
 
     match result {
-        Ok(()) => rx.recv().unwrap(),
+        Ok(output) => output,
         Err(_) => panic::resume_unwind(Box::new(errors::FatalErrorMarker)),
     }
 }
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index 50a647f244d..eaaae3261c7 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -1,7 +1,6 @@
 use std::fs::File;
 use std::io::prelude::*;
 use std::path::PathBuf;
-use std::cell::RefCell;
 
 use errors;
 use testing;
@@ -60,9 +59,10 @@ pub fn render(
     };
     let playground_url = options.markdown_playground_url
                             .or(options.playground_url);
-    if let Some(playground) = playground_url {
-        markdown::PLAYGROUND.with(|s| { *s.borrow_mut() = Some((None, playground)); });
-    }
+    let playground = playground_url.map(|url| markdown::Playground {
+        crate_name: None,
+        url,
+    });
 
     let mut out = match File::create(&output) {
         Err(e) => {
@@ -82,9 +82,9 @@ pub fn render(
     let mut ids = IdMap::new();
     let error_codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
     let text = if !options.markdown_no_toc {
-        MarkdownWithToc(text, RefCell::new(&mut ids), error_codes, edition).to_string()
+        MarkdownWithToc(text, &mut ids, error_codes, edition, &playground).to_string()
     } else {
-        Markdown(text, &[], RefCell::new(&mut ids), error_codes, edition).to_string()
+        Markdown(text, &[], &mut ids, error_codes, edition, &playground).to_string()
     };
 
     let err = write!(
diff --git a/src/librustdoc/passes/collapse_docs.rs b/src/librustdoc/passes/collapse_docs.rs
index 144ff226c42..31288345ce5 100644
--- a/src/librustdoc/passes/collapse_docs.rs
+++ b/src/librustdoc/passes/collapse_docs.rs
@@ -30,7 +30,9 @@ impl DocFragment {
 }
 
 pub fn collapse_docs(krate: clean::Crate, _: &DocContext<'_>) -> clean::Crate {
-    Collapser.fold_crate(krate)
+    let mut krate = Collapser.fold_crate(krate);
+    krate.collapsed = true;
+    krate
 }
 
 struct Collapser;
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index cd488b9df78..86e4e9fd956 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -4,7 +4,7 @@ use crate::fold::DocFolder;
 use super::Pass;
 
 use rustc::util::nodemap::FxHashSet;
-use rustc::hir::def_id::DefId;
+use rustc::hir::def_id::{LOCAL_CRATE, DefId};
 use syntax::symbol::sym;
 
 pub const COLLECT_TRAIT_IMPLS: Pass = Pass {
@@ -116,7 +116,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
 
     // `tcx.crates()` doesn't include the local crate, and `tcx.all_trait_implementations`
     // doesn't work with it anyway, so pull them from the HIR map instead
-    for &trait_did in cx.all_traits.iter() {
+    for &trait_did in cx.tcx.all_traits(LOCAL_CRATE).iter() {
         for &impl_node in cx.tcx.hir().trait_impls(trait_did) {
             let impl_did = cx.tcx.hir().local_def_id(impl_node);
             inline::build_impl(cx, impl_did, None, &mut new_items);
diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs
index 4e10b46bc8a..641a6df2214 100644
--- a/src/librustdoc/passes/mod.rs
+++ b/src/librustdoc/passes/mod.rs
@@ -57,8 +57,9 @@ pub struct Pass {
     pub description: &'static str,
 }
 
+
 /// The full list of passes.
-pub const PASSES: &'static [Pass] = &[
+pub const PASSES: &[Pass] = &[
     CHECK_PRIVATE_ITEMS_DOC_TESTS,
     STRIP_HIDDEN,
     UNINDENT_COMMENTS,
@@ -73,43 +74,43 @@ pub const PASSES: &'static [Pass] = &[
 ];
 
 /// The list of passes run by default.
-pub const DEFAULT_PASSES: &[&str] = &[
-    "collect-trait-impls",
-    "collapse-docs",
-    "unindent-comments",
-    "check-private-items-doc-tests",
-    "strip-hidden",
-    "strip-private",
-    "collect-intra-doc-links",
-    "check-code-block-syntax",
-    "propagate-doc-cfg",
+pub const DEFAULT_PASSES: &[Pass] = &[
+    COLLECT_TRAIT_IMPLS,
+    COLLAPSE_DOCS,
+    UNINDENT_COMMENTS,
+    CHECK_PRIVATE_ITEMS_DOC_TESTS,
+    STRIP_HIDDEN,
+    STRIP_PRIVATE,
+    COLLECT_INTRA_DOC_LINKS,
+    CHECK_CODE_BLOCK_SYNTAX,
+    PROPAGATE_DOC_CFG,
 ];
 
 /// The list of default passes run with `--document-private-items` is passed to rustdoc.
-pub const DEFAULT_PRIVATE_PASSES: &[&str] = &[
-    "collect-trait-impls",
-    "collapse-docs",
-    "unindent-comments",
-    "check-private-items-doc-tests",
-    "strip-priv-imports",
-    "collect-intra-doc-links",
-    "check-code-block-syntax",
-    "propagate-doc-cfg",
+pub const DEFAULT_PRIVATE_PASSES: &[Pass] = &[
+    COLLECT_TRAIT_IMPLS,
+    COLLAPSE_DOCS,
+    UNINDENT_COMMENTS,
+    CHECK_PRIVATE_ITEMS_DOC_TESTS,
+    STRIP_PRIV_IMPORTS,
+    COLLECT_INTRA_DOC_LINKS,
+    CHECK_CODE_BLOCK_SYNTAX,
+    PROPAGATE_DOC_CFG,
 ];
 
 /// The list of default passes run when `--doc-coverage` is passed to rustdoc.
-pub const DEFAULT_COVERAGE_PASSES: &'static [&'static str] = &[
-    "collect-trait-impls",
-    "strip-hidden",
-    "strip-private",
-    "calculate-doc-coverage",
+pub const DEFAULT_COVERAGE_PASSES: &[Pass] = &[
+    COLLECT_TRAIT_IMPLS,
+    STRIP_HIDDEN,
+    STRIP_PRIVATE,
+    CALCULATE_DOC_COVERAGE,
 ];
 
 /// The list of default passes run when `--doc-coverage --document-private-items` is passed to
 /// rustdoc.
-pub const PRIVATE_COVERAGE_PASSES: &'static [&'static str] = &[
-    "collect-trait-impls",
-    "calculate-doc-coverage",
+pub const PRIVATE_COVERAGE_PASSES: &[Pass] = &[
+    COLLECT_TRAIT_IMPLS,
+    CALCULATE_DOC_COVERAGE,
 ];
 
 /// A shorthand way to refer to which set of passes to use, based on the presence of
@@ -124,7 +125,7 @@ pub enum DefaultPassOption {
 }
 
 /// Returns the given default set of passes.
-pub fn defaults(default_set: DefaultPassOption) -> &'static [&'static str] {
+pub fn defaults(default_set: DefaultPassOption) -> &'static [Pass] {
     match default_set {
         DefaultPassOption::Default => DEFAULT_PASSES,
         DefaultPassOption::Private => DEFAULT_PRIVATE_PASSES,
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index f01b9eeb30f..35b6d9972da 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -6,6 +6,7 @@ use rustc::hir::def::{Res, DefKind};
 use rustc::hir::def_id::{DefId, LOCAL_CRATE};
 use rustc::middle::privacy::AccessLevel;
 use rustc::util::nodemap::{FxHashSet, FxHashMap};
+use rustc::ty::TyCtxt;
 use syntax::ast;
 use syntax::ext::base::MacroKind;
 use syntax::source_map::Spanned;
@@ -15,9 +16,26 @@ use syntax_pos::{self, Span};
 use std::mem;
 
 use crate::core;
-use crate::clean::{self, AttributesExt, NestedAttributesExt, def_id_to_path};
+use crate::clean::{self, AttributesExt, NestedAttributesExt};
 use crate::doctree::*;
 
+// FIXME: Should this be replaced with tcx.def_path_str?
+fn def_id_to_path(
+    tcx: TyCtxt<'_>,
+    did: DefId,
+) -> Vec<String> {
+    let crate_name = tcx.crate_name(did.krate).to_string();
+    let relative = tcx.def_path(did).data.into_iter().filter_map(|elem| {
+        // extern blocks have an empty name
+        let s = elem.data.to_string();
+        if !s.is_empty() {
+            Some(s)
+        } else {
+            None
+        }
+    });
+    std::iter::once(crate_name).chain(relative).collect()
+}
 
 // Also, is there some reason that this doesn't use the 'visit'
 // framework from syntax?.
@@ -48,12 +66,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
     }
 
     fn store_path(&mut self, did: DefId) {
-        // We can't use the entry API, as that keeps the mutable borrow of `self` active
-        // when we try to use `cx`.
-        if self.exact_paths.get(&did).is_none() {
-            let path = def_id_to_path(self.cx, did, self.cx.crate_name.clone());
-            self.exact_paths.insert(did, path);
-        }
+        let tcx = self.cx.tcx;
+        self.exact_paths.entry(did).or_insert_with(|| def_id_to_path(tcx, did));
     }
 
     pub fn visit(mut self, krate: &'tcx hir::Crate) -> Module<'tcx> {
diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs
index 6e111f24236..512839a12c0 100644
--- a/src/libstd/ffi/c_str.rs
+++ b/src/libstd/ffi/c_str.rs
@@ -195,6 +195,12 @@ pub struct CString {
 /// [`from_ptr`]: #method.from_ptr
 #[derive(Hash)]
 #[stable(feature = "rust1", since = "1.0.0")]
+// FIXME:
+// `fn from` in `impl From<&CStr> for Box<CStr>` current implementation relies
+// on `CStr` being layout-compatible with `[u8]`.
+// When attribute privacy is implemented, `CStr` should be annotated as `#[repr(transparent)]`.
+// Anyway, `CStr` representation and layout are considered implementation detail, are
+// not documented and must not be relied upon.
 pub struct CStr {
     // FIXME: this should not be represented with a DST slice but rather with
     //        just a raw `c_char` along with some form of marker to make
diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs
index 54e1c876f4c..1f384cbada3 100644
--- a/src/libstd/ffi/os_str.rs
+++ b/src/libstd/ffi/os_str.rs
@@ -97,6 +97,12 @@ pub struct OsString {
 /// [`String`]: ../string/struct.String.html
 /// [conversions]: index.html#conversions
 #[stable(feature = "rust1", since = "1.0.0")]
+// FIXME:
+// `OsStr::from_inner` current implementation relies
+// on `OsStr` being layout-compatible with `Slice`.
+// When attribute privacy is implemented, `OsStr` should be annotated as `#[repr(transparent)]`.
+// Anyway, `OsStr` representation and layout are considered implementation detail, are
+// not documented and must not be relied upon.
 pub struct OsStr {
     inner: Slice
 }
diff --git a/src/libstd/path.rs b/src/libstd/path.rs
index 87c071b512a..fd6ff1032bb 100644
--- a/src/libstd/path.rs
+++ b/src/libstd/path.rs
@@ -1123,6 +1123,12 @@ impl FusedIterator for Ancestors<'_> {}
 /// Which method works best depends on what kind of situation you're in.
 #[derive(Clone)]
 #[stable(feature = "rust1", since = "1.0.0")]
+// FIXME:
+// `PathBuf::as_mut_vec` current implementation relies
+// on `PathBuf` being layout-compatible with `Vec<u8>`.
+// When attribute privacy is implemented, `PathBuf` should be annotated as `#[repr(transparent)]`.
+// Anyway, `PathBuf` representation and layout are considered implementation detail, are
+// not documented and must not be relied upon.
 pub struct PathBuf {
     inner: OsString,
 }
@@ -1745,6 +1751,12 @@ impl AsRef<OsStr> for PathBuf {
 /// assert_eq!(extension, Some(OsStr::new("txt")));
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
+// FIXME:
+// `Path::new` current implementation relies
+// on `Path` being layout-compatible with `OsStr`.
+// When attribute privacy is implemented, `Path` should be annotated as `#[repr(transparent)]`.
+// Anyway, `Path` representation and layout are considered implementation detail, are
+// not documented and must not be relied upon.
 pub struct Path {
     inner: OsStr,
 }
diff --git a/src/libstd/sys_common/os_str_bytes.rs b/src/libstd/sys_common/os_str_bytes.rs
index a4961974d89..d734f412bf8 100644
--- a/src/libstd/sys_common/os_str_bytes.rs
+++ b/src/libstd/sys_common/os_str_bytes.rs
@@ -18,6 +18,12 @@ pub(crate) struct Buf {
     pub inner: Vec<u8>
 }
 
+// FIXME:
+// `Buf::as_slice` current implementation relies
+// on `Slice` being layout-compatible with `[u8]`.
+// When attribute privacy is implemented, `Slice` should be annotated as `#[repr(transparent)]`.
+// Anyway, `Slice` representation and layout are considered implementation detail, are
+// not documented and must not be relied upon.
 pub(crate) struct Slice {
     pub inner: [u8]
 }
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index f7e1b983e54..2d9556233d1 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -412,6 +412,7 @@ symbols! {
         match_beginning_vert,
         match_default_bindings,
         may_dangle,
+        mem,
         member_constraints,
         message,
         meta,
@@ -695,6 +696,7 @@ symbols! {
         underscore_imports,
         underscore_lifetimes,
         uniform_paths,
+        uninitialized,
         universal_impl_trait,
         unmarked_api,
         unreachable_code,
@@ -726,6 +728,7 @@ symbols! {
         windows,
         windows_subsystem,
         Yield,
+        zeroed,
     }
 }
 
diff --git a/src/test/ui/consts/uninhabited-const-issue-61744.rs b/src/test/ui/consts/uninhabited-const-issue-61744.rs
index 21fbbf8cfb5..4509ebc6338 100644
--- a/src/test/ui/consts/uninhabited-const-issue-61744.rs
+++ b/src/test/ui/consts/uninhabited-const-issue-61744.rs
@@ -1,11 +1,11 @@
 // compile-fail
 
 pub const unsafe fn fake_type<T>() -> T {
-    hint_unreachable()
+    hint_unreachable() //~ ERROR any use of this value will cause an error
 }
 
 pub const unsafe fn hint_unreachable() -> ! {
-    fake_type() //~ ERROR any use of this value will cause an error
+    fake_type()
 }
 
 trait Const {
diff --git a/src/test/ui/consts/uninhabited-const-issue-61744.stderr b/src/test/ui/consts/uninhabited-const-issue-61744.stderr
index e317bdf103c..f390676fda6 100644
--- a/src/test/ui/consts/uninhabited-const-issue-61744.stderr
+++ b/src/test/ui/consts/uninhabited-const-issue-61744.stderr
@@ -1,11 +1,60 @@
 error: any use of this value will cause an error
-  --> $DIR/uninhabited-const-issue-61744.rs:8:5
+  --> $DIR/uninhabited-const-issue-61744.rs:4:5
    |
-LL |     fake_type()
-   |     ^^^^^^^^^^^
+LL |     hint_unreachable()
+   |     ^^^^^^^^^^^^^^^^^^
    |     |
-   |     tried to call a function with return type T passing return place of type !
+   |     reached the configured maximum number of stack frames
    |     inside call to `hint_unreachable` at $DIR/uninhabited-const-issue-61744.rs:4:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
+   |     inside call to `fake_type::<!>` at $DIR/uninhabited-const-issue-61744.rs:8:5
    |     inside call to `fake_type::<i32>` at $DIR/uninhabited-const-issue-61744.rs:12:36
 ...
 LL |     const CONSTANT: i32 = unsafe { fake_type() };
diff --git a/src/test/ui/lint/uninitialized-zeroed.rs b/src/test/ui/lint/uninitialized-zeroed.rs
new file mode 100644
index 00000000000..8f9ca8717bd
--- /dev/null
+++ b/src/test/ui/lint/uninitialized-zeroed.rs
@@ -0,0 +1,58 @@
+// ignore-tidy-linelength
+// This test checks that calling `mem::{uninitialized,zeroed}` with certain types results
+// in a lint.
+
+#![feature(never_type)]
+#![allow(deprecated)]
+#![deny(invalid_value)]
+
+use std::mem::{self, MaybeUninit};
+
+enum Void {}
+
+struct Ref(&'static i32);
+
+struct Wrap<T> { wrapped: T }
+
+#[allow(unused)]
+fn generic<T: 'static>() {
+    unsafe {
+        let _val: &'static T = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: &'static T = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+        let _val: Wrap<&'static T> = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: Wrap<&'static T> = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+    }
+}
+
+fn main() {
+    unsafe {
+        let _val: ! = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: ! = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+        let _val: (i32, !) = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: (i32, !) = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+        let _val: Void = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: Void = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+        let _val: &'static i32 = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: &'static i32 = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+        let _val: Ref = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: Ref = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+        let _val: fn() = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: fn() = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+        let _val: Wrap<fn()> = mem::zeroed(); //~ ERROR: does not permit zero-initialization
+        let _val: Wrap<fn()> = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+        // Some types that should work just fine.
+        let _val: Option<&'static i32> = mem::zeroed();
+        let _val: Option<fn()> = mem::zeroed();
+        let _val: MaybeUninit<&'static i32> = mem::zeroed();
+        let _val: bool = mem::zeroed();
+        let _val: i32 = mem::zeroed();
+    }
+}
diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr
new file mode 100644
index 00000000000..af54b16bd0b
--- /dev/null
+++ b/src/test/ui/lint/uninitialized-zeroed.stderr
@@ -0,0 +1,169 @@
+error: the type `&'static T` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:20:32
+   |
+LL |         let _val: &'static T = mem::zeroed();
+   |                                ^^^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/uninitialized-zeroed.rs:7:9
+   |
+LL | #![deny(invalid_value)]
+   |         ^^^^^^^^^^^^^
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `&'static T` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:21:32
+   |
+LL |         let _val: &'static T = mem::uninitialized();
+   |                                ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `Wrap<&'static T>` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:23:38
+   |
+LL |         let _val: Wrap<&'static T> = mem::zeroed();
+   |                                      ^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `Wrap<&'static T>` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:24:38
+   |
+LL |         let _val: Wrap<&'static T> = mem::uninitialized();
+   |                                      ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `!` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:30:23
+   |
+LL |         let _val: ! = mem::zeroed();
+   |                       ^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `!` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:31:23
+   |
+LL |         let _val: ! = mem::uninitialized();
+   |                       ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `(i32, !)` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:33:30
+   |
+LL |         let _val: (i32, !) = mem::zeroed();
+   |                              ^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `(i32, !)` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:34:30
+   |
+LL |         let _val: (i32, !) = mem::uninitialized();
+   |                              ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `Void` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:36:26
+   |
+LL |         let _val: Void = mem::zeroed();
+   |                          ^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `Void` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:37:26
+   |
+LL |         let _val: Void = mem::uninitialized();
+   |                          ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `&'static i32` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:39:34
+   |
+LL |         let _val: &'static i32 = mem::zeroed();
+   |                                  ^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `&'static i32` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:40:34
+   |
+LL |         let _val: &'static i32 = mem::uninitialized();
+   |                                  ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `Ref` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:42:25
+   |
+LL |         let _val: Ref = mem::zeroed();
+   |                         ^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `Ref` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:43:25
+   |
+LL |         let _val: Ref = mem::uninitialized();
+   |                         ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `fn()` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:45:26
+   |
+LL |         let _val: fn() = mem::zeroed();
+   |                          ^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `fn()` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:46:26
+   |
+LL |         let _val: fn() = mem::uninitialized();
+   |                          ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `Wrap<fn()>` does not permit zero-initialization
+  --> $DIR/uninitialized-zeroed.rs:48:32
+   |
+LL |         let _val: Wrap<fn()> = mem::zeroed();
+   |                                ^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: the type `Wrap<fn()>` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:49:32
+   |
+LL |         let _val: Wrap<fn()> = mem::uninitialized();
+   |                                ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this means that this code causes undefined behavior when executed
+   = help: use `MaybeUninit` instead
+
+error: aborting due to 18 previous errors
+
diff --git a/src/test/ui/panic-uninitialized-zeroed.rs b/src/test/ui/panic-uninitialized-zeroed.rs
index 0c97babd51c..b0d66295618 100644
--- a/src/test/ui/panic-uninitialized-zeroed.rs
+++ b/src/test/ui/panic-uninitialized-zeroed.rs
@@ -4,7 +4,7 @@
 // in a runtime panic.
 
 #![feature(never_type)]
-#![allow(deprecated)]
+#![allow(deprecated, invalid_value)]
 
 use std::{mem, panic};
 
diff --git a/src/tools/clippy b/src/tools/clippy
-Subproject b041511b5fcd386c4ae74a30b60a5081f8717fb
+Subproject 72da1015d6d918fe1b29170acbf486d30e0c269
diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs
index c31a5069e46..a9d1d9997f6 100644
--- a/src/tools/error_index_generator/main.rs
+++ b/src/tools/error_index_generator/main.rs
@@ -16,7 +16,7 @@ use std::cell::RefCell;
 use syntax::edition::DEFAULT_EDITION;
 use syntax::diagnostics::metadata::{get_metadata_dir, ErrorMetadataMap, ErrorMetadata};
 
-use rustdoc::html::markdown::{Markdown, IdMap, ErrorCodes, PLAYGROUND};
+use rustdoc::html::markdown::{Markdown, IdMap, ErrorCodes, Playground};
 use rustc_serialize::json;
 
 enum OutputFormat {
@@ -95,9 +95,13 @@ impl Formatter for HTMLFormatter {
         match info.description {
             Some(ref desc) => {
                 let mut id_map = self.0.borrow_mut();
+                let playground = Playground {
+                    crate_name: None,
+                    url: String::from("https://play.rust-lang.org/"),
+                };
                 write!(output, "{}",
-                    Markdown(desc, &[], RefCell::new(&mut id_map),
-                             ErrorCodes::Yes, DEFAULT_EDITION))?
+                    Markdown(desc, &[], &mut id_map,
+                             ErrorCodes::Yes, DEFAULT_EDITION, &Some(playground)).to_string())?
             },
             None => write!(output, "<p>No description.</p>\n")?,
         }
@@ -260,9 +264,6 @@ fn parse_args() -> (OutputFormat, PathBuf) {
 
 fn main() {
     env_logger::init();
-    PLAYGROUND.with(|slot| {
-        *slot.borrow_mut() = Some((None, String::from("https://play.rust-lang.org/")));
-    });
     let (format, dst) = parse_args();
     let result = syntax::with_default_globals(move || {
         main_with_result(format, &dst)