diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 3c576316f6230..39cda360afb14 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2571,6 +2571,9 @@ pub enum TyPatKind {
     /// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`).
     Range(Option<P<AnonConst>>, Option<P<AnonConst>>, Spanned<RangeEnd>),
 
+    /// A `!null` pattern for raw pointers.
+    NotNull,
+
     Or(ThinVec<P<TyPat>>),
 
     /// Placeholder for a pattern that wasn't syntactically well formed in some way.
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 37fcc0d2167b2..1f29abb8c682c 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -1208,7 +1208,7 @@ macro_rules! common_visitor_and_walkers {
                     try_visit!(visit_span(vis, span));
                 }
                 TyPatKind::Or(variants) => walk_list!(vis, visit_ty_pat, variants),
-                TyPatKind::Err(_) => {}
+                TyPatKind::NotNull | TyPatKind::Err(_) => {}
             }
             visit_span(vis, span)
         }
diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs
index e444062104813..e7c7471611ee3 100644
--- a/compiler/rustc_ast_lowering/src/pat.rs
+++ b/compiler/rustc_ast_lowering/src/pat.rs
@@ -143,7 +143,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     }
                     // return inner to be processed in next loop
                     PatKind::Paren(inner) => pattern = inner,
-                    PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", pattern.span),
+                    PatKind::MacCall(_) => {
+                        panic!("{pattern:#?} shouldn't exist here")
+                    }
                     PatKind::Err(guar) => break hir::PatKind::Err(*guar),
                 }
             };
@@ -460,6 +462,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         )
                     }),
             ),
+            TyPatKind::NotNull => hir::TyPatKind::NotNull,
             TyPatKind::Or(variants) => {
                 hir::TyPatKind::Or(self.arena.alloc_from_iter(
                     variants.iter().map(|pat| self.lower_ty_pat_mut(pat, base_type)),
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index def0cb74d295b..0e5fdcc07b7c6 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1234,6 +1234,7 @@ impl<'a> State<'a> {
                     self.print_expr_anon_const(end, &[]);
                 }
             }
+            rustc_ast::TyPatKind::NotNull => self.word("!null"),
             rustc_ast::TyPatKind::Or(variants) => {
                 let mut first = true;
                 for pat in variants {
diff --git a/compiler/rustc_builtin_macros/src/pattern_type.rs b/compiler/rustc_builtin_macros/src/pattern_type.rs
index b61af0b0aaa12..9768646e5f919 100644
--- a/compiler/rustc_builtin_macros/src/pattern_type.rs
+++ b/compiler/rustc_builtin_macros/src/pattern_type.rs
@@ -28,15 +28,21 @@ fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P
     let ty = parser.parse_ty()?;
     parser.expect_keyword(exp!(Is))?;
 
-    let pat = pat_to_ty_pat(
-        cx,
-        *parser.parse_pat_no_top_guard(
-            None,
-            RecoverComma::No,
-            RecoverColon::No,
-            CommaRecoveryMode::EitherTupleOrPipe,
-        )?,
-    );
+    let start = parser.token.span;
+    let pat = if parser.eat(exp!(Bang)) {
+        parser.expect_keyword(exp!(Null))?;
+        ty_pat(TyPatKind::NotNull, start.to(parser.token.span))
+    } else {
+        pat_to_ty_pat(
+            cx,
+            *parser.parse_pat_no_top_guard(
+                None,
+                RecoverComma::No,
+                RecoverColon::No,
+                CommaRecoveryMode::EitherTupleOrPipe,
+            )?,
+        )
+    };
 
     if parser.token != token::Eof {
         parser.unexpected()?;
diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs
index 2aee0b2e97424..2dba3a0fa6f1e 100644
--- a/compiler/rustc_codegen_cranelift/src/unsize.rs
+++ b/compiler/rustc_codegen_cranelift/src/unsize.rs
@@ -133,6 +133,11 @@ pub(crate) fn coerce_unsized_into<'tcx>(
         dst.write_cvalue(fx, CValue::by_val_pair(base, info, dst.layout()));
     };
     match (&src_ty.kind(), &dst_ty.kind()) {
+        (ty::Pat(a, _), ty::Pat(b, _)) => {
+            let src = src.cast_pat_ty_to_base(fx.layout_of(*a));
+            let dst = dst.place_transmute_type(fx, *b);
+            return coerce_unsized_into(fx, src, dst);
+        }
         (&ty::Ref(..), &ty::Ref(..))
         | (&ty::Ref(..), &ty::RawPtr(..))
         | (&ty::RawPtr(..), &ty::RawPtr(..)) => coerce_ptr(),
diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
index 9d73f200afe2b..0c98c4f33f80d 100644
--- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs
+++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
@@ -342,6 +342,14 @@ impl<'tcx> CValue<'tcx> {
         assert_eq!(self.layout().backend_repr, layout.backend_repr);
         CValue(self.0, layout)
     }
+
+    pub(crate) fn cast_pat_ty_to_base(self, layout: TyAndLayout<'tcx>) -> Self {
+        let ty::Pat(base, _) = *self.layout().ty.kind() else {
+            panic!("not a pattern type: {:#?}", self.layout())
+        };
+        assert_eq!(layout.ty, base);
+        CValue(self.0, layout)
+    }
 }
 
 /// A place where you can write a value to or read a value from
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 9b4736e50e6c3..079b89a5ca389 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -463,7 +463,18 @@ pub(crate) fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) ->
             AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id),
         },
         ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id),
-        _ => bug!("debuginfo: unexpected type in type_di_node(): {:?}", t),
+        ty::Pat(base, _) => return type_di_node(cx, base),
+        // FIXME(unsafe_binders): impl debug info
+        ty::UnsafeBinder(_) => unimplemented!(),
+        ty::Alias(..)
+        | ty::Param(_)
+        | ty::Bound(..)
+        | ty::Infer(_)
+        | ty::Placeholder(_)
+        | ty::CoroutineWitness(..)
+        | ty::Error(_) => {
+            bug!("debuginfo: unexpected type in type_di_node(): {:?}", t)
+        }
     };
 
     {
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index 18581f854b664..dc50041203b3e 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -227,6 +227,7 @@ pub(crate) fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
 ) -> (Bx::Value, Bx::Value) {
     debug!("unsize_ptr: {:?} => {:?}", src_ty, dst_ty);
     match (src_ty.kind(), dst_ty.kind()) {
+        (&ty::Pat(a, _), &ty::Pat(b, _)) => unsize_ptr(bx, src, a, b, old_info),
         (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _))
         | (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => {
             assert_eq!(bx.cx().type_is_sized(a), old_info.is_none());
diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs
index ad3e02580f338..53c726e00777e 100644
--- a/compiler/rustc_const_eval/src/interpret/call.rs
+++ b/compiler/rustc_const_eval/src/interpret/call.rs
@@ -86,6 +86,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 let (_, field) = layout.non_1zst_field(self).unwrap();
                 self.unfold_transparent(field, may_unfold)
             }
+            ty::Pat(base, _) => self.layout_of(*base).expect(
+                "if the layout of a pattern type could be computed, so can the layout of its base",
+            ),
             // Not a transparent type, no further unfolding.
             _ => layout,
         }
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index 7a73d70fc85dc..179d6d8d5cfbd 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -459,6 +459,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     ) -> InterpResult<'tcx> {
         trace!("Unsizing {:?} of type {} into {}", *src, src.layout.ty, cast_ty.ty);
         match (src.layout.ty.kind(), cast_ty.ty.kind()) {
+            (&ty::Pat(_, s_pat), &ty::Pat(cast_ty, c_pat)) if s_pat == c_pat => {
+                let src = self.project_field(src, FieldIdx::ZERO)?;
+                let dest = self.project_field(dest, FieldIdx::ZERO)?;
+                let cast_ty = self.layout_of(cast_ty)?;
+                self.unsize_into(&src, cast_ty, &dest)
+            }
             (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(c, _))
             | (&ty::RawPtr(s, _), &ty::RawPtr(c, _)) => self.unsize_into_ptr(src, dest, s, c),
             (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index fc44490c96d37..fce61a040eac4 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -1251,9 +1251,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt,
                 // When you extend this match, make sure to also add tests to
                 // tests/ui/type/pattern_types/validity.rs((
                 match **pat {
-                    // Range patterns are precisely reflected into `valid_range` and thus
+                    // Range and non-null patterns are precisely reflected into `valid_range` and thus
                     // handled fully by `visit_scalar` (called below).
                     ty::PatternKind::Range { .. } => {},
+                    ty::PatternKind::NotNull => {},
 
                     // FIXME(pattern_types): check that the value is covered by one of the variants.
                     // For now, we rely on layout computation setting the scalar's `valid_range` to
diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs
index a27b664613159..16e492ff7152f 100644
--- a/compiler/rustc_const_eval/src/interpret/visitor.rs
+++ b/compiler/rustc_const_eval/src/interpret/visitor.rs
@@ -139,7 +139,12 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
                 );
                 // ... that contains a `NonNull`... (gladly, only a single field here)
                 assert_eq!(nonnull_ptr.layout().fields.count(), 1);
-                let raw_ptr = self.ecx().project_field(&nonnull_ptr, FieldIdx::ZERO)?; // the actual raw ptr
+                let pat_ty = self.ecx().project_field(&nonnull_ptr, FieldIdx::ZERO)?; // `*mut T is !null`
+                let base = match *pat_ty.layout().ty.kind() {
+                    ty::Pat(base, _) => self.ecx().layout_of(base)?,
+                    _ => unreachable!(),
+                };
+                let raw_ptr = pat_ty.transmute(base, self.ecx())?; // The actual raw pointer
                 // ... whose only field finally is a raw ptr we can dereference.
                 self.visit_box(ty, &raw_ptr)?;
 
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 159518a8d8716..37c18477329fa 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1829,6 +1829,9 @@ pub enum TyPatKind<'hir> {
     /// A range pattern (e.g., `1..=2` or `1..2`).
     Range(&'hir ConstArg<'hir>, &'hir ConstArg<'hir>),
 
+    /// A pattern that excludes null pointers
+    NotNull,
+
     /// A list of patterns where only one needs to be satisfied
     Or(&'hir [TyPat<'hir>]),
 
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 3edb94c28da7f..9815529fc3d39 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -723,7 +723,7 @@ pub fn walk_ty_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v TyPat<'v>)
             try_visit!(visitor.visit_const_arg_unambig(upper_bound));
         }
         TyPatKind::Or(patterns) => walk_list!(visitor, visit_pattern_type_pattern, patterns),
-        TyPatKind::Err(_) => (),
+        TyPatKind::NotNull | TyPatKind::Err(_) => (),
     }
     V::Result::output()
 }
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 529d3578985ae..2c97e23077714 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -106,6 +106,8 @@ hir_analysis_coerce_pointee_not_struct = `derive(CoercePointee)` is only applica
 
 hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout
 
+hir_analysis_coerce_same_pat_kind = only pattern types with the same pattern can be coerced between each other
+
 hir_analysis_coerce_unsized_field_validity = for `{$ty}` to have a valid implementation of `{$trait_name}`, it must be possible to coerce the field of type `{$field_ty}`
     .label = `{$field_ty}` must be a pointer, reference, or smart pointer that is allowed to be unsized
 
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 8356a0af63c35..68085f095a945 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -248,6 +248,18 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
     // in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent)
     // even if they do not carry that attribute.
     match (source.kind(), target.kind()) {
+        (&ty::Pat(_, pat_a), &ty::Pat(_, pat_b)) => {
+            if pat_a != pat_b {
+                return Err(tcx.dcx().emit_err(errors::CoerceSamePatKind {
+                    span,
+                    trait_name,
+                    pat_a: pat_a.to_string(),
+                    pat_b: pat_b.to_string(),
+                }));
+            }
+            Ok(())
+        }
+
         (&ty::Ref(r_a, _, mutbl_a), ty::Ref(r_b, _, mutbl_b))
             if r_a == *r_b && mutbl_a == *mutbl_b =>
         {
@@ -413,6 +425,18 @@ pub(crate) fn coerce_unsized_info<'tcx>(
         (mt_a.ty, mt_b.ty, unsize_trait, None, span)
     };
     let (source, target, trait_def_id, kind, field_span) = match (source.kind(), target.kind()) {
+        (&ty::Pat(ty_a, pat_a), &ty::Pat(ty_b, pat_b)) => {
+            if pat_a != pat_b {
+                return Err(tcx.dcx().emit_err(errors::CoerceSamePatKind {
+                    span,
+                    trait_name,
+                    pat_a: pat_a.to_string(),
+                    pat_b: pat_b.to_string(),
+                }));
+            }
+            (ty_a, ty_b, coerce_unsized_trait, None, span)
+        }
+
         (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
             infcx.sub_regions(SubregionOrigin::RelateObjectBound(span), r_b, r_a);
             let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index c75fef9f716d6..6660ed4181084 100644
--- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -206,12 +206,8 @@ pub(crate) fn orphan_check_impl(
                 (LocalImpl::Disallow { problematic_kind }, NonlocalImpl::DisallowOther)
             }
 
-            ty::Pat(..) => (
-                LocalImpl::Disallow { problematic_kind: "pattern type" },
-                NonlocalImpl::DisallowOther,
-            ),
-
             ty::Bool
+            | ty::Pat(..)
             | ty::Char
             | ty::Int(..)
             | ty::Uint(..)
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index c1c828392126f..b283ea95e4365 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -1279,6 +1279,16 @@ pub(crate) struct CoerceUnsizedNonStruct {
     pub trait_name: &'static str,
 }
 
+#[derive(Diagnostic)]
+#[diag(hir_analysis_coerce_same_pat_kind)]
+pub(crate) struct CoerceSamePatKind {
+    #[primary_span]
+    pub span: Span,
+    pub trait_name: &'static str,
+    pub pat_a: String,
+    pub pat_b: String,
+}
+
 #[derive(Diagnostic)]
 #[diag(hir_analysis_coerce_unsized_may, code = E0377)]
 pub(crate) struct CoerceSameStruct {
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index a5bd7c1a34aef..f67019a1cf7cd 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -2579,6 +2579,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         .span_delayed_bug(ty_span, "invalid base type for range pattern")),
                 }
             }
+            hir::TyPatKind::NotNull => Ok(ty::PatternKind::NotNull),
             hir::TyPatKind::Or(patterns) => {
                 self.tcx()
                     .mk_patterns_from_iter(patterns.iter().map(|pat| {
diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs
index 960ec7f66ab15..09da0e2ec6511 100644
--- a/compiler/rustc_hir_analysis/src/variance/constraints.rs
+++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs
@@ -340,6 +340,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
                 self.add_constraints_from_const(current, start, variance);
                 self.add_constraints_from_const(current, end, variance);
             }
+            ty::PatternKind::NotNull => {}
             ty::PatternKind::Or(patterns) => {
                 for pat in patterns {
                     self.add_constraints_from_pat(current, variance, pat)
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 2c13c9ef43878..906a4997910cd 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -1874,6 +1874,10 @@ impl<'a> State<'a> {
                 self.word("..=");
                 self.print_const_arg(end);
             }
+            TyPatKind::NotNull => {
+                self.word_space("not");
+                self.word("null");
+            }
             TyPatKind::Or(patterns) => {
                 self.popen();
                 let mut first = true;
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index fc9d795cb2315..481fc2012682e 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -904,6 +904,7 @@ fn pat_ty_is_known_nonnull<'tcx>(
                     // to ensure we aren't wrapping over zero.
                     start > 0 && end >= start
                 }
+                ty::PatternKind::NotNull => true,
                 ty::PatternKind::Or(patterns) => {
                     patterns.iter().all(|pat| pat_ty_is_known_nonnull(tcx, typing_env, pat))
                 }
@@ -1065,7 +1066,9 @@ fn get_nullable_type_from_pat<'tcx>(
     pat: ty::Pattern<'tcx>,
 ) -> Option<Ty<'tcx>> {
     match *pat {
-        ty::PatternKind::Range { .. } => get_nullable_type(tcx, typing_env, base),
+        ty::PatternKind::NotNull | ty::PatternKind::Range { .. } => {
+            get_nullable_type(tcx, typing_env, base)
+        }
         ty::PatternKind::Or(patterns) => {
             let first = get_nullable_type_from_pat(tcx, typing_env, base, patterns[0])?;
             for &pat in &patterns[1..] {
diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs
index c498e6b3c83da..34b5aafc6aec7 100644
--- a/compiler/rustc_middle/src/traits/select.rs
+++ b/compiler/rustc_middle/src/traits/select.rs
@@ -154,6 +154,9 @@ pub enum SelectionCandidate<'tcx> {
     /// types generated for a fn pointer type (e.g., `fn(int) -> int`)
     FnPointerCandidate,
 
+    /// Builtin impl of the `PointerLike` trait.
+    PointerLikeCandidate,
+
     TraitAliasCandidate,
 
     /// Matching `dyn Trait` with a supertrait of `Trait`. The index is the
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 809717513c795..a99049ff92dcc 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -808,11 +808,21 @@ where
                 | ty::FnDef(..)
                 | ty::CoroutineWitness(..)
                 | ty::Foreign(..)
-                | ty::Pat(_, _)
                 | ty::Dynamic(_, _, ty::Dyn) => {
                     bug!("TyAndLayout::field({:?}): not applicable", this)
                 }
 
+                // May contain wide pointers
+                ty::Pat(base, pat) => match *pat {
+                    ty::PatternKind::NotNull => {
+                        assert_eq!(i, 0);
+                        TyMaybeWithLayout::Ty(base)
+                    }
+                    ty::PatternKind::Range { .. } | ty::PatternKind::Or(_) => {
+                        bug!("TyAndLayout::field({this:?}): only applicable to !null patterns")
+                    }
+                },
+
                 ty::UnsafeBinder(bound_ty) => {
                     let ty = tcx.instantiate_bound_regions_with_erased(bound_ty.into());
                     field_ty_or_layout(TyAndLayout { ty, ..this }, cx, i)
diff --git a/compiler/rustc_middle/src/ty/pattern.rs b/compiler/rustc_middle/src/ty/pattern.rs
index 5af9b17dd7777..335e5c0647435 100644
--- a/compiler/rustc_middle/src/ty/pattern.rs
+++ b/compiler/rustc_middle/src/ty/pattern.rs
@@ -30,6 +30,7 @@ impl<'tcx> Flags for Pattern<'tcx> {
                 }
                 flags
             }
+            ty::PatternKind::NotNull => rustc_type_ir::TypeFlags::empty(),
         }
     }
 
@@ -45,6 +46,7 @@ impl<'tcx> Flags for Pattern<'tcx> {
                 }
                 idx
             }
+            ty::PatternKind::NotNull => rustc_type_ir::INNERMOST,
         }
     }
 }
@@ -91,6 +93,7 @@ impl<'tcx> IrPrint<PatternKind<'tcx>> for TyCtxt<'tcx> {
 
                 write!(f, "..={end}")
             }
+            PatternKind::NotNull => write!(f, "!null"),
             PatternKind::Or(patterns) => {
                 write!(f, "(")?;
                 let mut first = true;
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index dc1d60f3d43c1..a4d7423818ea2 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -59,6 +59,7 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for ty::Pattern<'tcx> {
                 let end = relation.relate(end_a, end_b)?;
                 Ok(tcx.mk_pat(ty::PatternKind::Range { start, end }))
             }
+            (ty::PatternKind::NotNull, ty::PatternKind::NotNull) => Ok(a),
             (&ty::PatternKind::Or(a), &ty::PatternKind::Or(b)) => {
                 if a.len() != b.len() {
                     return Err(TypeError::Mismatch);
@@ -67,7 +68,10 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for ty::Pattern<'tcx> {
                 let patterns = tcx.mk_patterns_from_iter(v)?;
                 Ok(tcx.mk_pat(ty::PatternKind::Or(patterns)))
             }
-            (ty::PatternKind::Range { .. } | ty::PatternKind::Or(_), _) => Err(TypeError::Mismatch),
+            (
+                ty::PatternKind::NotNull | ty::PatternKind::Range { .. } | ty::PatternKind::Or(_),
+                _,
+            ) => Err(TypeError::Mismatch),
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
index 5c344a806880c..c187503f2f2d8 100644
--- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
@@ -7,7 +7,7 @@ use rustc_hir::def_id::DefId;
 use rustc_middle::mir::visit::MutVisitor;
 use rustc_middle::mir::*;
 use rustc_middle::span_bug;
-use rustc_middle::ty::{Ty, TyCtxt};
+use rustc_middle::ty::{PatternKind, Ty, TyCtxt};
 
 use crate::patch::MirPatch;
 
@@ -17,13 +17,14 @@ fn build_ptr_tys<'tcx>(
     pointee: Ty<'tcx>,
     unique_did: DefId,
     nonnull_did: DefId,
-) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
+) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
     let args = tcx.mk_args(&[pointee.into()]);
     let unique_ty = tcx.type_of(unique_did).instantiate(tcx, args);
     let nonnull_ty = tcx.type_of(nonnull_did).instantiate(tcx, args);
     let ptr_ty = Ty::new_imm_ptr(tcx, pointee);
+    let pat_ty = Ty::new_pat(tcx, ptr_ty, tcx.mk_pat(PatternKind::NotNull));
 
-    (unique_ty, nonnull_ty, ptr_ty)
+    (unique_ty, nonnull_ty, pat_ty, ptr_ty)
 }
 
 /// Constructs the projection needed to access a Box's pointer
@@ -63,7 +64,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'a, 'tcx> {
         {
             let source_info = self.local_decls[place.local].source_info;
 
-            let (unique_ty, nonnull_ty, ptr_ty) =
+            let (unique_ty, nonnull_ty, _pat_ty, ptr_ty) =
                 build_ptr_tys(tcx, boxed_ty, self.unique_did, self.nonnull_did);
 
             let ptr_local = self.patch.new_temp(ptr_ty, source_info.span);
@@ -130,10 +131,11 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs {
                         let new_projections =
                             new_projections.get_or_insert_with(|| base.projection.to_vec());
 
-                        let (unique_ty, nonnull_ty, ptr_ty) =
+                        let (unique_ty, nonnull_ty, pat_ty, ptr_ty) =
                             build_ptr_tys(tcx, boxed_ty, unique_did, nonnull_did);
 
                         new_projections.extend_from_slice(&build_projection(unique_ty, nonnull_ty));
+                        new_projections.push(PlaceElem::Field(FieldIdx::ZERO, pat_ty));
                         // While we can't project into `NonNull<_>` in a basic block
                         // due to MCP#807, this is debug info where it's fine.
                         new_projections.push(PlaceElem::Field(FieldIdx::ZERO, ptr_ty));
diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs
index cbb9bbfd12f9f..05847139ddf91 100644
--- a/compiler/rustc_mir_transform/src/validate.rs
+++ b/compiler/rustc_mir_transform/src/validate.rs
@@ -707,6 +707,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                         };
                         check_equal(self, location, *f_ty);
                     }
+                    // Debug info is allowed to project into pattern types
+                    ty::Pat(base, _) => check_equal(self, location, *base),
                     ty::Adt(adt_def, args) => {
                         // see <https://github.com/rust-lang/rust/blob/7601adcc764d42c9f2984082b49948af652df986/compiler/rustc_middle/src/ty/layout.rs#L861-L864>
                         if self.tcx.is_lang_item(adt_def.did(), LangItem::DynMetadata) {
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 91c8e64ce9afe..3c172f2dfc4b2 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -1070,6 +1070,7 @@ fn find_tails_for_unsizing<'tcx>(
     debug_assert!(!target_ty.has_param(), "{target_ty} should be fully monomorphic");
 
     match (source_ty.kind(), target_ty.kind()) {
+        (&ty::Pat(source, _), &ty::Pat(target, _)) => find_tails_for_unsizing(tcx, source, target),
         (
             &ty::Ref(_, source_pointee, _),
             &ty::Ref(_, target_pointee, _) | &ty::RawPtr(target_pointee, _),
diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs
index b91548196a305..1d7ff549f1b09 100644
--- a/compiler/rustc_parse/src/parser/token_type.rs
+++ b/compiler/rustc_parse/src/parser/token_type.rs
@@ -139,6 +139,7 @@ pub enum TokenType {
     SymNomem,
     SymNoreturn,
     SymNostack,
+    SymNull,
     SymOptions,
     SymOut,
     SymPreservesFlags,
@@ -273,6 +274,7 @@ impl TokenType {
             SymNomem,
             SymNoreturn,
             SymNostack,
+            SymNull,
             SymOptions,
             SymOut,
             SymPreservesFlags,
@@ -348,6 +350,7 @@ impl TokenType {
             TokenType::SymNomem => Some(sym::nomem),
             TokenType::SymNoreturn => Some(sym::noreturn),
             TokenType::SymNostack => Some(sym::nostack),
+            TokenType::SymNull => Some(sym::null),
             TokenType::SymOptions => Some(sym::options),
             TokenType::SymOut => Some(sym::out),
             TokenType::SymPreservesFlags => Some(sym::preserves_flags),
@@ -562,6 +565,7 @@ macro_rules! exp {
     (Nomem)          => { exp!(@sym, nomem,           SymNomem) };
     (Noreturn)       => { exp!(@sym, noreturn,        SymNoreturn) };
     (Nostack)        => { exp!(@sym, nostack,         SymNostack) };
+    (Null)           => { exp!(@sym, null,            SymNull) };
     (Options)        => { exp!(@sym, options,         SymOptions) };
     (Out)            => { exp!(@sym, out,             SymOut) };
     (PreservesFlags) => { exp!(@sym, preserves_flags, SymPreservesFlags) };
diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs
index 75c2978878751..ba263ba981bde 100644
--- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs
+++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs
@@ -496,6 +496,7 @@ impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> {
                 end: Some(end.stable(tables, cx)),
                 include_end: true,
             },
+            ty::PatternKind::NotNull => todo!(),
             ty::PatternKind::Or(_) => todo!(),
         }
     }
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 44a567f78100d..3d4fb6b1427b4 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -976,7 +976,7 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc
                     self.visit_ty_pat(pat)
                 }
             }
-            TyPatKind::Err(_) => {}
+            TyPatKind::NotNull | TyPatKind::Err(_) => {}
         }
     }
 
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 8b12edf426c1b..d7871aa2a25e5 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1510,6 +1510,7 @@ symbols! {
         not,
         notable_trait,
         note,
+        null,
         object_safe_for_dispatch,
         of,
         off,
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index fe0f8e6113ef7..86221decf3df0 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -263,6 +263,9 @@ impl<'tcx> SymbolMangler<'tcx> {
                     Ty::new_array_with_const_len(self.tcx, self.tcx.types.unit, ct).print(self)?;
                 }
             }
+            ty::PatternKind::NotNull => {
+                self.tcx.types.unit.print(self)?;
+            }
             ty::PatternKind::Or(patterns) => {
                 for pat in patterns {
                     self.print_pat(pat)?;
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index ee8cef2027991..fb03ecb00eecf 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -115,6 +115,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 ImplSource::Builtin(BuiltinImplSource::Misc, data)
             }
 
+            PointerLikeCandidate => {
+                let data = self.confirm_pointer_like_candidate(obligation);
+                ImplSource::Builtin(BuiltinImplSource::Misc, data)
+            }
+
             TraitAliasCandidate => {
                 let data = self.confirm_trait_alias_candidate(obligation);
                 ImplSource::Builtin(BuiltinImplSource::Misc, data)
@@ -635,6 +640,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         Ok(nested)
     }
 
+    fn confirm_pointer_like_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> PredicateObligations<'tcx> {
+        debug!(?obligation, "confirm_pointer_like_candidate");
+        let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
+        let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
+        let ty::Pat(base, _) = *self_ty.kind() else { bug!() };
+        let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);
+
+        self.collect_predicates_for_types(
+            obligation.param_env,
+            cause,
+            obligation.recursion_depth + 1,
+            placeholder_predicate.def_id(),
+            vec![base],
+        )
+    }
+
     fn confirm_trait_alias_candidate(
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 2e65750db25de..a1a1ce022ebb8 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -1990,6 +1990,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 | TraitUpcastingUnsizeCandidate(_)
                 | BuiltinObjectCandidate
                 | BuiltinUnsizeCandidate
+                | PointerLikeCandidate
                 | BikeshedGuaranteedNoDropCandidate => false,
                 // Non-global param candidates have already been handled, global
                 // where-bounds get ignored.
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index fed9f254cdf83..779d9d3b6b9a3 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -704,6 +704,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
                 check(start);
                 check(end);
             }
+            ty::PatternKind::NotNull => {}
             ty::PatternKind::Or(patterns) => {
                 for pat in patterns {
                     self.add_wf_preds_for_pat_ty(base_ty, pat)
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index 163e2b3088374..cfeb35b050cb3 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -213,9 +213,7 @@ fn layout_of_uncached<'tcx>(
             let mut layout = LayoutData::clone(&layout.0);
             match *pat {
                 ty::PatternKind::Range { start, end } => {
-                    if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) =
-                        &mut layout.backend_repr
-                    {
+                    if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr {
                         scalar.valid_range_mut().start = extract_const_value(cx, ty, start)?
                             .try_to_bits(tcx, cx.typing_env)
                             .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
@@ -263,6 +261,31 @@ fn layout_of_uncached<'tcx>(
                         bug!("pattern type with range but not scalar layout: {ty:?}, {layout:?}")
                     }
                 }
+                ty::PatternKind::NotNull => {
+                    if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) =
+                        &mut layout.backend_repr
+                    {
+                        scalar.valid_range_mut().start = 1;
+                        let niche = Niche {
+                            offset: Size::ZERO,
+                            value: scalar.primitive(),
+                            valid_range: scalar.valid_range(cx),
+                        };
+
+                        layout.largest_niche = Some(niche);
+                        // Make wide pointer pattern types contain only a single field
+                        // of the wide pointer type itself.
+                        layout.fields = FieldsShape::Arbitrary {
+                            offsets: [Size::ZERO].into_iter().collect(),
+                            memory_index: [0].into_iter().collect(),
+                        }
+                    } else {
+                        bug!(
+                            "pattern type with `!null` pattern but not scalar/pair layout: {ty:?}, {layout:?}"
+                        )
+                    }
+                }
+
                 ty::PatternKind::Or(variants) => match *variants[0] {
                     ty::PatternKind::Range { .. } => {
                         if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr {
@@ -279,7 +302,7 @@ fn layout_of_uncached<'tcx>(
                                             .try_to_bits(tcx, cx.typing_env)
                                             .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?,
                                     )),
-                                    ty::PatternKind::Or(_) => {
+                                    ty::PatternKind::NotNull | ty::PatternKind::Or(_) => {
                                         unreachable!("mixed or patterns are not allowed")
                                     }
                                 })
@@ -344,6 +367,7 @@ fn layout_of_uncached<'tcx>(
                             )
                         }
                     }
+                    ty::PatternKind::NotNull => bug!("or patterns can't contain `!null` patterns"),
                     ty::PatternKind::Or(..) => bug!("patterns cannot have nested or patterns"),
                 },
             }
diff --git a/compiler/rustc_type_ir/src/pattern.rs b/compiler/rustc_type_ir/src/pattern.rs
index 7e56565917c67..8237254f8c44d 100644
--- a/compiler/rustc_type_ir/src/pattern.rs
+++ b/compiler/rustc_type_ir/src/pattern.rs
@@ -14,4 +14,5 @@ use crate::Interner;
 pub enum PatternKind<I: Interner> {
     Range { start: I::Const, end: I::Const },
     Or(I::PatList),
+    NotNull,
 }
diff --git a/compiler/rustc_type_ir/src/walk.rs b/compiler/rustc_type_ir/src/walk.rs
index 737550eb73e99..d1d707b896f0f 100644
--- a/compiler/rustc_type_ir/src/walk.rs
+++ b/compiler/rustc_type_ir/src/walk.rs
@@ -178,5 +178,6 @@ fn push_ty_pat<I: Interner>(stack: &mut TypeWalkerStack<I>, pat: I::Pat) {
                 push_ty_pat::<I>(stack, pat)
             }
         }
+        ty::PatternKind::NotNull => {}
     }
 }
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 2f701171505c7..adc0883dfeff8 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -115,6 +115,7 @@
 #![feature(link_cfg)]
 #![feature(offset_of_enum)]
 #![feature(panic_internals)]
+#![feature(pattern_type_macro)]
 #![feature(ptr_alignment_type)]
 #![feature(ptr_metadata)]
 #![feature(set_ptr_value)]
@@ -168,6 +169,7 @@
 #![feature(never_type)]
 #![feature(no_core)]
 #![feature(optimize_attribute)]
+#![feature(pattern_types)]
 #![feature(prelude_import)]
 #![feature(repr_simd)]
 #![feature(rustc_allow_const_fn_unstable)]
diff --git a/library/core/src/num/niche_types.rs b/library/core/src/num/niche_types.rs
index b92561c9e356d..da02468b81cb1 100644
--- a/library/core/src/num/niche_types.rs
+++ b/library/core/src/num/niche_types.rs
@@ -5,60 +5,48 @@
 )]
 
 use crate::cmp::Ordering;
-use crate::fmt;
 use crate::hash::{Hash, Hasher};
 use crate::marker::StructuralPartialEq;
+use crate::{fmt, pattern_type};
 
 macro_rules! define_valid_range_type {
     ($(
         $(#[$m:meta])*
-        $vis:vis struct $name:ident($int:ident as $uint:ident in $low:literal..=$high:literal);
+        $vis:vis struct $name:ident($int:ident is $pat:pat);
     )+) => {$(
-        #[derive(Clone, Copy, Eq)]
+        #[derive(Clone, Copy)]
         #[repr(transparent)]
-        #[rustc_layout_scalar_valid_range_start($low)]
-        #[rustc_layout_scalar_valid_range_end($high)]
         $(#[$m])*
-        $vis struct $name($int);
-
-        const _: () = {
-            // With the `valid_range` attributes, it's always specified as unsigned
-            assert!(<$uint>::MIN == 0);
-            let ulow: $uint = $low;
-            let uhigh: $uint = $high;
-            assert!(ulow <= uhigh);
-
-            assert!(size_of::<$int>() == size_of::<$uint>());
-        };
-
+        $vis struct $name(pattern_type!($int is $pat));
         impl $name {
             #[inline]
             pub const fn new(val: $int) -> Option<Self> {
-                if (val as $uint) >= ($low as $uint) && (val as $uint) <= ($high as $uint) {
-                    // SAFETY: just checked the inclusive range
-                    Some(unsafe { $name(val) })
+                #[allow(non_contiguous_range_endpoints)]
+                if let $pat = val {
+                    // SAFETY: just checked that the value matches the pattern
+                    Some(unsafe { $name(crate::mem::transmute(val)) })
                 } else {
                     None
                 }
             }
 
             /// Constructs an instance of this type from the underlying integer
-            /// primitive without checking whether its zero.
+            /// primitive without checking whether its valid.
             ///
             /// # Safety
-            /// Immediate language UB if `val == 0`, as it violates the validity
+            /// Immediate language UB if `val` is not in the range of the pattern type,
+            /// as it violates the validity
             /// invariant of this type.
             #[inline]
             pub const unsafe fn new_unchecked(val: $int) -> Self {
-                // SAFETY: Caller promised that `val` is non-zero.
-                unsafe { $name(val) }
+                // SAFETY: Caller promised that `val` is in the valid range.
+                unsafe { $name(crate::mem::transmute(val)) }
             }
 
             #[inline]
             pub const fn as_inner(self) -> $int {
-                // SAFETY: This is a transparent wrapper, so unwrapping it is sound
-                // (Not using `.0` due to MCP#807.)
-                unsafe { crate::mem::transmute(self) }
+                // SAFETY: pattern types are always legal values of their base type
+                unsafe { crate::mem::transmute(self.0) }
             }
         }
 
@@ -67,6 +55,8 @@ macro_rules! define_valid_range_type {
         // by <https://github.com/rust-lang/compiler-team/issues/807>.
         impl StructuralPartialEq for $name {}
 
+        impl Eq for $name {}
+
         impl PartialEq for $name {
             #[inline]
             fn eq(&self, other: &Self) -> bool {
@@ -104,7 +94,7 @@ macro_rules! define_valid_range_type {
 }
 
 define_valid_range_type! {
-    pub struct Nanoseconds(u32 as u32 in 0..=999_999_999);
+    pub struct Nanoseconds(u32 is 0..=999_999_999);
 }
 
 impl Nanoseconds {
@@ -119,47 +109,32 @@ impl Default for Nanoseconds {
     }
 }
 
-define_valid_range_type! {
-    pub struct NonZeroU8Inner(u8 as u8 in 1..=0xff);
-    pub struct NonZeroU16Inner(u16 as u16 in 1..=0xff_ff);
-    pub struct NonZeroU32Inner(u32 as u32 in 1..=0xffff_ffff);
-    pub struct NonZeroU64Inner(u64 as u64 in 1..=0xffffffff_ffffffff);
-    pub struct NonZeroU128Inner(u128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff);
-
-    pub struct NonZeroI8Inner(i8 as u8 in 1..=0xff);
-    pub struct NonZeroI16Inner(i16 as u16 in 1..=0xff_ff);
-    pub struct NonZeroI32Inner(i32 as u32 in 1..=0xffff_ffff);
-    pub struct NonZeroI64Inner(i64 as u64 in 1..=0xffffffff_ffffffff);
-    pub struct NonZeroI128Inner(i128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff);
-
-    pub struct NonZeroCharInner(char as u32 in 1..=0x10ffff);
-}
+const HALF_USIZE: usize = usize::MAX >> 1;
 
-#[cfg(target_pointer_width = "16")]
-define_valid_range_type! {
-    pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff);
-    pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff);
-    pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff);
-}
-#[cfg(target_pointer_width = "32")]
 define_valid_range_type! {
-    pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff);
-    pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff);
-    pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff);
-}
-#[cfg(target_pointer_width = "64")]
-define_valid_range_type! {
-    pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff_ffff_ffff);
-    pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff_ffff_ffff);
-    pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff_ffff_ffff);
-}
+    pub struct NonZeroU8Inner(u8 is 1..);
+    pub struct NonZeroU16Inner(u16 is 1..);
+    pub struct NonZeroU32Inner(u32 is 1..);
+    pub struct NonZeroU64Inner(u64 is 1..);
+    pub struct NonZeroU128Inner(u128 is 1..);
 
-define_valid_range_type! {
-    pub struct U32NotAllOnes(u32 as u32 in 0..=0xffff_fffe);
-    pub struct I32NotAllOnes(i32 as u32 in 0..=0xffff_fffe);
+    pub struct NonZeroI8Inner(i8 is ..0 | 1..);
+    pub struct NonZeroI16Inner(i16 is ..0 | 1..);
+    pub struct NonZeroI32Inner(i32 is ..0 | 1..);
+    pub struct NonZeroI64Inner(i64 is ..0 | 1..);
+    pub struct NonZeroI128Inner(i128 is ..0 | 1..);
+
+    pub struct UsizeNoHighBit(usize is 0..=HALF_USIZE);
+    pub struct NonZeroUsizeInner(usize is 1..);
+    pub struct NonZeroIsizeInner(isize is ..0 | 1..);
+
+    pub struct U32NotAllOnes(u32 is 0..u32::MAX);
+    pub struct I32NotAllOnes(i32 is ..-1 | 0..);
+
+    pub struct U64NotAllOnes(u64 is 0..u64::MAX);
+    pub struct I64NotAllOnes(i64 is ..-1 | 0..);
 
-    pub struct U64NotAllOnes(u64 as u64 in 0..=0xffff_ffff_ffff_fffe);
-    pub struct I64NotAllOnes(i64 as u64 in 0..=0xffff_ffff_ffff_fffe);
+    pub struct NonZeroCharInner(char is '\u{1}' ..= '\u{10ffff}');
 }
 
 pub trait NotAllOnesHelper {
diff --git a/library/core/src/pat.rs b/library/core/src/pat.rs
index 91d015b1bc53f..c818b9f77cd6f 100644
--- a/library/core/src/pat.rs
+++ b/library/core/src/pat.rs
@@ -1,5 +1,8 @@
 //! Helper module for exporting the `pattern_type` macro
 
+use crate::marker::{Freeze, PointeeSized, Unsize};
+use crate::ops::{CoerceUnsized, DispatchFromDyn};
+
 /// Creates a pattern type.
 /// ```ignore (cannot test this from within core yet)
 /// type Positive = std::pat::pattern_type!(i32 is 1..);
@@ -74,3 +77,16 @@ impl const RangePattern for char {
         }
     }
 }
+
+impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<pattern_type!(*const U is !null)> for pattern_type!(*const T is !null) where
+    T: Unsize<U>
+{
+}
+
+impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<pattern_type!(U is !null)> for pattern_type!(T is !null) {}
+
+impl<T: PointeeSized> Unpin for pattern_type!(*const T is !null) {}
+
+unsafe impl<T: PointeeSized> Freeze for pattern_type!(*const T is !null) {}
+
+unsafe impl<T: PointeeSized> Freeze for pattern_type!(*mut T is !null) {}
diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs
index c4ca29a367971..67c3b790bc379 100644
--- a/library/core/src/ptr/non_null.rs
+++ b/library/core/src/ptr/non_null.rs
@@ -1,6 +1,6 @@
 use crate::cmp::Ordering;
 use crate::marker::{PointeeSized, Unsize};
-use crate::mem::{MaybeUninit, SizedTypeProperties};
+use crate::mem::{MaybeUninit, SizedTypeProperties, transmute};
 use crate::num::NonZero;
 use crate::ops::{CoerceUnsized, DispatchFromDyn};
 use crate::pin::PinCoerceUnsized;
@@ -69,13 +69,10 @@ use crate::{fmt, hash, intrinsics, mem, ptr};
 /// [null pointer optimization]: crate::option#representation
 #[stable(feature = "nonnull", since = "1.25.0")]
 #[repr(transparent)]
-#[rustc_layout_scalar_valid_range_start(1)]
 #[rustc_nonnull_optimization_guaranteed]
 #[rustc_diagnostic_item = "NonNull"]
 pub struct NonNull<T: PointeeSized> {
-    // Remember to use `.as_ptr()` instead of `.pointer`, as field projecting to
-    // this is banned by <https://github.com/rust-lang/compiler-team/issues/807>.
-    pointer: *const T,
+    pointer: crate::pattern_type!(*const T is !null),
 }
 
 /// `NonNull` pointers are not `Send` because the data they reference may be aliased.
@@ -99,9 +96,9 @@ impl<T: Sized> NonNull<T> {
     #[must_use]
     #[inline]
     pub const fn without_provenance(addr: NonZero<usize>) -> Self {
-        let pointer = crate::ptr::without_provenance(addr.get());
+        let pointer: *const T = crate::ptr::without_provenance(addr.get());
         // SAFETY: we know `addr` is non-zero.
-        unsafe { NonNull { pointer } }
+        unsafe { NonNull { pointer: transmute(pointer) } }
     }
 
     /// Creates a new `NonNull` that is dangling, but well-aligned.
@@ -231,7 +228,7 @@ impl<T: PointeeSized> NonNull<T> {
                 "NonNull::new_unchecked requires that the pointer is non-null",
                 (ptr: *mut () = ptr as *mut ()) => !ptr.is_null()
             );
-            NonNull { pointer: ptr as _ }
+            NonNull { pointer: transmute(ptr) }
         }
     }
 
@@ -274,7 +271,7 @@ impl<T: PointeeSized> NonNull<T> {
     #[inline]
     pub const fn from_ref(r: &T) -> Self {
         // SAFETY: A reference cannot be null.
-        unsafe { NonNull { pointer: r as *const T } }
+        unsafe { NonNull { pointer: transmute(r as *const T) } }
     }
 
     /// Converts a mutable reference to a `NonNull` pointer.
@@ -283,7 +280,7 @@ impl<T: PointeeSized> NonNull<T> {
     #[inline]
     pub const fn from_mut(r: &mut T) -> Self {
         // SAFETY: A mutable reference cannot be null.
-        unsafe { NonNull { pointer: r as *mut T } }
+        unsafe { NonNull { pointer: transmute(r as *mut T) } }
     }
 
     /// Performs the same functionality as [`std::ptr::from_raw_parts`], except that a
@@ -494,7 +491,7 @@ impl<T: PointeeSized> NonNull<T> {
     #[inline]
     pub const fn cast<U>(self) -> NonNull<U> {
         // SAFETY: `self` is a `NonNull` pointer which is necessarily non-null
-        unsafe { NonNull { pointer: self.as_ptr() as *mut U } }
+        unsafe { NonNull { pointer: transmute(self.as_ptr() as *mut U) } }
     }
 
     /// Try to cast to a pointer of another type by checking alignment.
@@ -573,7 +570,7 @@ impl<T: PointeeSized> NonNull<T> {
         // Additionally safety contract of `offset` guarantees that the resulting pointer is
         // pointing to an allocation, there can't be an allocation at null, thus it's safe to
         // construct `NonNull`.
-        unsafe { NonNull { pointer: intrinsics::offset(self.as_ptr(), count) } }
+        unsafe { NonNull { pointer: transmute(intrinsics::offset(self.as_ptr(), count)) } }
     }
 
     /// Calculates the offset from a pointer in bytes.
@@ -597,7 +594,7 @@ impl<T: PointeeSized> NonNull<T> {
         // Additionally safety contract of `offset` guarantees that the resulting pointer is
         // pointing to an allocation, there can't be an allocation at null, thus it's safe to
         // construct `NonNull`.
-        unsafe { NonNull { pointer: self.as_ptr().byte_offset(count) } }
+        unsafe { NonNull { pointer: transmute(self.as_ptr().byte_offset(count)) } }
     }
 
     /// Adds an offset to a pointer (convenience for `.offset(count as isize)`).
@@ -649,7 +646,7 @@ impl<T: PointeeSized> NonNull<T> {
         // Additionally safety contract of `offset` guarantees that the resulting pointer is
         // pointing to an allocation, there can't be an allocation at null, thus it's safe to
         // construct `NonNull`.
-        unsafe { NonNull { pointer: intrinsics::offset(self.as_ptr(), count) } }
+        unsafe { NonNull { pointer: transmute(intrinsics::offset(self.as_ptr(), count)) } }
     }
 
     /// Calculates the offset from a pointer in bytes (convenience for `.byte_offset(count as isize)`).
@@ -673,7 +670,7 @@ impl<T: PointeeSized> NonNull<T> {
         // Additionally safety contract of `add` guarantees that the resulting pointer is pointing
         // to an allocation, there can't be an allocation at null, thus it's safe to construct
         // `NonNull`.
-        unsafe { NonNull { pointer: self.as_ptr().byte_add(count) } }
+        unsafe { NonNull { pointer: transmute(self.as_ptr().byte_add(count)) } }
     }
 
     /// Subtracts an offset from a pointer (convenience for
@@ -755,7 +752,7 @@ impl<T: PointeeSized> NonNull<T> {
         // Additionally safety contract of `sub` guarantees that the resulting pointer is pointing
         // to an allocation, there can't be an allocation at null, thus it's safe to construct
         // `NonNull`.
-        unsafe { NonNull { pointer: self.as_ptr().byte_sub(count) } }
+        unsafe { NonNull { pointer: transmute(self.as_ptr().byte_sub(count)) } }
     }
 
     /// Calculates the distance between two pointers within the same allocation. The returned value is in
diff --git a/library/std/src/os/unix/io/tests.rs b/library/std/src/os/unix/io/tests.rs
index fc147730578ac..ce5e7aac5a99d 100644
--- a/library/std/src/os/unix/io/tests.rs
+++ b/library/std/src/os/unix/io/tests.rs
@@ -2,8 +2,7 @@ use crate::os::unix::io::RawFd;
 
 #[test]
 fn test_raw_fd_layout() {
-    // `OwnedFd` and `BorrowedFd` use `rustc_layout_scalar_valid_range_start`
-    // and `rustc_layout_scalar_valid_range_end`, with values that depend on
+    // `OwnedFd` and `BorrowedFd` use pattern types, with ranges that depend on
     // the bit width of `RawFd`. If this ever changes, those values will need
     // to be updated.
     assert_eq!(size_of::<RawFd>(), 4);
diff --git a/library/std/src/os/wasi/io/tests.rs b/library/std/src/os/wasi/io/tests.rs
index c5c6a19a6c885..d18b9fe10cab0 100644
--- a/library/std/src/os/wasi/io/tests.rs
+++ b/library/std/src/os/wasi/io/tests.rs
@@ -2,8 +2,7 @@ use crate::os::wasi::io::RawFd;
 
 #[test]
 fn test_raw_fd_layout() {
-    // `OwnedFd` and `BorrowedFd` use `rustc_layout_scalar_valid_range_start`
-    // and `rustc_layout_scalar_valid_range_end`, with values that depend on
+    // `OwnedFd` and `BorrowedFd` use pattern types with ranges that depend on
     // the bit width of `RawFd`. If this ever changes, those values will need
     // to be updated.
     assert_eq!(size_of::<RawFd>(), 4);
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
index 26323af312288..81b4946595029 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
@@ -242,6 +242,10 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
     loop {
         ty = cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty).unwrap_or(ty);
         return match *ty.kind() {
+            ty::Pat(base, _) => {
+                ty = base;
+                continue;
+            },
             ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => {
                 ReducedTy::TypeErasure { raw_ptr_only: false }
             },
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index f0d7fb89c4463..c3c6e665ac829 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -1121,7 +1121,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                     self.hash_ty_pat(variant);
                 }
             },
-            TyPatKind::Err(_) => {},
+            TyPatKind::NotNull | TyPatKind::Err(_) => {},
         }
     }
 
diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.stderr b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.stderr
index efe89c0d220a7..4efc4613455b6 100644
--- a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.stderr
+++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: constructing invalid value at .0: encountered 0, but expected something greater or equal to 1
+error: Undefined Behavior: constructing invalid value at .0.0: encountered 0, but expected something greater or equal to 1
   --> tests/fail/validity/cast_fn_ptr_invalid_callee_ret.rs:LL:CC
    |
 LL |     f();
diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.stderr b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.stderr
index c370e19ef313f..fa0c7dde677e7 100644
--- a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.stderr
+++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: constructing invalid value at .0: encountered 0, but expected something greater or equal to 1
+error: Undefined Behavior: constructing invalid value at .0.0: encountered 0, but expected something greater or equal to 1
   --> tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs:LL:CC
    |
 LL |             Call(_res = f(*ptr), ReturnTo(retblock), UnwindContinue())
diff --git a/src/tools/miri/tests/pass/pattern-types.rs b/src/tools/miri/tests/pass/pattern-types.rs
new file mode 100644
index 0000000000000..90fe0de546915
--- /dev/null
+++ b/src/tools/miri/tests/pass/pattern-types.rs
@@ -0,0 +1,18 @@
+#![feature(pattern_types, pattern_type_macro, sized_hierarchy)]
+#![allow(dead_code)]
+
+use std::marker::PointeeSized;
+use std::mem::transmute;
+
+pub struct NonNull<T: PointeeSized> {
+    pointer: std::pat::pattern_type!(*const T is !null),
+}
+
+trait Trait {}
+impl Trait for () {}
+
+fn main() {
+    unsafe {
+        let _: NonNull<dyn Trait> = NonNull { pointer: transmute(&mut () as *mut dyn Trait) };
+    }
+}
diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs
index 9ee10d8627030..b22a8f31debed 100644
--- a/src/tools/rustfmt/src/types.rs
+++ b/src/tools/rustfmt/src/types.rs
@@ -1100,7 +1100,7 @@ impl Rewrite for ast::TyPat {
                 }
                 Ok(s)
             }
-            ast::TyPatKind::Err(_) => Err(RewriteError::Unknown),
+            ast::TyPatKind::NotNull | ast::TyPatKind::Err(_) => Err(RewriteError::Unknown),
         }
     }
 }
diff --git a/tests/codegen/loads.rs b/tests/codegen/loads.rs
index 88d67642b7250..e19af7a3c2bc1 100644
--- a/tests/codegen/loads.rs
+++ b/tests/codegen/loads.rs
@@ -58,7 +58,7 @@ pub fn load_raw_pointer<'a>(x: &*const i32) -> *const i32 {
 // CHECK-LABEL: @load_box
 #[no_mangle]
 pub fn load_box<'a>(x: Box<Box<i32>>) -> Box<i32> {
-    // CHECK: load ptr, ptr %{{.*}}, align [[PTR_ALIGNMENT]], !nonnull !{{[0-9]+}}, !align ![[ALIGN_4_META]], !noundef !{{[0-9]+}}
+    // CHECK: load ptr, ptr %{{.*}}, align [[PTR_ALIGNMENT]], !nonnull !{{[0-9]+}}, !noundef !{{[0-9]+}}
     *x
 }
 
diff --git a/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.32bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.32bit.diff
index b698d8f373575..f4d395335870a 100644
--- a/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.32bit.diff
+++ b/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.32bit.diff
@@ -13,8 +13,8 @@
           StorageLive(_1);
 -         _1 = const 1_usize as std::boxed::Box<Never> (Transmute);
 -         _2 = copy ((_1.0: std::ptr::Unique<Never>).0: std::ptr::NonNull<Never>) as *const Never (Transmute);
-+         _1 = const Box::<Never>(Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} }}, _marker: PhantomData::<Never> }}, std::alloc::Global);
-+         _2 = const std::ptr::NonNull::<Never> {{ pointer: {0x1 as *const Never} }} as *const Never (Transmute);
++         _1 = const Box::<Never>(Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} is !null }}, _marker: PhantomData::<Never> }}, std::alloc::Global);
++         _2 = const std::ptr::NonNull::<Never> {{ pointer: {0x1 as *const Never} is !null }} as *const Never (Transmute);
           unreachable;
       }
   }
diff --git a/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.64bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.64bit.diff
index b698d8f373575..f4d395335870a 100644
--- a/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.64bit.diff
+++ b/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.64bit.diff
@@ -13,8 +13,8 @@
           StorageLive(_1);
 -         _1 = const 1_usize as std::boxed::Box<Never> (Transmute);
 -         _2 = copy ((_1.0: std::ptr::Unique<Never>).0: std::ptr::NonNull<Never>) as *const Never (Transmute);
-+         _1 = const Box::<Never>(Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} }}, _marker: PhantomData::<Never> }}, std::alloc::Global);
-+         _2 = const std::ptr::NonNull::<Never> {{ pointer: {0x1 as *const Never} }} as *const Never (Transmute);
++         _1 = const Box::<Never>(Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} is !null }}, _marker: PhantomData::<Never> }}, std::alloc::Global);
++         _2 = const std::ptr::NonNull::<Never> {{ pointer: {0x1 as *const Never} is !null }} as *const Never (Transmute);
           unreachable;
       }
   }
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff
index 2c89670dcf7d7..4fc66f1bf7200 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff
@@ -21,7 +21,7 @@
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let mut _7: (*const [bool; 0]) is !null;
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::<usize>::get) {
@@ -45,19 +45,19 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
-          _6 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize));
+          _6 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize is 1..));
           StorageLive(_7);
-          _7 = const {0x1 as *const [bool; 0]};
-          _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
+          _7 = const {0x1 as *const [bool; 0]} is !null;
+          _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }};
           StorageDead(_7);
           StorageDead(_6);
-          _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
+          _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }}, _marker: PhantomData::<[bool; 0]> }};
           StorageDead(_5);
-          _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }};
+          _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }};
           StorageDead(_4);
-          _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
+          _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
           StorageDead(_3);
-          _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
+          _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
           StorageDead(_2);
           _0 = const ();
           drop(_1) -> [return: bb1, unwind unreachable];
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff
index 8fecfe224cc69..c9b9d2fb8bc96 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff
@@ -21,7 +21,7 @@
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let mut _7: (*const [bool; 0]) is !null;
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::<usize>::get) {
@@ -45,19 +45,19 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
-          _6 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize));
+          _6 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize is 1..));
           StorageLive(_7);
-          _7 = const {0x1 as *const [bool; 0]};
-          _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
+          _7 = const {0x1 as *const [bool; 0]} is !null;
+          _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }};
           StorageDead(_7);
           StorageDead(_6);
-          _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
+          _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }}, _marker: PhantomData::<[bool; 0]> }};
           StorageDead(_5);
-          _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }};
+          _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }};
           StorageDead(_4);
-          _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
+          _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
           StorageDead(_3);
-          _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
+          _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
           StorageDead(_2);
           _0 = const ();
           drop(_1) -> [return: bb1, unwind: bb2];
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff
index 976ea252c2f89..9b455a71afa24 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff
@@ -21,7 +21,7 @@
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let mut _7: (*const [bool; 0]) is !null;
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::<usize>::get) {
@@ -45,19 +45,19 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
-          _6 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize));
+          _6 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize is 1..));
           StorageLive(_7);
-          _7 = const {0x1 as *const [bool; 0]};
-          _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
+          _7 = const {0x1 as *const [bool; 0]} is !null;
+          _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }};
           StorageDead(_7);
           StorageDead(_6);
-          _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
+          _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }}, _marker: PhantomData::<[bool; 0]> }};
           StorageDead(_5);
-          _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }};
+          _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }};
           StorageDead(_4);
-          _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
+          _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
           StorageDead(_3);
-          _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
+          _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
           StorageDead(_2);
           _0 = const ();
           drop(_1) -> [return: bb1, unwind unreachable];
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff
index 6c59f5e3e2e86..fb9e46fa2c517 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff
@@ -21,7 +21,7 @@
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let mut _7: (*const [bool; 0]) is !null;
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::<usize>::get) {
@@ -45,19 +45,19 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
-          _6 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize));
+          _6 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize is 1..));
           StorageLive(_7);
-          _7 = const {0x1 as *const [bool; 0]};
-          _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
+          _7 = const {0x1 as *const [bool; 0]} is !null;
+          _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }};
           StorageDead(_7);
           StorageDead(_6);
-          _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
+          _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }}, _marker: PhantomData::<[bool; 0]> }};
           StorageDead(_5);
-          _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }};
+          _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }};
           StorageDead(_4);
-          _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
+          _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
           StorageDead(_3);
-          _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
+          _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
           StorageDead(_2);
           _0 = const ();
           drop(_1) -> [return: bb1, unwind: bb2];
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff
index 1f9cf6d6aca83..c8548a9f9a4b6 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff
@@ -21,7 +21,7 @@
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let mut _7: (*const [bool; 0]) is !null;
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::<usize>::get) {
@@ -46,25 +46,25 @@
           StorageLive(_5);
           StorageLive(_6);
 -         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as std::num::NonZero<usize> (Transmute);
-+         _6 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize));
++         _6 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize is 1..));
           StorageLive(_7);
--         _7 = copy _6 as *const [bool; 0] (Transmute);
--         _5 = NonNull::<[bool; 0]> { pointer: copy _7 };
-+         _7 = const {0x1 as *const [bool; 0]};
-+         _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
+-         _7 = copy _6 as (*const [bool; 0]) is !null (Transmute);
+-         _5 = NonNull::<[bool; 0]> { pointer: move _7 };
++         _7 = const {0x1 as *const [bool; 0]} is !null;
++         _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }};
           StorageDead(_7);
           StorageDead(_6);
 -         _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
-+         _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
++         _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }}, _marker: PhantomData::<[bool; 0]> }};
           StorageDead(_5);
 -         _3 = move _4 as std::ptr::Unique<[bool]> (PointerCoercion(Unsize, Implicit));
-+         _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }};
++         _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }};
           StorageDead(_4);
 -         _2 = Box::<[bool]>(copy _3, const std::alloc::Global);
-+         _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
++         _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
           StorageDead(_3);
 -         _1 = A { foo: move _2 };
-+         _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
++         _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
           StorageDead(_2);
           _0 = const ();
           drop(_1) -> [return: bb1, unwind unreachable];
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff
index a8760285fac11..5417d6555fc76 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff
@@ -21,7 +21,7 @@
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let mut _7: (*const [bool; 0]) is !null;
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::<usize>::get) {
@@ -46,25 +46,25 @@
           StorageLive(_5);
           StorageLive(_6);
 -         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as std::num::NonZero<usize> (Transmute);
-+         _6 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize));
++         _6 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize is 1..));
           StorageLive(_7);
--         _7 = copy _6 as *const [bool; 0] (Transmute);
--         _5 = NonNull::<[bool; 0]> { pointer: copy _7 };
-+         _7 = const {0x1 as *const [bool; 0]};
-+         _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
+-         _7 = copy _6 as (*const [bool; 0]) is !null (Transmute);
+-         _5 = NonNull::<[bool; 0]> { pointer: move _7 };
++         _7 = const {0x1 as *const [bool; 0]} is !null;
++         _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }};
           StorageDead(_7);
           StorageDead(_6);
 -         _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
-+         _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
++         _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }}, _marker: PhantomData::<[bool; 0]> }};
           StorageDead(_5);
 -         _3 = move _4 as std::ptr::Unique<[bool]> (PointerCoercion(Unsize, Implicit));
-+         _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }};
++         _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }};
           StorageDead(_4);
 -         _2 = Box::<[bool]>(copy _3, const std::alloc::Global);
-+         _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
++         _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
           StorageDead(_3);
 -         _1 = A { foo: move _2 };
-+         _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
++         _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
           StorageDead(_2);
           _0 = const ();
           drop(_1) -> [return: bb1, unwind: bb2];
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff
index c398ae70a1a3e..e046cc38ea331 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff
@@ -21,7 +21,7 @@
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let mut _7: (*const [bool; 0]) is !null;
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::<usize>::get) {
@@ -46,25 +46,25 @@
           StorageLive(_5);
           StorageLive(_6);
 -         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as std::num::NonZero<usize> (Transmute);
-+         _6 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize));
++         _6 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize is 1..));
           StorageLive(_7);
--         _7 = copy _6 as *const [bool; 0] (Transmute);
--         _5 = NonNull::<[bool; 0]> { pointer: copy _7 };
-+         _7 = const {0x1 as *const [bool; 0]};
-+         _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
+-         _7 = copy _6 as (*const [bool; 0]) is !null (Transmute);
+-         _5 = NonNull::<[bool; 0]> { pointer: move _7 };
++         _7 = const {0x1 as *const [bool; 0]} is !null;
++         _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }};
           StorageDead(_7);
           StorageDead(_6);
 -         _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
-+         _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
++         _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }}, _marker: PhantomData::<[bool; 0]> }};
           StorageDead(_5);
 -         _3 = move _4 as std::ptr::Unique<[bool]> (PointerCoercion(Unsize, Implicit));
-+         _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }};
++         _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }};
           StorageDead(_4);
 -         _2 = Box::<[bool]>(copy _3, const std::alloc::Global);
-+         _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
++         _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
           StorageDead(_3);
 -         _1 = A { foo: move _2 };
-+         _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
++         _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
           StorageDead(_2);
           _0 = const ();
           drop(_1) -> [return: bb1, unwind unreachable];
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff
index 02934c02587d2..91cb8622b45ea 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff
@@ -21,7 +21,7 @@
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let mut _7: (*const [bool; 0]) is !null;
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::<usize>::get) {
@@ -46,25 +46,25 @@
           StorageLive(_5);
           StorageLive(_6);
 -         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as std::num::NonZero<usize> (Transmute);
-+         _6 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize));
++         _6 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize is 1..));
           StorageLive(_7);
--         _7 = copy _6 as *const [bool; 0] (Transmute);
--         _5 = NonNull::<[bool; 0]> { pointer: copy _7 };
-+         _7 = const {0x1 as *const [bool; 0]};
-+         _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
+-         _7 = copy _6 as (*const [bool; 0]) is !null (Transmute);
+-         _5 = NonNull::<[bool; 0]> { pointer: move _7 };
++         _7 = const {0x1 as *const [bool; 0]} is !null;
++         _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }};
           StorageDead(_7);
           StorageDead(_6);
 -         _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
-+         _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
++         _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} is !null }}, _marker: PhantomData::<[bool; 0]> }};
           StorageDead(_5);
 -         _3 = move _4 as std::ptr::Unique<[bool]> (PointerCoercion(Unsize, Implicit));
-+         _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }};
++         _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }};
           StorageDead(_4);
 -         _2 = Box::<[bool]>(copy _3, const std::alloc::Global);
-+         _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
++         _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global);
           StorageDead(_3);
 -         _1 = A { foo: move _2 };
-+         _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
++         _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: (*const [bool]) is !null }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }};
           StorageDead(_2);
           _0 = const ();
           drop(_1) -> [return: bb1, unwind: bb2];
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff
index fa6c2e29e072e..802d6daa1a27d 100644
--- a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff
@@ -12,7 +12,7 @@
       bb0: {
           StorageLive(_1);
 -         _1 = const 1_usize as std::boxed::Box<Never> (Transmute);
-+         _1 = const Box::<Never>(Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} }}, _marker: PhantomData::<Never> }}, std::alloc::Global);
++         _1 = const Box::<Never>(Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} is !null }}, _marker: PhantomData::<Never> }}, std::alloc::Global);
           _2 = copy ((_1.0: std::ptr::Unique<Never>).0: std::ptr::NonNull<Never>) as *const Never (Transmute);
           unreachable;
       }
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff
index fa6c2e29e072e..802d6daa1a27d 100644
--- a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff
@@ -12,7 +12,7 @@
       bb0: {
           StorageLive(_1);
 -         _1 = const 1_usize as std::boxed::Box<Never> (Transmute);
-+         _1 = const Box::<Never>(Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} }}, _marker: PhantomData::<Never> }}, std::alloc::Global);
++         _1 = const Box::<Never>(Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} is !null }}, _marker: PhantomData::<Never> }}, std::alloc::Global);
           _2 = copy ((_1.0: std::ptr::Unique<Never>).0: std::ptr::NonNull<Never>) as *const Never (Transmute);
           unreachable;
       }
diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff
index 25ffff619e60b..16a3dffcce07d 100644
--- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff
+++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff
@@ -10,7 +10,7 @@
       let mut _8: *const [()];
       let mut _9: std::boxed::Box<()>;
       let mut _10: *const ();
-      let mut _23: usize;
+      let mut _25: usize;
       scope 1 {
           debug vp_ctx => _1;
           let _4: *const ();
@@ -47,7 +47,9 @@
                       scope 11 (inlined NonNull::<[u8]>::as_mut_ptr) {
                           scope 12 (inlined NonNull::<[u8]>::as_non_null_ptr) {
                               scope 13 (inlined NonNull::<[u8]>::cast::<u8>) {
-                                  let mut _22: *mut [u8];
+                                  let mut _22: (*const u8) is !null;
+                                  let mut _23: *mut u8;
+                                  let mut _24: *mut [u8];
                                   scope 14 (inlined NonNull::<[u8]>::as_ptr) {
                                   }
                               }
@@ -105,8 +107,14 @@
       bb4: {
           _17 = copy ((_15 as Ok).0: std::ptr::NonNull<[u8]>);
           StorageLive(_22);
-          _22 = copy _17 as *mut [u8] (Transmute);
-          _13 = copy _22 as *mut u8 (PtrToPtr);
+          StorageLive(_23);
+          StorageLive(_24);
+          _24 = copy _17 as *mut [u8] (Transmute);
+          _23 = move _24 as *mut u8 (PtrToPtr);
+          StorageDead(_24);
+          _22 = move _23 as (*const u8) is !null (Transmute);
+          StorageDead(_23);
+          _13 = copy _22 as *mut u8 (Transmute);
           StorageDead(_22);
           StorageDead(_15);
           StorageDead(_17);
@@ -129,11 +137,11 @@
           StorageLive(_6);
 -         _6 = copy _4;
 +         _6 = copy _10;
-          StorageLive(_23);
-          _23 = const 1_usize;
--         _5 = *const [()] from (copy _6, copy _23);
+          StorageLive(_25);
+          _25 = const 1_usize;
+-         _5 = *const [()] from (copy _6, copy _25);
 +         _5 = *const [()] from (copy _10, const 1_usize);
-          StorageDead(_23);
+          StorageDead(_25);
           StorageDead(_6);
           StorageLive(_7);
           StorageLive(_8);
diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff
index 839b53e3b0b3b..592734e01e5a0 100644
--- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff
+++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff
@@ -10,7 +10,7 @@
       let mut _8: *const [()];
       let mut _9: std::boxed::Box<()>;
       let mut _10: *const ();
-      let mut _23: usize;
+      let mut _25: usize;
       scope 1 {
           debug vp_ctx => _1;
           let _4: *const ();
@@ -47,7 +47,9 @@
                       scope 11 (inlined NonNull::<[u8]>::as_mut_ptr) {
                           scope 12 (inlined NonNull::<[u8]>::as_non_null_ptr) {
                               scope 13 (inlined NonNull::<[u8]>::cast::<u8>) {
-                                  let mut _22: *mut [u8];
+                                  let mut _22: (*const u8) is !null;
+                                  let mut _23: *mut u8;
+                                  let mut _24: *mut [u8];
                                   scope 14 (inlined NonNull::<[u8]>::as_ptr) {
                                   }
                               }
@@ -105,8 +107,14 @@
       bb4: {
           _17 = copy ((_15 as Ok).0: std::ptr::NonNull<[u8]>);
           StorageLive(_22);
-          _22 = copy _17 as *mut [u8] (Transmute);
-          _13 = copy _22 as *mut u8 (PtrToPtr);
+          StorageLive(_23);
+          StorageLive(_24);
+          _24 = copy _17 as *mut [u8] (Transmute);
+          _23 = move _24 as *mut u8 (PtrToPtr);
+          StorageDead(_24);
+          _22 = move _23 as (*const u8) is !null (Transmute);
+          StorageDead(_23);
+          _13 = copy _22 as *mut u8 (Transmute);
           StorageDead(_22);
           StorageDead(_15);
           StorageDead(_17);
@@ -129,11 +137,11 @@
           StorageLive(_6);
 -         _6 = copy _4;
 +         _6 = copy _10;
-          StorageLive(_23);
-          _23 = const 1_usize;
--         _5 = *const [()] from (copy _6, copy _23);
+          StorageLive(_25);
+          _25 = const 1_usize;
+-         _5 = *const [()] from (copy _6, copy _25);
 +         _5 = *const [()] from (copy _10, const 1_usize);
-          StorageDead(_23);
+          StorageDead(_25);
           StorageDead(_6);
           StorageLive(_7);
           StorageLive(_8);
diff --git a/tests/mir-opt/elaborate_box_deref_in_debuginfo.pointee.ElaborateBoxDerefs.diff b/tests/mir-opt/elaborate_box_deref_in_debuginfo.pointee.ElaborateBoxDerefs.diff
index 279c1a1990dc8..6075d7895eeb3 100644
--- a/tests/mir-opt/elaborate_box_deref_in_debuginfo.pointee.ElaborateBoxDerefs.diff
+++ b/tests/mir-opt/elaborate_box_deref_in_debuginfo.pointee.ElaborateBoxDerefs.diff
@@ -3,7 +3,7 @@
   
   fn pointee(_1: Box<i32>) -> () {
 -     debug foo => (*_1);
-+     debug foo => (*(((_1.0: std::ptr::Unique<i32>).0: std::ptr::NonNull<i32>).0: *const i32));
++     debug foo => (*((((_1.0: std::ptr::Unique<i32>).0: std::ptr::NonNull<i32>).0: (*const i32) is !null).0: *const i32));
       let mut _0: ();
   
       bb0: {
diff --git a/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff b/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff
index f56af33ea603f..55fea9aef9101 100644
--- a/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff
+++ b/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff
@@ -12,6 +12,7 @@
                   scope 5 (inlined std::ptr::Alignment::as_nonzero) {
                   }
                   scope 6 (inlined NonNull::<u8>::without_provenance) {
+                      let mut _4: (*const u8) is !null;
                       scope 7 {
                       }
                       scope 8 (inlined NonZero::<usize>::get) {
@@ -29,9 +30,9 @@
           }
       }
       scope 12 (inlined Foo::<u8>::cmp_ptr) {
-          let mut _4: *const u8;
-          let mut _5: *mut u8;
-          let mut _6: *const u8;
+          let mut _5: *const u8;
+          let mut _6: *mut u8;
+          let mut _7: *const u8;
           scope 13 (inlined std::ptr::eq::<u8>) {
           }
       }
@@ -39,26 +40,29 @@
       bb0: {
           StorageLive(_1);
           StorageLive(_2);
+          StorageLive(_4);
           StorageLive(_3);
 -         _3 = const std::ptr::Alignment::of::<u8>::{constant#0} as std::num::NonZero<usize> (Transmute);
--         _2 = copy _3 as *mut u8 (Transmute);
-+         _3 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize));
-+         _2 = const {0x1 as *mut u8};
+-         _4 = copy _3 as (*const u8) is !null (Transmute);
++         _3 = const NonZero::<usize>(core::num::niche_types::NonZeroUsizeInner(1_usize is 1..));
++         _4 = const {0x1 as *const u8} is !null;
           StorageDead(_3);
-          StorageLive(_4);
+-         _2 = copy _4 as *mut u8 (Transmute);
++         _2 = const {0x1 as *const u8} is !null as *mut u8 (Transmute);
+          StorageDead(_4);
           StorageLive(_5);
--         _5 = copy _2;
--         _4 = copy _2 as *const u8 (PtrToPtr);
-+         _5 = const {0x1 as *mut u8};
-+         _4 = const {0x1 as *const u8};
-          StorageDead(_5);
           StorageLive(_6);
--         _6 = const Foo::<u8>::SENTINEL as *const u8 (PtrToPtr);
--         _1 = Eq(copy _4, copy _6);
-+         _6 = const {0x1 as *const u8};
-+         _1 = const true;
+          _6 = copy _2;
+-         _5 = copy _2 as *const u8 (PtrToPtr);
++         _5 = const {0x1 as *const u8} is !null as *const u8 (Transmute);
           StorageDead(_6);
-          StorageDead(_4);
+          StorageLive(_7);
+-         _7 = const Foo::<u8>::SENTINEL as *const u8 (PtrToPtr);
+-         _1 = Eq(copy _5, copy _7);
++         _7 = const {0x1 as *const u8};
++         _1 = Eq(copy _5, const {0x1 as *const u8});
+          StorageDead(_7);
+          StorageDead(_5);
           StorageDead(_2);
           StorageDead(_1);
           _0 = const ();
diff --git a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff
index f6c111a2228a9..e75984b98fbe4 100644
--- a/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff
+++ b/tests/mir-opt/inline/inline_shims.drop.Inline.panic-abort.diff
@@ -19,9 +19,10 @@
 +                 scope 4 (inlined alloc::raw_vec::RawVec::<A>::ptr) {
 +                     scope 5 (inlined alloc::raw_vec::RawVecInner::ptr::<A>) {
 +                         scope 6 (inlined alloc::raw_vec::RawVecInner::non_null::<A>) {
-+                             let mut _11: std::ptr::NonNull<u8>;
++                             let mut _12: std::ptr::NonNull<u8>;
 +                             scope 7 (inlined Unique::<u8>::cast::<A>) {
 +                                 scope 8 (inlined NonNull::<u8>::cast::<A>) {
++                                     let mut _11: (*const A) is !null;
 +                                     scope 9 (inlined NonNull::<u8>::as_ptr) {
 +                                     }
 +                                 }
@@ -39,15 +40,15 @@
 +                 }
 +             }
 +             scope 14 (inlined drop_in_place::<[A]> - shim(Some([A]))) {
-+                 let mut _12: usize;
-+                 let mut _13: *mut A;
-+                 let mut _14: bool;
++                 let mut _13: usize;
++                 let mut _14: *mut A;
++                 let mut _15: bool;
 +             }
 +         }
 +     }
 +     scope 15 (inlined drop_in_place::<Option<B>> - shim(Some(Option<B>))) {
-+         let mut _15: isize;
 +         let mut _16: isize;
++         let mut _17: isize;
 +     }
   
       bb0: {
@@ -62,16 +63,19 @@
 +         StorageLive(_8);
 +         StorageLive(_9);
 +         StorageLive(_11);
-+         _11 = copy (((((*_6).0: alloc::raw_vec::RawVec<A>).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique<u8>).0: std::ptr::NonNull<u8>);
++         StorageLive(_12);
++         _12 = copy (((((*_6).0: alloc::raw_vec::RawVec<A>).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique<u8>).0: std::ptr::NonNull<u8>);
++         _11 = copy _12 as (*const A) is !null (Transmute);
++         StorageDead(_12);
 +         _9 = copy _11 as *mut A (Transmute);
 +         StorageDead(_11);
 +         _10 = copy ((*_6).1: usize);
 +         _8 = *mut [A] from (copy _9, copy _10);
 +         StorageDead(_9);
-+         StorageLive(_12);
 +         StorageLive(_13);
 +         StorageLive(_14);
-+         _12 = const 0_usize;
++         StorageLive(_15);
++         _13 = const 0_usize;
 +         goto -> bb4;
       }
   
@@ -83,35 +87,35 @@
           StorageLive(_5);
           _5 = copy _2;
 -         _0 = drop_in_place::<Option<B>>(move _5) -> [return: bb2, unwind unreachable];
-+         StorageLive(_15);
 +         StorageLive(_16);
-+         _15 = discriminant((*_5));
-+         switchInt(move _15) -> [0: bb5, otherwise: bb6];
++         StorageLive(_17);
++         _16 = discriminant((*_5));
++         switchInt(move _16) -> [0: bb5, otherwise: bb6];
       }
   
       bb2: {
++         StorageDead(_15);
 +         StorageDead(_14);
 +         StorageDead(_13);
-+         StorageDead(_12);
 +         StorageDead(_8);
 +         StorageDead(_10);
 +         drop(((*_4).0: alloc::raw_vec::RawVec<A>)) -> [return: bb1, unwind unreachable];
 +     }
 + 
 +     bb3: {
-+         _13 = &raw mut (*_8)[_12];
-+         _12 = Add(move _12, const 1_usize);
-+         drop((*_13)) -> [return: bb4, unwind unreachable];
++         _14 = &raw mut (*_8)[_13];
++         _13 = Add(move _13, const 1_usize);
++         drop((*_14)) -> [return: bb4, unwind unreachable];
 +     }
 + 
 +     bb4: {
-+         _14 = Eq(copy _12, copy _10);
-+         switchInt(move _14) -> [0: bb3, otherwise: bb2];
++         _15 = Eq(copy _13, copy _10);
++         switchInt(move _15) -> [0: bb3, otherwise: bb2];
 +     }
 + 
 +     bb5: {
++         StorageDead(_17);
 +         StorageDead(_16);
-+         StorageDead(_15);
           StorageDead(_5);
           return;
 +     }
diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir
index 2eee8a97db0d4..7a656ba12956c 100644
--- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir
@@ -7,8 +7,8 @@ fn vec_deref_to_slice(_1: &Vec<u8>) -> &[u8] {
         debug self => _1;
         scope 2 (inlined Vec::<u8>::as_slice) {
             debug self => _1;
-            let mut _3: *const u8;
-            let mut _4: usize;
+            let mut _4: *const u8;
+            let mut _5: usize;
             scope 3 (inlined Vec::<u8>::as_ptr) {
                 debug self => _1;
                 scope 4 (inlined alloc::raw_vec::RawVec::<u8>::ptr) {
@@ -17,6 +17,7 @@ fn vec_deref_to_slice(_1: &Vec<u8>) -> &[u8] {
                             let mut _2: std::ptr::NonNull<u8>;
                             scope 7 (inlined Unique::<u8>::cast::<u8>) {
                                 scope 8 (inlined NonNull::<u8>::cast::<u8>) {
+                                    let mut _3: (*const u8) is !null;
                                     scope 9 (inlined NonNull::<u8>::as_ptr) {
                                     }
                                 }
@@ -30,9 +31,9 @@ fn vec_deref_to_slice(_1: &Vec<u8>) -> &[u8] {
                 }
             }
             scope 12 (inlined #[track_caller] std::slice::from_raw_parts::<'_, u8>) {
-                debug data => _3;
-                debug len => _4;
-                let _5: *const [u8];
+                debug data => _4;
+                debug len => _5;
+                let _6: *const [u8];
                 scope 13 (inlined core::ub_checks::check_language_ub) {
                     scope 14 (inlined core::ub_checks::check_language_ub::runtime) {
                     }
@@ -42,10 +43,10 @@ fn vec_deref_to_slice(_1: &Vec<u8>) -> &[u8] {
                 scope 16 (inlined std::mem::align_of::<u8>) {
                 }
                 scope 17 (inlined slice_from_raw_parts::<u8>) {
-                    debug data => _3;
-                    debug len => _4;
+                    debug data => _4;
+                    debug len => _5;
                     scope 18 (inlined std::ptr::from_raw_parts::<[u8], u8>) {
-                        debug data_pointer => _3;
+                        debug data_pointer => _4;
                     }
                 }
             }
@@ -53,19 +54,22 @@ fn vec_deref_to_slice(_1: &Vec<u8>) -> &[u8] {
     }
 
     bb0: {
-        StorageLive(_2);
         StorageLive(_3);
-        _2 = copy (((((*_1).0: alloc::raw_vec::RawVec<u8>).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique<u8>).0: std::ptr::NonNull<u8>);
-        _3 = copy _2 as *const u8 (Transmute);
         StorageLive(_4);
-        _4 = copy ((*_1).1: usize);
+        StorageLive(_2);
+        _2 = copy (((((*_1).0: alloc::raw_vec::RawVec<u8>).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique<u8>).0: std::ptr::NonNull<u8>);
+        _3 = copy _2 as (*const u8) is !null (Transmute);
+        StorageDead(_2);
+        _4 = copy _3 as *const u8 (Transmute);
         StorageLive(_5);
-        _5 = *const [u8] from (copy _3, copy _4);
-        _0 = &(*_5);
+        _5 = copy ((*_1).1: usize);
+        StorageLive(_6);
+        _6 = *const [u8] from (copy _4, copy _5);
+        _0 = &(*_6);
+        StorageDead(_6);
         StorageDead(_5);
         StorageDead(_4);
         StorageDead(_3);
-        StorageDead(_2);
         return;
     }
 }
diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir
index 2eee8a97db0d4..7a656ba12956c 100644
--- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir
@@ -7,8 +7,8 @@ fn vec_deref_to_slice(_1: &Vec<u8>) -> &[u8] {
         debug self => _1;
         scope 2 (inlined Vec::<u8>::as_slice) {
             debug self => _1;
-            let mut _3: *const u8;
-            let mut _4: usize;
+            let mut _4: *const u8;
+            let mut _5: usize;
             scope 3 (inlined Vec::<u8>::as_ptr) {
                 debug self => _1;
                 scope 4 (inlined alloc::raw_vec::RawVec::<u8>::ptr) {
@@ -17,6 +17,7 @@ fn vec_deref_to_slice(_1: &Vec<u8>) -> &[u8] {
                             let mut _2: std::ptr::NonNull<u8>;
                             scope 7 (inlined Unique::<u8>::cast::<u8>) {
                                 scope 8 (inlined NonNull::<u8>::cast::<u8>) {
+                                    let mut _3: (*const u8) is !null;
                                     scope 9 (inlined NonNull::<u8>::as_ptr) {
                                     }
                                 }
@@ -30,9 +31,9 @@ fn vec_deref_to_slice(_1: &Vec<u8>) -> &[u8] {
                 }
             }
             scope 12 (inlined #[track_caller] std::slice::from_raw_parts::<'_, u8>) {
-                debug data => _3;
-                debug len => _4;
-                let _5: *const [u8];
+                debug data => _4;
+                debug len => _5;
+                let _6: *const [u8];
                 scope 13 (inlined core::ub_checks::check_language_ub) {
                     scope 14 (inlined core::ub_checks::check_language_ub::runtime) {
                     }
@@ -42,10 +43,10 @@ fn vec_deref_to_slice(_1: &Vec<u8>) -> &[u8] {
                 scope 16 (inlined std::mem::align_of::<u8>) {
                 }
                 scope 17 (inlined slice_from_raw_parts::<u8>) {
-                    debug data => _3;
-                    debug len => _4;
+                    debug data => _4;
+                    debug len => _5;
                     scope 18 (inlined std::ptr::from_raw_parts::<[u8], u8>) {
-                        debug data_pointer => _3;
+                        debug data_pointer => _4;
                     }
                 }
             }
@@ -53,19 +54,22 @@ fn vec_deref_to_slice(_1: &Vec<u8>) -> &[u8] {
     }
 
     bb0: {
-        StorageLive(_2);
         StorageLive(_3);
-        _2 = copy (((((*_1).0: alloc::raw_vec::RawVec<u8>).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique<u8>).0: std::ptr::NonNull<u8>);
-        _3 = copy _2 as *const u8 (Transmute);
         StorageLive(_4);
-        _4 = copy ((*_1).1: usize);
+        StorageLive(_2);
+        _2 = copy (((((*_1).0: alloc::raw_vec::RawVec<u8>).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique<u8>).0: std::ptr::NonNull<u8>);
+        _3 = copy _2 as (*const u8) is !null (Transmute);
+        StorageDead(_2);
+        _4 = copy _3 as *const u8 (Transmute);
         StorageLive(_5);
-        _5 = *const [u8] from (copy _3, copy _4);
-        _0 = &(*_5);
+        _5 = copy ((*_1).1: usize);
+        StorageLive(_6);
+        _6 = *const [u8] from (copy _4, copy _5);
+        _0 = &(*_6);
+        StorageDead(_6);
         StorageDead(_5);
         StorageDead(_4);
         StorageDead(_3);
-        StorageDead(_2);
         return;
     }
 }
diff --git a/tests/ui/consts/const-eval/raw-bytes.32bit.stderr b/tests/ui/consts/const-eval/raw-bytes.32bit.stderr
index 36183e2892101..c09fe36a3df9c 100644
--- a/tests/ui/consts/const-eval/raw-bytes.32bit.stderr
+++ b/tests/ui/consts/const-eval/raw-bytes.32bit.stderr
@@ -53,7 +53,7 @@ LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::tran
                78 00 00 00 ff ff ff ff                         │ x.......
            }
 
-error[E0080]: constructing invalid value: encountered 0, but expected something greater or equal to 1
+error[E0080]: constructing invalid value at .pointer: encountered 0, but expected something greater or equal to 1
   --> $DIR/raw-bytes.rs:58:1
    |
 LL | const NULL_PTR: NonNull<u8> = unsafe { mem::transmute(0usize) };
@@ -64,7 +64,7 @@ LL | const NULL_PTR: NonNull<u8> = unsafe { mem::transmute(0usize) };
                00 00 00 00                                     │ ....
            }
 
-error[E0080]: constructing invalid value at .0: encountered 0, but expected something greater or equal to 1
+error[E0080]: constructing invalid value at .0.0: encountered 0, but expected something greater or equal to 1
   --> $DIR/raw-bytes.rs:61:1
    |
 LL | const NULL_U8: NonZero<u8> = unsafe { mem::transmute(0u8) };
@@ -75,7 +75,7 @@ LL | const NULL_U8: NonZero<u8> = unsafe { mem::transmute(0u8) };
                00                                              │ .
            }
 
-error[E0080]: constructing invalid value at .0: encountered 0, but expected something greater or equal to 1
+error[E0080]: constructing invalid value at .0.0: encountered 0, but expected something greater or equal to 1
   --> $DIR/raw-bytes.rs:63:1
    |
 LL | const NULL_USIZE: NonZero<usize> = unsafe { mem::transmute(0usize) };
@@ -108,7 +108,7 @@ LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) };
                14 00 00 00                                     │ ....
            }
 
-error[E0080]: constructing invalid value: encountered 0, but expected something greater or equal to 1
+error[E0080]: constructing invalid value at .pointer: encountered 0, but expected something greater or equal to 1
   --> $DIR/raw-bytes.rs:78:1
    |
 LL | const NULL_FAT_PTR: NonNull<dyn Send> = unsafe {
diff --git a/tests/ui/consts/const-eval/raw-bytes.64bit.stderr b/tests/ui/consts/const-eval/raw-bytes.64bit.stderr
index c53326534fdc5..55a55ddb57092 100644
--- a/tests/ui/consts/const-eval/raw-bytes.64bit.stderr
+++ b/tests/ui/consts/const-eval/raw-bytes.64bit.stderr
@@ -53,7 +53,7 @@ LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::tran
                78 00 00 00 ff ff ff ff                         │ x.......
            }
 
-error[E0080]: constructing invalid value: encountered 0, but expected something greater or equal to 1
+error[E0080]: constructing invalid value at .pointer: encountered 0, but expected something greater or equal to 1
   --> $DIR/raw-bytes.rs:58:1
    |
 LL | const NULL_PTR: NonNull<u8> = unsafe { mem::transmute(0usize) };
@@ -64,7 +64,7 @@ LL | const NULL_PTR: NonNull<u8> = unsafe { mem::transmute(0usize) };
                00 00 00 00 00 00 00 00                         │ ........
            }
 
-error[E0080]: constructing invalid value at .0: encountered 0, but expected something greater or equal to 1
+error[E0080]: constructing invalid value at .0.0: encountered 0, but expected something greater or equal to 1
   --> $DIR/raw-bytes.rs:61:1
    |
 LL | const NULL_U8: NonZero<u8> = unsafe { mem::transmute(0u8) };
@@ -75,7 +75,7 @@ LL | const NULL_U8: NonZero<u8> = unsafe { mem::transmute(0u8) };
                00                                              │ .
            }
 
-error[E0080]: constructing invalid value at .0: encountered 0, but expected something greater or equal to 1
+error[E0080]: constructing invalid value at .0.0: encountered 0, but expected something greater or equal to 1
   --> $DIR/raw-bytes.rs:63:1
    |
 LL | const NULL_USIZE: NonZero<usize> = unsafe { mem::transmute(0usize) };
@@ -108,7 +108,7 @@ LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) };
                14 00 00 00                                     │ ....
            }
 
-error[E0080]: constructing invalid value: encountered 0, but expected something greater or equal to 1
+error[E0080]: constructing invalid value at .pointer: encountered 0, but expected something greater or equal to 1
   --> $DIR/raw-bytes.rs:78:1
    |
 LL | const NULL_FAT_PTR: NonNull<dyn Send> = unsafe {
diff --git a/tests/ui/consts/const-eval/ub-nonnull.stderr b/tests/ui/consts/const-eval/ub-nonnull.stderr
index 314141e48370d..20ef71ed1c4c5 100644
--- a/tests/ui/consts/const-eval/ub-nonnull.stderr
+++ b/tests/ui/consts/const-eval/ub-nonnull.stderr
@@ -1,4 +1,4 @@
-error[E0080]: constructing invalid value: encountered 0, but expected something greater or equal to 1
+error[E0080]: constructing invalid value at .pointer: encountered 0, but expected something greater or equal to 1
   --> $DIR/ub-nonnull.rs:16:1
    |
 LL | const NULL_PTR: NonNull<u8> = unsafe { mem::transmute(0usize) };
@@ -15,7 +15,7 @@ error[E0080]: in-bounds pointer arithmetic failed: attempting to offset pointer
 LL |     let out_of_bounds_ptr = &ptr[255];
    |                             ^^^^^^^^^ evaluation of `OUT_OF_BOUNDS_PTR` failed here
 
-error[E0080]: constructing invalid value at .0: encountered 0, but expected something greater or equal to 1
+error[E0080]: constructing invalid value at .0.0: encountered 0, but expected something greater or equal to 1
   --> $DIR/ub-nonnull.rs:26:1
    |
 LL | const NULL_U8: NonZero<u8> = unsafe { mem::transmute(0u8) };
@@ -26,7 +26,7 @@ LL | const NULL_U8: NonZero<u8> = unsafe { mem::transmute(0u8) };
                HEX_DUMP
            }
 
-error[E0080]: constructing invalid value at .0: encountered 0, but expected something greater or equal to 1
+error[E0080]: constructing invalid value at .0.0: encountered 0, but expected something greater or equal to 1
   --> $DIR/ub-nonnull.rs:28:1
    |
 LL | const NULL_USIZE: NonZero<usize> = unsafe { mem::transmute(0usize) };
@@ -65,7 +65,7 @@ LL | const BAD_RANGE2: RestrictedRange2 = unsafe { RestrictedRange2(20) };
                HEX_DUMP
            }
 
-error[E0080]: constructing invalid value: encountered 0, but expected something greater or equal to 1
+error[E0080]: constructing invalid value at .pointer: encountered 0, but expected something greater or equal to 1
   --> $DIR/ub-nonnull.rs:53:1
    |
 LL | const NULL_FAT_PTR: NonNull<dyn Send> = unsafe {
diff --git a/tests/ui/lint/invalid_value.stderr b/tests/ui/lint/invalid_value.stderr
index cc6a2a1c8e532..63df1e5d11d68 100644
--- a/tests/ui/lint/invalid_value.stderr
+++ b/tests/ui/lint/invalid_value.stderr
@@ -314,7 +314,6 @@ LL |         let _val: NonNull<i32> = mem::uninitialized();
    |                                  help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
    = note: `std::ptr::NonNull<i32>` must be non-null
-   = note: raw pointers must be initialized
 
 error: the type `(NonZero<u32>, i32)` does not permit zero-initialization
   --> $DIR/invalid_value.rs:94:41
@@ -333,7 +332,6 @@ LL |         let _val: (NonZero<u32>, i32) = mem::uninitialized();
    |
    = note: `std::num::NonZero<u32>` must be non-null
    = note: because `core::num::niche_types::NonZeroU32Inner` must be non-null
-   = note: integers must be initialized
 
 error: the type `*const dyn Send` does not permit zero-initialization
   --> $DIR/invalid_value.rs:97:37
@@ -430,7 +428,6 @@ note: because `std::num::NonZero<u32>` must be non-null (in this field of the on
 LL |     Banana(NonZero<u32>),
    |            ^^^^^^^^^^^^
    = note: because `core::num::niche_types::NonZeroU32Inner` must be non-null
-   = note: integers must be initialized
 
 error: the type `bool` does not permit being left uninitialized
   --> $DIR/invalid_value.rs:111:26
@@ -625,7 +622,6 @@ LL |         let _val: NonNull<i32> = MaybeUninit::uninit().assume_init();
    |                                  help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
    = note: `std::ptr::NonNull<i32>` must be non-null
-   = note: raw pointers must be initialized
 
 error: the type `bool` does not permit being left uninitialized
   --> $DIR/invalid_value.rs:158:26
diff --git a/tests/ui/type/pattern_types/bad_pat.rs b/tests/ui/type/pattern_types/bad_pat.rs
index 549b0d11dd187..7e2b4cb996f27 100644
--- a/tests/ui/type/pattern_types/bad_pat.rs
+++ b/tests/ui/type/pattern_types/bad_pat.rs
@@ -10,4 +10,15 @@ type Positive2 = pattern_type!(i32 is 0..=);
 type Wild = pattern_type!(() is _);
 //~^ ERROR: pattern not supported in pattern types
 
+// FIXME: confusing diagnostic because `not` can be a binding
+type NonNull = pattern_type!(*const () is not null);
+//~^ ERROR: expected one of `@` or `|`, found `null`
+//~| ERROR: pattern not supported in pattern types
+
+type NonNull2 = pattern_type!(*const () is !nil);
+//~^ ERROR: expected `null`, found `nil`
+
+// FIXME: reject with a type mismatch
+type Mismatch2 = pattern_type!(() is !null);
+
 fn main() {}
diff --git a/tests/ui/type/pattern_types/bad_pat.stderr b/tests/ui/type/pattern_types/bad_pat.stderr
index d2a5a20bf89b6..e72279542280c 100644
--- a/tests/ui/type/pattern_types/bad_pat.stderr
+++ b/tests/ui/type/pattern_types/bad_pat.stderr
@@ -30,6 +30,24 @@ error: pattern not supported in pattern types
 LL | type Wild = pattern_type!(() is _);
    |                                 ^
 
-error: aborting due to 3 previous errors
+error: pattern not supported in pattern types
+  --> $DIR/bad_pat.rs:14:43
+   |
+LL | type NonNull = pattern_type!(*const () is not null);
+   |                                           ^^^
+
+error: expected one of `@` or `|`, found `null`
+  --> $DIR/bad_pat.rs:14:47
+   |
+LL | type NonNull = pattern_type!(*const () is not null);
+   |                                               ^^^^ expected one of `@` or `|`
+
+error: expected `null`, found `nil`
+  --> $DIR/bad_pat.rs:18:45
+   |
+LL | type NonNull2 = pattern_type!(*const () is !nil);
+   |                                             ^^^ expected `null`
+
+error: aborting due to 6 previous errors
 
 For more information about this error, try `rustc --explain E0586`.
diff --git a/tests/ui/type/pattern_types/non_null.rs b/tests/ui/type/pattern_types/non_null.rs
new file mode 100644
index 0000000000000..7e86b8b684d17
--- /dev/null
+++ b/tests/ui/type/pattern_types/non_null.rs
@@ -0,0 +1,21 @@
+//! Show that pattern-types non-null is the same as libstd's
+
+//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED"
+//@ only-64bit
+
+#![feature(pattern_type_macro, pattern_types, rustc_attrs)]
+
+use std::pat::pattern_type;
+
+#[rustc_layout(debug)]
+type NonNull<T> = pattern_type!(*const T is !null); //~ ERROR layout_of
+
+#[rustc_layout(debug)]
+type Test = Option<NonNull<()>>; //~ ERROR layout_of
+
+#[rustc_layout(debug)]
+type Wide = pattern_type!(*const [u8] is !null); //~ ERROR layout_of
+
+const _: () = assert!(size_of::<NonNull<()>>() == size_of::<Option<NonNull<()>>>());
+
+fn main() {}
diff --git a/tests/ui/type/pattern_types/non_null.stderr b/tests/ui/type/pattern_types/non_null.stderr
new file mode 100644
index 0000000000000..ad61e9a591473
--- /dev/null
+++ b/tests/ui/type/pattern_types/non_null.stderr
@@ -0,0 +1,218 @@
+error: layout_of((*const T) is !null) = Layout {
+           size: Size(8 bytes),
+           align: AbiAlign {
+               abi: Align(8 bytes),
+           },
+           backend_repr: Scalar(
+               Initialized {
+                   value: Pointer(
+                       AddressSpace(
+                           0,
+                       ),
+                   ),
+                   valid_range: 1..=18446744073709551615,
+               },
+           ),
+           fields: Arbitrary {
+               offsets: [
+                   Size(0 bytes),
+               ],
+               memory_index: [
+                   0,
+               ],
+           },
+           largest_niche: Some(
+               Niche {
+                   offset: Size(0 bytes),
+                   value: Pointer(
+                       AddressSpace(
+                           0,
+                       ),
+                   ),
+                   valid_range: 1..=18446744073709551615,
+               },
+           ),
+           uninhabited: false,
+           variants: Single {
+               index: 0,
+           },
+           max_repr_align: None,
+           unadjusted_abi_align: Align(8 bytes),
+           randomization_seed: $SEED,
+       }
+  --> $DIR/non_null.rs:11:1
+   |
+LL | type NonNull<T> = pattern_type!(*const T is !null);
+   | ^^^^^^^^^^^^^^^
+
+error: layout_of(Option<(*const ()) is !null>) = Layout {
+           size: Size(8 bytes),
+           align: AbiAlign {
+               abi: Align(8 bytes),
+           },
+           backend_repr: Scalar(
+               Initialized {
+                   value: Pointer(
+                       AddressSpace(
+                           0,
+                       ),
+                   ),
+                   valid_range: (..=0) | (1..),
+               },
+           ),
+           fields: Arbitrary {
+               offsets: [
+                   Size(0 bytes),
+               ],
+               memory_index: [
+                   0,
+               ],
+           },
+           largest_niche: None,
+           uninhabited: false,
+           variants: Multiple {
+               tag: Initialized {
+                   value: Pointer(
+                       AddressSpace(
+                           0,
+                       ),
+                   ),
+                   valid_range: (..=0) | (1..),
+               },
+               tag_encoding: Niche {
+                   untagged_variant: 1,
+                   niche_variants: 0..=0,
+                   niche_start: 0,
+               },
+               tag_field: 0,
+               variants: [
+                   Layout {
+                       size: Size(0 bytes),
+                       align: AbiAlign {
+                           abi: Align(1 bytes),
+                       },
+                       backend_repr: Memory {
+                           sized: true,
+                       },
+                       fields: Arbitrary {
+                           offsets: [],
+                           memory_index: [],
+                       },
+                       largest_niche: None,
+                       uninhabited: false,
+                       variants: Single {
+                           index: 0,
+                       },
+                       max_repr_align: None,
+                       unadjusted_abi_align: Align(1 bytes),
+                       randomization_seed: $SEED,
+                   },
+                   Layout {
+                       size: Size(8 bytes),
+                       align: AbiAlign {
+                           abi: Align(8 bytes),
+                       },
+                       backend_repr: Scalar(
+                           Initialized {
+                               value: Pointer(
+                                   AddressSpace(
+                                       0,
+                                   ),
+                               ),
+                               valid_range: 1..=18446744073709551615,
+                           },
+                       ),
+                       fields: Arbitrary {
+                           offsets: [
+                               Size(0 bytes),
+                           ],
+                           memory_index: [
+                               0,
+                           ],
+                       },
+                       largest_niche: Some(
+                           Niche {
+                               offset: Size(0 bytes),
+                               value: Pointer(
+                                   AddressSpace(
+                                       0,
+                                   ),
+                               ),
+                               valid_range: 1..=18446744073709551615,
+                           },
+                       ),
+                       uninhabited: false,
+                       variants: Single {
+                           index: 1,
+                       },
+                       max_repr_align: None,
+                       unadjusted_abi_align: Align(8 bytes),
+                       randomization_seed: $SEED,
+                   },
+               ],
+           },
+           max_repr_align: None,
+           unadjusted_abi_align: Align(8 bytes),
+           randomization_seed: $SEED,
+       }
+  --> $DIR/non_null.rs:14:1
+   |
+LL | type Test = Option<NonNull<()>>;
+   | ^^^^^^^^^
+
+error: layout_of((*const [u8]) is !null) = Layout {
+           size: Size(16 bytes),
+           align: AbiAlign {
+               abi: Align(8 bytes),
+           },
+           backend_repr: ScalarPair(
+               Initialized {
+                   value: Pointer(
+                       AddressSpace(
+                           0,
+                       ),
+                   ),
+                   valid_range: 1..=18446744073709551615,
+               },
+               Initialized {
+                   value: Int(
+                       I64,
+                       false,
+                   ),
+                   valid_range: 0..=18446744073709551615,
+               },
+           ),
+           fields: Arbitrary {
+               offsets: [
+                   Size(0 bytes),
+               ],
+               memory_index: [
+                   0,
+               ],
+           },
+           largest_niche: Some(
+               Niche {
+                   offset: Size(0 bytes),
+                   value: Pointer(
+                       AddressSpace(
+                           0,
+                       ),
+                   ),
+                   valid_range: 1..=18446744073709551615,
+               },
+           ),
+           uninhabited: false,
+           variants: Single {
+               index: 0,
+           },
+           max_repr_align: None,
+           unadjusted_abi_align: Align(8 bytes),
+           randomization_seed: $SEED,
+       }
+  --> $DIR/non_null.rs:17:1
+   |
+LL | type Wide = pattern_type!(*const [u8] is !null);
+   | ^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/type/pattern_types/unsize.rs b/tests/ui/type/pattern_types/unsize.rs
new file mode 100644
index 0000000000000..1020dd8c21544
--- /dev/null
+++ b/tests/ui/type/pattern_types/unsize.rs
@@ -0,0 +1,18 @@
+//! Show that pattern-types with pointer base types can be part of unsizing coercions
+
+//@ check-pass
+
+#![feature(pattern_type_macro, pattern_types)]
+
+use std::pat::pattern_type;
+
+type NonNull<T> = pattern_type!(*const T is !null);
+
+trait Trait {}
+impl Trait for u32 {}
+impl Trait for i32 {}
+
+fn main() {
+    let x: NonNull<u32> = unsafe { std::mem::transmute(std::ptr::dangling::<u32>()) };
+    let x: NonNull<dyn Trait> = x;
+}