From 888c03c4c6bde5f6874985abdf076dfd76e7080b Mon Sep 17 00:00:00 2001 From: Tahina Ramananandro Date: Thu, 13 Apr 2023 18:35:01 -0700 Subject: [PATCH] import SteelC from FStarLang/FStar#2349 by @john-ml (aka tahina-pro/FStar@b52a9d11cea3ac248f773ccafbb8fa15e940fb4e) --- include/steel/steel_c.h | 10 + lib/steel/c/Steel.C.Array.Base.fsti | 749 ++++++ lib/steel/c/Steel.C.Array.fsti | 99 + lib/steel/c/Steel.C.Fields.fst | 7 + lib/steel/c/Steel.C.Fields.fsti | 177 ++ lib/steel/c/Steel.C.Model.Connection.fst | 675 ++++++ lib/steel/c/Steel.C.Model.Frac.fst | 53 + lib/steel/c/Steel.C.Model.PCM.fst | 362 +++ lib/steel/c/Steel.C.Model.PCM.fsti | 412 ++++ lib/steel/c/Steel.C.Model.Ref.Base.fst | 83 + lib/steel/c/Steel.C.Model.Ref.Base.fsti | 67 + lib/steel/c/Steel.C.Model.Ref.fst | 444 ++++ lib/steel/c/Steel.C.Model.Ref.fsti | 286 +++ lib/steel/c/Steel.C.Model.Struct.fst | 178 ++ lib/steel/c/Steel.C.Model.Uninit.fsti | 225 ++ lib/steel/c/Steel.C.Model.Union.fst | 47 + lib/steel/c/Steel.C.Model.Universe.fst | 132 ++ lib/steel/c/Steel.C.Opt.fst | 45 + lib/steel/c/Steel.C.Opt.fsti | 89 + lib/steel/c/Steel.C.Reference.fst | 388 +++ lib/steel/c/Steel.C.StdInt.Base.fst | 60 + lib/steel/c/Steel.C.StdInt.Base.fsti | 87 + lib/steel/c/Steel.C.StdInt.fst | 26 + lib/steel/c/Steel.C.StructLiteral.fsti | 472 ++++ lib/steel/c/Steel.C.Typedef.fst | 29 + lib/steel/c/Steel.C.TypedefNorm.fst | 27 + lib/steel/c/Steel.C.Typenat.fst | 4 + lib/steel/c/Steel.C.Typenat.fsti | 27 + lib/steel/c/Steel.C.Typestring.fst | 69 + lib/steel/c/Steel.C.Typestring.fsti | 166 ++ lib/steel/c/Steel.C.UnionLiteral.fst | 308 +++ lib/steel/c/Steel.C.UnionLiteral.fsti | 214 ++ lib/steel/c/Steel.ST.C.Model.Array.fst | 1043 ++++++++ lib/steel/c/Steel.ST.C.Model.Frac.fst | 44 + lib/steel/c/Steel.ST.C.Model.Frac.fsti | 66 + lib/steel/c/Steel.ST.C.Model.Ref.fst | 134 ++ lib/steel/c/Steel.ST.C.Model.Ref.fsti | 90 + lib/steel/c/Steel.ST.C.Model.Rewrite.fst | 46 + lib/steel/c/Steel.ST.C.Model.Rewrite.fsti | 113 + lib/steel/c/Steel.ST.C.Model.Struct.fst | 889 +++++++ lib/steel/c/Steel.ST.C.Model.Union.fst | 484 ++++ lib/steel/c/Steel.ST.C.Opt.fst | 46 + lib/steel/c/Steel.ST.C.Opt.fsti | 78 + lib/steel/c/Steel.ST.C.Types.Array.fsti | 977 ++++++++ lib/steel/c/Steel.ST.C.Types.Base.fsti | 269 +++ lib/steel/c/Steel.ST.C.Types.Fields.fsti | 48 + lib/steel/c/Steel.ST.C.Types.Rewrite.fsti | 2 + lib/steel/c/Steel.ST.C.Types.Scalar.fsti | 90 + lib/steel/c/Steel.ST.C.Types.Struct.Aux.fsti | 13 + lib/steel/c/Steel.ST.C.Types.Struct.fsti | 334 +++ lib/steel/c/Steel.ST.C.Types.Union.fsti | 360 +++ lib/steel/c/Steel.ST.C.Types.UserStruct.fsti | 243 ++ lib/steel/c/Steel.ST.C.Types.fst | 6 + share/steel/examples/steelc/Basetypes.md | 131 + share/steel/examples/steelc/HaclExample.fst | 145 ++ share/steel/examples/steelc/HaclExample2.fst | 129 + share/steel/examples/steelc/HaclExample3.fst | 146 ++ share/steel/examples/steelc/LList.fst | 180 ++ share/steel/examples/steelc/LList2.fst | 196 ++ share/steel/examples/steelc/Makefile | 108 + share/steel/examples/steelc/PointStruct.fst | 203 ++ share/steel/examples/steelc/PointStruct2.fst | 55 + share/steel/examples/steelc/ScalarUnion.fst | 112 + share/steel/examples/steelc/ScalarUnion2.fst | 78 + src/extraction/ExtractSteelC.fst | 489 ++++ src/extraction/ExtractSteelC.fsti | 2 + src/proofs/steelc/README | 6 + src/proofs/steelc/Steel.C.Array.Base.fst | 2102 +++++++++++++++++ src/proofs/steelc/Steel.C.Array.fst | 132 ++ src/proofs/steelc/Steel.ST.C.Types.Array.fst | 1322 +++++++++++ src/proofs/steelc/Steel.ST.C.Types.Base.fst | 1024 ++++++++ src/proofs/steelc/Steel.ST.C.Types.Fields.fst | 4 + .../steelc/Steel.ST.C.Types.Rewrite.fst | 27 + src/proofs/steelc/Steel.ST.C.Types.Scalar.fst | 104 + .../steelc/Steel.ST.C.Types.Struct.Aux.fst | 364 +++ src/proofs/steelc/Steel.ST.C.Types.Struct.fst | 115 + src/proofs/steelc/Steel.ST.C.Types.Union.fst | 445 ++++ .../steelc/Steel.ST.C.Types.UserStruct.fst | 204 ++ 78 files changed, 19215 insertions(+) create mode 100644 include/steel/steel_c.h create mode 100644 lib/steel/c/Steel.C.Array.Base.fsti create mode 100644 lib/steel/c/Steel.C.Array.fsti create mode 100644 lib/steel/c/Steel.C.Fields.fst create mode 100644 lib/steel/c/Steel.C.Fields.fsti create mode 100644 lib/steel/c/Steel.C.Model.Connection.fst create mode 100644 lib/steel/c/Steel.C.Model.Frac.fst create mode 100644 lib/steel/c/Steel.C.Model.PCM.fst create mode 100644 lib/steel/c/Steel.C.Model.PCM.fsti create mode 100644 lib/steel/c/Steel.C.Model.Ref.Base.fst create mode 100644 lib/steel/c/Steel.C.Model.Ref.Base.fsti create mode 100644 lib/steel/c/Steel.C.Model.Ref.fst create mode 100644 lib/steel/c/Steel.C.Model.Ref.fsti create mode 100644 lib/steel/c/Steel.C.Model.Struct.fst create mode 100644 lib/steel/c/Steel.C.Model.Uninit.fsti create mode 100644 lib/steel/c/Steel.C.Model.Union.fst create mode 100644 lib/steel/c/Steel.C.Model.Universe.fst create mode 100644 lib/steel/c/Steel.C.Opt.fst create mode 100644 lib/steel/c/Steel.C.Opt.fsti create mode 100644 lib/steel/c/Steel.C.Reference.fst create mode 100644 lib/steel/c/Steel.C.StdInt.Base.fst create mode 100644 lib/steel/c/Steel.C.StdInt.Base.fsti create mode 100644 lib/steel/c/Steel.C.StdInt.fst create mode 100644 lib/steel/c/Steel.C.StructLiteral.fsti create mode 100644 lib/steel/c/Steel.C.Typedef.fst create mode 100644 lib/steel/c/Steel.C.TypedefNorm.fst create mode 100644 lib/steel/c/Steel.C.Typenat.fst create mode 100644 lib/steel/c/Steel.C.Typenat.fsti create mode 100644 lib/steel/c/Steel.C.Typestring.fst create mode 100644 lib/steel/c/Steel.C.Typestring.fsti create mode 100644 lib/steel/c/Steel.C.UnionLiteral.fst create mode 100644 lib/steel/c/Steel.C.UnionLiteral.fsti create mode 100644 lib/steel/c/Steel.ST.C.Model.Array.fst create mode 100644 lib/steel/c/Steel.ST.C.Model.Frac.fst create mode 100644 lib/steel/c/Steel.ST.C.Model.Frac.fsti create mode 100644 lib/steel/c/Steel.ST.C.Model.Ref.fst create mode 100644 lib/steel/c/Steel.ST.C.Model.Ref.fsti create mode 100644 lib/steel/c/Steel.ST.C.Model.Rewrite.fst create mode 100644 lib/steel/c/Steel.ST.C.Model.Rewrite.fsti create mode 100644 lib/steel/c/Steel.ST.C.Model.Struct.fst create mode 100644 lib/steel/c/Steel.ST.C.Model.Union.fst create mode 100644 lib/steel/c/Steel.ST.C.Opt.fst create mode 100644 lib/steel/c/Steel.ST.C.Opt.fsti create mode 100644 lib/steel/c/Steel.ST.C.Types.Array.fsti create mode 100644 lib/steel/c/Steel.ST.C.Types.Base.fsti create mode 100644 lib/steel/c/Steel.ST.C.Types.Fields.fsti create mode 100644 lib/steel/c/Steel.ST.C.Types.Rewrite.fsti create mode 100644 lib/steel/c/Steel.ST.C.Types.Scalar.fsti create mode 100644 lib/steel/c/Steel.ST.C.Types.Struct.Aux.fsti create mode 100644 lib/steel/c/Steel.ST.C.Types.Struct.fsti create mode 100644 lib/steel/c/Steel.ST.C.Types.Union.fsti create mode 100644 lib/steel/c/Steel.ST.C.Types.UserStruct.fsti create mode 100644 lib/steel/c/Steel.ST.C.Types.fst create mode 100644 share/steel/examples/steelc/Basetypes.md create mode 100644 share/steel/examples/steelc/HaclExample.fst create mode 100644 share/steel/examples/steelc/HaclExample2.fst create mode 100644 share/steel/examples/steelc/HaclExample3.fst create mode 100644 share/steel/examples/steelc/LList.fst create mode 100644 share/steel/examples/steelc/LList2.fst create mode 100644 share/steel/examples/steelc/Makefile create mode 100644 share/steel/examples/steelc/PointStruct.fst create mode 100644 share/steel/examples/steelc/PointStruct2.fst create mode 100644 share/steel/examples/steelc/ScalarUnion.fst create mode 100644 share/steel/examples/steelc/ScalarUnion2.fst create mode 100644 src/extraction/ExtractSteelC.fst create mode 100644 src/extraction/ExtractSteelC.fsti create mode 100644 src/proofs/steelc/README create mode 100644 src/proofs/steelc/Steel.C.Array.Base.fst create mode 100644 src/proofs/steelc/Steel.C.Array.fst create mode 100644 src/proofs/steelc/Steel.ST.C.Types.Array.fst create mode 100644 src/proofs/steelc/Steel.ST.C.Types.Base.fst create mode 100644 src/proofs/steelc/Steel.ST.C.Types.Fields.fst create mode 100644 src/proofs/steelc/Steel.ST.C.Types.Rewrite.fst create mode 100644 src/proofs/steelc/Steel.ST.C.Types.Scalar.fst create mode 100644 src/proofs/steelc/Steel.ST.C.Types.Struct.Aux.fst create mode 100644 src/proofs/steelc/Steel.ST.C.Types.Struct.fst create mode 100644 src/proofs/steelc/Steel.ST.C.Types.Union.fst create mode 100644 src/proofs/steelc/Steel.ST.C.Types.UserStruct.fst diff --git a/include/steel/steel_c.h b/include/steel/steel_c.h new file mode 100644 index 000000000..d8a2e116c --- /dev/null +++ b/include/steel/steel_c.h @@ -0,0 +1,10 @@ +#ifndef __STEEL_C +#define __STEEL_C + +// This file is a stopgap measure to avoid creating a Karamel branch. +// Ideally, it should be empty, and its contents should be added to +// krmllib.h + +typedef void* Steel_ST_C_Types_Base_void_ptr; + +#endif // ndef __STEEL_C diff --git a/lib/steel/c/Steel.C.Array.Base.fsti b/lib/steel/c/Steel.C.Array.Base.fsti new file mode 100644 index 000000000..6cadb2ae1 --- /dev/null +++ b/lib/steel/c/Steel.C.Array.Base.fsti @@ -0,0 +1,749 @@ +(* + Copyright 2021 Microsoft Research + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*) + +module Steel.C.Array.Base +include Steel.C.StdInt.Base +open Steel.Memory +open Steel.FractionalPermission +open Steel.Effect +open FStar.Ghost +open Steel.Effect.Atomic + +open Steel.C.Typedef +open Steel.C.Model.PCM +open Steel.C.Fields +open Steel.C.Typenat + +#set-options "--ide_id_info_off" + +/// A library for arrays in Steel +/// TODO: add back support for fractional permissions, or even any element view + +val array_pcm_carrier (t: Type u#0) (n: Ghost.erased size_t) : Type u#0 + +val array_pcm (t: Type u#0) (n: Ghost.erased size_t) : Tot (Steel.C.Model.PCM.pcm (array_pcm_carrier t n)) + +// FIXME: how to produce array type t[n] as the type of some struct field? +let array_view_type (t: Type u#0) (n: size_t) +: Type u#0 = + Seq.lseq t (size_v n) + +/// A variant of array_view_type, which records the length of the +/// array in Type as a Steel.C.Typenat, for extraction +let size_t_of (n': Type u#0) = n:size_t{n' == nat_t_of_nat (size_v n)} +let array_view_type_sized (t: Type u#0) (n': Type u#0) (n: size_t_of n') +: Type u#0 += array_view_type t n + +val array_view (t: Type u#0) (n: size_t) + : Pure (Steel.C.Model.Ref.sel_view (array_pcm t n) (array_view_type t n) false) + (requires (size_v n > 0)) + (ensures (fun _ -> True)) + +/// Abstract datatype for a Steel array of type [t] +/// We model it as three parts: +/// - a pure part, which represents the beginning of the array, and should extract to t* +/// - a ghost part, which represents the end of the array, and should be erased at extraction +/// - a refinement, because KaRaMeL does not support inlining of dependent pair types where one part is ghost. +val array_or_null_from (base: Type0) (t: Type0) : Tot Type0 +[@@erasable] +val array_or_null_to (base: Type0) (t: Type0) : Tot Type0 +val array_or_null_spec (#base: Type0) (#t: Type0) (x: (array_or_null_from base t & array_or_null_to base t)) : Tot prop +inline_for_extraction +let array_or_null (base: Type u#0) (t: Type u#0) : Type u#0 = (x: (array_or_null_from base t & array_or_null_to base t) { array_or_null_spec x }) + +/// Returns the length of the array. Usable for specification and proof purposes, +/// as modeled by the GTot effect +val len (#base: Type) (#t: Type) (a: array_or_null base t) : GTot size_t +let length (#base: Type) (#t: Type) (a: array_or_null base t) : GTot nat = size_v (len a) + + +val null_from (base: Type u#0) (t: Type u#0) : Tot (array_or_null_from base t) +val null_to (base: Type u#0) (t: Type u#0) : Pure (array_or_null_to base t) (requires True) (ensures (fun r0 -> + array_or_null_spec (null_from base t, r0) /\ + len (null_from base t, r0) == zero_size)) + +val null_to_unique + (#base #t: Type) + (to: array_or_null_to base t) +: Lemma + (requires ( + array_or_null_spec (null_from base t, to) + )) + (ensures ( + to == null_to base t + )) + +inline_for_extraction +let null (base: Type u#0) (t: Type u#0) : Pure (array_or_null base t) (requires True) (ensures (fun r -> len r == zero_size)) += (null_from base t, null_to base t) +val g_is_null (#base: Type) (#t: Type) (a: array_or_null base t) : Ghost bool (requires True) (ensures (fun res -> res == true <==> a == null base t)) +inline_for_extraction +noextract +let array (base: Type u#0) (t:Type u#0) : Type u#0 = (a: array_or_null base t { g_is_null a == false }) + +val array_is_unit (t: Type0) (n: size_t) (a: array_pcm_carrier t n) +: b:bool{b <==> a == one (array_pcm t n)} + +[@@c_struct] +let array_typedef_sized (t: Type0) (n': Type0) (n: size_t_of n'{size_v n > 0}): typedef = { + carrier = array_pcm_carrier t n; + pcm = array_pcm t n; + view_type = array_view_type_sized t n' n; + view = array_view t n; + is_unit = array_is_unit t n; +} + +/// Combining the elements above to create an array vprop +/// TODO: generalize to any view + +// val g_array_as_ref (#base: Type u#0) (#t: Type u#0) (a: array base t) +// : GTot (Steel.C.Reference.ref base (array_view_type t (len a)) (array_pcm t (len a))) + +// [@@ __steel_reduce__] +// let varray (#base: Type) (#t: Type) (x: array base t) : Tot vprop +// = Steel.C.Model.Ref.pts_to_view (g_array_as_ref x) (array_view t (len x)) + +val varray_hp (#base: Type0) (#t: Type0) (x: array base t) : Tot (slprop u#1) + +val varray_sel (#base: Type0) (#t: Type0) (x: array base t) : GTot (selector (array_view_type t (len x)) (varray_hp x)) + +[@@ __steel_reduce__ ] +let varray' (#base: Type) (#t: Type) (x: array base t) : GTot vprop' = { + hp = varray_hp x; + t = array_view_type t (len x); + sel = varray_sel x; +} + +[@@ __steel_reduce__ ] +let varray (#base: Type) (#t: Type) (x: array base t) : Tot vprop = + VUnit (varray' x) + +val g_mk_array (#base: Type u#0) (#t: Type u#0) (#n: size_t) (r: Steel.C.Reference.ref base (array_view_type t n) (array_pcm t n)) + (a: array base t) +: Tot prop + +val g_mk_array_weak + (#base: Type u#0) (#t: Type u#0) (#n: size_t) (r: Steel.C.Reference.ref base (array_view_type t n) (array_pcm t n)) + (a: array base t) +: Lemma + (requires (g_mk_array r a)) + (ensures ( + size_v n > 0 /\ + len a == Ghost.reveal n + )) + [SMTPat (g_mk_array r a)] + +val g_mk_array_from + (#base: Type u#0) (#t: Type u#0) (#n: size_t) (r: Steel.C.Reference.ref base (array_view_type t n) (array_pcm t n)) + (a: array_or_null_from base t) +: Tot prop + +val g_mk_array_to + (#base: Type u#0) (#t: Type u#0) (#n: size_t) (r: Steel.C.Reference.ref base (array_view_type t n) (array_pcm t n)) + (a: array_or_null_from base t) +: Pure (array_or_null_to base t) + (requires (g_mk_array_from r a)) + (ensures (fun a' -> + let a0 = (a, a') in + array_or_null_spec a0 /\ + g_is_null a0 == false /\ + g_mk_array r a0 + )) + +val intro_varray_from (#base: Type u#0) (#t: Type u#0) (#opened: _) (#n: size_t) (r: Steel.C.Reference.ref base (array_view_type t n) (array_pcm t n)) + (_: squash (size_v n > 0)) +: SteelAtomicBase (al: array_or_null_from base t { g_mk_array_from r al }) + false opened Unobservable + (Steel.C.Model.Ref.pts_to_view r (array_view t n)) + (fun al -> varray (al, g_mk_array_to r al)) + (requires fun _ -> True) + (ensures (fun h al h' -> + let a = (al, g_mk_array_to r al) in + g_mk_array r a /\ + h' (varray a) == h (Steel.C.Model.Ref.pts_to_view r (array_view t n)) + )) + +inline_for_extraction +let intro_varray (#base: Type u#0) (#t: Type u#0) (#opened: _) (#n: size_t) (r: Steel.C.Reference.ref base (array_view_type t n) (array_pcm t n)) + (_: squash (size_v n > 0)) +: SteelAtomicBase (array base t) + false opened Unobservable + (Steel.C.Model.Ref.pts_to_view r (array_view t n)) + (fun a -> varray a) + (requires fun _ -> True) + (ensures (fun h a h' -> + g_mk_array r a /\ + h' (varray a) == h (Steel.C.Model.Ref.pts_to_view r (array_view t n)) + )) += + let al = intro_varray_from r () in + let a = (al, g_mk_array_to r al) in + change_equal_slprop + (varray (al, g_mk_array_to r al)) + (varray a); + return a + +val elim_varray (#inames: _) (#base: Type u#0) (#t: Type u#0) (#n: size_t) (r: Steel.C.Reference.ref base (array_view_type t n) (array_pcm t n)) (a: array base t) (_: squash (size_v n > 0)) +: SteelGhost unit inames + (varray a) + (fun _ -> Steel.C.Model.Ref.pts_to_view r (array_view t n)) + (requires fun _ -> g_mk_array r a) + (ensures (fun h _ h' -> + g_mk_array r a /\ + h (varray a) == h' (Steel.C.Model.Ref.pts_to_view r (array_view t n)) + )) + +/// Splitting an array into subarrays + +val adjacent + (#base: Type) + (#t: Type) + (r1 r2: array base t) +: Tot prop + +val merge + (#base: Type) + (#t: Type) + (r1 r2: array base t) +: Ghost (array base t) + (requires (adjacent r1 r2)) + (ensures (fun r -> + length r == length r1 + length r2 /\ + fst r == fst r1 // this property justifies array_or_null_from _ t being extracted to t* + )) + +let merge_into + (#base: Type) + (#t: Type) + (r1 r2 r3: array base t) +: Tot prop += adjacent r1 r2 /\ + merge r1 r2 == r3 + +val merge_assoc + (#base: Type) + (#t: Type) + (r1 r2 r3: array base t) +: Lemma + (requires ( + (adjacent r1 r2 /\ (adjacent r2 r3 \/ adjacent (merge r1 r2) r3)) \/ + (adjacent r2 r3 /\ adjacent r1 (merge r2 r3)) + )) + (ensures ( + adjacent r1 r2 /\ adjacent r2 r3 /\ + begin + let r12 = merge r1 r2 in + let r23 = merge r2 r3 in + adjacent r1 r23 /\ adjacent r12 r3 /\ + merge r1 r23 == merge r12 r3 + end + )) + [SMTPat (merge (merge r1 r2) r3)] + +val merge_inj_right + (#base: Type) + (#t: Type) + (a b1 b2: array base t) +: Lemma + (requires (adjacent a b1 /\ adjacent a b2 /\ merge a b1 == merge a b2)) + (ensures (b1 == b2)) + +val merge_inj_left + (#base: Type) + (#t: Type) + (a1 a2 b: array base t) +: Lemma + (requires (adjacent a1 b /\ adjacent a2 b /\ merge a1 b == merge a2 b)) + (ensures (a1 == a2)) + +val no_self_merge_1 (#base #t: Type) (a b: array base t) : Lemma + (~ (merge_into a b a)) + +val no_self_merge_2 (#base #t: Type) (a b: array base t) : Lemma + (~ (merge_into a b b)) + +[@erasable] +noeq +type gpair (a b: Type) = | GPair: (fst: a) -> (snd: b) -> gpair a b + +val gsplit + (#base: Type) + (#t: Type) + (r: array base t) + (i: size_t) +: Ghost (array base t `gpair` array base t) + (requires (size_v i <= length r)) + (ensures (fun (GPair rl rr) -> + merge_into rl rr r /\ + length rl == size_v i + )) + +val split' (#opened: _) (#base: Type) (#t:Type) (a:array base t) (i:size_t) + : SteelGhost (array base t `gpair` array base t) opened + (varray a) + (fun res -> varray (GPair?.fst res) `star` varray (GPair?.snd res)) + (fun _ -> size_v i <= length a) + (fun h res h' -> + let s = h (varray a) in + let sl = h' (varray (GPair?.fst res)) in + let sr = h' (varray (GPair?.snd res)) in + size_v i <= length a /\ + res == gsplit a i /\ + sl == Seq.slice s 0 (size_v i) /\ + sr == Seq.slice s (size_v i) (length a) /\ + s == sl `Seq.append` sr + ) + +inline_for_extraction +let split_left (#base: _) (#t:Type) (#opened: _) (a:array base t) + (al ar: Ghost.erased (array base t)) + : SteelAtomicBase (array base t) false opened Unobservable + (varray al) + (fun res -> varray res) + (fun _ -> + merge_into al ar a + ) + (fun h res h' -> + res == Ghost.reveal al /\ + h' (varray res) == h (varray al) + ) += match a with + | (a_, _) -> + let res = (a_, snd al) in + change_equal_slprop + (varray al) + (varray res); + return res + +val split_right_from (#base: _) (#t:Type) (#opened: _) (a:array base t) (i:size_t) + : SteelAtomicBase (array_or_null_from base t) false opened Unobservable + (varray a) + (fun _ -> varray a) + (fun _ -> size_v i <= length a) + (fun h res h' -> + h' (varray a) == h (varray a) /\ + size_v i <= length a /\ + res == fst (GPair?.snd (gsplit a i)) + ) + +inline_for_extraction +let split_right (#base: _) (#t:Type) (#opened: _) (a:array base t) (i:size_t) + : SteelAtomicBase (array base t) false opened Unobservable + (varray a) + (fun _ -> varray a) + (fun _ -> size_v i <= length a) + (fun h res h' -> + h' (varray a) == h (varray a) /\ + size_v i <= length a /\ + res == GPair?.snd (gsplit a i) + ) += let from = split_right_from a i in + let res = (from, snd (GPair?.snd (gsplit a i))) in + return res + +inline_for_extraction +let split (#opened: _) (#base: Type) (#t:Type) (a:array base t) (i:size_t) (sq: squash (size_v i <= length a)) + : SteelAtomicBase (array base t) false opened Unobservable + (varray a) + (fun res -> varray (Ghost.reveal (Ghost.hide (GPair?.fst (gsplit a i)))) + `star` varray res) + (fun _ -> size_v i <= length a) + (fun h res h' -> + let s = h (varray a) in + let sl = h' (varray (GPair?.fst (gsplit a i))) in + let sr = h' (varray res) in + size_v i <= length a /\ + res == GPair?.snd (gsplit a i) /\ + sl == Seq.slice s 0 (size_v i) /\ + sr == Seq.slice s (size_v i) (length a) /\ + s == sl `Seq.append` sr + ) += + let sr = split_right a i in + let g = split' a i in + change_equal_slprop + (varray (GPair?.fst g)) + (varray (Ghost.reveal (Ghost.hide (GPair?.fst (gsplit a i))))); + change_equal_slprop + (varray (GPair?.snd g)) + (varray sr); + return sr + +val join' (#opened: _) (#base: _) (#t:Type) (al ar:array base t) + : SteelGhost (Ghost.erased (array base t)) opened + (varray al `star` varray ar) + (fun a -> varray a) + (fun _ -> adjacent al ar) + (fun h a h' -> + let s = h' (varray a) in + s == (h (varray al) `Seq.append` h (varray ar)) /\ + merge_into al ar a + ) + +inline_for_extraction +let joinc (#base: _) (#t:Type) (#opened: _) (al ar:array base t) + : SteelAtomicBase (array base t) false opened Unobservable + (varray al `star` varray ar) + (fun a -> varray al `star` varray ar) + (fun _ -> adjacent al ar) + (fun h a h' -> + h' (varray al) == h (varray al) /\ + h' (varray ar) == h (varray ar) /\ + merge_into al ar a + ) += match al with + | (a, _) -> + let res = (a, snd (merge al ar)) in + return res + +inline_for_extraction +let join (#opened: _) (#base: _) (#t:Type) (al ar:array base t) + : SteelAtomicBase (array base t) false opened Unobservable + (varray al `star` varray ar) + (fun a -> varray a) + (fun _ -> adjacent al ar) + (fun h a h' -> + let s = h' (varray a) in + s == (h (varray al) `Seq.append` h (varray ar)) /\ + merge_into al ar a + ) += + let a = joinc al ar in + let ga = join' al ar in + change_equal_slprop + (varray ga) + (varray a); + return a + +/// Converting an array into a pointer, after it has been split to an array of size 1 +/// Those two functions should extract to identity functions + +val g_ref_of_array + (#base: Type) (#t:Type0) (r:array base t) +: Ghost (Steel.C.Reference.ref base t (Steel.C.Opt.opt_pcm #t)) + (requires (length r == 1)) + (ensures (fun _ -> True)) + +val v_ref_of_array + (#base: Type) (#t:Type0) (r:array base t) +: Ghost vprop + (requires (length r == 1)) + (ensures (fun _ -> True)) + +val ref_of_array_ghost (#inames: _) (#base: Type) (#t:Type0) (r:array base t) (sq: squash (length r == 1)) + : SteelGhost unit inames + (varray r) + (fun _ -> Steel.C.Model.Ref.pts_to_view (g_ref_of_array r) (Steel.C.Opt.opt_view t) `star` v_ref_of_array r) + (requires fun _ -> True) + (ensures fun h0 _ h1 -> + let r' = g_ref_of_array r in + let s = h0 (varray r) in + Seq.length s == 1 /\ + h1 (Steel.C.Model.Ref.pts_to_view r' (Steel.C.Opt.opt_view t)) == Seq.index s 0 + ) + +val ref_of_array_from (#base: Type) (#t:Type0) (#opened: _) (r_from:array_or_null_from base t) (r_to: array_or_null_to base t) (sq: squash (let r = (r_from, r_to) in array_or_null_spec r /\ length r == 1)) + : SteelAtomicBase (Steel.C.Reference.ref base t (Steel.C.Opt.opt_pcm #t)) + false opened Unobservable + (varray (r_from, r_to)) + (fun r' -> Steel.C.Model.Ref.pts_to_view r' (Steel.C.Opt.opt_view t) `star` v_ref_of_array (r_from, r_to)) + (requires fun _ -> True) + (ensures fun h0 r' h1 -> + let r = (r_from, r_to) in + let s = h0 (varray r) in + Seq.length s == 1 /\ + g_ref_of_array r == r' /\ + h1 (Steel.C.Model.Ref.pts_to_view r' (Steel.C.Opt.opt_view t)) == Seq.index s 0 + ) + +inline_for_extraction +let ref_of_array (#base: Type) (#t:Type0) (#opened: _) (r:array base t) (sq: squash (length r == 1)) + : SteelAtomicBase (Steel.C.Reference.ref base t (Steel.C.Opt.opt_pcm #t)) + false opened Unobservable + (varray r) + (fun r' -> Steel.C.Model.Ref.pts_to_view r' (Steel.C.Opt.opt_view t) `star` v_ref_of_array r) + (requires fun _ -> True) + (ensures fun h0 r' h1 -> + let s = h0 (varray r) in + Seq.length s == 1 /\ + g_ref_of_array r == r' /\ + h1 (Steel.C.Model.Ref.pts_to_view r' (Steel.C.Opt.opt_view t)) == Seq.index s 0 + ) += match r with + | (r_from, r_to) -> + change_equal_slprop + (varray r) + (varray (r_from, r_to)); + let res = ref_of_array_from r_from r_to () in + change_equal_slprop + (v_ref_of_array (r_from, r_to)) + (v_ref_of_array r); + return res + +val array_of_ref (#inames: _) (#base: Type) (#t:Type0) (r': array base t) (r: Steel.C.Reference.ref base t (Steel.C.Opt.opt_pcm #t)) (sq: squash (length r' == 1)) + : SteelGhost unit inames + (Steel.C.Model.Ref.pts_to_view r (Steel.C.Opt.opt_view t) `star` v_ref_of_array r') + (fun _ -> varray r') + (requires fun _ -> g_ref_of_array r' == r) + (ensures fun h0 _ h1 -> + let s = h1 (varray r') in + Seq.length s == 1 /\ + g_ref_of_array r' == r /\ + h0 (Steel.C.Model.Ref.pts_to_view r (Steel.C.Opt.opt_view t)) == Seq.index s 0 + ) + +// this function should be used only to pass a pointer as an argument to a function that expects an array + +val mk_array_of_ref_from_spec + (#base: Type) (#t:Type0) (r: Steel.C.Reference.ref base t (Steel.C.Opt.opt_pcm #t)) + (from: array_or_null_from base t) +: Tot prop + +val mk_array_of_ref_to + (#base: Type) (#t:Type0) (r: Steel.C.Reference.ref base t (Steel.C.Opt.opt_pcm #t)) + (from: array_or_null_from base t) +: Pure (array_or_null_to base t) + (requires (mk_array_of_ref_from_spec r from)) + (ensures (fun to -> + let r' = (from, to) in + array_or_null_spec r' /\ + g_is_null r' == false + )) + +val mk_array_of_ref_from (#base: Type) (#t:Type0) (#opened: _) (r: Steel.C.Reference.ref base t (Steel.C.Opt.opt_pcm #t)) + : SteelAtomicBase (r0: array_or_null_from base t { mk_array_of_ref_from_spec r r0 }) + false opened Unobservable + (Steel.C.Model.Ref.pts_to_view r (Steel.C.Opt.opt_view t)) + (fun r0 -> varray (r0, mk_array_of_ref_to r r0)) + (requires fun _ -> True) + (ensures fun h0 r0 h1 -> + let r' = (r0, mk_array_of_ref_to r r0) in + let s = h1 (varray r') in + Seq.length s == 1 /\ + g_ref_of_array r' == r /\ + h0 (Steel.C.Model.Ref.pts_to_view r (Steel.C.Opt.opt_view t)) == Seq.index s 0 + ) + +inline_for_extraction +let mk_array_of_ref (#base: Type) (#t:Type0) (#opened: _) (r: Steel.C.Reference.ref base t (Steel.C.Opt.opt_pcm #t)) + : SteelAtomicBase (array base t) + false opened Unobservable + (Steel.C.Model.Ref.pts_to_view r (Steel.C.Opt.opt_view t)) + (fun r' -> varray r') + (requires fun _ -> True) + (ensures fun h0 r' h1 -> + let s = h1 (varray r') in + Seq.length s == 1 /\ + g_ref_of_array r' == r /\ + h0 (Steel.C.Model.Ref.pts_to_view r (Steel.C.Opt.opt_view t)) == Seq.index s 0 + ) += let from = mk_array_of_ref_from r in + let r' = (from, mk_array_of_ref_to r from) in + change_equal_slprop + (varray (from, mk_array_of_ref_to r from)) + (varray r'); + return r' + +val is_array_or_null (#base #a:Type0) (r:array_or_null base a) : slprop u#1 +val array_or_null_sel (#base #a:Type0) (r:array_or_null base a) : GTot (selector (option (array_view_type a (len r))) (is_array_or_null r)) + +[@@ __steel_reduce__] +let varray_or_null' #base #a (r: array_or_null base a) : GTot vprop' = + {hp = is_array_or_null r; + t = option (array_view_type a (len r)); + sel = array_or_null_sel r} + +[@@ __steel_reduce__] +let varray_or_null r = VUnit (varray_or_null' r) + +val intro_varray_or_null_none + (#opened: _) + (#base #a: Type) + (x: array_or_null base a) +: SteelGhost unit opened + emp + (fun _ -> varray_or_null x) + (fun _ -> g_is_null x == true) + (fun _ _ h' -> h' (varray_or_null x) == None) + +val intro_varray_or_null_some + (#opened: _) + (#base #a: Type) + (x: array base a) +: SteelGhost unit opened + (varray x) + (fun _ -> varray_or_null x) + (fun _ -> True) + (fun h _ h' -> + g_is_null x == false /\ + h' (varray_or_null x) == Some (h (varray x) + )) + +val elim_varray_or_null_some + (#opened: _) + (#base #a: Type) + (x: array_or_null base a) +: SteelGhost (squash (g_is_null x == false)) opened + (varray_or_null x) + (fun _ -> varray x) + (fun h -> g_is_null x == false \/ Some? (h (varray_or_null x))) + (fun h _ h' -> + g_is_null x == false /\ + h (varray_or_null x) == Some (h' (varray x)) + ) + +val elim_varray_or_null_none + (#opened: _) + (#base #a: Type) + (x: array_or_null base a) +: SteelGhost unit opened + (varray_or_null x) + (fun _ -> emp) + (fun h -> g_is_null x == true \/ None? (h (varray_or_null x))) + (fun h _ _ -> + g_is_null x == true /\ + h (varray_or_null x) == None + ) + +/// Allocates an array of size [n] where all cells have initial value [x] + +val freeable + (#base: Type0) + (#t: Type0) + (a: array base t) +: Tot prop + +val malloc_from_spec + (#t: Type0) + (x: t) + (n: size_t) + (from: array_or_null_from (array_pcm_carrier t n) t) +: Tot prop + +val malloc_to + (#t: Type0) + (x: t) + (n: size_t) + (from: array_or_null_from (array_pcm_carrier t n) t) +: Pure (array_or_null_to (array_pcm_carrier t n) t) + (requires (size_v n > 0 /\ malloc_from_spec x n from)) + (ensures (fun to -> array_or_null_spec (from, to))) + +val malloc_from + (#t: Type0) + (x: t) + (n: size_t) + (sq: squash (size_v n > 0)) +: Steel (from: array_or_null_from (array_pcm_carrier t n) t { malloc_from_spec x n from }) + emp + (fun r -> varray_or_null (r, malloc_to x n r)) + (requires fun _ -> True) + (ensures fun _ r0 h' -> + size_v n > 0 /\ + begin let r : array_or_null (array_pcm_carrier t n) t = (r0, malloc_to x n r0) in + g_is_null r == false ==> (freeable r /\ len r == n /\ h' (varray_or_null r) == Some (Seq.create (size_v n) x)) + end + ) + +inline_for_extraction +let malloc + (#t: Type0) + (x: t) + (n: size_t) +: Steel (array_or_null (array_pcm_carrier t n) t) + emp + (fun r -> varray_or_null r) + (requires fun _ -> size_v n > 0) + (ensures fun _ r h' -> + g_is_null r == false ==> (freeable r /\ len r == n /\ h' (varray_or_null r) == Some (Seq.create (size_v n) x)) + ) += let r0 = malloc_from x n () in + let r = (r0, malloc_to x n r0) in + change_equal_slprop + (varray_or_null (r0, malloc_to x n r0)) + (varray_or_null r); + return r + +val free_from + (#base: Type0) + (#t: Type0) + (a: array_or_null_from base t) + (a' : array_or_null_to base t) + (sq: squash (array_or_null_spec (a, a') /\ g_is_null (a, a') == false)) +: Steel unit + (varray (a, a')) + (fun _ -> emp) + (requires (fun _ -> freeable (a,a'))) + (ensures (fun _ _ _ -> True)) + +inline_for_extraction +let free + (#base: Type0) + (#t: Type0) + (a: array base t) +: Steel unit + (varray a) + (fun _ -> emp) + (requires (fun _ -> freeable a)) + (ensures (fun _ _ _ -> True)) += match a with + | (af, a') -> + change_equal_slprop + (varray a) + (varray (af, a')); + free_from af a' () + +val is_null_from + (#base: Type0) + (#t: Type0) + (#opened: _) + (a: array_or_null_from base t) + (a' : array_or_null_to base t) + (sq: squash (array_or_null_spec (a, a'))) +: SteelAtomicBase bool false opened Unobservable + (varray_or_null (a, a')) + (fun _ -> varray_or_null (a, a')) + (requires fun _ -> True) + (ensures fun h res h' -> + res == g_is_null (a, a') /\ + h' (varray_or_null (a, a')) == h (varray_or_null (a, a')) + ) + +inline_for_extraction +let is_null + (#base: Type0) + (#t: Type0) + (#opened: _) + (a: array_or_null base t) +: SteelAtomicBase bool false opened Unobservable + (varray_or_null a) + (fun _ -> varray_or_null a) + (requires fun _ -> True) + (ensures fun h res h' -> + res == g_is_null a /\ + h' (varray_or_null a) == h (varray_or_null a) + ) += match a with + | (af, a') -> + change_equal_slprop + (varray_or_null a) + (varray_or_null (af, a')); + let res = is_null_from af a' () in + change_equal_slprop + (varray_or_null (af, a')) + (varray_or_null a); + return res diff --git a/lib/steel/c/Steel.C.Array.fsti b/lib/steel/c/Steel.C.Array.fsti new file mode 100644 index 000000000..88b8edb43 --- /dev/null +++ b/lib/steel/c/Steel.C.Array.fsti @@ -0,0 +1,99 @@ +(* + Copyright 2021 Microsoft Research + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*) + +module Steel.C.Array +include Steel.C.Array.Base +open Steel.Memory +open Steel.FractionalPermission +open Steel.Effect +open FStar.Ghost +open Steel.Effect.Atomic + +open Steel.C.Typedef +open Steel.C.Model.PCM +open Steel.C.Fields +open Steel.C.Typenat + +#set-options "--ide_id_info_off" + +/// Accesses index [i] in array [r], as long as [i] is in bounds and the array +/// is currently valid in memory + +val index_from (#base: Type) (#t:Type) (r:array_or_null_from base t) (r' : array_or_null_to base t { array_or_null_spec (r, r') /\ g_is_null (r, r') == false }) (i:size_t) + : Steel t + (varray (r, r')) + (fun _ -> varray (r, r')) + (requires fun _ -> size_v i < length (r, r')) + (ensures fun h0 x h1 -> + let s = h1 (varray (r, r')) in + size_v i < length (r, r') /\ + h0 (varray (r, r')) == s /\ + x == Seq.index s (size_v i)) + +inline_for_extraction +let index (#base: Type) (#t:Type) (r:array base t) (i:size_t) + : Steel t + (varray r) + (fun _ -> varray r) + (requires fun _ -> size_v i < length r) + (ensures fun h0 x h1 -> + let s = h1 (varray r) in + size_v i < length r /\ + h0 (varray r) == s /\ + x == Seq.index s (size_v i)) += match r with + | (r0, r') -> + change_equal_slprop + (varray r) + (varray (r0, r')); + let res = index_from r0 r' i in + change_equal_slprop + (varray (r0, r')) + (varray r); + return res + + +/// Updates index [i] in array [r] with value [x], as long as [i] +/// is in bounds and the array is currently valid in memory + + +val upd_from (#base: Type) (#t:Type) (r:array_or_null_from base t) (r' : array_or_null_to base t { array_or_null_spec (r, r') /\ g_is_null (r, r') == false }) (i:size_t) (x:t) + : Steel unit + (varray (r, r')) + (fun _ -> varray (r, r')) + (requires fun h -> size_v i < length (r, r')) + (ensures fun h0 _ h1 -> + size_v i < length (r, r') /\ + h1 (varray (r, r')) == Seq.upd (h0 (varray (r, r'))) (size_v i) x) + +inline_for_extraction +let upd (#base: Type) (#t:Type) (r:array base t) (i:size_t) (x:t) + : Steel unit + (varray r) + (fun _ -> varray r) + (requires fun h -> size_v i < length r) + (ensures fun h0 _ h1 -> + size_v i < length r /\ + h1 (varray r) == Seq.upd (h0 (varray r)) (size_v i) x) += match r with + | (r0, r') -> + change_equal_slprop + (varray r) + (varray (r0, r')); + upd_from r0 r' i x; + change_equal_slprop + (varray (r0, r')) + (varray r) diff --git a/lib/steel/c/Steel.C.Fields.fst b/lib/steel/c/Steel.C.Fields.fst new file mode 100644 index 000000000..aff451e95 --- /dev/null +++ b/lib/steel/c/Steel.C.Fields.fst @@ -0,0 +1,7 @@ +module Steel.C.Fields + +let c_fields_t_nil: Type0 = unit +let c_fields_t_cons + (field: Type0) (t: Type0) (fields: Type0) +: Type0 += unit diff --git a/lib/steel/c/Steel.C.Fields.fsti b/lib/steel/c/Steel.C.Fields.fsti new file mode 100644 index 000000000..8bd6c7598 --- /dev/null +++ b/lib/steel/c/Steel.C.Fields.fsti @@ -0,0 +1,177 @@ +module Steel.C.Fields + +open FStar.FSet +open FStar.FunctionalExtensionality + +open Steel.C.Typedef +open Steel.C.Opt + +module TS = Steel.C.Typestring + +(** Used to control normalization *) +irreducible let c_struct = () +irreducible let c_union = () +irreducible let c_typedef = () + +[@@c_typedef] +let trivial_typedef: typedef = { + carrier = option unit; + pcm = opt_pcm #unit; + view_type = unit; + view = opt_view unit; + is_unit = (fun o -> None? o); +} + +(** While possible to encode struct fields as a list of (field name, + typedef) pairs, such a representation does not play well with F*'s + normalizer due to the fact that many kinds of queries we would like to + perform on such lists of struct fields require recursion over that + list. This interacts poorly with Steel's normalization tactic, and + requires the user to increase fuel, which can be costly. To sidestep + this, we essentially encode a list of fields by all of the various + types at which we would like to eliminate that list, and build up each + elimination at "list"-construction time by exposing combinators + {c_fields_nil, c_fields_cons} which, rather than constructing a + list, just precompute all of the facts that we could ever need to + know in the future about it. All such facts are represented in + the following c_fields struct. *) +//[@@__reduce__] +noeq type c_fields = { + //cfields: clist string; + cfields: list string; + has_field: set string; + has_field_mt: squash (has_field "" == true); + has_field_prf: squash (forall field. has_field field == field `List.Tot.mem` cfields); + get_field: string ^-> typedef; + // get_field_prf: forall field. has_field field == false ==> get_field field == trivial_typedef; + get_field_mt: squash (get_field "" == trivial_typedef); + nonempty_witness: + o:option string + {(None? o ==> cfields == [""]) /\ + (Some? o ==> Some?.v o `List.Tot.mem` cfields /\ Some?.v o =!= "")}; +} + +(* Begin for extraction *) + +(** The following combinators encode c_fields as a F* type, which + allows struct field information to stick around after erasure for + Karamel. For more details about why we need this, see + Steel.C.Typestring.fsti and Steel.C.Typenat.fsti *) + +val c_fields_t_nil: Type0 +val c_fields_t_cons + (field: Type0) (t: Type0) (fields: Type0) +: Type0 + +let c_fields_t (fields: c_fields) = + List.Tot.fold_right + (fun field fields' -> + c_fields_t_cons + (TS.mk_string_t field) + (fields.get_field field).view_type + fields') + fields.cfields + c_fields_t_nil + +(* End for extraction *) + +//[@@__reduce__] +let fields_nil: c_fields = { + cfields = [""]; + has_field = insert "" emptyset; + has_field_mt = (); + has_field_prf = (); + //has_field_prf = (); + get_field = on_dom _ (fun _ -> trivial_typedef); + get_field_mt = (); + nonempty_witness = None; +} + +let field_t = field:string{field =!= ""} + +//[@@__reduce__] +let fields_cons (field: field_t) (td: typedef) (fields: c_fields): c_fields = { + cfields = field :: fields.cfields; + has_field = insert field fields.has_field; + has_field_mt = fields.has_field_mt; + has_field_prf = fields.has_field_prf; + get_field = on_dom _ (fun field' -> if field = field' then td else fields.get_field field'); + get_field_mt = (); + nonempty_witness = Some field; +} + +let field_of (fields: c_fields) = field:string{fields.has_field field == true /\ field =!= ""} + +(** We divide normalization into two stages: + 1) First, all typedefs (which ought to have been defined with attribute c_typedef) are unfolded. + 2) Then, struct/union fields (which ought to have been defined + with attributes c_struct and c_union respectively) are unfolded + along with a number of helper definitions. + This two-step normalization process is used by + addr_of_struct_field and addr_of_union_field, and was developed to + ensure that, in the case where a struct has structs inside of it, + only the outermost typedef representing the outermost struct is + unfolded. + + In retrospect, it's unclear whether this is needed, or even + whether [norm] actually carries out such a 2-stage process. + TODO see if this can be simplified *) + +unfold let unfold_typedefs = [delta_attr [`%c_typedef]] + +unfold let simplify_typedefs = + [delta_attr [`%c_struct; `%c_union]; + delta_only + [`%fields_cons; + `%fields_nil; + `%Mkc_fields?.get_field; + `%Mktypedef?.carrier; + `%Mktypedef?.pcm; + `%Mktypedef?.view_type; + `%Mktypedef?.view]; + iota; zeta; primops] + +(* Operations on views *) + +[@@c_typedef] +noextract inline_for_extraction +let opt_typedef (t: Type0): typedef = { + carrier = option t; + pcm = opt_pcm #t; + view_type = t; + view = opt_view t; + is_unit = (fun x -> None? x); +} + +open Steel.C.Reference + +[@@c_typedef] +inline_for_extraction noextract +let refine_typedef + (t: typedef) + (p: (t.view_type -> Tot prop)) +: Tot typedef += { + carrier = t.carrier; + pcm = t.pcm; + view_type = Steel.C.Model.Ref.refine t.view_type p; + view = refine_view t.view p; + is_unit = t.is_unit; +} + +[@@c_typedef] +inline_for_extraction noextract +let rewrite_typedef + (t: typedef) + (#view': Type) + (f: t.view_type -> Tot view') + (g: view' -> Tot t.view_type) + (prf: squash (f `Steel.C.Model.Connection.is_inverse_of` g)) +: Tot typedef += { + carrier = t.carrier; + pcm = t.pcm; + view_type = view'; + view = rewrite_view t.view f g prf; + is_unit = t.is_unit; +} diff --git a/lib/steel/c/Steel.C.Model.Connection.fst b/lib/steel/c/Steel.C.Model.Connection.fst new file mode 100644 index 000000000..af9849fe5 --- /dev/null +++ b/lib/steel/c/Steel.C.Model.Connection.fst @@ -0,0 +1,675 @@ +module Steel.C.Model.Connection + +open Steel.C.Model.PCM +open FStar.FunctionalExtensionality + +(** PCM morphisms *) + +let morph_compose2 (pa: pcm 'a) (pb: pcm 'b) (morph: 'a -> 'b) + (x1: 'a) (x2: 'a{composable pa x1 x2}) += squash ( + composable pb (morph x1) (morph x2) /\ + morph (x1 `op pa` x2) == morph x1 `op pb` morph x2) + +let morph_compose1 (pa: pcm 'a) (pb: pcm 'b) (morph: 'a -> 'b) (x1: 'a) = + restricted_t (x2:'a{composable pa x1 x2}) (morph_compose2 pa pb morph x1) + +noeq +type morphism (#a #b: Type) (pa: pcm a) (pb: pcm b) = { + morph: (a ^-> b); + morph_unit: squash (morph (one pa) == one pb); + morph_compose: restricted_t a (morph_compose1 pa pb morph); + (** Extensionality is needed to show that composition of morphism is associative *) +} + +(** A smart constructor for extensional morphisms *) +let mkmorphism (#pa: pcm 'a) (#pb: pcm 'b) (morph: 'a -> 'b) + (morph_unit: squash (morph (one pa) == one pb)) + (morph_compose: (x1:'a -> x2:'a{composable pa x1 x2} -> morph_compose2 pa pb (on_dom 'a morph) x1 x2)) +: pa `morphism` pb = { + morph = on_dom 'a morph; + morph_unit = morph_unit; + morph_compose = on_dom 'a (fun x1 -> on_dom (x2:'a{composable pa x1 x2}) (fun x2 -> morph_compose x1 x2)); +} + +let morph_compose2_irrelevant (pa: pcm 'a) (pb: pcm 'b) (morph: 'a ^-> 'b) + (x1: 'a) (x2: 'a{composable pa x1 x2}) + (prf1 prf2: morph_compose2 pa pb morph x1 x2) +: Lemma (prf1 == prf2) += () + +let morph_compose1_irrelevant (pa: pcm 'a) (pb: pcm 'b) (morph: 'a ^-> 'b) (x1: 'a) + (prf1 prf2: morph_compose1 pa pb morph x1) +: Lemma (prf1 == prf2) += assert (prf1 `feq` prf2) + +let morph_compose_irrelevant (pa: pcm 'a) (pb: pcm 'b) (morph: 'a ^-> 'b) + (prf1 prf2: restricted_t 'a (morph_compose1 pa pb morph)) +: Lemma (prf1 == prf2) += let aux (x: 'a): Lemma (prf1 x == prf2 x) [SMTPat (prf1 x)] = + morph_compose1_irrelevant pa pb morph x (prf1 x) (prf2 x) + in assert (prf1 `feq` prf2) + +let morph_eq (f g: 'p `morphism` 'q) +: Lemma (requires f.morph `feq` g.morph) (ensures f == g) + [SMTPat (f.morph `feq` g.morph)] += assert (f.morph == g.morph); + morph_compose_irrelevant 'p 'q f.morph f.morph_compose g.morph_compose + +let morphism_morph_compose + (#a #b: Type) (#pa: pcm a) (#pb: pcm b) (m: morphism pa pb) + (x1: a) + (x2: a) +: Lemma + (requires (composable pa x1 x2)) + (ensures (composable pb (m.morph x1) (m.morph x2) /\ m.morph (x1 `op pa` x2) == m.morph x1 `op pb` m.morph x2)) + [SMTPat (composable pb (m.morph x1) (m.morph x2))] += m.morph_compose x1 x2 + +let morphism_compose (#a #b #c: Type) (#pa: pcm a) (#pb: pcm b) (#pc: pcm c) (fab: morphism pa pb) (fbc: morphism pb pc) : Tot (morphism pa pc) = + mkmorphism + (fun x -> fbc.morph (fab.morph x)) + () + (fun x1 x2 -> + fab.morph_compose x1 x2; + fbc.morph_compose (fab.morph x1) (fab.morph x2)) + +let morphism_compose_morph + (#a #b #c: Type) (#pa: pcm a) (#pb: pcm b) (#pc: pcm c) (fab: morphism pa pb) (fbc: morphism pb pc) + (x: a) +: Lemma + ((morphism_compose fab fbc).morph x == fbc.morph (fab.morph x)) += () + +let morphism_id + (#a: Type) + (p: pcm a) +: Tot (morphism p p) += mkmorphism + (fun x -> x) + () + (fun _ _ -> ()) + +let morphism_compose_id_left + (#a #b: Type) (#pa: pcm a) (#pb: pcm b) + (m: morphism pa pb) +: Lemma + (morphism_id pa `morphism_compose` m == m) += morph_eq (morphism_id pa `morphism_compose` m) m + +let morphism_compose_id_right + (#a #b: Type) (#pa: pcm a) (#pb: pcm b) + (m: morphism pa pb) +: Lemma + (m `morphism_compose` morphism_id pb == m) += morph_eq (m `morphism_compose` morphism_id pb) m + +let compatible_morphism + (#p: pcm 'a) (#q: pcm 'b) + (f: p `morphism` q) + (x y: Ghost.erased 'a) +: Lemma + (requires compatible p x y) + (ensures compatible q (f.morph x) (f.morph y)) += let frame_x = compatible_elim p x y in + f.morph_compose frame_x x; + compatible_intro q (f.morph x) (f.morph y) (f.morph frame_x) + +let injective (#a #b: Type) (f: (a -> Tot b)) : Tot prop = + (forall x1 x2 . {:pattern f x1; f x2} f x1 == f x2 ==> x1 == x2) + +let is_inverse_of (#a #b: Type) (g: (b -> Tot a)) (f: (a -> Tot b)) : Tot prop = + (forall x . {:pattern (g (f x))} g (f x) == x) + +let is_inverse_of_injective (#a #b: Type) (g: (b -> Tot a)) (f: (a -> Tot b)) + x1 x2 +: Lemma + (requires (g `is_inverse_of` f /\ f x1 == f x2)) + (ensures (x1 == x2)) + [SMTPat (g `is_inverse_of` f); SMTPat (f x1); SMTPat (f x2)] += assert (g (f x1) == g (f x2)) + +let is_inverse_of_intro (#a #b: Type) (g: (b -> Tot a)) (f: (a -> Tot b)) + (phi: (x: a) -> Lemma + (g (f x) == x) + ) +: Lemma + (g `is_inverse_of` f) += Classical.forall_intro phi + +#push-options "--print_universes" + +let restricted_frame_preserving_upd + (#a:Type u#a) (p:pcm a) (x y:a) += + restricted_t + (frame_preserving_upd_dom p x) + (frame_preserving_upd_codom p x y) + +let restricted_frame_preserving_upd_intro + (#a:Type u#a) (#p:pcm a) (#x #y: Ghost.erased a) + (f: frame_preserving_upd p x y) +: Tot (restricted_frame_preserving_upd p x y) += + on_dom + (frame_preserving_upd_dom p x) + #(frame_preserving_upd_codom p x y) + (fun v -> f v) + +let restricted_frame_preserving_upd_elim + (#a:Type u#a) (#p:pcm a) (#x #y: Ghost.erased a) + (f: restricted_frame_preserving_upd p x y) +: Tot (frame_preserving_upd p x y) += f + +noeq +type fpu_t + (#a:Type u#a) (p:pcm a) (x y: Ghost.erased a) += { + fpu_f: (frame_preserving_upd_dom p x ^-> a); + fpu_prf: squash (forall (v: frame_preserving_upd_dom p x) . frame_preserving_upd_post p x y v (fpu_f v)); +} + +let mk_restricted_frame_preserving_upd + (#a:Type u#a) (#p:pcm a) (#x #y: Ghost.erased a) + (phi: fpu_t p x y) +: Tot (restricted_frame_preserving_upd p x y) += restricted_frame_preserving_upd_intro #_ #p #x #y (fun v -> phi.fpu_f v) + +noeq type fpu_lift_dom (#t_small: Type) (p_small: pcm t_small) = { + fpu_lift_dom_x: (x:Ghost.erased t_small{~ (Ghost.reveal x == (one p_small))}); + fpu_lift_dom_y: Ghost.erased t_small; + fpu_lift_dom_f: restricted_frame_preserving_upd p_small fpu_lift_dom_x fpu_lift_dom_y; +} + +let fpu_lift_cod (#t_large:Type) (#t_small: Type) (#p_large: pcm t_large) (#p_small: pcm t_small) + (conn_small_to_large: morphism p_small p_large) +: fpu_lift_dom p_small -> Type += fun { fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f} -> + fpu_t p_large (conn_small_to_large.morph x) (conn_small_to_large.morph y) + +let fpu_lift (#t_large:Type) (#t_small: Type) (#p_large: pcm t_large) (#p_small: pcm t_small) + (conn_small_to_large: morphism p_small p_large) +: Type += restricted_t + (fpu_lift_dom p_small) + (fpu_lift_cod conn_small_to_large) + +let fpu_lift_elim (#t_large:Type) (#t_small: Type) (#p_large: pcm t_large) (#p_small: pcm t_small) + (#conn_small_to_large: morphism p_small p_large) + (lift: fpu_lift conn_small_to_large) + (x: Ghost.erased t_small { ~ (Ghost.reveal x == one p_small) }) + (y: Ghost.erased t_small) + (f: frame_preserving_upd p_small x y) +: Tot (frame_preserving_upd p_large (conn_small_to_large.morph x) (conn_small_to_large.morph y)) += let phi = lift ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = restricted_frame_preserving_upd_intro f; }) in + (fun v -> phi.fpu_f v) + +(** A connection from a "large" PCM p_large to a "small" PCM p_small + is composed of an injective morphism small->large + the left inverse + that witnesses its injectivity, along with a way of lifting + frame-preserving updates on p_small to frame-preserving updates on + p_large. + + Like morphisms, we require connections be extensional in order to + prove the associativity of connection composition. *) + +noeq +type connection (#t_large #t_small: Type) (p_large: pcm t_large) (p_small: pcm t_small) = { + conn_small_to_large: morphism p_small p_large; + conn_large_to_small: morphism p_large p_small; + conn_small_to_large_inv: squash (conn_large_to_small.morph `is_inverse_of` conn_small_to_large.morph); + conn_lift_frame_preserving_upd: fpu_lift conn_small_to_large; +} + +let on_dom_nondep + (a #b: Type) + (f: a -> b) +: Tot (a ^-> b) += on_dom a f + +let mkconnection1 + (#t_large #t_small: Type) (#p_large: pcm t_large) (#p_small: pcm t_small) + (conn_small_to_large: morphism p_small p_large) + (conn_large_to_small: morphism p_large p_small) + (conn_small_to_large_inv: + squash (conn_large_to_small.morph `is_inverse_of` conn_small_to_large.morph)) + (conn_lift_frame_preserving_upd_f: + (x:(x:Ghost.erased t_small{~ (Ghost.reveal x == (one p_small))}) -> + y:Ghost.erased t_small -> + restricted_frame_preserving_upd p_small x y -> + v:frame_preserving_upd_dom p_large (conn_small_to_large.morph x) -> + t_large + )) + (conn_lift_frame_preserving_upd_prf: + (x:(x:Ghost.erased t_small{~ (Ghost.reveal x == (one p_small))}) -> + y:Ghost.erased t_small -> + f: restricted_frame_preserving_upd p_small x y -> + v:frame_preserving_upd_dom p_large (conn_small_to_large.morph x) -> + Lemma + (frame_preserving_upd_post p_large (conn_small_to_large.morph x) (conn_small_to_large.morph y) v (conn_lift_frame_preserving_upd_f x y f v)) + )) +: connection p_large p_small = { + conn_small_to_large = conn_small_to_large; + conn_large_to_small = conn_large_to_small; + conn_small_to_large_inv = conn_small_to_large_inv; + conn_lift_frame_preserving_upd = + on_dom + (fpu_lift_dom p_small) + (fun (z: fpu_lift_dom p_small) -> + (let {fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; } = z in { + fpu_f = on_dom_nondep + (frame_preserving_upd_dom p_large (Ghost.reveal (Ghost.hide (conn_small_to_large.morph x)))) + (conn_lift_frame_preserving_upd_f x y f); + fpu_prf = Classical.forall_intro (conn_lift_frame_preserving_upd_prf x y f) + } <: fpu_t p_large (conn_small_to_large.morph x) (conn_small_to_large.morph y)) <: fpu_lift_cod conn_small_to_large z); +} + +let mkconnection (#t_large #t_small: Type) (#p_large: pcm t_large) (#p_small: pcm t_small) + (conn_small_to_large: morphism p_small p_large) + (conn_large_to_small: morphism p_large p_small) + (conn_small_to_large_inv: + squash (conn_large_to_small.morph `is_inverse_of` conn_small_to_large.morph)) + (conn_lift_frame_preserving_upd: + (x:(x:Ghost.erased t_small{~ (Ghost.reveal x == (one p_small))}) -> + y:Ghost.erased t_small -> + restricted_frame_preserving_upd p_small x y -> + frame_preserving_upd p_large (conn_small_to_large.morph x) (conn_small_to_large.morph y))) +: connection p_large p_small = + mkconnection1 + conn_small_to_large + conn_large_to_small + conn_small_to_large_inv + conn_lift_frame_preserving_upd + (fun x y f v -> ()) + +let connection_eq' #a (#p: pcm a) #b (#q: pcm b) (l m: p `connection` q) +: Lemma + (requires l.conn_small_to_large.morph `feq` m.conn_small_to_large.morph /\ + l.conn_large_to_small.morph `feq` m.conn_large_to_small.morph /\ + l.conn_lift_frame_preserving_upd `feq` m.conn_lift_frame_preserving_upd) + (ensures l == m) += () + +let extensionality (a: Type) (b: (a -> Type)) (f g: restricted_t a b) + : Lemma (ensures (feq #a #b f g <==> f == g)) += FStar.FunctionalExtensionality.extensionality a b f g + +let extensionality_nondep (a1 a2: Type) (b: Type) + (f: a1 ^-> b) + (g: a2 ^-> b) +: Lemma + (requires (a1 == a2)) + (ensures (feq f g <==> f == g)) += extensionality _ _ f g + +let connection_eq_gen + #a (#p: pcm a) #b (#q: pcm b) (l m: p `connection` q) + (sq: squash ( + l.conn_small_to_large.morph `feq` m.conn_small_to_large.morph /\ + l.conn_large_to_small.morph `feq` m.conn_large_to_small.morph + )) + (phi: + (x: Ghost.erased b { ~ (Ghost.reveal x == one q) }) -> + (y: Ghost.erased b) -> + (f: restricted_frame_preserving_upd q x y) -> + (v: frame_preserving_upd_dom p (l.conn_small_to_large.morph x)) -> + Lemma + ((l.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f v == (m.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f v) + ) +: Lemma + (l == m) += let psi + (x: Ghost.erased b { ~ (Ghost.reveal x == one q) }) + (y: Ghost.erased b) + (f: restricted_frame_preserving_upd q x y) + : Lemma + ((l.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f == (m.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f) + = Classical.forall_intro (phi x y f); + extensionality_nondep + (frame_preserving_upd_dom p (Ghost.reveal (Ghost.hide (l.conn_small_to_large.morph x)))) + (frame_preserving_upd_dom p (Ghost.reveal (Ghost.hide (m.conn_small_to_large.morph x)))) + a + (l.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f + (m.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f + in + Classical.forall_intro_3 psi; + connection_eq' l m + +let connection_compose (#a #b #c: Type) (#pa: pcm a) (#pb: pcm b) (#pc: pcm c) (fab: connection pa pb) (fbc: connection pb pc) : Tot (connection pa pc) = + mkconnection + (fbc.conn_small_to_large `morphism_compose` fab.conn_small_to_large) + (fab.conn_large_to_small `morphism_compose` fbc.conn_large_to_small) + () + (fun xc yc f -> + let xb = Ghost.hide (fbc.conn_small_to_large.morph xc) in + let yb = Ghost.hide (fbc.conn_small_to_large.morph yc) in + let fb = fbc.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = xc; fpu_lift_dom_y = yc; fpu_lift_dom_f = f }) in + mk_restricted_frame_preserving_upd (fab.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = xb; fpu_lift_dom_y = yb; fpu_lift_dom_f = mk_restricted_frame_preserving_upd fb }) )) + +let connection_compose_fpu (#a #b #c: Type) (#pa: pcm a) (#pb: pcm b) (#pc: pcm c) (fab: connection pa pb) (fbc: connection pb pc) + (xc: Ghost.erased c { ~ (Ghost.reveal xc == one pc) }) + (yc: Ghost.erased c) + (fc: restricted_frame_preserving_upd pc xc yc) + (fb: restricted_frame_preserving_upd pb (Ghost.hide (fbc.conn_small_to_large.morph xc)) (Ghost.hide (fbc.conn_small_to_large.morph yc))) +: Lemma + (requires ( + fb `feq` mk_restricted_frame_preserving_upd (fbc.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = xc; fpu_lift_dom_y = yc; fpu_lift_dom_f = fc; })) + )) + (ensures ( + (connection_compose fab fbc).conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = xc; fpu_lift_dom_y = yc; fpu_lift_dom_f = fc; }) == + fab.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = Ghost.hide (fbc.conn_small_to_large.morph xc); fpu_lift_dom_y = Ghost.hide (fbc.conn_small_to_large.morph yc); fpu_lift_dom_f = fb; })) + ) += let c = connection_compose fab fbc in + let f1 = c.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = xc; fpu_lift_dom_y = yc; fpu_lift_dom_f = fc; }) in + let f2 = fab.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = Ghost.hide (fbc.conn_small_to_large.morph xc); fpu_lift_dom_y = Ghost.hide (fbc.conn_small_to_large.morph yc); fpu_lift_dom_f = fb; }) in + extensionality_nondep + (frame_preserving_upd_dom pa (Ghost.hide (c.conn_small_to_large.morph xc))) + (frame_preserving_upd_dom pa (Ghost.hide (fab.conn_small_to_large.morph (Ghost.hide (fbc.conn_small_to_large.morph xc))))) + a + f1.fpu_f + f2.fpu_f + +let connection_id + (#a: Type) + (p: pcm a) +: Tot (connection p p) += mkconnection + (morphism_id p) + (morphism_id p) + () + (fun _ _ f -> f) + +let connection_id_fpu + (#a: Type) + (p: pcm a) + (x: Ghost.erased a { ~ (Ghost.reveal x == one p) }) + (y: Ghost.erased a) + (f: restricted_frame_preserving_upd p x y) + (v: frame_preserving_upd_dom p x) +: Lemma + (((connection_id p).conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f })).fpu_f v == f v) += () + +let connection_compose_id_left + (#t_large #t_small: Type) (#p_large: pcm t_large) (#p_small: pcm t_small) + (c: connection p_large p_small) +: Lemma + (connection_id p_large `connection_compose` c == c) += connection_eq_gen + (connection_id p_large `connection_compose` c) c + () + (fun x y f v -> ()) + +let connection_compose_id_right + (#t_large #t_small: Type) (#p_large: pcm t_large) (#p_small: pcm t_small) + (c: connection p_large p_small) +: Lemma + (c `connection_compose` connection_id p_small == c) += connection_eq_gen + (c `connection_compose` connection_id p_small) c + () + (fun x y f v -> ()) + +#push-options "--z3rlimit 16" + +let connection_compose_assoc + (#t1 #t2 #t3 #t4: Type) + (#p1: pcm t1) + (#p2: pcm t2) + (#p3: pcm t3) + (#p4: pcm t4) + (c12: connection p1 p2) + (c23: connection p2 p3) + (c34: connection p3 p4) +: Lemma + ((c12 `connection_compose` c23) `connection_compose` c34 == c12 `connection_compose` (c23 `connection_compose` c34)) += + connection_eq_gen + ((c12 `connection_compose` c23) `connection_compose` c34) + (c12 `connection_compose` (c23 `connection_compose` c34)) + () + (fun x y f v -> ()) + +#pop-options + +let morph_refine (pa: pcm 'a) (pb: pcm 'b) (morph: 'a -> 'b) + (xa: 'a { p_refine pa xa }) += squash ( + p_refine pb (morph xa) + ) + +noeq +type isomorphism (#t1 #t2: Type) (p1: pcm t1) (p2: pcm t2) = { + iso_1_2: morphism p1 p2; + iso_2_1: morphism p2 p1; + iso_1_2_inv_2_1: squash (iso_1_2.morph `is_inverse_of` iso_2_1.morph); + iso_2_1_inv_1_2: squash (iso_2_1.morph `is_inverse_of` iso_1_2.morph); + iso_1_2_refine: restricted_t (x1: t1 { p_refine p1 x1 }) (morph_refine p1 p2 iso_1_2.morph); + iso_2_1_refine: restricted_t (x2: t2 { p_refine p2 x2 }) (morph_refine p2 p1 iso_2_1.morph); +} + +let isomorphism_eq + (#t1 #t2: Type) (#p1: pcm t1) (#p2: pcm t2) (i i': isomorphism p1 p2) +: Lemma + (requires ( + i.iso_1_2.morph `feq` i'.iso_1_2.morph /\ + i.iso_2_1.morph `feq` i'.iso_2_1.morph + )) + (ensures ( + i == i' + )) += assert (i.iso_1_2_refine `feq` i'.iso_1_2_refine); + assert (i.iso_2_1_refine `feq` i'.iso_2_1_refine) + +let mkisomorphism + (#t1 #t2: Type) (#p1: pcm t1) (#p2: pcm t2) + (iso_1_2: morphism p1 p2) + (iso_2_1: morphism p2 p1) + (iso_1_2_inv_2_1: squash (iso_1_2.morph `is_inverse_of` iso_2_1.morph)) + (iso_2_1_inv_1_2: squash (iso_2_1.morph `is_inverse_of` iso_1_2.morph)) + (iso_1_2_refine: + (x1: t1) -> + Lemma + (requires (p_refine p1 x1)) + (ensures (p_refine p2 (iso_1_2.morph x1))) + ) + (iso_2_1_refine: + (x2: t2) -> + Lemma + (requires (p_refine p2 x2)) + (ensures (p_refine p1 (iso_2_1.morph x2))) + ) +: Tot (isomorphism p1 p2) += { + iso_1_2 = iso_1_2; + iso_2_1 = iso_2_1; + iso_1_2_inv_2_1 = iso_1_2_inv_2_1; + iso_2_1_inv_1_2 = iso_2_1_inv_1_2; + iso_1_2_refine = on_dom (x1: t1 { p_refine p1 x1 }) #(morph_refine p1 p2 iso_1_2.morph) (fun x1 -> iso_1_2_refine x1); + iso_2_1_refine = on_dom (x2: t2 { p_refine p2 x2 }) #(morph_refine p2 p1 iso_2_1.morph) (fun x2 -> iso_2_1_refine x2); +} + +let isomorphism_id + (#t: Type) + (p: pcm t) +: Tot (isomorphism p p) += mkisomorphism + (morphism_id p) + (morphism_id p) + () + () + (fun _ -> ()) + (fun _ -> ()) + +let isomorphism_inverse + (#t1 #t2: Type) (#p1: pcm t1) (#p2: pcm t2) + (i: isomorphism p1 p2) +: Tot (isomorphism p2 p1) += { + iso_1_2 = i.iso_2_1; + iso_2_1 = i.iso_1_2; + iso_1_2_inv_2_1 = i.iso_2_1_inv_1_2; + iso_2_1_inv_1_2 = i.iso_1_2_inv_2_1; + iso_1_2_refine = i.iso_2_1_refine; + iso_2_1_refine = i.iso_1_2_refine; +} + +let isomorphism_inverse_involutive + (#t1 #t2: Type) (#p1: pcm t1) (#p2: pcm t2) + (i: isomorphism p1 p2) +: Lemma + (isomorphism_inverse (isomorphism_inverse i) == i) += isomorphism_inverse (isomorphism_inverse i) `isomorphism_eq` i + +let connection_of_isomorphism_fpu' + (#t1 #t2: Type) + (#p1: pcm t1) + (#p2: pcm t2) + (i: isomorphism p1 p2) + (x: Ghost.erased t2 { ~ (Ghost.reveal x == one p2) }) + (y: Ghost.erased t2) + (f: restricted_frame_preserving_upd p2 x y) + (v: frame_preserving_upd_dom p1 (i.iso_2_1.morph x)) +: Tot t1 += + let x1 = Ghost.hide (i.iso_2_1.morph x) in + compatible_morphism i.iso_1_2 x1 v; + i.iso_1_2_refine v; + let v2' = f (i.iso_1_2.morph v) in + let v' = i.iso_2_1.morph v2' in + v' + +let connection_of_isomorphism_fpu'_correct + (#t1 #t2: Type) + (#p1: pcm t1) + (#p2: pcm t2) + (i: isomorphism p1 p2) + (x: Ghost.erased t2 { ~ (Ghost.reveal x == one p2) }) + (y: Ghost.erased t2) + (f: restricted_frame_preserving_upd p2 x y) + (v: frame_preserving_upd_dom p1 (i.iso_2_1.morph x)) +: Lemma + ( + let x1 = i.iso_2_1.morph x in + let y1 = i.iso_2_1.morph y in + let v_new = connection_of_isomorphism_fpu' i x y f v in + p_refine p1 v_new /\ + compatible p1 y1 v_new /\ + (forall (frame: _ {composable p1 x1 frame}).{:pattern composable p1 x1 frame} + composable p1 y1 frame /\ + (op p1 x1 frame == v ==> op p1 y1 frame == v_new)) + ) += + let x1 = Ghost.hide (i.iso_2_1.morph x) in + compatible_morphism i.iso_1_2 x1 v; + i.iso_1_2_refine v; + let v2' = f (i.iso_1_2.morph v) in + let v' = i.iso_2_1.morph v2' in + i.iso_2_1_refine v2' ; + assert (p_refine p1 v'); + compatible_morphism i.iso_2_1 y v2' ; + let y1 = Ghost.hide (i.iso_2_1.morph y) in + assert (compatible p1 y1 v'); + let aux + (frame: t1 { composable p1 x1 frame }) + : Lemma + (composable p1 y1 frame /\ + (op p1 x1 frame == v ==> op p1 y1 frame == v')) + [SMTPat (composable p1 x1 frame)] + = + let frame2 = i.iso_1_2.morph frame in + assert (composable p2 x frame2); + assert (composable p1 y1 (i.iso_2_1.morph frame2)); + () + in + () + +let connection_of_isomorphism_fpu + (#t1 #t2: Type) + (#p1: pcm t1) + (#p2: pcm t2) + (i: isomorphism p1 p2) + (x: Ghost.erased t2 { ~ (Ghost.reveal x == one p2) }) + (y: Ghost.erased t2) + (f: restricted_frame_preserving_upd p2 x y) +: Tot (restricted_frame_preserving_upd p1 (i.iso_2_1.morph x) (i.iso_2_1.morph y)) += + Classical.forall_intro (connection_of_isomorphism_fpu'_correct i x y f); + restricted_frame_preserving_upd_intro #_ #p1 #(i.iso_2_1.morph x) #(i.iso_2_1.morph y) (fun z -> connection_of_isomorphism_fpu' i x y f z) + +let connection_of_isomorphism_fpu_inverse' + (#t1 #t2: Type) + (#p1: pcm t1) + (#p2: pcm t2) + (i: isomorphism p1 p2) + (x: Ghost.erased t2 { ~ (Ghost.reveal x == one p2) }) + (y: Ghost.erased t2) + (f: restricted_frame_preserving_upd p2 x y) + (v: frame_preserving_upd_dom p2 x) +: Lemma + (connection_of_isomorphism_fpu (isomorphism_inverse i) (i.iso_2_1.morph x) (i.iso_2_1.morph y) (connection_of_isomorphism_fpu i x y f) v == f v) += compatible_morphism i.iso_2_1 x v; + i.iso_2_1_refine v + +let connection_of_isomorphism_fpu_inverse + (#t1 #t2: Type) + (#p1: pcm t1) + (#p2: pcm t2) + (i: isomorphism p1 p2) + (x: Ghost.erased t2 { ~ (Ghost.reveal x == one p2) }) + (y: Ghost.erased t2) + (f: restricted_frame_preserving_upd p2 x y) +: Lemma + (connection_of_isomorphism_fpu (isomorphism_inverse i) (i.iso_2_1.morph x) (i.iso_2_1.morph y) (connection_of_isomorphism_fpu i x y f) == f) += Classical.forall_intro (connection_of_isomorphism_fpu_inverse' i x y f); + assert (connection_of_isomorphism_fpu (isomorphism_inverse i) (i.iso_2_1.morph x) (i.iso_2_1.morph y) (connection_of_isomorphism_fpu i x y f) `feq` f) + +let connection_of_isomorphism + (#t1 #t2: Type) + (#p1: pcm t1) + (#p2: pcm t2) + (i: isomorphism p1 p2) +: Tot (connection p1 p2) += mkconnection + i.iso_2_1 + i.iso_1_2 + i.iso_1_2_inv_2_1 + (connection_of_isomorphism_fpu i) + +let connection_of_isomorphism_fpu_eq + (#t1 #t2: Type) + (#p1: pcm t1) + (#p2: pcm t2) + (i: isomorphism p1 p2) + (x: Ghost.erased t2 { ~ (Ghost.reveal x == one p2) }) + (y: Ghost.erased t2) + (f: restricted_frame_preserving_upd p2 x y) + (v: frame_preserving_upd_dom p1 (i.iso_2_1.morph x)) +: Lemma + (((connection_of_isomorphism i).conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f v == connection_of_isomorphism_fpu' i x y f v) += () + +let connection_of_isomorphism_inverse_left + (#t1 #t2: Type) + (#p1: pcm t1) + (#p2: pcm t2) + (i: isomorphism p1 p2) +: Lemma + (connection_of_isomorphism (isomorphism_inverse i) `connection_compose` connection_of_isomorphism i == connection_id _) += Classical.forall_intro_3 (connection_of_isomorphism_fpu_inverse i); + connection_eq_gen + (connection_of_isomorphism (isomorphism_inverse i) `connection_compose` connection_of_isomorphism i) + (connection_id _) + () + (fun x y f v -> ()) + +let connection_of_isomorphism_inverse_right + (#t1 #t2: Type) + (#p1: pcm t1) + (#p2: pcm t2) + (i: isomorphism p1 p2) +: Lemma + (connection_of_isomorphism i `connection_compose` connection_of_isomorphism (isomorphism_inverse i) == connection_id _) += isomorphism_inverse_involutive i; + connection_of_isomorphism_inverse_left (isomorphism_inverse i) diff --git a/lib/steel/c/Steel.C.Model.Frac.fst b/lib/steel/c/Steel.C.Model.Frac.fst new file mode 100644 index 000000000..ca26bee69 --- /dev/null +++ b/lib/steel/c/Steel.C.Model.Frac.fst @@ -0,0 +1,53 @@ +module Steel.C.Model.Frac +include Steel.ST.C.Model.Frac + +module STC = Steel.ST.Coercions // to use frac_pcm_write +module P = FStar.PCM +open Steel.C.Model.PCM +open Steel.C.Model.Ref +open Steel.Effect +open Steel.FractionalPermission + +let frac_view + (a: Type) + (p: perm) +: Tot (sel_view (pcm_frac #a) a false) += { + to_view_prop = (fun x -> Some? x == true); + to_view = (fun x -> let Some (v, _) = x in v); + to_carrier = (fun v -> Some (v, p)); + to_carrier_not_one = (); + to_view_frame = (fun v frame -> ()); +} + +let frac_read_sel + (#a: Type u#0) (#b: Type u#0) + (#p: perm) + (r: ref a (pcm_frac #b)) +: Steel b + (pts_to_view r (frac_view _ p)) + (fun _ -> pts_to_view r (frac_view _ p)) + (requires (fun _ -> True)) + (ensures (fun h res h' -> + res == h (pts_to_view r (frac_view _ p)) /\ + res == h' (pts_to_view r (frac_view _ p)) + )) += ref_read_sel r (frac_view _ p) + +let frac_write_sel + (#a: Type u#0) (#b: Type u#0) + (#p: perm) + (r: ref a (pcm_frac #b)) + (w: b) +: Steel unit + (pts_to_view r (frac_view _ p)) + (fun _ -> pts_to_view r (frac_view _ p)) + (requires (fun _ -> p == full_perm)) + (ensures (fun h _ h' -> + w == h' (pts_to_view r (frac_view _ p)) + )) += + let _ = pts_to_view_elim r (frac_view _ _) in + frac_pcm_write r _ w; + pts_to_view_intro r _ (frac_view _ p) w + diff --git a/lib/steel/c/Steel.C.Model.PCM.fst b/lib/steel/c/Steel.C.Model.PCM.fst new file mode 100644 index 000000000..3fda1089c --- /dev/null +++ b/lib/steel/c/Steel.C.Model.PCM.fst @@ -0,0 +1,362 @@ +module Steel.C.Model.PCM +module P = FStar.PCM +open FStar.FunctionalExtensionality +open FStar.IndefiniteDescription + +let symrel_codom (#t: Type) (x: t) : Tot Type0 = bool + +let symrel (a: Type u#a) = c:(restricted_g_t (a & a) symrel_codom) { (forall x y. c (x, y) == c (y, x)) } + +let op_dom (#a: Type) (composable: symrel a) : Type = (xy: (a & a) { composable xy == true }) + +let op_codom (#a: Type) (composable: symrel a) (x: op_dom composable) : Type = a + +noeq +type pcm' (a:Type u#a) : Type u#a = { + composable: symrel a; + op: restricted_t (op_dom composable) (op_codom composable); + one:a +} + +let pcm'_eq (#a: Type u#a) (p1 p2: pcm' a) : Lemma + (requires ( + p1.composable `feq_g` p2.composable /\ + p1.op `feq` p2.op /\ + p1.one == p2.one + )) + (ensures (p1 == p2)) += () + +let fstar_pcm'_of_pcm' + (#a: Type) + (p: pcm' a) +: Tot (P.pcm' a) += { + P.composable = (fun x y -> p.composable (x, y) == true); + P.op = (fun x y -> p.op (x, y)); + P.one = p.one; +} + +let composable_of_fstar_composable + (#a: Type) + (p: P.pcm' a) +: Tot (symrel a) += on_dom_g (a & a) (fun xy -> strong_excluded_middle (p.P.composable (fst xy) (snd xy)) <: bool) + +let op_of_fstar_op + (#a: Type) + (p: P.pcm' a) +: Tot (restricted_t (op_dom (composable_of_fstar_composable p)) (op_codom (composable_of_fstar_composable p))) += on_dom (op_dom (composable_of_fstar_composable p)) (fun xy -> p.P.op (fst xy) (snd xy)) + +let pcm'_of_fstar_pcm' + (#a: Type) + (p: P.pcm' a) +: Tot (pcm' a) += { + composable = composable_of_fstar_composable p; + op = op_of_fstar_op p; + one = p.P.one; +} + +let pcm'_of_fstar_pcm'_of_pcm' + (#a: Type) + (p: pcm' a) +: Lemma + (pcm'_of_fstar_pcm' (fstar_pcm'_of_pcm' p) == p) += pcm'_of_fstar_pcm' (fstar_pcm'_of_pcm' p) `pcm'_eq` p + +let lem_commutative_codom + (#a: Type u#a) (p:pcm' a) (xy: op_dom p.composable) +: Tot Type += squash (p.op xy == p.op (snd xy, fst xy)) + +let lem_commutative (#a: Type u#a) (p:pcm' a) = restricted_t (op_dom p.composable) (lem_commutative_codom p) + +let lem_assoc_l_dom (#a: Type u#a) (p: pcm' a) = + (xyz: (a & a & a) { + let (x, y, z) = xyz in + p.composable (y, z) == true /\ + p.composable (x, p.op (y, z)) == true + }) + +let lem_assoc_l_codom (#a :Type u#a) (p: pcm' a) (xyz: lem_assoc_l_dom p) = + squash ( + let (x, y, z) = xyz in + p.composable (x, y) == true /\ + p.composable (p.op (x, y), z) == true /\ + p.op (x, p.op (y, z)) == p.op (p.op (x, y), z) + ) + +let lem_assoc_l (#a :Type u#a) (p: pcm' a) = + restricted_t (lem_assoc_l_dom p) (lem_assoc_l_codom p) + +let lem_assoc_r_dom (#a: Type u#a) (p: pcm' a) = + (xyz: (a & a & a) { + let (x, y, z) = xyz in + p.composable (x, y) == true /\ + p.composable (p.op (x, y), z) == true + }) + +let lem_assoc_r_codom (#a :Type u#a) (p: pcm' a) (xyz: lem_assoc_r_dom p) = + squash ( + let (x, y, z) = xyz in + p.composable (y, z) == true /\ + p.composable (x, p.op (y, z)) == true /\ + p.op (x, p.op (y, z)) == p.op (p.op (x, y), z) + ) + +let lem_assoc_r (#a: Type u#a) (p: pcm' a) = + restricted_t (lem_assoc_r_dom p) (lem_assoc_r_codom p) + +let lem_is_unit_codom (#a: Type u#a) (p: pcm' a) (x: a) : Tot Type0 = + squash ( + p.composable (x, p.one) == true /\ + p.op (x, p.one) == x + ) + +let lem_is_unit (#a: Type) (p: pcm' a) = + restricted_t a (lem_is_unit_codom p) + +noeq +type pcm0 (a:Type u#a) : Type u#a = { + p:pcm' a; + comm:lem_commutative p; + assoc: lem_assoc_l p; + assoc_r: lem_assoc_r p; + is_unit: lem_is_unit p; + refine: restricted_g_t a symrel_codom; +} + +let pcm_eq (#a: Type u#a) (p1 p2: pcm0 a) : Lemma + (requires ( + p1.p.composable `feq_g` p2.p.composable /\ + p1.p.op `feq` p2.p.op /\ + p1.p.one == p2.p.one /\ + p1.refine `feq_g` p2.refine + )) + (ensures (p1 == p2)) += assert (p1.comm `feq` p2.comm); + assert (p1.assoc `feq` p2.assoc); + assert (p1.assoc_r `feq` p2.assoc_r); + assert (p1.is_unit `feq` p2.is_unit) + +let composable (#a: Type u#a) (p:pcm0 a) (x y:a) : Tot prop = p.p.composable (x, y) == true + +let one (#a: Type) (p: pcm0 a) : Tot a = p.p.one + +let op (#a: Type u#a) (p:pcm0 a) (x:a) (y:a{composable p x y}) : Tot a = p.p.op (x, y) + +let op_comm + (#a: Type u#a) + (p: pcm0 a) + (x y: a) +: Lemma + (requires (composable p x y)) + (ensures (composable p y x /\ op p x y == op p y x)) + [SMTPat (composable p x y)] += p.comm (x, y) + +let op_assoc_l + (#a: Type u#a) + (p: pcm0 a) + (x y z: a) +: Lemma + (requires (composable p y z /\ composable p x (op p y z))) + (ensures ( + composable p x y /\ composable p (op p x y) z /\ + op p x (op p y z) == op p (op p x y) z + )) + [SMTPat (composable p y z); SMTPat (composable p x (op p y z))] += p.assoc (x, y, z) + +let op_assoc_r + (#a: Type u#a) + (p: pcm0 a) + (x y z: a) +: Lemma + (requires (composable p x y /\ composable p (op p x y) z)) + (ensures ( + composable p y z /\ composable p x (op p y z) /\ + op p x (op p y z) == op p (op p x y) z + )) + [SMTPat (composable p x y); SMTPat (composable p (op p x y) z)] += p.assoc_r (x, y, z) + +let p_refine (#a: Type) (p: pcm0 a) (x: a) : Tot prop = + p.refine x == true + +let pcm0_ext #a p1 p2 composable_eq op_eq p_refine_eq one_eq = + let composable_eq' + (xy: (a & a)) + : Lemma + (p1.p.composable xy == p2.p.composable xy) + = let (x, y) = xy in + composable_eq x y + in + Classical.forall_intro composable_eq'; + assert (p1.p.composable `feq_g` p2.p.composable); + assert (op_dom p1.p.composable == op_dom p2.p.composable); + let op_eq' + (xy: op_dom p1.p.composable) + : Lemma + (p1.p.op xy == p2.p.op xy) + = let (x, y) = xy in + op_eq x y + in + Classical.forall_intro op_eq'; + Classical.forall_intro p_refine_eq; + pcm_eq p1 p2 + +let pcm_of_fstar_pcm + (#a: Type) + (p: P.pcm a) +: Tot (pcm0 a) += let pp = pcm'_of_fstar_pcm' p.P.p in + { + p = pp; + comm = on_dom (op_dom pp.composable) (fun xy -> p.P.comm (fst xy) (snd xy) <: lem_commutative_codom pp xy); + assoc = on_dom (lem_assoc_l_dom pp) (fun xyz -> let (x, y, z) = xyz in p.P.assoc x y z <: lem_assoc_l_codom pp xyz); + assoc_r = on_dom (lem_assoc_r_dom pp) (fun xyz -> let (x, y, z) = xyz in p.P.assoc_r x y z <: lem_assoc_r_codom pp xyz); + is_unit = on_dom a (fun x -> p.P.is_unit x <: lem_is_unit_codom pp x); + refine = on_dom_g _ (fun x -> strong_excluded_middle (p.P.refine x) <: bool); +} + +let fstar_pcm_of_pcm + (#a: Type) + (p: pcm0 a) +: Tot (P.pcm a) += let pp = fstar_pcm'_of_pcm' p.p in + { + P.p = pp; + P.comm = (fun x y -> p.comm (x, y)); + P.assoc = (fun x y z -> p.assoc (x, y, z)); + P.assoc_r = (fun x y z -> p.assoc_r (x, y, z)); + P.is_unit = (fun x -> + let _ : squash ( + p.p.composable (x, p.p.one) == true /\ + p.p.op (x, p.p.one) == x + ) = + p.is_unit x + in + assert (p.p.composable (x, p.p.one) == true); + assert (p.p.op (x, p.p.one) == x) + ); + P.refine = (fun x -> p.refine x == true); +} + +let pcm_of_fstar_pcm_of_pcm + (#a: Type) + (p: pcm0 a) +: Lemma + (pcm_of_fstar_pcm (fstar_pcm_of_pcm p) == p) += pcm_of_fstar_pcm (fstar_pcm_of_pcm p) `pcm_eq` p + +let composable_pcm_of_fstar_pcm + (#a: Type) + (p: P.pcm a) + (x y: a) +: Lemma + ((composable (pcm_of_fstar_pcm p) x y <==> P.composable p x y) /\ + (composable (pcm_of_fstar_pcm p) x y ==> op (pcm_of_fstar_pcm p) x y == P.op p x y)) + [SMTPat (composable (pcm_of_fstar_pcm p) x y)] += () + +let one_pcm_of_fstar_pcm p = () + +let p_refine_pcm_of_fstar_pcm + (#a: Type) + (p: P.pcm a) + (x: a) +: Lemma + (p_refine (pcm_of_fstar_pcm p) x <==> p.P.refine x) + [SMTPat (p_refine (pcm_of_fstar_pcm p) x)] += () + +let composable_fstar_pcm_of_pcm p x y = () + +let one_fstar_pcm_of_pcm p = () + +let refine_fstar_pcm_of_pcm p x = () + +let is_unit (#a: Type u#a) (p:pcm0 a) + (x:a) +: Lemma (composable p x (one p) /\ + op p x (one p) == x) += (fstar_pcm_of_pcm p).P.is_unit x + +let compatible_intro + (#a: Type u#a) + (pcm: pcm0 a) + (x y: a) + (frame: a) +: Lemma + (requires (composable pcm x frame /\ op pcm frame x == y)) + (ensures (compatible pcm x y)) += () + +let compatible_elim + (#a: Type u#a) + (pcm: pcm0 a) + (x y: a) +: Ghost a + (requires (compatible pcm x y)) + (ensures (fun frame -> + composable pcm x frame /\ + op pcm frame x == y + )) += FStar.IndefiniteDescription.indefinite_description_ghost _ (fun frame -> + composable pcm x frame /\ + op pcm frame x == y + ) + +let compatible_refl + (#a: Type u#a) + (pcm: pcm0 a) + (x: a) +: Lemma + (compatible pcm x x) += compatible_intro pcm x x (one pcm) + +let compatible_fstar_pcm_of_pcm p x y = () +let compatible_pcm_of_fstar_pcm p x y = () +let exclusive_fstar_pcm_of_pcm p x = () +let exclusive_pcm_of_fstar_pcm p x = () + +let frame_preserving_upd_post_intro + p x y f v prf1 prf2 prf3 += prf1 (); + Classical.forall_intro (Classical.move_requires (prf2)); + Classical.forall_intro (Classical.move_requires (prf3)) + +let frame_preserving_upd_post_intro' + #a p x y f v prf1 prf2 prf3 += + frame_preserving_upd_post_intro + p x y f v + (fun () -> + prf1 (); + let frame = compatible_elim p x v in + prf2 frame; + prf3 frame; + compatible_intro p y (f v) frame + ) + (fun frame -> prf2 frame) + (fun frame -> prf3 frame) + +let frame_preserving_upd_intro + p x y f prf1 prf2 prf3 += fun v -> + frame_preserving_upd_post_intro p x y f v (fun _ -> prf1 v) (prf2 v) (prf3 v); + f v + +let fstar_fpu_of_fpu + (#a: Type u#a) + (p: pcm0 a) + (x y: Ghost.erased a) + (f: frame_preserving_upd p x y) +: Tot (FStar.PCM.frame_preserving_upd (fstar_pcm_of_pcm p) x y) += fun v -> + let y : a = f v in + assert (forall frame . P.composable (fstar_pcm_of_pcm p) x frame <==> composable p x frame); + y diff --git a/lib/steel/c/Steel.C.Model.PCM.fsti b/lib/steel/c/Steel.C.Model.PCM.fsti new file mode 100644 index 000000000..e177ae81c --- /dev/null +++ b/lib/steel/c/Steel.C.Model.PCM.fsti @@ -0,0 +1,412 @@ +module Steel.C.Model.PCM +module P = FStar.PCM +open FStar.FunctionalExtensionality +open FStar.IndefiniteDescription + +val pcm0 (a:Type u#a) : Type u#a + +val composable (#a: Type u#a) (p:pcm0 a) (x y:a) : Tot prop + +val one (#a: Type) (p: pcm0 a) : Tot a + +val op (#a: Type u#a) (p:pcm0 a) (x:a) (y:a{composable p x y}) : Tot a + +val op_comm + (#a: Type u#a) + (p: pcm0 a) + (x y: a) +: Lemma + (requires (composable p x y)) + (ensures (composable p y x /\ op p x y == op p y x)) + [SMTPat (composable p x y)] + +val op_assoc_l + (#a: Type u#a) + (p: pcm0 a) + (x y z: a) +: Lemma + (requires (composable p y z /\ composable p x (op p y z))) + (ensures ( + composable p x y /\ composable p (op p x y) z /\ + op p x (op p y z) == op p (op p x y) z + )) + [SMTPat (composable p y z); SMTPat (composable p x (op p y z))] + +val op_assoc_r + (#a: Type u#a) + (p: pcm0 a) + (x y z: a) +: Lemma + (requires (composable p x y /\ composable p (op p x y) z)) + (ensures ( + composable p y z /\ composable p x (op p y z) /\ + op p x (op p y z) == op p (op p x y) z + )) + [SMTPat (composable p x y); SMTPat (composable p (op p x y) z)] + +val p_refine (#a: Type) (p: pcm0 a) (x: a) : Tot prop + +val pcm0_ext (#a: Type u#a) (p1 p2: pcm0 a) + (composable_eq: ( + (x: a) -> + (y: a) -> + Lemma + (composable p1 x y <==> composable p2 x y) + )) + (op_eq: ( + (x: a) -> + (y: a) -> + Lemma + (requires (composable p1 x y /\ composable p2 x y)) + (ensures ( + op p1 x y == op p2 x y + )) + )) + (p_refine_eq: ( + (x: a) -> + Lemma + (p_refine p1 x <==> p_refine p2 x) + )) + (one_eq: squash (one p1 == one p2)) +: Lemma + (p1 == p2) + +val pcm_of_fstar_pcm + (#a: Type) + (p: P.pcm a) +: Tot (pcm0 a) + +val fstar_pcm_of_pcm + (#a: Type) + (p: pcm0 a) +: Tot (P.pcm a) + +val pcm_of_fstar_pcm_of_pcm + (#a: Type) + (p: pcm0 a) +: Lemma + (pcm_of_fstar_pcm (fstar_pcm_of_pcm p) == p) + [SMTPat (pcm_of_fstar_pcm (fstar_pcm_of_pcm p))] + +val composable_pcm_of_fstar_pcm + (#a: Type) + (p: P.pcm a) + (x y: a) +: Lemma + ((composable (pcm_of_fstar_pcm p) x y <==> P.composable p x y) /\ + (composable (pcm_of_fstar_pcm p) x y ==> op (pcm_of_fstar_pcm p) x y == P.op p x y)) + [SMTPat (composable (pcm_of_fstar_pcm p) x y)] + +val one_pcm_of_fstar_pcm + (#a: Type) + (p: P.pcm a) +: Lemma + (one (pcm_of_fstar_pcm p) == P.one p) + [SMTPat (one (pcm_of_fstar_pcm p))] + +val p_refine_pcm_of_fstar_pcm + (#a: Type) + (p: P.pcm a) + (x: a) +: Lemma + (p_refine (pcm_of_fstar_pcm p) x <==> p.P.refine x) + [SMTPat (p_refine (pcm_of_fstar_pcm p) x)] + +val composable_fstar_pcm_of_pcm + (#a: Type) + (p: pcm0 a) + (x y: a) +: Lemma + ((P.composable (fstar_pcm_of_pcm p) x y <==> composable p x y) /\ + (P.composable (fstar_pcm_of_pcm p) x y ==> P.op (fstar_pcm_of_pcm p) x y == op p x y)) + [SMTPat (P.composable (fstar_pcm_of_pcm p) x y)] + +val one_fstar_pcm_of_pcm + (#a: Type) + (p: pcm0 a) +: Lemma + (P.one (fstar_pcm_of_pcm p) == one p) + [SMTPat (P.one (fstar_pcm_of_pcm p))] + +val refine_fstar_pcm_of_pcm + (#a: Type) + (p: pcm0 a) + (x: a) +: Lemma + ((fstar_pcm_of_pcm p).P.refine x <==> p_refine p x) + [SMTPat ((fstar_pcm_of_pcm p).P.refine x)] + +let exclusive (#a:Type u#a) (p:pcm0 a) (x:a) = + forall (frame:a). composable p x frame ==> frame == one p + +let compatible (#a: Type u#a) (pcm:pcm0 a) (x y:a) = + (exists (frame:a). + composable pcm x frame /\ op pcm x frame == y + ) + +val is_unit (#a: Type u#a) (p:pcm0 a) + (x:a) +: Lemma (composable p x (one p) /\ + op p x (one p) == x) + +let is_unit_pat (#a: Type u#a) (p:pcm0 a) + (x:a) +: Lemma (composable p x (one p) /\ + op p x (one p) == x) + [SMTPat (composable p x (one p))] += is_unit p x + +val compatible_intro + (#a: Type u#a) + (pcm: pcm0 a) + (x y: a) + (frame: a) +: Lemma + (requires (composable pcm x frame /\ op pcm x frame == y)) + (ensures (compatible pcm x y)) + +val compatible_elim + (#a: Type u#a) + (pcm: pcm0 a) + (x y: a) +: Ghost a + (requires (compatible pcm x y)) + (ensures (fun frame -> + composable pcm x frame /\ + op pcm x frame == y + )) + +val compatible_refl + (#a: Type u#a) + (pcm: pcm0 a) + (x: a) +: Lemma + (compatible pcm x x) + +val compatible_fstar_pcm_of_pcm + (#a: Type u#a) + (p: pcm0 a) + (x y: a) +: Lemma + (P.compatible (fstar_pcm_of_pcm p) x y <==> compatible p x y) + [SMTPat (P.compatible (fstar_pcm_of_pcm p) x y)] + +val compatible_pcm_of_fstar_pcm + (#a: Type u#a) + (p: P.pcm a) + (x y: a) +: Lemma + (compatible (pcm_of_fstar_pcm p) x y <==> P.compatible p x y) + [SMTPat (compatible (pcm_of_fstar_pcm p) x y)] + +val exclusive_fstar_pcm_of_pcm + (#a: Type u#a) + (p: pcm0 a) + (x: a) +: Lemma + (P.exclusive (fstar_pcm_of_pcm p) x <==> exclusive p x) + [SMTPat (P.exclusive (fstar_pcm_of_pcm p) x)] + +val exclusive_pcm_of_fstar_pcm + (#a: Type u#a) + (p: P.pcm a) + (x: a) +: Lemma + (exclusive (pcm_of_fstar_pcm p) x <==> P.exclusive p x) + [SMTPat (exclusive (pcm_of_fstar_pcm p) x)] + +let frame_preserving_upd_dom + (#a:Type u#a) (p:pcm0 a) (x:a) += + v:a{ + p_refine p v /\ + compatible p x v + } + +let frame_preserving_upd_post + (#a:Type u#a) (p:pcm0 a) (x y:a) + (v: frame_preserving_upd_dom p x) + (v_new: a) +: Tot prop += + p_refine p v_new /\ + compatible p y v_new /\ + (forall (frame:a{composable p x frame}).{:pattern composable p x frame} + composable p y frame /\ + (op p x frame == v ==> op p y frame == v_new)) + +let frame_preserving_upd_codom + (#a:Type u#a) (p:pcm0 a) (x y:a) + (v: frame_preserving_upd_dom p x) += + v_new:a{frame_preserving_upd_post p x y v v_new} + +type frame_preserving_upd (#a:Type u#a) (p:pcm0 a) (x y:a) = + v: frame_preserving_upd_dom p x -> + Tot (frame_preserving_upd_codom p x y v) + +unfold +let frame_preserving_upd_goal1 + (#a:Type u#a) (p:pcm0 a) (x y: Ghost.erased a) + (f: + (v: frame_preserving_upd_dom p x) -> + Tot a + ) + (v: frame_preserving_upd_dom p x) +: Tot prop += + let v_new = f v in + p_refine p v_new /\ + compatible p y v_new + +unfold +let frame_preserving_upd_goal2_pre + (#a:Type u#a) (p:pcm0 a) (x y: Ghost.erased a) + (f: + (v: frame_preserving_upd_dom p x) -> + Tot a + ) + (v: frame_preserving_upd_dom p x) + (frame: a) +: Tot prop += frame_preserving_upd_goal1 p x y f v /\ + composable p x frame + +unfold +let frame_preserving_upd_goal2_post + (#a:Type u#a) (p:pcm0 a) (x y: Ghost.erased a) + (f: + (v: frame_preserving_upd_dom p x) -> + Tot a + ) + (v: frame_preserving_upd_dom p x) + (frame: a) +: Tot prop += + frame_preserving_upd_goal2_pre p x y f v frame /\ + composable p y frame + +unfold +let frame_preserving_upd_goal3_pre + (#a:Type u#a) (p:pcm0 a) (x y: Ghost.erased a) + (f: + (v: frame_preserving_upd_dom p x) -> + Tot a + ) + (v: frame_preserving_upd_dom p x) + (frame: a) +: Tot prop += frame_preserving_upd_goal2_pre p x y f v frame /\ + frame_preserving_upd_goal2_post p x y f v frame /\ + op p x frame == v + +unfold +let frame_preserving_upd_goal3_post + (#a:Type u#a) (p:pcm0 a) (x y: Ghost.erased a) + (f: + (v: frame_preserving_upd_dom p x) -> + Tot a + ) + (v: frame_preserving_upd_dom p x) + (frame: a) +: Tot prop += frame_preserving_upd_goal3_pre p x y f v frame /\ + op p y frame == f v + +val frame_preserving_upd_post_intro + (#a:Type u#a) (p:pcm0 a) (x y: Ghost.erased a) + (f: + (v: frame_preserving_upd_dom p x) -> + Tot a + ) + (v: frame_preserving_upd_dom p x) + (prf1: + unit -> + Lemma + (frame_preserving_upd_goal1 p x y f v) + ) + (prf2: + (frame: a) -> + Lemma + (requires (frame_preserving_upd_goal2_pre p x y f v frame)) + (ensures (frame_preserving_upd_goal2_post p x y f v frame)) + ) + (prf3: + (frame: a) -> + Lemma + (requires (frame_preserving_upd_goal3_pre p x y f v frame)) + (ensures (frame_preserving_upd_goal3_post p x y f v frame)) + ) +: Lemma + (frame_preserving_upd_post p x y v (f v)) + +val frame_preserving_upd_post_intro' + (#a:Type u#a) (p:pcm0 a) (x y: Ghost.erased a) + (f: + (v: frame_preserving_upd_dom p x) -> + Tot a + ) + (v: frame_preserving_upd_dom p x) + (prf1: + unit -> + Lemma + (p_refine p (f v)) + ) + (prf2: + (frame: a) -> + Lemma + (requires (composable p x frame)) + (ensures (composable p y frame)) + ) + (prf3: + (frame: a) -> + Lemma + (requires (composable p x frame /\ composable p y frame /\ op p x frame == v)) + (ensures (op p y frame == f v)) + ) +: Lemma + (frame_preserving_upd_post p x y v (f v)) + +val frame_preserving_upd_intro + (#a:Type u#a) (p:pcm0 a) (x y: Ghost.erased a) + (f: + (v: frame_preserving_upd_dom p x) -> + Tot a + ) + (prf1: + (v: frame_preserving_upd_dom p x) -> + Lemma + (frame_preserving_upd_goal1 p x y f v) + ) + (prf2: + (v: frame_preserving_upd_dom p x) -> + (frame: a) -> + Lemma + (requires (frame_preserving_upd_goal2_pre p x y f v frame)) + (ensures (frame_preserving_upd_goal2_post p x y f v frame)) + ) + (prf3: + (v: frame_preserving_upd_dom p x) -> + (frame: a) -> + Lemma + (requires (frame_preserving_upd_goal3_pre p x y f v frame)) + (ensures (frame_preserving_upd_goal3_post p x y f v frame)) + ) +: Pure (frame_preserving_upd p x y) + (requires True) + (ensures (fun f' -> f `feq` f')) + +val fstar_fpu_of_fpu + (#a: Type u#a) + (p: pcm0 a) + (x y: Ghost.erased a) + (f: frame_preserving_upd p x y) +: Tot (FStar.PCM.frame_preserving_upd (fstar_pcm_of_pcm p) x y) + +let pcm (a: Type) : Tot Type = + (p: pcm0 a { + (forall (x:a) (y:a{composable p x y}).{:pattern (composable p x y)} + op p x y == one p ==> x == one p /\ y == one p) /\ // necessary to lift frame-preserving updates to unions + (forall (x:a) . {:pattern (p_refine p x)} p_refine p x ==> exclusive p x) /\ // nice to have, but not used yet + (~ (p_refine p (one p))) // necessary to maintain (refine ==> exclusive) for uninit, also necessary to prove full_not_unknown + }) diff --git a/lib/steel/c/Steel.C.Model.Ref.Base.fst b/lib/steel/c/Steel.C.Model.Ref.Base.fst new file mode 100644 index 000000000..d77d6fd71 --- /dev/null +++ b/lib/steel/c/Steel.C.Model.Ref.Base.fst @@ -0,0 +1,83 @@ +module Steel.C.Model.Ref.Base +module P = FStar.PCM +module U = Steel.C.Model.Universe +open FStar.FunctionalExtensionality + +#push-options "--print_universes" + +noeq type ref0 (a: Type u#0) (b: Type u#b) : Type u#b = { + p: pcm a; + q: pcm b; + pl: connection p q; + r: Steel.Memory.ref (U.raise_t u#0 u#1 a) (fstar_pcm_of_pcm (U.raise_pcm p)); +} + +noeq type ptr' (a: Type u#0) (b: Type u#b) : Type u#b = + | NonNull: (v: ref0 a b) -> ptr' a b + | Null: (v: pcm b) -> ptr' a b + +let pcm_of_ptr' + (#a: Type u#0) + (#b: Type u#b) + (r: ptr' a b) +: Tot (pcm b) += if Null? r then Null?.v r else (NonNull?.v r).q + +let ptr a #b p = (r: ptr' a b { pcm_of_ptr' r == p }) + +let null a p = Null p + +let ptr_is_null p = Null? p + +let mpts_to (#a: Type u#1) (#p: P.pcm a) (r: Steel.Memory.ref a p) ([@@@smt_fallback] v: a) = Steel.PCMReference.pts_to r v + +let raise_p + (#a: Type u#0) + (#b: Type u#b) + (r: ptr' a b { NonNull? r}) +: Tot (pcm (U.raise_t u#0 u#1 a)) += U.raise_pcm (NonNull?.v r).p + +let lower_conn + (#a: Type u#0) + (#b: Type u#b) + (r: ptr' a b { NonNull? r}) +: Tot (connection (raise_p r) (NonNull?.v r).p) += connection_of_isomorphism (isomorphism_inverse (U.raise_pcm_isomorphism u#0 u#1 (NonNull?.v r).p)) + +let raise_pl + (#a: Type u#0) + (#b: Type u#b) + (r: ptr' a b {NonNull? r}) +: Tot (connection (raise_p r) (NonNull?.v r).q) += lower_conn r `connection_compose` (NonNull?.v r).pl + +let t_ref_focus + (#a:Type) (#b:Type) (#c:Type) (#p: pcm b) + (r: ref a p) (#q: pcm c) (l: connection p q) +: Tot (ref a q) += let NonNull r = r in + NonNull ({p = r.p; pl = connection_compose r.pl l; r = r.r; q = q}) + +let ref_focus r l = t_ref_focus r l + +let ref_focus_id r = connection_compose_id_right (NonNull?.v r).pl + +let ref_focus_comp r l m += connection_compose_assoc (NonNull?.v r).pl l m + +(* freeable r if and only if r is a "base" reference, i.e. its connection path is empty *) + +let freeable #a #b #p r = + let NonNull r = r in + a == b /\ + r.p == p /\ + r.pl == connection_id p + +let pts_to r v = + (NonNull?.v r).r `mpts_to` (raise_pl r).conn_small_to_large.morph v + +let base_fpu p x y = + fun _ -> + compatible_refl p y; + y diff --git a/lib/steel/c/Steel.C.Model.Ref.Base.fsti b/lib/steel/c/Steel.C.Model.Ref.Base.fsti new file mode 100644 index 000000000..e1e2c304c --- /dev/null +++ b/lib/steel/c/Steel.C.Model.Ref.Base.fsti @@ -0,0 +1,67 @@ +module Steel.C.Model.Ref.Base +open FStar.FunctionalExtensionality +open Steel.C.Model.PCM +open Steel.C.Model.Connection +open Steel.Effect.Common + +(** A [ptr a b] is a (maybe null) pointer to some value of type b inside of a "base object" of type a. *) +val ptr (a: Type u#0) (#b: Type u#b) (p: pcm b) : Tot (Type u#b) + +val null (a: Type u#0) (#b: Type u#b) (p: pcm b) : Tot (ptr a p) + +val ptr_is_null (#a: Type u#0) (#b: Type u#b) (#p: pcm b) (r: ptr a p) : Ghost bool (requires True) (ensures (fun res -> res == true <==> r == null a p)) + +let refine (a: Type) (p: (a -> prop)) : Tot Type = + (x: a { p x }) + +let not_null + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) (r: ptr a p) +: Tot prop += ptr_is_null r == false + +(** A [ref a #b q] is a [ref' a b] where the PCM inside the ref' is forced to be q *) +let ref (a: Type u#0) (#b: Type u#b) (q: pcm b) : Type u#b = + refine (ptr a q) (not_null #a #b #q) + +(** Given a reference to an element of PCM p and a connection l from p to q, + [ref_focus r l] is a reference to an element of q. The intuition is that + q represents a "part of" p (e.g. a struct field, union case, or array slice). + This is a model, so we do not need to worry about ghost vs. pure + *) +val ref_focus + (#a:Type) (#b:Type) (#c:Type) (#p: pcm b) + (r: ref a p) (#q: pcm c) (l: connection p q) +: Tot (ref a q) + +val ref_focus_id + (#a:Type) (#b:Type) (#p: pcm b) + (r: ref a p) +: Lemma + (ref_focus r (connection_id _) == r) + +val ref_focus_comp (#p: pcm 'a) (#q: pcm 'b) (#s: pcm 'c) (r: ref 'd p) + (l: connection p q) (m: connection q s) +: Lemma (ref_focus (ref_focus r l) m == ref_focus r (l `connection_compose` m)) + [SMTPatOr [ + [SMTPat (ref_focus (ref_focus r l) m)]; + [SMTPat (ref_focus r (l `connection_compose` m))]]] + +val freeable + (#a: Type0) (#b:Type0) (#p: pcm b) (r: ref a p) +: Tot prop + +(** r points to PCM carrier value v *) +val pts_to + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) ([@@@smt_fallback] v: b) +: vprop + +(** Construct a write from a frame-preserving update. *) +val base_fpu + (#a: Type) + (p: pcm a) + (x: Ghost.erased a) + (y: a) +: Pure (frame_preserving_upd p x y) + (requires (exclusive p x /\ p_refine p y)) + (ensures (fun _ -> True)) diff --git a/lib/steel/c/Steel.C.Model.Ref.fst b/lib/steel/c/Steel.C.Model.Ref.fst new file mode 100644 index 000000000..275f2228e --- /dev/null +++ b/lib/steel/c/Steel.C.Model.Ref.fst @@ -0,0 +1,444 @@ +module Steel.C.Model.Ref +module P = FStar.PCM +module U = Steel.C.Model.Universe +open FStar.FunctionalExtensionality + +friend Steel.C.Model.Ref.Base + +let pts_to_view_explicit + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (#c: Type u#c) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) + (v: Ghost.erased c) +: Tot M.slprop += hp_of (pts_to r (vw.to_carrier v)) + +let pts_to_view_explicit_witinv + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (#c: Type u#c) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: Lemma + (M.is_witness_invariant (pts_to_view_explicit r vw)) += let aux (x y : Ghost.erased c) (m:M.mem) + : Lemma + (requires (M.interp (pts_to_view_explicit r vw x) m /\ M.interp (pts_to_view_explicit r vw y) m)) + (ensures (x == y)) + = + let c = raise_pl r in + let x_ = vw.to_carrier x in + let y_ = vw.to_carrier y in + let x' = c.conn_small_to_large.morph x_ in + let y' = c.conn_small_to_large.morph y_ in + M.pts_to_join (NonNull?.v r).r x' y' m; + let z' = FStar.IndefiniteDescription.indefinite_description_ghost _ (fun z' -> compatible (raise_p r) x' z' /\ compatible (raise_p r) y' z') in + let frame_x' = FStar.IndefiniteDescription.indefinite_description_ghost _ (fun frame_x' -> composable (raise_p r) x' frame_x' /\ op (raise_p r) frame_x' x' == z') in + let frame_y' = FStar.IndefiniteDescription.indefinite_description_ghost _ (fun frame_y' -> composable (raise_p r) y' frame_y' /\ op (raise_p r) frame_y' y' == z') in + let frame_x_ = c.conn_large_to_small.morph frame_x' in + let frame_y_ = c.conn_large_to_small.morph frame_y' in + op_comm (raise_p r) x' frame_x'; + c.conn_large_to_small.morph_compose x' frame_x'; + vw.to_view_frame x (c.conn_large_to_small.morph frame_x'); + op_comm (raise_p r) y' frame_y'; + c.conn_large_to_small.morph_compose y' frame_y'; + vw.to_view_frame y (c.conn_large_to_small.morph frame_y'); + () + in + Classical.forall_intro_3 (fun x y -> Classical.move_requires (aux x y)) + +let pts_to_view_sl + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (#c: Type u#c) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: Tot M.slprop += M.h_exists (pts_to_view_explicit r vw) + + +let pts_to_view_sel' + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: Tot (selector' c (pts_to_view_sl r vw)) += fun h -> + let x = M.id_elim_exists #(Ghost.erased c) (pts_to_view_explicit r vw) h in + Ghost.reveal (Ghost.reveal x) + +let pts_to_view_depends_only_on + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) + (m0:M.hmem (pts_to_view_sl r vw)) (m1:M.mem{M.disjoint m0 m1}) +: Lemma (pts_to_view_sel' r vw m0 == pts_to_view_sel' r vw (M.join m0 m1)) += let x = Ghost.reveal (M.id_elim_exists #(Ghost.erased c) (pts_to_view_explicit r vw) m0) in + let y = Ghost.reveal (M.id_elim_exists #(Ghost.erased c) (pts_to_view_explicit r vw) (M.join m0 m1)) in + pts_to_view_explicit_witinv r vw; + M.elim_wi (pts_to_view_explicit r vw) x y (M.join m0 m1) + +let pts_to_view_depends_only_on_core + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) + (m0:M.hmem (pts_to_view_sl r vw)) +: Lemma (pts_to_view_sel' r vw m0 == pts_to_view_sel' r vw (M.core_mem m0)) += let x = Ghost.reveal (M.id_elim_exists #(Ghost.erased c) (pts_to_view_explicit r vw) m0) in + let y = Ghost.reveal (M.id_elim_exists #(Ghost.erased c) (pts_to_view_explicit r vw) (M.core_mem m0)) in + pts_to_view_explicit_witinv r vw; + M.elim_wi (pts_to_view_explicit r vw) x y (M.core_mem m0) + +let pts_to_view_sel + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: Tot (selector c (pts_to_view_sl r vw)) += Classical.forall_intro_2 (pts_to_view_depends_only_on r vw); + Classical.forall_intro (pts_to_view_depends_only_on_core r vw); + pts_to_view_sel' r vw + +let pts_to_view_intro_lemma + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (x: Ghost.erased b) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) + (y: Ghost.erased c) // necessary because to_view may erase information from x + (m: M.mem) +: Lemma + (requires (M.interp (hp_of (pts_to r x)) m) /\ vw.to_carrier y == Ghost.reveal x) + (ensures ( + M.interp (pts_to_view_sl r vw) m /\ + pts_to_view_sel r vw m == Ghost.reveal y + )) += + M.intro_h_exists y (pts_to_view_explicit r vw) m; + pts_to_view_explicit_witinv r vw + +let pts_to_view_intro + (#invs: _) + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (x: Ghost.erased b) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) + (y: Ghost.erased c) // necessary because to_view may erase information from x +: A.SteelGhost unit invs + (pts_to r x) + (fun _ -> pts_to_view r vw) + (fun _ -> vw.to_carrier y == Ghost.reveal x) + (fun _ _ h' -> + h' (pts_to_view r vw) == Ghost.reveal y + ) += A.change_slprop_2 + (pts_to r x) + (pts_to_view r vw) + y + (fun m -> + pts_to_view_intro_lemma r x vw y m + ) + + +let pts_to_view_elim_lemma + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) + (m: M.mem) +: Lemma + (requires (M.interp (pts_to_view_sl r vw) m)) + (ensures ( + M.interp (hp_of (pts_to r (vw.to_carrier (pts_to_view_sel r vw m)))) m + )) += + M.elim_h_exists (pts_to_view_explicit r vw) m; + pts_to_view_explicit_witinv r vw + +/// Introducing a dependent star for [v] and [q] +let intro_vdep2 (#opened:_) + (v: vprop) + (q: vprop) + (p: (t_of v -> Tot vprop)) + (x: t_of v) +: A.SteelGhost unit opened + (v `star` q) + (fun _ -> vdep v p) + (requires (fun h -> h v == x /\ q == p x)) + (ensures (fun h _ h' -> + let x2 = h' (vdep v p) in + q == p (h v) /\ + dfst x2 == (h v) /\ + dsnd x2 == (h q) + )) += + A.intro_vdep v q p + +let pts_to_view_elim + (#invs: _) + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: A.SteelGhost (Ghost.erased b) invs + (pts_to_view r vw) + (fun res -> pts_to r res) + (fun _ -> True) + (fun h res _ -> + Ghost.reveal res == vw.to_carrier (h (pts_to_view r vw)) /\ + vw.to_view_prop res /\ + True //~ (Ghost.reveal res == one p) + ) += + let g : Ghost.erased c = A.gget (pts_to_view r vw) in + let res : Ghost.erased b = Ghost.hide (vw.to_carrier g) in + // vw.to_carrier_not_one g; + A.intro_pure (vw.to_carrier (Ghost.reveal g) == Ghost.reveal res); + let f (x: t_of (pts_to_view r vw)) : Tot vprop = pure (vw.to_carrier x == Ghost.reveal res) in + intro_vdep2 + (pts_to_view r vw) + (pure (vw.to_carrier (Ghost.reveal g) == Ghost.reveal res)) + f + (Ghost.reveal g); + A.rewrite_slprop + (vdep (pts_to_view r vw) f) + (pts_to r res) + (fun m -> + interp_vdep_hp (pts_to_view r vw) f m; + M.interp_star (hp_of (pts_to_view r vw)) (hp_of (f (sel_of (pts_to_view r vw) m))) m; + M.pure_interp (vw.to_carrier (sel_of (pts_to_view r vw) m) == Ghost.reveal res) m; + pts_to_view_elim_lemma r vw m + ); + res + + +let compatible_elim' + (#a: Type u#a) + (pcm: pcm0 a) + (x y: a) + (sq: squash (compatible pcm x y)) +: GTot (frame: a { + composable pcm x frame /\ + op pcm frame x == y + }) += compatible_elim pcm x y + +module STC = Steel.ST.Coercions + +let ref_read_sel + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: Steel c + (pts_to_view r vw) + (fun _ -> pts_to_view r vw) + (requires (fun _ -> True)) + (ensures (fun h res h' -> + res == h (pts_to_view r vw) /\ + res == h' (pts_to_view r vw) + )) += + let _v = pts_to_view_elim r vw in + let v = ref_read r in + let sq : squash (compatible p _v v) = () in + let frame = Ghost.hide (compatible_elim' p _v v sq) in + vw.to_view_frame (vw.to_view _v) frame ; + let res = vw.to_view v in + pts_to_view_intro r _v vw res; + A.return res + + +// [@@__steel_reduce__; __reduce__] +let pts_to_view_or_null0 + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ptr a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: Tot vprop += if ptr_is_null r + then (emp `vrewrite` (fun _ -> None <: option c)) + else (pts_to_view r vw `vrewrite` (fun x -> Some x)) + +let pts_to_view_or_null_sl + r vw += + hp_of (pts_to_view_or_null0 r vw) + +let pts_to_view_or_null_sel + r vw += + sel_of (pts_to_view_or_null0 r vw) + +let pts_to_view_or_null_prop_null + (#inames: _) + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ptr a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: A.SteelGhost unit inames + (pts_to_view_or_null r vw) + (fun _ -> pts_to_view_or_null r vw) + (requires fun _ -> Null? r) + (ensures fun h _ h' -> + let s = h (pts_to_view_or_null r vw) in + h' (pts_to_view_or_null r vw) == s /\ + None? s == Null? r + ) += + A.change_slprop_rel + (pts_to_view_or_null r vw) + (pts_to_view_or_null0 r vw) + (fun x y -> x == y) + (fun _ -> ()); + A.change_equal_slprop + (pts_to_view_or_null0 r vw) + (emp `vrewrite` (fun _ -> None <: option c)); + A.elim_vrewrite emp (fun _ -> None <: option c); + A.intro_vrewrite emp (fun _ -> None <: option c); + A.change_equal_slprop + (emp `vrewrite` (fun _ -> None <: option c)) + (pts_to_view_or_null0 r vw); + A.change_slprop_rel + (pts_to_view_or_null0 r vw) + (pts_to_view_or_null r vw) + (fun x y -> x == y) + (fun _ -> ()) + +#push-options "--z3rlimit 16" +#restart-solver +let pts_to_view_or_null_prop_not_null + (#inames: _) + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ptr a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: A.SteelGhost unit inames + (pts_to_view_or_null r vw) + (fun _ -> pts_to_view_or_null r vw) + (requires fun _ -> NonNull? r) + (ensures fun h _ h' -> + let s = h (pts_to_view_or_null r vw) in + h' (pts_to_view_or_null r vw) == s /\ + None? s == Null? r + ) += + A.change_slprop_rel + (pts_to_view_or_null r vw) + (pts_to_view_or_null0 r vw) + (fun x y -> x == y) + (fun _ -> ()); + A.change_equal_slprop + (pts_to_view_or_null0 r vw) + (pts_to_view r vw `vrewrite` (fun x -> Some x)); + A.elim_vrewrite (pts_to_view r vw) (fun x -> Some x); + A.intro_vrewrite (pts_to_view r vw) (fun x -> Some x); + A.change_equal_slprop + (pts_to_view r vw `vrewrite` (fun x -> Some x)) + (pts_to_view_or_null0 r vw); + A.change_slprop_rel + (pts_to_view_or_null0 r vw) + (pts_to_view_or_null r vw) + (fun x y -> x == y) + (fun _ -> ()) +#pop-options + +let pts_to_view_or_null_prop + (#inames: _) + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ptr a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: A.SteelGhost unit inames + (pts_to_view_or_null r vw) + (fun _ -> pts_to_view_or_null r vw) + (requires fun _ -> True) + (ensures fun h _ h' -> + let s = h (pts_to_view_or_null r vw) in + h' (pts_to_view_or_null r vw) == s /\ + None? s == Null? r + ) += + if Null? r + then pts_to_view_or_null_prop_null r vw + else pts_to_view_or_null_prop_not_null r vw + +let is_null + r vw += + pts_to_view_or_null_prop r vw; + A.return (Null? r) + +let intro_pts_to_view_or_null_null + a #b #p #c vw += + A.intro_vrewrite emp (fun _ -> None <: option c); + A.change_equal_slprop + (emp `vrewrite` (fun _ -> None <: option c)) + (pts_to_view_or_null0 (null a p) vw); + A.change_slprop_rel + (pts_to_view_or_null0 (null a p) vw) + (pts_to_view_or_null (null a p) vw) + (fun x y -> x == y) + (fun _ -> ()) + +let elim_pts_to_view_or_null_null + a #b #p #c vw += + A.change_slprop_rel + (pts_to_view_or_null (null a p) vw) + (pts_to_view_or_null0 (null a p) vw) + (fun x y -> x == y) + (fun _ -> ()); + A.change_equal_slprop + (pts_to_view_or_null0 (null a p) vw) + (emp `vrewrite` (fun _ -> None <: option c)); + A.elim_vrewrite emp (fun _ -> None <: option c) + +let intro_pts_to_view_or_null_not_null + r vw += + A.intro_vrewrite + (pts_to_view r vw) + (fun x -> Some x); + A.change_equal_slprop + (pts_to_view r vw `vrewrite` (fun x -> Some x)) + (pts_to_view_or_null0 r vw); + A.change_slprop_rel + (pts_to_view_or_null0 r vw) + (pts_to_view_or_null r vw) + (fun x y -> x == y) + (fun _ -> ()) + +let elim_pts_to_view_or_null_not_null + r vw += + A.change_slprop_rel + (pts_to_view_or_null r vw) + (pts_to_view_or_null0 r vw) + (fun x y -> x == y) + (fun _ -> ()); + A.change_equal_slprop + (pts_to_view_or_null0 r vw) + (pts_to_view r vw `vrewrite` (fun x -> Some x)); + A.elim_vrewrite + (pts_to_view r vw) + (fun x -> Some x) diff --git a/lib/steel/c/Steel.C.Model.Ref.fsti b/lib/steel/c/Steel.C.Model.Ref.fsti new file mode 100644 index 000000000..762c0f64a --- /dev/null +++ b/lib/steel/c/Steel.C.Model.Ref.fsti @@ -0,0 +1,286 @@ +module Steel.C.Model.Ref +open FStar.FunctionalExtensionality +open Steel.C.Model.PCM +open Steel.C.Model.Connection +include Steel.ST.C.Model.Ref + +#push-options "--print_universes" + +open Steel.Effect + +module A = Steel.Effect.Atomic + +module M = Steel.Memory + +(** PCM carrier values are cumbersome to work with directly. To + abstract over them, we define "view"s, which are essentially + lossless partial functions from PCM carrier values to "view + types". *) +noeq +type sel_view + (#carrier: Type u#a) + (p: pcm carrier) + (view: Type u#b) + (can_view_unit:bool) += { + (** When is a PCM carrier value viewable? *) + to_view_prop: (carrier -> Tot prop); + to_view: (refine carrier to_view_prop -> Tot view); + (** Construct a PCM carrier value from a view (used for writes) *) + to_carrier: (view -> Tot (refine carrier to_view_prop)); + (** If can_view_unit is false, then the unit of the PCM must be unviewable. + If can_view_unit is true, all bets are off. + This was originally used to allow viewing empty structs (which + would have can_view_unit := true). Empty structs aren't useful + in C programming, but they can temporarily arise in our model + after one has taken pointers to every field of a nonempty + struct. + We eventually found a different way of coping with this + situation (see Steel.C.StructLiteral for details), so we in fact use (can_view_unit := false) everywhere + and we could get rid of can_view_unit entirely. *) + to_carrier_not_one: + squash (~ can_view_unit ==> ~ (exists x. to_carrier x == one p) /\ ~ (to_view_prop (one p))); + (** The PCM carrier value corresponding to a view must be stable under composition with surrounding frames. *) + to_view_frame: + (x: view) -> + (frame: carrier) -> + Lemma + (requires (composable p (to_carrier x) frame)) + (ensures (to_view_prop (op p (to_carrier x) frame) /\ to_view (op p (to_carrier x) frame) == x)); +} + +(** Every sel_view gives rise to a selector, which we can use to hide even the view-type values. *) + +let weaken_view (#p: pcm 'a) (v: sel_view p 'b false): sel_view p 'b true = { + to_view_prop = v.to_view_prop; + to_view = v.to_view; + to_carrier = v.to_carrier; + to_carrier_not_one = (); + to_view_frame = v.to_view_frame; +} + +let g_is_inverse_of (#a #b: Type) (g: (b -> GTot a)) (f: (a -> GTot b)) : Tot prop = + (forall x . {:pattern (g (f x))} g (f x) == x) + +let sel_view_inv + (#carrier: Type u#a) + (#p: pcm carrier) + (#view: Type u#b) + (#can_view_unit: bool) + (vw: sel_view p view can_view_unit) +: Lemma + (vw.to_view `g_is_inverse_of` vw.to_carrier) + [SMTPat (has_type vw (sel_view p view can_view_unit))] += let aux + (x: view) + : Lemma + (vw.to_view (vw.to_carrier x) == x) + [SMTPat (vw.to_view (vw.to_carrier x))] + = is_unit p (vw.to_carrier x); + vw.to_view_frame x (one p) + in + () + +val pts_to_view_sl + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (#c: Type u#c) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: Tot (M.slprop u#1) + +val pts_to_view_sel + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: Tot (selector c (pts_to_view_sl r vw)) + +[@@__steel_reduce__] +let pts_to_view' + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: Tot vprop' += { + hp = pts_to_view_sl r vw; + t = c; + sel = pts_to_view_sel r vw; +} + +[@@__steel_reduce__] +let pts_to_view + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) + //(#a: Type u#0) (#[@@@smt_fallback] b: Type u#b) (#[@@@smt_fallback] p: pcm b) + //(r: ref a p) + //(#[@@@smt_fallback] c: Type0) + //(#can_view_unit: bool) + //([@@@smt_fallback] vw: sel_view p c can_view_unit) +: Tot vprop += VUnit (pts_to_view' r vw) + +val pts_to_view_intro + (#invs: _) + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (x: Ghost.erased b) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) + (y: Ghost.erased c) // necessary because to_view may erase information from x +: A.SteelGhost unit invs + (pts_to r x) + (fun _ -> pts_to_view r vw) + (fun _ -> vw.to_carrier y == Ghost.reveal x) + (fun _ _ h' -> + h' (pts_to_view r vw) == Ghost.reveal y + ) + +val pts_to_view_elim + (#invs: _) + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: A.SteelGhost (Ghost.erased b) invs + (pts_to_view r vw) + (fun res -> pts_to r res) + (fun _ -> True) + (fun h res _ -> + Ghost.reveal res == vw.to_carrier (h (pts_to_view r vw)) /\ + vw.to_view_prop res /\ + True //~ (Ghost.reveal res == one p) + ) + +val ref_read_sel + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ref a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: Steel c + (pts_to_view r vw) + (fun _ -> pts_to_view r vw) + (requires (fun _ -> True)) + (ensures (fun h res h' -> + res == h (pts_to_view r vw) /\ + res == h' (pts_to_view r vw) + )) + +(* write cannot be defined generically because of p_refine *) + +/// Pointers (and the null pointer) + +val pts_to_view_or_null_sl + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ptr a p) + (#c: Type u#0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: Tot (M.slprop u#1) + +val pts_to_view_or_null_sel + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ptr a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: GTot (selector (option c) (pts_to_view_or_null_sl r vw)) + +[@@__steel_reduce__] +let pts_to_view_or_null' + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ptr a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: GTot vprop' += { + hp = pts_to_view_or_null_sl r vw; + t = option c; + sel = pts_to_view_or_null_sel r vw; +} + +[@@__steel_reduce__] +let pts_to_view_or_null + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: ptr a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: Tot vprop += VUnit (pts_to_view_or_null' r vw) + +val is_null + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (#opened: _) + (r: ptr a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: A.SteelAtomicBase bool false opened A.Unobservable + (pts_to_view_or_null r vw) + (fun _ -> pts_to_view_or_null r vw) + (requires (fun _ -> True)) + (ensures (fun h res h' -> + let s = h (pts_to_view_or_null r vw) in + h' (pts_to_view_or_null r vw) == s /\ + res == ptr_is_null r /\ + res == (None? s) + )) + +val intro_pts_to_view_or_null_null + (#inames: _) + (a: Type) (#b: Type) (#p: pcm b) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: A.SteelGhost unit inames + emp + (fun _ -> pts_to_view_or_null (null a p) vw) + (requires (fun _ -> True)) + (ensures (fun _ _ h' -> h' (pts_to_view_or_null (null a p) vw) == None)) + +val elim_pts_to_view_or_null_null + (#inames: _) + (a: Type) (#b: Type) (#p: pcm b) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: A.SteelGhostT unit inames + (pts_to_view_or_null (null a p) vw) + (fun _ -> emp) + +val intro_pts_to_view_or_null_not_null + (#inames: _) + (#a: Type) (#b: Type) (#p: pcm b) + (r: ref a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: A.SteelGhost unit inames + (pts_to_view r vw) + (fun _ -> pts_to_view_or_null r vw) + (requires (fun _ -> True)) + (ensures (fun h _ h' -> h' (pts_to_view_or_null r vw) == Some (h (pts_to_view r vw)))) + +val elim_pts_to_view_or_null_not_null + (#inames: _) + (#a: Type) (#b: Type) (#p: pcm b) + (r: ref a p) + (#c: Type0) + (#can_view_unit: bool) + (vw: sel_view p c can_view_unit) +: A.SteelGhost unit inames + (pts_to_view_or_null r vw) + (fun _ -> pts_to_view r vw) + (requires (fun _ -> True)) + (ensures (fun h _ h' -> h (pts_to_view_or_null r vw) == Some (h' (pts_to_view r vw)))) diff --git a/lib/steel/c/Steel.C.Model.Struct.fst b/lib/steel/c/Steel.C.Model.Struct.fst new file mode 100644 index 000000000..a33a74de0 --- /dev/null +++ b/lib/steel/c/Steel.C.Model.Struct.fst @@ -0,0 +1,178 @@ +module Steel.C.Model.Struct +include Steel.ST.C.Model.Struct + +module STC = Steel.ST.Coercions +module P = FStar.PCM +open Steel.C.Model.PCM +open Steel.C.Model.Connection +open Steel.C.Model.Ref +// module Ptr = Steel.C.Model.Ptr +open Steel.Effect +module A = Steel.Effect.Atomic + +open FStar.FunctionalExtensionality +open FStar.Classical + + +(* +let struct_view_to_view_prop + (#a:Type) (#b: a -> Type) (#p:(k:a -> pcm (b k))) + (fa:a -> prop) + (view_t:(refine a fa -> Type)) + (field_view:(k:refine a fa -> sel_view (p k) (view_t k))) +: restricted_t a b -> Tot prop += fun (f : restricted_t a b) -> + forall (k:a). + (fa k ==> (field_view k).to_view_prop (f k)) + +let struct_view_to_view + (#a:Type) (#b: a -> Type) (#p:(k:a -> pcm (b k))) + (#fa:a -> prop) + (view_t:(refine a fa -> Type)) + (field_view:(k:refine a fa -> sel_view (p k) (view_t k))) +: refine (restricted_t a b) (struct_view_to_view_prop fa view_t field_view) -> + Tot (restricted_t (refine a fa) view_t) += fun (f: refine (restricted_t a b) (struct_view_to_view_prop fa view_t field_view)) -> + let g = on_dom (refine a fa) (fun (k: refine a fa) -> (field_view k).to_view (f k)) in + g + +let struct_view_to_carrier + (#a:Type) (#b: a -> Type) (#p:(k:a -> pcm (b k))) + (#fa:a -> prop) + (dec_fa: decidable fa) + (view_t:(refine a fa -> Type)) + (field_view:(k:refine a fa -> sel_view (p k) (view_t k))) +: restricted_t (refine a fa) view_t -> + Tot (refine (restricted_t a b) (struct_view_to_view_prop fa view_t field_view)) += fun (f: restricted_t (refine a fa) view_t) -> + let g: restricted_t a b = on_dom a (fun k -> + if dec_fa k then + (field_view k).to_carrier (f k) <: b k + else one (p k)) + in g + +// let struct_view_to_carrier_not_one +// (#a:Type) (#b: a -> Type) (#p:(k:a -> pcm (b k))) +// (#fa:a -> prop) +// (dec_fa: decidable fa) +// (view_t:(refine a fa -> Type)) +// (field_view:(k:refine a fa -> sel_view (p k) (view_t k))) +// (x:restricted_t (refine a fa) view_t) +// : Lemma +// (requires exists (k:a). fa k) +// (ensures struct_view_to_carrier dec_fa view_t field_view x =!= one (prod_pcm p)) +// = let k = FStar.IndefiniteDescription.indefinite_description_ghost a fa in +// (field_view k).to_carrier_not_one (x k) + +let struct_view_to_view_frame + (#a:Type) (#b: a -> Type) (#p:(k:a -> pcm (b k))) + (#fa:a -> prop) + (dec_fa: decidable fa) + (view_t:(refine a fa -> Type)) + (field_view:(k:refine a fa -> sel_view (p k) (view_t k))) + (x:restricted_t (refine a fa) view_t) + (frame: restricted_t a b) +: Lemma + (requires (composable (prod_pcm p) (struct_view_to_carrier dec_fa view_t field_view x) frame)) + (ensures + struct_view_to_view_prop fa view_t field_view + (op (prod_pcm p) (struct_view_to_carrier dec_fa view_t field_view x) frame) /\ + struct_view_to_view view_t field_view + (op (prod_pcm p) (struct_view_to_carrier dec_fa view_t field_view x) frame) == x) += let aux (k:refine a fa) + : Lemma ( + (field_view k).to_view_prop (op (p k) ((field_view k).to_carrier (x k)) (frame k)) /\ + (field_view k).to_view (op (p k) ((field_view k).to_carrier (x k)) (frame k)) == x k) + = assert (composable (p k) ((field_view k).to_carrier (x k)) (frame k)); + (field_view k).to_view_frame (x k) (frame k) + in forall_intro aux; + assert ( + struct_view_to_view view_t field_view + (op (prod_pcm p) (struct_view_to_carrier dec_fa view_t field_view x) frame) `feq` x) +*) + +let mem (#a:eqtype) (xs:list a) x : prop = List.mem x xs == true + +let struct_view_to_view_prop + (#a:eqtype) (#b: a -> Type) (#p:(k:a -> pcm (b k))) + (#view_t:a -> Type) + (#can_view_units: bool) + (field_view:(k:a -> sel_view (p k) (view_t k) can_view_units)) + (included: list a) +: restricted_t a b -> prop += fun f -> forall (k:a). (mem included k ==> (field_view k).to_view_prop (f k)) + +let struct_view_to_view + (#a:eqtype) (#b: a -> Type) (#p:(k:a -> pcm (b k))) + (#view_t:a -> Type) + (#can_view_units: bool) + (field_view:(k:a -> sel_view (p k) (view_t k) can_view_units)) + (included: list a) +: refine (restricted_t a b) (struct_view_to_view_prop field_view included) -> + Tot (restricted_t (refine a (mem included)) view_t) += fun f -> on_dom (refine a (mem included)) (fun k -> (field_view k).to_view (f k)) + +let struct_view_to_carrier + (#a:eqtype) (#b: a -> Type) (#p:(k:a -> pcm (b k))) + (#view_t:a -> Type) + (#can_view_units: bool) + (field_view:(k:a -> sel_view (p k) (view_t k) can_view_units)) + (included: list a) +: restricted_t (refine a (mem included)) view_t -> + Tot (refine (restricted_t a b) (struct_view_to_view_prop field_view included)) += fun f -> + let g: restricted_t a b = on_dom a (fun k -> + if k `List.mem` included then + (field_view k).to_carrier (f k) <: b k + else one (p k)) + in g + +let struct_view_to_view_frame + (#a:eqtype) (#b: a -> Type) (#p:(k:a -> pcm (b k))) + (#view_t:a -> Type) + (#can_view_units: bool) + (field_view:(k:a -> sel_view (p k) (view_t k) can_view_units)) + (included: list a) + (x:restricted_t (refine a (mem included)) view_t) + (frame: restricted_t a b) +: Lemma + (requires (composable (prod_pcm p) (struct_view_to_carrier field_view included x) frame)) + (ensures + struct_view_to_view_prop field_view included + (op (prod_pcm p) (struct_view_to_carrier field_view included x) frame) /\ + struct_view_to_view field_view included + (op (prod_pcm p) (struct_view_to_carrier field_view included x) frame) == x) += let aux (k:refine a (mem included)) + : Lemma ( + (field_view k).to_view_prop (op (p k) ((field_view k).to_carrier (x k)) (frame k)) /\ + (field_view k).to_view (op (p k) ((field_view k).to_carrier (x k)) (frame k)) == x k) + = assert (composable (p k) ((field_view k).to_carrier (x k)) (frame k)); + (field_view k).to_view_frame (x k) (frame k) + in forall_intro aux; + assert ( + struct_view_to_view field_view included + (op (prod_pcm p) (struct_view_to_carrier field_view included x) frame) `feq` x) + +let struct_view + (#a:eqtype) (#b: a -> Type) (#p:(k:a -> pcm (b k))) + (#view_t:a -> Type) + (#can_view_units: bool) + (field_view:(k:a -> sel_view (p k) (view_t k) can_view_units)) + (included: list a) +: sel_view (prod_pcm p) + (restricted_t (refine a (mem included)) view_t) + (can_view_units || Nil? included) += { + to_view_prop = struct_view_to_view_prop field_view included; + to_view = struct_view_to_view field_view included; + to_carrier = struct_view_to_carrier field_view included; + to_carrier_not_one = begin + let aux () : Lemma + (requires ~ (can_view_units || Nil? included)) + (ensures + ~ (exists x. struct_view_to_carrier field_view included x == one (prod_pcm p)) /\ + ~ (struct_view_to_view_prop field_view included (one (prod_pcm p)))) + = let k :: _ = included in () + in move_requires aux () end; + to_view_frame = struct_view_to_view_frame field_view included; +} diff --git a/lib/steel/c/Steel.C.Model.Uninit.fsti b/lib/steel/c/Steel.C.Model.Uninit.fsti new file mode 100644 index 000000000..54e520ccb --- /dev/null +++ b/lib/steel/c/Steel.C.Model.Uninit.fsti @@ -0,0 +1,225 @@ +module Steel.C.Model.Uninit + +module P = FStar.PCM +open Steel.C.Model.PCM +open Steel.C.Model.Ref +open Steel.C.Model.Connection +open Steel.Effect + +/// Uninitialized + +noeq +type uninit_t (a: Type) += | Uninitialized + | InitOrUnit: a -> uninit_t a + +let uninit_composable + (#a: Type) + (p: pcm a) +: Tot (P.symrel (uninit_t a)) += fun u1 u2 -> + match u1, u2 with + | Uninitialized, InitOrUnit x + | InitOrUnit x, Uninitialized + -> x == one p + | InitOrUnit x1, InitOrUnit x2 + -> composable p x1 x2 + | _ -> False + +let uninit_compose + (#a: Type) + (p: pcm a) + (u1: uninit_t a) + (u2: uninit_t a { uninit_composable p u1 u2 }) +: Tot (uninit_t a) += match u1, u2 with + | Uninitialized, _ + | _, Uninitialized + -> Uninitialized + | InitOrUnit x1, InitOrUnit x2 + -> InitOrUnit (op p x1 x2) + +let uninit_refine + (#a: Type) + (p: pcm a) + (x: uninit_t a) +: Tot prop += match x with + | Uninitialized -> True + | InitOrUnit y -> p_refine p y + +let fstar_pcm_uninit #a (p: pcm a) : Tot (P.pcm (uninit_t a)) = let open P in { + p = { + composable = uninit_composable p; + op = uninit_compose p; + one = InitOrUnit (Steel.C.Model.PCM.one p); + }; + comm = (fun _ _ -> ()); + assoc = (fun x1 x2 x3 -> ()); + assoc_r = (fun _ _ _ -> ()); + is_unit = (fun _ -> ()); + refine = uninit_refine p; +} + +let pcm_uninit #a (p: pcm a) : Tot (pcm (uninit_t a)) = pcm_of_fstar_pcm (fstar_pcm_uninit p) + +let value_to_uninit + (#a: Type) + (p: pcm a) +: Tot (morphism p (pcm_uninit p)) += mkmorphism + (fun x -> InitOrUnit x) + () + (fun _ _ -> ()) + +let uninit_to_value + (#a: Type) + (p: pcm a) +: Tot (morphism (pcm_uninit p) p) += mkmorphism + (fun x -> match x with InitOrUnit y -> y | _ -> one p) + () + (fun _ _ -> Classical.forall_intro (is_unit p)) + +let uninit_conn_fpu' + (#a: Type) + (p: pcm a) + (x: Ghost.erased a { ~ (Ghost.reveal x == one p) }) + (y: Ghost.erased a) + (f: frame_preserving_upd p x y) + (v: uninit_t a { + p_refine (pcm_uninit p) v /\ + compatible (pcm_uninit p) ((value_to_uninit p).morph x) v + }) +: Tot (uninit_t a) += + let InitOrUnit x' = v in + InitOrUnit (f x') + +let uninit_conn_fpu_prop + (#a: Type) + (p: pcm a) + (x: Ghost.erased a { ~ (Ghost.reveal x == one p) }) + (y: Ghost.erased a) + (f: frame_preserving_upd p x y) + (v: uninit_t a { + p_refine (pcm_uninit p) v /\ + compatible (pcm_uninit p) ((value_to_uninit p).morph x) v + }) +: Lemma + (let v_new = uninit_conn_fpu' p x y f v in + p_refine (pcm_uninit p) v_new /\ + compatible (pcm_uninit p) ((value_to_uninit p).morph y) v_new /\ + (forall (frame:_{composable (pcm_uninit p) ((value_to_uninit p).morph x) frame}). + composable (pcm_uninit p) ((value_to_uninit p).morph y) frame /\ + (op (pcm_uninit p) ((value_to_uninit p).morph x) frame == v ==> op (pcm_uninit p) ((value_to_uninit p).morph y) frame == v_new)) + ) += Classical.forall_intro (is_unit p); + let y' = (value_to_uninit p).morph y in + let InitOrUnit x' = v in + let v_new = uninit_conn_fpu' p x y f v in + let frame : a = compatible_elim p y (f x') in + let frame' : uninit_t a = InitOrUnit frame in + assert (composable (pcm_uninit p) y' frame'); + assert (op (pcm_uninit p) frame' y' == v_new); + compatible_intro (pcm_uninit p) y' v_new frame'; + assert (forall (frame:_{composable (pcm_uninit p) ((value_to_uninit p).morph x) frame}). + composable (pcm_uninit p) ((value_to_uninit p).morph y) frame /\ + (op (pcm_uninit p) ((value_to_uninit p).morph x) frame == v ==> op (pcm_uninit p) ((value_to_uninit p).morph y) frame == v_new)); + () + +let uninit_conn_fpu + (#a: Type) + (p: pcm a) + (x: Ghost.erased a { ~ (Ghost.reveal x == one p) }) + (y: Ghost.erased a) + (f: frame_preserving_upd p x y) +: Tot (frame_preserving_upd (pcm_uninit p) ((value_to_uninit p).morph x) ((value_to_uninit p).morph y)) += + fun v -> + uninit_conn_fpu_prop p x y f v; + uninit_conn_fpu' p x y f v + +let uninit_conn + (#a: Type) + (p: pcm a) +: Tot (connection (pcm_uninit p) p) += mkconnection + (value_to_uninit p) + (uninit_to_value p) + () + (uninit_conn_fpu p) + +let exclusive_uninit + (#a: Type) + (p: pcm a) + (x: uninit_t a) +: Lemma + (exclusive (pcm_uninit p) x <==> begin match x with + | Uninitialized -> True + | InitOrUnit z -> exclusive p z /\ (~ (z == one p)) + end) += match x with + | Uninitialized -> () + | InitOrUnit z -> + if FStar.StrongExcludedMiddle.strong_excluded_middle (z == one p) + then begin + assert (composable (pcm_uninit p) x Uninitialized) + end else + let phi2 + frame + : Lemma + (requires (exclusive (pcm_uninit p) x /\ composable p z frame)) + (ensures (frame == one p)) + [SMTPat (composable p z frame)] + = assert (composable (pcm_uninit p) x (InitOrUnit frame)) + in + () + + +let uninit_view + (#a: Type) + (#p: pcm a) + (#b: Type) + (w: sel_view p b false) +: Tot (sel_view #(uninit_t a) (pcm_uninit p) (uninit_t b) false) += { + to_view_prop = (fun x -> match x with + | Uninitialized -> True + | InitOrUnit x' -> w.to_view_prop x' + ); + to_view = (fun x -> match x with + | Uninitialized -> Uninitialized + | InitOrUnit x' -> InitOrUnit (w.to_view x') + ); + to_carrier = (fun v -> match v with + | Uninitialized -> Uninitialized + | InitOrUnit v' -> InitOrUnit (w.to_carrier v') + ); + to_carrier_not_one = (); + to_view_frame = (fun v frame -> match v with + | Uninitialized -> () + | InitOrUnit v' -> let InitOrUnit frame' = frame in w.to_view_frame v' frame' + ); +} + +let uninit_view_initialized + (#a: Type) + (#p: pcm a) + (#b: Type) + (w: sel_view p b false) +: Tot (sel_view #(uninit_t a) (pcm_uninit p) b false) += { + to_view_prop = (fun x -> match x with + | Uninitialized -> False + | InitOrUnit x' -> w.to_view_prop x' + ); + to_view = (fun x -> match x with + | InitOrUnit x' -> w.to_view x' + ); + to_carrier = (fun v' -> InitOrUnit (w.to_carrier v')); + to_carrier_not_one = (); + to_view_frame = (fun v frame -> + let InitOrUnit frame' = frame in w.to_view_frame v frame' + ); +} diff --git a/lib/steel/c/Steel.C.Model.Union.fst b/lib/steel/c/Steel.C.Model.Union.fst new file mode 100644 index 000000000..6d0878075 --- /dev/null +++ b/lib/steel/c/Steel.C.Model.Union.fst @@ -0,0 +1,47 @@ +module Steel.C.Model.Union +include Steel.ST.C.Model.Union + +module STC = Steel.ST.Coercions +module P = FStar.PCM +open Steel.C.Model.PCM +open Steel.C.Model.Connection +open Steel.C.Model.Ref +open Steel.C.Model.Struct +open Steel.Effect +module A = Steel.Effect.Atomic + +open FStar.FunctionalExtensionality + +let union_view_to_view_prop + (#a:Type) (#b: a -> Type) (#p:(k:a -> pcm (b k))) + (#view_t:a -> Type) (case_view:(k:a -> sel_view (p k) (view_t k) false)) +: union p -> Tot prop += fun u -> + u =!= one (union_pcm p) /\ + (forall k. case_refinement_f p k u ==> (case_view k).to_view_prop (u k)) + +let union_view_to_view + (#a:Type) (#b: a -> Type) (#p:(k:a -> pcm (b k))) + (#view_t:a -> Type) (case_view:(k:a -> sel_view (p k) (view_t k) false)) + (case_of:(u:union p -> k:a{case_refinement_f p k u})) +: refine (union p) (union_view_to_view_prop case_view) -> dtuple2 a view_t += fun u -> let k = case_of u in (|k, (case_view k).to_view (u k)|) + +let union_view_to_carrier + (#a:eqtype) (#b: a -> Type) (#p:(k:a -> pcm (b k))) + (#view_t:a -> Type) (case_view:(k:a -> sel_view (p k) (view_t k) false)) +: dtuple2 a view_t -> refine (union p) (union_view_to_view_prop case_view) += fun (|k, x|) -> field_to_union_f p k ((case_view k).to_carrier x) + +let union_view + (#a:eqtype) (#b: a -> Type) (#p:(k:a -> pcm (b k))) + (#view_t:a -> Type) (case_view:(k:a -> sel_view (p k) (view_t k) false)) + (case_of:(u:union p -> k:a{case_refinement_f p k u})) +: Tot (sel_view (union_pcm p) (dtuple2 a view_t) false) += { + to_view_prop = union_view_to_view_prop case_view; + to_view = union_view_to_view case_view case_of; + to_carrier = union_view_to_carrier case_view; + to_carrier_not_one = (); + to_view_frame = (fun (|k, x|) u -> (case_view k).to_view_frame x (u k)); +} diff --git a/lib/steel/c/Steel.C.Model.Universe.fst b/lib/steel/c/Steel.C.Model.Universe.fst new file mode 100644 index 000000000..ca2ea5741 --- /dev/null +++ b/lib/steel/c/Steel.C.Model.Universe.fst @@ -0,0 +1,132 @@ +module Steel.C.Model.Universe +open Steel.C.Model.PCM +include FStar.Universe +module P = FStar.PCM + +(* Raising to a greater universe *) + +#push-options "--print_universes" + +let raise_pcm_composable + (#a: Type u#a) + (p: pcm0 a) + (x y: raise_t u#a u#b a) +: Tot prop += composable p (downgrade_val x) (downgrade_val y) + +let raise_pcm_op + (#a: Type u#a) + (p: pcm0 a) + (x: raise_t u#a u#b a) + (y: _ { raise_pcm_composable p x y }) +: Tot (raise_t u#a u#b a) += raise_val (op p (downgrade_val x) (downgrade_val y)) + +let raise_pcm' + (#a: Type u#a) + (p: pcm0 a) +: Tot (P.pcm' (raise_t u#a u#b a)) += + { + P.composable = raise_pcm_composable p; + P.op = raise_pcm_op p; + P.one = raise_val (one p); + } + +let fstar_raise_pcm0 + (#a: Type u#a) + (p: pcm0 a) +: Tot (P.pcm (raise_t u#a u#b a)) += { + P.p = raise_pcm' p; + P.comm = (fun x y -> ()); + P.assoc = (fun x y z -> ()); + P.assoc_r = (fun x y z -> ()); + P.is_unit = (fun x -> ()); + P.refine = (fun x -> p_refine p (downgrade_val x)); + } + +let raise_pcm0 + (#a: Type u#a) + (p: pcm0 a) +: Tot (pcm0 (raise_t u#a u#b a)) += pcm_of_fstar_pcm (fstar_raise_pcm0 p) + +let raise_pcm + (#a: Type u#a) + (p: pcm a) +: Tot (pcm (raise_t u#a u#b a)) += let res = raise_pcm0 u#a u#b p in + let aux1 + (x: raise_t a) + (y: raise_t a { composable res x y }) + : Lemma + (requires (op res x y == one res)) + (ensures (x == one res /\ y == one res)) + [SMTPat (composable res x y)] + = + let x' = downgrade_val x in + let y' = downgrade_val y in + assert (composable p x' y'); + assert (op p x' y' == downgrade_val (raise_val (one p))); + assert (op res x y == one res) + in + let aux2 + (x: raise_t a) + : Lemma + (requires (p_refine res x)) + (ensures (exclusive res x)) + [SMTPat (p_refine res x)] + = + let aux + (frame: raise_t a) + : Lemma + (requires (composable res x frame)) + (ensures (frame == one res)) + [SMTPat (composable res x frame)] + = + let x' = downgrade_val x in + assert (p_refine p x'); + let frame' = downgrade_val frame in + assert (composable p x' frame'); + assert (frame' == one p); + assert (frame == raise_val frame') + in + () + in + res + +open Steel.C.Model.Connection + +let raise_pcm_morphism + (#a: Type u#a) + (p: pcm a) +: Tot (morphism p (raise_pcm u#a u#b p)) += + mkmorphism + raise_val + () + (fun _ _ -> ()) + +let raise_pcm_morphism_inverse + (#a: Type u#a) + (p: pcm a) +: Tot (morphism (raise_pcm u#a u#b p) p) += + mkmorphism + downgrade_val + () + (fun _ _ -> ()) + +let raise_pcm_isomorphism + (#a: Type u#a) + (p: pcm a) +: Tot (isomorphism p (raise_pcm u#a u#b p)) += + mkisomorphism + (raise_pcm_morphism p) + (raise_pcm_morphism_inverse p) + () + () + (fun _ -> ()) + (fun _ -> ()) diff --git a/lib/steel/c/Steel.C.Opt.fst b/lib/steel/c/Steel.C.Opt.fst new file mode 100644 index 000000000..eac2b3e48 --- /dev/null +++ b/lib/steel/c/Steel.C.Opt.fst @@ -0,0 +1,45 @@ +module Steel.C.Opt + +open Steel.C.Model.PCM +module A = Steel.Effect.Atomic +module STC = Steel.ST.Coercions + + +let opt_read_sel + #a #b r += ref_read_sel r (opt_view b) + +let opt_write_sel + #a #b r w += + let _ = pts_to_view_elim r (opt_view _) in + opt_pcm_write r _ w; + pts_to_view_intro r _ (opt_view _) w + +let ref_opt_read + #a #b r += ref_read_sel r (opt_view b) + +let ref_opt_write + #a #b r w += opt_write_sel r w + +let malloc + #c x += + let xc = ((opt_view c).to_carrier x) in + let r = Steel.C.Model.Ref.ref_alloc _ xc in + pts_to_view_intro r xc (opt_view c) x; + let r' : ref (option c) c (opt_pcm #c) = r in + A.change_equal_slprop + (Steel.C.Model.Ref.pts_to_view r (opt_view c)) + (pts_to_view r' (opt_view c)); + intro_pts_to_view_or_null_not_null r' (opt_view c); + A.return r' + +let free + #c r += + let r' : Steel.C.Model.Ref.ref (option c) (opt_pcm #c) = r in + let _ = pts_to_view_elim r (opt_view c) in + Steel.C.Model.Ref.ref_free r diff --git a/lib/steel/c/Steel.C.Opt.fsti b/lib/steel/c/Steel.C.Opt.fsti new file mode 100644 index 000000000..b0ae13221 --- /dev/null +++ b/lib/steel/c/Steel.C.Opt.fsti @@ -0,0 +1,89 @@ +module Steel.C.Opt +include Steel.ST.C.Opt + +module P = FStar.PCM +open Steel.C.Model.PCM +open Steel.C.Model.Ref +open Steel.Effect + +let opt_view + (a: Type) +: Tot (sel_view (opt_pcm #a) a false) += { + to_view_prop = (fun x -> Some? x == true); + to_view = (fun x -> Some?.v x); + to_carrier = (fun z -> Some z); + to_carrier_not_one = (); + to_view_frame = (fun x frame -> ()); +} + +val opt_read_sel + (#a: Type u#0) (#b: Type u#0) + (r: ref a (opt_pcm #b)) +: Steel b + (pts_to_view r (opt_view b)) + (fun _ -> pts_to_view r (opt_view b)) + (requires (fun _ -> True)) + (ensures (fun h res h' -> + res == h (pts_to_view r (opt_view b)) /\ + res == h' (pts_to_view r (opt_view b)) + )) + +val opt_write_sel + (#a: Type u#0) (#b: Type u#0) + (r: ref a (opt_pcm #b)) + (w: b) +: Steel unit + (pts_to_view r (opt_view b)) + (fun _ -> pts_to_view r (opt_view b)) + (requires (fun _ -> True)) + (ensures (fun _ _ h' -> + w == h' (pts_to_view r (opt_view b)) + )) + +open Steel.C.Reference + +val ref_opt_read + (#a: Type u#0) (#b: Type u#0) + (r: ref a b (opt_pcm #b)) +: Steel b + (pts_to_view r (opt_view b)) + (fun _ -> pts_to_view r (opt_view b)) + (requires (fun _ -> True)) + (ensures (fun h res h' -> + res == h (pts_to_view r (opt_view b)) /\ + res == h' (pts_to_view r (opt_view b)) + )) + +val ref_opt_write + (#a: Type u#0) (#b: Type u#0) + (r: ref a b (opt_pcm #b)) + (w: b) +: Steel unit + (pts_to_view r (opt_view b)) + (fun _ -> pts_to_view r (opt_view b)) + (requires (fun _ -> True)) + (ensures (fun _ _ h' -> + w == h' (pts_to_view r (opt_view b)) + )) + +val malloc + (#c:Type0) (x: c) +: Steel (ptr (option c) c (opt_pcm #c)) + emp + (fun r -> pts_to_view_or_null r (opt_view c)) + (requires fun _ -> True) + (ensures fun _ r h' -> + let s = h' (pts_to_view_or_null r (opt_view c)) in + ptr_is_null r == None? s /\ + (Some? s ==> (Some?.v s == x /\ freeable r)) + ) + +val free + (#c: Type0) + (r: ref (option c) c (opt_pcm #c)) +: Steel unit + (pts_to_view r (opt_view c)) + (fun _ -> emp) + (requires fun _ -> freeable r) + (ensures fun _ _ _ -> True) diff --git a/lib/steel/c/Steel.C.Reference.fst b/lib/steel/c/Steel.C.Reference.fst new file mode 100644 index 000000000..cf0f594f2 --- /dev/null +++ b/lib/steel/c/Steel.C.Reference.fst @@ -0,0 +1,388 @@ +module Steel.C.Reference +open Steel.C.Model.PCM +open Steel.C.Model.Ref +open Steel.Effect +open Steel.Effect.Atomic + +#push-options "--print_universes" + +// [@@__reduce__] +let ptr (a: Type u#0) (view_t: Type u#0) (#b: Type u#b) (q: pcm b) +: Type u#b += ptr a q + +// [@@__reduce__] +inline_for_extraction +let ref (a: Type u#0) (view_t: Type u#0) (#b: Type u#b) (q: pcm b) +: Tot (Type u#b) += + (x: ptr a view_t q { not_null x }) + +unfold +let ref_of_ref + (#a: Type u#0) (#view_t: Type u#0) (#b: Type u#b) (#q: pcm b) + (r: ref a view_t q) +: Tot (Steel.C.Model.Ref.ref a q) += r + +[@@__steel_reduce__] // ; __reduce__] +let pts_to_view + (#a: Type u#0) + (#view_t: Type u#0) + (#view_t': Type u#0) + (#b: Type u#b) (#p: pcm b) + (r: ref a view_t p) (view: sel_view p view_t' false) +: vprop += r `pts_to_view` view + +let ref_read + (#a: Type u#0) (#b: Type u#b) + (#view_t: Type u#0) + (#p: pcm b) + (#vw: sel_view p view_t false) + (r: ref a view_t p) +: Steel view_t + (r `pts_to_view` vw) + (fun _ -> r `pts_to_view` vw) + (requires (fun _ -> True)) + (ensures (fun h res h' -> + res == h (r `pts_to_view` vw) /\ + res == h' (r `pts_to_view` vw) + )) += ref_read_sel r vw + +let null (a: Type u#0) (view_t: Type u#0) (#b: Type u#b) (p: pcm b) : Tot (ptr a view_t p) = null a p + +[@@__steel_reduce__] // ; __reduce__] +let pts_to_view_or_null + (#a: Type u#0) + (#view_t: Type u#0) + (#view_t': Type u#0) + (#b: Type u#b) (#p: pcm b) + (r: ptr a view_t p) (view: sel_view p view_t' false) +: vprop += r `pts_to_view_or_null` view + +let is_null + (#a: Type u#0) (#b: Type u#b) (#c: Type0) (#opened: _) (#p: pcm b) + (r: ptr a c p) + (vw: sel_view p c false) +: SteelAtomicBase bool false opened Unobservable + (pts_to_view_or_null r vw) + (fun _ -> pts_to_view_or_null r vw) + (requires (fun _ -> True)) + (ensures (fun h res h' -> + let s = h (pts_to_view_or_null r vw) in + h' (pts_to_view_or_null r vw) == s /\ + res == ptr_is_null r /\ + res == (None? s) + )) += is_null r vw + +let intro_pts_to_view_or_null_null + (#inames: _) + (a: Type) (#b: Type) (#p: pcm b) + (#c: Type0) + (vw: sel_view p c false) +: SteelGhost unit inames + emp + (fun _ -> pts_to_view_or_null (null a c p) vw) + (requires (fun _ -> True)) + (ensures (fun _ _ h' -> h' (pts_to_view_or_null (null a c p) vw) == None)) += intro_pts_to_view_or_null_null a vw + +let elim_pts_to_view_or_null_null + (#inames: _) + (a: Type) (#b: Type) (#p: pcm b) + (#c: Type0) + (r: ptr a c p) + (vw: sel_view p c false) +: SteelGhost unit inames + (pts_to_view_or_null r vw) + (fun _ -> emp) + (requires (fun _ -> ptr_is_null r == true)) + (ensures (fun _ _ _ -> True)) += change_equal_slprop + (pts_to_view_or_null r vw) + (pts_to_view_or_null (null a c p) vw); + elim_pts_to_view_or_null_null a vw + +let intro_pts_to_view_or_null_not_null + (#inames: _) + (#a: Type) (#b: Type) (#p: pcm b) + (#c: Type0) + (r: ref a c p) + (vw: sel_view p c false) +: SteelGhost unit inames + (pts_to_view r vw) + (fun _ -> pts_to_view_or_null r vw) + (requires (fun _ -> True)) + (ensures (fun h _ h' -> h' (pts_to_view_or_null r vw) == Some (h (pts_to_view r vw)))) += intro_pts_to_view_or_null_not_null r vw + +let elim_pts_to_view_or_null_not_null + (#inames: _) + (#a: Type) (#b: Type) (#p: pcm b) + (#c: Type0) + (r: ref a c p) + (vw: sel_view p c false) +: SteelGhost unit inames + (pts_to_view_or_null r vw) + (fun _ -> pts_to_view r vw) + (requires (fun _ -> True)) + (ensures (fun h _ h' -> h (pts_to_view_or_null r vw) == Some (h' (pts_to_view r vw)))) += elim_pts_to_view_or_null_not_null r vw + +let freeable + (#a: Type u#0) (#view_t: Type u#0) (#b: Type u#0) (#q: pcm b) + (r: ref a view_t q) +: Tot prop += freeable (r <: Steel.C.Model.Ref.ref a q) + +(* Operations on views *) + +let refine_view + (#carrier: Type u#a) + (#p: pcm carrier) + (#view: Type u#b) + (#can_view_unit:bool) + (vw: sel_view p view can_view_unit) + (pr: (view -> Tot prop)) +: Tot (sel_view p (refine view pr) can_view_unit) += { + to_view_prop = (fun (c: carrier) -> vw.to_view_prop c /\ pr (vw.to_view c)); + to_view = (fun c -> vw.to_view c <: refine view pr); + to_carrier = (fun (v: refine view pr) -> vw.to_carrier v <: carrier); + to_carrier_not_one = vw.to_carrier_not_one; + to_view_frame = (fun x frame -> vw.to_view_frame x frame); +} + +let intro_refine_view' + (#opened: _) + (#carrier: Type u#a) + (#p: pcm carrier) + (#view: Type) + (vw: sel_view p view false) + (pr: (view -> Tot prop)) + (#base: Type) + (r: ref base view p) +: SteelGhost unit opened + (pts_to_view r vw) + (fun _ -> pts_to_view r (refine_view vw pr)) + (fun h -> pr (h (pts_to_view r vw))) + (fun h _ h' -> + let x = h (pts_to_view r vw) in + pr x /\ + x == h' (pts_to_view r (refine_view vw pr)) + ) += let g = gget (pts_to_view r vw) in + let v = pts_to_view_elim r vw in + pts_to_view_intro r v (refine_view vw pr) (Ghost.hide (Ghost.reveal g)) + +inline_for_extraction +noextract +let intro_refine_view + (#opened: _) + (#carrier: Type u#a) + (#p: pcm carrier) + (#view: Type) + (vw: sel_view p view false) + (pr: (view -> Tot prop)) + (#base: Type) + (r: ref base view p) +: SteelAtomicBase (ref base (refine view pr) p) false opened Unobservable + (pts_to_view r vw) + (fun r' -> pts_to_view r' (refine_view vw pr)) + (fun h -> pr (h (pts_to_view r vw))) + (fun h r' h' -> + let x = h (pts_to_view r vw) in + pr x /\ + r == r' /\ + x == h' (pts_to_view r' (refine_view vw pr)) + ) += intro_refine_view' vw pr r; + let r' : ref base (refine view pr) p = r in + change_equal_slprop + (pts_to_view r (refine_view vw pr)) + (pts_to_view r' (refine_view vw pr)); + return r' + +let elim_refine_view' + (#opened: _) + (#carrier: Type u#a) + (#p: pcm carrier) + (#view: Type) + (vw: sel_view p view false) + (pr: (view -> Tot prop)) + (#base: Type) + (r: ref base (refine view pr) p) +: SteelGhost unit opened + (pts_to_view r (refine_view vw pr)) + (fun _ -> pts_to_view r vw) + (fun h -> True) + (fun h _ h' -> + let x = h' (pts_to_view r vw) in + pr x /\ + x == h (pts_to_view r (refine_view vw pr)) + ) += let g = gget (pts_to_view r (refine_view vw pr)) in + let v = pts_to_view_elim r (refine_view vw pr) in + pts_to_view_intro r v vw (Ghost.hide (Ghost.reveal g)) + +inline_for_extraction +noextract +let elim_refine_view + (#opened: _) + (#carrier: Type u#a) + (#p: pcm carrier) + (#view: Type) + (vw: sel_view p view false) + (pr: (view -> Tot prop)) + (#base: Type) + (r: ref base (refine view pr) p) +: SteelAtomicBase (ref base view p) false opened Unobservable + (pts_to_view r (refine_view vw pr)) + (fun r' -> pts_to_view r' vw) + (fun h -> True) + (fun h r' h' -> + let x = h' (pts_to_view r' vw) in + pr x /\ + r' == r /\ + x == h (pts_to_view r (refine_view vw pr)) + ) += elim_refine_view' vw pr r; + let r' : ref base view p = r in + change_equal_slprop + (pts_to_view r vw) + (pts_to_view r' vw); + return r' + +let rewrite_view + (#carrier: Type u#a) + (#p: pcm carrier) + (#view: Type u#b) + (#can_view_unit:bool) + (vw: sel_view p view can_view_unit) + (#view' : Type u#c) + (f: view -> view') + (g: view' -> view) + (prf: squash (f `Steel.C.Model.Connection.is_inverse_of` g)) +: Tot (sel_view p view' can_view_unit) += { + to_view_prop = vw.to_view_prop; + to_view = (fun c -> f (vw.to_view c)); + to_carrier = (fun v -> vw.to_carrier (g v)); + to_carrier_not_one = vw.to_carrier_not_one; + to_view_frame = (fun x frame -> vw.to_view_frame (g x) frame); +} + +let intro_rewrite_view' + (#opened: _) + (#carrier: Type u#a) + (#p: pcm carrier) + (#view: Type) + (vw: sel_view p view false) + (#view' : Type) + (f: view -> view') + (g: view' -> view) + (prf: squash (f `Steel.C.Model.Connection.is_inverse_of` g)) + (#base: _) + (r: ref base view p) + (x' : Ghost.erased view') +: SteelGhost unit opened + (pts_to_view r vw) + (fun _ -> pts_to_view r (rewrite_view vw f g prf)) + (fun h -> h (pts_to_view r vw) == g x') + (fun h _ h' -> + f (h (pts_to_view r vw)) == Ghost.reveal x' /\ + h' (pts_to_view r (rewrite_view vw f g prf)) == Ghost.reveal x' + ) += let v = pts_to_view_elim r vw in + pts_to_view_intro r v (rewrite_view vw f g prf) x' + +inline_for_extraction +noextract +let intro_rewrite_view + (#opened: _) + (#carrier: Type u#a) + (#p: pcm carrier) + (#view: Type) + (vw: sel_view p view false) + (#view' : Type) + (f: view -> view') + (g: view' -> view) + (prf: squash (f `Steel.C.Model.Connection.is_inverse_of` g)) + (#base: _) + (r: ref base view p) + (x' : Ghost.erased view') +: SteelAtomicBase (ref base view' p) false opened Unobservable + (pts_to_view r vw) + (fun r' -> pts_to_view r' (rewrite_view vw f g prf)) + (fun h -> h (pts_to_view r vw) == g x') + (fun h r' h' -> + f (h (pts_to_view r vw)) == Ghost.reveal x' /\ + h' (pts_to_view r' (rewrite_view vw f g prf)) == Ghost.reveal x' + ) += intro_rewrite_view' vw f g prf r x'; + let r' : ref base view' p = r in + change_equal_slprop + (pts_to_view r (rewrite_view vw f g prf)) + (pts_to_view r' (rewrite_view vw f g prf)); + return r' + +let elim_rewrite_view' + (#opened: _) + (#carrier: Type u#a) + (#p: pcm carrier) + (#view: Type) + (vw: sel_view p view false) + (#view' : Type) + (f: view -> view') + (g: view' -> view) + (prf: squash (f `Steel.C.Model.Connection.is_inverse_of` g)) + (#base: _) + (r: ref base view' p) +: SteelGhost unit opened + (pts_to_view r (rewrite_view vw f g prf)) + (fun _ -> pts_to_view r vw) + (fun _ -> True) + (fun h _ h' -> + let x = h (pts_to_view r (rewrite_view vw f g prf)) in + let x' = h' (pts_to_view r vw) in + Ghost.reveal x' == g (Ghost.reveal x) /\ + f (Ghost.reveal x') == Ghost.reveal x + ) += let gv = gget (pts_to_view r (rewrite_view vw f g prf)) in + let v = pts_to_view_elim r (rewrite_view vw f g prf) in + pts_to_view_intro r v vw (g gv) + +inline_for_extraction +noextract +let elim_rewrite_view + (#opened: _) + (#carrier: Type u#a) + (#p: pcm carrier) + (#view: Type) + (vw: sel_view p view false) + (#view' : Type) + (f: view -> view') + (g: view' -> view) + (prf: squash (f `Steel.C.Model.Connection.is_inverse_of` g)) + (#base: _) + (r: ref base view' p) +: SteelAtomicBase (ref base view p) false opened Unobservable + (pts_to_view r (rewrite_view vw f g prf)) + (fun r' -> pts_to_view r' vw) + (fun _ -> True) + (fun h r' h' -> + let x = h (pts_to_view r (rewrite_view vw f g prf)) in + let x' = h' (pts_to_view r' vw) in + Ghost.reveal x' == g (Ghost.reveal x) /\ + f (Ghost.reveal x') == Ghost.reveal x + ) += elim_rewrite_view' vw f g prf r; + let r' : ref base view p = r in + change_equal_slprop + (pts_to_view r vw) + (pts_to_view r' vw); + return r' diff --git a/lib/steel/c/Steel.C.StdInt.Base.fst b/lib/steel/c/Steel.C.StdInt.Base.fst new file mode 100644 index 000000000..2c7bc3a1c --- /dev/null +++ b/lib/steel/c/Steel.C.StdInt.Base.fst @@ -0,0 +1,60 @@ +module Steel.C.StdInt.Base + +module I64 = FStar.Int64 +module Cast = FStar.Int.Cast + +(* FIXME: this could be defined as U64.t, but KaRaMeL currently demands U32.t. + NS: A long-term proposal would be to make KaRaMeL platform-aware and introduce + a platform switch in this library here. +*) + +let size_t = U32.t + +let size_precond x = + FStar.UInt.fits x U32.n == true + +let size_v x = + U32.v x + +let size_v_inj (x1 x2: size_t) : Lemma + (size_v x1 == size_v x2 ==> x1 == x2) + [SMTPat (size_v x1); SMTPat (size_v x2)] += () + +let mk_size_t (x: U32.t) : Pure size_t + (requires True) + (ensures (fun y -> size_v y == U32.v x)) += x + +let int_to_size_t x = + U32.uint_to_t x + +let size_precond_le x y = () + +let size_add x y = x `U32.add` y + +let size_sub x y = x `U32.sub` y + +let size_mul x y = x `U32.mul` y + +let size_div x y = x `U32.div` y + +let size_le x y = x `U32.lte` y + +let size_lt x y = x `U32.lt` y + +let ptrdiff_t = I64.t + +let ptrdiff_v x = + I64.v x + +let ptrdiff_v_inj (x1 x2: ptrdiff_t) : Lemma + (ptrdiff_v x1 == ptrdiff_v x2 ==> x1 == x2) + [SMTPat (ptrdiff_v x1); SMTPat (ptrdiff_v x2)] += () + +let mk_ptrdiff_t x = Cast.int32_to_int64 x + +let ptrdiff_precond x = FStar.Int.fits x I64.n == true + +let intro_ptrdiff_precond x = () diff --git a/lib/steel/c/Steel.C.StdInt.Base.fsti b/lib/steel/c/Steel.C.StdInt.Base.fsti new file mode 100644 index 000000000..27f40e8e4 --- /dev/null +++ b/lib/steel/c/Steel.C.StdInt.Base.fsti @@ -0,0 +1,87 @@ +module Steel.C.StdInt.Base + +open FStar.Mul + +module U32 = FStar.UInt32 +module I32 = FStar.Int32 + +inline_for_extraction noextract // TODO: replace with primitive extraction +val size_t : eqtype + +val size_precond (x: nat) : Tot prop + +noextract +val size_v (x: size_t) : Pure nat // should be Ghost, but need Pure to implement alloc + (requires True) + (ensures (fun y -> size_precond y)) + +val size_v_inj (x1 x2: size_t) : Lemma + (size_v x1 == size_v x2 ==> x1 == x2) + [SMTPat (size_v x1); SMTPat (size_v x2)] + +inline_for_extraction noextract +val mk_size_t (x: U32.t) : Pure size_t + (requires True) + (ensures (fun y -> size_v y == U32.v x)) + +noextract +val int_to_size_t (x: nat) : Pure size_t // should be Ghost, but need Pure to implement array views + (requires (size_precond x)) + (ensures (fun y -> size_v y == x)) + +val size_precond_le (x y: nat) : Lemma + (requires (x <= y /\ size_precond y)) + (ensures (size_precond x)) + [SMTPat (size_precond x); SMTPat (size_precond y)] + +val size_add (x y: size_t) : Pure size_t + (requires (size_precond (size_v x + size_v y))) + (ensures (fun z -> size_v z == size_v x + size_v y)) + +val size_sub (x y: size_t) : Pure size_t + (requires (size_v x >= size_v y)) + (ensures (fun z -> size_v z == size_v x - size_v y)) + +val size_mul (x y: size_t) : Pure size_t + (requires (size_precond (size_v x * size_v y))) + (ensures (fun z -> size_v z == size_v x * size_v y)) + +val size_div (x y: size_t) : Pure size_t + (requires (size_v y > 0)) + (ensures (fun z -> size_v z == size_v x / size_v y)) + +val size_le (x y: size_t) : Pure bool + (requires True) + (ensures (fun z -> z == (size_v x <= size_v y))) + +val size_lt (x y: size_t) : Pure bool + (requires True) + (ensures (fun z -> z == (size_v x < size_v y))) + +let zero_size : (zero_size: size_t { size_v zero_size == 0 }) = mk_size_t 0ul + +let one_size : (zero_size: size_t { size_v zero_size == 1 }) = mk_size_t 1ul + +inline_for_extraction noextract // TODO: replace with primitive extraction +val ptrdiff_t : eqtype + +module I32 = FStar.Int32 + +val ptrdiff_v (x: ptrdiff_t) : Tot int // same remark as for size_v + +val ptrdiff_v_inj (x1 x2: ptrdiff_t) : Lemma + (ptrdiff_v x1 == ptrdiff_v x2 ==> x1 == x2) + [SMTPat (ptrdiff_v x1); SMTPat (ptrdiff_v x2)] + +val mk_ptrdiff_t (x: I32.t) : Pure ptrdiff_t + (requires True) + (ensures (fun y -> ptrdiff_v y == I32.v x)) + +let zero_ptrdiff : (zero_ptrdiff: ptrdiff_t { ptrdiff_v zero_ptrdiff == 0 }) = + mk_ptrdiff_t 0l + +val ptrdiff_precond (x: int) : Tot prop + +val intro_ptrdiff_precond (x: int) : Lemma + (requires (FStar.Int.size x I32.n)) + (ensures (ptrdiff_precond x)) diff --git a/lib/steel/c/Steel.C.StdInt.fst b/lib/steel/c/Steel.C.StdInt.fst new file mode 100644 index 000000000..f54a56d88 --- /dev/null +++ b/lib/steel/c/Steel.C.StdInt.fst @@ -0,0 +1,26 @@ +module Steel.C.StdInt +include Steel.C.StdInt.Base + +open Steel.C.Fields +open Steel.C.Typedef +open Steel.C.Opt + +module U16 = FStar.UInt16 +module U32 = FStar.UInt32 +module U64 = FStar.UInt64 + +[@@c_typedef] +noextract inline_for_extraction +let size: typedef = opt_typedef size_t + +[@@c_typedef] +noextract inline_for_extraction +let uint16: typedef = opt_typedef U16.t + +[@@c_typedef] +noextract inline_for_extraction +let uint32: typedef = opt_typedef U32.t + +[@@c_typedef] +noextract inline_for_extraction +let uint64: typedef = opt_typedef U64.t diff --git a/lib/steel/c/Steel.C.StructLiteral.fsti b/lib/steel/c/Steel.C.StructLiteral.fsti new file mode 100644 index 000000000..d6d5dc4ec --- /dev/null +++ b/lib/steel/c/Steel.C.StructLiteral.fsti @@ -0,0 +1,472 @@ +module Steel.C.StructLiteral + +#set-options "--z3rlimit 50 --ide_id_info_off" + +open Steel.Memory +open Steel.Effect +open Steel.Effect.Common +open Steel.Effect.Atomic + +open Steel.C.Model.PCM +open Steel.C.Model.Struct +open Steel.C.Typedef +open Steel.C.Model.Ref +open Steel.C.Model.Connection +open Steel.C.Opt +open Steel.C.Fields + +open FStar.List.Tot +open FStar.FunctionalExtensionality +open FStar.FSet + +module TS = Steel.C.Typestring + +val mk_struct_def (tag: Type0) (field_descriptions: Type0): Type0 + +(** To declare a struct definition, one needs to write + let _ = norm norm_c_typedef (mk_c_struct ). + This normalizes to a type (mk_struct_def t fs), where + t is an embedding of the struct tag name as a Type0 (see Steel.C.Typestring), and + fs is an embedding of the struct fields as a Type0 + Karamel then parses this embedding and emits a C struct definition. *) +let mk_c_struct (tag: Type0) (fields: c_fields) = + mk_struct_def tag (c_fields_t fields) + +(** When we take a pointer &p->foo, p loses access to field foo. + To record this fact, we explcitly track the set of excluded fields. *) +let excluded_fields = s:set string{s "" == false} + +(** A [struct' tag fields excluded] is a view type for C structs with + tag [tag], fields [fields], and excluded fields [excluded]. *) +val struct' (tag: Type0) (fields: c_fields) (excluded: excluded_fields): Type0 + +(** A [struct tag fields] is a view type for C structs with tag [tag] and fields [fields]. *) +inline_for_extraction +let struct (tag: Type0) (fields: c_fields) = struct' tag fields emptyset + +(** Combinators for constructing struct literals *) + +val mk_nil (tag: Type0): struct tag fields_nil + +val mk_cons (tag: Type0) (fields: c_fields) + (field: field_t) (td: typedef) (x: td.view_type) (v: struct tag fields) +: Pure (struct tag (fields_cons field td fields)) + (requires fields.has_field field == false) + (ensures fun _ -> True) + +(** [struct_pcm_carrier tag fields] is the carrier of the PCM which + models structs with tag [tag] and fields [fields]. *) +val struct_pcm_carrier (tag: Type0) (fields: c_fields): Type0 + +(** [struct_pcm] is the PCM that models structs with tag [tag] and fields [fields]. *) +val struct_pcm (tag: Type0) (fields: c_fields): pcm (struct_pcm_carrier tag fields) + +/// Reading a struct field +val struct_get + (#tag: Type0) (#fields: c_fields) (#excluded: excluded_fields) + (x: struct' tag fields excluded) (field: field_of fields) +: Pure (fields.get_field field).view_type + (requires excluded field == false) + (ensures fun _ -> True) + +/// Writing a struct field +val struct_put + (#tag: Type0) (#fields: c_fields) (#excluded: excluded_fields) + (x: struct' tag fields excluded) + (field: field_of fields) + (v: (fields.get_field field).view_type) +: Pure (struct' tag fields excluded) + (requires excluded field == false) + (ensures fun _ -> True) + +/// For a fixed field name, struct_get and struct_put form a lens + +val struct_get_put + (#tag: Type0) (#fields: c_fields) (#excluded: excluded_fields) + (x: struct' tag fields excluded) + (field: field_of fields) + (v: (fields.get_field field).view_type) +: Lemma + (requires excluded field == false) + (ensures struct_put x field v `struct_get` field == v) + [SMTPat (struct_put x field v `struct_get` field)] + +val struct_put_get + (#tag: Type0) (#fields: c_fields) (#excluded: excluded_fields) + (x: struct' tag fields excluded) + (field: field_of fields) +: Lemma + (requires excluded field == false) + (ensures struct_put x field (x `struct_get` field) == x) + [SMTPat (struct_put x field (x `struct_get` field))] + +val struct_put_put + (#tag: Type0) (#fields: c_fields) + (x: struct tag fields) + (field: field_of fields) + (v w: (fields.get_field field).view_type) +: Lemma (struct_put (struct_put x field v) field w == struct_put x field w) + [SMTPat (struct_put (struct_put x field v) field w)] + +/// struct_get/struct_put pairs for different fields don't interfere with each other + +val struct_get_put_ne + (#tag: Type0) (#fields: c_fields) (#excluded: excluded_fields) + (x: struct' tag fields excluded) + (field1: field_of fields) + (field2: field_of fields) + (v: (fields.get_field field1).view_type) +: Lemma + (requires field1 =!= field2 /\ excluded field1 == false /\ excluded field2 == false) + (ensures struct_put x field1 v `struct_get` field2 == x `struct_get` field2) + [SMTPat (struct_put x field1 v `struct_get` field2)] + +val struct_put_put_ne + (#tag: Type0) (#fields: c_fields) (#excluded: excluded_fields) + (x: struct' tag fields excluded) + (field1: field_of fields) + (v: (fields.get_field field1).view_type) + (field2: field_of fields) + (w: (fields.get_field field2).view_type) +: Lemma + (requires field1 =!= field2 /\ excluded field1 == false /\ excluded field2 == false) + (ensures + struct_put (struct_put x field1 v) field2 w == + struct_put (struct_put x field2 w) field1 v) + +let struct_pcm_one (tag: Type0) (fields: c_fields) +: struct_pcm_carrier tag fields += one (struct_pcm tag fields) + +/// Reading a pcm_struct_carrier field +val struct_pcm_get + (#tag: Type0) (#fields: c_fields) + (x: struct_pcm_carrier tag fields) (field: field_of fields) +: (fields.get_field field).carrier + +/// Writing a struct_pcm_carrier field +val struct_pcm_put + (#tag: Type0) (#fields: c_fields) + (x: struct_pcm_carrier tag fields) + (field: field_of fields) + (v: (fields.get_field field).carrier) +: struct_pcm_carrier tag fields + +/// For a fixed field name, struct_pcm_get and struct_pcm_put form a lens + +val struct_pcm_get_put + (#tag: Type0) (#fields: c_fields) + (x: struct_pcm_carrier tag fields) + (field: field_of fields) + (v: (fields.get_field field).carrier) +: Lemma (struct_pcm_put x field v `struct_pcm_get` field == v) + [SMTPat (struct_pcm_put x field v `struct_pcm_get` field)] + +val struct_pcm_put_get + (#tag: Type0) (#fields: c_fields) + (x: struct_pcm_carrier tag fields) + (field: field_of fields) +: Lemma (struct_pcm_put x field (x `struct_pcm_get` field) == x) + [SMTPat (struct_pcm_put x field (x `struct_pcm_get` field))] + +val struct_pcm_put_put + (#tag: Type0) (#fields: c_fields) + (x: struct_pcm_carrier tag fields) + (field: field_of fields) + (v w: (fields.get_field field).carrier) +: Lemma (struct_pcm_put (struct_pcm_put x field v) field w == struct_pcm_put x field w) + [SMTPat (struct_pcm_put (struct_pcm_put x field v) field w)] + +/// struct_pcm_get/struct_pcm_put pairs for different fields don't interfere with each other + +val struct_pcm_get_put_ne + (#tag: Type0) (#fields: c_fields) + (x: struct_pcm_carrier tag fields) + (field1: field_of fields) + (field2: field_of fields) + (v: (fields.get_field field1).carrier) +: Lemma + (requires field1 =!= field2) + (ensures struct_pcm_put x field1 v `struct_pcm_get` field2 == x `struct_pcm_get` field2) + [SMTPat (struct_pcm_put x field1 v `struct_pcm_get` field2)] + +val struct_pcm_put_put_ne + (#tag: Type0) (#fields: c_fields) + (x: struct_pcm_carrier tag fields) + (field1: field_of fields) + (v: (fields.get_field field1).carrier) + (field2: field_of fields) + (w: (fields.get_field field2).carrier) +: Lemma + (requires field1 =!= field2) + (ensures + struct_pcm_put (struct_pcm_put x field1 v) field2 w == + struct_pcm_put (struct_pcm_put x field2 w) field1 v) + +val struct_view (tag: Type0) (fields: c_fields) (excluded: excluded_fields) +: sel_view (struct_pcm tag fields) (struct' tag fields excluded) false + +val struct_is_unit (tag: Type0) (fields: c_fields) + (v: struct_pcm_carrier tag fields) +: b:bool{b <==> v == one (struct_pcm tag fields)} + +[@@c_typedef] +let typedef_struct (tag: Type0) (fields: c_fields): typedef = { + carrier = struct_pcm_carrier tag fields; + pcm = struct_pcm tag fields; + view_type = struct tag fields; + view = struct_view tag fields emptyset; + is_unit = struct_is_unit tag fields; +} + +(** A connection from a struct to any of its fields *) +val struct_field + (tag: Type0) (fields: c_fields) (field: field_of fields) +: connection (struct_pcm tag fields) (fields.get_field field).pcm + +(** extract_field tag fields excluded field v = (v without field field, v.field) *) +val extract_field + (tag: Type0) (fields: c_fields) (excluded: excluded_fields) + (field: field_of fields) + (v: struct' tag fields excluded) +: Pure (struct' tag fields (insert field excluded) & (fields.get_field field).view_type) + (requires not (excluded field)) + (ensures fun _ -> True) + +val extract_field_extracted + (tag: Type0) (fields: c_fields) (excluded: excluded_fields) + (field: field_of fields) + (v: struct' tag fields excluded) +: Lemma + (requires not (excluded field)) + (ensures snd (extract_field tag fields excluded field v) == v `struct_get` field) + [SMTPat (extract_field tag fields excluded field v)] + +val extract_field_unextracted + (tag: Type0) (fields: c_fields) (excluded: excluded_fields) + (field: field_of fields) + (field': field_of fields) + (v: struct' tag fields excluded) +: Lemma + (requires not (excluded field) /\ not (excluded field') /\ (field =!= field')) + (ensures + not (excluded field) /\ not (excluded field') /\ (field =!= field') /\ + fst (extract_field tag fields excluded field v) `struct_get` field' + == v `struct_get` field') +// [SMTPat (extract_field tag fields excluded field v); +// SMTPat (has_type field' string)] + +val extract_field_unextracted' + (tag: Type0) (fields: c_fields) (excluded: excluded_fields) + (field: field_of fields) + (v: struct' tag fields excluded) +: Lemma (forall (field': field_of fields). + (not (excluded field) /\ not (excluded field') /\ (field =!= field')) ==> + (fst (extract_field tag fields excluded field v) `struct_get` field' == v `struct_get` field')) + [SMTPat (extract_field tag fields excluded field v)] + +val addr_of_struct_field_ref + (#tag: Type0) (#fields: c_fields) (#excluded: excluded_fields) + (field: field_of fields) + (p: ref 'a (struct_pcm tag fields)) +: Steel (ref 'a (fields.get_field field).pcm) + (p `pts_to_view` struct_view tag fields excluded) + (fun q -> + (p `pts_to_view` struct_view tag fields (insert field excluded)) `star` + (pts_to_view u#0 + #'a + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).carrier)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).pcm)) + q + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view_type)) + #false + (norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view)))) + (requires fun _ -> not (excluded field)) + (ensures fun h q h' -> + not (excluded field) /\ + q == ref_focus p (struct_field tag fields field) /\ + fst (extract_field tag fields excluded field + (h (p `pts_to_view` struct_view tag fields excluded))) + == h' (p `pts_to_view` struct_view tag fields (insert field excluded)) /\ + snd (extract_field tag fields excluded field + (h (p `pts_to_view` struct_view tag fields excluded))) + == h' (q `pts_to_view` (fields.get_field field).view)) + +val unaddr_of_struct_field_ref' + (#tag: Type0) (#fields: c_fields) (#excluded: excluded_fields) + (field: field_of fields) + (p: ref 'a (struct_pcm tag fields)) + (q: ref 'a (fields.get_field field).pcm) +: Steel unit + ((p `pts_to_view` struct_view tag fields excluded) `star` + (q `pts_to_view` (fields.get_field field).view)) + (fun _ -> p `pts_to_view` struct_view tag fields (remove field excluded)) + (requires fun _ -> + excluded field == true /\ + q == ref_focus p (struct_field tag fields field)) + (ensures fun h _ h' -> + excluded field == true /\ + fst (extract_field tag fields (remove field excluded) field + (h' (p `pts_to_view` struct_view tag fields (remove field excluded)))) + == + h (p `pts_to_view` struct_view tag fields excluded) /\ + snd (extract_field tag fields (remove field excluded) field + (h' (p `pts_to_view` struct_view tag fields (remove field excluded)))) + == + h (q `pts_to_view` (fields.get_field field).view)) + +#restart-solver + +val dummy_def : unit + +val unaddr_of_struct_field_ref + (#tag: Type0) (#fields: c_fields) (#excluded: excluded_fields) + (field: field_of fields) + (p: ref 'a (struct_pcm tag fields)) + (q: ref 'a (fields.get_field field).pcm) +: Steel unit + ((p `pts_to_view` struct_view tag fields excluded) `star` + (pts_to_view u#0 + #'a + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).carrier)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).pcm)) + q + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view_type)) + #false + (norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view)))) + (fun _ -> p `pts_to_view` struct_view tag fields (remove field excluded)) + (requires fun _ -> + excluded field == true /\ + q == ref_focus p (struct_field tag fields field)) + (ensures fun h _ h' -> + excluded field == true /\ + fst + (extract_field tag fields (remove field excluded) field + (h' (p `pts_to_view` struct_view tag fields (remove field excluded)))) + == h (p `pts_to_view` struct_view tag fields excluded) /\ + snd + (extract_field tag fields (remove field excluded) field + (h' (p `pts_to_view` struct_view tag fields (remove field excluded)))) + == h (q `pts_to_view` (fields.get_field field).view)) + +open Steel.C.Reference + +val addr_of_struct_field'' + (return_view_type: Type0) + (return_carrier: Type0) + (tag: Type0) (fields: c_fields) (excluded: excluded_fields) + (field: field_of fields{ + return_view_type == (fields.get_field field).view_type /\ + return_carrier == (fields.get_field field).carrier}) + (p: ref 'a (struct tag fields) (struct_pcm tag fields)) +: Steel (ref 'a return_view_type #return_carrier (fields.get_field field).pcm) + (p `pts_to_view` struct_view tag fields excluded) + (fun q -> + (p `pts_to_view` struct_view tag fields (insert field excluded)) `star` + (pts_to_view u#0 + #'a + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view_type)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view_type)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).carrier)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).pcm)) + q + (norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view)))) + (requires fun _ -> not (excluded field)) + (ensures fun h q h' -> + not (excluded field) /\ + q == Steel.C.Model.Ref.ref_focus p (struct_field tag fields field) /\ + fst (extract_field tag fields excluded field + (h (p `pts_to_view` struct_view tag fields excluded))) + == + h' (p `pts_to_view` struct_view tag fields (insert field excluded)) + /\ + snd (extract_field tag fields excluded field + (h (p `pts_to_view` struct_view tag fields excluded))) == + h' (q `pts_to_view` (fields.get_field field).view)) +// = addr_of_struct_field_ref #'a #tag #fields #excluded field p + +(** Take the address of a field of a struct. + The above definitions are set up so that calls to addr_of_struct_field are erased to calls to addr_of_struct_field'' with + (fields.get_field field).view_type + and + (fields.get_field field).carrier + fully normalized. This allows the extracted OCaml code to be + ML-typable, avoiding Obj.magic and the insertion of spurious casts + in the extracted C. + + Calls to [norm] are used to compute the type of values pointed to + by the returned reference, and to ensure that the Steel tactic + will be able to unify vprops properly. *) +val addr_of_struct_field + (#tag: Type0) (#fields: c_fields) (#excluded: excluded_fields) + (field: field_of fields) + (p: ref 'a (struct tag fields) (struct_pcm tag fields)) +: Steel (ref 'a + (norm simplify_typedefs (fields.get_field field).view_type) + #(norm simplify_typedefs (fields.get_field field).carrier) + (fields.get_field field).pcm) + (p `pts_to_view` struct_view tag fields excluded) + (fun q -> + (p `pts_to_view` struct_view tag fields (insert field excluded)) `star` + (pts_to_view u#0 + #'a + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view_type)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view_type)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).carrier)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).pcm)) + q + (norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view)))) + (requires fun _ -> not (excluded field)) + (ensures fun h q h' -> + not (excluded field) /\ + q == Steel.C.Model.Ref.ref_focus p (struct_field tag fields field) /\ + fst (extract_field tag fields excluded field + (h (p `pts_to_view` struct_view tag fields excluded))) + == h' (p `pts_to_view` struct_view tag fields (insert field excluded)) + /\ + snd (extract_field tag fields excluded field + (h (p `pts_to_view` struct_view tag fields excluded))) + == h' (q `pts_to_view` (fields.get_field field).view)) + +(* += addr_of_struct_field'' + (normalize (fields.get_field field).view_type) + (normalize (fields.get_field field).carrier) + tag fields excluded field p +*) + +(** Inverse of unaddr_of_struct_field. *) +val unaddr_of_struct_field + (#tag: Type0) (#fields: c_fields) (#excluded: excluded_fields) + (field: field_of fields) + (p: ref 'a (struct tag fields) (struct_pcm tag fields)) + (q: ref 'a + (norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view_type)) + (fields.get_field field).pcm) +: Steel unit + ((p `pts_to_view` struct_view tag fields excluded) `star` + (pts_to_view u#0 + #'a + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view_type)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view_type)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).carrier)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).pcm)) + q + (norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view)))) + (fun _ -> p `pts_to_view` struct_view tag fields (remove field excluded)) + (requires fun _ -> + excluded field == true /\ + q == ref_focus p (struct_field tag fields field)) + (ensures fun h _ h' -> + excluded field == true /\ + fst (extract_field tag fields (remove field excluded) field + (h' (p `pts_to_view` struct_view tag fields (remove field excluded)))) + == h (p `pts_to_view` struct_view tag fields excluded) + /\ + snd (extract_field tag fields (remove field excluded) field + (h' (p `pts_to_view` struct_view tag fields (remove field excluded)))) == + h (q `pts_to_view` (fields.get_field field).view)) +// = +//let unaddr_of_struct_field #a #tag #fields #excluded field p q = + // unaddr_of_struct_field_ref' field p q + diff --git a/lib/steel/c/Steel.C.Typedef.fst b/lib/steel/c/Steel.C.Typedef.fst new file mode 100644 index 000000000..330a96941 --- /dev/null +++ b/lib/steel/c/Steel.C.Typedef.fst @@ -0,0 +1,29 @@ +module Steel.C.Typedef + +open FStar.List.Tot +open Steel.C.Model.PCM +open Steel.C.Model.Ref +open FStar.FunctionalExtensionality +open Steel.Effect + +irreducible let iter_unfold = 0 + +(** A typedef bundles together the various pieces of information needed to model a C data type in Steel. *) +[@@__reduce__] +noeq type typedef = { + (** The PCM used to model values of the corresponding C type. *) + carrier: Type0; + pcm: pcm carrier; + (** A way to view PCM carrier values as F* values that model the corresponding C values. *) + view_type: Type0; + view: sel_view pcm view_type false; + (** A way to decide whether a given element of the PCM is unit (needed to determine the case of a union) *) + is_unit: x:carrier -> b:bool{b <==> x == one pcm}; +} + +inline_for_extraction noextract +let view_type_of_typedef + (t: typedef) +: Tot Type0 += match t with + | { view_type = t'; } -> t' diff --git a/lib/steel/c/Steel.C.TypedefNorm.fst b/lib/steel/c/Steel.C.TypedefNorm.fst new file mode 100644 index 000000000..0d6f65deb --- /dev/null +++ b/lib/steel/c/Steel.C.TypedefNorm.fst @@ -0,0 +1,27 @@ +module Steel.C.TypedefNorm + +open Steel.C.StructLiteral +open Steel.C.UnionLiteral +open Steel.C.Fields +open Steel.C.Typedef +open Steel.C.Typenat +open Steel.C.Typestring + +unfold let norm_c_typedef = + [delta_only + [`%mk_c_union; + `%mk_c_struct; + `%c_fields_t; + `%List.Tot.fold_right; + `%Steel.C.Typestring.mk_string_t; + `%Steel.C.Typestring.string_t_of_chars; + `%Steel.C.Typestring.char_t_of_char; + `%Mkc_fields?.get_field; + `%Mkc_fields?.cfields; + `%Mktypedef?.view_type; + `%fields_cons; + `%fields_nil; + `%Steel.C.Typenat.nat_t_of_nat; + ]; + delta_attr [`%c_struct; `%c_typedef]; + iota; zeta; primops] diff --git a/lib/steel/c/Steel.C.Typenat.fst b/lib/steel/c/Steel.C.Typenat.fst new file mode 100644 index 000000000..7b0767074 --- /dev/null +++ b/lib/steel/c/Steel.C.Typenat.fst @@ -0,0 +1,4 @@ +module Steel.C.Typenat + +let z = unit +let s _ = unit diff --git a/lib/steel/c/Steel.C.Typenat.fsti b/lib/steel/c/Steel.C.Typenat.fsti new file mode 100644 index 000000000..17f28a1e0 --- /dev/null +++ b/lib/steel/c/Steel.C.Typenat.fsti @@ -0,0 +1,27 @@ +module Steel.C.Typenat + +(** Suppose [array (n : nat) (t : Type)] represents the type of array values. + Then, when extracting values of type [ref (array n t)], the length n is lost. + To make sure this information sticks around, this module provides + an encoding of natural numbers as types. *) + +val z: Type0 +val s: Type0 -> Type0 + +let rec nat_t_of_nat (n: nat): Type0 = + match n with + | 0 -> z + | n -> s (nat_t_of_nat (n - 1)) + +unfold +let norm_typenat = + [ + delta_only [ + `%nat_t_of_nat; + ]; + iota; zeta; primops; + ] + +let solve_nat_t_of_nat () = + FStar.Tactics.norm norm_typenat; + FStar.Tactics.trefl () diff --git a/lib/steel/c/Steel.C.Typestring.fst b/lib/steel/c/Steel.C.Typestring.fst new file mode 100644 index 000000000..3a5c93437 --- /dev/null +++ b/lib/steel/c/Steel.C.Typestring.fst @@ -0,0 +1,69 @@ +module Steel.C.Typestring + +let ca: Type0 = unit +let cb: Type0 = unit +let cc: Type0 = unit +let cd: Type0 = unit +let ce: Type0 = unit +let cf: Type0 = unit +let cg: Type0 = unit +let ch: Type0 = unit +let ci: Type0 = unit +let cj: Type0 = unit +let ck: Type0 = unit +let cl: Type0 = unit +let cm: Type0 = unit +let cn: Type0 = unit +let co: Type0 = unit +let cp: Type0 = unit +let cq: Type0 = unit +let cr: Type0 = unit +let cs: Type0 = unit +let ct: Type0 = unit +let cu: Type0 = unit +let cv: Type0 = unit +let cw: Type0 = unit +let cx: Type0 = unit +let cy: Type0 = unit +let cz: Type0 = unit +let cA: Type0 = unit +let cB: Type0 = unit +let cC: Type0 = unit +let cD: Type0 = unit +let cE: Type0 = unit +let cF: Type0 = unit +let cG: Type0 = unit +let cH: Type0 = unit +let cI: Type0 = unit +let cJ: Type0 = unit +let cK: Type0 = unit +let cL: Type0 = unit +let cM: Type0 = unit +let cN: Type0 = unit +let cO: Type0 = unit +let cP: Type0 = unit +let cQ: Type0 = unit +let cR: Type0 = unit +let cS: Type0 = unit +let cT: Type0 = unit +let cU: Type0 = unit +let cV: Type0 = unit +let cW: Type0 = unit +let cX: Type0 = unit +let cY: Type0 = unit +let cZ: Type0 = unit +let c0: Type0 = unit +let c1: Type0 = unit +let c2: Type0 = unit +let c3: Type0 = unit +let c4: Type0 = unit +let c5: Type0 = unit +let c6: Type0 = unit +let c7: Type0 = unit +let c8: Type0 = unit +let c9: Type0 = unit +let c_: Type0 = unit +let cdot: Type0 = unit + +let string_nil: Type0 = unit +let string_cons (c: Type0) (s: Type0): Type0 = unit diff --git a/lib/steel/c/Steel.C.Typestring.fsti b/lib/steel/c/Steel.C.Typestring.fsti new file mode 100644 index 000000000..9eae146ae --- /dev/null +++ b/lib/steel/c/Steel.C.Typestring.fsti @@ -0,0 +1,166 @@ +module Steel.C.Typestring + +(** Suppose [struct (t : string) (fields : struct_fields)] represents the type of struct values. + Then, when extracting values of type [ref (struct t fields)], the tag t is lost. + To make sure this information sticks around, this module provides + an encoding of strings like t as types. *) + +val ca: Type0 +val cb: Type0 +val cc: Type0 +val cd: Type0 +val ce: Type0 +val cf: Type0 +val cg: Type0 +val ch: Type0 +val ci: Type0 +val cj: Type0 +val ck: Type0 +val cl: Type0 +val cm: Type0 +val cn: Type0 +val co: Type0 +val cp: Type0 +val cq: Type0 +val cr: Type0 +val cs: Type0 +val ct: Type0 +val cu: Type0 +val cv: Type0 +val cw: Type0 +val cx: Type0 +val cy: Type0 +val cz: Type0 +val cA: Type0 +val cB: Type0 +val cC: Type0 +val cD: Type0 +val cE: Type0 +val cF: Type0 +val cG: Type0 +val cH: Type0 +val cI: Type0 +val cJ: Type0 +val cK: Type0 +val cL: Type0 +val cM: Type0 +val cN: Type0 +val cO: Type0 +val cP: Type0 +val cQ: Type0 +val cR: Type0 +val cS: Type0 +val cT: Type0 +val cU: Type0 +val cV: Type0 +val cW: Type0 +val cX: Type0 +val cY: Type0 +val cZ: Type0 +val c0: Type0 +val c1: Type0 +val c2: Type0 +val c3: Type0 +val c4: Type0 +val c5: Type0 +val c6: Type0 +val c7: Type0 +val c8: Type0 +val c9: Type0 +val c_: Type0 +val cdot: Type0 + +val string_nil: Type0 +val string_cons (c: Type0) (s: Type0): Type0 + +open FStar.String + +let char_t_of_char (c: char): Type0 = + match c with + | 'a' -> ca + | 'b' -> cb + | 'c' -> cc + | 'd' -> cd + | 'e' -> ce + | 'f' -> cf + | 'g' -> cg + | 'h' -> ch + | 'i' -> ci + | 'j' -> cj + | 'k' -> ck + | 'l' -> cl + | 'm' -> cm + | 'n' -> cn + | 'o' -> co + | 'p' -> cp + | 'q' -> cq + | 'r' -> cr + | 's' -> cs + | 't' -> ct + | 'u' -> cu + | 'v' -> cv + | 'w' -> cw + | 'x' -> cx + | 'y' -> cy + | 'z' -> cz + | 'A' -> cA + | 'B' -> cB + | 'C' -> cC + | 'D' -> cD + | 'E' -> cE + | 'F' -> cF + | 'G' -> cG + | 'H' -> cH + | 'I' -> cI + | 'J' -> cJ + | 'K' -> cK + | 'L' -> cL + | 'M' -> cM + | 'N' -> cN + | 'O' -> cO + | 'P' -> cP + | 'Q' -> cQ + | 'R' -> cR + | 'S' -> cS + | 'T' -> cT + | 'U' -> cU + | 'V' -> cV + | 'W' -> cW + | 'X' -> cX + | 'Y' -> cY + | 'Z' -> cZ + | '0' -> c0 + | '1' -> c1 + | '2' -> c2 + | '3' -> c3 + | '4' -> c4 + | '5' -> c5 + | '6' -> c6 + | '7' -> c7 + | '8' -> c8 + | '9' -> c9 + | '_' -> c_ + | '.' -> cdot + | _ -> c_ + +let rec string_t_of_chars (s: list char): Type0 = + match s with + | [] -> string_nil + | c :: s -> string_cons (char_t_of_char c) (string_t_of_chars s) + +let mk_string_t s: Type0 = string_t_of_chars (String.list_of_string s) + +unfold +let norm_typestring = + [ + delta_only [ + `%char_t_of_char; + `%string_t_of_chars; + `%mk_string_t; + ]; + iota; zeta; primops; + ] + +let solve_mk_string_t () = + FStar.Tactics.norm norm_typestring; + FStar.Tactics.trefl () diff --git a/lib/steel/c/Steel.C.UnionLiteral.fst b/lib/steel/c/Steel.C.UnionLiteral.fst new file mode 100644 index 000000000..afa9ef814 --- /dev/null +++ b/lib/steel/c/Steel.C.UnionLiteral.fst @@ -0,0 +1,308 @@ +module Steel.C.UnionLiteral + +open Steel.Memory +open Steel.Effect +open Steel.Effect.Common +open Steel.Effect.Atomic + +open Steel.C.Model.PCM +open Steel.C.Model.Union +open Steel.C.Typedef +open Steel.C.Model.Ref +open Steel.C.Model.Connection +open Steel.C.Opt +open Steel.C.Fields + +open FStar.List.Tot +open FStar.FunctionalExtensionality +open FStar.FSet + +module TS = Steel.C.Typestring + +let mk_union_def (tag: Type0) (field_descriptions: Type0): Type0 = unit + +let union (tag: Type0) (fields: c_fields): Type0 = + dtuple2 (field_of fields) (union_views fields) + +let mk_union (tag: Type0) (fields: c_fields) + (field: field_of fields) (x: (fields.get_field field).view_type) +: Pure (union tag fields) + (requires fields.has_field field == true) + (ensures fun _ -> True) += (|field, x|) + +let union_carriers (fields: c_fields) (field: field_of fields): Type0 = + (fields.get_field field).carrier + +let union_pcms (fields: c_fields) (field: field_of fields): pcm (union_carriers fields field) = + (fields.get_field field).pcm + +let union_pcm_carrier (tag: Type0) (fields: c_fields): Type0 = + Steel.C.Model.Union.union (union_pcms fields) + +let union_pcm (tag: Type0) (fields: c_fields): pcm (union_pcm_carrier tag fields) = + union_pcm (union_pcms fields) + +let is_units (fields: c_fields) (field: field_of fields) +: x:(fields.get_field field).carrier -> + b:bool{b <==> x == one (fields.get_field field).pcm} += (fields.get_field field).is_unit + +let rec case_of_union_aux (fields: c_fields) + (fields_list: list string) + (u: Steel.C.Model.Union.union (union_pcms fields)) +: Pure (option (field_of fields)) + (requires forall (field:string). field `mem` fields_list ==> fields.has_field field == true) + (ensures fun field -> + (None? field ==> (forall (field:field_t). field `mem` fields_list ==> u field == one (union_pcms fields field))) /\ + (Some? field ==> u (Some?.v field) =!= one (union_pcms fields (Some?.v field)))) + (decreases fields_list) += match fields_list with + | [] -> None + | field :: fields_list -> + match case_of_union_aux fields fields_list u with + | Some field -> Some field + | None -> + if field = "" then None else + if (fields.get_field field).is_unit (u field) then None else Some field + +let case_of_union (fields: nonempty_c_fields) + (u: Steel.C.Model.Union.union (union_pcms fields)) +: field:field_of fields{case_refinement_f (union_pcms fields) field u} += match case_of_union_aux fields fields.cfields u with + | None -> Some?.v fields.nonempty_witness + | Some field -> field + +let union_views' (fields: c_fields) (field: field_of fields) +: sel_view (union_pcms fields field) (union_views fields field) false += (fields.get_field field).view + +let union_view (tag: Type0) (fields: nonempty_c_fields) +: sel_view (union_pcm tag fields) (union tag fields) false += Steel.C.Model.Union.union_view (union_views' fields) (case_of_union fields) + +let dtuple2_of_union (#tag: Type0) (#fields: c_fields) (x: union tag fields) +: dtuple2 (field_of fields) (union_views fields) += x + +let union_of_dtuple2 (#tag: Type0) (#fields: c_fields) + (x: dtuple2 (field_of fields) (union_views fields)) +: union tag fields += x + +let dtuple2_of_union_of_dtuple2 + (#tag: Type0) (#fields: c_fields) + (x: dtuple2 (field_of fields) (union_views fields)) +: Lemma (dtuple2_of_union (union_of_dtuple2 #tag #fields x) == x) += () + +let union_of_dtuple2_of_union + (#tag: Type0) (#fields: c_fields) + (x: union tag fields) +: Lemma (union_of_dtuple2 (dtuple2_of_union #tag #fields x) == x) += () + +let union_field + (tag: Type0) (fields: c_fields) (field: field_of fields) +: connection (union_pcm tag fields) (fields.get_field field).pcm += union_field (union_pcms fields) field + +let rec union_is_unit_aux (tag: Type0) (fields: c_fields) + (fields_list: list string) + (v: union_pcm_carrier tag fields) +: Pure bool + (requires forall field. field `mem` fields_list ==> fields.has_field field == true) + (ensures fun b -> b <==> (forall (field: field_t). field `mem` fields_list ==> v field == one (union_pcm tag fields) field)) + (decreases fields_list) += match fields_list with + | [] -> true + | field :: fields_list -> + if field = "" then union_is_unit_aux tag fields fields_list v else + (fields.get_field field).is_unit (v field) && + union_is_unit_aux tag fields fields_list v + +let union_is_unit tag fields v +: b:bool{b <==> v == one (union_pcm tag fields)} += let b = union_is_unit_aux tag fields fields.cfields v in + assert (b <==> v `feq` one (union_pcm tag fields)); + b + +open Steel.C.Reference + +#push-options "--z3rlimit 64" +#restart-solver +let addr_of_union_field' + (#tag: Type0) (#fields: c_fields) + (field: field_of fields) + (p: ref 'a (union tag fields) (union_pcm tag fields)) +: Steel (ref 'a + (fields.get_field field).view_type + (fields.get_field field).pcm) + (p `pts_to_view` union_view tag fields) + (fun q -> + pts_to_view u#0 + #'a + #(fields.get_field field).view_type + #(fields.get_field field).view_type + #(fields.get_field field).carrier + #(fields.get_field field).pcm + q + (fields.get_field field).view) + (requires fun h -> + dfst (dtuple2_of_union #tag #fields (h (p `pts_to_view` union_view tag fields))) == field) + (ensures fun h q h' -> + q == ref_focus p (union_field tag fields field) /\ + dtuple2_of_union #tag #fields (h (p `pts_to_view` union_view tag fields)) + == (|field, h' (q `pts_to_view` (fields.get_field field).view)|)) += let v: Ghost.erased (union tag fields) = + gget (p `pts_to_view` union_view tag fields) + in + let s: Ghost.erased (union_pcm_carrier tag fields) = + pts_to_view_elim p (union_view tag fields) + in + // assert (Ghost.reveal s == (union_view tag fields).to_carrier v); + let q = Steel.C.Model.Union.addr_of_union_field #'a #_ #_ #(union_pcms fields) p field s in +// change_equal_slprop (q `pts_to` _) (q `pts_to` _); + pts_to_view_intro q (Ghost.reveal s field) + (fields.get_field field).view + (dsnd (Ghost.reveal v)); +// assert (Ghost.reveal v == (|field, dsnd (Ghost.reveal v)|)); + return q +#pop-options + +let addr_of_union_field'' #a return_view_type return_carrier tag fields field p = + addr_of_union_field' #a #tag #fields field p + +let unaddr_of_union_field' + (#tag: Type0) (#fields: c_fields) + (field: field_of fields) + (p: ref 'a (union tag fields) (union_pcm tag fields)) + (q: ref 'a (fields.get_field field).view_type (fields.get_field field).pcm) +: Steel unit + (pts_to_view u#0 + #'a + #(fields.get_field field).view_type + #(fields.get_field field).view_type + #(fields.get_field field).carrier + #(fields.get_field field).pcm + q + (fields.get_field field).view) + (fun q -> p `pts_to_view` union_view tag fields) + (requires fun _ -> q == ref_focus p (union_field tag fields field)) + (ensures fun h _ h' -> + dtuple2_of_union #tag #fields (h' (p `pts_to_view` union_view tag fields)) + == (|field, h (q `pts_to_view` (fields.get_field field).view)|)) += let v: Ghost.erased (fields.get_field field).view_type = + gget (q `pts_to_view` (fields.get_field field).view) + in + let s: Ghost.erased (fields.get_field field).carrier = + pts_to_view_elim q (fields.get_field field).view + in + Steel.C.Model.Union.unaddr_of_union_field #_ #_ #_ #_ #(union_pcms fields) field q p s; + pts_to_view_intro p + (field_to_union_f (union_pcms fields) field s) + (union_view tag fields) + (|field, Ghost.reveal v|); + return () + +let unaddr_of_union_field #a #tag #fields field p q = + unaddr_of_union_field' #a #tag #fields field p q + +#restart-solver +#push-options "--z3rlimit 64" +let exclusive_refine_union_field + (tag: Type0) (fields: c_fields) + (old_field new_field: field_of fields) + (old_value: (fields.get_field old_field).view_type) + (new_value: (fields.get_field new_field).view_type) +: Lemma + (requires + exclusive (fields.get_field old_field).pcm ((fields.get_field old_field).view.to_carrier old_value) /\ + p_refine (fields.get_field new_field).pcm ((fields.get_field new_field).view.to_carrier new_value)) + (ensures + exclusive (union_pcm tag fields) ((union_view tag fields).to_carrier (|old_field, old_value|)) /\ + p_refine (union_pcm tag fields) ((union_view tag fields).to_carrier (|new_field, new_value|))) += assert ( + one (fields.get_field old_field).pcm =!= + (fields.get_field old_field).view.to_carrier old_value); + let aux frame + : Lemma + (requires + Steel.C.Model.PCM.composable + (union_pcm tag fields) + ((union_view tag fields).to_carrier (|old_field, old_value|)) + frame) + (ensures frame == one (union_pcm tag fields)) + = assert (frame old_field == one (fields.get_field old_field).pcm); + assert (frame `feq` one (union_pcm tag fields)) + in + FStar.Classical.(forall_intro (move_requires aux)) +#pop-options + +let switch_union_field'' + (#tag: Type0) (#fields: c_fields) + (field: field_of fields) (new_value: (fields.get_field field).view_type) + (p: ref 'a (union tag fields) (union_pcm tag fields)) +: Steel unit + (p `pts_to_view` union_view tag fields) + (fun _ -> p `pts_to_view` union_view tag fields) + (requires fun h -> + let (|old_field, v|) = + dtuple2_of_union #tag #fields (h (p `pts_to_view` union_view tag fields)) + in + exclusive (fields.get_field old_field).pcm ((fields.get_field old_field).view.to_carrier v) /\ + p_refine (fields.get_field field).pcm ((fields.get_field field).view.to_carrier new_value) + ) + (ensures fun _ _ h' -> + dtuple2_of_union #tag #fields (h' (p `pts_to_view` union_view tag fields)) + == (|field, new_value|)) += let v: Ghost.erased (union tag fields) = + gget (p `pts_to_view` union_view tag fields) + in + let s: Ghost.erased (union_pcm_carrier tag fields) = + pts_to_view_elim p (union_view tag fields) + in + let s': Ghost.erased (union_pcm_carrier tag fields) = + Ghost.hide ((union_view tag fields).to_carrier (Ghost.reveal v)) + in + assert (Ghost.reveal s == Ghost.reveal s'); + let old_field: Ghost.erased (field_of fields) = + Ghost.hide (dfst (Ghost.reveal v)) + in + let old_value: Ghost.erased (fields.get_field old_field).view_type = + Ghost.hide (dsnd (Ghost.reveal v)) + in + let new_s: union_pcm_carrier tag fields = + (union_view tag fields).to_carrier (|field, new_value|) + in + exclusive_refine_union_field tag fields old_field field old_value new_value; + assert (exclusive (union_pcm tag fields) s); + assert (p_refine (union_pcm tag fields) new_s); + let upd: frame_preserving_upd (union_pcm tag fields) s new_s = + base_fpu (union_pcm tag fields) s new_s + in + Steel.C.Model.Ref.ref_upd p s new_s upd; + pts_to_view_intro p new_s + (union_view tag fields) + (|field, new_value|); + return () + +let switch_union_field' + (new_value_ty: Type0) (tag: Type0) (fields: c_fields) + (field: field_of fields{new_value_ty == (fields.get_field field).view_type}) + (new_value: new_value_ty) + (p: ref 'a (union tag fields) (union_pcm tag fields)) +: Steel unit + (p `pts_to_view` union_view tag fields) + (fun _ -> p `pts_to_view` union_view tag fields) + (requires fun h -> + let (|old_field, v|) = + dtuple2_of_union #tag #fields (h (p `pts_to_view` union_view tag fields)) + in + exclusive (fields.get_field old_field).pcm ((fields.get_field old_field).view.to_carrier v) /\ + p_refine (fields.get_field field).pcm ((fields.get_field field).view.to_carrier new_value) + ) + (ensures fun _ _ h' -> + dtuple2_of_union #tag #fields (h' (p `pts_to_view` union_view tag fields)) + == (|field, new_value|)) += switch_union_field'' #'a #tag #fields field new_value p diff --git a/lib/steel/c/Steel.C.UnionLiteral.fsti b/lib/steel/c/Steel.C.UnionLiteral.fsti new file mode 100644 index 000000000..a2ad75eef --- /dev/null +++ b/lib/steel/c/Steel.C.UnionLiteral.fsti @@ -0,0 +1,214 @@ +module Steel.C.UnionLiteral + +open Steel.Memory +open Steel.Effect +open Steel.Effect.Common +open Steel.Effect.Atomic + +open Steel.C.Model.PCM +open Steel.C.Model.Union +open Steel.C.Typedef +open Steel.C.Model.Ref +open Steel.C.Model.Connection +open Steel.C.Opt +open Steel.C.Fields + +open FStar.List.Tot +open FStar.FunctionalExtensionality +open FStar.FSet + +module TS = Steel.C.Typestring + +val mk_union_def (tag: Type0) (field_descriptions: Type0): Type0 + +(** To declare a struct definition, one needs to write + let _ = norm norm_c_typedef (mk_c_struct ). + See Steel.C.StructLiteral.mk_c_struct for more details. *) +let mk_c_union (tag: Type0) (fields: c_fields) = + mk_union_def tag (c_fields_t fields) + +let union_views (fields: c_fields) (field: field_of fields): Type0 = + (fields.get_field field).view_type + +(** A [union tag fields] is a view type for C union with tag [tag] and fields [fields]. *) +val union (tag: Type0) (fields: c_fields): Type0 + +(** [mk_union tag fields field x] represents the union literal (union tag) {.field = x} *) +val mk_union (tag: Type0) (fields: c_fields) + (field: field_of fields) (x: (fields.get_field field).view_type) +: Pure (union tag fields) + (requires fields.has_field field == true) + (ensures fun _ -> True) + +(** The carrier of the PCM representing unions with tag [tag] and fields [fields]. *) +val union_pcm_carrier (tag: Type0) (fields: c_fields): Type0 + +(** The PCM representing unions with tag [tag] and fields [fields]. *) +val union_pcm (tag: Type0) (fields: c_fields): pcm (union_pcm_carrier tag fields) + +let nonempty_c_fields = fields:c_fields{Some? fields.nonempty_witness} + +val union_view (tag: Type0) (fields: nonempty_c_fields) +: sel_view (union_pcm tag fields) (union tag fields) false + +(** For now we expose an isomorphism between values of type [union tag fields] and dependent tuples. + This allows a particularly dedicated Steel programmer to bypass the nominality of C unions, because + it reveals the fact that the tag name is completely unused. In the future we should essentially expose + (dfst . dtuple2_of_union) and (dsnd . dtuple2_of_union) and the right laws, but not the whole isomorphism. *) + +val dtuple2_of_union (#tag: Type0) (#fields: c_fields) (x: union tag fields) +: dtuple2 (field_of fields) (union_views fields) + +val union_of_dtuple2 (#tag: Type0) (#fields: c_fields) + (x: dtuple2 (field_of fields) (union_views fields)) +: union tag fields + +val dtuple2_of_union_of_dtuple2 + (#tag: Type0) (#fields: c_fields) + (x: dtuple2 (field_of fields) (union_views fields)) +: Lemma (dtuple2_of_union (union_of_dtuple2 #tag #fields x) == x) + +val union_of_dtuple2_of_union + (#tag: Type0) (#fields: c_fields) + (x: union tag fields) +: Lemma (union_of_dtuple2 (dtuple2_of_union #tag #fields x) == x) + +(** A connection from a union to any of its fields *) +val union_field + (tag: Type0) (fields: c_fields) (field: field_of fields) +: connection (union_pcm tag fields) (fields.get_field field).pcm + +val union_is_unit (tag:Type0) (fields:c_fields) (v:union_pcm_carrier tag fields) +: b:bool{b <==> v == one (union_pcm tag fields)} + +[@@c_typedef] +let typedef_union (tag: Type0) (fields: nonempty_c_fields): typedef = { + carrier = union_pcm_carrier tag fields; + pcm = union_pcm tag fields; + view_type = union tag fields; + view = union_view tag fields; + is_unit = union_is_unit tag fields; +} + +open Steel.C.Reference + +val addr_of_union_field'' + (return_view_type: Type0) + (return_carrier: Type0) + (tag: Type0) (fields: c_fields) + (field: field_of fields{ + return_view_type == (fields.get_field field).view_type /\ + return_carrier == (fields.get_field field).carrier}) + (p: ref 'a (union tag fields) (union_pcm tag fields)) +: Steel (ref 'a return_view_type #return_carrier (fields.get_field field).pcm) + (p `pts_to_view` union_view tag fields) + (fun q -> + pts_to_view u#0 + #'a + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view_type)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view_type)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).carrier)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).pcm)) + q + (norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view))) + (requires fun h -> + dfst (dtuple2_of_union #tag #fields (h (p `pts_to_view` union_view tag fields))) == field) + (ensures fun h q h' -> + q == ref_focus p (union_field tag fields field) /\ + dtuple2_of_union #tag #fields (h (p `pts_to_view` union_view tag fields)) + == (|field, h' (q `pts_to_view` (fields.get_field field).view)|)) + +(** Take the address of a field of a union. + The definitions and normalization strategies are identical to + those described in Steel.C.StructLiteral.addr_of_struct_field. *) +inline_for_extraction noextract +let addr_of_union_field + (#tag: Type0) (#fields: c_fields) + (field: field_of fields) + (p: ref 'a (union tag fields) (union_pcm tag fields)) +: Steel (ref 'a + (norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view_type)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).carrier)) + (fields.get_field field).pcm) + (p `pts_to_view` union_view tag fields) + (fun q -> + pts_to_view u#0 + #'a + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view_type)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view_type)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).carrier)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).pcm)) + q + (norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view))) + (requires fun h -> + dfst (dtuple2_of_union #tag #fields (h (p `pts_to_view` union_view tag fields))) == field) + (ensures fun h q h' -> + q == ref_focus p (union_field tag fields field) /\ + dtuple2_of_union #tag #fields (h (p `pts_to_view` union_view tag fields)) + == (|field, h' (q `pts_to_view` (fields.get_field field).view)|)) += addr_of_union_field'' + (normalize (fields.get_field field).view_type) + (normalize (fields.get_field field).carrier) + tag fields field p + +(** Inverse of addr_of_union_field. *) +val unaddr_of_union_field + (#tag: Type0) (#fields: c_fields) + (field: field_of fields) + (p: ref 'a (union tag fields) (union_pcm tag fields)) + (q: ref 'a (fields.get_field field).view_type (fields.get_field field).pcm) +: Steel unit + (pts_to_view u#0 + #'a + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view_type)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view_type)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).carrier)) + #(norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).pcm)) + q + (norm simplify_typedefs (norm unfold_typedefs (fields.get_field field).view))) + (fun q -> p `pts_to_view` union_view tag fields) + (requires fun _ -> q == ref_focus p (union_field tag fields field)) + (ensures fun h _ h' -> + dtuple2_of_union #tag #fields (h' (p `pts_to_view` union_view tag fields)) + == (|field, h (q `pts_to_view` (fields.get_field field).view)|)) + +val switch_union_field' + (new_value_ty: Type0) (tag: Type0) (fields: c_fields) + (field: field_of fields{new_value_ty == (fields.get_field field).view_type}) + (new_value: new_value_ty) + (p: ref 'a (union tag fields) (union_pcm tag fields)) +: Steel unit + (p `pts_to_view` union_view tag fields) + (fun _ -> p `pts_to_view` union_view tag fields) + (requires fun h -> + let (|old_field, v|) = + dtuple2_of_union #tag #fields (h (p `pts_to_view` union_view tag fields)) + in + exclusive (fields.get_field old_field).pcm ((fields.get_field old_field).view.to_carrier v) /\ + p_refine (fields.get_field field).pcm ((fields.get_field field).view.to_carrier new_value) + ) + (ensures fun _ _ h' -> + dtuple2_of_union #tag #fields (h' (p `pts_to_view` union_view tag fields)) + == (|field, new_value|)) + +(** Switch the case of a union to field [field] by writing [new_value] to it. *) +noextract inline_for_extraction +let switch_union_field + (#tag: Type0) (#fields: c_fields) + (field: field_of fields) (new_value: (fields.get_field field).view_type) + (p: ref 'a (union tag fields) (union_pcm tag fields)) + // TODO it would be nice permute the arguments so that their order matches the C code p->field = new_value +: Steel unit + (p `pts_to_view` union_view tag fields) + (fun _ -> p `pts_to_view` union_view tag fields) + (requires fun h -> + let (|old_field, v|) = + dtuple2_of_union #tag #fields (h (p `pts_to_view` union_view tag fields)) + in + exclusive (fields.get_field old_field).pcm ((fields.get_field old_field).view.to_carrier v) /\ + p_refine (fields.get_field field).pcm ((fields.get_field field).view.to_carrier new_value) + ) + (ensures fun _ _ h' -> + dtuple2_of_union #tag #fields (h' (p `pts_to_view` union_view tag fields)) + == (|field, new_value|)) += switch_union_field' (normalize (fields.get_field field).view_type) tag fields field new_value p diff --git a/lib/steel/c/Steel.ST.C.Model.Array.fst b/lib/steel/c/Steel.ST.C.Model.Array.fst new file mode 100644 index 000000000..d4a0432b1 --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Model.Array.fst @@ -0,0 +1,1043 @@ +module Steel.ST.C.Model.Array +open Steel.ST.Util + +open Steel.C.Model.PCM +open Steel.C.Model.Connection +open Steel.ST.C.Model.Ref +open Steel.ST.C.Model.Struct +module R = Steel.ST.C.Model.Ref +module SZ = FStar.SizeT + +(* Base array type *) + +let size_t = SZ.t +let size_v = SZ.v + +let array_domain + (n: Ghost.erased size_t) +: Tot Type0 += (x: size_t { size_v x < size_v n }) + +let array_range + (t: Type u#0) + (n: Ghost.erased size_t) + (x: array_domain n) +: Tot Type0 += t + +open FStar.FunctionalExtensionality + +let array_pcm_carrier + (t: Type) + (n: Ghost.erased size_t) +: Tot Type += restricted_t (array_domain n) (array_range t n) + +let array_pcm_carrier_ext + (t: Type) + (n: size_t) + (x1 x2: array_pcm_carrier t n) + (f: ( + (i: array_domain n) -> + Lemma + (x1 i == x2 i) + )) +: Lemma + (ensures (x1 == x2)) += Classical.forall_intro f; + assert (x1 `feq` x2) + +let array_elements_pcm + (#t: Type u#0) + (p: pcm t) + (n: Ghost.erased size_t) + (x: array_domain n) +: Tot (Steel.C.Model.PCM.pcm (array_range t n x)) += p + +let array_pcm + (#t: Type u#0) + (p: pcm t) + (n: Ghost.erased size_t) +: Tot (pcm (array_pcm_carrier t n)) += prod_pcm (array_elements_pcm p n) + +noeq +type array + (base_t: Type) + (#t: Type) + (p: pcm t) += { + base_len: Ghost.erased size_t; + base: R.ref base_t (array_pcm p base_len); + offset: size_t; + len: Ghost.erased size_t; + prf: squash (size_v offset + size_v len <= size_v base_len); + } + +let length + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (a: array base_t p) +: GTot nat += size_v a.len + +let adjacent + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (a1 a2: array base_t p) +: Tot prop += a1.base_len == a2.base_len /\ + a1.base == a2.base /\ + size_v a1.offset + size_v a1.len == size_v a2.offset + +let size_add = SZ.add + +let merge + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (a1 a2: array base_t p) +: Pure (array base_t p) + (requires (adjacent a1 a2)) + (ensures (fun y -> length y == length a1 + length a2)) += { + base_len = a1.base_len; + base = a1.base; + offset = a1.offset; + len = size_add a1.len a2.len; + prf = (); + } + +let size_le = SZ.lte +let size_lt = SZ.lt +let size_sub = SZ.sub + +let large_to_small_index + (large_len: size_t) + (offset: size_t) + (small_len: size_t) + (sq: squash (size_v offset + size_v small_len <= size_v large_len)) + (x: array_domain large_len) +: Tot (option (array_domain small_len)) += if if offset `size_le` x then x `size_lt` (offset `size_add` small_len) else false + then Some (x `size_sub` offset) + else None + +let small_to_large_index + (large_len: size_t) + (offset: size_t) + (small_len: size_t) + (sq: squash (size_v offset + size_v small_len <= size_v large_len)) + (x: array_domain small_len) +: Tot (array_domain large_len) += offset `size_add` x + +let ref_of_array_conn + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) +: GTot (connection (array_pcm p r.base_len) (array_pcm p r.len)) += substruct (array_elements_pcm p r.base_len) (array_elements_pcm p r.len) (small_to_large_index r.base_len r.offset r.len ()) (large_to_small_index r.base_len r.offset r.len ()) () + +let ref_of_array + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) +: GTot (R.ref base_t (array_pcm p r.len)) += R.ref_focus r.base (ref_of_array_conn r) + +let ref_of_array_id + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) +: Lemma + (requires ( + size_v r.offset == 0 /\ + r.len == r.base_len + )) + (ensures ( + ref_of_array r == r.base + )) += substruct_id (array_elements_pcm p r.len) (small_to_large_index r.base_len r.offset r.len ()) (large_to_small_index r.base_len r.offset r.len ()) (); + R.ref_focus_id r.base + +let array_pcm_carrier_of_seq + (#t: Type) + (n: Ghost.erased size_t) + (s: Seq.lseq t (size_v n)) +: Tot (array_pcm_carrier t n) += on_dom (array_domain n) (fun i -> Seq.index s (size_v i) <: array_range t n i) + +let array_pcm_carrier_of_seq_eq + (#t: Type) + (n: Ghost.erased size_t) + (s: Seq.lseq t (size_v n)) + (i: array_domain n) +: Lemma + (array_pcm_carrier_of_seq n s i == Seq.index s (SZ.v i)) + [SMTPat (array_pcm_carrier_of_seq n s i)] += () + +let int_to_size_t = SZ.uint_to_t + +let seq_of_array_pcm_carrier + (#t: Type) + (#n: Ghost.erased size_t) + (x: array_pcm_carrier t n) +: GTot (Seq.lseq t (size_v n)) += Seq.init (size_v n) (fun i -> x (int_to_size_t i) <: t) + +let array_pcm_carrier_of_seq_of_array_pcm_carrier + (#t: Type) + (#n: size_t) + (x: array_pcm_carrier t n) +: Lemma + (array_pcm_carrier_of_seq n (seq_of_array_pcm_carrier x) `feq` x) += () + +let seq_of_array_pcm_carrier_of_seq + (#t: Type) + (n: Ghost.erased size_t) + (s: Seq.lseq t (size_v n)) +: Lemma + (seq_of_array_pcm_carrier (array_pcm_carrier_of_seq n s) `Seq.equal` s) += () + +let pts_to0 + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) + (x: Seq.seq t) +: Tot vprop += if Seq.length x = size_v r.len + then R.pts_to (ref_of_array r) (array_pcm_carrier_of_seq r.len x) + else pure False + +let trivial_selector + (hp: Steel.Memory.slprop u#1) +: Tot (selector unit hp) += fun _ -> () + +[@@__steel_reduce__] +let pts_to + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) + (x: Seq.seq t) +: Tot vprop += VUnit ({ + hp = hp_of (pts_to0 r x); + t = unit; + sel = trivial_selector _; + }) + +let intro_pts_to' + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) + (x: Seq.lseq t (size_v r.len)) +: STGhostT unit opened + (R.pts_to (ref_of_array r) (array_pcm_carrier_of_seq r.len x)) + (fun _ -> pts_to r x) += weaken + (R.pts_to (ref_of_array r) (array_pcm_carrier_of_seq r.len x)) + (pts_to r x) + (fun _ -> ()) + +let intro_pts_to + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) + (s: array_pcm_carrier t r.len) +: STGhostT unit opened + (R.pts_to (ref_of_array r) s) + (fun _ -> pts_to r (seq_of_array_pcm_carrier s)) += array_pcm_carrier_of_seq_of_array_pcm_carrier s; + rewrite (R.pts_to _ _) (R.pts_to _ _); + intro_pts_to' r (seq_of_array_pcm_carrier s) + +let intro_pts_to0 + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) + (s: array_pcm_carrier t r.len) + (s': Seq.seq t) +: STGhost unit opened + (R.pts_to (ref_of_array r) s) + (fun _ -> pts_to r s') + (seq_of_array_pcm_carrier s `Seq.equal` s') + (fun _ -> True) += intro_pts_to r s; + rewrite (pts_to r (seq_of_array_pcm_carrier s)) (pts_to r s') + +let intro_pts_to1 + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) + (r0: R.ref base_t (array_pcm p r.len)) + (s: array_pcm_carrier t r.len) + (s': Seq.seq t) +: STGhost unit opened + (R.pts_to r0 s) + (fun _ -> pts_to r s') + ( + r0 == ref_of_array r /\ + seq_of_array_pcm_carrier s `Seq.equal` s' + ) + (fun _ -> True) += rewrite (R.pts_to r0 s) (R.pts_to (ref_of_array r) s); + intro_pts_to0 r s s' + +let intro_pts_to2 + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) + (#base_t0: Type) + (#t0: Type) + (#p0: pcm t0) + (r0: R.ref base_t0 p0) + (s: t0) + (s': Seq.seq t) +: STGhost unit opened + (R.pts_to r0 s) + (fun _ -> pts_to r s') + ( + base_t0 == base_t /\ + t0 == array_pcm_carrier t r.len /\ + p0 == array_pcm p r.len /\ + r0 == ref_of_array r /\ + seq_of_array_pcm_carrier (s <: array_pcm_carrier t r.len) `Seq.equal` s' + ) + (fun _ -> True) += rewrite + (R.pts_to r0 s) + (R.pts_to (r0 <: R.ref base_t (array_pcm p r.len)) s); + intro_pts_to1 r r0 s s' + +let elim_pts_to + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) + (x: Seq.seq t) +: STGhostT (squash (Seq.length x == size_v r.len)) opened + (pts_to r x) + (fun _ -> R.pts_to (ref_of_array r) (array_pcm_carrier_of_seq r.len (x <: Seq.lseq t (size_v r.len)))) += if Seq.length x = size_v r.len + then begin + let sq : squash (Seq.length x == size_v r.len) = () in + weaken + (pts_to r x) + (R.pts_to (ref_of_array r) (array_pcm_carrier_of_seq r.len (x <: Seq.lseq t (size_v r.len)))) + (fun _ -> ()); + let sq : squash (Seq.length x == size_v r.len) = () in + noop (); + sq + end else begin + weaken + (pts_to r x) + (pure False) + (fun _ -> ()); + elim_pure False; + assert False; + weaken + (pure False) + (R.pts_to (ref_of_array r) (array_pcm_carrier_of_seq r.len x)) + (fun _ -> ()); + let sq : squash (Seq.length x == size_v r.len) = () in + noop (); + sq + end + +let pts_to_length + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) + (x: Seq.seq t) +: STGhostT (squash (Seq.length x == size_v r.len)) opened + (pts_to r x) + (fun _ -> pts_to r x) += + let _ = elim_pts_to r _ in + intro_pts_to0 r _ x + +let cell + (#t: Type) + (p: pcm t) + (len: Ghost.erased size_t) + (i: size_t) +: Pure (connection (array_pcm p len) p) + (requires (size_v i < size_v len)) + (ensures (fun _ -> True)) += struct_field (array_elements_pcm p len) i + +let ref_of_array_eq + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) + (i: size_t) +: Lemma + (requires (size_v i < size_v r.len)) + (ensures ( + R.ref_focus (ref_of_array r) (cell p r.len i) == + R.ref_focus r.base (cell p r.base_len (r.offset `size_add` i)) + )) += ref_focus_comp r.base (ref_of_array_conn r) (cell p r.len i); + substruct_field (array_elements_pcm p r.base_len) (array_elements_pcm p r.len) (small_to_large_index r.base_len r.offset r.len ()) (large_to_small_index r.base_len r.offset r.len ()) () i + +let g_focus_cell + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) + (s: Seq.seq t) + (i: size_t) + (sq: squash (size_v i < size_v r.len \/ size_v i < Seq.length s)) +: STGhostT (squash (size_v i < size_v r.len /\ size_v r.len == Seq.length s)) opened + (pts_to r s) + (fun _ -> pts_to r (Seq.upd s (size_v i) (one p)) `star` R.pts_to (ref_focus (ref_of_array r) (cell p r.len i)) (Seq.index s (size_v i))) += let _ = elim_pts_to r _ in + g_addr_of_struct_field (ref_of_array r) i _; + intro_pts_to0 r _ (Seq.upd s (size_v i) (one p)); + rewrite (R.pts_to (ref_focus _ _) _) (R.pts_to (ref_focus _ _) _) + +#push-options "--z3rlimit 16" + +let pts_to_elim_to_base + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) + (x: Seq.seq t) +: STGhost (Ghost.erased (array_pcm_carrier t r.base_len)) opened + (pts_to r x) + (fun y -> R.pts_to r.base y) + (True) + (fun y -> + Seq.length x == size_v r.len /\ + Ghost.reveal y == (ref_of_array_conn r).conn_small_to_large.morph (array_pcm_carrier_of_seq r.len x) /\ + Ghost.reveal y == substruct_to_struct_f (array_elements_pcm p r.base_len) (array_elements_pcm p r.len) (small_to_large_index r.base_len r.offset r.len ()) (large_to_small_index r.base_len r.offset r.len ()) () (array_pcm_carrier_of_seq r.len x) + ) += let _ = elim_pts_to r _ in + unfocus (ref_of_array r) r.base (ref_of_array_conn r) _; + let y = Ghost.hide ((ref_of_array_conn r).conn_small_to_large.morph (array_pcm_carrier_of_seq r.len x)) in + rewrite (R.pts_to _ _) (R.pts_to _ _); + y + +#pop-options + +let pts_to_intro_from_base + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) + (y: array_pcm_carrier t r.base_len) + (x: Seq.seq t) +: STGhost unit opened + (R.pts_to r.base y) + (fun _ -> pts_to r x) + ( + Seq.length x == size_v r.len /\ + y `feq` substruct_to_struct_f (array_elements_pcm p r.base_len) (array_elements_pcm p r.len) (small_to_large_index r.base_len r.offset r.len ()) (large_to_small_index r.base_len r.offset r.len ()) () (array_pcm_carrier_of_seq r.len x) + ) + (fun _ -> True) += gfocus r.base (ref_of_array_conn r) _ (array_pcm_carrier_of_seq r.len x); + rewrite (R.pts_to _ _) (R.pts_to (ref_of_array r) (array_pcm_carrier_of_seq r.len x)); + intro_pts_to0 r _ x + +#push-options "--z3rlimit 16 --split_queries" + +#restart-solver +let focus_cell + (#base_t: Type) + (#t: Type) + (#opened: _) + (#p: pcm t) + (r: array base_t p) + (s: Ghost.erased (Seq.seq t)) + (i: size_t) + (sq: squash (size_v i < size_v r.len \/ size_v i < Seq.length s)) +: STAtomicBase (_: ref base_t p { (size_v i < size_v r.len /\ size_v r.len == Seq.length s) }) false opened Unobservable + (pts_to r s) + (fun r' -> pts_to r (Seq.upd s (size_v i) (one p)) `star` R.pts_to r' (Seq.index s (size_v i))) + (True) + (fun r' -> + r' == ref_focus (ref_of_array r) (cell p r.len i) + ) += let y = pts_to_elim_to_base r _ in + ref_of_array_eq r i; + let r' = addr_of_struct_field r.base (r.offset `size_add` i) _ in + pts_to_intro_from_base r _ _; + rewrite (R.pts_to r' _) (R.pts_to r' _); + return r' + +#pop-options + +let unfocus_cell + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) + (s: Seq.seq t) + (i: size_t) + (r': R.ref base_t p) + (v: t) + (sq: squash (size_v i < size_v r.len /\ size_v i < Seq.length s)) +: STGhost unit opened + (pts_to r s `star` R.pts_to r' v) + (fun _ -> pts_to r (Seq.upd s (size_v i) v)) + ( + r' == ref_focus (ref_of_array r) (cell p r.len i) /\ + Seq.index s (size_v i) == one p + ) + (fun _ -> True) += let _ = elim_pts_to r _ in + unaddr_of_struct_field #_ #_ #_ #_ #(array_elements_pcm p r.len) i r' (ref_of_array r) _ _; + intro_pts_to0 r _ (Seq.upd s (size_v i) v) + +let share + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) + (s s1 s2: Seq.seq t) +: STGhost unit opened + (pts_to r s) + (fun _ -> pts_to r s1 `star` pts_to r s2) + ( + Seq.length s1 == Seq.length s /\ + Seq.length s2 == Seq.length s /\ + (forall (i: nat) . + i < Seq.length s ==> ( + composable p (Seq.index s1 i) (Seq.index s2 i) /\ + op p (Seq.index s1 i) (Seq.index s2 i) == Seq.index s i + )) + ) + (fun _ -> True) += let _ = elim_pts_to r _ in + let a1 = array_pcm_carrier_of_seq r.len s1 in + let a2 = array_pcm_carrier_of_seq r.len s2 in + assert ( + composable (array_pcm p r.len) a1 a2 /\ + op (array_pcm p r.len) a1 a2 `feq` array_pcm_carrier_of_seq r.len s + ); + R.split _ _ a1 a2; + intro_pts_to0 r a1 s1; + intro_pts_to0 r a2 s2 + +let gather_exists + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) + (s1 s2: Seq.seq t) +: STGhost (Ghost.erased (Seq.seq t)) opened + (pts_to r s1 `star` pts_to r s2) + (fun s -> pts_to r s) + ( + Seq.length s1 == Seq.length s2 + ) + (fun s -> + Seq.length s1 == Seq.length s /\ + Seq.length s2 == Seq.length s /\ + (forall (i: nat) . + i < Seq.length s ==> ( + composable p (Seq.index s1 i) (Seq.index s2 i) /\ + op p (Seq.index s1 i) (Seq.index s2 i) == Seq.index s i + )) + ) += let _ = elim_pts_to r s1 in + let _ = elim_pts_to r s2 in + let a1 = array_pcm_carrier_of_seq r.len s1 in + let a2 = array_pcm_carrier_of_seq r.len s2 in + let _ = R.gather _ (array_pcm_carrier_of_seq r.len s1) _ in + let v = vpattern_replace (R.pts_to _) in + let s = seq_of_array_pcm_carrier v in + assert ( + composable (array_pcm p r.len) a1 a2 /\ + op (array_pcm p r.len) a1 a2 `feq` array_pcm_carrier_of_seq r.len s + ); + assert (forall (i: nat) . i < Seq.length s ==> composable p (a1 (SZ.uint_to_t i)) (a2 (SZ.uint_to_t i))); + intro_pts_to0 r _ s; + noop (); + s + +let gather + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: array base_t p) + (s s1 s2: Seq.seq t) +: STGhost unit opened + (pts_to r s1 `star` pts_to r s2) + (fun _ -> pts_to r s) + ( + Seq.length s1 == Seq.length s /\ + Seq.length s2 == Seq.length s /\ + (forall (i: nat) . + (i < Seq.length s /\ composable p (Seq.index s1 i) (Seq.index s2 i)) ==> ( + op p (Seq.index s1 i) (Seq.index s2 i) == Seq.index s i + )) + ) + (fun _ -> True) += let s' = gather_exists r s1 s2 in + assert (s `Seq.equal` s'); + vpattern_rewrite (pts_to r) s + +let sub + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (a: array base_t p) + (offset: size_t) + (len: Ghost.erased size_t) +: Pure (array base_t p) + (requires (size_v offset + size_v len <= size_v a.len)) + (ensures (fun _ -> True)) += { + a with + offset = a.offset `size_add` offset; + len = len; + } + +let split_l + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (a: array base_t p) + (i: Ghost.erased size_t) +: Pure (array base_t p) + (requires (size_v i <= size_v a.len)) + (ensures (fun _ -> True)) += sub a 0sz i + +let split_r + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (a: array base_t p) + (i: size_t) +: Pure (array base_t p) + (requires (size_v i <= size_v a.len)) + (ensures (fun _ -> True)) += sub a i (a.len `size_sub` i) + +#push-options "--z3rlimit 128" + +#restart-solver +let g_focus_sub + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (a: array base_t p) + (s: Seq.seq t) + (offset: size_t) + (len: size_t) + (sq: squash (size_v offset + size_v len <= size_v a.len /\ Seq.length s == size_v a.len)) + (sl: Seq.lseq t (size_v a.len)) + (al: array base_t p) + (sl0: Seq.lseq t (size_v len)) +: STGhost unit opened + (pts_to a sl) + (fun _ -> pts_to al sl0) + ( + al == sub a offset len /\ + sl0 `Seq.equal` Seq.slice s (size_v offset) (size_v offset + size_v len) /\ + sl `Seq.equal` (Seq.create (size_v offset) (one p) `Seq.append` sl0 `Seq.append` Seq.create (size_v a.len - size_v len - size_v offset) (one p)) + ) + (fun _ -> True) += + substruct_compose + (array_elements_pcm p a.base_len) + (array_elements_pcm p a.len) + (small_to_large_index a.base_len a.offset a.len ()) + (large_to_small_index a.base_len a.offset a.len ()) + () + (array_elements_pcm p al.len) + (small_to_large_index a.len offset al.len ()) + (large_to_small_index a.len offset al.len ()) + () + (small_to_large_index al.base_len al.offset al.len ()) + (large_to_small_index al.base_len al.offset al.len ()) + (); + let cl = + substruct + (array_elements_pcm p a.len) + (array_elements_pcm p al.len) + (small_to_large_index a.len offset al.len ()) + (large_to_small_index a.len offset al.len ()) + () + in + let xl = array_pcm_carrier_of_seq a.len sl in + elim_pts_to a sl; + ref_focus_comp + a.base + (ref_of_array_conn a) + cl; + let xl0 = array_pcm_carrier_of_seq al.len sl0 in + assert ( + xl `feq` + substruct_to_struct_f + (array_elements_pcm p a.len) + (array_elements_pcm p al.len) + (small_to_large_index a.len offset al.len ()) + (large_to_small_index a.len offset al.len ()) + () + xl0 + ); + gfocus (ref_of_array a) cl _ xl0; + intro_pts_to2 al (ref_focus (ref_of_array a) cl) _ sl0 + +#pop-options + +#push-options "--z3rlimit 32" + +#restart-solver +let g_split + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (a: array base_t p) + (s: Seq.seq t) + (i: size_t) + (sq: squash (size_v i <= size_v a.len)) +: STGhostT (squash (Seq.length s == size_v a.len)) opened + (pts_to a s) + (fun _ -> pts_to (split_l a i) (Seq.slice s 0 (size_v i)) `star` pts_to (split_r a i) (Seq.slice s (size_v i) (size_v a.len))) += let _ = pts_to_length a _ in + Classical.forall_intro (is_unit p); + let sl0 = Seq.slice s 0 (size_v i) in + let sl : Seq.lseq t (size_v a.len) = sl0 `Seq.append` Seq.create (size_v a.len - size_v i) (one p) in + let sr0 = Seq.slice s (size_v i) (size_v a.len) in + let sr : Seq.lseq t (size_v a.len) = Seq.create (size_v i) (one p) `Seq.append` sr0 in + share a s sl sr; + g_focus_sub a s 0sz i () sl (split_l a i) (Seq.slice s 0 (size_v i)); + g_focus_sub a s i (a.len `size_sub` i) () sr (split_r a i) (Seq.slice s (size_v i) (size_v a.len)) + +#pop-options + +#push-options "--z3rlimit 128" + +#restart-solver +let unfocus_sub + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (a: array base_t p) + (s: Seq.seq t) + (offset: size_t) + (len: size_t) + (sq: squash (size_v offset + size_v len <= size_v a.len /\ Seq.length s == size_v a.len)) + (sl: Seq.lseq t (size_v a.len)) + (al: array base_t p) + (sl0: Seq.lseq t (size_v len)) +: STGhost unit opened + (pts_to al sl0) + (fun _ -> pts_to a sl) + ( + al == sub a offset len /\ + sl0 `Seq.equal` Seq.slice s (size_v offset) (size_v offset + size_v len) /\ + sl `Seq.equal` (Seq.create (size_v offset) (one p) `Seq.append` sl0 `Seq.append` Seq.create (size_v a.len - size_v len - size_v offset) (one p)) + ) + (fun _ -> True) += + substruct_compose + (array_elements_pcm p a.base_len) + (array_elements_pcm p a.len) + (small_to_large_index a.base_len a.offset a.len ()) + (large_to_small_index a.base_len a.offset a.len ()) + () + (array_elements_pcm p al.len) + (small_to_large_index a.len offset al.len ()) + (large_to_small_index a.len offset al.len ()) + () + (small_to_large_index al.base_len al.offset al.len ()) + (large_to_small_index al.base_len al.offset al.len ()) + (); + let cl = + substruct + (array_elements_pcm p a.len) + (array_elements_pcm p al.len) + (small_to_large_index a.len offset al.len ()) + (large_to_small_index a.len offset al.len ()) + () + in + let xl = array_pcm_carrier_of_seq a.len sl in + let _ = elim_pts_to al sl0 in + ref_focus_comp + a.base + (ref_of_array_conn a) + cl; + let xl0 = array_pcm_carrier_of_seq al.len sl0 in + assert ( + xl `feq` + substruct_to_struct_f + (array_elements_pcm p a.len) + (array_elements_pcm p al.len) + (small_to_large_index a.len offset al.len ()) + (large_to_small_index a.len offset al.len ()) + () + xl0 + ); + unfocus (ref_of_array al) (ref_of_array a) cl _; + intro_pts_to2 a (ref_of_array a) _ sl + +#pop-options + +let mk_lseq + (#t: Type) + (s: Seq.seq t) + (l: nat) + (sq: squash (Seq.length s == l)) +: Tot (Seq.lseq t l) += s + +#push-options "--z3rlimit 256 --fuel 0 --ifuel 1 --z3cliopt smt.arith.nl=false" + +#restart-solver +let join + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (a: array base_t p) + (i: size_t) + (al ar: array base_t p) + (sl0 sr0: Seq.seq t) +: STGhost unit opened + (pts_to al sl0 `star` pts_to ar sr0) + (fun _ -> pts_to a (sl0 `Seq.append` sr0)) + ( + size_v i <= size_v a.len /\ + al == split_l a i /\ + ar == split_r a i + ) + (fun _ -> True) += + let _ = pts_to_length al _ in + let _ = pts_to_length ar _ in + Classical.forall_intro (is_unit p); + let sl : Seq.lseq t (size_v a.len) = mk_lseq (sl0 `Seq.append` Seq.create (size_v a.len - size_v i) (one p)) (size_v a.len) () in + let sr : Seq.lseq t (size_v a.len) = mk_lseq (Seq.create (size_v i) (one p) `Seq.append` sr0) (size_v a.len) () in + let s : Seq.lseq t (size_v a.len) = mk_lseq (Seq.append sl0 sr0) (size_v a.len) () in + assert (i == Ghost.reveal al.len); + unfocus_sub a s 0sz i () sl al sl0; + unfocus_sub a s i (a.len `size_sub` i) () sr ar sr0; + gather a s sl sr; + rewrite (pts_to a _) (pts_to a _) + +#pop-options + +#restart-solver +let array_as_one_ref_iso + (#t: Type) + (p: pcm t) +: Tot (isomorphism (array_pcm p 1sz) p) += assert_norm (size_v 1sz == 1); + let c = cell p 1sz 0sz in + let c1 = c.conn_large_to_small in + let c2 = c.conn_small_to_large in + Steel.C.Model.Connection.mkisomorphism + c1 + c2 + () + (Steel.C.Model.Connection.is_inverse_of_intro + c2.Steel.C.Model.Connection.morph + c1.Steel.C.Model.Connection.morph + (fun x -> + array_pcm_carrier_ext t 1sz (c2.Steel.C.Model.Connection.morph (c1.Steel.C.Model.Connection.morph x)) x (fun i -> + () + ) + ) + ) + (fun x -> ()) + (fun x -> ()) + +#restart-solver +let array_as_one_ref_iso_eq + (#t: Type) + (p: pcm t) +: Lemma + ( + let _ = assert_norm (size_v 0sz == 0) in + let _ = assert_norm (size_v 1sz == 1) in + let _ : squash (size_v 0sz < size_v 1sz) = () in + connection_of_isomorphism (array_as_one_ref_iso p) == cell p 1sz 0sz + ) += assert_norm (size_v 0sz == 0); + assert_norm (size_v 1sz == 1); + let l = (connection_of_isomorphism (array_as_one_ref_iso p)) in + let m = (cell p 1sz 0sz) in + connection_eq_gen + l + m + () + (fun x y f v -> + connection_of_isomorphism_fpu_eq (array_as_one_ref_iso p) x y f v; + assert_norm ((m.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f v == struct_field_lift_fpu' (array_elements_pcm p 1sz) 0sz x y f v); + assert (connection_of_isomorphism_fpu' (array_as_one_ref_iso p) x y f v `feq` struct_field_lift_fpu' (array_elements_pcm p 1sz) 0sz x y f v); + assert ((l.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f v == (m.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f v) + ) + +let array_of_ref_conn + (#t: Type) + (p: pcm t) +: Tot (connection p (array_pcm p 1sz)) += connection_of_isomorphism (isomorphism_inverse (array_as_one_ref_iso p)) + +let g_array_of_ref + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: ref base_t p) +: Ghost (array base_t p) + (requires True) + (ensures (fun a -> + size_v a.base_len == 1 /\ + size_v a.len == 1 + )) += assert_norm (size_v 1sz == 1); + { + base_len = _; + base = ref_focus r (array_of_ref_conn p); + offset = 0sz; + len = 1sz; + prf = (); + } + +let ref_of_array_of_ref_base + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: ref base_t p) +: Lemma + (ref_of_array (g_array_of_ref r) == ref_focus r (array_of_ref_conn p)) += ref_of_array_id (g_array_of_ref r) + +#push-options "--split_queries" + +#restart-solver +let ref_of_array_of_ref + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: ref base_t p) +: Lemma + (ref_focus (ref_of_array (g_array_of_ref r)) (cell p 1sz 0sz) == r) += ref_of_array_of_ref_base r; + ref_focus_comp r (array_of_ref_conn p) (cell p 1sz 0sz); + array_as_one_ref_iso_eq p; + connection_of_isomorphism_inverse_left (array_as_one_ref_iso p); + ref_focus_id r + +#pop-options + +#restart-solver +let ghost_array_of_ref + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (#v: t) + (r: ref base_t p) +: STGhostT unit opened + (R.pts_to r v) + (fun _ -> pts_to (g_array_of_ref r) (Seq.create 1 v)) += assert_norm (size_v 0sz == 0); + assert_norm (size_v 1sz == 1); + let v' : array_pcm_carrier t 1sz = field_to_struct_f (array_elements_pcm p 1sz) 0sz v in + assert (seq_of_array_pcm_carrier v' `Seq.equal` Seq.create 1 v); + R.gfocus r (array_of_ref_conn p) _ v'; + ref_of_array_of_ref_base r; + intro_pts_to1 _ _ _ _ + +#restart-solver +let array_of_ref + (#base_t: Type) + (#t: Type) + (#opened: _) + (#p: pcm t) + (#v: Ghost.erased t) + (r: ref base_t p) +: STAtomicBase (array base_t p) false opened Unobservable + (R.pts_to r v) + (fun a -> pts_to a (Seq.create 1 (Ghost.reveal v))) + (True) + (fun a -> a == g_array_of_ref r) += assert_norm (size_v 0sz == 0); + assert_norm (size_v 1sz == 1); + let v' : Ghost.erased (array_pcm_carrier t 1sz) = Ghost.hide (field_to_struct_f (array_elements_pcm p 1sz) 0sz v) in + assert (seq_of_array_pcm_carrier v' `Seq.equal` Seq.create 1 (Ghost.reveal v)); + let r' = R.focus r (array_of_ref_conn p) _ v' in + let a : array base_t p = { + base_len = 1sz; + base = r'; + offset = 0sz; + len = 1sz; + prf = (); + } + in + ref_of_array_of_ref_base r; + intro_pts_to1 a _ _ _; + return a + +#push-options "--split_queries --z3rlimit 32 --query_stats" + +#restart-solver +let unarray_of_ref + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (#v: Seq.seq t) + (r: ref base_t p) + (a: array base_t p) +: STGhost (squash (Seq.length v == 1)) opened + (pts_to a v) + (fun _ -> R.pts_to r (Seq.index v 0)) + (a == g_array_of_ref r) + (fun _ -> True) += assert_norm (size_v 0sz == 0); + assert_norm (size_v 1sz == 1); + let _ = elim_pts_to _ _ in + ref_of_array_of_ref_base r; + R.unfocus (ref_of_array a) r (array_of_ref_conn p) _; + let x = (array_pcm_carrier_of_seq a.len v) in + assert_norm ((array_of_ref_conn p).conn_small_to_large.morph x == x 0sz); + array_pcm_carrier_of_seq_eq a.len v 0sz; + assert (x 0sz == Seq.index v 0); + rewrite (R.pts_to _ _) (R.pts_to _ _) + +#pop-options diff --git a/lib/steel/c/Steel.ST.C.Model.Frac.fst b/lib/steel/c/Steel.ST.C.Model.Frac.fst new file mode 100644 index 000000000..7d87edc07 --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Model.Frac.fst @@ -0,0 +1,44 @@ +module Steel.ST.C.Model.Frac + +let frac_pcm_write r x y += ref_upd r x (Some (y, full_perm)) (frac_pcm_fpu x y); + rewrite (r `pts_to` _) (r `pts_to` _) + +let frac_pcm_read r x += let y' = ref_read r in + assert (Some? y' /\ fst (Some?.v (Ghost.reveal x)) == fst (Some?.v y')); + fst (Some?.v y') + +let exclusive_frac + (#a: Type) + (x: option (a & perm)) +: Lemma + (exclusive pcm_frac x <==> ((exists (y: a) . True) ==> (Some? x /\ full_perm `lesser_equal_perm` snd (Some?.v x)))) += match x with + | None -> + if FStar.StrongExcludedMiddle.strong_excluded_middle (exists (y: a). True) + then begin + let y = FStar.IndefiniteDescription.indefinite_description_ghost a (fun _ -> True) in + let frame = Some (y, full_perm) in + assert (~ (frame == one pcm_frac)); + assert (composable pcm_frac x frame) + end else begin + let phi + (frame: option (a & perm)) + : Lemma + (frame == None) + = match frame with + | None -> () + | Some (z, _) -> assert (exists (y: a) . True) + in + Classical.forall_intro phi + end + | Some (y, p) -> + assert (exists (z: a) . True); + if FStar.StrongExcludedMiddle.strong_excluded_middle (full_perm `lesser_equal_perm` p) + then () + else begin + let frame = Some (y, MkPerm (let open FStar.Real in one -. p.v)) in + assert (composable pcm_frac x frame); + assert (~ (frame == one pcm_frac)) + end diff --git a/lib/steel/c/Steel.ST.C.Model.Frac.fsti b/lib/steel/c/Steel.ST.C.Model.Frac.fsti new file mode 100644 index 000000000..ce256985e --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Model.Frac.fsti @@ -0,0 +1,66 @@ +module Steel.ST.C.Model.Frac +open Steel.ST.Util + +module P = FStar.PCM +open Steel.C.Model.PCM +open Steel.ST.C.Model.Ref + +/// Fractional permissions: from Steel.HigherReference +open Steel.FractionalPermission + +let fractional (a:Type u#a) = option (a & perm) + +let fractional_composable #a : P.symrel (fractional a) = + fun (f0 f1:fractional a) -> + match f0, f1 with + | None, _ + | _, None -> True + | Some (x0, p0), Some (x1, p1) -> x0==x1 /\ sum_perm p0 p1 `lesser_equal_perm` full_perm + +let fractional_compose #a (f0:fractional a) (f1:fractional a{fractional_composable f0 f1}) : fractional a = + match f0, f1 with + | None, f + | f, None -> f + | Some (x0, p0), Some (_, p1) -> Some (x0, sum_perm p0 p1) + +let fstar_pcm_frac #a : P.pcm (fractional a) = let open P in { + p = { + composable = fractional_composable; + op = fractional_compose; + one = None + }; + comm = (fun _ _ -> ()); + assoc = (fun _ _ _ -> ()); + assoc_r = (fun _ _ _ -> ()); + is_unit = (fun _ -> ()); + refine = (fun x -> Some? x /\ snd (Some?.v x) == full_perm) +} + +let pcm_frac #a : pcm (fractional a) = pcm_of_fstar_pcm fstar_pcm_frac + +let frac_pcm_fpu + (#a: Type) + (x: Ghost.erased (fractional a) { Some? x /\ snd (Some?.v x) == full_perm }) + (y: a) +: Tot (frame_preserving_upd pcm_frac x (Some (y, full_perm))) += base_fpu pcm_frac x (Some (y, full_perm)) + +val frac_pcm_write + (#a:Type) (#b: Type) + (r: ref a (pcm_frac #b)) (x: Ghost.erased (fractional b)) (y: b) +: ST unit (r `pts_to` x) (fun _ -> r `pts_to` Some (y, full_perm)) + (requires (Some? x /\ snd (Some?.v x) == full_perm)) + (ensures (fun _ -> True)) + +val frac_pcm_read + (#a:Type) (#b: Type) + (r: ref a (pcm_frac #b)) (x: Ghost.erased (fractional b)) +: ST b (r `pts_to` x) (fun _ -> r `pts_to` x) + (requires (Some? x)) + (ensures (fun y -> Some? x /\ y == fst (Some?.v (Ghost.reveal x)))) + +val exclusive_frac + (#a: Type) + (x: option (a & perm)) +: Lemma + (exclusive pcm_frac x <==> ((exists (y: a) . True) ==> (Some? x /\ full_perm `lesser_equal_perm` snd (Some?.v x)))) diff --git a/lib/steel/c/Steel.ST.C.Model.Ref.fst b/lib/steel/c/Steel.ST.C.Model.Ref.fst new file mode 100644 index 000000000..a1d605ae5 --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Model.Ref.fst @@ -0,0 +1,134 @@ +module Steel.ST.C.Model.Ref +module P = FStar.PCM +module U = Steel.C.Model.Universe +open FStar.FunctionalExtensionality + +friend Steel.C.Model.Ref.Base + +let mk_id_ref + (#a: Type0) + (p: pcm a) + (r0: Steel.Memory.ref (U.raise_t u#0 u#1 a) (fstar_pcm_of_pcm (U.raise_pcm u#0 u#1 p))) +: Tot (ref a p) += + let p' : pcm u#1 _ = U.raise_pcm u#0 u#1 p in + let fp = fstar_pcm_of_pcm p' in + NonNull ({ p = p; q = p; pl = connection_id p; r = r0 }) + +#push-options "--z3rlimit 16" + +let ref_alloc #a p x = + let x' : U.raise_t u#0 u#1 a = U.raise_val u#0 u#1 x in + let p' : pcm u#1 _ = U.raise_pcm u#0 u#1 p in +// let fp : P.pcm u#1 _ = fstar_pcm_of_pcm p' in // FIXME: I can define this local definition, but WHY WHY WHY can't I USE it? + compatible_refl p' x'; + let r0 : Steel.Memory.ref (U.raise_t u#0 u#1 a) (fstar_pcm_of_pcm (U.raise_pcm u#0 u#1 p)) = Steel.ST.PCMReference.alloc #_ #(fstar_pcm_of_pcm (U.raise_pcm u#0 u#1 p)) x' in + let r : ref a p = mk_id_ref p r0 in + connection_compose_id_right (lower_conn r); + rewrite (r0 `mpts_to` _) (r `pts_to` x); + return r + +let ref_free #a #b #p #x r = + // TODO: use Steel.PCMReference.free, but we are blocked by (p.refine (one p)), which we explicitly excluded in Steel.C.Model.PCM + drop (pts_to _ _) + +#pop-options + +let gfocus r l s x = + connection_compose_assoc (lower_conn r) (NonNull?.v r).pl l; + rewrite + (r `pts_to` s) + (ref_focus r l `pts_to` x) + +let focus r l s x = + let r' = t_ref_focus r l in + gfocus r l s x; + rewrite + (ref_focus r l `pts_to` x) + (r' `pts_to` x); + return r' + +let unfocus r r' l x = + connection_compose_assoc (lower_conn r') (NonNull?.v r').pl l; + rewrite + (r `pts_to` x) + (r' `pts_to` l.conn_small_to_large.morph x) + +let split r xy x y = + let c = raise_pl r in + let xy2 = Ghost.hide (c.conn_small_to_large.morph xy) in + let x2 = Ghost.hide (c.conn_small_to_large.morph x) in + let y2 = Ghost.hide (c.conn_small_to_large.morph y) in + assert (composable (raise_p r) x2 y2); + rewrite + (r `pts_to` xy) + ((NonNull?.v r).r `mpts_to` xy2); + Steel.ST.PCMReference.split (NonNull?.v r).r + xy2 + x2 + y2; + rewrite + ((NonNull?.v r).r `mpts_to` x2) + (r `pts_to` x); + rewrite + ((NonNull?.v r).r `mpts_to` y2) + (r `pts_to` y) + +let mgather + (#inames: _) (#a:Type) (#p:P.pcm a) + (r:Steel.Memory.ref a p) (v0:Ghost.erased a) (v1:Ghost.erased a) +: STGhostT (_:unit{P.composable p v0 v1}) inames + (mpts_to r v0 `star` mpts_to r v1) + (fun _ -> mpts_to r (P.op p v0 v1)) += Steel.ST.PCMReference.gather r v0 v1 + +let gather #inames #a #b #p r x y = + let c = raise_pl r in + let x2 = Ghost.hide (c.conn_small_to_large.morph x) in + let y2 = Ghost.hide (c.conn_small_to_large.morph y) in + rewrite + (r `pts_to` x) + ((NonNull?.v r).r `mpts_to` x2); + rewrite + (r `pts_to` y) + ((NonNull?.v r).r `mpts_to` y2); + let _ = mgather (NonNull?.v r).r + x2 + y2 + in + assert (composable (raise_p r) x2 y2); + assert ( + let x' = c.conn_large_to_small.morph x2 in + let y' = c.conn_large_to_small.morph y2 in + composable p x' y' /\ + Ghost.reveal x == x' /\ Ghost.reveal y == y' + ); + rewrite _ (r `pts_to` op p x y) + +let ref_read + #_ #_ #p #x r += let w = Ghost.hide ((raise_pl r).conn_small_to_large.morph x) in + rewrite (r `pts_to` x) ((NonNull?.v r).r `mpts_to` w); + let w' = Steel.ST.PCMReference.read (NonNull?.v r).r w in + rewrite ((NonNull?.v r).r `mpts_to` w) (r `pts_to` x); + let x' = (raise_pl r).conn_large_to_small.morph w' in + compatible_morphism (raise_pl r).conn_large_to_small w w'; + return x' + +let ref_upd_act (r: ref 'a 'p) (x: Ghost.erased 'b { ~ (Ghost.reveal x == one 'p) }) (y: Ghost.erased 'b) (f: frame_preserving_upd 'p x y) +: Tot (M.action_except unit Set.empty (hp_of (r `pts_to` x)) (fun _ -> hp_of (r `pts_to` y))) += let c = raise_pl r in + let x' = Ghost.hide (c.conn_small_to_large.morph x) in + let y' = Ghost.hide (c.conn_small_to_large.morph y) in + M.upd_gen Set.empty (NonNull?.v r).r x' y' (fstar_fpu_of_fpu (raise_p r) x' y' (mk_restricted_frame_preserving_upd (c.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = restricted_frame_preserving_upd_intro f; }) ))) + +let as_action (#p:vprop) + (#q:vprop) + (f:M.action_except unit Set.empty (hp_of p) (fun _ -> hp_of q)) +: STT unit p (fun x -> q) += weaken p (to_vprop (hp_of p)) (fun m -> ()); + let x = Steel.ST.Coercions.coerce_steel (fun _ -> Steel.Effect.as_action f) in + weaken (to_vprop (hp_of q)) q (fun m -> ()); + return x + +let ref_upd r x y f = as_action (ref_upd_act r x y f) diff --git a/lib/steel/c/Steel.ST.C.Model.Ref.fsti b/lib/steel/c/Steel.ST.C.Model.Ref.fsti new file mode 100644 index 000000000..36fa6e143 --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Model.Ref.fsti @@ -0,0 +1,90 @@ +module Steel.ST.C.Model.Ref +open Steel.ST.Util + +include Steel.C.Model.Ref.Base + +open FStar.FunctionalExtensionality +open Steel.C.Model.PCM +open Steel.C.Model.Connection + +(** Allocate a reference containing value x. *) +val ref_alloc + (#a:Type0) (p: pcm a) (x: a) +: ST (ref a p) + emp + (fun r -> r `pts_to` x) + (requires p_refine p x) + (ensures fun r -> freeable r) + +(** Free a "base" (freeable) reference containing a "whole" (p_refine) value x. *) +val ref_free + (#a #b:Type0) (#p: pcm b) (#x: Ghost.erased b) (r: ref a p) +: ST unit + (r `pts_to` x) + (fun _ -> emp) + (requires p_refine p x /\ freeable r) + (ensures fun _ -> True) + + +(** Take a pointer to a "substructure" of a reference. *) +val gfocus (#inames: _) (#p: pcm 'b) (r: ref 'a p) + (#q: pcm 'c) + (l: connection p q) (s: Ghost.erased 'b) (x: Ghost.erased 'c) +: STGhost unit inames + (r `pts_to` s) + (fun _ -> ref_focus r l `pts_to` x) + (Ghost.reveal s == l.conn_small_to_large.morph x) + (fun _ -> True) + +val focus (#opened: _) (#p: pcm 'b) (r: ref 'a p) + (#q: pcm 'c) + (l: connection p q) (s: Ghost.erased 'b) (x: Ghost.erased 'c) +: STAtomicBase (ref 'a q) + false opened Unobservable + (r `pts_to` s) + (fun r' -> r' `pts_to` x) + (Ghost.reveal s == l.conn_small_to_large.morph x) + (fun r' -> r' == ref_focus r l) + +module M = Steel.Memory + +(** Inverse of focus. *) +val unfocus (#opened:M.inames) + (#p: pcm 'b) + (#q: pcm 'c) + (r: ref 'a q) (r': ref 'a p) + (l: connection p q) (x: Ghost.erased 'c) +: STGhost unit opened + (r `pts_to` x) + (fun _ -> r' `pts_to` l.conn_small_to_large.morph x) + (requires r == ref_focus r' l) + (ensures fun _ -> True) + +(** Split the permissions on a reference into two halves. *) +val split (#inames: _) (#a:Type) (#b:Type) (#p: pcm b) (r: ref a p) (xy x y: Ghost.erased b) +: STGhost unit inames + (r `pts_to` xy) + (fun _ -> (r `pts_to` x) `star` (r `pts_to` y)) + (composable p x y /\ xy == Ghost.hide (op p x y)) + (fun _ -> True) + +(** Inverse of split. *) +val gather (#inames: _) (#a:Type) (#b:Type) (#p: pcm b) (r: ref a p) (x y: Ghost.erased b) +: STGhostT (_:unit{composable p x y}) inames + ((r `pts_to` x) `star` (r `pts_to` y)) + (fun _ -> r `pts_to` op p x y) + +(** Read a PCM carrier value. *) +val ref_read + (#a:Type) (#b:Type) (#p: pcm b) (#x: Ghost.erased b) (r: ref a p) +: ST b + (r `pts_to` x) + (fun _ -> r `pts_to` x) + (requires True) + (ensures fun x' -> compatible p x x') + +(** Write a PCM carrier value. *) +val ref_upd + (#a:Type) (#b:Type) (#p: pcm b) + (r: ref a p) (x: Ghost.erased b { ~ (Ghost.reveal x == one p) }) (y: Ghost.erased b) (f: frame_preserving_upd p x y) +: STT unit (r `pts_to` x) (fun _ -> r `pts_to` y) diff --git a/lib/steel/c/Steel.ST.C.Model.Rewrite.fst b/lib/steel/c/Steel.ST.C.Model.Rewrite.fst new file mode 100644 index 000000000..b6b69c490 --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Model.Rewrite.fst @@ -0,0 +1,46 @@ +module Steel.ST.C.Model.Rewrite + +module P = FStar.PCM + +let fstar_rewrite_pcm + (#from #to: Type) + (p: pcm from) + (rewrite: rewrite_elts from to) +: Tot (P.pcm to) += let fp = fstar_pcm_of_pcm p in + { + P.p = { + P.composable = (fun y1 y2 -> composable p (rewrite.rewrite_to_from y1) (rewrite.rewrite_to_from y2)); + P.op = (fun y1 y2 -> rewrite.rewrite_from_to (op p (rewrite.rewrite_to_from y1) (rewrite.rewrite_to_from y2))); + P.one = rewrite.rewrite_from_to (one p); + }; + P.comm = (fun _ _ -> ()); + P.assoc = (fun _ _ _ -> ()); + P.assoc_r = (fun _ _ _ -> ()); + P.is_unit = (fun _ -> ()); + P.refine = (fun y -> p_refine p (rewrite.rewrite_to_from y)); + } + +let rewrite_pcm + (#from #to: Type) + (p: pcm from) + (rewrite: rewrite_elts from to) +: Tot (pcm to) += let fp = fstar_pcm_of_pcm p in + pcm_of_fstar_pcm (fstar_rewrite_pcm p rewrite) + +let rewrite_pcm_composable + p rewrite x1 x2 += () + +let rewrite_pcm_op + p rewrite x1 x2 += () + +let rewrite_pcm_one + p rewrite += () + +let rewrite_pcm_refine + p rewrite x += () diff --git a/lib/steel/c/Steel.ST.C.Model.Rewrite.fsti b/lib/steel/c/Steel.ST.C.Model.Rewrite.fsti new file mode 100644 index 000000000..2304dfe89 --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Model.Rewrite.fsti @@ -0,0 +1,113 @@ +module Steel.ST.C.Model.Rewrite + +open Steel.C.Model.PCM +open Steel.C.Model.Connection + +let rewrite_forall_from + (#from #to: Type) + (rewrite_from_to : (from -> Tot to)) + (rewrite_to_from: (to -> Tot from)) +: GTot prop += forall (x: from) . rewrite_to_from (rewrite_from_to x) == x + +let rewrite_forall_from_intro + (#from #to: Type) + (rewrite_from_to : (from -> Tot to)) + (rewrite_to_from: (to -> Tot from)) + (f: (x: from) -> Lemma + (rewrite_to_from (rewrite_from_to x) == x) + ) +: Lemma + (rewrite_forall_from rewrite_from_to rewrite_to_from) += Classical.forall_intro f + +let rewrite_forall_to + (#from #to: Type) + (rewrite_from_to : (from -> Tot to)) + (rewrite_to_from: (to -> Tot from)) +: GTot prop += forall (y: to) . rewrite_from_to (rewrite_to_from y) == y + +let rewrite_forall_to_intro + (#from #to: Type) + (rewrite_from_to : (from -> Tot to)) + (rewrite_to_from: (to -> Tot from)) + (f: (x: to) -> Lemma + (rewrite_from_to (rewrite_to_from x) == x) + ) +: Lemma + (rewrite_forall_to rewrite_from_to rewrite_to_from) += Classical.forall_intro f + +noeq +type rewrite_elts (from: Type) (to: Type) = { + rewrite_from_to : (from -> Tot to); + rewrite_to_from: (to -> Tot from); + rewrite_equiv : squash ( + rewrite_forall_from rewrite_from_to rewrite_to_from /\ + rewrite_forall_to rewrite_from_to rewrite_to_from + ); +} + +val rewrite_pcm + (#from #to: Type) + (p: pcm from) + (rewrite: rewrite_elts from to) +: Tot (pcm to) + +val rewrite_pcm_composable + (#from #to: Type) + (p: pcm from) + (rewrite: rewrite_elts from to) + (x1 x2: to) +: Lemma + (composable (rewrite_pcm p rewrite) x1 x2 <==> composable p (rewrite.rewrite_to_from x1) (rewrite.rewrite_to_from x2)) + [SMTPat (composable (rewrite_pcm p rewrite) x1 x2)] + +val rewrite_pcm_op + (#from #to: Type) + (p: pcm from) + (rewrite: rewrite_elts from to) + (x1 x2: to) +: Lemma + (requires (composable (rewrite_pcm p rewrite) x1 x2)) + (ensures (op (rewrite_pcm p rewrite) x1 x2 == rewrite.rewrite_from_to (op p (rewrite.rewrite_to_from x1) (rewrite.rewrite_to_from x2)))) + [SMTPat (op (rewrite_pcm p rewrite) x1 x2)] + +val rewrite_pcm_one + (#from #to: Type) + (p: pcm from) + (rewrite: rewrite_elts from to) +: Lemma + (one (rewrite_pcm p rewrite) == rewrite.rewrite_from_to (one p)) + [SMTPat (one (rewrite_pcm p rewrite))] + +val rewrite_pcm_refine + (#from #to: Type) + (p: pcm from) + (rewrite: rewrite_elts from to) + (x: to) +: Lemma + (p_refine (rewrite_pcm p rewrite) x <==> p_refine p (rewrite.rewrite_to_from x)) + [SMTPat (p_refine (rewrite_pcm p rewrite) x)] + +let rewrite_iso + (#from #to: Type) + (p: pcm from) + (rewrite: rewrite_elts from to) +: Tot (isomorphism p (rewrite_pcm p rewrite)) += mkisomorphism + (mkmorphism + rewrite.rewrite_from_to + () + (fun _ _ -> ()) + ) + (mkmorphism + rewrite.rewrite_to_from + () + (fun _ _ -> ()) + ) + () + () + (fun _ -> ()) + (fun _ -> ()) diff --git a/lib/steel/c/Steel.ST.C.Model.Struct.fst b/lib/steel/c/Steel.ST.C.Model.Struct.fst new file mode 100644 index 000000000..c4bacb873 --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Model.Struct.fst @@ -0,0 +1,889 @@ +module Steel.ST.C.Model.Struct +open Steel.ST.Util + +module P = FStar.PCM +open Steel.C.Model.PCM +open Steel.C.Model.Connection +open Steel.ST.C.Model.Ref + +(** A PCM for structs *) + +/// We can generalize to 'a-ary products (k:'a -> 'b k), given a PCM for each k: + +open FStar.FunctionalExtensionality +open FStar.Classical +let ext (f g: restricted_t 'a 'b) (fg:(x:'a -> Lemma (f x == g x))) : Lemma (f == g) = + extensionality 'a 'b f g; + forall_intro fg + +let prod_comp (p:(k:'a -> pcm ('b k))) (x y: restricted_t 'a 'b): prop = + forall k. composable (p k) (x k) (y k) + +let prod_op (p:(k:'a -> pcm ('b k))) + (x: restricted_t 'a 'b) (y: restricted_t 'a 'b{prod_comp p x y}) +: restricted_t 'a 'b += on_domain 'a (fun k -> op (p k) (x k) (y k) <: 'b k) + +let prod_one (p:(k:'a -> pcm ('b k))): restricted_t 'a 'b = + on_domain 'a (fun k -> one (p k)) + +let prod_comm (p:(k:'a -> pcm ('b k))) + (x: restricted_t 'a 'b) (y: restricted_t 'a 'b{prod_comp p x y}) +: Lemma (prod_op p x y == prod_op p y x) += ext (prod_op p x y) (prod_op p y x) (fun k -> ()) + +let prod_assoc (p:(k:'a -> pcm ('b k))) + (x y: restricted_t 'a 'b) + (z: restricted_t 'a 'b{prod_comp p y z /\ prod_comp p x (prod_op p y z)}) +: Lemma (prod_comp p x y /\ + prod_comp p (prod_op p x y) z /\ + prod_op p x (prod_op p y z) == prod_op p (prod_op p x y) z) += let aux k + : Lemma (composable (p k) (x k) (y k) /\ + composable (p k) (op (p k) (x k) (y k)) (z k)) + [SMTPat (p k)] + = () + in + ext (prod_op p x (prod_op p y z)) (prod_op p (prod_op p x y) z) + (fun k -> ()) + +let prod_assoc_r (p:(k:'a -> pcm ('b k))) + (x y: restricted_t 'a 'b) + (z: restricted_t 'a 'b{prod_comp p x y /\ prod_comp p (prod_op p x y) z}) +: Lemma (prod_comp p y z /\ + prod_comp p x (prod_op p y z) /\ + prod_op p x (prod_op p y z) == prod_op p (prod_op p x y) z) += let aux k + : Lemma (composable (p k) (y k) (z k) /\ + composable (p k) (x k) (op (p k) (y k) (z k))) + [SMTPat (p k)] + = () + in + ext (prod_op p x (prod_op p y z)) (prod_op p (prod_op p x y) z) + (fun k -> ()) + +let prod_is_unit (p:(k:'a -> pcm ('b k))) (x: restricted_t 'a 'b) +: Lemma (prod_comp p x (prod_one p) /\ + prod_op p x (prod_one p) == x) += let is_unit k + : Lemma (composable (p k) (x k) (prod_one p k)) + [SMTPat (p k)] + = () + in ext (prod_op p x (prod_one p)) x (fun k -> ()) + +let prod_refine (p:(k:'a -> pcm ('b k))) (x: restricted_t 'a 'b): prop = + (exists (k: 'a). True) /\ (forall k. p_refine (p k) (x k)) + +let fstar_prod_pcm (p:(k:'a -> pcm ('b k))): P.pcm (restricted_t 'a 'b) = let open P in { + comm = prod_comm p; + p = {composable = prod_comp p; op = prod_op p; one = prod_one p}; + assoc = prod_assoc p; + assoc_r = prod_assoc_r p; + is_unit = prod_is_unit p; + refine = prod_refine p +} + +let prod_pcm' (p:(k:'a -> pcm ('b k))): pcm0 (restricted_t 'a 'b) = pcm_of_fstar_pcm (fstar_prod_pcm p) + +let prod_pcm (p:(k:'a -> pcm ('b k))): pcm (restricted_t 'a 'b) = + let p' = prod_pcm' p in + assert (forall x y . (composable p' x y /\ op p' x y == one p') ==> ( + x `feq` one p' /\ y `feq` one p' + )); + assert (forall x frame . (prod_refine p x /\ prod_comp p x frame) ==> frame `feq` prod_one p); + prod_pcm' p + +let prod_pcm_ext + (#a: Type) + (#b: (a -> Type)) + (p1 p2: ((k: a) -> pcm (b k))) + (p_eq: ( + (k: a) -> + Lemma + (p1 k == p2 k) + )) +: Lemma + (prod_pcm p1 == prod_pcm p2) += Classical.forall_intro p_eq; + pcm0_ext (prod_pcm p1) (prod_pcm p2) + (fun x y -> ()) + (fun x y -> assert (op (prod_pcm p1) x y `feq` op (prod_pcm p2) x y)) + (fun _ -> ()) + (assert (one (prod_pcm p1) `feq` one (prod_pcm p2))) + +let prod_pcm_composable_intro0 + (p:(k:'a -> pcm ('b k))) + (x y: restricted_t 'a 'b) +: Lemma + ((composable (prod_pcm p) x y <==> prod_comp p x y) /\ + (composable (prod_pcm p) x y ==> op (prod_pcm p) x y == prod_op p x y)) + [SMTPat (composable (prod_pcm p) x y)] += () + +let prod_pcm_composable_intro (p:(k:'a -> pcm ('b k))) (x y: restricted_t 'a 'b) + (h:(k:'a -> Lemma (composable (p k) (x k) (y k)))) +: Lemma (composable (prod_pcm p) x y) = FStar.Classical.forall_intro h + +let field_to_struct_f + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (k: a) + (x: b k) +: Pure (restricted_t a b) + (requires True) + (ensures (fun y -> forall k' . y k' == (if k' = k then (x <: b k') else one (p k')))) += on_dom a (fun k' -> if k' = k then (x <: b k') else one (p k')) + +let field_to_struct + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (k: a) +: Tot (morphism (p k) (prod_pcm p)) += mkmorphism + (field_to_struct_f p k) + (assert (field_to_struct_f p k (one (p k)) `feq` one (prod_pcm p))) + (fun x1 x2 -> + Classical.forall_intro_2 (fun k -> is_unit (p k)); + assert (prod_op p (field_to_struct_f p k x1) (field_to_struct_f p k x2) `feq` field_to_struct_f p k (op (p k) x1 x2)); + ()) + +let struct_to_field_f + (#a: Type) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (k: a) + (x: restricted_t a b) +: Tot (b k) += x k + +let struct_to_field + (#a: Type) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (k: a) +: Tot (morphism (prod_pcm p) (p k)) += mkmorphism + (struct_to_field_f p k) () + (fun x1 x2 -> ()) + +let struct_field_lift_fpu' + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (k: a) + (x: Ghost.erased (b k) { ~ (Ghost.reveal x == one (p k)) }) + (y: Ghost.erased (b k)) + (f: frame_preserving_upd (p k) x y) + (v: restricted_t a b { + p_refine (prod_pcm p) v /\ + compatible (prod_pcm p) ((field_to_struct p k).morph x) v + }) +: Tot (restricted_t a b) += + on_dom a (fun k' -> + if k' = k + then f (v k) <: b k' + else v k' + ) + +#push-options "--query_stats --z3rlimit 30" +#restart-solver + +let struct_field_lift_fpu_prf + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (k: a) + (x: Ghost.erased (b k) { ~ (Ghost.reveal x == one (p k)) }) + (y: Ghost.erased (b k)) + (f: frame_preserving_upd (p k) x y) + (v: restricted_t a b { + p_refine (prod_pcm p) v /\ + compatible (prod_pcm p) ((field_to_struct p k).morph x) v + }) +: Lemma + (let v_new = struct_field_lift_fpu' p k x y f v in + p_refine (prod_pcm p) v_new /\ + compatible (prod_pcm p) ((field_to_struct p k).morph y) v_new /\ + (forall (frame:_{composable (prod_pcm p) ((field_to_struct p k).morph x) frame}). + composable (prod_pcm p) ((field_to_struct p k).morph y) frame /\ + (op (prod_pcm p) ((field_to_struct p k).morph x) frame == v ==> op (prod_pcm p) ((field_to_struct p k).morph y) frame == v_new)) + ) += + let y' = (field_to_struct p k).morph y in + let v_new = struct_field_lift_fpu' p k x y f v in + Classical.forall_intro_2 (fun k -> is_unit (p k)); + assert (forall (frame: b k) . + (composable (p k) y frame /\ op (p k) frame y == f (v k)) ==> ( + let frame' : restricted_t a b = on_dom a (fun k' -> if k' = k then (frame <: b k') else v_new k') in + composable (prod_pcm p) y' frame' /\ + op (prod_pcm p) frame' y' `feq` v_new + )); + assert (compatible (prod_pcm p) y' v_new); + assert (forall (frame:_{composable (prod_pcm p) ((field_to_struct p k).morph x) frame}). + composable (prod_pcm p) ((field_to_struct p k).morph y) frame /\ + (op (prod_pcm p) ((field_to_struct p k).morph x) frame == v ==> op (prod_pcm p) ((field_to_struct p k).morph y) frame `feq` v_new)); + () + +#pop-options + +let struct_field_lift_fpu + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (k: a) + (x: Ghost.erased (b k) { ~ (Ghost.reveal x == one (p k)) }) + (y: Ghost.erased (b k)) + (f: frame_preserving_upd (p k) x y) +: Tot (frame_preserving_upd (prod_pcm p) ((field_to_struct p k).morph x) ((field_to_struct p k).morph y)) += fun v -> + struct_field_lift_fpu_prf p k x y f v; + struct_field_lift_fpu' p k x y f v + +let struct_field + (#a: eqtype) + (#b: a -> Type u#b) + (p:(k: a -> pcm (b k))) + (k: a) +: Tot (connection (prod_pcm p) (p k)) += mkconnection + (field_to_struct p k) + (struct_to_field p k) + () + (struct_field_lift_fpu p k) + +#push-options "--split_queries" + +#restart-solver +let struct_field_ext + (#a: eqtype) + (#b: a -> Type u#b) + (p1 p2:(k: a -> pcm (b k))) + (p_eq: ( + (k: a) -> + Lemma + (p1 k == p2 k) + )) + (k: a) +: Lemma + (prod_pcm p1 == prod_pcm p2 /\ + p1 k == p2 k /\ + struct_field p1 k === struct_field p2 k + ) += prod_pcm_ext p1 p2 p_eq; + p_eq k; + Classical.forall_intro p_eq; + let l = struct_field p1 k in + let m : connection (prod_pcm p1) (p1 k) = coerce_eq () (struct_field p2 k) in + assert (forall x . field_to_struct_f p1 k x `feq` field_to_struct_f p2 k x); + connection_eq_gen + l + m + () + (fun x y f v -> + struct_field_lift_fpu_prf p1 k x y f v; + struct_field_lift_fpu_prf p2 k x y f v; + assert (forall k' . struct_field_lift_fpu' p1 k x y f v k' == struct_field_lift_fpu' p2 k x y f v k'); + assert (struct_field_lift_fpu' p1 k x y f v == struct_field_lift_fpu' p2 k x y f v); + assert ((l.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f v == (m.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f v) + ) + +#pop-options + +let is_substruct + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (#a': eqtype) + (#b': (a' -> Type)) + (p': (k: a' -> pcm (b' k))) + (inj: (a' -> a)) + (surj: (a -> option a')) +: Tot prop += (forall (k: a') . b' k == b (inj k) /\ p' k == p (inj k)) /\ + (forall (k: a') . surj (inj k) == Some k) /\ + (forall (k: a) . (match surj k with None -> True | Some k' -> inj k' == k)) + +let substruct_to_struct_f + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (#a': eqtype) + (#b': (a' -> Type)) + (p': (k: a' -> pcm (b' k))) + (inj: (a' -> a)) + (surj: (a -> option a')) + (sq: squash (is_substruct p p' inj surj)) + (x: restricted_t a' b') +: Pure (restricted_t a b) + (requires True) + (ensures (fun y -> forall k . y k == (match surj k with Some k' -> (x k' <: b k) | _ -> one (p k)))) += on_dom a (fun k -> match surj k with Some k' -> (x k' <: b k) | _ -> one (p k)) + +let substruct_to_struct + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (#a': eqtype) + (#b': (a' -> Type)) + (p': (k: a' -> pcm (b' k))) + (inj: (a' -> a)) + (surj: (a -> option a')) + (sq: squash (is_substruct p p' inj surj)) +: Tot (morphism (prod_pcm p') (prod_pcm p)) += mkmorphism + (substruct_to_struct_f p p' inj surj sq) + (assert (substruct_to_struct_f p p' inj surj sq (one (prod_pcm p')) `feq` one (prod_pcm p))) + (fun x1 x2 -> + assert (prod_op p (substruct_to_struct_f p p' inj surj sq x1) (substruct_to_struct_f p p' inj surj sq x2) `feq` substruct_to_struct_f p p' inj surj sq (prod_op p' x1 x2)) + ) + +let struct_to_substruct_f + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (#a': eqtype) + (#b': (a' -> Type)) + (p': (k: a' -> pcm (b' k))) + (inj: (a' -> a)) + (surj: (a -> option a')) + (sq: squash (is_substruct p p' inj surj)) + (x: restricted_t a b) +: Pure (restricted_t a' b') + (requires True) + (ensures (fun y -> forall k . y k == x (inj k))) += on_dom a' (fun k -> x (inj k) <: b' k) + +let struct_to_substruct + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (#a': eqtype) + (#b': (a' -> Type)) + (p': (k: a' -> pcm (b' k))) + (inj: (a' -> a)) + (surj: (a -> option a')) + (sq: squash (is_substruct p p' inj surj)) +: Tot (morphism (prod_pcm p) (prod_pcm p')) += mkmorphism + (struct_to_substruct_f p p' inj surj sq) + (assert (struct_to_substruct_f p p' inj surj sq (one (prod_pcm p)) `feq` one (prod_pcm p'))) + (fun x1 x2 -> + assert (prod_op p' (struct_to_substruct_f p p' inj surj sq x1) (struct_to_substruct_f p p' inj surj sq x2) `feq` struct_to_substruct_f p p' inj surj sq (prod_op p x1 x2)) + ) + +let substruct_lift_fpu' + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (#a': eqtype) + (#b': (a' -> Type)) + (p': (k: a' -> pcm (b' k))) + (inj: (a' -> a)) + (surj: (a -> option a')) + (sq: squash (is_substruct p p' inj surj)) + (x': Ghost.erased (restricted_t a' b') { ~ (Ghost.reveal x' == one (prod_pcm p')) }) + (y': Ghost.erased (restricted_t a' b')) + (f': frame_preserving_upd (prod_pcm p') x' y') + (v: restricted_t a b { + p_refine (prod_pcm p) v /\ + compatible (prod_pcm p) ((substruct_to_struct p p' inj surj sq).morph x') v + }) +: Tot (restricted_t a b) += + on_dom a (fun k -> + let v' = ((struct_to_substruct p p' inj surj sq).morph v) in + let x = Ghost.hide ((substruct_to_struct p p' inj surj sq).morph x') in + assert (forall frame . (composable (prod_pcm p) x frame /\ op (prod_pcm p) x frame == v) ==> ( + let frame' = (struct_to_substruct p p' inj surj sq).morph frame in + composable (prod_pcm p') x' frame' /\ op (prod_pcm p') x' frame' `feq` v' + )); + assert ((~ (exists (k' : a') . True)) ==> Ghost.reveal x' `feq` one (prod_pcm p')); + match surj k with + | Some k' -> f' v' k' <: b k + | _ -> v k + ) + +#push-options "--query_stats --z3rlimit 64 --split_queries" + +#restart-solver +let substruct_lift_fpu_prf + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (#a': eqtype) + (#b': (a' -> Type)) + (p': (k: a' -> pcm (b' k))) + (inj: (a' -> a)) + (surj: (a -> option a')) + (sq: squash (is_substruct p p' inj surj)) + (x': Ghost.erased (restricted_t a' b') { ~ (Ghost.reveal x' == one (prod_pcm p')) }) + (y': Ghost.erased (restricted_t a' b')) + (f': frame_preserving_upd (prod_pcm p') x' y') + (v: restricted_t a b { + p_refine (prod_pcm p) v /\ + compatible (prod_pcm p) ((substruct_to_struct p p' inj surj sq).morph x') v + }) +: Lemma + (let v_new = substruct_lift_fpu' p p' inj surj sq x' y' f' v in + frame_preserving_upd_post (prod_pcm p) + ((substruct_to_struct p p' inj surj sq).morph x') + ((substruct_to_struct p p' inj surj sq).morph y') + v + (substruct_lift_fpu' p p' inj surj sq x' y' f' v) + ) += + let y = (substruct_to_struct p p' inj surj sq).morph y' in + let v_new = substruct_lift_fpu' p p' inj surj sq x' y' f' v in + let v' = ((struct_to_substruct p p' inj surj sq).morph v) in + let x = Ghost.hide ((substruct_to_struct p p' inj surj sq).morph x') in + assert (forall frame . (composable (prod_pcm p) x frame /\ op (prod_pcm p) x frame == v) ==> ( + let frame' = (struct_to_substruct p p' inj surj sq).morph frame in + composable (prod_pcm p') x' frame' /\ op (prod_pcm p') x' frame' `feq` v' + )); + assert ((~ (exists (k' : a') . True)) ==> Ghost.reveal x' `feq` one (prod_pcm p')); + assert (compatible (prod_pcm p') y' (f' v')); + assert (forall (frame': restricted_t a' b') . + (composable (prod_pcm p') y' frame' /\ op (prod_pcm p') frame' y' == f' v') ==> ( + let frame : restricted_t a b = on_dom a (fun k -> match surj k with None -> v_new k | Some k' -> frame' k' <: b k) in + composable (prod_pcm p) y frame /\ + op (prod_pcm p) frame y `feq` v_new + )); + assert (compatible (prod_pcm p) y v_new); + assert (p_refine (prod_pcm p) v_new); + Classical.forall_intro_2 (fun k -> is_unit (p k)); + let prf (frame: restricted_t a b) : Lemma + (requires ( + composable (prod_pcm p) x frame + )) + (ensures ( + composable (prod_pcm p) x frame /\ + composable (prod_pcm p) y frame /\ + (op (prod_pcm p) x frame == v ==> op (prod_pcm p) y frame `feq` v_new) + )) + = + let frame' = struct_to_substruct_f p p' inj surj sq frame in + assert (composable (prod_pcm p') x' frame'); + assert (composable (prod_pcm p') y' frame'); + assert (op (prod_pcm p) x frame == v ==> op (prod_pcm p') x' frame' `feq` v'); + () + in + Classical.forall_intro (Classical.move_requires prf) + +#pop-options + +let substruct_lift_fpu + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (#a': eqtype) + (#b': (a' -> Type)) + (p': (k: a' -> pcm (b' k))) + (inj: (a' -> a)) + (surj: (a -> option a')) + (sq: squash (is_substruct p p' inj surj)) + (x': Ghost.erased (restricted_t a' b') { ~ (Ghost.reveal x' == one (prod_pcm p')) }) + (y': Ghost.erased (restricted_t a' b')) + (f': frame_preserving_upd (prod_pcm p') x' y') +: Tot (frame_preserving_upd (prod_pcm p) ((substruct_to_struct p p' inj surj sq).morph x') ((substruct_to_struct p p' inj surj sq).morph y')) += fun v -> + substruct_lift_fpu_prf p p' inj surj sq x' y' f' v; + substruct_lift_fpu' p p' inj surj sq x' y' f' v + +let substruct + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (#a': eqtype) + (#b': (a' -> Type)) + (p': (k: a' -> pcm (b' k))) + (inj: (a' -> a)) + (surj: (a -> option a')) + (sq: squash (is_substruct p p' inj surj)) +: Tot (connection (prod_pcm p) (prod_pcm p')) += mkconnection + (substruct_to_struct p p' inj surj sq) + (struct_to_substruct p p' inj surj sq) + (assert (forall x . + struct_to_substruct_f p p' inj surj sq (substruct_to_struct_f p p' inj surj sq x) `feq` x + )) + (substruct_lift_fpu p p' inj surj sq) + +#push-options "--query_stats --z3rlimit 64" + +#restart-solver +let substruct_id + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (inj: (a -> a)) + (surj: (a -> option a)) + (sq: squash ( + (forall x . inj x == x) /\ + (forall x . surj x == Some x) + )) +: Lemma + (substruct p p inj surj () == connection_id (prod_pcm p)) += let l = substruct p p inj surj () in + let m = connection_id (prod_pcm p) in + let _ : squash (l.conn_small_to_large.morph `feq` m.conn_small_to_large.morph) = + assert (forall x . l.conn_small_to_large.morph x `feq` m.conn_small_to_large.morph x) + in + let _ : squash (l.conn_large_to_small.morph `feq` m.conn_large_to_small.morph) = () in + connection_eq_gen + l + m + () + (fun x y f v -> + assert ((l.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f v `feq` (m.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f v) + ) + +#pop-options + +#push-options "--query_stats --z3rlimit 256" + +#restart-solver +let substruct_compose + (#a1: eqtype) + (#b1: a1 -> Type) + (p1:(k: a1 -> pcm (b1 k))) + (#a2: eqtype) + (#b2: (a2 -> Type)) + (p2: (k: a2 -> pcm (b2 k))) + (inj21: (a2 -> a1)) + (surj12: (a1 -> option a2)) + (sq12: squash (is_substruct p1 p2 inj21 surj12)) + (#a3: eqtype) + (#b3: (a3 -> Type)) + (p3: (k: a3 -> pcm (b3 k))) + (inj32: (a3 -> a2)) + (surj23: (a2 -> option a3)) + (sq23: squash (is_substruct p2 p3 inj32 surj23)) + (inj31: (a3 -> a1)) + (surj13: (a1 -> option a3)) + (sq13: squash (is_substruct p1 p3 inj31 surj13)) +: Lemma + (requires ( + (forall x3 . inj31 x3 == inj21 (inj32 x3)) /\ + (forall x1 . surj13 x1 == (match surj12 x1 with + | None -> None + | Some x2 -> surj23 x2 + )))) + (ensures ( + substruct p1 p3 inj31 surj13 sq13 == + substruct p1 p2 inj21 surj12 sq12 `connection_compose` + substruct p2 p3 inj32 surj23 sq23 + )) += + let c12 = substruct p1 p2 inj21 surj12 sq12 in + let c23 = substruct p2 p3 inj32 surj23 sq23 in + let l = substruct p1 p3 inj31 surj13 sq13 in + let m = connection_compose c12 c23 in + let _ : squash (l.conn_small_to_large.morph `feq` m.conn_small_to_large.morph) = + assert (forall x . l.conn_small_to_large.morph x `feq` m.conn_small_to_large.morph x) + in + let _ : squash (l.conn_large_to_small.morph `feq` m.conn_large_to_small.morph) = + assert (forall x . l.conn_large_to_small.morph x `feq` m.conn_large_to_small.morph x) + in + connection_eq_gen + l + m + () + (fun x y f v -> + assert ((l.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f v `feq` (m.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f v) + ) + +#pop-options + +#push-options "--query_stats --z3rlimit 64" + +#restart-solver +let substruct_field + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (#a': eqtype) + (#b': (a' -> Type)) + (p': (k: a' -> pcm (b' k))) + (inj: (a' -> a)) + (surj: (a -> option a')) + (sq: squash (is_substruct p p' inj surj)) + (field': a') +: Lemma + (substruct p p' inj surj sq `connection_compose` struct_field p' field' == + struct_field p (inj field') + ) += + let l = substruct p p' inj surj sq `connection_compose` struct_field p' field' in + let m = struct_field p (inj field') in + let _ : squash (l.conn_small_to_large.morph `feq` m.conn_small_to_large.morph) = + assert (forall x . l.conn_small_to_large.morph x `feq` m.conn_small_to_large.morph x) + in + let _ : squash (l.conn_large_to_small.morph `feq` m.conn_large_to_small.morph) = () in + connection_eq_gen + l + m + () + (fun x y f v -> + assert ((l.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f v `feq` (m.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f v) + ) + +#pop-options + +let substruct_erase_fields + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (#a': eqtype) + (surj: (a -> option a')) + (f: restricted_t a b) +: Tot (restricted_t a b) += on_dom a (fun x -> if Some? (surj x) then one (p x) else f x) + +let substruct_erase_fields_op + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (#a': eqtype) + (#b': (a' -> Type)) + (p': (k: a' -> pcm (b' k))) + (inj: (a' -> a)) + (surj: (a -> option a')) + (sq: squash (is_substruct p p' inj surj)) + (f: restricted_t a b) +: Lemma + ( + let f_sub = substruct_to_struct_f p p' inj surj sq (struct_to_substruct_f p p' inj surj sq f) in + let f_rem = substruct_erase_fields p surj f in + composable (prod_pcm p) f_sub f_rem /\ + op (prod_pcm p) f_sub f_rem `feq` f + ) += Classical.forall_intro_2 (fun k -> is_unit (p k)) + +let substruct_composable + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (#a': eqtype) + (#b': (a' -> Type)) + (p': (k: a' -> pcm (b' k))) + (inj: (a' -> a)) + (surj: (a -> option a')) + (sq: squash (is_substruct p p' inj surj)) + (f: restricted_t a b) + (g': restricted_t a' b') +: Lemma + (requires ( + forall x' . f (inj x') == one (p' x') + )) + (ensures ( + let g = substruct_to_struct_f p p' inj surj sq g' in + composable (prod_pcm p) f g /\ + (forall x . op (prod_pcm p) f g x == (match surj x with None -> f x | Some x' -> g' x' <: b x)) + )) += Classical.forall_intro_2 (fun k -> is_unit (p k)) + +let substruct_pts_to_intro + (#opened: _) + (#base: Type) + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (#a': eqtype) + (#b': (a' -> Type)) + (p': (k: a' -> pcm (b' k))) + (inj: (a' -> a)) + (surj: (a -> option a')) + (sq: squash (is_substruct p p' inj surj)) + (r: ref base (prod_pcm p)) + (f: restricted_t a b) +: STGhostT unit opened + (pts_to r f) + (fun _ -> + pts_to r (substruct_erase_fields p surj f) `star` + pts_to (r `ref_focus` substruct p p' inj surj sq) (struct_to_substruct_f p p' inj surj sq f) + ) += substruct_erase_fields_op p p' inj surj sq f; + split r _ (substruct_erase_fields p surj f) (substruct_to_struct_f p p' inj surj sq (struct_to_substruct_f p p' inj surj sq f)); + gfocus r (substruct p p' inj surj sq) (substruct_to_struct_f _ _ _ _ _ _) _ + +let substruct_pts_to_elim + (#opened: _) + (#base: Type) + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (#a': eqtype) + (#b': (a' -> Type)) + (p': (k: a' -> pcm (b' k))) + (inj: (a' -> a)) + (surj: (a -> option a')) + (sq: squash (is_substruct p p' inj surj)) + (r: ref base (prod_pcm p)) + (f: restricted_t a b) + (g': restricted_t a' b') +: STGhost (Ghost.erased (restricted_t a b)) opened + (pts_to r f `star` pts_to (r `ref_focus` substruct p p' inj surj sq) g') + (fun res -> pts_to r res) + ( + forall x' . f (inj x') == one (p' x') + ) + (fun res -> + let g = substruct_to_struct_f p p' inj surj sq g' in + composable (prod_pcm p) f g /\ + Ghost.reveal res == op (prod_pcm p) f g /\ + (forall x . Ghost.reveal res x == (match surj x with None -> f x | Some x' -> g' x' <: b x)) + ) += substruct_composable p p' inj surj sq f g'; + let g = substruct_to_struct_f p p' inj surj sq g' in + let res = Ghost.hide (op (prod_pcm p) f g) in + unfocus (r `ref_focus` _) r (substruct p p' inj surj sq) _; + let _ = gather r f _ in + rewrite + (pts_to r _) + (pts_to r res); + res + +let exclusive_struct_intro + (#a: Type) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (x: restricted_t a b) +: Lemma + (requires ( + forall k . exclusive (p k) (struct_to_field_f p k x) + )) + (ensures ( + exclusive (prod_pcm p) x + )) + [SMTPat (exclusive (prod_pcm p) x)] += + assert (forall frame . prod_comp p x frame ==> frame `feq` prod_one p) + +let exclusive_struct_elim + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (x: restricted_t a b) + (k: a) +: Lemma + (requires (exclusive (prod_pcm p) x)) + (ensures (exclusive (p k) (struct_to_field_f p k x))) += + let phi + frame + : Lemma + (requires (composable (p k) (struct_to_field_f p k x) frame)) + (ensures (composable (prod_pcm p) x (field_to_struct_f p k frame))) + [SMTPat (composable (p k) (struct_to_field_f p k x) frame)] + = let x' = struct_to_field_f p k x in + let f' = field_to_struct_f p k frame in + let psi + k' + : Lemma + (composable (p k') (x k') (f' k')) + [SMTPat (composable (p k') (x k') (f' k'))] + = if k' = k + then () + else is_unit (p k') (x k') + in + () + in + () + +let struct_without_field (#a:eqtype) (#b: a -> Type u#b) (p:(k:a -> pcm (b k))) (k:a) + (xs: restricted_t a b) +: restricted_t a b += on_dom a (fun k' -> if k' = k then one (p k) else xs k') + +let struct_peel (#a:eqtype) (#b: a -> Type u#b) (p:(k:a -> pcm (b k))) (k:a) + (xs: restricted_t a b) +: Lemma ( + composable (prod_pcm p) (struct_without_field p k xs) (field_to_struct_f p k (xs k)) /\ + xs == op (prod_pcm p) (struct_without_field p k xs) (field_to_struct_f p k (xs k))) += Classical.forall_intro_2 (fun k -> is_unit (p k)); + assert (xs `feq` op (prod_pcm p) (struct_without_field p k xs) (field_to_struct_f p k (xs k))) + +let g_addr_of_struct_field + (#opened: _) + (#base:Type) (#a:eqtype) (#b: a -> Type u#b) (#p:(k:a -> pcm (b k))) + (r: ref base (prod_pcm p)) (k:a) + (xs: Ghost.erased (restricted_t a b)) +: STGhostT unit opened + (r `pts_to` xs) + (fun _ -> + (r `pts_to` struct_without_field p k xs) `star` + (ref_focus r (struct_field p k) `pts_to` Ghost.reveal xs k)) += struct_peel p k xs; + split r xs (struct_without_field p k xs) (field_to_struct_f p k (Ghost.reveal xs k)); + rewrite (r `pts_to` _) (r `pts_to` _); + gfocus r (struct_field p k) (field_to_struct_f p k (Ghost.reveal xs k)) (Ghost.reveal xs k) + +let addr_of_struct_field + (#opened: _) + (#base:Type) (#a:eqtype) (#b: a -> Type u#b) (#p:(k:a -> pcm (b k))) + (r: ref base (prod_pcm p)) (k:a) + (xs: Ghost.erased (restricted_t a b)) +: STAtomicBase (ref base (p k)) false opened Unobservable + (r `pts_to` xs) + (fun s -> + (r `pts_to` struct_without_field p k xs) `star` + (s `pts_to` Ghost.reveal xs k)) + (requires True) + (ensures fun r' -> r' == ref_focus r (struct_field p k)) += struct_peel p k xs; + split r xs (struct_without_field p k xs) (field_to_struct_f p k (Ghost.reveal xs k)); + rewrite (r `pts_to` _) (r `pts_to` _); + let r = focus r (struct_field p k) (field_to_struct_f p k (Ghost.reveal xs k)) (Ghost.reveal xs k) in + rewrite (r `pts_to` _) (r `pts_to` _); + return r + +(* +let ptr_addr_of_struct_field + (#base:Type) (#a:eqtype) (#b: a -> Type u#b) (#p:(k:a -> pcm (b k))) + (r: Ptr.ptr base (prod_pcm p)) (k:a) + (xs: Ghost.erased (restricted_t a b)) +: Steel (ref base (p k)) + (r `pts_to` xs) + (fun s -> + (r `pts_to` struct_without_field p k xs) `star` + (s `pts_to` Ghost.reveal xs k)) + (requires fun _ -> True) + (ensures fun _ r' _ -> r' == ref_focus r (struct_field p k)) += struct_peel p k xs; + split r xs (struct_without_field p k xs) (field_to_struct_f p k (Ghost.reveal xs k)); + let r = focus r (struct_field p k) (field_to_struct_f p k (Ghost.reveal xs k)) (Ghost.reveal xs k) in + return r +*) + +let struct_with_field (#a:eqtype) (#b: a -> Type u#b) (p:(k:a -> pcm (b k))) (k:a) + (x:b k) (xs: restricted_t a b) +: restricted_t a b += on_dom a (fun k' -> if k' = k then x else xs k') + +let struct_unpeel (#a:eqtype) (#b: a -> Type u#b) (p:(k:a -> pcm (b k))) (k:a) + (x: b k) (xs: restricted_t a b) +: Lemma + (requires xs k == one (p k)) + (ensures + composable (prod_pcm p) xs (field_to_struct_f p k x) /\ + struct_with_field p k x xs == op (prod_pcm p) xs (field_to_struct_f p k x)) += Classical.forall_intro_2 (fun k -> is_unit (p k)); + assert (struct_with_field p k x xs `feq` op (prod_pcm p) xs (field_to_struct_f p k x)) + +let unaddr_of_struct_field + (#opened: _) + (#base:Type) (#a:eqtype) (#b: a -> Type u#b) (#p:(k:a -> pcm (b k))) (k:a) + (r': ref base (p k)) (r: ref base (prod_pcm p)) + (xs: Ghost.erased (restricted_t a b)) (x: Ghost.erased (b k)) +: STGhost unit opened + ((r `pts_to` xs) `star` (r' `pts_to` x)) + (fun s -> r `pts_to` struct_with_field p k x xs) + (requires r' == ref_focus r (struct_field p k) /\ Ghost.reveal xs k == one (p k)) + (ensures fun _ -> True) += unfocus r' r (struct_field p k) x; + let _ = gather r xs (field_to_struct_f p k x) in + struct_unpeel p k x xs; + rewrite (r `pts_to` _) (r `pts_to` _) diff --git a/lib/steel/c/Steel.ST.C.Model.Union.fst b/lib/steel/c/Steel.ST.C.Model.Union.fst new file mode 100644 index 000000000..321c7cbaf --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Model.Union.fst @@ -0,0 +1,484 @@ +module Steel.ST.C.Model.Union +open Steel.ST.Util + +module P = FStar.PCM +open Steel.C.Model.PCM +open Steel.C.Model.Connection +open Steel.ST.C.Model.Ref +open Steel.ST.C.Model.Struct + +(** A PCM for unions *) + +open FStar.FunctionalExtensionality + +let case_refinement_f (p:(k:'a -> pcm ('b k))) (k:'a) (f: restricted_t 'a 'b): prop = + forall k'. ~ (k == k') ==> f k' == one (p k') + +let case_refinement_f_intro (p:(k:'a -> pcm ('b k))) (k:'a) (f: restricted_t 'a 'b) + (h:(k':'a{~ (k == k')} -> Lemma (f k' == one (p k')))) +: Lemma (case_refinement_f p k f) = FStar.Classical.forall_intro h + +let case_refinement_f_uniq (p:(k:'a -> pcm ('b k))) (j k:'a) (f: restricted_t 'a 'b) +: Lemma + (requires case_refinement_f p j f /\ case_refinement_f p k f /\ ~ (j == k)) + (ensures f == one (prod_pcm p)) += ext f (one (prod_pcm p)) (fun k -> ()) + +let is_union (#a:Type) (#b:a->Type) (p:(k:a -> pcm (b k))) (f: restricted_t a b) = + (exists (k:a). True) ==> (exists k. case_refinement_f p k f) + (** precondition is there because we don't care if 'a is inhabited *) + +let union (#a:Type) (#b:a->Type) (p:(k:a -> pcm (b k))) = f:restricted_t a b{is_union p f} + +let union_elim (p:(k:'a -> pcm ('b k))) (f: union p) (goal:Type) + (cont:(k:'a -> Lemma (requires case_refinement_f p k f) (ensures goal) + [SMTPat (case_refinement_f p k f)])) +: Lemma (forall (j:'a). goal) += let _ = cont in () + +let is_union_intro (p:(k:'a -> pcm ('b k))) (f: restricted_t 'a 'b) + (k:'a{case_refinement_f p k f}) +: Lemma (is_union p f) += () + +let union_comp0 (p:(k:'a -> pcm ('b k))) (f g: union p) : Tot prop = + forall j k. + ~ (f j == one (p j)) /\ ~ (g k == one (p k)) ==> + j == k /\ composable (p k) (f k) (g k) + +let union_comp (p:(k:'a -> pcm ('b k))) : Tot (P.symrel (union p)) = + union_comp0 p + +let union_comp_intro (p:(k:'a -> pcm ('b k))) (f g: union p) + (h:(j:'a -> k:'a -> + Lemma + (requires ~ (f j == one (p j)) /\ ~ (g k == one (p k))) + (ensures j == k /\ composable (p k) (f k) (g k)) + [SMTPat (f j); SMTPat (g k)])) +: Lemma (union_comp p f g) += let _ = h in () + +let union_comp_elim (p:(k:'a -> pcm ('b k))) (f g: union p) + (j:'a) (k:'a) +: Lemma + (requires (union_comp p f g /\ ~ (f j == one (p j)) /\ ~ (g k == one (p k)))) + (ensures j == k /\ composable (p k) (f k) (g k)) += () + +let union_comp_prod_comp (p:(k:'a -> pcm ('b k))) (f g: union p) +: Lemma + (requires union_comp p f g) + (ensures prod_comp p f g) + [SMTPat (union_comp p f g)] += prod_pcm_composable_intro p f g (fun k -> is_unit (p k) (f k); is_unit (p k) (g k)) + +let case_refinement_f_one (p:(k:'a -> pcm ('b k))) (k:'a) (f: restricted_t 'a 'b) +: Lemma + (requires case_refinement_f p k f /\ f k == one (p k)) + (ensures f == one (prod_pcm p)) + [SMTPat (case_refinement_f p k f); SMTPat (f k == one (p k))] += ext f (one (prod_pcm p)) (fun _ -> ()) + +let case_refinement_f_op (p:(k:'a -> pcm ('b k))) (j k:'a) (f g: restricted_t 'a 'b) +: Lemma + (requires case_refinement_f p j f /\ case_refinement_f p k g /\ union_comp p f g) + (ensures + f == one (prod_pcm p) \/ + g == one (prod_pcm p) \/ + case_refinement_f p k (prod_op p f g)) + [SMTPat (case_refinement_f p j f); SMTPat (case_refinement_f p k g)] += let fj_or_gk_one + : squash + (f j == one (p j) \/ g k == one (p k) ==> + feq f (one (prod_pcm p)) \/ feq g (one (prod_pcm p))) + = () + in let fj_gk_both_not_one () + : Lemma + (requires ~ (f j == one (p j)) /\ ~ (g k == one (p k))) + (ensures case_refinement_f p k (prod_op p f g)) + = case_refinement_f_intro p k (prod_op p f g) (fun k' -> is_unit (p k') (g k')) + in + FStar.Classical.move_requires fj_gk_both_not_one (); + assert + ((f j == one (p j) \/ g k == one (p k)) ==> + f == one (prod_pcm p) \/ + g == one (prod_pcm p) \/ + case_refinement_f p k (prod_op p f g)) + +let union_op (p:(k:'a -> pcm ('b k))) (f: union p) (g: union p{union_comp p f g}): union p = + let h = prod_op p f g in + let goal = is_union p h in + union_elim p f goal (fun j -> + union_elim p g goal (fun k -> + case_refinement_f_op p j k f g; + is_unit (prod_pcm p) g)); + h + +let union_one (p:(k:'a -> pcm ('b k))): union p = prod_one p + +let union_refine (p:(k:'a -> pcm ('b k))) (u: union p): Tot prop = exists k. p_refine (p k) (u k) + +let union_assoc (p:(k:'a -> pcm ('b k))) + (x y: union p) + (z: union p{union_comp p y z /\ union_comp p x (union_op p y z)}) +: Lemma (union_comp p x y /\ + union_comp p (union_op p x y) z /\ + union_op p x (union_op p y z) == union_op p (union_op p x y) z) += prod_assoc p x y z; + union_comp_intro p x y (fun j k -> is_unit (prod_pcm p) y); + union_comp_intro p (union_op p x y) z (fun j k -> ()); + assert (union_op p x (union_op p y z) `feq` union_op p (union_op p x y) z) + +let union_assoc_r (p:(k:'a -> pcm ('b k))) + (x y: union p) + (z: union p{union_comp p x y /\ union_comp p (union_op p x y) z}) +: Lemma (union_comp p y z /\ + union_comp p x (union_op p y z) /\ + union_op p x (union_op p y z) == union_op p (union_op p x y) z) += prod_assoc_r p x y z; + union_comp_intro p x y (fun j k -> is_unit (prod_pcm p) y); + union_comp_intro p (union_op p x y) z (fun j k -> ()); + assert (union_op p x (union_op p y z) `feq` union_op p (union_op p x y) z) + +let union_is_unit (p:(k:'a -> pcm ('b k))) (x: union p) +: Lemma (union_comp p x (union_one p) /\ + union_op p x (union_one p) == x) += is_unit (prod_pcm p) x + +let fstar_union_pcm (p:(k:'a -> pcm ('b k))): P.pcm (union p) = let open P in { + FStar.PCM.p = {composable = union_comp p; op = union_op p; one = union_one p}; + comm = (fun x y -> prod_comm p x y); + assoc = union_assoc p; + assoc_r = union_assoc_r p; + is_unit = union_is_unit p; + refine = union_refine p; + } + +let union_pcm' (p:(k:'a -> pcm ('b k))): pcm0 (union p) = pcm_of_fstar_pcm (fstar_union_pcm p) + +let union_pcm (p:(k:'a -> pcm ('b k))): pcm (union p) = + let p' = union_pcm' p in + let aux (x:union p) (y:union p{composable p' x y}) + : Lemma (requires op p' x y == one p') (ensures x == one p' /\ y == one p') + [SMTPat (op p' x y)] + = ext x (one p') (fun k -> let _ = p k in ()); + ext y (one p') (fun k -> let _ = p k in ()) + in + assert (forall x frame . (union_refine p x /\ union_comp p x frame) ==> frame `feq` union_one p); + union_pcm' p + +let union_pcm_composable_intro0 + (p:(k:'a -> pcm ('b k))) + (x y: union p) +: Lemma + ((composable (union_pcm p) x y <==> union_comp p x y) /\ + (composable (union_pcm p) x y ==> op (union_pcm p) x y == union_op p x y)) + [SMTPat (composable (union_pcm p) x y)] += () + +let union_comp_intro0 (p:(k:'a -> pcm ('b k))) (f g: union p) + (h:(j:'a -> k:'a -> + Lemma + (requires ~ (f j == one (p j)) /\ ~ (g k == one (p k))) + (ensures j == k /\ composable (p k) (f k) (g k)) + [SMTPat (f j); SMTPat (g k)])) +: Lemma (composable (union_pcm p) f g) += let _ = h in () + +let union_comp_elim0 (p:(k:'a -> pcm ('b k))) (f g: union p) + (j:'a) (k:'a) +: Lemma + (requires (composable (union_pcm p) f g /\ ~ (f j == one (p j)) /\ ~ (g k == one (p k)))) + (ensures j == k /\ composable (p k) (f k) (g k)) += () + +let field_to_union_f + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (k: a) + (x: b k) +: Pure (union p) + (requires True) + (ensures (fun y -> forall k' . y k' == (if k' = k then (x <: b k') else one (p k')))) += on_dom a (fun k' -> if k' = k then (x <: b k') else one (p k')) + +let field_to_union + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (k: a) +: Tot (morphism (p k) (union_pcm p)) += mkmorphism + (field_to_union_f p k) + (assert (field_to_union_f p k (one (p k)) `feq` one (union_pcm p))) + (fun x1 x2 -> + Classical.forall_intro_2 (fun k -> is_unit (p k)); + assert (union_op p (field_to_union_f p k x1) (field_to_union_f p k x2) `feq` field_to_union_f p k (op (p k) x1 x2)); + ()) + +let field_to_union_elim (#a: eqtype) (#b: a -> Type) (p: (k: a -> pcm (b k))) + (k: a) + (x: b k) + (k': a) +: Lemma + (requires (~ ((field_to_union p k).morph x k' == one (p k')))) + (ensures (k == k')) += () + +let union_to_field_f + (#a: Type) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (k: a) + (x: union p) +: Tot (b k) += x k + +let union_to_field + (#a: Type) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (k: a) +: Tot (morphism (union_pcm p) (p k)) += mkmorphism + (union_to_field_f p k) () + (fun x1 x2 -> ()) + +let union_field_lift_fpu' + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (k: a) + (x: Ghost.erased (b k) { ~ (Ghost.reveal x == one (p k)) }) + (y: Ghost.erased (b k)) + (f: frame_preserving_upd (p k) x y) + (v: frame_preserving_upd_dom (union_pcm p) ((field_to_struct p k).morph x)) +: Tot (union p) += + on_dom a (fun k' -> + if k' = k + then f (v k) <: b k' + else one (p k') + ) + +#restart-solver + +#push-options "--z3rlimit 30 --query_stats --fuel 2 --ifuel 4" + +let union_field_lift_fpu0_prf1 + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (k: a) + (x: Ghost.erased (b k) { ~ (Ghost.reveal x == one (p k)) }) + (y: Ghost.erased (b k)) + (f: frame_preserving_upd (p k) x y) + (v: frame_preserving_upd_dom (union_pcm p) ((field_to_union p k).morph x)) +: Lemma + (frame_preserving_upd_goal1 (union_pcm p) ((field_to_union p k).morph x) ((field_to_union p k).morph y) (union_field_lift_fpu' p k x y f) v) += + let y' = (field_to_union p k).morph y in + let v_new = union_field_lift_fpu' p k x y f v in + assert (p_refine (union_pcm p) v_new); + Classical.forall_intro_2 (fun k -> is_unit (p k)); + let frame : b k = compatible_elim (p k) y (f (v k)) in + let frame' : union p = on_dom a (fun k' -> if k' = k then (frame <: b k') else one (p k')) in + assert (composable (union_pcm p) y' frame'); + assert (op (union_pcm p) frame' y' `feq` v_new); + compatible_intro (union_pcm p) y' v_new frame' + +#pop-options + +#restart-solver + +#push-options "--query_stats --fuel 2 --ifuel 4 --z3rlimit 64" + +let union_field_lift_fpu0_prf2 + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (k: a) + (x: Ghost.erased (b k) { ~ (Ghost.reveal x == one (p k)) }) + (y: Ghost.erased (b k)) + (f: frame_preserving_upd (p k) x y) + (v: frame_preserving_upd_dom (union_pcm p) ((field_to_union p k).morph x)) + (frame: union p) +: Lemma + (requires ( + frame_preserving_upd_goal2_pre (union_pcm p) ((field_to_union p k).morph x) ((field_to_union p k).morph y) (union_field_lift_fpu' p k x y f) v frame + )) + (ensures ( + frame_preserving_upd_goal2_post (union_pcm p) ((field_to_union p k).morph x) ((field_to_union p k).morph y) (union_field_lift_fpu' p k x y f) v frame + )) += + union_comp_intro0 + p + ((field_to_union p k).morph y) + frame + (fun j' k' -> + field_to_union_elim p k y j'; + union_comp_elim0 p ((field_to_union p k).morph x) frame k k'; + let _ = f (v k) in + assert (composable (p k) x (frame k)); + assert (composable (p k) y (frame k)) + ) + +#pop-options + +#push-options "--query_stats --fuel 2 --ifuel 4 --z3rlimit 128" + +#restart-solver + +let union_field_lift_fpu0_prf3 + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (k: a) + (x: Ghost.erased (b k) { ~ (Ghost.reveal x == one (p k)) }) + (y: Ghost.erased (b k)) + (f: frame_preserving_upd (p k) x y) + (v: frame_preserving_upd_dom (union_pcm p) ((field_to_union p k).morph x)) + (frame: union p) +: Lemma + (requires ( + frame_preserving_upd_goal3_pre (union_pcm p) ((field_to_union p k).morph x) ((field_to_union p k).morph y) (union_field_lift_fpu' p k x y f) v frame + )) + (ensures ( + frame_preserving_upd_goal3_post (union_pcm p) ((field_to_union p k).morph x) ((field_to_union p k).morph y) (union_field_lift_fpu' p k x y f) v frame + )) += + let w = op (union_pcm p) ((field_to_union p k).morph x) frame in + union_pcm_composable_intro0 p ((field_to_union p k).morph x) frame; + assert (w == union_op p ((field_to_union p k).morph x) frame); + assert (w == prod_op p ((field_to_union p k).morph x) frame); + assert (w k == op (p k) x (frame k)); + assert (w k == v k); + let v'k = f (v k) in + let w' = op (union_pcm p) ((field_to_union p k).morph y) frame in + union_pcm_composable_intro0 p ((field_to_union p k).morph y) frame; + assert (w' == union_op p ((field_to_union p k).morph y) frame); + assert (w' == prod_op p ((field_to_union p k).morph y) frame); + assert (w' k == op (p k) y (frame k)); + assert (w' k == v'k); + assert (union_op p ((field_to_union p k).morph y) frame `feq` + union_field_lift_fpu' p k x y f v) + +#pop-options + +let union_field_lift_fpu + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (k: a) + (x: Ghost.erased (b k) { ~ (Ghost.reveal x == one (p k)) }) + (y: Ghost.erased (b k)) + (f: frame_preserving_upd (p k) x y) +: Tot (frame_preserving_upd (union_pcm p) ((field_to_union p k).morph x) ((field_to_union p k).morph y)) += + let y' = Ghost.hide ((field_to_union p k).morph y) in + frame_preserving_upd_intro + (union_pcm p) + ((field_to_union p k).morph x) + ((field_to_union p k).morph y) + (union_field_lift_fpu' p k x y f) + (union_field_lift_fpu0_prf1 p k x y f) + (union_field_lift_fpu0_prf2 p k x y f) + (union_field_lift_fpu0_prf3 p k x y f) + +let union_field + (#a: eqtype) + (#b: a -> Type) + (p:(k: a -> pcm (b k))) + (k: a) +: Tot (connection (union_pcm p) (p k)) += mkconnection + (field_to_union p k) + (union_to_field p k) + () + (union_field_lift_fpu p k) + +module I = FStar.IndefiniteDescription +let case_of_union (p:(k:'a -> pcm ('b k))) (u: union p) +: GTot (k:option 'a{match k with Some k -> ~ (u k == one (p k)) | None -> u == one (union_pcm p)}) += if I.strong_excluded_middle (exists k. ~ (u k == one (p k))) then + let k = I.indefinite_description_ghost 'a (fun k -> ~ (u k == one (p k))) in + Some k + else begin + assert (u `feq` one (union_pcm p)); + None + end + +let exclusive_union_intro + (#a: Type) + (#b: _) + (p:(k: a -> pcm (b k))) + (x: union p) + (k: a) +: Lemma + (requires (exclusive (p k) (x k) /\ (~ (x k == one (p k))))) + (ensures (exclusive (union_pcm p) x)) += let phi + (frame: union p) + : Lemma + (requires (composable (union_pcm p) x frame)) + (ensures (frame `feq` union_one p)) + [SMTPat (composable (union_pcm p) x frame)] + = () + in + () + +let exclusive_union_elim + (#a: eqtype) + (#b: _) + (p: (k: a -> pcm (b k))) + (x: union p) + (k: a) +: Lemma + (requires (exclusive (union_pcm p) x)) + (ensures (x k == one (p k) \/ exclusive (p k) (x k))) += if FStar.StrongExcludedMiddle.strong_excluded_middle (x k == one (p k)) + then () + else + let phi + (frame: b k) + : Lemma + (requires (composable (p k) (x k) frame)) + (ensures (frame == one (p k))) + [SMTPat (composable (p k) (x k) frame)] + = let frame' = field_to_union_f p k frame in + () + in + () + +let union_peel (#a:eqtype) #b (p:(k:a -> pcm (b k))) (k:a) + (xs: union p{~ (xs k == one (p k))}) +: Lemma (xs == field_to_union_f p k (xs k)) += assert (xs `feq` field_to_union_f p k (xs k)) + +let addr_of_union_field + #base (#a:eqtype) #b (#p:(k:a -> pcm (b k))) + (r: ref base (union_pcm p)) (k:a) + (xs: Ghost.erased (union p)) +: ST (ref base (p k)) + (r `pts_to` xs) + (fun r' -> r' `pts_to` Ghost.reveal xs k) + (requires ~ (Ghost.reveal xs k == one (p k))) + (ensures fun r' -> r' == ref_focus r (union_field p k)) += union_peel p k xs; + rewrite (r `pts_to` xs) (r `pts_to` _); + let s = focus r (union_field p k) (field_to_union_f p k (Ghost.reveal xs k)) (Ghost.reveal xs k) in + rewrite (s `pts_to` _) (s `pts_to` _); + return s + +module M = Steel.Memory +let unaddr_of_union_field + (#opened:M.inames) #base (#a:eqtype) #b (#p:(k:a -> pcm (b k))) (k:a) + (r': ref base (p k)) (r: ref base (union_pcm p)) + (x: Ghost.erased (b k)) +: STGhost unit opened + (r' `pts_to` x) + (fun s -> r `pts_to` field_to_union_f p k x) + (requires r' == ref_focus r (union_field p k)) + (ensures fun _ -> True) += unfocus r' r (union_field p k) x diff --git a/lib/steel/c/Steel.ST.C.Opt.fst b/lib/steel/c/Steel.ST.C.Opt.fst new file mode 100644 index 000000000..e03b4b3d7 --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Opt.fst @@ -0,0 +1,46 @@ +module Steel.ST.C.Opt + +let opt_read r = + rewrite (r `pts_to` _) (r `pts_to` _); + let Some x = ref_read r in + x + +let opt_write #a #b #x r y = + rewrite (r `pts_to` _) (r `pts_to` _); + ref_upd r (Some (Ghost.reveal x)) (Some y) (fun (Some _) -> Some y); + rewrite (r `pts_to` _) (r `pts_to` _) + +let exclusive_opt + #a x += + match x with + | None -> + if FStar.StrongExcludedMiddle.strong_excluded_middle (exists (y: a). True) + then begin + let y = FStar.IndefiniteDescription.indefinite_description_ghost a (fun _ -> True) in + assert (composable opt_pcm x (Some y)) + end else begin + let phi + (frame: option a) + : Lemma + (frame == None) + = match frame with + | None -> () + | Some z -> assert (exists (y: a) . True) + in + Classical.forall_intro phi + end + | Some _ -> () + +let opt_pcm_read + #a #b r x += rewrite (r `pts_to` _) (r `pts_to` _); + let y' = ref_read r in + assert (Ghost.reveal x == y'); + Some?.v y' + +let opt_pcm_write + #a #b r x y += rewrite (r `pts_to` _) (r `pts_to` _); + ref_upd r x (Some y) (opt_pcm_fpu x y); + rewrite (r `pts_to` _) (r `pts_to` _) diff --git a/lib/steel/c/Steel.ST.C.Opt.fsti b/lib/steel/c/Steel.ST.C.Opt.fsti new file mode 100644 index 000000000..1b5fb94e1 --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Opt.fsti @@ -0,0 +1,78 @@ +module Steel.ST.C.Opt + +module P = FStar.PCM +open Steel.ST.Util +open Steel.C.Model.PCM +open Steel.ST.C.Model.Ref + +/// If no custom PCM is needed, p and q can be instantiated with an all-or-none PCM: + +let opt_comp (x y: option 'a): prop = match x, y with + | None, _ | _, None -> True + | _, _ -> False + +let opt_op (x: option 'a) (y: option 'a{opt_comp x y}): option 'a = match x, y with + | None, z | z, None -> z + +let fstar_opt_pcm #a : P.pcm (option a) = let open P in { + p = {composable = opt_comp; op = opt_op; one = None}; + comm = (fun _ _ -> ()); + assoc = (fun _ _ _ -> ()); + assoc_r = (fun _ _ _ -> ()); + is_unit = (fun _ -> ()); + refine = (fun x -> Some? x == true); +} + +let opt_pcm #a : pcm (option a) = pcm_of_fstar_pcm fstar_opt_pcm + +let option: Type u#a -> Type u#a = option + +let none #a: Ghost.erased (option a) = None + +[@@__reduce__] +let some (x: Ghost.erased 'a): Ghost.erased (option 'a) = Some (Ghost.reveal x) + +let some_v (x: Ghost.erased (option 'a){Some? x}): Ghost.erased 'a = Some?.v x + +val opt_read + (#a:Type) (#b:Type) (#x: Ghost.erased b) + (r: ref a (opt_pcm #b)) +: ST b + (r `pts_to` Some #b x) + (fun _ -> r `pts_to` Some #b x) + (requires True) + (ensures fun x' -> Ghost.reveal x == x') + +val opt_write + (#a:Type) (#b:Type) (#x: Ghost.erased b) + (r: ref a (opt_pcm #b)) (y: b) +: STT unit + (r `pts_to` Some #b x) + (fun _ -> r `pts_to` Some y) + +val exclusive_opt + (#a: Type) + (x: option a) +: Lemma + (exclusive opt_pcm x <==> ((exists (y: a) . True) ==> Some? x)) + +let opt_pcm_fpu + (#a: Type) + (x: Ghost.erased (option a) { ~ (Ghost.reveal x == one opt_pcm) }) + (y: a) +: Tot (frame_preserving_upd opt_pcm x (Some y)) += base_fpu opt_pcm x (Some y) + +val opt_pcm_read + (#a:Type) (#b: Type) + (r: ref a (opt_pcm #b)) (x: Ghost.erased (option b)) +: ST b (r `pts_to` x) (fun _ -> r `pts_to` x) + (requires (Some? x)) + (ensures (fun y -> Ghost.reveal x == Some y)) + +val opt_pcm_write + (#a:Type) (#b: Type) + (r: ref a (opt_pcm #b)) (x: Ghost.erased (option b)) (y: b) +: ST unit (r `pts_to` x) (fun _ -> r `pts_to` Some y) + (requires (Some? x)) + (ensures (fun _ -> True)) diff --git a/lib/steel/c/Steel.ST.C.Types.Array.fsti b/lib/steel/c/Steel.ST.C.Types.Array.fsti new file mode 100644 index 000000000..4e38d9ac6 --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Types.Array.fsti @@ -0,0 +1,977 @@ +module Steel.ST.C.Types.Array +open Steel.ST.Util +include Steel.ST.C.Types.Base +open Steel.C.Typenat + +module P = Steel.FractionalPermission +module SZ = FStar.SizeT + +// To be extracted as: t[tn] +// Per the C standard, base array types must be of nonzero size +inline_for_extraction [@@noextract_to "krml"] +let array_size_t = (n: SZ.t { SZ.v n > 0 }) +val base_array_t ([@@@strictly_positive] t: Type0) (tn: Type0 (* using Typenat (or Typestring for `#define`d constants) *)) (n: array_size_t) : Type0 +inline_for_extraction [@@noextract_to "krml"] // MUST be syntactically equal to Steel.C.Model.Array.array_domain +let base_array_index_t (n: array_size_t) : Tot eqtype = (i: SZ.t { SZ.v i < SZ.v n }) +[@@noextract_to "krml"] +val base_array0 (#t: Type0) (tn: Type0) (td: typedef t) (n: array_size_t) : Tot (typedef (base_array_t t tn n)) + +inline_for_extraction +[@@noextract_to "krml"] // proof-only +let base_array (#t: Type0) (#tn: Type0) (td: typedef t) (n: nat {SZ.fits n /\ n > 0}) (# [solve_nat_t_of_nat ()] prf: squash (norm norm_typenat (nat_t_of_nat n == tn))) : Tot (typedef (base_array_t t tn (SZ.uint_to_t n))) += base_array0 tn td (SZ.uint_to_t n) + +val base_array_index (#t: Type0) (#tn: Type0) (#n: array_size_t) (a: base_array_t t tn n) (i: base_array_index_t n) : GTot t +val base_array_eq (#t: Type0) (#tn: Type0) (#n: array_size_t) (a1 a2: base_array_t t tn n) : Ghost prop + (requires True) + (ensures (fun y -> + (y <==> (a1 == a2)) /\ + (y <==> (forall (i: base_array_index_t n) . base_array_index a1 i == base_array_index a2 i)) + )) +val mk_base_array (#t: Type) (tn: Type0) (n: array_size_t) (v: Seq.seq t) : Ghost (base_array_t t tn n) + (requires ( + Seq.length v == SZ.v n + )) + (ensures (fun y -> True)) +val mk_base_array_index (#t: Type) (tn: Type) (n: array_size_t) (v: Seq.seq t) (i: base_array_index_t n) : Lemma + (requires (Seq.length v == SZ.v n)) + (ensures ( + Seq.length v == SZ.v n /\ + base_array_index (mk_base_array tn n v) i == Seq.index v (SZ.v i) + )) + [SMTPat (base_array_index (mk_base_array tn n v) i)] + +let mk_base_array_inj (#t: Type) (tn: Type0) (n: array_size_t) (v1 v2: Seq.seq t) : Lemma + (requires ( + Seq.length v1 == SZ.v n /\ + Seq.length v2 == SZ.v n /\ + mk_base_array tn n v1 == mk_base_array tn n v2 + )) + (ensures (v1 == v2)) + [SMTPat (mk_base_array tn n v1); SMTPat (mk_base_array tn n v2)] += assert (forall (i: nat) . i < SZ.v n ==> base_array_index (mk_base_array tn n v1) (SZ.uint_to_t i) == base_array_index (mk_base_array tn n v2) (SZ.uint_to_t i)); + assert (v1 `Seq.equal` v2) +val base_array_fractionable (#t: Type) (#tn: Type0) (#n: array_size_t) (a: base_array_t t tn n) (td: typedef t) : Lemma + ( + fractionable (base_array0 tn td n) a <==> + (forall (i: base_array_index_t n) . fractionable td (base_array_index a i)) + ) + [SMTPat (fractionable (base_array0 tn td n) a)] +val base_array_mk_fraction (#t: Type) (#tn: Type0) (#n: array_size_t) (a: base_array_t t tn n) (td: typedef t) (p: P.perm) (i: base_array_index_t n) : Lemma + (requires ( + fractionable (base_array0 tn td n) a + )) + (ensures ( + fractionable (base_array0 tn td n) a /\ + base_array_index (mk_fraction (base_array0 tn td n) a p) i == mk_fraction td (base_array_index a i) p + )) + [SMTPat (base_array_index (mk_fraction (base_array0 tn td n) a p) i)] + +val base_array_index_unknown (#t: Type) (tn: Type0) (n: array_size_t) (td: typedef t) (i: base_array_index_t n) : Lemma + (base_array_index (unknown (base_array0 tn td n)) i == unknown td) + [SMTPat (base_array_index (unknown (base_array0 tn td n)) i)] + +val base_array_index_uninitialized (#t: Type) (tn: Type0) (n: array_size_t) (td: typedef t) (i: base_array_index_t n) : Lemma + (base_array_index (uninitialized (base_array0 tn td n)) i == uninitialized td) + [SMTPat (base_array_index (uninitialized (base_array0 tn td n)) i)] + +val base_array_index_full (#t: Type) (#tn: Type0) (#n: array_size_t) (td: typedef t) (x: base_array_t t tn n) : Lemma + (full (base_array0 tn td n) x <==> (forall (i: base_array_index_t n) . full td (base_array_index x i))) + [SMTPat (full (base_array0 tn td n) x)] + +val has_base_array_cell + (#t: Type) + (#tn: Type0) + (#n: array_size_t) + (#td: typedef t) + (r: ref (base_array0 tn td n)) + (i: SZ.t) + (r': ref td) +: Tot vprop + +val has_base_array_cell_post + (#opened: _) + (#t: Type) + (#tn: Type0) + (#n: array_size_t) + (#td: typedef t) + (r: ref (base_array0 tn td n)) + (i: SZ.t) + (r': ref td) +: STGhost unit opened + (has_base_array_cell r i r') + (fun _ -> has_base_array_cell r i r') + (True) + (fun _ -> SZ.v i < SZ.v n) + +val has_base_array_cell_dup + (#opened: _) + (#t: Type) + (#tn: Type0) + (#n: array_size_t) + (#td: typedef t) + (r: ref (base_array0 tn td n)) + (i: SZ.t) + (r': ref td) +: STGhostT unit opened + (has_base_array_cell r i r') + (fun _ -> has_base_array_cell r i r' `star` has_base_array_cell r i r') + +val has_base_array_cell_inj + (#opened: _) + (#t: Type) + (#tn: Type0) + (#n: array_size_t) + (#td: typedef t) + (r: ref (base_array0 tn td n)) + (i: SZ.t) + (r1 r2: ref td) +: STGhostT unit opened + (has_base_array_cell r i r1 `star` has_base_array_cell r i r2) + (fun _ -> has_base_array_cell r i r1 `star` has_base_array_cell r i r2 `star` ref_equiv r1 r2) + +val has_base_array_cell_equiv_from + (#opened: _) + (#t: Type) + (#tn: Type0) + (#n: array_size_t) + (#td: typedef t) + (r1 r2: ref (base_array0 tn td n)) + (i: SZ.t) + (r': ref td) +: STGhostT unit opened + (has_base_array_cell r1 i r' `star` ref_equiv r1 r2) + (fun _ -> has_base_array_cell r2 i r' `star` ref_equiv r1 r2) + +val has_base_array_cell_equiv_to + (#opened: _) + (#t: Type) + (#tn: Type0) + (#n: array_size_t) + (#td: typedef t) + (r: ref (base_array0 tn td n)) + (i: SZ.t) + (r1 r2: ref td) +: STGhostT unit opened + (has_base_array_cell r i r1 `star` ref_equiv r1 r2) + (fun _ -> has_base_array_cell r i r2 `star` ref_equiv r1 r2) + +// contrary to array fields, one is not supposed to take an array cell directly from a base array. one should use arrays instead + +// To be extracted to: t* (array type decays to pointer type) + +// We still want to prove that cutting off some cell range on the +// right-hand end of an array won't change the C pointer to which an +// array extracts to. This is why we separately introduce `array_ref` +// to represent the "base+offset" pointer, and `array` which holds the +// ghost length of an array. + +[@@noextract_to "krml"] // primitive +val array_ptr_gen ([@@@strictly_positive] t: Type0) : Tot Type0 +inline_for_extraction [@@noextract_to "krml"] // primitive +let array_ptr (#t: Type) (td: typedef t) = array_ptr_gen t +[@@noextract_to "krml"] // primitive +val null_array_ptr (#t: Type) (td: typedef t) : Tot (array_ptr td) +val g_array_ptr_is_null (#t: Type) (#td: typedef t) (a: array_ptr td) : Ghost bool + (requires True) + (ensures (fun y -> y == true <==> a == null_array_ptr _)) +inline_for_extraction [@@noextract_to "krml"] +let array_ref (#t: Type) (td: typedef t) = (a: array_ptr td { g_array_ptr_is_null a == false }) + +(* +val array_ref_base_size_type (#t: Type) (#td: typedef t) (a: array_ref td) : GTot Type0 +*) +val array_ref_base_size (#t: Type) (#td: typedef t) (a: array_ptr td) : Ghost SZ.t + (requires True) + (ensures (fun y -> SZ.v y == 0 <==> a == null_array_ptr _)) +val has_array_ref_base (#t: Type) (#td: typedef t) (a: array_ref td) (#ty: Type) (r: ref (base_array0 ty td (array_ref_base_size a))) : GTot prop +val has_array_ref_base_inj (#t: Type) (#td: typedef t) (a: array_ref td) (#ty: Type) (r1 r2: ref (base_array0 ty td (array_ref_base_size a))) : Lemma + (requires (has_array_ref_base a r1 /\ has_array_ref_base a r2)) + (ensures (r1 == r2)) +val array_ref_offset (#t: Type) (#td: typedef t) (a: array_ptr td) : Ghost SZ.t + (requires True) + (ensures (fun y -> SZ.v y <= SZ.v (array_ref_base_size a))) +val array_ref_base_offset_inj (#t: Type) (#td: typedef t) (#ty: Type) (a1: array_ref td) (r1: ref (base_array0 ty td (array_ref_base_size a1))) (a2: array_ref td) (r2: ref (base_array0 ty td (array_ref_base_size a2))) : Lemma + (requires ( + array_ref_base_size a1 == array_ref_base_size a2 /\ + has_array_ref_base a1 r1 /\ + has_array_ref_base a2 r2 /\ + r1 == coerce_eq () r2 /\ + array_ref_offset a1 == array_ref_offset a2 + )) + (ensures (a1 == a2)) + +inline_for_extraction [@@noextract_to "krml"] +let array_len_t (#t: Type) (#td: typedef t) (r: array_ptr td) : Tot Type0 = + (len: Ghost.erased SZ.t { SZ.v (array_ref_offset r) + SZ.v len <= SZ.v (array_ref_base_size r) }) + +inline_for_extraction [@@noextract_to "krml"] +let array_or_null (#t: Type) (td: typedef t) : Tot Type0 = (r: array_ptr td & array_len_t r) + +inline_for_extraction [@@noextract_to "krml"] +let array_ptr_of (#t: Type) (#td: typedef t) (ar: array_or_null td) : Tot (array_ptr td) = + match ar with + | (| a, _ |) -> a + +let g_array_is_null (#t: Type) (#td: typedef t) (a: array_or_null td) : GTot bool = + g_array_ptr_is_null (array_ptr_of a) + +inline_for_extraction [@@noextract_to "krml"] +let array (#t: Type) (td: typedef t) : Tot Type0 = (a: array_or_null td { g_array_is_null a == false }) + +let array_length + (#t: Type) + (#td: typedef t) + (a: array td) +: GTot nat += SZ.v (dsnd a) + +val array_pts_to + (#t: Type) + (#td: typedef t) + (r: array td) + (v: Ghost.erased (Seq.seq t)) +: Tot vprop + +let array_pts_to_or_null + (#t: Type) + (#td: typedef t) + (r: array_or_null td) + (v: Ghost.erased (Seq.seq t)) +: Tot vprop += if g_array_is_null r + then emp + else array_pts_to r v + +[@@noextract_to "krml"] // primitive +val array_ptr_is_null + (#t: Type) + (#opened: _) + (#td: typedef t) + (#v: Ghost.erased (Seq.seq t)) + (r: array_ptr td) + (len: array_len_t r) +: STAtomicBase bool false opened Unobservable + (array_pts_to_or_null (| r, len |) v) + (fun _ -> array_pts_to_or_null (| r, len |) v) + (True) + (fun b -> b == g_array_is_null (| r, len |)) + +inline_for_extraction [@@noextract_to "krml"] +let array_is_null + (#t: Type) + (#opened: _) + (#td: typedef t) + (#v: Ghost.erased (Seq.seq t)) + (r: array_or_null td) +: STAtomicBase bool false opened Unobservable + (array_pts_to_or_null r v) + (fun _ -> array_pts_to_or_null r v) + (True) + (fun b -> b == g_array_is_null r) += let a = array_ptr_of r in + let len : array_len_t a = dsnd r in + rewrite (array_pts_to_or_null _ _) (array_pts_to_or_null (| a, len |) v); + let res = array_ptr_is_null a len in + rewrite (array_pts_to_or_null _ _) (array_pts_to_or_null r v); + return res + +val array_pts_to_length + (#opened: _) + (#t: Type) + (#td: typedef t) + (r: array td) + (v: Ghost.erased (Seq.seq t)) +: STGhost unit opened + (array_pts_to r v) + (fun _ -> array_pts_to r v) + (True) + (fun _ -> Seq.length v == SZ.v (dsnd r)) + +#set-options "--print_implicits" + +let has_array_of_base + (#t: Type) + (#tn: Type0) + (#n: array_size_t) + (#td: typedef t) + (r: ref (base_array0 tn td n)) + (a: array td) +: GTot prop += let (| al, len |) = a in + array_ref_base_size al == n /\ + has_array_ref_base al #tn r /\ + array_ref_offset al == 0sz /\ + Ghost.reveal len == n + +let has_array_of_base_inj + (#t: Type) + (#tn: Type0) + (#n: array_size_t) + (#td: typedef t) + (r: ref (base_array0 tn td n)) + (a1 a2: array td) +: Lemma + (requires ( + has_array_of_base #t #tn #n #td r a1 /\ + has_array_of_base #t #tn #n #td r a2 + )) + (ensures (a1 == a2)) += let (| ar1, _ |) = a1 in + let (| ar2, _ |) = a2 in + array_ref_base_offset_inj #t #td #tn ar1 r ar2 r + +let seq_of_base_array + (#t: Type) + (#tn: Type) + (#n: array_size_t) + (v: base_array_t t tn n) +: GTot (Seq.lseq t (SZ.v n)) += Seq.init_ghost (SZ.v n) (fun i -> base_array_index v (SZ.uint_to_t i)) + +val ghost_array_of_base_focus + (#t: Type) + (#tn: Type0) + (#opened: _) + (#n: array_size_t) + (#td: typedef t) + (#v: Ghost.erased (base_array_t t tn n)) + (r: ref (base_array0 tn td n)) + (a: array td) +: STGhost unit opened + (pts_to r v) + (fun _ -> array_pts_to a (seq_of_base_array v)) + (has_array_of_base r a) + (fun _ -> True) + +val ghost_array_of_base + (#t: Type) + (#tn: Type0) + (#opened: _) + (#n: array_size_t) + (#td: typedef t) + (#v: Ghost.erased (base_array_t t tn n)) + (r: ref (base_array0 tn td n)) +: STGhostT (a: Ghost.erased (array td) { has_array_of_base r a }) opened + (pts_to r v) + (fun a -> array_pts_to a (seq_of_base_array v)) + +let array_ref_of_base_post + (#t: Type) + (#tn: Type0) + (#n: Ghost.erased array_size_t) + (#td: typedef t) + (v: Ghost.erased (base_array_t t tn n)) + (r: ref (base_array0 tn td n)) + (a: array_ref td) + (ar: array td) +: GTot prop += + array_ptr_of ar == a /\ + array_ref_base_size a == Ghost.reveal n /\ + array_ref_offset a == 0sz /\ + has_array_of_base r ar /\ + Ghost.reveal (dsnd ar) == Ghost.reveal n + +// to be extracted to just r +[@@noextract_to "krml"] // primitive +val array_ref_of_base + (#t: Type) + (#tn: Type0) + (#opened: _) + (#n: Ghost.erased array_size_t) + (#td: typedef t) + (#v: Ghost.erased (base_array_t t tn n)) + (r: ref (base_array0 tn td n)) +: STAtomicBase (array_ref td) false opened Unobservable + (pts_to r v) + (fun a -> exists_ (fun (ar: array td) -> + array_pts_to ar (seq_of_base_array v) `star` pure ( + array_ref_of_base_post v r a ar + ))) + (True) + (fun _ -> True) + +inline_for_extraction [@@noextract_to "krml"] +let array_of_base + (#t: Type) + (#tn: Type0) + (#opened: _) + (#n: Ghost.erased array_size_t) + (#td: typedef t) + (#v: Ghost.erased (base_array_t t tn n)) + (r: ref (base_array0 tn td n)) +: STAtomicBase (a: array td { has_array_of_base r a }) false opened Unobservable + (pts_to r v) + (fun a -> array_pts_to a (seq_of_base_array v)) + (True) + (fun _ -> True) += let al = array_ref_of_base r in + let _ = elim_exists () in + elim_pure _; + let a = (| al, Ghost.hide (n <: SZ.t) |) in + rewrite (array_pts_to _ _) (array_pts_to _ _); + return a + +val unarray_of_base + (#t: Type) + (#tn: Type0) + (#opened: _) + (#n: array_size_t) + (#td: typedef t) + (#v: Ghost.erased (Seq.seq t)) + (r: ref (base_array0 tn td n)) + (a: array td) +: STGhost (Ghost.erased (base_array_t t tn n)) opened + (array_pts_to a v) + (fun v' -> pts_to r v') + ( + has_array_of_base r a + ) + (fun v' -> Ghost.reveal v `Seq.equal` seq_of_base_array v') + +val freeable_array + (#t: Type) + (#td: typedef t) + (a: array td) +: Tot vprop + +let freeable_or_null_array + (#t: Type) + (#td: typedef t) + (a: array_or_null td) +: Tot vprop += if g_array_is_null a + then emp + else freeable_array a + +[@@noextract_to "krml"] // primitive +val array_ptr_alloc + (#t: Type) + (td: typedef t) + (sz: SZ.t { SZ.v sz > 0 }) +: STT (array_ptr td) + emp + (fun a -> + exists_ (fun (ar: array_or_null td) -> exists_ (fun (s: Seq.seq t) -> + freeable_or_null_array ar `star` + array_pts_to_or_null ar s `star` pure ( + array_ptr_of ar == a /\ + (g_array_is_null ar == false ==> array_length ar == SZ.v sz) /\ + Ghost.reveal s `Seq.equal` FStar.Seq.create (SZ.v sz) (uninitialized td) + )))) + +inline_for_extraction [@@noextract_to "krml"] +let array_alloc + (#t: Type) + (td: typedef t) + (sz: SZ.t { SZ.v sz > 0 }) +: STT (array_or_null td) + emp + (fun ar -> + freeable_or_null_array ar `star` + exists_ (fun s -> + array_pts_to_or_null ar s `star` pure ( + (g_array_is_null ar == false ==> array_length ar == SZ.v sz) /\ + Ghost.reveal s == FStar.Seq.create (SZ.v sz) (uninitialized td) + ))) += let a : array_ptr td = array_ptr_alloc td sz in + let ar' : Ghost.erased (array_or_null td) = elim_exists () in + let s = elim_exists () in + elim_pure _; + let len : array_len_t a = dsnd ar' in + let ar = (| a, len |) in + rewrite (array_pts_to_or_null _ _) (array_pts_to_or_null ar s); + rewrite (freeable_or_null_array _) (freeable_or_null_array ar); + noop (); + return ar + +let full_seq (#t: Type) (td: typedef t) (v: Seq.seq t) : GTot prop = + forall (i: nat { i < Seq.length v }) . {:pattern (Seq.index v i)} full td (Seq.index v i) + +let full_seq_seq_of_base_array + (#t: Type0) (tn: Type0) (td: typedef t) (#n: array_size_t) + (b: base_array_t t tn n) +: Lemma + (ensures (full_seq td (seq_of_base_array b) <==> full (base_array0 tn td n) b)) + [SMTPat (full_seq td (seq_of_base_array b))] += assert (forall (i: base_array_index_t n) . base_array_index b i == Seq.index (seq_of_base_array b) (SZ.v i)) + +[@@noextract_to "krml"] // primitive +val array_ref_free + (#t: Type) + (#td: typedef t) + (#s: Ghost.erased (Seq.seq t)) + (a: array_ref td) + (n: array_len_t a) +: ST unit + (freeable_array (| a, n |) `star` array_pts_to (| a, n |) s) + (fun _ -> emp) + (full_seq td s) + (fun _ -> True) + +inline_for_extraction [@@noextract_to "krml"] +let array_free + (#t: Type) + (#td: typedef t) + (#s: Ghost.erased (Seq.seq t)) + (a: array td) +: ST unit + (freeable_array a `star` array_pts_to a s) + (fun _ -> emp) + (full_seq td s) + (fun _ -> True) += let al = array_ptr_of a in + let n: array_len_t al = dsnd a in + rewrite (freeable_array _) (freeable_array (| al, n |)); + rewrite (array_pts_to _ _) (array_pts_to (| al, n |) s); + array_ref_free al n + +(* +val has_array_of_ref + (#t: Type) + (#td: typedef t) + (r: ref td) + (a: array td) +: Tot vprop + +val has_array_of_ref_post + (#opened: _) + (#t: Type) + (#td: typedef t) + (r: ref td) + (a: array td) +: STGhost unit opened + (has_array_of_ref r a) + (fun _ -> has_array_of_ref r a) + (True) + (fun _ -> + let (| al, len |) = a in + array_ref_base_size al == 1sz /\ + array_ref_offset al == 0sz /\ + Ghost.reveal len == 1sz + ) + +// val has_array_of_ref_inj +// (#t: Type) +// (#td: typedef t) +// (r: ref td) +// (a1 a2: array td) +// : Lemma +// (requires ( +// has_array_of_ref r a1 /\ +// has_array_of_ref r a2 +// )) +// (ensures a1 == a2) + +val ghost_array_of_ref_focus + (#t: Type) + (#opened: _) + (#td: typedef t) + (#v: Ghost.erased t) + (r: ref td) + (a: array td) +: STGhostT unit opened + (pts_to r v `star` has_array_of_ref r a) + (fun _ -> has_array_of_ref r a `star` array_pts_to a (Seq.create 1 (Ghost.reveal v))) + +val ghost_array_of_ref + (#t: Type) + (#opened: _) + (#td: typedef t) + (#v: Ghost.erased t) + (r: ref td) +: STGhostT (Ghost.erased (array td)) opened + (pts_to r v) + (fun a -> array_pts_to a (Seq.create 1 (Ghost.reveal v)) `star` has_array_of_ref r a) + +// to be extracted to just r +[@@noextract_to "krml"] // primitive +val array_ref_of_ref + (#t: Type) + (#td: typedef t) + (#v: Ghost.erased t) + (r: ref td) +: STT (a: array_ref td { array_ref_base_size a == 1sz /\ array_ref_offset a == 0sz }) + (pts_to r v) + (fun a -> array_pts_to (| a, Ghost.hide 1sz |) (Seq.create 1 (Ghost.reveal v)) `star` has_array_of_ref r (| a, Ghost.hide 1sz |)) + +inline_for_extraction [@@noextract_to "krml"] +let array_of_ref + (#t: Type) + (#td: typedef t) + (#v: Ghost.erased t) + (r: ref td) +: STT (array td) + (pts_to r v) + (fun a -> array_pts_to a (Seq.create 1 (Ghost.reveal v)) `star` has_array_of_ref r a) += let al = array_ref_of_ref r in + let a : array td = (| al, Ghost.hide 1sz |) in + rewrite (array_pts_to _ _) (array_pts_to _ _); + rewrite (has_array_of_ref _ _) (has_array_of_ref r a); + return a + +val unarray_of_ref + (#t: Type) + (#opened: _) + (#td: typedef t) + (#s: Ghost.erased (Seq.seq t)) + (r: ref td) + (a: array td) +: STGhostT (squash (Seq.length s == 1)) opened + (array_pts_to a s `star` has_array_of_ref r a) + (fun _ -> pts_to r (Seq.index s 0) `star` has_array_of_ref r a) +*) + +val has_array_cell + (#t: Type) + (#td: typedef t) + (a: array td) + (i: SZ.t) + (r: ref td) +: Tot vprop +(* += SZ.v i < SZ.v (dsnd a) /\ + has_base_array_cell (array_ref_base (array_ptr_of a)) (array_ref_offset (array_ptr_of a) `SZ.add` i) r +*) + +val has_array_cell_post + (#opened: _) + (#t: Type) + (#td: typedef t) + (a: array td) + (i: SZ.t) + (r': ref td) +: STGhost unit opened + (has_array_cell a i r') + (fun _ -> has_array_cell a i r') + (True) + (fun _ -> SZ.v i < SZ.v (dsnd a)) + +val has_array_cell_has_base_array_cell + (#opened: _) + (#t: Type) + (#td: typedef t) + (a: array td) + (i: SZ.t) + (r: ref td) + (#ty: Type) + (br: ref (base_array0 ty td (array_ref_base_size (array_ptr_of a)))) +: STGhost (Ghost.erased SZ.t) opened + (has_array_cell a i r) + (fun j -> has_base_array_cell br j r) + (has_array_ref_base (array_ptr_of a) br) + (fun j -> + SZ.v j == SZ.v (array_ref_offset (array_ptr_of a)) + SZ.v i + ) + +val has_base_array_cell_has_array_cell + (#opened: _) + (#t: Type) + (#td: typedef t) + (a: array td) + (i: SZ.t) + (r: ref td) + (#ty: Type) + (br: ref (base_array0 ty td (array_ref_base_size (array_ptr_of a)))) +: STGhost (Ghost.erased SZ.t) opened + (has_base_array_cell br i r) + (fun j -> has_array_cell a j r) + (has_array_ref_base (array_ptr_of a) br /\ + SZ.v i >= SZ.v (array_ref_offset (array_ptr_of a)) /\ + SZ.v i < SZ.v (array_ref_offset (array_ptr_of a)) + SZ.v (dsnd a) + ) + (fun j -> + SZ.v i == SZ.v (array_ref_offset (array_ptr_of a)) + SZ.v j + ) + +val has_array_cell_inj + (#opened: _) + (#t: Type) + (#td: typedef t) + (a: array td) + (i: SZ.t) + (r1 r2: ref td) +: STGhostT unit opened + ( + has_array_cell a i r1 `star` + has_array_cell a i r2 + ) + (fun _ -> + has_array_cell a i r1 `star` + has_array_cell a i r2 `star` + ref_equiv r1 r2 + ) +// = has_base_array_cell_inj (array_ref_base (array_ptr_of a)) (array_ref_offset (array_ptr_of a) `SZ.add` i) r1 r2 + +(* +val has_array_cell_array_of_ref + (#opened: _) + (#t: Type) + (#td: typedef t) + (r: ref td) + (a: array td) +: SteelGhostT unit opened + (has_array_of_ref r a) + (fun _ -> has_array_of_ref r a `star` has_array_cell a 0sz r) +*) + +val ghost_array_cell_focus + (#opened: _) + (#t: Type) + (#td: typedef t) + (#s: Ghost.erased (Seq.seq t)) + (a: array td) + (i: SZ.t) + (r: ref td) +: STGhostT (squash (SZ.v i < Seq.length s /\ Seq.length s == SZ.v (dsnd a))) opened + (array_pts_to a s `star` has_array_cell a i r) + (fun _ -> array_pts_to a (Seq.upd s (SZ.v i) (unknown td)) `star` pts_to r (Seq.index s (SZ.v i)) `star` has_array_cell a i r) + +val ghost_array_cell + (#opened: _) + (#t: Type) + (#td: typedef t) + (#s: Ghost.erased (Seq.seq t)) + (a: array td) + (i: SZ.t) +: STGhost (r: Ghost.erased (ref td) { SZ.v i < Seq.length s /\ Seq.length s == SZ.v (dsnd a) }) opened + (array_pts_to a s) + (fun r -> array_pts_to a (Seq.upd s (SZ.v i) (unknown td)) `star` pts_to r (Seq.index s (SZ.v i)) `star` has_array_cell a i r) + ( + (SZ.v i < Seq.length s \/ SZ.v i < SZ.v (dsnd a)) + ) + (fun _ -> True) + +[@@noextract_to "krml"] // primitive +val array_ref_cell + (#t: Type) + (#td: typedef t) + (#s: Ghost.erased (Seq.seq t)) + (a: array_ref td) + (len: array_len_t a) + (i: SZ.t) +: ST (r: ref td { SZ.v i < Seq.length s /\ Seq.length s == SZ.v len }) + (array_pts_to (| a, len |) s) + (fun r -> array_pts_to (| a, len |) (Seq.upd s (SZ.v i) (unknown td)) `star` pts_to r (Seq.index s (SZ.v i)) `star` has_array_cell (| a, len |) i r) + ( + (SZ.v i < Seq.length s \/ SZ.v i < SZ.v len) + ) + (fun _ -> True) + +inline_for_extraction [@@noextract_to "krml"] +let array_cell + (#t: Type) + (#td: typedef t) + (#s: Ghost.erased (Seq.seq t)) + (a: array td) + (i: SZ.t) +: ST (r: ref td { SZ.v i < Seq.length s /\ Seq.length s == SZ.v (dsnd a) }) + (array_pts_to a s) + (fun r -> array_pts_to a (Seq.upd s (SZ.v i) (unknown td)) `star` pts_to r (Seq.index s (SZ.v i)) `star` has_array_cell a i r) + ( + (SZ.v i < Seq.length s \/ SZ.v i < SZ.v (dsnd a)) + ) + (fun _ -> True) += let (| al, len |) = a in + rewrite (array_pts_to _ _) (array_pts_to _ s); + let r = array_ref_cell al len i in + rewrite (array_pts_to _ _) (array_pts_to _ _); + rewrite (has_array_cell _ _ _) (has_array_cell a i r); + return r + +val unarray_cell + (#opened: _) + (#t: Type) + (#td: typedef t) + (#s: Ghost.erased (Seq.seq t)) + (#v: Ghost.erased t) + (a: array td) + (i: SZ.t) + (r: ref td) +: STGhost (squash (SZ.v i < Seq.length s /\ Seq.length s == SZ.v (dsnd a))) opened + (array_pts_to a s `star` pts_to r v `star` has_array_cell a i r) + (fun _ -> array_pts_to a (Seq.upd s (SZ.v i) v) `star` has_array_cell a i r) + ( + (SZ.v i < Seq.length s ==> Seq.index s (SZ.v i) == unknown td) + ) + (fun _ -> True) + +val array_ref_shift + (#t: Type) + (#td: typedef t) + (a: array_ref td) + (i: SZ.t) +: Ghost (array_ref td) + (requires (SZ.v (array_ref_offset a) + SZ.v i <= SZ.v (array_ref_base_size a))) + (ensures (fun y -> + array_ref_base_size y == array_ref_base_size a /\ + (forall ty r . has_array_ref_base a #ty r ==> has_array_ref_base y #ty (coerce_eq () r)) /\ + array_ref_offset y == array_ref_offset a `SZ.add` i + )) + +val array_ref_shift_zero + (#t: Type) + (#td: typedef t) + (a: array_ref td) +: Lemma + (ensures ( + array_ref_shift a 0sz == a + )) + +val array_ref_shift_assoc + (#t: Type) + (#td: typedef t) + (a: array_ref td) + (i1 i2: SZ.t) +: Lemma + (requires (SZ.v (array_ref_offset a) + SZ.v i1 + SZ.v i2 <= SZ.v (array_ref_base_size a))) + (ensures ( + array_ref_shift a (SZ.add i1 i2) == array_ref_shift (array_ref_shift a i1) i2 + )) + +inline_for_extraction [@@noextract_to "krml"] +let array_split_l + (#t: Type) + (#td: typedef t) + (a: array td) + (i: SZ.t) +: Pure (array td) + (requires (SZ.v i <= SZ.v (dsnd a))) + (ensures (fun _ -> True)) += let (| al, _ |) = a in + (| al, Ghost.hide i |) + +let array_split_r + (#t: Type) + (#td: typedef t) + (a: array td) + (i: SZ.t) +: Ghost (array td) + (requires (SZ.v i <= SZ.v (dsnd a))) + (ensures (fun _ -> True)) += let (| al, len |) = a in + (| array_ref_shift al i, Ghost.hide (len `SZ.sub` i) |) + +val ghost_array_split + (#opened: _) + (#t: Type) + (#td: typedef t) + (#s: Ghost.erased (Seq.seq t)) + (a: array td) + (i: SZ.t) +: STGhost (squash (SZ.v i <= SZ.v (dsnd a) /\ Seq.length s == SZ.v (dsnd a))) opened + (array_pts_to a s) + (fun _ -> array_pts_to (array_split_l a i) (Seq.slice s 0 (SZ.v i)) `star` + array_pts_to (array_split_r a i) (Seq.slice s (SZ.v i) (Seq.length s))) + (SZ.v i <= SZ.v (dsnd a) \/ SZ.v i <= Seq.length s) + (fun _ -> True) + +[@@noextract_to "krml"] // primitive +val array_ref_split + (#t: Type) + (#td: typedef t) + (#s: Ghost.erased (Seq.seq t)) + (al: array_ref td) + (len: array_len_t al) + (i: SZ.t) +: ST (ar: array_ref td { SZ.v i <= SZ.v len /\ Seq.length s == SZ.v len}) + (array_pts_to (| al, len |) s) + (fun _ -> array_pts_to (array_split_l (| al, len |) i) (Seq.slice s 0 (SZ.v i)) `star` + array_pts_to (array_split_r (| al, len |) i) (Seq.slice s (SZ.v i) (Seq.length s))) + (SZ.v i <= SZ.v len \/ SZ.v i <= Seq.length s) + (fun ar -> ar == array_ptr_of (array_split_r (| al, len |) i)) + +inline_for_extraction [@@noextract_to "krml"] +let array_split + (#t: Type) + (#td: typedef t) + (#s: Ghost.erased (Seq.seq t)) + (a: array td) + (i: SZ.t) +: ST (a': array td {SZ.v i <= SZ.v (dsnd a) /\ Seq.length s == SZ.v (dsnd a)}) + (array_pts_to a s) + (fun a' -> array_pts_to (array_split_l a i) (Seq.slice s 0 (SZ.v i)) `star` + array_pts_to a' (Seq.slice s (SZ.v i) (Seq.length s))) + (SZ.v i <= SZ.v (dsnd a) \/ SZ.v i <= Seq.length s) + (fun a' -> a' == array_split_r a i) += let (| al, len |) = a in + rewrite (array_pts_to _ _) (array_pts_to _ s); + let ar = array_ref_split al len i in + let a' = (| ar, Ghost.hide (len `SZ.sub` i) |) in + rewrite (array_pts_to (array_split_l _ _) _) (array_pts_to (array_split_l a _) _); + rewrite (array_pts_to (array_split_r _ _) _) (array_pts_to a' _); + return a' + +val array_join + (#opened: _) + (#t: Type) + (#td: typedef t) + (#sl #sr: Ghost.erased (Seq.seq t)) + (a al ar: array td) + (i: SZ.t) +: STGhost unit opened + (array_pts_to al sl `star` array_pts_to ar sr) + (fun _ -> array_pts_to a (sl `Seq.append` sr)) + ( + SZ.v i <= SZ.v (dsnd a) /\ + al == array_split_l a i /\ + ar == array_split_r a i + ) + (fun _ -> True) + +let fractionable_seq (#t: Type) (td: typedef t) (s: Seq.seq t) : GTot prop = + forall (i: nat). i < Seq.length s ==> fractionable td (Seq.index s i) + +let mk_fraction_seq (#t: Type) (td: typedef t) (s: Seq.seq t) (p: P.perm) : Ghost (Seq.seq t) + (requires (fractionable_seq td s)) + (ensures (fun _ -> True)) += Seq.init_ghost (Seq.length s) (fun i -> mk_fraction td (Seq.index s i) p) + +let mk_fraction_seq_full (#t: Type0) (td: typedef t) (x: Seq.seq t) : Lemma + (requires (fractionable_seq td x)) + (ensures (mk_fraction_seq td x P.full_perm == x)) + [SMTPat (mk_fraction_seq td x P.full_perm)] += assert (mk_fraction_seq td x P.full_perm `Seq.equal` x) + +val mk_fraction_seq_split_gen + (#opened: _) + (#t: Type) (#td: typedef t) (r: array td) (v: Seq.seq t { fractionable_seq td v }) (p p1 p2: P.perm) +: STGhost unit opened + (array_pts_to r (mk_fraction_seq td v p)) + (fun _ -> array_pts_to r (mk_fraction_seq td v p1) `star` array_pts_to r (mk_fraction_seq td v p2)) + (p == p1 `P.sum_perm` p2 /\ (p `P.lesser_equal_perm` P.full_perm \/ Seq.length v == 0)) + (fun _ -> True) + +let mk_fraction_seq_split + (#opened: _) + (#t: Type) (#td: typedef t) (r: array td) (v: Ghost.erased (Seq.seq t) { fractionable_seq td v }) (p1 p2: P.perm) +: STGhost unit opened + (array_pts_to r v) + (fun _ -> array_pts_to r (mk_fraction_seq td v p1) `star` array_pts_to r (mk_fraction_seq td v p2)) + (P.full_perm == p1 `P.sum_perm` p2) + (fun _ -> True) += mk_fraction_seq_full td v; + rewrite (array_pts_to _ _) (array_pts_to _ _); + mk_fraction_seq_split_gen r v P.full_perm p1 p2 + +val mk_fraction_seq_join + (#opened: _) + (#t: Type) (#td: typedef t) (r: array td) (v: Seq.seq t { fractionable_seq td v }) (p1 p2: P.perm) +: STGhostT unit opened + (array_pts_to r (mk_fraction_seq td v p1) `star` array_pts_to r (mk_fraction_seq td v p2)) + (fun _ -> array_pts_to r (mk_fraction_seq td v (p1 `P.sum_perm` p2))) + +val array_fractional_permissions_theorem + (#opened: _) + (#t: Type) + (#td: typedef t) + (v1: Seq.seq t { fractionable_seq td v1 }) + (v2: Seq.seq t { fractionable_seq td v2 }) + (p1 p2: P.perm) + (r: array td) +: STGhost unit opened + (array_pts_to r (mk_fraction_seq td v1 p1) `star` array_pts_to r (mk_fraction_seq td v2 p2)) + (fun _ -> array_pts_to r (mk_fraction_seq td v1 p1) `star` array_pts_to r (mk_fraction_seq td v2 p2)) + (full_seq td v1 /\ full_seq td v2) + (fun _ -> v1 == v2 /\ (array_length r > 0 ==> (p1 `P.sum_perm` p2) `P.lesser_equal_perm` P.full_perm)) diff --git a/lib/steel/c/Steel.ST.C.Types.Base.fsti b/lib/steel/c/Steel.ST.C.Types.Base.fsti new file mode 100644 index 000000000..17d79145d --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Types.Base.fsti @@ -0,0 +1,269 @@ +module Steel.ST.C.Types.Base +open Steel.ST.Util + +module P = Steel.FractionalPermission + +/// Helper to compose two permissions into one +val prod_perm (p1 p2: P.perm) : Pure P.perm + (requires True) + (ensures (fun p -> + ((p1 `P.lesser_equal_perm` P.full_perm /\ p2 `P.lesser_equal_perm` P.full_perm) ==> + p `P.lesser_equal_perm` P.full_perm) /\ + p.v == (let open FStar.Real in p1.v *. p2.v) + )) + +[@@noextract_to "krml"] // proof-only +val typedef (t: Type0) : Type0 + +inline_for_extraction [@@noextract_to "krml"] +let typeof (#t: Type0) (td: typedef t) : Tot Type0 = t + +val fractionable (#t: Type0) (td: typedef t) (x: t) : GTot prop + +val mk_fraction (#t: Type0) (td: typedef t) (x: t) (p: P.perm) : Ghost t + (requires (fractionable td x)) + (ensures (fun y -> p `P.lesser_equal_perm` P.full_perm ==> fractionable td y)) + +val mk_fraction_full (#t: Type0) (td: typedef t) (x: t) : Lemma + (requires (fractionable td x)) + (ensures (mk_fraction td x P.full_perm == x)) + [SMTPat (mk_fraction td x P.full_perm)] + +val mk_fraction_compose (#t: Type0) (td: typedef t) (x: t) (p1 p2: P.perm) : Lemma + (requires (fractionable td x /\ p1 `P.lesser_equal_perm` P.full_perm /\ p2 `P.lesser_equal_perm` P.full_perm)) + (ensures (mk_fraction td (mk_fraction td x p1) p2 == mk_fraction td x (p1 `prod_perm` p2))) + +val full (#t: Type0) (td: typedef t) (v: t) : GTot prop + +val uninitialized (#t: Type0) (td: typedef t) : Ghost t + (requires True) + (ensures (fun y -> full td y /\ fractionable td y)) + +val unknown (#t: Type0) (td: typedef t) : Ghost t + (requires True) + (ensures (fun y -> fractionable td y)) + +val full_not_unknown + (#t: Type) + (td: typedef t) + (v: t) +: Lemma + (requires (full td v)) + (ensures (~ (v == unknown td))) + [SMTPat (full td v)] + +val mk_fraction_unknown (#t: Type0) (td: typedef t) (p: P.perm) : Lemma + (ensures (mk_fraction td (unknown td) p == unknown td)) + +val mk_fraction_eq_unknown (#t: Type0) (td: typedef t) (v: t) (p: P.perm) : Lemma + (requires (fractionable td v /\ mk_fraction td v p == unknown td)) + (ensures (v == unknown td)) + + +// To be extracted as: void* + +// FIXME: Currently, Karamel does not directly support +// void*. examples/steel/arraystructs currently has a stopgap in +// lib/steel_c.h, whose contents should be moved to krmllib.h, unless +// direct support for void* is added to Karamel. + +[@@noextract_to "krml"] // primitive +val void_ptr : Type0 + +// To be extracted as: NULL +[@@noextract_to "krml"] // primitive +val void_null: void_ptr + +// To be extracted as: *t +[@@noextract_to "krml"] // primitive +let ptr_gen (t: Type) : Tot Type0 = void_ptr +[@@noextract_to "krml"] // primitive +let null_gen (t: Type) : Tot (ptr_gen t) = void_null + +inline_for_extraction [@@noextract_to "krml"] // primitive +let ptr (#t: Type) (td: typedef t) : Tot Type0 = ptr_gen t +inline_for_extraction [@@noextract_to "krml"] // primitive +let null (#t: Type) (td: typedef t) : Tot (ptr td) = null_gen t + +inline_for_extraction [@@noextract_to "krml"] +let ref (#t: Type) (td: typedef t) : Tot Type0 = (p: ptr td { ~ (p == null td) }) + +val pts_to (#t: Type) (#td: typedef t) (r: ref td) ([@@@smt_fallback] v: Ghost.erased t) : vprop + +let pts_to_or_null + (#t: Type) (#td: typedef t) (p: ptr td) (v: Ghost.erased t) : vprop += if FStar.StrongExcludedMiddle.strong_excluded_middle (p == null _) + then emp + else pts_to p v + +[@@noextract_to "krml"] // primitive +val is_null + (#t: Type) + (#opened: _) + (#td: typedef t) + (#v: Ghost.erased t) + (p: ptr td) +: STAtomicBase bool false opened Unobservable + (pts_to_or_null p v) + (fun _ -> pts_to_or_null p v) + (True) + (fun res -> res == true <==> p == null _) + +let assert_null + (#t: Type) + (#opened: _) + (#td: typedef t) + (#v: Ghost.erased t) + (p: ptr td) +: STGhost unit opened + (pts_to_or_null p v) + (fun _ -> emp) + (p == null _) + (fun _ -> True) += rewrite (pts_to_or_null p v) emp + +let assert_not_null + (#t: Type) + (#opened: _) + (#td: typedef t) + (#v: Ghost.erased t) + (p: ptr td) +: STGhost (squash (~ (p == null _))) opened + (pts_to_or_null p v) + (fun _ -> pts_to p v) + (~ (p == null _)) + (fun _ -> True) += rewrite (pts_to_or_null p v) (pts_to p v) + +val ref_equiv + (#t: Type) + (#td: typedef t) + (r1 r2: ref td) +: Tot vprop + +val pts_to_equiv + (#opened: _) + (#t: Type) + (#td: typedef t) + (r1 r2: ref td) + (v: Ghost.erased t) +: STGhostT unit opened + (ref_equiv r1 r2 `star` pts_to r1 v) + (fun _ -> ref_equiv r1 r2 `star` pts_to r2 v) + +val freeable + (#t: Type) + (#td: typedef t) + (r: ref td) +: Tot vprop + +val freeable_dup + (#opened: _) + (#t: Type) + (#td: typedef t) + (r: ref td) +: STGhostT unit opened + (freeable r) + (fun _ -> freeable r `star` freeable r) + +val freeable_equiv + (#opened: _) + (#t: Type) + (#td: typedef t) + (r1 r2: ref td) +: STGhostT unit opened + (ref_equiv r1 r2 `star` freeable r1) + (fun _ -> ref_equiv r1 r2 `star` freeable r2) + +let freeable_or_null + (#t: Type) + (#td: typedef t) + (r: ptr td) +: Tot vprop += if FStar.StrongExcludedMiddle.strong_excluded_middle (r == null _) + then emp + else freeable r + +(* +let freeable_or_null_dup + (#opened: _) + (#t: Type) + (#td: typedef t) + (r: ptr td) +: SteelGhostT vprop opened + (freeable_or_null r) + (fun _ -> freeable_or_null r `star` freeable_or_null r) += if FStar.StrongExcludedMiddle.strong_excluded_middle (r == null _) + then () + else freeable r +*) + +[@@noextract_to "krml"] // primitive +val alloc + (#t: Type) + (td: typedef t) +: STT (ptr td) + emp + (fun p -> pts_to_or_null p (uninitialized td) `star` freeable_or_null p) + +[@@noextract_to "krml"] // primitive +val free + (#t: Type) + (#td: typedef t) + (#v: Ghost.erased t) + (r: ref td) +: ST unit + (pts_to r v `star` freeable r) + (fun _ -> emp) + ( + full td v + ) + (fun _ -> True) + +val mk_fraction_split_gen + (#opened: _) + (#t: Type) (#td: typedef t) (r: ref td) (v: t { fractionable td v }) (p p1 p2: P.perm) : STGhost unit opened + (pts_to r (mk_fraction td v p)) + (fun _ -> pts_to r (mk_fraction td v p1) `star` pts_to r (mk_fraction td v p2)) + (p == p1 `P.sum_perm` p2 /\ p `P.lesser_equal_perm` P.full_perm) + (fun _ -> True) + +let mk_fraction_split + (#opened: _) + (#t: Type) (#td: typedef t) (r: ref td) (v: Ghost.erased t { fractionable td v }) (p1 p2: P.perm) : STGhost unit opened + (pts_to r v) + (fun _ -> pts_to r (mk_fraction td v p1) `star` pts_to r (mk_fraction td v p2)) + (P.full_perm == p1 `P.sum_perm` p2) + (fun _ -> True) += mk_fraction_full td v; + rewrite (pts_to _ _) (pts_to _ _); + mk_fraction_split_gen r v P.full_perm p1 p2 + +val mk_fraction_join + (#opened: _) + (#t: Type) (#td: typedef t) (r: ref td) (v: t { fractionable td v }) (p1 p2: P.perm) +: STGhostT unit opened + (pts_to r (mk_fraction td v p1) `star` pts_to r (mk_fraction td v p2)) + (fun _ -> pts_to r (mk_fraction td v (p1 `P.sum_perm` p2))) + +val fractional_permissions_theorem + (#opened: _) + (#t: Type) + (#td: typedef t) + (v1: t { fractionable td v1 }) + (v2: t { fractionable td v2 }) + (p1 p2: P.perm) + (r: ref td) +: STGhost unit opened + (pts_to r (mk_fraction td v1 p1) `star` pts_to r (mk_fraction td v2 p2)) + (fun _ -> pts_to r (mk_fraction td v1 p1) `star` pts_to r (mk_fraction td v2 p2)) + (full td v1 /\ full td v2) + (fun _ -> v1 == v2 /\ (p1 `P.sum_perm` p2) `P.lesser_equal_perm` P.full_perm) + +val norm_field_attr : unit + +noextract +let norm_field_steps = [ + delta_attr [`%norm_field_attr]; + iota; zeta; primops; +] diff --git a/lib/steel/c/Steel.ST.C.Types.Fields.fsti b/lib/steel/c/Steel.ST.C.Types.Fields.fsti new file mode 100644 index 000000000..9371f7098 --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Types.Fields.fsti @@ -0,0 +1,48 @@ +module Steel.ST.C.Types.Fields +include Steel.ST.C.Types.Base +open Steel.C.Typestring +open Steel.ST.Util + +[@@noextract_to "krml"] // primitive +val field_t_nil: Type0 +[@@noextract_to "krml"] // primitive +val field_t_cons (fn: Type0) (ft: Type0) (fc: Type0): Type0 + +inline_for_extraction [@@noextract_to "krml"; norm_field_attr] +noeq +type field_description_t (t: Type0) : Type u#1 = { + fd_def: (string -> GTot bool); + fd_empty: (fd_empty: bool { fd_empty == true <==> (forall s . fd_def s == false) }); + fd_type: (string -> Type0); + fd_typedef: ((s: string) -> Pure (typedef (fd_type s)) (requires (fd_def s)) (ensures (fun _ -> True))); +} + +inline_for_extraction [@@noextract_to "krml"; norm_field_attr] +let nonempty_field_description_t (t: Type0) = + (fd: field_description_t t { fd.fd_empty == false }) + +[@@noextract_to "krml"] // proof-only +let field_t (#t: Type0) (fd: field_description_t t) : Tot eqtype = (s: string { fd.fd_def s }) + +inline_for_extraction [@@noextract_to "krml"] +let field_description_nil : field_description_t field_t_nil = { + fd_def = (fun _ -> false); + fd_empty = true; + fd_type = (fun _ -> unit); + fd_typedef = (fun _ -> false_elim ()); +} + +inline_for_extraction [@@noextract_to "krml"; norm_field_attr] +let field_description_cons0 + (fn: Type0) (#ft: Type0) (#fc: Type0) (n: string) (t: typedef ft) (fd: field_description_t fc) +: Tot (nonempty_field_description_t (field_t_cons fn ft fc)) += { + fd_def = (fun n' -> n = n' || fd.fd_def n'); + fd_empty = false; + fd_type = (fun n' -> if n = n' then ft else fd.fd_type n'); + fd_typedef = (fun n' -> if n = n' then t else fd.fd_typedef n'); + } + +inline_for_extraction [@@noextract_to "krml"; norm_field_attr] +let field_description_cons (#ft: Type0) (#fc: Type0) (n: string) (#fn: Type0) (# [ solve_mk_string_t ()] prf: squash (norm norm_typestring (mk_string_t n == fn))) (t: typedef ft) (fd: field_description_t fc) : Tot (nonempty_field_description_t (field_t_cons fn ft fc)) = + field_description_cons0 fn #ft #fc n t fd diff --git a/lib/steel/c/Steel.ST.C.Types.Rewrite.fsti b/lib/steel/c/Steel.ST.C.Types.Rewrite.fsti new file mode 100644 index 000000000..8deb566e2 --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Types.Rewrite.fsti @@ -0,0 +1,2 @@ +module Steel.ST.C.Types.Rewrite + diff --git a/lib/steel/c/Steel.ST.C.Types.Scalar.fsti b/lib/steel/c/Steel.ST.C.Types.Scalar.fsti new file mode 100644 index 000000000..4428be991 --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Types.Scalar.fsti @@ -0,0 +1,90 @@ +module Steel.ST.C.Types.Scalar +open Steel.ST.Util +include Steel.ST.C.Types.Base + +module P = Steel.FractionalPermission + +// To be extracted as: t +[@@noextract_to "krml"] // primitive +val scalar_t ( [@@@strictly_positive] t: Type0) : Type0 +[@@noextract_to "krml"] // proof-only +val scalar (t: Type) : typedef (scalar_t t) +val mk_scalar (#t: Type) (v: t) : Ghost (scalar_t t) + (requires True) + (ensures (fun y -> + fractionable (scalar t) y /\ + full (scalar t) y + )) + +val mk_scalar_fractionable + (#t: Type) + (v: t) + (p: P.perm) +: Lemma + (requires (fractionable (scalar t) (mk_fraction (scalar t) (mk_scalar v) p))) + (ensures (p `P.lesser_equal_perm` P.full_perm)) + +val mk_scalar_inj + (#t: Type) + (v1 v2: t) + (p1 p2: P.perm) +: Lemma + (requires (mk_fraction (scalar t) (mk_scalar v1) p1 == mk_fraction (scalar t) (mk_scalar v2) p2)) + (ensures (v1 == v2 /\ p1 == p2)) + [SMTPat [mk_fraction (scalar t) (mk_scalar v1) p1; mk_fraction (scalar t) (mk_scalar v2) p2]] + +let scalar_unique + (#opened: _) + (#t: Type) + (v1 v2: t) + (p1 p2: P.perm) + (r: ref (scalar t)) +: STGhost unit opened + (pts_to r (mk_fraction (scalar t) (mk_scalar v1) p1) `star` pts_to r (mk_fraction (scalar t) (mk_scalar v2) p2)) + (fun _ -> pts_to r (mk_fraction (scalar t) (mk_scalar v1) p1) `star` pts_to r (mk_fraction (scalar t) (mk_scalar v2) p2)) + (True) + (fun _ -> v1 == v2 /\ (p1 `P.sum_perm` p2) `P.lesser_equal_perm` P.full_perm) += fractional_permissions_theorem (mk_scalar v1) (mk_scalar v2) p1 p2 r; + mk_scalar_inj v1 v2 P.full_perm P.full_perm + +[@@noextract_to "krml"] // primitive +val read0 (#t: Type) (#v: Ghost.erased t) (#p: P.perm) (r: ref (scalar t)) : ST t + (pts_to r (mk_fraction (scalar t) (mk_scalar (Ghost.reveal v)) p)) + (fun _ -> pts_to r (mk_fraction (scalar t) (mk_scalar (Ghost.reveal v)) p)) + (True) + (fun v' -> v' == Ghost.reveal v) + +let mk_fraction_full_scalar (#t: Type) (v: t) : Lemma + (mk_scalar v == mk_fraction (scalar t) (mk_scalar v) P.full_perm) + [SMTPat (mk_scalar v)] += () + +inline_for_extraction [@@noextract_to "krml"] +let read (#t: Type) (#v: Ghost.erased (scalar_t t)) (r: ref (scalar t)) : ST t + (pts_to r v) + (fun _ -> pts_to r v) + (exists v0 p . Ghost.reveal v == mk_fraction (scalar t) (mk_scalar v0) p) + (fun v1 -> forall v0 p . (* {:pattern (mk_fraction (scalar t) (mk_scalar v0) p)} *) Ghost.reveal v == mk_fraction (scalar t) (mk_scalar v0) p ==> v0 == v1) += let v0 = FStar.IndefiniteDescription.indefinite_description_tot _ (fun v0 -> exists p . Ghost.reveal v == mk_fraction (scalar t) (mk_scalar v0) p) in + let p = FStar.IndefiniteDescription.indefinite_description_tot _ (fun p -> Ghost.reveal v == mk_fraction (scalar t) (mk_scalar (Ghost.reveal v0)) p) in + let prf v0' p' : Lemma + (requires (Ghost.reveal v == mk_fraction (scalar t) (mk_scalar v0') p')) + (ensures (v0' == Ghost.reveal v0 /\ p' == Ghost.reveal p)) + = mk_scalar_inj (Ghost.reveal v0) v0' p p' + in + let prf' v0' p' : Lemma + (Ghost.reveal v == mk_fraction (scalar t) (mk_scalar v0') p' ==> (v0' == Ghost.reveal v0 /\ p' == Ghost.reveal p)) + = Classical.move_requires (prf v0') p' + in + Classical.forall_intro_2 prf'; + rewrite (pts_to _ _) (pts_to r (mk_fraction (scalar t) (mk_scalar (Ghost.reveal v0)) p)); + let v1 = read0 r in + rewrite (pts_to _ _) (pts_to r v); + return v1 + +[@@noextract_to "krml"] // primitive +val write (#t: Type) (#v: Ghost.erased (scalar_t t)) (r: ref (scalar t)) (v': t) : ST unit + (pts_to r v) + (fun _ -> pts_to r (mk_fraction (scalar t) (mk_scalar v') P.full_perm)) + (full (scalar t) v) + (fun _ -> True) diff --git a/lib/steel/c/Steel.ST.C.Types.Struct.Aux.fsti b/lib/steel/c/Steel.ST.C.Types.Struct.Aux.fsti new file mode 100644 index 000000000..fd77470e2 --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Types.Struct.Aux.fsti @@ -0,0 +1,13 @@ +module Steel.ST.C.Types.Struct.Aux +include Steel.ST.C.Types.Base + +// This module is `friend`ed by Steel.ST.C.Types.Struct and Steel.ST.C.Types.Array + +[@@noextract_to "krml"; norm_field_attr] +inline_for_extraction +noeq +type field_description_gen_t (field_t: eqtype) : Type u#1 = { + fd_nonempty: squash (exists (f: field_t) . True); + fd_type: (field_t -> Type0); + fd_typedef: ((s: field_t) -> Tot (typedef (fd_type s))); +} diff --git a/lib/steel/c/Steel.ST.C.Types.Struct.fsti b/lib/steel/c/Steel.ST.C.Types.Struct.fsti new file mode 100644 index 000000000..cc0ceecaa --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Types.Struct.fsti @@ -0,0 +1,334 @@ +module Steel.ST.C.Types.Struct +open Steel.ST.Util +include Steel.ST.C.Types.Fields +open Steel.C.Typestring + +module P = Steel.FractionalPermission + +// To be extracted as: struct t { fields ... } + +[@@noextract_to "krml"] // primitive +val define_struct0 (tn: Type0) (#tf: Type0) (n: string) (fields: nonempty_field_description_t tf) : Tot Type0 +inline_for_extraction [@@noextract_to "krml"] +let define_struct (n: string) (#tf: Type0) (#tn: Type0) (#[solve_mk_string_t ()] prf: squash (norm norm_typestring (mk_string_t n == tn))) (fields: nonempty_field_description_t tf) : Tot Type0 += define_struct0 tn #tf n fields + +// To be extracted as: struct t +[@@noextract_to "krml"] // primitive +val struct_t0 (tn: Type0) (#tf: Type0) (n: string) (fields: nonempty_field_description_t tf) : Tot Type0 +inline_for_extraction [@@noextract_to "krml"] +let struct_t (#tf: Type0) (n: string) (#tn: Type0) (# [solve_mk_string_t ()] prf: squash (norm norm_typestring (mk_string_t n == tn))) (fields: nonempty_field_description_t tf) : Tot Type0 += struct_t0 tn #tf n fields + +val struct_set_field (#tn: Type0) (#tf: Type0) (#n: string) (#fields: nonempty_field_description_t tf) (f: field_t fields) (v: fields.fd_type f) (s: struct_t0 tn n fields) : GTot (struct_t0 tn n fields) + +val struct_get_field + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (s: struct_t0 tn n fields) + (field: field_t fields) +: GTot (fields.fd_type field) + +val struct_eq + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (s1 s2: struct_t0 tn n fields) +: Ghost prop + (requires True) + (ensures (fun y -> + (y <==> (s1 == s2)) /\ + (y <==> (forall field . struct_get_field s1 field == struct_get_field s2 field)) + )) + +val struct_get_field_same + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (s: struct_t0 tn n fields) + (field: field_t fields) + (v: fields.fd_type field) +: Lemma + (struct_get_field (struct_set_field field v s) field == v) + [SMTPat (struct_get_field (struct_set_field field v s) field)] + +val struct_get_field_other + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (s: struct_t0 tn n fields) + (field: field_t fields) + (v: fields.fd_type field) + (field': field_t fields) +: Lemma + (requires (field' <> field)) + (ensures (struct_get_field (struct_set_field field v s) field' == struct_get_field s field')) + +let struct_get_field_pat + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (s: struct_t0 tn n fields) + (field: field_t fields) + (v: fields.fd_type field) + (field': field_t fields) +: Lemma + (struct_get_field (struct_set_field field v s) field' == (if field' = field then v else struct_get_field s field')) + [SMTPat (struct_get_field (struct_set_field field v s) field')] += if field' = field + then () + else struct_get_field_other s field v field' + +[@@noextract_to "krml"] // proof-only +val struct0 (tn: Type0) (#tf: Type0) (n: string) (fields: nonempty_field_description_t tf) : Tot (typedef (struct_t0 tn n fields)) + +inline_for_extraction +[@@noextract_to "krml"; norm_field_attr] // proof-only +let struct (#tf: Type0) (n: string) (#tn: Type0) (# [solve_mk_string_t ()] prf: squash (norm norm_typestring (mk_string_t n == tn))) (fields: nonempty_field_description_t tf) : Tot (typedef (struct_t0 tn n fields)) += struct0 tn #tf n fields + +val struct_get_field_unknown + (tn: Type0) + (#tf: Type0) + (n: string) + (fields: nonempty_field_description_t tf) + (field: field_t fields) +: Lemma + (struct_get_field (unknown (struct0 tn n fields)) field == unknown (fields.fd_typedef field)) + [SMTPat (struct_get_field (unknown (struct0 tn n fields)) field)] + +val struct_get_field_uninitialized + (tn: Type0) + (#tf: Type0) + (n: string) + (fields: nonempty_field_description_t tf) + (field: field_t fields) +: Lemma + (struct_get_field (uninitialized (struct0 tn n fields)) field == uninitialized (fields.fd_typedef field)) + [SMTPat (struct_get_field (uninitialized (struct0 tn n fields)) field)] + +val has_struct_field + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (r: ref (struct0 tn n fields)) + (field: field_t fields) + (r': ref (fields.fd_typedef field)) +: Tot vprop + +val has_struct_field_dup + (#opened: _) + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (r: ref (struct0 tn n fields)) + (field: field_t fields) + (r': ref (fields.fd_typedef field)) +: STGhostT unit opened + (has_struct_field r field r') + (fun _ -> has_struct_field r field r' `star` has_struct_field r field r') + +val has_struct_field_inj + (#opened: _) + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (r: ref (struct0 tn n fields)) + (field: field_t fields) + (r1 r2: ref (fields.fd_typedef field)) +: STGhostT unit opened + (has_struct_field r field r1 `star` has_struct_field r field r2) + (fun _ -> has_struct_field r field r1 `star` has_struct_field r field r2 `star` ref_equiv r1 r2) + +val has_struct_field_equiv_from + (#opened: _) + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (r1: ref (struct0 tn n fields)) + (field: field_t fields) + (r': ref (fields.fd_typedef field)) + (r2: ref (struct0 tn n fields)) +: STGhostT unit opened + (ref_equiv r1 r2 `star` has_struct_field r1 field r') + (fun _ -> ref_equiv r1 r2 `star` has_struct_field r2 field r') + +val has_struct_field_equiv_to + (#opened: _) + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (r: ref (struct0 tn n fields)) + (field: field_t fields) + (r1': ref (fields.fd_typedef field)) + (r2': ref (fields.fd_typedef field)) +: STGhostT unit opened + (ref_equiv r1' r2' `star` has_struct_field r field r1') + (fun _ -> ref_equiv r1' r2' `star` has_struct_field r field r2') + +val ghost_struct_field_focus + (#opened: _) + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (#v: Ghost.erased (struct_t0 tn n fields)) + (r: ref (struct0 tn n fields)) + (field: field_t fields) + (r': ref (fields.fd_typedef field)) +: STGhostT unit opened + (has_struct_field r field r' `star` pts_to r v) + (fun _ -> has_struct_field r field r' `star` pts_to r (struct_set_field field (unknown (fields.fd_typedef field)) v) `star` pts_to r' (struct_get_field v field)) + +val ghost_struct_field + (#opened: _) + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (#v: Ghost.erased (struct_t0 tn n fields)) + (r: ref (struct0 tn n fields)) + (field: field_t fields) +: STGhostT (Ghost.erased (ref (fields.fd_typedef field))) opened + (pts_to r v) + (fun r' -> pts_to r (struct_set_field field (unknown (fields.fd_typedef field)) v) `star` pts_to r' (struct_get_field v field) `star` has_struct_field r field r') + +[@@noextract_to "krml"] // primitive +val struct_field0 + (#tn: Type0) + (#tf: Type0) + (t': Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (#v: Ghost.erased (struct_t0 tn n fields)) + (r: ref (struct0 tn n fields)) + (field: field_t fields) + (td': typedef t' { + t' == fields.fd_type field /\ + td' == fields.fd_typedef field + }) +: STT (ref td') + (pts_to r v) + (fun r' -> pts_to r (struct_set_field field (unknown (fields.fd_typedef field)) v) `star` pts_to r' (struct_get_field v field) `star` has_struct_field r field r') + +inline_for_extraction [@@noextract_to "krml"] // primitive +let struct_field + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (#v: Ghost.erased (struct_t0 tn n fields)) + (r: ref (struct0 tn n fields)) + (field: field_t fields) +: STT (ref #(norm norm_field_steps (fields.fd_type field)) (fields.fd_typedef field)) + (pts_to r v) + (fun r' -> pts_to r (struct_set_field field (unknown (fields.fd_typedef field)) v) `star` pts_to #(norm norm_field_steps (fields.fd_type field)) r' (struct_get_field v field) `star` has_struct_field r field r') += struct_field0 + (norm norm_field_steps (fields.fd_type field)) + r + field + (fields.fd_typedef field) + +val unstruct_field + (#opened: _) + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (#v: Ghost.erased (struct_t0 tn n fields)) + (r: ref (struct0 tn n fields)) + (field: field_t fields) + (#v': Ghost.erased (fields.fd_type field)) + (r': ref (fields.fd_typedef field)) +: STGhost unit opened + (has_struct_field r field r' `star` pts_to r v `star` pts_to r' v') + (fun _ -> has_struct_field r field r' `star` pts_to r (struct_set_field field v' v)) + ( + struct_get_field v field == unknown (fields.fd_typedef field) + ) + (fun _ -> True) + +let unstruct_field_alt + (#opened: _) + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (#v: Ghost.erased (struct_t0 tn n fields)) + (r: ref (struct0 tn n fields)) + (field: field_t fields) + (#v': Ghost.erased (fields.fd_type field)) + (r': ref (fields.fd_typedef field)) +: STGhost (Ghost.erased (struct_t0 tn n fields)) opened + (has_struct_field r field r' `star` pts_to r v `star` pts_to r' v') + (fun s' -> has_struct_field r field r' `star` pts_to r s') + ( + struct_get_field v field == unknown (fields.fd_typedef field) + ) + (fun s' -> + Ghost.reveal s' == struct_set_field field v' v + ) += unstruct_field r field r'; + _ + +val fractionable_struct + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (s: struct_t0 tn n fields) +: Lemma + (fractionable (struct0 tn n fields) s <==> (forall field . fractionable (fields.fd_typedef field) (struct_get_field s field))) + [SMTPat (fractionable (struct0 tn n fields) s)] + +val mk_fraction_struct + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (s: struct_t0 tn n fields) + (p: P.perm) + (field: field_t fields) +: Lemma + (requires (fractionable (struct0 tn n fields) s)) + (ensures (struct_get_field (mk_fraction (struct0 tn n fields) s p) field == mk_fraction (fields.fd_typedef field) (struct_get_field s field) p)) + [SMTPat (struct_get_field (mk_fraction (struct0 tn n fields) s p) field)] + +(* +val mk_fraction_struct_recip + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (s: struct_t0 tn n fields) + (p: P.perm) +: Ghost (struct_t0 tn n fields) + (requires ( + (forall field . exists v . fractionable (fields.fd_typedef field) v /\ struct_get_field s field == mk_fraction (fields.fd_typedef field) v p) + )) + (ensures (fun s' -> + fractionable (struct0 tn n fields) s' /\ + s == mk_fraction (struct0 tn n fields) s' p + )) +*) + +val full_struct + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (s: struct_t0 tn n fields) +: Lemma + (full (struct0 tn n fields) s <==> (forall field . full (fields.fd_typedef field) (struct_get_field s field))) + [SMTPat (full (struct0 tn n fields) s)] diff --git a/lib/steel/c/Steel.ST.C.Types.Union.fsti b/lib/steel/c/Steel.ST.C.Types.Union.fsti new file mode 100644 index 000000000..831b4e15a --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Types.Union.fsti @@ -0,0 +1,360 @@ +module Steel.ST.C.Types.Union +open Steel.ST.Util +include Steel.ST.C.Types.Fields +open Steel.C.Typestring + +module P = Steel.FractionalPermission + +[@@noextract_to "krml"] // primitive +val define_union0 (tn: Type0) (#tf: Type0) (n: string) (fields: field_description_t tf) : Tot Type0 +inline_for_extraction [@@noextract_to "krml"] +let define_union (n: string) (#tf: Type0) (#tn: Type0) (#[solve_mk_string_t ()] prf: squash (norm norm_typestring (mk_string_t n == tn))) (fields: field_description_t tf) : Tot Type0 += define_union0 tn #tf n fields + +// To be extracted as: union t +[@@noextract_to "krml"] // primitive +val union_t0 (tn: Type0) (#tf: Type0) (n: string) (fields: field_description_t tf) : Tot Type0 +inline_for_extraction [@@noextract_to "krml"] +let union_t (#tf: Type0) (n: string) (#tn: Type0) (# [solve_mk_string_t ()] prf: squash (norm norm_typestring (mk_string_t n == tn))) (fields: field_description_t tf) : Tot Type0 += union_t0 tn #tf n fields + +val union_set_field (tn: Type0) (#tf: Type0) (n: string) (fields: field_description_t tf) (f: field_t fields) (v: fields.fd_type f) : GTot (union_t0 tn n fields) + +val union_get_case + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (u: union_t0 tn n fields) +: GTot (option (field_t fields)) + +val union_get_field + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (u: union_t0 tn n fields) + (field: field_t fields) +: Ghost (fields.fd_type field) + (requires (union_get_case u == Some field)) + (ensures (fun _ -> True)) + +val union_get_field_same + (tn: Type0) + (#tf: Type0) + (n: string) + (fields: field_description_t tf) + (field: field_t fields) + (v: fields.fd_type field) +: Lemma + (requires (~ (v == unknown (fields.fd_typedef field)))) + (ensures ( + let u = union_set_field tn n fields field v in + union_get_case u == Some field /\ + union_get_field u field == v + )) + [SMTPatOr [ + [SMTPat (union_get_case (union_set_field tn n fields field v))]; + [SMTPat (union_get_field (union_set_field tn n fields field v) field)]; + ]] + +val union_set_field_same + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (s: union_t0 tn n fields) + (field: field_t fields) +: Lemma + (requires (union_get_case s == Some field)) + (ensures ( + union_set_field tn n fields field (union_get_field s field) == s + )) + [SMTPat (union_set_field tn n fields (union_get_field s field))] + +[@@noextract_to "krml"] // proof-only +val union0 (tn: Type0) (#tf: Type0) (n: string) (fields: field_description_t tf) : Tot (typedef (union_t0 tn n fields)) + +inline_for_extraction +[@@noextract_to "krml"; norm_field_attr] // proof-only +let union (#tf: Type0) (n: string) (#tn: Type0) (# [solve_mk_string_t ()] prf: squash (norm norm_typestring (mk_string_t n == tn))) (fields: field_description_t tf) : Tot (typedef (union_t0 tn n fields)) += union0 tn #tf n fields + +val union_get_case_unknown + (tn: Type0) + (#tf: Type0) + (n: string) + (fields: field_description_t tf) +: Lemma + (union_get_case (unknown (union0 tn n fields)) == None) + [SMTPat (unknown (union0 tn n fields))] + +val union_set_field_unknown + (tn: Type0) + (#tf: Type0) + (n: string) + (fields: field_description_t tf) + (field: field_t fields) +: Lemma + (union_set_field tn n fields field (unknown (fields.fd_typedef field)) == unknown (union0 tn n fields)) + [SMTPat (union_set_field tn n fields field (unknown (fields.fd_typedef field)))] + +val union_get_case_uninitialized + (tn: Type0) + (#tf: Type0) + (n: string) + (fields: field_description_t tf) +: Lemma + (union_get_case (uninitialized (union0 tn n fields)) == None) + [SMTPat (uninitialized (union0 tn n fields))] + +val mk_fraction_union_get_case + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (s: union_t0 tn n fields) + (p: P.perm) +: Lemma + (requires (fractionable (union0 tn n fields) s)) + (ensures ( + union_get_case (mk_fraction (union0 tn n fields) s p) == union_get_case s + )) + [SMTPat (union_get_case (mk_fraction (union0 tn n fields) s p))] + +val fractionable_union_get_field + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (s: union_t0 tn n fields) + (field: field_t fields) +: Lemma + (requires (union_get_case s == Some field)) + (ensures ( + fractionable (union0 tn n fields) s <==> fractionable (fields.fd_typedef field) (union_get_field s field) + )) + [SMTPat (fractionable (union0 tn n fields) s); SMTPat (union_get_field s field)] + +val mk_fraction_union_get_field + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (s: union_t0 tn n fields) + (p: P.perm) + (field: field_t fields) +: Lemma + (requires (fractionable (union0 tn n fields) s /\ union_get_case s == Some field)) + (ensures (union_get_field (mk_fraction (union0 tn n fields) s p) field == mk_fraction (fields.fd_typedef field) (union_get_field s field) p)) + [SMTPat (union_get_field (mk_fraction (union0 tn n fields) s p) field)] + +val mk_fraction_union_set_field + (tn: Type0) + (#tf: Type0) + (n: string) + (fields: field_description_t tf) + (field: field_t fields) + (v: fields.fd_type field) + (p: P.perm) +: Lemma + (requires (fractionable (fields.fd_typedef field) v)) + (ensures ( + fractionable (union0 tn n fields) (union_set_field tn n fields field v) /\ + mk_fraction (union0 tn n fields) (union_set_field tn n fields field v) p == union_set_field tn n fields field (mk_fraction (fields.fd_typedef field) v p) + )) + +val full_union + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (s: union_t0 tn n fields) + (field: field_t fields) +: Lemma + (requires (union_get_case s == Some field)) + (ensures ( + full (union0 tn n fields) s <==> full (fields.fd_typedef field) (union_get_field s field) + )) + [SMTPat (full (union0 tn n fields) s); SMTPat (union_get_field s field)] + +val has_union_field + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (r: ref (union0 tn n fields)) + (field: field_t fields) + (r': ref (fields.fd_typedef field)) +: Tot vprop + +val has_union_field_dup + (#opened: _) + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (r: ref (union0 tn n fields)) + (field: field_t fields) + (r': ref (fields.fd_typedef field)) +: STGhostT unit opened + (has_union_field r field r') + (fun _ -> has_union_field r field r' `star` has_union_field r field r') + +val has_union_field_inj + (#opened: _) + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (r: ref (union0 tn n fields)) + (field: field_t fields) + (r1 r2: ref (fields.fd_typedef field)) +: STGhostT unit opened + (has_union_field r field r1 `star` has_union_field r field r2) + (fun _ -> has_union_field r field r1 `star` has_union_field r field r2 `star` ref_equiv r1 r2) + +val has_union_field_equiv_from + (#opened: _) + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (r1 r2: ref (union0 tn n fields)) + (field: field_t fields) + (r': ref (fields.fd_typedef field)) +: STGhostT unit opened + (has_union_field r1 field r' `star` ref_equiv r1 r2) + (fun _ -> has_union_field r2 field r' `star` ref_equiv r1 r2) + +val has_union_field_equiv_to + (#opened: _) + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (r: ref (union0 tn n fields)) + (field: field_t fields) + (r1 r2: ref (fields.fd_typedef field)) +: STGhostT unit opened + (has_union_field r field r1 `star` ref_equiv r1 r2) + (fun _ -> has_union_field r field r2 `star` ref_equiv r1 r2) + +val ghost_union_field_focus + (#opened: _) + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (#v: Ghost.erased (union_t0 tn n fields)) + (r: ref (union0 tn n fields)) + (field: field_t fields {union_get_case v == Some field}) + (r': ref (fields.fd_typedef field)) +: STGhostT unit opened + (has_union_field r field r' `star` pts_to r v) + (fun _ -> has_union_field r field r' `star` pts_to r' (union_get_field v field)) + +val ghost_union_field + (#opened: _) + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (#v: Ghost.erased (union_t0 tn n fields)) + (r: ref (union0 tn n fields)) + (field: field_t fields {union_get_case v == Some field}) +: STGhostT (Ghost.erased (ref (fields.fd_typedef field))) opened + (pts_to r v) + (fun r' -> has_union_field r field r' `star` pts_to r' (union_get_field v field)) + +[@@noextract_to "krml"] // primitive +val union_field0 + (#tn: Type0) + (#tf: Type0) + (t': Type0) + (#n: string) + (#fields: field_description_t tf) + (#v: Ghost.erased (union_t0 tn n fields)) + (r: ref (union0 tn n fields)) + (field: field_t fields {union_get_case v == Some field}) + (td': typedef t' { + t' == fields.fd_type field /\ + td' == fields.fd_typedef field + }) +: STT (ref td') + (pts_to r v) + (fun r' -> has_union_field r field r' `star` pts_to r' (union_get_field v field)) + +inline_for_extraction [@@noextract_to "krml"] // primitive +let union_field + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (#v: Ghost.erased (union_t0 tn n fields)) + (r: ref (union0 tn n fields)) + (field: field_t fields {union_get_case v == Some field}) +: STT (ref #(norm norm_field_steps (fields.fd_type field)) (fields.fd_typedef field)) + (pts_to r v) + (fun r' -> has_union_field r field r' `star` pts_to #(norm norm_field_steps (fields.fd_type field)) r' (union_get_field v field)) += union_field0 + (norm norm_field_steps (fields.fd_type field)) + r + field + (fields.fd_typedef field) + +val ununion_field + (#opened: _) + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (r: ref (union0 tn n fields)) + (field: field_t fields) + (#v': Ghost.erased (fields.fd_type field)) + (r': ref (fields.fd_typedef field)) +: STGhostT unit opened + (has_union_field r field r' `star` pts_to r' v') + (fun _ -> has_union_field r field r' `star` pts_to r (union_set_field tn n fields field v')) + +// NOTE: we DO NOT support preservation of struct prefixes + +[@@noextract_to "krml"] // primitive +val union_switch_field0 + (#tn: Type0) + (#tf: Type0) + (t': Type0) + (#n: string) + (#fields: field_description_t tf) + (#v: Ghost.erased (union_t0 tn n fields)) + (r: ref (union0 tn n fields)) + (field: field_t fields) + (td': typedef t' { + t' == fields.fd_type field /\ + td' == fields.fd_typedef field + }) +: ST (ref td') // need to write the pcm carrier value, so this cannot be Ghost or Atomic + (pts_to r v) + (fun r' -> has_union_field r field r' `star` pts_to r' (uninitialized (fields.fd_typedef field))) + (full (union0 tn n fields) v) + (fun r' -> True) + +inline_for_extraction [@@noextract_to "krml"] +let union_switch_field + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (#v: Ghost.erased (union_t0 tn n fields)) + (r: ref (union0 tn n fields)) + (field: field_t fields) +: ST (ref #(norm norm_field_steps (fields.fd_type field)) (fields.fd_typedef field)) // need to write the pcm carrier value, so this cannot be Ghost or Atomic + (pts_to r v) + (fun r' -> has_union_field r field r' `star` pts_to #(norm norm_field_steps (fields.fd_type field)) r' (uninitialized (fields.fd_typedef field))) + (full (union0 tn n fields) v) + (fun r' -> True) += union_switch_field0 + (norm norm_field_steps (fields.fd_type field)) + r + field + (fields.fd_typedef field) diff --git a/lib/steel/c/Steel.ST.C.Types.UserStruct.fsti b/lib/steel/c/Steel.ST.C.Types.UserStruct.fsti new file mode 100644 index 000000000..163a49d60 --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Types.UserStruct.fsti @@ -0,0 +1,243 @@ +module Steel.ST.C.Types.UserStruct +open Steel.ST.Util +open Steel.ST.C.Types.Struct.Aux + +module Set = FStar.Set + +(* This library allows the user to define their own struct type, with +a constructor from field values, and a destructor to field values for +each field. This may be necessary for recursive structs *) + +[@@noextract_to "krml"] +let field_t (s: Set.set string) : Tot eqtype = + (f: string { Set.mem f s }) + +[@@noextract_to "krml"; norm_field_attr] +inline_for_extraction // for field_desc.fd_type +noeq +type struct_def (t: Type) = { + fields: Set.set string; + field_desc: field_description_gen_t (field_t fields); + mk: ((f: field_t fields) -> Tot (field_desc.fd_type f)) -> Tot t; + get: (t -> (f: field_t fields) -> Tot (field_desc.fd_type f)); + get_mk: (phi: ((f: field_t fields) -> Tot (field_desc.fd_type f))) -> (f: field_t fields) -> Lemma + (get (mk phi) f == phi f); + extensionality: (x1: t) -> (x2: t) -> ((f: field_t fields) -> Lemma (get x1 f == get x2 f)) -> Lemma (x1 == x2); +} + +let nonempty_set_nonempty_type (x: string) (s: Set.set string) : Lemma + (requires (x `Set.mem` s)) + (ensures (exists (x: field_t s) . True)) += Classical.exists_intro (fun (_: field_t s) -> True) x + +[@@noextract_to "krml"] +let set_aux + (#t: Type) (sd: struct_def t) (x: t) (f: field_t sd.fields) (v: sd.field_desc.fd_type f) (f': field_t sd.fields) +: Tot (sd.field_desc.fd_type f') += if f = f' then v else sd.get x f' + +[@@noextract_to "krml"] +let set (#t: Type) (sd: struct_def t) (x: t) (f: field_t sd.fields) (v: sd.field_desc.fd_type f) : Tot t = + sd.mk (set_aux sd x f v) + +let get_set (#t: Type) (sd: struct_def t) (x: t) (f: field_t sd.fields) (v: sd.field_desc.fd_type f) (f' : field_t sd.fields) : Lemma + (sd.get (set sd x f v) f' == (if f = f' then v else sd.get x f')) + [SMTPat (sd.get (set sd x f v) f')] += sd.get_mk (set_aux sd x f v) f' + +[@@noextract_to "krml"] +val struct_typedef + (#t: Type) + (sd: struct_def t) +: Tot (typedef t) + +val has_struct_field + (#t: Type) + (#sd: struct_def t) + (r: ref (struct_typedef sd)) + (field: field_t sd.fields) + (r': ref (sd.field_desc.fd_typedef field)) +: Tot vprop + +val has_struct_field_dup + (#opened: _) + (#t: Type) + (#sd: struct_def t) + (r: ref (struct_typedef sd)) + (field: field_t sd.fields) + (r': ref (sd.field_desc.fd_typedef field)) +: STGhostT unit opened + (has_struct_field r field r') + (fun _ -> has_struct_field r field r' `star` has_struct_field r field r') + +val has_struct_field_inj + (#opened: _) + (#t: Type) + (#sd: struct_def t) + (r: ref (struct_typedef sd)) + (field: field_t sd.fields) + (r1 r2: ref (sd.field_desc.fd_typedef field)) +: STGhostT unit opened + (has_struct_field r field r1 `star` has_struct_field r field r2) + (fun _ -> has_struct_field r field r1 `star` has_struct_field r field r2 `star` ref_equiv r1 r2) + +val has_struct_field_equiv_from + (#opened: _) + (#t: Type) + (#sd: struct_def t) + (r1: ref (struct_typedef sd)) + (field: field_t sd.fields) + (r': ref (sd.field_desc.fd_typedef field)) + (r2: ref (struct_typedef sd)) +: STGhostT unit opened + (ref_equiv r1 r2 `star` has_struct_field r1 field r') + (fun _ -> ref_equiv r1 r2 `star` has_struct_field r2 field r') + +val has_struct_field_equiv_to + (#opened: _) + (#t: Type) + (#sd: struct_def t) + (r: ref (struct_typedef sd)) + (field: field_t sd.fields) + (r1' r2': ref (sd.field_desc.fd_typedef field)) +: STGhostT unit opened + (ref_equiv r1' r2' `star` has_struct_field r field r1') + (fun _ -> ref_equiv r1' r2' `star` has_struct_field r field r2') + +val ghost_struct_field_focus + (#opened: _) + (#t: Type) + (#sd: struct_def t) + (#v: Ghost.erased t) + (r: ref (struct_typedef sd)) + (field: field_t sd.fields) + (r': ref (sd.field_desc.fd_typedef field)) +: STGhostT unit opened + (has_struct_field r field r' `star` pts_to r v) + (fun _ -> has_struct_field r field r' `star` pts_to r (set sd v field (unknown (sd.field_desc.fd_typedef field))) `star` pts_to r' (sd.get v field)) + +val ghost_struct_field + (#opened: _) + (#t: Type) + (#sd: struct_def t) + (#v: Ghost.erased t) + (r: ref (struct_typedef sd)) + (field: field_t sd.fields) +: STGhostT (Ghost.erased (ref (sd.field_desc.fd_typedef field))) opened + (pts_to r v) + (fun r' -> has_struct_field r field r' `star` pts_to r (set sd v field (unknown (sd.field_desc.fd_typedef field))) `star` pts_to r' (sd.get v field)) + +[@@noextract_to "krml"] // primitive +val struct_field0 + (#t: Type) + (t': Type0) + (#sd: struct_def t) + (#v: Ghost.erased t) + (r: ref (struct_typedef sd)) + (field: field_t sd.fields) + (td': typedef t' { + t' == sd.field_desc.fd_type field /\ + td' == sd.field_desc.fd_typedef field + }) +: STT (ref td') + (pts_to r v) + (fun r' -> has_struct_field r field (coerce_eq () r') `star` pts_to r (set sd (Ghost.reveal v) field (unknown (sd.field_desc.fd_typedef field))) `star` pts_to #_ #(sd.field_desc.fd_typedef field) (coerce_eq () r') (sd.get (Ghost.reveal v) field)) + +inline_for_extraction [@@noextract_to "krml"] // primitive +let struct_field + (#t: Type) + (#sd: struct_def t) + (#v: Ghost.erased t) + (r: ref (struct_typedef sd)) + (field: field_t sd.fields) +: STT (ref #(norm norm_field_steps (sd.field_desc.fd_type field)) (sd.field_desc.fd_typedef field)) + (pts_to r v) + (fun r' -> pts_to r (set sd v field (unknown (sd.field_desc.fd_typedef field))) `star` pts_to #(norm norm_field_steps (sd.field_desc.fd_type field)) r' (sd.get v field) `star` has_struct_field r field r') += struct_field0 + (norm norm_field_steps (sd.field_desc.fd_type field)) + r + field + (sd.field_desc.fd_typedef field) + +val unstruct_field + (#opened: _) + (#t: Type) + (#sd: struct_def t) + (#v: Ghost.erased t) + (r: ref (struct_typedef sd)) + (field: field_t sd.fields) + (#v': Ghost.erased (sd.field_desc.fd_type field)) + (r': ref (sd.field_desc.fd_typedef field)) +: STGhost unit opened + (has_struct_field r field r' `star` pts_to r v `star` pts_to r' v') + (fun _ -> has_struct_field r field r' `star` pts_to r (set sd v field v')) + ( + sd.get v field == unknown (sd.field_desc.fd_typedef field) + ) + (fun _ -> True) + +let unstruct_field_alt + (#opened: _) + (#t: Type) + (#sd: struct_def t) + (#v: Ghost.erased t) + (r: ref (struct_typedef sd)) + (field: field_t sd.fields) + (#v': Ghost.erased (sd.field_desc.fd_type field)) + (r': ref (sd.field_desc.fd_typedef field)) +: STGhost (Ghost.erased t) opened + (has_struct_field r field r' `star` pts_to r v `star` pts_to r' v') + (fun s' -> has_struct_field r field r' `star` pts_to r s') + ( + sd.get v field == unknown (sd.field_desc.fd_typedef field) + ) + (fun s' -> Ghost.reveal s' == set sd v field v') += unstruct_field r field r'; + _ + +val fractionable_struct + (#t: Type) + (sd: struct_def t) + (s: t) +: Lemma + (fractionable (struct_typedef sd) s <==> (forall field . fractionable (sd.field_desc.fd_typedef field) (sd.get s field))) + [SMTPat (fractionable (struct_typedef sd) s)] + +module P = Steel.FractionalPermission + +val mk_fraction_struct + (#t: Type) + (sd: struct_def t) + (s: t) + (p: P.perm) + (field: field_t sd.fields) +: Lemma + (requires (fractionable (struct_typedef sd) s)) + (ensures (sd.get (mk_fraction (struct_typedef sd) s p) field == mk_fraction (sd.field_desc.fd_typedef field) (sd.get s field) p)) + [SMTPat (sd.get (mk_fraction (struct_typedef sd) s p) field)] + +(* +val mk_fraction_struct_recip + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: nonempty_field_description_t tf) + (s: struct_t0 tn n fields) + (p: P.perm) +: Ghost (struct_t0 tn n fields) + (requires ( + (forall field . exists v . fractionable (fields.fd_typedef field) v /\ struct_get_field s field == mk_fraction (fields.fd_typedef field) v p) + )) + (ensures (fun s' -> + fractionable (struct0 tn n fields) s' /\ + s == mk_fraction (struct0 tn n fields) s' p + )) +*) + +val full_struct + (#t: Type) + (sd: struct_def t) + (s: t) +: Lemma + (full (struct_typedef sd) s <==> (forall field . full (sd.field_desc.fd_typedef field) (sd.get s field))) + [SMTPat (full (struct_typedef sd) s)] diff --git a/lib/steel/c/Steel.ST.C.Types.fst b/lib/steel/c/Steel.ST.C.Types.fst new file mode 100644 index 000000000..933041ca4 --- /dev/null +++ b/lib/steel/c/Steel.ST.C.Types.fst @@ -0,0 +1,6 @@ +module Steel.ST.C.Types + +include Steel.ST.C.Types.Scalar +include Steel.ST.C.Types.Struct +include Steel.ST.C.Types.Union +include Steel.ST.C.Types.Array diff --git a/share/steel/examples/steelc/Basetypes.md b/share/steel/examples/steelc/Basetypes.md new file mode 100644 index 000000000..0a3e09f9b --- /dev/null +++ b/share/steel/examples/steelc/Basetypes.md @@ -0,0 +1,131 @@ +The Steel heap maps addresses to cells, and each cell holds both a value `v` and its type `a`: +```fst +noeq +type cell : Type u#(a + 1) = + | Ref : a:Type u#a -> + p:pcm a -> + frac:Frac.perm{ frac `Frac.lesser_equal_perm` Frac.full_perm } -> + v:a { frac == Frac.full_perm ==> p.refine v } -> + cell +``` +If we would like to model references to substructures `w:b` of `v` +without having to add new constructors to `cell` specifically for structs and arrays, +it seems inevitable that the model will have to keep track of both the +type `b` of the substructure and the type `a` of its base object. +AggregateRef.fst defines a type of such references `ref a b`. +Because ref has to keep track of the type of its base object, code +that works with references has to carry around extra parameters. +The model runs into trouble when the number of references isn't known +statically; for example, if one wants to specify the contents of an array +of references, each with possibly different base objects. +Unfortunately it's not possible to solve this by hiding `a` behind +an existential, because that would increase `ref`'s universe level. + +One idea could be to hide `a` inside a closure, exposing only a record of basic operations: +```fstar +let operations = + let r: ref a b = .. in { + read = (fun () -> .. r ..); + write = (fun () -> .. r ..); + } +``` +This would work if we just wanted simply typed versions of `read` and `write`, +but there is no way to give dependently-typed specifications to these +functions without talking about the base object `a`. + +Here are some ideas on how to resolve this: + +### Hide `a` in a closure, then carry along proofs extrinsically + +Construct a record `{read = .., write = ..}` hiding the base type `a` +and the raw `ref a b` inside closures, and give `read` and `write` simple types. +Return, with it, a proof of `exists a. read spec /\ write spec`. +This would allow the record to live in universe `0` (the record is +simply typed and the proof is squashed), so refs could safely be stored in the Steel heap. + +However, giving the operations simple types means that they have to return some bogus +value (or be partial) on inputs which don't satisfy their preconditions; +this seems tricky, or maybe even impossible if the preconditions aren't decidable. + +### Represent refs just as addresses + +The heap stores the types and PCMs along with values. +Is it possible, then, to avoid storing those same types and PCMs inside the ref? +If so, we could store refs in the heap, and only mention base types/PCMs +in predicates like `pts_to`. Unfortunately, this doesn't seem possible +either, because the base type is needed to give a type to the lens. + +### Store the lens in the heap + +Can we treat a reference like an addressable stack-local and store +its lens in the heap? For example, if we start with +``` +p `pts_to` {x, y} +``` +and take a pointer to the first field of `{x, y}`, we get +``` +(p `pts_to` {x, y}) `star` +(r `pts_to` {base_type & lens for field x of base_type}) +r: Steel.Memory.ref _ +``` +This loses a lot of equalities. For example, if we take +a second pointer to the first field of `{x, y}` we get +``` +(p `pts_to` {x, y}) `star` +(r `pts_to` {base_type & lens for field x of base_type}) +(s `pts_to` {base_type & lens for field x of base_type}) +r, s: Steel.Memory.ref _ +``` +If we would like to use the fact that `r` and `s` alias later on, +we need to use separation logic to prove that any code between then +and when the pointers were taken didn't modify either of the lenses. + +### Store lenses along with base objects + +Instead of just storing values v in the heap, store (v, [l1, .., ln]) +where [l1, .., ln] are a (heterogenous) list of lenses into +v. Represent references as (addr, k) where k is an index into this +list. (This may not necessarily mean modifying Steel.Heap---it could +be possible to design a PCM to carry along these lists of lenses.) + +This would fix the universe issue, but it's very complicated.. +Also, in order to nicely support reasoning about aliasing, we would +want that the list of lenses not contain any duplicates; to maintain +that invariant, we'd need decidable equality on lenses, which isn't guaranteed. + +### Give up on hiding the base object + +Though the model in AggregateRef.fst is designed for writing C-like code, +the representation of a reference as a lens + Steel memory ref +suggests that there could be more high-level applications of references as well. +For example, it might be possible to construct a "reference" to a linked list +which allows viewing it as an ordinary F* list of its elements, or to +construct a "reference" to a u32 from the upper 16 bits of two other u32 references. +These things aren't possible with the current model because an AggregateRef.ref +contains only one Steel.Memory.ref, and therefore can only point to +one piece of memory; in order to support these fancy applications, +we'd need to allow an AggregateRef.ref to point to parts of multiple +different pieces at once. + +We could address this and somewhat get around the base object issue by +representing references as a lens + a family of `Steel.Memory.ref`s +instead of just a single `Steel.Memory.ref`. +Then: +- It would be possible to write a function like + `ref u32 u16 -> ref u32 u16 -> ref (u32 & u32) u32` + to combine two refs for the upper 16 bits of two `u32`s into + a 32-bit ref +- It would be possible to write a function like + `cons: ref 'a 'c -> ref 'b (list 'c) -> ref ('a & 'b) (list 'c)` + to build up a "reference" to a linked list +- An array of pointers `t *a[n]` could be represented by a + `ref base_type_at (i:nat{i ref (base_type_at i) t)` + For example, if I have two references `p: ref ('a1 * .. * 'am) 'c` + and `q: ref ('b1 * .. * 'bn) 'c` then an array containing `p` and `q` + could be represented by + + ```fstar + let base_type is_p = + if is_p then 'a1*..*'am else 'b1*..*'bn + in r: ref base_type (is_q:bool -> ref (base_type is_q) 'c) + ``` \ No newline at end of file diff --git a/share/steel/examples/steelc/HaclExample.fst b/share/steel/examples/steelc/HaclExample.fst new file mode 100644 index 000000000..fed7efe2b --- /dev/null +++ b/share/steel/examples/steelc/HaclExample.fst @@ -0,0 +1,145 @@ +module HaclExample + +open Steel.C.Model.PCM +open Steel.C.Opt +open Steel.C.Model.Connection +open Steel.C.StructLiteral +open Steel.C.Typedef +open FStar.FunctionalExtensionality +open Steel.Effect +open Steel.Effect.Atomic +open Steel.C.Fields +open Steel.C.Opt +open Steel.C.Model.Ref +open Steel.C.Reference +open Steel.C.TypedefNorm +open Steel.C.Array + +open FStar.FSet +open Steel.C.Typenat +open Steel.C.Typestring + +module U64 = FStar.UInt64 +module I = Steel.C.StdInt + +(** In this file we demonstrate how Steel could be used to manipulate the following data type used in Hacl*: + https://github.com/project-everest/hacl-star/blob/master/code/poly1305/Hacl.Impl.Poly1305.fsti#L18 + This Low* definition amounts to the struct definition + struct poly1305_ctx { uint64_t limbs[5]; uint64_t precomp[20]; }; + and, with our new model of structs and arrays and pointer-to-field, can be expresesd directly in Steel. + + See PointStruct.fst for more detailed explanations of the various definitions needed below. +*) + +module T = FStar.Tactics + +noextract inline_for_extraction +//[@@FStar.Tactics.Effect.postprocess_for_extraction_with(fun () -> +// T.norm [delta; iota; zeta_full; primops]; T.trefl ())] +let comp_tag = normalize (mk_string_t "HaclExample.comp") + +module U32 = FStar.UInt32 + +#push-options "--z3rlimit 30 --fuel 30" +noextract inline_for_extraction let five' = normalize (nat_t_of_nat 5) +noextract inline_for_extraction let five: size_t_of five' = mk_size_t (U32.uint_to_t 5) +noextract inline_for_extraction let twenty' = normalize (nat_t_of_nat 20) +noextract inline_for_extraction let twenty: size_t_of twenty' = mk_size_t (U32.uint_to_t 20) +#pop-options + +(** uint64_t[5] *) +[@@c_struct] +noextract inline_for_extraction +let five_u64s: typedef = array_typedef_sized U64.t five' five + +(** uint64_t[20] *) +[@@c_struct] +noextract inline_for_extraction +let twenty_u64s: typedef = array_typedef_sized U64.t twenty' twenty + +(** struct comp { uint64_t limbs[5]; uint64_t precomp[20]; }; *) + +[@@c_struct]//;c_typedef] +noextract inline_for_extraction +let comp_fields: c_fields = + fields_cons "limbs" five_u64s ( + fields_cons "precomp" twenty_u64s ( + fields_nil)) + +noextract inline_for_extraction +let comp = struct comp_tag comp_fields + +noextract inline_for_extraction +let comp_view = struct_view comp_tag comp_fields + +noextract inline_for_extraction +let comp_pcm = struct_pcm comp_tag comp_fields + +[@@c_typedef] +noextract inline_for_extraction +let c_comp: typedef = typedef_struct comp_tag comp_fields + +let _ = norm norm_c_typedef (mk_c_struct comp_tag comp_fields) + +(** To demonstrate how our model could be used, we write a simple + function that takes pointers to the limbs and precomp fields and + passes them to helper functions (which in this case simply set on + element of the corresponding array to zero) *) + +let do_something_with_limbs + (a: array 'a U64.t) +: Steel unit + (varray a) + (fun _ -> varray a) + (requires fun _ -> length a == 5) + (ensures fun _ _ _ -> True) += upd a (mk_size_t (U32.uint_to_t 2)) (U64.uint_to_t 0); + return () + +let do_something_with_precomp + (a: array 'a U64.t) +: Steel (array_or_null 'a U64.t) + (varray a) + (fun _ -> varray a) + (requires fun _ -> length a == 20) + (ensures fun _ _ _ -> True) += upd a (mk_size_t (U32.uint_to_t 19)) (U64.uint_to_t 0); + return (null _ _) + +let test_alloc_free + () +: SteelT unit + emp + (fun _ -> emp) += + let a = malloc true (mk_size_t 42ul) in + if Steel.C.Array.is_null a + then begin + Steel.C.Array.elim_varray_or_null_none a + end else begin + Steel.C.Array.elim_varray_or_null_some a; + free a + end; + return () + +#push-options "--fuel 0 --print_universes --print_implicits --z3rlimit 30" + +let test + (p: ref 'a comp comp_pcm) +: SteelT unit + (p `pts_to_view` comp_view emptyset) + (fun _ -> p `pts_to_view` comp_view emptyset) += let q = addr_of_struct_field "limbs" p in + let a = intro_varray q () in + let r = addr_of_struct_field "precomp" p in + let b = intro_varray r () in + do_something_with_limbs a; + let _ = do_something_with_precomp b in + elim_varray q a (); + elim_varray r b (); + unaddr_of_struct_field "precomp" p r; + unaddr_of_struct_field "limbs" p q; + change_equal_slprop (p `pts_to_view` _) (p `pts_to_view` _); + return () + +#pop-options diff --git a/share/steel/examples/steelc/HaclExample2.fst b/share/steel/examples/steelc/HaclExample2.fst new file mode 100644 index 000000000..f0b7afccb --- /dev/null +++ b/share/steel/examples/steelc/HaclExample2.fst @@ -0,0 +1,129 @@ +module HaclExample2 +open Steel.ST.GenElim +open Steel.ST.C.Types +open Steel.C.Typenat +open Steel.C.Typestring + +module SZ = FStar.SizeT +module U64 = FStar.UInt64 + +(** In this file we demonstrate how Steel could be used to manipulate the following data type used in Hacl*: + https://github.com/project-everest/hacl-star/blob/master/code/poly1305/Hacl.Impl.Poly1305.fsti#L18 + This Low* definition amounts to the struct definition + struct poly1305_ctx { uint64_t limbs[5]; uint64_t precomp[20]; }; + and, with our new model of structs and arrays and pointer-to-field, can be expresesd directly in Steel. + + See PointStruct.fst for more detailed explanations of the various definitions needed below. +*) + +noextract inline_for_extraction let five = normalize (nat_t_of_nat 5) +noextract inline_for_extraction let twenty = normalize (nat_t_of_nat 20) +noextract inline_for_extraction let comp_name = normalize (mk_string_t "HaclExample2.comp") + +noextract +inline_for_extraction +[@@norm_field_attr] +let comp_fields = + field_description_cons "limbs" (base_array0 five (scalar U64.t) 5sz) ( + field_description_cons "precomp" (base_array0 twenty (scalar U64.t) 20sz) ( + field_description_nil + )) + +noextract inline_for_extraction +let comp = struct0 comp_name "HaclExample2.comp" comp_fields + +let _ = define_struct0 comp_name "HaclExample2.comp" comp_fields + +(** To demonstrate how our model could be used, we write a simple + function that takes pointers to the limbs and precomp fields and + passes them to helper functions (which in this case simply set on + element of the corresponding array to zero) *) + +let do_something_with_limbs + (#v: Ghost.erased (Seq.seq (scalar_t U64.t))) + (a: array (scalar U64.t)) +: ST (Ghost.erased (Seq.seq (scalar_t U64.t))) + (array_pts_to a v) + (fun v' -> array_pts_to a v') + (requires + array_length a == 5 /\ + full_seq (scalar U64.t) v + ) + (ensures (fun v' -> + full_seq (scalar U64.t) v' + )) += let p = array_cell a 2sz in + write p 0uL; + unarray_cell _ _ _; + drop (has_array_cell _ _ _); + return _ + +let do_something_with_precomp + (#v: Ghost.erased (Seq.seq (scalar_t U64.t))) + (a: array (scalar U64.t)) +: ST (ptr (scalar U64.t)) + (array_pts_to a v) + (fun _ -> exists_ (fun (v': Seq.seq (scalar_t U64.t)) -> + array_pts_to a v' `star` + pure (full_seq (scalar U64.t) v') + )) + (requires + array_length a == 20 /\ + full_seq (scalar U64.t) v + ) + (ensures fun _ -> True) += let p = array_cell a 19sz in + write p 0uL; + unarray_cell _ _ _; + drop (has_array_cell _ _ _); + noop (); + return (null _) + +let test_alloc_free + () +: STT unit + emp + (fun _ -> emp) += + let a = array_alloc (scalar bool) 42sz in + let _ = gen_elim () in + if array_is_null a + then begin + rewrite (array_pts_to_or_null _ _) emp; + rewrite (freeable_or_null_array _) emp; + noop () + end else begin + let s = vpattern_replace (array_pts_to_or_null _) in + rewrite (array_pts_to_or_null _ _) (array_pts_to a s); + rewrite (freeable_or_null_array _) (freeable_array a); + array_free a + end + +#push-options "--fuel 0 --print_universes --print_implicits --z3rlimit 128" +#restart-solver + +let test + (#v: Ghost.erased (typeof comp)) + (p: ref comp) +: ST (Ghost.erased (typeof comp)) + (p `pts_to` v) + (fun v' -> p `pts_to` v') + (full comp v) + (fun v' -> full comp v') += let q = p `struct_field` "limbs" in + let a = array_of_base q in + let r = p `struct_field` "precomp" in + let _ = vpattern_replace_erased (pts_to p) in // FIXME: WHY WHY WHY? + let b = array_of_base r in + let _ = do_something_with_limbs a in + let _ = do_something_with_precomp b in + let _ = gen_elim () in + let _ = unarray_of_base q a in + let _ = unarray_of_base r b in + let _ = unstruct_field_alt p "precomp" r in + let _ = unstruct_field_alt p "limbs" q in + drop (has_struct_field p "limbs" q); + drop (has_struct_field p "precomp" r); + return _ + +#pop-options diff --git a/share/steel/examples/steelc/HaclExample3.fst b/share/steel/examples/steelc/HaclExample3.fst new file mode 100644 index 000000000..0c9bc6bee --- /dev/null +++ b/share/steel/examples/steelc/HaclExample3.fst @@ -0,0 +1,146 @@ +module HaclExample3 +open Steel.ST.GenElim +open Steel.ST.C.Types +open Steel.C.Typenat +open Steel.C.Typestring +open Steel.ST.C.Types.Struct.Aux +open Steel.ST.C.Types.UserStruct // hides Struct + +module SZ = FStar.SizeT +module U64 = FStar.UInt64 + +(** In this file we demonstrate how Steel could be used to manipulate the following data type used in Hacl*: + https://github.com/project-everest/hacl-star/blob/master/code/poly1305/Hacl.Impl.Poly1305.fsti#L18 + This Low* definition amounts to the struct definition + struct poly1305_ctx { uint64_t limbs[5]; uint64_t precomp[20]; }; + and, with our new model of structs and arrays and pointer-to-field, can be expresesd directly in Steel. + + See PointStruct.fst for more detailed explanations of the various definitions needed below. +*) + +noextract inline_for_extraction let five = normalize (nat_t_of_nat 5) +noextract inline_for_extraction let twenty = normalize (nat_t_of_nat 20) +noextract inline_for_extraction let comp_name = normalize (mk_string_t "HaclExample2.comp") + +noeq +type comp_t = { + limbs: base_array_t (scalar_t U64.t) five 5sz; + precomp: base_array_t (scalar_t U64.t) twenty 20sz; +} + +noextract +inline_for_extraction +[@@ norm_field_attr] +let comp_struct_def : struct_def comp_t = + let fields = FStar.Set.add "limbs" (FStar.Set.singleton "precomp") in + let fd_type (n: field_t fields) : Tot Type0 = match n with "limbs" -> base_array_t (scalar_t U64.t) five 5sz | "precomp" -> base_array_t (scalar_t U64.t) twenty 20sz in + let field_desc : field_description_gen_t (field_t fields) = { + fd_nonempty = nonempty_set_nonempty_type "limbs" fields; + fd_type = fd_type; + fd_typedef = (fun (n: field_t fields) -> match n returns typedef (fd_type n) with "limbs" -> base_array0 five (scalar U64.t) 5sz | "precomp" -> base_array0 twenty (scalar U64.t) 20sz); + } + in { + fields = fields; + field_desc = field_desc; + mk = (fun f -> Mkcomp_t (f "limbs") (f "precomp")); + get = (fun x f -> match f with "limbs" -> x.limbs | "precomp" -> x.precomp); + get_mk = (fun _ _ -> ()); + extensionality = (fun s1 s2 phi -> phi "limbs"; phi "precomp"); + } + +noextract inline_for_extraction +let comp = struct_typedef comp_struct_def + +(** To demonstrate how our model could be used, we write a simple + function that takes pointers to the limbs and precomp fields and + passes them to helper functions (which in this case simply set on + element of the corresponding array to zero) *) + +let do_something_with_limbs + (#v: Ghost.erased (Seq.seq (scalar_t U64.t))) + (a: array (scalar U64.t)) +: ST (Ghost.erased (Seq.seq (scalar_t U64.t))) + (array_pts_to a v) + (fun v' -> array_pts_to a v') + (requires + array_length a == 5 /\ + full_seq (scalar U64.t) v + ) + (ensures (fun v' -> + full_seq (scalar U64.t) v' + )) += let p = array_cell a 2sz in + write p 0uL; + unarray_cell _ _ _; + drop (has_array_cell _ _ _); + return _ + +let do_something_with_precomp + (#v: Ghost.erased (Seq.seq (scalar_t U64.t))) + (a: array (scalar U64.t)) +: ST (ptr (scalar U64.t)) + (array_pts_to a v) + (fun _ -> exists_ (fun (v': Seq.seq (scalar_t U64.t)) -> + array_pts_to a v' `star` + pure (full_seq (scalar U64.t) v') + )) + (requires + array_length a == 20 /\ + full_seq (scalar U64.t) v + ) + (ensures fun _ -> True) += let p = array_cell a 19sz in + write p 0uL; + unarray_cell _ _ _; + drop (has_array_cell _ _ _); + noop (); + return (null _) + +let test_alloc_free + () +: STT unit + emp + (fun _ -> emp) += + let a = array_alloc (scalar bool) 42sz in + let _ = gen_elim () in + if array_is_null a + then begin + rewrite (array_pts_to_or_null _ _) emp; + rewrite (freeable_or_null_array _) emp; + noop () + end else begin + let s = vpattern_replace (array_pts_to_or_null _) in + rewrite (array_pts_to_or_null _ _) (array_pts_to a s); + rewrite (freeable_or_null_array _) (freeable_array a); + array_free a + end + +#push-options "--z3rlimit 16" +#restart-solver + +let test + (#v: Ghost.erased (typeof comp)) + (p: ref comp) +: ST (Ghost.erased (typeof comp)) + (p `pts_to` v) + (fun v' -> p `pts_to` v') + (full comp v) + (fun v' -> full comp v') += let q = p `struct_field` "limbs" in + let a = array_of_base q in + let r = p `struct_field` "precomp" in + let _ = vpattern_replace_erased (pts_to p) in // FIXME: WHY WHY WHY? + let b = array_of_base r in + let _ = do_something_with_limbs a in + let _ = do_something_with_precomp b in + let _ = gen_elim () in + let _ = unarray_of_base q a in + let _ = unarray_of_base r b in + let _ = unstruct_field p "precomp" r in + let _ = unstruct_field p "limbs" q in + drop (has_struct_field p "limbs" q); + drop (has_struct_field p "precomp" r); + return _ + +#pop-options diff --git a/share/steel/examples/steelc/LList.fst b/share/steel/examples/steelc/LList.fst new file mode 100644 index 000000000..f14025af0 --- /dev/null +++ b/share/steel/examples/steelc/LList.fst @@ -0,0 +1,180 @@ +module LList +open Steel.ST.GenElim +open Steel.ST.C.Types + +module U32 = FStar.UInt32 + +noextract +inline_for_extraction +[@@ norm_field_attr] +let cell_fields = + field_description_cons "hd" (scalar U32.t) ( + field_description_cons "tl" (scalar void_ptr) ( + field_description_nil)) + +inline_for_extraction noextract +let cell_n : Type0 = norm Steel.C.Typestring.norm_typestring (Steel.C.Typestring.mk_string_t "LList.cell") + +let _ = define_struct0 cell_n "LList.cell" cell_fields + +inline_for_extraction noextract +let cell = struct0 cell_n "LList.cell" cell_fields + +[@@__reduce__] +let llist_nil (p: ptr cell) : Tot vprop = + pure (p == null _) + +[@@__reduce__] +let llist_cons (p: ptr cell) (a: U32.t) (q: Ghost.erased (list U32.t)) (llist: (ptr cell -> (l: Ghost.erased (list U32.t) { List.Tot.length l < List.Tot.length (a :: q) }) -> Tot vprop)) : Tot vprop = + exists_ (fun (p1: ref cell) -> exists_ (fun (p2: ptr cell) -> + pts_to p1 (struct_set_field "hd" (mk_scalar a) (struct_set_field "tl" (mk_scalar (p2 <: void_ptr)) (unknown cell))) `star` + llist p2 q `star` + freeable p1 `star` + pure (p == p1) + )) + +let rec llist (p: ptr cell) (l: Ghost.erased (list U32.t)) : Tot vprop (decreases (List.Tot.length l)) = + match Ghost.reveal l with + | [] -> llist_nil p + | a :: q -> llist_cons p a q llist + +let intro_llist_cons + (#opened: _) + (p1: ref cell) (#v1: Ghost.erased (typeof cell)) (p2: ptr cell) (a: U32.t) (q: Ghost.erased (list U32.t)) +: STGhost unit opened + (pts_to p1 v1 `star` + llist p2 q `star` + freeable p1 + ) + (fun _ -> llist p1 (a :: q)) + (Ghost.reveal v1 `struct_eq` struct_set_field "hd" (mk_scalar a) (struct_set_field "tl" (mk_scalar (p2 <: void_ptr)) (unknown cell))) + (fun _ -> True) += noop (); + rewrite_with_tactic (llist_cons p1 a q llist) (llist p1 (a :: q)) + +let elim_llist_cons + (#opened: _) + (p1: ptr cell) (a: U32.t) (q: Ghost.erased (list U32.t)) +: STGhostT (p2: Ghost.erased (ptr cell) { ~ (p1 == null _) }) opened + (llist p1 (a :: q)) + (fun p2 -> + pts_to p1 (struct_set_field "hd" (mk_scalar a) (struct_set_field "tl" (mk_scalar (p2 <: void_ptr)) (unknown cell))) `star` + llist p2 q `star` + freeable p1 + ) += rewrite_with_tactic (llist p1 (a :: q)) (llist_cons p1 a q llist); + let _ = gen_elim () in + let p2' = vpattern_erased (fun x -> llist x q) in + let p2 : (p2: Ghost.erased (ptr cell) { ~ (p1 == null _) }) = p2' in + vpattern_rewrite (fun x -> llist x q) p2; + rewrite (pts_to _ _) (pts_to _ _); + rewrite (freeable _) (freeable _); + _ + +[@@__reduce__] +let pllist0 + (p: ref (scalar (ptr cell))) + (l: Ghost.erased (list U32.t)) +: Tot vprop += exists_ (fun (pc: ptr cell) -> + pts_to p (mk_scalar pc) `star` + llist pc l + ) + +let pllist + (p: ref (scalar (ptr cell))) + (l: Ghost.erased (list U32.t)) +: Tot vprop += pllist0 p l + +let pllist_get + (#l: Ghost.erased (list U32.t)) + (p: ref (scalar (ptr cell))) +: STT (ptr cell) + (pllist p l) + (fun pc -> pts_to p (mk_scalar (Ghost.reveal pc)) `star` llist pc l) += rewrite (pllist p l) (pllist0 p l); + let _ = gen_elim () in + let pc = read p in + vpattern_rewrite (fun x -> llist x l) pc; + return pc + +let pllist_put + (#v: Ghost.erased (scalar_t (ptr cell))) + (#l: Ghost.erased (list U32.t)) + (p: ref (scalar (ptr cell))) + (pc: ptr cell) +: ST unit + (pts_to p v `star` llist pc l) + (fun _ -> pllist p l) + (full (scalar (ptr cell)) v) + (fun _ -> True) += write p pc; + rewrite (pllist0 p l) (pllist p l) + +let push + (#l: Ghost.erased (list U32.t)) + (a: U32.t) + (p: ref (scalar (ptr cell))) +: STT bool + (pllist p l) + (fun b -> pllist p (if b then a :: l else l)) += let c = alloc cell in + if is_null c + then begin + rewrite (pts_to_or_null _ _) emp; + rewrite (freeable_or_null _) emp; + return false + end else begin + rewrite (pts_to_or_null _ _) (pts_to c (uninitialized cell)); + rewrite (freeable_or_null c) (freeable c); + let p_tl = pllist_get p in + let c_hd = struct_field c "hd" in + let c_tl = struct_field c "tl" in + write c_hd a; + write c_tl p_tl; + unstruct_field c "tl" c_tl; + unstruct_field c "hd" c_hd; + intro_llist_cons c p_tl a l; + pllist_put p c; + drop (has_struct_field c "hd" _); + drop (has_struct_field _ _ _); + return true + end + +let pop + (#l: Ghost.erased (list U32.t)) + (p: ref (scalar (ptr cell))) + (sq: squash (Cons? l)) +: ST U32.t + (pllist p l) + (fun _ -> pllist p (List.Tot.tl l)) + (True) + (fun res -> res == List.Tot.hd l) += rewrite (pllist p l) (pllist p (List.Tot.hd l :: List.Tot.tl l)); + let c = pllist_get p in + let _ = elim_llist_cons c (List.Tot.hd l) (List.Tot.tl l) in + let c_hd = struct_field c "hd" in + let c_tl = struct_field c "tl" in + let res = read c_hd in + let p_tl = read c_tl in + vpattern_rewrite (fun x -> llist x _) p_tl; + unstruct_field c "tl" c_tl; + unstruct_field c "hd" c_hd; + free c; + pllist_put p p_tl; + drop (has_struct_field c "hd" _); + drop (has_struct_field _ _ _); + return res + +let init + (#v: Ghost.erased (scalar_t (ptr cell))) + (r: ref (scalar (ptr cell))) +: ST unit + (pts_to r v) + (fun _ -> pllist r []) + (full (scalar (ptr cell)) v) + (fun _ -> True) += noop (); + rewrite (llist_nil (null _)) (llist (null _) []); + pllist_put r (null _) diff --git a/share/steel/examples/steelc/LList2.fst b/share/steel/examples/steelc/LList2.fst new file mode 100644 index 000000000..eb625ba7f --- /dev/null +++ b/share/steel/examples/steelc/LList2.fst @@ -0,0 +1,196 @@ +module LList2 +open Steel.ST.GenElim +open Steel.ST.C.Types +open Steel.ST.C.Types.Struct.Aux +open Steel.ST.C.Types.UserStruct // hides Struct + +module U32 = FStar.UInt32 + +noeq +type cell_t = { + hd: scalar_t U32.t; + tl: scalar_t (ptr_gen cell_t); +} + +noextract +inline_for_extraction +[@@ norm_field_attr] +let cell_struct_def : struct_def cell_t = + let fields = FStar.Set.add "hd" (FStar.Set.singleton "tl") in + let field_desc : field_description_gen_t (field_t fields) = { + fd_nonempty = nonempty_set_nonempty_type "hd" fields; + fd_type = (fun (n: field_t fields) -> match n with "hd" -> scalar_t U32.t | "tl" -> scalar_t (ptr_gen cell_t)); + fd_typedef = (fun (n: field_t fields) -> match n with "hd" -> scalar U32.t | "tl" -> scalar (ptr_gen cell_t)); + } + in { + fields = fields; + field_desc = field_desc; + mk = (fun f -> Mkcell_t (f "hd") (f "tl")); + get = (fun x (f: field_t fields) -> match f with "hd" -> x.hd | "tl" -> x.tl); + get_mk = (fun _ _ -> ()); + extensionality = (fun s1 s2 phi -> phi "hd"; phi "tl"); +} + +noextract +inline_for_extraction +[@@ norm_field_attr] +let cell = struct_typedef cell_struct_def + +[@@__reduce__] +let llist_nil (p: ptr cell) : Tot vprop = + pure (p == null _) + +[@@__reduce__] +let llist_cons (p: ptr cell) (a: U32.t) (q: Ghost.erased (list U32.t)) (llist: (ptr cell -> (l: Ghost.erased (list U32.t) { List.Tot.length l < List.Tot.length (a :: q) }) -> Tot vprop)) : Tot vprop = + exists_ (fun (p1: ref cell) -> exists_ (fun (p2: ptr cell) -> + pts_to p1 ({ hd = mk_scalar a; tl = mk_scalar p2 }) `star` + llist p2 q `star` + freeable p1 `star` + pure (p == p1) + )) + +let rec llist (p: ptr cell) (l: Ghost.erased (list U32.t)) : Tot vprop (decreases (List.Tot.length l)) = + match Ghost.reveal l with + | [] -> llist_nil p + | a :: q -> llist_cons p a q llist + +let intro_llist_cons + (#opened: _) + (p1: ref cell) (#v1: Ghost.erased (typeof cell)) (p2: ptr cell) (a: U32.t) (q: Ghost.erased (list U32.t)) +: STGhost unit opened + (pts_to p1 v1 `star` + llist p2 q `star` + freeable p1 + ) + (fun _ -> llist p1 (a :: q)) + (Ghost.reveal v1 == ({ hd = mk_scalar a; tl = mk_scalar p2 })) + (fun _ -> True) += noop (); + rewrite_with_tactic (llist_cons p1 a q llist) (llist p1 (a :: q)) + +let elim_llist_cons + (#opened: _) + (p1: ptr cell) (a: U32.t) (q: Ghost.erased (list U32.t)) +: STGhostT (p2: Ghost.erased (ptr cell) { ~ (p1 == null _) }) opened + (llist p1 (a :: q)) + (fun p2 -> + pts_to p1 ({ hd = mk_scalar a; tl = mk_scalar (Ghost.reveal p2) }) `star` + llist p2 q `star` + freeable p1 + ) += rewrite_with_tactic (llist p1 (a :: q)) (llist_cons p1 a q llist); + let _ = gen_elim () in + let p2' = vpattern_erased (fun x -> llist x q) in + let p2 : (p2: Ghost.erased (ptr cell) { ~ (p1 == null _) }) = p2' in + vpattern_rewrite (fun x -> llist x q) p2; + rewrite (pts_to _ _) (pts_to _ _); + rewrite (freeable _) (freeable _); + _ + +[@@__reduce__] +let pllist0 + (p: ref (scalar (ptr cell))) + (l: Ghost.erased (list U32.t)) +: Tot vprop += exists_ (fun (pc: ptr cell) -> + pts_to p (mk_scalar pc) `star` + llist pc l + ) + +let pllist + (p: ref (scalar (ptr cell))) + (l: Ghost.erased (list U32.t)) +: Tot vprop += pllist0 p l + +let pllist_get + (#l: Ghost.erased (list U32.t)) + (p: ref (scalar (ptr cell))) +: STT (ptr cell) + (pllist p l) + (fun pc -> pts_to p (mk_scalar (Ghost.reveal pc)) `star` llist pc l) += rewrite (pllist p l) (pllist0 p l); + let _ = gen_elim () in + let pc = read p in + vpattern_rewrite (fun x -> llist x l) pc; + return pc + +let pllist_put + (#v: Ghost.erased (scalar_t (ptr cell))) + (#l: Ghost.erased (list U32.t)) + (p: ref (scalar (ptr cell))) + (pc: ptr cell) +: ST unit + (pts_to p v `star` llist pc l) + (fun _ -> pllist p l) + (full (scalar (ptr cell)) v) + (fun _ -> True) += write p pc; + rewrite (pllist0 p l) (pllist p l) + +let push + (#l: Ghost.erased (list U32.t)) + (a: U32.t) + (p: ref (scalar (ptr cell))) +: STT bool + (pllist p l) + (fun b -> pllist p (if b then a :: l else l)) += let c = alloc cell in + if is_null c + then begin + rewrite (pts_to_or_null _ _) emp; + rewrite (freeable_or_null _) emp; + return false + end else begin + rewrite (pts_to_or_null _ _) (pts_to c (uninitialized cell)); + rewrite (freeable_or_null c) (freeable c); + let p_tl = pllist_get p in + let c_hd = struct_field c "hd" in + let c_tl = struct_field c "tl" in + write c_hd a; + write c_tl p_tl; + unstruct_field c "tl" c_tl; + unstruct_field c "hd" c_hd; + intro_llist_cons c p_tl a l; + pllist_put p c; + drop (has_struct_field c "hd" _); + drop (has_struct_field _ _ _); + return true + end + +let pop + (#l: Ghost.erased (list U32.t)) + (p: ref (scalar (ptr cell))) + (sq: squash (Cons? l)) +: ST U32.t + (pllist p l) + (fun _ -> pllist p (List.Tot.tl l)) + (True) + (fun res -> res == List.Tot.hd l) += rewrite (pllist p l) (pllist p (List.Tot.hd l :: List.Tot.tl l)); + let c = pllist_get p in + let _ = elim_llist_cons c (List.Tot.hd l) (List.Tot.tl l) in + let c_hd = struct_field c "hd" in + let c_tl = struct_field c "tl" in + let res = read c_hd in + let p_tl = read c_tl in + vpattern_rewrite (fun x -> llist x _) p_tl; + unstruct_field c "tl" c_tl; + unstruct_field c "hd" c_hd; + free c; + pllist_put p p_tl; + drop (has_struct_field c "hd" _); + drop (has_struct_field _ _ _); + return res + +let init + (#v: Ghost.erased (scalar_t (ptr cell))) + (r: ref (scalar (ptr cell))) +: ST unit + (pts_to r v) + (fun _ -> pllist r []) + (full (scalar (ptr cell)) v) + (fun _ -> True) += noop (); + rewrite (llist_nil (null _)) (llist (null _) []); + pllist_put r (null _) diff --git a/share/steel/examples/steelc/Makefile b/share/steel/examples/steelc/Makefile new file mode 100644 index 000000000..295945c23 --- /dev/null +++ b/share/steel/examples/steelc/Makefile @@ -0,0 +1,108 @@ +all: world + +FSTAR_HOME ?= $(realpath $(dir $(shell which fstar.exe))/..) +export FSTAR_HOME +FSTAR_EXE = $(FSTAR_HOME)/bin/fstar.exe + +INCLUDE_PATH := $(FSTAR_HOME)/ulib/.cache $(FSTAR_HOME)/ulib/experimental + +ifdef KRML_HOME +KRML_EXE = $(KRML_HOME)/krml +MY_FSTAR_PATH=$(CURDIR)/my_fstar +INCLUDE_PATH += $(KRML_HOME)/krmllib $(KRML_HOME)/krmllib/obj $(MY_FSTAR_PATH)/lib/steel_c +endif + +world: verify test fstlib + +fstlib: + +$(MAKE) -C $@ + +FSTAR_OPTIONS := --cache_checked_modules \ + --cmi \ + --compat_pre_typed_indexed_effects \ + --already_cached 'Prims,FStar,LowStar,Steel,C' \ + $(addprefix --include ,$(INCLUDE_PATH)) \ + $(OTHERFLAGS) + +ifdef KRML_HOME +FSTAR_OPTIONS += --load ExtractSteelC +endif + +FSTAR = $(FSTAR_EXE) $(FSTAR_OPTIONS) + +ALL_SOURCE_FILES = $(wildcard *.fst *.fsti) +NO_EXTRACT_MODULES=PointStruct HaclExample ScalarUnion +EXTRACT_SOURCE_FILES = $(filter-out $(addsuffix .fst,$(NO_EXTRACT_MODULES)),$(ALL_SOURCE_FILES)) + +comma=, + +ifdef KRML_HOME # FIXME: should be HAS_OCAML +# We need to add some Low* files to the dependency roots, because +# of C._zero_for_deref +SOME_LOWSTAR_FILES = $(KRML_HOME)/krmllib/C.fst +MY_FSTAR=$(MY_FSTAR_PATH)/lib/steel_c/ExtractSteelC.cmxs +$(MY_FSTAR): + +$(MAKE) -C $(MY_FSTAR_PATH) +endif + +.depend: $(ALL_SOURCE_FILES) Makefile $(MY_FSTAR) + $(FSTAR) --dep full $(ALL_SOURCE_FILES) $(SOME_LOWSTAR_FILES) --extract 'krml:* -FStar -LowStar $(addprefix -,$(NO_EXTRACT_MODULES))' > $@.tmp + mv $@.tmp $@ + +depend: .depend + +-include .depend + +$(ALL_CHECKED_FILES): %.checked: + $(FSTAR) $< + @touch -c $@ + +verify: $(ALL_CHECKED_FILES) + echo $* + +%.fst-in %.fsti-in: + @echo $(FSTAR_OPTIONS) + +.PRECIOUS: %.ml +%.ml: + $(FSTAR) $(notdir $(subst .checked,,$<)) --codegen OCaml \ + --extract_module $(basename $(notdir $(subst .checked,,$<))) + +clean: + -rm -rf *.checked *.krml .depend .depend.tmp *.c *.h *.o compile_flags.txt extract + +ifdef KRML_HOME + +.PRECIOUS: %.krml +%.krml: + $(FSTAR) $(notdir $(subst .checked,,$<)) --codegen krml \ + --extract_module $(basename $(notdir $(subst .checked,,$<))) + +ALL_MODULE_NAMES=$(basename $(EXTRACT_SOURCE_FILES)) +FILTERED_KRML_FILES=$(filter-out FStar_NMST.krml Steel_%.krml,$(ALL_KRML_FILES)) + +extract: $(FILTERED_KRML_FILES) + $(KRML_EXE) -skip-compilation -skip-makefiles -bundle 'FStar.\*,Steel.\*,C' -add-include '"steel_c.h"' $^ + touch $@ + +ALL_C_FILES=$(addsuffix .c,$(ALL_MODULE_NAMES)) + +$(ALL_C_FILES): %.c: extract + test -f $@ + touch $@ + +ALL_O_FILES=$(subst .c,.o,$(ALL_C_FILES)) + +$(ALL_O_FILES): %.o: %.c + $(CC) $(CFLAGS) -DKRML_VERIFIED_UINT128 -I $(KRML_HOME)/include -I $(KRML_HOME)/krmllib/dist/minimal -I lib -o $@ -c $< + +test: $(ALL_O_FILES) + +else # no KRML_HOME + +test: + echo KaRaMeL is not installed, skipping test + +endif # KRML_HOME + +.PHONY: all world verify clean depend test fstlib diff --git a/share/steel/examples/steelc/PointStruct.fst b/share/steel/examples/steelc/PointStruct.fst new file mode 100644 index 000000000..c8069857a --- /dev/null +++ b/share/steel/examples/steelc/PointStruct.fst @@ -0,0 +1,203 @@ +module PointStruct + +open Steel.C.Model.PCM +open Steel.C.Opt +open Steel.C.Model.Connection +open Steel.C.Model.Struct +open Steel.C.StructLiteral +open Steel.C.Typedef +open FStar.FunctionalExtensionality +open Steel.Effect +open Steel.Effect.Atomic +open Steel.C.Fields +open Steel.C.Reference +open Steel.C.TypedefNorm + +open FStar.FSet +open Steel.C.Typestring + +module U32 = FStar.UInt32 + +(** A struct is encoded by what amounts to a list of (field name, typedef) pairs. + In this example, we define a struct named point with two u32 fields; to do so + we need a typedef for u32s, which can be found in Steel.C.StdInt. *) +module I = Steel.C.StdInt + +module T = FStar.Tactics + +(** To enforce nominality, structs are labelled by struct tags, which + are represented by strings encoded as F* types. This encoding allows + struct tags to stick around after erasure to OCaml. + + TODO the normalization is only needed for extraction, and so it + should be possible to use postprocess_for_extraction_with instead + of normalize. However, at present it seems that + postprocess_for_extraction_with does not run on definitions of + type Type. *) + +noextract inline_for_extraction +//[@@FStar.Tactics.Effect.postprocess_for_extraction_with(fun () -> +// T.norm [delta; iota; zeta_full; primops]; T.trefl ())] +let point_tag = normalize (mk_string_t "PointStruct.point") + +(** point_fields is a representation of the list of (field name, + typedef) pairs of the point struct we are defining. For a more + detailed explanation for why this list is constructed using + fields_cons and fields_nil rather than as a normal F* list, see + Steel.C.Fields.fsti *) + +[@@c_struct] +noextract inline_for_extraction +let point_fields: c_fields = + fields_cons "x" I.uint32 ( + fields_cons "y" I.uint32 ( + fields_nil)) + +(** The type of (struct point) values *) +noextract inline_for_extraction +let point = struct point_tag point_fields + +(** A way of viewing (struct point) PCM carrier values as (struct point) values *) +noextract inline_for_extraction +let point_view = struct_view point_tag point_fields + +(** The PCM used to model the point struct *) +noextract inline_for_extraction +let point_pcm = struct_pcm point_tag point_fields + +(** A typedef for the point struct (useful if this struct needs to be nested inside another struct definition) *) +[@@c_typedef] +noextract inline_for_extraction +let c_point: typedef = typedef_struct point_tag point_fields + +(** Define the point struct. Karamel detects this definition and + emits a corresponding C typedef at extraction time. See + Steel.C.StructLiteral.mk_c_struct for more information. *) +let _ = norm norm_c_typedef (mk_c_struct point_tag point_fields) + +(** There is some flexibility in how these definitions can be constructed. + Below, we define + struct line { struct point first; struct point second; }; + but split the list of (field name, typedef) pairs across two + definitions: .second is declared in the definition below, while + .first is declared "inline" in the call to mk_c_struct parsed by + Karamel. + + This code is just to illustrate that extraction is fairly + flexible: all Karamel cares about is that the call to mk_c_struct + normalizes (under rules norm_c_typedef) to a valid struct + definition. In practice, it isn't recommended to split the list of + fields like this. *) +noextract inline_for_extraction +let line_fields_second_half: c_fields = + fields_cons "second" c_point fields_nil + +noextract inline_for_extraction +let line_tag = normalize (mk_string_t "PointStruct.line") + +let _ = norm norm_c_typedef (mk_c_struct line_tag (fields_cons "first" c_point line_fields_second_half)) + +#push-options "--fuel 0" + +#push-options "--print_universes --print_implicits --z3rlimit 100 --query_stats" + +open Steel.C.Reference + +(** To illustrate pointer-to-field in action, we write a function swap + that swaps x and y coordinates of a point struct. *) +val swap (p: ref 'a point point_pcm) +: Steel unit + (p `pts_to_view` point_view emptyset) + (fun _ -> p `pts_to_view` point_view emptyset) + (requires fun _ -> True) + (ensures fun h q h' -> + h' (p `pts_to_view` point_view emptyset) `struct_get` "x" + == h (p `pts_to_view` point_view emptyset) `struct_get` "y" /\ + h' (p `pts_to_view` point_view emptyset) `struct_get` "y" + == h (p `pts_to_view` point_view emptyset) `struct_get` "x") + +let swap p = + let initial_point = gget (p `pts_to_view` point_view emptyset) in + (** Take pointers to the "x" and "y" fields *) + let q = addr_of_struct_field "x" p in + let r = addr_of_struct_field "y" p in + let x = opt_read_sel q in + let y = opt_read_sel r in + q `opt_write_sel` y; + r `opt_write_sel` x; + (** Give ownership of x and y fields back to p *) + unaddr_of_struct_field "y" p r; + unaddr_of_struct_field "x" p q; + (** The view for structs is parameterized by a set of fields that have been loaned out. + When these loans are returned to p, the corresponding field names are removed from the set of loaned fields. + However, this new set is not definitionally equal to emptyset, the following equality needs to be proved by SMT: *) + change_equal_slprop + (p `pts_to_view` point_view (remove "x" (remove "y" (insert "y" (insert "x" emptyset))))) + (p `pts_to_view` point_view emptyset); + (** TOOD in the future, may want to make struct_view smt_fallback in its first argument and mark point_view as unfold *) + (** For some reason these assertions is necessary to get the program + to verify. It's unclear why, since such assertions are normally + easily provable by SMT *) + let final_point = gget (p `pts_to_view` point_view emptyset) in + assert (struct_get #point_tag #point_fields #emptyset final_point "x" == + struct_get #point_tag #point_fields #emptyset initial_point "y"); + assert (struct_get #point_tag #point_fields #emptyset final_point "y" == + struct_get #point_tag #point_fields #emptyset initial_point "x"); + return () + +(** We can also define swap by calling a helper function that swaps + any two pointers. This demonstrates that one can manipulate + pointers in a generic way: the helper function does not need to + know that its inputs are pointers to fields of a struct in order + to work. *) +let generic_swap_sel (p:ref 'a 'c (opt_pcm #'c)) (q:ref 'b 'c (opt_pcm #'c)) +: Steel unit + ((p `pts_to_view` opt_view _) `star` (q `pts_to_view` opt_view _)) + (fun _ -> (p `pts_to_view` opt_view _) `star` (q `pts_to_view` opt_view _)) + (requires (fun _ -> True)) + (ensures (fun h _ h' -> + h' (p `pts_to_view` opt_view _) == h (q `pts_to_view` opt_view _) /\ + h' (q `pts_to_view` opt_view _) == h (p `pts_to_view` opt_view _) + )) += (* A tmp = *p; *) + let tmp = opt_read_sel p in + (* *p = *q; *) + let vy = opt_read_sel q in + opt_write_sel p vy; + (* *q = tmp *) + opt_write_sel q tmp; + return () + +val swap' (p: ref 'a point point_pcm) +: Steel (ptr 'a point point_pcm) + (p `pts_to_view` point_view emptyset) + (fun _ -> p `pts_to_view` point_view emptyset) + (requires fun _ -> True) + (ensures fun h q h' -> + //h' (p `pts_to_view` point_view emptyset) `struct_get` "x" + //== h (p `pts_to_view` point_view emptyset) `struct_get` "y" /\ + //h' (p `pts_to_view` point_view emptyset) `struct_get` "y" + //== h (p `pts_to_view` point_view emptyset) `struct_get` "x") + True) + +let swap' p = + let q = addr_of_struct_field "x" p in + let r = addr_of_struct_field "y" p in + generic_swap_sel q r; + unaddr_of_struct_field "y" p r; + unaddr_of_struct_field "x" p q; + change_equal_slprop (p `pts_to_view` _) (p `pts_to_view` _); + return (null _ _ _) + +let test_malloc_free () : SteelT unit emp (fun _ -> emp) = + let c = malloc 42ul in + if is_null c _ + then begin + elim_pts_to_view_or_null_null _ c (opt_view _); + return () + end else begin + elim_pts_to_view_or_null_not_null c (opt_view _); + free c + end; + return () + diff --git a/share/steel/examples/steelc/PointStruct2.fst b/share/steel/examples/steelc/PointStruct2.fst new file mode 100644 index 000000000..d47110023 --- /dev/null +++ b/share/steel/examples/steelc/PointStruct2.fst @@ -0,0 +1,55 @@ +module PointStruct2 +open Steel.ST.Util +open Steel.ST.C.Types + +module U32 = FStar.UInt32 +// module C = C // for _zero_for_deref + +let swap (#v1 #v2: Ghost.erased U32.t) (r1 r2: ref (scalar U32.t)) : STT unit + ((r1 `pts_to` mk_scalar (Ghost.reveal v1)) `star` (r2 `pts_to` mk_scalar (Ghost.reveal v2))) + (fun _ -> (r1 `pts_to` mk_scalar (Ghost.reveal v2)) `star` (r2 `pts_to` mk_scalar (Ghost.reveal v1))) += let x1 = read r1 in + let x2 = read r2 in + write r1 x2; + write r2 x1; + return () // necessary to enable smt_fallback + +noextract +inline_for_extraction +[@@ norm_field_attr] +let point_fields = + field_description_cons "x" (scalar U32.t) ( + field_description_cons "y" (scalar U32.t) ( + field_description_nil)) + +let _ = define_struct "PointStruct2.point" point_fields + +inline_for_extraction noextract +let point = struct "PointStruct2.point" point_fields + +#push-options "--query_stats --fuel 0" + +let swap_struct (p: ref point) (v: Ghost.erased (typeof point)) +: ST (Ghost.erased (typeof point)) + (p `pts_to` v) + (fun v' -> p `pts_to` v') + (requires + exists (vx vy: U32.t) . struct_get_field v "x" == mk_scalar vx /\ struct_get_field v "y" == mk_scalar vy + ) + (ensures fun v' -> + struct_get_field v' "x" == struct_get_field v "y" /\ + struct_get_field v' "y" == struct_get_field v "x" + ) += let px = struct_field p "x" in + let py = struct_field p "y" in + let x = read px in + let y = read py in + write px y; + write py x; + unstruct_field p "x" px; + unstruct_field p "y" py; + drop (has_struct_field _ _ px); + drop (has_struct_field _ _ _); + return _ + +#pop-options diff --git a/share/steel/examples/steelc/ScalarUnion.fst b/share/steel/examples/steelc/ScalarUnion.fst new file mode 100644 index 000000000..33ad215b8 --- /dev/null +++ b/share/steel/examples/steelc/ScalarUnion.fst @@ -0,0 +1,112 @@ +module ScalarUnion + +open Steel.C.Model.PCM +open Steel.C.Opt +open Steel.C.Model.Connection +open Steel.C.StructLiteral +open Steel.C.UnionLiteral +open Steel.C.Typedef +open FStar.FunctionalExtensionality +open Steel.Effect +open Steel.Effect.Atomic +open Steel.C.Fields +open Steel.C.Model.Ref +open Steel.C.Reference +open Steel.C.TypedefNorm + +open FStar.FSet +open Steel.C.Typestring +open Steel.C.Typenat + +module U32 = FStar.UInt32 +module U16 = FStar.UInt16 + +(** A union is encoded by what amounts to a list of (field name, + typedef) pairs. In this example, we define a union named + u32_or_u16 with one u32 field and one u16 field; to do so we need + typedefs for u32s and u16s, which can be found in Steel.C.StdInt. *) +module I = Steel.C.StdInt + +module T = FStar.Tactics + +(** Like structs, unions are labelled by tags to enforce nominality. + For a more detailed explanation see PointStruct.fst *) + +noextract inline_for_extraction +//[@@FStar.Tactics.Effect.postprocess_for_extraction_with(fun () -> +// T.norm [delta; iota; zeta_full; primops]; T.trefl ())] +let u32_or_u16_tag = normalize (mk_string_t "ScalarUnion.u32_or_u16") + +(** The fields of a u32_or_u16. *) +[@@c_struct] +noextract inline_for_extraction +let u32_or_u16_fields: c_fields = + fields_cons "as_u32" I.uint32 ( + fields_cons "as_u16" I.uint16 ( + fields_nil)) + +(** The type of (union u32_or_u16) values. *) +noextract inline_for_extraction +let u32_or_u16 = union u32_or_u16_tag u32_or_u16_fields + +(** A way to view PCM carrier values as (union u32_or_u16) values. *) +noextract inline_for_extraction +let u32_or_u16_view = union_view u32_or_u16_tag u32_or_u16_fields + +(** The PCM that models (union u32_or_u16) values. *) +noextract inline_for_extraction +let u32_or_u16_pcm = union_pcm u32_or_u16_tag u32_or_u16_fields + +(** A typedef for u32_or_u16, useful in case it is needed as a field of a struct or union *) +[@@c_typedef] +noextract inline_for_extraction +let c_u32_or_u16: typedef = typedef_union u32_or_u16_tag u32_or_u16_fields + +(** Define the union. Like with mk_c_struct, Karamel detects this + definition at extraction type and emits the corresponding typedef. *) +let _ = norm norm_c_typedef (mk_c_union u32_or_u16_tag u32_or_u16_fields) + +#push-options "--fuel 0" + +#push-options "--print_universes --print_implicits" +// --z3rlimit 30" + +(** Switch a case of the union to the u16 case, by writing x to it. *) +val switch_to_u16 + (p: ref unit u32_or_u16 u32_or_u16_pcm) + (x: U16.t) +: Steel unit + (p `pts_to_view` u32_or_u16_view) + (fun _ -> p `pts_to_view` u32_or_u16_view) + (requires fun _ -> True) + (ensures fun h q h' -> True) + +#push-options "--fuel 0" + +let switch_to_u16 p x = + let h = get () in // Needed to prove switch_union_field's precondition + switch_union_field "as_u16" x p; + return () + +(** Helper function that zeros the memory location pointed to by p. *) +let zero_u32_ref (p:ref 'a U32.t (opt_pcm #U32.t)) +: Steel unit + (p `pts_to_view` opt_view _) + (fun _ -> p `pts_to_view` opt_view _) + (requires fun _ -> True) + (ensures fun _ _ _ -> True) += opt_write_sel p 0ul + +(** Given a union in the u32 case, set the u32 to zero. *) +val zero_u32_of_union (p: ref unit u32_or_u16 u32_or_u16_pcm) +: Steel unit + (p `pts_to_view` u32_or_u16_view) + (fun _ -> p `pts_to_view` u32_or_u16_view) + (requires fun h -> dfst (dtuple2_of_union (h (p `pts_to_view` u32_or_u16_view))) == "as_u32") + (ensures fun h q h' -> True) + +let zero_u32_of_union p = + let q: ref _ U32.t _ = addr_of_union_field "as_u32" p in + zero_u32_ref q; + unaddr_of_union_field "as_u32" p q; + return () diff --git a/share/steel/examples/steelc/ScalarUnion2.fst b/share/steel/examples/steelc/ScalarUnion2.fst new file mode 100644 index 000000000..2f278941c --- /dev/null +++ b/share/steel/examples/steelc/ScalarUnion2.fst @@ -0,0 +1,78 @@ +module ScalarUnion2 +open Steel.ST.Util +open Steel.ST.C.Types + +module U32 = FStar.UInt32 +module U16 = FStar.UInt16 + +(** Like structs, unions are labelled by tags to enforce nominality. + For a more detailed explanation see PointStruct2.fst *) + +noextract +inline_for_extraction +[@@ norm_field_attr] +let u32_or_u16_fields = + field_description_cons "as_u32" (scalar U32.t) ( + field_description_cons "as_u16" (scalar U16.t) ( + field_description_nil)) + +(** Define the union. Like with mk_c_struct, Karamel detects this + definition at extraction type and emits the corresponding typedef. *) +let _ = define_union "ScalarUnion2.u32_or_u16" u32_or_u16_fields + +(** The type of (union u32_or_u16) values. *) +noextract inline_for_extraction +let u32_or_u16 = union "ScalarUnion2.u32_or_u16" u32_or_u16_fields + +noextract inline_for_extraction +let u32_or_u16_t = typeof u32_or_u16 + +#push-options "--fuel 0" + +#push-options "--print_universes --print_implicits" +// --z3rlimit 30" + +(** Switch a case of the union to the u16 case, by writing x to it. *) +val switch_to_u16 + (#v: Ghost.erased u32_or_u16_t) + (p: ref u32_or_u16) + (x: U16.t) +: ST unit + (p `pts_to` v) + (fun _ -> p `pts_to` union_set_field _ _ _ "as_u16" (mk_scalar x)) + (requires full u32_or_u16 v) + (ensures fun _ -> True) + +#push-options "--fuel 0 --print_bound_var_types" + +let switch_to_u16 p x = + let p16 = union_switch_field p "as_u16" in + write p16 x; + ununion_field p "as_u16" _; + drop (has_union_field _ _ _); + return () + +(** Helper function that zeros the memory location pointed to by p. *) +let zero_u32_ref (#v: Ghost.erased (scalar_t U32.t)) (p:ref (scalar U32.t)) +: ST unit + (p `pts_to` v) + (fun _ -> p `pts_to` mk_scalar 0ul) + (requires full (scalar U32.t) v) + (ensures fun _ -> True) += write p 0ul; + return () + +(** Given a union in the u32 case, set the u32 to zero. *) +val zero_u32_of_union (#v: Ghost.erased u32_or_u16_t) (p: ref u32_or_u16) +: ST unit + (p `pts_to` v) + (fun _ -> p `pts_to` union_set_field _ _ _ "as_u32" (mk_scalar 0ul)) + (requires exists (v0: scalar_t U32.t) . Ghost.reveal v == union_set_field _ _ _ "as_u32" v0 /\ full (scalar U32.t) v0) + (ensures fun _ -> True) + +let zero_u32_of_union #v p = + let q = union_field p "as_u32" in + zero_u32_ref q; + ununion_field p "as_u32" _; + drop (has_union_field _ _ _); + return () diff --git a/src/extraction/ExtractSteelC.fst b/src/extraction/ExtractSteelC.fst new file mode 100644 index 000000000..28543f73f --- /dev/null +++ b/src/extraction/ExtractSteelC.fst @@ -0,0 +1,489 @@ +module ExtractSteelC + +friend FStar.Extraction.Krml +open FStar.Compiler.Effect +open FStar.Compiler.List +open FStar +open FStar.Compiler +open FStar.Compiler.Util +open FStar.Extraction +open FStar.Extraction.ML +open FStar.Extraction.ML.Syntax +open FStar.Const +open FStar.BaseTypes +open FStar.Extraction.Krml +module K = FStar.Extraction.Krml +module BU = FStar.Compiler.Util + +(* JL: TODO: in stdlib somewhere? *) +let opt_bind (m: option 'a) (k: 'a -> option 'b): option 'b = + match m with Some x -> k x | None -> None + +let char_of_typechar (t: mlty): option char = + match t with + | MLTY_Named ([], p) -> + let p = Syntax.string_of_mlpath p in + if p = "Steel.C.Typestring.cdot" then + Some '.' + else if BU.starts_with p "Steel.C.Typestring.c" then + Some (FStar.String.get p (FStar.String.strlen "Steel.C.Typestring.c")) + else + None + + | _ -> None + +let string_of_typestring (t: mlty): option string = + let rec go t: option (list string) = + match t with + | MLTY_Named ([], p) + when Syntax.string_of_mlpath p = "Steel.C.Typestring.string_nil" + -> + Some [] + + | MLTY_Named ([c; t], p) + when Syntax.string_of_mlpath p = "Steel.C.Typestring.string_cons" + -> + opt_bind (char_of_typechar c) (fun c' -> + opt_bind (go t) (fun s' -> + Some (String.make 1 c' :: s'))) + + | _ -> None + in + opt_bind (go t) (fun ss -> Some (FStar.String.concat "" ss)) + +let lident_of_string (s: string): option lident = + let path = FStar.String.split ['.'] s in + let rec go p = + match p with + | [] -> None + | [s] -> Some ([], s) + | s :: p -> + opt_bind (go p) (fun (names, name) -> + Some (s :: names, name)) + in go path + +let lident_of_typestring (t: mlty): option lident = + opt_bind (string_of_typestring t) lident_of_string + +let int_of_typenat (t: mlty): option int = + let rec go t = + match t with + | MLTY_Named ([], p) + when Syntax.string_of_mlpath p = "Steel.C.Typenat.z" + -> + Some 0 + + | MLTY_Named ([t], p) + when Syntax.string_of_mlpath p = "Steel.C.Typenat.s" + -> + opt_bind (go t) (fun n -> Some (n + 1)) + + | _ -> + None + in + go t + +let my_types_without_decay () = + register_pre_translate_type_without_decay begin fun env t -> + match t with + + | MLTY_Named ([tag; _; _], p) when + BU.starts_with (Syntax.string_of_mlpath p) "Steel.C.StructLiteral.struct'" + -> + TQualified (must (lident_of_typestring tag)) + + | MLTY_Named ([tag; _; _; _], p) when + BU.starts_with (Syntax.string_of_mlpath p) "Steel.ST.C.Types.Struct.struct_t0" + || BU.starts_with (Syntax.string_of_mlpath p) "Steel.ST.C.Types.Union.union_t0" + -> + TQualified (must (lident_of_typestring tag)) + + | MLTY_Named ([tag; _], p) when + BU.starts_with (Syntax.string_of_mlpath p) "Steel.C.UnionLiteral.union" + -> + TQualified (must (lident_of_typestring tag)) + + | MLTY_Named ([_; arg; _; _], p) when + Syntax.string_of_mlpath p = "Steel.C.Reference.ptr" + -> + TBuf (translate_type_without_decay env arg) + + | MLTY_Named ([arg], p) when + Syntax.string_of_mlpath p = "Steel.ST.C.Types.Array.array_ptr_gen" + -> + TBuf (translate_type_without_decay env arg) + + | MLTY_Named ([arg], p) when + Syntax.string_of_mlpath p = "Steel.ST.C.Types.Base.ptr_gen" + -> + TBuf (translate_type_without_decay env arg) + + | MLTY_Named ([arg], p) when + Syntax.string_of_mlpath p = "Steel.ST.C.Types.Scalar.scalar_t" + -> + translate_type_without_decay env arg + + | MLTY_Named ([t; n; s], p) + when Syntax.string_of_mlpath p = "Steel.C.Array.Base.array_view_type_sized" + || Syntax.string_of_mlpath p = "Steel.ST.C.Types.Array.base_array_t" + -> + TArray ( + translate_type_without_decay env t, + (UInt32, string_of_int (must (int_of_typenat n)))) + + | MLTY_Named ([_; arg], p) when + Syntax.string_of_mlpath p = "Steel.C.Array.Base.array_or_null_from" + -> + TBuf (translate_type_without_decay env arg) + + | _ -> raise NotSupportedByKrmlExtension +end + +let my_types () = register_pre_translate_type begin fun env t -> + match t with + | MLTY_Named ([t; _; _], p) + when Syntax.string_of_mlpath p = "Steel.C.Array.Base.array_view_type_sized" + || Syntax.string_of_mlpath p = "Steel.ST.C.Types.Array.base_array_t" + -> + TBuf (translate_type_without_decay env t) + + | MLTY_Named ([t; _], p) + when Syntax.string_of_mlpath p = "Steel.C.Array.Base.array_view_type" + -> + TBuf (translate_type_without_decay env t) + + | _ -> raise NotSupportedByKrmlExtension +end + +let my_exprs () = register_pre_translate_expr begin fun env e -> + match e.expr with + | MLE_App ({ expr = MLE_TApp({ expr = MLE_Name p }, _) }, [ _ (* typedef *) ]) + when ( + string_of_mlpath p = "Steel.ST.C.Types.Base.alloc" || + false) -> + EBufCreateNoInit (ManuallyManaged, EConstant (UInt32, "1")) + + | MLE_App ({ expr = MLE_TApp({ expr = MLE_Name p }, _) }, [ e1; e2; _ (* sq *) ]) + when ( + string_of_mlpath p = "Steel.C.Array.Base.malloc_from" || + false) -> + EBufCreate (ManuallyManaged, translate_expr env e1, translate_expr env e2) + + | MLE_App ({ expr = MLE_TApp({ expr = MLE_Name p }, _) }, [ _ (* td *); sz ]) + when ( + string_of_mlpath p = "Steel.ST.C.Types.Array.array_ptr_alloc" || + false) -> + EBufCreateNoInit (ManuallyManaged, translate_expr env sz) + + | MLE_App ({ expr = MLE_TApp({ expr = MLE_Name p }, _) }, [ e ]) + when ( + string_of_mlpath p = "Steel.C.Opt.malloc" || + false) -> + EBufCreate (ManuallyManaged, translate_expr env e, EConstant (UInt32, "1")) + + | MLE_App ({ expr = MLE_TApp({ expr = MLE_Name p }, _) }, [ e2 ]) + when string_of_mlpath p = "Steel.C.Opt.free" + -> + EBufFree (translate_expr env e2) + + | MLE_App ({ expr = MLE_TApp({ expr = MLE_Name p }, _) }, [ e2; _ (* a' *); _ (* sq *) ]) + when ( + string_of_mlpath p = "Steel.C.Array.Base.free_from" || + false) -> + EBufFree (translate_expr env e2) + + | MLE_App ({ expr = MLE_TApp({ expr = MLE_Name p }, _) }, [ _(* td *); _ (* s *); e2; _ (* len *) ]) + when ( + string_of_mlpath p = "Steel.ST.C.Types.Array.array_ref_free" || + false) -> + EBufFree (translate_expr env e2) + + | MLE_App ({ expr = MLE_TApp({ expr = MLE_Name p }, _) }, [ _ (* typedef *); _ (* v *); e ]) when + string_of_mlpath p = "Steel.ST.C.Types.Base.free" -> + EBufFree (translate_expr env e) + +(* BEGIN support for the Steel null pointer. *) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, [t])}, [_ (* opened *); _ (* td *); _ (* v *); e; _ (* len *) ]) + when string_of_mlpath p = "Steel.ST.C.Types.Array.array_ptr_is_null" + -> generate_is_null (translate_type env t) (translate_expr env e) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, [t])}, [_ (* opened *); e; _ (* a' *); _ (* sq *) ]) + when string_of_mlpath p = "Steel.C.Array.Base.is_null_from" + -> generate_is_null (translate_type env t) (translate_expr env e) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, [_; t])}, [_ (* opened *); _ (* pcm *); e; _ (* view *)]) + when string_of_mlpath p = "Steel.C.Reference.is_null" + -> generate_is_null (translate_type env t) (translate_expr env e) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, [t])}, [_ (* opened *); _ (* td *); _ (* v *); e]) + when string_of_mlpath p = "Steel.ST.C.Types.Base.is_null" + -> generate_is_null (translate_type env t) (translate_expr env e) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, [t])}, _) + when Syntax.string_of_mlpath p = "Steel.C.Array.Base.null_from" + || Syntax.string_of_mlpath p = "Steel.ST.C.Types.Array.null_array_ptr" + -> EBufNull (translate_type env t) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, t::_)}, [_ (* pcm *)]) + when string_of_mlpath p = "Steel.C.Reference.null" + -> EBufNull (translate_type env t) + + | MLE_TApp ({expr=MLE_Name p}, [t]) when + string_of_mlpath p = "Steel.ST.C.Types.Base.null_gen" + -> EBufNull (translate_type env t) + +(* END support for the Steel null pointer *) + + + (* Operations on Steel.C.Reference.ref *) + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, _)}, _) + when string_of_mlpath p = "Steel.C.StructLiteral.unaddr_of_struct_field" -> + EUnit + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, _)}, _) + when string_of_mlpath p = "Steel.C.UnionLiteral.unaddr_of_union_field" -> + EUnit + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, [_; _; _; struct_name])}, + [_; _; {expr=MLE_Const (MLC_String field_name)}; r]) + when string_of_mlpath p = "Steel.C.StructLiteral.addr_of_struct_field''" -> + EAddrOf (EField ( + TQualified (must (lident_of_typestring struct_name)), + EBufRead (translate_expr env r, EConstant (UInt32, "0")), + field_name)) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, _)}, + [ + ({expr=MLE_Const (MLC_String struct_name)}) + ; _ (* fields *) + ; _ (* v *) + ; r + ; ({expr=MLE_Const (MLC_String field_name)}) + ; _ (* td' *) + ]) + when string_of_mlpath p = "Steel.ST.C.Types.Struct.struct_field0" + || string_of_mlpath p = "Steel.ST.C.Types.Union.union_field0" + || string_of_mlpath p = "Steel.ST.C.Types.Union.union_switch_field0" + -> + EAddrOf (EField ( + TQualified (must (lident_of_string struct_name)), + EBufRead (translate_expr env r, EQualified (["C"], "_zero_for_deref")), + field_name)) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, _)}, + [ + _ (* struct_def *) + ; _ (* v *) + ; r + ; ({expr=MLE_Const (MLC_String field_name)}) + ; _ (* td' *) + ]) + when string_of_mlpath p = "Steel.ST.C.Types.UserStruct.struct_field0" + -> + EAddrOf (EField ( + assert_lid env r.mlty, + EBufRead (translate_expr env r, EQualified (["C"], "_zero_for_deref")), + field_name)) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, [_; _; union_name])}, + [_; {expr=MLE_Const (MLC_String field_name)}; r]) + when string_of_mlpath p = "Steel.C.UnionLiteral.addr_of_union_field''" -> + EAddrOf (EField ( + TQualified (must (lident_of_typestring union_name)), + EBufRead (translate_expr env r, EConstant (UInt32, "0")), + field_name)) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, [_; _; union_name])}, + [_; {expr=MLE_Const (MLC_String field_name)}; new_value; r]) + when string_of_mlpath p = "Steel.C.UnionLiteral.switch_union_field'" -> + EAssign ( + EField ( + TQualified (must (lident_of_typestring union_name)), + EBufRead (translate_expr env r, EConstant (UInt32, "0")), + field_name), + translate_expr env new_value) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, [_; _])}, [r]) + when string_of_mlpath p = "Steel.C.Opt.opt_read_sel" -> + EBufRead (translate_expr env r, EConstant (UInt32, "0")) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, [_; _])}, [r; x]) + when string_of_mlpath p = "Steel.C.Opt.opt_write_sel" -> + EAssign ( + EBufRead (translate_expr env r, EConstant (UInt32, "0")), + translate_expr env x) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, _)}, [_ (* value *) ; _ (* perm *) ; r]) + when string_of_mlpath p = "Steel.ST.C.Types.Scalar.read0" -> + EBufRead (translate_expr env r, EQualified (["C"], "_zero_for_deref")) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, _)}, [_ (* value *); r; x]) + when string_of_mlpath p = "Steel.ST.C.Types.Scalar.write" -> + EAssign ( + EBufRead (translate_expr env r, EQualified (["C"], "_zero_for_deref")), + translate_expr env x) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, _)}, [ + _ (* opened *); + _ (* n *); + _ (* typedef *); + _ (* v *); + r + ]) + when string_of_mlpath p = "Steel.ST.C.Types.Array.array_ref_of_base" -> + // this is not a true read, this is how Karamel models arrays decaying into pointers + EBufRead (translate_expr env r, EQualified (["C"], "_zero_for_deref")) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, _)}, [ + _ (* typedef *); + _ (* s *); + a; + _ (* len *); + i + ]) + when string_of_mlpath p = "Steel.ST.C.Types.Array.array_ref_cell" + || string_of_mlpath p = "Steel.ST.C.Types.Array.array_ref_split" + -> + EBufSub (translate_expr env a, translate_expr env i) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, _)}, [_ (* opened *); r; _ (* r_to *); _ (* sq *) ]) + when string_of_mlpath p = "Steel.C.Array.Base.ref_of_array_from" -> + translate_expr env r + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, [_; _])}, [_ (* opened *); r]) + when string_of_mlpath p = "Steel.C.Array.Base.mk_array_of_ref_from" -> + translate_expr env r + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, [_; _])}, [_ (* opened*); _ (* n *); r; _ (* squash *)]) + when string_of_mlpath p = "Steel.C.Array.Base.intro_varray_from" -> + EBufRead (translate_expr env r, EConstant (UInt32, "0")) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, [_; _])}, [r; _ (* r' *); i]) + when string_of_mlpath p = "Steel.C.Array.index_from" -> + EBufRead (translate_expr env r, translate_expr env i) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, [_; _])}, [r; _ (* r' *); i; x]) + when string_of_mlpath p = "Steel.C.Array.upd_from" -> + EBufWrite (translate_expr env r, translate_expr env i, translate_expr env x) + + | MLE_App ({expr=MLE_TApp ({expr=MLE_Name p}, [_; _])}, [_; a; i]) + when string_of_mlpath p = "Steel.C.Array.Base.split_right_from" -> + EAddrOf (EBufRead (translate_expr env a, translate_expr env i)) + + | _ -> raise NotSupportedByKrmlExtension +end + +let parse_steel_c_fields env (fields: mlty): option (list _) = + let rec go fields = + match fields with + | MLTY_Named ([], p) + when Syntax.string_of_mlpath p = "Steel.C.Fields.c_fields_t_nil" + || Syntax.string_of_mlpath p = "Steel.ST.C.Types.Fields.field_t_nil" + -> Some [] + + | MLTY_Named ([field; t; fields], p) + when Syntax.string_of_mlpath p = "Steel.C.Fields.c_fields_t_cons" + || Syntax.string_of_mlpath p = "Steel.ST.C.Types.Fields.field_t_cons" + -> + opt_bind (string_of_typestring field) (fun field -> + if field = "" then go fields else + opt_bind (go fields) (fun fields -> + Some ((field, t) :: fields))) + + | _ -> + None + in + match go fields with + | None -> + BU.print1 "Failed to parse fields from %s.\n" + (FStar.Extraction.ML.Code.string_of_mlty ([], "") fields); + None + + | Some fields -> + print_endline "Got fields:"; + List.fold_left + (fun () (field, ty) -> + BU.print2 " %s : %s\n" + field + (FStar.Extraction.ML.Code.string_of_mlty ([], "") ty)) + () + fields; + Some ( + List.map + (fun (field, ty) -> + BU.print1 "Translating %s.\n" + (FStar.Extraction.ML.Code.string_of_mlty ([], "") ty); + (field, translate_type_without_decay env ty)) + fields) + +let define_struct + env tag fields += + (* JL: TODO remove/improve these print commands *) + print_endline "Parsing struct definition."; + match lident_of_typestring tag with + | None -> + BU.print1 "Failed to parse struct tag from %s.\n" + (FStar.Extraction.ML.Code.string_of_mlty ([], "") tag); + None + | Some p -> + let fields = must (parse_steel_c_fields env fields) in + Some (DTypeFlat (p, [], 0, + List.map (fun (field, ty) -> (field, (ty, true))) fields)) + +let define_union + env tag fields += + (* JL: TODO remove/improve these print commands *) + print_endline "Parsing union definition."; + match lident_of_typestring tag with + | None -> + BU.print1 "Failed to parse union tag from %s.\n" + (FStar.Extraction.ML.Code.string_of_mlty ([], "") tag); + None + | Some p -> + let fields = must (parse_steel_c_fields env fields) in + Some (DUntaggedUnion (p, [], 0, fields)) + +let my_type_decls () = register_pre_translate_type_decl begin fun env ty -> + match ty with + | {tydecl_defn=Some (MLTD_Abbrev (MLTY_Named ([tag; fields], p)))} + when Syntax.string_of_mlpath p = "Steel.C.StructLiteral.mk_struct_def" + -> + define_struct env tag fields + + | {tydecl_defn=Some (MLTD_Abbrev (MLTY_Named ([tag; fields; _; _], p)))} + when Syntax.string_of_mlpath p = "Steel.ST.C.Types.Struct.define_struct0" + -> + define_struct env tag fields + + | {tydecl_defn=Some (MLTD_Abbrev (MLTY_Named ([tag; fields; _; _], p)))} + when Syntax.string_of_mlpath p = "Steel.ST.C.Types.Union.define_union0" + -> + define_union env tag fields + + | {tydecl_defn=Some (MLTD_Abbrev (MLTY_Named ([tag; fields], p)))} + when Syntax.string_of_mlpath p = "Steel.C.UnionLiteral.mk_union_def" + -> + begin + (* JL: TODO remove/improve these print commands *) + print_endline "Parsing union definition."; + begin match lident_of_typestring tag with + | None -> + BU.print1 "Failed to parse struct tag from %s.\n" + (FStar.Extraction.ML.Code.string_of_mlty ([], "") tag); + None + | Some p -> + let fields = must (parse_steel_c_fields env fields) in + Some (DUntaggedUnion (p, [], 0, fields)) + end + end + | _ -> raise NotSupportedByKrmlExtension +end + +let _ = + my_types_without_decay (); + my_types (); + my_exprs (); + my_type_decls () diff --git a/src/extraction/ExtractSteelC.fsti b/src/extraction/ExtractSteelC.fsti new file mode 100644 index 000000000..8fbbb0547 --- /dev/null +++ b/src/extraction/ExtractSteelC.fsti @@ -0,0 +1,2 @@ +module ExtractSteelC +(* this interface is necessary because ExtractSteelC `friend`s FStar.Extraction.Krml *) diff --git a/src/proofs/steelc/README b/src/proofs/steelc/README new file mode 100644 index 000000000..a55ab4f2a --- /dev/null +++ b/src/proofs/steelc/README @@ -0,0 +1,6 @@ +This directory contains the .fst implementations of SteelC modules +that used to be part of ulib/experimental, but take too long to +verify. + +Those .fst here all have corresponding .fsti files in +ulib/experimental. diff --git a/src/proofs/steelc/Steel.C.Array.Base.fst b/src/proofs/steelc/Steel.C.Array.Base.fst new file mode 100644 index 000000000..db3964f47 --- /dev/null +++ b/src/proofs/steelc/Steel.C.Array.Base.fst @@ -0,0 +1,2102 @@ +module Steel.C.Array.Base + +module S = Steel.C.Model.Struct + +#push-options "--smtencoding.elim_box true --smtencoding.l_arith_repr native --smtencoding.nl_arith_repr native" +let half_perm + (p: Steel.FractionalPermission.perm) +: Pure Steel.FractionalPermission.perm + (requires True) + (ensures (fun y -> + y `Steel.FractionalPermission.sum_perm` y == p /\ + y == Steel.FractionalPermission.half_perm p + )) += + let open Steel.FractionalPermission in + let open FStar.Real in + assert ((p.v /. 2.0R) +. (p.v /. 2.0R) == p.v); + MkPerm (p.v /. 2.0R) +#pop-options + +let array_domain + (t: Type u#0) + (n: Ghost.erased size_t) +: Tot Type0 += (x: size_t { size_v x < size_v n }) + +let array_range + (t: Type u#0) + (n: Ghost.erased size_t) + (x: array_domain t n) +: Tot Type0 += option t + +open FStar.FunctionalExtensionality + +let array_pcm_carrier t n = restricted_t (array_domain t n) (array_range t n) + +let array_pcm_carrier_ext + (t: Type) + (n: size_t) + (x1 x2: array_pcm_carrier t n) + (f: ( + (i: array_domain t n) -> + Lemma + (x1 i == x2 i) + )) +: Lemma + (ensures (x1 == x2)) += Classical.forall_intro f; + assert (x1 `feq` x2) + +let array_elements_pcm + (t: Type u#0) + (n: Ghost.erased size_t) + (x: array_domain t n) +: Tot (Steel.C.Model.PCM.pcm (array_range t n x)) += Steel.C.Opt.opt_pcm #t + +let array_pcm t n = S.prod_pcm (array_elements_pcm t n) + +[@"opaque_to_smt"] +let rec raise_list_array_domain + (t: Type u#0) + (n n': size_t) + (l: list (array_domain t n)) +: Pure (list (array_domain t n')) + (requires (size_v n' >= size_v n)) + (ensures (fun l' -> + (forall (x': array_domain t n') . List.Tot.mem x' l' <==> (size_v x' < size_v n /\ List.Tot.mem x' l)) /\ + List.Tot.length l' == List.Tot.length l + )) += match l with + | [] -> [] + | x :: l_ -> x :: raise_list_array_domain t n n' l_ + +[@"opaque_to_smt"] +let rec included_indices + (t: Type u#0) + (n: size_t) +: Pure (list (array_domain t n)) + (requires True) + (ensures (fun l -> + (forall (x: array_domain t n) . List.Tot.mem x l) /\ + List.Tot.length l == size_v n + )) + (decreases (size_v n)) += if n = mk_size_t (FStar.UInt32.uint_to_t 0) + then [] + else + let n' = size_sub n (mk_size_t (FStar.UInt32.uint_to_t 1)) in + n' :: raise_list_array_domain t n' n (included_indices t n') + +let array_elements_view_type + (t: Type u#0) + (n: size_t) + (k: array_domain t n) +: Tot Type0 += t + +let array_elements_view + (t: Type u#0) + (n: size_t) + (k: array_domain t n) +: Tot (Steel.C.Model.Ref.sel_view (array_elements_pcm t n k) (array_elements_view_type t n k) false) += Steel.C.Opt.opt_view _ + +let intro_array_view_init + (t: Type u#0) + (n: size_t) + (x: restricted_t (Steel.C.Model.Ref.refine (array_domain t n) (S.mem (included_indices t n))) (array_elements_view_type t n)) + (k: nat { k < size_v n }) +: Tot t += x (int_to_size_t k) + +let intro_array_view + (t: Type u#0) + (n: size_t) + (x: restricted_t (Steel.C.Model.Ref.refine (array_domain t n) (S.mem (included_indices t n))) (array_elements_view_type t n)) +: Tot (array_view_type t n) += Seq.init (size_v n) (intro_array_view_init t n x) + +let array_to_view + (t: Type u#0) + (n: size_t) + (x: Steel.C.Model.Ref.refine (array_pcm_carrier t n) (S.struct_view_to_view_prop (array_elements_view t n) (included_indices t n))) +: Tot (array_view_type t n) += intro_array_view t n (S.struct_view_to_view (array_elements_view t n) (included_indices t n) x) + +let elim_array_view_f + (t: Type u#0) + (n: size_t) + (x: array_view_type t n) + (k: Steel.C.Model.Ref.refine (array_domain t n) (S.mem (included_indices t n))) +: Tot (array_elements_view_type t n k) += Seq.index x (size_v k) + +let elim_array_view + (t: Type u#0) + (n: size_t) + (x: array_view_type t n) +: Tot (restricted_t (Steel.C.Model.Ref.refine (array_domain t n) (S.mem (included_indices t n))) (array_elements_view_type t n)) += on_dom (Steel.C.Model.Ref.refine (array_domain t n) (S.mem (included_indices t n))) (elim_array_view_f t n x) + +let array_to_carrier + (t: Type u#0) + (n: size_t) + (x: array_view_type t n) +: Tot (Steel.C.Model.Ref.refine (array_pcm_carrier t n) (S.struct_view_to_view_prop (array_elements_view t n) (included_indices t n))) += S.struct_view_to_carrier (array_elements_view t n) (included_indices t n) (elim_array_view t n x) + +open Steel.C.Model.PCM + +let array_view_to_view_frame + (t: Type u#0) + (n: size_t) + (x: array_view_type t n) + (frame: array_pcm_carrier t n) +: Lemma + (requires (composable (array_pcm t n) (array_to_carrier t n x) frame)) + (ensures + S.struct_view_to_view_prop (array_elements_view t n) (included_indices t n) + (op (array_pcm t n) (array_to_carrier t n x) frame) /\ + array_to_view t n + (op (array_pcm t n) (array_to_carrier t n x) frame) `Seq.equal` x) += S.struct_view_to_view_frame (array_elements_view t n) (included_indices t n) + (elim_array_view t n x) frame + +let array_view' (t: Type u#0) (n: size_t) + : Tot (Steel.C.Model.Ref.sel_view (array_pcm t n) (array_view_type t n) (size_v n = 0)) += + let open Steel.C.Model.Ref in + { + to_view_prop = S.struct_view_to_view_prop (array_elements_view t n) (included_indices t n); + to_view = array_to_view t n; + to_carrier = array_to_carrier t n; + to_carrier_not_one = (S.struct_view (array_elements_view t n) (included_indices t n)).to_carrier_not_one; + to_view_frame = array_view_to_view_frame t n; + } + +let array_view t n = + assert (size_v n > 0); + array_view' t n + +noeq +type array_from0 base t = { + base_len: Ghost.erased size_t; + base_ref: Steel.C.Reference.ref base (array_view_type t base_len) (array_pcm t base_len); + from: size_t; + perm_ref: Steel.Reference.ghost_ref unit; +} + +[@@erasable] +noeq +type array_to0 = { + to: size_t; + perm_val: Steel.FractionalPermission.perm; +} + +let array0_spec + (#base: _) + (#t: _) + (from: array_from0 base t) + (to: array_to0) +: Tot prop += + size_v from.base_len >= 0 /\ + size_v from.from <= size_v to.to /\ + size_v to.to <= size_v from.base_len + +let array_or_null_from base t = option (array_from0 base t) +let array_or_null_to base t = Ghost.erased (option array_to0) +let array_or_null_spec (from, to) = + None? from == None? to /\ + ((Some? from \/ Some? to) ==> array0_spec (Some?.v from) (Some?.v to)) + +let len (from, to) = + match from with + | Some from -> + let Some to = Ghost.reveal to in to.to `size_sub` from.from + | _ -> zero_size + +let null_from _ _ = None +let null_to _ _ = None +let null_to_unique _ = () + +let g_is_null a = None? (fst a) + +let rec array_is_unit_aux + (t: Type0) (n: size_t) (a: array_pcm_carrier t n) + (i: size_t) + (f: + (j: size_t) -> + Lemma + (requires (size_v j < size_v n - size_v i)) + (ensures (size_v j < size_v n - size_v i /\ a j == one (array_elements_pcm t n j))) + ) +: Pure bool + (requires True) + (ensures (fun y -> y == true <==> (forall j . size_v j < size_v n ==> a j == one (array_elements_pcm t n j)))) + (decreases (size_v i)) += Classical.forall_intro (Classical.move_requires f); + if size_le i zero_size + then true + else + let i' = size_sub i one_size in + if not (size_le i n) + then array_is_unit_aux t n a i' (fun _ -> ()) + else if None? (a (size_sub n i)) + then array_is_unit_aux t n a i' (fun j -> if j = size_sub n i then () else f j) + else false + +let array_is_unit_lemma + (t: Type0) (n: size_t) (a: array_pcm_carrier t n) +: Lemma + (requires (forall (j: array_domain t n) . a j == one (array_elements_pcm t n j))) + (ensures (a == one (array_pcm t n))) += S.ext a (one (array_pcm t n)) (fun _ -> ()) + +let array_is_unit t n a = + Classical.move_requires (array_is_unit_lemma t n) a; + array_is_unit_aux t n a n (fun _ -> ()) + +let array_large_to_small_f + (t: Type0) + (base_len: Ghost.erased size_t) + (from: size_t) + (to: Ghost.erased size_t) + (sq: squash ( + size_v from <= size_v to /\ + size_v to <= size_v base_len + )) + (x: array_pcm_carrier t base_len) +: Tot (array_pcm_carrier t (to `size_sub` from)) += on_dom (array_domain t (to `size_sub` from)) (fun k -> x (from `size_add` k)) + +let array_large_to_small_f_eq + (t: Type0) + (base_len: Ghost.erased size_t) + (from: size_t) + (to: Ghost.erased size_t) + (sq: squash ( + size_v from <= size_v to /\ + size_v to <= size_v base_len + )) + (x: array_pcm_carrier t base_len) + (k: array_domain t (to `size_sub` from)) +: Lemma + (array_large_to_small_f t base_len from to sq x k == x (from `size_add` k)) += () + +let array_large_to_small_f_eq' + (t: Type0) + (base_len: Ghost.erased size_t) + (from: size_t) + (to: Ghost.erased size_t) + (sq: squash ( + size_v from <= size_v to /\ + size_v to <= size_v base_len + )) + (x: array_pcm_carrier t base_len) + (k' : array_domain t base_len) +: Lemma + (requires ( + size_v from <= size_v k' /\ + size_v k' < size_v to + )) + (ensures ( + array_large_to_small_f t base_len from to sq x (k' `size_sub` from) == x k' + )) += () + +let array_large_to_small + (t: Type0) + (base_len: Ghost.erased size_t) + (from: size_t) + (to: Ghost.erased size_t) + (sq: squash ( + size_v from <= size_v to /\ + size_v to <= size_v base_len + )) +: Tot (Steel.C.Model.Connection.morphism #(array_pcm_carrier t base_len) #(array_pcm_carrier t (to `size_sub` from)) (array_pcm t base_len) (array_pcm t (to `size_sub` from))) += Steel.C.Model.Connection.mkmorphism + (array_large_to_small_f t base_len from to sq) + (assert (array_large_to_small_f t base_len from to sq (one (array_pcm t base_len)) `feq` one (array_pcm t (to `size_sub` from)))) + (fun x1 x2 -> + assert (array_large_to_small_f t base_len from to sq (op (array_pcm t base_len) x1 x2) `feq` op (array_pcm t (to `size_sub` from)) (array_large_to_small_f t base_len from to sq x1) (array_large_to_small_f t base_len from to sq x2)) + ) + +let array_small_to_large_f + (t: Type0) + (base_len: Ghost.erased size_t) + (from: size_t) + (to: size_t) // Tot, argh + (sq: squash ( + size_v from <= size_v to /\ + size_v to <= size_v base_len + )) + (x: array_pcm_carrier t (to `size_sub` from)) +: Tot (array_pcm_carrier t base_len) += on_dom (array_domain t base_len) (fun k -> if size_le from k && not (size_le to k) then x (k `size_sub` from) + else one (Steel.C.Opt.opt_pcm #t)) + +let array_small_to_large + (t: Type0) + (base_len: Ghost.erased size_t) + (from: size_t) + (to: size_t) + (sq: squash ( + size_v from <= size_v to /\ + size_v to <= size_v base_len + )) +: Tot (Steel.C.Model.Connection.morphism (array_pcm t (to `size_sub` from)) (array_pcm t base_len)) += Steel.C.Model.Connection.mkmorphism + (array_small_to_large_f t base_len from to sq) + (assert (array_small_to_large_f t base_len from to sq (one (array_pcm t (to `size_sub` from))) `feq` one (array_pcm t (base_len)))) + (fun x1 x2 -> + assert (array_small_to_large_f t base_len from to sq (op (array_pcm t (to `size_sub` from)) x1 x2) `feq` op (array_pcm t (base_len)) (array_small_to_large_f t base_len from to sq x1) (array_small_to_large_f t base_len from to sq x2)) + ) + +let array_small_to_large_to_small + (t: Type0) + (base_len: Ghost.erased size_t) + (from: size_t) + (to: size_t) + (sq: squash ( + size_v from <= size_v to /\ + size_v to <= size_v base_len + )) +: Lemma + (array_large_to_small_f t base_len from to sq `Steel.C.Model.Connection.is_inverse_of` array_small_to_large_f t base_len from to sq) += assert (forall x . array_large_to_small_f t base_len from to sq (array_small_to_large_f t base_len from to sq x) `feq` x) + +#push-options "--z3rlimit 64 --fuel 1 --ifuel 2 --query_stats --z3cliopt smt.arith.nl=false" +#restart-solver + +let size_sub' (x y: size_t) (sq: squash (size_v x >= size_v y)) : Pure size_t + (requires True) + (ensures (fun z -> size_v z == size_v x - size_v y)) += size_sub x y + +#restart-solver + +let array_conn_fpu_compatible + (t: Type0) + (base_len: Ghost.erased size_t) + (from: size_t) + (to: size_t) + (sq: squash ( + size_v from <= size_v to /\ + size_v to <= size_v base_len + )) + (x: Ghost.erased (array_pcm_carrier t (to `size_sub` from)) { ~ (Ghost.reveal x == one (array_pcm t (to `size_sub` from))) }) + (v: frame_preserving_upd_dom (array_pcm t base_len) (array_small_to_large_f t base_len from to sq x)) +: Lemma + ( + let z = size_sub to from in + let v_small : array_pcm_carrier t z = array_large_to_small_f t base_len from to sq v in + compatible (array_pcm t z) x v_small + ) += + let z = size_sub to from in + let v_small : array_pcm_carrier t z = array_large_to_small_f t base_len from to sq v in + let frame : Ghost.erased (array_pcm_carrier t base_len) = Ghost.hide (compatible_elim (array_pcm t base_len) (array_small_to_large_f t base_len from to sq x) v) in + let frame_small : Ghost.erased (array_pcm_carrier t (z)) = Ghost.hide (array_large_to_small_f t base_len from to sq (Ghost.reveal frame)) in + S.prod_pcm_composable_intro + (array_elements_pcm t z) + x + frame_small + (fun h -> + assert (composable (Steel.C.Opt.opt_pcm #t) (array_small_to_large_f t base_len from to sq x (from `size_add` h)) (Ghost.reveal frame (from `size_add` h)) + ) + ); + assert (composable (array_pcm t (z)) x frame_small); + array_pcm_carrier_ext t z (op (array_pcm t (z)) x frame_small) v_small (fun i -> + assert (op (Steel.C.Opt.opt_pcm #t) (array_small_to_large_f t base_len from to sq x (from `size_add` i)) (Ghost.reveal frame (from `size_add` i)) == v (from `size_add` i)) + ); + compatible_intro (array_pcm t (z)) x v_small frame_small + +let array_conn_fpu_refine + (t: Type0) + (base_len: Ghost.erased size_t) + (from: size_t) + (to: size_t) + (sq: squash ( + size_v from <= size_v to /\ + size_v to <= size_v base_len + )) + (x: Ghost.erased (array_pcm_carrier t (to `size_sub` from)) { ~ (Ghost.reveal x == one (array_pcm t (to `size_sub` from))) }) + (v: frame_preserving_upd_dom (array_pcm t base_len) (array_small_to_large_f t base_len from to sq x)) +: Lemma + ( + let z = size_sub to from in + let v_small : array_pcm_carrier t z = array_large_to_small_f t base_len from to sq v in + p_refine (array_pcm t (z)) v_small + ) += + let z = size_sub to from in + let v_small : array_pcm_carrier t z = array_large_to_small_f t base_len from to sq v in + if FStar.StrongExcludedMiddle.strong_excluded_middle (exists (x: array_domain t z) . True) + then () + else assert (Ghost.reveal x `feq` one (array_pcm t z)) + +let overwrite_array_slice + (t: Type0) + (base_len: Ghost.erased size_t) + (from: size_t) + (to: size_t) + (sq: squash ( + size_v from <= size_v to /\ + size_v to <= size_v base_len + )) + (v: array_pcm_carrier t base_len) + (v_small' : array_pcm_carrier t (to `size_sub` from)) +: Tot (array_pcm_carrier t base_len) += + on_dom (array_domain t base_len) (fun (k: array_domain t base_len) -> + if from `size_le` k && not (to `size_le` k) + then begin + let sq2 : squash (size_v k >= size_v from) = assert (size_v k >= size_v from) in + v_small' (size_sub' k from sq2) <: option t + end + else v k + ) + +let overwrite_array_slice_index + (t: Type0) + (base_len: Ghost.erased size_t) + (from: size_t) + (to: size_t) + (sq: squash ( + size_v from <= size_v to /\ + size_v to <= size_v base_len + )) + (v: array_pcm_carrier t base_len) + (v_small' : array_pcm_carrier t (to `size_sub` from)) + (k: array_domain t base_len) +: Lemma ( + overwrite_array_slice t base_len from to sq v v_small' k == ( + if size_v from <= size_v k && size_v k < size_v to + then v_small' (k `size_sub` from) + else v k + )) += () + +let overwrite_array_slice_index_in + (t: Type0) + (base_len: Ghost.erased size_t) + (from: size_t) + (to: size_t) + (sq: squash ( + size_v from <= size_v to /\ + size_v to <= size_v base_len + )) + (v: array_pcm_carrier t base_len) + (v_small' : array_pcm_carrier t (to `size_sub` from)) + (k: array_domain t base_len) +: Lemma + (requires ( + size_v from <= size_v k /\ size_v k < size_v to + )) + (ensures ( + overwrite_array_slice t base_len from to sq v v_small' k == v_small' (k `size_sub` from) + )) += () + +let overwrite_array_slice_index_out + (t: Type0) + (base_len: Ghost.erased size_t) + (from: size_t) + (to: size_t) + (sq: squash ( + size_v from <= size_v to /\ + size_v to <= size_v base_len + )) + (v: array_pcm_carrier t base_len) + (v_small' : array_pcm_carrier t (to `size_sub` from)) + (k: array_domain t base_len) +: Lemma + (requires ( + ~ (size_v from <= size_v k /\ size_v k < size_v to) + )) + (ensures ( + overwrite_array_slice t base_len from to sq v v_small' k == v k + )) += () + +let overwrite_array_slice_id + (t: Type0) + (base_len: Ghost.erased size_t) + (v: array_pcm_carrier t base_len) + (v_small' : array_pcm_carrier t base_len) +: Lemma + (overwrite_array_slice t base_len zero_size base_len () v v_small' == v_small') += array_pcm_carrier_ext t base_len + (overwrite_array_slice t base_len zero_size base_len () v v_small') + v_small' + (fun i -> ()) + +let array_conn_fpu_f + (t: Type0) + (base_len: Ghost.erased size_t) + (from: size_t) + (to: size_t) + (sq: squash ( + size_v from <= size_v to /\ + size_v to <= size_v base_len + )) + (x: Ghost.erased (array_pcm_carrier t (to `size_sub` from)) { ~ (Ghost.reveal x == one (array_pcm t (to `size_sub` from))) }) + (y: Ghost.erased (array_pcm_carrier t (to `size_sub` from))) + (f: frame_preserving_upd (array_pcm t (to `size_sub` from)) x y) + (v: frame_preserving_upd_dom (array_pcm t base_len) (array_small_to_large_f t base_len from to sq x)) +: Tot (array_pcm_carrier t base_len) += let sq0 : squash (size_v to >= size_v from) = () in + let z : size_t = size_sub' to from sq0 in + let v_small : array_pcm_carrier t z = array_large_to_small_f t base_len from to sq v in + array_conn_fpu_compatible t base_len from to sq x v; + array_conn_fpu_refine t base_len from to sq x v; + let v_small' : array_pcm_carrier t z = f v_small in + overwrite_array_slice t base_len from to sq v v_small' + +#pop-options + +let array_conn + (t: Type0) + (base_len: Ghost.erased size_t) + (from: size_t) + (to: size_t) + (sq: squash ( + size_v from <= size_v to /\ + size_v to <= size_v base_len + )) +: Steel.C.Model.Connection.connection + (array_pcm t base_len) + (array_pcm t (to `size_sub` from)) += + Steel.C.Model.Connection.mkconnection1 + (array_small_to_large t base_len from to sq) + (array_large_to_small t base_len from to sq) + (array_small_to_large_to_small t base_len from to sq) + (array_conn_fpu_f t base_len from to sq) + (fun x y f v -> assume False) + +#push-options "--z3rlimit 64 --fuel 1 --ifuel 2 --query_stats --z3cliopt smt.arith.nl=false" +#restart-solver + +let array_conn_fpu_eq + (t: Type0) + (base_len: Ghost.erased size_t) + (from: size_t) + (to: size_t) + (sq: squash ( + size_v from <= size_v to /\ + size_v to <= size_v base_len + )) + (x: Ghost.erased (array_pcm_carrier t (to `size_sub` from)) { ~ (Ghost.reveal x == one (array_pcm t (to `size_sub` from))) }) + (y: Ghost.erased (array_pcm_carrier t (to `size_sub` from))) + (f: Steel.C.Model.Connection.restricted_frame_preserving_upd (array_pcm t (to `size_sub` from)) x y) + (v: frame_preserving_upd_dom (array_pcm t base_len) (array_small_to_large_f t base_len from to sq x)) +: Lemma + (let open Steel.C.Model.Connection in + ((array_conn t base_len from to sq).conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })).fpu_f v == array_conn_fpu_f t base_len from to sq x y f v) += () + +#restart-solver + +let connection_eq_gen + #a (#p: pcm a) #b1 (#q1: pcm b1) (c1: p `Steel.C.Model.Connection.connection` q1) + #b2 (#q2: pcm b2) (c2: p `Steel.C.Model.Connection.connection` q2) + (sq: squash ( + b1 == b2 /\ + q1 == q2 /\ + c1.conn_small_to_large.morph `feq` c2.conn_small_to_large.morph /\ + c1.conn_large_to_small.morph `feq` c2.conn_large_to_small.morph + )) + (phi: + (x1: Ghost.erased b1 { ~ (Ghost.reveal x1 == one q1) }) -> + (y1: Ghost.erased b1) -> + (f1: Steel.C.Model.Connection.restricted_frame_preserving_upd q1 x1 y1) -> + (v1: frame_preserving_upd_dom p (c1.conn_small_to_large.morph x1)) -> + (x2: Ghost.erased b2 { ~ (Ghost.reveal x2 == one q2) }) -> + (y2: Ghost.erased b2) -> + (f2: Steel.C.Model.Connection.restricted_frame_preserving_upd q2 x2 y2) -> + (v2: frame_preserving_upd_dom p (c2.conn_small_to_large.morph x2)) -> + (sq': squash ( + x1 == x2 /\ + y1 == y2 /\ + f1 == f2 /\ + v1 == v2 + )) -> + Tot + (squash ((c1.conn_lift_frame_preserving_upd Steel.C.Model.Connection.({ fpu_lift_dom_x = x1; fpu_lift_dom_y = y1; fpu_lift_dom_f = f1 })).fpu_f v1 == (c2.conn_lift_frame_preserving_upd Steel.C.Model.Connection.({ fpu_lift_dom_x = x2; fpu_lift_dom_y = y2; fpu_lift_dom_f = f2 })).fpu_f v2)) + ) +: Lemma + (c1 == c2) += Steel.C.Model.Connection.connection_eq_gen c1 c2 () (fun x y f v -> phi x y f v x y f v ()) + +#restart-solver +let array_conn_id + (t: Type0) + (base_len: Ghost.erased size_t) +: Lemma + (array_conn t base_len (mk_size_t (FStar.UInt32.uint_to_t 0)) base_len () == Steel.C.Model.Connection.connection_id (array_pcm t base_len)) += let z = mk_size_t (FStar.UInt32.uint_to_t 0) in + assert (forall x . array_small_to_large_f t base_len z base_len () x `feq` x); + assert (forall x . array_small_to_large_f t base_len z base_len () x == x); + assert (forall x . array_large_to_small_f t base_len z base_len () x `feq` x); + assert (forall x . array_large_to_small_f t base_len z base_len () x == x); + let c = array_conn t base_len z base_len () in + connection_eq_gen + c + (Steel.C.Model.Connection.connection_id (array_pcm t base_len)) + () + (fun x1 y1 f1 v1 x2 y2 f2 v2 sq12 -> + let v_small : array_pcm_carrier t base_len = array_large_to_small_f t base_len z base_len () v1 in + assert (v_small == v1); + array_conn_fpu_compatible t base_len z base_len () x1 v1; + array_conn_fpu_refine t base_len z base_len () x1 v1; + let v_small' : array_pcm_carrier t base_len = f1 v1 in + overwrite_array_slice_id t base_len v1 v_small'; + let s' : array_pcm_carrier t base_len = overwrite_array_slice t base_len z base_len () v1 v_small' in + assert (array_conn_fpu_f t base_len z base_len () x1 y1 f1 v1 == s'); + assert (s' == f1 v1); + assert ((c.Steel.C.Model.Connection.conn_lift_frame_preserving_upd Steel.C.Model.Connection.({ fpu_lift_dom_x = x1; fpu_lift_dom_y = y1; fpu_lift_dom_f = f1; })).Steel.C.Model.Connection.fpu_f v1 == array_conn_fpu_f t base_len z base_len () x1 y1 f1 v1); + Steel.C.Model.Connection.connection_id_fpu (array_pcm t base_len) x2 y2 f2 v2; + assert (((Steel.C.Model.Connection.connection_id (array_pcm t base_len)).conn_lift_frame_preserving_upd Steel.C.Model.Connection.({ fpu_lift_dom_x = x2; fpu_lift_dom_y = y2; fpu_lift_dom_f = f2; })).Steel.C.Model.Connection.fpu_f v2 == f2 v2); + () + ) + +let ifthenelse_prf + (p: prop) + (cond: bool) + (iftrue: squash (cond == true) -> Lemma p) + (iffalse: squash (cond == false) -> Lemma p) +: Lemma p += if cond + then iftrue () + else iffalse () + +#restart-solver +let array_conn_compose_morphisms + (t: Type0) + (base_len: Ghost.erased size_t) + (from1: size_t) + (to1: size_t) + (from2: size_t) + (to2: size_t) + (h: squash ( + size_v from1 <= size_v to1 /\ + size_v to1 <= size_v base_len /\ + size_v from2 <= size_v to2 /\ + size_v from1 + size_v to2 <= size_v to1 + )) +: Tot (squash ( + let z = to1 `size_sub` from1 in + let c1 = array_conn t base_len from1 to1 () in + let c2 = array_conn t z from2 to2 () in + let cc = c1 `Steel.C.Model.Connection.connection_compose` c2 in + let c = array_conn t base_len (from1 `size_add` from2) (from1 `size_add` to2) () in + cc.conn_small_to_large.morph `feq` c.conn_small_to_large.morph /\ + cc.conn_large_to_small.morph `feq` c.conn_large_to_small.morph + )) += + let z = to1 `size_sub` from1 in + let sz = size_sub (size_add from1 to2) (size_add from1 from2) in + let _ : squash (sz == size_sub to2 from2) = () in + assert (forall x . array_small_to_large_f t base_len from1 to1 () (array_small_to_large_f t z from2 to2 () x) `feq` array_small_to_large_f t base_len (from1 `size_add` from2) (from1 `size_add` to2) () x); + assert (forall x . array_large_to_small_f t z from2 to2 () (array_large_to_small_f t base_len from1 to1 () x) `feq` array_large_to_small_f t base_len (from1 `size_add` from2) (from1 `size_add` to2) () x) + +#push-options "--print_implicits --z3rlimit 256" + +let size_sub_size_add_l + (from1: size_t) + (to1: size_t) + (from2: size_t) + (to2: size_t) + (sq: squash ( + size_v from1 <= size_v to1 /\ + size_v from2 <= size_v to2 /\ + size_v from1 + size_v to2 <= size_v to1 + )) +: Lemma + ((from1 `size_add` to2) `size_sub` (from1 `size_add` from2) == to2 `size_sub` from2) += () + +let size_sub_size_sub + (from1: size_t) + (to1: size_t) + (from2: size_t) + (to2: size_t) + (i: size_t) + (sq: squash ( + size_v from1 <= size_v to1 /\ + size_v from1 + size_v to2 <= size_v to1 /\ + size_v from1 + size_v from2 <= size_v i /\ + size_v i <= size_v from1 + size_v to2 + )) +: Lemma + ((i `size_sub` from1) `size_sub` from2 == i `size_sub` (from1 `size_add` from2)) += () + +let array_large_to_small_f_compose + (t: Type0) + (base_len: Ghost.erased size_t) + (from1: size_t) + (to1: size_t) + (from2: size_t) + (to2: size_t) + (sq: squash ( + size_v from1 <= size_v to1 /\ + size_v to1 <= size_v base_len /\ + size_v from2 <= size_v to2 /\ + size_v from1 + size_v to2 <= size_v to1 + )) + (a: array_pcm_carrier t base_len) +: Lemma + (array_large_to_small_f t (to1 `size_sub` from1) from2 to2 () (array_large_to_small_f t base_len from1 to1 () a) == + array_large_to_small_f t base_len (from1 `size_add` from2) (from1 `size_add` to2) () a) += assert ( + (array_large_to_small_f t (to1 `size_sub` from1) from2 to2 () (array_large_to_small_f t base_len from1 to1 () a) `feq` + array_large_to_small_f t base_len (from1 `size_add` from2) (from1 `size_add` to2) () a) + ) + +#restart-solver +let array_conn_compose_fpu + (t: Type0) + (base_len: Ghost.erased size_t) + (from1: size_t) + (to1: size_t) + (from2: size_t) + (to2: size_t) + (sq: squash ( + size_v from1 <= size_v to1 /\ + size_v to1 <= size_v base_len /\ + size_v from2 <= size_v to2 /\ + size_v from1 + size_v to2 <= size_v to1 + )) + (x: Ghost.erased (array_pcm_carrier t (to2 `size_sub` from2)) {~ (Ghost.reveal x == one (array_pcm t (to2 `size_sub` from2)))}) + (y: Ghost.erased (array_pcm_carrier t (to2 `size_sub` from2))) + (f: frame_preserving_upd (array_pcm t (to2 `size_sub` from2)) x y) + (x2: Ghost.erased (array_pcm_carrier t (to1 `size_sub` from1))) + (sqx2: squash ( + Ghost.reveal x2 == array_small_to_large_f t (to1 `size_sub` from1) from2 to2 () x /\ + (~ (Ghost.reveal x2 == one (array_pcm t (to1 `size_sub` from1)))) + )) + (y2: Ghost.erased (array_pcm_carrier t (to1 `size_sub` from1))) + (sqy2: squash ( + Ghost.reveal y2 == array_small_to_large_f t (to1 `size_sub` from1) from2 to2 () y + )) + (f2: frame_preserving_upd (array_pcm t (to1 `size_sub` from1)) x2 y2) + (sqf2: ( + (v: frame_preserving_upd_dom (array_pcm t (to1 `size_sub` from1)) x2) -> + Lemma + (f2 v == array_conn_fpu_f t (to1 `size_sub` from1) from2 to2 () x y f v) + )) + (x0: Ghost.erased (array_pcm_carrier t base_len)) + (sqx0: squash ( + Ghost.reveal x0 == array_small_to_large_f t base_len (from1 `size_add` from2) (from1 `size_add` to2) () x /\ + Ghost.reveal x0 == array_small_to_large_f t base_len from1 to1 () x2 /\ + (~ (Ghost.reveal x0 == one (array_pcm t base_len))) + )) + (v: frame_preserving_upd_dom (array_pcm t base_len) x0) +: Lemma + (ensures ( + array_conn_fpu_f t base_len from1 to1 () x2 y2 f2 v == array_conn_fpu_f t base_len (from1 `size_add` from2) (from1 `size_add` to2) () x y f v + )) += let al : array_pcm_carrier t base_len = array_conn_fpu_f t base_len from1 to1 () x2 y2 f2 v in + array_conn_fpu_compatible t base_len from1 to1 () x2 v; + array_conn_fpu_refine t base_len from1 to1 () x2 v; + let sz1 = to1 `size_sub` from1 in + let v_l_out_small : array_pcm_carrier t sz1 = array_large_to_small_f t base_len from1 to1 () v in + sqf2 v_l_out_small; + array_conn_fpu_compatible t sz1 from2 to2 () x v_l_out_small; + array_conn_fpu_refine t sz1 from2 to2 () x v_l_out_small; + let sz2 = to2 `size_sub` from2 in + let v_l_in_small : array_pcm_carrier t sz2 = array_large_to_small_f t sz1 from2 to2 () v_l_out_small in + let v_l_in_small' : array_pcm_carrier t sz2 = f v_l_in_small in + let v_l_in' : array_pcm_carrier t sz1 = overwrite_array_slice t sz1 from2 to2 () v_l_out_small v_l_in_small' in + let v_l' : array_pcm_carrier t base_len = overwrite_array_slice t base_len from1 to1 () v v_l_in' in + assert (v_l' == al); + let from = from1 `size_add` from2 in + let to = from1 `size_add` to2 in + let _ : squash (sz2 == to `size_sub` from) = size_sub_size_add_l from1 to1 from2 to2 () in + let ar : array_pcm_carrier t base_len = array_conn_fpu_f t base_len from to () x y f v in + array_conn_fpu_compatible t base_len from to () x v; + array_conn_fpu_refine t base_len from to () x v; + let v_r_small : array_pcm_carrier t sz2 = array_large_to_small_f t base_len from to () v in + let _ : squash (v_r_small == v_l_in_small) = array_large_to_small_f_compose t base_len from1 to1 from2 to2 () v in + let v_r_small' : array_pcm_carrier t sz2 = f v_r_small in + assert (v_r_small' == v_l_in_small'); + let v_r' : array_pcm_carrier t base_len = overwrite_array_slice t base_len from to () v v_r_small' in + assert (v_r' == ar); + array_pcm_carrier_ext t base_len v_l' v_r' (fun i -> + overwrite_array_slice_index t base_len from1 to1 () v v_l_in' i; + overwrite_array_slice_index t base_len from to () v v_r_small' i; + if size_v from1 <= size_v i && size_v i < size_v to1 + then begin + let i' : array_domain t sz1 = i `size_sub` from1 in + let b = (size_v from2 <= size_v i' && size_v i' < size_v to2) in + assert ((size_v (from1 `size_add` from2) <= size_v i && size_v i < size_v (from1 `size_add` to2)) == b); + overwrite_array_slice_index t sz1 from2 to2 () v_l_out_small v_l_in_small' i'; + if size_v from2 <= size_v i' && size_v i' < size_v to2 + then begin + size_sub_size_sub from1 to1 from2 to2 i () + end else begin + assert (f2 v_l_out_small i' == v_l_out_small i'); + array_large_to_small_f_eq' t base_len from1 to1 () v i + end + end else begin + assert ((size_v (from1 `size_add` from2) <= size_v i && size_v i < size_v (from1 `size_add` to2)) == false) + end + ) + +#restart-solver +let array_conn_compose + (t: Type0) + (base_len: Ghost.erased size_t) + (from1: size_t) + (to1: size_t) + (from2: size_t) + (to2: size_t) +: Lemma + (requires ( + size_v from1 <= size_v to1 /\ + size_v to1 <= size_v base_len /\ + size_v from2 <= size_v to2 /\ + size_v from1 + size_v to2 <= size_v to1 + )) + (ensures ( + array_conn t base_len from1 to1 () `Steel.C.Model.Connection.connection_compose` array_conn t (to1 `size_sub` from1) from2 to2 () == + array_conn t base_len (from1 `size_add` from2) (from1 `size_add` to2) () + )) += + let z = to1 `size_sub` from1 in + let sz = size_sub (size_add from1 to2) (size_add from1 from2) in + let _ : squash (sz == size_sub to2 from2) = () in + let c1 = array_conn t base_len from1 to1 () in + let c2 = array_conn t z from2 to2 () in + let cc = c1 `Steel.C.Model.Connection.connection_compose` c2 in + let c = array_conn t base_len (from1 `size_add` from2) (from1 `size_add` to2) () in + let sq : squash ( + cc.conn_small_to_large.morph `feq` c.conn_small_to_large.morph /\ + cc.conn_large_to_small.morph `feq` c.conn_large_to_small.morph + ) = + array_conn_compose_morphisms t base_len from1 to1 from2 to2 () + in + Steel.C.Model.Connection.connection_eq_gen cc c sq (fun x y f v -> + let open Steel.C.Model.Connection in + let x' : Ghost.erased (array_pcm_carrier t z) = c2.conn_small_to_large.morph x in + let y' : Ghost.erased (array_pcm_carrier t z) = c2.conn_small_to_large.morph y in + let phi = mk_restricted_frame_preserving_upd (c2.conn_lift_frame_preserving_upd ({ fpu_lift_dom_x = x; fpu_lift_dom_y = y; fpu_lift_dom_f = f; })) in + connection_compose_fpu + c1 + c2 + x y f + phi; + array_conn_fpu_eq t base_len from1 to1 () x' y' phi v; + array_conn_fpu_eq t base_len (from1 `size_add` from2) (from1 `size_add` to2) () x y f v; + array_conn_compose_fpu + t base_len from1 to1 from2 to2 () + x y f + x' () y' () + phi + (fun v' -> + array_conn_fpu_eq t z from2 to2 () x y f v' + ) + (cc.conn_small_to_large.morph x) + () + v + ) + + +#pop-options + +#restart-solver + +let to_view_array_conn + (t: Type0) + (base_len: Ghost.erased size_t) + (from: size_t) + (to: size_t) + (sq: squash ( + size_v from <= size_v to /\ + size_v to <= size_v base_len + )) + (x: array_pcm_carrier t base_len) +: Lemma + (requires ( + S.struct_view_to_view_prop (array_elements_view t base_len) (included_indices t base_len) x + )) + (ensures ( + let x' = array_large_to_small_f t base_len from to sq x in + S.struct_view_to_view_prop (array_elements_view t (to `size_sub` from)) (included_indices t (to `size_sub` from)) x' /\ + array_to_view t (to `size_sub` from) x' `Seq.equal` Seq.slice (array_to_view t base_len x) (size_v from) (size_v to) + )) += () + +#pop-options + +let array__base_len + (#base #t: _) + (a: array base t) +: GTot size_t += (Some?.v (fst a)).base_len + +let array__base_ref + (#base #t: _) + (a: array base t) +: Tot (Steel.C.Reference.ref base (array_view_type t (array__base_len a)) (array_pcm t (array__base_len a))) += (Some?.v (fst a)).base_ref + +let array__from + (#base #t: _) + (a: array base t) +: Tot size_t += (Some?.v (fst a)).from + +let array__to + (#base #t: _) + (a: array base t) +: GTot size_t += (Some?.v (snd a)).to + +let array__perm_ref + (#base #t: _) + (a: array base t) +: Tot (Steel.Reference.ghost_ref unit) += (Some?.v (fst a)).perm_ref + +let array__perm_val + (#base #t: _) + (a: array base t) +: Tot Steel.FractionalPermission.perm += (Some?.v (snd a)).perm_val + +let array_as_ref_conn + (#base: Type) + (#t: Type) + (a: array base t) +: GTot (Steel.C.Model.Connection.connection (array_pcm t (array__base_len a)) (array_pcm t (len a))) += array_conn t (array__base_len a) (array__from a) (array__to a) () + +let array_as_ref + (#base: Type) + (#t: Type) + (a: array base t) +: GTot (Steel.C.Reference.ref base (array_view_type t (len a)) (array_pcm t (len a))) += Steel.C.Model.Ref.ref_focus (array__base_ref a) (array_as_ref_conn a) + +[@@__steel_reduce__] +let varray0 + (#base: Type) + (#t: Type) + (x: array base t) +: Tot vprop += Steel.C.Model.Ref.pts_to_view + #base + #(array_pcm_carrier t (len x)) + #(array_pcm t (len x)) + (array_as_ref #base #t x) + #(array_view_type t (len x)) + #(size_v (len x) = 0) + (array_view' t (len x)) + +[@@__steel_reduce__] +let varray9 + (#base: Type) + (#t: Type) + (x: array base t) +: Tot vprop += (varray0 x `star` Steel.Reference.ghost_vptrp (array__perm_ref x) (array__perm_val x)) `vrewrite` fst + +let varray_hp #base #t x = hp_of (varray9 #base #t x) + +#push-options "--debug Steel.C.Array --debug_level Extreme" + +let varray_sel #base #t x = sel_of (varray9 #base #t x) + +#pop-options + +let intro_varray1 + (#inames: _) + (#base: Type) + (#t: Type) + (x: array base t) +: SteelGhost unit inames + (varray0 x `star` Steel.Reference.ghost_vptrp (array__perm_ref x) (array__perm_val x)) + (fun _ -> varray x) + (fun _ -> True) + (fun h _ h' -> h' (varray x) == h (varray0 x)) += intro_vrewrite + (varray0 x `star` Steel.Reference.ghost_vptrp (array__perm_ref x) (array__perm_val x)) + fst; + change_slprop_rel + ((varray0 x `star` Steel.Reference.ghost_vptrp (array__perm_ref x) (array__perm_val x)) `vrewrite` fst) + (varray x) + (fun u v -> u == v) + (fun m -> ()) + +let elim_varray1 + (#inames: _) + (#base: Type) + (#t: Type) + (x: array base t) +: SteelGhost unit inames + (varray x) + (fun _ -> varray0 x `star` Steel.Reference.ghost_vptrp (array__perm_ref x) (array__perm_val x)) + (fun _ -> True) + (fun h _ h' -> h' (varray0 x) == h (varray x)) += change_slprop_rel + (varray x) + ((varray0 x `star` Steel.Reference.ghost_vptrp (array__perm_ref x) (array__perm_val x)) `vrewrite` fst) + (fun u v -> u == v) + (fun m -> ()); + elim_vrewrite + (varray0 x `star` Steel.Reference.ghost_vptrp (array__perm_ref x) (array__perm_val x)) + fst + +let g_mk_array_from' + (#base: Type u#0) (#t: Type u#0) (#n: size_t) (r: Steel.C.Reference.ref base (array_view_type t n) (array_pcm t n)) + (a: array_or_null_from base t) +: Tot prop += + Some? a /\ + size_v n > 0 /\ + begin let a = Some?.v a in + Ghost.reveal a.base_len == n /\ + a.base_ref == r /\ + a.from == mk_size_t 0ul + end + +let g_mk_array #base #t #n r a = + g_mk_array_from' r (fst a) /\ + (array__to a) == n /\ + (array__perm_val a) == Steel.FractionalPermission.full_perm + +let g_mk_array_weak r a = () + +let g_mk_array_from r a = g_mk_array_from' r a + +let g_mk_array_to #base #t #n r a += + Some ({ + to = n; + perm_val = Steel.FractionalPermission.full_perm + }) + +#push-options "--z3rlimit 32" + +val intro_varray0 (#base: Type u#0) (#t: Type u#0) (#opened: _) (#n: size_t) (r: Steel.C.Reference.ref base (array_view_type t n) (array_pcm t n)) + (_: squash (size_v n > 0)) +: SteelAtomicBase (array base t) + false opened Unobservable + (Steel.C.Model.Ref.pts_to_view r (array_view t n)) + (fun a -> varray a) + (requires fun _ -> True) + (ensures (fun h a h' -> + g_mk_array r a /\ + snd a == g_mk_array_to r (fst a) /\ + h' (varray a) == h (Steel.C.Model.Ref.pts_to_view r (array_view t n)) + )) + +let intro_varray0 + #base #t #_ #n r sq += + let perm_ref = Steel.Reference.ghost_alloc #unit () in + let from = Some ({ + base_len = n; + base_ref = r; + from = mk_size_t 0ul; + perm_ref = perm_ref; + }) in + let res = (from, g_mk_array_to r from) in + change_equal_slprop + (Steel.Reference.ghost_vptr perm_ref) + (Steel.Reference.ghost_vptrp (array__perm_ref res) (array__perm_val res)); + assert ((array_as_ref res <: Steel.C.Model.Ref.ref base (array_pcm t n)) == Steel.C.Model.Ref.ref_focus r (array_conn t n (mk_size_t 0ul) n ())); + array_conn_id t n; + assert (array_conn t n (mk_size_t 0ul) n () == Steel.C.Model.Connection.connection_id (array_pcm t n)); + assert (array_as_ref res == Steel.C.Model.Ref.ref_focus r (Steel.C.Model.Connection.connection_id (array_pcm t n))); + Steel.C.Model.Ref.ref_focus_id r; + assert (Steel.C.Model.Ref.ref_focus r (Steel.C.Model.Connection.connection_id (array_pcm t n)) == r); + assert (array_as_ref res == r); + change_equal_slprop + (r `Steel.C.Model.Ref.pts_to_view` _) + (varray0 res); + intro_varray1 res; + return res + +let intro_varray_from r _ = + let a = intro_varray0 r () in + let res = fst a in + change_equal_slprop + (varray a) + (varray (res, g_mk_array_to r res)); + return res + +let elim_varray + #_ #base #t #n r res sq += + assert (g_mk_array r res); + assert (array_as_ref res == Steel.C.Model.Ref.ref_focus r (array_conn t n (mk_size_t 0ul) n ())); + array_conn_id t n; + assert (array_conn t n (mk_size_t 0ul) n () == Steel.C.Model.Connection.connection_id (array_pcm t n)); + assert (array_as_ref res == Steel.C.Model.Ref.ref_focus r (Steel.C.Model.Connection.connection_id (array_pcm t n))); + Steel.C.Model.Ref.ref_focus_id r; + assert (Steel.C.Model.Ref.ref_focus r (Steel.C.Model.Connection.connection_id (array_pcm t n)) == r); + assert (array_as_ref res == r); + elim_varray1 res; + change_equal_slprop + (varray0 res) + (r `Steel.C.Model.Ref.pts_to_view` _); + let perm_ref = (array__perm_ref res) in + change_equal_slprop + (Steel.Reference.ghost_vptrp ((array__perm_ref res)) ((array__perm_val res))) + (Steel.Reference.ghost_vptr perm_ref); + Steel.Reference.ghost_free perm_ref + +#pop-options + +let adjacent r1 r2 = + (array__base_len r1) == (array__base_len r2) /\ + (array__base_ref r1) == (array__base_ref r2) /\ + (array__perm_ref r1) == (array__perm_ref r2) /\ + (array__to r1) == (array__from r2) + +val t_merge + (#base: Type) + (#t: Type) + (r1 r2: array base t) +: Pure (array base t) + (requires (adjacent r1 r2)) + (ensures (fun r -> length r == length r1 + length r2)) + +let t_merge r1 r2 = + (fst r1, Ghost.hide (Some ({ + to = (array__to r2); + perm_val = (array__perm_val r1) `Steel.FractionalPermission.sum_perm` (array__perm_val r2); + }))) + +let merge r1 r2 = t_merge r1 r2 + +let merge_assoc r1 r2 r3 = () + +let merge_inj_right a b1 b2 = () + +let merge_inj_left a1 a2 b = () + +let no_self_merge_1 (#base #t: Type) (a b: array base t) : Lemma + (~ (merge_into a b a)) += let aux () : Lemma + (requires (merge_into a b a)) + (ensures False) + = assert ( + let open Steel.FractionalPermission in + let open FStar.Real in + (array__perm_val a).v +. (array__perm_val b).v >. (array__perm_val a).v + ) + in + Classical.move_requires aux () + +let no_self_merge_2 (#base #t: Type) (a b: array base t) : Lemma + (~ (merge_into a b b)) += let aux () : Lemma + (requires (merge_into a b a)) + (ensures False) + = assert ( + let open Steel.FractionalPermission in + let open FStar.Real in + (array__perm_val a).v +. (array__perm_val b).v >. (array__perm_val b).v + ) + in + Classical.move_requires aux () + +val tsplit + (#base: Type) + (#t: Type) + (r: array base t) + (i: size_t) +: Pure (array base t & array base t) + (requires (size_v i <= length r)) + (ensures (fun (rl, rr) -> + merge_into rl rr r /\ + length rl == size_v i + )) + +let tsplit #base #t r i = + let h = half_perm (array__perm_val r) in + let r1 : array base t = + (fst r, Ghost.hide (Some ({ + to = (array__from r) `size_add` i; + perm_val = h; + }))) + in + let r2 : array base t = (Some ({ + base_len = (array__base_len r); + base_ref = (array__base_ref r); + from = (array__from r) `size_add` i; + perm_ref = (array__perm_ref r); + }), Ghost.hide (Some ({ + to = (array__to r); + perm_val = h; + }))) + in + (r1, r2) + +let gsplit r i = + let (rl, rr) = tsplit r i in + GPair rl rr + +val pts_to_split + (t: Type) + (n: size_t) + (x: array_pcm_carrier t n) + (i: size_t) +: Lemma + (requires (size_v i <= size_v n)) + (ensures ( + let z = mk_size_t 0ul in + let xl = array_small_to_large_f t n z i () (array_large_to_small_f t n z i () x) in + let xr = array_small_to_large_f t n i n () (array_large_to_small_f t n i n () x) in + composable (array_pcm t n) xl xr /\ + op (array_pcm t n) xl xr == x + )) + +let pts_to_split t n x i = + let z = mk_size_t 0ul in + let xl = array_small_to_large_f t n z i () (array_large_to_small_f t n z i () x) in + let xr = array_small_to_large_f t n i n () (array_large_to_small_f t n i n () x) in + assert (composable (array_pcm t n) xl xr); + assert (op (array_pcm t n) xl xr `feq` x) + +val to_carrier_split + (t: Type) + (n: size_t) + (x: array_pcm_carrier t n) + (v: array_view_type t n) + (i: size_t) +: Lemma + (requires ( + size_v i <= size_v n /\ + (array_view' t n).Steel.C.Model.Ref.to_carrier v == x + )) + (ensures ( + let z = mk_size_t 0ul in + let xl = (array_large_to_small_f t n z i () x) in + let xr = (array_large_to_small_f t n i n () x) in + (array_view' t i).Steel.C.Model.Ref.to_carrier (Seq.slice v 0 (size_v i)) == xl /\ + (array_view' t (n `size_sub` i)).Steel.C.Model.Ref.to_carrier (Seq.slice v (size_v i) (size_v n)) == xr + )) + +#push-options "--z3rlimit 32" +#restart-solver + +let to_carrier_split t n x v i = + let z = mk_size_t 0ul in + let xl = (array_large_to_small_f t n z i () x) in + let xr = (array_large_to_small_f t n i n () x) in + assert ((array_view' t i).Steel.C.Model.Ref.to_carrier (Seq.slice v 0 (size_v i)) `feq` xl); + assert ((array_view' t (n `size_sub` i)).Steel.C.Model.Ref.to_carrier (Seq.slice v (size_v i) (size_v n)) `feq` xr) + +let array_as_ref_split_left + (base: Type) + (t: Type) + (x: array base t) + (i: size_t) +: Lemma + (requires (size_v i <= length x)) + (ensures ( + array_as_ref (fst (tsplit x i)) == Steel.C.Model.Ref.ref_focus (array_as_ref x) (array_conn t (len x) zero_size i ()) + )) += + array_conn_compose t (array__base_len x) (array__from x) (array__to x) zero_size i; + Steel.C.Model.Ref.ref_focus_comp (array__base_ref x) (array_as_ref_conn x) (array_conn t (len x) zero_size i ()) + +#restart-solver +let array_as_ref_split_right + (base: Type) + (t: Type) + (x: array base t) + (i: size_t) +: Lemma + (requires (size_v i <= length x)) + (ensures ( + array_as_ref (snd (tsplit x i)) == Steel.C.Model.Ref.ref_focus (array_as_ref x) (array_conn t (len x) i (len x) ()) + )) += + array_conn_compose t (array__base_len x) (array__from x) (array__to x) i (len x); + Steel.C.Model.Ref.ref_focus_comp (array__base_ref x) (array_as_ref_conn x) (array_conn t (len x) i (len x) ()) + +#restart-solver +assume +val split_ (#opened: _) (#base: Type) (#t:Type) (a:array base t) (i:size_t) + : SteelGhost (array base t `gpair` array base t) opened + (varray a) + (fun res -> varray (GPair?.fst res) `star` varray (GPair?.snd res)) + (fun _ -> size_v i <= length a) + (fun h res h' -> + let s = h (varray a) in + let sl = h' (varray (GPair?.fst res)) in + let sr = h' (varray (GPair?.snd res)) in + size_v i <= length a /\ + res == gsplit a i /\ + sl == Seq.slice s 0 (size_v i) /\ + sr == Seq.slice s (size_v i) (length a) + ) + +#pop-options + +#push-options "--z3rlimit 128" + +(* +#restart-solver +let split_ + #j #base #t x i += + let gv = gget (varray x) in + elim_varray1 x; + let v = Steel.C.Model.Ref.pts_to_view_elim + #j + #base + #(array_pcm_carrier t (len x)) + #(array_pcm t (len x)) + (array_as_ref #base #t x) + #(array_view_type t (len x)) + #(size_v (len x) = 0) + (array_view' t (len x)) + in + pts_to_split t (len x) v i; + let (xl, xr) = tsplit x i in + let n = len x in + let z = mk_size_t 0ul in + let vl' : array_pcm_carrier t (len xl) = array_large_to_small_f t n z i () v in + let vl : array_pcm_carrier t (len x) = array_small_to_large_f t n z i () vl' in + let vr' : array_pcm_carrier t (len xr) = array_large_to_small_f t n i n () v in + let vr : array_pcm_carrier t (len x) = array_small_to_large_f t n i n () vr' in + Steel.C.Model.Ref.split + (array_as_ref #base #t x) + v + vl + vr; + let cl : (cl: Steel.C.Model.Connection.connection + (array_pcm t (len x)) + (array_pcm t (len xl)) { + cl === array_conn t n z i () + }) + = magic () // array_conn t n z i () // FIXME: WHY WHY WHY does this send F* off rails (> 35 GB RAM consumption and going) + in + Steel.C.Model.Ref.gfocus + (array_as_ref #base #t x) + cl + vl + vl'; + array_as_ref_split_left _ t x i; + assert (array_as_ref xl == Steel.C.Model.Ref.ref_focus (array_as_ref x) cl); + change_equal_slprop + (_ `Steel.C.Model.Ref.pts_to` vl') + (array_as_ref xl `Steel.C.Model.Ref.pts_to` vl'); + to_carrier_split t n v gv i; + let gvl : array_view_type t (len xl) = Seq.slice gv 0 (size_v i) in + Steel.C.Model.Ref.pts_to_view_intro + #j + #base + #(array_pcm_carrier t (len xl)) + #(array_pcm t (len xl)) + (array_as_ref xl) + vl' + #(array_view_type t (len xl)) + #(size_v (len xl) = 0) + (array_view' t (len xl)) + gvl; + change_equal_slprop // necessary, otherwise F* goes off rails + (array_as_ref xl `Steel.C.Model.Ref.pts_to_view` _) + (varray0 xl); + Steel.Reference.ghost_share (array__perm_ref x); + change_equal_slprop + (Steel.Reference.ghost_vptrp (array__perm_ref x) (Steel.FractionalPermission.half_perm (array__perm_val x))) + (Steel.Reference.ghost_vptrp (array__perm_ref xl) (array__perm_val xl)); + intro_varray1 xl; + let cr : (cr: Steel.C.Model.Connection.connection + (array_pcm t (len x)) + (array_pcm t (len xr)) { + cr === array_conn t n i n () + }) + = magic () // array_conn t n i n () // FIXME: WHY WHY WHY does this send F* off rails (> 35 GB RAM consumption and going) + in + Steel.C.Model.Ref.gfocus + (array_as_ref #base #t x) + cr + vr + vr'; + array_as_ref_split_right _ t x i; + assert (array_as_ref xr == Steel.C.Model.Ref.ref_focus (array_as_ref x) cr); + change_equal_slprop + (_ `Steel.C.Model.Ref.pts_to` vr') + (array_as_ref xr `Steel.C.Model.Ref.pts_to` vr'); + let gvr : array_view_type t (len xr) = Seq.slice gv (size_v i) (size_v n) in +// let _ : squash ((Ghost.reveal gv <: Seq.seq t) == gvl `Seq.append` gvr) = +// Seq.lemma_split gv (size_v i) +// in + Steel.C.Model.Ref.pts_to_view_intro + #j + #base + #(array_pcm_carrier t (len xr)) + #(array_pcm t (len xr)) + (array_as_ref xr) + vr' + #(array_view_type t (len xr)) + #(size_v (len xr) = 0) + (array_view' t (len xr)) + gvr; + change_equal_slprop // necessary, otherwise F* goes off rails + (array_as_ref xr `Steel.C.Model.Ref.pts_to_view` _) + (varray0 xr); + change_equal_slprop + (Steel.Reference.ghost_vptrp (array__perm_ref x) (Steel.FractionalPermission.half_perm (array__perm_val x))) + (Steel.Reference.ghost_vptrp (array__perm_ref xr) (array__perm_val xr)); + intro_varray1 xr; + let res = GPair xl xr in + change_equal_slprop + (varray xl) + (varray (GPair?.fst res)); + change_equal_slprop + (varray xr) + (varray (GPair?.snd res)); + res +*) + +let split' + #_ #_ #t a i += + let g = gget (varray a) in + Seq.lemma_split #t (Ghost.reveal g) (size_v i); + split_ a i + +let split_right_from + a i += + return (fst (snd (tsplit a i))) + +let join' = admit () + +let array_as_one_ref_iso + (base: Type) + (t: Type) +: Tot (Steel.C.Model.Connection.isomorphism (array_pcm t one_size) (Steel.C.Opt.opt_pcm #t)) += let c1 = (Steel.C.Model.Struct.struct_to_field (array_elements_pcm t one_size) zero_size) in + let c2 = (Steel.C.Model.Struct.field_to_struct (array_elements_pcm t one_size) zero_size) in + Steel.C.Model.Connection.mkisomorphism + c1 + c2 + () + (Steel.C.Model.Connection.is_inverse_of_intro + c2.Steel.C.Model.Connection.morph + c1.Steel.C.Model.Connection.morph + (fun x -> + array_pcm_carrier_ext t one_size (c2.Steel.C.Model.Connection.morph (c1.Steel.C.Model.Connection.morph x)) x (fun i -> + () + ) + ) + ) + (fun x -> ()) + (fun x -> ()) + +let array_as_one_ref_conn + (base: Type) + (t: Type) +: Tot (Steel.C.Model.Connection.connection (array_pcm t one_size) (Steel.C.Opt.opt_pcm #t)) += Steel.C.Model.Connection.connection_of_isomorphism (array_as_one_ref_iso base t) + +let g_ref_of_array + #base #t r += + array_as_ref r `Steel.C.Model.Ref.ref_focus` array_as_one_ref_conn base t + +let array_as_one_ref_conn' + (#base: Type) (#t:Type0) (r:array base t) +: Pure (Steel.C.Model.Connection.connection (array_pcm t (array__base_len r)) (Steel.C.Opt.opt_pcm #t)) + (requires (size_v (len r) == 1)) + (ensures (fun _ -> True)) += + array_conn t (array__base_len r) (array__from r) ((array__from r) `size_add` one_size) () `Steel.C.Model.Connection.connection_compose` array_as_one_ref_conn base t + +#restart-solver +let array_as_one_ref_conn'_small_to_large + (#base: Type) (#t:Type0) (r:array base t) + (x: option t) + (i: array_domain t (array__base_len r)) +: Lemma + (requires (size_v (len r) == 1)) + (ensures ((array_as_one_ref_conn' r).Steel.C.Model.Connection.conn_small_to_large.Steel.C.Model.Connection.morph x i == (if i = (array__from r) then x else None))) += Steel.C.Model.Connection.morphism_compose_morph + (array_as_one_ref_conn base t).Steel.C.Model.Connection.conn_small_to_large + (array_conn t (array__base_len r) (array__from r) (array__from r `size_add` one_size) ()).Steel.C.Model.Connection.conn_small_to_large + x + +let g_ref_of_array' + (#base: Type) (#t:Type0) (r:array base t) +: Ghost (Steel.C.Reference.ref base t (Steel.C.Opt.opt_pcm #t)) + (requires (size_v (len r) == 1)) + (ensures (fun _ -> True)) += (array__base_ref r) `Steel.C.Model.Ref.ref_focus` array_as_one_ref_conn' r + +let g_ref_of_array'_correct + (#base: Type) (#t:Type0) (r:array base t) +: Lemma + (requires (length r == 1)) + (ensures (g_ref_of_array r == g_ref_of_array' r)) += + Steel.C.Model.Ref.ref_focus_comp (array__base_ref r) (array_conn t (array__base_len r) (array__from r) (array__to r) ()) (array_as_one_ref_conn base t) + +let get_pts_to + (#inames: _) + (#a: Type u#0) (#b: Type u#b) (#p: Steel.C.Model.PCM.pcm b) + (r: Steel.C.Model.Ref.ref a p) (v: Ghost.erased b) +: SteelGhost (Ghost.erased b) inames + (Steel.C.Model.Ref.pts_to r v) + (fun v' -> Steel.C.Model.Ref.pts_to r v) + (fun _ -> True) + (fun _ v' _ -> v' == v) += noop(); v + +let v_ref_of_array r = + Steel.Reference.ghost_vptrp (array__perm_ref r) (array__perm_val r) + +(* +assume +val abstract_id + (#t: Type) + (x: t) +: Pure t + (requires True) + (ensures (fun y -> x == y)) +*) + +#push-options "--z3rlimit 64 --fuel 1 --ifuel 2 --query_stats --z3cliopt smt.arith.nl=false --print_implicits" + +#restart-solver +let ref_of_array_ghost #inames #base #t x sq = + let gv = gget (varray x) in + elim_varray1 x; + let v : Ghost.erased (array_pcm_carrier t (len x)) = Steel.C.Model.Ref.pts_to_view_elim + #inames + #base + #(array_pcm_carrier t (len x)) + #(array_pcm t (len x)) + (array_as_ref #base #t x) + #(array_view_type t (len x)) + #(size_v (len x) = 0) + (array_view' t (len x)) + in + assert (len x == one_size); + let z : array_domain t one_size = zero_size in + assert (Ghost.reveal v `feq` (array_as_one_ref_conn base t).Steel.C.Model.Connection.conn_small_to_large.Steel.C.Model.Connection.morph (Ghost.reveal v z)); + Steel.C.Model.Ref.gfocus + #base + #(array_pcm_carrier t (len x)) + #(option t) + #_ + #(array_pcm t (len x)) + (array_as_ref x) + #(Steel.C.Opt.opt_pcm #t) + (array_as_one_ref_conn base t) + _ + (Ghost.reveal v z); + Steel.C.Model.Ref.pts_to_view_intro + #inames + #base + #(option t) + #(Steel.C.Opt.opt_pcm #t) + (Steel.C.Model.Ref.ref_focus (array_as_ref x) (array_as_one_ref_conn base t)) + (Ghost.reveal v z) + #t + #false + (Steel.C.Opt.opt_view t) + (Ghost.hide (Seq.index (Ghost.reveal gv <: Seq.seq t) 0)); + change_equal_slprop + (Steel.C.Model.Ref.pts_to_view _ _) + (Steel.C.Model.Ref.pts_to_view (g_ref_of_array x) (Steel.C.Opt.opt_view t)) + +#restart-solver +val ref_of_array0 (#base: Type) (#t:Type0) (#opened: _) (r:array base t) (sq: squash (length r == 1)) (v0: Ghost.erased t) + : SteelAtomicBase (Steel.C.Reference.ref base t (Steel.C.Opt.opt_pcm #t)) + false opened Unobservable + (varray r) + (fun r' -> (Steel.C.Model.Ref.pts_to_view r' (Steel.C.Opt.opt_view t) `vrefine` (fun v' -> v' == Ghost.reveal v0)) `star` pure (g_ref_of_array #base #t r == r') `star` v_ref_of_array r) + (requires fun h0 -> Seq.index (h0 (varray r)) 0 == Ghost.reveal v0) + (ensures fun h0 r' h1 -> True) + +#restart-solver +let ref_of_array0 #base #t x sq v0 = + let gv : Ghost.erased (array_view_type t (len x)) = gget (varray x) in + assert (Seq.index (Ghost.reveal gv) 0 == Ghost.reveal v0); + elim_varray1 x; + let v : Ghost.erased (array_pcm_carrier t (len x)) = Steel.C.Model.Ref.pts_to_view_elim + #_ + #base + #(array_pcm_carrier t (len x)) + #(array_pcm t (len x)) + (array_as_ref #base #t x) + #(array_view_type t (len x)) + #(size_v (len x) = 0) + (array_view' t (len x)) + in + Steel.C.Model.Ref.unfocus _ (array__base_ref x) (array_as_ref_conn x) _; + let s = get_pts_to (array__base_ref x) _ in + let ar : Ghost.erased (array_pcm_carrier t (array__base_len x)) = Ghost.hide ((array_as_one_ref_conn' x).Steel.C.Model.Connection.conn_small_to_large.Steel.C.Model.Connection.morph (Ghost.reveal v zero_size)) in + array_pcm_carrier_ext t (array__base_len x) (Ghost.reveal s) (Ghost.reveal ar) (fun i -> + array_as_one_ref_conn'_small_to_large x (Ghost.reveal v zero_size) i + ); + g_ref_of_array'_correct x; + let r : Steel.C.Reference.ref base t (Steel.C.Opt.opt_pcm #t) = Steel.C.Model.Ref.focus (array__base_ref x) (array_as_one_ref_conn' x) s (Ghost.reveal v zero_size) in + Steel.C.Model.Ref.pts_to_view_intro + #_ + #base + #(option t) + #(Steel.C.Opt.opt_pcm #t) + r + (Ghost.reveal v zero_size) + #t + #false + (Steel.C.Opt.opt_view t) + (Ghost.hide (Seq.index (Ghost.reveal gv <: Seq.seq t) 0)); + intro_vrefine + (Steel.C.Model.Ref.pts_to_view r (Steel.C.Opt.opt_view t)) + (fun v' -> v' == Ghost.reveal v0); + intro_pure (g_ref_of_array #base #t x == r); + return r + +#restart-solver +let ref_of_array_from #base #t r_from r_to sq = + let x : array base t = (r_from, r_to) in + change_equal_slprop + (varray (r_from, r_to)) + (varray x); + let gv : Ghost.erased (array_view_type t (len x)) = gget (varray x) in + let v0 = Ghost.hide (Seq.index (Ghost.reveal gv) 0) in + let r = ref_of_array0 x () v0 in + elim_pure (g_ref_of_array x == r); + elim_vrefine + (Steel.C.Model.Ref.pts_to_view r (Steel.C.Opt.opt_view t)) + (fun v' -> v' == Ghost.reveal v0); + change_equal_slprop + (v_ref_of_array x) + (v_ref_of_array (r_from, r_to)); + return r + +#restart-solver +let array_of_ref + #_ #base #t r' r sq += + let g : Ghost.erased t = gget (Steel.C.Model.Ref.pts_to_view r (Steel.C.Opt.opt_view t)) in + let v = Steel.C.Model.Ref.pts_to_view_elim + r + (Steel.C.Opt.opt_view t) + in + Steel.C.Model.Ref.unfocus + r + (array_as_ref r') + (array_as_one_ref_conn base t) + v; + let g' : Ghost.erased (array_view_type t (len r')) = + (Ghost.hide (Seq.create 1 (Ghost.reveal g))) + in + let v' : Ghost.erased (array_pcm_carrier t (len r')) = + get_pts_to (array_as_ref r') _ + in + array_pcm_carrier_ext t (len r') ((array_view t (len r')).Steel.C.Model.Ref.to_carrier g') (Ghost.reveal v') (fun i -> + assert (i == zero_size) + ); + Steel.C.Model.Ref.pts_to_view_intro + _ + _ + (array_view t (len r')) + g'; + change_equal_slprop + (Steel.C.Model.Ref.pts_to_view (array_as_ref r') (array_view t (len r'))) + (varray0 r'); + intro_varray1 r' + +#restart-solver +let one_ref_as_array_conn + (base: Type) (t:Type0) +: Tot (Steel.C.Model.Connection.connection (Steel.C.Opt.opt_pcm #t) (array_pcm t one_size)) += + Steel.C.Model.Connection.(connection_of_isomorphism (isomorphism_inverse (array_as_one_ref_iso base t))) + +let mk_array_of_ref' (#base: Type) (#t:Type0) (r: Steel.C.Reference.ref base t (Steel.C.Opt.opt_pcm #t)) (perm_ref: Steel.Reference.ghost_ref unit) : GTot (array base t) = + (Some ({ + base_len = one_size; + base_ref = r `Steel.C.Model.Ref.ref_focus` one_ref_as_array_conn base t; + from = zero_size; + perm_ref = perm_ref; + }), Ghost.hide (Some ({ + to = one_size; + perm_val = Steel.FractionalPermission.full_perm; + }))) + +#restart-solver +let mk_array_of_ref'_correct + (#base: Type) (#t:Type0) (r: Steel.C.Reference.ref base t (Steel.C.Opt.opt_pcm #t)) (perm_ref: Steel.Reference.ghost_ref unit) +: Lemma + (g_ref_of_array (mk_array_of_ref' r perm_ref) == r) += + g_ref_of_array'_correct (mk_array_of_ref' r perm_ref); + array_conn_id t one_size; + Steel.C.Model.Connection.connection_compose_id_left (array_as_one_ref_conn base t); + Steel.C.Model.Ref.ref_focus_comp r (one_ref_as_array_conn base t) (array_as_one_ref_conn base t); + Steel.C.Model.Connection.connection_of_isomorphism_inverse_left (array_as_one_ref_iso base t); + Steel.C.Model.Ref.ref_focus_id r + +#restart-solver +let array_as_ref_eq_base_ref + (#base: Type) (#t:Type0) (a: array base t) +: Lemma + (requires ( + array__base_len a == one_size /\ + array__from a == zero_size /\ + array__to a == one_size + )) + (ensures ( + array_as_ref a == (array__base_ref a) + )) += + array_conn_id t one_size; + Steel.C.Model.Ref.ref_focus_id (array__base_ref a) + +#restart-solver +let array_as_ref_mk_array_of_ref' + (#base: Type) (#t:Type0) (r: Steel.C.Reference.ref base t (Steel.C.Opt.opt_pcm #t)) (perm_ref: Steel.Reference.ghost_ref unit) +: Lemma + (ensures ( + let x = mk_array_of_ref' r perm_ref in + array_as_ref x == (array__base_ref x) + )) += + let x = mk_array_of_ref' r perm_ref in + array_as_ref_eq_base_ref x + +let array_domain_one_size + (t: Type) + (i: array_domain t one_size) +: Lemma + (i == zero_size) += () + +#restart-solver +let mk_array_of_ref_view_intro (base: Type) (#t:Type0) + (g: Ghost.erased t) + (v: Ghost.erased (option t)) + (v' : Ghost.erased (array_pcm_carrier t one_size)) + (g' : Ghost.erased (array_view_type t one_size)) +: Lemma + (requires ( + Ghost.reveal v == (Steel.C.Opt.opt_view t).Steel.C.Model.Ref.to_carrier (Ghost.reveal g) /\ + Ghost.reveal v' == (array_as_one_ref_conn base t).Steel.C.Model.Connection.conn_small_to_large.Steel.C.Model.Connection.morph (Ghost.reveal v) /\ + Ghost.reveal g' == Seq.create 1 (Ghost.reveal g) + )) + (ensures ( + (array_view t one_size).Steel.C.Model.Ref.to_carrier g' == (Ghost.reveal v') + )) += array_pcm_carrier_ext t one_size ((array_view t one_size).Steel.C.Model.Ref.to_carrier g') (Ghost.reveal v') (fun i -> + () + ) + +let mk_array_of_ref_to' + (base: Type) (t:Type0) +: Tot (array_or_null_to base t) += Some ({ + to = one_size; + perm_val = Steel.FractionalPermission.full_perm; + }) + +let mk_array_of_ref_from_spec + #base #t r from += + let a = (from, mk_array_of_ref_to' base t) in + array_or_null_spec a /\ + g_is_null a == false /\ + array__base_len a == one_size /\ + array__from a == zero_size /\ + array__base_ref a == r `Steel.C.Model.Ref.ref_focus` one_ref_as_array_conn base t + +let mk_array_of_ref_to #base #t r from = mk_array_of_ref_to' base t + +val mk_array_of_ref0 (#base: Type) (#t:Type0) (#opened: _) (r: Steel.C.Reference.ref base t (Steel.C.Opt.opt_pcm #t)) + : SteelAtomicBase (array base t) + false opened Unobservable + (Steel.C.Model.Ref.pts_to_view r (Steel.C.Opt.opt_view t)) + (fun r' -> varray r') + (requires fun _ -> True) + (ensures fun h0 r' h1 -> + let s = h1 (varray r') in + Seq.length s == 1 /\ + g_ref_of_array r' == r /\ + r' == mk_array_of_ref' r (array__perm_ref r') /\ + h0 (Steel.C.Model.Ref.pts_to_view r (Steel.C.Opt.opt_view t)) == Seq.index s 0 + ) + +#restart-solver +let mk_array_of_ref0 + #base #t r += + let g : Ghost.erased t = gget (Steel.C.Model.Ref.pts_to_view r (Steel.C.Opt.opt_view t)) in + let v : Ghost.erased (option t) = Steel.C.Model.Ref.pts_to_view_elim r (Steel.C.Opt.opt_view t) in + let v' : Ghost.erased (array_pcm_carrier t one_size) = Ghost.hide ((array_as_one_ref_conn base t).Steel.C.Model.Connection.conn_small_to_large.Steel.C.Model.Connection.morph (Ghost.reveal v)) in + let _ : squash (Ghost.reveal v == (one_ref_as_array_conn base t).Steel.C.Model.Connection.conn_small_to_large.Steel.C.Model.Connection.morph (Ghost.reveal v')) = + Steel.C.Model.Connection.connection_of_isomorphism_inverse_left (array_as_one_ref_iso base t) + in + let r' = Steel.C.Model.Ref.focus r (one_ref_as_array_conn base t) v v' in + let perm_ref = Steel.Reference.ghost_alloc #unit () in + let res : array base t = (Some ({ + base_len = one_size; + base_ref = r'; + from = zero_size; + perm_ref = perm_ref; + }), Ghost.hide (Some ({ + to = one_size; + perm_val = Steel.FractionalPermission.full_perm; + }))) + in + assert (res == mk_array_of_ref' r perm_ref); + mk_array_of_ref'_correct r perm_ref; + let g' : Ghost.erased (array_view_type t one_size) = + Ghost.hide (Seq.create 1 (Ghost.reveal g)) + in + mk_array_of_ref_view_intro base g v v' g' ; + Steel.C.Model.Ref.pts_to_view_intro + _ + _ + (array_view t one_size) + g'; + array_as_ref_mk_array_of_ref' r perm_ref; + change_equal_slprop + (Steel.C.Model.Ref.pts_to_view r' (array_view t one_size)) + (varray0 res); + change_equal_slprop + (Steel.Reference.ghost_vptr perm_ref) + (Steel.Reference.ghost_vptrp (array__perm_ref res) (array__perm_val res)); + intro_varray1 res; + return res + +let mk_array_of_ref_from + #base #t r += + let a = mk_array_of_ref0 r in + let res = fst a in + change_equal_slprop + (varray a) + (varray (res, mk_array_of_ref_to r res)); + return res + +#pop-options + +let varray_or_null0_rewrite + (#base #a: Type0) + (r: array_or_null base a) + (_: t_of emp) +: Tot (option (array_view_type a (len r))) += None + +[@@__steel_reduce__] +let varray_or_null0 + (#base #a: Type0) + (r: array_or_null base a) +: Tot vprop += if g_is_null r + then emp `vrewrite` varray_or_null0_rewrite r + else varray r `vrewrite` Some + +let is_array_or_null r = hp_of (varray_or_null0 r) +let array_or_null_sel r = sel_of (varray_or_null0 r) + +let intro_varray_or_null_none x = + intro_vrewrite emp (varray_or_null0_rewrite x); + change_equal_slprop + (emp `vrewrite` varray_or_null0_rewrite x) + (varray_or_null0 x); + change_slprop_rel + (varray_or_null0 x) + (varray_or_null x) + (fun u v -> u == v) + (fun _ -> ()) + +let intro_varray_or_null_some x = + intro_vrewrite (varray x) Some; + change_equal_slprop + (varray x `vrewrite` Some) + (varray_or_null0 x); + change_slprop_rel + (varray_or_null0 x) + (varray_or_null x) + (fun u v -> u == v) + (fun _ -> ()) + +let elim_varray_or_null_some x = + change_slprop_rel + (varray_or_null x) + (varray_or_null0 x) + (fun u v -> u == v) + (fun _ -> ()); + if g_is_null x + then begin + change_equal_slprop + (varray_or_null0 x) + (emp `vrewrite` varray_or_null0_rewrite x); + elim_vrewrite emp (varray_or_null0_rewrite x); + assert False; + change_equal_slprop + emp + (varray x) + end else begin + change_equal_slprop + (varray_or_null0 x) + (varray x `vrewrite` Some); + elim_vrewrite (varray x) Some + end + +let elim_varray_or_null_none x = + change_slprop_rel + (varray_or_null x) + (varray_or_null0 x) + (fun u v -> u == v) + (fun _ -> ()); + if g_is_null x + then begin + change_equal_slprop + (varray_or_null0 x) + (emp `vrewrite` varray_or_null0_rewrite x); + elim_vrewrite emp (varray_or_null0_rewrite x) + end else begin + change_equal_slprop + (varray_or_null0 x) + (varray x `vrewrite` Some); + elim_vrewrite (varray x) Some; + assert False; + change_equal_slprop + (varray x) + emp + end + +#restart-solver +let freeable + #base #t a += + Steel.C.Model.Ref.freeable (array__base_ref a) /\ + size_v (array__base_len a) > 0 /\ + (array__perm_val a) == Steel.FractionalPermission.full_perm /\ + (array__from a) == zero_size /\ + (array__to a) == (array__base_len a) + +#restart-solver +let array_to_carrier_refine + (#t: Type0) + (n: size_t) + (v: array_view_type t n) +: Lemma + (requires (size_v n > 0)) + (ensures (p_refine (array_pcm t n) (array_to_carrier t n v))) += FStar.Classical.exists_intro (fun (k: array_domain t n) -> True) zero_size + +let malloc_to' + (#t: Type0) + (x: t) + (n: size_t) + (from: array_or_null_from (array_pcm_carrier t n) t) +: Tot (array_or_null_to (array_pcm_carrier t n) t) += if None? from + then None + else Some ({ + to = n; + perm_val = Steel.FractionalPermission.full_perm; + }) + +let malloc_from_spec + #t x n from += + let a = (from, malloc_to' x n from) in + array_or_null_spec a /\ + (g_is_null a == false ==> freeable a) + +let malloc_to x n from = malloc_to' x n from + +val malloc0 + (#t: Type0) + (x: t) + (n: size_t) +: Steel (array_or_null (array_pcm_carrier t n) t) + emp + (fun r -> varray_or_null r) + (requires fun _ -> size_v n > 0) + (ensures fun _ r h' -> + size_v n > 0 /\ + malloc_from_spec x n (fst r) /\ + snd r == malloc_to x n (fst r) /\ + (g_is_null r == false ==> (freeable r /\ len r == n /\ h' (varray_or_null r) == Some (Seq.create (size_v n) x))) + ) + +#restart-solver +let malloc0 + #t x n += + let v = Seq.create (size_v n) x in + let c = array_to_carrier t n v in + array_to_carrier_refine n v; + let r0 = Steel.C.Model.Ref.ref_alloc (array_pcm t n) c in + Steel.C.Model.Ref.pts_to_view_intro r0 c (array_view t n) v; + let r = intro_varray r0 () in + intro_varray_or_null_some r; + return r + +let malloc_from + #t x n sq += let a = malloc0 x n in + let res = fst a in + change_equal_slprop + (varray_or_null a) + (varray_or_null (res, malloc_to x n res)); + return res + +val free0 + (#base: Type0) + (#t: Type0) + (a: array base t) +: Steel unit + (varray a) + (fun _ -> emp) + (requires (fun _ -> freeable a)) + (ensures (fun _ _ _ -> True)) + +#restart-solver +#push-options "--print_implicits" +let free0 + #base #t a += + let r = (array__base_ref a) in + elim_varray r a (); + let v = Steel.C.Model.Ref.pts_to_view_elim + #_ + #_ + #(array_pcm_carrier t (Ghost.hide (Ghost.reveal (array__base_len a)))) + #(array_pcm t (Ghost.hide (Ghost.reveal (array__base_len a)))) + r + (array_view t (array__base_len a)) + in + Steel.C.Model.Ref.ref_free + #_ + #(array_pcm_carrier t (Ghost.hide (Ghost.reveal (array__base_len a)))) + #(array_pcm t (Ghost.hide (Ghost.reveal (array__base_len a)))) + #v + r + +let free_from + #base #t a a' sq += + let a0 : array base t = (a, a') in + change_equal_slprop + (varray (a, a')) + (varray a0); + free0 a0 + +let is_null_from a a' sq = + return (None? a) diff --git a/src/proofs/steelc/Steel.C.Array.fst b/src/proofs/steelc/Steel.C.Array.fst new file mode 100644 index 000000000..1f122de96 --- /dev/null +++ b/src/proofs/steelc/Steel.C.Array.fst @@ -0,0 +1,132 @@ +module Steel.C.Array + +let seq_equal_1 + (t: Type) + (s1 s2: Seq.seq t) +: Lemma + (requires ( + Seq.length s1 == 1 /\ + Seq.length s2 == 1 /\ + Seq.index s1 0 == Seq.index s2 0 + )) + (ensures (s1 == s2)) += assert (s1 `Seq.equal` s2) + +val index0 (#base: Type) (#t:Type) (r:array base t) (i:size_t) + : Steel t + (varray r) + (fun _ -> varray r) + (requires fun _ -> size_v i < length r) + (ensures fun h0 x h1 -> + let s = h1 (varray r) in + size_v i < length r /\ + h0 (varray r) == s /\ + x == Seq.index s (size_v i)) + +#push-options "--z3rlimit 128 --fuel 1 --ifuel 2 --query_stats --z3cliopt smt.arith.nl=false" +#restart-solver + +let index0 + #_ #t r i += + let rr = split r i () in + let rrr = split rr one_size () in + change_equal_slprop + (varray rrr) + (varray (Ghost.reveal (Ghost.hide rrr))); + let rrl = split_left rr (GPair?.fst (gsplit rr one_size)) rrr in + let grl = gget (varray rrl) in + let r0 = ref_of_array rrl () in + let res = Steel.C.Opt.ref_opt_read r0 in + array_of_ref rrl r0 (); + let grl' = gget (varray rrl) in + seq_equal_1 t (Ghost.reveal grl) (Ghost.reveal grl'); + let rr' = join' rrl (Ghost.reveal (Ghost.hide rrr)) in + let r' = join' (Ghost.reveal (Ghost.hide (GPair?.fst (gsplit r i)))) rr' in + change_equal_slprop + (varray r') + (varray r); + return res + +let index_from + #base #t r r' i += + let r0 : array base t = (r, r') in + change_equal_slprop + (varray (r, r')) + (varray r0); + let res = index0 r0 i in + change_equal_slprop + (varray r0) + (varray (r, r')); + return res + +let seq_append_append_upd + (t: Type) + (i: nat) + (x: t) + (s1 s2 s2' s3: Seq.seq t) +: Lemma + (requires ( + Seq.length s1 == i /\ + Seq.length s2 == 1 /\ + Seq.length s2' == 1 /\ + Seq.index s2' 0 == x + )) + (ensures ( + s1 `Seq.append` (s2' `Seq.append` s3) == Seq.upd (s1 `Seq.append` (s2 `Seq.append` s3)) i x + )) += assert ( + (s1 `Seq.append` (s2' `Seq.append` s3)) `Seq.equal` (Seq.upd (s1 `Seq.append` (s2 `Seq.append` s3)) i x) + ) + +val upd0 (#base: Type) (#t:Type) (r:array base t) (i:size_t) (x:t) + : Steel unit + (varray r) + (fun _ -> varray r) + (requires fun h -> size_v i < length r) + (ensures fun h0 _ h1 -> + size_v i < length r /\ + h1 (varray r) == Seq.upd (h0 (varray r)) (size_v i) x) + +let upd0 + #_ #t r i x += + let rr = split r i () in + let rrr = split rr one_size () in + let s3 = gget (varray rrr) in + change_equal_slprop + (varray rrr) + (varray (Ghost.reveal (Ghost.hide rrr))); + let rrl = split_left rr (GPair?.fst (gsplit rr one_size)) rrr in + let s1 = gget (varray (Ghost.reveal (Ghost.hide (GPair?.fst (gsplit r i))))) in + let s2 = gget (varray rrl) in + let r0 = ref_of_array rrl () in + Steel.C.Opt.ref_opt_write r0 x; + array_of_ref rrl r0 (); + let s2' = gget (varray rrl) in + seq_append_append_upd t (size_v i) x s1 s2 s2' s3; + let rr' = join' rrl (Ghost.reveal (Ghost.hide rrr)) in + let r' = join' (Ghost.reveal (Ghost.hide (GPair?.fst (gsplit r i)))) rr' in + change_equal_slprop + (varray r') + (varray r) + +let upd_from + #base #t r r' i x += + let r0 : array base t = (r, r') in + change_equal_slprop + (varray (r, r')) + (varray r0); + upd0 r0 i x; + change_equal_slprop + (varray r0) + (varray (r, r')) + +let varray_or_null0_rewrite + (#base #a: Type0) + (r: array_or_null base a) + (_: t_of emp) +: Tot (option (array_view_type a (len r))) += None diff --git a/src/proofs/steelc/Steel.ST.C.Types.Array.fst b/src/proofs/steelc/Steel.ST.C.Types.Array.fst new file mode 100644 index 000000000..ac3c3926b --- /dev/null +++ b/src/proofs/steelc/Steel.ST.C.Types.Array.fst @@ -0,0 +1,1322 @@ +module Steel.ST.C.Types.Array +open Steel.ST.GenElim +friend Steel.ST.C.Types.Base +friend Steel.ST.C.Types.Struct.Aux +open Steel.ST.C.Types.Struct.Aux + +open Steel.C.Model.PCM + +/// Base arrays (without decay: explicit array types as top-level arrays or struct/union fields of array type) + +module GHR = Steel.ST.GhostHigherReference +module R = Steel.ST.C.Model.Ref +module HR = Steel.ST.HigherReference +module FX = FStar.FunctionalExtensionality +module A = Steel.ST.C.Model.Array + +let base_array_t' + (t: Type0) + (n: Ghost.erased array_size_t) +: Tot Type0 += A.array_pcm_carrier t (Ghost.hide (Ghost.reveal n)) + +let base_array_t t _ n = base_array_t' t n + +[@@noextract_to "krml"] // proof-only +let base_array_fd + (#t: Type) + (td: typedef t) + (n: Ghost.erased array_size_t) +: Tot (field_description_gen_t (base_array_index_t n)) += { + fd_nonempty = (let _ : base_array_index_t n = 0sz in ()); + fd_type = A.array_range t (Ghost.hide (Ghost.reveal n)); + fd_typedef = (fun _ -> td); + } + +[@@noextract_to "krml"] +let base_array1 (#t: Type0) (td: typedef t) (n: Ghost.erased array_size_t) : Tot (typedef (base_array_t' t n)) = struct1 (base_array_fd td n) + +let base_array0 tn td n = base_array1 td n + +let base_array_index a i = a i + +let base_array_eq #_ #_ #n a1 a2 = + assert (a1 `FX.feq` a2 <==> (forall (i: base_array_index_t n) . a1 i == a2 i)); + a1 `FX.feq` a2 + +let mk_base_array _ n v = A.array_pcm_carrier_of_seq n v + +let mk_base_array_index _ _ _ _ = () + +let base_array_fractionable a td = () + +let base_array_mk_fraction a td p i = () + +let base_array_index_unknown tn n td i = () + +let base_array_index_uninitialized tn n td i = () + +let base_array_index_full td x = () + +let base_array_index_t' (n: Ghost.erased array_size_t) : Tot eqtype = + A.array_domain (Ghost.hide (Ghost.reveal n)) + +let base_array_index_t'_eq + (n: array_size_t) +: Lemma + (base_array_index_t n == base_array_index_t' n) + [SMTPat (base_array_index_t n)] += // syntactic equality of refinement types + assert (base_array_index_t n == base_array_index_t' n) by (FStar.Tactics.trefl ()) + +let array_index_as_field_marker + (n: Ghost.erased array_size_t) + (i: SZ.t) + (j: base_array_index_t' n) +: Tot (base_array_index_t' n) += j + +#set-options "--print_implicits" + +let base_array1_eq + (#t: Type) + (n: Ghost.erased array_size_t) + (td: typedef t) +: Lemma + (ref (base_array1 td n) == ref (struct1 #(base_array_index_t' n) (base_array_fd td n))) +// [SMTPat (ref (base_array1 td n))] += () // assert (ref (base_array1 td n) == ref (struct1 #(base_array_index_t' n) (base_array_fd td n))) by (FStar.Tactics.trefl ()) + +[@@__reduce__] +let has_base_array_cell_as_struct_field0 + (#t: Type) + (#n: Ghost.erased array_size_t) + (#td: typedef t) + (r: ref (base_array1 td n)) + (i: SZ.t) + (j: base_array_index_t' n) + (r': ref td) +: Tot vprop += has_struct_field1 #(base_array_index_t' n) #(base_array_fd td n) r (array_index_as_field_marker n i j) r' + +let has_base_array_cell_as_struct_field + (#t: Type) + (#n: Ghost.erased array_size_t) + (#td: typedef t) + (r: ref (base_array1 td n)) + (i: SZ.t) + (j: base_array_index_t' n) + (r': ref td) +: Tot vprop += has_base_array_cell_as_struct_field0 r i j r' + +[@@__reduce__] +let has_base_array_cell0 + (#t: Type) + (#n: Ghost.erased array_size_t) + (#td: typedef t) + (r: ref (base_array1 td n)) + (i: SZ.t) + (r': ref td) +: Tot vprop += exists_ (fun j -> + has_base_array_cell_as_struct_field r i j r' `star` + pure (i == j) + ) + +let has_base_array_cell1 + (#t: Type) + (#n: Ghost.erased array_size_t) + (#td: typedef t) + (r: ref (base_array1 td n)) + (i: SZ.t) + (r': ref td) +: Tot vprop += has_base_array_cell0 r i r' + +let has_base_array_cell + r i r' += has_base_array_cell0 r i r' + +let has_base_array_cell_post + r i r' += rewrite (has_base_array_cell r i r') (has_base_array_cell0 r i r'); + let _ = gen_elim () in + rewrite (has_base_array_cell0 r i r') (has_base_array_cell r i r') + +let has_base_array_cell_dup' + (#opened: _) + (#t: Type) + (#n: Ghost.erased array_size_t) + (#td: typedef t) + (r: ref (base_array1 td n)) + (i: SZ.t) + (r': ref td) +: STGhostT unit opened + (has_base_array_cell1 r i r') + (fun _ -> has_base_array_cell1 r i r' `star` has_base_array_cell1 r i r') += rewrite (has_base_array_cell1 r i r') (has_base_array_cell0 r i r'); + let _ = gen_elim () in + has_struct_field_dup' #_ #(base_array_index_t' n) #(base_array_fd td n) (r) _ _; + rewrite (has_base_array_cell0 r i r') (has_base_array_cell1 r i r'); + noop (); + rewrite (has_base_array_cell0 r i r') (has_base_array_cell1 r i r') + +let has_base_array_cell_dup + r i r' += has_base_array_cell_dup' r i r' + +let has_base_array_cell_inj' + (#opened: _) + (#t: Type) + (#n: Ghost.erased array_size_t) + (#td: typedef t) + (r: ref (base_array1 td n)) + (i: SZ.t) + (r1 r2: ref td) +: STGhostT unit opened + (has_base_array_cell1 r i r1 `star` has_base_array_cell1 r i r2) + (fun _ -> has_base_array_cell1 r i r1 `star` has_base_array_cell1 r i r2 `star` ref_equiv r1 r2) += rewrite (has_base_array_cell1 r i r1) (has_base_array_cell0 r i r1); + let _ = gen_elim () in + let j = vpattern_replace (fun j -> has_base_array_cell_as_struct_field r i j _) in + rewrite (has_base_array_cell1 r i r2) (has_base_array_cell0 r i r2); + let _ = gen_elim () in + vpattern_rewrite (fun j' -> has_base_array_cell_as_struct_field r i j _ `star` has_base_array_cell_as_struct_field r i j' _) j; + has_struct_field_inj' #_ #(base_array_index_t' n) #(base_array_fd td n) (r) _ r1 r2; + rewrite (has_base_array_cell0 r i r2) (has_base_array_cell1 r i r2); + rewrite (has_base_array_cell0 r i r1) (has_base_array_cell1 r i r1) + +let has_base_array_cell_inj + r i r1 r2 += has_base_array_cell_inj' r i r1 r2 + +let has_base_array_cell_equiv_from' + (#opened: _) + (#t: Type) + (#n: Ghost.erased array_size_t) + (#td: typedef t) + (r1 r2: ref (base_array1 td n)) + (i: SZ.t) + (r': ref td) +: STGhostT unit opened + (has_base_array_cell1 r1 i r' `star` ref_equiv r1 r2) + (fun _ -> has_base_array_cell1 r2 i r' `star` ref_equiv r1 r2) += rewrite (has_base_array_cell1 r1 i r') (has_base_array_cell0 r1 i r'); + let _ = gen_elim () in + has_struct_field_equiv_from' #_ #(base_array_index_t' n) #(base_array_fd td n) (r1) _ r' (r2); + rewrite (has_base_array_cell0 r2 i r') (has_base_array_cell1 r2 i r') + +let has_base_array_cell_equiv_from + r1 r2 i r' += has_base_array_cell_equiv_from' r1 r2 i r' + +let has_base_array_cell_equiv_to' + (#opened: _) + (#t: Type) + (#n: Ghost.erased array_size_t) + (#td: typedef t) + (r: ref (base_array1 td n)) + (i: SZ.t) + (r1 r2: ref td) +: STGhostT unit opened + (has_base_array_cell1 r i r1 `star` ref_equiv r1 r2) + (fun _ -> has_base_array_cell1 r i r2 `star` ref_equiv r1 r2) += rewrite (has_base_array_cell1 r i r1) (has_base_array_cell0 r i r1); + let _ = gen_elim () in + has_struct_field_equiv_to' #_ #(base_array_index_t' n) #(base_array_fd td n) r _ r1 (r2); + rewrite (has_base_array_cell0 r i r2) (has_base_array_cell1 r i r2) + +let has_base_array_cell_equiv_to + r i r1 r2 += has_base_array_cell_equiv_to' r i r1 r2 + +/// Array pointers (with decay) + +noeq +type array_ptr_gen t = { + ar_is_null: Ghost.erased bool; + ar_base_size: Ghost.erased array_size_t; + ar_base: ptr_gen (base_array_t' t ar_base_size); + ar_offset: SZ.t; + ar_prf: squash ( + SZ.v ar_offset <= SZ.v ar_base_size /\ + (Ghost.reveal ar_is_null == true <==> ar_base == void_null) /\ + (ar_base == void_null ==> (SZ.v ar_base_size == 1 /\ SZ.v ar_offset == 0)) + ); +} +let null_array_ptr td = { + ar_is_null = true; + ar_base_size = 1sz; + ar_base = null_gen _; + ar_offset = 0sz; + ar_prf = (); +} +let g_array_ptr_is_null a = a.ar_is_null +let array_ref_base_size ar = if ar.ar_is_null then 0sz else ar.ar_base_size +let has_array_ref_base ar r = ar.ar_base == r +let has_array_ref_base_inj ar r1 r2 = () +let array_ref_offset ar = ar.ar_offset +let array_ref_base_offset_inj a1 r1 a2 r2 = () + +let base_array_pcm_eq + (#t: Type) + (td: typedef t) + (n: Ghost.erased array_size_t) +: Lemma + (A.array_pcm td.pcm (Ghost.hide (Ghost.reveal n)) == (base_array1 td n).pcm) + [SMTPat (base_array1 td n).pcm] += pcm0_ext (A.array_pcm td.pcm (Ghost.hide (Ghost.reveal n))) (base_array1 td n).pcm + (fun _ _ -> ()) + (fun x1 x2 -> + assert (op (A.array_pcm td.pcm (Ghost.hide (Ghost.reveal n))) x1 x2 `FX.feq` op (base_array1 td n).pcm x1 x2) + ) + (fun _ -> ()) + () + +let model_array_of_array_precond + (#t: Type) + (#td: typedef t) + (a: array td) + (base: ref0_v) +: GTot prop += + base.t == base_array_t' t (array_ptr_of a).ar_base_size /\ + base.td == base_array1 td (array_ptr_of a).ar_base_size + +[@@noextract_to "krml"] // proof-only +let model_array_of_array + (#t: Type) + (#td: typedef t) + (a: array td) + (base: ref0_v) +: Pure (A.array base.base td.pcm) + (requires ( + model_array_of_array_precond a base + )) + (ensures (fun _ -> True)) += let (| al, len |) = a in + { + base_len = Ghost.hide (Ghost.reveal al.ar_base_size); + base = base.ref; + offset = al.ar_offset; + len = len; + prf = (); + } + +let has_model_array_of_array + (#t: Type) + (#td: typedef t) + (a: array td) + (base: ref0_v) + (ar: A.array base.base td.pcm) +: GTot prop += + model_array_of_array_precond a base /\ + model_array_of_array a base == ar + +[@@__reduce__] +let array_pts_to0 + (#t: Type) + (#td: typedef t) + (r: array td) + (v: Ghost.erased (Seq.seq t)) +: Tot vprop += exists_ (fun br -> exists_ (fun p -> exists_ (fun (a: A.array br.base td.pcm) -> + HR.pts_to (array_ptr_of r).ar_base p br `star` + A.pts_to a v `star` + pure (has_model_array_of_array r br a) + ))) + +let array_pts_to r v = + array_pts_to0 r v + +let array_pts_to_intro + (#opened: _) + (#t: Type) + (#td: typedef t) + (r: array td) + (v: Ghost.erased (Seq.seq t)) + (br: ref0_v) + (p: P.perm) + (sq: squash (model_array_of_array_precond r br)) +: STGhostT unit opened + ( + HR.pts_to (array_ptr_of r).ar_base p br `star` + A.pts_to (model_array_of_array r br) v + ) + (fun _ -> array_pts_to r v) += noop (); + intro_exists p (fun p -> exists_ (fun (a: A.array br.base td.pcm) -> + HR.pts_to (array_ptr_of r).ar_base p br `star` + A.pts_to a v `star` + pure (has_model_array_of_array r br a) + )); + rewrite (array_pts_to0 r v) (array_pts_to r v) + +let array_pts_to_res_t + (#t: Type) + (#td: typedef t) + (r: array td) +: Tot Type += (br: Ghost.erased ref0_v { model_array_of_array_precond r br }) + +let array_pts_to_elim + (#opened: _) + (#t: Type) + (#td: typedef t) + (r: array td) + (v: Ghost.erased (Seq.seq t)) +: STGhostT (array_pts_to_res_t r) opened + (array_pts_to r v) + (fun br -> exists_ (fun p -> + HR.pts_to (array_ptr_of r).ar_base p br `star` + A.pts_to (model_array_of_array r br) v + )) += rewrite (array_pts_to _ _) (array_pts_to0 r v); + let _ = gen_elim () in + let p = vpattern_replace (fun p -> HR.pts_to _ p _) in + let br0 = vpattern_replace_erased (HR.pts_to _ _) in + let br : array_pts_to_res_t r = br0 in + rewrite (HR.pts_to _ _ _) (HR.pts_to (array_ptr_of r).ar_base p br); + rewrite (A.pts_to _ _) (A.pts_to (model_array_of_array r br) v); + br + +let array_ptr_is_null + r _ += return (HR.is_null r.ar_base) + +let array_pts_to_length r v = + let _ = array_pts_to_elim _ _ in + let _ = gen_elim () in + let _ = A.pts_to_length _ _ in + array_pts_to_intro _ _ _ _ () + +let has_array_of_base' + (#t: Type) + (#n: array_size_t) + (#td: typedef t) + (r: ref (base_array1 td n)) + (a: array td) +: GTot prop += let (| al, len |) = a in + array_ref_base_size al == n /\ + al.ar_base == r /\ + array_ref_offset al == 0sz /\ + Ghost.reveal len == n + +#push-options "--z3rlimit 16 --split_queries" + +#restart-solver + +let base_array_index' (#t: Type0) (#n: array_size_t) (a: base_array_t' t n) +(i: base_array_index_t n) : GTot t += a i + +let seq_of_base_array0 + (#t: Type) + (#n: array_size_t) + (v: base_array_t' t n) +: GTot (Seq.lseq t (SZ.v n)) += Seq.init_ghost (SZ.v n) (fun i -> base_array_index' v (SZ.uint_to_t i)) + +#pop-options + + +#push-options "--z3rlimit 16" +#restart-solver + +let ghost_array_of_base_focus' + (#t: Type) + (#opened: _) + (#n: Ghost.erased array_size_t) + (#td: typedef t) + (#v: Ghost.erased (base_array_t' t n)) + (r: ref (base_array1 td n)) + (a: array td) +: STGhost unit opened + (pts_to r v) + (fun _ -> array_pts_to a (seq_of_base_array0 v)) + (has_array_of_base' r a) + (fun _ -> True) += rewrite (pts_to r v) (pts_to0 r v); + let _ = gen_elim () in + let w = vpattern_replace (HR.pts_to r _) in + let rr = get_ref r in + assert ((model_array_of_array a w).base == rr); + rewrite (r_pts_to _ _) (R.pts_to (model_array_of_array a w).base v); + assert (seq_of_base_array0 v `Seq.equal` A.seq_of_array_pcm_carrier v); + A.array_pcm_carrier_of_seq_of_array_pcm_carrier v; + A.pts_to_intro_from_base (model_array_of_array a w) v (seq_of_base_array0 v); + let p = vpattern_replace (fun p -> HR.pts_to _ p _) in + rewrite (HR.pts_to _ _ _) (HR.pts_to (array_ptr_of a).ar_base p w); + array_pts_to_intro a (seq_of_base_array0 v) w _ () + +let ghost_array_of_base_focus + #_ #_ #_ #_ #td #v r a += ghost_array_of_base_focus' r a + +#pop-options + +let ghost_array_of_base + #_ #tn #_ #n #td #v r += + let al : array_ref td = { + ar_is_null = false; + ar_base_size = n; + ar_base = r; + ar_offset = 0sz; + ar_prf = (); + } + in + let a : (a: Ghost.erased (array td) { has_array_of_base r a }) = (| al, Ghost.hide (Ghost.reveal n) |) in + ghost_array_of_base_focus r a; + a + +[@@noextract_to "krml"] // primitive +let array_of_base0 + (#t: Type) + (#opened: _) + (#n: Ghost.erased array_size_t) + (#td: typedef t) + (#v: Ghost.erased (base_array_t' t n)) + (r: ref (base_array1 td n)) +: STAtomicBase (array td) false opened Unobservable + (pts_to r v) + (fun a -> array_pts_to a (seq_of_base_array0 v)) + (True) + (fun a -> has_array_of_base' r a) += + let al : array_ref td = { + ar_is_null = false; + ar_base_size = n; + ar_base = r; + ar_offset = 0sz; + ar_prf = (); + } + in + let a : (a: array td { has_array_of_base' r a }) = (| al, Ghost.hide (Ghost.reveal n) |) in + ghost_array_of_base_focus' r a; + return a + +let array_ref_of_base + #_ #tn #_ #n #td #v r += + let ar = array_of_base0 r in + let a : array_ref td = array_ptr_of ar in + return a + +#push-options "--z3rlimit 64" +#restart-solver + +let unarray_of_base0 + (#t: Type) + (#opened: _) + (#n: array_size_t) + (#td: typedef t) + (#v: Ghost.erased (Seq.seq t)) + (r: ref (base_array1 td n)) + (a: array td) +: STGhost (Ghost.erased (base_array_t' t n)) opened + (array_pts_to a v) + (fun v' -> pts_to r v') + ( + has_array_of_base' r a + ) + (fun v' -> Ghost.reveal v `Seq.equal` seq_of_base_array0 v') += let ba = array_pts_to_elim a v in + let _ = gen_elim () in + let p = vpattern_replace (fun p -> HR.pts_to _ p _) in + rewrite (HR.pts_to _ _ _) (HR.pts_to r p ba); + let m = model_array_of_array a ba in + rewrite (A.pts_to _ _) (A.pts_to m v); + let y : Ghost.erased (A.array_pcm_carrier t m.base_len) = A.pts_to_elim_to_base m v in + let rr : R.ref ba.base (base_array1 td n).pcm = coerce_eq () m.base in + let y' : Ghost.erased (base_array_t' t n) = Ghost.hide (Ghost.reveal y) in + rewrite (R.pts_to _ _) (r_pts_to rr y'); + pts_to_intro r p ba rr y'; + y' + +#pop-options + +let unarray_of_base + #t #tn #_ #n #td #v r a += unarray_of_base0 r a + +[@@ __reduce__ ] +let freeable_array0 + (#t: Type) (#td: typedef t) (a: array td) +: Tot vprop += freeable #_ #(base_array1 #t td (array_ptr_of a).ar_base_size) (array_ptr_of a).ar_base `star` + pure (has_array_of_base' (array_ptr_of a).ar_base a) + +let freeable_array + a += freeable_array0 a + +let array_ptr_alloc + #t td sz += let base = alloc (base_array1 td sz) in + if is_null base + then begin + noop (); + let a = null_array_ptr td in + let ar : array_or_null td = (| a, Ghost.hide 0sz |) in + rewrite (pts_to_or_null _ _) (array_pts_to_or_null ar (seq_of_base_array0 (uninitialized (base_array1 td sz)))); + rewrite (freeable_or_null _) (freeable_or_null_array ar); + return a + end else begin + noop (); + let sq: squash (~ (base == null _)) = () in + noop (); + rewrite (pts_to_or_null _ _) (pts_to base (uninitialized (base_array1 td sz))); + let ar : array td = array_of_base0 base in + rewrite (array_pts_to ar _) (array_pts_to_or_null ar (seq_of_base_array0 (uninitialized (base_array1 td sz)))); + let a = array_ptr_of ar in + rewrite (freeable_or_null _) (freeable #_ #(base_array1 #t td (array_ptr_of ar).ar_base_size) (array_ptr_of ar).ar_base); + rewrite (freeable_array0 ar) (freeable_or_null_array ar); + return a + end + +#push-options "--z3rlimit 16" +#restart-solver + +let full_seq_seq_of_base_array' + (#t: Type0) (td: typedef t) (#n: array_size_t) + (b: base_array_t' t n) +: Lemma + (ensures (full_seq td (seq_of_base_array0 b) <==> full (base_array1 td n) b)) += assert (forall (i: base_array_index_t n) . base_array_index' b i == Seq.index (seq_of_base_array0 b) (SZ.v i)) + +let array_ref_free + #t #td #s a len += rewrite (freeable_array _) (freeable_array0 (| a, len |)); + elim_pure _; + let len0 : Ghost.erased array_size_t = Ghost.hide (Ghost.reveal len) in + let r : ref (base_array1 td len0) = a.ar_base in + array_pts_to_length _ _; + let s' = unarray_of_base0 r (| a, len |) in + full_seq_seq_of_base_array' td s'; + rewrite (pts_to _ _) (pts_to r s'); + rewrite (freeable _) (freeable r); + free r + +#pop-options + +(* +let has_array_of_ref + r a += TD.type_of_token (array_ptr_of a).ar_base_size_token == unit /\ + model_array_of_array a == A.g_array_of_ref (coerce _ (Some?.v r).ref) + +let has_array_of_ref_inj + r a1 a2 += TD.type_of_token_inj (array_ptr_of a1).ar_base_size_token (array_ptr_of a2).ar_base_size_token; + TD.type_of_token_inj (Some?.v (array_ptr_of a1).ar_base).dest (Some?.v (array_ptr_of a2).ar_base).dest + +let ghost_array_of_ref_focus + #t #_ #td #v r a += let mr : R.ref td.pcm = (Some?.v r).ref in + rewrite_slprop (pts_to _ _) (R.pts_to mr v) (fun _ -> ()); + let ma = A.ghost_array_of_ref mr in + rewrite_slprop (A.pts_to _ _) (array_pts_to _ _) (fun _ -> ()) + +let ghost_array_of_ref + #t #_ #td #v r += let mr : R.ref td.pcm = (Some?.v r).ref in + let ma = A.g_array_of_ref mr in + let tok_unit = TD.get_token unit in + let tok_array = TD.get_token (A.array_pcm_carrier t 1sz) in + let ar = { + ar_base_size_token = tok_unit; + ar_base_size = 1sz; + ar_base = Some ({ + dest = tok_array; + typedef = base_array0 unit td 1sz; + ref = coerce _ ma.base; + }); + ar_offset = 0sz; + } + in + let res: (a: Ghost.erased (array td) { has_array_of_ref r a }) = Ghost.hide (| ar, Ghost.hide 1sz |) in + ghost_array_of_ref_focus r res; + res + +let array_ref_of_ref + #t #_ #td #v r += let mr : R.ref td.pcm = (Some?.v r).ref in + rewrite_slprop (pts_to _ _) (R.pts_to mr v) (fun _ -> ()); + let ma = A.array_of_ref mr in + let tok_unit = TD.get_token unit in + let tok_array = TD.get_token (A.array_pcm_carrier t 1sz) in + let res = { + ar_base_size_token = tok_unit; + ar_base_size = 1sz; + ar_base = Some ({ + dest = tok_array; + typedef = base_array0 unit td 1sz; + ref = coerce _ ma.base; + }); + ar_offset = 0sz; + } + in + rewrite_slprop (A.pts_to _ _) (array_pts_to _ _) (fun _ -> ()); + return res + +let unarray_of_ref = magic () +*) + +[@@noextract_to "krml"] +let array_index_as_base_array_index_marker + (index: SZ.t) + (base_index: SZ.t) +: Tot SZ.t += base_index + +[@@__reduce__] +let has_array_cell0 + (#t: Type) + (#td: typedef t) + (a: array td) + (i: SZ.t) + (r: ref td) +: Tot vprop += exists_ (fun (j: SZ.t) -> + has_base_array_cell1 (array_ptr_of a).ar_base (array_index_as_base_array_index_marker i j) r `star` + pure ( + SZ.v j == SZ.v ((array_ptr_of a).ar_offset) + SZ.v i /\ + SZ.v i < SZ.v (dsnd a) + ) + ) + +let has_array_cell1 + (#t: Type) + (#td: typedef t) + (a: array td) + (i: SZ.t) + (r: ref td) +: Tot vprop += has_array_cell0 a i r + +let has_array_cell + a i r += has_array_cell0 a i r + +let has_array_cell_post + a i r += rewrite (has_array_cell a i r) (has_array_cell0 a i r); + let _ = gen_elim () in + rewrite (has_array_cell0 a i r) (has_array_cell a i r) + +let has_array_cell_has_base_array_cell + a i r br += rewrite (has_array_cell a i r) (has_array_cell0 a i r); + let _ = gen_elim () in + let j = vpattern_replace_erased (fun j -> has_base_array_cell1 _ j r) in + rewrite (has_base_array_cell1 _ _ _) (has_base_array_cell br j r); + j + +let has_base_array_cell_has_array_cell + a i r br += let j : Ghost.erased SZ.t = Ghost.hide (i `SZ.sub` (array_ptr_of a).ar_offset) in + rewrite (has_base_array_cell br i r) (has_base_array_cell1 (array_ptr_of a).ar_base (array_index_as_base_array_index_marker j i) r); + rewrite (has_array_cell0 a j r) (has_array_cell a j r); + j + +let has_array_cell_inj + #_ #_ #td a i r1 r2 += has_array_cell_post a i r1; + let br : ref (base_array0 unit (* dummy *) td (array_ref_base_size (array_ptr_of a))) = (array_ptr_of a).ar_base in + let j1 = has_array_cell_has_base_array_cell a i r1 br in + let j2 = has_array_cell_has_base_array_cell a i r2 br in + vpattern_rewrite (fun j2 -> has_base_array_cell _ j2 r2) j1; + has_base_array_cell_inj br j1 r1 r2; + let _ = has_base_array_cell_has_array_cell a j1 r1 br in + vpattern_rewrite (fun i -> has_array_cell _ i r1) i; + let _ = has_base_array_cell_has_array_cell a j1 r2 br in + vpattern_rewrite (fun i -> has_array_cell _ i r2) i + + +#restart-solver +let struct_field_eq_cell + (#t: Type) + (td: typedef t) + (n: array_size_t) + (k: base_array_index_t n) +: Lemma + (Steel.ST.C.Model.Struct.struct_field (struct_field_pcm (base_array_fd td n)) k == A.cell td.pcm n k) += // assert_norm (A.array_domain n == base_array_index_t n); + Steel.ST.C.Model.Struct.struct_field_ext #(A.array_domain n) #(A.array_range t n) (struct_field_pcm (base_array_fd td n)) (A.array_elements_pcm td.pcm n) (fun _ -> ()) k + +(* +#push-options "--split_queries --z3rlimit 16" + +#restart-solver +let has_array_cell_array_of_ref + #_ #td r a += assert_norm (SZ.v 0sz == 0); + assert_norm (SZ.v 1sz == 1); + A.ref_of_array_of_ref (Some?.v r).ref; + A.ref_of_array_of_ref_base (Some?.v r).ref; + assert (Ghost.reveal (dsnd a) == 1sz); + assert ((array_ptr_of a).ar_offset == 0sz); + struct_field_eq_cell td 1sz 0sz; + assert (has_base_array_cell0 (array_ref_base (array_ptr_of a)) (array_ref_offset (array_ptr_of a) `SZ.add` 0sz) r) + +#pop-options +*) + +let has_struct_field_gen + (#field_t: eqtype) + (fields: field_description_gen_t field_t) + (r: ref0_v) + (field: field_t) + (r': ref0_v) +: GTot prop += r'.base == r.base /\ + r.t == struct_t1 fields /\ + r.td == struct1 fields /\ + r'.t == fields.fd_type field /\ + r'.td == fields.fd_typedef field /\ + r'.ref == coerce_eq () (R.ref_focus r.ref (Steel.ST.C.Model.Struct.struct_field (struct_field_pcm fields) field)) + + +let has_struct_field1_intro + (#opened: _) + (#field_t: eqtype) + (#fields: field_description_gen_t field_t) + (r: ref (struct1 fields)) + (field: field_t) + (r': ref (fields.fd_typedef field)) + (p: P.perm) + (w: ref0_v) + (p': P.perm) + (w': ref0_v) + () +: STGhost unit opened + (HR.pts_to r p w `star` HR.pts_to r' p' w') + (fun _ -> + has_struct_field1 r field r' + ) + ( + has_struct_field_gen fields w field w' + ) + (fun _ -> True) += noop (); + rewrite + (has_struct_field0 r field r') + (has_struct_field1 r field r') + +let has_array_cell_drop + (#opened: _) + (#t: Type) + (#td: typedef t) + (a: array td) + (#p': P.perm) + (#b': ref0_v) + (i: SZ.t) + (r: ref td) +: STGhostT unit opened + (has_array_cell1 a i r `star` + HR.pts_to r p' b' + ) + (fun _ -> has_array_cell1 a i r) += rewrite (has_array_cell1 a i r) (has_array_cell0 a i r); + let _ = gen_elim () in + let j = vpattern_replace (fun j -> has_base_array_cell1 _ j _) in + rewrite (has_base_array_cell1 (array_ptr_of a).ar_base j r) (has_base_array_cell0 (array_ptr_of a).ar_base j r); + let _ = gen_elim () in + let j' : base_array_index_t' (array_ptr_of a).ar_base_size = vpattern_replace (fun j' -> has_base_array_cell_as_struct_field _ _ j' _) in + rewrite (has_base_array_cell_as_struct_field (array_ptr_of a).ar_base j j' r) (has_struct_field0 #(base_array_index_t' (array_ptr_of a).ar_base_size) #(base_array_fd td (array_ptr_of a).ar_base_size) (array_ptr_of a).ar_base j' r); + let _ = gen_elim () in + HR.gather p' r; + has_struct_field1_intro + #_ #(base_array_index_t' (array_ptr_of a).ar_base_size) #(base_array_fd td (array_ptr_of a).ar_base_size) (array_ptr_of a).ar_base j' r _ _ _ _ (); + rewrite + (has_struct_field1 #(base_array_index_t' (array_ptr_of a).ar_base_size) #(base_array_fd td (array_ptr_of a).ar_base_size) (array_ptr_of a).ar_base j' r) + (has_base_array_cell_as_struct_field (array_ptr_of a).ar_base j j' r); + rewrite + (has_base_array_cell0 (array_ptr_of a).ar_base j r) + (has_base_array_cell1 (array_ptr_of a).ar_base (array_index_as_base_array_index_marker i j) r); + rewrite + (has_array_cell0 a i r) + (has_array_cell a i r) + +let has_array_cell_elim + (#opened: _) + (#t: Type) + (#td: typedef t) + (#p: P.perm) + (a: array td) + (#b: ref0_v) + (i: SZ.t) + (r: ref td) +: STGhost (Ghost.erased (ref0_v)) opened + (has_array_cell1 a i r `star` + HR.pts_to (array_ptr_of a).ar_base p b + ) + (fun b' -> has_array_cell1 a i r `star` + exists_ (fun p -> exists_ (fun p' -> + HR.pts_to (array_ptr_of a).ar_base p b `star` + HR.pts_to r p' b' + ))) + True + (fun b' -> + model_array_of_array_precond a b /\ + b'.base == b.base /\ + b'.t == t /\ + b'.td == td /\ + begin let ar = model_array_of_array a b in + SZ.v i < SZ.v ar.len /\ + b'.ref == R.ref_focus (A.ref_of_array ar) (A.cell td.pcm ar.len i) + end + ) += + rewrite (has_array_cell1 a i r) (has_array_cell0 a i r); + let _ = gen_elim () in + let j = vpattern_replace (fun j -> has_base_array_cell1 _ j _) in + rewrite (has_base_array_cell1 (array_ptr_of a).ar_base j r) (has_base_array_cell0 (array_ptr_of a).ar_base j r); + let _ = gen_elim () in + let j' : base_array_index_t' (array_ptr_of a).ar_base_size = vpattern_replace (fun j' -> has_base_array_cell_as_struct_field _ _ j' _) in + rewrite (has_base_array_cell_as_struct_field (array_ptr_of a).ar_base j j' r) (has_struct_field0 #(base_array_index_t' (array_ptr_of a).ar_base_size) #(base_array_fd td (array_ptr_of a).ar_base_size) (array_ptr_of a).ar_base j' r); + let _ = gen_elim () in + hr_gather b (array_ptr_of a).ar_base; + HR.share r; + HR.share (array_ptr_of a).ar_base; + has_struct_field1_intro #_ #(base_array_index_t' (array_ptr_of a).ar_base_size) #(base_array_fd td (array_ptr_of a).ar_base_size) (array_ptr_of a).ar_base j' r _ _ _ _ (); + rewrite (has_struct_field1 #(base_array_index_t' (array_ptr_of a).ar_base_size) #(base_array_fd td (array_ptr_of a).ar_base_size) (array_ptr_of a).ar_base j' r) (has_base_array_cell_as_struct_field (array_ptr_of a).ar_base j j' r); + rewrite + (has_base_array_cell0 (array_ptr_of a).ar_base j r) + (has_base_array_cell1 (array_ptr_of a).ar_base (array_index_as_base_array_index_marker i j) r); + rewrite + (has_array_cell0 a i r) + (has_array_cell a i r); + A.ref_of_array_eq (model_array_of_array a b) i; + struct_field_eq_cell td (array_ptr_of a).ar_base_size j'; + let b' = vpattern_replace_erased (HR.pts_to r _) in + noop (); + b' + +let ghost_array_cell_focus + #_ #_ #td #s a i r += rewrite (array_pts_to a s) (array_pts_to0 a s); + let _ = gen_elim () in + let b = vpattern_replace (HR.pts_to (array_ptr_of a).ar_base _) in + let r' = has_array_cell_elim a i r in + let _ = gen_elim () in + let _ = A.g_focus_cell _ _ i () in + pts_to_intro_rewrite r _ (Seq.index s (SZ.v i)); + rewrite (A.pts_to _ _) (A.pts_to (model_array_of_array a b) (Seq.upd s (SZ.v i) (unknown td))); + array_pts_to_intro a (Seq.upd s (SZ.v i) (unknown td)) _ _ () + +let has_array_cell_intro + (#opened: _) + (#t: Type) + (#td: typedef t) + (#p: P.perm) + (a: array td) + (#b: ref0_v) + (#p': P.perm) + (#b': ref0_v) + (i: SZ.t) + (r: ref td) +: STGhost unit opened + (HR.pts_to (array_ptr_of a).ar_base p b `star` + HR.pts_to r p' b' + ) + (fun _ -> has_array_cell1 a i r) + ( + model_array_of_array_precond a b /\ + begin let ar = model_array_of_array a b in + SZ.v i < SZ.v ar.len /\ + b'.base == b.base /\ + b'.t == t /\ + b'.td == td /\ + b'.ref == R.ref_focus (A.ref_of_array ar) (A.cell td.pcm ar.len i) + end + ) + (fun _ -> True) += + A.ref_of_array_eq (model_array_of_array a b) i; + let j : base_array_index_t' (array_ptr_of a).ar_base_size = (array_ptr_of a).ar_offset `SZ.add` i in + struct_field_eq_cell td (array_ptr_of a).ar_base_size j; + has_struct_field1_intro #_ #(base_array_index_t' (array_ptr_of a).ar_base_size) #(base_array_fd td (array_ptr_of a).ar_base_size) (array_ptr_of a).ar_base j r _ _ _ _ (); + rewrite (has_struct_field1 #(base_array_index_t' (array_ptr_of a).ar_base_size) #(base_array_fd td (array_ptr_of a).ar_base_size) (array_ptr_of a).ar_base j r) (has_base_array_cell_as_struct_field (array_ptr_of a).ar_base j j r); + rewrite + (has_base_array_cell0 (array_ptr_of a).ar_base j r) + (has_base_array_cell1 (array_ptr_of a).ar_base (array_index_as_base_array_index_marker i j) r); + rewrite + (has_array_cell0 a i r) + (has_array_cell a i r) + +let ghost_array_cell + #_ #_ #td #s a i += array_pts_to_length _ _; + let b = array_pts_to_elim a s in + let _ = gen_elim () in + HR.share _; + array_pts_to_intro a s _ _ (); + let ar = model_array_of_array a b in + let b' = { + t = _; + td = td; + base = b.base; + ref = R.ref_focus (A.ref_of_array ar) (A.cell td.pcm ar.len i); + } + in + let ghr = GHR.alloc b' in + GHR.reveal_pts_to ghr P.full_perm b'; + let hr = GHR.reveal_ref ghr in + rewrite_equiv (GHR.pts_to _ _ _) (HR.pts_to hr P.full_perm b'); + HR.pts_to_not_null hr; + let r : (r: Ghost.erased (ref td) { SZ.v i < Seq.length s /\ Seq.length s == SZ.v (dsnd a) }) = hr in + vpattern_rewrite (fun hr -> HR.pts_to hr P.full_perm b') r; + has_array_cell_intro a i r; + let _ = ghost_array_cell_focus a i r in + noop (); + r + +[@@ noextract_to "krml"] +let array_cell0 + (#t: Type) + (#td: typedef t) + (#s: Ghost.erased (Seq.seq t)) + (a: array td) + (i: SZ.t) +: ST (r: ref td { SZ.v i < Seq.length s /\ Seq.length s == SZ.v (dsnd a) }) + (array_pts_to a s) + (fun r -> array_pts_to a (Seq.upd s (SZ.v i) (unknown td)) `star` pts_to r (Seq.index s (SZ.v i)) `star` has_array_cell a i r) + ( + (SZ.v i < Seq.length s \/ SZ.v i < SZ.v (dsnd a)) + ) + (fun _ -> True) += array_pts_to_length _ _; + let _ = array_pts_to_elim a s in + let _ = gen_elim () in + HR.share _; + array_pts_to_intro a s _ _ (); + let b = HR.read (array_ptr_of a).ar_base in + vpattern_rewrite (HR.pts_to (array_ptr_of a).ar_base _) b; + let ar = model_array_of_array a b in + A.ref_of_array_eq ar i; + let b' = { + t = _; + td = td; + base = b.base; + ref = R.ref_focus ar.base (A.cell td.pcm ar.base_len (ar.offset `SZ.add` i)); + } + in + let hr = HR.alloc b' in + HR.pts_to_not_null hr; + let r : (r: ref td { SZ.v i < Seq.length s /\ Seq.length s == SZ.v (dsnd a) }) = hr in + vpattern_rewrite (fun hr -> HR.pts_to hr P.full_perm b') r; + has_array_cell_intro a i r; + let _ = ghost_array_cell_focus a i r in + noop (); + return r + +let array_ref_cell + #_ #td #s a len i += let r0 : (r: ref td { SZ.v i < Seq.length s /\ Seq.length s == SZ.v (dsnd ((| a, len |) <: array td)) }) = array_cell0 _ _ in + let r : (r: ref td { SZ.v i < Seq.length s /\ Seq.length s == SZ.v len }) = r0 in + vpattern_rewrite (fun r -> pts_to r _) r; + vpattern_rewrite (has_array_cell _ _) r; + noop (); + return r + +let ar_unfocus_cell + (#opened: _) + (#base_t #base_t': Type) + (#t: Type) + (#p: pcm t) + (r: A.array base_t p) + (s: Seq.seq t) + (i: SZ.t) + (r': R.ref base_t' p) + (v: t) + (sq: squash (SZ.v i < SZ.v r.len /\ SZ.v i < Seq.length s)) +: STGhost unit opened + (A.pts_to r s `star` R.pts_to r' v) + (fun _ -> A.pts_to r (Seq.upd s (SZ.v i) v)) + ( + base_t' == base_t /\ + r' == R.ref_focus (A.ref_of_array r) (A.cell p r.len i) /\ + Seq.index s (SZ.v i) == one p + ) + (fun _ -> True) += let r1' : R.ref base_t p = coerce_eq () r' in + rewrite (R.pts_to r' v) (R.pts_to r1' v); + A.unfocus_cell r s i r1' v () + +let unarray_cell + #_ #_ #td #s #v a i r += array_pts_to_length _ _; + let b = array_pts_to_elim a s in + let _ = gen_elim () in + let w = has_array_cell_elim a i r in + let _ = gen_elim () in + rewrite (pts_to r v) (pts_to0 r v); + let _ = gen_elim () in + hr_gather (Ghost.reveal w) r; + ar_unfocus_cell _ _ i _ _ (); + rewrite (A.pts_to _ _) (A.pts_to (model_array_of_array a b) (Seq.upd s (SZ.v i) v)); + array_pts_to_intro a (Seq.upd s (SZ.v i) v) _ _ (); + has_array_cell_drop _ _ _ + +#push-options "--split_queries --z3rlimit 16" + +let t_array_ref_shift + (#t: Type) + (#td: typedef t) + (a: array_ref td) + (i: SZ.t) +: Pure (array_ref td) + (requires (SZ.v (array_ref_offset a) + SZ.v i <= SZ.v (array_ref_base_size a))) + (ensures (fun y -> + array_ref_base_size y == array_ref_base_size a /\ + (forall ty r . has_array_ref_base a #ty r ==> has_array_ref_base y #ty (coerce_eq () r)) /\ + array_ref_offset y == array_ref_offset a `SZ.add` i + )) += { + a with + ar_offset = a.ar_offset `SZ.add` i + } + +let array_ref_shift + a i += t_array_ref_shift a i + +let array_ref_shift_zero + a += () + +let array_ref_shift_assoc + a i1 i2 += () + +let ghost_array_split + #_ #_ #td #s a i += array_pts_to_length _ _; + let sq : squash (SZ.v i <= SZ.v (dsnd a) /\ Seq.length s == SZ.v (dsnd a)) = () in + let br = array_pts_to_elim a s in + let _ = gen_elim () in + A.g_split _ _ i (); + HR.share _; + let p = vpattern_replace (fun p -> HR.pts_to _ p _ `star` HR.pts_to _ p _) in + rewrite (HR.pts_to _ _ _) (HR.pts_to (array_ptr_of (array_split_l a i)).ar_base p br); + rewrite (A.pts_to _ (Seq.slice s 0 _)) (A.pts_to (model_array_of_array (array_split_l a i) br) (Seq.slice s 0 (SZ.v i))); + array_pts_to_intro (array_split_l a i) (Seq.slice s 0 (SZ.v i)) _ _ (); + rewrite (HR.pts_to _ _ _) (HR.pts_to (array_ptr_of (array_split_r a i)).ar_base p br); + rewrite (A.pts_to _ _) (A.pts_to (model_array_of_array (array_split_r a i) br) (Seq.slice s (SZ.v i) (Seq.length s))); + array_pts_to_intro (array_split_r a i) (Seq.slice s (SZ.v i) (Seq.length s)) _ _ (); + sq + +let t_array_split_r + (#t: Type) + (#td: typedef t) + (a: array td) + (i: SZ.t) +: Pure (array td) + (requires (SZ.v i <= SZ.v (dsnd a))) + (ensures (fun _ -> True)) += let (| al, len |) = a in + (| t_array_ref_shift al i, Ghost.hide (len `SZ.sub` i) |) + +let array_ref_split + #_ #td #s al len i += let _ = ghost_array_split (| al, len |) i in + let ar: (ar: array_ref td { SZ.v i <= SZ.v len /\ Seq.length s == SZ.v len}) = t_array_ref_shift al i in + return ar + +let hr_gather_by_perm + (#opened: _) + (#t1: Type) + (#r1: HR.ref t1) + (#v1: t1) + (#t2: Type) + (#r2: HR.ref t2) + (#v2: t2) + (p1: P.perm) + (p2: P.perm) +: STGhost unit opened + (HR.pts_to r1 p1 v1 `star` HR.pts_to r2 p2 v2) + (fun _ -> HR.pts_to r1 (p1 `P.sum_perm` p2) v1) + (t1 == t2 /\ + r1 == r2) + (fun _ -> + t1 == t2 /\ + r1 == r2 /\ + v1 == v2) += rewrite (HR.pts_to r2 p2 v2) (HR.pts_to r1 p2 (coerce_eq () v2)); + HR.gather p2 r1 + +let ar_join + (#opened: _) + (#base_t #base_tl #base_tr: Type) + (#t: Type) + (#p: pcm t) + (a: A.array base_t p) + (i: SZ.t) + (al: A.array base_tl p) + (ar: A.array base_tr p) + (sl0 sr0: Seq.seq t) +: STGhost unit opened + (A.pts_to al sl0 `star` A.pts_to ar sr0) + (fun _ -> A.pts_to a (sl0 `Seq.append` sr0)) + ( + SZ.v i <= SZ.v a.len /\ + base_t == base_tl /\ + base_t == base_tr /\ + al == A.split_l a i /\ + ar == A.split_r a i + ) + (fun _ -> True) += let al' : A.array base_t p = coerce_eq () al in + let ar' : A.array base_t p = coerce_eq () ar in + rewrite (A.pts_to al sl0) (A.pts_to al' sl0); + rewrite (A.pts_to ar sr0) (A.pts_to ar' sr0); + A.join a i al' ar' _ _ + +let array_join + #_ #_ #td #sl #sr a al ar i += let br = array_pts_to_elim al sl in + let _ = gen_elim () in + let pl = vpattern_replace (fun p -> HR.pts_to _ p _) in + vpattern_rewrite (fun x -> HR.pts_to x pl _) (array_ptr_of a).ar_base; + let _ = array_pts_to_elim ar sr in + let _ = gen_elim () in + let pr = vpattern_replace (fun pr -> HR.pts_to _ pl _ `star` HR.pts_to _ pr _) in + vpattern_rewrite (fun x -> HR.pts_to x pr _) (array_ptr_of a).ar_base; + hr_gather_by_perm pl pr; + ar_join (model_array_of_array a br) i _ _ sl sr; + array_pts_to_intro a (sl `Seq.append` sr) _ _ () + +let ar_share + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: A.array base_t p) + (s s1 s2: Seq.seq t) + (prf: ( + (i: nat) -> + Lemma + (requires (i < Seq.length s /\ i < Seq.length s1 /\ i < Seq.length s2)) + (ensures ( + i < Seq.length s /\ i < Seq.length s1 /\ i < Seq.length s2 /\ + composable p (Seq.index s1 i) (Seq.index s2 i) /\ + op p (Seq.index s1 i) (Seq.index s2 i) == Seq.index s i + )) + )) +: STGhost unit opened + (A.pts_to r s) + (fun _ -> A.pts_to r s1 `star` A.pts_to r s2) + ( + Seq.length s1 == Seq.length s /\ + Seq.length s2 == Seq.length s + ) + (fun _ -> True) += Classical.forall_intro (Classical.move_requires prf); + A.share r s s1 s2 + +let mk_fraction_seq_split_gen + #_ #_ #td r v p p1 p2 += let br = array_pts_to_elim r (mk_fraction_seq td v p) in + let _ = gen_elim () in + rewrite (A.pts_to _ _) (A.pts_to (model_array_of_array r br) (mk_fraction_seq td v p)); + ar_share _ _ (mk_fraction_seq td v p1) (mk_fraction_seq td v p2) (fun i -> + td.mk_fraction_split (Seq.index v i) p1 p2; + td.mk_fraction_join (Seq.index v i) p1 p2 + ); + HR.share _; + array_pts_to_intro r (mk_fraction_seq td v p1) _ _ (); + array_pts_to_intro r (mk_fraction_seq td v p2) _ _ () + +let ar_gather + (#opened: _) + (#base_t: Type) + (#t: Type) + (#p: pcm t) + (r: A.array base_t p) + (s s1 s2: Seq.seq t) + (prf: ( + (i: nat) -> + Lemma + (requires ( + i < Seq.length s /\ i < Seq.length s1 /\ i < Seq.length s2 /\ + composable p (Seq.index s1 i) (Seq.index s2 i) + )) + (ensures ( + i < Seq.length s /\ i < Seq.length s1 /\ i < Seq.length s2 /\ + composable p (Seq.index s1 i) (Seq.index s2 i) /\ + op p (Seq.index s1 i) (Seq.index s2 i) == Seq.index s i + )) + )) +: STGhost unit opened + (A.pts_to r s1 `star` A.pts_to r s2) + (fun _ -> A.pts_to r s) + ( + Seq.length s1 == Seq.length s /\ + Seq.length s2 == Seq.length s + ) + (fun _ -> True) += Classical.forall_intro (Classical.move_requires prf); + A.gather r s s1 s2 + +let mk_fraction_seq_join + #_ #_ #td r v p1 p2 += let br = array_pts_to_elim r (mk_fraction_seq td v p1) in + let _ = gen_elim () in + rewrite (A.pts_to _ _) (A.pts_to (model_array_of_array r br) (mk_fraction_seq td v p1)); + let _ = array_pts_to_elim r (mk_fraction_seq td v p2) in + let _ = gen_elim () in + hr_gather (Ghost.reveal br) (array_ptr_of r).ar_base; + rewrite (A.pts_to _ (mk_fraction_seq _ _ p2)) (A.pts_to (model_array_of_array r br) (mk_fraction_seq td v p2)); + ar_gather _ (mk_fraction_seq td v (p1 `P.sum_perm` p2)) (mk_fraction_seq td v p1) (mk_fraction_seq td v p2) (fun i -> + td.mk_fraction_join (Seq.index v i) p1 p2 + ); + array_pts_to_intro r (mk_fraction_seq td v (p1 `P.sum_perm` p2)) _ _ () + +#restart-solver + +let array_fractional_permissions_theorem + (#opened: _) + (#t: Type) + (#td: typedef t) + (v1: Seq.seq t { fractionable_seq td v1 }) + (v2: Seq.seq t { fractionable_seq td v2 }) + (p1 p2: P.perm) + (r: array td) +: STGhost unit opened + (array_pts_to r (mk_fraction_seq td v1 p1) `star` array_pts_to r (mk_fraction_seq td v2 p2)) + (fun _ -> array_pts_to r (mk_fraction_seq td v1 p1) `star` array_pts_to r (mk_fraction_seq td v2 p2)) + (full_seq td v1 /\ full_seq td v2) + (fun _ -> v1 == v2 /\ (array_length r > 0 ==> (p1 `P.sum_perm` p2) `P.lesser_equal_perm` P.full_perm)) += array_pts_to_length r (mk_fraction_seq td v1 p1); + array_pts_to_length r (mk_fraction_seq td v2 p2); + let br = array_pts_to_elim r (mk_fraction_seq td v1 p1) in + let _ = gen_elim () in + rewrite (A.pts_to _ _) (A.pts_to (model_array_of_array r br) (mk_fraction_seq td v1 p1)); + let _ = array_pts_to_elim r (mk_fraction_seq td v2 p2) in + let _ = gen_elim () in + hr_gather (Ghost.reveal br) (array_ptr_of r).ar_base; + rewrite (A.pts_to _ (mk_fraction_seq _ _ p2)) (A.pts_to (model_array_of_array r br) (mk_fraction_seq td v2 p2)); + let _ = A.gather_exists _ (mk_fraction_seq td v1 p1) (mk_fraction_seq td v2 p2) in + let prf + (i: nat) + : Lemma + (requires (i < array_length r)) + (ensures ( + i < array_length r /\ + Seq.index v1 i == Seq.index v2 i /\ + (p1 `P.sum_perm` p2) `P.lesser_equal_perm` P.full_perm + )) + = td.mk_fraction_full_composable (Seq.index v1 i) p1 (Seq.index v2 i) p2 + in + Classical.forall_intro (Classical.move_requires prf); + assert (v1 `Seq.equal` v2); + A.share _ _ (mk_fraction_seq td v1 p1) (mk_fraction_seq td v2 p2); + HR.share _; + array_pts_to_intro r (mk_fraction_seq td v1 p1) _ _ (); + array_pts_to_intro r (mk_fraction_seq td v2 p2) _ _ () diff --git a/src/proofs/steelc/Steel.ST.C.Types.Base.fst b/src/proofs/steelc/Steel.ST.C.Types.Base.fst new file mode 100644 index 000000000..9aedbfeb6 --- /dev/null +++ b/src/proofs/steelc/Steel.ST.C.Types.Base.fst @@ -0,0 +1,1024 @@ +module Steel.ST.C.Types.Base + +open Steel.C.Model.PCM +open Steel.ST.GenElim + +#set-options "--smtencoding.elim_box true --smtencoding.l_arith_repr native --smtencoding.nl_arith_repr native" + +let prod_perm + p1 p2 += let w = let open FStar.Real in P.MkPerm?.v p1 *. P.MkPerm?.v p2 in + assert (let open FStar.Real in (p2 `P.lesser_equal_perm` P.full_perm ==> w <=. P.MkPerm?.v p1 *. 1.0R)); + P.MkPerm w + +noeq +type typedef (t: Type0) : Type0 = { + pcm: pcm t; + fractionable: (t -> GTot bool); + mk_fraction: ( + (x: t) -> + (p: P.perm) -> + Pure t + (requires (fractionable x)) + (ensures (fun y -> p `P.lesser_equal_perm` P.full_perm ==> fractionable y)) + ); + mk_fraction_full: ( + (x: t) -> + Lemma + (requires (fractionable x)) + (ensures (fractionable x /\ mk_fraction x P.full_perm == x)) + ); + mk_fraction_compose: ( + (x: t) -> + (p1: P.perm) -> + (p2: P.perm) -> + Lemma + (requires (fractionable x /\ p1 `P.lesser_equal_perm` P.full_perm /\ p2 `P.lesser_equal_perm` P.full_perm)) + (ensures (mk_fraction (mk_fraction x p1) p2 == mk_fraction x (p1 `prod_perm` p2))) + ); + fractionable_one: squash (fractionable (one pcm)); + mk_fraction_one: ( + (p: P.perm) -> + Lemma + (mk_fraction (one pcm) p == one pcm) + ); + uninitialized: (y: t { + exclusive pcm y /\ + fractionable y /\ + p_refine pcm y + }); + mk_fraction_split: ( + (v: t) -> + (p1: P.perm) -> + (p2: P.perm) -> + Lemma + (requires (fractionable v /\ (p1 `P.sum_perm` p2) `P.lesser_equal_perm` P.full_perm)) + (ensures ( + composable pcm (mk_fraction v p1) (mk_fraction v p2) + )) + ); + mk_fraction_join: ( + (v: t) -> + (p1: P.perm) -> + (p2: P.perm) -> + Lemma + (requires ( + fractionable v /\ + composable pcm (mk_fraction v p1) (mk_fraction v p2) + )) + (ensures ( + op pcm (mk_fraction v p1) (mk_fraction v p2) == mk_fraction v (p1 `P.sum_perm` p2) + )) + ); + mk_fraction_eq_one: ( + (v: t) -> + (p: P.perm) -> + Lemma + (requires (fractionable v /\ mk_fraction v p == one pcm)) + (ensures (v == one pcm)) + ); + mk_fraction_full_composable: ( + (v1: t) -> + (p1: P.perm) -> + (v2: t) -> + (p2: P.perm) -> + Lemma + (requires ( + exclusive pcm v1 /\ p_refine pcm v1 /\ + exclusive pcm v2 /\ p_refine pcm v2 /\ + fractionable v1 /\ fractionable v2 /\ + composable pcm (mk_fraction v1 p1) (mk_fraction v2 p2) + )) + (ensures ( + v1 == v2 /\ + (p1 `P.sum_perm` p2) `P.lesser_equal_perm` P.full_perm + )) + ); +} + +let fractionable td x = td.fractionable x == true +let mk_fraction td x p = td.mk_fraction x p +let mk_fraction_full td x = td.mk_fraction_full x +let mk_fraction_compose td x p1 p2 = td.mk_fraction_compose x p1 p2 + +let full td v = exclusive td.pcm v /\ p_refine td.pcm v + +let uninitialized td = td.uninitialized + +let unknown td = one td.pcm + +let full_not_unknown + td v += () + +let mk_fraction_unknown td p = td.mk_fraction_one p +let mk_fraction_eq_unknown td v p = td.mk_fraction_eq_one v p + +module R = Steel.ST.C.Model.Ref + +noeq +type ref0_v : Type u#1 = { + base: Type0; + t: Type; + td: typedef t; + ref: R.ref base td.pcm; +} + +module HR = Steel.ST.HigherReference + +let void_ptr = HR.ref ref0_v +let void_null = HR.null + +let r_pts_to + (#a: Type u#0) (#b: Type u#b) (#p: pcm b) + (r: R.ref a p) (v: b) +: vprop += R.pts_to r v + +let pts_to_cond + (#t: Type) + (#td: typedef t) + (r: ptr td) + (w: ref0_v) + (r': R.ref w.base td.pcm) +: GTot prop += + t == w.t /\ + td == w.td /\ + r' == w.ref + +[@@__reduce__] +let pts_to0 + (#t: Type) + (#td: typedef t) + (r: ptr td) + (v: t) +: Tot vprop += exists_ (fun p -> exists_ (fun w -> exists_ (fun (r': R.ref w.base td.pcm) -> + HR.pts_to r p w `star` + r_pts_to r' v `star` + pure (pts_to_cond r w r') + ))) + +let pts_to r v = pts_to0 r v + +let pts_to_intro_precond + (#t: Type) + (#td: typedef t) + (r: ref td) + (w: ref0_v) + (#tbase: Type) + (r': R.ref tbase td.pcm) +: GTot prop += tbase == w.base /\ + t == w.t /\ + td == w.td /\ + r' == w.ref + +let pts_to_intro + (#opened: _) + (#t: Type) + (#td: typedef t) + (r: ref td) + (p: P.perm) + (w: ref0_v) + (#tbase: Type) + (r': R.ref tbase td.pcm) + (v: t) +: STGhost unit opened + (HR.pts_to r p w `star` R.pts_to r' v) + (fun _ -> pts_to r v) + (pts_to_intro_precond r w r') + (fun _ -> True) += let r2 : R.ref w.base td.pcm = coerce_eq () r' in + rewrite (R.pts_to r' v) (r_pts_to #w.base #t #td.pcm r2 v); + intro_pure (pts_to_cond r w r2); + intro_exists r2 (fun (r': R.ref w.base td.pcm) -> + HR.pts_to r p w `star` + r_pts_to r' v `star` + pure (pts_to_cond r w r') + ); + rewrite (pts_to0 r v) (pts_to r v) + +let is_null + p += let res = HR.is_null p in + return res + +[@@__reduce__] +let ref_equiv0 + (#t: Type) + (#td: typedef t) + (r1 r2: ref td) +: Tot vprop += exists_ (fun p1 -> exists_ (fun p2 -> exists_ (fun w -> + HR.pts_to r1 p1 w `star` + HR.pts_to r2 p2 w + ))) + +let ref_equiv + (#t: Type) + (#td: typedef t) + (r1 r2: ref td) +: Tot vprop += ref_equiv0 r1 r2 + +let ref_equiv_dup' + (#opened: _) + (#t: Type) + (#td: typedef t) + (r1 r2: ref td) +: STGhostT unit opened + (ref_equiv r1 r2) + (fun _ -> ref_equiv r1 r2 `star` ref_equiv r1 r2) += rewrite (ref_equiv r1 r2) (ref_equiv0 r1 r2); + let _ = gen_elim () in + HR.share r1; + HR.share r2; + rewrite (ref_equiv0 r1 r2) (ref_equiv r1 r2); + noop (); + rewrite (ref_equiv0 r1 r2) (ref_equiv r1 r2) + +let ref_equiv_sym' + (#opened: _) + (#t: Type) + (#td: typedef t) + (r1 r2: ref td) +: STGhostT unit opened + (ref_equiv r1 r2) + (fun _ -> ref_equiv r1 r2 `star` ref_equiv r2 r1) += ref_equiv_dup' r1 r2; + rewrite (ref_equiv r1 r2) (ref_equiv0 r1 r2); + let _ = gen_elim () in + noop (); + rewrite (ref_equiv0 r2 r1) (ref_equiv r2 r1) + +let hr_share (#a:Type) + (#uses:_) + (#p:P.perm) + (#v:a) + (r:HR.ref a) + : STGhostT unit uses + (HR.pts_to r p v) + (fun _ -> HR.pts_to r (P.half_perm p) v `star` HR.pts_to r (P.half_perm p) v) += HR.share #_ #_ #_ #v r + +let hr_gather + (#a:Type) + (#uses:_) + (#p0 #p1:P.perm) + (v0 #v1:a) + (r:HR.ref a) +: STGhost unit uses + (HR.pts_to r p0 v0 `star` HR.pts_to r p1 v1) + (fun _ -> HR.pts_to r (P.sum_perm p0 p1) v0) + (requires True) + (ensures fun _ -> v0 == v1) += HR.gather p1 r + +let ref_equiv_trans' + (#opened: _) + (#t: Type) + (#td: typedef t) + (r1 r2 r3: ref td) +: STGhostT unit opened + (ref_equiv r1 r2 `star` ref_equiv r2 r3) + (fun _ -> ref_equiv r1 r2 `star` ref_equiv r2 r3 `star` ref_equiv r1 r3) += rewrite (ref_equiv r1 r2) (ref_equiv0 r1 r2); + let _ = gen_elim () in + let w = vpattern_replace (fun w -> HR.pts_to r1 _ w `star` HR.pts_to r2 _ w) in + let p2 = vpattern_replace (fun p -> HR.pts_to r2 p _) in + rewrite (ref_equiv r2 r3) (ref_equiv0 r2 r3); + let _ = gen_elim () in + HR.pts_to_injective_eq #_ #_ #_ #_ #w #_ r2; + vpattern_rewrite (HR.pts_to r3 _) w; + hr_share r1; + hr_share r3; + HR.gather p2 r2; + hr_share r2; + noop (); + rewrite (ref_equiv0 r1 r2) (ref_equiv r1 r2); + rewrite (ref_equiv0 r2 r3) (ref_equiv r2 r3); + rewrite (ref_equiv0 r1 r3) (ref_equiv r1 r3) + +let hr_share_imbalance (#a:Type) + (#uses:_) + (#p:P.perm) + (#v:a) + (r:HR.ref a) + : STGhostT P.perm uses + (HR.pts_to r p v) + (fun p1 -> HR.pts_to r p1 v `star` exists_ (fun p2 -> HR.pts_to r p2 v)) += HR.share #_ #_ #_ #v r; + _ + +#set-options "--ide_id_info_off" + +let pts_to_equiv + r1 r2 v += rewrite (ref_equiv r1 r2) (ref_equiv0 r1 r2); + let _ = gen_elim () in + let w = vpattern_replace (fun w -> HR.pts_to r1 _ w `star` HR.pts_to r2 _ w) in + rewrite (pts_to r1 v) (pts_to0 r1 v); + let _ = gen_elim () in + hr_gather w r1; + hr_share r2; + pts_to_intro r2 _ _ _ _; + rewrite (ref_equiv0 r1 r2) (ref_equiv r1 r2) + +[@@__steel_reduce__; __reduce__] +let freeable0 + (#t: Type) + (#td: typedef t) + (r: ref td) +: Tot vprop += exists_ (fun p -> exists_ (fun w -> + HR.pts_to r p w `star` + pure (R.freeable w.ref) + )) + +let freeable + r += freeable0 r + +let freeable_dup + r += rewrite (freeable r) (freeable0 r); + let _ = gen_elim () in + HR.share r; + noop (); + rewrite (freeable0 r) (freeable r); + noop (); + rewrite (freeable0 r) (freeable r) + +let freeable_equiv + r1 r2 += rewrite (ref_equiv r1 r2) (ref_equiv0 r1 r2); + let _ = gen_elim () in + let w = vpattern_replace (fun w -> HR.pts_to r1 _ w `star` HR.pts_to r2 _ w) in + rewrite (freeable r1) (freeable0 r1); + let _ = gen_elim () in + hr_gather w r1; + HR.share r2; + rewrite (freeable0 r2) (freeable r2); + rewrite (ref_equiv0 r1 r2) (ref_equiv r1 r2) + +let alloc + td += let r = R.ref_alloc td.pcm td.uninitialized in + let w = { + base = _; + t = _; + td = td; + ref = r; + } + in + let res : ptr td = HR.alloc w in + HR.share res; + HR.pts_to_not_null res; + pts_to_intro res _ _ r _; + rewrite (pts_to _ _) (pts_to_or_null res (uninitialized td)); + rewrite (freeable0 res) (freeable_or_null res); + return res + +[@@noextract_to "krml"] +let read_ref + (#t: Type) + (#td: typedef t) + (#p: P.perm) + (#v: Ghost.erased t) + (#w: Ghost.erased ref0_v) + (#tbase: Type0) + (#r': Ghost.erased (R.ref tbase td.pcm)) + (r: ref td) +: ST (R.ref w.base td.pcm) + (HR.pts_to r p w `star` + r_pts_to r' v) + (fun r' -> HR.pts_to r p w `star` + R.pts_to r' v) + (tbase == w.base /\ + t == w.t /\ + td == w.td /\ + Ghost.reveal r' == coerce_eq () w.ref + ) + (fun r' -> + tbase == w.base /\ + t == w.t /\ + td == w.td /\ + r' == coerce_eq () w.ref + ) += let w0 = HR.read r in + let res : R.ref w.base td.pcm = coerce_eq () w0.ref in + rewrite (r_pts_to _ _) (R.pts_to res v); + return res + +let free + #_ #_ #v r += rewrite (pts_to r v) (pts_to0 r v); + let _ = gen_elim () in + let w = vpattern_replace_erased (HR.pts_to r _) in + let r' = read_ref r in + rewrite (freeable r) (freeable0 r); + let _ = gen_elim () in + hr_gather (Ghost.reveal w) r; + R.ref_free r'; + drop (HR.pts_to _ _ _); + return () + +let get_ref + (#opened: _) + (#t: Type) + (#td: typedef t) + (#p: P.perm) + (#v: t) + (#w: ref0_v) + (#tbase: Type0) + (#r': R.ref tbase td.pcm) + (r: ref td) +: STGhost (R.ref w.base td.pcm) opened + (HR.pts_to r p w `star` + r_pts_to r' v) + (fun r' -> HR.pts_to r p w `star` + R.pts_to r' v) + (tbase == w.base /\ + t == w.t /\ + td == w.td /\ + Ghost.reveal r' == coerce_eq () w.ref + ) + (fun r' -> + tbase == w.base /\ + t == w.t /\ + td == w.td /\ + r' == coerce_eq () w.ref + ) += let res : R.ref w.base td.pcm = coerce_eq () w.ref in + rewrite (r_pts_to _ _) (R.pts_to res v); + res + +let pts_to_intro_rewrite + (#opened: _) + (#t: Type) + (#td: typedef t) + (r: ref td) + (#p: P.perm) + (#w: ref0_v) + (#tbase: Type) + (r': R.ref tbase td.pcm) + (#v: t) + (v': Ghost.erased t) +: STGhost unit opened + (HR.pts_to r p w `star` R.pts_to r' v) + (fun _ -> pts_to r v') + (pts_to_intro_precond r w r' /\ + v == Ghost.reveal v' + ) + (fun _ -> True) += pts_to_intro r p w r' v; + vpattern_rewrite (pts_to r) v' + +let mk_fraction_split_gen + #_ #_ #td r v p p1 p2 += rewrite (pts_to _ _) (pts_to0 r (mk_fraction td v p)); + let _ = gen_elim () in + let r' = get_ref r in + td.mk_fraction_split v p1 p2; + td.mk_fraction_join v p1 p2; + rewrite + (R.pts_to _ _) + (R.pts_to r' (op td.pcm (td.mk_fraction v p1) (td.mk_fraction v p2))); + R.split _ _ (td.mk_fraction v p1) (td.mk_fraction v p2); + HR.share r; + pts_to_intro_rewrite r r' #(td.mk_fraction v p1) (mk_fraction td v p1); + pts_to_intro_rewrite r r' #(td.mk_fraction v p2) (mk_fraction td v p2) + +let mk_fraction_join + #_ #_ #td r v p1 p2 += rewrite (pts_to r (mk_fraction td v p1)) (pts_to0 r (mk_fraction td v p1)); + let _ = gen_elim () in + let w = vpattern_replace (HR.pts_to r _) in + let r' = get_ref r in + rewrite (pts_to r (mk_fraction td v p2)) (pts_to0 r (mk_fraction td v p2)); + let _ = gen_elim () in + hr_gather w r; + rewrite (R.pts_to _ (mk_fraction _ _ p2)) (R.pts_to r' (td.mk_fraction v p2)); + let _ = R.gather r' (mk_fraction td v p1) _ in + td.mk_fraction_join v p1 p2; + pts_to_intro_rewrite r r' _ + +let fractional_permissions_theorem + (#opened: _) + (#t: Type) + (#td: typedef t) + (v1: t { fractionable td v1 }) + (v2: t { fractionable td v2 }) + (p1 p2: P.perm) + (r: ref td) +: STGhost unit opened + (pts_to r (mk_fraction td v1 p1) `star` pts_to r (mk_fraction td v2 p2)) + (fun _ -> pts_to r (mk_fraction td v1 p1) `star` pts_to r (mk_fraction td v2 p2)) + (full td v1 /\ full td v2) + (fun _ -> v1 == v2 /\ (p1 `P.sum_perm` p2) `P.lesser_equal_perm` P.full_perm) += rewrite (pts_to r (mk_fraction td v1 p1)) (pts_to0 r (mk_fraction td v1 p1)); + let _ = gen_elim () in + let w = vpattern_replace (HR.pts_to r _) in + let rr = get_ref r in + rewrite (pts_to r (mk_fraction td v2 p2)) (pts_to0 r (mk_fraction td v2 p2)); + let _ = gen_elim () in + hr_gather w r; + rewrite (R.pts_to _ (mk_fraction td v2 p2)) (R.pts_to rr (mk_fraction td v2 p2)); + let _ = R.gather rr (mk_fraction td v1 p1) (mk_fraction td v2 p2) in + td.mk_fraction_full_composable v1 p1 v2 p2; + R.split rr _ (mk_fraction td v1 p1) (mk_fraction td v2 p2); + hr_share r; + rewrite (pts_to0 r (mk_fraction td v2 p2)) (pts_to r (mk_fraction td v2 p2)); + rewrite (pts_to0 r (mk_fraction td v1 p1)) (pts_to r (mk_fraction td v1 p1)) + +let r_unfocus (#opened:_) + (#ta #ta' #tb #tc: Type) + (#p: pcm tb) + (#q: pcm tc) + (r: R.ref ta q) (r': R.ref ta' p) + (l: Steel.C.Model.Connection.connection p q) (x: tc) +: STGhost (Ghost.erased tb) opened + (r `R.pts_to` x) + (fun x' -> r' `R.pts_to` x') + (requires + ta == ta' /\ + r == R.ref_focus r' l) + (ensures fun x' -> Ghost.reveal x' == l.conn_small_to_large.morph x) += let r1 : R.ref ta' q = r in + rewrite (r `R.pts_to` x) (r1 `R.pts_to` x); + R.unfocus r1 r' l x; + let x' = vpattern_replace_erased (R.pts_to r') in + x' + +irreducible let norm_field_attr = () + +let has_focus_ref_gen + (#t1: Type) + (td1: typedef t1) + (r: ref0_v) + (#t2: Type) + (td2: typedef t2) + (c: Steel.C.Model.Connection.connection td1.pcm td2.pcm) + (r': ref0_v) +: GTot prop += r'.base == r.base /\ + r.t == t1 /\ + r.td == td1 /\ + r'.t == t2 /\ + r'.td == td2 /\ + r'.ref == coerce_eq () (R.ref_focus r.ref c) + +[@@__reduce__] +let has_focus_ref0 + (#t1: Type) + (#td1: typedef t1) + (p1: ref td1) + (#t2: Type) + (#td2: typedef t2) + (c: Steel.C.Model.Connection.connection td1.pcm td2.pcm) + (p2: ref td2) +: Tot vprop += exists_ (fun p -> exists_ (fun w -> exists_ (fun p' -> exists_ (fun w' -> + HR.pts_to p1 p w `star` + HR.pts_to p2 p' w' `star` + pure (has_focus_ref_gen td1 w td2 c w') + )))) + +let has_focus_ref + (#t1: Type) + (#td1: typedef t1) + (p1: ref td1) + (#t2: Type) + (#td2: typedef t2) + (c: Steel.C.Model.Connection.connection td1.pcm td2.pcm) + (p2: ref td2) +: Tot vprop += has_focus_ref0 p1 c p2 + +module GHR = Steel.ST.GhostHigherReference + +let ghost_focus_ref + (#opened: _) + (#t1: Type) + (#td1: typedef t1) + (#v: Ghost.erased t1) + (r: ref td1) + (#t2: Type) + (td2: typedef t2) + (c: Steel.C.Model.Connection.connection td1.pcm td2.pcm) +: STGhostT (Ghost.erased (ref td2)) opened + (pts_to r v) + (fun r' -> pts_to r v `star` has_focus_ref r c r') += rewrite (pts_to r v) (pts_to0 r v); + let _ = gen_elim () in + let w = vpattern_replace (HR.pts_to r _) in + let rr = get_ref r in + let w' = { + base = w.base; + t = t2; + td = td2; + ref = R.ref_focus rr c; + } + in + let gr' = GHR.alloc w' in + let r1' = GHR.reveal_ref gr' in + GHR.reveal_pts_to gr' P.full_perm w'; + rewrite_equiv (GHR.pts_to _ _ _) (HR.pts_to r1' P.full_perm w'); + HR.pts_to_not_null r1'; + let r' = Ghost.hide r1' in + rewrite (HR.pts_to r1' P.full_perm w') (HR.pts_to r' P.full_perm w'); + hr_share r; + rewrite (has_focus_ref0 r c r') (has_focus_ref r c r'); + pts_to_intro r _ _ rr _; + r' + +[@@noextract_to "krml"] // proof-only +let focus_ref + (#t1: Type) + (#td1: typedef t1) + (#v: Ghost.erased t1) + (r: ref td1) + (#t2: Type) + (td2: typedef t2) + (c: Steel.C.Model.Connection.connection td1.pcm td2.pcm) +: STT (ref td2) + (pts_to r v) + (fun r' -> pts_to r v `star` has_focus_ref r c r') += rewrite (pts_to r v) (pts_to0 r v); + let _ = gen_elim () in + let w = vpattern_replace_erased (HR.pts_to r _) in + let rr = read_ref r in + let w' = { + base = w.base; + t = t2; + td = td2; + ref = R.ref_focus rr c; + } + in + let r' = HR.alloc w' in + HR.pts_to_not_null r'; + hr_share r; + rewrite (has_focus_ref0 r c r') (has_focus_ref r c r'); + pts_to_intro r _ _ rr _; + return r' + +let has_focus_ref_dup + (#opened: _) + (#t1: Type) + (#td1: typedef t1) + (r: ref td1) + (#t2: Type) + (#td2: typedef t2) + (c: Steel.C.Model.Connection.connection td1.pcm td2.pcm) + (r': ref td2) +: STGhostT unit opened + (has_focus_ref r c r') + (fun _ -> has_focus_ref r c r' `star` has_focus_ref r c r') += + rewrite (has_focus_ref r c r') (has_focus_ref0 r c r'); + let _ = gen_elim () in + HR.share r; + HR.share r'; + noop (); + rewrite (has_focus_ref0 r c r') (has_focus_ref r c r'); + noop (); + rewrite (has_focus_ref0 r c r') (has_focus_ref r c r') + +let has_focus_ref_inj + (#opened: _) + (#t1: Type) + (#td1: typedef t1) + (r: ref td1) + (#t2: Type) + (#td2: typedef t2) + (c: Steel.C.Model.Connection.connection td1.pcm td2.pcm) + (r1 r2: ref td2) +: STGhostT unit opened + (has_focus_ref r c r1 `star` has_focus_ref r c r2) + (fun _ -> has_focus_ref r c r1 `star` has_focus_ref r c r2 `star` ref_equiv r1 r2) += + rewrite (has_focus_ref r c r1) (has_focus_ref0 r c r1); + let _ = gen_elim () in + let w = vpattern_replace (HR.pts_to r _) in + let w1 = vpattern_replace (HR.pts_to r1 _) in + rewrite (has_focus_ref r c r2) (has_focus_ref0 r c r2); + let _ = gen_elim () in + hr_gather w r; + vpattern_rewrite (HR.pts_to r2 _) w1; + hr_share r; + hr_share r1; + rewrite (has_focus_ref0 r c r1) (has_focus_ref r c r1); + hr_share r2; + rewrite (has_focus_ref0 r c r2) (has_focus_ref r c r2); + rewrite (ref_equiv0 r1 r2) (ref_equiv r1 r2) + +let has_focus_ref_equiv_from + (#opened: _) + (#t1: Type) + (#td1: typedef t1) + (r1: ref td1) + (#t2: Type) + (#td2: typedef t2) + (c: Steel.C.Model.Connection.connection td1.pcm td2.pcm) + (r': ref td2) + (r2: ref td1) +: STGhostT unit opened + (ref_equiv r1 r2 `star` has_focus_ref r1 c r') + (fun _ -> ref_equiv r1 r2 `star` has_focus_ref r2 c r') += rewrite (ref_equiv r1 r2) (ref_equiv0 r1 r2); + let _ = gen_elim () in + let w = vpattern_replace (fun w -> HR.pts_to r1 _ w `star` HR.pts_to r2 _ w) in + rewrite (has_focus_ref r1 c r') (has_focus_ref0 r1 c r'); + let _ = gen_elim () in + hr_gather w r1; + hr_share r2; + rewrite (has_focus_ref0 r2 c r') (has_focus_ref r2 c r'); + rewrite (ref_equiv0 r1 r2) (ref_equiv r1 r2) + +let has_focus_ref_equiv_to + (#opened: _) + (#t1: Type) + (#td1: typedef t1) + (r: ref td1) + (#t2: Type) + (#td2: typedef t2) + (c: Steel.C.Model.Connection.connection td1.pcm td2.pcm) + (r1' r2': ref td2) +: STGhostT unit opened + (ref_equiv r1' r2' `star` has_focus_ref r c r1') + (fun _ -> ref_equiv r1' r2' `star` has_focus_ref r c r2') += rewrite (ref_equiv r1' r2') (ref_equiv0 r1' r2'); + let _ = gen_elim () in + let w = vpattern_replace (fun w -> HR.pts_to r1' _ w `star` HR.pts_to r2' _ w) in + rewrite (has_focus_ref r c r1') (has_focus_ref0 r c r1'); + let _ = gen_elim () in + hr_gather w r1'; + hr_share r2'; + rewrite (has_focus_ref0 r c r2') (has_focus_ref r c r2'); + rewrite (ref_equiv0 r1' r2') (ref_equiv r1' r2') + +#push-options "--split_queries" +#restart-solver + +let has_focus_ref_id + (#opened: _) + (#t1: Type) + (#td1: typedef t1) + (r1: ref td1) + (r2: ref td1) +: STGhostT unit opened + (has_focus_ref r1 (Steel.C.Model.Connection.connection_id td1.pcm) r2) + (fun _ -> has_focus_ref r1 (Steel.C.Model.Connection.connection_id td1.pcm) r2 `star` ref_equiv r1 r2) += has_focus_ref_dup r1 (Steel.C.Model.Connection.connection_id td1.pcm) r2; + rewrite (has_focus_ref _ _ _) (has_focus_ref0 r1 (Steel.C.Model.Connection.connection_id td1.pcm) r2); + let _ = gen_elim () in + let w = vpattern_replace (HR.pts_to r1 _) in + R.ref_focus_id #w.base #_ #td1.pcm (coerce_eq () w.ref); + vpattern_rewrite (HR.pts_to r2 _) w; + rewrite (ref_equiv0 r1 r2) (ref_equiv r1 r2) + +#pop-options + +let has_focus_ref_compose + (#opened: _) + (#t1: Type) + (#td1: typedef t1) + (r1: ref td1) + (#t2: Type) + (#td2: typedef t2) + (c12: Steel.C.Model.Connection.connection td1.pcm td2.pcm) + (r2: ref td2) + (#t3: Type) + (#td3: typedef t3) + (c23: Steel.C.Model.Connection.connection td2.pcm td3.pcm) + (r3: ref td3) +: STGhostT unit opened + (has_focus_ref r1 c12 r2 `star` has_focus_ref r2 c23 r3) + (fun _ -> has_focus_ref r1 c12 r2 `star` has_focus_ref r2 c23 r3 `star` has_focus_ref r1 (Steel.C.Model.Connection.connection_compose c12 c23) r3) += rewrite (has_focus_ref r1 c12 r2) (has_focus_ref0 r1 c12 r2); + let _ = gen_elim () in + let w1 = vpattern_replace (HR.pts_to r1 _) in + let w2 = vpattern_replace (HR.pts_to r2 _) in + rewrite (has_focus_ref r2 c23 r3) (has_focus_ref0 r2 c23 r3); + let _ = gen_elim () in + let w3 = vpattern_replace (HR.pts_to r3 _) in + hr_gather w2 r2; + R.ref_focus_comp #_ #_ #_ #_ #td1.pcm #td2.pcm #td3.pcm (coerce_eq () w1.ref <: R.ref w1.base _) c12 c23; + HR.share r1; + HR.share r2; + rewrite (has_focus_ref0 r1 c12 r2) (has_focus_ref r1 c12 r2); + HR.share r3; + rewrite (has_focus_ref0 r2 c23 r3) (has_focus_ref r2 c23 r3); + rewrite (has_focus_ref0 r1 (Steel.C.Model.Connection.connection_compose c12 c23) r3) (has_focus_ref r1 (Steel.C.Model.Connection.connection_compose c12 c23) r3) + +module Conn = Steel.C.Model.Connection + +let focus_ref_iso + (#opened: _) + (#t1: Type) + (#td1: typedef t1) + (#v: Ghost.erased t1) + (r: ref td1) + (#t2: Type) + (#td2: typedef t2) + (r': ref td2) + (c: Conn.isomorphism td1.pcm td2.pcm) +: STGhost (Ghost.erased t2) opened + (pts_to r v `star` has_focus_ref r (Conn.connection_of_isomorphism c) r') + (fun v' -> pts_to r' v' `star` has_focus_ref r (Conn.connection_of_isomorphism c) r') + True + (fun v' -> Ghost.reveal v' == c.iso_1_2.morph v) += rewrite (has_focus_ref r (Conn.connection_of_isomorphism c) r') (has_focus_ref0 r (Conn.connection_of_isomorphism c) r'); + let _ = gen_elim () in + let w = vpattern_replace (HR.pts_to r _) in + let w' = vpattern_replace (HR.pts_to r' _) in + rewrite (pts_to r v) (pts_to0 r v); + let _ = gen_elim () in + let rr = get_ref r in + hr_gather w r; + let v' = c.iso_1_2.morph v in + R.gfocus rr (Conn.connection_of_isomorphism c) v v'; + hr_share r'; + rewrite (has_focus_ref0 r (Conn.connection_of_isomorphism c) r') (has_focus_ref r (Conn.connection_of_isomorphism c) r'); + pts_to_intro r' _ _ _ _; + _ + +let unfocus_ref + (#opened: _) + (#t1: Type) + (#td1: typedef t1) + (r: ref td1) + (#t2: Type) + (#td2: typedef t2) + (#v': Ghost.erased t2) + (r': ref td2) + (c: Steel.C.Model.Connection.connection td1.pcm td2.pcm) +: STGhost (Ghost.erased t1) opened + (pts_to r' v' `star` has_focus_ref r c r') + (fun v -> pts_to r v `star` has_focus_ref r c r') + True + (fun v -> Ghost.reveal v == c.conn_small_to_large.morph v') += rewrite (has_focus_ref r c r') (has_focus_ref0 r c r'); + let _ = gen_elim () in + let w = vpattern_replace (HR.pts_to r _) in + let w' = vpattern_replace (HR.pts_to r' _) in + rewrite (pts_to r' v') (pts_to0 r' v'); + let _ = gen_elim () in + hr_gather w' r'; + let rr' = get_ref r' in + let rr : R.ref w'.base td1.pcm = coerce_eq () w.ref in + R.unfocus rr' rr c _; + hr_share r; + rewrite (has_focus_ref0 r c r') (has_focus_ref r c r'); + pts_to_intro r _ _ _ _; + _ + +let has_focus_ref_compose_12_13 + (#opened: _) + (#t1: Type) + (#td1: typedef t1) + (r1: ref td1) + (#t2: Type) + (#td2: typedef t2) + (c12: Conn.connection td1.pcm td2.pcm) + (r2: ref td2) + (#t3: Type) + (#td3: typedef t3) + (c23: Conn.connection td2.pcm td3.pcm) + (r3: ref td3) +: STGhostT unit opened + (has_focus_ref r1 c12 r2 `star` has_focus_ref r1 (Conn.connection_compose c12 c23) r3) + (fun _ -> has_focus_ref r1 c12 r2 `star` has_focus_ref r2 c23 r3 `star` has_focus_ref r1 (Conn.connection_compose c12 c23) r3) += rewrite (has_focus_ref r1 c12 r2) (has_focus_ref0 r1 c12 r2); + let _ = gen_elim () in + let w1 = vpattern_replace (HR.pts_to r1 _) in + let w2 = vpattern_replace (HR.pts_to r2 _) in + rewrite (has_focus_ref r1 (Conn.connection_compose c12 c23) r3) (has_focus_ref0 r1 (Conn.connection_compose c12 c23) r3); + let _ = gen_elim () in + let w3 = vpattern_replace (HR.pts_to r3 _) in + hr_gather w1 r1; + R.ref_focus_comp #_ #_ #_ #_ #td1.pcm #td2.pcm #td3.pcm (coerce_eq () w1.ref <: R.ref w1.base _) c12 c23; + HR.share r1; + HR.share r2; + rewrite (has_focus_ref0 r1 c12 r2) (has_focus_ref r1 c12 r2); + HR.share r3; + rewrite (has_focus_ref0 r2 c23 r3) (has_focus_ref r2 c23 r3); + rewrite (has_focus_ref0 r1 (Conn.connection_compose c12 c23) r3) (has_focus_ref r1 (Conn.connection_compose c12 c23) r3) + +let ghost_focus_ref_gen + (#opened: _) + (#t1: Type) + (#td1: typedef t1) + (#p: P.perm) + (#w: ref0_v) + (r: ref td1) + (#t2: Type) + (td2: typedef t2) + (c: Conn.connection td1.pcm td2.pcm) +: STGhost (Ghost.erased (ref td2)) opened + (HR.pts_to r p w) + (fun r' -> exists_ (fun p' -> HR.pts_to r p' w `star` has_focus_ref r c r')) + (t1 == w.t /\ + td1 == w.td + ) + (fun _ -> True) += let rr : R.ref w.base td1.pcm = coerce_eq () w.ref in + let w' = { + base = w.base; + t = t2; + td = td2; + ref = R.ref_focus rr c; + } + in + let gr' = GHR.alloc w' in + let r1' = GHR.reveal_ref gr' in + GHR.reveal_pts_to gr' P.full_perm w'; + rewrite_equiv (GHR.pts_to _ _ _) (HR.pts_to r1' P.full_perm w'); + HR.pts_to_not_null r1'; + let r' = Ghost.hide r1' in + rewrite (HR.pts_to r1' P.full_perm w') (HR.pts_to r' P.full_perm w'); + hr_share r; + rewrite (has_focus_ref0 r c r') (has_focus_ref r c r'); + noop (); + r' + +[@@noextract_to "krml"] // proof-only +let focus_ref_gen + (#t1: Type) + (#td1: typedef t1) + (#p: P.perm) + (#v: Ghost.erased ref0_v) + (r: ref td1) + (#t2: Type) + (td2: typedef t2) + (c: Conn.connection td1.pcm td2.pcm) +: ST (ref td2) + (HR.pts_to r p v) + (fun r' -> exists_ (fun p' -> HR.pts_to r p' v `star` has_focus_ref r c r')) + (t1 == v.t /\ + td1 == v.td + ) + (fun _ -> True) += let w = HR.read r in + let rr : R.ref w.base td1.pcm = coerce_eq () w.ref in + let w' = { + base = w.base; + t = t2; + td = td2; + ref = R.ref_focus rr c; + } + in + let r' = HR.alloc w' in + HR.pts_to_not_null r'; + hr_share r; + rewrite (has_focus_ref0 r c r') (has_focus_ref r c r'); + return r' + +let ghost_focus_ref_compose_12_13 + (#opened: _) + (#t1: Type) + (#td1: typedef t1) + (r1: ref td1) + (#t2: Type) + (td2: typedef t2) + (c12: Conn.connection td1.pcm td2.pcm) + (#t3: Type) + (#td3: typedef t3) + (c23: Conn.connection td2.pcm td3.pcm) + (r3: ref td3) +: STGhostT (Ghost.erased (ref td2)) opened + (has_focus_ref r1 (Conn.connection_compose c12 c23) r3) + (fun r2 -> has_focus_ref r1 c12 r2 `star` has_focus_ref r2 c23 r3 `star` has_focus_ref r1 (Conn.connection_compose c12 c23) r3) += rewrite (has_focus_ref r1 (Conn.connection_compose c12 c23) r3) (has_focus_ref0 r1 (Conn.connection_compose c12 c23) r3); + let _ = gen_elim () in + let r2 = ghost_focus_ref_gen r1 td2 c12 in + let _ = gen_elim () in + rewrite (has_focus_ref0 r1 (Conn.connection_compose c12 c23) r3) (has_focus_ref r1 (Conn.connection_compose c12 c23) r3); + has_focus_ref_compose_12_13 r1 c12 r2 c23 r3; + r2 + +[@@noextract_to "krml"] // proof-only +let focus_ref_compose_12_13 + (#t1: Type) + (#td1: typedef t1) + (r1: ref td1) + (#t2: Type) + (td2: typedef t2) + (c12: Conn.connection td1.pcm td2.pcm) + (#t3: Type) + (#td3: typedef t3) + (c23: Conn.connection td2.pcm td3.pcm) + (r3: ref td3) +: STT (ref td2) + (has_focus_ref r1 (Conn.connection_compose c12 c23) r3) + (fun r2 -> has_focus_ref r1 c12 r2 `star` has_focus_ref r2 c23 r3 `star` has_focus_ref r1 (Conn.connection_compose c12 c23) r3) += rewrite (has_focus_ref r1 (Conn.connection_compose c12 c23) r3) (has_focus_ref0 r1 (Conn.connection_compose c12 c23) r3); + let _ = gen_elim () in + let r2 = focus_ref_gen r1 td2 c12 in + let _ = gen_elim () in + rewrite (has_focus_ref0 r1 (Conn.connection_compose c12 c23) r3) (has_focus_ref r1 (Conn.connection_compose c12 c23) r3); + has_focus_ref_compose_12_13 r1 c12 r2 c23 r3; + return r2 + diff --git a/src/proofs/steelc/Steel.ST.C.Types.Fields.fst b/src/proofs/steelc/Steel.ST.C.Types.Fields.fst new file mode 100644 index 000000000..081c9bd71 --- /dev/null +++ b/src/proofs/steelc/Steel.ST.C.Types.Fields.fst @@ -0,0 +1,4 @@ +module Steel.ST.C.Types.Fields + +let field_t_nil = unit +let field_t_cons _ _ _ = unit diff --git a/src/proofs/steelc/Steel.ST.C.Types.Rewrite.fst b/src/proofs/steelc/Steel.ST.C.Types.Rewrite.fst new file mode 100644 index 000000000..4a6930ece --- /dev/null +++ b/src/proofs/steelc/Steel.ST.C.Types.Rewrite.fst @@ -0,0 +1,27 @@ +module Steel.ST.C.Types.Rewrite +open Steel.ST.Util + +friend Steel.ST.C.Types.Base +open Steel.ST.C.Types.Base + +module RW = Steel.ST.C.Model.Rewrite + +let rewrite_typedef + (#from #to: Type) + (td: typedef from) + (rewrite: RW.rewrite_elts from to) +: Tot (typedef to) += { + pcm = RW.rewrite_pcm td.pcm rewrite; + fractionable = (fun y -> td.fractionable (rewrite.rewrite_to_from y)); + mk_fraction = (fun y p -> rewrite.rewrite_from_to (td.mk_fraction (rewrite.rewrite_to_from y) p)); + mk_fraction_full = (fun y -> td.mk_fraction_full (rewrite.rewrite_to_from y)); + mk_fraction_compose = (fun y p1 p2 -> td.mk_fraction_compose (rewrite.rewrite_to_from y) p1 p2); + fractionable_one = (); + mk_fraction_one = (fun p -> td.mk_fraction_one p); + uninitialized = rewrite.rewrite_from_to td.uninitialized; + mk_fraction_split = (fun y p1 p2 -> td.mk_fraction_split (rewrite.rewrite_to_from y) p1 p2); + mk_fraction_join = (fun y p1 p2 -> td.mk_fraction_join (rewrite.rewrite_to_from y) p1 p2); + mk_fraction_eq_one = (fun y p -> td.mk_fraction_eq_one (rewrite.rewrite_to_from y) p); + mk_fraction_full_composable = (fun v1 p1 v2 p2 -> td.mk_fraction_full_composable (rewrite.rewrite_to_from v1) p1 (rewrite.rewrite_to_from v2) p2); + } diff --git a/src/proofs/steelc/Steel.ST.C.Types.Scalar.fst b/src/proofs/steelc/Steel.ST.C.Types.Scalar.fst new file mode 100644 index 000000000..684dcda17 --- /dev/null +++ b/src/proofs/steelc/Steel.ST.C.Types.Scalar.fst @@ -0,0 +1,104 @@ +module Steel.ST.C.Types.Scalar +open Steel.ST.GenElim +friend Steel.ST.C.Types.Base +open Steel.ST.C.Types.Base + +open Steel.C.Model.PCM + +module R = Steel.ST.C.Model.Ref +module HR = Steel.ST.HigherReference +module F = Steel.ST.C.Model.Frac + +let scalar_t t = F.fractional (option t) + +let scalar_fractionable + (#t: Type) + (s: scalar_t t) +: GTot bool += match s with + | Some (_, p) -> p `P.lesser_equal_perm` P.full_perm + | _ -> true + +[@@noextract_to "krml"] // proof-only +let scalar_mk_fraction + (#t: Type) + (x: scalar_t t) + (p: P.perm) +: Pure (scalar_t t) + (requires (scalar_fractionable x)) + (ensures (fun y -> p `P.lesser_equal_perm` P.full_perm ==> scalar_fractionable y)) += match x with + | (Some (v, p')) -> + (Some (v, p `prod_perm` p')) + | _ -> x + +#set-options "--smtencoding.elim_box true --smtencoding.l_arith_repr native --smtencoding.nl_arith_repr native" // for mk_fraction_split + +#restart-solver +let scalar t = { + pcm = F.pcm_frac; + fractionable = scalar_fractionable #t; + mk_fraction = scalar_mk_fraction #t; + mk_fraction_full = (fun x -> + match x with + | (Some (v, p)) -> + assert_norm ((P.full_perm `prod_perm` p).v == (let open FStar.Real in 1.0R *. p.v)); + assert (P.full_perm `prod_perm` p == p) + | _ -> () + ); + mk_fraction_compose = (fun w p1 p2 -> + match w with + | (Some (v, p)) -> + assert_norm (let open FStar.Real in ((p1 `prod_perm` p2) `prod_perm` p).v == (p1.v *. p2.v) *. p.v); + assert_norm (let open FStar.Real in (p2 `prod_perm` (p1 `prod_perm` p)).v == p2.v *. (p1.v *. p.v)); + assert ((p1 `prod_perm` p2) `prod_perm` p == p2 `prod_perm` (p1 `prod_perm` p)) + | _ -> () + ); + fractionable_one = (); + mk_fraction_one = (fun _ -> ()); + uninitialized = Some (None, P.full_perm); + mk_fraction_split = (fun w p1 p2 -> + match w with + | (Some (v, p)) -> + assert_norm (((p1 `P.sum_perm` p2) `prod_perm` p).v == (let open FStar.Real in (p1.v +. p2.v) *. p.v)); + assert_norm (((p1 `prod_perm` p) `P.sum_perm` (p2 `prod_perm` p)).v == (let open FStar.Real in (p1.v *. p.v) +. (p2.v *. p.v))); + assert ((p1 `P.sum_perm` p2) `prod_perm` p == (p1 `prod_perm` p) `P.sum_perm` (p2 `prod_perm` p)); + assert (composable (F.pcm_frac) (scalar_mk_fraction w p1) (scalar_mk_fraction w p2)); +() // assert (op (U.pcm_uninit F.pcm_frac) (scalar_mk_fraction w p1) (scalar_mk_fraction w p2) == scalar_mk_fraction w (p1 `P.sum_perm` p2)) + | _ -> () + ); + mk_fraction_join = (fun w p1 p2 -> + match w with + | (Some (v, p)) -> + assert_norm (((p1 `P.sum_perm` p2) `prod_perm` p).v == (let open FStar.Real in (p1.v +. p2.v) *. p.v)); + assert_norm (((p1 `prod_perm` p) `P.sum_perm` (p2 `prod_perm` p)).v == (let open FStar.Real in (p1.v *. p.v) +. (p2.v *. p.v))); + assert ((p1 `P.sum_perm` p2) `prod_perm` p == (p1 `prod_perm` p) `P.sum_perm` (p2 `prod_perm` p)) + | _ -> () + ); + mk_fraction_eq_one = (fun v p -> ()); + mk_fraction_full_composable = (fun _ _ _ _ -> ()); +} + +let mk_scalar v = (Some (Some v, P.full_perm)) + +let mk_scalar_fractionable v p = () + +let mk_scalar_inj v1 v2 p1 p2 = () + +let read0 + #t #v #p r += rewrite (pts_to r _) (pts_to0 r (Some (Some (Ghost.reveal v), p))); + let _ = gen_elim () in + let r' = read_ref r in + let v' = R.ref_read r' in + let Some (Some v0, _) = v' in + pts_to_intro_rewrite r r' _; + return v0 + +let write + #t #v r v' += rewrite (pts_to r _) (pts_to0 r (Ghost.reveal v)); + let _ = gen_elim () in + let r' = read_ref r in + R.ref_upd r' _ _ (R.base_fpu _ _ (Some (Some v', P.full_perm))); + pts_to_intro_rewrite r r' _ diff --git a/src/proofs/steelc/Steel.ST.C.Types.Struct.Aux.fst b/src/proofs/steelc/Steel.ST.C.Types.Struct.Aux.fst new file mode 100644 index 000000000..58339b2b9 --- /dev/null +++ b/src/proofs/steelc/Steel.ST.C.Types.Struct.Aux.fst @@ -0,0 +1,364 @@ +module Steel.ST.C.Types.Struct.Aux +open Steel.ST.GenElim +friend Steel.ST.C.Types.Base +open Steel.ST.C.Types.Base + +open Steel.C.Model.PCM + +module P = Steel.FractionalPermission +module R = Steel.ST.C.Model.Ref +module HR = Steel.ST.HigherReference + +module S = Steel.ST.C.Model.Struct + +[@@noextract_to "krml"] // proof-only +let struct_field_pcm + (#field_t: eqtype) + (fields: field_description_gen_t field_t) + (f: field_t) +: Tot (pcm (fields.fd_type f)) += (fields.fd_typedef f).pcm + +module FX = FStar.FunctionalExtensionality + +[@@noextract_to "krml"] // primitive +let struct_t1 (#field_t: eqtype) (fields: field_description_gen_t field_t) : Tot Type0 = + FX.restricted_t field_t fields.fd_type + +[@@noextract_to "krml"] // proof-only +let struct_pcm + (#field_t: eqtype) + (fields: field_description_gen_t field_t) +: Tot (pcm (struct_t1 fields)) += S.prod_pcm (struct_field_pcm fields) + +[@@noextract_to "krml"] // proof-only +let t_struct_set_field + (#field_t: eqtype) (#fields: field_description_gen_t field_t) (f: field_t) (v: fields.fd_type f) (s: struct_t1 fields) +: Tot (struct_t1 fields) += FX.on_dom (field_t) (fun f' -> if f = f' then v else s f') + +let struct_eq_intro + (#field_t: eqtype) + (#fields: field_description_gen_t field_t) + (s1 s2: struct_t1 fields) + (prf: ( + (f: field_t) -> + Lemma + (s1 f == s2 f) + )) +: Lemma + (s1 == s2) += Classical.forall_intro prf; + assert (s1 `FX.feq` s2) + +let struct_fractionable + (#field_t: eqtype) (#fields: field_description_gen_t field_t) + (s: struct_t1 fields) +: GTot bool += FStar.StrongExcludedMiddle.strong_excluded_middle (forall (f: field_t) . (fields.fd_typedef f).fractionable (s f)) + +[@@noextract_to "krml"] // proof-only +let struct_mk_fraction + (#field_t: eqtype) (#fields: field_description_gen_t field_t) + (s: struct_t1 fields) + (p: P.perm) +: Pure (struct_t1 fields) + (requires (struct_fractionable s)) + (ensures (fun s' -> p `P.lesser_equal_perm` P.full_perm ==> struct_fractionable s')) += FX.on_dom field_t (fun f -> (fields.fd_typedef f).mk_fraction (s f) p) + +[@@noextract_to "krml"] // proof-only +let struct_uninitialized + (#field_t: eqtype) (fields: field_description_gen_t field_t) +: Pure (struct_t1 fields) + (requires True) + (ensures (fun y -> p_refine (struct_pcm fields) y)) += FX.on_dom field_t (fun f -> (fields.fd_typedef f).uninitialized <: fields.fd_type f) + +let struct1 + (#field_t: eqtype) + (fields: field_description_gen_t field_t) += { + pcm = struct_pcm fields; + fractionable = struct_fractionable; + mk_fraction = struct_mk_fraction; + mk_fraction_full = (fun x -> + struct_eq_intro (struct_mk_fraction x P.full_perm) x (fun f -> + (fields.fd_typedef f).mk_fraction_full (x f) + ) + ); + mk_fraction_compose = (fun x p1 p2 -> + struct_eq_intro (struct_mk_fraction (struct_mk_fraction x p1) p2) (struct_mk_fraction x (p1 `prod_perm` p2)) (fun f -> + (fields.fd_typedef f).mk_fraction_compose (x f) p1 p2 + ) + ); + fractionable_one = (); + mk_fraction_one = (fun p -> + struct_eq_intro (struct_mk_fraction (one (struct_pcm fields)) p) (one (struct_pcm fields)) (fun f -> + (fields.fd_typedef f).mk_fraction_one p + ) + ); + uninitialized = struct_uninitialized _; + mk_fraction_split = (fun v p1 p2 -> + let prf + (f: field_t) + : Lemma + (composable (fields.fd_typedef f).pcm (mk_fraction (fields.fd_typedef f) (v f) p1) (mk_fraction (fields.fd_typedef f) (v f) p2)) + = (fields.fd_typedef f).mk_fraction_split (v f) p1 p2 + in + Classical.forall_intro prf + ); + mk_fraction_join = (fun v p1 p2 -> + struct_eq_intro (op (struct_pcm fields) (struct_mk_fraction v p1) (struct_mk_fraction v p2)) (struct_mk_fraction v (p1 `P.sum_perm` p2)) (fun f -> + (fields.fd_typedef f).mk_fraction_join (v f) p1 p2 + ) + ); + mk_fraction_eq_one = (fun v p -> + struct_eq_intro v (one (struct_pcm fields)) (fun f -> + (fields.fd_typedef f).mk_fraction_eq_one (v f) p + ) + ); + mk_fraction_full_composable = (fun v1 p1 v2 p2 -> + let f = FStar.IndefiniteDescription.indefinite_description_ghost field_t (fun _ -> True) in + (fields.fd_typedef f).mk_fraction_full_composable (v1 f) p1 (v2 f) p2; + struct_eq_intro v1 v2 (fun f -> + (fields.fd_typedef f).mk_fraction_full_composable (v1 f) p1 (v2 f) p2 + ) + ); +} + +[@@__reduce__] +let has_struct_field0 + (#field_t: eqtype) + (#fields: field_description_gen_t field_t) + (r: ref (struct1 fields)) + (field: field_t) + (r': ref (fields.fd_typedef field)) +: Tot vprop += has_focus_ref0 r (S.struct_field (struct_field_pcm fields) field) r' + +[@@__reduce__] +let has_struct_field05 + (#field_t: eqtype) + (#fields: field_description_gen_t field_t) + (r: ref (struct1 fields)) + (field: field_t) + (r': ref (fields.fd_typedef field)) +: Tot vprop += has_focus_ref r (S.struct_field (struct_field_pcm fields) field) r' + +let has_struct_field1 + (#field_t: eqtype) + (#fields: field_description_gen_t field_t) + (r: ref (struct1 fields)) + (field: field_t) + (r': ref (fields.fd_typedef field)) +: Tot vprop += has_struct_field0 r field r' + +let has_struct_field_dup' + (#opened: _) + (#field_t: eqtype) + (#fields: field_description_gen_t field_t) + (r: ref (struct1 fields)) + (field: field_t) + (r': ref (fields.fd_typedef field)) +: STGhostT unit opened + (has_struct_field1 r field r') + (fun _ -> has_struct_field1 r field r' `star` has_struct_field1 r field r') += rewrite (has_struct_field1 r field r') (has_struct_field05 r field r'); + has_focus_ref_dup r _ r'; + rewrite (has_struct_field05 r field r') (has_struct_field1 r field r'); + rewrite (has_struct_field05 r field r') (has_struct_field1 r field r') + +let has_struct_field_inj' + (#opened: _) + (#field_t: eqtype) + (#fields: field_description_gen_t field_t) + (r: ref (struct1 fields)) + (field: field_t) + (r1 r2: ref (fields.fd_typedef field)) +: STGhostT unit opened + (has_struct_field1 r field r1 `star` has_struct_field1 r field r2) + (fun _ -> has_struct_field1 r field r1 `star` has_struct_field1 r field r2 `star` ref_equiv r1 r2) += rewrite (has_struct_field1 r field r1) (has_struct_field05 r field r1); + rewrite (has_struct_field1 r field r2) (has_struct_field05 r field r2); + has_focus_ref_inj r _ r1 r2; + rewrite (has_struct_field05 r field r1) (has_struct_field1 r field r1); + rewrite (has_struct_field05 r field r2) (has_struct_field1 r field r2) + +let has_struct_field_equiv_from' + (#opened: _) + (#field_t: eqtype) + (#fields: field_description_gen_t field_t) + (r1: ref (struct1 fields)) + (field: field_t) + (r': ref (fields.fd_typedef field)) + (r2: ref (struct1 fields)) +: STGhostT unit opened + (ref_equiv r1 r2 `star` has_struct_field1 r1 field r') + (fun _ -> ref_equiv r1 r2 `star` has_struct_field1 r2 field r') += rewrite (has_struct_field1 r1 field r') (has_struct_field05 r1 field r'); + has_focus_ref_equiv_from r1 _ r' r2; + rewrite (has_struct_field05 r2 field r') (has_struct_field1 r2 field r') + +let has_struct_field_equiv_to' + (#opened: _) + (#field_t: eqtype) + (#fields: field_description_gen_t field_t) + (r: ref (struct1 fields)) + (field: field_t) + (r1': ref (fields.fd_typedef field)) + (r2': ref (fields.fd_typedef field)) +: STGhostT unit opened + (ref_equiv r1' r2' `star` has_struct_field1 r field r1') + (fun _ -> ref_equiv r1' r2' `star` has_struct_field1 r field r2') += rewrite (has_struct_field1 r field r1') (has_struct_field05 r field r1'); + has_focus_ref_equiv_to r _ r1' r2'; + rewrite (has_struct_field05 r field r2') (has_struct_field1 r field r2') + +[@@noextract_to "krml"] // proof-only +let t_struct_get_field + (#field_t: eqtype) (#fields: field_description_gen_t field_t) (s: struct_t1 fields) (f: field_t) +: Tot (fields.fd_type f) += s f + +let ghost_struct_field_focus' + (#opened: _) + (#field_t: eqtype) + (#fields: field_description_gen_t field_t) + (#v: Ghost.erased (struct_t1 fields)) + (r: ref (struct1 fields)) + (field: field_t) + (r': ref (fields.fd_typedef field)) +: STGhostT unit opened + (has_struct_field1 r field r' `star` pts_to r v) + (fun _ -> has_struct_field1 r field r' `star` pts_to r (t_struct_set_field field (unknown (fields.fd_typedef field)) v) `star` pts_to r' (t_struct_get_field v field)) += has_struct_field_dup' r field r'; + rewrite (has_struct_field1 r field r') (has_struct_field0 r field r'); + let _ = gen_elim () in + let w = vpattern_replace (HR.pts_to r _) in + rewrite (pts_to r v) (pts_to0 r v); + let _ = gen_elim () in + hr_gather w r; + let rr = get_ref r in + S.g_addr_of_struct_field rr field v; + assert (t_struct_set_field field (unknown (fields.fd_typedef field)) v `FX.feq` S.struct_without_field (struct_field_pcm fields) field v); + noop (); + pts_to_intro_rewrite r rr _; + pts_to_intro_rewrite r' _ _ + +let ghost_struct_field' + (#opened: _) + (#field_t: eqtype) + (#fields: field_description_gen_t field_t) + (#v: Ghost.erased (struct_t1 fields)) + (r: ref (struct1 fields)) + (field: field_t) +: STGhostT (Ghost.erased (ref (fields.fd_typedef field))) opened + (pts_to r v) + (fun r' -> pts_to r (t_struct_set_field field (unknown (fields.fd_typedef field)) v) `star` pts_to r' (t_struct_get_field v field) `star` has_struct_field1 r field r') += let r' = ghost_focus_ref r (fields.fd_typedef field) (S.struct_field (struct_field_pcm fields) field) in + rewrite (has_struct_field05 r field r') (has_struct_field1 r field r'); + ghost_struct_field_focus' r field r'; + r' + +let struct_field' + (#field_t: eqtype) + (#fields: field_description_gen_t field_t) + (#v: Ghost.erased (struct_t1 fields)) + (r: ref (struct1 fields)) + (field: field_t) +: STT (ref (fields.fd_typedef field)) + (pts_to r v) + (fun r' -> pts_to r (t_struct_set_field field (unknown (fields.fd_typedef field)) v) `star` pts_to r' (t_struct_get_field v field) `star` has_struct_field1 r field r') += let r' = focus_ref r (fields.fd_typedef field) (S.struct_field (struct_field_pcm fields) field) in + rewrite (has_struct_field05 r field r') (has_struct_field1 r field r'); + ghost_struct_field_focus' r field r'; + return r' + +let r_unaddr_of_struct_field + (#opened: _) + (#base #base':Type) (#a:eqtype) (#b: a -> Type u#b) (p:(k:a -> pcm (b k))) (k:a) + (r': R.ref base' (p k)) (r: R.ref base (S.prod_pcm p)) + (xs: Ghost.erased (FX.restricted_t a b)) (x: Ghost.erased (b k)) +: STGhost unit opened + ((r `R.pts_to` xs) `star` (r' `R.pts_to` x)) + (fun s -> r `R.pts_to` S.struct_with_field p k x xs) + (requires + base' == base /\ + r' == R.ref_focus r (S.struct_field p k) /\ Ghost.reveal xs k == one (p k)) + (ensures fun _ -> True) += let r0' : R.ref base (p k) = coerce_eq () r' in + rewrite (r' `R.pts_to` _) (r0' `R.pts_to` x); + S.unaddr_of_struct_field k r0' r xs x + +let unstruct_field' + (#opened: _) + (#field_t: eqtype) + (#fields: field_description_gen_t field_t) + (#v: Ghost.erased (struct_t1 fields)) + (r: ref (struct1 fields)) + (field: field_t) + (#v': Ghost.erased (fields.fd_type field)) + (r': ref (fields.fd_typedef field)) +: STGhost unit opened + (has_struct_field1 r field r' `star` pts_to r v `star` pts_to r' v') + (fun _ -> has_struct_field1 r field r' `star` pts_to r (t_struct_set_field field v' v)) + ( + t_struct_get_field v field == unknown (fields.fd_typedef field) + ) + (fun _ -> True) += rewrite (has_struct_field1 r field r') (has_struct_field0 r field r'); + let _ = gen_elim () in + let w = vpattern_replace (HR.pts_to r _) in + let w' = vpattern_replace (HR.pts_to r' _) in + rewrite (pts_to r v) (pts_to0 r v); + let _ = gen_elim () in + hr_gather w r; + let rr : R.ref w.base (struct_pcm fields) = get_ref r in + rewrite (pts_to r' v') (pts_to0 r' v'); + let _ = gen_elim () in + hr_gather w' r'; + let rr' = get_ref r' in + r_unaddr_of_struct_field (struct_field_pcm fields) field rr' rr v v'; + assert (t_struct_set_field field v' v `FX.feq` S.struct_with_field (struct_field_pcm fields) field v' v); + hr_share r; + rewrite (has_struct_field0 r field r') (has_struct_field1 r field r'); + pts_to_intro_rewrite r rr _ + +let full_struct_gen + (#field_t: eqtype) + (#fields: field_description_gen_t field_t) + (s: struct_t1 fields) +: Lemma + (full (struct1 fields) s <==> (forall field . full (fields.fd_typedef field) (s field))) += + let is_unit' + (f': field_t) + (x: (fields.fd_type f')) + : Lemma + (let p = (fields.fd_typedef f').pcm in + composable p x (one p) /\ + op p x (one p) == x + ) + = is_unit (fields.fd_typedef f').pcm x + in + Classical.forall_intro_2 is_unit'; + let prf + (field: field_t) + : Lemma + (requires (full (struct1 fields) s)) + (ensures (full (fields.fd_typedef field) (s field))) + = let prf' + (x: fields.fd_type field) + : Lemma + (requires (composable (fields.fd_typedef field).pcm (s field) x)) + (ensures (x == one (fields.fd_typedef field).pcm)) + = let s' = t_struct_set_field field x (one (struct_pcm fields)) in + assert (composable (struct_pcm fields) s s') + in + Classical.forall_intro (Classical.move_requires prf') + in + Classical.forall_intro (Classical.move_requires prf) diff --git a/src/proofs/steelc/Steel.ST.C.Types.Struct.fst b/src/proofs/steelc/Steel.ST.C.Types.Struct.fst new file mode 100644 index 000000000..a0cd5afca --- /dev/null +++ b/src/proofs/steelc/Steel.ST.C.Types.Struct.fst @@ -0,0 +1,115 @@ +module Steel.ST.C.Types.Struct +open Steel.ST.GenElim +friend Steel.ST.C.Types.Base +friend Steel.ST.C.Types.Struct.Aux +open Steel.ST.C.Types.Struct.Aux + +module FX = FStar.FunctionalExtensionality + +let define_struct0 _ _ _ = unit + +let nonempty_field_description_nonempty + (#tf: Type) + (fd: nonempty_field_description_t tf) +: Lemma + (exists (f: field_t fd) . True) += if StrongExcludedMiddle.strong_excluded_middle (exists (f: field_t fd) . True) + then () + else begin + let prf + (f: string) + : Lemma + (fd.fd_def f == false) + = if fd.fd_def f + then Classical.exists_intro (fun (f: field_t fd) -> True) f + else () + in + Classical.forall_intro prf + end + +[@@noextract_to "krml"] +let fd_gen_of_nonempty_fd (#tf: Type0) (fd: nonempty_field_description_t tf) : Tot (field_description_gen_t (field_t fd)) = { + fd_nonempty = nonempty_field_description_nonempty fd; + fd_type = fd.fd_type; + fd_typedef = (fun (s: field_t fd) -> fd.fd_typedef s); +} + +let struct_t0 _ n fields = + struct_t1 (fd_gen_of_nonempty_fd fields) + +let struct_set_field + f v s += t_struct_set_field f v s + +let struct_get_field + s field += t_struct_get_field s field + +let struct_eq + s1 s2 += s1 `FX.feq` s2 + +let struct_get_field_same + s field v += () + +let struct_get_field_other + s field v field' += () + +let struct0 _ _ _ = struct1 _ + +let struct_get_field_unknown + tn n fields field += () + +let struct_get_field_uninitialized + tn n fields field += () + +let has_struct_field + r field r' += has_struct_field1 r field r' + +let has_struct_field_dup + r field r' += has_struct_field_dup' r field r' + +let has_struct_field_inj + r field r1 r2 += has_struct_field_inj' r field r1 r2 + +let has_struct_field_equiv_from + r1 field r' r2 += has_struct_field_equiv_from' r1 field r' r2 + +let has_struct_field_equiv_to + r field r1 r2 += has_struct_field_equiv_to' r field r1 r2 + +let ghost_struct_field_focus + r field r' += noop (); // FIXME: WHY WHY WHY? without this noop, z3 fails to prove precondition of field_description_t.fd_typedef . But also works if I put noop () after the function call + ghost_struct_field_focus' r field r' + +let ghost_struct_field + r field += noop (); // FIXME: WHY WHY WHY? (same as ghost_struct_field_focus above) + ghost_struct_field' r field + +let struct_field0 + t' #_ #_ #v r field td' += let r1' = struct_field' r field in + let r' : ref td' = r1' in + rewrite (pts_to r1' _) (pts_to r' (struct_get_field v field)); + rewrite (has_struct_field1 _ _ _) (has_struct_field r field r'); + return r' + +let unstruct_field + r field r' += unstruct_field' r field r' + +let fractionable_struct _ = () +let mk_fraction_struct _ _ _ = () + +let full_struct s = full_struct_gen s diff --git a/src/proofs/steelc/Steel.ST.C.Types.Union.fst b/src/proofs/steelc/Steel.ST.C.Types.Union.fst new file mode 100644 index 000000000..84810cad0 --- /dev/null +++ b/src/proofs/steelc/Steel.ST.C.Types.Union.fst @@ -0,0 +1,445 @@ +module Steel.ST.C.Types.Union +open Steel.ST.GenElim +friend Steel.ST.C.Types.Base +open Steel.ST.C.Types.Fields +open Steel.ST.C.Types.Scalar + +open Steel.C.Model.PCM + +module GHR = Steel.ST.GhostHigherReference +module R = Steel.ST.C.Model.Ref +module HR = Steel.ST.HigherReference +module U = Steel.ST.C.Model.Union + +let define_union0 _ _ _ = unit + +[@@noextract_to "krml"] // proof-only +let union_field_t + (#t: Type) + (fd: field_description_t t) +: Tot Type0 += option (field_t fd) + +[@@noextract_to "krml"] // proof-only +let union_field_type + (#t: Type) + (fd: field_description_t t) + (field: union_field_t fd) +: Tot Type0 += match field with + | None -> scalar_t unit + | Some f -> fd.fd_type f + +[@@noextract_to "krml"] // proof-only +let union_field_typedef + (#t: Type) + (fd: field_description_t t) + (field: union_field_t fd) +: Tot (typedef (union_field_type fd field)) += match field with + | None -> scalar unit + | Some f -> fd.fd_typedef f + +[@@noextract_to "krml"] // proof-only +let union_field_pcm + (#t: Type) + (fd: field_description_t t) + (field: union_field_t fd) +: Tot (pcm (union_field_type fd field)) += (union_field_typedef fd field).pcm + +let union_t0 + tn n fields += U.union (union_field_pcm fields) + +let union_set_field + tn n fields f v += U.field_to_union_f (union_field_pcm fields) (Some f) v + +let union_get_case + u += match U.case_of_union _ u with + | None -> None + | Some s -> s + +let union_get_field + u field += U.union_to_field_f _ (Some field) u + +let union_get_field_same + tn n fields field v += () + +module FX = FStar.FunctionalExtensionality + +let union_set_field_same + #tn #_ #n #fields s field += assert (union_set_field tn n fields field (union_get_field s field) `FX.feq` s) + +let union_fractionable + (#tn: Type0) (#tf: Type0) (#n: string) (#fields: field_description_t tf) + (s: union_t0 tn n fields) +: GTot bool += match U.case_of_union (union_field_pcm fields) s with + | Some f -> (union_field_typedef fields f).fractionable (s f) + | _ -> true + +let union_fractionable_fields + (#tn: Type0) (#tf: Type0) (#n: string) (#fields: field_description_t tf) + (s: union_t0 tn n fields) + (f: union_field_t fields) +: Lemma + (requires (union_fractionable s)) + (ensures (fractionable (union_field_typedef fields f) (s f))) += () + +[@@noextract_to "krml"] // proof-only +let union_mk_fraction + (#tn: Type0) (#tf: Type0) (#n: string) (#fields: field_description_t tf) + (s: union_t0 tn n fields) + (p: P.perm) +: Pure (union_t0 tn n fields) + (requires (union_fractionable s)) + (ensures (fun s' -> p `P.lesser_equal_perm` P.full_perm ==> union_fractionable s')) += let prf + (f: union_field_t fields) + : Lemma + (let u = one (union_field_typedef fields f).pcm in + (union_field_typedef fields f).mk_fraction u p == u + ) + = (union_field_typedef fields f).mk_fraction_one p + in + Classical.forall_intro prf; + FX.on_dom (union_field_t fields) (fun f -> + (union_field_typedef fields f).mk_fraction (s f) p + ) + +[@@noextract_to "krml"] // proof-only +let union_pcm + (tn: Type0) (#tf: Type0) (n: string) (fields: field_description_t tf) +: Tot (pcm (union_t0 tn n fields)) += U.union_pcm (union_field_pcm fields) + +let union_eq_intro + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (s1 s2: union_t0 tn n fields) + (prf: ( + (f: union_field_t fields) -> + Lemma + (s1 f == s2 f) + )) +: Lemma + (s1 == s2) += Classical.forall_intro prf; + assert (s1 `FX.feq` s2) + +[@@noextract_to "krml"] // proof-only +let union_uninitialized + (tn: Type0) (#tf: Type0) (n: string) (fields: field_description_t tf) +: Pure (union_t0 tn n fields) + (requires True) + (ensures (fun y -> exclusive (union_pcm tn n fields) y /\ p_refine (union_pcm tn n fields) y)) += let y : union_t0 tn n fields = + U.field_to_union_f (union_field_pcm fields) None (scalar unit).uninitialized + in + U.exclusive_union_intro (union_field_pcm fields) y None; + y + +#push-options "--z3rlimit 16" + +#restart-solver +let union0 + tn n fields += { + pcm = union_pcm tn n fields; + fractionable = union_fractionable; + mk_fraction = union_mk_fraction; + mk_fraction_full = (fun x -> + union_eq_intro (union_mk_fraction x P.full_perm) x (fun f -> + (union_field_typedef fields f).mk_fraction_full (x f) + ) + ); + mk_fraction_compose = (fun x p1 p2 -> + union_eq_intro (union_mk_fraction (union_mk_fraction x p1) p2) (union_mk_fraction x (p1 `prod_perm` p2)) (fun f -> + union_fractionable_fields x f; + (union_field_typedef fields f).mk_fraction_compose (x f) p1 p2 + ) + ); + fractionable_one = (); + mk_fraction_one = (fun p -> + union_eq_intro (union_mk_fraction (one (union_pcm tn n fields)) p) (one (union_pcm tn n fields)) (fun f -> + (union_field_typedef fields f).mk_fraction_one p + ) + ); + uninitialized = union_uninitialized _ _ _; + mk_fraction_split = (fun v p1 p2 -> + U.union_comp_intro (union_field_pcm fields) (union_mk_fraction v p1) (union_mk_fraction v p2) (fun j k -> + (union_field_typedef fields j).mk_fraction_one p1; + (union_field_typedef fields k).mk_fraction_one p2; + assert (j == k); + (union_field_typedef fields j).mk_fraction_split (v j) p1 p2 + ) + ); + mk_fraction_join = (fun v p1 p2 -> + union_eq_intro (op (union_pcm tn n fields) (union_mk_fraction v p1) (union_mk_fraction v p2)) (union_mk_fraction v (p1 `P.sum_perm` p2)) (fun f -> + (union_field_typedef fields f).mk_fraction_join (v f) p1 p2 + ) + ); + mk_fraction_eq_one = (fun v p -> + union_eq_intro v (one (union_pcm tn n fields)) (fun f -> + (union_field_typedef fields f).mk_fraction_eq_one (v f) p + ) + ); + mk_fraction_full_composable = (fun v1 p1 v2 p2 -> + let co1 = U.case_of_union _ v1 in + let co2 = U.case_of_union _ v2 in + assert (Some? co1); + assert (Some? co2); + let Some c1 = co1 in + let Some c2 = co2 in + U.exclusive_union_elim (union_field_pcm fields) v1 c1; + U.exclusive_union_elim (union_field_pcm fields) v2 c2; + Classical.move_requires ((union_field_typedef fields c1).mk_fraction_eq_one (v1 c1)) p1; + Classical.move_requires ((union_field_typedef fields c2).mk_fraction_eq_one (v2 c2)) p2; + U.union_comp_elim0 (union_field_pcm fields) (union_mk_fraction v1 p1) (union_mk_fraction v2 p2) c1 c2; + assert (c1 == c2); + (union_field_typedef fields c1).mk_fraction_full_composable (v1 c1) p1 (v2 c1) p2; + assert (v1 `FX.feq` v2) + ); +} + +#pop-options + +let union_get_case_unknown + tn n fields += () + +let union_set_field_unknown + tn n fields field += () + +let union_get_case_uninitialized + tn n fields += () + +let mk_fraction_union_get_case + #tn #_ #n #fields s p += match U.case_of_union (union_field_pcm fields) s with + | None -> (union0 tn n fields).mk_fraction_one p + | Some f -> + Classical.move_requires ((union_field_typedef fields f).mk_fraction_eq_one (s f)) p + +let fractionable_union_get_field + s field += () + +let mk_fraction_union_get_field + s p field += () + +let mk_fraction_union_set_field + tn n fields field v p += + assert (fractionable (union0 tn n fields) (union_set_field tn n fields field v)); + let prf + (f: union_field_t fields) + : Lemma + (let u = one (union_field_typedef fields f).pcm in + (union_field_typedef fields f).mk_fraction u p == u + ) + = (union_field_typedef fields f).mk_fraction_one p + in + Classical.forall_intro prf; + assert (mk_fraction (union0 tn n fields) (union_set_field tn n fields field v) p `FX.feq` union_set_field tn n fields field (mk_fraction (fields.fd_typedef field) v p)) + +let full_union + #_ #_ #_ #fields s field += Classical.move_requires (U.exclusive_union_intro (union_field_pcm fields) s) (Some field); + Classical.move_requires (U.exclusive_union_elim (union_field_pcm fields) s) (Some field) + +[@@__reduce__] +let has_union_field0 + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (r: ref (union0 tn n fields)) + (field: field_t fields) + (r': ref (fields.fd_typedef field)) +: Tot vprop += has_focus_ref0 r (U.union_field (union_field_pcm fields) (Some field)) r' + +[@@__reduce__] +let has_union_field05 + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (r: ref (union0 tn n fields)) + (field: field_t fields) + (r': ref (fields.fd_typedef field)) +: Tot vprop += has_focus_ref r (U.union_field (union_field_pcm fields) (Some field)) r' + +let has_union_field + r field r' += has_union_field0 r field r' + +let has_union_field_dup + r field r' += rewrite (has_union_field r field r') (has_union_field05 r field r'); + has_focus_ref_dup r _ r'; + rewrite (has_union_field05 r field r') (has_union_field r field r'); + rewrite (has_union_field05 r field r') (has_union_field r field r') + +let has_union_field_inj + #_ #tn #_ #n r field r1 r2 += rewrite (has_union_field r field r1) (has_union_field05 r field r1); + rewrite (has_union_field r field r2) (has_union_field05 r field r2); + has_focus_ref_inj r _ r1 r2; + rewrite (has_union_field05 r field r1) (has_union_field r field r1); + rewrite (has_union_field05 r field r2) (has_union_field r field r2) + +let has_union_field_equiv_from + r1 r2 field r' += rewrite (has_union_field r1 field r') (has_union_field05 r1 field r'); + has_focus_ref_equiv_from r1 _ r' r2; + rewrite (has_union_field05 r2 field r') (has_union_field r2 field r') + +let has_union_field_equiv_to + r field r1' r2' += rewrite (has_union_field r field r1') (has_union_field05 r field r1'); + has_focus_ref_equiv_to r _ r1' r2'; + rewrite (has_union_field05 r field r2') (has_union_field r field r2') + +let ghost_union_field_focus + #_ #tn #_ #n #fields #v r field r' += rewrite (has_union_field r field r') (has_union_field0 r field r'); + let _ = gen_elim () in + let w = vpattern_replace (HR.pts_to r _) in + let w' = vpattern_replace (HR.pts_to r' _) in + rewrite (pts_to r v) (pts_to0 r v); + let _ = gen_elim () in + hr_gather w r; + let rr = get_ref r in + let v' = U.field_to_union_f (union_field_pcm fields) (Some field) (union_get_field v field) in + assert (v' `FX.feq` v); + R.gfocus rr (U.union_field (union_field_pcm fields) (Some field)) v (union_get_field v field); +// let rr' = get_ref r' in + hr_share r'; +// pts_to_intro_rewrite r' rr' _ ; + pts_to_intro_rewrite r' _ _ ; + rewrite (has_union_field0 r field r') (has_union_field r field r') + +let ghost_union_field + #_ #tn #_ #n #fields #v r field += let r' = ghost_focus_ref r (fields.fd_typedef field) (U.union_field (union_field_pcm fields) (Some field)) in + rewrite (has_union_field05 r field r') (has_union_field r field r'); + ghost_union_field_focus r field r'; + r' + +[@@noextract_to "krml"] // primitive +let union_field' + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (#v: Ghost.erased (union_t0 tn n fields)) + (r: ref (union0 tn n fields)) + (field: field_t fields {union_get_case v == Some field}) +: STT (ref (fields.fd_typedef field)) + (pts_to r v) + (fun r' -> has_union_field r field r' `star` pts_to r' (union_get_field v field)) += let r' = focus_ref r (fields.fd_typedef field) (U.union_field (union_field_pcm fields) (Some field)) in + rewrite (has_union_field05 r field r') (has_union_field r field r'); + ghost_union_field_focus r field r'; + return r' + +let union_field0 + t' r field td' += + let r' = union_field' r field in + let res : ref td' = r' in + rewrite (pts_to r' _) (pts_to res _); + rewrite (has_union_field r field _) (has_union_field r field res); + return res + +#push-options "--z3rlimit 16" +#restart-solver + +let ununion_field + #_ #tn #_ #n #fields r field #v' r' += rewrite (has_union_field r field r') (has_union_field0 r field r'); + let _ = gen_elim () in + let w = vpattern_replace (HR.pts_to r _) in + let w' = vpattern_replace (HR.pts_to r' _) in + rewrite (pts_to r' v') (pts_to0 r' v'); + let _ = gen_elim () in + hr_gather w' r'; + let rr : R.ref w.base (union0 tn n fields).pcm = coerce_eq () w.ref in + let rr' = get_ref r' in + let x = r_unfocus rr' rr (coerce_eq () (U.union_field (union_field_pcm fields) (Some field))) _ in + hr_share r; + rewrite (has_union_field0 r field r') (has_union_field r field r'); + pts_to_intro_rewrite r rr #x _ + +#pop-options + +[@@noextract_to "krml"] // primitive +let union_switch_field' + (#tn: Type0) + (#tf: Type0) + (#n: string) + (#fields: field_description_t tf) + (#v: Ghost.erased (union_t0 tn n fields)) + (r: ref (union0 tn n fields)) + (field: field_t fields) +: ST (ref (fields.fd_typedef field)) + (pts_to r v) + (fun r' -> has_union_field r field r' `star` pts_to r' (uninitialized (fields.fd_typedef field))) + (full (union0 tn n fields) v) + (fun _ -> True) += rewrite (pts_to r v) (pts_to0 r v); + let _ = gen_elim () in + let w = HR.read r in + vpattern_rewrite (HR.pts_to r _) w; + let rr = read_ref r in + let v' : union_t0 tn n fields = U.field_to_union_f (union_field_pcm fields) (Some field) (fields.fd_typedef field).uninitialized in + R.ref_upd rr _ _ (R.base_fpu (union_pcm tn n fields) _ v'); + pts_to_intro r _ _ rr v' ; + let r' = union_field' r field in + rewrite (pts_to r' _) (pts_to r' (uninitialized (fields.fd_typedef field))); + return r' + +[@@noextract_to "krml"] // primitive +let union_switch_field0' + (#tn: Type0) + (#tf: Type0) + (t': Type0) + (#n: string) + (#fields: field_description_t tf) + (#v: Ghost.erased (union_t0 tn n fields)) + (r: ref (union0 tn n fields)) + (field: field_t fields) + (td': typedef t') + (sq: squash ( + t' == fields.fd_type field /\ + td' == fields.fd_typedef field + )) +: ST (ref td') // need to write the pcm carrier value, so this cannot be Ghost or Atomic + (pts_to r v) + (fun r' -> has_union_field r field (coerce_eq () r') `star` pts_to r' (Ghost.hide (coerce_eq () (uninitialized (fields.fd_typedef field))))) + (full (union0 tn n fields) v) + (fun _ -> True) += let r' = union_switch_field' #tn #tf #n #fields #v r field in + let res : ref td' = r' in + rewrite (pts_to r' _) (pts_to res (Ghost.hide (coerce_eq () (uninitialized (fields.fd_typedef field))))); + rewrite (has_union_field r field _) (has_union_field r field (coerce_eq () res)); + return res + +let union_switch_field0 + t' r field td' += union_switch_field0' t' r field td' () diff --git a/src/proofs/steelc/Steel.ST.C.Types.UserStruct.fst b/src/proofs/steelc/Steel.ST.C.Types.UserStruct.fst new file mode 100644 index 000000000..5c00a8a7b --- /dev/null +++ b/src/proofs/steelc/Steel.ST.C.Types.UserStruct.fst @@ -0,0 +1,204 @@ +module Steel.ST.C.Types.UserStruct +open Steel.ST.GenElim +open Steel.ST.C.Types.Base +module RW = Steel.ST.C.Types.Rewrite +module MRW = Steel.ST.C.Model.Rewrite +module S = Steel.ST.C.Types.Struct.Aux +module FX = FStar.FunctionalExtensionality +module Conn = Steel.C.Model.Connection +module MS = Steel.ST.C.Model.Struct + +friend Steel.ST.C.Types.Base +friend Steel.ST.C.Types.Struct.Aux +friend Steel.ST.C.Types.Rewrite + +[@@noextract_to "krml"] +let rewrite_from_struct + (#t: Type) + (sd: struct_def t) + (f: S.struct_t1 sd.field_desc) +: Tot t += sd.mk f + +[@@noextract_to "krml"] +let rewrite_to_struct + (#t: Type) + (sd: struct_def t) + (f: t) +: Tot (S.struct_t1 sd.field_desc) += FX.on_dom (field_t sd.fields) (sd.get f) + +let rewrite_forall_struct + (#t: Type) + (sd: struct_def t) +: Lemma + (MRW.rewrite_forall_from (rewrite_from_struct sd) (rewrite_to_struct sd)) += MRW.rewrite_forall_from_intro (rewrite_from_struct sd) (rewrite_to_struct sd) (fun (x: S.struct_t1 sd.field_desc) -> + Classical.forall_intro (sd.get_mk x); + assert (x `FX.feq` rewrite_to_struct sd (sd.mk x)) + ) + +let rewrite_forall_t + (#t: Type) + (sd: struct_def t) +: Lemma + (MRW.rewrite_forall_to (rewrite_from_struct sd) (rewrite_to_struct sd)) += MRW.rewrite_forall_to_intro (rewrite_from_struct sd) (rewrite_to_struct sd) (fun (x: t) -> + sd.extensionality (rewrite_from_struct sd (rewrite_to_struct sd x)) x (fun (f: field_t sd.fields) -> + sd.get_mk (rewrite_to_struct sd x) f + ) + ) + +[@@noextract_to "krml"] +let rewrite_struct + (#t: Type) + (sd: struct_def t) +: Tot (MRW.rewrite_elts (S.struct_t1 sd.field_desc) t) += { + rewrite_from_to = rewrite_from_struct sd; + rewrite_to_from = rewrite_to_struct sd; + rewrite_equiv = begin + rewrite_forall_struct sd; + rewrite_forall_t sd; + () + end; + } + +let struct_typedef sd = + S.struct1 sd.field_desc `RW.rewrite_typedef` rewrite_struct sd + +let iso_to_struct + (#t: Type) + (sd: struct_def t) +: Tot (Conn.isomorphism (struct_typedef sd).pcm (S.struct1 sd.field_desc).pcm) += coerce_eq () (Conn.isomorphism_inverse (MRW.rewrite_iso (S.struct_pcm sd.field_desc) (rewrite_struct sd))) // without the coercion, F* blows up memory + +let conn_to_struct + (#t: Type) + (sd: struct_def t) +: Tot (Conn.connection (struct_typedef sd).pcm (S.struct1 sd.field_desc).pcm) += Conn.connection_of_isomorphism (iso_to_struct sd) + +let conn_struct_field + (#t: Type) + (sd: struct_def t) + (field: field_t sd.fields) +: Tot (Conn.connection (S.struct1 sd.field_desc).pcm (sd.field_desc.fd_typedef field).pcm) += MS.struct_field (struct_field_pcm sd.field_desc) field + +[@@__reduce__] +let has_struct_field0 + (#t: Type) + (#sd: struct_def t) + (r: ref (struct_typedef sd)) + (field: field_t sd.fields) + (r': ref (sd.field_desc.fd_typedef field)) +: Tot vprop += has_focus_ref r (conn_to_struct sd `Conn.connection_compose` conn_struct_field sd field) r' + +let has_struct_field + r field r' += has_struct_field0 r field r' + +let has_struct_field_dup + r field r' += rewrite (has_struct_field r field r') (has_struct_field0 r field r'); + has_focus_ref_dup r _ r'; + rewrite (has_struct_field0 r field r') (has_struct_field r field r'); + rewrite (has_struct_field0 r field r') (has_struct_field r field r') + +let has_struct_field_inj + r field r1 r2 += rewrite (has_struct_field r field r1) (has_struct_field0 r field r1); + rewrite (has_struct_field r field r2) (has_struct_field0 r field r2); + has_focus_ref_inj r _ r1 r2; + rewrite (has_struct_field0 r field r1) (has_struct_field r field r1); + rewrite (has_struct_field0 r field r2) (has_struct_field r field r2) + +let has_struct_field_equiv_from + r1 field r' r2 += rewrite (has_struct_field r1 field r') (has_struct_field0 r1 field r'); + has_focus_ref_equiv_from r1 _ r' r2; + rewrite (has_struct_field0 r2 field r') (has_struct_field r2 field r') + +let has_struct_field_equiv_to + r field r1' r2' += rewrite (has_struct_field r field r1') (has_struct_field0 r field r1'); + has_focus_ref_equiv_to r _ r1' r2'; + rewrite (has_struct_field0 r field r2') (has_struct_field r field r2') + +#push-options "--z3rlimit 16" +#restart-solver + +let ghost_struct_field_focus + #_ #_ #sd #v r field r' += rewrite (has_struct_field r field r') (has_struct_field0 r field r'); + let r1 = ghost_focus_ref r (struct1 sd.field_desc) (conn_to_struct sd) in + has_focus_ref_compose_12_13 r _ r1 _ r'; + let v1 = focus_ref_iso r r1 _ in + S.ghost_struct_field_focus' r1 field r'; + drop (has_focus_ref r1 _ _); + let v' = unfocus_ref r r1 _ in + drop (has_focus_ref _ _ r1); + rewrite (has_struct_field0 r field r') (has_struct_field r field r'); + sd.extensionality v' (set sd v field (unknown (sd.field_desc.fd_typedef field))) (fun f' -> sd.get_mk v1 f'); + noop (); + rewrite (pts_to r _) (pts_to r _); + rewrite (pts_to r' _) (pts_to r' _) + +#pop-options + +let ghost_struct_field + #_ #_ #sd r field += let r' = ghost_focus_ref r (sd.field_desc.fd_typedef field) (conn_to_struct sd `Conn.connection_compose` conn_struct_field sd field) in + rewrite (has_struct_field0 r field r') (has_struct_field r field r'); + ghost_struct_field_focus r field r'; + r' + +let struct_field_1 + (#t: Type) + (#sd: struct_def t) + (#v: Ghost.erased t) + (r: ref (struct_typedef sd)) + (field: field_t sd.fields) +: STT (ref (sd.field_desc.fd_typedef field)) + (pts_to r v) + (fun r' -> has_struct_field r field r' `star` pts_to r (set sd v field (unknown (sd.field_desc.fd_typedef field))) `star` pts_to r' (sd.get v field)) += let r' = focus_ref r (sd.field_desc.fd_typedef field) (conn_to_struct sd `Conn.connection_compose` conn_struct_field sd field) in + rewrite (has_struct_field0 r field r') (has_struct_field r field r'); + ghost_struct_field_focus r field r'; + return r' + +let struct_field0 + #t _ #sd #v r field td' += let r1' = struct_field_1 #t #sd #v r field in + let r' : ref td' = coerce_eq () r1' in + rewrite (pts_to r1' _) (pts_to #_ #(sd.field_desc.fd_typedef field) (coerce_eq () r') (sd.get (Ghost.reveal v) field)); + rewrite (has_struct_field _ _ _) (has_struct_field r field (coerce_eq () r')); + return r' + +#push-options "--z3rlimit 16" +#restart-solver + +let unstruct_field + #_ #_ #sd #v r field #v' r' += rewrite (has_struct_field r field r') (has_struct_field0 r field r'); + let r1 = ghost_focus_ref r (struct1 sd.field_desc) (conn_to_struct sd) in + has_focus_ref_compose_12_13 r _ r1 _ r'; + let v1 = focus_ref_iso r r1 _ in + S.unstruct_field' r1 field r'; + drop (has_focus_ref r1 _ _); + let vf = unfocus_ref r r1 _ in + drop (has_focus_ref _ _ r1); + rewrite (has_struct_field0 r field r') (has_struct_field r field r'); + sd.extensionality vf (set sd v field v') (fun f' -> sd.get_mk v1 f'); + noop (); + rewrite (pts_to r _) (pts_to r _) + +#pop-options + +let fractionable_struct sd s = () + +let mk_fraction_struct sd s p field = () + +let full_struct sd s = ()