diff --git a/compiler/rustc_error_messages/locales/en-US/parse.ftl b/compiler/rustc_error_messages/locales/en-US/parse.ftl
index 8f063f5082c95..a3e2002da781c 100644
--- a/compiler/rustc_error_messages/locales/en-US/parse.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/parse.ftl
@@ -238,6 +238,7 @@ parse_const_let_mutually_exclusive = `const` and `let` are mutually exclusive
 
 parse_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else`
 parse_invalid_curly_in_let_else = right curly brace `{"}"}` before `else` in a `let...else` statement not allowed
+parse_extra_if_in_let_else = remove the `if` if you meant to write a `let...else` statement
 
 parse_compound_assignment_expression_in_let = can't reassign to an uninitialized variable
     .suggestion = initialize the variable
diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs
index 8e2a13a6c0ab1..93d1671634691 100644
--- a/compiler/rustc_feature/src/lib.rs
+++ b/compiler/rustc_feature/src/lib.rs
@@ -83,7 +83,8 @@ impl UnstableFeatures {
     /// Otherwise, only `RUSTC_BOOTSTRAP=1` will work.
     pub fn from_environment(krate: Option<&str>) -> Self {
         // `true` if this is a feature-staged build, i.e., on the beta or stable channel.
-        let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
+        let disable_unstable_features =
+            option_env!("CFG_DISABLE_UNSTABLE_FEATURES").map(|s| s != "0").unwrap_or(false);
         // Returns whether `krate` should be counted as unstable
         let is_unstable_crate = |var: &str| {
             krate.map_or(false, |name| var.split(',').any(|new_krate| new_krate == name))
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index bbf7b81a2cc66..1a0715a91cb0c 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -1613,12 +1613,14 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                 if visitor.ret_exprs.len() > 0 && let Some(expr) = expression {
                     self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs);
                 }
+
                 let reported = err.emit_unless(unsized_return);
 
                 self.final_ty = Some(fcx.tcx.ty_error_with_guaranteed(reported));
             }
         }
     }
+
     fn note_unreachable_loop_return(
         &self,
         err: &mut Diagnostic,
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index bd1626dff7951..7379e75963f53 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -59,7 +59,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             || self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
             || self.suggest_clone_for_ref(err, expr, expr_ty, expected)
             || self.suggest_into(err, expr, expr_ty, expected)
-            || self.suggest_floating_point_literal(err, expr, expected);
+            || self.suggest_floating_point_literal(err, expr, expected)
+            || self.note_result_coercion(err, expr, expected, expr_ty);
         if !suggested {
             self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected);
         }
@@ -81,7 +82,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.annotate_expected_due_to_let_ty(err, expr, error);
         self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error);
         self.note_type_is_not_clone(err, expected, expr_ty, expr);
-        self.note_need_for_fn_pointer(err, expected, expr_ty);
         self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
         self.check_for_range_as_method_call(err, expr, expr_ty, expected);
         self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected);
@@ -697,6 +697,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         );
     }
 
+    pub(crate) fn note_result_coercion(
+        &self,
+        err: &mut Diagnostic,
+        expr: &hir::Expr<'tcx>,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+    ) -> bool {
+        let ty::Adt(e, substs_e) = expected.kind() else { return false; };
+        let ty::Adt(f, substs_f) = found.kind() else { return false; };
+        if e.did() != f.did() {
+            return false;
+        }
+        if Some(e.did()) != self.tcx.get_diagnostic_item(sym::Result) {
+            return false;
+        }
+        let map = self.tcx.hir();
+        if let Some(hir::Node::Expr(expr)) = map.find_parent(expr.hir_id)
+            && let hir::ExprKind::Ret(_) = expr.kind
+        {
+            // `return foo;`
+        } else if map.get_return_block(expr.hir_id).is_some() {
+            // Function's tail expression.
+        } else {
+            return false;
+        }
+        let e = substs_e.type_at(1);
+        let f = substs_f.type_at(1);
+        if self
+            .infcx
+            .type_implements_trait(
+                self.tcx.get_diagnostic_item(sym::Into).unwrap(),
+                [f, e],
+                self.param_env,
+            )
+            .must_apply_modulo_regions()
+        {
+            err.multipart_suggestion(
+                "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \
+                 in `Ok` so the expression remains of type `Result`",
+                vec![
+                    (expr.span.shrink_to_lo(), "Ok(".to_string()),
+                    (expr.span.shrink_to_hi(), "?)".to_string()),
+                ],
+                Applicability::MaybeIncorrect,
+            );
+            return true;
+        }
+        false
+    }
+
     /// If the expected type is an enum (Issue #55250) with any variants whose
     /// sole field is of the found type, suggest such variants. (Issue #42764)
     fn suggest_compatible_variants(
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 6ed8adb47425a..e9858aef6d0bf 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -926,43 +926,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    pub(in super::super) fn note_need_for_fn_pointer(
-        &self,
-        err: &mut Diagnostic,
-        expected: Ty<'tcx>,
-        found: Ty<'tcx>,
-    ) {
-        let (sig, did, substs) = match (&expected.kind(), &found.kind()) {
-            (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
-                let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1);
-                let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2);
-                if sig1 != sig2 {
-                    return;
-                }
-                err.note(
-                    "different `fn` items always have unique types, even if their signatures are \
-                     the same",
-                );
-                (sig1, *did1, substs1)
-            }
-            (ty::FnDef(did, substs), ty::FnPtr(sig2)) => {
-                let sig1 = self.tcx.bound_fn_sig(*did).subst(self.tcx, substs);
-                if sig1 != *sig2 {
-                    return;
-                }
-                (sig1, *did, substs)
-            }
-            _ => return,
-        };
-        err.help(&format!("change the expected type to be function pointer `{}`", sig));
-        err.help(&format!(
-            "if the expected type is due to type inference, cast the expected `fn` to a function \
-             pointer: `{} as {}`",
-            self.tcx.def_path_str_with_substs(did, substs),
-            sig
-        ));
-    }
-
     // Instantiates the given path, which must refer to an item with the given
     // number of type parameters and type.
     #[instrument(skip(self, span), level = "debug")]
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 28fd03b878b2b..212683a5429ed 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -1841,6 +1841,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
                 self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
                 self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
+                self.suggest_function_pointers(cause, span, &exp_found, diag);
             }
         }
 
@@ -2585,7 +2586,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     /// with the other type. A TyVar inference type is compatible with any type, and an IntVar or
     /// FloatVar inference type are compatible with themselves or their concrete types (Int and
     /// Float types, respectively). When comparing two ADTs, these rules apply recursively.
-    pub fn same_type_modulo_infer(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
+    pub fn same_type_modulo_infer<T: relate::Relate<'tcx>>(&self, a: T, b: T) -> bool {
         let (a, b) = self.resolve_vars_if_possible((a, b));
         SameTypeModuloInfer(self).relate(a, b).is_ok()
     }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
index 5b02956a106c6..b2ab39630bdaf 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
@@ -8,7 +8,7 @@ use rustc_middle::traits::{
     StatementAsExpression,
 };
 use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::{self as ty, Ty, TypeVisitable};
+use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TypeVisitable};
 use rustc_span::{sym, BytePos, Span};
 
 use crate::errors::SuggAddLetForLetChains;
@@ -351,6 +351,82 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
     }
 
+    pub(super) fn suggest_function_pointers(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        span: Span,
+        exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+        diag: &mut Diagnostic,
+    ) {
+        debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
+        let ty::error::ExpectedFound { expected, found } = exp_found;
+        let expected_inner = expected.peel_refs();
+        let found_inner = found.peel_refs();
+        if !expected_inner.is_fn() || !found_inner.is_fn() {
+            return;
+        }
+        match (&expected_inner.kind(), &found_inner.kind()) {
+            (ty::FnPtr(sig), ty::FnDef(did, substs)) => {
+                let expected_sig = &(self.normalize_fn_sig)(*sig);
+                let found_sig =
+                    &(self.normalize_fn_sig)(self.tcx.bound_fn_sig(*did).subst(self.tcx, substs));
+
+                let fn_name = self.tcx.def_path_str_with_substs(*did, substs);
+
+                if !self.same_type_modulo_infer(*found_sig, *expected_sig)
+                    || !sig.is_suggestable(self.tcx, true)
+                    || ty::util::is_intrinsic(self.tcx, *did)
+                {
+                    return;
+                }
+
+                let (msg, sugg) = match (expected.is_ref(), found.is_ref()) {
+                    (true, false) => {
+                        let msg = "consider using a reference";
+                        let sug = format!("&{fn_name}");
+                        (msg, sug)
+                    }
+                    (false, true) => {
+                        let msg = "consider removing the reference";
+                        let sug = format!("{fn_name}");
+                        (msg, sug)
+                    }
+                    (true, true) => {
+                        diag.note("fn items are distinct from fn pointers");
+                        let msg = "consider casting to a fn pointer";
+                        let sug = format!("&({fn_name} as {sig})");
+                        (msg, sug)
+                    }
+                    (false, false) => {
+                        diag.note("fn items are distinct from fn pointers");
+                        let msg = "consider casting to a fn pointer";
+                        let sug = format!("{fn_name} as {sig}");
+                        (msg, sug)
+                    }
+                };
+                diag.span_suggestion(span, msg, &sugg, Applicability::MaybeIncorrect);
+            }
+            (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
+                let expected_sig =
+                    &(self.normalize_fn_sig)(self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1));
+                let found_sig =
+                    &(self.normalize_fn_sig)(self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2));
+
+                if self.same_type_modulo_infer(*found_sig, *expected_sig) {
+                    diag.note(
+                    "different fn items have unique types, even if their signatures are the same",
+                    );
+                }
+            }
+            (ty::FnDef(_, _), ty::FnPtr(_)) => {
+                diag.note("fn items are distinct from fn pointers");
+            }
+            _ => {
+                return;
+            }
+        };
+    }
+
     pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
         if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
             (expected.kind(), found.kind())
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 44da3fbe300ca..bb2dd290c6d5d 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -985,7 +985,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
         let vis = self.get_visibility(id);
         let span = self.get_span(id, sess);
         let macro_rules = match kind {
-            DefKind::Macro(..) => self.root.tables.macro_rules.get(self, id).is_some(),
+            DefKind::Macro(..) => self.root.tables.is_macro_rules.get(self, id),
             _ => false,
         };
 
@@ -1283,7 +1283,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
     fn get_macro(self, id: DefIndex, sess: &Session) -> ast::MacroDef {
         match self.def_kind(id) {
             DefKind::Macro(_) => {
-                let macro_rules = self.root.tables.macro_rules.get(self, id).is_some();
+                let macro_rules = self.root.tables.is_macro_rules.get(self, id);
                 let body =
                     self.root.tables.macro_definition.get(self, id).unwrap().decode((self, sess));
                 ast::MacroDef { macro_rules, body: ast::ptr::P(body) }
@@ -1595,11 +1595,11 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
     }
 
     fn get_attr_flags(self, index: DefIndex) -> AttrFlags {
-        self.root.tables.attr_flags.get(self, index).unwrap_or(AttrFlags::empty())
+        self.root.tables.attr_flags.get(self, index)
     }
 
     fn get_is_intrinsic(self, index: DefIndex) -> bool {
-        self.root.tables.is_intrinsic.get(self, index).is_some()
+        self.root.tables.is_intrinsic.get(self, index)
     }
 }
 
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index 2fa645cd9e33d..eebc2f21dfe4e 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -226,12 +226,7 @@ provide! { tcx, def_id, other, cdata,
     deduced_param_attrs => { table }
     is_type_alias_impl_trait => {
         debug_assert_eq!(tcx.def_kind(def_id), DefKind::OpaqueTy);
-        cdata
-            .root
-            .tables
-            .is_type_alias_impl_trait
-            .get(cdata, def_id.index)
-            .is_some()
+        cdata.root.tables.is_type_alias_impl_trait.get(cdata, def_id.index)
     }
     collect_return_position_impl_trait_in_trait_tys => {
         Ok(cdata
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 2ecaa33d4d315..97f0457ba7116 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -483,7 +483,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         self.lazy(DefPathHashMapRef::BorrowedFromTcx(self.tcx.def_path_hash_to_def_index_map()))
     }
 
-    fn encode_source_map(&mut self) -> LazyTable<u32, LazyValue<rustc_span::SourceFile>> {
+    fn encode_source_map(&mut self) -> LazyTable<u32, Option<LazyValue<rustc_span::SourceFile>>> {
         let source_map = self.tcx.sess.source_map();
         let all_source_files = source_map.files();
 
@@ -1130,7 +1130,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             attr_flags |= AttrFlags::IS_DOC_HIDDEN;
         }
         if !attr_flags.is_empty() {
-            self.tables.attr_flags.set(def_id.local_def_index, attr_flags);
+            self.tables.attr_flags.set_nullable(def_id.local_def_index, attr_flags);
         }
     }
 
@@ -1387,7 +1387,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         if impl_item.kind == ty::AssocKind::Fn {
             record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
             if tcx.is_intrinsic(def_id) {
-                self.tables.is_intrinsic.set(def_id.index, ());
+                self.tables.is_intrinsic.set_nullable(def_id.index, true);
             }
         }
     }
@@ -1519,7 +1519,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             }
             hir::ItemKind::Macro(ref macro_def, _) => {
                 if macro_def.macro_rules {
-                    self.tables.macro_rules.set(def_id.index, ());
+                    self.tables.is_macro_rules.set_nullable(def_id.index, true);
                 }
                 record!(self.tables.macro_definition[def_id] <- &*macro_def.body);
             }
@@ -1529,7 +1529,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             hir::ItemKind::OpaqueTy(ref opaque) => {
                 self.encode_explicit_item_bounds(def_id);
                 if matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias) {
-                    self.tables.is_type_alias_impl_trait.set(def_id.index, ());
+                    self.tables.is_type_alias_impl_trait.set_nullable(def_id.index, true);
                 }
             }
             hir::ItemKind::Enum(..) => {
@@ -1636,7 +1636,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         if let hir::ItemKind::Fn(..) = item.kind {
             record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
             if tcx.is_intrinsic(def_id) {
-                self.tables.is_intrinsic.set(def_id.index, ());
+                self.tables.is_intrinsic.set_nullable(def_id.index, true);
             }
         }
         if let hir::ItemKind::Impl { .. } = item.kind {
@@ -2038,7 +2038,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         }
         if let hir::ForeignItemKind::Fn(..) = nitem.kind {
             if tcx.is_intrinsic(def_id) {
-                self.tables.is_intrinsic.set(def_id.index, ());
+                self.tables.is_intrinsic.set_nullable(def_id.index, true);
             }
         }
     }
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 69690264ae4ea..698b2ebc4732a 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -185,9 +185,9 @@ enum LazyState {
     Previous(NonZeroUsize),
 }
 
-type SyntaxContextTable = LazyTable<u32, LazyValue<SyntaxContextData>>;
-type ExpnDataTable = LazyTable<ExpnIndex, LazyValue<ExpnData>>;
-type ExpnHashTable = LazyTable<ExpnIndex, LazyValue<ExpnHash>>;
+type SyntaxContextTable = LazyTable<u32, Option<LazyValue<SyntaxContextData>>>;
+type ExpnDataTable = LazyTable<ExpnIndex, Option<LazyValue<ExpnData>>>;
+type ExpnHashTable = LazyTable<ExpnIndex, Option<LazyValue<ExpnHash>>>;
 
 #[derive(MetadataEncodable, MetadataDecodable)]
 pub(crate) struct ProcMacroData {
@@ -253,7 +253,7 @@ pub(crate) struct CrateRoot {
 
     def_path_hash_map: LazyValue<DefPathHashMapRef<'static>>,
 
-    source_map: LazyTable<u32, LazyValue<rustc_span::SourceFile>>,
+    source_map: LazyTable<u32, Option<LazyValue<rustc_span::SourceFile>>>,
 
     compiler_builtins: bool,
     needs_allocator: bool,
@@ -315,21 +315,27 @@ pub(crate) struct IncoherentImpls {
 
 /// Define `LazyTables` and `TableBuilders` at the same time.
 macro_rules! define_tables {
-    ($($name:ident: Table<$IDX:ty, $T:ty>),+ $(,)?) => {
+    (
+        - nullable: $($name1:ident: Table<$IDX1:ty, $T1:ty>,)+
+        - optional: $($name2:ident: Table<$IDX2:ty, $T2:ty>,)+
+    ) => {
         #[derive(MetadataEncodable, MetadataDecodable)]
         pub(crate) struct LazyTables {
-            $($name: LazyTable<$IDX, $T>),+
+            $($name1: LazyTable<$IDX1, $T1>,)+
+            $($name2: LazyTable<$IDX2, Option<$T2>>,)+
         }
 
         #[derive(Default)]
         struct TableBuilders {
-            $($name: TableBuilder<$IDX, $T>),+
+            $($name1: TableBuilder<$IDX1, $T1>,)+
+            $($name2: TableBuilder<$IDX2, Option<$T2>>,)+
         }
 
         impl TableBuilders {
             fn encode(&self, buf: &mut FileEncoder) -> LazyTables {
                 LazyTables {
-                    $($name: self.$name.encode(buf)),+
+                    $($name1: self.$name1.encode(buf),)+
+                    $($name2: self.$name2.encode(buf),)+
                 }
             }
         }
@@ -337,9 +343,15 @@ macro_rules! define_tables {
 }
 
 define_tables! {
+- nullable:
+    is_intrinsic: Table<DefIndex, bool>,
+    is_macro_rules: Table<DefIndex, bool>,
+    is_type_alias_impl_trait: Table<DefIndex, bool>,
+    attr_flags: Table<DefIndex, AttrFlags>,
+
+- optional:
     attributes: Table<DefIndex, LazyArray<ast::Attribute>>,
     children: Table<DefIndex, LazyArray<DefIndex>>,
-
     opt_def_kind: Table<DefIndex, DefKind>,
     visibility: Table<DefIndex, LazyValue<ty::Visibility<DefIndex>>>,
     def_span: Table<DefIndex, LazyValue<Span>>,
@@ -370,7 +382,6 @@ define_tables! {
     impl_parent: Table<DefIndex, RawDefId>,
     impl_polarity: Table<DefIndex, ty::ImplPolarity>,
     constness: Table<DefIndex, hir::Constness>,
-    is_intrinsic: Table<DefIndex, ()>,
     impl_defaultness: Table<DefIndex, hir::Defaultness>,
     // FIXME(eddyb) perhaps compute this on the fly if cheap enough?
     coerce_unsized_info: Table<DefIndex, LazyValue<ty::adjustment::CoerceUnsizedInfo>>,
@@ -380,7 +391,6 @@ define_tables! {
     fn_arg_names: Table<DefIndex, LazyArray<Ident>>,
     generator_kind: Table<DefIndex, LazyValue<hir::GeneratorKind>>,
     trait_def: Table<DefIndex, LazyValue<ty::TraitDef>>,
-
     trait_item_def_id: Table<DefIndex, RawDefId>,
     inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
     expn_that_defined: Table<DefIndex, LazyValue<ExpnId>>,
@@ -395,18 +405,12 @@ define_tables! {
     def_path_hashes: Table<DefIndex, DefPathHash>,
     proc_macro_quoted_spans: Table<usize, LazyValue<Span>>,
     generator_diagnostic_data: Table<DefIndex, LazyValue<GeneratorDiagnosticData<'static>>>,
-    attr_flags: Table<DefIndex, AttrFlags>,
     variant_data: Table<DefIndex, LazyValue<VariantData>>,
     assoc_container: Table<DefIndex, ty::AssocItemContainer>,
-    // Slot is full when macro is macro_rules.
-    macro_rules: Table<DefIndex, ()>,
     macro_definition: Table<DefIndex, LazyValue<ast::DelimArgs>>,
     proc_macro: Table<DefIndex, MacroKind>,
     module_reexports: Table<DefIndex, LazyArray<ModChild>>,
     deduced_param_attrs: Table<DefIndex, LazyArray<DeducedParamAttrs>>,
-    // Slot is full when opaque is TAIT.
-    is_type_alias_impl_trait: Table<DefIndex, ()>,
-
     trait_impl_trait_tys: Table<DefIndex, LazyValue<FxHashMap<DefId, Ty<'static>>>>,
 }
 
@@ -419,6 +423,7 @@ struct VariantData {
 }
 
 bitflags::bitflags! {
+    #[derive(Default)]
     pub struct AttrFlags: u8 {
         const MAY_HAVE_DOC_LINKS = 1 << 0;
         const IS_DOC_HIDDEN      = 1 << 1;
diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs
index dc003227d40bd..70dbf6476e2fa 100644
--- a/compiler/rustc_metadata/src/rmeta/table.rs
+++ b/compiler/rustc_metadata/src/rmeta/table.rs
@@ -16,6 +16,7 @@ use std::num::NonZeroUsize;
 /// but this has no impact on safety.
 pub(super) trait FixedSizeEncoding: Default {
     /// This should be `[u8; BYTE_LEN]`;
+    /// Cannot use an associated `const BYTE_LEN: usize` instead due to const eval limitations.
     type ByteArray;
 
     fn from_bytes(b: &Self::ByteArray) -> Self;
@@ -199,31 +200,31 @@ impl FixedSizeEncoding for Option<RawDefId> {
     }
 }
 
-impl FixedSizeEncoding for Option<AttrFlags> {
+impl FixedSizeEncoding for AttrFlags {
     type ByteArray = [u8; 1];
 
     #[inline]
     fn from_bytes(b: &[u8; 1]) -> Self {
-        (b[0] != 0).then(|| AttrFlags::from_bits_truncate(b[0]))
+        AttrFlags::from_bits_truncate(b[0])
     }
 
     #[inline]
     fn write_to_bytes(self, b: &mut [u8; 1]) {
-        b[0] = self.map_or(0, |flags| flags.bits())
+        b[0] = self.bits();
     }
 }
 
-impl FixedSizeEncoding for Option<()> {
+impl FixedSizeEncoding for bool {
     type ByteArray = [u8; 1];
 
     #[inline]
     fn from_bytes(b: &[u8; 1]) -> Self {
-        (b[0] != 0).then(|| ())
+        b[0] != 0
     }
 
     #[inline]
     fn write_to_bytes(self, b: &mut [u8; 1]) {
-        b[0] = self.is_some() as u8
+        b[0] = self as u8
     }
 }
 
@@ -273,44 +274,38 @@ impl<T> FixedSizeEncoding for Option<LazyArray<T>> {
 }
 
 /// Helper for constructing a table's serialization (also see `Table`).
-pub(super) struct TableBuilder<I: Idx, T>
-where
-    Option<T>: FixedSizeEncoding,
-{
-    blocks: IndexVec<I, <Option<T> as FixedSizeEncoding>::ByteArray>,
+pub(super) struct TableBuilder<I: Idx, T: FixedSizeEncoding> {
+    blocks: IndexVec<I, T::ByteArray>,
     _marker: PhantomData<T>,
 }
 
-impl<I: Idx, T> Default for TableBuilder<I, T>
-where
-    Option<T>: FixedSizeEncoding,
-{
+impl<I: Idx, T: FixedSizeEncoding> Default for TableBuilder<I, T> {
     fn default() -> Self {
         TableBuilder { blocks: Default::default(), _marker: PhantomData }
     }
 }
 
-impl<I: Idx, T> TableBuilder<I, T>
+impl<I: Idx, const N: usize, T> TableBuilder<I, Option<T>>
 where
-    Option<T>: FixedSizeEncoding,
+    Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
 {
-    pub(crate) fn set<const N: usize>(&mut self, i: I, value: T)
-    where
-        Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
-    {
+    pub(crate) fn set(&mut self, i: I, value: T) {
+        self.set_nullable(i, Some(value))
+    }
+}
+
+impl<I: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]>> TableBuilder<I, T> {
+    pub(crate) fn set_nullable(&mut self, i: I, value: T) {
         // FIXME(eddyb) investigate more compact encodings for sparse tables.
         // On the PR @michaelwoerister mentioned:
         // > Space requirements could perhaps be optimized by using the HAMT `popcnt`
         // > trick (i.e. divide things into buckets of 32 or 64 items and then
         // > store bit-masks of which item in each bucket is actually serialized).
         self.blocks.ensure_contains_elem(i, || [0; N]);
-        Some(value).write_to_bytes(&mut self.blocks[i]);
+        value.write_to_bytes(&mut self.blocks[i]);
     }
 
-    pub(crate) fn encode<const N: usize>(&self, buf: &mut FileEncoder) -> LazyTable<I, T>
-    where
-        Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
-    {
+    pub(crate) fn encode(&self, buf: &mut FileEncoder) -> LazyTable<I, T> {
         let pos = buf.position();
         for block in &self.blocks {
             buf.emit_raw_bytes(block);
@@ -323,34 +318,27 @@ where
     }
 }
 
-impl<I: Idx, T: ParameterizedOverTcx> LazyTable<I, T>
+impl<I: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]> + ParameterizedOverTcx>
+    LazyTable<I, T>
 where
-    Option<T>: FixedSizeEncoding,
+    for<'tcx> T::Value<'tcx>: FixedSizeEncoding<ByteArray = [u8; N]>,
 {
     /// Given the metadata, extract out the value at a particular index (if any).
     #[inline(never)]
-    pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>, const N: usize>(
-        &self,
-        metadata: M,
-        i: I,
-    ) -> Option<T::Value<'tcx>>
-    where
-        Option<T::Value<'tcx>>: FixedSizeEncoding<ByteArray = [u8; N]>,
-    {
+    pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>(&self, metadata: M, i: I) -> T::Value<'tcx> {
         debug!("LazyTable::lookup: index={:?} len={:?}", i, self.encoded_size);
 
         let start = self.position.get();
         let bytes = &metadata.blob()[start..start + self.encoded_size];
         let (bytes, []) = bytes.as_chunks::<N>() else { panic!() };
-        let bytes = bytes.get(i.index())?;
-        FixedSizeEncoding::from_bytes(bytes)
+        match bytes.get(i.index()) {
+            Some(bytes) => FixedSizeEncoding::from_bytes(bytes),
+            None => FixedSizeEncoding::from_bytes(&[0; N]),
+        }
     }
 
     /// Size of the table in entries, including possible gaps.
-    pub(super) fn size<const N: usize>(&self) -> usize
-    where
-        for<'tcx> Option<T::Value<'tcx>>: FixedSizeEncoding<ByteArray = [u8; N]>,
-    {
+    pub(super) fn size(&self) -> usize {
         self.encoded_size / N
     }
 }
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 06b970ad97977..40763da0bb547 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -337,7 +337,9 @@ pub(crate) struct IfExpressionMissingThenBlock {
     #[primary_span]
     pub if_span: Span,
     #[subdiagnostic]
-    pub sub: IfExpressionMissingThenBlockSub,
+    pub missing_then_block_sub: IfExpressionMissingThenBlockSub,
+    #[subdiagnostic]
+    pub let_else_sub: Option<IfExpressionLetSomeSub>,
 }
 
 #[derive(Subdiagnostic)]
@@ -348,6 +350,13 @@ pub(crate) enum IfExpressionMissingThenBlockSub {
     AddThenBlock(#[primary_span] Span),
 }
 
+#[derive(Subdiagnostic)]
+#[help(parse_extra_if_in_let_else)]
+pub(crate) struct IfExpressionLetSomeSub {
+    #[primary_span]
+    pub if_span: Span,
+}
+
 #[derive(Diagnostic)]
 #[diag(parse_if_expression_missing_condition)]
 pub(crate) struct IfExpressionMissingCondition {
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index bf93a89f06555..3225a309a319b 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -11,15 +11,15 @@ use crate::errors::{
     ComparisonOrShiftInterpretedAsGenericSugg, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit,
     ExpectedElseBlock, ExpectedEqForLetExpr, ExpectedExpressionFoundLet,
     FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt,
-    IfExpressionMissingCondition, IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub,
-    InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub,
-    InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex, InvalidLogicalOperator,
-    InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator,
-    LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, MalformedLoopLabel,
-    MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm,
-    MissingDotDot, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray,
-    NoFieldsForFnCall, NotAsNegationOperator, NotAsNegationOperatorSub,
-    OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields,
+    IfExpressionLetSomeSub, IfExpressionMissingCondition, IfExpressionMissingThenBlock,
+    IfExpressionMissingThenBlockSub, InvalidBlockMacroSegment, InvalidComparisonOperator,
+    InvalidComparisonOperatorSub, InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex,
+    InvalidLogicalOperator, InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported,
+    LeftArrowOperator, LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath,
+    MalformedLoopLabel, MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg,
+    MissingCommaAfterMatchArm, MissingDotDot, MissingInInForLoop, MissingInInForLoopSub,
+    MissingSemicolonBeforeArray, NoFieldsForFnCall, NotAsNegationOperator,
+    NotAsNegationOperatorSub, OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields,
     RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere,
     StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedIfWithIf,
     UnexpectedTokenAfterLabel, UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses,
@@ -2251,9 +2251,10 @@ impl<'a> Parser<'a> {
                     if let ExprKind::Block(_, None) = right.kind => {
                         self.sess.emit_err(IfExpressionMissingThenBlock {
                             if_span: lo,
-                            sub: IfExpressionMissingThenBlockSub::UnfinishedCondition(
-                                cond_span.shrink_to_lo().to(*binop_span)
-                            ),
+                            missing_then_block_sub:
+                                IfExpressionMissingThenBlockSub::UnfinishedCondition(cond_span.shrink_to_lo().to(*binop_span)),
+                                let_else_sub: None,
+
                         });
                         std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi()))
                     },
@@ -2279,9 +2280,15 @@ impl<'a> Parser<'a> {
             if let Some(block) = recover_block_from_condition(self) {
                 block
             } else {
+                let let_else_sub = matches!(cond.kind, ExprKind::Let(..))
+                    .then(|| IfExpressionLetSomeSub { if_span: lo });
+
                 self.sess.emit_err(IfExpressionMissingThenBlock {
                     if_span: lo,
-                    sub: IfExpressionMissingThenBlockSub::AddThenBlock(cond_span.shrink_to_hi()),
+                    missing_then_block_sub: IfExpressionMissingThenBlockSub::AddThenBlock(
+                        cond_span.shrink_to_hi(),
+                    ),
+                    let_else_sub,
                 });
                 self.mk_block_err(cond_span.shrink_to_hi())
             }
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 40b9bedc84fd3..d59fa71406c31 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -1,5 +1,6 @@
 use std::mem;
 
+use super::{Certainty, InferCtxtEvalExt};
 use rustc_infer::{
     infer::InferCtxt,
     traits::{
@@ -8,8 +9,6 @@ use rustc_infer::{
     },
 };
 
-use super::{search_graph, Certainty, EvalCtxt};
-
 /// A trait engine using the new trait solver.
 ///
 /// This is mostly identical to how `evaluate_all` works inside of the
@@ -66,9 +65,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
             let mut has_changed = false;
             for obligation in mem::take(&mut self.obligations) {
                 let goal = obligation.clone().into();
-                let search_graph = &mut search_graph::SearchGraph::new(infcx.tcx);
-                let mut ecx = EvalCtxt::new_outside_solver(infcx, search_graph);
-                let (changed, certainty) = match ecx.evaluate_goal(goal) {
+                let (changed, certainty) = match infcx.evaluate_root_goal(goal) {
                     Ok(result) => result,
                     Err(NoSolution) => {
                         errors.push(FulfillmentError {
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index da2a1a19957e1..70f094014453e 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -152,6 +152,36 @@ impl<'tcx> TyCtxtExt<'tcx> for TyCtxt<'tcx> {
     }
 }
 
+pub trait InferCtxtEvalExt<'tcx> {
+    /// Evaluates a goal from **outside** of the trait solver.
+    ///
+    /// Using this while inside of the solver is wrong as it uses a new
+    /// search graph which would break cycle detection.
+    fn evaluate_root_goal(
+        &self,
+        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+    ) -> Result<(bool, Certainty), NoSolution>;
+}
+
+impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
+    fn evaluate_root_goal(
+        &self,
+        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+    ) -> Result<(bool, Certainty), NoSolution> {
+        let mut search_graph = search_graph::SearchGraph::new(self.tcx);
+
+        let result = EvalCtxt {
+            search_graph: &mut search_graph,
+            infcx: self,
+            var_values: CanonicalVarValues::dummy(),
+        }
+        .evaluate_goal(goal);
+
+        assert!(search_graph.is_empty());
+        result
+    }
+}
+
 struct EvalCtxt<'a, 'tcx> {
     infcx: &'a InferCtxt<'tcx>,
     var_values: CanonicalVarValues<'tcx>,
@@ -164,18 +194,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
         self.infcx.tcx
     }
 
-    /// Creates a new evaluation context outside of the trait solver.
-    ///
-    /// With this solver making a canonical response doesn't make much sense.
-    /// The `search_graph` for this solver has to be completely empty.
-    fn new_outside_solver(
-        infcx: &'a InferCtxt<'tcx>,
-        search_graph: &'a mut search_graph::SearchGraph<'tcx>,
-    ) -> EvalCtxt<'a, 'tcx> {
-        assert!(search_graph.is_empty());
-        EvalCtxt { infcx, var_values: CanonicalVarValues::dummy(), search_graph }
-    }
-
     #[instrument(level = "debug", skip(tcx, search_graph), ret)]
     fn evaluate_canonical_goal(
         tcx: TyCtxt<'tcx>,
diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs
index 2a7ec544f9e2e..0145ba5e3cde6 100644
--- a/library/core/src/fmt/mod.rs
+++ b/library/core/src/fmt/mod.rs
@@ -489,9 +489,26 @@ pub struct Arguments<'a> {
 }
 
 impl<'a> Arguments<'a> {
-    /// Get the formatted string, if it has no arguments to be formatted.
+    /// Get the formatted string, if it has no arguments to be formatted at runtime.
     ///
-    /// This can be used to avoid allocations in the most trivial case.
+    /// This can be used to avoid allocations in some cases.
+    ///
+    /// # Guarantees
+    ///
+    /// For `format_args!("just a literal")`, this function is guaranteed to
+    /// return `Some("just a literal")`.
+    ///
+    /// For most cases with placeholders, this function will return `None`.
+    ///
+    /// However, the compiler may perform optimizations that can cause this
+    /// function to return `Some(_)` even if the format string contains
+    /// placeholders. For example, `format_args!("Hello, {}!", "world")` may be
+    /// optimized to `format_args!("Hello, world!")`, such that `as_str()`
+    /// returns `Some("Hello, world!")`.
+    ///
+    /// The behavior for anything but the trivial case (without placeholders)
+    /// is not guaranteed, and should not be relied upon for anything other
+    /// than optimization.
     ///
     /// # Examples
     ///
@@ -512,7 +529,7 @@ impl<'a> Arguments<'a> {
     /// ```rust
     /// assert_eq!(format_args!("hello").as_str(), Some("hello"));
     /// assert_eq!(format_args!("").as_str(), Some(""));
-    /// assert_eq!(format_args!("{}", 1).as_str(), None);
+    /// assert_eq!(format_args!("{:?}", std::env::current_dir()).as_str(), None);
     /// ```
     #[stable(feature = "fmt_as_str", since = "1.52.0")]
     #[rustc_const_unstable(feature = "const_arguments_as_str", issue = "103900")]
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 7b1cb5488bcac..16eb726f6f614 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -731,7 +731,7 @@ impl<T: ?Sized> *const T {
     /// This computes the same value that [`offset_from`](#method.offset_from)
     /// would compute, but with the added precondition that the offset is
     /// guaranteed to be non-negative.  This method is equivalent to
-    /// `usize::from(self.offset_from(origin)).unwrap_unchecked()`,
+    /// `usize::try_from(self.offset_from(origin)).unwrap_unchecked()`,
     /// but it provides slightly more information to the optimizer, which can
     /// sometimes allow it to optimize slightly better with some backends.
     ///
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index ed1e3bd481227..0a2f63e3ec6a5 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -904,7 +904,7 @@ impl<T: ?Sized> *mut T {
     /// This computes the same value that [`offset_from`](#method.offset_from)
     /// would compute, but with the added precondition that the offset is
     /// guaranteed to be non-negative.  This method is equivalent to
-    /// `usize::from(self.offset_from(origin)).unwrap_unchecked()`,
+    /// `usize::try_from(self.offset_from(origin)).unwrap_unchecked()`,
     /// but it provides slightly more information to the optimizer, which can
     /// sometimes allow it to optimize slightly better with some backends.
     ///
diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs
index 796796e07a9c1..9d22ebbee873b 100644
--- a/library/test/src/cli.rs
+++ b/library/test/src/cli.rs
@@ -309,7 +309,8 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
 // FIXME: Copied from librustc_ast until linkage errors are resolved. Issue #47566
 fn is_nightly() -> bool {
     // Whether this is a feature-staged build, i.e., on the beta or stable channel
-    let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
+    let disable_unstable_features =
+        option_env!("CFG_DISABLE_UNSTABLE_FEATURES").map(|s| s != "0").unwrap_or(false);
     // Whether we should enable unstable features for bootstrapping
     let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();
 
diff --git a/src/librustdoc/html/static/css/settings.css b/src/librustdoc/html/static/css/settings.css
index 4e9803fe2366d..c28cefebc8bf5 100644
--- a/src/librustdoc/html/static/css/settings.css
+++ b/src/librustdoc/html/static/css/settings.css
@@ -3,8 +3,7 @@
 	position: relative;
 }
 
-.setting-line .radio-line input,
-.setting-line .settings-toggle input {
+.setting-radio input, .setting-check input {
 	margin-right: 0.3em;
 	height: 1.2rem;
 	width: 1.2rem;
@@ -14,21 +13,20 @@
 	-webkit-appearance: none;
 	cursor: pointer;
 }
-.setting-line .radio-line input {
+.setting-radio input {
 	border-radius: 50%;
 }
-.setting-line .settings-toggle input:checked {
+.setting-check input:checked {
 	content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">\
 		<path d="M7,25L17,32L33,12" fill="none" stroke="black" stroke-width="5"/>\
 		<path d="M7,23L17,30L33,10" fill="none" stroke="white" stroke-width="5"/></svg>');
 }
 
-.setting-line .radio-line input + span,
-.setting-line .settings-toggle span {
+.setting-radio span, .setting-check span {
 	padding-bottom: 1px;
 }
 
-.radio-line .choice {
+.setting-radio {
 	margin-top: 0.1em;
 	margin-bottom: 0.1em;
 	min-width: 3.8em;
@@ -37,11 +35,11 @@
 	align-items: center;
 	cursor: pointer;
 }
-.radio-line .choice + .choice {
+.setting-radio + .setting-radio {
 	margin-left: 0.5em;
 }
 
-.settings-toggle {
+.setting-check {
 	position: relative;
 	width: 100%;
 	margin-right: 20px;
@@ -50,23 +48,21 @@
 	cursor: pointer;
 }
 
-.setting-line .radio-line input:checked {
+.setting-radio input:checked {
 	box-shadow: inset 0 0 0 3px var(--main-background-color);
 	background-color: var(--settings-input-color);
 }
-.setting-line .settings-toggle input:checked {
+.setting-check input:checked {
 	background-color: var(--settings-input-color);
 }
-.setting-line .radio-line input:focus,
-.setting-line .settings-toggle input:focus {
+.setting-radio input:focus, .setting-check input:focus {
 	box-shadow: 0 0 1px 1px var(--settings-input-color);
 }
 /* In here we combine both `:focus` and `:checked` properties. */
-.setting-line .radio-line input:checked:focus {
+.setting-radio input:checked:focus {
 	box-shadow: inset 0 0 0 3px var(--main-background-color),
 		0 0 2px 2px var(--settings-input-color);
 }
-.setting-line .radio-line input:hover,
-.setting-line .settings-toggle input:hover {
+.setting-radio input:hover, .setting-check input:hover {
 	border-color: var(--settings-input-color) !important;
 }
diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js
index 84df1b7d3911a..a841b4b63bae8 100644
--- a/src/librustdoc/html/static/js/settings.js
+++ b/src/librustdoc/html/static/js/settings.js
@@ -48,13 +48,13 @@
     }
 
     function showLightAndDark() {
-        removeClass(document.getElementById("preferred-light-theme").parentElement, "hidden");
-        removeClass(document.getElementById("preferred-dark-theme").parentElement, "hidden");
+        removeClass(document.getElementById("preferred-light-theme"), "hidden");
+        removeClass(document.getElementById("preferred-dark-theme"), "hidden");
     }
 
     function hideLightAndDark() {
-        addClass(document.getElementById("preferred-light-theme").parentElement, "hidden");
-        addClass(document.getElementById("preferred-dark-theme").parentElement, "hidden");
+        addClass(document.getElementById("preferred-light-theme"), "hidden");
+        addClass(document.getElementById("preferred-dark-theme"), "hidden");
     }
 
     function updateLightAndDark() {
@@ -80,17 +80,6 @@
             toggle.onkeyup = handleKey;
             toggle.onkeyrelease = handleKey;
         });
-        onEachLazy(settingsElement.getElementsByClassName("select-wrapper"), elem => {
-            const select = elem.getElementsByTagName("select")[0];
-            const settingId = select.id;
-            const settingValue = getSettingValue(settingId);
-            if (settingValue !== null) {
-                select.value = settingValue;
-            }
-            select.onchange = function() {
-                changeSetting(this.id, this.value);
-            };
-        });
         onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => {
             const settingId = elem.name;
             let settingValue = getSettingValue(settingId);
@@ -127,38 +116,40 @@
         let output = "";
 
         for (const setting of settings) {
-            output += "<div class=\"setting-line\">";
             const js_data_name = setting["js_name"];
             const setting_name = setting["name"];
 
             if (setting["options"] !== undefined) {
                 // This is a select setting.
                 output += `\
-<div class="radio-line" id="${js_data_name}">
-    <div class="setting-name">${setting_name}</div>
-<div class="choices">`;
+<div class="setting-line" id="${js_data_name}">
+    <div class="setting-radio-name">${setting_name}</div>
+    <div class="setting-radio-choices">`;
                 onEach(setting["options"], option => {
                     const checked = option === setting["default"] ? " checked" : "";
                     const full = `${js_data_name}-${option.replace(/ /g,"-")}`;
 
                     output += `\
-<label for="${full}" class="choice">
-    <input type="radio" name="${js_data_name}"
-        id="${full}" value="${option}"${checked}>
-    <span>${option}</span>
-</label>`;
+        <label for="${full}" class="setting-radio">
+            <input type="radio" name="${js_data_name}"
+                id="${full}" value="${option}"${checked}>
+            <span>${option}</span>
+        </label>`;
                 });
-                output += "</div></div>";
+                output += `\
+    </div>
+</div>`;
             } else {
                 // This is a checkbox toggle.
                 const checked = setting["default"] === true ? " checked" : "";
                 output += `\
-<label class="settings-toggle">\
-    <input type="checkbox" id="${js_data_name}"${checked}>\
-    <span class="label">${setting_name}</span>\
-</label>`;
+<div class="setting-line">\
+    <label class="setting-check">\
+        <input type="checkbox" id="${js_data_name}"${checked}>\
+        <span>${setting_name}</span>\
+    </label>\
+</div>`;
             }
-            output += "</div>";
         }
         return output;
     }
diff --git a/tests/rustdoc-gui/mobile.goml b/tests/rustdoc-gui/mobile.goml
index 895864d89445f..3e444cbd6dc99 100644
--- a/tests/rustdoc-gui/mobile.goml
+++ b/tests/rustdoc-gui/mobile.goml
@@ -28,7 +28,7 @@ goto: "file://" + |DOC_PATH| + "/settings.html"
 size: (400, 600)
 // Ignored for now https://github.com/rust-lang/rust/issues/93784.
 // compare-elements-position-near-false: (
-//     "#preferred-light-theme .setting-name",
-//     "#preferred-light-theme .choice",
+//     "#preferred-light-theme .setting-radio-name",
+//     "#preferred-light-theme .setting-radio",
 //     {"y": 16},
 // )
diff --git a/tests/rustdoc-gui/settings.goml b/tests/rustdoc-gui/settings.goml
index 419cc5ebac35d..a841728857808 100644
--- a/tests/rustdoc-gui/settings.goml
+++ b/tests/rustdoc-gui/settings.goml
@@ -43,12 +43,12 @@ wait-for: "#settings"
 // We check that the "Use system theme" is disabled.
 assert-property: ("#theme-system-preference", {"checked": "false"})
 // Meaning that only the "theme" menu is showing up.
-assert: ".setting-line:not(.hidden) #theme"
-assert: ".setting-line.hidden #preferred-dark-theme"
-assert: ".setting-line.hidden #preferred-light-theme"
+assert: "#theme.setting-line:not(.hidden)"
+assert: "#preferred-dark-theme.setting-line.hidden"
+assert: "#preferred-light-theme.setting-line.hidden"
 
 // We check that the correct theme is selected.
-assert-property: ("#theme .choices #theme-dark", {"checked": "true"})
+assert-property: ("#theme .setting-radio-choices #theme-dark", {"checked": "true"})
 
 // Some style checks...
 move-cursor-to: "#settings-menu > a"
@@ -109,31 +109,31 @@ assert-css: (
         "box-shadow": "rgb(33, 150, 243) 0px 0px 1px 1px",
     },
 )
-// Now we check the setting-name for radio buttons is on a different line than the label.
+// Now we check the setting-radio-name is on a different line than the label.
 compare-elements-position-near: (
-    "#theme .setting-name",
-    "#theme .choices",
+    "#theme .setting-radio-name",
+    "#theme .setting-radio-choices",
     {"x": 1}
 )
 compare-elements-position-near-false: (
-    "#theme .setting-name",
-    "#theme .choices",
+    "#theme .setting-radio-name",
+    "#theme .setting-radio-choices",
     {"y": 1}
 )
 // Now we check that the label positions are all on the same line.
 compare-elements-position-near: (
-    "#theme .choices #theme-light",
-    "#theme .choices #theme-dark",
+    "#theme .setting-radio-choices #theme-light",
+    "#theme .setting-radio-choices #theme-dark",
     {"y": 1}
 )
 compare-elements-position-near: (
-    "#theme .choices #theme-dark",
-    "#theme .choices #theme-ayu",
+    "#theme .setting-radio-choices #theme-dark",
+    "#theme .setting-radio-choices #theme-ayu",
     {"y": 1}
 )
 compare-elements-position-near: (
-    "#theme .choices #theme-ayu",
-    "#theme .choices #theme-system-preference",
+    "#theme .setting-radio-choices #theme-ayu",
+    "#theme .setting-radio-choices #theme-system-preference",
     {"y": 1}
 )
 
@@ -180,17 +180,17 @@ assert-css: (
 // We now switch the display.
 click: "#theme-system-preference"
 // Wait for the hidden element to show up.
-wait-for: ".setting-line:not(.hidden) #preferred-dark-theme"
-assert: ".setting-line:not(.hidden) #preferred-light-theme"
+wait-for: "#preferred-dark-theme.setting-line:not(.hidden)"
+assert: "#preferred-light-theme.setting-line:not(.hidden)"
 
 // We check their text as well.
-assert-text: ("#preferred-dark-theme .setting-name", "Preferred dark theme")
-assert-text: ("#preferred-light-theme .setting-name", "Preferred light theme")
+assert-text: ("#preferred-dark-theme .setting-radio-name", "Preferred dark theme")
+assert-text: ("#preferred-light-theme .setting-radio-name", "Preferred light theme")
 
 // We now check that clicking on the toggles' text is like clicking on the checkbox.
 // To test it, we use the "Disable keyboard shortcuts".
 local-storage: {"rustdoc-disable-shortcuts": "false"}
-click: ".setting-line:last-child .settings-toggle .label"
+click: ".setting-line:last-child .setting-check span"
 assert-local-storage: {"rustdoc-disable-shortcuts": "true"}
 
 // Make sure that "Disable keyboard shortcuts" actually took effect.
@@ -200,7 +200,7 @@ assert-false: "#help-button .popover"
 wait-for-css: ("#settings-menu .popover", {"display": "block"})
 
 // Now turn keyboard shortcuts back on, and see if they work.
-click: ".setting-line:last-child .settings-toggle .label"
+click: ".setting-line:last-child .setting-check span"
 assert-local-storage: {"rustdoc-disable-shortcuts": "false"}
 press-key: "Escape"
 press-key: "?"
diff --git a/tests/rustdoc-gui/theme-change.goml b/tests/rustdoc-gui/theme-change.goml
index cc47f1f450c5a..31c9d99aa8324 100644
--- a/tests/rustdoc-gui/theme-change.goml
+++ b/tests/rustdoc-gui/theme-change.goml
@@ -43,7 +43,7 @@ assert-local-storage: { "rustdoc-theme": "ayu" }
 
 assert-local-storage-false: { "rustdoc-use-system-theme": "true" }
 click: "#theme-system-preference"
-wait-for: ".setting-line:not(.hidden) #preferred-light-theme"
+wait-for: "#preferred-light-theme.setting-line:not(.hidden)"
 assert-local-storage: { "rustdoc-use-system-theme": "true" }
 // We click on both preferred light and dark themes to be sure that there is a change.
 click: "#preferred-light-theme-dark"
@@ -52,16 +52,16 @@ wait-for-css: ("body", { "background-color": |background_dark| })
 
 reload:
 // Ensure that the "preferred themes" are still displayed.
-wait-for: ".setting-line:not(.hidden) #preferred-light-theme"
+wait-for: "#preferred-light-theme.setting-line:not(.hidden)"
 click: "#theme-light"
 wait-for-css: ("body", { "background-color": |background_light| })
 assert-local-storage: { "rustdoc-theme": "light" }
 // Ensure it's now hidden again
-wait-for: ".setting-line.hidden #preferred-light-theme"
+wait-for: "#preferred-light-theme.setting-line.hidden"
 // And ensure the theme was rightly set.
 wait-for-css: ("body", { "background-color": |background_light| })
 assert-local-storage: { "rustdoc-theme": "light" }
 
 reload:
 wait-for: "#settings"
-assert: ".setting-line.hidden #preferred-light-theme"
+assert: "#preferred-light-theme.setting-line.hidden"
diff --git a/tests/ui/fn/fn-compare-mismatch.stderr b/tests/ui/fn/fn-compare-mismatch.stderr
index df838cb118105..f247ff6cf3f55 100644
--- a/tests/ui/fn/fn-compare-mismatch.stderr
+++ b/tests/ui/fn/fn-compare-mismatch.stderr
@@ -19,6 +19,7 @@ LL |     let x = f == g;
    |
    = note: expected fn item `fn() {f}`
               found fn item `fn() {g}`
+   = note: different fn items have unique types, even if their signatures are the same
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/fn/fn-item-type.rs b/tests/ui/fn/fn-item-type.rs
index 1831e6cbf1050..b6ebc867d284b 100644
--- a/tests/ui/fn/fn-item-type.rs
+++ b/tests/ui/fn/fn-item-type.rs
@@ -1,13 +1,22 @@
 // Test that the types of distinct fn items are not compatible by
 // default. See also `run-pass/fn-item-type-*.rs`.
 
-fn foo<T>(x: isize) -> isize { x * 2 }
-fn bar<T>(x: isize) -> isize { x * 4 }
+fn foo<T>(x: isize) -> isize {
+    x * 2
+}
+fn bar<T>(x: isize) -> isize {
+    x * 4
+}
 
-fn eq<T>(x: T, y: T) { }
+fn eq<T>(x: T, y: T) {}
 
-trait Foo { fn foo() { /* this is a default fn */ } }
-impl<T> Foo for T { /* `foo` is still default here */ }
+trait Foo {
+    fn foo() { /* this is a default fn */
+    }
+}
+impl<T> Foo for T {
+    /* `foo` is still default here */
+}
 
 fn main() {
     eq(foo::<u8>, bar::<u8>);
@@ -15,39 +24,29 @@ fn main() {
     //~| expected fn item `fn(_) -> _ {foo::<u8>}`
     //~| found fn item `fn(_) -> _ {bar::<u8>}`
     //~| expected fn item, found a different fn item
-    //~| different `fn` items always have unique types, even if their signatures are the same
-    //~| change the expected type to be function pointer
-    //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
+    //~| different fn items have unique types, even if their signatures are the same
 
     eq(foo::<u8>, foo::<i8>);
     //~^ ERROR mismatched types
     //~| expected `u8`, found `i8`
-    //~| different `fn` items always have unique types, even if their signatures are the same
-    //~| change the expected type to be function pointer
-    //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
+    //~| different fn items have unique types, even if their signatures are the same
 
     eq(bar::<String>, bar::<Vec<u8>>);
     //~^ ERROR mismatched types
     //~| found fn item `fn(_) -> _ {bar::<Vec<u8>>}`
     //~| expected struct `String`, found struct `Vec`
-    //~| different `fn` items always have unique types, even if their signatures are the same
-    //~| change the expected type to be function pointer
-    //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
+    //~| different fn items have unique types, even if their signatures are the same
 
     // Make sure we distinguish between trait methods correctly.
     eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
     //~^ ERROR mismatched types
     //~| expected `u8`, found `u16`
-    //~| different `fn` items always have unique types, even if their signatures are the same
-    //~| change the expected type to be function pointer
-    //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
+    //~| different fn items have unique types, even if their signatures are the same
 
     eq(foo::<u8>, bar::<u8> as fn(isize) -> isize);
     //~^ ERROR mismatched types
     //~| found fn pointer `fn(_) -> _`
     //~| expected fn item, found fn pointer
-    //~| change the expected type to be function pointer
-    //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
 
     eq(foo::<u8> as fn(isize) -> isize, bar::<u8>); // ok!
 }
diff --git a/tests/ui/fn/fn-item-type.stderr b/tests/ui/fn/fn-item-type.stderr
index f03a47d5c2c75..cb1b88c7ab8f3 100644
--- a/tests/ui/fn/fn-item-type.stderr
+++ b/tests/ui/fn/fn-item-type.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/fn-item-type.rs:13:19
+  --> $DIR/fn-item-type.rs:22:19
    |
 LL |     eq(foo::<u8>, bar::<u8>);
    |     --            ^^^^^^^^^ expected fn item, found a different fn item
@@ -8,17 +8,15 @@ LL |     eq(foo::<u8>, bar::<u8>);
    |
    = note: expected fn item `fn(_) -> _ {foo::<u8>}`
               found fn item `fn(_) -> _ {bar::<u8>}`
-   = note: different `fn` items always have unique types, even if their signatures are the same
-   = help: change the expected type to be function pointer `fn(isize) -> isize`
-   = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize`
+   = note: different fn items have unique types, even if their signatures are the same
 note: function defined here
-  --> $DIR/fn-item-type.rs:7:4
+  --> $DIR/fn-item-type.rs:11:4
    |
-LL | fn eq<T>(x: T, y: T) { }
+LL | fn eq<T>(x: T, y: T) {}
    |    ^^          ----
 
 error[E0308]: mismatched types
-  --> $DIR/fn-item-type.rs:22:19
+  --> $DIR/fn-item-type.rs:29:19
    |
 LL |     eq(foo::<u8>, foo::<i8>);
    |     --            ^^^^^^^^^ expected `u8`, found `i8`
@@ -27,17 +25,15 @@ LL |     eq(foo::<u8>, foo::<i8>);
    |
    = note: expected fn item `fn(_) -> _ {foo::<u8>}`
               found fn item `fn(_) -> _ {foo::<i8>}`
-   = note: different `fn` items always have unique types, even if their signatures are the same
-   = help: change the expected type to be function pointer `fn(isize) -> isize`
-   = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize`
+   = note: different fn items have unique types, even if their signatures are the same
 note: function defined here
-  --> $DIR/fn-item-type.rs:7:4
+  --> $DIR/fn-item-type.rs:11:4
    |
-LL | fn eq<T>(x: T, y: T) { }
+LL | fn eq<T>(x: T, y: T) {}
    |    ^^          ----
 
 error[E0308]: mismatched types
-  --> $DIR/fn-item-type.rs:29:23
+  --> $DIR/fn-item-type.rs:34:23
    |
 LL |     eq(bar::<String>, bar::<Vec<u8>>);
    |     --                ^^^^^^^^^^^^^^ expected struct `String`, found struct `Vec`
@@ -46,17 +42,15 @@ LL |     eq(bar::<String>, bar::<Vec<u8>>);
    |
    = note: expected fn item `fn(_) -> _ {bar::<String>}`
               found fn item `fn(_) -> _ {bar::<Vec<u8>>}`
-   = note: different `fn` items always have unique types, even if their signatures are the same
-   = help: change the expected type to be function pointer `fn(isize) -> isize`
-   = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `bar::<String> as fn(isize) -> isize`
+   = note: different fn items have unique types, even if their signatures are the same
 note: function defined here
-  --> $DIR/fn-item-type.rs:7:4
+  --> $DIR/fn-item-type.rs:11:4
    |
-LL | fn eq<T>(x: T, y: T) { }
+LL | fn eq<T>(x: T, y: T) {}
    |    ^^          ----
 
 error[E0308]: mismatched types
-  --> $DIR/fn-item-type.rs:38:26
+  --> $DIR/fn-item-type.rs:41:26
    |
 LL |     eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
    |     --                   ^^^^^^^^^^^^^^^^^ expected `u8`, found `u16`
@@ -65,17 +59,15 @@ LL |     eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
    |
    = note: expected fn item `fn() {<u8 as Foo>::foo}`
               found fn item `fn() {<u16 as Foo>::foo}`
-   = note: different `fn` items always have unique types, even if their signatures are the same
-   = help: change the expected type to be function pointer `fn()`
-   = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `<u8 as Foo>::foo as fn()`
+   = note: different fn items have unique types, even if their signatures are the same
 note: function defined here
-  --> $DIR/fn-item-type.rs:7:4
+  --> $DIR/fn-item-type.rs:11:4
    |
-LL | fn eq<T>(x: T, y: T) { }
+LL | fn eq<T>(x: T, y: T) {}
    |    ^^          ----
 
 error[E0308]: mismatched types
-  --> $DIR/fn-item-type.rs:45:19
+  --> $DIR/fn-item-type.rs:46:19
    |
 LL |     eq(foo::<u8>, bar::<u8> as fn(isize) -> isize);
    |     --            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer
@@ -84,12 +76,11 @@ LL |     eq(foo::<u8>, bar::<u8> as fn(isize) -> isize);
    |
    = note: expected fn item `fn(_) -> _ {foo::<u8>}`
            found fn pointer `fn(_) -> _`
-   = help: change the expected type to be function pointer `fn(isize) -> isize`
-   = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize`
+   = note: fn items are distinct from fn pointers
 note: function defined here
-  --> $DIR/fn-item-type.rs:7:4
+  --> $DIR/fn-item-type.rs:11:4
    |
-LL | fn eq<T>(x: T, y: T) { }
+LL | fn eq<T>(x: T, y: T) {}
    |    ^^          ----
 
 error: aborting due to 5 previous errors
diff --git a/tests/ui/fn/fn-pointer-mismatch.rs b/tests/ui/fn/fn-pointer-mismatch.rs
new file mode 100644
index 0000000000000..0597478cb4292
--- /dev/null
+++ b/tests/ui/fn/fn-pointer-mismatch.rs
@@ -0,0 +1,56 @@
+fn foo(x: u32) -> u32 {
+    x * 2
+}
+
+fn bar(x: u32) -> u32 {
+    x * 3
+}
+
+// original example from Issue #102608
+fn foobar(n: u32) -> u32 {
+    let g = if n % 2 == 0 { &foo } else { &bar };
+    //~^ ERROR `if` and `else` have incompatible types
+    //~| different fn items have unique types, even if their signatures are the same
+    g(n)
+}
+
+fn main() {
+    assert_eq!(foobar(7), 21);
+    assert_eq!(foobar(8), 16);
+
+    // general mismatch of fn item types
+    let mut a = foo;
+    a = bar;
+    //~^ ERROR mismatched types
+    //~| expected fn item `fn(_) -> _ {foo}`
+    //~| found fn item `fn(_) -> _ {bar}`
+    //~| different fn items have unique types, even if their signatures are the same
+
+    // display note even when boxed
+    let mut b = Box::new(foo);
+    b = Box::new(bar);
+    //~^ ERROR mismatched types
+    //~| different fn items have unique types, even if their signatures are the same
+
+    // suggest removing reference
+    let c: fn(u32) -> u32 = &foo;
+    //~^ ERROR mismatched types
+    //~| expected fn pointer `fn(u32) -> u32`
+    //~| found reference `&fn(u32) -> u32 {foo}`
+
+    // suggest using reference
+    let d: &fn(u32) -> u32 = foo;
+    //~^ ERROR mismatched types
+    //~| expected reference `&fn(u32) -> u32`
+    //~| found fn item `fn(u32) -> u32 {foo}`
+
+    // suggest casting with reference
+    let e: &fn(u32) -> u32 = &foo;
+    //~^ ERROR mismatched types
+    //~| expected reference `&fn(u32) -> u32`
+    //~| found reference `&fn(u32) -> u32 {foo}`
+
+    // OK
+    let mut z: fn(u32) -> u32 = foo as fn(u32) -> u32;
+    z = bar;
+}
diff --git a/tests/ui/fn/fn-pointer-mismatch.stderr b/tests/ui/fn/fn-pointer-mismatch.stderr
new file mode 100644
index 0000000000000..2dc0710e27e46
--- /dev/null
+++ b/tests/ui/fn/fn-pointer-mismatch.stderr
@@ -0,0 +1,81 @@
+error[E0308]: `if` and `else` have incompatible types
+  --> $DIR/fn-pointer-mismatch.rs:11:43
+   |
+LL |     let g = if n % 2 == 0 { &foo } else { &bar };
+   |                             ----          ^^^^ expected fn item, found a different fn item
+   |                             |
+   |                             expected because of this
+   |
+   = note: expected reference `&fn(u32) -> u32 {foo}`
+              found reference `&fn(u32) -> u32 {bar}`
+   = note: different fn items have unique types, even if their signatures are the same
+
+error[E0308]: mismatched types
+  --> $DIR/fn-pointer-mismatch.rs:23:9
+   |
+LL |     let mut a = foo;
+   |                 --- expected due to this value
+LL |     a = bar;
+   |         ^^^ expected fn item, found a different fn item
+   |
+   = note: expected fn item `fn(_) -> _ {foo}`
+              found fn item `fn(_) -> _ {bar}`
+   = note: different fn items have unique types, even if their signatures are the same
+
+error[E0308]: mismatched types
+  --> $DIR/fn-pointer-mismatch.rs:31:18
+   |
+LL |     b = Box::new(bar);
+   |         -------- ^^^ expected fn item, found a different fn item
+   |         |
+   |         arguments to this function are incorrect
+   |
+   = note: expected fn item `fn(_) -> _ {foo}`
+              found fn item `fn(_) -> _ {bar}`
+   = note: different fn items have unique types, even if their signatures are the same
+note: associated function defined here
+  --> $SRC_DIR/alloc/src/boxed.rs:LL:COL
+
+error[E0308]: mismatched types
+  --> $DIR/fn-pointer-mismatch.rs:36:29
+   |
+LL |     let c: fn(u32) -> u32 = &foo;
+   |            --------------   ^^^^
+   |            |                |
+   |            |                expected fn pointer, found reference
+   |            |                help: consider removing the reference: `foo`
+   |            expected due to this
+   |
+   = note: expected fn pointer `fn(u32) -> u32`
+               found reference `&fn(u32) -> u32 {foo}`
+
+error[E0308]: mismatched types
+  --> $DIR/fn-pointer-mismatch.rs:42:30
+   |
+LL |     let d: &fn(u32) -> u32 = foo;
+   |            ---------------   ^^^
+   |            |                 |
+   |            |                 expected `&fn(u32) -> u32`, found fn item
+   |            |                 help: consider using a reference: `&foo`
+   |            expected due to this
+   |
+   = note: expected reference `&fn(u32) -> u32`
+                found fn item `fn(u32) -> u32 {foo}`
+
+error[E0308]: mismatched types
+  --> $DIR/fn-pointer-mismatch.rs:48:30
+   |
+LL |     let e: &fn(u32) -> u32 = &foo;
+   |            ---------------   ^^^^
+   |            |                 |
+   |            |                 expected fn pointer, found fn item
+   |            |                 help: consider casting to a fn pointer: `&(foo as fn(u32) -> u32)`
+   |            expected due to this
+   |
+   = note: expected reference `&fn(u32) -> u32`
+              found reference `&fn(u32) -> u32 {foo}`
+   = note: fn items are distinct from fn pointers
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/let-else/accidental-if.rs b/tests/ui/let-else/accidental-if.rs
new file mode 100644
index 0000000000000..3fba630435c71
--- /dev/null
+++ b/tests/ui/let-else/accidental-if.rs
@@ -0,0 +1,6 @@
+fn main() {
+    let x = Some(123);
+    if let Some(y) = x else { //~ ERROR this `if` expression is missing a block
+        return;
+    };
+}
diff --git a/tests/ui/let-else/accidental-if.stderr b/tests/ui/let-else/accidental-if.stderr
new file mode 100644
index 0000000000000..5474a67aac45a
--- /dev/null
+++ b/tests/ui/let-else/accidental-if.stderr
@@ -0,0 +1,19 @@
+error: this `if` expression is missing a block after the condition
+  --> $DIR/accidental-if.rs:3:5
+   |
+LL |     if let Some(y) = x else {
+   |     ^^
+   |
+help: add a block here
+  --> $DIR/accidental-if.rs:3:23
+   |
+LL |     if let Some(y) = x else {
+   |                       ^
+help: remove the `if` if you meant to write a `let...else` statement
+  --> $DIR/accidental-if.rs:3:5
+   |
+LL |     if let Some(y) = x else {
+   |     ^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/reify-intrinsic.stderr b/tests/ui/reify-intrinsic.stderr
index f78f1d822bf60..310b6c224e0e7 100644
--- a/tests/ui/reify-intrinsic.stderr
+++ b/tests/ui/reify-intrinsic.stderr
@@ -23,9 +23,7 @@ LL |         std::intrinsics::unlikely,
    |
    = note: expected fn item `extern "rust-intrinsic" fn(_) -> _ {likely}`
               found fn item `extern "rust-intrinsic" fn(_) -> _ {unlikely}`
-   = note: different `fn` items always have unique types, even if their signatures are the same
-   = help: change the expected type to be function pointer `extern "rust-intrinsic" fn(bool) -> bool`
-   = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `likely as extern "rust-intrinsic" fn(bool) -> bool`
+   = note: different fn items have unique types, even if their signatures are the same
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr b/tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr
index 498a112fa9bb3..f34ccecdd45e6 100644
--- a/tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr
+++ b/tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr
@@ -37,6 +37,11 @@ help: add a block here
    |
 LL |     if let Some(n) = opt else {
    |                         ^
+help: remove the `if` if you meant to write a `let...else` statement
+  --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:24:5
+   |
+LL |     if let Some(n) = opt else {
+   |     ^^
 
 error: this `if` expression is missing a block after the condition
   --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:28:5
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr
index cf5815df56e1c..07f6dc906c6bb 100644
--- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr
+++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.mir.stderr
@@ -5,12 +5,15 @@ LL | #[target_feature(enable = "sse2")]
    | ---------------------------------- `#[target_feature]` added here
 ...
 LL |     let foo: fn() = foo;
-   |              ----   ^^^ cannot coerce functions with `#[target_feature]` to safe function pointers
-   |              |
+   |              ----   ^^^
+   |              |      |
+   |              |      cannot coerce functions with `#[target_feature]` to safe function pointers
+   |              |      help: consider casting to a fn pointer: `foo as fn()`
    |              expected due to this
    |
    = note: expected fn pointer `fn()`
                  found fn item `fn() {foo}`
+   = note: fn items are distinct from fn pointers
    = note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers
 
 error: aborting due to previous error
diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr
index cf5815df56e1c..07f6dc906c6bb 100644
--- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr
+++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.thir.stderr
@@ -5,12 +5,15 @@ LL | #[target_feature(enable = "sse2")]
    | ---------------------------------- `#[target_feature]` added here
 ...
 LL |     let foo: fn() = foo;
-   |              ----   ^^^ cannot coerce functions with `#[target_feature]` to safe function pointers
-   |              |
+   |              ----   ^^^
+   |              |      |
+   |              |      cannot coerce functions with `#[target_feature]` to safe function pointers
+   |              |      help: consider casting to a fn pointer: `foo as fn()`
    |              expected due to this
    |
    = note: expected fn pointer `fn()`
                  found fn item `fn() {foo}`
+   = note: fn items are distinct from fn pointers
    = note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers
 
 error: aborting due to previous error
diff --git a/tests/ui/static/static-reference-to-fn-1.stderr b/tests/ui/static/static-reference-to-fn-1.stderr
index 67b478bdb75c3..f68939d0ec8c9 100644
--- a/tests/ui/static/static-reference-to-fn-1.stderr
+++ b/tests/ui/static/static-reference-to-fn-1.stderr
@@ -2,10 +2,14 @@ error[E0308]: mismatched types
   --> $DIR/static-reference-to-fn-1.rs:17:15
    |
 LL |         func: &foo,
-   |               ^^^^ expected fn pointer, found fn item
+   |               ^^^^
+   |               |
+   |               expected fn pointer, found fn item
+   |               help: consider casting to a fn pointer: `&(foo as fn() -> Option<isize>)`
    |
    = note: expected reference `&fn() -> Option<isize>`
               found reference `&fn() -> Option<isize> {foo}`
+   = note: fn items are distinct from fn pointers
 
 error: aborting due to previous error
 
diff --git a/tests/ui/type/type-check/coerce-result-return-value-2.rs b/tests/ui/type/type-check/coerce-result-return-value-2.rs
new file mode 100644
index 0000000000000..23bafa6c5c94c
--- /dev/null
+++ b/tests/ui/type/type-check/coerce-result-return-value-2.rs
@@ -0,0 +1,24 @@
+struct A;
+struct B;
+impl From<A> for B {
+    fn from(_: A) -> Self { B }
+}
+fn foo4(x: Result<(), A>) -> Result<(), B> {
+    match true {
+        true => x, //~ ERROR mismatched types
+        false => x,
+    }
+}
+fn foo5(x: Result<(), A>) -> Result<(), B> {
+    match true {
+        true => return x, //~ ERROR mismatched types
+        false => return x,
+    }
+}
+fn main() {
+    let _ = foo4(Ok(()));
+    let _ = foo5(Ok(()));
+    let _: Result<(), B> = { //~ ERROR mismatched types
+        Err(A);
+    };
+}
diff --git a/tests/ui/type/type-check/coerce-result-return-value-2.stderr b/tests/ui/type/type-check/coerce-result-return-value-2.stderr
new file mode 100644
index 0000000000000..5992162341e6e
--- /dev/null
+++ b/tests/ui/type/type-check/coerce-result-return-value-2.stderr
@@ -0,0 +1,47 @@
+error[E0308]: mismatched types
+  --> $DIR/coerce-result-return-value-2.rs:8:17
+   |
+LL | fn foo4(x: Result<(), A>) -> Result<(), B> {
+   |                              ------------- expected `Result<(), B>` because of return type
+LL |     match true {
+LL |         true => x,
+   |                 ^ expected struct `B`, found struct `A`
+   |
+   = note: expected enum `Result<_, B>`
+              found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+   |
+LL |         true => Ok(x?),
+   |                 +++ ++
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-result-return-value-2.rs:14:24
+   |
+LL | fn foo5(x: Result<(), A>) -> Result<(), B> {
+   |                              ------------- expected `Result<(), B>` because of return type
+LL |     match true {
+LL |         true => return x,
+   |                        ^ expected struct `B`, found struct `A`
+   |
+   = note: expected enum `Result<_, B>`
+              found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+   |
+LL |         true => return Ok(x?),
+   |                        +++ ++
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-result-return-value-2.rs:21:28
+   |
+LL |       let _: Result<(), B> = {
+   |  ____________________________^
+LL | |         Err(A);
+LL | |     };
+   | |_____^ expected enum `Result`, found `()`
+   |
+   = note:   expected enum `Result<(), B>`
+           found unit type `()`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/type/type-check/coerce-result-return-value.fixed b/tests/ui/type/type-check/coerce-result-return-value.fixed
new file mode 100644
index 0000000000000..8a05407070dad
--- /dev/null
+++ b/tests/ui/type/type-check/coerce-result-return-value.fixed
@@ -0,0 +1,24 @@
+// run-rustfix
+struct A;
+struct B;
+impl From<A> for B {
+    fn from(_: A) -> Self { B }
+}
+fn foo1(x: Result<(), A>) -> Result<(), B> {
+    Ok(x?) //~ ERROR mismatched types
+}
+fn foo2(x: Result<(), A>) -> Result<(), B> {
+    return Ok(x?); //~ ERROR mismatched types
+}
+fn foo3(x: Result<(), A>) -> Result<(), B> {
+    if true {
+        Ok(x?) //~ ERROR mismatched types
+    } else {
+        Ok(x?) //~ ERROR mismatched types
+    }
+}
+fn main() {
+    let _ = foo1(Ok(()));
+    let _ = foo2(Ok(()));
+    let _ = foo3(Ok(()));
+}
diff --git a/tests/ui/type/type-check/coerce-result-return-value.rs b/tests/ui/type/type-check/coerce-result-return-value.rs
new file mode 100644
index 0000000000000..442203addb787
--- /dev/null
+++ b/tests/ui/type/type-check/coerce-result-return-value.rs
@@ -0,0 +1,24 @@
+// run-rustfix
+struct A;
+struct B;
+impl From<A> for B {
+    fn from(_: A) -> Self { B }
+}
+fn foo1(x: Result<(), A>) -> Result<(), B> {
+    x //~ ERROR mismatched types
+}
+fn foo2(x: Result<(), A>) -> Result<(), B> {
+    return x; //~ ERROR mismatched types
+}
+fn foo3(x: Result<(), A>) -> Result<(), B> {
+    if true {
+        x //~ ERROR mismatched types
+    } else {
+        x //~ ERROR mismatched types
+    }
+}
+fn main() {
+    let _ = foo1(Ok(()));
+    let _ = foo2(Ok(()));
+    let _ = foo3(Ok(()));
+}
diff --git a/tests/ui/type/type-check/coerce-result-return-value.stderr b/tests/ui/type/type-check/coerce-result-return-value.stderr
new file mode 100644
index 0000000000000..550153520782c
--- /dev/null
+++ b/tests/ui/type/type-check/coerce-result-return-value.stderr
@@ -0,0 +1,65 @@
+error[E0308]: mismatched types
+  --> $DIR/coerce-result-return-value.rs:8:5
+   |
+LL | fn foo1(x: Result<(), A>) -> Result<(), B> {
+   |                              ------------- expected `Result<(), B>` because of return type
+LL |     x
+   |     ^ expected struct `B`, found struct `A`
+   |
+   = note: expected enum `Result<_, B>`
+              found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+   |
+LL |     Ok(x?)
+   |     +++ ++
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-result-return-value.rs:11:12
+   |
+LL | fn foo2(x: Result<(), A>) -> Result<(), B> {
+   |                              ------------- expected `Result<(), B>` because of return type
+LL |     return x;
+   |            ^ expected struct `B`, found struct `A`
+   |
+   = note: expected enum `Result<_, B>`
+              found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+   |
+LL |     return Ok(x?);
+   |            +++ ++
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-result-return-value.rs:15:9
+   |
+LL | fn foo3(x: Result<(), A>) -> Result<(), B> {
+   |                              ------------- expected `Result<(), B>` because of return type
+LL |     if true {
+LL |         x
+   |         ^ expected struct `B`, found struct `A`
+   |
+   = note: expected enum `Result<_, B>`
+              found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+   |
+LL |         Ok(x?)
+   |         +++ ++
+
+error[E0308]: mismatched types
+  --> $DIR/coerce-result-return-value.rs:17:9
+   |
+LL | fn foo3(x: Result<(), A>) -> Result<(), B> {
+   |                              ------------- expected `Result<(), B>` because of return type
+...
+LL |         x
+   |         ^ expected struct `B`, found struct `A`
+   |
+   = note: expected enum `Result<_, B>`
+              found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+   |
+LL |         Ok(x?)
+   |         +++ ++
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.