diff --git a/Cargo.lock b/Cargo.lock
index 9e773c572cc41..328a220430601 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4568,6 +4568,7 @@ name = "rustdoc-json-types"
 version = "0.1.0"
 dependencies = [
  "serde",
+ "serde_json",
 ]
 
 [[package]]
diff --git a/compiler/rustc/src/main.rs b/compiler/rustc/src/main.rs
index 0151973eccfda..859028957db56 100644
--- a/compiler/rustc/src/main.rs
+++ b/compiler/rustc/src/main.rs
@@ -24,6 +24,20 @@ fn main() {
         static _F5: unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void = jemalloc_sys::realloc;
         #[used]
         static _F6: unsafe extern "C" fn(*mut c_void) = jemalloc_sys::free;
+
+        // On OSX, jemalloc doesn't directly override malloc/free, but instead
+        // registers itself with the allocator's zone APIs in a ctor. However,
+        // the linker doesn't seem to consider ctors as "used" when statically
+        // linking, so we need to explicitly depend on the function.
+        #[cfg(target_os = "macos")]
+        {
+            extern "C" {
+                fn _rjem_je_zone_register();
+            }
+
+            #[used]
+            static _F7: unsafe extern "C" fn() = _rjem_je_zone_register;
+        }
     }
 
     rustc_driver::set_sigpipe_handler();
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index de44a2031ab82..99196210e004b 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -915,16 +915,6 @@ impl Stmt {
         }
     }
 
-    pub fn tokens_mut(&mut self) -> Option<&mut LazyTokenStream> {
-        match self.kind {
-            StmtKind::Local(ref mut local) => local.tokens.as_mut(),
-            StmtKind::Item(ref mut item) => item.tokens.as_mut(),
-            StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => expr.tokens.as_mut(),
-            StmtKind::Empty => None,
-            StmtKind::MacCall(ref mut mac) => mac.tokens.as_mut(),
-        }
-    }
-
     pub fn has_trailing_semicolon(&self) -> bool {
         match &self.kind {
             StmtKind::Semi(_) => true,
diff --git a/compiler/rustc_ast/src/ast_like.rs b/compiler/rustc_ast/src/ast_like.rs
index 6649cda69a0b0..a71f2ac981501 100644
--- a/compiler/rustc_ast/src/ast_like.rs
+++ b/compiler/rustc_ast/src/ast_like.rs
@@ -11,13 +11,7 @@ use super::{AttrVec, Attribute, Stmt, StmtKind};
 pub trait AstLike: Sized {
     fn attrs(&self) -> &[Attribute];
     fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>));
-    /// Called by `Parser::collect_tokens` to store the collected
-    /// tokens inside an AST node
-    fn finalize_tokens(&mut self, _tokens: LazyTokenStream) {
-        // This default impl makes this trait easier to implement
-        // in tools like `rust-analyzer`
-        panic!("`finalize_tokens` is not supported!")
-    }
+    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>>;
 }
 
 impl<T: AstLike + 'static> AstLike for P<T> {
@@ -27,8 +21,8 @@ impl<T: AstLike + 'static> AstLike for P<T> {
     fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
         (**self).visit_attrs(f);
     }
-    fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
-        (**self).finalize_tokens(tokens)
+    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+        (**self).tokens_mut()
     }
 }
 
@@ -42,12 +36,12 @@ fn visit_attrvec(attrs: &mut AttrVec, f: impl FnOnce(&mut Vec<Attribute>)) {
 
 impl AstLike for StmtKind {
     fn attrs(&self) -> &[Attribute] {
-        match *self {
-            StmtKind::Local(ref local) => local.attrs(),
-            StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(),
-            StmtKind::Item(ref item) => item.attrs(),
+        match self {
+            StmtKind::Local(local) => local.attrs(),
+            StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.attrs(),
+            StmtKind::Item(item) => item.attrs(),
             StmtKind::Empty => &[],
-            StmtKind::MacCall(ref mac) => &*mac.attrs,
+            StmtKind::MacCall(mac) => &mac.attrs,
         }
     }
 
@@ -60,17 +54,14 @@ impl AstLike for StmtKind {
             StmtKind::MacCall(mac) => visit_attrvec(&mut mac.attrs, f),
         }
     }
-    fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
-        let stmt_tokens = match self {
-            StmtKind::Local(ref mut local) => &mut local.tokens,
-            StmtKind::Item(ref mut item) => &mut item.tokens,
-            StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => &mut expr.tokens,
-            StmtKind::Empty => return,
-            StmtKind::MacCall(ref mut mac) => &mut mac.tokens,
-        };
-        if stmt_tokens.is_none() {
-            *stmt_tokens = Some(tokens);
-        }
+    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+        Some(match self {
+            StmtKind::Local(local) => &mut local.tokens,
+            StmtKind::Item(item) => &mut item.tokens,
+            StmtKind::Expr(expr) | StmtKind::Semi(expr) => &mut expr.tokens,
+            StmtKind::Empty => return None,
+            StmtKind::MacCall(mac) => &mut mac.tokens,
+        })
     }
 }
 
@@ -82,8 +73,8 @@ impl AstLike for Stmt {
     fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
         self.kind.visit_attrs(f);
     }
-    fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
-        self.kind.finalize_tokens(tokens)
+    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+        self.kind.tokens_mut()
     }
 }
 
@@ -92,17 +83,13 @@ impl AstLike for Attribute {
         &[]
     }
     fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {}
-    fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
-        match &mut self.kind {
-            AttrKind::Normal(_, attr_tokens) => {
-                if attr_tokens.is_none() {
-                    *attr_tokens = Some(tokens);
-                }
+    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+        Some(match &mut self.kind {
+            AttrKind::Normal(_, tokens) => tokens,
+            kind @ AttrKind::DocComment(..) => {
+                panic!("Called tokens_mut on doc comment attr {:?}", kind)
             }
-            AttrKind::DocComment(..) => {
-                panic!("Called finalize_tokens on doc comment attr {:?}", self)
-            }
-        }
+        })
     }
 }
 
@@ -115,10 +102,8 @@ impl<T: AstLike> AstLike for Option<T> {
             inner.visit_attrs(f);
         }
     }
-    fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
-        if let Some(inner) = self {
-            inner.finalize_tokens(tokens);
-        }
+    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+        self.as_mut().and_then(|inner| inner.tokens_mut())
     }
 }
 
@@ -152,11 +137,8 @@ macro_rules! derive_has_tokens_and_attrs {
                 VecOrAttrVec::visit(&mut self.attrs, f)
             }
 
-            fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
-                if self.tokens.is_none() {
-                    self.tokens = Some(tokens);
-                }
-
+            fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+                Some(&mut self.tokens)
             }
         }
     )* }
@@ -173,7 +155,9 @@ macro_rules! derive_has_attrs_no_tokens {
                 VecOrAttrVec::visit(&mut self.attrs, f)
             }
 
-            fn finalize_tokens(&mut self, _tokens: LazyTokenStream) {}
+            fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+                None
+            }
         }
     )* }
 }
@@ -185,14 +169,10 @@ macro_rules! derive_has_tokens_no_attrs {
                 &[]
             }
 
-            fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {
-            }
-
-            fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
-                if self.tokens.is_none() {
-                    self.tokens = Some(tokens);
-                }
+            fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {}
 
+            fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+                Some(&mut self.tokens)
             }
         }
     )* }
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index c564ada0395e9..756caf18ec5eb 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -12,7 +12,7 @@
 //! For the simpler lowering steps, IDs and spans should be preserved. Unlike
 //! expansion we do not preserve the process of lowering in the spans, so spans
 //! should not be modified here. When creating a new node (as opposed to
-//! 'folding' an existing one), then you create a new ID using `next_id()`.
+//! "folding" an existing one), create a new ID using `next_id()`.
 //!
 //! You must ensure that IDs are unique. That means that you should only use the
 //! ID from an AST node in a single HIR node (you can assume that AST node-IDs
@@ -26,7 +26,7 @@
 //! span and spans don't need to be kept in order, etc. Where code is preserved
 //! by lowering, it should have the same span as in the AST. Where HIR nodes are
 //! new it is probably best to give a span for the whole AST node being lowered.
-//! All nodes should have real spans, don't use dummy spans. Tools are likely to
+//! All nodes should have real spans; don't use dummy spans. Tools are likely to
 //! get confused if the spans from leaf AST nodes occur in multiple places
 //! in the HIR, especially for multiple identifiers.
 
@@ -95,7 +95,7 @@ struct LoweringContext<'a, 'hir: 'a> {
     /// librustc_middle is independent of the parser, we use dynamic dispatch here.
     nt_to_tokenstream: NtToTokenstream,
 
-    /// Used to allocate HIR nodes
+    /// Used to allocate HIR nodes.
     arena: &'hir Arena<'hir>,
 
     /// The items being lowered are collected here.
@@ -128,7 +128,7 @@ struct LoweringContext<'a, 'hir: 'a> {
     is_in_trait_impl: bool,
     is_in_dyn_type: bool,
 
-    /// What to do when we encounter either an "anonymous lifetime
+    /// What to do when we encounter an "anonymous lifetime
     /// reference". The term "anonymous" is meant to encompass both
     /// `'_` lifetimes as well as fully elided cases where nothing is
     /// written at all (e.g., `&T` or `std::cell::Ref<T>`).
@@ -238,11 +238,13 @@ enum ImplTraitContext<'b, 'a> {
     OtherOpaqueTy {
         /// Set of lifetimes that this opaque type can capture, if it uses
         /// them. This includes lifetimes bound since we entered this context.
-        /// For example, in
+        /// For example:
         ///
+        /// ```
         /// type A<'b> = impl for<'a> Trait<'a, Out = impl Sized + 'a>;
+        /// ```
         ///
-        /// the inner opaque type captures `'a` because it uses it. It doesn't
+        /// Here the inner opaque type captures `'a` because it uses it. It doesn't
         /// need to capture `'b` because it already inherits the lifetime
         /// parameter from `A`.
         // FIXME(impl_trait): but `required_region_bounds` will ICE later
@@ -2110,7 +2112,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         hir::FnRetTy::Return(self.arena.alloc(opaque_ty))
     }
 
-    /// Transforms `-> T` into `Future<Output = T>`
+    /// Transforms `-> T` into `Future<Output = T>`.
     fn lower_async_fn_output_type_to_future_bound(
         &mut self,
         output: &FnRetTy,
diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs
new file mode 100644
index 0000000000000..eea4d785dee06
--- /dev/null
+++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs
@@ -0,0 +1,157 @@
+use crate::util::check_builtin_macro_attribute;
+
+use rustc_ast::mut_visit::{self, MutVisitor};
+use rustc_ast::ptr::P;
+use rustc_ast::{self as ast, AstLike};
+use rustc_expand::base::{Annotatable, ExtCtxt};
+use rustc_expand::config::StripUnconfigured;
+use rustc_expand::configure;
+use rustc_span::symbol::sym;
+use rustc_span::Span;
+use smallvec::SmallVec;
+
+crate fn expand(
+    ecx: &mut ExtCtxt<'_>,
+    _span: Span,
+    meta_item: &ast::MetaItem,
+    annotatable: Annotatable,
+) -> Vec<Annotatable> {
+    check_builtin_macro_attribute(ecx, meta_item, sym::cfg_eval);
+    cfg_eval(ecx, annotatable)
+}
+
+crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Vec<Annotatable> {
+    let mut visitor = CfgEval {
+        cfg: StripUnconfigured { sess: ecx.sess, features: ecx.ecfg.features, modified: false },
+    };
+    let mut annotatable = visitor.configure_annotatable(annotatable);
+    if visitor.cfg.modified {
+        // Erase the tokens if cfg-stripping modified the item
+        // This will cause us to synthesize fake tokens
+        // when `nt_to_tokenstream` is called on this item.
+        if let Some(tokens) = annotatable.tokens_mut() {
+            *tokens = None;
+        }
+    }
+    vec![annotatable]
+}
+
+struct CfgEval<'a> {
+    cfg: StripUnconfigured<'a>,
+}
+
+impl CfgEval<'_> {
+    fn configure<T: AstLike>(&mut self, node: T) -> Option<T> {
+        self.cfg.configure(node)
+    }
+
+    fn configure_annotatable(&mut self, annotatable: Annotatable) -> Annotatable {
+        // Since the item itself has already been configured by the InvocationCollector,
+        // we know that fold result vector will contain exactly one element
+        match annotatable {
+            Annotatable::Item(item) => Annotatable::Item(self.flat_map_item(item).pop().unwrap()),
+            Annotatable::TraitItem(item) => {
+                Annotatable::TraitItem(self.flat_map_trait_item(item).pop().unwrap())
+            }
+            Annotatable::ImplItem(item) => {
+                Annotatable::ImplItem(self.flat_map_impl_item(item).pop().unwrap())
+            }
+            Annotatable::ForeignItem(item) => {
+                Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap())
+            }
+            Annotatable::Stmt(stmt) => {
+                Annotatable::Stmt(stmt.map(|stmt| self.flat_map_stmt(stmt).pop().unwrap()))
+            }
+            Annotatable::Expr(mut expr) => Annotatable::Expr({
+                self.visit_expr(&mut expr);
+                expr
+            }),
+            Annotatable::Arm(arm) => Annotatable::Arm(self.flat_map_arm(arm).pop().unwrap()),
+            Annotatable::Field(field) => {
+                Annotatable::Field(self.flat_map_field(field).pop().unwrap())
+            }
+            Annotatable::FieldPat(fp) => {
+                Annotatable::FieldPat(self.flat_map_field_pattern(fp).pop().unwrap())
+            }
+            Annotatable::GenericParam(param) => {
+                Annotatable::GenericParam(self.flat_map_generic_param(param).pop().unwrap())
+            }
+            Annotatable::Param(param) => {
+                Annotatable::Param(self.flat_map_param(param).pop().unwrap())
+            }
+            Annotatable::StructField(sf) => {
+                Annotatable::StructField(self.flat_map_struct_field(sf).pop().unwrap())
+            }
+            Annotatable::Variant(v) => {
+                Annotatable::Variant(self.flat_map_variant(v).pop().unwrap())
+            }
+        }
+    }
+}
+
+impl MutVisitor for CfgEval<'_> {
+    fn visit_expr(&mut self, expr: &mut P<ast::Expr>) {
+        self.cfg.configure_expr(expr);
+        mut_visit::noop_visit_expr(expr, self);
+    }
+
+    fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
+        let mut expr = configure!(self, expr);
+        mut_visit::noop_visit_expr(&mut expr, self);
+        Some(expr)
+    }
+
+    fn flat_map_generic_param(
+        &mut self,
+        param: ast::GenericParam,
+    ) -> SmallVec<[ast::GenericParam; 1]> {
+        mut_visit::noop_flat_map_generic_param(configure!(self, param), self)
+    }
+
+    fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
+        mut_visit::noop_flat_map_stmt(configure!(self, stmt), self)
+    }
+
+    fn flat_map_item(&mut self, item: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
+        mut_visit::noop_flat_map_item(configure!(self, item), self)
+    }
+
+    fn flat_map_impl_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
+        mut_visit::noop_flat_map_assoc_item(configure!(self, item), self)
+    }
+
+    fn flat_map_trait_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
+        mut_visit::noop_flat_map_assoc_item(configure!(self, item), self)
+    }
+
+    fn flat_map_foreign_item(
+        &mut self,
+        foreign_item: P<ast::ForeignItem>,
+    ) -> SmallVec<[P<ast::ForeignItem>; 1]> {
+        mut_visit::noop_flat_map_foreign_item(configure!(self, foreign_item), self)
+    }
+
+    fn flat_map_arm(&mut self, arm: ast::Arm) -> SmallVec<[ast::Arm; 1]> {
+        mut_visit::noop_flat_map_arm(configure!(self, arm), self)
+    }
+
+    fn flat_map_field(&mut self, field: ast::Field) -> SmallVec<[ast::Field; 1]> {
+        mut_visit::noop_flat_map_field(configure!(self, field), self)
+    }
+
+    fn flat_map_field_pattern(&mut self, fp: ast::FieldPat) -> SmallVec<[ast::FieldPat; 1]> {
+        mut_visit::noop_flat_map_field_pattern(configure!(self, fp), self)
+    }
+
+    fn flat_map_param(&mut self, p: ast::Param) -> SmallVec<[ast::Param; 1]> {
+        mut_visit::noop_flat_map_param(configure!(self, p), self)
+    }
+
+    fn flat_map_struct_field(&mut self, sf: ast::StructField) -> SmallVec<[ast::StructField; 1]> {
+        mut_visit::noop_flat_map_struct_field(configure!(self, sf), self)
+    }
+
+    fn flat_map_variant(&mut self, variant: ast::Variant) -> SmallVec<[ast::Variant; 1]> {
+        mut_visit::noop_flat_map_variant(configure!(self, variant), self)
+    }
+}
diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs
index fad64858ce3ff..0da2c1c1021f3 100644
--- a/compiler/rustc_builtin_macros/src/derive.rs
+++ b/compiler/rustc_builtin_macros/src/derive.rs
@@ -1,7 +1,8 @@
+use crate::cfg_eval::cfg_eval;
+
 use rustc_ast::{self as ast, token, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
 use rustc_errors::{struct_span_err, Applicability};
 use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier};
-use rustc_expand::config::StripUnconfigured;
 use rustc_feature::AttributeTemplate;
 use rustc_parse::validate_attr;
 use rustc_session::Session;
@@ -51,26 +52,7 @@ impl MultiItemModifier for Expander {
 
         // FIXME: Try to cache intermediate results to avoid collecting same paths multiple times.
         match ecx.resolver.resolve_derives(ecx.current_expansion.id, derives, ecx.force_mode) {
-            Ok(()) => {
-                let mut visitor =
-                    StripUnconfigured { sess, features: ecx.ecfg.features, modified: false };
-                let mut item = visitor.fully_configure(item);
-                if visitor.modified {
-                    // Erase the tokens if cfg-stripping modified the item
-                    // This will cause us to synthesize fake tokens
-                    // when `nt_to_tokenstream` is called on this item.
-                    match &mut item {
-                        Annotatable::Item(item) => item,
-                        Annotatable::Stmt(stmt) => match &mut stmt.kind {
-                            StmtKind::Item(item) => item,
-                            _ => unreachable!(),
-                        },
-                        _ => unreachable!(),
-                    }
-                    .tokens = None;
-                }
-                ExpandResult::Ready(vec![item])
-            }
+            Ok(()) => ExpandResult::Ready(cfg_eval(ecx, item)),
             Err(Indeterminate) => ExpandResult::Retry(item),
         }
     }
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index 9a3c914337ca2..1017b23e5675a 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -24,6 +24,7 @@ mod asm;
 mod assert;
 mod cfg;
 mod cfg_accessible;
+mod cfg_eval;
 mod compile_error;
 mod concat;
 mod concat_idents;
@@ -89,6 +90,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
     register_attr! {
         bench: test::expand_bench,
         cfg_accessible: cfg_accessible::Expander,
+        cfg_eval: cfg_eval::expand,
         derive: derive::Expander,
         global_allocator: global_allocator::expand,
         test: test::expand_test,
diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs
index 28efd483c8670..4aafcb2fb6dfe 100644
--- a/compiler/rustc_builtin_macros/src/source_util.rs
+++ b/compiler/rustc_builtin_macros/src/source_util.rs
@@ -4,7 +4,7 @@ use rustc_ast::token;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast_pretty::pprust;
 use rustc_expand::base::{self, *};
-use rustc_expand::module::DirectoryOwnership;
+use rustc_expand::module::DirOwnership;
 use rustc_parse::parser::{ForceCollect, Parser};
 use rustc_parse::{self, new_parser_from_file};
 use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
@@ -101,7 +101,7 @@ pub fn expand_include<'cx>(
         None => return DummyResult::any(sp),
     };
     // The file will be added to the code map by the parser
-    let mut file = match cx.resolve_path(file, sp) {
+    let file = match cx.resolve_path(file, sp) {
         Ok(f) => f,
         Err(mut err) => {
             err.emit();
@@ -114,10 +114,9 @@ pub fn expand_include<'cx>(
     // then the path of `bar.rs` should be relative to the directory of `file`.
     // See https://github.com/rust-lang/rust/pull/69838/files#r395217057 for a discussion.
     // `MacroExpander::fully_expand_fragment` later restores, so "stack discipline" is maintained.
-    file.pop();
-    cx.current_expansion.directory_ownership = DirectoryOwnership::Owned { relative: None };
-    let mod_path = cx.current_expansion.module.mod_path.clone();
-    cx.current_expansion.module = Rc::new(ModuleData { mod_path, directory: file });
+    let dir_path = file.parent().unwrap_or(&file).to_owned();
+    cx.current_expansion.module = Rc::new(cx.current_expansion.module.with_dir_path(dir_path));
+    cx.current_expansion.dir_ownership = DirOwnership::Owned { relative: None };
 
     struct ExpandResult<'a> {
         p: Parser<'a>,
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 929cc56294d3d..666065efdfb43 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -1,11 +1,11 @@
 use crate::expand::{self, AstFragment, Invocation};
-use crate::module::DirectoryOwnership;
+use crate::module::DirOwnership;
 
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Nonterminal};
 use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, LazyTokenStream, TokenStream};
 use rustc_ast::visit::{AssocCtxt, Visitor};
-use rustc_ast::{self as ast, AstLike, Attribute, NodeId, PatKind};
+use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind};
 use rustc_attr::{self as attr, Deprecation, Stability};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::{self, Lrc};
@@ -81,8 +81,22 @@ impl AstLike for Annotatable {
         }
     }
 
-    fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
-        panic!("Called finalize_tokens on an Annotatable: {:?}", tokens);
+    fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
+        match self {
+            Annotatable::Item(item) => item.tokens_mut(),
+            Annotatable::TraitItem(trait_item) => trait_item.tokens_mut(),
+            Annotatable::ImplItem(impl_item) => impl_item.tokens_mut(),
+            Annotatable::ForeignItem(foreign_item) => foreign_item.tokens_mut(),
+            Annotatable::Stmt(stmt) => stmt.tokens_mut(),
+            Annotatable::Expr(expr) => expr.tokens_mut(),
+            Annotatable::Arm(arm) => arm.tokens_mut(),
+            Annotatable::Field(field) => field.tokens_mut(),
+            Annotatable::FieldPat(fp) => fp.tokens_mut(),
+            Annotatable::GenericParam(gp) => gp.tokens_mut(),
+            Annotatable::Param(p) => p.tokens_mut(),
+            Annotatable::StructField(sf) => sf.tokens_mut(),
+            Annotatable::Variant(v) => v.tokens_mut(),
+        }
     }
 }
 
@@ -900,10 +914,26 @@ pub trait ResolverExpand {
     fn cfg_accessible(&mut self, expn_id: ExpnId, path: &ast::Path) -> Result<bool, Indeterminate>;
 }
 
-#[derive(Clone)]
+#[derive(Clone, Default)]
 pub struct ModuleData {
+    /// Path to the module starting from the crate name, like `my_crate::foo::bar`.
     pub mod_path: Vec<Ident>,
-    pub directory: PathBuf,
+    /// Stack of paths to files loaded by out-of-line module items,
+    /// used to detect and report recursive module inclusions.
+    pub file_path_stack: Vec<PathBuf>,
+    /// Directory to search child module files in,
+    /// often (but not necessarily) the parent of the top file path on the `file_path_stack`.
+    pub dir_path: PathBuf,
+}
+
+impl ModuleData {
+    pub fn with_dir_path(&self, dir_path: PathBuf) -> ModuleData {
+        ModuleData {
+            mod_path: self.mod_path.clone(),
+            file_path_stack: self.file_path_stack.clone(),
+            dir_path,
+        }
+    }
 }
 
 #[derive(Clone)]
@@ -911,10 +941,13 @@ pub struct ExpansionData {
     pub id: ExpnId,
     pub depth: usize,
     pub module: Rc<ModuleData>,
-    pub directory_ownership: DirectoryOwnership,
+    pub dir_ownership: DirOwnership,
     pub prior_type_ascription: Option<(Span, bool)>,
 }
 
+type OnExternModLoaded<'a> =
+    Option<&'a dyn Fn(Ident, Vec<Attribute>, Vec<P<Item>>, Span) -> (Vec<Attribute>, Vec<P<Item>>)>;
+
 /// One of these is made during expansion and incrementally updated as we go;
 /// when a macro expansion occurs, the resulting nodes have the `backtrace()
 /// -> expn_data` of their expansion context stored into their span.
@@ -932,7 +965,7 @@ pub struct ExtCtxt<'a> {
     /// Called directly after having parsed an external `mod foo;` in expansion.
     ///
     /// `Ident` is the module name.
-    pub(super) extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate, Ident)>,
+    pub(super) extern_mod_loaded: OnExternModLoaded<'a>,
 }
 
 impl<'a> ExtCtxt<'a> {
@@ -940,7 +973,7 @@ impl<'a> ExtCtxt<'a> {
         sess: &'a Session,
         ecfg: expand::ExpansionConfig<'a>,
         resolver: &'a mut dyn ResolverExpand,
-        extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate, Ident)>,
+        extern_mod_loaded: OnExternModLoaded<'a>,
     ) -> ExtCtxt<'a> {
         ExtCtxt {
             sess,
@@ -952,8 +985,8 @@ impl<'a> ExtCtxt<'a> {
             current_expansion: ExpansionData {
                 id: ExpnId::root(),
                 depth: 0,
-                module: Rc::new(ModuleData { mod_path: Vec::new(), directory: PathBuf::new() }),
-                directory_ownership: DirectoryOwnership::Owned { relative: None },
+                module: Default::default(),
+                dir_ownership: DirOwnership::Owned { relative: None },
                 prior_type_ascription: None,
             },
             force_mode: false,
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index 7d0becf1f5d85..a23731cf309a4 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -1,8 +1,5 @@
 //! Conditional compilation stripping.
 
-use crate::base::Annotatable;
-
-use rustc_ast::mut_visit::*;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{DelimToken, Token, TokenKind};
 use rustc_ast::tokenstream::{DelimSpan, LazyTokenStream, Spacing, TokenStream, TokenTree};
@@ -22,8 +19,6 @@ use rustc_span::edition::{Edition, ALL_EDITIONS};
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 
-use smallvec::SmallVec;
-
 /// A folder that strips out items that do not belong in the current configuration.
 pub struct StripUnconfigured<'a> {
     pub sess: &'a Session,
@@ -272,7 +267,7 @@ impl<'a> StripUnconfigured<'a> {
     /// Gives compiler warnings if any `cfg_attr` does not contain any
     /// attributes and is in the original source code. Gives compiler errors if
     /// the syntax of any `cfg_attr` is incorrect.
-    pub fn process_cfg_attrs<T: AstLike>(&mut self, node: &mut T) {
+    fn process_cfg_attrs<T: AstLike>(&mut self, node: &mut T) {
         node.visit_attrs(|attrs| {
             attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr));
         });
@@ -387,7 +382,7 @@ impl<'a> StripUnconfigured<'a> {
     }
 
     /// Determines if a node with the given attributes should be included in this configuration.
-    pub fn in_cfg(&self, attrs: &[Attribute]) -> bool {
+    fn in_cfg(&self, attrs: &[Attribute]) -> bool {
         attrs.iter().all(|attr| {
             if !is_cfg(self.sess, attr) {
                 return true;
@@ -427,16 +422,8 @@ impl<'a> StripUnconfigured<'a> {
         })
     }
 
-    /// Visit attributes on expression and statements (but not attributes on items in blocks).
-    fn visit_expr_attrs(&mut self, attrs: &[Attribute]) {
-        // flag the offending attributes
-        for attr in attrs.iter() {
-            self.maybe_emit_expr_attr_err(attr);
-        }
-    }
-
     /// If attributes are not allowed on expressions, emit an error for `attr`
-    pub fn maybe_emit_expr_attr_err(&self, attr: &Attribute) {
+    crate fn maybe_emit_expr_attr_err(&self, attr: &Attribute) {
         if !self.features.map_or(true, |features| features.stmt_expr_attributes) {
             let mut err = feature_err(
                 &self.sess.parse_sess,
@@ -453,49 +440,10 @@ impl<'a> StripUnconfigured<'a> {
         }
     }
 
-    pub fn configure_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) {
-        let ast::ForeignMod { unsafety: _, abi: _, items } = foreign_mod;
-        items.flat_map_in_place(|item| self.configure(item));
-    }
-
-    fn configure_variant_data(&mut self, vdata: &mut ast::VariantData) {
-        match vdata {
-            ast::VariantData::Struct(fields, ..) | ast::VariantData::Tuple(fields, _) => {
-                fields.flat_map_in_place(|field| self.configure(field))
-            }
-            ast::VariantData::Unit(_) => {}
-        }
-    }
-
-    pub fn configure_item_kind(&mut self, item: &mut ast::ItemKind) {
-        match item {
-            ast::ItemKind::Struct(def, _generics) | ast::ItemKind::Union(def, _generics) => {
-                self.configure_variant_data(def)
-            }
-            ast::ItemKind::Enum(ast::EnumDef { variants }, _generics) => {
-                variants.flat_map_in_place(|variant| self.configure(variant));
-                for variant in variants {
-                    self.configure_variant_data(&mut variant.data);
-                }
-            }
-            _ => {}
-        }
-    }
-
-    pub fn configure_expr_kind(&mut self, expr_kind: &mut ast::ExprKind) {
-        match expr_kind {
-            ast::ExprKind::Match(_m, arms) => {
-                arms.flat_map_in_place(|arm| self.configure(arm));
-            }
-            ast::ExprKind::Struct(_path, fields, _base) => {
-                fields.flat_map_in_place(|field| self.configure(field));
-            }
-            _ => {}
-        }
-    }
-
     pub fn configure_expr(&mut self, expr: &mut P<ast::Expr>) {
-        self.visit_expr_attrs(expr.attrs());
+        for attr in expr.attrs.iter() {
+            self.maybe_emit_expr_attr_err(attr);
+        }
 
         // If an expr is valid to cfg away it will have been removed by the
         // outer stmt or expression folder before descending in here.
@@ -511,117 +459,6 @@ impl<'a> StripUnconfigured<'a> {
 
         self.process_cfg_attrs(expr)
     }
-
-    pub fn configure_pat(&mut self, pat: &mut P<ast::Pat>) {
-        if let ast::PatKind::Struct(_path, fields, _etc) = &mut pat.kind {
-            fields.flat_map_in_place(|field| self.configure(field));
-        }
-    }
-
-    pub fn configure_fn_decl(&mut self, fn_decl: &mut ast::FnDecl) {
-        fn_decl.inputs.flat_map_in_place(|arg| self.configure(arg));
-    }
-
-    pub fn fully_configure(&mut self, item: Annotatable) -> Annotatable {
-        // Since the item itself has already been configured by the InvocationCollector,
-        // we know that fold result vector will contain exactly one element
-        match item {
-            Annotatable::Item(item) => Annotatable::Item(self.flat_map_item(item).pop().unwrap()),
-            Annotatable::TraitItem(item) => {
-                Annotatable::TraitItem(self.flat_map_trait_item(item).pop().unwrap())
-            }
-            Annotatable::ImplItem(item) => {
-                Annotatable::ImplItem(self.flat_map_impl_item(item).pop().unwrap())
-            }
-            Annotatable::ForeignItem(item) => {
-                Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap())
-            }
-            Annotatable::Stmt(stmt) => {
-                Annotatable::Stmt(stmt.map(|stmt| self.flat_map_stmt(stmt).pop().unwrap()))
-            }
-            Annotatable::Expr(mut expr) => Annotatable::Expr({
-                self.visit_expr(&mut expr);
-                expr
-            }),
-            Annotatable::Arm(arm) => Annotatable::Arm(self.flat_map_arm(arm).pop().unwrap()),
-            Annotatable::Field(field) => {
-                Annotatable::Field(self.flat_map_field(field).pop().unwrap())
-            }
-            Annotatable::FieldPat(fp) => {
-                Annotatable::FieldPat(self.flat_map_field_pattern(fp).pop().unwrap())
-            }
-            Annotatable::GenericParam(param) => {
-                Annotatable::GenericParam(self.flat_map_generic_param(param).pop().unwrap())
-            }
-            Annotatable::Param(param) => {
-                Annotatable::Param(self.flat_map_param(param).pop().unwrap())
-            }
-            Annotatable::StructField(sf) => {
-                Annotatable::StructField(self.flat_map_struct_field(sf).pop().unwrap())
-            }
-            Annotatable::Variant(v) => {
-                Annotatable::Variant(self.flat_map_variant(v).pop().unwrap())
-            }
-        }
-    }
-}
-
-impl<'a> MutVisitor for StripUnconfigured<'a> {
-    fn visit_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) {
-        self.configure_foreign_mod(foreign_mod);
-        noop_visit_foreign_mod(foreign_mod, self);
-    }
-
-    fn visit_item_kind(&mut self, item: &mut ast::ItemKind) {
-        self.configure_item_kind(item);
-        noop_visit_item_kind(item, self);
-    }
-
-    fn visit_expr(&mut self, expr: &mut P<ast::Expr>) {
-        self.configure_expr(expr);
-        self.configure_expr_kind(&mut expr.kind);
-        noop_visit_expr(expr, self);
-    }
-
-    fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
-        let mut expr = configure!(self, expr);
-        self.configure_expr_kind(&mut expr.kind);
-        noop_visit_expr(&mut expr, self);
-        Some(expr)
-    }
-
-    fn flat_map_generic_param(
-        &mut self,
-        param: ast::GenericParam,
-    ) -> SmallVec<[ast::GenericParam; 1]> {
-        noop_flat_map_generic_param(configure!(self, param), self)
-    }
-
-    fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
-        noop_flat_map_stmt(configure!(self, stmt), self)
-    }
-
-    fn flat_map_item(&mut self, item: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
-        noop_flat_map_item(configure!(self, item), self)
-    }
-
-    fn flat_map_impl_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
-        noop_flat_map_assoc_item(configure!(self, item), self)
-    }
-
-    fn flat_map_trait_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
-        noop_flat_map_assoc_item(configure!(self, item), self)
-    }
-
-    fn visit_pat(&mut self, pat: &mut P<ast::Pat>) {
-        self.configure_pat(pat);
-        noop_visit_pat(pat, self)
-    }
-
-    fn visit_fn_decl(&mut self, mut fn_decl: &mut P<ast::FnDecl>) {
-        self.configure_fn_decl(&mut fn_decl);
-        noop_visit_fn_decl(fn_decl, self);
-    }
 }
 
 fn is_cfg(sess: &Session, attr: &Attribute) -> bool {
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index b474cad1242e8..dd2eaa0f3d53c 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -3,7 +3,7 @@ use crate::config::StripUnconfigured;
 use crate::configure;
 use crate::hygiene::SyntaxContext;
 use crate::mbe::macro_rules::annotate_err_with_kind;
-use crate::module::{parse_external_mod, push_directory, Directory, DirectoryOwnership};
+use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod};
 use crate::placeholders::{placeholder, PlaceholderExpander};
 
 use rustc_ast as ast;
@@ -355,16 +355,17 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
     // FIXME: Avoid visiting the crate as a `Mod` item,
     // make crate a first class expansion target instead.
     pub fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate {
-        let mut module = ModuleData {
-            mod_path: vec![Ident::from_str(&self.cx.ecfg.crate_name)],
-            directory: match self.cx.source_map().span_to_unmapped_path(krate.span) {
-                FileName::Real(name) => name.into_local_path(),
-                other => PathBuf::from(other.to_string()),
-            },
+        let file_path = match self.cx.source_map().span_to_unmapped_path(krate.span) {
+            FileName::Real(name) => name.into_local_path(),
+            other => PathBuf::from(other.to_string()),
         };
-        module.directory.pop();
-        self.cx.root_path = module.directory.clone();
-        self.cx.current_expansion.module = Rc::new(module);
+        let dir_path = file_path.parent().unwrap_or(&file_path).to_owned();
+        self.cx.root_path = dir_path.clone();
+        self.cx.current_expansion.module = Rc::new(ModuleData {
+            mod_path: vec![Ident::from_str(&self.cx.ecfg.crate_name)],
+            file_path_stack: vec![file_path],
+            dir_path,
+        });
 
         let krate_item = AstFragment::Items(smallvec![P(ast::Item {
             attrs: krate.attrs,
@@ -1245,10 +1246,12 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
     }
 
     fn visit_block(&mut self, block: &mut P<Block>) {
-        let old_directory_ownership = self.cx.current_expansion.directory_ownership;
-        self.cx.current_expansion.directory_ownership = DirectoryOwnership::UnownedViaBlock;
+        let orig_dir_ownership = mem::replace(
+            &mut self.cx.current_expansion.dir_ownership,
+            DirOwnership::UnownedViaBlock,
+        );
         noop_visit_block(block, self);
-        self.cx.current_expansion.directory_ownership = old_directory_ownership;
+        self.cx.current_expansion.dir_ownership = orig_dir_ownership;
     }
 
     fn flat_map_item(&mut self, item: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
@@ -1276,63 +1279,76 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
                 })
             }
             ast::ItemKind::Mod(_, ref mut mod_kind) if ident != Ident::invalid() => {
-                let sess = &self.cx.sess.parse_sess;
-                let orig_ownership = self.cx.current_expansion.directory_ownership;
-                let mut module = (*self.cx.current_expansion.module).clone();
-
-                let pushed = &mut false; // Record `parse_external_mod` pushing so we can pop.
-                let dir = Directory { ownership: orig_ownership, path: module.directory };
-                let Directory { ownership, path } = match mod_kind {
-                    ModKind::Loaded(_, Inline::Yes, _) => {
+                let (file_path, dir_path, dir_ownership) = match mod_kind {
+                    ModKind::Loaded(_, inline, _) => {
                         // Inline `mod foo { ... }`, but we still need to push directories.
+                        assert!(
+                            *inline == Inline::Yes,
+                            "`mod` item is loaded from a file for the second time"
+                        );
+                        let (dir_path, dir_ownership) = mod_dir_path(
+                            &self.cx.sess,
+                            ident,
+                            &attrs,
+                            &self.cx.current_expansion.module,
+                            self.cx.current_expansion.dir_ownership,
+                        );
                         item.attrs = attrs;
-                        push_directory(&self.cx.sess, ident, &item.attrs, dir)
-                    }
-                    ModKind::Loaded(_, Inline::No, _) => {
-                        panic!("`mod` item is loaded from a file for the second time")
+                        (None, dir_path, dir_ownership)
                     }
                     ModKind::Unloaded => {
                         // We have an outline `mod foo;` so we need to parse the file.
-                        let (items, inner_span, dir) =
-                            parse_external_mod(&self.cx.sess, ident, span, dir, &mut attrs, pushed);
+                        let old_attrs_len = attrs.len();
+                        let ParsedExternalMod {
+                            mut items,
+                            inner_span,
+                            file_path,
+                            dir_path,
+                            dir_ownership,
+                        } = parse_external_mod(
+                            &self.cx.sess,
+                            ident,
+                            span,
+                            &self.cx.current_expansion.module,
+                            self.cx.current_expansion.dir_ownership,
+                            &mut attrs,
+                        );
 
-                        let krate =
-                            ast::Crate { attrs, items, span: inner_span, proc_macros: vec![] };
                         if let Some(extern_mod_loaded) = self.cx.extern_mod_loaded {
-                            extern_mod_loaded(&krate, ident);
+                            (attrs, items) = extern_mod_loaded(ident, attrs, items, inner_span);
                         }
 
-                        *mod_kind = ModKind::Loaded(krate.items, Inline::No, inner_span);
-                        item.attrs = krate.attrs;
-                        // File can have inline attributes, e.g., `#![cfg(...)]` & co. => Reconfigure.
-                        item = match self.configure(item) {
-                            Some(node) => node,
-                            None => {
-                                if *pushed {
-                                    sess.included_mod_stack.borrow_mut().pop();
-                                }
-                                return Default::default();
-                            }
-                        };
-                        dir
+                        *mod_kind = ModKind::Loaded(items, Inline::No, inner_span);
+                        item.attrs = attrs;
+                        if item.attrs.len() > old_attrs_len {
+                            // If we loaded an out-of-line module and added some inner attributes,
+                            // then we need to re-configure it.
+                            // FIXME: Attributes also need to be recollected
+                            // for resolution and expansion.
+                            item = configure!(self, item);
+                        }
+                        (Some(file_path), dir_path, dir_ownership)
                     }
                 };
 
                 // Set the module info before we flat map.
-                self.cx.current_expansion.directory_ownership = ownership;
-                module.directory = path;
+                let mut module = self.cx.current_expansion.module.with_dir_path(dir_path);
                 module.mod_path.push(ident);
+                if let Some(file_path) = file_path {
+                    module.file_path_stack.push(file_path);
+                }
+
                 let orig_module =
                     mem::replace(&mut self.cx.current_expansion.module, Rc::new(module));
+                let orig_dir_ownership =
+                    mem::replace(&mut self.cx.current_expansion.dir_ownership, dir_ownership);
 
                 let result = noop_flat_map_item(item, self);
 
                 // Restore the module info.
+                self.cx.current_expansion.dir_ownership = orig_dir_ownership;
                 self.cx.current_expansion.module = orig_module;
-                self.cx.current_expansion.directory_ownership = orig_ownership;
-                if *pushed {
-                    sess.included_mod_stack.borrow_mut().pop();
-                }
+
                 result
             }
             _ => {
diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs
index c5d8ff25ea94b..1a93975533de0 100644
--- a/compiler/rustc_expand/src/lib.rs
+++ b/compiler/rustc_expand/src/lib.rs
@@ -1,5 +1,7 @@
+#![feature(bool_to_option)]
 #![feature(crate_visibility_modifier)]
 #![feature(decl_macro)]
+#![feature(destructuring_assignment)]
 #![feature(or_patterns)]
 #![feature(proc_macro_diagnostic)]
 #![feature(proc_macro_internals)]
diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs
index 076d3b02be93f..2ec656d4895e7 100644
--- a/compiler/rustc_expand/src/module.rs
+++ b/compiler/rustc_expand/src/module.rs
@@ -1,232 +1,155 @@
+use crate::base::ModuleData;
 use rustc_ast::ptr::P;
 use rustc_ast::{token, Attribute, Item};
-use rustc_errors::{struct_span_err, PResult};
+use rustc_errors::{struct_span_err, DiagnosticBuilder};
 use rustc_parse::new_parser_from_file;
 use rustc_session::parse::ParseSess;
 use rustc_session::Session;
-use rustc_span::source_map::{FileName, Span};
 use rustc_span::symbol::{sym, Ident};
+use rustc_span::Span;
 
 use std::path::{self, Path, PathBuf};
 
-#[derive(Clone)]
-pub struct Directory {
-    pub path: PathBuf,
-    pub ownership: DirectoryOwnership,
-}
-
 #[derive(Copy, Clone)]
-pub enum DirectoryOwnership {
+pub enum DirOwnership {
     Owned {
         // None if `mod.rs`, `Some("foo")` if we're in `foo.rs`.
         relative: Option<Ident>,
     },
     UnownedViaBlock,
-    UnownedViaMod,
 }
 
-/// Information about the path to a module.
 // Public for rustfmt usage.
-pub struct ModulePath<'a> {
-    name: String,
-    path_exists: bool,
-    pub result: PResult<'a, ModulePathSuccess>,
+pub struct ModulePathSuccess {
+    pub file_path: PathBuf,
+    pub dir_ownership: DirOwnership,
 }
 
-// Public for rustfmt usage.
-pub struct ModulePathSuccess {
-    pub path: PathBuf,
-    pub ownership: DirectoryOwnership,
+crate struct ParsedExternalMod {
+    pub items: Vec<P<Item>>,
+    pub inner_span: Span,
+    pub file_path: PathBuf,
+    pub dir_path: PathBuf,
+    pub dir_ownership: DirOwnership,
+}
+
+pub enum ModError<'a> {
+    CircularInclusion(Vec<PathBuf>),
+    ModInBlock(Option<Ident>),
+    FileNotFound(Ident, PathBuf),
+    MultipleCandidates(Ident, String, String),
+    ParserError(DiagnosticBuilder<'a>),
 }
 
 crate fn parse_external_mod(
     sess: &Session,
-    id: Ident,
+    ident: Ident,
     span: Span, // The span to blame on errors.
-    Directory { mut ownership, path }: Directory,
+    module: &ModuleData,
+    mut dir_ownership: DirOwnership,
     attrs: &mut Vec<Attribute>,
-    pop_mod_stack: &mut bool,
-) -> (Vec<P<Item>>, Span, Directory) {
+) -> ParsedExternalMod {
     // We bail on the first error, but that error does not cause a fatal error... (1)
-    let result: PResult<'_, _> = try {
+    let result: Result<_, ModError<'_>> = try {
         // Extract the file path and the new ownership.
-        let mp = submod_path(sess, id, span, &attrs, ownership, &path)?;
-        ownership = mp.ownership;
+        let mp = mod_file_path(sess, ident, &attrs, &module.dir_path, dir_ownership)?;
+        dir_ownership = mp.dir_ownership;
 
         // Ensure file paths are acyclic.
-        let mut included_mod_stack = sess.parse_sess.included_mod_stack.borrow_mut();
-        error_on_circular_module(&sess.parse_sess, span, &mp.path, &included_mod_stack)?;
-        included_mod_stack.push(mp.path.clone());
-        *pop_mod_stack = true; // We have pushed, so notify caller.
-        drop(included_mod_stack);
+        if let Some(pos) = module.file_path_stack.iter().position(|p| p == &mp.file_path) {
+            Err(ModError::CircularInclusion(module.file_path_stack[pos..].to_vec()))?;
+        }
 
         // Actually parse the external file as a module.
-        let mut parser = new_parser_from_file(&sess.parse_sess, &mp.path, Some(span));
-        let (mut inner_attrs, items, inner_span) = parser.parse_mod(&token::Eof)?;
+        let mut parser = new_parser_from_file(&sess.parse_sess, &mp.file_path, Some(span));
+        let (mut inner_attrs, items, inner_span) =
+            parser.parse_mod(&token::Eof).map_err(|err| ModError::ParserError(err))?;
         attrs.append(&mut inner_attrs);
-        (items, inner_span)
+        (items, inner_span, mp.file_path)
     };
     // (1) ...instead, we return a dummy module.
-    let (items, inner_span) = result.map_err(|mut err| err.emit()).unwrap_or_default();
+    let (items, inner_span, file_path) =
+        result.map_err(|err| err.report(sess, span)).unwrap_or_default();
 
-    // Extract the directory path for submodules of  the module.
-    let path = sess.source_map().span_to_unmapped_path(inner_span);
-    let mut path = match path {
-        FileName::Real(name) => name.into_local_path(),
-        other => PathBuf::from(other.to_string()),
-    };
-    path.pop();
+    // Extract the directory path for submodules of the module.
+    let dir_path = file_path.parent().unwrap_or(&file_path).to_owned();
 
-    (items, inner_span, Directory { ownership, path })
+    ParsedExternalMod { items, inner_span, file_path, dir_path, dir_ownership }
 }
 
-fn error_on_circular_module<'a>(
-    sess: &'a ParseSess,
-    span: Span,
-    path: &Path,
-    included_mod_stack: &[PathBuf],
-) -> PResult<'a, ()> {
-    if let Some(i) = included_mod_stack.iter().position(|p| *p == path) {
-        let mut err = String::from("circular modules: ");
-        for p in &included_mod_stack[i..] {
-            err.push_str(&p.to_string_lossy());
-            err.push_str(" -> ");
-        }
-        err.push_str(&path.to_string_lossy());
-        return Err(sess.span_diagnostic.struct_span_err(span, &err[..]));
-    }
-    Ok(())
-}
-
-crate fn push_directory(
+crate fn mod_dir_path(
     sess: &Session,
-    id: Ident,
+    ident: Ident,
     attrs: &[Attribute],
-    Directory { mut ownership, mut path }: Directory,
-) -> Directory {
-    if let Some(filename) = sess.first_attr_value_str_by_name(attrs, sym::path) {
-        path.push(&*filename.as_str());
-        ownership = DirectoryOwnership::Owned { relative: None };
-    } else {
-        // We have to push on the current module name in the case of relative
-        // paths in order to ensure that any additional module paths from inline
-        // `mod x { ... }` come after the relative extension.
-        //
-        // For example, a `mod z { ... }` inside `x/y.rs` should set the current
-        // directory path to `/x/y/z`, not `/x/z` with a relative offset of `y`.
-        if let DirectoryOwnership::Owned { relative } = &mut ownership {
-            if let Some(ident) = relative.take() {
-                // Remove the relative offset.
-                path.push(&*ident.as_str());
-            }
+    module: &ModuleData,
+    mut dir_ownership: DirOwnership,
+) -> (PathBuf, DirOwnership) {
+    if let Some(file_path) = mod_file_path_from_attr(sess, attrs, &module.dir_path) {
+        // For inline modules file path from `#[path]` is actually the directory path
+        // for historical reasons, so we don't pop the last segment here.
+        return (file_path, DirOwnership::Owned { relative: None });
+    }
+
+    // We have to push on the current module name in the case of relative
+    // paths in order to ensure that any additional module paths from inline
+    // `mod x { ... }` come after the relative extension.
+    //
+    // For example, a `mod z { ... }` inside `x/y.rs` should set the current
+    // directory path to `/x/y/z`, not `/x/z` with a relative offset of `y`.
+    let mut dir_path = module.dir_path.clone();
+    if let DirOwnership::Owned { relative } = &mut dir_ownership {
+        if let Some(ident) = relative.take() {
+            // Remove the relative offset.
+            dir_path.push(&*ident.as_str());
         }
-        path.push(&*id.as_str());
     }
-    Directory { ownership, path }
+    dir_path.push(&*ident.as_str());
+
+    (dir_path, dir_ownership)
 }
 
-fn submod_path<'a>(
+fn mod_file_path<'a>(
     sess: &'a Session,
-    id: Ident,
-    span: Span,
+    ident: Ident,
     attrs: &[Attribute],
-    ownership: DirectoryOwnership,
     dir_path: &Path,
-) -> PResult<'a, ModulePathSuccess> {
-    if let Some(path) = submod_path_from_attr(sess, attrs, dir_path) {
-        let ownership = match path.file_name().and_then(|s| s.to_str()) {
-            // All `#[path]` files are treated as though they are a `mod.rs` file.
-            // This means that `mod foo;` declarations inside `#[path]`-included
-            // files are siblings,
-            //
-            // Note that this will produce weirdness when a file named `foo.rs` is
-            // `#[path]` included and contains a `mod foo;` declaration.
-            // If you encounter this, it's your own darn fault :P
-            Some(_) => DirectoryOwnership::Owned { relative: None },
-            _ => DirectoryOwnership::UnownedViaMod,
-        };
-        return Ok(ModulePathSuccess { ownership, path });
+    dir_ownership: DirOwnership,
+) -> Result<ModulePathSuccess, ModError<'a>> {
+    if let Some(file_path) = mod_file_path_from_attr(sess, attrs, dir_path) {
+        // All `#[path]` files are treated as though they are a `mod.rs` file.
+        // This means that `mod foo;` declarations inside `#[path]`-included
+        // files are siblings,
+        //
+        // Note that this will produce weirdness when a file named `foo.rs` is
+        // `#[path]` included and contains a `mod foo;` declaration.
+        // If you encounter this, it's your own darn fault :P
+        let dir_ownership = DirOwnership::Owned { relative: None };
+        return Ok(ModulePathSuccess { file_path, dir_ownership });
     }
 
-    let relative = match ownership {
-        DirectoryOwnership::Owned { relative } => relative,
-        DirectoryOwnership::UnownedViaBlock | DirectoryOwnership::UnownedViaMod => None,
+    let relative = match dir_ownership {
+        DirOwnership::Owned { relative } => relative,
+        DirOwnership::UnownedViaBlock => None,
     };
-    let ModulePath { path_exists, name, result } =
-        default_submod_path(&sess.parse_sess, id, span, relative, dir_path);
-    match ownership {
-        DirectoryOwnership::Owned { .. } => Ok(result?),
-        DirectoryOwnership::UnownedViaBlock => {
-            let _ = result.map_err(|mut err| err.cancel());
-            error_decl_mod_in_block(&sess.parse_sess, span, path_exists, &name)
-        }
-        DirectoryOwnership::UnownedViaMod => {
-            let _ = result.map_err(|mut err| err.cancel());
-            error_cannot_declare_mod_here(&sess.parse_sess, span, path_exists, &name)
-        }
+    let result = default_submod_path(&sess.parse_sess, ident, relative, dir_path);
+    match dir_ownership {
+        DirOwnership::Owned { .. } => result,
+        DirOwnership::UnownedViaBlock => Err(ModError::ModInBlock(match result {
+            Ok(_) | Err(ModError::MultipleCandidates(..)) => Some(ident),
+            _ => None,
+        })),
     }
 }
 
-fn error_decl_mod_in_block<'a, T>(
-    sess: &'a ParseSess,
-    span: Span,
-    path_exists: bool,
-    name: &str,
-) -> PResult<'a, T> {
-    let msg = "Cannot declare a non-inline module inside a block unless it has a path attribute";
-    let mut err = sess.span_diagnostic.struct_span_err(span, msg);
-    if path_exists {
-        let msg = format!("Maybe `use` the module `{}` instead of redeclaring it", name);
-        err.span_note(span, &msg);
-    }
-    Err(err)
-}
-
-fn error_cannot_declare_mod_here<'a, T>(
-    sess: &'a ParseSess,
-    span: Span,
-    path_exists: bool,
-    name: &str,
-) -> PResult<'a, T> {
-    let mut err =
-        sess.span_diagnostic.struct_span_err(span, "cannot declare a new module at this location");
-    if !span.is_dummy() {
-        if let FileName::Real(src_name) = sess.source_map().span_to_filename(span) {
-            let src_path = src_name.into_local_path();
-            if let Some(stem) = src_path.file_stem() {
-                let mut dest_path = src_path.clone();
-                dest_path.set_file_name(stem);
-                dest_path.push("mod.rs");
-                err.span_note(
-                    span,
-                    &format!(
-                        "maybe move this module `{}` to its own directory via `{}`",
-                        src_path.display(),
-                        dest_path.display()
-                    ),
-                );
-            }
-        }
-    }
-    if path_exists {
-        err.span_note(
-            span,
-            &format!("... or maybe `use` the module `{}` instead of possibly redeclaring it", name),
-        );
-    }
-    Err(err)
-}
-
 /// Derive a submodule path from the first found `#[path = "path_string"]`.
 /// The provided `dir_path` is joined with the `path_string`.
-pub(super) fn submod_path_from_attr(
+fn mod_file_path_from_attr(
     sess: &Session,
     attrs: &[Attribute],
     dir_path: &Path,
 ) -> Option<PathBuf> {
     // Extract path string from first `#[path = "path_string"]` attribute.
-    let path_string = sess.first_attr_value_str_by_name(attrs, sym::path)?;
-    let path_string = path_string.as_str();
+    let path_string = sess.first_attr_value_str_by_name(attrs, sym::path)?.as_str();
 
     // On windows, the base path might have the form
     // `\\?\foo\bar` in which case it does not tolerate
@@ -242,15 +165,14 @@ pub(super) fn submod_path_from_attr(
 // Public for rustfmt usage.
 pub fn default_submod_path<'a>(
     sess: &'a ParseSess,
-    id: Ident,
-    span: Span,
+    ident: Ident,
     relative: Option<Ident>,
     dir_path: &Path,
-) -> ModulePath<'a> {
+) -> Result<ModulePathSuccess, ModError<'a>> {
     // If we're in a foo.rs file instead of a mod.rs file,
     // we need to look for submodules in
-    // `./foo/<id>.rs` and `./foo/<id>/mod.rs` rather than
-    // `./<id>.rs` and `./<id>/mod.rs`.
+    // `./foo/<ident>.rs` and `./foo/<ident>/mod.rs` rather than
+    // `./<ident>.rs` and `./<ident>/mod.rs`.
     let relative_prefix_string;
     let relative_prefix = if let Some(ident) = relative {
         relative_prefix_string = format!("{}{}", ident.name, path::MAIN_SEPARATOR);
@@ -259,7 +181,7 @@ pub fn default_submod_path<'a>(
         ""
     };
 
-    let mod_name = id.name.to_string();
+    let mod_name = ident.name.to_string();
     let default_path_str = format!("{}{}.rs", relative_prefix, mod_name);
     let secondary_path_str =
         format!("{}{}{}mod.rs", relative_prefix, mod_name, path::MAIN_SEPARATOR);
@@ -268,44 +190,74 @@ pub fn default_submod_path<'a>(
     let default_exists = sess.source_map().file_exists(&default_path);
     let secondary_exists = sess.source_map().file_exists(&secondary_path);
 
-    let result = match (default_exists, secondary_exists) {
+    match (default_exists, secondary_exists) {
         (true, false) => Ok(ModulePathSuccess {
-            path: default_path,
-            ownership: DirectoryOwnership::Owned { relative: Some(id) },
+            file_path: default_path,
+            dir_ownership: DirOwnership::Owned { relative: Some(ident) },
         }),
         (false, true) => Ok(ModulePathSuccess {
-            path: secondary_path,
-            ownership: DirectoryOwnership::Owned { relative: None },
+            file_path: secondary_path,
+            dir_ownership: DirOwnership::Owned { relative: None },
         }),
-        (false, false) => {
-            let mut err = struct_span_err!(
-                sess.span_diagnostic,
-                span,
-                E0583,
-                "file not found for module `{}`",
-                mod_name,
-            );
-            err.help(&format!(
-                "to create the module `{}`, create file \"{}\"",
-                mod_name,
-                default_path.display(),
-            ));
-            Err(err)
-        }
+        (false, false) => Err(ModError::FileNotFound(ident, default_path)),
         (true, true) => {
-            let mut err = struct_span_err!(
-                sess.span_diagnostic,
-                span,
-                E0761,
-                "file for module `{}` found at both {} and {}",
-                mod_name,
-                default_path_str,
-                secondary_path_str,
-            );
-            err.help("delete or rename one of them to remove the ambiguity");
-            Err(err)
+            Err(ModError::MultipleCandidates(ident, default_path_str, secondary_path_str))
         }
-    };
+    }
+}
 
-    ModulePath { name: mod_name, path_exists: default_exists || secondary_exists, result }
+impl ModError<'_> {
+    fn report(self, sess: &Session, span: Span) {
+        let diag = &sess.parse_sess.span_diagnostic;
+        match self {
+            ModError::CircularInclusion(file_paths) => {
+                let mut msg = String::from("circular modules: ");
+                for file_path in &file_paths {
+                    msg.push_str(&file_path.display().to_string());
+                    msg.push_str(" -> ");
+                }
+                msg.push_str(&file_paths[0].display().to_string());
+                diag.struct_span_err(span, &msg)
+            }
+            ModError::ModInBlock(ident) => {
+                let msg = "cannot declare a non-inline module inside a block unless it has a path attribute";
+                let mut err = diag.struct_span_err(span, msg);
+                if let Some(ident) = ident {
+                    let note =
+                        format!("maybe `use` the module `{}` instead of redeclaring it", ident);
+                    err.span_note(span, &note);
+                }
+                err
+            }
+            ModError::FileNotFound(ident, default_path) => {
+                let mut err = struct_span_err!(
+                    diag,
+                    span,
+                    E0583,
+                    "file not found for module `{}`",
+                    ident,
+                );
+                err.help(&format!(
+                    "to create the module `{}`, create file \"{}\"",
+                    ident,
+                    default_path.display(),
+                ));
+                err
+            }
+            ModError::MultipleCandidates(ident, default_path_short, secondary_path_short) => {
+                let mut err = struct_span_err!(
+                    diag,
+                    span,
+                    E0761,
+                    "file for module `{}` found at both {} and {}",
+                    ident,
+                    default_path_short,
+                    secondary_path_short,
+                );
+                err.help("delete or rename one of them to remove the ambiguity");
+                err
+            }
+            ModError::ParserError(err) => err,
+        }.emit()
+    }
 }
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 5217066bbefde..94be7a03a932b 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -302,8 +302,10 @@ fn configure_and_expand_inner<'a>(
             ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string())
         };
 
-        let extern_mod_loaded = |k: &ast::Crate, ident: Ident| {
-            pre_expansion_lint(sess, lint_store, k, &*ident.name.as_str())
+        let extern_mod_loaded = |ident: Ident, attrs, items, span| {
+            let krate = ast::Crate { attrs, items, span, proc_macros: vec![] };
+            pre_expansion_lint(sess, lint_store, &krate, &ident.name.as_str());
+            (krate.attrs, krate.items)
         };
         let mut ecx = ExtCtxt::new(&sess, cfg, &mut resolver, Some(&extern_mod_loaded));
 
@@ -988,7 +990,7 @@ fn encode_and_write_metadata(
             .unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err)));
         let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps);
         let metadata_filename = emit_metadata(tcx.sess, &metadata, &metadata_tmpdir);
-        if let Err(e) = fs::rename(&metadata_filename, &out_filename) {
+        if let Err(e) = util::non_durable_rename(&metadata_filename, &out_filename) {
             tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
         }
         if tcx.sess.opts.json_artifact_notifications {
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 798996263c700..0a30eda1ec406 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -694,6 +694,24 @@ pub fn build_output_filenames(
     }
 }
 
+#[cfg(not(target_os = "linux"))]
+pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> {
+    std::fs::rename(src, dst)
+}
+
+/// This function attempts to bypass the auto_da_alloc heuristic implemented by some filesystems
+/// such as btrfs and ext4. When renaming over a file that already exists then they will "helpfully"
+/// write back the source file before committing the rename in case a developer forgot some of
+/// the fsyncs in the open/write/fsync(file)/rename/fsync(dir) dance for atomic file updates.
+///
+/// To avoid triggering this heuristic we delete the destination first, if it exists.
+/// The cost of an extra syscall is much lower than getting descheduled for the sync IO.
+#[cfg(target_os = "linux")]
+pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> {
+    let _ = std::fs::remove_file(dst);
+    std::fs::rename(src, dst)
+}
+
 // Note: Also used by librustdoc, see PR #43348. Consider moving this struct elsewhere.
 //
 // FIXME: Currently the `everybody_loops` transformation is not applied to:
diff --git a/compiler/rustc_mir/src/transform/dest_prop.rs b/compiler/rustc_mir/src/transform/dest_prop.rs
index 0822ab73c4c67..f7568e1d929dd 100644
--- a/compiler/rustc_mir/src/transform/dest_prop.rs
+++ b/compiler/rustc_mir/src/transform/dest_prop.rs
@@ -127,6 +127,11 @@ pub struct DestinationPropagation;
 
 impl<'tcx> MirPass<'tcx> for DestinationPropagation {
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+        //  FIXME(#79191, #82678)
+        if !tcx.sess.opts.debugging_opts.unsound_mir_opts {
+            return;
+        }
+
         // Only run at mir-opt-level=3 or higher for now (we don't fix up debuginfo and remove
         // storage statements at the moment).
         if tcx.sess.mir_opt_level() < 3 {
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index f45d8d6c7a00e..7512f46988c91 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -72,6 +72,10 @@ impl<'a> Parser<'a> {
         let cursor_snapshot = self.token_cursor.clone();
 
         let (mut ret, trailing_token) = f(self, attrs.attrs)?;
+        let tokens = match ret.tokens_mut() {
+            Some(tokens) if tokens.is_none() => tokens,
+            _ => return Ok(ret),
+        };
 
         // Produces a `TokenStream` on-demand. Using `cursor_snapshot`
         // and `num_calls`, we can reconstruct the `TokenStream` seen
@@ -128,14 +132,14 @@ impl<'a> Parser<'a> {
             }
         }
 
-        let lazy_impl = LazyTokenStreamImpl {
+        *tokens = Some(LazyTokenStream::new(LazyTokenStreamImpl {
             start_token,
             num_calls,
             cursor_snapshot,
             desugar_doc_comments: self.desugar_doc_comments,
             append_unglued_token: self.token_cursor.append_unglued_token.clone(),
-        };
-        ret.finalize_tokens(LazyTokenStream::new(lazy_impl));
+        }));
+
         Ok(ret)
     }
 }
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 81b38347414e8..592773bfe1b44 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -13,7 +13,6 @@ use rustc_span::hygiene::ExpnId;
 use rustc_span::source_map::{FilePathMapping, SourceMap};
 use rustc_span::{MultiSpan, Span, Symbol};
 
-use std::path::PathBuf;
 use std::str;
 
 /// The set of keys (and, optionally, values) that define the compilation
@@ -122,8 +121,6 @@ pub struct ParseSess {
     pub missing_fragment_specifiers: Lock<FxHashMap<Span, NodeId>>,
     /// Places where raw identifiers were used. This is used for feature-gating raw identifiers.
     pub raw_identifier_spans: Lock<Vec<Span>>,
-    /// Used to determine and report recursive module inclusions.
-    pub included_mod_stack: Lock<Vec<PathBuf>>,
     source_map: Lrc<SourceMap>,
     pub buffered_lints: Lock<Vec<BufferedEarlyLint>>,
     /// Contains the spans of block expressions that could have been incomplete based on the
@@ -157,7 +154,6 @@ impl ParseSess {
             edition: ExpnId::root().expn_data().edition,
             missing_fragment_specifiers: Default::default(),
             raw_identifier_spans: Lock::new(Vec::new()),
-            included_mod_stack: Lock::new(vec![]),
             source_map,
             buffered_lints: Lock::new(vec![]),
             ambiguous_block_expr_parse: Lock::new(FxHashMap::default()),
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 9663760cba189..507eb1e1cbe89 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -344,6 +344,7 @@ symbols! {
         cfg_attr,
         cfg_attr_multi,
         cfg_doctest,
+        cfg_eval,
         cfg_panic,
         cfg_sanitize,
         cfg_target_feature,
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index 3e70ba81d4997..28fed9b8a14cd 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -1452,6 +1452,18 @@ pub(crate) mod builtin {
         /* compiler built-in */
     }
 
+    /// Expands all `#[cfg]` and `#[cfg_attr]` attributes in the code fragment it's applied to.
+    #[cfg(not(bootstrap))]
+    #[unstable(
+        feature = "cfg_eval",
+        issue = "82679",
+        reason = "`cfg_eval` is a recently implemented feature"
+    )]
+    #[rustc_builtin_macro]
+    pub macro cfg_eval($($tt:tt)*) {
+        /* compiler built-in */
+    }
+
     /// Unstable implementation detail of the `rustc` compiler, do not use.
     #[rustc_builtin_macro]
     #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs
index a1fbd8dec7505..5e8a8d252a238 100644
--- a/library/core/src/prelude/v1.rs
+++ b/library/core/src/prelude/v1.rs
@@ -81,3 +81,12 @@ pub use crate::macros::builtin::derive;
 )]
 #[doc(no_inline)]
 pub use crate::macros::builtin::cfg_accessible;
+
+#[cfg(not(bootstrap))]
+#[unstable(
+    feature = "cfg_eval",
+    issue = "82679",
+    reason = "`cfg_eval` is a recently implemented feature"
+)]
+#[doc(no_inline)]
+pub use crate::macros::builtin::cfg_eval;
diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs
index 00bf8b9af7384..9870cfc4c95c3 100644
--- a/library/std/src/io/impls.rs
+++ b/library/std/src/io/impls.rs
@@ -1,6 +1,7 @@
 #[cfg(test)]
 mod tests;
 
+use crate::alloc::Allocator;
 use crate::cmp;
 use crate::fmt;
 use crate::io::{
@@ -357,7 +358,7 @@ impl Write for &mut [u8] {
 /// Write is implemented for `Vec<u8>` by appending to the vector.
 /// The vector will grow as needed.
 #[stable(feature = "rust1", since = "1.0.0")]
-impl Write for Vec<u8> {
+impl<A: Allocator> Write for Vec<u8, A> {
     #[inline]
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
         self.extend_from_slice(buf);
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 72b86338d2c97..acdf7550fe71d 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -234,6 +234,7 @@
 #![feature(box_syntax)]
 #![feature(c_variadic)]
 #![feature(cfg_accessible)]
+#![cfg_attr(not(bootstrap), feature(cfg_eval))]
 #![feature(cfg_target_has_atomic)]
 #![feature(cfg_target_thread_local)]
 #![feature(char_error_internals)]
diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs
index ef9aec54a4ca2..7181dc6e710e4 100644
--- a/library/std/src/prelude/v1.rs
+++ b/library/std/src/prelude/v1.rs
@@ -67,6 +67,15 @@ pub use core::prelude::v1::derive;
 #[doc(hidden)]
 pub use core::prelude::v1::cfg_accessible;
 
+#[cfg(not(bootstrap))]
+#[unstable(
+    feature = "cfg_eval",
+    issue = "82679",
+    reason = "`cfg_eval` is a recently implemented feature"
+)]
+#[doc(hidden)]
+pub use core::prelude::v1::cfg_eval;
+
 // The file so far is equivalent to src/libcore/prelude/v1.rs,
 // and below to src/liballoc/prelude.rs.
 // Those files are duplicated rather than using glob imports
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 9317c89625d06..22a1eb6370235 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -397,6 +397,7 @@ impl<'a> Builder<'a> {
                 test::Crate,
                 test::CrateLibrustc,
                 test::CrateRustdoc,
+                test::CrateRustdocJsonTypes,
                 test::Linkcheck,
                 test::TierCheck,
                 test::Cargotest,
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index c0cd24dd81f01..03ae684e23fed 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -1922,6 +1922,77 @@ impl Step for CrateRustdoc {
     }
 }
 
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct CrateRustdocJsonTypes {
+    host: TargetSelection,
+    test_kind: TestKind,
+}
+
+impl Step for CrateRustdocJsonTypes {
+    type Output = ();
+    const DEFAULT: bool = true;
+    const ONLY_HOSTS: bool = true;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.path("src/rustdoc-json-types")
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        let builder = run.builder;
+
+        let test_kind = builder.kind.into();
+
+        builder.ensure(CrateRustdocJsonTypes { host: run.target, test_kind });
+    }
+
+    fn run(self, builder: &Builder<'_>) {
+        let test_kind = self.test_kind;
+        let target = self.host;
+
+        // Use the previous stage compiler to reuse the artifacts that are
+        // created when running compiletest for src/test/rustdoc. If this used
+        // `compiler`, then it would cause rustdoc to be built *again*, which
+        // isn't really necessary.
+        let compiler = builder.compiler_for(builder.top_stage, target, target);
+        builder.ensure(compile::Rustc { compiler, target });
+
+        let mut cargo = tool::prepare_tool_cargo(
+            builder,
+            compiler,
+            Mode::ToolRustc,
+            target,
+            test_kind.subcommand(),
+            "src/rustdoc-json-types",
+            SourceType::InTree,
+            &[],
+        );
+        if test_kind.subcommand() == "test" && !builder.fail_fast {
+            cargo.arg("--no-fail-fast");
+        }
+
+        cargo.arg("-p").arg("rustdoc-json-types");
+
+        cargo.arg("--");
+        cargo.args(&builder.config.cmd.test_args());
+
+        if self.host.contains("musl") {
+            cargo.arg("'-Ctarget-feature=-crt-static'");
+        }
+
+        if !builder.config.verbose_tests {
+            cargo.arg("--quiet");
+        }
+
+        builder.info(&format!(
+            "{} rustdoc-json-types stage{} ({} -> {})",
+            test_kind, compiler.stage, &compiler.host, target
+        ));
+        let _time = util::timeit(&builder);
+
+        try_run(builder, &mut cargo.into());
+    }
+}
+
 /// Some test suites are run inside emulators or on remote devices, and most
 /// of our test binaries are linked dynamically which means we need to ship
 /// the standard library and such to the emulator ahead of time. This step
diff --git a/src/etc/natvis/liballoc.natvis b/src/etc/natvis/liballoc.natvis
index cfaafc5734bce..9cc60fc7b47e5 100644
--- a/src/etc/natvis/liballoc.natvis
+++ b/src/etc/natvis/liballoc.natvis
@@ -75,4 +75,11 @@
       <ExpandedItem>ptr.pointer->data</ExpandedItem>
     </Expand>
   </Type>
+  <Type Name="alloc::borrow::Cow&lt;*&gt;">
+    <DisplayString Condition="RUST$ENUM$DISR == 0x0">Borrowed({__0})</DisplayString>
+    <DisplayString Condition="RUST$ENUM$DISR == 0x1">Owned({__0})</DisplayString>
+    <Expand>
+      <Item Name="[value]" ExcludeView="simple">__0</Item>
+    </Expand>
+  </Type>
 </AutoVisualizer>
diff --git a/src/etc/natvis/libcore.natvis b/src/etc/natvis/libcore.natvis
index 984a8bfb13c7c..9c3c26f597838 100644
--- a/src/etc/natvis/libcore.natvis
+++ b/src/etc/natvis/libcore.natvis
@@ -30,4 +30,19 @@
     </Expand>
   </Type>
 
+  <Type Name="core::result::Result&lt;*&gt;">
+    <DisplayString Condition="RUST$ENUM$DISR == 0x0">Ok({__0})</DisplayString>
+    <DisplayString Condition="RUST$ENUM$DISR == 0x1">Err({(*($T2*) &amp;__0)})</DisplayString>
+    <Expand>
+      <Item Name="[value]" Condition="RUST$ENUM$DISR == 0x0">__0</Item>
+      <Item Name="[value]" Condition="RUST$ENUM$DISR == 0x1">(*($T2*) &amp;__0)</Item>
+    </Expand>
+  </Type>
+
+  <Type Name="core::ptr::non_null::NonNull&lt;*&gt;">
+    <DisplayString>{(void*) pointer}</DisplayString>
+    <Expand>
+      <Item Name="[value]">*pointer</Item>
+    </Expand>
+  </Type>
 </AutoVisualizer>
\ No newline at end of file
diff --git a/src/etc/natvis/libstd.natvis b/src/etc/natvis/libstd.natvis
index 7e5ee7b13daf1..b0542b0c63303 100644
--- a/src/etc/natvis/libstd.natvis
+++ b/src/etc/natvis/libstd.natvis
@@ -72,4 +72,33 @@
       </CustomListItems>
     </Expand>
   </Type>
+
+  <Type Name="std::ffi::c_str::CString">
+    <DisplayString>{inner.data_ptr,s}</DisplayString>
+    <Expand>
+      <Synthetic Name="[chars]">
+        <Expand>
+          <ArrayItems>
+            <Size>inner.length</Size>
+            <ValuePointer>(char*)inner.data_ptr</ValuePointer>
+          </ArrayItems>
+        </Expand>
+      </Synthetic>
+    </Expand>
+  </Type>
+
+  <Type Name="std::ffi::c_str::CStr">
+    <DisplayString>{(char*) inner}</DisplayString>
+    <Expand>
+      <Synthetic Name="[chars]">
+        <DisplayString>{(char*) inner}</DisplayString>
+        <Expand>
+          <ArrayItems>
+            <Size>strlen((char *) inner) + 1</Size>
+            <ValuePointer>(char*)inner</ValuePointer>
+          </ArrayItems>
+        </Expand>
+      </Synthetic>
+    </Expand>
+  </Type>
 </AutoVisualizer>
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 47e4f3f0aa320..063d8aab1ec3e 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -23,7 +23,6 @@ use std::collections::HashSet;
 
 impl JsonRenderer<'_> {
     pub(super) fn convert_item(&self, item: clean::Item) -> Option<Item> {
-        let item_type = ItemType::from(&item);
         let deprecation = item.deprecation(self.tcx);
         let clean::Item { source, name, attrs, kind, visibility, def_id } = item;
         let inner = match *kind {
@@ -50,7 +49,6 @@ impl JsonRenderer<'_> {
                 .map(rustc_ast_pretty::pprust::attribute_to_string)
                 .collect(),
             deprecation: deprecation.map(from_deprecation),
-            kind: item_type.into(),
             inner,
         })
     }
@@ -154,30 +152,30 @@ crate fn from_def_id(did: DefId) -> Id {
 fn from_clean_item_kind(item: clean::ItemKind, tcx: TyCtxt<'_>, name: &Option<Symbol>) -> ItemEnum {
     use clean::ItemKind::*;
     match item {
-        ModuleItem(m) => ItemEnum::ModuleItem(m.into()),
-        ImportItem(i) => ItemEnum::ImportItem(i.into()),
-        StructItem(s) => ItemEnum::StructItem(s.into()),
-        UnionItem(u) => ItemEnum::UnionItem(u.into()),
-        StructFieldItem(f) => ItemEnum::StructFieldItem(f.into()),
-        EnumItem(e) => ItemEnum::EnumItem(e.into()),
-        VariantItem(v) => ItemEnum::VariantItem(v.into()),
-        FunctionItem(f) => ItemEnum::FunctionItem(f.into()),
-        ForeignFunctionItem(f) => ItemEnum::FunctionItem(f.into()),
-        TraitItem(t) => ItemEnum::TraitItem(t.into()),
-        TraitAliasItem(t) => ItemEnum::TraitAliasItem(t.into()),
-        MethodItem(m, _) => ItemEnum::MethodItem(from_function_method(m, true)),
-        TyMethodItem(m) => ItemEnum::MethodItem(from_function_method(m, false)),
-        ImplItem(i) => ItemEnum::ImplItem(i.into()),
-        StaticItem(s) => ItemEnum::StaticItem(from_clean_static(s, tcx)),
-        ForeignStaticItem(s) => ItemEnum::StaticItem(from_clean_static(s, tcx)),
-        ForeignTypeItem => ItemEnum::ForeignTypeItem,
-        TypedefItem(t, _) => ItemEnum::TypedefItem(t.into()),
-        OpaqueTyItem(t) => ItemEnum::OpaqueTyItem(t.into()),
-        ConstantItem(c) => ItemEnum::ConstantItem(c.into()),
-        MacroItem(m) => ItemEnum::MacroItem(m.source),
-        ProcMacroItem(m) => ItemEnum::ProcMacroItem(m.into()),
-        AssocConstItem(t, s) => ItemEnum::AssocConstItem { type_: t.into(), default: s },
-        AssocTypeItem(g, t) => ItemEnum::AssocTypeItem {
+        ModuleItem(m) => ItemEnum::Module(m.into()),
+        ImportItem(i) => ItemEnum::Import(i.into()),
+        StructItem(s) => ItemEnum::Struct(s.into()),
+        UnionItem(u) => ItemEnum::Union(u.into()),
+        StructFieldItem(f) => ItemEnum::StructField(f.into()),
+        EnumItem(e) => ItemEnum::Enum(e.into()),
+        VariantItem(v) => ItemEnum::Variant(v.into()),
+        FunctionItem(f) => ItemEnum::Function(f.into()),
+        ForeignFunctionItem(f) => ItemEnum::Function(f.into()),
+        TraitItem(t) => ItemEnum::Trait(t.into()),
+        TraitAliasItem(t) => ItemEnum::TraitAlias(t.into()),
+        MethodItem(m, _) => ItemEnum::Method(from_function_method(m, true)),
+        TyMethodItem(m) => ItemEnum::Method(from_function_method(m, false)),
+        ImplItem(i) => ItemEnum::Impl(i.into()),
+        StaticItem(s) => ItemEnum::Static(from_clean_static(s, tcx)),
+        ForeignStaticItem(s) => ItemEnum::Static(from_clean_static(s, tcx)),
+        ForeignTypeItem => ItemEnum::ForeignType,
+        TypedefItem(t, _) => ItemEnum::Typedef(t.into()),
+        OpaqueTyItem(t) => ItemEnum::OpaqueTy(t.into()),
+        ConstantItem(c) => ItemEnum::Constant(c.into()),
+        MacroItem(m) => ItemEnum::Macro(m.source),
+        ProcMacroItem(m) => ItemEnum::ProcMacro(m.into()),
+        AssocConstItem(t, s) => ItemEnum::AssocConst { type_: t.into(), default: s },
+        AssocTypeItem(g, t) => ItemEnum::AssocType {
             bounds: g.into_iter().map(Into::into).collect(),
             default: t.map(Into::into),
         },
@@ -185,7 +183,7 @@ fn from_clean_item_kind(item: clean::ItemKind, tcx: TyCtxt<'_>, name: &Option<Sy
         PrimitiveItem(_) | KeywordItem(_) => {
             panic!("{:?} is not supported for JSON output", item)
         }
-        ExternCrateItem { ref src } => ItemEnum::ExternCrateItem {
+        ExternCrateItem { ref src } => ItemEnum::ExternCrate {
             name: name.as_ref().unwrap().to_string(),
             rename: src.map(|x| x.to_string()),
         },
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index 863610baed484..8b0c049c05bc7 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -108,8 +108,7 @@ impl JsonRenderer<'tcx> {
                                 .last()
                                 .map(Clone::clone),
                             visibility: types::Visibility::Public,
-                            kind: types::ItemKind::Trait,
-                            inner: types::ItemEnum::TraitItem(trait_item.clone().into()),
+                            inner: types::ItemEnum::Trait(trait_item.clone().into()),
                             source: None,
                             docs: Default::default(),
                             links: Default::default(),
@@ -158,11 +157,11 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
 
         let id = item.def_id;
         if let Some(mut new_item) = self.convert_item(item) {
-            if let types::ItemEnum::TraitItem(ref mut t) = new_item.inner {
+            if let types::ItemEnum::Trait(ref mut t) = new_item.inner {
                 t.implementors = self.get_trait_implementors(id)
-            } else if let types::ItemEnum::StructItem(ref mut s) = new_item.inner {
+            } else if let types::ItemEnum::Struct(ref mut s) = new_item.inner {
                 s.impls = self.get_impls(id)
-            } else if let types::ItemEnum::EnumItem(ref mut e) = new_item.inner {
+            } else if let types::ItemEnum::Enum(ref mut e) = new_item.inner {
                 e.impls = self.get_impls(id)
             }
             let removed = self.index.borrow_mut().insert(from_def_id(id), new_item.clone());
diff --git a/src/rustdoc-json-types/Cargo.toml b/src/rustdoc-json-types/Cargo.toml
index 7bba16a68b96c..9c02d5e645bfe 100644
--- a/src/rustdoc-json-types/Cargo.toml
+++ b/src/rustdoc-json-types/Cargo.toml
@@ -9,3 +9,6 @@ path = "lib.rs"
 
 [dependencies]
 serde = { version = "1.0", features = ["derive"] }
+
+[dev-dependencies]
+serde_json = "1.0"
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index 6188b87d2c617..4e7794fe61064 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -76,7 +76,7 @@ pub struct Item {
     /// Stringified versions of the attributes on this item (e.g. `"#[inline]"`)
     pub attrs: Vec<String>,
     pub deprecation: Option<Deprecation>,
-    pub kind: ItemKind,
+    #[serde(flatten)]
     pub inner: ItemEnum,
 }
 
@@ -185,48 +185,48 @@ pub enum ItemKind {
 }
 
 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
-#[serde(untagged)]
+#[serde(tag = "kind", content = "inner", rename_all = "snake_case")]
 pub enum ItemEnum {
-    ModuleItem(Module),
-    ExternCrateItem {
+    Module(Module),
+    ExternCrate {
         name: String,
         rename: Option<String>,
     },
-    ImportItem(Import),
+    Import(Import),
 
-    UnionItem(Union),
-    StructItem(Struct),
-    StructFieldItem(Type),
-    EnumItem(Enum),
-    VariantItem(Variant),
+    Union(Union),
+    Struct(Struct),
+    StructField(Type),
+    Enum(Enum),
+    Variant(Variant),
 
-    FunctionItem(Function),
+    Function(Function),
 
-    TraitItem(Trait),
-    TraitAliasItem(TraitAlias),
-    MethodItem(Method),
-    ImplItem(Impl),
+    Trait(Trait),
+    TraitAlias(TraitAlias),
+    Method(Method),
+    Impl(Impl),
 
-    TypedefItem(Typedef),
-    OpaqueTyItem(OpaqueTy),
-    ConstantItem(Constant),
+    Typedef(Typedef),
+    OpaqueTy(OpaqueTy),
+    Constant(Constant),
 
-    StaticItem(Static),
+    Static(Static),
 
     /// `type`s from an extern block
-    ForeignTypeItem,
+    ForeignType,
 
     /// Declarative macro_rules! macro
-    MacroItem(String),
-    ProcMacroItem(ProcMacro),
+    Macro(String),
+    ProcMacro(ProcMacro),
 
-    AssocConstItem {
+    AssocConst {
         #[serde(rename = "type")]
         type_: Type,
         /// e.g. `const X: usize = 5;`
         default: Option<String>,
     },
-    AssocTypeItem {
+    AssocType {
         bounds: Vec<GenericBound>,
         /// e.g. `type X = usize;`
         default: Option<Type>,
@@ -508,3 +508,6 @@ pub struct Static {
     pub mutable: bool,
     pub expr: String,
 }
+
+#[cfg(test)]
+mod tests;
diff --git a/src/rustdoc-json-types/tests.rs b/src/rustdoc-json-types/tests.rs
new file mode 100644
index 0000000000000..e7f6447ed8753
--- /dev/null
+++ b/src/rustdoc-json-types/tests.rs
@@ -0,0 +1,34 @@
+use super::*;
+
+#[test]
+fn test_struct_info_roundtrip() {
+    let s = ItemEnum::Struct(Struct {
+        struct_type: StructType::Plain,
+        generics: Generics { params: vec![], where_predicates: vec![] },
+        fields_stripped: false,
+        fields: vec![],
+        impls: vec![],
+    });
+
+    let struct_json = serde_json::to_string(&s).unwrap();
+
+    let de_s = serde_json::from_str(&struct_json).unwrap();
+
+    assert_eq!(s, de_s);
+}
+
+#[test]
+fn test_union_info_roundtrip() {
+    let u = ItemEnum::Union(Union {
+        generics: Generics { params: vec![], where_predicates: vec![] },
+        fields_stripped: false,
+        fields: vec![],
+        impls: vec![],
+    });
+
+    let union_json = serde_json::to_string(&u).unwrap();
+
+    let de_u = serde_json::from_str(&union_json).unwrap();
+
+    assert_eq!(u, de_u);
+}
diff --git a/src/test/mir-opt/deduplicate_blocks.is_line_doc_comment_2.DeduplicateBlocks.diff b/src/test/mir-opt/deduplicate_blocks.is_line_doc_comment_2.DeduplicateBlocks.diff
index b6e4469e87069..b0c97f4237818 100644
--- a/src/test/mir-opt/deduplicate_blocks.is_line_doc_comment_2.DeduplicateBlocks.diff
+++ b/src/test/mir-opt/deduplicate_blocks.is_line_doc_comment_2.DeduplicateBlocks.diff
@@ -5,22 +5,26 @@
       debug s => _1;                       // in scope 0 at $DIR/deduplicate_blocks.rs:2:36: 2:37
       let mut _0: bool;                    // return place in scope 0 at $DIR/deduplicate_blocks.rs:2:48: 2:52
       let mut _2: &[u8];                   // in scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:23
-      let mut _3: usize;                   // in scope 0 at $DIR/deduplicate_blocks.rs:5:9: 5:31
-      let mut _4: bool;                    // in scope 0 at $DIR/deduplicate_blocks.rs:5:9: 5:31
-      let mut _5: usize;                   // in scope 0 at $DIR/deduplicate_blocks.rs:4:9: 4:37
-      let mut _6: bool;                    // in scope 0 at $DIR/deduplicate_blocks.rs:4:9: 4:37
+      let mut _3: &str;                    // in scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:12
+      let mut _4: usize;                   // in scope 0 at $DIR/deduplicate_blocks.rs:5:9: 5:31
+      let mut _5: bool;                    // in scope 0 at $DIR/deduplicate_blocks.rs:5:9: 5:31
+      let mut _6: usize;                   // in scope 0 at $DIR/deduplicate_blocks.rs:4:9: 4:37
+      let mut _7: bool;                    // in scope 0 at $DIR/deduplicate_blocks.rs:4:9: 4:37
       scope 1 (inlined core::str::<impl str>::as_bytes) { // at $DIR/deduplicate_blocks.rs:3:11: 3:23
-          debug self => _7;                // in scope 1 at $DIR/deduplicate_blocks.rs:3:11: 3:23
-          let mut _7: &str;                // in scope 1 at $DIR/deduplicate_blocks.rs:3:11: 3:23
+          debug self => _3;                // in scope 1 at $DIR/deduplicate_blocks.rs:3:11: 3:23
+          let mut _8: &str;                // in scope 1 at $DIR/deduplicate_blocks.rs:3:11: 3:23
           scope 2 {
           }
       }
   
       bb0: {
           StorageLive(_2);                 // scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:23
-          _7 = _1;                         // scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:12
--         _2 = transmute::<&str, &[u8]>(move _7) -> bb14; // scope 2 at $DIR/deduplicate_blocks.rs:3:11: 3:23
-+         _2 = transmute::<&str, &[u8]>(move _7) -> bb12; // scope 2 at $DIR/deduplicate_blocks.rs:3:11: 3:23
+          StorageLive(_3);                 // scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:12
+          _3 = _1;                         // scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:12
+          StorageLive(_8);                 // scope 2 at $DIR/deduplicate_blocks.rs:3:11: 3:23
+          _8 = _3;                         // scope 2 at $DIR/deduplicate_blocks.rs:3:11: 3:23
+-         _2 = transmute::<&str, &[u8]>(move _8) -> bb14; // scope 2 at $DIR/deduplicate_blocks.rs:3:11: 3:23
++         _2 = transmute::<&str, &[u8]>(move _8) -> bb12; // scope 2 at $DIR/deduplicate_blocks.rs:3:11: 3:23
                                            // mir::Constant
                                            // + span: $DIR/deduplicate_blocks.rs:3:11: 3:23
                                            // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&str) -> &[u8] {std::intrinsics::transmute::<&str, &[u8]>}, val: Value(Scalar(<ZST>)) }
@@ -44,9 +48,9 @@
       }
   
       bb5: {
-          _3 = Len((*_2));                 // scope 0 at $DIR/deduplicate_blocks.rs:5:9: 5:31
-          _4 = Ge(move _3, const 3_usize); // scope 0 at $DIR/deduplicate_blocks.rs:5:9: 5:31
-          switchInt(move _4) -> [false: bb9, otherwise: bb6]; // scope 0 at $DIR/deduplicate_blocks.rs:5:9: 5:31
+          _4 = Len((*_2));                 // scope 0 at $DIR/deduplicate_blocks.rs:5:9: 5:31
+          _5 = Ge(move _4, const 3_usize); // scope 0 at $DIR/deduplicate_blocks.rs:5:9: 5:31
+          switchInt(move _5) -> [false: bb9, otherwise: bb6]; // scope 0 at $DIR/deduplicate_blocks.rs:5:9: 5:31
       }
   
       bb6: {
@@ -93,9 +97,11 @@
   
 -     bb14: {
 +     bb12: {
-          _5 = Len((*_2));                 // scope 0 at $DIR/deduplicate_blocks.rs:4:9: 4:37
-          _6 = Ge(move _5, const 4_usize); // scope 0 at $DIR/deduplicate_blocks.rs:4:9: 4:37
-          switchInt(move _6) -> [false: bb5, otherwise: bb1]; // scope 0 at $DIR/deduplicate_blocks.rs:4:9: 4:37
+          StorageDead(_8);                 // scope 2 at $DIR/deduplicate_blocks.rs:3:11: 3:23
+          StorageDead(_3);                 // scope 0 at $DIR/deduplicate_blocks.rs:3:22: 3:23
+          _6 = Len((*_2));                 // scope 0 at $DIR/deduplicate_blocks.rs:4:9: 4:37
+          _7 = Ge(move _6, const 4_usize); // scope 0 at $DIR/deduplicate_blocks.rs:4:9: 4:37
+          switchInt(move _7) -> [false: bb5, otherwise: bb1]; // scope 0 at $DIR/deduplicate_blocks.rs:4:9: 4:37
       }
   }
   
diff --git a/src/test/mir-opt/dest-prop/branch.rs b/src/test/mir-opt/dest-prop/branch.rs
index 7e0e40671ddb2..fffcf82b3f1d8 100644
--- a/src/test/mir-opt/dest-prop/branch.rs
+++ b/src/test/mir-opt/dest-prop/branch.rs
@@ -1,5 +1,5 @@
 //! Tests that assignment in both branches of an `if` are eliminated.
-
+// compile-flags: -Zunsound-mir-opts
 fn val() -> i32 {
     1
 }
diff --git a/src/test/mir-opt/dest-prop/copy_propagation_arg.rs b/src/test/mir-opt/dest-prop/copy_propagation_arg.rs
index b5188a8b4b260..41796247789aa 100644
--- a/src/test/mir-opt/dest-prop/copy_propagation_arg.rs
+++ b/src/test/mir-opt/dest-prop/copy_propagation_arg.rs
@@ -1,6 +1,6 @@
 // Check that DestinationPropagation does not propagate an assignment to a function argument
 // (doing so can break usages of the original argument value)
-
+// compile-flags: -Zunsound-mir-opts
 fn dummy(x: u8) -> u8 {
     x
 }
diff --git a/src/test/mir-opt/dest-prop/cycle.rs b/src/test/mir-opt/dest-prop/cycle.rs
index 7fbffb1335946..c9187d408675c 100644
--- a/src/test/mir-opt/dest-prop/cycle.rs
+++ b/src/test/mir-opt/dest-prop/cycle.rs
@@ -1,5 +1,5 @@
 //! Tests that cyclic assignments don't hang DestinationPropagation, and result in reasonable code.
-
+// compile-flags: -Zunsound-mir-opts
 fn val() -> i32 {
     1
 }
diff --git a/src/test/mir-opt/dest-prop/simple.rs b/src/test/mir-opt/dest-prop/simple.rs
index 4655f96699874..3627d479a9aa3 100644
--- a/src/test/mir-opt/dest-prop/simple.rs
+++ b/src/test/mir-opt/dest-prop/simple.rs
@@ -1,5 +1,5 @@
 //! Copy of `nrvo-simple.rs`, to ensure that full dest-prop handles it too.
-
+// compile-flags: -Zunsound-mir-opts
 // EMIT_MIR simple.nrvo.DestinationPropagation.diff
 fn nrvo(init: fn(&mut [u8; 1024])) -> [u8; 1024] {
     let mut buf = [0; 1024];
diff --git a/src/test/mir-opt/dest-prop/union.rs b/src/test/mir-opt/dest-prop/union.rs
index b9d831389e8b3..0ac9661a66a31 100644
--- a/src/test/mir-opt/dest-prop/union.rs
+++ b/src/test/mir-opt/dest-prop/union.rs
@@ -1,5 +1,5 @@
 //! Tests that projections through unions cancel `DestinationPropagation`.
-
+// compile-flags: -Zunsound-mir-opts
 fn val() -> u32 {
     1
 }
diff --git a/src/test/mir-opt/inline/inline_closure_captures.foo.Inline.after.mir b/src/test/mir-opt/inline/inline_closure_captures.foo.Inline.after.mir
index a861eab39d337..a7c2c93aad23a 100644
--- a/src/test/mir-opt/inline/inline_closure_captures.foo.Inline.after.mir
+++ b/src/test/mir-opt/inline/inline_closure_captures.foo.Inline.after.mir
@@ -17,7 +17,8 @@ fn foo(_1: T, _2: i32) -> (i32, T) {
             debug _q => _9;              // in scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9
             debug q => (*((*_6).0: &i32)); // in scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9
             debug t => (*((*_6).1: &T)); // in scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9
-            let mut _10: T;              // in scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9
+            let mut _10: i32;            // in scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9
+            let mut _11: T;              // in scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9
         }
     }
 
@@ -39,10 +40,13 @@ fn foo(_1: T, _2: i32) -> (i32, T) {
         (_7.0: i32) = move _8;           // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:9
         StorageLive(_9);                 // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:9
         _9 = move (_7.0: i32);           // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:9
-        (_0.0: i32) = (*((*_6).0: &i32)); // scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9
         StorageLive(_10);                // scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9
-        _10 = (*((*_6).1: &T));          // scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9
-        (_0.1: T) = move _10;            // scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9
+        _10 = (*((*_6).0: &i32));        // scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9
+        StorageLive(_11);                // scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9
+        _11 = (*((*_6).1: &T));          // scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9
+        (_0.0: i32) = move _10;          // scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9
+        (_0.1: T) = move _11;            // scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9
+        StorageDead(_11);                // scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9
         StorageDead(_10);                // scope 2 at $DIR/inline-closure-captures.rs:12:5: 12:9
         StorageDead(_9);                 // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:9
         StorageDead(_8);                 // scope 1 at $DIR/inline-closure-captures.rs:12:8: 12:9
diff --git a/src/test/mir-opt/inline/inline_diverging.h.Inline.diff b/src/test/mir-opt/inline/inline_diverging.h.Inline.diff
index e945629b6138b..e2fad5b60ebfa 100644
--- a/src/test/mir-opt/inline/inline_diverging.h.Inline.diff
+++ b/src/test/mir-opt/inline/inline_diverging.h.Inline.diff
@@ -5,18 +5,20 @@
       let mut _0: ();                      // return place in scope 0 at $DIR/inline-diverging.rs:21:12: 21:12
       let _1: (!, !);                      // in scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
 +     let mut _2: fn() -> ! {sleep};       // in scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
-+     let mut _7: ();                      // in scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
-+     let mut _8: ();                      // in scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
++     let mut _9: ();                      // in scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
++     let mut _10: ();                     // in scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
 +     scope 1 (inlined call_twice::<!, fn() -> ! {sleep}>) { // at $DIR/inline-diverging.rs:22:5: 22:22
 +         debug f => _2;                   // in scope 1 at $DIR/inline-diverging.rs:22:5: 22:22
 +         let _3: !;                       // in scope 1 at $DIR/inline-diverging.rs:22:5: 22:22
 +         let mut _4: &fn() -> ! {sleep};  // in scope 1 at $DIR/inline-diverging.rs:22:5: 22:22
-+         let mut _5: &fn() -> ! {sleep};  // in scope 1 at $DIR/inline-diverging.rs:22:5: 22:22
-+         let mut _6: !;                   // in scope 1 at $DIR/inline-diverging.rs:22:5: 22:22
++         let mut _6: &fn() -> ! {sleep};  // in scope 1 at $DIR/inline-diverging.rs:22:5: 22:22
++         let mut _7: !;                   // in scope 1 at $DIR/inline-diverging.rs:22:5: 22:22
++         let mut _8: !;                   // in scope 1 at $DIR/inline-diverging.rs:22:5: 22:22
 +         scope 2 {
 +             debug a => _3;               // in scope 2 at $DIR/inline-diverging.rs:22:5: 22:22
++             let _5: !;                   // in scope 2 at $DIR/inline-diverging.rs:22:5: 22:22
 +             scope 3 {
-+                 debug b => _6;           // in scope 3 at $DIR/inline-diverging.rs:22:5: 22:22
++                 debug b => _5;           // in scope 3 at $DIR/inline-diverging.rs:22:5: 22:22
 +             }
 +             scope 6 (inlined <fn() -> ! {sleep} as Fn<()>>::call - shim(fn() -> ! {sleep})) { // at $DIR/inline-diverging.rs:22:5: 22:22
 +                 scope 7 (inlined sleep) { // at $DIR/inline-diverging.rs:22:5: 22:22
@@ -40,12 +42,11 @@
 -                                          // mir::Constant
                                            // + span: $DIR/inline-diverging.rs:22:16: 22:21
                                            // + literal: Const { ty: fn() -> ! {sleep}, val: Value(Scalar(<ZST>)) }
-+         StorageLive(_6);                 // scope 0 at $DIR/inline-diverging.rs:22:5: 22:22
 +         StorageLive(_3);                 // scope 1 at $DIR/inline-diverging.rs:22:5: 22:22
 +         StorageLive(_4);                 // scope 1 at $DIR/inline-diverging.rs:22:5: 22:22
 +         _4 = &_2;                        // scope 1 at $DIR/inline-diverging.rs:22:5: 22:22
-+         StorageLive(_7);                 // scope 1 at $DIR/inline-diverging.rs:22:5: 22:22
-+         _7 = const ();                   // scope 1 at $DIR/inline-diverging.rs:22:5: 22:22
++         StorageLive(_9);                 // scope 1 at $DIR/inline-diverging.rs:22:5: 22:22
++         _9 = const ();                   // scope 1 at $DIR/inline-diverging.rs:22:5: 22:22
 +         goto -> bb1;                     // scope 4 at $DIR/inline-diverging.rs:22:5: 22:22
       }
   
diff --git a/src/test/mir-opt/inline/inline_generator.main.Inline.diff b/src/test/mir-opt/inline/inline_generator.main.Inline.diff
index 066ac8d82d36b..fba88c83eb4f3 100644
--- a/src/test/mir-opt/inline/inline_generator.main.Inline.diff
+++ b/src/test/mir-opt/inline/inline_generator.main.Inline.diff
@@ -24,9 +24,12 @@
 +         }
 +     }
 +     scope 6 (inlined g::{closure#0}) {   // at $DIR/inline-generator.rs:9:14: 9:46
-+         debug a => _8;                   // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46
-+         let mut _8: bool;                // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46
-+         let mut _9: u32;                 // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         debug a => _11;                  // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         let mut _8: i32;                 // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         let mut _9: bool;                // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         let mut _10: bool;               // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         let _11: bool;                   // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         let mut _12: u32;                // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46
 +     }
   
       bb0: {
@@ -65,16 +68,18 @@
 -                                          // + literal: Const { ty: for<'r> fn(std::pin::Pin<&'r mut impl std::ops::Generator<bool>>, bool) -> std::ops::GeneratorState<<impl std::ops::Generator<bool> as std::ops::Generator<bool>>::Yield, <impl std::ops::Generator<bool> as std::ops::Generator<bool>>::Return> {<impl std::ops::Generator<bool> as std::ops::Generator<bool>>::resume}, val: Value(Scalar(<ZST>)) }
 +         StorageLive(_7);                 // scope 0 at $DIR/inline-generator.rs:9:14: 9:46
 +         _7 = const false;                // scope 0 at $DIR/inline-generator.rs:9:14: 9:46
-+         StorageLive(_8);                 // scope 0 at $DIR/inline-generator.rs:9:14: 9:46
-+         StorageLive(_9);                 // scope 0 at $DIR/inline-generator.rs:9:14: 9:46
-+         _9 = discriminant((*(_2.0: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]))); // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
-+         switchInt(move _9) -> [0_u32: bb3, 1_u32: bb8, 3_u32: bb7, otherwise: bb9]; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         StorageLive(_10);                // scope 0 at $DIR/inline-generator.rs:9:14: 9:46
++         StorageLive(_11);                // scope 0 at $DIR/inline-generator.rs:9:14: 9:46
++         StorageLive(_12);                // scope 0 at $DIR/inline-generator.rs:9:14: 9:46
++         _12 = discriminant((*(_2.0: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]))); // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         switchInt(move _12) -> [0_u32: bb3, 1_u32: bb8, 3_u32: bb7, otherwise: bb9]; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
       }
   
 -     bb3: {
 +     bb1: {
-+         StorageDead(_9);                 // scope 0 at $DIR/inline-generator.rs:9:14: 9:46
-+         StorageDead(_8);                 // scope 0 at $DIR/inline-generator.rs:9:14: 9:46
++         StorageDead(_12);                // scope 0 at $DIR/inline-generator.rs:9:14: 9:46
++         StorageDead(_11);                // scope 0 at $DIR/inline-generator.rs:9:14: 9:46
++         StorageDead(_10);                // scope 0 at $DIR/inline-generator.rs:9:14: 9:46
 +         StorageDead(_7);                 // scope 0 at $DIR/inline-generator.rs:9:14: 9:46
           StorageDead(_2);                 // scope 0 at $DIR/inline-generator.rs:9:45: 9:46
           StorageDead(_4);                 // scope 0 at $DIR/inline-generator.rs:9:46: 9:47
@@ -89,28 +94,36 @@
 +     }
 + 
 +     bb3: {
-+         _8 = move _7;                    // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
-+         switchInt(move _8) -> [false: bb5, otherwise: bb4]; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         _11 = move _7;                   // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         StorageLive(_8);                 // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         StorageLive(_9);                 // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         _9 = _11;                        // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         switchInt(move _9) -> [false: bb5, otherwise: bb4]; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
 +     }
 + 
 +     bb4: {
-+         ((_1 as Yielded).0: i32) = const 7_i32; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         _8 = const 7_i32;                // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
 +         goto -> bb6;                     // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
 +     }
 + 
 +     bb5: {
-+         ((_1 as Yielded).0: i32) = const 13_i32; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         _8 = const 13_i32;               // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
 +         goto -> bb6;                     // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
 +     }
 + 
 +     bb6: {
++         StorageDead(_9);                 // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         ((_1 as Yielded).0: i32) = move _8; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
 +         discriminant(_1) = 0;            // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
 +         discriminant((*(_2.0: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]))) = 3; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
 +         goto -> bb1;                     // scope 0 at $DIR/inline-generator.rs:15:11: 15:39
 +     }
 + 
 +     bb7: {
-+         ((_1 as Complete).0: bool) = move _7; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         StorageLive(_8);                 // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         _10 = move _7;                   // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         StorageDead(_8);                 // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
++         ((_1 as Complete).0: bool) = move _10; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
 +         discriminant(_1) = 1;            // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
 +         discriminant((*(_2.0: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]))) = 1; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46
 +         goto -> bb1;                     // scope 0 at $DIR/inline-generator.rs:15:41: 15:41
diff --git a/src/test/mir-opt/issue_73223.main.PreCodegen.32bit.diff b/src/test/mir-opt/issue_73223.main.PreCodegen.32bit.diff
index c630ab70de13b..e2ec0e82ebf09 100644
--- a/src/test/mir-opt/issue_73223.main.PreCodegen.32bit.diff
+++ b/src/test/mir-opt/issue_73223.main.PreCodegen.32bit.diff
@@ -6,25 +6,32 @@
       let _1: i32;                         // in scope 0 at $DIR/issue-73223.rs:2:9: 2:14
       let mut _2: std::option::Option<i32>; // in scope 0 at $DIR/issue-73223.rs:2:23: 2:30
       let _3: i32;                         // in scope 0 at $DIR/issue-73223.rs:3:14: 3:15
-      let mut _5: (&i32, &i32);            // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-      let mut _6: &i32;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-      let mut _7: bool;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-      let mut _8: bool;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-      let mut _9: i32;                     // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-      let mut _11: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-      let mut _12: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-      let mut _13: std::option::Option<std::fmt::Arguments>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _5: i32;                     // in scope 0 at $DIR/issue-73223.rs:7:22: 7:27
+      let mut _6: (&i32, &i32);            // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _7: &i32;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _8: &i32;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _11: bool;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _12: bool;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _13: i32;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _15: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let _16: &i32;                       // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _17: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let _18: &i32;                       // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _19: std::option::Option<std::fmt::Arguments>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
       scope 1 {
           debug split => _1;               // in scope 1 at $DIR/issue-73223.rs:2:9: 2:14
           let _4: std::option::Option<i32>; // in scope 1 at $DIR/issue-73223.rs:7:9: 7:14
           scope 3 {
               debug _prev => _4;           // in scope 3 at $DIR/issue-73223.rs:7:9: 7:14
+              let _9: &i32;                // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+              let _10: &i32;               // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+              let mut _20: &i32;           // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
               scope 4 {
-                  debug left_val => _11;   // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-                  debug right_val => _12;  // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-                  let _10: core::panicking::AssertKind; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+                  debug left_val => _9;    // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+                  debug right_val => _10;  // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+                  let _14: core::panicking::AssertKind; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
                   scope 5 {
-                      debug kind => _10;   // in scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+                      debug kind => _14;   // in scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
                   }
               }
           }
@@ -43,36 +50,57 @@
           _1 = _3;                         // scope 2 at $DIR/issue-73223.rs:3:20: 3:21
           StorageDead(_3);                 // scope 0 at $DIR/issue-73223.rs:3:20: 3:21
           StorageDead(_2);                 // scope 0 at $DIR/issue-73223.rs:5:6: 5:7
-          ((_4 as Some).0: i32) = _1;      // scope 1 at $DIR/issue-73223.rs:7:22: 7:27
+          StorageLive(_4);                 // scope 1 at $DIR/issue-73223.rs:7:9: 7:14
+          StorageLive(_5);                 // scope 1 at $DIR/issue-73223.rs:7:22: 7:27
+          _5 = _1;                         // scope 1 at $DIR/issue-73223.rs:7:22: 7:27
+          ((_4 as Some).0: i32) = move _5; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28
           discriminant(_4) = 1;            // scope 1 at $DIR/issue-73223.rs:7:17: 7:28
-          (_5.0: &i32) = &_1;              // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          _6 = const main::promoted[0];    // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_5);                 // scope 1 at $DIR/issue-73223.rs:7:27: 7:28
+          StorageLive(_6);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_7);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _7 = &_1;                        // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_8);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _20 = const main::promoted[0];   // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
                                            // ty::Const
                                            // + ty: &i32
                                            // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main), const_param_did: None }, [], Some(promoted[0]))
                                            // mir::Constant
                                            // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
                                            // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main), const_param_did: None }, [], Some(promoted[0])) }
-          (_5.1: &i32) = move _6;          // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          _11 = (_5.0: &i32);              // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          _12 = (_5.1: &i32);              // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          StorageLive(_7);                 // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          StorageLive(_8);                 // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          StorageLive(_9);                 // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          _9 = (*_11);                     // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          _8 = Eq(move _9, const 1_i32);   // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          StorageDead(_9);                 // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          _7 = Not(move _8);               // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          StorageDead(_8);                 // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          switchInt(move _7) -> [false: bb2, otherwise: bb1]; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _8 = _20;                        // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          (_6.0: &i32) = move _7;          // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          (_6.1: &i32) = move _8;          // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_8);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_7);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_9);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _9 = (_6.0: &i32);               // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_10);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _10 = (_6.1: &i32);              // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_11);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_12);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_13);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _13 = (*_9);                     // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _12 = Eq(move _13, const 1_i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_13);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _11 = Not(move _12);             // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_12);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          switchInt(move _11) -> [false: bb2, otherwise: bb1]; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
       }
   
       bb1: {
-          StorageLive(_10);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          discriminant(_10) = 0;           // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          StorageLive(_13);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          discriminant(_13) = 0;           // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          core::panicking::assert_failed::<i32, i32>(const core::panicking::AssertKind::Eq, move _11, move _12, move _13); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_14);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          discriminant(_14) = 0;           // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_15);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_16);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _16 = _9;                        // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _15 = _16;                       // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_17);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_18);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _18 = _10;                       // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _17 = _18;                       // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_19);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          discriminant(_19) = 0;           // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          core::panicking::assert_failed::<i32, i32>(const core::panicking::AssertKind::Eq, move _15, move _17, move _19); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
                                            // mir::Constant
                                            // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
                                            // + literal: Const { ty: for<'r, 's, 't0> fn(core::panicking::AssertKind, &'r i32, &'s i32, std::option::Option<std::fmt::Arguments<'t0>>) -> ! {core::panicking::assert_failed::<i32, i32>}, val: Value(Scalar(<ZST>)) }
@@ -85,8 +113,12 @@
       }
   
       bb2: {
-          StorageDead(_7);                 // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_11);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_10);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_9);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_6);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           _0 = const ();                   // scope 0 at $DIR/issue-73223.rs:1:11: 9:2
+          StorageDead(_4);                 // scope 1 at $DIR/issue-73223.rs:9:1: 9:2
           StorageDead(_1);                 // scope 0 at $DIR/issue-73223.rs:9:1: 9:2
           return;                          // scope 0 at $DIR/issue-73223.rs:9:2: 9:2
       }
diff --git a/src/test/mir-opt/issue_73223.main.PreCodegen.64bit.diff b/src/test/mir-opt/issue_73223.main.PreCodegen.64bit.diff
index c630ab70de13b..e2ec0e82ebf09 100644
--- a/src/test/mir-opt/issue_73223.main.PreCodegen.64bit.diff
+++ b/src/test/mir-opt/issue_73223.main.PreCodegen.64bit.diff
@@ -6,25 +6,32 @@
       let _1: i32;                         // in scope 0 at $DIR/issue-73223.rs:2:9: 2:14
       let mut _2: std::option::Option<i32>; // in scope 0 at $DIR/issue-73223.rs:2:23: 2:30
       let _3: i32;                         // in scope 0 at $DIR/issue-73223.rs:3:14: 3:15
-      let mut _5: (&i32, &i32);            // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-      let mut _6: &i32;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-      let mut _7: bool;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-      let mut _8: bool;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-      let mut _9: i32;                     // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-      let mut _11: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-      let mut _12: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-      let mut _13: std::option::Option<std::fmt::Arguments>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _5: i32;                     // in scope 0 at $DIR/issue-73223.rs:7:22: 7:27
+      let mut _6: (&i32, &i32);            // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _7: &i32;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _8: &i32;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _11: bool;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _12: bool;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _13: i32;                    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _15: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let _16: &i32;                       // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _17: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let _18: &i32;                       // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _19: std::option::Option<std::fmt::Arguments>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
       scope 1 {
           debug split => _1;               // in scope 1 at $DIR/issue-73223.rs:2:9: 2:14
           let _4: std::option::Option<i32>; // in scope 1 at $DIR/issue-73223.rs:7:9: 7:14
           scope 3 {
               debug _prev => _4;           // in scope 3 at $DIR/issue-73223.rs:7:9: 7:14
+              let _9: &i32;                // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+              let _10: &i32;               // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+              let mut _20: &i32;           // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
               scope 4 {
-                  debug left_val => _11;   // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-                  debug right_val => _12;  // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-                  let _10: core::panicking::AssertKind; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+                  debug left_val => _9;    // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+                  debug right_val => _10;  // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+                  let _14: core::panicking::AssertKind; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
                   scope 5 {
-                      debug kind => _10;   // in scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+                      debug kind => _14;   // in scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
                   }
               }
           }
@@ -43,36 +50,57 @@
           _1 = _3;                         // scope 2 at $DIR/issue-73223.rs:3:20: 3:21
           StorageDead(_3);                 // scope 0 at $DIR/issue-73223.rs:3:20: 3:21
           StorageDead(_2);                 // scope 0 at $DIR/issue-73223.rs:5:6: 5:7
-          ((_4 as Some).0: i32) = _1;      // scope 1 at $DIR/issue-73223.rs:7:22: 7:27
+          StorageLive(_4);                 // scope 1 at $DIR/issue-73223.rs:7:9: 7:14
+          StorageLive(_5);                 // scope 1 at $DIR/issue-73223.rs:7:22: 7:27
+          _5 = _1;                         // scope 1 at $DIR/issue-73223.rs:7:22: 7:27
+          ((_4 as Some).0: i32) = move _5; // scope 1 at $DIR/issue-73223.rs:7:17: 7:28
           discriminant(_4) = 1;            // scope 1 at $DIR/issue-73223.rs:7:17: 7:28
-          (_5.0: &i32) = &_1;              // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          _6 = const main::promoted[0];    // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_5);                 // scope 1 at $DIR/issue-73223.rs:7:27: 7:28
+          StorageLive(_6);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_7);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _7 = &_1;                        // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_8);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _20 = const main::promoted[0];   // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
                                            // ty::Const
                                            // + ty: &i32
                                            // + val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main), const_param_did: None }, [], Some(promoted[0]))
                                            // mir::Constant
                                            // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
                                            // + literal: Const { ty: &i32, val: Unevaluated(WithOptConstParam { did: DefId(0:3 ~ issue_73223[317d]::main), const_param_did: None }, [], Some(promoted[0])) }
-          (_5.1: &i32) = move _6;          // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          _11 = (_5.0: &i32);              // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          _12 = (_5.1: &i32);              // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          StorageLive(_7);                 // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          StorageLive(_8);                 // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          StorageLive(_9);                 // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          _9 = (*_11);                     // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          _8 = Eq(move _9, const 1_i32);   // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          StorageDead(_9);                 // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          _7 = Not(move _8);               // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          StorageDead(_8);                 // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          switchInt(move _7) -> [false: bb2, otherwise: bb1]; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _8 = _20;                        // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          (_6.0: &i32) = move _7;          // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          (_6.1: &i32) = move _8;          // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_8);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_7);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_9);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _9 = (_6.0: &i32);               // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_10);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _10 = (_6.1: &i32);              // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_11);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_12);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_13);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _13 = (*_9);                     // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _12 = Eq(move _13, const 1_i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_13);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _11 = Not(move _12);             // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_12);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          switchInt(move _11) -> [false: bb2, otherwise: bb1]; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
       }
   
       bb1: {
-          StorageLive(_10);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          discriminant(_10) = 0;           // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          StorageLive(_13);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          discriminant(_13) = 0;           // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          core::panicking::assert_failed::<i32, i32>(const core::panicking::AssertKind::Eq, move _11, move _12, move _13); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_14);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          discriminant(_14) = 0;           // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_15);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_16);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _16 = _9;                        // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _15 = _16;                       // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_17);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_18);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _18 = _10;                       // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _17 = _18;                       // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_19);                // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          discriminant(_19) = 0;           // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          core::panicking::assert_failed::<i32, i32>(const core::panicking::AssertKind::Eq, move _15, move _17, move _19); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
                                            // mir::Constant
                                            // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
                                            // + literal: Const { ty: for<'r, 's, 't0> fn(core::panicking::AssertKind, &'r i32, &'s i32, std::option::Option<std::fmt::Arguments<'t0>>) -> ! {core::panicking::assert_failed::<i32, i32>}, val: Value(Scalar(<ZST>)) }
@@ -85,8 +113,12 @@
       }
   
       bb2: {
-          StorageDead(_7);                 // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_11);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_10);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_9);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_6);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           _0 = const ();                   // scope 0 at $DIR/issue-73223.rs:1:11: 9:2
+          StorageDead(_4);                 // scope 1 at $DIR/issue-73223.rs:9:1: 9:2
           StorageDead(_1);                 // scope 0 at $DIR/issue-73223.rs:9:1: 9:2
           return;                          // scope 0 at $DIR/issue-73223.rs:9:2: 9:2
       }
diff --git a/src/test/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.mir b/src/test/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.mir
index 0f718a720f4cc..19d161ac2b103 100644
--- a/src/test/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.mir
+++ b/src/test/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.mir
@@ -3,60 +3,67 @@
 fn num_to_digit(_1: char) -> u32 {
     debug num => _1;                     // in scope 0 at $DIR/issue-59352.rs:12:21: 12:24
     let mut _0: u32;                     // return place in scope 0 at $DIR/issue-59352.rs:12:35: 12:38
-    let mut _2: std::option::Option<u32>; // in scope 0 at $DIR/issue-59352.rs:14:26: 14:41
-    let mut _3: char;                    // in scope 0 at $DIR/issue-59352.rs:14:26: 14:29
-    let mut _4: u32;                     // in scope 0 at $DIR/issue-59352.rs:14:8: 14:23
-    let mut _9: isize;                   // in scope 0 at $DIR/issue-59352.rs:14:8: 14:23
+    let mut _2: char;                    // in scope 0 at $DIR/issue-59352.rs:14:8: 14:11
+    let mut _3: std::option::Option<u32>; // in scope 0 at $DIR/issue-59352.rs:14:26: 14:41
+    let mut _4: char;                    // in scope 0 at $DIR/issue-59352.rs:14:26: 14:29
+    let mut _5: u32;                     // in scope 0 at $DIR/issue-59352.rs:14:8: 14:23
+    let mut _11: isize;                  // in scope 0 at $DIR/issue-59352.rs:14:8: 14:23
     scope 1 (inlined char::methods::<impl char>::is_digit) { // at $DIR/issue-59352.rs:14:8: 14:23
-        debug self => _7;                // in scope 1 at $DIR/issue-59352.rs:14:8: 14:23
-        debug radix => _4;               // in scope 1 at $DIR/issue-59352.rs:14:8: 14:23
-        let mut _5: &std::option::Option<u32>; // in scope 1 at $DIR/issue-59352.rs:14:8: 14:23
-        let _6: std::option::Option<u32>; // in scope 1 at $DIR/issue-59352.rs:14:8: 14:23
-        let mut _7: char;                // in scope 1 at $DIR/issue-59352.rs:14:8: 14:23
+        debug self => _2;                // in scope 1 at $DIR/issue-59352.rs:14:8: 14:23
+        debug radix => _5;               // in scope 1 at $DIR/issue-59352.rs:14:8: 14:23
+        let mut _6: &std::option::Option<u32>; // in scope 1 at $DIR/issue-59352.rs:14:8: 14:23
+        let _7: std::option::Option<u32>; // in scope 1 at $DIR/issue-59352.rs:14:8: 14:23
+        let mut _8: char;                // in scope 1 at $DIR/issue-59352.rs:14:8: 14:23
         scope 2 (inlined Option::<u32>::is_some) { // at $DIR/issue-59352.rs:14:8: 14:23
-            debug self => _5;            // in scope 2 at $DIR/issue-59352.rs:14:8: 14:23
+            debug self => _6;            // in scope 2 at $DIR/issue-59352.rs:14:8: 14:23
+            let mut _9: isize;           // in scope 2 at $DIR/issue-59352.rs:14:8: 14:23
         }
     }
     scope 3 (inlined #[track_caller] Option::<u32>::unwrap) { // at $DIR/issue-59352.rs:14:26: 14:50
-        debug self => _2;                // in scope 3 at $DIR/issue-59352.rs:14:26: 14:50
-        let mut _8: isize;               // in scope 3 at $DIR/issue-59352.rs:14:26: 14:50
+        debug self => _3;                // in scope 3 at $DIR/issue-59352.rs:14:26: 14:50
+        let mut _10: isize;              // in scope 3 at $DIR/issue-59352.rs:14:26: 14:50
         scope 4 {
             debug val => _0;             // in scope 4 at $DIR/issue-59352.rs:14:26: 14:50
         }
     }
 
     bb0: {
-        _7 = _1;                         // scope 0 at $DIR/issue-59352.rs:14:8: 14:11
-        StorageLive(_4);                 // scope 0 at $DIR/issue-59352.rs:14:8: 14:23
-        _4 = const 8_u32;                // scope 0 at $DIR/issue-59352.rs:14:8: 14:23
-        StorageLive(_5);                 // scope 1 at $DIR/issue-59352.rs:14:8: 14:23
+        StorageLive(_2);                 // scope 0 at $DIR/issue-59352.rs:14:8: 14:11
+        _2 = _1;                         // scope 0 at $DIR/issue-59352.rs:14:8: 14:11
+        StorageLive(_5);                 // scope 0 at $DIR/issue-59352.rs:14:8: 14:23
+        _5 = const 8_u32;                // scope 0 at $DIR/issue-59352.rs:14:8: 14:23
         StorageLive(_6);                 // scope 1 at $DIR/issue-59352.rs:14:8: 14:23
-        _6 = char::methods::<impl char>::to_digit(move _7, const 8_u32) -> bb5; // scope 1 at $DIR/issue-59352.rs:14:8: 14:23
+        StorageLive(_7);                 // scope 1 at $DIR/issue-59352.rs:14:8: 14:23
+        StorageLive(_8);                 // scope 1 at $DIR/issue-59352.rs:14:8: 14:23
+        _8 = _2;                         // scope 1 at $DIR/issue-59352.rs:14:8: 14:23
+        _7 = char::methods::<impl char>::to_digit(move _8, const 8_u32) -> bb5; // scope 1 at $DIR/issue-59352.rs:14:8: 14:23
                                          // mir::Constant
                                          // + span: $DIR/issue-59352.rs:14:8: 14:23
                                          // + literal: Const { ty: fn(char, u32) -> std::option::Option<u32> {std::char::methods::<impl char>::to_digit}, val: Value(Scalar(<ZST>)) }
     }
 
     bb1: {
-        StorageLive(_2);                 // scope 0 at $DIR/issue-59352.rs:14:26: 14:41
-        StorageLive(_3);                 // scope 0 at $DIR/issue-59352.rs:14:26: 14:29
-        _3 = _1;                         // scope 0 at $DIR/issue-59352.rs:14:26: 14:29
-        _2 = char::methods::<impl char>::to_digit(move _3, const 8_u32) -> bb3; // scope 0 at $DIR/issue-59352.rs:14:26: 14:41
+        StorageDead(_11);                // scope 0 at $DIR/issue-59352.rs:14:5: 14:63
+        StorageLive(_3);                 // scope 0 at $DIR/issue-59352.rs:14:26: 14:41
+        StorageLive(_4);                 // scope 0 at $DIR/issue-59352.rs:14:26: 14:29
+        _4 = _1;                         // scope 0 at $DIR/issue-59352.rs:14:26: 14:29
+        _3 = char::methods::<impl char>::to_digit(move _4, const 8_u32) -> bb3; // scope 0 at $DIR/issue-59352.rs:14:26: 14:41
                                          // mir::Constant
                                          // + span: $DIR/issue-59352.rs:14:30: 14:38
                                          // + literal: Const { ty: fn(char, u32) -> std::option::Option<u32> {std::char::methods::<impl char>::to_digit}, val: Value(Scalar(<ZST>)) }
     }
 
     bb2: {
+        StorageDead(_11);                // scope 0 at $DIR/issue-59352.rs:14:5: 14:63
         _0 = const 0_u32;                // scope 0 at $DIR/issue-59352.rs:14:60: 14:61
         goto -> bb4;                     // scope 0 at $DIR/issue-59352.rs:14:5: 14:63
     }
 
     bb3: {
-        StorageDead(_3);                 // scope 0 at $DIR/issue-59352.rs:14:40: 14:41
-        StorageLive(_8);                 // scope 0 at $DIR/issue-59352.rs:14:26: 14:50
-        _8 = discriminant(_2);           // scope 3 at $DIR/issue-59352.rs:14:26: 14:50
-        switchInt(move _8) -> [0_isize: bb6, 1_isize: bb8, otherwise: bb7]; // scope 3 at $DIR/issue-59352.rs:14:26: 14:50
+        StorageDead(_4);                 // scope 0 at $DIR/issue-59352.rs:14:40: 14:41
+        StorageLive(_10);                // scope 0 at $DIR/issue-59352.rs:14:26: 14:50
+        _10 = discriminant(_3);          // scope 3 at $DIR/issue-59352.rs:14:26: 14:50
+        switchInt(move _10) -> [0_isize: bb6, 1_isize: bb8, otherwise: bb7]; // scope 3 at $DIR/issue-59352.rs:14:26: 14:50
     }
 
     bb4: {
@@ -64,12 +71,18 @@ fn num_to_digit(_1: char) -> u32 {
     }
 
     bb5: {
-        _5 = &_6;                        // scope 1 at $DIR/issue-59352.rs:14:8: 14:23
-        _9 = discriminant((*_5));        // scope 2 at $DIR/issue-59352.rs:14:8: 14:23
-        StorageDead(_5);                 // scope 1 at $DIR/issue-59352.rs:14:8: 14:23
+        _6 = &_7;                        // scope 1 at $DIR/issue-59352.rs:14:8: 14:23
+        StorageDead(_8);                 // scope 1 at $DIR/issue-59352.rs:14:8: 14:23
+        StorageLive(_9);                 // scope 1 at $DIR/issue-59352.rs:14:8: 14:23
+        _9 = discriminant((*_6));        // scope 2 at $DIR/issue-59352.rs:14:8: 14:23
+        StorageLive(_11);                // scope 2 at $DIR/issue-59352.rs:14:8: 14:23
+        _11 = move _9;                   // scope 2 at $DIR/issue-59352.rs:14:8: 14:23
+        StorageDead(_9);                 // scope 1 at $DIR/issue-59352.rs:14:8: 14:23
         StorageDead(_6);                 // scope 1 at $DIR/issue-59352.rs:14:8: 14:23
-        StorageDead(_4);                 // scope 0 at $DIR/issue-59352.rs:14:8: 14:23
-        switchInt(move _9) -> [1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/issue-59352.rs:14:5: 14:63
+        StorageDead(_7);                 // scope 1 at $DIR/issue-59352.rs:14:8: 14:23
+        StorageDead(_5);                 // scope 0 at $DIR/issue-59352.rs:14:8: 14:23
+        StorageDead(_2);                 // scope 0 at $DIR/issue-59352.rs:14:22: 14:23
+        switchInt(move _11) -> [1_isize: bb1, otherwise: bb2]; // scope 0 at $DIR/issue-59352.rs:14:5: 14:63
     }
 
     bb6: {
@@ -90,9 +103,9 @@ fn num_to_digit(_1: char) -> u32 {
     }
 
     bb8: {
-        _0 = move ((_2 as Some).0: u32); // scope 3 at $DIR/issue-59352.rs:14:26: 14:50
-        StorageDead(_8);                 // scope 0 at $DIR/issue-59352.rs:14:26: 14:50
-        StorageDead(_2);                 // scope 0 at $DIR/issue-59352.rs:14:49: 14:50
+        _0 = move ((_3 as Some).0: u32); // scope 3 at $DIR/issue-59352.rs:14:26: 14:50
+        StorageDead(_10);                // scope 0 at $DIR/issue-59352.rs:14:26: 14:50
+        StorageDead(_3);                 // scope 0 at $DIR/issue-59352.rs:14:49: 14:50
         goto -> bb4;                     // scope 0 at $DIR/issue-59352.rs:14:5: 14:63
     }
 }
diff --git a/src/test/mir-opt/lower_intrinsics.f_u64.PreCodegen.before.mir b/src/test/mir-opt/lower_intrinsics.f_u64.PreCodegen.before.mir
index 740a6e0edb0c4..a87d5e1f0ff8b 100644
--- a/src/test/mir-opt/lower_intrinsics.f_u64.PreCodegen.before.mir
+++ b/src/test/mir-opt/lower_intrinsics.f_u64.PreCodegen.before.mir
@@ -2,9 +2,9 @@
 
 fn f_u64() -> () {
     let mut _0: ();                      // return place in scope 0 at $DIR/lower_intrinsics.rs:34:16: 34:16
+    let mut _1: u64;                     // in scope 0 at $DIR/lower_intrinsics.rs:35:5: 35:21
     scope 1 (inlined f_dispatch::<u64>) { // at $DIR/lower_intrinsics.rs:35:5: 35:21
         debug t => _1;                   // in scope 1 at $DIR/lower_intrinsics.rs:35:5: 35:21
-        let mut _1: u64;                 // in scope 1 at $DIR/lower_intrinsics.rs:35:5: 35:21
         let _2: ();                      // in scope 1 at $DIR/lower_intrinsics.rs:35:5: 35:21
         let mut _3: u64;                 // in scope 1 at $DIR/lower_intrinsics.rs:35:5: 35:21
         scope 2 (inlined std::mem::size_of::<u64>) { // at $DIR/lower_intrinsics.rs:35:5: 35:21
@@ -12,6 +12,7 @@ fn f_u64() -> () {
     }
 
     bb0: {
+        StorageLive(_1);                 // scope 0 at $DIR/lower_intrinsics.rs:35:5: 35:21
         _1 = const 0_u64;                // scope 0 at $DIR/lower_intrinsics.rs:35:5: 35:21
         StorageLive(_2);                 // scope 1 at $DIR/lower_intrinsics.rs:35:5: 35:21
         StorageLive(_3);                 // scope 1 at $DIR/lower_intrinsics.rs:35:5: 35:21
@@ -25,6 +26,7 @@ fn f_u64() -> () {
     bb1: {
         StorageDead(_3);                 // scope 1 at $DIR/lower_intrinsics.rs:35:5: 35:21
         StorageDead(_2);                 // scope 1 at $DIR/lower_intrinsics.rs:35:5: 35:21
+        StorageDead(_1);                 // scope 0 at $DIR/lower_intrinsics.rs:35:5: 35:21
         _0 = const ();                   // scope 0 at $DIR/lower_intrinsics.rs:34:16: 36:2
         return;                          // scope 0 at $DIR/lower_intrinsics.rs:36:2: 36:2
     }
diff --git a/src/test/mir-opt/simplify_locals.c.SimplifyLocals.diff b/src/test/mir-opt/simplify_locals.c.SimplifyLocals.diff
index dbac7dff9e2bb..f1718c5752c1c 100644
--- a/src/test/mir-opt/simplify_locals.c.SimplifyLocals.diff
+++ b/src/test/mir-opt/simplify_locals.c.SimplifyLocals.diff
@@ -17,8 +17,13 @@
           StorageLive(_1);                 // scope 0 at $DIR/simplify-locals.rs:14:9: 14:14
           _1 = [const 0_u8; 10];           // scope 0 at $DIR/simplify-locals.rs:14:17: 14:26
 -         StorageLive(_2);                 // scope 1 at $DIR/simplify-locals.rs:16:20: 16:26
--         _3 = &_1;                        // scope 1 at $DIR/simplify-locals.rs:16:20: 16:26
+-         StorageLive(_3);                 // scope 1 at $DIR/simplify-locals.rs:16:20: 16:26
+-         StorageLive(_4);                 // scope 1 at $DIR/simplify-locals.rs:16:20: 16:26
+-         _4 = &_1;                        // scope 1 at $DIR/simplify-locals.rs:16:20: 16:26
+-         _3 = _4;                         // scope 1 at $DIR/simplify-locals.rs:16:20: 16:26
 -         _2 = move _3 as &[u8] (Pointer(Unsize)); // scope 1 at $DIR/simplify-locals.rs:16:20: 16:26
+-         StorageDead(_3);                 // scope 1 at $DIR/simplify-locals.rs:16:25: 16:26
+-         StorageDead(_4);                 // scope 1 at $DIR/simplify-locals.rs:16:26: 16:27
 -         StorageDead(_2);                 // scope 1 at $DIR/simplify-locals.rs:16:26: 16:27
           _0 = const ();                   // scope 0 at $DIR/simplify-locals.rs:13:8: 17:2
           StorageDead(_1);                 // scope 0 at $DIR/simplify-locals.rs:17:1: 17:2
diff --git a/src/test/ui/directory_ownership/macro-expanded-mod.rs b/src/test/ui/directory_ownership/macro-expanded-mod.rs
index 9cb159603a8c5..fa81769e5a800 100644
--- a/src/test/ui/directory_ownership/macro-expanded-mod.rs
+++ b/src/test/ui/directory_ownership/macro-expanded-mod.rs
@@ -2,7 +2,7 @@
 
 macro_rules! mod_decl {
     ($i:ident) => {
-        mod $i; //~ ERROR Cannot declare a non-inline module inside a block
+        mod $i; //~ ERROR cannot declare a non-inline module inside a block
     };
 }
 
diff --git a/src/test/ui/directory_ownership/macro-expanded-mod.stderr b/src/test/ui/directory_ownership/macro-expanded-mod.stderr
index f90419247c92b..4039728e18ac3 100644
--- a/src/test/ui/directory_ownership/macro-expanded-mod.stderr
+++ b/src/test/ui/directory_ownership/macro-expanded-mod.stderr
@@ -1,4 +1,4 @@
-error: Cannot declare a non-inline module inside a block unless it has a path attribute
+error: cannot declare a non-inline module inside a block unless it has a path attribute
   --> $DIR/macro-expanded-mod.rs:5:9
    |
 LL |         mod $i;
diff --git a/src/test/ui/directory_ownership/non-inline-mod-restriction.rs b/src/test/ui/directory_ownership/non-inline-mod-restriction.rs
index af31b8a49281f..de4f816656cc4 100644
--- a/src/test/ui/directory_ownership/non-inline-mod-restriction.rs
+++ b/src/test/ui/directory_ownership/non-inline-mod-restriction.rs
@@ -1,5 +1,5 @@
 // Test that non-inline modules are not allowed inside blocks.
 
 fn main() {
-    mod foo; //~ ERROR Cannot declare a non-inline module inside a block
+    mod foo; //~ ERROR cannot declare a non-inline module inside a block
 }
diff --git a/src/test/ui/directory_ownership/non-inline-mod-restriction.stderr b/src/test/ui/directory_ownership/non-inline-mod-restriction.stderr
index d034942ca5d4c..64189bee43f6e 100644
--- a/src/test/ui/directory_ownership/non-inline-mod-restriction.stderr
+++ b/src/test/ui/directory_ownership/non-inline-mod-restriction.stderr
@@ -1,4 +1,4 @@
-error: Cannot declare a non-inline module inside a block unless it has a path attribute
+error: cannot declare a non-inline module inside a block unless it has a path attribute
   --> $DIR/non-inline-mod-restriction.rs:4:5
    |
 LL |     mod foo;
diff --git a/src/test/ui/modules/path-no-file-name.rs b/src/test/ui/modules/path-no-file-name.rs
new file mode 100644
index 0000000000000..f62cd2a9eb4e4
--- /dev/null
+++ b/src/test/ui/modules/path-no-file-name.rs
@@ -0,0 +1,7 @@
+// normalize-stderr-test: "\.:.*\(" -> ".: $$ACCESS_DENIED_MSG ("
+// normalize-stderr-test: "os error \d+" -> "os error $$ACCESS_DENIED_CODE"
+
+#[path = "."]
+mod m; //~ ERROR couldn't read
+
+fn main() {}
diff --git a/src/test/ui/modules/path-no-file-name.stderr b/src/test/ui/modules/path-no-file-name.stderr
new file mode 100644
index 0000000000000..32a213c68f654
--- /dev/null
+++ b/src/test/ui/modules/path-no-file-name.stderr
@@ -0,0 +1,8 @@
+error: couldn't read $DIR/.: $ACCESS_DENIED_MSG (os error $ACCESS_DENIED_CODE)
+  --> $DIR/path-no-file-name.rs:5:1
+   |
+LL | mod m;
+   | ^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/parser/circular_modules_main.rs b/src/test/ui/parser/circular_modules_main.rs
index 1ae36a1f7605e..d4b47efe68158 100644
--- a/src/test/ui/parser/circular_modules_main.rs
+++ b/src/test/ui/parser/circular_modules_main.rs
@@ -1,10 +1,12 @@
+// error-pattern: circular modules
+
 #[path = "circular_modules_hello.rs"]
-mod circular_modules_hello; //~ ERROR: circular modules
+mod circular_modules_hello;
 
 pub fn hi_str() -> String {
     "Hi!".to_string()
 }
 
 fn main() {
-    circular_modules_hello::say_hello(); //~ ERROR cannot find function `say_hello` in module
+    circular_modules_hello::say_hello();
 }
diff --git a/src/test/ui/parser/circular_modules_main.stderr b/src/test/ui/parser/circular_modules_main.stderr
index 5d4db8c31a2c3..ee45f65a3bd5a 100644
--- a/src/test/ui/parser/circular_modules_main.stderr
+++ b/src/test/ui/parser/circular_modules_main.stderr
@@ -1,18 +1,18 @@
-error: circular modules: $DIR/circular_modules_hello.rs -> $DIR/circular_modules_main.rs -> $DIR/circular_modules_hello.rs
-  --> $DIR/circular_modules_main.rs:2:1
+error: circular modules: $DIR/circular_modules_main.rs -> $DIR/circular_modules_hello.rs -> $DIR/circular_modules_main.rs
+  --> $DIR/circular_modules_hello.rs:4:1
    |
-LL | mod circular_modules_hello;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | mod circular_modules_main;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0425]: cannot find function `say_hello` in module `circular_modules_hello`
-  --> $DIR/circular_modules_main.rs:9:29
+error[E0425]: cannot find function `hi_str` in module `circular_modules_main`
+  --> $DIR/circular_modules_hello.rs:7:43
    |
-LL |     circular_modules_hello::say_hello();
-   |                             ^^^^^^^^^ not found in `circular_modules_hello`
+LL |     println!("{}", circular_modules_main::hi_str());
+   |                                           ^^^^^^ not found in `circular_modules_main`
    |
 help: consider importing this function
    |
-LL | use circular_modules_hello::say_hello;
+LL | use hi_str;
    |
 
 error: aborting due to 2 previous errors
diff --git a/src/test/ui/proc-macro/cfg-eval-fail.rs b/src/test/ui/proc-macro/cfg-eval-fail.rs
new file mode 100644
index 0000000000000..379491f3126b0
--- /dev/null
+++ b/src/test/ui/proc-macro/cfg-eval-fail.rs
@@ -0,0 +1,9 @@
+#![feature(cfg_eval)]
+#![feature(stmt_expr_attributes)]
+
+fn main() {
+    let _ = #[cfg_eval] #[cfg(FALSE)] 0;
+    //~^ ERROR removing an expression is not supported in this position
+    //~| ERROR removing an expression is not supported in this position
+    //~| ERROR removing an expression is not supported in this position
+}
diff --git a/src/test/ui/proc-macro/cfg-eval-fail.stderr b/src/test/ui/proc-macro/cfg-eval-fail.stderr
new file mode 100644
index 0000000000000..010ac006b0bee
--- /dev/null
+++ b/src/test/ui/proc-macro/cfg-eval-fail.stderr
@@ -0,0 +1,20 @@
+error: removing an expression is not supported in this position
+  --> $DIR/cfg-eval-fail.rs:5:25
+   |
+LL |     let _ = #[cfg_eval] #[cfg(FALSE)] 0;
+   |                         ^^^^^^^^^^^^^
+
+error: removing an expression is not supported in this position
+  --> $DIR/cfg-eval-fail.rs:5:25
+   |
+LL |     let _ = #[cfg_eval] #[cfg(FALSE)] 0;
+   |                         ^^^^^^^^^^^^^
+
+error: removing an expression is not supported in this position
+  --> $DIR/cfg-eval-fail.rs:5:25
+   |
+LL |     let _ = #[cfg_eval] #[cfg(FALSE)] 0;
+   |                         ^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/proc-macro/cfg-eval.rs b/src/test/ui/proc-macro/cfg-eval.rs
new file mode 100644
index 0000000000000..ea397df545265
--- /dev/null
+++ b/src/test/ui/proc-macro/cfg-eval.rs
@@ -0,0 +1,32 @@
+// check-pass
+// compile-flags: -Z span-debug
+// aux-build:test-macros.rs
+
+#![feature(cfg_eval)]
+#![feature(proc_macro_hygiene)]
+#![feature(stmt_expr_attributes)]
+
+#![no_std] // Don't load unnecessary hygiene information from std
+extern crate std;
+
+#[macro_use]
+extern crate test_macros;
+
+#[cfg_eval]
+#[print_attr]
+struct S1 {
+    #[cfg(FALSE)]
+    field_false: u8,
+    #[cfg(all(/*true*/))]
+    #[cfg_attr(FALSE, unknown_attr)]
+    #[cfg_attr(all(/*true*/), allow())]
+    field_true: u8,
+}
+
+#[cfg_eval]
+#[cfg(FALSE)]
+struct S2 {}
+
+fn main() {
+    let _ = #[cfg_eval] #[print_attr](#[cfg(FALSE)] 0, #[cfg(all(/*true*/))] 1);
+}
diff --git a/src/test/ui/proc-macro/cfg-eval.stdout b/src/test/ui/proc-macro/cfg-eval.stdout
new file mode 100644
index 0000000000000..b98e8961bfea7
--- /dev/null
+++ b/src/test/ui/proc-macro/cfg-eval.stdout
@@ -0,0 +1,135 @@
+PRINT-ATTR INPUT (DISPLAY): struct S1 { #[cfg(all())] #[allow()] field_true : u8, }
+PRINT-ATTR INPUT (DEBUG): TokenStream [
+    Ident {
+        ident: "struct",
+        span: $DIR/cfg-eval.rs:17:1: 24:2 (#0),
+    },
+    Ident {
+        ident: "S1",
+        span: $DIR/cfg-eval.rs:17:1: 24:2 (#0),
+    },
+    Group {
+        delimiter: Brace,
+        stream: TokenStream [
+            Punct {
+                ch: '#',
+                spacing: Alone,
+                span: $DIR/cfg-eval.rs:17:1: 24:2 (#0),
+            },
+            Group {
+                delimiter: Bracket,
+                stream: TokenStream [
+                    Ident {
+                        ident: "cfg",
+                        span: $DIR/cfg-eval.rs:17:1: 24:2 (#0),
+                    },
+                    Group {
+                        delimiter: Parenthesis,
+                        stream: TokenStream [
+                            Ident {
+                                ident: "all",
+                                span: $DIR/cfg-eval.rs:17:1: 24:2 (#0),
+                            },
+                            Group {
+                                delimiter: Parenthesis,
+                                stream: TokenStream [],
+                                span: $DIR/cfg-eval.rs:17:1: 24:2 (#0),
+                            },
+                        ],
+                        span: $DIR/cfg-eval.rs:17:1: 24:2 (#0),
+                    },
+                ],
+                span: $DIR/cfg-eval.rs:17:1: 24:2 (#0),
+            },
+            Punct {
+                ch: '#',
+                spacing: Alone,
+                span: $DIR/cfg-eval.rs:17:1: 24:2 (#0),
+            },
+            Group {
+                delimiter: Bracket,
+                stream: TokenStream [
+                    Ident {
+                        ident: "allow",
+                        span: $DIR/cfg-eval.rs:17:1: 24:2 (#0),
+                    },
+                    Group {
+                        delimiter: Parenthesis,
+                        stream: TokenStream [],
+                        span: $DIR/cfg-eval.rs:17:1: 24:2 (#0),
+                    },
+                ],
+                span: $DIR/cfg-eval.rs:17:1: 24:2 (#0),
+            },
+            Ident {
+                ident: "field_true",
+                span: $DIR/cfg-eval.rs:17:1: 24:2 (#0),
+            },
+            Punct {
+                ch: ':',
+                spacing: Alone,
+                span: $DIR/cfg-eval.rs:17:1: 24:2 (#0),
+            },
+            Ident {
+                ident: "u8",
+                span: $DIR/cfg-eval.rs:17:1: 24:2 (#0),
+            },
+            Punct {
+                ch: ',',
+                spacing: Alone,
+                span: $DIR/cfg-eval.rs:17:1: 24:2 (#0),
+            },
+        ],
+        span: $DIR/cfg-eval.rs:17:1: 24:2 (#0),
+    },
+]
+PRINT-ATTR INPUT (DISPLAY): (#[cfg(all())] 1,)
+PRINT-ATTR INPUT (DEBUG): TokenStream [
+    Group {
+        delimiter: Parenthesis,
+        stream: TokenStream [
+            Punct {
+                ch: '#',
+                spacing: Alone,
+                span: $DIR/cfg-eval.rs:31:38: 31:80 (#0),
+            },
+            Group {
+                delimiter: Bracket,
+                stream: TokenStream [
+                    Ident {
+                        ident: "cfg",
+                        span: $DIR/cfg-eval.rs:31:38: 31:80 (#0),
+                    },
+                    Group {
+                        delimiter: Parenthesis,
+                        stream: TokenStream [
+                            Ident {
+                                ident: "all",
+                                span: $DIR/cfg-eval.rs:31:38: 31:80 (#0),
+                            },
+                            Group {
+                                delimiter: Parenthesis,
+                                stream: TokenStream [],
+                                span: $DIR/cfg-eval.rs:31:38: 31:80 (#0),
+                            },
+                        ],
+                        span: $DIR/cfg-eval.rs:31:38: 31:80 (#0),
+                    },
+                ],
+                span: $DIR/cfg-eval.rs:31:38: 31:80 (#0),
+            },
+            Literal {
+                kind: Integer,
+                symbol: "1",
+                suffix: None,
+                span: $DIR/cfg-eval.rs:31:38: 31:80 (#0),
+            },
+            Punct {
+                ch: ',',
+                spacing: Alone,
+                span: $DIR/cfg-eval.rs:31:38: 31:80 (#0),
+            },
+        ],
+        span: $DIR/cfg-eval.rs:31:38: 31:80 (#0),
+    },
+]