Skip to main content

vest_lib/combinators/recursive/
spec.rs

1//! Fixpoint specifications and recursive-body interfaces.
2use crate::core::{proof::*, spec::*};
3use vstd::prelude::*;
4
5verus! {
6
7/// Bundled triple of parser, consistency, and byte-length spec functions.
8pub type ParserSpecs<SpecP, Cnstcy, Blen> = (SpecP, Cnstcy, Blen);
9
10pub type ParserFnSpecs<T> = (ParserFnSpec<T>, PredFnSpec<T>, ByteLenFnSpec<T>);
11
12impl<SpecP, Cnstcy, Blen> SpecByteLen for ParserSpecs<SpecP, Cnstcy, Blen> where
13    Blen: SpecByteLen,
14    SpecP: SpecParser<PVal = Blen::T>,
15    Cnstcy: Consistency<Val = Blen::T>,
16 {
17    type T = Blen::T;
18
19    open spec fn byte_len(&self, v: Self::T) -> nat {
20        (self.2).byte_len(v)
21    }
22}
23
24impl<SpecP, Cnstcy, Blen> Consistency for ParserSpecs<SpecP, Cnstcy, Blen> where
25    Blen: SpecByteLen,
26    SpecP: SpecParser<PVal = Blen::T>,
27    Cnstcy: Consistency<Val = Blen::T>,
28 {
29    type Val = Blen::T;
30
31    open spec fn consistent(&self, v: Self::Val) -> bool {
32        (self.1).consistent(v)
33    }
34}
35
36impl<SpecP, Cnstcy, Blen> SpecParser for ParserSpecs<SpecP, Cnstcy, Blen> where
37    Blen: SpecByteLen,
38    SpecP: SpecParser<PVal = Blen::T>,
39    Cnstcy: Consistency<Val = Blen::T>,
40 {
41    type PVal = Blen::T;
42
43    open spec fn spec_parse(&self, ibuf: Seq<u8>) -> Option<(int, Self::PVal)> {
44        (self.0).spec_parse(ibuf)
45    }
46}
47
48/// The functional version of [`SafeParser`].
49pub open spec fn safe_parser<T>(parser: ParserFnSpec<T>) -> bool {
50    forall|input: Seq<u8>| #[trigger] parser(input) matches Some((n, _)) ==> 0 <= n <= input.len()
51}
52
53/// A bundled non-DPS serializer: pairs a [`SerializerFnSpec`] with a [`ByteLenFnSpec`].
54pub type SerializerSpecs<SpecS, Blen> = (SpecS, Blen);
55
56pub type SerializerFnSpecs<T> = (SerializerFnSpec<T>, ByteLenFnSpec<T>);
57
58impl<SpecS, Blen> SpecByteLen for SerializerSpecs<SpecS, Blen> where
59    Blen: SpecByteLen,
60    SpecS: SpecSerializer<SVal = Blen::T>,
61 {
62    type T = Blen::T;
63
64    open spec fn byte_len(&self, v: Self::T) -> nat {
65        (self.1).byte_len(v)
66    }
67}
68
69impl<SpecS, Blen> SpecSerializer for SerializerSpecs<SpecS, Blen> where
70    Blen: SpecByteLen,
71    SpecS: SpecSerializer<SVal = Blen::T>,
72 {
73    type SVal = Blen::T;
74
75    open spec fn spec_serialize(&self, v: Self::SVal) -> Seq<u8> {
76        (self.0).spec_serialize(v)
77    }
78}
79
80/// Functional version of [`NonTailFmt`] for DPS serializer functions.
81pub open spec fn non_tail_fmt_dps<T>(
82    serializer_dps: SerializerDPSFnSpec<T>,
83    byte_len: ByteLenFnSpec<T>,
84) -> bool {
85    &&& forall|v: T, obuf: Seq<u8>| #[trigger]
86        serializer_dps(v, obuf).len() - obuf.len() == byte_len(
87            v,
88        )
89    // FIXME: Using existentials here cause verus fail to verify
90    &&& forall|v: T, obuf: Seq<u8>| #[trigger]
91        serializer_dps(v, obuf) == (choose|w: Seq<u8>| serializer_dps(v, obuf) == w + obuf) + obuf
92}
93
94/// Functional version of [`GoodSerializer`] for serializer functions.
95pub open spec fn good_serializer_fn<T>(
96    serializer: SerializerFnSpec<T>,
97    byte_len: ByteLenFnSpec<T>,
98) -> bool {
99    forall|v: T| #[trigger] serializer(v).len() == byte_len(v)
100}
101
102impl<SpecS, Blen> GoodSerializer for SerializerSpecs<SpecS, Blen> where
103    Blen: SpecByteLen,
104    SpecS: SpecSerializer<SVal = Blen::T>,
105 {
106    open spec fn serialize_inv(&self) -> bool {
107        let (s, b) = *self;
108        let (s_fn, b_fn) = (|v| s.spec_serialize(v), |v| b.byte_len(v));
109        good_serializer_fn(s_fn, b_fn)
110    }
111
112    proof fn lemma_serialize_len(&self, v: Self::SVal) {
113        let (s, b) = *self;
114        let (s_fn, b_fn) = (|v| s.spec_serialize(v), |v| b.byte_len(v));
115        assert(good_serializer_fn(s_fn, b_fn));
116        assert(s_fn(v).len() == b_fn(v));
117    }
118}
119
120impl<SpecP, Cnstcy, Blen> SafeParser for (SpecP, Cnstcy, Blen) where
121    Blen: SpecByteLen,
122    SpecP: SpecParser<PVal = Blen::T>,
123    Cnstcy: Consistency<Val = Blen::T>,
124 {
125    open spec fn safe_inv(&self) -> bool {
126        let (p, _, _) = *self;
127        let p_fn = |ibuf| p.spec_parse(ibuf);
128        safe_parser(p_fn)
129    }
130
131    proof fn lemma_parse_safe(&self, ibuf: Seq<u8>) {
132        let (p, _, _) = *self;
133        let p_fn = |i: Seq<u8>| p.spec_parse(i);
134        assert(self.safe_inv());
135        if let Some((n, v)) = self.spec_parse(ibuf) {
136            assert(safe_parser(p_fn));
137            assert(p_fn(ibuf) == Some((n, v)));
138            assert(0 <= n <= ibuf.len());
139        }
140    }
141}
142
143impl<SpecP, Cnstcy, Blen> SoundParser for (SpecP, Cnstcy, Blen) where
144    Blen: SpecByteLen,
145    SpecP: SpecParser<PVal = Blen::T>,
146    Cnstcy: Consistency<Val = Blen::T>,
147 {
148    open spec fn sound_inv(&self) -> bool {
149        let (p, c, b) = *self;
150        let (p_fn, c_fn, b_fn) = (
151            |ibuf| p.spec_parse(ibuf),
152            |v| c.consistent(v),
153            |v| b.byte_len(v),
154        );
155        sound_parser(p_fn, c_fn, b_fn)
156    }
157
158    proof fn lemma_parse_sound_consumption(&self, ibuf: Seq<u8>) {
159        let (p, c, b) = *self;
160        let (p_fn, c_fn, b_fn) = (
161            |i: Seq<u8>| p.spec_parse(i),
162            |v: Blen::T| c.consistent(v),
163            |v: Blen::T| b.byte_len(v),
164        );
165        assert(self.sound_inv());
166        if let Some((n, v)) = self.spec_parse(ibuf) {
167            assert(sound_parser(p_fn, c_fn, b_fn));
168            assert(p_fn(ibuf) == Some((n, v)));
169            assert(b_fn(v) == n);
170        }
171    }
172
173    proof fn lemma_parse_sound_value(&self, ibuf: Seq<u8>) {
174        let (p, c, b) = *self;
175        let (p_fn, c_fn, b_fn) = (
176            |i: Seq<u8>| p.spec_parse(i),
177            |v: Blen::T| c.consistent(v),
178            |v: Blen::T| b.byte_len(v),
179        );
180        assert(self.sound_inv());
181        if let Some((n, v)) = self.spec_parse(ibuf) {
182            assert(sound_parser(p_fn, c_fn, b_fn));
183            assert(p_fn(ibuf) == Some((n, v)));
184            assert(c_fn(v));
185        }
186    }
187}
188
189impl<SpecP, Cnstcy, Blen> Productive for (SpecP, Cnstcy, Blen) where
190    Blen: SpecByteLen,
191    SpecP: SpecParser<PVal = Blen::T>,
192    Cnstcy: Consistency<Val = Blen::T>,
193 {
194    open spec fn productive_inv(&self) -> bool {
195        let (p, _, _) = *self;
196        let p_fn = |ibuf| p.spec_parse(ibuf);
197        productive_parser(p_fn)
198    }
199
200    proof fn lemma_productive(&self, ibuf: Seq<u8>) {
201        let (p, _, _) = *self;
202        let p_fn = |i: Seq<u8>| p.spec_parse(i);
203        assert(self.productive_inv());
204        if let Some((n, v)) = self.spec_parse(ibuf) {
205            assert(productive_parser(p_fn));
206            assert(p_fn(ibuf) == Some((n, v)));
207            assert(n > 0);
208        }
209    }
210}
211
212/// The functional version of [`SoundParser`].
213pub open spec fn sound_parser<T>(
214    parser: ParserFnSpec<T>,
215    consistent: PredFnSpec<T>,
216    byte_len: ByteLenFnSpec<T>,
217) -> bool {
218    forall|input: Seq<u8>| #[trigger]
219        parser(input) matches Some((n, v)) ==> {
220            &&& consistent(v)
221            &&& byte_len(v) == n
222        }
223}
224
225/// The functional version of [`Productive`].
226pub open spec fn productive_parser<T>(parser: ParserFnSpec<T>) -> bool {
227    forall|input: Seq<u8>| #[trigger] parser(input) matches Some((n, _)) ==> n > 0
228}
229
230pub type BundledSpecs<T> = (
231    PredFnSpec<T>,
232    ByteLenFnSpec<T>,
233    ParserFnSpec<T>,
234    SerializerFnSpec<T>,
235    SerializerDPSFnSpec<T>,
236);
237
238pub type ParamRecSpecs<P, T> = spec_fn(P) -> BundledSpecs<T>;
239
240pub open spec fn parser_specs<T>(bundled: BundledSpecs<T>) -> ParserSpecs<
241    ParserFnSpec<T>,
242    PredFnSpec<T>,
243    ByteLenFnSpec<T>,
244> {
245    (bundled.2, bundled.0, bundled.1)
246}
247
248pub open spec fn serializer_specs<T>(bundled: BundledSpecs<T>) -> SerializerSpecs<
249    SerializerFnSpec<T>,
250    ByteLenFnSpec<T>,
251> {
252    (bundled.3, bundled.1)
253}
254
255impl<T> Consistency for BundledSpecs<T> {
256    type Val = T;
257
258    open spec fn consistent(&self, v: Self::Val) -> bool {
259        (self.0)(v)
260    }
261}
262
263impl<T> SpecByteLen for BundledSpecs<T> {
264    type T = T;
265
266    open spec fn byte_len(&self, v: Self::T) -> nat {
267        (self.1)(v)
268    }
269}
270
271impl<T> SpecParser for BundledSpecs<T> {
272    type PVal = T;
273
274    open spec fn spec_parse(&self, input: Seq<u8>) -> Option<(int, Self::PVal)> {
275        (self.2)(input)
276    }
277}
278
279impl<T> SpecSerializer for BundledSpecs<T> {
280    type SVal = T;
281
282    open spec fn spec_serialize(&self, v: Self::SVal) -> Seq<u8> {
283        (self.3)(v)
284    }
285}
286
287impl<T> SpecSerializerDps for BundledSpecs<T> {
288    type SValue = T;
289
290    open spec fn spec_serialize_dps(&self, v: Self::SValue, obuf: Seq<u8>) -> Seq<u8> {
291        (self.4)(v, obuf)
292    }
293}
294
295impl<T> SafeParser for BundledSpecs<T> {
296    open spec fn safe_inv(&self) -> bool {
297        parser_specs(*self).safe_inv()
298    }
299
300    proof fn lemma_parse_safe(&self, ibuf: Seq<u8>) {
301        parser_specs(*self).lemma_parse_safe(ibuf);
302    }
303}
304
305impl<T> SoundParser for BundledSpecs<T> {
306    open spec fn sound_inv(&self) -> bool {
307        parser_specs(*self).sound_inv()
308    }
309
310    proof fn lemma_parse_sound_consumption(&self, ibuf: Seq<u8>) {
311        parser_specs(*self).lemma_parse_sound_consumption(ibuf);
312    }
313
314    proof fn lemma_parse_sound_value(&self, ibuf: Seq<u8>) {
315        parser_specs(*self).lemma_parse_sound_value(ibuf);
316    }
317}
318
319impl<T> Productive for BundledSpecs<T> {
320    open spec fn productive_inv(&self) -> bool {
321        parser_specs(*self).productive_inv()
322    }
323
324    proof fn lemma_productive(&self, ibuf: Seq<u8>) {
325        parser_specs(*self).lemma_productive(ibuf);
326    }
327}
328
329impl<T> GoodSerializer for BundledSpecs<T> {
330    open spec fn serialize_inv(&self) -> bool {
331        serializer_specs(*self).serialize_inv()
332    }
333
334    proof fn lemma_serialize_len(&self, v: Self::SVal) {
335        serializer_specs(*self).lemma_serialize_len(v);
336    }
337}
338
339impl<T> NonTailFmt for BundledSpecs<T> {
340    open spec fn serialize_dps_inv(&self) -> bool {
341        let (_, b, _, _, s_dps) = *self;
342        non_tail_fmt_dps(s_dps, b)
343    }
344
345    proof fn lemma_serialize_dps_prepend(&self, v: Self::SValue, obuf: Seq<u8>) {
346        let (_, b, _, _, s_dps) = *self;
347        let witness = choose|w: Seq<u8>| (s_dps)(v, obuf) == w + obuf;
348        assert((s_dps)(v, obuf) == witness + obuf);
349    }
350
351    proof fn lemma_serialize_dps_len(&self, v: Self::SValue, obuf: Seq<u8>) {
352        let (_, b, _, _, s_dps) = *self;
353        assert((s_dps)(v, obuf).len() - obuf.len() == (b)(v));
354    }
355}
356
357/// Defines one level of a recursive format for use with [`super::FixWith`].
358pub trait SpecRecBody {
359    type Param;
360
361    type T;
362
363    type Body: SpecCombinator<T = Self::T>;
364
365    /// Define one recursive unfolding for `param`, where `rec` provides callbacks for all
366    /// recursive positions in the body.
367    spec fn spec_body(
368        &self,
369        param: Self::Param,
370        rec: ParamRecSpecs<Self::Param, Self::T>,
371    ) -> Self::Body;
372}
373
374/// Safety preservation for recursive bodies.
375pub trait SafeParserRecBody: SpecRecBody where Self::Body: SafeParser {
376    proof fn lemma_body_safe_inv_preservation(
377        &self,
378        param: Self::Param,
379        rec: ParamRecSpecs<Self::Param, Self::T>,
380    )
381        requires
382            forall|p: Self::Param| #![trigger rec(p)] rec(p).safe_inv(),
383        ensures
384            self.spec_body(param, rec).safe_inv(),
385    ;
386}
387
388/// Soundness preservation for recursive bodies.
389pub trait SoundParserRecBody: SpecRecBody where Self::Body: SoundParser {
390    proof fn lemma_body_sound_inv_preservation(
391        &self,
392        param: Self::Param,
393        rec: ParamRecSpecs<Self::Param, Self::T>,
394    )
395        requires
396            forall|p: Self::Param| #![trigger rec(p)] rec(p).sound_inv(),
397        ensures
398            self.spec_body(param, rec).sound_inv(),
399    ;
400}
401
402/// Productivity preservation for recursive bodies.
403pub trait ProductiveRecBody: SafeParserRecBody where Self::Body: Productive {
404    proof fn lemma_body_productive_inv_preservation(
405        &self,
406        param: Self::Param,
407        rec: ParamRecSpecs<Self::Param, Self::T>,
408    )
409        requires
410            forall|p: Self::Param| #![trigger rec(p)] rec(p).safe_inv(),
411            forall|p: Self::Param| #![trigger rec(p)] rec(p).productive_inv(),
412        ensures
413            self.spec_body(param, rec).productive_inv(),
414    ;
415}
416
417/// Serializer's properties preservation for recursive bodies.
418pub trait GoodSerializerRecBody: SpecRecBody where Self::Body: GoodSerializer {
419    proof fn lemma_s_body_serialize_inv_preservation(
420        &self,
421        param: Self::Param,
422        rec: ParamRecSpecs<Self::Param, Self::T>,
423    )
424        requires
425            forall|p: Self::Param| #![trigger rec(p)] rec(p).serialize_inv(),
426        ensures
427            self.spec_body(param, rec).serialize_inv(),
428    ;
429}
430
431/// DPS serializer's properties preservation for recursive bodies.
432pub trait NonTailFmtRecBody: SpecRecBody where Self::Body: NonTailFmt {
433    proof fn lemma_s_body_dps_serialize_dps_inv_preservation(
434        &self,
435        param: Self::Param,
436        rec: ParamRecSpecs<Self::Param, Self::T>,
437    )
438        requires
439            forall|p: Self::Param| #![trigger rec(p)] rec(p).serialize_dps_inv(),
440        ensures
441            self.spec_body(param, rec).serialize_dps_inv(),
442    ;
443}
444
445impl<const LIMIT: usize, Body, Param> SpecByteLen for super::FixWith<LIMIT, Body, Param> where
446    Body: SpecRecBody,
447    Param: DeepView<V = Body::Param>,
448 {
449    type T = Body::T;
450
451    open spec fn byte_len(&self, v: Self::T) -> nat {
452        Self::byte_len_gas(&self.0, LIMIT as nat, self.1.deep_view(), v)
453    }
454}
455
456impl<const LIMIT: usize, Body, Param> Consistency for super::FixWith<LIMIT, Body, Param> where
457    Body: SpecRecBody,
458    Param: DeepView<V = Body::Param>,
459 {
460    type Val = Body::T;
461
462    open spec fn consistent(&self, v: Self::Val) -> bool {
463        Self::consistent_gas(&self.0, LIMIT as nat, self.1.deep_view(), v)
464    }
465}
466
467impl<const LIMIT: usize, Body, Param> SpecParser for super::FixWith<LIMIT, Body, Param> where
468    Body: SpecRecBody,
469    Param: DeepView<V = Body::Param>,
470 {
471    type PVal = Body::T;
472
473    open spec fn spec_parse(&self, input: Seq<u8>) -> Option<(int, Self::PVal)> {
474        Self::spec_parse_gas(&self.0, LIMIT as nat, self.1.deep_view(), input)
475    }
476}
477
478impl<const LIMIT: usize, Body, Param> SpecSerializer for super::FixWith<LIMIT, Body, Param> where
479    Body: SpecRecBody,
480    Param: DeepView<V = Body::Param>,
481 {
482    type SVal = Body::T;
483
484    open spec fn spec_serialize(&self, v: Self::SVal) -> Seq<u8> {
485        Self::spec_serialize_gas(&self.0, LIMIT as nat, self.1.deep_view(), v)
486    }
487}
488
489impl<const LIMIT: usize, Body, Param> SpecSerializerDps for super::FixWith<
490    LIMIT,
491    Body,
492    Param,
493> where Body: SpecRecBody, Param: DeepView<V = Body::Param> {
494    type SValue = Body::T;
495
496    open spec fn spec_serialize_dps(&self, v: Self::SValue, obuf: Seq<u8>) -> Seq<u8> {
497        Self::spec_serialize_dps_gas(&self.0, LIMIT as nat, self.1.deep_view(), v, obuf)
498    }
499}
500
501impl<const LIMIT: usize, Body, Param> super::FixWith<LIMIT, Body, Param> where
502    Body: SpecRecBody,
503    Param: DeepView<V = Body::Param>,
504 {
505    pub open spec fn byte_len_gas(body: &Body, gas: nat, param: Body::Param, v: Body::T) -> nat
506        decreases gas, 2nat,
507    {
508        body.spec_body(param, Self::specs_callback(&body, gas)).byte_len(v)
509    }
510
511    pub open spec fn consistent_gas(body: &Body, gas: nat, param: Body::Param, v: Body::T) -> bool
512        decreases gas, 2nat,
513    {
514        body.spec_body(param, Self::specs_callback(&body, gas)).consistent(v)
515    }
516
517    pub open spec fn spec_parse_gas(
518        body: &Body,
519        gas: nat,
520        param: Body::Param,
521        input: Seq<u8>,
522    ) -> Option<(int, Body::T)>
523        decreases gas, 2nat,
524    {
525        body.spec_body(param, Self::specs_callback(&body, gas)).spec_parse(input)
526    }
527
528    pub open spec fn spec_serialize_gas(
529        body: &Body,
530        gas: nat,
531        param: Body::Param,
532        v: Body::T,
533    ) -> Seq<u8>
534        decreases gas, 2nat,
535    {
536        body.spec_body(param, Self::specs_callback(&body, gas)).spec_serialize(v)
537    }
538
539    pub open spec fn spec_serialize_dps_gas(
540        body: &Body,
541        gas: nat,
542        param: Body::Param,
543        v: Body::T,
544        obuf: Seq<u8>,
545    ) -> Seq<u8>
546        decreases gas, 2nat,
547    {
548        body.spec_body(param, Self::specs_callback(&body, gas)).spec_serialize_dps(v, obuf)
549    }
550
551    pub open spec fn spec_parse_callback(body: &Body, gas: nat, param: Body::Param) -> ParserFnSpec<
552        Body::T,
553    >
554        decreases gas, 0nat,
555    {
556        |ibuf: Seq<u8>|
557            if gas > 0 {
558                Self::spec_parse_gas(body, (gas - 1) as nat, param, ibuf)
559            } else {
560                None
561            }
562    }
563
564    pub open spec fn consistent_callback(body: &Body, gas: nat, param: Body::Param) -> PredFnSpec<
565        Body::T,
566    >
567        decreases gas, 0nat,
568    {
569        |vv: Body::T|
570            if gas > 0 {
571                Self::consistent_gas(body, (gas - 1) as nat, param, vv)
572            } else {
573                false
574            }
575    }
576
577    pub open spec fn byte_len_callback(body: &Body, gas: nat, param: Body::Param) -> ByteLenFnSpec<
578        Body::T,
579    >
580        decreases gas, 0nat,
581    {
582        |vv: Body::T|
583            if gas > 0 {
584                Self::byte_len_gas(body, (gas - 1) as nat, param, vv)
585            } else {
586                0
587            }
588    }
589
590    pub open spec fn spec_serialize_callback(
591        body: &Body,
592        gas: nat,
593        param: Body::Param,
594    ) -> SerializerFnSpec<Body::T>
595        decreases gas, 0nat,
596    {
597        |vv: Body::T|
598            if gas > 0 {
599                Self::spec_serialize_gas(body, (gas - 1) as nat, param, vv)
600            } else {
601                Seq::empty()
602            }
603    }
604
605    pub open spec fn spec_serialize_dps_callback(
606        body: &Body,
607        gas: nat,
608        param: Body::Param,
609    ) -> SerializerDPSFnSpec<Body::T>
610        decreases gas, 0nat,
611    {
612        |vv: Body::T, obuf: Seq<u8>|
613            if gas > 0 {
614                Self::spec_serialize_dps_gas(body, (gas - 1) as nat, param, vv, obuf)
615            } else {
616                obuf
617            }
618    }
619
620    /// Bundled callbacks used when unfolding one recursive level.
621    pub open spec fn specs_callback(body: &Body, gas: nat) -> ParamRecSpecs<Body::Param, Body::T>
622        decreases gas, 1nat,
623    {
624        |param: Body::Param|
625            (
626                Self::consistent_callback(&body, gas, param),
627                Self::byte_len_callback(&body, gas, param),
628                Self::spec_parse_callback(&body, gas, param),
629                Self::spec_serialize_callback(&body, gas, param),
630                Self::spec_serialize_dps_callback(&body, gas, param),
631            )
632    }
633}
634
635impl<const LIMIT: usize, Body, Param> super::FixWith<LIMIT, Body, Param> where
636    Body: SafeParserRecBody,
637    Body::Body: SafeParser,
638    Param: DeepView<V = Body::Param>,
639 {
640    pub proof fn lemma_specs_callback_safe_inv(&self, gas: nat, param: Body::Param)
641        ensures
642            Self::specs_callback(&self.0, gas)(param).safe_inv(),
643        decreases gas,
644    {
645        let callback = Self::specs_callback(&self.0, gas);
646
647        assert forall|p: Body::Param, input: Seq<u8>| #[trigger]
648            callback(p).2(input) matches Some((n, _v)) ==> 0 <= n <= input.len() by {
649            if let Some((n, v)) = callback(p).2(input) {
650                self.safe_parser_by_induction((gas - 1) as nat, p, input, n, v);
651                assert(Self::spec_parse_gas(&self.0, (gas - 1) as nat, p, input) == Some((n, v)));
652            }
653        }
654
655        assert(callback(param).safe_inv());
656    }
657
658    /// Inductive proof that `spec_parse_gas` satisfies [`safe_parser`].
659    pub(crate) proof fn safe_parser_by_induction(
660        &self,
661        gas: nat,
662        param: Body::Param,
663        input: Seq<u8>,
664        n: int,
665        v: Body::T,
666    )
667        ensures
668            Self::spec_parse_gas(&self.0, gas, param, input) == Some((n, v)) ==> 0 <= n
669                <= input.len(),
670        decreases gas,
671    {
672        if !(Self::spec_parse_gas(&self.0, gas, param, input) == Some((n, v))) {
673            return;
674        }
675        let callback = Self::specs_callback(&self.0, gas);
676        let callback_p = callback(param).2;
677
678        assert forall|p: Body::Param, rem: Seq<u8>| #[trigger]
679            callback(p).2(rem) matches Some((nn, _vv)) ==> 0 <= nn <= rem.len() by {
680            if let Some((nn, vv)) = callback(p).2(rem) {
681                self.safe_parser_by_induction((gas - 1) as nat, p, rem, nn, vv);
682                assert(Self::spec_parse_gas(&self.0, (gas - 1) as nat, p, rem) == Some((nn, vv)));
683                assert(0 <= nn <= rem.len());
684            }
685        }
686
687        assert forall|p: Body::Param| #[trigger] callback(p).safe_inv() by {
688            assert(safe_parser(callback(p).2));
689        }
690
691        self.0.lemma_body_safe_inv_preservation(param, callback);
692        let body = self.0.spec_body(param, callback);
693        body.lemma_parse_safe(input);
694
695        assert(Self::spec_parse_gas(&self.0, gas, param, input) == body.spec_parse(input));
696    }
697}
698
699impl<const LIMIT: usize, Body, Param> SafeParser for super::FixWith<LIMIT, Body, Param> where
700    Body: SafeParserRecBody,
701    Body::Body: SafeParser,
702    Param: DeepView<V = Body::Param>,
703 {
704    proof fn lemma_parse_safe(&self, ibuf: Seq<u8>) {
705        if let Some((n, v)) = self.spec_parse(ibuf) {
706            self.safe_parser_by_induction(LIMIT as nat, self.1.deep_view(), ibuf, n, v);
707        }
708    }
709}
710
711impl<const LIMIT: usize, Body, Param> super::FixWith<LIMIT, Body, Param> where
712    Body: SoundParserRecBody,
713    Body::Body: SoundParser,
714    Param: DeepView<V = Body::Param>,
715 {
716    /// Inductive proof that `spec_parse_gas` satisfies [`sound_parser`].
717    pub proof fn sound_parser_by_induction(
718        &self,
719        gas: nat,
720        param: Body::Param,
721        input: Seq<u8>,
722        n: int,
723        v: Body::T,
724    )
725        ensures
726            Self::spec_parse_gas(&self.0, gas, param, input) == Some((n, v)) ==> {
727                &&& Self::consistent_gas(&self.0, gas, param, v)
728                &&& Self::byte_len_gas(&self.0, gas, param, v) == n
729            },
730        decreases gas,
731    {
732        if !(Self::spec_parse_gas(&self.0, gas, param, input) == Some((n, v))) {
733            return;
734        }
735        let callback = Self::specs_callback(&self.0, gas);
736        let callback_p = callback(param).2;
737        let callback_c = callback(param).0;
738        let callback_b = callback(param).1;
739
740        assert forall|p: Body::Param, rem: Seq<u8>| #[trigger]
741            callback(p).2(rem) matches Some((nn, vv)) ==> {
742                &&& callback(p).0(vv)
743                &&& callback(p).1(vv) == nn
744            } by {
745            if let Some((nn, vv)) = callback(p).2(rem) {
746                self.sound_parser_by_induction((gas - 1) as nat, p, rem, nn, vv);
747                assert(Self::spec_parse_gas(&self.0, (gas - 1) as nat, p, rem) == Some((nn, vv)));
748                assert(Self::consistent_gas(&self.0, (gas - 1) as nat, p, vv) == callback(p).0(vv));
749                assert(Self::byte_len_gas(&self.0, (gas - 1) as nat, p, vv) == callback(p).1(vv));
750                assert(callback(p).0(vv));
751                assert(callback(p).1(vv) == nn);
752            }
753        }
754
755        assert forall|p: Body::Param| #[trigger] callback(p).sound_inv() by {
756            assert(sound_parser(callback(p).2, callback(p).0, callback(p).1));
757        }
758
759        self.0.lemma_body_sound_inv_preservation(param, callback);
760        let body = self.0.spec_body(param, callback);
761
762        body.lemma_parse_sound_consumption(input);
763        body.lemma_parse_sound_value(input);
764
765        assert(Self::spec_parse_gas(&self.0, gas, param, input) == body.spec_parse(input));
766        assert(Self::consistent_gas(&self.0, gas, param, v) == body.consistent(v));
767        assert(Self::byte_len_gas(&self.0, gas, param, v) == body.byte_len(v));
768    }
769}
770
771impl<const LIMIT: usize, Body, Param> SoundParser for super::FixWith<LIMIT, Body, Param> where
772    Body: SoundParserRecBody,
773    Body::Body: SoundParser,
774    Param: DeepView<V = Body::Param>,
775 {
776    proof fn lemma_parse_sound_consumption(&self, ibuf: Seq<u8>) {
777        if let Some((n, v)) = self.spec_parse(ibuf) {
778            self.sound_parser_by_induction(LIMIT as nat, self.1.deep_view(), ibuf, n, v);
779        }
780    }
781
782    proof fn lemma_parse_sound_value(&self, ibuf: Seq<u8>) {
783        if let Some((n, v)) = self.spec_parse(ibuf) {
784            self.sound_parser_by_induction(LIMIT as nat, self.1.deep_view(), ibuf, n, v);
785        }
786    }
787}
788
789impl<const LIMIT: usize, Body, Param> super::FixWith<LIMIT, Body, Param> where
790    Body: ProductiveRecBody,
791    Body::Body: Productive,
792    Param: DeepView<V = Body::Param>,
793 {
794    /// Establishes productivity for one bundled recursive callback.
795    pub proof fn lemma_specs_callback_productive_inv(&self, gas: nat, param: Body::Param)
796        ensures
797            Self::specs_callback(&self.0, gas)(param).productive_inv(),
798        decreases gas,
799    {
800        let callback = Self::specs_callback(&self.0, gas);
801
802        assert forall|input: Seq<u8>| #[trigger]
803            callback(param).2(input) matches Some((n, _v)) ==> n > 0 by {
804            if let Some((n, v)) = callback(param).2(input) {
805                self.productive_by_induction((gas - 1) as nat, param, input, n, v);
806                assert(Self::spec_parse_gas(&self.0, (gas - 1) as nat, param, input) == Some(
807                    (n, v),
808                ));
809            }
810        }
811
812        assert(callback(param).productive_inv());
813    }
814
815    /// Inductive proof that `spec_parse_gas` satisfies [`productive_parser`].
816    pub(crate) proof fn productive_by_induction(
817        &self,
818        gas: nat,
819        param: Body::Param,
820        input: Seq<u8>,
821        n: int,
822        v: Body::T,
823    )
824        ensures
825            Self::spec_parse_gas(&self.0, gas, param, input) == Some((n, v)) ==> n > 0,
826        decreases gas,
827    {
828        if !(Self::spec_parse_gas(&self.0, gas, param, input) == Some((n, v))) {
829            return;
830        }
831        let callback = Self::specs_callback(&self.0, gas);
832
833        assert forall|p: Body::Param, rem: Seq<u8>| #[trigger]
834            callback(p).2(rem) matches Some((nn, _vv)) ==> 0 <= nn <= rem.len() by {
835            if let Some((nn, vv)) = callback(p).2(rem) {
836                self.safe_parser_by_induction((gas - 1) as nat, p, rem, nn, vv);
837                assert(Self::spec_parse_gas(&self.0, (gas - 1) as nat, p, rem) == Some((nn, vv)));
838                assert(0 <= nn <= rem.len());
839            }
840        }
841
842        assert forall|p: Body::Param| #[trigger] callback(p).safe_inv() by {
843            assert(safe_parser(callback(p).2));
844        }
845
846        assert forall|p: Body::Param, rem: Seq<u8>| #[trigger]
847            callback(p).2(rem) matches Some((nn, _vv)) ==> nn > 0 by {
848            if let Some((nn, vv)) = callback(p).2(rem) {
849                self.productive_by_induction((gas - 1) as nat, p, rem, nn, vv);
850                assert(Self::spec_parse_gas(&self.0, (gas - 1) as nat, p, rem) == Some((nn, vv)));
851                assert(nn > 0);
852            }
853        }
854
855        assert forall|p: Body::Param| #[trigger] callback(p).productive_inv() by {
856            assert(productive_parser(callback(p).2));
857        }
858
859        self.0.lemma_body_safe_inv_preservation(param, callback);
860        self.0.lemma_body_productive_inv_preservation(param, callback);
861
862        let body = self.0.spec_body(param, callback);
863        body.lemma_parse_safe(input);
864        body.lemma_productive(input);
865
866        assert(Self::spec_parse_gas(&self.0, gas, param, input) == body.spec_parse(input));
867    }
868}
869
870impl<const LIMIT: usize, Body, Param> Productive for super::FixWith<LIMIT, Body, Param> where
871    Body: ProductiveRecBody,
872    Body::Body: Productive,
873    Param: DeepView<V = Body::Param>,
874 {
875    proof fn lemma_productive(&self, ibuf: Seq<u8>) {
876        if let Some((n, v)) = self.spec_parse(ibuf) {
877            self.productive_by_induction(LIMIT as nat, self.1.deep_view(), ibuf, n, v);
878        }
879    }
880}
881
882impl<const LIMIT: usize, Body, Param> super::FixWith<LIMIT, Body, Param> where
883    Body: GoodSerializerRecBody,
884    Body::Body: GoodSerializer,
885    Param: DeepView<V = Body::Param>,
886 {
887    /// Inductive proof that `spec_serialize_gas` satisfies [`good_serializer_fn`].
888    proof fn good_serializer_by_induction(&self, gas: nat, param: Body::Param, v: Body::T)
889        ensures
890            Self::spec_serialize_gas(&self.0, gas, param, v).len() == Self::byte_len_gas(
891                &self.0,
892                gas,
893                param,
894                v,
895            ),
896        decreases gas,
897    {
898        let callback = Self::specs_callback(&self.0, gas);
899
900        assert forall|p: Body::Param, vv: Body::T| #[trigger]
901            callback(p).3(vv).len() == callback(p).1(vv) by {
902            if gas > 0 {
903                self.good_serializer_by_induction((gas - 1) as nat, p, vv);
904            }
905        }
906
907        assert forall|p: Body::Param| #[trigger] callback(p).serialize_inv() by {
908            assert(good_serializer_fn(callback(p).3, callback(p).1));
909        }
910
911        self.0.lemma_s_body_serialize_inv_preservation(param, callback);
912        let body = self.0.spec_body(param, callback);
913
914        body.lemma_serialize_len(v);
915
916        assert(Self::spec_serialize_gas(&self.0, gas, param, v) == body.spec_serialize(v));
917        assert(Self::byte_len_gas(&self.0, gas, param, v) == body.byte_len(v));
918    }
919}
920
921impl<const LIMIT: usize, Body, Param> GoodSerializer for super::FixWith<LIMIT, Body, Param> where
922    Body: GoodSerializerRecBody,
923    Body::Body: GoodSerializer,
924    Param: DeepView<V = Body::Param>,
925 {
926    proof fn lemma_serialize_len(&self, v: Self::SVal) {
927        self.good_serializer_by_induction(LIMIT as nat, self.1.deep_view(), v);
928    }
929}
930
931impl<const LIMIT: usize, Body, Param> super::FixWith<LIMIT, Body, Param> where
932    Body: NonTailFmtRecBody,
933    Body::Body: NonTailFmt,
934    Param: DeepView<V = Body::Param>,
935 {
936    /// Inductive proof that `spec_serialize_gas` satisfies [`non_tail_fmt_dps`].
937    pub(crate) proof fn nontail_dps_by_induction(
938        &self,
939        gas: nat,
940        param: Body::Param,
941        v: Body::T,
942        obuf: Seq<u8>,
943    )
944        ensures
945            exists|new_buf: Seq<u8>|
946                (#[trigger] Self::spec_serialize_dps_gas(&self.0, gas, param, v, obuf) == new_buf
947                    + obuf),
948            Self::spec_serialize_dps_gas(&self.0, gas, param, v, obuf).len() - obuf.len()
949                == Self::byte_len_gas(&self.0, gas, param, v),
950        decreases gas, 1nat,
951    {
952        let callback = Self::specs_callback(&self.0, gas);
953
954        assert forall|p: Body::Param, vv: Body::T, buf: Seq<u8>| #[trigger]
955            callback(p).4(vv, buf).len() - buf.len() == callback(p).1(vv) by {
956            if gas > 0 {
957                self.nontail_dps_by_induction((gas - 1) as nat, p, vv, buf);
958            }
959        }
960
961        assert forall|p: Body::Param, vv: Body::T, buf: Seq<u8>|
962            exists|new_buf: Seq<u8>| (#[trigger] callback(p).4(vv, buf)) == new_buf + buf by {
963            if gas > 0 {
964                self.nontail_dps_by_induction((gas - 1) as nat, p, vv, buf);
965                let witness = choose|w: Seq<u8>|
966                    Self::spec_serialize_dps_gas(&self.0, (gas - 1) as nat, p, vv, buf) == w + buf;
967                assert(callback(p).4(vv, buf) == witness + buf);
968            } else {
969                assert(callback(p).4(vv, buf) == Seq::<u8>::empty() + buf);
970            }
971        }
972
973        assert forall|p: Body::Param| #[trigger] callback(p).serialize_dps_inv() by {
974            assert(non_tail_fmt_dps(callback(p).4, callback(p).1));
975        }
976        self.0.lemma_s_body_dps_serialize_dps_inv_preservation(param, callback);
977        let body = self.0.spec_body(param, callback);
978
979        body.lemma_serialize_dps_prepend(v, obuf);
980        body.lemma_serialize_dps_len(v, obuf);
981
982        assert(Self::spec_serialize_dps_gas(&self.0, gas, param, v, obuf)
983            == body.spec_serialize_dps(v, obuf));
984        assert(Self::byte_len_gas(&self.0, gas, param, v) == body.byte_len(v));
985    }
986}
987
988impl<const LIMIT: usize, Body, Param> NonTailFmt for super::FixWith<LIMIT, Body, Param> where
989    Body: NonTailFmtRecBody,
990    Body::Body: NonTailFmt,
991    Param: DeepView<V = Body::Param>,
992 {
993    proof fn lemma_serialize_dps_prepend(&self, v: Self::SValue, obuf: Seq<u8>) {
994        self.nontail_dps_by_induction(LIMIT as nat, self.1.deep_view(), v, obuf);
995    }
996
997    proof fn lemma_serialize_dps_len(&self, v: Self::SValue, obuf: Seq<u8>) {
998        self.nontail_dps_by_induction(LIMIT as nat, self.1.deep_view(), v, obuf);
999    }
1000}
1001
1002} // verus!