Skip to content

Commit 525c2f0

Browse files
vonakaFedor Ryabinintautschnigfeliperodri
authored
LLM-generated contracts (#473)
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: Fedor Ryabinin <[email protected]> Co-authored-by: Michael Tautschnig <[email protected]> Co-authored-by: Felipe R. Monteiro <[email protected]>
1 parent 28ab0a2 commit 525c2f0

File tree

7 files changed

+68
-0
lines changed

7 files changed

+68
-0
lines changed

library/alloctests/tests/sort/known_good_stable_sort.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,18 @@
44
//
55
// Based on https://github.com/voultapher/tiny-sort-rs.
66

7+
#![feature(ub_checks)]
78
use alloc::alloc::{Layout, alloc, dealloc};
9+
#[cfg(kani)]
10+
#[unstable(feature = "kani", issue = "none")]
11+
use core::kani;
12+
#[allow(unused_imports)]
13+
#[unstable(feature = "ub_checks", issue = "none")]
14+
use core::ub_checks::*;
815
use std::ptr;
916

17+
use safety::requires;
18+
1019
/// Sort `v` preserving initial order of equal elements.
1120
///
1221
/// - Guaranteed O(N * log(N)) worst case perf
@@ -48,6 +57,7 @@ fn stable_sort<T, F: FnMut(&T, &T) -> bool>(v: &mut [T], mut is_less: F) {
4857
///
4958
/// SAFETY: The caller has to ensure that len is > 0 and that T is not a ZST.
5059
#[inline(never)]
60+
#[requires(len > 0 && size_of::<T>() > 0)]
5161
unsafe fn mergesort_main<T, F: FnMut(&T, &T) -> bool>(v: &mut [T], is_less: &mut F) {
5262
// While it would be nice to have a merge implementation that only requires N / 2 auxiliary
5363
// memory. Doing so would make the merge implementation significantly more complex and
@@ -66,6 +76,7 @@ unsafe fn mergesort_main<T, F: FnMut(&T, &T) -> bool>(v: &mut [T], is_less: &mut
6676
///
6777
/// Buffer as pointed to by `scratch` must have space for `v.len()` writes. And must not alias `v`.
6878
#[inline(always)]
79+
#[requires(can_dereference(scratch_ptr) && !same_allocation(scratch_ptr, v.as_ptr()))]
6980
unsafe fn mergesort_core<T, F: FnMut(&T, &T) -> bool>(
7081
v: &mut [T],
7182
scratch_ptr: *mut T,
@@ -97,6 +108,7 @@ unsafe fn mergesort_core<T, F: FnMut(&T, &T) -> bool>(
97108
/// SAFETY: The caller must ensure that `scratch_ptr` is valid for `v.len()` writes. And that mid is
98109
/// in-bounds.
99110
#[inline(always)]
111+
#[requires(mid <= len && can_dereference(scratch_ptr) && !same_allocation(scratch_ptr, v.as_ptr()))]
100112
unsafe fn merge<T, F>(v: &mut [T], scratch_ptr: *mut T, is_less: &mut F, mid: usize)
101113
where
102114
F: FnMut(&T, &T) -> bool,
@@ -143,6 +155,7 @@ where
143155
}
144156

145157
// SAFETY: The caller has to ensure that Option is Some, UB otherwise.
158+
#[requires(opt_val.is_some())]
146159
unsafe fn unwrap_unchecked<T>(opt_val: Option<T>) -> T {
147160
match opt_val {
148161
Some(val) => val,
@@ -165,6 +178,7 @@ struct BufGuard<T> {
165178

166179
impl<T> BufGuard<T> {
167180
// SAFETY: The caller has to ensure that len is not 0 and that T is not a ZST.
181+
#[requires(len > 0 && size_of::<T>() > 0)]
168182
unsafe fn new(len: usize) -> Self {
169183
debug_assert!(len > 0 && size_of::<T>() > 0);
170184

library/core/src/array/ascii.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
use safety::requires;
2+
13
use crate::ascii;
4+
#[cfg(kani)]
5+
use crate::kani;
26

37
impl<const N: usize> [u8; N] {
48
/// Converts this array of bytes into an array of ASCII characters,
@@ -36,10 +40,33 @@ impl<const N: usize> [u8; N] {
3640
#[unstable(feature = "ascii_char", issue = "110998")]
3741
#[must_use]
3842
#[inline]
43+
#[requires(self.iter().all(|&b| b <= 127))]
3944
pub const unsafe fn as_ascii_unchecked(&self) -> &[ascii::Char; N] {
4045
let byte_ptr: *const [u8; N] = self;
4146
let ascii_ptr = byte_ptr as *const [ascii::Char; N];
4247
// SAFETY: The caller promised all the bytes are ASCII
4348
unsafe { &*ascii_ptr }
4449
}
4550
}
51+
#[cfg(kani)]
52+
mod verify {
53+
use super::*;
54+
55+
#[cfg(kani)]
56+
#[kani::proof_for_contract(<[u8; 4]>::as_ascii_unchecked)]
57+
#[kani::unwind(10)]
58+
fn proof_for_as_ascii_unchecked() {
59+
// Create a small array of bytes that are all ASCII (0-127)
60+
let mut bytes = [0u8; 4];
61+
62+
// Fill the array with arbitrary ASCII bytes
63+
for i in 0..4 {
64+
bytes[i] = kani::any::<u8>() & 0x7F; // Ensure value is in range 0-127
65+
}
66+
67+
// The precondition self.iter().all(|&b| b <= 127) is automatically assumed by Kani
68+
69+
// Call the function - if preconditions are met, this should not cause undefined behavior
70+
unsafe { bytes.as_ascii_unchecked() };
71+
}
72+
}

library/core/src/array/drain.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
use safety::requires;
2+
13
use crate::iter::{TrustedLen, UncheckedIterator};
4+
#[cfg(kani)]
5+
use crate::kani;
26
use crate::mem::ManuallyDrop;
37
use crate::ptr::drop_in_place;
48
use crate::slice;
@@ -66,6 +70,7 @@ impl<T> ExactSizeIterator for Drain<'_, T> {
6670
unsafe impl<T> TrustedLen for Drain<'_, T> {}
6771

6872
impl<T> UncheckedIterator for Drain<'_, T> {
73+
#[requires(self.0.len() > 0)]
6974
unsafe fn next_unchecked(&mut self) -> T {
7075
// SAFETY: `Drain` is 1:1 with the inner iterator, so if the caller promised
7176
// that there's an element left, the inner iterator has one too.

library/core/src/fmt/rt.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@
66
//!
77
//! Do not modify them without understanding the consequences for the format_args!() macro.
88
9+
use safety::requires;
10+
911
use super::*;
1012
use crate::hint::unreachable_unchecked;
13+
#[cfg(kani)]
14+
use crate::kani;
1115
use crate::ptr::NonNull;
16+
use crate::ub_checks::Invariant;
1217

1318
#[lang = "format_placeholder"]
1419
#[derive(Copy, Clone)]
@@ -161,6 +166,7 @@ impl Argument<'_> {
161166
///
162167
/// This argument must actually be a placeholder argument.
163168
#[inline]
169+
#[requires(matches!(self.ty, ArgumentType::Placeholder { .. }))]
164170
pub(super) unsafe fn fmt(&self, f: &mut Formatter<'_>) -> Result {
165171
match self.ty {
166172
// SAFETY:
@@ -228,6 +234,7 @@ impl<'a> Arguments<'a> {
228234
/// const _: () = if false { panic!("a {:1}", "a") };
229235
/// ```
230236
#[inline]
237+
#[requires(pieces.len() >= fmt.len())]
231238
pub unsafe fn new_v1_formatted(
232239
pieces: &'a [&'static str],
233240
args: &'a [rt::Argument<'a>],

library/core/src/iter/adapters/map.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ where
204204
I: UncheckedIterator,
205205
F: FnMut(I::Item) -> B,
206206
{
207+
#[requires(self.iter.size_hint().0 > 0)]
207208
unsafe fn next_unchecked(&mut self) -> B {
208209
// SAFETY: `Map` is 1:1 with the inner iterator, so if the caller promised
209210
// that there's an element left, the inner iterator has one too.

library/core/src/iter/adapters/map_windows.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::iter::FusedIterator;
22
use crate::mem::MaybeUninit;
3+
use crate::ub_checks::Invariant;
34
use crate::{fmt, ptr};
45

56
/// An iterator over the mapped windows of another iterator.
@@ -289,3 +290,10 @@ where
289290
Self { f: self.f.clone(), inner: self.inner.clone() }
290291
}
291292
}
293+
294+
#[unstable(feature = "ub_checks", issue = "none")]
295+
impl<T, const N: usize> Invariant for Buffer<T, N> {
296+
fn is_safe(&self) -> bool {
297+
self.start + N <= 2 * N
298+
}
299+
}

library/core/src/iter/traits/unchecked_iterator.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
use safety::requires;
2+
13
use crate::iter::TrustedLen;
4+
#[cfg(kani)]
5+
use crate::kani;
26

37
/// [`TrustedLen`] cannot have methods, so this allows augmenting it.
48
///
@@ -27,6 +31,8 @@ pub(crate) trait UncheckedIterator: TrustedLen {
2731
/// point you might want to implement this manually instead.
2832
#[unstable(feature = "trusted_len_next_unchecked", issue = "37572")]
2933
#[inline]
34+
#[requires(self.size_hint().0 > 0)]
35+
#[cfg_attr(kani, kani::modifies(self))]
3036
unsafe fn next_unchecked(&mut self) -> Self::Item {
3137
let opt = self.next();
3238
// SAFETY: The caller promised that we're not empty, and

0 commit comments

Comments
 (0)