Skip to content
9 changes: 8 additions & 1 deletion library/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,11 @@ check-cfg = [

[package.metadata.flux]
enabled = true
include = [ "src/ascii{*.rs,/**/*.rs}" ]
check_overflow = "lazy"
include = [ "src/ascii*.rs",
"src/num/mod.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 @@ -479,6 +479,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 @@ -516,6 +517,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))]
#[unstable(feature = "ascii_char", issue = "110998")]
#[inline]
#[track_caller]
Expand All @@ -536,8 +541,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
79 changes: 77 additions & 2 deletions library/core/src/flux_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,87 @@
/// 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)
}

fn is_ascii_uppercase(n: int) -> bool {
cast('A') <= n && n <= cast('Z')
}

fn is_ascii_lowercase(n: int) -> bool {
cast('a') <= n && n <= cast('z')
}

fn to_ascii_uppercase(n: int) -> int {
n - (cast(is_ascii_lowercase(n)) * 32)
}

fn to_ascii_lowercase(n: int) -> int {
n + (cast(is_ascii_uppercase(n)) * 32)
}

property BitXor0[^](x, y) {
(y == 0) => [^](x, y) == x
}

property BitXor32[^](x, y) {
(is_ascii_lowercase(x) && y == 32) => [^](x, y) == x - 32
}

property BitOr0[|](x, y) {
(y == 0) => [|](x, y) == x
}

property BitOr32[|](x, y) {
(is_ascii_uppercase(x) && y == 32) => [|](x, y) == x + 32
}

}]
#[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 _: () = {};
5 changes: 5 additions & 0 deletions library/core/src/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@ impl u8 {
/// # Safety
///
/// This byte must be valid ASCII, or else this is UB.
#[cfg_attr(flux, flux::spec(fn({&Self[@n] | n <= 127}) -> _))]
#[must_use]
#[unstable(feature = "ascii_char", issue = "110998")]
#[inline]
Expand Down Expand Up @@ -533,6 +534,7 @@ impl u8 {
/// ```
///
/// [`make_ascii_uppercase`]: Self::make_ascii_uppercase
#[cfg_attr(flux, flux::spec(fn(&u8[@n]) -> u8[to_ascii_uppercase(n)]))]
#[must_use = "to uppercase the value in-place, use `make_ascii_uppercase()`"]
#[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
#[rustc_const_stable(feature = "const_ascii_methods_on_intrinsics", since = "1.52.0")]
Expand All @@ -558,6 +560,7 @@ impl u8 {
/// ```
///
/// [`make_ascii_lowercase`]: Self::make_ascii_lowercase
#[cfg_attr(flux, flux::spec(fn(&u8[@n]) -> u8[to_ascii_lowercase(n)]))]
#[must_use = "to lowercase the value in-place, use `make_ascii_lowercase()`"]
#[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
#[rustc_const_stable(feature = "const_ascii_methods_on_intrinsics", since = "1.52.0")]
Expand Down Expand Up @@ -706,6 +709,7 @@ impl u8 {
/// assert!(!lf.is_ascii_uppercase());
/// assert!(!esc.is_ascii_uppercase());
/// ```
#[cfg_attr(flux, flux::spec(fn(&Self[@n]) -> bool[is_ascii_uppercase(n)]))]
#[must_use]
#[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
#[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")]
Expand Down Expand Up @@ -740,6 +744,7 @@ impl u8 {
/// assert!(!lf.is_ascii_lowercase());
/// assert!(!esc.is_ascii_lowercase());
/// ```
#[cfg_attr(flux, flux::spec(fn(&u8[@n]) -> bool[is_ascii_lowercase(n)]))]
#[must_use]
#[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
#[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")]
Expand Down
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 @@ -629,6 +629,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