Skip to content

Commit 994cbf4

Browse files
authored
Merge branch 'main' into update-readme-challenge-1
2 parents 9265556 + 195e1b6 commit 994cbf4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+11601
-27
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# This workflow runs the tests for testable simd models.
2+
3+
name: Testable simd models
4+
5+
on:
6+
workflow_dispatch:
7+
merge_group:
8+
pull_request:
9+
branches: [ main ]
10+
push:
11+
paths:
12+
- '.github/workflows/testable-simd-models.yml'
13+
- 'testable-simd-models/**'
14+
15+
defaults:
16+
run:
17+
shell: bash
18+
19+
jobs:
20+
testable-simd-models:
21+
name: Test testable simd models
22+
runs-on: ubuntu-latest
23+
24+
steps:
25+
- name: Checkout Repository
26+
uses: actions/checkout@v4
27+
28+
- name: Run tests
29+
working-directory: testable-simd-models
30+
run: cargo test -- --test-threads=1 --nocapture
31+

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,4 @@ goto-transcoder
5656
# already existing elements were commented out
5757

5858
#/target
59+
testable-simd-models/target

README.md

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,29 +29,29 @@ These are the challenges:
2929
| [2: Verify the memory safety of core intrinsics using raw pointers](https://model-checking.github.io/verify-rust-std/challenges/0002-intrinsics-memory.html) | N/A | Open | |
3030
| [3: Verifying Raw Pointer Arithmetic Operations](https://model-checking.github.io/verify-rust-std/challenges/0003-pointer-arithmentic.html) | N/A | [Resolved](https://github.com/model-checking/verify-rust-std/pull/212) | [Kani](https://github.com/model-checking/verify-rust-std/pull/212/files) |
3131
| [4: Memory safety of BTreeMap's `btree::node` module](https://model-checking.github.io/verify-rust-std/challenges/0004-btree-node.html) | 10,000 USD | Open | |
32-
| [5: Verify functions iterating over inductive data type: `linked_list`](./challenges/0005-linked-list.md) | 5,000 USD | [Resolved](https://github.com/model-checking/verify-rust-std/pull/238) | [VeriFast](https://github.com/model-checking/verify-rust-std/tree/main/verifast-proofs/alloc/collections/linked_list.rs) |
33-
| [6: Safety of `NonNull`](./challenges/0006-nonnull.md) | N/A | [Resolved](https://github.com/model-checking/verify-rust-std/pull/247) | [Kani](https://github.com/model-checking/verify-rust-std/blob/main/library/core/src/ptr/non_null.rs) |
34-
| [7: Safety of Methods for Atomic Types & Atomic Intrinsics](./challenges/0007-atomic-types.md) | 10,000 USD | Open | |
35-
| [8: Contracts for SmallSort](./challenges/0008-smallsort.md) | 10,000 USD | Open | |
36-
| [9: Safe abstractions for `core::time::Duration`](./challenges/0009-duration.md) | N/A | [Resolved](https://github.com/model-checking/verify-rust-std/pull/136) | [Kani](https://github.com/model-checking/verify-rust-std/blob/main/library/core/src/time.rs) |
37-
| [10: Memory safety of String](./challenges/0010-string.md) | N/A | Open | |
38-
| [11: Safety of Methods for Numeric Primitive Types](./challenges/0011-floats-ints.md) | N/A | [Resolved](https://github.com/model-checking/verify-rust-std/issues/59) | [Kani](https://github.com/model-checking/verify-rust-std/tree/main/library/core/src/num) |
39-
| [12: Safety of `NonZero`](./challenges/0012-nonzero.md) | N/A | Open | |
40-
| [13: Safety of `CStr`](./challenges/0013-cstr.md) | N/A | Open | |
41-
| [14: Safety of Primitive Conversions](./challenges/0014-convert-num.md) | TBD | [Resolved](https://github.com/model-checking/verify-rust-std/pull/247) | [Kani](https://github.com/model-checking/verify-rust-std/blob/main/library/core/src/convert/num.rs) |
42-
| [15: Contracts and Tests for SIMD Intrinsics](./challenges/0015-intrinsics-simd.md) | | Open | |
43-
| [16: Verify the safety of Iterator functions](./challenges/0016-iter.md) | 10,000 USD | Open | |
44-
| [17: Verify the safety of slice functions](./challenges/0017-slice.md) | 10,000 USD | Open | |
45-
| [18: Verify the safety of slice iter functions](./challenges/0018-slice-iter.md) | 10,000 USD | Open | |
46-
| [19: Safety of `RawVec`](./challenges/0019-rawvec.md) | 10,000 USD | [Resolved](https://github.com/model-checking/verify-rust-std/pull/422) | [VeriFast](https://github.com/model-checking/verify-rust-std/tree/main/verifast-proofs/alloc/raw_vec/mod.rs) |
47-
| [20: Verify the safety of char-related functions in str::pattern](./challenges/0020-str-pattern-pt1.md) | 25,000 USD | Open | |
48-
| [21: Verify the safety of substring-related functions in str::pattern](./challenges/0021-str-pattern-pt2.md) | 25,000 USD | Open | |
49-
| [22: Verify the safety of str iter functions](./challenges/0022-str-iter.md) | 10,000 USD | Open | |
50-
| [23: Verify the safety of Vec functions part 1](./challenges/0023-vec-pt1.md) | 15,000 USD | Open | |
51-
| [24: Verify the safety of Vec functions part 2](./challenges/0024-vec-pt2.md) | 15,000 USD | Open | |
52-
| [25: Verify the safety of `VecDeque` functions](./challenges/0025-vecdeque.md) | 10,000 USD | Open | |
53-
| [26: Verify reference-counted Cell implementation](./challenges/0026-rc.md) | 10,000 USD | Open | |
54-
| [27: Verify atomically reference-counted Cell implementation](./challenges/0027-arc.md) | 10,000 USD | Open | |
32+
| [5: Verify functions iterating over inductive data type: `linked_list`](https://model-checking.github.io/verify-rust-std/challenges/0005-linked-list.html) | 5,000 USD | [Resolved](https://github.com/model-checking/verify-rust-std/pull/238) | [VeriFast](https://github.com/model-checking/verify-rust-std/tree/main/verifast-proofs/alloc/collections/linked_list.rs) |
33+
| [6: Safety of `NonNull`](https://model-checking.github.io/verify-rust-std/challenges/0006-nonnull.html) | N/A | [Resolved](https://github.com/model-checking/verify-rust-std/pull/247) | [Kani](https://github.com/model-checking/verify-rust-std/blob/main/library/core/src/ptr/non_null.rs) |
34+
| [7: Safety of Methods for Atomic Types & Atomic Intrinsics](https://model-checking.github.io/verify-rust-std/challenges/0007-atomic-types.html) | 10,000 USD | Open | |
35+
| [8: Contracts for SmallSort](https://model-checking.github.io/verify-rust-std/challenges/0008-smallsort.html) | 10,000 USD | Open | |
36+
| [9: Safe abstractions for `core::time::Duration`](https://model-checking.github.io/verify-rust-std/challenges/0009-duration.html) | N/A | [Resolved](https://github.com/model-checking/verify-rust-std/pull/136) | [Kani](https://github.com/model-checking/verify-rust-std/blob/main/library/core/src/time.rs) |
37+
| [10: Memory safety of String](https://model-checking.github.io/verify-rust-std/challenges/0010-string.html) | N/A | Open | |
38+
| [11: Safety of Methods for Numeric Primitive Types](https://model-checking.github.io/verify-rust-std/challenges/0011-floats-ints.html) | N/A | [Resolved](https://github.com/model-checking/verify-rust-std/issues/59) | [Kani](https://github.com/model-checking/verify-rust-std/tree/main/library/core/src/num) |
39+
| [12: Safety of `NonZero`](https://model-checking.github.io/verify-rust-std/challenges/0012-nonzero.html) | N/A | Open | |
40+
| [13: Safety of `CStr`](https://model-checking.github.io/verify-rust-std/challenges/0013-cstr.html) | N/A | Open | |
41+
| [14: Safety of Primitive Conversions](https://model-checking.github.io/verify-rust-std/challenges/0014-convert-num.html) | TBD | [Resolved](https://github.com/model-checking/verify-rust-std/pull/247) | [Kani](https://github.com/model-checking/verify-rust-std/blob/main/library/core/src/convert/num.rs) |
42+
| [15: Contracts and Tests for SIMD Intrinsics](https://model-checking.github.io/verify-rust-std/challenges/0015-intrinsics-simd.html) | | Open | |
43+
| [16: Verify the safety of Iterator functions](https://model-checking.github.io/verify-rust-std/challenges/0016-iter.html) | 10,000 USD | Open | |
44+
| [17: Verify the safety of slice functions](https://model-checking.github.io/verify-rust-std/challenges/0017-slice.html) | 10,000 USD | Open | |
45+
| [18: Verify the safety of slice iter functions](https://model-checking.github.io/verify-rust-std/challenges/0018-slice-iter.html) | 10,000 USD | Open | |
46+
| [19: Safety of `RawVec`](https://model-checking.github.io/verify-rust-std/challenges/0019-rawvec.html) | 10,000 USD | [Resolved](https://github.com/model-checking/verify-rust-std/pull/422) | [VeriFast](https://github.com/model-checking/verify-rust-std/tree/main/verifast-proofs/alloc/raw_vec/mod.rs) |
47+
| [20: Verify the safety of char-related functions in str::pattern](https://model-checking.github.io/verify-rust-std/challenges/0020-str-pattern-pt1.html) | 25,000 USD | Open | |
48+
| [21: Verify the safety of substring-related functions in str::pattern](https://model-checking.github.io/verify-rust-std/challenges/0021-str-pattern-pt2.html) | 25,000 USD | Open | |
49+
| [22: Verify the safety of str iter functions](https://model-checking.github.io/verify-rust-std/challenges/0022-str-iter.html) | 10,000 USD | Open | |
50+
| [23: Verify the safety of Vec functions part 1](https://model-checking.github.io/verify-rust-std/challenges/0023-vec-pt1.html) | 15,000 USD | Open | |
51+
| [24: Verify the safety of Vec functions part 2](https://model-checking.github.io/verify-rust-std/challenges/0024-vec-pt2.html) | 15,000 USD | Open | |
52+
| [25: Verify the safety of `VecDeque` functions](https://model-checking.github.io/verify-rust-std/challenges/0025-vecdeque.html) | 10,000 USD | Open | |
53+
| [26: Verify reference-counted Cell implementation](https://model-checking.github.io/verify-rust-std/challenges/0026-rc.html) | 10,000 USD | Open | |
54+
| [27: Verify atomically reference-counted Cell implementation](https://model-checking.github.io/verify-rust-std/challenges/0027-arc.html) | 10,000 USD | Open | |
5555

5656
See [our book](https://model-checking.github.io/verify-rust-std/intro.html) for more details on the challenge rules.
5757

library/core/Cargo.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,11 @@ check-cfg = [
4747

4848
[package.metadata.flux]
4949
enabled = true
50-
include = [ "src/ascii{*.rs,/**/*.rs}" ]
50+
check_overflow = "lazy"
51+
include = [ "src/ascii*.rs",
52+
"src/num/mod.rs",
53+
"src/pat.rs",
54+
"src/bstr/*.rs",
55+
"src/hash/*.rs",
56+
"src/time.rs",
57+
]

library/core/src/ascii/ascii_char.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ impl AsciiChar {
479479
/// `b` must be in `0..=127`, or else this is UB.
480480
#[unstable(feature = "ascii_char", issue = "110998")]
481481
#[inline]
482+
#[cfg_attr(flux, flux::spec(fn(b: u8{b <= 127}) -> Self))]
482483
#[requires(b <= 127)]
483484
#[ensures(|result| *result as u8 == b)]
484485
pub const unsafe fn from_u8_unchecked(b: u8) -> Self {
@@ -516,6 +517,10 @@ impl AsciiChar {
516517
/// when writing code using this method, since the implementation doesn't
517518
/// need something really specific, not to make those other arguments do
518519
/// something useful. It might be tightened before stabilization.)
520+
// Only `d < 64` is required for safety as described above, but we use `d < 10` as
521+
// in the `assert_unsafe_precondition` inside. See https://github.com/rust-lang/rust/pull/129374
522+
// for some context about the discrepancy.
523+
#[cfg_attr(flux, flux::spec(fn(d: u8{d < 10}) -> Self))]
519524
#[unstable(feature = "ascii_char", issue = "110998")]
520525
#[inline]
521526
#[track_caller]
@@ -536,8 +541,8 @@ impl AsciiChar {
536541
}
537542

538543
/// Gets this ASCII character as a byte.
544+
#[cfg_attr(flux, flux::spec(fn (Self) -> u8{v: v <= 127}))]
539545
#[unstable(feature = "ascii_char", issue = "110998")]
540-
#[cfg_attr(flux, flux::spec(fn(Self) -> u8{v: v <= 127}))]
541546
#[inline]
542547
pub const fn to_u8(self) -> u8 {
543548
self as u8

library/core/src/flux_info.rs

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,87 @@
44
/// See the following link for more information on how extensible properties for primitive operations work:
55
/// <https://flux-rs.github.io/flux/guide/specifications.html#extensible-properties-for-primitive-ops>
66
#[flux::defs {
7+
fn char_to_int(x:char) -> int {
8+
cast(x)
9+
}
10+
711
property ShiftRightByFour[>>](x, y) {
812
16 * [>>](x, 4) == x
913
}
1014
11-
property MaskBy15[&](x, y) {
12-
[&](x, y) <= y
15+
property MaskLess[&](x, y) {
16+
[&](x, y) <= x && [&](x, y) <= y
17+
}
18+
19+
property ShiftLeft[<<](n, k) {
20+
0 < k => n <= [<<](n, k)
21+
}
22+
23+
fn is_ascii_uppercase(n: int) -> bool {
24+
cast('A') <= n && n <= cast('Z')
25+
}
26+
27+
fn is_ascii_lowercase(n: int) -> bool {
28+
cast('a') <= n && n <= cast('z')
29+
}
30+
31+
fn to_ascii_uppercase(n: int) -> int {
32+
n - (cast(is_ascii_lowercase(n)) * 32)
33+
}
34+
35+
fn to_ascii_lowercase(n: int) -> int {
36+
n + (cast(is_ascii_uppercase(n)) * 32)
37+
}
38+
39+
property BitXor0[^](x, y) {
40+
(y == 0) => [^](x, y) == x
41+
}
42+
43+
property BitXor32[^](x, y) {
44+
(is_ascii_lowercase(x) && y == 32) => [^](x, y) == x - 32
45+
}
46+
47+
property BitOr0[|](x, y) {
48+
(y == 0) => [|](x, y) == x
49+
}
50+
51+
property BitOr32[|](x, y) {
52+
(is_ascii_uppercase(x) && y == 32) => [|](x, y) == x + 32
53+
}
54+
55+
}]
56+
#[flux::specs {
57+
mod hash {
58+
mod sip {
59+
struct Hasher {
60+
k0: u64,
61+
k1: u64,
62+
length: usize, // how many bytes we've processed
63+
state: State, // hash State
64+
tail: u64, // unprocessed bytes le
65+
ntail: usize{v: v <= 8}, // how many bytes in tail are valid
66+
_marker: PhantomData<S>,
67+
}
68+
}
69+
70+
impl BuildHasherDefault {
71+
#[trusted(reason="https://github.com/flux-rs/flux/issues/1185")]
72+
fn new() -> Self;
73+
}
74+
}
75+
76+
impl Hasher for hash::sip::Hasher {
77+
fn write(self: &mut Self, msg: &[u8]) ensures self: Self; // mut-ref-unfolding
78+
}
79+
80+
impl Clone for hash::BuildHasherDefault {
81+
#[trusted(reason="https://github.com/flux-rs/flux/issues/1185")]
82+
fn clone(self: &Self) -> Self;
83+
}
84+
85+
impl Debug for time::Duration {
86+
#[trusted(reason="modular arithmetic invariant inside nested fmt_decimal")]
87+
fn fmt(self: &Self, f: &mut fmt::Formatter) -> fmt::Result;
1388
}
1489
}]
1590
const _: () = {};

library/core/src/num/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ impl u8 {
503503
/// # Safety
504504
///
505505
/// This byte must be valid ASCII, or else this is UB.
506+
#[cfg_attr(flux, flux::spec(fn({&Self[@n] | n <= 127}) -> _))]
506507
#[must_use]
507508
#[unstable(feature = "ascii_char", issue = "110998")]
508509
#[inline]
@@ -533,6 +534,7 @@ impl u8 {
533534
/// ```
534535
///
535536
/// [`make_ascii_uppercase`]: Self::make_ascii_uppercase
537+
#[cfg_attr(flux, flux::spec(fn(&u8[@n]) -> u8[to_ascii_uppercase(n)]))]
536538
#[must_use = "to uppercase the value in-place, use `make_ascii_uppercase()`"]
537539
#[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
538540
#[rustc_const_stable(feature = "const_ascii_methods_on_intrinsics", since = "1.52.0")]
@@ -558,6 +560,7 @@ impl u8 {
558560
/// ```
559561
///
560562
/// [`make_ascii_lowercase`]: Self::make_ascii_lowercase
563+
#[cfg_attr(flux, flux::spec(fn(&u8[@n]) -> u8[to_ascii_lowercase(n)]))]
561564
#[must_use = "to lowercase the value in-place, use `make_ascii_lowercase()`"]
562565
#[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
563566
#[rustc_const_stable(feature = "const_ascii_methods_on_intrinsics", since = "1.52.0")]
@@ -706,6 +709,7 @@ impl u8 {
706709
/// assert!(!lf.is_ascii_uppercase());
707710
/// assert!(!esc.is_ascii_uppercase());
708711
/// ```
712+
#[cfg_attr(flux, flux::spec(fn(&Self[@n]) -> bool[is_ascii_uppercase(n)]))]
709713
#[must_use]
710714
#[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
711715
#[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")]
@@ -740,6 +744,7 @@ impl u8 {
740744
/// assert!(!lf.is_ascii_lowercase());
741745
/// assert!(!esc.is_ascii_lowercase());
742746
/// ```
747+
#[cfg_attr(flux, flux::spec(fn(&u8[@n]) -> bool[is_ascii_lowercase(n)]))]
743748
#[must_use]
744749
#[stable(feature = "ascii_ctype_on_intrinsics", since = "1.24.0")]
745750
#[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")]

library/core/src/num/niche_types.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ macro_rules! define_valid_range_type {
1414
$(#[$m:meta])*
1515
$vis:vis struct $name:ident($int:ident as $uint:ident in $low:literal..=$high:literal);
1616
)+) => {$(
17+
#[cfg_attr(flux, flux::opaque)]
18+
#[cfg_attr(flux, flux::refined_by(val: int))]
19+
#[cfg_attr(flux, flux::invariant($low <= cast(val) && cast(val) <= $high))]
1720
#[derive(Clone, Copy, Eq)]
1821
#[repr(transparent)]
1922
#[rustc_layout_scalar_valid_range_start($low)]
@@ -33,6 +36,7 @@ macro_rules! define_valid_range_type {
3336

3437
impl $name {
3538
#[inline]
39+
#[cfg_attr(flux, flux::spec(fn (val: $int) -> Option<Self[{val: cast(val)}]>))]
3640
pub const fn new(val: $int) -> Option<Self> {
3741
if (val as $uint) >= ($low as $uint) && (val as $uint) <= ($high as $uint) {
3842
// SAFETY: just checked the inclusive range
@@ -49,12 +53,14 @@ macro_rules! define_valid_range_type {
4953
/// Immediate language UB if `val` is not within the valid range for this
5054
/// type, as it violates the validity invariant.
5155
#[inline]
56+
#[cfg_attr(flux, flux::spec(fn (val: $int{ $low <= cast(val) && cast(val) <= $high }) -> Self[{val:cast(val)}]))]
5257
pub const unsafe fn new_unchecked(val: $int) -> Self {
5358
// SAFETY: Caller promised that `val` is within the valid range.
5459
unsafe { $name(val) }
5560
}
5661

5762
#[inline]
63+
#[cfg_attr(flux, flux::spec(fn (self: Self) -> $int[cast(self.val)] ensures $low <= cast(self.val) && cast(self.val) <= $high))]
5864
pub const fn as_inner(self) -> $int {
5965
// SAFETY: This is a transparent wrapper, so unwrapping it is sound
6066
// (Not using `.0` due to MCP#807.)

library/core/src/num/uint_macros.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,7 @@ macro_rules! uint_impl {
629629
without modifying the original"]
630630
#[inline(always)]
631631
#[track_caller]
632+
#[cfg_attr(flux, flux::spec(fn(self: Self, rhs: Self{self + rhs <= $MaxV}) -> Self[self + rhs]))]
632633
#[requires(!self.overflowing_add(rhs).1)]
633634
pub const unsafe fn unchecked_add(self, rhs: Self) -> Self {
634635
assert_unsafe_precondition!(

library/core/src/pat.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,22 @@ macro_rules! pattern_type {
1313
};
1414
}
1515

16+
// The Flux spec for the `trait RangePattern` below uses
17+
// [associated refinements](https://flux-rs.github.io/flux/tutorial/08-traits.html)
18+
// The `sub_one` method may only be safe for certain values,
19+
// e.g. when the value is not the "minimum of the type" as in the
20+
// case of the `char` implementation below. To specify this precondition generically
21+
// 1. at the trait level, we introduce the predicate `sub_ok`
22+
// which characterizes the "valid" values at which `sub_one`
23+
// can be safely called, and by default, specify this predicate
24+
// is "true";
25+
// 2. at the impl level, we can provide a type-specific implementation
26+
// of `sub_ok` that permits the verification of the impl for that type.
27+
1628
/// A trait implemented for integer types and `char`.
1729
/// Useful in the future for generic pattern types, but
1830
/// used right now to simplify ast lowering of pattern type ranges.
31+
#[cfg_attr(flux, flux::assoc(fn sub_ok(self: Self) -> bool { true }))]
1932
#[unstable(feature = "pattern_type_range_trait", issue = "123646")]
2033
#[rustc_const_unstable(feature = "pattern_type_range_trait", issue = "123646")]
2134
#[const_trait]
@@ -33,6 +46,7 @@ pub trait RangePattern {
3346
const MAX: Self;
3447

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

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

6885
const MAX: Self = char::MAX;
6986

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

0 commit comments

Comments
 (0)