From 9a1a75321ed1c62bffef83e7965d7450638608be Mon Sep 17 00:00:00 2001
From: Lukas Markeffsky <@>
Date: Fri, 7 Oct 2022 20:57:34 +0200
Subject: [PATCH 1/7] constify `exact_div` intrinsic

---
 compiler/rustc_const_eval/src/interpret/intrinsics.rs | 5 +++++
 library/core/src/intrinsics.rs                        | 1 +
 library/core/src/lib.rs                               | 1 +
 src/tools/miri/src/shims/intrinsics/mod.rs            | 5 -----
 4 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 8637d6a7767e4..7534a27f0770c 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -234,6 +234,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let discr_val = self.read_discriminant(&place.into())?.0;
                 self.write_scalar(discr_val, dest)?;
             }
+            sym::exact_div => {
+                let l = self.read_immediate(&args[0])?;
+                let r = self.read_immediate(&args[1])?;
+                self.exact_div(&l, &r, dest)?;
+            }
             sym::unchecked_shl
             | sym::unchecked_shr
             | sym::unchecked_add
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index 2399262c05b46..be9acae8c986b 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -1846,6 +1846,7 @@ extern "rust-intrinsic" {
     /// `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`
     ///
     /// This intrinsic does not have a stable counterpart.
+    #[rustc_const_unstable(feature = "const_exact_div", issue = "none")]
     pub fn exact_div<T: Copy>(x: T, y: T) -> T;
 
     /// Performs an unchecked division, resulting in undefined behavior
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 2fd8180f8b2a2..6294ef0df3dec 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -109,6 +109,7 @@
 #![feature(const_cmp)]
 #![feature(const_discriminant)]
 #![feature(const_eval_select)]
+#![feature(const_exact_div)]
 #![feature(const_float_bits_conv)]
 #![feature(const_float_classify)]
 #![feature(const_fmt_arguments_new)]
diff --git a/src/tools/miri/src/shims/intrinsics/mod.rs b/src/tools/miri/src/shims/intrinsics/mod.rs
index e0985ace5be7d..31e9f7a7796db 100644
--- a/src/tools/miri/src/shims/intrinsics/mod.rs
+++ b/src/tools/miri/src/shims/intrinsics/mod.rs
@@ -357,11 +357,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             }
 
             // Other
-            "exact_div" => {
-                let [num, denom] = check_arg_count(args)?;
-                this.exact_div(&this.read_immediate(num)?, &this.read_immediate(denom)?, dest)?;
-            }
-
             "breakpoint" => {
                 let [] = check_arg_count(args)?;
                 // normally this would raise a SIGTRAP, which aborts if no debugger is connected

From f24fc19b51acadaf82b7c81849788c8ff5f6ed62 Mon Sep 17 00:00:00 2001
From: Lukas Markeffsky <@>
Date: Thu, 20 Oct 2022 20:15:37 +0200
Subject: [PATCH 2/7] unify inherent impls of `CompileTimeEvalContext`

---
 .../src/const_eval/machine.rs                 | 90 +++++++++----------
 1 file changed, 44 insertions(+), 46 deletions(-)

diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index e5acacd918888..de601c574562e 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -23,52 +23,6 @@ use crate::interpret::{
 
 use super::error::*;
 
-impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> {
-    /// "Intercept" a function call to a panic-related function
-    /// because we have something special to do for it.
-    /// If this returns successfully (`Ok`), the function should just be evaluated normally.
-    fn hook_special_const_fn(
-        &mut self,
-        instance: ty::Instance<'tcx>,
-        args: &[OpTy<'tcx>],
-    ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
-        // All `#[rustc_do_not_const_check]` functions should be hooked here.
-        let def_id = instance.def_id();
-
-        if Some(def_id) == self.tcx.lang_items().panic_display()
-            || Some(def_id) == self.tcx.lang_items().begin_panic_fn()
-        {
-            // &str or &&str
-            assert!(args.len() == 1);
-
-            let mut msg_place = self.deref_operand(&args[0])?;
-            while msg_place.layout.ty.is_ref() {
-                msg_place = self.deref_operand(&msg_place.into())?;
-            }
-
-            let msg = Symbol::intern(self.read_str(&msg_place)?);
-            let span = self.find_closest_untracked_caller_location();
-            let (file, line, col) = self.location_triple_for_span(span);
-            return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into());
-        } else if Some(def_id) == self.tcx.lang_items().panic_fmt() {
-            // For panic_fmt, call const_panic_fmt instead.
-            if let Some(const_panic_fmt) = self.tcx.lang_items().const_panic_fmt() {
-                return Ok(Some(
-                    ty::Instance::resolve(
-                        *self.tcx,
-                        ty::ParamEnv::reveal_all(),
-                        const_panic_fmt,
-                        self.tcx.intern_substs(&[]),
-                    )
-                    .unwrap()
-                    .unwrap(),
-                ));
-            }
-        }
-        Ok(None)
-    }
-}
-
 /// Extra machine state for CTFE, and the Machine instance
 pub struct CompileTimeInterpreter<'mir, 'tcx> {
     /// For now, the number of terminators that can be evaluated before we throw a resource
@@ -191,6 +145,50 @@ impl interpret::MayLeak for ! {
 }
 
 impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
+    /// "Intercept" a function call to a panic-related function
+    /// because we have something special to do for it.
+    /// If this returns successfully (`Ok`), the function should just be evaluated normally.
+    fn hook_special_const_fn(
+        &mut self,
+        instance: ty::Instance<'tcx>,
+        args: &[OpTy<'tcx>],
+    ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
+        // All `#[rustc_do_not_const_check]` functions should be hooked here.
+        let def_id = instance.def_id();
+
+        if Some(def_id) == self.tcx.lang_items().panic_display()
+            || Some(def_id) == self.tcx.lang_items().begin_panic_fn()
+        {
+            // &str or &&str
+            assert!(args.len() == 1);
+
+            let mut msg_place = self.deref_operand(&args[0])?;
+            while msg_place.layout.ty.is_ref() {
+                msg_place = self.deref_operand(&msg_place.into())?;
+            }
+
+            let msg = Symbol::intern(self.read_str(&msg_place)?);
+            let span = self.find_closest_untracked_caller_location();
+            let (file, line, col) = self.location_triple_for_span(span);
+            return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into());
+        } else if Some(def_id) == self.tcx.lang_items().panic_fmt() {
+            // For panic_fmt, call const_panic_fmt instead.
+            if let Some(const_panic_fmt) = self.tcx.lang_items().const_panic_fmt() {
+                return Ok(Some(
+                    ty::Instance::resolve(
+                        *self.tcx,
+                        ty::ParamEnv::reveal_all(),
+                        const_panic_fmt,
+                        self.tcx.intern_substs(&[]),
+                    )
+                    .unwrap()
+                    .unwrap(),
+                ));
+            }
+        }
+        Ok(None)
+    }
+
     /// See documentation on the `ptr_guaranteed_cmp` intrinsic.
     fn guaranteed_cmp(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, u8> {
         Ok(match (a, b) {

From d4e692d465e87081c3e0ba3e01c76117c54f8e33 Mon Sep 17 00:00:00 2001
From: Lukas Markeffsky <@>
Date: Fri, 7 Oct 2022 22:23:34 +0200
Subject: [PATCH 3/7] make const `align_offset` useful

---
 .../src/const_eval/machine.rs                 | 129 ++++++++++++++----
 library/core/src/ptr/const_ptr.rs             |  15 ++
 library/core/src/ptr/mod.rs                   |  21 ++-
 library/core/src/ptr/mut_ptr.rs               |  15 ++
 4 files changed, 152 insertions(+), 28 deletions(-)

diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index de601c574562e..dffd4c74e86bd 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -1,9 +1,12 @@
 use rustc_hir::def::DefKind;
 use rustc_middle::mir;
+use rustc_middle::mir::interpret::PointerArithmetic;
+use rustc_middle::ty::layout::FnAbiOf;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use std::borrow::Borrow;
 use std::collections::hash_map::Entry;
 use std::hash::Hash;
+use std::ops::ControlFlow;
 
 use rustc_data_structures::fx::FxHashMap;
 use std::fmt;
@@ -17,8 +20,8 @@ use rustc_target::abi::{Align, Size};
 use rustc_target::spec::abi::Abi as CallAbi;
 
 use crate::interpret::{
-    self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult,
-    OpTy, PlaceTy, Pointer, Scalar, StackPopUnwind,
+    self, compile_time_machine, AllocId, ConstAllocation, FnVal, Frame, ImmTy, InterpCx,
+    InterpResult, OpTy, PlaceTy, Pointer, Scalar, StackPopUnwind,
 };
 
 use super::error::*;
@@ -145,15 +148,19 @@ impl interpret::MayLeak for ! {
 }
 
 impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
-    /// "Intercept" a function call to a panic-related function
-    /// because we have something special to do for it.
-    /// If this returns successfully (`Ok`), the function should just be evaluated normally.
+    /// "Intercept" a function call, because we have something special to do for it.
+    /// All `#[rustc_do_not_const_check]` functions should be hooked here.
+    /// If this returns `Some` function, which may be `instance` or a different function with
+    /// compatible arguments, then evaluation should continue with that function.
+    /// If this returns `None`, the function call has been handled and the function has returned.
     fn hook_special_const_fn(
         &mut self,
         instance: ty::Instance<'tcx>,
+        _abi: CallAbi,
         args: &[OpTy<'tcx>],
+        dest: &PlaceTy<'tcx>,
+        ret: Option<mir::BasicBlock>,
     ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
-        // All `#[rustc_do_not_const_check]` functions should be hooked here.
         let def_id = instance.def_id();
 
         if Some(def_id) == self.tcx.lang_items().panic_display()
@@ -173,20 +180,91 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
             return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into());
         } else if Some(def_id) == self.tcx.lang_items().panic_fmt() {
             // For panic_fmt, call const_panic_fmt instead.
-            if let Some(const_panic_fmt) = self.tcx.lang_items().const_panic_fmt() {
-                return Ok(Some(
-                    ty::Instance::resolve(
-                        *self.tcx,
-                        ty::ParamEnv::reveal_all(),
-                        const_panic_fmt,
-                        self.tcx.intern_substs(&[]),
-                    )
-                    .unwrap()
-                    .unwrap(),
-                ));
+            let Some(const_def_id) = self.tcx.lang_items().const_panic_fmt() else {
+                bug!("`const_panic_fmt` must be defined to call `panic_fmt` in const eval")
+            };
+            let new_instance = ty::Instance::resolve(
+                *self.tcx,
+                ty::ParamEnv::reveal_all(),
+                const_def_id,
+                instance.substs,
+            )
+            .unwrap()
+            .unwrap();
+
+            return Ok(Some(new_instance));
+        } else if Some(def_id) == self.tcx.lang_items().align_offset_fn() {
+            // For align_offset, we replace the function call if the pointer has no address.
+            match self.align_offset(instance, args, dest, ret)? {
+                ControlFlow::Continue(()) => return Ok(Some(instance)),
+                ControlFlow::Break(()) => return Ok(None),
+            }
+        }
+        Ok(Some(instance))
+    }
+
+    /// `align_offset(ptr, target_align)` needs special handling in const eval, because the pointer
+    /// may not have an address.
+    ///
+    /// If `ptr` does have a known address, then we return `CONTINUE` and the function call should
+    /// proceed as normal.
+    ///
+    /// If `ptr` doesn't have an address, but its underlying allocation's alignment is at most
+    /// `target_align`, then we call the function again with an dummy address relative to the
+    /// allocation.
+    ///
+    /// If `ptr` doesn't have an address and `target_align` is stricter than the underlying
+    /// allocation's alignment, then we return `usize::MAX` immediately.
+    fn align_offset(
+        &mut self,
+        instance: ty::Instance<'tcx>,
+        args: &[OpTy<'tcx>],
+        dest: &PlaceTy<'tcx>,
+        ret: Option<mir::BasicBlock>,
+    ) -> InterpResult<'tcx, ControlFlow<()>> {
+        assert_eq!(args.len(), 2);
+
+        let ptr = self.read_pointer(&args[0])?;
+        let target_align = self.read_scalar(&args[1])?.to_machine_usize(self)?;
+
+        if !target_align.is_power_of_two() {
+            throw_ub_format!("`align_offset` called with non-power-of-two align: {}", target_align);
+        }
+
+        match self.ptr_try_get_alloc_id(ptr) {
+            Ok((alloc_id, offset, _extra)) => {
+                let (_size, alloc_align, _kind) = self.get_alloc_info(alloc_id);
+
+                if target_align <= alloc_align.bytes() {
+                    // Extract the address relative to the allocation base that is definitely
+                    // sufficiently aligned and call `align_offset` again.
+                    let addr = ImmTy::from_uint(offset.bytes(), args[0].layout).into();
+                    let align = ImmTy::from_uint(target_align, args[1].layout).into();
+
+                    let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?;
+                    self.eval_fn_call(
+                        FnVal::Instance(instance),
+                        (CallAbi::Rust, fn_abi),
+                        &[addr, align],
+                        false,
+                        dest,
+                        ret,
+                        StackPopUnwind::NotAllowed,
+                    )?;
+                    Ok(ControlFlow::BREAK)
+                } else {
+                    // Not alignable in const, return `usize::MAX`.
+                    let usize_max = Scalar::from_machine_usize(self.machine_usize_max(), self);
+                    self.write_scalar(usize_max, dest)?;
+                    self.return_to_block(ret)?;
+                    Ok(ControlFlow::BREAK)
+                }
+            }
+            Err(_addr) => {
+                // The pointer has an address, continue with function call.
+                Ok(ControlFlow::CONTINUE)
             }
         }
-        Ok(None)
     }
 
     /// See documentation on the `ptr_guaranteed_cmp` intrinsic.
@@ -269,8 +347,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
         instance: ty::Instance<'tcx>,
         _abi: CallAbi,
         args: &[OpTy<'tcx>],
-        _dest: &PlaceTy<'tcx>,
-        _ret: Option<mir::BasicBlock>,
+        dest: &PlaceTy<'tcx>,
+        ret: Option<mir::BasicBlock>,
         _unwind: StackPopUnwind, // unwinding is not supported in consts
     ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> {
         debug!("find_mir_or_eval_fn: {:?}", instance);
@@ -289,7 +367,11 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
                 }
             }
 
-            if let Some(new_instance) = ecx.hook_special_const_fn(instance, args)? {
+            let Some(new_instance) = ecx.hook_special_const_fn(instance, _abi, args, dest, ret)? else {
+                return Ok(None);
+            };
+
+            if new_instance != instance {
                 // We call another const fn instead.
                 // However, we return the *original* instance to make backtraces work out
                 // (and we hope this does not confuse the FnAbi checks too much).
@@ -298,13 +380,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
                     new_instance,
                     _abi,
                     args,
-                    _dest,
-                    _ret,
+                    dest,
+                    ret,
                     _unwind,
                 )?
                 .map(|(body, _instance)| (body, instance)));
             }
         }
+
         // This is a const fn. Call it.
         Ok(Some((ecx.load_mir(instance.def, None)?, instance)))
     }
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 67e59460d74b0..29ba36c501637 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -1303,6 +1303,21 @@ impl<T: ?Sized> *const T {
     /// ```
     #[stable(feature = "align_offset", since = "1.36.0")]
     #[rustc_const_unstable(feature = "const_align_offset", issue = "90962")]
+    #[cfg(not(bootstrap))]
+    pub const fn align_offset(self, align: usize) -> usize
+    where
+        T: Sized,
+    {
+        assert!(align.is_power_of_two(), "align_offset: align is not a power-of-two");
+
+        // SAFETY: `align` has been checked to be a power of 2 above
+        unsafe { align_offset(self, align) }
+    }
+
+    #[stable(feature = "align_offset", since = "1.36.0")]
+    #[rustc_const_unstable(feature = "const_align_offset", issue = "90962")]
+    #[allow(missing_docs)]
+    #[cfg(bootstrap)]
     pub const fn align_offset(self, align: usize) -> usize
     where
         T: Sized,
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index 1f7cf6e5d052c..1e9add0067f8b 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -1554,10 +1554,14 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
 
 /// Align pointer `p`.
 ///
-/// Calculate offset (in terms of elements of `stride` stride) that has to be applied
+/// Calculate offset (in terms of elements of `size_of::<T>()` stride) that has to be applied
 /// to pointer `p` so that pointer `p` would get aligned to `a`.
 ///
-/// Note: This implementation has been carefully tailored to not panic. It is UB for this to panic.
+/// # Safety
+/// `a` must be a power of two.
+///
+/// # Notes
+/// This implementation has been carefully tailored to not panic. It is UB for this to panic.
 /// The only real change that can be made here is change of `INV_TABLE_MOD_16` and associated
 /// constants.
 ///
@@ -1566,8 +1570,10 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
 /// than trying to adapt this to accommodate that change.
 ///
 /// Any questions go to @nagisa.
+// #[cfg(not(bootstrap))] -- Calling this function in a const context from the bootstrap
+// compiler will always cause an error.
 #[lang = "align_offset"]
-pub(crate) unsafe fn align_offset<T: Sized>(p: *const T, a: usize) -> usize {
+pub(crate) const unsafe fn align_offset<T: Sized>(p: *const T, a: usize) -> usize {
     // FIXME(#75598): Direct use of these intrinsics improves codegen significantly at opt-level <=
     // 1, where the method versions of these operations are not inlined.
     use intrinsics::{
@@ -1584,7 +1590,7 @@ pub(crate) unsafe fn align_offset<T: Sized>(p: *const T, a: usize) -> usize {
     ///
     /// Implementation of this function shall not panic. Ever.
     #[inline]
-    unsafe fn mod_inv(x: usize, m: usize) -> usize {
+    const unsafe fn mod_inv(x: usize, m: usize) -> usize {
         /// Multiplicative modular inverse table modulo 2⁴ = 16.
         ///
         /// Note, that this table does not contain values where inverse does not exist (i.e., for
@@ -1624,8 +1630,13 @@ pub(crate) unsafe fn align_offset<T: Sized>(p: *const T, a: usize) -> usize {
         }
     }
 
-    let addr = p.addr();
     let stride = mem::size_of::<T>();
+
+    // SAFETY: At runtime transmuting a pointer to `usize` is always safe, because they have the
+    // same layout. During const eval we hook this function to ensure that the pointer always has
+    // an address (only the standard library can do this).
+    let addr = unsafe { mem::transmute(p) };
+
     // SAFETY: `a` is a power-of-two, therefore non-zero.
     let a_minus_one = unsafe { unchecked_sub(a, 1) };
 
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index bbcc7c699e036..9990f738688a3 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -1574,6 +1574,21 @@ impl<T: ?Sized> *mut T {
     /// ```
     #[stable(feature = "align_offset", since = "1.36.0")]
     #[rustc_const_unstable(feature = "const_align_offset", issue = "90962")]
+    #[cfg(not(bootstrap))]
+    pub const fn align_offset(self, align: usize) -> usize
+    where
+        T: Sized,
+    {
+        assert!(align.is_power_of_two(), "align_offset: align is not a power-of-two");
+
+        // SAFETY: `align` has been checked to be a power of 2 above
+        unsafe { align_offset(self, align) }
+    }
+
+    #[stable(feature = "align_offset", since = "1.36.0")]
+    #[rustc_const_unstable(feature = "const_align_offset", issue = "90962")]
+    #[allow(missing_docs)]
+    #[cfg(bootstrap)]
     pub const fn align_offset(self, align: usize) -> usize
     where
         T: Sized,

From de33f9ac9f588665203de1cabc4b9d361a473ff7 Mon Sep 17 00:00:00 2001
From: Lukas Markeffsky <@>
Date: Fri, 7 Oct 2022 20:59:23 +0200
Subject: [PATCH 4/7] add coretests for const `align_offset`

---
 library/core/tests/lib.rs |   1 +
 library/core/tests/ptr.rs | 165 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 166 insertions(+)

diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index b1f492381b136..f4b3ab8f89c7f 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -4,6 +4,7 @@
 #![feature(array_windows)]
 #![feature(bigint_helper_methods)]
 #![feature(cell_update)]
+#![feature(const_align_offset)]
 #![feature(const_assume)]
 #![feature(const_black_box)]
 #![feature(const_bool_to_option)]
diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs
index 97a369810056d..81a66f5f71231 100644
--- a/library/core/tests/ptr.rs
+++ b/library/core/tests/ptr.rs
@@ -358,6 +358,23 @@ fn align_offset_zst() {
     }
 }
 
+#[test]
+#[cfg(not(bootstrap))]
+fn align_offset_zst_const() {
+    const {
+        // For pointers of stride = 0, the pointer is already aligned or it cannot be aligned at
+        // all, because no amount of elements will align the pointer.
+        let mut p = 1;
+        while p < 1024 {
+            assert!(ptr::invalid::<()>(p).align_offset(p) == 0);
+            if p != 1 {
+                assert!(ptr::invalid::<()>(p + 1).align_offset(p) == !0);
+            }
+            p = (p + 1).next_power_of_two();
+        }
+    }
+}
+
 #[test]
 fn align_offset_stride_one() {
     // For pointers of stride = 1, the pointer can always be aligned. The offset is equal to
@@ -379,6 +396,26 @@ fn align_offset_stride_one() {
     }
 }
 
+#[test]
+#[cfg(not(bootstrap))]
+fn align_offset_stride_one_const() {
+    const {
+        // For pointers of stride = 1, the pointer can always be aligned. The offset is equal to
+        // number of bytes.
+        let mut align = 1;
+        while align < 1024 {
+            let mut ptr = 1;
+            while ptr < 2 * align {
+                let expected = ptr % align;
+                let offset = if expected == 0 { 0 } else { align - expected };
+                assert!(ptr::invalid::<u8>(ptr).align_offset(align) == offset);
+                ptr += 1;
+            }
+            align = (align + 1).next_power_of_two();
+        }
+    }
+}
+
 #[test]
 fn align_offset_various_strides() {
     unsafe fn test_stride<T>(ptr: *const T, align: usize) -> bool {
@@ -455,6 +492,134 @@ fn align_offset_various_strides() {
     assert!(!x);
 }
 
+#[test]
+#[cfg(not(bootstrap))]
+fn align_offset_various_strides_const() {
+    const unsafe fn test_stride<T>(ptr: *const T, numptr: usize, align: usize) {
+        let mut expected = usize::MAX;
+        // Naive but definitely correct way to find the *first* aligned element of stride::<T>.
+        let mut el = 0;
+        while el < align {
+            if (numptr + el * ::std::mem::size_of::<T>()) % align == 0 {
+                expected = el;
+                break;
+            }
+            el += 1;
+        }
+        let got = ptr.align_offset(align);
+        assert!(got == expected);
+    }
+
+    // For pointers of stride != 1, we verify the algorithm against the naivest possible
+    // implementation
+    let mut align = 1;
+    let limit = 1024;
+    while align < limit {
+        for ptr in 1usize..4 * align {
+            unsafe {
+                #[repr(packed)]
+                struct A3(u16, u8);
+                test_stride::<A3>(ptr::invalid::<A3>(ptr), ptr, align);
+
+                struct A4(u32);
+                test_stride::<A4>(ptr::invalid::<A4>(ptr), ptr, align);
+
+                #[repr(packed)]
+                struct A5(u32, u8);
+                test_stride::<A5>(ptr::invalid::<A5>(ptr), ptr, align);
+
+                #[repr(packed)]
+                struct A6(u32, u16);
+                test_stride::<A6>(ptr::invalid::<A6>(ptr), ptr, align);
+
+                #[repr(packed)]
+                struct A7(u32, u16, u8);
+                test_stride::<A7>(ptr::invalid::<A7>(ptr), ptr, align);
+
+                #[repr(packed)]
+                struct A8(u32, u32);
+                test_stride::<A8>(ptr::invalid::<A8>(ptr), ptr, align);
+
+                #[repr(packed)]
+                struct A9(u32, u32, u8);
+                test_stride::<A9>(ptr::invalid::<A9>(ptr), ptr, align);
+
+                #[repr(packed)]
+                struct A10(u32, u32, u16);
+                test_stride::<A10>(ptr::invalid::<A10>(ptr), ptr, align);
+
+                test_stride::<u32>(ptr::invalid::<u32>(ptr), ptr, align);
+                test_stride::<u128>(ptr::invalid::<u128>(ptr), ptr, align);
+            }
+        }
+        align = (align + 1).next_power_of_two();
+    }
+}
+
+#[test]
+#[cfg(not(bootstrap))]
+fn align_offset_with_provenance_const() {
+    const {
+        let data = 42;
+
+        let ptr: *const i32 = &data;
+        assert!(ptr.align_offset(1) == 0);
+        assert!(ptr.align_offset(2) == 0);
+        assert!(ptr.align_offset(4) == 0);
+        assert!(ptr.align_offset(8) == usize::MAX);
+        assert!(ptr.wrapping_byte_add(1).align_offset(1) == 0);
+        assert!(ptr.wrapping_byte_add(1).align_offset(2) == usize::MAX);
+        assert!(ptr.wrapping_byte_add(2).align_offset(1) == 0);
+        assert!(ptr.wrapping_byte_add(2).align_offset(2) == 0);
+        assert!(ptr.wrapping_byte_add(2).align_offset(4) == usize::MAX);
+        assert!(ptr.wrapping_byte_add(3).align_offset(1) == 0);
+        assert!(ptr.wrapping_byte_add(3).align_offset(2) == usize::MAX);
+
+        assert!(ptr.wrapping_add(42).align_offset(4) == 0);
+        assert!(ptr.wrapping_add(42).align_offset(8) == usize::MAX);
+
+        let ptr1: *const i8 = ptr.cast();
+        assert!(ptr1.align_offset(1) == 0);
+        assert!(ptr1.align_offset(2) == 0);
+        assert!(ptr1.align_offset(4) == 0);
+        assert!(ptr1.align_offset(8) == usize::MAX);
+        assert!(ptr1.wrapping_byte_add(1).align_offset(1) == 0);
+        assert!(ptr1.wrapping_byte_add(1).align_offset(2) == 1);
+        assert!(ptr1.wrapping_byte_add(1).align_offset(4) == 3);
+        assert!(ptr1.wrapping_byte_add(1).align_offset(8) == usize::MAX);
+        assert!(ptr1.wrapping_byte_add(2).align_offset(1) == 0);
+        assert!(ptr1.wrapping_byte_add(2).align_offset(2) == 0);
+        assert!(ptr1.wrapping_byte_add(2).align_offset(4) == 2);
+        assert!(ptr1.wrapping_byte_add(2).align_offset(8) == usize::MAX);
+        assert!(ptr1.wrapping_byte_add(3).align_offset(1) == 0);
+        assert!(ptr1.wrapping_byte_add(3).align_offset(2) == 1);
+        assert!(ptr1.wrapping_byte_add(3).align_offset(4) == 1);
+        assert!(ptr1.wrapping_byte_add(3).align_offset(8) == usize::MAX);
+
+        let ptr2: *const i16 = ptr.cast();
+        assert!(ptr2.align_offset(1) == 0);
+        assert!(ptr2.align_offset(2) == 0);
+        assert!(ptr2.align_offset(4) == 0);
+        assert!(ptr2.align_offset(8) == usize::MAX);
+        assert!(ptr2.wrapping_byte_add(1).align_offset(1) == 0);
+        assert!(ptr2.wrapping_byte_add(1).align_offset(2) == usize::MAX);
+        assert!(ptr2.wrapping_byte_add(2).align_offset(1) == 0);
+        assert!(ptr2.wrapping_byte_add(2).align_offset(2) == 0);
+        assert!(ptr2.wrapping_byte_add(2).align_offset(4) == 1);
+        assert!(ptr2.wrapping_byte_add(2).align_offset(8) == usize::MAX);
+        assert!(ptr2.wrapping_byte_add(3).align_offset(1) == 0);
+        assert!(ptr2.wrapping_byte_add(3).align_offset(2) == usize::MAX);
+
+        let ptr3: *const i64 = ptr.cast();
+        assert!(ptr3.align_offset(1) == 0);
+        assert!(ptr3.align_offset(2) == 0);
+        assert!(ptr3.align_offset(4) == 0);
+        assert!(ptr3.align_offset(8) == usize::MAX);
+        assert!(ptr3.wrapping_byte_add(1).align_offset(1) == 0);
+        assert!(ptr3.wrapping_byte_add(1).align_offset(2) == usize::MAX);
+    }
+}
+
 #[test]
 fn offset_from() {
     let mut a = [0; 5];

From 5d1c7a2524727291e30c4f8926f36074d0c6f3a1 Mon Sep 17 00:00:00 2001
From: Lukas Markeffsky <@>
Date: Fri, 7 Oct 2022 22:24:31 +0200
Subject: [PATCH 5/7] constify `pointer::is_aligned{,_to}`

---
 library/core/src/lib.rs           |  1 +
 library/core/src/ptr/const_ptr.rs | 25 +++++++++++++++++++------
 library/core/src/ptr/mut_ptr.rs   | 25 +++++++++++++++++++------
 3 files changed, 39 insertions(+), 12 deletions(-)

diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 6294ef0df3dec..b6c1dd85d5792 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -129,6 +129,7 @@
 #![feature(const_option)]
 #![feature(const_option_ext)]
 #![feature(const_pin)]
+#![feature(const_pointer_is_aligned)]
 #![feature(const_ptr_sub_ptr)]
 #![feature(const_replace)]
 #![feature(const_ptr_as_ref)]
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 29ba36c501637..2baa4e19ae937 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -1344,10 +1344,13 @@ impl<T: ?Sized> *const T {
     }
 
     /// Returns whether the pointer is properly aligned for `T`.
+    // #[cfg(not(bootstrap))] -- Calling this function in a const context from the bootstrap
+    // compiler will always return false.
     #[must_use]
     #[inline]
     #[unstable(feature = "pointer_is_aligned", issue = "96284")]
-    pub fn is_aligned(self) -> bool
+    #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "none")]
+    pub const fn is_aligned(self) -> bool
     where
         T: Sized,
     {
@@ -1362,16 +1365,26 @@ impl<T: ?Sized> *const T {
     /// # Panics
     ///
     /// The function panics if `align` is not a power-of-two (this includes 0).
+    // #[cfg(not(bootstrap))] -- Calling this function in a const context from the bootstrap
+    // compiler will always return false.
     #[must_use]
     #[inline]
     #[unstable(feature = "pointer_is_aligned", issue = "96284")]
-    pub fn is_aligned_to(self, align: usize) -> bool {
-        if !align.is_power_of_two() {
-            panic!("is_aligned_to: align is not a power-of-two");
+    #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "none")]
+    pub const fn is_aligned_to(self, align: usize) -> bool {
+        assert!(align.is_power_of_two(), "is_aligned_to: align is not a power-of-two");
+
+        #[inline]
+        fn runtime(ptr: *const u8, align: usize) -> bool {
+            ptr.addr() & (align - 1) == 0
+        }
+
+        const fn comptime(ptr: *const u8, align: usize) -> bool {
+            ptr.align_offset(align) == 0
         }
 
-        // Cast is needed for `T: !Sized`
-        self.cast::<u8>().addr() & align - 1 == 0
+        // SAFETY: `ptr.align_offset(align)` returns 0 if and only if the pointer is already aligned.
+        unsafe { intrinsics::const_eval_select((self.cast::<u8>(), align), comptime, runtime) }
     }
 }
 
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index 9990f738688a3..25437dd06ae75 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -1615,10 +1615,13 @@ impl<T: ?Sized> *mut T {
     }
 
     /// Returns whether the pointer is properly aligned for `T`.
+    // #[cfg(not(bootstrap))] -- Calling this function in a const context from the bootstrap
+    // compiler will always return false.
     #[must_use]
     #[inline]
     #[unstable(feature = "pointer_is_aligned", issue = "96284")]
-    pub fn is_aligned(self) -> bool
+    #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "none")]
+    pub const fn is_aligned(self) -> bool
     where
         T: Sized,
     {
@@ -1633,16 +1636,26 @@ impl<T: ?Sized> *mut T {
     /// # Panics
     ///
     /// The function panics if `align` is not a power-of-two (this includes 0).
+    // #[cfg(not(bootstrap))] -- Calling this function in a const context from the bootstrap
+    // compiler will always return false.
     #[must_use]
     #[inline]
     #[unstable(feature = "pointer_is_aligned", issue = "96284")]
-    pub fn is_aligned_to(self, align: usize) -> bool {
-        if !align.is_power_of_two() {
-            panic!("is_aligned_to: align is not a power-of-two");
+    #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "none")]
+    pub const fn is_aligned_to(self, align: usize) -> bool {
+        assert!(align.is_power_of_two(), "is_aligned_to: align is not a power-of-two");
+
+        #[inline]
+        fn runtime(ptr: *mut u8, align: usize) -> bool {
+            ptr.addr() & (align - 1) == 0
+        }
+
+        const fn comptime(ptr: *mut u8, align: usize) -> bool {
+            ptr.align_offset(align) == 0
         }
 
-        // Cast is needed for `T: !Sized`
-        self.cast::<u8>().addr() & align - 1 == 0
+        // SAFETY: `ptr.align_offset(align)` returns 0 if and only if the pointer is already aligned.
+        unsafe { intrinsics::const_eval_select((self.cast::<u8>(), align), comptime, runtime) }
     }
 }
 

From 5371cc4d4c657b783a87c9b47231c425a6ff8845 Mon Sep 17 00:00:00 2001
From: Lukas Markeffsky <@>
Date: Fri, 7 Oct 2022 21:46:28 +0200
Subject: [PATCH 6/7] add coretests for `is_aligned`

---
 library/core/tests/lib.rs |  2 ++
 library/core/tests/ptr.rs | 48 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 50 insertions(+)

diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index f4b3ab8f89c7f..9704650edf0f5 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -17,6 +17,7 @@
 #![feature(const_nonnull_new)]
 #![feature(const_num_from_num)]
 #![feature(const_pointer_byte_offsets)]
+#![feature(const_pointer_is_aligned)]
 #![feature(const_ptr_as_ref)]
 #![feature(const_ptr_read)]
 #![feature(const_ptr_write)]
@@ -80,6 +81,7 @@
 #![feature(never_type)]
 #![feature(unwrap_infallible)]
 #![feature(pointer_byte_offsets)]
+#![feature(pointer_is_aligned)]
 #![feature(portable_simd)]
 #![feature(ptr_metadata)]
 #![feature(once_cell)]
diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs
index 81a66f5f71231..a1c23db2932a4 100644
--- a/library/core/tests/ptr.rs
+++ b/library/core/tests/ptr.rs
@@ -620,6 +620,54 @@ fn align_offset_with_provenance_const() {
     }
 }
 
+#[test]
+fn is_aligned() {
+    let data = 42;
+    let ptr: *const i32 = &data;
+    assert!(ptr.is_aligned());
+    assert!(ptr.is_aligned_to(1));
+    assert!(ptr.is_aligned_to(2));
+    assert!(ptr.is_aligned_to(4));
+    assert!(ptr.wrapping_byte_add(2).is_aligned_to(1));
+    assert!(ptr.wrapping_byte_add(2).is_aligned_to(2));
+    assert!(!ptr.wrapping_byte_add(2).is_aligned_to(4));
+
+    // At runtime either `ptr` or `ptr+1` is aligned to 8.
+    assert_ne!(ptr.is_aligned_to(8), ptr.wrapping_add(1).is_aligned_to(8));
+}
+
+#[test]
+#[cfg(not(bootstrap))]
+fn is_aligned_const() {
+    const {
+        let data = 42;
+        let ptr: *const i32 = &data;
+        assert!(ptr.is_aligned());
+        assert!(ptr.is_aligned_to(1));
+        assert!(ptr.is_aligned_to(2));
+        assert!(ptr.is_aligned_to(4));
+        assert!(ptr.wrapping_byte_add(2).is_aligned_to(1));
+        assert!(ptr.wrapping_byte_add(2).is_aligned_to(2));
+        assert!(!ptr.wrapping_byte_add(2).is_aligned_to(4));
+
+        // At comptime neither `ptr` nor `ptr+1` is aligned to 8.
+        assert!(!ptr.is_aligned_to(8));
+        assert!(!ptr.wrapping_add(1).is_aligned_to(8));
+    }
+}
+
+#[test]
+#[cfg(bootstrap)]
+fn is_aligned_const() {
+    const {
+        let data = 42;
+        let ptr: *const i32 = &data;
+        // The bootstrap compiler always returns false for is_aligned.
+        assert!(!ptr.is_aligned());
+        assert!(!ptr.is_aligned_to(1));
+    }
+}
+
 #[test]
 fn offset_from() {
     let mut a = [0; 5];

From c2605f6711796bd2101bfa4f138cf973d5336136 Mon Sep 17 00:00:00 2001
From: Lukas Markeffsky <@>
Date: Thu, 20 Oct 2022 19:46:31 +0200
Subject: [PATCH 7/7] mark `align_offset` as `#[must_use]`

---
 library/core/src/ptr/const_ptr.rs | 1 +
 library/core/src/ptr/mut_ptr.rs   | 1 +
 2 files changed, 2 insertions(+)

diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 2baa4e19ae937..41e62cc769e3b 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -1301,6 +1301,7 @@ impl<T: ?Sized> *const T {
     /// }
     /// # }
     /// ```
+    #[must_use]
     #[stable(feature = "align_offset", since = "1.36.0")]
     #[rustc_const_unstable(feature = "const_align_offset", issue = "90962")]
     #[cfg(not(bootstrap))]
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index 25437dd06ae75..381652b9a3635 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -1572,6 +1572,7 @@ impl<T: ?Sized> *mut T {
     /// }
     /// # }
     /// ```
+    #[must_use]
     #[stable(feature = "align_offset", since = "1.36.0")]
     #[rustc_const_unstable(feature = "const_align_offset", issue = "90962")]
     #[cfg(not(bootstrap))]