Skip to content

Commit bbfcff3

Browse files
committed
Port all viable contracts from verify-rust-std
Ports over all contracts (other than those for `Alignment`, see the separate PR) that can be expressed using the current, experimental contracts syntax. (Notably, this excludes all contracts that refer to pointer validity.)
1 parent 772f380 commit bbfcff3

Some content is hidden

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

47 files changed

+755
-0
lines changed

library/alloc/src/collections/vec_deque/iter.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ impl<'a, T> Iterator for Iter<'a, T> {
144144
}
145145

146146
#[inline]
147+
#[allow(unused_parens)]
148+
#[core::contracts::requires(idx < self.len())]
147149
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
148150
// Safety: The TrustedRandomAccess contract requires that callers only pass an index
149151
// that is in bounds.

library/alloc/src/collections/vec_deque/iter_mut.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ impl<'a, T> Iterator for IterMut<'a, T> {
208208
}
209209

210210
#[inline]
211+
#[allow(unused_parens)]
212+
#[core::contracts::requires(idx < self.len())]
211213
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
212214
// Safety: The TrustedRandomAccess contract requires that callers only pass an index
213215
// that is in bounds.

library/alloc/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
#![feature(const_default)]
105105
#![feature(const_eval_select)]
106106
#![feature(const_heap)]
107+
#![feature(contracts)]
107108
#![feature(core_intrinsics)]
108109
#![feature(deprecated_suggestion)]
109110
#![feature(deref_pure_trait)]

library/alloc/src/vec/into_iter.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,8 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
354354
R::from_output(accum)
355355
}
356356

357+
#[allow(unused_parens)]
358+
#[core::contracts::requires(i < self.len())]
357359
unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> Self::Item
358360
where
359361
Self: TrustedRandomAccessNoCoerce,

library/core/src/alloc/layout.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ impl Layout {
6666
#[stable(feature = "alloc_layout", since = "1.28.0")]
6767
#[rustc_const_stable(feature = "const_alloc_layout_size_align", since = "1.50.0")]
6868
#[inline]
69+
#[rustc_allow_const_fn_unstable(contracts)]
70+
#[core::contracts::ensures(
71+
move |result: &Result<Self, LayoutError>|
72+
result.is_err() || (
73+
align.is_power_of_two() &&
74+
size <= isize::MAX as usize - (align - 1) &&
75+
result.as_ref().unwrap().size() == size &&
76+
result.as_ref().unwrap().align() == align))]
6977
pub const fn from_size_align(size: usize, align: usize) -> Result<Self, LayoutError> {
7078
if Layout::is_size_align_valid(size, align) {
7179
// SAFETY: Layout::is_size_align_valid checks the preconditions for this call.
@@ -127,6 +135,11 @@ impl Layout {
127135
#[must_use]
128136
#[inline]
129137
#[track_caller]
138+
#[rustc_allow_const_fn_unstable(contracts)]
139+
#[allow(unused_parens)]
140+
#[core::contracts::requires(Layout::from_size_align(size, align).is_ok())]
141+
#[core::contracts::ensures(
142+
move |result: &Self| result.size() == size && result.align() == align)]
130143
pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self {
131144
assert_unsafe_precondition!(
132145
check_library_ub,
@@ -167,6 +180,10 @@ impl Layout {
167180
#[rustc_const_stable(feature = "alloc_layout_const_new", since = "1.42.0")]
168181
#[must_use]
169182
#[inline]
183+
#[rustc_allow_const_fn_unstable(contracts)]
184+
#[core::contracts::ensures(
185+
|result: &Self|
186+
result.size() == mem::size_of::<T>() && result.align() == mem::align_of::<T>())]
170187
pub const fn new<T>() -> Self {
171188
let (size, align) = size_align::<T>();
172189
// SAFETY: if the type is instantiated, rustc already ensures that its
@@ -182,6 +199,11 @@ impl Layout {
182199
#[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")]
183200
#[must_use]
184201
#[inline]
202+
#[rustc_allow_const_fn_unstable(contracts)]
203+
#[allow(unused_parens)]
204+
#[core::contracts::requires(mem::align_of_val(t).is_power_of_two())]
205+
// FIXME: requires `&self` to be `'static`
206+
// #[core::contracts::ensures(move |result: &Self| result.align() == mem::align_of_val(t))]
185207
pub const fn for_value<T: ?Sized>(t: &T) -> Self {
186208
let (size, align) = (size_of_val(t), align_of_val(t));
187209
// SAFETY: see rationale in `new` for why this is using the unsafe variant
@@ -217,6 +239,8 @@ impl Layout {
217239
/// [extern type]: ../../unstable-book/language-features/extern-types.html
218240
#[unstable(feature = "layout_for_ptr", issue = "69835")]
219241
#[must_use]
242+
#[rustc_allow_const_fn_unstable(contracts)]
243+
#[core::contracts::ensures(|result: &Self| result.align().is_power_of_two())]
220244
pub const unsafe fn for_value_raw<T: ?Sized>(t: *const T) -> Self {
221245
// SAFETY: we pass along the prerequisites of these functions to the caller
222246
let (size, align) = unsafe { (mem::size_of_val_raw(t), mem::align_of_val_raw(t)) };
@@ -233,6 +257,8 @@ impl Layout {
233257
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
234258
#[must_use]
235259
#[inline]
260+
#[rustc_allow_const_fn_unstable(contracts)]
261+
#[core::contracts::ensures(|result: &NonNull<u8>| result.is_aligned())]
236262
pub const fn dangling(&self) -> NonNull<u8> {
237263
NonNull::without_provenance(self.align.as_nonzero())
238264
}
@@ -254,6 +280,12 @@ impl Layout {
254280
#[stable(feature = "alloc_layout_manipulation", since = "1.44.0")]
255281
#[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")]
256282
#[inline]
283+
#[rustc_allow_const_fn_unstable(contracts)]
284+
#[core::contracts::ensures(
285+
move |result: &Result<Self, LayoutError>|
286+
result.is_err() || (
287+
result.as_ref().unwrap().align() >= align &&
288+
result.as_ref().unwrap().align().is_power_of_two()))]
257289
pub const fn align_to(&self, align: usize) -> Result<Self, LayoutError> {
258290
if let Some(align) = Alignment::new(align) {
259291
Layout::from_size_alignment(self.size, Alignment::max(self.align, align))
@@ -282,6 +314,8 @@ impl Layout {
282314
#[must_use = "this returns the padding needed, \
283315
without modifying the `Layout`"]
284316
#[inline]
317+
#[rustc_allow_const_fn_unstable(contracts)]
318+
#[core::contracts::ensures(move |result| *result <= align)]
285319
pub const fn padding_needed_for(&self, align: usize) -> usize {
286320
// FIXME: Can we just change the type on this to `Alignment`?
287321
let Some(align) = Alignment::new(align) else { return usize::MAX };
@@ -331,6 +365,14 @@ impl Layout {
331365
#[must_use = "this returns a new `Layout`, \
332366
without modifying the original"]
333367
#[inline]
368+
// FIXME: requires `&self` to be `'static`
369+
// #[rustc_allow_const_fn_unstable(contracts)]
370+
// #[core::contracts::ensures(
371+
// move |result: &Layout|
372+
// result.size() >= self.size() &&
373+
// result.align() == self.align() &&
374+
// result.size() % result.align() == 0 &&
375+
// self.size() + self.padding_needed_for(self.align()) == result.size())]
334376
pub const fn pad_to_align(&self) -> Layout {
335377
// This cannot overflow. Quoting from the invariant of Layout:
336378
// > `size`, when rounded up to the nearest multiple of `align`,
@@ -371,6 +413,12 @@ impl Layout {
371413
/// ```
372414
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
373415
#[inline]
416+
#[rustc_allow_const_fn_unstable(contracts)]
417+
#[core::contracts::ensures(
418+
move |result: &Result<(Self, usize), LayoutError>|
419+
result.is_err() || (
420+
(n == 0 || result.as_ref().unwrap().0.size() % n == 0) &&
421+
result.as_ref().unwrap().0.size() == n * result.as_ref().unwrap().1))]
374422
pub const fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutError> {
375423
let padded = self.pad_to_align();
376424
if let Ok(repeated) = padded.repeat_packed(n) {
@@ -428,6 +476,15 @@ impl Layout {
428476
#[stable(feature = "alloc_layout_manipulation", since = "1.44.0")]
429477
#[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")]
430478
#[inline]
479+
// FIXME: requires `&self` to be `'static`
480+
// #[rustc_allow_const_fn_unstable(contracts)]
481+
// #[core::contracts::ensures(
482+
// move |result: &Result<(Self, usize), LayoutError>|
483+
// result.is_err() || (
484+
// result.as_ref().unwrap().0.align() == cmp::max(self.align(), next.align()) &&
485+
// result.as_ref().unwrap().0.size() >= self.size() + next.size() &&
486+
// result.as_ref().unwrap().1 >= self.size() &&
487+
// result.as_ref().unwrap().1 <= result.as_ref().unwrap().0.size()))]
431488
pub const fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError> {
432489
let new_align = Alignment::max(self.align, next.align);
433490
let offset = self.size_rounded_up_to_custom_align(next.align);
@@ -459,6 +516,13 @@ impl Layout {
459516
/// On arithmetic overflow, returns `LayoutError`.
460517
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
461518
#[inline]
519+
// FIXME: requires `&self` to be `'static`
520+
// #[rustc_allow_const_fn_unstable(contracts)]
521+
// #[core::contracts::ensures(
522+
// move |result: &Result<Self, LayoutError>|
523+
// result.is_err() || (
524+
// result.as_ref().unwrap().size() == n * self.size() &&
525+
// result.as_ref().unwrap().align() == self.align()))]
462526
pub const fn repeat_packed(&self, n: usize) -> Result<Self, LayoutError> {
463527
if let Some(size) = self.size.checked_mul(n) {
464528
// The safe constructor is called here to enforce the isize size limit.
@@ -476,6 +540,13 @@ impl Layout {
476540
/// On arithmetic overflow, returns `LayoutError`.
477541
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
478542
#[inline]
543+
// FIXME: requires `&self` to be `'static`
544+
// #[rustc_allow_const_fn_unstable(contracts)]
545+
// #[core::contracts::ensures(
546+
// move |result: &Result<Self, LayoutError>|
547+
// result.is_err() || (
548+
// result.as_ref().unwrap().size() == self.size() + next.size() &&
549+
// result.as_ref().unwrap().align() == self.align()))]
479550
pub const fn extend_packed(&self, next: Self) -> Result<Self, LayoutError> {
480551
// SAFETY: each `size` is at most `isize::MAX == usize::MAX/2`, so the
481552
// sum is at most `usize::MAX/2*2 == usize::MAX - 1`, and cannot overflow.
@@ -491,6 +562,12 @@ impl Layout {
491562
#[stable(feature = "alloc_layout_manipulation", since = "1.44.0")]
492563
#[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")]
493564
#[inline]
565+
#[rustc_allow_const_fn_unstable(contracts)]
566+
#[core::contracts::ensures(
567+
move |result: &Result<Self, LayoutError>|
568+
result.is_err() || (
569+
result.as_ref().unwrap().size() == n * mem::size_of::<T>() &&
570+
result.as_ref().unwrap().align() == mem::align_of::<T>()))]
494571
pub const fn array<T>(n: usize) -> Result<Self, LayoutError> {
495572
// Reduce the amount of code we need to monomorphize per `T`.
496573
return inner(T::LAYOUT, n);

library/core/src/array/iter.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ impl<T, const N: usize> IntoIter<T, N> {
138138
/// ```
139139
#[unstable(feature = "array_into_iter_constructors", issue = "91583")]
140140
#[inline]
141+
#[rustc_allow_const_fn_unstable(contracts)]
142+
#[allow(unused_parens)]
143+
#[core::contracts::requires(initialized.start <= initialized.end && initialized.end <= N)]
141144
pub const unsafe fn new_unchecked(
142145
buffer: [MaybeUninit<T>; N],
143146
initialized: Range<usize>,
@@ -279,6 +282,8 @@ impl<T, const N: usize> Iterator for IntoIter<T, N> {
279282
}
280283

281284
#[inline]
285+
#[allow(unused_parens)]
286+
#[core::contracts::requires(idx < self.len())]
282287
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
283288
// SAFETY: The caller must provide an idx that is in bound of the remainder.
284289
let elem_ref = unsafe { self.as_mut_slice().get_unchecked_mut(idx) };

library/core/src/ascii/ascii_char.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,10 @@ impl AsciiChar {
458458
/// or returns `None` if it's too large.
459459
#[unstable(feature = "ascii_char", issue = "110998")]
460460
#[inline]
461+
#[rustc_allow_const_fn_unstable(contracts)]
462+
#[core::contracts::ensures(
463+
move |result: &Option<AsciiChar>|
464+
(b <= 127) == (result.is_some() && result.unwrap() as u8 == b))]
461465
pub const fn from_u8(b: u8) -> Option<Self> {
462466
if b <= 127 {
463467
// SAFETY: Just checked that `b` is in-range
@@ -475,6 +479,10 @@ impl AsciiChar {
475479
/// `b` must be in `0..=127`, or else this is UB.
476480
#[unstable(feature = "ascii_char", issue = "110998")]
477481
#[inline]
482+
#[rustc_allow_const_fn_unstable(contracts)]
483+
#[allow(unused_parens)]
484+
#[core::contracts::requires(b <= 127)]
485+
#[core::contracts::ensures(move |result: &Self| *result as u8 == b)]
478486
pub const unsafe fn from_u8_unchecked(b: u8) -> Self {
479487
// SAFETY: Our safety precondition is that `b` is in-range.
480488
unsafe { transmute(b) }
@@ -513,6 +521,12 @@ impl AsciiChar {
513521
#[unstable(feature = "ascii_char", issue = "110998")]
514522
#[inline]
515523
#[track_caller]
524+
// Only `d < 64` is required for safety as described above, but we use `d < 10` as in the
525+
// `assert_unsafe_precondition` inside. See https://github.com/rust-lang/rust/pull/129374 for
526+
// some context about the discrepancy.
527+
#[rustc_allow_const_fn_unstable(contracts)]
528+
#[allow(unused_parens)]
529+
#[core::contracts::requires(d < 10)]
516530
pub const unsafe fn digit_unchecked(d: u8) -> Self {
517531
assert_unsafe_precondition!(
518532
check_library_ub,
@@ -532,6 +546,8 @@ impl AsciiChar {
532546
/// Gets this ASCII character as a byte.
533547
#[unstable(feature = "ascii_char", issue = "110998")]
534548
#[inline]
549+
#[rustc_allow_const_fn_unstable(contracts)]
550+
#[core::contracts::ensures(|result: &u8| *result <= 127)]
535551
pub const fn to_u8(self) -> u8 {
536552
self as u8
537553
}

library/core/src/char/convert.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ pub(super) const fn from_u32(i: u32) -> Option<char> {
2323
#[must_use]
2424
#[allow(unnecessary_transmutes)]
2525
#[track_caller]
26+
#[rustc_allow_const_fn_unstable(contracts)]
27+
#[allow(unused_parens)]
28+
#[core::contracts::requires(char_try_from_u32(i).is_ok())]
29+
#[core::contracts::ensures(move |result: &char| *result as u32 == i)]
2630
pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char {
2731
// SAFETY: the caller must guarantee that `i` is a valid char value.
2832
unsafe {

library/core/src/char/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ pub const fn from_u32(i: u32) -> Option<char> {
138138
#[rustc_const_stable(feature = "const_char_from_u32_unchecked", since = "1.81.0")]
139139
#[must_use]
140140
#[inline]
141+
#[rustc_allow_const_fn_unstable(contracts)]
142+
#[allow(unused_parens)]
143+
#[core::contracts::requires(i <= 0x10FFFF && (i < 0xD800 || i > 0xDFFF))]
141144
pub const unsafe fn from_u32_unchecked(i: u32) -> char {
142145
// SAFETY: the safety contract must be upheld by the caller.
143146
unsafe { self::convert::from_u32_unchecked(i) }
@@ -399,6 +402,7 @@ macro_rules! casemappingiter_impls {
399402
self.0.advance_by(n)
400403
}
401404

405+
#[core::contracts::requires(idx < self.0.len())]
402406
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
403407
// SAFETY: just forwarding requirements to caller
404408
unsafe { self.0.__iterator_get_unchecked(idx) }
@@ -533,6 +537,8 @@ impl Iterator for CaseMappingIter {
533537
self.0.advance_by(n)
534538
}
535539

540+
#[allow(unused_parens)]
541+
#[core::contracts::requires(idx < self.len())]
536542
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
537543
// SAFETY: just forwarding requirements to caller
538544
unsafe { self.0.__iterator_get_unchecked(idx) }

library/core/src/ffi/c_str.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,14 @@ impl CStr {
250250
#[must_use]
251251
#[stable(feature = "rust1", since = "1.0.0")]
252252
#[rustc_const_stable(feature = "const_cstr_from_ptr", since = "1.81.0")]
253+
#[rustc_allow_const_fn_unstable(contracts)]
254+
#[allow(unused_parens)]
255+
#[core::contracts::requires(!ptr.is_null())]
256+
#[core::contracts::ensures(
257+
|result: &&CStr|
258+
!result.inner.is_empty() &&
259+
result.inner[result.inner.len() - 1] == 0 &&
260+
!result.inner[..result.inner.len() - 1].contains(&0))]
253261
pub const unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr {
254262
// SAFETY: The caller has provided a pointer that points to a valid C
255263
// string with a NUL terminator less than `isize::MAX` from `ptr`.
@@ -385,6 +393,15 @@ impl CStr {
385393
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
386394
#[rustc_const_stable(feature = "const_cstr_unchecked", since = "1.59.0")]
387395
#[rustc_allow_const_fn_unstable(const_eval_select)]
396+
#[rustc_allow_const_fn_unstable(contracts)]
397+
#[allow(unused_parens)]
398+
#[core::contracts::requires(
399+
!bytes.is_empty() && bytes[bytes.len() - 1] == 0 && !bytes[..bytes.len()-1].contains(&0))]
400+
#[core::contracts::ensures(
401+
|result: &&CStr|
402+
!result.inner.is_empty() &&
403+
result.inner[result.inner.len() - 1] == 0 &&
404+
!result.inner[..result.inner.len() - 1].contains(&0))]
388405
pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr {
389406
const_eval_select!(
390407
@capture { bytes: &[u8] } -> &CStr:
@@ -723,6 +740,12 @@ impl const AsRef<CStr> for CStr {
723740
#[inline]
724741
#[unstable(feature = "cstr_internals", issue = "none")]
725742
#[rustc_allow_const_fn_unstable(const_eval_select)]
743+
#[rustc_allow_const_fn_unstable(contracts)]
744+
#[core::contracts::ensures(
745+
move |&result|
746+
result < isize::MAX as usize &&
747+
// SAFETY: result is within isize::MAX
748+
unsafe { *ptr.add(result) } == 0)]
726749
const unsafe fn strlen(ptr: *const c_char) -> usize {
727750
const_eval_select!(
728751
@capture { s: *const c_char = ptr } -> usize:

0 commit comments

Comments
 (0)