Skip to main content

vstd/
string.rs

1#![feature(rustc_attrs)]
2#![allow(unused_imports)]
3
4#[cfg(all(feature = "alloc", not(verus_verify_core)))]
5use alloc::str::Chars;
6#[cfg(all(feature = "alloc", not(verus_verify_core)))]
7use alloc::string::{self, String, ToString};
8#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
9use core::ops::{Bound, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
10#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
11use core::slice::SliceIndex;
12
13use super::prelude::*;
14use super::seq::Seq;
15use super::slice::*;
16#[cfg(verus_keep_ghost)]
17#[cfg(all(feature = "alloc", not(verus_verify_core)))]
18use super::std_specs::iter::IteratorSpec;
19#[cfg(verus_keep_ghost)]
20use super::std_specs::range::{
21    ExRange, RangeBoundsSpec, slice_range_end, slice_range_start, slice_range_valid,
22};
23use super::utf8::*;
24use super::view::*;
25
26#[cfg(verus_keep_ghost)]
27#[cfg(not(verus_verify_core))]
28use super::std_specs::cmp::PartialEqSpecImpl;
29
30use verus as verus_skip_verusfmt; // verusfmt doesn't handle s[..e] yet
31verus_skip_verusfmt! {
32
33broadcast use {super::seq::group_seq_lemmas, super::slice::group_slice_axioms};
34
35#[cfg(not(verus_verify_core))]
36impl View for str {
37    type V = Seq<char>;
38
39    uninterp spec fn view(&self) -> Seq<char>;
40}
41
42#[cfg(not(verus_verify_core))]
43impl DeepView for str {
44    type V = Seq<char>;
45
46    open spec fn deep_view(&self) -> Seq<char> {
47        self.view()
48    }
49}
50
51#[cfg(verus_keep_ghost)]
52#[cfg(not(verus_verify_core))]
53impl PartialEqSpecImpl for str {
54    open spec fn obeys_eq_spec() -> bool {
55        true
56    }
57
58    open spec fn eq_spec(&self, other: &str) -> bool {
59        self@ == other@
60    }
61}
62
63#[cfg(not(verus_verify_core))]
64pub trait StringSliceAdditionalSpecFns {
65    spec fn spec_bytes(&self) -> Seq<u8>;
66}
67
68#[cfg(not(verus_verify_core))]
69impl StringSliceAdditionalSpecFns for str {
70    open spec fn spec_bytes(&self) -> Seq<u8> {
71        encode_utf8(self@)
72    }
73}
74
75#[cfg(not(verus_verify_core))]
76pub open spec fn is_ascii(s: &str) -> bool {
77    is_ascii_chars(s@)
78}
79
80#[cfg(not(verus_verify_core))]
81pub broadcast proof fn is_ascii_spec_bytes(s: &str)
82    ensures
83        #[trigger] is_ascii(s) ==> #[trigger] s.spec_bytes() =~= Seq::new(
84            s@.len(),
85            |i| s@.index(i) as u8,
86        ),
87{
88    if (is_ascii(s)) {
89        is_ascii_chars_encode_utf8(s@);
90    }
91}
92
93#[cfg(not(verus_verify_core))]
94pub broadcast proof fn is_ascii_concat(s1: &str, s2: &str, s3: &str)
95    requires
96        s1@ =~= s2@ + s3@,
97    ensures
98        #![trigger s2@ + s3@, is_ascii(s1), is_ascii(s2), is_ascii(s3)]
99        is_ascii(s1) <==> is_ascii(s2) && is_ascii(s3),
100{
101    broadcast use is_ascii_chars_concat;
102
103    if (is_ascii(s1)) {
104        is_ascii_chars_concat(s1@, s2@, s3@);
105    }
106}
107
108#[cfg(not(verus_verify_core))]
109#[verifier::when_used_as_spec(is_ascii)]
110pub assume_specification[ str::is_ascii ](s: &str) -> (b: bool)
111    ensures
112        b == is_ascii(s),
113;
114
115#[cfg(all(feature = "alloc", not(verus_verify_core)))]
116use crate::alloc::borrow::ToOwned;
117
118#[cfg(all(feature = "alloc", not(verus_verify_core)))]
119pub assume_specification[ str::to_owned ](s: &str) -> (res: String)
120    ensures
121        s@ == res@,
122;
123
124#[cfg(not(verus_verify_core))]
125pub assume_specification[ str::as_bytes ](s: &str) -> (b: &[u8])
126    ensures
127        b@ == s.spec_bytes(),
128;
129
130#[cfg(not(verus_verify_core))]
131#[verifier::allow_in_spec]
132pub assume_specification[ str::len ](s: &str) -> usize
133    returns
134        s.spec_bytes().len() as usize,
135;
136
137#[cfg(not(verus_verify_core))]
138#[verifier::allow_in_spec]
139pub assume_specification[ str::is_empty ](s: &str) -> bool
140    returns
141        s@.len() == 0,
142;
143
144#[cfg(not(verus_verify_core))]
145#[verifier::allow_in_spec]
146pub assume_specification[ str::is_char_boundary ](s: &str, index: usize) -> bool
147    returns
148        is_char_boundary(s.spec_bytes(), index as int),
149;
150
151#[cfg(not(verus_verify_core))]
152pub assume_specification[ str::split_at ](s: &str, mid: usize) -> (res: (&str, &str))
153    requires
154        is_char_boundary(s.spec_bytes(), mid as int),
155    ensures
156        res.0.spec_bytes() =~= s.spec_bytes()[..mid],
157        res.1.spec_bytes() =~= s.spec_bytes()[mid..],
158;
159
160#[cfg(not(verus_verify_core))]
161pub assume_specification[ str::from_utf8_unchecked ](v: &[u8]) -> (res: &str)
162    requires
163        valid_utf8(v@),
164    ensures
165        res.spec_bytes() =~= v@,
166;
167
168#[cfg(all(feature = "alloc", not(verus_verify_core)))]
169pub uninterp spec fn to_string_from_display_ensures<T: core::fmt::Display + ?Sized>(
170    t: &T,
171    s: String,
172) -> bool;
173
174#[cfg(all(feature = "alloc", not(verus_verify_core)))]
175pub broadcast proof fn to_string_from_display_ensures_for_str(t: &str, res: String)
176    ensures
177        #[trigger] to_string_from_display_ensures::<str>(t, res) <==> (t@ == res@),
178{
179    admit();
180}
181
182#[cfg(all(feature = "alloc", not(verus_verify_core)))]
183pub assume_specification<T: core::fmt::Display + ?Sized>[ <T as ToString>::to_string ](
184    t: &T,
185) -> (res: String)
186    ensures
187        to_string_from_display_ensures::<T>(t, res),
188;
189
190#[cfg(not(verus_verify_core))]
191#[verifier::external]
192pub trait StrSliceExecFns {
193    fn unicode_len(&self) -> usize;
194
195    fn get_char(&self, i: usize) -> char;
196
197    fn substring_ascii<'a>(&'a self, from: usize, to: usize) -> &'a str;
198
199    fn substring_char<'a>(&'a self, from: usize, to: usize) -> &'a str;
200
201    fn get_ascii(&self, i: usize) -> u8;
202
203    #[cfg(feature = "alloc")]
204    fn as_bytes_vec(&self) -> alloc::vec::Vec<u8>;
205}
206
207#[cfg(not(verus_verify_core))]
208impl StrSliceExecFns for str {
209    /// The len() function in rust returns the byte length.
210    /// It is more useful to talk about the length of characters and therefore this function was added.
211    /// Please note that this function counts the unicode variation selectors as characters.
212    /// Warning: O(n)
213    #[verifier::external_body]
214    fn unicode_len(&self) -> (l: usize)
215        ensures
216            l as nat == self@.len(),
217    {
218        self.chars().count()
219    }
220
221    /// Warning: O(n) not O(1) due to unicode decoding needed
222    #[verifier::external_body]
223    fn get_char(&self, i: usize) -> (c: char)
224        requires
225            i < self@.len(),
226        ensures
227            self@.index(i as int) == c,
228    {
229        self.chars().nth(i).unwrap()
230    }
231
232    #[verifier::external_body]
233    fn substring_ascii<'a>(&'a self, from: usize, to: usize) -> (ret: &'a str)
234        requires
235            self.is_ascii(),
236            from <= to <= self@.len(),
237        ensures
238            ret@ == self@[from..to],
239            ret.is_ascii(),
240    {
241        // Range::index panics if from > to or from > self@.len()
242        &self[from..to]
243    }
244
245    #[verifier::external_body]
246    fn substring_char<'a>(&'a self, from: usize, to: usize) -> (ret: &'a str)
247        requires
248            from <= to <= self@.len(),
249        ensures
250            ret@ == self@[from..to],
251    {
252        let mut char_pos = 0;
253        let mut byte_start = None;
254        let mut byte_end = None;
255        let mut byte_pos = 0;
256        let mut it = self.chars();
257        loop {
258            if char_pos == from {
259                byte_start = Some(byte_pos);
260            }
261            if char_pos == to {
262                byte_end = Some(byte_pos);
263                break;
264            }
265            if let Some(c) = it.next() {
266                char_pos += 1;
267                byte_pos += c.len_utf8();
268            } else {
269                break;
270            }
271        }
272        let byte_start = byte_start.unwrap();
273        let byte_end = byte_end.unwrap();
274        // Range::index panics if from > to or from > self@.len()
275        &self[byte_start..byte_end]
276    }
277
278    fn get_ascii(&self, i: usize) -> (b: u8)
279        requires
280            self.is_ascii(),
281            i < self@.len(),
282        ensures
283            self@.index(i as int) as u8 == b,
284    {
285        broadcast use is_ascii_spec_bytes;
286        // panics if i is not a valid index
287
288        self.as_bytes()[i]
289    }
290
291    #[cfg(feature = "alloc")]
292    fn as_bytes_vec(&self) -> (ret: alloc::vec::Vec<u8>)
293        ensures
294            ret@ == self.spec_bytes(),
295    {
296        slice_to_vec(self.as_bytes())
297    }
298}
299
300#[cfg(not(verus_verify_core))]
301pub uninterp spec fn strlit_view_id(s: Seq<char>) -> int;
302
303/// Distinct string literals have provably distinct views.
304#[cfg(not(verus_verify_core))]
305pub broadcast axiom fn axiom_new_strlit_view_id(id: int)
306    ensures
307        strlit_view_id(#[trigger] strslice_new_strlit(id).view()) == id,
308;
309
310#[cfg(not(verus_verify_core))]
311pub broadcast axiom fn axiom_str_literal_len<'a>(s: &'a str)
312    ensures
313        #[trigger] s@.len() == strslice_len(s),
314;
315
316#[cfg(not(verus_verify_core))]
317pub broadcast axiom fn axiom_str_literal_get_char<'a>(s: &'a str, i: int)
318    ensures
319        #[trigger] s@.index(i) == strslice_get_char(s, i),
320;
321
322#[cfg(all(not(feature = "alloc"), not(verus_verify_core)))]
323pub broadcast group group_string_axioms {
324    axiom_new_strlit_view_id,
325    axiom_str_literal_len,
326    axiom_str_literal_get_char,
327    is_ascii_spec_bytes,
328    is_ascii_concat,
329}
330
331#[cfg(all(feature = "alloc", not(verus_verify_core)))]
332pub broadcast group group_string_axioms {
333    axiom_new_strlit_view_id,
334    axiom_str_literal_len,
335    axiom_str_literal_get_char,
336    to_string_from_display_ensures_for_str,
337    is_ascii_spec_bytes,
338    is_ascii_concat,
339}
340
341#[cfg(all(feature = "alloc", not(verus_verify_core)))]
342impl View for String {
343    type V = Seq<char>;
344
345    uninterp spec fn view(&self) -> Seq<char>;
346}
347
348#[cfg(all(feature = "alloc", not(verus_verify_core)))]
349impl DeepView for String {
350    type V = Seq<char>;
351
352    open spec fn deep_view(&self) -> Seq<char> {
353        self.view()
354    }
355}
356
357#[cfg(all(feature = "alloc", not(verus_verify_core)))]
358#[verifier::external_type_specification]
359#[verifier::external_body]
360pub struct ExString(String);
361
362#[cfg(all(feature = "alloc", not(verus_verify_core)))]
363pub open spec fn string_is_ascii(s: &String) -> bool {
364    is_ascii_chars(s@)
365}
366
367#[cfg(all(feature = "alloc", not(verus_verify_core)))]
368pub assume_specification<'a>[ String::as_str ](s: &'a String) -> (res: &'a str)
369    ensures
370        res@ == s@,
371;
372
373// same as above
374#[cfg(all(feature = "alloc", not(verus_verify_core)))]
375pub assume_specification<'a>[ <String as core::ops::Deref>::deref ](s: &'a String) -> (res: &'a str)
376    ensures
377        res@ == s@,
378;
379
380#[cfg(all(feature = "alloc", not(verus_verify_core)))]
381pub assume_specification[ <String as Clone>::clone ](s: &String) -> (res: String)
382    ensures
383        res == s,
384;
385
386#[cfg(all(feature = "alloc", not(verus_verify_core)))]
387pub assume_specification[ <String as PartialEq>::eq ](s: &String, other: &String) -> (res: bool)
388    ensures
389        res == (s@ == other@),
390;
391
392#[cfg(all(feature = "alloc", not(verus_verify_core)))]
393pub assume_specification[ String::new ]() -> (res: String)
394    ensures
395        res@ == Seq::<char>::empty(),
396;
397
398#[cfg(all(feature = "alloc", not(verus_verify_core)))]
399pub assume_specification[ String::push ](s: &mut String, c: char)
400    ensures
401        final(s)@ == old(s)@.push(c),
402;
403
404#[cfg(all(feature = "alloc", not(verus_verify_core)))]
405pub assume_specification[ String::pop ](s: &mut String) -> (res: Option<char>)
406    ensures
407        old(s)@.len() == 0 ==> res is None && final(s)@ == old(s)@,
408        old(s)@.len() > 0 ==> res == Some(old(s)@.last()) && final(s)@ == old(s)@.drop_last(),
409;
410
411#[cfg(all(feature = "alloc", not(verus_verify_core)))]
412pub assume_specification[ String::push_str ](s: &mut String, other: &str)
413    ensures
414        final(s)@ == old(s)@ + other@,
415;
416
417#[cfg(all(feature = "alloc", not(verus_verify_core)))]
418pub assume_specification[ String::is_empty ](s: &String) -> (res: bool)
419    ensures
420        res == (s@.len() == 0),
421;
422
423#[cfg(all(feature = "alloc", not(verus_verify_core)))]
424pub assume_specification[ String::clear ](s: &mut String)
425    ensures
426        final(s)@ == Seq::<char>::empty(),
427;
428
429#[cfg(all(feature = "alloc", not(verus_verify_core)))]
430pub assume_specification[ <String as core::default::Default>::default ]() -> (r: String)
431    ensures
432        r@ == Seq::<char>::empty(),
433;
434
435#[cfg(all(feature = "alloc", not(verus_verify_core)))]
436pub trait StringExecFnsIsAscii: Sized {
437    fn is_ascii(&self) -> bool;
438}
439
440#[cfg(all(feature = "alloc", not(verus_verify_core)))]
441impl StringExecFnsIsAscii for String {
442    #[inline(always)]
443    #[verifier::when_used_as_spec(string_is_ascii)]
444    fn is_ascii(&self) -> (ret: bool)
445        ensures
446            ret == string_is_ascii(self),
447    {
448        self.as_str().is_ascii()
449    }
450}
451
452#[cfg(all(feature = "alloc", not(verus_verify_core)))]
453#[verifier::external]
454pub trait StringExecFns: Sized {
455    fn from_str<'a>(s: &'a str) -> String;
456
457    fn append<'a, 'b>(&'a mut self, other: &'b str);
458
459    fn concat<'b>(self, other: &'b str) -> String;
460}
461
462#[cfg(all(feature = "alloc", not(verus_verify_core)))]
463impl StringExecFns for String {
464    #[verifier::external_body]
465    fn from_str<'a>(s: &'a str) -> (ret: String)
466        ensures
467            s@ == ret@,
468    {
469        s.to_string()
470    }
471
472    #[verifier::external_body]
473    fn append<'a, 'b>(&'a mut self, other: &'b str)
474        ensures
475            final(self)@ == old(self)@ + other@,
476    {
477        *self += other;
478    }
479
480    #[verifier::external_body]
481    fn concat<'b>(self, other: &'b str) -> (ret: String)
482        ensures
483            ret@ == self@ + other@,
484    {
485        self + other
486    }
487}
488
489// The `chars` method of a `str` returns an iterator of type `Chars`,
490// so we specify that type here.
491#[verifier::external_type_specification]
492#[verifier::external_body]
493#[cfg(all(feature = "alloc", not(verus_verify_core)))]
494pub struct ExChars<'a>(Chars<'a>);
495
496// To allow reasoning about the "contents" of the string iterator, without using
497// a prophecy, we need a function that gives us the underlying sequence of the original string.
498#[cfg(feature = "alloc")]
499pub uninterp spec fn into_iter_elts<'a>(i: Chars<'a>) -> Seq<char>;
500
501#[cfg(feature = "alloc")]
502pub assume_specification[ str::chars ](s: &str) -> (iter: Chars<'_>)
503    ensures
504        IteratorSpec::remaining(&iter) == s@,
505        IteratorSpec::decrease(&iter) is Some,
506;
507
508#[cfg(verus_keep_ghost)]
509#[cfg(feature = "alloc")]
510impl<'a> super::std_specs::iter::IteratorSpecImpl for Chars<'a> {
511    open spec fn obeys_prophetic_iter_laws(&self) -> bool {
512        true
513    }
514
515    uninterp spec fn remaining(&self) -> Seq<Self::Item>;
516
517    uninterp spec fn will_return_none(&self) -> bool;
518
519    uninterp spec fn decrease(&self) -> Option<nat>;
520
521    open spec fn peek(&self, index: int) -> Option<Self::Item> {
522        if 0 <= index < into_iter_elts(*self).len() {
523            Some(into_iter_elts(*self)[index])
524        } else {
525            None
526        }
527    }
528}
529
530// There are various types you can use to index into a `str` to get a
531// slice, i.e., to implement `SliceIndex<str>`. Here we indicate, for
532// any such type (e.g., `Range<usize>`), whether an index of that
533// type is valid when applied to a given `&str`.
534#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
535pub open spec fn str_slice_in_bounds<R: RangeBoundsSpec<usize>>(range: &R, s: &str) -> bool {
536    &&& slice_range_valid(range, s.spec_bytes().len())
537    &&& is_char_boundary(s.spec_bytes(), slice_range_start(range))
538    &&& is_char_boundary(s.spec_bytes(), slice_range_end(range, s.spec_bytes().len()))
539}
540
541// There are various types you can use to index into a `str` to get a
542// slice, i.e., to implement `SliceIndex<str>`. Here we indicate, for
543// any such type (e.g., `Range<usize>`), the semantics of immutably
544// indexing a string.
545#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
546pub open spec fn str_slice_index_postcondition<R: RangeBoundsSpec<usize>>(
547    range: &R,
548    s: Seq<u8>,
549    r: Seq<u8>,
550) -> bool {
551    r == s[slice_range_start(range)..slice_range_end(range, s.len())]
552}
553
554// There are various types you can use to index into a `str` to get a
555// slice, i.e., to implement `SliceIndex<str>`. Here we indicate, for
556// any such type (e.g., `Range<usize>`), the semantics of mutably
557// indexing a string.
558#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
559pub open spec fn str_slice_index_mut_postcondition<R: RangeBoundsSpec<usize>>(
560    range: &R,
561    old_s: Seq<u8>,
562    final_s: Seq<u8>,
563    r: Seq<u8>,
564    final_r: Seq<u8>,
565) -> bool {
566    let start = slice_range_start(range);
567    let end = slice_range_end(range, old_s.len());
568    &&& r == old_s[start..end]
569    &&& final_s.len() == old_s.len()
570    &&& final_s[..start] == old_s[..start]
571    &&& final_s[start..end] == final_r
572    &&& final_s[end..old_s.len()] == old_s[end..]
573}
574
575#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
576impl super::slice::SliceIndexSpecImpl<str> for (Bound<usize>, Bound<usize>) {
577    open spec fn in_bounds(&self, s: &str) -> bool {
578        str_slice_in_bounds(self, s)
579    }
580
581    open spec fn index_postcondition(&self, s: &str, r: &str) -> bool {
582        str_slice_index_postcondition(self, s.spec_bytes(), r.spec_bytes())
583    }
584
585    open spec fn index_mut_postcondition(
586        &self,
587        old_slice: &str,
588        final_slice: &str,
589        immediate_output: &str,
590        final_output: &str,
591    ) -> bool {
592        str_slice_index_mut_postcondition(
593            self,
594            old_slice.spec_bytes(),
595            final_slice.spec_bytes(),
596            immediate_output.spec_bytes(),
597            final_output.spec_bytes(),
598        )
599    }
600}
601
602#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
603pub assume_specification[ <(Bound<usize>, Bound<usize>) as SliceIndex<str>>::get ](
604    i: (Bound<usize>, Bound<usize>),
605    s: &str,
606) -> (r: Option<&str>)
607;
608
609#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
610pub assume_specification[ <(Bound<usize>, Bound<usize>) as SliceIndex<str>>::index ](
611    i: (Bound<usize>, Bound<usize>),
612    s: &str,
613) -> (r: &str)
614;
615
616#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
617pub assume_specification[ <(Bound<usize>, Bound<usize>) as SliceIndex<str>>::get_mut ](
618    i: (Bound<usize>, Bound<usize>),
619    s: &mut str,
620) -> (r: Option<&mut str>)
621;
622
623#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
624pub assume_specification[ <(Bound<usize>, Bound<usize>) as SliceIndex<str>>::index_mut ](
625    i: (Bound<usize>, Bound<usize>),
626    s: &mut str,
627) -> (r: &mut str)
628;
629
630#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
631impl super::slice::SliceIndexSpecImpl<str> for Range<usize> {
632    open spec fn in_bounds(&self, s: &str) -> bool {
633        str_slice_in_bounds(self, s)
634    }
635
636    open spec fn index_postcondition(&self, s: &str, r: &str) -> bool {
637        str_slice_index_postcondition(self, s.spec_bytes(), r.spec_bytes())
638    }
639
640    open spec fn index_mut_postcondition(
641        &self,
642        old_slice: &str,
643        final_slice: &str,
644        immediate_output: &str,
645        final_output: &str,
646    ) -> bool {
647        str_slice_index_mut_postcondition(
648            self,
649            old_slice.spec_bytes(),
650            final_slice.spec_bytes(),
651            immediate_output.spec_bytes(),
652            final_output.spec_bytes(),
653        )
654    }
655}
656
657#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
658pub assume_specification[ <Range<usize> as SliceIndex<str>>::get ](i: Range<usize>, s: &str) -> (r:
659    Option<&<Range<usize> as SliceIndex<str>>::Output>)
660;
661
662#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
663pub assume_specification[ <Range<usize> as SliceIndex<str>>::index ](
664    i: Range<usize>,
665    s: &str,
666) -> (r: &<Range<usize> as SliceIndex<str>>::Output)
667;
668
669#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
670pub assume_specification[ <Range<usize> as SliceIndex<str>>::get_mut ](
671    i: Range<usize>,
672    s: &mut str,
673) -> (r: Option<&mut <Range<usize> as SliceIndex<str>>::Output>)
674;
675
676#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
677pub assume_specification[ <Range<usize> as SliceIndex<str>>::index_mut ](
678    i: Range<usize>,
679    s: &mut str,
680) -> (r: &mut <Range<usize> as SliceIndex<str>>::Output)
681;
682
683#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
684impl super::slice::SliceIndexSpecImpl<str> for RangeFrom<usize> {
685    open spec fn in_bounds(&self, s: &str) -> bool {
686        str_slice_in_bounds(self, s)
687    }
688
689    open spec fn index_postcondition(&self, s: &str, r: &str) -> bool {
690        str_slice_index_postcondition(self, s.spec_bytes(), r.spec_bytes())
691    }
692
693    open spec fn index_mut_postcondition(
694        &self,
695        old_slice: &str,
696        final_slice: &str,
697        immediate_output: &str,
698        final_output: &str,
699    ) -> bool {
700        str_slice_index_mut_postcondition(
701            self,
702            old_slice.spec_bytes(),
703            final_slice.spec_bytes(),
704            immediate_output.spec_bytes(),
705            final_output.spec_bytes(),
706        )
707    }
708}
709
710#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
711pub assume_specification[ <RangeFrom<usize> as SliceIndex<str>>::get ](
712    i: RangeFrom<usize>,
713    s: &str,
714) -> (r: Option<&<RangeFrom<usize> as SliceIndex<str>>::Output>)
715;
716
717#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
718pub assume_specification[ <RangeFrom<usize> as SliceIndex<str>>::index ](
719    i: RangeFrom<usize>,
720    s: &str,
721) -> (r: &<RangeFrom<usize> as SliceIndex<str>>::Output)
722;
723
724#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
725pub assume_specification[ <RangeFrom<usize> as SliceIndex<str>>::get_mut ](
726    i: RangeFrom<usize>,
727    s: &mut str,
728) -> (r: Option<&mut <RangeFrom<usize> as SliceIndex<str>>::Output>)
729;
730
731#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
732pub assume_specification[ <RangeFrom<usize> as SliceIndex<str>>::index_mut ](
733    i: RangeFrom<usize>,
734    s: &mut str,
735) -> (r: &mut <RangeFrom<usize> as SliceIndex<str>>::Output)
736;
737
738#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
739impl super::slice::SliceIndexSpecImpl<str> for RangeFull {
740    open spec fn in_bounds(&self, s: &str) -> bool {
741        str_slice_in_bounds(self, s)
742    }
743
744    open spec fn index_postcondition(&self, s: &str, r: &str) -> bool {
745        str_slice_index_postcondition(self, s.spec_bytes(), r.spec_bytes())
746    }
747
748    open spec fn index_mut_postcondition(
749        &self,
750        old_slice: &str,
751        final_slice: &str,
752        immediate_output: &str,
753        final_output: &str,
754    ) -> bool {
755        str_slice_index_mut_postcondition(
756            self,
757            old_slice.spec_bytes(),
758            final_slice.spec_bytes(),
759            immediate_output.spec_bytes(),
760            final_output.spec_bytes(),
761        )
762    }
763}
764
765#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
766pub assume_specification[ <RangeFull as SliceIndex<str>>::get ](i: RangeFull, s: &str) -> (r:
767    Option<&<RangeFull as SliceIndex<str>>::Output>)
768;
769
770#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
771pub assume_specification[ <RangeFull as SliceIndex<str>>::index ](i: RangeFull, s: &str) -> (r:
772    &<RangeFull as SliceIndex<str>>::Output)
773;
774
775#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
776pub assume_specification[ <RangeFull as SliceIndex<str>>::get_mut ](
777    i: RangeFull,
778    s: &mut str,
779) -> (r: Option<&mut <RangeFull as SliceIndex<str>>::Output>)
780;
781
782#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
783pub assume_specification[ <RangeFull as SliceIndex<str>>::index_mut ](
784    i: RangeFull,
785    s: &mut str,
786) -> (r: &mut <RangeFull as SliceIndex<str>>::Output)
787;
788
789#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
790impl super::slice::SliceIndexSpecImpl<str> for RangeInclusive<usize> {
791    open spec fn in_bounds(&self, s: &str) -> bool {
792        str_slice_in_bounds(self, s)
793    }
794
795    open spec fn index_postcondition(&self, s: &str, r: &str) -> bool {
796        str_slice_index_postcondition(self, s.spec_bytes(), r.spec_bytes())
797    }
798
799    open spec fn index_mut_postcondition(
800        &self,
801        old_slice: &str,
802        final_slice: &str,
803        immediate_output: &str,
804        final_output: &str,
805    ) -> bool {
806        str_slice_index_mut_postcondition(
807            self,
808            old_slice.spec_bytes(),
809            final_slice.spec_bytes(),
810            immediate_output.spec_bytes(),
811            final_output.spec_bytes(),
812        )
813    }
814}
815
816#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
817pub assume_specification[ <RangeInclusive<usize> as SliceIndex<str>>::get ](
818    i: RangeInclusive<usize>,
819    s: &str,
820) -> (r: Option<&<RangeInclusive<usize> as SliceIndex<str>>::Output>)
821;
822
823#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
824pub assume_specification[ <RangeInclusive<usize> as SliceIndex<str>>::index ](
825    i: RangeInclusive<usize>,
826    s: &str,
827) -> (r: &<RangeInclusive<usize> as SliceIndex<str>>::Output)
828;
829
830#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
831pub assume_specification[ <RangeInclusive<usize> as SliceIndex<str>>::get_mut ](
832    i: RangeInclusive<usize>,
833    s: &mut str,
834) -> (r: Option<&mut <RangeInclusive<usize> as SliceIndex<str>>::Output>)
835;
836
837#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
838pub assume_specification[ <RangeInclusive<usize> as SliceIndex<str>>::index_mut ](
839    i: RangeInclusive<usize>,
840    s: &mut str,
841) -> (r: &mut <RangeInclusive<usize> as SliceIndex<str>>::Output)
842;
843
844#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
845impl super::slice::SliceIndexSpecImpl<str> for RangeTo<usize> {
846    open spec fn in_bounds(&self, s: &str) -> bool {
847        str_slice_in_bounds(self, s)
848    }
849
850    open spec fn index_postcondition(&self, s: &str, r: &str) -> bool {
851        str_slice_index_postcondition(self, s.spec_bytes(), r.spec_bytes())
852    }
853
854    open spec fn index_mut_postcondition(
855        &self,
856        old_slice: &str,
857        final_slice: &str,
858        immediate_output: &str,
859        final_output: &str,
860    ) -> bool {
861        str_slice_index_mut_postcondition(
862            self,
863            old_slice.spec_bytes(),
864            final_slice.spec_bytes(),
865            immediate_output.spec_bytes(),
866            final_output.spec_bytes(),
867        )
868    }
869}
870
871#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
872pub assume_specification[ <RangeTo<usize> as SliceIndex<str>>::get ](
873    i: RangeTo<usize>,
874    s: &str,
875) -> (r: Option<&<RangeTo<usize> as SliceIndex<str>>::Output>)
876;
877
878#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
879pub assume_specification[ <RangeTo<usize> as SliceIndex<str>>::index ](
880    i: RangeTo<usize>,
881    s: &str,
882) -> (r: &<RangeTo<usize> as SliceIndex<str>>::Output)
883;
884
885#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
886pub assume_specification[ <RangeTo<usize> as SliceIndex<str>>::get_mut ](
887    i: RangeTo<usize>,
888    s: &mut str,
889) -> (r: Option<&mut <RangeTo<usize> as SliceIndex<str>>::Output>)
890;
891
892#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
893pub assume_specification[ <RangeTo<usize> as SliceIndex<str>>::index_mut ](
894    i: RangeTo<usize>,
895    s: &mut str,
896) -> (r: &mut <RangeTo<usize> as SliceIndex<str>>::Output)
897;
898
899#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
900impl super::slice::SliceIndexSpecImpl<str> for RangeToInclusive<usize> {
901    open spec fn in_bounds(&self, s: &str) -> bool {
902        str_slice_in_bounds(self, s)
903    }
904
905    open spec fn index_postcondition(&self, s: &str, r: &str) -> bool {
906        str_slice_index_postcondition(self, s.spec_bytes(), r.spec_bytes())
907    }
908
909    open spec fn index_mut_postcondition(
910        &self,
911        old_slice: &str,
912        final_slice: &str,
913        immediate_output: &str,
914        final_output: &str,
915    ) -> bool {
916        str_slice_index_mut_postcondition(
917            self,
918            old_slice.spec_bytes(),
919            final_slice.spec_bytes(),
920            immediate_output.spec_bytes(),
921            final_output.spec_bytes(),
922        )
923    }
924}
925
926#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
927pub assume_specification[ <RangeToInclusive<usize> as SliceIndex<str>>::get ](
928    i: RangeToInclusive<usize>,
929    s: &str,
930) -> (r: Option<&<RangeToInclusive<usize> as SliceIndex<str>>::Output>)
931;
932
933#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
934pub assume_specification[ <RangeToInclusive<usize> as SliceIndex<str>>::index ](
935    i: RangeToInclusive<usize>,
936    s: &str,
937) -> (r: &<RangeToInclusive<usize> as SliceIndex<str>>::Output)
938;
939
940#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
941pub assume_specification[ <RangeToInclusive<usize> as SliceIndex<str>>::get_mut ](
942    i: RangeToInclusive<usize>,
943    s: &mut str,
944) -> (r: Option<&mut <RangeToInclusive<usize> as SliceIndex<str>>::Output>)
945;
946
947#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
948pub assume_specification[ <RangeToInclusive<usize> as SliceIndex<str>>::index_mut ](
949    i: RangeToInclusive<usize>,
950    s: &mut str,
951) -> (r: &mut <RangeToInclusive<usize> as SliceIndex<str>>::Output)
952;
953
954// `<str as ops::Index<I>>::index(&self, index: I)` just invokes
955// `index.index(self)`. So we likewise delegate determining the meaning
956// of the string-index operation to that of the index type.
957#[cfg(all(verus_keep_ghost, not(verus_verify_core)))]
958impl<I: SliceIndexSpec<str>> super::std_specs::core::IndexSpecImpl<I> for str {
959    open spec fn index_req(&self, index: &I) -> bool {
960        index.in_bounds(self)
961    }
962}
963
964pub use super::view::View;
965
966} // verus!