Skip to content

Commit 32115e8

Browse files
Add safety preconditions to core/src/iter/range.rs (#331)
For the signed and unsigned methods in the macros, I added contracts that require the checked operations to succeed, which is what the safety comments are stating. These contracts formalize the safety requirements that were previously only documented in comments. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Carolyn Zech <carolynzech@gmail.com>
1 parent a4edf5f commit 32115e8

File tree

2 files changed

+30
-0
lines changed

2 files changed

+30
-0
lines changed

.github/workflows/kani.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ jobs:
102102
scripts/run-kani.sh --run autoharness --kani-args \
103103
--include-pattern "<(.+)[[:space:]]as[[:space:]](.+)>::disjoint_bitor" \
104104
--include-pattern "<(.+)[[:space:]]as[[:space:]](.+)>::unchecked_disjoint_bitor" \
105+
--include-pattern "<(.+)[[:space:]]as[[:space:]]iter::range::Step>::backward_unchecked" \
106+
--include-pattern "<(.+)[[:space:]]as[[:space:]]iter::range::Step>::forward_unchecked" \
105107
--include-pattern alloc::__default_lib_allocator:: \
106108
--include-pattern alloc::layout::Layout::from_size_align \
107109
--include-pattern ascii::ascii_char::AsciiChar::from_u8 \

library/core/src/iter/range.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
use safety::requires;
2+
13
use super::{
24
FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce, TrustedStep,
35
};
46
use crate::ascii::Char as AsciiChar;
7+
#[cfg(kani)]
8+
use crate::kani;
59
use crate::mem;
610
use crate::net::{Ipv4Addr, Ipv6Addr};
711
use crate::num::NonZero;
@@ -184,12 +188,14 @@ pub trait Step: Clone + PartialOrd + Sized {
184188
// than the signed::MAX value. Therefore `as` casting to the signed type would be incorrect.
185189
macro_rules! step_signed_methods {
186190
($unsigned: ty) => {
191+
#[requires(start.checked_add_unsigned(n as $unsigned).is_some())]
187192
#[inline]
188193
unsafe fn forward_unchecked(start: Self, n: usize) -> Self {
189194
// SAFETY: the caller has to guarantee that `start + n` doesn't overflow.
190195
unsafe { start.checked_add_unsigned(n as $unsigned).unwrap_unchecked() }
191196
}
192197

198+
#[requires(start.checked_sub_unsigned(n as $unsigned).is_some())]
193199
#[inline]
194200
unsafe fn backward_unchecked(start: Self, n: usize) -> Self {
195201
// SAFETY: the caller has to guarantee that `start - n` doesn't overflow.
@@ -200,12 +206,14 @@ macro_rules! step_signed_methods {
200206

201207
macro_rules! step_unsigned_methods {
202208
() => {
209+
#[requires(start.checked_add(n as Self).is_some())]
203210
#[inline]
204211
unsafe fn forward_unchecked(start: Self, n: usize) -> Self {
205212
// SAFETY: the caller has to guarantee that `start + n` doesn't overflow.
206213
unsafe { start.unchecked_add(n as Self) }
207214
}
208215

216+
#[requires(start >= (n as Self))]
209217
#[inline]
210218
unsafe fn backward_unchecked(start: Self, n: usize) -> Self {
211219
// SAFETY: the caller has to guarantee that `start - n` doesn't overflow.
@@ -495,6 +503,13 @@ impl Step for char {
495503
Some(unsafe { char::from_u32_unchecked(res) })
496504
}
497505

506+
#[requires({
507+
(start as u32).checked_add(count as u32).is_some_and(|dist|
508+
(start as u32) >= 0xD800 ||
509+
dist < 0xD800 ||
510+
dist.checked_add(0x800).is_some()
511+
)
512+
})]
498513
#[inline]
499514
unsafe fn forward_unchecked(start: char, count: usize) -> char {
500515
let start = start as u32;
@@ -511,6 +526,13 @@ impl Step for char {
511526
unsafe { char::from_u32_unchecked(res) }
512527
}
513528

529+
#[requires({
530+
(start as u32).checked_sub(count as u32).is_some_and(|dist|
531+
(start as u32) < 0xE000 ||
532+
dist >= 0xE000 ||
533+
dist.checked_sub(0x800).is_some()
534+
)
535+
})]
514536
#[inline]
515537
unsafe fn backward_unchecked(start: char, count: usize) -> char {
516538
let start = start as u32;
@@ -549,6 +571,7 @@ impl Step for AsciiChar {
549571
Some(unsafe { AsciiChar::from_u8_unchecked(end) })
550572
}
551573

574+
#[requires(count < 256 && start.to_u8().checked_add(count as u8).is_some())]
552575
#[inline]
553576
unsafe fn forward_unchecked(start: AsciiChar, count: usize) -> AsciiChar {
554577
// SAFETY: Caller asserts that result is a valid ASCII character,
@@ -559,6 +582,7 @@ impl Step for AsciiChar {
559582
unsafe { AsciiChar::from_u8_unchecked(end) }
560583
}
561584

585+
#[requires(count < 256 && start.to_u8().checked_sub(count as u8).is_some())]
562586
#[inline]
563587
unsafe fn backward_unchecked(start: AsciiChar, count: usize) -> AsciiChar {
564588
// SAFETY: Caller asserts that result is a valid ASCII character,
@@ -587,13 +611,15 @@ impl Step for Ipv4Addr {
587611
u32::backward_checked(start.to_bits(), count).map(Ipv4Addr::from_bits)
588612
}
589613

614+
#[requires(start.to_bits().checked_add(count as u32).is_some())]
590615
#[inline]
591616
unsafe fn forward_unchecked(start: Ipv4Addr, count: usize) -> Ipv4Addr {
592617
// SAFETY: Since u32 and Ipv4Addr are losslessly convertible,
593618
// this is as safe as the u32 version.
594619
Ipv4Addr::from_bits(unsafe { u32::forward_unchecked(start.to_bits(), count) })
595620
}
596621

622+
#[requires(start.to_bits().checked_sub(count as u32).is_some())]
597623
#[inline]
598624
unsafe fn backward_unchecked(start: Ipv4Addr, count: usize) -> Ipv4Addr {
599625
// SAFETY: Since u32 and Ipv4Addr are losslessly convertible,
@@ -619,13 +645,15 @@ impl Step for Ipv6Addr {
619645
u128::backward_checked(start.to_bits(), count).map(Ipv6Addr::from_bits)
620646
}
621647

648+
#[requires(start.to_bits().checked_add(count as u128).is_some())]
622649
#[inline]
623650
unsafe fn forward_unchecked(start: Ipv6Addr, count: usize) -> Ipv6Addr {
624651
// SAFETY: Since u128 and Ipv6Addr are losslessly convertible,
625652
// this is as safe as the u128 version.
626653
Ipv6Addr::from_bits(unsafe { u128::forward_unchecked(start.to_bits(), count) })
627654
}
628655

656+
#[requires(start.to_bits().checked_sub(count as u128).is_some())]
629657
#[inline]
630658
unsafe fn backward_unchecked(start: Ipv6Addr, count: usize) -> Ipv6Addr {
631659
// SAFETY: Since u128 and Ipv6Addr are losslessly convertible,

0 commit comments

Comments
 (0)