Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions library/core/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2647,6 +2647,7 @@ pub const unsafe fn const_make_global(ptr: *mut u8) -> *const u8 {
// doesn't honor `#[allow_internal_unstable]`, so for the const feature gate we use the user-facing
// `contracts` feature rather than the perma-unstable `contracts_internals`
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
#[miri::intrinsic_fallback_is_spec]
#[lang = "contract_check_requires"]
#[rustc_intrinsic]
pub const fn contract_check_requires<C: Fn() -> bool + Copy>(cond: C) {
Expand Down Expand Up @@ -2676,6 +2677,7 @@ pub const fn contract_check_requires<C: Fn() -> bool + Copy>(cond: C) {
// `contracts` feature rather than the perma-unstable `contracts_internals`.
// Const-checking doesn't honor allow_internal_unstable logic used by contract expansion.
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
#[miri::intrinsic_fallback_is_spec]
#[lang = "contract_check_ensures"]
#[rustc_intrinsic]
pub const fn contract_check_ensures<C: Fn(&Ret) -> bool + Copy, Ret>(
Expand Down
1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
#![feature(const_destruct)]
#![feature(const_eval_select)]
#![feature(const_select_unpredictable)]
#![feature(contracts)]
#![feature(core_intrinsics)]
#![feature(coverage_attribute)]
#![feature(disjoint_bitor)]
Expand Down
2 changes: 2 additions & 0 deletions library/core/src/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,8 @@ pub const fn align_of<T>() -> usize {
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_align_of_val", since = "1.85.0")]
#[rustc_allow_const_fn_unstable(contracts)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should add rustc_const_stable_indirect to contract functions instead. @rust-lang/wg-const-eval?

Copy link
Member

@RalfJung RalfJung Mar 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which functions would that affect?

Ah, #136925 anyway needs to be resolved first.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd still like to find a solution that does not require rustc_allow_const_fn_unstable. As @celinval suggested, we should try to mark the contract functions as #[rustc_const_stable_indirect]; if that works, that's a better solution.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tautschnig did you try to make this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I finally tried adding #[rustc_const_stable_indirect] to contract_check_ensures. Yet I then got:

error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_eval_select)]`
    --> library/core/src/intrinsics/mod.rs:2439:9
[...]
help: if the function is not (yet) meant to be exposed to stable const contexts, add `#[rustc_const_unstable]`
     |
2684 + #[rustc_const_unstable(feature = "...", issue = "...")]
2685 | pub const fn contract_check_ensures<C: Fn(&Ret) -> bool + Copy, Ret>(
     |

where the proposed fix won't actually work as the function is already marked with rustc_const_unstable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah you'll have to add rustc_allow_const_fn_unstable(const_eval_select) to contract_check_requires and contract_check_ensures. That's fine, we have const_eval_select in a whole bunch of stable const fn at this point.

#[core::contracts::ensures(|result: &usize| result.is_power_of_two())]
pub const fn align_of_val<T: ?Sized>(val: &T) -> usize {
// SAFETY: val is a reference, so it's a valid raw pointer
unsafe { intrinsics::align_of_val(val) }
Expand Down
28 changes: 28 additions & 0 deletions library/core/src/ptr/alignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ impl Alignment {
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
#[inline]
#[must_use]
#[rustc_allow_const_fn_unstable(contracts)]
#[core::contracts::ensures(|result: &Alignment| result.as_usize().is_power_of_two())]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an odd contract, since the statement is true about all Alignment. Shouldn't it be a postcondition of as_usize instead?

pub const fn of<T>() -> Self {
// This can't actually panic since type alignment is always a power of two.
const { Alignment::new(align_of::<T>()).unwrap() }
Expand All @@ -56,6 +58,11 @@ impl Alignment {
/// Note that `0` is not a power of two, nor a valid alignment.
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
#[inline]
#[rustc_allow_const_fn_unstable(contracts)]
#[core::contracts::ensures(
move |result: &Option<Alignment>|
align.is_power_of_two() == result.is_some() &&
(result.is_none() || result.unwrap().as_usize() == align))]
Comment on lines +62 to +65
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The formatting here is not great, with the code having the same indentation level as the closure header. What about something like this?

Suggested change
#[core::contracts::ensures(
move |result: &Option<Alignment>|
align.is_power_of_two() == result.is_some() &&
(result.is_none() || result.unwrap().as_usize() == align))]
#[core::contracts::ensures(move |result: &Option<Alignment>|
align.is_power_of_two() == result.is_some() &&
(result.is_none() || result.unwrap().as_usize() == align)
)]

pub const fn new(align: usize) -> Option<Self> {
if align.is_power_of_two() {
// SAFETY: Just checked it only has one bit set
Expand All @@ -76,6 +83,11 @@ impl Alignment {
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
#[inline]
#[track_caller]
#[rustc_allow_const_fn_unstable(contracts)]
#[core::contracts::requires(align.is_power_of_two())]
#[core::contracts::ensures(
move |result: &Alignment|
result.as_usize() == align && result.as_usize().is_power_of_two())]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result.as_usize().is_power_of_two() is redundant, it follows from the precondition and the first conjunct.

pub const unsafe fn new_unchecked(align: usize) -> Self {
assert_unsafe_precondition!(
check_language_ub,
Expand All @@ -91,13 +103,19 @@ impl Alignment {
/// Returns the alignment as a [`usize`].
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
#[inline]
#[rustc_allow_const_fn_unstable(contracts)]
#[core::contracts::ensures(|result: &usize| result.is_power_of_two())]
pub const fn as_usize(self) -> usize {
self.0 as usize
}

/// Returns the alignment as a <code>[NonZero]<[usize]></code>.
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
#[inline]
#[rustc_allow_const_fn_unstable(contracts)]
#[core::contracts::ensures(
move |result: &NonZero<usize>|
result.get().is_power_of_two() && result.get() == self.as_usize())]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result.get() == self.as_usize() implies result.get().is_power_of_two()... seems odd to repeat that? If we always repeat the transitive closure of everything that can be deduced, that surely won't scale.^^

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What we really need is Type invariant. This is basically repeating the type invariant which should be implied.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well that would be even nicer, sure. But even with the status quo, I don't get the point of this contract. To make use of the is_power_of_two fact I have to call self.as_usize(), and at that point the is_power_of_two will be asserted. So what is the point of repeating it everywhere?

pub const fn as_nonzero(self) -> NonZero<usize> {
// This transmutes directly to avoid the UbCheck in `NonZero::new_unchecked`
// since there's no way for the user to trip that check anyway -- the
Expand All @@ -123,6 +141,11 @@ impl Alignment {
/// ```
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
#[inline]
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
#[core::contracts::requires(self.as_usize().is_power_of_two())]
#[core::contracts::ensures(
move |result: &u32|
*result < usize::BITS && (1usize << *result) == self.as_usize())]
pub const fn log2(self) -> u32 {
self.as_nonzero().trailing_zeros()
}
Expand Down Expand Up @@ -152,6 +175,11 @@ impl Alignment {
/// ```
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
#[inline]
#[rustc_const_unstable(feature = "contracts", issue = "128044")]
#[core::contracts::ensures(
move |result: &usize|
*result > 0 && *result == !(self.as_usize() -1) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
*result > 0 && *result == !(self.as_usize() -1) &&
*result > 0 && *result == !(self.as_usize() - 1) &&

Formatting nit

self.as_usize() & *result == self.as_usize())]
pub const fn mask(self) -> usize {
// SAFETY: The alignment is always nonzero, and therefore decrementing won't overflow.
!(unsafe { self.as_usize().unchecked_sub(1) })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,37 @@
scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
let mut _6: std::num::NonZero<usize>;
scope 6 {
scope 8 (inlined std::ptr::Alignment::as_nonzero) {
scope 12 (inlined std::ptr::Alignment::as_nonzero) {
scope 13 {
scope 14 {
scope 15 {
}
}
}
scope 16 (inlined core::contracts::build_check_ensures::<NonZero<usize>, {closure@std::ptr::Alignment::as_nonzero::{closure#0}}>) {
}
}
scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
scope 17 (inlined NonNull::<[bool; 0]>::without_provenance) {
let _7: *const [bool; 0];
scope 10 {
scope 18 {
}
scope 11 (inlined NonZero::<usize>::get) {
scope 19 (inlined NonZero::<usize>::get) {
}
scope 12 (inlined std::ptr::without_provenance::<[bool; 0]>) {
scope 13 (inlined without_provenance_mut::<[bool; 0]>) {
scope 20 (inlined std::ptr::without_provenance::<[bool; 0]>) {
scope 21 (inlined without_provenance_mut::<[bool; 0]>) {
}
}
}
}
scope 7 (inlined std::ptr::Alignment::of::<[bool; 0]>) {
scope 8 {
scope 9 {
scope 10 {
}
}
}
scope 11 (inlined core::contracts::build_check_ensures::<std::ptr::Alignment, {closure@std::ptr::Alignment::of<[bool; 0]>::{closure#0}}>) {
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,37 @@
scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
let mut _6: std::num::NonZero<usize>;
scope 6 {
scope 8 (inlined std::ptr::Alignment::as_nonzero) {
scope 12 (inlined std::ptr::Alignment::as_nonzero) {
scope 13 {
scope 14 {
scope 15 {
}
}
}
scope 16 (inlined core::contracts::build_check_ensures::<NonZero<usize>, {closure@std::ptr::Alignment::as_nonzero::{closure#0}}>) {
}
}
scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
scope 17 (inlined NonNull::<[bool; 0]>::without_provenance) {
let _7: *const [bool; 0];
scope 10 {
scope 18 {
}
scope 11 (inlined NonZero::<usize>::get) {
scope 19 (inlined NonZero::<usize>::get) {
}
scope 12 (inlined std::ptr::without_provenance::<[bool; 0]>) {
scope 13 (inlined without_provenance_mut::<[bool; 0]>) {
scope 20 (inlined std::ptr::without_provenance::<[bool; 0]>) {
scope 21 (inlined without_provenance_mut::<[bool; 0]>) {
}
}
}
}
scope 7 (inlined std::ptr::Alignment::of::<[bool; 0]>) {
scope 8 {
scope 9 {
scope 10 {
}
}
}
scope 11 (inlined core::contracts::build_check_ensures::<std::ptr::Alignment, {closure@std::ptr::Alignment::of<[bool; 0]>::{closure#0}}>) {
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,37 @@
scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
let mut _6: std::num::NonZero<usize>;
scope 6 {
scope 8 (inlined std::ptr::Alignment::as_nonzero) {
scope 12 (inlined std::ptr::Alignment::as_nonzero) {
scope 13 {
scope 14 {
scope 15 {
}
}
}
scope 16 (inlined core::contracts::build_check_ensures::<NonZero<usize>, {closure@std::ptr::Alignment::as_nonzero::{closure#0}}>) {
}
}
scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
scope 17 (inlined NonNull::<[bool; 0]>::without_provenance) {
let _7: *const [bool; 0];
scope 10 {
scope 18 {
}
scope 11 (inlined NonZero::<usize>::get) {
scope 19 (inlined NonZero::<usize>::get) {
}
scope 12 (inlined std::ptr::without_provenance::<[bool; 0]>) {
scope 13 (inlined without_provenance_mut::<[bool; 0]>) {
scope 20 (inlined std::ptr::without_provenance::<[bool; 0]>) {
scope 21 (inlined without_provenance_mut::<[bool; 0]>) {
}
}
}
}
scope 7 (inlined std::ptr::Alignment::of::<[bool; 0]>) {
scope 8 {
scope 9 {
scope 10 {
}
}
}
scope 11 (inlined core::contracts::build_check_ensures::<std::ptr::Alignment, {closure@std::ptr::Alignment::of<[bool; 0]>::{closure#0}}>) {
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,37 @@
scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
let mut _6: std::num::NonZero<usize>;
scope 6 {
scope 8 (inlined std::ptr::Alignment::as_nonzero) {
scope 12 (inlined std::ptr::Alignment::as_nonzero) {
scope 13 {
scope 14 {
scope 15 {
}
}
}
scope 16 (inlined core::contracts::build_check_ensures::<NonZero<usize>, {closure@std::ptr::Alignment::as_nonzero::{closure#0}}>) {
}
}
scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
scope 17 (inlined NonNull::<[bool; 0]>::without_provenance) {
let _7: *const [bool; 0];
scope 10 {
scope 18 {
}
scope 11 (inlined NonZero::<usize>::get) {
scope 19 (inlined NonZero::<usize>::get) {
}
scope 12 (inlined std::ptr::without_provenance::<[bool; 0]>) {
scope 13 (inlined without_provenance_mut::<[bool; 0]>) {
scope 20 (inlined std::ptr::without_provenance::<[bool; 0]>) {
scope 21 (inlined without_provenance_mut::<[bool; 0]>) {
}
}
}
}
scope 7 (inlined std::ptr::Alignment::of::<[bool; 0]>) {
scope 8 {
scope 9 {
scope 10 {
}
}
}
scope 11 (inlined core::contracts::build_check_ensures::<std::ptr::Alignment, {closure@std::ptr::Alignment::of<[bool; 0]>::{closure#0}}>) {
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,37 @@
scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
let mut _6: std::num::NonZero<usize>;
scope 6 {
scope 8 (inlined std::ptr::Alignment::as_nonzero) {
scope 12 (inlined std::ptr::Alignment::as_nonzero) {
scope 13 {
scope 14 {
scope 15 {
}
}
}
scope 16 (inlined core::contracts::build_check_ensures::<NonZero<usize>, {closure@std::ptr::Alignment::as_nonzero::{closure#0}}>) {
}
}
scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
scope 17 (inlined NonNull::<[bool; 0]>::without_provenance) {
let _7: *const [bool; 0];
scope 10 {
scope 18 {
}
scope 11 (inlined NonZero::<usize>::get) {
scope 19 (inlined NonZero::<usize>::get) {
}
scope 12 (inlined std::ptr::without_provenance::<[bool; 0]>) {
scope 13 (inlined without_provenance_mut::<[bool; 0]>) {
scope 20 (inlined std::ptr::without_provenance::<[bool; 0]>) {
scope 21 (inlined without_provenance_mut::<[bool; 0]>) {
}
}
}
}
scope 7 (inlined std::ptr::Alignment::of::<[bool; 0]>) {
scope 8 {
scope 9 {
scope 10 {
}
}
}
scope 11 (inlined core::contracts::build_check_ensures::<std::ptr::Alignment, {closure@std::ptr::Alignment::of<[bool; 0]>::{closure#0}}>) {
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,37 @@
scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
let mut _6: std::num::NonZero<usize>;
scope 6 {
scope 8 (inlined std::ptr::Alignment::as_nonzero) {
scope 12 (inlined std::ptr::Alignment::as_nonzero) {
scope 13 {
scope 14 {
scope 15 {
}
}
}
scope 16 (inlined core::contracts::build_check_ensures::<NonZero<usize>, {closure@std::ptr::Alignment::as_nonzero::{closure#0}}>) {
}
}
scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
scope 17 (inlined NonNull::<[bool; 0]>::without_provenance) {
let _7: *const [bool; 0];
scope 10 {
scope 18 {
}
scope 11 (inlined NonZero::<usize>::get) {
scope 19 (inlined NonZero::<usize>::get) {
}
scope 12 (inlined std::ptr::without_provenance::<[bool; 0]>) {
scope 13 (inlined without_provenance_mut::<[bool; 0]>) {
scope 20 (inlined std::ptr::without_provenance::<[bool; 0]>) {
scope 21 (inlined without_provenance_mut::<[bool; 0]>) {
}
}
}
}
scope 7 (inlined std::ptr::Alignment::of::<[bool; 0]>) {
scope 8 {
scope 9 {
scope 10 {
}
}
}
scope 11 (inlined core::contracts::build_check_ensures::<std::ptr::Alignment, {closure@std::ptr::Alignment::of<[bool; 0]>::{closure#0}}>) {
}
}
}
}
Expand Down
Loading
Loading