about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast/src/ast.rs33
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs20
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs36
-rw-r--r--compiler/rustc_ast/src/token.rs19
-rw-r--r--compiler/rustc_ast/src/tokenstream.rs41
-rw-r--r--compiler/rustc_ast/src/visit.rs2
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs15
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs2
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs14
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs36
-rw-r--r--compiler/rustc_ast_pretty/src/pprust.rs13
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/tests.rs7
-rw-r--r--compiler/rustc_builtin_macros/src/cmdline_attrs.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/concat_idents.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/debug.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs13
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/mod.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/format_foreign.rs12
-rw-r--r--compiler/rustc_builtin_macros/src/global_asm.rs7
-rw-r--r--compiler/rustc_builtin_macros/src/proc_macro_harness.rs8
-rw-r--r--compiler/rustc_builtin_macros/src/test.rs9
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs3
-rw-r--r--compiler/rustc_codegen_llvm/Cargo.toml2
-rw-r--r--compiler/rustc_codegen_llvm/src/back/lto.rs6
-rw-r--r--compiler/rustc_codegen_llvm/src/back/write.rs25
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs7
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs59
-rw-r--r--compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/write.rs6
-rw-r--r--compiler/rustc_data_structures/src/graph/iterate/mod.rs15
-rw-r--r--compiler/rustc_data_structures/src/lib.rs1
-rw-r--r--compiler/rustc_data_structures/src/temp_dir.rs2
-rw-r--r--compiler/rustc_error_codes/src/error_codes.rs1
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0224.md4
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0433.md16
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0607.md14
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0774.md24
-rw-r--r--compiler/rustc_expand/src/base.rs3
-rw-r--r--compiler/rustc_expand/src/build.rs56
-rw-r--r--compiler/rustc_expand/src/config.rs2
-rw-r--r--compiler/rustc_expand/src/expand.rs48
-rw-r--r--compiler/rustc_expand/src/mbe/transcribe.rs4
-rw-r--r--compiler/rustc_expand/src/module.rs15
-rw-r--r--compiler/rustc_expand/src/placeholders.rs24
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs12
-rw-r--r--compiler/rustc_feature/src/active.rs9
-rw-r--r--compiler/rustc_feature/src/lib.rs2
-rw-r--r--compiler/rustc_graphviz/src/lib.rs20
-rw-r--r--compiler/rustc_hir/src/def.rs9
-rw-r--r--compiler/rustc_hir/src/lib.rs1
-rw-r--r--compiler/rustc_index/src/bit_set.rs54
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs2
-rw-r--r--compiler/rustc_infer/src/infer/lub.rs2
-rw-r--r--compiler/rustc_interface/src/util.rs3
-rw-r--r--compiler/rustc_llvm/Cargo.toml16
-rw-r--r--compiler/rustc_llvm/build.rs322
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/.editorconfig6
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp226
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp70
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h115
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/Linker.cpp48
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp1655
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/README16
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp1721
-rw-r--r--compiler/rustc_llvm/src/lib.rs173
-rw-r--r--compiler/rustc_macros/src/lib.rs13
-rw-r--r--compiler/rustc_macros/src/session_diagnostic.rs666
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs8
-rw-r--r--compiler/rustc_middle/Cargo.toml1
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation.rs12
-rw-r--r--compiler/rustc_middle/src/mir/interpret/mod.rs32
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs42
-rw-r--r--compiler/rustc_middle/src/mir/type_foldable.rs2
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs2
-rw-r--r--compiler/rustc_middle/src/traits/query.rs68
-rw-r--r--compiler/rustc_middle/src/ty/trait_def.rs2
-rw-r--r--compiler/rustc_mir/Cargo.toml1
-rw-r--r--compiler/rustc_mir/src/borrow_check/def_use.rs3
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs119
-rw-r--r--compiler/rustc_mir/src/borrow_check/region_infer/values.rs4
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/cursor.rs41
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/direction.rs28
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/engine.rs116
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/fmt.rs172
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/graphviz.rs555
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/lattice.rs230
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/mod.rs135
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/tests.rs29
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/visitor.rs13
-rw-r--r--compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs17
-rw-r--r--compiler/rustc_mir/src/dataflow/impls/borrows.rs26
-rw-r--r--compiler/rustc_mir/src/dataflow/impls/init_locals.rs19
-rw-r--r--compiler/rustc_mir/src/dataflow/impls/liveness.rs18
-rw-r--r--compiler/rustc_mir/src/dataflow/impls/mod.rs85
-rw-r--r--compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs33
-rw-r--r--compiler/rustc_mir/src/dataflow/mod.rs6
-rw-r--r--compiler/rustc_mir/src/lib.rs1
-rw-r--r--compiler/rustc_mir/src/monomorphize/partitioning/mod.rs6
-rw-r--r--compiler/rustc_mir/src/transform/check_const_item_mutation.rs114
-rw-r--r--compiler/rustc_mir/src/transform/check_consts/resolver.rs18
-rw-r--r--compiler/rustc_mir/src/transform/inline.rs22
-rw-r--r--compiler/rustc_mir/src/transform/instcombine.rs52
-rw-r--r--compiler/rustc_mir/src/transform/instrument_coverage.rs4
-rw-r--r--compiler/rustc_mir/src/transform/mod.rs5
-rw-r--r--compiler/rustc_mir/src/transform/promote_consts.rs18
-rw-r--r--compiler/rustc_mir/src/transform/rustc_peek.rs17
-rw-r--r--compiler/rustc_mir/src/transform/simplify_try.rs8
-rw-r--r--compiler/rustc_mir/src/transform/validate.rs28
-rw-r--r--compiler/rustc_mir/src/util/find_self_call.rs35
-rw-r--r--compiler/rustc_mir/src/util/graphviz.rs29
-rw-r--r--compiler/rustc_mir/src/util/mod.rs2
-rw-r--r--compiler/rustc_mir/src/util/pretty.rs2
-rw-r--r--compiler/rustc_mir/src/util/spanview.rs379
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_constant.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_temp.rs3
-rw-r--r--compiler/rustc_mir_build/src/lib.rs1
-rw-r--r--compiler/rustc_mir_build/src/lints.rs6
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs15
-rw-r--r--compiler/rustc_mir_build/src/thir/mod.rs4
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs57
-rw-r--r--compiler/rustc_parse/src/lexer/tokentrees.rs24
-rw-r--r--compiler/rustc_parse/src/lexer/unicode_chars.rs2
-rw-r--r--compiler/rustc_parse/src/lib.rs30
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs2
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs11
-rw-r--r--compiler/rustc_parse/src/parser/item.rs84
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs44
-rw-r--r--compiler/rustc_parse/src/parser/nonterminal.rs75
-rw-r--r--compiler/rustc_parse/src/parser/path.rs9
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs4
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs2
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs19
-rw-r--r--compiler/rustc_resolve/src/check_unused.rs2
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs2
-rw-r--r--compiler/rustc_resolve/src/late.rs32
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs63
-rw-r--r--compiler/rustc_resolve/src/lib.rs18
-rw-r--r--compiler/rustc_save_analysis/src/lib.rs2
-rw-r--r--compiler/rustc_save_analysis/src/sig.rs2
-rw-r--r--compiler/rustc_session/src/config.rs4
-rw-r--r--compiler/rustc_session/src/lint/builtin.rs18
-rw-r--r--compiler/rustc_session/src/options.rs4
-rw-r--r--compiler/rustc_session/src/session.rs11
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_target/src/spec/avr_gnu_base.rs2
-rw-r--r--compiler/rustc_target/src/spec/wasm32_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs78
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs13
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs11
-rw-r--r--compiler/rustc_typeck/Cargo.toml1
-rw-r--r--compiler/rustc_typeck/src/astconv/generics.rs85
-rw-r--r--compiler/rustc_typeck/src/astconv/mod.rs57
-rw-r--r--compiler/rustc_typeck/src/check/compare_method.rs27
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs187
-rw-r--r--compiler/rustc_typeck/src/check/intrinsic.rs47
-rw-r--r--compiler/rustc_typeck/src/check/method/probe.rs11
-rw-r--r--compiler/rustc_typeck/src/check/pat.rs90
-rw-r--r--compiler/rustc_typeck/src/check/place_op.rs27
-rw-r--r--compiler/rustc_typeck/src/coherence/builtin.rs29
-rw-r--r--compiler/rustc_typeck/src/collect.rs57
-rw-r--r--compiler/rustc_typeck/src/collect/type_of.rs11
-rw-r--r--compiler/rustc_typeck/src/errors.rs199
-rw-r--r--compiler/rustc_typeck/src/impl_wf_check.rs18
-rw-r--r--compiler/rustc_typeck/src/lib.rs1
168 files changed, 8271 insertions, 1595 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 49aa1fc1735..dee3a16f9b1 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -96,6 +96,7 @@ pub struct Path {
     /// The segments in the path: the things separated by `::`.
     /// Global paths begin with `kw::PathRoot`.
     pub segments: Vec<PathSegment>,
+    pub tokens: Option<TokenStream>,
 }
 
 impl PartialEq<Symbol> for Path {
@@ -117,7 +118,7 @@ impl Path {
     // Convert a span and an identifier to the corresponding
     // one-segment path.
     pub fn from_ident(ident: Ident) -> Path {
-        Path { segments: vec![PathSegment::from_ident(ident)], span: ident.span }
+        Path { segments: vec![PathSegment::from_ident(ident)], span: ident.span, tokens: None }
     }
 
     pub fn is_global(&self) -> bool {
@@ -540,6 +541,7 @@ pub struct Block {
     /// Distinguishes between `unsafe { ... }` and `{ ... }`.
     pub rules: BlockCheckMode,
     pub span: Span,
+    pub tokens: Option<TokenStream>,
 }
 
 /// A match pattern.
@@ -586,7 +588,7 @@ impl Pat {
             _ => return None,
         };
 
-        Some(P(Ty { kind, id: self.id, span: self.span }))
+        Some(P(Ty { kind, id: self.id, span: self.span, tokens: None }))
     }
 
     /// Walk top-down and call `it` in each place where a pattern occurs
@@ -916,6 +918,7 @@ pub struct Stmt {
     pub id: NodeId,
     pub kind: StmtKind,
     pub span: Span,
+    pub tokens: Option<TokenStream>,
 }
 
 impl Stmt {
@@ -1068,7 +1071,7 @@ pub struct Expr {
 
 // `Expr` is used a lot. Make sure it doesn't unintentionally get bigger.
 #[cfg(target_arch = "x86_64")]
-rustc_data_structures::static_assert_size!(Expr, 104);
+rustc_data_structures::static_assert_size!(Expr, 112);
 
 impl Expr {
     /// Returns `true` if this expression would be valid somewhere that expects a value;
@@ -1168,7 +1171,7 @@ impl Expr {
             _ => return None,
         };
 
-        Some(P(Ty { kind, id: self.id, span: self.span }))
+        Some(P(Ty { kind, id: self.id, span: self.span, tokens: None }))
     }
 
     pub fn precedence(&self) -> ExprPrecedence {
@@ -1866,6 +1869,7 @@ pub struct Ty {
     pub id: NodeId,
     pub kind: TyKind,
     pub span: Span,
+    pub tokens: Option<TokenStream>,
 }
 
 #[derive(Clone, Encodable, Decodable, Debug)]
@@ -2144,7 +2148,7 @@ impl Param {
     /// Builds a `Param` object from `ExplicitSelf`.
     pub fn from_self(attrs: AttrVec, eself: ExplicitSelf, eself_ident: Ident) -> Param {
         let span = eself.span.to(eself_ident.span);
-        let infer_ty = P(Ty { id: DUMMY_NODE_ID, kind: TyKind::ImplicitSelf, span });
+        let infer_ty = P(Ty { id: DUMMY_NODE_ID, kind: TyKind::ImplicitSelf, span, tokens: None });
         let param = |mutbl, ty| Param {
             attrs,
             pat: P(Pat {
@@ -2167,6 +2171,7 @@ impl Param {
                     id: DUMMY_NODE_ID,
                     kind: TyKind::Rptr(lt, MutTy { ty: infer_ty, mutbl }),
                     span,
+                    tokens: None,
                 }),
             ),
         }
@@ -2289,12 +2294,15 @@ impl FnRetTy {
 /// Module declaration.
 ///
 /// E.g., `mod foo;` or `mod foo { .. }`.
-#[derive(Clone, Encodable, Decodable, Debug, Default)]
+#[derive(Clone, Encodable, Decodable, Debug)]
 pub struct Mod {
     /// A span from the first token past `{` to the last token until `}`.
     /// For `mod foo;`, the inner span ranges from the first token
     /// to the last token in the external file.
     pub inner: Span,
+    /// `unsafe` keyword accepted syntactically for macro DSLs, but not
+    /// semantically by Rust.
+    pub unsafety: Unsafe,
     pub items: Vec<P<Item>>,
     /// `true` for `mod foo { .. }`; `false` for `mod foo;`.
     pub inline: bool,
@@ -2302,9 +2310,12 @@ pub struct Mod {
 
 /// Foreign module declaration.
 ///
-/// E.g., `extern { .. }` or `extern C { .. }`.
+/// E.g., `extern { .. }` or `extern "C" { .. }`.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct ForeignMod {
+    /// `unsafe` keyword accepted syntactically for macro DSLs, but not
+    /// semantically by Rust.
+    pub unsafety: Unsafe,
     pub abi: Option<StrLit>,
     pub items: Vec<P<ForeignItem>>,
 }
@@ -2410,6 +2421,7 @@ impl<D: Decoder> rustc_serialize::Decodable<D> for AttrId {
 pub struct AttrItem {
     pub path: Path,
     pub args: MacArgs,
+    pub tokens: Option<TokenStream>,
 }
 
 /// A list of attributes.
@@ -2479,7 +2491,12 @@ pub enum CrateSugar {
     JustCrate,
 }
 
-pub type Visibility = Spanned<VisibilityKind>;
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct Visibility {
+    pub kind: VisibilityKind,
+    pub span: Span,
+    pub tokens: Option<TokenStream>,
+}
 
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum VisibilityKind {
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 12d6f7cc33d..2782869fb88 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -8,7 +8,7 @@ use crate::ast::{Path, PathSegment};
 use crate::mut_visit::visit_clobber;
 use crate::ptr::P;
 use crate::token::{self, CommentKind, Token};
-use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};
+use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing};
 
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_span::source_map::{BytePos, Spanned};
@@ -330,7 +330,7 @@ crate fn mk_attr_id() -> AttrId {
 }
 
 pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attribute {
-    mk_attr_from_item(style, AttrItem { path, args }, span)
+    mk_attr_from_item(style, AttrItem { path, args, tokens: None }, span)
 }
 
 pub fn mk_attr_from_item(style: AttrStyle, item: AttrItem, span: Span) -> Attribute {
@@ -361,7 +361,7 @@ pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
 }
 
 impl MetaItem {
-    fn token_trees_and_joints(&self) -> Vec<TreeAndJoint> {
+    fn token_trees_and_spacings(&self) -> Vec<TreeAndSpacing> {
         let mut idents = vec![];
         let mut last_pos = BytePos(0 as u32);
         for (i, segment) in self.path.segments.iter().enumerate() {
@@ -374,7 +374,7 @@ impl MetaItem {
             idents.push(TokenTree::Token(Token::from_ast_ident(segment.ident)).into());
             last_pos = segment.ident.span.hi();
         }
-        idents.extend(self.kind.token_trees_and_joints(self.span));
+        idents.extend(self.kind.token_trees_and_spacings(self.span));
         idents
     }
 
@@ -415,7 +415,7 @@ impl MetaItem {
                     }
                 }
                 let span = span.with_hi(segments.last().unwrap().ident.span.hi());
-                Path { span, segments }
+                Path { span, segments, tokens: None }
             }
             Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. })) => match *nt {
                 token::Nonterminal::NtMeta(ref item) => return item.meta(item.path.span),
@@ -447,7 +447,7 @@ impl MetaItemKind {
                     if i > 0 {
                         tts.push(TokenTree::token(token::Comma, span).into());
                     }
-                    tts.extend(item.token_trees_and_joints())
+                    tts.extend(item.token_trees_and_spacings())
                 }
                 MacArgs::Delimited(
                     DelimSpan::from_single(span),
@@ -458,7 +458,7 @@ impl MetaItemKind {
         }
     }
 
-    fn token_trees_and_joints(&self, span: Span) -> Vec<TreeAndJoint> {
+    fn token_trees_and_spacings(&self, span: Span) -> Vec<TreeAndSpacing> {
         match *self {
             MetaItemKind::Word => vec![],
             MetaItemKind::NameValue(ref lit) => {
@@ -470,7 +470,7 @@ impl MetaItemKind {
                     if i > 0 {
                         tokens.push(TokenTree::token(token::Comma, span).into());
                     }
-                    tokens.extend(item.token_trees_and_joints())
+                    tokens.extend(item.token_trees_and_spacings())
                 }
                 vec![
                     TokenTree::Delimited(
@@ -553,9 +553,9 @@ impl NestedMetaItem {
         }
     }
 
-    fn token_trees_and_joints(&self) -> Vec<TreeAndJoint> {
+    fn token_trees_and_spacings(&self) -> Vec<TreeAndSpacing> {
         match *self {
-            NestedMetaItem::MetaItem(ref item) => item.token_trees_and_joints(),
+            NestedMetaItem::MetaItem(ref item) => item.token_trees_and_spacings(),
             NestedMetaItem::Literal(ref lit) => vec![lit.token_tree().into()],
         }
     }
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 3ef83ef3fc9..425ef83b57a 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -14,7 +14,7 @@ use crate::tokenstream::*;
 
 use rustc_data_structures::map_in_place::MapInPlace;
 use rustc_data_structures::sync::Lrc;
-use rustc_span::source_map::{respan, Spanned};
+use rustc_span::source_map::Spanned;
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
 
@@ -451,7 +451,7 @@ pub fn noop_visit_ty_constraint<T: MutVisitor>(
 }
 
 pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
-    let Ty { id, kind, span } = ty.deref_mut();
+    let Ty { id, kind, span, tokens: _ } = ty.deref_mut();
     vis.visit_id(id);
     match kind {
         TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err | TyKind::Never | TyKind::CVarArgs => {}
@@ -490,7 +490,7 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
 }
 
 pub fn noop_visit_foreign_mod<T: MutVisitor>(foreign_mod: &mut ForeignMod, vis: &mut T) {
-    let ForeignMod { abi: _, items } = foreign_mod;
+    let ForeignMod { unsafety: _, abi: _, items } = foreign_mod;
     items.flat_map_in_place(|item| vis.flat_map_foreign_item(item));
 }
 
@@ -513,7 +513,7 @@ pub fn noop_visit_ident<T: MutVisitor>(Ident { name: _, span }: &mut Ident, vis:
     vis.visit_span(span);
 }
 
-pub fn noop_visit_path<T: MutVisitor>(Path { segments, span }: &mut Path, vis: &mut T) {
+pub fn noop_visit_path<T: MutVisitor>(Path { segments, span, tokens: _ }: &mut Path, vis: &mut T) {
     vis.visit_span(span);
     for PathSegment { ident, id, args } in segments {
         vis.visit_ident(ident);
@@ -579,7 +579,7 @@ pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
 pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
     let Attribute { kind, id: _, style: _, span } = attr;
     match kind {
-        AttrKind::Normal(AttrItem { path, args }) => {
+        AttrKind::Normal(AttrItem { path, args, tokens: _ }) => {
             vis.visit_path(path);
             visit_mac_args(args, vis);
         }
@@ -709,7 +709,7 @@ pub fn noop_visit_interpolated<T: MutVisitor>(nt: &mut token::Nonterminal, vis:
         token::NtLifetime(ident) => vis.visit_ident(ident),
         token::NtLiteral(expr) => vis.visit_expr(expr),
         token::NtMeta(item) => {
-            let AttrItem { path, args } = item.deref_mut();
+            let AttrItem { path, args, tokens: _ } = item.deref_mut();
             vis.visit_path(path);
             visit_mac_args(args, vis);
         }
@@ -871,7 +871,7 @@ pub fn noop_visit_mt<T: MutVisitor>(MutTy { ty, mutbl: _ }: &mut MutTy, vis: &mu
 }
 
 pub fn noop_visit_block<T: MutVisitor>(block: &mut P<Block>, vis: &mut T) {
-    let Block { id, stmts, rules: _, span } = block.deref_mut();
+    let Block { id, stmts, rules: _, span, tokens: _ } = block.deref_mut();
     vis.visit_id(id);
     stmts.flat_map_in_place(|stmt| vis.flat_map_stmt(stmt));
     vis.visit_span(span);
@@ -970,18 +970,21 @@ pub fn noop_visit_fn_header<T: MutVisitor>(header: &mut FnHeader, vis: &mut T) {
     vis.visit_asyncness(asyncness);
 }
 
-pub fn noop_visit_mod<T: MutVisitor>(Mod { inner, items, inline: _ }: &mut Mod, vis: &mut T) {
+pub fn noop_visit_mod<T: MutVisitor>(module: &mut Mod, vis: &mut T) {
+    let Mod { inner, unsafety: _, items, inline: _ } = module;
     vis.visit_span(inner);
     items.flat_map_in_place(|item| vis.flat_map_item(item));
 }
 
 pub fn noop_visit_crate<T: MutVisitor>(krate: &mut Crate, vis: &mut T) {
     visit_clobber(krate, |Crate { module, attrs, span, proc_macros }| {
+        let item_vis =
+            Visibility { kind: VisibilityKind::Public, span: span.shrink_to_lo(), tokens: None };
         let item = P(Item {
             ident: Ident::invalid(),
             attrs,
             id: DUMMY_NODE_ID,
-            vis: respan(span.shrink_to_lo(), VisibilityKind::Public),
+            vis: item_vis,
             span,
             kind: ItemKind::Mod(module),
             tokens: None,
@@ -990,7 +993,7 @@ pub fn noop_visit_crate<T: MutVisitor>(krate: &mut Crate, vis: &mut T) {
 
         let len = items.len();
         if len == 0 {
-            let module = Mod { inner: span, items: vec![], inline: true };
+            let module = Mod { inner: span, unsafety: Unsafe::No, items: vec![], inline: true };
             Crate { module, attrs: vec![], span, proc_macros }
         } else if len == 1 {
             let Item { attrs, span, kind, .. } = items.into_iter().next().unwrap().into_inner();
@@ -1283,12 +1286,15 @@ pub fn noop_filter_map_expr<T: MutVisitor>(mut e: P<Expr>, vis: &mut T) -> Optio
 }
 
 pub fn noop_flat_map_stmt<T: MutVisitor>(
-    Stmt { kind, mut span, mut id }: Stmt,
+    Stmt { kind, mut span, mut id, tokens }: Stmt,
     vis: &mut T,
 ) -> SmallVec<[Stmt; 1]> {
     vis.visit_id(&mut id);
     vis.visit_span(&mut span);
-    noop_flat_map_stmt_kind(kind, vis).into_iter().map(|kind| Stmt { id, kind, span }).collect()
+    noop_flat_map_stmt_kind(kind, vis)
+        .into_iter()
+        .map(|kind| Stmt { id, kind, span, tokens: tokens.clone() })
+        .collect()
 }
 
 pub fn noop_flat_map_stmt_kind<T: MutVisitor>(
@@ -1313,13 +1319,13 @@ pub fn noop_flat_map_stmt_kind<T: MutVisitor>(
     }
 }
 
-pub fn noop_visit_vis<T: MutVisitor>(Spanned { node, span }: &mut Visibility, vis: &mut T) {
-    match node {
+pub fn noop_visit_vis<T: MutVisitor>(visibility: &mut Visibility, vis: &mut T) {
+    match &mut visibility.kind {
         VisibilityKind::Public | VisibilityKind::Crate(_) | VisibilityKind::Inherited => {}
         VisibilityKind::Restricted { path, id } => {
             vis.visit_path(path);
             vis.visit_id(id);
         }
     }
-    vis.visit_span(span);
+    vis.visit_span(&mut visibility.span);
 }
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index c6cc890b47f..d5b3e87adc3 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -173,6 +173,7 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool {
             kw::Move,
             kw::Return,
             kw::True,
+            kw::Try,
             kw::Unsafe,
             kw::While,
             kw::Yield,
@@ -699,7 +700,7 @@ pub enum Nonterminal {
 
 // `Nonterminal` is used a lot. Make sure it doesn't unintentionally get bigger.
 #[cfg(target_arch = "x86_64")]
-rustc_data_structures::static_assert_size!(Nonterminal, 40);
+rustc_data_structures::static_assert_size!(Nonterminal, 48);
 
 #[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable)]
 pub enum NonterminalKind {
@@ -809,9 +810,19 @@ impl Nonterminal {
             if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind {
                 let filename = source_map.span_to_filename(orig_span);
                 if let FileName::Real(RealFileName::Named(path)) = filename {
-                    if (path.ends_with("time-macros-impl/src/lib.rs")
-                        && macro_name == sym::impl_macros)
-                        || (path.ends_with("js-sys/src/lib.rs") && macro_name == sym::arrays)
+                    let matches_prefix = |prefix| {
+                        // Check for a path that ends with 'prefix*/src/lib.rs'
+                        let mut iter = path.components().rev();
+                        iter.next().and_then(|p| p.as_os_str().to_str()) == Some("lib.rs")
+                            && iter.next().and_then(|p| p.as_os_str().to_str()) == Some("src")
+                            && iter
+                                .next()
+                                .and_then(|p| p.as_os_str().to_str())
+                                .map_or(false, |p| p.starts_with(prefix))
+                    };
+
+                    if (macro_name == sym::impl_macros && matches_prefix("time-macros-impl"))
+                        || (macro_name == sym::arrays && matches_prefix("js-sys"))
                     {
                         let snippet = source_map.span_to_snippet(orig_span);
                         if snippet.as_deref() == Ok("$name") {
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index 151acddae84..f201f0b5c66 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -83,7 +83,7 @@ impl TokenTree {
     }
 
     pub fn joint(self) -> TokenStream {
-        TokenStream::new(vec![(self, Joint)])
+        TokenStream::new(vec![(self, Spacing::Joint)])
     }
 
     pub fn token(kind: TokenKind, span: Span) -> TokenTree {
@@ -125,22 +125,20 @@ where
 /// instead of a representation of the abstract syntax tree.
 /// Today's `TokenTree`s can still contain AST via `token::Interpolated` for back-compat.
 #[derive(Clone, Debug, Default, Encodable, Decodable)]
-pub struct TokenStream(pub Lrc<Vec<TreeAndJoint>>);
+pub struct TokenStream(pub Lrc<Vec<TreeAndSpacing>>);
 
-pub type TreeAndJoint = (TokenTree, IsJoint);
+pub type TreeAndSpacing = (TokenTree, Spacing);
 
 // `TokenStream` is used a lot. Make sure it doesn't unintentionally get bigger.
 #[cfg(target_arch = "x86_64")]
 rustc_data_structures::static_assert_size!(TokenStream, 8);
 
 #[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable)]
-pub enum IsJoint {
+pub enum Spacing {
+    Alone,
     Joint,
-    NonJoint,
 }
 
-use IsJoint::*;
-
 impl TokenStream {
     /// Given a `TokenStream` with a `Stream` of only two arguments, return a new `TokenStream`
     /// separating the two arguments with a comma for diagnostic suggestions.
@@ -153,7 +151,7 @@ impl TokenStream {
                 let sp = match (&ts, &next) {
                     (_, (TokenTree::Token(Token { kind: token::Comma, .. }), _)) => continue,
                     (
-                        (TokenTree::Token(token_left), NonJoint),
+                        (TokenTree::Token(token_left), Spacing::Alone),
                         (TokenTree::Token(token_right), _),
                     ) if ((token_left.is_ident() && !token_left.is_reserved_ident())
                         || token_left.is_lit())
@@ -162,11 +160,11 @@ impl TokenStream {
                     {
                         token_left.span
                     }
-                    ((TokenTree::Delimited(sp, ..), NonJoint), _) => sp.entire(),
+                    ((TokenTree::Delimited(sp, ..), Spacing::Alone), _) => sp.entire(),
                     _ => continue,
                 };
                 let sp = sp.shrink_to_hi();
-                let comma = (TokenTree::token(token::Comma, sp), NonJoint);
+                let comma = (TokenTree::token(token::Comma, sp), Spacing::Alone);
                 suggestion = Some((pos, comma, sp));
             }
         }
@@ -184,19 +182,19 @@ impl TokenStream {
 
 impl From<TokenTree> for TokenStream {
     fn from(tree: TokenTree) -> TokenStream {
-        TokenStream::new(vec![(tree, NonJoint)])
+        TokenStream::new(vec![(tree, Spacing::Alone)])
     }
 }
 
-impl From<TokenTree> for TreeAndJoint {
-    fn from(tree: TokenTree) -> TreeAndJoint {
-        (tree, NonJoint)
+impl From<TokenTree> for TreeAndSpacing {
+    fn from(tree: TokenTree) -> TreeAndSpacing {
+        (tree, Spacing::Alone)
     }
 }
 
 impl iter::FromIterator<TokenTree> for TokenStream {
     fn from_iter<I: IntoIterator<Item = TokenTree>>(iter: I) -> Self {
-        TokenStream::new(iter.into_iter().map(Into::into).collect::<Vec<TreeAndJoint>>())
+        TokenStream::new(iter.into_iter().map(Into::into).collect::<Vec<TreeAndSpacing>>())
     }
 }
 
@@ -209,7 +207,7 @@ impl PartialEq<TokenStream> for TokenStream {
 }
 
 impl TokenStream {
-    pub fn new(streams: Vec<TreeAndJoint>) -> TokenStream {
+    pub fn new(streams: Vec<TreeAndSpacing>) -> TokenStream {
         TokenStream(Lrc::new(streams))
     }
 
@@ -320,11 +318,11 @@ impl TokenStreamBuilder {
         // If `self` is not empty and the last tree within the last stream is a
         // token tree marked with `Joint`...
         if let Some(TokenStream(ref mut last_stream_lrc)) = self.0.last_mut() {
-            if let Some((TokenTree::Token(last_token), Joint)) = last_stream_lrc.last() {
+            if let Some((TokenTree::Token(last_token), Spacing::Joint)) = last_stream_lrc.last() {
                 // ...and `stream` is not empty and the first tree within it is
                 // a token tree...
                 let TokenStream(ref mut stream_lrc) = stream;
-                if let Some((TokenTree::Token(token), is_joint)) = stream_lrc.first() {
+                if let Some((TokenTree::Token(token), spacing)) = stream_lrc.first() {
                     // ...and the two tokens can be glued together...
                     if let Some(glued_tok) = last_token.glue(&token) {
                         // ...then do so, by overwriting the last token
@@ -337,8 +335,7 @@ impl TokenStreamBuilder {
                         // Overwrite the last token tree with the merged
                         // token.
                         let last_vec_mut = Lrc::make_mut(last_stream_lrc);
-                        *last_vec_mut.last_mut().unwrap() =
-                            (TokenTree::Token(glued_tok), *is_joint);
+                        *last_vec_mut.last_mut().unwrap() = (TokenTree::Token(glued_tok), *spacing);
 
                         // Remove the first token tree from `stream`. (This
                         // is almost always the only tree in `stream`.)
@@ -375,7 +372,7 @@ impl Iterator for Cursor {
     type Item = TokenTree;
 
     fn next(&mut self) -> Option<TokenTree> {
-        self.next_with_joint().map(|(tree, _)| tree)
+        self.next_with_spacing().map(|(tree, _)| tree)
     }
 }
 
@@ -384,7 +381,7 @@ impl Cursor {
         Cursor { stream, index: 0 }
     }
 
-    pub fn next_with_joint(&mut self) -> Option<TreeAndJoint> {
+    pub fn next_with_spacing(&mut self) -> Option<TreeAndSpacing> {
         if self.index < self.stream.len() {
             self.index += 1;
             Some(self.stream.0[self.index - 1].clone())
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 234ce280f97..86fd87f6c42 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -879,7 +879,7 @@ pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) {
 }
 
 pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) {
-    if let VisibilityKind::Restricted { ref path, id } = vis.node {
+    if let VisibilityKind::Restricted { ref path, id } = vis.kind {
         visitor.visit_path(path, id);
     }
 }
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index f3309afec7d..6d41b7836b1 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -251,7 +251,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             ItemKind::ExternCrate(orig_name) => hir::ItemKind::ExternCrate(orig_name),
             ItemKind::Use(ref use_tree) => {
                 // Start with an empty prefix.
-                let prefix = Path { segments: vec![], span: use_tree.span };
+                let prefix = Path { segments: vec![], span: use_tree.span, tokens: None };
 
                 self.lower_use_tree(use_tree, &prefix, id, vis, ident, attrs)
             }
@@ -488,7 +488,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 *ident = tree.ident();
 
                 // First, apply the prefix to the path.
-                let mut path = Path { segments, span: path.span };
+                let mut path = Path { segments, span: path.span, tokens: None };
 
                 // Correctly resolve `self` imports.
                 if path.segments.len() > 1
@@ -540,8 +540,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 hir::ItemKind::Use(path, hir::UseKind::Single)
             }
             UseTreeKind::Glob => {
-                let path =
-                    self.lower_path(id, &Path { segments, span: path.span }, ParamMode::Explicit);
+                let path = self.lower_path(
+                    id,
+                    &Path { segments, span: path.span, tokens: None },
+                    ParamMode::Explicit,
+                );
                 hir::ItemKind::Use(path, hir::UseKind::Glob)
             }
             UseTreeKind::Nested(ref trees) => {
@@ -569,7 +572,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 // for that we return the `{}` import (called the
                 // `ListStem`).
 
-                let prefix = Path { segments, span: prefix.span.to(path.span) };
+                let prefix = Path { segments, span: prefix.span.to(path.span), tokens: None };
 
                 // Add all the nested `PathListItem`s to the HIR.
                 for &(ref use_tree, id) in trees {
@@ -927,7 +930,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         v: &Visibility,
         explicit_owner: Option<NodeId>,
     ) -> hir::Visibility<'hir> {
-        let node = match v.node {
+        let node = match v.kind {
             VisibilityKind::Public => hir::VisibilityKind::Public,
             VisibilityKind::Crate(sugar) => hir::VisibilityKind::Crate(sugar),
             VisibilityKind::Restricted { ref path, id } => {
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 586355fe613..a28d022c661 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -967,6 +967,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             AttrKind::Normal(ref item) => AttrKind::Normal(AttrItem {
                 path: item.path.clone(),
                 args: self.lower_mac_args(&item.args),
+                tokens: None,
             }),
             AttrKind::DocComment(comment_kind, data) => AttrKind::DocComment(comment_kind, data),
         };
@@ -1106,6 +1107,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                                 id: node_id,
                                 kind: TyKind::ImplTrait(impl_trait_node_id, bounds.clone()),
                                 span: constraint.span,
+                                tokens: None,
                             },
                             itctx,
                         );
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index a01dd8c939c..31c05325d1d 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -198,13 +198,13 @@ impl<'a> AstValidator<'a> {
     }
 
     fn invalid_visibility(&self, vis: &Visibility, note: Option<&str>) {
-        if let VisibilityKind::Inherited = vis.node {
+        if let VisibilityKind::Inherited = vis.kind {
             return;
         }
 
         let mut err =
             struct_span_err!(self.session, vis.span, E0449, "unnecessary visibility qualifier");
-        if vis.node.is_pub() {
+        if vis.kind.is_pub() {
             err.span_label(vis.span, "`pub` not permitted here because it's implied");
         }
         if let Some(note) = note {
@@ -990,12 +990,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     self.error_item_without_body(item.span, "function", msg, " { <body> }");
                 }
             }
-            ItemKind::ForeignMod(_) => {
+            ItemKind::ForeignMod(ForeignMod { unsafety, .. }) => {
                 let old_item = mem::replace(&mut self.extern_mod, Some(item));
                 self.invalid_visibility(
                     &item.vis,
                     Some("place qualifiers on individual foreign items instead"),
                 );
+                if let Unsafe::Yes(span) = unsafety {
+                    self.err_handler().span_err(span, "extern block cannot be declared unsafe");
+                }
                 visit::walk_item(self, item);
                 self.extern_mod = old_item;
                 return; // Avoid visiting again.
@@ -1029,7 +1032,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 walk_list!(self, visit_attribute, &item.attrs);
                 return;
             }
-            ItemKind::Mod(Mod { inline, .. }) => {
+            ItemKind::Mod(Mod { inline, unsafety, .. }) => {
+                if let Unsafe::Yes(span) = unsafety {
+                    self.err_handler().span_err(span, "module cannot be declared unsafe");
+                }
                 // Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
                 if !inline && !self.session.contains_name(&item.attrs, sym::path) {
                     self.check_mod_file_item_asciionly(item.ident);
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 0ee8ef55e61..40643da2881 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -594,7 +594,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
     }
 
     fn visit_vis(&mut self, vis: &'a ast::Visibility) {
-        if let ast::VisibilityKind::Crate(ast::CrateSugar::JustCrate) = vis.node {
+        if let ast::VisibilityKind::Crate(ast::CrateSugar::JustCrate) = vis.kind {
             gate_feature_post!(
                 &self,
                 crate_visibility_modifier,
@@ -608,6 +608,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
 
 pub fn check_crate(krate: &ast::Crate, sess: &Session) {
     maybe_stage_features(sess, krate);
+    check_incompatible_features(sess);
     let mut visitor = PostExpansionVisitor { sess, features: &sess.features_untracked() };
 
     let spans = sess.parse_sess.gated_spans.spans.borrow();
@@ -677,3 +678,36 @@ fn maybe_stage_features(sess: &Session, krate: &ast::Crate) {
         }
     }
 }
+
+fn check_incompatible_features(sess: &Session) {
+    let features = sess.features_untracked();
+
+    let declared_features = features
+        .declared_lang_features
+        .iter()
+        .copied()
+        .map(|(name, span, _)| (name, span))
+        .chain(features.declared_lib_features.iter().copied());
+
+    for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES
+        .iter()
+        .filter(|&&(f1, f2)| features.enabled(f1) && features.enabled(f2))
+    {
+        if let Some((f1_name, f1_span)) = declared_features.clone().find(|(name, _)| name == f1) {
+            if let Some((f2_name, f2_span)) = declared_features.clone().find(|(name, _)| name == f2)
+            {
+                let spans = vec![f1_span, f2_span];
+                sess.struct_span_err(
+                    spans.clone(),
+                    &format!(
+                        "features `{}` and `{}` are incompatible, using them at the same time \
+                        is not allowed",
+                        f1_name, f2_name
+                    ),
+                )
+                .help("remove one of these features")
+                .emit();
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_ast_pretty/src/pprust.rs b/compiler/rustc_ast_pretty/src/pprust.rs
index 9743a000429..d16b541c699 100644
--- a/compiler/rustc_ast_pretty/src/pprust.rs
+++ b/compiler/rustc_ast_pretty/src/pprust.rs
@@ -1139,7 +1139,11 @@ impl<'a> State<'a> {
                 self.print_fn_full(sig, item.ident, gen, &item.vis, def, body, &item.attrs);
             }
             ast::ItemKind::Mod(ref _mod) => {
-                self.head(visibility_qualified(&item.vis, "mod"));
+                self.head(to_string(|s| {
+                    s.print_visibility(&item.vis);
+                    s.print_unsafety(_mod.unsafety);
+                    s.word("mod");
+                }));
                 self.print_ident(item.ident);
 
                 if _mod.inline || self.is_expanded {
@@ -1154,7 +1158,10 @@ impl<'a> State<'a> {
                 }
             }
             ast::ItemKind::ForeignMod(ref nmod) => {
-                self.head("extern");
+                self.head(to_string(|s| {
+                    s.print_unsafety(nmod.unsafety);
+                    s.word("extern");
+                }));
                 if let Some(abi) = nmod.abi {
                     self.print_literal(&abi.as_lit());
                     self.nbsp();
@@ -1352,7 +1359,7 @@ impl<'a> State<'a> {
     }
 
     crate fn print_visibility(&mut self, vis: &ast::Visibility) {
-        match vis.node {
+        match vis.kind {
             ast::VisibilityKind::Public => self.word_nbsp("pub"),
             ast::VisibilityKind::Crate(sugar) => match sugar {
                 ast::CrateSugar::PubCrate => self.word_nbsp("pub(crate)"),
diff --git a/compiler/rustc_ast_pretty/src/pprust/tests.rs b/compiler/rustc_ast_pretty/src/pprust/tests.rs
index fdbf3feb900..b1a73a0bf02 100644
--- a/compiler/rustc_ast_pretty/src/pprust/tests.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/tests.rs
@@ -1,7 +1,6 @@
 use super::*;
 
 use rustc_ast as ast;
-use rustc_span::source_map::respan;
 use rustc_span::symbol::Ident;
 use rustc_span::with_default_session_globals;
 
@@ -45,7 +44,11 @@ fn test_variant_to_string() {
 
         let var = ast::Variant {
             ident,
-            vis: respan(rustc_span::DUMMY_SP, ast::VisibilityKind::Inherited),
+            vis: ast::Visibility {
+                span: rustc_span::DUMMY_SP,
+                kind: ast::VisibilityKind::Inherited,
+                tokens: None,
+            },
             attrs: Vec::new(),
             id: ast::DUMMY_NODE_ID,
             data: ast::VariantData::Unit(ast::DUMMY_NODE_ID),
diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
index 34e2accc615..5ed8b69d92a 100644
--- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
+++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
@@ -15,7 +15,7 @@ pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) -
         );
 
         let start_span = parser.token.span;
-        let AttrItem { path, args } = match parser.parse_attr_item() {
+        let AttrItem { path, args, tokens: _ } = match parser.parse_attr_item() {
             Ok(ai) => ai,
             Err(mut err) => {
                 err.emit();
diff --git a/compiler/rustc_builtin_macros/src/concat_idents.rs b/compiler/rustc_builtin_macros/src/concat_idents.rs
index 8223cdda072..c4d1c6eee31 100644
--- a/compiler/rustc_builtin_macros/src/concat_idents.rs
+++ b/compiler/rustc_builtin_macros/src/concat_idents.rs
@@ -61,6 +61,7 @@ pub fn expand_concat_idents<'cx>(
                 id: ast::DUMMY_NODE_ID,
                 kind: ast::TyKind::Path(None, ast::Path::from_ident(self.ident)),
                 span: self.ident.span,
+                tokens: None,
             }))
         }
     }
diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs
index 120e859f2b1..d84b3956475 100644
--- a/compiler/rustc_builtin_macros/src/deriving/debug.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs
@@ -133,5 +133,5 @@ fn stmt_let_underscore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<ast::Expr>) -> as
         span: sp,
         attrs: ast::AttrVec::new(),
     });
-    ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp }
+    ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp, tokens: None }
 }
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 849e8b136e1..d235caec103 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -187,7 +187,6 @@ use rustc_ast::{GenericArg, GenericParamKind, VariantData};
 use rustc_attr as attr;
 use rustc_data_structures::map_in_place::MapInPlace;
 use rustc_expand::base::{Annotatable, ExtCtxt};
-use rustc_span::source_map::respan;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::Span;
 
@@ -532,7 +531,11 @@ impl<'a> TraitDef<'a> {
                 id: ast::DUMMY_NODE_ID,
                 span: self.span,
                 ident,
-                vis: respan(self.span.shrink_to_lo(), ast::VisibilityKind::Inherited),
+                vis: ast::Visibility {
+                    span: self.span.shrink_to_lo(),
+                    kind: ast::VisibilityKind::Inherited,
+                    tokens: None,
+                },
                 attrs: Vec::new(),
                 kind: ast::AssocItemKind::TyAlias(
                     ast::Defaultness::Final,
@@ -933,7 +936,11 @@ impl<'a> MethodDef<'a> {
             id: ast::DUMMY_NODE_ID,
             attrs: self.attributes.clone(),
             span: trait_.span,
-            vis: respan(trait_lo_sp, ast::VisibilityKind::Inherited),
+            vis: ast::Visibility {
+                span: trait_lo_sp,
+                kind: ast::VisibilityKind::Inherited,
+                tokens: None,
+            },
             ident: method_ident,
             kind: ast::AssocItemKind::Fn(def, sig, fn_generics, Some(body_block)),
             tokens: None,
diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs
index 7e3fd131d44..9c8e0fc2f01 100644
--- a/compiler/rustc_builtin_macros/src/deriving/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs
@@ -75,6 +75,7 @@ fn call_intrinsic(
         id: ast::DUMMY_NODE_ID,
         rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated),
         span,
+        tokens: None,
     }))
 }
 
diff --git a/compiler/rustc_builtin_macros/src/format_foreign.rs b/compiler/rustc_builtin_macros/src/format_foreign.rs
index 85cf4c42e94..b39423b86e7 100644
--- a/compiler/rustc_builtin_macros/src/format_foreign.rs
+++ b/compiler/rustc_builtin_macros/src/format_foreign.rs
@@ -166,14 +166,14 @@ pub mod printf {
             let cap = self.span.len() + if has_options { 2 } else { 0 };
             let mut s = String::with_capacity(cap);
 
-            s.push_str("{");
+            s.push('{');
 
             if let Some(arg) = self.parameter {
                 write!(s, "{}", arg.checked_sub(1)?).ok()?;
             }
 
             if has_options {
-                s.push_str(":");
+                s.push(':');
 
                 let align = if let Some(fill) = fill {
                     s.push_str(fill);
@@ -191,11 +191,11 @@ pub mod printf {
                 }
 
                 if alt {
-                    s.push_str("#");
+                    s.push('#');
                 }
 
                 if zero_fill {
-                    s.push_str("0");
+                    s.push('0');
                 }
 
                 if let Some(width) = width {
@@ -203,7 +203,7 @@ pub mod printf {
                 }
 
                 if let Some(precision) = precision {
-                    s.push_str(".");
+                    s.push('.');
                     precision.translate(&mut s).ok()?;
                 }
 
@@ -212,7 +212,7 @@ pub mod printf {
                 }
             }
 
-            s.push_str("}");
+            s.push('}');
             Some(s)
         }
     }
diff --git a/compiler/rustc_builtin_macros/src/global_asm.rs b/compiler/rustc_builtin_macros/src/global_asm.rs
index 2465f33622e..3689e33be6f 100644
--- a/compiler/rustc_builtin_macros/src/global_asm.rs
+++ b/compiler/rustc_builtin_macros/src/global_asm.rs
@@ -14,7 +14,6 @@ use rustc_ast::token;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_errors::DiagnosticBuilder;
 use rustc_expand::base::{self, *};
-use rustc_span::source_map::respan;
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
 use smallvec::smallvec;
@@ -30,7 +29,11 @@ pub fn expand_global_asm<'cx>(
             attrs: Vec::new(),
             id: ast::DUMMY_NODE_ID,
             kind: ast::ItemKind::GlobalAsm(P(global_asm)),
-            vis: respan(sp.shrink_to_lo(), ast::VisibilityKind::Inherited),
+            vis: ast::Visibility {
+                span: sp.shrink_to_lo(),
+                kind: ast::VisibilityKind::Inherited,
+                tokens: None,
+            },
             span: cx.with_def_site_ctxt(sp),
             tokens: None,
         })]),
diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
index 0c6769906f3..c6ab3faf568 100644
--- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
+++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
@@ -98,7 +98,7 @@ pub fn inject(
 
 impl<'a> CollectProcMacros<'a> {
     fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) {
-        if self.is_proc_macro_crate && self.in_root && vis.node.is_pub() {
+        if self.is_proc_macro_crate && self.in_root && vis.kind.is_pub() {
             self.handler.span_err(
                 sp,
                 "`proc-macro` crate types currently cannot export any items other \
@@ -184,7 +184,7 @@ impl<'a> CollectProcMacros<'a> {
             Vec::new()
         };
 
-        if self.in_root && item.vis.node.is_pub() {
+        if self.in_root && item.vis.kind.is_pub() {
             self.macros.push(ProcMacro::Derive(ProcMacroDerive {
                 id: item.id,
                 span: item.span,
@@ -204,7 +204,7 @@ impl<'a> CollectProcMacros<'a> {
     }
 
     fn collect_attr_proc_macro(&mut self, item: &'a ast::Item) {
-        if self.in_root && item.vis.node.is_pub() {
+        if self.in_root && item.vis.kind.is_pub() {
             self.macros.push(ProcMacro::Def(ProcMacroDef {
                 id: item.id,
                 span: item.span,
@@ -223,7 +223,7 @@ impl<'a> CollectProcMacros<'a> {
     }
 
     fn collect_bang_proc_macro(&mut self, item: &'a ast::Item) {
-        if self.in_root && item.vis.node.is_pub() {
+        if self.in_root && item.vis.kind.is_pub() {
             self.macros.push(ProcMacro::Def(ProcMacroDef {
                 id: item.id,
                 span: item.span,
diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs
index 8e56e80bba2..1de0b32f519 100644
--- a/compiler/rustc_builtin_macros/src/test.rs
+++ b/compiler/rustc_builtin_macros/src/test.rs
@@ -7,7 +7,6 @@ use rustc_ast::attr;
 use rustc_ast_pretty::pprust;
 use rustc_expand::base::*;
 use rustc_session::Session;
-use rustc_span::source_map::respan;
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::Span;
 
@@ -35,7 +34,11 @@ pub fn expand_test_case(
     let sp = ecx.with_def_site_ctxt(attr_sp);
     let mut item = anno_item.expect_item();
     item = item.map(|mut item| {
-        item.vis = respan(item.vis.span, ast::VisibilityKind::Public);
+        item.vis = ast::Visibility {
+            span: item.vis.span,
+            kind: ast::VisibilityKind::Public,
+            tokens: None,
+        };
         item.ident.span = item.ident.span.with_ctxt(sp.ctxt());
         item.attrs.push(ecx.attribute(ecx.meta_word(sp, sym::rustc_test_marker)));
         item
@@ -292,7 +295,7 @@ pub fn expand_test_or_bench(
         ),
     );
     test_const = test_const.map(|mut tc| {
-        tc.vis.node = ast::VisibilityKind::Public;
+        tc.vis.kind = ast::VisibilityKind::Public;
         tc
     });
 
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index 0ea60665d67..0a60ca8faaa 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -10,7 +10,6 @@ use rustc_expand::expand::{AstFragment, ExpansionConfig};
 use rustc_feature::Features;
 use rustc_session::Session;
 use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency};
-use rustc_span::source_map::respan;
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::spec::PanicStrategy;
@@ -333,7 +332,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
         attrs: vec![main_attr],
         id: ast::DUMMY_NODE_ID,
         kind: main,
-        vis: respan(sp, ast::VisibilityKind::Public),
+        vis: ast::Visibility { span: sp, kind: ast::VisibilityKind::Public, tokens: None },
         span: sp,
         tokens: None,
     });
diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml
index 38f552558c8..04792b334d5 100644
--- a/compiler/rustc_codegen_llvm/Cargo.toml
+++ b/compiler/rustc_codegen_llvm/Cargo.toml
@@ -25,7 +25,7 @@ rustc_fs_util = { path = "../rustc_fs_util" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_incremental = { path = "../rustc_incremental" }
 rustc_index = { path = "../rustc_index" }
-rustc_llvm = { path = "../../src/librustc_llvm" }
+rustc_llvm = { path = "../rustc_llvm" }
 rustc_session = { path = "../rustc_session" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_target = { path = "../rustc_target" }
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index 7c710a1cb3d..4b2d5907a02 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -346,14 +346,14 @@ fn fat_lto(
     Ok(LtoModuleCodegen::Fat { module: Some(module), _serialized_bitcode: serialized_bitcode })
 }
 
-struct Linker<'a>(&'a mut llvm::Linker<'a>);
+crate struct Linker<'a>(&'a mut llvm::Linker<'a>);
 
 impl Linker<'a> {
-    fn new(llmod: &'a llvm::Module) -> Self {
+    crate fn new(llmod: &'a llvm::Module) -> Self {
         unsafe { Linker(llvm::LLVMRustLinkerNew(llmod)) }
     }
 
-    fn add(&mut self, bytecode: &[u8]) -> Result<(), ()> {
+    crate fn add(&mut self, bytecode: &[u8]) -> Result<(), ()> {
         unsafe {
             if llvm::LLVMRustLinkerAdd(
                 self.0,
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index 6f386c1287c..937821e9d4f 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -617,6 +617,31 @@ unsafe fn add_sanitizer_passes(config: &ModuleConfig, passes: &mut Vec<&'static
     }
 }
 
+pub(crate) fn link(
+    cgcx: &CodegenContext<LlvmCodegenBackend>,
+    diag_handler: &Handler,
+    mut modules: Vec<ModuleCodegen<ModuleLlvm>>,
+) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
+    use super::lto::{Linker, ModuleBuffer};
+    // Sort the modules by name to ensure to ensure deterministic behavior.
+    modules.sort_by(|a, b| a.name.cmp(&b.name));
+    let (first, elements) =
+        modules.split_first().expect("Bug! modules must contain at least one module.");
+
+    let mut linker = Linker::new(first.module_llvm.llmod());
+    for module in elements {
+        let _timer =
+            cgcx.prof.generic_activity_with_arg("LLVM_link_module", format!("{:?}", module.name));
+        let buffer = ModuleBuffer::new(module.module_llvm.llmod());
+        linker.add(&buffer.data()).map_err(|()| {
+            let msg = format!("failed to serialize module {:?}", module.name);
+            llvm_err(&diag_handler, &msg)
+        })?;
+    }
+    drop(linker);
+    Ok(modules.remove(0))
+}
+
 pub(crate) unsafe fn codegen(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
     diag_handler: &Handler,
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index 67d4b2642c0..2e2abe9fb30 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -130,6 +130,13 @@ impl WriteBackendMethods for LlvmCodegenBackend {
             llvm::LLVMRustPrintPassTimings();
         }
     }
+    fn run_link(
+        cgcx: &CodegenContext<Self>,
+        diag_handler: &Handler,
+        modules: Vec<ModuleCodegen<Self::Module>>,
+    ) -> Result<ModuleCodegen<Self::Module>, FatalError> {
+        back::write::link(cgcx, diag_handler, modules)
+    }
     fn run_fat_lto(
         cgcx: &CodegenContext<Self>,
         modules: Vec<FatLTOInput<Self>>,
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index 32822eba930..4942c997682 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -96,7 +96,7 @@ pub enum DLLStorageClass {
     DllExport = 2, // Function to be accessible from DLL.
 }
 
-/// Matches LLVMRustAttribute in rustllvm.h
+/// Matches LLVMRustAttribute in LLVMWrapper.h
 /// Semantically a subset of the C++ enum llvm::Attribute::AttrKind,
 /// though it is not ABI compatible (since it's a C++ enum)
 #[repr(C)]
@@ -1705,7 +1705,7 @@ extern "C" {
         PM: &PassManager<'_>,
     );
 
-    // Stuff that's in rustllvm/ because it's not upstream yet.
+    // Stuff that's in llvm-wrapper/ because it's not upstream yet.
 
     /// Opens an object file.
     pub fn LLVMCreateObjectFile(
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index c044020d930..faeb727202c 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -1076,7 +1076,7 @@ fn exec_linker(
             }
             .to_string(),
         );
-        args.push_str("\n");
+        args.push('\n');
     }
     let file = tmpdir.join("linker-arguments");
     let bytes = if sess.target.target.options.is_like_msvc {
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 7d69bb983dd..0edf0fcd1a2 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -702,6 +702,7 @@ impl<B: WriteBackendMethods> WorkItem<B> {
 
 enum WorkItemResult<B: WriteBackendMethods> {
     Compiled(CompiledModule),
+    NeedsLink(ModuleCodegen<B::Module>),
     NeedsFatLTO(FatLTOInput<B>),
     NeedsThinLTO(String, B::ThinBuffer),
 }
@@ -801,11 +802,8 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
         None
     };
 
-    Ok(match lto_type {
-        ComputedLtoType::No => {
-            let module = unsafe { B::codegen(cgcx, &diag_handler, module, module_config)? };
-            WorkItemResult::Compiled(module)
-        }
+    match lto_type {
+        ComputedLtoType::No => finish_intra_module_work(cgcx, module, module_config),
         ComputedLtoType::Thin => {
             let (name, thin_buffer) = B::prepare_thin(module);
             if let Some(path) = bitcode {
@@ -813,7 +811,7 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
                     panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e);
                 });
             }
-            WorkItemResult::NeedsThinLTO(name, thin_buffer)
+            Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer))
         }
         ComputedLtoType::Fat => match bitcode {
             Some(path) => {
@@ -821,11 +819,11 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
                 fs::write(&path, buffer.data()).unwrap_or_else(|e| {
                     panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e);
                 });
-                WorkItemResult::NeedsFatLTO(FatLTOInput::Serialized { name, buffer })
+                Ok(WorkItemResult::NeedsFatLTO(FatLTOInput::Serialized { name, buffer }))
             }
-            None => WorkItemResult::NeedsFatLTO(FatLTOInput::InMemory(module)),
+            None => Ok(WorkItemResult::NeedsFatLTO(FatLTOInput::InMemory(module))),
         },
-    })
+    }
 }
 
 fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
@@ -871,12 +869,25 @@ fn execute_lto_work_item<B: ExtraBackendMethods>(
     mut module: lto::LtoModuleCodegen<B>,
     module_config: &ModuleConfig,
 ) -> Result<WorkItemResult<B>, FatalError> {
+    let module = unsafe { module.optimize(cgcx)? };
+    finish_intra_module_work(cgcx, module, module_config)
+}
+
+fn finish_intra_module_work<B: ExtraBackendMethods>(
+    cgcx: &CodegenContext<B>,
+    module: ModuleCodegen<B::Module>,
+    module_config: &ModuleConfig,
+) -> Result<WorkItemResult<B>, FatalError> {
     let diag_handler = cgcx.create_diag_handler();
 
-    unsafe {
-        let module = module.optimize(cgcx)?;
-        let module = B::codegen(cgcx, &diag_handler, module, module_config)?;
+    if !cgcx.opts.debugging_opts.combine_cgu
+        || module.kind == ModuleKind::Metadata
+        || module.kind == ModuleKind::Allocator
+    {
+        let module = unsafe { B::codegen(cgcx, &diag_handler, module, module_config)? };
         Ok(WorkItemResult::Compiled(module))
+    } else {
+        Ok(WorkItemResult::NeedsLink(module))
     }
 }
 
@@ -891,6 +902,10 @@ pub enum Message<B: WriteBackendMethods> {
         thin_buffer: B::ThinBuffer,
         worker_id: usize,
     },
+    NeedsLink {
+        module: ModuleCodegen<B::Module>,
+        worker_id: usize,
+    },
     Done {
         result: Result<CompiledModule, Option<WorkerFatalError>>,
         worker_id: usize,
@@ -1178,6 +1193,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
         let mut compiled_modules = vec![];
         let mut compiled_metadata_module = None;
         let mut compiled_allocator_module = None;
+        let mut needs_link = Vec::new();
         let mut needs_fat_lto = Vec::new();
         let mut needs_thin_lto = Vec::new();
         let mut lto_import_only_modules = Vec::new();
@@ -1434,6 +1450,10 @@ fn start_executing_work<B: ExtraBackendMethods>(
                         }
                     }
                 }
+                Message::NeedsLink { module, worker_id } => {
+                    free_worker(worker_id);
+                    needs_link.push(module);
+                }
                 Message::NeedsFatLTO { result, worker_id } => {
                     assert!(!started_lto);
                     free_worker(worker_id);
@@ -1462,6 +1482,18 @@ fn start_executing_work<B: ExtraBackendMethods>(
             }
         }
 
+        let needs_link = mem::take(&mut needs_link);
+        if !needs_link.is_empty() {
+            assert!(compiled_modules.is_empty());
+            let diag_handler = cgcx.create_diag_handler();
+            let module = B::run_link(&cgcx, &diag_handler, needs_link).map_err(|_| ())?;
+            let module = unsafe {
+                B::codegen(&cgcx, &diag_handler, module, cgcx.config(ModuleKind::Regular))
+                    .map_err(|_| ())?
+            };
+            compiled_modules.push(module);
+        }
+
         // Drop to print timings
         drop(llvm_start_time);
 
@@ -1521,6 +1553,9 @@ fn spawn_work<B: ExtraBackendMethods>(cgcx: CodegenContext<B>, work: WorkItem<B>
                     Some(Ok(WorkItemResult::Compiled(m))) => {
                         Message::Done::<B> { result: Ok(m), worker_id }
                     }
+                    Some(Ok(WorkItemResult::NeedsLink(m))) => {
+                        Message::NeedsLink::<B> { module: m, worker_id }
+                    }
                     Some(Ok(WorkItemResult::NeedsFatLTO(m))) => {
                         Message::NeedsFatLTO::<B> { result: m, worker_id }
                     }
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
index 54e5d4d00f6..0c0f1bc681c 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
@@ -37,7 +37,7 @@ pub fn push_debuginfo_type_name<'tcx>(
         ty::Bool => output.push_str("bool"),
         ty::Char => output.push_str("char"),
         ty::Str => output.push_str("str"),
-        ty::Never => output.push_str("!"),
+        ty::Never => output.push('!'),
         ty::Int(int_ty) => output.push_str(int_ty.name_str()),
         ty::Uint(uint_ty) => output.push_str(uint_ty.name_str()),
         ty::Float(float_ty) => output.push_str(float_ty.name_str()),
diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs
index 27d52e9b9c5..264e7c2aa92 100644
--- a/compiler/rustc_codegen_ssa/src/traits/write.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/write.rs
@@ -13,6 +13,12 @@ pub trait WriteBackendMethods: 'static + Sized + Clone {
     type ThinData: Send + Sync;
     type ThinBuffer: ThinBufferMethods;
 
+    /// Merge all modules into main_module and returning it
+    fn run_link(
+        cgcx: &CodegenContext<Self>,
+        diag_handler: &Handler,
+        modules: Vec<ModuleCodegen<Self::Module>>,
+    ) -> Result<ModuleCodegen<Self::Module>, FatalError>;
     /// Performs fat LTO by merging all modules into a single one and returning it
     /// for further optimization.
     fn run_fat_lto(
diff --git a/compiler/rustc_data_structures/src/graph/iterate/mod.rs b/compiler/rustc_data_structures/src/graph/iterate/mod.rs
index 64ff6130ddf..bc3d1ce53ba 100644
--- a/compiler/rustc_data_structures/src/graph/iterate/mod.rs
+++ b/compiler/rustc_data_structures/src/graph/iterate/mod.rs
@@ -87,11 +87,8 @@ where
 }
 
 /// Allows searches to terminate early with a value.
-#[derive(Clone, Copy, Debug)]
-pub enum ControlFlow<T> {
-    Break(T),
-    Continue,
-}
+// FIXME (#75744): remove the alias once the generics are in a better order and `C=()`.
+pub type ControlFlow<T> = std::ops::ControlFlow<(), T>;
 
 /// The status of a node in the depth-first search.
 ///
@@ -260,12 +257,12 @@ where
         _node: G::Node,
         _prior_status: Option<NodeStatus>,
     ) -> ControlFlow<Self::BreakVal> {
-        ControlFlow::Continue
+        ControlFlow::CONTINUE
     }
 
     /// Called after all nodes reachable from this one have been examined.
     fn node_settled(&mut self, _node: G::Node) -> ControlFlow<Self::BreakVal> {
-        ControlFlow::Continue
+        ControlFlow::CONTINUE
     }
 
     /// Behave as if no edges exist from `source` to `target`.
@@ -289,8 +286,8 @@ where
         prior_status: Option<NodeStatus>,
     ) -> ControlFlow<Self::BreakVal> {
         match prior_status {
-            Some(NodeStatus::Visited) => ControlFlow::Break(()),
-            _ => ControlFlow::Continue,
+            Some(NodeStatus::Visited) => ControlFlow::BREAK,
+            _ => ControlFlow::CONTINUE,
         }
     }
 }
diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs
index de4e7a13424..88c160e93b6 100644
--- a/compiler/rustc_data_structures/src/lib.rs
+++ b/compiler/rustc_data_structures/src/lib.rs
@@ -8,6 +8,7 @@
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
 #![allow(incomplete_features)]
+#![feature(control_flow_enum)]
 #![feature(in_band_lifetimes)]
 #![feature(unboxed_closures)]
 #![feature(generators)]
diff --git a/compiler/rustc_data_structures/src/temp_dir.rs b/compiler/rustc_data_structures/src/temp_dir.rs
index 0d9b3e3ca25..a780d2386a6 100644
--- a/compiler/rustc_data_structures/src/temp_dir.rs
+++ b/compiler/rustc_data_structures/src/temp_dir.rs
@@ -12,7 +12,7 @@ pub struct MaybeTempDir {
 
 impl Drop for MaybeTempDir {
     fn drop(&mut self) {
-        // Safety: We are in the destructor, and no further access will
+        // SAFETY: We are in the destructor, and no further access will
         // occur.
         let dir = unsafe { ManuallyDrop::take(&mut self.dir) };
         if self.keep {
diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs
index 789a1fc35a6..b0be1bf7e72 100644
--- a/compiler/rustc_error_codes/src/error_codes.rs
+++ b/compiler/rustc_error_codes/src/error_codes.rs
@@ -455,6 +455,7 @@ E0769: include_str!("./error_codes/E0769.md"),
 E0770: include_str!("./error_codes/E0770.md"),
 E0771: include_str!("./error_codes/E0771.md"),
 E0773: include_str!("./error_codes/E0773.md"),
+E0774: include_str!("./error_codes/E0774.md"),
 ;
 //  E0006, // merged with E0005
 //  E0008, // cannot bind by-move into a pattern guard
diff --git a/compiler/rustc_error_codes/src/error_codes/E0224.md b/compiler/rustc_error_codes/src/error_codes/E0224.md
index fd89c1d5256..628488575b2 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0224.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0224.md
@@ -1,4 +1,4 @@
-A trait object was declaired with no traits.
+A trait object was declared with no traits.
 
 Erroneous code example:
 
@@ -8,7 +8,7 @@ type Foo = dyn 'static +;
 
 Rust does not currently support this.
 
-To solve ensure the the trait object has at least one trait:
+To solve, ensure that the trait object has at least one trait:
 
 ```
 type Foo = dyn 'static + Copy;
diff --git a/compiler/rustc_error_codes/src/error_codes/E0433.md b/compiler/rustc_error_codes/src/error_codes/E0433.md
index f9e333e8ccd..5a64c13c9af 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0433.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0433.md
@@ -1,17 +1,27 @@
-An undeclared type or module was used.
+An undeclared crate, module, or type was used.
 
 Erroneous code example:
 
 ```compile_fail,E0433
 let map = HashMap::new();
-// error: failed to resolve: use of undeclared type or module `HashMap`
+// error: failed to resolve: use of undeclared type `HashMap`
 ```
 
 Please verify you didn't misspell the type/module's name or that you didn't
 forget to import it:
 
-
 ```
 use std::collections::HashMap; // HashMap has been imported.
 let map: HashMap<u32, u32> = HashMap::new(); // So it can be used!
 ```
+
+If you've expected to use a crate name:
+
+```compile_fail
+use ferris_wheel::BigO;
+// error: failed to resolve: use of undeclared crate or module `ferris_wheel`
+```
+
+Make sure the crate has been added as a dependency in `Cargo.toml`.
+
+To use a module from your current crate, add the `crate::` prefix to the path.
diff --git a/compiler/rustc_error_codes/src/error_codes/E0607.md b/compiler/rustc_error_codes/src/error_codes/E0607.md
index ea6e10105b0..0545246929f 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0607.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0607.md
@@ -12,15 +12,15 @@ First: what are thin and fat pointers?
 Thin pointers are "simple" pointers: they are purely a reference to a memory
 address.
 
-Fat pointers are pointers referencing Dynamically Sized Types (also called DST).
-DST don't have a statically known size, therefore they can only exist behind
-some kind of pointers that contain additional information. Slices and trait
-objects are DSTs. In the case of slices, the additional information the fat
-pointer holds is their size.
+Fat pointers are pointers referencing Dynamically Sized Types (also called
+DSTs). DSTs don't have a statically known size, therefore they can only exist
+behind some kind of pointer that contains additional information. For example,
+slices and trait objects are DSTs. In the case of slices, the additional
+information the fat pointer holds is their size.
 
 To fix this error, don't try to cast directly between thin and fat pointers.
 
-For more information about casts, take a look at the Type cast section in
-[The Reference Book][1].
+For more information about type casts, take a look at the section of the
+[The Rust Reference][1] on type cast expressions.
 
 [1]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions
diff --git a/compiler/rustc_error_codes/src/error_codes/E0774.md b/compiler/rustc_error_codes/src/error_codes/E0774.md
new file mode 100644
index 00000000000..79793ba9d7d
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0774.md
@@ -0,0 +1,24 @@
+`derive` was applied on something which is not a struct, a union or an enum.
+
+Erroneous code example:
+
+```compile_fail,E0774
+trait Foo {
+    #[derive(Clone)] // error!
+    type Bar;
+}
+```
+
+As said above, the `derive` attribute is only allowed on structs, unions or
+enums:
+
+```
+#[derive(Clone)] // ok!
+struct Bar {
+    field: u32,
+}
+```
+
+You can find more information about `derive` in the [Rust Book].
+
+[Rust Book]: https://doc.rust-lang.org/book/appendix-03-derivable-traits.html
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 4c01cb8159a..926e3dbfc52 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -400,6 +400,7 @@ macro_rules! make_stmts_default {
                 id: ast::DUMMY_NODE_ID,
                 span: e.span,
                 kind: ast::StmtKind::Expr(e),
+                tokens: None
             }]
         })
     };
@@ -607,6 +608,7 @@ impl DummyResult {
             id: ast::DUMMY_NODE_ID,
             kind: if is_error { ast::TyKind::Err } else { ast::TyKind::Tup(Vec::new()) },
             span: sp,
+            tokens: None,
         })
     }
 }
@@ -641,6 +643,7 @@ impl MacResult for DummyResult {
             id: ast::DUMMY_NODE_ID,
             kind: ast::StmtKind::Expr(DummyResult::raw_expr(self.span, self.is_error)),
             span: self.span,
+            tokens: None
         }])
     }
 
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index 9490b62aa17..a5a7ee6c9a3 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -3,7 +3,7 @@ use crate::base::ExtCtxt;
 use rustc_ast::attr;
 use rustc_ast::ptr::P;
 use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, PatKind, UnOp};
-use rustc_span::source_map::{respan, Spanned};
+use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 
 use rustc_span::Span;
@@ -46,7 +46,7 @@ impl<'a> ExtCtxt<'a> {
             id: ast::DUMMY_NODE_ID,
             args,
         });
-        ast::Path { span, segments }
+        ast::Path { span, segments, tokens: None }
     }
 
     pub fn ty_mt(&self, ty: P<ast::Ty>, mutbl: ast::Mutability) -> ast::MutTy {
@@ -54,7 +54,7 @@ impl<'a> ExtCtxt<'a> {
     }
 
     pub fn ty(&self, span: Span, kind: ast::TyKind) -> P<ast::Ty> {
-        P(ast::Ty { id: ast::DUMMY_NODE_ID, span, kind })
+        P(ast::Ty { id: ast::DUMMY_NODE_ID, span, kind, tokens: None })
     }
 
     pub fn ty_path(&self, path: ast::Path) -> P<ast::Ty> {
@@ -158,7 +158,12 @@ impl<'a> ExtCtxt<'a> {
     }
 
     pub fn stmt_expr(&self, expr: P<ast::Expr>) -> ast::Stmt {
-        ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr) }
+        ast::Stmt {
+            id: ast::DUMMY_NODE_ID,
+            span: expr.span,
+            kind: ast::StmtKind::Expr(expr),
+            tokens: None,
+        }
     }
 
     pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P<ast::Expr>) -> ast::Stmt {
@@ -176,7 +181,12 @@ impl<'a> ExtCtxt<'a> {
             span: sp,
             attrs: AttrVec::new(),
         });
-        ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp }
+        ast::Stmt {
+            id: ast::DUMMY_NODE_ID,
+            kind: ast::StmtKind::Local(local),
+            span: sp,
+            tokens: None,
+        }
     }
 
     // Generates `let _: Type;`, which is usually used for type assertions.
@@ -189,11 +199,16 @@ impl<'a> ExtCtxt<'a> {
             span,
             attrs: AttrVec::new(),
         });
-        ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span }
+        ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span, tokens: None }
     }
 
     pub fn stmt_item(&self, sp: Span, item: P<ast::Item>) -> ast::Stmt {
-        ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Item(item), span: sp }
+        ast::Stmt {
+            id: ast::DUMMY_NODE_ID,
+            kind: ast::StmtKind::Item(item),
+            span: sp,
+            tokens: None,
+        }
     }
 
     pub fn block_expr(&self, expr: P<ast::Expr>) -> P<ast::Block> {
@@ -203,11 +218,18 @@ impl<'a> ExtCtxt<'a> {
                 id: ast::DUMMY_NODE_ID,
                 span: expr.span,
                 kind: ast::StmtKind::Expr(expr),
+                tokens: None,
             }],
         )
     }
     pub fn block(&self, span: Span, stmts: Vec<ast::Stmt>) -> P<ast::Block> {
-        P(ast::Block { stmts, id: ast::DUMMY_NODE_ID, rules: BlockCheckMode::Default, span })
+        P(ast::Block {
+            stmts,
+            id: ast::DUMMY_NODE_ID,
+            rules: BlockCheckMode::Default,
+            span,
+            tokens: None,
+        })
     }
 
     pub fn expr(&self, span: Span, kind: ast::ExprKind) -> P<ast::Expr> {
@@ -578,7 +600,11 @@ impl<'a> ExtCtxt<'a> {
             attrs,
             id: ast::DUMMY_NODE_ID,
             kind,
-            vis: respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited),
+            vis: ast::Visibility {
+                span: span.shrink_to_lo(),
+                kind: ast::VisibilityKind::Inherited,
+                tokens: None,
+            },
             span,
             tokens: None,
         })
@@ -592,7 +618,11 @@ impl<'a> ExtCtxt<'a> {
                 span: ty.span,
                 ty,
                 ident: None,
-                vis: respan(vis_span, ast::VisibilityKind::Inherited),
+                vis: ast::Visibility {
+                    span: vis_span,
+                    kind: ast::VisibilityKind::Inherited,
+                    tokens: None,
+                },
                 attrs: Vec::new(),
                 id: ast::DUMMY_NODE_ID,
                 is_placeholder: false,
@@ -611,7 +641,11 @@ impl<'a> ExtCtxt<'a> {
             disr_expr: None,
             id: ast::DUMMY_NODE_ID,
             ident,
-            vis: respan(vis_span, ast::VisibilityKind::Inherited),
+            vis: ast::Visibility {
+                span: vis_span,
+                kind: ast::VisibilityKind::Inherited,
+                tokens: None,
+            },
             span,
             is_placeholder: false,
         }
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index 97608a38903..dd087ab9150 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -399,7 +399,7 @@ impl<'a> StripUnconfigured<'a> {
     }
 
     pub fn configure_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) {
-        let ast::ForeignMod { abi: _, items } = foreign_mod;
+        let ast::ForeignMod { unsafety: _, abi: _, items } = foreign_mod;
         items.flat_map_in_place(|item| self.configure(item));
     }
 
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 105f81c6e0f..e5cfb866938 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -13,7 +13,7 @@ use rustc_ast::token;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::visit::{self, AssocCtxt, Visitor};
 use rustc_ast::{self as ast, AttrItem, Block, LitKind, NodeId, PatKind, Path};
-use rustc_ast::{ItemKind, MacArgs, MacCallStmt, MacStmtStyle, StmtKind};
+use rustc_ast::{ItemKind, MacArgs, MacCallStmt, MacStmtStyle, StmtKind, Unsafe};
 use rustc_ast_pretty::pprust;
 use rustc_attr::{self as attr, is_builtin_attr, HasAttrs};
 use rustc_data_structures::map_in_place::MapInPlace;
@@ -26,7 +26,6 @@ use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS;
 use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_session::parse::{feature_err, ParseSess};
 use rustc_session::Limit;
-use rustc_span::source_map::respan;
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::{ExpnId, FileName, Span, DUMMY_SP};
 
@@ -358,7 +357,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             kind: ast::ItemKind::Mod(krate.module),
             ident: Ident::invalid(),
             id: ast::DUMMY_NODE_ID,
-            vis: respan(krate.span.shrink_to_lo(), ast::VisibilityKind::Public),
+            vis: ast::Visibility {
+                span: krate.span.shrink_to_lo(),
+                kind: ast::VisibilityKind::Public,
+                tokens: None,
+            },
             tokens: None,
         })]);
 
@@ -370,11 +373,21 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             None => {
                 // Resolution failed so we return an empty expansion
                 krate.attrs = vec![];
-                krate.module = ast::Mod { inner: orig_mod_span, items: vec![], inline: true };
+                krate.module = ast::Mod {
+                    inner: orig_mod_span,
+                    unsafety: Unsafe::No,
+                    items: vec![],
+                    inline: true,
+                };
             }
             Some(ast::Item { span, kind, .. }) => {
                 krate.attrs = vec![];
-                krate.module = ast::Mod { inner: orig_mod_span, items: vec![], inline: true };
+                krate.module = ast::Mod {
+                    inner: orig_mod_span,
+                    unsafety: Unsafe::No,
+                    items: vec![],
+                    inline: true,
+                };
                 self.cx.span_err(
                     span,
                     &format!(
@@ -529,9 +542,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
     fn error_derive_forbidden_on_non_adt(&self, derives: &[Path], item: &Annotatable) {
         let attr = self.cx.sess.find_by_name(item.attrs(), sym::derive);
         let span = attr.map_or(item.span(), |attr| attr.span);
-        let mut err = self
-            .cx
-            .struct_span_err(span, "`derive` may only be applied to structs, enums and unions");
+        let mut err = rustc_errors::struct_span_err!(
+            self.cx.sess,
+            span,
+            E0774,
+            "`derive` may only be applied to structs, enums and unions",
+        );
         if let Some(ast::Attribute { style: ast::AttrStyle::Inner, .. }) = attr {
             let trait_list = derives.iter().map(|t| pprust::path_to_string(t)).collect::<Vec<_>>();
             let suggestion = format!("#[derive({})]", trait_list.join(", "));
@@ -1380,10 +1396,10 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
         }
 
         // The placeholder expander gives ids to statements, so we avoid folding the id here.
-        let ast::Stmt { id, kind, span } = stmt;
+        let ast::Stmt { id, kind, span, tokens } = stmt;
         noop_flat_map_stmt_kind(kind, self)
             .into_iter()
-            .map(|kind| ast::Stmt { id, kind, span })
+            .map(|kind| ast::Stmt { id, kind, span, tokens: tokens.clone() })
             .collect()
     }
 
@@ -1438,8 +1454,15 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
                     push_directory(&self.cx.sess, ident, &item.attrs, dir)
                 } else {
                     // We have an outline `mod foo;` so we need to parse the file.
-                    let (new_mod, dir) =
-                        parse_external_mod(&self.cx.sess, ident, span, dir, &mut attrs, pushed);
+                    let (new_mod, dir) = parse_external_mod(
+                        &self.cx.sess,
+                        ident,
+                        span,
+                        old_mod.unsafety,
+                        dir,
+                        &mut attrs,
+                        pushed,
+                    );
 
                     let krate = ast::Crate {
                         span: new_mod.inner,
@@ -1757,6 +1780,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
                 kind: ast::AttrKind::Normal(AttrItem {
                     path: meta.path,
                     args: meta.kind.mac_args(meta.span),
+                    tokens: None,
                 }),
                 span: at.span,
                 id: at.id,
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index b908a12c1fc..0e5c5fe4d44 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -4,7 +4,7 @@ use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch};
 
 use rustc_ast::mut_visit::{self, MutVisitor};
 use rustc_ast::token::{self, NtTT, Token};
-use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};
+use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing};
 use rustc_ast::MacCall;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
@@ -111,7 +111,7 @@ pub(super) fn transcribe<'a>(
     //
     // Thus, if we try to pop the `result_stack` and it is empty, we have reached the top-level
     // again, and we are done transcribing.
-    let mut result: Vec<TreeAndJoint> = Vec::new();
+    let mut result: Vec<TreeAndSpacing> = Vec::new();
     let mut result_stack = Vec::new();
     let mut marker = Marker(cx.current_expansion.id, transparency);
 
diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs
index 1e123a2e145..171cb3fa8e6 100644
--- a/compiler/rustc_expand/src/module.rs
+++ b/compiler/rustc_expand/src/module.rs
@@ -1,4 +1,4 @@
-use rustc_ast::{token, Attribute, Mod};
+use rustc_ast::{token, Attribute, Mod, Unsafe};
 use rustc_errors::{struct_span_err, PResult};
 use rustc_parse::new_parser_from_file;
 use rustc_session::parse::ParseSess;
@@ -42,6 +42,7 @@ crate fn parse_external_mod(
     sess: &Session,
     id: Ident,
     span: Span, // The span to blame on errors.
+    unsafety: Unsafe,
     Directory { mut ownership, path }: Directory,
     attrs: &mut Vec<Attribute>,
     pop_mod_stack: &mut bool,
@@ -60,13 +61,16 @@ crate fn parse_external_mod(
         drop(included_mod_stack);
 
         // Actually parse the external file as a module.
-        let mut module =
-            new_parser_from_file(&sess.parse_sess, &mp.path, Some(span)).parse_mod(&token::Eof)?;
+        let mut parser = new_parser_from_file(&sess.parse_sess, &mp.path, Some(span));
+        let mut module = parser.parse_mod(&token::Eof, unsafety)?;
         module.0.inline = false;
         module
     };
     // (1) ...instead, we return a dummy module.
-    let (module, mut new_attrs) = result.map_err(|mut err| err.emit()).unwrap_or_default();
+    let (module, mut new_attrs) = result.map_err(|mut err| err.emit()).unwrap_or_else(|_| {
+        let module = Mod { inner: Span::default(), unsafety, items: Vec::new(), inline: false };
+        (module, Vec::new())
+    });
     attrs.append(&mut new_attrs);
 
     // Extract the directory path for submodules of `module`.
@@ -219,8 +223,7 @@ fn error_cannot_declare_mod_here<'a, T>(
 
 /// Derive a submodule path from the first found `#[path = "path_string"]`.
 /// The provided `dir_path` is joined with the `path_string`.
-// Public for rustfmt usage.
-pub fn submod_path_from_attr(
+pub(super) fn submod_path_from_attr(
     sess: &Session,
     attrs: &[Attribute],
     dir_path: &Path,
diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs
index dbd2e70af6a..4c9271a58df 100644
--- a/compiler/rustc_expand/src/placeholders.rs
+++ b/compiler/rustc_expand/src/placeholders.rs
@@ -4,7 +4,7 @@ use crate::expand::{AstFragment, AstFragmentKind};
 use rustc_ast as ast;
 use rustc_ast::mut_visit::*;
 use rustc_ast::ptr::P;
-use rustc_span::source_map::{dummy_spanned, DUMMY_SP};
+use rustc_span::source_map::DUMMY_SP;
 use rustc_span::symbol::Ident;
 
 use smallvec::{smallvec, SmallVec};
@@ -18,7 +18,7 @@ pub fn placeholder(
 ) -> AstFragment {
     fn mac_placeholder() -> ast::MacCall {
         ast::MacCall {
-            path: ast::Path { span: DUMMY_SP, segments: Vec::new() },
+            path: ast::Path { span: DUMMY_SP, segments: Vec::new(), tokens: None },
             args: P(ast::MacArgs::Empty),
             prior_type_ascription: None,
         }
@@ -26,7 +26,11 @@ pub fn placeholder(
 
     let ident = Ident::invalid();
     let attrs = Vec::new();
-    let vis = vis.unwrap_or_else(|| dummy_spanned(ast::VisibilityKind::Inherited));
+    let vis = vis.unwrap_or(ast::Visibility {
+        span: DUMMY_SP,
+        kind: ast::VisibilityKind::Inherited,
+        tokens: None,
+    });
     let span = DUMMY_SP;
     let expr_placeholder = || {
         P(ast::Expr {
@@ -37,7 +41,8 @@ pub fn placeholder(
             tokens: None,
         })
     };
-    let ty = || P(ast::Ty { id, kind: ast::TyKind::MacCall(mac_placeholder()), span });
+    let ty =
+        || P(ast::Ty { id, kind: ast::TyKind::MacCall(mac_placeholder()), span, tokens: None });
     let pat =
         || P(ast::Pat { id, kind: ast::PatKind::MacCall(mac_placeholder()), span, tokens: None });
 
@@ -88,16 +93,19 @@ pub fn placeholder(
             kind: ast::PatKind::MacCall(mac_placeholder()),
             tokens: None,
         })),
-        AstFragmentKind::Ty => {
-            AstFragment::Ty(P(ast::Ty { id, span, kind: ast::TyKind::MacCall(mac_placeholder()) }))
-        }
+        AstFragmentKind::Ty => AstFragment::Ty(P(ast::Ty {
+            id,
+            span,
+            kind: ast::TyKind::MacCall(mac_placeholder()),
+            tokens: None,
+        })),
         AstFragmentKind::Stmts => AstFragment::Stmts(smallvec![{
             let mac = P(ast::MacCallStmt {
                 mac: mac_placeholder(),
                 style: ast::MacStmtStyle::Braces,
                 attrs: ast::AttrVec::new(),
             });
-            ast::Stmt { id, span, kind: ast::StmtKind::MacCall(mac) }
+            ast::Stmt { id, span, kind: ast::StmtKind::MacCall(mac), tokens: None }
         }]),
         AstFragmentKind::Arms => AstFragment::Arms(smallvec![ast::Arm {
             attrs: Default::default(),
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 39c82f97e0a..ec41fd7a3ee 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -2,7 +2,7 @@ use crate::base::ExtCtxt;
 
 use rustc_ast as ast;
 use rustc_ast::token;
-use rustc_ast::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream, TreeAndJoint};
+use rustc_ast::tokenstream::{self, DelimSpan, Spacing::*, TokenStream, TreeAndSpacing};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::Diagnostic;
@@ -47,15 +47,15 @@ impl ToInternal<token::DelimToken> for Delimiter {
     }
 }
 
-impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec<Self>)>
+impl FromInternal<(TreeAndSpacing, &'_ ParseSess, &'_ mut Vec<Self>)>
     for TokenTree<Group, Punct, Ident, Literal>
 {
     fn from_internal(
-        ((tree, is_joint), sess, stack): (TreeAndJoint, &ParseSess, &mut Vec<Self>),
+        ((tree, spacing), sess, stack): (TreeAndSpacing, &ParseSess, &mut Vec<Self>),
     ) -> Self {
         use rustc_ast::token::*;
 
-        let joint = is_joint == Joint;
+        let joint = spacing == Joint;
         let Token { kind, span } = match tree {
             tokenstream::TokenTree::Delimited(span, delim, tts) => {
                 let delimiter = Delimiter::from_internal(delim);
@@ -261,7 +261,7 @@ impl ToInternal<TokenStream> for TokenTree<Group, Punct, Ident, Literal> {
         };
 
         let tree = tokenstream::TokenTree::token(kind, span);
-        TokenStream::new(vec![(tree, if joint { Joint } else { NonJoint })])
+        TokenStream::new(vec![(tree, if joint { Joint } else { Alone })])
     }
 }
 
@@ -444,7 +444,7 @@ impl server::TokenStreamIter for Rustc<'_> {
     ) -> Option<TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>> {
         loop {
             let tree = iter.stack.pop().or_else(|| {
-                let next = iter.cursor.next_with_joint()?;
+                let next = iter.cursor.next_with_spacing()?;
                 Some(TokenTree::from_internal((next, self.sess, &mut iter.stack)))
             })?;
             // A hack used to pass AST fragments to attribute and derive macros
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index e858980738d..1aeb0bd5ad9 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -585,6 +585,9 @@ declare_features! (
     /// Allows `if let` guard in match arms.
     (active, if_let_guard, "1.47.0", Some(51114), None),
 
+    /// Allows non trivial generic constants which have to be manually propageted upwards.
+    (active, const_evaluatable_checked, "1.48.0", Some(76560), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
@@ -600,8 +603,14 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
     sym::const_generics,
     sym::let_chains,
     sym::raw_dylib,
+    sym::const_evaluatable_checked,
     sym::const_trait_impl,
     sym::const_trait_bound_opt_out,
     sym::lazy_normalization_consts,
     sym::specialization,
 ];
+
+/// Some features are not allowed to be used together at the same time, if
+/// the two are present, produce an error.
+pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] =
+    &[(sym::const_generics, sym::min_const_generics)];
diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs
index 4393368cd45..15564a59658 100644
--- a/compiler/rustc_feature/src/lib.rs
+++ b/compiler/rustc_feature/src/lib.rs
@@ -131,7 +131,7 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option<NonZeroU3
 }
 
 pub use accepted::ACCEPTED_FEATURES;
-pub use active::{Features, ACTIVE_FEATURES, INCOMPLETE_FEATURES};
+pub use active::{Features, ACTIVE_FEATURES, INCOMPATIBLE_FEATURES, INCOMPLETE_FEATURES};
 pub use builtin_attrs::{
     deprecated_attributes, find_gated_cfg, is_builtin_attr_name, AttributeGate, AttributeTemplate,
     AttributeType, BuiltinAttribute, GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP,
diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs
index 4339092b63e..29ec3572016 100644
--- a/compiler/rustc_graphviz/src/lib.rs
+++ b/compiler/rustc_graphviz/src/lib.rs
@@ -599,6 +599,7 @@ pub enum RenderOption {
     NoNodeStyles,
 
     Monospace,
+    DarkTheme,
 }
 
 /// Returns vec holding all the default render options.
@@ -630,10 +631,23 @@ where
     writeln!(w, "digraph {} {{", g.graph_id().as_slice())?;
 
     // Global graph properties
+    let mut graph_attrs = Vec::new();
+    let mut content_attrs = Vec::new();
     if options.contains(&RenderOption::Monospace) {
-        writeln!(w, r#"    graph[fontname="monospace"];"#)?;
-        writeln!(w, r#"    node[fontname="monospace"];"#)?;
-        writeln!(w, r#"    edge[fontname="monospace"];"#)?;
+        let font = r#"fontname="Courier, monospace""#;
+        graph_attrs.push(font);
+        content_attrs.push(font);
+    };
+    if options.contains(&RenderOption::DarkTheme) {
+        graph_attrs.push(r#"bgcolor="black""#);
+        content_attrs.push(r#"color="white""#);
+        content_attrs.push(r#"fontcolor="white""#);
+    }
+    if !(graph_attrs.is_empty() && content_attrs.is_empty()) {
+        writeln!(w, r#"    graph[{}];"#, graph_attrs.join(" "))?;
+        let content_attrs_str = content_attrs.join(" ");
+        writeln!(w, r#"    node[{}];"#, content_attrs_str)?;
+        writeln!(w, r#"    edge[{}];"#, content_attrs_str)?;
     }
 
     for n in g.nodes().iter() {
diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs
index 0d61dc037c6..b019e518d0c 100644
--- a/compiler/rustc_hir/src/def.rs
+++ b/compiler/rustc_hir/src/def.rs
@@ -6,6 +6,7 @@ use rustc_ast::NodeId;
 use rustc_macros::HashStable_Generic;
 use rustc_span::hygiene::MacroKind;
 
+use std::array::IntoIter;
 use std::fmt::Debug;
 
 /// Encodes if a `DefKind::Ctor` is the constructor of an enum variant or a struct.
@@ -291,6 +292,14 @@ impl<T> PerNS<T> {
     pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> PerNS<U> {
         PerNS { value_ns: f(self.value_ns), type_ns: f(self.type_ns), macro_ns: f(self.macro_ns) }
     }
+
+    pub fn into_iter(self) -> IntoIter<T, 3> {
+        IntoIter::new([self.value_ns, self.type_ns, self.macro_ns])
+    }
+
+    pub fn iter(&self) -> IntoIter<&T, 3> {
+        IntoIter::new([&self.value_ns, &self.type_ns, &self.macro_ns])
+    }
 }
 
 impl<T> ::std::ops::Index<Namespace> for PerNS<T> {
diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs
index c69a9b063ae..9d931b3a9e1 100644
--- a/compiler/rustc_hir/src/lib.rs
+++ b/compiler/rustc_hir/src/lib.rs
@@ -2,6 +2,7 @@
 //!
 //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html
 
+#![feature(array_value_iter)]
 #![feature(crate_visibility_modifier)]
 #![feature(const_fn)] // For the unsizing cast on `&[]`
 #![feature(const_panic)]
diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs
index c43d1a6830d..8e00e54650d 100644
--- a/compiler/rustc_index/src/bit_set.rs
+++ b/compiler/rustc_index/src/bit_set.rs
@@ -28,13 +28,20 @@ pub const WORD_BITS: usize = WORD_BYTES * 8;
 /// will panic if the bitsets have differing domain sizes.
 ///
 /// [`GrowableBitSet`]: struct.GrowableBitSet.html
-#[derive(Clone, Eq, PartialEq, Decodable, Encodable)]
-pub struct BitSet<T: Idx> {
+#[derive(Eq, PartialEq, Decodable, Encodable)]
+pub struct BitSet<T> {
     domain_size: usize,
     words: Vec<Word>,
     marker: PhantomData<T>,
 }
 
+impl<T> BitSet<T> {
+    /// Gets the domain size.
+    pub fn domain_size(&self) -> usize {
+        self.domain_size
+    }
+}
+
 impl<T: Idx> BitSet<T> {
     /// Creates a new, empty bitset with a given `domain_size`.
     #[inline]
@@ -52,11 +59,6 @@ impl<T: Idx> BitSet<T> {
         result
     }
 
-    /// Gets the domain size.
-    pub fn domain_size(&self) -> usize {
-        self.domain_size
-    }
-
     /// Clear all elements.
     #[inline]
     pub fn clear(&mut self) {
@@ -75,12 +77,6 @@ impl<T: Idx> BitSet<T> {
         }
     }
 
-    /// Efficiently overwrite `self` with `other`.
-    pub fn overwrite(&mut self, other: &BitSet<T>) {
-        assert!(self.domain_size == other.domain_size);
-        self.words.clone_from_slice(&other.words);
-    }
-
     /// Count the number of set bits in the set.
     pub fn count(&self) -> usize {
         self.words.iter().map(|e| e.count_ones() as usize).sum()
@@ -243,6 +239,21 @@ impl<T: Idx> SubtractFromBitSet<T> for BitSet<T> {
     }
 }
 
+impl<T> Clone for BitSet<T> {
+    fn clone(&self) -> Self {
+        BitSet { domain_size: self.domain_size, words: self.words.clone(), marker: PhantomData }
+    }
+
+    fn clone_from(&mut self, from: &Self) {
+        if self.domain_size != from.domain_size {
+            self.words.resize(from.domain_size, 0);
+            self.domain_size = from.domain_size;
+        }
+
+        self.words.copy_from_slice(&from.words);
+    }
+}
+
 impl<T: Idx> fmt::Debug for BitSet<T> {
     fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
         w.debug_list().entries(self.iter()).finish()
@@ -363,7 +374,7 @@ const SPARSE_MAX: usize = 8;
 ///
 /// This type is used by `HybridBitSet`; do not use directly.
 #[derive(Clone, Debug)]
-pub struct SparseBitSet<T: Idx> {
+pub struct SparseBitSet<T> {
     domain_size: usize,
     elems: ArrayVec<[T; SPARSE_MAX]>,
 }
@@ -464,18 +475,27 @@ impl<T: Idx> SubtractFromBitSet<T> for SparseBitSet<T> {
 /// All operations that involve an element will panic if the element is equal
 /// to or greater than the domain size. All operations that involve two bitsets
 /// will panic if the bitsets have differing domain sizes.
-#[derive(Clone, Debug)]
-pub enum HybridBitSet<T: Idx> {
+#[derive(Clone)]
+pub enum HybridBitSet<T> {
     Sparse(SparseBitSet<T>),
     Dense(BitSet<T>),
 }
 
+impl<T: Idx> fmt::Debug for HybridBitSet<T> {
+    fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::Sparse(b) => b.fmt(w),
+            Self::Dense(b) => b.fmt(w),
+        }
+    }
+}
+
 impl<T: Idx> HybridBitSet<T> {
     pub fn new_empty(domain_size: usize) -> Self {
         HybridBitSet::Sparse(SparseBitSet::new_empty(domain_size))
     }
 
-    fn domain_size(&self) -> usize {
+    pub fn domain_size(&self) -> usize {
         match self {
             HybridBitSet::Sparse(sparse) => sparse.domain_size,
             HybridBitSet::Dense(dense) => dense.domain_size,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index b53605b0796..1225776db45 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -2093,7 +2093,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 _ => String::new(),
             };
             if !s.is_empty() {
-                s.push_str(" ");
+                s.push(' ');
             }
             s
         };
diff --git a/compiler/rustc_infer/src/infer/lub.rs b/compiler/rustc_infer/src/infer/lub.rs
index 3e2ea3d0f8f..9f43fac0916 100644
--- a/compiler/rustc_infer/src/infer/lub.rs
+++ b/compiler/rustc_infer/src/infer/lub.rs
@@ -50,7 +50,7 @@ impl TypeRelation<'tcx> for Lub<'combine, 'infcx, 'tcx> {
             ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b),
             ty::Covariant => self.relate(a, b),
             // FIXME(#41044) -- not correct, need test
-            ty::Bivariant => Ok(a.clone()),
+            ty::Bivariant => Ok(a),
             ty::Contravariant => self.fields.glb(self.a_is_expected).relate(a, b),
         }
     }
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index b1b39fd1ad2..f15eb413833 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -693,6 +693,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> {
                 rules,
                 id: resolver.next_node_id(),
                 span: rustc_span::DUMMY_SP,
+                tokens: None,
             }
         }
 
@@ -709,6 +710,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> {
                 id: resolver.next_node_id(),
                 kind: ast::StmtKind::Expr(expr),
                 span: rustc_span::DUMMY_SP,
+                tokens: None,
             }
         }
 
@@ -725,6 +727,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> {
             id: self.resolver.next_node_id(),
             span: rustc_span::DUMMY_SP,
             kind: ast::StmtKind::Expr(loop_expr),
+            tokens: None,
         };
 
         if self.within_static_or_const {
diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml
new file mode 100644
index 00000000000..ee83689f0a4
--- /dev/null
+++ b/compiler/rustc_llvm/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+authors = ["The Rust Project Developers"]
+name = "rustc_llvm"
+version = "0.0.0"
+edition = "2018"
+
+[features]
+static-libstdcpp = []
+emscripten = []
+
+[dependencies]
+libc = "0.2.73"
+
+[build-dependencies]
+build_helper = { path = "../../src/build_helper" }
+cc = "1.0.58"
diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs
new file mode 100644
index 00000000000..7f1e5cf336a
--- /dev/null
+++ b/compiler/rustc_llvm/build.rs
@@ -0,0 +1,322 @@
+use std::env;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+use build_helper::{output, tracked_env_var_os};
+
+fn detect_llvm_link() -> (&'static str, &'static str) {
+    // Force the link mode we want, preferring static by default, but
+    // possibly overridden by `configure --enable-llvm-link-shared`.
+    if tracked_env_var_os("LLVM_LINK_SHARED").is_some() {
+        ("dylib", "--link-shared")
+    } else {
+        ("static", "--link-static")
+    }
+}
+
+fn main() {
+    if tracked_env_var_os("RUST_CHECK").is_some() {
+        // If we're just running `check`, there's no need for LLVM to be built.
+        return;
+    }
+
+    build_helper::restore_library_path();
+
+    let target = env::var("TARGET").expect("TARGET was not set");
+    let llvm_config =
+        tracked_env_var_os("LLVM_CONFIG").map(|x| Some(PathBuf::from(x))).unwrap_or_else(|| {
+            if let Some(dir) = tracked_env_var_os("CARGO_TARGET_DIR").map(PathBuf::from) {
+                let to_test = dir
+                    .parent()
+                    .unwrap()
+                    .parent()
+                    .unwrap()
+                    .join(&target)
+                    .join("llvm/bin/llvm-config");
+                if Command::new(&to_test).output().is_ok() {
+                    return Some(to_test);
+                }
+            }
+            None
+        });
+
+    if let Some(llvm_config) = &llvm_config {
+        println!("cargo:rerun-if-changed={}", llvm_config.display());
+    }
+    let llvm_config = llvm_config.unwrap_or_else(|| PathBuf::from("llvm-config"));
+
+    // Test whether we're cross-compiling LLVM. This is a pretty rare case
+    // currently where we're producing an LLVM for a different platform than
+    // what this build script is currently running on.
+    //
+    // In that case, there's no guarantee that we can actually run the target,
+    // so the build system works around this by giving us the LLVM_CONFIG for
+    // the host platform. This only really works if the host LLVM and target
+    // LLVM are compiled the same way, but for us that's typically the case.
+    //
+    // We *want* detect this cross compiling situation by asking llvm-config
+    // what its host-target is. If that's not the TARGET, then we're cross
+    // compiling. Unfortunately `llvm-config` seems either be buggy, or we're
+    // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will
+    // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This
+    // tricks us into thinking we're doing a cross build when we aren't, so
+    // havoc ensues.
+    //
+    // In any case, if we're cross compiling, this generally just means that we
+    // can't trust all the output of llvm-config because it might be targeted
+    // for the host rather than the target. As a result a bunch of blocks below
+    // are gated on `if !is_crossed`
+    let target = env::var("TARGET").expect("TARGET was not set");
+    let host = env::var("HOST").expect("HOST was not set");
+    let is_crossed = target != host;
+
+    let mut optional_components = vec![
+        "x86",
+        "arm",
+        "aarch64",
+        "amdgpu",
+        "avr",
+        "mips",
+        "powerpc",
+        "systemz",
+        "jsbackend",
+        "webassembly",
+        "msp430",
+        "sparc",
+        "nvptx",
+        "hexagon",
+    ];
+
+    let mut version_cmd = Command::new(&llvm_config);
+    version_cmd.arg("--version");
+    let version_output = output(&mut version_cmd);
+    let mut parts = version_output.split('.').take(2).filter_map(|s| s.parse::<u32>().ok());
+    let (major, _minor) = if let (Some(major), Some(minor)) = (parts.next(), parts.next()) {
+        (major, minor)
+    } else {
+        (6, 0)
+    };
+
+    if major > 6 {
+        optional_components.push("riscv");
+    }
+
+    let required_components = &[
+        "ipo",
+        "bitreader",
+        "bitwriter",
+        "linker",
+        "asmparser",
+        "lto",
+        "coverage",
+        "instrumentation",
+    ];
+
+    let components = output(Command::new(&llvm_config).arg("--components"));
+    let mut components = components.split_whitespace().collect::<Vec<_>>();
+    components.retain(|c| optional_components.contains(c) || required_components.contains(c));
+
+    for component in required_components {
+        if !components.contains(component) {
+            panic!("require llvm component {} but wasn't found", component);
+        }
+    }
+
+    for component in components.iter() {
+        println!("cargo:rustc-cfg=llvm_component=\"{}\"", component);
+    }
+
+    if major >= 9 {
+        println!("cargo:rustc-cfg=llvm_has_msp430_asm_parser");
+    }
+
+    // Link in our own LLVM shims, compiled with the same flags as LLVM
+    let mut cmd = Command::new(&llvm_config);
+    cmd.arg("--cxxflags");
+    let cxxflags = output(&mut cmd);
+    let mut cfg = cc::Build::new();
+    cfg.warnings(false);
+    for flag in cxxflags.split_whitespace() {
+        // Ignore flags like `-m64` when we're doing a cross build
+        if is_crossed && flag.starts_with("-m") {
+            continue;
+        }
+
+        if flag.starts_with("-flto") {
+            continue;
+        }
+
+        // -Wdate-time is not supported by the netbsd cross compiler
+        if is_crossed && target.contains("netbsd") && flag.contains("date-time") {
+            continue;
+        }
+
+        // Include path contains host directory, replace it with target
+        if is_crossed && flag.starts_with("-I") {
+            cfg.flag(&flag.replace(&host, &target));
+            continue;
+        }
+
+        cfg.flag(flag);
+    }
+
+    for component in &components {
+        let mut flag = String::from("LLVM_COMPONENT_");
+        flag.push_str(&component.to_uppercase());
+        cfg.define(&flag, None);
+    }
+
+    if tracked_env_var_os("LLVM_RUSTLLVM").is_some() {
+        cfg.define("LLVM_RUSTLLVM", None);
+    }
+
+    if tracked_env_var_os("LLVM_NDEBUG").is_some() {
+        cfg.define("NDEBUG", None);
+        cfg.debug(false);
+    }
+
+    build_helper::rerun_if_changed_anything_in_dir(Path::new("llvm-wrapper"));
+    cfg.file("llvm-wrapper/PassWrapper.cpp")
+        .file("llvm-wrapper/RustWrapper.cpp")
+        .file("llvm-wrapper/ArchiveWrapper.cpp")
+        .file("llvm-wrapper/CoverageMappingWrapper.cpp")
+        .file("llvm-wrapper/Linker.cpp")
+        .cpp(true)
+        .cpp_link_stdlib(None) // we handle this below
+        .compile("llvm-wrapper");
+
+    let (llvm_kind, llvm_link_arg) = detect_llvm_link();
+
+    // Link in all LLVM libraries, if we're using the "wrong" llvm-config then
+    // we don't pick up system libs because unfortunately they're for the host
+    // of llvm-config, not the target that we're attempting to link.
+    let mut cmd = Command::new(&llvm_config);
+    cmd.arg(llvm_link_arg).arg("--libs");
+
+    if !is_crossed {
+        cmd.arg("--system-libs");
+    } else if target.contains("windows-gnu") {
+        println!("cargo:rustc-link-lib=shell32");
+        println!("cargo:rustc-link-lib=uuid");
+    } else if target.contains("netbsd") || target.contains("haiku") {
+        println!("cargo:rustc-link-lib=z");
+    }
+    cmd.args(&components);
+
+    for lib in output(&mut cmd).split_whitespace() {
+        let name = if lib.starts_with("-l") {
+            &lib[2..]
+        } else if lib.starts_with('-') {
+            &lib[1..]
+        } else if Path::new(lib).exists() {
+            // On MSVC llvm-config will print the full name to libraries, but
+            // we're only interested in the name part
+            let name = Path::new(lib).file_name().unwrap().to_str().unwrap();
+            name.trim_end_matches(".lib")
+        } else if lib.ends_with(".lib") {
+            // Some MSVC libraries just come up with `.lib` tacked on, so chop
+            // that off
+            lib.trim_end_matches(".lib")
+        } else {
+            continue;
+        };
+
+        // Don't need or want this library, but LLVM's CMake build system
+        // doesn't provide a way to disable it, so filter it here even though we
+        // may or may not have built it. We don't reference anything from this
+        // library and it otherwise may just pull in extra dependencies on
+        // libedit which we don't want
+        if name == "LLVMLineEditor" {
+            continue;
+        }
+
+        let kind = if name.starts_with("LLVM") { llvm_kind } else { "dylib" };
+        println!("cargo:rustc-link-lib={}={}", kind, name);
+    }
+
+    // LLVM ldflags
+    //
+    // If we're a cross-compile of LLVM then unfortunately we can't trust these
+    // ldflags (largely where all the LLVM libs are located). Currently just
+    // hack around this by replacing the host triple with the target and pray
+    // that those -L directories are the same!
+    let mut cmd = Command::new(&llvm_config);
+    cmd.arg(llvm_link_arg).arg("--ldflags");
+    for lib in output(&mut cmd).split_whitespace() {
+        if is_crossed {
+            if lib.starts_with("-LIBPATH:") {
+                println!("cargo:rustc-link-search=native={}", lib[9..].replace(&host, &target));
+            } else if lib.starts_with("-L") {
+                println!("cargo:rustc-link-search=native={}", lib[2..].replace(&host, &target));
+            }
+        } else if lib.starts_with("-LIBPATH:") {
+            println!("cargo:rustc-link-search=native={}", &lib[9..]);
+        } else if lib.starts_with("-l") {
+            println!("cargo:rustc-link-lib={}", &lib[2..]);
+        } else if lib.starts_with("-L") {
+            println!("cargo:rustc-link-search=native={}", &lib[2..]);
+        }
+    }
+
+    // Some LLVM linker flags (-L and -l) may be needed even when linking
+    // rustc_llvm, for example when using static libc++, we may need to
+    // manually specify the library search path and -ldl -lpthread as link
+    // dependencies.
+    let llvm_linker_flags = tracked_env_var_os("LLVM_LINKER_FLAGS");
+    if let Some(s) = llvm_linker_flags {
+        for lib in s.into_string().unwrap().split_whitespace() {
+            if lib.starts_with("-l") {
+                println!("cargo:rustc-link-lib={}", &lib[2..]);
+            } else if lib.starts_with("-L") {
+                println!("cargo:rustc-link-search=native={}", &lib[2..]);
+            }
+        }
+    }
+
+    let llvm_static_stdcpp = tracked_env_var_os("LLVM_STATIC_STDCPP");
+    let llvm_use_libcxx = tracked_env_var_os("LLVM_USE_LIBCXX");
+
+    let stdcppname = if target.contains("openbsd") {
+        if target.contains("sparc64") { "estdc++" } else { "c++" }
+    } else if target.contains("freebsd") {
+        "c++"
+    } else if target.contains("darwin") {
+        "c++"
+    } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() {
+        // NetBSD uses a separate library when relocation is required
+        "stdc++_pic"
+    } else if llvm_use_libcxx.is_some() {
+        "c++"
+    } else {
+        "stdc++"
+    };
+
+    // RISC-V requires libatomic for sub-word atomic operations
+    if target.starts_with("riscv") {
+        println!("cargo:rustc-link-lib=atomic");
+    }
+
+    // C++ runtime library
+    if !target.contains("msvc") {
+        if let Some(s) = llvm_static_stdcpp {
+            assert!(!cxxflags.contains("stdlib=libc++"));
+            let path = PathBuf::from(s);
+            println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display());
+            if target.contains("windows") {
+                println!("cargo:rustc-link-lib=static-nobundle={}", stdcppname);
+            } else {
+                println!("cargo:rustc-link-lib=static={}", stdcppname);
+            }
+        } else if cxxflags.contains("stdlib=libc++") {
+            println!("cargo:rustc-link-lib=c++");
+        } else {
+            println!("cargo:rustc-link-lib={}", stdcppname);
+        }
+    }
+
+    // Libstdc++ depends on pthread which Rust doesn't link on MinGW
+    // since nothing else requires it.
+    if target.contains("windows-gnu") {
+        println!("cargo:rustc-link-lib=static-nobundle=pthread");
+    }
+}
diff --git a/compiler/rustc_llvm/llvm-wrapper/.editorconfig b/compiler/rustc_llvm/llvm-wrapper/.editorconfig
new file mode 100644
index 00000000000..865cd45f708
--- /dev/null
+++ b/compiler/rustc_llvm/llvm-wrapper/.editorconfig
@@ -0,0 +1,6 @@
+[*.{h,cpp}]
+end_of_line = lf
+insert_final_newline = true
+charset = utf-8
+indent_style = space
+indent_size = 2
diff --git a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp
new file mode 100644
index 00000000000..2797fe8df4a
--- /dev/null
+++ b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp
@@ -0,0 +1,226 @@
+#include "LLVMWrapper.h"
+
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ArchiveWriter.h"
+#include "llvm/Support/Path.h"
+
+using namespace llvm;
+using namespace llvm::object;
+
+struct RustArchiveMember {
+  const char *Filename;
+  const char *Name;
+  Archive::Child Child;
+
+  RustArchiveMember()
+      : Filename(nullptr), Name(nullptr),
+        Child(nullptr, nullptr, nullptr)
+  {
+  }
+  ~RustArchiveMember() {}
+};
+
+struct RustArchiveIterator {
+  bool First;
+  Archive::child_iterator Cur;
+  Archive::child_iterator End;
+  std::unique_ptr<Error> Err;
+
+  RustArchiveIterator(Archive::child_iterator Cur, Archive::child_iterator End,
+      std::unique_ptr<Error> Err)
+    : First(true),
+      Cur(Cur),
+      End(End),
+      Err(std::move(Err)) {}
+};
+
+enum class LLVMRustArchiveKind {
+  GNU,
+  BSD,
+  DARWIN,
+  COFF,
+};
+
+static Archive::Kind fromRust(LLVMRustArchiveKind Kind) {
+  switch (Kind) {
+  case LLVMRustArchiveKind::GNU:
+    return Archive::K_GNU;
+  case LLVMRustArchiveKind::BSD:
+    return Archive::K_BSD;
+  case LLVMRustArchiveKind::DARWIN:
+    return Archive::K_DARWIN;
+  case LLVMRustArchiveKind::COFF:
+    return Archive::K_COFF;
+  default:
+    report_fatal_error("Bad ArchiveKind.");
+  }
+}
+
+typedef OwningBinary<Archive> *LLVMRustArchiveRef;
+typedef RustArchiveMember *LLVMRustArchiveMemberRef;
+typedef Archive::Child *LLVMRustArchiveChildRef;
+typedef Archive::Child const *LLVMRustArchiveChildConstRef;
+typedef RustArchiveIterator *LLVMRustArchiveIteratorRef;
+
+extern "C" LLVMRustArchiveRef LLVMRustOpenArchive(char *Path) {
+  ErrorOr<std::unique_ptr<MemoryBuffer>> BufOr =
+      MemoryBuffer::getFile(Path, -1, false);
+  if (!BufOr) {
+    LLVMRustSetLastError(BufOr.getError().message().c_str());
+    return nullptr;
+  }
+
+  Expected<std::unique_ptr<Archive>> ArchiveOr =
+      Archive::create(BufOr.get()->getMemBufferRef());
+
+  if (!ArchiveOr) {
+    LLVMRustSetLastError(toString(ArchiveOr.takeError()).c_str());
+    return nullptr;
+  }
+
+  OwningBinary<Archive> *Ret = new OwningBinary<Archive>(
+      std::move(ArchiveOr.get()), std::move(BufOr.get()));
+
+  return Ret;
+}
+
+extern "C" void LLVMRustDestroyArchive(LLVMRustArchiveRef RustArchive) {
+  delete RustArchive;
+}
+
+extern "C" LLVMRustArchiveIteratorRef
+LLVMRustArchiveIteratorNew(LLVMRustArchiveRef RustArchive) {
+  Archive *Archive = RustArchive->getBinary();
+#if LLVM_VERSION_GE(10, 0)
+  std::unique_ptr<Error> Err = std::make_unique<Error>(Error::success());
+#else
+  std::unique_ptr<Error> Err = llvm::make_unique<Error>(Error::success());
+#endif
+  auto Cur = Archive->child_begin(*Err);
+  if (*Err) {
+    LLVMRustSetLastError(toString(std::move(*Err)).c_str());
+    return nullptr;
+  }
+  auto End = Archive->child_end();
+  return new RustArchiveIterator(Cur, End, std::move(Err));
+}
+
+extern "C" LLVMRustArchiveChildConstRef
+LLVMRustArchiveIteratorNext(LLVMRustArchiveIteratorRef RAI) {
+  if (RAI->Cur == RAI->End)
+    return nullptr;
+
+  // Advancing the iterator validates the next child, and this can
+  // uncover an error. LLVM requires that we check all Errors,
+  // so we only advance the iterator if we actually need to fetch
+  // the next child.
+  // This means we must not advance the iterator in the *first* call,
+  // but instead advance it *before* fetching the child in all later calls.
+  if (!RAI->First) {
+    ++RAI->Cur;
+    if (*RAI->Err) {
+      LLVMRustSetLastError(toString(std::move(*RAI->Err)).c_str());
+      return nullptr;
+    }
+  } else {
+    RAI->First = false;
+  }
+
+  if (RAI->Cur == RAI->End)
+    return nullptr;
+
+  const Archive::Child &Child = *RAI->Cur.operator->();
+  Archive::Child *Ret = new Archive::Child(Child);
+
+  return Ret;
+}
+
+extern "C" void LLVMRustArchiveChildFree(LLVMRustArchiveChildRef Child) {
+  delete Child;
+}
+
+extern "C" void LLVMRustArchiveIteratorFree(LLVMRustArchiveIteratorRef RAI) {
+  delete RAI;
+}
+
+extern "C" const char *
+LLVMRustArchiveChildName(LLVMRustArchiveChildConstRef Child, size_t *Size) {
+  Expected<StringRef> NameOrErr = Child->getName();
+  if (!NameOrErr) {
+    // rustc_codegen_llvm currently doesn't use this error string, but it might be
+    // useful in the future, and in the mean time this tells LLVM that the
+    // error was not ignored and that it shouldn't abort the process.
+    LLVMRustSetLastError(toString(NameOrErr.takeError()).c_str());
+    return nullptr;
+  }
+  StringRef Name = NameOrErr.get();
+  *Size = Name.size();
+  return Name.data();
+}
+
+extern "C" const char *LLVMRustArchiveChildData(LLVMRustArchiveChildRef Child,
+                                                size_t *Size) {
+  StringRef Buf;
+  Expected<StringRef> BufOrErr = Child->getBuffer();
+  if (!BufOrErr) {
+    LLVMRustSetLastError(toString(BufOrErr.takeError()).c_str());
+    return nullptr;
+  }
+  Buf = BufOrErr.get();
+  *Size = Buf.size();
+  return Buf.data();
+}
+
+extern "C" LLVMRustArchiveMemberRef
+LLVMRustArchiveMemberNew(char *Filename, char *Name,
+                         LLVMRustArchiveChildRef Child) {
+  RustArchiveMember *Member = new RustArchiveMember;
+  Member->Filename = Filename;
+  Member->Name = Name;
+  if (Child)
+    Member->Child = *Child;
+  return Member;
+}
+
+extern "C" void LLVMRustArchiveMemberFree(LLVMRustArchiveMemberRef Member) {
+  delete Member;
+}
+
+extern "C" LLVMRustResult
+LLVMRustWriteArchive(char *Dst, size_t NumMembers,
+                     const LLVMRustArchiveMemberRef *NewMembers,
+                     bool WriteSymbtab, LLVMRustArchiveKind RustKind) {
+
+  std::vector<NewArchiveMember> Members;
+  auto Kind = fromRust(RustKind);
+
+  for (size_t I = 0; I < NumMembers; I++) {
+    auto Member = NewMembers[I];
+    assert(Member->Name);
+    if (Member->Filename) {
+      Expected<NewArchiveMember> MOrErr =
+          NewArchiveMember::getFile(Member->Filename, true);
+      if (!MOrErr) {
+        LLVMRustSetLastError(toString(MOrErr.takeError()).c_str());
+        return LLVMRustResult::Failure;
+      }
+      MOrErr->MemberName = sys::path::filename(MOrErr->MemberName);
+      Members.push_back(std::move(*MOrErr));
+    } else {
+      Expected<NewArchiveMember> MOrErr =
+          NewArchiveMember::getOldMember(Member->Child, true);
+      if (!MOrErr) {
+        LLVMRustSetLastError(toString(MOrErr.takeError()).c_str());
+        return LLVMRustResult::Failure;
+      }
+      Members.push_back(std::move(*MOrErr));
+    }
+  }
+
+  auto Result = writeArchive(Dst, Members, WriteSymbtab, Kind, true, false);
+  if (!Result)
+    return LLVMRustResult::Success;
+  LLVMRustSetLastError(toString(std::move(Result)).c_str());
+
+  return LLVMRustResult::Failure;
+}
diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
new file mode 100644
index 00000000000..2b1143a4ecf
--- /dev/null
+++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
@@ -0,0 +1,70 @@
+#include "LLVMWrapper.h"
+#include "llvm/ProfileData/Coverage/CoverageMapping.h"
+#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
+#include "llvm/ProfileData/InstrProf.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/LEB128.h"
+
+#include <iostream>
+
+using namespace llvm;
+
+extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer(
+    const char* const Filenames[],
+    size_t FilenamesLen,
+    RustStringRef BufferOut) {
+  // LLVM 11's CoverageFilenamesSectionWriter uses its new `Version4` format,
+  // so we're manually writing the `Version3` format ourselves.
+  RawRustStringOstream OS(BufferOut);
+  encodeULEB128(FilenamesLen, OS);
+  for (size_t i = 0; i < FilenamesLen; i++) {
+    StringRef Filename(Filenames[i]);
+    encodeULEB128(Filename.size(), OS);
+    OS << Filename;
+  }
+}
+
+extern "C" void LLVMRustCoverageWriteMappingToBuffer(
+    const unsigned *VirtualFileMappingIDs,
+    unsigned NumVirtualFileMappingIDs,
+    const coverage::CounterExpression *Expressions,
+    unsigned NumExpressions,
+    coverage::CounterMappingRegion *MappingRegions,
+    unsigned NumMappingRegions,
+    RustStringRef BufferOut) {
+  auto CoverageMappingWriter = coverage::CoverageMappingWriter(
+      makeArrayRef(VirtualFileMappingIDs, NumVirtualFileMappingIDs),
+      makeArrayRef(Expressions, NumExpressions),
+      makeMutableArrayRef(MappingRegions, NumMappingRegions));
+  RawRustStringOstream OS(BufferOut);
+  CoverageMappingWriter.write(OS);
+}
+
+extern "C" LLVMValueRef LLVMRustCoverageCreatePGOFuncNameVar(LLVMValueRef F, const char *FuncName) {
+  StringRef FuncNameRef(FuncName);
+  return wrap(createPGOFuncNameVar(*cast<Function>(unwrap(F)), FuncNameRef));
+}
+
+extern "C" uint64_t LLVMRustCoverageComputeHash(const char *Name) {
+  StringRef NameRef(Name);
+  return IndexedInstrProf::ComputeHash(NameRef);
+}
+
+extern "C" void LLVMRustCoverageWriteSectionNameToString(LLVMModuleRef M,
+                                                         RustStringRef Str) {
+  Triple TargetTriple(unwrap(M)->getTargetTriple());
+  auto name = getInstrProfSectionName(IPSK_covmap,
+                                      TargetTriple.getObjectFormat());
+  RawRustStringOstream OS(Str);
+  OS << name;
+}
+
+extern "C" void LLVMRustCoverageWriteMappingVarNameToString(RustStringRef Str) {
+  auto name = getCoverageMappingVarName();
+  RawRustStringOstream OS(Str);
+  OS << name;
+}
+
+extern "C" uint32_t LLVMRustCoverageMappingVersion() {
+  return coverage::CovMapVersion::Version3;
+}
diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
new file mode 100644
index 00000000000..57b8664d3b6
--- /dev/null
+++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
@@ -0,0 +1,115 @@
+#include "llvm-c/BitReader.h"
+#include "llvm-c/Core.h"
+#include "llvm-c/Object.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Analysis/Lint.h"
+#include "llvm/Analysis/Passes.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InlineAsm.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/Memory.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/Timer.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetOptions.h"
+#include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/Instrumentation.h"
+#include "llvm/Transforms/Scalar.h"
+#include "llvm/Transforms/Vectorize.h"
+
+#define LLVM_VERSION_GE(major, minor)                                          \
+  (LLVM_VERSION_MAJOR > (major) ||                                             \
+   LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR >= (minor))
+
+#define LLVM_VERSION_EQ(major, minor)                                          \
+  (LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR == (minor))
+
+#define LLVM_VERSION_LE(major, minor)                                          \
+  (LLVM_VERSION_MAJOR < (major) ||                                             \
+   LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR <= (minor))
+
+#define LLVM_VERSION_LT(major, minor) (!LLVM_VERSION_GE((major), (minor)))
+
+#include "llvm/IR/LegacyPassManager.h"
+
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+
+#include "llvm/IR/DIBuilder.h"
+#include "llvm/IR/DebugInfo.h"
+#include "llvm/IR/IRPrintingPasses.h"
+#include "llvm/Linker/Linker.h"
+
+extern "C" void LLVMRustSetLastError(const char *);
+
+enum class LLVMRustResult { Success, Failure };
+
+enum LLVMRustAttribute {
+  AlwaysInline = 0,
+  ByVal = 1,
+  Cold = 2,
+  InlineHint = 3,
+  MinSize = 4,
+  Naked = 5,
+  NoAlias = 6,
+  NoCapture = 7,
+  NoInline = 8,
+  NonNull = 9,
+  NoRedZone = 10,
+  NoReturn = 11,
+  NoUnwind = 12,
+  OptimizeForSize = 13,
+  ReadOnly = 14,
+  SExt = 15,
+  StructRet = 16,
+  UWTable = 17,
+  ZExt = 18,
+  InReg = 19,
+  SanitizeThread = 20,
+  SanitizeAddress = 21,
+  SanitizeMemory = 22,
+  NonLazyBind = 23,
+  OptimizeNone = 24,
+  ReturnsTwice = 25,
+  ReadNone = 26,
+  InaccessibleMemOnly = 27,
+};
+
+typedef struct OpaqueRustString *RustStringRef;
+typedef struct LLVMOpaqueTwine *LLVMTwineRef;
+typedef struct LLVMOpaqueSMDiagnostic *LLVMSMDiagnosticRef;
+
+extern "C" void LLVMRustStringWriteImpl(RustStringRef Str, const char *Ptr,
+                                        size_t Size);
+
+class RawRustStringOstream : public llvm::raw_ostream {
+  RustStringRef Str;
+  uint64_t Pos;
+
+  void write_impl(const char *Ptr, size_t Size) override {
+    LLVMRustStringWriteImpl(Str, Ptr, Size);
+    Pos += Size;
+  }
+
+  uint64_t current_pos() const override { return Pos; }
+
+public:
+  explicit RawRustStringOstream(RustStringRef Str) : Str(Str), Pos(0) {}
+
+  ~RawRustStringOstream() {
+    // LLVM requires this.
+    flush();
+  }
+};
diff --git a/compiler/rustc_llvm/llvm-wrapper/Linker.cpp b/compiler/rustc_llvm/llvm-wrapper/Linker.cpp
new file mode 100644
index 00000000000..8766e96f086
--- /dev/null
+++ b/compiler/rustc_llvm/llvm-wrapper/Linker.cpp
@@ -0,0 +1,48 @@
+#include "llvm/Linker/Linker.h"
+
+#include "LLVMWrapper.h"
+
+using namespace llvm;
+
+struct RustLinker {
+  Linker L;
+  LLVMContext &Ctx;
+
+  RustLinker(Module &M) :
+    L(M),
+    Ctx(M.getContext())
+  {}
+};
+
+extern "C" RustLinker*
+LLVMRustLinkerNew(LLVMModuleRef DstRef) {
+  Module *Dst = unwrap(DstRef);
+
+  return new RustLinker(*Dst);
+}
+
+extern "C" void
+LLVMRustLinkerFree(RustLinker *L) {
+  delete L;
+}
+
+extern "C" bool
+LLVMRustLinkerAdd(RustLinker *L, char *BC, size_t Len) {
+  std::unique_ptr<MemoryBuffer> Buf =
+      MemoryBuffer::getMemBufferCopy(StringRef(BC, Len));
+
+  Expected<std::unique_ptr<Module>> SrcOrError =
+      llvm::getLazyBitcodeModule(Buf->getMemBufferRef(), L->Ctx);
+  if (!SrcOrError) {
+    LLVMRustSetLastError(toString(SrcOrError.takeError()).c_str());
+    return false;
+  }
+
+  auto Src = std::move(*SrcOrError);
+
+  if (L->L.linkInModule(std::move(Src))) {
+    LLVMRustSetLastError("");
+    return false;
+  }
+  return true;
+}
diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
new file mode 100644
index 00000000000..7b1c3f9ba2c
--- /dev/null
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -0,0 +1,1655 @@
+#include <stdio.h>
+
+#include <vector>
+#include <set>
+
+#include "LLVMWrapper.h"
+
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/CodeGen/TargetSubtargetInfo.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/IR/AutoUpgrade.h"
+#include "llvm/IR/AssemblyAnnotationWriter.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/IRObjectFile.h"
+#include "llvm/Passes/PassBuilder.h"
+#if LLVM_VERSION_GE(9, 0)
+#include "llvm/Passes/StandardInstrumentations.h"
+#endif
+#include "llvm/Support/CBindingWrapping.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#include "llvm/Transforms/IPO/AlwaysInliner.h"
+#include "llvm/Transforms/IPO/FunctionImport.h"
+#include "llvm/Transforms/Utils/FunctionImportUtils.h"
+#include "llvm/LTO/LTO.h"
+#include "llvm-c/Transforms/PassManagerBuilder.h"
+
+#include "llvm/Transforms/Instrumentation.h"
+#if LLVM_VERSION_GE(9, 0)
+#include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
+#include "llvm/Support/TimeProfiler.h"
+#endif
+#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
+#include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
+#if LLVM_VERSION_GE(9, 0)
+#include "llvm/Transforms/Utils/CanonicalizeAliases.h"
+#endif
+#include "llvm/Transforms/Utils/NameAnonGlobals.h"
+
+using namespace llvm;
+
+typedef struct LLVMOpaquePass *LLVMPassRef;
+typedef struct LLVMOpaqueTargetMachine *LLVMTargetMachineRef;
+
+DEFINE_STDCXX_CONVERSION_FUNCTIONS(Pass, LLVMPassRef)
+DEFINE_STDCXX_CONVERSION_FUNCTIONS(TargetMachine, LLVMTargetMachineRef)
+#if LLVM_VERSION_LT(11, 0)
+DEFINE_STDCXX_CONVERSION_FUNCTIONS(PassManagerBuilder,
+                                   LLVMPassManagerBuilderRef)
+#endif
+
+extern "C" void LLVMInitializePasses() {
+  PassRegistry &Registry = *PassRegistry::getPassRegistry();
+  initializeCore(Registry);
+  initializeCodeGen(Registry);
+  initializeScalarOpts(Registry);
+  initializeVectorization(Registry);
+  initializeIPO(Registry);
+  initializeAnalysis(Registry);
+  initializeTransformUtils(Registry);
+  initializeInstCombine(Registry);
+  initializeInstrumentation(Registry);
+  initializeTarget(Registry);
+}
+
+extern "C" void LLVMTimeTraceProfilerInitialize() {
+#if LLVM_VERSION_GE(10, 0)
+  timeTraceProfilerInitialize(
+      /* TimeTraceGranularity */ 0,
+      /* ProcName */ "rustc");
+#elif LLVM_VERSION_GE(9, 0)
+  timeTraceProfilerInitialize();
+#endif
+}
+
+extern "C" void LLVMTimeTraceProfilerFinish(const char* FileName) {
+#if LLVM_VERSION_GE(9, 0)
+  StringRef FN(FileName);
+  std::error_code EC;
+  raw_fd_ostream OS(FN, EC, sys::fs::CD_CreateAlways);
+
+  timeTraceProfilerWrite(OS);
+  timeTraceProfilerCleanup();
+#endif
+}
+
+enum class LLVMRustPassKind {
+  Other,
+  Function,
+  Module,
+};
+
+static LLVMRustPassKind toRust(PassKind Kind) {
+  switch (Kind) {
+  case PT_Function:
+    return LLVMRustPassKind::Function;
+  case PT_Module:
+    return LLVMRustPassKind::Module;
+  default:
+    return LLVMRustPassKind::Other;
+  }
+}
+
+extern "C" LLVMPassRef LLVMRustFindAndCreatePass(const char *PassName) {
+  StringRef SR(PassName);
+  PassRegistry *PR = PassRegistry::getPassRegistry();
+
+  const PassInfo *PI = PR->getPassInfo(SR);
+  if (PI) {
+    return wrap(PI->createPass());
+  }
+  return nullptr;
+}
+
+extern "C" LLVMPassRef LLVMRustCreateAddressSanitizerFunctionPass(bool Recover) {
+  const bool CompileKernel = false;
+  const bool UseAfterScope = true;
+
+  return wrap(createAddressSanitizerFunctionPass(CompileKernel, Recover, UseAfterScope));
+}
+
+extern "C" LLVMPassRef LLVMRustCreateModuleAddressSanitizerPass(bool Recover) {
+  const bool CompileKernel = false;
+
+#if LLVM_VERSION_GE(9, 0)
+  return wrap(createModuleAddressSanitizerLegacyPassPass(CompileKernel, Recover));
+#else
+  return wrap(createAddressSanitizerModulePass(CompileKernel, Recover));
+#endif
+}
+
+extern "C" LLVMPassRef LLVMRustCreateMemorySanitizerPass(int TrackOrigins, bool Recover) {
+#if LLVM_VERSION_GE(9, 0)
+  const bool CompileKernel = false;
+
+  return wrap(createMemorySanitizerLegacyPassPass(
+      MemorySanitizerOptions{TrackOrigins, Recover, CompileKernel}));
+#else
+  return wrap(createMemorySanitizerLegacyPassPass(TrackOrigins, Recover));
+#endif
+}
+
+extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() {
+  return wrap(createThreadSanitizerLegacyPassPass());
+}
+
+extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) {
+  assert(RustPass);
+  Pass *Pass = unwrap(RustPass);
+  return toRust(Pass->getPassKind());
+}
+
+extern "C" void LLVMRustAddPass(LLVMPassManagerRef PMR, LLVMPassRef RustPass) {
+  assert(RustPass);
+  Pass *Pass = unwrap(RustPass);
+  PassManagerBase *PMB = unwrap(PMR);
+  PMB->add(Pass);
+}
+
+extern "C"
+void LLVMRustPassManagerBuilderPopulateThinLTOPassManager(
+  LLVMPassManagerBuilderRef PMBR,
+  LLVMPassManagerRef PMR
+) {
+  unwrap(PMBR)->populateThinLTOPassManager(*unwrap(PMR));
+}
+
+extern "C"
+void LLVMRustAddLastExtensionPasses(
+    LLVMPassManagerBuilderRef PMBR, LLVMPassRef *Passes, size_t NumPasses) {
+  auto AddExtensionPasses = [Passes, NumPasses](
+      const PassManagerBuilder &Builder, PassManagerBase &PM) {
+    for (size_t I = 0; I < NumPasses; I++) {
+      PM.add(unwrap(Passes[I]));
+    }
+  };
+  // Add the passes to both of the pre-finalization extension points,
+  // so they are run for optimized and non-optimized builds.
+  unwrap(PMBR)->addExtension(PassManagerBuilder::EP_OptimizerLast,
+                             AddExtensionPasses);
+  unwrap(PMBR)->addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
+                             AddExtensionPasses);
+}
+
+#ifdef LLVM_COMPONENT_X86
+#define SUBTARGET_X86 SUBTARGET(X86)
+#else
+#define SUBTARGET_X86
+#endif
+
+#ifdef LLVM_COMPONENT_ARM
+#define SUBTARGET_ARM SUBTARGET(ARM)
+#else
+#define SUBTARGET_ARM
+#endif
+
+#ifdef LLVM_COMPONENT_AARCH64
+#define SUBTARGET_AARCH64 SUBTARGET(AArch64)
+#else
+#define SUBTARGET_AARCH64
+#endif
+
+#ifdef LLVM_COMPONENT_AVR
+#define SUBTARGET_AVR SUBTARGET(AVR)
+#else
+#define SUBTARGET_AVR
+#endif
+
+#ifdef LLVM_COMPONENT_MIPS
+#define SUBTARGET_MIPS SUBTARGET(Mips)
+#else
+#define SUBTARGET_MIPS
+#endif
+
+#ifdef LLVM_COMPONENT_POWERPC
+#define SUBTARGET_PPC SUBTARGET(PPC)
+#else
+#define SUBTARGET_PPC
+#endif
+
+#ifdef LLVM_COMPONENT_SYSTEMZ
+#define SUBTARGET_SYSTEMZ SUBTARGET(SystemZ)
+#else
+#define SUBTARGET_SYSTEMZ
+#endif
+
+#ifdef LLVM_COMPONENT_MSP430
+#define SUBTARGET_MSP430 SUBTARGET(MSP430)
+#else
+#define SUBTARGET_MSP430
+#endif
+
+#ifdef LLVM_COMPONENT_RISCV
+#define SUBTARGET_RISCV SUBTARGET(RISCV)
+#else
+#define SUBTARGET_RISCV
+#endif
+
+#ifdef LLVM_COMPONENT_SPARC
+#define SUBTARGET_SPARC SUBTARGET(Sparc)
+#else
+#define SUBTARGET_SPARC
+#endif
+
+#ifdef LLVM_COMPONENT_HEXAGON
+#define SUBTARGET_HEXAGON SUBTARGET(Hexagon)
+#else
+#define SUBTARGET_HEXAGON
+#endif
+
+#define GEN_SUBTARGETS                                                         \
+  SUBTARGET_X86                                                                \
+  SUBTARGET_ARM                                                                \
+  SUBTARGET_AARCH64                                                            \
+  SUBTARGET_AVR                                                                \
+  SUBTARGET_MIPS                                                               \
+  SUBTARGET_PPC                                                                \
+  SUBTARGET_SYSTEMZ                                                            \
+  SUBTARGET_MSP430                                                             \
+  SUBTARGET_SPARC                                                              \
+  SUBTARGET_HEXAGON                                                            \
+  SUBTARGET_RISCV                                                              \
+
+#define SUBTARGET(x)                                                           \
+  namespace llvm {                                                             \
+  extern const SubtargetFeatureKV x##FeatureKV[];                              \
+  extern const SubtargetFeatureKV x##SubTypeKV[];                              \
+  }
+
+GEN_SUBTARGETS
+#undef SUBTARGET
+
+extern "C" bool LLVMRustHasFeature(LLVMTargetMachineRef TM,
+                                   const char *Feature) {
+  TargetMachine *Target = unwrap(TM);
+  const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
+  return MCInfo->checkFeatures(std::string("+") + Feature);
+}
+
+enum class LLVMRustCodeModel {
+  Tiny,
+  Small,
+  Kernel,
+  Medium,
+  Large,
+  None,
+};
+
+static Optional<CodeModel::Model> fromRust(LLVMRustCodeModel Model) {
+  switch (Model) {
+  case LLVMRustCodeModel::Tiny:
+    return CodeModel::Tiny;
+  case LLVMRustCodeModel::Small:
+    return CodeModel::Small;
+  case LLVMRustCodeModel::Kernel:
+    return CodeModel::Kernel;
+  case LLVMRustCodeModel::Medium:
+    return CodeModel::Medium;
+  case LLVMRustCodeModel::Large:
+    return CodeModel::Large;
+  case LLVMRustCodeModel::None:
+    return None;
+  default:
+    report_fatal_error("Bad CodeModel.");
+  }
+}
+
+enum class LLVMRustCodeGenOptLevel {
+  None,
+  Less,
+  Default,
+  Aggressive,
+};
+
+static CodeGenOpt::Level fromRust(LLVMRustCodeGenOptLevel Level) {
+  switch (Level) {
+  case LLVMRustCodeGenOptLevel::None:
+    return CodeGenOpt::None;
+  case LLVMRustCodeGenOptLevel::Less:
+    return CodeGenOpt::Less;
+  case LLVMRustCodeGenOptLevel::Default:
+    return CodeGenOpt::Default;
+  case LLVMRustCodeGenOptLevel::Aggressive:
+    return CodeGenOpt::Aggressive;
+  default:
+    report_fatal_error("Bad CodeGenOptLevel.");
+  }
+}
+
+enum class LLVMRustPassBuilderOptLevel {
+  O0,
+  O1,
+  O2,
+  O3,
+  Os,
+  Oz,
+};
+
+static PassBuilder::OptimizationLevel fromRust(LLVMRustPassBuilderOptLevel Level) {
+  switch (Level) {
+  case LLVMRustPassBuilderOptLevel::O0:
+    return PassBuilder::OptimizationLevel::O0;
+  case LLVMRustPassBuilderOptLevel::O1:
+    return PassBuilder::OptimizationLevel::O1;
+  case LLVMRustPassBuilderOptLevel::O2:
+    return PassBuilder::OptimizationLevel::O2;
+  case LLVMRustPassBuilderOptLevel::O3:
+    return PassBuilder::OptimizationLevel::O3;
+  case LLVMRustPassBuilderOptLevel::Os:
+    return PassBuilder::OptimizationLevel::Os;
+  case LLVMRustPassBuilderOptLevel::Oz:
+    return PassBuilder::OptimizationLevel::Oz;
+  default:
+    report_fatal_error("Bad PassBuilderOptLevel.");
+  }
+}
+
+enum class LLVMRustRelocModel {
+  Static,
+  PIC,
+  DynamicNoPic,
+  ROPI,
+  RWPI,
+  ROPIRWPI,
+};
+
+static Reloc::Model fromRust(LLVMRustRelocModel RustReloc) {
+  switch (RustReloc) {
+  case LLVMRustRelocModel::Static:
+    return Reloc::Static;
+  case LLVMRustRelocModel::PIC:
+    return Reloc::PIC_;
+  case LLVMRustRelocModel::DynamicNoPic:
+    return Reloc::DynamicNoPIC;
+  case LLVMRustRelocModel::ROPI:
+    return Reloc::ROPI;
+  case LLVMRustRelocModel::RWPI:
+    return Reloc::RWPI;
+  case LLVMRustRelocModel::ROPIRWPI:
+    return Reloc::ROPI_RWPI;
+  }
+  report_fatal_error("Bad RelocModel.");
+}
+
+#ifdef LLVM_RUSTLLVM
+/// getLongestEntryLength - Return the length of the longest entry in the table.
+template<typename KV>
+static size_t getLongestEntryLength(ArrayRef<KV> Table) {
+  size_t MaxLen = 0;
+  for (auto &I : Table)
+    MaxLen = std::max(MaxLen, std::strlen(I.Key));
+  return MaxLen;
+}
+
+extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM) {
+  const TargetMachine *Target = unwrap(TM);
+  const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
+  const Triple::ArchType HostArch = Triple(sys::getProcessTriple()).getArch();
+  const Triple::ArchType TargetArch = Target->getTargetTriple().getArch();
+  const ArrayRef<SubtargetSubTypeKV> CPUTable = MCInfo->getCPUTable();
+  unsigned MaxCPULen = getLongestEntryLength(CPUTable);
+
+  printf("Available CPUs for this target:\n");
+  if (HostArch == TargetArch) {
+    const StringRef HostCPU = sys::getHostCPUName();
+    printf("    %-*s - Select the CPU of the current host (currently %.*s).\n",
+      MaxCPULen, "native", (int)HostCPU.size(), HostCPU.data());
+  }
+  for (auto &CPU : CPUTable)
+    printf("    %-*s\n", MaxCPULen, CPU.Key);
+  printf("\n");
+}
+
+extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef TM) {
+  const TargetMachine *Target = unwrap(TM);
+  const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
+  const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getFeatureTable();
+  unsigned MaxFeatLen = getLongestEntryLength(FeatTable);
+
+  printf("Available features for this target:\n");
+  for (auto &Feature : FeatTable)
+    printf("    %-*s - %s.\n", MaxFeatLen, Feature.Key, Feature.Desc);
+  printf("\nRust-specific features:\n");
+  printf("    %-*s - %s.\n",
+    MaxFeatLen,
+    "crt-static",
+    "Enables libraries with C Run-time Libraries(CRT) to be statically linked"
+  );
+  printf("\n");
+
+  printf("Use +feature to enable a feature, or -feature to disable it.\n"
+         "For example, rustc -C -target-cpu=mycpu -C "
+         "target-feature=+feature1,-feature2\n\n");
+}
+
+#else
+
+extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef) {
+  printf("Target CPU help is not supported by this LLVM version.\n\n");
+}
+
+extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef) {
+  printf("Target features help is not supported by this LLVM version.\n\n");
+}
+#endif
+
+extern "C" const char* LLVMRustGetHostCPUName(size_t *len) {
+  StringRef Name = sys::getHostCPUName();
+  *len = Name.size();
+  return Name.data();
+}
+
+extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
+    const char *TripleStr, const char *CPU, const char *Feature,
+    const char *ABIStr, LLVMRustCodeModel RustCM, LLVMRustRelocModel RustReloc,
+    LLVMRustCodeGenOptLevel RustOptLevel, bool UseSoftFloat,
+    bool FunctionSections,
+    bool DataSections,
+    bool TrapUnreachable,
+    bool Singlethread,
+    bool AsmComments,
+    bool EmitStackSizeSection,
+    bool RelaxELFRelocations,
+    bool UseInitArray) {
+
+  auto OptLevel = fromRust(RustOptLevel);
+  auto RM = fromRust(RustReloc);
+  auto CM = fromRust(RustCM);
+
+  std::string Error;
+  Triple Trip(Triple::normalize(TripleStr));
+  const llvm::Target *TheTarget =
+      TargetRegistry::lookupTarget(Trip.getTriple(), Error);
+  if (TheTarget == nullptr) {
+    LLVMRustSetLastError(Error.c_str());
+    return nullptr;
+  }
+
+  TargetOptions Options;
+
+  Options.FloatABIType = FloatABI::Default;
+  if (UseSoftFloat) {
+    Options.FloatABIType = FloatABI::Soft;
+  }
+  Options.DataSections = DataSections;
+  Options.FunctionSections = FunctionSections;
+  Options.MCOptions.AsmVerbose = AsmComments;
+  Options.MCOptions.PreserveAsmComments = AsmComments;
+  Options.MCOptions.ABIName = ABIStr;
+  Options.RelaxELFRelocations = RelaxELFRelocations;
+  Options.UseInitArray = UseInitArray;
+
+  if (TrapUnreachable) {
+    // Tell LLVM to codegen `unreachable` into an explicit trap instruction.
+    // This limits the extent of possible undefined behavior in some cases, as
+    // it prevents control flow from "falling through" into whatever code
+    // happens to be laid out next in memory.
+    Options.TrapUnreachable = true;
+  }
+
+  if (Singlethread) {
+    Options.ThreadModel = ThreadModel::Single;
+  }
+
+  Options.EmitStackSizeSection = EmitStackSizeSection;
+
+  TargetMachine *TM = TheTarget->createTargetMachine(
+      Trip.getTriple(), CPU, Feature, Options, RM, CM, OptLevel);
+  return wrap(TM);
+}
+
+extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) {
+  delete unwrap(TM);
+}
+
+extern "C" void LLVMRustConfigurePassManagerBuilder(
+    LLVMPassManagerBuilderRef PMBR, LLVMRustCodeGenOptLevel OptLevel,
+    bool MergeFunctions, bool SLPVectorize, bool LoopVectorize, bool PrepareForThinLTO,
+    const char* PGOGenPath, const char* PGOUsePath) {
+  unwrap(PMBR)->MergeFunctions = MergeFunctions;
+  unwrap(PMBR)->SLPVectorize = SLPVectorize;
+  unwrap(PMBR)->OptLevel = fromRust(OptLevel);
+  unwrap(PMBR)->LoopVectorize = LoopVectorize;
+  unwrap(PMBR)->PrepareForThinLTO = PrepareForThinLTO;
+
+  if (PGOGenPath) {
+    assert(!PGOUsePath);
+    unwrap(PMBR)->EnablePGOInstrGen = true;
+    unwrap(PMBR)->PGOInstrGen = PGOGenPath;
+  }
+  if (PGOUsePath) {
+    assert(!PGOGenPath);
+    unwrap(PMBR)->PGOInstrUse = PGOUsePath;
+  }
+}
+
+// Unfortunately, the LLVM C API doesn't provide a way to set the `LibraryInfo`
+// field of a PassManagerBuilder, we expose our own method of doing so.
+extern "C" void LLVMRustAddBuilderLibraryInfo(LLVMPassManagerBuilderRef PMBR,
+                                              LLVMModuleRef M,
+                                              bool DisableSimplifyLibCalls) {
+  Triple TargetTriple(unwrap(M)->getTargetTriple());
+  TargetLibraryInfoImpl *TLI = new TargetLibraryInfoImpl(TargetTriple);
+  if (DisableSimplifyLibCalls)
+    TLI->disableAllFunctions();
+  unwrap(PMBR)->LibraryInfo = TLI;
+}
+
+// Unfortunately, the LLVM C API doesn't provide a way to create the
+// TargetLibraryInfo pass, so we use this method to do so.
+extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M,
+                                       bool DisableSimplifyLibCalls) {
+  Triple TargetTriple(unwrap(M)->getTargetTriple());
+  TargetLibraryInfoImpl TLII(TargetTriple);
+  if (DisableSimplifyLibCalls)
+    TLII.disableAllFunctions();
+  unwrap(PMR)->add(new TargetLibraryInfoWrapperPass(TLII));
+}
+
+// Unfortunately, the LLVM C API doesn't provide an easy way of iterating over
+// all the functions in a module, so we do that manually here. You'll find
+// similar code in clang's BackendUtil.cpp file.
+extern "C" void LLVMRustRunFunctionPassManager(LLVMPassManagerRef PMR,
+                                               LLVMModuleRef M) {
+  llvm::legacy::FunctionPassManager *P =
+      unwrap<llvm::legacy::FunctionPassManager>(PMR);
+  P->doInitialization();
+
+  // Upgrade all calls to old intrinsics first.
+  for (Module::iterator I = unwrap(M)->begin(), E = unwrap(M)->end(); I != E;)
+    UpgradeCallsToIntrinsic(&*I++); // must be post-increment, as we remove
+
+  for (Module::iterator I = unwrap(M)->begin(), E = unwrap(M)->end(); I != E;
+       ++I)
+    if (!I->isDeclaration())
+      P->run(*I);
+
+  P->doFinalization();
+}
+
+extern "C" void LLVMRustSetLLVMOptions(int Argc, char **Argv) {
+  // Initializing the command-line options more than once is not allowed. So,
+  // check if they've already been initialized.  (This could happen if we're
+  // being called from rustpkg, for example). If the arguments change, then
+  // that's just kinda unfortunate.
+  static bool Initialized = false;
+  if (Initialized)
+    return;
+  Initialized = true;
+  cl::ParseCommandLineOptions(Argc, Argv);
+}
+
+enum class LLVMRustFileType {
+  AssemblyFile,
+  ObjectFile,
+};
+
+#if LLVM_VERSION_GE(10, 0)
+static CodeGenFileType fromRust(LLVMRustFileType Type) {
+  switch (Type) {
+  case LLVMRustFileType::AssemblyFile:
+    return CGFT_AssemblyFile;
+  case LLVMRustFileType::ObjectFile:
+    return CGFT_ObjectFile;
+  default:
+    report_fatal_error("Bad FileType.");
+  }
+}
+#else
+static TargetMachine::CodeGenFileType fromRust(LLVMRustFileType Type) {
+  switch (Type) {
+  case LLVMRustFileType::AssemblyFile:
+    return TargetMachine::CGFT_AssemblyFile;
+  case LLVMRustFileType::ObjectFile:
+    return TargetMachine::CGFT_ObjectFile;
+  default:
+    report_fatal_error("Bad FileType.");
+  }
+}
+#endif
+
+extern "C" LLVMRustResult
+LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR,
+                        LLVMModuleRef M, const char *Path,
+                        LLVMRustFileType RustFileType) {
+  llvm::legacy::PassManager *PM = unwrap<llvm::legacy::PassManager>(PMR);
+  auto FileType = fromRust(RustFileType);
+
+  std::string ErrorInfo;
+  std::error_code EC;
+  raw_fd_ostream OS(Path, EC, sys::fs::F_None);
+  if (EC)
+    ErrorInfo = EC.message();
+  if (ErrorInfo != "") {
+    LLVMRustSetLastError(ErrorInfo.c_str());
+    return LLVMRustResult::Failure;
+  }
+
+  buffer_ostream BOS(OS);
+  unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, false);
+  PM->run(*unwrap(M));
+
+  // Apparently `addPassesToEmitFile` adds a pointer to our on-the-stack output
+  // stream (OS), so the only real safe place to delete this is here? Don't we
+  // wish this was written in Rust?
+  LLVMDisposePassManager(PMR);
+  return LLVMRustResult::Success;
+}
+
+extern "C" typedef void (*LLVMRustSelfProfileBeforePassCallback)(void*, // LlvmSelfProfiler
+                                                      const char*,      // pass name
+                                                      const char*);     // IR name
+extern "C" typedef void (*LLVMRustSelfProfileAfterPassCallback)(void*); // LlvmSelfProfiler
+
+#if LLVM_VERSION_GE(9, 0)
+
+std::string LLVMRustwrappedIrGetName(const llvm::Any &WrappedIr) {
+  if (any_isa<const Module *>(WrappedIr))
+    return any_cast<const Module *>(WrappedIr)->getName().str();
+  if (any_isa<const Function *>(WrappedIr))
+    return any_cast<const Function *>(WrappedIr)->getName().str();
+  if (any_isa<const Loop *>(WrappedIr))
+    return any_cast<const Loop *>(WrappedIr)->getName().str();
+  if (any_isa<const LazyCallGraph::SCC *>(WrappedIr))
+    return any_cast<const LazyCallGraph::SCC *>(WrappedIr)->getName();
+  return "<UNKNOWN>";
+}
+
+
+void LLVMSelfProfileInitializeCallbacks(
+    PassInstrumentationCallbacks& PIC, void* LlvmSelfProfiler,
+    LLVMRustSelfProfileBeforePassCallback BeforePassCallback,
+    LLVMRustSelfProfileAfterPassCallback AfterPassCallback) {
+  PIC.registerBeforePassCallback([LlvmSelfProfiler, BeforePassCallback](
+                                     StringRef Pass, llvm::Any Ir) {
+    std::string PassName = Pass.str();
+    std::string IrName = LLVMRustwrappedIrGetName(Ir);
+    BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str());
+    return true;
+  });
+
+  PIC.registerAfterPassCallback(
+      [LlvmSelfProfiler, AfterPassCallback](StringRef Pass, llvm::Any Ir) {
+        AfterPassCallback(LlvmSelfProfiler);
+      });
+
+  PIC.registerAfterPassInvalidatedCallback(
+      [LlvmSelfProfiler, AfterPassCallback](StringRef Pass) {
+        AfterPassCallback(LlvmSelfProfiler);
+      });
+
+  PIC.registerBeforeAnalysisCallback([LlvmSelfProfiler, BeforePassCallback](
+                                         StringRef Pass, llvm::Any Ir) {
+    std::string PassName = Pass.str();
+    std::string IrName = LLVMRustwrappedIrGetName(Ir);
+    BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str());
+  });
+
+  PIC.registerAfterAnalysisCallback(
+      [LlvmSelfProfiler, AfterPassCallback](StringRef Pass, llvm::Any Ir) {
+        AfterPassCallback(LlvmSelfProfiler);
+      });
+}
+#endif
+
+enum class LLVMRustOptStage {
+  PreLinkNoLTO,
+  PreLinkThinLTO,
+  PreLinkFatLTO,
+  ThinLTO,
+  FatLTO,
+};
+
+struct LLVMRustSanitizerOptions {
+  bool SanitizeAddress;
+  bool SanitizeAddressRecover;
+  bool SanitizeMemory;
+  bool SanitizeMemoryRecover;
+  int  SanitizeMemoryTrackOrigins;
+  bool SanitizeThread;
+};
+
+extern "C" void
+LLVMRustOptimizeWithNewPassManager(
+    LLVMModuleRef ModuleRef,
+    LLVMTargetMachineRef TMRef,
+    LLVMRustPassBuilderOptLevel OptLevelRust,
+    LLVMRustOptStage OptStage,
+    bool NoPrepopulatePasses, bool VerifyIR, bool UseThinLTOBuffers,
+    bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, bool LoopVectorize,
+    bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers,
+    LLVMRustSanitizerOptions *SanitizerOptions,
+    const char *PGOGenPath, const char *PGOUsePath,
+    void* LlvmSelfProfiler,
+    LLVMRustSelfProfileBeforePassCallback BeforePassCallback,
+    LLVMRustSelfProfileAfterPassCallback AfterPassCallback) {
+#if LLVM_VERSION_GE(9, 0)
+  Module *TheModule = unwrap(ModuleRef);
+  TargetMachine *TM = unwrap(TMRef);
+  PassBuilder::OptimizationLevel OptLevel = fromRust(OptLevelRust);
+
+  // FIXME: MergeFunctions is not supported by NewPM yet.
+  (void) MergeFunctions;
+
+  PipelineTuningOptions PTO;
+  PTO.LoopUnrolling = UnrollLoops;
+  PTO.LoopInterleaving = UnrollLoops;
+  PTO.LoopVectorization = LoopVectorize;
+  PTO.SLPVectorization = SLPVectorize;
+
+  PassInstrumentationCallbacks PIC;
+  StandardInstrumentations SI;
+  SI.registerCallbacks(PIC);
+
+  if (LlvmSelfProfiler){
+    LLVMSelfProfileInitializeCallbacks(PIC,LlvmSelfProfiler,BeforePassCallback,AfterPassCallback);
+  }
+
+  Optional<PGOOptions> PGOOpt;
+  if (PGOGenPath) {
+    assert(!PGOUsePath);
+    PGOOpt = PGOOptions(PGOGenPath, "", "", PGOOptions::IRInstr);
+  } else if (PGOUsePath) {
+    assert(!PGOGenPath);
+    PGOOpt = PGOOptions(PGOUsePath, "", "", PGOOptions::IRUse);
+  }
+
+  PassBuilder PB(TM, PTO, PGOOpt, &PIC);
+
+  // FIXME: We may want to expose this as an option.
+  bool DebugPassManager = false;
+  LoopAnalysisManager LAM(DebugPassManager);
+  FunctionAnalysisManager FAM(DebugPassManager);
+  CGSCCAnalysisManager CGAM(DebugPassManager);
+  ModuleAnalysisManager MAM(DebugPassManager);
+
+  FAM.registerPass([&] { return PB.buildDefaultAAPipeline(); });
+
+  Triple TargetTriple(TheModule->getTargetTriple());
+  std::unique_ptr<TargetLibraryInfoImpl> TLII(new TargetLibraryInfoImpl(TargetTriple));
+  if (DisableSimplifyLibCalls)
+    TLII->disableAllFunctions();
+  FAM.registerPass([&] { return TargetLibraryAnalysis(*TLII); });
+
+  PB.registerModuleAnalyses(MAM);
+  PB.registerCGSCCAnalyses(CGAM);
+  PB.registerFunctionAnalyses(FAM);
+  PB.registerLoopAnalyses(LAM);
+  PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+
+  // We manually collect pipeline callbacks so we can apply them at O0, where the
+  // PassBuilder does not create a pipeline.
+  std::vector<std::function<void(ModulePassManager &)>> PipelineStartEPCallbacks;
+#if LLVM_VERSION_GE(11, 0)
+  std::vector<std::function<void(ModulePassManager &, PassBuilder::OptimizationLevel)>>
+      OptimizerLastEPCallbacks;
+#else
+  std::vector<std::function<void(FunctionPassManager &, PassBuilder::OptimizationLevel)>>
+      OptimizerLastEPCallbacks;
+#endif
+
+  if (VerifyIR) {
+    PipelineStartEPCallbacks.push_back([VerifyIR](ModulePassManager &MPM) {
+        MPM.addPass(VerifierPass());
+    });
+  }
+
+  if (SanitizerOptions) {
+    if (SanitizerOptions->SanitizeMemory) {
+      MemorySanitizerOptions Options(
+          SanitizerOptions->SanitizeMemoryTrackOrigins,
+          SanitizerOptions->SanitizeMemoryRecover,
+          /*CompileKernel=*/false);
+#if LLVM_VERSION_GE(11, 0)
+      OptimizerLastEPCallbacks.push_back(
+        [Options](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) {
+          MPM.addPass(MemorySanitizerPass(Options));
+          MPM.addPass(createModuleToFunctionPassAdaptor(MemorySanitizerPass(Options)));
+        }
+      );
+#else
+#if LLVM_VERSION_GE(10, 0)
+      PipelineStartEPCallbacks.push_back([Options](ModulePassManager &MPM) {
+        MPM.addPass(MemorySanitizerPass(Options));
+      });
+#endif
+      OptimizerLastEPCallbacks.push_back(
+        [Options](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) {
+          FPM.addPass(MemorySanitizerPass(Options));
+        }
+      );
+#endif
+    }
+
+    if (SanitizerOptions->SanitizeThread) {
+#if LLVM_VERSION_GE(11, 0)
+      OptimizerLastEPCallbacks.push_back(
+        [](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) {
+          MPM.addPass(ThreadSanitizerPass());
+          MPM.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass()));
+        }
+      );
+#else
+#if LLVM_VERSION_GE(10, 0)
+      PipelineStartEPCallbacks.push_back([](ModulePassManager &MPM) {
+        MPM.addPass(ThreadSanitizerPass());
+      });
+#endif
+      OptimizerLastEPCallbacks.push_back(
+        [](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) {
+          FPM.addPass(ThreadSanitizerPass());
+        }
+      );
+#endif
+    }
+
+    if (SanitizerOptions->SanitizeAddress) {
+#if LLVM_VERSION_GE(11, 0)
+      OptimizerLastEPCallbacks.push_back(
+        [SanitizerOptions](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) {
+          MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>());
+          MPM.addPass(ModuleAddressSanitizerPass(
+              /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover));
+          MPM.addPass(createModuleToFunctionPassAdaptor(AddressSanitizerPass(
+              /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover,
+              /*UseAfterScope=*/true)));
+        }
+      );
+#else
+      PipelineStartEPCallbacks.push_back([&](ModulePassManager &MPM) {
+        MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>());
+      });
+      OptimizerLastEPCallbacks.push_back(
+        [SanitizerOptions](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) {
+          FPM.addPass(AddressSanitizerPass(
+              /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover,
+              /*UseAfterScope=*/true));
+        }
+      );
+      PipelineStartEPCallbacks.push_back(
+        [SanitizerOptions](ModulePassManager &MPM) {
+          MPM.addPass(ModuleAddressSanitizerPass(
+              /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover));
+        }
+      );
+#endif
+    }
+  }
+
+  ModulePassManager MPM(DebugPassManager);
+  if (!NoPrepopulatePasses) {
+    if (OptLevel == PassBuilder::OptimizationLevel::O0) {
+      for (const auto &C : PipelineStartEPCallbacks)
+        C(MPM);
+
+#if LLVM_VERSION_GE(11, 0)
+      for (const auto &C : OptimizerLastEPCallbacks)
+        C(MPM, OptLevel);
+#else
+      if (!OptimizerLastEPCallbacks.empty()) {
+        FunctionPassManager FPM(DebugPassManager);
+        for (const auto &C : OptimizerLastEPCallbacks)
+          C(FPM, OptLevel);
+        MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
+      }
+#endif
+
+      MPM.addPass(AlwaysInlinerPass(EmitLifetimeMarkers));
+
+#if LLVM_VERSION_GE(10, 0)
+      if (PGOOpt) {
+        PB.addPGOInstrPassesForO0(
+            MPM, DebugPassManager, PGOOpt->Action == PGOOptions::IRInstr,
+            /*IsCS=*/false, PGOOpt->ProfileFile, PGOOpt->ProfileRemappingFile);
+      }
+#endif
+    } else {
+      for (const auto &C : PipelineStartEPCallbacks)
+        PB.registerPipelineStartEPCallback(C);
+      if (OptStage != LLVMRustOptStage::PreLinkThinLTO) {
+        for (const auto &C : OptimizerLastEPCallbacks)
+          PB.registerOptimizerLastEPCallback(C);
+      }
+
+      switch (OptStage) {
+      case LLVMRustOptStage::PreLinkNoLTO:
+        MPM = PB.buildPerModuleDefaultPipeline(OptLevel, DebugPassManager);
+        break;
+      case LLVMRustOptStage::PreLinkThinLTO:
+        MPM = PB.buildThinLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager);
+#if LLVM_VERSION_GE(11, 0)
+        for (const auto &C : OptimizerLastEPCallbacks)
+          C(MPM, OptLevel);
+#else
+        if (!OptimizerLastEPCallbacks.empty()) {
+          FunctionPassManager FPM(DebugPassManager);
+          for (const auto &C : OptimizerLastEPCallbacks)
+            C(FPM, OptLevel);
+          MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
+        }
+#endif
+        break;
+      case LLVMRustOptStage::PreLinkFatLTO:
+        MPM = PB.buildLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager);
+        break;
+      case LLVMRustOptStage::ThinLTO:
+        // FIXME: Does it make sense to pass the ModuleSummaryIndex?
+        // It only seems to be needed for C++ specific optimizations.
+        MPM = PB.buildThinLTODefaultPipeline(OptLevel, DebugPassManager, nullptr);
+        break;
+      case LLVMRustOptStage::FatLTO:
+        MPM = PB.buildLTODefaultPipeline(OptLevel, DebugPassManager, nullptr);
+        break;
+      }
+    }
+  }
+
+  if (UseThinLTOBuffers) {
+    MPM.addPass(CanonicalizeAliasesPass());
+    MPM.addPass(NameAnonGlobalPass());
+  }
+
+  // Upgrade all calls to old intrinsics first.
+  for (Module::iterator I = TheModule->begin(), E = TheModule->end(); I != E;)
+    UpgradeCallsToIntrinsic(&*I++); // must be post-increment, as we remove
+
+  MPM.run(*TheModule, MAM);
+#else
+  // The new pass manager has been available for a long time,
+  // but we don't bother supporting it on old LLVM versions.
+  report_fatal_error("New pass manager only supported since LLVM 9");
+#endif
+}
+
+// Callback to demangle function name
+// Parameters:
+// * name to be demangled
+// * name len
+// * output buffer
+// * output buffer len
+// Returns len of demangled string, or 0 if demangle failed.
+typedef size_t (*DemangleFn)(const char*, size_t, char*, size_t);
+
+
+namespace {
+
+class RustAssemblyAnnotationWriter : public AssemblyAnnotationWriter {
+  DemangleFn Demangle;
+  std::vector<char> Buf;
+
+public:
+  RustAssemblyAnnotationWriter(DemangleFn Demangle) : Demangle(Demangle) {}
+
+  // Return empty string if demangle failed
+  // or if name does not need to be demangled
+  StringRef CallDemangle(StringRef name) {
+    if (!Demangle) {
+      return StringRef();
+    }
+
+    if (Buf.size() < name.size() * 2) {
+      // Semangled name usually shorter than mangled,
+      // but allocate twice as much memory just in case
+      Buf.resize(name.size() * 2);
+    }
+
+    auto R = Demangle(name.data(), name.size(), Buf.data(), Buf.size());
+    if (!R) {
+      // Demangle failed.
+      return StringRef();
+    }
+
+    auto Demangled = StringRef(Buf.data(), R);
+    if (Demangled == name) {
+      // Do not print anything if demangled name is equal to mangled.
+      return StringRef();
+    }
+
+    return Demangled;
+  }
+
+  void emitFunctionAnnot(const Function *F,
+                         formatted_raw_ostream &OS) override {
+    StringRef Demangled = CallDemangle(F->getName());
+    if (Demangled.empty()) {
+        return;
+    }
+
+    OS << "; " << Demangled << "\n";
+  }
+
+  void emitInstructionAnnot(const Instruction *I,
+                            formatted_raw_ostream &OS) override {
+    const char *Name;
+    const Value *Value;
+    if (const CallInst *CI = dyn_cast<CallInst>(I)) {
+      Name = "call";
+      Value = CI->getCalledOperand();
+    } else if (const InvokeInst* II = dyn_cast<InvokeInst>(I)) {
+      Name = "invoke";
+      Value = II->getCalledOperand();
+    } else {
+      // Could demangle more operations, e. g.
+      // `store %place, @function`.
+      return;
+    }
+
+    if (!Value->hasName()) {
+      return;
+    }
+
+    StringRef Demangled = CallDemangle(Value->getName());
+    if (Demangled.empty()) {
+      return;
+    }
+
+    OS << "; " << Name << " " << Demangled << "\n";
+  }
+};
+
+} // namespace
+
+extern "C" LLVMRustResult
+LLVMRustPrintModule(LLVMModuleRef M, const char *Path, DemangleFn Demangle) {
+  std::string ErrorInfo;
+  std::error_code EC;
+  raw_fd_ostream OS(Path, EC, sys::fs::F_None);
+  if (EC)
+    ErrorInfo = EC.message();
+  if (ErrorInfo != "") {
+    LLVMRustSetLastError(ErrorInfo.c_str());
+    return LLVMRustResult::Failure;
+  }
+
+  RustAssemblyAnnotationWriter AAW(Demangle);
+  formatted_raw_ostream FOS(OS);
+  unwrap(M)->print(FOS, &AAW);
+
+  return LLVMRustResult::Success;
+}
+
+extern "C" void LLVMRustPrintPasses() {
+  LLVMInitializePasses();
+  struct MyListener : PassRegistrationListener {
+    void passEnumerate(const PassInfo *Info) {
+      StringRef PassArg = Info->getPassArgument();
+      StringRef PassName = Info->getPassName();
+      if (!PassArg.empty()) {
+        // These unsigned->signed casts could theoretically overflow, but
+        // realistically never will (and even if, the result is implementation
+        // defined rather plain UB).
+        printf("%15.*s - %.*s\n", (int)PassArg.size(), PassArg.data(),
+               (int)PassName.size(), PassName.data());
+      }
+    }
+  } Listener;
+
+  PassRegistry *PR = PassRegistry::getPassRegistry();
+  PR->enumerateWith(&Listener);
+}
+
+extern "C" void LLVMRustAddAlwaysInlinePass(LLVMPassManagerBuilderRef PMBR,
+                                            bool AddLifetimes) {
+  unwrap(PMBR)->Inliner = llvm::createAlwaysInlinerLegacyPass(AddLifetimes);
+}
+
+extern "C" void LLVMRustRunRestrictionPass(LLVMModuleRef M, char **Symbols,
+                                           size_t Len) {
+  llvm::legacy::PassManager passes;
+
+  auto PreserveFunctions = [=](const GlobalValue &GV) {
+    for (size_t I = 0; I < Len; I++) {
+      if (GV.getName() == Symbols[I]) {
+        return true;
+      }
+    }
+    return false;
+  };
+
+  passes.add(llvm::createInternalizePass(PreserveFunctions));
+
+  passes.run(*unwrap(M));
+}
+
+extern "C" void LLVMRustMarkAllFunctionsNounwind(LLVMModuleRef M) {
+  for (Module::iterator GV = unwrap(M)->begin(), E = unwrap(M)->end(); GV != E;
+       ++GV) {
+    GV->setDoesNotThrow();
+    Function *F = dyn_cast<Function>(GV);
+    if (F == nullptr)
+      continue;
+
+    for (Function::iterator B = F->begin(), BE = F->end(); B != BE; ++B) {
+      for (BasicBlock::iterator I = B->begin(), IE = B->end(); I != IE; ++I) {
+        if (isa<InvokeInst>(I)) {
+          InvokeInst *CI = cast<InvokeInst>(I);
+          CI->setDoesNotThrow();
+        }
+      }
+    }
+  }
+}
+
+extern "C" void
+LLVMRustSetDataLayoutFromTargetMachine(LLVMModuleRef Module,
+                                       LLVMTargetMachineRef TMR) {
+  TargetMachine *Target = unwrap(TMR);
+  unwrap(Module)->setDataLayout(Target->createDataLayout());
+}
+
+extern "C" void LLVMRustSetModulePICLevel(LLVMModuleRef M) {
+  unwrap(M)->setPICLevel(PICLevel::Level::BigPIC);
+}
+
+extern "C" void LLVMRustSetModulePIELevel(LLVMModuleRef M) {
+  unwrap(M)->setPIELevel(PIELevel::Level::Large);
+}
+
+// Here you'll find an implementation of ThinLTO as used by the Rust compiler
+// right now. This ThinLTO support is only enabled on "recent ish" versions of
+// LLVM, and otherwise it's just blanket rejected from other compilers.
+//
+// Most of this implementation is straight copied from LLVM. At the time of
+// this writing it wasn't *quite* suitable to reuse more code from upstream
+// for our purposes, but we should strive to upstream this support once it's
+// ready to go! I figure we may want a bit of testing locally first before
+// sending this upstream to LLVM. I hear though they're quite eager to receive
+// feedback like this!
+//
+// If you're reading this code and wondering "what in the world" or you're
+// working "good lord by LLVM upgrade is *still* failing due to these bindings"
+// then fear not! (ok maybe fear a little). All code here is mostly based
+// on `lib/LTO/ThinLTOCodeGenerator.cpp` in LLVM.
+//
+// You'll find that the general layout here roughly corresponds to the `run`
+// method in that file as well as `ProcessThinLTOModule`. Functions are
+// specifically commented below as well, but if you're updating this code
+// or otherwise trying to understand it, the LLVM source will be useful in
+// interpreting the mysteries within.
+//
+// Otherwise I'll apologize in advance, it probably requires a relatively
+// significant investment on your part to "truly understand" what's going on
+// here. Not saying I do myself, but it took me awhile staring at LLVM's source
+// and various online resources about ThinLTO to make heads or tails of all
+// this.
+
+// This is a shared data structure which *must* be threadsafe to share
+// read-only amongst threads. This also corresponds basically to the arguments
+// of the `ProcessThinLTOModule` function in the LLVM source.
+struct LLVMRustThinLTOData {
+  // The combined index that is the global analysis over all modules we're
+  // performing ThinLTO for. This is mostly managed by LLVM.
+  ModuleSummaryIndex Index;
+
+  // All modules we may look at, stored as in-memory serialized versions. This
+  // is later used when inlining to ensure we can extract any module to inline
+  // from.
+  StringMap<MemoryBufferRef> ModuleMap;
+
+  // A set that we manage of everything we *don't* want internalized. Note that
+  // this includes all transitive references right now as well, but it may not
+  // always!
+  DenseSet<GlobalValue::GUID> GUIDPreservedSymbols;
+
+  // Not 100% sure what these are, but they impact what's internalized and
+  // what's inlined across modules, I believe.
+  StringMap<FunctionImporter::ImportMapTy> ImportLists;
+  StringMap<FunctionImporter::ExportSetTy> ExportLists;
+  StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries;
+
+  LLVMRustThinLTOData() : Index(/* HaveGVs = */ false) {}
+};
+
+// Just an argument to the `LLVMRustCreateThinLTOData` function below.
+struct LLVMRustThinLTOModule {
+  const char *identifier;
+  const char *data;
+  size_t len;
+};
+
+// This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp`, not sure what it
+// does.
+static const GlobalValueSummary *
+getFirstDefinitionForLinker(const GlobalValueSummaryList &GVSummaryList) {
+  auto StrongDefForLinker = llvm::find_if(
+      GVSummaryList, [](const std::unique_ptr<GlobalValueSummary> &Summary) {
+        auto Linkage = Summary->linkage();
+        return !GlobalValue::isAvailableExternallyLinkage(Linkage) &&
+               !GlobalValue::isWeakForLinker(Linkage);
+      });
+  if (StrongDefForLinker != GVSummaryList.end())
+    return StrongDefForLinker->get();
+
+  auto FirstDefForLinker = llvm::find_if(
+      GVSummaryList, [](const std::unique_ptr<GlobalValueSummary> &Summary) {
+        auto Linkage = Summary->linkage();
+        return !GlobalValue::isAvailableExternallyLinkage(Linkage);
+      });
+  if (FirstDefForLinker == GVSummaryList.end())
+    return nullptr;
+  return FirstDefForLinker->get();
+}
+
+// The main entry point for creating the global ThinLTO analysis. The structure
+// here is basically the same as before threads are spawned in the `run`
+// function of `lib/LTO/ThinLTOCodeGenerator.cpp`.
+extern "C" LLVMRustThinLTOData*
+LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules,
+                          int num_modules,
+                          const char **preserved_symbols,
+                          int num_symbols) {
+#if LLVM_VERSION_GE(10, 0)
+  auto Ret = std::make_unique<LLVMRustThinLTOData>();
+#else
+  auto Ret = llvm::make_unique<LLVMRustThinLTOData>();
+#endif
+
+  // Load each module's summary and merge it into one combined index
+  for (int i = 0; i < num_modules; i++) {
+    auto module = &modules[i];
+    StringRef buffer(module->data, module->len);
+    MemoryBufferRef mem_buffer(buffer, module->identifier);
+
+    Ret->ModuleMap[module->identifier] = mem_buffer;
+
+    if (Error Err = readModuleSummaryIndex(mem_buffer, Ret->Index, i)) {
+      LLVMRustSetLastError(toString(std::move(Err)).c_str());
+      return nullptr;
+    }
+  }
+
+  // Collect for each module the list of function it defines (GUID -> Summary)
+  Ret->Index.collectDefinedGVSummariesPerModule(Ret->ModuleToDefinedGVSummaries);
+
+  // Convert the preserved symbols set from string to GUID, this is then needed
+  // for internalization.
+  for (int i = 0; i < num_symbols; i++) {
+    auto GUID = GlobalValue::getGUID(preserved_symbols[i]);
+    Ret->GUIDPreservedSymbols.insert(GUID);
+  }
+
+  // Collect the import/export lists for all modules from the call-graph in the
+  // combined index
+  //
+  // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp`
+  auto deadIsPrevailing = [&](GlobalValue::GUID G) {
+    return PrevailingType::Unknown;
+  };
+  // We don't have a complete picture in our use of ThinLTO, just our immediate
+  // crate, so we need `ImportEnabled = false` to limit internalization.
+  // Otherwise, we sometimes lose `static` values -- see #60184.
+  computeDeadSymbolsWithConstProp(Ret->Index, Ret->GUIDPreservedSymbols,
+                                  deadIsPrevailing, /* ImportEnabled = */ false);
+  ComputeCrossModuleImport(
+    Ret->Index,
+    Ret->ModuleToDefinedGVSummaries,
+    Ret->ImportLists,
+    Ret->ExportLists
+  );
+
+  // Resolve LinkOnce/Weak symbols, this has to be computed early be cause it
+  // impacts the caching.
+  //
+  // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp` with some of this
+  // being lifted from `lib/LTO/LTO.cpp` as well
+  StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>> ResolvedODR;
+  DenseMap<GlobalValue::GUID, const GlobalValueSummary *> PrevailingCopy;
+  for (auto &I : Ret->Index) {
+    if (I.second.SummaryList.size() > 1)
+      PrevailingCopy[I.first] = getFirstDefinitionForLinker(I.second.SummaryList);
+  }
+  auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) {
+    const auto &Prevailing = PrevailingCopy.find(GUID);
+    if (Prevailing == PrevailingCopy.end())
+      return true;
+    return Prevailing->second == S;
+  };
+  auto recordNewLinkage = [&](StringRef ModuleIdentifier,
+                              GlobalValue::GUID GUID,
+                              GlobalValue::LinkageTypes NewLinkage) {
+    ResolvedODR[ModuleIdentifier][GUID] = NewLinkage;
+  };
+#if LLVM_VERSION_GE(9, 0)
+  thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage,
+                                  Ret->GUIDPreservedSymbols);
+#else
+  thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage);
+#endif
+
+  // Here we calculate an `ExportedGUIDs` set for use in the `isExported`
+  // callback below. This callback below will dictate the linkage for all
+  // summaries in the index, and we basically just only want to ensure that dead
+  // symbols are internalized. Otherwise everything that's already external
+  // linkage will stay as external, and internal will stay as internal.
+  std::set<GlobalValue::GUID> ExportedGUIDs;
+  for (auto &List : Ret->Index) {
+    for (auto &GVS: List.second.SummaryList) {
+      if (GlobalValue::isLocalLinkage(GVS->linkage()))
+        continue;
+      auto GUID = GVS->getOriginalName();
+      if (GVS->flags().Live)
+        ExportedGUIDs.insert(GUID);
+    }
+  }
+#if LLVM_VERSION_GE(10, 0)
+  auto isExported = [&](StringRef ModuleIdentifier, ValueInfo VI) {
+    const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier);
+    return (ExportList != Ret->ExportLists.end() &&
+      ExportList->second.count(VI)) ||
+      ExportedGUIDs.count(VI.getGUID());
+  };
+  thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported, isPrevailing);
+#else
+  auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) {
+    const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier);
+    return (ExportList != Ret->ExportLists.end() &&
+      ExportList->second.count(GUID)) ||
+      ExportedGUIDs.count(GUID);
+  };
+  thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported);
+#endif
+
+  return Ret.release();
+}
+
+extern "C" void
+LLVMRustFreeThinLTOData(LLVMRustThinLTOData *Data) {
+  delete Data;
+}
+
+// Below are the various passes that happen *per module* when doing ThinLTO.
+//
+// In other words, these are the functions that are all run concurrently
+// with one another, one per module. The passes here correspond to the analysis
+// passes in `lib/LTO/ThinLTOCodeGenerator.cpp`, currently found in the
+// `ProcessThinLTOModule` function. Here they're split up into separate steps
+// so rustc can save off the intermediate bytecode between each step.
+
+#if LLVM_VERSION_GE(11, 0)
+static bool
+clearDSOLocalOnDeclarations(Module &Mod, TargetMachine &TM) {
+  // When linking an ELF shared object, dso_local should be dropped. We
+  // conservatively do this for -fpic.
+  bool ClearDSOLocalOnDeclarations =
+      TM.getTargetTriple().isOSBinFormatELF() &&
+      TM.getRelocationModel() != Reloc::Static &&
+      Mod.getPIELevel() == PIELevel::Default;
+  return ClearDSOLocalOnDeclarations;
+}
+#endif
+
+extern "C" bool
+LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data, LLVMModuleRef M,
+                             LLVMTargetMachineRef TM) {
+  Module &Mod = *unwrap(M);
+  TargetMachine &Target = *unwrap(TM);
+
+#if LLVM_VERSION_GE(11, 0)
+  bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target);
+  bool error = renameModuleForThinLTO(Mod, Data->Index, ClearDSOLocal);
+#else
+  bool error = renameModuleForThinLTO(Mod, Data->Index);
+#endif
+
+  if (error) {
+    LLVMRustSetLastError("renameModuleForThinLTO failed");
+    return false;
+  }
+  return true;
+}
+
+extern "C" bool
+LLVMRustPrepareThinLTOResolveWeak(const LLVMRustThinLTOData *Data, LLVMModuleRef M) {
+  Module &Mod = *unwrap(M);
+  const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier());
+  thinLTOResolvePrevailingInModule(Mod, DefinedGlobals);
+  return true;
+}
+
+extern "C" bool
+LLVMRustPrepareThinLTOInternalize(const LLVMRustThinLTOData *Data, LLVMModuleRef M) {
+  Module &Mod = *unwrap(M);
+  const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier());
+  thinLTOInternalizeModule(Mod, DefinedGlobals);
+  return true;
+}
+
+extern "C" bool
+LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M,
+                             LLVMTargetMachineRef TM) {
+  Module &Mod = *unwrap(M);
+  TargetMachine &Target = *unwrap(TM);
+
+  const auto &ImportList = Data->ImportLists.lookup(Mod.getModuleIdentifier());
+  auto Loader = [&](StringRef Identifier) {
+    const auto &Memory = Data->ModuleMap.lookup(Identifier);
+    auto &Context = Mod.getContext();
+    auto MOrErr = getLazyBitcodeModule(Memory, Context, true, true);
+
+    if (!MOrErr)
+      return MOrErr;
+
+    // The rest of this closure is a workaround for
+    // https://bugs.llvm.org/show_bug.cgi?id=38184 where during ThinLTO imports
+    // we accidentally import wasm custom sections into different modules,
+    // duplicating them by in the final output artifact.
+    //
+    // The issue is worked around here by manually removing the
+    // `wasm.custom_sections` named metadata node from any imported module. This
+    // we know isn't used by any optimization pass so there's no need for it to
+    // be imported.
+    //
+    // Note that the metadata is currently lazily loaded, so we materialize it
+    // here before looking up if there's metadata inside. The `FunctionImporter`
+    // will immediately materialize metadata anyway after an import, so this
+    // shouldn't be a perf hit.
+    if (Error Err = (*MOrErr)->materializeMetadata()) {
+      Expected<std::unique_ptr<Module>> Ret(std::move(Err));
+      return Ret;
+    }
+
+    auto *WasmCustomSections = (*MOrErr)->getNamedMetadata("wasm.custom_sections");
+    if (WasmCustomSections)
+      WasmCustomSections->eraseFromParent();
+
+    return MOrErr;
+  };
+#if LLVM_VERSION_GE(11, 0)
+  bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target);
+  FunctionImporter Importer(Data->Index, Loader, ClearDSOLocal);
+#else
+  FunctionImporter Importer(Data->Index, Loader);
+#endif
+  Expected<bool> Result = Importer.importFunctions(Mod, ImportList);
+  if (!Result) {
+    LLVMRustSetLastError(toString(Result.takeError()).c_str());
+    return false;
+  }
+  return true;
+}
+
+extern "C" typedef void (*LLVMRustModuleNameCallback)(void*, // payload
+                                                      const char*, // importing module name
+                                                      const char*); // imported module name
+
+// Calls `module_name_callback` for each module import done by ThinLTO.
+// The callback is provided with regular null-terminated C strings.
+extern "C" void
+LLVMRustGetThinLTOModuleImports(const LLVMRustThinLTOData *data,
+                                LLVMRustModuleNameCallback module_name_callback,
+                                void* callback_payload) {
+  for (const auto& importing_module : data->ImportLists) {
+    const std::string importing_module_id = importing_module.getKey().str();
+    const auto& imports = importing_module.getValue();
+    for (const auto& imported_module : imports) {
+      const std::string imported_module_id = imported_module.getKey().str();
+      module_name_callback(callback_payload,
+                           importing_module_id.c_str(),
+                           imported_module_id.c_str());
+    }
+  }
+}
+
+// This struct and various functions are sort of a hack right now, but the
+// problem is that we've got in-memory LLVM modules after we generate and
+// optimize all codegen-units for one compilation in rustc. To be compatible
+// with the LTO support above we need to serialize the modules plus their
+// ThinLTO summary into memory.
+//
+// This structure is basically an owned version of a serialize module, with
+// a ThinLTO summary attached.
+struct LLVMRustThinLTOBuffer {
+  std::string data;
+};
+
+extern "C" LLVMRustThinLTOBuffer*
+LLVMRustThinLTOBufferCreate(LLVMModuleRef M) {
+#if LLVM_VERSION_GE(10, 0)
+  auto Ret = std::make_unique<LLVMRustThinLTOBuffer>();
+#else
+  auto Ret = llvm::make_unique<LLVMRustThinLTOBuffer>();
+#endif
+  {
+    raw_string_ostream OS(Ret->data);
+    {
+      legacy::PassManager PM;
+      PM.add(createWriteThinLTOBitcodePass(OS));
+      PM.run(*unwrap(M));
+    }
+  }
+  return Ret.release();
+}
+
+extern "C" void
+LLVMRustThinLTOBufferFree(LLVMRustThinLTOBuffer *Buffer) {
+  delete Buffer;
+}
+
+extern "C" const void*
+LLVMRustThinLTOBufferPtr(const LLVMRustThinLTOBuffer *Buffer) {
+  return Buffer->data.data();
+}
+
+extern "C" size_t
+LLVMRustThinLTOBufferLen(const LLVMRustThinLTOBuffer *Buffer) {
+  return Buffer->data.length();
+}
+
+// This is what we used to parse upstream bitcode for actual ThinLTO
+// processing.  We'll call this once per module optimized through ThinLTO, and
+// it'll be called concurrently on many threads.
+extern "C" LLVMModuleRef
+LLVMRustParseBitcodeForLTO(LLVMContextRef Context,
+                           const char *data,
+                           size_t len,
+                           const char *identifier) {
+  StringRef Data(data, len);
+  MemoryBufferRef Buffer(Data, identifier);
+  unwrap(Context)->enableDebugTypeODRUniquing();
+  Expected<std::unique_ptr<Module>> SrcOrError =
+      parseBitcodeFile(Buffer, *unwrap(Context));
+  if (!SrcOrError) {
+    LLVMRustSetLastError(toString(SrcOrError.takeError()).c_str());
+    return nullptr;
+  }
+  return wrap(std::move(*SrcOrError).release());
+}
+
+// Find the bitcode section in the object file data and return it as a slice.
+// Fail if the bitcode section is present but empty.
+//
+// On success, the return value is the pointer to the start of the slice and
+// `out_len` is filled with the (non-zero) length. On failure, the return value
+// is `nullptr` and `out_len` is set to zero.
+extern "C" const char*
+LLVMRustGetBitcodeSliceFromObjectData(const char *data,
+                                      size_t len,
+                                      size_t *out_len) {
+  *out_len = 0;
+
+  StringRef Data(data, len);
+  MemoryBufferRef Buffer(Data, ""); // The id is unused.
+
+  Expected<MemoryBufferRef> BitcodeOrError =
+    object::IRObjectFile::findBitcodeInMemBuffer(Buffer);
+  if (!BitcodeOrError) {
+    LLVMRustSetLastError(toString(BitcodeOrError.takeError()).c_str());
+    return nullptr;
+  }
+
+  *out_len = BitcodeOrError->getBufferSize();
+  return BitcodeOrError->getBufferStart();
+}
+
+// Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See
+// the comment in `back/lto.rs` for why this exists.
+extern "C" void
+LLVMRustThinLTOGetDICompileUnit(LLVMModuleRef Mod,
+                                DICompileUnit **A,
+                                DICompileUnit **B) {
+  Module *M = unwrap(Mod);
+  DICompileUnit **Cur = A;
+  DICompileUnit **Next = B;
+  for (DICompileUnit *CU : M->debug_compile_units()) {
+    *Cur = CU;
+    Cur = Next;
+    Next = nullptr;
+    if (Cur == nullptr)
+      break;
+  }
+}
+
+// Rewrite all `DICompileUnit` pointers to the `DICompileUnit` specified. See
+// the comment in `back/lto.rs` for why this exists.
+extern "C" void
+LLVMRustThinLTOPatchDICompileUnit(LLVMModuleRef Mod, DICompileUnit *Unit) {
+  Module *M = unwrap(Mod);
+
+  // If the original source module didn't have a `DICompileUnit` then try to
+  // merge all the existing compile units. If there aren't actually any though
+  // then there's not much for us to do so return.
+  if (Unit == nullptr) {
+    for (DICompileUnit *CU : M->debug_compile_units()) {
+      Unit = CU;
+      break;
+    }
+    if (Unit == nullptr)
+      return;
+  }
+
+  // Use LLVM's built-in `DebugInfoFinder` to find a bunch of debuginfo and
+  // process it recursively. Note that we used to specifically iterate over
+  // instructions to ensure we feed everything into it, but `processModule`
+  // started doing this the same way in LLVM 7 (commit d769eb36ab2b8).
+  DebugInfoFinder Finder;
+  Finder.processModule(*M);
+
+  // After we've found all our debuginfo, rewrite all subprograms to point to
+  // the same `DICompileUnit`.
+  for (auto &F : Finder.subprograms()) {
+    F->replaceUnit(Unit);
+  }
+
+  // Erase any other references to other `DICompileUnit` instances, the verifier
+  // will later ensure that we don't actually have any other stale references to
+  // worry about.
+  auto *MD = M->getNamedMetadata("llvm.dbg.cu");
+  MD->clearOperands();
+  MD->addOperand(Unit);
+}
diff --git a/compiler/rustc_llvm/llvm-wrapper/README b/compiler/rustc_llvm/llvm-wrapper/README
new file mode 100644
index 00000000000..e1c6dd07d2b
--- /dev/null
+++ b/compiler/rustc_llvm/llvm-wrapper/README
@@ -0,0 +1,16 @@
+This directory currently contains some LLVM support code. This will generally
+be sent upstream to LLVM in time; for now it lives here.
+
+NOTE: the LLVM C++ ABI is subject to between-version breakage and must *never*
+be exposed to Rust. To allow for easy auditing of that, all Rust-exposed types
+must be typedef-ed as "LLVMXyz", or "LLVMRustXyz" if they were defined here.
+
+Functions that return a failure status and leave the error in
+the LLVM last error should return an LLVMRustResult rather than an
+int or anything to avoid confusion.
+
+When translating enums, add a single `Other` variant as the first
+one to allow for new variants to be added. It should abort when used
+as an input.
+
+All other types must not be typedef-ed as such.
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
new file mode 100644
index 00000000000..e85a9b76380
--- /dev/null
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -0,0 +1,1721 @@
+#include "LLVMWrapper.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Bitcode/BitcodeWriterPass.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/ADT/Optional.h"
+
+#include <iostream>
+
+//===----------------------------------------------------------------------===
+//
+// This file defines alternate interfaces to core functions that are more
+// readily callable by Rust's FFI.
+//
+//===----------------------------------------------------------------------===
+
+using namespace llvm;
+using namespace llvm::sys;
+using namespace llvm::object;
+
+// LLVMAtomicOrdering is already an enum - don't create another
+// one.
+static AtomicOrdering fromRust(LLVMAtomicOrdering Ordering) {
+  switch (Ordering) {
+  case LLVMAtomicOrderingNotAtomic:
+    return AtomicOrdering::NotAtomic;
+  case LLVMAtomicOrderingUnordered:
+    return AtomicOrdering::Unordered;
+  case LLVMAtomicOrderingMonotonic:
+    return AtomicOrdering::Monotonic;
+  case LLVMAtomicOrderingAcquire:
+    return AtomicOrdering::Acquire;
+  case LLVMAtomicOrderingRelease:
+    return AtomicOrdering::Release;
+  case LLVMAtomicOrderingAcquireRelease:
+    return AtomicOrdering::AcquireRelease;
+  case LLVMAtomicOrderingSequentiallyConsistent:
+    return AtomicOrdering::SequentiallyConsistent;
+  }
+
+  report_fatal_error("Invalid LLVMAtomicOrdering value!");
+}
+
+static LLVM_THREAD_LOCAL char *LastError;
+
+// Custom error handler for fatal LLVM errors.
+//
+// Notably it exits the process with code 101, unlike LLVM's default of 1.
+static void FatalErrorHandler(void *UserData,
+                              const std::string& Reason,
+                              bool GenCrashDiag) {
+  // Do the same thing that the default error handler does.
+  std::cerr << "LLVM ERROR: " << Reason << std::endl;
+
+  // Since this error handler exits the process, we have to run any cleanup that
+  // LLVM would run after handling the error. This might change with an LLVM
+  // upgrade.
+  sys::RunInterruptHandlers();
+
+  exit(101);
+}
+
+extern "C" void LLVMRustInstallFatalErrorHandler() {
+  install_fatal_error_handler(FatalErrorHandler);
+}
+
+extern "C" LLVMMemoryBufferRef
+LLVMRustCreateMemoryBufferWithContentsOfFile(const char *Path) {
+  ErrorOr<std::unique_ptr<MemoryBuffer>> BufOr =
+      MemoryBuffer::getFile(Path, -1, false);
+  if (!BufOr) {
+    LLVMRustSetLastError(BufOr.getError().message().c_str());
+    return nullptr;
+  }
+  return wrap(BufOr.get().release());
+}
+
+extern "C" char *LLVMRustGetLastError(void) {
+  char *Ret = LastError;
+  LastError = nullptr;
+  return Ret;
+}
+
+extern "C" unsigned int LLVMRustGetInstructionCount(LLVMModuleRef M) {
+  return unwrap(M)->getInstructionCount();
+}
+
+extern "C" void LLVMRustSetLastError(const char *Err) {
+  free((void *)LastError);
+  LastError = strdup(Err);
+}
+
+extern "C" LLVMContextRef LLVMRustContextCreate(bool shouldDiscardNames) {
+  auto ctx = new LLVMContext();
+  ctx->setDiscardValueNames(shouldDiscardNames);
+  return wrap(ctx);
+}
+
+extern "C" void LLVMRustSetNormalizedTarget(LLVMModuleRef M,
+                                            const char *Triple) {
+  unwrap(M)->setTargetTriple(Triple::normalize(Triple));
+}
+
+extern "C" void LLVMRustPrintPassTimings() {
+  raw_fd_ostream OS(2, false); // stderr.
+  TimerGroup::printAll(OS);
+}
+
+extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name,
+                                              size_t NameLen) {
+  return wrap(unwrap(M)->getNamedValue(StringRef(Name, NameLen)));
+}
+
+extern "C" LLVMValueRef LLVMRustGetOrInsertFunction(LLVMModuleRef M,
+                                                    const char *Name,
+                                                    size_t NameLen,
+                                                    LLVMTypeRef FunctionTy) {
+  return wrap(unwrap(M)
+                  ->getOrInsertFunction(StringRef(Name, NameLen),
+                                        unwrap<FunctionType>(FunctionTy))
+#if LLVM_VERSION_GE(9, 0)
+                  .getCallee()
+#endif
+  );
+}
+
+extern "C" LLVMValueRef
+LLVMRustGetOrInsertGlobal(LLVMModuleRef M, const char *Name, size_t NameLen, LLVMTypeRef Ty) {
+  StringRef NameRef(Name, NameLen);
+  return wrap(unwrap(M)->getOrInsertGlobal(NameRef, unwrap(Ty)));
+}
+
+extern "C" LLVMValueRef
+LLVMRustInsertPrivateGlobal(LLVMModuleRef M, LLVMTypeRef Ty) {
+  return wrap(new GlobalVariable(*unwrap(M),
+                                 unwrap(Ty),
+                                 false,
+                                 GlobalValue::PrivateLinkage,
+                                 nullptr));
+}
+
+extern "C" LLVMTypeRef LLVMRustMetadataTypeInContext(LLVMContextRef C) {
+  return wrap(Type::getMetadataTy(*unwrap(C)));
+}
+
+static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
+  switch (Kind) {
+  case AlwaysInline:
+    return Attribute::AlwaysInline;
+  case ByVal:
+    return Attribute::ByVal;
+  case Cold:
+    return Attribute::Cold;
+  case InlineHint:
+    return Attribute::InlineHint;
+  case MinSize:
+    return Attribute::MinSize;
+  case Naked:
+    return Attribute::Naked;
+  case NoAlias:
+    return Attribute::NoAlias;
+  case NoCapture:
+    return Attribute::NoCapture;
+  case NoInline:
+    return Attribute::NoInline;
+  case NonNull:
+    return Attribute::NonNull;
+  case NoRedZone:
+    return Attribute::NoRedZone;
+  case NoReturn:
+    return Attribute::NoReturn;
+  case NoUnwind:
+    return Attribute::NoUnwind;
+  case OptimizeForSize:
+    return Attribute::OptimizeForSize;
+  case ReadOnly:
+    return Attribute::ReadOnly;
+  case SExt:
+    return Attribute::SExt;
+  case StructRet:
+    return Attribute::StructRet;
+  case UWTable:
+    return Attribute::UWTable;
+  case ZExt:
+    return Attribute::ZExt;
+  case InReg:
+    return Attribute::InReg;
+  case SanitizeThread:
+    return Attribute::SanitizeThread;
+  case SanitizeAddress:
+    return Attribute::SanitizeAddress;
+  case SanitizeMemory:
+    return Attribute::SanitizeMemory;
+  case NonLazyBind:
+    return Attribute::NonLazyBind;
+  case OptimizeNone:
+    return Attribute::OptimizeNone;
+  case ReturnsTwice:
+    return Attribute::ReturnsTwice;
+  case ReadNone:
+    return Attribute::ReadNone;
+  case InaccessibleMemOnly:
+    return Attribute::InaccessibleMemOnly;
+  }
+  report_fatal_error("bad AttributeKind");
+}
+
+extern "C" void LLVMRustAddCallSiteAttribute(LLVMValueRef Instr, unsigned Index,
+                                             LLVMRustAttribute RustAttr) {
+  CallBase *Call = unwrap<CallBase>(Instr);
+  Attribute Attr = Attribute::get(Call->getContext(), fromRust(RustAttr));
+  Call->addAttribute(Index, Attr);
+}
+
+extern "C" void LLVMRustAddAlignmentCallSiteAttr(LLVMValueRef Instr,
+                                                 unsigned Index,
+                                                 uint32_t Bytes) {
+  CallBase *Call = unwrap<CallBase>(Instr);
+  AttrBuilder B;
+  B.addAlignmentAttr(Bytes);
+  Call->setAttributes(Call->getAttributes().addAttributes(
+      Call->getContext(), Index, B));
+}
+
+extern "C" void LLVMRustAddDereferenceableCallSiteAttr(LLVMValueRef Instr,
+                                                       unsigned Index,
+                                                       uint64_t Bytes) {
+  CallBase *Call = unwrap<CallBase>(Instr);
+  AttrBuilder B;
+  B.addDereferenceableAttr(Bytes);
+  Call->setAttributes(Call->getAttributes().addAttributes(
+      Call->getContext(), Index, B));
+}
+
+extern "C" void LLVMRustAddDereferenceableOrNullCallSiteAttr(LLVMValueRef Instr,
+                                                             unsigned Index,
+                                                             uint64_t Bytes) {
+  CallBase *Call = unwrap<CallBase>(Instr);
+  AttrBuilder B;
+  B.addDereferenceableOrNullAttr(Bytes);
+  Call->setAttributes(Call->getAttributes().addAttributes(
+      Call->getContext(), Index, B));
+}
+
+extern "C" void LLVMRustAddByValCallSiteAttr(LLVMValueRef Instr, unsigned Index,
+                                             LLVMTypeRef Ty) {
+  CallBase *Call = unwrap<CallBase>(Instr);
+#if LLVM_VERSION_GE(9, 0)
+  Attribute Attr = Attribute::getWithByValType(Call->getContext(), unwrap(Ty));
+#else
+  Attribute Attr = Attribute::get(Call->getContext(), Attribute::ByVal);
+#endif
+  Call->addAttribute(Index, Attr);
+}
+
+extern "C" void LLVMRustAddFunctionAttribute(LLVMValueRef Fn, unsigned Index,
+                                             LLVMRustAttribute RustAttr) {
+  Function *A = unwrap<Function>(Fn);
+  Attribute Attr = Attribute::get(A->getContext(), fromRust(RustAttr));
+  AttrBuilder B(Attr);
+  A->addAttributes(Index, B);
+}
+
+extern "C" void LLVMRustAddAlignmentAttr(LLVMValueRef Fn,
+                                         unsigned Index,
+                                         uint32_t Bytes) {
+  Function *A = unwrap<Function>(Fn);
+  AttrBuilder B;
+  B.addAlignmentAttr(Bytes);
+  A->addAttributes(Index, B);
+}
+
+extern "C" void LLVMRustAddDereferenceableAttr(LLVMValueRef Fn, unsigned Index,
+                                               uint64_t Bytes) {
+  Function *A = unwrap<Function>(Fn);
+  AttrBuilder B;
+  B.addDereferenceableAttr(Bytes);
+  A->addAttributes(Index, B);
+}
+
+extern "C" void LLVMRustAddDereferenceableOrNullAttr(LLVMValueRef Fn,
+                                                     unsigned Index,
+                                                     uint64_t Bytes) {
+  Function *A = unwrap<Function>(Fn);
+  AttrBuilder B;
+  B.addDereferenceableOrNullAttr(Bytes);
+  A->addAttributes(Index, B);
+}
+
+extern "C" void LLVMRustAddByValAttr(LLVMValueRef Fn, unsigned Index,
+                                     LLVMTypeRef Ty) {
+  Function *F = unwrap<Function>(Fn);
+#if LLVM_VERSION_GE(9, 0)
+  Attribute Attr = Attribute::getWithByValType(F->getContext(), unwrap(Ty));
+#else
+  Attribute Attr = Attribute::get(F->getContext(), Attribute::ByVal);
+#endif
+  F->addAttribute(Index, Attr);
+}
+
+extern "C" void LLVMRustAddFunctionAttrStringValue(LLVMValueRef Fn,
+                                                   unsigned Index,
+                                                   const char *Name,
+                                                   const char *Value) {
+  Function *F = unwrap<Function>(Fn);
+  AttrBuilder B;
+  B.addAttribute(Name, Value);
+  F->addAttributes(Index, B);
+}
+
+extern "C" void LLVMRustRemoveFunctionAttributes(LLVMValueRef Fn,
+                                                 unsigned Index,
+                                                 LLVMRustAttribute RustAttr) {
+  Function *F = unwrap<Function>(Fn);
+  Attribute Attr = Attribute::get(F->getContext(), fromRust(RustAttr));
+  AttrBuilder B(Attr);
+  auto PAL = F->getAttributes();
+  auto PALNew = PAL.removeAttributes(F->getContext(), Index, B);
+  F->setAttributes(PALNew);
+}
+
+// enable fpmath flag UnsafeAlgebra
+extern "C" void LLVMRustSetHasUnsafeAlgebra(LLVMValueRef V) {
+  if (auto I = dyn_cast<Instruction>(unwrap<Value>(V))) {
+    I->setFast(true);
+  }
+}
+
+extern "C" LLVMValueRef
+LLVMRustBuildAtomicLoad(LLVMBuilderRef B, LLVMValueRef Source, const char *Name,
+                        LLVMAtomicOrdering Order) {
+  Value *Ptr = unwrap(Source);
+  Type *Ty = Ptr->getType()->getPointerElementType();
+  LoadInst *LI = unwrap(B)->CreateLoad(Ty, Ptr, Name);
+  LI->setAtomic(fromRust(Order));
+  return wrap(LI);
+}
+
+extern "C" LLVMValueRef LLVMRustBuildAtomicStore(LLVMBuilderRef B,
+                                                 LLVMValueRef V,
+                                                 LLVMValueRef Target,
+                                                 LLVMAtomicOrdering Order) {
+  StoreInst *SI = unwrap(B)->CreateStore(unwrap(V), unwrap(Target));
+  SI->setAtomic(fromRust(Order));
+  return wrap(SI);
+}
+
+// FIXME: Use the C-API LLVMBuildAtomicCmpXchg and LLVMSetWeak
+// once we raise our minimum support to LLVM 10.
+extern "C" LLVMValueRef
+LLVMRustBuildAtomicCmpXchg(LLVMBuilderRef B, LLVMValueRef Target,
+                           LLVMValueRef Old, LLVMValueRef Source,
+                           LLVMAtomicOrdering Order,
+                           LLVMAtomicOrdering FailureOrder, LLVMBool Weak) {
+  AtomicCmpXchgInst *ACXI = unwrap(B)->CreateAtomicCmpXchg(
+      unwrap(Target), unwrap(Old), unwrap(Source), fromRust(Order),
+      fromRust(FailureOrder));
+  ACXI->setWeak(Weak);
+  return wrap(ACXI);
+}
+
+enum class LLVMRustSynchronizationScope {
+  SingleThread,
+  CrossThread,
+};
+
+static SyncScope::ID fromRust(LLVMRustSynchronizationScope Scope) {
+  switch (Scope) {
+  case LLVMRustSynchronizationScope::SingleThread:
+    return SyncScope::SingleThread;
+  case LLVMRustSynchronizationScope::CrossThread:
+    return SyncScope::System;
+  default:
+    report_fatal_error("bad SynchronizationScope.");
+  }
+}
+
+extern "C" LLVMValueRef
+LLVMRustBuildAtomicFence(LLVMBuilderRef B, LLVMAtomicOrdering Order,
+                         LLVMRustSynchronizationScope Scope) {
+  return wrap(unwrap(B)->CreateFence(fromRust(Order), fromRust(Scope)));
+}
+
+enum class LLVMRustAsmDialect {
+  Att,
+  Intel,
+};
+
+static InlineAsm::AsmDialect fromRust(LLVMRustAsmDialect Dialect) {
+  switch (Dialect) {
+  case LLVMRustAsmDialect::Att:
+    return InlineAsm::AD_ATT;
+  case LLVMRustAsmDialect::Intel:
+    return InlineAsm::AD_Intel;
+  default:
+    report_fatal_error("bad AsmDialect.");
+  }
+}
+
+extern "C" LLVMValueRef
+LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen,
+                  char *Constraints, size_t ConstraintsLen,
+                  LLVMBool HasSideEffects, LLVMBool IsAlignStack,
+                  LLVMRustAsmDialect Dialect) {
+  return wrap(InlineAsm::get(unwrap<FunctionType>(Ty),
+                             StringRef(AsmString, AsmStringLen),
+                             StringRef(Constraints, ConstraintsLen),
+                             HasSideEffects, IsAlignStack, fromRust(Dialect)));
+}
+
+extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints,
+                                        size_t ConstraintsLen) {
+  return InlineAsm::Verify(unwrap<FunctionType>(Ty),
+                           StringRef(Constraints, ConstraintsLen));
+}
+
+extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm,
+                                              size_t AsmLen) {
+  unwrap(M)->appendModuleInlineAsm(StringRef(Asm, AsmLen));
+}
+
+typedef DIBuilder *LLVMRustDIBuilderRef;
+
+template <typename DIT> DIT *unwrapDIPtr(LLVMMetadataRef Ref) {
+  return (DIT *)(Ref ? unwrap<MDNode>(Ref) : nullptr);
+}
+
+#define DIDescriptor DIScope
+#define DIArray DINodeArray
+#define unwrapDI unwrapDIPtr
+
+// These values **must** match debuginfo::DIFlags! They also *happen*
+// to match LLVM, but that isn't required as we do giant sets of
+// matching below. The value shouldn't be directly passed to LLVM.
+enum class LLVMRustDIFlags : uint32_t {
+  FlagZero = 0,
+  FlagPrivate = 1,
+  FlagProtected = 2,
+  FlagPublic = 3,
+  FlagFwdDecl = (1 << 2),
+  FlagAppleBlock = (1 << 3),
+  FlagBlockByrefStruct = (1 << 4),
+  FlagVirtual = (1 << 5),
+  FlagArtificial = (1 << 6),
+  FlagExplicit = (1 << 7),
+  FlagPrototyped = (1 << 8),
+  FlagObjcClassComplete = (1 << 9),
+  FlagObjectPointer = (1 << 10),
+  FlagVector = (1 << 11),
+  FlagStaticMember = (1 << 12),
+  FlagLValueReference = (1 << 13),
+  FlagRValueReference = (1 << 14),
+  FlagExternalTypeRef = (1 << 15),
+  FlagIntroducedVirtual = (1 << 18),
+  FlagBitField = (1 << 19),
+  FlagNoReturn = (1 << 20),
+  // Do not add values that are not supported by the minimum LLVM
+  // version we support! see llvm/include/llvm/IR/DebugInfoFlags.def
+};
+
+inline LLVMRustDIFlags operator&(LLVMRustDIFlags A, LLVMRustDIFlags B) {
+  return static_cast<LLVMRustDIFlags>(static_cast<uint32_t>(A) &
+                                      static_cast<uint32_t>(B));
+}
+
+inline LLVMRustDIFlags operator|(LLVMRustDIFlags A, LLVMRustDIFlags B) {
+  return static_cast<LLVMRustDIFlags>(static_cast<uint32_t>(A) |
+                                      static_cast<uint32_t>(B));
+}
+
+inline LLVMRustDIFlags &operator|=(LLVMRustDIFlags &A, LLVMRustDIFlags B) {
+  return A = A | B;
+}
+
+inline bool isSet(LLVMRustDIFlags F) { return F != LLVMRustDIFlags::FlagZero; }
+
+inline LLVMRustDIFlags visibility(LLVMRustDIFlags F) {
+  return static_cast<LLVMRustDIFlags>(static_cast<uint32_t>(F) & 0x3);
+}
+
+static DINode::DIFlags fromRust(LLVMRustDIFlags Flags) {
+  DINode::DIFlags Result = DINode::DIFlags::FlagZero;
+
+  switch (visibility(Flags)) {
+  case LLVMRustDIFlags::FlagPrivate:
+    Result |= DINode::DIFlags::FlagPrivate;
+    break;
+  case LLVMRustDIFlags::FlagProtected:
+    Result |= DINode::DIFlags::FlagProtected;
+    break;
+  case LLVMRustDIFlags::FlagPublic:
+    Result |= DINode::DIFlags::FlagPublic;
+    break;
+  default:
+    // The rest are handled below
+    break;
+  }
+
+  if (isSet(Flags & LLVMRustDIFlags::FlagFwdDecl)) {
+    Result |= DINode::DIFlags::FlagFwdDecl;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagAppleBlock)) {
+    Result |= DINode::DIFlags::FlagAppleBlock;
+  }
+#if LLVM_VERSION_LT(10, 0)
+  if (isSet(Flags & LLVMRustDIFlags::FlagBlockByrefStruct)) {
+    Result |= DINode::DIFlags::FlagBlockByrefStruct;
+  }
+#endif
+  if (isSet(Flags & LLVMRustDIFlags::FlagVirtual)) {
+    Result |= DINode::DIFlags::FlagVirtual;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagArtificial)) {
+    Result |= DINode::DIFlags::FlagArtificial;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagExplicit)) {
+    Result |= DINode::DIFlags::FlagExplicit;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagPrototyped)) {
+    Result |= DINode::DIFlags::FlagPrototyped;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagObjcClassComplete)) {
+    Result |= DINode::DIFlags::FlagObjcClassComplete;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagObjectPointer)) {
+    Result |= DINode::DIFlags::FlagObjectPointer;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagVector)) {
+    Result |= DINode::DIFlags::FlagVector;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagStaticMember)) {
+    Result |= DINode::DIFlags::FlagStaticMember;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagLValueReference)) {
+    Result |= DINode::DIFlags::FlagLValueReference;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagRValueReference)) {
+    Result |= DINode::DIFlags::FlagRValueReference;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagIntroducedVirtual)) {
+    Result |= DINode::DIFlags::FlagIntroducedVirtual;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagBitField)) {
+    Result |= DINode::DIFlags::FlagBitField;
+  }
+  if (isSet(Flags & LLVMRustDIFlags::FlagNoReturn)) {
+    Result |= DINode::DIFlags::FlagNoReturn;
+  }
+
+  return Result;
+}
+
+// These values **must** match debuginfo::DISPFlags! They also *happen*
+// to match LLVM, but that isn't required as we do giant sets of
+// matching below. The value shouldn't be directly passed to LLVM.
+enum class LLVMRustDISPFlags : uint32_t {
+  SPFlagZero = 0,
+  SPFlagVirtual = 1,
+  SPFlagPureVirtual = 2,
+  SPFlagLocalToUnit = (1 << 2),
+  SPFlagDefinition = (1 << 3),
+  SPFlagOptimized = (1 << 4),
+  SPFlagMainSubprogram = (1 << 5),
+  // Do not add values that are not supported by the minimum LLVM
+  // version we support! see llvm/include/llvm/IR/DebugInfoFlags.def
+  // (In LLVM < 8, createFunction supported these as separate bool arguments.)
+};
+
+inline LLVMRustDISPFlags operator&(LLVMRustDISPFlags A, LLVMRustDISPFlags B) {
+  return static_cast<LLVMRustDISPFlags>(static_cast<uint32_t>(A) &
+                                      static_cast<uint32_t>(B));
+}
+
+inline LLVMRustDISPFlags operator|(LLVMRustDISPFlags A, LLVMRustDISPFlags B) {
+  return static_cast<LLVMRustDISPFlags>(static_cast<uint32_t>(A) |
+                                      static_cast<uint32_t>(B));
+}
+
+inline LLVMRustDISPFlags &operator|=(LLVMRustDISPFlags &A, LLVMRustDISPFlags B) {
+  return A = A | B;
+}
+
+inline bool isSet(LLVMRustDISPFlags F) { return F != LLVMRustDISPFlags::SPFlagZero; }
+
+inline LLVMRustDISPFlags virtuality(LLVMRustDISPFlags F) {
+  return static_cast<LLVMRustDISPFlags>(static_cast<uint32_t>(F) & 0x3);
+}
+
+static DISubprogram::DISPFlags fromRust(LLVMRustDISPFlags SPFlags) {
+  DISubprogram::DISPFlags Result = DISubprogram::DISPFlags::SPFlagZero;
+
+  switch (virtuality(SPFlags)) {
+  case LLVMRustDISPFlags::SPFlagVirtual:
+    Result |= DISubprogram::DISPFlags::SPFlagVirtual;
+    break;
+  case LLVMRustDISPFlags::SPFlagPureVirtual:
+    Result |= DISubprogram::DISPFlags::SPFlagPureVirtual;
+    break;
+  default:
+    // The rest are handled below
+    break;
+  }
+
+  if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagLocalToUnit)) {
+    Result |= DISubprogram::DISPFlags::SPFlagLocalToUnit;
+  }
+  if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagDefinition)) {
+    Result |= DISubprogram::DISPFlags::SPFlagDefinition;
+  }
+  if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagOptimized)) {
+    Result |= DISubprogram::DISPFlags::SPFlagOptimized;
+  }
+#if LLVM_VERSION_GE(9, 0)
+  if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagMainSubprogram)) {
+    Result |= DISubprogram::DISPFlags::SPFlagMainSubprogram;
+  }
+#endif
+
+  return Result;
+}
+
+enum class LLVMRustDebugEmissionKind {
+  NoDebug,
+  FullDebug,
+  LineTablesOnly,
+};
+
+static DICompileUnit::DebugEmissionKind fromRust(LLVMRustDebugEmissionKind Kind) {
+  switch (Kind) {
+  case LLVMRustDebugEmissionKind::NoDebug:
+    return DICompileUnit::DebugEmissionKind::NoDebug;
+  case LLVMRustDebugEmissionKind::FullDebug:
+    return DICompileUnit::DebugEmissionKind::FullDebug;
+  case LLVMRustDebugEmissionKind::LineTablesOnly:
+    return DICompileUnit::DebugEmissionKind::LineTablesOnly;
+  default:
+    report_fatal_error("bad DebugEmissionKind.");
+  }
+}
+
+enum class LLVMRustChecksumKind {
+  None,
+  MD5,
+  SHA1,
+};
+
+static Optional<DIFile::ChecksumKind> fromRust(LLVMRustChecksumKind Kind) {
+  switch (Kind) {
+  case LLVMRustChecksumKind::None:
+    return None;
+  case LLVMRustChecksumKind::MD5:
+    return DIFile::ChecksumKind::CSK_MD5;
+  case LLVMRustChecksumKind::SHA1:
+    return DIFile::ChecksumKind::CSK_SHA1;
+  default:
+    report_fatal_error("bad ChecksumKind.");
+  }
+}
+
+extern "C" uint32_t LLVMRustDebugMetadataVersion() {
+  return DEBUG_METADATA_VERSION;
+}
+
+extern "C" uint32_t LLVMRustVersionMinor() { return LLVM_VERSION_MINOR; }
+
+extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; }
+
+extern "C" void LLVMRustAddModuleFlag(LLVMModuleRef M, const char *Name,
+                                      uint32_t Value) {
+  unwrap(M)->addModuleFlag(Module::Warning, Name, Value);
+}
+
+extern "C" LLVMValueRef LLVMRustMetadataAsValue(LLVMContextRef C, LLVMMetadataRef MD) {
+  return wrap(MetadataAsValue::get(*unwrap(C), unwrap(MD)));
+}
+
+extern "C" LLVMRustDIBuilderRef LLVMRustDIBuilderCreate(LLVMModuleRef M) {
+  return new DIBuilder(*unwrap(M));
+}
+
+extern "C" void LLVMRustDIBuilderDispose(LLVMRustDIBuilderRef Builder) {
+  delete Builder;
+}
+
+extern "C" void LLVMRustDIBuilderFinalize(LLVMRustDIBuilderRef Builder) {
+  Builder->finalize();
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit(
+    LLVMRustDIBuilderRef Builder, unsigned Lang, LLVMMetadataRef FileRef,
+    const char *Producer, size_t ProducerLen, bool isOptimized,
+    const char *Flags, unsigned RuntimeVer,
+    const char *SplitName, size_t SplitNameLen,
+    LLVMRustDebugEmissionKind Kind) {
+  auto *File = unwrapDI<DIFile>(FileRef);
+
+  return wrap(Builder->createCompileUnit(Lang, File, StringRef(Producer, ProducerLen),
+                                         isOptimized, Flags, RuntimeVer,
+                                         StringRef(SplitName, SplitNameLen),
+                                         fromRust(Kind)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFile(
+    LLVMRustDIBuilderRef Builder,
+    const char *Filename, size_t FilenameLen,
+    const char *Directory, size_t DirectoryLen, LLVMRustChecksumKind CSKind,
+    const char *Checksum, size_t ChecksumLen) {
+  Optional<DIFile::ChecksumKind> llvmCSKind = fromRust(CSKind);
+  Optional<DIFile::ChecksumInfo<StringRef>> CSInfo{};
+  if (llvmCSKind)
+    CSInfo.emplace(*llvmCSKind, StringRef{Checksum, ChecksumLen});
+  return wrap(Builder->createFile(StringRef(Filename, FilenameLen),
+                                  StringRef(Directory, DirectoryLen),
+                                  CSInfo));
+}
+
+extern "C" LLVMMetadataRef
+LLVMRustDIBuilderCreateSubroutineType(LLVMRustDIBuilderRef Builder,
+                                      LLVMMetadataRef ParameterTypes) {
+  return wrap(Builder->createSubroutineType(
+      DITypeRefArray(unwrap<MDTuple>(ParameterTypes))));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen,
+    const char *LinkageName, size_t LinkageNameLen,
+    LLVMMetadataRef File, unsigned LineNo,
+    LLVMMetadataRef Ty, unsigned ScopeLine, LLVMRustDIFlags Flags,
+    LLVMRustDISPFlags SPFlags, LLVMValueRef Fn, LLVMMetadataRef TParam,
+    LLVMMetadataRef Decl) {
+  DITemplateParameterArray TParams =
+      DITemplateParameterArray(unwrap<MDTuple>(TParam));
+  DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags);
+  DINode::DIFlags llvmFlags = fromRust(Flags);
+#if LLVM_VERSION_LT(9, 0)
+  if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagMainSubprogram))
+    llvmFlags |= DINode::DIFlags::FlagMainSubprogram;
+#endif
+  DISubprogram *Sub = Builder->createFunction(
+      unwrapDI<DIScope>(Scope),
+      StringRef(Name, NameLen),
+      StringRef(LinkageName, LinkageNameLen),
+      unwrapDI<DIFile>(File), LineNo,
+      unwrapDI<DISubroutineType>(Ty), ScopeLine, llvmFlags,
+      llvmSPFlags, TParams, unwrapDIPtr<DISubprogram>(Decl));
+  unwrap<Function>(Fn)->setSubprogram(Sub);
+  return wrap(Sub);
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateBasicType(
+    LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen,
+    uint64_t SizeInBits, unsigned Encoding) {
+  return wrap(Builder->createBasicType(StringRef(Name, NameLen), SizeInBits, Encoding));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTypedef(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Type, const char *Name, size_t NameLen,
+    LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Scope) {
+  return wrap(Builder->createTypedef(
+    unwrap<DIType>(Type), StringRef(Name, NameLen), unwrap<DIFile>(File),
+    LineNo, unwrap<DIScope>(Scope)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreatePointerType(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef PointeeTy,
+    uint64_t SizeInBits, uint32_t AlignInBits, unsigned AddressSpace,
+    const char *Name, size_t NameLen) {
+  return wrap(Builder->createPointerType(unwrapDI<DIType>(PointeeTy),
+                                         SizeInBits, AlignInBits,
+                                         AddressSpace,
+                                         StringRef(Name, NameLen)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen,
+    LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits,
+    uint32_t AlignInBits, LLVMRustDIFlags Flags,
+    LLVMMetadataRef DerivedFrom, LLVMMetadataRef Elements,
+    unsigned RunTimeLang, LLVMMetadataRef VTableHolder,
+    const char *UniqueId, size_t UniqueIdLen) {
+  return wrap(Builder->createStructType(
+      unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen),
+      unwrapDI<DIFile>(File), LineNumber,
+      SizeInBits, AlignInBits, fromRust(Flags), unwrapDI<DIType>(DerivedFrom),
+      DINodeArray(unwrapDI<MDTuple>(Elements)), RunTimeLang,
+      unwrapDI<DIType>(VTableHolder), StringRef(UniqueId, UniqueIdLen)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen,
+    LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits,
+    uint32_t AlignInBits, LLVMRustDIFlags Flags, LLVMMetadataRef Discriminator,
+    LLVMMetadataRef Elements, const char *UniqueId, size_t UniqueIdLen) {
+  return wrap(Builder->createVariantPart(
+      unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen),
+      unwrapDI<DIFile>(File), LineNumber,
+      SizeInBits, AlignInBits, fromRust(Flags), unwrapDI<DIDerivedType>(Discriminator),
+      DINodeArray(unwrapDI<MDTuple>(Elements)), StringRef(UniqueId, UniqueIdLen)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMemberType(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen,
+    LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits,
+    uint32_t AlignInBits, uint64_t OffsetInBits, LLVMRustDIFlags Flags,
+    LLVMMetadataRef Ty) {
+  return wrap(Builder->createMemberType(unwrapDI<DIDescriptor>(Scope),
+                                        StringRef(Name, NameLen),
+                                        unwrapDI<DIFile>(File), LineNo,
+                                        SizeInBits, AlignInBits, OffsetInBits,
+                                        fromRust(Flags), unwrapDI<DIType>(Ty)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo,
+    uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits, LLVMValueRef Discriminant,
+    LLVMRustDIFlags Flags, LLVMMetadataRef Ty) {
+  llvm::ConstantInt* D = nullptr;
+  if (Discriminant) {
+    D = unwrap<llvm::ConstantInt>(Discriminant);
+  }
+  return wrap(Builder->createVariantMemberType(unwrapDI<DIDescriptor>(Scope),
+                                               StringRef(Name, NameLen),
+                                               unwrapDI<DIFile>(File), LineNo,
+                                               SizeInBits, AlignInBits, OffsetInBits, D,
+                                               fromRust(Flags), unwrapDI<DIType>(Ty)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateLexicalBlock(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    LLVMMetadataRef File, unsigned Line, unsigned Col) {
+  return wrap(Builder->createLexicalBlock(unwrapDI<DIDescriptor>(Scope),
+                                          unwrapDI<DIFile>(File), Line, Col));
+}
+
+extern "C" LLVMMetadataRef
+LLVMRustDIBuilderCreateLexicalBlockFile(LLVMRustDIBuilderRef Builder,
+                                        LLVMMetadataRef Scope,
+                                        LLVMMetadataRef File) {
+  return wrap(Builder->createLexicalBlockFile(unwrapDI<DIDescriptor>(Scope),
+                                              unwrapDI<DIFile>(File)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Context,
+    const char *Name, size_t NameLen,
+    const char *LinkageName, size_t LinkageNameLen,
+    LLVMMetadataRef File, unsigned LineNo,
+    LLVMMetadataRef Ty, bool IsLocalToUnit, LLVMValueRef V,
+    LLVMMetadataRef Decl = nullptr, uint32_t AlignInBits = 0) {
+  llvm::GlobalVariable *InitVal = cast<llvm::GlobalVariable>(unwrap(V));
+
+  llvm::DIExpression *InitExpr = nullptr;
+  if (llvm::ConstantInt *IntVal = llvm::dyn_cast<llvm::ConstantInt>(InitVal)) {
+    InitExpr = Builder->createConstantValueExpression(
+        IntVal->getValue().getSExtValue());
+  } else if (llvm::ConstantFP *FPVal =
+                 llvm::dyn_cast<llvm::ConstantFP>(InitVal)) {
+    InitExpr = Builder->createConstantValueExpression(
+        FPVal->getValueAPF().bitcastToAPInt().getZExtValue());
+  }
+
+  llvm::DIGlobalVariableExpression *VarExpr = Builder->createGlobalVariableExpression(
+      unwrapDI<DIDescriptor>(Context), StringRef(Name, NameLen),
+      StringRef(LinkageName, LinkageNameLen),
+      unwrapDI<DIFile>(File), LineNo, unwrapDI<DIType>(Ty), IsLocalToUnit,
+#if LLVM_VERSION_GE(10, 0)
+      /* isDefined */ true,
+#endif
+      InitExpr, unwrapDIPtr<MDNode>(Decl),
+      /* templateParams */ nullptr,
+      AlignInBits);
+
+  InitVal->setMetadata("dbg", VarExpr);
+
+  return wrap(VarExpr);
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable(
+    LLVMRustDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen,
+    LLVMMetadataRef File, unsigned LineNo,
+    LLVMMetadataRef Ty, bool AlwaysPreserve, LLVMRustDIFlags Flags,
+    unsigned ArgNo, uint32_t AlignInBits) {
+  if (Tag == 0x100) { // DW_TAG_auto_variable
+    return wrap(Builder->createAutoVariable(
+        unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen),
+        unwrapDI<DIFile>(File), LineNo,
+        unwrapDI<DIType>(Ty), AlwaysPreserve, fromRust(Flags), AlignInBits));
+  } else {
+    return wrap(Builder->createParameterVariable(
+        unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), ArgNo,
+        unwrapDI<DIFile>(File), LineNo,
+        unwrapDI<DIType>(Ty), AlwaysPreserve, fromRust(Flags)));
+  }
+}
+
+extern "C" LLVMMetadataRef
+LLVMRustDIBuilderCreateArrayType(LLVMRustDIBuilderRef Builder, uint64_t Size,
+                                 uint32_t AlignInBits, LLVMMetadataRef Ty,
+                                 LLVMMetadataRef Subscripts) {
+  return wrap(
+      Builder->createArrayType(Size, AlignInBits, unwrapDI<DIType>(Ty),
+                               DINodeArray(unwrapDI<MDTuple>(Subscripts))));
+}
+
+extern "C" LLVMMetadataRef
+LLVMRustDIBuilderGetOrCreateSubrange(LLVMRustDIBuilderRef Builder, int64_t Lo,
+                                     int64_t Count) {
+  return wrap(Builder->getOrCreateSubrange(Lo, Count));
+}
+
+extern "C" LLVMMetadataRef
+LLVMRustDIBuilderGetOrCreateArray(LLVMRustDIBuilderRef Builder,
+                                  LLVMMetadataRef *Ptr, unsigned Count) {
+  Metadata **DataValue = unwrap(Ptr);
+  return wrap(
+      Builder->getOrCreateArray(ArrayRef<Metadata *>(DataValue, Count)).get());
+}
+
+extern "C" LLVMValueRef LLVMRustDIBuilderInsertDeclareAtEnd(
+    LLVMRustDIBuilderRef Builder, LLVMValueRef V, LLVMMetadataRef VarInfo,
+    int64_t *AddrOps, unsigned AddrOpsCount, LLVMValueRef DL,
+    LLVMBasicBlockRef InsertAtEnd) {
+  return wrap(Builder->insertDeclare(
+      unwrap(V), unwrap<DILocalVariable>(VarInfo),
+      Builder->createExpression(llvm::ArrayRef<int64_t>(AddrOps, AddrOpsCount)),
+      DebugLoc(cast<MDNode>(unwrap<MetadataAsValue>(DL)->getMetadata())),
+      unwrap(InsertAtEnd)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerator(
+    LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen,
+    int64_t Value, bool IsUnsigned) {
+  return wrap(Builder->createEnumerator(StringRef(Name, NameLen), Value, IsUnsigned));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen,
+    LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits,
+    uint32_t AlignInBits, LLVMMetadataRef Elements,
+    LLVMMetadataRef ClassTy, bool IsScoped) {
+  return wrap(Builder->createEnumerationType(
+      unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen),
+      unwrapDI<DIFile>(File), LineNumber,
+      SizeInBits, AlignInBits, DINodeArray(unwrapDI<MDTuple>(Elements)),
+      unwrapDI<DIType>(ClassTy), "", IsScoped));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen,
+    LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits,
+    uint32_t AlignInBits, LLVMRustDIFlags Flags, LLVMMetadataRef Elements,
+    unsigned RunTimeLang, const char *UniqueId, size_t UniqueIdLen) {
+  return wrap(Builder->createUnionType(
+      unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), unwrapDI<DIFile>(File),
+      LineNumber, SizeInBits, AlignInBits, fromRust(Flags),
+      DINodeArray(unwrapDI<MDTuple>(Elements)), RunTimeLang,
+      StringRef(UniqueId, UniqueIdLen)));
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTemplateTypeParameter(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen, LLVMMetadataRef Ty) {
+#if LLVM_VERSION_GE(11, 0)
+  bool IsDefault = false; // FIXME: should we ever set this true?
+  return wrap(Builder->createTemplateTypeParameter(
+      unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), unwrapDI<DIType>(Ty), IsDefault));
+#else
+  return wrap(Builder->createTemplateTypeParameter(
+      unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), unwrapDI<DIType>(Ty)));
+#endif
+}
+
+extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateNameSpace(
+    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
+    const char *Name, size_t NameLen, bool ExportSymbols) {
+  return wrap(Builder->createNameSpace(
+      unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), ExportSymbols
+  ));
+}
+
+extern "C" void
+LLVMRustDICompositeTypeReplaceArrays(LLVMRustDIBuilderRef Builder,
+                                     LLVMMetadataRef CompositeTy,
+                                     LLVMMetadataRef Elements,
+                                     LLVMMetadataRef Params) {
+  DICompositeType *Tmp = unwrapDI<DICompositeType>(CompositeTy);
+  Builder->replaceArrays(Tmp, DINodeArray(unwrap<MDTuple>(Elements)),
+                         DINodeArray(unwrap<MDTuple>(Params)));
+}
+
+extern "C" LLVMValueRef
+LLVMRustDIBuilderCreateDebugLocation(LLVMContextRef ContextRef, unsigned Line,
+                                     unsigned Column, LLVMMetadataRef Scope,
+                                     LLVMMetadataRef InlinedAt) {
+  LLVMContext &Context = *unwrap(ContextRef);
+
+  DebugLoc debug_loc = DebugLoc::get(Line, Column, unwrapDIPtr<MDNode>(Scope),
+                                     unwrapDIPtr<MDNode>(InlinedAt));
+
+  return wrap(MetadataAsValue::get(Context, debug_loc.getAsMDNode()));
+}
+
+extern "C" int64_t LLVMRustDIBuilderCreateOpDeref() {
+  return dwarf::DW_OP_deref;
+}
+
+extern "C" int64_t LLVMRustDIBuilderCreateOpPlusUconst() {
+  return dwarf::DW_OP_plus_uconst;
+}
+
+extern "C" void LLVMRustWriteTypeToString(LLVMTypeRef Ty, RustStringRef Str) {
+  RawRustStringOstream OS(Str);
+  unwrap<llvm::Type>(Ty)->print(OS);
+}
+
+extern "C" void LLVMRustWriteValueToString(LLVMValueRef V,
+                                           RustStringRef Str) {
+  RawRustStringOstream OS(Str);
+  if (!V) {
+    OS << "(null)";
+  } else {
+    OS << "(";
+    unwrap<llvm::Value>(V)->getType()->print(OS);
+    OS << ":";
+    unwrap<llvm::Value>(V)->print(OS);
+    OS << ")";
+  }
+}
+
+// Note that the two following functions look quite similar to the
+// LLVMGetSectionName function. Sadly, it appears that this function only
+// returns a char* pointer, which isn't guaranteed to be null-terminated. The
+// function provided by LLVM doesn't return the length, so we've created our own
+// function which returns the length as well as the data pointer.
+//
+// For an example of this not returning a null terminated string, see
+// lib/Object/COFFObjectFile.cpp in the getSectionName function. One of the
+// branches explicitly creates a StringRef without a null terminator, and then
+// that's returned.
+
+inline section_iterator *unwrap(LLVMSectionIteratorRef SI) {
+  return reinterpret_cast<section_iterator *>(SI);
+}
+
+extern "C" size_t LLVMRustGetSectionName(LLVMSectionIteratorRef SI,
+                                         const char **Ptr) {
+#if LLVM_VERSION_GE(10, 0)
+  auto NameOrErr = (*unwrap(SI))->getName();
+  if (!NameOrErr)
+    report_fatal_error(NameOrErr.takeError());
+  *Ptr = NameOrErr->data();
+  return NameOrErr->size();
+#else
+  StringRef Ret;
+  if (std::error_code EC = (*unwrap(SI))->getName(Ret))
+    report_fatal_error(EC.message());
+  *Ptr = Ret.data();
+  return Ret.size();
+#endif
+}
+
+// LLVMArrayType function does not support 64-bit ElementCount
+extern "C" LLVMTypeRef LLVMRustArrayType(LLVMTypeRef ElementTy,
+                                         uint64_t ElementCount) {
+  return wrap(ArrayType::get(unwrap(ElementTy), ElementCount));
+}
+
+DEFINE_SIMPLE_CONVERSION_FUNCTIONS(Twine, LLVMTwineRef)
+
+extern "C" void LLVMRustWriteTwineToString(LLVMTwineRef T, RustStringRef Str) {
+  RawRustStringOstream OS(Str);
+  unwrap(T)->print(OS);
+}
+
+extern "C" void LLVMRustUnpackOptimizationDiagnostic(
+    LLVMDiagnosticInfoRef DI, RustStringRef PassNameOut,
+    LLVMValueRef *FunctionOut, unsigned* Line, unsigned* Column,
+    RustStringRef FilenameOut, RustStringRef MessageOut) {
+  // Undefined to call this not on an optimization diagnostic!
+  llvm::DiagnosticInfoOptimizationBase *Opt =
+      static_cast<llvm::DiagnosticInfoOptimizationBase *>(unwrap(DI));
+
+  RawRustStringOstream PassNameOS(PassNameOut);
+  PassNameOS << Opt->getPassName();
+  *FunctionOut = wrap(&Opt->getFunction());
+
+  RawRustStringOstream FilenameOS(FilenameOut);
+  DiagnosticLocation loc = Opt->getLocation();
+  if (loc.isValid()) {
+    *Line = loc.getLine();
+    *Column = loc.getColumn();
+    FilenameOS << loc.getAbsolutePath();
+  }
+
+  RawRustStringOstream MessageOS(MessageOut);
+  MessageOS << Opt->getMsg();
+}
+
+enum class LLVMRustDiagnosticLevel {
+    Error,
+    Warning,
+    Note,
+    Remark,
+};
+
+extern "C" void
+LLVMRustUnpackInlineAsmDiagnostic(LLVMDiagnosticInfoRef DI,
+                                  LLVMRustDiagnosticLevel *LevelOut,
+                                  unsigned *CookieOut,
+                                  LLVMTwineRef *MessageOut,
+                                  LLVMValueRef *InstructionOut) {
+  // Undefined to call this not on an inline assembly diagnostic!
+  llvm::DiagnosticInfoInlineAsm *IA =
+      static_cast<llvm::DiagnosticInfoInlineAsm *>(unwrap(DI));
+
+  *CookieOut = IA->getLocCookie();
+  *MessageOut = wrap(&IA->getMsgStr());
+  *InstructionOut = wrap(IA->getInstruction());
+
+  switch (IA->getSeverity()) {
+    case DS_Error:
+      *LevelOut = LLVMRustDiagnosticLevel::Error;
+      break;
+    case DS_Warning:
+      *LevelOut = LLVMRustDiagnosticLevel::Warning;
+      break;
+    case DS_Note:
+      *LevelOut = LLVMRustDiagnosticLevel::Note;
+      break;
+    case DS_Remark:
+      *LevelOut = LLVMRustDiagnosticLevel::Remark;
+      break;
+    default:
+      report_fatal_error("Invalid LLVMRustDiagnosticLevel value!");
+  }
+}
+
+extern "C" void LLVMRustWriteDiagnosticInfoToString(LLVMDiagnosticInfoRef DI,
+                                                    RustStringRef Str) {
+  RawRustStringOstream OS(Str);
+  DiagnosticPrinterRawOStream DP(OS);
+  unwrap(DI)->print(DP);
+}
+
+enum class LLVMRustDiagnosticKind {
+  Other,
+  InlineAsm,
+  StackSize,
+  DebugMetadataVersion,
+  SampleProfile,
+  OptimizationRemark,
+  OptimizationRemarkMissed,
+  OptimizationRemarkAnalysis,
+  OptimizationRemarkAnalysisFPCommute,
+  OptimizationRemarkAnalysisAliasing,
+  OptimizationRemarkOther,
+  OptimizationFailure,
+  PGOProfile,
+  Linker,
+};
+
+static LLVMRustDiagnosticKind toRust(DiagnosticKind Kind) {
+  switch (Kind) {
+  case DK_InlineAsm:
+    return LLVMRustDiagnosticKind::InlineAsm;
+  case DK_StackSize:
+    return LLVMRustDiagnosticKind::StackSize;
+  case DK_DebugMetadataVersion:
+    return LLVMRustDiagnosticKind::DebugMetadataVersion;
+  case DK_SampleProfile:
+    return LLVMRustDiagnosticKind::SampleProfile;
+  case DK_OptimizationRemark:
+    return LLVMRustDiagnosticKind::OptimizationRemark;
+  case DK_OptimizationRemarkMissed:
+    return LLVMRustDiagnosticKind::OptimizationRemarkMissed;
+  case DK_OptimizationRemarkAnalysis:
+    return LLVMRustDiagnosticKind::OptimizationRemarkAnalysis;
+  case DK_OptimizationRemarkAnalysisFPCommute:
+    return LLVMRustDiagnosticKind::OptimizationRemarkAnalysisFPCommute;
+  case DK_OptimizationRemarkAnalysisAliasing:
+    return LLVMRustDiagnosticKind::OptimizationRemarkAnalysisAliasing;
+  case DK_PGOProfile:
+    return LLVMRustDiagnosticKind::PGOProfile;
+  case DK_Linker:
+    return LLVMRustDiagnosticKind::Linker;
+  default:
+    return (Kind >= DK_FirstRemark && Kind <= DK_LastRemark)
+               ? LLVMRustDiagnosticKind::OptimizationRemarkOther
+               : LLVMRustDiagnosticKind::Other;
+  }
+}
+
+extern "C" LLVMRustDiagnosticKind
+LLVMRustGetDiagInfoKind(LLVMDiagnosticInfoRef DI) {
+  return toRust((DiagnosticKind)unwrap(DI)->getKind());
+}
+
+// This is kept distinct from LLVMGetTypeKind, because when
+// a new type kind is added, the Rust-side enum must be
+// updated or UB will result.
+extern "C" LLVMTypeKind LLVMRustGetTypeKind(LLVMTypeRef Ty) {
+  switch (unwrap(Ty)->getTypeID()) {
+  case Type::VoidTyID:
+    return LLVMVoidTypeKind;
+  case Type::HalfTyID:
+    return LLVMHalfTypeKind;
+  case Type::FloatTyID:
+    return LLVMFloatTypeKind;
+  case Type::DoubleTyID:
+    return LLVMDoubleTypeKind;
+  case Type::X86_FP80TyID:
+    return LLVMX86_FP80TypeKind;
+  case Type::FP128TyID:
+    return LLVMFP128TypeKind;
+  case Type::PPC_FP128TyID:
+    return LLVMPPC_FP128TypeKind;
+  case Type::LabelTyID:
+    return LLVMLabelTypeKind;
+  case Type::MetadataTyID:
+    return LLVMMetadataTypeKind;
+  case Type::IntegerTyID:
+    return LLVMIntegerTypeKind;
+  case Type::FunctionTyID:
+    return LLVMFunctionTypeKind;
+  case Type::StructTyID:
+    return LLVMStructTypeKind;
+  case Type::ArrayTyID:
+    return LLVMArrayTypeKind;
+  case Type::PointerTyID:
+    return LLVMPointerTypeKind;
+#if LLVM_VERSION_GE(11, 0)
+  case Type::FixedVectorTyID:
+    return LLVMVectorTypeKind;
+#else
+  case Type::VectorTyID:
+    return LLVMVectorTypeKind;
+#endif
+  case Type::X86_MMXTyID:
+    return LLVMX86_MMXTypeKind;
+  case Type::TokenTyID:
+    return LLVMTokenTypeKind;
+#if LLVM_VERSION_GE(11, 0)
+  case Type::ScalableVectorTyID:
+    return LLVMScalableVectorTypeKind;
+  case Type::BFloatTyID:
+    return LLVMBFloatTypeKind;
+#endif
+  }
+  report_fatal_error("Unhandled TypeID.");
+}
+
+DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef)
+
+extern "C" void LLVMRustSetInlineAsmDiagnosticHandler(
+    LLVMContextRef C, LLVMContext::InlineAsmDiagHandlerTy H, void *CX) {
+  unwrap(C)->setInlineAsmDiagnosticHandler(H, CX);
+}
+
+extern "C" bool LLVMRustUnpackSMDiagnostic(LLVMSMDiagnosticRef DRef,
+                                           RustStringRef MessageOut,
+                                           RustStringRef BufferOut,
+                                           LLVMRustDiagnosticLevel* LevelOut,
+                                           unsigned* LocOut,
+                                           unsigned* RangesOut,
+                                           size_t* NumRanges) {
+  SMDiagnostic& D = *unwrap(DRef);
+  RawRustStringOstream MessageOS(MessageOut);
+  MessageOS << D.getMessage();
+
+  switch (D.getKind()) {
+    case SourceMgr::DK_Error:
+      *LevelOut = LLVMRustDiagnosticLevel::Error;
+      break;
+    case SourceMgr::DK_Warning:
+      *LevelOut = LLVMRustDiagnosticLevel::Warning;
+      break;
+    case SourceMgr::DK_Note:
+      *LevelOut = LLVMRustDiagnosticLevel::Note;
+      break;
+    case SourceMgr::DK_Remark:
+      *LevelOut = LLVMRustDiagnosticLevel::Remark;
+      break;
+    default:
+      report_fatal_error("Invalid LLVMRustDiagnosticLevel value!");
+  }
+
+  if (D.getLoc() == SMLoc())
+    return false;
+
+  const SourceMgr &LSM = *D.getSourceMgr();
+  const MemoryBuffer *LBuf = LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc()));
+  LLVMRustStringWriteImpl(BufferOut, LBuf->getBufferStart(), LBuf->getBufferSize());
+
+  *LocOut = D.getLoc().getPointer() - LBuf->getBufferStart();
+
+  *NumRanges = std::min(*NumRanges, D.getRanges().size());
+  size_t LineStart = *LocOut - (size_t)D.getColumnNo();
+  for (size_t i = 0; i < *NumRanges; i++) {
+    RangesOut[i * 2] = LineStart + D.getRanges()[i].first;
+    RangesOut[i * 2 + 1] = LineStart + D.getRanges()[i].second;
+  }
+
+  return true;
+}
+
+extern "C" LLVMValueRef LLVMRustBuildCleanupPad(LLVMBuilderRef B,
+                                                LLVMValueRef ParentPad,
+                                                unsigned ArgCount,
+                                                LLVMValueRef *LLArgs,
+                                                const char *Name) {
+  Value **Args = unwrap(LLArgs);
+  if (ParentPad == nullptr) {
+    Type *Ty = Type::getTokenTy(unwrap(B)->getContext());
+    ParentPad = wrap(Constant::getNullValue(Ty));
+  }
+  return wrap(unwrap(B)->CreateCleanupPad(
+      unwrap(ParentPad), ArrayRef<Value *>(Args, ArgCount), Name));
+}
+
+extern "C" LLVMValueRef LLVMRustBuildCleanupRet(LLVMBuilderRef B,
+                                                LLVMValueRef CleanupPad,
+                                                LLVMBasicBlockRef UnwindBB) {
+  CleanupPadInst *Inst = cast<CleanupPadInst>(unwrap(CleanupPad));
+  return wrap(unwrap(B)->CreateCleanupRet(Inst, unwrap(UnwindBB)));
+}
+
+extern "C" LLVMValueRef
+LLVMRustBuildCatchPad(LLVMBuilderRef B, LLVMValueRef ParentPad,
+                      unsigned ArgCount, LLVMValueRef *LLArgs, const char *Name) {
+  Value **Args = unwrap(LLArgs);
+  return wrap(unwrap(B)->CreateCatchPad(
+      unwrap(ParentPad), ArrayRef<Value *>(Args, ArgCount), Name));
+}
+
+extern "C" LLVMValueRef LLVMRustBuildCatchRet(LLVMBuilderRef B,
+                                              LLVMValueRef Pad,
+                                              LLVMBasicBlockRef BB) {
+  return wrap(unwrap(B)->CreateCatchRet(cast<CatchPadInst>(unwrap(Pad)),
+                                              unwrap(BB)));
+}
+
+extern "C" LLVMValueRef LLVMRustBuildCatchSwitch(LLVMBuilderRef B,
+                                                 LLVMValueRef ParentPad,
+                                                 LLVMBasicBlockRef BB,
+                                                 unsigned NumHandlers,
+                                                 const char *Name) {
+  if (ParentPad == nullptr) {
+    Type *Ty = Type::getTokenTy(unwrap(B)->getContext());
+    ParentPad = wrap(Constant::getNullValue(Ty));
+  }
+  return wrap(unwrap(B)->CreateCatchSwitch(unwrap(ParentPad), unwrap(BB),
+                                                 NumHandlers, Name));
+}
+
+extern "C" void LLVMRustAddHandler(LLVMValueRef CatchSwitchRef,
+                                   LLVMBasicBlockRef Handler) {
+  Value *CatchSwitch = unwrap(CatchSwitchRef);
+  cast<CatchSwitchInst>(CatchSwitch)->addHandler(unwrap(Handler));
+}
+
+extern "C" OperandBundleDef *LLVMRustBuildOperandBundleDef(const char *Name,
+                                                           LLVMValueRef *Inputs,
+                                                           unsigned NumInputs) {
+  return new OperandBundleDef(Name, makeArrayRef(unwrap(Inputs), NumInputs));
+}
+
+extern "C" void LLVMRustFreeOperandBundleDef(OperandBundleDef *Bundle) {
+  delete Bundle;
+}
+
+extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMValueRef Fn,
+                                          LLVMValueRef *Args, unsigned NumArgs,
+                                          OperandBundleDef *Bundle) {
+  Value *Callee = unwrap(Fn);
+  FunctionType *FTy = cast<FunctionType>(Callee->getType()->getPointerElementType());
+  unsigned Len = Bundle ? 1 : 0;
+  ArrayRef<OperandBundleDef> Bundles = makeArrayRef(Bundle, Len);
+  return wrap(unwrap(B)->CreateCall(
+      FTy, Callee, makeArrayRef(unwrap(Args), NumArgs), Bundles));
+}
+
+extern "C" LLVMValueRef LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) {
+  return wrap(llvm::Intrinsic::getDeclaration(unwrap(M),
+              (llvm::Intrinsic::ID)llvm::Intrinsic::instrprof_increment));
+}
+
+extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B,
+                                            LLVMValueRef Dst, unsigned DstAlign,
+                                            LLVMValueRef Src, unsigned SrcAlign,
+                                            LLVMValueRef Size, bool IsVolatile) {
+#if LLVM_VERSION_GE(10, 0)
+  return wrap(unwrap(B)->CreateMemCpy(
+      unwrap(Dst), MaybeAlign(DstAlign),
+      unwrap(Src), MaybeAlign(SrcAlign),
+      unwrap(Size), IsVolatile));
+#else
+  return wrap(unwrap(B)->CreateMemCpy(
+      unwrap(Dst), DstAlign,
+      unwrap(Src), SrcAlign,
+      unwrap(Size), IsVolatile));
+#endif
+}
+
+extern "C" LLVMValueRef LLVMRustBuildMemMove(LLVMBuilderRef B,
+                                             LLVMValueRef Dst, unsigned DstAlign,
+                                             LLVMValueRef Src, unsigned SrcAlign,
+                                             LLVMValueRef Size, bool IsVolatile) {
+#if LLVM_VERSION_GE(10, 0)
+  return wrap(unwrap(B)->CreateMemMove(
+      unwrap(Dst), MaybeAlign(DstAlign),
+      unwrap(Src), MaybeAlign(SrcAlign),
+      unwrap(Size), IsVolatile));
+#else
+  return wrap(unwrap(B)->CreateMemMove(
+      unwrap(Dst), DstAlign,
+      unwrap(Src), SrcAlign,
+      unwrap(Size), IsVolatile));
+#endif
+}
+
+extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B,
+                                            LLVMValueRef Dst, unsigned DstAlign,
+                                            LLVMValueRef Val,
+                                            LLVMValueRef Size, bool IsVolatile) {
+#if LLVM_VERSION_GE(10, 0)
+  return wrap(unwrap(B)->CreateMemSet(
+      unwrap(Dst), unwrap(Val), unwrap(Size), MaybeAlign(DstAlign), IsVolatile));
+#else
+  return wrap(unwrap(B)->CreateMemSet(
+      unwrap(Dst), unwrap(Val), unwrap(Size), DstAlign, IsVolatile));
+#endif
+}
+
+extern "C" LLVMValueRef
+LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
+                    unsigned NumArgs, LLVMBasicBlockRef Then,
+                    LLVMBasicBlockRef Catch, OperandBundleDef *Bundle,
+                    const char *Name) {
+  Value *Callee = unwrap(Fn);
+  FunctionType *FTy = cast<FunctionType>(Callee->getType()->getPointerElementType());
+  unsigned Len = Bundle ? 1 : 0;
+  ArrayRef<OperandBundleDef> Bundles = makeArrayRef(Bundle, Len);
+  return wrap(unwrap(B)->CreateInvoke(FTy, Callee, unwrap(Then), unwrap(Catch),
+                                      makeArrayRef(unwrap(Args), NumArgs),
+                                      Bundles, Name));
+}
+
+extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B,
+                                               LLVMBasicBlockRef BB) {
+  auto Point = unwrap(BB)->getFirstInsertionPt();
+  unwrap(B)->SetInsertPoint(unwrap(BB), Point);
+}
+
+extern "C" void LLVMRustSetComdat(LLVMModuleRef M, LLVMValueRef V,
+                                  const char *Name, size_t NameLen) {
+  Triple TargetTriple(unwrap(M)->getTargetTriple());
+  GlobalObject *GV = unwrap<GlobalObject>(V);
+  if (!TargetTriple.isOSBinFormatMachO()) {
+    StringRef NameRef(Name, NameLen);
+    GV->setComdat(unwrap(M)->getOrInsertComdat(NameRef));
+  }
+}
+
+extern "C" void LLVMRustUnsetComdat(LLVMValueRef V) {
+  GlobalObject *GV = unwrap<GlobalObject>(V);
+  GV->setComdat(nullptr);
+}
+
+enum class LLVMRustLinkage {
+  ExternalLinkage = 0,
+  AvailableExternallyLinkage = 1,
+  LinkOnceAnyLinkage = 2,
+  LinkOnceODRLinkage = 3,
+  WeakAnyLinkage = 4,
+  WeakODRLinkage = 5,
+  AppendingLinkage = 6,
+  InternalLinkage = 7,
+  PrivateLinkage = 8,
+  ExternalWeakLinkage = 9,
+  CommonLinkage = 10,
+};
+
+static LLVMRustLinkage toRust(LLVMLinkage Linkage) {
+  switch (Linkage) {
+  case LLVMExternalLinkage:
+    return LLVMRustLinkage::ExternalLinkage;
+  case LLVMAvailableExternallyLinkage:
+    return LLVMRustLinkage::AvailableExternallyLinkage;
+  case LLVMLinkOnceAnyLinkage:
+    return LLVMRustLinkage::LinkOnceAnyLinkage;
+  case LLVMLinkOnceODRLinkage:
+    return LLVMRustLinkage::LinkOnceODRLinkage;
+  case LLVMWeakAnyLinkage:
+    return LLVMRustLinkage::WeakAnyLinkage;
+  case LLVMWeakODRLinkage:
+    return LLVMRustLinkage::WeakODRLinkage;
+  case LLVMAppendingLinkage:
+    return LLVMRustLinkage::AppendingLinkage;
+  case LLVMInternalLinkage:
+    return LLVMRustLinkage::InternalLinkage;
+  case LLVMPrivateLinkage:
+    return LLVMRustLinkage::PrivateLinkage;
+  case LLVMExternalWeakLinkage:
+    return LLVMRustLinkage::ExternalWeakLinkage;
+  case LLVMCommonLinkage:
+    return LLVMRustLinkage::CommonLinkage;
+  default:
+    report_fatal_error("Invalid LLVMRustLinkage value!");
+  }
+}
+
+static LLVMLinkage fromRust(LLVMRustLinkage Linkage) {
+  switch (Linkage) {
+  case LLVMRustLinkage::ExternalLinkage:
+    return LLVMExternalLinkage;
+  case LLVMRustLinkage::AvailableExternallyLinkage:
+    return LLVMAvailableExternallyLinkage;
+  case LLVMRustLinkage::LinkOnceAnyLinkage:
+    return LLVMLinkOnceAnyLinkage;
+  case LLVMRustLinkage::LinkOnceODRLinkage:
+    return LLVMLinkOnceODRLinkage;
+  case LLVMRustLinkage::WeakAnyLinkage:
+    return LLVMWeakAnyLinkage;
+  case LLVMRustLinkage::WeakODRLinkage:
+    return LLVMWeakODRLinkage;
+  case LLVMRustLinkage::AppendingLinkage:
+    return LLVMAppendingLinkage;
+  case LLVMRustLinkage::InternalLinkage:
+    return LLVMInternalLinkage;
+  case LLVMRustLinkage::PrivateLinkage:
+    return LLVMPrivateLinkage;
+  case LLVMRustLinkage::ExternalWeakLinkage:
+    return LLVMExternalWeakLinkage;
+  case LLVMRustLinkage::CommonLinkage:
+    return LLVMCommonLinkage;
+  }
+  report_fatal_error("Invalid LLVMRustLinkage value!");
+}
+
+extern "C" LLVMRustLinkage LLVMRustGetLinkage(LLVMValueRef V) {
+  return toRust(LLVMGetLinkage(V));
+}
+
+extern "C" void LLVMRustSetLinkage(LLVMValueRef V,
+                                   LLVMRustLinkage RustLinkage) {
+  LLVMSetLinkage(V, fromRust(RustLinkage));
+}
+
+// Returns true if both high and low were successfully set. Fails in case constant wasn’t any of
+// the common sizes (1, 8, 16, 32, 64, 128 bits)
+extern "C" bool LLVMRustConstInt128Get(LLVMValueRef CV, bool sext, uint64_t *high, uint64_t *low)
+{
+    auto C = unwrap<llvm::ConstantInt>(CV);
+    if (C->getBitWidth() > 128) { return false; }
+    APInt AP;
+    if (sext) {
+        AP = C->getValue().sextOrSelf(128);
+    } else {
+        AP = C->getValue().zextOrSelf(128);
+    }
+    *low = AP.getLoBits(64).getZExtValue();
+    *high = AP.getHiBits(64).getZExtValue();
+    return true;
+}
+
+enum class LLVMRustVisibility {
+  Default = 0,
+  Hidden = 1,
+  Protected = 2,
+};
+
+static LLVMRustVisibility toRust(LLVMVisibility Vis) {
+  switch (Vis) {
+  case LLVMDefaultVisibility:
+    return LLVMRustVisibility::Default;
+  case LLVMHiddenVisibility:
+    return LLVMRustVisibility::Hidden;
+  case LLVMProtectedVisibility:
+    return LLVMRustVisibility::Protected;
+  }
+  report_fatal_error("Invalid LLVMRustVisibility value!");
+}
+
+static LLVMVisibility fromRust(LLVMRustVisibility Vis) {
+  switch (Vis) {
+  case LLVMRustVisibility::Default:
+    return LLVMDefaultVisibility;
+  case LLVMRustVisibility::Hidden:
+    return LLVMHiddenVisibility;
+  case LLVMRustVisibility::Protected:
+    return LLVMProtectedVisibility;
+  }
+  report_fatal_error("Invalid LLVMRustVisibility value!");
+}
+
+extern "C" LLVMRustVisibility LLVMRustGetVisibility(LLVMValueRef V) {
+  return toRust(LLVMGetVisibility(V));
+}
+
+// Oh hey, a binding that makes sense for once? (because LLVM’s own do not)
+extern "C" LLVMValueRef LLVMRustBuildIntCast(LLVMBuilderRef B, LLVMValueRef Val,
+                                             LLVMTypeRef DestTy, bool isSigned) {
+  return wrap(unwrap(B)->CreateIntCast(unwrap(Val), unwrap(DestTy), isSigned, ""));
+}
+
+extern "C" void LLVMRustSetVisibility(LLVMValueRef V,
+                                      LLVMRustVisibility RustVisibility) {
+  LLVMSetVisibility(V, fromRust(RustVisibility));
+}
+
+struct LLVMRustModuleBuffer {
+  std::string data;
+};
+
+extern "C" LLVMRustModuleBuffer*
+LLVMRustModuleBufferCreate(LLVMModuleRef M) {
+#if LLVM_VERSION_GE(10, 0)
+  auto Ret = std::make_unique<LLVMRustModuleBuffer>();
+#else
+  auto Ret = llvm::make_unique<LLVMRustModuleBuffer>();
+#endif
+  {
+    raw_string_ostream OS(Ret->data);
+    {
+      legacy::PassManager PM;
+      PM.add(createBitcodeWriterPass(OS));
+      PM.run(*unwrap(M));
+    }
+  }
+  return Ret.release();
+}
+
+extern "C" void
+LLVMRustModuleBufferFree(LLVMRustModuleBuffer *Buffer) {
+  delete Buffer;
+}
+
+extern "C" const void*
+LLVMRustModuleBufferPtr(const LLVMRustModuleBuffer *Buffer) {
+  return Buffer->data.data();
+}
+
+extern "C" size_t
+LLVMRustModuleBufferLen(const LLVMRustModuleBuffer *Buffer) {
+  return Buffer->data.length();
+}
+
+extern "C" uint64_t
+LLVMRustModuleCost(LLVMModuleRef M) {
+  auto f = unwrap(M)->functions();
+  return std::distance(std::begin(f), std::end(f));
+}
+
+// Vector reductions:
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceFAdd(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Src) {
+    return wrap(unwrap(B)->CreateFAddReduce(unwrap(Acc),unwrap(Src)));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceFMul(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Src) {
+    return wrap(unwrap(B)->CreateFMulReduce(unwrap(Acc),unwrap(Src)));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceAdd(LLVMBuilderRef B, LLVMValueRef Src) {
+    return wrap(unwrap(B)->CreateAddReduce(unwrap(Src)));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceMul(LLVMBuilderRef B, LLVMValueRef Src) {
+    return wrap(unwrap(B)->CreateMulReduce(unwrap(Src)));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceAnd(LLVMBuilderRef B, LLVMValueRef Src) {
+    return wrap(unwrap(B)->CreateAndReduce(unwrap(Src)));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceOr(LLVMBuilderRef B, LLVMValueRef Src) {
+    return wrap(unwrap(B)->CreateOrReduce(unwrap(Src)));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceXor(LLVMBuilderRef B, LLVMValueRef Src) {
+    return wrap(unwrap(B)->CreateXorReduce(unwrap(Src)));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceMin(LLVMBuilderRef B, LLVMValueRef Src, bool IsSigned) {
+    return wrap(unwrap(B)->CreateIntMinReduce(unwrap(Src), IsSigned));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceMax(LLVMBuilderRef B, LLVMValueRef Src, bool IsSigned) {
+    return wrap(unwrap(B)->CreateIntMaxReduce(unwrap(Src), IsSigned));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceFMin(LLVMBuilderRef B, LLVMValueRef Src, bool NoNaN) {
+   return wrap(unwrap(B)->CreateFPMinReduce(unwrap(Src), NoNaN));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildVectorReduceFMax(LLVMBuilderRef B, LLVMValueRef Src, bool NoNaN) {
+  return wrap(unwrap(B)->CreateFPMaxReduce(unwrap(Src), NoNaN));
+}
+
+extern "C" LLVMValueRef
+LLVMRustBuildMinNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) {
+    return wrap(unwrap(B)->CreateMinNum(unwrap(LHS),unwrap(RHS)));
+}
+extern "C" LLVMValueRef
+LLVMRustBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS) {
+    return wrap(unwrap(B)->CreateMaxNum(unwrap(LHS),unwrap(RHS)));
+}
diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs
new file mode 100644
index 00000000000..9d23397ade0
--- /dev/null
+++ b/compiler/rustc_llvm/src/lib.rs
@@ -0,0 +1,173 @@
+#![feature(nll)]
+#![feature(static_nobundle)]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
+
+// NOTE: This crate only exists to allow linking on mingw targets.
+
+use libc::{c_char, size_t};
+use std::cell::RefCell;
+use std::slice;
+
+#[repr(C)]
+pub struct RustString {
+    pub bytes: RefCell<Vec<u8>>,
+}
+
+impl RustString {
+    pub fn len(&self) -> usize {
+        self.bytes.borrow().len()
+    }
+}
+
+/// Appending to a Rust string -- used by RawRustStringOstream.
+#[no_mangle]
+#[allow(improper_ctypes_definitions)]
+pub unsafe extern "C" fn LLVMRustStringWriteImpl(
+    sr: &RustString,
+    ptr: *const c_char,
+    size: size_t,
+) {
+    let slice = slice::from_raw_parts(ptr as *const u8, size as usize);
+
+    sr.bytes.borrow_mut().extend_from_slice(slice);
+}
+
+/// Initialize targets enabled by the build script via `cfg(llvm_component = "...")`.
+/// N.B., this function can't be moved to `rustc_codegen_llvm` because of the `cfg`s.
+pub fn initialize_available_targets() {
+    macro_rules! init_target(
+        ($cfg:meta, $($method:ident),*) => { {
+            #[cfg($cfg)]
+            fn init() {
+                extern {
+                    $(fn $method();)*
+                }
+                unsafe {
+                    $($method();)*
+                }
+            }
+            #[cfg(not($cfg))]
+            fn init() { }
+            init();
+        } }
+    );
+    init_target!(
+        llvm_component = "x86",
+        LLVMInitializeX86TargetInfo,
+        LLVMInitializeX86Target,
+        LLVMInitializeX86TargetMC,
+        LLVMInitializeX86AsmPrinter,
+        LLVMInitializeX86AsmParser
+    );
+    init_target!(
+        llvm_component = "arm",
+        LLVMInitializeARMTargetInfo,
+        LLVMInitializeARMTarget,
+        LLVMInitializeARMTargetMC,
+        LLVMInitializeARMAsmPrinter,
+        LLVMInitializeARMAsmParser
+    );
+    init_target!(
+        llvm_component = "aarch64",
+        LLVMInitializeAArch64TargetInfo,
+        LLVMInitializeAArch64Target,
+        LLVMInitializeAArch64TargetMC,
+        LLVMInitializeAArch64AsmPrinter,
+        LLVMInitializeAArch64AsmParser
+    );
+    init_target!(
+        llvm_component = "amdgpu",
+        LLVMInitializeAMDGPUTargetInfo,
+        LLVMInitializeAMDGPUTarget,
+        LLVMInitializeAMDGPUTargetMC,
+        LLVMInitializeAMDGPUAsmPrinter,
+        LLVMInitializeAMDGPUAsmParser
+    );
+    init_target!(
+        llvm_component = "avr",
+        LLVMInitializeAVRTargetInfo,
+        LLVMInitializeAVRTarget,
+        LLVMInitializeAVRTargetMC,
+        LLVMInitializeAVRAsmPrinter,
+        LLVMInitializeAVRAsmParser
+    );
+    init_target!(
+        llvm_component = "mips",
+        LLVMInitializeMipsTargetInfo,
+        LLVMInitializeMipsTarget,
+        LLVMInitializeMipsTargetMC,
+        LLVMInitializeMipsAsmPrinter,
+        LLVMInitializeMipsAsmParser
+    );
+    init_target!(
+        llvm_component = "powerpc",
+        LLVMInitializePowerPCTargetInfo,
+        LLVMInitializePowerPCTarget,
+        LLVMInitializePowerPCTargetMC,
+        LLVMInitializePowerPCAsmPrinter,
+        LLVMInitializePowerPCAsmParser
+    );
+    init_target!(
+        llvm_component = "systemz",
+        LLVMInitializeSystemZTargetInfo,
+        LLVMInitializeSystemZTarget,
+        LLVMInitializeSystemZTargetMC,
+        LLVMInitializeSystemZAsmPrinter,
+        LLVMInitializeSystemZAsmParser
+    );
+    init_target!(
+        llvm_component = "jsbackend",
+        LLVMInitializeJSBackendTargetInfo,
+        LLVMInitializeJSBackendTarget,
+        LLVMInitializeJSBackendTargetMC
+    );
+    init_target!(
+        llvm_component = "msp430",
+        LLVMInitializeMSP430TargetInfo,
+        LLVMInitializeMSP430Target,
+        LLVMInitializeMSP430TargetMC,
+        LLVMInitializeMSP430AsmPrinter
+    );
+    init_target!(
+        all(llvm_component = "msp430", llvm_has_msp430_asm_parser),
+        LLVMInitializeMSP430AsmParser
+    );
+    init_target!(
+        llvm_component = "riscv",
+        LLVMInitializeRISCVTargetInfo,
+        LLVMInitializeRISCVTarget,
+        LLVMInitializeRISCVTargetMC,
+        LLVMInitializeRISCVAsmPrinter,
+        LLVMInitializeRISCVAsmParser
+    );
+    init_target!(
+        llvm_component = "sparc",
+        LLVMInitializeSparcTargetInfo,
+        LLVMInitializeSparcTarget,
+        LLVMInitializeSparcTargetMC,
+        LLVMInitializeSparcAsmPrinter,
+        LLVMInitializeSparcAsmParser
+    );
+    init_target!(
+        llvm_component = "nvptx",
+        LLVMInitializeNVPTXTargetInfo,
+        LLVMInitializeNVPTXTarget,
+        LLVMInitializeNVPTXTargetMC,
+        LLVMInitializeNVPTXAsmPrinter
+    );
+    init_target!(
+        llvm_component = "hexagon",
+        LLVMInitializeHexagonTargetInfo,
+        LLVMInitializeHexagonTarget,
+        LLVMInitializeHexagonTargetMC,
+        LLVMInitializeHexagonAsmPrinter,
+        LLVMInitializeHexagonAsmParser
+    );
+    init_target!(
+        llvm_component = "webassembly",
+        LLVMInitializeWebAssemblyTargetInfo,
+        LLVMInitializeWebAssemblyTarget,
+        LLVMInitializeWebAssemblyTargetMC,
+        LLVMInitializeWebAssemblyAsmPrinter
+    );
+}
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs
index 7fb3b0e7ea6..5c28839c9b7 100644
--- a/compiler/rustc_macros/src/lib.rs
+++ b/compiler/rustc_macros/src/lib.rs
@@ -1,3 +1,4 @@
+#![feature(proc_macro_diagnostic)]
 #![allow(rustc::default_hash_types)]
 #![recursion_limit = "128"]
 
@@ -9,6 +10,7 @@ mod hash_stable;
 mod lift;
 mod query;
 mod serialize;
+mod session_diagnostic;
 mod symbols;
 mod type_foldable;
 
@@ -36,3 +38,14 @@ decl_derive!([MetadataDecodable] => serialize::meta_decodable_derive);
 decl_derive!([MetadataEncodable] => serialize::meta_encodable_derive);
 decl_derive!([TypeFoldable, attributes(type_foldable)] => type_foldable::type_foldable_derive);
 decl_derive!([Lift, attributes(lift)] => lift::lift_derive);
+decl_derive!(
+    [SessionDiagnostic, attributes(
+        message,
+        lint,
+        error,
+        label,
+        suggestion,
+        suggestion_short,
+        suggestion_hidden,
+        suggestion_verbose)] => session_diagnostic::session_diagnostic_derive
+);
diff --git a/compiler/rustc_macros/src/session_diagnostic.rs b/compiler/rustc_macros/src/session_diagnostic.rs
new file mode 100644
index 00000000000..610b9155cfc
--- /dev/null
+++ b/compiler/rustc_macros/src/session_diagnostic.rs
@@ -0,0 +1,666 @@
+#![deny(unused_must_use)]
+use proc_macro::Diagnostic;
+use quote::{format_ident, quote};
+use syn::spanned::Spanned;
+
+use std::collections::{BTreeSet, HashMap};
+
+/// Implements #[derive(SessionDiagnostic)], which allows for errors to be specified as a struct, independent
+/// from the actual diagnostics emitting code.
+/// ```ignore (pseudo-rust)
+/// # extern crate rustc_errors;
+/// # use rustc_errors::Applicability;
+/// # extern crate rustc_span;
+/// # use rustc_span::{symbol::Ident, Span};
+/// # extern crate rust_middle;
+/// # use rustc_middle::ty::Ty;
+/// #[derive(SessionDiagnostic)]
+/// #[code = "E0505"]
+/// #[error = "cannot move out of {name} because it is borrowed"]
+/// pub struct MoveOutOfBorrowError<'tcx> {
+///     pub name: Ident,
+///     pub ty: Ty<'tcx>,
+///     #[label = "cannot move out of borrow"]
+///     pub span: Span,
+///     #[label = "`{ty}` first borrowed here"]
+///     pub other_span: Span,
+///     #[suggestion(message = "consider cloning here", code = "{name}.clone()")]
+///     pub opt_sugg: Option<(Span, Applicability)>
+/// }
+/// ```
+/// Then, later, to emit the error:
+///
+/// ```ignore (pseudo-rust)
+/// sess.emit_err(MoveOutOfBorrowError {
+///     expected,
+///     actual,
+///     span,
+///     other_span,
+///     opt_sugg: Some(suggestion, Applicability::MachineApplicable),
+/// });
+/// ```
+pub fn session_diagnostic_derive(s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
+    // Names for the diagnostic we build and the session we build it from.
+    let diag = format_ident!("diag");
+    let sess = format_ident!("sess");
+
+    SessionDiagnosticDerive::new(diag, sess, s).into_tokens()
+}
+
+// Checks whether the type name of `ty` matches `name`.
+//
+// Given some struct at a::b::c::Foo, this will return true for c::Foo, b::c::Foo, or
+// a::b::c::Foo. This reasonably allows qualified names to be used in the macro.
+fn type_matches_path(ty: &syn::Type, name: &[&str]) -> bool {
+    if let syn::Type::Path(ty) = ty {
+        ty.path
+            .segments
+            .iter()
+            .map(|s| s.ident.to_string())
+            .rev()
+            .zip(name.iter().rev())
+            .all(|(x, y)| &x.as_str() == y)
+    } else {
+        false
+    }
+}
+
+/// The central struct for constructing the as_error method from an annotated struct.
+struct SessionDiagnosticDerive<'a> {
+    structure: synstructure::Structure<'a>,
+    builder: SessionDiagnosticDeriveBuilder<'a>,
+}
+
+impl std::convert::From<syn::Error> for SessionDiagnosticDeriveError {
+    fn from(e: syn::Error) -> Self {
+        SessionDiagnosticDeriveError::SynError(e)
+    }
+}
+
+/// Equivalent to rustc:errors::diagnostic::DiagnosticId, except stores the quoted expression to
+/// initialise the code with.
+enum DiagnosticId {
+    Error(proc_macro2::TokenStream),
+    Lint(proc_macro2::TokenStream),
+}
+
+#[derive(Debug)]
+enum SessionDiagnosticDeriveError {
+    SynError(syn::Error),
+    ErrorHandled,
+}
+
+impl SessionDiagnosticDeriveError {
+    fn to_compile_error(self) -> proc_macro2::TokenStream {
+        match self {
+            SessionDiagnosticDeriveError::SynError(e) => e.to_compile_error(),
+            SessionDiagnosticDeriveError::ErrorHandled => {
+                // Return ! to avoid having to create a blank DiagnosticBuilder to return when an
+                // error has already been emitted to the compiler.
+                quote! {
+                    unreachable!()
+                }
+            }
+        }
+    }
+}
+
+fn span_err(span: impl proc_macro::MultiSpan, msg: &str) -> proc_macro::Diagnostic {
+    Diagnostic::spanned(span, proc_macro::Level::Error, msg)
+}
+
+/// For methods that return a Result<_, SessionDiagnosticDeriveError>: emit a diagnostic on
+/// span $span with msg $msg (and, optionally, perform additional decoration using the FnOnce
+/// passed in `diag`). Then, return Err(ErrorHandled).
+macro_rules! throw_span_err {
+    ($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }};
+    ($span:expr, $msg:expr, $f:expr) => {{
+        return Err(_throw_span_err($span, $msg, $f));
+    }};
+}
+
+/// When possible, prefer using throw_span_err! over using this function directly. This only exists
+/// as a function to constrain `f` to an impl FnOnce.
+fn _throw_span_err(
+    span: impl proc_macro::MultiSpan,
+    msg: &str,
+    f: impl FnOnce(proc_macro::Diagnostic) -> proc_macro::Diagnostic,
+) -> SessionDiagnosticDeriveError {
+    let diag = span_err(span, msg);
+    f(diag).emit();
+    SessionDiagnosticDeriveError::ErrorHandled
+}
+
+impl<'a> SessionDiagnosticDerive<'a> {
+    fn new(diag: syn::Ident, sess: syn::Ident, structure: synstructure::Structure<'a>) -> Self {
+        // Build the mapping of field names to fields. This allows attributes to peek values from
+        // other fields.
+        let mut fields_map = HashMap::new();
+
+        // Convenience bindings.
+        let ast = structure.ast();
+
+        if let syn::Data::Struct(syn::DataStruct { fields, .. }) = &ast.data {
+            for field in fields.iter() {
+                if let Some(ident) = &field.ident {
+                    fields_map.insert(ident.to_string(), field);
+                }
+            }
+        }
+
+        Self {
+            builder: SessionDiagnosticDeriveBuilder { diag, sess, fields: fields_map, kind: None },
+            structure,
+        }
+    }
+    fn into_tokens(self) -> proc_macro2::TokenStream {
+        let SessionDiagnosticDerive { structure, mut builder } = self;
+
+        let ast = structure.ast();
+        let attrs = &ast.attrs;
+
+        let implementation = {
+            if let syn::Data::Struct(..) = ast.data {
+                let preamble = {
+                    let preamble = attrs.iter().map(|attr| {
+                        builder
+                            .generate_structure_code(attr)
+                            .unwrap_or_else(|v| v.to_compile_error())
+                    });
+                    quote! {
+                        #(#preamble)*;
+                    }
+                };
+
+                let body = structure.each(|field_binding| {
+                    let field = field_binding.ast();
+                    let result = field.attrs.iter().map(|attr| {
+                        builder
+                            .generate_field_code(
+                                attr,
+                                FieldInfo {
+                                    vis: &field.vis,
+                                    binding: field_binding,
+                                    ty: &field.ty,
+                                    span: &field.span(),
+                                },
+                            )
+                            .unwrap_or_else(|v| v.to_compile_error())
+                    });
+                    return quote! {
+                        #(#result);*
+                    };
+                });
+                // Finally, putting it altogether.
+                match builder.kind {
+                    None => {
+                        span_err(ast.span().unwrap(), "`code` not specified")
+                        .help("use the [code = \"...\"] attribute to set this diagnostic's error code ")
+                        .emit();
+                        SessionDiagnosticDeriveError::ErrorHandled.to_compile_error()
+                    }
+                    Some((kind, _)) => match kind {
+                        DiagnosticId::Lint(_lint) => todo!(),
+                        DiagnosticId::Error(code) => {
+                            let (diag, sess) = (&builder.diag, &builder.sess);
+                            quote! {
+                                let mut #diag = #sess.struct_err_with_code("", rustc_errors::DiagnosticId::Error(#code));
+                                #preamble
+                                match self {
+                                    #body
+                                }
+                                #diag
+                            }
+                        }
+                    },
+                }
+            } else {
+                span_err(
+                    ast.span().unwrap(),
+                    "`#[derive(SessionDiagnostic)]` can only be used on structs",
+                )
+                .emit();
+                SessionDiagnosticDeriveError::ErrorHandled.to_compile_error()
+            }
+        };
+
+        let sess = &builder.sess;
+        structure.gen_impl(quote! {
+            gen impl<'__session_diagnostic_sess> rustc_session::SessionDiagnostic<'__session_diagnostic_sess>
+                    for @Self
+            {
+                fn into_diagnostic(
+                    self,
+                    #sess: &'__session_diagnostic_sess rustc_session::Session
+                ) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess> {
+                    #implementation
+                }
+            }
+        })
+    }
+}
+
+/// Field information passed to the builder. Deliberately omits attrs to discourage the generate_*
+/// methods from walking the attributes themselves.
+struct FieldInfo<'a> {
+    vis: &'a syn::Visibility,
+    binding: &'a synstructure::BindingInfo<'a>,
+    ty: &'a syn::Type,
+    span: &'a proc_macro2::Span,
+}
+
+/// Tracks persistent information required for building up the individual calls to diagnostic
+/// methods for the final generated method. This is a separate struct to SessionDerive only to be
+/// able to destructure and split self.builder and the self.structure up to avoid a double mut
+/// borrow later on.
+struct SessionDiagnosticDeriveBuilder<'a> {
+    /// Name of the session parameter that's passed in to the as_error method.
+    sess: syn::Ident,
+
+    /// Store a map of field name to its corresponding field. This is built on construction of the
+    /// derive builder.
+    fields: HashMap<String, &'a syn::Field>,
+
+    /// The identifier to use for the generated DiagnosticBuilder instance.
+    diag: syn::Ident,
+
+    /// Whether this is a lint or an error. This dictates how the diag will be initialised. Span
+    /// stores at what Span the kind was first set at (for error reporting purposes, if the kind
+    /// was multiply specified).
+    kind: Option<(DiagnosticId, proc_macro2::Span)>,
+}
+
+impl<'a> SessionDiagnosticDeriveBuilder<'a> {
+    fn generate_structure_code(
+        &mut self,
+        attr: &syn::Attribute,
+    ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
+        Ok(match attr.parse_meta()? {
+            syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
+                let formatted_str = self.build_format(&s.value(), attr.span());
+                let name = attr.path.segments.last().unwrap().ident.to_string();
+                let name = name.as_str();
+                match name {
+                    "message" => {
+                        let diag = &self.diag;
+                        quote! {
+                            #diag.set_primary_message(#formatted_str);
+                        }
+                    }
+                    attr @ "error" | attr @ "lint" => {
+                        self.set_kind_once(
+                            if attr == "error" {
+                                DiagnosticId::Error(formatted_str)
+                            } else if attr == "lint" {
+                                DiagnosticId::Lint(formatted_str)
+                            } else {
+                                unreachable!()
+                            },
+                            s.span(),
+                        )?;
+                        // This attribute is only allowed to be applied once, and the attribute
+                        // will be set in the initialisation code.
+                        quote! {}
+                    }
+                    other => throw_span_err!(
+                        attr.span().unwrap(),
+                        &format!(
+                            "`#[{} = ...]` is not a valid SessionDiagnostic struct attribute",
+                            other
+                        )
+                    ),
+                }
+            }
+            _ => todo!("unhandled meta kind"),
+        })
+    }
+
+    #[must_use]
+    fn set_kind_once(
+        &mut self,
+        kind: DiagnosticId,
+        span: proc_macro2::Span,
+    ) -> Result<(), SessionDiagnosticDeriveError> {
+        if self.kind.is_none() {
+            self.kind = Some((kind, span));
+            Ok(())
+        } else {
+            let kind_str = |kind: &DiagnosticId| match kind {
+                DiagnosticId::Lint(..) => "lint",
+                DiagnosticId::Error(..) => "error",
+            };
+
+            let existing_kind = kind_str(&self.kind.as_ref().unwrap().0);
+            let this_kind = kind_str(&kind);
+
+            let msg = if this_kind == existing_kind {
+                format!("`{}` specified multiple times", existing_kind)
+            } else {
+                format!("`{}` specified when `{}` was already specified", this_kind, existing_kind)
+            };
+            throw_span_err!(span.unwrap(), &msg);
+        }
+    }
+
+    fn generate_field_code(
+        &mut self,
+        attr: &syn::Attribute,
+        info: FieldInfo<'_>,
+    ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
+        let field_binding = &info.binding.binding;
+
+        let option_ty = option_inner_ty(&info.ty);
+
+        let generated_code = self.generate_non_option_field_code(
+            attr,
+            FieldInfo {
+                vis: info.vis,
+                binding: info.binding,
+                ty: option_ty.unwrap_or(&info.ty),
+                span: info.span,
+            },
+        )?;
+        Ok(if option_ty.is_none() {
+            quote! { #generated_code }
+        } else {
+            quote! {
+                if let Some(#field_binding) = #field_binding {
+                    #generated_code
+                }
+            }
+        })
+    }
+
+    fn generate_non_option_field_code(
+        &mut self,
+        attr: &syn::Attribute,
+        info: FieldInfo<'_>,
+    ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
+        let diag = &self.diag;
+        let field_binding = &info.binding.binding;
+        let name = attr.path.segments.last().unwrap().ident.to_string();
+        let name = name.as_str();
+        // At this point, we need to dispatch based on the attribute key + the
+        // type.
+        let meta = attr.parse_meta()?;
+        Ok(match meta {
+            syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
+                let formatted_str = self.build_format(&s.value(), attr.span());
+                match name {
+                    "message" => {
+                        if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
+                            quote! {
+                                #diag.set_span(*#field_binding);
+                                #diag.set_primary_message(#formatted_str);
+                            }
+                        } else {
+                            throw_span_err!(
+                                attr.span().unwrap(),
+                                "the `#[message = \"...\"]` attribute can only be applied to fields of type Span"
+                            );
+                        }
+                    }
+                    "label" => {
+                        if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
+                            quote! {
+                                #diag.span_label(*#field_binding, #formatted_str);
+                            }
+                        } else {
+                            throw_span_err!(
+                                attr.span().unwrap(),
+                                "The `#[label = ...]` attribute can only be applied to fields of type Span"
+                            );
+                        }
+                    }
+                    other => throw_span_err!(
+                        attr.span().unwrap(),
+                        &format!(
+                            "`#[{} = ...]` is not a valid SessionDiagnostic field attribute",
+                            other
+                        )
+                    ),
+                }
+            }
+            syn::Meta::List(list) => {
+                match list.path.segments.iter().last().unwrap().ident.to_string().as_str() {
+                    suggestion_kind @ "suggestion"
+                    | suggestion_kind @ "suggestion_short"
+                    | suggestion_kind @ "suggestion_hidden"
+                    | suggestion_kind @ "suggestion_verbose" => {
+                        // For suggest, we need to ensure we are running on a (Span,
+                        // Applicability) pair.
+                        let (span, applicability) = (|| match &info.ty {
+                            ty @ syn::Type::Path(..)
+                                if type_matches_path(ty, &["rustc_span", "Span"]) =>
+                            {
+                                let binding = &info.binding.binding;
+                                Ok((
+                                    quote!(*#binding),
+                                    quote!(rustc_errors::Applicability::Unspecified),
+                                ))
+                            }
+                            syn::Type::Tuple(tup) => {
+                                let mut span_idx = None;
+                                let mut applicability_idx = None;
+                                for (idx, elem) in tup.elems.iter().enumerate() {
+                                    if type_matches_path(elem, &["rustc_span", "Span"]) {
+                                        if span_idx.is_none() {
+                                            span_idx = Some(syn::Index::from(idx));
+                                        } else {
+                                            throw_span_err!(
+                                                info.span.clone().unwrap(),
+                                                "type of field annotated with `#[suggestion(...)]` contains more than one Span"
+                                            );
+                                        }
+                                    } else if type_matches_path(
+                                        elem,
+                                        &["rustc_errors", "Applicability"],
+                                    ) {
+                                        if applicability_idx.is_none() {
+                                            applicability_idx = Some(syn::Index::from(idx));
+                                        } else {
+                                            throw_span_err!(
+                                                info.span.clone().unwrap(),
+                                                "type of field annotated with `#[suggestion(...)]` contains more than one Applicability"
+                                            );
+                                        }
+                                    }
+                                }
+                                if let Some(span_idx) = span_idx {
+                                    let binding = &info.binding.binding;
+                                    let span = quote!(#binding.#span_idx);
+                                    let applicability = applicability_idx
+                                        .map(
+                                            |applicability_idx| quote!(#binding.#applicability_idx),
+                                        )
+                                        .unwrap_or(quote!(
+                                            rustc_errors::Applicability::Unspecified
+                                        ));
+                                    return Ok((span, applicability));
+                                }
+                                throw_span_err!(
+                                    info.span.clone().unwrap(),
+                                    "wrong types for suggestion",
+                                    |diag| {
+                                        diag.help("#[suggestion(...)] on a tuple field must be applied to fields of type (Span, Applicability)")
+                                    }
+                                );
+                            }
+                            _ => throw_span_err!(
+                                info.span.clone().unwrap(),
+                                "wrong field type for suggestion",
+                                |diag| {
+                                    diag.help("#[suggestion(...)] should be applied to fields of type Span or (Span, Applicability)")
+                                }
+                            ),
+                        })()?;
+                        // Now read the key-value pairs.
+                        let mut msg = None;
+                        let mut code = None;
+
+                        for arg in list.nested.iter() {
+                            if let syn::NestedMeta::Meta(syn::Meta::NameValue(arg_name_value)) = arg
+                            {
+                                if let syn::MetaNameValue { lit: syn::Lit::Str(s), .. } =
+                                    arg_name_value
+                                {
+                                    let name = arg_name_value
+                                        .path
+                                        .segments
+                                        .last()
+                                        .unwrap()
+                                        .ident
+                                        .to_string();
+                                    let name = name.as_str();
+                                    let formatted_str = self.build_format(&s.value(), arg.span());
+                                    match name {
+                                        "message" => {
+                                            msg = Some(formatted_str);
+                                        }
+                                        "code" => {
+                                            code = Some(formatted_str);
+                                        }
+                                        other => throw_span_err!(
+                                            arg.span().unwrap(),
+                                            &format!(
+                                                "`{}` is not a valid key for `#[suggestion(...)]`",
+                                                other
+                                            )
+                                        ),
+                                    }
+                                }
+                            }
+                        }
+                        let msg = if let Some(msg) = msg {
+                            quote!(#msg.as_str())
+                        } else {
+                            throw_span_err!(
+                                list.span().unwrap(),
+                                "missing suggestion message",
+                                |diag| {
+                                    diag.help("provide a suggestion message using #[suggestion(message = \"...\")]")
+                                }
+                            );
+                        };
+                        let code = code.unwrap_or_else(|| quote! { String::new() });
+                        // Now build it out:
+                        let suggestion_method = format_ident!("span_{}", suggestion_kind);
+                        quote! {
+                            #diag.#suggestion_method(#span, #msg, #code, #applicability);
+                        }
+                    }
+                    other => throw_span_err!(
+                        list.span().unwrap(),
+                        &format!("invalid annotation list `#[{}(...)]`", other)
+                    ),
+                }
+            }
+            _ => panic!("unhandled meta kind"),
+        })
+    }
+
+    /// In the strings in the attributes supplied to this macro, we want callers to be able to
+    /// reference fields in the format string. Take this, for example:
+    /// ```ignore (not-usage-example)
+    /// struct Point {
+    ///     #[error = "Expected a point greater than ({x}, {y})"]
+    ///     x: i32,
+    ///     y: i32,
+    /// }
+    /// ```
+    /// We want to automatically pick up that {x} refers `self.x` and {y} refers to `self.y`, then
+    /// generate this call to format!:
+    /// ```ignore (not-usage-example)
+    /// format!("Expected a point greater than ({x}, {y})", x = self.x, y = self.y)
+    /// ```
+    /// This function builds the entire call to format!.
+    fn build_format(&self, input: &String, span: proc_macro2::Span) -> proc_macro2::TokenStream {
+        // This set is used later to generate the final format string. To keep builds reproducible,
+        // the iteration order needs to be deterministic, hence why we use a BTreeSet here instead
+        // of a HashSet.
+        let mut referenced_fields: BTreeSet<String> = BTreeSet::new();
+
+        // At this point, we can start parsing the format string.
+        let mut it = input.chars().peekable();
+        // Once the start of a format string has been found, process the format string and spit out
+        // the referenced fields. Leaves `it` sitting on the closing brace of the format string, so the
+        // next call to `it.next()` retrieves the next character.
+        while let Some(c) = it.next() {
+            if c == '{' && *it.peek().unwrap_or(&'\0') != '{' {
+                #[must_use]
+                let mut eat_argument = || -> Option<String> {
+                    let mut result = String::new();
+                    // Format specifiers look like
+                    // format   := '{' [ argument ] [ ':' format_spec ] '}' .
+                    // Therefore, we only need to eat until ':' or '}' to find the argument.
+                    while let Some(c) = it.next() {
+                        result.push(c);
+                        let next = *it.peek().unwrap_or(&'\0');
+                        if next == '}' {
+                            break;
+                        } else if next == ':' {
+                            // Eat the ':' character.
+                            assert_eq!(it.next().unwrap(), ':');
+                            break;
+                        }
+                    }
+                    // Eat until (and including) the matching '}'
+                    while it.next()? != '}' {
+                        continue;
+                    }
+                    Some(result)
+                };
+
+                if let Some(referenced_field) = eat_argument() {
+                    referenced_fields.insert(referenced_field);
+                }
+            }
+        }
+        // At this point, `referenced_fields` contains a set of the unique fields that were
+        // referenced in the format string. Generate the corresponding "x = self.x" format
+        // string parameters:
+        let args = referenced_fields.into_iter().map(|field: String| {
+            let field_ident = format_ident!("{}", field);
+            let value = if self.fields.contains_key(&field) {
+                quote! {
+                    &self.#field_ident
+                }
+            } else {
+                // This field doesn't exist. Emit a diagnostic.
+                Diagnostic::spanned(
+                    span.unwrap(),
+                    proc_macro::Level::Error,
+                    format!("`{}` doesn't refer to a field on this type", field),
+                )
+                .emit();
+                quote! {
+                    "{#field}"
+                }
+            };
+            quote! {
+                #field_ident = #value
+            }
+        });
+        quote! {
+            format!(#input #(,#args)*)
+        }
+    }
+}
+
+/// If `ty` is an Option, returns Some(inner type). Else, returns None.
+fn option_inner_ty(ty: &syn::Type) -> Option<&syn::Type> {
+    if type_matches_path(ty, &["std", "option", "Option"]) {
+        if let syn::Type::Path(ty_path) = ty {
+            let path = &ty_path.path;
+            let ty = path.segments.iter().last().unwrap();
+            if let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments {
+                if bracketed.args.len() == 1 {
+                    if let syn::GenericArgument::Type(ty) = &bracketed.args[0] {
+                        return Some(ty);
+                    }
+                }
+            }
+        }
+    }
+    None
+}
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index 36ff65fc5eb..94abfac19c6 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -18,7 +18,7 @@ use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::utils::NativeLibKind;
 use rustc_session::{CrateDisambiguator, Session};
-use rustc_span::source_map::{self, Span, Spanned};
+use rustc_span::source_map::{Span, Spanned};
 use rustc_span::symbol::Symbol;
 
 use rustc_data_structures::sync::Lrc;
@@ -421,7 +421,11 @@ impl CStore {
                 span,
                 attrs: attrs.to_vec(),
                 kind: ast::ItemKind::MacroDef(data.get_macro(id.index, sess)),
-                vis: source_map::respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited),
+                vis: ast::Visibility {
+                    span: span.shrink_to_lo(),
+                    kind: ast::VisibilityKind::Inherited,
+                    tokens: None,
+                },
                 tokens: None,
             },
             data.root.edition,
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index 302a907538c..a5a860a38b3 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -26,7 +26,6 @@ rustc_index = { path = "../rustc_index" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_span = { path = "../rustc_span" }
-byteorder = { version = "1.3" }
 chalk-ir = "0.21.0"
 smallvec = { version = "1.0", features = ["union", "may_dangle"] }
 measureme = "0.7.1"
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index 505939d56ed..ee1ea816e01 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -345,10 +345,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
 
     /// Reads a *non-ZST* scalar.
     ///
-    /// ZSTs can't be read for two reasons:
-    /// * byte-order cannot work with zero-element buffers;
-    /// * in order to obtain a `Pointer`, we need to check for ZSTness anyway due to integer
-    ///   pointers being valid for ZSTs.
+    /// ZSTs can't be read because in order to obtain a `Pointer`, we need to check
+    /// for ZSTness anyway due to integer pointers being valid for ZSTs.
     ///
     /// It is the caller's responsibility to check bounds and alignment beforehand.
     /// Most likely, you want to call `InterpCx::read_scalar` instead of this method.
@@ -397,10 +395,8 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
 
     /// Writes a *non-ZST* scalar.
     ///
-    /// ZSTs can't be read for two reasons:
-    /// * byte-order cannot work with zero-element buffers;
-    /// * in order to obtain a `Pointer`, we need to check for ZSTness anyway due to integer
-    ///   pointers being valid for ZSTs.
+    /// ZSTs can't be read because in order to obtain a `Pointer`, we need to check
+    /// for ZSTness anyway due to integer pointers being valid for ZSTs.
     ///
     /// It is the caller's responsibility to check bounds and alignment beforehand.
     /// Most likely, you want to call `InterpCx::write_scalar` instead of this method.
diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs
index c7e32dd0708..cbc362d934f 100644
--- a/compiler/rustc_middle/src/mir/interpret/mod.rs
+++ b/compiler/rustc_middle/src/mir/interpret/mod.rs
@@ -98,10 +98,10 @@ mod value;
 use std::convert::TryFrom;
 use std::fmt;
 use std::io;
+use std::io::{Read, Write};
 use std::num::NonZeroU32;
 use std::sync::atomic::{AtomicU32, Ordering};
 
-use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
 use rustc_ast::LitKind;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::{HashMapExt, Lock};
@@ -561,19 +561,33 @@ pub fn write_target_uint(
     mut target: &mut [u8],
     data: u128,
 ) -> Result<(), io::Error> {
-    let len = target.len();
+    // This u128 holds an "any-size uint" (since smaller uints can fits in it)
+    // So we do not write all bytes of the u128, just the "payload".
     match endianness {
-        Endian::Little => target.write_uint128::<LittleEndian>(data, len),
-        Endian::Big => target.write_uint128::<BigEndian>(data, len),
-    }
+        Endian::Little => target.write(&data.to_le_bytes())?,
+        Endian::Big => target.write(&data.to_be_bytes()[16 - target.len()..])?,
+    };
+    debug_assert!(target.len() == 0); // We should have filled the target buffer.
+    Ok(())
 }
 
 #[inline]
 pub fn read_target_uint(endianness: Endian, mut source: &[u8]) -> Result<u128, io::Error> {
-    match endianness {
-        Endian::Little => source.read_uint128::<LittleEndian>(source.len()),
-        Endian::Big => source.read_uint128::<BigEndian>(source.len()),
-    }
+    // This u128 holds an "any-size uint" (since smaller uints can fits in it)
+    let mut buf = [0u8; std::mem::size_of::<u128>()];
+    // So we do not read exactly 16 bytes into the u128, just the "payload".
+    let uint = match endianness {
+        Endian::Little => {
+            source.read(&mut buf)?;
+            Ok(u128::from_le_bytes(buf))
+        }
+        Endian::Big => {
+            source.read(&mut buf[16 - source.len()..])?;
+            Ok(u128::from_be_bytes(buf))
+        }
+    };
+    debug_assert!(source.len() == 0); // We should have consumed the source buffer.
+    uint
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 1181ba6bbf9..d32a7a4062e 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -186,6 +186,23 @@ pub struct Body<'tcx> {
     /// FIXME(oli-obk): rewrite the promoted during promotion to eliminate the cell components.
     pub ignore_interior_mut_in_const_validation: bool,
 
+    /// Does this body use generic parameters. This is used for the `ConstEvaluatable` check.
+    ///
+    /// Note that this does not actually mean that this body is not computable right now.
+    /// The repeat count in the following example is polymorphic, but can still be evaluated
+    /// without knowing anything about the type parameter `T`.
+    ///
+    /// ```rust
+    /// fn test<T>() {
+    ///     let _ = [0; std::mem::size_of::<*mut T>()];
+    /// }
+    /// ```
+    ///
+    /// **WARNING**: Do not change this flags after the MIR was originally created, even if an optimization
+    /// removed the last mention of all generic params. We do not want to rely on optimizations and
+    /// potentially allow things like `[u8; std::mem::size_of::<T>() * 0]` due to this.
+    pub is_polymorphic: bool,
+
     predecessor_cache: PredecessorCache,
 }
 
@@ -208,7 +225,7 @@ impl<'tcx> Body<'tcx> {
             local_decls.len()
         );
 
-        Body {
+        let mut body = Body {
             phase: MirPhase::Build,
             basic_blocks,
             source_scopes,
@@ -224,8 +241,11 @@ impl<'tcx> Body<'tcx> {
             span,
             required_consts: Vec::new(),
             ignore_interior_mut_in_const_validation: false,
+            is_polymorphic: false,
             predecessor_cache: PredecessorCache::new(),
-        }
+        };
+        body.is_polymorphic = body.has_param_types_or_consts();
+        body
     }
 
     /// Returns a partially initialized MIR body containing only a list of basic blocks.
@@ -234,7 +254,7 @@ impl<'tcx> Body<'tcx> {
     /// is only useful for testing but cannot be `#[cfg(test)]` because it is used in a different
     /// crate.
     pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self {
-        Body {
+        let mut body = Body {
             phase: MirPhase::Build,
             basic_blocks,
             source_scopes: IndexVec::new(),
@@ -250,8 +270,11 @@ impl<'tcx> Body<'tcx> {
             generator_kind: None,
             var_debug_info: Vec::new(),
             ignore_interior_mut_in_const_validation: false,
+            is_polymorphic: false,
             predecessor_cache: PredecessorCache::new(),
-        }
+        };
+        body.is_polymorphic = body.has_param_types_or_consts();
+        body
     }
 
     #[inline]
@@ -899,6 +922,8 @@ pub enum LocalInfo<'tcx> {
     User(ClearCrossCrate<BindingForm<'tcx>>),
     /// A temporary created that references the static with the given `DefId`.
     StaticRef { def_id: DefId, is_thread_local: bool },
+    /// A temporary created that references the const with the given `DefId`
+    ConstRef { def_id: DefId },
 }
 
 impl<'tcx> LocalDecl<'tcx> {
@@ -1954,6 +1979,15 @@ impl<'tcx> Operand<'tcx> {
             Operand::Constant(_) => None,
         }
     }
+
+    /// Returns the `Constant` that is the target of this `Operand`, or `None` if this `Operand` is a
+    /// place.
+    pub fn constant(&self) -> Option<&Constant<'tcx>> {
+        match self {
+            Operand::Constant(x) => Some(&**x),
+            Operand::Copy(_) | Operand::Move(_) => None,
+        }
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////
diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs
index 6bb6abe0289..ad2eae0298c 100644
--- a/compiler/rustc_middle/src/mir/type_foldable.rs
+++ b/compiler/rustc_middle/src/mir/type_foldable.rs
@@ -175,7 +175,7 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> {
         use crate::mir::Rvalue::*;
         match *self {
             Use(ref op) => Use(op.fold_with(folder)),
-            Repeat(ref op, len) => Repeat(op.fold_with(folder), len),
+            Repeat(ref op, len) => Repeat(op.fold_with(folder), len.fold_with(folder)),
             ThreadLocalRef(did) => ThreadLocalRef(did.fold_with(folder)),
             Ref(region, bk, ref place) => {
                 Ref(region.fold_with(folder), bk, place.fold_with(folder))
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 6515ae31b46..a008bd5f75f 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -1150,8 +1150,6 @@ pub enum NonUseContext {
     StorageDead,
     /// User type annotation assertions for NLL.
     AscribeUserTy,
-    /// Coverage code region and counter metadata.
-    Coverage,
     /// The data of an user variable, for debug info.
     VarDebugInfo,
 }
diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs
index ffa6e6f5324..f9cadb3bb2d 100644
--- a/compiler/rustc_middle/src/traits/query.rs
+++ b/compiler/rustc_middle/src/traits/query.rs
@@ -190,74 +190,6 @@ impl<'tcx> FromIterator<DtorckConstraint<'tcx>> for DtorckConstraint<'tcx> {
     }
 }
 
-/// This returns true if the type `ty` is "trivial" for
-/// dropck-outlives -- that is, if it doesn't require any types to
-/// outlive. This is similar but not *quite* the same as the
-/// `needs_drop` test in the compiler already -- that is, for every
-/// type T for which this function return true, needs-drop would
-/// return `false`. But the reverse does not hold: in particular,
-/// `needs_drop` returns false for `PhantomData`, but it is not
-/// trivial for dropck-outlives.
-///
-/// Note also that `needs_drop` requires a "global" type (i.e., one
-/// with erased regions), but this function does not.
-pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
-    match ty.kind() {
-        // None of these types have a destructor and hence they do not
-        // require anything in particular to outlive the dtor's
-        // execution.
-        ty::Infer(ty::FreshIntTy(_))
-        | ty::Infer(ty::FreshFloatTy(_))
-        | ty::Bool
-        | ty::Int(_)
-        | ty::Uint(_)
-        | ty::Float(_)
-        | ty::Never
-        | ty::FnDef(..)
-        | ty::FnPtr(_)
-        | ty::Char
-        | ty::GeneratorWitness(..)
-        | ty::RawPtr(_)
-        | ty::Ref(..)
-        | ty::Str
-        | ty::Foreign(..)
-        | ty::Error(_) => true,
-
-        // [T; N] and [T] have same properties as T.
-        ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty),
-
-        // (T1..Tn) and closures have same properties as T1..Tn --
-        // check if *any* of those are trivial.
-        ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())),
-        ty::Closure(_, ref substs) => {
-            substs.as_closure().upvar_tys().all(|t| trivial_dropck_outlives(tcx, t))
-        }
-
-        ty::Adt(def, _) => {
-            if Some(def.did) == tcx.lang_items().manually_drop() {
-                // `ManuallyDrop` never has a dtor.
-                true
-            } else {
-                // Other types might. Moreover, PhantomData doesn't
-                // have a dtor, but it is considered to own its
-                // content, so it is non-trivial. Unions can have `impl Drop`,
-                // and hence are non-trivial as well.
-                false
-            }
-        }
-
-        // The following *might* require a destructor: needs deeper inspection.
-        ty::Dynamic(..)
-        | ty::Projection(..)
-        | ty::Param(_)
-        | ty::Opaque(..)
-        | ty::Placeholder(..)
-        | ty::Infer(_)
-        | ty::Bound(..)
-        | ty::Generator(..) => false,
-    }
-}
-
 #[derive(Debug, HashStable)]
 pub struct CandidateStep<'tcx> {
     pub self_ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs
index 86fe3ac3751..9d5b558234b 100644
--- a/compiler/rustc_middle/src/ty/trait_def.rs
+++ b/compiler/rustc_middle/src/ty/trait_def.rs
@@ -167,7 +167,7 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
-    /// Returns a vector containing all impls
+    /// Returns an iterator containing all impls
     pub fn all_impls(self, def_id: DefId) -> impl Iterator<Item = DefId> + 'tcx {
         let TraitImpls { blanket_impls, non_blanket_impls } = self.trait_impls_of(def_id);
 
diff --git a/compiler/rustc_mir/Cargo.toml b/compiler/rustc_mir/Cargo.toml
index 6b0412ece7a..0a22bc7d762 100644
--- a/compiler/rustc_mir/Cargo.toml
+++ b/compiler/rustc_mir/Cargo.toml
@@ -14,6 +14,7 @@ itertools = "0.8"
 tracing = "0.1"
 log_settings = "0.1.1"
 polonius-engine = "0.12.0"
+regex = "1"
 rustc_middle = { path = "../rustc_middle" }
 rustc_attr = { path = "../rustc_attr" }
 rustc_data_structures = { path = "../rustc_data_structures" }
diff --git a/compiler/rustc_mir/src/borrow_check/def_use.rs b/compiler/rustc_mir/src/borrow_check/def_use.rs
index 6574e584406..689ec249a2f 100644
--- a/compiler/rustc_mir/src/borrow_check/def_use.rs
+++ b/compiler/rustc_mir/src/borrow_check/def_use.rs
@@ -72,8 +72,7 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> {
         PlaceContext::MutatingUse(MutatingUseContext::Drop) =>
             Some(DefUse::Drop),
 
-        // Coverage and debug info are neither def nor use.
-        PlaceContext::NonUse(NonUseContext::Coverage) |
+        // Debug info is neither def nor use.
         PlaceContext::NonUse(NonUseContext::VarDebugInfo) => None,
     }
 }
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
index 88ff0271228..3cee32834be 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
@@ -150,8 +150,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             Some(mut descr) => {
                 // Surround descr with `backticks`.
                 descr.reserve(2);
-                descr.insert_str(0, "`");
-                descr.push_str("`");
+                descr.insert(0, '`');
+                descr.push('`');
                 descr
             }
             None => "value".to_string(),
@@ -222,7 +222,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             if self.upvars[var_index].by_ref {
                                 buf.push_str(&name);
                             } else {
-                                buf.push_str(&format!("*{}", &name));
+                                buf.push('*');
+                                buf.push_str(&name);
                             }
                         } else {
                             if autoderef {
@@ -234,7 +235,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                                     &including_downcast,
                                 )?;
                             } else {
-                                buf.push_str(&"*");
+                                buf.push('*');
                                 self.append_place_to_string(
                                     PlaceRef { local, projection: proj_base },
                                     buf,
@@ -272,7 +273,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                                 autoderef,
                                 &including_downcast,
                             )?;
-                            buf.push_str(&format!(".{}", field_name));
+                            buf.push('.');
+                            buf.push_str(&field_name);
                         }
                     }
                     ProjectionElem::Index(index) => {
@@ -284,11 +286,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             autoderef,
                             &including_downcast,
                         )?;
-                        buf.push_str("[");
+                        buf.push('[');
                         if self.append_local_to_string(*index, buf).is_err() {
-                            buf.push_str("_");
+                            buf.push('_');
                         }
-                        buf.push_str("]");
+                        buf.push(']');
                     }
                     ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
                         autoderef = true;
@@ -301,7 +303,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             autoderef,
                             &including_downcast,
                         )?;
-                        buf.push_str(&"[..]");
+                        buf.push_str("[..]");
                     }
                 };
             }
@@ -648,7 +650,7 @@ impl UseSpans {
                     " in closure".to_string()
                 }
             }
-            _ => "".to_string(),
+            _ => String::new(),
         }
     }
 
@@ -804,68 +806,51 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         debug!("move_spans: target_temp = {:?}", target_temp);
 
         if let Some(Terminator {
-            kind: TerminatorKind::Call { func, args, fn_span, from_hir_call, .. },
-            ..
+            kind: TerminatorKind::Call { fn_span, from_hir_call, .. }, ..
         }) = &self.body[location.block].terminator
         {
-            let mut method_did = None;
-            if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func {
-                if let ty::FnDef(def_id, _) = *ty.kind() {
-                    debug!("move_spans: fn = {:?}", def_id);
-                    if let Some(ty::AssocItem { fn_has_self_parameter, .. }) =
-                        self.infcx.tcx.opt_associated_item(def_id)
-                    {
-                        if *fn_has_self_parameter {
-                            method_did = Some(def_id);
-                        }
-                    }
-                }
-            }
+            let method_did = if let Some(method_did) =
+                crate::util::find_self_call(self.infcx.tcx, &self.body, target_temp, location.block)
+            {
+                method_did
+            } else {
+                return normal_ret;
+            };
 
             let tcx = self.infcx.tcx;
-            let method_did = if let Some(did) = method_did { did } else { return normal_ret };
-
-            if let [Operand::Move(self_place), ..] = **args {
-                if self_place.as_local() == Some(target_temp) {
-                    let parent = tcx.parent(method_did);
-                    let is_fn_once = parent == tcx.lang_items().fn_once_trait();
-                    let is_operator = !from_hir_call
-                        && parent.map_or(false, |p| {
-                            tcx.lang_items().group(LangItemGroup::Op).contains(&p)
-                        });
-                    let fn_call_span = *fn_span;
-
-                    let self_arg = tcx.fn_arg_names(method_did)[0];
-
-                    let kind = if is_fn_once {
-                        FnSelfUseKind::FnOnceCall
-                    } else if is_operator {
-                        FnSelfUseKind::Operator { self_arg }
-                    } else {
-                        debug!(
-                            "move_spans: method_did={:?}, fn_call_span={:?}",
-                            method_did, fn_call_span
-                        );
-                        let implicit_into_iter = matches!(
-                            fn_call_span.desugaring_kind(),
-                            Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter))
-                        );
-                        FnSelfUseKind::Normal { self_arg, implicit_into_iter }
-                    };
 
-                    return FnSelfUse {
-                        var_span: stmt.source_info.span,
-                        fn_call_span,
-                        fn_span: self
-                            .infcx
-                            .tcx
-                            .sess
-                            .source_map()
-                            .guess_head_span(self.infcx.tcx.def_span(method_did)),
-                        kind,
-                    };
-                }
-            }
+            let parent = tcx.parent(method_did);
+            let is_fn_once = parent == tcx.lang_items().fn_once_trait();
+            let is_operator = !from_hir_call
+                && parent.map_or(false, |p| tcx.lang_items().group(LangItemGroup::Op).contains(&p));
+            let fn_call_span = *fn_span;
+
+            let self_arg = tcx.fn_arg_names(method_did)[0];
+
+            let kind = if is_fn_once {
+                FnSelfUseKind::FnOnceCall
+            } else if is_operator {
+                FnSelfUseKind::Operator { self_arg }
+            } else {
+                debug!("move_spans: method_did={:?}, fn_call_span={:?}", method_did, fn_call_span);
+                let implicit_into_iter = matches!(
+                    fn_call_span.desugaring_kind(),
+                    Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter))
+                );
+                FnSelfUseKind::Normal { self_arg, implicit_into_iter }
+            };
+
+            return FnSelfUse {
+                var_span: stmt.source_info.span,
+                fn_call_span,
+                fn_span: self
+                    .infcx
+                    .tcx
+                    .sess
+                    .source_map()
+                    .guess_head_span(self.infcx.tcx.def_span(method_did)),
+                kind,
+            };
         }
         normal_ret
     }
diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/values.rs b/compiler/rustc_mir/src/borrow_check/region_infer/values.rs
index 8a5a600cfdd..f247d07e1f0 100644
--- a/compiler/rustc_mir/src/borrow_check/region_infer/values.rs
+++ b/compiler/rustc_mir/src/borrow_check/region_infer/values.rs
@@ -417,7 +417,7 @@ crate fn location_set_str(
 
 fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String {
     let mut result = String::new();
-    result.push_str("{");
+    result.push('{');
 
     // Set to Some(l1, l2) when we have observed all the locations
     // from l1..=l2 (inclusive) but not yet printed them. This
@@ -478,7 +478,7 @@ fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String
         push_location_range(&mut result, location1, location2);
     }
 
-    result.push_str("}");
+    result.push('}');
 
     return result;
 
diff --git a/compiler/rustc_mir/src/dataflow/framework/cursor.rs b/compiler/rustc_mir/src/dataflow/framework/cursor.rs
index 4f5930dc3f5..4942bed656c 100644
--- a/compiler/rustc_mir/src/dataflow/framework/cursor.rs
+++ b/compiler/rustc_mir/src/dataflow/framework/cursor.rs
@@ -4,6 +4,7 @@ use std::borrow::Borrow;
 use std::cmp::Ordering;
 
 use rustc_index::bit_set::BitSet;
+use rustc_index::vec::Idx;
 use rustc_middle::mir::{self, BasicBlock, Location};
 
 use super::{Analysis, Direction, Effect, EffectIndex, Results};
@@ -26,7 +27,7 @@ where
 {
     body: &'mir mir::Body<'tcx>,
     results: R,
-    state: BitSet<A::Idx>,
+    state: A::Domain,
 
     pos: CursorPosition,
 
@@ -46,17 +47,16 @@ where
 {
     /// Returns a new cursor that can inspect `results`.
     pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self {
-        let bits_per_block = results.borrow().entry_set_for_block(mir::START_BLOCK).domain_size();
-
+        let bottom_value = results.borrow().analysis.bottom_value(body);
         ResultsCursor {
             body,
             results,
 
-            // Initialize to an empty `BitSet` and set `state_needs_reset` to tell the cursor that
+            // Initialize to the `bottom_value` and set `state_needs_reset` to tell the cursor that
             // it needs to reset to block entry before the first seek. The cursor position is
             // immaterial.
             state_needs_reset: true,
-            state: BitSet::new_empty(bits_per_block),
+            state: bottom_value,
             pos: CursorPosition::block_entry(mir::START_BLOCK),
 
             #[cfg(debug_assertions)]
@@ -68,23 +68,21 @@ where
         self.body
     }
 
-    /// Returns the `Analysis` used to generate the underlying results.
+    /// Returns the underlying `Results`.
+    pub fn results(&self) -> &Results<'tcx, A> {
+        &self.results.borrow()
+    }
+
+    /// Returns the `Analysis` used to generate the underlying `Results`.
     pub fn analysis(&self) -> &A {
         &self.results.borrow().analysis
     }
 
     /// Returns the dataflow state at the current location.
-    pub fn get(&self) -> &BitSet<A::Idx> {
+    pub fn get(&self) -> &A::Domain {
         &self.state
     }
 
-    /// Returns `true` if the dataflow state at the current location contains the given element.
-    ///
-    /// Shorthand for `self.get().contains(elem)`
-    pub fn contains(&self, elem: A::Idx) -> bool {
-        self.state.contains(elem)
-    }
-
     /// Resets the cursor to hold the entry set for the given basic block.
     ///
     /// For forward dataflow analyses, this is the dataflow state prior to the first statement.
@@ -94,7 +92,7 @@ where
         #[cfg(debug_assertions)]
         assert!(self.reachable_blocks.contains(block));
 
-        self.state.overwrite(&self.results.borrow().entry_set_for_block(block));
+        self.state.clone_from(&self.results.borrow().entry_set_for_block(block));
         self.pos = CursorPosition::block_entry(block);
         self.state_needs_reset = false;
     }
@@ -202,12 +200,23 @@ where
     ///
     /// This can be used, e.g., to apply the call return effect directly to the cursor without
     /// creating an extra copy of the dataflow state.
-    pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut BitSet<A::Idx>)) {
+    pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut A::Domain)) {
         f(&self.results.borrow().analysis, &mut self.state);
         self.state_needs_reset = true;
     }
 }
 
+impl<'mir, 'tcx, A, R, T> ResultsCursor<'mir, 'tcx, A, R>
+where
+    A: Analysis<'tcx, Domain = BitSet<T>>,
+    T: Idx,
+    R: Borrow<Results<'tcx, A>>,
+{
+    pub fn contains(&self, elem: T) -> bool {
+        self.get().contains(elem)
+    }
+}
+
 #[derive(Clone, Copy, Debug)]
 struct CursorPosition {
     block: BasicBlock,
diff --git a/compiler/rustc_mir/src/dataflow/framework/direction.rs b/compiler/rustc_mir/src/dataflow/framework/direction.rs
index 1a3b13f0d1f..76c48100371 100644
--- a/compiler/rustc_mir/src/dataflow/framework/direction.rs
+++ b/compiler/rustc_mir/src/dataflow/framework/direction.rs
@@ -18,7 +18,7 @@ pub trait Direction {
     /// `effects.start()` must precede or equal `effects.end()` in this direction.
     fn apply_effects_in_range<A>(
         analysis: &A,
-        state: &mut BitSet<A::Idx>,
+        state: &mut A::Domain,
         block: BasicBlock,
         block_data: &mir::BasicBlockData<'tcx>,
         effects: RangeInclusive<EffectIndex>,
@@ -27,7 +27,7 @@ pub trait Direction {
 
     fn apply_effects_in_block<A>(
         analysis: &A,
-        state: &mut BitSet<A::Idx>,
+        state: &mut A::Domain,
         block: BasicBlock,
         block_data: &mir::BasicBlockData<'tcx>,
     ) where
@@ -55,9 +55,9 @@ pub trait Direction {
         tcx: TyCtxt<'tcx>,
         body: &mir::Body<'tcx>,
         dead_unwinds: Option<&BitSet<BasicBlock>>,
-        exit_state: &mut BitSet<A::Idx>,
+        exit_state: &mut A::Domain,
         block: (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
-        propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>),
+        propagate: impl FnMut(BasicBlock, &A::Domain),
     ) where
         A: Analysis<'tcx>;
 }
@@ -72,7 +72,7 @@ impl Direction for Backward {
 
     fn apply_effects_in_block<A>(
         analysis: &A,
-        state: &mut BitSet<A::Idx>,
+        state: &mut A::Domain,
         block: BasicBlock,
         block_data: &mir::BasicBlockData<'tcx>,
     ) where
@@ -112,7 +112,7 @@ impl Direction for Backward {
 
     fn apply_effects_in_range<A>(
         analysis: &A,
-        state: &mut BitSet<A::Idx>,
+        state: &mut A::Domain,
         block: BasicBlock,
         block_data: &mir::BasicBlockData<'tcx>,
         effects: RangeInclusive<EffectIndex>,
@@ -224,9 +224,9 @@ impl Direction for Backward {
         _tcx: TyCtxt<'tcx>,
         body: &mir::Body<'tcx>,
         dead_unwinds: Option<&BitSet<BasicBlock>>,
-        exit_state: &mut BitSet<A::Idx>,
+        exit_state: &mut A::Domain,
         (bb, _bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
-        mut propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>),
+        mut propagate: impl FnMut(BasicBlock, &A::Domain),
     ) where
         A: Analysis<'tcx>,
     {
@@ -281,7 +281,7 @@ impl Direction for Forward {
 
     fn apply_effects_in_block<A>(
         analysis: &A,
-        state: &mut BitSet<A::Idx>,
+        state: &mut A::Domain,
         block: BasicBlock,
         block_data: &mir::BasicBlockData<'tcx>,
     ) where
@@ -321,7 +321,7 @@ impl Direction for Forward {
 
     fn apply_effects_in_range<A>(
         analysis: &A,
-        state: &mut BitSet<A::Idx>,
+        state: &mut A::Domain,
         block: BasicBlock,
         block_data: &mir::BasicBlockData<'tcx>,
         effects: RangeInclusive<EffectIndex>,
@@ -428,9 +428,9 @@ impl Direction for Forward {
         tcx: TyCtxt<'tcx>,
         body: &mir::Body<'tcx>,
         dead_unwinds: Option<&BitSet<BasicBlock>>,
-        exit_state: &mut BitSet<A::Idx>,
+        exit_state: &mut A::Domain,
         (bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
-        mut propagate: impl FnMut(BasicBlock, &BitSet<A::Idx>),
+        mut propagate: impl FnMut(BasicBlock, &A::Domain),
     ) where
         A: Analysis<'tcx>,
     {
@@ -499,7 +499,7 @@ impl Direction for Forward {
                         // MIR building adds discriminants to the `values` array in the same order as they
                         // are yielded by `AdtDef::discriminants`. We rely on this to match each
                         // discriminant in `values` to its corresponding variant in linear time.
-                        let mut tmp = BitSet::new_empty(exit_state.domain_size());
+                        let mut tmp = analysis.bottom_value(body);
                         let mut discriminants = enum_def.discriminants(tcx);
                         for (value, target) in values.iter().zip(targets.iter().copied()) {
                             let (variant_idx, _) =
@@ -508,7 +508,7 @@ impl Direction for Forward {
                                          from that of `SwitchInt::values`",
                                 );
 
-                            tmp.overwrite(exit_state);
+                            tmp.clone_from(exit_state);
                             analysis.apply_discriminant_switch_effect(
                                 &mut tmp,
                                 bb,
diff --git a/compiler/rustc_mir/src/dataflow/framework/engine.rs b/compiler/rustc_mir/src/dataflow/framework/engine.rs
index b703852b1de..0b5b437d186 100644
--- a/compiler/rustc_mir/src/dataflow/framework/engine.rs
+++ b/compiler/rustc_mir/src/dataflow/framework/engine.rs
@@ -1,5 +1,6 @@
 //! A solver for dataflow problems.
 
+use std::borrow::BorrowMut;
 use std::ffi::OsString;
 use std::fs;
 use std::path::PathBuf;
@@ -9,14 +10,16 @@ use rustc_data_structures::work_queue::WorkQueue;
 use rustc_graphviz as dot;
 use rustc_hir::def_id::DefId;
 use rustc_index::bit_set::BitSet;
-use rustc_index::vec::IndexVec;
+use rustc_index::vec::{Idx, IndexVec};
 use rustc_middle::mir::{self, traversal, BasicBlock};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::symbol::{sym, Symbol};
 
+use super::fmt::DebugWithContext;
 use super::graphviz;
 use super::{
-    visit_results, Analysis, Direction, GenKillAnalysis, GenKillSet, ResultsCursor, ResultsVisitor,
+    visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice,
+    ResultsCursor, ResultsVisitor,
 };
 use crate::util::pretty::dump_enabled;
 
@@ -26,7 +29,7 @@ where
     A: Analysis<'tcx>,
 {
     pub analysis: A,
-    pub(super) entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>,
+    pub(super) entry_sets: IndexVec<BasicBlock, A::Domain>,
 }
 
 impl<A> Results<'tcx, A>
@@ -39,7 +42,7 @@ where
     }
 
     /// Gets the dataflow state for the given block.
-    pub fn entry_set_for_block(&self, block: BasicBlock) -> &BitSet<A::Idx> {
+    pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
         &self.entry_sets[block]
     }
 
@@ -47,7 +50,7 @@ where
         &self,
         body: &'mir mir::Body<'tcx>,
         blocks: impl IntoIterator<Item = BasicBlock>,
-        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet<A::Idx>>,
+        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
     ) {
         visit_results(body, blocks, self, vis)
     }
@@ -55,7 +58,7 @@ where
     pub fn visit_reachable_with(
         &self,
         body: &'mir mir::Body<'tcx>,
-        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet<A::Idx>>,
+        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
     ) {
         let blocks = mir::traversal::reachable(body);
         visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
@@ -64,7 +67,7 @@ where
     pub fn visit_in_rpo_with(
         &self,
         body: &'mir mir::Body<'tcx>,
-        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = BitSet<A::Idx>>,
+        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
     ) {
         let blocks = mir::traversal::reverse_postorder(body);
         visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
@@ -76,21 +79,27 @@ pub struct Engine<'a, 'tcx, A>
 where
     A: Analysis<'tcx>,
 {
-    bits_per_block: usize,
     tcx: TyCtxt<'tcx>,
     body: &'a mir::Body<'tcx>,
     def_id: DefId,
     dead_unwinds: Option<&'a BitSet<BasicBlock>>,
-    entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>,
+    entry_sets: IndexVec<BasicBlock, A::Domain>,
     analysis: A,
 
     /// Cached, cumulative transfer functions for each block.
-    trans_for_block: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>,
+    //
+    // FIXME(ecstaticmorse): This boxed `Fn` trait object is invoked inside a tight loop for
+    // gen/kill problems on cyclic CFGs. This is not ideal, but it doesn't seem to degrade
+    // performance in practice. I've tried a few ways to avoid this, but they have downsides. See
+    // the message for the commit that added this FIXME for more information.
+    apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
 }
 
-impl<A> Engine<'a, 'tcx, A>
+impl<A, D, T> Engine<'a, 'tcx, A>
 where
-    A: GenKillAnalysis<'tcx>,
+    A: GenKillAnalysis<'tcx, Idx = T, Domain = D>,
+    D: Clone + JoinSemiLattice + GenKill<T> + BorrowMut<BitSet<T>>,
+    T: Idx,
 {
     /// Creates a new `Engine` to solve a gen-kill dataflow problem.
     pub fn new_gen_kill(
@@ -109,22 +118,26 @@ where
 
         // Otherwise, compute and store the cumulative transfer function for each block.
 
-        let bits_per_block = analysis.bits_per_block(body);
-        let mut trans_for_block =
-            IndexVec::from_elem(GenKillSet::identity(bits_per_block), body.basic_blocks());
+        let identity = GenKillSet::identity(analysis.bottom_value(body).borrow().domain_size());
+        let mut trans_for_block = IndexVec::from_elem(identity, body.basic_blocks());
 
         for (block, block_data) in body.basic_blocks().iter_enumerated() {
             let trans = &mut trans_for_block[block];
             A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data);
         }
 
-        Self::new(tcx, body, def_id, analysis, Some(trans_for_block))
+        let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| {
+            trans_for_block[bb].apply(state.borrow_mut());
+        });
+
+        Self::new(tcx, body, def_id, analysis, Some(apply_trans as Box<_>))
     }
 }
 
-impl<A> Engine<'a, 'tcx, A>
+impl<A, D> Engine<'a, 'tcx, A>
 where
-    A: Analysis<'tcx>,
+    A: Analysis<'tcx, Domain = D>,
+    D: Clone + JoinSemiLattice,
 {
     /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
     /// function.
@@ -145,32 +158,24 @@ where
         body: &'a mir::Body<'tcx>,
         def_id: DefId,
         analysis: A,
-        trans_for_block: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>,
+        apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
     ) -> Self {
-        let bits_per_block = analysis.bits_per_block(body);
-
-        let bottom_value_set = if A::BOTTOM_VALUE {
-            BitSet::new_filled(bits_per_block)
-        } else {
-            BitSet::new_empty(bits_per_block)
-        };
-
-        let mut entry_sets = IndexVec::from_elem(bottom_value_set.clone(), body.basic_blocks());
+        let bottom_value = analysis.bottom_value(body);
+        let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), body.basic_blocks());
         analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);
 
-        if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value_set {
+        if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value {
             bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
         }
 
         Engine {
             analysis,
-            bits_per_block,
             tcx,
             body,
             def_id,
             dead_unwinds: None,
             entry_sets,
-            trans_for_block,
+            apply_trans_for_block,
         }
     }
 
@@ -185,16 +190,18 @@ where
     }
 
     /// Computes the fixpoint for this dataflow problem and returns it.
-    pub fn iterate_to_fixpoint(self) -> Results<'tcx, A> {
+    pub fn iterate_to_fixpoint(self) -> Results<'tcx, A>
+    where
+        A::Domain: DebugWithContext<A>,
+    {
         let Engine {
             analysis,
-            bits_per_block,
             body,
             dead_unwinds,
             def_id,
             mut entry_sets,
             tcx,
-            trans_for_block,
+            apply_trans_for_block,
             ..
         } = self;
 
@@ -213,14 +220,14 @@ where
             }
         }
 
-        let mut state = BitSet::new_empty(bits_per_block);
+        let mut state = analysis.bottom_value(body);
         while let Some(bb) = dirty_queue.pop() {
             let bb_data = &body[bb];
 
             // Apply the block transfer function, using the cached one if it exists.
-            state.overwrite(&entry_sets[bb]);
-            match &trans_for_block {
-                Some(trans_for_block) => trans_for_block[bb].apply(&mut state),
+            state.clone_from(&entry_sets[bb]);
+            match &apply_trans_for_block {
+                Some(apply) => apply(bb, &mut state),
                 None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data),
             }
 
@@ -231,8 +238,8 @@ where
                 dead_unwinds,
                 &mut state,
                 (bb, bb_data),
-                |target: BasicBlock, state: &BitSet<A::Idx>| {
-                    let set_changed = analysis.join(&mut entry_sets[target], state);
+                |target: BasicBlock, state: &A::Domain| {
+                    let set_changed = entry_sets[target].join(state);
                     if set_changed {
                         dirty_queue.insert(target);
                     }
@@ -242,7 +249,7 @@ where
 
         let results = Results { analysis, entry_sets };
 
-        let res = write_graphviz_results(tcx, def_id, &body, &results, trans_for_block);
+        let res = write_graphviz_results(tcx, def_id, &body, &results);
         if let Err(e) = res {
             warn!("Failed to write graphviz dataflow results: {}", e);
         }
@@ -260,10 +267,10 @@ fn write_graphviz_results<A>(
     def_id: DefId,
     body: &mir::Body<'tcx>,
     results: &Results<'tcx, A>,
-    block_transfer_functions: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>,
 ) -> std::io::Result<()>
 where
     A: Analysis<'tcx>,
+    A::Domain: DebugWithContext<A>,
 {
     let attrs = match RustcMirAttrs::parse(tcx, def_id) {
         Ok(attrs) => attrs,
@@ -290,27 +297,20 @@ where
         None => return Ok(()),
     };
 
-    let bits_per_block = results.analysis.bits_per_block(body);
-
-    let mut formatter: Box<dyn graphviz::StateFormatter<'tcx, _>> = match attrs.formatter {
-        Some(sym::two_phase) => Box::new(graphviz::TwoPhaseDiff::new(bits_per_block)),
-        Some(sym::gen_kill) => {
-            if let Some(trans_for_block) = block_transfer_functions {
-                Box::new(graphviz::BlockTransferFunc::new(body, trans_for_block))
-            } else {
-                Box::new(graphviz::SimpleDiff::new(body, &results))
-            }
-        }
-
-        // Default to the `SimpleDiff` output style.
-        _ => Box::new(graphviz::SimpleDiff::new(body, &results)),
+    let style = match attrs.formatter {
+        Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter,
+        _ => graphviz::OutputStyle::AfterOnly,
     };
 
     debug!("printing dataflow results for {:?} to {}", def_id, path.display());
     let mut buf = Vec::new();
 
-    let graphviz = graphviz::Formatter::new(body, def_id, results, &mut *formatter);
-    dot::render_opts(&graphviz, &mut buf, &[dot::RenderOption::Monospace])?;
+    let graphviz = graphviz::Formatter::new(body, def_id, results, style);
+    let mut render_opts = vec![dot::RenderOption::Monospace];
+    if tcx.sess.opts.debugging_opts.graphviz_dark_mode {
+        render_opts.push(dot::RenderOption::DarkTheme);
+    }
+    dot::render_opts(&graphviz, &mut buf, &render_opts)?;
 
     if let Some(parent) = path.parent() {
         fs::create_dir_all(parent)?;
diff --git a/compiler/rustc_mir/src/dataflow/framework/fmt.rs b/compiler/rustc_mir/src/dataflow/framework/fmt.rs
new file mode 100644
index 00000000000..0140a750544
--- /dev/null
+++ b/compiler/rustc_mir/src/dataflow/framework/fmt.rs
@@ -0,0 +1,172 @@
+//! Custom formatting traits used when outputting Graphviz diagrams with the results of a dataflow
+//! analysis.
+
+use rustc_index::bit_set::{BitSet, HybridBitSet};
+use rustc_index::vec::Idx;
+use std::fmt;
+
+/// An extension to `fmt::Debug` for data that can be better printed with some auxiliary data `C`.
+pub trait DebugWithContext<C>: Eq + fmt::Debug {
+    fn fmt_with(&self, _ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Debug::fmt(self, f)
+    }
+
+    /// Print the difference between `self` and `old`.
+    ///
+    /// This should print nothing if `self == old`.
+    ///
+    /// `+` and `-` are typically used to indicate differences. However, these characters are
+    /// fairly common and may be needed to print a types representation. If using them to indicate
+    /// a diff, prefix them with the "Unit Separator"  control character (␟  U+001F).
+    fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if self == old {
+            return Ok(());
+        }
+
+        write!(f, "\u{001f}+")?;
+        self.fmt_with(ctxt, f)?;
+
+        if f.alternate() {
+            write!(f, "\n")?;
+        } else {
+            write!(f, "\t")?;
+        }
+
+        write!(f, "\u{001f}-")?;
+        self.fmt_with(ctxt, f)
+    }
+}
+
+/// Implements `fmt::Debug` by deferring to `<T as DebugWithContext<C>>::fmt_with`.
+pub struct DebugWithAdapter<'a, T, C> {
+    pub this: T,
+    pub ctxt: &'a C,
+}
+
+impl<T, C> fmt::Debug for DebugWithAdapter<'_, T, C>
+where
+    T: DebugWithContext<C>,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.this.fmt_with(self.ctxt, f)
+    }
+}
+
+/// Implements `fmt::Debug` by deferring to `<T as DebugWithContext<C>>::fmt_diff_with`.
+pub struct DebugDiffWithAdapter<'a, T, C> {
+    pub new: T,
+    pub old: T,
+    pub ctxt: &'a C,
+}
+
+impl<T, C> fmt::Debug for DebugDiffWithAdapter<'_, T, C>
+where
+    T: DebugWithContext<C>,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.new.fmt_diff_with(&self.old, self.ctxt, f)
+    }
+}
+
+// Impls
+
+impl<T, C> DebugWithContext<C> for BitSet<T>
+where
+    T: Idx + DebugWithContext<C>,
+{
+    fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_set().entries(self.iter().map(|i| DebugWithAdapter { this: i, ctxt })).finish()
+    }
+
+    fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let size = self.domain_size();
+        assert_eq!(size, old.domain_size());
+
+        let mut set_in_self = HybridBitSet::new_empty(size);
+        let mut cleared_in_self = HybridBitSet::new_empty(size);
+
+        for i in (0..size).map(T::new) {
+            match (self.contains(i), old.contains(i)) {
+                (true, false) => set_in_self.insert(i),
+                (false, true) => cleared_in_self.insert(i),
+                _ => continue,
+            };
+        }
+
+        let mut first = true;
+        for idx in set_in_self.iter() {
+            let delim = if first {
+                "\u{001f}+"
+            } else if f.alternate() {
+                "\n\u{001f}+"
+            } else {
+                ", "
+            };
+
+            write!(f, "{}", delim)?;
+            idx.fmt_with(ctxt, f)?;
+            first = false;
+        }
+
+        if !f.alternate() {
+            first = true;
+            if !set_in_self.is_empty() && !cleared_in_self.is_empty() {
+                write!(f, "\t")?;
+            }
+        }
+
+        for idx in cleared_in_self.iter() {
+            let delim = if first {
+                "\u{001f}-"
+            } else if f.alternate() {
+                "\n\u{001f}-"
+            } else {
+                ", "
+            };
+
+            write!(f, "{}", delim)?;
+            idx.fmt_with(ctxt, f)?;
+            first = false;
+        }
+
+        Ok(())
+    }
+}
+
+impl<T, C> DebugWithContext<C> for &'_ T
+where
+    T: DebugWithContext<C>,
+{
+    fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        (*self).fmt_with(ctxt, f)
+    }
+
+    fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        (*self).fmt_diff_with(*old, ctxt, f)
+    }
+}
+
+impl<C> DebugWithContext<C> for rustc_middle::mir::Local {}
+impl<C> DebugWithContext<C> for crate::dataflow::move_paths::InitIndex {}
+
+impl<'tcx, C> DebugWithContext<C> for crate::dataflow::move_paths::MovePathIndex
+where
+    C: crate::dataflow::move_paths::HasMoveData<'tcx>,
+{
+    fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", ctxt.move_data().move_paths[*self])
+    }
+}
+
+impl<T, C> DebugWithContext<C> for crate::dataflow::lattice::Dual<T>
+where
+    T: DebugWithContext<C>,
+{
+    fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        (self.0).fmt_with(ctxt, f)
+    }
+
+    fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        (self.0).fmt_diff_with(&old.0, ctxt, f)
+    }
+}
diff --git a/compiler/rustc_mir/src/dataflow/framework/graphviz.rs b/compiler/rustc_mir/src/dataflow/framework/graphviz.rs
index 896616a2175..179c471cf48 100644
--- a/compiler/rustc_mir/src/dataflow/framework/graphviz.rs
+++ b/compiler/rustc_mir/src/dataflow/framework/graphviz.rs
@@ -1,26 +1,40 @@
 //! A helpful diagram for debugging dataflow problems.
 
-use std::cell::RefCell;
+use std::borrow::Cow;
 use std::{io, ops, str};
 
+use regex::Regex;
 use rustc_graphviz as dot;
 use rustc_hir::def_id::DefId;
-use rustc_index::bit_set::{BitSet, HybridBitSet};
-use rustc_index::vec::{Idx, IndexVec};
 use rustc_middle::mir::{self, BasicBlock, Body, Location};
 
-use super::{Analysis, Direction, GenKillSet, Results, ResultsRefCursor};
+use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext};
+use super::{Analysis, Direction, Results, ResultsRefCursor, ResultsVisitor};
 use crate::util::graphviz_safe_def_name;
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum OutputStyle {
+    AfterOnly,
+    BeforeAndAfter,
+}
+
+impl OutputStyle {
+    fn num_state_columns(&self) -> usize {
+        match self {
+            Self::AfterOnly => 1,
+            Self::BeforeAndAfter => 2,
+        }
+    }
+}
+
 pub struct Formatter<'a, 'tcx, A>
 where
     A: Analysis<'tcx>,
 {
     body: &'a Body<'tcx>,
     def_id: DefId,
-
-    // This must be behind a `RefCell` because `dot::Labeller` takes `&self`.
-    block_formatter: RefCell<BlockFormatter<'a, 'tcx, A>>,
+    results: &'a Results<'tcx, A>,
+    style: OutputStyle,
 }
 
 impl<A> Formatter<'a, 'tcx, A>
@@ -31,15 +45,9 @@ where
         body: &'a Body<'tcx>,
         def_id: DefId,
         results: &'a Results<'tcx, A>,
-        state_formatter: &'a mut dyn StateFormatter<'tcx, A>,
+        style: OutputStyle,
     ) -> Self {
-        let block_formatter = BlockFormatter {
-            bg: Background::Light,
-            results: ResultsRefCursor::new(body, results),
-            state_formatter,
-        };
-
-        Formatter { body, def_id, block_formatter: RefCell::new(block_formatter) }
+        Formatter { body, def_id, results, style }
     }
 }
 
@@ -62,6 +70,7 @@ fn dataflow_successors(body: &Body<'tcx>, bb: BasicBlock) -> Vec<CfgEdge> {
 impl<A> dot::Labeller<'_> for Formatter<'a, 'tcx, A>
 where
     A: Analysis<'tcx>,
+    A::Domain: DebugWithContext<A>,
 {
     type Node = BasicBlock;
     type Edge = CfgEdge;
@@ -77,7 +86,13 @@ where
 
     fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
         let mut label = Vec::new();
-        self.block_formatter.borrow_mut().write_node_label(&mut label, self.body, *block).unwrap();
+        let mut fmt = BlockFormatter {
+            results: ResultsRefCursor::new(self.body, self.results),
+            style: self.style,
+            bg: Background::Light,
+        };
+
+        fmt.write_node_label(&mut label, self.body, *block).unwrap();
         dot::LabelText::html(String::from_utf8(label).unwrap())
     }
 
@@ -126,19 +141,16 @@ where
 {
     results: ResultsRefCursor<'a, 'a, 'tcx, A>,
     bg: Background,
-    state_formatter: &'a mut dyn StateFormatter<'tcx, A>,
+    style: OutputStyle,
 }
 
 impl<A> BlockFormatter<'a, 'tcx, A>
 where
     A: Analysis<'tcx>,
+    A::Domain: DebugWithContext<A>,
 {
     const HEADER_COLOR: &'static str = "#a0a0a0";
 
-    fn num_state_columns(&self) -> usize {
-        std::cmp::max(1, self.state_formatter.column_names().len())
-    }
-
     fn toggle_background(&mut self) -> Background {
         let bg = self.bg;
         self.bg = !bg;
@@ -187,40 +199,30 @@ where
         write!(w, r#"<table{fmt}>"#, fmt = table_fmt)?;
 
         // A + B: Block header
-        if self.state_formatter.column_names().is_empty() {
-            self.write_block_header_simple(w, block)?;
-        } else {
-            self.write_block_header_with_state_columns(w, block)?;
+        match self.style {
+            OutputStyle::AfterOnly => self.write_block_header_simple(w, block)?,
+            OutputStyle::BeforeAndAfter => {
+                self.write_block_header_with_state_columns(w, block, &["BEFORE", "AFTER"])?
+            }
         }
 
         // C: State at start of block
         self.bg = Background::Light;
         self.results.seek_to_block_start(block);
-        let block_entry_state = self.results.get().clone();
-
+        let block_start_state = self.results.get().clone();
         self.write_row_with_full_state(w, "", "(on start)")?;
 
-        // D: Statement transfer functions
-        for (i, statement) in body[block].statements.iter().enumerate() {
-            let location = Location { block, statement_index: i };
-            let statement_str = format!("{:?}", statement);
-            self.write_row_for_location(w, &i.to_string(), &statement_str, location)?;
-        }
-
-        // E: Terminator transfer function
-        let terminator = body[block].terminator();
-        let terminator_loc = body.terminator_loc(block);
-        let mut terminator_str = String::new();
-        terminator.kind.fmt_head(&mut terminator_str).unwrap();
-
-        self.write_row_for_location(w, "T", &terminator_str, terminator_loc)?;
+        // D + E: Statement and terminator transfer functions
+        self.write_statements_and_terminator(w, body, block)?;
 
         // F: State at end of block
 
+        let terminator = body[block].terminator();
+
         // Write the full dataflow state immediately after the terminator if it differs from the
         // state at block entry.
         self.results.seek_to_block_end(block);
-        if self.results.get() != &block_entry_state || A::Direction::is_backward() {
+        if self.results.get() != &block_start_state || A::Direction::is_backward() {
             let after_terminator_name = match terminator.kind {
                 mir::TerminatorKind::Call { destination: Some(_), .. } => "(on unwind)",
                 _ => "(on end)",
@@ -229,8 +231,11 @@ where
             self.write_row_with_full_state(w, "", after_terminator_name)?;
         }
 
-        // Write any changes caused by terminator-specific effects
-        let num_state_columns = self.num_state_columns();
+        // Write any changes caused by terminator-specific effects.
+        //
+        // FIXME: These should really be printed as part of each outgoing edge rather than the node
+        // for the basic block itself. That way, we could display terminator-specific effects for
+        // backward dataflow analyses as well as effects for `SwitchInt` terminators.
         match terminator.kind {
             mir::TerminatorKind::Call {
                 destination: Some((return_place, _)),
@@ -239,44 +244,43 @@ where
                 ..
             } => {
                 self.write_row(w, "", "(on successful return)", |this, w, fmt| {
-                    write!(
-                        w,
-                        r#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#,
-                        colspan = num_state_columns,
-                        fmt = fmt,
-                    )?;
-
                     let state_on_unwind = this.results.get().clone();
                     this.results.apply_custom_effect(|analysis, state| {
                         analysis.apply_call_return_effect(state, block, func, args, return_place);
                     });
 
-                    write_diff(w, this.results.analysis(), &state_on_unwind, this.results.get())?;
-                    write!(w, "</td>")
+                    write!(
+                        w,
+                        r#"<td balign="left" colspan="{colspan}" {fmt} align="left">{diff}</td>"#,
+                        colspan = this.style.num_state_columns(),
+                        fmt = fmt,
+                        diff = diff_pretty(
+                            this.results.get(),
+                            &state_on_unwind,
+                            this.results.analysis()
+                        ),
+                    )
                 })?;
             }
 
             mir::TerminatorKind::Yield { resume, resume_arg, .. } => {
                 self.write_row(w, "", "(on yield resume)", |this, w, fmt| {
-                    write!(
-                        w,
-                        r#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#,
-                        colspan = num_state_columns,
-                        fmt = fmt,
-                    )?;
-
                     let state_on_generator_drop = this.results.get().clone();
                     this.results.apply_custom_effect(|analysis, state| {
                         analysis.apply_yield_resume_effect(state, resume, resume_arg);
                     });
 
-                    write_diff(
+                    write!(
                         w,
-                        this.results.analysis(),
-                        &state_on_generator_drop,
-                        this.results.get(),
-                    )?;
-                    write!(w, "</td>")
+                        r#"<td balign="left" colspan="{colspan}" {fmt} align="left">{diff}</td>"#,
+                        colspan = this.style.num_state_columns(),
+                        fmt = fmt,
+                        diff = diff_pretty(
+                            this.results.get(),
+                            &state_on_generator_drop,
+                            this.results.analysis()
+                        ),
+                    )
                 })?;
             }
 
@@ -322,6 +326,7 @@ where
         &mut self,
         w: &mut impl io::Write,
         block: BasicBlock,
+        state_column_names: &[&str],
     ) -> io::Result<()> {
         //   +------------------------------------+-------------+
         // A |                bb4                 |    STATE    |
@@ -330,8 +335,6 @@ where
         //   +-+----------------------------------+------+------+
         //   | |              ...                 |      |      |
 
-        let state_column_names = self.state_formatter.column_names();
-
         // A
         write!(
             w,
@@ -357,6 +360,56 @@ where
         write!(w, "</tr>")
     }
 
+    fn write_statements_and_terminator(
+        &mut self,
+        w: &mut impl io::Write,
+        body: &'a Body<'tcx>,
+        block: BasicBlock,
+    ) -> io::Result<()> {
+        let diffs = StateDiffCollector::run(body, block, self.results.results(), self.style);
+
+        let mut befores = diffs.before.map(|v| v.into_iter());
+        let mut afters = diffs.after.into_iter();
+
+        let next_in_dataflow_order = |it: &mut std::vec::IntoIter<_>| {
+            if A::Direction::is_forward() { it.next().unwrap() } else { it.next_back().unwrap() }
+        };
+
+        for (i, statement) in body[block].statements.iter().enumerate() {
+            let statement_str = format!("{:?}", statement);
+            let index_str = format!("{}", i);
+
+            let after = next_in_dataflow_order(&mut afters);
+            let before = befores.as_mut().map(next_in_dataflow_order);
+
+            self.write_row(w, &index_str, &statement_str, |_this, w, fmt| {
+                if let Some(before) = before {
+                    write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = before)?;
+                }
+
+                write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = after)
+            })?;
+        }
+
+        let after = next_in_dataflow_order(&mut afters);
+        let before = befores.as_mut().map(next_in_dataflow_order);
+
+        assert!(afters.is_empty());
+        assert!(befores.as_ref().map_or(true, ExactSizeIterator::is_empty));
+
+        let terminator = body[block].terminator();
+        let mut terminator_str = String::new();
+        terminator.kind.fmt_head(&mut terminator_str).unwrap();
+
+        self.write_row(w, "T", &terminator_str, |_this, w, fmt| {
+            if let Some(before) = before {
+                write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = before)?;
+            }
+
+            write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = after)
+        })
+    }
+
     /// Write a row with the given index and MIR, using the function argument to fill in the
     /// "STATE" column(s).
     fn write_row<W: io::Write>(
@@ -397,319 +450,169 @@ where
             let state = this.results.get();
             let analysis = this.results.analysis();
 
+            // FIXME: The full state vector can be quite long. It would be nice to split on commas
+            // and use some text wrapping algorithm.
             write!(
                 w,
-                r#"<td colspan="{colspan}" {fmt} align="left">{{"#,
-                colspan = this.num_state_columns(),
+                r#"<td colspan="{colspan}" {fmt} align="left">{state}</td>"#,
+                colspan = this.style.num_state_columns(),
                 fmt = fmt,
-            )?;
-            pretty_print_state_elems(w, analysis, state.iter(), ", ", LIMIT_30_ALIGN_1)?;
-            write!(w, "}}</td>")
-        })
-    }
-
-    fn write_row_for_location(
-        &mut self,
-        w: &mut impl io::Write,
-        i: &str,
-        mir: &str,
-        location: Location,
-    ) -> io::Result<()> {
-        self.write_row(w, i, mir, |this, w, fmt| {
-            this.state_formatter.write_state_for_location(w, fmt, &mut this.results, location)
+                state = format!("{:?}", DebugWithAdapter { this: state, ctxt: analysis }),
+            )
         })
     }
 }
 
-/// Controls what gets printed under the `STATE` header.
-pub trait StateFormatter<'tcx, A>
+struct StateDiffCollector<'a, 'tcx, A>
 where
     A: Analysis<'tcx>,
 {
-    /// The columns that will get printed under `STATE`.
-    fn column_names(&self) -> &[&str];
-
-    fn write_state_for_location(
-        &mut self,
-        w: &mut dyn io::Write,
-        fmt: &str,
-        results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
-        location: Location,
-    ) -> io::Result<()>;
+    analysis: &'a A,
+    prev_state: A::Domain,
+    before: Option<Vec<String>>,
+    after: Vec<String>,
 }
 
-/// Prints a single column containing the state vector immediately *after* each statement.
-pub struct SimpleDiff<'a, 'tcx, A>
+impl<A> StateDiffCollector<'a, 'tcx, A>
 where
     A: Analysis<'tcx>,
+    A::Domain: DebugWithContext<A>,
 {
-    prev_state: ResultsRefCursor<'a, 'a, 'tcx, A>,
-}
+    fn run(
+        body: &'a mir::Body<'tcx>,
+        block: BasicBlock,
+        results: &'a Results<'tcx, A>,
+        style: OutputStyle,
+    ) -> Self {
+        let mut collector = StateDiffCollector {
+            analysis: &results.analysis,
+            prev_state: results.analysis.bottom_value(body),
+            after: vec![],
+            before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]),
+        };
 
-impl<A> SimpleDiff<'a, 'tcx, A>
-where
-    A: Analysis<'tcx>,
-{
-    pub fn new(body: &'a Body<'tcx>, results: &'a Results<'tcx, A>) -> Self {
-        SimpleDiff { prev_state: ResultsRefCursor::new(body, results) }
+        results.visit_with(body, std::iter::once(block), &mut collector);
+        collector
     }
 }
 
-impl<A> StateFormatter<'tcx, A> for SimpleDiff<'_, 'tcx, A>
+impl<A> ResultsVisitor<'a, 'tcx> for StateDiffCollector<'a, 'tcx, A>
 where
     A: Analysis<'tcx>,
+    A::Domain: DebugWithContext<A>,
 {
-    fn column_names(&self) -> &[&str] {
-        &[]
-    }
+    type FlowState = A::Domain;
 
-    fn write_state_for_location(
+    fn visit_block_start(
         &mut self,
-        mut w: &mut dyn io::Write,
-        fmt: &str,
-        results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
-        location: Location,
-    ) -> io::Result<()> {
+        state: &Self::FlowState,
+        _block_data: &'mir mir::BasicBlockData<'tcx>,
+        _block: BasicBlock,
+    ) {
         if A::Direction::is_forward() {
-            if location.statement_index == 0 {
-                self.prev_state.seek_to_block_start(location.block);
-            } else {
-                self.prev_state.seek_after_primary_effect(Location {
-                    statement_index: location.statement_index - 1,
-                    ..location
-                });
-            }
-        } else {
-            if location == results.body().terminator_loc(location.block) {
-                self.prev_state.seek_to_block_end(location.block);
-            } else {
-                self.prev_state.seek_after_primary_effect(location.successor_within_block());
-            }
+            self.prev_state.clone_from(state);
         }
-
-        write!(w, r#"<td {fmt} balign="left" align="left">"#, fmt = fmt)?;
-        results.seek_after_primary_effect(location);
-        let curr_state = results.get();
-        write_diff(&mut w, results.analysis(), self.prev_state.get(), curr_state)?;
-        write!(w, "</td>")
     }
-}
 
-/// Prints two state columns, one containing only the "before" effect of each statement and one
-/// containing the full effect.
-pub struct TwoPhaseDiff<T: Idx> {
-    prev_state: BitSet<T>,
-    prev_loc: Location,
-}
+    fn visit_block_end(
+        &mut self,
+        state: &Self::FlowState,
+        _block_data: &'mir mir::BasicBlockData<'tcx>,
+        _block: BasicBlock,
+    ) {
+        if A::Direction::is_backward() {
+            self.prev_state.clone_from(state);
+        }
+    }
 
-impl<T: Idx> TwoPhaseDiff<T> {
-    pub fn new(bits_per_block: usize) -> Self {
-        TwoPhaseDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START }
+    fn visit_statement_before_primary_effect(
+        &mut self,
+        state: &Self::FlowState,
+        _statement: &'mir mir::Statement<'tcx>,
+        _location: Location,
+    ) {
+        if let Some(before) = self.before.as_mut() {
+            before.push(diff_pretty(state, &self.prev_state, self.analysis));
+            self.prev_state.clone_from(state)
+        }
     }
-}
 
-impl<A> StateFormatter<'tcx, A> for TwoPhaseDiff<A::Idx>
-where
-    A: Analysis<'tcx>,
-{
-    fn column_names(&self) -> &[&str] {
-        &["BEFORE", " AFTER"]
+    fn visit_statement_after_primary_effect(
+        &mut self,
+        state: &Self::FlowState,
+        _statement: &'mir mir::Statement<'tcx>,
+        _location: Location,
+    ) {
+        self.after.push(diff_pretty(state, &self.prev_state, self.analysis));
+        self.prev_state.clone_from(state)
     }
 
-    fn write_state_for_location(
+    fn visit_terminator_before_primary_effect(
         &mut self,
-        mut w: &mut dyn io::Write,
-        fmt: &str,
-        results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
-        location: Location,
-    ) -> io::Result<()> {
-        if location.statement_index == 0 {
-            results.seek_to_block_entry(location.block);
-            self.prev_state.overwrite(results.get());
-        } else {
-            // Ensure that we are visiting statements in order, so `prev_state` is correct.
-            assert_eq!(self.prev_loc.successor_within_block(), location);
+        state: &Self::FlowState,
+        _terminator: &'mir mir::Terminator<'tcx>,
+        _location: Location,
+    ) {
+        if let Some(before) = self.before.as_mut() {
+            before.push(diff_pretty(state, &self.prev_state, self.analysis));
+            self.prev_state.clone_from(state)
         }
-
-        self.prev_loc = location;
-
-        // Before
-
-        write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
-        results.seek_before_primary_effect(location);
-        let curr_state = results.get();
-        write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
-        self.prev_state.overwrite(curr_state);
-        write!(w, "</td>")?;
-
-        // After
-
-        write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
-        results.seek_after_primary_effect(location);
-        let curr_state = results.get();
-        write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
-        self.prev_state.overwrite(curr_state);
-        write!(w, "</td>")
     }
-}
-
-/// Prints the gen/kill set for the entire block.
-pub struct BlockTransferFunc<'a, 'tcx, T: Idx> {
-    body: &'a mir::Body<'tcx>,
-    trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>,
-}
 
-impl<T: Idx> BlockTransferFunc<'mir, 'tcx, T> {
-    pub fn new(
-        body: &'mir mir::Body<'tcx>,
-        trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>,
-    ) -> Self {
-        BlockTransferFunc { body, trans_for_block }
+    fn visit_terminator_after_primary_effect(
+        &mut self,
+        state: &Self::FlowState,
+        _terminator: &'mir mir::Terminator<'tcx>,
+        _location: Location,
+    ) {
+        self.after.push(diff_pretty(state, &self.prev_state, self.analysis));
+        self.prev_state.clone_from(state)
     }
 }
 
-impl<A> StateFormatter<'tcx, A> for BlockTransferFunc<'mir, 'tcx, A::Idx>
+fn diff_pretty<T, C>(new: T, old: T, ctxt: &C) -> String
 where
-    A: Analysis<'tcx>,
+    T: DebugWithContext<C>,
 {
-    fn column_names(&self) -> &[&str] {
-        &["GEN", "KILL"]
+    if new == old {
+        return String::new();
     }
 
-    fn write_state_for_location(
-        &mut self,
-        mut w: &mut dyn io::Write,
-        fmt: &str,
-        results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
-        location: Location,
-    ) -> io::Result<()> {
-        // Only print a single row.
-        if location.statement_index != 0 {
-            return Ok(());
-        }
+    let re = Regex::new("\u{001f}([+-])").unwrap();
 
-        let block_trans = &self.trans_for_block[location.block];
-        let rowspan = self.body.basic_blocks()[location.block].statements.len();
+    let raw_diff = format!("{:#?}", DebugDiffWithAdapter { new, old, ctxt });
 
-        for set in &[&block_trans.gen, &block_trans.kill] {
-            write!(
-                w,
-                r#"<td {fmt} rowspan="{rowspan}" balign="left" align="left">"#,
-                fmt = fmt,
-                rowspan = rowspan
-            )?;
+    // Replace newlines in the `Debug` output with `<br/>`
+    let raw_diff = raw_diff.replace('\n', r#"<br align="left"/>"#);
 
-            pretty_print_state_elems(&mut w, results.analysis(), set.iter(), BR_LEFT, None)?;
-            write!(w, "</td>")?;
+    let mut inside_font_tag = false;
+    let html_diff = re.replace_all(&raw_diff, |captures: &regex::Captures<'_>| {
+        let mut ret = String::new();
+        if inside_font_tag {
+            ret.push_str(r#"</font>"#);
         }
 
-        Ok(())
-    }
-}
-
-/// Writes two lines, one containing the added bits and one the removed bits.
-fn write_diff<A: Analysis<'tcx>>(
-    w: &mut impl io::Write,
-    analysis: &A,
-    from: &BitSet<A::Idx>,
-    to: &BitSet<A::Idx>,
-) -> io::Result<()> {
-    assert_eq!(from.domain_size(), to.domain_size());
-    let len = from.domain_size();
-
-    let mut set = HybridBitSet::new_empty(len);
-    let mut clear = HybridBitSet::new_empty(len);
-
-    // FIXME: Implement a lazy iterator over the symmetric difference of two bitsets.
-    for i in (0..len).map(A::Idx::new) {
-        match (from.contains(i), to.contains(i)) {
-            (false, true) => set.insert(i),
-            (true, false) => clear.insert(i),
-            _ => continue,
+        let tag = match &captures[1] {
+            "+" => r#"<font color="darkgreen">+"#,
+            "-" => r#"<font color="red">-"#,
+            _ => unreachable!(),
         };
-    }
-
-    if !set.is_empty() {
-        write!(w, r#"<font color="darkgreen">+"#)?;
-        pretty_print_state_elems(w, analysis, set.iter(), ", ", LIMIT_30_ALIGN_1)?;
-        write!(w, r#"</font>"#)?;
-    }
-
-    if !set.is_empty() && !clear.is_empty() {
-        write!(w, "{}", BR_LEFT)?;
-    }
-
-    if !clear.is_empty() {
-        write!(w, r#"<font color="red">-"#)?;
-        pretty_print_state_elems(w, analysis, clear.iter(), ", ", LIMIT_30_ALIGN_1)?;
-        write!(w, r#"</font>"#)?;
-    }
-
-    Ok(())
-}
 
-const BR_LEFT: &str = r#"<br align="left"/>"#;
-const BR_LEFT_SPACE: &str = r#"<br align="left"/> "#;
+        inside_font_tag = true;
+        ret.push_str(tag);
+        ret
+    });
 
-/// Line break policy that breaks at 40 characters and starts the next line with a single space.
-const LIMIT_30_ALIGN_1: Option<LineBreak> = Some(LineBreak { sequence: BR_LEFT_SPACE, limit: 30 });
-
-struct LineBreak {
-    sequence: &'static str,
-    limit: usize,
-}
-
-/// Formats each `elem` using the pretty printer provided by `analysis` into a list with the given
-/// separator (`sep`).
-///
-/// Optionally, it will break lines using the given character sequence (usually `<br/>`) and
-/// character limit.
-fn pretty_print_state_elems<A>(
-    w: &mut impl io::Write,
-    analysis: &A,
-    elems: impl Iterator<Item = A::Idx>,
-    sep: &str,
-    line_break: Option<LineBreak>,
-) -> io::Result<bool>
-where
-    A: Analysis<'tcx>,
-{
-    let sep_width = sep.chars().count();
-
-    let mut buf = Vec::new();
-
-    let mut first = true;
-    let mut curr_line_width = 0;
-    let mut line_break_inserted = false;
-
-    for idx in elems {
-        buf.clear();
-        analysis.pretty_print_idx(&mut buf, idx)?;
-        let idx_str =
-            str::from_utf8(&buf).expect("Output of `pretty_print_idx` must be valid UTF-8");
-        let escaped = dot::escape_html(idx_str);
-        let escaped_width = escaped.chars().count();
-
-        if first {
-            first = false;
-        } else {
-            write!(w, "{}", sep)?;
-            curr_line_width += sep_width;
-
-            if let Some(line_break) = &line_break {
-                if curr_line_width + sep_width + escaped_width > line_break.limit {
-                    write!(w, "{}", line_break.sequence)?;
-                    line_break_inserted = true;
-                    curr_line_width = 0;
-                }
-            }
-        }
+    let mut html_diff = match html_diff {
+        Cow::Borrowed(_) => return raw_diff,
+        Cow::Owned(s) => s,
+    };
 
-        write!(w, "{}", escaped)?;
-        curr_line_width += escaped_width;
+    if inside_font_tag {
+        html_diff.push_str("</font>");
     }
 
-    Ok(line_break_inserted)
+    html_diff
 }
 
 /// The background color used for zebra-striping the table.
diff --git a/compiler/rustc_mir/src/dataflow/framework/lattice.rs b/compiler/rustc_mir/src/dataflow/framework/lattice.rs
new file mode 100644
index 00000000000..e7ef9267db5
--- /dev/null
+++ b/compiler/rustc_mir/src/dataflow/framework/lattice.rs
@@ -0,0 +1,230 @@
+//! Traits used to represent [lattices] for use as the domain of a dataflow analysis.
+//!
+//! # Overview
+//!
+//! The most common lattice is a powerset of some set `S`, ordered by [set inclusion]. The [Hasse
+//! diagram] for the powerset of a set with two elements (`X` and `Y`) is shown below. Note that
+//! distinct elements at the same height in a Hasse diagram (e.g. `{X}` and `{Y}`) are
+//! *incomparable*, not equal.
+//!
+//! ```text
+//!      {X, Y}    <- top
+//!       /  \
+//!    {X}    {Y}
+//!       \  /
+//!        {}      <- bottom
+//!
+//! ```
+//!
+//! The defining characteristic of a lattice—the one that differentiates it from a [partially
+//! ordered set][poset]—is the existence of a *unique* least upper and greatest lower bound for
+//! every pair of elements. The lattice join operator (`∨`) returns the least upper bound, and the
+//! lattice meet operator (`∧`) returns the greatest lower bound. Types that implement one operator
+//! but not the other are known as semilattices. Dataflow analysis only uses the join operator and
+//! will work with any join-semilattice, but both should be specified when possible.
+//!
+//! ## `PartialOrd`
+//!
+//! Given that they represent partially ordered sets, you may be surprised that [`JoinSemiLattice`]
+//! and [`MeetSemiLattice`] do not have [`PartialOrd`][std::cmp::PartialOrd] as a supertrait. This
+//! is because most standard library types use lexicographic ordering instead of set inclusion for
+//! their `PartialOrd` impl. Since we do not actually need to compare lattice elements to run a
+//! dataflow analysis, there's no need for a newtype wrapper with a custom `PartialOrd` impl. The
+//! only benefit would be the ability to check that the least upper (or greatest lower) bound
+//! returned by the lattice join (or meet) operator was in fact greater (or lower) than the inputs.
+//!
+//! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order)
+//! [set inclusion]: https://en.wikipedia.org/wiki/Subset
+//! [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram
+//! [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set
+
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::{Idx, IndexVec};
+
+/// A [partially ordered set][poset] that has a [least upper bound][lub] for any pair of elements
+/// in the set.
+///
+/// [lub]: https://en.wikipedia.org/wiki/Infimum_and_supremum
+/// [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set
+pub trait JoinSemiLattice: Eq {
+    /// Computes the least upper bound of two elements, storing the result in `self` and returning
+    /// `true` if `self` has changed.
+    ///
+    /// The lattice join operator is abbreviated as `∨`.
+    fn join(&mut self, other: &Self) -> bool;
+}
+
+/// A [partially ordered set][poset] that has a [greatest lower bound][glb] for any pair of
+/// elements in the set.
+///
+/// Dataflow analyses only require that their domains implement [`JoinSemiLattice`], not
+/// `MeetSemiLattice`. However, types that will be used as dataflow domains should implement both
+/// so that they can be used with [`Dual`].
+///
+/// [glb]: https://en.wikipedia.org/wiki/Infimum_and_supremum
+/// [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set
+pub trait MeetSemiLattice: Eq {
+    /// Computes the greatest lower bound of two elements, storing the result in `self` and
+    /// returning `true` if `self` has changed.
+    ///
+    /// The lattice meet operator is abbreviated as `∧`.
+    fn meet(&mut self, other: &Self) -> bool;
+}
+
+/// A `bool` is a "two-point" lattice with `true` as the top element and `false` as the bottom:
+///
+/// ```text
+///      true
+///        |
+///      false
+/// ```
+impl JoinSemiLattice for bool {
+    fn join(&mut self, other: &Self) -> bool {
+        if let (false, true) = (*self, *other) {
+            *self = true;
+            return true;
+        }
+
+        false
+    }
+}
+
+impl MeetSemiLattice for bool {
+    fn meet(&mut self, other: &Self) -> bool {
+        if let (true, false) = (*self, *other) {
+            *self = false;
+            return true;
+        }
+
+        false
+    }
+}
+
+/// A tuple (or list) of lattices is itself a lattice whose least upper bound is the concatenation
+/// of the least upper bounds of each element of the tuple (or list).
+///
+/// In other words:
+///     (A₀, A₁, ..., Aₙ) ∨ (B₀, B₁, ..., Bₙ) = (A₀∨B₀, A₁∨B₁, ..., Aₙ∨Bₙ)
+impl<I: Idx, T: JoinSemiLattice> JoinSemiLattice for IndexVec<I, T> {
+    fn join(&mut self, other: &Self) -> bool {
+        assert_eq!(self.len(), other.len());
+
+        let mut changed = false;
+        for (a, b) in self.iter_mut().zip(other.iter()) {
+            changed |= a.join(b);
+        }
+        changed
+    }
+}
+
+impl<I: Idx, T: MeetSemiLattice> MeetSemiLattice for IndexVec<I, T> {
+    fn meet(&mut self, other: &Self) -> bool {
+        assert_eq!(self.len(), other.len());
+
+        let mut changed = false;
+        for (a, b) in self.iter_mut().zip(other.iter()) {
+            changed |= a.meet(b);
+        }
+        changed
+    }
+}
+
+/// A `BitSet` represents the lattice formed by the powerset of all possible values of
+/// the index type `T` ordered by inclusion. Equivalently, it is a tuple of "two-point" lattices,
+/// one for each possible value of `T`.
+impl<T: Idx> JoinSemiLattice for BitSet<T> {
+    fn join(&mut self, other: &Self) -> bool {
+        self.union(other)
+    }
+}
+
+impl<T: Idx> MeetSemiLattice for BitSet<T> {
+    fn meet(&mut self, other: &Self) -> bool {
+        self.intersect(other)
+    }
+}
+
+/// The counterpart of a given semilattice `T` using the [inverse order].
+///
+/// The dual of a join-semilattice is a meet-semilattice and vice versa. For example, the dual of a
+/// powerset has the empty set as its top element and the full set as its bottom element and uses
+/// set *intersection* as its join operator.
+///
+/// [inverse order]: https://en.wikipedia.org/wiki/Duality_(order_theory)
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct Dual<T>(pub T);
+
+impl<T> std::borrow::Borrow<T> for Dual<T> {
+    fn borrow(&self) -> &T {
+        &self.0
+    }
+}
+
+impl<T> std::borrow::BorrowMut<T> for Dual<T> {
+    fn borrow_mut(&mut self) -> &mut T {
+        &mut self.0
+    }
+}
+
+impl<T: MeetSemiLattice> JoinSemiLattice for Dual<T> {
+    fn join(&mut self, other: &Self) -> bool {
+        self.0.meet(&other.0)
+    }
+}
+
+impl<T: JoinSemiLattice> MeetSemiLattice for Dual<T> {
+    fn meet(&mut self, other: &Self) -> bool {
+        self.0.join(&other.0)
+    }
+}
+
+/// Extends a type `T` with top and bottom elements to make it a partially ordered set in which no
+/// value of `T` is comparable with any other. A flat set has the following [Hasse diagram]:
+///
+/// ```text
+///         top
+///       / /  \ \
+/// all possible values of `T`
+///       \ \  / /
+///        bottom
+/// ```
+///
+/// [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum FlatSet<T> {
+    Bottom,
+    Elem(T),
+    Top,
+}
+
+impl<T: Clone + Eq> JoinSemiLattice for FlatSet<T> {
+    fn join(&mut self, other: &Self) -> bool {
+        let result = match (&*self, other) {
+            (Self::Top, _) | (_, Self::Bottom) => return false,
+            (Self::Elem(a), Self::Elem(b)) if a == b => return false,
+
+            (Self::Bottom, Self::Elem(x)) => Self::Elem(x.clone()),
+
+            _ => Self::Top,
+        };
+
+        *self = result;
+        true
+    }
+}
+
+impl<T: Clone + Eq> MeetSemiLattice for FlatSet<T> {
+    fn meet(&mut self, other: &Self) -> bool {
+        let result = match (&*self, other) {
+            (Self::Bottom, _) | (_, Self::Top) => return false,
+            (Self::Elem(ref a), Self::Elem(ref b)) if a == b => return false,
+
+            (Self::Top, Self::Elem(ref x)) => Self::Elem(x.clone()),
+
+            _ => Self::Bottom,
+        };
+
+        *self = result;
+        true
+    }
+}
diff --git a/compiler/rustc_mir/src/dataflow/framework/mod.rs b/compiler/rustc_mir/src/dataflow/framework/mod.rs
index a21bbacb467..eefa1395a62 100644
--- a/compiler/rustc_mir/src/dataflow/framework/mod.rs
+++ b/compiler/rustc_mir/src/dataflow/framework/mod.rs
@@ -30,8 +30,8 @@
 //!
 //! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems
 
+use std::borrow::BorrowMut;
 use std::cmp::Ordering;
-use std::io;
 
 use rustc_hir::def_id::DefId;
 use rustc_index::bit_set::{BitSet, HybridBitSet};
@@ -43,69 +43,27 @@ use rustc_target::abi::VariantIdx;
 mod cursor;
 mod direction;
 mod engine;
+pub mod fmt;
 mod graphviz;
+pub mod lattice;
 mod visitor;
 
 pub use self::cursor::{ResultsCursor, ResultsRefCursor};
 pub use self::direction::{Backward, Direction, Forward};
 pub use self::engine::{Engine, Results};
+pub use self::lattice::{JoinSemiLattice, MeetSemiLattice};
 pub use self::visitor::{visit_results, ResultsVisitor};
 pub use self::visitor::{BorrowckFlowState, BorrowckResults};
 
-/// Parameterization for the precise form of data flow that is used.
-///
-/// `BottomValue` determines whether the initial entry set for each basic block is empty or full.
-/// This also determines the semantics of the lattice `join` operator used to merge dataflow
-/// results, since dataflow works by starting at the bottom and moving monotonically to a fixed
-/// point.
-///
-/// This means, for propagation across the graph, that you either want to start at all-zeroes and
-/// then use Union as your merge when propagating, or you start at all-ones and then use Intersect
-/// as your merge when propagating.
-pub trait BottomValue {
-    /// Specifies the initial value for each bit in the entry set for each basic block.
-    const BOTTOM_VALUE: bool;
-
-    /// Merges `in_set` into `inout_set`, returning `true` if `inout_set` changed.
-    ///
-    /// It is almost certainly wrong to override this, since it automatically applies
-    /// * `inout_set & in_set` if `BOTTOM_VALUE == true`
-    /// * `inout_set | in_set` if `BOTTOM_VALUE == false`
-    ///
-    /// This means that if a bit is not `BOTTOM_VALUE`, it is propagated into all target blocks.
-    /// For clarity, the above statement again from a different perspective:
-    /// A bit in the block's entry set is `!BOTTOM_VALUE` if *any* predecessor block's bit value is
-    /// `!BOTTOM_VALUE`.
-    ///
-    /// There are situations where you want the opposite behaviour: propagate only if *all*
-    /// predecessor blocks's value is `!BOTTOM_VALUE`.
-    /// E.g. if you want to know whether a bit is *definitely* set at a specific location. This
-    /// means that all code paths leading to the location must have set the bit, instead of any
-    /// code path leading there.
-    ///
-    /// If you want this kind of "definitely set" analysis, you need to
-    /// 1. Invert `BOTTOM_VALUE`
-    /// 2. Reset the `entry_set` in `start_block_effect` to `!BOTTOM_VALUE`
-    /// 3. Override `join` to do the opposite from what it's doing now.
-    #[inline]
-    fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
-        if !Self::BOTTOM_VALUE { inout_set.union(in_set) } else { inout_set.intersect(in_set) }
-    }
-}
-
 /// Define the domain of a dataflow problem.
 ///
-/// This trait specifies the lattice on which this analysis operates. For now, this must be a
-/// powerset of values of type `Idx`. The elements of this lattice are represented with a `BitSet`
-/// and referred to as the state vector.
-///
-/// This trait also defines the initial value for the dataflow state upon entry to the
-/// `START_BLOCK`, as well as some names used to refer to this analysis when debugging.
-pub trait AnalysisDomain<'tcx>: BottomValue {
-    /// The type of the elements in the state vector.
-    type Idx: Idx;
+/// This trait specifies the lattice on which this analysis operates (the domain) as well as its
+/// initial value at the entry point of each basic block.
+pub trait AnalysisDomain<'tcx> {
+    /// The type that holds the dataflow state at any given point in the program.
+    type Domain: Clone + JoinSemiLattice;
 
-    /// The direction of this analyis. Either `Forward` or `Backward`.
+    /// The direction of this analysis. Either `Forward` or `Backward`.
     type Direction: Direction = Forward;
 
     /// A descriptive name for this analysis. Used only for debugging.
@@ -114,11 +72,10 @@ pub trait AnalysisDomain<'tcx>: BottomValue {
     /// suitable as part of a filename.
     const NAME: &'static str;
 
-    /// The size of the state vector.
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize;
+    /// The initial value of the dataflow state upon entry to each basic block.
+    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain;
 
-    /// Mutates the entry set of the `START_BLOCK` to contain the initial state for dataflow
-    /// analysis.
+    /// Mutates the initial value of the dataflow state upon entry to the `START_BLOCK`.
     ///
     /// For backward analyses, initial state besides the bottom value is not yet supported. Trying
     /// to mutate the initial state will result in a panic.
@@ -126,20 +83,30 @@ pub trait AnalysisDomain<'tcx>: BottomValue {
     // FIXME: For backward dataflow analyses, the initial state should be applied to every basic
     // block where control flow could exit the MIR body (e.g., those terminated with `return` or
     // `resume`). It's not obvious how to handle `yield` points in generators, however.
-    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>);
-
-    /// Prints an element in the state vector for debugging.
-    fn pretty_print_idx(&self, w: &mut impl io::Write, idx: Self::Idx) -> io::Result<()> {
-        write!(w, "{:?}", idx)
-    }
+    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain);
 }
 
 /// A dataflow problem with an arbitrarily complex transfer function.
+///
+/// # Convergence
+///
+/// When implementing this trait directly (not via [`GenKillAnalysis`]), it's possible to choose a
+/// transfer function such that the analysis does not reach fixpoint. To guarantee convergence,
+/// your transfer functions must maintain the following invariant:
+///
+/// > If the dataflow state **before** some point in the program changes to be greater
+/// than the prior state **before** that point, the dataflow state **after** that point must
+/// also change to be greater than the prior state **after** that point.
+///
+/// This invariant guarantees that the dataflow state at a given point in the program increases
+/// monotonically until fixpoint is reached. Note that this monotonicity requirement only applies
+/// to the same point in the program at different points in time. The dataflow state at a given
+/// point in the program may or may not be greater than the state at any preceding point.
 pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// Updates the current dataflow state with the effect of evaluating a statement.
     fn apply_statement_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         statement: &mir::Statement<'tcx>,
         location: Location,
     );
@@ -152,7 +119,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// analyses should not implement this without implementing `apply_statement_effect`.
     fn apply_before_statement_effect(
         &self,
-        _state: &mut BitSet<Self::Idx>,
+        _state: &mut Self::Domain,
         _statement: &mir::Statement<'tcx>,
         _location: Location,
     ) {
@@ -166,7 +133,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// initialized here.
     fn apply_terminator_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         terminator: &mir::Terminator<'tcx>,
         location: Location,
     );
@@ -179,7 +146,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// analyses should not implement this without implementing `apply_terminator_effect`.
     fn apply_before_terminator_effect(
         &self,
-        _state: &mut BitSet<Self::Idx>,
+        _state: &mut Self::Domain,
         _terminator: &mir::Terminator<'tcx>,
         _location: Location,
     ) {
@@ -192,7 +159,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// edges.
     fn apply_call_return_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         block: BasicBlock,
         func: &mir::Operand<'tcx>,
         args: &[mir::Operand<'tcx>],
@@ -207,7 +174,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// By default, no effects happen.
     fn apply_yield_resume_effect(
         &self,
-        _state: &mut BitSet<Self::Idx>,
+        _state: &mut Self::Domain,
         _resume_block: BasicBlock,
         _resume_place: mir::Place<'tcx>,
     ) {
@@ -222,7 +189,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
     /// FIXME: This class of effects is not supported for backward dataflow analyses.
     fn apply_discriminant_switch_effect(
         &self,
-        _state: &mut BitSet<Self::Idx>,
+        _state: &mut Self::Domain,
         _block: BasicBlock,
         _enum_place: mir::Place<'tcx>,
         _adt: &ty::AdtDef,
@@ -264,6 +231,8 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
 ///
 /// `Analysis` is automatically implemented for all implementers of `GenKillAnalysis`.
 pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
+    type Idx: Idx;
+
     /// See `Analysis::apply_statement_effect`.
     fn statement_effect(
         &self,
@@ -332,10 +301,11 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
 impl<A> Analysis<'tcx> for A
 where
     A: GenKillAnalysis<'tcx>,
+    A::Domain: GenKill<A::Idx> + BorrowMut<BitSet<A::Idx>>,
 {
     fn apply_statement_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut A::Domain,
         statement: &mir::Statement<'tcx>,
         location: Location,
     ) {
@@ -344,7 +314,7 @@ where
 
     fn apply_before_statement_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut A::Domain,
         statement: &mir::Statement<'tcx>,
         location: Location,
     ) {
@@ -353,7 +323,7 @@ where
 
     fn apply_terminator_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut A::Domain,
         terminator: &mir::Terminator<'tcx>,
         location: Location,
     ) {
@@ -362,7 +332,7 @@ where
 
     fn apply_before_terminator_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut A::Domain,
         terminator: &mir::Terminator<'tcx>,
         location: Location,
     ) {
@@ -371,7 +341,7 @@ where
 
     fn apply_call_return_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut A::Domain,
         block: BasicBlock,
         func: &mir::Operand<'tcx>,
         args: &[mir::Operand<'tcx>],
@@ -382,7 +352,7 @@ where
 
     fn apply_yield_resume_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut A::Domain,
         resume_block: BasicBlock,
         resume_place: mir::Place<'tcx>,
     ) {
@@ -391,7 +361,7 @@ where
 
     fn apply_discriminant_switch_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut A::Domain,
         block: BasicBlock,
         enum_place: mir::Place<'tcx>,
         adt: &ty::AdtDef,
@@ -450,7 +420,7 @@ pub trait GenKill<T> {
 /// applied multiple times efficiently. When there are multiple calls to `gen` and/or `kill` for
 /// the same element, the most recent one takes precedence.
 #[derive(Clone)]
-pub struct GenKillSet<T: Idx> {
+pub struct GenKillSet<T> {
     gen: HybridBitSet<T>,
     kill: HybridBitSet<T>,
 }
@@ -464,7 +434,6 @@ impl<T: Idx> GenKillSet<T> {
         }
     }
 
-    /// Applies this transfer function to the given state vector.
     pub fn apply(&self, state: &mut BitSet<T>) {
         state.union(&self.gen);
         state.subtract(&self.kill);
@@ -493,6 +462,16 @@ impl<T: Idx> GenKill<T> for BitSet<T> {
     }
 }
 
+impl<T: Idx> GenKill<T> for lattice::Dual<BitSet<T>> {
+    fn gen(&mut self, elem: T) {
+        self.0.insert(elem);
+    }
+
+    fn kill(&mut self, elem: T) {
+        self.0.remove(elem);
+    }
+}
+
 // NOTE: DO NOT CHANGE VARIANT ORDER. The derived `Ord` impls rely on the current order.
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
 pub enum Effect {
diff --git a/compiler/rustc_mir/src/dataflow/framework/tests.rs b/compiler/rustc_mir/src/dataflow/framework/tests.rs
index 9349f5133a5..a5989121679 100644
--- a/compiler/rustc_mir/src/dataflow/framework/tests.rs
+++ b/compiler/rustc_mir/src/dataflow/framework/tests.rs
@@ -9,7 +9,6 @@ use rustc_middle::ty;
 use rustc_span::DUMMY_SP;
 
 use super::*;
-use crate::dataflow::BottomValue;
 
 /// Creates a `mir::Body` with a few disconnected basic blocks.
 ///
@@ -92,13 +91,13 @@ impl<D: Direction> MockAnalysis<'tcx, D> {
     /// The entry set for each `BasicBlock` is the ID of that block offset by a fixed amount to
     /// avoid colliding with the statement/terminator effects.
     fn mock_entry_set(&self, bb: BasicBlock) -> BitSet<usize> {
-        let mut ret = BitSet::new_empty(self.bits_per_block(self.body));
+        let mut ret = self.bottom_value(self.body);
         ret.insert(Self::BASIC_BLOCK_OFFSET + bb.index());
         ret
     }
 
     fn mock_entry_sets(&self) -> IndexVec<BasicBlock, BitSet<usize>> {
-        let empty = BitSet::new_empty(self.bits_per_block(self.body));
+        let empty = self.bottom_value(self.body);
         let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks());
 
         for (bb, _) in self.body.basic_blocks().iter_enumerated() {
@@ -130,7 +129,7 @@ impl<D: Direction> MockAnalysis<'tcx, D> {
     /// would be `[102, 0, 1, 2, 3, 4]`.
     fn expected_state_at_target(&self, target: SeekTarget) -> BitSet<usize> {
         let block = target.block();
-        let mut ret = BitSet::new_empty(self.bits_per_block(self.body));
+        let mut ret = self.bottom_value(self.body);
         ret.insert(Self::BASIC_BLOCK_OFFSET + block.index());
 
         let target = match target {
@@ -161,21 +160,17 @@ impl<D: Direction> MockAnalysis<'tcx, D> {
     }
 }
 
-impl<D: Direction> BottomValue for MockAnalysis<'tcx, D> {
-    const BOTTOM_VALUE: bool = false;
-}
-
 impl<D: Direction> AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> {
-    type Idx = usize;
+    type Domain = BitSet<usize>;
     type Direction = D;
 
     const NAME: &'static str = "mock";
 
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
-        Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len()
+    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+        BitSet::new_empty(Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len())
     }
 
-    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
         unimplemented!("This is never called since `MockAnalysis` is never iterated to fixpoint");
     }
 }
@@ -183,7 +178,7 @@ impl<D: Direction> AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> {
 impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
     fn apply_statement_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         _statement: &mir::Statement<'tcx>,
         location: Location,
     ) {
@@ -193,7 +188,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
 
     fn apply_before_statement_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         _statement: &mir::Statement<'tcx>,
         location: Location,
     ) {
@@ -203,7 +198,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
 
     fn apply_terminator_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         _terminator: &mir::Terminator<'tcx>,
         location: Location,
     ) {
@@ -213,7 +208,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
 
     fn apply_before_terminator_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         _terminator: &mir::Terminator<'tcx>,
         location: Location,
     ) {
@@ -223,7 +218,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
 
     fn apply_call_return_effect(
         &self,
-        _state: &mut BitSet<Self::Idx>,
+        _state: &mut Self::Domain,
         _block: BasicBlock,
         _func: &mir::Operand<'tcx>,
         _args: &[mir::Operand<'tcx>],
diff --git a/compiler/rustc_mir/src/dataflow/framework/visitor.rs b/compiler/rustc_mir/src/dataflow/framework/visitor.rs
index 257f3cb9a6d..82eb734ed06 100644
--- a/compiler/rustc_mir/src/dataflow/framework/visitor.rs
+++ b/compiler/rustc_mir/src/dataflow/framework/visitor.rs
@@ -1,4 +1,3 @@
-use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::{self, BasicBlock, Location};
 
 use super::{Analysis, Direction, Results};
@@ -139,16 +138,16 @@ impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A>
 where
     A: Analysis<'tcx>,
 {
-    type FlowState = BitSet<A::Idx>;
+    type FlowState = A::Domain;
 
     type Direction = A::Direction;
 
     fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState {
-        BitSet::new_empty(self.analysis.bits_per_block(body))
+        self.analysis.bottom_value(body)
     }
 
     fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock) {
-        state.overwrite(&self.entry_set_for_block(block));
+        state.clone_from(&self.entry_set_for_block(block));
     }
 
     fn reconstruct_before_statement_effect(
@@ -217,11 +216,11 @@ macro_rules! impl_visitable {
             $( $A: Analysis<'tcx, Direction = D>, )*
         {
             type Direction = D;
-            type FlowState = $T<$( BitSet<$A::Idx> ),*>;
+            type FlowState = $T<$( $A::Domain ),*>;
 
             fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState {
                 $T {
-                    $( $field: BitSet::new_empty(self.$field.analysis.bits_per_block(body)) ),*
+                    $( $field: self.$field.analysis.bottom_value(body) ),*
                 }
             }
 
@@ -230,7 +229,7 @@ macro_rules! impl_visitable {
                 state: &mut Self::FlowState,
                 block: BasicBlock,
             ) {
-                $( state.$field.overwrite(&self.$field.entry_set_for_block(block)); )*
+                $( state.$field.clone_from(&self.$field.entry_set_for_block(block)); )*
             }
 
             fn reconstruct_before_statement_effect(
diff --git a/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs b/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs
index a3fc51cad65..65e04ed6831 100644
--- a/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs
+++ b/compiler/rustc_mir/src/dataflow/impls/borrowed_locals.rs
@@ -82,15 +82,15 @@ impl<K> AnalysisDomain<'tcx> for MaybeBorrowedLocals<K>
 where
     K: BorrowAnalysisKind<'tcx>,
 {
-    type Idx = Local;
-
+    type Domain = BitSet<Local>;
     const NAME: &'static str = K::ANALYSIS_NAME;
 
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
-        body.local_decls().len()
+    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = unborrowed
+        BitSet::new_empty(body.local_decls().len())
     }
 
-    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
         // No locals are aliased on function entry
     }
 }
@@ -99,6 +99,8 @@ impl<K> GenKillAnalysis<'tcx> for MaybeBorrowedLocals<K>
 where
     K: BorrowAnalysisKind<'tcx>,
 {
+    type Idx = Local;
+
     fn statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -128,11 +130,6 @@ where
     }
 }
 
-impl<K> BottomValue for MaybeBorrowedLocals<K> {
-    // bottom = unborrowed
-    const BOTTOM_VALUE: bool = false;
-}
-
 /// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`.
 struct TransferFunction<'a, T, K> {
     trans: &'a mut T,
diff --git a/compiler/rustc_mir/src/dataflow/impls/borrows.rs b/compiler/rustc_mir/src/dataflow/impls/borrows.rs
index aeb7ffe3e3b..0be13b6ba81 100644
--- a/compiler/rustc_mir/src/dataflow/impls/borrows.rs
+++ b/compiler/rustc_mir/src/dataflow/impls/borrows.rs
@@ -8,9 +8,9 @@ use rustc_index::bit_set::BitSet;
 use crate::borrow_check::{
     places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, ToRegionVid,
 };
-use crate::dataflow::BottomValue;
-use crate::dataflow::{self, GenKill};
+use crate::dataflow::{self, fmt::DebugWithContext, GenKill};
 
+use std::fmt;
 use std::rc::Rc;
 
 rustc_index::newtype_index! {
@@ -227,25 +227,24 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
 }
 
 impl<'tcx> dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
-    type Idx = BorrowIndex;
+    type Domain = BitSet<BorrowIndex>;
 
     const NAME: &'static str = "borrows";
 
-    fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
-        self.borrow_set.len() * 2
+    fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = nothing is reserved or activated yet;
+        BitSet::new_empty(self.borrow_set.len() * 2)
     }
 
-    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
         // no borrows of code region_scopes have been taken prior to
         // function execution, so this method has no effect.
     }
-
-    fn pretty_print_idx(&self, w: &mut impl std::io::Write, idx: Self::Idx) -> std::io::Result<()> {
-        write!(w, "{:?}", self.location(idx))
-    }
 }
 
 impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
+    type Idx = BorrowIndex;
+
     fn before_statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -344,7 +343,8 @@ impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> BottomValue for Borrows<'a, 'tcx> {
-    /// bottom = nothing is reserved or activated yet;
-    const BOTTOM_VALUE: bool = false;
+impl DebugWithContext<Borrows<'_, '_>> for BorrowIndex {
+    fn fmt_with(&self, ctxt: &Borrows<'_, '_>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{:?}", ctxt.location(*self))
+    }
 }
diff --git a/compiler/rustc_mir/src/dataflow/impls/init_locals.rs b/compiler/rustc_mir/src/dataflow/impls/init_locals.rs
index 0e7cd1bb0e4..bb7292cd033 100644
--- a/compiler/rustc_mir/src/dataflow/impls/init_locals.rs
+++ b/compiler/rustc_mir/src/dataflow/impls/init_locals.rs
@@ -2,7 +2,7 @@
 //!
 //! A local will be maybe initialized if *any* projections of that local might be initialized.
 
-use crate::dataflow::{self, BottomValue, GenKill};
+use crate::dataflow::{self, GenKill};
 
 use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::visit::{PlaceContext, Visitor};
@@ -10,21 +10,17 @@ use rustc_middle::mir::{self, BasicBlock, Local, Location};
 
 pub struct MaybeInitializedLocals;
 
-impl BottomValue for MaybeInitializedLocals {
-    /// bottom = uninit
-    const BOTTOM_VALUE: bool = false;
-}
-
 impl dataflow::AnalysisDomain<'tcx> for MaybeInitializedLocals {
-    type Idx = Local;
+    type Domain = BitSet<Local>;
 
     const NAME: &'static str = "maybe_init_locals";
 
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
-        body.local_decls.len()
+    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = uninit
+        BitSet::new_empty(body.local_decls.len())
     }
 
-    fn initialize_start_block(&self, body: &mir::Body<'tcx>, entry_set: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, body: &mir::Body<'tcx>, entry_set: &mut Self::Domain) {
         // Function arguments are initialized to begin with.
         for arg in body.args_iter() {
             entry_set.insert(arg);
@@ -33,6 +29,8 @@ impl dataflow::AnalysisDomain<'tcx> for MaybeInitializedLocals {
 }
 
 impl dataflow::GenKillAnalysis<'tcx> for MaybeInitializedLocals {
+    type Idx = Local;
+
     fn statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -99,7 +97,6 @@ where
             PlaceContext::NonUse(
                 NonUseContext::StorageLive
                 | NonUseContext::AscribeUserTy
-                | NonUseContext::Coverage
                 | NonUseContext::VarDebugInfo,
             )
             | PlaceContext::NonMutatingUse(
diff --git a/compiler/rustc_mir/src/dataflow/impls/liveness.rs b/compiler/rustc_mir/src/dataflow/impls/liveness.rs
index 784b0bd9293..b0da28156d1 100644
--- a/compiler/rustc_mir/src/dataflow/impls/liveness.rs
+++ b/compiler/rustc_mir/src/dataflow/impls/liveness.rs
@@ -2,7 +2,7 @@ use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::{self, Local, Location};
 
-use crate::dataflow::{AnalysisDomain, Backward, BottomValue, GenKill, GenKillAnalysis};
+use crate::dataflow::{AnalysisDomain, Backward, GenKill, GenKillAnalysis};
 
 /// A [live-variable dataflow analysis][liveness].
 ///
@@ -22,27 +22,25 @@ impl MaybeLiveLocals {
     }
 }
 
-impl BottomValue for MaybeLiveLocals {
-    // bottom = not live
-    const BOTTOM_VALUE: bool = false;
-}
-
 impl AnalysisDomain<'tcx> for MaybeLiveLocals {
-    type Idx = Local;
+    type Domain = BitSet<Local>;
     type Direction = Backward;
 
     const NAME: &'static str = "liveness";
 
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
-        body.local_decls.len()
+    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = not live
+        BitSet::new_empty(body.local_decls.len())
     }
 
-    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
         // No variables are live until we observe a use
     }
 }
 
 impl GenKillAnalysis<'tcx> for MaybeLiveLocals {
+    type Idx = Local;
+
     fn statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
diff --git a/compiler/rustc_mir/src/dataflow/impls/mod.rs b/compiler/rustc_mir/src/dataflow/impls/mod.rs
index 8975faec487..c42d5867856 100644
--- a/compiler/rustc_mir/src/dataflow/impls/mod.rs
+++ b/compiler/rustc_mir/src/dataflow/impls/mod.rs
@@ -13,7 +13,7 @@ use super::MoveDataParamEnv;
 use crate::util::elaborate_drops::DropFlagState;
 
 use super::move_paths::{HasMoveData, InitIndex, InitKind, MoveData, MovePathIndex};
-use super::{AnalysisDomain, BottomValue, GenKill, GenKillAnalysis};
+use super::{lattice, AnalysisDomain, GenKill, GenKillAnalysis};
 
 use super::drop_flag_effects_for_function_entry;
 use super::drop_flag_effects_for_location;
@@ -290,27 +290,25 @@ impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
 }
 
 impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
-    type Idx = MovePathIndex;
-
+    type Domain = BitSet<MovePathIndex>;
     const NAME: &'static str = "maybe_init";
 
-    fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
-        self.move_data().move_paths.len()
+    fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = uninitialized
+        BitSet::new_empty(self.move_data().move_paths.len())
     }
 
-    fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
         drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
             assert!(s == DropFlagState::Present);
             state.insert(path);
         });
     }
-
-    fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> {
-        write!(w, "{}", self.move_data().move_paths[mpi])
-    }
 }
 
 impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
+    type Idx = MovePathIndex;
+
     fn statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -376,18 +374,18 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
 }
 
 impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
-    type Idx = MovePathIndex;
+    type Domain = BitSet<MovePathIndex>;
 
     const NAME: &'static str = "maybe_uninit";
 
-    fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
-        self.move_data().move_paths.len()
+    fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = initialized (start_block_effect counters this at outset)
+        BitSet::new_empty(self.move_data().move_paths.len())
     }
 
     // sets on_entry bits for Arg places
-    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
         // set all bits to 1 (uninit) before gathering counterevidence
-        assert!(self.bits_per_block(body) == state.domain_size());
         state.insert_all();
 
         drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
@@ -395,13 +393,11 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
             state.remove(path);
         });
     }
-
-    fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> {
-        write!(w, "{}", self.move_data().move_paths[mpi])
-    }
 }
 
 impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
+    type Idx = MovePathIndex;
+
     fn statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -471,30 +467,30 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
 }
 
 impl<'a, 'tcx> AnalysisDomain<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
-    type Idx = MovePathIndex;
+    /// Use set intersection as the join operator.
+    type Domain = lattice::Dual<BitSet<MovePathIndex>>;
 
     const NAME: &'static str = "definite_init";
 
-    fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
-        self.move_data().move_paths.len()
+    fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = initialized (start_block_effect counters this at outset)
+        lattice::Dual(BitSet::new_filled(self.move_data().move_paths.len()))
     }
 
     // sets on_entry bits for Arg places
-    fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
-        state.clear();
+    fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
+        state.0.clear();
 
         drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
             assert!(s == DropFlagState::Present);
-            state.insert(path);
+            state.0.insert(path);
         });
     }
-
-    fn pretty_print_idx(&self, w: &mut impl std::io::Write, mpi: Self::Idx) -> std::io::Result<()> {
-        write!(w, "{}", self.move_data().move_paths[mpi])
-    }
 }
 
 impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
+    type Idx = MovePathIndex;
+
     fn statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -540,15 +536,16 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
 }
 
 impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> {
-    type Idx = InitIndex;
+    type Domain = BitSet<InitIndex>;
 
     const NAME: &'static str = "ever_init";
 
-    fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
-        self.move_data().inits.len()
+    fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = no initialized variables by default
+        BitSet::new_empty(self.move_data().inits.len())
     }
 
-    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
         for arg_init in 0..body.arg_count {
             state.insert(InitIndex::new(arg_init));
         }
@@ -556,6 +553,8 @@ impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> {
 }
 
 impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
+    type Idx = InitIndex;
+
     fn statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -625,23 +624,3 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
         }
     }
 }
-
-impl<'a, 'tcx> BottomValue for MaybeInitializedPlaces<'a, 'tcx> {
-    /// bottom = uninitialized
-    const BOTTOM_VALUE: bool = false;
-}
-
-impl<'a, 'tcx> BottomValue for MaybeUninitializedPlaces<'a, 'tcx> {
-    /// bottom = initialized (start_block_effect counters this at outset)
-    const BOTTOM_VALUE: bool = false;
-}
-
-impl<'a, 'tcx> BottomValue for DefinitelyInitializedPlaces<'a, 'tcx> {
-    /// bottom = initialized (start_block_effect counters this at outset)
-    const BOTTOM_VALUE: bool = true;
-}
-
-impl<'a, 'tcx> BottomValue for EverInitializedPlaces<'a, 'tcx> {
-    /// bottom = no initialized variables by default
-    const BOTTOM_VALUE: bool = false;
-}
diff --git a/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs b/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs
index 21623e3cad5..9250cd40847 100644
--- a/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs
+++ b/compiler/rustc_mir/src/dataflow/impls/storage_liveness.rs
@@ -1,6 +1,5 @@
 pub use super::*;
 
-use crate::dataflow::BottomValue;
 use crate::dataflow::{self, GenKill, Results, ResultsRefCursor};
 use crate::util::storage::AlwaysLiveLocals;
 use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
@@ -19,15 +18,16 @@ impl MaybeStorageLive {
 }
 
 impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive {
-    type Idx = Local;
+    type Domain = BitSet<Local>;
 
     const NAME: &'static str = "maybe_storage_live";
 
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
-        body.local_decls.len()
+    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = dead
+        BitSet::new_empty(body.local_decls.len())
     }
 
-    fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) {
         assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size());
         for local in self.always_live_locals.iter() {
             on_entry.insert(local);
@@ -40,6 +40,8 @@ impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive {
 }
 
 impl dataflow::GenKillAnalysis<'tcx> for MaybeStorageLive {
+    type Idx = Local;
+
     fn statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -74,11 +76,6 @@ impl dataflow::GenKillAnalysis<'tcx> for MaybeStorageLive {
     }
 }
 
-impl BottomValue for MaybeStorageLive {
-    /// bottom = dead
-    const BOTTOM_VALUE: bool = false;
-}
-
 type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>;
 
 /// Dataflow analysis that determines whether each local requires storage at a
@@ -101,15 +98,16 @@ impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
 }
 
 impl<'mir, 'tcx> dataflow::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
-    type Idx = Local;
+    type Domain = BitSet<Local>;
 
     const NAME: &'static str = "requires_storage";
 
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
-        body.local_decls.len()
+    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+        // bottom = dead
+        BitSet::new_empty(body.local_decls.len())
     }
 
-    fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) {
         // The resume argument is live on function entry (we don't care about
         // the `self` argument)
         for arg in body.args_iter().skip(1) {
@@ -119,6 +117,8 @@ impl<'mir, 'tcx> dataflow::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, '
 }
 
 impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
+    type Idx = Local;
+
     fn before_statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -285,11 +285,6 @@ impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
     }
 }
 
-impl<'mir, 'tcx> BottomValue for MaybeRequiresStorage<'mir, 'tcx> {
-    /// bottom = dead
-    const BOTTOM_VALUE: bool = false;
-}
-
 struct MoveVisitor<'a, 'mir, 'tcx, T> {
     borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
     trans: &'a mut T,
diff --git a/compiler/rustc_mir/src/dataflow/mod.rs b/compiler/rustc_mir/src/dataflow/mod.rs
index a0c24636059..5575a97982f 100644
--- a/compiler/rustc_mir/src/dataflow/mod.rs
+++ b/compiler/rustc_mir/src/dataflow/mod.rs
@@ -5,9 +5,9 @@ use rustc_span::symbol::{sym, Symbol};
 
 pub(crate) use self::drop_flag_effects::*;
 pub use self::framework::{
-    visit_results, Analysis, AnalysisDomain, Backward, BorrowckFlowState, BorrowckResults,
-    BottomValue, Engine, Forward, GenKill, GenKillAnalysis, Results, ResultsCursor,
-    ResultsRefCursor, ResultsVisitor,
+    fmt, lattice, visit_results, Analysis, AnalysisDomain, Backward, BorrowckFlowState,
+    BorrowckResults, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results,
+    ResultsCursor, ResultsRefCursor, ResultsVisitor,
 };
 
 use self::move_paths::MoveData;
diff --git a/compiler/rustc_mir/src/lib.rs b/compiler/rustc_mir/src/lib.rs
index 2e3b5084635..42717f27384 100644
--- a/compiler/rustc_mir/src/lib.rs
+++ b/compiler/rustc_mir/src/lib.rs
@@ -14,6 +14,7 @@ Rust MIR: a lowered representation of Rust.
 #![feature(crate_visibility_modifier)]
 #![feature(decl_macro)]
 #![feature(drain_filter)]
+#![feature(exact_size_is_empty)]
 #![feature(exhaustive_patterns)]
 #![feature(iter_order_by)]
 #![feature(never_type)]
diff --git a/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs b/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs
index e96af77bbb8..0f6f078d968 100644
--- a/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs
+++ b/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs
@@ -382,7 +382,7 @@ fn collect_and_partition_mono_items<'tcx>(
                 cgus.sort_by_key(|(name, _)| *name);
                 cgus.dedup();
                 for &(ref cgu_name, (linkage, _)) in cgus.iter() {
-                    output.push_str(" ");
+                    output.push(' ');
                     output.push_str(&cgu_name.as_str());
 
                     let linkage_abbrev = match linkage {
@@ -399,9 +399,9 @@ fn collect_and_partition_mono_items<'tcx>(
                         Linkage::Common => "Common",
                     };
 
-                    output.push_str("[");
+                    output.push('[');
                     output.push_str(linkage_abbrev);
-                    output.push_str("]");
+                    output.push(']');
                 }
                 output
             })
diff --git a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs
new file mode 100644
index 00000000000..589268e39bd
--- /dev/null
+++ b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs
@@ -0,0 +1,114 @@
+use rustc_errors::DiagnosticBuilder;
+use rustc_middle::lint::LintDiagnosticBuilder;
+use rustc_middle::mir::visit::Visitor;
+use rustc_middle::mir::*;
+use rustc_middle::ty::TyCtxt;
+use rustc_session::lint::builtin::CONST_ITEM_MUTATION;
+use rustc_span::def_id::DefId;
+
+use crate::transform::{MirPass, MirSource};
+
+pub struct CheckConstItemMutation;
+
+impl<'tcx> MirPass<'tcx> for CheckConstItemMutation {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+        let mut checker = ConstMutationChecker { body, tcx, target_local: None };
+        checker.visit_body(&body);
+    }
+}
+
+struct ConstMutationChecker<'a, 'tcx> {
+    body: &'a Body<'tcx>,
+    tcx: TyCtxt<'tcx>,
+    target_local: Option<Local>,
+}
+
+impl<'a, 'tcx> ConstMutationChecker<'a, 'tcx> {
+    fn is_const_item(&self, local: Local) -> Option<DefId> {
+        if let Some(box LocalInfo::ConstRef { def_id }) = self.body.local_decls[local].local_info {
+            Some(def_id)
+        } else {
+            None
+        }
+    }
+    fn lint_const_item_usage(
+        &self,
+        const_item: DefId,
+        location: Location,
+        decorate: impl for<'b> FnOnce(LintDiagnosticBuilder<'b>) -> DiagnosticBuilder<'b>,
+    ) {
+        let source_info = self.body.source_info(location);
+        let lint_root = self.body.source_scopes[source_info.scope]
+            .local_data
+            .as_ref()
+            .assert_crate_local()
+            .lint_root;
+
+        self.tcx.struct_span_lint_hir(CONST_ITEM_MUTATION, lint_root, source_info.span, |lint| {
+            decorate(lint)
+                .span_note(self.tcx.def_span(const_item), "`const` item defined here")
+                .emit()
+        });
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> {
+    fn visit_statement(&mut self, stmt: &Statement<'tcx>, loc: Location) {
+        if let StatementKind::Assign(box (lhs, _)) = &stmt.kind {
+            // Check for assignment to fields of a constant
+            // Assigning directly to a constant (e.g. `FOO = true;`) is a hard error,
+            // so emitting a lint would be redundant.
+            if !lhs.projection.is_empty() {
+                if let Some(def_id) = self.is_const_item(lhs.local) {
+                    self.lint_const_item_usage(def_id, loc, |lint| {
+                        let mut lint = lint.build("attempting to modify a `const` item");
+                        lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified");
+                        lint
+                    })
+                }
+            }
+            // We are looking for MIR of the form:
+            //
+            // ```
+            // _1 = const FOO;
+            // _2 = &mut _1;
+            // method_call(_2, ..)
+            // ```
+            //
+            // Record our current LHS, so that we can detect this
+            // pattern in `visit_rvalue`
+            self.target_local = lhs.as_local();
+        }
+        self.super_statement(stmt, loc);
+        self.target_local = None;
+    }
+    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, loc: Location) {
+        if let Rvalue::Ref(_, BorrowKind::Mut { .. }, place) = rvalue {
+            let local = place.local;
+            if let Some(def_id) = self.is_const_item(local) {
+                // If this Rvalue is being used as the right-hand side of a
+                // `StatementKind::Assign`, see if it ends up getting used as
+                // the `self` parameter of a method call (as the terminator of our current
+                // BasicBlock). If so, we emit a more specific lint.
+                let method_did = self.target_local.and_then(|target_local| {
+                    crate::util::find_self_call(self.tcx, &self.body, target_local, loc.block)
+                });
+                let lint_loc =
+                    if method_did.is_some() { self.body.terminator_loc(loc.block) } else { loc };
+                self.lint_const_item_usage(def_id, lint_loc, |lint| {
+                    let mut lint = lint.build("taking a mutable reference to a `const` item");
+                    lint
+                        .note("each usage of a `const` item creates a new temporary")
+                        .note("the mutable reference will refer to this temporary, not the original `const` item");
+
+                    if let Some(method_did) = method_did {
+                        lint.span_note(self.tcx.def_span(method_did), "mutable reference created due to call to this method");
+                    }
+
+                    lint
+                });
+            }
+        }
+        self.super_rvalue(rvalue, loc);
+    }
+}
diff --git a/compiler/rustc_mir/src/transform/check_consts/resolver.rs b/compiler/rustc_mir/src/transform/check_consts/resolver.rs
index b8104292aab..a00301952b3 100644
--- a/compiler/rustc_mir/src/transform/check_consts/resolver.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/resolver.rs
@@ -165,23 +165,19 @@ where
     }
 }
 
-impl<Q> dataflow::BottomValue for FlowSensitiveAnalysis<'_, '_, '_, Q> {
-    const BOTTOM_VALUE: bool = false;
-}
-
 impl<Q> dataflow::AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
 where
     Q: Qualif,
 {
-    type Idx = Local;
+    type Domain = BitSet<Local>;
 
     const NAME: &'static str = Q::ANALYSIS_NAME;
 
-    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
-        body.local_decls.len()
+    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+        BitSet::new_empty(body.local_decls.len())
     }
 
-    fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
+    fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut Self::Domain) {
         self.transfer_function(state).initialize_state();
     }
 }
@@ -192,7 +188,7 @@ where
 {
     fn apply_statement_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         statement: &mir::Statement<'tcx>,
         location: Location,
     ) {
@@ -201,7 +197,7 @@ where
 
     fn apply_terminator_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         terminator: &mir::Terminator<'tcx>,
         location: Location,
     ) {
@@ -210,7 +206,7 @@ where
 
     fn apply_call_return_effect(
         &self,
-        state: &mut BitSet<Self::Idx>,
+        state: &mut Self::Domain,
         block: BasicBlock,
         func: &mir::Operand<'tcx>,
         args: &[mir::Operand<'tcx>],
diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs
index 0a3424bb944..d6e6371e886 100644
--- a/compiler/rustc_mir/src/transform/inline.rs
+++ b/compiler/rustc_mir/src/transform/inline.rs
@@ -4,7 +4,7 @@ use rustc_attr as attr;
 use rustc_hir::def_id::DefId;
 use rustc_index::bit_set::BitSet;
 use rustc_index::vec::{Idx, IndexVec};
-use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
+use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
 use rustc_middle::mir::visit::*;
 use rustc_middle::mir::*;
 use rustc_middle::ty::subst::{Subst, SubstsRef};
@@ -45,7 +45,8 @@ impl<'tcx> MirPass<'tcx> for Inline {
                 // based function.
                 debug!("function inlining is disabled when compiling with `instrument_coverage`");
             } else {
-                Inliner { tcx, source }.run_pass(body);
+                Inliner { tcx, source, codegen_fn_attrs: tcx.codegen_fn_attrs(source.def_id()) }
+                    .run_pass(body);
             }
         }
     }
@@ -54,6 +55,7 @@ impl<'tcx> MirPass<'tcx> for Inline {
 struct Inliner<'tcx> {
     tcx: TyCtxt<'tcx>,
     source: MirSource<'tcx>,
+    codegen_fn_attrs: &'tcx CodegenFnAttrs,
 }
 
 impl Inliner<'tcx> {
@@ -242,9 +244,19 @@ impl Inliner<'tcx> {
             return false;
         }
 
-        // Avoid inlining functions marked as no_sanitize if sanitizer is enabled,
-        // since instrumentation might be enabled and performed on the caller.
-        if self.tcx.sess.opts.debugging_opts.sanitizer.intersects(codegen_fn_attrs.no_sanitize) {
+        let self_features = &self.codegen_fn_attrs.target_features;
+        let callee_features = &codegen_fn_attrs.target_features;
+        if callee_features.iter().any(|feature| !self_features.contains(feature)) {
+            debug!("`callee has extra target features - not inlining");
+            return false;
+        }
+
+        let self_no_sanitize =
+            self.codegen_fn_attrs.no_sanitize & self.tcx.sess.opts.debugging_opts.sanitizer;
+        let callee_no_sanitize =
+            codegen_fn_attrs.no_sanitize & self.tcx.sess.opts.debugging_opts.sanitizer;
+        if self_no_sanitize != callee_no_sanitize {
+            debug!("`callee has incompatible no_sanitize attribute - not inlining");
             return false;
         }
 
diff --git a/compiler/rustc_mir/src/transform/instcombine.rs b/compiler/rustc_mir/src/transform/instcombine.rs
index c6474ba2d73..c4924cf16ab 100644
--- a/compiler/rustc_mir/src/transform/instcombine.rs
+++ b/compiler/rustc_mir/src/transform/instcombine.rs
@@ -6,7 +6,7 @@ use rustc_hir::Mutability;
 use rustc_index::vec::Idx;
 use rustc_middle::mir::visit::{MutVisitor, Visitor};
 use rustc_middle::mir::{
-    Body, Constant, Local, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue,
+    BinOp, Body, Constant, Local, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue,
 };
 use rustc_middle::ty::{self, TyCtxt};
 use std::mem;
@@ -66,6 +66,11 @@ impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> {
             *rvalue = Rvalue::Use(Operand::Constant(box constant));
         }
 
+        if let Some(operand) = self.optimizations.unneeded_equality_comparison.remove(&location) {
+            debug!("replacing {:?} with {:?}", rvalue, operand);
+            *rvalue = Rvalue::Use(operand);
+        }
+
         self.super_rvalue(rvalue, location)
     }
 }
@@ -81,6 +86,48 @@ impl OptimizationFinder<'b, 'tcx> {
     fn new(body: &'b Body<'tcx>, tcx: TyCtxt<'tcx>) -> OptimizationFinder<'b, 'tcx> {
         OptimizationFinder { body, tcx, optimizations: OptimizationList::default() }
     }
+
+    fn find_unneeded_equality_comparison(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
+        // find Ne(_place, false) or Ne(false, _place)
+        // or   Eq(_place, true) or Eq(true, _place)
+        if let Rvalue::BinaryOp(op, l, r) = rvalue {
+            let const_to_find = if *op == BinOp::Ne {
+                false
+            } else if *op == BinOp::Eq {
+                true
+            } else {
+                return;
+            };
+            // (const, _place)
+            if let Some(o) = self.find_operand_in_equality_comparison_pattern(l, r, const_to_find) {
+                self.optimizations.unneeded_equality_comparison.insert(location, o.clone());
+            }
+            // (_place, const)
+            else if let Some(o) =
+                self.find_operand_in_equality_comparison_pattern(r, l, const_to_find)
+            {
+                self.optimizations.unneeded_equality_comparison.insert(location, o.clone());
+            }
+        }
+    }
+
+    fn find_operand_in_equality_comparison_pattern(
+        &self,
+        l: &Operand<'tcx>,
+        r: &'a Operand<'tcx>,
+        const_to_find: bool,
+    ) -> Option<&'a Operand<'tcx>> {
+        let const_ = l.constant()?;
+        if const_.literal.ty == self.tcx.types.bool
+            && const_.literal.val.try_to_bool() == Some(const_to_find)
+        {
+            if r.place().is_some() {
+                return Some(r);
+            }
+        }
+
+        return None;
+    }
 }
 
 impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> {
@@ -106,6 +153,8 @@ impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> {
             }
         }
 
+        self.find_unneeded_equality_comparison(rvalue, location);
+
         self.super_rvalue(rvalue, location)
     }
 }
@@ -114,4 +163,5 @@ impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> {
 struct OptimizationList<'tcx> {
     and_stars: FxHashSet<Location>,
     arrays_lengths: FxHashMap<Location, Constant<'tcx>>,
+    unneeded_equality_comparison: FxHashMap<Location, Operand<'tcx>>,
 }
diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs
index d3a2bd24123..a5b30a25a9b 100644
--- a/compiler/rustc_mir/src/transform/instrument_coverage.rs
+++ b/compiler/rustc_mir/src/transform/instrument_coverage.rs
@@ -309,7 +309,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
         for coverage_region in coverage_regions {
             span_viewables.push(SpanViewable {
                 span: coverage_region.span,
-                title: format!("{}", coverage_region.blocks[0].index()),
+                id: format!("{}", coverage_region.blocks[0].index()),
                 tooltip: self.make_tooltip_text(coverage_region),
             });
         }
@@ -353,7 +353,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
                             if !INCLUDE_COVERAGE_STATEMENTS {
                                 continue;
                             }
-                            format!("unreachable")
+                            String::from("unreachable")
                         }
                     },
                     _ => format!("{:?}", statement),
diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs
index c3a34756122..8025b7c0204 100644
--- a/compiler/rustc_mir/src/transform/mod.rs
+++ b/compiler/rustc_mir/src/transform/mod.rs
@@ -16,6 +16,7 @@ use std::borrow::Cow;
 pub mod add_call_guards;
 pub mod add_moves_for_packed_drops;
 pub mod add_retag;
+pub mod check_const_item_mutation;
 pub mod check_consts;
 pub mod check_packed_ref;
 pub mod check_unsafety;
@@ -307,6 +308,7 @@ fn mir_const<'tcx>(
         &[&[
             // MIR-level lints.
             &check_packed_ref::CheckPackedRef,
+            &check_const_item_mutation::CheckConstItemMutation,
             // What we need to do constant evaluation.
             &simplify::SimplifyCfg::new("initial"),
             &rustc_peek::SanityCheck,
@@ -453,8 +455,9 @@ fn run_optimization_passes<'tcx>(
 
     // The main optimizations that we do on MIR.
     let optimizations: &[&dyn MirPass<'tcx>] = &[
-        &instcombine::InstCombine,
         &match_branches::MatchBranchSimplification,
+        // inst combine is after MatchBranchSimplification to clean up Ne(_1, false)
+        &instcombine::InstCombine,
         &const_prop::ConstProp,
         &simplify_branches::SimplifyBranches::new("after-const-prop"),
         &simplify_comparison_integral::SimplifyComparisonIntegral,
diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs
index 852629a45f7..1d2295a37dd 100644
--- a/compiler/rustc_mir/src/transform/promote_consts.rs
+++ b/compiler/rustc_mir/src/transform/promote_consts.rs
@@ -364,15 +364,7 @@ impl<'tcx> Validator<'_, 'tcx> {
                             // In theory, any zero-sized value could be borrowed
                             // mutably without consequences. However, only &mut []
                             // is allowed right now, and only in functions.
-                            if self.const_kind
-                                == Some(hir::ConstContext::Static(hir::Mutability::Mut))
-                            {
-                                // Inside a `static mut`, &mut [...] is also allowed.
-                                match ty.kind() {
-                                    ty::Array(..) | ty::Slice(_) => {}
-                                    _ => return Err(Unpromotable),
-                                }
-                            } else if let ty::Array(_, len) = ty.kind() {
+                            if let ty::Array(_, len) = ty.kind() {
                                 // FIXME(eddyb) the `self.is_non_const_fn` condition
                                 // seems unnecessary, given that this is merely a ZST.
                                 match len.try_eval_usize(self.tcx, self.param_env) {
@@ -673,13 +665,7 @@ impl<'tcx> Validator<'_, 'tcx> {
                     // In theory, any zero-sized value could be borrowed
                     // mutably without consequences. However, only &mut []
                     // is allowed right now, and only in functions.
-                    if self.const_kind == Some(hir::ConstContext::Static(hir::Mutability::Mut)) {
-                        // Inside a `static mut`, &mut [...] is also allowed.
-                        match ty.kind() {
-                            ty::Array(..) | ty::Slice(_) => {}
-                            _ => return Err(Unpromotable),
-                        }
-                    } else if let ty::Array(_, len) = ty.kind() {
+                    if let ty::Array(_, len) = ty.kind() {
                         // FIXME(eddyb): We only return `Unpromotable` for `&mut []` inside a
                         // const context which seems unnecessary given that this is merely a ZST.
                         match len.try_eval_usize(self.tcx, self.param_env) {
diff --git a/compiler/rustc_mir/src/transform/rustc_peek.rs b/compiler/rustc_mir/src/transform/rustc_peek.rs
index 242192e75b4..015af44b80f 100644
--- a/compiler/rustc_mir/src/transform/rustc_peek.rs
+++ b/compiler/rustc_mir/src/transform/rustc_peek.rs
@@ -1,4 +1,6 @@
-use rustc_ast as ast;
+use std::borrow::Borrow;
+
+use rustc_ast::ast;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_target::spec::abi::Abi;
@@ -16,7 +18,7 @@ use crate::dataflow::impls::{
 use crate::dataflow::move_paths::{HasMoveData, MoveData};
 use crate::dataflow::move_paths::{LookupResult, MovePathIndex};
 use crate::dataflow::MoveDataParamEnv;
-use crate::dataflow::{Analysis, Results, ResultsCursor};
+use crate::dataflow::{Analysis, JoinSemiLattice, Results, ResultsCursor};
 
 pub struct SanityCheck;
 
@@ -248,25 +250,26 @@ pub trait RustcPeekAt<'tcx>: Analysis<'tcx> {
         &self,
         tcx: TyCtxt<'tcx>,
         place: mir::Place<'tcx>,
-        flow_state: &BitSet<Self::Idx>,
+        flow_state: &Self::Domain,
         call: PeekCall,
     );
 }
 
-impl<'tcx, A> RustcPeekAt<'tcx> for A
+impl<'tcx, A, D> RustcPeekAt<'tcx> for A
 where
-    A: Analysis<'tcx, Idx = MovePathIndex> + HasMoveData<'tcx>,
+    A: Analysis<'tcx, Domain = D> + HasMoveData<'tcx>,
+    D: JoinSemiLattice + Clone + Borrow<BitSet<MovePathIndex>>,
 {
     fn peek_at(
         &self,
         tcx: TyCtxt<'tcx>,
         place: mir::Place<'tcx>,
-        flow_state: &BitSet<Self::Idx>,
+        flow_state: &Self::Domain,
         call: PeekCall,
     ) {
         match self.move_data().rev_lookup.find(place.as_ref()) {
             LookupResult::Exact(peek_mpi) => {
-                let bit_state = flow_state.contains(peek_mpi);
+                let bit_state = flow_state.borrow().contains(peek_mpi);
                 debug!("rustc_peek({:?} = &{:?}) bit_state: {}", call.arg, place, bit_state);
                 if !bit_state {
                     tcx.sess.span_err(call.span, "rustc_peek: bit not set");
diff --git a/compiler/rustc_mir/src/transform/simplify_try.rs b/compiler/rustc_mir/src/transform/simplify_try.rs
index 5c9a191e905..a320d00614d 100644
--- a/compiler/rustc_mir/src/transform/simplify_try.rs
+++ b/compiler/rustc_mir/src/transform/simplify_try.rs
@@ -367,11 +367,7 @@ fn optimization_applies<'tcx>(
 }
 
 impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
-        if tcx.sess.opts.debugging_opts.mir_opt_level < 2 {
-            return;
-        }
-
+    fn run_pass(&self, _tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
         trace!("running SimplifyArmIdentity on {:?}", source);
         let local_uses = LocalUseCounter::get_local_uses(body);
         let (basic_blocks, local_decls, debug_info) =
@@ -678,7 +674,7 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
         y_bb_idx: BasicBlock,
     ) -> StatementEquality {
         let helper = |rhs: &Rvalue<'tcx>,
-                      place: &Box<Place<'tcx>>,
+                      place: &Place<'tcx>,
                       variant_index: &VariantIdx,
                       side_to_choose| {
             let place_type = place.ty(self.body, self.tcx).ty;
diff --git a/compiler/rustc_mir/src/transform/validate.rs b/compiler/rustc_mir/src/transform/validate.rs
index 8f01e942801..d3ca14abdca 100644
--- a/compiler/rustc_mir/src/transform/validate.rs
+++ b/compiler/rustc_mir/src/transform/validate.rs
@@ -4,8 +4,8 @@ use super::{MirPass, MirSource};
 use rustc_middle::mir::visit::Visitor;
 use rustc_middle::{
     mir::{
-        AggregateKind, BasicBlock, Body, Location, MirPhase, Operand, Rvalue, Statement,
-        StatementKind, Terminator, TerminatorKind,
+        AggregateKind, BasicBlock, Body, BorrowKind, Location, MirPhase, Operand, Rvalue,
+        Statement, StatementKind, Terminator, TerminatorKind,
     },
     ty::{
         self,
@@ -274,9 +274,33 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                             )
                         }
                     }
+                    Rvalue::Ref(_, BorrowKind::Shallow, _) => {
+                        if self.mir_phase > MirPhase::DropLowering {
+                            self.fail(
+                                location,
+                                "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase",
+                            );
+                        }
+                    }
                     _ => {}
                 }
             }
+            StatementKind::AscribeUserType(..) => {
+                if self.mir_phase > MirPhase::DropLowering {
+                    self.fail(
+                        location,
+                        "`AscribeUserType` should have been removed after drop lowering phase",
+                    );
+                }
+            }
+            StatementKind::FakeRead(..) => {
+                if self.mir_phase > MirPhase::DropLowering {
+                    self.fail(
+                        location,
+                        "`FakeRead` should have been removed after drop lowering phase",
+                    );
+                }
+            }
             _ => {}
         }
     }
diff --git a/compiler/rustc_mir/src/util/find_self_call.rs b/compiler/rustc_mir/src/util/find_self_call.rs
new file mode 100644
index 00000000000..049b5f01214
--- /dev/null
+++ b/compiler/rustc_mir/src/util/find_self_call.rs
@@ -0,0 +1,35 @@
+use rustc_middle::mir::*;
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_span::def_id::DefId;
+
+/// Checks if the specified `local` is used as the `self` prameter of a method call
+/// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is
+/// returned.
+pub fn find_self_call(
+    tcx: TyCtxt<'_>,
+    body: &Body<'_>,
+    local: Local,
+    block: BasicBlock,
+) -> Option<DefId> {
+    debug!("find_self_call(local={:?}): terminator={:?}", local, &body[block].terminator);
+    if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) =
+        &body[block].terminator
+    {
+        debug!("find_self_call: func={:?}", func);
+        if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func {
+            if let ty::FnDef(def_id, _) = *ty.kind() {
+                if let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) =
+                    tcx.opt_associated_item(def_id)
+                {
+                    debug!("find_self_call: args={:?}", args);
+                    if let [Operand::Move(self_place) | Operand::Copy(self_place), ..] = **args {
+                        if self_place.as_local() == Some(local) {
+                            return Some(def_id);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    None
+}
diff --git a/compiler/rustc_mir/src/util/graphviz.rs b/compiler/rustc_mir/src/util/graphviz.rs
index 50193c4a0db..e89c9437706 100644
--- a/compiler/rustc_mir/src/util/graphviz.rs
+++ b/compiler/rustc_mir/src/util/graphviz.rs
@@ -55,16 +55,28 @@ where
     writeln!(w, "{} {}Mir_{} {{", kind, cluster, def_name)?;
 
     // Global graph properties
-    writeln!(w, r#"    graph [fontname="monospace"];"#)?;
-    writeln!(w, r#"    node [fontname="monospace"];"#)?;
-    writeln!(w, r#"    edge [fontname="monospace"];"#)?;
+    let font = r#"fontname="Courier, monospace""#;
+    let mut graph_attrs = vec![font];
+    let mut content_attrs = vec![font];
+
+    let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode;
+    if dark_mode {
+        graph_attrs.push(r#"bgcolor="black""#);
+        content_attrs.push(r#"color="white""#);
+        content_attrs.push(r#"fontcolor="white""#);
+    }
+
+    writeln!(w, r#"    graph [{}];"#, graph_attrs.join(" "))?;
+    let content_attrs_str = content_attrs.join(" ");
+    writeln!(w, r#"    node [{}];"#, content_attrs_str)?;
+    writeln!(w, r#"    edge [{}];"#, content_attrs_str)?;
 
     // Graph label
     write_graph_label(tcx, def_id, body, w)?;
 
     // Nodes
     for (block, _) in body.basic_blocks().iter_enumerated() {
-        write_node(def_id, block, body, w)?;
+        write_node(def_id, block, body, dark_mode, w)?;
     }
 
     // Edges
@@ -84,6 +96,7 @@ where
 pub fn write_node_label<W: Write, INIT, FINI>(
     block: BasicBlock,
     body: &Body<'_>,
+    dark_mode: bool,
     w: &mut W,
     num_cols: u32,
     init: INIT,
@@ -100,8 +113,9 @@ where
     // Basic block number at the top.
     write!(
         w,
-        r#"<tr><td {attrs} colspan="{colspan}">{blk}</td></tr>"#,
-        attrs = r#"bgcolor="gray" align="center""#,
+        r#"<tr><td bgcolor="{bgcolor}" {attrs} colspan="{colspan}">{blk}</td></tr>"#,
+        bgcolor = if dark_mode { "dimgray" } else { "gray" },
+        attrs = r#"align="center""#,
         colspan = num_cols,
         blk = block.index()
     )?;
@@ -134,11 +148,12 @@ fn write_node<W: Write>(
     def_id: DefId,
     block: BasicBlock,
     body: &Body<'_>,
+    dark_mode: bool,
     w: &mut W,
 ) -> io::Result<()> {
     // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
     write!(w, r#"    {} [shape="none", label=<"#, node(def_id, block))?;
-    write_node_label(block, body, w, 1, |_| Ok(()), |_| Ok(()))?;
+    write_node_label(block, body, dark_mode, w, 1, |_| Ok(()), |_| Ok(()))?;
     // Close the node label and the node itself.
     writeln!(w, ">];")
 }
diff --git a/compiler/rustc_mir/src/util/mod.rs b/compiler/rustc_mir/src/util/mod.rs
index ed0fafb1aac..699f3bcf014 100644
--- a/compiler/rustc_mir/src/util/mod.rs
+++ b/compiler/rustc_mir/src/util/mod.rs
@@ -7,12 +7,14 @@ pub mod storage;
 
 mod alignment;
 pub mod collect_writes;
+mod find_self_call;
 mod graphviz;
 pub(crate) mod pretty;
 pub(crate) mod spanview;
 
 pub use self::aggregate::expand_aggregate;
 pub use self::alignment::is_disaligned;
+pub use self::find_self_call::find_self_call;
 pub use self::graphviz::write_node_label as write_graphviz_node_label;
 pub use self::graphviz::{graphviz_safe_def_name, write_mir_graphviz};
 pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty, PassWhere};
diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs
index 54bc248bc5b..75567181b69 100644
--- a/compiler/rustc_mir/src/util/pretty.rs
+++ b/compiler/rustc_mir/src/util/pretty.rs
@@ -514,7 +514,7 @@ fn write_scope_tree(
                 write!(indented_decl, " as {:?}", user_ty).unwrap();
             }
         }
-        indented_decl.push_str(";");
+        indented_decl.push(';');
 
         let local_name =
             if local == RETURN_PLACE { " return place".to_string() } else { String::new() };
diff --git a/compiler/rustc_mir/src/util/spanview.rs b/compiler/rustc_mir/src/util/spanview.rs
index b2f2b5fc1e6..fe33fffe0ea 100644
--- a/compiler/rustc_mir/src/util/spanview.rs
+++ b/compiler/rustc_mir/src/util/spanview.rs
@@ -3,13 +3,16 @@ use rustc_middle::hir;
 use rustc_middle::mir::*;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::config::MirSpanview;
-use rustc_span::{BytePos, Pos, Span};
+use rustc_span::{BytePos, Pos, Span, SyntaxContext};
 
+use std::cmp;
 use std::io::{self, Write};
-use std::iter::Peekable;
 
 pub const TOOLTIP_INDENT: &str = "    ";
 
+const CARET: char = '\u{2038}'; // Unicode `CARET`
+const ANNOTATION_LEFT_BRACKET: char = '\u{298a}'; // Unicode `Z NOTATION RIGHT BINDING BRACKET
+const ANNOTATION_RIGHT_BRACKET: char = '\u{2989}'; // Unicode `Z NOTATION LEFT BINDING BRACKET`
 const NEW_LINE_SPAN: &str = "</span>\n<span class=\"line\">";
 const HEADER: &str = r#"<!DOCTYPE html>
 <html>
@@ -80,7 +83,7 @@ const FOOTER: &str = r#"
 /// Metadata to highlight the span of a MIR BasicBlock, Statement, or Terminator.
 pub struct SpanViewable {
     pub span: Span,
-    pub title: String,
+    pub id: String,
     pub tooltip: String,
 }
 
@@ -139,16 +142,22 @@ where
     W: Write,
 {
     let fn_span = fn_span(tcx, def_id);
-    writeln!(w, "{}", HEADER)?;
-    let mut next_pos = fn_span.lo();
+    let mut from_pos = fn_span.lo();
     let end_pos = fn_span.hi();
     let source_map = tcx.sess.source_map();
-    let start = source_map.lookup_char_pos(next_pos);
+    let start = source_map.lookup_char_pos(from_pos);
+    let indent_to_initial_start_col = " ".repeat(start.col.to_usize());
+    debug!(
+        "fn_span source is:\n{}{}",
+        indent_to_initial_start_col,
+        source_map.span_to_snippet(fn_span).expect("function should have printable source")
+    );
+    writeln!(w, "{}", HEADER)?;
     write!(
         w,
         r#"<div class="code" style="counter-reset: line {}"><span class="line">{}"#,
         start.line - 1,
-        " ".repeat(start.col.to_usize())
+        indent_to_initial_start_col,
     )?;
     span_viewables.sort_unstable_by(|a, b| {
         let a = a.span;
@@ -163,14 +172,43 @@ where
         }
         .unwrap()
     });
-    let mut ordered_span_viewables = span_viewables.iter().peekable();
+    let mut ordered_viewables = &span_viewables[..];
+    const LOWEST_VIEWABLE_LAYER: usize = 1;
     let mut alt = false;
-    while ordered_span_viewables.peek().is_some() {
-        next_pos = write_span_viewables(tcx, next_pos, &mut ordered_span_viewables, false, 1, w)?;
-        alt = !alt;
+    while ordered_viewables.len() > 0 {
+        debug!(
+            "calling write_next_viewable with from_pos={}, end_pos={}, and viewables len={}",
+            from_pos.to_usize(),
+            end_pos.to_usize(),
+            ordered_viewables.len()
+        );
+        let (next_from_pos, next_ordered_viewables) = write_next_viewable_with_overlaps(
+            tcx,
+            from_pos,
+            end_pos,
+            ordered_viewables,
+            alt,
+            LOWEST_VIEWABLE_LAYER,
+            w,
+        )?;
+        debug!(
+            "DONE calling write_next_viewable, with new from_pos={}, \
+             and remaining viewables len={}",
+            next_from_pos.to_usize(),
+            next_ordered_viewables.len()
+        );
+        assert!(
+            from_pos != next_from_pos || ordered_viewables.len() != next_ordered_viewables.len(),
+            "write_next_viewable_with_overlaps() must make a state change"
+        );
+        from_pos = next_from_pos;
+        if next_ordered_viewables.len() != ordered_viewables.len() {
+            ordered_viewables = next_ordered_viewables;
+            alt = !alt;
+        }
     }
-    if next_pos < end_pos {
-        write_coverage_gap(tcx, next_pos, end_pos, w)?;
+    if from_pos < end_pos {
+        write_coverage_gap(tcx, from_pos, end_pos, w)?;
     }
     write!(w, r#"</span></div>"#)?;
     writeln!(w, "{}", FOOTER)?;
@@ -233,9 +271,9 @@ fn statement_span_viewable<'tcx>(
     if !body_span.contains(span) {
         return None;
     }
-    let title = format!("bb{}[{}]", bb.index(), i);
-    let tooltip = tooltip(tcx, &title, span, vec![statement.clone()], &None);
-    Some(SpanViewable { span, title, tooltip })
+    let id = format!("{}[{}]", bb.index(), i);
+    let tooltip = tooltip(tcx, &id, span, vec![statement.clone()], &None);
+    Some(SpanViewable { span, id, tooltip })
 }
 
 fn terminator_span_viewable<'tcx>(
@@ -249,9 +287,9 @@ fn terminator_span_viewable<'tcx>(
     if !body_span.contains(span) {
         return None;
     }
-    let title = format!("bb{}`{}`", bb.index(), terminator_kind_name(term));
-    let tooltip = tooltip(tcx, &title, span, vec![], &data.terminator);
-    Some(SpanViewable { span, title, tooltip })
+    let id = format!("{}:{}", bb.index(), terminator_kind_name(term));
+    let tooltip = tooltip(tcx, &id, span, vec![], &data.terminator);
+    Some(SpanViewable { span, id, tooltip })
 }
 
 fn block_span_viewable<'tcx>(
@@ -264,16 +302,16 @@ fn block_span_viewable<'tcx>(
     if !body_span.contains(span) {
         return None;
     }
-    let title = format!("bb{}", bb.index());
-    let tooltip = tooltip(tcx, &title, span, data.statements.clone(), &data.terminator);
-    Some(SpanViewable { span, title, tooltip })
+    let id = format!("{}", bb.index());
+    let tooltip = tooltip(tcx, &id, span, data.statements.clone(), &data.terminator);
+    Some(SpanViewable { span, id, tooltip })
 }
 
 fn compute_block_span<'tcx>(data: &BasicBlockData<'tcx>, body_span: Span) -> Span {
     let mut span = data.terminator().source_info.span;
     for statement_span in data.statements.iter().map(|statement| statement.source_info.span) {
-        // Only combine Spans from the function's body_span.
-        if body_span.contains(statement_span) {
+        // Only combine Spans from the root context, and within the function's body_span.
+        if statement_span.ctxt() == SyntaxContext::root() && body_span.contains(statement_span) {
             span = span.to(statement_span);
         }
     }
@@ -286,100 +324,217 @@ fn compute_block_span<'tcx>(data: &BasicBlockData<'tcx>, body_span: Span) -> Spa
 /// The `layer` is incremented for each overlap, and the `alt` bool alternates between true
 /// and false, for each adjacent non-overlapping span. Source code between the spans (code
 /// that is not in any coverage region) has neutral styling.
-fn write_span_viewables<'tcx, 'b, W>(
+fn write_next_viewable_with_overlaps<'tcx, 'b, W>(
     tcx: TyCtxt<'tcx>,
-    next_pos: BytePos,
-    ordered_span_viewables: &mut Peekable<impl Iterator<Item = &'b SpanViewable>>,
+    mut from_pos: BytePos,
+    mut to_pos: BytePos,
+    ordered_viewables: &'b [SpanViewable],
     alt: bool,
     layer: usize,
     w: &mut W,
-) -> io::Result<BytePos>
+) -> io::Result<(BytePos, &'b [SpanViewable])>
 where
     W: Write,
 {
-    let span_viewable =
-        ordered_span_viewables.next().expect("ordered_span_viewables should have some");
-    if next_pos < span_viewable.span.lo() {
-        write_coverage_gap(tcx, next_pos, span_viewable.span.lo(), w)?;
+    let debug_indent = "  ".repeat(layer);
+    let (viewable, mut remaining_viewables) =
+        ordered_viewables.split_first().expect("ordered_viewables should have some");
+
+    if from_pos < viewable.span.lo() {
+        debug!(
+            "{}advance from_pos to next SpanViewable (from from_pos={} to viewable.span.lo()={} \
+             of {:?}), with to_pos={}",
+            debug_indent,
+            from_pos.to_usize(),
+            viewable.span.lo().to_usize(),
+            viewable.span,
+            to_pos.to_usize()
+        );
+        let hi = cmp::min(viewable.span.lo(), to_pos);
+        write_coverage_gap(tcx, from_pos, hi, w)?;
+        from_pos = hi;
+        if from_pos < viewable.span.lo() {
+            debug!(
+                "{}EARLY RETURN: stopped before getting to next SpanViewable, at {}",
+                debug_indent,
+                from_pos.to_usize()
+            );
+            return Ok((from_pos, ordered_viewables));
+        }
     }
-    let mut remaining_span = span_viewable.span;
+
+    if from_pos < viewable.span.hi() {
+        // Set to_pos to the end of this `viewable` to ensure the recursive calls stop writing
+        // with room to print the tail.
+        to_pos = cmp::min(viewable.span.hi(), to_pos);
+        debug!(
+            "{}update to_pos (if not closer) to viewable.span.hi()={}; to_pos is now {}",
+            debug_indent,
+            viewable.span.hi().to_usize(),
+            to_pos.to_usize()
+        );
+    }
+
     let mut subalt = false;
-    loop {
-        let next_span_viewable = match ordered_span_viewables.peek() {
-            None => break,
-            Some(span_viewable) => *span_viewable,
+    while remaining_viewables.len() > 0 && remaining_viewables[0].span.overlaps(viewable.span) {
+        let overlapping_viewable = &remaining_viewables[0];
+        debug!("{}overlapping_viewable.span={:?}", debug_indent, overlapping_viewable.span);
+
+        let span =
+            trim_span(viewable.span, from_pos, cmp::min(overlapping_viewable.span.lo(), to_pos));
+        let mut some_html_snippet = if from_pos <= viewable.span.hi() || viewable.span.is_empty() {
+            // `viewable` is not yet fully rendered, so start writing the span, up to either the
+            // `to_pos` or the next `overlapping_viewable`, whichever comes first.
+            debug!(
+                "{}make html_snippet (may not write it if early exit) for partial span {:?} \
+                 of viewable.span {:?}",
+                debug_indent, span, viewable.span
+            );
+            from_pos = span.hi();
+            make_html_snippet(tcx, span, Some(&viewable))
+        } else {
+            None
         };
-        if !next_span_viewable.span.overlaps(remaining_span) {
-            break;
+
+        // Defer writing the HTML snippet (until after early return checks) ONLY for empty spans.
+        // An empty Span with Some(html_snippet) is probably a tail marker. If there is an early
+        // exit, there should be another opportunity to write the tail marker.
+        if !span.is_empty() {
+            if let Some(ref html_snippet) = some_html_snippet {
+                debug!(
+                    "{}write html_snippet for that partial span of viewable.span {:?}",
+                    debug_indent, viewable.span
+                );
+                write_span(html_snippet, &viewable.tooltip, alt, layer, w)?;
+            }
+            some_html_snippet = None;
         }
-        write_span(
-            tcx,
-            remaining_span.until(next_span_viewable.span),
-            Some(span_viewable),
-            alt,
-            layer,
-            w,
-        )?;
-        let next_pos = write_span_viewables(
+
+        if from_pos < overlapping_viewable.span.lo() {
+            debug!(
+                "{}EARLY RETURN: from_pos={} has not yet reached the \
+                 overlapping_viewable.span {:?}",
+                debug_indent,
+                from_pos.to_usize(),
+                overlapping_viewable.span
+            );
+            // must have reached `to_pos` before reaching the start of the
+            // `overlapping_viewable.span`
+            return Ok((from_pos, ordered_viewables));
+        }
+
+        if from_pos == to_pos
+            && !(from_pos == overlapping_viewable.span.lo() && overlapping_viewable.span.is_empty())
+        {
+            debug!(
+                "{}EARLY RETURN: from_pos=to_pos={} and overlapping_viewable.span {:?} is not \
+                 empty, or not from_pos",
+                debug_indent,
+                to_pos.to_usize(),
+                overlapping_viewable.span
+            );
+            // `to_pos` must have occurred before the overlapping viewable. Return
+            // `ordered_viewables` so we can continue rendering the `viewable`, from after the
+            // `to_pos`.
+            return Ok((from_pos, ordered_viewables));
+        }
+
+        if let Some(ref html_snippet) = some_html_snippet {
+            debug!(
+                "{}write html_snippet for that partial span of viewable.span {:?}",
+                debug_indent, viewable.span
+            );
+            write_span(html_snippet, &viewable.tooltip, alt, layer, w)?;
+        }
+
+        debug!(
+            "{}recursively calling write_next_viewable with from_pos={}, to_pos={}, \
+             and viewables len={}",
+            debug_indent,
+            from_pos.to_usize(),
+            to_pos.to_usize(),
+            remaining_viewables.len()
+        );
+        // Write the overlaps (and the overlaps' overlaps, if any) up to `to_pos`.
+        let (next_from_pos, next_remaining_viewables) = write_next_viewable_with_overlaps(
             tcx,
-            next_span_viewable.span.lo(),
-            ordered_span_viewables,
+            from_pos,
+            to_pos,
+            &remaining_viewables,
             subalt,
             layer + 1,
             w,
         )?;
-        subalt = !subalt;
-        if next_pos < remaining_span.hi() {
-            remaining_span = remaining_span.with_lo(next_pos);
-        } else {
-            return Ok(next_pos);
+        debug!(
+            "{}DONE recursively calling write_next_viewable, with new from_pos={}, and remaining \
+             viewables len={}",
+            debug_indent,
+            next_from_pos.to_usize(),
+            next_remaining_viewables.len()
+        );
+        assert!(
+            from_pos != next_from_pos
+                || remaining_viewables.len() != next_remaining_viewables.len(),
+            "write_next_viewable_with_overlaps() must make a state change"
+        );
+        from_pos = next_from_pos;
+        if next_remaining_viewables.len() != remaining_viewables.len() {
+            remaining_viewables = next_remaining_viewables;
+            subalt = !subalt;
+        }
+    }
+    if from_pos <= viewable.span.hi() {
+        let span = trim_span(viewable.span, from_pos, to_pos);
+        debug!(
+            "{}After overlaps, writing (end span?) {:?} of viewable.span {:?}",
+            debug_indent, span, viewable.span
+        );
+        if let Some(ref html_snippet) = make_html_snippet(tcx, span, Some(&viewable)) {
+            from_pos = span.hi();
+            write_span(html_snippet, &viewable.tooltip, alt, layer, w)?;
         }
     }
-    write_span(tcx, remaining_span, Some(span_viewable), alt, layer, w)
+    debug!("{}RETURN: No more overlap", debug_indent);
+    Ok((
+        from_pos,
+        if from_pos < viewable.span.hi() { ordered_viewables } else { remaining_viewables },
+    ))
 }
 
+#[inline(always)]
 fn write_coverage_gap<'tcx, W>(
     tcx: TyCtxt<'tcx>,
     lo: BytePos,
     hi: BytePos,
     w: &mut W,
-) -> io::Result<BytePos>
+) -> io::Result<()>
 where
     W: Write,
 {
-    write_span(tcx, Span::with_root_ctxt(lo, hi), None, false, 0, w)
+    let span = Span::with_root_ctxt(lo, hi);
+    if let Some(ref html_snippet) = make_html_snippet(tcx, span, None) {
+        write_span(html_snippet, "", false, 0, w)
+    } else {
+        Ok(())
+    }
 }
 
-fn write_span<'tcx, W>(
-    tcx: TyCtxt<'tcx>,
-    span: Span,
-    span_viewable: Option<&SpanViewable>,
+fn write_span<W>(
+    html_snippet: &str,
+    tooltip: &str,
     alt: bool,
     layer: usize,
     w: &mut W,
-) -> io::Result<BytePos>
+) -> io::Result<()>
 where
     W: Write,
 {
-    let source_map = tcx.sess.source_map();
-    let snippet = source_map
-        .span_to_snippet(span)
-        .unwrap_or_else(|err| bug!("span_to_snippet error for span {:?}: {:?}", span, err));
-    let labeled_snippet = if let Some(SpanViewable { title, .. }) = span_viewable {
-        if span.is_empty() {
-            format!(r#"<span class="annotation">@{}</span>"#, title)
-        } else {
-            format!(r#"<span class="annotation">@{}:</span> {}"#, title, escape_html(&snippet))
-        }
-    } else {
-        snippet
-    };
-    let maybe_alt = if layer > 0 {
+    let maybe_alt_class = if layer > 0 {
         if alt { " odd" } else { " even" }
     } else {
         ""
     };
-    let maybe_tooltip = if let Some(SpanViewable { tooltip, .. }) = span_viewable {
+    let maybe_title_attr = if !tooltip.is_empty() {
         format!(" title=\"{}\"", escape_attr(tooltip))
     } else {
         "".to_owned()
@@ -387,32 +542,73 @@ where
     if layer == 1 {
         write!(w, "<span>")?;
     }
-    for (i, line) in labeled_snippet.lines().enumerate() {
+    for (i, line) in html_snippet.lines().enumerate() {
         if i > 0 {
             write!(w, "{}", NEW_LINE_SPAN)?;
         }
         write!(
             w,
             r#"<span class="code{}" style="--layer: {}"{}>{}</span>"#,
-            maybe_alt, layer, maybe_tooltip, line
+            maybe_alt_class, layer, maybe_title_attr, line
         )?;
     }
+    // Check for and translate trailing newlines, because `str::lines()` ignores them
+    if html_snippet.ends_with('\n') {
+        write!(w, "{}", NEW_LINE_SPAN)?;
+    }
     if layer == 1 {
         write!(w, "</span>")?;
     }
-    Ok(span.hi())
+    Ok(())
+}
+
+fn make_html_snippet<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    span: Span,
+    some_viewable: Option<&SpanViewable>,
+) -> Option<String> {
+    let source_map = tcx.sess.source_map();
+    let snippet = source_map
+        .span_to_snippet(span)
+        .unwrap_or_else(|err| bug!("span_to_snippet error for span {:?}: {:?}", span, err));
+    let html_snippet = if let Some(viewable) = some_viewable {
+        let is_head = span.lo() == viewable.span.lo();
+        let is_tail = span.hi() == viewable.span.hi();
+        let mut labeled_snippet = if is_head {
+            format!(r#"<span class="annotation">{}{}</span>"#, viewable.id, ANNOTATION_LEFT_BRACKET)
+        } else {
+            "".to_owned()
+        };
+        if span.is_empty() {
+            if is_head && is_tail {
+                labeled_snippet.push(CARET);
+            }
+        } else {
+            labeled_snippet.push_str(&escape_html(&snippet));
+        };
+        if is_tail {
+            labeled_snippet.push_str(&format!(
+                r#"<span class="annotation">{}{}</span>"#,
+                ANNOTATION_RIGHT_BRACKET, viewable.id
+            ));
+        }
+        labeled_snippet
+    } else {
+        escape_html(&snippet)
+    };
+    if html_snippet.is_empty() { None } else { Some(html_snippet) }
 }
 
 fn tooltip<'tcx>(
     tcx: TyCtxt<'tcx>,
-    title: &str,
+    spanview_id: &str,
     span: Span,
     statements: Vec<Statement<'tcx>>,
     terminator: &Option<Terminator<'tcx>>,
 ) -> String {
     let source_map = tcx.sess.source_map();
     let mut text = Vec::new();
-    text.push(format!("{}: {}:", title, &source_map.span_to_string(span)));
+    text.push(format!("{}: {}:", spanview_id, &source_map.span_to_string(span)));
     for statement in statements {
         let source_range = source_range_no_file(tcx, &statement.source_info.span);
         text.push(format!(
@@ -436,10 +632,25 @@ fn tooltip<'tcx>(
     text.join("")
 }
 
+fn trim_span(span: Span, from_pos: BytePos, to_pos: BytePos) -> Span {
+    trim_span_hi(trim_span_lo(span, from_pos), to_pos)
+}
+
+fn trim_span_lo(span: Span, from_pos: BytePos) -> Span {
+    if from_pos <= span.lo() { span } else { span.with_lo(cmp::min(span.hi(), from_pos)) }
+}
+
+fn trim_span_hi(span: Span, to_pos: BytePos) -> Span {
+    if to_pos >= span.hi() { span } else { span.with_hi(cmp::max(span.lo(), to_pos)) }
+}
+
 fn fn_span<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Span {
     let hir_id =
         tcx.hir().local_def_id_to_hir_id(def_id.as_local().expect("expected DefId is local"));
-    tcx.hir().span(hir_id)
+    let fn_decl_span = tcx.hir().span(hir_id);
+    let body_span = hir_body(tcx, def_id).value.span;
+    debug_assert_eq!(fn_decl_span.ctxt(), body_span.ctxt());
+    fn_decl_span.to(body_span)
 }
 
 fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> {
diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
index 982aefcf604..244a70f83b0 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
@@ -21,7 +21,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let Expr { ty, temp_lifetime: _, span, kind } = expr;
         match kind {
             ExprKind::Scope { region_scope: _, lint_level: _, value } => this.as_constant(value),
-            ExprKind::Literal { literal, user_ty } => {
+            ExprKind::Literal { literal, user_ty, const_id: _ } => {
                 let user_ty = user_ty.map(|user_ty| {
                     this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
                         span,
diff --git a/compiler/rustc_mir_build/src/build/expr/as_temp.rs b/compiler/rustc_mir_build/src/build/expr/as_temp.rs
index a9cc0cc2f24..9984b527ffd 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_temp.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_temp.rs
@@ -76,6 +76,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     local_decl.local_info =
                         Some(box LocalInfo::StaticRef { def_id, is_thread_local: true });
                 }
+                ExprKind::Literal { const_id: Some(def_id), .. } => {
+                    local_decl.local_info = Some(box LocalInfo::ConstRef { def_id });
+                }
                 _ => {}
             }
             this.local_decls.push(local_decl)
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs
index 313bb979a51..e55180ff4be 100644
--- a/compiler/rustc_mir_build/src/lib.rs
+++ b/compiler/rustc_mir_build/src/lib.rs
@@ -6,6 +6,7 @@
 #![feature(box_syntax)]
 #![feature(const_fn)]
 #![feature(const_panic)]
+#![feature(control_flow_enum)]
 #![feature(crate_visibility_modifier)]
 #![feature(bool_to_option)]
 #![feature(or_patterns)]
diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs
index 22ce278cee0..a8d7c612a84 100644
--- a/compiler/rustc_mir_build/src/lints.rs
+++ b/compiler/rustc_mir_build/src/lints.rs
@@ -117,7 +117,7 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> {
             // A diverging InlineAsm is treated as non-recursing
             TerminatorKind::InlineAsm { destination, .. } => {
                 if destination.is_some() {
-                    ControlFlow::Continue
+                    ControlFlow::CONTINUE
                 } else {
                     ControlFlow::Break(NonRecursive)
                 }
@@ -131,7 +131,7 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> {
             | TerminatorKind::FalseEdge { .. }
             | TerminatorKind::FalseUnwind { .. }
             | TerminatorKind::Goto { .. }
-            | TerminatorKind::SwitchInt { .. } => ControlFlow::Continue,
+            | TerminatorKind::SwitchInt { .. } => ControlFlow::CONTINUE,
         }
     }
 
@@ -144,7 +144,7 @@ impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> {
             }
         }
 
-        ControlFlow::Continue
+        ControlFlow::CONTINUE
     }
 
     fn ignore_edge(&mut self, bb: BasicBlock, target: BasicBlock) -> bool {
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index e9e49d054b8..70c7abc2654 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -247,6 +247,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
         hir::ExprKind::Lit(ref lit) => ExprKind::Literal {
             literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, false),
             user_ty: None,
+            const_id: None,
         },
 
         hir::ExprKind::Binary(op, ref lhs, ref rhs) => {
@@ -306,6 +307,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
                     ExprKind::Literal {
                         literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, true),
                         user_ty: None,
+                        const_id: None,
                     }
                 } else {
                     ExprKind::Unary { op: UnOp::Neg, arg: arg.to_ref() }
@@ -447,6 +449,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
                                             kind: ExprKind::Literal {
                                                 literal: ty::Const::zero_sized(cx.tcx, ty),
                                                 user_ty,
+                                                const_id: None,
                                             },
                                         }
                                         .to_ref(),
@@ -473,6 +476,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
                                             kind: ExprKind::Literal {
                                                 literal: ty::Const::zero_sized(cx.tcx, ty),
                                                 user_ty: None,
+                                                const_id: None,
                                             },
                                         }
                                         .to_ref(),
@@ -585,7 +589,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
                             temp_lifetime,
                             ty: var_ty,
                             span: expr.span,
-                            kind: ExprKind::Literal { literal, user_ty: None },
+                            kind: ExprKind::Literal { literal, user_ty: None, const_id: None },
                         }
                         .to_ref()
                     };
@@ -714,7 +718,11 @@ fn method_callee<'a, 'tcx>(
         temp_lifetime,
         ty,
         span,
-        kind: ExprKind::Literal { literal: ty::Const::zero_sized(cx.tcx(), ty), user_ty },
+        kind: ExprKind::Literal {
+            literal: ty::Const::zero_sized(cx.tcx(), ty),
+            user_ty,
+            const_id: None,
+        },
     }
 }
 
@@ -777,6 +785,7 @@ fn convert_path_expr<'a, 'tcx>(
             ExprKind::Literal {
                 literal: ty::Const::zero_sized(cx.tcx, cx.typeck_results().node_type(expr.hir_id)),
                 user_ty,
+                const_id: None,
             }
         }
 
@@ -794,6 +803,7 @@ fn convert_path_expr<'a, 'tcx>(
                     .tcx
                     .mk_const(ty::Const { val, ty: cx.typeck_results().node_type(expr.hir_id) }),
                 user_ty: None,
+                const_id: Some(def_id),
             }
         }
 
@@ -810,6 +820,7 @@ fn convert_path_expr<'a, 'tcx>(
                     ty: cx.typeck_results().node_type(expr.hir_id),
                 }),
                 user_ty,
+                const_id: Some(def_id),
             }
         }
 
diff --git a/compiler/rustc_mir_build/src/thir/mod.rs b/compiler/rustc_mir_build/src/thir/mod.rs
index 2837bfa040f..4d57fd5c64f 100644
--- a/compiler/rustc_mir_build/src/thir/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/mod.rs
@@ -273,6 +273,10 @@ crate enum ExprKind<'tcx> {
     Literal {
         literal: &'tcx Const<'tcx>,
         user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
+        /// The `DefId` of the `const` item this literal
+        /// was produced from, if this is not a user-written
+        /// literal value.
+        const_id: Option<DefId>,
     },
     /// A literal containing the address of a `static`.
     ///
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index 034442b798b..32b124970cf 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -1,22 +1,19 @@
 use rustc_ast::ast::AttrStyle;
 use rustc_ast::token::{self, CommentKind, Token, TokenKind};
-use rustc_ast::tokenstream::IsJoint;
-use rustc_data_structures::sync::Lrc;
-use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError};
-use rustc_lexer::Base;
-use rustc_lexer::{unescape, RawStrError};
+use rustc_ast::tokenstream::{Spacing, TokenStream};
+use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError, PResult};
+use rustc_lexer::unescape::{self, Mode};
+use rustc_lexer::{Base, DocStyle, RawStrError};
 use rustc_session::parse::ParseSess;
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::{BytePos, Pos, Span};
 
-use std::char;
 use tracing::debug;
 
 mod tokentrees;
 mod unescape_error_reporting;
 mod unicode_chars;
 
-use rustc_lexer::{unescape::Mode, DocStyle};
 use unescape_error_reporting::{emit_unescape_error, push_escaped_char};
 
 #[derive(Clone, Debug)]
@@ -28,7 +25,17 @@ pub struct UnmatchedBrace {
     pub candidate_span: Option<Span>,
 }
 
-crate struct StringReader<'a> {
+crate fn parse_token_trees<'a>(
+    sess: &'a ParseSess,
+    src: &'a str,
+    start_pos: BytePos,
+    override_span: Option<Span>,
+) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
+    StringReader { sess, start_pos, pos: start_pos, end_src_index: src.len(), src, override_span }
+        .into_token_trees()
+}
+
+struct StringReader<'a> {
     sess: &'a ParseSess,
     /// Initial position, read-only.
     start_pos: BytePos,
@@ -37,38 +44,18 @@ crate struct StringReader<'a> {
     /// Stop reading src at this index.
     end_src_index: usize,
     /// Source text to tokenize.
-    src: Lrc<String>,
+    src: &'a str,
     override_span: Option<Span>,
 }
 
 impl<'a> StringReader<'a> {
-    crate fn new(
-        sess: &'a ParseSess,
-        source_file: Lrc<rustc_span::SourceFile>,
-        override_span: Option<Span>,
-    ) -> Self {
-        let src = source_file.src.clone().unwrap_or_else(|| {
-            sess.span_diagnostic
-                .bug(&format!("cannot lex `source_file` without source: {}", source_file.name));
-        });
-
-        StringReader {
-            sess,
-            start_pos: source_file.start_pos,
-            pos: source_file.start_pos,
-            end_src_index: src.len(),
-            src,
-            override_span,
-        }
-    }
-
     fn mk_sp(&self, lo: BytePos, hi: BytePos) -> Span {
         self.override_span.unwrap_or_else(|| Span::with_root_ctxt(lo, hi))
     }
 
     /// Returns the next token, and info about preceding whitespace, if any.
-    fn next_token(&mut self) -> (IsJoint, Token) {
-        let mut is_joint = IsJoint::Joint;
+    fn next_token(&mut self) -> (Spacing, Token) {
+        let mut spacing = Spacing::Joint;
 
         // Skip `#!` at the start of the file
         let start_src_index = self.src_index(self.pos);
@@ -77,7 +64,7 @@ impl<'a> StringReader<'a> {
         if is_beginning_of_file {
             if let Some(shebang_len) = rustc_lexer::strip_shebang(text) {
                 self.pos = self.pos + BytePos::from_usize(shebang_len);
-                is_joint = IsJoint::NonJoint;
+                spacing = Spacing::Alone;
             }
         }
 
@@ -88,7 +75,7 @@ impl<'a> StringReader<'a> {
 
             if text.is_empty() {
                 let span = self.mk_sp(self.pos, self.pos);
-                return (is_joint, Token::new(token::Eof, span));
+                return (spacing, Token::new(token::Eof, span));
             }
 
             let token = rustc_lexer::first_token(text);
@@ -101,9 +88,9 @@ impl<'a> StringReader<'a> {
             match self.cook_lexer_token(token.kind, start) {
                 Some(kind) => {
                     let span = self.mk_sp(start, self.pos);
-                    return (is_joint, Token::new(kind, span));
+                    return (spacing, Token::new(kind, span));
                 }
-                None => is_joint = IsJoint::NonJoint,
+                None => spacing = Spacing::Alone,
             }
         }
     }
diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs
index d5977ca3c7d..0f364bffb13 100644
--- a/compiler/rustc_parse/src/lexer/tokentrees.rs
+++ b/compiler/rustc_parse/src/lexer/tokentrees.rs
@@ -3,8 +3,8 @@ use super::{StringReader, UnmatchedBrace};
 use rustc_ast::token::{self, DelimToken, Token};
 use rustc_ast::tokenstream::{
     DelimSpan,
-    IsJoint::{self, *},
-    TokenStream, TokenTree, TreeAndJoint,
+    Spacing::{self, *},
+    TokenStream, TokenTree, TreeAndSpacing,
 };
 use rustc_ast_pretty::pprust::token_to_string;
 use rustc_data_structures::fx::FxHashMap;
@@ -12,7 +12,7 @@ use rustc_errors::PResult;
 use rustc_span::Span;
 
 impl<'a> StringReader<'a> {
-    crate fn into_token_trees(self) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
+    pub(super) fn into_token_trees(self) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
         let mut tt_reader = TokenTreesReader {
             string_reader: self,
             token: Token::dummy(),
@@ -77,7 +77,7 @@ impl<'a> TokenTreesReader<'a> {
         }
     }
 
-    fn parse_token_tree(&mut self) -> PResult<'a, TreeAndJoint> {
+    fn parse_token_tree(&mut self) -> PResult<'a, TreeAndSpacing> {
         let sm = self.string_reader.sess.source_map();
 
         match self.token.kind {
@@ -262,29 +262,29 @@ impl<'a> TokenTreesReader<'a> {
             }
             _ => {
                 let tt = TokenTree::Token(self.token.take());
-                let mut is_joint = self.bump();
+                let mut spacing = self.bump();
                 if !self.token.is_op() {
-                    is_joint = NonJoint;
+                    spacing = Alone;
                 }
-                Ok((tt, is_joint))
+                Ok((tt, spacing))
             }
         }
     }
 
-    fn bump(&mut self) -> IsJoint {
-        let (joint_to_prev, token) = self.string_reader.next_token();
+    fn bump(&mut self) -> Spacing {
+        let (spacing, token) = self.string_reader.next_token();
         self.token = token;
-        joint_to_prev
+        spacing
     }
 }
 
 #[derive(Default)]
 struct TokenStreamBuilder {
-    buf: Vec<TreeAndJoint>,
+    buf: Vec<TreeAndSpacing>,
 }
 
 impl TokenStreamBuilder {
-    fn push(&mut self, (tree, joint): TreeAndJoint) {
+    fn push(&mut self, (tree, joint): TreeAndSpacing) {
         if let Some((TokenTree::Token(prev_token), Joint)) = self.buf.last() {
             if let TokenTree::Token(token) = &tree {
                 if let Some(glued) = prev_token.glue(token) {
diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs
index 8dc0db01ecb..40e2e34aa05 100644
--- a/compiler/rustc_parse/src/lexer/unicode_chars.rs
+++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs
@@ -332,7 +332,7 @@ const ASCII_ARRAY: &[(char, &str, Option<token::TokenKind>)] = &[
     ('"', "Quotation Mark", None),
 ];
 
-crate fn check_for_substitution<'a>(
+pub(super) fn check_for_substitution<'a>(
     reader: &StringReader<'a>,
     pos: BytePos,
     ch: char,
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index 462279b0a9e..dedb9850b5a 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -8,7 +8,7 @@
 
 use rustc_ast as ast;
 use rustc_ast::token::{self, DelimToken, Nonterminal, Token, TokenKind};
-use rustc_ast::tokenstream::{self, IsJoint, TokenStream, TokenTree};
+use rustc_ast::tokenstream::{self, Spacing, TokenStream, TokenTree};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{Diagnostic, FatalError, Level, PResult};
@@ -109,7 +109,7 @@ pub fn maybe_new_parser_from_source_str(
 }
 
 /// Creates a new parser, handling errors as appropriate if the file doesn't exist.
-/// If a span is given, that is used on an error as the as the source of the problem.
+/// If a span is given, that is used on an error as the source of the problem.
 pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, sp: Option<Span>) -> Parser<'a> {
     source_file_to_parser(sess, file_to_source_file(sess, path, sp))
 }
@@ -200,8 +200,13 @@ pub fn maybe_file_to_stream(
     source_file: Lrc<SourceFile>,
     override_span: Option<Span>,
 ) -> Result<(TokenStream, Vec<lexer::UnmatchedBrace>), Vec<Diagnostic>> {
-    let srdr = lexer::StringReader::new(sess, source_file, override_span);
-    let (token_trees, unmatched_braces) = srdr.into_token_trees();
+    let src = source_file.src.as_ref().unwrap_or_else(|| {
+        sess.span_diagnostic
+            .bug(&format!("cannot lex `source_file` without source: {}", source_file.name));
+    });
+
+    let (token_trees, unmatched_braces) =
+        lexer::parse_token_trees(sess, src.as_str(), source_file.start_pos, override_span);
 
     match token_trees {
         Ok(stream) => Ok((stream, unmatched_braces)),
@@ -263,21 +268,32 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke
         Nonterminal::NtItem(ref item) => {
             prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span)
         }
+        Nonterminal::NtBlock(ref block) => block.tokens.clone(),
+        Nonterminal::NtStmt(ref stmt) => {
+            // FIXME: We currently only collect tokens for `:stmt`
+            // matchers in `macro_rules!` macros. When we start collecting
+            // tokens for attributes on statements, we will need to prepend
+            // attributes here
+            stmt.tokens.clone()
+        }
         Nonterminal::NtPat(ref pat) => pat.tokens.clone(),
+        Nonterminal::NtTy(ref ty) => ty.tokens.clone(),
         Nonterminal::NtIdent(ident, is_raw) => {
             Some(tokenstream::TokenTree::token(token::Ident(ident.name, is_raw), ident.span).into())
         }
         Nonterminal::NtLifetime(ident) => {
             Some(tokenstream::TokenTree::token(token::Lifetime(ident.name), ident.span).into())
         }
+        Nonterminal::NtMeta(ref attr) => attr.tokens.clone(),
+        Nonterminal::NtPath(ref path) => path.tokens.clone(),
+        Nonterminal::NtVis(ref vis) => vis.tokens.clone(),
         Nonterminal::NtTT(ref tt) => Some(tt.clone().into()),
-        Nonterminal::NtExpr(ref expr) => {
+        Nonterminal::NtExpr(ref expr) | Nonterminal::NtLiteral(ref expr) => {
             if expr.tokens.is_none() {
                 debug!("missing tokens for expr {:?}", expr);
             }
             prepend_attrs(sess, &expr.attrs, expr.tokens.as_ref(), span)
         }
-        _ => None,
     };
 
     // FIXME(#43081): Avoid this pretty-print + reparse hack
@@ -432,7 +448,7 @@ pub fn tokenstream_probably_equal_for_proc_macro(
             // issue #75734 tracks resolving this.
             nt_to_tokenstream(nt, sess, *span).into_trees()
         } else {
-            TokenStream::new(vec![(tree, IsJoint::NonJoint)]).into_trees()
+            TokenStream::new(vec![(tree, Spacing::Alone)]).into_trees()
         }
     };
 
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 4e4429e461f..98f94098bfc 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -162,7 +162,7 @@ impl<'a> Parser<'a> {
         } else {
             let path = self.parse_path(PathStyle::Mod)?;
             let args = self.parse_attr_args()?;
-            ast::AttrItem { path, args }
+            ast::AttrItem { path, args, tokens: None }
         })
     }
 
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 12efe391fb9..e2a735188f9 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -28,7 +28,7 @@ pub(super) fn dummy_arg(ident: Ident) -> Param {
         span: ident.span,
         tokens: None,
     });
-    let ty = Ty { kind: TyKind::Err, span: ident.span, id: ast::DUMMY_NODE_ID };
+    let ty = Ty { kind: TyKind::Err, span: ident.span, id: ast::DUMMY_NODE_ID, tokens: None };
     Param {
         attrs: AttrVec::default(),
         id: ast::DUMMY_NODE_ID,
@@ -75,7 +75,12 @@ impl RecoverQPath for Ty {
         Some(P(self.clone()))
     }
     fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
-        Self { span: path.span, kind: TyKind::Path(qself, path), id: ast::DUMMY_NODE_ID }
+        Self {
+            span: path.span,
+            kind: TyKind::Path(qself, path),
+            id: ast::DUMMY_NODE_ID,
+            tokens: None,
+        }
     }
 }
 
@@ -896,7 +901,7 @@ impl<'a> Parser<'a> {
     ) -> PResult<'a, P<T>> {
         self.expect(&token::ModSep)?;
 
-        let mut path = ast::Path { segments: Vec::new(), span: DUMMY_SP };
+        let mut path = ast::Path { segments: Vec::new(), span: DUMMY_SP, tokens: None };
         self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?;
         path.span = ty_span.to(self.prev_token.span);
 
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 9143af651df..26ca9980127 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -28,7 +28,7 @@ impl<'a> Parser<'a> {
     /// Parses a source module as a crate. This is the main entry point for the parser.
     pub fn parse_crate_mod(&mut self) -> PResult<'a, ast::Crate> {
         let lo = self.token.span;
-        let (module, attrs) = self.parse_mod(&token::Eof)?;
+        let (module, attrs) = self.parse_mod(&token::Eof, Unsafe::No)?;
         let span = lo.to(self.token.span);
         let proc_macros = Vec::new(); // Filled in by `proc_macro_harness::inject()`.
         Ok(ast::Crate { attrs, module, span, proc_macros })
@@ -36,27 +36,38 @@ impl<'a> Parser<'a> {
 
     /// Parses a `mod <foo> { ... }` or `mod <foo>;` item.
     fn parse_item_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> {
+        let unsafety = self.parse_unsafety();
+        self.expect_keyword(kw::Mod)?;
         let id = self.parse_ident()?;
         let (module, mut inner_attrs) = if self.eat(&token::Semi) {
-            Default::default()
+            (Mod { inner: Span::default(), unsafety, items: Vec::new(), inline: false }, Vec::new())
         } else {
             self.expect(&token::OpenDelim(token::Brace))?;
-            self.parse_mod(&token::CloseDelim(token::Brace))?
+            self.parse_mod(&token::CloseDelim(token::Brace), unsafety)?
         };
         attrs.append(&mut inner_attrs);
         Ok((id, ItemKind::Mod(module)))
     }
 
     /// Parses the contents of a module (inner attributes followed by module items).
-    pub fn parse_mod(&mut self, term: &TokenKind) -> PResult<'a, (Mod, Vec<Attribute>)> {
+    pub fn parse_mod(
+        &mut self,
+        term: &TokenKind,
+        unsafety: Unsafe,
+    ) -> PResult<'a, (Mod, Vec<Attribute>)> {
         let lo = self.token.span;
         let attrs = self.parse_inner_attributes()?;
-        let module = self.parse_mod_items(term, lo)?;
+        let module = self.parse_mod_items(term, lo, unsafety)?;
         Ok((module, attrs))
     }
 
     /// Given a termination token, parses all of the items in a module.
-    fn parse_mod_items(&mut self, term: &TokenKind, inner_lo: Span) -> PResult<'a, Mod> {
+    fn parse_mod_items(
+        &mut self,
+        term: &TokenKind,
+        inner_lo: Span,
+        unsafety: Unsafe,
+    ) -> PResult<'a, Mod> {
         let mut items = vec![];
         while let Some(item) = self.parse_item()? {
             items.push(item);
@@ -75,7 +86,7 @@ impl<'a> Parser<'a> {
 
         let hi = if self.token.span.is_dummy() { inner_lo } else { self.prev_token.span };
 
-        Ok(Mod { inner: inner_lo.to(hi), items, inline: true })
+        Ok(Mod { inner: inner_lo.to(hi), unsafety, items, inline: true })
     }
 }
 
@@ -176,7 +187,7 @@ impl<'a> Parser<'a> {
 
     /// Error in-case a non-inherited visibility was parsed but no item followed.
     fn error_on_unmatched_vis(&self, vis: &Visibility) {
-        if let VisibilityKind::Inherited = vis.node {
+        if let VisibilityKind::Inherited = vis.kind {
             return;
         }
         let vs = pprust::vis_to_string(&vis);
@@ -235,8 +246,13 @@ impl<'a> Parser<'a> {
                 self.parse_item_extern_crate()?
             } else {
                 // EXTERN BLOCK
-                self.parse_item_foreign_mod(attrs)?
+                self.parse_item_foreign_mod(attrs, Unsafe::No)?
             }
+        } else if self.is_unsafe_foreign_mod() {
+            // EXTERN BLOCK
+            let unsafety = self.parse_unsafety();
+            self.expect_keyword(kw::Extern)?;
+            self.parse_item_foreign_mod(attrs, unsafety)?
         } else if self.is_static_global() {
             // STATIC ITEM
             self.bump(); // `static`
@@ -256,7 +272,9 @@ impl<'a> Parser<'a> {
         {
             // IMPL ITEM
             self.parse_item_impl(attrs, def())?
-        } else if self.eat_keyword(kw::Mod) {
+        } else if self.check_keyword(kw::Mod)
+            || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Mod])
+        {
             // MODULE ITEM
             self.parse_item_mod(attrs)?
         } else if self.eat_keyword(kw::Type) {
@@ -278,7 +296,7 @@ impl<'a> Parser<'a> {
         } else if self.is_macro_rules_item() {
             // MACRO_RULES ITEM
             self.parse_item_macro_rules(vis)?
-        } else if vis.node.is_pub() && self.isnt_macro_invocation() {
+        } else if vis.kind.is_pub() && self.isnt_macro_invocation() {
             self.recover_missing_kw_before_item()?;
             return Ok(None);
         } else if macros_allowed && self.check_path() {
@@ -492,7 +510,12 @@ impl<'a> Parser<'a> {
         {
             let span = self.prev_token.span.between(self.token.span);
             self.struct_span_err(span, "missing trait in a trait impl").emit();
-            P(Ty { kind: TyKind::Path(None, err_path(span)), span, id: DUMMY_NODE_ID })
+            P(Ty {
+                kind: TyKind::Path(None, err_path(span)),
+                span,
+                id: DUMMY_NODE_ID,
+                tokens: None,
+            })
         } else {
             self.parse_ty()?
         };
@@ -764,7 +787,7 @@ impl<'a> Parser<'a> {
     fn parse_use_tree(&mut self) -> PResult<'a, UseTree> {
         let lo = self.token.span;
 
-        let mut prefix = ast::Path { segments: Vec::new(), span: lo.shrink_to_lo() };
+        let mut prefix = ast::Path { segments: Vec::new(), span: lo.shrink_to_lo(), tokens: None };
         let kind = if self.check(&token::OpenDelim(token::Brace))
             || self.check(&token::BinOp(token::Star))
             || self.is_import_coupler()
@@ -893,10 +916,14 @@ impl<'a> Parser<'a> {
     /// extern "C" {}
     /// extern {}
     /// ```
-    fn parse_item_foreign_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> {
+    fn parse_item_foreign_mod(
+        &mut self,
+        attrs: &mut Vec<Attribute>,
+        unsafety: Unsafe,
+    ) -> PResult<'a, ItemInfo> {
         let abi = self.parse_abi(); // ABI?
         let items = self.parse_item_list(attrs, |p| p.parse_foreign_item())?;
-        let module = ast::ForeignMod { abi, items };
+        let module = ast::ForeignMod { unsafety, abi, items };
         Ok((Ident::invalid(), ItemKind::ForeignMod(module)))
     }
 
@@ -938,6 +965,15 @@ impl<'a> Parser<'a> {
             .emit();
     }
 
+    fn is_unsafe_foreign_mod(&self) -> bool {
+        self.token.is_keyword(kw::Unsafe)
+            && self.is_keyword_ahead(1, &[kw::Extern])
+            && self.look_ahead(
+                2 + self.look_ahead(2, |t| t.can_begin_literal_maybe_minus() as usize),
+                |t| t.kind == token::OpenDelim(token::Brace),
+            )
+    }
+
     fn is_static_global(&mut self) -> bool {
         if self.check_keyword(kw::Static) {
             // Check if this could be a closure.
@@ -1015,7 +1051,7 @@ impl<'a> Parser<'a> {
 
         // The user intended that the type be inferred,
         // so treat this as if the user wrote e.g. `const A: _ = expr;`.
-        P(Ty { kind: TyKind::Infer, span: id.span, id: ast::DUMMY_NODE_ID })
+        P(Ty { kind: TyKind::Infer, span: id.span, id: ast::DUMMY_NODE_ID, tokens: None })
     }
 
     /// Parses an enum declaration.
@@ -1382,7 +1418,7 @@ impl<'a> Parser<'a> {
     /// Item macro invocations or `macro_rules!` definitions need inherited visibility.
     /// If that's not the case, emit an error.
     fn complain_if_pub_macro(&self, vis: &Visibility, macro_rules: bool) {
-        if let VisibilityKind::Inherited = vis.node {
+        if let VisibilityKind::Inherited = vis.kind {
             return;
         }
 
@@ -1552,10 +1588,14 @@ impl<'a> Parser<'a> {
             // `$qual fn` or `$qual $qual`:
             || QUALS.iter().any(|&kw| self.check_keyword(kw))
                 && self.look_ahead(1, |t| {
-                    // ...qualified and then `fn`, e.g. `const fn`.
+                    // `$qual fn`, e.g. `const fn` or `async fn`.
                     t.is_keyword(kw::Fn)
-                    // Two qualifiers. This is enough. Due `async` we need to check that it's reserved.
-                    || t.is_non_raw_ident_where(|i| QUALS.contains(&i.name) && i.is_reserved())
+                    // Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`.
+                    || t.is_non_raw_ident_where(|i| QUALS.contains(&i.name)
+                        // Rule out 2015 `const async: T = val`.
+                        && i.is_reserved()
+                        // Rule out unsafe extern block.
+                        && !self.is_unsafe_foreign_mod())
                 })
             // `extern ABI fn`
             || self.check_keyword(kw::Extern)
@@ -1567,9 +1607,9 @@ impl<'a> Parser<'a> {
     /// up to and including the `fn` keyword. The formal grammar is:
     ///
     /// ```
-    /// Extern = "extern" StringLit ;
+    /// Extern = "extern" StringLit? ;
     /// FnQual = "const"? "async"? "unsafe"? Extern? ;
-    /// FnFrontMatter = FnQual? "fn" ;
+    /// FnFrontMatter = FnQual "fn" ;
     /// ```
     pub(super) fn parse_fn_front_matter(&mut self) -> PResult<'a, FnHeader> {
         let constness = self.parse_constness();
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 84edfecad19..5eefae3af60 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -15,14 +15,14 @@ pub use path::PathStyle;
 
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, DelimToken, Token, TokenKind};
-use rustc_ast::tokenstream::{self, DelimSpan, TokenStream, TokenTree, TreeAndJoint};
+use rustc_ast::tokenstream::{self, DelimSpan, TokenStream, TokenTree, TreeAndSpacing};
 use rustc_ast::DUMMY_NODE_ID;
 use rustc_ast::{self as ast, AttrStyle, AttrVec, Const, CrateSugar, Extern, Unsafe};
 use rustc_ast::{Async, MacArgs, MacDelimiter, Mutability, StrLit, Visibility, VisibilityKind};
 use rustc_ast_pretty::pprust;
 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, FatalError, PResult};
 use rustc_session::parse::ParseSess;
-use rustc_span::source_map::{respan, Span, DUMMY_SP};
+use rustc_span::source_map::{Span, DUMMY_SP};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use tracing::debug;
 
@@ -118,7 +118,7 @@ impl<'a> Drop for Parser<'a> {
 struct TokenCursor {
     frame: TokenCursorFrame,
     stack: Vec<TokenCursorFrame>,
-    cur_token: Option<TreeAndJoint>,
+    cur_token: Option<TreeAndSpacing>,
     collecting: Option<Collecting>,
 }
 
@@ -136,7 +136,7 @@ struct TokenCursorFrame {
 struct Collecting {
     /// Holds the current tokens captured during the most
     /// recent call to `collect_tokens`
-    buf: Vec<TreeAndJoint>,
+    buf: Vec<TreeAndSpacing>,
     /// The depth of the `TokenCursor` stack at the time
     /// collection was started. When we encounter a `TokenTree::Delimited`,
     /// we want to record the `TokenTree::Delimited` itself,
@@ -167,7 +167,7 @@ impl TokenCursor {
             let tree = if !self.frame.open_delim {
                 self.frame.open_delim = true;
                 TokenTree::open_tt(self.frame.span, self.frame.delim).into()
-            } else if let Some(tree) = self.frame.tree_cursor.next_with_joint() {
+            } else if let Some(tree) = self.frame.tree_cursor.next_with_spacing() {
                 tree
             } else if !self.frame.close_delim {
                 self.frame.close_delim = true;
@@ -1022,14 +1022,22 @@ impl<'a> Parser<'a> {
         if self.is_crate_vis() {
             self.bump(); // `crate`
             self.sess.gated_spans.gate(sym::crate_visibility_modifier, self.prev_token.span);
-            return Ok(respan(self.prev_token.span, VisibilityKind::Crate(CrateSugar::JustCrate)));
+            return Ok(Visibility {
+                span: self.prev_token.span,
+                kind: VisibilityKind::Crate(CrateSugar::JustCrate),
+                tokens: None,
+            });
         }
 
         if !self.eat_keyword(kw::Pub) {
             // We need a span for our `Spanned<VisibilityKind>`, but there's inherently no
             // keyword to grab a span from for inherited visibility; an empty span at the
             // beginning of the current token would seem to be the "Schelling span".
-            return Ok(respan(self.token.span.shrink_to_lo(), VisibilityKind::Inherited));
+            return Ok(Visibility {
+                span: self.token.span.shrink_to_lo(),
+                kind: VisibilityKind::Inherited,
+                tokens: None,
+            });
         }
         let lo = self.prev_token.span;
 
@@ -1046,7 +1054,11 @@ impl<'a> Parser<'a> {
                 self.bump(); // `crate`
                 self.expect(&token::CloseDelim(token::Paren))?; // `)`
                 let vis = VisibilityKind::Crate(CrateSugar::PubCrate);
-                return Ok(respan(lo.to(self.prev_token.span), vis));
+                return Ok(Visibility {
+                    span: lo.to(self.prev_token.span),
+                    kind: vis,
+                    tokens: None,
+                });
             } else if self.is_keyword_ahead(1, &[kw::In]) {
                 // Parse `pub(in path)`.
                 self.bump(); // `(`
@@ -1054,7 +1066,11 @@ impl<'a> Parser<'a> {
                 let path = self.parse_path(PathStyle::Mod)?; // `path`
                 self.expect(&token::CloseDelim(token::Paren))?; // `)`
                 let vis = VisibilityKind::Restricted { path: P(path), id: ast::DUMMY_NODE_ID };
-                return Ok(respan(lo.to(self.prev_token.span), vis));
+                return Ok(Visibility {
+                    span: lo.to(self.prev_token.span),
+                    kind: vis,
+                    tokens: None,
+                });
             } else if self.look_ahead(2, |t| t == &token::CloseDelim(token::Paren))
                 && self.is_keyword_ahead(1, &[kw::Super, kw::SelfLower])
             {
@@ -1063,7 +1079,11 @@ impl<'a> Parser<'a> {
                 let path = self.parse_path(PathStyle::Mod)?; // `super`/`self`
                 self.expect(&token::CloseDelim(token::Paren))?; // `)`
                 let vis = VisibilityKind::Restricted { path: P(path), id: ast::DUMMY_NODE_ID };
-                return Ok(respan(lo.to(self.prev_token.span), vis));
+                return Ok(Visibility {
+                    span: lo.to(self.prev_token.span),
+                    kind: vis,
+                    tokens: None,
+                });
             } else if let FollowedByType::No = fbt {
                 // Provide this diagnostic if a type cannot follow;
                 // in particular, if this is not a tuple struct.
@@ -1072,7 +1092,7 @@ impl<'a> Parser<'a> {
             }
         }
 
-        Ok(respan(lo, VisibilityKind::Public))
+        Ok(Visibility { span: lo, kind: VisibilityKind::Public, tokens: None })
     }
 
     /// Recovery for e.g. `pub(something) fn ...` or `struct X { pub(something) y: Z }`
@@ -1154,7 +1174,7 @@ impl<'a> Parser<'a> {
         f: impl FnOnce(&mut Self) -> PResult<'a, R>,
     ) -> PResult<'a, (R, TokenStream)> {
         // Record all tokens we parse when parsing this item.
-        let tokens: Vec<TreeAndJoint> = self.token_cursor.cur_token.clone().into_iter().collect();
+        let tokens: Vec<TreeAndSpacing> = self.token_cursor.cur_token.clone().into_iter().collect();
         debug!("collect_tokens: starting with {:?}", tokens);
 
         // We need special handling for the case where `collect_tokens` is called
diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs
index f40cd1131d2..15660fd574c 100644
--- a/compiler/rustc_parse/src/parser/nonterminal.rs
+++ b/compiler/rustc_parse/src/parser/nonterminal.rs
@@ -111,11 +111,28 @@ impl<'a> Parser<'a> {
                     return Err(self.struct_span_err(self.token.span, "expected an item keyword"));
                 }
             },
-            NonterminalKind::Block => token::NtBlock(self.parse_block()?),
-            NonterminalKind::Stmt => match self.parse_stmt()? {
-                Some(s) => token::NtStmt(s),
-                None => return Err(self.struct_span_err(self.token.span, "expected a statement")),
-            },
+            NonterminalKind::Block => {
+                let (mut block, tokens) = self.collect_tokens(|this| this.parse_block())?;
+                // We have have eaten an NtBlock, which could already have tokens
+                if block.tokens.is_none() {
+                    block.tokens = Some(tokens);
+                }
+                token::NtBlock(block)
+            }
+            NonterminalKind::Stmt => {
+                let (stmt, tokens) = self.collect_tokens(|this| this.parse_stmt())?;
+                match stmt {
+                    Some(mut s) => {
+                        if s.tokens.is_none() {
+                            s.tokens = Some(tokens);
+                        }
+                        token::NtStmt(s)
+                    }
+                    None => {
+                        return Err(self.struct_span_err(self.token.span, "expected a statement"));
+                    }
+                }
+            }
             NonterminalKind::Pat => {
                 let (mut pat, tokens) = self.collect_tokens(|this| this.parse_pat(None))?;
                 // We have have eaten an NtPat, which could already have tokens
@@ -133,8 +150,23 @@ impl<'a> Parser<'a> {
                 }
                 token::NtExpr(expr)
             }
-            NonterminalKind::Literal => token::NtLiteral(self.parse_literal_maybe_minus()?),
-            NonterminalKind::Ty => token::NtTy(self.parse_ty()?),
+            NonterminalKind::Literal => {
+                let (mut lit, tokens) =
+                    self.collect_tokens(|this| this.parse_literal_maybe_minus())?;
+                // We have have eaten a nonterminal, which  could already have tokens
+                if lit.tokens.is_none() {
+                    lit.tokens = Some(tokens);
+                }
+                token::NtLiteral(lit)
+            }
+            NonterminalKind::Ty => {
+                let (mut ty, tokens) = self.collect_tokens(|this| this.parse_ty())?;
+                // We have an eaten an NtTy, which could already have tokens
+                if ty.tokens.is_none() {
+                    ty.tokens = Some(tokens);
+                }
+                token::NtTy(ty)
+            }
             // this could be handled like a token, since it is one
             NonterminalKind::Ident => {
                 if let Some((ident, is_raw)) = get_macro_ident(&self.token) {
@@ -146,10 +178,33 @@ impl<'a> Parser<'a> {
                     return Err(self.struct_span_err(self.token.span, msg));
                 }
             }
-            NonterminalKind::Path => token::NtPath(self.parse_path(PathStyle::Type)?),
-            NonterminalKind::Meta => token::NtMeta(P(self.parse_attr_item()?)),
+            NonterminalKind::Path => {
+                let (mut path, tokens) =
+                    self.collect_tokens(|this| this.parse_path(PathStyle::Type))?;
+                // We have have eaten an NtPath, which could already have tokens
+                if path.tokens.is_none() {
+                    path.tokens = Some(tokens);
+                }
+                token::NtPath(path)
+            }
+            NonterminalKind::Meta => {
+                let (mut attr, tokens) = self.collect_tokens(|this| this.parse_attr_item())?;
+                // We may have eaten a nonterminal, which could already have tokens
+                if attr.tokens.is_none() {
+                    attr.tokens = Some(tokens);
+                }
+                token::NtMeta(P(attr))
+            }
             NonterminalKind::TT => token::NtTT(self.parse_token_tree()),
-            NonterminalKind::Vis => token::NtVis(self.parse_visibility(FollowedByType::Yes)?),
+            NonterminalKind::Vis => {
+                let (mut vis, tokens) =
+                    self.collect_tokens(|this| this.parse_visibility(FollowedByType::Yes))?;
+                // We may have etan an `NtVis`, which could already have tokens
+                if vis.tokens.is_none() {
+                    vis.tokens = Some(tokens);
+                }
+                token::NtVis(vis)
+            }
             NonterminalKind::Lifetime => {
                 if self.check_lifetime() {
                     token::NtLifetime(self.expect_lifetime().ident)
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 54b4df8613f..66ce015d02e 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -64,7 +64,7 @@ impl<'a> Parser<'a> {
             path_span = path_lo.to(self.prev_token.span);
         } else {
             path_span = self.token.span.to(self.token.span);
-            path = ast::Path { segments: Vec::new(), span: path_span };
+            path = ast::Path { segments: Vec::new(), span: path_span, tokens: None };
         }
 
         // See doc comment for `unmatched_angle_bracket_count`.
@@ -81,7 +81,10 @@ impl<'a> Parser<'a> {
         let qself = QSelf { ty, path_span, position: path.segments.len() };
         self.parse_path_segments(&mut path.segments, style)?;
 
-        Ok((qself, Path { segments: path.segments, span: lo.to(self.prev_token.span) }))
+        Ok((
+            qself,
+            Path { segments: path.segments, span: lo.to(self.prev_token.span), tokens: None },
+        ))
     }
 
     /// Recover from an invalid single colon, when the user likely meant a qualified path.
@@ -144,7 +147,7 @@ impl<'a> Parser<'a> {
         }
         self.parse_path_segments(&mut segments, style)?;
 
-        Ok(Path { segments, span: lo.to(self.prev_token.span) })
+        Ok(Path { segments, span: lo.to(self.prev_token.span), tokens: None })
     }
 
     pub(super) fn parse_path_segments(
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 947ca6b5bd5..64b959e8325 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -411,11 +411,11 @@ impl<'a> Parser<'a> {
     }
 
     pub(super) fn mk_block(&self, stmts: Vec<Stmt>, rules: BlockCheckMode, span: Span) -> P<Block> {
-        P(Block { stmts, id: DUMMY_NODE_ID, rules, span })
+        P(Block { stmts, id: DUMMY_NODE_ID, rules, span, tokens: None })
     }
 
     pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt {
-        Stmt { id: DUMMY_NODE_ID, kind, span }
+        Stmt { id: DUMMY_NODE_ID, kind, span, tokens: None }
     }
 
     fn mk_stmt_err(&self, span: Span) -> Stmt {
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 4356850818e..259764a317d 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -626,6 +626,6 @@ impl<'a> Parser<'a> {
     }
 
     pub(super) fn mk_ty(&self, span: Span, kind: TyKind) -> P<Ty> {
-        P(Ty { kind, span, id: ast::DUMMY_NODE_ID })
+        P(Ty { kind, span, id: ast::DUMMY_NODE_ID, tokens: None })
     }
 }
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 761724be57d..565313902a4 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -218,7 +218,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
         speculative: bool,
     ) -> Result<ty::Visibility, VisResolutionError<'ast>> {
         let parent_scope = &self.parent_scope;
-        match vis.node {
+        match vis.kind {
             ast::VisibilityKind::Public => Ok(ty::Visibility::Public),
             ast::VisibilityKind::Crate(..) => {
                 Ok(ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)))
@@ -796,23 +796,26 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                         vis
                     };
 
+                    let mut ret_fields = Vec::with_capacity(vdata.fields().len());
+
                     for field in vdata.fields() {
                         // NOTE: The field may be an expansion placeholder, but expansion sets
                         // correct visibilities for unnamed field placeholders specifically, so the
                         // constructor visibility should still be determined correctly.
-                        if let Ok(field_vis) = self.resolve_visibility_speculative(&field.vis, true)
-                        {
-                            if ctor_vis.is_at_least(field_vis, &*self.r) {
-                                ctor_vis = field_vis;
-                            }
+                        let field_vis = self
+                            .resolve_visibility_speculative(&field.vis, true)
+                            .unwrap_or(ty::Visibility::Public);
+                        if ctor_vis.is_at_least(field_vis, &*self.r) {
+                            ctor_vis = field_vis;
                         }
+                        ret_fields.push(field_vis);
                     }
                     let ctor_res = Res::Def(
                         DefKind::Ctor(CtorOf::Struct, CtorKind::from_ast(vdata)),
                         self.r.local_def_id(ctor_node_id).to_def_id(),
                     );
                     self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, sp, expansion));
-                    self.r.struct_constructors.insert(def_id, (ctor_res, ctor_vis));
+                    self.r.struct_constructors.insert(def_id, (ctor_res, ctor_vis, ret_fields));
                 }
             }
 
@@ -964,7 +967,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
             Res::Def(DefKind::Ctor(CtorOf::Struct, ..), def_id) => {
                 let parent = cstore.def_key(def_id).parent;
                 if let Some(struct_def_id) = parent.map(|index| DefId { index, ..def_id }) {
-                    self.r.struct_constructors.insert(struct_def_id, (res, vis));
+                    self.r.struct_constructors.insert(struct_def_id, (res, vis, vec![]));
                 }
             }
             _ => {}
diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs
index 5624a6b6acc..89ce89b2e9a 100644
--- a/compiler/rustc_resolve/src/check_unused.rs
+++ b/compiler/rustc_resolve/src/check_unused.rs
@@ -105,7 +105,7 @@ impl<'a, 'b> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b> {
         // because this means that they were generated in some fashion by the
         // compiler and we don't need to consider them.
         if let ast::ItemKind::Use(..) = item.kind {
-            if item.vis.node.is_pub() || item.span.is_dummy() {
+            if item.vis.kind.is_pub() || item.span.is_dummy() {
                 return;
             }
         }
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 48e1068b8da..7a0503d68f3 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -794,7 +794,7 @@ impl<'a> Resolver<'a> {
                         }
 
                         segms.push(ast::PathSegment::from_ident(ident));
-                        let path = Path { span: name_binding.span, segments: segms };
+                        let path = Path { span: name_binding.span, segments: segms, tokens: None };
                         let did = match res {
                             Res::Def(DefKind::Ctor(..), did) => this.parent(did),
                             _ => res.opt_def_id(),
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 2b2123e295d..07f36c7b7ad 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -188,7 +188,7 @@ crate enum PathSource<'a> {
     // Paths in struct expressions and patterns `Path { .. }`.
     Struct,
     // Paths in tuple struct patterns `Path(..)`.
-    TupleStruct(Span),
+    TupleStruct(Span, &'a [Span]),
     // `m::A::B` in `<T as m::A>::B::C`.
     TraitItem(Namespace),
 }
@@ -197,7 +197,7 @@ impl<'a> PathSource<'a> {
     fn namespace(self) -> Namespace {
         match self {
             PathSource::Type | PathSource::Trait(_) | PathSource::Struct => TypeNS,
-            PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(_) => ValueNS,
+            PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(..) => ValueNS,
             PathSource::TraitItem(ns) => ns,
         }
     }
@@ -208,7 +208,7 @@ impl<'a> PathSource<'a> {
             | PathSource::Expr(..)
             | PathSource::Pat
             | PathSource::Struct
-            | PathSource::TupleStruct(_) => true,
+            | PathSource::TupleStruct(..) => true,
             PathSource::Trait(_) | PathSource::TraitItem(..) => false,
         }
     }
@@ -219,7 +219,7 @@ impl<'a> PathSource<'a> {
             PathSource::Trait(_) => "trait",
             PathSource::Pat => "unit struct, unit variant or constant",
             PathSource::Struct => "struct, variant or union type",
-            PathSource::TupleStruct(_) => "tuple struct or tuple variant",
+            PathSource::TupleStruct(..) => "tuple struct or tuple variant",
             PathSource::TraitItem(ns) => match ns {
                 TypeNS => "associated type",
                 ValueNS => "method or associated constant",
@@ -305,7 +305,7 @@ impl<'a> PathSource<'a> {
                 | Res::SelfCtor(..) => true,
                 _ => false,
             },
-            PathSource::TupleStruct(_) => match res {
+            PathSource::TupleStruct(..) => match res {
                 Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..) => true,
                 _ => false,
             },
@@ -340,8 +340,8 @@ impl<'a> PathSource<'a> {
             (PathSource::Struct, false) => error_code!(E0422),
             (PathSource::Expr(..), true) => error_code!(E0423),
             (PathSource::Expr(..), false) => error_code!(E0425),
-            (PathSource::Pat | PathSource::TupleStruct(_), true) => error_code!(E0532),
-            (PathSource::Pat | PathSource::TupleStruct(_), false) => error_code!(E0531),
+            (PathSource::Pat | PathSource::TupleStruct(..), true) => error_code!(E0532),
+            (PathSource::Pat | PathSource::TupleStruct(..), false) => error_code!(E0531),
             (PathSource::TraitItem(..), true) => error_code!(E0575),
             (PathSource::TraitItem(..), false) => error_code!(E0576),
         }
@@ -411,7 +411,7 @@ struct LateResolutionVisitor<'a, 'b, 'ast> {
 }
 
 /// Walks the whole crate in DFS order, visiting each item, resolving names as it goes.
-impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
+impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
     fn visit_item(&mut self, item: &'ast Item) {
         let prev = replace(&mut self.diagnostic_metadata.current_item, Some(item));
         // Always report errors in items we just entered.
@@ -659,7 +659,7 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
     }
 }
 
-impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
+impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
     fn new(resolver: &'b mut Resolver<'a>) -> LateResolutionVisitor<'a, 'b, 'ast> {
         // During late resolution we only track the module component of the parent scope,
         // although it may be useful to track other components as well for diagnostics.
@@ -1539,8 +1539,16 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                         .unwrap_or_else(|| self.fresh_binding(ident, pat.id, pat_src, bindings));
                     self.r.record_partial_res(pat.id, PartialRes::new(res));
                 }
-                PatKind::TupleStruct(ref path, ..) => {
-                    self.smart_resolve_path(pat.id, None, path, PathSource::TupleStruct(pat.span));
+                PatKind::TupleStruct(ref path, ref sub_patterns) => {
+                    self.smart_resolve_path(
+                        pat.id,
+                        None,
+                        path,
+                        PathSource::TupleStruct(
+                            pat.span,
+                            self.r.arenas.alloc_pattern_spans(sub_patterns.iter().map(|p| p.span)),
+                        ),
+                    );
                 }
                 PatKind::Path(ref qself, ref path) => {
                     self.smart_resolve_path(pat.id, qself.as_ref(), path, PathSource::Pat);
@@ -1967,7 +1975,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
 
         if qself.is_none() {
             let path_seg = |seg: &Segment| PathSegment::from_ident(seg.ident);
-            let path = Path { segments: path.iter().map(path_seg).collect(), span };
+            let path = Path { segments: path.iter().map(path_seg).collect(), span, tokens: None };
             if let Ok((_, res)) =
                 self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false)
             {
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 8cb6b6553ff..9b5650c260c 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -83,13 +83,14 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str
     let enum_path = ast::Path {
         span: suggestion.path.span,
         segments: suggestion.path.segments[0..path_len - 1].to_vec(),
+        tokens: None,
     };
     let enum_path_string = path_names_to_string(&enum_path);
 
     (variant_path_string, enum_path_string)
 }
 
-impl<'a> LateResolutionVisitor<'a, '_, '_> {
+impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
     fn def_span(&self, def_id: DefId) -> Option<Span> {
         match def_id.krate {
             LOCAL_CRATE => self.r.opt_span(def_id),
@@ -622,12 +623,12 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
                         );
                     }
                 }
-                PathSource::Expr(_) | PathSource::TupleStruct(_) | PathSource::Pat => {
+                PathSource::Expr(_) | PathSource::TupleStruct(..) | PathSource::Pat => {
                     let span = match &source {
                         PathSource::Expr(Some(Expr {
                             span, kind: ExprKind::Call(_, _), ..
                         }))
-                        | PathSource::TupleStruct(span) => {
+                        | PathSource::TupleStruct(span, _) => {
                             // We want the main underline to cover the suggested code as well for
                             // cleaner output.
                             err.set_span(*span);
@@ -639,7 +640,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
                         err.span_label(span, &format!("`{}` defined here", path_str));
                     }
                     let (tail, descr, applicability) = match source {
-                        PathSource::Pat | PathSource::TupleStruct(_) => {
+                        PathSource::Pat | PathSource::TupleStruct(..) => {
                             ("", "pattern", Applicability::MachineApplicable)
                         }
                         _ => (": val", "literal", Applicability::HasPlaceholders),
@@ -704,7 +705,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
             }
             (
                 Res::Def(DefKind::Enum, def_id),
-                PathSource::TupleStruct(_) | PathSource::Expr(..),
+                PathSource::TupleStruct(..) | PathSource::Expr(..),
             ) => {
                 if self
                     .diagnostic_metadata
@@ -744,15 +745,50 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
                 }
             }
             (Res::Def(DefKind::Struct, def_id), _) if ns == ValueNS => {
-                if let Some((ctor_def, ctor_vis)) = self.r.struct_constructors.get(&def_id).cloned()
+                if let Some((ctor_def, ctor_vis, fields)) =
+                    self.r.struct_constructors.get(&def_id).cloned()
                 {
                     let accessible_ctor =
                         self.r.is_accessible_from(ctor_vis, self.parent_scope.module);
                     if is_expected(ctor_def) && !accessible_ctor {
-                        err.span_label(
-                            span,
-                            "constructor is not visible here due to private fields".to_string(),
-                        );
+                        let mut better_diag = false;
+                        if let PathSource::TupleStruct(_, pattern_spans) = source {
+                            if pattern_spans.len() > 0 && fields.len() == pattern_spans.len() {
+                                let non_visible_spans: Vec<Span> = fields
+                                    .iter()
+                                    .zip(pattern_spans.iter())
+                                    .filter_map(|(vis, span)| {
+                                        match self
+                                            .r
+                                            .is_accessible_from(*vis, self.parent_scope.module)
+                                        {
+                                            true => None,
+                                            false => Some(*span),
+                                        }
+                                    })
+                                    .collect();
+                                // Extra check to be sure
+                                if non_visible_spans.len() > 0 {
+                                    let mut m: rustc_span::MultiSpan =
+                                        non_visible_spans.clone().into();
+                                    non_visible_spans.into_iter().for_each(|s| {
+                                        m.push_span_label(s, "private field".to_string())
+                                    });
+                                    err.span_note(
+                                        m,
+                                        "constructor is not visible here due to private fields",
+                                    );
+                                    better_diag = true;
+                                }
+                            }
+                        }
+
+                        if !better_diag {
+                            err.span_label(
+                                span,
+                                "constructor is not visible here due to private fields".to_string(),
+                            );
+                        }
                     }
                 } else {
                     bad_struct_syntax_suggestion(def_id);
@@ -1065,7 +1101,8 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
                     path_segments.push(ast::PathSegment::from_ident(ident));
                     let module_def_id = module.def_id().unwrap();
                     if module_def_id == def_id {
-                        let path = Path { span: name_binding.span, segments: path_segments };
+                        let path =
+                            Path { span: name_binding.span, segments: path_segments, tokens: None };
                         result = Some((
                             module,
                             ImportSuggestion {
@@ -1095,7 +1132,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
                 if let Res::Def(DefKind::Variant, _) = name_binding.res() {
                     let mut segms = enum_import_suggestion.path.segments.clone();
                     segms.push(ast::PathSegment::from_ident(ident));
-                    variants.push(Path { span: name_binding.span, segments: segms });
+                    variants.push(Path { span: name_binding.span, segments: segms, tokens: None });
                 }
             });
             variants
@@ -1387,7 +1424,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
                         }
                     }
                 }
-                introduce_suggestion.push((*for_span, for_sugg.to_string()));
+                introduce_suggestion.push((*for_span, for_sugg));
                 introduce_suggestion.push((span, formatter(&lt_name)));
                 err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect);
             }
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 50729086ec6..0f5b9b51816 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -1005,7 +1005,8 @@ pub struct Resolver<'a> {
 
     /// Table for mapping struct IDs into struct constructor IDs,
     /// it's not used during normal resolution, only for better error reporting.
-    struct_constructors: DefIdMap<(Res, ty::Visibility)>,
+    /// Also includes of list of each fields visibility
+    struct_constructors: DefIdMap<(Res, ty::Visibility, Vec<ty::Visibility>)>,
 
     /// Features enabled for this crate.
     active_features: FxHashSet<Symbol>,
@@ -1042,6 +1043,7 @@ pub struct ResolverArenas<'a> {
     name_resolutions: TypedArena<RefCell<NameResolution<'a>>>,
     macro_rules_bindings: TypedArena<MacroRulesBinding<'a>>,
     ast_paths: TypedArena<ast::Path>,
+    pattern_spans: TypedArena<Span>,
 }
 
 impl<'a> ResolverArenas<'a> {
@@ -1073,6 +1075,9 @@ impl<'a> ResolverArenas<'a> {
     fn alloc_ast_paths(&'a self, paths: &[ast::Path]) -> &'a [ast::Path] {
         self.ast_paths.alloc_from_iter(paths.iter().cloned())
     }
+    fn alloc_pattern_spans(&'a self, spans: impl Iterator<Item = Span>) -> &'a [Span] {
+        self.pattern_spans.alloc_from_iter(spans)
+    }
 }
 
 impl<'a> AsMut<Resolver<'a>> for Resolver<'a> {
@@ -2413,7 +2418,14 @@ impl<'a> Resolver<'a> {
                             (format!("maybe a missing crate `{}`?", ident), None)
                         }
                     } else if i == 0 {
-                        (format!("use of undeclared type or module `{}`", ident), None)
+                        if ident
+                            .name
+                            .with(|n| n.chars().next().map_or(false, |c| c.is_ascii_uppercase()))
+                        {
+                            (format!("use of undeclared type `{}`", ident), None)
+                        } else {
+                            (format!("use of undeclared crate or module `{}`", ident), None)
+                        }
                     } else {
                         let mut msg =
                             format!("could not find `{}` in `{}`", ident, path[i - 1].ident);
@@ -3182,6 +3194,7 @@ impl<'a> Resolver<'a> {
                     .chain(path_str.split("::").skip(1).map(Ident::from_str))
                     .map(|i| self.new_ast_path_segment(i))
                     .collect(),
+                tokens: None,
             }
         } else {
             ast::Path {
@@ -3191,6 +3204,7 @@ impl<'a> Resolver<'a> {
                     .map(Ident::from_str)
                     .map(|i| self.new_ast_path_segment(i))
                     .collect(),
+                tokens: None,
             }
         };
         let module = self.get_module(module_id);
diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs
index c95e7e193be..032d7cb3ed6 100644
--- a/compiler/rustc_save_analysis/src/lib.rs
+++ b/compiler/rustc_save_analysis/src/lib.rs
@@ -438,7 +438,7 @@ impl<'tcx> SaveContext<'tcx> {
                                     .next()
                                     .map(|item| item.def_id);
                             }
-                            qualname.push_str(">");
+                            qualname.push('>');
 
                             (qualname, trait_id, decl_id, docs, attrs)
                         }
diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs
index 6dd7f89d594..747e198cd93 100644
--- a/compiler/rustc_save_analysis/src/sig.rs
+++ b/compiler/rustc_save_analysis/src/sig.rs
@@ -497,7 +497,7 @@ impl<'hir> Sig for hir::Item<'hir> {
                     sig.text.push_str(&bounds_to_string(bounds));
                 }
                 // FIXME where clause
-                sig.text.push_str(";");
+                sig.text.push(';');
 
                 Ok(sig)
             }
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 19cd2385992..4aecb35294a 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -581,9 +581,9 @@ impl OutputFilenames {
 
         if !ext.is_empty() {
             if !extension.is_empty() {
-                extension.push_str(".");
+                extension.push('.');
                 extension.push_str(RUST_CGU_EXT);
-                extension.push_str(".");
+                extension.push('.');
             }
 
             extension.push_str(ext);
diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_session/src/lint/builtin.rs
index 2db4d2a7f51..a9deaaae0da 100644
--- a/compiler/rustc_session/src/lint/builtin.rs
+++ b/compiler/rustc_session/src/lint/builtin.rs
@@ -233,6 +233,12 @@ declare_lint! {
 }
 
 declare_lint! {
+    pub CONST_ITEM_MUTATION,
+    Warn,
+    "detects attempts to mutate a `const` item",
+}
+
+declare_lint! {
     pub SAFE_PACKED_BORROWS,
     Warn,
     "safe borrows of fields of packed structs were erroneously allowed",
@@ -539,6 +545,16 @@ declare_lint! {
     };
 }
 
+declare_lint! {
+    pub CONST_EVALUATABLE_UNCHECKED,
+    Warn,
+    "detects a generic constant is used in a type without a emitting a warning",
+    @future_incompatible = FutureIncompatibleInfo {
+        reference: "issue #76200 <https://github.com/rust-lang/rust/issues/76200>",
+        edition: None,
+    };
+}
+
 declare_lint_pass! {
     /// Does nothing as a lint pass, but registers some `Lint`s
     /// that are used by other parts of the compiler.
@@ -572,6 +588,7 @@ declare_lint_pass! {
         CONST_ERR,
         RENAMED_AND_REMOVED_LINTS,
         UNALIGNED_REFERENCES,
+        CONST_ITEM_MUTATION,
         SAFE_PACKED_BORROWS,
         PATTERNS_IN_FNS_WITHOUT_BODY,
         LATE_BOUND_LIFETIME_ARGUMENTS,
@@ -612,6 +629,7 @@ declare_lint_pass! {
         UNSAFE_OP_IN_UNSAFE_FN,
         INCOMPLETE_INCLUDE,
         CENUM_IMPL_DROP_CAST,
+        CONST_EVALUATABLE_UNCHECKED,
     ]
 }
 
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index ad36fa76986..c5050dbea73 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -850,6 +850,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "enable the experimental Chalk-based trait solving engine"),
     codegen_backend: Option<String> = (None, parse_opt_string, [TRACKED],
         "the backend to use"),
+    combine_cgu: bool = (false, parse_bool, [TRACKED],
+        "combine CGUs into a single one"),
     crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED],
         "inject the given attribute in the crate"),
     debug_macros: bool = (false, parse_bool, [TRACKED],
@@ -907,6 +909,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "force all crates to be `rustc_private` unstable (default: no)"),
     fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED],
         "set the optimization fuel quota for a crate"),
+    graphviz_dark_mode: bool = (false, parse_bool, [UNTRACKED],
+        "use dark-themed colors in graphviz output (default: no)"),
     hir_stats: bool = (false, parse_bool, [UNTRACKED],
         "print some statistics about AST and HIR (default: no)"),
     human_readable_cgu_names: bool = (false, parse_bool, [TRACKED],
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index ff22b4ce4ad..974f4c31bb6 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -237,6 +237,14 @@ enum DiagnosticBuilderMethod {
                             // Add more variants as needed to support one-time diagnostics.
 }
 
+/// Trait implemented by error types. This should not be implemented manually. Instead, use
+/// `#[derive(SessionDiagnostic)]` -- see [rustc_macros::SessionDiagnostic].
+pub trait SessionDiagnostic<'a> {
+    /// Write out as a diagnostic out of `sess`.
+    #[must_use]
+    fn into_diagnostic(self, sess: &'a Session) -> DiagnosticBuilder<'a>;
+}
+
 /// Diagnostic message ID, used by `Session.one_time_diagnostics` to avoid
 /// emitting the same message more than once.
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -392,6 +400,9 @@ impl Session {
     pub fn err(&self, msg: &str) {
         self.diagnostic().err(msg)
     }
+    pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) {
+        err.into_diagnostic(self).emit()
+    }
     pub fn err_count(&self) -> usize {
         self.diagnostic().err_count()
     }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 5092b945f72..407663e5757 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -348,6 +348,7 @@ symbols! {
         const_compare_raw_pointers,
         const_constructor,
         const_eval_limit,
+        const_evaluatable_checked,
         const_extern_fn,
         const_fn,
         const_fn_transmute,
diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs
index ff559c2bfd6..527a322d56a 100644
--- a/compiler/rustc_target/src/spec/avr_gnu_base.rs
+++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs
@@ -45,6 +45,8 @@ pub fn target(target_cpu: String) -> TargetResult {
             late_link_args: vec![(LinkerFlavor::Gcc, vec!["-lgcc".to_owned()])]
                 .into_iter()
                 .collect(),
+            max_atomic_width: Some(0),
+            atomic_cas: false,
             ..TargetOptions::default()
         },
     })
diff --git a/compiler/rustc_target/src/spec/wasm32_base.rs b/compiler/rustc_target/src/spec/wasm32_base.rs
index 62fc8f06183..a7957d84cbe 100644
--- a/compiler/rustc_target/src/spec/wasm32_base.rs
+++ b/compiler/rustc_target/src/spec/wasm32_base.rs
@@ -83,6 +83,7 @@ pub fn options() -> TargetOptions {
         dll_prefix: String::new(),
         dll_suffix: ".wasm".to_string(),
         linker_is_gnu: false,
+        eh_frame_header: false,
 
         max_atomic_width: Some(64),
 
diff --git a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs
index fd55a0fc6a1..fcb2af0005f 100644
--- a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs
+++ b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs
@@ -22,7 +22,7 @@ pub fn opts() -> TargetOptions {
         "-lmingw32".to_string(),
     ];
     late_link_args.insert(LinkerFlavor::Gcc, mingw_libs.clone());
-    late_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), mingw_libs.clone());
+    late_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), mingw_libs);
 
     TargetOptions {
         executables: false,
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
new file mode 100644
index 00000000000..fdb87c085b5
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -0,0 +1,78 @@
+use rustc_hir::def::DefKind;
+use rustc_infer::infer::InferCtxt;
+use rustc_middle::mir::interpret::ErrorHandled;
+use rustc_middle::ty::subst::SubstsRef;
+use rustc_middle::ty::{self, TypeFoldable};
+use rustc_session::lint;
+use rustc_span::def_id::DefId;
+use rustc_span::Span;
+
+pub fn is_const_evaluatable<'cx, 'tcx>(
+    infcx: &InferCtxt<'cx, 'tcx>,
+    def: ty::WithOptConstParam<DefId>,
+    substs: SubstsRef<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    span: Span,
+) -> Result<(), ErrorHandled> {
+    debug!("is_const_evaluatable({:?}, {:?})", def, substs);
+    if infcx.tcx.features().const_evaluatable_checked {
+        // FIXME(const_evaluatable_checked): Actually look into generic constants to
+        // implement const equality.
+        for pred in param_env.caller_bounds() {
+            match pred.skip_binders() {
+                ty::PredicateAtom::ConstEvaluatable(b_def, b_substs) => {
+                    debug!("is_const_evaluatable: caller_bound={:?}, {:?}", b_def, b_substs);
+                    if b_def == def && b_substs == substs {
+                        debug!("is_const_evaluatable: caller_bound ~~> ok");
+                        return Ok(());
+                    }
+                }
+                _ => {} // don't care
+            }
+        }
+    }
+
+    let future_compat_lint = || {
+        if let Some(local_def_id) = def.did.as_local() {
+            infcx.tcx.struct_span_lint_hir(
+                lint::builtin::CONST_EVALUATABLE_UNCHECKED,
+                infcx.tcx.hir().local_def_id_to_hir_id(local_def_id),
+                span,
+                |err| {
+                    err.build("cannot use constants which depend on generic parameters in types")
+                        .emit();
+                },
+            );
+        }
+    };
+
+    // FIXME: We should only try to evaluate a given constant here if it is fully concrete
+    // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`.
+    //
+    // We previously did not check this, so we only emit a future compat warning if
+    // const evaluation succeeds and the given constant is still polymorphic for now
+    // and hopefully soon change this to an error.
+    //
+    // See #74595 for more details about this.
+    let concrete = infcx.const_eval_resolve(param_env, def, substs, None, Some(span));
+
+    if concrete.is_ok() && substs.has_param_types_or_consts() {
+        match infcx.tcx.def_kind(def.did) {
+            DefKind::AnonConst => {
+                let mir_body = if let Some(def) = def.as_const_arg() {
+                    infcx.tcx.optimized_mir_of_const_arg(def)
+                } else {
+                    infcx.tcx.optimized_mir(def.did)
+                };
+
+                if mir_body.is_polymorphic {
+                    future_compat_lint();
+                }
+            }
+            _ => future_compat_lint(),
+        }
+    }
+
+    debug!(?concrete, "is_const_evaluatable");
+    concrete.map(drop)
+}
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index a5c6dc042ab..4818022bf62 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -10,6 +10,7 @@ use rustc_middle::ty::ToPredicate;
 use rustc_middle::ty::{self, Binder, Const, Ty, TypeFoldable};
 use std::marker::PhantomData;
 
+use super::const_evaluatable;
 use super::project;
 use super::select::SelectionContext;
 use super::wf;
@@ -458,15 +459,15 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
                 }
 
                 ty::PredicateAtom::ConstEvaluatable(def_id, substs) => {
-                    match self.selcx.infcx().const_eval_resolve(
-                        obligation.param_env,
+                    match const_evaluatable::is_const_evaluatable(
+                        self.selcx.infcx(),
                         def_id,
                         substs,
-                        None,
-                        Some(obligation.cause.span),
+                        obligation.param_env,
+                        obligation.cause.span,
                     ) {
-                        Ok(_) => ProcessResult::Changed(vec![]),
-                        Err(err) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(err))),
+                        Ok(()) => ProcessResult::Changed(vec![]),
+                        Err(e) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(e))),
                     }
                 }
 
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index fe406e88c52..49dac873cde 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -7,6 +7,7 @@ pub mod auto_trait;
 mod chalk_fulfill;
 pub mod codegen;
 mod coherence;
+mod const_evaluatable;
 mod engine;
 pub mod error_reporting;
 mod fulfill;
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 4258d8e3010..7e8e2baa8a1 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -6,6 +6,7 @@ use self::EvaluationResult::*;
 use self::SelectionCandidate::*;
 
 use super::coherence::{self, Conflict};
+use super::const_evaluatable;
 use super::project;
 use super::project::normalize_with_depth_to;
 use super::util;
@@ -542,14 +543,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             }
 
             ty::PredicateAtom::ConstEvaluatable(def_id, substs) => {
-                match self.tcx().const_eval_resolve(
-                    obligation.param_env,
+                match const_evaluatable::is_const_evaluatable(
+                    self.infcx,
                     def_id,
                     substs,
-                    None,
-                    None,
+                    obligation.param_env,
+                    obligation.cause.span,
                 ) {
-                    Ok(_) => Ok(EvaluatedToOk),
+                    Ok(()) => Ok(EvaluatedToOk),
                     Err(ErrorHandled::TooGeneric) => Ok(EvaluatedToAmbig),
                     Err(_) => Ok(EvaluatedToErr),
                 }
diff --git a/compiler/rustc_typeck/Cargo.toml b/compiler/rustc_typeck/Cargo.toml
index 0a6bfaef431..e3ba0bea7e8 100644
--- a/compiler/rustc_typeck/Cargo.toml
+++ b/compiler/rustc_typeck/Cargo.toml
@@ -11,6 +11,7 @@ doctest = false
 [dependencies]
 rustc_arena = { path = "../rustc_arena" }
 tracing = "0.1"
+rustc_macros = { path = "../rustc_macros" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_attr = { path = "../rustc_attr" }
 rustc_data_structures = { path = "../rustc_data_structures" }
diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs
index 84dab6de958..b54de1d0916 100644
--- a/compiler/rustc_typeck/src/astconv/generics.rs
+++ b/compiler/rustc_typeck/src/astconv/generics.rs
@@ -1,8 +1,9 @@
 use crate::astconv::{
     AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition,
 };
+use crate::errors::AssocTypeBindingNotAllowed;
 use rustc_ast::ast::ParamKindOrd;
-use rustc_errors::{pluralize, struct_span_err, DiagnosticId, ErrorReported};
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{GenericArg, GenericArgs};
@@ -367,7 +368,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         }
 
         if position != GenericArgPosition::Type && !args.bindings.is_empty() {
-            Self::prohibit_assoc_ty_binding(tcx, args.bindings[0].span);
+            AstConv::prohibit_assoc_ty_binding(tcx, args.bindings[0].span);
         }
 
         let explicit_late_bound =
@@ -392,7 +393,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             }
 
             if silent {
-                return Err(true);
+                return Err((0i32, None));
             }
 
             // Unfortunately lifetime and type parameter mismatches are typically styled
@@ -441,16 +442,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             for span in spans {
                 err.span_label(span, label.as_str());
             }
-            err.emit();
 
-            Err(true)
+            assert_ne!(bound, provided);
+            Err((bound as i32 - provided as i32, Some(err)))
         };
 
-        let mut arg_count_correct = Ok(());
         let mut unexpected_spans = vec![];
 
+        let mut lifetime_count_correct = Ok(());
         if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes {
-            arg_count_correct = check_kind_count(
+            lifetime_count_correct = check_kind_count(
                 "lifetime",
                 param_counts.lifetimes,
                 param_counts.lifetimes,
@@ -458,12 +459,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 0,
                 &mut unexpected_spans,
                 explicit_late_bound == ExplicitLateBound::Yes,
-            )
-            .and(arg_count_correct);
+            );
         }
+
         // FIXME(const_generics:defaults)
+        let mut const_count_correct = Ok(());
         if !infer_args || arg_counts.consts > param_counts.consts {
-            arg_count_correct = check_kind_count(
+            const_count_correct = check_kind_count(
                 "const",
                 param_counts.consts,
                 param_counts.consts,
@@ -471,13 +473,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 arg_counts.lifetimes + arg_counts.types,
                 &mut unexpected_spans,
                 false,
-            )
-            .and(arg_count_correct);
+            );
         }
+
         // Note that type errors are currently be emitted *after* const errors.
+        let mut type_count_correct = Ok(());
         if !infer_args || arg_counts.types > param_counts.types - defaults.types - has_self as usize
         {
-            arg_count_correct = check_kind_count(
+            type_count_correct = check_kind_count(
                 "type",
                 param_counts.types - defaults.types - has_self as usize,
                 param_counts.types - has_self as usize,
@@ -485,14 +488,54 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 arg_counts.lifetimes,
                 &mut unexpected_spans,
                 false,
-            )
-            .and(arg_count_correct);
+            );
+        }
+
+        // Emit a help message if it's possible that a type could be surrounded in braces
+        if let Err((c_mismatch, Some(ref mut _const_err))) = const_count_correct {
+            if let Err((_, Some(ref mut type_err))) = type_count_correct {
+                let possible_matches = args.args[arg_counts.lifetimes..]
+                    .iter()
+                    .filter(|arg| {
+                        matches!(
+                            arg,
+                            GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. })
+                        )
+                    })
+                    .take(c_mismatch.max(0) as usize);
+                for arg in possible_matches {
+                    let suggestions = vec![
+                        (arg.span().shrink_to_lo(), String::from("{ ")),
+                        (arg.span().shrink_to_hi(), String::from(" }")),
+                    ];
+                    type_err.multipart_suggestion(
+                        "If this generic argument was intended as a const parameter, \
+                        try surrounding it with braces:",
+                        suggestions,
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
         }
 
+        let emit_correct =
+            |correct: Result<(), (_, Option<rustc_errors::DiagnosticBuilder<'_>>)>| match correct {
+                Ok(()) => Ok(()),
+                Err((_, None)) => Err(()),
+                Err((_, Some(mut err))) => {
+                    err.emit();
+                    Err(())
+                }
+            };
+
+        let arg_count_correct = emit_correct(lifetime_count_correct)
+            .and(emit_correct(const_count_correct))
+            .and(emit_correct(type_count_correct));
+
         GenericArgCountResult {
             explicit_late_bound,
-            correct: arg_count_correct.map_err(|reported_err| GenericArgCountMismatch {
-                reported: if reported_err { Some(ErrorReported) } else { None },
+            correct: arg_count_correct.map_err(|()| GenericArgCountMismatch {
+                reported: Some(ErrorReported),
                 invalid_args: unexpected_spans,
             }),
         }
@@ -544,13 +587,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
     /// Emits an error regarding forbidden type binding associations
     pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) {
-        let mut err = struct_span_err!(
-            tcx.sess,
-            span,
-            E0229,
-            "associated type bindings are not allowed here"
-        );
-        err.span_label(span, "associated type not allowed here").emit();
+        tcx.sess.emit_err(AssocTypeBindingNotAllowed { span });
     }
 
     /// Prohibits explicit lifetime arguments if late-bound lifetime parameters
diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs
index bb79a1258fb..9e339b1082c 100644
--- a/compiler/rustc_typeck/src/astconv/mod.rs
+++ b/compiler/rustc_typeck/src/astconv/mod.rs
@@ -7,6 +7,10 @@ mod generics;
 
 use crate::bounds::Bounds;
 use crate::collect::PlaceholderHirTyCollector;
+use crate::errors::{
+    AmbiguousLifetimeBound, MultipleRelaxedDefaultBounds, TraitObjectDeclaredWithNoTraits,
+    TypeofReservedKeywordUsed, ValueOfAssociatedStructAlreadySpecified,
+};
 use crate::middle::resolve_lifetime as rl;
 use crate::require_c_abi_if_c_variadic;
 use rustc_ast::util::lev_distance::find_best_match_for_name;
@@ -684,14 +688,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 if unbound.is_none() {
                     unbound = Some(&ptr.trait_ref);
                 } else {
-                    struct_span_err!(
-                        tcx.sess,
-                        span,
-                        E0203,
-                        "type parameter has more than one relaxed default \
-                        bound, only one is supported"
-                    )
-                    .emit();
+                    tcx.sess.emit_err(MultipleRelaxedDefaultBounds { span });
                 }
             }
         }
@@ -927,18 +924,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             dup_bindings
                 .entry(assoc_ty.def_id)
                 .and_modify(|prev_span| {
-                    struct_span_err!(
-                        self.tcx().sess,
-                        binding.span,
-                        E0719,
-                        "the value of the associated type `{}` (from trait `{}`) \
-                         is already specified",
-                        binding.item_name,
-                        tcx.def_path_str(assoc_ty.container.id())
-                    )
-                    .span_label(binding.span, "re-bound here")
-                    .span_label(*prev_span, format!("`{}` bound here first", binding.item_name))
-                    .emit();
+                    self.tcx().sess.emit_err(ValueOfAssociatedStructAlreadySpecified {
+                        span: binding.span,
+                        prev_span: *prev_span,
+                        item_name: binding.item_name,
+                        def_path: tcx.def_path_str(assoc_ty.container.id()),
+                    });
                 })
                 .or_insert(binding.span);
         }
@@ -1051,13 +1042,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         }
 
         if regular_traits.is_empty() && auto_traits.is_empty() {
-            struct_span_err!(
-                tcx.sess,
-                span,
-                E0224,
-                "at least one trait is required for an object type"
-            )
-            .emit();
+            tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span });
             return tcx.ty_error();
         }
 
@@ -2059,15 +2044,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 self.normalize_ty(ast_ty.span, array_ty)
             }
             hir::TyKind::Typeof(ref _e) => {
-                struct_span_err!(
-                    tcx.sess,
-                    ast_ty.span,
-                    E0516,
-                    "`typeof` is a reserved keyword but unimplemented"
-                )
-                .span_label(ast_ty.span, "reserved keyword")
-                .emit();
-
+                tcx.sess.emit_err(TypeofReservedKeywordUsed { span: ast_ty.span });
                 tcx.ty_error()
             }
             hir::TyKind::Infer => {
@@ -2283,13 +2260,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         // error.
         let r = derived_region_bounds[0];
         if derived_region_bounds[1..].iter().any(|r1| r != *r1) {
-            struct_span_err!(
-                tcx.sess,
-                span,
-                E0227,
-                "ambiguous lifetime bound, explicit lifetime bound required"
-            )
-            .emit();
+            tcx.sess.emit_err(AmbiguousLifetimeBound { span });
         }
         Some(r)
     }
diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs
index 7adcd7b472e..bbf5153d35d 100644
--- a/compiler/rustc_typeck/src/check/compare_method.rs
+++ b/compiler/rustc_typeck/src/check/compare_method.rs
@@ -1,3 +1,4 @@
+use crate::errors::LifetimesOrBoundsMismatchOnTrait;
 use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
@@ -366,24 +367,18 @@ fn check_region_bounds_on_impl_item<'tcx>(
         let item_kind = assoc_item_kind_str(impl_m);
         let def_span = tcx.sess.source_map().guess_head_span(span);
         let span = tcx.hir().get_generics(impl_m.def_id).map(|g| g.span).unwrap_or(def_span);
-        let mut err = struct_span_err!(
-            tcx.sess,
+        let generics_span = if let Some(sp) = tcx.hir().span_if_local(trait_m.def_id) {
+            let def_sp = tcx.sess.source_map().guess_head_span(sp);
+            Some(tcx.hir().get_generics(trait_m.def_id).map(|g| g.span).unwrap_or(def_sp))
+        } else {
+            None
+        };
+        tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait {
             span,
-            E0195,
-            "lifetime parameters or bounds on {} `{}` do not match the trait declaration",
             item_kind,
-            impl_m.ident,
-        );
-        err.span_label(span, &format!("lifetimes do not match {} in trait", item_kind));
-        if let Some(sp) = tcx.hir().span_if_local(trait_m.def_id) {
-            let def_sp = tcx.sess.source_map().guess_head_span(sp);
-            let sp = tcx.hir().get_generics(trait_m.def_id).map(|g| g.span).unwrap_or(def_sp);
-            err.span_label(
-                sp,
-                &format!("lifetimes in impl do not match this {} in trait", item_kind),
-            );
-        }
-        err.emit();
+            ident: impl_m.ident,
+            generics_span,
+        });
         return Err(ErrorReported);
     }
 
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 05fd957e1f4..dba46f35dca 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -14,8 +14,13 @@ use crate::check::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExp
 use crate::check::FnCtxt;
 use crate::check::Needs;
 use crate::check::TupleArgumentsFlag::DontTupleArguments;
+use crate::errors::{
+    FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct,
+    YieldExprOutsideOfGenerator,
+};
 use crate::type_error_struct;
 
+use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
 use rustc_ast as ast;
 use rustc_ast::util::lev_distance::find_best_match_for_name;
 use rustc_data_structures::fx::FxHashMap;
@@ -439,14 +444,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             })
         });
         if !is_named {
-            struct_span_err!(
-                self.tcx.sess,
-                oprnd.span,
-                E0745,
-                "cannot take address of a temporary"
-            )
-            .span_label(oprnd.span, "temporary value")
-            .emit();
+            self.tcx.sess.emit_err(AddressOfTemporaryTaken { span: oprnd.span })
         }
     }
 
@@ -665,13 +663,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr: &'tcx hir::Expr<'tcx>,
     ) -> Ty<'tcx> {
         if self.ret_coercion.is_none() {
-            struct_span_err!(
-                self.tcx.sess,
-                expr.span,
-                E0572,
-                "return statement outside of function body",
-            )
-            .emit();
+            self.tcx.sess.emit_err(ReturnStmtOutsideOfFnBody { span: expr.span });
         } else if let Some(ref e) = expr_opt {
             if self.ret_coercion_span.borrow().is_none() {
                 *self.ret_coercion_span.borrow_mut() = Some(e.span);
@@ -740,6 +732,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr_span: &Span,
     ) {
         if !lhs.is_syntactic_place_expr() {
+            // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set.
             let mut err = self.tcx.sess.struct_span_err_with_code(
                 *expr_span,
                 "invalid left-hand side of assignment",
@@ -1120,14 +1113,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // Prohibit struct expressions when non-exhaustive flag is set.
         let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type");
         if !adt.did.is_local() && variant.is_field_list_non_exhaustive() {
-            struct_span_err!(
-                self.tcx.sess,
-                expr.span,
-                E0639,
-                "cannot create non-exhaustive {} using struct expression",
-                adt.variant_descr()
-            )
-            .emit();
+            self.tcx
+                .sess
+                .emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() });
         }
 
         let error_happened = self.check_expr_struct_fields(
@@ -1165,13 +1153,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             .insert(expr.hir_id, fru_field_types);
                     }
                     _ => {
-                        struct_span_err!(
-                            self.tcx.sess,
-                            base_expr.span,
-                            E0436,
-                            "functional record update syntax requires a struct"
-                        )
-                        .emit();
+                        self.tcx
+                            .sess
+                            .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
                     }
                 }
             }
@@ -1234,18 +1218,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             } else {
                 error_happened = true;
                 if let Some(prev_span) = seen_fields.get(&ident) {
-                    let mut err = struct_span_err!(
-                        self.tcx.sess,
-                        field.ident.span,
-                        E0062,
-                        "field `{}` specified more than once",
-                        ident
-                    );
-
-                    err.span_label(field.ident.span, "used more than once");
-                    err.span_label(*prev_span, format!("first use of `{}`", ident));
-
-                    err.emit();
+                    tcx.sess.emit_err(FieldMultiplySpecifiedInInitializer {
+                        span: field.ident.span,
+                        prev_span: *prev_span,
+                        ident,
+                    });
                 } else {
                     self.report_unknown_field(adt_ty, variant, field, ast_fields, kind_name, span);
                 }
@@ -1264,42 +1241,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 tcx.sess.span_err(span, "union expressions should have exactly one field");
             }
         } else if check_completeness && !error_happened && !remaining_fields.is_empty() {
-            let len = remaining_fields.len();
-
-            let mut displayable_field_names =
-                remaining_fields.keys().map(|ident| ident.as_str()).collect::<Vec<_>>();
-
-            displayable_field_names.sort();
+            let no_accessible_remaining_fields = remaining_fields
+                .iter()
+                .filter(|(_, (_, field))| {
+                    field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
+                })
+                .next()
+                .is_none();
 
-            let truncated_fields_error = if len <= 3 {
-                String::new()
+            if no_accessible_remaining_fields {
+                self.report_no_accessible_fields(adt_ty, span);
             } else {
-                format!(" and {} other field{}", (len - 3), if len - 3 == 1 { "" } else { "s" })
-            };
-
-            let remaining_fields_names = displayable_field_names
-                .iter()
-                .take(3)
-                .map(|n| format!("`{}`", n))
-                .collect::<Vec<_>>()
-                .join(", ");
-
-            struct_span_err!(
-                tcx.sess,
-                span,
-                E0063,
-                "missing field{} {}{} in initializer of `{}`",
-                pluralize!(remaining_fields.len()),
-                remaining_fields_names,
-                truncated_fields_error,
-                adt_ty
-            )
-            .span_label(
-                span,
-                format!("missing {}{}", remaining_fields_names, truncated_fields_error),
-            )
-            .emit();
+                self.report_missing_field(adt_ty, span, remaining_fields);
+            }
         }
+
         error_happened
     }
 
@@ -1316,6 +1272,79 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    /// Report an error for a struct field expression when there are fields which aren't provided.
+    ///
+    /// ```ignore (diagnostic)
+    /// error: missing field `you_can_use_this_field` in initializer of `foo::Foo`
+    ///  --> src/main.rs:8:5
+    ///   |
+    /// 8 |     foo::Foo {};
+    ///   |     ^^^^^^^^ missing `you_can_use_this_field`
+    ///
+    /// error: aborting due to previous error
+    /// ```
+    fn report_missing_field(
+        &self,
+        adt_ty: Ty<'tcx>,
+        span: Span,
+        remaining_fields: FxHashMap<Ident, (usize, &ty::FieldDef)>,
+    ) {
+        let tcx = self.tcx;
+        let len = remaining_fields.len();
+
+        let mut displayable_field_names =
+            remaining_fields.keys().map(|ident| ident.as_str()).collect::<Vec<_>>();
+
+        displayable_field_names.sort();
+
+        let truncated_fields_error = if len <= 3 {
+            String::new()
+        } else {
+            format!(" and {} other field{}", (len - 3), if len - 3 == 1 { "" } else { "s" })
+        };
+
+        let remaining_fields_names = displayable_field_names
+            .iter()
+            .take(3)
+            .map(|n| format!("`{}`", n))
+            .collect::<Vec<_>>()
+            .join(", ");
+
+        struct_span_err!(
+            tcx.sess,
+            span,
+            E0063,
+            "missing field{} {}{} in initializer of `{}`",
+            pluralize!(remaining_fields.len()),
+            remaining_fields_names,
+            truncated_fields_error,
+            adt_ty
+        )
+        .span_label(span, format!("missing {}{}", remaining_fields_names, truncated_fields_error))
+        .emit();
+    }
+
+    /// Report an error for a struct field expression when there are no visible fields.
+    ///
+    /// ```ignore (diagnostic)
+    /// error: cannot construct `Foo` with struct literal syntax due to inaccessible fields
+    ///  --> src/main.rs:8:5
+    ///   |
+    /// 8 |     foo::Foo {};
+    ///   |     ^^^^^^^^
+    ///
+    /// error: aborting due to previous error
+    /// ```
+    fn report_no_accessible_fields(&self, adt_ty: Ty<'tcx>, span: Span) {
+        self.tcx.sess.span_err(
+            span,
+            &format!(
+                "cannot construct `{}` with struct literal syntax due to inaccessible fields",
+                adt_ty,
+            ),
+        );
+    }
+
     fn report_unknown_field(
         &self,
         ty: Ty<'tcx>,
@@ -1876,13 +1905,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.tcx.mk_unit()
             }
             _ => {
-                struct_span_err!(
-                    self.tcx.sess,
-                    expr.span,
-                    E0627,
-                    "yield expression outside of generator literal"
-                )
-                .emit();
+                self.tcx.sess.emit_err(YieldExprOutsideOfGenerator { span: expr.span });
                 self.tcx.mk_unit()
             }
         }
diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs
index 47cea8649ef..b8230f52444 100644
--- a/compiler/rustc_typeck/src/check/intrinsic.rs
+++ b/compiler/rustc_typeck/src/check/intrinsic.rs
@@ -1,6 +1,10 @@
 //! Type-checking for the rust-intrinsic and platform-intrinsic
 //! intrinsics that the compiler exposes.
 
+use crate::errors::{
+    SimdShuffleMissingLength, UnrecognizedAtomicOperation, UnrecognizedIntrinsicFunction,
+    WrongNumberOfTypeArgumentsToInstrinsic,
+};
 use crate::require_same_types;
 
 use rustc_errors::struct_span_err;
@@ -41,17 +45,11 @@ fn equate_intrinsic_type<'tcx>(
             _ => bug!(),
         };
 
-        struct_span_err!(
-            tcx.sess,
+        tcx.sess.emit_err(WrongNumberOfTypeArgumentsToInstrinsic {
             span,
-            E0094,
-            "intrinsic has wrong number of type \
-                         parameters: found {}, expected {}",
-            i_n_tps,
-            n_tps
-        )
-        .span_label(span, format!("expected {} type parameter", n_tps))
-        .emit();
+            found: i_n_tps,
+            expected: n_tps,
+        });
         return;
     }
 
@@ -146,15 +144,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
             | "umin" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], param(0)),
             "fence" | "singlethreadfence" => (0, Vec::new(), tcx.mk_unit()),
             op => {
-                struct_span_err!(
-                    tcx.sess,
-                    it.span,
-                    E0092,
-                    "unrecognized atomic operation function: `{}`",
-                    op
-                )
-                .span_label(it.span, "unrecognized atomic operation")
-                .emit();
+                tcx.sess.emit_err(UnrecognizedAtomicOperation { span: it.span, op });
                 return;
             }
         };
@@ -380,15 +370,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
             sym::nontemporal_store => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()),
 
             other => {
-                struct_span_err!(
-                    tcx.sess,
-                    it.span,
-                    E0093,
-                    "unrecognized intrinsic function: `{}`",
-                    other,
-                )
-                .span_label(it.span, "unrecognized intrinsic")
-                .emit();
+                tcx.sess.emit_err(UnrecognizedIntrinsicFunction { span: it.span, name: other });
                 return;
             }
         };
@@ -468,14 +450,7 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
                     (2, params, param(1))
                 }
                 Err(_) => {
-                    struct_span_err!(
-                        tcx.sess,
-                        it.span,
-                        E0439,
-                        "invalid `simd_shuffle`, needs length: `{}`",
-                        name
-                    )
-                    .emit();
+                    tcx.sess.emit_err(SimdShuffleMissingLength { span: it.span, name });
                     return;
                 }
             }
diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs
index 7645ea4ff8c..14d80fded71 100644
--- a/compiler/rustc_typeck/src/check/method/probe.rs
+++ b/compiler/rustc_typeck/src/check/method/probe.rs
@@ -4,6 +4,7 @@ use super::NoMatchData;
 use super::{CandidateSource, ImplSource, TraitSource};
 
 use crate::check::FnCtxt;
+use crate::errors::MethodCallOnUnknownType;
 use crate::hir::def::DefKind;
 use crate::hir::def_id::DefId;
 
@@ -11,7 +12,6 @@ use rustc_ast as ast;
 use rustc_ast::util::lev_distance::{find_best_match_for_name, lev_distance};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def::Namespace;
 use rustc_infer::infer::canonical::OriginalQueryValues;
@@ -376,14 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // so we do a future-compat lint here for the 2015 edition
                 // (see https://github.com/rust-lang/rust/issues/46906)
                 if self.tcx.sess.rust_2018() {
-                    struct_span_err!(
-                        self.tcx.sess,
-                        span,
-                        E0699,
-                        "the type of this value must be known to call a method on a raw pointer on \
-                         it"
-                    )
-                    .emit();
+                    self.tcx.sess.emit_err(MethodCallOnUnknownType { span });
                 } else {
                     self.tcx.struct_span_lint_hir(
                         lint::builtin::TYVAR_BEHIND_RAW_POINTER,
diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs
index f06929aa98f..1896155e327 100644
--- a/compiler/rustc_typeck/src/check/pat.rs
+++ b/compiler/rustc_typeck/src/check/pat.rs
@@ -1078,8 +1078,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let mut unmentioned_fields = variant
             .fields
             .iter()
-            .map(|field| field.ident.normalize_to_macros_2_0())
-            .filter(|ident| !used_fields.contains_key(&ident))
+            .map(|field| (field, field.ident.normalize_to_macros_2_0()))
+            .filter(|(_, ident)| !used_fields.contains_key(&ident))
             .collect::<Vec<_>>();
 
         let inexistent_fields_err = if !(inexistent_fields.is_empty() || variant.is_recovered()) {
@@ -1110,7 +1110,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit();
             }
         } else if !etc && !unmentioned_fields.is_empty() {
-            unmentioned_err = Some(self.error_unmentioned_fields(pat, &unmentioned_fields));
+            let no_accessible_unmentioned_fields = unmentioned_fields
+                .iter()
+                .filter(|(field, _)| {
+                    field.vis.is_accessible_from(tcx.parent_module(pat.hir_id).to_def_id(), tcx)
+                })
+                .next()
+                .is_none();
+
+            if no_accessible_unmentioned_fields {
+                unmentioned_err = Some(self.error_no_accessible_fields(pat, &fields));
+            } else {
+                unmentioned_err = Some(self.error_unmentioned_fields(pat, &unmentioned_fields));
+            }
         }
         match (inexistent_fields_err, unmentioned_err) {
             (Some(mut i), Some(mut u)) => {
@@ -1173,7 +1185,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         kind_name: &str,
         inexistent_fields: &[Ident],
-        unmentioned_fields: &mut Vec<Ident>,
+        unmentioned_fields: &mut Vec<(&ty::FieldDef, Ident)>,
         variant: &ty::VariantDef,
     ) -> DiagnosticBuilder<'tcx> {
         let tcx = self.tcx;
@@ -1215,7 +1227,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 ),
             );
             if plural == "" {
-                let input = unmentioned_fields.iter().map(|field| &field.name);
+                let input = unmentioned_fields.iter().map(|(_, field)| &field.name);
                 let suggested_name = find_best_match_for_name(input, ident.name, None);
                 if let Some(suggested_name) = suggested_name {
                     err.span_suggestion(
@@ -1232,7 +1244,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // `smart_resolve_context_dependent_help`.
                     if suggested_name.to_ident_string().parse::<usize>().is_err() {
                         // We don't want to throw `E0027` in case we have thrown `E0026` for them.
-                        unmentioned_fields.retain(|&x| x.name != suggested_name);
+                        unmentioned_fields.retain(|&(_, x)| x.name != suggested_name);
                     }
                 }
             }
@@ -1300,17 +1312,77 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         None
     }
 
+    /// Returns a diagnostic reporting a struct pattern which is missing an `..` due to
+    /// inaccessible fields.
+    ///
+    /// ```ignore (diagnostic)
+    /// error: pattern requires `..` due to inaccessible fields
+    ///   --> src/main.rs:10:9
+    ///    |
+    /// LL |     let foo::Foo {} = foo::Foo::default();
+    ///    |         ^^^^^^^^^^^
+    ///    |
+    /// help: add a `..`
+    ///    |
+    /// LL |     let foo::Foo { .. } = foo::Foo::default();
+    ///    |                  ^^^^^^
+    /// ```
+    fn error_no_accessible_fields(
+        &self,
+        pat: &Pat<'_>,
+        fields: &'tcx [hir::FieldPat<'tcx>],
+    ) -> DiagnosticBuilder<'tcx> {
+        let mut err = self
+            .tcx
+            .sess
+            .struct_span_err(pat.span, "pattern requires `..` due to inaccessible fields");
+
+        if let Some(field) = fields.last() {
+            err.span_suggestion_verbose(
+                field.span.shrink_to_hi(),
+                "ignore the inaccessible and unused fields",
+                ", ..".to_string(),
+                Applicability::MachineApplicable,
+            );
+        } else {
+            let qpath_span = if let PatKind::Struct(qpath, ..) = &pat.kind {
+                qpath.span()
+            } else {
+                bug!("`error_no_accessible_fields` called on non-struct pattern");
+            };
+
+            // Shrink the span to exclude the `foo:Foo` in `foo::Foo { }`.
+            let span = pat.span.with_lo(qpath_span.shrink_to_hi().hi());
+            err.span_suggestion_verbose(
+                span,
+                "ignore the inaccessible and unused fields",
+                " { .. }".to_string(),
+                Applicability::MachineApplicable,
+            );
+        }
+        err
+    }
+
+    /// Returns a diagnostic reporting a struct pattern which does not mention some fields.
+    ///
+    /// ```ignore (diagnostic)
+    /// error[E0027]: pattern does not mention field `you_cant_use_this_field`
+    ///   --> src/main.rs:15:9
+    ///    |
+    /// LL |     let foo::Foo {} = foo::Foo::new();
+    ///    |         ^^^^^^^^^^^ missing field `you_cant_use_this_field`
+    /// ```
     fn error_unmentioned_fields(
         &self,
         pat: &Pat<'_>,
-        unmentioned_fields: &[Ident],
+        unmentioned_fields: &[(&ty::FieldDef, Ident)],
     ) -> DiagnosticBuilder<'tcx> {
         let field_names = if unmentioned_fields.len() == 1 {
-            format!("field `{}`", unmentioned_fields[0])
+            format!("field `{}`", unmentioned_fields[0].1)
         } else {
             let fields = unmentioned_fields
                 .iter()
-                .map(|name| format!("`{}`", name))
+                .map(|(_, name)| format!("`{}`", name))
                 .collect::<Vec<String>>()
                 .join(", ");
             format!("fields {}", fields)
diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_typeck/src/check/place_op.rs
index cdbed28f754..aed2af20e52 100644
--- a/compiler/rustc_typeck/src/check/place_op.rs
+++ b/compiler/rustc_typeck/src/check/place_op.rs
@@ -193,7 +193,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
     /// into `DerefMut` and `IndexMut` respectively.
     ///
-    /// This is a second pass of typechecking derefs/indices. We need this we do not
+    /// This is a second pass of typechecking derefs/indices. We need this because we do not
     /// always know whether a place needs to be mutable or not in the first pass.
     /// This happens whether there is an implicit mutable reborrow, e.g. when the type
     /// is used as the receiver of a method call.
@@ -211,13 +211,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
 
         // Fix up autoderefs and derefs.
+        let mut inside_union = false;
         for (i, &expr) in exprs.iter().rev().enumerate() {
             debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
 
+            let mut source = self.node_ty(expr.hir_id);
+            if matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::UnDeref, _)) {
+                // Clear previous flag; after a pointer indirection it does not apply any more.
+                inside_union = false;
+            }
+            if source.ty_adt_def().map_or(false, |adt| adt.is_union()) {
+                inside_union = true;
+            }
             // Fix up the autoderefs. Autorefs can only occur immediately preceding
             // overloaded place ops, and will be fixed by them in order to get
             // the correct region.
-            let mut source = self.node_ty(expr.hir_id);
             // Do not mutate adjustments in place, but rather take them,
             // and replace them after mutating them, to avoid having the
             // typeck results borrowed during (`deref_mut`) method resolution.
@@ -236,6 +244,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() {
                                 *deref = OverloadedDeref { region, mutbl };
                             }
+                            // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514).
+                            // This helps avoid accidental drops.
+                            if inside_union
+                                && source.ty_adt_def().map_or(false, |adt| adt.is_manually_drop())
+                            {
+                                let mut err = self.tcx.sess.struct_span_err(
+                                    expr.span,
+                                    "not automatically applying `DerefMut` on `ManuallyDrop` union field",
+                                );
+                                err.help(
+                                    "writing to this reference calls the destructor for the old value",
+                                );
+                                err.help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor");
+                                err.emit();
+                            }
                         }
                     }
                     source = adjustment.target;
diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs
index bc316a45be6..89270fb6c77 100644
--- a/compiler/rustc_typeck/src/coherence/builtin.rs
+++ b/compiler/rustc_typeck/src/coherence/builtin.rs
@@ -1,6 +1,7 @@
 //! Check properties that are required by built-in traits and set
 //! up data structures required by type-checking/codegen.
 
+use crate::errors::{CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem};
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -58,14 +59,7 @@ fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
         _ => bug!("expected Drop impl item"),
     };
 
-    struct_span_err!(
-        tcx.sess,
-        sp,
-        E0120,
-        "the `Drop` trait may only be implemented for structs, enums, and unions",
-    )
-    .span_label(sp, "must be a struct, enum, or union")
-    .emit();
+    tcx.sess.emit_err(DropImplOnWrongItem { span: sp });
 }
 
 fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
@@ -108,25 +102,10 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
             let span =
                 if let ItemKind::Impl { self_ty, .. } = item.kind { self_ty.span } else { span };
 
-            struct_span_err!(
-                tcx.sess,
-                span,
-                E0206,
-                "the trait `Copy` may not be implemented for this type"
-            )
-            .span_label(span, "type is not a structure or enumeration")
-            .emit();
+            tcx.sess.emit_err(CopyImplOnNonAdt { span });
         }
         Err(CopyImplementationError::HasDestructor) => {
-            struct_span_err!(
-                tcx.sess,
-                span,
-                E0184,
-                "the trait `Copy` may not be implemented for this type; the \
-                              type has a destructor"
-            )
-            .span_label(span, "Copy not allowed on types with destructors")
-            .emit();
+            tcx.sess.emit_err(CopyImplOnTypeWithDtor { span });
         }
     }
 }
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 94555e588bd..7d6b3df03b0 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -18,6 +18,7 @@ use crate::astconv::{AstConv, SizedByDefault};
 use crate::bounds::Bounds;
 use crate::check::intrinsic::intrinsic_operation_unsafety;
 use crate::constrained_generic_params as cgp;
+use crate::errors;
 use crate::middle::resolve_lifetime as rl;
 use rustc_ast as ast;
 use rustc_ast::MetaItemKind;
@@ -36,11 +37,12 @@ use rustc_middle::hir::map::Map;
 use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
 use rustc_middle::mir::mono::Linkage;
 use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::subst::InternalSubsts;
+use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
 use rustc_middle::ty::util::Discr;
 use rustc_middle::ty::util::IntTypeExt;
 use rustc_middle::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt};
 use rustc_middle::ty::{ReprOptions, ToPredicate, WithConstness};
+use rustc_middle::ty::{TypeFoldable, TypeVisitor};
 use rustc_session::config::SanitizerSet;
 use rustc_session::lint;
 use rustc_session::parse::feature_err;
@@ -49,6 +51,8 @@ use rustc_span::{Span, DUMMY_SP};
 use rustc_target::spec::abi;
 use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName;
 
+use smallvec::SmallVec;
+
 mod type_of;
 
 struct OnlySelfBounds(bool);
@@ -834,16 +838,11 @@ fn convert_variant(
             let fid = tcx.hir().local_def_id(f.hir_id);
             let dup_span = seen_fields.get(&f.ident.normalize_to_macros_2_0()).cloned();
             if let Some(prev_span) = dup_span {
-                struct_span_err!(
-                    tcx.sess,
-                    f.span,
-                    E0124,
-                    "field `{}` is already declared",
-                    f.ident
-                )
-                .span_label(f.span, "field already declared")
-                .span_label(prev_span, format!("`{}` first declared here", f.ident))
-                .emit();
+                tcx.sess.emit_err(errors::FieldAlreadyDeclared {
+                    field_name: f.ident,
+                    span: f.span,
+                    prev_span,
+                });
             } else {
                 seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span);
             }
@@ -1676,10 +1675,46 @@ fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicate
                 .alloc_from_iter(result.predicates.iter().chain(inferred_outlives).copied());
         }
     }
+
+    if tcx.features().const_evaluatable_checked {
+        let const_evaluatable = const_evaluatable_predicates_of(tcx, def_id, &result);
+        result.predicates =
+            tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(const_evaluatable));
+    }
+
     debug!("predicates_defined_on({:?}) = {:?}", def_id, result);
     result
 }
 
+pub fn const_evaluatable_predicates_of<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    predicates: &ty::GenericPredicates<'tcx>,
+) -> impl Iterator<Item = (ty::Predicate<'tcx>, Span)> {
+    #[derive(Default)]
+    struct ConstCollector<'tcx> {
+        ct: SmallVec<[(ty::WithOptConstParam<DefId>, SubstsRef<'tcx>); 4]>,
+    }
+
+    impl<'tcx> TypeVisitor<'tcx> for ConstCollector<'tcx> {
+        fn visit_const(&mut self, ct: &'tcx Const<'tcx>) -> bool {
+            if let ty::ConstKind::Unevaluated(def, substs, None) = ct.val {
+                self.ct.push((def, substs));
+            }
+            false
+        }
+    }
+
+    let mut collector = ConstCollector::default();
+    for (pred, _span) in predicates.predicates.iter() {
+        pred.visit_with(&mut collector);
+    }
+    warn!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.ct);
+    collector.ct.into_iter().map(move |(def_id, subst)| {
+        (ty::PredicateAtom::ConstEvaluatable(def_id, subst).to_predicate(tcx), DUMMY_SP)
+    })
+}
+
 /// Returns a list of all type predicates (explicit and implicit) for the definition with
 /// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus
 /// `Self: Trait` predicates for traits.
diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs
index f9668f1a0da..4b3250a1d44 100644
--- a/compiler/rustc_typeck/src/collect/type_of.rs
+++ b/compiler/rustc_typeck/src/collect/type_of.rs
@@ -1,5 +1,6 @@
+use crate::errors::AssocTypeOnInherentImpl;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{struct_span_err, Applicability, ErrorReported, StashKey};
+use rustc_errors::{Applicability, ErrorReported, StashKey};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -627,11 +628,5 @@ fn infer_placeholder_type(
 }
 
 fn report_assoc_ty_on_inherent_impl(tcx: TyCtxt<'_>, span: Span) {
-    struct_span_err!(
-        tcx.sess,
-        span,
-        E0202,
-        "associated types are not yet supported in inherent impls (see #8995)"
-    )
-    .emit();
+    tcx.sess.emit_err(AssocTypeOnInherentImpl { span });
 }
diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs
new file mode 100644
index 00000000000..a769e48d2ca
--- /dev/null
+++ b/compiler/rustc_typeck/src/errors.rs
@@ -0,0 +1,199 @@
+//! Errors emitted by typeck.
+use rustc_macros::SessionDiagnostic;
+use rustc_span::{symbol::Ident, Span, Symbol};
+
+#[derive(SessionDiagnostic)]
+#[error = "E0062"]
+pub struct FieldMultiplySpecifiedInInitializer {
+    #[message = "field `{ident}` specified more than once"]
+    #[label = "used more than once"]
+    pub span: Span,
+    #[label = "first use of `{ident}`"]
+    pub prev_span: Span,
+    pub ident: Ident,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0092"]
+pub struct UnrecognizedAtomicOperation<'a> {
+    #[message = "unrecognized atomic operation function: `{op}`"]
+    #[label = "unrecognized atomic operation"]
+    pub span: Span,
+    pub op: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0094"]
+pub struct WrongNumberOfTypeArgumentsToInstrinsic {
+    #[message = "intrinsic has wrong number of type \
+                         parameters: found {found}, expected {expected}"]
+    #[label = "expected {expected} type parameter"]
+    pub span: Span,
+    pub found: usize,
+    pub expected: usize,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0093"]
+pub struct UnrecognizedIntrinsicFunction {
+    #[message = "unrecognized intrinsic function: `{name}`"]
+    #[label = "unrecognized intrinsic"]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0195"]
+pub struct LifetimesOrBoundsMismatchOnTrait {
+    #[message = "lifetime parameters or bounds on {item_kind} `{ident}` do not match the trait declaration"]
+    #[label = "lifetimes do not match {item_kind} in trait"]
+    pub span: Span,
+    #[label = "lifetimes in impl do not match this {item_kind} in trait"]
+    pub generics_span: Option<Span>,
+    pub item_kind: &'static str,
+    pub ident: Ident,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0120"]
+pub struct DropImplOnWrongItem {
+    #[message = "the `Drop` trait may only be implemented for structs, enums, and unions"]
+    #[label = "must be a struct, enum, or union"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0124"]
+pub struct FieldAlreadyDeclared {
+    pub field_name: Ident,
+    #[message = "field `{field_name}` is already declared"]
+    #[label = "field already declared"]
+    pub span: Span,
+    #[label = "`{field_name}` first declared here"]
+    pub prev_span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0184"]
+pub struct CopyImplOnTypeWithDtor {
+    #[message = "the trait `Copy` may not be implemented for this type; the \
+                              type has a destructor"]
+    #[label = "Copy not allowed on types with destructors"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0202"]
+pub struct AssocTypeOnInherentImpl {
+    #[message = "associated types are not yet supported in inherent impls (see #8995)"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0203"]
+pub struct MultipleRelaxedDefaultBounds {
+    #[message = "type parameter has more than one relaxed default bound, only one is supported"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0206"]
+pub struct CopyImplOnNonAdt {
+    #[message = "the trait `Copy` may not be implemented for this type"]
+    #[label = "type is not a structure or enumeration"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0224"]
+pub struct TraitObjectDeclaredWithNoTraits {
+    #[message = "at least one trait is required for an object type"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0227"]
+pub struct AmbiguousLifetimeBound {
+    #[message = "ambiguous lifetime bound, explicit lifetime bound required"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0229"]
+pub struct AssocTypeBindingNotAllowed {
+    #[message = "associated type bindings are not allowed here"]
+    #[label = "associated type not allowed here"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0439"]
+pub struct SimdShuffleMissingLength {
+    #[message = "invalid `simd_shuffle`, needs length: `{name}`"]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0436"]
+pub struct FunctionalRecordUpdateOnNonStruct {
+    #[message = "functional record update syntax requires a struct"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0516"]
+pub struct TypeofReservedKeywordUsed {
+    #[message = "`typeof` is a reserved keyword but unimplemented"]
+    #[label = "reserved keyword"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0572"]
+pub struct ReturnStmtOutsideOfFnBody {
+    #[message = "return statement outside of function body"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0627"]
+pub struct YieldExprOutsideOfGenerator {
+    #[message = "yield expression outside of generator literal"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0639"]
+pub struct StructExprNonExhaustive {
+    #[message = "cannot create non-exhaustive {what} using struct expression"]
+    pub span: Span,
+    pub what: &'static str,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0699"]
+pub struct MethodCallOnUnknownType {
+    #[message = "the type of this value must be known to call a method on a raw pointer on it"]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0719"]
+pub struct ValueOfAssociatedStructAlreadySpecified {
+    #[message = "the value of the associated type `{item_name}` (from trait `{def_path}`) is already specified"]
+    #[label = "re-bound here"]
+    pub span: Span,
+    #[label = "`{item_name}` bound here first"]
+    pub prev_span: Span,
+    pub item_name: Ident,
+    pub def_path: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[error = "E0745"]
+pub struct AddressOfTemporaryTaken {
+    #[message = "cannot take address of a temporary"]
+    #[label = "temporary value"]
+    pub span: Span,
+}
diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs
index 891e482b431..4901d6041d6 100644
--- a/compiler/rustc_typeck/src/impl_wf_check.rs
+++ b/compiler/rustc_typeck/src/impl_wf_check.rs
@@ -187,7 +187,7 @@ fn enforce_impl_params_are_constrained(
     }
 
     // (*) This is a horrible concession to reality. I think it'd be
-    // better to just ban unconstrianed lifetimes outright, but in
+    // better to just ban unconstrained lifetimes outright, but in
     // practice people do non-hygenic macros like:
     //
     // ```
@@ -207,7 +207,7 @@ fn enforce_impl_params_are_constrained(
 }
 
 fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: &str) {
-    struct_span_err!(
+    let mut err = struct_span_err!(
         tcx.sess,
         span,
         E0207,
@@ -215,9 +215,17 @@ fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: &str)
         impl trait, self type, or predicates",
         kind,
         name
-    )
-    .span_label(span, format!("unconstrained {} parameter", kind))
-    .emit();
+    );
+    err.span_label(span, format!("unconstrained {} parameter", kind));
+    if kind == "const" {
+        err.note(
+            "expressions using a const parameter must map each value to a distinct output value",
+        );
+        err.note(
+            "proving the result of expressions other than the parameter are unique is not supported",
+        );
+    }
+    err.emit();
 }
 
 /// Enforce that we do not have two items in an impl with the same name.
diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs
index b056582faf8..0e9f4476c20 100644
--- a/compiler/rustc_typeck/src/lib.rs
+++ b/compiler/rustc_typeck/src/lib.rs
@@ -84,6 +84,7 @@ mod check_unused;
 mod coherence;
 mod collect;
 mod constrained_generic_params;
+mod errors;
 mod impl_wf_check;
 mod mem_categorization;
 mod outlives;