about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-10-06 11:43:07 +0000
committerbors <bors@rust-lang.org>2024-10-06 11:43:07 +0000
commit96d9d8aa7de9396fe22702ff6df33816ae4d01b6 (patch)
tree3e6361197c5c3ad473e23a54991a6da0afdd2160
parent68301a6a968326bac8b85b04d5ef7e79cc042a00 (diff)
parentdd09e9c74240fe7537cd55ddffb19b8162756a91 (diff)
downloadrust-96d9d8aa7de9396fe22702ff6df33816ae4d01b6.tar.gz
rust-96d9d8aa7de9396fe22702ff6df33816ae4d01b6.zip
Auto merge of #131320 - matthiaskrgr:rollup-tom15b3, r=matthiaskrgr
Rollup of 5 pull requests

Successful merges:

 - #129392 (Do not consider match/let/ref of place that evaluates to `!` to diverge, disallow coercions from them too)
 - #131279 (update "build/host" symlink comment)
 - #131312 (On function and method calls in patterns, link to the book)
 - #131315 (bootstrap: add `std_features` config)
 - #131316 (Fix typo in primitive_docs.rs)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs46
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs191
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs6
-rw-r--r--compiler/rustc_parse/messages.ftl3
-rw-r--r--compiler/rustc_parse/src/errors.rs1
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs13
-rw-r--r--config.example.toml10
-rw-r--r--library/core/src/primitive_docs.rs2
-rw-r--r--src/bootstrap/src/core/config/config.rs12
-rw-r--r--src/bootstrap/src/core/config/tests.rs22
-rw-r--r--src/bootstrap/src/lib.rs29
-rw-r--r--src/tools/miri/tests/pass/underscore_pattern.rs51
-rw-r--r--tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir3
-rw-r--r--tests/mir-opt/uninhabited_enum.process_void.SimplifyLocals-final.after.mir2
-rw-r--r--tests/mir-opt/uninhabited_enum.rs5
-rw-r--r--tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir49
-rw-r--r--tests/mir-opt/uninhabited_not_read.rs26
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr6
-rw-r--r--tests/ui/half-open-range-patterns/range_pat_interactions1.stderr3
-rw-r--r--tests/ui/half-open-range-patterns/range_pat_interactions2.stderr3
-rw-r--r--tests/ui/never_type/diverging-place-match.rs74
-rw-r--r--tests/ui/never_type/diverging-place-match.stderr142
-rw-r--r--tests/ui/parser/bad-name.stderr4
-rw-r--r--tests/ui/parser/issues/issue-24197.stderr4
-rw-r--r--tests/ui/parser/issues/issue-24375.stderr3
-rw-r--r--tests/ui/parser/pat-lt-bracket-5.stderr4
-rw-r--r--tests/ui/parser/pat-lt-bracket-6.stderr4
-rw-r--r--tests/ui/parser/pat-ranges-3.stderr8
-rw-r--r--tests/ui/parser/recover/recover-pat-exprs.stderr103
-rw-r--r--tests/ui/parser/recover/recover-pat-issues.stderr18
-rw-r--r--tests/ui/parser/recover/recover-pat-lets.stderr18
-rw-r--r--tests/ui/parser/recover/recover-pat-ranges.stderr18
-rw-r--r--tests/ui/parser/recover/recover-pat-wildcards.stderr3
-rw-r--r--tests/ui/raw-ref-op/never-place-isnt-diverging.rs22
-rw-r--r--tests/ui/raw-ref-op/never-place-isnt-diverging.stderr34
-rw-r--r--tests/ui/reachable/expr_assign.stderr9
-rw-r--r--tests/ui/resolve/issue-10200.rs2
-rw-r--r--tests/ui/resolve/issue-10200.stderr4
-rw-r--r--tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr4
39 files changed, 850 insertions, 111 deletions
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index fb462eec1b9..642db3f36b5 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -82,6 +82,11 @@ struct Coerce<'a, 'tcx> {
     /// See #47489 and #48598
     /// See docs on the "AllowTwoPhase" type for a more detailed discussion
     allow_two_phase: AllowTwoPhase,
+    /// Whether we allow `NeverToAny` coercions. This is unsound if we're
+    /// coercing a place expression without it counting as a read in the MIR.
+    /// This is a side-effect of HIR not really having a great distinction
+    /// between places and values.
+    coerce_never: bool,
 }
 
 impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> {
@@ -125,8 +130,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         fcx: &'f FnCtxt<'f, 'tcx>,
         cause: ObligationCause<'tcx>,
         allow_two_phase: AllowTwoPhase,
+        coerce_never: bool,
     ) -> Self {
-        Coerce { fcx, cause, allow_two_phase, use_lub: false }
+        Coerce { fcx, cause, allow_two_phase, use_lub: false, coerce_never }
     }
 
     fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
@@ -177,7 +183,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
 
         // Coercing from `!` to any type is allowed:
         if a.is_never() {
-            return success(simple(Adjust::NeverToAny)(b), b, vec![]);
+            if self.coerce_never {
+                return success(simple(Adjust::NeverToAny)(b), b, vec![]);
+            } else {
+                // Otherwise the only coercion we can do is unification.
+                return self.unify_and(a, b, identity);
+            }
         }
 
         // Coercing *from* an unresolved inference variable means that
@@ -1038,7 +1049,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// The expressions *must not* have any preexisting adjustments.
     pub(crate) fn coerce(
         &self,
-        expr: &hir::Expr<'_>,
+        expr: &'tcx hir::Expr<'tcx>,
         expr_ty: Ty<'tcx>,
         mut target: Ty<'tcx>,
         allow_two_phase: AllowTwoPhase,
@@ -1055,7 +1066,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let cause =
             cause.unwrap_or_else(|| self.cause(expr.span, ObligationCauseCode::ExprAssignable));
-        let coerce = Coerce::new(self, cause, allow_two_phase);
+        let coerce = Coerce::new(
+            self,
+            cause,
+            allow_two_phase,
+            self.expr_guaranteed_to_constitute_read_for_never(expr),
+        );
         let ok = self.commit_if_ok(|_| coerce.coerce(source, target))?;
 
         let (adjustments, _) = self.register_infer_ok_obligations(ok);
@@ -1077,8 +1093,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("coercion::can_with_predicates({:?} -> {:?})", source, target);
 
         let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable);
-        // We don't ever need two-phase here since we throw out the result of the coercion
-        let coerce = Coerce::new(self, cause, AllowTwoPhase::No);
+        // We don't ever need two-phase here since we throw out the result of the coercion.
+        // We also just always set `coerce_never` to true, since this is a heuristic.
+        let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true);
         self.probe(|_| {
             let Ok(ok) = coerce.coerce(source, target) else {
                 return false;
@@ -1090,12 +1107,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 
     /// Given a type and a target type, this function will calculate and return
-    /// how many dereference steps needed to achieve `expr_ty <: target`. If
+    /// how many dereference steps needed to coerce `expr_ty` to `target`. If
     /// it's not possible, return `None`.
-    pub(crate) fn deref_steps(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> Option<usize> {
+    pub(crate) fn deref_steps_for_suggestion(
+        &self,
+        expr_ty: Ty<'tcx>,
+        target: Ty<'tcx>,
+    ) -> Option<usize> {
         let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable);
-        // We don't ever need two-phase here since we throw out the result of the coercion
-        let coerce = Coerce::new(self, cause, AllowTwoPhase::No);
+        // We don't ever need two-phase here since we throw out the result of the coercion.
+        let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true);
         coerce
             .autoderef(DUMMY_SP, expr_ty)
             .find_map(|(ty, steps)| self.probe(|_| coerce.unify(ty, target)).ok().map(|_| steps))
@@ -1252,7 +1273,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // probably aren't processing function arguments here and even if we were,
         // they're going to get autorefed again anyway and we can apply 2-phase borrows
         // at that time.
-        let mut coerce = Coerce::new(self, cause.clone(), AllowTwoPhase::No);
+        //
+        // NOTE: we set `coerce_never` to `true` here because coercion LUBs only
+        // operate on values and not places, so a never coercion is valid.
+        let mut coerce = Coerce::new(self, cause.clone(), AllowTwoPhase::No, true);
         coerce.use_lub = true;
 
         // First try to coerce the new expression to the type of the previous ones,
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index e3c7dded0ca..2d718295374 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -1,3 +1,6 @@
+// ignore-tidy-filelength
+// FIXME: we should move the field error reporting code somewhere else.
+
 //! Type checking expressions.
 //!
 //! See [`rustc_hir_analysis::check`] for more context on type checking in general.
@@ -62,7 +65,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // While we don't allow *arbitrary* coercions here, we *do* allow
         // coercions from ! to `expected`.
-        if ty.is_never() {
+        if ty.is_never() && self.expr_guaranteed_to_constitute_read_for_never(expr) {
             if let Some(_) = self.typeck_results.borrow().adjustments().get(expr.hir_id) {
                 let reported = self.dcx().span_delayed_bug(
                     expr.span,
@@ -238,8 +241,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             _ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"),
         }
 
-        // Any expression that produces a value of type `!` must have diverged
-        if ty.is_never() {
+        // Any expression that produces a value of type `!` must have diverged,
+        // unless it's a place expression that isn't being read from, in which case
+        // diverging would be unsound since we may never actually read the `!`.
+        // e.g. `let _ = *never_ptr;` with `never_ptr: *const !`.
+        if ty.is_never() && self.expr_guaranteed_to_constitute_read_for_never(expr) {
             self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
         }
 
@@ -257,6 +263,185 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ty
     }
 
+    /// Whether this expression constitutes a read of value of the type that
+    /// it evaluates to.
+    ///
+    /// This is used to determine if we should consider the block to diverge
+    /// if the expression evaluates to `!`, and if we should insert a `NeverToAny`
+    /// coercion for values of type `!`.
+    ///
+    /// This function generally returns `false` if the expression is a place
+    /// expression and the *parent* expression is the scrutinee of a match or
+    /// the pointee of an `&` addr-of expression, since both of those parent
+    /// expressions take a *place* and not a value.
+    pub(super) fn expr_guaranteed_to_constitute_read_for_never(
+        &self,
+        expr: &'tcx hir::Expr<'tcx>,
+    ) -> bool {
+        // We only care about place exprs. Anything else returns an immediate
+        // which would constitute a read. We don't care about distinguishing
+        // "syntactic" place exprs since if the base of a field projection is
+        // not a place then it would've been UB to read from it anyways since
+        // that constitutes a read.
+        if !expr.is_syntactic_place_expr() {
+            return true;
+        }
+
+        let parent_node = self.tcx.parent_hir_node(expr.hir_id);
+        match parent_node {
+            hir::Node::Expr(parent_expr) => {
+                match parent_expr.kind {
+                    // Addr-of, field projections, and LHS of assignment don't constitute reads.
+                    // Assignment does call `drop_in_place`, though, but its safety
+                    // requirements are not the same.
+                    ExprKind::AddrOf(..) | hir::ExprKind::Field(..) => false,
+                    ExprKind::Assign(lhs, _, _) => {
+                        // Only the LHS does not constitute a read
+                        expr.hir_id != lhs.hir_id
+                    }
+
+                    // See note on `PatKind::Or` below for why this is `all`.
+                    ExprKind::Match(scrutinee, arms, _) => {
+                        assert_eq!(scrutinee.hir_id, expr.hir_id);
+                        arms.iter()
+                            .all(|arm| self.pat_guaranteed_to_constitute_read_for_never(arm.pat))
+                    }
+                    ExprKind::Let(hir::LetExpr { init, pat, .. }) => {
+                        assert_eq!(init.hir_id, expr.hir_id);
+                        self.pat_guaranteed_to_constitute_read_for_never(*pat)
+                    }
+
+                    // Any expression child of these expressions constitute reads.
+                    ExprKind::Array(_)
+                    | ExprKind::Call(_, _)
+                    | ExprKind::MethodCall(_, _, _, _)
+                    | ExprKind::Tup(_)
+                    | ExprKind::Binary(_, _, _)
+                    | ExprKind::Unary(_, _)
+                    | ExprKind::Cast(_, _)
+                    | ExprKind::Type(_, _)
+                    | ExprKind::DropTemps(_)
+                    | ExprKind::If(_, _, _)
+                    | ExprKind::Closure(_)
+                    | ExprKind::Block(_, _)
+                    | ExprKind::AssignOp(_, _, _)
+                    | ExprKind::Index(_, _, _)
+                    | ExprKind::Break(_, _)
+                    | ExprKind::Ret(_)
+                    | ExprKind::Become(_)
+                    | ExprKind::InlineAsm(_)
+                    | ExprKind::Struct(_, _, _)
+                    | ExprKind::Repeat(_, _)
+                    | ExprKind::Yield(_, _) => true,
+
+                    // These expressions have no (direct) sub-exprs.
+                    ExprKind::ConstBlock(_)
+                    | ExprKind::Loop(_, _, _, _)
+                    | ExprKind::Lit(_)
+                    | ExprKind::Path(_)
+                    | ExprKind::Continue(_)
+                    | ExprKind::OffsetOf(_, _)
+                    | ExprKind::Err(_) => unreachable!("no sub-expr expected for {:?}", expr.kind),
+                }
+            }
+
+            // If we have a subpattern that performs a read, we want to consider this
+            // to diverge for compatibility to support something like `let x: () = *never_ptr;`.
+            hir::Node::LetStmt(hir::LetStmt { init: Some(target), pat, .. }) => {
+                assert_eq!(target.hir_id, expr.hir_id);
+                self.pat_guaranteed_to_constitute_read_for_never(*pat)
+            }
+
+            // These nodes (if they have a sub-expr) do constitute a read.
+            hir::Node::Block(_)
+            | hir::Node::Arm(_)
+            | hir::Node::ExprField(_)
+            | hir::Node::AnonConst(_)
+            | hir::Node::ConstBlock(_)
+            | hir::Node::ConstArg(_)
+            | hir::Node::Stmt(_)
+            | hir::Node::Item(hir::Item {
+                kind: hir::ItemKind::Const(..) | hir::ItemKind::Static(..),
+                ..
+            })
+            | hir::Node::TraitItem(hir::TraitItem {
+                kind: hir::TraitItemKind::Const(..), ..
+            })
+            | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => true,
+
+            // These nodes do not have direct sub-exprs.
+            hir::Node::Param(_)
+            | hir::Node::Item(_)
+            | hir::Node::ForeignItem(_)
+            | hir::Node::TraitItem(_)
+            | hir::Node::ImplItem(_)
+            | hir::Node::Variant(_)
+            | hir::Node::Field(_)
+            | hir::Node::PathSegment(_)
+            | hir::Node::Ty(_)
+            | hir::Node::AssocItemConstraint(_)
+            | hir::Node::TraitRef(_)
+            | hir::Node::Pat(_)
+            | hir::Node::PatField(_)
+            | hir::Node::LetStmt(_)
+            | hir::Node::Synthetic
+            | hir::Node::Err(_)
+            | hir::Node::Ctor(_)
+            | hir::Node::Lifetime(_)
+            | hir::Node::GenericParam(_)
+            | hir::Node::Crate(_)
+            | hir::Node::Infer(_)
+            | hir::Node::WhereBoundPredicate(_)
+            | hir::Node::ArrayLenInfer(_)
+            | hir::Node::PreciseCapturingNonLifetimeArg(_)
+            | hir::Node::OpaqueTy(_) => {
+                unreachable!("no sub-expr expected for {parent_node:?}")
+            }
+        }
+    }
+
+    /// Whether this pattern constitutes a read of value of the scrutinee that
+    /// it is matching against. This is used to determine whether we should
+    /// perform `NeverToAny` coercions.
+    ///
+    /// See above for the nuances of what happens when this returns true.
+    pub(super) fn pat_guaranteed_to_constitute_read_for_never(&self, pat: &hir::Pat<'_>) -> bool {
+        match pat.kind {
+            // Does not constitute a read.
+            hir::PatKind::Wild => false,
+
+            // This is unnecessarily restrictive when the pattern that doesn't
+            // constitute a read is unreachable.
+            //
+            // For example `match *never_ptr { value => {}, _ => {} }` or
+            // `match *never_ptr { _ if false => {}, value => {} }`.
+            //
+            // It is however fine to be restrictive here; only returning `true`
+            // can lead to unsoundness.
+            hir::PatKind::Or(subpats) => {
+                subpats.iter().all(|pat| self.pat_guaranteed_to_constitute_read_for_never(pat))
+            }
+
+            // Does constitute a read, since it is equivalent to a discriminant read.
+            hir::PatKind::Never => true,
+
+            // All of these constitute a read, or match on something that isn't `!`,
+            // which would require a `NeverToAny` coercion.
+            hir::PatKind::Binding(_, _, _, _)
+            | hir::PatKind::Struct(_, _, _)
+            | hir::PatKind::TupleStruct(_, _, _)
+            | hir::PatKind::Path(_)
+            | hir::PatKind::Tuple(_, _)
+            | hir::PatKind::Box(_)
+            | hir::PatKind::Ref(_, _)
+            | hir::PatKind::Deref(_)
+            | hir::PatKind::Lit(_)
+            | hir::PatKind::Range(_, _, _)
+            | hir::PatKind::Slice(_, _, _)
+            | hir::PatKind::Err(_) => true,
+        }
+    }
+
     #[instrument(skip(self, expr), level = "debug")]
     fn check_expr_kind(
         &self,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 435b7d0f39a..1df4d32f3cb 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -2608,7 +2608,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
 
                     if let hir::ExprKind::Unary(hir::UnOp::Deref, inner) = expr.kind
-                        && let Some(1) = self.deref_steps(expected, checked_ty)
+                        && let Some(1) = self.deref_steps_for_suggestion(expected, checked_ty)
                     {
                         // We have `*&T`, check if what was expected was `&T`.
                         // If so, we may want to suggest removing a `*`.
@@ -2738,7 +2738,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
             }
             (_, &ty::RawPtr(ty_b, mutbl_b), &ty::Ref(_, ty_a, mutbl_a)) => {
-                if let Some(steps) = self.deref_steps(ty_a, ty_b)
+                if let Some(steps) = self.deref_steps_for_suggestion(ty_a, ty_b)
                     // Only suggest valid if dereferencing needed.
                     && steps > 0
                     // The pointer type implements `Copy` trait so the suggestion is always valid.
@@ -2782,7 +2782,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
             }
             _ if sp == expr.span => {
-                if let Some(mut steps) = self.deref_steps(checked_ty, expected) {
+                if let Some(mut steps) = self.deref_steps_for_suggestion(checked_ty, expected) {
                     let mut expr = expr.peel_blocks();
                     let mut prefix_span = expr.span.shrink_to_lo();
                     let mut remove = String::new();
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 5d1c300b453..948199fd55c 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -812,7 +812,8 @@ parse_unexpected_expr_in_pat =
        *[false] a pattern
     }, found an expression
 
-    .label = arbitrary expressions are not allowed in patterns
+    .label = not a pattern
+    .note = arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 
 parse_unexpected_expr_in_pat_const_sugg = consider extracting the expression into a `const`
 
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 40502158469..dade3912751 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -2608,6 +2608,7 @@ pub(crate) struct ExpectedCommaAfterPatternField {
 
 #[derive(Diagnostic)]
 #[diag(parse_unexpected_expr_in_pat)]
+#[note]
 pub(crate) struct UnexpectedExpressionInPattern {
     /// The unexpected expr's span.
     #[primary_span]
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 35d166e8b4a..fce5ec36c66 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -443,6 +443,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
 
         self.suggest_bare_struct_literal(&mut err);
         self.suggest_changing_type_to_const_param(&mut err, res, source, span);
+        self.explain_functions_in_pattern(&mut err, res, source);
 
         if self.suggest_pattern_match_with_let(&mut err, source, span) {
             // Fallback label.
@@ -1166,6 +1167,18 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
         }
     }
 
+    fn explain_functions_in_pattern(
+        &mut self,
+        err: &mut Diag<'_>,
+        res: Option<Res>,
+        source: PathSource<'_>,
+    ) {
+        let PathSource::TupleStruct(_, _) = source else { return };
+        let Some(Res::Def(DefKind::Fn, _)) = res else { return };
+        err.primary_message("expected a pattern, found a function call");
+        err.note("function calls are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>");
+    }
+
     fn suggest_changing_type_to_const_param(
         &mut self,
         err: &mut Diag<'_>,
diff --git a/config.example.toml b/config.example.toml
index 47ebb20d8fa..4b591b949b3 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -759,6 +759,16 @@
 # Build compiler with the optimization enabled and -Zvalidate-mir, currently only for `std`
 #validate-mir-opts = 3
 
+# Configure `std` features used during bootstrap.
+# Default features will be expanded in the following cases:
+#  - If `rust.llvm-libunwind` or `target.llvm-libunwind` is enabled:
+#    - "llvm-libunwind" will be added for in-tree LLVM builds.
+#    - "system-llvm-libunwind" will be added for system LLVM builds.
+#  - If `rust.backtrace` is enabled, "backtrace" will be added.
+#  - If `rust.profiler` or `target.profiler` is enabled, "profiler" will be added.
+#  - If building for a zkvm target, "compiler-builtins-mem" will be added.
+#std-features = ["panic_unwind"]
+
 # =============================================================================
 # Options for specific targets
 #
diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs
index 962da6643dd..c25501f1200 100644
--- a/library/core/src/primitive_docs.rs
+++ b/library/core/src/primitive_docs.rs
@@ -1251,7 +1251,7 @@ mod prim_f16 {}
 ///   - **Unchanged NaN propagation**: The quiet bit and payload are copied from any input operand
 ///     that is a NaN. If the inputs and outputs do not have the same size (i.e., for `as` casts), the
 ///     same rules as for "quieting NaN propagation" apply, with one caveat: if the output is smaller
-///     than the input, droppig the low-order bits may result in a payload of 0; a payload of 0 is not
+///     than the input, dropping the low-order bits may result in a payload of 0; a payload of 0 is not
 ///     possible with a signaling NaN (the all-0 significand encodes an infinity) so unchanged NaN
 ///     propagation cannot occur with some inputs.
 ///   - **Target-specific NaN**: The quiet bit is set and the payload is picked from a target-specific
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 07460b81412..7dc3b7b081a 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -4,7 +4,7 @@
 //! how the build runs.
 
 use std::cell::{Cell, RefCell};
-use std::collections::{HashMap, HashSet};
+use std::collections::{BTreeSet, HashMap, HashSet};
 use std::fmt::{self, Display};
 use std::io::IsTerminal;
 use std::path::{Path, PathBuf, absolute};
@@ -287,6 +287,7 @@ pub struct Config {
     pub rust_profile_generate: Option<String>,
     pub rust_lto: RustcLto,
     pub rust_validate_mir_opts: Option<u32>,
+    pub rust_std_features: BTreeSet<String>,
     pub llvm_profile_use: Option<String>,
     pub llvm_profile_generate: bool,
     pub llvm_libunwind_default: Option<LlvmLibunwind>,
@@ -1152,6 +1153,7 @@ define_config! {
         download_rustc: Option<StringOrBool> = "download-rustc",
         lto: Option<String> = "lto",
         validate_mir_opts: Option<u32> = "validate-mir-opts",
+        std_features: Option<BTreeSet<String>> = "std-features",
     }
 }
 
@@ -1645,6 +1647,7 @@ impl Config {
         let mut optimize = None;
         let mut omit_git_hash = None;
         let mut lld_enabled = None;
+        let mut std_features = None;
 
         let mut is_user_configured_rust_channel = false;
 
@@ -1703,6 +1706,7 @@ impl Config {
                 stack_protector,
                 strip,
                 lld_mode,
+                std_features: std_features_toml,
             } = rust;
 
             is_user_configured_rust_channel = channel.is_some();
@@ -1722,6 +1726,7 @@ impl Config {
             debuginfo_level_tools = debuginfo_level_tools_toml;
             debuginfo_level_tests = debuginfo_level_tests_toml;
             lld_enabled = lld_enabled_toml;
+            std_features = std_features_toml;
 
             optimize = optimize_toml;
             omit_git_hash = omit_git_hash_toml;
@@ -2118,6 +2123,9 @@ impl Config {
             );
         }
 
+        let default_std_features = BTreeSet::from([String::from("panic-unwind")]);
+        config.rust_std_features = std_features.unwrap_or(default_std_features);
+
         let default = debug == Some(true);
         config.rust_debug_assertions = debug_assertions.unwrap_or(default);
         config.rust_debug_assertions_std =
@@ -3016,6 +3024,7 @@ fn check_incompatible_options_for_ci_rustc(
         description,
         incremental,
         default_linker,
+        std_features,
 
         // Rest of the options can simply be ignored.
         debug: _,
@@ -3077,6 +3086,7 @@ fn check_incompatible_options_for_ci_rustc(
     err!(current_rust_config.default_linker, default_linker);
     err!(current_rust_config.stack_protector, stack_protector);
     err!(current_rust_config.lto, lto);
+    err!(current_rust_config.std_features, std_features);
 
     warn!(current_rust_config.channel, channel);
     warn!(current_rust_config.description, description);
diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs
index e38d4eac051..278becdcbc7 100644
--- a/src/bootstrap/src/core/config/tests.rs
+++ b/src/bootstrap/src/core/config/tests.rs
@@ -1,3 +1,4 @@
+use std::collections::BTreeSet;
 use std::env;
 use std::fs::{File, remove_file};
 use std::io::Write;
@@ -326,3 +327,24 @@ fn verbose_tests_default_value() {
     let config = Config::parse(Flags::parse(&["build".into(), "compiler".into(), "-v".into()]));
     assert_eq!(config.verbose_tests, true);
 }
+
+#[test]
+fn parse_rust_std_features() {
+    let config = parse("rust.std-features = [\"panic-unwind\", \"backtrace\"]");
+    let expected_features: BTreeSet<String> =
+        ["panic-unwind", "backtrace"].into_iter().map(|s| s.to_string()).collect();
+    assert_eq!(config.rust_std_features, expected_features);
+}
+
+#[test]
+fn parse_rust_std_features_empty() {
+    let config = parse("rust.std-features = []");
+    let expected_features: BTreeSet<String> = BTreeSet::new();
+    assert_eq!(config.rust_std_features, expected_features);
+}
+
+#[test]
+#[should_panic]
+fn parse_rust_std_features_invalid() {
+    parse("rust.std-features = \"backtrace\"");
+}
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 75659f46431..ecb219ea33f 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -17,7 +17,7 @@
 //! also check out the `src/bootstrap/README.md` file for more information.
 
 use std::cell::{Cell, RefCell};
-use std::collections::{HashMap, HashSet};
+use std::collections::{BTreeSet, HashMap, HashSet};
 use std::fmt::Display;
 use std::fs::{self, File};
 use std::path::{Path, PathBuf};
@@ -470,7 +470,7 @@ impl Build {
             crate::core::metadata::build(&mut build);
         }
 
-        // Make a symbolic link so we can use a consistent directory in the documentation.
+        // Create symbolic link to use host sysroot from a consistent path (e.g., in the rust-analyzer config file).
         let build_triple = build.out.join(build.build);
         t!(fs::create_dir_all(&build_triple));
         let host = build.out.join("host");
@@ -645,28 +645,31 @@ impl Build {
         &self.config.rust_info
     }
 
-    /// Gets the space-separated set of activated features for the standard
-    /// library.
+    /// Gets the space-separated set of activated features for the standard library.
+    /// This can be configured with the `std-features` key in config.toml.
     fn std_features(&self, target: TargetSelection) -> String {
-        let mut features = " panic-unwind".to_string();
+        let mut features: BTreeSet<&str> =
+            self.config.rust_std_features.iter().map(|s| s.as_str()).collect();
 
         match self.config.llvm_libunwind(target) {
-            LlvmLibunwind::InTree => features.push_str(" llvm-libunwind"),
-            LlvmLibunwind::System => features.push_str(" system-llvm-libunwind"),
-            LlvmLibunwind::No => {}
-        }
+            LlvmLibunwind::InTree => features.insert("llvm-libunwind"),
+            LlvmLibunwind::System => features.insert("system-llvm-libunwind"),
+            LlvmLibunwind::No => false,
+        };
+
         if self.config.backtrace {
-            features.push_str(" backtrace");
+            features.insert("backtrace");
         }
         if self.config.profiler_enabled(target) {
-            features.push_str(" profiler");
+            features.insert("profiler");
         }
         // Generate memcpy, etc.  FIXME: Remove this once compiler-builtins
         // automatically detects this target.
         if target.contains("zkvm") {
-            features.push_str(" compiler-builtins-mem");
+            features.insert("compiler-builtins-mem");
         }
-        features
+
+        features.into_iter().collect::<Vec<_>>().join(" ")
     }
 
     /// Gets the space-separated set of activated features for the compiler.
diff --git a/src/tools/miri/tests/pass/underscore_pattern.rs b/src/tools/miri/tests/pass/underscore_pattern.rs
index f0afe558954..f59bb9f5c82 100644
--- a/src/tools/miri/tests/pass/underscore_pattern.rs
+++ b/src/tools/miri/tests/pass/underscore_pattern.rs
@@ -1,5 +1,7 @@
 // Various tests ensuring that underscore patterns really just construct the place, but don't check its contents.
 #![feature(strict_provenance)]
+#![feature(never_type)]
+
 use std::ptr;
 
 fn main() {
@@ -9,6 +11,7 @@ fn main() {
     invalid_let();
     dangling_let_type_annotation();
     invalid_let_type_annotation();
+    never();
 }
 
 fn dangling_match() {
@@ -34,6 +37,13 @@ fn invalid_match() {
             _ => {}
         }
     }
+
+    unsafe {
+        let x: Uninit<!> = Uninit { uninit: () };
+        match x.value {
+            _ => {}
+        }
+    }
 }
 
 fn dangling_let() {
@@ -41,6 +51,11 @@ fn dangling_let() {
         let ptr = ptr::without_provenance::<bool>(0x40);
         let _ = *ptr;
     }
+
+    unsafe {
+        let ptr = ptr::without_provenance::<!>(0x40);
+        let _ = *ptr;
+    }
 }
 
 fn invalid_let() {
@@ -49,6 +64,12 @@ fn invalid_let() {
         let ptr = ptr::addr_of!(val).cast::<bool>();
         let _ = *ptr;
     }
+
+    unsafe {
+        let val = 3u8;
+        let ptr = ptr::addr_of!(val).cast::<!>();
+        let _ = *ptr;
+    }
 }
 
 // Adding a type annotation used to change how MIR is generated, make sure we cover both cases.
@@ -57,6 +78,11 @@ fn dangling_let_type_annotation() {
         let ptr = ptr::without_provenance::<bool>(0x40);
         let _: bool = *ptr;
     }
+
+    unsafe {
+        let ptr = ptr::without_provenance::<!>(0x40);
+        let _: ! = *ptr;
+    }
 }
 
 fn invalid_let_type_annotation() {
@@ -65,7 +91,28 @@ fn invalid_let_type_annotation() {
         let ptr = ptr::addr_of!(val).cast::<bool>();
         let _: bool = *ptr;
     }
+
+    unsafe {
+        let val = 3u8;
+        let ptr = ptr::addr_of!(val).cast::<!>();
+        let _: ! = *ptr;
+    }
 }
 
-// FIXME: we should also test `!`, not just `bool` -- but that s currently buggy:
-// https://github.com/rust-lang/rust/issues/117288
+// Regression test from <https://github.com/rust-lang/rust/issues/117288>.
+fn never() {
+    unsafe {
+        let x = 3u8;
+        let x: *const ! = &x as *const u8 as *const _;
+        let _: ! = *x;
+    }
+
+    // Without a type annotation, make sure we don't implicitly coerce `!` to `()`
+    // when we do the noop `*x` (as that would require a `!` *value*, creating
+    // which is UB).
+    unsafe {
+        let x = 3u8;
+        let x: *const ! = &x as *const u8 as *const _;
+        let _ = *x;
+    }
+}
diff --git a/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir b/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir
index 240f409817d..02e1f4be15e 100644
--- a/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir
+++ b/tests/mir-opt/uninhabited_enum.process_never.SimplifyLocals-final.after.mir
@@ -3,9 +3,8 @@
 fn process_never(_1: *const !) -> () {
     debug input => _1;
     let mut _0: ();
-    let _2: &!;
     scope 1 {
-        debug _input => _2;
+        debug _input => const ();
     }
 
     bb0: {
diff --git a/tests/mir-opt/uninhabited_enum.process_void.SimplifyLocals-final.after.mir b/tests/mir-opt/uninhabited_enum.process_void.SimplifyLocals-final.after.mir
index 51514ba5e5d..64711755f73 100644
--- a/tests/mir-opt/uninhabited_enum.process_void.SimplifyLocals-final.after.mir
+++ b/tests/mir-opt/uninhabited_enum.process_void.SimplifyLocals-final.after.mir
@@ -4,7 +4,7 @@ fn process_void(_1: *const Void) -> () {
     debug input => _1;
     let mut _0: ();
     scope 1 {
-        debug _input => _1;
+        debug _input => const ZeroSized: Void;
     }
 
     bb0: {
diff --git a/tests/mir-opt/uninhabited_enum.rs b/tests/mir-opt/uninhabited_enum.rs
index 859535852cf..90b5353f291 100644
--- a/tests/mir-opt/uninhabited_enum.rs
+++ b/tests/mir-opt/uninhabited_enum.rs
@@ -1,18 +1,19 @@
 // skip-filecheck
 #![feature(never_type)]
 
+#[derive(Copy, Clone)]
 pub enum Void {}
 
 // EMIT_MIR uninhabited_enum.process_never.SimplifyLocals-final.after.mir
 #[no_mangle]
 pub fn process_never(input: *const !) {
-    let _input = unsafe { &*input };
+    let _input = unsafe { *input };
 }
 
 // EMIT_MIR uninhabited_enum.process_void.SimplifyLocals-final.after.mir
 #[no_mangle]
 pub fn process_void(input: *const Void) {
-    let _input = unsafe { &*input };
+    let _input = unsafe { *input };
     // In the future, this should end with `unreachable`, but we currently only do
     // unreachability analysis for `!`.
 }
diff --git a/tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir b/tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir
new file mode 100644
index 00000000000..6bf4be652be
--- /dev/null
+++ b/tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir
@@ -0,0 +1,49 @@
+// MIR for `main` after SimplifyLocals-final
+
+fn main() -> () {
+    let mut _0: ();
+    let _1: u8;
+    let mut _2: *const !;
+    let mut _3: *const u8;
+    let _4: u8;
+    let mut _5: *const !;
+    let mut _6: *const u8;
+    scope 1 {
+        debug x => _1;
+        scope 2 {
+            debug x => _2;
+            scope 3 {
+            }
+        }
+    }
+    scope 4 {
+        debug x => _4;
+        scope 5 {
+            debug x => _5;
+            scope 6 {
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_1);
+        _1 = const 3_u8;
+        StorageLive(_2);
+        StorageLive(_3);
+        _3 = &raw const _1;
+        _2 = move _3 as *const ! (PtrToPtr);
+        StorageDead(_3);
+        StorageDead(_2);
+        StorageDead(_1);
+        StorageLive(_4);
+        _4 = const 3_u8;
+        StorageLive(_5);
+        StorageLive(_6);
+        _6 = &raw const _4;
+        _5 = move _6 as *const ! (PtrToPtr);
+        StorageDead(_6);
+        StorageDead(_5);
+        StorageDead(_4);
+        return;
+    }
+}
diff --git a/tests/mir-opt/uninhabited_not_read.rs b/tests/mir-opt/uninhabited_not_read.rs
new file mode 100644
index 00000000000..15769cdd75b
--- /dev/null
+++ b/tests/mir-opt/uninhabited_not_read.rs
@@ -0,0 +1,26 @@
+// skip-filecheck
+
+//@ edition: 2021
+// In ed 2021 and below, we don't fallback `!` to `()`.
+// This would introduce a `! -> ()` coercion which would
+// be UB if we didn't disallow this explicitly.
+
+#![feature(never_type)]
+
+// EMIT_MIR uninhabited_not_read.main.SimplifyLocals-final.after.mir
+fn main() {
+    // With a type annotation
+    unsafe {
+        let x = 3u8;
+        let x: *const ! = &x as *const u8 as *const _;
+        let _: ! = *x;
+    }
+
+    // Without a type annotation, make sure we don't implicitly coerce `!` to `()`
+    // when we do the noop `*x`.
+    unsafe {
+        let x = 3u8;
+        let x: *const ! = &x as *const u8 as *const _;
+        let _ = *x;
+    }
+}
diff --git a/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr b/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr
index f8ed792e3c6..e0d900a1eb5 100644
--- a/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr
+++ b/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr
@@ -7,14 +7,12 @@ LL |     let c1 = || match x { };
    |                       ^ `x` used here but it isn't initialized
 
 error[E0381]: used binding `x` isn't initialized
-  --> $DIR/pattern-matching-should-fail.rs:15:14
+  --> $DIR/pattern-matching-should-fail.rs:15:23
    |
 LL |     let x: !;
    |         - binding declared here but left uninitialized
 LL |     let c2 = || match x { _ => () };
-   |              ^^       - borrow occurs due to use in closure
-   |              |
-   |              `x` used here but it isn't initialized
+   |                       ^ `x` used here but it isn't initialized
 
 error[E0381]: used binding `variant` isn't initialized
   --> $DIR/pattern-matching-should-fail.rs:27:13
diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr
index 9831348de75..e2916725fbd 100644
--- a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr
+++ b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr
@@ -2,8 +2,9 @@ error: expected a pattern range bound, found an expression
   --> $DIR/range_pat_interactions1.rs:17:16
    |
 LL |             0..5+1 => errors_only.push(x),
-   |                ^^^ arbitrary expressions are not allowed in patterns
+   |                ^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider extracting the expression into a `const`
    |
 LL +         const VAL: /* Type */ = 5+1;
diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr
index 1b5e875cccb..f54e07c3a63 100644
--- a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr
+++ b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr
@@ -14,8 +14,9 @@ error: expected a pattern range bound, found an expression
   --> $DIR/range_pat_interactions2.rs:10:18
    |
 LL |             0..=(5+1) => errors_only.push(x),
-   |                  ^^^ arbitrary expressions are not allowed in patterns
+   |                  ^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider extracting the expression into a `const`
    |
 LL +         const VAL: /* Type */ = 5+1;
diff --git a/tests/ui/never_type/diverging-place-match.rs b/tests/ui/never_type/diverging-place-match.rs
new file mode 100644
index 00000000000..b9bc29a218c
--- /dev/null
+++ b/tests/ui/never_type/diverging-place-match.rs
@@ -0,0 +1,74 @@
+#![feature(never_type)]
+
+fn not_a_read() -> ! {
+    unsafe {
+    //~^ ERROR mismatched types
+        let x: *const ! = 0 as _;
+        let _: ! = *x;
+        // Since `*x` "diverges" in HIR, but doesn't count as a read in MIR, this
+        // is unsound since we act as if it diverges but it doesn't.
+    }
+}
+
+fn not_a_read_implicit() -> ! {
+    unsafe {
+    //~^ ERROR mismatched types
+        let x: *const ! = 0 as _;
+        let _ = *x;
+    }
+}
+
+fn not_a_read_guide_coercion() -> ! {
+    unsafe {
+        //~^ ERROR mismatched types
+        let x: *const ! = 0 as _;
+        let _: () = *x;
+        //~^ ERROR mismatched types
+    }
+}
+
+fn empty_match() -> ! {
+    unsafe {
+    //~^ ERROR mismatched types
+        let x: *const ! = 0 as _;
+        match *x { _ => {} };
+    }
+}
+
+fn field_projection() -> ! {
+    unsafe {
+    //~^ ERROR mismatched types
+        let x: *const (!, ()) = 0 as _;
+        let _ = (*x).0;
+        // ^ I think this is still UB, but because of the inbounds projection.
+    }
+}
+
+fn covered_arm() -> ! {
+    unsafe {
+    //~^ ERROR mismatched types
+        let x: *const ! = 0 as _;
+        let (_ | 1i32) = *x;
+        //~^ ERROR mismatched types
+    }
+}
+
+// FIXME: This *could* be considered a read of `!`, but we're not that sophisticated..
+fn uncovered_arm() -> ! {
+    unsafe {
+    //~^ ERROR mismatched types
+        let x: *const ! = 0 as _;
+        let (1i32 | _) = *x;
+        //~^ ERROR mismatched types
+    }
+}
+
+fn coerce_ref_binding() -> ! {
+    unsafe {
+        let x: *const ! = 0 as _;
+        let ref _x: () = *x;
+        //~^ ERROR mismatched types
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/never_type/diverging-place-match.stderr b/tests/ui/never_type/diverging-place-match.stderr
new file mode 100644
index 00000000000..74e1bfa0a6b
--- /dev/null
+++ b/tests/ui/never_type/diverging-place-match.stderr
@@ -0,0 +1,142 @@
+error[E0308]: mismatched types
+  --> $DIR/diverging-place-match.rs:4:5
+   |
+LL | /     unsafe {
+LL | |
+LL | |         let x: *const ! = 0 as _;
+LL | |         let _: ! = *x;
+LL | |         // Since `*x` "diverges" in HIR, but doesn't count as a read in MIR, this
+LL | |         // is unsound since we act as if it diverges but it doesn't.
+LL | |     }
+   | |_____^ expected `!`, found `()`
+   |
+   = note:   expected type `!`
+           found unit type `()`
+
+error[E0308]: mismatched types
+  --> $DIR/diverging-place-match.rs:14:5
+   |
+LL | /     unsafe {
+LL | |
+LL | |         let x: *const ! = 0 as _;
+LL | |         let _ = *x;
+LL | |     }
+   | |_____^ expected `!`, found `()`
+   |
+   = note:   expected type `!`
+           found unit type `()`
+
+error[E0308]: mismatched types
+  --> $DIR/diverging-place-match.rs:25:21
+   |
+LL |         let _: () = *x;
+   |                --   ^^ expected `()`, found `!`
+   |                |
+   |                expected due to this
+   |
+   = note: expected unit type `()`
+                   found type `!`
+
+error[E0308]: mismatched types
+  --> $DIR/diverging-place-match.rs:22:5
+   |
+LL | /     unsafe {
+LL | |
+LL | |         let x: *const ! = 0 as _;
+LL | |         let _: () = *x;
+LL | |
+LL | |     }
+   | |_____^ expected `!`, found `()`
+   |
+   = note:   expected type `!`
+           found unit type `()`
+
+error[E0308]: mismatched types
+  --> $DIR/diverging-place-match.rs:31:5
+   |
+LL | /     unsafe {
+LL | |
+LL | |         let x: *const ! = 0 as _;
+LL | |         match *x { _ => {} };
+LL | |     }
+   | |_____^ expected `!`, found `()`
+   |
+   = note:   expected type `!`
+           found unit type `()`
+
+error[E0308]: mismatched types
+  --> $DIR/diverging-place-match.rs:39:5
+   |
+LL | /     unsafe {
+LL | |
+LL | |         let x: *const (!, ()) = 0 as _;
+LL | |         let _ = (*x).0;
+LL | |         // ^ I think this is still UB, but because of the inbounds projection.
+LL | |     }
+   | |_____^ expected `!`, found `()`
+   |
+   = note:   expected type `!`
+           found unit type `()`
+
+error[E0308]: mismatched types
+  --> $DIR/diverging-place-match.rs:51:18
+   |
+LL |         let (_ | 1i32) = *x;
+   |                  ^^^^    -- this expression has type `!`
+   |                  |
+   |                  expected `!`, found `i32`
+   |
+   = note: expected type `!`
+              found type `i32`
+
+error[E0308]: mismatched types
+  --> $DIR/diverging-place-match.rs:48:5
+   |
+LL | /     unsafe {
+LL | |
+LL | |         let x: *const ! = 0 as _;
+LL | |         let (_ | 1i32) = *x;
+LL | |
+LL | |     }
+   | |_____^ expected `!`, found `()`
+   |
+   = note:   expected type `!`
+           found unit type `()`
+
+error[E0308]: mismatched types
+  --> $DIR/diverging-place-match.rs:61:14
+   |
+LL |         let (1i32 | _) = *x;
+   |              ^^^^        -- this expression has type `!`
+   |              |
+   |              expected `!`, found `i32`
+   |
+   = note: expected type `!`
+              found type `i32`
+
+error[E0308]: mismatched types
+  --> $DIR/diverging-place-match.rs:58:5
+   |
+LL | /     unsafe {
+LL | |
+LL | |         let x: *const ! = 0 as _;
+LL | |         let (1i32 | _) = *x;
+LL | |
+LL | |     }
+   | |_____^ expected `!`, found `()`
+   |
+   = note:   expected type `!`
+           found unit type `()`
+
+error[E0308]: mismatched types
+  --> $DIR/diverging-place-match.rs:69:26
+   |
+LL |         let ref _x: () = *x;
+   |                          ^^ expected `()`, found `!`
+   |
+   = note: expected unit type `()`
+                   found type `!`
+
+error: aborting due to 11 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/parser/bad-name.stderr b/tests/ui/parser/bad-name.stderr
index 3fc416dd531..5ca248380ee 100644
--- a/tests/ui/parser/bad-name.stderr
+++ b/tests/ui/parser/bad-name.stderr
@@ -8,7 +8,9 @@ error: expected a pattern, found an expression
   --> $DIR/bad-name.rs:2:7
    |
 LL |   let x.y::<isize>.z foo;
-   |       ^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |       ^^^^^^^^^^^^^^ not a pattern
+   |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 
 error: expected one of `(`, `.`, `::`, `:`, `;`, `=`, `?`, `|`, or an operator, found `foo`
   --> $DIR/bad-name.rs:2:22
diff --git a/tests/ui/parser/issues/issue-24197.stderr b/tests/ui/parser/issues/issue-24197.stderr
index 7ebbf4ac370..c92e165b23b 100644
--- a/tests/ui/parser/issues/issue-24197.stderr
+++ b/tests/ui/parser/issues/issue-24197.stderr
@@ -2,7 +2,9 @@ error: expected a pattern, found an expression
   --> $DIR/issue-24197.rs:2:9
    |
 LL |     let buf[0] = 0;
-   |         ^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^ not a pattern
+   |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/parser/issues/issue-24375.stderr b/tests/ui/parser/issues/issue-24375.stderr
index a25c277d78a..fef3fcde7b7 100644
--- a/tests/ui/parser/issues/issue-24375.stderr
+++ b/tests/ui/parser/issues/issue-24375.stderr
@@ -2,8 +2,9 @@ error: expected a pattern, found an expression
   --> $DIR/issue-24375.rs:6:9
    |
 LL |         tmp[0] => {}
-   |         ^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == tmp[0] => {}
diff --git a/tests/ui/parser/pat-lt-bracket-5.stderr b/tests/ui/parser/pat-lt-bracket-5.stderr
index 18cf2df0282..a2a972652d1 100644
--- a/tests/ui/parser/pat-lt-bracket-5.stderr
+++ b/tests/ui/parser/pat-lt-bracket-5.stderr
@@ -2,7 +2,9 @@ error: expected a pattern, found an expression
   --> $DIR/pat-lt-bracket-5.rs:2:9
    |
 LL |     let v[0] = v[1];
-   |         ^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^ not a pattern
+   |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 
 error[E0425]: cannot find value `v` in this scope
   --> $DIR/pat-lt-bracket-5.rs:2:16
diff --git a/tests/ui/parser/pat-lt-bracket-6.stderr b/tests/ui/parser/pat-lt-bracket-6.stderr
index 892883c4aed..14ae602fedf 100644
--- a/tests/ui/parser/pat-lt-bracket-6.stderr
+++ b/tests/ui/parser/pat-lt-bracket-6.stderr
@@ -2,7 +2,9 @@ error: expected a pattern, found an expression
   --> $DIR/pat-lt-bracket-6.rs:5:14
    |
 LL |     let Test(&desc[..]) = x;
-   |              ^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |              ^^^^^^^^^ not a pattern
+   |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 
 error[E0308]: mismatched types
   --> $DIR/pat-lt-bracket-6.rs:10:30
diff --git a/tests/ui/parser/pat-ranges-3.stderr b/tests/ui/parser/pat-ranges-3.stderr
index 5e1f35d1b6f..ef080368e19 100644
--- a/tests/ui/parser/pat-ranges-3.stderr
+++ b/tests/ui/parser/pat-ranges-3.stderr
@@ -2,13 +2,17 @@ error: expected a pattern range bound, found an expression
   --> $DIR/pat-ranges-3.rs:4:16
    |
 LL |     let 10 ..= 10 + 3 = 12;
-   |                ^^^^^^ arbitrary expressions are not allowed in patterns
+   |                ^^^^^^ not a pattern
+   |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 
 error: expected a pattern range bound, found an expression
   --> $DIR/pat-ranges-3.rs:7:9
    |
 LL |     let 10 - 3 ..= 10 = 8;
-   |         ^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^ not a pattern
+   |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/parser/recover/recover-pat-exprs.stderr b/tests/ui/parser/recover/recover-pat-exprs.stderr
index 63956f35c07..6cb3753de8d 100644
--- a/tests/ui/parser/recover/recover-pat-exprs.stderr
+++ b/tests/ui/parser/recover/recover-pat-exprs.stderr
@@ -2,8 +2,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:5:9
    |
 LL |         x.y => (),
-   |         ^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == x.y => (),
@@ -24,8 +25,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:6:9
    |
 LL |         x.0 => (),
-   |         ^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == x.0 => (),
@@ -47,8 +49,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:7:9
    |
 LL |         x._0 => (),
-   |         ^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == x._0 => (),
@@ -71,8 +74,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:8:9
    |
 LL |         x.0.1 => (),
-   |         ^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == x.0.1 => (),
@@ -95,8 +99,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:9:9
    |
 LL |         x.4.y.17.__z => (),
-   |         ^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == x.4.y.17.__z => (),
@@ -149,8 +154,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:23:9
    |
 LL |         x[0] => (),
-   |         ^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == x[0] => (),
@@ -170,8 +176,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:24:9
    |
 LL |         x[..] => (),
-   |         ^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == x[..] => (),
@@ -219,8 +226,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:37:9
    |
 LL |         x.f() => (),
-   |         ^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == x.f() => (),
@@ -240,8 +248,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:38:9
    |
 LL |         x._f() => (),
-   |         ^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == x._f() => (),
@@ -262,8 +271,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:39:9
    |
 LL |         x? => (),
-   |         ^^ arbitrary expressions are not allowed in patterns
+   |         ^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == x? => (),
@@ -285,8 +295,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:40:9
    |
 LL |         ().f() => (),
-   |         ^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == ().f() => (),
@@ -309,8 +320,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:41:9
    |
 LL |         (0, x)?.f() => (),
-   |         ^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == (0, x)?.f() => (),
@@ -333,8 +345,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:42:9
    |
 LL |         x.f().g() => (),
-   |         ^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == x.f().g() => (),
@@ -357,8 +370,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:43:9
    |
 LL |         0.f()?.g()?? => (),
-   |         ^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == 0.f()?.g()?? => (),
@@ -381,8 +395,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:50:9
    |
 LL |         x as usize => (),
-   |         ^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == x as usize => (),
@@ -402,8 +417,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:51:9
    |
 LL |         0 as usize => (),
-   |         ^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == 0 as usize => (),
@@ -424,8 +440,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:52:9
    |
 LL |         x.f().0.4 as f32 => (),
-   |         ^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^^^^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == x.f().0.4 as f32 => (),
@@ -447,8 +464,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:59:9
    |
 LL |         1 + 1 => (),
-   |         ^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == 1 + 1 => (),
@@ -468,8 +486,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:60:9
    |
 LL |         (1 + 2) * 3 => (),
-   |         ^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == (1 + 2) * 3 => (),
@@ -490,8 +509,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:63:9
    |
 LL |         x.0 > 2 => (),
-   |         ^^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == (x.0 > 2) => (),
@@ -514,8 +534,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:64:9
    |
 LL |         x.0 == 2 => (),
-   |         ^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == (x.0 == 2) => (),
@@ -538,8 +559,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:69:13
    |
 LL |         (x, y.0 > 2) if x != 0 => (),
-   |             ^^^^^^^ arbitrary expressions are not allowed in patterns
+   |             ^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to the match arm guard
    |
 LL |         (x, val) if x != 0 && val == (y.0 > 2) => (),
@@ -559,8 +581,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:70:13
    |
 LL |         (x, y.0 > 2) if x != 0 || x != 1 => (),
-   |             ^^^^^^^ arbitrary expressions are not allowed in patterns
+   |             ^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to the match arm guard
    |
 LL |         (x, val) if (x != 0 || x != 1) && val == (y.0 > 2) => (),
@@ -598,8 +621,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:81:9
    |
 LL |         u8::MAX.abs() => (),
-   |         ^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == u8::MAX.abs() => (),
@@ -619,8 +643,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:86:17
    |
 LL |         z @ w @ v.u() => (),
-   |                 ^^^^^ arbitrary expressions are not allowed in patterns
+   |                 ^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         z @ w @ val if val == v.u() => (),
@@ -643,8 +668,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:88:9
    |
 LL |         y.ilog(3) => (),
-   |         ^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == y.ilog(3) => (),
@@ -667,8 +693,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:90:9
    |
 LL |         n + 1 => (),
-   |         ^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == n + 1 => (),
@@ -691,8 +718,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:92:10
    |
 LL |         ("".f() + 14 * 8) => (),
-   |          ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |          ^^^^^^^^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         (val) if val == "".f() + 14 * 8 => (),
@@ -715,8 +743,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:95:9
    |
 LL |         f?() => (),
-   |         ^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         val if val == f?() => (),
@@ -739,7 +768,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:101:9
    |
 LL |     let 1 + 1 = 2;
-   |         ^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^ not a pattern
+   |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 
 error: expected one of `)`, `,`, `@`, or `|`, found `*`
   --> $DIR/recover-pat-exprs.rs:104:28
@@ -754,19 +785,25 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:60:10
    |
 LL |         (1 + 2) * 3 => (),
-   |          ^^^^^ arbitrary expressions are not allowed in patterns
+   |          ^^^^^ not a pattern
+   |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:75:5
    |
 LL |     1 + 2 * PI.cos() => 2,
-   |     ^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |     ^^^^^^^^^^^^^^^^ not a pattern
+   |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:83:9
    |
 LL |         x.sqrt() @ .. => (),
-   |         ^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^^^ not a pattern
+   |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 
 error: aborting due to 45 previous errors
 
diff --git a/tests/ui/parser/recover/recover-pat-issues.stderr b/tests/ui/parser/recover/recover-pat-issues.stderr
index 596bff21395..17cb7b4aead 100644
--- a/tests/ui/parser/recover/recover-pat-issues.stderr
+++ b/tests/ui/parser/recover/recover-pat-issues.stderr
@@ -2,8 +2,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-issues.rs:6:13
    |
 LL |         Foo("hi".to_owned()) => true,
-   |             ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |             ^^^^^^^^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         Foo(val) if val == "hi".to_owned() => true,
@@ -23,8 +24,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-issues.rs:14:20
    |
 LL |         Bar { baz: "hi".to_owned() } => true,
-   |                    ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |                    ^^^^^^^^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         Bar { baz } if baz == "hi".to_owned() => true,
@@ -44,8 +46,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-issues.rs:25:11
    |
 LL |         &["foo".to_string()] => {}
-   |           ^^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |           ^^^^^^^^^^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider moving the expression to a match arm guard
    |
 LL |         &[val] if val == "foo".to_string() => {}
@@ -65,8 +68,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-issues.rs:36:17
    |
 LL |     if let Some(MAGIC.0 as usize) = None::<usize> {}
-   |                 ^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |                 ^^^^^^^^^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider extracting the expression into a `const`
    |
 LL +     const VAL: /* Type */ = MAGIC.0 as usize;
@@ -81,8 +85,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-issues.rs:41:13
    |
 LL |     if let (-1.some(4)) = (0, Some(4)) {}
-   |             ^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |             ^^^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider extracting the expression into a `const`
    |
 LL +     const VAL: /* Type */ = -1.some(4);
@@ -97,8 +102,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-issues.rs:44:13
    |
 LL |     if let (-1.Some(4)) = (0, Some(4)) {}
-   |             ^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |             ^^^^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider extracting the expression into a `const`
    |
 LL +     const VAL: /* Type */ = -1.Some(4);
diff --git a/tests/ui/parser/recover/recover-pat-lets.stderr b/tests/ui/parser/recover/recover-pat-lets.stderr
index e54586b0924..b481813b246 100644
--- a/tests/ui/parser/recover/recover-pat-lets.stderr
+++ b/tests/ui/parser/recover/recover-pat-lets.stderr
@@ -2,26 +2,33 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-lets.rs:4:9
    |
 LL |     let x.expect("foo");
-   |         ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^^^^^^^^^^ not a pattern
+   |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-lets.rs:7:9
    |
 LL |     let x.unwrap(): u32;
-   |         ^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^^^^^^ not a pattern
+   |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-lets.rs:10:9
    |
 LL |     let x[0] = 1;
-   |         ^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^ not a pattern
+   |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-lets.rs:13:14
    |
 LL |     let Some(1 + 1) = x else {
-   |              ^^^^^ arbitrary expressions are not allowed in patterns
+   |              ^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider extracting the expression into a `const`
    |
 LL +     const VAL: /* Type */ = 1 + 1;
@@ -36,8 +43,9 @@ error: expected a pattern, found an expression
   --> $DIR/recover-pat-lets.rs:17:17
    |
 LL |     if let Some(1 + 1) = x {
-   |                 ^^^^^ arbitrary expressions are not allowed in patterns
+   |                 ^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider extracting the expression into a `const`
    |
 LL +     const VAL: /* Type */ = 1 + 1;
diff --git a/tests/ui/parser/recover/recover-pat-ranges.stderr b/tests/ui/parser/recover/recover-pat-ranges.stderr
index 088f83b0ccb..0a9b5447468 100644
--- a/tests/ui/parser/recover/recover-pat-ranges.stderr
+++ b/tests/ui/parser/recover/recover-pat-ranges.stderr
@@ -86,8 +86,9 @@ error: expected a pattern range bound, found an expression
   --> $DIR/recover-pat-ranges.rs:11:12
    |
 LL |         ..=1 + 2 => (),
-   |            ^^^^^ arbitrary expressions are not allowed in patterns
+   |            ^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider extracting the expression into a `const`
    |
 LL +     const VAL: /* Type */ = 1 + 2;
@@ -106,8 +107,9 @@ error: expected a pattern range bound, found an expression
   --> $DIR/recover-pat-ranges.rs:15:10
    |
 LL |         (-4 + 0).. => (),
-   |          ^^^^^^ arbitrary expressions are not allowed in patterns
+   |          ^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider extracting the expression into a `const`
    |
 LL +     const VAL: /* Type */ = -4 + 0;
@@ -126,8 +128,9 @@ error: expected a pattern range bound, found an expression
   --> $DIR/recover-pat-ranges.rs:18:10
    |
 LL |         (1 + 4)...1 * 2 => (),
-   |          ^^^^^ arbitrary expressions are not allowed in patterns
+   |          ^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider extracting the expression into a `const`
    |
 LL +     const VAL: /* Type */ = 1 + 4;
@@ -146,8 +149,9 @@ error: expected a pattern range bound, found an expression
   --> $DIR/recover-pat-ranges.rs:18:19
    |
 LL |         (1 + 4)...1 * 2 => (),
-   |                   ^^^^^ arbitrary expressions are not allowed in patterns
+   |                   ^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider extracting the expression into a `const`
    |
 LL +     const VAL: /* Type */ = 1 * 2;
@@ -166,8 +170,9 @@ error: expected a pattern range bound, found an expression
   --> $DIR/recover-pat-ranges.rs:24:9
    |
 LL |         0.x()..="y".z() => (),
-   |         ^^^^^ arbitrary expressions are not allowed in patterns
+   |         ^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider extracting the expression into a `const`
    |
 LL +     const VAL: /* Type */ = 0.x();
@@ -186,8 +191,9 @@ error: expected a pattern range bound, found an expression
   --> $DIR/recover-pat-ranges.rs:24:17
    |
 LL |         0.x()..="y".z() => (),
-   |                 ^^^^^^^ arbitrary expressions are not allowed in patterns
+   |                 ^^^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider extracting the expression into a `const`
    |
 LL +     const VAL: /* Type */ = "y".z();
diff --git a/tests/ui/parser/recover/recover-pat-wildcards.stderr b/tests/ui/parser/recover/recover-pat-wildcards.stderr
index 30307726a97..8d4212ed389 100644
--- a/tests/ui/parser/recover/recover-pat-wildcards.stderr
+++ b/tests/ui/parser/recover/recover-pat-wildcards.stderr
@@ -75,8 +75,9 @@ error: expected a pattern range bound, found an expression
   --> $DIR/recover-pat-wildcards.rs:55:14
    |
 LL |         4..=(2 + _) => ()
-   |              ^^^^^ arbitrary expressions are not allowed in patterns
+   |              ^^^^^ not a pattern
    |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 help: consider extracting the expression into a `const`
    |
 LL +     const VAL: /* Type */ = 2 + _;
diff --git a/tests/ui/raw-ref-op/never-place-isnt-diverging.rs b/tests/ui/raw-ref-op/never-place-isnt-diverging.rs
new file mode 100644
index 00000000000..80d441729f7
--- /dev/null
+++ b/tests/ui/raw-ref-op/never-place-isnt-diverging.rs
@@ -0,0 +1,22 @@
+#![feature(never_type)]
+
+fn make_up_a_value<T>() -> T {
+    unsafe {
+    //~^ ERROR mismatched types
+        let x: *const ! = 0 as _;
+        &raw const *x;
+        // Since `*x` is `!`, HIR typeck used to think that it diverges
+        // and allowed the block to coerce to any value, leading to UB.
+    }
+}
+
+
+fn make_up_a_pointer<T>() -> *const T {
+    unsafe {
+        let x: *const ! = 0 as _;
+        &raw const *x
+        //~^ ERROR mismatched types
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr b/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr
new file mode 100644
index 00000000000..af9e7889821
--- /dev/null
+++ b/tests/ui/raw-ref-op/never-place-isnt-diverging.stderr
@@ -0,0 +1,34 @@
+error[E0308]: mismatched types
+  --> $DIR/never-place-isnt-diverging.rs:4:5
+   |
+LL |   fn make_up_a_value<T>() -> T {
+   |                      - expected this type parameter
+LL | /     unsafe {
+LL | |
+LL | |         let x: *const ! = 0 as _;
+LL | |         &raw const *x;
+LL | |         // Since `*x` is `!`, HIR typeck used to think that it diverges
+LL | |         // and allowed the block to coerce to any value, leading to UB.
+LL | |     }
+   | |_____^ expected type parameter `T`, found `()`
+   |
+   = note: expected type parameter `T`
+                   found unit type `()`
+
+error[E0308]: mismatched types
+  --> $DIR/never-place-isnt-diverging.rs:17:9
+   |
+LL | fn make_up_a_pointer<T>() -> *const T {
+   |                      -       -------- expected `*const T` because of return type
+   |                      |
+   |                      expected this type parameter
+...
+LL |         &raw const *x
+   |         ^^^^^^^^^^^^^ expected `*const T`, found `*const !`
+   |
+   = note: expected raw pointer `*const T`
+              found raw pointer `*const !`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/reachable/expr_assign.stderr b/tests/ui/reachable/expr_assign.stderr
index c51156b3f40..cfbbe04db76 100644
--- a/tests/ui/reachable/expr_assign.stderr
+++ b/tests/ui/reachable/expr_assign.stderr
@@ -14,12 +14,13 @@ LL | #![deny(unreachable_code)]
    |         ^^^^^^^^^^^^^^^^
 
 error: unreachable expression
-  --> $DIR/expr_assign.rs:20:14
+  --> $DIR/expr_assign.rs:20:9
    |
 LL |         *p = return;
-   |         --   ^^^^^^ unreachable expression
-   |         |
-   |         any code following this expression is unreachable
+   |         ^^^^^------
+   |         |    |
+   |         |    any code following this expression is unreachable
+   |         unreachable expression
 
 error: unreachable expression
   --> $DIR/expr_assign.rs:26:15
diff --git a/tests/ui/resolve/issue-10200.rs b/tests/ui/resolve/issue-10200.rs
index fe36a7e00bf..d529536b952 100644
--- a/tests/ui/resolve/issue-10200.rs
+++ b/tests/ui/resolve/issue-10200.rs
@@ -3,7 +3,7 @@ fn foo(_: usize) -> Foo { Foo(false) }
 
 fn main() {
     match Foo(true) {
-        foo(x) //~ ERROR expected tuple struct or tuple variant, found function `foo`
+        foo(x) //~ ERROR expected a pattern, found a function call
         => ()
     }
 }
diff --git a/tests/ui/resolve/issue-10200.stderr b/tests/ui/resolve/issue-10200.stderr
index 7b218694b26..172d016c6e6 100644
--- a/tests/ui/resolve/issue-10200.stderr
+++ b/tests/ui/resolve/issue-10200.stderr
@@ -1,4 +1,4 @@
-error[E0532]: expected tuple struct or tuple variant, found function `foo`
+error[E0532]: expected a pattern, found a function call
   --> $DIR/issue-10200.rs:6:9
    |
 LL | struct Foo(bool);
@@ -6,6 +6,8 @@ LL | struct Foo(bool);
 ...
 LL |         foo(x)
    |         ^^^ help: a tuple struct with a similar name exists (notice the capitalization): `Foo`
+   |
+   = note: function calls are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr
index fc431eb1412..d40e9822435 100644
--- a/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr
+++ b/tests/ui/sized/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.stderr
@@ -2,7 +2,9 @@ error: expected a pattern, found an expression
   --> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:31
    |
 LL |     let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes;
-   |                               ^^^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
+   |                               ^^^^^^^^^^^^^^^^^^ not a pattern
+   |
+   = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch18-00-patterns.html>
 
 error[E0412]: cannot find type `T` in this scope
   --> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:55