Skip to content

Add Flux specifications to path/bstr/hash/time #438

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
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: 1 addition & 1 deletion .github/workflows/flux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:

env:
FIXPOINT_VERSION: "556104ba5508891c357b0bdf819ce706e93d9349"
FLUX_VERSION: "ebafb8d0ca32d8c0fcd2a0cfef6b1b4bd4dc4a6f"
FLUX_VERSION: "90f59e3b2c7fcbea5e2968208a89d0b83a6521c2"

jobs:
check-flux-on-core:
Expand Down
8 changes: 7 additions & 1 deletion library/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,10 @@ check-cfg = [

[package.metadata.flux]
enabled = true
include = [ "src/ascii{*.rs,/**/*.rs}" ]
check_overflow = "lazy"
include = [ "src/ascii*.rs",
"src/pat.rs",
"src/bstr/*.rs",
"src/hash/*.rs",
"src/time.rs",
]
7 changes: 6 additions & 1 deletion library/core/src/ascii/ascii_char.rs
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,7 @@ impl AsciiChar {
/// `b` must be in `0..=127`, or else this is UB.
#[unstable(feature = "ascii_char", issue = "110998")]
#[inline]
#[cfg_attr(flux, flux::spec(fn(b: u8{b <= 127}) -> Self))]
#[requires(b <= 127)]
#[ensures(|result| *result as u8 == b)]
pub const unsafe fn from_u8_unchecked(b: u8) -> Self {
Expand Down Expand Up @@ -508,6 +509,10 @@ impl AsciiChar {
/// when writing code using this method, since the implementation doesn't
/// need something really specific, not to make those other arguments do
/// something useful. It might be tightened before stabilization.)
// Only `d < 64` is required for safety as described above, but we use `d < 10` as
// in the `assert_unsafe_precondition` inside. See https://github.com/rust-lang/rust/pull/129374
// for some context about the discrepancy.
#[cfg_attr(flux, flux::spec(fn(d: u8{d < 10}) -> Self))]
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this be d <= 64?

Copy link
Author

Choose a reason for hiding this comment

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

The safety comment doesn't match the UB assertion inside the function, so I wasn't sure which one to choose. If the intention is to keep the more relaxed version, then maybe we should change the UB assertion upstream.

Choose a reason for hiding this comment

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

Yes I picked 10 because that’s what the assertion inside the function checked..

Copy link
Member

Choose a reason for hiding this comment

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

I think we should get something changed upstream. It doesn't seem right to have an assert_unsafe_precondition with d < 10 here as there isn't really anything unsafe up until 63 (could be a debug_assert instead). If they, however, deem it unsafe between 10 and 63 because of some other piece of code then the comment should be changed.

Copy link
Author

Choose a reason for hiding this comment

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

I think they just yoloed it rust-lang#129374

Copy link
Member

Choose a reason for hiding this comment

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

Thank you for digging deeper! So I'd say pick whichever you prefer at this point, but please include a comment noting the disparity (and possibly linking to that PR).

Choose a reason for hiding this comment

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

@nilehmann FWIW the code checks if we change it to d <= 64 (as in the unchecked_add is fine...)

#[unstable(feature = "ascii_char", issue = "110998")]
#[inline]
#[track_caller]
Expand All @@ -528,8 +533,8 @@ impl AsciiChar {
}

/// Gets this ASCII character as a byte.
#[cfg_attr(flux, flux::spec(fn (Self) -> u8{v: v <= 127}))]
#[unstable(feature = "ascii_char", issue = "110998")]
#[cfg_attr(flux, flux::spec(fn(Self) -> u8{v: v <= 127}))]
#[inline]
pub const fn to_u8(self) -> u8 {
self as u8
Expand Down
46 changes: 44 additions & 2 deletions library/core/src/flux_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,54 @@
/// See the following link for more information on how extensible properties for primitive operations work:
/// <https://flux-rs.github.io/flux/guide/specifications.html#extensible-properties-for-primitive-ops>
#[flux::defs {
fn char_to_int(x:char) -> int {
cast(x)
}

property ShiftRightByFour[>>](x, y) {
16 * [>>](x, 4) == x
}

property MaskBy15[&](x, y) {
[&](x, y) <= y
property MaskLess[&](x, y) {
[&](x, y) <= x && [&](x, y) <= y
}

property ShiftLeft[<<](n, k) {
0 < k => n <= [<<](n, k)
}
}]
#[flux::specs {
mod hash {
mod sip {
struct Hasher {
k0: u64,
k1: u64,
length: usize, // how many bytes we've processed
state: State, // hash State
tail: u64, // unprocessed bytes le
ntail: usize{v: v <= 8}, // how many bytes in tail are valid
_marker: PhantomData<S>,
}
}

impl BuildHasherDefault {
#[trusted(reason="https://github.com/flux-rs/flux/issues/1185")]
fn new() -> Self;
}
}

impl Hasher for hash::sip::Hasher {
fn write(self: &mut Self, msg: &[u8]) ensures self: Self; // mut-ref-unfolding
}

impl Clone for hash::BuildHasherDefault {
#[trusted(reason="https://github.com/flux-rs/flux/issues/1185")]
fn clone(self: &Self) -> Self;
}

impl Debug for time::Duration {
#[trusted(reason="modular arithmetic invariant inside nested fmt_decimal")]
fn fmt(self: &Self, f: &mut fmt::Formatter) -> fmt::Result;
}
}]
const _: () = {};
6 changes: 6 additions & 0 deletions library/core/src/num/niche_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ macro_rules! define_valid_range_type {
$(#[$m:meta])*
$vis:vis struct $name:ident($int:ident as $uint:ident in $low:literal..=$high:literal);
)+) => {$(
#[cfg_attr(flux, flux::opaque)]
#[cfg_attr(flux, flux::refined_by(val: int))]
#[cfg_attr(flux, flux::invariant($low <= cast(val) && cast(val) <= $high))]
#[derive(Clone, Copy, Eq)]
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start($low)]
Expand All @@ -33,6 +36,7 @@ macro_rules! define_valid_range_type {

impl $name {
#[inline]
#[cfg_attr(flux, flux::spec(fn (val: $int) -> Option<Self[{val: cast(val)}]>))]
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
Expand All @@ -49,12 +53,14 @@ macro_rules! define_valid_range_type {
/// Immediate language UB if `val` is not within the valid range for this
/// type, as it violates the validity invariant.
#[inline]
#[cfg_attr(flux, flux::spec(fn (val: $int{ $low <= cast(val) && cast(val) <= $high }) -> Self[{val:cast(val)}]))]
pub const unsafe fn new_unchecked(val: $int) -> Self {
// SAFETY: Caller promised that `val` is within the valid range.
unsafe { $name(val) }
}

#[inline]
#[cfg_attr(flux, flux::spec(fn (self: Self) -> $int[cast(self.val)] ensures $low <= cast(self.val) && cast(self.val) <= $high))]
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.)
Expand Down
1 change: 1 addition & 0 deletions library/core/src/num/uint_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,7 @@ macro_rules! uint_impl {
without modifying the original"]
#[inline(always)]
#[track_caller]
#[cfg_attr(flux, flux::spec(fn(self: Self, rhs: Self{self + rhs <= $MaxV}) -> Self[self + rhs]))]
#[requires(!self.overflowing_add(rhs).1)]
pub const unsafe fn unchecked_add(self, rhs: Self) -> Self {
assert_unsafe_precondition!(
Expand Down
18 changes: 18 additions & 0 deletions library/core/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,22 @@ macro_rules! pattern_type {
};
}

// The Flux spec for the `trait RangePattern` below uses
// [associated refinements](https://flux-rs.github.io/flux/tutorial/08-traits.html)
// The `sub_one` method may only be safe for certain values,
// e.g. when the value is not the "minimum of the type" as in the
// case of the `char` implementation below. To specify this precondition generically
// 1. at the trait level, we introduce the predicate `sub_ok`
// which characterizes the "valid" values at which `sub_one`
// can be safely called, and by default, specify this predicate
// is "true";
// 2. at the impl level, we can provide a type-specific implementation
// of `sub_ok` that permits the verification of the impl for that type.

/// A trait implemented for integer types and `char`.
/// Useful in the future for generic pattern types, but
/// used right now to simplify ast lowering of pattern type ranges.
#[cfg_attr(flux, flux::assoc(fn sub_ok(self: Self) -> bool { true }))]
#[unstable(feature = "pattern_type_range_trait", issue = "123646")]
#[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")]
#[const_trait]
Expand All @@ -33,6 +46,7 @@ pub trait RangePattern {
const MAX: Self;

/// A compile-time helper to subtract 1 for exclusive ranges.
#[cfg_attr(flux, flux::spec(fn (self: Self{<Self as RangePattern>::sub_ok(self)}) -> Self))]
#[lang = "RangeSub"]
#[track_caller]
fn sub_one(self) -> Self;
Expand Down Expand Up @@ -61,12 +75,16 @@ impl_range_pat! {
u8, u16, u32, u64, u128, usize,
}

// The "associated refinement" `sub_ok` is defined as `non-zero` for `char`, to let Flux
// verify that the `self as u32 -1` in the impl does not underflow.
#[cfg_attr(flux, flux::assoc(fn sub_ok(self: char) -> bool { 0 < char_to_int(self)}))]
#[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")]
impl const RangePattern for char {
const MIN: Self = char::MIN;

const MAX: Self = char::MAX;

#[cfg_attr(flux, flux::spec(fn (self: char{<char as RangePattern>::sub_ok(self)}) -> char))]
fn sub_one(self) -> Self {
match char::from_u32(self as u32 - 1) {
None => panic!("exclusive range to start of valid chars"),
Expand Down
Loading