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