diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 4e2495ab14c9a..e5b61d7000a32 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -31,7 +31,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::sync::Lrc;
 use rustc_data_structures::thin_vec::ThinVec;
 use rustc_macros::HashStable_Generic;
-use rustc_serialize::{self, Decoder, Encoder};
+use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use rustc_span::source_map::{respan, Spanned};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
@@ -2488,11 +2488,11 @@ rustc_index::newtype_index! {
     }
 }
 
-impl<S: Encoder> rustc_serialize::Encodable<S> for AttrId {
+impl<S: Encoder> Encodable<S> for AttrId {
     fn encode(&self, _s: &mut S) {}
 }
 
-impl<D: Decoder> rustc_serialize::Decodable<D> for AttrId {
+impl<D: Decoder> Decodable<D> for AttrId {
     fn decode(_: &mut D) -> AttrId {
         crate::attr::mk_attr_id()
     }
diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs
index 8d187a4be8aee..37a4bf5fdcad7 100644
--- a/compiler/rustc_builtin_macros/src/assert/context.rs
+++ b/compiler/rustc_builtin_macros/src/assert/context.rs
@@ -1,44 +1,299 @@
-use rustc_ast::{ptr::P, Expr, Path};
+use crate::assert::expr_if_not;
+use rustc_ast::{
+    attr,
+    ptr::P,
+    token,
+    tokenstream::{DelimSpan, TokenStream, TokenTree},
+    BorrowKind, Expr, ExprKind, ItemKind, MacArgs, MacCall, MacDelimiter, Mutability, Path,
+    PathSegment, Stmt, UseTree, UseTreeKind, DUMMY_NODE_ID,
+};
+use rustc_ast_pretty::pprust;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_expand::base::ExtCtxt;
-use rustc_span::Span;
+use rustc_span::{
+    symbol::{sym, Ident, Symbol},
+    Span,
+};
 
 pub(super) struct Context<'cx, 'a> {
+    // Top-level `let captureN = Capture::new()` statements
+    capture_decls: Vec<Capture>,
     cx: &'cx ExtCtxt<'a>,
+    // Formatting string used for debugging
+    fmt_string: String,
+    // Top-level `let __local_bindN = &expr` statements
+    local_bind_decls: Vec<Stmt>,
+    // Used to avoid capturing duplicated paths
+    //
+    // ```rust
+    // let a = 1i32;
+    // assert!(add(a, a) == 3);
+    // ```
+    paths: FxHashSet<Ident>,
     span: Span,
 }
 
 impl<'cx, 'a> Context<'cx, 'a> {
     pub(super) fn new(cx: &'cx ExtCtxt<'a>, span: Span) -> Self {
-        Self { cx, span }
+        Self {
+            capture_decls: <_>::default(),
+            cx,
+            fmt_string: <_>::default(),
+            local_bind_decls: <_>::default(),
+            paths: <_>::default(),
+            span,
+        }
     }
 
-    /// Builds the whole `assert!` expression.
+    /// Builds the whole `assert!` expression. For example, `let elem = 1; assert!(elem == 1);` expands to:
     ///
+    /// ```rust
+    /// let elem = 1;
     /// {
-    ///    use ::core::asserting::{ ... };
+    ///   #[allow(unused_imports)]
+    ///   use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable};
+    ///   let mut __capture0 = ::core::asserting::Capture::new();
+    ///   let __local_bind0 = &elem;
+    ///   if !(
+    ///     *{
+    ///       (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
+    ///       __local_bind0
+    ///     } == 1
+    ///   ) {
+    ///     panic!("Assertion failed: elem == 1\nWith captures:\n  elem = {}", __capture0)
+    ///   }
+    /// }
+    /// ```
+    pub(super) fn build(mut self, mut cond_expr: P<Expr>, panic_path: Path) -> P<Expr> {
+        let expr_str = pprust::expr_to_string(&cond_expr);
+        self.manage_cond_expr(&mut cond_expr);
+        let initial_imports = self.build_initial_imports();
+        let panic = self.build_panic(&expr_str, panic_path);
+
+        let Self { capture_decls, cx, local_bind_decls, span, .. } = self;
+
+        let mut stmts = Vec::with_capacity(4);
+        stmts.push(initial_imports);
+        stmts.extend(capture_decls.into_iter().map(|c| c.decl));
+        stmts.extend(local_bind_decls);
+        stmts.push(cx.stmt_expr(expr_if_not(cx, span, cond_expr, panic, None)));
+        cx.expr_block(cx.block(span, stmts))
+    }
+
+    /// Initial **trait** imports
+    ///
+    /// use ::core::asserting::{ ... };
+    fn build_initial_imports(&self) -> Stmt {
+        let nested_tree = |this: &Self, sym| {
+            (
+                UseTree {
+                    prefix: this.cx.path(this.span, vec![Ident::with_dummy_span(sym)]),
+                    kind: UseTreeKind::Simple(None, DUMMY_NODE_ID, DUMMY_NODE_ID),
+                    span: this.span,
+                },
+                DUMMY_NODE_ID,
+            )
+        };
+        self.cx.stmt_item(
+            self.span,
+            self.cx.item(
+                self.span,
+                Ident::empty(),
+                vec![self.cx.attribute(attr::mk_list_item(
+                    Ident::new(sym::allow, self.span),
+                    vec![attr::mk_nested_word_item(Ident::new(sym::unused_imports, self.span))],
+                ))],
+                ItemKind::Use(UseTree {
+                    prefix: self.cx.path(self.span, self.cx.std_path(&[sym::asserting])),
+                    kind: UseTreeKind::Nested(vec![
+                        nested_tree(self, sym::TryCaptureGeneric),
+                        nested_tree(self, sym::TryCapturePrintable),
+                    ]),
+                    span: self.span,
+                }),
+            ),
+        )
+    }
+
+    /// The necessary custom `panic!(...)` expression.
+    ///
+    /// panic!(
+    ///     "Assertion failed: ... \n With expansion: ...",
+    ///     __capture0,
+    ///     ...
+    /// );
+    fn build_panic(&self, expr_str: &str, panic_path: Path) -> P<Expr> {
+        let escaped_expr_str = escape_to_fmt(expr_str);
+        let initial = [
+            TokenTree::token(
+                token::Literal(token::Lit {
+                    kind: token::LitKind::Str,
+                    symbol: Symbol::intern(&if self.fmt_string.is_empty() {
+                        format!("Assertion failed: {escaped_expr_str}")
+                    } else {
+                        format!(
+                            "Assertion failed: {escaped_expr_str}\nWith captures:\n{}",
+                            &self.fmt_string
+                        )
+                    }),
+                    suffix: None,
+                }),
+                self.span,
+            ),
+            TokenTree::token(token::Comma, self.span),
+        ];
+        let captures = self.capture_decls.iter().flat_map(|cap| {
+            [
+                TokenTree::token(token::Ident(cap.ident.name, false), cap.ident.span),
+                TokenTree::token(token::Comma, self.span),
+            ]
+        });
+        self.cx.expr(
+            self.span,
+            ExprKind::MacCall(MacCall {
+                path: panic_path,
+                args: P(MacArgs::Delimited(
+                    DelimSpan::from_single(self.span),
+                    MacDelimiter::Parenthesis,
+                    initial.into_iter().chain(captures).collect::<TokenStream>(),
+                )),
+                prior_type_ascription: None,
+            }),
+        )
+    }
+
+    /// Recursive function called until `cond_expr` and `fmt_str` are fully modified.
+    ///
+    /// See [Self::manage_initial_capture] and [Self::manage_try_capture]
+    fn manage_cond_expr(&mut self, expr: &mut P<Expr>) {
+        match (*expr).kind {
+            ExprKind::Binary(_, ref mut lhs, ref mut rhs) => {
+                self.manage_cond_expr(lhs);
+                self.manage_cond_expr(rhs);
+            }
+            ExprKind::Path(_, Path { ref segments, .. }) if let &[ref path_segment] = &segments[..] => {
+                let path_ident = path_segment.ident;
+                self.manage_initial_capture(expr, path_ident);
+            }
+            _ => {}
+        }
+    }
+
+    /// Pushes the top-level declarations and modifies `expr` to try capturing variables.
     ///
-    ///    let mut __capture0 = Capture::new();
-    ///    ...
-    ///    ...
-    ///    ...
+    /// `fmt_str`, the formatting string used for debugging, is constructed to show possible
+    /// captured variables.
+    fn manage_initial_capture(&mut self, expr: &mut P<Expr>, path_ident: Ident) {
+        if self.paths.contains(&path_ident) {
+            return;
+        } else {
+            self.fmt_string.push_str("  ");
+            self.fmt_string.push_str(path_ident.as_str());
+            self.fmt_string.push_str(" = {:?}\n");
+            let _ = self.paths.insert(path_ident);
+        }
+        let curr_capture_idx = self.capture_decls.len();
+        let capture_string = format!("__capture{curr_capture_idx}");
+        let ident = Ident::new(Symbol::intern(&capture_string), self.span);
+        let init_std_path = self.cx.std_path(&[sym::asserting, sym::Capture, sym::new]);
+        let init = self.cx.expr_call(
+            self.span,
+            self.cx.expr_path(self.cx.path(self.span, init_std_path)),
+            vec![],
+        );
+        let capture = Capture { decl: self.cx.stmt_let(self.span, true, ident, init), ident };
+        self.capture_decls.push(capture);
+        self.manage_try_capture(ident, curr_capture_idx, expr);
+    }
+
+    /// Tries to copy `__local_bindN` into `__captureN`.
     ///
-    ///    if !{
-    ///       ...
-    ///       ...
-    ///       ...
-    ///    } {
-    ///        panic!(
-    ///            "Assertion failed: ... \n With expansion: ...",
-    ///            __capture0,
-    ///            ...
-    ///            ...
-    ///            ...
-    ///        );
-    ///    }
+    /// *{
+    ///    (&Wrapper(__local_bindN)).try_capture(&mut __captureN);
+    ///    __local_bindN
     /// }
-    pub(super) fn build(self, _cond_expr: P<Expr>, _panic_path: Path) -> P<Expr> {
-        let Self { cx, span, .. } = self;
-        let stmts = Vec::new();
-        cx.expr_block(cx.block(span, stmts))
+    fn manage_try_capture(&mut self, capture: Ident, curr_capture_idx: usize, expr: &mut P<Expr>) {
+        let local_bind_string = format!("__local_bind{curr_capture_idx}");
+        let local_bind = Ident::new(Symbol::intern(&local_bind_string), self.span);
+        self.local_bind_decls.push(self.cx.stmt_let(
+            self.span,
+            false,
+            local_bind,
+            self.cx.expr_addr_of(self.span, expr.clone()),
+        ));
+        let wrapper = self.cx.expr_call(
+            self.span,
+            self.cx.expr_path(
+                self.cx.path(self.span, self.cx.std_path(&[sym::asserting, sym::Wrapper])),
+            ),
+            vec![self.cx.expr_path(Path::from_ident(local_bind))],
+        );
+        let try_capture_call = self
+            .cx
+            .stmt_expr(expr_method_call(
+                self.cx,
+                PathSegment {
+                    args: None,
+                    id: DUMMY_NODE_ID,
+                    ident: Ident::new(sym::try_capture, self.span),
+                },
+                vec![
+                    expr_paren(self.cx, self.span, self.cx.expr_addr_of(self.span, wrapper)),
+                    expr_addr_of_mut(
+                        self.cx,
+                        self.span,
+                        self.cx.expr_path(Path::from_ident(capture)),
+                    ),
+                ],
+                self.span,
+            ))
+            .add_trailing_semicolon();
+        let local_bind_path = self.cx.expr_path(Path::from_ident(local_bind));
+        let ret = self.cx.stmt_expr(local_bind_path);
+        let block = self.cx.expr_block(self.cx.block(self.span, vec![try_capture_call, ret]));
+        *expr = self.cx.expr_deref(self.span, block);
+    }
+}
+
+/// Information about a captured element.
+#[derive(Debug)]
+struct Capture {
+    // Generated indexed `Capture` statement.
+    //
+    // `let __capture{} = Capture::new();`
+    decl: Stmt,
+    // The name of the generated indexed `Capture` variable.
+    //
+    // `__capture{}`
+    ident: Ident,
+}
+
+/// Escapes to use as a formatting string.
+fn escape_to_fmt(s: &str) -> String {
+    let mut rslt = String::with_capacity(s.len());
+    for c in s.chars() {
+        rslt.extend(c.escape_debug());
+        match c {
+            '{' | '}' => rslt.push(c),
+            _ => {}
+        }
     }
+    rslt
+}
+
+fn expr_addr_of_mut(cx: &ExtCtxt<'_>, sp: Span, e: P<Expr>) -> P<Expr> {
+    cx.expr(sp, ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, e))
+}
+
+fn expr_method_call(
+    cx: &ExtCtxt<'_>,
+    path: PathSegment,
+    args: Vec<P<Expr>>,
+    span: Span,
+) -> P<Expr> {
+    cx.expr(span, ExprKind::MethodCall(path, args, span))
+}
+
+fn expr_paren(cx: &ExtCtxt<'_>, sp: Span, e: P<Expr>) -> P<Expr> {
+    cx.expr(sp, ExprKind::Paren(e))
 }
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index 124d0d18cdbe2..11565ba72d755 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -6,6 +6,7 @@
 #![feature(array_windows)]
 #![feature(box_patterns)]
 #![feature(decl_macro)]
+#![feature(if_let_guard)]
 #![feature(is_sorted)]
 #![feature(let_chains)]
 #![feature(let_else)]
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index 771157dcad954..750432b0b2653 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -29,7 +29,8 @@ use rustc_middle::dep_graph::WorkProduct;
 use rustc_middle::middle::dependency_format::Dependencies;
 use rustc_middle::middle::exported_symbols::SymbolExportKind;
 use rustc_middle::ty::query::{ExternProviders, Providers};
-use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder};
+use rustc_serialize::opaque::{MemDecoder, MemEncoder};
+use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT};
 use rustc_session::cstore::{self, CrateSource};
 use rustc_session::utils::NativeLibKind;
@@ -203,7 +204,7 @@ const RUSTC_VERSION: Option<&str> = option_env!("CFG_VERSION");
 
 impl CodegenResults {
     pub fn serialize_rlink(codegen_results: &CodegenResults) -> Vec<u8> {
-        let mut encoder = opaque::Encoder::new();
+        let mut encoder = MemEncoder::new();
         encoder.emit_raw_bytes(RLINK_MAGIC);
         // `emit_raw_bytes` is used to make sure that the version representation does not depend on
         // Encoder's inner representation of `u32`.
@@ -230,7 +231,7 @@ impl CodegenResults {
             return Err(".rlink file was produced with encoding version {version_array}, but the current version is {RLINK_VERSION}".to_string());
         }
 
-        let mut decoder = opaque::Decoder::new(&data[4..], 0);
+        let mut decoder = MemDecoder::new(&data[4..], 0);
         let rustc_version = decoder.read_str();
         let current_version = RUSTC_VERSION.unwrap();
         if rustc_version != current_version {
diff --git a/compiler/rustc_data_structures/src/fingerprint.rs b/compiler/rustc_data_structures/src/fingerprint.rs
index a032b039f34ef..5ff2d18dd2be3 100644
--- a/compiler/rustc_data_structures/src/fingerprint.rs
+++ b/compiler/rustc_data_structures/src/fingerprint.rs
@@ -1,5 +1,5 @@
 use crate::stable_hasher;
-use rustc_serialize::{Decodable, Encodable};
+use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use std::convert::TryInto;
 use std::hash::{Hash, Hasher};
 
@@ -142,14 +142,14 @@ impl stable_hasher::StableHasherResult for Fingerprint {
 
 impl_stable_hash_via_hash!(Fingerprint);
 
-impl<E: rustc_serialize::Encoder> Encodable<E> for Fingerprint {
+impl<E: Encoder> Encodable<E> for Fingerprint {
     #[inline]
     fn encode(&self, s: &mut E) {
         s.emit_raw_bytes(&self.to_le_bytes());
     }
 }
 
-impl<D: rustc_serialize::Decoder> Decodable<D> for Fingerprint {
+impl<D: Decoder> Decodable<D> for Fingerprint {
     #[inline]
     fn decode(d: &mut D) -> Self {
         Fingerprint::from_le_bytes(d.read_raw_bytes(16).try_into().unwrap())
@@ -184,7 +184,7 @@ impl std::fmt::Display for PackedFingerprint {
     }
 }
 
-impl<E: rustc_serialize::Encoder> Encodable<E> for PackedFingerprint {
+impl<E: Encoder> Encodable<E> for PackedFingerprint {
     #[inline]
     fn encode(&self, s: &mut E) {
         // Copy to avoid taking reference to packed field.
@@ -193,7 +193,7 @@ impl<E: rustc_serialize::Encoder> Encodable<E> for PackedFingerprint {
     }
 }
 
-impl<D: rustc_serialize::Decoder> Decodable<D> for PackedFingerprint {
+impl<D: Decoder> Decodable<D> for PackedFingerprint {
     #[inline]
     fn decode(d: &mut D) -> Self {
         Self(Fingerprint::decode(d))
diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs
index 9de14950aa8d3..9c325faae8058 100644
--- a/compiler/rustc_incremental/src/persist/load.rs
+++ b/compiler/rustc_incremental/src/persist/load.rs
@@ -4,7 +4,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::memmap::Mmap;
 use rustc_middle::dep_graph::{SerializedDepGraph, WorkProduct, WorkProductId};
 use rustc_middle::ty::OnDiskCache;
-use rustc_serialize::opaque::Decoder;
+use rustc_serialize::opaque::MemDecoder;
 use rustc_serialize::Decodable;
 use rustc_session::config::IncrementalStateAssertion;
 use rustc_session::Session;
@@ -156,7 +156,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture {
 
         if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result {
             // Decode the list of work_products
-            let mut work_product_decoder = Decoder::new(&work_products_data[..], start_pos);
+            let mut work_product_decoder = MemDecoder::new(&work_products_data[..], start_pos);
             let work_products: Vec<SerializedWorkProduct> =
                 Decodable::decode(&mut work_product_decoder);
 
@@ -193,7 +193,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture {
             LoadResult::DataOutOfDate => LoadResult::DataOutOfDate,
             LoadResult::Error { message } => LoadResult::Error { message },
             LoadResult::Ok { data: (bytes, start_pos) } => {
-                let mut decoder = Decoder::new(&bytes, start_pos);
+                let mut decoder = MemDecoder::new(&bytes, start_pos);
                 let prev_commandline_args_hash = u64::decode(&mut decoder);
 
                 if prev_commandline_args_hash != expected_hash {
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 186031d4586aa..c1ded99a25af5 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -26,7 +26,8 @@ use rustc_middle::ty::codec::TyDecoder;
 use rustc_middle::ty::fast_reject::SimplifiedType;
 use rustc_middle::ty::GeneratorDiagnosticData;
 use rustc_middle::ty::{self, ParameterizedOverTcx, Ty, TyCtxt, Visibility};
-use rustc_serialize::{opaque, Decodable, Decoder};
+use rustc_serialize::opaque::MemDecoder;
+use rustc_serialize::{Decodable, Decoder};
 use rustc_session::cstore::{
     CrateSource, ExternCrate, ForeignModule, LinkagePreference, NativeLib,
 };
@@ -154,7 +155,7 @@ struct ImportedSourceFile {
 }
 
 pub(super) struct DecodeContext<'a, 'tcx> {
-    opaque: opaque::Decoder<'a>,
+    opaque: MemDecoder<'a>,
     cdata: Option<CrateMetadataRef<'a>>,
     blob: &'a MetadataBlob,
     sess: Option<&'tcx Session>,
@@ -186,7 +187,7 @@ pub(super) trait Metadata<'a, 'tcx>: Copy {
     fn decoder(self, pos: usize) -> DecodeContext<'a, 'tcx> {
         let tcx = self.tcx();
         DecodeContext {
-            opaque: opaque::Decoder::new(self.blob(), pos),
+            opaque: MemDecoder::new(self.blob(), pos),
             cdata: self.cdata(),
             blob: self.blob(),
             sess: self.sess().or(tcx.map(|tcx| tcx.sess)),
@@ -418,7 +419,7 @@ impl<'a, 'tcx> TyDecoder for DecodeContext<'a, 'tcx> {
     where
         F: FnOnce(&mut Self) -> R,
     {
-        let new_opaque = opaque::Decoder::new(self.opaque.data, pos);
+        let new_opaque = MemDecoder::new(self.opaque.data, pos);
         let old_opaque = mem::replace(&mut self.opaque, new_opaque);
         let old_state = mem::replace(&mut self.lazy_state, LazyState::NoNode);
         let r = f(self);
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 50c09aaf8d445..26fb21020008a 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -27,7 +27,8 @@ use rustc_middle::ty::codec::TyEncoder;
 use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams};
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, SymbolName, Ty, TyCtxt};
-use rustc_serialize::{opaque, Encodable, Encoder};
+use rustc_serialize::opaque::MemEncoder;
+use rustc_serialize::{Encodable, Encoder};
 use rustc_session::config::CrateType;
 use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib};
 use rustc_span::hygiene::{ExpnIndex, HygieneEncodeContext, MacroKind};
@@ -43,7 +44,7 @@ use std::num::NonZeroUsize;
 use tracing::{debug, trace};
 
 pub(super) struct EncodeContext<'a, 'tcx> {
-    opaque: opaque::Encoder,
+    opaque: MemEncoder,
     tcx: TyCtxt<'tcx>,
     feat: &'tcx rustc_feature::Features,
 
@@ -93,8 +94,8 @@ macro_rules! encoder_methods {
 }
 
 impl<'a, 'tcx> Encoder for EncodeContext<'a, 'tcx> {
-    type Ok = <opaque::Encoder as Encoder>::Ok;
-    type Err = <opaque::Encoder as Encoder>::Err;
+    type Ok = <MemEncoder as Encoder>::Ok;
+    type Err = <MemEncoder as Encoder>::Err;
 
     encoder_methods! {
         emit_usize(usize);
@@ -2180,7 +2181,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata {
 }
 
 fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata {
-    let mut encoder = opaque::Encoder::new();
+    let mut encoder = MemEncoder::new();
     encoder.emit_raw_bytes(METADATA_HEADER);
 
     // Will be filled with the root position after encoding everything.
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index fb2ffe1d73d96..04f0847f5cccc 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -22,7 +22,7 @@ use rustc_middle::ty::fast_reject::SimplifiedType;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, ReprOptions, Ty};
 use rustc_middle::ty::{GeneratorDiagnosticData, ParameterizedOverTcx, TyCtxt};
-use rustc_serialize::opaque::Encoder;
+use rustc_serialize::opaque::MemEncoder;
 use rustc_session::config::SymbolManglingVersion;
 use rustc_session::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib};
 use rustc_span::edition::Edition;
@@ -323,7 +323,7 @@ macro_rules! define_tables {
         }
 
         impl TableBuilders {
-            fn encode(&self, buf: &mut Encoder) -> LazyTables {
+            fn encode(&self, buf: &mut MemEncoder) -> LazyTables {
                 LazyTables {
                     $($name: self.$name.encode(buf)),+
                 }
diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs
index 8baa67a8f9fcf..5ab4269ae99ad 100644
--- a/compiler/rustc_metadata/src/rmeta/table.rs
+++ b/compiler/rustc_metadata/src/rmeta/table.rs
@@ -4,8 +4,8 @@ use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_hir::def::{CtorKind, CtorOf};
 use rustc_index::vec::Idx;
 use rustc_middle::ty::ParameterizedOverTcx;
-use rustc_serialize::opaque::Encoder;
-use rustc_serialize::Encoder as _;
+use rustc_serialize::opaque::MemEncoder;
+use rustc_serialize::Encoder;
 use rustc_span::hygiene::MacroKind;
 use std::convert::TryInto;
 use std::marker::PhantomData;
@@ -281,7 +281,7 @@ where
         Some(value).write_to_bytes(&mut self.blocks[i]);
     }
 
-    pub(crate) fn encode<const N: usize>(&self, buf: &mut Encoder) -> LazyTable<I, T>
+    pub(crate) fn encode<const N: usize>(&self, buf: &mut MemEncoder) -> LazyTable<I, T>
     where
         Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
     {
diff --git a/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs b/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs
index 096bf8cbc158a..1279f5aee3691 100644
--- a/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs
+++ b/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs
@@ -3,7 +3,7 @@ use rustc_data_structures::graph::{
 };
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::OnceCell;
-use rustc_serialize as serialize;
+use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 
 /// Helper type to cache the result of `graph::is_cyclic`.
 #[derive(Clone, Debug)]
@@ -36,17 +36,17 @@ impl GraphIsCyclicCache {
     }
 }
 
-impl<S: serialize::Encoder> serialize::Encodable<S> for GraphIsCyclicCache {
+impl<S: Encoder> Encodable<S> for GraphIsCyclicCache {
     #[inline]
     fn encode(&self, s: &mut S) {
-        serialize::Encodable::encode(&(), s);
+        Encodable::encode(&(), s);
     }
 }
 
-impl<D: serialize::Decoder> serialize::Decodable<D> for GraphIsCyclicCache {
+impl<D: Decoder> Decodable<D> for GraphIsCyclicCache {
     #[inline]
     fn decode(d: &mut D) -> Self {
-        let () = serialize::Decodable::decode(d);
+        let () = Decodable::decode(d);
         Self::new()
     }
 }
diff --git a/compiler/rustc_middle/src/mir/predecessors.rs b/compiler/rustc_middle/src/mir/predecessors.rs
index 9bc0cb1138ff1..620cf7e336ba4 100644
--- a/compiler/rustc_middle/src/mir/predecessors.rs
+++ b/compiler/rustc_middle/src/mir/predecessors.rs
@@ -3,7 +3,7 @@
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::OnceCell;
 use rustc_index::vec::IndexVec;
-use rustc_serialize as serialize;
+use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use smallvec::SmallVec;
 
 use crate::mir::{BasicBlock, BasicBlockData};
@@ -54,12 +54,12 @@ impl PredecessorCache {
     }
 }
 
-impl<S: serialize::Encoder> serialize::Encodable<S> for PredecessorCache {
+impl<S: Encoder> Encodable<S> for PredecessorCache {
     #[inline]
     fn encode(&self, _s: &mut S) {}
 }
 
-impl<D: serialize::Decoder> serialize::Decodable<D> for PredecessorCache {
+impl<D: Decoder> Decodable<D> for PredecessorCache {
     #[inline]
     fn decode(_: &mut D) -> Self {
         Self::new()
diff --git a/compiler/rustc_middle/src/mir/switch_sources.rs b/compiler/rustc_middle/src/mir/switch_sources.rs
index 4872a7835e3fa..99d13fcfef43e 100644
--- a/compiler/rustc_middle/src/mir/switch_sources.rs
+++ b/compiler/rustc_middle/src/mir/switch_sources.rs
@@ -5,7 +5,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::stable_map::FxHashMap;
 use rustc_data_structures::sync::OnceCell;
 use rustc_index::vec::IndexVec;
-use rustc_serialize as serialize;
+use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use smallvec::SmallVec;
 
 use crate::mir::{BasicBlock, BasicBlockData, Terminator, TerminatorKind};
@@ -54,12 +54,12 @@ impl SwitchSourceCache {
     }
 }
 
-impl<S: serialize::Encoder> serialize::Encodable<S> for SwitchSourceCache {
+impl<S: Encoder> Encodable<S> for SwitchSourceCache {
     #[inline]
     fn encode(&self, _s: &mut S) {}
 }
 
-impl<D: serialize::Decoder> serialize::Decodable<D> for SwitchSourceCache {
+impl<D: Decoder> Decodable<D> for SwitchSourceCache {
     #[inline]
     fn decode(_: &mut D) -> Self {
         Self::new()
diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs
index f745e55307ae2..7228e3f33b126 100644
--- a/compiler/rustc_middle/src/mir/traversal.rs
+++ b/compiler/rustc_middle/src/mir/traversal.rs
@@ -1,7 +1,7 @@
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::OnceCell;
 use rustc_index::bit_set::BitSet;
-use rustc_serialize as serialize;
+use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 
 use super::*;
 
@@ -365,12 +365,12 @@ impl PostorderCache {
     }
 }
 
-impl<S: serialize::Encoder> serialize::Encodable<S> for PostorderCache {
+impl<S: Encoder> Encodable<S> for PostorderCache {
     #[inline]
     fn encode(&self, _s: &mut S) {}
 }
 
-impl<D: serialize::Decoder> serialize::Decodable<D> for PostorderCache {
+impl<D: Decoder> Decodable<D> for PostorderCache {
     #[inline]
     fn decode(_: &mut D) -> Self {
         Self::new()
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index b224fa9596eb8..81bab0e3513c9 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -2248,36 +2248,59 @@ impl<'a> Parser<'a> {
         &mut self,
         attrs: AttrVec,
         lo: Span,
-        cond: P<Expr>,
+        mut cond: P<Expr>,
     ) -> PResult<'a, P<Expr>> {
-        let missing_then_block_binop_span = || {
-            match cond.kind {
-                ExprKind::Binary(Spanned { span: binop_span, .. }, _, ref right)
-                    if let ExprKind::Block(..) = right.kind => Some(binop_span),
-                _ => None
+        let cond_span = cond.span;
+        // Tries to interpret `cond` as either a missing expression if it's a block,
+        // or as an unfinished expression if it's a binop and the RHS is a block.
+        // We could probably add more recoveries here too...
+        let mut recover_block_from_condition = |this: &mut Self| {
+            let block = match &mut cond.kind {
+                ExprKind::Binary(Spanned { span: binop_span, .. }, _, right)
+                    if let ExprKind::Block(_, None) = right.kind => {
+                        this.error_missing_if_then_block(lo, cond_span.shrink_to_lo().to(*binop_span), true).emit();
+                        std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi()))
+                    },
+                ExprKind::Block(_, None) => {
+                    this.error_missing_if_cond(lo, cond_span).emit();
+                    std::mem::replace(&mut cond, this.mk_expr_err(cond_span.shrink_to_hi()))
+                }
+                _ => {
+                    return None;
+                }
+            };
+            if let ExprKind::Block(block, _) = &block.kind {
+                Some(block.clone())
+            } else {
+                unreachable!()
             }
         };
-        // Verify that the parsed `if` condition makes sense as a condition. If it is a block, then
-        // verify that the last statement is either an implicit return (no `;`) or an explicit
-        // return. This won't catch blocks with an explicit `return`, but that would be caught by
-        // the dead code lint.
-        let thn = if self.token.is_keyword(kw::Else) || !cond.returns() {
-            if let Some(binop_span) = missing_then_block_binop_span() {
-                self.error_missing_if_then_block(lo, None, Some(binop_span)).emit();
-                self.mk_block_err(cond.span)
+        // Parse then block
+        let thn = if self.token.is_keyword(kw::Else) {
+            if let Some(block) = recover_block_from_condition(self) {
+                block
             } else {
-                self.error_missing_if_cond(lo, cond.span)
+                self.error_missing_if_then_block(lo, cond_span, false).emit();
+                self.mk_block_err(cond_span.shrink_to_hi())
             }
         } else {
             let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery.
-            let not_block = self.token != token::OpenDelim(Delimiter::Brace);
-            let block = self.parse_block().map_err(|err| {
-                if not_block {
-                    self.error_missing_if_then_block(lo, Some(err), missing_then_block_binop_span())
+            let block = if self.check(&token::OpenDelim(Delimiter::Brace)) {
+                self.parse_block()?
+            } else {
+                if let Some(block) = recover_block_from_condition(self) {
+                    block
                 } else {
-                    err
+                    // Parse block, which will always fail, but we can add a nice note to the error
+                    self.parse_block().map_err(|mut err| {
+                        err.span_note(
+                            cond_span,
+                            "the `if` expression is missing a block after this condition",
+                        );
+                        err
+                    })?
                 }
-            })?;
+            };
             self.error_on_if_block_attrs(lo, false, block.span, &attrs);
             block
         };
@@ -2288,31 +2311,34 @@ impl<'a> Parser<'a> {
     fn error_missing_if_then_block(
         &self,
         if_span: Span,
-        err: Option<DiagnosticBuilder<'a, ErrorGuaranteed>>,
-        binop_span: Option<Span>,
+        cond_span: Span,
+        is_unfinished: bool,
     ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        let msg = "this `if` expression has a condition, but no block";
-
-        let mut err = if let Some(mut err) = err {
-            err.span_label(if_span, msg);
-            err
+        let mut err = self.struct_span_err(
+            if_span,
+            "this `if` expression is missing a block after the condition",
+        );
+        if is_unfinished {
+            err.span_help(cond_span, "this binary operation is possibly unfinished");
         } else {
-            self.struct_span_err(if_span, msg)
-        };
-
-        if let Some(binop_span) = binop_span {
-            err.span_help(binop_span, "maybe you forgot the right operand of the condition?");
+            err.span_help(cond_span.shrink_to_hi(), "add a block here");
         }
-
         err
     }
 
-    fn error_missing_if_cond(&self, lo: Span, span: Span) -> P<ast::Block> {
-        let sp = self.sess.source_map().next_point(lo);
-        self.struct_span_err(sp, "missing condition for `if` expression")
-            .span_label(sp, "expected if condition here")
-            .emit();
-        self.mk_block_err(span)
+    fn error_missing_if_cond(
+        &self,
+        lo: Span,
+        span: Span,
+    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+        let next_span = self.sess.source_map().next_point(lo);
+        let mut err = self.struct_span_err(next_span, "missing condition for `if` expression");
+        err.span_label(next_span, "expected condition here");
+        err.span_label(
+            self.sess.source_map().start_point(span),
+            "if this block is the condition of the `if` expression, then it must be followed by another block"
+        );
+        err
     }
 
     /// Parses the condition of a `if` or `while` expression.
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 42355dd93a7a5..51bd9d2d386ad 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -432,10 +432,23 @@ impl<'a> Parser<'a> {
         //
         // which is valid in other languages, but not Rust.
         match self.parse_stmt_without_recovery(false, ForceCollect::No) {
-            // If the next token is an open brace (e.g., `if a b {`), the place-
-            // inside-a-block suggestion would be more likely wrong than right.
+            // If the next token is an open brace, e.g., we have:
+            //
+            //     if expr other_expr {
+            //        ^    ^          ^- lookahead(1) is a brace
+            //        |    |- current token is not "else"
+            //        |- (statement we just parsed)
+            //
+            // the place-inside-a-block suggestion would be more likely wrong than right.
+            //
+            // FIXME(compiler-errors): this should probably parse an arbitrary expr and not
+            // just lookahead one token, so we can see if there's a brace after _that_,
+            // since we want to protect against:
+            //     `if 1 1 + 1 {` being suggested as  `if { 1 } 1 + 1 {`
+            //                                            +   +
             Ok(Some(_))
-                if self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace))
+                if (!self.token.is_keyword(kw::Else)
+                    && self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace)))
                     || do_not_suggest_help => {}
             // Do not suggest `if foo println!("") {;}` (as would be seen in test for #46836).
             Ok(Some(Stmt { kind: StmtKind::Empty, .. })) => {}
diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs
index c2c876f7f1a12..d7327ca4bc602 100644
--- a/compiler/rustc_query_impl/src/on_disk_cache.rs
+++ b/compiler/rustc_query_impl/src/on_disk_cache.rs
@@ -15,7 +15,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_query_system::dep_graph::DepContext;
 use rustc_query_system::query::{QueryCache, QueryContext, QuerySideEffects};
 use rustc_serialize::{
-    opaque::{self, FileEncodeResult, FileEncoder, IntEncodedWithFixedSize},
+    opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixedSize, MemDecoder},
     Decodable, Decoder, Encodable, Encoder,
 };
 use rustc_session::Session;
@@ -158,7 +158,7 @@ impl<'sess> rustc_middle::ty::OnDiskCache<'sess> for OnDiskCache<'sess> {
 
         // Wrap in a scope so we can borrow `data`.
         let footer: Footer = {
-            let mut decoder = opaque::Decoder::new(&data, start_pos);
+            let mut decoder = MemDecoder::new(&data, start_pos);
 
             // Decode the *position* of the footer, which can be found in the
             // last 8 bytes of the file.
@@ -437,7 +437,7 @@ impl<'sess> OnDiskCache<'sess> {
         let serialized_data = self.serialized_data.read();
         let mut decoder = CacheDecoder {
             tcx,
-            opaque: opaque::Decoder::new(serialized_data.as_deref().unwrap_or(&[]), pos.to_usize()),
+            opaque: MemDecoder::new(serialized_data.as_deref().unwrap_or(&[]), pos.to_usize()),
             source_map: self.source_map,
             file_index_to_file: &self.file_index_to_file,
             file_index_to_stable_id: &self.file_index_to_stable_id,
@@ -458,7 +458,7 @@ impl<'sess> OnDiskCache<'sess> {
 /// will also handle things that contain `Ty` instances.
 pub struct CacheDecoder<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
-    opaque: opaque::Decoder<'a>,
+    opaque: MemDecoder<'a>,
     source_map: &'a SourceMap,
     file_index_to_file: &'a Lock<FxHashMap<SourceFileIndex, Lrc<SourceFile>>>,
     file_index_to_stable_id: &'a FxHashMap<SourceFileIndex, EncodedSourceFileId>,
@@ -510,7 +510,7 @@ trait DecoderWithPosition: Decoder {
     fn position(&self) -> usize;
 }
 
-impl<'a> DecoderWithPosition for opaque::Decoder<'a> {
+impl<'a> DecoderWithPosition for MemDecoder<'a> {
     fn position(&self) -> usize {
         self.position()
     }
@@ -586,7 +586,7 @@ impl<'a, 'tcx> TyDecoder for CacheDecoder<'a, 'tcx> {
     {
         debug_assert!(pos < self.opaque.data.len());
 
-        let new_opaque = opaque::Decoder::new(self.opaque.data, pos);
+        let new_opaque = MemDecoder::new(self.opaque.data, pos);
         let old_opaque = mem::replace(&mut self.opaque, new_opaque);
         let r = f(self);
         self.opaque = old_opaque;
diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs
index 7fde9c0119b85..d583b45698ac4 100644
--- a/compiler/rustc_query_system/src/dep_graph/serialized.rs
+++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs
@@ -19,7 +19,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::profiling::SelfProfilerRef;
 use rustc_data_structures::sync::Lock;
 use rustc_index::vec::{Idx, IndexVec};
-use rustc_serialize::opaque::{self, FileEncodeResult, FileEncoder, IntEncodedWithFixedSize};
+use rustc_serialize::opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixedSize, MemDecoder};
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use smallvec::SmallVec;
 use std::convert::TryInto;
@@ -96,11 +96,11 @@ impl<K: DepKind> SerializedDepGraph<K> {
     }
 }
 
-impl<'a, K: DepKind + Decodable<opaque::Decoder<'a>>> Decodable<opaque::Decoder<'a>>
+impl<'a, K: DepKind + Decodable<MemDecoder<'a>>> Decodable<MemDecoder<'a>>
     for SerializedDepGraph<K>
 {
     #[instrument(level = "debug", skip(d))]
-    fn decode(d: &mut opaque::Decoder<'a>) -> SerializedDepGraph<K> {
+    fn decode(d: &mut MemDecoder<'a>) -> SerializedDepGraph<K> {
         let start_position = d.position();
 
         // The last 16 bytes are the node count and edge count.
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index d2306254e3142..460d505ba98b5 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -396,7 +396,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
 
         // Try to lookup name in more relaxed fashion for better error reporting.
         let ident = path.last().unwrap().ident;
-        let candidates = self
+        let mut candidates = self
             .r
             .lookup_import_candidates(ident, ns, &self.parent_scope, is_expected)
             .into_iter()
@@ -408,6 +408,18 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
             })
             .collect::<Vec<_>>();
         let crate_def_id = CRATE_DEF_ID.to_def_id();
+        // Try to filter out intrinsics candidates, as long as we have
+        // some other candidates to suggest.
+        let intrinsic_candidates: Vec<_> = candidates
+            .drain_filter(|sugg| {
+                let path = path_names_to_string(&sugg.path);
+                path.starts_with("core::intrinsics::") || path.starts_with("std::intrinsics::")
+            })
+            .collect();
+        if candidates.is_empty() {
+            // Put them back if we have no more candidates to suggest...
+            candidates.extend(intrinsic_candidates);
+        }
         if candidates.is_empty() && is_expected(Res::Def(DefKind::Enum, crate_def_id)) {
             let mut enum_candidates: Vec<_> = self
                 .r
diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs
index b2dbf937eb75a..f828c0b71063d 100644
--- a/compiler/rustc_serialize/src/opaque.rs
+++ b/compiler/rustc_serialize/src/opaque.rs
@@ -1,5 +1,5 @@
 use crate::leb128::{self, max_leb128_len};
-use crate::serialize::{self, Decoder as _, Encoder as _};
+use crate::serialize::{Decodable, Decoder, Encodable, Encoder};
 use std::convert::TryInto;
 use std::fs::File;
 use std::io::{self, Write};
@@ -11,13 +11,13 @@ use std::ptr;
 // Encoder
 // -----------------------------------------------------------------------------
 
-pub struct Encoder {
+pub struct MemEncoder {
     pub data: Vec<u8>,
 }
 
-impl Encoder {
-    pub fn new() -> Encoder {
-        Encoder { data: vec![] }
+impl MemEncoder {
+    pub fn new() -> MemEncoder {
+        MemEncoder { data: vec![] }
     }
 
     #[inline]
@@ -53,7 +53,7 @@ macro_rules! write_leb128 {
 /// [utf8]: https://en.wikipedia.org/w/index.php?title=UTF-8&oldid=1058865525#Codepage_layout
 const STR_SENTINEL: u8 = 0xC1;
 
-impl serialize::Encoder for Encoder {
+impl Encoder for MemEncoder {
     type Ok = Vec<u8>;
     type Err = !;
 
@@ -161,7 +161,7 @@ pub type FileEncodeResult = Result<usize, io::Error>;
 // `FileEncoder` encodes data to file via fixed-size buffer.
 //
 // When encoding large amounts of data to a file, using `FileEncoder` may be
-// preferred over using `Encoder` to encode to a `Vec`, and then writing the
+// preferred over using `MemEncoder` to encode to a `Vec`, and then writing the
 // `Vec` to file, as the latter uses as much memory as there is encoded data,
 // while the former uses the fixed amount of memory allocated to the buffer.
 // `FileEncoder` also has the advantage of not needing to reallocate as data
@@ -425,7 +425,7 @@ macro_rules! file_encoder_write_leb128 {
     }};
 }
 
-impl serialize::Encoder for FileEncoder {
+impl Encoder for FileEncoder {
     type Ok = usize;
     type Err = io::Error;
 
@@ -535,15 +535,15 @@ impl serialize::Encoder for FileEncoder {
 // Decoder
 // -----------------------------------------------------------------------------
 
-pub struct Decoder<'a> {
+pub struct MemDecoder<'a> {
     pub data: &'a [u8],
     position: usize,
 }
 
-impl<'a> Decoder<'a> {
+impl<'a> MemDecoder<'a> {
     #[inline]
-    pub fn new(data: &'a [u8], position: usize) -> Decoder<'a> {
-        Decoder { data, position }
+    pub fn new(data: &'a [u8], position: usize) -> MemDecoder<'a> {
+        MemDecoder { data, position }
     }
 
     #[inline]
@@ -566,7 +566,7 @@ macro_rules! read_leb128 {
     ($dec:expr, $fun:ident) => {{ leb128::$fun($dec.data, &mut $dec.position) }};
 }
 
-impl<'a> serialize::Decoder for Decoder<'a> {
+impl<'a> Decoder for MemDecoder<'a> {
     #[inline]
     fn read_u128(&mut self) -> u128 {
         read_leb128!(self, read_u128_leb128)
@@ -688,25 +688,25 @@ impl<'a> serialize::Decoder for Decoder<'a> {
 
 // Specialize encoding byte slices. This specialization also applies to encoding `Vec<u8>`s, etc.,
 // since the default implementations call `encode` on their slices internally.
-impl serialize::Encodable<Encoder> for [u8] {
-    fn encode(&self, e: &mut Encoder) {
-        serialize::Encoder::emit_usize(e, self.len());
+impl Encodable<MemEncoder> for [u8] {
+    fn encode(&self, e: &mut MemEncoder) {
+        Encoder::emit_usize(e, self.len());
         e.emit_raw_bytes(self);
     }
 }
 
-impl serialize::Encodable<FileEncoder> for [u8] {
+impl Encodable<FileEncoder> for [u8] {
     fn encode(&self, e: &mut FileEncoder) {
-        serialize::Encoder::emit_usize(e, self.len());
+        Encoder::emit_usize(e, self.len());
         e.emit_raw_bytes(self);
     }
 }
 
 // Specialize decoding `Vec<u8>`. This specialization also applies to decoding `Box<[u8]>`s, etc.,
 // since the default implementations call `decode` to produce a `Vec<u8>` internally.
-impl<'a> serialize::Decodable<Decoder<'a>> for Vec<u8> {
-    fn decode(d: &mut Decoder<'a>) -> Self {
-        let len = serialize::Decoder::read_usize(d);
+impl<'a> Decodable<MemDecoder<'a>> for Vec<u8> {
+    fn decode(d: &mut MemDecoder<'a>) -> Self {
+        let len = Decoder::read_usize(d);
         d.read_raw_bytes(len).to_owned()
     }
 }
@@ -718,9 +718,9 @@ impl IntEncodedWithFixedSize {
     pub const ENCODED_SIZE: usize = 8;
 }
 
-impl serialize::Encodable<Encoder> for IntEncodedWithFixedSize {
+impl Encodable<MemEncoder> for IntEncodedWithFixedSize {
     #[inline]
-    fn encode(&self, e: &mut Encoder) {
+    fn encode(&self, e: &mut MemEncoder) {
         let _start_pos = e.position();
         e.emit_raw_bytes(&self.0.to_le_bytes());
         let _end_pos = e.position();
@@ -728,7 +728,7 @@ impl serialize::Encodable<Encoder> for IntEncodedWithFixedSize {
     }
 }
 
-impl serialize::Encodable<FileEncoder> for IntEncodedWithFixedSize {
+impl Encodable<FileEncoder> for IntEncodedWithFixedSize {
     #[inline]
     fn encode(&self, e: &mut FileEncoder) {
         let _start_pos = e.position();
@@ -738,9 +738,9 @@ impl serialize::Encodable<FileEncoder> for IntEncodedWithFixedSize {
     }
 }
 
-impl<'a> serialize::Decodable<Decoder<'a>> for IntEncodedWithFixedSize {
+impl<'a> Decodable<MemDecoder<'a>> for IntEncodedWithFixedSize {
     #[inline]
-    fn decode(decoder: &mut Decoder<'a>) -> IntEncodedWithFixedSize {
+    fn decode(decoder: &mut MemDecoder<'a>) -> IntEncodedWithFixedSize {
         let _start_pos = decoder.position();
         let bytes = decoder.read_raw_bytes(IntEncodedWithFixedSize::ENCODED_SIZE);
         let value = u64::from_le_bytes(bytes.try_into().unwrap());
diff --git a/compiler/rustc_serialize/tests/opaque.rs b/compiler/rustc_serialize/tests/opaque.rs
index 703b7f5e7a5f4..4eafb6fabde8e 100644
--- a/compiler/rustc_serialize/tests/opaque.rs
+++ b/compiler/rustc_serialize/tests/opaque.rs
@@ -1,7 +1,7 @@
 #![allow(rustc::internal)]
 
 use rustc_macros::{Decodable, Encodable};
-use rustc_serialize::opaque::{Decoder, Encoder};
+use rustc_serialize::opaque::{MemDecoder, MemEncoder};
 use rustc_serialize::{Decodable, Encodable, Encoder as EncoderTrait};
 use std::fmt::Debug;
 
@@ -28,16 +28,18 @@ struct Struct {
     q: Option<u32>,
 }
 
-fn check_round_trip<T: Encodable<Encoder> + for<'a> Decodable<Decoder<'a>> + PartialEq + Debug>(
+fn check_round_trip<
+    T: Encodable<MemEncoder> + for<'a> Decodable<MemDecoder<'a>> + PartialEq + Debug,
+>(
     values: Vec<T>,
 ) {
-    let mut encoder = Encoder::new();
+    let mut encoder = MemEncoder::new();
     for value in &values {
         Encodable::encode(value, &mut encoder);
     }
 
     let data = encoder.finish().unwrap();
-    let mut decoder = Decoder::new(&data[..], 0);
+    let mut decoder = MemDecoder::new(&data[..], 0);
 
     for value in values {
         let decoded = Decodable::decode(&mut decoder);
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index d7e2f621065f0..5d3d56b1e6699 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -1911,13 +1911,13 @@ impl_pos! {
     pub struct CharPos(pub usize);
 }
 
-impl<S: rustc_serialize::Encoder> Encodable<S> for BytePos {
+impl<S: Encoder> Encodable<S> for BytePos {
     fn encode(&self, s: &mut S) {
         s.emit_u32(self.0);
     }
 }
 
-impl<D: rustc_serialize::Decoder> Decodable<D> for BytePos {
+impl<D: Decoder> Decodable<D> for BytePos {
     fn decode(d: &mut D) -> BytePos {
         BytePos(d.read_u32())
     }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 6547ec493c862..c35a11ff7619e 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -157,6 +157,7 @@ symbols! {
         C,
         CStr,
         CString,
+        Capture,
         Center,
         Clone,
         Continue,
@@ -267,6 +268,8 @@ symbols! {
         ToOwned,
         ToString,
         Try,
+        TryCaptureGeneric,
+        TryCapturePrintable,
         TryFrom,
         TryInto,
         Ty,
@@ -276,6 +279,7 @@ symbols! {
         UnsafeArg,
         Vec,
         VecDeque,
+        Wrapper,
         Yield,
         _DECLS,
         _Self,
@@ -358,6 +362,7 @@ symbols! {
         assert_receiver_is_total_eq,
         assert_uninit_valid,
         assert_zero_valid,
+        asserting,
         associated_const_equality,
         associated_consts,
         associated_type_bounds,
@@ -1437,6 +1442,7 @@ symbols! {
         truncf32,
         truncf64,
         try_blocks,
+        try_capture,
         try_from,
         try_into,
         try_trait_v2,
@@ -1499,6 +1505,7 @@ symbols! {
         unsized_tuple_coercion,
         unstable,
         untagged_unions,
+        unused_imports,
         unused_qualifications,
         unwind,
         unwind_attributes,
diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs
index a6a0d02c8ba99..9407218439993 100644
--- a/compiler/rustc_type_ir/src/sty.rs
+++ b/compiler/rustc_type_ir/src/sty.rs
@@ -14,7 +14,7 @@ use crate::UintTy;
 use self::TyKind::*;
 
 use rustc_data_structures::stable_hasher::HashStable;
-use rustc_serialize::{Decodable, Encodable};
+use rustc_serialize::{Decodable, Decoder, Encodable};
 
 /// Defines the kinds of types used by the type system.
 ///
@@ -833,56 +833,34 @@ where
     I::AllocId: Decodable<D>,
 {
     fn decode(d: &mut D) -> Self {
-        match rustc_serialize::Decoder::read_usize(d) {
+        match Decoder::read_usize(d) {
             0 => Bool,
             1 => Char,
-            2 => Int(rustc_serialize::Decodable::decode(d)),
-            3 => Uint(rustc_serialize::Decodable::decode(d)),
-            4 => Float(rustc_serialize::Decodable::decode(d)),
-            5 => Adt(rustc_serialize::Decodable::decode(d), rustc_serialize::Decodable::decode(d)),
-            6 => Foreign(rustc_serialize::Decodable::decode(d)),
+            2 => Int(Decodable::decode(d)),
+            3 => Uint(Decodable::decode(d)),
+            4 => Float(Decodable::decode(d)),
+            5 => Adt(Decodable::decode(d), Decodable::decode(d)),
+            6 => Foreign(Decodable::decode(d)),
             7 => Str,
-            8 => {
-                Array(rustc_serialize::Decodable::decode(d), rustc_serialize::Decodable::decode(d))
-            }
-            9 => Slice(rustc_serialize::Decodable::decode(d)),
-            10 => RawPtr(rustc_serialize::Decodable::decode(d)),
-            11 => Ref(
-                rustc_serialize::Decodable::decode(d),
-                rustc_serialize::Decodable::decode(d),
-                rustc_serialize::Decodable::decode(d),
-            ),
-            12 => {
-                FnDef(rustc_serialize::Decodable::decode(d), rustc_serialize::Decodable::decode(d))
-            }
-            13 => FnPtr(rustc_serialize::Decodable::decode(d)),
-            14 => Dynamic(
-                rustc_serialize::Decodable::decode(d),
-                rustc_serialize::Decodable::decode(d),
-            ),
-            15 => Closure(
-                rustc_serialize::Decodable::decode(d),
-                rustc_serialize::Decodable::decode(d),
-            ),
-            16 => Generator(
-                rustc_serialize::Decodable::decode(d),
-                rustc_serialize::Decodable::decode(d),
-                rustc_serialize::Decodable::decode(d),
-            ),
-            17 => GeneratorWitness(rustc_serialize::Decodable::decode(d)),
+            8 => Array(Decodable::decode(d), Decodable::decode(d)),
+            9 => Slice(Decodable::decode(d)),
+            10 => RawPtr(Decodable::decode(d)),
+            11 => Ref(Decodable::decode(d), Decodable::decode(d), Decodable::decode(d)),
+            12 => FnDef(Decodable::decode(d), Decodable::decode(d)),
+            13 => FnPtr(Decodable::decode(d)),
+            14 => Dynamic(Decodable::decode(d), Decodable::decode(d)),
+            15 => Closure(Decodable::decode(d), Decodable::decode(d)),
+            16 => Generator(Decodable::decode(d), Decodable::decode(d), Decodable::decode(d)),
+            17 => GeneratorWitness(Decodable::decode(d)),
             18 => Never,
-            19 => Tuple(rustc_serialize::Decodable::decode(d)),
-            20 => Projection(rustc_serialize::Decodable::decode(d)),
-            21 => {
-                Opaque(rustc_serialize::Decodable::decode(d), rustc_serialize::Decodable::decode(d))
-            }
-            22 => Param(rustc_serialize::Decodable::decode(d)),
-            23 => {
-                Bound(rustc_serialize::Decodable::decode(d), rustc_serialize::Decodable::decode(d))
-            }
-            24 => Placeholder(rustc_serialize::Decodable::decode(d)),
-            25 => Infer(rustc_serialize::Decodable::decode(d)),
-            26 => Error(rustc_serialize::Decodable::decode(d)),
+            19 => Tuple(Decodable::decode(d)),
+            20 => Projection(Decodable::decode(d)),
+            21 => Opaque(Decodable::decode(d), Decodable::decode(d)),
+            22 => Param(Decodable::decode(d)),
+            23 => Bound(Decodable::decode(d), Decodable::decode(d)),
+            24 => Placeholder(Decodable::decode(d)),
+            25 => Infer(Decodable::decode(d)),
+            26 => Error(Decodable::decode(d)),
             _ => panic!(
                 "{}",
                 format!(
diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs
index 242f926967c93..da09ae9dd0662 100644
--- a/src/librustdoc/scrape_examples.rs
+++ b/src/librustdoc/scrape_examples.rs
@@ -17,7 +17,7 @@ use rustc_middle::hir::map::Map;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_serialize::{
-    opaque::{Decoder, FileEncoder},
+    opaque::{FileEncoder, MemDecoder},
     Decodable, Encodable, Encoder,
 };
 use rustc_session::getopts;
@@ -336,7 +336,7 @@ pub(crate) fn load_call_locations(
         let mut all_calls: AllCallLocations = FxHashMap::default();
         for path in with_examples {
             let bytes = fs::read(&path).map_err(|e| format!("{} (for path {})", e, path))?;
-            let mut decoder = Decoder::new(&bytes, 0);
+            let mut decoder = MemDecoder::new(&bytes, 0);
             let calls = AllCallLocations::decode(&mut decoder);
 
             for (function, fn_calls) in calls.into_iter() {
diff --git a/src/test/ui-fulldeps/deriving-encodable-decodable-box.rs b/src/test/ui-fulldeps/deriving-encodable-decodable-box.rs
index a09deeec4f182..4ad4ef60a5294 100644
--- a/src/test/ui-fulldeps/deriving-encodable-decodable-box.rs
+++ b/src/test/ui-fulldeps/deriving-encodable-decodable-box.rs
@@ -7,7 +7,7 @@ extern crate rustc_macros;
 extern crate rustc_serialize;
 
 use rustc_macros::{Decodable, Encodable};
-use rustc_serialize::opaque;
+use rustc_serialize::opaque::{MemDecoder, MemEncoder};
 use rustc_serialize::{Decodable, Encodable, Encoder};
 
 #[derive(Encodable, Decodable)]
@@ -18,11 +18,11 @@ struct A {
 fn main() {
     let obj = A { foo: Box::new([true, false]) };
 
-    let mut encoder = opaque::Encoder::new();
+    let mut encoder = MemEncoder::new();
     obj.encode(&mut encoder);
     let data = encoder.finish().unwrap();
 
-    let mut decoder = opaque::Decoder::new(&data, 0);
+    let mut decoder = MemDecoder::new(&data, 0);
     let obj2 = A::decode(&mut decoder);
 
     assert_eq!(obj.foo, obj2.foo);
diff --git a/src/test/ui-fulldeps/deriving-encodable-decodable-cell-refcell.rs b/src/test/ui-fulldeps/deriving-encodable-decodable-cell-refcell.rs
index 9b6fb0e580621..3ac3abae692af 100644
--- a/src/test/ui-fulldeps/deriving-encodable-decodable-cell-refcell.rs
+++ b/src/test/ui-fulldeps/deriving-encodable-decodable-cell-refcell.rs
@@ -9,7 +9,7 @@ extern crate rustc_macros;
 extern crate rustc_serialize;
 
 use rustc_macros::{Decodable, Encodable};
-use rustc_serialize::opaque;
+use rustc_serialize::opaque::{MemDecoder, MemEncoder};
 use rustc_serialize::{Decodable, Encodable, Encoder};
 use std::cell::{Cell, RefCell};
 
@@ -27,11 +27,11 @@ struct B {
 fn main() {
     let obj = B { foo: Cell::new(true), bar: RefCell::new(A { baz: 2 }) };
 
-    let mut encoder = opaque::Encoder::new();
+    let mut encoder = MemEncoder::new();
     obj.encode(&mut encoder);
     let data = encoder.finish().unwrap();
 
-    let mut decoder = opaque::Decoder::new(&data, 0);
+    let mut decoder = MemDecoder::new(&data, 0);
     let obj2 = B::decode(&mut decoder);
 
     assert_eq!(obj.foo.get(), obj2.foo.get());
diff --git a/src/test/ui-fulldeps/issue-14021.rs b/src/test/ui-fulldeps/issue-14021.rs
index 4241456367e46..b7b6e1b860d35 100644
--- a/src/test/ui-fulldeps/issue-14021.rs
+++ b/src/test/ui-fulldeps/issue-14021.rs
@@ -8,7 +8,7 @@ extern crate rustc_macros;
 extern crate rustc_serialize;
 
 use rustc_macros::{Decodable, Encodable};
-use rustc_serialize::opaque;
+use rustc_serialize::opaque::{MemDecoder, MemEncoder};
 use rustc_serialize::{Decodable, Encodable, Encoder};
 
 #[derive(Encodable, Decodable, PartialEq, Debug)]
@@ -17,11 +17,11 @@ struct UnitLikeStruct;
 pub fn main() {
     let obj = UnitLikeStruct;
 
-    let mut encoder = opaque::Encoder::new();
+    let mut encoder = MemEncoder::new();
     obj.encode(&mut encoder);
     let data = encoder.finish().unwrap();
 
-    let mut decoder = opaque::Decoder::new(&data, 0);
+    let mut decoder = MemDecoder::new(&data, 0);
     let obj2 = UnitLikeStruct::decode(&mut decoder);
 
     assert_eq!(obj, obj2);
diff --git a/src/test/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr b/src/test/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr
index 609a5efd46fee..3ccc14bba5422 100644
--- a/src/test/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr
+++ b/src/test/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr
@@ -25,10 +25,16 @@ LL |         println!("Then when?");
 error: expected `{`, found `;`
   --> $DIR/issue-46836-identifier-not-instead-of-negation.rs:20:31
    |
-LL |     if not  // lack of braces is [sic]
-   |     -- this `if` expression has a condition, but no block
 LL |         println!("Then when?");
    |                               ^ expected `{`
+   |
+note: the `if` expression is missing a block after this condition
+  --> $DIR/issue-46836-identifier-not-instead-of-negation.rs:19:8
+   |
+LL |       if not  // lack of braces is [sic]
+   |  ________^
+LL | |         println!("Then when?");
+   | |______________________________^
 
 error: unexpected `2` after identifier
   --> $DIR/issue-46836-identifier-not-instead-of-negation.rs:26:24
diff --git a/src/test/ui/expr/if/if-without-block.rs b/src/test/ui/expr/if/if-without-block.rs
index 8a4c59f32613d..5add9dfda4b9d 100644
--- a/src/test/ui/expr/if/if-without-block.rs
+++ b/src/test/ui/expr/if/if-without-block.rs
@@ -1,9 +1,7 @@
 fn main() {
     let n = 1;
     if 5 == {
-    //~^ NOTE this `if` expression has a condition, but no block
+    //~^ ERROR this `if` expression is missing a block after the condition
         println!("five");
     }
 }
-//~^ ERROR expected `{`, found `}`
-//~| NOTE expected `{`
diff --git a/src/test/ui/expr/if/if-without-block.stderr b/src/test/ui/expr/if/if-without-block.stderr
index d3f6ca07617f4..2d1ee04ce09a8 100644
--- a/src/test/ui/expr/if/if-without-block.stderr
+++ b/src/test/ui/expr/if/if-without-block.stderr
@@ -1,17 +1,14 @@
-error: expected `{`, found `}`
-  --> $DIR/if-without-block.rs:7:1
+error: this `if` expression is missing a block after the condition
+  --> $DIR/if-without-block.rs:3:5
    |
 LL |     if 5 == {
-   |     -- this `if` expression has a condition, but no block
-...
-LL | }
-   | ^ expected `{`
+   |     ^^
    |
-help: maybe you forgot the right operand of the condition?
-  --> $DIR/if-without-block.rs:3:10
+help: this binary operation is possibly unfinished
+  --> $DIR/if-without-block.rs:3:8
    |
 LL |     if 5 == {
-   |          ^^
+   |        ^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-39848.stderr b/src/test/ui/issues/issue-39848.stderr
index feabe3814dcf9..387ef0776ff4a 100644
--- a/src/test/ui/issues/issue-39848.stderr
+++ b/src/test/ui/issues/issue-39848.stderr
@@ -2,13 +2,19 @@ error: expected `{`, found `foo`
   --> $DIR/issue-39848.rs:3:21
    |
 LL |         if $tgt.has_$field() {}
-   |         --          ^^^^^^ expected `{`
-   |         |
-   |         this `if` expression has a condition, but no block
+   |                     ^^^^^^ expected `{`
 ...
 LL |     get_opt!(bar, foo);
    |     ------------------ in this macro invocation
    |
+note: the `if` expression is missing a block after this condition
+  --> $DIR/issue-39848.rs:3:12
+   |
+LL |         if $tgt.has_$field() {}
+   |            ^^^^^^^^^
+...
+LL |     get_opt!(bar, foo);
+   |     ------------------ in this macro invocation
    = note: this error originates in the macro `get_opt` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: try placing this code inside a block
    |
diff --git a/src/test/ui/macros/assert-trailing-junk.rs b/src/test/ui/macros/assert-trailing-junk.rs
index cd7faf9bf8bfb..da725e19e2ada 100644
--- a/src/test/ui/macros/assert-trailing-junk.rs
+++ b/src/test/ui/macros/assert-trailing-junk.rs
@@ -1,3 +1,6 @@
+// revisions: with-generic-asset without-generic-asset
+// [with-generic-asset] compile-flags: --cfg feature="generic_assert"
+
 // Ensure assert macro does not ignore trailing garbage.
 //
 // See https://github.com/rust-lang/rust/issues/60024 for details.
diff --git a/src/test/ui/macros/assert-trailing-junk.stderr b/src/test/ui/macros/assert-trailing-junk.with-generic-asset.stderr
similarity index 86%
rename from src/test/ui/macros/assert-trailing-junk.stderr
rename to src/test/ui/macros/assert-trailing-junk.with-generic-asset.stderr
index eb001429c5522..09dd16a0b0d89 100644
--- a/src/test/ui/macros/assert-trailing-junk.stderr
+++ b/src/test/ui/macros/assert-trailing-junk.with-generic-asset.stderr
@@ -1,17 +1,17 @@
 error: expected one of `,`, `.`, `?`, or an operator, found `some`
-  --> $DIR/assert-trailing-junk.rs:6:18
+  --> $DIR/assert-trailing-junk.rs:9:18
    |
 LL |     assert!(true some extra junk, "whatever");
    |                  ^^^^ expected one of `,`, `.`, `?`, or an operator
 
 error: expected one of `,`, `.`, `?`, or an operator, found `some`
-  --> $DIR/assert-trailing-junk.rs:9:18
+  --> $DIR/assert-trailing-junk.rs:12:18
    |
 LL |     assert!(true some extra junk);
    |                  ^^^^ expected one of `,`, `.`, `?`, or an operator
 
 error: no rules expected the token `blah`
-  --> $DIR/assert-trailing-junk.rs:12:30
+  --> $DIR/assert-trailing-junk.rs:15:30
    |
 LL |     assert!(true, "whatever" blah);
    |                             -^^^^ no rules expected this token in macro call
@@ -19,7 +19,7 @@ LL |     assert!(true, "whatever" blah);
    |                             help: missing comma here
 
 error: unexpected string literal
-  --> $DIR/assert-trailing-junk.rs:15:18
+  --> $DIR/assert-trailing-junk.rs:18:18
    |
 LL |     assert!(true "whatever" blah);
    |                 -^^^^^^^^^^
@@ -27,7 +27,7 @@ LL |     assert!(true "whatever" blah);
    |                 help: try adding a comma
 
 error: no rules expected the token `blah`
-  --> $DIR/assert-trailing-junk.rs:15:29
+  --> $DIR/assert-trailing-junk.rs:18:29
    |
 LL |     assert!(true "whatever" blah);
    |                            -^^^^ no rules expected this token in macro call
@@ -35,7 +35,7 @@ LL |     assert!(true "whatever" blah);
    |                            help: missing comma here
 
 error: macro requires an expression as an argument
-  --> $DIR/assert-trailing-junk.rs:19:5
+  --> $DIR/assert-trailing-junk.rs:22:5
    |
 LL |     assert!(true;);
    |     ^^^^^^^^^^^^-^
@@ -43,7 +43,7 @@ LL |     assert!(true;);
    |                 help: try removing semicolon
 
 error: unexpected string literal
-  --> $DIR/assert-trailing-junk.rs:22:27
+  --> $DIR/assert-trailing-junk.rs:25:27
    |
 LL |     assert!(false || true "error message");
    |                          -^^^^^^^^^^^^^^^
diff --git a/src/test/ui/macros/assert-trailing-junk.without-generic-asset.stderr b/src/test/ui/macros/assert-trailing-junk.without-generic-asset.stderr
new file mode 100644
index 0000000000000..09dd16a0b0d89
--- /dev/null
+++ b/src/test/ui/macros/assert-trailing-junk.without-generic-asset.stderr
@@ -0,0 +1,54 @@
+error: expected one of `,`, `.`, `?`, or an operator, found `some`
+  --> $DIR/assert-trailing-junk.rs:9:18
+   |
+LL |     assert!(true some extra junk, "whatever");
+   |                  ^^^^ expected one of `,`, `.`, `?`, or an operator
+
+error: expected one of `,`, `.`, `?`, or an operator, found `some`
+  --> $DIR/assert-trailing-junk.rs:12:18
+   |
+LL |     assert!(true some extra junk);
+   |                  ^^^^ expected one of `,`, `.`, `?`, or an operator
+
+error: no rules expected the token `blah`
+  --> $DIR/assert-trailing-junk.rs:15:30
+   |
+LL |     assert!(true, "whatever" blah);
+   |                             -^^^^ no rules expected this token in macro call
+   |                             |
+   |                             help: missing comma here
+
+error: unexpected string literal
+  --> $DIR/assert-trailing-junk.rs:18:18
+   |
+LL |     assert!(true "whatever" blah);
+   |                 -^^^^^^^^^^
+   |                 |
+   |                 help: try adding a comma
+
+error: no rules expected the token `blah`
+  --> $DIR/assert-trailing-junk.rs:18:29
+   |
+LL |     assert!(true "whatever" blah);
+   |                            -^^^^ no rules expected this token in macro call
+   |                            |
+   |                            help: missing comma here
+
+error: macro requires an expression as an argument
+  --> $DIR/assert-trailing-junk.rs:22:5
+   |
+LL |     assert!(true;);
+   |     ^^^^^^^^^^^^-^
+   |                 |
+   |                 help: try removing semicolon
+
+error: unexpected string literal
+  --> $DIR/assert-trailing-junk.rs:25:27
+   |
+LL |     assert!(false || true "error message");
+   |                          -^^^^^^^^^^^^^^^
+   |                          |
+   |                          help: try adding a comma
+
+error: aborting due to 7 previous errors
+
diff --git a/src/test/ui/macros/assert.rs b/src/test/ui/macros/assert.rs
index 71b0dbb19e262..a314db907b8a2 100644
--- a/src/test/ui/macros/assert.rs
+++ b/src/test/ui/macros/assert.rs
@@ -1,3 +1,6 @@
+// revisions: with-generic-asset without-generic-asset
+// [with-generic-asset] compile-flags: --cfg feature="generic_assert"
+
 fn main() {
     assert!();  //~ ERROR requires a boolean expression
     assert!(struct); //~ ERROR expected expression
diff --git a/src/test/ui/macros/assert.stderr b/src/test/ui/macros/assert.with-generic-asset.stderr
similarity index 87%
rename from src/test/ui/macros/assert.stderr
rename to src/test/ui/macros/assert.with-generic-asset.stderr
index 57e5c77a56692..51d8f28a35c39 100644
--- a/src/test/ui/macros/assert.stderr
+++ b/src/test/ui/macros/assert.with-generic-asset.stderr
@@ -1,17 +1,17 @@
 error: macro requires a boolean expression as an argument
-  --> $DIR/assert.rs:2:5
+  --> $DIR/assert.rs:5:5
    |
 LL |     assert!();
    |     ^^^^^^^^^ boolean expression required
 
 error: expected expression, found keyword `struct`
-  --> $DIR/assert.rs:3:13
+  --> $DIR/assert.rs:6:13
    |
 LL |     assert!(struct);
    |             ^^^^^^ expected expression
 
 error: macro requires a boolean expression as an argument
-  --> $DIR/assert.rs:4:5
+  --> $DIR/assert.rs:7:5
    |
 LL |     debug_assert!();
    |     ^^^^^^^^^^^^^^^ boolean expression required
@@ -19,7 +19,7 @@ LL |     debug_assert!();
    = note: this error originates in the macro `debug_assert` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: expected expression, found keyword `struct`
-  --> $DIR/assert.rs:5:19
+  --> $DIR/assert.rs:8:19
    |
 LL |     debug_assert!(struct);
    |                   ^^^^^^ expected expression
diff --git a/src/test/ui/macros/assert.without-generic-asset.stderr b/src/test/ui/macros/assert.without-generic-asset.stderr
new file mode 100644
index 0000000000000..51d8f28a35c39
--- /dev/null
+++ b/src/test/ui/macros/assert.without-generic-asset.stderr
@@ -0,0 +1,28 @@
+error: macro requires a boolean expression as an argument
+  --> $DIR/assert.rs:5:5
+   |
+LL |     assert!();
+   |     ^^^^^^^^^ boolean expression required
+
+error: expected expression, found keyword `struct`
+  --> $DIR/assert.rs:6:13
+   |
+LL |     assert!(struct);
+   |             ^^^^^^ expected expression
+
+error: macro requires a boolean expression as an argument
+  --> $DIR/assert.rs:7:5
+   |
+LL |     debug_assert!();
+   |     ^^^^^^^^^^^^^^^ boolean expression required
+   |
+   = note: this error originates in the macro `debug_assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: expected expression, found keyword `struct`
+  --> $DIR/assert.rs:8:19
+   |
+LL |     debug_assert!(struct);
+   |                   ^^^^^^ expected expression
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/missing/missing-block-hint.stderr b/src/test/ui/missing/missing-block-hint.stderr
index 148e214e58ec1..16954223a4521 100644
--- a/src/test/ui/missing/missing-block-hint.stderr
+++ b/src/test/ui/missing/missing-block-hint.stderr
@@ -2,18 +2,25 @@ error: expected `{`, found `=>`
   --> $DIR/missing-block-hint.rs:3:18
    |
 LL |         if (foo) => {}
-   |         --       ^^ expected `{`
-   |         |
-   |         this `if` expression has a condition, but no block
+   |                  ^^ expected `{`
+   |
+note: the `if` expression is missing a block after this condition
+  --> $DIR/missing-block-hint.rs:3:12
+   |
+LL |         if (foo) => {}
+   |            ^^^^^
 
 error: expected `{`, found `bar`
   --> $DIR/missing-block-hint.rs:7:13
    |
-LL |         if (foo)
-   |         -- this `if` expression has a condition, but no block
 LL |             bar;
    |             ^^^ expected `{`
    |
+note: the `if` expression is missing a block after this condition
+  --> $DIR/missing-block-hint.rs:6:12
+   |
+LL |         if (foo)
+   |            ^^^^^
 help: try placing this code inside a block
    |
 LL |             { bar; }
diff --git a/src/test/ui/parser/bad-if-statements.rs b/src/test/ui/parser/bad-if-statements.rs
new file mode 100644
index 0000000000000..2c501e3a5b399
--- /dev/null
+++ b/src/test/ui/parser/bad-if-statements.rs
@@ -0,0 +1,38 @@
+fn a() {
+    if {}
+    //~^ ERROR missing condition for `if` expression
+}
+
+fn b() {
+    if true && {}
+    //~^ ERROR this `if` expression is missing a block after the condition
+}
+
+fn c() {
+    let x = {};
+    if true x
+    //~^ ERROR expected `{`, found `x`
+}
+
+fn a2() {
+    if {} else {}
+    //~^ ERROR missing condition for `if` expression
+}
+
+fn b2() {
+    if true && {} else {}
+    //~^ ERROR this `if` expression is missing a block after the condition
+}
+
+fn c2() {
+    let x = {};
+    if true x else {}
+    //~^ ERROR expected `{`, found `x`
+}
+
+fn d() {
+    if true else {}
+    //~^ ERROR this `if` expression is missing a block after the condition
+}
+
+fn main() {}
diff --git a/src/test/ui/parser/bad-if-statements.stderr b/src/test/ui/parser/bad-if-statements.stderr
new file mode 100644
index 0000000000000..ee839db645509
--- /dev/null
+++ b/src/test/ui/parser/bad-if-statements.stderr
@@ -0,0 +1,86 @@
+error: missing condition for `if` expression
+  --> $DIR/bad-if-statements.rs:2:7
+   |
+LL |     if {}
+   |       ^- if this block is the condition of the `if` expression, then it must be followed by another block
+   |       |
+   |       expected condition here
+
+error: this `if` expression is missing a block after the condition
+  --> $DIR/bad-if-statements.rs:7:5
+   |
+LL |     if true && {}
+   |     ^^
+   |
+help: this binary operation is possibly unfinished
+  --> $DIR/bad-if-statements.rs:7:8
+   |
+LL |     if true && {}
+   |        ^^^^^^^
+
+error: expected `{`, found `x`
+  --> $DIR/bad-if-statements.rs:13:13
+   |
+LL |     if true x
+   |             ^ expected `{`
+   |
+note: the `if` expression is missing a block after this condition
+  --> $DIR/bad-if-statements.rs:13:8
+   |
+LL |     if true x
+   |        ^^^^
+help: try placing this code inside a block
+   |
+LL |     if true { x }
+   |             +   +
+
+error: missing condition for `if` expression
+  --> $DIR/bad-if-statements.rs:18:7
+   |
+LL |     if {} else {}
+   |       ^- if this block is the condition of the `if` expression, then it must be followed by another block
+   |       |
+   |       expected condition here
+
+error: this `if` expression is missing a block after the condition
+  --> $DIR/bad-if-statements.rs:23:5
+   |
+LL |     if true && {} else {}
+   |     ^^
+   |
+help: this binary operation is possibly unfinished
+  --> $DIR/bad-if-statements.rs:23:8
+   |
+LL |     if true && {} else {}
+   |        ^^^^^^^
+
+error: expected `{`, found `x`
+  --> $DIR/bad-if-statements.rs:29:13
+   |
+LL |     if true x else {}
+   |             ^ expected `{`
+   |
+note: the `if` expression is missing a block after this condition
+  --> $DIR/bad-if-statements.rs:29:8
+   |
+LL |     if true x else {}
+   |        ^^^^
+help: try placing this code inside a block
+   |
+LL |     if true { x } else {}
+   |             +   +
+
+error: this `if` expression is missing a block after the condition
+  --> $DIR/bad-if-statements.rs:34:5
+   |
+LL |     if true else {}
+   |     ^^
+   |
+help: add a block here
+  --> $DIR/bad-if-statements.rs:34:12
+   |
+LL |     if true else {}
+   |            ^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/test/ui/parser/if-block-unreachable-expr.rs b/src/test/ui/parser/if-block-unreachable-expr.rs
new file mode 100644
index 0000000000000..4063a33708402
--- /dev/null
+++ b/src/test/ui/parser/if-block-unreachable-expr.rs
@@ -0,0 +1,8 @@
+// check-pass
+
+// This regressed from 1.20 -> 1.21 -- the condition is unreachable,
+// but it's still an expression, and should parse fine.
+
+fn main() {
+    if { if true { return; } else { return; }; } {}
+}
diff --git a/src/test/ui/parser/issue-61858.stderr b/src/test/ui/parser/issue-61858.stderr
index 8b95d9c6ae48b..03f51c6e3a8f4 100644
--- a/src/test/ui/parser/issue-61858.stderr
+++ b/src/test/ui/parser/issue-61858.stderr
@@ -2,9 +2,13 @@ error: expected `{`, found `)`
   --> $DIR/issue-61858.rs:2:15
    |
 LL |     (if foobar)
-   |      --       ^ expected `{`
-   |      |
-   |      this `if` expression has a condition, but no block
+   |               ^ expected `{`
+   |
+note: the `if` expression is missing a block after this condition
+  --> $DIR/issue-61858.rs:2:9
+   |
+LL |     (if foobar)
+   |         ^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/issue-68091-unicode-ident-after-if.rs b/src/test/ui/parser/issue-68091-unicode-ident-after-if.rs
index 00f90cc73b344..57d36feb37b13 100644
--- a/src/test/ui/parser/issue-68091-unicode-ident-after-if.rs
+++ b/src/test/ui/parser/issue-68091-unicode-ident-after-if.rs
@@ -1,7 +1,8 @@
 macro_rules! x {
     ($($c:tt)*) => {
-        $($c)ö* {} //~ ERROR missing condition for `if` expression
-    };             //~| ERROR mismatched types
+        $($c)ö* {}
+        //~^ ERROR missing condition for `if` expression
+    };
 }
 
 fn main() {
diff --git a/src/test/ui/parser/issue-68091-unicode-ident-after-if.stderr b/src/test/ui/parser/issue-68091-unicode-ident-after-if.stderr
index cdd4c670500f0..6674b924e9c4b 100644
--- a/src/test/ui/parser/issue-68091-unicode-ident-after-if.stderr
+++ b/src/test/ui/parser/issue-68091-unicode-ident-after-if.stderr
@@ -2,19 +2,9 @@ error: missing condition for `if` expression
   --> $DIR/issue-68091-unicode-ident-after-if.rs:3:14
    |
 LL |         $($c)ö* {}
-   |              ^ expected if condition here
+   |              ^  - if this block is the condition of the `if` expression, then it must be followed by another block
+   |              |
+   |              expected condition here
 
-error[E0308]: mismatched types
-  --> $DIR/issue-68091-unicode-ident-after-if.rs:3:17
-   |
-LL |         $($c)ö* {}
-   |                 ^^ expected `bool`, found `()`
-...
-LL |     x!(if);
-   |     ------ in this macro invocation
-   |
-   = note: this error originates in the macro `x` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/issue-91421.rs b/src/test/ui/parser/issue-91421.rs
index 9959df5663837..8bba27f372439 100644
--- a/src/test/ui/parser/issue-91421.rs
+++ b/src/test/ui/parser/issue-91421.rs
@@ -2,9 +2,8 @@
 
 fn main() {
     let value = if true && {
-    //~^ ERROR: this `if` expression has a condition, but no block
-    //~| HELP: maybe you forgot the right operand of the condition?
+    //~^ ERROR: this `if` expression is missing a block after the condition
+    //~| HELP: this binary operation is possibly unfinished
         3
-        //~^ ERROR: mismatched types [E0308]
     } else { 4 };
 }
diff --git a/src/test/ui/parser/issue-91421.stderr b/src/test/ui/parser/issue-91421.stderr
index 04284d5e3b2f7..2d9652051dd56 100644
--- a/src/test/ui/parser/issue-91421.stderr
+++ b/src/test/ui/parser/issue-91421.stderr
@@ -1,21 +1,14 @@
-error: this `if` expression has a condition, but no block
+error: this `if` expression is missing a block after the condition
   --> $DIR/issue-91421.rs:4:17
    |
 LL |     let value = if true && {
    |                 ^^
    |
-help: maybe you forgot the right operand of the condition?
-  --> $DIR/issue-91421.rs:4:25
+help: this binary operation is possibly unfinished
+  --> $DIR/issue-91421.rs:4:20
    |
 LL |     let value = if true && {
-   |                         ^^
+   |                    ^^^^^^^
 
-error[E0308]: mismatched types
-  --> $DIR/issue-91421.rs:7:9
-   |
-LL |         3
-   |         ^ expected `bool`, found integer
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/issues/issue-13483.rs b/src/test/ui/parser/issues/issue-13483.rs
index a2fd9264b1530..4e32fcab3f098 100644
--- a/src/test/ui/parser/issues/issue-13483.rs
+++ b/src/test/ui/parser/issues/issue-13483.rs
@@ -1,15 +1,15 @@
 fn main() {
     if true {
-    } else if { //~ ERROR missing condition
-    //~^ ERROR mismatched types
+    } else if {
+    //~^ ERROR missing condition for `if` expression
     } else {
     }
 }
 
 fn foo() {
     if true {
-    } else if { //~ ERROR missing condition
-    //~^ ERROR mismatched types
+    } else if {
+    //~^ ERROR missing condition for `if` expression
     }
     bar();
 }
diff --git a/src/test/ui/parser/issues/issue-13483.stderr b/src/test/ui/parser/issues/issue-13483.stderr
index 5fd05b18ce06e..f5534090f18a4 100644
--- a/src/test/ui/parser/issues/issue-13483.stderr
+++ b/src/test/ui/parser/issues/issue-13483.stderr
@@ -2,32 +2,17 @@ error: missing condition for `if` expression
   --> $DIR/issue-13483.rs:3:14
    |
 LL |     } else if {
-   |              ^ expected if condition here
+   |              ^- if this block is the condition of the `if` expression, then it must be followed by another block
+   |              |
+   |              expected condition here
 
 error: missing condition for `if` expression
   --> $DIR/issue-13483.rs:11:14
    |
 LL |     } else if {
-   |              ^ expected if condition here
+   |              ^- if this block is the condition of the `if` expression, then it must be followed by another block
+   |              |
+   |              expected condition here
 
-error[E0308]: mismatched types
-  --> $DIR/issue-13483.rs:3:15
-   |
-LL |       } else if {
-   |  _______________^
-LL | |
-LL | |     } else {
-   | |_____^ expected `bool`, found `()`
-
-error[E0308]: mismatched types
-  --> $DIR/issue-13483.rs:11:15
-   |
-LL |       } else if {
-   |  _______________^
-LL | |
-LL | |     }
-   | |_____^ expected `bool`, found `()`
-
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/issues/issue-51602.stderr b/src/test/ui/parser/issues/issue-51602.stderr
index d800890bca38d..4a5653fdb513e 100644
--- a/src/test/ui/parser/issues/issue-51602.stderr
+++ b/src/test/ui/parser/issues/issue-51602.stderr
@@ -2,9 +2,13 @@ error: expected `{`, found keyword `in`
   --> $DIR/issue-51602.rs:2:10
    |
 LL |     if i in 1..10 {
-   |     --   ^^ expected `{`
-   |     |
-   |     this `if` expression has a condition, but no block
+   |          ^^ expected `{`
+   |
+note: the `if` expression is missing a block after this condition
+  --> $DIR/issue-51602.rs:2:8
+   |
+LL |     if i in 1..10 {
+   |        ^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/issues/issue-62554.stderr b/src/test/ui/parser/issues/issue-62554.stderr
index 3589016e1dc83..9e62572e388fd 100644
--- a/src/test/ui/parser/issues/issue-62554.stderr
+++ b/src/test/ui/parser/issues/issue-62554.stderr
@@ -57,10 +57,13 @@ error: expected `{`, found `macro_rules`
   --> $DIR/issue-62554.rs:6:23
    |
 LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 {
-   |                 --    ^^^^^^^^^^^ expected `{`
-   |                 |
-   |                 this `if` expression has a condition, but no block
+   |                       ^^^^^^^^^^^ expected `{`
    |
+note: the `if` expression is missing a block after this condition
+  --> $DIR/issue-62554.rs:6:20
+   |
+LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 {
+   |                    ^^
 help: try placing this code inside a block
    |
 LL | fn foo(u: u8) { if u8 { macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 { }
diff --git a/src/test/ui/resolve/filter-intrinsics.rs b/src/test/ui/resolve/filter-intrinsics.rs
new file mode 100644
index 0000000000000..c0956ef85aff6
--- /dev/null
+++ b/src/test/ui/resolve/filter-intrinsics.rs
@@ -0,0 +1,10 @@
+fn main() {
+    // Should suggest only `std::mem::size_of`
+    let _ = size_of::<usize>();
+    //~^ ERROR cannot find
+
+    // Should suggest `std::intrinsics::fabsf64`,
+    // since there is no non-intrinsic to suggest.
+    let _ = fabsf64(1.0);
+    //~^ ERROR cannot find
+}
diff --git a/src/test/ui/resolve/filter-intrinsics.stderr b/src/test/ui/resolve/filter-intrinsics.stderr
new file mode 100644
index 0000000000000..955070891fbbe
--- /dev/null
+++ b/src/test/ui/resolve/filter-intrinsics.stderr
@@ -0,0 +1,25 @@
+error[E0425]: cannot find function `size_of` in this scope
+  --> $DIR/filter-intrinsics.rs:3:13
+   |
+LL |     let _ = size_of::<usize>();
+   |             ^^^^^^^ not found in this scope
+   |
+help: consider importing this function
+   |
+LL | use std::mem::size_of;
+   |
+
+error[E0425]: cannot find function `fabsf64` in this scope
+  --> $DIR/filter-intrinsics.rs:8:13
+   |
+LL |     let _ = fabsf64(1.0);
+   |             ^^^^^^^ not found in this scope
+   |
+help: consider importing this function
+   |
+LL | use std::intrinsics::fabsf64;
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0425`.
diff --git a/src/test/ui/rfc-2011-nicer-assert-messages/all-expr-kinds.rs b/src/test/ui/rfc-2011-nicer-assert-messages/all-expr-kinds.rs
new file mode 100644
index 0000000000000..c0e9f29fdbce0
--- /dev/null
+++ b/src/test/ui/rfc-2011-nicer-assert-messages/all-expr-kinds.rs
@@ -0,0 +1,143 @@
+// edition:2021
+// ignore-tidy-linelength
+// only-x86_64
+// run-pass
+
+#![allow(path_statements, unused_allocation)]
+#![feature(box_syntax, core_intrinsics, generic_assert, generic_assert_internals)]
+
+macro_rules! test {
+  (
+    let mut $elem_ident:ident = $elem_expr:expr;
+    [ $($assert:tt)* ] => $msg:literal
+  ) => {
+    {
+      #[allow(unused_assignments, unused_mut, unused_variables)]
+      let rslt = std::panic::catch_unwind(|| {
+        let mut $elem_ident = $elem_expr;
+        assert!($($assert)*);
+      });
+      let err = rslt.unwrap_err();
+      if let Some(elem) = err.downcast_ref::<String>() {
+        assert_eq!(elem, &$msg);
+      }
+      else if let Some(elem) = err.downcast_ref::<&str>() {
+        assert_eq!(elem, &$msg);
+      }
+      else {
+        panic!("assert!( ... ) should return a string");
+      }
+    }
+  }
+}
+
+macro_rules! tests {
+  (
+    let mut $elem_ident:ident = $elem_expr:expr;
+
+    $(
+      [ $($elem_assert:tt)* ] => $elem_msg:literal
+    )+
+  ) => {
+    $(
+      test!(
+        let mut $elem_ident = $elem_expr;
+        [ $($elem_assert)* ] => $elem_msg
+      );
+    )+
+  }
+}
+
+const FOO: Foo = Foo { bar: 1 };
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+struct Foo {
+  bar: i32
+}
+
+fn main() {
+  // ***** Allowed *****
+
+  tests!(
+    let mut elem = 1i32;
+
+    // binary
+    [ elem + 1 == 3 ] => "Assertion failed: elem + 1 == 3\nWith captures:\n  elem = 1\n"
+  );
+
+  // ***** Disallowed *****
+
+  tests!(
+    let mut elem = 1i32;
+
+    // assign
+    [ { let local = elem; local } == 3 ] => "Assertion failed: { let local = elem; local } == 3"
+
+    // assign op
+    [ { elem += 1; elem } == 3 ] => "Assertion failed: { elem += 1; elem } == 3"
+
+    // async
+    [ { let _ = async { elem }; elem } == 3 ] => "Assertion failed: { let _ = async { elem }; elem } == 3"
+
+    // await
+
+    // block
+    [ { elem } == 3 ] => "Assertion failed: { elem } == 3"
+
+    // box
+    [ box elem == box 3 ] => "Assertion failed: box elem == box 3"
+
+    // break
+    [ loop { break elem; } ==  3 ] => "Assertion failed: loop { break elem; } == 3"
+
+    // closure
+    [(|| elem)() ==  3 ] => "Assertion failed: (|| elem)() == 3"
+
+    // const block
+
+    // continue
+
+    // err
+
+    // field
+    [ FOO.bar ==  3 ] => "Assertion failed: FOO.bar == 3"
+
+    // for loop
+    [ { for _ in 0..elem { elem; } elem } ==  3 ] => "Assertion failed: { for _ in 0..elem { elem; } elem } == 3"
+
+    // if
+    [ if true { elem } else { elem } == 3 ] => "Assertion failed: if true { elem } else { elem } == 3"
+
+    // inline asm
+
+    // let
+    [ if let true = true { elem } else { elem } == 3 ] => "Assertion failed: if let true = true { elem } else { elem } == 3"
+
+    // lit
+
+    // loop
+    [ loop { elem; break elem; } == 3 ] => "Assertion failed: loop { elem; break elem; } == 3"
+
+    // mac call
+
+    // match
+    [ match elem { _ => elem } == 3 ] => "Assertion failed: match elem { _ => elem, } == 3"
+
+    // ret
+    [ (|| { return elem; })() == 3 ] => "Assertion failed: (|| { return elem; })() == 3"
+
+    // try
+    [ (|| { Some(Some(elem)?) })() == Some(3) ] => "Assertion failed: (|| { Some(Some(elem)?) })() == Some(3)"
+
+    // try block
+
+    // underscore
+
+    // while
+    [ { while false { elem; break; } elem } == 3 ] => "Assertion failed: { while false { elem; break; } elem } == 3"
+
+    // yeet
+
+    // yield
+  );
+}
diff --git a/src/test/ui/rfc-2011-nicer-assert-messages/all-not-available-cases.rs b/src/test/ui/rfc-2011-nicer-assert-messages/all-not-available-cases.rs
new file mode 100644
index 0000000000000..86697c58fbcf1
--- /dev/null
+++ b/src/test/ui/rfc-2011-nicer-assert-messages/all-not-available-cases.rs
@@ -0,0 +1,43 @@
+// aux-build:common.rs
+// ignore-tidy-linelength
+// only-x86_64
+// run-pass
+
+#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
+
+extern crate common;
+
+#[derive(Clone, Copy, PartialEq)]
+struct CopyNoDebug(i32);
+
+#[derive(Debug, PartialEq)]
+struct NoCopyDebug(i32);
+
+#[derive(PartialEq)]
+struct NoCopyNoDebug(i32);
+
+fn main() {
+  // Has Copy but does not have Debug
+  common::test!(
+    let mut copy_no_debug = CopyNoDebug(1);
+    [ copy_no_debug == CopyNoDebug(3) ] => "Assertion failed: copy_no_debug == CopyNoDebug(3)\nWith captures:\n  copy_no_debug = N/A\n"
+  );
+
+  // Does not have Copy but has Debug
+  common::test!(
+    let mut no_copy_debug = NoCopyDebug(1);
+    [ no_copy_debug == NoCopyDebug(3) ] => "Assertion failed: no_copy_debug == NoCopyDebug(3)\nWith captures:\n  no_copy_debug = N/A\n"
+  );
+
+  // Does not have Copy and does not have Debug
+  common::test!(
+    let mut no_copy_no_debug = NoCopyNoDebug(1);
+    [ no_copy_no_debug == NoCopyNoDebug(3) ] => "Assertion failed: no_copy_no_debug == NoCopyNoDebug(3)\nWith captures:\n  no_copy_no_debug = N/A\n"
+  );
+
+  // Unevaluated (Expression short-circuited)
+  common::test!(
+    let mut elem = true;
+    [ false && elem ] => "Assertion failed: false && elem\nWith captures:\n  elem = N/A\n"
+  );
+}
diff --git a/src/test/ui/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs b/src/test/ui/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs
new file mode 100644
index 0000000000000..6a1435f792bf4
--- /dev/null
+++ b/src/test/ui/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs
@@ -0,0 +1,13 @@
+// compile-flags: --test
+// run-pass
+
+#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
+
+#[should_panic(expected = "Custom user message")]
+#[test]
+fn test() {
+  assert!(1 == 3, "Custom user message");
+}
+
+fn main() {
+}
diff --git a/src/test/ui/rfc-2011-nicer-assert-messages/assert-without-captures-does-not-create-unnecessary-code.rs b/src/test/ui/rfc-2011-nicer-assert-messages/assert-without-captures-does-not-create-unnecessary-code.rs
new file mode 100644
index 0000000000000..06c4993ec30d6
--- /dev/null
+++ b/src/test/ui/rfc-2011-nicer-assert-messages/assert-without-captures-does-not-create-unnecessary-code.rs
@@ -0,0 +1,14 @@
+// aux-build:common.rs
+// only-x86_64
+// run-pass
+
+#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
+
+extern crate common;
+
+fn main() {
+  common::test!(
+    let mut _nothing = ();
+    [ 1 == 3 ] => "Assertion failed: 1 == 3"
+  );
+}
diff --git a/src/test/ui/rfc-2011-nicer-assert-messages/auxiliary/common.rs b/src/test/ui/rfc-2011-nicer-assert-messages/auxiliary/common.rs
new file mode 100644
index 0000000000000..903ed507c2e51
--- /dev/null
+++ b/src/test/ui/rfc-2011-nicer-assert-messages/auxiliary/common.rs
@@ -0,0 +1,25 @@
+#[macro_export]
+macro_rules! test {
+  (
+    let mut $elem_ident:ident = $elem_expr:expr;
+    [ $($assert:tt)* ] => $msg:literal
+  ) => {
+    {
+      #[allow(unused_assignments, unused_mut, unused_variables)]
+      let rslt = std::panic::catch_unwind(|| {
+        let mut $elem_ident = $elem_expr;
+        assert!($($assert)*);
+      });
+      let err = rslt.unwrap_err();
+      if let Some(elem) = err.downcast_ref::<String>() {
+        assert_eq!(elem, &$msg);
+      }
+      else if let Some(elem) = err.downcast_ref::<&str>() {
+        assert_eq!(elem, &$msg);
+      }
+      else {
+        panic!("assert!( ... ) should return a string");
+      }
+    }
+  }
+}
diff --git a/src/test/ui/rfc-2011-nicer-assert-messages/codegen.rs b/src/test/ui/rfc-2011-nicer-assert-messages/codegen.rs
new file mode 100644
index 0000000000000..1db9d33c72aee
--- /dev/null
+++ b/src/test/ui/rfc-2011-nicer-assert-messages/codegen.rs
@@ -0,0 +1,9 @@
+// check-pass
+// compile-flags: -Z unpretty=expanded
+
+#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
+
+fn main() {
+    let elem = 1i32;
+    assert!(elem == 1);
+}
diff --git a/src/test/ui/rfc-2011-nicer-assert-messages/codegen.stdout b/src/test/ui/rfc-2011-nicer-assert-messages/codegen.stdout
new file mode 100644
index 0000000000000..a590eb3223254
--- /dev/null
+++ b/src/test/ui/rfc-2011-nicer-assert-messages/codegen.stdout
@@ -0,0 +1,29 @@
+#![feature(prelude_import)]
+#![no_std]
+// check-pass
+// compile-flags: -Z unpretty=expanded
+
+#![feature(core_intrinsics, generic_assert, generic_assert_internals)]
+#[prelude_import]
+use ::std::prelude::rust_2015::*;
+#[macro_use]
+extern crate std;
+
+fn main() {
+    let elem = 1i32;
+    {
+        #[allow(unused_imports)]
+        use ::core::asserting::{TryCaptureGeneric, TryCapturePrintable};
+        let mut __capture0 = ::core::asserting::Capture::new();
+        let __local_bind0 = &elem;
+        if !(*{
+                                (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
+                                __local_bind0
+                            } == 1) {
+                {
+                    ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem == 1\nWith captures:\n  elem = ",
+                                        "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)]))
+                }
+            }
+    };
+}
diff --git a/src/test/ui/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs b/src/test/ui/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs
index f70ca87e304a9..01860adaac250 100644
--- a/src/test/ui/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs
+++ b/src/test/ui/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs
@@ -1,8 +1,7 @@
 // compile-flags: --test
+// ignore-tidy-linelength
 // run-pass
 
-// `generic_assert` is completely unimplemented and doesn't generate any logic, thus the
-// reason why this test currently passes
 #![feature(core_intrinsics, generic_assert, generic_assert_internals)]
 
 use std::fmt::{Debug, Formatter};
@@ -16,10 +15,11 @@ impl Debug for CopyDebug {
   }
 }
 
+#[should_panic(expected = "Assertion failed: copy_debug == CopyDebug(3)\nWith captures:\n  copy_debug = With great power comes great electricity bills\n")]
 #[test]
 fn test() {
-  let _copy_debug = CopyDebug(1);
-  assert!(_copy_debug == CopyDebug(3));
+  let copy_debug = CopyDebug(1);
+  assert!(copy_debug == CopyDebug(3));
 }
 
 fn main() {
diff --git a/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs b/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs
index e24649ea044f5..e66caa19ec96b 100644
--- a/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs
+++ b/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs
@@ -21,15 +21,15 @@ fn main() {
     };
 
     if let Some(n) = opt else {
-    //~^ ERROR missing condition for `if` expression
+    //~^ ERROR this `if` expression is missing a block after the condition
         return;
     };
     if let Some(n) = opt && n == 1 else {
-    //~^ ERROR missing condition for `if` expression
+    //~^ ERROR this `if` expression is missing a block after the condition
         return;
     };
     if let Some(n) = opt && let another = n else {
-    //~^ ERROR missing condition for `if` expression
+    //~^ ERROR this `if` expression is missing a block after the condition
         return;
     };
 
diff --git a/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr b/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr
index aebfc1a72b7aa..eea8ed0c9633e 100644
--- a/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr
+++ b/src/test/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr
@@ -20,23 +20,41 @@ help: wrap the expression in parentheses
 LL |     let Some(n) = (opt && let another = n) else {
    |                   +                      +
 
-error: missing condition for `if` expression
-  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:23:7
+error: this `if` expression is missing a block after the condition
+  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:23:5
    |
 LL |     if let Some(n) = opt else {
-   |       ^ expected if condition here
+   |     ^^
+   |
+help: add a block here
+  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:23:25
+   |
+LL |     if let Some(n) = opt else {
+   |                         ^
 
-error: missing condition for `if` expression
-  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:27:7
+error: this `if` expression is missing a block after the condition
+  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:27:5
    |
 LL |     if let Some(n) = opt && n == 1 else {
-   |       ^ expected if condition here
+   |     ^^
+   |
+help: add a block here
+  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:27:35
+   |
+LL |     if let Some(n) = opt && n == 1 else {
+   |                                   ^
 
-error: missing condition for `if` expression
-  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:31:7
+error: this `if` expression is missing a block after the condition
+  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:31:5
+   |
+LL |     if let Some(n) = opt && let another = n else {
+   |     ^^
+   |
+help: add a block here
+  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:31:44
    |
 LL |     if let Some(n) = opt && let another = n else {
-   |       ^ expected if condition here
+   |                                            ^
 
 error: expected `{`, found keyword `else`
   --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:37:33