From fa316e4f37137897af803f6ab0f7761f9026f9a2 Mon Sep 17 00:00:00 2001
From: Mark Gritter temporarily fixing the universe of this lemma to u#1 because
+otherwise tactics for LowStar.Resource canonicalization fails
+by picking up an incorrect universe u#0 for resource type fsdoc: no-summary-found fsdoc: no-comment-found fsdoc: no-summary-found fsdoc: no-comment-found Opens module FStar.Classical Aliases module FStar.PropositionalExtensionality as AR: 05/12: adding calls to equational lemmas from PropositionalExtensionality Definition of a monoid Some monoid structures let int_mul_monoid : monoid int = intro_monoid int 1 op_Multiply Definition of a morphism of monoid Definition of a left action mf ought to be a monoid morphism but we don't use this fact in the property fsdoc: no-summary-found fsdoc: no-comment-found fsdoc: no-summary-found fsdoc: no-comment-found This module defines an abstract type of length-indexed bit
+vectors. The type and its operations are handled primitively in
+F*'s SMT encoding, which maps them to the SMT sort of bit vectors
+and operations on that sort. One way to use this module is in conjuction with
+FStar.Tactics.BV. Its main tactic, for now just opening this for logand, logxor, etc. but we need a better solution. The main type of this module, bit vectors of length Experimental:
+Redefining basic type from UInt to avoid importing UInt
+Reduces verification time by 50% in small examples
+// let max_int (n:nat) : Tot int = pow2 n - 1
+// let min_int (n:nat) : Tot int = 0
+// let fits (x:int) (n:nat) : Tot bool = min_int n <= x && x <= max_int n
+// let size (x:int) (n:nat) : Tot Type0 = b2t(fits x n)
+// type uint_t' (n:nat) = x:int{size x n} Extending a bit vector of length Mapping a bounded unsigned integer of size Mapping a bit vector back to a bounded unsigned integer of size [<
+2^n] Mapping a list of booleans to a bitvector Mapping a bitvector to a list of booleans Bitwise conjunction Bitwise exclusive or Bitwise disjunction Bitwise negation Bitwise shift left Bitwise shift right Inequality on bitvectors Addition Subtraction Division Modulus Multiplication modulo fsdoc: no-summary-found fsdoc: no-comment-found This module aggregates commonly used primitive type constants into
+a single module, providing abbreviations for them. fsdoc: no-summary-found fsdoc: no-comment-found This library provides propositional connectives over finite sets
+expressed as lists, aka "big operators", in analogy with LaTeX
+usage for \bigand, \bigor, etc. The library is designed with a dual usage in mind: In this latter form, partially computing We control reduction using the We wrap A useful lemma to relate terms to their implicilty reducing variants A utility that combines map and fold: Equations for Equations for Equations for Equations for Note: defining This is in part because the The discrepancy means that I preferred to prove these
+operators in Interpreting the finite conjunction Equations for Equations for Interpreting the finite disjunction We provide functions to apply a reflexive, symmetric binary
+operator to elements in a list Mapping pairs of elements of Equations for Equations for Equations for Equations for fsdoc: no-summary-found fsdoc: no-comment-found is_subset_vec is the property that the zero bits of b are also zero in a. I.e. that a is a subset of b. is_superset_vec is the property that the non-zero bits of b are also non-zero in a. I.e. that a is a superset of b. lemma_slice_subset_vec proves that the subset property is conserved in subslices. lemma_slice_superset_vec proves that the superset property is conserved in subslices. This module defines a bit vector as a sequence of booleans of a
+given length, and provides various utilities. NOTE: THE TYPE TODO: We might rename this module to FStar.Seq.Boolean? A length A vector of length A length Bitwise logical and Bitwise logical exclusive or Bitwise logical or Bitwise negation Bitwise lemmas If both The property that the zero bits of b are also zero in a.
+I.e. that a is a subset of b. The property that the non-zero bits of b are also non-zero in a.
+I.e. that a is a superset of b. Proves that the subset property is conserved in subslices. Proves that the superset property is conserved in subslices. Note: the shift amount is extracted as a bitvector
+NS: Not sure what this remark means. Shift The fill bits of a shift left are zero Relating the indexes of the shifted vector to the original Shift The fill bits of a shift right are zero Relating the indexes of the shifted vector to the original Arithmetic shift right of The fill bits of arithmetic shift right is the value of its
+most-significant bit (position zero) Relating the indexes of the shifted vector to the original fsdoc: no-summary-found fsdoc: no-comment-found A standard library for manipulation of value bytes. This model is realized by Bytes.bytes in OCaml and by
+struct {uintX_t size; char *bytes} (or similar) in C. This file is essentially a specialized version of FStar.Seq,
+with lemmas and refinements taylored for typical operations on
+bytes, and with support for machine integers and C-extractible versions
+(which Seq does not provide.) @summary Value bytes standard library Realized by uint8_t in C and int in OCaml (char does not have necessary operators...) Realized in C by a pair of a length field and uint8_t* in C Realized in OCaml by a string Realized in C by a pair of a length field and uint8_t* in C
+Realized in OCaml by a string representation for specs that need lemmas not defined here. If you statically know the length, it is OK to read at arbitrary indexes creating byte values * this is a hack JROESCH
+admit () create 1ul b init 2ul (fun i -> if i = 0ul then fst b else snd b) appending bytes * Interpret a sequence of bytes as a mathematical integer encoded in big endian * repr_bytes n: The number of bytes needed to represent a nat * 18-02-25 use ////////////////////////////////////////////////////////////////////////////// missing post on the length of the results (exact on constant arguments) A better implementation of BufferBytes, formerly found in miTLS Aliases module LowStar.Buffer as Aliases module LowStar.Modifies as Opens module FStar.HyperStack.ST JP: let's not add from_bytes here because we want to leave it up to the
+caller to allocate on the stack or on the heap fsdoc: no-summary-found fsdoc: no-comment-found GM: The Every chain of Need to annotate #p seemingly due to #1486 let _calc_finish (#t:Type) (#rs : list (relation t)) (p : relation t) (#x #y : t) (pf : unit -> calc_proof rs x y) : Lemma (requires (norm iota; zeta] (calc_chain_compatible rs p))) (ensures (p x y)) = elim_calc_proof rs (pf ()) fsdoc: no-summary-found fsdoc: no-comment-found This module provides the UTF-8 characters are representing in a variable-length encoding of
+between 1 and 4 bytes, with a maximum of 21 bits used to represent
+a code. See https://en.wikipedia.org/wiki/UTF-8 and
+https://erratique.ch/software/uucp/doc/unicode.html A A primitive to extract the A primitive to promote a Encoding and decoding from Encoding and decoding from A couple of utilities to use mathematical integers rather than Case conversion This private primitive is used internally by the compiler to
+translate character literals with a desugaring-time check of the
+size of the number, rather than an expensive verifiation check.
+Since it is marked private, client programs cannot call it
+directly Since it is marked unfold, it eagerly reduces,
+eliminating the verification overhead of the wrapper 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 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. This module provides a few combinators that are targeted
+by the desugaring phase of the F* front end The combinators it provides are fairly standard, except for one
+subtlety. In F*, the typechecking of terms formed using the
+logical connectives is biased from left to right. That is: In In So, many of these combinators reflect that bias by taking as
+instantiations for The other subtlety is that the when using these combinators, we
+encapsulate any proof terms provided by the caller within a
+thunk. This is to ensure that if, for instance, the caller simply
+admits a goal, that they do not inadvertently discard any proof
+obligations in the remainder of their programs. For example, consider the difference between and In (1) the proof of rest is admitted also. Eliminate a universal quantifier by providing an instantiation Eliminate an existential quantifier into a proof of a goal Eliminate an implication, by providing a proof of the hypothesis
+Note, the proof is thunked Eliminate a disjunction Eliminate a conjunction Introduce a universal quantifier Introduce an existential quantifier Introduce an implication Introduce an disjunction on the left Introduce an disjunction on the right Introduce a conjunction fsdoc: no-summary-found fsdoc: no-comment-found This module provides various utilities to manipulate the squashed
+logical connectives In summary: We provide several utilities to turn proofs of various
+propositions with non-trivial proof terms into proof-irrelevant,
+classical proofs. This turns a proof-irrelevant postcondition into a squashed proof Turning an equality precondition into returned squash proof,
+similar to Turning an The converse of Similar to Similar to A lemma with a precondition can also be treated as a proof a quantified implication. See the remark at the top of this section comparing nested lemmas
+with SMT pattern to The arity 2 version of The arity 3 version of When proving predicate Many of the utilities for universal quantification are designed to
+help in the proofs of lemmas that ensure quantified
+postconditions. For example, in order to prove [Lemma (forall
+(x:a). p x)] it is often useful to "get your hands" on a freshly
+introduced variable That said, it may often be more convenient to prove such
+properties using local lemmas in inner scopes. For example, here
+are two proof sketches for In In The style of That said, there may still be cases where Turning an universally quantified precondition into returned
+squash proof, similar to This introduces a squash proof of a universal
+quantifier. TODO: Perhaps remove this? It seems redundant This turns a dependent arrow into a proof-irrelevant postcondition
+of a universal quantifier. This turns a dependent arrow producing a proof a This is the analog of TODO: perhaps remove this? This is the analog of The main workhorse for introducing universally quantified postconditions, at arity 1. See the remark at the start of this section for guidelines on its
+use. You may prefer to use a local lemma with an SMT pattern. The main workhorse for introducing universally quantified
+postconditions, at arity 1, including a provision for a single
+pattern. See the remark at the start of this section for guidelines on its
+use. You may prefer to use a local lemma with an SMT pattern. This function is almost identical to However, sometimes it is convenient to introduce a quantifier from
+a lemma while relying on subtyping--- The arity 2 version of The arity 2 version of The arity 3 version of The arity 3 version of The arity 4 version of This combines th use of TODO: Seems overly specific; could be removed? This is similar to Note: It's unclear why The most basic way to introduce a squashed existential quantifier
+ While However, unlike with we do not yet provide any conveniences for
+higher arities. One workaround is to tuple witnesses together,
+e.g., instead of proving Introducing an exists via its classical correspondence with a negated universal quantifier If The arity two variant of TODO: overly specific, remove? An eliminator for squashed existentials: If every witnesse can be
+eliminated into a squashed proof of the Eliminating The law of excluded middle: squashed types are classical fsdoc: no-summary-found fsdoc: no-comment-found A module providing primitives for dates and times EXT marks an external function fsdoc: no-summary-found fsdoc: no-comment-found This module provides an abstract type of maps whose co-domain
+depends on the value of each key. i.e., it is an encapsulation
+of The main constructors of the type are: The main eliminators are: The interface is specified in a style that describes the action of
+each eliminator over each of the constructors The map also supports an extensional equality principle. Abstract type of dependent maps, with universe polymorphic values
+and keys in universe 0 with decidable equality Creating a new map from a function Querying the map for its value at a given key Updating a map at a point The action of selecting a key This is one of the classic McCarthy select/update axioms in the
+setting of a dependent map. The action of selecting a key This is one of the classic McCarthy select/update axioms in the
+setting of a dependent map. Extensional propositional equality on maps Introducing extensional equality by lifting equality on the map, pointwise Restricts the domain of the map to those keys satisfying The action of Concatenating I.e., the key space varies contravariantly, to take the union of
+the component key spaces. The co-domain is the dependent product
+of the co-domains of the original map The key space of a concatenated map is the product of the key spaces Concatenating maps The action of The action of Given a map from The type of the co-domain of the renamed map also involves
+transformation along the renaming function Renaming the keys of a map AR: wanted to write an SMTPatOr, but gives some error We seem to miss lemmas that relate map to the other constructors,
+including create, restrict etc. fsdoc: no-summary-found fsdoc: no-comment-found Dynamic casts, realized by OCaml's NOTE: THIS PROVIDES CASTS BETWEEN ARBITRARY TYPES
+BUT ONLY IN Promoting a value of type This coerces a value of type fsdoc: no-summary-found fsdoc: no-comment-found A library of lemmas for reasoning about sequences of machine integers and
+their (little|big)-endian representation as a sequence of bytes. The functions in this module aim to be as generic as possible, in order to
+facilitate compatibility with: To achieve maximum compatibility, we try to state most lemmas using nat
+rather than UIntX. To limit context pollution, the definitions of the recursive functions are
+abstract; please add lemmas as you see fit. In extreme cases, .. note:: This module supersedes the poorly-named This is our spec, to be audited. From bytes to nat. lt_to_n interprets a byte sequence as a little-endian natural number be_to_n interprets a byte sequence as a big-endian natural number Induction for le_to_n and be_to_n From nat to bytes, and their functional correctness. n_to_le encodes a number as a little-endian byte sequence of a fixed,
+sufficiently large length. n_to_be encodes a numbers as a big-endian byte sequence of a fixed,
+sufficiently large length These are useful because they take care of calling the right TODO: 16-bit (but is it really needed?)
+TODO: should these be specializations of generic functions that chop on
+N-byte boundaries, and operate on bounded nats instead of uints? TODO: this is fairly incomplete
+TODO: the *_base cases seem ad-hoc and derivable trivially from offset above; why have them? TODO: also incomplete (Needs SMTPats above for roundtripping in their proof, hence why they're at the end.) Some reasoning about zero bytes fsdoc: no-summary-found fsdoc: no-comment-found Providing the signature of fsdoc: no-summary-found fsdoc: no-comment-found This module is supposed to contain various lemmas about
+finiteness. For now, it mainly provides a basic pigeonhole
+principle TODO: We might generalize this to also support general utilities
+for reasoning about cardinality, relation with injections and
+surjections, etc. The type of natural numbers bounded by Length-indexed list Length-indexed sequence Find an index of an element in Given a sequence fsdoc: no-summary-found fsdoc: no-comment-found Support for floating point numbers in F* is nearly non-existent.
+This module is a placeholder fsdoc: no-summary-found fsdoc: no-comment-found Functional extensionality asserts the equality of pointwise-equal
+functions. The formulation of this axiom is particularly subtle in F* because
+of its interaction with subtyping. In fact, prior formulations of
+this axiom were discovered to be unsound by Aseem Rastogi. The predicate However, due to subtyping For more context on how functional extensionality works in F*, see The type of total, dependent functions Using feq #a #b f g: pointwise equality of This is a key function provided by the module. It has several
+features. Intuitively, While, feq_on_domain:
+ on_domain is idempotent Though stated indirectly, Equivalently, one may see its definition as
+ restricted_t a b:
+Lifts the This is the type of functions whose maximal domain is Notation for non-dependent restricted functions from The main axiom of this module states that functions The type of ghost, total, dependent functions Use The counterpart of on_domain_g is idempotent Counterpart of Counterpart of Notation for ghost, non-dependent restricted functions from Main axiom for ghost functions * fsdoc: no-summary-found fsdoc: no-comment-found Computatiional sets (on Types): membership is a boolean function destructors constructors a property about sets ops Properties extensionality Converting lists to sets fsdoc: no-summary-found fsdoc: no-comment-found This module provides an erased type to abstract computationally
+irrelevant values. It relies on the GHOST effect defined in Prims. The type is considered non-informative. So, The compiler extracts The type is Importantly, computationally relevant code cannot use Just like Coq's prop, it is okay to use erased types
+freely as long as we produce an erased type. The rest of this module includes several well-defined defined
+notions. They are not trusted. Sequential composition of erased Unary map Binary map Ternary map Pushing a refinement type under the Mapping a function with a refined domain over a refined erased value Mapping a binary function with a refined domain over a pair of
+refined erased values Mapping a function with a refined domain and co-domain over a
+refined erased value producing a refined erased value Mapping a binary function with a refined domain and co-domain over
+a pair of refined erased values producing a refined erased value fsdoc: no-summary-found fsdoc: no-comment-found fsdoc: no-summary-found fsdoc: no-comment-found fsdoc: no-summary-found fsdoc: no-comment-found Opens module FStar.HyperStack Aliases module FStar.Monotonic.Witnessed as Aliases module FStar.HyperStack as Opens module FStar.Preorder Setting up the preorder for mem Starting the predicates that constitute the preorder The preorder is the conjunction of above predicates Predicates that we will witness with regions and refs TODO: we should derive these using DM4F effect State (a:Type) (wp:st_wp a) = STATE a wp WARNING: this effect is unsafe, for C/C++ extraction it shall only be used by
+code that would later extract to OCaml or by library functions ***** defining predicates for equal refs in some regions ***** // * AR: (may be this is an overkill)
+// * various effects below talk about refs being equal in some regions (all regions, stack regions, etc.)
+// * this was done by defining, for example, an equal_dom predicate with a (forall (r:rid)) quantifier
+// * this quantifier was only guarded with Map.contains (HS.get_hmap m) r
+// * which meant it could fire for all the contained regions
+// *
+// * instead now we define abstract predicates, e.g. same_refs_in_all_regions, and provide intro and elim forms
+// * the advantage is that, the (lemma) quantifiers are now guarded additionally by same_refs_in_all_regions kind
+// * of predicates, and hence should fire more contextually
+// * should profile the queries to see if it actually helps
+// // * marking these opaque, since expect them to be unfolded away beforehand
+// predicates intro and elim forms The frame invariant is enforced The heap structure is unchanged Any region that is not the tip has no seen any allocation The stack invariant is enforced No frame may have received an allocation but the tip Pushes a new empty frame on the stack * Removes old frame from the stack * JP, AR: these are not supported in C, and NS: This version is just fine; all the operation on mem are ghost
+and we can rig it so that mem just get erased at the end MR witness etc. * states that p is preserved by any valid updates on r; note that h0 and h1 may differ arbitrarily elsewhere, hence proving stability usually requires that p depends only on r's content. ***** Begin: preferred API for witnessing and recalling predicates ***** ***** End: preferred API for witnessing and recalling predicates ***** ***** logical properties of witnessed ***** fsdoc: no-summary-found fsdoc: no-comment-found Two references with different reads are disjoint. fsdoc: no-summary-found fsdoc: no-comment-found FStar.IFC provides a simple, generic abstraction for monadic
+information-flow control based on a user-defined (semi-)lattice of
+information flow labels. The main idea is to provide an abstract type Several papers develop this idea, ranging from Fable: A language for enforcing user-defined security policies
+http://www.cs.umd.edu/~nswamy/papers/fable-tr.pdf To more modern variants like
+https://hackage.haskell.org/package/lio The The The A semilattice has a top element and a
+associative-commutative-idempotent least upper bound operator.
+This is effectvely the typeclass of a semilattice, however, we
+program explicitly with semilattice, rather than use typeclass
+instantiation. For most of the rest of this development, we'll use an erased
+counterpart of a semilattice A lattice element is just an element of the carrier type A convenience for joining elements in the lattice The main type provided by this module is Note, any The next pair of lemmas show that reveal/hide are inverses This is just a map of This is almost a regular monadic This is almost like a regular bind, except like As such, any computation that observes the protected value held in
+ fsdoc: no-summary-found fsdoc: no-comment-found assume val print_nat_hex : nat -> ML unit assume val print_nat_dec : nat -> ML unit Print as hexadecimal with a leading 0x Print as decimal Print as hex in fixed width, no leading 0x Print as decimal, zero padded to maximum possible length An UNSOUND escape hatch for printf-debugging;
+Although it always returns false, we mark it
+as returning a bool, so that extraction doesn't
+erase this call. Note: no guarantees are provided regarding the order
+of eassume valuation of this function; since it is marked as pure,
+the compiler may re-order or replicate it. fsdoc: no-summary-found fsdoc: no-comment-found Indefinite description is an axiom that allows picking a witness
+for existentially quantified predicate. Many other axioms can be derived from this one: Use it with care! For some background on the axiom, see: https://github.com/coq/coq/wiki/CoqAndAxioms#indefinite-description--hilberts-epsilon-operator
+https://en.wikipedia.org/wiki/Theory_of_descriptions#Indefinite_descriptions The main axiom: Given a classical proof of A version in ghost is easily derivable An alternate formulation, mainly for legacy reasons. Given a classical proof of We should take Indefinite description entails the a strong form of the excluded
+middle, i.e., one can case-analyze the truth of a proposition
+(only in We also can combine this with a the classical tautology converting
+with a Note, F*+SMT can easily prove, since it is just classical logic:
+ A variant of the previous lemma, but for a A proof for squash p can be eliminated to get p in the Ghost effect fsdoc: no-summary-found fsdoc: no-comment-found Includes module FStar.Int.Cast Aliases module FStar.UInt64 as Aliases module FStar.UInt128 as fsdoc: no-summary-found fsdoc: no-comment-found Unsigned to unsigned Signed to signed Unsigned to signed Signed to unsigned fsdoc: no-summary-found fsdoc: no-comment-found NOTE: anything that you fix/update here should be reflected in a copy-paste of this module. Opens module FStar.Mul Opens module FStar.BitVector Opens module FStar.Math.Lemmas Specs Machine integer type Multiplicative operator semantics, see C11 6.5.5 Truncation towards zero division Wrap-around modulo: wraps into [-p/2; p/2[ Constants Increment and decrement Addition primitives Subtraction primitives Multiplication primitives Division primitives Modulo primitives Comparison operators Casts WARNING: Mind the big endian vs little endian definition Relations between constants in BitVector and in UInt. (nth a i) returns a boolean indicating the i-th bit of a. Lemmas for constants Bitwise operators Bitwise operators definitions Two's complement unary minus Bitwise operators lemmas TODO: lemmas about the relations between different operators Bitwise AND operator Bitwise XOR operator Shift operators If a is negative the result is undefined behaviour If a is negative the result is implementation defined Shift operators lemmas fsdoc: no-summary-found fsdoc: no-comment-found NOTE: anything that you fix/update here should be reflected in Subtraction primitives Multiplication primitives Division primitives division overflows on INT_MIN / -1 Modulo primitives If a/b is not representable the result of a%b is undefind Bitwise operators Shift operators If a is negative the result is implementation-defined If a is negative or a * pow2 s is not representable the result is undefined Comparison operators Infix notations To input / output constants .. in decimal representation This private primitive is used internally by the
+compiler to translate bounded integer constants
+with a desugaring-time check of the size of the number,
+rather than an expensive verification check.
+Since it is marked private, client programs cannot call it directly
+Since it is marked unfold, it eagerly reduces,
+eliminating the verification overhead of the wrapper fsdoc: no-summary-found fsdoc: no-comment-found NOTE: anything that you fix/update here should be reflected in Subtraction primitives Multiplication primitives Division primitives division overflows on INT_MIN / -1 Modulo primitives If a/b is not representable the result of a%b is undefind Bitwise operators Shift operators If a is negative the result is implementation-defined If a is negative or a * pow2 s is not representable the result is undefined Comparison operators Infix notations To input / output constants .. in decimal representation This private primitive is used internally by the
+compiler to translate bounded integer constants
+with a desugaring-time check of the size of the number,
+rather than an expensive verification check.
+Since it is marked private, client programs cannot call it directly
+Since it is marked unfold, it eagerly reduces,
+eliminating the verification overhead of the wrapper fsdoc: no-summary-found fsdoc: no-comment-found NOTE: anything that you fix/update here should be reflected in Subtraction primitives Multiplication primitives Division primitives division overflows on INT_MIN / -1 Modulo primitives If a/b is not representable the result of a%b is undefind Bitwise operators Shift operators If a is negative the result is implementation-defined If a is negative or a * pow2 s is not representable the result is undefined Comparison operators Infix notations To input / output constants .. in decimal representation This private primitive is used internally by the
+compiler to translate bounded integer constants
+with a desugaring-time check of the size of the number,
+rather than an expensive verification check.
+Since it is marked private, client programs cannot call it directly
+Since it is marked unfold, it eagerly reduces,
+eliminating the verification overhead of the wrapper fsdoc: no-summary-found fsdoc: no-comment-found NOTE: anything that you fix/update here should be reflected in Subtraction primitives Multiplication primitives Division primitives division overflows on INT_MIN / -1 Modulo primitives If a/b is not representable the result of a%b is undefind Bitwise operators Shift operators If a is negative the result is implementation-defined If a is negative or a * pow2 s is not representable the result is undefined Comparison operators Infix notations To input / output constants .. in decimal representation This private primitive is used internally by the
+compiler to translate bounded integer constants
+with a desugaring-time check of the size of the number,
+rather than an expensive verification check.
+Since it is marked private, client programs cannot call it directly
+Since it is marked unfold, it eagerly reduces,
+eliminating the verification overhead of the wrapper fsdoc: no-summary-found fsdoc: no-comment-found NOTE: anything that you fix/update here should be reflected in Subtraction primitives Multiplication primitives Division primitives division overflows on INT_MIN / -1 Modulo primitives If a/b is not representable the result of a%b is undefind Bitwise operators Shift operators If a is negative the result is implementation-defined If a is negative or a * pow2 s is not representable the result is undefined Comparison operators Infix notations To input / output constants .. in decimal representation This private primitive is used internally by the
+compiler to translate bounded integer constants
+with a desugaring-time check of the size of the number,
+rather than an expensive verification check.
+Since it is marked private, client programs cannot call it directly
+Since it is marked unfold, it eagerly reduces,
+eliminating the verification overhead of the wrapper fsdoc: no-summary-found fsdoc: no-comment-found //////////////////////////////////////////////////////////////////////////////
+Test
+////////////////////////////////////////////////////////////////////////////// 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 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. Authors: Aseem Rastogi and Nikhil Swamy This module proves that lexicographic ordering is well-founded
+(i.e. every element is accessible) It defines the lex relation as an inductive, and prove its well-foundedness Since SMT proofs in F* are more amenable to squashed definitions,
+the module also defines a squashed version of the lex relation,
+and prove its well-foundedness, reusing the proof for the constructive version See tests/micro-benchmarks/Test.WellFoundedRecursion.fst for
+how we use squashed Finally, the module defines a non-dependent version of lex
+(in-terms of dependent lex), and uses it to prove well-foundedness of symmetric products too Some references: Definition of lexicographic ordering as a relation over dependent tuples Two elements are related if: Given two well-founded relations We can also define a squashed version of lex relation Provide a mapping from a point in lex_aux to a squashed point in lex And prove that is it is well-founded A user-friendly lex_wf that returns a well-founded relation We can also define a non-dependent version of the lex ordering,
+in terms of the dependent lex tuple,
+and prove its well-foundedness The non-dependent lexicographic ordering
+and its well-foundedness Symmetric product relation
+we can prove its well-foundedness by showing that it is a subrelation of non-dep lex sym is a subrelation of non-dependent lex Theorem for symmetric product fsdoc: no-summary-found fsdoc: no-comment-found [map2] takes a pair of list of the same length [x1; ...; xn] [y1; ... ; yn] and return the list [f x1 y1; ... ; f xn yn] [map3] takes three lists of the same length [x1; ...; xn][y1; ... ; yn] [z1; ... ; zn] and return the list [f x1 y1 z1; ... ; f xn yn zn] [zip] takes a pair of list of the same length and returns the list of index-wise pairs [zip3] takes a 3-tuple of list of the same length and returns the list of index-wise 3-tuples Functions on list with a pure specification fsdoc: no-summary-found fsdoc: no-comment-found If we [append] the two lists produced using a [splitAt], then we get back the original list If we [splitAt] the point at which two lists have been [append]ed, then we get back the original lists. Fully characterize behavior of [splitAt] in terms of more standard list concepts The [hd] of the second list returned via [splitAt] is the [n]th element of the original list If two lists have the same left prefix, then shorter left prefixes are also the same. Doing an [index] on the left-part of a [splitAt] is same as doing it on the original list Doing an [index] on the right-part of a [splitAt] is same as doing it on the original list, but shifted The 3 pieces returned via [split3] can be joined together via an [append] and a [cons] The middle element returned via [split3] is the [n]th [index]ed element The lengths of the left and right parts of a [split3] are as expected. If we [split3] on lists with the same left prefix, we get the same element and left prefix. If we perform an [unsnoc] on a list, then the left part is the same as an [append]+[cons] on the list after [split3]. Doing [unsnoc] and [split3] in either order leads to the same left part, and element. The head of the right side of a [split3] can be [index]ed from original list. Properties of splitAt If we If we Fully characterize behavior of The If two lists have the same left prefix, then shorter left prefixes are
+also the same. Doing an Doing an Properties of split3 The 3 pieces returned via The middle element returned via The lengths of the left and right parts of a If we assert ((a1 @ If we perform an Doing assert (fst (splitAt (length xs) xs) == xs);
+assert (fst (splitAt (length xs) xs) == fst (splitAt (length xs) l));
+assert (i+1 <= length xs);
+assert (fst (splitAt (i+1) xs) == fst (splitAt (i+1) l)); The head of the right side of a fsdoc: no-summary-found fsdoc: no-comment-found Pure total operations on lists This module defines all pure and total operations on lists that can be used in specifications. [isEmpty l] returns [true] if and only if [l] is empty [hd l] returns the first element of [l]. Requires [l] to be nonempty, at type-checking time. Named as in: OCaml, F#, Coq [tail l] returns [l] without its first element. Requires, at type-checking time, that [l] be nonempty. Similar to: tl in OCaml, F#, Coq [tl l] returns [l] without its first element. Requires, at type-checking time, that [l] be nonempty. Named as in: OCaml, F#, Coq [last l] returns the last element of [l]. Requires, at type-checking time, that [l] be nonempty. Named as in: Haskell [init l] returns [l] without its last element. Requires, at type-checking time, that [l] be nonempty. Named as in: Haskell [length l] returns the total number of elements in [l]. Named as in: OCaml, F#, Coq [nth l n] returns the [n]-th element in list [l] (with the first element being the 0-th) if [l] is long enough, or [None] otherwise. Named as in: OCaml, F#, Coq [index l n] returns the [n]-th element in list [l] (with the first element being the 0-th). Requires, at type-checking time, that [l] be of length at least [n+1]. [count x l] returns the number of occurrences of [x] in [l]. Requires, at type-checking time, the type of [a] to have equality defined. Similar to: [List.count_occ] in Coq. [rev_acc l1 l2] appends the elements of [l1] to the beginning of [l2], in reverse order. It is equivalent to [append (rev l1) l2], but is tail-recursive. Similar to: [List.rev_append] in OCaml, Coq. [rev l] returns the list [l] in reverse order. Named as in: OCaml, F#, Coq. [append l1 l2] appends the elements of [l2] to the end of [l1]. Named as: OCaml, F#. Similar to: [List.app] in Coq. Defines notation [@] for [append], as in OCaml, F# . [snoc (l, x)] adds [x] to the end of the list [l]. This module defines all pure and total operations on lists that can be
+used in specifications. It is implemented by FStar_List_Tot_Base.ml, any
+functional change and/or the addition of new functions MUST be reflected
+there. @summary Pure total operations on lists Base operations Defines notation Note: We use an uncurried [flatten l], where [l] is a list of lists, returns the list of the elements of the lists in [l], preserving their order. Named as in: OCaml, Coq. [map f l] applies [f] to each element of [l] and returns the list of results, in the order of the original elements in [l]. Requires, at type-checking time, [f] to be a pure total function. Named as in: OCaml, Coq, F# [mapi_init f n l] applies, for each [k], [f (n+k)] to the [k]-th element of [l] and returns the list of results, in the order of the original elements in [l]. Requires, at type-checking time, [f] to be a pure total function. [mapi f l] applies, for each [k], [f k] to the [k]-th element of [l] and returns the list of results, in the order of the original elements in [l]. Requires, at type-checking time, [f] to be a pure total function. Named as in: OCaml [concatMap f l] applies [f] to each element of [l] and returns the concatenation of the results, in the order of the original elements of [l]. This is equivalent to [flatten (map f l)]. Requires, at type-checking time, [f] to be a pure total function. [fold_left f x [y1; y2; ...; yn]] computes (f (... (f x y1) y2) ... yn). Requires, at type-checking time, [f] to be a pure total function. Named as in: OCaml, Coq. [fold_right f [x1; x2; ...; xn] y] computes (f x1 (f x2 (... (f xn y)) ... )). Requires, at type-checking time, [f] to be a pure total function. Named as in: OCaml, Coq [fold_right_gtot] is just like [fold_right], except [fold_left2 f x [y1; y2; ...; yn] [z1; z2; ...; zn]] computes (f (... (f x y1 z1) y2 z2) ... yn zn). Requires, at type-checking time, [f] to be a pure total function, and the lists [y1; y2; ...; yn] and [z1; z2; ...; zn] to have the same lengths. Named as in: OCaml [mem x l] returns [true] if, and only if, [x] appears as an element of [l]. Requires, at type-checking time, the type of elements of [l] to have decidable equality. Named as in: OCaml. See also: List.In in Coq, which is propositional. [memP x l] holds if, and only if, [x] appears as an element of [l]. Similar to: List.In in Coq. [contains x l] returns [true] if, and only if, [x] appears as an element of [l]. Requires, at type-checking time, the type of elements of [l] to have decidable equality. It is equivalent to: [mem x l]. TODO: should we rather swap the order of arguments? [existsb f l] returns [true] if, and only if, there exists some element [x] in [l] such that [f x] holds. [find f l] returns [Some x] for some element [x] appearing in [l] such that [f x] holds, or [None] only if no such [x] exists. We would like to have a postcondition for [filter f l] saying that, for any element [x] of [filter f l], [f x] holds. To this end, we need to use [mem] as defined above, which would require the underlying type [a] of list elements to have decidable equality. However, we would still like to define [filter] on all element types, even those that do not have decidable equality. Thus, we define our postcondition as [mem_filter_spec f m u] below, where [m] is the intended [filter f l] and [u] indicates whether [a] has decidable equality ([None] if not). Requires, at type-checking time, [f] to be a pure total function. [filter f l] returns [l] with all elements [x] such that [f x] does not hold removed. Requires, at type-checking time, [f] to be a pure total function. Named as in: OCaml, Coq Postcondition on [filter f l] for types with decidable equality: for any element [x] of [filter f l], [f x] holds. Requires, at type-checking time, [f] to be a pure total function. Postcondition on [filter f l] for types with decidable equality, stated with [forall]: for any element [x] of [filter f l], [f x] holds. Requires, at type-checking time, [f] to be a pure total function. [for_all f l] returns [true] if, and only if, for all elements [x] appearing in [l], [f x] holds. Requires, at type-checking time, [f] to be a pure total function. Named as in: OCaml. Similar to: List.forallb in Coq Specification for [for_all f l] vs. mem [collect f l] applies [f] to each element of [l] and returns the concatenation of the results, in the order of the original elements of [l]. It is equivalent to [flatten (map f l)]. Requires, at type-checking time, [f] to be a pure total function. TODO: what is the difference with [concatMap]? [tryFind f l] returns [Some x] for some element [x] appearing in [l] such that [f x] holds, or [None] only if no such [x] exists. Requires, at type-checking time, [f] to be a pure total function. Contrary to [find], [tryFind] provides no postcondition on its result. [tryPick f l] returns [y] for some element [x] appearing in [l] such that [f x = Some y] for some y, or [None] only if [f x = None] for all elements [x] of [l]. Requires, at type-checking time, [f] to be a pure total function. [choose f l] returns the list of [y] for all elements [x] appearing in [l] such that [f x = Some y] for some [y]. Requires, at type-checking time, [f] to be a pure total function. [partition f l] returns the pair of lists [(l1, l2)] where all elements [x] of [l] are in [l1] if [f x] holds, and in [l2] otherwise. Both [l1] and [l2] retain the original order of [l]. Requires, at type-checking time, [f] to be a pure total function. [subset la lb] is true if and only if all the elements from [la] are also in [lb]. Requires, at type-checking time, the type of elements of [la] and [lb] to have decidable equality. [noRepeats l] returns [true] if, and only if, no element of [l] appears in [l] more than once. Requires, at type-checking time, the type of elements of [la] and [lb] to have decidable equality. [no_repeats_p l] valid if, and only if, no element of [l] appears in [l] more than once. [assoc x l] returns [Some y] where [(x, y)] is the first element of [l] whose first element is [x], or [None] only if no such element exists. Requires, at type-checking time, the type of [x] to have decidable equality. Named as in: OCaml. [split] takes a list of pairs [(x1, y1), ..., (xn, yn)] and returns the pair of lists ([x1, ..., xn], [y1, ..., yn]). Named as in: OCaml [unzip] takes a list of pairs [(x1, y1), ..., (xn, yn)] and returns the pair of lists ([x1, ..., xn], [y1, ..., yn]). Named as in: Haskell [unzip3] takes a list of triples [(x1, y1, z1), ..., (xn, yn, zn)] and returns the triple of lists ([x1, ..., xn], [y1, ..., yn], [z1, ..., zn]). Named as in: Haskell [splitAt] takes a natural number n and a list and returns a pair of the maximal prefix of l of size smaller than n and the rest of the list [unsnoc] is an inverse of [snoc]. It splits a list into all-elements-except-last and last element. [split3] splits a list into 3 parts. This allows easy access to the part of the list before and after the element, as well as the element itself. [partition] splits a list [l] into two lists, the sum of whose lengths is the length of [l]. [bool_of_compare] turns a comparison function into a strict order. More precisely, [bool_of_compare compare x y] returns true if, and only if, [compare x y] is positive. Inspired from OCaml, where polymorphic comparison using both the [compare] function and the (>) infix operator are such that [compare x y] is positive if, and only if, x > y. Requires, at type-checking time, [compare] to be a pure total function. [compare_of_bool] turns a strict order into a comparison function. More precisely, [compare_of_bool rel x y] returns a positive number if, and only if, x [sortWith compare l] returns the list [l'] containing the elements of [l] sorted along the comparison function [compare], in such a way that if [compare x y > 0], then [x] appears before [y] in [l']. Requires, at type-checking time, [compare] to be a pure total function.
+FStar.Algebra.CommMonoid.Equiv
+
+
+unopteq
+type equiv (a:Type) =
+ | EQ :
+ eq:(a -> a -> Type0) ->
+ reflexivity:(x:a -> Lemma (x `eq` x)) ->
+ symmetry:(x:a -> y:a -> Lemma (requires (x `eq` y)) (ensures (y `eq` x))) ->
+ transitivity:(x:a -> y:a -> z:a -> Lemma (requires (x `eq` y /\ y `eq` z)) (ensures (x `eq` z))) ->
+ equiv a
let equality_equiv (a:Type) : equiv a =
+ EQ (fun x y -> x == y) (fun x -> ()) (fun x y -> ()) (fun x y z -> ())
unopteq
+type cm (a:Type) (eq:equiv a) =
+ | CM :
+ unit:a ->
+ mult:(a -> a -> a) ->
+ identity : (x:a -> Lemma ((unit `mult` x) `EQ?.eq eq` x)) ->
+ associativity : (x:a -> y:a -> z:a ->
+ Lemma ((x `mult` y `mult` z) `EQ?.eq eq` (x `mult` (y `mult` z)))) ->
+ commutativity:(x:a -> y:a -> Lemma ((x `mult` y) `EQ?.eq eq` (y `mult` x))) ->
+ congruence:(x:a -> y:a -> z:a -> w:a -> Lemma (requires (x `EQ?.eq eq` z /\ y `EQ?.eq eq` w)) (ensures ((mult x y) `EQ?.eq eq` (mult z w)))) ->
+ cm a eq
let right_identity (#a:Type u#aa) (eq:equiv a) (m:cm a eq) (x:a)
+ : Lemma (x `CM?.mult m` (CM?.unit m) `EQ?.eq eq` x) =
+ CM?.commutativity m x (CM?.unit m);
+ CM?.identity m x;
+ EQ?.transitivity eq (x `CM?.mult m` (CM?.unit m)) ((CM?.unit m) `CM?.mult m` x) x
let int_plus_cm : cm int (equality_equiv int) =
+ CM 0 (+) (fun _ -> ()) (fun _ _ _ -> ()) (fun _ _ -> ()) (fun _ _ _ _ -> ())
let int_multiply_cm : cm int (equality_equiv int) =
+ CM 1 ( * ) (fun _ -> ()) (fun _ _ _ -> ()) (fun _ _ -> ()) (fun _ _ _ _ -> ())
module FStar.Algebra.CommMonoid
-
+FStar.Algebra.CommMonoid
+
+
+unopteq
+type cm (a:Type) =
+ | CM :
+ unit:a ->
+ mult:(a -> a -> a) ->
+ identity : (x:a -> Lemma (unit `mult` x == x)) ->
+ associativity : (x:a -> y:a -> z:a ->
+ Lemma (x `mult` y `mult` z == x `mult` (y `mult` z))) ->
+ commutativity:(x:a -> y:a -> Lemma (x `mult` y == y `mult` x)) ->
+ cm a
let right_identity (#a:Type) (m:cm a) (x:a) :
+ Lemma (CM?.mult m x (CM?.unit m) == x) =
+ CM?.commutativity m x (CM?.unit m); CM?.identity m x
let int_plus_cm : cm int =
+ CM 0 (+) (fun x -> ()) (fun x y z -> ()) (fun x y -> ())
let int_multiply_cm : cm int =
+ CM 1 ( * ) (fun x -> ()) (fun x y z -> ()) (fun x y -> ())
module FStar.Algebra.Monoid
- Definition of a monoid Some monoid structures
+FStar.Algebra.Monoid
+
+
+PropExt
+ these should go away with proper prop support
+
+ also see the comment in PropositionalExtensionality.fst
+let right_unitality_lemma (m:Type) (u:m) (mult:m -> m -> m) =
+ forall (x:m). x `mult` u == x
let left_unitality_lemma (m:Type) (u:m) (mult:m -> m -> m) =
+ forall (x:m). u `mult` x == x
let associativity_lemma (m:Type) (mult:m -> m -> m) =
+ forall (x y z:m). x `mult` y `mult` z == x `mult` (y `mult` z)
unopteq
+type monoid (m:Type) =
+ | Monoid :
+ unit:m ->
+ mult:(m -> m -> m) ->
+ right_unitality:squash (right_unitality_lemma m unit mult) ->
+ left_unitality:squash (left_unitality_lemma m unit mult) ->
+ associativity:squash (associativity_lemma m mult) ->
+ monoid m
let intro_monoid (m:Type) (u:m) (mult:m -> m -> m)
+ : Pure (monoid m)
+ (requires (right_unitality_lemma m u mult /\ left_unitality_lemma m u mult /\ associativity_lemma m mult))
+ (ensures (fun mm -> Monoid?.unit mm == u /\ Monoid?.mult mm == mult))
+=
+ Monoid u mult () () ()
let nat_plus_monoid : monoid nat =
+ let add (x y : nat) : nat = x + y in
+ intro_monoid nat 0 add
let int_plus_monoid : monoid int =
+ intro_monoid int 0 (+)
let conjunction_monoid : monoid prop =
+ let u : prop = singleton True in
+ let mult (p q : prop) : prop = p /\ q in
let left_unitality_helper (p:prop) : Lemma ((u `mult` p) == p) =
+ assert ((u `mult` p) <==> p) ;
+ PropExt.apply (u `mult` p) p
+in
let right_unitality_helper (p:prop) : Lemma ((p `mult` u) == p) =
+ assert ((p `mult` u) <==> p) ;
+ PropExt.apply (p `mult` u) p
+in
let associativity_helper (p1 p2 p3 : prop) : Lemma (p1 `mult` p2 `mult` p3 == p1 `mult` (p2 `mult` p3)) =
+ assert (p1 `mult` p2 `mult` p3 <==> p1 `mult` (p2 `mult` p3)) ;
+ PropExt.apply (p1 `mult` p2 `mult` p3) (p1 `mult` (p2 `mult` p3))
+in
forall_intro right_unitality_helper ;
+assert (right_unitality_lemma prop u mult) ;
+forall_intro left_unitality_helper ;
+assert (left_unitality_lemma prop u mult) ;
+forall_intro_3 associativity_helper;
+assert (associativity_lemma prop mult) ;
+intro_monoid prop u mult
let disjunction_monoid : monoid prop =
+ let u : prop = singleton False in
+ let mult (p q : prop) : prop = p \/ q in
let left_unitality_helper (p:prop) : Lemma ((u `mult` p) == p) =
+ assert ((u `mult` p) <==> p) ;
+ PropExt.apply (u `mult` p) p
+in
let right_unitality_helper (p:prop) : Lemma ((p `mult` u) == p) =
+ assert ((p `mult` u) <==> p) ;
+ PropExt.apply (p `mult` u) p
+in
let associativity_helper (p1 p2 p3 : prop) : Lemma (p1 `mult` p2 `mult` p3 == p1 `mult` (p2 `mult` p3)) =
+ assert (p1 `mult` p2 `mult` p3 <==> p1 `mult` (p2 `mult` p3)) ;
+ PropExt.apply (p1 `mult` p2 `mult` p3) (p1 `mult` (p2 `mult` p3))
+in
forall_intro right_unitality_helper ;
+assert (right_unitality_lemma prop u mult) ;
+forall_intro left_unitality_helper ;
+assert (left_unitality_lemma prop u mult) ;
+forall_intro_3 associativity_helper;
+assert (associativity_lemma prop mult) ;
+intro_monoid prop u mult
let bool_and_monoid : monoid bool =
+ let and_ b1 b2 = b1 && b2 in
+ intro_monoid bool true and_
let bool_or_monoid : monoid bool =
+ let or_ b1 b2 = b1 || b2 in
+ intro_monoid bool false or_
let bool_xor_monoid : monoid bool =
+ let xor b1 b2 = (b1 || b2) && not (b1 && b2) in
+ intro_monoid bool false xor
let lift_monoid_option (#a:Type) (m:monoid a) : monoid (option a) =
+ let mult (x y:option a) =
+ match x, y with
+ | Some x0, Some y0 -> Some (m.mult x0 y0)
+ | _, _ -> None
+ in
+ intro_monoid (option a) (Some m.unit) mult
let monoid_morphism_unit_lemma (#a #b:Type) (f:a -> b) (ma:monoid a) (mb:monoid b) =
+ f (Monoid?.unit ma) == Monoid?.unit mb
let monoid_morphism_mult_lemma (#a #b:Type) (f:a -> b) (ma:monoid a) (mb:monoid b) =
+ forall (x y:a). Monoid?.mult mb (f x) (f y) == f (Monoid?.mult ma x y)
type monoid_morphism (#a #b:Type) (f:a -> b) (ma:monoid a) (mb:monoid b) =
+ | MonoidMorphism :
+ unit:squash (monoid_morphism_unit_lemma f ma mb) ->
+ mult:squash (monoid_morphism_mult_lemma f ma mb) ->
+ monoid_morphism f ma mb
let intro_monoid_morphism (#a #b:Type) (f:a -> b) (ma:monoid a) (mb:monoid b)
+ : Pure (monoid_morphism f ma mb)
+ (requires (monoid_morphism_unit_lemma f ma mb /\ monoid_morphism_mult_lemma f ma mb))
+ (ensures (fun _ -> True))
+=
+ MonoidMorphism () ()
let embed_nat_int (n:nat) : int = n
+let _ = intro_monoid_morphism embed_nat_int nat_plus_monoid int_plus_monoid
let neg (p:prop) : prop = ~p
+let _ =
+ assert (neg True <==> False) ;
+ PropExt.apply (neg True) False ;
+ let mult_lemma_helper (p q:prop) : Lemma (neg (p /\ q) == (neg p \/ neg q)) =
+ assert (neg (p /\ q) <==> (neg p \/ neg q)) ;
+ PropExt.apply (neg (p /\ q)) (neg p \/ neg q)
+ in
+ forall_intro_2 mult_lemma_helper ;
+ intro_monoid_morphism neg conjunction_monoid disjunction_monoid
let _ =
+ assert (neg False <==> True) ;
+ PropExt.apply (neg False) True ;
+ let mult_lemma_helper (p q:prop) : Lemma (neg (p \/ q) == (neg p /\ neg q)) =
+ assert (neg (p \/ q) <==> (neg p /\ neg q)) ;
+ PropExt.apply (neg (p \/ q)) (neg p /\ neg q)
+ in
+ forall_intro_2 mult_lemma_helper ;
+ intro_monoid_morphism neg disjunction_monoid conjunction_monoid
let mult_act_lemma (m a:Type) (mult:m -> m -> m) (act:m -> a -> a) =
+ forall (x x':m) (y:a). (x `mult` x') `act` y == x `act` (x' `act` y)
let unit_act_lemma (m a:Type) (u:m) (act:m -> a -> a) =
+ forall (y:a). u `act` y == y
unopteq
+type left_action (#m:Type) (mm:monoid m) (a:Type) =
+ | LAct :
+ act:(m -> a -> a) ->
+ mult_lemma: squash (mult_act_lemma m a (Monoid?.mult mm) act) ->
+ unit_lemma: squash (unit_act_lemma m a (Monoid?.unit mm) act) ->
+ left_action mm a
let left_action_morphism
+ (#a #b #ma #mb:Type)
+ (f:a -> b)
(mf: ma -> mb)
+ (#mma:monoid ma)
+ (#mmb:monoid mb)
+ (la:left_action mma a)
+ (lb:left_action mmb b)
+= forall (g:ma) (x:a). LAct?.act lb (mf g) (f x) == f (LAct?.act la g x)
module FStar.All
-
+FStar.All
+
+
+let all_pre = all_pre_h heap
+let all_post' (a : Type) (pre:Type) = all_post_h' heap a pre
+let all_post (a : Type) = all_post_h heap a
+let all_wp (a : Type) = all_wp_h heap a
+new_effect ALL = ALL_h heap
unfold let lift_state_all (a : Type) (wp : st_wp a) (p : all_post a) = wp (fun a -> p (V a))
+sub_effect STATE ~> ALL { lift_wp = lift_state_all }
unfold
+let lift_exn_all (a : Type) (wp : ex_wp a) (p : all_post a) (h : heap) = wp (fun ra -> p ra h)
+sub_effect EXN ~> ALL { lift_wp = lift_exn_all }
effect All (a:Type) (pre:all_pre) (post:(h:heap -> Tot (all_post' a (pre h)))) =
+ ALL a
+ (fun (p : all_post a) (h : heap) -> pre h /\ (forall ra h1. post h ra h1 ==> p ra h1))
+effect ML (a:Type) = ALL a (fun (p:all_post a) (_:heap) -> forall (a:result a) (h:heap). p a h)
let ( |> ) (x : 'a) (f : ('a -> ML 'b)) : ML 'b = f x
+let pipe_right = ( |> )
let ( <| ) (f : ('a -> ML 'b)) (x : 'a) : ML 'b = f x
+let pipe_left = ( <| )
assume val exit : int -> ML 'a
+assume val try_with : (unit -> ML 'a) -> (exn -> ML 'a) -> ML 'a
assume exception Failure of string
+assume val failwith : string -> All 'a (fun h -> True) (fun h a h' -> Err? a /\ h == h')
module FStar.BV
-
+FStar.BV
+
+
+bv_tac, converts bitwise
+operations on unsigned integers to operations on bit vectors and
+back using the int2bv / bv2int isomorphism. This can be an
+effective way of discharging such proof obligations for bitwise
+operatoins on integers using the SMT solver's theory of
+bitvectors.
+
+
+bv_t
+n, with
+decidable equalityval bv_t (n: nat) : eqtype
+bv_uext
+n to a larger vector of size
+m+n, filling the extra bits with 0val bv_uext (#n #m: pos) (a: bv_t n) : Tot (normalize (bv_t (m + n)))
+Relating unsigned integers to bitvectors
+
+int2bv
+< 2^n, to a n-length
+bit vectorval int2bv (#n: pos) (num: uint_t n) : Tot (bv_t n)
+bv2int
+val bv2int (#n: pos) (vec: bv_t n) : Tot (uint_t n)
val int2bv_lemma_1 (#n: pos) (a b: uint_t n)
+ : Lemma (requires a = b) (ensures (int2bv #n a = int2bv #n b))
val int2bv_lemma_2 (#n: pos) (a b: uint_t n)
+ : Lemma (requires (int2bv a = int2bv b)) (ensures a = b)
val inverse_vec_lemma (#n: pos) (vec: bv_t n)
+ : Lemma (requires True) (ensures vec = (int2bv (bv2int vec))) [SMTPat (int2bv (bv2int vec))]
val inverse_num_lemma (#n: pos) (num: uint_t n)
+ : Lemma (requires True)
+ (ensures num = bv2int #n (int2bv #n num))
+ [SMTPat (bv2int #n (int2bv #n num))]
+Relating lists to bitvectors
+
+list2bv
+val list2bv (#n: pos) (l: list bool {List.length l = n}) : Tot (bv_t n)
+bv2list
+val bv2list: #n: pos -> bv_t n -> Tot (l: list bool {List.length l = n})
val list2bv_bij (#n: pos) (a: list bool {List.length a = n})
+ : Lemma (requires (True)) (ensures (bv2list (list2bv #n a) = a))
val bv2list_bij (#n: pos) (a: bv_t n)
+ : Lemma (requires (True)) (ensures (list2bv (bv2list #n a) = a))
+Bitwise logical operators
+
+bvand
+val bvand (#n: pos) (a b: bv_t n) : Tot (bv_t n)
val int2bv_logand:
+ #n: pos ->
+ #x: uint_t n ->
+ #y: uint_t n ->
+ #z: bv_t n ->
+ squash (bvand #n (int2bv #n x) (int2bv #n y) == z)
+ -> Lemma (int2bv #n (logand #n x y) == z)
+bvxor
+val bvxor (#n: pos) (a b: bv_t n) : Tot (bv_t n)
val int2bv_logxor:
+ #n: pos ->
+ #x: uint_t n ->
+ #y: uint_t n ->
+ #z: bv_t n ->
+ squash (bvxor #n (int2bv #n x) (int2bv #n y) == z)
+ -> Lemma (int2bv #n (logxor #n x y) == z)
+bvor
+val bvor (#n: pos) (a b: bv_t n) : Tot (bv_t n)
val int2bv_logor:
+ #n: pos ->
+ #x: uint_t n ->
+ #y: uint_t n ->
+ #z: bv_t n ->
+ squash (bvor #n (int2bv #n x) (int2bv #n y) == z)
+ -> Lemma (int2bv #n (logor #n x y) == z)
+bvnot
+val bvnot (#n: pos) (a: bv_t n) : Tot (bv_t n)
val int2bv_lognot: #n: pos -> #x: uint_t n -> #z: bv_t n -> squash (bvnot #n (int2bv #n x) == z)
+ -> Lemma (int2bv #n (lognot #n x) == z)
+bvshl
+val bvshl (#n: pos) (a: bv_t n) (s: nat) : Tot (bv_t n)
val int2bv_shl:
+ #n: pos ->
+ #x: uint_t n ->
+ #y: uint_t n ->
+ #z: bv_t n ->
+ squash (bvshl #n (int2bv #n x) y == z)
+ -> Lemma (int2bv #n (shift_left #n x y) == z)
+bvshr
+val bvshr (#n: pos) (a: bv_t n) (s: nat) : Tot (bv_t n)
val int2bv_shr:
+ #n: pos ->
+ #x: uint_t n ->
+ #y: uint_t n ->
+ #z: bv_t n ->
+ squash (bvshr #n (int2bv #n x) y == z)
+ -> Lemma (int2bv #n (shift_right #n x y) == z)
+Arithmetic operations
+unfold
+let bv_zero #n = int2bv #n 0
+bvult
+val bvult (#n: pos) (a b: bv_t n) : Tot (bool)
val int2bv_lemma_ult_1 (#n: pos) (a b: uint_t n)
+ : Lemma (requires a < b) (ensures (bvult #n (int2bv #n a) (int2bv #n b)))
val int2bv_lemma_ult_2 (#n: pos) (a b: uint_t n)
+ : Lemma (requires (bvult #n (int2bv #n a) (int2bv #n b))) (ensures a < b)
+bvadd
+val bvadd (#n: pos) (a b: bv_t n) : Tot (bv_t n)
val int2bv_add:
+ #n: pos ->
+ #x: uint_t n ->
+ #y: uint_t n ->
+ #z: bv_t n ->
+ squash (bvadd #n (int2bv #n x) (int2bv #n y) == z)
+ -> Lemma (int2bv #n (add_mod #n x y) == z)
+bvsub
+val bvsub (#n: pos) (a b: bv_t n) : Tot (bv_t n)
val int2bv_sub:
+ #n: pos ->
+ #x: uint_t n ->
+ #y: uint_t n ->
+ #z: bv_t n ->
+ squash (bvsub #n (int2bv #n x) (int2bv #n y) == z)
+ -> Lemma (int2bv #n (sub_mod #n x y) == z)
+bvdiv
+val bvdiv (#n: pos) (a: bv_t n) (b: uint_t n {b <> 0}) : Tot (bv_t n)
val int2bv_div:
+ #n: pos ->
+ #x: uint_t n ->
+ #y: uint_t n {y <> 0} ->
+ #z: bv_t n ->
+ squash (bvdiv #n (int2bv #n x) y == z)
+ -> Lemma (int2bv #n (udiv #n x y) == z)
+bvmod
+val bvmod (#n: pos) (a: bv_t n) (b: uint_t n {b <> 0}) : Tot (bv_t n)
val int2bv_mod:
+ #n: pos ->
+ #x: uint_t n ->
+ #y: uint_t n {y <> 0} ->
+ #z: bv_t n ->
+ squash (bvmod #n (int2bv #n x) y == z)
+ -> Lemma (int2bv #n (mod #n x y) == z)
+bvmul
+val bvmul (#n: pos) (a: bv_t n) (b: uint_t n) : Tot (bv_t n)
val int2bv_mul:
+ #n: pos ->
+ #x: uint_t n ->
+ #y: uint_t n ->
+ #z: bv_t n ->
+ squash (bvmul #n (int2bv #n x) y == z)
+ -> Lemma (int2bv #n (mul_mod #n x y) == z)
module FStar.BaseTypes
-
+FStar.BaseTypes
+
+
+type char = FStar.Char.char
+type float = FStar.Float.float
+type double = FStar.Float.double
+type byte = FStar.UInt8.byte
+type int8 = FStar.Int8.t
+type uint8 = FStar.UInt8.t
+type int16 = FStar.Int16.t
+type uint16 = FStar.UInt16.t
+type int32 = FStar.Int32.t
+type uint32 = FStar.UInt32.t
+type int64 = FStar.Int64.t
+type uint64 = FStar.UInt64.t
module FStar.BigOps
-
+FStar.BigOps
+
+
+
+1. Normalization: When applied to a list literal, we want
+ ```FStarbig_and f [a;b;c]``` to implicilty reduce to [f a /\ f b /\ f c]
+
+2. Symbolic manipulation: We provide lemmas of the form
+
+ `big_and f l <==> forall x. L.memP x l ==> f x`
+big_and as a fold over
+a list is cumbersome for proof. So, we provide variants big_and'
+etc., that do not reduce implicitly.
+
+L
+
+reduce
+
+delta_attr feature of the
+normalizer. See FStar.Pervasives for how that works. Every term
+that is to be reduced is with the __reduce__ attributelet __reduce__ = ()
norm with a module-specific custom usage, triggering
+specific reduction steps[@@ __reduce__]
+unfold
+let normal (#a: Type) (x: a) : a =
+ FStar.Pervasives.norm [
+ iota;
+ zeta;
+ delta_only [`%L.fold_right_gtot; `%L.map_gtot];
+ delta_attr [`%__reduce__];
+ primops;
+ simplify
+ ]
+ x
+normal_eq
+val normal_eq (#a: Type) (f: a) : Lemma (f == normal f)
+Map and fold
+
+map_op'
+map_op' op f l z maps each
+element of l by f and then combines them using op[@@ __reduce__]
+let map_op' #a #b #c (op: (b -> c -> GTot c)) (f: (a -> GTot b)) (l: list a) (z: c) : GTot c =
+ L.fold_right_gtot #a #c l (fun x acc -> (f x) `op` acc) z
+map_op'_nil
+map_op' showing how it folds over the empty listval map_op'_nil (#a #b #c: Type) (op: (b -> c -> GTot c)) (f: (a -> GTot b)) (z: c)
+ : Lemma (map_op' op f [] z == z)
+map_op'_cons
+map_op' showing how it folds over a cons cellval map_op'_cons
+ (#a #b #c: Type)
+ (op: (b -> c -> GTot c))
+ (f: (a -> GTot b))
+ (hd: a)
+ (tl: list a)
+ (z: c)
+ : Lemma (map_op' op f (hd :: tl) z == (f hd) `op` (map_op' op f tl z))
+Conjunction
+
+big_and'
+big_and' f l = /\_{x in l} f x[@@ __reduce__]
+let big_and' #a (f: (a -> Type)) (l: list a) : Type = map_op' l_and f l True
+big_and'_nil
+big_and' showing it to be trivial over the empty listval big_and'_nil (#a: Type) (f: (a -> Type)) : Lemma (big_and' f [] == True)
+big_and'_cons
+big_and' showing it to be a fold over a list with /\val big_and'_cons (#a: Type) (f: (a -> Type)) (hd: a) (tl: list a)
+ : Lemma (big_and' f (hd :: tl) == (f hd /\ big_and' f tl))
+big_and'_prop
+big_and' f l is a prop, i.e., it is proof irrelevant.val big_and'_prop (#a: Type) (f: (a -> Type)) (l: list a) : Lemma ((big_and' f l) `subtype_of` unit)
big_and' to intrinsically be in prop
+is also possible, but it's much more tedious in proofs./\ is not defined in prop,
+though one can prove that a /\ b is a prop.prop extrinsically.
+big_and'_forall
+big_and f l
+as an infinite conjunction forallval big_and'_forall (#a: Type) (f: (a -> Type)) (l: list a)
+ : Lemma (big_and' f l <==> (forall x. L.memP x l ==> f x))
big_and f l is an implicitly reducing variant of big_and'
+It is defined in prop[@@ __reduce__]
+unfold
+let big_and #a (f: (a -> Type)) (l: list a) : prop =
+ big_and'_prop f l;
+ normal (big_and' f l)
+Disjunction
+
+big_or'
+big_or f l = \/_{x in l} f x[@@ __reduce__]
+let big_or' #a (f: (a -> Type)) (l: list a) : Type = map_op' l_or f l False
+big_or'_nil
+big_or showing it to be False on the empty listval big_or'_nil (#a: Type) (f: (a -> Type)) : Lemma (big_or' f [] == False)
+big_or'_cons
+big_or showing it to fold over a listval big_or'_cons (#a: Type) (f: (a -> Type)) (hd: a) (tl: list a)
+ : Lemma (big_or' f (hd :: tl) == (f hd \/ big_or' f tl))
+big_or'_prop
+big_or f l is a prop
+See the remark above on the style of proof for propval big_or'_prop (#a: Type) (f: (a -> Type)) (l: list a) : Lemma ((big_or' f l) `subtype_of` unit)
+big_or'_exists
+big_or f l
+as an infinite disjunction existsval big_or'_exists (#a: Type) (f: (a -> Type)) (l: list a)
+ : Lemma (big_or' f l <==> (exists x. L.memP x l /\ f x))
big_or f l is an implicitly reducing variant of big_or'
+It is defined in prop[@@ __reduce__]
+unfold
+let big_or #a (f: (a -> Type)) (l: list a) : prop =
+ big_or'_prop f l;
+ normal (big_or' f l)
+Pairwise operators
+
+
+l pairwise, in a triangle of
+elements in the square matrix of l X l. To illustrate, for a
+list of n elements, we fold the operator over the pairwise
+elements of the list in top-down, left-to-right order of the
+diagram below 0 1 2 3 ... n
+0
+1 x
+2 x x
+3 x x x
+. x x x x
+ n x x x x ```
+pairwise_op'
+l using f and combining them with
+op.[@@ __reduce__]
+let rec pairwise_op' #a #b (op: (b -> b -> GTot b)) (f: (a -> a -> b)) (l: list a) (z: b) : GTot b =
+ match l with
+ | [] -> z
+ | hd :: tl -> (map_op' op (f hd) tl z) `op` (pairwise_op' op f tl z)
+symmetric
+f is a symmetric relationlet symmetric (#a: Type) (f: (a -> a -> Type)) = forall x y. f x y <==> f y x
+reflexive
+f is a reflexive relationlet reflexive (#a: Type) (f: (a -> a -> Type)) = forall x. f x x
+anti_reflexive
+f is a anti-reflexive relationlet anti_reflexive (#a: Type) (f: (a -> a -> Type)) = forall x. ~(f x x)
+Pairwise conjunction
+
+pairwise_and'
+pairwise_and f l conjoins f on all pairs excluding the diagonal
+i.e.,[@@ __reduce__]
+let pairwise_and' #a (f: (a -> a -> Type)) (l: list a) : Type = pairwise_op' l_and f l True
FStar pairwise_and f [a; b; c] = f a b /\ f a c /\ f b c
+pairwise_and'_nil
+pairwise_and showing it to be a fold with big_andval pairwise_and'_nil (#a: Type) (f: (a -> a -> Type0)) : Lemma (pairwise_and' f [] == True)
+pairwise_and'_cons
+pairwise_and showing it to be a fold with big_andval pairwise_and'_cons (#a: Type) (f: (a -> a -> Type0)) (hd: a) (tl: list a)
+ : Lemma (pairwise_and' f (hd :: tl) == (big_and' (f hd) tl /\ pairwise_and' f tl))
+pairwise_and'_prop
+pairwise_and' f l is a prop
+See the remark above on the style of proof for propval pairwise_and'_prop (#a: Type) (f: (a -> a -> Type)) (l: list a)
+ : Lemma ((pairwise_and' f l) `subtype_of` unit)
+pairwise_and'_forall
+pairwise_and' f l for symmetric reflexive relations f
+is interpreted as universal quantification over pairs of list elements *val pairwise_and'_forall (#a: Type) (f: (a -> a -> Type)) (l: list a)
+ : Lemma (requires symmetric f /\ reflexive f)
+ (ensures (pairwise_and' f l <==> (forall x y. L.memP x l /\ L.memP y l ==> f x y)))
+pairwise_and'_forall_no_repeats
+pairwise_and' f l for symmetric relations f interpreted as
+universal quantification over pairs of list of unique elementsval pairwise_and'_forall_no_repeats (#a: Type) (f: (a -> a -> Type)) (l: list a)
+ : Lemma (requires symmetric f /\ L.no_repeats_p l)
+ (ensures (pairwise_and' f l <==> (forall x y. L.memP x l /\ L.memP y l /\ x =!= y ==> f x y)))
pairwise_and f l is an implicitly reducing variant of pairwise_and'
+It is defined in prop[@@ __reduce__]
+unfold
+let pairwise_and #a (f: (a -> a -> Type)) (l: list a) : prop =
+ pairwise_and'_prop f l;
+ normal (pairwise_and' f l)
+Pairwise disjunction
+
+pairwise_or'
+pairwise_or f l disjoins f on all pairs excluding the diagonal
+i.e., pairwise_or f a; b; c = f a b \/ f a c \/ f b c[@@ __reduce__]
+let pairwise_or' #a (f: (a -> a -> Type)) (l: list a) : Type = pairwise_op' l_or f l False
+pairwise_or'_nil
+pairwise_or' showing it to be a fold with big_or'val pairwise_or'_nil (#a: Type) (f: (a -> a -> Type0)) : Lemma (pairwise_or' f [] == False)
+pairwise_or'_cons
+pairwise_or' showing it to be a fold with big_or'val pairwise_or'_cons (#a: Type) (f: (a -> a -> Type0)) (hd: a) (tl: list a)
+ : Lemma (pairwise_or' f (hd :: tl) == (big_or' (f hd) tl \/ pairwise_or' f tl))
+pairwise_or'_prop
+pairwise_or' f l is a prop
+See the remark above on the style of proof for propval pairwise_or'_prop (#a: Type) (f: (a -> a -> Type)) (l: list a)
+ : Lemma ((pairwise_or' f l) `subtype_of` unit)
+pairwise_or'_exists
+pairwise_or' f l for symmetric, anti-reflexive relations f
+interpreted as existential quantification over
+pairs of list elementsval pairwise_or'_exists (#a: Type) (f: (a -> a -> Type)) (l: list a)
+ : Lemma (requires symmetric f /\ anti_reflexive f)
+ (ensures (pairwise_or' f l <==> (exists x y. L.memP x l /\ L.memP y l /\ f x y)))
+pairwise_or'_exists_no_repeats
+pairwise_or' f l for symmetric, anti-reflexive relations f
+interpreted as existential quantification over
+pairs of list elementsval pairwise_or'_exists_no_repeats (#a: Type) (f: (a -> a -> Type)) (l: list a)
+ : Lemma (requires symmetric f /\ L.no_repeats_p l)
+ (ensures (pairwise_or' f l <==> (exists x y. L.memP x l /\ L.memP y l /\ x =!= y /\ f x y)))
pairwise_or f l is an implicitly reducing variant of pairwise_or'
+It is defined in prop[@@ __reduce__]
+unfold
+let pairwise_or #a (f: (a -> a -> Type)) (l: list a) : prop =
+ pairwise_or'_prop f l;
+ normal (pairwise_or' f l)
module FStar.BitVector
-let (is_subset_vec (#n:pos) (a:bv_t n) (b:bv_t n)):forall i:nat.{:pattern } ==>(<(i, n), ==>(=(index b i, false), =(index a i, false)))let (is_superset_vec (#n:pos) (a:bv_t n) (b:bv_t n)):forall i:nat.{:pattern } ==>(<(i, n), ==>(=(index b i, true), =(index a i, true)))val lemma_slice_subset_vec:Unidentified product: [#n:pos] Unidentified product: [a:bv_t n] Unidentified product: [b:bv_t n] Unidentified product: [i:nat] Unidentified product: [j:j:nat:{&&(<(i, j), <=(j, n))}] (Lemma ((requires is_subset_vec a b)) ((ensures (match n with 1 -> True | _ -> is_subset_vec #(-(j, i)) (slice a i j) (slice b i j)))))val lemma_slice_superset_vec:Unidentified product: [#n:pos] Unidentified product: [a:bv_t n] Unidentified product: [b:bv_t n] Unidentified product: [i:nat] Unidentified product: [j:j:nat:{&&(<(i, j), <=(j, n))}] (Lemma ((requires is_superset_vec a b)) ((ensures (match n with 1 -> True | _ -> is_superset_vec #(-(j, i)) (slice a i j) (slice b i j)))))
+FStar.BitVector
+
+
+
+bv_t DEFINED IS UNRELATED TO THE SMT SOLVER'S
+THEORY OF BIT VECTORS. SEE FStar.BV FOR THAT.
+bv_t
+bv_t n is just a sequence of booleans of length ntype bv_t (n: nat) = vec: seq bool {length vec = n}
+Common constants
+
+zero_vec
+n zero vectorlet zero_vec (#n: pos) : bv_t n = create n false
+elem_vec
+n whose ith bit is set, onlylet elem_vec (#n: pos) (i: nat{i < n}) : bv_t n = upd (create n false) i true
+ones_vec
+n vector all of whose bits are setlet ones_vec (#n: pos) : bv_t n = create n true
+logand_vec
+let rec logand_vec (#n: pos) (a b: bv_t n) : Tot (bv_t n) =
+ if n = 1
+ then create 1 (index a 0 && index b 0)
+ else append (create 1 (index a 0 && index b 0)) (logand_vec #(n - 1) (slice a 1 n) (slice b 1 n))
+logand_vec_definition
+logand defined in terms of its indexing behaviorlet rec logand_vec_definition (#n: pos) (a b: bv_t n) (i: nat{i < n})
+ : Lemma (ensures index (logand_vec #n a b) i = (index a i && index b i))
+ [SMTPat (index (logand_vec #n a b) i)] =
+ if i = 0 then () else logand_vec_definition #(n - 1) (slice a 1 n) (slice b 1 n) (i - 1)
+logxor_vec
+let rec logxor_vec (#n: pos) (a b: bv_t n) : Tot (bv_t n) =
+ if n = 1
+ then create 1 (index a 0 <> index b 0)
+ else append (create 1 (index a 0 <> index b 0)) (logxor_vec #(n - 1) (slice a 1 n) (slice b 1 n))
+logxor_vec_definition
+logxor defined in terms of its indexing behaviorlet rec logxor_vec_definition (#n: pos) (a b: bv_t n) (i: nat{i < n})
+ : Lemma (ensures index (logxor_vec #n a b) i = (index a i <> index b i))
+ [SMTPat (index (logxor_vec #n a b) i)] =
+ if i = 0 then () else logxor_vec_definition #(n - 1) (slice a 1 n) (slice b 1 n) (i - 1)
+logor_vec
+let rec logor_vec (#n: pos) (a b: bv_t n) : Tot (bv_t n) =
+ if n = 1
+ then create 1 (index a 0 || index b 0)
+ else append (create 1 (index a 0 || index b 0)) (logor_vec #(n - 1) (slice a 1 n) (slice b 1 n))
+logor_vec_definition
+logor defined in terms of its indexing behaviorlet rec logor_vec_definition (#n: pos) (a b: bv_t n) (i: nat{i < n})
+ : Lemma (ensures index (logor_vec #n a b) i = (index a i || index b i))
+ [SMTPat (index (logor_vec #n a b) i)] =
+ if i = 0 then () else logor_vec_definition #(n - 1) (slice a 1 n) (slice b 1 n) (i - 1)
+lognot_vec
+let rec lognot_vec (#n: pos) (a: bv_t n) : Tot (bv_t n) =
+ if n = 1
+ then create 1 (not (index a 0))
+ else append (create 1 (not (index a 0))) (lognot_vec #(n - 1) (slice a 1 n))
+lognot_vec_definition
+lognot defined in terms of its indexing behaviorlet rec lognot_vec_definition (#n: pos) (a: bv_t n) (i: nat{i < n})
+ : Lemma (ensures index (lognot_vec #n a) i = not (index a i))
+ [SMTPat (index (lognot_vec #n a) i)] =
+ if i = 0 then () else lognot_vec_definition #(n - 1) (slice a 1 n) (i - 1)
+lemma_xor_bounded
+x and y are false at a given index i, then so is they logical xor at ilet lemma_xor_bounded (m: pos) (n: nat) (x y: bv_t m)
+ : Lemma
+ (requires
+ (forall (i: nat).
+ (i < m /\ i >= n) ==>
+ (Seq.index x (m - 1 - i) = false /\ Seq.index y (m - 1 - i) = false)))
+ (ensures
+ (forall (i: nat). (i < m /\ i >= n) ==> (Seq.index (logxor_vec x y) (m - 1 - i) = false))) =
+ ()
+is_subset_vec
+let is_subset_vec (#n: pos) (a b: bv_t n) =
+ forall (i: nat). i < n ==> index b i = false ==> index a i = false
+is_superset_vec
+let is_superset_vec (#n: pos) (a b: bv_t n) =
+ forall (i: nat). i < n ==> index b i = true ==> index a i = true
+lemma_slice_subset_vec
+let lemma_slice_subset_vec (#n: pos) (a b: bv_t n) (i: nat) (j: nat{i < j && j <= n})
+ : Lemma (requires is_subset_vec a b)
+ (ensures
+ (match n with
+ | 1 -> True
+ | _ -> is_subset_vec #(j - i) (slice a i j) (slice b i j))) = ()
+lemma_slice_superset_vec
+let lemma_slice_superset_vec (#n: pos) (a b: bv_t n) (i: nat) (j: nat{i < j && j <= n})
+ : Lemma (requires is_superset_vec a b)
+ (ensures
+ (match n with
+ | 1 -> True
+ | _ -> is_superset_vec #(j - i) (slice a i j) (slice b i j))) = ()
+Shift operators
+
+shift_left_vec
+a left by s bits, filling with zeroeslet shift_left_vec (#n: pos) (a: bv_t n) (s: nat) : bv_t n =
+ if s >= n then zero_vec #n else if s = 0 then a else append (slice a s n) (zero_vec #s)
+shift_left_vec_lemma_1
+let shift_left_vec_lemma_1 (#n: pos) (a: bv_t n) (s: nat) (i: nat{i < n && i >= n - s})
+ : Lemma (ensures index (shift_left_vec #n a s) i = false)
+ [SMTPat (index (shift_left_vec #n a s) i)] = ()
+shift_left_vec_lemma_2
+let shift_left_vec_lemma_2 (#n: pos) (a: bv_t n) (s: nat) (i: nat{i < n && i < n - s})
+ : Lemma (ensures index (shift_left_vec #n a s) i = index a (i + s))
+ [SMTPat (index (shift_left_vec #n a s) i)] = ()
+shift_right_vec
+a right by s bits, filling with zeroeslet shift_right_vec (#n: pos) (a: bv_t n) (s: nat) : bv_t n =
+ if s >= n then zero_vec #n else if s = 0 then a else append (zero_vec #s) (slice a 0 (n - s))
+shift_right_vec_lemma_1
+let shift_right_vec_lemma_1 (#n: pos) (a: bv_t n) (s: nat) (i: nat{i < n && i < s})
+ : Lemma (ensures index (shift_right_vec #n a s) i = false)
+ [SMTPat (index (shift_right_vec #n a s) i)] = ()
+shift_right_vec_lemma_2
+let shift_right_vec_lemma_2 (#n: pos) (a: bv_t n) (s: nat) (i: nat{i < n && i >= s})
+ : Lemma (ensures index (shift_right_vec #n a s) i = index a (i - s))
+ [SMTPat (index (shift_right_vec #n a s) i)] = ()
+shift_arithmetic_right_vec
+a, interpreting position 0 as the
+most-significant bit, and using its value to filllet shift_arithmetic_right_vec (#n: pos) (a: bv_t n) (s: nat) : bv_t n =
+ if index a 0
+ then if s >= n then ones_vec #n else if s = 0 then a else append (ones_vec #s) (slice a 0 (n - s))
+ else shift_right_vec a s
+shift_arithmetic_right_vec_lemma_1
+let shift_arithmetic_right_vec_lemma_1 (#n: pos) (a: bv_t n) (s: nat) (i: nat{i < n && i < s})
+ : Lemma (ensures index (shift_arithmetic_right_vec #n a s) i = index a 0)
+ [SMTPat (index (shift_arithmetic_right_vec #n a s) i)] = ()
+shift_arithmetic_right_vec_lemma_2
+let shift_arithmetic_right_vec_lemma_2 (#n: pos) (a: bv_t n) (s: nat) (i: nat{i < n && i >= s})
+ : Lemma (ensures index (shift_arithmetic_right_vec #n a s) i = index a (i - s))
+ [SMTPat (index (shift_arithmetic_right_vec #n a s) i)] = ()
module FStar.Bytes
-typeabbrev
+FStar.Bytes
+
+
+S
+U
+U8
+U16
+U32
+U64
+Str
+Chr
+unfold let u8 = U8.t
+unfold let u16 = U16.t
+unfold let u32 = U32.t
+byte
val bytes:t:Type0:{hasEq t}val reveal:Unidentified product: [bytes] (GTot (S.seq byte))unfold type byte = u8
+bytes
+val bytes : t:Type0{hasEq t}
+val len : bytes -> u32
unfold let length b = FStar.UInt32.v (len b)
+reveal
val get:Unidentified product: [b:bytes] Unidentified product: [pos:pos:u32:{<(U32.v pos, length b)}] (Pure byte ((requires True)) ((ensures ((fun y -> ==(y, S.index (reveal b) (U32.v pos)))))))val reveal:
+ bytes
+ -> GTot (S.seq byte)
val length_reveal:
+ x:bytes
+ -> Lemma (ensures (S.length (reveal x) = length x))
+ [SMTPatOr [[SMTPat (S.length (reveal x))];
+ [SMTPat (len x)]]]
val hide:
+ s:S.seq byte{S.length s < pow2 32}
+ -> GTot bytes
val hide_reveal:
+ x:bytes
+ -> Lemma (ensures (hide (reveal x) = x))
+ [SMTPat (reveal x)]
val reveal_hide:
+ x:S.seq byte{S.length x < pow2 32}
+ -> Lemma (ensures (reveal (hide x) == x))
+ [SMTPat (hide x)]
type lbytes (l:nat) = b:bytes{length b = l}
+type kbytes (k:nat) = b:bytes{length b < pow2 k}
let lbytes32 (l:UInt32.t) = b:bytes{len b = l}
val empty_bytes : lbytes 0
+val empty_unique:
+ b:bytes
+ -> Lemma (length b = 0 ==> b = empty_bytes)
+ [SMTPat (len b)]
+get
val create:Unidentified product: [len:u32] Unidentified product: [v:byte] b:lbytes (U32.v len):{forall i:i:u32:{let open U32 in <^(i, len)}.{:pattern .[](b, i)} ==(.[](b, i), v)}val get:
+ b:bytes
+ -> pos:u32{U32.v pos < length b}
+ -> Pure byte
+ (requires True)
+ (ensures (fun y -> y == S.index (reveal b) (U32.v pos)))
unfold let op_String_Access = get
unfold let index (b:bytes) (i:nat{i < length b}) = get b (U32.uint_to_t i)
let equal b1 b2 =
+ length b1 = length b2 /\
+ (forall (i:u32{U32.v i < length b1}).{:pattern (b1.[i]); (b2.[i])} b1.[i] == b2.[i])
val extensionality:
+ b1:bytes
+ -> b2:bytes
+ -> Lemma (requires (equal b1 b2))
+ (ensures (b1 = b2))
+create
val append:Unidentified product: [b1:bytes] Unidentified product: [b2:bytes] (Pure bytes ((requires (UInt.size (+(length b1, length b2)) U32.n))) ((ensures ((fun b -> ==(reveal b, S.append (reveal b1) (reveal b2)))))))val create:
+ len:u32
+ -> v:byte
+ -> b:lbytes (U32.v len){forall (i:u32{U32.(i <^ len)}).{:pattern b.[i]} b.[i] == v}
unfold
+let create_ (n:nat{FStar.UInt.size n U32.n}) v = create (U32.uint_to_t n) v
val init:
+ len:u32
+ -> f:(i:u32{U32.(i <^ len)} -> byte)
+ -> b:lbytes (U32.v len){forall (i:u32{U32.(i <^ len)}).{:pattern b.[i]} b.[i] == f i}
val abyte (b:byte) : lbytes 1
val twobytes (b:byte*byte) : lbytes 2
+append
let (fits_in_k_bytes (n:nat) (k:nat)):FStar.UInt.size n (op_Multiply 8 k)val append:
+ b1:bytes
+ -> b2:bytes
+ -> Pure bytes
+ (requires (UInt.size (length b1 + length b2) U32.n))
+ (ensures (fun b -> reveal b == S.append (reveal b1) (reveal b2)))
+unfold let op_At_Bar = append
val slice:
+ b:bytes
+ -> s:u32
+ -> e:u32{U32.(s <=^ e) /\ U32.v e <= length b}
+ -> r:bytes{reveal r == Seq.slice (reveal b) (U32.v s) (U32.v e)}
+let slice_ b (s:nat) (e:nat{s <= e /\ e <= length b}) = slice b (U32.uint_to_t s) (U32.uint_to_t e)
val sub:
+ b:bytes
+ -> s:u32
+ -> l:u32{U32.v s + U32.v l <= length b}
+ -> r:bytes{reveal r == Seq.slice (reveal b) (U32.v s) (U32.v s + U32.v l)}
val split:
+ b:bytes
+ -> k:u32{U32.v k <= length b}
+ -> p:(bytes*bytes){
+ let x, y = p in
+ (reveal x, reveal y) == Seq.split (reveal b) (U32.v k)}
unfold let split_ b (k:nat{FStar.UInt.size k U32.n /\ k < length b}) = split b (U32.uint_to_t k)
+fits_in_k_bytes
val repr_bytes:Unidentified product: [n:nat] k:pos:{fits_in_k_bytes n k}let fits_in_k_bytes (n:nat) (k:nat) = FStar.UInt.size n (op_Multiply 8 k)
+type uint_k (k:nat) = n:nat{fits_in_k_bytes n k}
+repr_bytes
A better implementation of BufferBytes, formerly found in miTLS val repr_bytes:
+ n:nat
+ -> k:pos{fits_in_k_bytes n k}
val lemma_repr_bytes_values:
+ n:nat
+ -> Lemma (ensures ( let k = repr_bytes n in
+ if n < 256 then k==1
+ else if n < 65536 then k==2
+ else if n < 16777216 then k==3
+ else if n < 4294967296 then k==4
+ else if n < 1099511627776 then k==5
+ else if n < 281474976710656 then k==6
+ else if n < 72057594037927936 then k==7
+ else if n < 18446744073709551616 then k==8
+ else True ))
+ [SMTPat (repr_bytes n)]
val repr_bytes_size:
+ k:nat
+ -> n:uint_k k
+ -> Lemma (ensures (repr_bytes n <= k))
+ [SMTPat (fits_in_k_bytes n k)]
val int_of_bytes:
+ b:bytes
+ -> Tot (uint_k (length b))
val bytes_of_int:
+ k:nat
+ -> n:nat{repr_bytes n <= k /\ k < pow2 32}
+ -> lbytes k
val int_of_bytes_of_int:
+ #k:nat{k <= 32}
+ -> n:uint_k k
+ -> Lemma (ensures (int_of_bytes (bytes_of_int k n) == n))
+ [SMTPat (bytes_of_int k n)]
val bytes_of_int_of_bytes:
+ b:bytes{length b <= 32}
+ -> Lemma (ensures (bytes_of_int (length b) (int_of_bytes b) == b))
+ [SMTPat (int_of_bytes b)]
uint32 instead of int32 etc?val int32_of_bytes:
+ b:bytes{length b <= 4}
+ -> n:u32{U32.v n == int_of_bytes b}
val int16_of_bytes:
+ b:bytes{length b <= 2}
+ -> n:u16{U16.v n == int_of_bytes b}
val int8_of_bytes:
+ b:bytes{length b = 1}
+ -> n:u8{U8.v n = int_of_bytes b}
val bytes_of_int32:
+ n:U32.t
+ -> b:lbytes 4{b == bytes_of_int 4 (U32.v n)}
val bytes_of_int16:
+ n:U16.t
+ -> b:lbytes 2{b == bytes_of_int 2 (U16.v n)}
val bytes_of_int8:
+ n:U8.t
+ -> b:lbytes 1{b == bytes_of_int 1 (U8.v n)}
type minbytes (n:nat) = b:bytes{length b >= n}
val xor:
+ n:u32
+ -> b1:minbytes (U32.v n)
+ -> b2:minbytes (U32.v n)
+ -> b:bytes{len b = n}
unfold let xor_ (#n:nat{FStar.UInt.size n U32.n}) (b1:minbytes n) (b2:minbytes n) = xor (U32.uint_to_t n) b1 b2
val xor_commutative:
+ n:u32
+ -> b1:minbytes (U32.v n)
+ -> b2:minbytes (U32.v n)
+ -> Lemma (ensures (xor n b1 b2 == xor n b2 b1))
+ [SMTPat (xor n b1 b2)]
val xor_append:
+ b1:bytes
+ -> b2:bytes{FStar.UInt.size (length b1 + length b2) U32.n}
+ -> x1:bytes{len x1 = len b1}
+ -> x2:bytes{len x2 = len b2}
+ -> Lemma (ensures (xor U32.(len b1 +^ len b2)
+ (b1 @| b2)
+ (x1 @| x2)
+ ==
+ xor (len b1) b1 x1 @| xor (len b2) b2 x2))
val xor_idempotent:
+ n:u32
+ -> b1:lbytes (U32.v n)
+ -> b2:lbytes (U32.v n)
+ -> Lemma (ensures (xor n (xor n b1 b2) b2 == b1))
val utf8_encode:
+ s:string{Str.maxlen s (pow2 30)}
+ -> b:bytes{length b <= op_Multiply 4 (Str.length s)}
val iutf8_opt:
+ m:bytes
+ -> (option (s:string{Str.maxlen s (pow2 30) /\ utf8_encode s == m}))
val string_of_hex: string -> Tot string
val bytes_of_hex: string -> Tot bytes
+val hex_of_string: string -> Tot string
+val hex_of_bytes: bytes -> Tot string
+val print_bytes: bytes -> Tot string
+val bytes_of_string: string -> bytes //abytes
+
+B M type lbuffer (l:UInt32.t) = b:B.buffer UInt8.t {B.length b == U32.v l}
val of_buffer (l:UInt32.t) (#p #q:_) (buf:B.mbuffer UInt8.t p q{B.length buf == U32.v l})
+ : Stack (b:bytes{length b = UInt32.v l})
+ (requires fun h0 ->
+ B.live h0 buf)
+ (ensures fun h0 b h1 ->
+ B.(modifies loc_none h0 h1) /\
+ b = hide (B.as_seq h0 buf))
val store_bytes: src:bytes { length src <> 0 } ->
+ dst:lbuffer (len src) ->
+ Stack unit
+ (requires (fun h0 -> B.live h0 dst))
+ (ensures (fun h0 r h1 ->
+ M.(modifies (loc_buffer dst) h0 h1) /\
+ Seq.equal (reveal src) (B.as_seq h1 dst)))
module FStar.Calc
-
+FStar.Calc
+
+
+relation )](FStar.Preorder ( for relation *).html)noeq
+type calc_proof #t : list (relation t) -> t -> t -> Type =
+ | CalcRefl : #x:t -> calc_proof [] x x
+ | CalcStep : rs:(list (relation t)) -> #p:(relation t) ->
+ #x:t -> #y:t -> #z:t -> calc_proof rs x y -> squash (p y z) -> calc_proof (p::rs) x z
noeq
+type calc_pack #t (x y : t) = {
+ rels : list (relation t);
+ proof : calc_proof rels x y
+}
[@@"opaque_to_smt"]
+let pk_rels #t #x #y (pk : calc_pack #t x y) = pk.rels
let rec calc_chain_related (#t : Type) (rs : list (relation t)) (x y : t) : Tot Type0 (decreases rs) =
+ match rs with
+ | [] -> x == y
:t annotation below matters a lot for compactness of the formula!| r1::rs -> exists (w:t). calc_chain_related rs x w /\ r1 w y
t's related by rs (reversed!) has its endpoints related by p[@@"opaque_to_smt"]
+let calc_chain_compatible (#t : Type) (rs : list (relation t)) (p : relation t) : Tot Type0 =
+ forall x y. calc_chain_related rs x y ==> p x y
[@@"opaque_to_smt"]
+let rec elim_calc_proof #t rs (#x #y : t) (pf : calc_proof rs x y)
+ : Lemma (ensures (calc_chain_related rs x y))
+ (decreases pf) =
+ match pf with
+ | CalcRefl -> ()
+ | CalcStep rs #p' #x #y #z pf p'xy -> elim_calc_proof rs pf
[@@"opaque_to_smt"]
+let _calc_init (#t:Type) (x : t) : calc_proof [] x x = CalcRefl
[@@"opaque_to_smt"]
+let calc_init (#t:Type) (x : t) : calc_pack x x = { rels = []; proof = _calc_init x }
[@@"opaque_to_smt"]
+let _calc_step (#t:Type) (#rs : list (relation t)) (#x #y : t)
+ (p : relation t) (* Relation for this step *)
+ (z : t) (* Next expression *)
+ (pf : unit -> GTot (calc_proof rs x y)) (* Rest of the proof *)
+ (j : unit -> Tot (squash (p y z))) (* Justification, thunked to avoid confusion such as #1397 *)
+ : GTot (calc_proof (p::rs) x z)
= CalcStep rs #p (pf ()) (j ())
[@@"opaque_to_smt"]
+let calc_step (#t:Type) (#x #y : t) (p : relation t)
+ (z : t)
+ (pf : unit -> GTot (calc_pack x y))
+ (j : unit -> Tot (squash (p y z)))
+ : GTot (calc_pack x z)
+ =
+ let pk = pf () in
+ { rels = p::pk.rels ; proof = _calc_step p z (fun () -> pk.proof) j }
delta_only ``%calc_chain_compatible; %calc_chain_related`;[@@"opaque_to_smt"]
+let calc_finish (#t:Type) (p : relation t) (#x #y : t) (pf : unit -> GTot (calc_pack x y))
+ : Lemma (requires (norm [delta_only [`%calc_chain_compatible; `%calc_chain_related;
+ "FStar.Calc.__proj__Mkcalc_pack__item__rels";
+ `%calc_step; `%_calc_step;
+ `%calc_init; `%_calc_init; `%pk_rels];
+ iota;
+ zeta] (labeled range_0 "Could not prove that this calc-chain is compatible"
+ (calc_chain_compatible (pk_rels (pf ())) p))))
+ (ensures (p x y))
+ = let pk = pf () in
+ elim_calc_proof pk.rels pk.proof
val calc_push_impl (#p #q : Type) (f : squash p -> GTot (squash q)) : GTot (squash (p ==> q))
+let calc_push_impl #p #q f = Classical.arrow_to_impl f
module FStar.Char
-
+FStar.Char
+
+
+char type, an abstract type
+representing UTF-8 characters.
+
+U32
+
+char:eqtype
+char is a new primitive type with decidable equalitynew
+val char:eqtype
+char_code
+char_code is the representation of a UTF-8 char code in
+an unsigned 32-bit integer whose value is at most 2^21type char_code = n: U32.t{U32.v n < pow2 21}
+u32_of_char
+char_code of a charval u32_of_char: char -> Tot char_code
+char_of_u32
+char_code to a charval char_of_u32: char_code -> Tot char
+char_of_u32_of_char
+char to char_code is the identityval char_of_u32_of_char (c: char)
+ : Lemma (ensures (char_of_u32 (u32_of_char c) == c)) [SMTPat (u32_of_char c)]
+u32_of_char_of_u32
+char to char_code is the identityval u32_of_char_of_u32 (c: char_code)
+ : Lemma (ensures (u32_of_char (char_of_u32 c) == c)) [SMTPat (char_of_u32 c)]
+int_of_char
+U32.t
+to represent a char_codelet int_of_char (c: char) : nat = U32.v (u32_of_char c)
+let char_of_int (i: nat{i < pow2 21}) : char = char_of_u32 (U32.uint_to_t i)
+lowercase
+val lowercase: char -> Tot char
+val uppercase: char -> Tot char
#set-options "--lax"
private unfold
+let __char_of_int (x: int) : char = char_of_int x
+#reset-options
+http://www.apache.org/licenses/LICENSE-2.0
+
+FStar.Classical.Sugar
+
+
+
+
+p /\ q and p ==> q, the well-typedness of q is in a
+context assuming squash pp \/ q, the well-typedness of q is in a context assuming
+squash (~p)q functions that depend on squash p or
+squash (~p).
+
+
+
+
+forall_elim
+val forall_elim
+ (#a:Type)
+ (#p:a -> Type)
+ (v:a)
+ (f:squash (forall (x:a). p x))
+ : Tot (squash (p v))
+exists_elim
+qval exists_elim
+ (#t:Type)
+ (#p:t -> Type)
+ (#q:Type)
+ ($s_ex_p: squash (exists (x:t). p x))
+ (f: (x:t -> squash (p x) -> Tot (squash q)))
+ : Tot (squash q)
+implies_elim
+let implies_elim
+ (p:Type)
+ (q:Type)
+ (_:squash (p ==> q))
+ (f:unit -> squash p)
+ : squash q
+ = f()
+or_elim
+
+
+val or_elim
+ (p:Type)
+ (q:squash (~p) -> Type)
+ (r:Type)
+ (p_or:squash (p \/ q()))
+ (left:squash p -> Tot (squash r))
+ (right:squash (~p) -> squash (q()) -> Tot (squash r))
+ : Tot (squash r)
+and_elim
+
+
+val and_elim
+ (p:Type)
+ (q:squash p -> Type)
+ (r:Type)
+ (_:squash (p /\ q()))
+ (f:squash p -> squash (q()) -> Tot (squash r))
+ : Tot (squash r)
+forall_intro
+val forall_intro
+ (a:Type)
+ (p:a -> Type)
+ (f: (x:a -> Tot (squash (p x))))
+ : Tot (squash (forall x. p x))
+exists_intro
+val exists_intro
+ (a:Type)
+ (p:a -> Type)
+ (v:a)
+ (x: unit -> squash (p v))
+ : Tot (squash (exists x. p x))
+implies_intro
+
+
+val implies_intro
+ (p:Type)
+ (q:squash p -> Type)
+ (f:(squash p -> Tot (squash (q()))))
+ : Tot (squash (p ==> q()))
+or_intro_left
+
+
+val or_intro_left
+ (p:Type)
+ (q:squash (~p) -> Type)
+ (f:unit -> squash p)
+ : Tot (squash (p \/ q()))
+or_intro_right
+
+
+val or_intro_right
+ (p:Type)
+ (q:squash (~p) -> Type)
+ (f:squash (~p) -> squash (q()))
+ : Tot (squash (p \/ q()))
+and_intro
+
+
+val and_intro
+ (p:Type)
+ (q:squash p -> Type)
+ (left:unit -> squash p)
+ (right:squash p -> squash (q()))
+ : Tot (squash (p /\ q()))
module FStar.Classical
-
+FStar.Classical
+
+
+==>, /\, \/, forall, exists and ==,
+defined in Prims in terms of the squash type. See Prims and
+FStar.Squash for basic explanations of the squash type.
+
+squash p is proof-irrelevant proof of p, expressed as a unit
+refinement.Lemma p is also a proof-irrelevant proof of p, expressed as
+a postcondition of a unit-returning Ghost computation.
+give_witness
+give_witness x transforms a constructive proof x:a into a
+proof-irrelevant postcondition. It is similar to
+FStar.Squash.return_squashval give_witness (#a: Type) (_: a) : Lemma (ensures a)
+give_witness_from_squash
+give_witness_from_squash s moves from a unit-refinement to a
+postcondition. It is similar to FStar.Squash.give_proofval give_witness_from_squash (#a: Type) (_: squash a) : Lemma (ensures a)
+lemma_to_squash_gtot
+val lemma_to_squash_gtot (#a: Type) (#p: (a -> GTot Type)) ($_: (x: a -> Lemma (p x))) (x: a)
+ : GTot (squash (p x))
+Equality
+
+get_equality
+FStar.Squash.get_proof, but avoiding an extra squash,
+since == is already squashed.val get_equality (#t: Type) (a b: t) : Pure (a == b) (requires (a == b)) (ensures (fun _ -> True))
+Implication
+
+impl_to_arrow
+a ==> b into a squash a -> squash b. Note a ==> b is
+defined as squash (a -> b), so this distributes the squash over the arrow.val impl_to_arrow (#a #b: Type0) (_: (a ==> b)) (_: squash a) : Tot (squash b)
+arrow_to_impl
+impl_to_arrowval arrow_to_impl (#a #b: Type0) (_: (squash a -> GTot (squash b))) : GTot (a ==> b)
+impl_intro_gtot
+arrow_to_impl, but without squashing proofs on the leftval impl_intro_gtot (#p #q: Type0) ($_: (p -> GTot q)) : GTot (p ==> q)
+impl_intro
+arrow_to_impl, but not squashing the proof of p on the LHS.val impl_intro (#p #q: Type0) ($_: (p -> Lemma q)) : Lemma (p ==> q)
+move_requires
+val move_requires
+ (#a: Type)
+ (#p #q: (a -> Type))
+ ($_: (x: a -> Lemma (requires (p x)) (ensures (q x))))
+ (x: a)
+ : Lemma (p x ==> q x)
move_requires and forall_intro
+move_requires_2
+move_requiresval move_requires_2
+ (#a #b: Type)
+ (#p #q: (a -> b -> Type))
+ ($_: (x: a -> y: b -> Lemma (requires (p x y)) (ensures (q x y))))
+ (x: a)
+ (y: b)
+ : Lemma (p x y ==> q x y)
+move_requires_3
+move_requiresval move_requires_3
+ (#a #b #c: Type)
+ (#p #q: (a -> b -> c -> Type))
+ ($_: (x: a -> y: b -> z: c -> Lemma (requires (p x y z)) (ensures (q x y z))))
+ (x: a)
+ (y: b)
+ (z: c)
+ : Lemma (p x y z ==> q x y z)
+impl_intro_gen
+q whose well-formedness depends on the
+predicate p, it is convenient to have q appear only under a
+context where p is know to be valid.val impl_intro_gen (#p: Type0) (#q: (squash p -> Tot Type0)) (_: (squash p -> Lemma (q ())))
+ : Lemma (p ==> q ())
+Universal quantification
+
+
+x and to prove p x for it, i.e., to prove
+x:a -> Lemma (p x) and to turn this into a proof for forall x. p x. Functions like forall_intro in this module let you do
+just that.forall x. p x.assume
+val p : nat -> prop
+
+let proof1 =
+ let lem (x:nat)
+ : Lemma (ensures p x)
+ = admit()
+ in
+ forall_intro lem;
+ assert (forall x. p x)
+
+let proof2 =
+ let lem (x:nat)
+ : Lemma (ensures p x)
+ `SMTPat (p x)`
+ = admit()
+ in
+ assert (forall x. p x)
proof1, we prove an auxiliary lemma lem and then use
+forall_intro to turn it into a proof of forall x. p x.proof2, we simply decorate lem with an SMT pattern to
+allow the solver to use that lemma to prove forall x. p x
+directly.proof2 is often more robust for several reasons:
+
+forall_intro only works with lemmas that do not have
+preconditions. E.g., if you wanted to prove forall x. q x ==>
+p x, you would have had to prove lemwith the typex:nat ->
+Lemma (q x ==> p x). In contrast, in the style of proof2, you could have proven [x:nat -> Lemma (requires q x) (ensures p x), which is easier, since you can assume the precondition q x]. To use this style of lemma-with-precondition with [forall_intro](#forall_intro), one typically must also use [move_requires](#move_requires) to coerce a lemma with a precondition into a lemma proving an implication, or to use [ghost_lemma`.forall_intro introduces a quantifier without an SMT
+pattern. This can pollute the local context with an unguarded
+quantifier, leading to inefficient proofs. Note, the variants
+forall_intro_with_pat help with this somewhat, but they only
+support a single pattern, rather than conjunctive and
+disjunctive patterns.forall_intro and its variants are available for only a fixed
+arity up to 4. The nested SMTPat lemma style of proof2 works
+are arbitrary arity.forall_intro etc. are
+more suitable.
+get_forall
+FStar.Squash.get_proof, but avoiding an
+extra squash, since forall is already squashed.val get_forall (#a: Type) (p: (a -> GTot Type0))
+ : Pure (forall (x: a). p x) (requires (forall (x: a). p x)) (ensures (fun _ -> True))
+forall_intro_gtot
+forall_intro_gtot f is equivalent to `return_squash
+(return_squash f)].val forall_intro_gtot (#a: Type) (#p: (a -> GTot Type)) ($_: (x: a -> GTot (p x)))
+ : Tot (squash (forall (x: a). p x))
+lemma_forall_intro_gtot
+val lemma_forall_intro_gtot (#a: Type) (#p: (a -> GTot Type)) ($_: (x: a -> GTot (p x)))
+ : Lemma (forall (x: a). p x)
+gtot_to_lemma
+p into a lemma
+ensuring p, effectively squashing the proof of p, while still
+retaining the arrow.val gtot_to_lemma (#a: Type) (#p: (a -> GTot Type)) ($_: (x: a -> GTot (p x))) (x: a) : Lemma (p x)
+forall_intro_squash_gtot
+lemma_forall_intro_gtot but with squashed
+proofs on both sides, including a redundant extra squash on the result.val forall_intro_squash_gtot (#a: Type) (#p: (a -> GTot Type)) ($_: (x: a -> GTot (squash (p x))))
+ : Tot (squash (forall (x: a). p x))
+forall_intro_squash_gtot_join
+lemma_forall_intro_gtot but with squashed
+proofs on both sidesval forall_intro_squash_gtot_join
+ (#a: Type)
+ (#p: (a -> GTot Type))
+ ($_: (x: a -> GTot (squash (p x))))
+ : Tot (forall (x: a). p x)
+forall_intro
+val forall_intro (#a: Type) (#p: (a -> GTot Type)) ($_: (x: a -> Lemma (p x)))
+ : Lemma (forall (x: a). p x)
+forall_intro_with_pat
+val forall_intro_with_pat
+ (#a: Type)
+ (#c: (x: a -> Type))
+ (#p: (x: a -> GTot Type0))
+ ($pat: (x: a -> Tot (c x)))
+ ($_: (x: a -> Lemma (p x)))
+ : Lemma (forall (x: a). {:pattern (pat x)} p x)
+forall_intro_sub
+forall_intro. The only
+difference is that rather in forall_intro f the type of f is
+unified with expected type of that argument, leading to better
+resolution of implicit variables.val forall_intro_sub (#a: Type) (#p: (a -> GTot Type)) (_: (x: a -> Lemma (p x)))
+ : Lemma (forall (x: a). p x)
forall_intro_sub f allows
+the use of subtyping when comparing the type of f to the
+expected type of the argument. This will likely mean that the
+implicit arguments, notably p, will have to be provided
+explicilty.
+forall_intro_2
+forall_introval forall_intro_2
+ (#a: Type)
+ (#b: (a -> Type))
+ (#p: (x: a -> b x -> GTot Type0))
+ ($_: (x: a -> y: b x -> Lemma (p x y)))
+ : Lemma (forall (x: a) (y: b x). p x y)
+forall_intro_2_with_pat
+forall_intro_with_patval forall_intro_2_with_pat
+ (#a: Type)
+ (#b: (a -> Type))
+ (#c: (x: a -> y: b x -> Type))
+ (#p: (x: a -> b x -> GTot Type0))
+ ($pat: (x: a -> y: b x -> Tot (c x y)))
+ ($_: (x: a -> y: b x -> Lemma (p x y)))
+ : Lemma (forall (x: a) (y: b x). {:pattern (pat x y)} p x y)
+forall_intro_3
+forall_introval forall_intro_3
+ (#a: Type)
+ (#b: (a -> Type))
+ (#c: (x: a -> y: b x -> Type))
+ (#p: (x: a -> y: b x -> z: c x y -> Type0))
+ ($_: (x: a -> y: b x -> z: c x y -> Lemma (p x y z)))
+ : Lemma (forall (x: a) (y: b x) (z: c x y). p x y z)
+forall_intro_3_with_pat
+forall_intro_with_patval forall_intro_3_with_pat
+ (#a: Type)
+ (#b: (a -> Type))
+ (#c: (x: a -> y: b x -> Type))
+ (#d: (x: a -> y: b x -> z: c x y -> Type))
+ (#p: (x: a -> y: b x -> z: c x y -> GTot Type0))
+ ($pat: (x: a -> y: b x -> z: c x y -> Tot (d x y z)))
+ ($_: (x: a -> y: b x -> z: c x y -> Lemma (p x y z)))
+ : Lemma (forall (x: a) (y: b x) (z: c x y). {:pattern (pat x y z)} p x y z)
+forall_intro_4
+forall_introval forall_intro_4
+ (#a: Type)
+ (#b: (a -> Type))
+ (#c: (x: a -> y: b x -> Type))
+ (#d: (x: a -> y: b x -> z: c x y -> Type))
+ (#p: (x: a -> y: b x -> z: c x y -> w: d x y z -> Type0))
+ ($_: (x: a -> y: b x -> z: c x y -> w: d x y z -> Lemma (p x y z w)))
+ : Lemma (forall (x: a) (y: b x) (z: c x y) (w: d x y z). p x y z w)
+forall_impl_intro
+arrow_to_impl with forall_intro.val forall_impl_intro
+ (#a: Type)
+ (#p #q: (a -> GTot Type))
+ ($_: (x: a -> squash (p x) -> Lemma (q x)))
+ : Lemma (forall x. p x ==> q x)
+ghost_lemma
+forall_intro, but with a lemma that has a precondition.val ghost_lemma
+ (#a: Type)
+ (#p: (a -> GTot Type0))
+ (#q: (a -> unit -> GTot Type0))
+ ($_: (x: a -> Lemma (requires p x) (ensures (q x ()))))
+ : Lemma (forall (x: a). p x ==> q x ())
q has an additional unit argument.
+Existential quantification
+
+exists_intro
+exists x. p x is to present a witness w such that p w.val exists_intro (#a: Type) (p: (a -> Type)) (witness: a)
+ : Lemma (requires (p witness)) (ensures (exists (x: a). p x))
exists_intro is very explicit, as with universal
+quantification and forall_intro, it is only available for a
+fixed arity.exists x y. p x y to prove instead
+exists xy. p (fst xy) (snd xy) and to allow the SMT solver to convert
+the latter to the former.
+exists_intro_not_all_not
+val exists_intro_not_all_not
+ (#a: Type)
+ (#p: (a -> Type))
+ ($f: ((x: a -> Lemma (~(p x))) -> Lemma False))
+ : Lemma (exists x. p x)
+forall_to_exists
+r is true for all x:a{p x}, then one can use
+forall_to_exists to establish (exists x. p x) ==> r.val forall_to_exists (#a: Type) (#p: (a -> Type)) (#r: Type) ($_: (x: a -> Lemma (p x ==> r)))
+ : Lemma ((exists (x: a). p x) ==> r)
+forall_to_exists_2
+forall_to_exists for two separate
+existentially quantified hypotheses.val forall_to_exists_2
+ (#a: Type)
+ (#p: (a -> Type))
+ (#b: Type)
+ (#q: (b -> Type))
+ (#r: Type)
+ ($f: (x: a -> y: b -> Lemma ((p x /\ q y) ==> r)))
+ : Lemma (((exists (x: a). p x) /\ (exists (y: b). q y)) ==> r)
+exists_elim
+goal, then the goal
+postcondition is valid.val exists_elim
+ (goal #a: Type)
+ (#p: (a -> Type))
+ (_: squash (exists (x: a). p x))
+ (_: (x: a{p x} -> GTot (squash goal)))
+ : Lemma goal
+Disjunction
+
+or_elim
+l \/ r into a goal whose well-formedness depends on
+l \/ rval or_elim
+ (#l #r: Type0)
+ (#goal: (squash (l \/ r) -> Tot Type0))
+ (hl: (squash l -> Lemma (goal ())))
+ (hr: (squash r -> Lemma (goal ())))
+ : Lemma ((l \/ r) ==> goal ())
+excluded_middle
+val excluded_middle (p: Type) : Lemma (requires (True)) (ensures (p \/ ~p))
module FStar.Date
-
+FStar.Date
+
+
+new
+val dateTime:Type0
+new
+val timeSpan:Type0
+now
+val now: unit -> EXT dateTime
+val secondsFromDawn: unit -> EXT (n: nat{n < pow2 32})
+val newTimeSpan: int -> int -> int -> int -> Tot timeSpan
+val addTimeSpan: dateTime -> timeSpan -> Tot dateTime
+val greaterDateTime: dateTime -> dateTime -> Tot bool
module FStar.DependentMap
-
+FStar.DependentMap
+
+
+x:key -> value x, where key supports decidable equality.
+
+create: To create the whole map from a functionupd: To update a map at a pointrestrict: To restrict the domain of a mapconcat: To concatenate maps by taking the union of their key spacesrename: To rename the keys of a mapmap: To map a function over the values of a map
+
+sel: To query the map for its value at a point
+t
+val t (key: eqtype) (value: (key -> Type u#v)) : Type u#v
+create
+val create (#key: eqtype) (#value: (key -> Tot Type)) (f: (k: key -> Tot (value k)))
+ : Tot (t key value)
+sel
+val sel (#key: eqtype) (#value: (key -> Tot Type)) (m: t key value) (k: key) : Tot (value k)
+sel_create
+
+val sel_create (#key: eqtype) (#value: (key -> Tot Type)) (f: (k: key -> Tot (value k))) (k: key)
+ : Lemma (ensures (sel #key #value (create f) k == f k)) [SMTPat (sel #key #value (create f) k)]
+upd
+val upd (#key: eqtype) (#value: (key -> Tot Type)) (m: t key value) (k: key) (v: value k)
+ : Tot (t key value)
+sel_upd_same
+k a map with an updated value v
+at kval sel_upd_same (#key: eqtype) (#value: (key -> Tot Type)) (m: t key value) (k: key) (v: value k)
+ : Lemma (ensures (sel (upd m k v) k == v)) [SMTPat (sel (upd m k v) k)]
+sel_upd_other
+k a map with an updated value v
+at a different key k'val sel_upd_other
+ (#key: eqtype)
+ (#value: (key -> Tot Type))
+ (m: t key value)
+ (k: key)
+ (v: value k)
+ (k': key)
+ : Lemma (requires (k' <> k))
+ (ensures (sel (upd m k v) k' == sel m k'))
+ [SMTPat (sel (upd m k v) k')]
+equal
+val equal (#key: eqtype) (#value: (key -> Tot Type)) (m1 m2: t key value) : prop
+equal_intro
+val equal_intro (#key: eqtype) (#value: (key -> Tot Type)) (m1 m2: t key value)
+ : Lemma (requires (forall k. sel m1 k == sel m2 k))
+ (ensures (equal m1 m2))
+ [SMTPat (equal m1 m2)]
+equal_refl
+equal is reflexiveval equal_refl (#key: eqtype) (#value: (key -> Tot Type)) (m: t key value)
+ : Lemma (ensures (equal m m)) [SMTPat (equal m m)]
+equal_elim
+equal can be eliminated into standard propositional equality
+(==), also proving that it is an equivalence relationval equal_elim (#key: eqtype) (#value: (key -> Tot Type)) (m1 m2: t key value)
+ : Lemma (requires (equal m1 m2)) (ensures (m1 == m2)) [SMTPat (equal m1 m2)]
+Restricting the domain of a map
+
+restrict
+pval restrict (#key: eqtype) (#value: (key -> Tot Type)) (p: (key -> Tot Type0)) (m: t key value)
+ : Tot (t (k: key{p k}) value)
+sel_restrict
+sel on restrict : the contents of the map isn't changedval sel_restrict
+ (#key: eqtype)
+ (#value: (key -> Tot Type))
+ (p: (key -> Tot Type0))
+ (m: t key value)
+ (k: key{p k})
+ : Lemma (ensures (sel (restrict p m) k == sel m k))
+Concatenating maps
+
+
+t k1 v1 and t k2 v2 produces a map
+t (either k1 k2) (concat_value v1 v2)
+concat_value
+let concat_value
+ (#key1: eqtype)
+ (value1: (key1 -> Tot Type))
+ (#key2: eqtype)
+ (value2: (key2 -> Tot Type))
+ (k: either key1 key2)
+ : Tot Type =
+ match k with
+ | Inl k1 -> value1 k1
+ | Inr k2 -> value2 k2
+concat
+val concat
+ (#key1: eqtype)
+ (#value1: (key1 -> Tot (Type u#v)))
+ (#key2: eqtype)
+ (#value2: (key2 -> Tot (Type u#v)))
+ (m1: t key1 value1)
+ (m2: t key2 value2)
+ : Tot (t (either key1 key2) (concat_value value1 value2))
+sel_concat_l
+sel on concat, for a key on the left picks a
+value from the left mapval sel_concat_l
+ (#key1: eqtype)
+ (#value1: (key1 -> Tot (Type u#v)))
+ (#key2: eqtype)
+ (#value2: (key2 -> Tot (Type u#v)))
+ (m1: t key1 value1)
+ (m2: t key2 value2)
+ (k1: key1)
+ : Lemma (ensures (sel (concat m1 m2) (Inl k1) == sel m1 k1))
+sel_concat_r
+sel on concat, for a key on the right picks a
+value from the right mapval sel_concat_r
+ (#key1: eqtype)
+ (#value1: (key1 -> Tot Type))
+ (#key2: eqtype)
+ (#value2: (key2 -> Tot Type))
+ (m1: t key1 value1)
+ (m2: t key2 value2)
+ (k2: key2)
+ : Lemma (ensures (sel (concat m1 m2) (Inr k2) == sel m2 k2))
+Renamings
+
+
+key2 to key1, we can revise a map from t key1 v to a map t key2 v, by composing the maps.
+rename_value
+let rename_value
+ (#key1: eqtype)
+ (value1: (key1 -> Tot Type))
+ (#key2: eqtype)
+ (ren: (key2 -> Tot key1))
+ (k: key2)
+ : Tot Type = value1 (ren k)
+rename
+val rename
+ (#key1: eqtype)
+ (#value1: (key1 -> Tot Type))
+ (m: t key1 value1)
+ (#key2: eqtype)
+ (ren: (key2 -> Tot key1))
+ : Tot (t key2 (rename_value value1 ren))
+sel_rename
+
+val sel_rename
+ (#key1: eqtype)
+ (#value1: (key1 -> Tot Type))
+ (m: t key1 value1)
+ (#key2: eqtype)
+ (ren: (key2 -> Tot key1))
+ (k2: key2)
+ : Lemma (ensures (sel (rename m ren) k2 == sel m (ren k2)))
+Mapping a function over a dependent map
+
+map
+map f m applies f to each value in m's co-domainval map
+ (#key: eqtype)
+ (#value1 #value2: (key -> Tot Type))
+ (f: (k: key -> value1 k -> Tot (value2 k)))
+ (m: t key value1)
+ : Tot (t key value2)
+sel_map
+
+val sel_map
+ (#key: eqtype)
+ (#value1 #value2: (key -> Tot Type))
+ (f: (k: key -> value1 k -> Tot (value2 k)))
+ (m: t key value1)
+ (k: key)
+ : Lemma (ensures (sel (map f m) k == f k (sel m k)))
+ [SMTPat (sel #key #value2 (map #key #value1 #value2 f m) k)]
+map_upd
+map explained in terms of its action on updval map_upd
+ (#key: eqtype)
+ (#value1 #value2: (key -> Tot Type))
+ (f: (k: key -> value1 k -> Tot (value2 k)))
+ (m: t key value1)
+ (k: key)
+ (v: value1 k)
+ : Lemma (ensures (map f (upd m k v) == upd (map f m) k (f k v)))
+ [
SMTPat (map #key #value1 #value2 f (upd #key #value1 m k v))
+]
+
+
diff --git a/docs/FStar.Dyn.html b/docs/FStar.Dyn.html
index 509a4a9..277c6bd 100644
--- a/docs/FStar.Dyn.html
+++ b/docs/FStar.Dyn.html
@@ -1,16 +1,33 @@
-
-
+
+
-
-
- module FStar.Dyn
-
+FStar.Dyn
+
+
+
+
+ObjFalse CONTEXTS. USE WISELY.assume new
+type dyn
+mkdyn
+'a to dynval mkdyn: 'a -> EXT dyn
+undyn
+dyn to any type 'a,
+but only with False preconditionval undyn (d: dyn{false}) : EXT 'a
module FStar.Endianness
-
+FStar.Endianness
+
+
+
+
+friend'ing
+might be de rigueur.FStar.Kremlin.Endianness.
+
+U8
+U32
+U64
+Math
+S
+[@@ noextract_to "Kremlin"]
+type bytes = S.seq U8.t
+
+
+
+
+Definition of little and big-endianness
+
+
+val le_to_n : b:bytes -> Tot nat
+
+val be_to_n : b:bytes -> Tot nat
+
+val reveal_le_to_n (b:bytes)
+ : Lemma
+ (le_to_n b ==
+ (match Seq.length b with
+ | 0 -> 0
+ | _ -> U8.v (S.head b) + pow2 8 * le_to_n (S.tail b)))
val reveal_be_to_n (b:bytes)
+ : Lemma
+ (be_to_n b ==
+ (match Seq.length b with
+ | 0 -> 0
+ | _ -> U8.v (S.last b) + pow2 8 * be_to_n (S.slice b 0 (S.length b - 1))))
val lemma_le_to_n_is_bounded: b:bytes -> Lemma
+ (requires True)
+ (ensures (le_to_n b < pow2 (8 * Seq.length b)))
+ (decreases (Seq.length b))
val lemma_be_to_n_is_bounded: b:bytes -> Lemma
+ (requires True)
+ (ensures (be_to_n b < pow2 (8 * Seq.length b)))
+ (decreases (Seq.length b))
+
+
+Inverse operations
+
+
+val n_to_le : len:nat -> n:nat{n < pow2 (8 * len)} ->
+ Tot (b:bytes{S.length b == len /\ n == le_to_n b})
+ (decreases len)
+
+val n_to_be:
+ len:nat -> n:nat{n < pow2 (8 * len)} ->
+ Tot (b:bytes{S.length b == len /\ n == be_to_n b})
+ (decreases len)
+
+
+Injectivity
+val n_to_le_inj (len: nat) (n1 n2: (n:nat{n < pow2 (8 * len)})):
+ Lemma (requires (n_to_le len n1 == n_to_le len n2))
+ (ensures (n1 == n2))
val n_to_be_inj (len: nat) (n1 n2: (n:nat{n < pow2 (8 * len)})) :
+ Lemma (requires (n_to_be len n1 == n_to_be len n2))
+ (ensures (n1 == n2))
val be_to_n_inj
+ (b1 b2: Seq.seq U8.t)
+: Lemma
+ (requires (Seq.length b1 == Seq.length b2 /\ be_to_n b1 == be_to_n b2))
+ (ensures (Seq.equal b1 b2))
+ (decreases (Seq.length b1))
val le_to_n_inj
+ (b1 b2: Seq.seq U8.t)
+: Lemma
+ (requires (Seq.length b1 == Seq.length b2 /\ le_to_n b1 == le_to_n b2))
+ (ensures (Seq.equal b1 b2))
+ (decreases (Seq.length b1))
+
+
+Roundtripping
+val n_to_be_be_to_n (len: nat) (s: Seq.seq U8.t) : Lemma
+ (requires (Seq.length s == len))
+ (ensures (
+ be_to_n s < pow2 (8 * len) /\
+ n_to_be len (be_to_n s) == s
+ ))
+ [SMTPat (n_to_be len (be_to_n s))]
val n_to_le_le_to_n (len: nat) (s: Seq.seq U8.t) : Lemma
+ (requires (Seq.length s == len))
+ (ensures (
+ le_to_n s < pow2 (8 * len) /\
+ n_to_le len (le_to_n s) == s
+ ))
+ [SMTPat (n_to_le len (le_to_n s))]
+
+
+Specializations for F* machine integers
+*_is_bounded lemmas.let uint32_of_le (b: bytes { S.length b = 4 }) =
+ let n = le_to_n b in
+ lemma_le_to_n_is_bounded b;
+ UInt32.uint_to_t n
let le_of_uint32 (x: UInt32.t): b:bytes{ S.length b = 4 } =
+ n_to_le 4 (UInt32.v x)
let uint32_of_be (b: bytes { S.length b = 4 }) =
+ let n = be_to_n b in
+ lemma_be_to_n_is_bounded b;
+ UInt32.uint_to_t n
let be_of_uint32 (x: UInt32.t): b:bytes{ S.length b = 4 } =
+ n_to_be 4 (UInt32.v x)
let uint64_of_le (b: bytes { S.length b = 8 }) =
+ let n = le_to_n b in
+ lemma_le_to_n_is_bounded b;
+ UInt64.uint_to_t n
let le_of_uint64 (x: UInt64.t): b:bytes{ S.length b = 8 } =
+ n_to_le 8 (UInt64.v x)
let uint64_of_be (b: bytes { S.length b = 8 }) =
+ let n = be_to_n b in
+ lemma_be_to_n_is_bounded b;
+ UInt64.uint_to_t n
let be_of_uint64 (x: UInt64.t): b:bytes{ S.length b = 8 } =
+ n_to_be 8 (UInt64.v x)
+
+
+Lifting {le,be}to_n / n_to{le,be} to sequences
+val seq_uint32_of_le (l: nat) (b: bytes{ S.length b = 4 * l }):
+ s:S.seq UInt32.t { S.length s = l }
val le_of_seq_uint32 (s: S.seq UInt32.t):
+ Tot (b:bytes { S.length b = 4 * S.length s })
+ (decreases (S.length s))
val seq_uint32_of_be (l: nat) (b: bytes{ S.length b = 4 * l }):
+ s:S.seq UInt32.t { S.length s = l }
val be_of_seq_uint32 (s: S.seq UInt32.t):
+ Tot (b:bytes { S.length b = 4 * S.length s })
+ (decreases (S.length s))
val seq_uint64_of_le (l: nat) (b: bytes{ S.length b = 8 * l }):
+ s:S.seq UInt64.t { S.length s = l }
val le_of_seq_uint64 (s: S.seq UInt64.t):
+ Tot (b:bytes { S.length b = 8 * S.length s })
+ (decreases (S.length s))
val seq_uint64_of_be (l: nat) (b: bytes{ S.length b = 8 * l }):
+ s:S.seq UInt64.t { S.length s = l }
val be_of_seq_uint64 (s: S.seq UInt64.t):
+ Tot (b:bytes { S.length b = 8 * S.length s })
+ (decreases (S.length s))
+
+
+Complete specification of the combinators above, relating them to {le,be}to / n_to_{le,be}
+val offset_uint32_be (b: bytes) (n: nat) (i: nat):
+ Lemma
+ (requires (
+ S.length b = 4 * n /\
+ i < n))
+ (ensures (
+ S.index (seq_uint32_of_be n b) i == uint32_of_be (S.slice b (4 * i) (4 * i + 4))))
+ (decreases (
+ S.length b))
+ [ SMTPat (S.index (seq_uint32_of_be n b) i) ]
val offset_uint32_le (b: bytes) (n: nat) (i: nat):
+ Lemma
+ (requires (
+ S.length b = 4 * n /\
+ i < n))
+ (ensures (
+ S.index (seq_uint32_of_le n b) i == uint32_of_le (S.slice b (4 * i) (4 * i + 4))))
+ (decreases (
+ S.length b))
+ [ SMTPat (S.index (seq_uint32_of_le n b) i) ]
val offset_uint64_be (b: bytes) (n: nat) (i: nat):
+ Lemma
+ (requires (
+ S.length b = 8 * n /\
+ i < n))
+ (ensures (
+ S.index (seq_uint64_of_be n b) i == uint64_of_be (S.slice b (8 * i) (8 * i + 8))))
+ (decreases (
+ S.length b))
+ [ SMTPat (S.index (seq_uint64_of_be n b) i) ]
val offset_uint64_le (b: bytes) (n: nat) (i: nat):
+ Lemma
+ (requires (
+ S.length b = 8 * n /\
+ i < n))
+ (ensures (
+ S.index (seq_uint64_of_le n b) i == uint64_of_le (S.slice b (8 * i) (8 * i + 8))))
+ (decreases (
+ S.length b))
+ [ SMTPat (S.index (seq_uint64_of_le n b) i) ]
+
+
+Reasoning about appending such sequences
+val be_of_seq_uint32_base (s1: S.seq U32.t) (s2: S.seq U8.t): Lemma
+ (requires (
+ S.length s1 = 1 /\
+ S.length s2 = 4 /\
+ be_to_n s2 = U32.v (S.index s1 0)))
+ (ensures (S.equal s2 (be_of_seq_uint32 s1)))
+ [ SMTPat (be_to_n s2); SMTPat (U32.v (S.index s1 0)) ]
val le_of_seq_uint32_base (s1: S.seq U32.t) (s2: S.seq U8.t): Lemma
+ (requires (
+ S.length s1 = 1 /\
+ S.length s2 = 4 /\
+ le_to_n s2 = U32.v (S.index s1 0)))
+ (ensures (S.equal s2 (le_of_seq_uint32 s1)))
+ [ SMTPat (le_to_n s2); SMTPat (U32.v (S.index s1 0)) ]
val be_of_seq_uint64_base (s1: S.seq U64.t) (s2: S.seq U8.t): Lemma
+ (requires (
+ S.length s1 = 1 /\
+ S.length s2 = 8 /\
+ be_to_n s2 = U64.v (S.index s1 0)))
+ (ensures (S.equal s2 (be_of_seq_uint64 s1)))
+ [ SMTPat (be_to_n s2); SMTPat (U64.v (S.index s1 0)) ]
val be_of_seq_uint32_append (s1 s2: S.seq U32.t): Lemma
+ (ensures (
+ S.equal (be_of_seq_uint32 (S.append s1 s2))
+ (S.append (be_of_seq_uint32 s1) (be_of_seq_uint32 s2))))
+ (decreases (
+ S.length s1))
+ [ SMTPat (S.append (be_of_seq_uint32 s1) (be_of_seq_uint32 s2)) ]
val le_of_seq_uint32_append (s1 s2: S.seq U32.t): Lemma
+ (ensures (
+ S.equal (le_of_seq_uint32 (S.append s1 s2))
+ (S.append (le_of_seq_uint32 s1) (le_of_seq_uint32 s2))))
+ (decreases (
+ S.length s1))
+ [ SMTPat (S.append (le_of_seq_uint32 s1) (le_of_seq_uint32 s2)) ]
val be_of_seq_uint64_append (s1 s2: S.seq U64.t): Lemma
+ (ensures (
+ S.equal (be_of_seq_uint64 (S.append s1 s2))
+ (S.append (be_of_seq_uint64 s1) (be_of_seq_uint64 s2))))
+ (decreases (
+ S.length s1))
+ [ SMTPat (S.append (be_of_seq_uint64 s1) (be_of_seq_uint64 s2)) ]
+
+
+Roundtripping
+val seq_uint32_of_be_be_of_seq_uint32 (n: nat) (s: S.seq U32.t) : Lemma
+ (requires (n == S.length s))
+ (ensures (seq_uint32_of_be n (be_of_seq_uint32 s) `S.equal` s))
+ (decreases n)
+ [SMTPat (seq_uint32_of_be n (be_of_seq_uint32 s))]
val be_of_seq_uint32_seq_uint32_of_be (n: nat) (s: S.seq U8.t) : Lemma
+ (requires (4 * n == S.length s))
+ (ensures (be_of_seq_uint32 (seq_uint32_of_be n s) `S.equal` s))
+ (decreases n)
+ [SMTPat (be_of_seq_uint32 (seq_uint32_of_be n s))]
+
+
+Reasoning about slicing such sequences
+val slice_seq_uint32_of_be (n: nat) (s: S.seq U8.t) (lo: nat) (hi: nat) : Lemma
+ (requires (4 * n == S.length s /\ lo <= hi /\ hi <= n))
+ (ensures (S.slice (seq_uint32_of_be n s) lo hi) `S.equal` seq_uint32_of_be (hi - lo) (S.slice s (4 * lo) (4 * hi)))
val be_of_seq_uint32_slice (s: S.seq U32.t) (lo: nat) (hi: nat) : Lemma
+ (requires (lo <= hi /\ hi <= S.length s))
+ (ensures (be_of_seq_uint32 (S.slice s lo hi) `S.equal` S.slice (be_of_seq_uint32 s) (4 * lo) (4 * hi)))
+
+let rec le_to_n_zeros (s:bytes)
+ : Lemma
+ (requires
+ forall (i:nat). i < Seq.length s ==> Seq.index s i == 0uy)
+ (ensures le_to_n s == 0)
+ (decreases (Seq.length s))
+ = reveal_le_to_n s;
+ if Seq.length s = 0 then ()
+ else le_to_n_zeros (Seq.tail s)
let rec be_to_n_zeros (s:bytes)
+ : Lemma
+ (requires
+ forall (i:nat). i < Seq.length s ==> Seq.index s i == 0uy)
+ (ensures be_to_n s == 0)
+ (decreases (Seq.length s))
+ = reveal_be_to_n s;
+ if Seq.length s = 0 then ()
+ else be_to_n_zeros (Seq.slice s 0 (Seq.length s - 1))
module FStar.Exn
-
+FStar.Exn
+
+raise
+raise,
+that is implemented natively in FStar_Exn.ml as primitive raiseassume
+val raise (e: exn) : Exn 'a (requires True) (ensures (fun r -> r == E e))
module FStar.Fin
-
+FStar.Fin
+
+
+
+
+L
+S
+
+fin
+nlet fin (n: nat) = k: int{0 <= k /\ k < n}
+vect
+let vect (n: nat) (a: Type) = l: list a {L.length l = n}
+seqn
+let seqn (n: nat) (a: Type) = s: S.seq a {S.length s = n}
+in_
+in_ s is the type of a valid index into the sequence slet in_ (#a: Type) (s: S.seq a) = n: nat{n < S.length s}
+find
+s startig from i that validates plet rec find (#a: Type) (s: S.seq a) (p: (a -> bool)) (i: in_ s)
+ : Pure (option (in_ s))
+ (requires True)
+ (ensures
+ (function
+ | None -> (forall (k: nat{i <= k /\ k < S.length s}). p (S.index s k) == false)
+ | Some j -> i <= j /\ p (S.index s j)))
+ (decreases (S.length s - i)) =
+ if p (S.index s i) then Some i else if i + 1 < S.length s then find #a s p (i + 1) else None
+pigeonhole
+s all of whose elements are at most n, if the
+length of s is greater than n, then there are two distinct
+indexes in s that contain the same elementlet rec pigeonhole (#n: nat) (s: S.seq (fin n))
+ : Pure (in_ s * in_ s)
+ (requires S.length s = n + 1)
+ (ensures (fun (i1, i2) -> i1 < i2 /\ S.index s i1 = S.index s i2))
+ (decreases n) =
+ if n = 0
+ then (match S.index s 0 with )
+ else
+ if n = 1
+ then
+ (assert (S.index s 0 = S.index s 1);
+ 0, 1)
+ else
+ let k0 = S.index s 0 in
+ match find s (fun k -> k = k0) 1 with
+ | Some i -> 0, i
+ | None ->
+ let reduced_s:S.seq (fin (n - 1)) =
+ S.init #(fin (n - 1))
+ n
+ (fun i ->
+ let k:nat = S.index s (i + 1) in
+ assert (k <> k0);
+ if k < k0 then (k <: fin (n - 1)) else (k - 1 <: fin (n - 1)))
+ in
+ let i1, i2 = pigeonhole reduced_s in
+ i1 + 1, i2 + 1
module FStar.Float
-
+FStar.Float
+
+
+assume new
+type float : Type0
type double = float
module FStar.FunctionalExtensionality
-* MAIN AXIOM ***
- * DUPLICATED FOR GHOST FUNCTIONS
- **val extensionality_g:a:Type -> b:Unidentified product: [a] Type -> f:arrow_g a b -> g:arrow_g a b -> (Lemma ((ensures (<==>(feq_g #a #b f g, ==(on_domain_g a f, on_domain_g a g))))) (Prims.Cons (SMTPat (feq_g #a #b f g)) (Prims.Nil )))
+FStar.FunctionalExtensionality
+
+
+feq #a #b f g asserts that f, g: x:a -> (b x) are
+pointwise equal on the domain a.f and g may also be defined on some
+domain larger than a. We need to be careful to ensure that merely
+proving f and g equal on their sub-domain a does not lead us
+to conclude that they are equal everywhere.
+
+
+arrow
+unfold
+let arrow (a: Type) (b: (a -> Type)) = x: a -> Tot (b x)
+efun
+arrow instead[@@ (deprecated "use arrow instead")]
+let efun (a: Type) (b: (a -> Type)) = arrow a b
+feq
+f and g on domain alet feq (#a: Type) (#b: (a -> Type)) (f g: arrow a b) = forall x. {:pattern (f x)\/(g x)} f x == g x
+on_domain
+on_domain a f:inline_for_extraction
+val on_domain (a: Type) (#b: (a -> Type)) (f: arrow a b) : Tot (arrow a b)
+
+on_domain a f can be seen as a function whose
+maximal domain is a.on_domain a f is proven to be pointwise equal to f,
+crucially it is not provably equal to f, since f may
+actually have a domain larger than a.on_domain is idempotenton_domain a f x has special treatment in F*'s normalizer. It
+reduces to f x, reflecting the pointwise equality of
+on_domain a f and f.on_domain is marked inline_for_extraction, to eliminate the
+overhead of an indirection in extracted code. (This feature
+will be exercised as part of cross-module inlining across
+interface boundaries)
+feq_on_domain
+on_domain a f is pointwise equal to fval feq_on_domain (#a: Type) (#b: (a -> Type)) (f: arrow a b)
+ : Lemma (feq (on_domain a f) f) [SMTPat (on_domain a f)]
+idempotence_on_domain
+val idempotence_on_domain (#a: Type) (#b: (a -> Type)) (f: arrow a b)
+ : Lemma (on_domain a (on_domain a f) == on_domain a f) [SMTPat (on_domain a (on_domain a f))]
+is_restricted
+is_restricted a f:let is_restricted (a: Type) (#b: (a -> Type)) (f: arrow a b) = on_domain a f == f
is_restricted a f is valid when f
+is a function whose maximal domain is equal to a.exists g. f == on_domain a g
+restricted_t
+is_restricted predicate into a refinement typelet restricted_t (a: Type) (b: (a -> Type)) = f: arrow a b {is_restricted a f}
a
+and whose (dependent) co-domain is b.
+op_Hat_Subtraction_Greater
+a ^-> b:unfold
+let op_Hat_Subtraction_Greater (a b: Type) = restricted_t a (fun _ -> b)
a to b.
+The first symbol ^ makes it right associative, as expected for
+arrows.
+on_dom
+on_dom a f:
+A convenience function to introduce a restricted, dependent functionunfold
+let on_dom (a: Type) (#b: (a -> Type)) (f: arrow a b) : restricted_t a b = on_domain a f
+on
+on a f:
+A convenience function to introduce a restricted, non-dependent functionunfold
+let on (a #b: Type) (f: (a -> Tot b)) : (a ^-> b) = on_dom a f
+MAIN AXIOM
+
+extensionality
+
+val extensionality (a: Type) (b: (a -> Type)) (f g: arrow a b)
+ : Lemma (ensures (feq #a #b f g <==> on_domain a f == on_domain a g)) [SMTPat (feq #a #b f g)]
f and g
+that are pointwise equal on domain a are provably equal when
+restricted to a
+DUPLICATED FOR GHOST FUNCTIONS
+
+arrow_g
+unfold
+let arrow_g (a: Type) (b: (a -> Type)) = x: a -> GTot (b x)
+efun_g
+arrow_g instead[@@ (deprecated "use arrow_g instead")]
+let efun_g (a: Type) (b: (a -> Type)) = arrow_g a b
+feq_g
+feq_g #a #b f g: pointwise equality of f and g on domain a *let feq_g (#a: Type) (#b: (a -> Type)) (f g: arrow_g a b) =
+ forall x. {:pattern (f x)\/(g x)} f x == g x
+on_domain_g
+on_domain for ghost functionsval on_domain_g (a: Type) (#b: (a -> Type)) (f: arrow_g a b) : Tot (arrow_g a b)
+feq_on_domain_g
+on_domain_g a f is pointwise equal to fval feq_on_domain_g (#a: Type) (#b: (a -> Type)) (f: arrow_g a b)
+ : Lemma (feq_g (on_domain_g a f) f) [SMTPat (on_domain_g a f)]
+idempotence_on_domain_g
+val idempotence_on_domain_g (#a: Type) (#b: (a -> Type)) (f: arrow_g a b)
+ : Lemma (on_domain_g a (on_domain_g a f) == on_domain_g a f)
+ [SMTPat (on_domain_g a (on_domain_g a f))]
+is_restricted_g
+is_restricted for ghost functionslet is_restricted_g (a: Type) (#b: (a -> Type)) (f: arrow_g a b) = on_domain_g a f == f
+restricted_g_t
+restricted_t for ghost functionslet restricted_g_t (a: Type) (b: (a -> Type)) = f: arrow_g a b {is_restricted_g a f}
+op_Hat_Subtraction_Greater_Greater
+a ^->> b:unfold
+let op_Hat_Subtraction_Greater_Greater (a b: Type) = restricted_g_t a (fun _ -> b)
a
+a to b.
+on_dom_g
+on_dom_g a f:
+A convenience function to introduce a restricted, ghost, dependent functionunfold
+let on_dom_g (a: Type) (#b: (a -> Type)) (f: arrow_g a b) : restricted_g_t a b = on_domain_g a f
+on_g
+on_g a f:
+A convenience function to introduce a restricted, ghost, non-dependent functionunfold
+let on_g (a #b: Type) (f: (a -> GTot b)) : (a ^->> b) = on_dom_g a f
+extensionality_g
val extensionality_g (a: Type) (b: (a -> Type)) (f g: arrow_g a b)
+ : Lemma (ensures (feq_g #a #b f g <==> on_domain_g a f == on_domain_g a g))
+ [SMTPat (feq_g #a #b f g)]
module FStar.GSet
-pragma
+FStar.GSet
#set-options "--initial_fuel 0 --max_fuel 0 --initial_ifuel 0 --max_ifuel 0"
+
+[@@must_erase_for_extraction]
+val set (a: Type u#a) : Type u#a
val equal (#a:Type) (s1:set a) (s2:set a) : Type0
val mem : #a:Type -> a -> set a -> GTot bool
val empty : #a:Type -> Tot (set a)
+val singleton : #a:Type -> a -> Tot (set a)
+val union : #a:Type -> set a -> set a -> Tot (set a)
+val intersect : #a:Type -> set a -> set a -> Tot (set a)
+val complement : #a:Type -> set a -> Tot (set a)
+val comprehend (#a: Type) (f: (a -> GTot bool)) : set a
+val of_set (#a: eqtype) (f: Set.set a) : set a
let disjoint (#a:Type) (s1: set a) (s2: set a) =
+ equal (intersect s1 s2) empty
type subset (#a:Type) (s1:set a) (s2:set a) :Type0 = forall x. mem x s1 ==> mem x s2
val mem_empty: #a:Type -> x:a -> Lemma
+ (requires True)
+ (ensures (not (mem x empty)))
+ [SMTPat (mem x empty)]
val mem_singleton: #a:Type -> x:a -> y:a -> Lemma
+ (requires True)
+ (ensures (mem y (singleton x) <==> (x==y)))
+ [SMTPat (mem y (singleton x))]
val mem_union: #a:Type -> x:a -> s1:set a -> s2:set a -> Lemma
+ (requires True)
+ (ensures (mem x (union s1 s2) = (mem x s1 || mem x s2)))
+ [SMTPat (mem x (union s1 s2))]
val mem_intersect: #a:Type -> x:a -> s1:set a -> s2:set a -> Lemma
+ (requires True)
+ (ensures (mem x (intersect s1 s2) = (mem x s1 && mem x s2)))
+ [SMTPat (mem x (intersect s1 s2))]
val mem_complement: #a:Type -> x:a -> s:set a -> Lemma
+ (requires True)
+ (ensures (mem x (complement s) = not (mem x s)))
+ [SMTPat (mem x (complement s))]
val mem_subset: #a:Type -> s1:set a -> s2:set a -> Lemma
+ (requires (forall x. mem x s1 ==> mem x s2))
+ (ensures (subset s1 s2))
+ [SMTPat (subset s1 s2)]
val subset_mem: #a:Type -> s1:set a -> s2:set a -> Lemma
+ (requires (subset s1 s2))
+ (ensures (forall x. mem x s1 ==> mem x s2))
+ [SMTPat (subset s1 s2)]
val comprehend_mem (#a: Type) (f: (a -> GTot bool)) (x: a)
+ : Lemma (ensures (mem x (comprehend f) == f x))
+ [SMTPat (mem x (comprehend f))]
val mem_of_set (#a: eqtype) (f: Set.set a) (x: a)
+ : Lemma (ensures (mem x (of_set f) <==> Set.mem x f))
+ [SMTPat (mem x (of_set f))]
val lemma_equal_intro: #a:Type -> s1:set a -> s2:set a -> Lemma
+ (requires (forall x. mem x s1 = mem x s2))
+ (ensures (equal s1 s2))
+ [SMTPat (equal s1 s2)]
val lemma_equal_elim: #a:Type -> s1:set a -> s2:set a -> Lemma
+ (requires (equal s1 s2))
+ (ensures (s1 == s2))
+ [SMTPat (equal s1 s2)]
val lemma_equal_refl: #a:Type -> s1:set a -> s2:set a -> Lemma
+ (requires (s1 == s2))
+ (ensures (equal s1 s2))
+ [SMTPat (equal s1 s2)]
let disjoint_not_in_both (a:Type) (s1:set a) (s2:set a) :
+ Lemma
+ (requires (disjoint s1 s2))
+ (ensures (forall (x:a).{:pattern (mem x s1) \/ (mem x s2)} mem x s1 ==> ~(mem x s2)))
+ [SMTPat (disjoint s1 s2)]
+= let f (x:a) : Lemma (~(mem x (intersect s1 s2))) = () in
+ FStar.Classical.forall_intro f
#reset-options //restore fuel usage here
let rec as_set' (#a:Type) (l:list a) : set a =
+ match l with
+ | [] -> empty
+ | hd::tl -> union (singleton hd) (as_set' tl)
let lemma_disjoint_subset (#a:Type) (s1:set a) (s2:set a) (s3:set a)
+ : Lemma (requires (disjoint s1 s2 /\ subset s3 s1))
+ (ensures (disjoint s3 s2))
+ = ()
module FStar.Ghost
-
+FStar.Ghost
+
+
+erased a is decorated with the erasable attribute. As such,
+
+Ghost (erased a) can be subsumed to Pure (erased a)erased a to uniterased a is in a bijection with a, as
+witnessed by the hide and reveal function.reveal
+(it's marked GTot)reveal and hide are coercions: the typechecker will
+automatically insert them when required. That is, if the type of
+an expression is erased X, and the expected type is NOT an
+erased Y, it will insert reveal, and viceversa for hide.
+erased
+erased t is the computationally irrelevant counterpart of t[@@ erasable]
+val erased: Type u#a -> Type u#a
+reveal
+erased t is in a bijection with t, as witnessed by reveal
+and hideval reveal: #a: Type u#a -> erased a -> GTot a
val hide: #a: Type u#a -> a -> Tot (erased a)
val hide_reveal (#a: Type) (x: erased a)
+ : Lemma (ensures (hide (reveal x) == x)) [SMTPat (reveal x)]
val reveal_hide (#a: Type) (x: a) : Lemma (ensures (reveal (hide x) == x)) [SMTPat (hide x)]
+
+
+tot_to_gtot
+Tot is a sub-effect of GTot F* will usually subsume Tot
+computations to GTot computations, though, occasionally, it may
+be useful to apply this coercion explicitly.let tot_to_gtot (f: ('a -> Tot 'b)) (x: 'a) : GTot 'b = f x
+return
+erased: Injecting a value into erased; just an alias of hidelet return (#a: Type) (x: a) : erased a = hide x
+bind
+let bind (#a #b: Type) (x: erased a) (f: (a -> Tot (erased b))) : Tot (erased b) =
+ let y = reveal x in
+ f y
+elift1
+irreducible
+let elift1 (#a #b: Type) (f: (a -> GTot b)) (x: erased a)
+ : Tot (y: erased b {reveal y == f (reveal x)}) = xx <-- x ; return (f xx)
+elift2
+irreducible
+let elift2 (#a #b #c: Type) (f: (a -> b -> GTot c)) (x: erased a) (y: erased b)
+ : Tot (z: erased c {reveal z == f (reveal x) (reveal y)}) =
+ xx <-- x ; yy <-- y ; return (f xx yy)
+elift3
+irreducible
+let elift3
+ (#a #b #c #d: Type)
+ (f: (a -> b -> c -> GTot d))
+ (ga: erased a)
+ (gb: erased b)
+ (gc: erased c)
+ : Tot (gd: erased d {reveal gd == f (reveal ga) (reveal gb) (reveal gc)}) =
+ a <-- ga ; b <-- gb ; c <-- gc ; return (f a b c)
+push_refinement
+erased constructorlet push_refinement #a (#p: (a -> Type0)) (r: erased a {p (reveal r)})
+ : erased (x: a{p x /\ x == reveal r}) =
+ let x:(x: a{p x}) = reveal r in
+ return x
+elift1_p
+irreducible
+let elift1_p
+ (#a #b: Type)
+ (#p: (a -> Type))
+ ($f: (x: a{p x} -> GTot b))
+ (r: erased a {p (reveal r)})
+ : Tot (z: erased b {reveal z == f (reveal r)}) =
+ let x:(x: a{p x}) = reveal r in
+ return (f x)
+elift2_p
+irreducible
+let elift2_p
+ (#a #b #c: Type)
+ (#p: (a -> b -> Type))
+ ($f: (xa: a -> xb: b{p xa xb} -> GTot c))
+ (ra: erased a)
+ (rb: erased b {p (reveal ra) (reveal rb)})
+ : Tot (rc: erased c {reveal rc == f (reveal ra) (reveal rb)}) =
+ let x = reveal ra in
+ let y:(y: b{p x y}) = reveal rb in
+ return (f x y)
+elift1_pq
+irreducible
+let elift1_pq
+ (#a #b: Type)
+ (#p: (a -> Type))
+ (#q: (x: a{p x} -> b -> Type))
+ ($f: (x: a{p x} -> GTot (y: b{q x y})))
+ (r: erased a {p (reveal r)})
+ : Tot (z: erased b {reveal z == f (reveal r)}) =
+ let x:(x: a{p x}) = reveal r in
+ return (f x)
+elift2_pq
+irreducible
+let elift2_pq
+ (#a #b #c: Type)
+ (#p: (a -> b -> Type))
+ (#q: (x: a -> y: b{p x y} -> c -> Type))
+ ($f: (x: a -> y: b{p x y} -> GTot (z: c{q x y z})))
+ (ra: erased a)
+ (rb: erased b {p (reveal ra) (reveal rb)})
+ : Tot (z: erased c {reveal z == f (reveal ra) (reveal rb)}) =
+ let x = reveal ra in
+ let y:(y: b{p x y}) = reveal rb in
+ return (f x y)
module FStar.Heap
-
+FStar.Heap
+
+
+let trivial_rel (a:Type0) :Preorder.relation a = fun x y -> True
let trivial_preorder (a:Type0) :Preorder.preorder a = trivial_rel a
type ref (a:Type0) = mref a (trivial_preorder a)
module FStar.HyperStack.All
-
+FStar.HyperStack.All
+
+
+let all_pre = all_pre_h HyperStack.mem
+let all_post' (a:Type) (pre:Type) = all_post_h' HyperStack.mem a pre
+let all_post (a:Type) = all_post_h HyperStack.mem a
+let all_wp (a:Type) = all_wp_h HyperStack.mem a
+new_effect ALL = ALL_h HyperStack.mem
unfold let lift_state_all (a:Type) (wp:st_wp a) (p:all_post a) = wp (fun a -> p (V a))
+sub_effect STATE ~> ALL = lift_state_all
unfold let lift_exn_all (a:Type) (wp:ex_wp a) (p:all_post a) (h:HyperStack.mem) = wp (fun ra -> p ra h)
+sub_effect EXN ~> ALL = lift_exn_all
effect All (a:Type) (pre:all_pre) (post: (h0:HyperStack.mem -> Tot (all_post' a (pre h0)))) =
+ ALL a
+ (fun (p:all_post a) (h:HyperStack.mem) -> pre h /\ (forall ra h1. post h ra h1 ==> p ra h1)) (* WP *)
+effect ML (a:Type) =
+ ALL a (fun (p:all_post a) (_:HyperStack.mem) -> forall (a:result a) (h:HyperStack.mem). p a h)
assume val pipe_right: 'a -> ('a -> ML 'b) -> ML 'b
+assume val pipe_left: ('a -> ML 'b) -> 'a -> ML 'b
+assume val failwith: string -> All 'a (fun h -> True) (fun h a h' -> Err? a /\ h==h')
+assume val exit: int -> ML 'a
+assume val try_with: (unit -> ML 'a) -> (exn -> ML 'a) -> ML 'a
module FStar.HyperStack.ST
-*** Global ST (GST) effect with put, get, witness, and recall ****typeabbrev
-WARNING: this effect is unsafe, for C/C++ extraction it shall only be used by
-code that would later extract to OCaml or by library functions**** defining predicates for equal refs in some regions *****typeabbrev
+FStar.HyperStack.ST
+
+
+W HS [@@"opaque_to_smt"]
+private unfold let contains_region (m:mem) (r:rid) = get_hmap m `Map.contains` r
val mem_rel :preorder mem
type mem_predicate = mem -> Type0
val region_contains_pred (r:HS.rid) :mem_predicate
val ref_contains_pred (#a:Type) (#rel:preorder a) (r:HS.mreference a rel) :mem_predicate
+Global ST (GST) effect with put, get, witness, and recall ****
+new_effect GST = STATE_h mem
let gst_pre = st_pre_h mem
+let gst_post' (a:Type) (pre:Type) = st_post_h' mem a pre
+let gst_post (a:Type) = st_post_h mem a
+let gst_wp (a:Type) = st_wp_h mem a
unfold let lift_div_gst (a:Type) (wp:pure_wp a) (p:gst_post a) (h:mem) = wp (fun a -> p a h)
+sub_effect DIV ~> GST = lift_div_gst
+
+
+- The interface closely mimics the interface we formalized in our POPL'18 paper
+
+- Specifically, `witnessed` is defined for any mem_predicate (not necessarily stable ones)
+
+- `stable p` is a precondition for `gst_witness`
+
+- `gst_recall` does not have a precondition for `stable p`, since `gst_witness` is the only way
+
+ clients would have obtained `witnessed p`, and so, `p` should already be stable
+
+- `lemma_functoriality` does not require stablility for either `p` or `q`
+
+ Our metatheory ensures that this is sound (without requiring stability of `q`)
+
+ This form is useful in defining the MRRef interface (see mr_witness)
+val stable (p:mem_predicate) :Type0
val witnessed (p:mem_predicate) :Type0
private val gst_get: unit -> GST mem (fun p h0 -> p h0 h0)
+private val gst_put: h1:mem -> GST unit (fun p h0 -> mem_rel h0 h1 /\ p () h1)
private val gst_witness: p:mem_predicate -> GST unit (fun post h0 -> p h0 /\ stable p /\ (witnessed p ==> post () h0))
+private val gst_recall: p:mem_predicate -> GST unit (fun post h0 -> witnessed p /\ (p h0 ==> post () h0))
val lemma_functoriality (p:mem_predicate{witnessed p}) (q:mem_predicate{(forall (h:mem). p h ==> q h)})
+ : Lemma (witnessed q)
let st_pre = gst_pre
+let st_post' = gst_post'
+let st_post = gst_post
+let st_wp = gst_wp
new_effect STATE = GST
unfold let lift_gst_state (a:Type) (wp:gst_wp a) = wp
+sub_effect GST ~> STATE = lift_gst_state
+Unsafe
+effect Unsafe (a:Type) (pre:st_pre) (post: (m0:mem -> Tot (st_post' a (pre m0)))) =
+ STATE a
+ (fun (p:st_post a) (h:mem) -> pre h /\ (forall a h1. pre h /\ post h a h1 ==> p a h1)) (* WP *)
[@@"opaque_to_smt"]
+unfold private let equal_heap_dom (r:rid) (m0 m1:mem) :Type0
+ = Heap.equal_dom (get_hmap m0 `Map.sel` r) (get_hmap m1 `Map.sel` r)
[@@"opaque_to_smt"]
+unfold private let contained_region :mem -> mem -> rid -> Type0
+ = fun m0 m1 r -> m0 `contains_region` r /\ m1 `contains_region` r
[@@"opaque_to_smt"]
+unfold private let contained_stack_region :mem -> mem -> rid -> Type0
+ = fun m0 m1 r -> is_stack_region r /\ contained_region m0 m1 r
[@@"opaque_to_smt"]
+unfold private let contained_non_tip_region :mem -> mem -> rid -> Type0
+ = fun m0 m1 r -> r =!= get_tip m0 /\ r =!= get_tip m1 /\ contained_region m0 m1 r
[@@"opaque_to_smt"]
+unfold private let contained_non_tip_stack_region :mem -> mem -> rid -> Type0
+ = fun m0 m1 r -> is_stack_region r /\ contained_non_tip_region m0 m1 r
[@@"opaque_to_smt"]
+unfold private let same_refs_common (p:mem -> mem -> rid -> Type0) (m0 m1:mem) =
+ forall (r:rid). p m0 m1 r ==> equal_heap_dom r m0 m1
val same_refs_in_all_regions (m0 m1:mem) :Type0
+val same_refs_in_stack_regions (m0 m1:mem) :Type0
+val same_refs_in_non_tip_regions (m0 m1:mem) :Type0
+val same_refs_in_non_tip_stack_regions (m0 m1:mem) :Type0
val lemma_same_refs_in_all_regions_intro (m0 m1:mem)
+ :Lemma (requires (same_refs_common contained_region m0 m1)) (ensures (same_refs_in_all_regions m0 m1))
+ [SMTPat (same_refs_in_all_regions m0 m1)]
+val lemma_same_refs_in_all_regions_elim (m0 m1:mem) (r:rid)
+ :Lemma (requires (same_refs_in_all_regions m0 m1 /\ contained_region m0 m1 r)) (ensures (equal_heap_dom r m0 m1))
+ [SMTPatOr [[SMTPat (same_refs_in_all_regions m0 m1); SMTPat (m0 `contains_region` r)];
+ [SMTPat (same_refs_in_all_regions m0 m1); SMTPat (m1 `contains_region` r)]]]
val lemma_same_refs_in_stack_regions_intro (m0 m1:mem)
+ :Lemma (requires (same_refs_common contained_stack_region m0 m1)) (ensures (same_refs_in_stack_regions m0 m1))
+ [SMTPat (same_refs_in_stack_regions m0 m1)]
+val lemma_same_refs_in_stack_regions_elim (m0 m1:mem) (r:rid)
+ :Lemma (requires (same_refs_in_stack_regions m0 m1 /\ contained_stack_region m0 m1 r)) (ensures (equal_heap_dom r m0 m1))
+ [SMTPatOr [[SMTPat (same_refs_in_stack_regions m0 m1); SMTPat (is_stack_region r); SMTPat (m0 `contains_region` r)];
+ [SMTPat (same_refs_in_stack_regions m0 m1); SMTPat (is_stack_region r); SMTPat (m1 `contains_region` r)]]]
val lemma_same_refs_in_non_tip_regions_intro (m0 m1:mem)
+ :Lemma (requires (same_refs_common contained_non_tip_region m0 m1)) (ensures (same_refs_in_non_tip_regions m0 m1))
+ [SMTPat (same_refs_in_non_tip_regions m0 m1)]
val lemma_same_refs_in_non_tip_regions_elim (m0 m1:mem) (r:rid)
+ :Lemma (requires (same_refs_in_non_tip_regions m0 m1 /\ contained_non_tip_region m0 m1 r)) (ensures (equal_heap_dom r m0 m1))
+ [SMTPatOr [[SMTPat (same_refs_in_non_tip_regions m0 m1); SMTPat (m0 `contains_region` r)];
+ [SMTPat (same_refs_in_non_tip_regions m0 m1); SMTPat (m1 `contains_region` r)]]]
val lemma_same_refs_in_non_tip_stack_regions_intro (m0 m1:mem)
+ :Lemma (requires (same_refs_common contained_non_tip_stack_region m0 m1)) (ensures (same_refs_in_non_tip_stack_regions m0 m1))
+ [SMTPat (same_refs_in_non_tip_stack_regions m0 m1)]
+val lemma_same_refs_in_non_tip_stack_regions_elim (m0 m1:mem) (r:rid)
+ :Lemma (requires (same_refs_in_non_tip_stack_regions m0 m1 /\ contained_non_tip_stack_region m0 m1 r))
+ (ensures (equal_heap_dom r m0 m1))
+ [SMTPatOr [[SMTPat (same_refs_in_non_tip_stack_regions m0 m1); SMTPat (is_stack_region r); SMTPat (m0 `contains_region` r);];
+ [SMTPat (same_refs_in_non_tip_stack_regions m0 m1); SMTPat (is_stack_region r); SMTPat (m1 `contains_region` r)]]]
+let equal_domains (m0 m1:mem) =
+ get_tip m0 == get_tip m1 /\
+ Set.equal (Map.domain (get_hmap m0)) (Map.domain (get_hmap m1)) /\
+ same_refs_in_all_regions m0 m1
val lemma_equal_domains_trans (m0 m1 m2:mem)
+ :Lemma (requires (equal_domains m0 m1 /\ equal_domains m1 m2))
+ (ensures (equal_domains m0 m2))
+ [SMTPat (equal_domains m0 m1); SMTPat (equal_domains m1 m2)]
+Stack
+
+
-
+
+
+
+
typeabbrev effect Stack (a:Type) (pre:st_pre) (post: (m0:mem -> Tot (st_post' a (pre m0)))) =
+ STATE a
+ (fun (p:st_post a) (h:mem) -> pre h /\ (forall a h1. (pre h /\ post h a h1 /\ equal_domains h h1) ==> p a h1)) (* WP *)
+Heap
+
+
-
+
+
+
+
+
+
typeabbrev effect Heap (a:Type) (pre:st_pre) (post: (m0:mem -> Tot (st_post' a (pre m0)))) =
+ STATE a
+ (fun (p:st_post a) (h:mem) -> pre h /\ (forall a h1. (pre h /\ post h a h1 /\ get_tip h = HS.root /\ get_tip h1 = HS.root ) ==> p a h1)) (* WP *)
let equal_stack_domains (m0 m1:mem) =
+ get_tip m0 == get_tip m1 /\
+ same_refs_in_stack_regions m0 m1
+ST
+
+
-
+
+
typeabbrev effect ST (a:Type) (pre:st_pre) (post: (m0:mem -> Tot (st_post' a (pre m0)))) =
+ STATE a
+ (fun (p:st_post a) (h:mem) -> pre h /\ (forall a h1. (pre h /\ post h a h1 /\ equal_stack_domains h h1) ==> p a h1)) (* WP *)
effect St (a:Type) = ST a (fun _ -> True) (fun _ _ _ -> True)
let inline_stack_inv h h' : GTot Type0 =
get_tip h == get_tip h' /\
Map.domain (get_hmap h) == Map.domain (get_hmap h') /\
same_refs_in_non_tip_regions h h'
+StackInline
-typeabbrev effect StackInline (a:Type) (pre:st_pre) (post: (m0:mem -> Tot (st_post' a (pre m0)))) =
+ STATE a
+ (fun (p:st_post a) (h:mem) -> pre h /\ is_stack_region (get_tip h) /\ (forall a h1. (pre h /\ post h a h1 /\ inline_stack_inv h h1) ==> p a h1)) (* WP *)
let inline_inv h h' : GTot Type0 =
get_tip h == get_tip h' /\
same_refs_in_non_tip_stack_regions h h'
+Inline
-module FStar.HyperStack.ST
typeabbrev effect Inline (a:Type) (pre:st_pre) (post: (m0:mem -> Tot (st_post' a (pre m0)))) =
+ STATE a
+ (fun (p:st_post a) (h:mem) -> pre h /\ (forall a h1. (pre h /\ post h a h1 /\ inline_inv h h1) ==> p a h1)) (* WP *)
+STL
-val push_frame:uu___80:unit -> (Unsafe unit ((requires ((fun m -> True)))) ((ensures ((fun (m0:mem) _ (m1:mem) -> fresh_frame m0 m1)))))effect STL (a:Type) (pre:st_pre) (post: (m0:mem -> Tot (st_post' a (pre m0)))) = Stack a pre post
sub_effect
+ DIV ~> STATE = fun (a:Type) (wp:pure_wp a) (p:st_post a) (h:mem) -> wp (fun a -> p a h)
+
+type mreference (a:Type) (rel:preorder a) =
+ r:HS.mreference a rel{witnessed (ref_contains_pred r) /\
+ witnessed (region_contains_pred (HS.frameOf r))}
+type mstackref (a:Type) (rel:preorder a) =
+ r:HS.mstackref a rel{witnessed (ref_contains_pred r) /\
+ witnessed (region_contains_pred (HS.frameOf r))}
+type mref (a:Type) (rel:preorder a) =
+ r:HS.mref a rel{witnessed (ref_contains_pred r) /\
+ witnessed (region_contains_pred (HS.frameOf r))}
+type mmmstackref (a:Type) (rel:preorder a) =
+ r:HS.mmmstackref a rel{witnessed (ref_contains_pred r) /\
+ witnessed (region_contains_pred (HS.frameOf r))}
+type mmmref (a:Type) (rel:preorder a) =
+ r:HS.mmmref a rel{witnessed (ref_contains_pred r) /\
+ witnessed (region_contains_pred (HS.frameOf r))}
+type s_mref (i:rid) (a:Type) (rel:preorder a) =
+ r:HS.s_mref i a rel{witnessed (ref_contains_pred r) /\
+ witnessed (region_contains_pred (HS.frameOf r))}
+type reference (a:Type) = mreference a (Heap.trivial_preorder a)
+type stackref (a:Type) = mstackref a (Heap.trivial_preorder a)
+type ref (a:Type) = mref a (Heap.trivial_preorder a)
+type mmstackref (a:Type) = mmmstackref a (Heap.trivial_preorder a)
+type mmref (a:Type) = mmmref a (Heap.trivial_preorder a)
+type s_ref (i:rid) (a:Type) = s_mref i a (Heap.trivial_preorder a)
let is_eternal_region (r:rid) :Type0
+ = HS.is_eternal_region_hs r /\ (r == HS.root \/ witnessed (region_contains_pred r))
+
+
+since subtyping should take care of most instances in the client usage.
+
+However, one case where it could be an issue is modifies clauses that use
+
+Set.set rid.
+
+push_frame
val pop_frame:uu___81:unit -> (Unsafe unit ((requires ((fun m -> poppable m)))) ((ensures ((fun (m0:mem) _ (m1:mem) -> /\(/\(poppable m0, ==(m1, pop m0)), popped m0 m1))))))val push_frame (_:unit) :Unsafe unit (requires (fun m -> True)) (ensures (fun (m0:mem) _ (m1:mem) -> fresh_frame m0 m1))
+pop_frame
val salloc:#a:Type -> #rel:preorder a -> init:a -> (StackInline (mstackref a rel) ((requires ((fun m -> is_stack_region (get_tip m))))) ((ensures salloc_post init)))val pop_frame (_:unit)
+ :Unsafe unit (requires (fun m -> poppable m))
+ (ensures (fun (m0:mem) _ (m1:mem) -> poppable m0 /\ m1 == pop m0 /\ popped m0 m1))
let salloc_post (#a:Type) (#rel:preorder a) (init:a) (m0:mem)
+ (s:mreference a rel{is_stack_region (frameOf s)}) (m1:mem)
+ = is_stack_region (get_tip m0) /\
+ Map.domain (get_hmap m0) == Map.domain (get_hmap m1) /\
+ get_tip m0 == get_tip m1 /\
+ frameOf s = get_tip m1 /\
+ HS.fresh_ref s m0 m1 /\ //it's a fresh reference in the top frame
+ m1 == HyperStack.upd m0 s init //and it's been initialized
+salloc
-val op_Colon_Equals:#a:Type -> #rel:preorder a -> r:mreference a rel -> v:a -> (STL unit ((requires ((fun m -> /\(is_live_for_rw_in r m, rel (HS.sel m r) v))))) ((ensures (assign_post r v))))val salloc (#a:Type) (#rel:preorder a) (init:a)
+ :StackInline (mstackref a rel) (requires (fun m -> is_stack_region (get_tip m)))
+ (ensures salloc_post init)
salloc already benefits from
+automatic memory management.[@@ (deprecated "use salloc instead") ]
+val salloc_mm (#a:Type) (#rel:preorder a) (init:a)
+ :StackInline (mmmstackref a rel) (requires (fun m -> is_stack_region (get_tip m)))
+ (ensures salloc_post init)
[@@ (deprecated "use salloc instead") ]
+val sfree (#a:Type) (#rel:preorder a) (r:mmmstackref a rel)
+ :StackInline unit (requires (fun m0 -> frameOf r = get_tip m0 /\ m0 `contains` r))
+ (ensures (fun m0 _ m1 -> m0 `contains` r /\ m1 == HS.free r m0))
unfold
+let new_region_post_common (r0 r1:rid) (m0 m1:mem) =
+ r1 `HS.extends` r0 /\
+ HS.fresh_region r1 m0 m1 /\
+ get_hmap m1 == Map.upd (get_hmap m0) r1 Heap.emp /\
+ get_tip m1 == get_tip m0 /\
+ HS.live_region m0 r0
val new_region (r0:rid)
+ :ST rid
+ (requires (fun m -> is_eternal_region r0))
+ (ensures (fun m0 r1 m1 ->
+ new_region_post_common r0 r1 m0 m1 /\
+ HS.color r1 = HS.color r0 /\
+ is_eternal_region r1 /\
+ (r1, m1) == HS.new_eternal_region m0 r0 None))
val new_colored_region (r0:rid) (c:int)
+ :ST rid
+ (requires (fun m -> HS.is_heap_color c /\ is_eternal_region r0))
+ (ensures (fun m0 r1 m1 ->
+ new_region_post_common r0 r1 m0 m1 /\
+ HS.color r1 = c /\
+ is_eternal_region r1 /\
+ (r1, m1) == HS.new_eternal_region m0 r0 (Some c)))
let ralloc_post (#a:Type) (#rel:preorder a) (i:rid) (init:a) (m0:mem)
+ (x:mreference a rel) (m1:mem) =
+ let region_i = get_hmap m0 `Map.sel` i in
+ as_ref x `Heap.unused_in` region_i /\
+ i `is_in` get_hmap m0 /\
+ i = frameOf x /\
+ m1 == upd m0 x init
val ralloc (#a:Type) (#rel:preorder a) (i:rid) (init:a)
+ :ST (mref a rel) (requires (fun m -> is_eternal_region i))
+ (ensures (ralloc_post i init))
val ralloc_mm (#a:Type) (#rel:preorder a) (i:rid) (init:a)
+ :ST (mmmref a rel) (requires (fun m -> is_eternal_region i))
+ (ensures (ralloc_post i init))
+
+
+ the client can either prove contains
+
+ or give us enough so that we can use monotonicity to derive contains
+let is_live_for_rw_in (#a:Type) (#rel:preorder a) (r:mreference a rel) (m:mem) :Type0 =
+ (m `contains` r) \/
+ (let i = HS.frameOf r in
+ (is_eternal_region i \/ i `HS.is_above` get_tip m) /\
+ (not (is_mm r) \/ m `HS.contains_ref_in_its_region` r))
val rfree (#a:Type) (#rel:preorder a) (r:mreference a rel{HS.is_mm r /\ HS.is_heap_color (HS.color (HS.frameOf r))})
+ :ST unit (requires (fun m0 -> r `is_live_for_rw_in` m0))
+ (ensures (fun m0 _ m1 -> m0 `contains` r /\ m1 == HS.free r m0))
unfold let assign_post (#a:Type) (#rel:preorder a) (r:mreference a rel) (v:a) (m0:mem) (_:unit) (m1:mem) =
+ m0 `contains` r /\ m1 == HyperStack.upd m0 r v
+op_Colon_Equals
-val op_Bang:#a:Type -> #rel:preorder a -> r:mreference a rel -> (Stack a ((requires ((fun m -> is_live_for_rw_in r m)))) ((ensures (deref_post r))))val op_Colon_Equals (#a:Type) (#rel:preorder a) (r:mreference a rel) (v:a)
+ :STL unit (requires (fun m -> r `is_live_for_rw_in` m /\ rel (HS.sel m r) v))
+ (ensures (assign_post r v))
unfold let deref_post (#a:Type) (#rel:preorder a) (r:mreference a rel) (m0:mem) (x:a) (m1:mem) =
+ m1 == m0 /\ m0 `contains` r /\ x == HyperStack.sel m0 r
+op_Bang
-val get:uu___82:unit -> (Stack mem ((requires ((fun m -> True)))) ((ensures ((fun m0 x m1 -> /\(==(m0, x), ==(m1, m0)))))))val op_Bang (#a:Type) (#rel:preorder a) (r:mreference a rel)
+ :Stack a (requires (fun m -> r `is_live_for_rw_in` m))
+ (ensures (deref_post r))
let modifies_none (h0:mem) (h1:mem) = modifies Set.empty h0 h1
+get
-val recall:#a:Type -> #rel:preorder a -> r:r:mreference a rel:{not (HS.is_mm r)} -> (Stack unit ((requires ((fun m -> \/(is_eternal_region (HS.frameOf r), contains_region m (HS.frameOf r)))))) ((ensures ((fun m0 _ m1 -> /\(==(m0, m1), contains m1 r))))))val get (_:unit)
+ :Stack mem (requires (fun m -> True))
+ (ensures (fun m0 x m1 -> m0 == x /\ m1 == m0))
+recall
-val recall_region:i:i:rid:{is_eternal_region i} -> (Stack unit ((requires ((fun m -> True)))) ((ensures ((fun m0 _ m1 -> /\(==(m0, m1), is_in i get_hmap m1))))))val recall (#a:Type) (#rel:preorder a) (r:mreference a rel{not (HS.is_mm r)})
+ :Stack unit (requires (fun m -> is_eternal_region (HS.frameOf r) \/ m `contains_region` (HS.frameOf r)))
+ (ensures (fun m0 _ m1 -> m0 == m1 /\ m1 `contains` r))
+recall_region
- MR witness etc. ***** Begin: preferred API for witnessing and recalling predicates ********* End: preferred API for witnessing and recalling predicates ********* logical properties of witnessed ****** Support for dynamic regions **val recall_region (i:rid{is_eternal_region i})
+ :Stack unit (requires (fun m -> True))
+ (ensures (fun m0 _ m1 -> m0 == m1 /\ i `is_in` get_hmap m1))
val witness_region (i:rid)
+ :Stack unit (requires (fun m0 -> HS.is_eternal_region_hs i ==> i `is_in` get_hmap m0))
+ (ensures (fun m0 _ m1 -> m0 == m1 /\ witnessed (region_contains_pred i)))
val witness_hsref (#a:Type) (#rel:preorder a) (r:HS.mreference a rel)
+ :ST unit (fun h0 -> h0 `HS.contains` r)
+ (fun h0 _ h1 -> h0 == h1 /\ witnessed (ref_contains_pred r))
type erid = r:rid{is_eternal_region r}
type m_rref (r:erid) (a:Type) (b:preorder a) = x:mref a b{HS.frameOf x = r}
unfold type stable_on (#a:Type0) (#rel:preorder a) (p:mem_predicate) (r:mreference a rel)
+ = forall (h0 h1:mem).{:pattern (p h0); rel (HS.sel h0 r) (HS.sel h1 r)}
+ (p h0 /\ rel (HS.sel h0 r) (HS.sel h1 r)) ==> p h1
+
+unfold type stable_on_t (#i:erid) (#a:Type) (#b:preorder a)
+ (r:m_rref i a b) (p:mem_predicate)
+ = stable_on p r
val mr_witness (#r:erid) (#a:Type) (#b:preorder a)
+ (m:m_rref r a b) (p:mem_predicate)
+ :ST unit (requires (fun h0 -> p h0 /\ stable_on_t m p))
+ (ensures (fun h0 _ h1 -> h0==h1 /\ witnessed p))
val weaken_witness (p q:mem_predicate)
+ :Lemma ((forall h. p h ==> q h) /\ witnessed p ==> witnessed q)
val testify (p:mem_predicate)
+ :ST unit (requires (fun _ -> witnessed p))
+ (ensures (fun h0 _ h1 -> h0==h1 /\ p h1))
val testify_forall (#c:Type) (#p:(c -> mem -> Type0))
+ ($s:squash (forall (x:c). witnessed (p x)))
+ :ST unit (requires (fun h -> True))
+ (ensures (fun h0 _ h1 -> h0==h1 /\ (forall (x:c). p x h1)))
val testify_forall_region_contains_pred (#c:Type) (#p:(c -> GTot rid))
+ ($s:squash (forall (x:c). witnessed (region_contains_pred (p x))))
+ :ST unit (requires (fun _ -> True))
+ (ensures (fun h0 _ h1 -> h0 == h1 /\
+ (forall (x:c). HS.is_eternal_region_hs (p x) ==> h1 `contains_region` (p x))))
val token_p (#a:Type0) (#rel:preorder a) (r:mreference a rel) (p:mem_predicate) :Type0
val witness_p (#a:Type0) (#rel:preorder a) (r:mreference a rel) (p:mem_predicate)
+ :ST unit (fun h0 -> p h0 /\ p `stable_on` r)
+ (fun h0 _ h1 -> h0 == h1 /\ token_p r p)
val recall_p (#a:Type0) (#rel:preorder a) (r:mreference a rel) (p:mem_predicate)
+ :ST unit (fun h0 -> ((is_eternal_region (HS.frameOf r) /\ not (HS.is_mm r)) \/ h0 `HS.contains` r) /\ token_p r p)
+ (fun h0 _ h1 -> h0 == h1 /\ h0 `HS.contains` r /\ p h0)
val token_functoriality
+ (#a:Type0) (#rel:preorder a) (r:mreference a rel)
+ (p:mem_predicate{token_p r p}) (q:mem_predicate{forall (h:mem). p h ==> q h})
+ : Lemma (token_p r q)
type ex_rid = erid
val lemma_witnessed_constant (p:Type0)
+ :Lemma (witnessed (fun (m:mem) -> p) <==> p)
val lemma_witnessed_nested (p:mem_predicate)
+ : Lemma (witnessed (fun (m:mem) -> witnessed p) <==> witnessed p)
val lemma_witnessed_and (p q:mem_predicate)
+ :Lemma (witnessed (fun s -> p s /\ q s) <==> (witnessed p /\ witnessed q))
val lemma_witnessed_or (p q:mem_predicate)
+ :Lemma ((witnessed p \/ witnessed q) ==> witnessed (fun s -> p s \/ q s))
val lemma_witnessed_impl (p q:mem_predicate)
+ :Lemma ((witnessed (fun s -> p s ==> q s) /\ witnessed p) ==> witnessed q)
val lemma_witnessed_forall (#t:Type) (p:(t -> mem_predicate))
+ :Lemma ((witnessed (fun s -> forall x. p x s)) <==> (forall x. witnessed (p x)))
val lemma_witnessed_exists (#t:Type) (p:(t -> mem_predicate))
+ :Lemma ((exists x. witnessed (p x)) ==> witnessed (fun s -> exists x. p x s))
+Support for dynamic regions **
+let is_freeable_heap_region (r:rid) : Type0 =
+ HS.is_heap_color (color r) /\ HS.rid_freeable r /\ witnessed (region_contains_pred r)
type d_hrid = r:rid{is_freeable_heap_region r}
val drgn : Type0
val rid_of_drgn (d:drgn) : d_hrid
val new_drgn (r0:rid)
+: ST drgn
+ (requires fun m -> is_eternal_region r0)
+ (ensures fun m0 d m1 ->
+ let r1 = rid_of_drgn d in
+ new_region_post_common r0 r1 m0 m1 /\
+ HS.color r1 == HS.color r0 /\
+ (r1, m1) == HS.new_freeable_heap_region m0 r0)
val free_drgn (d:drgn)
+: ST unit
+ (requires fun m -> contains_region m (rid_of_drgn d))
+ (ensures fun m0 _ m1 -> m1 == HS.free_heap_region m0 (rid_of_drgn d))
val ralloc_drgn (#a:Type) (#rel:preorder a) (d:drgn) (init:a)
+: ST (mreference a rel)
+ (requires fun m -> m `contains_region` (rid_of_drgn d))
+ (ensures fun m0 r m1 ->
+ not (HS.is_mm r) /\
+ ralloc_post (rid_of_drgn d) init m0 r m1)
val ralloc_drgn_mm (#a:Type) (#rel:preorder a) (d:drgn) (init:a)
+: ST (mreference a rel)
+ (requires fun m -> m `contains_region` (rid_of_drgn d))
+ (ensures fun m0 r m1 ->
+ HS.is_mm r /\
+ ralloc_post (rid_of_drgn d) init m0 r m1)
module FStar.HyperStack
-
+FStar.HyperStack
+
+
+type reference (a:Type) = mreference a (Heap.trivial_preorder a)
let stackref (a:Type) = mstackref a (Heap.trivial_preorder a)
+let ref (a:Type) = mref a (Heap.trivial_preorder a)
let mmstackref (a:Type) = mmmstackref a (Heap.trivial_preorder a)
+let mmref (a:Type) = mmmref a (Heap.trivial_preorder a)
+type s_ref (i:rid) (a:Type) = s_mref i a (Heap.trivial_preorder a)
let reference_distinct_sel_disjoint
+ (#a:Type0) (h: mem) (r1: reference a) (r2: reference a)
+: Lemma
+ (requires (
+ h `contains` r1 /\
+ h `contains` r2 /\
+ frameOf r1 == frameOf r2 /\
+ as_addr r1 == as_addr r2
+ ))
+ (ensures (
+ sel h r1 == sel h r2
+ ))
+= mreference_distinct_sel_disjoint h r1 r2
module FStar.IFC
-
- * FStar.IFC provides a simple, generic abstraction
- * for monadic information-flow control
- * based on a user-defined (semi-)lattice of information flow labels
- *
+FStar.IFC
+
+
+protected a l,
+encapsulating values of type a carrying information at
+confidentiality level l. Operations that compute on the
+underlying a are instrumented to reflect the sensitivity of
+their arguments on their results.
+Basic definitions for a join semilattice
+
+associative
+lub is associativelet associative #a (f: (a -> a -> a)) = forall x y z. f (f x y) z == f x (f y z)
+commutative
+lub is commutativelet commutative #a (f: (a -> a -> a)) = forall x y. f x y == f y x
+idempotent
+lub is idempotentlet idempotent #a (f: (a -> a -> a)) = forall x. f x x == x
+semilattice
+noeq
+type semilattice : Type u#(c + 1) =
+ | SemiLattice :
+ #carrier: Type u#c ->
+ top: carrier ->
+ lub: (f: (carrier -> carrier -> carrier){associative f /\ commutative f /\ idempotent f})
+ -> semilattice
+sl:Type
+let sl:Type u#(c + 1) = FStar.Ghost.erased semilattice
+lattice_element
+let lattice_element (sl: sl) = Ghost.erased (SemiLattice?.carrier (Ghost.reveal sl))
+lub
+unfold
+let lub #sl (x: lattice_element sl) (y: lattice_element sl) : Tot (lattice_element sl) =
+ Ghost.hide (SemiLattice?.lub (Ghost.reveal sl) (Ghost.reveal x) (Ghost.reveal y))
+protected
+protected l b i.e,, a
+b-typed value protected at IFC level l.val protected (#sl: sl u#c) (l: lattice_element sl) (b: Type u#b) : Type u#b
protected b l is in a bijection with b, as shown by reveal
+and hide below
+reveal
+reveal projects a b from a protected b l, but incurs a ghost effectval reveal (#sl: _) (#l: lattice_element sl) (#b: _) (x: protected l b) : GTot b
+hide
+hide injects a b into a protected b l.val hide (#sl: _) (#l: lattice_element sl) (#b: _) (x: b) : Tot (protected l b)
b can be promoted to a protected l b i.e.,
+protected l b is only meant to enforce confidentiality
+reveal_hide
+val reveal_hide (#l #t #b: _) (x: b) : Lemma (reveal (hide #l #t x) == x) [SMTPat (hide #l #t x)]
val hide_reveal (#sl: _) (#l: lattice_element sl) (#b: _) (x: protected l b)
+ : Lemma (hide (reveal x) == x) [SMTPat (reveal x)]
+
+protected l b is a form of parameterized monad
+It provides:
+-- return (via hide)
+-- map (i.e., it's a functor)
+-- join (so it's also a monad)
+Which we package up as a bindunfold
+let return #sl #a (l: lattice_element sl) (x: a) : protected l a = hide x
+map
+f over x But, notice the order of
+arguments is flipped We write map x f instead of map f x so
+that f's type can depend on xval map (#a #b #sl: _) (#l: lattice_element sl) (x: protected l a) (f: (y: a{y == reveal x} -> b))
+ : Tot (y: protected l b {reveal y == f (reveal x)})
+join
+join
+Except notice that the label of the result is the lub
+of the both the labels in the argumentval join (#sl: _) (#l1 #l2: lattice_element sl) (#a: _) (x: protected l1 (protected l2 a))
+ : Tot (y: protected (l1 `lub` l2) a {reveal y == reveal (reveal x)})
+bind
+map the type of
+the continuation's argument depends on the argument x; and, like
+join, the indexes on the result are at least as high as the
+indexes of the argumentunfold
+let bind
+ #sl
+ (#l1: lattice_element sl)
+ #a
+ (x: protected l1 a)
+ (#l2: lattice_element sl)
+ #b
+ (f: (y: a{y == reveal x} -> protected l2 b))
+ : Tot (protected (l1 `lub` l2) b) = join (map x f)
x has a secrecy level at least as secret as x itselfmodule FStar.IO
-
+FStar.IO
+
+
+exception EOF
+assume new type fd_read : Type0
+assume new type fd_write : Type0
assume val stdin : fd_read
+assume val stdout : fd_write
+assume val stderr : fd_write
assume val print_newline : unit -> ML unit
+assume val print_string : string -> ML unit
assume val print_uint8 : FStar.UInt8.t -> ML unit
+assume val print_uint16 : FStar.UInt16.t -> ML unit
+assume val print_uint32 : FStar.UInt32.t -> ML unit
+assume val print_uint64 : FStar.UInt64.t -> ML unit
assume val print_uint8_dec : FStar.UInt8.t -> ML unit
+assume val print_uint16_dec : FStar.UInt16.t -> ML unit
+assume val print_uint32_dec : FStar.UInt32.t -> ML unit
+assume val print_uint64_dec : FStar.UInt64.t -> ML unit
assume val print_uint8_hex_pad : FStar.UInt8.t -> ML unit
+assume val print_uint16_hex_pad : FStar.UInt16.t -> ML unit
+assume val print_uint32_hex_pad : FStar.UInt32.t -> ML unit
+assume val print_uint64_hex_pad : FStar.UInt64.t -> ML unit
assume val print_uint8_dec_pad : FStar.UInt8.t -> ML unit
+assume val print_uint16_dec_pad : FStar.UInt16.t -> ML unit
+assume val print_uint32_dec_pad : FStar.UInt32.t -> ML unit
+assume val print_uint64_dec_pad : FStar.UInt64.t -> ML unit
assume val print_any : 'a -> ML unit
+assume val input_line : unit -> ML string
+assume val input_int : unit -> ML int
+assume val input_float : unit -> ML FStar.Float.float
+assume val open_read_file : string -> ML fd_read
+assume val open_write_file : string -> ML fd_write
+assume val close_read_file : fd_read -> ML unit
+assume val close_write_file : fd_write -> ML unit
+assume val read_line : fd_read -> ML string
+assume val write_string : fd_write -> string -> ML unit
assume val debug_print_string : string -> Tot bool
module FStar.IndefiniteDescription
-
+FStar.IndefiniteDescription
+
+
+
+indefinite_description_tot
+assume
+val indefinite_description_tot (a:Type) (p:(a -> prop) { exists x. p x })
+ : Tot (w:Ghost.erased a{ p w })
exists x. p x, we can exhibit an erased
+(computationally irrelevant) a witness x:erased a validating
+p x.
+indefinite_description_ghost
+let indefinite_description_ghost (a: Type) (p: (a -> prop) { exists x. p x })
+ : GTot (x: a { p x })
+ = let w = indefinite_description_tot a p in
+ let x = Ghost.reveal w in
+ x
+indefinite_description
+[@@deprecated "Consider using indefinite_description_ghost instead"]
+assume
+val indefinite_description (a: Type) (p: (a -> GTot Type0))
+ : Ghost (x: a & p x) (requires (exists x. p x)) (ensures (fun _ -> True))
exists x. p x, we can exhibit (ghostly) a
+witness x:a validating p x.p to be a -> prop. However, see
+Prims.prop for a description of the ongoing work on more
+systematically using prop in the libraries
+
+
+strong_excluded_middle
+Ghost)let strong_excluded_middle (p: Type0) : GTot (b: bool{b = true <==> p}) =
+ let aux (p: Type0) : Lemma (exists b. b = true <==> p) =
+ give_proof (bind_squash (get_proof (l_or p (~p)))
+ (fun (b: l_or p (~p)) ->
+ bind_squash b
+ (fun (b': c_or p (~p)) ->
+ match b' with
+ | Left hp ->
+ give_witness hp;
+ exists_intro (fun b -> b = true <==> p) true;
+ get_proof (exists b. b = true <==> p)
+ | Right hnp ->
+ give_witness hnp;
+ exists_intro (fun b -> b = true <==> p) false;
+ get_proof (exists b. b = true <==> p))))
+ in
+ aux p;
+ indefinite_description_ghost bool (fun b -> b = true <==> p)
+stronger_markovs_principle
+forall and an exists to extract a witness of validity of p from
+a classical proof that p is not universally invalid.let stronger_markovs_principle (p: (nat -> GTot bool))
+ : Ghost nat (requires (~(forall (n: nat). ~(p n)))) (ensures (fun n -> p n)) =
+ indefinite_description_ghost _ (fun n -> p n==true)
(~(forall n. ~(p n))) ==> (exists n. p n)
+stronger_markovs_principle_prop
+prop rather than a
+boolean predicatelet stronger_markovs_principle_prop (p: (nat -> GTot prop))
+ : Ghost nat (requires (~(forall (n: nat). ~(p n)))) (ensures (fun n -> p n)) =
+ indefinite_description_ghost _ p
let elim_squash (#p:Type u#a) (s:squash p) : GTot p =
+ let uu : squash (x:p & squash c_True) =
+ bind_squash s (fun x -> return_squash (| x, return_squash T |)) in
+ give_proof (return_squash uu);
+ indefinite_description_ghost p (fun _ -> squash c_True)
module FStar.Int.Cast.Full
-
+FStar.Int.Cast.Full
+
+
+U64 U128 inline_for_extraction noextract
+val uint64_to_uint128: a:U64.t -> b:U128.t{U128.v b == U64.v a}
+inline_for_extraction noextract
+let uint64_to_uint128 a = U128.uint64_to_uint128 a
inline_for_extraction noextract
+val uint128_to_uint64: a:U128.t -> b:U64.t{U64.v b == U128.v a % pow2 64}
+inline_for_extraction noextract
+let uint128_to_uint64 a = U128.uint128_to_uint64 a
module FStar.Int.Cast
-
+FStar.Int.Cast
+
+
+U8
+U16
+U32
+U64
+I8
+I16
+I32
+I64
+let op_At_Percent = FStar.Int.op_At_Percent
+
+val uint8_to_uint64: a:U8.t -> Tot (b:U64.t{U64.v b = U8.v a})
+let uint8_to_uint64 a = U64.uint_to_t (U8.v a)
val uint8_to_uint32: a:U8.t -> Tot (b:U32.t{U32.v b = U8.v a})
+let uint8_to_uint32 x = U32.uint_to_t (U8.v x)
val uint8_to_uint16: a:U8.t -> Tot (b:U16.t{U16.v b = U8.v a})
+let uint8_to_uint16 x = U16.uint_to_t (U8.v x)
val uint16_to_uint64: a:U16.t -> Tot (b:U64.t{U64.v b = U16.v a})
+let uint16_to_uint64 x = U64.uint_to_t (U16.v x)
val uint16_to_uint32: a:U16.t -> Tot (b:U32.t{U32.v b = U16.v a})
+let uint16_to_uint32 x = U32.uint_to_t (U16.v x)
val uint16_to_uint8 : a:U16.t -> Tot (b:U8.t{U8.v b = U16.v a % pow2 8})
+let uint16_to_uint8 x = U8.uint_to_t (U16.v x % pow2 8)
val uint32_to_uint64: a:U32.t -> Tot (b:U64.t{U64.v b = U32.v a})
+let uint32_to_uint64 x = U64.uint_to_t (U32.v x)
val uint32_to_uint16: a:U32.t -> Tot (b:U16.t{U16.v b = U32.v a % pow2 16})
+let uint32_to_uint16 x = U16.uint_to_t (U32.v x % pow2 16)
val uint32_to_uint8 : a:U32.t -> Tot (b:U8.t{U8.v b = U32.v a % pow2 8})
+let uint32_to_uint8 x = U8.uint_to_t (U32.v x % pow2 8)
val uint64_to_uint32: a:U64.t -> Tot (b:U32.t{U32.v b = U64.v a % pow2 32})
+let uint64_to_uint32 x = U32.uint_to_t (U64.v x % pow2 32)
val uint64_to_uint16: a:U64.t -> Tot (b:U16.t{U16.v b = U64.v a % pow2 16})
+let uint64_to_uint16 x = U16.uint_to_t (U64.v x % pow2 16)
val uint64_to_uint8 : a:U64.t -> Tot (b:U8.t{U8.v b = U64.v a % pow2 8})
+let uint64_to_uint8 x = U8.uint_to_t (U64.v x % pow2 8)
+
+val int8_to_int64: a:I8.t -> Tot (b:I64.t{I64.v b = I8.v a})
+let int8_to_int64 x = I64.int_to_t (I8.v x)
val int8_to_int32: a:I8.t -> Tot (b:I32.t{I32.v b = I8.v a})
+let int8_to_int32 x = I32.int_to_t (I8.v x)
val int8_to_int16: a:I8.t -> Tot (b:I16.t{I16.v b = I8.v a})
+let int8_to_int16 x = I16.int_to_t (I8.v x)
val int16_to_int64: a:I16.t -> Tot (b:I64.t{I64.v b = I16.v a})
+let int16_to_int64 x = I64.int_to_t (I16.v x @% pow2 64)
val int16_to_int32: a:I16.t -> Tot (b:I32.t{I32.v b = I16.v a})
+let int16_to_int32 x = I32.int_to_t (I16.v x @% pow2 32)
[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+val int16_to_int8 : a:I16.t -> Tot (b:I8.t {I8.v b = (I16.v a @% pow2 8)})
+[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+let int16_to_int8 x = I8.int_to_t (I16.v x @% pow2 8)
val int32_to_int64: a:I32.t -> Tot (b:I64.t{I64.v b = I32.v a})
+let int32_to_int64 x = I64.int_to_t (I32.v x @% pow2 64)
[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+val int32_to_int16: a:I32.t -> Tot (b:I16.t{I16.v b = (I32.v a @% pow2 16)})
+[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+let int32_to_int16 x = I16.int_to_t (I32.v x @% pow2 16)
[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+val int32_to_int8 : a:I32.t -> Tot (b:I8.t {I8.v b = (I32.v a @% pow2 8)})
+[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+let int32_to_int8 x = I8.int_to_t (I32.v x @% pow2 8)
[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+val int64_to_int32: a:I64.t -> Tot (b:I32.t{I32.v b = (I64.v a @% pow2 32)})
+[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+let int64_to_int32 x = I32.int_to_t (I64.v x @% pow2 32)
[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+val int64_to_int16: a:I64.t -> Tot (b:I16.t{I16.v b = (I64.v a @% pow2 16)})
+[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+let int64_to_int16 x = I16.int_to_t (I64.v x @% pow2 16)
[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+val int64_to_int8 : a:I64.t -> Tot (b:I8.t {I8.v b = (I64.v a @% pow2 8)})
+[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+let int64_to_int8 x = I8.int_to_t (I64.v x @% pow2 8)
+
+val uint8_to_int64: a:U8.t -> Tot (b:I64.t{I64.v b = U8.v a})
+let uint8_to_int64 x = I64.int_to_t (U8.v x)
val uint8_to_int32: a:U8.t -> Tot (b:I32.t{I32.v b = U8.v a})
+let uint8_to_int32 x = I32.int_to_t (U8.v x)
val uint8_to_int16: a:U8.t -> Tot (b:I16.t{I16.v b = U8.v a})
+let uint8_to_int16 x = I16.int_to_t (U8.v x)
[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+val uint8_to_int8 : a:U8.t -> Tot (b:I8.t {I8.v b = (U8.v a @% pow2 8)})
+[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+let uint8_to_int8 x = I8.int_to_t (U8.v x @% pow2 8)
val uint16_to_int64: a:U16.t -> Tot (b:I64.t{I64.v b = U16.v a})
+let uint16_to_int64 x = I64.int_to_t (U16.v x)
val uint16_to_int32: a:U16.t -> Tot (b:I32.t{I32.v b = U16.v a})
+let uint16_to_int32 x = I32.int_to_t (U16.v x)
[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+val uint16_to_int16: a:U16.t -> Tot (b:I16.t{I16.v b = (U16.v a @% pow2 16)})
+[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+let uint16_to_int16 x = I16.int_to_t (U16.v x @% pow2 16)
[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+val uint16_to_int8 : a:U16.t -> Tot (b:I8.t {I8.v b = (U16.v a @% pow2 8)})
+[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+let uint16_to_int8 x = I8.int_to_t (U16.v x @% pow2 8)
val uint32_to_int64: a:U32.t -> Tot (b:I64.t{I64.v b = U32.v a})
+let uint32_to_int64 x = I64.int_to_t (U32.v x)
[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+val uint32_to_int32: a:U32.t -> Tot (b:I32.t{I32.v b = (U32.v a @% pow2 32)})
+[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+let uint32_to_int32 x = I32.int_to_t (U32.v x @% pow2 32)
[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+val uint32_to_int16: a:U32.t -> Tot (b:I16.t{I16.v b = (U32.v a @% pow2 16)})
+[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+let uint32_to_int16 x = I16.int_to_t (U32.v x @% pow2 16)
[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+val uint32_to_int8 : a:U32.t -> Tot (b:I8.t {I8.v b = (U32.v a @% pow2 8)})
+[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+let uint32_to_int8 x = I8.int_to_t (U32.v x @% pow2 8)
[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+val uint64_to_int64: a:U64.t -> Tot (b:I64.t{I64.v b = (U64.v a @% pow2 64)})
+[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+let uint64_to_int64 x = I64.int_to_t (U64.v x @% pow2 64)
[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+val uint64_to_int32: a:U64.t -> Tot (b:I32.t{I32.v b = (U64.v a @% pow2 32)})
+[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+let uint64_to_int32 x = I32.int_to_t (U64.v x @% pow2 32)
[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+val uint64_to_int16: a:U64.t -> Tot (b:I16.t{I16.v b = (U64.v a @% pow2 16)})
+[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+let uint64_to_int16 x = I16.int_to_t (U64.v x @% pow2 16)
[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+val uint64_to_int8 : a:U64.t -> Tot (b:I8.t {I8.v b = (U64.v a @% pow2 8)})
+[@@(deprecated "with care; in C the result is implementation-defined when not representable")]
+let uint64_to_int8 x = I8.int_to_t (U64.v x @% pow2 8)
+
+val int8_to_uint64: a:I8.t -> Tot (b:U64.t{U64.v b = I8.v a % pow2 64})
+let int8_to_uint64 x = U64.uint_to_t (I8.v x % pow2 64)
val int8_to_uint32: a:I8.t -> Tot (b:U32.t{U32.v b = I8.v a % pow2 32})
+let int8_to_uint32 x = U32.uint_to_t (I8.v x % pow2 32)
val int8_to_uint16: a:I8.t -> Tot (b:U16.t{U16.v b = I8.v a % pow2 16})
+let int8_to_uint16 x = U16.uint_to_t (I8.v x % pow2 16)
val int8_to_uint8 : a:I8.t -> Tot (b:U8.t {U8.v b = I8.v a % pow2 8})
+let int8_to_uint8 x = U8.uint_to_t (I8.v x % pow2 8)
val int16_to_uint64: a:I16.t -> Tot (b:U64.t{U64.v b = I16.v a % pow2 64})
+let int16_to_uint64 x = U64.uint_to_t (I16.v x % pow2 64)
val int16_to_uint32: a:I16.t -> Tot (b:U32.t{U32.v b = I16.v a % pow2 32})
+let int16_to_uint32 x = U32.uint_to_t (I16.v x % pow2 32)
val int16_to_uint16: a:I16.t -> Tot (b:U16.t{U16.v b = I16.v a % pow2 16})
+let int16_to_uint16 x = U16.uint_to_t (I16.v x % pow2 16)
val int16_to_uint8 : a:I16.t -> Tot (b:U8.t {U8.v b = I16.v a % pow2 8})
+let int16_to_uint8 x = U8.uint_to_t (I16.v x % pow2 8)
val int32_to_uint64: a:I32.t -> Tot (b:U64.t{U64.v b = I32.v a % pow2 64})
+let int32_to_uint64 x = U64.uint_to_t (I32.v x % pow2 64)
val int32_to_uint32: a:I32.t -> Tot (b:U32.t{U32.v b = I32.v a % pow2 32})
+let int32_to_uint32 x = U32.uint_to_t (I32.v x % pow2 32)
val int32_to_uint16: a:I32.t -> Tot (b:U16.t{U16.v b = I32.v a % pow2 16})
+let int32_to_uint16 x = U16.uint_to_t (I32.v x % pow2 16)
val int32_to_uint8 : a:I32.t -> Tot (b:U8.t {U8.v b = I32.v a % pow2 8})
+let int32_to_uint8 x = U8.uint_to_t (I32.v x % pow2 8)
val int64_to_uint64: a:I64.t -> Tot (b:U64.t{U64.v b = I64.v a % pow2 64})
+let int64_to_uint64 x = U64.uint_to_t (I64.v x % pow2 64)
val int64_to_uint32: a:I64.t -> Tot (b:U32.t{U32.v b = I64.v a % pow2 32})
+let int64_to_uint32 x = U32.uint_to_t (I64.v x % pow2 32)
val int64_to_uint16: a:I64.t -> Tot (b:U16.t{U16.v b = I64.v a % pow2 16})
+let int64_to_uint16 x = U16.uint_to_t (I64.v x % pow2 16)
val int64_to_uint8 : a:I64.t -> Tot (b:U8.t {U8.v b = I64.v a % pow2 8})
+let int64_to_uint8 x = U8.uint_to_t (I64.v x % pow2 8)
module FStar.Int
-val shift_left:Unidentified product: [#n:pos] Unidentified product: [a:a:int_t n:{<=(0, a)}] Unidentified product: [s:nat] (Tot (int_t n))
+FStar.Int
+FStar.UInt.fsti, which is mostly
+
+val pow2_values: x:nat -> Lemma
+ (let p = pow2 x in
+ match x with
+ | 0 -> p=1
+ | 1 -> p=2
+ | 8 -> p=256
+ | 16 -> p=65536
+ | 31 -> p=2147483648
+ | 32 -> p=4294967296
+ | 63 -> p=9223372036854775808
+ | 64 -> p=18446744073709551616
+ | _ -> True)
+ [SMTPat (pow2 x)]
+
+let max_int (n:pos) : Tot int = pow2 (n-1) - 1
+let min_int (n:pos) : Tot int = - (pow2 (n-1))
let fits (x:int) (n:pos) : Tot bool = min_int n <= x && x <= max_int n
+let size (x:int) (n:pos) : Tot Type0 = b2t(fits x n)
type int_t (n:pos) = x:int{size x n}
+
+let op_Slash (a:int) (b:int{b <> 0}) : Tot int =
+ if (a >= 0 && b < 0) || (a < 0 && b >= 0) then - (abs a / abs b)
+ else abs a / abs b
let op_At_Percent (v:int) (p:int{p>0/\ p%2=0}) : Tot int =
+ let m = v % p in if m >= p/2 then m - p else m
+
+let zero (n:pos) : Tot (int_t n) = 0
#push-options "--initial_fuel 1 --max_fuel 1"
let pow2_n (#n:pos) (p:nat{p < n-1}) : Tot (int_t n) =
+ pow2_le_compat (n - 2) p; pow2 p
let pow2_minus_one (#n:pos{1 < n}) (m:nat{m < n}) : Tot (int_t n) =
+ pow2_le_compat (n - 1) m;
+ pow2 m - 1
let one (n:pos{1 < n}) : Tot (int_t n) = 1
#pop-options
let ones (n:pos) : Tot (int_t n) = -1
let incr (#n:pos) (a:int_t n)
+ : Pure (int_t n)
+ (requires (b2t (a < max_int n))) (ensures (fun _ -> True))
+ = a + 1
let decr (#n:pos) (a:int_t n)
+ : Pure (int_t n)
+ (requires (b2t (a > min_int n))) (ensures (fun _ -> True))
+ = a - 1
val incr_underspec: #n:pos -> a:int_t n -> Pure (int_t n)
+ (requires (b2t (a < max_int n)))
+ (ensures (fun b -> a + 1 = b))
val decr_underspec: #n:pos -> a:int_t n -> Pure (int_t n)
+ (requires (b2t (a > min_int n)))
+ (ensures (fun b -> a - 1 = b))
let incr_mod (#n:pos) (a:int_t n) : Tot (int_t n) =
+ (a + 1) % (pow2 (n-1))
let decr_mod (#n:pos) (a:int_t n) : Tot (int_t n) =
+ (a - 1) % (pow2 (n-1))
let add (#n:pos) (a:int_t n) (b:int_t n)
+ : Pure (int_t n)
+ (requires (size (a + b) n))
+ (ensures (fun _ -> True))
+ = a + b
val add_underspec: #n:pos -> a:int_t n -> b:int_t n -> Pure (int_t n)
+ (requires True)
+ (ensures (fun c ->
+ size (a + b) n ==> a + b = c))
#push-options "--initial_fuel 1 --max_fuel 1"
let add_mod (#n:pos) (a:int_t n) (b:int_t n) : Tot (int_t n) =
+ (a + b) @% (pow2 n)
let sub (#n:pos) (a:int_t n) (b:int_t n)
+ : Pure (int_t n)
+ (requires (size (a - b) n))
+ (ensures (fun _ -> True))
+ = a - b
val sub_underspec: #n:pos -> a:int_t n -> b:int_t n -> Pure (int_t n)
+ (requires True)
+ (ensures (fun c ->
+ size (a - b) n ==> a - b = c))
let sub_mod (#n:pos) (a:int_t n) (b:int_t n) : Tot (int_t n) =
+ (a - b) @% (pow2 n)
let mul (#n:pos) (a:int_t n) (b:int_t n)
+ : Pure (int_t n)
+ (requires (size (a * b) n))
+ (ensures (fun _ -> True))
+ = a * b
val mul_underspec: #n:pos -> a:int_t n -> b:int_t n -> Pure (int_t n)
+ (requires True)
+ (ensures (fun c ->
+ size (a * b) n ==> a * b = c))
let mul_mod (#n:pos) (a:int_t n) (b:int_t n) : Tot (int_t n) =
+ (a * b) @% (pow2 n)
#pop-options
let div (#n:pos) (a:int_t n) (b:int_t n{b <> 0})
+ : Pure (int_t n)
+ (requires (size (a / b) n))
+ (ensures (fun c -> b <> 0 ==> a / b = c))
+= a / b
val div_underspec: #n:pos -> a:int_t n -> b:int_t n{b <> 0} -> Pure (int_t n)
+ (requires True)
+ (ensures (fun c ->
+ (b <> 0 /\ size (a / b) n) ==> a / b = c))
val div_size: #n:pos -> a:int_t n{min_int n < a} -> b:int_t n{b <> 0} ->
+ Lemma (requires (size a n)) (ensures (size (a / b) n))
let udiv (#n:pos) (a:int_t n{min_int n < a}) (b:int_t n{b <> 0})
+ : Tot (c:int_t n{b <> 0 ==> a / b = c})
+ = div_size #n a b;
+ a / b
let mod (#n:pos) (a:int_t n) (b:int_t n{b <> 0}) : Tot (int_t n) =
+ a - ((a/b) * b)
let eq #n (a:int_t n) (b:int_t n) : Tot bool = a = b
+let gt #n (a:int_t n) (b:int_t n) : Tot bool = a > b
+let gte #n (a:int_t n) (b:int_t n) : Tot bool = a >= b
+let lt #n (a:int_t n) (b:int_t n) : Tot bool = a < b
+let lte #n (a:int_t n) (b:int_t n) : Tot bool = a <= b
#push-options "--initial_fuel 1 --max_fuel 1"
+
+let to_uint (#n:pos) (x:int_t n) : Tot (UInt.uint_t n) =
+ if 0 <= x then x else x + pow2 n
let from_uint (#n:pos) (x:UInt.uint_t n) : Tot (int_t n) =
+ if x <= max_int n then x else x - pow2 n
val to_uint_injective: #n:pos -> x:int_t n
+ -> Lemma (ensures from_uint (to_uint x) == x) [SMTPat (to_uint x)]
let to_int_t (m:pos) (a:int) : Tot (int_t m) = a @% pow2 m
+
+let to_vec (#n:pos) (num:int_t n) : Tot (bv_t n) =
+ UInt.to_vec (to_uint num)
let from_vec (#n:pos) (vec:bv_t n) : Tot (int_t n) =
+ let x = UInt.from_vec vec in
+ if max_int n < x then x - pow2 n else x
val to_vec_lemma_1: #n:pos -> a:int_t n -> b:int_t n ->
+ Lemma (requires a = b) (ensures equal (to_vec a) (to_vec b))
val to_vec_lemma_2: #n:pos -> a:int_t n -> b:int_t n ->
+ Lemma (requires equal (to_vec a) (to_vec b)) (ensures a = b)
val inverse_aux: #n:nat -> vec:bv_t n -> i:nat{i < n} ->
+ Lemma (requires True) (ensures index vec i = index (to_vec (from_vec vec)) i)
+ [SMTPat (index (to_vec (from_vec vec)) i)]
val inverse_vec_lemma: #n:pos -> vec:bv_t n ->
+ Lemma (requires True) (ensures equal vec (to_vec (from_vec vec)))
+ [SMTPat (to_vec (from_vec vec))]
val inverse_num_lemma: #n:pos -> num:int_t n ->
+ Lemma (requires True) (ensures num = from_vec (to_vec num))
+ [SMTPat (from_vec (to_vec num))]
val from_vec_lemma_1: #n:pos -> a:bv_t n -> b:bv_t n ->
+ Lemma (requires equal a b) (ensures from_vec a = from_vec b)
val from_vec_lemma_2: #n:pos -> a:bv_t n -> b:bv_t n ->
+ Lemma (requires from_vec a = from_vec b) (ensures equal a b)
val zero_to_vec_lemma: #n:pos -> i:nat{i < n} ->
+ Lemma (requires True) (ensures index (to_vec (zero n)) i = index (zero_vec #n) i)
+ [SMTPat (index (to_vec (zero n)) i)]
val zero_from_vec_lemma: #n:pos ->
+ Lemma (requires True) (ensures from_vec (zero_vec #n) = zero n)
+ [SMTPat (from_vec (zero_vec #n))]
val one_to_vec_lemma: #n:pos{1 < n} -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures index (to_vec (one n)) i = index (elem_vec #n (n - 1)) i)
+ [SMTPat (index (to_vec (one n)) i)]
val pow2_to_vec_lemma: #n:pos -> p:nat{p < n-1} -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures index (to_vec (pow2_n #n p)) i = index (elem_vec #n (n - p - 1)) i)
+ [SMTPat (index (to_vec (pow2_n #n p)) i)]
val pow2_from_vec_lemma: #n:pos -> p:pos{p < n-1} ->
+ Lemma (requires True) (ensures from_vec (elem_vec #n p) = pow2_n #n (n - p - 1))
+ [SMTPat (from_vec (elem_vec #n p))]
val ones_to_vec_lemma: #n:pos -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures index (to_vec (ones n)) i = index (ones_vec #n) i)
+ [SMTPat (index (to_vec (ones n)) i)]
val ones_from_vec_lemma: #n:pos ->
+ Lemma (requires True) (ensures from_vec (ones_vec #n) = ones n)
+ [SMTPat (from_vec (ones_vec #n))]
let nth (#n:pos) (a:int_t n) (i:nat{i < n}) : Tot bool = index (to_vec #n a) i
val nth_lemma: #n:pos -> a:int_t n -> b:int_t n ->
+ Lemma (requires forall (i:nat{i < n}). nth a i = nth b i)
+ (ensures a = b)
val zero_nth_lemma: #n:pos -> i:nat{i < n} ->
+ Lemma (requires True) (ensures nth (zero n) i = false)
+ [SMTPat (nth (zero n) i)]
val one_nth_lemma: #n:pos{1 < n} -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures (i = n - 1 ==> nth (one n) i = true) /\
+ (i < n - 1 ==> nth (one n) i = false))
+ [SMTPat (nth (one n) i)]
val ones_nth_lemma: #n:pos -> i:nat{i < n} ->
+ Lemma (requires True) (ensures (nth (ones n) i) = true)
+ [SMTPat (nth (ones n) i)]
let logand (#n:pos) (a:int_t n) (b:int_t n) : Tot (int_t n) =
+ from_vec #n (logand_vec #n (to_vec #n a) (to_vec #n b))
let logxor (#n:pos) (a:int_t n) (b:int_t n) : Tot (int_t n) =
+ from_vec #n (logxor_vec #n (to_vec #n a) (to_vec #n b))
let logor (#n:pos) (a:int_t n) (b:int_t n) : Tot (int_t n) =
+ from_vec #n (logor_vec #n (to_vec #n a) (to_vec #n b))
let lognot (#n:pos) (a:int_t n) : Tot (int_t n)=
+ from_vec #n (lognot_vec #n (to_vec #n a))
val logand_definition: #n:pos -> a:int_t n -> b:int_t n -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures (nth (logand a b) i = (nth a i && nth b i)))
+ [SMTPat (nth (logand a b) i)]
val logxor_definition: #n:pos -> a:int_t n -> b:int_t n -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures (nth (logxor a b) i = (nth a i <> nth b i)))
+ [SMTPat (nth (logxor a b) i)]
val logor_definition: #n:pos -> a:int_t n -> b:int_t n -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures (nth (logor a b) i = (nth a i || nth b i)))
+ [SMTPat (nth (logor a b) i)]
val lognot_definition: #n:pos -> a:int_t n -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures (nth (lognot a) i = not(nth a i)))
+ [SMTPat (nth (lognot a) i)]
inline_for_extraction
+let minus (#n:pos{1 < n}) (a:int_t n) : Tot (int_t n) =
+ add_mod (lognot a) 1
val logand_commutative: #n:pos -> a:int_t n -> b:int_t n ->
+ Lemma (requires True) (ensures (logand #n a b = logand #n b a))
val logand_associative: #n:pos -> a:int_t n -> b:int_t n -> c:int_t n ->
+ Lemma (logand #n (logand #n a b) c = logand #n a (logand #n b c))
val logand_self: #n:pos -> a:int_t n ->
+ Lemma (logand #n a a = a)
val logand_lemma_1: #n:pos -> a:int_t n ->
+ Lemma (requires True) (ensures (logand #n a (zero n) = zero n))
val logand_lemma_2: #n:pos -> a:int_t n ->
+ Lemma (logand #n a (ones n) = a)
val sign_bit_negative: #n:pos{1 < n} -> a:int_t n ->
+ Lemma (nth a 0 = true <==> a < 0)
val sign_bit_positive: #n:pos{1 < n} -> a:int_t n ->
+ Lemma (nth a 0 = false <==> 0 <= a)
val logand_pos_le: #n:pos{1 < n} -> a:int_t n{0 <= a} -> b:int_t n{0 <= b} ->
+ Lemma (0 <= logand a b /\ logand a b <= a /\ logand a b <= b)
val logand_pow2_minus_one: #n:pos{1 < n} -> a:int_t n -> m:pos{m < n} ->
+ Lemma (0 <= logand a (pow2_minus_one m) /\
+ logand a (pow2_minus_one m) <= pow2_minus_one #n m)
val logand_max: #n:pos{1 < n} -> a:int_t n{0 <= a} ->
+ Lemma (0 <= logand a (max_int n) /\ a = logand a (max_int n))
val logxor_commutative: #n:pos -> a:int_t n -> b:int_t n ->
+ Lemma (requires True) (ensures (logxor #n a b = logxor #n b a))
val logxor_associative: #n:pos -> a:int_t n -> b:int_t n -> c:int_t n ->
+ Lemma (requires True) (ensures (logxor #n (logxor #n a b) c = logxor #n a (logxor #n b c)))
val logxor_self: #n:pos -> a:int_t n ->
+ Lemma (requires True) (ensures (logxor #n a a = zero n))
val logxor_lemma_1: #n:pos -> a:int_t n ->
+ Lemma (requires True) (ensures (logxor #n a (zero n) = a))
val logxor_lemma_2: #n:pos -> a:int_t n ->
+ Lemma (requires True) (ensures (logxor #n a (ones n) = lognot #n a))
val logxor_inv: #n:pos -> a:int_t n -> b:int_t n -> Lemma
+ (a = logxor #n (logxor #n a b) b)
val logxor_neq_nonzero: #n:pos -> a:int_t n -> b:int_t n -> Lemma
+ (a <> b ==> logxor a b <> 0)
val lognot_negative: #n:pos -> a:int_t n -> Lemma
+ (requires a < 0)
+ (ensures lognot a == UInt.lognot #n (a + pow2 n))
+shift_left
val shift_right:Unidentified product: [#n:pos] Unidentified product: [a:a:int_t n:{<=(0, a)}] Unidentified product: [s:nat] (Tot (int_t n))let shift_left (#n:pos) (a:int_t n{0 <= a}) (s:nat) : Tot (int_t n) =
+ from_vec (shift_left_vec #n (to_vec #n a) s)
+shift_right
let shift_right (#n:pos) (a:int_t n{0 <= a}) (s:nat) : Tot (int_t n) =
+ from_vec (shift_right_vec #n (to_vec #n a) s)
let shift_arithmetic_right (#n:pos) (a:int_t n) (s:nat) : Tot (int_t n) =
+ from_vec (shift_arithmetic_right_vec #n (to_vec #n a) s)
val shift_left_lemma_1: #n:pos -> a:int_t n{0 <= a} -> s:nat -> i:nat{i < n && i >= n - s} ->
+ Lemma (requires True)
+ (ensures (nth (shift_left #n a s) i = false))
+ [SMTPat (nth (shift_left #n a s) i)]
val shift_left_lemma_2: #n:pos -> a:int_t n{0 <= a} -> s:nat -> i:nat{i < n && i < n - s} ->
+ Lemma (requires True)
+ (ensures (nth (shift_left #n a s) i = nth #n a (i + s)))
+ [SMTPat (nth (shift_left #n a s) i)]
val shift_left_value_lemma: #n:pos -> a:int_t n{0 <= a} -> s:nat ->
+ Lemma (requires True)
+ (ensures shift_left #n a s = (a * pow2 s) @% pow2 n)
+ [SMTPat (shift_left #n a s)]
val shift_right_lemma_1: #n:pos -> a:int_t n{0 <= a} -> s:nat -> i:nat{i < n && i < s} ->
+ Lemma (requires True)
+ (ensures (nth (shift_right #n a s) i = false))
+ [SMTPat (nth (shift_right #n a s) i)]
val shift_right_lemma_2: #n:pos -> a:int_t n{0 <= a} -> s:nat -> i:nat{i < n && i >= s} ->
+ Lemma (requires True)
+ (ensures (nth (shift_right #n a s) i = nth #n a (i - s)))
+ [SMTPat (nth (shift_right #n a s) i)]
val shift_arithmetic_right_lemma_1: #n:pos -> a:int_t n -> s:nat -> i:nat{i < n && i < s} ->
+ Lemma (requires True)
+ (ensures (nth (shift_arithmetic_right #n a s) i = nth a 0))
+ [SMTPat (nth (shift_arithmetic_right #n a s) i)]
val shift_arithmetic_right_lemma_2: #n:pos -> a:int_t n -> s:nat -> i:nat{i < n && i >= s} ->
+ Lemma (requires True)
+ (ensures (nth (shift_arithmetic_right #n a s) i = nth #n a (i - s)))
+ [SMTPat (nth (shift_arithmetic_right #n a s) i)]
module FStar.Int128
-let ((shift_right (a:t) (s:UInt32.t)):(Pure t ((requires (/\(<=(0, v a), <(UInt32.v s, n))))) ((ensures ((fun c -> =(FStar.Int.shift_right (v a) (UInt32.v s), v c))))))):(Mk (shift_right (v a) (UInt32.v s)))
+FStar.Int128
+
+THIS MODULE IS GENETATED AUTOMATICALLY USING
+mk_int.sh, DO NOT EDIT DIRECTLY ***unfold let n = 128
#set-options "--max_fuel 0 --max_ifuel 0"
FStar.UIntN.fstp, which is mostly
+
+new val t : eqtype
val v (x:t) : Tot (int_t n)
val int_to_t: x:int_t n -> Pure t
+ (requires True)
+ (ensures (fun y -> v y = x))
val uv_inv (x : t) : Lemma
+ (ensures (int_to_t (v x) == x))
+ [SMTPat (v x)]
val vu_inv (x : int_t n) : Lemma
+ (ensures (v (int_to_t x) == x))
+ [SMTPat (int_to_t x)]
val v_inj (x1 x2: t): Lemma
+ (requires (v x1 == v x2))
+ (ensures (x1 == x2))
val zero : x:t{v x = 0}
val one : x:t{v x = 1}
val add (a:t) (b:t) : Pure t
+ (requires (size (v a + v b) n))
+ (ensures (fun c -> v a + v b = v c))
val sub (a:t) (b:t) : Pure t
+ (requires (size (v a - v b) n))
+ (ensures (fun c -> v a - v b = v c))
val mul (a:t) (b:t) : Pure t
+ (requires (size (v a * v b) n))
+ (ensures (fun c -> v a * v b = v c))
val div (a:t) (b:t{v b <> 0}) : Pure t
(requires (size (v a / v b) n))
+(ensures (fun c -> v a / v b = v c))
val rem (a:t) (b:t{v b <> 0}) : Pure t
+ (requires (size (v a / v b) n))
+ (ensures (fun c -> FStar.Int.mod (v a) (v b) = v c))
val logand (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logand` v y = v z))
val logxor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logxor` v y == v z))
val logor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logor` v y == v z))
val lognot (x:t) : Pure t
+ (requires True)
+ (ensures (fun z -> lognot (v x) == v z))
+shift_right
let ((shift_left (a:t) (s:UInt32.t)):(Pure t ((requires (/\(/\(<=(0, v a), <=(*(v a, pow2 (UInt32.v s)), max_int n)), <(UInt32.v s, n))))) ((ensures ((fun c -> =(FStar.Int.shift_left (v a) (UInt32.v s), v c))))))):(Mk (shift_left (v a) (UInt32.v s)))val shift_right (a:t) (s:UInt32.t) : Pure t
+ (requires (0 <= v a /\ UInt32.v s < n))
+ (ensures (fun c -> FStar.Int.shift_right (v a) (UInt32.v s) = v c))
+shift_left
val shift_left (a:t) (s:UInt32.t) : Pure t
+ (requires (0 <= v a /\ v a * pow2 (UInt32.v s) <= max_int n /\ UInt32.v s < n))
+ (ensures (fun c -> FStar.Int.shift_left (v a) (UInt32.v s) = v c))
val shift_arithmetic_right (a:t) (s:UInt32.t) : Pure t
+ (requires (UInt32.v s < n))
+ (ensures (fun c -> FStar.Int.shift_arithmetic_right (v a) (UInt32.v s) = v c))
let eq (a:t) (b:t) : Tot bool = eq #n (v a) (v b)
+let gt (a:t) (b:t) : Tot bool = gt #n (v a) (v b)
+let gte (a:t) (b:t) : Tot bool = gte #n (v a) (v b)
+let lt (a:t) (b:t) : Tot bool = lt #n (v a) (v b)
+let lte (a:t) (b:t) : Tot bool = lte #n (v a) (v b)
unfold let op_Plus_Hat = add
+unfold let op_Subtraction_Hat = sub
+unfold let op_Star_Hat = mul
+unfold let op_Slash_Hat = div
+unfold let op_Percent_Hat = rem
+unfold let op_Hat_Hat = logxor
+unfold let op_Amp_Hat = logand
+unfold let op_Bar_Hat = logor
+unfold let op_Less_Less_Hat = shift_left
+unfold let op_Greater_Greater_Hat = shift_right
+unfold let op_Greater_Greater_Greater_Hat = shift_arithmetic_right
+unfold let op_Equals_Hat = eq
+unfold let op_Greater_Hat = gt
+unfold let op_Greater_Equals_Hat = gte
+unfold let op_Less_Hat = lt
+unfold let op_Less_Equals_Hat = lte
inline_for_extraction
+let ct_abs (a:t{min_int n < v a}) : Tot (b:t{v b = abs (v a)}) =
+ let mask = a >>>^ UInt32.uint_to_t (n - 1) in
+ if 0 <= v a then
+ begin
+ sign_bit_positive (v a);
+ nth_lemma (v mask) (FStar.Int.zero _);
+ logxor_lemma_1 (v a)
+ end
+ else
+ begin
+ sign_bit_negative (v a);
+ nth_lemma (v mask) (ones _);
+ logxor_lemma_2 (v a);
+ lognot_negative (v a);
+ UInt.lemma_lognot_value #n (to_uint (v a))
+ end;
+ (a ^^ mask) -^ mask
val to_string: t -> Tot string
val of_string: string -> Tot t
#set-options "--lax"
private
+unfold
+let __int_to_t (x:int) : Tot t
+ = int_to_t x
+#reset-options
val mul_wide: a:Int64.t -> b:Int64.t -> Pure t
+ (requires True)
+ (ensures (fun c -> v c = Int64.v a * Int64.v b))
module FStar.Int16
-let ((shift_right (a:t) (s:UInt32.t)):(Pure t ((requires (/\(<=(0, v a), <(UInt32.v s, n))))) ((ensures ((fun c -> =(FStar.Int.shift_right (v a) (UInt32.v s), v c))))))):(Mk (shift_right (v a) (UInt32.v s)))
+FStar.Int16
+
+THIS MODULE IS GENETATED AUTOMATICALLY USING
+mk_int.sh, DO NOT EDIT DIRECTLY ***unfold let n = 16
#set-options "--max_fuel 0 --max_ifuel 0"
FStar.UIntN.fstp, which is mostly
+
+new val t : eqtype
val v (x:t) : Tot (int_t n)
val int_to_t: x:int_t n -> Pure t
+ (requires True)
+ (ensures (fun y -> v y = x))
val uv_inv (x : t) : Lemma
+ (ensures (int_to_t (v x) == x))
+ [SMTPat (v x)]
val vu_inv (x : int_t n) : Lemma
+ (ensures (v (int_to_t x) == x))
+ [SMTPat (int_to_t x)]
val v_inj (x1 x2: t): Lemma
+ (requires (v x1 == v x2))
+ (ensures (x1 == x2))
val zero : x:t{v x = 0}
val one : x:t{v x = 1}
val add (a:t) (b:t) : Pure t
+ (requires (size (v a + v b) n))
+ (ensures (fun c -> v a + v b = v c))
val sub (a:t) (b:t) : Pure t
+ (requires (size (v a - v b) n))
+ (ensures (fun c -> v a - v b = v c))
val mul (a:t) (b:t) : Pure t
+ (requires (size (v a * v b) n))
+ (ensures (fun c -> v a * v b = v c))
val div (a:t) (b:t{v b <> 0}) : Pure t
(requires (size (v a / v b) n))
+(ensures (fun c -> v a / v b = v c))
val rem (a:t) (b:t{v b <> 0}) : Pure t
+ (requires (size (v a / v b) n))
+ (ensures (fun c -> FStar.Int.mod (v a) (v b) = v c))
val logand (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logand` v y = v z))
val logxor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logxor` v y == v z))
val logor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logor` v y == v z))
val lognot (x:t) : Pure t
+ (requires True)
+ (ensures (fun z -> lognot (v x) == v z))
+shift_right
let ((shift_left (a:t) (s:UInt32.t)):(Pure t ((requires (/\(/\(<=(0, v a), <=(*(v a, pow2 (UInt32.v s)), max_int n)), <(UInt32.v s, n))))) ((ensures ((fun c -> =(FStar.Int.shift_left (v a) (UInt32.v s), v c))))))):(Mk (shift_left (v a) (UInt32.v s)))val shift_right (a:t) (s:UInt32.t) : Pure t
+ (requires (0 <= v a /\ UInt32.v s < n))
+ (ensures (fun c -> FStar.Int.shift_right (v a) (UInt32.v s) = v c))
+shift_left
val shift_left (a:t) (s:UInt32.t) : Pure t
+ (requires (0 <= v a /\ v a * pow2 (UInt32.v s) <= max_int n /\ UInt32.v s < n))
+ (ensures (fun c -> FStar.Int.shift_left (v a) (UInt32.v s) = v c))
val shift_arithmetic_right (a:t) (s:UInt32.t) : Pure t
+ (requires (UInt32.v s < n))
+ (ensures (fun c -> FStar.Int.shift_arithmetic_right (v a) (UInt32.v s) = v c))
let eq (a:t) (b:t) : Tot bool = eq #n (v a) (v b)
+let gt (a:t) (b:t) : Tot bool = gt #n (v a) (v b)
+let gte (a:t) (b:t) : Tot bool = gte #n (v a) (v b)
+let lt (a:t) (b:t) : Tot bool = lt #n (v a) (v b)
+let lte (a:t) (b:t) : Tot bool = lte #n (v a) (v b)
unfold let op_Plus_Hat = add
+unfold let op_Subtraction_Hat = sub
+unfold let op_Star_Hat = mul
+unfold let op_Slash_Hat = div
+unfold let op_Percent_Hat = rem
+unfold let op_Hat_Hat = logxor
+unfold let op_Amp_Hat = logand
+unfold let op_Bar_Hat = logor
+unfold let op_Less_Less_Hat = shift_left
+unfold let op_Greater_Greater_Hat = shift_right
+unfold let op_Greater_Greater_Greater_Hat = shift_arithmetic_right
+unfold let op_Equals_Hat = eq
+unfold let op_Greater_Hat = gt
+unfold let op_Greater_Equals_Hat = gte
+unfold let op_Less_Hat = lt
+unfold let op_Less_Equals_Hat = lte
inline_for_extraction
+let ct_abs (a:t{min_int n < v a}) : Tot (b:t{v b = abs (v a)}) =
+ let mask = a >>>^ UInt32.uint_to_t (n - 1) in
+ if 0 <= v a then
+ begin
+ sign_bit_positive (v a);
+ nth_lemma (v mask) (FStar.Int.zero _);
+ logxor_lemma_1 (v a)
+ end
+ else
+ begin
+ sign_bit_negative (v a);
+ nth_lemma (v mask) (ones _);
+ logxor_lemma_2 (v a);
+ lognot_negative (v a);
+ UInt.lemma_lognot_value #n (to_uint (v a))
+ end;
+ (a ^^ mask) -^ mask
val to_string: t -> Tot string
val of_string: string -> Tot t
#set-options "--lax"
private
+unfold
+let __int_to_t (x:int) : Tot t
+ = int_to_t x
+#reset-options
module FStar.Int32
-let ((shift_right (a:t) (s:UInt32.t)):(Pure t ((requires (/\(<=(0, v a), <(UInt32.v s, n))))) ((ensures ((fun c -> =(FStar.Int.shift_right (v a) (UInt32.v s), v c))))))):(Mk (shift_right (v a) (UInt32.v s)))
+FStar.Int32
+
+THIS MODULE IS GENETATED AUTOMATICALLY USING
+mk_int.sh, DO NOT EDIT DIRECTLY ***unfold let n = 32
#set-options "--max_fuel 0 --max_ifuel 0"
FStar.UIntN.fstp, which is mostly
+
+new val t : eqtype
val v (x:t) : Tot (int_t n)
val int_to_t: x:int_t n -> Pure t
+ (requires True)
+ (ensures (fun y -> v y = x))
val uv_inv (x : t) : Lemma
+ (ensures (int_to_t (v x) == x))
+ [SMTPat (v x)]
val vu_inv (x : int_t n) : Lemma
+ (ensures (v (int_to_t x) == x))
+ [SMTPat (int_to_t x)]
val v_inj (x1 x2: t): Lemma
+ (requires (v x1 == v x2))
+ (ensures (x1 == x2))
val zero : x:t{v x = 0}
val one : x:t{v x = 1}
val add (a:t) (b:t) : Pure t
+ (requires (size (v a + v b) n))
+ (ensures (fun c -> v a + v b = v c))
val sub (a:t) (b:t) : Pure t
+ (requires (size (v a - v b) n))
+ (ensures (fun c -> v a - v b = v c))
val mul (a:t) (b:t) : Pure t
+ (requires (size (v a * v b) n))
+ (ensures (fun c -> v a * v b = v c))
val div (a:t) (b:t{v b <> 0}) : Pure t
(requires (size (v a / v b) n))
+(ensures (fun c -> v a / v b = v c))
val rem (a:t) (b:t{v b <> 0}) : Pure t
+ (requires (size (v a / v b) n))
+ (ensures (fun c -> FStar.Int.mod (v a) (v b) = v c))
val logand (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logand` v y = v z))
val logxor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logxor` v y == v z))
val logor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logor` v y == v z))
val lognot (x:t) : Pure t
+ (requires True)
+ (ensures (fun z -> lognot (v x) == v z))
+shift_right
let ((shift_left (a:t) (s:UInt32.t)):(Pure t ((requires (/\(/\(<=(0, v a), <=(*(v a, pow2 (UInt32.v s)), max_int n)), <(UInt32.v s, n))))) ((ensures ((fun c -> =(FStar.Int.shift_left (v a) (UInt32.v s), v c))))))):(Mk (shift_left (v a) (UInt32.v s)))val shift_right (a:t) (s:UInt32.t) : Pure t
+ (requires (0 <= v a /\ UInt32.v s < n))
+ (ensures (fun c -> FStar.Int.shift_right (v a) (UInt32.v s) = v c))
+shift_left
val shift_left (a:t) (s:UInt32.t) : Pure t
+ (requires (0 <= v a /\ v a * pow2 (UInt32.v s) <= max_int n /\ UInt32.v s < n))
+ (ensures (fun c -> FStar.Int.shift_left (v a) (UInt32.v s) = v c))
val shift_arithmetic_right (a:t) (s:UInt32.t) : Pure t
+ (requires (UInt32.v s < n))
+ (ensures (fun c -> FStar.Int.shift_arithmetic_right (v a) (UInt32.v s) = v c))
let eq (a:t) (b:t) : Tot bool = eq #n (v a) (v b)
+let gt (a:t) (b:t) : Tot bool = gt #n (v a) (v b)
+let gte (a:t) (b:t) : Tot bool = gte #n (v a) (v b)
+let lt (a:t) (b:t) : Tot bool = lt #n (v a) (v b)
+let lte (a:t) (b:t) : Tot bool = lte #n (v a) (v b)
unfold let op_Plus_Hat = add
+unfold let op_Subtraction_Hat = sub
+unfold let op_Star_Hat = mul
+unfold let op_Slash_Hat = div
+unfold let op_Percent_Hat = rem
+unfold let op_Hat_Hat = logxor
+unfold let op_Amp_Hat = logand
+unfold let op_Bar_Hat = logor
+unfold let op_Less_Less_Hat = shift_left
+unfold let op_Greater_Greater_Hat = shift_right
+unfold let op_Greater_Greater_Greater_Hat = shift_arithmetic_right
+unfold let op_Equals_Hat = eq
+unfold let op_Greater_Hat = gt
+unfold let op_Greater_Equals_Hat = gte
+unfold let op_Less_Hat = lt
+unfold let op_Less_Equals_Hat = lte
inline_for_extraction
+let ct_abs (a:t{min_int n < v a}) : Tot (b:t{v b = abs (v a)}) =
+ let mask = a >>>^ UInt32.uint_to_t (n - 1) in
+ if 0 <= v a then
+ begin
+ sign_bit_positive (v a);
+ nth_lemma (v mask) (FStar.Int.zero _);
+ logxor_lemma_1 (v a)
+ end
+ else
+ begin
+ sign_bit_negative (v a);
+ nth_lemma (v mask) (ones _);
+ logxor_lemma_2 (v a);
+ lognot_negative (v a);
+ UInt.lemma_lognot_value #n (to_uint (v a))
+ end;
+ (a ^^ mask) -^ mask
val to_string: t -> Tot string
val of_string: string -> Tot t
#set-options "--lax"
private
+unfold
+let __int_to_t (x:int) : Tot t
+ = int_to_t x
+#reset-options
module FStar.Int64
-let ((shift_right (a:t) (s:UInt32.t)):(Pure t ((requires (/\(<=(0, v a), <(UInt32.v s, n))))) ((ensures ((fun c -> =(FStar.Int.shift_right (v a) (UInt32.v s), v c))))))):(Mk (shift_right (v a) (UInt32.v s)))
+FStar.Int64
+
+THIS MODULE IS GENETATED AUTOMATICALLY USING
+mk_int.sh, DO NOT EDIT DIRECTLY ***unfold let n = 64
#set-options "--max_fuel 0 --max_ifuel 0"
FStar.UIntN.fstp, which is mostly
+
+new val t : eqtype
val v (x:t) : Tot (int_t n)
val int_to_t: x:int_t n -> Pure t
+ (requires True)
+ (ensures (fun y -> v y = x))
val uv_inv (x : t) : Lemma
+ (ensures (int_to_t (v x) == x))
+ [SMTPat (v x)]
val vu_inv (x : int_t n) : Lemma
+ (ensures (v (int_to_t x) == x))
+ [SMTPat (int_to_t x)]
val v_inj (x1 x2: t): Lemma
+ (requires (v x1 == v x2))
+ (ensures (x1 == x2))
val zero : x:t{v x = 0}
val one : x:t{v x = 1}
val add (a:t) (b:t) : Pure t
+ (requires (size (v a + v b) n))
+ (ensures (fun c -> v a + v b = v c))
val sub (a:t) (b:t) : Pure t
+ (requires (size (v a - v b) n))
+ (ensures (fun c -> v a - v b = v c))
val mul (a:t) (b:t) : Pure t
+ (requires (size (v a * v b) n))
+ (ensures (fun c -> v a * v b = v c))
val div (a:t) (b:t{v b <> 0}) : Pure t
(requires (size (v a / v b) n))
+(ensures (fun c -> v a / v b = v c))
val rem (a:t) (b:t{v b <> 0}) : Pure t
+ (requires (size (v a / v b) n))
+ (ensures (fun c -> FStar.Int.mod (v a) (v b) = v c))
val logand (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logand` v y = v z))
val logxor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logxor` v y == v z))
val logor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logor` v y == v z))
val lognot (x:t) : Pure t
+ (requires True)
+ (ensures (fun z -> lognot (v x) == v z))
+shift_right
let ((shift_left (a:t) (s:UInt32.t)):(Pure t ((requires (/\(/\(<=(0, v a), <=(*(v a, pow2 (UInt32.v s)), max_int n)), <(UInt32.v s, n))))) ((ensures ((fun c -> =(FStar.Int.shift_left (v a) (UInt32.v s), v c))))))):(Mk (shift_left (v a) (UInt32.v s)))val shift_right (a:t) (s:UInt32.t) : Pure t
+ (requires (0 <= v a /\ UInt32.v s < n))
+ (ensures (fun c -> FStar.Int.shift_right (v a) (UInt32.v s) = v c))
+shift_left
val shift_left (a:t) (s:UInt32.t) : Pure t
+ (requires (0 <= v a /\ v a * pow2 (UInt32.v s) <= max_int n /\ UInt32.v s < n))
+ (ensures (fun c -> FStar.Int.shift_left (v a) (UInt32.v s) = v c))
val shift_arithmetic_right (a:t) (s:UInt32.t) : Pure t
+ (requires (UInt32.v s < n))
+ (ensures (fun c -> FStar.Int.shift_arithmetic_right (v a) (UInt32.v s) = v c))
let eq (a:t) (b:t) : Tot bool = eq #n (v a) (v b)
+let gt (a:t) (b:t) : Tot bool = gt #n (v a) (v b)
+let gte (a:t) (b:t) : Tot bool = gte #n (v a) (v b)
+let lt (a:t) (b:t) : Tot bool = lt #n (v a) (v b)
+let lte (a:t) (b:t) : Tot bool = lte #n (v a) (v b)
unfold let op_Plus_Hat = add
+unfold let op_Subtraction_Hat = sub
+unfold let op_Star_Hat = mul
+unfold let op_Slash_Hat = div
+unfold let op_Percent_Hat = rem
+unfold let op_Hat_Hat = logxor
+unfold let op_Amp_Hat = logand
+unfold let op_Bar_Hat = logor
+unfold let op_Less_Less_Hat = shift_left
+unfold let op_Greater_Greater_Hat = shift_right
+unfold let op_Greater_Greater_Greater_Hat = shift_arithmetic_right
+unfold let op_Equals_Hat = eq
+unfold let op_Greater_Hat = gt
+unfold let op_Greater_Equals_Hat = gte
+unfold let op_Less_Hat = lt
+unfold let op_Less_Equals_Hat = lte
inline_for_extraction
+let ct_abs (a:t{min_int n < v a}) : Tot (b:t{v b = abs (v a)}) =
+ let mask = a >>>^ UInt32.uint_to_t (n - 1) in
+ if 0 <= v a then
+ begin
+ sign_bit_positive (v a);
+ nth_lemma (v mask) (FStar.Int.zero _);
+ logxor_lemma_1 (v a)
+ end
+ else
+ begin
+ sign_bit_negative (v a);
+ nth_lemma (v mask) (ones _);
+ logxor_lemma_2 (v a);
+ lognot_negative (v a);
+ UInt.lemma_lognot_value #n (to_uint (v a))
+ end;
+ (a ^^ mask) -^ mask
val to_string: t -> Tot string
val of_string: string -> Tot t
#set-options "--lax"
private
+unfold
+let __int_to_t (x:int) : Tot t
+ = int_to_t x
+#reset-options
module FStar.Int8
-let ((shift_right (a:t) (s:UInt32.t)):(Pure t ((requires (/\(<=(0, v a), <(UInt32.v s, n))))) ((ensures ((fun c -> =(FStar.Int.shift_right (v a) (UInt32.v s), v c))))))):(Mk (shift_right (v a) (UInt32.v s)))
+FStar.Int8
+
+THIS MODULE IS GENETATED AUTOMATICALLY USING
+mk_int.sh, DO NOT EDIT DIRECTLY ***unfold let n = 8
#set-options "--max_fuel 0 --max_ifuel 0"
FStar.UIntN.fstp, which is mostly
+
+new val t : eqtype
val v (x:t) : Tot (int_t n)
val int_to_t: x:int_t n -> Pure t
+ (requires True)
+ (ensures (fun y -> v y = x))
val uv_inv (x : t) : Lemma
+ (ensures (int_to_t (v x) == x))
+ [SMTPat (v x)]
val vu_inv (x : int_t n) : Lemma
+ (ensures (v (int_to_t x) == x))
+ [SMTPat (int_to_t x)]
val v_inj (x1 x2: t): Lemma
+ (requires (v x1 == v x2))
+ (ensures (x1 == x2))
val zero : x:t{v x = 0}
val one : x:t{v x = 1}
val add (a:t) (b:t) : Pure t
+ (requires (size (v a + v b) n))
+ (ensures (fun c -> v a + v b = v c))
val sub (a:t) (b:t) : Pure t
+ (requires (size (v a - v b) n))
+ (ensures (fun c -> v a - v b = v c))
val mul (a:t) (b:t) : Pure t
+ (requires (size (v a * v b) n))
+ (ensures (fun c -> v a * v b = v c))
val div (a:t) (b:t{v b <> 0}) : Pure t
(requires (size (v a / v b) n))
+(ensures (fun c -> v a / v b = v c))
val rem (a:t) (b:t{v b <> 0}) : Pure t
+ (requires (size (v a / v b) n))
+ (ensures (fun c -> FStar.Int.mod (v a) (v b) = v c))
val logand (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logand` v y = v z))
val logxor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logxor` v y == v z))
val logor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logor` v y == v z))
val lognot (x:t) : Pure t
+ (requires True)
+ (ensures (fun z -> lognot (v x) == v z))
+shift_right
let ((shift_left (a:t) (s:UInt32.t)):(Pure t ((requires (/\(/\(<=(0, v a), <=(*(v a, pow2 (UInt32.v s)), max_int n)), <(UInt32.v s, n))))) ((ensures ((fun c -> =(FStar.Int.shift_left (v a) (UInt32.v s), v c))))))):(Mk (shift_left (v a) (UInt32.v s)))val shift_right (a:t) (s:UInt32.t) : Pure t
+ (requires (0 <= v a /\ UInt32.v s < n))
+ (ensures (fun c -> FStar.Int.shift_right (v a) (UInt32.v s) = v c))
+shift_left
val shift_left (a:t) (s:UInt32.t) : Pure t
+ (requires (0 <= v a /\ v a * pow2 (UInt32.v s) <= max_int n /\ UInt32.v s < n))
+ (ensures (fun c -> FStar.Int.shift_left (v a) (UInt32.v s) = v c))
val shift_arithmetic_right (a:t) (s:UInt32.t) : Pure t
+ (requires (UInt32.v s < n))
+ (ensures (fun c -> FStar.Int.shift_arithmetic_right (v a) (UInt32.v s) = v c))
let eq (a:t) (b:t) : Tot bool = eq #n (v a) (v b)
+let gt (a:t) (b:t) : Tot bool = gt #n (v a) (v b)
+let gte (a:t) (b:t) : Tot bool = gte #n (v a) (v b)
+let lt (a:t) (b:t) : Tot bool = lt #n (v a) (v b)
+let lte (a:t) (b:t) : Tot bool = lte #n (v a) (v b)
unfold let op_Plus_Hat = add
+unfold let op_Subtraction_Hat = sub
+unfold let op_Star_Hat = mul
+unfold let op_Slash_Hat = div
+unfold let op_Percent_Hat = rem
+unfold let op_Hat_Hat = logxor
+unfold let op_Amp_Hat = logand
+unfold let op_Bar_Hat = logor
+unfold let op_Less_Less_Hat = shift_left
+unfold let op_Greater_Greater_Hat = shift_right
+unfold let op_Greater_Greater_Greater_Hat = shift_arithmetic_right
+unfold let op_Equals_Hat = eq
+unfold let op_Greater_Hat = gt
+unfold let op_Greater_Equals_Hat = gte
+unfold let op_Less_Hat = lt
+unfold let op_Less_Equals_Hat = lte
inline_for_extraction
+let ct_abs (a:t{min_int n < v a}) : Tot (b:t{v b = abs (v a)}) =
+ let mask = a >>>^ UInt32.uint_to_t (n - 1) in
+ if 0 <= v a then
+ begin
+ sign_bit_positive (v a);
+ nth_lemma (v mask) (FStar.Int.zero _);
+ logxor_lemma_1 (v a)
+ end
+ else
+ begin
+ sign_bit_negative (v a);
+ nth_lemma (v mask) (ones _);
+ logxor_lemma_2 (v a);
+ lognot_negative (v a);
+ UInt.lemma_lognot_value #n (to_uint (v a))
+ end;
+ (a ^^ mask) -^ mask
val to_string: t -> Tot string
val of_string: string -> Tot t
#set-options "--lax"
private
+unfold
+let __int_to_t (x:int) : Tot t
+ = int_to_t x
+#reset-options
module FStar.Integers
-
+FStar.Integers
+#set-options "--initial_ifuel 2 --max_ifuel 2 --initial_fuel 0 --max_fuel 0"
irreducible
+let mark_for_norm = ()
unfold
+let norm (#a:Type) (x:a) = norm [iota; delta_attr [`%mark_for_norm]] x
type width =
+ | W8
+ | W16
+ | W32
+ | W64
+ | W128
+ | Winfinite
[@@mark_for_norm]
+let nat_of_width = function
+ | W8 -> Some 8
+ | W16 -> Some 16
+ | W32 -> Some 32
+ | W64 -> Some 64
+ | W128 -> Some 128
+ | Winfinite -> None
let fixed_width = w:width{w <> Winfinite}
[@@mark_for_norm]
+let nat_of_fixed_width (w:fixed_width) =
+ match nat_of_width w with
+ | Some v -> v
type signed_width =
+ | Signed of width
+ | Unsigned of fixed_width //We don't support (Unsigned WInfinite); use nat instead
[@@mark_for_norm]
+let width_of_sw = function
+ | Signed w -> w
+ | Unsigned w -> w
[@@mark_for_norm]
+noextract
+inline_for_extraction
+let int_t sw : Tot Type0 =
+ match sw with
+ | Unsigned W8 -> FStar.UInt8.t
+ | Unsigned W16 -> FStar.UInt16.t
+ | Unsigned W32 -> FStar.UInt32.t
+ | Unsigned W64 -> FStar.UInt64.t
+ | Unsigned W128 -> FStar.UInt128.t
+ | Signed Winfinite -> int
+ | Signed W8 -> FStar.Int8.t
+ | Signed W16 -> FStar.Int16.t
+ | Signed W32 -> FStar.Int32.t
+ | Signed W64 -> FStar.Int64.t
+ | Signed W128 -> FStar.Int128.t
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let within_bounds' sw (x:int) =
+ match sw, nat_of_width (width_of_sw sw) with
+ | Signed _, None -> True
+ | Signed _, Some n -> FStar.Int.size x n
+ | Unsigned _, Some n -> FStar.UInt.size x n
unfold
+let within_bounds sw x = norm (within_bounds' sw x)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let v #sw (x:int_t sw)
+ : Tot (y:int_t (Signed Winfinite){within_bounds sw y})
+ = match sw with
+ | Unsigned w ->
+ (match w with
+ | W8 -> FStar.UInt8.v x
+ | W16 -> FStar.UInt16.v x
+ | W32 -> FStar.UInt32.v x
+ | W64 -> FStar.UInt64.v x
+ | W128 -> FStar.UInt128.v x)
+ | Signed w ->
+ (match w with
+ | Winfinite -> x
+ | W8 -> FStar.Int8.v x
+ | W16 -> FStar.Int16.v x
+ | W32 -> FStar.Int32.v x
+ | W64 -> FStar.Int64.v x
+ | W128 -> FStar.Int128.v x)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let u #sw
+ (x:int_t (Signed Winfinite){within_bounds sw x})
+ : Tot (y:int_t sw{norm (v x == v y)})
+ = match sw with
+ | Unsigned w ->
+ (match w with
+ | W8 -> FStar.UInt8.uint_to_t x
+ | W16 -> FStar.UInt16.uint_to_t x
+ | W32 -> FStar.UInt32.uint_to_t x
+ | W64 -> FStar.UInt64.uint_to_t x
+ | W128 -> FStar.UInt128.uint_to_t x)
+ | Signed w ->
+ (match w with
+ | Winfinite -> x
+ | W8 -> FStar.Int8.int_to_t x
+ | W16 -> FStar.Int16.int_to_t x
+ | W32 -> FStar.Int32.int_to_t x
+ | W64 -> FStar.Int64.int_to_t x
+ | W128 -> FStar.Int128.int_to_t x)
irreducible
+noextract
+let cast #sw #sw'
+ (from:int_t sw{within_bounds sw' (v from)})
+ : Tot (to:int_t sw'{norm (v from == v to)})
+ = u (v from)
[@@mark_for_norm]
+unfold
+noextract
+let cast_ok #from to (x:int_t from) = within_bounds to (v x)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let ( + ) #sw
+ (x:int_t sw)
+ (y:int_t sw{within_bounds sw (v x + v y)})
+ : Tot (int_t sw)
+ = match sw with
+ | Signed Winfinite -> x + y
+ | Unsigned W8 -> FStar.UInt8.(x +^ y)
+ | Unsigned W16 -> FStar.UInt16.(x +^ y)
+ | Unsigned W32 -> FStar.UInt32.(x +^ y)
+ | Unsigned W64 -> FStar.UInt64.(x +^ y)
+ | Unsigned W128 -> FStar.UInt128.(x +^ y)
+ | Signed W8 -> FStar.Int8.(x +^ y)
+ | Signed W16 -> FStar.Int16.(x +^ y)
+ | Signed W32 -> FStar.Int32.(x +^ y)
+ | Signed W64 -> FStar.Int64.(x +^ y)
+ | Signed W128 -> FStar.Int128.(x +^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let ( +? ) (#w:fixed_width)
+ (x:int_t (Unsigned w))
+ (y:int_t (Unsigned w))
+ : Tot (int_t (Unsigned w))
+ = match w with
+ | W8 -> FStar.UInt8.(x +?^ y)
+ | W16 -> FStar.UInt16.(x +?^ y)
+ | W32 -> FStar.UInt32.(x +?^ y)
+ | W64 -> FStar.UInt64.(x +?^ y)
+ | W128 -> FStar.UInt128.(x +?^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+noextract
+let modulo sw (x:int) (y:pos{Signed? sw ==> y%2=0}) =
+ match sw with
+ | Unsigned _ -> x % y
+ | _ -> FStar.Int.(x @% y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let ( +% ) (#sw:_{Unsigned? sw})
+ (x:int_t sw)
+ (y:int_t sw)
+ : Tot (int_t sw)
+ = let Unsigned w = sw in
+ match w with
+ | W8 -> FStar.UInt8.(x +%^ y)
+ | W16 -> FStar.UInt16.(x +%^ y)
+ | W32 -> FStar.UInt32.(x +%^ y)
+ | W64 -> FStar.UInt64.(x +%^ y)
+ | W128 -> FStar.UInt128.(x +%^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let op_Subtraction #sw
+ (x:int_t sw)
+ (y:int_t sw{within_bounds sw (v x - v y)})
+ : Tot (int_t sw)
+ = match sw with
+ | Signed Winfinite -> x - y
+ | Unsigned W8 -> FStar.UInt8.(x -^ y)
+ | Unsigned W16 -> FStar.UInt16.(x -^ y)
+ | Unsigned W32 -> FStar.UInt32.(x -^ y)
+ | Unsigned W64 -> FStar.UInt64.(x -^ y)
+ | Unsigned W128 -> FStar.UInt128.(x -^ y)
+ | Signed W8 -> FStar.Int8.(x -^ y)
+ | Signed W16 -> FStar.Int16.(x -^ y)
+ | Signed W32 -> FStar.Int32.(x -^ y)
+ | Signed W64 -> FStar.Int64.(x -^ y)
+ | Signed W128 -> FStar.Int128.(x -^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let op_Subtraction_Question
+ (#sw:_{Unsigned? sw})
+ (x:int_t sw)
+ (y:int_t sw)
+ : Tot (int_t sw)
+ = let Unsigned w = sw in
+ match w with
+ | W8 -> FStar.UInt8.(x -?^ y)
+ | W16 -> FStar.UInt16.(x -?^ y)
+ | W32 -> FStar.UInt32.(x -?^ y)
+ | W64 -> FStar.UInt64.(x -?^ y)
+ | W128 -> FStar.UInt128.(x -?^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let op_Subtraction_Percent
+ (#sw:_{Unsigned? sw})
+ (x:int_t sw)
+ (y:int_t sw)
+ : Tot (int_t sw)
+ = let Unsigned w = sw in
+ match w with
+ | W8 -> FStar.UInt8.(x -%^ y)
+ | W16 -> FStar.UInt16.(x -%^ y)
+ | W32 -> FStar.UInt32.(x -%^ y)
+ | W64 -> FStar.UInt64.(x -%^ y)
+ | W128 -> FStar.UInt128.(x -%^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let op_Minus
+ (#sw:_{Signed? sw})
+ (x:int_t sw{within_bounds sw (0 - v x)})
+ : Tot (int_t sw)
+ = let Signed w = sw in
+ match w with
+ | Winfinite -> 0 - x
+ | W8 -> FStar.Int8.(0y -^ x)
+ | W16 -> FStar.Int16.(0s -^ x)
+ | W32 -> FStar.Int32.(0l -^ x)
+ | W64 -> FStar.Int64.(0L -^ x)
+ | W128 -> FStar.Int128.(int_to_t 0 -^ x)
+
+[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let ( * ) (#sw:signed_width{width_of_sw sw <> W128})
+ (x:int_t sw)
+ (y:int_t sw{within_bounds sw (v x * v y)})
+ : Tot (int_t sw)
+ = match sw with
+ | Signed Winfinite -> x * y
+ | Unsigned W8 -> FStar.UInt8.(x *^ y)
+ | Unsigned W16 -> FStar.UInt16.(x *^ y)
+ | Unsigned W32 -> FStar.UInt32.(x *^ y)
+ | Unsigned W64 -> FStar.UInt64.(x *^ y)
+ | Signed W8 -> FStar.Int8.(x *^ y)
+ | Signed W16 -> FStar.Int16.(x *^ y)
+ | Signed W32 -> FStar.Int32.(x *^ y)
+ | Signed W64 -> FStar.Int64.(x *^ y)
+ | Signed W128 -> FStar.Int128.(x *^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let ( *? ) (#sw:_{Unsigned? sw /\ width_of_sw sw <> W128})
+ (x:int_t sw)
+ (y:int_t sw)
+ : Tot (int_t sw)
+ = let Unsigned w = sw in
+ match w with
+ | W8 -> FStar.UInt8.(x *?^ y)
+ | W16 -> FStar.UInt16.(x *?^ y)
+ | W32 -> FStar.UInt32.(x *?^ y)
+ | W64 -> FStar.UInt64.(x *?^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let ( *% ) (#sw:_{Unsigned? sw /\ width_of_sw sw <> W128})
+ (x:int_t sw)
+ (y:int_t sw)
+ : Tot (int_t sw)
+ = let Unsigned w = sw in
+ match w with
+ | W8 -> FStar.UInt8.(x *%^ y)
+ | W16 -> FStar.UInt16.(x *%^ y)
+ | W32 -> FStar.UInt32.(x *%^ y)
+ | W64 -> FStar.UInt64.(x *%^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let ( > ) #sw (x:int_t sw) (y:int_t sw) : bool =
+ match sw with
+ | Signed Winfinite -> x > y
+ | Unsigned W8 -> FStar.UInt8.(x >^ y)
+ | Unsigned W16 -> FStar.UInt16.(x >^ y)
+ | Unsigned W32 -> FStar.UInt32.(x >^ y)
+ | Unsigned W64 -> FStar.UInt64.(x >^ y)
+ | Unsigned W128 -> FStar.UInt128.(x >^ y)
+ | Signed W8 -> FStar.Int8.(x >^ y)
+ | Signed W16 -> FStar.Int16.(x >^ y)
+ | Signed W32 -> FStar.Int32.(x >^ y)
+ | Signed W64 -> FStar.Int64.(x >^ y)
+ | Signed W128 -> FStar.Int128.(x >^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let ( >= ) #sw (x:int_t sw) (y:int_t sw) : bool =
+ match sw with
+ | Signed Winfinite -> x >= y
+ | Unsigned W8 -> FStar.UInt8.(x >=^ y)
+ | Unsigned W16 -> FStar.UInt16.(x >=^ y)
+ | Unsigned W32 -> FStar.UInt32.(x >=^ y)
+ | Unsigned W64 -> FStar.UInt64.(x >=^ y)
+ | Unsigned W128 -> FStar.UInt128.(x >=^ y)
+ | Signed W8 -> FStar.Int8.(x >=^ y)
+ | Signed W16 -> FStar.Int16.(x >=^ y)
+ | Signed W32 -> FStar.Int32.(x >=^ y)
+ | Signed W64 -> FStar.Int64.(x >=^ y)
+ | Signed W128 -> FStar.Int128.(x >=^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let ( < ) #sw (x:int_t sw) (y:int_t sw) : bool =
+ match sw with
+ | Signed Winfinite -> x < y
+ | Unsigned W8 -> FStar.UInt8.(x <^ y)
+ | Unsigned W16 -> FStar.UInt16.(x <^ y)
+ | Unsigned W32 -> FStar.UInt32.(x <^ y)
+ | Unsigned W64 -> FStar.UInt64.(x <^ y)
+ | Unsigned W128 -> FStar.UInt128.(x <^ y)
+ | Signed W8 -> FStar.Int8.(x <^ y)
+ | Signed W16 -> FStar.Int16.(x <^ y)
+ | Signed W32 -> FStar.Int32.(x <^ y)
+ | Signed W64 -> FStar.Int64.(x <^ y)
+ | Signed W128 -> FStar.Int128.(x <^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let ( <= ) #sw (x:int_t sw) (y:int_t sw) : bool =
+ match sw with
+ | Signed Winfinite -> x <= y
+ | Unsigned W8 -> FStar.UInt8.(x <=^ y)
+ | Unsigned W16 -> FStar.UInt16.(x <=^ y)
+ | Unsigned W32 -> FStar.UInt32.(x <=^ y)
+ | Unsigned W64 -> FStar.UInt64.(x <=^ y)
+ | Unsigned W128 -> FStar.UInt128.(x <=^ y)
+ | Signed W8 -> FStar.Int8.(x <=^ y)
+ | Signed W16 -> FStar.Int16.(x <=^ y)
+ | Signed W32 -> FStar.Int32.(x <=^ y)
+ | Signed W64 -> FStar.Int64.(x <=^ y)
+ | Signed W128 -> FStar.Int128.(x <=^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let ( / ) (#sw:signed_width{sw <> Unsigned W128})
+ (x:int_t sw)
+ (y:int_t sw{0 <> (v y <: Prims.int) /\
+ (match sw with
+ | Unsigned _ -> within_bounds sw (v x / v y)
+ | Signed _ -> within_bounds sw (v x `FStar.Int.op_Slash` v y))})
+ : Tot (int_t sw)
+ = match sw with
+ | Signed Winfinite -> x / y
+ | Unsigned W8 -> FStar.UInt8.(x /^ y)
+ | Unsigned W16 -> FStar.UInt16.(x /^ y)
+ | Unsigned W32 -> FStar.UInt32.(x /^ y)
+ | Unsigned W64 -> FStar.UInt64.(x /^ y)
+ | Signed W8 -> FStar.Int8.(x /^ y)
+ | Signed W16 -> FStar.Int16.(x /^ y)
+ | Signed W32 -> FStar.Int32.(x /^ y)
+ | Signed W64 -> FStar.Int64.(x /^ y)
+ | Signed W128 -> FStar.Int128.(x /^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let ( % ) (#sw:signed_width{sw <> Unsigned W128})
+ (x:int_t sw)
+ (y:int_t sw{0 <> (v y <: Prims.int) /\
+ (match sw with
+ | Unsigned _ -> within_bounds sw (FStar.UInt.mod #(nat_of_fixed_width (width_of_sw sw)) (v x) (v y))
+ | Signed Winfinite -> True
+ | Signed _ -> within_bounds sw (FStar.Int.mod #(nat_of_fixed_width (width_of_sw sw)) (v x) (v y))) /\
+ within_bounds sw (FStar.Int.op_Slash (v x) (v y))})
+ : Tot (int_t sw)
+ = match sw with
+ | Signed Winfinite -> x % y
+ | Unsigned W8 -> FStar.UInt8.(x %^ y)
+ | Unsigned W16 -> FStar.UInt16.(x %^ y)
+ | Unsigned W32 -> FStar.UInt32.(x %^ y)
+ | Unsigned W64 -> FStar.UInt64.(x %^ y)
+ | Signed W8 -> FStar.Int8.(x %^ y)
+ | Signed W16 -> FStar.Int16.(x %^ y)
+ | Signed W32 -> FStar.Int32.(x %^ y)
+ | Signed W64 -> FStar.Int64.(x %^ y)
+ | Signed W128 -> FStar.Int128.(x %^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let ( ^^ ) #sw (x:int_t sw) (y:int_t sw{width_of_sw sw <> Winfinite})
+ : Tot (int_t sw)
+ = match sw with
+ | Unsigned W8 -> FStar.UInt8.(x ^^ y)
+ | Unsigned W16 -> FStar.UInt16.(x ^^ y)
+ | Unsigned W32 -> FStar.UInt32.(x ^^ y)
+ | Unsigned W64 -> FStar.UInt64.(x ^^ y)
+ | Unsigned W128 -> FStar.UInt128.(x ^^ y)
+ | Signed W8 -> FStar.Int8.(x ^^ y)
+ | Signed W16 -> FStar.Int16.(x ^^ y)
+ | Signed W32 -> FStar.Int32.(x ^^ y)
+ | Signed W64 -> FStar.Int64.(x ^^ y)
+ | Signed W128 -> FStar.Int128.(x ^^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let ( &^ ) #sw (x:int_t sw) (y:int_t sw{width_of_sw sw <> Winfinite})
+ : Tot (int_t sw)
+ = match sw with
+ | Unsigned W8 -> FStar.UInt8.(x &^ y)
+ | Unsigned W16 -> FStar.UInt16.(x &^ y)
+ | Unsigned W32 -> FStar.UInt32.(x &^ y)
+ | Unsigned W64 -> FStar.UInt64.(x &^ y)
+ | Unsigned W128 -> FStar.UInt128.(x &^ y)
+ | Signed W8 -> FStar.Int8.(x &^ y)
+ | Signed W16 -> FStar.Int16.(x &^ y)
+ | Signed W32 -> FStar.Int32.(x &^ y)
+ | Signed W64 -> FStar.Int64.(x &^ y)
+ | Signed W128 -> FStar.Int128.(x &^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let ( |^ ) #sw (x:int_t sw) (y:int_t sw{width_of_sw sw <> Winfinite})
+ : Tot (int_t sw)
+ = match sw with
+ | Unsigned W8 -> FStar.UInt8.(x |^ y)
+ | Unsigned W16 -> FStar.UInt16.(x |^ y)
+ | Unsigned W32 -> FStar.UInt32.(x |^ y)
+ | Unsigned W64 -> FStar.UInt64.(x |^ y)
+ | Unsigned W128 -> FStar.UInt128.(x |^ y)
+ | Signed W8 -> FStar.Int8.(x |^ y)
+ | Signed W16 -> FStar.Int16.(x |^ y)
+ | Signed W32 -> FStar.Int32.(x |^ y)
+ | Signed W64 -> FStar.Int64.(x |^ y)
+ | Signed W128 -> FStar.Int128.(x |^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let ( <<^ ) #sw (x:int_t sw{0 <= v x})
+ (y:int_t (Unsigned W32){width_of_sw sw <> Winfinite /\ v y < nat_of_fixed_width (width_of_sw sw) /\ (Signed? sw ==> within_bounds sw (v x * pow2 (v y)))})
+ : Tot (int_t sw)
+ = match sw with
+ | Unsigned W8 -> FStar.UInt8.(x <<^ y)
+ | Unsigned W16 -> FStar.UInt16.(x <<^ y)
+ | Unsigned W32 -> FStar.UInt32.(x <<^ y)
+ | Unsigned W64 -> FStar.UInt64.(x <<^ y)
+ | Unsigned W128 -> FStar.UInt128.(x <<^ y)
+ | Signed W8 -> FStar.Int8.(x <<^ y)
+ | Signed W16 -> FStar.Int16.(x <<^ y)
+ | Signed W32 -> FStar.Int32.(x <<^ y)
+ | Signed W64 -> FStar.Int64.(x <<^ y)
+ | Signed W128 -> FStar.Int128.(x <<^ y)
[@@mark_for_norm; strict_on_arguments [0]]
+unfold
+noextract
+let ( >>^ ) #sw (x:int_t sw{0 <= v x})
+ (y:int_t (Unsigned W32){width_of_sw sw <> Winfinite /\ v y < nat_of_fixed_width (width_of_sw sw)})
+ : Tot (int_t sw)
+ = match sw with
+ | Unsigned W8 -> FStar.UInt8.(x >>^ y)
+ | Unsigned W16 -> FStar.UInt16.(x >>^ y)
+ | Unsigned W32 -> FStar.UInt32.(x >>^ y)
+ | Unsigned W64 -> FStar.UInt64.(x >>^ y)
+ | Unsigned W128 -> FStar.UInt128.(x >>^ y)
+ | Signed W8 -> FStar.Int8.(x >>^ y)
+ | Signed W16 -> FStar.Int16.(x >>^ y)
+ | Signed W32 -> FStar.Int32.(x >>^ y)
+ | Signed W64 -> FStar.Int64.(x >>^ y)
+ | Signed W128 -> FStar.Int128.(x >>^ y)
[@@mark_for_norm]
+unfold
+let uint_8 = int_t (Unsigned W8)
[@@mark_for_norm]
+unfold
+let uint_16 = int_t (Unsigned W16)
[@@mark_for_norm]
+unfold
+let uint_32 = int_t (Unsigned W32)
[@@mark_for_norm]
+unfold
+let uint_64 = int_t (Unsigned W64)
[@@mark_for_norm]
+unfold
+let int = int_t (Signed Winfinite)
[@@mark_for_norm]
+unfold
+let int_8 = int_t (Signed W8)
[@@mark_for_norm]
+unfold
+let int_16 = int_t (Signed W16)
[@@mark_for_norm]
+unfold
+let int_32 = int_t (Signed W32)
[@@mark_for_norm]
+unfold
+let int_64 = int_t (Signed W64)
[@@mark_for_norm]
+unfold
+let int_128 = int_t (Signed W128)
[@@mark_for_norm]
+unfold
+let ok #sw
+ (op:(int_t (Signed Winfinite)
+ -> int_t (Signed Winfinite)
+ -> int_t (Signed Winfinite)))
+ (x:int_t sw)
+ (y:int_t sw)
+ = within_bounds sw (op (v x) (v y))
[@@mark_for_norm]
+unfold
+let nat = i:int{ i >= 0 }
[@@mark_for_norm]
+unfold
+let pos = i:nat{ 0 < i }
let f_int (x:int) (y:int) = x + y
+let f_nat (x:nat) (y:nat) = x + y
+let f_nat_int_pos (x:nat) (y:int) (z:pos) = x + y + z
+let f_uint_8 (x:uint_8) (y:uint_8{ok (+) x y}) = x + y
+let f_int_16 (x:int_16) (y:int_16{ok (+) x y}) = x + y
+let g (x:uint_32) (y:uint_32{ok ( * ) y y /\ ok (+) x (y * y)}) = x + y * y
+let h (x:Prims.nat) (y:Prims.nat): nat = u x + u y
+let i (x:Prims.nat) (y:Prims.nat) = x + y
+let j (x:Prims.int) (y:Prims.nat) = x - y
+let k (x:Prims.int) (y:Prims.int) = x * y
+http://www.apache.org/licenses/LICENSE-2.0
+
+FStar.LexicographicOrdering
+
+
+lex to prove termination for the ackermann function
+
+
+
+
+
+
+
+noeq
+type lex_t (#a:Type) (#b:a -> Type) (r_a:relation a) (r_b:(x:a -> relation (b x)))
+ : (x:a & b x) -> (x:a & b x) -> Type =
+ | Left_lex:
+ x1:a -> x2:a ->
+ y1:b x1 -> y2:b x2 ->
+ r_a x1 x2 ->
+ lex_t r_a r_b (| x1, y1 |) (| x2, y2 |)
+ | Right_lex:
+ x:a ->
+ y1:b x -> y2:b x ->
+ r_b x y1 y2 ->
+ lex_t r_a r_b (| x, y1 |) (| x, y2 |)
+
+r_a and r_b,
+their lexicographic ordering is also well-foundedval lex_t_wf (#a:Type) (#b:a -> Type)
+ (#r_a:relation a)
+ (#r_b:(x:a -> relation (b x)))
+ (wf_a:well_founded r_a)
+ (wf_b:(x:a -> well_founded (r_b x)))
+ : well_founded (lex_t r_a r_b)
+
+unfold
+let lex_aux (#a:Type) (#b:a -> Type)
+ (r_a:relation a)
+ (r_b:(x:a -> relation (b x)))
+ : relation (x:a & b x)
+ = fun (| x1, y1 |) (| x2, y2 |) ->
+ (squash (r_a x1 x2)) \/
+ (x1 == x2 /\ squash ((r_b x1) y1 y2))
+
+val lex_to_lex_t (#a:Type) (#b:a -> Type)
+ (r_a:relation a)
+ (r_b:(x:a -> relation (b x)))
+ (t1 t2:(x:a & b x))
+ (p:lex_aux r_a r_b t1 t2)
+ : squash (lex_t r_a r_b t1 t2)
+
+let lex_wf (#a:Type) (#b:a -> Type)
+ (#r_a:relation a)
+ (#r_b:(x:a -> relation (b x)))
+ (wf_a:well_founded r_a)
+ (wf_b:(x:a -> well_founded (r_b x)))
+ : Lemma (is_well_founded (lex_aux r_a r_b))
+ = subrelation_squash_wf (lex_to_lex_t r_a r_b) (lex_t_wf wf_a wf_b)
+
+unfold
+let lex (#a:Type) (#b:a -> Type)
+ (#r_a:relation a)
+ (#r_b:(x:a -> relation (b x)))
+ (wf_a:well_founded r_a)
+ (wf_b:(x:a -> well_founded (r_b x)))
+ : well_founded_relation (x:a & b x)
+ = lex_wf wf_a wf_b;
+ lex_aux r_a r_b
+
+let tuple_to_dep_tuple (#a #b:Type) (x:a & b) : dtuple2 a (fun _ -> b) =
+ (| fst x, snd x |)
+
+let lex_t_non_dep (#a #b:Type) (r_a:relation a) (r_b:relation b)
+ : relation (a & b)
+ = fun x y ->
+ lex_t r_a (fun _ -> r_b) (tuple_to_dep_tuple x) (tuple_to_dep_tuple y)
val lex_t_non_dep_wf (#a #b:Type) (#r_a:relation a) (#r_b:relation b)
+ (wf_a:well_founded r_a)
+ (wf_b:well_founded r_b)
+ : well_founded (lex_t_non_dep r_a r_b)
+
+noeq
+type sym (#a:Type) (#b:Type) (r_a:relation a) (r_b:relation b)
+ : (a & b) -> (a & b) -> Type =
+ | Left_sym:
+ x1:a -> x2:a ->
+ y:b ->
+ r_a x1 x2 ->
+ sym r_a r_b (x1, y) (x2, y)
+ | Right_sym:
+ x:a ->
+ y1:b -> y2:b ->
+ r_b y1 y2 ->
+ sym r_a r_b (x, y1) (x, y2)
+
+let sym_sub_lex (#a #b:Type) (#r_a:relation a) (#r_b:relation b)
+ (t1 t2:a & b)
+ (p:sym r_a r_b t1 t2)
+ : lex_t_non_dep r_a r_b t1 t2
+ = match p with
+ | Left_sym x1 x2 y p ->
+ Left_lex #a #(fun _ -> b) #r_a #(fun _ -> r_b) x1 x2 y y p
+ | Right_sym x y1 y2 p ->
+ Right_lex #a #(fun _ -> b) #r_a #(fun _ -> r_b) x y1 y2 p
+
+let sym_wf (#a #b:Type)
+ (#r_a:relation a)
+ (#r_b:relation b)
+ (wf_a:well_founded r_a)
+ (wf_b:well_founded r_b)
+ : well_founded (sym r_a r_b)
+ = subrelation_wf sym_sub_lex (lex_t_non_dep_wf wf_a wf_b)
module FStar.List.Pure.Base
- Functions on list with a pure specification val map2:#a1:Type -> #a2:Type -> #b:Type -> f:Unidentified product: [a1] Unidentified product: [a2] b -> l1:list a1 -> l2:list a2 -> (Pure (list b) ((requires (==(length l1, length l2)))) ((ensures ((fun _ -> True)))) (decreases l1))val map3:#a1:Type -> #a2:Type -> #a3:Type -> #b:Type -> f:Unidentified product: [a1] Unidentified product: [a2] Unidentified product: [a3] b -> l1:list a1 -> l2:list a2 -> l3:list a3 -> (Pure (list b) ((requires (let n = length l1 in (/\(==(n, length l2), ==(n, length l3)))))) ((ensures ((fun _ -> True)))) (decreases l1))val zip:#a1:Type -> #a2:Type -> l1:list a1 -> l2:list a2 -> (Pure (list (*(a1, a2))) ((requires (let n = length l1 in ==(n, length l2)))) ((ensures ((fun _ -> True)))))val zip3:#a1:Type -> #a2:Type -> #a3:Type -> l1:list a1 -> l2:list a2 -> l3:list a3 -> (Pure (list (*(*(a1, a2), a3))) ((requires (let n = length l1 in /\(==(n, length l2), ==(n, length l3))))) ((ensures ((fun _ -> True)))))
+FStar.List.Pure.Base
+
+
+
+map2
+map2 takes a pair of list of the same length x1; ...; xn y1; ... ; yn
+and return the list f x1 y1; ... ; f xn ynval map2 (#a1 #a2 #b: Type)
+ (f: a1 -> a2 -> b)
+ (l1:list a1)
+ (l2:list a2)
+ : Pure (list b)
+ (requires (length l1 == length l2))
+ (ensures (fun _ -> True))
+ (decreases l1)
+let rec map2 #a1 #a2 #b f l1 l2 =
+ match l1, l2 with
+ | [], [] -> []
+ | x1::xs1, x2::xs2 -> f x1 x2 :: map2 f xs1 xs2
+map3
+map3 takes three lists of the same length x1; ...; xn
+y1; ... ; yn z1; ... ; zn and return the list
+f x1 y1 z1; ... ; f xn yn znval map3 (#a1 #a2 #a3 #b: Type)
+ (f: a1 -> a2 -> a3 -> b)
+ (l1:list a1)
+ (l2:list a2)
+ (l3:list a3)
+ : Pure (list b)
+ (requires (let n = length l1 in
+ (n == length l2 /\
+ n == length l3)))
+ (ensures (fun _ -> True))
+ (decreases l1)
+let rec map3 #a1 #a2 #a3 #b f l1 l2 l3 =
+ match l1, l2, l3 with
+ | [], [], [] -> []
+ | x1::xs1, x2::xs2, x3::xs3 -> f x1 x2 x3 :: map3 f xs1 xs2 xs3
+zip
+zip takes a pair of list of the same length and returns
+the list of index-wise pairsval zip (#a1 #a2:Type) (l1:list a1) (l2:list a2)
+ : Pure (list (a1 * a2))
+ (requires (let n = length l1 in n == length l2))
+ (ensures (fun _ -> True))
+let zip #a1 #a2 l1 l2 = map2 (fun x y -> x, y) l1 l2
+zip3
+zip3 takes a 3-tuple of list of the same length and returns
+the list of index-wise 3-tuplesval zip3 (#a1 #a2 #a3:Type) (l1:list a1) (l2:list a2) (l3:list a3)
+ : Pure (list (a1 * a2 * a3))
+ (requires (let n = length l1 in n == length l2 /\ n == length l3))
+ (ensures (fun _ -> True))
+let zip3 #a1 #a2 #a3 l1 l2 l3 = map3 (fun x y z -> x,y,z) l1 l2 l3
module FStar.List.Pure.Properties
- Properties of splitAt let ((lemma_splitAt_append (#a:Type) (n:nat) (l:list a)):(Lemma ((requires <=(n, length l))) ((ensures (let (l1, l2) = splitAt n l in /\(==(append l1 l2, l), =(length l1, n))))))):match n with 0 -> () | _ -> match l with [] -> () | (Prims.Cons x xs) -> lemma_splitAt_append (-(n, 1)) xslet ((lemma_append_splitAt (#t:Type) (l1:list t) (l2:list t)):(Lemma ((ensures (==(splitAt (length l1) (append l1 l2), ((FStar.Pervasives.Native.Mktuple2 l1 l2)))))))):match l1 with [] -> () | _ -> lemma_append_splitAt (tl l1) l2let ((lemma_splitAt (#t:Type) (l:list t) (l1:list t) (l2:list t) (n:n:nat:{<=(n, length l)})):(Lemma (<==>(==(splitAt n l, ((FStar.Pervasives.Native.Mktuple2 l1 l2))), /\(==(l, @(l1, l2)), =(length l1, n)))))):lemma_splitAt_append n l; lemma_append_splitAt l1 l2let ((lemma_splitAt_index_hd (#t:Type) (n:nat) (l:list t)):(Lemma ((requires (<(n, length l)))) ((ensures (let (l1, l2) = splitAt n l in splitAt_length n l; /\(>(length l2, 0), ==(hd l2, index l n))))))):let (Prims.Cons x xs) = l in match n with 0 -> () | _ -> lemma_splitAt_index_hd (-(n, 1)) (tl l)let ((lemma_splitAt_shorten_left (#t:Type) (l1:list t) (l2:list t) (i:i:nat:{/\(<=(i, length l1), <=(i, length l2))}) (j:j:nat:{<=(j, i)})):(Lemma ((requires (==(fst (splitAt i l1), fst (splitAt i l2))))) ((ensures (==(fst (splitAt j l1), fst (splitAt j l2))))))):match j with 0 -> () | _ -> lemma_splitAt_shorten_left (tl l1) (tl l2) (-(i, 1)) (-(j, 1))let ((lemma_splitAt_reindex_left (#t:Type) (i:nat) (l:list t) (j:nat)):(Lemma ((requires /\(<=(i, length l), <(j, i)))) ((ensures (let (left, right) = splitAt i l in splitAt_length i l; /\(<(j, length left), ==(index left j, index l j))))))):match (FStar.Pervasives.Native.Mktuple2 i j) with (1, _)|
- (_, 0) -> () | _ -> lemma_splitAt_reindex_left (-(i, 1)) (tl l) (-(j, 1))let ((lemma_splitAt_reindex_right (#t:Type) (i:nat) (l:list t) (j:nat)):(Lemma ((requires /\(<=(i, length l), <(+(j, i), length l)))) ((ensures (let (left, right) = splitAt i l in splitAt_length i l; /\(<(j, length right), ==(index right j, index l (+(j, i))))))))):match i with 0 -> () | _ -> lemma_splitAt_reindex_right (-(i, 1)) (tl l) j Properties of split3 let ((lemma_split3_append (#t:Type) (l:list t) (n:n:nat:{<(n, length l)})):(Lemma ((requires True)) ((ensures (let (a, b, c) = split3 l n in ==(l, append a ((Prims.Cons b c)))))))):lemma_splitAt_append n llet ((lemma_split3_index (#t:Type) (l:list t) (n:n:nat:{<(n, length l)})):(Lemma ((requires True)) ((ensures (let (a, b, c) = split3 l n in ==(b, index l n)))))):lemma_splitAt_index_hd n llet ((lemma_split3_length (#t:Type) (l:list t) (n:n:nat:{<(n, length l)})):(Lemma ((requires True)) ((ensures (let (a, b, c) = split3 l n in /\(=(length a, n), =(length c, -(-(length l, n), 1)))))))):splitAt_length n llet ((lemma_split3_on_same_leftprefix (#t:Type) (l1:list t) (l2:list t) (n:n:nat:{/\(<(n, length l1), <(n, length l2))})):(Lemma ((requires (==(fst (splitAt (+(n, 1)) l1), fst (splitAt (+(n, 1)) l2))))) ((ensures (let (a1, b1, c1) = split3 l1 n in let (a2, b2, c2) = split3 l2 n in /\(==(a1, a2), ==(b1, b2))))))):let (a1, b1, c1) = split3 l1 n in let (a2, b2, c2) = split3 l2 n in lemma_split3_append l1 n; lemma_split3_append l2 n; lemma_split3_length l1 n; lemma_split3_length l2 n; append_l_cons b1 c1 a1; append_l_cons b2 c2 a2; let (x1, y1) = splitAt (+(n, 1)) l1 in let (x2, y2) = splitAt (+(n, 1)) l2 in lemma_splitAt_append (+(n, 1)) l1; lemma_splitAt_append (+(n, 1)) l2; splitAt_length (+(n, 1)) l1; splitAt_length (+(n, 1)) l2; append_length_inv_head x1 y1 (append a1 (Prims.Cons b1 (Prims.Nil ))) c1; append_length_inv_head x2 y2 (append a2 (Prims.Cons b2 (Prims.Nil ))) c2; append_length_inv_tail a1 (Prims.Cons b1 (Prims.Nil )) a2 (Prims.Cons b2 (Prims.Nil )); ()let ((lemma_split3_unsnoc (#t:Type) (l:list t) (n:n:nat:{<(n, length l)})):(Lemma ((requires (<>(n, -(length l, 1))))) ((ensures (let (a, b, c) = split3 l n in lemma_split3_length l n; /\(>(length c, 0), (let (xs, x) = unsnoc l in let (ys, y) = unsnoc c in ==(append a ((Prims.Cons b ys)), xs)))))))):match n with 0 -> () | _ -> lemma_split3_unsnoc (tl l) (-(n, 1))let ((lemma_unsnoc_split3 (#t:Type) (l:list t) (i:i:nat:{<(i, length l)})):(Lemma ((requires (<>(i, -(length l, 1))))) ((ensures (let (xs, x) = unsnoc l in /\(<(i, length xs), (let (a0, b0, c0) = split3 l i in let (a1, b1, c1) = split3 xs i in /\(==(a0, a1), ==(b0, b1))))))))):let (xs, x) = unsnoc l in lemma_unsnoc_length l; let (a0, b0, c0) = split3 l i in let (a1, b1, c1) = split3 xs i in splitAt_length_total xs; lemma_splitAt_shorten_left xs l (length xs) (+(i, 1)); lemma_split3_on_same_leftprefix l xs ilet ((lemma_split3_r_hd (#t:Type) (l:list t) (i:i:nat:{<(i, length l)})):(Lemma ((ensures (let (a, b, c) = split3 l i in lemma_split3_length l i; ==>(>(length c, 0), /\(<(+(i, 1), length l), ==(hd c, index l (+(i, 1)))))))))):match i with 0 -> () | _ -> lemma_split3_r_hd (tl l) (-(i, 1))
+FStar.List.Pure.Properties
+
+
+let rec splitAt_length
+ (#a:Type)
+ (n:nat)
+ (l:list a)
+ : Lemma (requires True)
+ (ensures begin
+ let l_1, l_2 = splitAt n l in
+ if length l < n then
+ length l_1 == length l /\ length l_2 == 0
+ else
+ length l_1 == n /\ length l_2 = length l - n
+ end)
+ (decreases n)
+=
+ if n = 0 then ()
+ else
+ match l with
+ | [] -> ()
+ | _::xs -> splitAt_length (n-1) xs
let rec splitAt_assoc
+ (#a:Type)
+ (n1 n2:nat)
+ (l:list a)
+ : Lemma (requires True)
+ (ensures begin
+ let l1, l2 = splitAt n1 l in
+ let l2, l3 = splitAt n2 l2 in
+ let l1', l2' = splitAt (n1+n2) l in
+ l1' == l1 @ l2 /\ l2' == l3
+ end)
+ (decreases n1)
+=
+ if n1 = 0 then ()
+ else
+ match l with
+ | [] -> ()
+ | x :: xs -> splitAt_assoc (n1-1) n2 xs
let rec splitAt_length_total (#a:Type) (l:list a)
+ : Lemma (requires True) (ensures (splitAt (length l) l == (l, []))) (decreases l)
+=
+ match l with
+ | [] -> ()
+ | x :: xs -> splitAt_length_total xs
+lemma_splitAt_append
+append the two lists produced using a splitAt, then we
+get back the original listlet rec lemma_splitAt_append (#a:Type) (n:nat) (l:list a) :
+ Lemma
+ (requires n <= length l)
+ (ensures (let l1, l2 = splitAt n l in
+ append l1 l2 == l /\ length l1 = n)) =
+ match n with
+ | 0 -> ()
+ | _ ->
+ match l with
+ | [] -> ()
+ | x :: xs -> lemma_splitAt_append (n-1) xs
+lemma_append_splitAt
+splitAt the point at which two lists have been appended, then we
+get back the original lists.let rec lemma_append_splitAt (#t:Type) (l1 l2:list t) :
+ Lemma
+ (ensures (splitAt (length l1) (append l1 l2) == (l1, l2))) =
+ match l1 with
+ | [] -> ()
+ | _ -> lemma_append_splitAt (tl l1) l2
+lemma_splitAt
+splitAt in terms of more standard list conceptslet lemma_splitAt (#t: Type) (l l1 l2:list t) (n:nat{n <= length l}) :
+ Lemma (splitAt n l == (l1, l2) <==> l == l1 @ l2 /\ length l1 = n) =
+ lemma_splitAt_append n l;
+ lemma_append_splitAt l1 l2
+lemma_splitAt_index_hd
+hd of the second list returned via splitAt is the nth element of
+the original listlet rec lemma_splitAt_index_hd (#t:Type) (n:nat) (l:list t) :
+ Lemma
+ (requires (n < length l))
+ (ensures (let l1, l2 = splitAt n l in
+ splitAt_length n l;
+ length l2 > 0 /\ hd l2 == index l n)) =
+ let x :: xs = l in
+ match n with
+ | 0 -> ()
+ | _ -> lemma_splitAt_index_hd (n - 1) (tl l)
+lemma_splitAt_shorten_left
+let rec lemma_splitAt_shorten_left
+ (#t:Type) (l1 l2:list t) (i:nat{i <= length l1 /\ i <= length l2}) (j:nat{j <= i}) :
+ Lemma
+ (requires (fst (splitAt i l1) == fst (splitAt i l2)))
+ (ensures (fst (splitAt j l1) == fst (splitAt j l2))) =
+ match j with
+ | 0 -> ()
+ | _ ->
+ lemma_splitAt_shorten_left (tl l1) (tl l2) (i-1) (j-1)
+lemma_splitAt_reindex_left
+index on the left-part of a splitAt is same as doing it on
+the original listlet rec lemma_splitAt_reindex_left (#t:Type) (i:nat) (l:list t) (j:nat) :
+ Lemma
+ (requires i <= length l /\ j < i)
+ (ensures (
+ let left, right = splitAt i l in
+ splitAt_length i l;
+ j < length left /\ index left j == index l j)) =
+ match i, j with
+ | 1, _ | _, 0 -> ()
+ | _ -> lemma_splitAt_reindex_left (i - 1) (tl l) (j - 1)
+lemma_splitAt_reindex_right
+index on the right-part of a splitAt is same as doing it on
+the original list, but shiftedlet rec lemma_splitAt_reindex_right (#t:Type) (i:nat) (l:list t) (j:nat) :
+ Lemma
+ (requires i <= length l /\ j + i < length l)
+ (ensures (
+ let left, right = splitAt i l in
+ splitAt_length i l;
+ j < length right /\ index right j == index l (j + i))) =
+ match i with
+ | 0 -> ()
+ | _ -> lemma_splitAt_reindex_right (i - 1) (tl l) j
+lemma_split3_append
+split3 can be joined together via an
+append and a conslet lemma_split3_append (#t:Type) (l:list t) (n:nat{n < length l}) :
+ Lemma
+ (requires True)
+ (ensures (
+ let a, b, c = split3 l n in
+ l == append a (b :: c))) =
+ lemma_splitAt_append n l
+lemma_split3_index
+split3 is the nth indexed elementlet lemma_split3_index (#t:Type) (l:list t) (n:nat{n < length l}) :
+ Lemma
+ (requires True)
+ (ensures (
+ let a, b, c = split3 l n in
+ b == index l n)) =
+ lemma_splitAt_index_hd n l
+lemma_split3_length
+split3 are as expected.let lemma_split3_length (#t:Type) (l:list t) (n:nat{n < length l}) :
+ Lemma
+ (requires True)
+ (ensures (
+ let a, b, c = split3 l n in
+ length a = n /\ length c = length l - n - 1)) =
+ splitAt_length n l
+lemma_split3_on_same_leftprefix
+split3 on lists with the same left prefix, we get the same
+element and left prefix.let lemma_split3_on_same_leftprefix
+ (#t:Type) (l1 l2:list t) (n:nat{n < length l1 /\ n < length l2}) :
+ Lemma
+ (requires (fst (splitAt (n+1) l1) == fst (splitAt (n+1) l2)))
+ (ensures (let a1, b1, c1 = split3 l1 n in
+ let a2, b2, c2 = split3 l2 n in
+ a1 == a2 /\ b1 == b2)) =
+ let a1, b1, c1 = split3 l1 n in
+ let a2, b2, c2 = split3 l2 n in
+ lemma_split3_append l1 n;
+ lemma_split3_append l2 n;
+ lemma_split3_length l1 n;
+ lemma_split3_length l2 n;
+ append_l_cons b1 c1 a1;
+ append_l_cons b2 c2 a2;
b1) @ c1 == l1);
+assert ((a2 @ b2) @ c2 == l2);
+assert (x1 @ y1 == (a1 @ b1) @ c1);
+assert (x2 @ y2 == (a2 @ b2) @ c2);
+assert (a1 @ b1 == a2 @ b2);
+assert (a1 == a2 /\ b1 == b2);let x1, y1 = splitAt (n+1) l1 in
+let x2, y2 = splitAt (n+1) l2 in
+lemma_splitAt_append (n+1) l1;
+lemma_splitAt_append (n+1) l2;
+splitAt_length (n+1) l1;
+splitAt_length (n+1) l2;
+append_length_inv_head x1 y1 (append a1 [b1]) c1;
+append_length_inv_head x2 y2 (append a2 [b2]) c2;
+append_length_inv_tail a1 [b1] a2 [b2];
+()
+lemma_split3_unsnoc
+unsnoc on a list, then the left part is the same
+as an append+cons on the list after split3.let rec lemma_split3_unsnoc (#t:Type) (l:list t) (n:nat{n < length l}) :
+ Lemma
+ (requires (n <> length l - 1))
+ (ensures (
+ let a, b, c = split3 l n in
+ lemma_split3_length l n;
+ length c > 0 /\ (
+ let xs, x = unsnoc l in
+ let ys, y = unsnoc c in
+ append a (b :: ys) == xs))) =
+ match n with
+ | 0 -> ()
+ | _ -> lemma_split3_unsnoc (tl l) (n-1)
+lemma_unsnoc_split3
+unsnoc and split3 in either order leads to the same left
+part, and element.let lemma_unsnoc_split3 (#t:Type) (l:list t) (i:nat{i < length l}) :
+ Lemma
+ (requires (i <> length l - 1))
+ (ensures (
+ let xs, x = unsnoc l in
+ i < length xs /\ (
+ let a0, b0, c0 = split3 l i in
+ let a1, b1, c1 = split3 xs i in
+ a0 == a1 /\ b0 == b1))) =
+ let xs, x = unsnoc l in
+ lemma_unsnoc_length l;
+ let a0, b0, c0 = split3 l i in
+ let a1, b1, c1 = split3 xs i in
+ splitAt_length_total xs;
lemma_splitAt_shorten_left xs l (length xs) (i+1);
+lemma_split3_on_same_leftprefix l xs i
+lemma_split3_r_hd
+split3 can be indexed from original list.let rec lemma_split3_r_hd (#t:Type) (l:list t) (i:nat{i < length l}) :
+ Lemma
+ (ensures (let a, b, c = split3 l i in
+ lemma_split3_length l i;
+ length c > 0 ==> i + 1 < length l /\ hd c == index l (i + 1))) =
+ match i with
+ | 0 -> ()
+ | _ -> lemma_split3_r_hd (tl l) (i - 1)
module FStar.List.Pure
-
+FStar.List.Pure
+
+
+
diff --git a/docs/FStar.List.Tot.Base.html b/docs/FStar.List.Tot.Base.html
index 956f9bf..f3193ca 100644
--- a/docs/FStar.List.Tot.Base.html
+++ b/docs/FStar.List.Tot.Base.html
@@ -1,180 +1,595 @@
-
-
+
+
-
-
- module FStar.List.Tot.Base
-
-Base operationsval isEmpty:Unidentified product: [list 'a] (Tot bool)val hd:Unidentified product: [l:l:list 'a:{Cons? l}] (Tot 'a)val tail:Unidentified product: [l:l:list 'a:{Cons? l}] (Tot (list 'a))val tl:Unidentified product: [l:l:list 'a:{Cons? l}] (Tot (list 'a))val last:Unidentified product: [l:l:list 'a:{Cons? l}] (Tot 'a)val init:Unidentified product: [l:l:list 'a:{Cons? l}] (Tot (list 'a))val length:Unidentified product: [list 'a] (Tot nat)val nth:Unidentified product: [list 'a] Unidentified product: [nat] (Tot (option 'a))val index:Unidentified product: [#a:Type] Unidentified product: [l:list a] Unidentified product: [i:i:nat:{<(i, length l)}] (Tot a)val count:Unidentified product: [#a:eqtype] Unidentified product: [a] Unidentified product: [list a] (Tot nat)val rev_acc:Unidentified product: [list 'a] Unidentified product: [list 'a] (Tot (list 'a))val rev:Unidentified product: [list 'a] (Tot (list 'a))val append:Unidentified product: [list 'a] Unidentified product: [list 'a] (Tot (list 'a))let (op_At x y):append x yval snoc:Unidentified product: [(*(list 'a, 'a))] (Tot (list 'a))
-Note: We use an uncurried [snoc (l, x)] instead of the curried
-[snoc l x]. This is intentional. If [snoc] takes a pair instead
+
+FStar.List.Tot.Base
+
+isEmpty
+isEmpty l returns true if and only if l is emptyval isEmpty: list 'a -> Tot bool
+let isEmpty l = match l with
+ | [] -> true
+ | _ -> false
+hd
+hd l returns the first element of l. Requires l to be
+nonempty, at type-checking time. Named as in: OCaml, F#, Coqval hd: l:list 'a{Cons? l} -> Tot 'a
+let hd = function
+ | hd::_ -> hd
+tail
+tail l returns l without its first element. Requires, at
+type-checking time, that l be nonempty. Similar to: tl in OCaml, F#, Coqval tail: l:list 'a {Cons? l} -> Tot (list 'a)
+let tail = function
+ | _::tl -> tl
+tl
+tl l returns l without its first element. Requires, at
+type-checking time, that l be nonempty. Named as in: OCaml, F#, Coqval tl: l:list 'a {Cons? l} -> Tot (list 'a)
+let tl = tail
+last
+last l returns the last element of l. Requires, at
+type-checking time, that l be nonempty. Named as in: Haskellval last: l:list 'a {Cons? l} -> Tot 'a
+let rec last = function
+ | [hd] -> hd
+ | _::tl -> last tl
+init
+init l returns l without its last element. Requires, at
+type-checking time, that l be nonempty. Named as in: Haskellval init: l:list 'a {Cons? l} -> Tot (list 'a)
+let rec init = function
+ | [_] -> []
+ | hd::tl -> hd::(init tl)
+length
+length l returns the total number of elements in l. Named as
+in: OCaml, F#, Coqval length: list 'a -> Tot nat
+let rec length = function
+ | [] -> 0
+ | _::tl -> 1 + length tl
+nth
+nth l n returns the n-th element in list l (with the first
+element being the 0-th) if l is long enough, or None
+otherwise. Named as in: OCaml, F#, Coqval nth: list 'a -> nat -> Tot (option 'a)
+let rec nth l n = match l with
+ | [] -> None
+ | hd::tl -> if n = 0 then Some hd else nth tl (n - 1)
+index
+index l n returns the n-th element in list l (with the first
+element being the 0-th). Requires, at type-checking time, that l be
+of length at least n+1.val index: #a:Type -> l:list a -> i:nat{i < length l} -> Tot a
+let rec index #a (l: list a) (i:nat{i < length l}): Tot a =
+ if i = 0 then
+ hd l
+ else
+ index (tl l) (i - 1)
+count
+count x l returns the number of occurrences of x in
+l. Requires, at type-checking time, the type of a to have equality
+defined. Similar to: List.count_occ in Coq.val count: #a:eqtype -> a -> list a -> Tot nat
+let rec count #a x = function
+ | [] -> 0
+ | hd::tl -> if x=hd then 1 + count x tl else count x tl
+rev_acc
+rev_acc l1 l2 appends the elements of l1 to the beginning of
+l2, in reverse order. It is equivalent to append (rev l1) l2, but
+is tail-recursive. Similar to: List.rev_append in OCaml, Coq.val rev_acc: list 'a -> list 'a -> Tot (list 'a)
+let rec rev_acc l acc = match l with
+ | [] -> acc
+ | hd::tl -> rev_acc tl (hd::acc)
+rev
+rev l returns the list l in reverse order. Named as in: OCaml,
+F#, Coq.val rev: list 'a -> Tot (list 'a)
+let rev l = rev_acc l []
+append
+append l1 l2 appends the elements of l2 to the end of l1. Named as: OCaml, F#. Similar to: List.app in Coq.val append: list 'a -> list 'a -> Tot (list 'a)
+let rec append x y = match x with
+ | [] -> y
+ | a::tl -> a::append tl y
+op_At
+@@ for append, as in OCaml, F# .let op_At x y = append x y
+snoc
+snoc (l, x) adds x to the end of the list l.val snoc: (list 'a * 'a) -> Tot (list 'a)
+let snoc (l, x) = append l [x]
snoc (l, x) instead of the curried
+snoc l x. This is intentional. If snoc takes a pair instead
of 2 arguments, it allows for a better pattern on
-[lemma_unsnoc_snoc], which connects [snoc] and [unsnoc]. In
+lemma_unsnoc_snoc, which connects snoc and unsnoc. In
particular, if we had two arguments, then either the pattern would
either be too restrictive or would lead to over-triggering. More
context for this can be seen in the (collapsed and uncollapsed)
-comments at https://github.com/FStarLang/FStar/pull/1560 val flatten:Unidentified product: [list (list 'a)] (Tot (list 'a))val map:Unidentified product: [(Unidentified product: ['a] (Tot 'b))] Unidentified product: [list 'a] (Tot (list 'b))val mapi_init:Unidentified product: [(Unidentified product: [int] Unidentified product: ['a] (Tot 'b))] Unidentified product: [list 'a] Unidentified product: [int] (Tot (list 'b))val mapi:Unidentified product: [(Unidentified product: [int] Unidentified product: ['a] (Tot 'b))] Unidentified product: [list 'a] (Tot (list 'b))val concatMap:Unidentified product: [(Unidentified product: ['a] (Tot (list 'b)))] Unidentified product: [list 'a] (Tot (list 'b))val fold_left:Unidentified product: [(Unidentified product: ['a] Unidentified product: ['b] (Tot 'a))] Unidentified product: ['a] Unidentified product: [l:list 'b] (Tot 'a (decreases l))val fold_right:Unidentified product: [(Unidentified product: ['a] Unidentified product: ['b] (Tot 'b))] Unidentified product: [list 'a] Unidentified product: ['b] (Tot 'b)let ((fold_right_gtot (#a:Type) (#b:Type) (l:list a) (f:Unidentified product: [a] Unidentified product: [b] (GTot b)) (x:b)):(GTot b)):match l with [] -> x | (Prims.Cons hd tl) -> f hd (fold_right_gtot tl f x)f is a ghost function *val fold_left2:Unidentified product: [f:(Unidentified product: ['a] Unidentified product: ['b] Unidentified product: ['c] (Tot 'a))] Unidentified product: [accu:'a] Unidentified product: [l1:(list 'b)] Unidentified product: [l2:(list 'c)] (Pure 'a ((requires (==(length l1, length l2)))) ((ensures ((fun _ -> True)))) (decreases l1)) List searching *val mem:Unidentified product: [#a:eqtype] Unidentified product: [a] Unidentified product: [list a] (Tot bool) Propositional membership (as in Coq). Does not require decidable
-equality. let ((memP (#a:Type) (x:a) (l:list a)):(Tot Type0)):match l with [] -> False | (Prims.Cons y q) -> \/(==(x, y), memP x q)let contains:memval existsb:Unidentified product: [#a:Type] Unidentified product: [f:(Unidentified product: [a] (Tot bool))] Unidentified product: [list a] (Tot bool)val find:Unidentified product: [#a:Type] Unidentified product: [f:(Unidentified product: [a] (Tot bool))] Unidentified product: [list a] (Tot (option (x:a:{f x}))) Filtering elements of a list [l] through a Boolean pure total
-predicate [f] let ((mem_filter_spec (#a:Type) (f:(Unidentified product: [a] (Tot bool))) (m:list a) (u:option (x:unit:{hasEq a}))):(Tot Type0)):match u with None -> True | (Some z) -> forall x.{:pattern } ==>(mem x m, f x)val filter:Unidentified product: [#a:Type] Unidentified product: [f:(Unidentified product: [a] (Tot bool))] Unidentified product: [l:list a] (Tot (m:list a:{forall u.{:pattern } mem_filter_spec f m u}))val mem_filter:#a:eqtype -> f:(Unidentified product: [a] (Tot bool)) -> l:list a -> x:a -> (Lemma ((requires (mem #a x (filter f l)))) ((ensures (f x))))val mem_filter_forall:#a:eqtype -> f:(Unidentified product: [a] (Tot bool)) -> l:list a -> (Lemma ((requires True)) ((ensures (forall x.{:pattern } ==>(mem #a x (filter f l), f x)))) (Prims.Cons (SMTPat (filter f l)) (Prims.Nil )))val for_all:Unidentified product: [(Unidentified product: ['a] (Tot bool))] Unidentified product: [list 'a] (Tot bool)let ((for_all_mem (#a:eqtype) (f:(Unidentified product: [a] (Tot bool))) (l:list a)):(Lemma (<==>(for_all f l, (forall x.{:pattern } ==>(mem x l, f x)))))):match l with [] -> () | (Prims.Cons _ q) -> for_all_mem f qval collect:Unidentified product: [(Unidentified product: ['a] (Tot (list 'b)))] Unidentified product: [list 'a] (Tot (list 'b))val tryFind:Unidentified product: [(Unidentified product: ['a] (Tot bool))] Unidentified product: [list 'a] (Tot (option 'a))val tryPick:Unidentified product: [(Unidentified product: ['a] (Tot (option 'b)))] Unidentified product: [list 'a] (Tot (option 'b))val choose:Unidentified product: [(Unidentified product: ['a] (Tot (option 'b)))] Unidentified product: [list 'a] (Tot (list 'b))val partition:Unidentified product: [f:(Unidentified product: ['a] (Tot bool))] Unidentified product: [list 'a] (Tot (*(list 'a, list 'a)))val subset:Unidentified product: [#a:eqtype] Unidentified product: [list a] Unidentified product: [list a] (Tot bool)val noRepeats:Unidentified product: [#a:eqtype] Unidentified product: [list a] (Tot bool)val no_repeats_p:Unidentified product: [#a:Type] Unidentified product: [list a] (Tot prop) List of tuples *val assoc:Unidentified product: [#a:eqtype] Unidentified product: [#b:Type] Unidentified product: [a] Unidentified product: [list (*(a, b))] (Tot (option b))val split:Unidentified product: [list (*('a, 'b))] (Tot (*(list 'a, list 'b)))let unzip:splitval unzip3:Unidentified product: [list (*(*('a, 'b), 'c))] (Tot (*(*(list 'a, list 'b), list 'c))) Splitting a list at some index *let ((splitAt (#a:Type) (n:nat) (l:list a)):*(list a, list a)):if =(n, 0) then (FStar.Pervasives.Native.Mktuple2 (Prims.Nil ) l) else match l with [] -> (FStar.Pervasives.Native.Mktuple2 (Prims.Nil ) l) | (Prims.Cons x xs) -> let (l1, l2) = splitAt (-(n, 1)) xs in (FStar.Pervasives.Native.Mktuple2 (Prims.Cons x l1) l2)val unsnoc:Unidentified product: [#a:Type] Unidentified product: [l:l:list a:{>(length l, 0)}] (Tot (*(list a, a)))val split3:Unidentified product: [#a:Type] Unidentified product: [l:list a] Unidentified product: [i:i:nat:{<(i, length l)}] (Tot (*(*(list a, a), list a))) Sorting (implemented as quicksort) *val partition_length:Unidentified product: [f:(Unidentified product: ['a] (Tot bool))] Unidentified product: [l:list 'a] (Lemma ((requires True)) ((ensures (=(+(length (fst (partition f l)), length (snd (partition f l))), length l)))))val bool_of_compare:Unidentified product: [#a:Type] Unidentified product: [(Unidentified product: [a] Unidentified product: [a] (Tot int))] Unidentified product: [a] Unidentified product: [a] (Tot bool)val compare_of_bool:Unidentified product: [#a:eqtype] Unidentified product: [(Unidentified product: [a] Unidentified product: [a] (Tot bool))] Unidentified product: [a] Unidentified product: [a] (Tot int)rel y holds. Inspired from OCaml, where polymorphic comparison using both the [compare] function and the (>) infix operator are such that [compare x y] is positive if, and only if, x > y. Requires, at type-checking time, [rel] to be a pure total function.val sortWith:Unidentified product: [(Unidentified product: ['a] Unidentified product: ['a] (Tot int))] Unidentified product: [l:list 'a] (Tot (list 'a) (decreases (length l))) A l1 is a strict prefix of l2.
flatten l, where l is a list of lists, returns the list of the
+elements of the lists in l, preserving their order. Named as in:
+OCaml, Coq.
val flatten: list (list 'a) -> Tot (list 'a)
+let rec flatten l = match l with
+ | [] -> []
+ | hd::tl -> append hd (flatten tl)map f l applies f to each element of l and returns the list
+of results, in the order of the original elements in l. Requires, at
+type-checking time, f to be a pure total function. Named as in: OCaml, Coq, F#
val map: ('a -> Tot 'b) -> list 'a -> Tot (list 'b)
+let rec map f x = match x with
+ | [] -> []
+ | a::tl -> f a::map f tlmapi_init f n l applies, for each k, f (n+k) to the k-th
+element of l and returns the list of results, in the order of the
+original elements in l. Requires, at type-checking time, f to be a
+pure total function.
val mapi_init: (int -> 'a -> Tot 'b) -> list 'a -> int -> Tot (list 'b)
+let rec mapi_init f l i = match l with
+ | [] -> []
+ | hd::tl -> (f i hd)::(mapi_init f tl (i+1))mapi f l applies, for each k, f k to the k-th element of
+l and returns the list of results, in the order of the original
+elements in l. Requires, at type-checking time, f to be a pure
+total function. Named as in: OCaml
val mapi: (int -> 'a -> Tot 'b) -> list 'a -> Tot (list 'b)
+let mapi f l = mapi_init f l 0concatMap f l applies f to each element of l and returns the
+concatenation of the results, in the order of the original elements of
+l. This is equivalent to flatten (map f l). Requires, at
+type-checking time, f to be a pure total function.
val concatMap: ('a -> Tot (list 'b)) -> list 'a -> Tot (list 'b)
+let rec concatMap f = function
+ | [] -> []
+ | a::tl ->
+ let fa = f a in
+ let ftl = concatMap f tl in
+ append fa ftlfold_left f x y1; y2; ...; yn`` computes (f (... (f x y1) y2)
+... yn). Requires, at type-checking time, f to be a pure total
+function. Named as in: OCaml, Coq.
val fold_left: ('a -> 'b -> Tot 'a) -> 'a -> l:list 'b -> Tot 'a (decreases l)
+let rec fold_left f x l = match l with
+ | [] -> x
+ | hd::tl -> fold_left f (f x hd) tlfold_right f x1; x2; ...; xn y computes (f x1 (f x2 (... (f xn
+y)) ... )). Requires, at type-checking time, f to be a pure total
+function. Named as in: OCaml, Coq
val fold_right: ('a -> 'b -> Tot 'b) -> list 'a -> 'b -> Tot 'b
+let rec fold_right f l x = match l with
+ | [] -> x
+ | hd::tl -> f hd (fold_right f tl x)fold_right_gtot is just like fold_right, except f is
+a ghost function *
let rec fold_right_gtot (#a:Type) (#b:Type) (l:list a) (f:a -> b -> GTot b) (x:b)
+ : GTot b
+ = match l with
+ | [] -> x
+ | hd::tl -> f hd (fold_right_gtot tl f x)We define map in terms of fold, to share simple lemmas
+let map_gtot #a #b (f:a -> GTot b) (x:list a)
+ : GTot (list b)
+ = fold_right_gtot x (fun x tl -> f x :: tl) []fold_left2 f x y1; y2; ...; yn z1; z2; ...; zn`` computes (f
+(... (f x y1 z1) y2 z2) ... yn zn). Requires, at type-checking time,
+f to be a pure total function, and the lists `y1; y2; ...; yn` and
+`z1; z2; ...; zn` to have the same lengths. Named as in: OCaml
val fold_left2 : f:('a -> 'b -> 'c -> Tot 'a) -> accu:'a -> l1:(list 'b) -> l2:(list 'c) ->
+ Pure 'a (requires (length l1 == length l2)) (ensures (fun _ -> True)) (decreases l1)
+let rec fold_left2 f accu l1 l2 =
+ match (l1, l2) with
+ | ([], []) -> accu
+ | (a1::l1, a2::l2) -> fold_left2 f (f accu a1 a2) l1 l2Propositional membership (as in Coq). Does not require decidable +equality.
+memP x l holds if, and only if, x appears as an
+element of l. Similar to: List.In in Coq.
let rec memP (#a: Type) (x: a) (l: list a) : Tot Type0 =
+ match l with
+ | [] -> False
+ | y :: q -> x == y \/ memP x qList searching *
+mem x l returns true if, and only if, x appears as an
+element of l. Requires, at type-checking time, the type of elements
+of l to have decidable equality. Named as in: OCaml. See also:
+List.In in Coq, which is propositional.
val mem: #a:eqtype -> a -> list a -> Tot bool
+let rec mem #a x = function
+ | [] -> false
+ | hd::tl -> if hd = x then true else mem x tlcontains x l returns true if, and only if, x appears as an
+element of l. Requires, at type-checking time, the type of elements
+of l to have decidable equality. It is equivalent to: `mem x
+l]. TODO: should we rather swap the order of arguments?
let contains = memexistsb f l returns true if, and only if, there exists some
+element x in l such that f x holds.
val existsb: #a:Type
+ -> f:(a -> Tot bool)
+ -> list a
+ -> Tot bool
+let rec existsb #a f l = match l with
+ | [] -> false
+ | hd::tl -> if f hd then true else existsb f tlfind f l returns Some x for some element x appearing in l
+such that f x holds, or None only if no such x exists.
val find: #a:Type
+ -> f:(a -> Tot bool)
+ -> list a
+ -> Tot (option (x:a{f x}))
+let rec find #a f l = match l with
+ | [] -> None #(x:a{f x}) //These type annotations are only present because it makes bootstrapping go much faster
+ | hd::tl -> if f hd then Some #(x:a{f x}) hd else find f tlFiltering elements of a list l through a Boolean pure total
+predicate f
filter f l returns l with all elements x such that f x
+does not hold removed. Requires, at type-checking time, f to be a
+pure total function. Named as in: OCaml, Coq
val filter : #a: Type -> f:(a -> Tot bool) -> l: list a -> Tot (m:list a{forall x. memP x m ==> f x})
+let rec filter #a f = function
+ | [] -> []
+ | hd::tl -> if f hd then hd::filter f tl else filter f tlPostcondition on filter f l: for any element x of filter f l,
+f x holds. Requires, at type-checking time, f to be a pure total
+function.
val mem_filter (#a:Type) (f: (a -> Tot bool)) (l: list a) (x: a) : Lemma
+ (requires (memP x (filter f l)))
+ (ensures (f x))
+let mem_filter f l x = ()Postcondition on filter f l: stated with forall: for any element
+x of filter f l, f x holds. Requires, at type-checking time, f
+to be a pure total function.
val mem_filter_forall (#a:Type) (f: (a -> Tot bool)) (l: list a) : Lemma
+ (requires True)
+ (ensures (forall x . memP x (filter f l) ==> f x))
+ [SMTPat (filter f l)]
+let mem_filter_forall f l = FStar.Classical.ghost_lemma (mem_filter f l)for_all f l returns true if, and only if, for all elements x
+appearing in l, f x holds. Requires, at type-checking time, f to
+be a pure total function. Named as in: OCaml. Similar to: List.forallb
+in Coq
val for_all: ('a -> Tot bool) -> list 'a -> Tot bool
+let rec for_all f l = match l with
+ | [] -> true
+ | hd::tl -> if f hd then for_all f tl else falseSpecification for for_all f l vs. mem
let rec for_all_mem
+ (#a: Type)
+ (f: (a -> Tot bool))
+ (l: list a)
+: Lemma
+ (for_all f l <==> (forall x . memP x l ==> f x))
+= match l with
+ | [] -> ()
+ | _ :: q -> for_all_mem f qcollect f l applies f to each element of l and returns the
+concatenation of the results, in the order of the original elements of
+l. It is equivalent to flatten (map f l). Requires, at
+type-checking time, f to be a pure total function. TODO: what is
+the difference with concatMap?
val collect: ('a -> Tot (list 'b)) -> list 'a -> Tot (list 'b)
+let rec collect f l = match l with
+ | [] -> []
+ | hd::tl -> append (f hd) (collect f tl)tryFind f l returns Some x for some element x appearing in
+l such that f x holds, or None only if no such x
+exists. Requires, at type-checking time, f to be a pure total
+function. Contrary to find, tryFind provides no postcondition on
+its result.
val tryFind: ('a -> Tot bool) -> list 'a -> Tot (option 'a)
+let rec tryFind p l = match l with
+ | [] -> None
+ | hd::tl -> if p hd then Some hd else tryFind p tltryPick f l returns y for some element x appearing in l
+such that f x = Some y for some y, or None only if f x = None
+for all elements x of l. Requires, at type-checking time, f to
+be a pure total function.
val tryPick: ('a -> Tot (option 'b)) -> list 'a -> Tot (option 'b)
+let rec tryPick f l = match l with
+ | [] -> None
+ | hd::tl ->
+ match f hd with
+ | Some x -> Some x
+ | None -> tryPick f tlchoose f l returns the list of y for all elements x
+appearing in l such that f x = Some y for some y. Requires, at
+type-checking time, f to be a pure total function.
val choose: ('a -> Tot (option 'b)) -> list 'a -> Tot (list 'b)
+let rec choose f l = match l with
+ | [] -> []
+ | hd::tl ->
+ match f hd with
+ | Some x -> x::(choose f tl)
+ | None -> choose f tlpartition f l returns the pair of lists (l1, l2) where all
+elements x of l are in l1 if f x holds, and in l2
+otherwise. Both l1 and l2 retain the original order of
+l. Requires, at type-checking time, f to be a pure total
+function.
val partition: f:('a -> Tot bool) -> list 'a -> Tot (list 'a * list 'a)
+let rec partition f = function
+ | [] -> [], []
+ | hd::tl ->
+ let l1, l2 = partition f tl in
+ if f hd
+ then hd::l1, l2
+ else l1, hd::l2subset la lb is true if and only if all the elements from la
+are also in lb. Requires, at type-checking time, the type of
+elements of la and lb to have decidable equality.
val subset: #a:eqtype -> list a -> list a -> Tot bool
+let rec subset #a la lb =
+ match la with
+ | [] -> true
+ | h :: tl -> mem h lb && subset tl lbnoRepeats l returns true if, and only if, no element of l
+appears in l more than once. Requires, at type-checking time, the
+type of elements of la and lb to have decidable equality.
val noRepeats : #a:eqtype -> list a -> Tot bool
+let rec noRepeats #a la =
+ match la with
+ | [] -> true
+ | h :: tl -> not(mem h tl) && noRepeats tlno_repeats_p l valid if, and only if, no element of l
+appears in l more than once.
val no_repeats_p : #a:Type -> list a -> Tot prop
+let rec no_repeats_p #a la =
+ match la with
+ | [] -> True
+ | h :: tl -> ~(memP h tl) /\ no_repeats_p tlList of tuples *
+assoc x l returns Some y where (x, y) is the first element
+of l whose first element is x, or None only if no such element
+exists. Requires, at type-checking time, the type of x to have
+decidable equality. Named as in: OCaml.
val assoc: #a:eqtype -> #b:Type -> a -> list (a * b) -> Tot (option b)
+let rec assoc #a #b x = function
+ | [] -> None
+ | (x', y)::tl -> if x=x' then Some y else assoc x tlsplit takes a list of pairs (x1, y1), ..., (xn, yn) and
+returns the pair of lists (x1, ..., xn, y1, ..., yn). Named as in:
+OCaml
val split: list ('a * 'b) -> Tot (list 'a * list 'b)
+let rec split l = match l with
+ | [] -> ([],[])
+ | (hd1,hd2)::tl ->
+ let (tl1,tl2) = split tl in
+ (hd1::tl1,hd2::tl2)unzip takes a list of pairs (x1, y1), ..., (xn, yn) and
+returns the pair of lists (x1, ..., xn, y1, ..., yn). Named as in:
+Haskell
let unzip l = split lunzip3 takes a list of triples (x1, y1, z1), ..., (xn, yn, zn)
+and returns the triple of lists (x1, ..., xn, y1, ..., yn, `z1,
+..., zn]). Named as in: Haskell
val unzip3: list ('a * 'b * 'c) -> Tot (list 'a * list 'b * list 'c)
+let rec unzip3 l = match l with
+ | [] -> ([],[],[])
+ | (hd1,hd2,hd3)::tl ->
+ let (tl1,tl2,tl3) = unzip3 tl in
+ (hd1::tl1,hd2::tl2,hd3::tl3)Splitting a list at some index *
+splitAt takes a natural number n and a list and returns a pair
+of the maximal prefix of l of size smaller than n and the rest of
+the list
let rec splitAt (#a:Type) (n:nat) (l:list a) : Tot (list a * list a) =
+ if n = 0 then [], l
+ else
+ match l with
+ | [] -> [], l
+ | x :: xs -> let l1, l2 = splitAt (n-1) xs in x :: l1, l2let rec lemma_splitAt_snd_length (#a:Type) (n:nat) (l:list a) :
+ Lemma
+ (requires (n <= length l))
+ (ensures (length (snd (splitAt n l)) = length l - n)) =
+ match n, l with
+ | 0, _ -> ()
+ | _, [] -> ()
+ | _, _ :: l' -> lemma_splitAt_snd_length (n - 1) l'unsnoc is an inverse of snoc. It splits a list into
+all-elements-except-last and last element.
val unsnoc: #a:Type -> l:list a{length l > 0} -> Tot (list a * a)
+let unsnoc #a l =
+ let l1, l2 = splitAt (length l - 1) l in
+ lemma_splitAt_snd_length (length l - 1) l;
+ l1, hd l2split3 splits a list into 3 parts. This allows easy access to
+the part of the list before and after the element, as well as the
+element itself.
val split3: #a:Type -> l:list a -> i:nat{i < length l} -> Tot (list a * a * list a)
+let split3 #a l i =
+ let a, as = splitAt i l in
+ lemma_splitAt_snd_length i l;
+ let b :: c = as in
+ a, b, cSorting (implemented as quicksort) *
+partition splits a list l into two lists, the sum of whose
+lengths is the length of l.
val partition_length: f:('a -> Tot bool)
+ -> l:list 'a
+ -> Lemma (requires True)
+ (ensures (length (fst (partition f l))
+ + length (snd (partition f l)) = length l))
+let rec partition_length f l = match l with
+ | [] -> ()
+ | hd::tl -> partition_length f tlbool_of_compare turns a comparison function into a strict
+order. More precisely, bool_of_compare compare x y returns true
+if, and only if, compare x y is negative, meaning x precedes
+y in the ordering defined by compare.
val bool_of_compare : #a:Type -> (a -> a -> Tot int) -> a -> a -> Tot bool
+let bool_of_compare #a f x y = f x y < 0This is used in sorting, and is defined to be consistent with +OCaml and F#, where sorting is performed in ascending order.
+compare_of_bool turns a strict order into a comparison
+function. More precisely, compare_of_bool rel x y returns a positive
+number if, and only if, x rel y holds. Inspired from OCaml, where
+polymorphic comparison using both the compare function and the (>)
+infix operator are such that compare x y is positive if, and only
+if, x > y. Requires, at type-checking time, rel to be a pure total
+function.
val compare_of_bool : #a:eqtype -> (a -> a -> Tot bool) -> a -> a -> Tot int
+let compare_of_bool #a rel x y =
+ if x `rel` y then -1
+ else if x = y then 0
+ else 1let compare_of_bool_of_compare (#a:eqtype) (f:a -> a -> Tot bool)
+ : Lemma (forall x y. bool_of_compare (compare_of_bool f) x y == f x y)
+ = ()sortWith compare l returns the list l' containing the elements
+of l sorted along the comparison function compare, in such a
+way that if compare x y > 0, then x appears before y in
+l'. Sorts in ascending order
val sortWith: ('a -> 'a -> Tot int) -> l:list 'a -> Tot (list 'a) (decreases (length l))
+let rec sortWith f = function
+ | [] -> []
+ | pivot::tl ->
+ let hi, lo = partition (bool_of_compare f pivot) tl in
+ partition_length (bool_of_compare f pivot) tl;
+ append (sortWith f lo) (pivot::sortWith f hi)A l1 is a strict suffix of l2.
+let rec strict_suffix_of (#a: Type) (l1 l2: list a)
+: Pure Type0
+ (requires True)
+ (ensures (fun _ -> True))
+ (decreases l2)
+= match l2 with
+ | [] -> False
+ | _ :: q -> l1 == q \/ l1 `strict_suffix_of` q[@@deprecated "This function was misnamed: Please use 'strict_suffix_of'"]
+let strict_prefix_of = strict_suffix_ofval list_unref : #a:Type -> #p:(a -> Type0) -> list (x:a{p x}) -> Tot (list a)
+let rec list_unref #a #p l =
+ match l with
+ | [] -> []
+ | x::xs -> x :: list_unref xsval list_refb: #a:eqtype -> #p:(a -> Tot bool) ->
+ l:list a { for_all p l } ->
+ Tot (l':list (x:a{ p x }) {
+ length l = length l' /\
+ (forall i. {:pattern (index l i) } index l i = index l' i) })
+let rec list_refb #a #p l =
+ match l with
+ | hd :: tl -> hd :: list_refb #a #p tl
+ | [] -> []val list_ref: #a:eqtype -> #p:(a -> Tot prop) -> l:list a {
+ forall x. {:pattern mem x l} mem x l ==> p x
+} -> Tot (l':list (x:a{ p x }) {
+ length l = length l' /\
+ (forall i. {:pattern (index l i) } index l i = index l' i) })
+let rec list_ref #a #p l =
+ match l with
+ | hd :: tl ->
+ assert (mem hd l);
+ assert (p hd);
+ assert (forall x. {:pattern mem x tl} mem x tl ==> mem x l);
+ hd :: list_ref #a #p tl
+ | [] -> []Properties of pure total operations on lists
-This module states and proves some properties about pure and total operations on lists.
-let (llist a (n:nat)):l:list a:{=(length l, n)}@summary Properties of pure total operations on lists
+A list indexed by its length *
- Properties about mem *val mem_empty:Unidentified product: [#a:eqtype] Unidentified product: [x:a] (Lemma ((requires (mem x (Prims.Nil )))) ((ensures False)))let llist a (n:nat) = l:list a {length l = n}Properties about mem *
+The empty list has no elements
-val mem_existsb:Unidentified product: [#a:eqtype] Unidentified product: [f:(Unidentified product: [a] (Tot bool))] Unidentified product: [xs:list a] (Lemma ((ensures (<==>(existsb f xs, (exists x:a.{:pattern } (/\(=(f x, true), mem x xs))))))))Full specification for [existsb]: [existsb f xs] holds if, and only if, there exists an element [x] of [xs] such that [f x] holds.
- Properties about rev *val rev_mem:Unidentified product: [#a:eqtype] Unidentified product: [l:list a] Unidentified product: [x:a] (Lemma ((requires True)) ((ensures (<==>(mem x (rev l), mem x l)))))val mem_empty : #a:eqtype -> x:a ->
+ Lemma (requires (mem x []))
+ (ensures False)
+let mem_empty #a x = ()Full specification for existsb: existsb f xs holds if, and
+only if, there exists an element x of xs such that f x holds.
val mem_existsb: #a:eqtype -> f:(a -> Tot bool) -> xs:list a ->
+ Lemma(ensures (existsb f xs <==> (exists (x:a). (f x = true /\ mem x xs))))
+let rec mem_existsb #a f xs =
+ match xs with
+ | [] -> ()
+ | hd::tl -> mem_existsb f tllet rec mem_count
+ (#a: eqtype)
+ (l: list a)
+ (x: a)
+: Lemma
+ (mem x l <==> count x l > 0)
+= match l with
+ | [] -> ()
+ | x' :: l' -> mem_count l' xProperties about rev *
+val rev_acc_length : l:list 'a -> acc:list 'a ->
+ Lemma (requires True)
+ (ensures (length (rev_acc l acc) = length l + length acc))
+let rec rev_acc_length l acc = match l with
+ | [] -> ()
+ | hd::tl -> rev_acc_length tl (hd::acc)val rev_length : l:list 'a ->
+ Lemma (requires True)
+ (ensures (length (rev l) = length l))
+let rev_length l = rev_acc_length l []val rev_acc_mem : #a:eqtype -> l:list a -> acc:list a -> x:a ->
+ Lemma (requires True)
+ (ensures (mem x (rev_acc l acc) <==> (mem x l \/ mem x acc)))
+let rec rev_acc_mem #a l acc x = match l with
+ | [] -> ()
+ | hd::tl -> rev_acc_mem tl (hd::acc) xA list and its reversed have the same elements
- Properties about append *let ((lemma_append_last (#a:Type) (l1:list a) (l2:list a)):(Lemma ((requires (>(length l2, 0)))) ((ensures (==(last (@(l1, l2)), last l2)))))):match l1 with [] -> () | (Prims.Cons _ l1') -> lemma_append_last l1' l2The [last] element of a list remains the same, even after that list is [append]ed to another list.
- Properties mixing rev and append * Properties about snoc Reverse induction principle * Properties about iterators * Properties about unsnoc val lemma_unsnoc_snoc:Unidentified product: [#a:Type] Unidentified product: [l:l:list a:{>(length l, 0)}] (Lemma ((requires True)) ((ensures (==(snoc (unsnoc l), l)))) (Prims.Cons (SMTPat (snoc (unsnoc l))) (Prims.Nil )))[unsnoc] is the inverse of [snoc]
-val lemma_snoc_unsnoc:Unidentified product: [#a:Type] Unidentified product: [lx:(*(list a, a))] (Lemma ((requires True)) ((ensures (==(unsnoc (snoc lx), lx)))) (decreases (length (fst (lx)))) (Prims.Cons (SMTPat (unsnoc (snoc lx))) (Prims.Nil )))[snoc] is the inverse of [unsnoc]
-val lemma_unsnoc_length:Unidentified product: [#a:Type] Unidentified product: [l:l:list a:{>(length l, 0)}] (Lemma ((requires True)) ((ensures (==(length (fst (unsnoc l)), -(length l, 1))))))Doing an [unsnoc] gives us a list that is shorter in length by 1
-let ((lemma_unsnoc_append (#a:Type) (l1:list a) (l2:list a)):(Lemma ((requires (>(length l2, 0)))) ((ensures (let (as, a) = unsnoc (@(l1, l2)) in let (bs, b) = unsnoc l2 in /\(==(as, @(l1, bs)), ==(a, b))))))):match l1 with [] -> () | (Prims.Cons _ l1') -> lemma_unsnoc_append l1' l2[unsnoc] followed by [append] can be connected to the same vice-versa.
-let ((lemma_unsnoc_is_last (#t:Type) (l:list t)):(Lemma ((requires (>(length l, 0)))) ((ensures (/\(==(snd (unsnoc l), last l), ==(snd (unsnoc l), index l (-(length l, 1))))))))):match l with [_] -> () | _ -> lemma_unsnoc_is_last (tl l)[unsnoc] gives you [last] element, which is [index]ed at [length l - 1]
-let ((lemma_unsnoc_index (#t:Type) (l:list t) (i:nat)):(Lemma ((requires (/\(>(length l, 0), <(i, -(length l, 1)))))) ((ensures (/\(<(i, length (fst (unsnoc l))), ==(index (fst (unsnoc l)) i, index l i))))))):match i with 0 -> () | _ -> lemma_unsnoc_index (tl l) (-(i, 1))[index]ing on the left part of an [unsnoc]d list is the same as indexing the original list.
- Definition and properties about [split_using] let ((split_using (#t:Type) (l:list t) (x:x:t:{memP x l})):(GTot (r:(*(list t, list t))))):match l with [_] -> (FStar.Pervasives.Native.Mktuple2 (Prims.Nil ) l) | (Prims.Cons a as) -> if FStar.StrongExcludedMiddle.strong_excluded_middle (==(a, x)) then ((FStar.Pervasives.Native.Mktuple2 (Prims.Nil ) l)) else (let (l1', l2') = split_using as x in (FStar.Pervasives.Native.Mktuple2 (Prims.Cons a l1') l2'))[split_using] splits a list at the first instance of finding an element in it.
-NOTE: Uses [strong_excluded_middle] axiom.
- Definition of [index_of] let ((index_of (#t:Type) (l:list t) (x:x:t:{memP x l})):(GTot (i:nat:{/\(<(i, length l), ==(index l i, x))}))):match l with [_] -> 0 | (Prims.Cons a as) -> if FStar.StrongExcludedMiddle.strong_excluded_middle (==(a, x)) then (0) else (+(1, index_of as x))[index_of l x] gives the index of the leftmost [x] in [l].
-NOTE: Uses [strong_excluded_middle] axiom.
- Properties about partition *val partition_mem:Unidentified product: [#a:eqtype] Unidentified product: [f:(Unidentified product: [a] (Tot bool))] Unidentified product: [l:list a] Unidentified product: [x:a] (Lemma ((requires True)) ((ensures (let (l1, l2) = partition f l in =(mem x l, (||(mem x l1, mem x l2)))))))If [partition f l = (l1, l2)], then for any [x], [x] is in [l] if and only if [x] is in either one of [l1] or [l2]
-val partition_mem_forall:Unidentified product: [#a:eqtype] Unidentified product: [f:(Unidentified product: [a] (Tot bool))] Unidentified product: [l:list a] (Lemma ((requires True)) ((ensures (let (l1, l2) = partition f l in (forall x.{:pattern } =(mem x l, (||(mem x l1, mem x l2))))))))Same as [partition_mem], but using [forall]
-val partition_mem_p_forall:Unidentified product: [#a:eqtype] Unidentified product: [p:(Unidentified product: [a] (Tot bool))] Unidentified product: [l:list a] (Lemma ((requires True)) ((ensures (let (l1, l2) = partition p l in /\((forall x.{:pattern } ==>(mem x l1, p x)), (forall x.{:pattern } ==>(mem x l2, not (p x))))))))If [partition f l = (l1, l2)], then for any [x], if [x] is in [l1] (resp. [l2]), then [f x] holds (resp. does not hold)
-val partition_count:Unidentified product: [#a:eqtype] Unidentified product: [f:(Unidentified product: [a] (Tot bool))] Unidentified product: [l:list a] Unidentified product: [x:a] (Lemma ((requires True)) ((ensures (=(count x l, (+(count x (fst (partition f l)), count x (snd (partition f l)))))))))If [partition f l = (l1, l2)], then the number of occurrences of any [x] in [l] is the same as the sum of the number of occurrences in [l1] and [l2].
-val partition_count_forall:Unidentified product: [#a:eqtype] Unidentified product: [f:(Unidentified product: [a] (Tot bool))] Unidentified product: [l:list a] (Lemma ((requires True)) ((ensures (forall x.{:pattern } =(count x l, (+(count x (fst (partition f l)), count x (snd (partition f l)))))))))Same as [partition_count], but using [forall]
- Correctness of quicksort *val sortWith_permutation:Unidentified product: [#a:eqtype] Unidentified product: [f:(Unidentified product: [a] Unidentified product: [a] (Tot int))] Unidentified product: [l:list a] (Lemma ((requires True)) ((ensures (forall x.{:pattern } =(count x l, count x (sortWith f l))))) (decreases (length l)))Correctness of [sortWith], part 1/2: the number of occurrences of any [x] in [sortWith f l] is the same as the number of occurrences in [l].
-val sorted:Unidentified product: [(Unidentified product: ['a] Unidentified product: ['a] (Tot bool))] Unidentified product: [list 'a] (Tot bool)[sorted f l] holds if, and only if, any two consecutive elements [x], [y] of [l] are such that [f x y] holds.
-typeabbrev [f] is a total order if, and only if, it is reflexive, anti-symmetric, transitive and total.
-val append_sorted:Unidentified product: [#a:eqtype] Unidentified product: [f:(Unidentified product: [a] Unidentified product: [a] (Tot bool))] Unidentified product: [l1:l1:list a:{sorted f l1}] Unidentified product: [l2:l2:list a:{sorted f l2}] Unidentified product: [pivot:a] (Lemma ((requires (/\(/\(total_order #a f, (forall y.{:pattern } ==>(mem y l1, not (f pivot y)))), (forall y.{:pattern } ==>(mem y l2, f pivot y)))))) ((ensures (sorted f (@(l1, ((Prims.Cons pivot l2))))))) (Prims.Cons (SMTPat (sorted f (@(l1, ((Prims.Cons pivot l2)))))) (Prims.Nil )))val rev_mem : #a:eqtype -> l:list a -> x:a ->
+ Lemma (requires True)
+ (ensures (mem x (rev l) <==> mem x l))
+let rev_mem #a l x = rev_acc_mem l [] xProperties about append *
+val append_nil_l: l:list 'a ->
+ Lemma (requires True)
+ (ensures ([]@l == l))
+let append_nil_l l = ()val append_l_nil: l:list 'a ->
+ Lemma (requires True)
+ (ensures (l@[] == l)) [SMTPat (l@[])]
+let rec append_l_nil = function
+ | [] -> ()
+ | hd::tl -> append_l_nil tlval append_cons_l: hd:'a -> tl:list 'a -> l:list 'a ->
+ Lemma (requires True)
+ (ensures (((hd::tl)@l) == (hd::(tl@l))))
+let append_cons_l hd tl l = ()val append_l_cons: hd:'a -> tl:list 'a -> l:list 'a ->
+ Lemma (requires True)
+ (ensures ((l@(hd::tl)) == ((l@[hd])@tl)))
+let rec append_l_cons hd tl l = match l with
+ | [] -> ()
+ | hd'::tl' -> append_l_cons hd tl tl'val append_assoc: l1:list 'a -> l2:list 'a -> l3:list 'a ->
+ Lemma (requires True)
+ (ensures ((l1@(l2@l3)) == ((l1@l2)@l3)))
+let rec append_assoc l1 l2 l3 = match l1 with
+ | [] -> ()
+ | hd::tl -> append_assoc tl l2 l3val append_length: l1:list 'a -> l2:list 'a ->
+ Lemma (requires True)
+ (ensures (length (l1@l2) = length l1 + length l2)) [SMTPat (length (l1 @ l2))]
+let rec append_length l1 l2 = match l1 with
+ | [] -> ()
+ | hd::tl -> append_length tl l2val append_mem: #t:eqtype -> l1:list t
+ -> l2:list t
+ -> a:t
+ -> Lemma (requires True)
+ (ensures (mem a (l1@l2) = (mem a l1 || mem a l2)))SMTPat (mem a (l1@l2))
let rec append_mem #t l1 l2 a = match l1 with
+ | [] -> ()
+ | hd::tl -> append_mem tl l2 aval append_mem_forall: #a:eqtype -> l1:list a
+ -> l2:list a
+ -> Lemma (requires True)
+ (ensures (forall a. mem a (l1@l2) = (mem a l1 || mem a l2)))
+let rec append_mem_forall #a l1 l2 = match l1 with
+ | [] -> ()
+ | hd::tl -> append_mem_forall tl l2val append_count: #t:eqtype -> l1:list t
+ -> l2:list t
+ -> a:t
+ -> Lemma (requires True)
+ (ensures (count a (l1@l2) = (count a l1 + count a l2)))
+let rec append_count #t l1 l2 a = match l1 with
+ | [] -> ()
+ | hd::tl -> append_count tl l2 aval append_count_forall: #a:eqtype -> l1:list a
+ -> l2:list a
+ -> Lemma (requires True)
+ (ensures (forall a. count a (l1@l2) = (count a l1 + count a l2)))SMTPat (l1@l2)
let rec append_count_forall #a l1 l2 = match l1 with
+ | [] -> ()
+ | hd::tl -> append_count_forall tl l2val append_eq_nil: l1:list 'a -> l2:list 'a ->
+ Lemma (requires (l1@l2 == []))
+ (ensures (l1 == [] /\ l2 == []))
+let append_eq_nil l1 l2 = ()val append_eq_singl: l1:list 'a -> l2:list 'a -> x:'a ->
+ Lemma (requires (l1@l2 == [x]))
+ (ensures ((l1 == [x] /\ l2 == []) \/ (l1 == [] /\ l2 == [x])))
+let append_eq_singl l1 l2 x = ()val append_inv_head: l:list 'a -> l1:list 'a -> l2:list 'a ->
+ Lemma (requires ((l@l1) == (l@l2)))
+ (ensures (l1 == l2))
+let rec append_inv_head l l1 l2 = match l with
+ | [] -> ()
+ | hd::tl -> append_inv_head tl l1 l2val append_inv_tail: l:list 'a -> l1:list 'a -> l2:list 'a ->
+ Lemma (requires ((l1@l) == (l2@l)))
+ (ensures (l1 == l2))
+let rec append_inv_tail l l1 l2 = match l1, l2 with
+ | [], [] -> ()
+ | hd1::tl1, hd2::tl2 -> append_inv_tail l tl1 tl2
+ | [], hd2::tl2 ->
+ (match l with
+ | [] -> ()
+ | hd::tl -> append_l_cons hd tl tl2; append_inv_tail tl [] (tl2@[hd])We can here apply the induction hypothesis thanks to termination on a lexicographical ordering of the arguments!
+ )
+| hd1::tl1, [] ->
+ (match l with
+ | [] -> ()
+ | hd::tl -> append_l_cons hd tl tl1; append_inv_tail tl (tl1@[hd]) []Idem
+)let rec append_length_inv_head
+ (#a: Type)
+ (left1 right1 left2 right2: list a)
+: Lemma
+ (requires (append left1 right1 == append left2 right2 /\ length left1 == length left2))
+ (ensures (left1 == left2 /\ right1 == right2))
+ (decreases left1)
+= match left1 with
+ | [] -> ()
+ | _ :: left1' ->
+ append_length_inv_head left1' right1 (tl left2) right2let append_length_inv_tail
+ (#a: Type)
+ (left1 right1 left2 right2: list a)
+: Lemma
+ (requires (append left1 right1 == append left2 right2 /\ length right1 == length right2))
+ (ensures (left1 == left2 /\ right1 == right2))
+= append_length left1 right1;
+ append_length left2 right2;
+ append_length_inv_head left1 right1 left2 right2The last element of a list remains the same, even after that list is
+appended to another list.
let rec lemma_append_last (#a:Type) (l1 l2:list a) :
+ Lemma
+ (requires (length l2 > 0))
+ (ensures (last (l1 @ l2) == last l2)) =
+ match l1 with
+ | [] -> ()
+ | _ :: l1' -> lemma_append_last l1' l2Properties mixing rev and append *
+val rev': list 'a -> Tot (list 'a)
+let rec rev' = function
+ | [] -> []
+ | hd::tl -> (rev' tl)@[hd]
+let rev'T = rev'val rev_acc_rev': l:list 'a -> acc:list 'a ->
+ Lemma (requires (True))
+ (ensures ((rev_acc l acc) == ((rev' l)@acc)))
+let rec rev_acc_rev' l acc = match l with
+ | [] -> ()
+ | hd::tl -> rev_acc_rev' tl (hd::acc); append_l_cons hd acc (rev' tl)val rev_rev': l:list 'a ->
+ Lemma (requires True)
+ (ensures ((rev l) == (rev' l)))
+let rev_rev' l = rev_acc_rev' l []; append_l_nil (rev' l)val rev'_append: l1:list 'a -> l2:list 'a ->
+ Lemma (requires True)
+ (ensures ((rev' (l1@l2)) == ((rev' l2)@(rev' l1))))
+let rec rev'_append l1 l2 = match l1 with
+ | [] -> append_l_nil (rev' l2)
+ | hd::tl -> rev'_append tl l2; append_assoc (rev' l2) (rev' tl) [hd]val rev_append: l1:list 'a -> l2:list 'a ->
+ Lemma (requires True)
+ (ensures ((rev (l1@l2)) == ((rev l2)@(rev l1))))
+let rev_append l1 l2 = rev_rev' l1; rev_rev' l2; rev_rev' (l1@l2); rev'_append l1 l2val rev'_involutive : l:list 'a ->
+ Lemma (requires True)
+ (ensures (rev' (rev' l) == l))
+let rec rev'_involutive = function
+ | [] -> ()
+ | hd::tl -> rev'_append (rev' tl) [hd]; rev'_involutive tlval rev_involutive : l:list 'a ->
+ Lemma (requires True)
+ (ensures (rev (rev l) == l))
+let rev_involutive l = rev_rev' l; rev_rev' (rev' l); rev'_involutive lProperties about snoc
+val lemma_snoc_length : (lx:(list 'a * 'a)) ->
+ Lemma (requires True)
+ (ensures (length (snoc lx) = length (fst lx) + 1))
+let lemma_snoc_length (l, x) = append_length l [x]Reverse induction principle *
+val rev'_list_ind: p:(list 'a -> Tot bool) -> l:list 'a ->
+ Lemma (requires ((p []) /\ (forall hd tl. p (rev' tl) ==> p (rev' (hd::tl)))))
+ (ensures (p (rev' l)))
+let rec rev'_list_ind p = function
+ | [] -> ()
+ | hd::tl -> rev'_list_ind p tlval rev_ind: p:(list 'a -> Tot bool) -> l:list 'a ->
+ Lemma (requires ((p []) /\ (forall hd tl. p hd ==> p (hd@[tl]))))
+ (ensures (p l))
+let rev_ind p l = rev'_involutive l; rev'_list_ind p (rev' l)Properties about iterators *
+val map_lemma: f:('a -> Tot 'b)
+ -> l:(list 'a)
+ -> Lemma (requires True)
+ (ensures (length (map f l)) = length l)
+ [SMTPat (map f l)]
+let rec map_lemma f l =
+ match l with
+ | [] -> ()
+ | h::t -> map_lemma f tProperties about unsnoc
+unsnoc is the inverse of snoc
val lemma_unsnoc_snoc: #a:Type -> l:list a{length l > 0} ->
+ Lemma (requires True)
+ (ensures (snoc (unsnoc l) == l))
+ [SMTPat (snoc (unsnoc l))]
+let lemma_unsnoc_snoc #a l =
+ let l', x = unsnoc l in
+ let l1, l2 = l', [x] in
+ lemma_splitAt_snd_length (length l - 1) l;assert ((l1, l2) == splitAt (length l - 1) l);
+let rec aux (l:list a{length l > 0}) :
+ Lemma (let l1, l2 = splitAt (length l - 1) l in
+ append l1 l2 == l) =
+ if length l = 1 then () else aux (tl l) in
+aux lsnoc is the inverse of unsnoc
val lemma_snoc_unsnoc: #a:Type -> lx:(list a * a) ->
+ Lemma (requires True)
+ (ensures (unsnoc (snoc lx) == lx))
+ (decreases (length (fst (lx))))
+ [SMTPat (unsnoc (snoc lx))]
+let rec lemma_snoc_unsnoc #a lx =
+ let l, x = lx in
+ match l with
+ | [] -> ()
+ | _ -> lemma_snoc_unsnoc (tl l, x)Doing an unsnoc gives us a list that is shorter in length by 1
val lemma_unsnoc_length: #a:Type -> l:list a{length l > 0} ->
+ Lemma (requires True)
+ (ensures (length (fst (unsnoc l)) == length l - 1))
+let lemma_unsnoc_length #a l =
+ lemma_snoc_length (unsnoc l)unsnoc followed by append can be connected to the same vice-versa.
let rec lemma_unsnoc_append (#a:Type) (l1 l2:list a) :
+ Lemma
+ (requires (length l2 > 0)) // the [length l2 = 0] is trivial
+ (ensures (
+ let as, a = unsnoc (l1 @ l2) in
+ let bs, b = unsnoc l2 in
+ as == l1 @ bs /\ a == b)) =
+ match l1 with
+ | [] -> ()
+ | _ :: l1' -> lemma_unsnoc_append l1' l2unsnoc gives you last element, which is indexed at length l - 1
let rec lemma_unsnoc_is_last (#t:Type) (l:list t) :
+ Lemma
+ (requires (length l > 0))
+ (ensures (snd (unsnoc l) == last l /\ snd (unsnoc l) == index l (length l - 1))) =
+ match l with
+ | [_] -> ()
+ | _ -> lemma_unsnoc_is_last (tl l)indexing on the left part of an unsnocd list is the same as indexing
+the original list.
let rec lemma_unsnoc_index (#t:Type) (l:list t) (i:nat) :
+ Lemma
+ (requires (length l > 0 /\ i < length l - 1))
+ (ensures (
+ i < length (fst (unsnoc l)) /\
+ index (fst (unsnoc l)) i == index l i)) =
+ match i with
+ | 0 -> ()
+ | _ -> lemma_unsnoc_index (tl l) (i - 1)Definition and properties about split_using
split_using splits a list at the first instance of finding an
+element in it.
let rec split_using (#t:Type) (l:list t) (x:t{x `memP` l}) :
+ GTot (list t * list t) =
+ match l with
+ | [_] -> [], l
+ | a :: as ->
+ if FStar.StrongExcludedMiddle.strong_excluded_middle (a == x) then (
+ [], l
+ ) else (
+ let l1', l2' = split_using as x in
+ a :: l1', l2'
+ )NOTE: Uses strong_excluded_middle axiom.
let rec lemma_split_using (#t:Type) (l:list t) (x:t{x `memP` l}) :
+ Lemma
+ (ensures (
+ let l1, l2 = split_using l x in
+ length l2 > 0 /\
+ ~(x `memP` l1) /\
+ hd l2 == x /\
+ append l1 l2 == l)) =
+ match l with
+ | [_] -> ()
+ | a :: as ->
+ let goal =
+ let l1, l2 = split_using l x in
+ length l2 > 0 /\
+ ~(x `memP` l1) /\
+ hd l2 == x /\
+ append l1 l2 == l
+ in
+ FStar.Classical.or_elim
+ #_ #_
+ #(fun () -> goal)
+ (fun (_:squash (a == x)) -> ())
+ (fun (_:squash (x `memP` as)) -> lemma_split_using as x)Definition of index_of
index_of l x gives the index of the leftmost x in l.
let rec index_of (#t:Type) (l:list t) (x:t{x `memP` l}) :
+ GTot (i:nat{i < length l /\ index l i == x}) =
+ match l with
+ | [_] -> 0
+ | a :: as ->
+ if FStar.StrongExcludedMiddle.strong_excluded_middle (a == x) then (
+ 0
+ ) else (
+ 1 + index_of as x
+ )NOTE: Uses strong_excluded_middle axiom.
Properties about partition *
+If partition f l = (l1, l2), then for any x, x is in l if
+and only if x is in either one of l1 or l2
val partition_mem: #a:eqtype -> f:(a -> Tot bool)
+ -> l:list a
+ -> x:a
+ -> Lemma (requires True)
+ (ensures (let l1, l2 = partition f l in
+ mem x l = (mem x l1 || mem x l2)))
+let rec partition_mem #a f l x = match l with
+ | [] -> ()
+ | hd::tl -> partition_mem f tl xSame as partition_mem, but using forall
val partition_mem_forall: #a:eqtype -> f:(a -> Tot bool)
+ -> l:list a
+ -> Lemma (requires True)
+ (ensures (let l1, l2 = partition f l in
+ (forall x. mem x l = (mem x l1 || mem x l2))))
+let rec partition_mem_forall #a f l = match l with
+ | [] -> ()
+ | hd::tl -> partition_mem_forall f tlIf partition f l = (l1, l2), then for any x, if x is in l1
+(resp. l2), then f x holds (resp. does not hold)
val partition_mem_p_forall: #a:eqtype -> p:(a -> Tot bool)
+ -> l:list a
+ -> Lemma (requires True)
+ (ensures (let l1, l2 = partition p l in
+ (forall x. mem x l1 ==> p x) /\ (forall x. mem x l2 ==> not (p x))))
+let rec partition_mem_p_forall #a p l = match l with
+ | [] -> ()
+ | hd::tl -> partition_mem_p_forall p tlIf partition f l = (l1, l2), then the number of occurrences of
+any x in l is the same as the sum of the number of occurrences in
+l1 and l2.
val partition_count: #a:eqtype -> f:(a -> Tot bool)
+ -> l:list a
+ -> x:a
+ -> Lemma (requires True)
+ (ensures (count x l = (count x (fst (partition f l)) + count x (snd (partition f l)))))
+let rec partition_count #a f l x = match l with
+ | [] -> ()
+ | hd::tl -> partition_count f tl xSame as partition_count, but using forall
val partition_count_forall: #a:eqtype -> f:(a -> Tot bool)
+ -> l:list a
+ -> Lemma (requires True)
+ (ensures (forall x. count x l = (count x (fst (partition f l)) + count x (snd (partition f l)))))SMTPat (partitionT f l)
let rec partition_count_forall #a f l= match l with
+ | [] -> ()
+ | hd::tl -> partition_count_forall f tlCorrectness of quicksort *
+Correctness of sortWith, part 1/2: the number of occurrences of
+any x in sortWith f l is the same as the number of occurrences in
+l.
val sortWith_permutation: #a:eqtype -> f:(a -> a -> Tot int) -> l:list a ->
+ Lemma (requires True)
+ (ensures (forall x. count x l = count x (sortWith f l)))
+ (decreases (length l))
+let rec sortWith_permutation #a f l = match l with
+ | [] -> ()
+ | pivot::tl ->
+ let hi, lo = partition (bool_of_compare f pivot) tl in
+ partition_length (bool_of_compare f pivot) tl;
+ partition_count_forall (bool_of_compare f pivot) tl;
+ sortWith_permutation f lo;
+ sortWith_permutation f hi;
+ append_count_forall (sortWith f lo) (pivot::sortWith f hi)sorted f l holds if, and only if, any two consecutive elements
+x, y of l are such that f x y holds
val sorted: ('a -> 'a -> Tot bool) -> list 'a -> Tot bool
+let rec sorted f = function
+ | []
+ | [_] -> true
+ | x::y::tl -> f x y && sorted f (y::tl)f is a total order if, and only if, it is reflexive,
+anti-symmetric, transitive and total.
type total_order (#a:Type) (f: (a -> a -> Tot bool)) =
+ (forall a. f a a) (* reflexivity *)
+ /\ (forall a1 a2. f a1 a2 /\ f a2 a1 ==> a1 == a2) (* anti-symmetry *)
+ /\ (forall a1 a2 a3. f a1 a2 /\ f a2 a3 ==> f a1 a3) (* transitivity *)
+ /\ (forall a1 a2. f a1 a2 \/ f a2 a1) (* totality *)Correctness of the merging of two sorted lists around a pivot.
-val sortWith_sorted:Unidentified product: [#a:eqtype] Unidentified product: [f:(Unidentified product: [a] Unidentified product: [a] (Tot int))] Unidentified product: [l:list a] (Lemma ((requires (total_order #a (bool_of_compare f)))) ((ensures (/\((sorted (bool_of_compare f) (sortWith f l)), (forall x.{:pattern } =(mem x l, mem x (sortWith f l))))))) (decreases (length l)))Correctness of [sortWith], part 2/2: the elements of [sortWith f l] are sorted according to comparison function [f], and the elements of [sortWith f l] are the elements of [l].
-let ((mem_memP (#a:eqtype) (x:a) (l:list a)):(Lemma ((ensures (<==>(mem x l, memP x l)))))):match l with [] -> () | (Prims.Cons a q) -> mem_memP x qCorrectness of [mem] for types with decidable equality. TODO: replace [mem] with [memP] in relevant lemmas and define the right SMTPat to automatically recover lemmas about [mem] for types with decidable equality
-let ((lemma_index_memP (#t:Type) (l:list t) (i:i:nat:{<(i, length l)})):(Lemma ((ensures (memP index l i l))) (Prims.Cons (SMTPat (memP index l i l)) (Prims.Nil )))):match i with 0 -> () | _ -> lemma_index_memP (tl l) (-(i, 1))If an element can be [index]ed, then it is a [memP] of the list.
-val memP_empty:Unidentified product: [#a:Type] Unidentified product: [x:a] (Lemma ((requires (memP x (Prims.Nil )))) ((ensures False)))val append_sorted: #a:eqtype
+ -> f:(a -> a -> Tot bool)
+ -> l1:list a{sorted f l1}
+ -> l2:list a{sorted f l2}
+ -> pivot:a
+ -> Lemma (requires (total_order #a f
+ /\ (forall y. mem y l1 ==> not(f pivot y))
+ /\ (forall y. mem y l2 ==> f pivot y)))
+ (ensures (sorted f (l1@(pivot::l2))))
+ [SMTPat (sorted f (l1@(pivot::l2)))]
+let rec append_sorted #a f l1 l2 pivot = match l1 with
+ | [] -> ()
+ | hd::tl -> append_sorted f tl l2 pivotCorrectness of sortWith, part 2/2: the elements of sortWith f l are sorted according to comparison function f, and the elements
+of sortWith f l are the elements of l.
val sortWith_sorted: #a:eqtype -> f:(a -> a -> Tot int) -> l:list a ->
+ Lemma (requires (total_order #a (bool_of_compare f)))
+ (ensures ((sorted (bool_of_compare f) (sortWith f l)) /\ (forall x. mem x l = mem x (sortWith f l))))
+ (decreases (length l))
+let rec sortWith_sorted #a f l = match l with
+ | [] -> ()
+ | pivot::tl ->
+ let hi, lo = partition (bool_of_compare f pivot) tl in
+ partition_length (bool_of_compare f pivot) tl;
+ partition_mem_forall (bool_of_compare f pivot) tl;
+ partition_mem_p_forall (bool_of_compare f pivot) tl;
+ sortWith_sorted f lo;
+ sortWith_sorted f hi;
+ append_mem_forall (sortWith f lo) (pivot::sortWith f hi);
+ append_sorted (bool_of_compare f) (sortWith f lo) (sortWith f hi) pivotCorrectness of mem for types with decidable equality. TODO:
+replace mem with memP in relevant lemmas and define the right
+SMTPat to automatically recover lemmas about mem for types with
+decidable equality
let rec mem_memP
+ (#a: eqtype)
+ (x: a)
+ (l: list a)
+: Lemma (ensures (mem x l <==> memP x l))
+ [SMTPat (mem x l); SMTPat (memP x l)]
+= match l with
+ | [] -> ()
+ | a :: q -> mem_memP x qIf an element can be indexed, then it is a memP of the list.
let rec lemma_index_memP (#t:Type) (l:list t) (i:nat{i < length l}) :
+ Lemma
+ (ensures (index l i `memP` l))
+ [SMTPat (index l i `memP` l)] =
+ match i with
+ | 0 -> ()
+ | _ -> lemma_index_memP (tl l) (i - 1)The empty list has no elements.
-val memP_existsb:Unidentified product: [#a:Type] Unidentified product: [f:(Unidentified product: [a] (Tot bool))] Unidentified product: [xs:list a] (Lemma ((ensures (<==>(existsb f xs, (exists x:a.{:pattern } (/\(=(f x, true), memP x xs))))))))Full specification for [existsb]: [existsb f xs] holds if, and only if, there exists an element [x] of [xs] such that [f x] holds.
-let ((noRepeats_nil (#a:eqtype)):(Lemma ((ensures (noRepeats #a (Prims.Nil )))))):()Properties of [noRepeats]
- Properties of [assoc] Properties of [fold_left] Properties of [strict_prefix_of] Properties of << with lists Properties about find Properties of init and last val memP_empty : #a: Type -> x:a ->
+ Lemma (requires (memP x []))
+ (ensures False)
+let memP_empty #a x = ()Full specification for existsb: existsb f xs holds if, and
+only if, there exists an element x of xs such that f x holds.
val memP_existsb: #a: Type -> f:(a -> Tot bool) -> xs:list a ->
+ Lemma(ensures (existsb f xs <==> (exists (x:a). (f x = true /\ memP x xs))))
+let rec memP_existsb #a f xs =
+ match xs with
+ | [] -> ()
+ | hd::tl -> memP_existsb f tllet rec memP_map_intro
+ (#a #b: Type)
+ (f: a -> Tot b)
+ (x: a)
+ (l: list a)
+: Lemma
+ (requires True)
+ (ensures (memP x l ==> memP (f x) (map f l)))
+ (decreases l)
+= match l with
+ | [] -> ()
+ | _ :: q -> memP_map_intro f x q (* NOTE: would fail if [requires memP x l] instead of [ ==> ] *)let rec memP_map_elim
+ (#a #b: Type)
+ (f: a -> Tot b)
+ (y: b)
+ (l: list a)
+: Lemma
+ (requires True)
+ (ensures (memP y (map f l) ==> (exists (x : a) . memP x l /\ f x == y)))
+ (decreases l)
+= match l with
+ | [] -> ()
+ | _ :: q -> memP_map_elim f y qProperties of noRepeats
let noRepeats_nil
+ (#a: eqtype)
+: Lemma
+ (ensures (noRepeats #a []))
+= ()let noRepeats_cons
+ (#a: eqtype)
+ (h: a)
+ (tl: list a)
+: Lemma
+ (requires ((~ (mem h tl)) /\ noRepeats tl))
+ (ensures (noRepeats #a (h::tl)))
+= ()let rec noRepeats_append_elim
+ (#a: eqtype)
+ (l1 l2: list a)
+: Lemma
+ (requires (noRepeats (l1 @ l2)))
+ (ensures (noRepeats l1 /\ noRepeats l2 /\ (forall x . mem x l1 ==> ~ (mem x l2))))
+ (decreases l1)
+= match l1 with
+ | [] -> ()
+ | x :: q1 ->
+ append_mem q1 l2 x;
+ noRepeats_append_elim q1 l2let rec noRepeats_append_intro
+ (#a: eqtype)
+ (l1 l2: list a)
+: Lemma
+ (requires (noRepeats l1 /\ noRepeats l2 /\ (forall x . mem x l1 ==> ~ (mem x l2))))
+ (ensures (noRepeats (l1 @ l2)))
+ (decreases l1)
+= match l1 with
+ | [] -> ()
+ | x :: q1 ->
+ append_mem q1 l2 x;
+ noRepeats_append_intro q1 l2Properties of assoc
let assoc_nil
+ (#a: eqtype)
+ (#b: Type)
+ (x: a)
+: Lemma
+ (ensures (assoc #a #b x [] == None))
+= ()let assoc_cons_eq
+ (#a: eqtype)
+ (#b: Type)
+ (x: a)
+ (y: b)
+ (q: list (a * b))
+: Lemma
+ (ensures (assoc x ((x, y) :: q) == Some y))
+= ()let assoc_cons_not_eq
+ (#a: eqtype)
+ (#b: Type)
+ (x x': a)
+ (y: b)
+ (q: list (a * b))
+: Lemma
+ (requires (x <> x'))
+ (ensures (assoc x' ((x, y) :: q) == assoc x' q))
+= ()let rec assoc_append_elim_r
+ (#a: eqtype)
+ (#b: Type)
+ (x: a)
+ (l1 l2: list (a * b))
+: Lemma
+ (requires (assoc x l2 == None \/ ~ (assoc x l1 == None)))
+ (ensures (assoc x (l1 @ l2) == assoc x l1))
+ (decreases l1)
+= match l1 with
+ | [] -> ()
+ | (x', _) :: q -> if x = x' then () else assoc_append_elim_r x q l2let rec assoc_append_elim_l
+ (#a: eqtype)
+ (#b: Type)
+ (x: a)
+ (l1 l2: list (a * b))
+: Lemma
+ (requires (assoc x l1 == None))
+ (ensures (assoc x (l1 @ l2) == assoc x l2))
+ (decreases l1)
+= match l1 with
+ | [] -> ()
+ | (x', _) :: q -> if x = x' then assert False else assoc_append_elim_l x q l2let rec assoc_memP_some
+ (#a: eqtype)
+ (#b: Type)
+ (x: a)
+ (y: b)
+ (l: list (a * b))
+: Lemma
+ (requires (assoc x l == Some y))
+ (ensures (memP (x, y) l))
+ (decreases l)
+= match l with
+ | [] -> ()
+ | (x', _) :: q -> if x = x' then () else assoc_memP_some x y qlet rec assoc_memP_none
+ (#a: eqtype)
+ (#b: Type)
+ (x: a)
+ (l: list (a * b))
+: Lemma
+ (requires (assoc x l == None))
+ (ensures (forall y . ~ (memP (x, y) l)))
+ (decreases l)
+= match l with
+ | [] -> ()
+ | (x', _) :: q -> if x = x' then assert False else assoc_memP_none x qlet assoc_mem
+ (#a: eqtype)
+ (#b: Type)
+ (x: a)
+ (l: list (a * b))
+: Lemma
+ (ensures (mem x (map fst l) <==> (exists y . assoc x l == Some y)))
+= match assoc x l with
+ | None ->
+ assoc_memP_none x l;
+ mem_memP x (map fst l);
+ memP_map_elim fst x l
+ | Some y ->
+ assoc_memP_some x y l;
+ memP_map_intro fst (x, y) l;
+ mem_memP x (map fst l)Properties of fold_left
let rec fold_left_invar
+ (#a #b: Type)
+ (f: (a -> b -> Tot a))
+ (l: list b)
+ (p: (a -> Tot Type0))
+ : Lemma
+ (requires forall (x: a) (y: b) . p x ==> memP y l ==> p (f x y) )
+ (ensures forall (x: a) . p x ==> p (fold_left f x l))
+=
+ match l with
+ | [] -> ()
+ | y :: q -> fold_left_invar f q plet rec fold_left_map
+ (#a #b #c: Type)
+ (f_aba: a -> b -> Tot a)
+ (f_bc: b -> Tot c)
+ (f_aca: a -> c -> Tot a)
+ (l: list b)
+ : Lemma
+ (requires forall (x: a) (y: b) . f_aba x y == f_aca x (f_bc y) )
+ (ensures forall (x : a) . fold_left f_aba x l == fold_left f_aca x (map f_bc l) )
+ =
+ match l with
+ | [] -> ()
+ | y :: q -> fold_left_map f_aba f_bc f_aca qlet rec map_append
+ (#a #b: Type)
+ (f: a -> Tot b)
+ (l1 l2: list a)
+:
+ Lemma
+ (ensures map f (l1 @ l2) == map f l1 @ map f l2)
+=
+ match l1 with
+ | [] -> ()
+ | x :: q -> map_append f q l2let rec fold_left_append
+ (#a #b: Type)
+ (f: a -> b -> Tot a)
+ (l1 l2: list b)
+ : Lemma
+ (ensures forall x . fold_left f x (l1 @ l2) == fold_left f (fold_left f x l1) l2)
+= match l1 with
+ | [] -> ()
+ | x :: q -> fold_left_append f q l2let rec fold_left_monoid
+ (#a: Type)
+ (opA: (a -> a -> Tot a))
+ (zeroA: a)
+ (l: list a)
+: Lemma
+ (requires
+ (forall u v w . (u `opA` (v `opA` w)) == ((u `opA` v) `opA` w)) /\
+ (forall x . (x `opA` zeroA) == x) /\
+ (forall x . (zeroA `opA` x) == x))
+ (ensures
+ forall x .
+ (fold_left opA x l) == (x `opA` (fold_left opA zeroA l)))
+= match l with
+ | [] -> ()
+ | x :: q -> fold_left_monoid opA zeroA qlet fold_left_append_monoid
+ (#a: Type)
+ (f: (a -> a -> Tot a))
+ (z: a)
+ (l1 l2: list a)
+: Lemma
+ (requires
+ (forall u v w . f u (f v w) == f (f u v) w) /\
+ (forall x . f x z == x) /\
+ (forall x . f z x == x))
+ (ensures
+ fold_left f z (l1 @ l2) == f (fold_left f z l1) (fold_left f z l2))
+= fold_left_append f l1 l2;
+ fold_left_monoid f z l2Properties of index
private let rec index_extensionality_aux
+ (#a: Type)
+ (l1 l2: list a)
+ (l_len: (l_len: unit { length l1 == length l2 } ))
+ (l_index: (i: (i: nat {i < length l1})) -> Tot (l_index: unit {index l1 i == index l2 i}))
+: Lemma
+ (ensures (l1 == l2))
+= match (l1, l2) with
+ | (a1::q1, a2::q2) ->
+ let a_eq : (a_eq : unit {a1 == a2}) = l_index 0 in
+ let q_len : (q_len: unit {length q1 == length q2}) = () in
+ let q_index (i: (i: nat {i < length q1})) : Tot (q_index: unit {index q1 i == index q2 i}) =
+ l_index (i + 1) in
+ let q_eq : (q_eq : unit {l1 == l2}) = index_extensionality_aux q1 q2 q_len q_index in
+ ()
+ | _ -> ()let index_extensionality
+ (#a: Type)
+ (l1 l2: list a)
+: Lemma
+ (requires
+ (length l1 == length l2 /\
+ (forall (i: nat) . i < length l1 ==> index l1 i == index l2 i)))
+ (ensures (l1 == l2))
+= index_extensionality_aux l1 l2 () (fun i -> ())Properties of strict_suffix_of
let rec strict_suffix_of_nil (#a: Type) (x: a) (l: list a)
+: Lemma
+ (requires True)
+ (ensures (strict_suffix_of [] (x::l)))
+ (decreases l)
+= match l with
+ | [] -> ()
+ | a' :: q -> strict_suffix_of_nil a' qlet strict_suffix_of_or_eq_nil (#a: Type) (l: list a)
+: Lemma
+ (ensures (strict_suffix_of [] l \/ l == []))
+= match l with
+ | [] -> ()
+ | a :: q -> strict_suffix_of_nil a qlet strict_suffix_of_cons (#a: Type) (x: a) (l: list a) :
+ Lemma
+ (ensures (strict_suffix_of l (x::l)))
+= ()let rec strict_suffix_of_trans (#a: Type) (l1 l2 l3: list a)
+: Lemma
+ (requires True)
+ (ensures ((strict_suffix_of l1 l2 /\ strict_suffix_of l2 l3) ==> strict_suffix_of l1 l3))
+ (decreases l3)
+ [SMTPat (strict_suffix_of l1 l2); SMTPat (strict_suffix_of l2 l3)]
+= match l3 with
+ | [] -> ()
+ | _ :: q -> strict_suffix_of_trans l1 l2 qlet rec strict_suffix_of_correct (#a) (l1 l2: list a)
+: Lemma
+ (requires True)
+ (ensures (strict_suffix_of l1 l2 ==> l1 << l2))
+ (decreases l2)
+= match l2 with
+ | [] -> ()
+ | _ :: q ->
+ strict_suffix_of_correct l1 qlet rec map_strict_suffix_of (#a #b: Type) (f: a -> Tot b) (l1: list a) (l2: list a) :
+ Lemma
+ (requires True)
+ (ensures (strict_suffix_of l1 l2 ==> strict_suffix_of (map f l1) (map f l2)))
+ (decreases l2)
+= match l2 with
+ | [] -> ()
+ | a::q ->
+ map_strict_suffix_of f l1 qlet rec mem_strict_suffix_of (#a: eqtype) (l1: list a) (m: a) (l2: list a)
+: Lemma
+ (requires True)
+ (ensures ((mem m l1 /\ strict_suffix_of l1 l2) ==> mem m l2))
+= match l2 with
+ | [] -> ()
+ | a :: q ->
+ mem_strict_suffix_of l1 m qlet rec strict_suffix_of_exists_append
+ (#a: Type)
+ (l1 l2: list a)
+: Lemma
+ (ensures (strict_suffix_of l1 l2 ==> (exists l3 . l2 == append l3 l1)))
+= match l2 with
+ | [] -> ()
+ | a :: q ->
+ FStar.Classical.or_elim
+ #(l1 == q)
+ #(strict_suffix_of l1 q)
+ #(fun _ -> exists l3 . l2 == append l3 l1)
+ (fun _ ->
+ FStar.Classical.exists_intro (fun l3 -> l2 == append l3 l1) (a :: []))
+ (fun _ ->
+ FStar.Classical.exists_elim
+ (exists l3 . l2 == append l3 l1)
+ #_
+ #(fun l3 -> q == append l3 l1)
+ (strict_suffix_of_exists_append l1 q)
+ (fun l3 ->
+ FStar.Classical.exists_intro (fun l3 -> l2 == append l3 l1) (a :: l3)
+ ))let strict_suffix_of_or_eq_exists_append
+ (#a: Type)
+ (l1 l2: list a)
+: Lemma
+ (ensures ((strict_suffix_of l1 l2 \/ l1 == l2) ==> (exists l3 . l2 == append l3 l1)))
+= FStar.Classical.or_elim
+ #(strict_suffix_of l1 l2)
+ #(l1 == l2)
+ #(fun _ -> exists l3 . l2 == append l3 l1)
+ (fun _ ->
+ strict_suffix_of_exists_append l1 l2)
+ (fun _ ->
+ FStar.Classical.exists_intro
+ (fun l3 -> l2 == append l3 l1)
+ [] )Properties of << with lists
+let precedes_tl
+ (#a: Type)
+ (l: list a {Cons? l})
+: Lemma (ensures (tl l << l))
+= ()let rec precedes_append_cons_r
+ (#a: Type)
+ (l1: list a)
+ (x: a)
+ (l2: list a)
+: Lemma
+ (requires True)
+ (ensures (x << append l1 (x :: l2)))
+ [SMTPat (x << append l1 (x :: l2))]
+= match l1 with
+ | [] -> ()
+ | _ :: q -> precedes_append_cons_r q x l2let precedes_append_cons_prod_r
+ (#a #b: Type)
+ (l1: list (a * b))
+ (x: a)
+ (y: b)
+ (l2: list (a * b))
+: Lemma
+ (ensures
+ x << (append l1 ((x, y) :: l2)) /\
+ y << (append l1 ((x, y) :: l2)))
+= precedes_append_cons_r l1 (x, y) l2let rec memP_precedes
+ (#a: Type)
+ (x: a)
+ (l: list a)
+: Lemma
+ (requires True)
+ (ensures (memP x l ==> x << l))
+ (decreases l)
+= match l with
+ | [] -> ()
+ | y :: q ->
+ FStar.Classical.or_elim
+ #(x == y)
+ #(memP x q)
+ #(fun _ -> x << l)
+ (fun _ -> ())
+ (fun _ -> memP_precedes x q)let assoc_precedes
+ (#a: eqtype)
+ (#b: Type)
+ (x: a)
+ (l: list (a * b))
+ (y: b)
+: Lemma
+ (requires (assoc x l == Some y))
+ (ensures (x << l /\ y << l))
+= assoc_memP_some x y l;
+ memP_precedes (x, y) lProperties about find
+let rec find_none
+ (#a: Type)
+ (f: (a -> Tot bool))
+ (l: list a)
+ (x: a)
+: Lemma
+ (requires (find f l == None /\ memP x l))
+ (ensures (f x == false))
+= let (x' :: l') = l in
+ Classical.or_elim
+ #(x == x')
+ #(~ (x == x'))
+ #(fun _ -> f x == false)
+ (fun h -> ())
+ (fun h -> find_none f l' x)Properties of init and last
+let rec append_init_last (#a: Type) (l: list a { Cons? l }) : Lemma
+ (l == append (init l) [last l])
+= match l with
+ | a :: q ->
+ if Cons? q
+ then
+ append_init_last q
+ else
+ ()let rec init_last_def (#a: Type) (l: list a) (x: a) : Lemma
+ (let l' = append l [x] in
+ init l' == l /\ last l' == x)
+= match l with
+ | [] -> ()
+ | y :: q -> init_last_def q xlet init_last_inj (#a: Type) (l1: list a { Cons? l1 } ) (l2: list a { Cons? l2 } ) : Lemma
+ (requires (init l1 == init l2 /\ last l1 == last l2))
+ (ensures (l1 == l2))
+= append_init_last l1;
+ append_init_last l2fsdoc: no-summary-found
-fsdoc: no-comment-found
+F* stdlib List module.
-F* standard library List module.
- Base operations *val hd:Unidentified product: [list 'a] (ML 'a)[hd l] returns the first element of [l]. Raises an exception if [l] is empty (thus, [hd] hides [List.Tot.hd] which requires [l] to be nonempty at type-checking time.) Named as in: OCaml, F#, Coq
-val tail:Unidentified product: [list 'a] (ML (list 'a))[tail l] returns [l] without its first element. Raises an exception if [l] is empty (thus, [tail] hides [List.Tot.tail] which requires [l] to be nonempty at type-checking time). Similar to: tl in OCaml, F#, Coq
-val tl:Unidentified product: [list 'a] (ML (list 'a))[tl l] returns [l] without its first element. Raises an exception if [l] is empty (thus, [tl] hides [List.Tot.tl] which requires [l] to be nonempty at type-checking time). Named as in: tl in OCaml, F#, Coq
-val last:Unidentified product: [list 'a] (ML 'a)[last l] returns the last element of [l]. Requires, at type-checking time, that [l] be nonempty. Named as in: Haskell
-val init:Unidentified product: [list 'a] (ML (list 'a))[init l] returns [l] without its last element. Requires, at type-checking time, that [l] be nonempty. Named as in: Haskell
- [nth l n] returns the [n]-th element in list [l] (with the first
-element being the 0-th) if [l] is long enough, or raises an exception
-otherwise (thus, [nth] hides [List.Tot.nth] which has [option] type.)
-Named as in: OCaml, F#, Coq Iterators *val iter:Unidentified product: [(Unidentified product: ['a] (ML unit))] Unidentified product: [list 'a] (ML unit)[iter f l] performs [f x] for each element [x] of [l], in the order in which they appear in [l]. Named as in: OCaml, F# .
-val iteri_aux:Unidentified product: [int] Unidentified product: [(Unidentified product: [int] Unidentified product: ['a] (ML unit))] Unidentified product: [list 'a] (ML unit)[iteri_aux n f l] performs, for each i, [f (i+n) x] for the i-th element [x] of [l], in the order in which they appear in [l].
-val iteri:Unidentified product: [(Unidentified product: [int] Unidentified product: ['a] (ML unit))] Unidentified product: [list 'a] (ML unit)[iteri_aux f l] performs, for each [i], [f i x] for the i-th element [x] of [l], in the order in which they appear in [l]. Named as in: OCaml
-val map:Unidentified product: [(Unidentified product: ['a] (ML 'b))] Unidentified product: [list 'a] (ML (list 'b))[map f l] applies [f] to each element of [l] and returns the list of results, in the order of the original elements in [l]. (Hides [List.Tot.map] which requires, at type-checking time, [f] to be a pure total function.) Named as in: OCaml, Coq, F#
-val mapT:Unidentified product: [(Unidentified product: ['a] (Tot 'b))] Unidentified product: [list 'a] (Tot (list 'b))[mapT f l] applies [f] to each element of [l] and returns the list of results, in the order of the original elements in [l]. Requires, at type-checking time, [f] to be a pure total function.
-val mapi_init:Unidentified product: [(Unidentified product: [int] Unidentified product: ['a] (ML 'b))] Unidentified product: [list 'a] Unidentified product: [int] (ML (list 'b))[mapi_init f n l] applies, for each [k], [f (n+k)] to the [k]-th element of [l] and returns the list of results, in the order of the original elements in [l]. (Hides [List.Tot.mapi_init] which requires, at type-checking time, [f] to be a pure total function.)
-val mapi:Unidentified product: [(Unidentified product: [int] Unidentified product: ['a] (ML 'b))] Unidentified product: [list 'a] (ML (list 'b))[mapi f l] applies, for each [k], [f k] to the [k]-th element of [l] and returns the list of results, in the order of the original elements in [l]. (Hides [List.Tot.mapi] which requires, at type-checking time, [f] to be a pure total function.) Named as in: OCaml
- [concatMap f l] applies [f] to each element of [l] and returns the
-concatenation of the results, in the order of the original elements of
-[l]. This is equivalent to [flatten (map f l)]. (Hides
-[List.Tot.concatMap], which requires, at type-checking time, [f] to be
-a pure total function.) val map2:Unidentified product: [(Unidentified product: ['a] Unidentified product: ['b] (ML 'c))] Unidentified product: [list 'a] Unidentified product: [list 'b] (ML (list 'c))[map2 f l1 l2] computes [f x1 x2] for each element x1 of [l1] and the element [x2] of [l2] at the same position, and returns the list of such results, in the order of the original elements in [l1]. Raises an exception if [l1] and [l2] have different lengths. Named as in: OCaml
-val map3:Unidentified product: [(Unidentified product: ['a] Unidentified product: ['b] Unidentified product: ['c] (ML 'd))] Unidentified product: [list 'a] Unidentified product: [list 'b] Unidentified product: [list 'c] (ML (list 'd))[map3 f l1 l2 l3] computes [f x1 x2 x3] for each element x1 of [l1] and the element [x2] of [l2] and the element [x3] of [l3] at the same position, and returns the list of such results, in the order of the original elements in [l1]. Raises an exception if [l1], [l2] and [l3] have different lengths. Named as in: OCaml
-val fold_left:Unidentified product: [(Unidentified product: ['a] Unidentified product: ['b] (ML 'a))] Unidentified product: ['a] Unidentified product: [list 'b] (ML 'a)[fold_left f x [y1; y2; ...; yn]] computes (f (... (f x y1) y2) ... yn). (Hides [List.Tot.fold_left], which requires, at type-checking time, [f] to be a pure total function.) Named as in: OCaml, Coq
-val fold_left2:Unidentified product: [(Unidentified product: ['s] Unidentified product: ['a] Unidentified product: ['b] (ML 's))] Unidentified product: ['s] Unidentified product: [list 'a] Unidentified product: [list 'b] (ML 's)[fold_left2 f x [y1; y2; ...; yn] [z1; z2; ...; zn]] computes (f (... (f x y1 z1) y2 z2 ... yn zn). Raises an exception if [y1; y2; ...] and [z1; z2; ...] have different lengths. (Thus, hides [List.Tot.fold_left2] which requires such a condition at type-checking time.) Named as in: OCaml
-val fold_right:Unidentified product: [(Unidentified product: ['a] Unidentified product: ['b] (ML 'b))] Unidentified product: [list 'a] Unidentified product: ['b] (ML 'b)[fold_right f [x1; x2; ...; xn] y] computes (f x1 (f x2 (... (f xn y)) ... )). (Hides [List.Tot.fold_right], which requires, at type-checking time, [f] to be a pure total function.) Named as in: OCaml, Coq
- List searching *val filter:Unidentified product: [(Unidentified product: ['a] (ML bool))] Unidentified product: [list 'a] (ML (list 'a))[filter f l] returns [l] with all elements [x] such that [f x] does not hold removed. (Hides [List.Tot.filter] which requires, at type-checking time, [f] to be a pure total function.) Named as in: OCaml, Coq
-val for_all:Unidentified product: [(Unidentified product: ['a] (ML bool))] Unidentified product: [list 'a] (ML bool)[for_all f l] returns [true] if, and only if, for all elements [x] appearing in [l], [f x] holds. (Hides [List.Tot.for_all], which requires, at type-checking time, [f] to be a pure total function.) Named as in: OCaml. Similar to: List.forallb in Coq
-val forall2:Unidentified product: [(Unidentified product: ['a] Unidentified product: ['b] (ML bool))] Unidentified product: [list 'a] Unidentified product: [list 'b] (ML bool)[for_all f l1 l2] returns [true] if, and only if, for all elements [x1] appearing in [l1] and the element [x2] appearing in [l2] at the same position, [f x1 x2] holds. Raises an exception if [l1] and [l2] have different lengths. Similar to: List.for_all2 in OCaml. Similar to: List.Forall2 in Coq (which is propositional)
-val collect:Unidentified product: [(Unidentified product: ['a] (ML (list 'b)))] Unidentified product: [list 'a] (ML (list 'b))[collect f l] applies [f] to each element of [l] and returns the concatenation of the results, in the order of the original elements of [l]. It is equivalent to [flatten (map f l)]. (Hides [List.Tot.collect] which requires, at type-checking time, [f] to be a pure total function.) TODO: what is the difference with [concatMap]?
-val tryFind:Unidentified product: [(Unidentified product: ['a] (ML bool))] Unidentified product: [list 'a] (ML (option 'a))[tryFind f l] returns [Some x] for some element [x] appearing in [l] such that [f x] holds, or [None] only if no such [x] exists. (Hides [List.Tot.tryFind], which requires, at type-checking time, [f] to be a pure total function.)
-val tryPick:Unidentified product: [(Unidentified product: ['a] (ML (option 'b)))] Unidentified product: [list 'a] (ML (option 'b))[tryPick f l] returns [y] for some element [x] appearing in [l] such that [f x = Some y] for some y, or [None] only if [f x = None] for all elements [x] of [l]. (Hides [List.Tot.tryPick], which requires, at type-checking time, [f] to be a pure total function.)
-val choose:Unidentified product: [(Unidentified product: ['a] (ML (option 'b)))] Unidentified product: [list 'a] (ML (list 'b))[choose f l] returns the list of [y] for all elements [x] appearing in [l] such that [f x = Some y] for some [y]. (Hides [List.Tot.choose] which requires, at type-checking time, [f] to be a pure total function.)
-val partition:Unidentified product: [(Unidentified product: ['a] (ML bool))] Unidentified product: [list 'a] (ML (*(list 'a, list 'a)))[partition f l] returns the pair of lists [(l1, l2)] where all elements [x] of [l] are in [l1] if [f x] holds, and in [l2] otherwise. Both [l1] and [l2] retain the original order of [l]. (Hides [List.Tot.partition], which requires, at type-checking time, [f] to be a pure total function.)
- List of tuples *val zip:Unidentified product: [list 'a] Unidentified product: [list 'b] (ML (list (*('a, 'b))))[zip] takes two lists [x1, ..., xn] and [y1, ..., yn] and returns the list of pairs [(x1, y1), ..., (xn, yn)]. Raises an exception if the two lists have different lengths. Named as in: Haskell
- Sorting (implemented as quicksort) *val sortWith:Unidentified product: [(Unidentified product: ['a] Unidentified product: ['a] (ML int))] Unidentified product: [list 'a] (ML (list 'a))[sortWith compare l] returns the list [l'] containing the elements of [l] sorted along the comparison function [compare], in such a way that if [compare x y > 0], then [x] appears before [y] in [l']. (Hides [List.Tot.sortWith], which requires, at type-checking time, [compare] to be a pure total function.)
-val splitAt:Unidentified product: [nat] Unidentified product: [list 'a] (ML (*(list 'a, list 'a)))[splitAt n l] returns the pair of lists [(l1, l2)] such that [l1] contains the first [n] elements of [l] and [l2] contains the rest. Raises an exception if [l] has fewer than [n] elements.
-let ((filter_map (f:Unidentified product: ['a] (ML (option 'b))) (l:list 'a)):(ML (list 'b))):let rec ((filter_map_acc (acc:list 'b) (l:list 'a)):(ML (list 'b)))=match l with [] -> rev acc | (Prims.Cons hd tl) -> match f hd with (Some hd) -> filter_map_acc ((Prims.Cons hd acc)) tl | None -> filter_map_acc acc tl in filter_map_acc (Prims.Nil ) l[filter_map f l] returns the list of [y] for all elements [x] appearing in [l] such that [f x = Some y] for some [y]. (Implemented here as a tail-recursive version of [choose]
-val index:Unidentified product: [(Unidentified product: ['a] (ML bool))] Unidentified product: [list 'a] (ML int)[index f l] returns the position index in list [l] of the first element [x] in [l] such that [f x] holds. Raises an exception if no such [x] exists. TODO: rename this function (it hides List.Tot.index which has a completely different semantics.)
+@summary F* stdlib List module.
+Base operations *
+hd l returns the first element of l. Raises an exception if
+l is empty (thus, hd hides List.Tot.hd which requires l to be
+nonempty at type-checking time.) Named as in: OCaml, F#, Coq
val hd: list 'a -> ML 'a
+let hd = function
+ | hd::tl -> hd
+ | _ -> failwith "head of empty list"tail l returns l without its first element. Raises an
+exception if l is empty (thus, tail hides List.Tot.tail which
+requires l to be nonempty at type-checking time). Similar to: tl in
+OCaml, F#, Coq
val tail: list 'a -> ML (list 'a)
+let tail = function
+ | hd::tl -> tl
+ | _ -> failwith "tail of empty list"tl l returns l without its first element. Raises an exception
+if l is empty (thus, tl hides List.Tot.tl which requires l to
+be nonempty at type-checking time). Named as in: tl in OCaml, F#, Coq
val tl : list 'a -> ML (list 'a)
+let tl l = tail llast l returns the last element of l. Requires, at
+type-checking time, that l be nonempty. Named as in: Haskell
val last: list 'a -> ML 'a
+let rec last = function
+ | [hd] -> hd
+ | _::tl -> last tl
+ | _ -> failwith "last of empty list"init l returns l without its last element. Requires, at
+type-checking time, that l be nonempty. Named as in: Haskell
val init: list 'a -> ML (list 'a)
+let rec init = function
+ | [_] -> []
+ | hd::tl -> hd::(init tl)
+ | _ -> failwith "init of empty list"nth l n returns the n-th element in list l (with the first
+element being the 0-th) if l is long enough, or raises an exception
+otherwise (thus, nth hides List.Tot.nth which has option type.)
+Named as in: OCaml, F#, Coq
val nth: list 'a -> int -> ML 'a
+let rec nth l n =
+ if n < 0 then
+ failwith "nth takes a non-negative integer as input"
+ else
+ if n = 0 then
+ match l with
+ | [] -> failwith "not enough elements"
+ | hd::_ -> hd
+ else
+ match l with
+ | [] -> failwith "not enough elements"
+ | _::tl -> nth tl (n - 1)Iterators *
+iter f l performs f x for each element x of l, in the
+order in which they appear in l. Named as in: OCaml, F# .
val iter: ('a -> ML unit) -> list 'a -> ML unit
+let rec iter f x = match x with
+ | [] -> ()
+ | a::tl -> let _ = f a in iter f tliteri_aux n f l performs, for each i, f (i+n) x for the i-th
+element x of l, in the order in which they appear in l.
val iteri_aux: int -> (int -> 'a -> ML unit) -> list 'a -> ML unit
+let rec iteri_aux i f x = match x with
+ | [] -> ()
+ | a::tl -> f i a; iteri_aux (i+1) f tliteri_aux f l performs, for each i, f i x for the i-th
+element x of l, in the order in which they appear in l. Named as
+in: OCaml
val iteri: (int -> 'a -> ML unit) -> list 'a -> ML unit
+let iteri f x = iteri_aux 0 f xmap f l applies f to each element of l and returns the list
+of results, in the order of the original elements in l. (Hides
+List.Tot.map which requires, at type-checking time, f to be a pure
+total function.) Named as in: OCaml, Coq, F#
val map: ('a -> ML 'b) -> list 'a -> ML (list 'b)
+let rec map f x = match x with
+ | [] -> []
+ | a::tl -> f a::map f tlmapT f l applies f to each element of l and returns the list
+of results, in the order of the original elements in l. Requires, at
+type-checking time, f to be a pure total function.
val mapT: ('a -> Tot 'b) -> list 'a -> Tot (list 'b)
+let mapT = FStar.List.Tot.mapmapi_init f n l applies, for each k, f (n+k) to the k-th
+element of l and returns the list of results, in the order of the
+original elements in l. (Hides List.Tot.mapi_init which requires,
+at type-checking time, f to be a pure total function.)
val mapi_init: (int -> 'a -> ML 'b) -> list 'a -> int -> ML (list 'b)
+let rec mapi_init f l i = match l with
+ | [] -> []
+ | hd::tl -> (f i hd)::(mapi_init f tl (i+1))mapi f l applies, for each k, f k to the k-th element of
+l and returns the list of results, in the order of the original
+elements in l. (Hides List.Tot.mapi which requires, at
+type-checking time, f to be a pure total function.) Named as in:
+OCaml
val mapi: (int -> 'a -> ML 'b) -> list 'a -> ML (list 'b)
+let mapi f l = mapi_init f l 0concatMap f l applies f to each element of l and returns the
+concatenation of the results, in the order of the original elements of
+l. This is equivalent to flatten (map f l). (Hides
+List.Tot.concatMap, which requires, at type-checking time, f to be
+a pure total function.)
val concatMap: ('a -> ML (list 'b)) -> list 'a -> ML (list 'b)
+let rec concatMap f = function
+ | [] -> []
+ | a::tl ->
+ let fa = f a in
+ let ftl = concatMap f tl in
+ fa @ ftlmap2 f l1 l2 computes f x1 x2 for each element x1 of l1 and
+the element x2 of l2 at the same position, and returns the list of
+such results, in the order of the original elements in l1. Raises an
+exception if l1 and l2 have different lengths. Named as in: OCaml
val map2: ('a -> 'b -> ML 'c) -> list 'a -> list 'b -> ML (list 'c)
+let rec map2 f l1 l2 = match l1, l2 with
+ | [], [] -> []
+ | hd1::tl1, hd2::tl2 -> (f hd1 hd2)::(map2 f tl1 tl2)
+ | _, _ -> failwith "The lists do not have the same length"map3 f l1 l2 l3 computes f x1 x2 x3 for each element x1 of
+l1 and the element x2 of l2 and the element x3 of l3 at the
+same position, and returns the list of such results, in the order of
+the original elements in l1. Raises an exception if l1, l2 and
+l3 have different lengths. Named as in: OCaml
val map3: ('a -> 'b -> 'c -> ML 'd) -> list 'a -> list 'b -> list 'c -> ML (list 'd)
+let rec map3 f l1 l2 l3 = match l1, l2, l3 with
+ | [], [], [] -> []
+ | hd1::tl1, hd2::tl2, hd3::tl3 -> (f hd1 hd2 hd3)::(map3 f tl1 tl2 tl3)
+ | _, _, _ -> failwith "The lists do not have the same length"fold_left f x y1; y2; ...; yn`` computes (f (... (f x y1) y2)
+... yn). (Hides List.Tot.fold_left, which requires, at type-checking
+time, `f` to be a pure total function.) Named as in: OCaml, Coq
val fold_left: ('a -> 'b -> ML 'a) -> 'a -> list 'b -> ML 'a
+let rec fold_left f x y = match y with
+ | [] -> x
+ | hd::tl -> fold_left f (f x hd) tlfold_left2 f x y1; y2; ...; yn z1; z2; ...; zn`` computes (f
+(... (f x y1 z1) y2 z2 ... yn zn). Raises an exception if [y1; y2;
+...andz1; z2; ...` have different lengths. (Thus, hides
+`List.Tot.fold_left2` which requires such a condition at type-checking
+time.) Named as in: OCaml
val fold_left2: ('s -> 'a -> 'b -> ML 's) -> 's -> list 'a -> list 'b -> ML 's
+let rec fold_left2 f a l1 l2 = match l1, l2 with
+ | [], [] -> a
+ | hd1::tl1, hd2::tl2 -> fold_left2 f (f a hd1 hd2) tl1 tl2
+ | _, _ -> failwith "The lists do not have the same length"fold_right f x1; x2; ...; xn y computes (f x1 (f x2 (... (f xn
+y)) ... )). (Hides List.Tot.fold_right, which requires, at
+type-checking time, f to be a pure total function.) Named as in:
+OCaml, Coq
val fold_right: ('a -> 'b -> ML 'b) -> list 'a -> 'b -> ML 'b
+let rec fold_right f l x = match l with
+ | [] -> x
+ | hd::tl -> f hd (fold_right f tl x)List searching *
+filter f l returns l with all elements x such that f x
+does not hold removed. (Hides List.Tot.filter which requires, at
+type-checking time, f to be a pure total function.) Named as in:
+OCaml, Coq
val filter: ('a -> ML bool) -> list 'a -> ML (list 'a)
+let rec filter f = function
+ | [] -> []
+ | hd::tl -> if f hd then hd::(filter f tl) else filter f tlfor_all f l returns true if, and only if, for all elements x
+appearing in l, f x holds. (Hides List.Tot.for_all, which
+requires, at type-checking time, f to be a pure total function.)
+Named as in: OCaml. Similar to: List.forallb in Coq
val for_all: ('a -> ML bool) -> list 'a -> ML bool
+let rec for_all f l = match l with
+ | [] -> true
+ | hd::tl -> if f hd then for_all f tl else falsefor_all f l1 l2 returns true if, and only if, for all elements
+x1 appearing in l1 and the element x2 appearing in l2 at the
+same position, f x1 x2 holds. Raises an exception if l1 and l2
+have different lengths. Similar to: List.for_all2 in OCaml. Similar
+to: List.Forall2 in Coq (which is propositional)
val forall2: ('a -> 'b -> ML bool) -> list 'a -> list 'b -> ML bool
+let rec forall2 f l1 l2 = match l1,l2 with
+ | [], [] -> true
+ | hd1::tl1, hd2::tl2 -> if f hd1 hd2 then forall2 f tl1 tl2 else false
+ | _, _ -> failwith "The lists do not have the same length"collect f l applies f to each element of l and returns the
+concatenation of the results, in the order of the original elements of
+l. It is equivalent to flatten (map f l). (Hides
+List.Tot.collect which requires, at type-checking time, f to be a
+pure total function.) TODO: what is the difference with concatMap?
val collect: ('a -> ML (list 'b)) -> list 'a -> ML (list 'b)
+let rec collect f l = match l with
+ | [] -> []
+ | hd::tl -> append (f hd) (collect f tl)tryFind f l returns Some x for some element x appearing in
+l such that f x holds, or None only if no such x
+exists. (Hides List.Tot.tryFind, which requires, at type-checking
+time, f to be a pure total function.)
val tryFind: ('a -> ML bool) -> list 'a -> ML (option 'a)
+let rec tryFind p l = match l with
+ | [] -> None
+ | hd::tl -> if p hd then Some hd else tryFind p tltryPick f l returns y for some element x appearing in l
+such that f x = Some y for some y, or None only if f x = None
+for all elements x of l. (Hides List.Tot.tryPick, which
+requires, at type-checking time, f to be a pure total function.)
val tryPick: ('a -> ML (option 'b)) -> list 'a -> ML (option 'b)
+let rec tryPick f l = match l with
+ | [] -> None
+ | hd::tl ->
+ match f hd with
+ | Some x -> Some x
+ | None -> tryPick f tlchoose f l returns the list of y for all elements x
+appearing in l such that f x = Some y for some y. (Hides
+List.Tot.choose which requires, at type-checking time, f to be a
+pure total function.)
val choose: ('a -> ML (option 'b)) -> list 'a -> ML (list 'b)
+let rec choose f l = match l with
+ | [] -> []
+ | hd::tl ->
+ match f hd with
+ | Some x -> x::(choose f tl)
+ | None -> choose f tlpartition f l returns the pair of lists (l1, l2) where all
+elements x of l are in l1 if f x holds, and in l2
+otherwise. Both l1 and l2 retain the original order of l. (Hides
+List.Tot.partition, which requires, at type-checking time, f to be
+a pure total function.)
val partition: ('a -> ML bool) -> list 'a -> ML (list 'a * list 'a)
+let rec partition f = function
+ | [] -> [], []
+ | hd::tl ->
+ let l1, l2 = partition f tl in
+ if f hd
+ then hd::l1, l2
+ else l1, hd::l2List of tuples *
+zip takes two lists x1, ..., xn and y1, ..., yn and returns
+the list of pairs (x1, y1), ..., (xn, yn). Raises an exception if
+the two lists have different lengths. Named as in: Haskell
val zip: list 'a -> list 'b -> ML (list ('a * 'b))
+let rec zip l1 l2 = match l1,l2 with
+ | [], [] -> []
+ | hd1::tl1, hd2::tl2 -> (hd1,hd2)::(zip tl1 tl2)
+ | _, _ -> failwith "The lists do not have the same length"Sorting (implemented as quicksort) *
+sortWith compare l returns the list l' containing the elements
+of l sorted along the comparison function compare, in such a way
+that if compare x y > 0, then x appears before y in l'. (Hides
+List.Tot.sortWith, which requires, at type-checking time, compare
+to be a pure total function.)
val sortWith: ('a -> 'a -> ML int) -> list 'a -> ML (list 'a)
+let rec sortWith f = function
+ | [] -> []
+ | pivot::tl ->
+ let hi, lo = partition (fun x -> f pivot x > 0) tl in
+ sortWith f lo@(pivot::sortWith f hi)splitAt n l returns the pair of lists (l1, l2) such that l1
+contains the first n elements of l and l2 contains the
+rest. Raises an exception if l has fewer than n elements.
val splitAt: nat -> list 'a -> ML (list 'a * list 'a)
+let rec splitAt n l =
+ if n = 0 then [], l
+ else
+ match l with
+ | [] -> failwith "splitAt index is more that list length"
+ | hd::tl ->
+ let l1, l2 = splitAt (n - 1) tl in
+ hd::l1, l2filter_map f l returns the list of y for all elements x
+appearing in l such that f x = Some y for some y. (Implemented
+here as a tail-recursive version of choose
let filter_map (f:'a -> ML (option 'b)) (l:list 'a) : ML (list 'b) =
+ let rec filter_map_acc (acc:list 'b) (l:list 'a) : ML (list 'b) =
+ match l with
+ | [] ->
+ rev acc
+ | hd :: tl ->
+ match f hd with
+ | Some hd ->
+ filter_map_acc (hd :: acc) tl
+ | None ->
+ filter_map_acc acc tl
+ in
+ filter_map_acc [] lindex f l returns the position index in list l of the first
+element x in l such that f x holds. Raises an exception if no
+such x exists. TODO: rename this function (it hides List.Tot.index
+which has a completely different semantics.)
val index: ('a -> ML bool) -> list 'a -> ML int
+let index f l =
+ let rec index l i : ML int =
+ match l with
+ | [] ->
+ failwith "List.index: not found"
+ | hd :: tl ->
+ if f hd then
+ i
+ else
+ index tl (i + 1)
+ in
+ index l 0fsdoc: no-summary-found
-fsdoc: no-comment-found
+Opens module FStar.Heap
+Opens module FStar.ST
+Opens module FStar.Preorder
+let stable = FStar.Preorder.stableval token (#a:Type) (#b:preorder a) (r:mref a b) (p:(a -> Type){stable p b}) : Type0val witness_token: #a:Type -> #b:preorder a -> m:mref a b -> p:(a -> Type){stable p b}
+ -> ST unit (requires (fun h0 -> p (sel h0 m)))
+ (ensures (fun h0 _ h1 -> h0==h1 /\ token m p))val recall_token: #a:Type -> #b:preorder a -> m:mref a b -> p:(a -> Type){stable p b}
+ -> ST unit (requires (fun _ -> token m p))
+ (ensures (fun h0 _ h1 -> h0==h1 /\ p (sel h1 m)))let spred (#a:Type) (rel:preorder a) = p:(a -> Type){Preorder.stable p rel}val lemma_functoriality (#a:Type) (#rel:preorder a) (r:mref a rel) (p q:spred rel)
+ : Lemma (requires (token r p /\ (forall x. p x ==> q x)))
+ (ensures (token r q))KM : These don't have much to do here...
+val recall: p:(heap -> Type){ST.stable p} ->
+ ST unit
+ (requires (fun _ -> witnessed p))
+ (ensures (fun h0 _ h1 -> h0 == h1 /\ p h1))val witness: p:(heap -> Type){ST.stable p} ->
+ ST unit
+ (requires (fun h0 -> p h0))
+ (ensures (fun h0 _ h1 -> h0==h1 /\ witnessed p))FStar.Map provides a polymorphic, partial map from keys to
-values, where keys support decidable equality.
+S
+@summary FStar.Map provides a polymorphic, partial map from keys to
+values, where keys support decidable equality.m:Map.t key value is a partial map from key to value
A distinctive feature of the library is in its model of partiality.
-A map can be seen as a pair of: 1. a total map key -> Tot value 2. a set of keys that record the domain of the map
* Extensional equality **A map can be seen as a pair of:
+key -> Tot value
+Map.t key value: The main type provided by this module
+val t (key:eqtype) (value:Type u#a)
+ : Type u#asel m k : Look up key k in map m
val sel: #key:eqtype -> #value:Type -> t key value -> key -> Tot valueupd m k v : A map identical to m except mapping k to v
val upd: #key:eqtype -> #value:Type -> t key value -> key -> value -> Tot (t key value)const v : A constant map mapping all keys to v
val const: #key:eqtype -> #value:Type -> value -> Tot (t key value)domain m : The set of keys on which this partial map is defined
+val domain: #key:eqtype -> #value:Type -> t key value -> Tot (S.set key)contains m k: Decides if key k is in the map m
val contains: #key:eqtype -> #value:Type -> t key value -> key -> Tot boolconcat m1 m2 :
+A map whose domain is the union of the domains of m1 and m2.
Maps every key `k` in the domain of `m1` to `sel m1 k`
+ and all other keys to `sel m2 k`.
+
+val concat: #key:eqtype -> #value:Type -> t key value -> t key value -> Tot (t key value)map_val f m:
+A map whose domain is the same as m but all values have
+f applied to them.
val map_val: #val1:Type -> #val2:Type -> f:(val1 -> val2) -> #key:eqtype -> t key val1 -> Tot (t key val2)restrict s m:
+Restricts the domain of m to (domain m intersect s)
val restrict: #key:eqtype -> #value:Type -> S.set key -> t key value -> Tot (t key value)const_on dom v: A defined notion, for convenience +A partial constant map on dom
+let const_on (#key:eqtype) (#value:Type) (dom:S.set key) (v:value)
+ : t key value
+ = restrict dom (const v)disjoint_dom m1 m2:
+Disjoint domains. TODO: its pattern is biased towards m1. Why?
let disjoint_dom (#key:eqtype) (#value:Type) (m1:t key value) (m2:t key value)
+ = forall x.{:pattern (contains m1 x)(* ; (contains m2 x) *)} contains m1 x ==> not (contains m2 x)has_dom m dom: A relational version of the domain m function
let has_dom (#key:eqtype) (#value:Type) (m:t key value) (dom:S.set key)
+ = forall x. contains m x <==> S.mem x domProperties about map functions
+val lemma_SelUpd1: #key:eqtype -> #value:Type -> m:t key value -> k:key -> v:value ->
+ Lemma (requires True) (ensures (sel (upd m k v) k == v))
+ [SMTPat (sel (upd m k v) k)]val lemma_SelUpd2: #key:eqtype -> #value:Type -> m:t key value -> k1:key -> k2:key -> v:value ->
+ Lemma (requires True) (ensures (k2=!=k1 ==> sel (upd m k2 v) k1 == sel m k1))
+ [SMTPat (sel (upd m k2 v) k1)]val lemma_SelConst: #key:eqtype -> #value:Type -> v:value -> k:key ->
+ Lemma (requires True) (ensures (sel (const v) k == v))
+ [SMTPat (sel (const v) k)]val lemma_SelRestrict: #key:eqtype -> #value:Type -> m:t key value -> ks:S.set key -> k:key ->
+ Lemma (requires True) (ensures (S.mem k ks ==> sel (restrict ks m) k == sel m k))
+ [SMTPat (sel (restrict ks m) k)]val lemma_SelConcat1: #key:eqtype -> #value:Type -> m1:t key value -> m2:t key value -> k:key ->
+ Lemma (requires True) (ensures (contains m2 k ==> sel (concat m1 m2) k==sel m2 k))
+ [SMTPat (sel (concat m1 m2) k)]val lemma_SelConcat2: #key:eqtype -> #value:Type -> m1:t key value -> m2:t key value -> k:key ->
+ Lemma (requires True) (ensures (not(contains m2 k) ==> sel (concat m1 m2) k==sel m1 k))
+ [SMTPat (sel (concat m1 m2) k)]val lemma_SelMapVal: #val1:Type -> #val2:Type -> f:(val1 -> val2) -> #key:eqtype -> m:t key val1 -> k:key ->
+ Lemma (requires True) (ensures (sel (map_val f m) k == f (sel m k)))
+ [SMTPat (sel (map_val f m) k)]val lemma_InDomUpd1: #key:eqtype -> #value:Type -> m:t key value -> k1:key -> k2:key -> v:value ->
+ Lemma (requires True) (ensures (contains (upd m k1 v) k2 == (k1=k2 || contains m k2)))
+ [SMTPat (contains (upd m k1 v) k2)]val lemma_InDomUpd2: #key:eqtype -> #value:Type -> m:t key value -> k1:key -> k2:key -> v:value ->
+ Lemma (requires True) (ensures (k2=!=k1 ==> contains (upd m k2 v) k1 == contains m k1))
+ [SMTPat (contains (upd m k2 v) k1)]val lemma_InDomConstMap: #key:eqtype -> #value:Type -> v:value -> k:key ->
+ Lemma (requires True) (ensures (contains (const v) k))
+ [SMTPat (contains (const v) k)]val lemma_InDomConcat: #key:eqtype -> #value:Type -> m1:t key value -> m2:t key value -> k:key ->
+ Lemma (requires True) (ensures (contains (concat m1 m2) k==(contains m1 k || contains m2 k)))
+ [SMTPat (contains (concat m1 m2) k)]val lemma_InMapVal: #val1:Type -> #val2:Type -> f:(val1 -> val2) -> #key:eqtype -> m:t key val1 -> k:key ->
+ Lemma (requires True) (ensures (contains (map_val f m) k == contains m k))
+ [SMTPat (contains (map_val f m) k)]val lemma_InDomRestrict: #key:eqtype -> #value:Type -> m:t key value -> ks:S.set key -> k:key ->
+ Lemma (requires True) (ensures (contains (restrict ks m) k == (S.mem k ks && contains m k)))
+ [SMTPat (contains (restrict ks m) k)]val lemma_ContainsDom: #key:eqtype -> #value:Type -> m:t key value -> k:key ->
+ Lemma (requires True) (ensures (contains m k = S.mem k (domain m)))
+ [SMTPatOr[[SMTPat (contains m k)]; [SMTPat (S.mem k (domain m))]]]val lemma_UpdDomain : #key:eqtype -> #value:Type -> m:t key value -> k:key -> v:value ->
+ Lemma (requires True)
+ (ensures (S.equal (domain (upd m k v)) (S.union (domain m) (S.singleton k))))
+ [SMTPat (domain (upd m k v))]equal m1 m2:
+Maps m1 and m2 have the same domain and
+and are pointwise equal on that domain.
val equal (#key:eqtype) (#value:Type) (m1:t key value) (m2:t key value) : proplemma_equal_intro:
+Introducing equal m1 m2 by showing maps to be pointwise equal on the same domain
val lemma_equal_intro: #key:eqtype -> #value:Type -> m1:t key value -> m2:t key value ->
+ Lemma (requires (forall k. sel m1 k == sel m2 k /\
+ contains m1 k = contains m2 k))
+ (ensures (equal m1 m2))
+ [SMTPat (equal m1 m2)]lemma_equal_elim:
+Eliminating equal m1 m2 to provable equality of maps
+Internally, this involves a use of functional extensionality
val lemma_equal_elim: #key:eqtype -> #value:Type -> m1:t key value -> m2:t key value ->
+ Lemma (ensures (equal m1 m2 <==> m1 == m2))
+ [SMTPat (equal m1 m2)][@@(deprecated "use lemma_equal_elim")]
+val lemma_equal_refl: #key:eqtype -> #value:Type -> m1:t key value -> m2:t key value ->
+ Lemma (requires (m1 == m2))
+ (ensures (equal m1 m2))fsdoc: no-summary-found
-fsdoc: no-comment-found
+assume val markovs_principle : p:(nat -> Tot bool) -> Ghost nat
+ (requires (~(forall (n:nat). ~(p n))))
+ (ensures (fun n -> p n))here is a stronger variant of Markov's principle +(might be as strong as indefinite description?)
+assume val stronger_markovs_principle : p:(nat -> GTot bool) -> Ghost nat
+ (requires (~(forall (n:nat). ~(p n))))
+ (ensures (fun n -> p n))fsdoc: no-summary-found
-fsdoc: no-comment-found
+#set-options "--fuel 0 --ifuel 0 --z3rlimit 40"++Divides relation
+It is reflexive, transitive, and antisymmetric up to sign. +When a <> 0, a
+dividesb iff a % b = 0 (this is proved below)
let divides (a b:int) : prop = exists q. b = q * aval divides_reflexive (a:int) : Lemma (a `divides` a) [SMTPat (a `divides` a)]val divides_transitive (a b c:int) : Lemma
+ (requires a `divides` b /\ b `divides` c)
+ (ensures a `divides` c)val divide_antisym (a b:int) : Lemma
+ (requires a `divides` b /\ b `divides` a)
+ (ensures a = b \/ a = -b)val divides_0 (a:int) : Lemma (a `divides` 0)val divides_1 (a:int) : Lemma (requires a `divides` 1) (ensures a = 1 \/ a = -1)val divides_minus (a b:int) : Lemma
+ (requires a `divides` b)
+ (ensures a `divides` (-b))val divides_opp (a b:int) : Lemma
+ (requires a `divides` b)
+ (ensures (-a) `divides` b)val divides_plus (a b d:int) : Lemma
+ (requires d `divides` a /\ d `divides` b)
+ (ensures d `divides` (a + b))val divides_sub (a b d:int) : Lemma
+ (requires d `divides` a /\ d `divides` b)
+ (ensures d `divides` (a - b))val divides_mult_right (a b d:int) : Lemma
+ (requires d `divides` b)
+ (ensures d `divides` (a * b))++Greatest Common Divisor (GCD) relation
+We deviate from the standard definition in that we allow the divisor to +be negative. Thus, the GCD of two integers is unique up to sign.
+
let is_gcd (a b d:int) : prop =
+ d `divides` a /\
+ d `divides` b /\
+ (forall x. (x `divides` a /\ x `divides` b) ==> x `divides` d)val mod_divides (a:int) (b:nonzero) : Lemma (requires a % b = 0) (ensures b `divides` a)val divides_mod (a:int) (b:nonzero) : Lemma (requires b `divides` a) (ensures a % b = 0)val is_gcd_unique (a b c d:int) : Lemma
+ (requires is_gcd a b c /\ is_gcd a b d)
+ (ensures c = d \/ c = -d)val is_gcd_reflexive (a:int) : Lemma (is_gcd a a a)val is_gcd_symmetric (a b d:int) : Lemma
+ (requires is_gcd a b d)
+ (ensures is_gcd b a d)val is_gcd_0 (a:int) : Lemma (is_gcd a 0 a)val is_gcd_1 (a:int) : Lemma (is_gcd a 1 1)val is_gcd_minus (a b d:int) : Lemma
+ (requires is_gcd a (-b) d)
+ (ensures is_gcd b a d)val is_gcd_opp (a b d:int) : Lemma
+ (requires is_gcd a b d)
+ (ensures is_gcd b a (-d))val is_gcd_plus (a b q d:int) : Lemma
+ (requires is_gcd a b d)
+ (ensures is_gcd a (b + q * a) d)++Extended Euclidean algorithm
+Computes the GCD of two integers (a, b) together with Bézout coefficients +(r, s) satisfying r a + s b = gcd(a, b)
+
val euclid_gcd (a b:int) : Pure (int & int & int)
+ (requires True)
+ (ensures fun (r, s, d) -> r * a + s * b = d /\ is_gcd a b d)++A definition of primality based on the divides relation
+
let is_prime (p:int) =
+ 1 < p /\
+ (forall (d:int).{:pattern (d `divides` p)}
+ (d `divides` p ==> (d = 1 \/ d = -1 \/ d = p \/ d = -p)))val bezout_prime (p:int) (a:pos{a < p}) : Pure (int & int)
+ (requires is_prime p)
+ (ensures fun (r, s) -> r * p + s * a = 1)++Euclid's lemma and its generalization to arbitrary integers
++
+- If a prime p divides a*b, then it must divide at least one of a or b
+- If n divides a*b and a,n are coprime then n divides b
+
val euclid (n:pos) (a b r s:int) : Lemma
+ (requires (a * b) % n = 0 /\ r * n + s * a = 1)
+ (ensures b % n = 0)val euclid_prime (p:int{is_prime p}) (a b:int) : Lemma
+ (requires (a * b) % p = 0)
+ (ensures a % p = 0 \/ b % p = 0)fsdoc: no-summary-found
-fsdoc: no-comment-found
+++Fermat's Little Theorem (and Binomial Theorem)
+Proven by induction from the Freshman's dream identity
+pow (a + b) p % p = (pow a p + pow b p) % p
+which follows from the Binomial Theorem
+pow (a + b) n = sum_{i=0}^n (binomial n k * pow a (n - i) * pow b i)
+which in turn can be proved by induction from Pascal's identity
+binomial n k + binomial n (k - 1) = binomial (n + 1) k
+See +https://github.com/coqtail/coqtail/blob/master/src/Hierarchy/Commutative_ring_binomial.v +https://github.com/coq-contribs/rsa/blob/master/Binomials.v
+
#set-options "--fuel 0 --ifuel 0"let rec pow (a:int) (k:nat) : int =
+ if k = 0 then 1
+ else a * pow a (k - 1)val fermat (p:int{is_prime p}) (a:int) : Lemma (pow a p % p == a % p)val mod_mult_congr (p:int{is_prime p}) (a b c:int) : Lemma
+ (requires (a * c) % p = (b * c) % p /\ c % p <> 0)
+ (ensures a % p = b % p)val fermat_alt (p:int{is_prime p}) (a:int{a % p <> 0}) : Lemma (pow a (p - 1) % p == 1)fsdoc: no-summary-found
-fsdoc: no-comment-found
-val modulo_lemma:Unidentified product: [a:nat] Unidentified product: [b:pos] (Lemma ((requires (<(a, b)))) ((ensures (=(%(a, b), a)))))#push-options "--fuel 0 --ifuel 0"Lemma: definition of Euclidean division
+val euclidean_div_axiom: a:int -> b:pos -> Lemma
+ (a - b * (a / b) >= 0 /\ a - b * (a / b) < b)
+let euclidean_div_axiom a b = ()val lemma_eucl_div_bound: a:int -> b:int -> q:int -> Lemma
+ (requires (a < q))
+ (ensures (a + q * b < q * (b+1)))
+let lemma_eucl_div_bound a b q = ()val lemma_mult_le_left: a:nat -> b:int -> c:int -> Lemma
+ (requires (b <= c))
+ (ensures (a * b <= a * c))
+let lemma_mult_le_left a b c = ()val lemma_mult_le_right: a:nat -> b:int -> c:int -> Lemma
+ (requires (b <= c))
+ (ensures (b * a <= c * a))
+let lemma_mult_le_right a b c = ()val lemma_mult_lt_left: a:pos -> b:int -> c:int -> Lemma
+ (requires (b < c))
+ (ensures (a * b < a * c))
+let lemma_mult_lt_left a b c = ()val lemma_mult_lt_right: a:pos -> b:int -> c:int -> Lemma
+ (requires (b < c))
+ (ensures (b * a < c * a))
+let lemma_mult_lt_right a b c = ()let lemma_mult_lt_sqr (n:nat) (m:nat) (k:nat{n < k && m < k})
+ : Lemma (n * m < k * k) =
+ calc (<=) {
+ n * m;
+ <= { lemma_mult_le_left n m (k - 1) }
+ n * (k - 1);
+ <= { lemma_mult_le_right (k - 1) n (k - 1) }
+ (k - 1) * (k - 1);
+ <= {}
+ k*k - 1;
+ }Lemma: multiplication on integers is commutative
+val swap_mul: a:int -> b:int -> Lemma (a * b = b * a)
+let swap_mul a b = ()val lemma_cancel_mul (a b : int) (n : pos) : Lemma (requires (a * n = b * n)) (ensures (a = b))
+let lemma_cancel_mul a b n = ()Lemma: multiplication is right distributive over addition
+val distributivity_add_left: a:int -> b:int -> c:int -> Lemma
+ ((a + b) * c = a * c + b * c)
+let distributivity_add_left a b c = ()Lemma: multiplication is left distributive over addition
+val distributivity_add_right: a:int -> b:int -> c:int -> Lemma
+ (a * (b + c) = a * b + a * c)
+let distributivity_add_right a b c =
+ calc (==) {
+ a * (b + c);
+ == {}
+ (b + c) * a;
+ == { distributivity_add_left b c a }
+ b * a + c * a;
+ == {}
+ a * b + a * c;
+ }Lemma: multiplication is associative, hence parenthesizing is meaningless
+GM: This is really just an identity since the LHS is associated to the left
+val paren_mul_left: a:int -> b:int -> c:int -> Lemma
+ (a * b * c = (a * b) * c)
+let paren_mul_left a b c = ()Lemma: multiplication is associative, hence parenthesizing is meaningless
+val paren_mul_right: a:int -> b:int -> c:int -> Lemma
+ (a * b * c = a * (b * c))
+let paren_mul_right a b c = ()Lemma: addition is associative, hence parenthesizing is meaningless
+val paren_add_left: a:int -> b:int -> c:int -> Lemma
+ (a + b + c = (a + b) + c)
+let paren_add_left a b c = ()Lemma: addition is associative, hence parenthesizing is meaningless
+val paren_add_right: a:int -> b:int -> c:int -> Lemma
+ (a + b + c = a + (b + c))
+let paren_add_right a b c = ()val addition_is_associative: a:int -> b:int -> c:int -> Lemma
+ (a + b + c = (a + b) + c /\ a + b + c = a + (b + c))
+let addition_is_associative a b c = ()val subtraction_is_distributive: a:int -> b:int -> c:int -> Lemma
+ (a - b + c = (a - b) + c /\
+ a - b - c = a - (b + c) /\
+ a - b - c = (a - b) - c /\
+ a + (-b - c) = a - b - c /\
+ a - (b - c) = a - b + c)
+let subtraction_is_distributive a b c = ()val swap_add_plus_minus: a:int -> b:int -> c:int -> Lemma
+ (a + b - c = (a - c) + b)
+let swap_add_plus_minus a b c = ()Lemma: minus applies to the whole term
+val neg_mul_left: a:int -> b:int -> Lemma (-(a * b) = (-a) * b)
+let neg_mul_left a b = ()Lemma: minus applies to the whole term
+val neg_mul_right: a:int -> b:int -> Lemma (-(a * b) = a * (-b))
+let neg_mul_right a b = ()val swap_neg_mul: a:int -> b:int -> Lemma ((-a) * b = a * (-b))
+let swap_neg_mul a b =
+ neg_mul_left a b;
+ neg_mul_right a bLemma: multiplication is left distributive over substraction
+val distributivity_sub_left: a:int -> b:int -> c:int ->
+ Lemma ((a - b) * c = a * c - b * c)
+let distributivity_sub_left a b c =
+ calc (==) {
+ (a - b) * c;
+ == {}
+ (a + (-b)) * c;
+ == { distributivity_add_left a (-b) c }
+ a * c + (-b) * c;
+ == { neg_mul_left b c }
+ a * c - b * c;
+ }Lemma: multiplication is right distributive over substraction
+val distributivity_sub_right: a:int -> b:int -> c:int ->
+ Lemma ((a * (b - c) = a * b - a * c))
+let distributivity_sub_right a b c =
+ calc (==) {
+ a * (b - c);
+ == {}
+ a * (b + (-c));
+ == { distributivity_add_right a b (-c) }
+ a * b + a * (-c);
+ == { neg_mul_right a c }
+ a * b - a * c;
+ }Lemma: multiplication precedence on addition
+val mul_binds_tighter: a:int -> b:int -> c:int -> Lemma (a + (b * c) = a + b * c)
+let mul_binds_tighter a b c = ()val lemma_abs_mul : a:int -> b:int -> Lemma (abs a * abs b = abs (a * b))
+let lemma_abs_mul a b = ()val lemma_abs_bound : a:int -> b:nat -> Lemma (abs a < b <==> -b < a /\ a < b)
+let lemma_abs_bound a b = ()Lemma: multiplication keeps symetric bounds : +b > 0 && d > 0 && -b < a < b && -d < c < d ==> - b * d < a * c < b * d
+val mul_ineq1: a:int -> b:nat -> c:int -> d:nat -> Lemma
+ (requires (-b < a /\ a < b /\
+ -d < c /\ c < d))
+ (ensures (-(b * d) < a * c /\ a * c < b * d))
+let mul_ineq1 a b c d =
+ if a = 0 || c = 0 then ()
+ else begin
+ lemma_abs_bound a b;
+ lemma_abs_bound c d;
+ lemma_abs_mul a c;
+ lemma_mult_lt_left (abs a) (abs c) d;
+ lemma_mult_lt_right d (abs a) b;
+ lemma_abs_bound (a * c) (b * d);
+ ()
+ endZero is neutral for addition
+let add_zero_left_is_same (n : int) : Lemma(0 + n = n) = ()
+let add_zero_right_is_same (n : int) : Lemma(n + 0 = n) = ()One is neutral for multiplication
+let mul_one_left_is_same (n : int) : Lemma(1 * n = n) = ()
+let mul_one_right_is_same (n : int) : Lemma(n * 1 = n) = ()Multiplying by zero gives zero
+let mul_zero_left_is_zero (n : int) : Lemma(0 * n = 0) = ()
+let mul_zero_right_is_zero (n : int) : Lemma(n * 0 = 0) = ()val nat_times_nat_is_nat: a:nat -> b:nat -> Lemma (a * b >= 0)
+let nat_times_nat_is_nat a b = ()val pos_times_pos_is_pos: a:pos -> b:pos -> Lemma (a * b > 0)
+let pos_times_pos_is_pos a b = ()val nat_over_pos_is_nat: a:nat -> b:pos -> Lemma (a / b >= 0)
+let nat_over_pos_is_nat a b = ()val nat_plus_nat_equal_zero_lemma: a:nat -> b:nat{a + b = 0} -> Lemma(a = 0 /\ b = 0)
+let nat_plus_nat_equal_zero_lemma a b = ()val int_times_int_equal_zero_lemma: a:int -> b:int{a * b = 0} -> Lemma(a = 0 \/ b = 0)
+let int_times_int_equal_zero_lemma a b = ()#push-options "--fuel 1"
+val pow2_double_sum: n:nat -> Lemma (pow2 n + pow2 n = pow2 (n + 1))
+let pow2_double_sum n = ()val pow2_double_mult: n:nat -> Lemma (2 * pow2 n = pow2 (n + 1))
+let pow2_double_mult n = pow2_double_sum nval pow2_lt_compat: n:nat -> m:nat -> Lemma
+ (requires (m < n))
+ (ensures (pow2 m < pow2 n))
+ (decreases m)
+let rec pow2_lt_compat n m =
+ match m with
+ | 0 -> ()
+ | _ -> pow2_lt_compat (n-1) (m-1)
+#pop-optionsval pow2_le_compat: n:nat -> m:nat -> Lemma
+ (requires (m <= n))
+ (ensures (pow2 m <= pow2 n))
+let pow2_le_compat n m =
+ if m < n then pow2_lt_compat n m#push-options "--fuel 1"
+val pow2_plus: n:nat -> m:nat -> Lemma
+ (ensures (pow2 n * pow2 m = pow2 (n + m)))
+ (decreases n)
+let rec pow2_plus n m =
+ match n with
+ | 0 -> ()
+ | _ -> pow2_plus (n - 1) m
+#pop-optionsLemma : definition of the exponential property of pow2
+val pow2_minus: n:nat -> m:nat{ n >= m } -> Lemma
+ ((pow2 n) / (pow2 m) = pow2 (n - m))
+let pow2_minus n m =
+ pow2_plus (n - m) m;
+ slash_star_axiom (pow2 (n - m)) (pow2 m) (pow2 n)Lemma: loss of precision in euclidean division
+val multiply_fractions (a:int) (n:nonzero) : Lemma (n * ( a / n ) <= a)
+let multiply_fractions a n = ()Same as small_mod
val lemma_div_mod:Unidentified product: [a:int] Unidentified product: [p:nonzero] (Lemma (=(a, +(*(p, (/(a, p))), %(a, p)))))val modulo_lemma: a:nat -> b:pos -> Lemma (requires (a < b)) (ensures (a % b = a))
+let modulo_lemma a b = ()Same as lemma_div_def in Math.Lib
val lemma_div_mod: a:int -> p:nonzero -> Lemma (a = p * (a / p) + a % p)
+let lemma_div_mod a p = ()val lemma_mod_lt: a:int -> p:pos -> Lemma (0 <= a % p /\ a % p < p /\ (a >= 0 ==> a % p <= a))
+let lemma_mod_lt a p = ()val lemma_div_lt_nat: a:int -> n:nat -> m:nat{m <= n} ->
+ Lemma (requires (a < pow2 n))
+ (ensures (a / pow2 m < pow2 (n-m)))
+let lemma_div_lt_nat a n m =
+ lemma_div_mod a (pow2 m);
+ assert(a = pow2 m * (a / pow2 m) + a % pow2 m);
+ pow2_plus m (n-m);
+ assert(pow2 n = pow2 m * pow2 (n - m))val lemma_div_lt (a:int) (n:nat) (m:nat) : Lemma
+ (requires m <= n /\ a < pow2 n)
+ (ensures a / pow2 m < pow2 (n-m))
+let lemma_div_lt a n m =
+ if a >= 0 then lemma_div_lt_nat a n mval bounded_multiple_is_zero (x:int) (n:pos) : Lemma
+ (requires -n < x * n /\ x * n < n)
+ (ensures x == 0)
+let bounded_multiple_is_zero (x:int) (n:pos) = ()val small_div (a:nat) (n:pos) : Lemma (requires a < n) (ensures a / n == 0)
+let small_div (a:nat) (n:pos) : Lemma (requires a < n) (ensures a / n == 0) = ()val small_mod (a:nat) (n:pos) : Lemma (requires a < n) (ensures a % n == a)
+let small_mod (a:nat) (n:pos) : Lemma (requires a < n) (ensures a % n == a) = ()val lt_multiple_is_equal (a:nat) (b:nat) (x:int) (n:nonzero) : Lemma
+ (requires a < n /\ b < n /\ a == b + x * n)
+ (ensures a == b /\ x == 0)
+let lt_multiple_is_equal a b x n =
+ assert (0 * n == 0);
+ bounded_multiple_is_zero x nval lemma_mod_plus (a:int) (k:int) (n:pos) : Lemma ((a + k * n) % n = a % n)
+let lemma_mod_plus (a:int) (k:int) (n:pos) =
+ calc (==) {
+ (a+k*n)%n - a%n;
+ == { lemma_div_mod a n; lemma_div_mod (a+k*n) n }
+ ((a + k*n) - n*((a + k*n)/n)) - (a - n*(a/n));
+ == {}
+ n*k + n*(a/n) - n*((a + k*n)/n);
+ == { distributivity_add_right n k (a/n);
+ distributivity_sub_right n (k + a/n) ((a + k*n)/n) }
+ n * (k + a/n - (a+k*n)/n);
+ };
+ lt_multiple_is_equal ((a+k*n)%n) (a%n) (k + a/n - (a+k*n)/n) n;
+ ()val lemma_div_plus (a:int) (k:int) (n:pos) : Lemma ((a + k * n) / n = a / n + k)
+let lemma_div_plus (a:int) (k:int) (n:pos) =
+ calc (==) {
+ n * ((a+k*n)/n - a/n);
+ == { distributivity_sub_right n ((a+k*n)/n) (a/n) }
+ n * ((a+k*n)/n) - n*(a/n);
+ == { lemma_div_mod (a+k*n) n; lemma_div_mod a n }
+ (a + k*n - (a+k*n)%n) - (a - a%n);
+ == {}
+ k*n - (a+k*n)%n + a%n;
+ == { lemma_mod_plus a k n }
+ k*n;
+ };
+ lemma_cancel_mul ((a+k*n)/n - a/n) k nlet lemma_div_mod_plus (a:int) (k:int) (n:pos) : Lemma ((a + k * n) / n = a / n + k /\
+ (a + k * n) % n = a % n) =
+ lemma_div_plus a k n;
+ lemma_mod_plus a k nval add_div_mod_1 (a:int) (n:pos) : Lemma ((a + n) % n == a % n /\ (a + n) / n == a / n + 1)
+let add_div_mod_1 a n =
+ lemma_mod_plus a 1 n;
+ lemma_div_plus a 1 nval sub_div_mod_1 (a:int) (n:pos) : Lemma ((a - n) % n == a % n /\ (a - n) / n == a / n - 1)
+let sub_div_mod_1 a n =
+ lemma_mod_plus a (-1) n;
+ lemma_div_plus a (-1) n#push-options "--smtencoding.elim_box true --smtencoding.nl_arith_repr native"val cancel_mul_div (a:int) (n:nonzero) : Lemma ((a * n) / n == a)
+let cancel_mul_div (a:int) (n:nonzero) = ()#pop-optionsval cancel_mul_mod (a:int) (n:pos) : Lemma ((a * n) % n == 0)
+let cancel_mul_mod (a:int) (n:pos) =
+ small_mod 0 n;
+ lemma_mod_plus 0 a nval lemma_mod_add_distr (a:int) (b:int) (n:pos) : Lemma ((a + b % n) % n = (a + b) % n)
+let lemma_mod_add_distr (a:int) (b:int) (n:pos) =
+ calc (==) {
+ (a + b%n) % n;
+ == { lemma_mod_plus (a + (b % n)) (b / n) n }
+ (a + b%n + n * (b/n)) % n;
+ == { lemma_div_mod b n }
+ (a + b) % n;
+ }val lemma_mod_sub_distr (a:int) (b:int) (n:pos) : Lemma ((a - b % n) % n = (a - b) % n)
+let lemma_mod_sub_distr (a:int) (b:int) (n:pos) =
+ calc (==) {
+ (a - b%n) % n;
+ == { lemma_mod_plus (a - (b % n)) (-(b / n)) n }
+ (a - b%n + n * (-(b/n))) % n;
+ == { neg_mul_right n (b/n) }
+ (a - b%n - n * (b/n)) % n;
+ == { lemma_div_mod b n }
+ (a - b) % n;
+ }val lemma_mod_sub_0: a:pos -> Lemma ((-1) % a = a - 1)
+let lemma_mod_sub_0 a = ()val lemma_mod_sub_1: a:pos -> b:pos{a < b} -> Lemma ((-a) % b = b - (a%b))
+let lemma_mod_sub_1 a b =
+ calc (==) {
+ (-a) % b;
+ == { lemma_mod_plus (-a) 1 b }
+ ((-a) + 1*b) % b;
+ == {}
+ (b - a) % b;
+ == { small_mod (b-a) b }
+ b - a;
+ == { small_mod a b }
+ b - a%b;
+ }val lemma_mod_mul_distr_l (a:int) (b:int) (n:pos) : Lemma
+ (requires True)
+ (ensures (a * b) % n = ((a % n) * b) % n)let lemma_mod_mul_distr_l a b n =
+ calc (==) {
+ (a * b) % n;
+ == { lemma_div_mod a n }
+ ((n * (a/n) + a%n) * b) % n;
+ == { distributivity_add_left (n * (a/n)) (a%n) b }
+ (n * (a/n) * b + (a%n) * b) % n;
+ == { paren_mul_right n (a/n) b; swap_mul ((a/n) * b) n }
+ ((a%n) * b + ((a/n) * b) * n) % n;
+ == { lemma_mod_plus ((a%n) * b) ((a/n) * b) n }
+ ((a%n) * b) % n;
+ }val lemma_mod_mul_distr_r (a:int) (b:int) (n:pos) : Lemma ((a * b) % n = (a * (b % n)) % n)
+let lemma_mod_mul_distr_r (a:int) (b:int) (n:pos) =
+ calc (==) {
+ (a * b) % n;
+ == { swap_mul a b }
+ (b * a) % n;
+ == { lemma_mod_mul_distr_l b a n }
+ (b%n * a) % n;
+ == { swap_mul a (b%n) }
+ (a * (b%n)) % n;
+ }val lemma_mod_injective: p:pos -> a:nat -> b:nat -> Lemma
+ (requires (a < p /\ b < p /\ a % p = b % p))
+ (ensures (a = b))
+let lemma_mod_injective p a b = ()val lemma_mul_sub_distr: a:int -> b:int -> c:int -> Lemma
+ (a * b - a * c = a * (b - c))
+let lemma_mul_sub_distr a b c =
+ distributivity_sub_right a b cval lemma_div_exact: a:int -> p:pos -> Lemma
+ (requires (a % p = 0))
+ (ensures (a = p * (a / p)))
+let lemma_div_exact a p = ()val div_exact_r (a:int) (n:pos) : Lemma
+ (requires (a % n = 0))
+ (ensures (a = (a / n) * n))
+let div_exact_r (a:int) (n:pos) = lemma_div_exact a nval lemma_mod_spec: a:int -> p:pos -> Lemma
+ (a / p = (a - (a % p)) / p)let lemma_mod_spec a p =
+ calc (==) {
+ (a - a%p)/p;
+ == { lemma_div_mod a p }
+ (p*(a/p))/p;
+ == { cancel_mul_div (a/p) p }
+ a/p;
+ }val lemma_mod_spec2: a:int -> p:pos -> Lemma
+ (let q:int = (a - (a % p)) / p in a = (a % p) + q * p)
+let lemma_mod_spec2 a p =
+ calc (==) {
+ (a % p) + ((a - (a % p)) / p) * p;
+ == { lemma_mod_spec a p }
+ (a % p) + (a / p) * p;
+ == { lemma_div_mod a p }
+ a;
+ }val lemma_mod_plus_distr_l: a:int -> b:int -> p:pos -> Lemma
+ ((a + b) % p = ((a % p) + b) % p)
+let lemma_mod_plus_distr_l a b p =
+ let q = (a - (a % p)) / p in
+ lemma_mod_spec2 a p;
+ lemma_mod_plus (a % p + b) q pval lemma_mod_plus_distr_r: a:int -> b:int -> p:pos -> Lemma
+ ((a + b) % p = (a + (b % p)) % p)
+let lemma_mod_plus_distr_r a b p =
+ lemma_mod_plus_distr_l b a pval lemma_mod_mod: a:int -> b:int -> p:pos -> Lemma
+ (requires (a = b % p))
+ (ensures (a % p = b % p))
+let lemma_mod_mod a b p =
+ lemma_mod_lt b p;
+ modulo_lemma (b % p) pLemmas about multiplication, division and modulo. *
+This part focuses on the situation where *
+dividend: nat divisor: pos *
+TODO: add triggers for certain lemmas. *
+Lemma: Definition of euclidean division
+val euclidean_division_definition: a:int -> b:nonzero ->
+ Lemma (a = (a / b) * b + a % b)
+let euclidean_division_definition a b = ()Lemma: Propriety about modulo
+val modulo_range_lemma: a:int -> b:pos ->
+ Lemma (a % b >= 0 && a % b < b)
+let modulo_range_lemma a b = ()val small_modulo_lemma_1: a:nat -> b:nonzero ->
+ Lemma (requires a < b) (ensures a % b = a)
+let small_modulo_lemma_1 a b = ()val small_modulo_lemma_2: a:int -> b:pos ->
+ Lemma (requires a % b = a) (ensures a < b)
+let small_modulo_lemma_2 a b = ()val small_division_lemma_1: a:nat -> b:nonzero ->
+ Lemma (requires a < b) (ensures a / b = 0)
+let small_division_lemma_1 a b = ()val small_division_lemma_2 (a:int) (n:pos) : Lemma
+ (requires a / n = 0)
+ (ensures 0 <= a /\ a < n)
+let small_division_lemma_2 (a:int) (n:pos) = lemma_div_mod a nLemma: Multiplication by a positive integer preserves order
+val multiplication_order_lemma: a:int -> b:int -> p:pos ->
+ Lemma (a >= b <==> a * p >= b * p)
+let multiplication_order_lemma a b p = ()Lemma: Propriety about multiplication after division
+val division_propriety: a:int -> b:pos ->
+ Lemma (a - b < (a / b) * b && (a / b) * b <= a)
+let division_propriety a b = ()Internal lemmas for proving the definition of division
+val division_definition_lemma_1: a:int -> b:pos -> m:int{a - b < m * b} ->
+ Lemma (m > a / b - 1)
+let division_definition_lemma_1 a b m =
+ if a / b - 1 < 0 then () else begin
+ division_propriety a b;
+ multiplication_order_lemma m (a / b - 1) b
+ end
+val division_definition_lemma_2: a:int -> b:pos -> m:int{m * b <= a} ->
+ Lemma (m < a / b + 1)
+let division_definition_lemma_2 a b m =
+ division_propriety a b;
+ multiplication_order_lemma (a / b + 1) m bLemma: Definition of division
+val division_definition: a:int -> b:pos -> m:int{a - b < m * b && m * b <= a} ->
+ Lemma (m = a / b)
+let division_definition a b m =
+ division_definition_lemma_1 a b m;
+ division_definition_lemma_2 a b mLemma: (a * b) / b = a; identical to cancel_mul_div above
val multiple_division_lemma (a:int) (n:nonzero) : Lemma ((a * n) / n = a)
+let multiple_division_lemma (a:int) (n:nonzero) = cancel_mul_div a nLemma: (a * b) % b = 0
+val multiple_modulo_lemma (a:int) (n:pos) : Lemma ((a * n) % n = 0)
+let multiple_modulo_lemma (a:int) (n:pos) = cancel_mul_mod a nLemma: Division distributivity under special condition
+val division_addition_lemma: a:int -> b:pos -> n:int ->
+ Lemma ( (a + n * b) / b = a / b + n )
+let division_addition_lemma a b n = division_definition (a + n * b) b (a / b + n)let lemma_div_le_ (a:int) (b:int) (d:pos) : Lemma
+ (requires (a <= b /\ a / d > b / d))
+ (ensures (False))
+ = lemma_div_mod a d;
+ lemma_div_mod b d;
+ cut (d * (a / d) + a % d <= d * (b / d) + b % d);
+ cut (d * (a / d) - d * (b / d) <= b % d - a % d);
+ distributivity_sub_right d (a/d) (b/d);
+ cut (b % d < d /\ a % d < d);
+ cut (d * (a/d - b/d) <= d)val lemma_div_le: a:int -> b:int -> d:pos ->
+ Lemma (requires (a <= b))
+ (ensures (a / d <= b / d))
+let lemma_div_le a b d =
+ if a / d > b / d then lemma_div_le_ a b dLemma: Division distributivity under special condition
+val division_sub_lemma (a:int) (n:pos) (b:nat) : Lemma ((a - b * n) / n = a / n - b)
+let division_sub_lemma (a:int) (n:pos) (b:nat) =
+ neg_mul_left b n;
+ lemma_div_plus a (-b) nLemma: Modulo distributivity
+val modulo_distributivity: a:int -> b:int -> c:pos -> Lemma ((a + b) % c == (a % c + b % c) % c)
+let modulo_distributivity a b c =
+ calc (==) {
+ (a + b) % c;
+ == { lemma_mod_plus_distr_l a b c }
+ ((a % c) + b) % c;
+ == { lemma_mod_plus_distr_r (a % c) b c }
+ ((a % c) + (b % c)) % c;
+ }val lemma_mod_plus_mul_distr: a:int -> b:int -> c:int -> p:pos -> Lemma
+ (((a + b) * c) % p = ((((a % p) + (b % p)) % p) * (c % p)) % p)
+let lemma_mod_plus_mul_distr a b c p =
+ calc (==) {
+ ((a + b) * c) % p;
+ == { lemma_mod_mul_distr_l (a + b) c p }
+ (((a + b) % p) * c) % p;
+ == { lemma_mod_mul_distr_r ((a + b) % p) c p }
+ (((a + b) % p) * (c % p)) % p;
+ == { modulo_distributivity a b p }
+ ((((a % p) + (b % p)) % p) * (c % p)) % p;
+ }Lemma: Modulo distributivity under special condition
+val modulo_addition_lemma (a:int) (n:pos) (b:int) : Lemma ((a + b * n) % n = a % n)
+let modulo_addition_lemma (a:int) (n:pos) (b:int) = lemma_mod_plus a b nLemma: Modulo distributivity under special condition
+val lemma_mod_sub (a:int) (n:pos) (b:int) : Lemma (ensures (a - b * n) % n = a % n)
+let lemma_mod_sub (a:int) (n:pos) (b:int) =
+ neg_mul_left b n;
+ lemma_mod_plus a (-b) nval mod_mult_exact (a:int) (n:pos) (q:pos) : Lemma
+ (requires (a % (n * q) == 0))
+ (ensures a % n == 0)let mod_mult_exact (a:int) (n:pos) (q:pos) =
+ calc (==) {
+ a % n;
+ == { lemma_div_mod a (n * q) }
+ ((n * q) * (a / (n * q)) + a % (n * q)) % n;
+ == { (* hyp *) }
+ ((n * q) * (a / (n * q))) % n;
+ == { paren_mul_right n q (a / (n * q));
+ swap_mul n (q * (a / (n * q))) }
+ ((q * (a / (n * q))) * n) % n;
+ == { multiple_modulo_lemma (q * (a / (n*q))) n }
+ 0;
+ }val mod_mul_div_exact (a:int) (b:pos) (n:pos) : Lemma
+ (requires (a % (b * n) == 0))
+ (ensures (a / b) % n == 0)
+let mod_mul_div_exact (a:int) (b:pos) (n:pos) =
+ calc (==) {
+ (a / b) % n;
+ == { lemma_div_mod a (b * n) (* + hyp *) }
+ (((b*n)*(a / (b*n))) / b) % n;
+ == { paren_mul_right b n (a / (b*n)) }
+ ((b*(n*(a / (b*n)))) / b) % n;
+ == { cancel_mul_div (n * (a / (b * n))) b }
+ (n*(a / (b*n))) % n;
+ == { cancel_mul_mod (a / (b*n)) n }
+ 0;
+ }#push-options "--fuel 1"
+val mod_pow2_div2 (a:int) (m:pos) : Lemma
+ (requires a % pow2 m == 0)
+ (ensures (a / 2) % pow2 (m - 1) == 0)
+let mod_pow2_div2 (a:int) (m:pos) : Lemma
+ (requires a % pow2 m == 0)
+ (ensures (a / 2) % pow2 (m - 1) == 0)
+ =
+ mod_mul_div_exact a 2 (pow2 (m - 1))
+#pop-optionsprivate val lemma_div_lt_cancel (a : int) (b : pos) (n : int) :
+ Lemma (requires (a < b * n))
+ (ensures (a / b < n))private let lemma_div_lt_cancel a b n =by contradiction
+if a / b >= n then begin
+ calc (>=) {
+ a;
+ >= { slash_decr_axiom a b }
+ (a / b) * b;
+ >= {}
+ n * b;
+ };
+ assert False
+endprivate val lemma_mod_mult_zero (a : int) (b : pos) (c : pos) : Lemma ((a % (b * c)) / b / c == 0)
+private let lemma_mod_mult_zero a b c =< 1
+lemma_mod_lt a (b * c);
+lemma_div_lt_cancel (a % (b * c)) b c;
+lemma_div_lt_cancel ((a % (b * c)) / b) c 1;++= 0
+
nat_over_pos_is_nat (a % (b * c)) b;
+nat_over_pos_is_nat ((a % (b * c)) / b) c;
+()Lemma: Divided by a product is equivalent to being divided one by one
+val division_multiplication_lemma (a:int) (b:pos) (c:pos) : Lemma
+ (a / (b * c) = (a / b) / c)
+let division_multiplication_lemma (a:int) (b:pos) (c:pos) =
+ calc (==) {
+ a / b / c;
+ == { lemma_div_mod a (b * c) }
+ ((b * c) * (a / (b * c)) + a % (b * c)) / b / c;
+ == { paren_mul_right b c (a / (b * c)) }
+ (b * (c * (a / (b * c))) + a % (b * c)) / b / c;
+ == { lemma_div_plus (a % (b * c)) (c * (a / (b * c))) b }
+ (c * (a / (b * c)) + ((a % (b * c)) / b)) / c;
+ == { lemma_div_plus ((a % (b * c)) / b) (a / (b * c)) c }
+ (a / (b * c)) + (a % (b * c)) / b / c;
+ == { lemma_mod_mult_zero a b c }
+ a / (b * c);
+ }private val cancel_fraction (a:int) (b:pos) (c:pos) : Lemma ((a * c) / (b * c) == a / b)
+private let cancel_fraction a b c =
+ calc (==) {
+ (a * c) / (b * c);
+ == { swap_mul b c }
+ (a * c) / (c * b);
+ == { division_multiplication_lemma (a * c) c b }
+ ((a * c) / c) / b;
+ == { cancel_mul_div a c }
+ a / b;
+ }val modulo_scale_lemma : a:int -> b:pos -> c:pos -> Lemma ((a * b) % (b * c) == (a % c) * b)
+let modulo_scale_lemma a b c =
+ calc (==) {
+ (a * b) % (b * c);
+ == { lemma_div_mod (a * b) (b * c) }
+ a * b - (b * c) * ((a * b) / (b * c));
+ == { cancel_fraction a c b }
+ a * b - (b * c) * (a / c);
+ == { paren_mul_right b c (a / c) }
+ a * b - b * (c * (a / c));
+ == { swap_mul b (c * (a / c)); distributivity_sub_left a (c * (a / c)) b }
+ (a - c * (a / c)) * b;
+ == { lemma_div_mod a c }
+ (a % c) * b;
+ }let lemma_mul_pos_pos_is_pos (x:pos) (y:pos) : Lemma (x*y > 0) = ()
+let lemma_mul_nat_pos_is_nat (x:nat) (y:pos) : Lemma (x*y >= 0) = ()let modulo_division_lemma_0 (a:nat) (b:pos) (c:pos) : Lemma
+ (a / (b*c) <= a /\ (a - (a / (b * c)) * (b * c)) / b = a / b - ((a / (b * c)) * c))
+ = slash_decr_axiom a (b*c);
+ calc (==) {
+ (a / (b*c)) * (b * c);
+ == { swap_mul b c }
+ (a / (b*c)) * (c * b);
+ == { paren_mul_right (a / (b*c)) c b }
+ ((a / (b*c)) * c) * b;
+ };
+ cut ((a / (b*c)) * (b * c) = ((a / (b * c)) * c) * b);
+ lemma_div_mod a (b*c);
+ division_sub_lemma a b ((a / (b*c)) * c);
+ ()val modulo_division_lemma: a:nat -> b:pos -> c:pos ->
+ Lemma ((a % (b * c)) / b = (a / b) % c)let modulo_division_lemma a b c =
+ calc (==) {
+ (a % (b * c)) / b;
+ == { lemma_div_mod a (b * c) }
+ (a - (b * c) * (a / (b * c))) / b;
+ == { paren_mul_right b c ((a / (b * c))); neg_mul_right b (c * (a / (b * c))) }
+ (a + b * (-(c * (a / (b * c))))) / b;
+ == { lemma_div_plus a (-(c * (a / (b * c)))) b }
+ (a / b) - c * (a / (b * c));
+ == { division_multiplication_lemma a b c }
+ (a / b) - c * ((a / b) / c);
+ == { lemma_div_mod (a/b) c }
+ (a / b) % c;
+ }val modulo_modulo_lemma (a:int) (b:pos) (c:pos) : Lemma
+ ((a % (b * c)) % b = a % b)let modulo_modulo_lemma (a:int) (b:pos) (c:pos) =
+ pos_times_pos_is_pos b c;
+ calc (==) {
+ (a % (b * c)) % b;
+ == { calc (==) {
+ a % (b * c);
+ == { lemma_div_mod a (b * c) }
+ a - (b * c) * (a / (b * c));
+ == { paren_mul_right b c (a / (b * c)) }
+ a - b * (c * (a / (b * c)));
+ }}
+ (a - b * (c * (a / (b * c)))) % b;
+ == { () }
+ (a + (- (b * (c * (a / (b * c)))))) % b;
+ == { neg_mul_right b (c * (a / (b * c))) }
+ (a + (b * (-c * (a / (b * c))))) % b;
+ == { () }
+ (a + (-c * (a / (b * c))) * b) % b;
+ == { lemma_mod_plus a (-c * (a / (b * c))) b}
+ a % b;
+ }val pow2_multiplication_division_lemma_1: a:int -> b:nat -> c:nat{c >= b} ->
+ Lemma ( (a * pow2 c) / pow2 b = a * pow2 (c - b))
+let pow2_multiplication_division_lemma_1 a b c =
+ pow2_plus (c - b) b;
+ paren_mul_right a (pow2 (c - b)) (pow2 b);
+ paren_mul_left a (pow2 (c - b)) (pow2 b);
+ multiple_division_lemma (a * pow2 (c - b)) (pow2 b)val pow2_multiplication_division_lemma_2: a:int -> b:nat -> c:nat{c <= b} ->
+ Lemma ( (a * pow2 c) / pow2 b = a / pow2 (b - c))
+let pow2_multiplication_division_lemma_2 a b c =
+ pow2_plus c (b - c);
+ division_multiplication_lemma (a * pow2 c) (pow2 c) (pow2 (b - c));
+ multiple_division_lemma a (pow2 c)val pow2_multiplication_modulo_lemma_1: a:int -> b:nat -> c:nat{c >= b} ->
+ Lemma ( (a * pow2 c) % pow2 b = 0 )
+let pow2_multiplication_modulo_lemma_1 a b c =
+ pow2_plus (c - b) b;
+ paren_mul_right a (pow2 (c - b)) (pow2 b);
+ paren_mul_left a (pow2 (c - b)) (pow2 b);
+ multiple_modulo_lemma (a * pow2 (c - b)) (pow2 b)val pow2_multiplication_modulo_lemma_2: a:int -> b:nat -> c:nat{c <= b} ->
+ Lemma ( (a * pow2 c) % pow2 b = (a % pow2 (b - c)) * pow2 c )let pow2_multiplication_modulo_lemma_2 a b c =
+ calc (==) {
+ (a * pow2 c) % pow2 b;
+ == {}
+ (a * pow2 c) % pow2 (c + (b-c));
+ == { pow2_plus c (b-c) }
+ (a * pow2 c) % (pow2 c * pow2 (b-c));
+ == { modulo_scale_lemma a (pow2 c) (pow2 (b-c)) }
+ (a % pow2 (b - c)) * pow2 c;
+ }val pow2_modulo_division_lemma_1: a:nat -> b:nat -> c:nat{c >= b} ->
+ Lemma ( (a % pow2 c) / pow2 b = (a / pow2 b) % (pow2 (c - b)) )
+let pow2_modulo_division_lemma_1 a b c =
+ pow2_plus (c - b) b;
+ modulo_division_lemma a (pow2 b) (pow2 (c - b))val pow2_modulo_division_lemma_2: a:int -> b:nat -> c:nat{c <= b} ->
+ Lemma ( (a % pow2 c) / pow2 b = 0 )
+let pow2_modulo_division_lemma_2 a b c =
+ pow2_le_compat b c;
+ small_division_lemma_1 (a % pow2 c) (pow2 b)val pow2_modulo_modulo_lemma_1: a:int -> b:nat -> c:nat{c >= b} ->
+ Lemma ( (a % pow2 c) % pow2 b = a % pow2 b )
+let pow2_modulo_modulo_lemma_1 a b c =
+ pow2_plus (c - b) b;
+ modulo_modulo_lemma a (pow2 b) (pow2 (c - b))val pow2_modulo_modulo_lemma_2: a:int -> b:nat -> c:nat{c <= b} ->
+ Lemma ( (a % pow2 c) % pow2 b = a % pow2 c )
+let pow2_modulo_modulo_lemma_2 a b c =
+ pow2_le_compat b c;
+ small_modulo_lemma_1 (a % pow2 c) (pow2 b)val modulo_add : p:pos -> a:int -> b:int -> c:int -> Lemma
+ (requires (b % p = c % p))
+ (ensures ((a + b) % p = (a + c) % p))
+let modulo_add p a b c =
+ modulo_distributivity a b p;
+ modulo_distributivity a c pval lemma_mod_twice : a:int -> p:pos -> Lemma ((a % p) % p == a % p)
+let lemma_mod_twice a p = lemma_mod_mod (a % p) a pval modulo_sub : p:pos -> a:int -> b:int -> c:int -> Lemma
+ (requires ((a + b) % p = (a + c) % p))
+ (ensures (b % p = c % p))let modulo_sub p a b c =
+ modulo_add p (-a) (a + b) (a + c)val mod_add_both (a:int) (b:int) (x:int) (n:pos) : Lemma
+ (requires a % n == b % n)
+ (ensures (a + x) % n == (b + x) % n)
+let mod_add_both (a:int) (b:int) (x:int) (n:pos) =
+ calc (==) {
+ (a + x) % n;
+ == { modulo_distributivity a x n }
+ ((a % n) + (x % n)) % n;
+ == { (* hyp *) }
+ ((b % n) + (x % n)) % n;
+ == { modulo_distributivity b x n }
+ (b + x) % n;
+ }val lemma_mod_plus_injective (n:pos) (a:int) (b:nat) (c:nat) : Lemma
+ (requires b < n /\ c < n /\ (a + b) % n = (a + c) % n)
+ (ensures b = c)
+let lemma_mod_plus_injective (n:pos) (a:int) (b:nat) (c:nat) =
+ small_mod b n;
+ small_mod c n;
+ mod_add_both (a + b) (a + c) (-a) nAnother characterization of the modulo
+val modulo_sub_lemma (a : int) (b : nat) (c : pos) :
+ Lemma
+ (requires (b < c /\ (a - b) % c = 0))
+ (ensures (b = a % c))
+let modulo_sub_lemma a b c =
+ calc (==) {
+ b;
+ == { modulo_lemma b c }
+ b % c;
+ == { lemma_mod_twice b c }
+ (b%c) % c;
+ == { (* hyp *) }
+ (b%c + (a-b)%c) % c;
+ == { modulo_distributivity b (a-b) c }
+ (b+(a-b)) % c;
+ == {}
+ a % c;
+ }fsdoc: no-summary-found
-fsdoc: no-comment-found
- Useful lemmas for future proofs *Definition of the diviion operator
+val lemma_div_def: a:nat -> b:pos -> Lemma (a = b * (a/b) + a % b)
+let lemma_div_def a b = ()private let mul_lemma (a:nat) (b:nat) (c:nat) : Lemma (requires (a <= b))
+ (ensures (c * a <= c * b))
+ = ()private let mul_lemma' (a:nat) (b:nat) (c:pos) : Lemma (requires (c * a <= c * b))
+ (ensures (a <= b))
+ = ()private let mul_div_lemma (a:nat) (b:pos) : Lemma (b * (a / b) <= a) = ()val slash_decr_axiom: a:nat -> b:pos -> Lemma (a / b <= a)
+let slash_decr_axiom a b =
+ mul_lemma 1 b a;
+ mul_div_lemma a b;
+ mul_lemma' (a / b) a bprivate let lemma_mul_minus_distr_l (a:int) (b:int) (c:int) : Lemma (a * (b - c) = a * b - a * c)
+ = ()Axiom: definition of the "b divides c" relation
+#reset-options "--z3rlimit 30"
+val slash_star_axiom: a:nat -> b:pos -> c:nat -> Lemma
+ (requires (a * b = c))
+ (ensures (a = c / b))
+let slash_star_axiom a b c =
+ lemma_div_def c b;
+ lemma_mul_minus_distr_l b a (c/b)#reset-options
+val log_2: x:pos -> Tot nat
+let rec log_2 x =
+ if x >= 2 then 1 + log_2 (x / 2) else 0Function: power of x
+val powx : x:int -> n:nat -> Tot int
+let rec powx x n =
+ match n with
+ | 0 -> 1
+ | n -> x * powx x (n - 1)Function: absolute value
+val abs: x:int -> Tot (y:int{ (x >= 0 ==> y = x) /\ (x < 0 ==> y = -x) })
+let abs x = if x >= 0 then x else -xFunction: maximum value
+val max: x:int -> y:int -> Tot (z:int{ (x >= y ==> z = x) /\ (x < y ==> z = y) })
+let max x y = if x >= y then x else yFunction: minimum value
+val min: x:int -> y:int -> Tot (z:int{ (x >= y ==> z = y) /\ (x < y ==> z = x) })
+let min x y = if x >= y then y else xFunction: standard euclidean division, the rest is always positive
+val div: a:int -> b:pos -> Tot (c:int{(a < 0 ==> c < 0) /\ (a >= 0 ==> c >= 0)})
+let div a b =
+ if a < 0 then
+ begin
+ slash_decr_axiom (-a) b;
+ if a % b = 0 then - (-a / b)
+ else - (-a / b) - 1
+ end
+ else a / bFunction: equivalent of the '/' operator in C, hence the rest can be negative
+val div_non_eucl: a:int -> b:pos ->
+ Tot (q:int{ ( a >= 0 ==> q = a / b ) /\ ( a < 0 ==> q = -((-a)/b) ) })
+let div_non_eucl a b =
+ if a < 0 then 0 - ((0 - a) / b)
+ else a / bThe equivalent of the << C operator
+val shift_left: v:int -> i:nat -> Tot (res:int{res = v * (pow2 i)})
+let shift_left v i =
+ v * (pow2 i)asr OCaml operator
+val arithmetic_shift_right: v:int -> i:nat -> Tot (res:int{ res = div v (pow2 i) })
+let arithmetic_shift_right v i =
+ div v (pow2 i)Case of C cast functions ?
+Implemented by "mod" in OCaml
+val signed_modulo: v:int -> p:pos -> Tot (res:int{ res = v - ((div_non_eucl v p) * p) })
+let signed_modulo v p =
+ if v >= 0 then v % p
+ else 0 - ( (0-v) % p)val op_Plus_Percent : a:int -> p:pos ->
+ Tot (res:int{ (a >= 0 ==> res = a % p) /\ (a < 0 ==> res = -((-a) % p)) })
+let op_Plus_Percent a p = signed_modulo a pUseful lemmas for future proofs *
+Lemmas of x^n
+val powx_lemma1: a:int -> Lemma (powx a 1 = a)
+let powx_lemma1 a = ()val powx_lemma2: x:int -> n:nat -> m:nat -> Lemma
+ (powx x n * powx x m = powx x (n + m))
+let rec powx_lemma2 x n m =
+ let ass (x y z : int) : Lemma ((x*y)*z == x*(y*z)) = () in
+ match n with
+ | 0 -> ()
+ | _ -> powx_lemma2 x (n-1) m; ass x (powx x (n-1)) (powx x m)Lemma: absolute value of product is the product of the absolute values
+val abs_mul_lemma: a:int -> b:int -> Lemma (abs (a * b) = abs a * abs b)
+let abs_mul_lemma a b = ()Lemma: absolute value of a signed_module b is bounded by b
+val signed_modulo_property: v:int -> p:pos -> Lemma (abs (signed_modulo v p ) < p)
+let signed_modulo_property v p = ()Lemma: non-Euclidean division has a smaller output compared to its input
+val div_non_eucl_decr_lemma: a:int -> b:pos -> Lemma (abs (div_non_eucl a b) <= abs a)
+let div_non_eucl_decr_lemma a b =
+ slash_decr_axiom (abs a) bLemma: dividing by a bigger value leads to 0 in non-Euclidean division
+val div_non_eucl_bigger_denom_lemma: a:int -> b:pos -> Lemma
+ (requires (b > abs a))
+ (ensures (div_non_eucl a b = 0))
+let div_non_eucl_bigger_denom_lemma a b = ()fsdoc: no-summary-found
-fsdoc: no-comment-found
-* The modifies clause val loc_union_idem:s:loc -> (Lemma (==(loc_union s s, s)) (Prims.Cons (SMTPat (loc_union s s)) (Prims.Nil )))The following is useful to make Z3 cut matching loops with modifies_trans and modifies_refl
- The modifies clause proper BEGIN TODO: move to FStar.Monotonic.HyperStack END TODO HS
+HST
+B
+val loc : Type u#1val loc_none: locval loc_union
+ (s1 s2: loc)
+: GTot locThe following is useful to make Z3 cut matching loops with +modifies_trans and modifies_refl
+val loc_union_idem
+ (s: loc)
+: Lemma
+ (loc_union s s == s)
+ [SMTPat (loc_union s s)]val loc_union_comm
+ (s1 s2: loc)
+: Lemma
+ (loc_union s1 s2 == loc_union s2 s1)
+ [SMTPat (loc_union s1 s2)]val loc_union_assoc
+ (s1 s2 s3: loc)
+: Lemma
+ (loc_union s1 (loc_union s2 s3) == loc_union (loc_union s1 s2) s3)val loc_union_loc_none_l
+ (s: loc)
+: Lemma
+ (loc_union loc_none s == s)
+ [SMTPat (loc_union loc_none s)]val loc_union_loc_none_r
+ (s: loc)
+: Lemma
+ (loc_union s loc_none == s)
+ [SMTPat (loc_union s loc_none)]val loc_buffer
+ (#t: Type)
+ (b: B.buffer t)
+: GTot locval loc_addresses
+ (preserve_liveness: bool)
+ (r: HS.rid)
+ (n: Set.set nat)
+: GTot locval loc_regions
+ (preserve_liveness: bool)
+ (r: Set.set HS.rid)
+: GTot loclet loc_mreference
+ (#a: Type)
+ (#p: Preorder.preorder a)
+ (b: HS.mreference a p)
+: GTot loc
+= loc_addresses true (HS.frameOf b) (Set.singleton (HS.as_addr b))let loc_freed_mreference
+ (#a: Type)
+ (#p: Preorder.preorder a)
+ (b: HS.mreference a p)
+: GTot loc
+= loc_addresses false (HS.frameOf b) (Set.singleton (HS.as_addr b))let loc_region_only
+ (preserve_liveness: bool)
+ (r: HS.rid)
+: GTot loc
+= loc_regions preserve_liveness (Set.singleton r)let loc_all_regions_from
+ (preserve_liveness: bool)
+ (r: HS.rid)
+: GTot loc
+= loc_regions preserve_liveness (HS.mod_set (Set.singleton r))Inclusion of memory locations
+val loc_includes
+ (s1 s2: loc)
+: GTot Type0val loc_includes_refl
+ (s: loc)
+: Lemma
+ (loc_includes s s)
+ [SMTPat (loc_includes s s)]val loc_includes_trans
+ (s1 s2 s3: loc)
+: Lemma
+ (requires (loc_includes s1 s2 /\ loc_includes s2 s3))
+ (ensures (loc_includes s1 s3))val loc_includes_union_r
+ (s s1 s2: loc)
+: Lemma
+ (requires (loc_includes s s1 /\ loc_includes s s2))
+ (ensures (loc_includes s (loc_union s1 s2)))
+ [SMTPat (loc_includes s (loc_union s1 s2))]val loc_includes_union_l
+ (s1 s2 s: loc)
+: Lemma
+ (requires (loc_includes s1 s \/ loc_includes s2 s))
+ (ensures (loc_includes (loc_union s1 s2) s))
+ [SMTPat (loc_includes (loc_union s1 s2) s)]val loc_includes_none
+ (s: loc)
+: Lemma
+ (loc_includes s loc_none)
+ [SMTPat (loc_includes s loc_none)]val loc_includes_buffer
+ (#t: Type)
+ (b1 b2: B.buffer t)
+: Lemma
+ (requires (b1 `B.includes` b2))
+ (ensures (loc_includes (loc_buffer b1) (loc_buffer b2)))
+ [SMTPatOr [
+ [SMTPat (B.includes b1 b2)];
+ [SMTPat (loc_includes(loc_buffer b1) (loc_buffer b2))]
+ ]]val loc_includes_gsub_buffer_r
+ (l: loc)
+ (#t: Type)
+ (b: B.buffer t)
+ (i: UInt32.t)
+ (len: UInt32.t)
+: Lemma
+ (requires (UInt32.v i + UInt32.v len <= (B.length b) /\ loc_includes l (loc_buffer b)))
+ (ensures (UInt32.v i + UInt32.v len <= (B.length b) /\ loc_includes l (loc_buffer (B.sub b i len))))
+ [SMTPat (loc_includes l (loc_buffer (B.sub b i len)))]val loc_includes_gsub_buffer_l
+ (#t: Type)
+ (b: B.buffer t)
+ (i1: UInt32.t)
+ (len1: UInt32.t)
+ (i2: UInt32.t)
+ (len2: UInt32.t)
+: Lemma
+ (requires (UInt32.v i1 + UInt32.v len1 <= (B.length b) /\ UInt32.v i1 <= UInt32.v i2 /\ UInt32.v i2 + UInt32.v len2 <= UInt32.v i1 + UInt32.v len1))
+ (ensures (UInt32.v i1 + UInt32.v len1 <= (B.length b) /\ UInt32.v i1 <= UInt32.v i2 /\ UInt32.v i2 + UInt32.v len2 <= UInt32.v i1 + UInt32.v len1 /\ loc_includes (loc_buffer (B.sub b i1 len1)) (loc_buffer (B.sub b i2 len2))))
+ [SMTPat (loc_includes (loc_buffer (B.sub b i1 len1)) (loc_buffer (B.sub b i2 len2)))]val loc_includes_addresses_buffer
+ (#t: Type)
+ (preserve_liveness: bool)
+ (r: HS.rid)
+ (s: Set.set nat)
+ (p: B.buffer t)
+: Lemma
+ (requires (B.frameOf p == r /\ Set.mem (B.as_addr p) s))
+ (ensures (loc_includes (loc_addresses preserve_liveness r s) (loc_buffer p)))
+ [SMTPat (loc_includes (loc_addresses preserve_liveness r s) (loc_buffer p))]val loc_includes_region_buffer
+ (#t: Type)
+ (preserve_liveness: bool)
+ (s: Set.set HS.rid)
+ (b: B.buffer t)
+: Lemma
+ (requires (Set.mem (B.frameOf b) s))
+ (ensures (loc_includes (loc_regions preserve_liveness s) (loc_buffer b)))
+ [SMTPat (loc_includes (loc_regions preserve_liveness s) (loc_buffer b))]val loc_includes_region_addresses
+ (preserve_liveness1: bool)
+ (preserve_liveness2: bool)
+ (s: Set.set HS.rid)
+ (r: HS.rid)
+ (a: Set.set nat)
+: Lemma
+ (requires (Set.mem r s))
+ (ensures (loc_includes (loc_regions preserve_liveness1 s) (loc_addresses preserve_liveness2 r a)))
+ [SMTPat (loc_includes (loc_regions preserve_liveness1 s) (loc_addresses preserve_liveness2 r a))]val loc_includes_region_region
+ (preserve_liveness1: bool)
+ (preserve_liveness2: bool)
+ (s1 s2: Set.set HS.rid)
+: Lemma
+ (requires ((preserve_liveness1 ==> preserve_liveness2) /\ Set.subset s2 s1))
+ (ensures (loc_includes (loc_regions preserve_liveness1 s1) (loc_regions preserve_liveness2 s2)))
+ [SMTPat (loc_includes (loc_regions preserve_liveness1 s1) (loc_regions preserve_liveness2 s2))]val loc_includes_region_union_l
+ (preserve_liveness: bool)
+ (l: loc)
+ (s1 s2: Set.set HS.rid)
+: Lemma
+ (requires (loc_includes l (loc_regions preserve_liveness (Set.intersect s2 (Set.complement s1)))))
+ (ensures (loc_includes (loc_union (loc_regions preserve_liveness s1) l) (loc_regions preserve_liveness s2)))
+ [SMTPat (loc_includes (loc_union (loc_regions preserve_liveness s1) l) (loc_regions preserve_liveness s2))]val loc_includes_addresses_addresses
+ (preserve_liveness1 preserve_liveness2: bool)
+ (r: HS.rid)
+ (s1 s2: Set.set nat)
+: Lemma
+ (requires ((preserve_liveness1 ==> preserve_liveness2) /\ Set.subset s2 s1))
+ (ensures (loc_includes (loc_addresses preserve_liveness1 r s1) (loc_addresses preserve_liveness2 r s2)))Disjointness of two memory locations
+val loc_disjoint
+ (s1 s2: loc)
+: GTot Type0val loc_disjoint_sym
+ (s1 s2: loc)
+: Lemma
+ (requires (loc_disjoint s1 s2))
+ (ensures (loc_disjoint s2 s1))let loc_disjoint_sym'
+ (s1 s2: loc)
+: Lemma
+ (loc_disjoint s1 s2 <==> loc_disjoint s2 s1)
+ [SMTPat (loc_disjoint s1 s2)]
+= Classical.move_requires (loc_disjoint_sym s1) s2;
+ Classical.move_requires (loc_disjoint_sym s2) s1val loc_disjoint_none_r
+ (s: loc)
+: Lemma
+ (ensures (loc_disjoint s loc_none))
+ [SMTPat (loc_disjoint s loc_none)]val loc_disjoint_union_r
+ (s s1 s2: loc)
+: Lemma
+ (requires (loc_disjoint s s1 /\ loc_disjoint s s2))
+ (ensures (loc_disjoint s (loc_union s1 s2)))
+ [SMTPat (loc_disjoint s (loc_union s1 s2))]val loc_disjoint_includes
+ (p1 p2 p1' p2' : loc)
+: Lemma
+ (requires (loc_includes p1 p1' /\ loc_includes p2 p2' /\ loc_disjoint p1 p2))
+ (ensures (loc_disjoint p1' p2'))
+ [SMTPatOr [
+ [SMTPat (loc_disjoint p1 p2); SMTPat (loc_disjoint p1' p2')];
+ [SMTPat (loc_includes p1 p1'); SMTPat (loc_includes p2 p2')];
+ ]]val loc_disjoint_buffer
+ (#t1 #t2: Type)
+ (b1: B.buffer t1)
+ (b2: B.buffer t2)
+: Lemma
+ (requires (B.disjoint b1 b2))
+ (ensures (loc_disjoint (loc_buffer b1) (loc_buffer b2)))
+ [SMTPatOr [
+ [SMTPat (B.disjoint b1 b2)];
+ [SMTPat (loc_disjoint (loc_buffer b1) (loc_buffer b2))];
+ ]]val loc_disjoint_gsub_buffer
+ (#t: Type)
+ (b: B.buffer t)
+ (i1: UInt32.t)
+ (len1: UInt32.t)
+ (i2: UInt32.t)
+ (len2: UInt32.t)
+: Lemma
+ (requires (
+ UInt32.v i1 + UInt32.v len1 <= (B.length b) /\
+ UInt32.v i2 + UInt32.v len2 <= (B.length b) /\ (
+ UInt32.v i1 + UInt32.v len1 <= UInt32.v i2 \/
+ UInt32.v i2 + UInt32.v len2 <= UInt32.v i1
+ )))
+ (ensures (
+ UInt32.v i1 + UInt32.v len1 <= (B.length b) /\
+ UInt32.v i2 + UInt32.v len2 <= (B.length b) /\
+ loc_disjoint (loc_buffer (B.sub b i1 len1)) (loc_buffer (B.sub b i2 len2))
+ ))
+ [SMTPat (loc_disjoint (loc_buffer (B.sub b i1 len1)) (loc_buffer (B.sub b i2 len2)))]val loc_disjoint_addresses
+ (preserve_liveness1 preserve_liveness2: bool)
+ (r1 r2: HS.rid)
+ (n1 n2: Set.set nat)
+: Lemma
+ (requires (r1 <> r2 \/ Set.subset (Set.intersect n1 n2) Set.empty))
+ (ensures (loc_disjoint (loc_addresses preserve_liveness1 r1 n1) (loc_addresses preserve_liveness2 r2 n2)))
+ [SMTPat (loc_disjoint (loc_addresses preserve_liveness1 r1 n1) (loc_addresses preserve_liveness2 r2 n2))]val loc_disjoint_buffer_addresses
+ (#t: Type)
+ (p: B.buffer t)
+ (preserve_liveness: bool)
+ (r: HS.rid)
+ (n: Set.set nat)
+: Lemma
+ (requires (r <> B.frameOf p \/ (~ (Set.mem (B.as_addr p) n))))
+ (ensures (loc_disjoint (loc_buffer p) (loc_addresses preserve_liveness r n)))
+ [SMTPat (loc_disjoint (loc_buffer p) (loc_addresses preserve_liveness r n))]val loc_disjoint_regions
+ (preserve_liveness1 preserve_liveness2: bool)
+ (rs1 rs2: Set.set HS.rid)
+: Lemma
+ (requires (Set.subset (Set.intersect rs1 rs2) Set.empty))
+ (ensures (loc_disjoint (loc_regions preserve_liveness1 rs1) (loc_regions preserve_liveness2 rs2)))
+ [SMTPat (loc_disjoint (loc_regions preserve_liveness1 rs1) (loc_regions preserve_liveness2 rs2))]The modifies clause proper
+val modifies
+ (s: loc)
+ (h1 h2: HS.mem)
+: GTot Type0val modifies_mreference_elim
+ (#t: Type)
+ (#pre: Preorder.preorder t)
+ (b: HS.mreference t pre)
+ (p: loc)
+ (h h': HS.mem)
+: Lemma
+ (requires (
+ loc_disjoint (loc_mreference b) p /\
+ HS.contains h b /\
+ modifies p h h'
+ ))
+ (ensures (
+ HS.contains h' b /\
+ HS.sel h b == HS.sel h' b
+ ))
+ [SMTPatOr [
+ [ SMTPat (modifies p h h'); SMTPat (HS.sel h b) ] ;
+ [ SMTPat (modifies p h h'); SMTPat (HS.contains h b) ];
+ [ SMTPat (modifies p h h'); SMTPat (HS.sel h' b) ] ;
+ [ SMTPat (modifies p h h'); SMTPat (HS.contains h' b) ]
+ ] ]val modifies_buffer_elim
+ (#t1: Type)
+ (b: B.buffer t1)
+ (p: loc)
+ (h h': HS.mem)
+: Lemma
+ (requires (
+ loc_disjoint (loc_buffer b) p /\
+ B.live h b /\
+ modifies p h h'
+ ))
+ (ensures (
+ B.live h' b /\ (
+ B.as_seq h b == B.as_seq h' b
+ )))
+ [SMTPatOr [
+ [ SMTPat (modifies p h h'); SMTPat (B.as_seq h b) ] ;
+ [ SMTPat (modifies p h h'); SMTPat (B.live h b) ];
+ [ SMTPat (modifies p h h'); SMTPat (B.as_seq h' b) ] ;
+ [ SMTPat (modifies p h h'); SMTPat (B.live h' b) ]
+ ] ]val modifies_refl
+ (s: loc)
+ (h: HS.mem)
+: Lemma
+ (modifies s h h)
+ [SMTPat (modifies s h h)]val modifies_loc_includes
+ (s1: loc)
+ (h h': HS.mem)
+ (s2: loc)
+: Lemma
+ (requires (modifies s2 h h' /\ loc_includes s1 s2))
+ (ensures (modifies s1 h h'))
+ [SMTPatOr [
+ [SMTPat (modifies s1 h h'); SMTPat (modifies s2 h h')];
+ [SMTPat (modifies s1 h h'); SMTPat (loc_includes s1 s2)];
+ [SMTPat (modifies s2 h h'); SMTPat (loc_includes s1 s2)];
+ ]]++Some memory locations are tagged as liveness-insensitive: the +liveness preservation of a memory location only depends on its +disjointness from the liveness-sensitive memory locations of a +modifies clause.
+
val address_liveness_insensitive_locs: locval region_liveness_insensitive_locs: locval address_liveness_insensitive_buffer (#t: Type) (b: B.buffer t) : Lemma
+ (address_liveness_insensitive_locs `loc_includes` (loc_buffer b))
+ [SMTPat (address_liveness_insensitive_locs `loc_includes` (loc_buffer b))]val address_liveness_insensitive_addresses (r: HS.rid) (a: Set.set nat) : Lemma
+ (address_liveness_insensitive_locs `loc_includes` (loc_addresses true r a))
+ [SMTPat (address_liveness_insensitive_locs `loc_includes` (loc_addresses true r a))]val region_liveness_insensitive_buffer (#t: Type) (b: B.buffer t) : Lemma
+ (region_liveness_insensitive_locs `loc_includes` (loc_buffer b))
+ [SMTPat (region_liveness_insensitive_locs `loc_includes` (loc_buffer b))]val region_liveness_insensitive_addresses (preserve_liveness: bool) (r: HS.rid) (a: Set.set nat) : Lemma
+ (region_liveness_insensitive_locs `loc_includes` (loc_addresses preserve_liveness r a))
+ [SMTPat (region_liveness_insensitive_locs `loc_includes` (loc_addresses preserve_liveness r a))]val region_liveness_insensitive_regions (rs: Set.set HS.rid) : Lemma
+ (region_liveness_insensitive_locs `loc_includes` (loc_regions true rs))
+ [SMTPat (region_liveness_insensitive_locs `loc_includes` (loc_regions true rs))]val region_liveness_insensitive_address_liveness_insensitive:
+ squash (region_liveness_insensitive_locs `loc_includes` address_liveness_insensitive_locs)val modifies_liveness_insensitive_mreference
+ (l1 l2 : loc)
+ (h h' : HS.mem)
+ (#t: Type)
+ (#pre: Preorder.preorder t)
+ (x: HS.mreference t pre)
+: Lemma
+ (requires (modifies (loc_union l1 l2) h h' /\ loc_disjoint l1 (loc_mreference x) /\ address_liveness_insensitive_locs `loc_includes` l2 /\ h `HS.contains` x))
+ (ensures (h' `HS.contains` x))TODO: pattern
+val modifies_liveness_insensitive_buffer
+ (l1 l2 : loc)
+ (h h' : HS.mem)
+ (#t: Type)
+ (x: B.buffer t)
+: Lemma
+ (requires (modifies (loc_union l1 l2) h h' /\ loc_disjoint l1 (loc_buffer x) /\ address_liveness_insensitive_locs `loc_includes` l2 /\ B.live h x))
+ (ensures (B.live h' x))TODO: pattern
+let modifies_liveness_insensitive_mreference_weak
+ (l : loc)
+ (h h' : HS.mem)
+ (#t: Type)
+ (#pre: Preorder.preorder t)
+ (x: HS.mreference t pre)
+: Lemma
+ (requires (modifies l h h' /\ address_liveness_insensitive_locs `loc_includes` l /\ h `HS.contains` x))
+ (ensures (h' `HS.contains` x))
+ [SMTPatOr [
+ [SMTPat (h `HS.contains` x); SMTPat (modifies l h h');];
+ [SMTPat (h' `HS.contains` x); SMTPat (modifies l h h');];
+ ]]
+= modifies_liveness_insensitive_mreference loc_none l h h' xlet modifies_liveness_insensitive_buffer_weak
+ (l : loc)
+ (h h' : HS.mem)
+ (#t: Type)
+ (x: B.buffer t)
+: Lemma
+ (requires (modifies l h h' /\ address_liveness_insensitive_locs `loc_includes` l /\ B.live h x))
+ (ensures (B.live h' x))
+ [SMTPatOr [
+ [SMTPat (B.live h x); SMTPat (modifies l h h');];
+ [SMTPat (B.live h' x); SMTPat (modifies l h h');];
+ ]]
+= modifies_liveness_insensitive_buffer loc_none l h h' xval modifies_liveness_insensitive_region
+ (l1 l2 : loc)
+ (h h' : HS.mem)
+ (x: HS.rid)
+: Lemma
+ (requires (modifies (loc_union l1 l2) h h' /\ loc_disjoint l1 (loc_region_only false x) /\ region_liveness_insensitive_locs `loc_includes` l2 /\ HS.live_region h x))
+ (ensures (HS.live_region h' x))TODO: pattern
+val modifies_liveness_insensitive_region_mreference
+ (l1 l2 : loc)
+ (h h' : HS.mem)
+ (#t: Type)
+ (#pre: Preorder.preorder t)
+ (x: HS.mreference t pre)
+: Lemma
+ (requires (modifies (loc_union l1 l2) h h' /\ loc_disjoint l1 (loc_mreference x) /\ region_liveness_insensitive_locs `loc_includes` l2 /\ HS.live_region h (HS.frameOf x)))
+ (ensures (HS.live_region h' (HS.frameOf x)))TODO: pattern
+val modifies_liveness_insensitive_region_buffer
+ (l1 l2 : loc)
+ (h h' : HS.mem)
+ (#t: Type)
+ (x: B.buffer t)
+: Lemma
+ (requires (modifies (loc_union l1 l2) h h' /\ loc_disjoint l1 (loc_buffer x) /\ region_liveness_insensitive_locs `loc_includes` l2 /\ HS.live_region h (B.frameOf x)))
+ (ensures (HS.live_region h' (B.frameOf x)))TODO: pattern
+let modifies_liveness_insensitive_region_weak
+ (l2 : loc)
+ (h h' : HS.mem)
+ (x: HS.rid)
+: Lemma
+ (requires (modifies l2 h h' /\ region_liveness_insensitive_locs `loc_includes` l2 /\ HS.live_region h x))
+ (ensures (HS.live_region h' x))
+ [SMTPatOr [
+ [SMTPat (modifies l2 h h'); SMTPat (HS.live_region h x)];
+ [SMTPat (modifies l2 h h'); SMTPat (HS.live_region h' x)];
+ ]]
+= modifies_liveness_insensitive_region loc_none l2 h h' xlet modifies_liveness_insensitive_region_mreference_weak
+ (l2 : loc)
+ (h h' : HS.mem)
+ (#t: Type)
+ (#pre: Preorder.preorder t)
+ (x: HS.mreference t pre)
+: Lemma
+ (requires (modifies l2 h h' /\ region_liveness_insensitive_locs `loc_includes` l2 /\ HS.live_region h (HS.frameOf x)))
+ (ensures (HS.live_region h' (HS.frameOf x)))
+ [SMTPatOr [
+ [SMTPat (modifies l2 h h'); SMTPat (HS.live_region h (HS.frameOf x))];
+ [SMTPat (modifies l2 h h'); SMTPat (HS.live_region h' (HS.frameOf x))];
+ ]]
+= modifies_liveness_insensitive_region_mreference loc_none l2 h h' xlet modifies_liveness_insensitive_region_buffer_weak
+ (l2 : loc)
+ (h h' : HS.mem)
+ (#t: Type)
+ (x: B.buffer t)
+: Lemma
+ (requires (modifies l2 h h' /\ region_liveness_insensitive_locs `loc_includes` l2 /\ HS.live_region h (B.frameOf x)))
+ (ensures (HS.live_region h' (B.frameOf x)))
+ [SMTPatOr [
+ [SMTPat (modifies l2 h h'); SMTPat (HS.live_region h (B.frameOf x))];
+ [SMTPat (modifies l2 h h'); SMTPat (HS.live_region h' (B.frameOf x))];
+ ]]
+= modifies_liveness_insensitive_region_buffer loc_none l2 h h' xval modifies_trans
+ (s12: loc)
+ (h1 h2: HS.mem)
+ (s23: loc)
+ (h3: HS.mem)
+: Lemma
+ (requires (modifies s12 h1 h2 /\ modifies s23 h2 h3))
+ (ensures (modifies (loc_union s12 s23) h1 h3))
+ [SMTPat (modifies s12 h1 h2); SMTPat (modifies s23 h2 h3)]val modifies_only_live_regions
+ (rs: Set.set HS.rid)
+ (l: loc)
+ (h h' : HS.mem)
+: Lemma
+ (requires (
+ modifies (loc_union (loc_regions false rs) l) h h' /\
+ (forall r . Set.mem r rs ==> (~ (HS.live_region h r)))
+ ))
+ (ensures (modifies l h h'))val no_upd_fresh_region: r:HS.rid -> l:loc -> h0:HS.mem -> h1:HS.mem -> Lemma
+ (requires (HS.fresh_region r h0 h1 /\ modifies (loc_union (loc_all_regions_from false r) l) h0 h1))
+ (ensures (modifies l h0 h1))
+ [SMTPat (HS.fresh_region r h0 h1); SMTPat (modifies l h0 h1)]val modifies_fresh_frame_popped
+ (h0 h1: HS.mem)
+ (s: loc)
+ (h2 h3: HS.mem)
+: Lemma
+ (requires (
+ HS.fresh_frame h0 h1 /\
+ modifies (loc_union (loc_all_regions_from false (HS.get_tip h1)) s) h1 h2 /\
+ (HS.get_tip h2) == (HS.get_tip h1) /\
+ HS.popped h2 h3
+ ))
+ (ensures (
+ modifies s h0 h3 /\
+ (HS.get_tip h3) == HS.get_tip h0
+ ))
+ [SMTPat (HS.fresh_frame h0 h1); SMTPat (HS.popped h2 h3); SMTPat (modifies s h0 h3)]val modifies_loc_regions_intro
+ (rs: Set.set HS.rid)
+ (h1 h2: HS.mem)
+: Lemma
+ (requires (HS.modifies rs h1 h2))
+ (ensures (modifies (loc_regions true rs) h1 h2))val modifies_loc_addresses_intro
+ (r: HS.rid)
+ (a: Set.set nat)
+ (l: loc)
+ (h1 h2: HS.mem)
+: Lemma
+ (requires (
+ HS.live_region h2 r /\
+ modifies (loc_union (loc_region_only false r) l) h1 h2 /\
+ HS.modifies_ref r a h1 h2
+ ))
+ (ensures (modifies (loc_union (loc_addresses true r a) l) h1 h2))val modifies_ralloc_post
+ (#a: Type)
+ (#rel: Preorder.preorder a)
+ (i: HS.rid)
+ (init: a)
+ (h: HS.mem)
+ (x: HST.mreference a rel { HST.is_eternal_region (HS.frameOf x) } )
+ (h' : HS.mem)
+: Lemma
+ (requires (HST.ralloc_post i init h x h'))
+ (ensures (modifies loc_none h h'))val modifies_salloc_post
+ (#a: Type)
+ (#rel: Preorder.preorder a)
+ (init: a)
+ (h: HS.mem)
+ (x: HST.mreference a rel { HS.is_stack_region (HS.frameOf x) } )
+ (h' : HS.mem)
+: Lemma
+ (requires (HST.salloc_post init h x h'))
+ (ensures (modifies loc_none h h'))val modifies_free
+ (#a: Type)
+ (#rel: Preorder.preorder a)
+ (r: HS.mreference a rel { HS.is_mm r } )
+ (m: HS.mem { m `HS.contains` r } )
+: Lemma
+ (modifies (loc_freed_mreference r) m (HS.free r m))val modifies_none_modifies
+ (h1 h2: HS.mem)
+: Lemma
+ (requires (HST.modifies_none h1 h2))
+ (ensures (modifies loc_none h1 h2))val modifies_buffer_none_modifies
+ (h1 h2: HS.mem)
+: Lemma
+ (requires (B.modifies_none h1 h2))
+ (ensures (modifies loc_none h1 h2))val modifies_0_modifies
+ (h1 h2: HS.mem)
+: Lemma
+ (requires (B.modifies_0 h1 h2))
+ (ensures (modifies loc_none h1 h2))
+ [SMTPat (B.modifies_0 h1 h2)]val modifies_1_modifies
+ (#a: Type)
+ (b: B.buffer a)
+ (h1 h2: HS.mem)
+: Lemma
+ (requires (B.modifies_1 b h1 h2))
+ (ensures (modifies (loc_buffer b) h1 h2))
+ [SMTPat (B.modifies_1 b h1 h2)]val modifies_2_modifies
+ (#a1 #a2: Type)
+ (b1: B.buffer a1)
+ (b2: B.buffer a2)
+ (h1 h2: HS.mem)
+: Lemma
+ (requires (B.modifies_2 b1 b2 h1 h2))
+ (ensures (modifies (loc_union (loc_buffer b1) (loc_buffer b2)) h1 h2))
+ [SMTPat (B.modifies_2 b1 b2 h1 h2)]val modifies_3_modifies
+ (#a1 #a2 #a3: Type)
+ (b1: B.buffer a1)
+ (b2: B.buffer a2)
+ (b3: B.buffer a3)
+ (h1 h2: HS.mem)
+: Lemma
+ (requires (B.modifies_3 b1 b2 b3 h1 h2))
+ (ensures (modifies (loc_union (loc_buffer b1) (loc_union (loc_buffer b2) (loc_buffer b3))) h1 h2))val modifies_buffer_rcreate_post_common
+ (#a: Type)
+ (r: HS.rid)
+ (init: a)
+ (len: FStar.UInt32.t)
+ (b: B.buffer a)
+ (h0 h1: HS.mem)
+: Lemma
+ (requires (B.rcreate_post_common r init len b h0 h1))
+ (ensures (modifies loc_none h0 h1))val mreference_live_buffer_unused_in_disjoint
+ (#t1: Type)
+ (#pre: Preorder.preorder t1)
+ (#t2: Type)
+ (h: HS.mem)
+ (b1: HS.mreference t1 pre)
+ (b2: B.buffer t2)
+: Lemma
+ (requires (HS.contains h b1 /\ B.unused_in b2 h))
+ (ensures (loc_disjoint (loc_freed_mreference b1) (loc_buffer b2)))
+ [SMTPat (HS.contains h b1); SMTPat (B.unused_in b2 h)]val buffer_live_mreference_unused_in_disjoint
+ (#t1: Type)
+ (#t2: Type)
+ (#pre: Preorder.preorder t2)
+ (h: HS.mem)
+ (b1: B.buffer t1)
+ (b2: HS.mreference t2 pre)
+: Lemma
+ (requires (B.live h b1 /\ HS.unused_in b2 h))
+ (ensures (loc_disjoint (loc_buffer b1) (loc_freed_mreference b2)))
+ [SMTPat (B.live h b1); SMTPat (HS.unused_in b2 h)]BEGIN TODO: move to FStar.Monotonic.HyperStack
+val does_not_contain_addr
+ (h: HS.mem)
+ (ra: HS.rid * nat)
+: GTot Type0val not_live_region_does_not_contain_addr
+ (h: HS.mem)
+ (ra: HS.rid * nat)
+: Lemma
+ (requires (~ (HS.live_region h (fst ra))))
+ (ensures (h `does_not_contain_addr` ra))val unused_in_does_not_contain_addr
+ (h: HS.mem)
+ (#a: Type)
+ (#rel: Preorder.preorder a)
+ (r: HS.mreference a rel)
+: Lemma
+ (requires (r `HS.unused_in` h))
+ (ensures (h `does_not_contain_addr` (HS.frameOf r, HS.as_addr r)))val addr_unused_in_does_not_contain_addr
+ (h: HS.mem)
+ (ra: HS.rid * nat)
+: Lemma
+ (requires (HS.live_region h (fst ra) ==> snd ra `Heap.addr_unused_in` (Map.sel (HS.get_hmap h) (fst ra))))
+ (ensures (h `does_not_contain_addr` ra))val free_does_not_contain_addr
+ (#a: Type0)
+ (#rel: Preorder.preorder a)
+ (r: HS.mreference a rel)
+ (m: HS.mem)
+ (x: HS.rid * nat)
+: Lemma
+ (requires (
+ HS.is_mm r /\
+ m `HS.contains` r /\
+ fst x == HS.frameOf r /\
+ snd x == HS.as_addr r
+ ))
+ (ensures (
+ HS.free r m `does_not_contain_addr` x
+ ))
+ [SMTPat (HS.free r m `does_not_contain_addr` x)]val does_not_contain_addr_elim
+ (#a: Type0)
+ (#rel: Preorder.preorder a)
+ (r: HS.mreference a rel)
+ (m: HS.mem)
+ (x: HS.rid * nat)
+: Lemma
+ (requires (
+ m `does_not_contain_addr` x /\
+ HS.frameOf r == fst x /\
+ HS.as_addr r == snd x
+ ))
+ (ensures (~ (m `HS.contains` r)))END TODO
+val modifies_only_live_addresses
+ (r: HS.rid)
+ (a: Set.set nat)
+ (l: loc)
+ (h h' : HS.mem)
+: Lemma
+ (requires (
+ modifies (loc_union (loc_addresses false r a) l) h h' /\
+ (forall x . Set.mem x a ==> h `does_not_contain_addr` (r, x))
+ ))
+ (ensures (modifies l h h'))++Type class instantiation for compositionality with other kinds of memory locations than regions, references or buffers (just in case). +No usage pattern has been found yet.
+
MG
+val cloc_aloc : HS.rid -> nat -> Tot (Type u#1)val cloc_cls: MG.cls cloc_alocval cloc_of_loc (l: loc) : Tot (MG.loc cloc_cls)val loc_of_cloc (l: MG.loc cloc_cls) : Tot locval loc_of_cloc_of_loc (l: loc) : Lemma
+ (loc_of_cloc (cloc_of_loc l) == l)
+ [SMTPat (loc_of_cloc (cloc_of_loc l))]val cloc_of_loc_of_cloc (l: MG.loc cloc_cls) : Lemma
+ (cloc_of_loc (loc_of_cloc l) == l)
+ [SMTPat (cloc_of_loc (loc_of_cloc l))]val cloc_of_loc_none : unit -> Lemma (cloc_of_loc loc_none == MG.loc_none)val cloc_of_loc_union (l1 l2: loc) : Lemma
+ (cloc_of_loc (loc_union l1 l2) == MG.loc_union (cloc_of_loc l1) (cloc_of_loc l2))val cloc_of_loc_addresses
+ (preserve_liveness: bool)
+ (r: HS.rid)
+ (n: Set.set nat)
+: Lemma
+ (cloc_of_loc (loc_addresses preserve_liveness r n) == MG.loc_addresses preserve_liveness r n)val cloc_of_loc_regions
+ (preserve_liveness: bool)
+ (r: Set.set HS.rid)
+: Lemma
+ (cloc_of_loc (loc_regions preserve_liveness r) == MG.loc_regions preserve_liveness r)val loc_includes_to_cloc (l1 l2: loc) : Lemma
+ (loc_includes l1 l2 <==> MG.loc_includes (cloc_of_loc l1) (cloc_of_loc l2))val loc_disjoint_to_cloc (l1 l2: loc) : Lemma
+ (loc_disjoint l1 l2 <==> MG.loc_disjoint (cloc_of_loc l1) (cloc_of_loc l2))val modifies_to_cloc (l: loc) (h1 h2: HS.mem) : Lemma
+ (modifies l h1 h2 <==> MG.modifies (cloc_of_loc l) h1 h2)fsdoc: no-summary-found
-fsdoc: no-comment-found
-* The modifies clause val loc_union_idem:#aloc:aloc_t -> #c:cls aloc -> s:loc c -> (Lemma (==(loc_union s s, s)))The following is useful to make Z3 cut matching loops with modifies_trans and modifies_refl
- Liveness-insensitive memory locations The modifies clause proper BEGIN TODO: move to FStar.Monotonic.HyperStack END TODO * Compositionality HS
+HST
+NOTE: aloc cannot be a member of the class, because of OCaml +extraction. So it must be a parameter of the class instead.
+type aloc_t = HS.rid -> nat -> Tot Typenoeq
+type cls (aloc: aloc_t) : Type = | Cls:
+ (aloc_includes: (
+ (#r: HS.rid) ->
+ (#a: nat) ->
+ aloc r a ->
+ aloc r a ->
+ GTot Type0
+ )) ->
+ (aloc_includes_refl: (
+ (#r: HS.rid) ->
+ (#a: nat) ->
+ (x: aloc r a) ->
+ Lemma
+ (aloc_includes x x)
+ )) ->
+ (aloc_includes_trans: (
+ (#r: HS.rid) ->
+ (#a: nat) ->
+ (x1: aloc r a) ->
+ (x2: aloc r a) ->
+ (x3: aloc r a) ->
+ Lemma
+ (requires (aloc_includes x1 x2 /\ aloc_includes x2 x3))
+ (ensures (aloc_includes x1 x3))
+ )) ->
+ (aloc_disjoint: (
+ (#r: HS.rid) ->
+ (#a: nat) ->
+ (x1: aloc r a) ->
+ (x2: aloc r a) ->
+ GTot Type0
+ )) ->
+ (aloc_disjoint_sym: (
+ (#r: HS.rid) ->
+ (#a: nat) ->
+ (x1: aloc r a) ->
+ (x2: aloc r a) ->
+ Lemma
+ (aloc_disjoint x1 x2 <==> aloc_disjoint x2 x1)
+ )) ->
+ (aloc_disjoint_includes: (
+ (#r: HS.rid) ->
+ (#a: nat) ->
+ (larger1: aloc r a) ->
+ (larger2: aloc r a) ->
+ (smaller1: aloc r a) ->
+ (smaller2: aloc r a) ->
+ Lemma
+ (requires (aloc_disjoint larger1 larger2 /\ larger1 `aloc_includes` smaller1 /\ larger2 `aloc_includes` smaller2))
+ (ensures (aloc_disjoint smaller1 smaller2))
+ )) ->
+ (aloc_preserved: (
+ (#r: HS.rid) ->
+ (#a: nat) ->
+ aloc r a ->
+ HS.mem ->
+ HS.mem ->
+ GTot Type0
+ )) ->
+ (aloc_preserved_refl: (
+ (#r: HS.rid) ->
+ (#a: nat) ->
+ (x: aloc r a) ->
+ (h: HS.mem) ->
+ Lemma
+ (aloc_preserved x h h)
+ )) ->
+ (aloc_preserved_trans: (
+ (#r: HS.rid) ->
+ (#a: nat) ->
+ (x: aloc r a) ->
+ (h1: HS.mem) ->
+ (h2: HS.mem) ->
+ (h3: HS.mem) ->
+ Lemma
+ (requires (aloc_preserved x h1 h2 /\ aloc_preserved x h2 h3))
+ (ensures (aloc_preserved x h1 h3))
+ )) ->if any reference at this address is preserved, then any location at this address is preserved
+(same_mreference_aloc_preserved: (
+ (#r: HS.rid) ->
+ (#a: nat) ->
+ (b: aloc r a) ->
+ (h1: HS.mem) ->
+ (h2: HS.mem) ->
+ (f: (
+ (a' : Type0) ->
+ (pre: Preorder.preorder a') ->
+ (r': HS.mreference a' pre) ->
+ Lemma
+ (requires (h1 `HS.contains` r' /\ r == HS.frameOf r' /\ a == HS.as_addr r'))
+ (ensures (h2 `HS.contains` r' /\ h1 `HS.sel` r' == h2 `HS.sel` r'))
+ )) ->
+ Lemma
+ (aloc_preserved b h1 h2)
+)) ->
+cls alocval loc (#aloc: aloc_t u#x) (c: cls aloc) : Tot (Type u#x)val loc_none (#aloc: aloc_t) (#c: cls aloc): Tot (loc c)val loc_union
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s1 s2: loc c)
+: GTot (loc c)The following is useful to make Z3 cut matching loops with +modifies_trans and modifies_refl
+val loc_union_idem
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s: loc c)
+: Lemma
+ (loc_union s s == s)val loc_union_comm
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s1 s2: loc c)
+: Lemma
+ (loc_union s1 s2 == loc_union s2 s1)val loc_union_assoc
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s1 s2 s3: loc c)
+: Lemma
+ (loc_union s1 (loc_union s2 s3) == loc_union (loc_union s1 s2) s3)val loc_union_loc_none_l
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s: loc c)
+: Lemma
+ (loc_union loc_none s == s)val loc_union_loc_none_r
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s: loc c)
+: Lemma
+ (loc_union s loc_none == s)val loc_of_aloc
+ (#aloc: aloc_t) (#c: cls aloc)
+ (#r: HS.rid)
+ (#n: nat)
+ (b: aloc r n)
+: GTot (loc c)val loc_of_aloc_not_none
+ (#aloc: aloc_t) (#c: cls aloc)
+ (#r: HS.rid)
+ (#n: nat)
+ (b: aloc r n)
+: Lemma (loc_of_aloc #_ #c b == loc_none ==> False)val loc_addresses
+ (#aloc: aloc_t) (#c: cls aloc)
+ (preserve_liveness: bool)
+ (r: HS.rid)
+ (n: Set.set nat)
+: GTot (loc c)val loc_regions
+ (#aloc: aloc_t) (#c: cls aloc)
+ (preserve_liveness: bool)
+ (r: Set.set HS.rid)
+: GTot (loc c)let loc_mreference
+ (#aloc: aloc_t) (#c: cls aloc)
+ (#a: Type)
+ (#p: Preorder.preorder a)
+ (b: HS.mreference a p)
+: GTot (loc c)
+= loc_addresses true (HS.frameOf b) (Set.singleton (HS.as_addr b))let loc_freed_mreference
+ (#aloc: aloc_t) (#c: cls aloc)
+ (#a: Type)
+ (#p: Preorder.preorder a)
+ (b: HS.mreference a p)
+: GTot (loc c)
+= loc_addresses false (HS.frameOf b) (Set.singleton (HS.as_addr b))let loc_region_only
+ (#aloc: aloc_t) (#c: cls aloc)
+ (preserve_liveness: bool)
+ (r: HS.rid)
+: GTot (loc c)
+= loc_regions preserve_liveness (Set.singleton r)let loc_all_regions_from
+ (#aloc: aloc_t) (#c: cls aloc)
+ (preserve_liveness: bool)
+ (r: HS.rid)
+: GTot (loc c)
+= loc_regions preserve_liveness (HS.mod_set (Set.singleton r))Inclusion of memory locations
+val loc_includes
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s1 s2: loc c)
+: GTot Type0val loc_includes_refl
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s: loc c)
+: Lemma
+ (loc_includes s s)val loc_includes_trans
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s1 s2 s3: loc c)
+: Lemma
+ (requires (loc_includes s1 s2 /\ loc_includes s2 s3))
+ (ensures (loc_includes s1 s3))val loc_includes_union_r
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s s1 s2: loc c)
+: Lemma
+ (requires (loc_includes s s1 /\ loc_includes s s2))
+ (ensures (loc_includes s (loc_union s1 s2)))val loc_includes_union_l
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s1 s2 s: loc c)
+: Lemma
+ (requires (loc_includes s1 s \/ loc_includes s2 s))
+ (ensures (loc_includes (loc_union s1 s2) s))val loc_includes_none
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s: loc c)
+: Lemma
+ (loc_includes s loc_none)val loc_includes_none_elim
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s: loc c)
+: Lemma
+ (requires (loc_includes loc_none s))
+ (ensures (s == loc_none))val loc_includes_aloc
+ (#aloc: aloc_t) (#c: cls aloc)
+ (#r: HS.rid)
+ (#n: nat)
+ (b1 b2: aloc r n)
+: Lemma
+ (requires (c.aloc_includes b1 b2))
+ (ensures (loc_includes (loc_of_aloc b1) (loc_of_aloc #_ #c b2)))val loc_includes_aloc_elim
+ (#aloc: aloc_t) (#c: cls aloc)
+ (#r1 #r2: HS.rid)
+ (#n1 #n2: nat)
+ (b1: aloc r1 n1)
+ (b2: aloc r2 n2)
+: Lemma
+ (requires (loc_includes (loc_of_aloc b1) (loc_of_aloc #_ #c b2)))
+ (ensures (r1 == r2 /\ n1 == n2 /\ c.aloc_includes b1 b2))val loc_includes_addresses_aloc
+ (#aloc: aloc_t) (#c: cls aloc)
+ (preserve_liveness: bool)
+ (r: HS.rid)
+ (s: Set.set nat)
+ (#a: nat)
+ (p: aloc r a)
+: Lemma
+ (requires (Set.mem a s))
+ (ensures (loc_includes (loc_addresses preserve_liveness r s) (loc_of_aloc #_ #c p)))val loc_includes_region_aloc
+ (#aloc: aloc_t) (#c: cls aloc)
+ (preserve_liveness: bool)
+ (s: Set.set HS.rid)
+ (#r: HS.rid)
+ (#a: nat)
+ (b: aloc r a)
+: Lemma
+ (requires (Set.mem r s))
+ (ensures (loc_includes (loc_regions preserve_liveness s) (loc_of_aloc #_ #c b)))val loc_includes_region_addresses
+ (#aloc: aloc_t) (#c: cls aloc)
+ (preserve_liveness1 preserve_liveness2: bool)
+ (s: Set.set HS.rid)
+ (r: HS.rid)
+ (a: Set.set nat)
+: Lemma
+ (requires (Set.mem r s))
+ (ensures (loc_includes (loc_regions #_ #c preserve_liveness1 s) (loc_addresses preserve_liveness2 r a)))val loc_includes_region_region
+ (#aloc: aloc_t) (#c: cls aloc)
+ (preserve_liveness1 preserve_liveness2: bool)
+ (s1 s2: Set.set HS.rid)
+: Lemma
+ (requires ((preserve_liveness1 ==> preserve_liveness2) /\ Set.subset s2 s1))
+ (ensures (loc_includes (loc_regions #_ #c preserve_liveness1 s1) (loc_regions preserve_liveness2 s2)))val loc_includes_region_union_l
+ (#aloc: aloc_t) (#c: cls aloc)
+ (preserve_liveness: bool)
+ (l: loc c)
+ (s1 s2: Set.set HS.rid)
+: Lemma
+ (requires (loc_includes l (loc_regions preserve_liveness (Set.intersect s2 (Set.complement s1)))))
+ (ensures (loc_includes (loc_union (loc_regions preserve_liveness s1) l) (loc_regions preserve_liveness s2)))val loc_includes_addresses_addresses
+ (#aloc: aloc_t) (c: cls aloc)
+ (preserve_liveness1 preserve_liveness2: bool)
+ (r: HS.rid)
+ (a1 a2: Set.set nat)
+: Lemma
+ (requires ((preserve_liveness1 ==> preserve_liveness2) /\ Set.subset a2 a1))
+ (ensures (loc_includes #_ #c (loc_addresses preserve_liveness1 r a1) (loc_addresses preserve_liveness2 r a2)))Disjointness of two memory locations
+val loc_disjoint
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s1 s2: loc c)
+: GTot Type0val loc_disjoint_sym
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s1 s2: loc c)
+: Lemma
+ (requires (loc_disjoint s1 s2))
+ (ensures (loc_disjoint s2 s1))val loc_disjoint_none_r
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s: loc c)
+: Lemma
+ (ensures (loc_disjoint s loc_none))val loc_disjoint_union_r
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s s1 s2: loc c)
+: Lemma
+ (requires (loc_disjoint s s1 /\ loc_disjoint s s2))
+ (ensures (loc_disjoint s (loc_union s1 s2)))val loc_disjoint_includes
+ (#aloc: aloc_t) (#c: cls aloc)
+ (p1 p2 p1' p2' : loc c)
+: Lemma
+ (requires (loc_includes p1 p1' /\ loc_includes p2 p2' /\ loc_disjoint p1 p2))
+ (ensures (loc_disjoint p1' p2'))val loc_disjoint_aloc_intro
+ (#aloc: aloc_t) (#c: cls aloc)
+ (#r1: HS.rid)
+ (#a1: nat)
+ (#r2: HS.rid)
+ (#a2: nat)
+ (b1: aloc r1 a1)
+ (b2: aloc r2 a2)
+: Lemma
+ (requires ((r1 == r2 /\ a1 == a2) ==> c.aloc_disjoint b1 b2))
+ (ensures (loc_disjoint (loc_of_aloc b1) (loc_of_aloc #_ #c b2)))val loc_disjoint_aloc_elim
+ (#aloc: aloc_t) (#c: cls aloc)
+ (#r1: HS.rid)
+ (#a1: nat)
+ (#r2: HS.rid)
+ (#a2: nat)
+ (b1: aloc r1 a1)
+ (b2: aloc r2 a2)
+: Lemma
+ (requires (loc_disjoint (loc_of_aloc b1) (loc_of_aloc #_ #c b2)))
+ (ensures ((r1 == r2 /\ a1 == a2) ==> c.aloc_disjoint b1 b2))val loc_disjoint_addresses_intro
+ (#aloc: aloc_t) (#c: cls aloc)
+ (preserve_liveness1 preserve_liveness2: bool)
+ (r1 r2: HS.rid)
+ (n1 n2: Set.set nat)
+: Lemma
+ (requires (r1 <> r2 \/ Set.subset (Set.intersect n1 n2) Set.empty))
+ (ensures (loc_disjoint (loc_addresses #_ #c preserve_liveness1 r1 n1) (loc_addresses preserve_liveness2 r2 n2)))let loc_disjoint_addresses #aloc #c = loc_disjoint_addresses_intro #aloc #cval loc_disjoint_addresses_elim
+ (#aloc: aloc_t) (#c: cls aloc)
+ (preserve_liveness1 preserve_liveness2: bool)
+ (r1 r2: HS.rid)
+ (n1 n2: Set.set nat)
+: Lemma
+ (requires (loc_disjoint (loc_addresses #_ #c preserve_liveness1 r1 n1) (loc_addresses preserve_liveness2 r2 n2)))
+ (ensures (r1 <> r2 \/ Set.subset (Set.intersect n1 n2) Set.empty))val loc_disjoint_aloc_addresses_intro
+ (#aloc: aloc_t) (#c: cls aloc)
+ (#r' : HS.rid)
+ (#a' : nat)
+ (p: aloc r' a')
+ (preserve_liveness: bool)
+ (r: HS.rid)
+ (n: Set.set nat)
+: Lemma
+ (requires (r == r' ==> (~ (Set.mem a' n))))
+ (ensures (loc_disjoint (loc_of_aloc p) (loc_addresses #_ #c preserve_liveness r n)))val loc_disjoint_aloc_addresses_elim
+ (#aloc: aloc_t) (#c: cls aloc)
+ (#r' : HS.rid)
+ (#a' : nat)
+ (p: aloc r' a')
+ (preserve_liveness: bool)
+ (r: HS.rid)
+ (n: Set.set nat)
+: Lemma
+ (requires (loc_disjoint (loc_of_aloc p) (loc_addresses #_ #c preserve_liveness r n)))
+ (ensures (r == r' ==> (~ (Set.mem a' n))))val loc_disjoint_regions
+ (#aloc: aloc_t) (#c: cls aloc)
+ (preserve_liveness1 preserve_liveness2: bool)
+ (rs1 rs2: Set.set HS.rid)
+: Lemma
+ (requires (Set.subset (Set.intersect rs1 rs2) Set.empty))
+ (ensures (loc_disjoint (loc_regions #_ #c preserve_liveness1 rs1) (loc_regions preserve_liveness2 rs2)))Liveness-insensitive memory locations
+val address_liveness_insensitive_locs (#aloc: aloc_t) (c: cls aloc) : Tot (loc c)val loc_includes_address_liveness_insensitive_locs_aloc (#aloc: aloc_t) (#c: cls aloc) (#r: HS.rid) (#n: nat) (a: aloc r n) : Lemma
+ (loc_includes (address_liveness_insensitive_locs c) (loc_of_aloc a))val loc_includes_address_liveness_insensitive_locs_addresses (#aloc: aloc_t) (c: cls aloc) (r: HS.rid) (a: Set.set nat) : Lemma
+ (loc_includes (address_liveness_insensitive_locs c) (loc_addresses true r a))val region_liveness_insensitive_locs (#al: aloc_t) (c: cls al) : Tot (loc c)val loc_includes_region_liveness_insensitive_locs_address_liveness_insensitive_locs (#al: aloc_t) (c: cls al) : Lemma
+ (loc_includes (region_liveness_insensitive_locs c) (address_liveness_insensitive_locs c))val loc_includes_region_liveness_insensitive_locs_loc_regions
+ (#al: aloc_t) (c: cls al) (r: Set.set HS.rid)
+: Lemma
+ (region_liveness_insensitive_locs c `loc_includes` loc_regions #_ #c true r)val loc_includes_region_liveness_insensitive_locs_loc_addresses
+ (#al: aloc_t) (c: cls al) (preserve_liveness: bool) (r: HS.rid) (a: Set.set nat)
+: Lemma
+ (region_liveness_insensitive_locs c `loc_includes` loc_addresses #_ #c preserve_liveness r a)val loc_includes_region_liveness_insensitive_locs_loc_of_aloc
+ (#al: aloc_t) (c: cls al) (#r: HS.rid) (#a: nat) (x: al r a)
+: Lemma
+ (region_liveness_insensitive_locs c `loc_includes` loc_of_aloc #_ #c x)The modifies clause proper
+val modifies
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s: loc c)
+ (h1 h2: HS.mem)
+: GTot Type0val modifies_intro
+ (#al: aloc_t) (#c: cls al) (l: loc c) (h h' : HS.mem)
+ (regions: (
+ (r: HS.rid) ->
+ Lemma
+ (requires (HS.live_region h r))
+ (ensures (HS.live_region h' r))
+ ))
+ (mrefs: (
+ (t: Type0) ->
+ (pre: Preorder.preorder t) ->
+ (b: HS.mreference t pre) ->
+ Lemma
+ (requires ((loc_disjoint (loc_mreference b) l) /\ HS.contains h b))
+ (ensures (HS.contains h' b /\ HS.sel h' b == HS.sel h b))
+ ))
+ (livenesses: (
+ (t: Type0) ->
+ (pre: Preorder.preorder t) ->
+ (b: HS.mreference t pre) ->
+ Lemma
+ (requires (HS.contains h b))
+ (ensures (HS.contains h' b))
+ ))
+ (addr_unused_in: (
+ (r: HS.rid) ->
+ (n: nat) ->
+ Lemma
+ (requires (
+ HS.live_region h r /\
+ HS.live_region h' r /\ n `Heap.addr_unused_in` (HS.get_hmap h' `Map.sel` r)
+ ))
+ (ensures (n `Heap.addr_unused_in` (HS.get_hmap h `Map.sel` r)))
+ ))
+ (alocs: (
+ (r: HS.rid) ->
+ (a: nat) ->
+ (x: al r a) ->
+ Lemma
+ (requires (loc_disjoint (loc_of_aloc x) l))
+ (ensures (c.aloc_preserved x h h'))
+ ))
+: Lemma
+ (modifies l h h')val modifies_none_intro
+ (#al: aloc_t) (#c: cls al) (h h' : HS.mem)
+ (regions: (
+ (r: HS.rid) ->
+ Lemma
+ (requires (HS.live_region h r))
+ (ensures (HS.live_region h' r))
+ ))
+ (mrefs: (
+ (t: Type0) ->
+ (pre: Preorder.preorder t) ->
+ (b: HS.mreference t pre) ->
+ Lemma
+ (requires (HS.contains h b))
+ (ensures (HS.contains h' b /\ HS.sel h' b == HS.sel h b))
+ ))
+ (addr_unused_in: (
+ (r: HS.rid) ->
+ (n: nat) ->
+ Lemma
+ (requires (HS.live_region h r /\ HS.live_region h' r /\ n `Heap.addr_unused_in` (HS.get_hmap h' `Map.sel` r)))
+ (ensures (n `Heap.addr_unused_in` (HS.get_hmap h `Map.sel` r)))
+ ))
+: Lemma
+ (modifies (loc_none #_ #c) h h')val modifies_address_intro
+ (#al: aloc_t) (#c: cls al) (r: HS.rid) (n: nat) (h h' : HS.mem)
+ (regions: (
+ (r: HS.rid) ->
+ Lemma
+ (requires (HS.live_region h r))
+ (ensures (HS.live_region h' r))
+ ))
+ (mrefs: (
+ (t: Type0) ->
+ (pre: Preorder.preorder t) ->
+ (b: HS.mreference t pre) ->
+ Lemma
+ (requires ((r <> HS.frameOf b \/ n <> HS.as_addr b) /\ HS.contains h b))
+ (ensures (HS.contains h' b /\ HS.sel h' b == HS.sel h b))
+ ))
+ (addr_unused_in: (
+ (r': HS.rid) ->
+ (n' : nat) ->
+ Lemma
+ (requires ((r' <> r \/ n' <> n) /\ HS.live_region h r' /\ HS.live_region h' r' /\ n' `Heap.addr_unused_in` (HS.get_hmap h' `Map.sel` r')))
+ (ensures (n' `Heap.addr_unused_in` (HS.get_hmap h `Map.sel` r')))
+ ))
+: Lemma
+ (modifies (loc_addresses #_ #c false r (Set.singleton n)) h h')val modifies_aloc_intro
+ (#al: aloc_t) (#c: cls al) (#r: HS.rid) (#n: nat) (z: al r n) (h h' : HS.mem)
+ (regions: (
+ (r: HS.rid) ->
+ Lemma
+ (requires (HS.live_region h r))
+ (ensures (HS.live_region h' r))
+ ))
+ (mrefs: (
+ (t: Type0) ->
+ (pre: Preorder.preorder t) ->
+ (b: HS.mreference t pre) ->
+ Lemma
+ (requires ((r <> HS.frameOf b \/ n <> HS.as_addr b) /\ HS.contains h b))
+ (ensures (HS.contains h' b /\ HS.sel h' b == HS.sel h b))
+ ))
+ (livenesses: (
+ (t: Type0) ->
+ (pre: Preorder.preorder t) ->
+ (b: HS.mreference t pre) ->
+ Lemma
+ (requires (HS.contains h b))
+ (ensures (HS.contains h' b))
+ ))
+ (addr_unused_in: (
+ (r: HS.rid) ->
+ (n: nat) ->
+ Lemma
+ (requires (HS.live_region h r /\ HS.live_region h' r /\ n `Heap.addr_unused_in` (HS.get_hmap h' `Map.sel` r)))
+ (ensures (n `Heap.addr_unused_in` (HS.get_hmap h `Map.sel` r)))
+ ))
+ (alocs: (
+ (x: al r n) ->
+ Lemma
+ (requires (c.aloc_disjoint x z))
+ (ensures (c.aloc_preserved x h h'))
+ ))
+: Lemma
+ (modifies (loc_of_aloc #_ #c z) h h')val modifies_live_region
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s: loc c)
+ (h1 h2: HS.mem)
+ (r: HS.rid)
+: Lemma
+ (requires (modifies s h1 h2 /\ loc_disjoint s (loc_region_only false r) /\ HS.live_region h1 r))
+ (ensures (HS.live_region h2 r))val modifies_mreference_elim
+ (#aloc: aloc_t) (#c: cls aloc)
+ (#t: Type)
+ (#pre: Preorder.preorder t)
+ (b: HS.mreference t pre)
+ (p: loc c)
+ (h h': HS.mem)
+: Lemma
+ (requires (
+ loc_disjoint (loc_mreference b) p /\
+ HS.contains h b /\
+ modifies p h h'
+ ))
+ (ensures (
+ HS.contains h' b /\
+ HS.sel h b == HS.sel h' b
+ ))val modifies_aloc_elim
+ (#aloc: aloc_t) (#c: cls aloc)
+ (#r: HS.rid)
+ (#a: nat)
+ (b: aloc r a)
+ (p: loc c)
+ (h h': HS.mem)
+: Lemma
+ (requires (
+ loc_disjoint (loc_of_aloc b) p /\
+ modifies p h h'
+ ))
+ (ensures (
+ c.aloc_preserved b h h'
+ ))val modifies_refl
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s: loc c)
+ (h: HS.mem)
+: Lemma
+ (modifies s h h)val modifies_loc_includes
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s1: loc c)
+ (h h': HS.mem)
+ (s2: loc c)
+: Lemma
+ (requires (modifies s2 h h' /\ loc_includes s1 s2))
+ (ensures (modifies s1 h h'))val modifies_preserves_liveness
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s1 s2: loc c)
+ (h h' : HS.mem)
+ (#t: Type)
+ (#pre: Preorder.preorder t)
+ (r: HS.mreference t pre)
+: Lemma
+ (requires (modifies (loc_union s1 s2) h h' /\ loc_disjoint s1 (loc_mreference r) /\ loc_includes (address_liveness_insensitive_locs c) s2 /\ h `HS.contains` r))
+ (ensures (h' `HS.contains` r))val modifies_preserves_liveness_strong
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s1 s2: loc c)
+ (h h' : HS.mem)
+ (#t: Type)
+ (#pre: Preorder.preorder t)
+ (r: HS.mreference t pre)
+ (x: aloc (HS.frameOf r) (HS.as_addr r))
+: Lemma
+ (requires (modifies (loc_union s1 s2) h h' /\ loc_disjoint s1 (loc_of_aloc #_ #c #(HS.frameOf r) #(HS.as_addr r) x) /\ loc_includes (address_liveness_insensitive_locs c) s2 /\ h `HS.contains` r))
+ (ensures (h' `HS.contains` r))val modifies_preserves_region_liveness
+ (#al: aloc_t) (#c: cls al)
+ (l1 l2: loc c)
+ (h h' : HS.mem)
+ (r: HS.rid)
+: Lemma
+ (requires (modifies (loc_union l1 l2) h h' /\ region_liveness_insensitive_locs c `loc_includes` l2 /\ loc_disjoint (loc_region_only false r) l1 /\ HS.live_region h r))
+ (ensures (HS.live_region h' r))val modifies_preserves_region_liveness_reference
+ (#al: aloc_t) (#c: cls al)
+ (l1 l2: loc c)
+ (h h' : HS.mem)
+ (#t: Type)
+ (#pre: Preorder.preorder t)
+ (r: HS.mreference t pre)
+: Lemma
+ (requires (modifies (loc_union l1 l2) h h' /\ region_liveness_insensitive_locs c `loc_includes` l2 /\ loc_disjoint (loc_mreference r) l1 /\ HS.live_region h (HS.frameOf r)))
+ (ensures (HS.live_region h' (HS.frameOf r)))val modifies_preserves_region_liveness_aloc
+ (#al: aloc_t) (#c: cls al)
+ (l1 l2: loc c)
+ (h h' : HS.mem)
+ (#r: HS.rid)
+ (#n: nat)
+ (x: al r n)
+: Lemma
+ (requires (modifies (loc_union l1 l2) h h' /\ region_liveness_insensitive_locs c `loc_includes` l2 /\ loc_disjoint (loc_of_aloc x) l1 /\ HS.live_region h r))
+ (ensures (HS.live_region h' r))val modifies_trans
+ (#aloc: aloc_t) (#c: cls aloc)
+ (s12: loc c)
+ (h1 h2: HS.mem)
+ (s23: loc c)
+ (h3: HS.mem)
+: Lemma
+ (requires (modifies s12 h1 h2 /\ modifies s23 h2 h3))
+ (ensures (modifies (loc_union s12 s23) h1 h3))val modifies_only_live_regions
+ (#aloc: aloc_t) (#c: cls aloc)
+ (rs: Set.set HS.rid)
+ (l: loc c)
+ (h h' : HS.mem)
+: Lemma
+ (requires (
+ modifies (loc_union (loc_regions false rs) l) h h' /\
+ (forall r . Set.mem r rs ==> (~ (HS.live_region h r)))
+ ))
+ (ensures (modifies l h h'))val no_upd_fresh_region
+ (#aloc: aloc_t) (#c: cls aloc)
+ (r:HS.rid)
+ (l:loc c)
+ (h0:HS.mem)
+ (h1:HS.mem)
+: Lemma
+ (requires (HS.fresh_region r h0 h1 /\ modifies (loc_union (loc_all_regions_from false r) l) h0 h1))
+ (ensures (modifies l h0 h1))val fresh_frame_modifies
+ (#aloc: aloc_t) (c: cls aloc)
+ (h0 h1: HS.mem)
+: Lemma
+ (requires (HS.fresh_frame h0 h1))
+ (ensures (modifies #_ #c loc_none h0 h1))val new_region_modifies
+ (#al: aloc_t)
+ (c: cls al)
+ (m0: HS.mem)
+ (r0: HS.rid)
+ (col: option int)
+: Lemma
+ (requires (HST.is_eternal_region r0 /\ HS.live_region m0 r0 /\ (None? col \/ HS.is_heap_color (Some?.v col))))
+ (ensures (
+ let (_, m1) = HS.new_eternal_region m0 r0 col in
+ modifies (loc_none #_ #c) m0 m1
+ ))val popped_modifies
+ (#aloc: aloc_t) (c: cls aloc)
+ (h0 h1: HS.mem) : Lemma
+ (requires (HS.popped h0 h1))
+ (ensures (modifies #_ #c (loc_region_only false (HS.get_tip h0)) h0 h1))val modifies_fresh_frame_popped
+ (#aloc: aloc_t) (#c: cls aloc)
+ (h0 h1: HS.mem)
+ (s: loc c)
+ (h2 h3: HS.mem)
+: Lemma
+ (requires (
+ HS.fresh_frame h0 h1 /\
+ modifies (loc_union (loc_all_regions_from false (HS.get_tip h1)) s) h1 h2 /\
+ HS.get_tip h2 == HS.get_tip h1 /\
+ HS.popped h2 h3
+ ))
+ (ensures (
+ modifies s h0 h3 /\
+ HS.get_tip h3 == HS.get_tip h0
+ ))val modifies_loc_regions_intro
+ (#aloc: aloc_t) (#c: cls aloc)
+ (rs: Set.set HS.rid)
+ (h1 h2: HS.mem)
+: Lemma
+ (requires (HS.modifies rs h1 h2))
+ (ensures (modifies (loc_regions #_ #c true rs) h1 h2))val modifies_loc_addresses_intro
+ (#aloc: aloc_t) (#c: cls aloc)
+ (r: HS.rid)
+ (a: Set.set nat)
+ (l: loc c)
+ (h1 h2: HS.mem)
+: Lemma
+ (requires (
+ HS.live_region h2 r /\
+ modifies (loc_union (loc_region_only false r) l) h1 h2 /\
+ HS.modifies_ref r a h1 h2
+ ))
+ (ensures (modifies (loc_union (loc_addresses true r a) l) h1 h2))val modifies_ralloc_post
+ (#aloc: aloc_t) (#c: cls aloc)
+ (#a: Type)
+ (#rel: Preorder.preorder a)
+ (i: HS.rid)
+ (init: a)
+ (h: HS.mem)
+ (x: HST.mreference a rel)
+ (h' : HS.mem)
+: Lemma
+ (requires (HST.ralloc_post i init h x h'))
+ (ensures (modifies (loc_none #_ #c) h h'))val modifies_salloc_post
+ (#aloc: aloc_t) (#c: cls aloc)
+ (#a: Type)
+ (#rel: Preorder.preorder a)
+ (init: a)
+ (h: HS.mem)
+ (x: HST.mreference a rel { HS.is_stack_region (HS.frameOf x) } )
+ (h' : HS.mem)
+: Lemma
+ (requires (HST.salloc_post init h x h'))
+ (ensures (modifies (loc_none #_ #c) h h'))val modifies_free
+ (#aloc: aloc_t) (#c: cls aloc)
+ (#a: Type)
+ (#rel: Preorder.preorder a)
+ (r: HS.mreference a rel { HS.is_mm r } )
+ (m: HS.mem { m `HS.contains` r } )
+: Lemma
+ (modifies (loc_freed_mreference #_ #c r) m (HS.free r m))val modifies_none_modifies
+ (#aloc: aloc_t) (#c: cls aloc)
+ (h1 h2: HS.mem)
+: Lemma
+ (requires (HST.modifies_none h1 h2))
+ (ensures (modifies (loc_none #_ #c) h1 h2))val modifies_upd
+ (#aloc: aloc_t) (#c: cls aloc)
+ (#t: Type) (#pre: Preorder.preorder t)
+ (r: HS.mreference t pre)
+ (v: t)
+ (h: HS.mem)
+: Lemma
+ (requires (HS.contains h r))
+ (ensures (modifies #_ #c (loc_mreference r) h (HS.upd h r v)))val modifies_strengthen
+ (#al: aloc_t) (#c: cls al) (l: loc c) (#r0: HS.rid) (#a0: nat) (al0: al r0 a0) (h h' : HS.mem)
+ (alocs: (
+ (f: ((t: Type) -> (pre: Preorder.preorder t) -> (m: HS.mreference t pre) -> Lemma
+ (requires (HS.frameOf m == r0 /\ HS.as_addr m == a0 /\ HS.contains h m))
+ (ensures (HS.contains h' m))
+ )) ->
+ (x: al r0 a0) ->
+ Lemma
+ (requires (c.aloc_disjoint x al0 /\ loc_disjoint (loc_of_aloc x) l))
+ (ensures (c.aloc_preserved x h h'))
+ ))
+: Lemma
+ (requires (modifies (loc_union l (loc_addresses true r0 (Set.singleton a0))) h h'))
+ (ensures (modifies (loc_union l (loc_of_aloc al0)) h h'))BEGIN TODO: move to FStar.Monotonic.HyperStack
+val does_not_contain_addr
+ (h: HS.mem)
+ (ra: HS.rid * nat)
+: GTot Type0val not_live_region_does_not_contain_addr
+ (h: HS.mem)
+ (ra: HS.rid * nat)
+: Lemma
+ (requires (~ (HS.live_region h (fst ra))))
+ (ensures (h `does_not_contain_addr` ra))val unused_in_does_not_contain_addr
+ (h: HS.mem)
+ (#a: Type)
+ (#rel: Preorder.preorder a)
+ (r: HS.mreference a rel)
+: Lemma
+ (requires (r `HS.unused_in` h))
+ (ensures (h `does_not_contain_addr` (HS.frameOf r, HS.as_addr r)))val addr_unused_in_does_not_contain_addr
+ (h: HS.mem)
+ (ra: HS.rid * nat)
+: Lemma
+ (requires (HS.live_region h (fst ra) ==> snd ra `Heap.addr_unused_in` (HS.get_hmap h `Map.sel` (fst ra))))
+ (ensures (h `does_not_contain_addr` ra))val does_not_contain_addr_addr_unused_in
+ (h: HS.mem)
+ (ra: HS.rid * nat)
+: Lemma
+ (requires (h `does_not_contain_addr` ra))
+ (ensures (HS.live_region h (fst ra) ==> snd ra `Heap.addr_unused_in` (HS.get_hmap h `Map.sel` (fst ra))))val free_does_not_contain_addr
+ (#a: Type0)
+ (#rel: Preorder.preorder a)
+ (r: HS.mreference a rel)
+ (m: HS.mem)
+ (x: HS.rid * nat)
+: Lemma
+ (requires (
+ HS.is_mm r /\
+ m `HS.contains` r /\
+ fst x == HS.frameOf r /\
+ snd x == HS.as_addr r
+ ))
+ (ensures (
+ HS.free r m `does_not_contain_addr` x
+ ))val does_not_contain_addr_elim
+ (#a: Type0)
+ (#rel: Preorder.preorder a)
+ (r: HS.mreference a rel)
+ (m: HS.mem)
+ (x: HS.rid * nat)
+: Lemma
+ (requires (
+ m `does_not_contain_addr` x /\
+ HS.frameOf r == fst x /\
+ HS.as_addr r == snd x
+ ))
+ (ensures (~ (m `HS.contains` r)))END TODO
+val loc_not_unused_in (#al: aloc_t) (c: cls al) (h: HS.mem) : GTot (loc c)val loc_unused_in (#al: aloc_t) (c: cls al) (h: HS.mem) : GTot (loc c)val loc_regions_unused_in (#al: aloc_t) (c: cls al) (h: HS.mem) (rs: Set.set HS.rid) : Lemma
+ (requires (forall r . Set.mem r rs ==> (~ (HS.live_region h r))))
+ (ensures (loc_unused_in c h `loc_includes` loc_regions false rs))val loc_addresses_unused_in (#al: aloc_t) (c: cls al) (r: HS.rid) (a: Set.set nat) (h: HS.mem) : Lemma
+ (requires (forall x . Set.mem x a ==> h `does_not_contain_addr` (r, x)))
+ (ensures (loc_unused_in c h `loc_includes` loc_addresses false r a))val loc_addresses_not_unused_in (#al: aloc_t) (c: cls al) (r: HS.rid) (a: Set.set nat) (h: HS.mem) : Lemma
+ (requires (forall x . Set.mem x a ==> ~ (h `does_not_contain_addr` (r, x))))
+ (ensures (loc_not_unused_in c h `loc_includes` loc_addresses false r a))val loc_unused_in_not_unused_in_disjoint (#al: aloc_t) (c: cls al) (h: HS.mem) : Lemma
+ (loc_unused_in c h `loc_disjoint` loc_not_unused_in c h)val not_live_region_loc_not_unused_in_disjoint
+ (#al: aloc_t)
+ (c: cls al)
+ (h0: HS.mem)
+ (r: HS.rid)
+: Lemma
+ (requires (~ (HS.live_region h0 r)))
+ (ensures (loc_disjoint (loc_region_only false r) (loc_not_unused_in c h0)))val modifies_address_liveness_insensitive_unused_in
+ (#al: aloc_t)
+ (c: cls al)
+ (h h' : HS.mem)
+: Lemma
+ (requires (modifies (address_liveness_insensitive_locs c) h h'))
+ (ensures (loc_not_unused_in c h' `loc_includes` loc_not_unused_in c h /\ loc_unused_in c h `loc_includes` loc_unused_in c h'))val modifies_only_not_unused_in
+ (#al: aloc_t)
+ (#c: cls al)
+ (l: loc c)
+ (h h' : HS.mem)
+: Lemma
+ (requires (modifies (loc_unused_in c h `loc_union` l) h h'))
+ (ensures (modifies l h h'))let modifies_only_live_addresses
+ (#aloc: aloc_t) (#c: cls aloc)
+ (r: HS.rid)
+ (a: Set.set nat)
+ (l: loc c)
+ (h h' : HS.mem)
+: Lemma
+ (requires (
+ modifies (loc_union (loc_addresses false r a) l) h h' /\
+ (forall x . Set.mem x a ==> h `does_not_contain_addr` (r, x))
+ ))
+ (ensures (modifies l h h'))
+= loc_addresses_unused_in c r a h;
+ loc_includes_refl l;
+ loc_includes_union_l (loc_unused_in c h) l l;
+ loc_includes_union_l (loc_unused_in c h) l (loc_addresses false r a);
+ loc_includes_union_r (loc_union (loc_unused_in c h) l) (loc_addresses false r a) l;
+ modifies_loc_includes (loc_union (loc_unused_in c h) l) h h' (loc_union (loc_addresses false r a) l);
+ modifies_only_not_unused_in l h h'val mreference_live_loc_not_unused_in
+ (#al: aloc_t)
+ (c: cls al)
+ (#t: Type)
+ (#pre: Preorder.preorder t)
+ (h: HS.mem)
+ (r: HS.mreference t pre)
+: Lemma
+ (requires (h `HS.contains` r))
+ (ensures (loc_not_unused_in c h `loc_includes` loc_freed_mreference r /\ loc_not_unused_in c h `loc_includes` loc_mreference r))val mreference_unused_in_loc_unused_in
+ (#al: aloc_t)
+ (c: cls al)
+ (#t: Type)
+ (#pre: Preorder.preorder t)
+ (h: HS.mem)
+ (r: HS.mreference t pre)
+: Lemma
+ (requires (r `HS.unused_in` h))
+ (ensures (loc_unused_in c h `loc_includes` loc_freed_mreference r /\ loc_unused_in c h `loc_includes` loc_mreference r))val aloc_union: (bool -> Tot (aloc_t u#x)) -> Tot (aloc_t u#x)val cls_union (#a: (bool -> Tot aloc_t)) (c: ((b: bool) -> Tot (cls (a b)))) : Tot (cls (aloc_union a))val union_loc_of_loc (#al: (bool -> Tot aloc_t)) (c: (b: bool) -> Tot (cls (al b))) (b: bool) (l: loc (c b)) : GTot (loc (cls_union c))val union_loc_of_loc_none
+ (#al: (bool -> Tot aloc_t)) (c: (b: bool) -> Tot (cls (al b)))
+ (b: bool)
+: Lemma
+ (union_loc_of_loc c b (loc_none #_ #(c b)) == loc_none #_ #(cls_union c))val union_loc_of_loc_union
+ (#al: (bool -> Tot aloc_t)) (c: (b: bool) -> Tot (cls (al b)))
+ (b: bool)
+ (l1 l2: loc (c b))
+: Lemma
+ (union_loc_of_loc c b (loc_union #_ #(c b) l1 l2) == loc_union #_ #(cls_union c) (union_loc_of_loc c b l1) (union_loc_of_loc c b l2))val union_loc_of_loc_addresses
+ (#al: (bool -> Tot aloc_t)) (c: (b: bool) -> Tot (cls (al b)))
+ (b: bool)
+ (preserve_liveness: bool)
+ (r: HS.rid)
+ (n: Set.set nat)
+: Lemma
+ (union_loc_of_loc c b (loc_addresses #_ #(c b) preserve_liveness r n) == loc_addresses #_ #(cls_union c) preserve_liveness r n)val union_loc_of_loc_regions
+ (#al: (bool -> Tot aloc_t)) (c: (b: bool) -> Tot (cls (al b)))
+ (b: bool)
+ (preserve_liveness: bool)
+ (r: Set.set HS.rid)
+: Lemma
+ (union_loc_of_loc c b (loc_regions #_ #(c b) preserve_liveness r) == loc_regions #_ #(cls_union c) preserve_liveness r)val union_loc_of_loc_includes
+ (#al: (bool -> Tot aloc_t)) (c: (b: bool) -> Tot (cls (al b)))
+ (b: bool)
+ (s1 s2: loc (c b))
+: Lemma
+ (union_loc_of_loc c b s1 `loc_includes` union_loc_of_loc c b s2 <==> s1 `loc_includes` s2)val union_loc_of_loc_disjoint
+ (#al: (bool -> Tot aloc_t)) (c: (b: bool) -> Tot (cls (al b)))
+ (b: bool)
+ (s1 s2: loc (c b))
+: Lemma
+ (union_loc_of_loc c b s1 `loc_disjoint` union_loc_of_loc c b s2 <==> s1 `loc_disjoint` s2)val modifies_union_loc_of_loc
+ (#al: (bool -> Tot aloc_t)) (c: (b: bool) -> Tot (cls (al b)))
+ (b: bool)
+ (l: loc (c b))
+ (h1 h2: HS.mem)
+: Lemma
+ (modifies #_ #(cls_union c) (union_loc_of_loc c b l) h1 h2 <==> modifies #_ #(c b) l h1 h2)val loc_of_union_loc
+ (#al: (bool -> Tot aloc_t))
+ (#c: ((b: bool) -> Tot (cls (al b))))
+ (b: bool)
+ (l: loc (cls_union c))
+: GTot (loc (c b))val loc_of_union_loc_union_loc_of_loc
+ (#al: (bool -> HS.rid -> nat -> Tot Type))
+ (c: ((b: bool) -> Tot (cls (al b))))
+ (b: bool)
+ (s: loc (c b))
+: Lemma
+ (loc_of_union_loc b (union_loc_of_loc c b s) == s)val loc_of_union_loc_none
+ (#al: (bool -> Tot aloc_t))
+ (c: ((b: bool) -> Tot (cls (al b))))
+ (b: bool)
+: Lemma
+ (loc_of_union_loc #_ #c b loc_none == loc_none)val loc_of_union_loc_union
+ (#al: (bool -> Tot aloc_t))
+ (c: ((b: bool) -> Tot (cls (al b))))
+ (b: bool)
+ (l1 l2: loc (cls_union c))
+: Lemma
+ (loc_of_union_loc b (l1 `loc_union` l2) == loc_of_union_loc b l1 `loc_union` loc_of_union_loc b l2)val loc_of_union_loc_addresses
+ (#al: (bool -> Tot aloc_t)) (c: (b: bool) -> Tot (cls (al b)))
+ (b: bool)
+ (preserve_liveness: bool)
+ (r: HS.rid)
+ (n: Set.set nat)
+: Lemma
+ (loc_of_union_loc #_ #c b (loc_addresses preserve_liveness r n) == loc_addresses preserve_liveness r n)val loc_of_union_loc_regions
+ (#al: (bool -> Tot aloc_t)) (c: (b: bool) -> Tot (cls (al b)))
+ (b: bool)
+ (preserve_liveness: bool)
+ (r: Set.set HS.rid)
+: Lemma
+ (loc_of_union_loc #_ #c b (loc_regions preserve_liveness r) == loc_regions preserve_liveness r)++Universes
+
val raise_aloc (al: aloc_t u#x) : Tot (aloc_t u#(max x (y + 1)))val raise_cls (#al: aloc_t u#x) (c: cls al) : Tot (cls (raise_aloc u#x u#y al))val raise_loc (#al: aloc_t u#x) (#c: cls al) (l: loc c) : Tot (loc (raise_cls u#x u#y c))val raise_loc_none (#al: aloc_t u#x) (#c: cls al) : Lemma
+ (raise_loc u#x u#y (loc_none #_ #c) == loc_none)val raise_loc_union (#al: aloc_t u#x) (#c: cls al) (l1 l2: loc c) : Lemma
+ (raise_loc u#x u#y (loc_union l1 l2) == loc_union (raise_loc l1) (raise_loc l2))val raise_loc_addresses (#al: aloc_t u#x) (#c: cls al) (preserve_liveness: bool) (r: HS.rid) (a: Set.set nat) : Lemma
+ (raise_loc u#x u#y (loc_addresses #_ #c preserve_liveness r a) == loc_addresses preserve_liveness r a)val raise_loc_regions (#al: aloc_t u#x) (#c: cls al) (preserve_liveness: bool) (r: Set.set HS.rid) : Lemma
+ (raise_loc u#x u#y (loc_regions #_ #c preserve_liveness r) == loc_regions preserve_liveness r)val raise_loc_includes (#al: aloc_t u#x) (#c: cls al) (l1 l2: loc c) : Lemma
+ (loc_includes (raise_loc u#x u#y l1) (raise_loc l2) <==> loc_includes l1 l2)val raise_loc_disjoint (#al: aloc_t u#x) (#c: cls al) (l1 l2: loc c) : Lemma
+ (loc_disjoint (raise_loc u#x u#y l1) (raise_loc l2) <==> loc_disjoint l1 l2)val modifies_raise_loc (#al: aloc_t u#x) (#c: cls al) (l: loc c) (h1 h2: HS.mem) : Lemma
+ (modifies (raise_loc u#x u#y l) h1 h2 <==> modifies l h1 h2)val lower_loc (#al: aloc_t u#x) (#c: cls al) (l: loc (raise_cls u#x u#y c)) : Tot (loc c)val lower_loc_raise_loc (#al: aloc_t u#x) (#c: cls al) (l: loc c) : Lemma
+ (lower_loc (raise_loc u#x u#y l) == l)val raise_loc_lower_loc (#al: aloc_t u#x) (#c: cls al) (l: loc (raise_cls u#x u#y c)) : Lemma
+ (raise_loc (lower_loc l) == l)val lower_loc_none (#al: aloc_t u#x) (#c: cls al) : Lemma
+ (lower_loc u#x u#y #_ #c loc_none == loc_none)val lower_loc_union (#al: aloc_t u#x) (#c: cls al) (l1 l2: loc (raise_cls u#x u#y c)) : Lemma
+ (lower_loc u#x u#y (loc_union l1 l2) == loc_union (lower_loc l1) (lower_loc l2))val lower_loc_addresses (#al: aloc_t u#x) (#c: cls al) (preserve_liveness: bool) (r: HS.rid) (a: Set.set nat) : Lemma
+ (lower_loc u#x u#y #_ #c (loc_addresses preserve_liveness r a) == loc_addresses preserve_liveness r a)val lower_loc_regions (#al: aloc_t u#x) (#c: cls al) (preserve_liveness: bool) (r: Set.set HS.rid) : Lemma
+ (lower_loc u#x u#y #_ #c (loc_regions preserve_liveness r) == loc_regions preserve_liveness r)fsdoc: no-summary-found
-fsdoc: no-comment-found
-open FStar.HyperStack.STA library for mutable partial, dependent maps, that grow monotonically, while subject to an invariant on the entire map
+Opens module FStar.HyperStack.ST +A library for mutable partial, dependent maps, +that grow monotonically, +while subject to an invariant on the entire map
+Aliases module FStar.HyperStack as HS
Aliases module FStar.DependentMap as DM
Aliases module FStar.HyperStack.ST as HST
++The logical model of the map is given in terms of DM.t///
+
let opt (#a:eqtype) (b:a -> Type) = fun (x:a) -> option (b x)
+let partial_dependent_map (a:eqtype) (b:a -> Type) =
+ DM.t a (opt b)++An empty partial, dependent map maps all keys to None
+
let empty_partial_dependent_map (#a:_) (#b:_)
+ : partial_dependent_map a b
+ = DM.create #a #(opt b) (fun x -> None)//////////////////////////////////////////////////////////////////////////////
++++
map a b: Internally, the model is implemented using this abstract type +These maps provide three operations: +- empty, sel, upd +Which are proven to be in correspondence with the operations on DM.t +via the homomorphismreprbelow
val map
+ (a:eqtype)
+ (b:(a -> Type u#b))
+ : Type u#b+++
repr m: A ghost function that reveals the internalmapas aDM.t
val repr (#a:_) (#b:_)
+ (r:map a b)
+ : GTot (partial_dependent_map a b)++An
+empty : map a bis equivalent to theempty_partial_dependent_map
val empty (#a:_) (#b:_)
+ : r:map a b{repr r == empty_partial_dependent_map}++Selecting a key from a map
+sel r xis equivalent to selecting it from itsrepr
val sel (#a:_) (#b:_)
+ (r:map a b)
+ (x:a)
+ : Pure (option (b x))
+ (requires True)
+ (ensures (fun o -> DM.sel (repr r) x == o))++Updating a map using
+upd r x vis equivalent to updating its repr
val upd (#a:_) (#b:_)
+ (r:map a b)
+ (x:a)
+ (v:b x)
+ : Pure (map a b)
+ (requires True)
+ (ensures (fun r' -> repr r' == DM.upd (repr r) x (Some v)))+++
imap a b invfurther augments a map with an invariant on its repr
let imap (a:eqtype) (b: a -> Type) (inv:DM.t a (opt b) -> Type) =
+ r:map a b{inv (repr r)}+++
grows r1 r2is an abstract preorder onimap
val grows (#a:_) (#b:_) (#inv:DM.t a (opt b) -> Type)
+ : FStar.Preorder.preorder (imap a b inv)++And, finally, the main type of this module:
++
t r a b invis a mutable, imap stored in regionrconstrained +to evolve according togrows
let t (r:HST.erid) (a:eqtype) (b:a -> Type) (inv:DM.t a (opt b) -> Type) =
+ m_rref r (imap a b inv) grows+++
defined t x h: In stateh, maptis defined at pointx. +- We define these inTyperather thanbool+since it is typical for client code to usedefined+as a stable heap predicate, which requires aheap -> Type
let defined
+ (#a:eqtype)
+ (#b:a -> Type)
+ (#inv:DM.t a (opt b) -> Type)
+ (#r:HST.erid)
+ (t:t r a b inv)
+ (x:a)
+ (h:HS.mem)
+ : GTot Type
+ = Some? (sel (HS.sel h t) x)+++
fresh t x h: The map is not defined at pointx
let fresh
+ (#a:eqtype)
+ (#b:a -> Type)
+ (#inv:DM.t a (opt b) -> Type)
+ (#r:HST.erid)
+ (t:t r a b inv)
+ (x:a)
+ (h:HS.mem)
+ : GTot Type0
+ = ~ (defined t x h)+++
value_of t x h: Get the value ofxin the maptin stateh
let value_of
+ (#a:eqtype)
+ (#b:a -> Type)
+ (#inv:DM.t a (opt b) -> Type)
+ (#r:HST.erid)
+ (t:t r a b inv)
+ (x:a)
+ (h:HS.mem{defined t x h})
+ : GTot (b x)
+ = Some?.v (sel (HS.sel h t) x)+++
contains t x y h: In stateh,tmapsxtoy
let contains
+ (#a:eqtype)
+ (#b:a -> Type)
+ (#inv:DM.t a (opt b) -> Type)
+ (#r:HST.erid)
+ (t:t r a b inv)
+ (x:a)
+ (y:b x)
+ (h:HS.mem)
+ : GTot Type0
+ = defined t x h /\
+ value_of t x h == y+++
contains_stable: Thecontainspredicate is stable with respect togrows
val contains_stable
+ (#a:eqtype)
+ (#b:a -> Type)
+ (#inv:DM.t a (opt b) -> Type)
+ (#r:HST.erid)
+ (t:t r a b inv)
+ (x:a)
+ (y:b x)
+ : Lemma (ensures (HST.stable_on_t t (contains t x y)))+++
defined_stable: Thedefinedpredicate is stable with respect togrows+
+- this is easily derivable from the previous lemma +But, we provide it here as a convenience to clients
+
val defined_stable
+ (#a:eqtype)
+ (#b:a -> Type)
+ (#inv:DM.t a (opt b) -> Type)
+ (#r:HST.erid)
+ (t:t r a b inv)
+ (x:a)
+ : Lemma (ensures (HST.stable_on_t t (defined t x)))////////////////////////////////////////////////////////////////////////////// +Interface of stateful operations +//////////////////////////////////////////////////////////////////////////////
++++
alloc (): Allocating a newtrequires proving theinvof the empty map
val alloc (#a:eqtype) (#b:a -> Type) (#inv:DM.t a (opt b) -> Type) (#r:HST.erid)
+ (_:unit{inv (repr empty)})
+ : ST (t r a b inv)
+ (requires (fun h -> HyperStack.ST.witnessed (region_contains_pred r)))
+ (ensures (fun h0 x h1 ->
+ ralloc_post r empty h0 x h1))+++
extend t x y: Extendingtwith (x -> y) +Requires: - proving that thetdoes not already definex+- and that the resulting heap would still respectinv+Ensures: - that onlytis modified +- by updating it to contain(x -> y)+- and in the futuretwill always contain(x -> y)
val extend
+ (#a:eqtype)
+ (#b:a -> Type)
+ (#inv:DM.t a (opt b) -> Type)
+ (#r:HST.erid)
+ (t:t r a b inv)
+ (x:a)
+ (y:b x)
+ : Stack unit
+ (requires (fun h ->
+ ~(defined t x h) /\
+ inv (repr (upd (HS.sel h t) x y))))
+ (ensures (fun h0 u h1 ->
+ let cur = HS.sel h0 t in
+ HS.contains h1 t /\
+ HS.modifies (Set.singleton r) h0 h1 /\
+ HS.modifies_ref r (Set.singleton (HS.as_addr t)) h0 h1 /\
+ HS.sel h1 t == upd cur x y /\
+ witnessed (contains t x y)))+++
lookup t x: Querying the maptat pointx+Ensures: - The state does not change +- If it returnsSome v, thentwill always containsx -> v
val lookup
+ (#a:eqtype)
+ (#b:a -> Type)
+ (#inv:DM.t a (opt b) -> Type)
+ (#r:HST.erid)
+ (t:t r a b inv)
+ (x:a)
+ : ST (option (b x))
+ (requires (fun h -> True))
+ (ensures (fun h0 y h1 ->
+ h0==h1 /\
+ y == sel (HS.sel h1 t) x /\
+ (match y with
+ | None -> ~(defined t x h1)
+ | Some v ->
+ contains t x v h1 /\
+ witnessed (contains t x v))))let forall_t (#a:eqtype) (#b:a -> Type) (#inv:DM.t a (opt b) -> Type) (#r:HST.erid)
+ (t:t r a b inv) (h:HS.mem) (pred: (x:a) -> b x -> Type0)
+ = forall (x:a).{:pattern (sel (HS.sel h t) x) \/ (DM.sel (repr (HS.sel h t)) x)}
+ defined t x h ==> pred x (Some?.v (sel (HS.sel h t) x))let f_opt (#a:eqtype) (#b #c:a -> Type) (f: (x:a) -> b x -> c x) :(x:a) -> option (b x) -> option (c x)
+ = fun x y ->
+ match y with
+ | None -> None
+ | Some y -> Some (f x y)val mmap_f (#a:eqtype) (#b #c:a -> Type) (m:map a b) (f: (x:a) -> b x -> c x)
+ :Tot (m':(map a c){repr m' == DM.map (f_opt f) (repr m)})val map_f (#a:eqtype) (#b #c:a -> Type)
+ (#inv:DM.t a (opt b) -> Type) (#inv':DM.t a (opt c) -> Type)
+ (#r #r':HST.erid)
+ (m:t r a b inv) (f: (x:a) -> b x -> c x)
+ :ST (t r' a c inv')
+ (requires (fun h0 -> inv' (DM.map (f_opt f) (repr (HS.sel h0 m))) /\ witnessed (region_contains_pred r')))
+ (ensures (fun h0 m' h1 ->
+ inv' (DM.map (f_opt f) (repr (HS.sel h0 m))) /\ //AR: surprised that even after the fix for #57, we need this repetetion from the requires clause
+ ralloc_post r' (mmap_f (HS.sel h0 m) f) h0 m' h1))fsdoc: no-summary-found
-fsdoc: no-comment-found
-* Untyped views of monotonic references Aliases module FStar.Set as S
Aliases module FStar.TSet as TS
Opens module FStar.Preorder
+let set = Set.set
+let tset = TSet.setval heap :Type u#1val equal: heap -> heap -> Type0val equal_extensional (h1:heap) (h2:heap)
+ :Lemma (requires True) (ensures (equal h1 h2 <==> h1 == h2))
+ [SMTPat (equal h1 h2)]val emp :heapval next_addr: heap -> GTot pos[@@ remove_unused_type_parameters [1]]
+val mref ([@@@ strictly_positive] a:Type0) ([@@@ strictly_positive] rel:preorder a) :Type0val addr_of: #a:Type0 -> #rel:preorder a -> mref a rel -> GTot posval is_mm: #a:Type0 -> #rel:preorder a -> mref a rel -> GTot boollet compare_addrs (#a #b:Type0) (#rel1:preorder a) (#rel2:preorder b) (r1:mref a rel1) (r2:mref b rel2)
+ :GTot bool = addr_of r1 = addr_of r2val contains: #a:Type0 -> #rel:preorder a -> heap -> mref a rel -> Type0val addr_unused_in: nat -> heap -> Type0val not_addr_unused_in_nullptr (h: heap) : Lemma (~ (addr_unused_in 0 h))val unused_in: #a:Type0 -> #rel:preorder a -> mref a rel -> heap -> Type0let fresh (#a:Type) (#rel:preorder a) (r:mref a rel) (h0:heap) (h1:heap) =
+ r `unused_in` h0 /\ h1 `contains` rlet only_t (#a:Type0) (#rel:preorder a) (x:mref a rel) :GTot (tset nat) = TS.singleton (addr_of x)let only (#a:Type0) (#rel:preorder a) (x:mref a rel) :GTot (set nat) = S.singleton (addr_of x)let op_Hat_Plus_Plus (#a:Type0) (#rel:preorder a) (r:mref a rel) (s:set nat) :GTot (set nat) = S.union (only r) slet op_Plus_Plus_Hat (#a:Type0) (#rel:preorder a) (s:set nat) (r:mref a rel) :GTot (set nat) = S.union s (only r)let op_Hat_Plus_Hat (#a:Type0) (#b:Type0) (#rel1:preorder a) (#rel2:preorder b) (r1:mref a rel1) (r2:mref b rel2)
+ :GTot (set nat) = S.union (only r1) (only r2)val sel_tot: #a:Type0 -> #rel:preorder a -> h:heap -> r:mref a rel{h `contains` r} -> Tot aval sel: #a:Type0 -> #rel:preorder a -> heap -> mref a rel -> GTot aval upd_tot: #a:Type0 -> #rel:preorder a -> h:heap -> r:mref a rel{h `contains` r} -> x:a -> Tot heapval upd: #a:Type0 -> #rel:preorder a -> h:heap -> r:mref a rel -> x:a -> GTot heapval alloc: #a:Type0 -> rel:preorder a -> heap -> a -> mm:bool -> Tot (mref a rel * heap)val free_mm: #a:Type0 -> #rel:preorder a -> h:heap -> r:mref a rel{h `contains` r /\ is_mm r} -> Tot heaplet modifies_t (s:tset nat) (h0:heap) (h1:heap) =
+ (forall (a:Type) (rel:preorder a) (r:mref a rel).{:pattern (sel h1 r)}
+ ((~ (TS.mem (addr_of r) s)) /\ h0 `contains` r) ==> sel h1 r == sel h0 r) /\
+ (forall (a:Type) (rel:preorder a) (r:mref a rel).{:pattern (contains h1 r)}
+ h0 `contains` r ==> h1 `contains` r) /\
+ (forall (a:Type) (rel:preorder a) (r:mref a rel).{:pattern (r `unused_in` h0)}
+ r `unused_in` h1 ==> r `unused_in` h0) /\
+ (forall (n: nat) . {:pattern (n `addr_unused_in` h0) }
+ n `addr_unused_in` h1 ==> n `addr_unused_in` h0
+ )let modifies (s:set nat) (h0:heap) (h1:heap) = modifies_t (TS.tset_of_set s) h0 h1let equal_dom (h1:heap) (h2:heap) :GTot Type0 =
+ (forall (a:Type0) (rel:preorder a) (r:mref a rel).
+ {:pattern (h1 `contains` r) \/ (h2 `contains` r)}
+ h1 `contains` r <==> h2 `contains` r) /\
+ (forall (a:Type0) (rel:preorder a) (r:mref a rel).
+ {:pattern (r `unused_in` h1) \/ (r `unused_in` h2)}
+ r `unused_in` h1 <==> r `unused_in` h2)val lemma_ref_unused_iff_addr_unused (#a:Type0) (#rel:preorder a) (h:heap) (r:mref a rel)
+ :Lemma (requires True)
+ (ensures (r `unused_in` h <==> addr_of r `addr_unused_in` h))
+ [SMTPatOr [[SMTPat (r `unused_in` h)]; [SMTPat (addr_of r `addr_unused_in` h)]]]val lemma_contains_implies_used (#a:Type0) (#rel:preorder a) (h:heap) (r:mref a rel)
+ :Lemma (requires (h `contains` r))
+ (ensures (~ (r `unused_in` h)))
+ [SMTPatOr [[SMTPat (h `contains` r)]; [SMTPat (r `unused_in` h)]]]val lemma_distinct_addrs_distinct_types
+ (#a:Type0) (#b:Type0) (#rel1:preorder a) (#rel2:preorder b) (h:heap) (r1:mref a rel1) (r2:mref b rel2)
+ :Lemma (requires (a =!= b /\ h `contains` r1 /\ h `contains` r2))
+ (ensures (addr_of r1 <> addr_of r2))
+ [SMTPat (h `contains` r1); SMTPat (h `contains` r2)]val lemma_distinct_addrs_distinct_preorders (u:unit)
+ :Lemma (forall (a:Type0) (rel1 rel2:preorder a) (r1:mref a rel1) (r2:mref a rel2) (h:heap).
+ {:pattern (h `contains` r1); (h `contains` r2)}
+ (h `contains` r1 /\ h `contains` r2 /\ rel1 =!= rel2) ==> addr_of r1 <> addr_of r2)val lemma_distinct_addrs_distinct_mm (u:unit)
+ :Lemma (forall (a b:Type0) (rel1:preorder a) (rel2:preorder b) (r1:mref a rel1) (r2:mref b rel2) (h:heap).
+ {:pattern (h `contains` r1); (h `contains` r2)}
+ (h `contains` r1 /\ h `contains` r2 /\ is_mm r1 =!= is_mm r2) ==> addr_of r1 <> addr_of r2)val lemma_distinct_addrs_unused
+ (#a:Type0) (#b:Type0) (#rel1:preorder a) (#rel2:preorder b) (h:heap) (r1:mref a rel1) (r2:mref b rel2)
+ :Lemma (requires (r1 `unused_in` h /\ ~ (r2 `unused_in` h)))
+ (ensures (addr_of r1 <> addr_of r2 /\ (~ (r1 === r2))))
+ [SMTPat (r1 `unused_in` h); SMTPat (r2 `unused_in` h)]val lemma_alloc (#a:Type0) (rel:preorder a) (h0:heap) (x:a) (mm:bool)
+ :Lemma (requires True)
+ (ensures (let r, h1 = alloc rel h0 x mm in
+ fresh r h0 h1 /\ h1 == upd h0 r x /\ is_mm r = mm /\ addr_of r == next_addr h0))
+ [SMTPat (alloc rel h0 x mm)]val lemma_free_mm_sel
+ (#a:Type0) (#b:Type0) (#rel1:preorder a) (#rel2:preorder b) (h0:heap)
+ (r1:mref a rel1{h0 `contains` r1 /\ is_mm r1}) (r2:mref b rel2)
+ :Lemma (requires True)
+ (ensures (addr_of r2 <> addr_of r1 ==> sel h0 r2 == sel (free_mm h0 r1) r2))
+ [SMTPat (sel (free_mm h0 r1) r2)]val lemma_free_mm_contains
+ (#a:Type0) (#b:Type0) (#rel1:preorder a) (#rel2:preorder b) (h0:heap)
+ (r1:mref a rel1{h0 `contains` r1 /\ is_mm r1}) (r2:mref b rel2)
+ :Lemma (requires True)
+ (ensures (let h1 = free_mm h0 r1 in
+ (addr_of r2 <> addr_of r1 /\ h0 `contains` r2) <==> h1 `contains` r2))
+ [SMTPat ((free_mm h0 r1) `contains` r2)]val lemma_free_mm_unused
+ (#a:Type0) (#b:Type0) (#rel1:preorder a) (#rel2:preorder b) (h0:heap)
+ (r1:mref a rel1{h0 `contains` r1 /\ is_mm r1}) (r2:mref b rel2)
+ :Lemma (requires True)
+ (ensures (let h1 = free_mm h0 r1 in
+ ((addr_of r1 = addr_of r2 ==> r2 `unused_in` h1) /\
+ (r2 `unused_in` h0 ==> r2 `unused_in` h1) /\
+ (r2 `unused_in` h1 ==> (r2 `unused_in` h0 \/ addr_of r2 = addr_of r1)))))
+ [SMTPat (r2 `unused_in` (free_mm h0 r1))]val lemma_free_addr_unused_in
+ (#a: Type) (#rel: preorder a) (h: heap) (r: mref a rel { h `contains` r /\ is_mm r } )
+ (n: nat)
+: Lemma
+ (requires (n `addr_unused_in` (free_mm h r) /\ n <> addr_of r))
+ (ensures (n `addr_unused_in` h))
+ [SMTPat (n `addr_unused_in` (free_mm h r))]val lemma_sel_same_addr (#a:Type0) (#rel:preorder a) (h:heap) (r1:mref a rel) (r2:mref a rel)
+ :Lemma (requires (h `contains` r1 /\ addr_of r1 = addr_of r2 /\ is_mm r1 == is_mm r2))
+ (ensures (h `contains` r2 /\ sel h r1 == sel h r2))
+ [SMTPatOr [
+ [SMTPat (sel h r1); SMTPat (sel h r2)];
+ [SMTPat (h `contains` r1); SMTPat (h `contains` r2)];
+ ]]val lemma_sel_upd1 (#a:Type0) (#rel:preorder a) (h:heap) (r1:mref a rel) (x:a) (r2:mref a rel)
+ :Lemma (requires (addr_of r1 = addr_of r2 /\ is_mm r1 == is_mm r2))
+ (ensures (sel (upd h r1 x) r2 == x))
+ [SMTPat (sel (upd h r1 x) r2)]val lemma_sel_upd2 (#a:Type0) (#b:Type0) (#rel1:preorder a) (#rel2:preorder b) (h:heap) (r1:mref a rel1) (r2:mref b rel2) (x:b)
+ :Lemma (requires (addr_of r1 <> addr_of r2))
+ (ensures (sel (upd h r2 x) r1 == sel h r1))
+ [SMTPat (sel (upd h r2 x) r1)]val lemma_mref_injectivity
+ :(u:unit{forall (a:Type0) (b:Type0) (rel1:preorder a) (rel2:preorder b) (r1:mref a rel1) (r2:mref b rel2). a =!= b ==> ~ (eq3 r1 r2)})val lemma_mref_injectivity_preorder (_:unit)
+ : Lemma (forall (a:Type0) (rel1:preorder a) (rel2:preorder a) (r1:mref a rel1) (r2:mref a rel2). rel1 =!= rel2 ==> ~ (eq3 r1 r2))val lemma_in_dom_emp (#a:Type0) (#rel:preorder a) (r:mref a rel)
+ :Lemma (requires True)
+ (ensures (r `unused_in` emp))
+ [SMTPat (r `unused_in` emp)]val lemma_upd_contains (#a:Type0) (#rel:preorder a) (h:heap) (r:mref a rel) (x:a)
+ :Lemma (requires True)
+ (ensures ((upd h r x) `contains` r))
+ [SMTPat ((upd h r x) `contains` r)]val lemma_well_typed_upd_contains
+ (#a:Type0) (#b:Type0) (#rel1:preorder a) (#rel2:preorder b) (h:heap) (r1:mref a rel1) (x:a) (r2:mref b rel2)
+ :Lemma (requires (h `contains` r1))
+ (ensures (let h1 = upd h r1 x in
+ h1 `contains` r2 <==> h `contains` r2))
+ [SMTPat ((upd h r1 x) `contains` r2)]val lemma_unused_upd_contains
+ (#a:Type0) (#b:Type0) (#rel1:preorder a) (#rel2:preorder b) (h:heap) (r1:mref a rel1) (x:a) (r2:mref b rel2)
+ :Lemma (requires (r1 `unused_in` h))
+ (ensures (let h1 = upd h r1 x in
+ (h `contains` r2 ==> h1 `contains` r2) /\
+ (h1 `contains` r2 ==> (h `contains` r2 \/ addr_of r2 = addr_of r1))))
+ [SMTPat ((upd h r1 x) `contains` r2)]val lemma_upd_contains_different_addr
+ (#a:Type0) (#b:Type0) (#rel1:preorder a) (#rel2:preorder b) (h:heap) (r1:mref a rel1) (x:a) (r2:mref b rel2)
+ :Lemma (requires (h `contains` r2 /\ addr_of r1 <> addr_of r2))
+ (ensures ((upd h r1 x) `contains` r2))
+ [SMTPat ((upd h r1 x) `contains` r2)]val lemma_upd_unused
+ (#a:Type0) (#b:Type0) (#rel1:preorder a) (#rel2:preorder b) (h:heap) (r1:mref a rel1) (x:a) (r2:mref b rel2)
+ :Lemma (requires True)
+ (ensures ((addr_of r1 <> addr_of r2 /\ r2 `unused_in` h) <==> r2 `unused_in` (upd h r1 x)))
+ [SMTPat (r2 `unused_in` (upd h r1 x))]val lemma_contains_upd_modifies (#a:Type0) (#rel:preorder a) (h:heap) (r:mref a rel) (x:a)
+ :Lemma (requires (h `contains` r))
+ (ensures (modifies (S.singleton (addr_of r)) h (upd h r x)))
+ [SMTPat (upd h r x)]val lemma_unused_upd_modifies (#a:Type0) (#rel:preorder a) (h:heap) (r:mref a rel) (x:a)
+ :Lemma (requires (r `unused_in` h))
+ (ensures (modifies (Set.singleton (addr_of r)) h (upd h r x)))
+ [SMTPat (upd h r x); SMTPat (r `unused_in` h)]val lemma_sel_equals_sel_tot_for_contained_refs
+ (#a:Type0) (#rel:preorder a) (h:heap) (r:mref a rel{h `contains` r})
+ :Lemma (requires True)
+ (ensures (sel_tot h r == sel h r))val lemma_upd_equals_upd_tot_for_contained_refs
+ (#a:Type0) (#rel:preorder a) (h:heap) (r:mref a rel{h `contains` r}) (x:a)
+ :Lemma (requires True)
+ (ensures (upd_tot h r x == upd h r x))val lemma_modifies_and_equal_dom_sel_diff_addr
+ (#a:Type0)(#rel:preorder a) (s:set nat) (h0:heap) (h1:heap) (r:mref a rel)
+ :Lemma (requires (modifies s h0 h1 /\ equal_dom h0 h1 /\ (~ (S.mem (addr_of r) s))))
+ (ensures (sel h0 r == sel h1 r))
+ [SMTPat (modifies s h0 h1); SMTPat (equal_dom h0 h1); SMTPat (sel h1 r)]val lemma_heap_equality_upd_same_addr (#a: Type0) (#rel: preorder a) (h: heap) (r1 r2: mref a rel) (x: a)
+ :Lemma (requires ((h `contains` r1 \/ h `contains` r2) /\ addr_of r1 = addr_of r2 /\ is_mm r1 == is_mm r2))
+ (ensures (upd h r1 x == upd h r2 x))val lemma_heap_equality_cancel_same_mref_upd
+ (#a:Type) (#rel:preorder a) (h:heap) (r:mref a rel)
+ (x:a) (y:a)
+ :Lemma (requires True)
+ (ensures (upd (upd h r x) r y == upd h r y))val lemma_heap_equality_upd_with_sel
+ (#a:Type) (#rel:preorder a) (h:heap) (r:mref a rel)
+ :Lemma (requires (h `contains` r))
+ (ensures (upd h r (sel h r) == h))val lemma_heap_equality_commute_distinct_upds
+ (#a:Type) (#b:Type) (#rel_a:preorder a) (#rel_b:preorder b) (h:heap) (r1:mref a rel_a) (r2:mref b rel_b)
+ (x:a) (y:b)
+ :Lemma (requires (addr_of r1 =!= addr_of r2))
+ (ensures (upd (upd h r1 x) r2 y == upd (upd h r2 y) r1 x))val lemma_next_addr_upd_tot
+ (#a:Type0) (#rel:preorder a) (h0:heap) (r:mref a rel{h0 `contains` r}) (x:a)
+ :Lemma (let h1 = upd_tot h0 r x in next_addr h1 == next_addr h0)val lemma_next_addr_upd
+ (#a:Type0) (#rel:preorder a) (h0:heap) (r:mref a rel) (x:a)
+ :Lemma (let h1 = upd h0 r x in next_addr h1 >= next_addr h0)val lemma_next_addr_alloc
+ (#a:Type0) (rel:preorder a) (h0:heap) (x:a) (mm:bool)
+ :Lemma (let _, h1 = alloc rel h0 x mm in next_addr h1 > next_addr h0)val lemma_next_addr_free_mm
+ (#a:Type0) (#rel:preorder a) (h0:heap) (r:mref a rel{h0 `contains` r /\ is_mm r})
+ :Lemma (let h1 = free_mm h0 r in next_addr h1 == next_addr h0)val lemma_next_addr_contained_refs_addr
+ (#a:Type0) (#rel:preorder a) (h:heap) (r:mref a rel)
+ :Lemma (h `contains` r ==> addr_of r < next_addr h)Definition and ghost decidable equality
+val aref: Type0
+val dummy_aref: aref
+val aref_equal (a1 a2: aref) : Ghost bool (requires True) (ensures (fun b -> b == true <==> a1 == a2))Introduction rule
+val aref_of: #t: Type0 -> #rel: preorder t -> r: mref t rel -> Tot arefOperators lifted from ref
+val addr_of_aref: a: aref -> GTot (n: nat { n > 0 } )
+val addr_of_aref_of: #t: Type0 -> #rel: preorder t -> r: mref t rel -> Lemma (addr_of r == addr_of_aref (aref_of r))
+[SMTPat (addr_of_aref (aref_of r))]
+val aref_is_mm: aref -> GTot bool
+val is_mm_aref_of: #t: Type0 -> #rel: preorder t -> r: mref t rel -> Lemma (is_mm r == aref_is_mm (aref_of r))
+[SMTPat (aref_is_mm (aref_of r))]
+val aref_unused_in: aref -> heap -> Type0
+val unused_in_aref_of: #t: Type0 -> #rel: preorder t -> r: mref t rel -> h: heap -> Lemma (unused_in r h <==> aref_unused_in (aref_of r) h)
+[SMTPat (aref_unused_in (aref_of r) h)]
+val contains_aref_unused_in: #a:Type -> #rel: preorder a -> h:heap -> x:mref a rel -> y:aref -> Lemma
+ (requires (contains h x /\ aref_unused_in y h))
+ (ensures (addr_of x <> addr_of_aref y))Elimination rule
+val aref_live_at: h: heap -> a: aref -> t: Type0 -> rel: preorder t -> GTot Type0
+val gref_of: a: aref -> t: Type0 -> rel: preorder t -> Ghost (mref t rel) (requires (exists h . aref_live_at h a t rel)) (ensures (fun _ -> True))
+val ref_of: h: heap -> a: aref -> t: Type0 -> rel: preorder t -> Pure (mref t rel) (requires (aref_live_at h a t rel)) (ensures (fun x -> aref_live_at h a t rel /\ addr_of (gref_of a t rel) == addr_of x /\ is_mm x == aref_is_mm a))
+val aref_live_at_aref_of
+ (h: heap)
+ (#t: Type0)
+ (#rel: preorder t)
+ (r: mref t rel)
+: Lemma
+ (ensures (aref_live_at h (aref_of r) t rel <==> contains h r))
+ [SMTPat (aref_live_at h (aref_of r) t rel)]
+val contains_gref_of
+ (h: heap)
+ (a: aref)
+ (t: Type0)
+ (rel: preorder t)
+: Lemma
+ (requires (exists h' . aref_live_at h' a t rel))
+ (ensures ((exists h' . aref_live_at h' a t rel) /\ (contains h (gref_of a t rel) <==> aref_live_at h a t rel)))
+ [SMTPatOr [
+ [SMTPat (contains h (gref_of a t rel))];
+ [SMTPat (aref_live_at h a t rel)];
+ ]]val aref_of_gref_of
+ (a: aref)
+ (t: Type0)
+ (rel: preorder t)
+: Lemma
+ (requires (exists h . aref_live_at h a t rel))
+ (ensures ((exists h . aref_live_at h a t rel) /\ aref_of (gref_of a t rel) == a))
+ [SMTPat (aref_of (gref_of a t rel))]Operators lowered to ref
+val addr_of_gref_of
+ (a: aref)
+ (t: Type0)
+ (rel: preorder t)
+: Lemma
+ (requires (exists h . aref_live_at h a t rel))
+ (ensures ((exists h . aref_live_at h a t rel) /\ addr_of (gref_of a t rel) == addr_of_aref a))
+ [SMTPat (addr_of (gref_of a t rel))]val is_mm_gref_of
+ (a: aref)
+ (t: Type0)
+ (rel: preorder t)
+: Lemma
+ (requires (exists h . aref_live_at h a t rel))
+ (ensures ((exists h . aref_live_at h a t rel) /\ is_mm (gref_of a t rel) == aref_is_mm a))
+ [SMTPat (is_mm (gref_of a t rel))]val unused_in_gref_of
+ (a: aref)
+ (t: Type0)
+ (rel: preorder t)
+ (h: heap)
+: Lemma
+ (requires (exists h . aref_live_at h a t rel))
+ (ensures ((exists h . aref_live_at h a t rel) /\ (unused_in (gref_of a t rel) h <==> aref_unused_in a h)))
+ [SMTPat (unused_in (gref_of a t rel) h)]val sel_ref_of
+ (a: aref)
+ (t: Type0)
+ (rel: preorder t)
+ (h1 h2: heap)
+: Lemma
+ (requires (aref_live_at h1 a t rel /\ aref_live_at h2 a t rel))
+ (ensures (aref_live_at h2 a t rel /\ sel h1 (ref_of h2 a t rel) == sel h1 (gref_of a t rel)))
+ [SMTPat (sel h1 (ref_of h2 a t rel))]val upd_ref_of
+ (a: aref)
+ (t: Type0)
+ (rel: preorder t)
+ (h1 h2: heap)
+ (x: t)
+: Lemma
+ (requires (aref_live_at h1 a t rel /\ aref_live_at h2 a t rel))
+ (ensures (aref_live_at h2 a t rel /\ upd h1 (ref_of h2 a t rel) x == upd h1 (gref_of a t rel) x))
+ [SMTPat (upd h1 (ref_of h2 a t rel) x)]fsdoc: no-summary-found
-fsdoc: no-comment-found
+Aliases module FStar.Set as Set
Aliases module FStar.Map as Map
Opens module FStar.Monotonic.Heap
+Opens module FStar.Ghost
+This module provides the map view of the memory and associated functions and lemmas
+The intention of this module is for it to be included in HyperStack
+Clients should not open/know about HyperHeap, they should work only with HyperStack
+AR: mark it must_erase_for_extraction temporarily until CMI comes in
+[@@must_erase_for_extraction]
+val rid :eqtypeval reveal (r:rid) :GTot (list (int * int))val color (x:rid) :GTot intval rid_freeable (x:rid) : GTot booltype hmap = Map.t rid heapval root : r:rid{color r == 0 /\ not (rid_freeable r)}let root_has_color_zero (u:unit) :Lemma (color root == 0) = ()val root_is_not_freeable (_:unit) : Lemma (not (rid_freeable root))private val rid_length (r:rid) :GTot natprivate val rid_tail (r:rid{rid_length r > 0}) :ridval includes (r1:rid) (r2:rid) :GTot bool (decreases (reveal r2))let disjoint (i:rid) (j:rid) :GTot bool = not (includes i j) && not (includes j i)val lemma_disjoint_includes (i:rid) (j:rid) (k:rid)
+ :Lemma (requires (disjoint i j /\ includes j k))
+ (ensures (disjoint i k))
+ (decreases (List.Tot.length (reveal k)))
+ [SMTPat (disjoint i j); SMTPat (includes j k)]val extends (i:rid) (j:rid) :GTot boolval parent (r:rid{r =!= root}) :ridval lemma_includes_refl (i:rid)
+ :Lemma (includes i i)
+ [SMTPat (includes i i)]val lemma_extends_includes (i:rid) (j:rid)
+ :Lemma (requires (extends j i))
+ (ensures (includes i j /\ not(includes j i)))
+ [SMTPat (extends j i)]val lemma_includes_anti_symmetric (i:rid) (j:rid)
+ :Lemma (requires (includes i j /\ i =!= j))
+ (ensures (not (includes j i)))
+ [SMTPat (includes i j)]val lemma_extends_disjoint (i:rid) (j:rid) (k:rid)
+ :Lemma (requires (extends j i /\ extends k i /\ j =!= k))
+ (ensures (disjoint j k))val lemma_extends_parent (i:rid{i =!= root})
+ :Lemma (extends i (parent i))
+ [SMTPat (parent i)]val lemma_extends_not_root (i:rid) (j:rid{extends j i})
+ :Lemma (j =!= root)
+ [SMTPat (extends j i)]val lemma_extends_only_parent (i:rid) (j:rid{extends j i})
+ :Lemma (i == parent j)
+ [SMTPat (extends j i)]val mod_set (s:Set.set rid) :(Set.set rid)
+assume Mod_set_def: forall (x:rid) (s:Set.set rid). {:pattern Set.mem x (mod_set s)}
+ Set.mem x (mod_set s) <==> (exists (y:rid). Set.mem y s /\ includes y x)let modifies (s:Set.set rid) (m0:hmap) (m1:hmap) =
+ Map.equal m1 (Map.concat m1 (Map.restrict (Set.complement (mod_set s)) m0)) /\
+ Set.subset (Map.domain m0) (Map.domain m1)let modifies_just (s:Set.set rid) (m0:hmap) (m1:hmap) =
+ Map.equal m1 (Map.concat m1 (Map.restrict (Set.complement s) m0)) /\
+ Set.subset (Map.domain m0) (Map.domain m1)let modifies_one (r:rid) (m0:hmap) (m1:hmap) = modifies_just (Set.singleton r) m0 m1let equal_on (s:Set.set rid) (m0:hmap) (m1:hmap) =
+ (forall (r:rid). {:pattern (Map.contains m0 r)} (Set.mem r (mod_set s) /\ Map.contains m0 r) ==> Map.contains m1 r) /\
+ Map.equal m1 (Map.concat m1 (Map.restrict (mod_set s) m0))let lemma_modifies_just_trans (m1:hmap) (m2:hmap) (m3:hmap)
+ (s1:Set.set rid) (s2:Set.set rid)
+ :Lemma (requires (modifies_just s1 m1 m2 /\ modifies_just s2 m2 m3))
+ (ensures (modifies_just (Set.union s1 s2) m1 m3))
+ = ()let lemma_modifies_trans (m1:hmap) (m2:hmap) (m3:hmap)
+ (s1:Set.set rid) (s2:Set.set rid)
+ :Lemma (requires (modifies s1 m1 m2 /\ modifies s2 m2 m3))
+ (ensures (modifies (Set.union s1 s2) m1 m3))
+ = ()val lemma_includes_trans (i:rid) (j:rid) (k:rid)
+ :Lemma (requires (includes i j /\ includes j k))
+ (ensures (includes i k))
+ (decreases (reveal k))
+ [SMTPat (includes i j); SMTPat (includes j k)]val lemma_modset (i:rid) (j:rid)
+ :Lemma (requires (includes j i))
+ (ensures (Set.subset (mod_set (Set.singleton i)) (mod_set (Set.singleton j))))val lemma_modifies_includes (m1:hmap) (m2:hmap) (i:rid) (j:rid)
+ :Lemma (requires (modifies (Set.singleton i) m1 m2 /\ includes j i))
+ (ensures (modifies (Set.singleton j) m1 m2))val lemma_modifies_includes2 (m1:hmap) (m2:hmap) (s1:Set.set rid) (s2:Set.set rid)
+ :Lemma (requires (modifies s1 m1 m2 /\ (forall x. Set.mem x s1 ==> (exists y. Set.mem y s2 /\ includes y x))))
+ (ensures (modifies s2 m1 m2))val lemma_disjoint_parents (pr:rid) (r:rid) (ps:rid) (s:rid)
+ :Lemma (requires (r `extends` pr /\ s `extends` ps /\ disjoint pr ps))
+ (ensures (disjoint r s))
+ [SMTPat (extends r pr); SMTPat (extends s ps); SMTPat (disjoint pr ps)]val lemma_include_cons (i:rid) (j:rid)
+ :Lemma (requires (i =!= j /\ includes i j))
+ (ensures (j =!= root))let disjoint_regions (s1:Set.set rid) (s2:Set.set rid) =
+ forall x y. {:pattern (Set.mem x s1); (Set.mem y s2)} (Set.mem x s1 /\ Set.mem y s2) ==> disjoint x yval extends_parent (tip:rid{tip =!= root}) (r:rid)
+ :Lemma (extends r (parent tip) /\ r =!= tip ==> disjoint r tip \/ extends r tip)
+ [SMTPat (extends r (parent tip))]val includes_child (tip:rid{tip =!= root}) (r:rid)
+ :Lemma (includes r tip ==> r == tip \/ includes r (parent tip))
+ [SMTPat (includes r (parent tip))]val root_is_root (s:rid)
+ :Lemma (requires (includes s root))
+ (ensures (s == root))
+ [SMTPat (includes s root)]unfold
+let extend_post (r:rid) (n:int) (c:int) (freeable:bool) : pure_post rid =
+ fun s ->
+ s `extends` r /\
+ Cons? (reveal s) /\
+ Cons?.hd (reveal s) == (c, n) /\
+ color s == c /\
+ rid_freeable s == freeableval extend (r:rid) (n:int) (c:int)
+: Pure rid (requires True) (extend_post r n c (rid_freeable r))val extend_monochrome_freeable (r:rid) (n:int) (freeable:bool)
+: Pure rid (requires True) (extend_post r n (color r) freeable)val extend_monochrome (r:rid) (n:int)
+: Pure rid (requires True) (extend_post r n (color r) (rid_freeable r))fsdoc: no-summary-found
-fsdoc: no-comment-found
-**** Some predicates ********* Mem definition ********* Lemmas about mem and predicates ********** map_invariant related lemmas ********* downward_closed related lemmas ********** tip_top related lemmas ********* rid_ctr_pred related lemmas ********* Operations on mem ********* The following two lemmas are only used in FStar.Pointer.Base, and invoked explicitly ********* API for generating modifies clauses in the old style, should use new modifies clauses now ********* Lemmas about equality of mem ****** Untyped views of references Opens module FStar.Preorder
+Aliases module FStar.Map as Map
Includes module FStar.Monotonic.HyperHeap
+***** Some predicates *****
+unfold let is_in (r:rid) (h:hmap) = h `Map.contains` rlet is_stack_region r = color r > 0
+let is_heap_color c = c <= 0[@@(deprecated "FStar.HyperStack.ST.is_eternal_region")]
+let is_eternal_region r = is_heap_color (color r) && not (rid_freeable r)unfold let is_eternal_region_hs r = is_heap_color (color r) && not (rid_freeable r)type sid = r:rid{is_stack_region r} //stack region idsunfold let is_above r1 r2 = r1 `includes` r2
+unfold let is_just_below r1 r2 = r1 `extends` r2
+unfold let is_below r1 r2 = r2 `is_above` r1
+let is_strictly_below r1 r2 = r1 `is_below` r2 && r1 <> r2
+let is_strictly_above r1 r2 = r1 `is_above` r2 && r1 <> r2[@@"opaque_to_smt"]
+unfold private let map_invariant_predicate (m:hmap) :Type0 =
+ forall r. Map.contains m r ==>
+ (forall s. includes s r ==> Map.contains m s)[@@"opaque_to_smt"]
+unfold private let downward_closed_predicate (h:hmap) :Type0 =
+ forall (r:rid). r `is_in` h //for any region in the memory
+ ==> (r=root //either is the root
+ \/ (forall (s:rid). (r `is_above` s //or, any region beneath it
+ /\ s `is_in` h) //that is also in the memory
+ ==> ((is_stack_region r = is_stack_region s) /\ //must be of the same flavor as itself
+ ((is_heap_color (color r) /\ rid_freeable r) ==> s == r)))) //and if r is a freeable heap region, s can only be r (no regions strictly below r)[@@"opaque_to_smt"]
+unfold private let tip_top_predicate (tip:rid) (h:hmap) :Type0 =
+ forall (r:sid). r `is_in` h <==> r `is_above` tiplet rid_last_component (r:rid) :GTot int
+ = let open FStar.List.Tot in
+ let r = reveal r in
+ if length r = 0 then 0
+ else snd (hd r)[@@"opaque_to_smt"]
+unfold private let rid_ctr_pred_predicate (h:hmap) (n:int) :Type0 =
+ forall (r:rid). h `Map.contains` r ==> rid_last_component r < n***** Mem definition *****
+[@@ remove_unused_type_parameters [0]]
+val map_invariant (m:hmap) :Type0 //all regions above a contained region are contained
+[@@ remove_unused_type_parameters [0]]
+val downward_closed (h:hmap) :Type0 //regions below a non-root region are of the same color
+[@@ remove_unused_type_parameters [0;1]]
+val tip_top (tip:rid) (h:hmap) :Type0 //all contained stack regions are above tip
+[@@ remove_unused_type_parameters [0;1]]
+val rid_ctr_pred (h:hmap) (n:int) :Type0 //all live regions have last component less than the rid_ctrlet is_tip (tip:rid) (h:hmap) =
+ (is_stack_region tip \/ tip = root) /\ //the tip is a stack region, or the root
+ tip `is_in` h /\ //the tip is live
+ tip_top tip h //any other sid activation is a above (or equal to) the tiplet is_wf_with_ctr_and_tip (h:hmap) (ctr:int) (tip:rid)
+ = (not (rid_freeable root)) /\
+ root `is_in` h /\
+ tip `is_tip` h /\
+ map_invariant h /\
+ downward_closed h /\
+ rid_ctr_pred h ctrprivate val mem' :Type u#1private val mk_mem (rid_ctr:int) (h:hmap) (tip:rid) :mem'val get_hmap (m:mem') :hmap
+val get_rid_ctr (m:mem') :int
+val get_tip (m:mem') :ridprivate val lemma_mk_mem'_projectors (rid_ctr:int) (h:hmap) (tip:rid)
+ :Lemma (requires True)
+ (ensures (let m = mk_mem rid_ctr h tip in
+ (get_hmap m == h /\ get_rid_ctr m == rid_ctr /\ get_tip m == tip)))
+ [SMTPatOr [[SMTPat (get_hmap (mk_mem rid_ctr h tip))];
+ [SMTPat (get_rid_ctr (mk_mem rid_ctr h tip))];
+ [SMTPat (get_tip (mk_mem rid_ctr h tip))]
+ ]]type mem :Type = m:mem'{is_wf_with_ctr_and_tip (get_hmap m) (get_rid_ctr m) (get_tip m) }***** Lemmas about mem and predicates *****
+private val lemma_mem_projectors_are_in_wf_relation (m:mem)
+ :Lemma (is_wf_with_ctr_and_tip (get_hmap m) (get_rid_ctr m) (get_tip m))private val lemma_is_wf_ctr_and_tip_intro (h:hmap) (ctr:int) (tip:rid)
+ :Lemma (requires (root `is_in` h /\ (is_stack_region tip \/ tip = root) /\ tip `is_in` h /\
+ tip_top_predicate tip h /\ map_invariant_predicate h /\
+ downward_closed_predicate h /\ rid_ctr_pred_predicate h ctr))
+ (ensures (is_wf_with_ctr_and_tip h ctr tip))private val lemma_is_wf_ctr_and_tip_elim (m:mem)
+ :Lemma (let h, rid_ctr, tip = get_hmap m, get_rid_ctr m, get_tip m in
+ (root `is_in` h /\ (is_stack_region tip \/ tip = root) /\ tip `is_in` h /\
+ tip_top_predicate tip h /\ map_invariant_predicate h /\
+ downward_closed_predicate h /\ rid_ctr_pred_predicate h rid_ctr))****** map_invariant related lemmas *****
+val lemma_map_invariant (m:mem) (r s:rid)
+ :Lemma (requires (r `is_in` get_hmap m /\ s `is_above` r))
+ (ensures (s `is_in` get_hmap m))
+ [SMTPat (r `is_in` get_hmap m); SMTPat (s `is_above` r); SMTPat (s `is_in` get_hmap m)]***** downward_closed related lemmas ******
+val lemma_downward_closed (m:mem) (r:rid) (s:rid{s =!= root})
+ :Lemma (requires (r `is_in` get_hmap m /\ s `is_above` r))
+ (ensures (is_heap_color (color r) == is_heap_color (color s) /\
+ is_stack_region r == is_stack_region s))
+ [SMTPatOr [[SMTPat (get_hmap m `Map.contains` r); SMTPat (s `is_above` r); SMTPat (is_heap_color (color s))];
+ [SMTPat (get_hmap m `Map.contains` r); SMTPat (s `is_above` r); SMTPat (is_stack_region s)]
+ ]]***** tip_top related lemmas *****
+val lemma_tip_top (m:mem) (r:sid)
+ :Lemma (r `is_in` get_hmap m <==> r `is_above` get_tip m)val lemma_tip_top_smt (m:mem) (r:rid)
+ :Lemma (requires (is_stack_region r))
+ (ensures (r `is_in` get_hmap m <==> r `is_above` get_tip m))
+ [SMTPatOr [[SMTPat (is_stack_region r); SMTPat (r `is_above` get_tip m)];
+ [SMTPat (is_stack_region r); SMTPat (r `is_in` get_hmap m)]]]***** rid_ctr_pred related lemmas *****
+val lemma_rid_ctr_pred (_:unit)
+ :Lemma (forall (m:mem) (r:rid).{:pattern (get_hmap m `Map.contains` r)} get_hmap m `Map.contains` r ==> rid_last_component r < get_rid_ctr m)***** Operations on mem *****
+let empty_mem : mem =
+ let empty_map = Map.restrict Set.empty (Map.const Heap.emp) in
+ let h = Map.upd empty_map root Heap.emp in
+ let tip = root in
+ assume (rid_last_component root == 0);
+ lemma_is_wf_ctr_and_tip_intro h 1 tip;
+ mk_mem 1 h tiplet heap_region_does_not_overlap_with_tip
+ (m:mem) (r:rid{is_heap_color (color r) /\ not (disjoint r (get_tip m)) /\ r =!= root /\ is_stack_region (get_tip m)})
+ : Lemma (requires True)
+ (ensures (~ (r `is_in` get_hmap m)))
+ = root_has_color_zero()let poppable (m:mem) = get_tip m =!= rootprivate let remove_elt (#a:eqtype) (s:Set.set a) (x:a) = Set.intersect s (Set.complement (Set.singleton x))let popped (m0 m1:mem) =
+ poppable m0 /\
+ (let h0, tip0, h1, tip1 = get_hmap m0, get_tip m0, get_hmap m1, get_tip m1 in
+ (parent tip0 = tip1 /\
+ Set.equal (Map.domain h1) (remove_elt (Map.domain h0) tip0) /\
+ Map.equal h1 (Map.restrict (Map.domain h1) h0)))let pop (m0:mem{poppable m0}) :mem =
+ let h0, tip0, rid_ctr0 = get_hmap m0, get_tip m0, get_rid_ctr m0 in
+ root_has_color_zero();
+ lemma_is_wf_ctr_and_tip_elim m0;
+ let dom = remove_elt (Map.domain h0) tip0 in
+ let h1 = Map.restrict dom h0 in
+ let tip1 = parent tip0 in
+ lemma_is_wf_ctr_and_tip_intro h1 rid_ctr0 tip1;
+ mk_mem rid_ctr0 h1 tip1A (reference a) may reside in the stack or heap, and may be manually managed +Mark it private so that clients can't use its projectors etc. +enabling extraction of mreference to just a reference in ML and pointer in C +note that this not enforcing any abstraction
+private noeq
+type mreference' (a:Type) (rel:preorder a) =
+ | MkRef : frame:rid -> ref:Heap.mref a rel -> mreference' a rellet mreference a rel = mreference' a relTODO: rename to frame_of, avoiding the inconsistent use of camelCase
+let frameOf (#a:Type) (#rel:preorder a) (r:mreference a rel) :rid
+ = r.framelet mk_mreference (#a:Type) (#rel:preorder a) (id:rid)
+ (r:Heap.mref a rel)
+ :mreference a rel
+ = MkRef id rHopefully we can get rid of this one
+val as_ref (#a:Type0) (#rel:preorder a) (x:mreference a rel)
+ :Heap.mref a relAnd make this one abstract
+let as_addr #a #rel (x:mreference a rel)
+ :GTot pos
+ = Heap.addr_of (as_ref x)val lemma_as_ref_inj (#a:Type) (#rel:preorder a) (r:mreference a rel)
+ :Lemma (requires True) (ensures (mk_mreference (frameOf r) (as_ref r) == r))
+ [SMTPat (as_ref r)]let is_mm (#a:Type) (#rel:preorder a) (r:mreference a rel) :GTot bool =
+ Heap.is_mm (as_ref r)Warning: all of the type aliases below get special support for KreMLin +extraction. If you rename or add to this list, +src/extraction/FStar.Extraction.Kremlin.fs needs to be updated.
+adding (not s.mm) to stackref and ref so as to keep their semantics as is
+let mstackref (a:Type) (rel:preorder a) =
+ s:mreference a rel{ is_stack_region (frameOf s) && not (is_mm s) }let mref (a:Type) (rel:preorder a) =
+ s:mreference a rel{ is_eternal_region_hs (frameOf s) && not (is_mm s) }let mmmstackref (a:Type) (rel:preorder a) =
+ s:mreference a rel{ is_stack_region (frameOf s) && is_mm s }let mmmref (a:Type) (rel:preorder a) =
+ s:mreference a rel{ is_eternal_region_hs (frameOf s) && is_mm s }NS: Why do we need this one?
+let s_mref (i:rid) (a:Type) (rel:preorder a) = s:mreference a rel{frameOf s = i}is_above m.tip) /\ Map.contains ...As far as the memory model is concerned, this should just be Map.contains
+
+The fact that an eternal region is always contained (because of monotonicity) should be used in the ST interface
+
+let live_region (m:mem) (i:rid) :bool = get_hmap m `Map.contains` ilet contains (#a:Type) (#rel:preorder a) (m:mem) (s:mreference a rel) :GTot bool =
+ let i = frameOf s in
+ live_region m i && (FStar.StrongExcludedMiddle.strong_excluded_middle (Heap.contains (get_hmap m `Map.sel` i) (as_ref s)))let unused_in (#a:Type) (#rel:preorder a) (r:mreference a rel) (m:mem) :GTot bool =
+ let h = get_hmap m in
+ let i = frameOf r in
+ not (h `Map.contains` i) ||
+ FStar.StrongExcludedMiddle.strong_excluded_middle (Heap.unused_in (as_ref r) (h `Map.sel` i))let contains_ref_in_its_region (#a:Type) (#rel:preorder a) (m:mem) (r:mreference a rel) :GTot bool =
+ let i = frameOf r in
+ FStar.StrongExcludedMiddle.strong_excluded_middle (Heap.contains (get_hmap m `Map.sel` i) (as_ref r))let fresh_ref (#a:Type) (#rel:preorder a) (r:mreference a rel) (m0:mem) (m1:mem) :Type0 =
+ let i = frameOf r in
+ Heap.fresh (as_ref r) (get_hmap m0 `Map.sel` i) (get_hmap m1 `Map.sel` i)let fresh_region (i:rid) (m0 m1:mem) =
+ not (get_hmap m0 `Map.contains` i) /\ get_hmap m1 `Map.contains` ilet sel (#a:Type) (#rel:preorder a) (m:mem) (s:mreference a rel) :GTot a
+ = Heap.sel (get_hmap m `Map.sel` (frameOf s)) (as_ref s)let upd (#a:Type) (#rel:preorder a) (m:mem) (s:mreference a rel{live_region m (frameOf s)}) (v:a)
+ :GTot mem
+ = let h, rid_ctr, tip = get_hmap m, get_rid_ctr m, get_tip m in
+ lemma_is_wf_ctr_and_tip_elim m;
+ let i = frameOf s in
+ let h = Map.upd h i (Heap.upd (Map.sel h i) (as_ref s) v) in
+ lemma_is_wf_ctr_and_tip_intro h rid_ctr tip;
+ mk_mem rid_ctr h tiplet alloc (#a:Type0) (rel:preorder a) (id:rid) (init:a) (mm:bool) (m:mem{get_hmap m `Map.contains` id})
+ :Tot (p:(mreference a rel * mem){let (r, h) = Heap.alloc rel (get_hmap m `Map.sel` id) init mm in
+ as_ref (fst p) == r /\
+ get_hmap (snd p) == Map.upd (get_hmap m) id h})
+ = let h, rid_ctr, tip = get_hmap m, get_rid_ctr m, get_tip m in
+ lemma_is_wf_ctr_and_tip_elim m;
+ let r, id_h = Heap.alloc rel (Map.sel h id) init mm in
+ let h = Map.upd h id id_h in
+ lemma_is_wf_ctr_and_tip_intro h rid_ctr tip;
+ (mk_mreference id r), mk_mem rid_ctr h tiplet free (#a:Type0) (#rel:preorder a) (r:mreference a rel{is_mm r}) (m:mem{m `contains` r})
+ :Tot mem
+ = let h, rid_ctr, tip = get_hmap m, get_rid_ctr m, get_tip m in
+ lemma_is_wf_ctr_and_tip_elim m;
+ let i = frameOf r in
+ let i_h = h `Map.sel` i in
+ let i_h = Heap.free_mm i_h (as_ref r) in
+ let h = Map.upd h i i_h in
+ lemma_is_wf_ctr_and_tip_intro h rid_ctr tip;
+ mk_mem rid_ctr h tiplet upd_tot (#a:Type) (#rel:preorder a) (m:mem) (r:mreference a rel{m `contains` r}) (v:a)
+ :Tot mem
+ = let h, rid_ctr, tip = get_hmap m, get_rid_ctr m, get_tip m in
+ lemma_is_wf_ctr_and_tip_elim m;
+ let i = frameOf r in
+ let i_h = h `Map.sel` i in
+ let i_h = Heap.upd_tot i_h (as_ref r) v in
+ let h = Map.upd h i i_h in
+ lemma_is_wf_ctr_and_tip_intro h rid_ctr tip;
+ mk_mem rid_ctr h tiplet sel_tot (#a:Type) (#rel:preorder a) (m:mem) (r:mreference a rel{m `contains` r})
+ :Tot a
+ = Heap.sel_tot (get_hmap m `Map.sel` (frameOf r)) (as_ref r)let fresh_frame (m0:mem) (m1:mem) =
+ not (get_hmap m0 `Map.contains` get_tip m1) /\
+ parent (get_tip m1) == get_tip m0 /\
+ get_hmap m1 == Map.upd (get_hmap m0) (get_tip m1) Heap.emplet hs_push_frame (m:mem) :Tot (m':mem{fresh_frame m m'})
+ = let h, rid_ctr, tip = get_hmap m, get_rid_ctr m, get_tip m in
+ lemma_is_wf_ctr_and_tip_elim m;
+ let new_tip_rid = extend tip rid_ctr 1 in
+ let h = Map.upd h new_tip_rid Heap.emp in
+ assert (forall (s:rid). (new_tip_rid `is_above` s /\ s `is_in` h) ==> s = new_tip_rid);
+ lemma_is_wf_ctr_and_tip_intro h (rid_ctr + 1) new_tip_rid;
+ mk_mem (rid_ctr + 1) h new_tip_ridlet new_eternal_region (m:mem) (parent:rid{is_eternal_region_hs parent /\ get_hmap m `Map.contains` parent})
+ (c:option int{None? c \/ is_heap_color (Some?.v c)})
+ :Tot (t:(rid * mem){fresh_region (fst t) m (snd t)})
+ = let h, rid_ctr, tip = get_hmap m, get_rid_ctr m, get_tip m in
+ lemma_is_wf_ctr_and_tip_elim m;
+ let new_rid =
+ if None? c then extend_monochrome parent rid_ctr
+ else extend parent rid_ctr (Some?.v c)
+ in
+ let h = Map.upd h new_rid Heap.emp in
+ lemma_is_wf_ctr_and_tip_intro h (rid_ctr + 1) tip;
+ new_rid, mk_mem (rid_ctr + 1) h tiplet new_freeable_heap_region
+ (m:mem)
+ (parent:rid{is_eternal_region_hs parent /\ get_hmap m `Map.contains` parent})
+: t:(rid * mem){fresh_region (fst t) m (snd t) /\ rid_freeable (fst t)}
+= let h, rid_ctr, tip = get_hmap m, get_rid_ctr m, get_tip m in
+ lemma_is_wf_ctr_and_tip_elim m;
+ let new_rid = extend_monochrome_freeable parent rid_ctr true in
+ let h = Map.upd h new_rid Heap.emp in
+ lemma_is_wf_ctr_and_tip_intro h (rid_ctr + 1) tip;
+ new_rid, mk_mem (rid_ctr + 1) h tiplet free_heap_region
+ (m0:mem)
+ (r:rid{
+ is_heap_color (color r) /\
+ rid_freeable r /\
+ get_hmap m0 `Map.contains` r})
+: mem
+= let h0, rid_ctr0 = get_hmap m0, get_rid_ctr m0 in
+ lemma_is_wf_ctr_and_tip_elim m0;
+ let dom = remove_elt (Map.domain h0) r in
+ let h1 = Map.restrict dom h0 in
+ lemma_is_wf_ctr_and_tip_intro h1 rid_ctr0 (get_tip m0);
+ mk_mem (get_rid_ctr m0) h1 (get_tip m0)***** The following two lemmas are only used in FStar.Pointer.Base, and invoked explicitly *****
+val lemma_sel_same_addr (#a:Type0) (#rel:preorder a) (h:mem) (r1:mreference a rel) (r2:mreference a rel)
+ :Lemma (requires (frameOf r1 == frameOf r2 /\ h `contains` r1 /\ as_addr r1 = as_addr r2 /\ is_mm r1 == is_mm r2))
+ (ensures (h `contains` r2 /\ sel h r1 == sel h r2))val lemma_upd_same_addr (#a:Type0) (#rel:preorder a) (h:mem) (r1 r2:mreference a rel) (x: a)
+ :Lemma (requires (frameOf r1 == frameOf r2 /\ (h `contains` r1 \/ h `contains` r2) /\
+ as_addr r1 == as_addr r2 /\ is_mm r1 == is_mm r2))
+ (ensures (h `contains` r1 /\ h `contains` r2 /\ upd h r1 x == upd h r2 x))Two references with different reads are disjoint.
+val mreference_distinct_sel_disjoint
+ (#a:Type0) (#rel1: preorder a) (#rel2: preorder a) (h: mem) (r1: mreference a rel1) (r2:mreference a rel2)
+ : Lemma (requires (h `contains` r1 /\ h `contains` r2 /\ frameOf r1 == frameOf r2 /\ as_addr r1 == as_addr r2))
+ (ensures (sel h r1 == sel h r2)) NOTE: the modifies clauses used to have a m0.tip == m1.tip conjunct too
+
+ which seemed a bit misplaced
+
+ removing that conjunct required very few changes (one in HACL), since ST effect gives it already
+
+let modifies (s:Set.set rid) (m0:mem) (m1:mem) = modifies_just s (get_hmap m0) (get_hmap m1)let modifies_transitively (s:Set.set rid) (m0:mem) (m1:mem) = FStar.Monotonic.HyperHeap.modifies s (get_hmap m0) (get_hmap m1)let heap_only (m0:mem) = get_tip m0 == rootlet top_frame (m:mem) = get_hmap m `Map.sel` get_tip mval modifies_drop_tip (m0:mem) (m1:mem) (m2:mem) (s:Set.set rid)
+ : Lemma (fresh_frame m0 m1 /\ get_tip m1 == get_tip m2 /\
+ modifies_transitively (Set.union s (Set.singleton (get_tip m1))) m1 m2 ==>
+ modifies_transitively s m0 (pop m2))let modifies_one id h0 h1 = modifies_one id (get_hmap h0) (get_hmap h1)
+let modifies_ref (id:rid) (s:Set.set nat) (h0:mem) (h1:mem) =
+ Heap.modifies s (get_hmap h0 `Map.sel` id) (get_hmap h1 `Map.sel` id)***** API for generating modifies clauses in the old style, should use new modifies clauses now *****
+noeq type some_ref =
+ | Ref: #a:Type0 -> #rel:preorder a -> mreference a rel -> some_reflet some_refs = list some_ref[@@"opaque_to_smt"]
+private let rec regions_of_some_refs (rs:some_refs) :Tot (Set.set rid) =
+ match rs with
+ | [] -> Set.empty
+ | (Ref r)::tl -> Set.union (Set.singleton (frameOf r)) (regions_of_some_refs tl)[@@"opaque_to_smt"]
+private let rec refs_in_region (r:rid) (rs:some_refs) :GTot (Set.set nat) =
+ match rs with
+ | [] -> Set.empty
+ | (Ref x)::tl ->
+ Set.union (if frameOf x = r then Set.singleton (as_addr x) else Set.empty)
+ (refs_in_region r tl)[@@"opaque_to_smt"]
+private let rec modifies_some_refs (i:some_refs) (rs:some_refs) (h0:mem) (h1:mem) :GTot Type0 =
+ match i with
+ | [] -> True
+ | (Ref x)::tl ->
+ (modifies_ref (frameOf x) (refs_in_region (frameOf x) rs) h0 h1) /\
+ (modifies_some_refs tl rs h0 h1)[@@"opaque_to_smt"]
+unfold private let norm_steps :list norm_step =iota for reducing match
+[iota; zeta; delta; delta_only ["FStar.Monotonic.HyperStack.regions_of_some_refs";
+ "FStar.Monotonic.HyperStack.refs_in_region";
+ "FStar.Monotonic.HyperStack.modifies_some_refs"];
+ primops][@@"opaque_to_smt"]
+unfold let mods (rs:some_refs) (h0 h1:mem) :GTot Type0 =
+ (norm norm_steps (modifies (regions_of_some_refs rs) h0 h1)) /\
+ (norm norm_steps (modifies_some_refs rs rs h0 h1))////
+val eternal_disjoint_from_tip (h:mem{is_stack_region (get_tip h)})
+ (r:rid{is_heap_color (color r) /\
+ r =!= root /\
+ r `is_in` get_hmap h})
+ :Lemma (disjoint (get_tip h) r)val above_tip_is_live (#a:Type) (#rel:preorder a) (m:mem) (x:mreference a rel)
+ :Lemma (requires (frameOf x `is_above` get_tip m))
+ (ensures (frameOf x `is_in` get_hmap m))///
+***** Lemmas about equality of mem *****
+val lemma_heap_equality_cancel_same_mref_upd
+ (#a:Type) (#rel:preorder a) (h:mem) (r:mreference a rel) (x y:a)
+ :Lemma (requires (live_region h (frameOf r)))
+ (ensures (upd (upd h r x) r y == upd h r y))val lemma_heap_equality_upd_with_sel
+ (#a:Type) (#rel:preorder a) (h:mem) (r:mreference a rel)
+ :Lemma (requires (h `contains` r))
+ (ensures (upd h r (sel h r) == h))val lemma_heap_equality_commute_distinct_upds
+ (#a:Type) (#b:Type) (#rel_a:preorder a) (#rel_b:preorder b)
+ (h:mem) (r1:mreference a rel_a) (r2:mreference b rel_b) (x:a) (y:b)
+ :Lemma (requires (as_addr r1 =!= as_addr r2 /\ live_region h (frameOf r1) /\ live_region h (frameOf r2)))
+ (ensures (upd (upd h r1 x) r2 y == upd (upd h r2 y) r1 x))val lemma_next_addr_contained_refs_addr (_:unit)
+ :Lemma (forall (a:Type0) (rel:preorder a) (r:mreference a rel) (m:mem).
+ m `contains` r ==> as_addr r < Heap.next_addr (get_hmap m `Map.sel` frameOf r))Definition and ghost decidable equality
+val aref: Type0val dummy_aref :arefval aref_equal (a1 a2: aref)
+ :Ghost bool (requires True)
+ (ensures (fun b -> b == true <==> a1 == a2))Introduction rule
+val aref_of (#t: Type) (#rel: preorder t) (r: mreference t rel) :arefOperators lifted from reference
+val frameOf_aref (a:aref) :GTot ridval frameOf_aref_of (#t:Type) (#rel:preorder t) (r:mreference t rel)
+ :Lemma (frameOf_aref (aref_of r) == frameOf r)
+ [SMTPat (frameOf_aref (aref_of r))]val aref_as_addr (a:aref) :GTot posval aref_as_addr_aref_of (#t:Type) (#rel:preorder t) (r:mreference t rel)
+ :Lemma (aref_as_addr (aref_of r) == as_addr r)
+ [SMTPat (aref_as_addr (aref_of r))]val aref_is_mm (r:aref) :GTot boolval is_mm_aref_of (#t:Type) (#rel:preorder t) (r:mreference t rel)
+ :Lemma (aref_is_mm (aref_of r) == is_mm r)
+ [SMTPat (aref_is_mm (aref_of r))][@@ remove_unused_type_parameters [0;1]]
+val aref_unused_in (a:aref) (h:mem) :GTot Type0val unused_in_aref_of (#t:Type) (#rel:preorder t) (r:mreference t rel) (h:mem)
+ :Lemma (aref_unused_in (aref_of r) h <==> unused_in r h)
+ [SMTPat (aref_unused_in (aref_of r) h)]val contains_aref_unused_in (#a:Type) (#rel:preorder a) (h:mem) (x:mreference a rel) (y:aref)
+ :Lemma (requires (contains h x /\ aref_unused_in y h))
+ (ensures (frameOf x <> frameOf_aref y \/ as_addr x <> aref_as_addr y))
+ [SMTPat (contains h x); SMTPat (aref_unused_in y h)]Elimination rule
+[@@ remove_unused_type_parameters [0;1;2;3]]
+val aref_live_at (h:mem) (a:aref) (v:Type0) (rel:preorder v) :GTot Type0val greference_of (a:aref) (v:Type0) (rel:preorder v)
+ :Ghost (mreference v rel) (requires (exists h . aref_live_at h a v rel))
+ (ensures (fun _ -> True))val reference_of (h:mem) (a:aref) (v:Type0) (rel:preorder v)
+ :Pure (mreference v rel) (requires (aref_live_at h a v rel))
+ (ensures (fun x -> aref_live_at h a v rel /\ frameOf x == frameOf_aref a /\
+ as_addr x == aref_as_addr a /\ is_mm x == aref_is_mm a))val aref_live_at_aref_of (h:mem) (#t:Type0) (#rel:preorder t) (r:mreference t rel)
+ :Lemma (aref_live_at h (aref_of r) t rel <==> contains h r)
+ [SMTPat (aref_live_at h (aref_of r) t rel)]val contains_greference_of (h:mem) (a:aref) (t:Type0) (rel:preorder t)
+ :Lemma (requires (exists h' . aref_live_at h' a t rel))
+ (ensures ((exists h' . aref_live_at h' a t rel) /\ (contains h (greference_of a t rel) <==> aref_live_at h a t rel)))
+ [SMTPatOr [
+ [SMTPat (contains h (greference_of a t rel))];
+ [SMTPat (aref_live_at h a t rel)];
+ ]]val aref_of_greference_of (a:aref) (v:Type0) (rel:preorder v)
+ :Lemma (requires (exists h' . aref_live_at h' a v rel))
+ (ensures ((exists h' . aref_live_at h' a v rel) /\ aref_of (greference_of a v rel) == a))
+ [SMTPat (aref_of (greference_of a v rel))]Operators lowered to rref
+val frameOf_greference_of (a:aref) (t:Type0) (rel:preorder t)
+ :Lemma (requires (exists h . aref_live_at h a t rel))
+ (ensures ((exists h . aref_live_at h a t rel) /\ frameOf (greference_of a t rel) == frameOf_aref a))
+ [SMTPat (frameOf (greference_of a t rel))]val as_addr_greference_of (a:aref) (t:Type0) (rel:preorder t)
+ :Lemma (requires (exists h . aref_live_at h a t rel))
+ (ensures ((exists h . aref_live_at h a t rel) /\ as_addr (greference_of a t rel) == aref_as_addr a))
+ [SMTPat (as_addr (greference_of a t rel))]val is_mm_greference_of (a:aref) (t:Type0) (rel:preorder t)
+ :Lemma (requires (exists h . aref_live_at h a t rel))
+ (ensures ((exists h . aref_live_at h a t rel) /\ is_mm (greference_of a t rel) == aref_is_mm a))
+ [SMTPat (is_mm (greference_of a t rel))]val unused_in_greference_of (a:aref) (t:Type0) (rel:preorder t) (h:mem)
+ :Lemma (requires (exists h . aref_live_at h a t rel))
+ (ensures ((exists h . aref_live_at h a t rel) /\ (unused_in (greference_of a t rel) h <==> aref_unused_in a h)))
+ [SMTPat (unused_in (greference_of a t rel) h)]val sel_reference_of (a:aref) (v:Type0) (rel:preorder v) (h1 h2: mem)
+ :Lemma (requires (aref_live_at h1 a v rel /\ aref_live_at h2 a v rel))
+ (ensures (aref_live_at h2 a v rel /\ sel h1 (reference_of h2 a v rel) == sel h1 (greference_of a v rel)))
+ [SMTPat (sel h1 (reference_of h2 a v rel))]val upd_reference_of (a:aref) (v:Type0) (rel:preorder v) (h1 h2:mem) (x:v)
+ :Lemma (requires (aref_live_at h1 a v rel /\ aref_live_at h2 a v rel))
+ (ensures (aref_live_at h1 a v rel /\ aref_live_at h2 a v rel /\
+ upd h1 (reference_of h2 a v rel) x == upd h1 (greference_of a v rel) x))
+ [SMTPat (upd h1 (reference_of h2 a v rel) x)]fsdoc: no-summary-found
+A library for monotonic references to partial, dependent maps, with a whole-map invariant
+Opens module FStar.HyperStack
+Opens module FStar.HyperStack.ST
+Aliases module FStar.HyperStack as HS
Aliases module FStar.HyperStack.ST as HST
Partial, dependent maps
+type map' (a:Type) (b:a -> Type) =
+ (x:a -> Tot (option (b x)))Partial, dependent maps, with a whole-map invariant
+type map (a:Type) (b:a -> Type) (inv:map' a b -> Type0) =
+ m:map' a b{inv m}let upd (#a:eqtype) #b (m:map' a b) (x:a) (y:b x)
+ : Tot (map' a b)
+ = fun z -> if x = z then Some y else m zlet sel #a #b (m:map' a b) (x:a)
+ : Tot (option (b x))
+ = m xlet grows_aux #a #b #inv :Preorder.preorder (map a b inv) =
+ fun (m1 m2:map a b inv) ->
+ forall x.{:pattern (Some? (m1 x))}
+ Some? (m1 x) ==> Some? (m2 x) /\ Some?.v (m1 x) == Some?.v (m2 x)[@@"opaque_to_smt"]
+let grows #a #b #inv = grows_aux #a #b #invMonotone, partial, dependent maps, with a whole-map invariant
+type t r a b inv = m_rref r (map a b inv) grows //maybe grows can include the inv?let empty_map a b
+ : Tot (map' a b)
+ = fun x -> Nonetype rid = HST.eridlet alloc (#r:rid) #a #b #inv
+ : ST (t r a b inv)
+ (requires (fun h -> inv (empty_map a b) /\ witnessed (region_contains_pred r)))
+ (ensures (fun h0 x h1 ->
+ inv (empty_map a b) /\
+ ralloc_post r (empty_map a b) h0 x h1))
+ = ralloc r (empty_map a b)let defined #r #a #b #inv (m:t r a b inv) (x:a) (h:HS.mem)
+ : GTot Type0
+ = Some? (sel (HS.sel h m) x)let contains #r #a #b #inv (m:t r a b inv) (x:a) (y:b x) (h:HS.mem)
+ : GTot Type0
+ = Some? (sel (HS.sel h m) x) /\ Some?.v (sel (HS.sel h m) x) == ylet value #r #a #b #inv (m:t r a b inv) (x:a) (h:HS.mem{defined m x h})
+ : GTot (r:b x{contains m x r h})
+ = Some?.v (sel (HS.sel h m) x)let fresh #r #a #b #inv (m:t r a b inv) (x:a) (h:HS.mem)
+ : GTot Type0
+ = None? (sel (HS.sel h m) x)let contains_stable #r #a #b #inv (m:t r a b inv) (x:a) (y:b x)
+ : Lemma (ensures (stable_on_t m (contains m x y)))
+ = reveal_opaque (`%grows) (grows #a #b #inv)let extend (#r:rid) (#a:eqtype) (#b:a -> Type) (#inv:(map' a b -> Type0)) (m:t r a b inv) (x:a) (y:b x)
+ : ST unit
+ (requires (fun h -> let cur = HS.sel h m in inv (upd cur x y) /\ sel cur x == None))
+ (ensures (fun h0 u h1 ->
+ let cur = HS.sel h0 m in
+ let hsref = m in
+ HS.contains h1 m
+ /\ modifies (Set.singleton r) h0 h1
+ /\ modifies_ref r (Set.singleton (HS.as_addr hsref)) h0 h1
+ /\ HS.sel h1 m == upd cur x y
+ /\ HST.witnessed (defined m x)
+ /\ HST.witnessed (contains m x y)))
+ = recall m;
+ reveal_opaque (`%grows) (grows #a #b #inv);
+ let cur = !m in
+ m := upd cur x y;
+ contains_stable m x y;
+ mr_witness m (defined m x);
+ mr_witness m (contains m x y)let lookup #r #a #b #inv (m:t r a b inv) (x:a)
+ : ST (option (b x))
+ (requires (fun h -> True))
+ (ensures (fun h0 y h1 ->
+ h0==h1 /\
+ y == sel (HS.sel h1 m) x /\
+ (None? y ==> fresh m x h1) /\
+ (Some? y ==>
+ defined m x h1 /\
+ contains m x (Some?.v y) h1 /\
+ HST.witnessed (defined m x) /\
+ HST.witnessed (contains m x (Some?.v y)))))
+= reveal_opaque (`%grows) (grows #a #b #inv);
+ let y = sel !m x in
+ match y with
+ | None -> y
+ | Some b ->
+ contains_stable m x b;
+ mr_witness m (defined m x);
+ mr_witness m (contains m x b);
+ yCopyright 2019 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.
+unfold
+let is_monotonic (#a:Type) (wp:pure_wp' a) =forall (p q:pure_post a). (forall (x:a). p x ==> q x) ==> (wp p ==> wp q)let elim_pure_wp_monotonicity (#a:Type) (wp:pure_wp a)
+ : Lemma (is_monotonic wp)
+ = reveal_opaque (`%pure_wp_monotonic) pure_wp_monotoniclet elim_pure_wp_monotonicity_forall (_:unit)
+ : Lemma
+ (forall (a:Type) (wp:pure_wp a). is_monotonic wp)
+ = reveal_opaque (`%pure_wp_monotonic) pure_wp_monotoniclet intro_pure_wp_monotonicity (#a:Type) (wp:pure_wp' a)
+ : Lemma
+ (requires is_monotonic wp)
+ (ensures pure_wp_monotonic a wp)
+ = reveal_opaque (`%pure_wp_monotonic) pure_wp_monotonicunfold
+let as_pure_wp (#a:Type) (wp:pure_wp' a)
+ : Pure (pure_wp a)
+ (requires is_monotonic wp)
+ (ensures fun r -> r == wp)
+ = intro_pure_wp_monotonicity wp;
+ wpfsdoc: no-summary-found
-fsdoc: no-comment-found
-let ((write_at_end (#a:Type) (#i:rid) (r:m_rref i (seq a) grows) (x:a)):(ST unit ((requires ((fun h -> True)))) ((ensures ((fun h0 _ h1 -> /\(/\(/\(/\(contains h1 r, modifies_one i h0 h1), modifies_ref i (Set.singleton (HS.as_addr r)) h0 h1), ==(HS.sel h1 r, Seq.snoc (HS.sel h0 r) x)), witnessed (at_least (Seq.length (HS.sel h0 r)) x r)))))))):recall r; let s0 = !(r) in let n = Seq.length s0 in :=(r, Seq.snoc s0 x); at_least_is_stable n x r; Seq.contains_snoc s0 x; mr_witness r (at_least n x r)Opens module FStar.Seq
+Opens module FStar.Classical
+Aliases module FStar.HyperStack as HS
Aliases module FStar.HyperStack.ST as HST
Opens module FStar.HyperStack
+Opens module FStar.HyperStack.ST
+Aliases module FStar.Seq as Seq
+2016-11-22: The following is meant to override the fact that the
+enclosing namespace of the current module (here FStar.Monotonic) is
+automatically opened, which makes Seq resolve into
+FStar.Monotonic.Seq instead of FStar.Seq.
//////////////////////////////////////////////////////////////////////////////
+earlier it was written in terms of an exists s3. Seq.equal (append s1 s3) s2
+
+that meant going through many hoops to prove simple things like transitivity of grows
+
+so far this seems to work better.
+
+let grows_aux (#a:Type) :Preorder.preorder (seq a)
+ = fun (s1:seq a) (s2:seq a) ->
+ length s1 <= length s2 /\
+ (forall (i:nat).{:pattern (Seq.index s1 i) \/ (Seq.index s2 i)} i < length s1 ==> index s1 i == index s2 i)[@@"opaque_to_smt"]
+let grows #a = grows_aux #atype rid = HST.eridlet snoc (s:seq 'a) (x:'a)
+ : Tot (seq 'a)
+ = Seq.append s (Seq.create 1 x)let lemma_snoc_extends (#a:Type) (s:seq a) (x:a)
+ : Lemma (requires True)
+ (ensures (grows s (Seq.snoc s x)))
+ [SMTPat (grows s (Seq.snoc s x))]
+ = reveal_opaque (`%grows) (grows #a)let alloc_mref_seq (#a:Type) (r:rid) (init:seq a)
+ : ST (m_rref r (seq a) grows)
+ (requires (fun _ -> HST.witnessed (region_contains_pred r)))
+ (ensures (fun h0 m h1 ->
+ HS.contains h1 m /\
+ HS.sel h1 m == init /\
+ HST.ralloc_post r init h0 m h1))
+ = ralloc r initlet at_least (#a:Type) (#i:rid) (n:nat) (x:a) (r:m_rref i (seq a) grows) (h:mem) =
+ Seq.length (HS.sel h r) > n
+ /\ Seq.index (HS.sel h r) n == xlet at_least_is_stable (#a:Type) (#i:rid) (n:nat) (x:a) (r:m_rref i (seq a) grows)
+ : Lemma (ensures stable_on_t r (at_least n x r))
+ = reveal_opaque (`%grows) (grows #a)extending a stored sequence, witnessing its new entry for convenience.
-val collect:Unidentified product: [(Unidentified product: ['a] (Tot (seq 'b)))] Unidentified product: [s:seq 'a] (Tot (seq 'b) (decreases (Seq.length s)))let write_at_end (#a:Type) (#i:rid) (r:m_rref i (seq a) grows) (x:a)
+ : ST unit
+ (requires (fun h -> True))
+ (ensures (fun h0 _ h1 ->
+ contains h1 r
+ /\ modifies_one i h0 h1
+ /\ modifies_ref i (Set.singleton (HS.as_addr r)) h0 h1
+ /\ HS.sel h1 r == Seq.snoc (HS.sel h0 r) x
+ /\ witnessed (at_least (Seq.length (HS.sel h0 r)) x r)))
+ =
+ recall r;
+ let s0 = !r in
+ let n = Seq.length s0 in
+ r := Seq.snoc s0 x;
+ at_least_is_stable n x r;
+ Seq.contains_snoc s0 x;
+ mr_witness r (at_least n x r)////////////////////////////////////////////////////////////////////////////// +Monotone sequences with a (stateless) invariant of the whole sequence +//////////////////////////////////////////////////////////////////////////////
+let grows_p (#a:Type) (p:seq a -> Type) :Preorder.preorder (s:seq a{p s}) =
+ fun s1 s2 -> grows s1 s2let i_seq (r:rid) (a:Type) (p:seq a -> Type) = m_rref r (s:seq a{p s}) (grows_p p)let alloc_mref_iseq (#a:Type) (p:seq a -> Type) (r:rid) (init:seq a{p init})
+ : ST (i_seq r a p)
+ (requires (fun _ -> HST.witnessed (region_contains_pred r)))
+ (ensures (fun h0 m h1 -> HST.ralloc_post r init h0 m h1))
+ = ralloc r initlet i_at_least (#r:rid) (#a:Type) (#p:(seq a -> Type)) (n:nat) (x:a) (m:i_seq r a p) (h:mem) =
+ Seq.length (HS.sel h m) > n
+ /\ Seq.index (HS.sel h m) n == xlet i_at_least_is_stable (#r:rid) (#a:Type) (#p:seq a -> Type) (n:nat) (x:a) (m:i_seq r a p)
+ : Lemma (ensures stable_on_t m (i_at_least n x m))
+ = reveal_opaque (`%grows) (grows #a)let int_at_most #r #a #p (x:int) (is:i_seq r a p) (h:mem) : Type0 =
+ x < Seq.length (HS.sel h is)let int_at_most_is_stable (#r:rid) (#a:Type) (#p:seq a -> Type) (is:i_seq r a p) (k:int)
+ : Lemma (ensures stable_on_t is (int_at_most k is))
+ = reveal_opaque (`%grows) (grows #a)let i_sel (#r:rid) (#a:Type) (#p:seq a -> Type) (h:mem) (m:i_seq r a p)
+ : GTot (s:seq a{p s})
+ = HS.sel h mlet i_read (#a:Type) (#p:Seq.seq a -> Type) (#r:rid) (m:i_seq r a p)
+ : ST (s:seq a{p s})
+ (requires (fun h -> True))
+ (ensures (fun h0 x h1 -> h0==h1 /\ x == i_sel h0 m))
+ = !mlet i_contains (#r:rid) (#a:Type) (#p:seq a -> Type) (m:i_seq r a p) (h:mem)
+ : GTot Type0
+ = HS.contains h mlet i_write_at_end (#a:Type) (#p:seq a -> Type) (#rgn:rid) (r:i_seq rgn a p) (x:a)
+ : ST unit
+ (requires (fun h -> p (Seq.snoc (i_sel h r) x)))
+ (ensures (fun h0 _ h1 ->
+ i_contains r h1
+ /\ modifies_one rgn h0 h1
+ /\ modifies_ref rgn (Set.singleton (HS.as_addr r)) h0 h1
+ /\ i_sel h1 r == Seq.snoc (i_sel h0 r) x
+ /\ witnessed (i_at_least (Seq.length (i_sel h0 r)) x r)))
+ =
+ recall r;
+ let s0 = !r in
+ let n = Seq.length s0 in
+ r := Seq.snoc s0 x;
+ i_at_least_is_stable n x r;
+ contains_snoc s0 x;
+ mr_witness r (i_at_least n x r)////////////////////////////////////////////////////////////////////////////// +Testing invariant sequences +//////////////////////////////////////////////////////////////////////////////
+private let invariant (s:seq nat) =
+ forall (i:nat) (j:nat). i < Seq.length s /\ j < Seq.length s /\ i<>j
+ ==> Seq.index s i <> Seq.index s jprivate val test0: r:rid -> a:m_rref r (seq nat) grows -> k:nat -> ST unit
+ (requires (fun h -> k < Seq.length (HS.sel h a)))
+ (ensures (fun h0 result h1 -> True))
+let test0 r a k =
+ let h0 = HST.get() in
+ let _ =
+ let s = HS.sel h0 a in
+ at_least_is_stable k (Seq.index (HS.sel h0 a) k) a;
+ Seq.contains_intro s k (Seq.index s k) in
+ mr_witness a (at_least k (Seq.index (HS.sel h0 a) k) a)private val itest: r:rid -> a:i_seq r nat invariant -> k:nat -> ST unit
+ (requires (fun h -> k < Seq.length (i_sel h a)))
+ (ensures (fun h0 result h1 -> True))
+let itest r a k =
+ let h0 = HST.get() in
+ i_at_least_is_stable k (Seq.index (i_sel h0 a) k) a;
+ mr_witness a (i_at_least k (Seq.index (i_sel h0 a) k) a)////////////////////////////////////////////////////////////////////////////// +Mapping functions over monotone sequences +//////////////////////////////////////////////////////////////////////////////
+val un_snoc: #a: Type -> s:seq a {Seq.length s > 0} -> Tot(seq a * a)
+let un_snoc #a s =
+ let last = Seq.length s - 1 in
+ Seq.slice s 0 last, Seq.index s lastval map: ('a -> Tot 'b) -> s:seq 'a -> Tot (seq 'b)
+ (decreases (Seq.length s))
+let rec map f s =
+ if Seq.length s = 0 then Seq.empty
+ else let prefix, last = un_snoc s in
+ Seq.snoc (map f prefix) (f last)val map_snoc: f:('a -> Tot 'b) -> s:seq 'a -> a:'a -> Lemma
+ (map f (Seq.snoc s a) == Seq.snoc (map f s) (f a))
+let map_snoc f s a =
+ let prefix, last = un_snoc (Seq.snoc s a) in
+ cut (Seq.equal prefix s)private let op_At s1 s2 = Seq.append s1 s2val map_append: f:('a -> Tot 'b) -> s1:seq 'a -> s2:seq 'a -> Lemma
+ (requires True)
+ (ensures (map f (s1@s2) == (map f s1 @ map f s2)))
+ (decreases (Seq.length s2))
+#reset-options "--z3rlimit 10 --initial_fuel 1 --max_fuel 1 --initial_ifuel 1 --max_ifuel 1"
+let rec map_append f s_1 s_2 =
+ if Seq.length s_2 = 0
+ then (cut (Seq.equal (s_1@s_2) s_1);
+ cut (Seq.equal (map f s_1 @ map f s_2) (map f s_1)))
+ else (let prefix_2, last = un_snoc s_2 in
+ let m_s_1 = map f s_1 in
+ let m_p_2 = map f prefix_2 in
+ let flast = f last in
+ cut (Seq.equal (s_1@s_2) (Seq.snoc (s_1@prefix_2) last)); //map f (s1@s2) = map f (snoc (s1@p) last)
+ map_snoc f (Seq.append s_1 prefix_2) last; // = snoc (map f (s1@p)) (f last)
+ map_append f s_1 prefix_2; // = snoc (map f s_1 @ map f p) (f last)
+ cut (Seq.equal (Seq.snoc (m_s_1 @ m_p_2) flast)
+ (m_s_1 @ Seq.snoc m_p_2 flast)); // = map f s1 @ (snoc (map f p) (f last))
+ map_snoc f prefix_2 last) // = map f s1 @ map f (snoc p last)#reset-options "--z3rlimit 5"val map_length: f:('a -> Tot 'b) -> s1:seq 'a -> Lemma
+ (requires True)
+ (ensures (Seq.length s1 = Seq.length (map f s1)))
+ (decreases (length s1))
+ [SMTPat (Seq.length (map f s1))]
+let rec map_length f s1 =
+ if Seq.length s1 = 0 then ()
+ else let prefix, last = un_snoc s1 in
+ map_length f prefixval map_index: f:('a -> Tot 'b) -> s:seq 'a -> i:nat{i<Seq.length s} -> Lemma
+ (requires True)
+ (ensures (Seq.index (map f s) i == f (Seq.index s i)))
+ (decreases (Seq.length s))
+ [SMTPat (Seq.index (map f s) i)]
+let rec map_index f s i =
+ if i = Seq.length s - 1
+ then ()
+ else let prefix, last = un_snoc s in
+ map_index f prefix i17-01-05 all the stuff above should go to Seq.Properties!
+let map_grows (#a:Type) (#b:Type) (f:a -> Tot b)
+ (s1:seq a) (s3:seq a)
+ : Lemma (grows s1 s3
+ ==> grows (map f s1) (map f s3))
+ = reveal_opaque (`%grows) (grows #a);
+ reveal_opaque (`%grows) (grows #b)let map_prefix (#a:Type) (#b:Type) (#i:rid)
+ (r:m_rref i (seq a) grows)
+ (f:a -> Tot b)
+ (bs:seq b)
+ (h:mem) =
+ grows bs (map f (HS.sel h r))17-01-05 this applies to log_t's defined below.
+let map_prefix_stable (#a:Type) (#b:Type) (#i:rid) (r:m_rref i (seq a) grows) (f:a -> Tot b) (bs:seq b)
+ :Lemma (stable_on_t r (map_prefix r f bs))
+ = reveal_opaque (`%grows) (grows #a);
+ reveal_opaque (`%grows) (grows #b)let map_has_at_index (#a:Type) (#b:Type) (#i:rid)
+ (r:m_rref i (seq a) grows)
+ (f:a -> Tot b)
+ (n:nat) (v:b) (h:mem) =
+ let s = HS.sel h r in
+ n < Seq.length s
+ /\ Seq.index (map f s) n == vlet map_has_at_index_stable (#a:Type) (#b:Type) (#i:rid)
+ (r:m_rref i (seq a) grows)
+ (f:a -> Tot b) (n:nat) (v:b)
+ : Lemma (stable_on_t r (map_has_at_index r f n v))
+ = reveal_opaque (`%grows) (grows #a)////////////////////////////////////////////////////////////////////////////// +Collecting monotone sequences +//////////////////////////////////////////////////////////////////////////////
+yields the concatenation of all sequences returned by f applied to the sequence elements
+val collect: ('a -> Tot (seq 'b)) -> s:seq 'a -> Tot (seq 'b)
+ (decreases (Seq.length s))
+let rec collect f s =
+ if Seq.length s = 0 then Seq.empty
+ else let prefix, last = un_snoc s in
+ Seq.append (collect f prefix) (f last)val collect_snoc: f:('a -> Tot (seq 'b)) -> s:seq 'a -> a:'a -> Lemma
+ (collect f (Seq.snoc s a) == Seq.append (collect f s) (f a))
+let collect_snoc f s a =
+ let prefix, last = un_snoc (Seq.snoc s a) in
+ cut (Seq.equal prefix s)#reset-options "--z3rlimit 20 --initial_fuel 1 --max_fuel 1 --initial_ifuel 1 --max_ifuel 1"let collect_grows (f:'a -> Tot (seq 'b))
+ (s1:seq 'a) (s2:seq 'a)
+ : Lemma (grows s1 s2 ==> grows (collect f s1) (collect f s2))
+ = reveal_opaque (`%grows) (grows #'a);
+ reveal_opaque (`%grows) (grows #'b);
+ let rec collect_grows_aux (f:'a -> Tot (seq 'b)) (s1:seq 'a) (s2:seq 'a)
+ :Lemma (requires (grows s1 s2)) (ensures (grows (collect f s1) (collect f s2)))
+ (decreases (Seq.length s2))
+ = if length s1 = length s2 then assert (Seq.equal s1 s2)
+ else
+ let s2_prefix, s2_last = un_snoc s2 in
+ collect_grows_aux f s1 s2_prefix
+ in
+ Classical.arrow_to_impl #(grows s1 s2) #(grows (collect f s1) (collect f s2)) (fun _ -> collect_grows_aux f s1 s2)let collect_prefix (#a:Type) (#b:Type) (#i:rid)
+ (r:m_rref i (seq a) grows)
+ (f:a -> Tot (seq b))
+ (bs:seq b)
+ (h:mem) =
+ grows bs (collect f (HS.sel h r))let collect_prefix_stable (#a:Type) (#b:Type) (#i:rid) (r:m_rref i (seq a) grows) (f:a -> Tot (seq b)) (bs:seq b)
+ : Lemma (stable_on_t r (collect_prefix r f bs))
+ = let aux : h0:mem -> h1:mem -> Lemma
+ (collect_prefix r f bs h0
+ /\ grows (HS.sel h0 r) (HS.sel h1 r)
+ ==> collect_prefix r f bs h1) =
+ fun h0 h1 ->
+ let s1 = HS.sel h0 r in
+ let s3 = HS.sel h1 r in
+ collect_grows f s1 s3
+ in
+ forall_intro_2 auxlet collect_has_at_index (#a:Type) (#b:Type) (#i:rid)
+ (r:m_rref i (seq a) grows)
+ (f:a -> Tot (seq b))
+ (n:nat) (v:b) (h:mem) =
+ let s = HS.sel h r in
+ n < Seq.length (collect f s)
+ /\ Seq.index (collect f s) n == vlet collect_has_at_index_stable (#a:Type) (#b:Type) (#i:rid)
+ (r:m_rref i (seq a) grows)
+ (f:a -> Tot (seq b)) (n:nat) (v:b)
+ : Lemma (stable_on_t r (collect_has_at_index r f n v))
+ = reveal_opaque (`%grows) (grows #b);
+ Classical.forall_intro_2 (collect_grows f)////////////////////////////////////////////////////////////////////////////// +Monotonic sequence numbers, bounded by the length of a log +////////////////////////////////////////////////////////////////////////////// +17-01-05 the simpler variant, with an historic name; consider using uniform names below.
+type log_t (i:rid) (a:Type) = m_rref i (seq a) growslet increases (x:int) (y:int) = b2t (x <= y)let at_most_log_len (#l:rid) (#a:Type) (x:nat) (log:log_t l a)
+ : mem -> GTot Type0
+ = fun h -> x <= Seq.length (HS.sel h log)Note: we may want int seqn, instead of nat seqn +because the handshake uses an initial value of -1
+type seqn_val (#l:rid) (#a:Type) (i:rid) (log:log_t l a) (max:nat) =
+ (x:nat{x <= max /\ witnessed (at_most_log_len x log)}) //never more than the length of the logtype seqn (#l:rid) (#a:Type) (i:rid) (log:log_t l a) (max:nat) =
+ m_rref i //counter in region i
+ (seqn_val i log max) //never more than the length of the log
+ increases //increasinglet at_most_log_len_stable (#l:rid) (#a:Type) (x:nat) (log:log_t l a)
+ : Lemma (stable_on_t log (at_most_log_len x log))
+ = reveal_opaque (`%grows) (grows #a)let new_seqn (#a:Type) (#l:rid) (#max:nat)
+ (i:rid) (init:nat) (log:log_t l a)
+ : ST (seqn i log max)
+ (requires (fun h ->
+ HST.witnessed (region_contains_pred i) /\
+ init <= max /\
+ init <= Seq.length (HS.sel h log)))
+ (ensures (fun h0 c h1 -> //17-01-05 unify with ralloc_post?
+ modifies_one i h0 h1 /\
+ modifies_ref i Set.empty h0 h1 /\
+ fresh_ref c h0 h1 /\
+ HS.sel h1 c = init /\
+ FStar.Map.contains (HS.get_hmap h1) i))
+ = reveal_opaque (`%grows) (grows #a);
+ recall log; recall_region i;
+ mr_witness log (at_most_log_len init log);
+ ralloc i initlet increment_seqn (#a:Type) (#l:rid) (#max:nat)
+ (#i:rid) (#log:log_t l a) ($c:seqn i log max)
+ : ST unit
+ (requires (fun h ->
+ let log = HS.sel h log in
+ let n = HS.sel h c in
+ n < Seq.length log /\
+ n + 1 <= max))
+ (ensures (fun h0 _ h1 ->
+ modifies_one i h0 h1 /\
+ modifies_ref i (Set.singleton (HS.as_addr c)) h0 h1 /\
+ HS.sel h1 c = HS.sel h0 c + 1))
+ = reveal_opaque (`%grows) (grows #a);
+ recall c; recall log;
+ let n = !c + 1 in
+ mr_witness log (at_most_log_len n log);
+ c := nlet testify_seqn (#a:Type0) (#i:rid) (#l:rid) (#log:log_t l a) (#max:nat) (ctr:seqn i log max)
+ : ST unit
+ (requires (fun h -> True))
+ (ensures (fun h0 _ h1 ->
+ h0==h1 /\
+ at_most_log_len (HS.sel h1 ctr) log h1))
+ = let n = !ctr in
+ testify (at_most_log_len n log)private let test (i:rid) (l:rid) (a:Type0) (log:log_t l a) //(p:(nat -> Type))
+ (r:seqn i log 8) (h:mem)
+ = assert (HS.sel h r = Heap.sel (FStar.Map.sel (HS.get_hmap h) i) (HS.as_ref r))fsdoc: no-summary-found
-fsdoc: no-comment-found
+A module that defines the 'witnessed' logical capability/modality +that is the basis of reasoning about monotonic state in F*, as +discussed in Ahman et al.'s POPL 2018 paper "Recalling a Witness: +Foundations and Applications of Monotonic State". Compared to the +POPL paper, where 'witnessed' and 'witnessed_weakening' were +simply postulated as axioms, this module defines them on top of +a more basic hybrid modal extension of F*'s reasoning logic (see +the corresponding fst file). Also, compared to the POPL paper, this +module proves many additional logical properties of 'witnessed'.
+Witnessed modality
+[@@ remove_unused_type_parameters [0; 1; 2]]
+val witnessed : #state:Type -> rel:preorder state -> p:(state -> Type0) -> Type0Weakening for the witnessed modality
+val lemma_witnessed_weakening :#state:Type
+ -> rel:preorder state
+ -> p:(state -> Type0)
+ -> q:(state -> Type0)
+ -> Lemma (requires (forall s. p s ==> q s))
+ (ensures (witnessed rel p ==> witnessed rel q))Some logical properties of the witnessed modality
+val lemma_witnessed_constant :#state:Type
+ -> rel:preorder state
+ -> p:Type0
+ -> Lemma (witnessed rel (fun _ -> p) <==> p)val lemma_witnessed_nested :#state:Type
+ -> rel:preorder state
+ -> p:(state -> Type0)
+ -> Lemma (witnessed rel (fun _ -> witnessed rel p) <==> witnessed rel p)val lemma_witnessed_and :#state:Type
+ -> rel:preorder state
+ -> p:(state -> Type0)
+ -> q:(state -> Type0)
+ -> Lemma (witnessed rel (fun s -> p s /\ q s) <==> (witnessed rel p /\ witnessed rel q))val lemma_witnessed_or :#state:Type
+ -> rel:preorder state
+ -> p:(state -> Type0)
+ -> q:(state -> Type0)
+ -> Lemma ((witnessed rel p \/ witnessed rel q) ==> witnessed rel (fun s -> p s \/ q s))val lemma_witnessed_impl :#state:Type
+ -> rel:preorder state
+ -> p:(state -> Type0)
+ -> q:(state -> Type0)
+ -> Lemma ((witnessed rel (fun s -> p s ==> q s) /\ witnessed rel p) ==> witnessed rel q)val lemma_witnessed_forall :#state:Type
+ -> #t:Type
+ -> rel:preorder state
+ -> p:(t -> state -> Type0)
+ -> Lemma ((witnessed rel (fun s -> forall x. p x s)) <==> (forall x. witnessed rel (p x)))val lemma_witnessed_exists :#state:Type
+ -> #t:Type
+ -> rel:preorder state
+ -> p:(t -> state -> Type0)
+ -> Lemma ((exists x. witnessed rel (p x)) ==> witnessed rel (fun s -> exists x. p x s))fsdoc: no-summary-found
-fsdoc: no-comment-found
+If we're not doing anything with tuples, +open this module to let '*' be multiplication
+unfold let op_Star = Prims.op_Multiplyfsdoc: no-summary-found
-fsdoc: no-comment-found
+inline_for_extraction
+val isNone: option 'a -> Tot bool
+inline_for_extraction
+let isNone = function
+ | None -> true
+ | Some _ -> falseinline_for_extraction
+val isSome: option 'a -> Tot bool
+inline_for_extraction
+let isSome = function
+ | Some _ -> true
+ | None -> falseinline_for_extraction
+val map: ('a -> ML 'b) -> option 'a -> ML (option 'b)
+inline_for_extraction
+let map f = function
+ | Some x -> Some (f x)
+ | None -> Noneinline_for_extraction
+val mapTot: ('a -> Tot 'b) -> option 'a -> Tot (option 'b)
+inline_for_extraction
+let mapTot f = function
+ | Some x -> Some (f x)
+ | None -> Noneinline_for_extraction
+val get: option 'a -> ML 'a
+let get = function
+ | Some x -> x
+ | None -> failwith "empty option"fsdoc: no-summary-found
-fsdoc: no-comment-found
+val fold: #a:eqtype -> #b:Type -> #f:cmp a -> (a -> b -> Tot b) -> s:ordset a f -> b
+ -> Tot b (decreases (size s))
+let rec fold (#a:eqtype) (#b:Type) #f g s x =
+ if s = empty then x
+ else
+ let Some e = choose s in
+ let a_rest = fold g (remove e s) x in
+ g e a_restlet insert (#a:eqtype) (#f:cmp a) (x:a) (s:ordset a f) = union #a #f (singleton #a #f x) sval union':#a:eqtype -> #f:cmp a -> ordset a f -> ordset a f -> Tot (ordset a f)
+let union' (#a:eqtype) #f s1 s2 = fold (fun e (s:ordset a f) -> insert e s) s1 s2val union_lemma: #a:eqtype -> #f:cmp a -> s1:ordset a f -> s2:ordset a f
+ -> Lemma (requires (True))
+ (ensures (forall x. mem x (union s1 s2) = mem x (union' s1 s2)))
+ (decreases (size s1))
+let rec union_lemma (#a:eqtype) #f s1 s2 =
+ if s1 = empty then ()
+ else
+ union_lemma (remove (Some?.v (choose s1)) s1) s2val union_lemma': #a:eqtype -> #f:cmp a -> s1:ordset a f -> s2:ordset a f
+ -> Lemma (requires (True))
+ (ensures (union s1 s2 = union' s1 s2))
+let union_lemma' (#a:eqtype) #f s1 s2 =
+ union_lemma s1 s2;
+ eq_lemma (union s1 s2) (union' s1 s2)fsdoc: no-summary-found
-fsdoc: no-comment-found
+type order = | Lt | Eq | GtSome derived checks
+val ge : order -> bool
+let ge o = o <> Ltval le : order -> bool
+let le o = o <> Gtval ne : order -> bool
+let ne o = o <> EqJust for completeness and consistency...
+val gt : order -> bool
+let gt o = o = Gtval lt : order -> bool
+let lt o = o = Ltval eq : order -> bool
+let eq o = o = EqLexicographical combination, thunked to be lazy
+val lex : order -> (unit -> order) -> order
+let lex o1 o2 =
+ match o1 with
+ | Lt -> Lt
+ | Eq -> o2 ()
+ | Gt -> Gtval order_from_int : int -> order
+let order_from_int i =
+ if i < 0 then Lt
+ else if i = 0 then Eq
+ else Gtval int_of_order : order -> int
+let int_of_order = function
+ | Lt -> (-1)
+ | Eq -> 0
+ | Gt -> 1val compare_int : int -> int -> order
+let compare_int i j = order_from_int (i - j)val compare_list : ('a -> 'a -> order) -> list 'a -> list 'a -> order
+let rec compare_list f l1 l2 =
+ match l1, l2 with
+ | [], [] -> Eq
+ | [], _ -> Lt
+ | _, [] -> Gt
+ | x::xs, y::ys -> lex (f x y) (fun () -> compare_list f xs ys)val compare_option : ('a -> 'a -> order) -> option 'a -> option 'a -> order
+let compare_option f x y =
+ match x, y with
+ | None , None -> Eq
+ | None , Some _ -> Lt
+ | Some _ , None -> Gt
+ | Some x , Some y -> f x yCopyright 2020 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.
+++This module defines the partial commutative monoid (PCM) algebraic structure, as well as helper +predicates and functions to manipulate PCMs.
+
A symmetric relation
+let symrel (a: Type u#a) = c:(a -> a -> prop) { (forall x y. c x y <==> c y x) }pcm' is a magma, the base for the partial commutative monoid
noeq
+type pcm' (a:Type u#a) = {
+ composable: symrel a;
+ op: x:a -> y:a{composable x y} -> a;
+ one:a
+}The type of a commutativity property
+let lem_commutative (#a: Type u#a) (p:pcm' a) =
+ x:a ->
+ y:a{p.composable x y} ->
+ Lemma (p.op x y == p.op y x)The type of a left-associativity property
+let lem_assoc_l (#a: Type u#a) (p:pcm' a) =
+ x:a ->
+ y:a ->
+ z:a{p.composable y z /\ p.composable x (p.op y z)} ->
+ Lemma (p.composable x y /\
+ p.composable (p.op x y) z /\
+ p.op x (p.op y z) == p.op (p.op x y) z)The type of a right-associativity property
+let lem_assoc_r (#a: Type u#a) (p:pcm' a) =
+ x:a ->
+ y:a ->
+ z:a {p.composable x y /\
+ p.composable (p.op x y) z} ->
+ Lemma
+ (p.composable y z /\
+ p.composable x (p.op y z) /\
+ p.op x (p.op y z) == p.op (p.op x y) z)The type of the property characterizing the unit element of the monoid
+let lem_is_unit (#a: Type u#a) (p:pcm' a) =
+ x:a ->
+ Lemma (p.composable x p.one /\
+ p.op x p.one == x)Main type describing partial commutative monoids
+noeq
+type pcm (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: a -> prop
+}Returns the composable predicate of the PCM
+let composable (#a: Type u#a) (p:pcm a) (x y:a) = p.p.composable x yCalls the operation of the PCM
+let op (#a: Type u#a) (p:pcm a) (x:a) (y:a{composable p x y}) = p.p.op x yTwo elements x and y are compatible with respect to a PCM if their substraction
+is well-defined, e.g. if there exists an element frame such that x * z = y
let compatible (#a: Type u#a) (pcm:pcm a) (x y:a) =
+ (exists (frame:a).
+ composable pcm x frame /\ op pcm frame x == y
+ )Compatibility is reflexive
+let compatible_refl
+ (#a: Type u#a) (pcm:pcm a) (x:a)
+ : Lemma (compatible pcm x x)
+ =
+ pcm.is_unit x;
+ pcm.comm x pcm.p.one;
+ assert (op pcm pcm.p.one x == x)Compatibility is transitive
+let compatible_trans
+ (#a: Type u#a) (pcm:pcm a) (x y z:a)
+ : Lemma (requires (compatible pcm x y /\ compatible pcm y z))
+ (ensures (compatible pcm x z))
+ = Classical.forall_intro_3 pcm.assocHelper function to get access to the existentially quantified frame between two compatible +elements
+let compatible_elim
+ (#a: Type u#a) (pcm:pcm a) (x y:a)
+ (goal: Type)
+ (lemma: (frame: a{composable pcm x frame /\ op pcm frame x == y}) ->
+ Lemma (goal)
+ )
+ : Lemma (requires (compatible pcm x y)) (ensures (goal))
+ =
+ Classical.exists_elim
+ goal #a #(fun frame -> composable pcm x frame /\ op pcm frame x == y)
+ () (fun frame -> lemma frame)let compatible_intro
+ (#a: Type u#a) (pcm:pcm a) (x y:a)
+ (frame: a)
+ : Lemma
+ (requires (composable pcm x frame /\ op pcm frame x == y))
+ (ensures (compatible pcm x y))
+ = ()Two elements are joinable when they can evolve to a common point.
+let joinable #a (p:pcm a) (x y : a) : prop =
+ exists z. compatible p x z /\ compatible p y zlet frame_compatible #a (p:pcm a) (x:FStar.Ghost.erased a) (v y:a) =
+ (forall (frame:a). {:pattern (composable p x frame)}
+ composable p x frame /\
+ v == op p x frame ==>
+ composable p y frame /\
+ v == op p y frame)type frame_preserving_upd (#a:Type u#a) (p:pcm a) (x y:a) =
+ v:a{
+ p.refine v /\
+ compatible p x v
+ } ->
+ v_new:a{
+ p.refine 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 (#a: Type u#a) (pcm:pcm a) (x y: a) =
+ (forall frame. composable pcm frame x ==> composable pcm frame y) /\
+ (forall frame.{:pattern (composable pcm frame x)} composable pcm frame x ==> op pcm frame y == y)let frame_preserving_val_to_fp_upd (#a:Type u#a) (p:pcm a)
+ (x:Ghost.erased a) (v:a{frame_preserving p x v /\ p.refine v})
+ : frame_preserving_upd p x v
+ = Classical.forall_intro (p.comm v);
+ fun _ -> vThe PCM p is exclusive to element x if the only element composable with x is p.one
let exclusive (#a:Type u#a) (p:pcm a) (x:a) =
+ forall (frame:a). composable p x frame ==> frame == p.p.oneA mutation from x to p.one is frame preserving if p is exclusive to x
let exclusive_is_frame_preserving (#a: Type u#a) (p:pcm a) (x:a)
+ : Lemma (requires exclusive p x)
+ (ensures frame_preserving p x p.p.one)
+ = p.is_unit x;
+ p.is_unit p.p.oneSome sanity checks on the definition of frame preserving updates
+let no_op_is_frame_preserving (#a:Type u#a) (p:pcm a)
+ (x:a)
+ : frame_preserving_upd p x x
+ = fun v -> vlet compose_frame_preserving_updates (#a:Type u#a) (p:pcm a)
+ (x y z:a)
+ (f:frame_preserving_upd p x y)
+ (g:frame_preserving_upd p y z)
+ : frame_preserving_upd p x z
+ = fun v -> g (f v)let frame_preserving_subframe (#a:Type u#a) (p:pcm a) (x y:a)
+ (subframe:a{composable p x subframe /\ composable p y subframe})
+ (f:frame_preserving_upd p x y)
+ : frame_preserving_upd p (op p x subframe) (op p y subframe)
+ = fun v ->
+ compatible_elim p (op p x subframe) v (compatible p x v) (fun frame ->
+ p.comm x subframe;
+ p.assoc frame subframe x);
+ let w = f v in
+ let aux (frame: a{composable p (op p x subframe) frame}):
+ Lemma (composable p (op p y subframe) frame /\
+ (op p (op p x subframe) frame == v ==> op p (op p y subframe) frame == w))
+ [SMTPat (composable p (op p y subframe) frame)]
+ = p.assoc_r x subframe frame;
+ assert (composable p x (op p subframe frame));
+ assert (composable p y (op p subframe frame));
+ p.assoc y subframe frame
+ in
+ compatible_elim p (op p x subframe) v (compatible p (op p y subframe) w) (fun frame ->
+ aux frame;
+ p.comm frame (op p x subframe);
+ p.comm (op p y subframe) frame);
+ wfsdoc: no-summary-found
-fsdoc: no-comment-found
+This is a file from the core library, dependencies must be explicit
+++This module is implicitly opened in the scope of all other modules.
+It provides several basic types in F* that enjoy some special +status in extraction. For instance, the tuple type below is +compiled to OCaml's tuple type, rather than to a F*-defined +inductive type. See ulib/ml/FStar_Pervasives_Native.ml
+
option a represents either Some a-value or a non-informative None.
type option (a: Type) =
+ | None : option a
+ | Some : v: a -> option a++Aside from special support in extraction, the tuple types have +special syntax in F*.
+For instance, rather than
+tupleN a1 ... aN, +we usually writea1 & ... & aNora1 * ... * aN.The latter notation is more common for those coming to F* from +OCaml or F#. However, the
+*also clashes with the multiplication +operator on integers define in FStar.Mul. For this reason, we now +prefer to use the¬ation, though there are still many uses +of*remaining.Tuple values are introduced using as
+a1, ..., an, rather than +MktupleN a1 ... aN.We define tuples up to a fixed arity of 14. We have considered +splitting this module into 14 different modules, one for each +tuple type rather than eagerly including 14-tuples in the +dependence graph of all programs.
+
Pairs: tuple2 a b is can be written either as a * b, for
+notation compatible with OCaml's. Or, better, as a & b.
type tuple2 'a 'b = | Mktuple2 : _1: 'a -> _2: 'b -> tuple2 'a 'bThe fst and snd projections on pairs are very common
+let fst (x: tuple2 'a 'b) : 'a = Mktuple2?._1 x
+let snd (x: tuple2 'a 'b) : 'b = Mktuple2?._2 xtype tuple3 'a 'b 'c = | Mktuple3 : _1: 'a -> _2: 'b -> _3: 'c -> tuple3 'a 'b 'ctype tuple4 'a 'b 'c 'd = | Mktuple4 : _1: 'a -> _2: 'b -> _3: 'c -> _4: 'd -> tuple4 'a 'b 'c 'dtype tuple5 'a 'b 'c 'd 'e =
+ | Mktuple5 : _1: 'a -> _2: 'b -> _3: 'c -> _4: 'd -> _5: 'e -> tuple5 'a 'b 'c 'd 'etype tuple6 'a 'b 'c 'd 'e 'f =
+ | Mktuple6 : _1: 'a -> _2: 'b -> _3: 'c -> _4: 'd -> _5: 'e -> _6: 'f -> tuple6 'a 'b 'c 'd 'e 'ftype tuple7 'a 'b 'c 'd 'e 'f 'g =
+ | Mktuple7 : _1: 'a -> _2: 'b -> _3: 'c -> _4: 'd -> _5: 'e -> _6: 'f -> _7: 'g
+ -> tuple7 'a 'b 'c 'd 'e 'f 'gtype tuple8 'a 'b 'c 'd 'e 'f 'g 'h =
+ | Mktuple8 : _1: 'a -> _2: 'b -> _3: 'c -> _4: 'd -> _5: 'e -> _6: 'f -> _7: 'g -> _8: 'h
+ -> tuple8 'a 'b 'c 'd 'e 'f 'g 'htype tuple9 'a 'b 'c 'd 'e 'f 'g 'h 'i =
+ | Mktuple9 :
+ _1: 'a ->
+ _2: 'b ->
+ _3: 'c ->
+ _4: 'd ->
+ _5: 'e ->
+ _6: 'f ->
+ _7: 'g ->
+ _8: 'h ->
+ _9: 'i
+ -> tuple9 'a 'b 'c 'd 'e 'f 'g 'h 'itype tuple10 'a 'b 'c 'd 'e 'f 'g 'h 'i 'j =
+ | Mktuple10 :
+ _1: 'a ->
+ _2: 'b ->
+ _3: 'c ->
+ _4: 'd ->
+ _5: 'e ->
+ _6: 'f ->
+ _7: 'g ->
+ _8: 'h ->
+ _9: 'i ->
+ _10: 'j
+ -> tuple10 'a 'b 'c 'd 'e 'f 'g 'h 'i 'jtype tuple11 'a 'b 'c 'd 'e 'f 'g 'h 'i 'j 'k =
+ | Mktuple11 :
+ _1: 'a ->
+ _2: 'b ->
+ _3: 'c ->
+ _4: 'd ->
+ _5: 'e ->
+ _6: 'f ->
+ _7: 'g ->
+ _8: 'h ->
+ _9: 'i ->
+ _10: 'j ->
+ _11: 'k
+ -> tuple11 'a 'b 'c 'd 'e 'f 'g 'h 'i 'j 'ktype tuple12 'a 'b 'c 'd 'e 'f 'g 'h 'i 'j 'k 'l =
+ | Mktuple12 :
+ _1: 'a ->
+ _2: 'b ->
+ _3: 'c ->
+ _4: 'd ->
+ _5: 'e ->
+ _6: 'f ->
+ _7: 'g ->
+ _8: 'h ->
+ _9: 'i ->
+ _10: 'j ->
+ _11: 'k ->
+ _12: 'l
+ -> tuple12 'a 'b 'c 'd 'e 'f 'g 'h 'i 'j 'k 'ltype tuple13 'a 'b 'c 'd 'e 'f 'g 'h 'i 'j 'k 'l 'm =
+ | Mktuple13 :
+ _1: 'a ->
+ _2: 'b ->
+ _3: 'c ->
+ _4: 'd ->
+ _5: 'e ->
+ _6: 'f ->
+ _7: 'g ->
+ _8: 'h ->
+ _9: 'i ->
+ _10: 'j ->
+ _11: 'k ->
+ _12: 'l ->
+ _13: 'm
+ -> tuple13 'a 'b 'c 'd 'e 'f 'g 'h 'i 'j 'k 'l 'mtype tuple14 'a 'b 'c 'd 'e 'f 'g 'h 'i 'j 'k 'l 'm 'n =
+ | Mktuple14 :
+ _1: 'a ->
+ _2: 'b ->
+ _3: 'c ->
+ _4: 'd ->
+ _5: 'e ->
+ _6: 'f ->
+ _7: 'g ->
+ _8: 'h ->
+ _9: 'i ->
+ _10: 'j ->
+ _11: 'k ->
+ _12: 'l ->
+ _13: 'm ->
+ _14: 'n
+ -> tuple14 'a 'b 'c 'd 'e 'f 'g 'h 'i 'j 'k 'l 'm 'nfsdoc: no-summary-found
-fsdoc: no-comment-found
-let ((expect_failure (errs:list int)):unit):()When attached a top-level definition, the typechecker will succeed * if and only if checking the definition results in an error. The * error number list is actually OPTIONAL. If present, it will be * checked that the definition raises exactly those errors in the * specified multiplicity, but order does not matter.
-let ((expect_lax_failure (errs:list int)):unit):()When --lax is present, we the previous attribute since some definitions * only fail when verification is turned on. With this attribute, one can ensure * that a definition fails while lax-checking too. Same semantics as above, * but lax mode will be turned on for the definition.
-let (tcdecltime:unit):()Print the time it took to typecheck a top-level definition
-let (assume_strictly_positive:unit):()type t = ref (option t)let (unifier_hint_injective:unit):()This is a file from the core library, dependencies must be explicit
t marked with this attributet a1..an =?= t b1..bnai =?= bi fori, without trying to unfold the definition of t.let ((strict_on_arguments (x:list int)):unit):()++This module is implicitly opened in the scope of all other +modules.
+It provides several basic definitions in F* that are common to +most programs. Broadly, these include:
-
-- This attribute is used to control the evaluation order
-- and unfolding strategy for certain definitions.
- -- In particular, given
-- [@(strict_on_arguments [1;2])]
-- let f x0 (x1:list x0) (x1:option x0) = e
- -- An application
-f e0 e1 e2is reduced by the normalizer by:- - -
-
- evaluating e0 ~>* v0, e1 ~>* v1, e2 ~>* v2
-- 2 a.
-- -
If, according to the positional arguments [1;2],- -
if v1 and v2 have constant head symbols- -
(e.g., v1 = Cons _ _ _, and v2 = None _)- -
then `f` is unfolded to `e` and reduced as- - -
e[v0/x0][v1/x1][v2/x2]- 2 b.
- -- Otherwise,
-fis not unfolded and the term isf e0 e1 e2- reduces to
- +f v0 v1 v2.- +
+Utility types and functions, like
+id,either, dependent +tuples, etc.- +
+Utility effect definitions, including
+DIVfor divergence, +EXNof exceptions,STATE_ha template for state, and (the +poorly named)ALL_hwhich combines them all.- +
+Some utilities to control proofs, e.g., inversion of inductive +type definitions.
+- +
Built-in attributes that can be used to decorate definitions and +trigger various kinds of special treatments for those +definitions.
++let (erasable:unit):()
val remove_unused_type_parameters : list int -> Tot unitThis attribute is used to decorate signatures in interfaces for +type abbreviations, indicating that the 0-based positional +parameters are unused in the definition and should be eliminated +for extraction.
+This is important particularly for use with F# extraction, since +F# does not accept type abbreviations with unused type parameters.
+See tests/bug-reports/RemoveUnusedTyparsIFace.A.fsti
+Values of type pattern are used to tag Lemmas with SMT
+quantifier triggers
type pattern : Type0 = unitThe concrete syntax SMTPat desugars to smt_pat
val smt_pat (#a: Type) (x: a) : Tot patternThe concrete syntax SMTPatOr desugars to smt_pat_or. This is
+used to represent a disjunction of conjunctions of patterns.
val smt_pat_or (x: list (list pattern)) : Tot patternNote, the typing discipline and syntax of patterns is laxer than
+it should be. Patterns like SMTPatOr SMTPatOr `...``` are
+expressible, but unsupported by F*
TODO: We should tighten this up, perhaps just reusing the +attribute mechanism for patterns.
+Lemma is a very widely used effect abbreviation.
effect Lemma (a: Type) (pre: Type) (post: (squash pre -> Type)) (pats: list pattern) =
+ Pure a pre (fun r -> post ())It stands for a unit-returning Ghost computation, whose main
+value is its logical payload in proving an implication between its
+pre- and postcondition.
Lemma is desugared specially. The valid forms are:
Lemma (ensures post)
+Lemma post SMTPat ...
+Lemma (ensures post) SMTPat ...
+Lemma (ensures post) (decreases d)
+Lemma (ensures post) (decreases d) SMTPat ...
+Lemma (requires pre) (ensures post) (decreases d)
+Lemma (requires pre) (ensures post) SMTPat ...
+Lemma (requires pre) (ensures post) (decreases d) SMTPat ...
and
+Lemma post (== Lemma (ensures post))
+the squash argument on the postcondition allows to assume the +precondition for the well-formedness of the postcondition.
+In the default mode of operation, all proofs in a verification
+condition are bundled into a single SMT query. Sub-terms marked
+with the spinoff below are the exception: each of them is
+spawned off into a separate SMT query
val spinoff (p: Type0) : Type0Logically equivalent to assert, but spins off separate query
+val assert_spinoff (p: Type) : Pure unit (requires (spinoff (squash p))) (ensures (fun x -> p))The polymorphic identity function
+unfold
+let id (#a: Type) (x: a) : a = xTrivial postconditions for the PURE effect
unfold
+let trivial_pure_post (a: Type) : pure_post a = fun _ -> TrueSometimes it is convenient to explicit introduce nullary symbols +into the ambient context, so that SMT can appeal to their definitions +even when they are no mentioned explicitly in the program, e.g., when +needed for triggers.
+[@@ remove_unused_type_parameters [0; 1;]]
+val ambient (#a: Type) (x: a) : Type0Use intro_ambient t for that.
+See, e.g., LowStar.Monotonic.Buffer.fst and its usage there for loc_none
cf. ambient, above
val intro_ambient (#a: Type) (x: a) : Tot (squash (ambient x))++Controlling normalization
+
In any invocation of the F* normalizer, every occurrence of
+normalize_term e is reduced to the full normal for of e.
val normalize_term (#a: Type) (x: a) : Tot aIn any invocation of the F* normalizer, every occurrence of
+normalize e is reduced to the full normal for of e.
val normalize (a: Type0) : Type0Value of norm_step are used to enable specific normalization
+steps, controlling how the normalizer reduces terms.
val norm_step : Type0Logical simplification, e.g., P /\ True ~> P
val simplify : norm_stepWeak reduction: Do not reduce under binders
+val weak : norm_stepHead normal form
+val hnf : norm_stepReduce primitive operators, e.g., 1 + 1 ~> 2
val primops : norm_stepUnfold all non-recursive definitions
+val delta : norm_stepUnroll recursive calls
+val zeta : norm_stepNote: Since F*'s termination check is semantic rather than +syntactically structural, recursive calls in inconsistent contexts, +or recursive evaluation of open terms can diverge.
+When asking for the zeta step, F* implements a heuristic to
+disable zeta when reducing terms beneath a blocked match. This
+helps prevent some trivial looping behavior. However, it also
+means that with zeta alone, your term may not reduce as much as
+you might want. See zeta_full for that.
Unroll recursive calls
+val zeta_full : norm_stepUnlike zeta, zeta_full has no looping prevention
+heuristics. F* will try to unroll recursive functions as much as
+it can, potentially looping. Use with care.
Note, zeta_full implies zeta.
+See tests/micro-benchmarks/ReduceRecUnderMatch.fst for an example.
Reduce case analysis (i.e., match)
+val iota : norm_stepUse normalization-by-evaluation, instead of interpretation (experimental)
+val nbe : norm_stepReify effectful definitions into their representations
+val reify_ : norm_stepUnlike delta, unfold definitions for only the names in the given
+list. Each string is a fully qualified name like A.M.f
val delta_only (s: list string) : Tot norm_stepUnfold definitions for only the names in the given list, but +unfold each definition encountered after unfolding as well.
+val delta_fully (s: list string) : Tot norm_stepFor example, given
+let f0 = 0
+let f1 = f0 + 1norm delta_only %f1 f1will reduce tof0 + 1. norm delta_fully ``%f1`` f1 will reduce to 0 + 1.
Each string is a fully qualified name like A.M.f, typically
+constructed using a quotation, as in the example above.
Rather than mention a symbol to unfold by name, it can be +convenient to tag a collection of related symbols with a common +attribute and then to ask the normalizer to reduce them all.
+val delta_attr (s: list string) : Tot norm_stepFor example, given:
+irreducible let my_attr = ()
+
+`@@my_attr`
+let f0 = 0
+
+`@@my_attr`
+let f1 = f0 + 1FStarnorm [delta_attr [`%my_attr]] f1
will reduce to 0 + 1.
For example, given:
+val delta_qualifier (s: list string) : Tot norm_stepunfold
+let f0 = 0
+
+inline_for_extraction
+let f1 = f0 + 1
+FStarnorm [delta_qualifier ["unfold"; "inline_for_extraction"]] f1
will reduce to 0 + 1.
This step removes the some internal meta nodes during normalization
+val unmeta : norm_stepIn most cases you shouldn't need to use this step explicitly
+norm s e requests normalization of e with the reduction steps
+s.
val norm (s: list norm_step) (#a: Type) (x: a) : Tot aassert_norm p reduces p as much as possible and then asks the
+SMT solver to prove the reduct, concluding p
val assert_norm (p: Type) : Pure unit (requires (normalize p)) (ensures (fun _ -> p))Sometimes it is convenient to introduce an equation between a term +and its normal form in the context.
+val normalize_term_spec (#a: Type) (x: a) : Lemma (normalize_term #a x == x)Like normalize_term_spec, but specialized to Type0
val normalize_spec (a: Type0) : Lemma (normalize a == a)Like normalize_term_spec, but with specific normalization steps
val norm_spec (s: list norm_step) (#a: Type) (x: a) : Lemma (norm s #a x == x)Use the following to expose an "opaque_to_smt" definition to the
+solver as: reveal_opaque (%defn) defn`
let reveal_opaque (s: string) = norm_spec [delta_only [s]]Wrappers over pure wp combinators that return a pure_wp type +(with monotonicity refinement)
+unfold
+let pure_return (a:Type) (x:a) : pure_wp a =
+ reveal_opaque (`%pure_wp_monotonic) pure_wp_monotonic;
+ pure_return0 a xunfold
+let pure_bind_wp (a b:Type) (wp1:pure_wp a) (wp2:(a -> Tot (pure_wp b))) : Tot (pure_wp b) =
+ reveal_opaque (`%pure_wp_monotonic) pure_wp_monotonic;
+ pure_bind_wp0 a b wp1 wp2unfold
+let pure_if_then_else (a p:Type) (wp_then wp_else:pure_wp a) : Tot (pure_wp a) =
+ reveal_opaque (`%pure_wp_monotonic) pure_wp_monotonic;
+ pure_if_then_else0 a p wp_then wp_elseunfold
+let pure_ite_wp (a:Type) (wp:pure_wp a) : Tot (pure_wp a) =
+ reveal_opaque (`%pure_wp_monotonic) pure_wp_monotonic;
+ pure_ite_wp0 a wpunfold
+let pure_close_wp (a b:Type) (wp:b -> Tot (pure_wp a)) : Tot (pure_wp a) =
+ reveal_opaque (`%pure_wp_monotonic) pure_wp_monotonic;
+ pure_close_wp0 a b wpunfold
+let pure_null_wp (a:Type) : Tot (pure_wp a) =
+ reveal_opaque (`%pure_wp_monotonic) pure_wp_monotonic;
+ pure_null_wp0 a[@@ "opaque_to_smt"]
+unfold
+let pure_assert_wp (p:Type) : Tot (pure_wp unit) =
+ reveal_opaque (`%pure_wp_monotonic) pure_wp_monotonic;
+ pure_assert_wp0 p[@@ "opaque_to_smt"]
+unfold
+let pure_assume_wp (p:Type) : Tot (pure_wp unit) =
+ reveal_opaque (`%pure_wp_monotonic) pure_wp_monotonic;
+ pure_assume_wp0 p++The
+DIVeffect for divergent computationsThe wp-calculus for
+DIVis same as that ofPURE
The effect of divergence: from a specificational perspective it is
+identical to PURE, however the specs are given a partial
+correctness interpretation. Computations with the DIV effect may
+not terminate.
new_effect {
+ DIV : a:Type -> wp:pure_wp a -> Effect
+ with
+ return_wp = pure_return
+ ; bind_wp = pure_bind_wp
+ ; if_then_else = pure_if_then_else
+ ; ite_wp = pure_ite_wp
+ ; stronger = pure_stronger
+ ; close_wp = pure_close_wp
+ ; trivial = pure_trivial
+}PURE computations can be silently promoted for use in a DIV context
sub_effect PURE ~> DIV { lift_wp = purewp_id }Div is the Hoare-style counterpart of the wp-indexed DIV
unfold
+let div_hoare_to_wp (#a:Type) (#pre:pure_pre) (post:pure_post' a pre) : Tot (pure_wp a) =
+ reveal_opaque (`%pure_wp_monotonic) pure_wp_monotonic;
+ fun (p:pure_post a) -> pre /\ (forall a. post a ==> p a)effect Div (a: Type) (pre: pure_pre) (post: pure_post' a pre) =
+ DIV a (div_hoare_to_wp post)Dv is the instance of DIV with trivial pre- and postconditions
effect Dv (a: Type) = DIV a (pure_null_wp a)We use the EXT effect to underspecify external system calls
+as being impure but having no observable effect on the state
effect EXT (a: Type) = Dv a++The
+STATE_heffect template for stateful computations, generic +in the type of the state.Note,
+STATE_his itself not a computation type in F*, since it +is parameterized by the type of heap. However, instantiations of +STATE_hwith specific types of the heap are computation +types. See, e.g.,FStar.STfor such instantiations.Weakest preconditions for stateful computations transform +
+st_post_hpostconditions tost_pre_hpreconditions. Both are +parametric in the type of the state, here denoted by the +heap:Typevariable.
Preconditions are predicates on the heap
let st_pre_h (heap: Type) = heap -> GTot Type0Postconditions relate a-typed results to the final heap, here
+refined by some pure proposition pre, typically instantiated to
+the precondition applied to the initial heap
let st_post_h' (heap a pre: Type) = a -> _: heap{pre} -> GTot Type0Postconditions without refinements
+let st_post_h (heap a: Type) = st_post_h' heap a TrueThe type of the main WP-transformer for stateful comptuations
+let st_wp_h (heap a: Type) = st_post_h heap a -> Tot (st_pre_h heap)Returning a value does not transform the state
+unfold
+let st_return (heap a: Type) (x: a) (p: st_post_h heap a) = p xSequential composition of stateful WPs
+unfold
+let st_bind_wp
+ (heap: Type)
+ (a b: Type)
+ (wp1: st_wp_h heap a)
+ (wp2: (a -> GTot (st_wp_h heap b)))
+ (p: st_post_h heap b)
+ (h0: heap)
+ = wp1 (fun a h1 -> wp2 a p h1) h0Branching for stateful WPs
+unfold
+let st_if_then_else
+ (heap a p: Type)
+ (wp_then wp_else: st_wp_h heap a)
+ (post: st_post_h heap a)
+ (h0: heap)
+ = wp_then post h0 /\ (~p ==> wp_else post h0)As with PURE the wp combinator names the postcondition as
+k to avoid duplicating it.
unfold
+let st_ite_wp (heap a: Type) (wp: st_wp_h heap a) (post: st_post_h heap a) (h0: heap) =
+ forall (k: st_post_h heap a).
+ (forall (x: a) (h: heap). {:pattern (guard_free (k x h))} post x h ==> k x h) ==> wp k h0Subsumption for stateful WPs
+unfold
+let st_stronger (heap a: Type) (wp1 wp2: st_wp_h heap a) =
+ (forall (p: st_post_h heap a) (h: heap). wp1 p h ==> wp2 p h)Closing the scope of a binder within a stateful WP
+unfold
+let st_close_wp (heap a b: Type) (wp: (b -> GTot (st_wp_h heap a))) (p: st_post_h heap a) (h: heap) =
+ (forall (b: b). wp b p h)Applying a stateful WP to a trivial postcondition
+unfold
+let st_trivial (heap a: Type) (wp: st_wp_h heap a) = (forall h0. wp (fun r h1 -> True) h0)Introducing a new effect template STATE_h
new_effect {
+ STATE_h (heap: Type) : result: Type -> wp: st_wp_h heap result -> Effect
+ with
+ return_wp = st_return heap
+ ; bind_wp = st_bind_wp heap
+ ; if_then_else = st_if_then_else heap
+ ; ite_wp = st_ite_wp heap
+ ; stronger = st_stronger heap
+ ; close_wp = st_close_wp heap
+ ; trivial = st_trivial heap
+}++The
+EXNeffect for computations that may raise exceptions or +fatal errorsWeakest preconditions for stateful computations transform +
+ex_postpostconditions (predicates onresults) toex_pre+precondition propositions.
Normal results are represented using V x.
+Handleable exceptions are represented E e.
+Fatal errors are Err msg.
noeq
+type result (a: Type) =
+ | V : v: a -> result a
+ | E : e: exn -> result a
+ | Err : msg: string -> result aExceptional preconditions are just propositions
+let ex_pre = Type0Postconditions on results refined by a precondition
+let ex_post' (a pre: Type) = _: result a {pre} -> GTot Type0Postconditions on results
+let ex_post (a: Type) = ex_post' a TrueExceptions WP-predicate transformers
+let ex_wp (a: Type) = ex_post a -> GTot ex_preReturning a value x normally promotes it to the V x result
unfold
+let ex_return (a: Type) (x: a) (p: ex_post a) : GTot Type0 = p (V x)Sequential composition of exception-raising code requires case analysing +the result of the first computation before "running" the second one
+unfold
+let ex_bind_wp (a b: Type) (wp1: ex_wp a) (wp2: (a -> GTot (ex_wp b))) (p: ex_post b)
+ : GTot Type0 =
+ forall (k: ex_post b).
+ (forall (rb: result b). {:pattern (guard_free (k rb))} p rb ==> k rb) ==>
+ (wp1 (function
+ | V ra1 -> wp2 ra1 k
+ | E e -> k (E e)
+ | Err m -> k (Err m)))As for other effects, branching in ex_wp appears in two forms.
+First, a simple case analysis on p
unfold
+let ex_if_then_else (a p: Type) (wp_then wp_else: ex_wp a) (post: ex_post a) =
+ wp_then post /\ (~p ==> wp_else post)Naming continuations for use with branching
+unfold
+let ex_ite_wp (a: Type) (wp: ex_wp a) (post: ex_post a) =
+ forall (k: ex_post a).
+ (forall (rb: result a). {:pattern (guard_free (k rb))} post rb ==> k rb) ==> wp kSubsumption for exceptional WPs
+unfold
+let ex_stronger (a: Type) (wp1 wp2: ex_wp a) = (forall (p: ex_post a). wp1 p ==> wp2 p)Closing the scope of a binder for exceptional WPs
+unfold
+let ex_close_wp (a b: Type) (wp: (b -> GTot (ex_wp a))) (p: ex_post a) = (forall (b: b). wp b p)Applying a computation with a trivial poscondition
+unfold
+let ex_trivial (a: Type) (wp: ex_wp a) = wp (fun r -> True)Introduce a new effect for EXN
new_effect {
+ EXN : result: Type -> wp: ex_wp result -> Effect
+ with
+ return_wp = ex_return
+ ; bind_wp = ex_bind_wp
+ ; if_then_else = ex_if_then_else
+ ; ite_wp = ex_ite_wp
+ ; stronger = ex_stronger
+ ; close_wp = ex_close_wp
+ ; trivial = ex_trivial
+}A Hoare-style abbreviation for EXN
+effect Exn (a: Type) (pre: ex_pre) (post: ex_post' a pre) =
+ EXN a (fun (p: ex_post a) -> pre /\ (forall (r: result a). post r ==> p r))We include divergence in exceptions.
+unfold
+let lift_div_exn (a: Type) (wp: pure_wp a) (p: ex_post a) = wp (fun a -> p (V a))
+sub_effect DIV ~> EXN { lift_wp = lift_div_exn }NOTE: BE WARNED, CODE IN THE EXN EFFECT IS ONLY CHECKED FOR
+PARTIAL CORRECTNESS
A variant of Exn with trivial pre- and postconditions
effect Ex (a: Type) = Exn a True (fun v -> True)++The
+ALL_heffect template for computations that may diverge, +raise exceptions or fatal errors, and uses a generic state.Note, this effect is poorly named, particularly as F* has since +gained many more user-defined effect. We no longer have an effect +that includes all others.
+We might rename this in the future to something like
+StExnDiv_h.We layer state on top of exceptions, meaning that raising an +exception does not discard the state.
+As with
+STATE_h,ALL_his not a computation type, though its +instantiation with a specific type ofheap(in FStar.All) is.
all_pre_h is a predicate on the initial state
let all_pre_h (h: Type) = h -> GTot Type0Postconditions relate results to final heaps refined by a precondition
let all_post_h' (h a pre: Type) = result a -> _: h{pre} -> GTot Type0A variant of all_post_h' without the precondition refinement
let all_post_h (h a: Type) = all_post_h' h a TrueWP predicate transformers for the All_h effect template
let all_wp_h (h a: Type) = all_post_h h a -> Tot (all_pre_h h)Returning a value x normally promotes it to the V x result
+without touching the heap
unfold
+let all_return (heap a: Type) (x: a) (p: all_post_h heap a) = p (V x)Sequential composition for ALL_h is like EXN: case analysis of
+the exceptional result before "running" the continuation
unfold
+let all_bind_wp
+ (heap: Type)
+ (a b: Type)
+ (wp1: all_wp_h heap a)
+ (wp2: (a -> GTot (all_wp_h heap b)))
+ (p: all_post_h heap b)
+ (h0: heap)
+ : GTot Type0 =
+ wp1 (fun ra h1 ->
+ (match ra with
+ | V v -> wp2 v p h1
+ | E e -> p (E e) h1
+ | Err msg -> p (Err msg) h1))
+ h0Case analysis in ALL_h
unfold
+let all_if_then_else
+ (heap a p: Type)
+ (wp_then wp_else: all_wp_h heap a)
+ (post: all_post_h heap a)
+ (h0: heap)
+ = wp_then post h0 /\ (~p ==> wp_else post h0)Naming postcondition for better sharing in ALL_h
unfold
+let all_ite_wp (heap a: Type) (wp: all_wp_h heap a) (post: all_post_h heap a) (h0: heap) =
+ forall (k: all_post_h heap a).
+ (forall (x: result a) (h: heap). {:pattern (guard_free (k x h))} post x h ==> k x h) ==> wp k h0Subsumption in ALL_h
unfold
+let all_stronger (heap a: Type) (wp1 wp2: all_wp_h heap a) =
+ (forall (p: all_post_h heap a) (h: heap). wp1 p h ==> wp2 p h)Closing a binder in the scope of an ALL_h wp
unfold
+let all_close_wp
+ (heap a b: Type)
+ (wp: (b -> GTot (all_wp_h heap a)))
+ (p: all_post_h heap a)
+ (h: heap)
+ = (forall (b: b). wp b p h)Applying an ALL_h wp to a trivial postcondition
unfold
+let all_trivial (heap a: Type) (wp: all_wp_h heap a) = (forall (h0: heap). wp (fun r h1 -> True) h0)Introducing the ALL_h effect template
new_effect {
+ ALL_h (heap: Type) : a: Type -> wp: all_wp_h heap a -> Effect
+ with
+ return_wp = all_return heap
+ ; bind_wp = all_bind_wp heap
+ ; if_then_else = all_if_then_else heap
+ ; ite_wp = all_ite_wp heap
+ ; stronger = all_stronger heap
+ ; close_wp = all_close_wp heap
+ ; trivial = all_trivial heap
+}Controlling inversions of inductive type
+[@@ remove_unused_type_parameters [0]]
+val inversion (a: Type) : Type0Given a value of an inductive type v:t, where t = A | B, the SMT
+solver can only prove that v=A \/ v=B by inverting t. This
+inversion is controlled by the ifuel setting, which usually limits
+the recursion depth of the number of such inversions that the solver
+can perform.
The inversion predicate below is a way to circumvent the
+ifuel-based restrictions on inversion depth. In particular, if the
+inversion t is available in the SMT solver's context, it is free to
+invert t infinitely, regardless of the ifuel setting.
Be careful using this, since it explicitly subverts the ifuel
+setting. If used unwisely, this can lead to very poor SMT solver
+performance.
To introduce inversion t in the SMT solver's context, call
+allow_inverson t.
val allow_inversion (a: Type) : Pure unit (requires True) (ensures (fun x -> inversion a))Since the option type is so common, we always allow inverting
+options, regardless of ifuel
val invertOption (a: Type)
+ : Lemma (requires True) (ensures (forall (x: option a). None? x \/ Some? x)) [SMTPat (option a)]Values of type a or type b
type either a b =
+ | Inl : v: a -> either a b
+ | Inr : v: b -> either a bProjections for the components of a dependent pair
+let dfst (#a: Type) (#b: a -> GTot Type) (t: dtuple2 a b)
+ : Tot a
+ = Mkdtuple2?._1 tlet dsnd (#a: Type) (#b: a -> GTot Type) (t: dtuple2 a b)
+ : Tot (b (Mkdtuple2?._1 t))
+ = Mkdtuple2?._2 tDependent triples, with sugar x:a & y:b x & c x y
unopteq
+type dtuple3 (a: Type) (b: (a -> GTot Type)) (c: (x: a -> b x -> GTot Type)) =
+ | Mkdtuple3 : _1: a -> _2: b _1 -> _3: c _1 _2 -> dtuple3 a b cDependent quadruples, with sugar x:a & y:b x & z:c x y & d x y z
unopteq
+type dtuple4
+ (a: Type) (b: (x: a -> GTot Type)) (c: (x: a -> b x -> GTot Type))
+ (d: (x: a -> y: b x -> z: c x y -> GTot Type))
+ = | Mkdtuple4 : _1: a -> _2: b _1 -> _3: c _1 _2 -> _4: d _1 _2 _3 -> dtuple4 a b c dExplicitly discarding a value
+let ignore (#a: Type) (x: a) : Tot unit = ()In a context where false is provable, you can prove that any
+type a is inhabited.
val false_elim (#a: Type) (u: unit{False}) : Tot aThere are many proofs of this fact in F*. Here, in the implementation, we build an
+infinitely looping function, since the termination check succeeds
+in a False context.
++Attributes:
+An attribute is any F* term.
+Attributes are desugared and checked for being well-scoped. But, +they are not type-checked.
+It is associated with a definition using the
+@@attribute+notation, just preceding the definition.
We collect several internal ocaml attributes into a single +inductive type.
+type __internal_ocaml_attributes =
+ | PpxDerivingShow
+ | PpxDerivingShowConstant of string (* Generate [@@@ deriving show ] on the resulting OCaml type *)
+ | PpxDerivingYoJson (* Similar, but for constant printers. *)
+ | CInline (* Generate [@@@ deriving yojson ] on the resulting OCaml type *)This may be unnecessary. In the future, we are likely to flatten +this definition into several definitions of abstract top-level +names.
+An example:
+`@@ CInline ` let f x = UInt32.(x +%^ 1)is extracted to C by KReMLin to a C definition tagged with the
+inline qualifier.
KreMLin-only: generates a C "inline" attribute on the resulting
+* function declaration.
+KreMLin-only: forces KreMLin to inline the function at call-site; this is
+* deprecated and the recommended way is now to use F*'s
+* inline_for_extraction, which now also works for stateful functions.
+KreMLin-only: instructs KreMLin to heap-allocate any value of this
+* data-type; this requires running with a conservative GC as the
+* allocations are not freed.
+KreMLin-only: attach a comment to the declaration. Note that using F*-doc
+* syntax automatically fills in this attribute.
+KreMLin-only: verbatim C code to be prepended to the declaration.
+* Multiple attributes are valid and accumulate, separated by newlines.
+KreMLin-only: indicates that the parameter with that name is to be marked
+* as C const. This will be checked by the C compiler, not by KreMLin or F*.
+*
+* This is deprecated and doesn't work as intended. Use
+* LowStar.ConstBuffer.fst instead!
+KreMLin-only: for types that compile to struct types (records and
+* inductives), indicate that the header file should only contain a forward
+* declaration, which in turn forces the client to only ever use this type
+* through a pointer.
+KreMLin-only: for a top-level let v = e, compile as a macro
| Substitute
+| Gc
+| Comment of string
+| CPrologue of string
+| CEpilogue of string
+| CConst of string (* Ibid. *)
+| CCConv of string
+| CAbstractStruct (* A calling convention for C, one of stdcall, cdecl, fastcall *)
+| CIfDef
+| CMacro (* KreMLin-only: on a given `val foo`, compile if foo with #ifdef. *)The inline_let attribute on a local let-binding, instructs the
+extraction pipeline to inline the definition. This may be both to
+avoid generating unnecessary intermediate variables, and also to
+enable further partial evaluation. Note, use this with care, since
+inlining all lets can lead to an exponential blowup in code
+size.
val inline_let : unitThe rename_let attribute support a form of metaprogramming for
+the names of let-bound variables used in extracted code.
val rename_let (new_name: string) : Tot unitThis is useful, particularly in conjunction with partial +evaluation, to ensure that names reflect their usage context.
+See tests/micro-benchmarks/Renaming*.fst
+The plugin attribute is used in conjunction with native
+compilation of F* components, accelerating their reduction
+relative to the default strategy of just interpreting them.
val plugin (x: int) : Tot unitSee examples/native_tactics for several examples.
+An attribute to mark things that the typechecker should first +elaborate and typecheck, but unfold before verification.
+val tcnorm : unitWe erase all ghost functions and unit-returning pure functions to
+() at extraction. This creates a small issue with abstract
+types. Consider a module that defines an abstract type t whose
+(internal) definition is unit and also defines f: int -> t. f
+would be erased to be just () inside the module, while the
+client calls to f would not, since t is abstract. To get
+around this, when extracting interfaces, if we encounter an
+abstract type, we tag it with this attribute, so that
+extraction can treat it specially.
val must_erase_for_extraction : unitNote, since the use of cross-module inlining (the --cmi option),
+this attribute is no longer necessary. We retain it for legacy,
+but will remove it in the future.
This attribute is used with the Dijkstra Monads for Free +construction to track position information in generated VCs
+val dm4f_bind_range : unitWhen attached a top-level definition, the typechecker will succeed +if and only if checking the definition results in an error. The +error number list is actually OPTIONAL. If present, it will be +checked that the definition raises exactly those errors in the +specified multiplicity, but order does not matter.
+val expect_failure (errs: list int) : Tot unitWhen --lax is present, with the previous attribute since some +definitions only fail when verification is turned on. With this +attribute, one can ensure that a definition fails while lax-checking +too. Same semantics as above, but lax mode will be turned on for the +definition.
+val expect_lax_failure (errs: list int) : Tot unitPrint the time it took to typecheck a top-level definition
+val tcdecltime : unitTHIS ATTRIBUTE IS AN ESCAPE HATCH AND CAN BREAK SOUNDNESS
+val assume_strictly_positive : unitUSE WITH CARE
+The positivity check for inductive types stops at abstraction
+boundaries. This results in spurious errors about positivity,
+e.g., when defining types like type t = ref (option t) By adding
+this attribute to a declaration of a top-level name positivity
+checks on applications of that name are admitted. See, for
+instance, FStar.Monotonic.Heap.mref We plan to decorate binders of
+abstract types with polarities to allow us to check positivity
+across abstraction boundaries and will eventually remove this
+attribute.
This attribute is to be used as a hint for the unifier. A
+function-typed symbol t marked with this attribute will be treated
+as being injective in all its arguments by the unifier. That is,
+given a problem t a1..an =?= t b1..bn the unifier will solve it by
+proving ai =?= bi for all i, without trying to unfold the
+definition of t.
val unifier_hint_injective : unitThis attribute is used to control the evaluation order +and unfolding strategy for certain definitions.
+val strict_on_arguments (x: list int) : Tot unitIn particular, given
+FStar `@@(strict_on_arguments `1;2`)` let f x0 (x1:list x0) (x1:option x0) = e
An application f e0 e1 e2 is reduced by the normalizer by:
1. evaluating `e0 ~>* v0, e1 ~>* v1, e2 ~>* v2`
+
+2 a.
+ If, according to the positional arguments `1;2`,
+ if v1 and v2 have constant head symbols
+ (e.g., v1 = Cons _ _ _, and v2 = None _)
+ then `f` is unfolded to `e` and reduced as
+ ```FStare[v0/x0][v1/x1][v2/x2]```
+
+2 b.
+
+ Otherwise, `f` is not unfolded and the term is `f e0 e1 e2`
+ reduces to `f v0 v1 v2`.
+
+unit.Ghost effect, ensuring that computationally relevantval resolve_implicits : unitThis attribute can be added to an inductive type definition,
+indicating that it should be erased on extraction to unit.
val erasable : unitHowever, any pattern matching on the inductive type results
+in a Ghost effect, ensuring that computationally relevant
+code cannot rely on the values of the erasable type.
See tests/micro-benchmarks/Erasable.fst, for examples. Also +see https://github.com/FStarLang/FStar/issues/1844
+THIS ATTRIBUTE CAN BREAK EXTRACTION SOUNDNESS, USE WITH CARE
+val allow_informative_binders : unitCombinators for reifiable layered effects must have binders with +non-informative types, since at extraction time, those binders are +substituted with (). +This attribute can be added to a layered effect definition to skip this +check, i.e. adding it will allow informative binder types, but then +the code should not be extracted
+commute_nested_matches
+This attribute can be used to decorate an inductive type t
val commute_nested_matches : unitDuring normalization, if reduction is blocked on matching the
+constructors of t in the following sense:
[ +match (match e0 with | P1 -> e1 | ... | Pn -> en) with +| Q1 -> f1 ... | Qm -> fm +]
+i.e., the outer match is stuck due to the inner match on e0
+being stuck, and if the head constructor the outer Qi patterns
+are the constructors of the decorated inductive type t, then,
+this is reduced to
[ +match e0 with +| P1 -> (match e1 with | Q1 -> f1 ... | Qm -> fm) +| ... +| Pn -> (match en with | Q1 -> f1 ... | Qm -> fm) +]
+This is sometimes useful when partially evaluating code before +extraction, particularly when aiming to obtain first-order code +for KReMLin. However, this attribute should be used with care, +since if after the rewriting the inner matches do not reduce, then +this can cause an explosion in code size.
+See tests/micro-benchmarks/CommuteNestedMatches.fst +and examples/layeredeffects/LowParseWriters.fsti
+This attribute controls extraction: it can be used to disable +extraction of a given top-level definition into a specific backend, +such as "OCaml". If any extracted code must call into an erased +function, an error will be raised (code 340).
+val noextract_to (backend:string) : Tot unitThis attribute decorates a let binding, e.g.,
+val normalize_for_extraction (steps:list norm_step) : Tot unit@@normalize_for_extraction steps
+let f = e
The effect is that prior to extraction, F* will first reduce e
+using the normalization steps, and then proceed to extract it as
+usual.
Almost the same behavior can be achieved by using a
+postprocess_for_extraction_with t attribute, which runs tactic
+t on the goal e == ?u and extracts the solution to ?u in
+place of e. However, using a tactic to postprocess a term is
+more general than needed for some cases.
In particular, if we intend to only normalize e before
+extraction (rather than applying some other form of equational
+reasoning), then using normalize_for_extraction can be more
+efficient, for the following reason:
Since we are reducing e just before extraction, F* can enable an
+otherwise non-user-facing normalization feature that allows all
+arguments marked @@@erasable to be erased to ()---these terms
+will anyway be extracted to () so erasing them during
+normalization is a useful optimization.
A layered effect definition may optionally be annoated with +(ite_soundness_by t) attribute, where t is another attribute +When so, the implicits and the smt guard generated when +checking the soundness of the if-then-else combinator, are +dispatched to the tactic in scope that has the t attribute (in addition +to the resolve_implicits attribute as usual)
+val ite_soundness_by : unitSee examples/layeredeffects/IteSoundess.fst for a few examples
+A binder in a definition/declaration may optionally be annotated as strictly_positive +When the let definition is used in a data constructor type in an inductive +definition, this annotation is used to check the positivity of the inductive
+val strictly_positive : unitFurther F* checks that the binder is actually positive in the let definition
+See tests/micro-benchmarks/Positivity.fst and NegativeTests.Positivity.fst for a few examples
+Pure and ghost inner let bindings are now always inlined during +the wp computation, if: the return type is not unit and the head +symbol is not marked irreducible.
+val singleton (#a: Type) (x: a) : Tot (y: a{y == x})To circumvent this behavior, singleton can be used. +See the example usage in ulib/FStar.Algebra.Monoid.fst.
+with_type t e is just an identity function, but it receives
+special treatment in the SMT encoding, where in addition to being
+an identity function, we have an SMT axiom:
+forall t e.{:pattern (with_type t e)} has_type (with_type t e) t
val with_type (#t: Type) (e: t) : Tot tA weakening coercion from eqtype to Type.
+unfold let eqtype_as_type (a:eqtype) : Type = aOne of its uses is in types of layered effect combinators that +are subjected to stricter typing discipline (no subtyping)
+ diff --git a/docs/FStar.PredicateExtensionality.html b/docs/FStar.PredicateExtensionality.html index c558cdc..0aa4e0e 100644 --- a/docs/FStar.PredicateExtensionality.html +++ b/docs/FStar.PredicateExtensionality.html @@ -1,16 +1,27 @@ - - + + - - -fsdoc: no-summary-found
-fsdoc: no-comment-found
+F
+P
+let predicate (a:Type) = a -> Tot proplet peq (#a:Type) (p1:predicate a) (p2:predicate a) =
+ forall x. (p1 x <==> p2 x)let predicateExtensionality (a:Type) (p1 p2:predicate a)
+ : Lemma (requires (peq #a p1 p2))
+ (ensures (F.on_domain a p1==F.on_domain a p2))
+ = P.axiom();
+ assert (F.feq p1 p2)fsdoc: no-summary-found
-fsdoc: no-comment-found
+Preordered relations and stable predicates
+type relation (a:Type) = a -> a -> Type0type predicate (a:Type) = a -> Type0let reflexive (#a:Type) (rel:relation a) =
+ forall (x:a). rel x xlet transitive (#a:Type) (rel:relation a) =
+ forall (x:a) (y:a) (z:a). (rel x y /\ rel y z) ==> rel x zlet preorder_rel (#a:Type) (rel:relation a) =
+ reflexive rel /\ transitive reltype preorder (a:Type) = rel:relation a{preorder_rel rel}let stable (#a:Type) (p:predicate a) (rel:relation a{preorder_rel rel}) =
+ forall (x:a) (y:a). (p x /\ rel x y) ==> p yfsdoc: no-summary-found
-fsdoc: no-comment-found
+A variable arity C-style printf
+See tests/micro-benchmarks/Test.Printf.fst for example usage
+Opens module FStar.Char
+Opens module FStar.String
+Aliases module FStar.Integers as I
noeq
+type extension =
+ | MkExtension : #a:Type0 -> $f:(a -> Tot string) -> extension+++
arg: The format specifiers supported +%b : bool +%d : int +%c : char +%s : string +%uy : U8.t +%us : U16.t +%ul : U32.t +%uL : U64.t +%y : Int8.t +%i : Int16.t +%l : Int32.t +%L : Int64.t
noeq
+type arg =
+ | Bool
+ | Int
+ | Char
+ | String
+ | U8
+ | U16
+ | U32
+ | U64
+ | I8
+ | I16
+ | I32
+ | I64
+ | Extension of extension+++
arg_type: Interpreting aargtag as a type
let arg_type (a:arg) : Tot Type0 =
+ match a with
+ | Bool -> bool
+ | Int -> int
+ | Char -> char
+ | String -> string
+ | U8 -> FStar.UInt8.t
+ | U16 -> FStar.UInt16.t
+ | U32 -> FStar.UInt32.t
+ | U64 -> FStar.UInt64.t
+ | I8 -> FStar.Int8.t
+ | I16 -> FStar.Int16.t
+ | I32 -> FStar.Int32.t
+ | I64 -> FStar.Int64.t
+ | Extension (MkExtension #t _) -> tlet string_of_arg (#a:arg) (x:arg_type a) : string =
+ match a with
+ | Bool -> string_of_bool x
+ | Int -> string_of_int x
+ | Char -> string_of_char x
+ | String -> x
+ | U8 -> FStar.UInt8.to_string x
+ | U16 -> FStar.UInt16.to_string x
+ | U32 -> FStar.UInt32.to_string x
+ | U64 -> FStar.UInt64.to_string x
+ | I8 -> FStar.Int8.to_string x
+ | I16 -> FStar.Int16.to_string x
+ | I32 -> FStar.Int32.to_string x
+ | I64 -> FStar.Int64.to_string x
+ | Extension (MkExtension f) -> f x+++
dir: Internal to this module +A 'directive"; used when parsing a format specifier
noeq
+type dir =
+ | Lit of char
+ | Arg of arg+++
dir_type ds: Interpreting a list directives as a pure function type
let rec dir_type (ds:list dir) : Tot Type0 =
+ match ds with
+ | [] -> string
+ | Lit c :: ds' -> dir_type ds'
+ | Arg a :: ds' -> arg_type a -> dir_type ds'+++
string_of_dirs ds: +Interpreting a list of directives as its function, +in a continuation-passing style
let rec string_of_dirs
+ (ds:list dir)
+ (k:string -> string)
+ : dir_type ds
+ = match ds with
+ | [] -> k ""
+ | Lit c :: ds' ->
+ string_of_dirs ds' (fun res -> k (string_of_char c ^ res))
+ <: normalize_term (dir_type ds')
+ | Arg a :: ds' ->
+ fun (x : arg_type a) ->
+ string_of_dirs ds' (fun res -> ((k "")
+ ^ string_of_arg x
+ ^ res))type extension_parser = i:list char -> option (extension * o:list char{o << i})+++
parse_format s: +Parses a list of characters into a list of directives +Or None, in case the format string is invalid
let rec parse_format
+ (s:list char)
+ (parse_ext: extension_parser)
+ : option (list dir)
+ = let add_dir (d:dir) (ods : option (list dir))
+ : option (list dir)
+ = match ods with
+ | None -> None
+ | Some ds -> Some (d::ds)
+ in
+ match s with
+ | [] -> Some []
+ | ['%'] -> NoneUnsigned integers beging with '%u'
+| '%' :: 'u' :: s' -> begin
+ match s' with
+ | 'y' :: s'' -> add_dir (Arg U8) (parse_format s'' parse_ext)
+ | 's' :: s'' -> add_dir (Arg U16) (parse_format s'' parse_ext)
+ | 'l' :: s'' -> add_dir (Arg U32) (parse_format s'' parse_ext)
+ | 'L' :: s'' -> add_dir (Arg U64) (parse_format s'' parse_ext)
+ | _ -> None
+ endUser extensions begin with '%X'
+| '%' :: 'X' :: s' -> begin
+ match parse_ext s' with
+ | Some (ext, rest) -> add_dir (Arg (Extension ext)) (parse_format rest parse_ext)
+ | _ -> None
+ end| '%' :: c :: s' -> begin
+ match c with
+ | '%' -> add_dir (Lit '%') (parse_format s' parse_ext)
+ | 'b' -> add_dir (Arg Bool) (parse_format s' parse_ext)
+ | 'd' -> add_dir (Arg Int) (parse_format s' parse_ext)
+ | 'c' -> add_dir (Arg Char) (parse_format s' parse_ext)
+ | 's' -> add_dir (Arg String) (parse_format s' parse_ext)
+ | 'y' -> add_dir (Arg I8) (parse_format s' parse_ext)
+ | 'i' -> add_dir (Arg I16) (parse_format s' parse_ext)
+ | 'l' -> add_dir (Arg I32) (parse_format s' parse_ext)
+ | 'L' -> add_dir (Arg I64) (parse_format s' parse_ext)
+ | _ -> None
+ end
+| c :: s' ->
+ add_dir (Lit c) (parse_format s' parse_ext)+++
parse_format_string: parses a formatstringinto a list of directives
let parse_format_string
+ (s:string)
+ (parse_ext:extension_parser)
+ : option (list dir)
+ = parse_format (list_of_string s) parse_extlet no_extensions : extension_parser = fun s -> None+++
sprintf: The main function of this module +A variable arity string formatter +Used as:sprintf "format string" v1 ... vn+It's marked `inline_for_extraction`, meaning that we don't need +any special support in our compilation targets to support sprintf + +`sprintf "Hello %s" "world"` + will just extract to `"Hello " ^ "world"` +
inline_for_extraction
+let sprintf
+ (s:string{normalize_term (b2t (Some? (parse_format_string s no_extensions)))})
+ : normalize_term (dir_type (Some?.v (parse_format_string s no_extensions)))
+ = normalize_term (string_of_dirs (Some?.v (parse_format_string s no_extensions)) (fun s -> s))+++
ext_sprintf: An extensible version of sprintf
inline_for_extraction
+let ext_sprintf
+ (parse_ext: extension_parser)
+ (s:string{normalize_term (b2t (Some? (parse_format_string s parse_ext)))})
+ : normalize_term (dir_type (Some?.v (parse_format_string s parse_ext)))
+ = normalize_term (string_of_dirs (Some?.v (parse_format_string s parse_ext)) (fun s -> s))fsdoc: no-summary-found
-fsdoc: no-comment-found
+prop.Prims.prop is defined as the type of all subtypes of unit,squash t, for all t.prop, but some of which are actually inconsistent with thisassume
+val axiom (_:unit)
+ : Lemma (forall (p1 p2:prop). (p1 <==> p2) <==> (p1 == p2))let apply (p1 p2:prop)
+ : Lemma (ensures ((p1 <==> p2) <==> (p1 == p2)))
+ = axiom ()fsdoc: no-summary-found
-fsdoc: no-comment-found
+assume new type rangeval prims_to_fstar_range : Prims.range -> Tot rangefsdoc: no-summary-found
-fsdoc: no-comment-found
+This module provides a signature for real arithmetic.
+Real number constants can be specific in floating point format with +an 'R' suffix, e.g., 1.0R
+All these operations are mapped to the correspondings primitives +in Z3's theory of real arithmetic.
+val real : eqtypeval of_int : int -> Tot realUsed to extract real constants; this function is
+uninterpreted logically. i.e., 1.1R is extracted to
+of_string "1.1"
val of_string: string -> Tot realval ( +. ) : real -> real -> Tot real
+val ( -. ) : real -> real -> Tot real
+val ( *. ) : real -> real -> Tot real
+val ( /. ) : real -> d:real{d <> 0.0R} -> Tot realval ( >. ) : real -> real -> Tot bool
+val ( >=. ) : real -> real -> Tot boolval ( <. ) : real -> real -> Tot bool
+val ( <=. ) : real -> real -> Tot bool#reset-options "--smtencoding.elim_box true --smtencoding.l_arith_repr native --smtencoding.nl_arith_repr native"Tests
+let zero : real = of_int 0
+let one : real = of_int 1
+let two : real = of_int 2val sqrt_2 : r:real{r *. r = two}let n_over_n2 (n:real{n <> 0.0R /\ n*.n <> 0.0R}) = assert (n /. (n *. n) == 1.0R /. n)let test = assert (two >. one)
+let test1 = assert (one = 1.0R)let test_lt1 = assert (1.0R <. 2.0R)
+let test_lt2 = assert (~ (1.0R <. 1.0R))
+let test_lt3 = assert (~ (2.0R <. 1.0R))let test_le1 = assert (1.0R <=. 2.0R)
+let test_le2 = assert (1.0R <=. 1.0R)
+let test_le3 = assert (~ (2.0R <=. 1.0R))let test_gt1 = assert (~ (1.0R >. 2.0R))
+let test_gt2 = assert (~ (1.0R >. 1.0R))
+let test_gt3 = assert (2.0R >. 1.0R)let test_ge1 = assert (~ (1.0R >=. 2.0R))
+let test_ge2 = assert (1.0R >=. 1.0R)
+let test_ge3 = assert (2.0R >=. 1.0R)let test_add_eq = assert (1.0R +. 1.0R = 2.0R)
+let test_add_eq' = assert (1.0R +. 3.0R = 4.0R)
+let test_add_lt = assert (1.0R +. 1.0R <. 3.0R)let test_mul_eq = assert (2.0R *. 2.0R = 4.0R)
+let test_mul_lt = assert (2.0R *. 2.0R <. 5.0R)let test_div_eq = assert (8.0R /. 2.0R = 4.0R)
+let test_div_lt = assert (8.0R /. 2.0R <. 5.0R)let test_sqrt_2_mul = assert (sqrt_2 *. sqrt_2 = 2.0R)let test_sqrt_2_add = assert (sqrt_2 +. sqrt_2 >. 2.0R) // Fails
+let test_sqrt_2_scale = assert (1.0R /. sqrt_2 = sqrt_2 /. 2.0R)Common identities
+let add_id_l = assert (forall n. 0.0R +. n = n)
+let add_id_r = assert (forall n. n +. 0.0R = n)let mul_nil_l = assert (forall n. 0.0R *. n = 0.0R)
+let mul_nil_r = assert (forall n. n *. 0.0R = 0.0R)let mul_id_l = assert (forall n. 1.0R *. n = n)
+let mul_id_r = assert (forall n. n *. 1.0R = n)let add_comm = assert (forall x y. x +. y = y +.x)
+let add_assoc = assert (forall x y z. (x +. y) +.z = (x +. y) +. z)let mul_comm = assert (forall x y. x *. y = y *.x)
+let mul_assoc = assert (forall x y z. (x *. y) *.z = (x *. y) *. z)
+let mul_dist = assert (forall x y z. x *. (y +. z) = (x *. y) +. (x *.z))fsdoc: no-summary-found
-fsdoc: no-comment-found
+wrapper over FStar.ST to provide operations over refs with default preorder
+Includes module FStar.Heap
+Includes module FStar.ST
+Opens module FStar.Heap
+Opens module FStar.ST
+unfold
+let sel (#a:Type0) (h:heap) (r:ref a) : GTot a
+ = Heap.sel h runfold
+let upd (#a:Type0) (h:heap) (r:ref a) (v:a) :GTot heap
+ = Heap.upd h r vunfold
+let addr_of (#a:Type0) (r:ref a) : GTot nat = addr_of runfold
+let contains (#a:Type0) (h:heap) (r:ref a) :GTot Type0
+ = Heap.contains h runfold
+let unused_in (#a:Type0) (r:ref a) (h:heap) :GTot Type0
+ = Heap.unused_in r hunfold
+let fresh (#a:Type0) (r:ref a) (h0:heap) (h1:heap) : Type0
+ = Heap.fresh r h0 h1unfold
+let only (#a:Type0) (r:ref a) :GTot (Set.set nat)
+ = Heap.only rval recall (#a:Type0) (r:ref a) : STATE unit (fun p h -> h `contains` r ==> p () h)
+let recall #_ r = recall rval alloc (#a:Type0) (init:a)
+ :ST (ref a)
+ (fun _ -> True)
+ (fun h0 r h1 -> fresh r h0 h1 /\ modifies Set.empty h0 h1 /\ sel h1 r == init)
+let alloc #_ init = alloc initval read (#a:Type0) (r:ref a) :STATE a (fun p h -> p (sel h r) h)
+let read #_ r = read rval write (#a:Type0) (r:ref a) (v:a)
+ :ST unit (fun _ -> True) (fun h0 _ h1 -> h0 `contains` r /\ modifies (only r) h0 h1 /\ equal_dom h0 h1 /\ sel h1 r == v)
+let write #_ r v = write r vval op_Bang (#a:Type0) (r:ref a) :STATE a (fun p h -> p (sel h r) h)
+let op_Bang #_ r = read rval op_Colon_Equals (#a:Type0) (r:ref a) (v:a)
+ :ST unit (fun _ -> True) (fun h0 _ h1 -> h0 `contains` r /\ modifies (only r) h0 h1 /\ equal_dom h0 h1 /\ sel h1 r == v)
+let op_Colon_Equals #_ r v = write r vfsdoc: no-summary-found
-fsdoc: no-comment-found
+Opens module FStar.Tactics
+Opens module FStar.Reflection
+Aliases module FStar.Order as O
Simple decision procedure to decide if a term is an "arithmetic
+proposition", by which we mean a simple relation between two
+arithmetic expressions (each representing integers or naturals)
+Main use case: deciding, in a tactic, if a goal is an arithmetic
+expression and applying a custom decision procedure there (instead of
+feeding to the SMT solver)
+noeq
+type expr =
+ | Lit : int -> expratom, contains both a numerical ID and the actual term encountered +| Div : expr -> expr -> expr // Add this one?
+| Atom : nat -> term -> expr
+| Plus : expr -> expr -> expr
+| Mult : expr -> expr -> expr
+| Minus : expr -> expr -> expr
+| Land : expr -> expr -> expr
+| Lxor : expr -> expr -> expr
+| Lor : expr -> expr -> expr
+| Ladd : expr -> expr -> expr
+| Lsub : expr -> expr -> expr
+| Shl : expr -> expr -> expr
+| Shr : expr -> expr -> expr
+| Neg : expr -> expr
+| Udiv : expr -> expr -> expr
+| Umod : expr -> expr -> expr
+| MulMod : expr -> expr -> expr
+| NatToBv : expr -> exprnoeq
+type connective =
+ | C_Lt | C_Eq | C_Gt | C_Nenoeq
+type prop =
+ | CompProp : expr -> connective -> expr -> prop
+ | AndProp : prop -> prop -> prop
+ | OrProp : prop -> prop -> prop
+ | NotProp : prop -> proplet lt e1 e2 = CompProp e1 C_Lt e2
+let le e1 e2 = CompProp e1 C_Lt (Plus (Lit 1) e2)
+let eq e1 e2 = CompProp e1 C_Eq e2
+let ne e1 e2 = CompProp e1 C_Ne e2
+let gt e1 e2 = CompProp e1 C_Gt e2
+let ge e1 e2 = CompProp (Plus (Lit 1) e1) C_Gt e2Define a traversal monad! Makes exception handling and counter-keeping easy
+private let st = p:(nat * list term){fst p == List.Tot.Base.length (snd p)}
+private let tm a = st -> Tac (either string (a * st))
+private let return (x:'a) : tm 'a = fun i -> Inr (x, i)
+private let bind (m : tm 'a) (f : 'a -> tm 'b) : tm 'b =
+ fun i -> match m i with
+ | Inr (x, j) -> f x j
+ | s -> Inl (Inl?.v s) // why? To have a catch-all pattern and thus an easy WPval lift : ('a -> Tac 'b) -> ('a -> tm 'b)
+let lift f x st =
+ Inr (f x, st)val liftM : ('a -> 'b) -> (tm 'a -> tm 'b)
+let liftM f x =
+ xx <-- x;
+ return (f xx)val liftM2 : ('a -> 'b -> 'c) -> (tm 'a -> tm 'b -> tm 'c)
+let liftM2 f x y =
+ xx <-- x;
+ yy <-- y;
+ return (f xx yy)val liftM3 : ('a -> 'b -> 'c -> 'd) -> (tm 'a -> tm 'b -> tm 'c -> tm 'd)
+let liftM3 f x y z =
+ xx <-- x;
+ yy <-- y;
+ zz <-- z;
+ return (f xx yy zz)private let rec find_idx (f : 'a -> bool) (l : list 'a) : option ((n:nat{n < List.Tot.Base.length l}) * 'a) =
+ match l with
+ | [] -> None
+ | x::xs ->
+ if f x
+ then Some (0, x)
+ else begin match find_idx f xs with
+ | None -> None
+ | Some (i, x) -> Some (i+1, x)
+ endprivate let atom (t:term) : tm expr = fun (n, atoms) ->
+ match find_idx (term_eq t) atoms with
+ | None -> Inr (Atom n t, (n + 1, t::atoms))
+ | Some (i, t) -> Inr (Atom (n - 1 - i) t, (n, atoms))private val fail : (#a:Type) -> string -> tm a
+private let fail #a s = fun i -> Inl slet refined_list_t (#a:Type) (p:(a -> Type0)) = list (x:a{p x})val list_unref : #a:Type -> #p:(a -> Type0) -> refined_list_t p -> Tot (l:list a{forall x. List.Tot.Base.memP x l ==> p x})
+let rec list_unref #a #p l =
+ match l with
+ | [] -> []
+ | x::xs -> x :: list_unref xslet collect_app_ref (t:term) : ((h:term{h == t \/ h << t}) * refined_list_t (fun (a:argv) -> fst a << t)) =
+ collect_app_ref tval as_arith_expr : term -> tm expr
+#push-options "--initial_fuel 4 --max_fuel 4"
+let rec as_arith_expr (t:term) =
+ let hd, tl = collect_app_ref t in
+ let tl = list_unref tl in //need to be careful to instantiate list_unref at the right type to allow SMT to unfold its recursive definition properly
+ match inspect_ln hd, tl with
+ | Tv_FVar fv, [(e1, Q_Implicit); (e2, Q_Explicit) ; (e3, Q_Explicit)] ->
+ let qn = inspect_fv fv in
+ let e2' = as_arith_expr e2 in
+ let e3' = as_arith_expr e3 in
+ if qn = land_qn then liftM2 Land e2' e3'
+ else if qn = lxor_qn then liftM2 Lxor e2' e3'
+ else if qn = lor_qn then liftM2 Lor e2' e3'
+ else if qn = shiftr_qn then liftM2 Shr e2' e3'
+ else if qn = shiftl_qn then liftM2 Shl e2' e3'
+ else if qn = udiv_qn then liftM2 Udiv e2' e3'
+ else if qn = umod_qn then liftM2 Umod e2' e3'
+ else if qn = mul_mod_qn then liftM2 MulMod e2' e3'
+ else if qn = ladd_qn then liftM2 Ladd e2' e3'
+ else if qn = lsub_qn then liftM2 Lsub e2' e3'
+ else atom t
+ | Tv_FVar fv, [(l, Q_Explicit); (r, Q_Explicit)] ->
+ let qn = inspect_fv fv inHave to go through hoops to get F* to typecheck this. +Maybe the do notation is twisting the terms somehow unexpected?
+ let ll = as_arith_expr l in
+ let rr = as_arith_expr r in
+ if qn = add_qn then liftM2 Plus ll rr
+ else if qn = minus_qn then liftM2 Minus ll rr
+ else if qn = mult_qn then liftM2 Mult ll rr
+ else if qn = mult'_qn then liftM2 Mult ll rr
+ else atom t
+ | Tv_FVar fv, [(l, Q_Implicit); (r, Q_Explicit)] ->
+ let qn = inspect_fv fv in
+ let ll = as_arith_expr l in
+ let rr = as_arith_expr r in
+ if qn = nat_bv_qn then liftM NatToBv rr
+ else atom t
+ | Tv_FVar fv, [(a, Q_Explicit)] ->
+ let qn = inspect_fv fv in
+ let aa = as_arith_expr a in
+ if qn = neg_qn then liftM Neg aa
+ else atom t
+ | Tv_Const (C_Int i), _ ->
+ return (Lit i)
+ | _ ->
+ atom t
+#pop-optionsval is_arith_expr : term -> tm expr
+let is_arith_expr t =
+ a <-- as_arith_expr t ;
+ match a with
+ | Atom _ t -> begin
+ let hd, tl = collect_app_ref t in
+ match inspect_ln hd, tl with
+ | Tv_FVar _, []
+ | Tv_BVar _, []
+ | Tv_Var _, [] -> return a
+ | _ -> fail ("not an arithmetic expression: (" ^ term_to_string t ^ ")")
+ end
+ | _ -> return aCannot use this... +val is_arith_prop : term -> tm prop
+val is_arith_prop : term -> st -> Tac (either string (prop * st))
+let rec is_arith_prop (t:term) = fun i ->
+ (f <-- lift term_as_formula t;
+ match f with
+ | Comp (Eq _) l r -> liftM2 eq (is_arith_expr l) (is_arith_expr r)
+ | Comp (BoolEq _) l r -> liftM2 eq (is_arith_expr l) (is_arith_expr r)
+ | Comp Lt l r -> liftM2 lt (is_arith_expr l) (is_arith_expr r)
+ | Comp Le l r -> liftM2 le (is_arith_expr l) (is_arith_expr r)
+ | And l r -> liftM2 AndProp (is_arith_prop l) (is_arith_prop r)
+ | Or l r -> liftM2 OrProp (is_arith_prop l) (is_arith_prop r)
+ | _ -> fail ("connector (" ^ term_to_string t ^ ")")) iRun the monadic computations, disregard the counter
+let run_tm (m : tm 'a) : Tac (either string 'a) =
+ match m (0, []) with
+ | Inr (x, _) -> Inr x
+ | s -> Inl (Inl?.v s) // why? To have a catch-all pattern and thus an easy WPlet rec expr_to_string (e:expr) : string =
+ match e with
+ | Atom i _ -> "a"^(string_of_int i)
+ | Lit i -> string_of_int i
+ | Plus l r -> "(" ^ (expr_to_string l) ^ " + " ^ (expr_to_string r) ^ ")"
+ | Minus l r -> "(" ^ (expr_to_string l) ^ " - " ^ (expr_to_string r) ^ ")"
+ | Mult l r -> "(" ^ (expr_to_string l) ^ " * " ^ (expr_to_string r) ^ ")"
+ | Neg l -> "(- " ^ (expr_to_string l) ^ ")"
+ | Land l r -> "(" ^ (expr_to_string l) ^ " & " ^ (expr_to_string r) ^ ")"
+ | Lor l r -> "(" ^ (expr_to_string l) ^ " | " ^ (expr_to_string r) ^ ")"
+ | Lxor l r -> "(" ^ (expr_to_string l) ^ " ^ " ^ (expr_to_string r) ^ ")"
+ | Ladd l r -> "(" ^ (expr_to_string l) ^ " >> " ^ (expr_to_string r) ^ ")"
+ | Lsub l r -> "(" ^ (expr_to_string l) ^ " >> " ^ (expr_to_string r) ^ ")"
+ | Shl l r -> "(" ^ (expr_to_string l) ^ " << " ^ (expr_to_string r) ^ ")"
+ | Shr l r -> "(" ^ (expr_to_string l) ^ " >> " ^ (expr_to_string r) ^ ")"
+ | NatToBv l -> "(" ^ "to_vec " ^ (expr_to_string l) ^ ")"
+ | Udiv l r -> "(" ^ (expr_to_string l) ^ " / " ^ (expr_to_string r) ^ ")"
+ | Umod l r -> "(" ^ (expr_to_string l) ^ " % " ^ (expr_to_string r) ^ ")"
+ | MulMod l r -> "(" ^ (expr_to_string l) ^ " ** " ^ (expr_to_string r) ^ ")"let rec compare_expr (e1 e2 : expr) : O.order =
+ match e1, e2 with
+ | Lit i, Lit j -> O.compare_int i j
+ | Atom _ t, Atom _ s -> compare_term t s
+ | Plus l1 l2, Plus r1 r2
+ | Minus l1 l2, Minus r1 r2
+ | Mult l1 l2, Mult r1 r2 -> O.lex (compare_expr l1 r1) (fun () -> compare_expr l2 r2)
+ | Neg e1, Neg e2 -> compare_expr e1 e2
+ | Lit _, _ -> O.Lt | _, Lit _ -> O.Gt
+ | Atom _ _, _ -> O.Lt | _, Atom _ _ -> O.Gt
+ | Plus _ _, _ -> O.Lt | _, Plus _ _ -> O.Gt
+ | Mult _ _, _ -> O.Lt | _, Mult _ _ -> O.Gt
+ | Neg _, _ -> O.Lt | _, Neg _ -> O.Gt
+ | _ -> O.Gt // don't care about this for nowViews
+NOTE: You probably want inspect/pack from FStar.Tactics, which work
+val inspect_ln : (t:term) -> tv:term_view{smaller tv t}
+val pack_ln : term_view -> termval pack_inspect_inv : (t:term) -> Lemma (pack_ln (inspect_ln t) == t)
+val inspect_pack_inv : (tv:term_view) -> Lemma (inspect_ln (pack_ln tv) == tv)val inspect_comp : (c:comp) -> cv:comp_view{smaller_comp cv c}
+val pack_comp : comp_view -> compval inspect_sigelt : sigelt -> sigelt_view
+val pack_sigelt : sigelt_view -> sigeltval inspect_fv : fv -> name
+val pack_fv : name -> fvval inspect_bv : bv -> bv_view
+val pack_bv : bv_view -> bvval inspect_lb : letbinding -> lb_view
+val pack_lb : lb_view -> letbindingval inspect_binder : binder -> bv * (aqualv * list term)
+val pack_binder : bv -> aqualv -> list term -> binderThese are equivalent to String.concat ".", String.split '.'``
String.compare. We're only taking them as primitives to breakval implode_qn : list string -> string
+val explode_qn : string -> list string
+val compare_string : string -> string -> intPrimitives & helpers
+val lookup_typ : env -> name -> option sigelt
+val compare_bv : bv -> bv -> order
+val binders_of_env : env -> binders
+val moduleof : env -> name
+val is_free : bv -> term -> bool
+val free_bvs : term -> list bv
+val free_uvars : term -> list int
+val lookup_attr : term -> env -> list fv
+val all_defs_in_env : env -> list fv
+val defs_in_module : env -> name -> list fv
+val term_eq : term -> term -> bool
+val term_to_string : term -> string
+val comp_to_string : comp -> string
+val env_open_modules : env -> list namepush_binder extends the environment with a single binder.
+This is useful as one traverses the syntax of a term,
+pushing binders as one traverses a binder in a lambda,
+match, etc.
val push_binder : env -> binder -> envAttributes are terms, not to be confused with Prims.attribute
+val sigelt_attrs : sigelt -> list term
+val set_sigelt_attrs : list term -> sigelt -> sigeltSetting and reading qualifiers from sigelts
+val sigelt_quals : sigelt -> list qualifier
+val set_sigelt_quals : list qualifier -> sigelt -> sigeltReading the vconfig under which a particular sigelt was typechecked
+val sigelt_opts : sigelt -> option vconfigEmbed a vconfig as a term, for instance to use it with the check_with +attribute
+val embed_vconfig : vconfig -> termMarker to check a sigelt with a particular vconfig
+irreducible
+let check_with (vcfg : vconfig) : unit = ()val subst : bv -> term -> term -> termval close_term : binder -> term -> termfsdoc: no-summary-found
-fsdoc: no-comment-found
+Common lids
+TODO: these are awful names +TODO: _qn vs _lid
+let imp_qn = ["Prims"; "l_imp"]
+let and_qn = ["Prims"; "l_and"]
+let or_qn = ["Prims"; "l_or"]
+let not_qn = ["Prims"; "l_not"]
+let iff_qn = ["Prims"; "l_iff"]
+let eq2_qn = ["Prims"; "eq2"]
+let eq1_qn = ["Prims"; "eq"]
+let true_qn = ["Prims"; "l_True"]
+let false_qn = ["Prims"; "l_False"]
+let b2t_qn = ["Prims"; "b2t"]
+let forall_qn = ["Prims"; "l_Forall"]
+let exists_qn = ["Prims"; "l_Exists"]
+let squash_qn = ["Prims"; "squash"]let bool_true_qn = ["Prims"; "true"]
+let bool_false_qn = ["Prims"; "false"]let int_lid = ["Prims"; "int"]
+let bool_lid = ["Prims"; "bool"]
+let unit_lid = ["Prims"; "unit"]
+let string_lid = ["Prims"; "string"]let add_qn = ["Prims"; "op_Addition"]
+let neg_qn = ["Prims"; "op_Minus"]
+let minus_qn = ["Prims"; "op_Subtraction"]
+let mult_qn = ["Prims"; "op_Multiply"]
+let mult'_qn = ["FStar"; "Mul"; "op_Star"]
+let div_qn = ["Prims"; "op_Division"]
+let lt_qn = ["Prims"; "op_LessThan"]
+let lte_qn = ["Prims"; "op_LessThanOrEqual"]
+let gt_qn = ["Prims"; "op_GreaterThan"]
+let gte_qn = ["Prims"; "op_GreaterThanOrEqual"]
+let mod_qn = ["Prims"; "op_Modulus"]let nil_qn = ["Prims"; "Nil"]
+let cons_qn = ["Prims"; "Cons"]let mktuple2_qn = ["FStar"; "Pervasives"; "Native"; "Mktuple2"]
+let mktuple3_qn = ["FStar"; "Pervasives"; "Native"; "Mktuple3"]
+let mktuple4_qn = ["FStar"; "Pervasives"; "Native"; "Mktuple4"]
+let mktuple5_qn = ["FStar"; "Pervasives"; "Native"; "Mktuple5"]
+let mktuple6_qn = ["FStar"; "Pervasives"; "Native"; "Mktuple6"]
+let mktuple7_qn = ["FStar"; "Pervasives"; "Native"; "Mktuple7"]
+let mktuple8_qn = ["FStar"; "Pervasives"; "Native"; "Mktuple8"]let land_qn = ["FStar" ; "UInt" ; "logand"]
+let lxor_qn = ["FStar" ; "UInt" ; "logxor"]
+let lor_qn = ["FStar" ; "UInt" ; "logor"]
+let ladd_qn = ["FStar" ; "UInt" ; "add_mod"]
+let lsub_qn = ["FStar" ; "UInt" ; "sub_mod"]
+let shiftl_qn = ["FStar" ; "UInt" ; "shift_left"]
+let shiftr_qn = ["FStar" ; "UInt" ; "shift_right"]
+let udiv_qn = ["FStar" ; "UInt" ; "udiv"]
+let umod_qn = ["FStar" ; "UInt" ; "mod"]
+let mul_mod_qn = ["FStar" ; "UInt" ; "mul_mod"]
+let nat_bv_qn = ["FStar" ; "BV" ; "int2bv"]fsdoc: no-summary-found
-fsdoc: no-comment-found
+val uncurry : ('a -> 'b -> 'c) -> ('a * 'b -> 'c)
+let uncurry f (x, y) = f x yval curry : ('a * 'b -> 'c) -> ('a -> 'b -> 'c)
+let curry f x y = f (x, y)A glorified id
val list_ref : (#a:Type) -> (#p:(a -> Type)) -> (l:list a) ->
+ Pure (list (x:a{p x}))
+ (requires (forall_list p l))
+ (ensures (fun _ -> True))
+let rec list_ref #a #p l =
+ match l with
+ | [] -> []
+ | x::xs -> x :: list_ref #a #p xsval mk_app_collect_inv_s : (t:term) -> (args:list argv) ->
+ Lemma (uncurry mk_app (collect_app' args t) == mk_app t args)
+let rec mk_app_collect_inv_s t args =
+ match inspect_ln t with
+ | Tv_App l r ->
+ mk_app_collect_inv_s l (r::args);
+ pack_inspect_inv t
+ | _ -> ()val mk_app_collect_inv : (t:term) -> Lemma (uncurry mk_app (collect_app t) == t)
+let mk_app_collect_inv t = mk_app_collect_inv_s t []val collect_app_order' : (args:list argv) -> (tt:term) -> (t:term) ->
+ Lemma (requires (forall_list (fun a -> fst a << tt) args)
+ /\ t << tt)
+ (ensures (forall_list (fun a -> fst a << tt) (snd (collect_app' args t)))
+ /\ fst (collect_app' args t) << tt)
+ (decreases t)
+let rec collect_app_order' args tt t =
+ match inspect_ln t with
+ | Tv_App l r -> collect_app_order' (r::args) tt l
+ | _ -> ()val collect_app_order : (t:term) ->
+ Lemma (ensures (forall (f:term). forall (s:list argv). (f,s) == collect_app t ==>
+ (f << t /\ forall_list (fun a -> fst a << t) (snd (collect_app t)))
+ \/ (f == t /\ s == [])))
+let collect_app_order t =
+ match inspect_ln t with
+ | Tv_App l r -> collect_app_order' [r] t l
+ | _ -> ()val collect_app_ref : (t:term) -> (h:term{h == t \/ h << t}) * list (a:argv{fst a << t})
+let collect_app_ref t =
+ let h, a = collect_app t in
+ collect_app_order t;
+ h, list_ref #_ #(fun a -> fst a << t) afsdoc: no-summary-found
-fsdoc: no-comment-found
+let name_of_bv (bv : bv) : string =
+ (inspect_bv bv).bv_ppnamelet type_of_bv (bv : bv) : typ =
+ (inspect_bv bv).bv_sortlet bv_to_string (bv : bv) : string =
+ let bvv = inspect_bv bv in
+ "(" ^ bvv.bv_ppname ^ ":" ^ term_to_string bvv.bv_sort ^ ")"let bv_of_binder (b : binder) : bv =
+ let bv, _ = inspect_binder b in
+ bvlet mk_binder (bv : bv) : binder =
+ pack_binder bv Q_Explicit []let mk_implicit_binder (bv : bv) : binder =
+ pack_binder bv Q_Implicit []let name_of_binder (b : binder) : string =
+ name_of_bv (bv_of_binder b)let type_of_binder (b : binder) : typ =
+ type_of_bv (bv_of_binder b)let binder_to_string (b : binder) : string =
+ bv_to_string (bv_of_binder b) //TODO: print aqual, attributesval flatten_name : name -> Tot string
+let rec flatten_name ns =
+ match ns with
+ | [] -> ""
+ | [n] -> n
+ | n::ns -> n ^ "." ^ flatten_name nsHelpers for dealing with nested applications and arrows
+let rec collect_app' (args : list argv) (t : term) : Tot (term * list argv) (decreases t) =
+ match inspect_ln t with
+ | Tv_App l r ->
+ collect_app' (r::args) l
+ | _ -> (t, args)val collect_app : term -> term * list argv
+let collect_app = collect_app' []let rec mk_app (t : term) (args : list argv) : Tot term (decreases args) =
+ match args with
+ | [] -> t
+ | (x::xs) -> mk_app (pack_ln (Tv_App t x)) xsHelper for when all arguments are explicit
+let mk_e_app (t : term) (args : list term) : Tot term =
+ let e t = (t, Q_Explicit) in
+ mk_app t (List.Tot.Base.map e args)let rec mk_tot_arr_ln (bs: list binder) (cod : term) : Tot term (decreases bs) =
+ match bs with
+ | [] -> cod
+ | (b::bs) -> pack_ln (Tv_Arrow b (pack_comp (C_Total (mk_tot_arr_ln bs cod) [])))private
+let rec collect_arr' (bs : list binder) (c : comp) : Tot (list binder * comp) (decreases c) =
+ begin match inspect_comp c with
+ | C_Total t _ ->
+ begin match inspect_ln t with
+ | Tv_Arrow b c ->
+ collect_arr' (b::bs) c
+ | _ ->
+ (bs, c)
+ end
+ | _ -> (bs, c)
+ endval collect_arr_ln_bs : typ -> list binder * comp
+let collect_arr_ln_bs t =
+ let (bs, c) = collect_arr' [] (pack_comp (C_Total t [])) in
+ (List.Tot.Base.rev bs, c)val collect_arr_ln : typ -> list typ * comp
+let collect_arr_ln t =
+ let (bs, c) = collect_arr' [] (pack_comp (C_Total t [])) in
+ let ts = List.Tot.Base.map type_of_binder bs in
+ (List.Tot.Base.rev ts, c)private
+let rec collect_abs' (bs : list binder) (t : term) : Tot (list binder * term) (decreases t) =
+ match inspect_ln t with
+ | Tv_Abs b t' ->
+ collect_abs' (b::bs) t'
+ | _ -> (bs, t)val collect_abs_ln : term -> list binder * term
+let collect_abs_ln t =
+ let (bs, t') = collect_abs' [] t in
+ (List.Tot.Base.rev bs, t')let fv_to_string (fv:fv) : string = implode_qn (inspect_fv fv)let compare_name (n1 n2 : name) : order =
+ compare_list (fun s1 s2 -> order_from_int (compare_string s1 s2)) n1 n2let compare_fv (f1 f2 : fv) : order =
+ compare_name (inspect_fv f1) (inspect_fv f2)let compare_const (c1 c2 : vconst) : order =
+ match c1, c2 with
+ | C_Unit, C_Unit -> Eq
+ | C_Int i, C_Int j -> order_from_int (i - j)
+ | C_True, C_True -> Eq
+ | C_False, C_False -> Eq
+ | C_String s1, C_String s2 -> order_from_int (compare_string s1 s2)
+ | C_Range r1, C_Range r2 -> Eq
+ | C_Reify, C_Reify -> Eq
+ | C_Reflect l1, C_Reflect l2 -> compare_name l1 l2
+ | C_Unit, _ -> Lt | _, C_Unit -> Gt
+ | C_Int _, _ -> Lt | _, C_Int _ -> Gt
+ | C_True, _ -> Lt | _, C_True -> Gt
+ | C_False, _ -> Lt | _, C_False -> Gt
+ | C_String _, _ -> Lt | _, C_String _ -> Gt
+ | C_Range _, _ -> Lt | _, C_Range _ -> Gt
+ | C_Reify, _ -> Lt | _, C_Reify -> Gt
+ | C_Reflect _, _ -> Lt | _, C_Reflect _ -> Gtlet compare_binder (b1 b2 : binder) : order =
+ let bv1, _ = inspect_binder b1 in
+ let bv2, _ = inspect_binder b2 in
+ compare_bv bv1 bv2let rec compare_term (s t : term) : Tot order (decreases s) =
+ match inspect_ln s, inspect_ln t with
+ | Tv_Var sv, Tv_Var tv ->
+ compare_bv sv tv| Tv_BVar sv, Tv_BVar tv ->
+ compare_bv sv tv| Tv_FVar sv, Tv_FVar tv ->
+ compare_fv sv tv| Tv_App h1 a1, Tv_App h2 a2 ->
+ lex (compare_term h1 h2) (fun () -> compare_argv a1 a2)| Tv_Abs b1 e1, Tv_Abs b2 e2 ->
+ lex (compare_binder b1 b2) (fun () -> compare_term e1 e2)| Tv_Refine bv1 e1, Tv_Refine bv2 e2 ->
+ lex (compare_bv bv1 bv2) (fun () -> compare_term e1 e2)| Tv_Arrow b1 e1, Tv_Arrow b2 e2 ->
+ lex (compare_binder b1 b2) (fun () -> compare_comp e1 e2)| Tv_Type (), Tv_Type () ->
+ Eq| Tv_Const c1, Tv_Const c2 ->
+ compare_const c1 c2| Tv_Uvar u1 _, Tv_Uvar u2 _->
+ compare_int u1 u2| Tv_Let _r1 _attrs1 bv1 t1 t1', Tv_Let _r2 _attrs2 bv2 t2 t2' ->
+ lex (compare_bv bv1 bv2) (fun () ->
+ lex (compare_term t1 t2) (fun () ->
+ compare_term t1' t2'))| Tv_Match _ _ _, Tv_Match _ _ _ ->
+ Eq // TODO| Tv_AscribedT e1 t1 tac1, Tv_AscribedT e2 t2 tac2 ->
+ lex (compare_term e1 e2) (fun () ->
+ lex (compare_term t1 t2) (fun () ->
+ match tac1, tac2 with
+ | None, None -> Eq
+ | None, _ -> Lt
+ | _, None -> Gt
+ | Some e1, Some e2 -> compare_term e1 e2))| Tv_AscribedC e1 c1 tac1, Tv_AscribedC e2 c2 tac2 ->
+ lex (compare_term e1 e2) (fun () ->
+ lex (compare_comp c1 c2) (fun () ->
+ match tac1, tac2 with
+ | None, None -> Eq
+ | None, _ -> Lt
+ | _, None -> Gt
+ | Some e1, Some e2 -> compare_term e1 e2))| Tv_Unknown, Tv_Unknown ->
+ EqFrom here onwards, they must have different constructors. Order them arbitrarilly as in the definition.
+ | Tv_Var _, _ -> Lt | _, Tv_Var _ -> Gt
+ | Tv_BVar _, _ -> Lt | _, Tv_BVar _ -> Gt
+ | Tv_FVar _, _ -> Lt | _, Tv_FVar _ -> Gt
+ | Tv_App _ _, _ -> Lt | _, Tv_App _ _ -> Gt
+ | Tv_Abs _ _, _ -> Lt | _, Tv_Abs _ _ -> Gt
+ | Tv_Arrow _ _, _ -> Lt | _, Tv_Arrow _ _ -> Gt
+ | Tv_Type (), _ -> Lt | _, Tv_Type () -> Gt
+ | Tv_Refine _ _, _ -> Lt | _, Tv_Refine _ _ -> Gt
+ | Tv_Const _, _ -> Lt | _, Tv_Const _ -> Gt
+ | Tv_Uvar _ _, _ -> Lt | _, Tv_Uvar _ _ -> Gt
+ | Tv_Match _ _ _, _ -> Lt | _, Tv_Match _ _ _ -> Gt
+ | Tv_AscribedT _ _ _, _ -> Lt | _, Tv_AscribedT _ _ _ -> Gt
+ | Tv_AscribedC _ _ _, _ -> Lt | _, Tv_AscribedC _ _ _ -> Gt
+ | Tv_Unknown, _ -> Lt | _, Tv_Unknown -> Gt
+and compare_term_list (l1 l2:list term) : Tot order (decreases l1) =
+ match l1, l2 with
+ | [], [] -> Eq
+ | [], _ -> Lt
+ | _, [] -> Gt
+ | hd1::tl1, hd2::tl2 ->
+ lex (compare_term hd1 hd2) (fun () -> compare_term_list tl1 tl2)and compare_argv (a1 a2 : argv) : Tot order (decreases a1) =
+ let a1, q1 = a1 in
+ let a2, q2 = a2 in
+ match q1, q2 withWe should never see Q_Meta here
+ | Q_Implicit, Q_Explicit -> Lt
+ | Q_Explicit, Q_Implicit -> Gt
+ | _, _ -> compare_term a1 a2
+and compare_comp (c1 c2 : comp) : Tot order (decreases c1) =
+ let cv1 = inspect_comp c1 in
+ let cv2 = inspect_comp c2 in
+ match cv1, cv2 with
+ | C_Total t1 md1, C_Total t2 md2 -> lex (compare_term t1 t2)
+ (fun () -> compare_term_list md1 md2)| C_GTotal t1 md1, C_GTotal t2 md2 -> lex (compare_term t1 t2)
+ (fun () -> compare_term_list md1 md2)| C_Lemma p1 q1 s1, C_Lemma p2 q2 s2 ->
+ lex (compare_term p1 p2)
+ (fun () ->
+ lex (compare_term q1 q2)
+ (fun () -> compare_term s1 s2)
+ )| C_Eff _us1 eff1 res1 args1,
+ C_Eff _us2 eff2 res2 args2 ->This could be more complex, not sure it is worth it
+lex (compare_name eff1 eff2) (fun () -> compare_term res1 res2)| C_Total _ _, _ -> Lt | _, C_Total _ _ -> Gt
+| C_GTotal _ _, _ -> Lt | _, C_GTotal _ _ -> Gt
+| C_Lemma _ _ _, _ -> Lt | _, C_Lemma _ _ _ -> Gt
+| C_Eff _ _ _ _, _ -> Lt | _, C_Eff _ _ _ _ -> Gtlet mk_stringlit (s : string) : term =
+ pack_ln (Tv_Const (C_String s))let mk_strcat (t1 t2 : term) : term =
+ mk_e_app (pack_ln (Tv_FVar (pack_fv ["Prims"; "strcat"]))) [t1; t2]let mk_cons (h t : term) : term =
+ mk_e_app (pack_ln (Tv_FVar (pack_fv cons_qn))) [h; t]let mk_cons_t (ty h t : term) : term =
+ mk_app (pack_ln (Tv_FVar (pack_fv cons_qn))) [(ty, Q_Implicit); (h, Q_Explicit); (t, Q_Explicit)]let rec mk_list (ts : list term) : term =
+ match ts with
+ | [] -> pack_ln (Tv_FVar (pack_fv nil_qn))
+ | t::ts -> mk_cons t (mk_list ts)let mktuple_n (ts : list term) : term =
+ assume (List.Tot.Base.length ts <= 8);
+ match List.Tot.Base.length ts with
+ | 0 -> pack_ln (Tv_Const C_Unit)
+ | 1 -> let [x] = ts in x
+ | n -> begin
+ let qn = match n with
+ | 2 -> mktuple2_qn
+ | 3 -> mktuple3_qn
+ | 4 -> mktuple4_qn
+ | 5 -> mktuple5_qn
+ | 6 -> mktuple6_qn
+ | 7 -> mktuple7_qn
+ | 8 -> mktuple8_qn
+ in mk_e_app (pack_ln (Tv_FVar (pack_fv qn))) ts
+ endlet destruct_tuple (t : term) : option (list term) =
+ let head, args = collect_app t in
+ match inspect_ln head with
+ | Tv_FVar fv ->
+ if List.Tot.Base.mem
+ (inspect_fv fv) [mktuple2_qn; mktuple3_qn; mktuple4_qn; mktuple5_qn;
+ mktuple6_qn; mktuple7_qn; mktuple8_qn]
+ then Some (List.Tot.Base.concatMap (fun (t, q) ->
+ match q with
+ | Q_Explicit -> [t]
+ | _ -> []) args)
+ else None
+ | _ -> Nonelet mkpair (t1 t2 : term) : term =
+ mktuple_n [t1;t2]let rec head (t : term) : term =
+ match inspect_ln t with
+ | Tv_Match t _ _
+ | Tv_Let _ _ _ t _
+ | Tv_Abs _ t
+ | Tv_Refine _ t
+ | Tv_App t _
+ | Tv_AscribedT t _ _
+ | Tv_AscribedC t _ _ -> head t| Tv_Unknown
+| Tv_Uvar _ _
+| Tv_Const _
+| Tv_Type _
+| Tv_Var _
+| Tv_BVar _
+| Tv_FVar _
+| Tv_Arrow _ _ -> tlet nameof (t : term) : string =
+ match inspect_ln t with
+ | Tv_FVar fv -> implode_qn (inspect_fv fv)
+ | _ -> "?"let is_uvar (t : term) : bool =
+ match inspect_ln (head t) with
+ | Tv_Uvar _ _ -> true
+ | _ -> falselet binder_set_qual (q:aqualv) (b:binder) : Tot binder =
+ let bv, (_, attrs) = inspect_binder b in
+ pack_binder bv q attrsSet a vconfig for a sigelt
+val add_check_with : vconfig -> sigelt -> Tot sigelt
+let add_check_with vcfg se =
+ let attrs = sigelt_attrs se in
+ let vcfg_t = embed_vconfig vcfg in
+ let t = `(check_with (`#vcfg_t)) in
+ set_sigelt_attrs (t :: attrs) sefsdoc: no-summary-found
-fsdoc: no-comment-found
+Cannot open FStar.Tactics.Derived here
+let fresh_bv = fresh_bv_named "x"noeq type comparison =
+ | Eq of option typ (* Propositional equality (eq2), maybe annotated *)
+ | BoolEq of option typ (* Decidable, boolean equality (eq), maybe annotated *)
+ | Lt | Le | Gt | Ge (* Orderings, at type `int` (and subtypes) *)noeq type formula =
+ | True_ : formula
+ | False_ : formula
+ | Comp : comparison -> term -> term -> formula
+ | And : term -> term -> formula
+ | Or : term -> term -> formula
+ | Not : term -> formula
+ | Implies: term -> term -> formula
+ | Iff : term -> term -> formula
+ | Forall : bv -> term -> formula
+ | Exists : bv -> term -> formula
+ | App : term -> term -> formula
+ | Name : bv -> formula
+ | FV : fv -> formula
+ | IntLit : int -> formula
+ | F_Unknown : formula // Also a baked-in "None"let mk_Forall (typ : term) (pred : term) : Tac formula =
+ let b = pack_bv ({ bv_ppname = "x";
+ bv_sort = typ;
+ bv_index = 0; }) in
+ Forall b (pack_ln (Tv_App pred (pack_ln (Tv_BVar b), Q_Explicit)))let mk_Exists (typ : term) (pred : term) : Tac formula =
+ let b = pack_bv ({ bv_ppname = "x";
+ bv_sort = typ;
+ bv_index = 0; }) in
+ Exists b (pack_ln (Tv_App pred (pack_ln (Tv_BVar b), Q_Explicit)))let term_as_formula' (t:term) : Tac formula =
+ match inspect_ln t with
+ | Tv_Var n ->
+ Name n| Tv_FVar fv ->Cannot use when clauses when verifying!
let qn = inspect_fv fv in
+if qn = true_qn then True_
+else if qn = false_qn then False_
+else FV fvTODO: l_Forall +...or should we just try to drop all squashes? +TODO: b2t at this point ? +Non-annotated comparisons
+| Tv_App h0 t -> begin
+ let (h, ts) = collect_app h0 in
+ match inspect_ln h, ts@[t] with
+ | Tv_FVar fv, [(a1, Q_Implicit); (a2, Q_Explicit); (a3, Q_Explicit)] ->
+ let qn = inspect_fv fv in
+ if qn = eq2_qn then Comp (Eq (Some a1)) a2 a3
+ else if qn = eq1_qn then Comp (BoolEq (Some a1)) a2 a3
+ else if qn = lt_qn then Comp Lt a2 a3
+ else if qn = lte_qn then Comp Le a2 a3
+ else if qn = gt_qn then Comp Gt a2 a3
+ else if qn = gte_qn then Comp Ge a2 a3
+ else App h0 (fst t)
+ | Tv_FVar fv, [(a1, Q_Explicit); (a2, Q_Explicit)] ->
+ let qn = inspect_fv fv in
+ if qn = imp_qn then Implies a1 a2
+ else if qn = and_qn then And a1 a2
+ else if qn = iff_qn then Iff a1 a2
+ else if qn = or_qn then Or a1 a2
+ else if qn = eq2_qn then Comp (Eq None) a1 a2
+ else if qn = eq1_qn then Comp (BoolEq None) a1 a2
+ else App h0 (fst t)| Tv_FVar fv, [(a1, Q_Implicit); (a2, Q_Explicit)] ->
+ let qn = inspect_fv fv in
+ if qn = forall_qn then mk_Forall a1 a2
+ else if qn = exists_qn then mk_Exists a1 a2
+ else App h0 (fst t)
+| Tv_FVar fv, [(a, Q_Explicit)] ->
+ let qn = inspect_fv fv in
+ if qn = not_qn then Not a
+ else App h0 (fst t)
+| _ ->
+ App h0 (fst t)
+end| Tv_Const (C_Int i) ->
+ IntLit iTODO: all these. Do we want to export them?
+| Tv_Type _
+| Tv_Abs _ _
+| Tv_Refine _ _
+| Tv_Const (C_Unit)
+| _ ->
+ F_Unknownlet rec is_name_imp (nm : name) (t : term) : bool =
+ begin match inspect_ln t with
+ | Tv_FVar fv ->
+ if inspect_fv fv = nm
+ then true
+ else false
+ | Tv_App l (_, Q_Implicit) -> // ignore implicits
+ is_name_imp nm l
+ | _ -> false
+ endlet unsquash (t : term) : option term =
+ match inspect_ln t with
+ | Tv_App l (r, Q_Explicit) ->
+ if is_name_imp squash_qn l
+ then Some r
+ else None
+ | _ -> Nonelet unsquash_total (t : term) : term =
+ match inspect_ln t with
+ | Tv_App l (r, Q_Explicit) ->
+ if is_name_imp squash_qn l
+ then r
+ else t
+ | _ -> tUnsquashing
+let term_as_formula (t:term) : Tac formula =
+ match unsquash t with
+ | None -> F_Unknown
+ | Some t ->
+ term_as_formula' tlet term_as_formula_total (t:term) : Tac formula =
+ term_as_formula' (unsquash_total t)let formula_as_term_view (f:formula) : Tot term_view =
+ let mk_app' tv args = List.Tot.Base.fold_left (fun tv a -> Tv_App (pack_ln tv) a) tv args in
+ let e = Q_Explicit in
+ let i = Q_Implicit in
+ match f with
+ | True_ -> Tv_FVar (pack_fv true_qn)
+ | False_ -> Tv_FVar (pack_fv false_qn)
+ | Comp (Eq None) l r -> mk_app' (Tv_FVar (pack_fv eq2_qn)) [(l,e);(r,e)]
+ | Comp (Eq (Some t)) l r -> mk_app' (Tv_FVar (pack_fv eq2_qn)) [(t,i);(l,e);(r,e)]
+ | Comp (BoolEq None) l r -> mk_app' (Tv_FVar (pack_fv eq1_qn)) [(l,e);(r,e)]
+ | Comp (BoolEq (Some t)) l r -> mk_app' (Tv_FVar (pack_fv eq1_qn)) [(t,i);(l,e);(r,e)]
+ | Comp Lt l r -> mk_app' (Tv_FVar (pack_fv lt_qn)) [(l,e);(r,e)]
+ | Comp Le l r -> mk_app' (Tv_FVar (pack_fv lte_qn)) [(l,e);(r,e)]
+ | Comp Gt l r -> mk_app' (Tv_FVar (pack_fv gt_qn)) [(l,e);(r,e)]
+ | Comp Ge l r -> mk_app' (Tv_FVar (pack_fv gte_qn)) [(l,e);(r,e)]
+ | And p q -> mk_app' (Tv_FVar (pack_fv and_qn)) [(p,e);(q,e)]
+ | Or p q -> mk_app' (Tv_FVar (pack_fv or_qn)) [(p,e);(q,e)]
+ | Implies p q -> mk_app' (Tv_FVar (pack_fv imp_qn)) [(p,e);(q,e)]
+ | Not p -> mk_app' (Tv_FVar (pack_fv not_qn)) [(p,e)]
+ | Iff p q -> mk_app' (Tv_FVar (pack_fv iff_qn)) [(p,e);(q,e)]
+ | Forall b t -> Tv_Unknown // TODO: decide on meaning of this
+ | Exists b t -> Tv_Unknown // TODO: ^| App p q ->
+ Tv_App p (q, Q_Explicit)| Name b ->
+ Tv_Var b| FV fv ->
+ Tv_FVar fv| IntLit i ->
+ Tv_Const (C_Int i)| F_Unknown ->
+ Tv_Unknownlet formula_as_term (f:formula) : Tot term =
+ pack_ln (formula_as_term_view f)let formula_to_string (f:formula) : string =
+ match f with
+ | True_ -> "True_"
+ | False_ -> "False_"
+ | Comp (Eq mt) l r -> "Eq" ^
+ (match mt with
+ | None -> ""
+ | Some t -> " (" ^ term_to_string t ^ ")") ^
+ " (" ^ term_to_string l ^ ") (" ^ term_to_string r ^ ")"
+ | Comp (BoolEq mt) l r -> "BoolEq" ^
+ (match mt with
+ | None -> ""
+ | Some t -> " (" ^ term_to_string t ^ ")") ^
+ " (" ^ term_to_string l ^ ") (" ^ term_to_string r ^ ")"
+ | Comp Lt l r -> "Lt (" ^ term_to_string l ^ ") (" ^ term_to_string r ^ ")"
+ | Comp Le l r -> "Le (" ^ term_to_string l ^ ") (" ^ term_to_string r ^ ")"
+ | Comp Gt l r -> "Gt (" ^ term_to_string l ^ ") (" ^ term_to_string r ^ ")"
+ | Comp Ge l r -> "Ge (" ^ term_to_string l ^ ") (" ^ term_to_string r ^ ")"
+ | And p q -> "And (" ^ term_to_string p ^ ") (" ^ term_to_string q ^ ")"
+ | Or p q -> "Or (" ^ term_to_string p ^ ") (" ^ term_to_string q ^ ")"
+ | Implies p q -> "Implies (" ^ term_to_string p ^ ") (" ^ term_to_string q ^ ")"
+ | Not p -> "Not (" ^ term_to_string p ^ ")"
+ | Iff p q -> "Iff (" ^ term_to_string p ^ ") (" ^ term_to_string q ^ ")"
+ | Forall bs t -> "Forall <bs> (" ^ term_to_string t ^ ")"
+ | Exists bs t -> "Exists <bs> (" ^ term_to_string t ^ ")"
+ | App p q -> "App (" ^ term_to_string p ^ ") (" ^ term_to_string q ^ ")"
+ | Name bv -> "Name (" ^ bv_to_string bv ^ ")"
+ | FV fv -> "FV (" ^ flatten_name (inspect_fv fv) ^ ")"
+ | IntLit i -> "Int " ^ string_of_int i
+ | F_Unknown -> "?"fsdoc: no-summary-found
-fsdoc: no-comment-found
+assume new type binder
+assume new type bv
+assume new type term
+assume new type env
+assume new type fv
+assume new type comp
+assume new type sigelt // called `def` in the paper, but we keep the internal name here
+assume new type ctx_uvar_and_subst
+assume new type letbindingtype name : eqtype = list string
+type ident = range * string
+type univ_name = ident
+type typ = term
+type binders = list binderfsdoc: no-summary-found
-fsdoc: no-comment-found
+fsdoc: no-summary-found
-fsdoc: no-comment-found
-val closure_step:Unidentified product: [#a:Type0] Unidentified product: [r:relation a] Unidentified product: [x:a] Unidentified product: [y:a] (Lemma ((requires r x y)) ((ensures closure r x y)) (Prims.Cons (SMTPat (closure r x y)) (Prims.Nil )))++This module defines the reflexive transitive closure of a +relation. That is, the smallest preorder that includes it.
+Closures are convenient for defining monotonic memory references:
++
+- +
+Define a
+steprelation and takeclosure stepas the +monotonic relation of the reference.- +
+To witness a property of the value of the reference, one must +show that the property is stable with respect to
+closure step, +but this boils down to proving that is stable with respect to +step(see lemmastable_on_closurebelow).See examples/preorder/Closure.fst for usage examples.
+
val closure (#a:Type u#a) (r:relation a) : preorder aclosure r includes r
val closure_inversion:Unidentified product: [#a:Type0] Unidentified product: [r:relation a] Unidentified product: [x:a] Unidentified product: [y:a] (Lemma ((requires closure r x y)) ((ensures \/(==(x, y), (exists z.{:pattern } /\(r x z, closure r z y))))) (Prims.Cons (SMTPat (closure r x y)) (Prims.Nil )))val closure_step: #a:Type u#a -> r:relation a -> x:a -> y:a
+ -> Lemma (requires r x y) (ensures closure r x y)
+ [SMTPat (closure r x y)]closure r is the smallest preorder that includes r
val stable_on_closure:Unidentified product: [#a:Type0] Unidentified product: [r:relation a] Unidentified product: [p:(Unidentified product: [a] Type0)] Unidentified product: [p_stable_on_r:(squash (forall x y.{:pattern (p y); (r x y)} ==>(/\(p x, r x y), p y)))] (Lemma (forall x y.{:pattern (closure r x y)} ==>(/\(p x, closure r x y), p y)))val closure_inversion: #a:Type u#a -> r:relation a -> x:a -> y:a
+ -> Lemma (requires closure r x y)
+ (ensures x == y \/ (exists z. r x z /\ closure r z y))r is stable on closure rr is stable on closure r
+val stable_on_closure: #a:Type u#a -> r:relation a -> p:(a -> Type0)
+ -> p_stable_on_r: (squash (forall x y.{:pattern (p y); (r x y)} p x /\ r x y ==> p y))
+ -> Lemma (forall x y.{:pattern (closure r x y)} p x /\ closure r x y ==> p y)fsdoc: no-summary-found
-fsdoc: no-comment-found
-*** Global ST (GST) effect with put, get, witness, and recall ******* ST effect ****Opens module FStar.TSet
+Opens module FStar.Heap
+Opens module FStar.Preorder
+Aliases module FStar.Monotonic.Witnessed as W
new_effect GST = STATE_h heaplet gst_pre = st_pre_h heap
+let gst_post' (a:Type) (pre:Type) = st_post_h' heap a pre
+let gst_post (a:Type) = st_post_h heap a
+let gst_wp (a:Type) = st_wp_h heap aunfold let lift_div_gst (a:Type) (wp:pure_wp a) (p:gst_post a) (h:heap) = wp (fun a -> p a h)
+sub_effect DIV ~> GST = lift_div_gstlet heap_rel (h1:heap) (h2:heap) =
+ forall (a:Type0) (rel:preorder a) (r:mref a rel). h1 `contains` r ==>
+ (h2 `contains` r /\ rel (sel h1 r) (sel h2 r))assume val gst_get: unit -> GST heap (fun p h0 -> p h0 h0)
+assume val gst_put: h1:heap -> GST unit (fun p h0 -> heap_rel h0 h1 /\ p () h1)type heap_predicate = heap -> Type0let stable (p:heap_predicate) =
+ forall (h1:heap) (h2:heap). (p h1 /\ heap_rel h1 h2) ==> p h2[@@"opaque_to_smt"]
+let witnessed (p:heap_predicate{stable p}) : Type0 = W.witnessed heap_rel passume val gst_witness: p:heap_predicate -> GST unit (fun post h0 -> stable p /\ p h0 /\ (witnessed p ==> post () h0))
+assume val gst_recall: p:heap_predicate -> GST unit (fun post h0 -> stable p /\ witnessed p /\ (p h0 ==> post () h0))val lemma_functoriality (p:heap_predicate{stable p /\ witnessed p})
+ (q:heap_predicate{stable q /\ (forall (h:heap). p h ==> q h)})
+ :Lemma (ensures (witnessed q))
+let lemma_functoriality p q =
+ reveal_opaque (`%witnessed) witnessed;
+ W.lemma_witnessed_weakening heap_rel p qlet st_pre = gst_pre
+let st_post' = gst_post'
+let st_post = gst_post
+let st_wp = gst_wpnew_effect STATE = GSTunfold let lift_gst_state (a:Type) (wp:gst_wp a) = wp
+sub_effect GST ~> STATE = lift_gst_stateeffect State (a:Type) (wp:st_wp a) = STATE a wpeffect ST (a:Type) (pre:st_pre) (post: (h:heap -> Tot (st_post' a (pre h)))) =
+ STATE a (fun (p:st_post a) (h:heap) -> pre h /\ (forall a h1. post h a h1 ==> p a h1))
+effect St (a:Type) = ST a (fun h -> True) (fun h0 r h1 -> True)let contains_pred (#a:Type0) (#rel:preorder a) (r:mref a rel) = fun h -> h `contains` rtype mref (a:Type0) (rel:preorder a) = r:Heap.mref a rel{is_mm r = false /\ witnessed (contains_pred r)}let recall (#a:Type) (#rel:preorder a) (r:mref a rel) :STATE unit (fun p h -> Heap.contains h r ==> p () h)
+ = gst_recall (contains_pred r)let alloc (#a:Type) (#rel:preorder a) (init:a)
+ :ST (mref a rel)
+ (fun h -> True)
+ (fun h0 r h1 -> fresh r h0 h1 /\ modifies Set.empty h0 h1 /\ sel h1 r == init)
+ = let h0 = gst_get () in
+ let r, h1 = alloc rel h0 init false in
+ gst_put h1;
+ gst_witness (contains_pred r);
+ rlet read (#a:Type) (#rel:preorder a) (r:mref a rel) :STATE a (fun p h -> p (sel h r) h)
+ = let h0 = gst_get () in
+ gst_recall (contains_pred r);
+ Heap.lemma_sel_equals_sel_tot_for_contained_refs h0 r;
+ sel_tot h0 rlet write (#a:Type) (#rel:preorder a) (r:mref a rel) (v:a)
+ : ST unit
+ (fun h -> rel (sel h r) v)
+ (fun h0 x h1 -> rel (sel h0 r) v /\ h0 `contains` r /\
+ modifies (Set.singleton (addr_of r)) h0 h1 /\ equal_dom h0 h1 /\
+ sel h1 r == v)
+ = let h0 = gst_get () in
+ gst_recall (contains_pred r);
+ let h1 = upd_tot h0 r v in
+ Heap.lemma_distinct_addrs_distinct_preorders ();
+ Heap.lemma_distinct_addrs_distinct_mm ();
+ Heap.lemma_upd_equals_upd_tot_for_contained_refs h0 r v;
+ gst_put h1let get (u:unit) :ST heap (fun h -> True) (fun h0 h h1 -> h0==h1 /\ h==h1) = gst_get ()let op_Bang (#a:Type) (#rel:preorder a) (r:mref a rel)
+ : STATE a (fun p h -> p (sel h r) h)
+= read #a #rel rlet op_Colon_Equals (#a:Type) (#rel:preorder a) (r:mref a rel) (v:a)
+ : ST unit
+ (fun h -> rel (sel h r) v)
+ (fun h0 x h1 -> rel (sel h0 r) v /\ h0 `contains` r /\
+ modifies (Set.singleton (addr_of r)) h0 h1 /\ equal_dom h0 h1 /\
+ sel h1 r == v)
+= write #a #rel r vtype ref (a:Type0) = mref a (trivial_preorder a)let modifies_none (h0:heap) (h1:heap) = modifies !{} h0 h1fsdoc: no-summary-found
-fsdoc: no-comment-found
+A logical theory of sequences indexed by natural numbers in [0, n)
+#set-options "--initial_fuel 0 --max_fuel 0 --initial_ifuel 1 --max_ifuel 1"
+List
+new val seq (a : Type u#a) : Type u#aDestructors
+val length: #a:Type -> seq a -> Tot natval index: #a:Type -> s:seq a -> i:nat{i < length s} -> Tot aval create: #a:Type -> nat -> a -> Tot (seq a)private val init_aux (#a:Type) (len:nat) (k:nat{k < len}) (contents:(i:nat { i < len } -> Tot a))
+ :Tot (seq a)inline_for_extraction val init: #a:Type -> len:nat -> contents: (i:nat { i < len } -> Tot a) -> Tot (seq a)private val init_aux_ghost (#a:Type) (len:nat) (k:nat{k < len}) (contents:(i:nat { i < len } -> GTot a))
+ : GTot (seq a)inline_for_extraction val init_ghost: #a:Type -> len:nat -> contents: (i:nat { i < len } -> GTot a) -> GTot (seq a)val empty (#a:Type) : Tot (s:(seq a){length s=0})[@@(deprecated "Seq.empty")]
+unfold
+let createEmpty (#a:Type)
+ : Tot (s:(seq a){length s=0})
+ = empty #aval lemma_empty (#a:Type) (s:seq a) : Lemma (length s = 0 ==> s == empty #a)val upd: #a:Type -> s:seq a -> n:nat{n < length s} -> a -> Tot (seq a)val append: #a:Type -> seq a -> seq a -> Tot (seq a)let op_At_Bar (#a:Type) (s1:seq a) (s2:seq a) = append s1 s2val slice: #a:Type -> s:seq a -> i:nat -> j:nat{i <= j && j <= length s} -> Tot (seq a)Lemmas about length
+val lemma_create_len: #a:Type -> n:nat -> i:a -> Lemma
+ (requires True)
+ (ensures (length (create n i) = n))
+ [SMTPat (length (create n i))]val lemma_init_len: #a:Type -> n:nat -> contents: (i:nat { i < n } -> Tot a) -> Lemma
+ (requires True)
+ (ensures (length (init n contents) = n))
+ [SMTPat (length (init n contents))]private val lemma_init_aux_len (#a:Type) (n:nat) (k:nat{k < n}) (contents:(i:nat{ i < n } -> Tot a))
+ : Lemma (requires True)
+ (ensures (length (init_aux n k contents) = n - k))
+ [SMTPat (length (init_aux n k contents))]val lemma_init_ghost_len: #a:Type -> n:nat -> contents: (i:nat { i < n } -> GTot a) -> Lemma
+ (requires True)
+ (ensures (length (init_ghost n contents) = n))
+ [SMTPat (length (init_ghost n contents))]private val lemma_init_ghost_aux_len (#a:Type) (n:nat) (k:nat{k < n}) (contents:(i:nat{ i < n } -> GTot a))
+ : Lemma (requires True)
+ (ensures (length (init_aux_ghost n k contents) = n - k))
+ [SMTPat (length (init_aux_ghost n k contents))]val lemma_len_upd: #a:Type -> n:nat -> v:a -> s:seq a{n < length s} -> Lemma
+ (requires True)
+ (ensures (length (upd s n v) = length s))
+ [SMTPat (length (upd s n v))]val lemma_len_append: #a:Type -> s1:seq a -> s2:seq a -> Lemma
+ (requires True)
+ (ensures (length (append s1 s2) = length s1 + length s2))
+ [SMTPat (length (append s1 s2))]val lemma_len_slice: #a:Type -> s:seq a -> i:nat -> j:nat{i <= j && j <= length s} -> Lemma
+ (requires True)
+ (ensures (length (slice s i j) = j - i))
+ [SMTPat (length (slice s i j))]Lemmas about index
+val lemma_index_create: #a:Type -> n:nat -> v:a -> i:nat{i < n} -> Lemma
+ (requires True)
+ (ensures (index (create n v) i == v))
+ [SMTPat (index (create n v) i)]val lemma_index_upd1: #a:Type -> s:seq a -> n:nat{n < length s} -> v:a -> Lemma
+ (requires True)
+ (ensures (index (upd s n v) n == v))
+ [SMTPat (index (upd s n v) n)]val lemma_index_upd2: #a:Type -> s:seq a -> n:nat{n < length s} -> v:a -> i:nat{i<>n /\ i < length s} -> Lemma
+ (requires True)
+ (ensures (index (upd s n v) i == index s i))
+ [SMTPat (index (upd s n v) i)]val lemma_index_app1: #a:Type -> s1:seq a -> s2:seq a -> i:nat{i < length s1} -> Lemma
+ (requires True)
+ (ensures (index (append s1 s2) i == index s1 i))
+ [SMTPat (index (append s1 s2) i)]val lemma_index_app2: #a:Type -> s1:seq a -> s2:seq a -> i:nat{i < length s1 + length s2 /\ length s1 <= i} -> Lemma
+ (requires True)
+ (ensures (index (append s1 s2) i == index s2 (i - length s1)))
+ [SMTPat (index (append s1 s2) i)]val lemma_index_slice: #a:Type -> s:seq a -> i:nat -> j:nat{i <= j /\ j <= length s} -> k:nat{k < j - i} -> Lemma
+ (requires True)
+ (ensures (index (slice s i j) k == index s (k + i)))
+ [SMTPat (index (slice s i j) k)]val hasEq_lemma: a:Type -> Lemma (requires (hasEq a)) (ensures (hasEq (seq a))) [SMTPat (hasEq (seq a))][@@ remove_unused_type_parameters [0; 1; 2]]
+val equal (#a:Type) (s1:seq a) (s2:seq a) : Tot propdecidable equality
+private val eq_i:
+ #a:eqtype -> s1:seq a -> s2:seq a{length s1 = length s2}
+ -> i:nat{i <= length s1}
+ -> Tot (r:bool{r <==> (forall j. (j >= i /\ j < length s1) ==> (index s1 j = index s2 j))})val eq: #a:eqtype -> s1:seq a -> s2:seq a -> Tot (r:bool{r <==> equal s1 s2})val lemma_eq_intro: #a:Type -> s1:seq a -> s2:seq a -> Lemma
+ (requires (length s1 = length s2
+ /\ (forall (i:nat{i < length s1}).{:pattern (index s1 i); (index s2 i)} (index s1 i == index s2 i))))
+ (ensures (equal s1 s2))
+ [SMTPat (equal s1 s2)]val lemma_eq_refl: #a:Type -> s1:seq a -> s2:seq a -> Lemma
+ (requires (s1 == s2))
+ (ensures (equal s1 s2))
+ [SMTPat (equal s1 s2)]val lemma_eq_elim: #a:Type -> s1:seq a -> s2:seq a -> Lemma
+ (requires (equal s1 s2))
+ (ensures (s1==s2))
+ [SMTPat (equal s1 s2)]Properties of append
val append_assoc
+ (#a: Type)
+ (s1 s2 s3: seq a)
+: Lemma
+ (ensures (append (append s1 s2) s3 == append s1 (append s2 s3)))val append_empty_l
+ (#a: Type)
+ (s: seq a)
+: Lemma
+ (ensures (append empty s == s))val append_empty_r
+ (#a: Type)
+ (s: seq a)
+: Lemma
+ (ensures (append s empty == s))val init_index (#a:Type) (len:nat) (contents:(i:nat { i < len } -> Tot a))
+ : Lemma (requires True)
+ (ensures (forall (i:nat{i < len}). index (init len contents) i == contents i))val init_index_ (#a:Type) (len:nat) (contents:(i:nat { i < len } -> Tot a)) (j: nat)
+ : Lemma (requires j < len)
+ (ensures (index (init len contents) j == contents j))
+ [SMTPat (index (init len contents) j)]val init_ghost_index (#a:Type) (len:nat) (contents:(i:nat { i < len } -> GTot a))
+ : Lemma (requires True)
+ (ensures (forall (i:nat{i < len}). index (init_ghost len contents) i == contents i))val init_ghost_index_ (#a:Type) (len:nat) (contents:(i:nat { i < len } -> GTot a)) (j: nat)
+ : Lemma (requires j < len)
+ (ensures (index (init_ghost len contents) j == contents j))
+ [SMTPat (index (init_ghost len contents) j)]val lemma_equal_instances_implies_equal_types (_:unit)
+ :Lemma (forall (a:Type) (b:Type) (s1:seq a) (s2:seq b). s1 === s2 ==> a == b)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.
+Author: N. Swamy
+This module defines a permutation on sequences as a bijection among +the sequence indices relating equal elements.
+It defines a few utilities to work with such permutations.
+Notably:
+Given two sequence with equal element counts, it constructs a +permutation.
+Folding the multiplication of a commutative monoid over a +sequence and its permutation produces the same result
+A bounded natural number
+let nat_at_most (n:nat) = m:nat { m < n }A function from the indices of s to itself
let index_fun #a (s:seq a) = nat_at_most (Seq.length s) -> nat_at_most (Seq.length s)An abstract predicate defining when an index_fun is a permutation
+val is_permutation (#a:Type) (s0:seq a) (s1:seq a) (f:index_fun s0) : propRevealing the intepretation of is_permutation
+val reveal_is_permutation (#a:Type) (s0 s1:seq a) (f:index_fun s0)
+ : Lemma (is_permutation s0 s1 f <==>lengths of the sequences are the same
+Seq.length s0 == Seq.length s1 /\f is injective
+(forall x y. {:pattern f x; f y}
+ x <> y ==> f x <> f y) /\and f relates equal items in s0 and s1
+(forall (i:nat{i < Seq.length s0}).{:pattern (Seq.index s1 (f i))}
+ Seq.index s0 i == Seq.index s1 (f i)))A seqperm is an index_fun that is also a permutation
+let seqperm (#a:Type) (s0:seq a) (s1:seq a) =
+ f:index_fun s0 { is_permutation s0 s1 f }We can construct a permutation from +sequences whose element counts are the same
+val permutation_from_equal_counts
+ (#a:eqtype)
+ (s0:seq a) (s1:seq a{(forall x. count x s0 == count x s1)})
+ : Tot (seqperm s0 s1)Now, some utilities related to commutative monoids and permutations
+CM
+folding a m.mult over a sequence
+let foldm_snoc (#a:Type) (m:CM.cm a) (s:seq a) = foldr_snoc m.mult s m.unitfolding m over the concatenation of s1 and s2 +can be decomposed into a fold over s1 and a fold over s2
+val foldm_snoc_append (#a:Type) (m:CM.cm a) (s1 s2: seq a)
+ : Lemma
+ (ensures foldm_snoc m (append s1 s2) == m.mult (foldm_snoc m s1) (foldm_snoc m s2))folds over concatenated lists can is symmetric
+val foldm_snoc_sym (#a:Type) (m:CM.cm a) (s1 s2: seq a)
+ : Lemma
+ (ensures foldm_snoc m (append s1 s2) == foldm_snoc m (append s2 s1))And, finally, if s0 and s1 are permutations, +then folding m over them is identical
+val foldm_snoc_perm (#a:_)
+ (m:CM.cm a)
+ (s0:seq a)
+ (s1:seq a)
+ (p:seqperm s0 s1)
+ : Lemma
+ (ensures foldm_snoc m s0 == foldm_snoc m s1)fsdoc: no-summary-found
-fsdoc: no-comment-found
- More properties, with new naming conventions pragmaDealing efficiently with seq_of_list by meta-evaluating conjunctions over an entire list.
let ((sortWith (#a:eqtype) (f:Unidentified product: [a] Unidentified product: [a] (Tot int)) (s:seq a)):(Tot (seq a))):seq_of_list (List.Tot.Base.sortWith f (seq_to_list s))**** sortWith *****
+Seq
+let lseq (a: Type) (l: nat) : Type =
+ s: Seq.seq a { Seq.length s == l }let indexable (#a:Type) (s:Seq.seq a) (j:int) = 0 <= j /\ j < Seq.length sval lemma_append_inj_l: #a:Type -> s1:seq a -> s2:seq a -> t1:seq a -> t2:seq a{length s1 = length t1 /\ equal (append s1 s2) (append t1 t2)} -> i:nat{i < length s1}
+ -> Lemma (index s1 i == index t1 i)val lemma_append_inj_r: #a:Type -> s1:seq a -> s2:seq a -> t1:seq a -> t2:seq a{length s1 = length t1 /\ length s2 = length t2 /\ equal (append s1 s2) (append t1 t2)} -> i:nat{i < length s2}
+ -> Lemma (ensures (index s2 i == index t2 i))val lemma_append_len_disj: #a:Type -> s1:seq a -> s2:seq a -> t1:seq a -> t2:seq a {(length s1 = length t1 \/ length s2 = length t2) /\ (equal (append s1 s2) (append t1 t2))}
+ -> Lemma (ensures (length s1 = length t1 /\ length s2 = length t2))val lemma_append_inj: #a:Type -> s1:seq a -> s2:seq a -> t1:seq a -> t2:seq a {length s1 = length t1 \/ length s2 = length t2}
+ -> Lemma (requires (equal (append s1 s2) (append t1 t2)))
+ (ensures (equal s1 t1 /\ equal s2 t2))let head (#a:Type) (s:seq a{length s > 0}) : Tot a = index s 0let tail (#a:Type) (s:seq a{length s > 0}) : Tot (seq a) = slice s 1 (length s)val lemma_head_append: #a:Type -> s1:seq a{length s1 > 0} -> s2:seq a -> Lemma
+ (head (append s1 s2) == head s1)val lemma_tail_append: #a:Type -> s1:seq a{length s1 > 0} -> s2:seq a -> Lemma
+ (tail (append s1 s2) == append (tail s1) s2)let last (#a:Type) (s:seq a{length s > 0}) : Tot a = index s (length s - 1)let cons (#a:Type) (x:a) (s:seq a) : Tot (seq a) = append (create 1 x) sval lemma_cons_inj: #a:Type -> v1:a -> v2:a -> s1:seq a -> s2:seq a
+ -> Lemma (requires (equal (cons v1 s1) (cons v2 s2)))
+ (ensures (v1 == v2 /\ equal s1 s2))let split (#a:Type) (s:seq a) (i:nat{(0 <= i /\ i <= length s)}) : Tot (seq a * seq a)
+ = slice s 0 i, slice s i (length s)val lemma_split : #a:Type -> s:seq a -> i:nat{(0 <= i /\ i <= length s)} -> Lemma
+ (ensures (append (fst (split s i)) (snd (split s i)) == s))let split_eq (#a:Type) (s:seq a) (i:nat{(0 <= i /\ i <= length s)})
+: Pure
+ (seq a * seq a)
+ (requires True)
+ (ensures (fun x -> (append (fst x) (snd x) == s)))
+= let x = split s i in
+ lemma_split s i;
+ xlet rec count (#a:eqtype) (x:a) (s:seq a) : Tot nat (decreases (length s))
+= if length s = 0 then 0
+ else if head s = x
+ then 1 + count x (tail s)
+ else count x (tail s)let mem (#a:eqtype) (x:a) (l:seq a) : Tot bool = count x l > 0val mem_index (#a:eqtype) (x:a) (s:seq a)
+ : Lemma (requires (mem x s))
+ (ensures (exists i. index s i == x))index_mem:
+A utility function that finds the first index of
+x in s, given that we know the x is actually contained in s
let rec index_mem (#a:eqtype) (x:a) (s:seq a)
+ : Pure nat
+ (requires (mem x s))
+ (ensures (fun i -> i < length s /\ index s i == x))
+ (decreases (length s))
+ = if head s = x then 0
+ else 1 + index_mem x (tail s)let swap (#a:Type) (s:seq a) (i:nat{i<length s}) (j:nat{j<length s}) : Tot (seq a)
+= upd (upd s j (index s i)) i (index s j)val lemma_slice_append: #a:Type -> s1:seq a{length s1 >= 1} -> s2:seq a -> Lemma
+ (ensures (equal (append s1 s2) (append (slice s1 0 1) (append (slice s1 1 (length s1)) s2))))val lemma_slice_first_in_append: #a:Type -> s1:seq a -> s2:seq a -> i:nat{i <= length s1} -> Lemma
+ (ensures (equal (slice (append s1 s2) i (length (append s1 s2))) (append (slice s1 i (length s1)) s2)))val slice_upd: #a:Type -> s:seq a -> i:nat -> j:nat{i <= j /\ j <= length s}
+ -> k:nat{k < length s} -> v:a -> Lemma
+ (requires k < i \/ j <= k)
+ (ensures slice (upd s k v) i j == slice s i j)
+ [SMTPat (slice (upd s k v) i j)]val upd_slice: #a:Type -> s:seq a -> i:nat -> j:nat{i <= j /\ j <= length s}
+ -> k:nat{k < j - i} -> v:a -> Lemma
+ (requires i + k < j)
+ (ensures upd (slice s i j) k v == slice (upd s (i + k) v) i j)
+ [SMTPat (upd (slice s i j) k v)]TODO: should be renamed cons_head_append, or something like that (because it is NOT related to (append (cons _ _) _))
+val lemma_append_cons: #a:Type -> s1:seq a{length s1 > 0} -> s2:seq a -> Lemma
+ (requires True)
+ (ensures (equal (append s1 s2) (cons (head s1) (append (tail s1) s2))))val lemma_tl: #a:Type -> hd:a -> tl:seq a -> Lemma
+ (ensures (equal (tail (cons hd tl)) tl))let rec sorted (#a:Type) (f:a -> a -> Tot bool) (s:seq a)
+: Tot bool (decreases (length s))
+= if length s <= 1
+ then true
+ else let hd = head s in
+ f hd (index s 1) && sorted f (tail s)val sorted_feq (#a:Type)
+ (f g : (a -> a -> Tot bool))
+ (s:seq a{forall x y. f x y == g x y})
+ : Lemma (ensures (sorted f s <==> sorted g s))val lemma_append_count: #a:eqtype -> lo:seq a -> hi:seq a -> Lemma
+ (requires True)
+ (ensures (forall x. count x (append lo hi) = (count x lo + count x hi)))val lemma_append_count_aux: #a:eqtype -> x:a -> lo:seq a -> hi:seq a -> Lemma
+ (requires True)
+ (ensures (count x (append lo hi) = (count x lo + count x hi)))val lemma_mem_inversion: #a:eqtype -> s:seq a{length s > 0} -> Lemma
+ (ensures (forall x. mem x s = (x=head s || mem x (tail s))))val lemma_mem_count: #a:eqtype -> s:seq a -> f:(a -> Tot bool) -> Lemma
+ (requires (forall (i:nat{i<length s}). f (index s i)))
+ (ensures (forall (x:a). mem x s ==> f x))val lemma_count_slice: #a:eqtype -> s:seq a -> i:nat{i<=length s} -> Lemma
+ (requires True)
+ (ensures (forall x. count x s = count x (slice s 0 i) + count x (slice s i (length s))))type total_order (a:eqtype) (f: (a -> a -> Tot bool)) =
+ (forall a. f a a) (* reflexivity *)
+ /\ (forall a1 a2. (f a1 a2 /\ a1<>a2) <==> not (f a2 a1)) (* anti-symmetry *)
+ /\ (forall a1 a2 a3. f a1 a2 /\ f a2 a3 ==> f a1 a3) (* transitivity *)
+type tot_ord (a:eqtype) = f:(a -> a -> Tot bool){total_order a f}val sorted_concat_lemma: #a:eqtype
+ -> f:(a -> a -> Tot bool){total_order a f}
+ -> lo:seq a{sorted f lo}
+ -> pivot:a
+ -> hi:seq a{sorted f hi}
+ -> Lemma (requires (forall y. (mem y lo ==> f y pivot)
+ /\ (mem y hi ==> f pivot y)))
+ (ensures (sorted f (append lo (cons pivot hi))))val split_5 : #a:Type -> s:seq a -> i:nat -> j:nat{i < j && j < length s} -> Pure (seq (seq a))
+ (requires True)
+ (ensures (fun x ->
+ (length x = 5
+ /\ equal s (append (index x 0) (append (index x 1) (append (index x 2) (append (index x 3) (index x 4)))))
+ /\ equal (index x 0) (slice s 0 i)
+ /\ equal (index x 1) (slice s i (i+1))
+ /\ equal (index x 2) (slice s (i+1) j)
+ /\ equal (index x 3) (slice s j (j + 1))
+ /\ equal (index x 4) (slice s (j + 1) (length s)))))val lemma_swap_permutes_aux_frag_eq: #a:Type -> s:seq a -> i:nat{i<length s} -> j:nat{i <= j && j<length s}
+ -> i':nat -> j':nat{i' <= j' /\ j'<=length s /\
+ (j < i' //high slice
+ \/ j' <= i //low slice
+ \/ (i < i' /\ j' <= j)) //mid slice
+ }
+ -> Lemma (ensures (slice s i' j' == slice (swap s i j) i' j'
+ /\ slice s i (i + 1) == slice (swap s i j) j (j + 1)
+ /\ slice s j (j + 1) == slice (swap s i j) i (i + 1)))val lemma_swap_permutes_aux: #a:eqtype -> s:seq a -> i:nat{i<length s} -> j:nat{i <= j && j<length s} -> x:a -> Lemma
+ (requires True)
+ (ensures (count x s = count x (swap s i j)))type permutation (a:eqtype) (s1:seq a) (s2:seq a) =
+ (forall i. count i s1 = count i s2)
+val lemma_swap_permutes (#a:eqtype) (s:seq a) (i:nat{i<length s}) (j:nat{i <= j && j<length s})
+ : Lemma (permutation a s (swap s i j))perm_len: +A lemma that shows that two sequences that are permutations +of each other also have the same length
+val perm_len (#a:eqtype) (s1 s2: seq a)
+ : Lemma (requires (permutation a s1 s2))
+ (ensures (length s1 == length s2))val cons_perm: #a:eqtype -> tl:seq a -> s:seq a{length s > 0} ->
+ Lemma (requires (permutation a tl (tail s)))
+ (ensures (permutation a (cons (head s) tl) s))val lemma_mem_append : #a:eqtype -> s1:seq a -> s2:seq a
+ -> Lemma (ensures (forall x. mem x (append s1 s2) <==> (mem x s1 || mem x s2)))val lemma_slice_cons: #a:eqtype -> s:seq a -> i:nat -> j:nat{i < j && j <= length s}
+ -> Lemma (ensures (forall x. mem x (slice s i j) <==> (x = index s i || mem x (slice s (i + 1) j))))val lemma_slice_snoc: #a:eqtype -> s:seq a -> i:nat -> j:nat{i < j && j <= length s}
+ -> Lemma (ensures (forall x. mem x (slice s i j) <==> (x = index s (j - 1) || mem x (slice s i (j - 1)))))val lemma_ordering_lo_snoc: #a:eqtype -> f:tot_ord a -> s:seq a -> i:nat -> j:nat{i <= j && j < length s} -> pv:a
+ -> Lemma (requires ((forall y. mem y (slice s i j) ==> f y pv) /\ f (index s j) pv))
+ (ensures ((forall y. mem y (slice s i (j + 1)) ==> f y pv)))val lemma_ordering_hi_cons: #a:eqtype -> f:tot_ord a -> s:seq a -> back:nat -> len:nat{back < len && len <= length s} -> pv:a
+ -> Lemma (requires ((forall y. mem y (slice s (back + 1) len) ==> f pv y) /\ f pv (index s back)))
+ (ensures ((forall y. mem y (slice s back len) ==> f pv y)))val swap_frame_lo : #a:Type -> s:seq a -> lo:nat -> i:nat{lo <= i} -> j:nat{i <= j && j < length s}
+ -> Lemma (ensures (slice s lo i == slice (swap s i j) lo i))val swap_frame_lo' : #a:Type -> s:seq a -> lo:nat -> i':nat {lo <= i'} -> i:nat{i' <= i} -> j:nat{i <= j && j < length s}
+ -> Lemma (ensures (slice s lo i' == slice (swap s i j) lo i'))val swap_frame_hi : #a:Type -> s:seq a -> i:nat -> j:nat{i <= j} -> k:nat{j < k} -> hi:nat{k <= hi /\ hi <= length s}
+ -> Lemma (ensures (slice s k hi == slice (swap s i j) k hi))val lemma_swap_slice_commute : #a:Type -> s:seq a -> start:nat -> i:nat{start <= i} -> j:nat{i <= j} -> len:nat{j < len && len <= length s}
+ -> Lemma (ensures (slice (swap s i j) start len == (swap (slice s start len) (i - start) (j - start))))val lemma_swap_permutes_slice : #a:eqtype -> s:seq a -> start:nat -> i:nat{start <= i} -> j:nat{i <= j} -> len:nat{j < len && len <= length s}
+ -> Lemma (ensures (permutation a (slice s start len) (slice (swap s i j) start len)))replaces the [i,j) sub-sequence of s1 with the corresponding sub-sequence of s2
+let splice (#a:Type) (s1:seq a) (i:nat) (s2:seq a{length s1=length s2}) (j:nat{i <= j /\ j <= (length s2)})
+: Tot (seq a)
+= Seq.append (slice s1 0 i) (Seq.append (slice s2 i j) (slice s1 j (length s1)))replace with sub
+let replace_subseq (#a:Type0) (s:Seq.seq a) (i:nat) (j:nat{i <= j /\ j <= length s}) (sub:Seq.seq a{length sub == j - i}) :Tot (Seq.seq a)
+ = Seq.append (Seq.slice s 0 i) (Seq.append sub (Seq.slice s j (Seq.length s)))val splice_refl : #a:Type -> s:seq a -> i:nat -> j:nat{i <= j && j <= length s}
+ -> Lemma
+ (ensures (s == splice s i s j))val lemma_swap_splice : #a:Type -> s:seq a -> start:nat -> i:nat{start <= i} -> j:nat{i <= j} -> len:nat{j < len && len <= length s}
+ -> Lemma
+ (ensures (swap s i j == splice s start (swap s i j) len))val lemma_seq_frame_hi: #a:Type -> s1:seq a -> s2:seq a{length s1 = length s2} -> i:nat -> j:nat{i <= j} -> m:nat{j <= m} -> n:nat{m < n && n <= length s1}
+ -> Lemma
+ (requires (s1 == (splice s2 i s1 j)))
+ (ensures ((slice s1 m n == slice s2 m n) /\ (index s1 m == index s2 m)))val lemma_seq_frame_lo: #a:Type -> s1:seq a -> s2:seq a{length s1 = length s2} -> i:nat -> j:nat{i <= j} -> m:nat{j < m} -> n:nat{m <= n && n <= length s1}
+ -> Lemma
+ (requires (s1 == (splice s2 m s1 n)))
+ (ensures ((slice s1 i j == slice s2 i j) /\ (index s1 j == index s2 j)))val lemma_tail_slice: #a:Type -> s:seq a -> i:nat -> j:nat{i < j && j <= length s}
+ -> Lemma
+ (requires True)
+ (ensures (tail (slice s i j) == slice s (i + 1) j))
+ [SMTPat (tail (slice s i j))]val lemma_weaken_frame_right : #a:Type -> s1:seq a -> s2:seq a{length s1 = length s2} -> i:nat -> j:nat -> k:nat{i <= j && j <= k && k <= length s1}
+ -> Lemma
+ (requires (s1 == splice s2 i s1 j))
+ (ensures (s1 == splice s2 i s1 k))val lemma_weaken_frame_left : #a:Type -> s1:seq a -> s2:seq a{length s1 = length s2} -> i:nat -> j:nat -> k:nat{i <= j && j <= k && k <= length s1}
+ -> Lemma
+ (requires (s1 == splice s2 j s1 k))
+ (ensures (s1 == splice s2 i s1 k))val lemma_trans_frame : #a:Type -> s1:seq a -> s2:seq a -> s3:seq a{length s1 = length s2 /\ length s2 = length s3} -> i:nat -> j:nat{i <= j && j <= length s1}
+ -> Lemma
+ (requires ((s1 == splice s2 i s1 j) /\ s2 == splice s3 i s2 j))
+ (ensures (s1 == splice s3 i s1 j))val lemma_weaken_perm_left: #a:eqtype -> s1:seq a -> s2:seq a{length s1 = length s2} -> i:nat -> j:nat -> k:nat{i <= j /\ j <= k /\ k <= length s1}
+ -> Lemma
+ (requires (s1 == splice s2 j s1 k /\ permutation a (slice s2 j k) (slice s1 j k)))
+ (ensures (permutation a (slice s2 i k) (slice s1 i k)))val lemma_weaken_perm_right: #a:eqtype -> s1:seq a -> s2:seq a{length s1 = length s2} -> i:nat -> j:nat -> k:nat{i <= j /\ j <= k /\ k <= length s1}
+ -> Lemma
+ (requires (s1 == splice s2 i s1 j /\ permutation a (slice s2 i j) (slice s1 i j)))
+ (ensures (permutation a (slice s2 i k) (slice s1 i k)))val lemma_trans_perm: #a:eqtype -> s1:seq a -> s2:seq a -> s3:seq a{length s1 = length s2 /\ length s2 = length s3} -> i:nat -> j:nat{i<=j && j <= length s1}
+ -> Lemma
+ (requires (permutation a (slice s1 i j) (slice s2 i j)
+ /\ permutation a (slice s2 i j) (slice s3 i j)))
+ (ensures (permutation a (slice s1 i j) (slice s3 i j)))New addtions, please review
+let snoc (#a:Type) (s:seq a) (x:a) : Tot (seq a) = Seq.append s (Seq.create 1 x)val lemma_cons_snoc (#a:Type) (hd:a) (s:Seq.seq a) (tl:a)
+ : Lemma (requires True)
+ (ensures (Seq.equal (cons hd (snoc s tl))
+ (snoc (cons hd s) tl)))val lemma_tail_snoc: #a:Type -> s:Seq.seq a{Seq.length s > 0} -> x:a
+ -> Lemma (ensures (tail (snoc s x) == snoc (tail s) x))val lemma_snoc_inj: #a:Type -> s1:seq a -> s2:seq a -> v1:a -> v2:a
+ -> Lemma (requires (equal (snoc s1 v1) (snoc s2 v2)))
+ (ensures (v1 == v2 /\ equal s1 s2))val lemma_mem_snoc : #a:eqtype -> s:Seq.seq a -> x:a ->
+ Lemma (ensures (forall y. mem y (snoc s x) <==> mem y s \/ x=y))let rec find_l (#a:Type) (f:a -> Tot bool) (l:seq a)
+: Tot (o:option a{Some? o ==> f (Some?.v o)})
+ (decreases (Seq.length l))
+= if Seq.length l = 0 then None
+ else if f (head l) then Some (head l)
+ else find_l f (tail l)let rec ghost_find_l (#a:Type) (f:a -> GTot bool) (l:seq a)
+: GTot (o:option a{Some? o ==> f (Some?.v o)})
+ (decreases (Seq.length l))
+= if Seq.length l = 0 then None
+ else if f (head l) then Some (head l)
+ else ghost_find_l f (tail l)val find_append_some: #a:Type -> s1:seq a -> s2:seq a -> f:(a -> Tot bool) -> Lemma
+ (requires (Some? (find_l f s1)))
+ (ensures (find_l f (append s1 s2) == find_l f s1))val find_append_none: #a:Type -> s1:seq a -> s2:seq a -> f:(a -> Tot bool) -> Lemma
+ (requires (None? (find_l f s1)))
+ (ensures (find_l f (append s1 s2) == find_l f s2))val find_append_none_s2: #a:Type -> s1:seq a -> s2:seq a -> f:(a -> Tot bool) -> Lemma
+ (requires (None? (find_l f s2)))
+ (ensures (find_l f (append s1 s2) == find_l f s1))val find_snoc: #a:Type -> s:Seq.seq a -> x:a -> f:(a -> Tot bool)
+ -> Lemma (ensures (let res = find_l f (snoc s x) in
+ match res with
+ | None -> find_l f s == None /\ not (f x)
+ | Some y -> res == find_l f s \/ (f x /\ x==y)))let un_snoc (#a:Type) (s:seq a{length s <> 0}) : Tot (r:(seq a * a){s == snoc (fst r) (snd r)}) =
+ let s', a = split s (length s - 1) in
+ assert (Seq.equal (snoc s' (Seq.index a 0)) s);
+ s', Seq.index a 0val un_snoc_snoc (#a:Type) (s:seq a) (x:a) : Lemma (un_snoc (snoc s x) == (s, x))let rec find_r (#a:Type) (f:a -> Tot bool) (l:seq a)
+: Tot (o:option a{Some? o ==> f (Some?.v o)})
+ (decreases (Seq.length l))
+= if Seq.length l = 0 then None
+ else let prefix, last = un_snoc l in
+ if f last then Some last
+ else find_r f prefixtype found (i:nat) = Truelet rec seq_find_aux (#a:Type) (f:a -> Tot bool) (l:seq a) (ctr:nat{ctr <= Seq.length l})
+: Pure (option a)
+ (requires (forall (i:nat{ i < Seq.length l /\ i >= ctr}).
+ not (f (Seq.index l i) )))
+ (ensures (function
+ | None -> forall (i:nat{i < Seq.length l}). not (f (Seq.index l i))
+ | Some x -> f x /\ (exists (i:nat{i < Seq.length l}). {:pattern (found i)}
+ found i /\ x == Seq.index l i)))
+= match ctr with
+ | 0 -> None
+ | _ -> let i = ctr - 1 in
+ if f (Seq.index l i)
+ then (
+ cut (found i);
+ Some (Seq.index l i))
+ else seq_find_aux f l ilet seq_find (#a:Type) (f:a -> Tot bool) (l:seq a)
+: Pure (option a)
+ (requires True)
+ (ensures (function
+ | None -> forall (i:nat{i < Seq.length l}). not (f (Seq.index l i))
+ | Some x -> f x /\ (exists (i:nat{i < Seq.length l}).{:pattern (found i)}
+ found i /\ x == Seq.index l i)))
+= seq_find_aux f l (Seq.length l)val find_mem (#a:eqtype) (s:seq a) (f:a -> Tot bool) (x:a{f x})
+ : Lemma (requires (mem x s))
+ (ensures (Some? (seq_find f s) /\ f (Some?.v (seq_find f s))))let for_all
+ (#a: Type)
+ (f: (a -> Tot bool))
+ (l: seq a)
+: Pure bool
+ (requires True)
+ (ensures (fun b -> (b == true <==> (forall (i: nat {i < Seq.length l} ) . f (index l i) == true))))
+= None? (seq_find (fun i -> not (f i)) l)val seq_mem_k: #a:eqtype -> s:seq a -> n:nat{n < Seq.length s} ->
+ Lemma (requires True)
+ (ensures (mem (Seq.index s n) s))
+ [SMTPat (mem (Seq.index s n) s)]L
+let rec seq_to_list (#a:Type) (s:seq a)
+: Tot (l:list a{L.length l = length s})
+ (decreases (length s))
+= if length s = 0 then []
+ else index s 0::seq_to_list (slice s 1 (length s))[@@"opaque_to_smt"]
+let rec seq_of_list (#a:Type) (l:list a) : Tot (s:seq a{L.length l = length s}) =
+ match l with
+ | [] -> Seq.empty #a
+ | hd::tl -> create 1 hd @| seq_of_list tlval lemma_seq_of_list_induction (#a:Type) (l:list a)
+ :Lemma (requires True)
+ (ensures (let s = seq_of_list l in
+ match l with
+ | [] -> Seq.equal s empty
+ | hd::tl -> s == cons hd (seq_of_list tl) /\
+ head s == hd /\ tail s == (seq_of_list tl)))val lemma_seq_list_bij: #a:Type -> s:seq a -> Lemma
+ (requires (True))
+ (ensures (seq_of_list (seq_to_list s) == s))val lemma_list_seq_bij: #a:Type -> l:list a -> Lemma
+ (requires (True))
+ (ensures (seq_to_list (seq_of_list l) == l))unfold let createL_post (#a:Type0) (l:list a) (s:seq a) : GTot Type0 =
+ normalize (L.length l = length s) /\ seq_to_list s == l /\ seq_of_list l == slet createL (#a:Type0) (l:list a)
+: Pure (seq a)
+ (requires True)
+ (ensures (fun s -> createL_post #a l s))
+= let s = seq_of_list l in
+ lemma_list_seq_bij l;
+ sval lemma_index_is_nth: #a:Type -> s:seq a -> i:nat{i < length s} -> Lemma
+ (requires True)
+ (ensures (L.index (seq_to_list s) i == index s i))//////////////////////////////////////////////////////////////////////////////
+s contains x : Type0
+An undecidable version of mem,
+for when the sequence payload is not an eqtype
+//////////////////////////////////////////////////////////////////////////////
[@@ remove_unused_type_parameters [0; 1; 2]]
+val contains (#a:Type) (s:seq a) (x:a) : Tot Type0val contains_intro (#a:Type) (s:seq a) (k:nat) (x:a)
+ : Lemma (k < Seq.length s /\ Seq.index s k == x
+ ==>
+ s `contains` x)val contains_elim (#a:Type) (s:seq a) (x:a)
+ : Lemma (s `contains` x
+ ==>
+ (exists (k:nat). k < Seq.length s /\ Seq.index s k == x))val lemma_contains_empty (#a:Type) : Lemma (forall (x:a). ~ (contains Seq.empty x))val lemma_contains_singleton (#a:Type) (x:a) : Lemma (forall (y:a). contains (create 1 x) y ==> y == x)val append_contains_equiv (#a:Type) (s1:seq a) (s2:seq a) (x:a)
+ : Lemma ((append s1 s2) `contains` x
+ <==>
+ (s1 `contains` x \/ s2 `contains` x))val contains_snoc : #a:Type -> s:Seq.seq a -> x:a ->
+ Lemma (ensures (forall y. (snoc s x) `contains` y <==> s `contains` y \/ x==y))val lemma_find_l_contains (#a:Type) (f:a -> Tot bool) (l:seq a)
+ : Lemma (requires True) (ensures Some? (find_l f l) ==> l `contains` (Some?.v (find_l f l)))val contains_cons (#a:Type) (hd:a) (tl:Seq.seq a) (x:a)
+ : Lemma ((cons hd tl) `contains` x
+ <==>
+ (x==hd \/ tl `contains` x))val append_cons_snoc (#a:Type) (u: Seq.seq a) (x:a) (v:Seq.seq a)
+ : Lemma (Seq.equal (Seq.append u (cons x v))
+ (Seq.append (snoc u x) v))val append_slices (#a:Type) (s1:Seq.seq a) (s2:Seq.seq a)
+ : Lemma ( Seq.equal s1 (Seq.slice (Seq.append s1 s2) 0 (Seq.length s1)) /\
+ Seq.equal s2 (Seq.slice (Seq.append s1 s2) (Seq.length s1) (Seq.length s1 + Seq.length s2)) /\
+ (forall (i:nat) (j:nat).
+ i <= j /\ j <= Seq.length s2 ==>
+ Seq.equal (Seq.slice s2 i j)
+ (Seq.slice (Seq.append s1 s2) (Seq.length s1 + i) (Seq.length s1 + j))))val find_l_none_no_index (#a:Type) (s:Seq.seq a) (f:(a -> Tot bool)) :
+ Lemma (requires (None? (find_l f s)))
+ (ensures (forall (i:nat{i < Seq.length s}). not (f (Seq.index s i))))
+ (decreases (Seq.length s))More properties, with new naming conventions
+let suffix_of
+ (#a: Type)
+ (s_suff s: seq a)
+= exists s_pref . (s == append s_pref s_suff)val cons_head_tail
+ (#a: Type)
+ (s: seq a {length s > 0})
+: Lemma
+ (requires True)
+ (ensures (s == cons (head s) (tail s)))
+ [SMTPat (cons (head s) (tail s))]val head_cons
+ (#a: Type)
+ (x: a)
+ (s: seq a)
+: Lemma
+ (ensures (head (cons x s) == x))val suffix_of_tail
+ (#a: Type)
+ (s: seq a {length s > 0})
+: Lemma
+ (requires True)
+ (ensures ((tail s) `suffix_of` s))
+ [SMTPat ((tail s) `suffix_of` s)]val index_cons_l
+ (#a: Type)
+ (c: a)
+ (s: seq a)
+: Lemma
+ (ensures (index (cons c s) 0 == c))val index_cons_r
+ (#a: Type)
+ (c: a)
+ (s: seq a)
+ (i: nat {1 <= i /\ i <= length s})
+: Lemma
+ (ensures (index (cons c s) i == index s (i - 1)))val append_cons
+ (#a: Type)
+ (c: a)
+ (s1 s2: seq a)
+: Lemma
+ (ensures (append (cons c s1) s2 == cons c (append s1 s2)))val index_tail
+ (#a: Type)
+ (s: seq a {length s > 0})
+ (i: nat {i < length s - 1} )
+: Lemma
+ (ensures (index (tail s) i == index s (i + 1)))val mem_cons
+ (#a:eqtype)
+ (x:a)
+ (s:seq a)
+: Lemma
+ (ensures (forall y. mem y (cons x s) <==> mem y s \/ x=y))val snoc_slice_index
+ (#a: Type)
+ (s: seq a)
+ (i: nat)
+ (j: nat {i <= j /\ j < length s} )
+: Lemma
+ (requires True)
+ (ensures (snoc (slice s i j) (index s j) == slice s i (j + 1)))
+ [SMTPat (snoc (slice s i j) (index s j))]val cons_index_slice
+ (#a: Type)
+ (s: seq a)
+ (i: nat)
+ (j: nat {i < j /\ j <= length s} )
+ (k:nat{k == i+1})
+: Lemma
+ (requires True)
+ (ensures (cons (index s i) (slice s k j) == slice s i j))
+ [SMTPat (cons (index s i) (slice s k j))]val slice_is_empty
+ (#a: Type)
+ (s: seq a)
+ (i: nat {i <= length s})
+: Lemma
+ (requires True)
+ (ensures (slice s i i == Seq.empty))
+ [SMTPat (slice s i i)]val slice_length
+ (#a: Type)
+ (s: seq a)
+: Lemma
+ (requires True)
+ (ensures (slice s 0 (length s) == s))
+ [SMTPat (slice s 0 (length s))]val slice_slice
+ (#a: Type)
+ (s: seq a)
+ (i1: nat)
+ (j1: nat {i1 <= j1 /\ j1 <= length s} )
+ (i2: nat)
+ (j2: nat {i2 <= j2 /\ j2 <= j1 - i1} )
+: Lemma
+ (requires True)
+ (ensures (slice (slice s i1 j1) i2 j2 == slice s (i1 + i2) (i1 + j2)))
+ [SMTPat (slice (slice s i1 j1) i2 j2)]val lemma_seq_of_list_index (#a:Type) (l:list a) (i:nat{i < List.Tot.length l})
+ :Lemma (requires True)
+ (ensures (index (seq_of_list l) i == List.Tot.index l i))
+ [SMTPat (index (seq_of_list l) i)][@@(deprecated "seq_of_list")]
+let of_list (#a:Type) (l:list a) :seq a = seq_of_list lval seq_of_list_tl
+ (#a: Type)
+ (l: list a { List.Tot.length l > 0 } )
+: Lemma
+ (requires True)
+ (ensures (seq_of_list (List.Tot.tl l) == tail (seq_of_list l)))val mem_seq_of_list
+ (#a: eqtype)
+ (x: a)
+ (l: list a)
+: Lemma
+ (requires True)
+ (ensures (mem x (seq_of_list l) == List.Tot.mem x l))
+ [SMTPat (mem x (seq_of_list l))]Dealing efficiently with seq_of_list by meta-evaluating conjunctions over
+an entire list.
let rec explode_and (#a: Type)
+ (i: nat)
+ (s: seq a { i <= length s })
+ (l: list a { List.Tot.length l + i = length s }):
+ Tot Type
+ (decreases (List.Tot.length l))
+= match l with
+ | [] -> True
+ | hd :: tl -> index s i == hd /\ explode_and (i + 1) s tlunfold
+let pointwise_and s l =
+ norm [ iota; zeta; primops; delta_only [ `%(explode_and) ] ] (explode_and 0 s l)val intro_of_list': #a:Type ->
+ i:nat ->
+ s:seq a ->
+ l:list a ->
+ Lemma
+ (requires (
+ List.Tot.length l + i = length s /\
+ i <= length s /\
+ explode_and i s l))
+ (ensures (
+ equal (seq_of_list l) (slice s i (length s))))val intro_of_list (#a: Type) (s: seq a) (l: list a):
+ Lemma
+ (requires (
+ List.Tot.length l = length s /\
+ pointwise_and s l))
+ (ensures (
+ s == seq_of_list l))val elim_of_list': #a:Type ->
+ i:nat ->
+ s:seq a ->
+ l:list a ->
+ Lemma
+ (requires (
+ List.Tot.length l + i = length s /\
+ i <= length s /\
+ slice s i (length s) == seq_of_list l))
+ (ensures (
+ explode_and i s l))val elim_of_list (#a: Type) (l: list a):
+ Lemma
+ (ensures (
+ let s = seq_of_list l in
+ pointwise_and s l))***** sortWith *****
+let sortWith (#a:eqtype) (f:a -> a -> Tot int) (s:seq a) :Tot (seq a)
+ = seq_of_list (List.Tot.Base.sortWith f (seq_to_list s))val lemma_seq_to_list_permutation (#a:eqtype) (s:seq a)
+ :Lemma (requires True) (ensures (forall x. count x s == List.Tot.Base.count x (seq_to_list s))) (decreases (length s))val lemma_seq_of_list_permutation (#a:eqtype) (l:list a)
+ :Lemma (forall x. List.Tot.Base.count x l == count x (seq_of_list l))val lemma_seq_of_list_sorted (#a:Type) (f:a -> a -> Tot bool) (l:list a)
+ :Lemma (requires (List.Tot.Properties.sorted f l)) (ensures (sorted f (seq_of_list l)))val lemma_seq_sortwith_correctness (#a:eqtype) (f:a -> a -> Tot int) (s:seq a)
+ :Lemma (requires (total_order a (List.Tot.Base.bool_of_compare f)))
+ (ensures (let s' = sortWith f s in sorted (List.Tot.Base.bool_of_compare f) s' /\ permutation a s s'))sort_lseq: +A wrapper of Seq.sortWith which proves that the output sequences +is a sorted permutation of the input sequence with the same length
+let sort_lseq (#a:eqtype) #n (f:tot_ord a) (s:lseq a n)
+ : s':lseq a n{sorted f s' /\ permutation a s s'} =
+ lemma_seq_sortwith_correctness (L.compare_of_bool f) s;
+ let s' = sortWith (L.compare_of_bool f) s in
+ perm_len s s';
+ sorted_feq f (L.bool_of_compare (L.compare_of_bool f)) s';
+ s'let rec foldr (#a #b:Type) (f:b -> a -> Tot a) (s:seq b) (init:a)
+ : Tot a (decreases (length s))
+ = if length s = 0 then init
+ else f (head s) (foldr f (tail s) init)let rec foldr_snoc (#a #b:Type) (f:b -> a -> Tot a) (s:seq b) (init:a)
+ : Tot a (decreases (length s))
+ = if length s = 0 then init
+ else let s, last = un_snoc s in
+ f last (foldr_snoc f s init)fsdoc: no-summary-found
-fsdoc: no-comment-found
+type sorted_pred (#a:eqtype) (f:tot_ord a) (s:seq a) : Type0 =
+ forall (i j: (k:nat{k<length s})). i <= j ==> f (index s i) (index s j)val sorted_pred_tail :
+ #a:eqtype ->
+ f:tot_ord a ->
+ s:seq a{length s > 0} ->
+ Lemma (requires (sorted_pred #a f s)) (ensures (sorted_pred #a f (tail s)))
+let sorted_pred_tail #a f s = ()val sorted_pred_sorted_lemma :
+ #a:eqtype ->
+ f:tot_ord a ->
+ s:seq a ->
+ Lemma (requires (sorted_pred f s)) (ensures (sorted #a f s == true)) (decreases (length s))
+let rec sorted_pred_sorted_lemma #a f s =
+ if length s <= 1 then ()
+ else begin
+ assert (f (index s 0) (index s 1)) ;
+ sorted_pred_tail #a f s;
+ sorted_pred_sorted_lemma #a f (tail s)
+ endlet intro_sorted_pred (#a:eqtype) (f:tot_ord a) (s:seq a)
+ ($g:(i:nat{i < length s} -> j:nat{j < length s} -> Lemma (requires (i <= j)) (ensures (f (index s i) (index s j)))))
+ : Lemma (sorted_pred #a f s)
+= let aux (i j : (k:nat{k < length s})) (p:squash (i <= j)) : GTot (squash (f (index s i) (index s j))) =
+ FStar.Squash.give_proof p ;
+ g i j ;
+ FStar.Squash.get_proof (f (index s i) (index s j))
+ in
+ FStar.Classical.forall_intro_2 (fun (i j:(k:nat{k < length s})) ->
+ FStar.Classical.give_witness (FStar.Classical.arrow_to_impl (aux i j)) <: Lemma (i <= j ==> f (index s i) (index s j)))val sorted_pred_cons_lemma :
+ #a:eqtype ->
+ f:tot_ord a ->
+ s:seq a{length s > 1} ->
+ Lemma (requires (f (index s 0) (index s 1) /\ sorted_pred #a f (tail s))) (ensures (sorted_pred #a f s))
+let sorted_pred_cons_lemma #a f s =
+ let aux (i j : (k:nat{k < length s})) : Lemma (requires (i <= j)) (ensures (f (index s i) (index s j))) =
+ if i = 0 then
+ if j = 0 then ()
+ else assert (f (index s 0) (index (tail s) 0) /\ f (index (tail s) 0) (index (tail s) (j-1)))
+ else assert (f (index (tail s) (i - 1)) (index (tail s) (j - 1)))
+ in
+ intro_sorted_pred #a f s auxval sorted_sorted_pred_lemma :
+ #a:eqtype ->
+ f:tot_ord a ->
+ s:seq a ->
+ Lemma (requires (sorted #a f s == true)) (ensures (sorted_pred #a f s)) (decreases (length s))
+let rec sorted_sorted_pred_lemma #a f s =
+ if length s = 0 then ()
+ else if length s = 1 then ()
+ else (sorted_sorted_pred_lemma #a f (tail s) ; sorted_pred_cons_lemma #a f s)val sorted_pred_slice_lemma :
+ #a:eqtype ->
+ f:tot_ord a ->
+ s:seq a ->
+ i:nat{i < length s} ->
+ j:nat{i <= j /\ j <= length s} ->
+ Lemma (requires (sorted_pred #a f s)) (ensures (sorted_pred #a f (slice s i j)))
+let sorted_pred_slice_lemma #a f s i j = ()val sorted_slice_lemma :
+ #a:eqtype ->
+ f:tot_ord a ->
+ s:seq a ->
+ i:nat{i < length s} ->
+ j:nat{i <= j /\ j <= length s} ->
+ Lemma (requires (sorted #a f s == true)) (ensures (sorted #a f (slice s i j) == true))
+let sorted_slice_lemma #a f s i j =
+ sorted_sorted_pred_lemma #a f s ;
+ sorted_pred_slice_lemma #a f s i j ;
+ sorted_pred_sorted_lemma #a f (slice s i j)val sorted_split_lemma :
+ #a:eqtype ->
+ f:tot_ord a ->
+ s:seq a ->
+ i:nat{i < length s} ->
+ Lemma (requires (sorted #a f s == true))
+ (ensures (let s1, s2 = split s i in sorted #a f s1 == true /\ sorted #a f s2 == true))
+let sorted_split_lemma #a f s i =
+ sorted_slice_lemma #a f s 0 i ;
+ sorted_slice_lemma #a f s i (length s)val sorted_pred_append_lemma :
+ #a:eqtype ->
+ f:tot_ord a ->
+ s1:seq a ->
+ s2:seq a ->
+ Lemma (requires (sorted_pred #a f s1 /\ sorted_pred #a f s2 /\ (length s1 > 0 /\ length s2 > 0 ==> f (last s1) (head s2))))
+ (ensures (sorted_pred #a f (append s1 s2)))
+let sorted_pred_append_lemma #a f s1 s2 =
+ let s = append s1 s2 in
+ let aux (i j:(k:nat{k < length s})) : Lemma (requires (i <= j)) (ensures (f (index s i) (index s j))) =
+ if i < length s1 then
+ if j < length s1 then
+ assert (f (index s1 i) (index s1 j))
+ else
+ (assert (f (index s1 i) (last s1)) ; assert (f (head s2) (index s2 (j - length s1))))
+ else
+ (assert (j >= length s1) ; assert (f (index s2 (i - length s1)) (index s2 (j - length s1))))
+ in
+ intro_sorted_pred #a f s auxfsdoc: no-summary-found
-fsdoc: no-comment-found
+fsdoc: no-summary-found
-fsdoc: no-comment-found
-pragmaComputational sets (on eqtypes): membership is a boolean function
+#set-options "--initial_fuel 0 --max_fuel 0 --initial_ifuel 0 --max_ifuel 0"val set (a:eqtype)
+ : Type0val equal (#a:eqtype) (s1:set a) (s2:set a)
+ : Type0destructors
+val mem (#a:eqtype) (x:a) (s:set a)
+ : Tot boolconstructors
+val empty (#a:eqtype)
+ : Tot (set a)val singleton (#a:eqtype) (x:a)
+ : Tot (set a)val union : #a:eqtype -> set a -> set a -> Tot (set a)
+val intersect : #a:eqtype -> set a -> set a -> Tot (set a)
+val complement : #a:eqtype -> set a -> Tot (set a)a property about sets
+let disjoint (#a:eqtype) (s1: set a) (s2: set a) =
+ equal (intersect s1 s2) emptyops
+let subset (#a:eqtype) (s1:set a) (s2:set a) =
+ forall x. mem x s1 ==> mem x s2Properties
+val mem_empty: #a:eqtype -> x:a -> Lemma
+ (requires True)
+ (ensures (not (mem x empty)))
+ [SMTPat (mem x empty)]val mem_singleton: #a:eqtype -> x:a -> y:a -> Lemma
+ (requires True)
+ (ensures (mem y (singleton x) = (x=y)))
+ [SMTPat (mem y (singleton x))]val mem_union: #a:eqtype -> x:a -> s1:set a -> s2:set a -> Lemma
+ (requires True)
+ (ensures (mem x (union s1 s2) = (mem x s1 || mem x s2)))
+ [SMTPat (mem x (union s1 s2))]val mem_intersect: #a:eqtype -> x:a -> s1:set a -> s2:set a -> Lemma
+ (requires True)
+ (ensures (mem x (intersect s1 s2) = (mem x s1 && mem x s2)))
+ [SMTPat (mem x (intersect s1 s2))]val mem_complement: #a:eqtype -> x:a -> s:set a -> Lemma
+ (requires True)
+ (ensures (mem x (complement s) = not (mem x s)))
+ [SMTPat (mem x (complement s))]val mem_subset: #a:eqtype -> s1:set a -> s2:set a -> Lemma
+ (requires (forall x. mem x s1 ==> mem x s2))
+ (ensures (subset s1 s2))
+ [SMTPat (subset s1 s2)]val subset_mem: #a:eqtype -> s1:set a -> s2:set a -> Lemma
+ (requires (subset s1 s2))
+ (ensures (forall x. mem x s1 ==> mem x s2))
+ [SMTPat (subset s1 s2)]extensionality
+val lemma_equal_intro: #a:eqtype -> s1:set a -> s2:set a -> Lemma
+ (requires (forall x. mem x s1 = mem x s2))
+ (ensures (equal s1 s2))
+ [SMTPat (equal s1 s2)]val lemma_equal_elim: #a:eqtype -> s1:set a -> s2:set a -> Lemma
+ (requires (equal s1 s2))
+ (ensures (s1 == s2))
+ [SMTPat (equal s1 s2)]val lemma_equal_refl: #a:eqtype -> s1:set a -> s2:set a -> Lemma
+ (requires (s1 == s2))
+ (ensures (equal s1 s2))
+ [SMTPat (equal s1 s2)]val disjoint_not_in_both (a:eqtype) (s1:set a) (s2:set a)
+ : Lemma
+ (requires (disjoint s1 s2))
+ (ensures (forall (x:a).{:pattern (mem x s1) \/ (mem x s2)} mem x s1 ==> ~(mem x s2)))
+ [SMTPat (disjoint s1 s2)]Converting lists to sets
+WHY IS THIS HERE? It is not strictly part of the interface
+#reset-options //restore fuel usage here
+let rec as_set' (#a:eqtype) (l:list a) : set a =
+ match l with
+ | [] -> empty
+ | hd::tl -> union (singleton hd) (as_set' tl)unfold
+let as_set (#a:eqtype) (l:list a) = normalize_term (as_set' l)let lemma_disjoint_subset (#a:eqtype) (s1:set a) (s2:set a) (s3:set a)
+ : Lemma (requires (disjoint s1 s2 /\ subset s3 s1))
+ (ensures (disjoint s3 s2))
+ = ()fsdoc: no-summary-found
-fsdoc: no-comment-found
+++The module provides an interface to work with
+squashtypes, F*'s +representation for proof-irrelevant propositions.The type
+squash pis defined inPrimsas_:unit{p}. As such, +thesquashtype captures the classical logic used in F*'s +refinement types, although the interface in this module isn't +specifically classical. The moduleFStar.Classicalprovides +further derived forms to manipulatesquashtypes.This is inspired in part by: Quotient Types: A Modular +Approach. Aleksey Nogin, TPHOLs 2002. +http://www.nuprl.org/documents/Nogin/QuotientTypes_02.pdf
+Broadly,
+squashis a monad, support the usualreturnand +bindoperations.Additionally, it supports a
+push_squashoperation that relates +arrow types andsquash.
A proof of a can be forgotten to create a squashed proof of a
val return_squash (#a: Type) (x: a) : Tot (squash a)Sequential composition of squashed proofs
+val bind_squash (#a #b: Type) (x: squash a) (f: (a -> GTot (squash b))) : Tot (squash b)The push operation, together with bind_squash, allow deriving
+some of the other operations, notably squash_double_arrow. We
+rarely use the push_squash operation directly.
val push_squash (#a: Type) (#b: (a -> Type)) (f: (x: a -> Tot (squash (b x))))
+ : Tot (squash (x: a -> GTot (b x)))One reading of push f is that for a function f that builds a
+proof-irrelevant prooof of b x for all x:a, there exists a
+proof-irrelevant proof of forall (x:a). b x.
Note: since f is not itself squashed, push_squash f is not
+equal to f.
++The pre- and postconditions of of
+Pureare equivalent to +squashed arguments and results.
get_proof p, in a context requiring p is equivalent to a proof
+of squash p
val get_proof (p: Type) : Pure (squash p) (requires p) (ensures (fun _ -> True))give_proof x, for x:squash p is a equivalent to ensuring
+p.
val give_proof (#p: Type) (x: squash p) : Pure unit (requires True) (ensures (fun _ -> p))All proofs of squash p are equal
val proof_irrelevance (p: Type) (x y: squash p) : Tot (squash (x == y))Squashing the proof of the co-domain of squashed universal
+quantifier is redundant---squash_double_arrow allows removing
+it.
val squash_double_arrow (#a: Type) (#p: (a -> Type)) ($f: (squash (x: a -> GTot (squash (p x)))))
+ : GTot (squash (x: a -> GTot (p x)))The analog of push_squash for sums (existential quantification
val push_sum (#a: Type) (#b: (a -> Type)) ($p: (dtuple2 a (fun (x: a) -> squash (b x))))
+ : Tot (squash (dtuple2 a b))The analog of squash_double_arrow for sums (existential quantification)
val squash_double_sum
+ (#a: Type)
+ (#b: (a -> Type))
+ ($p: (squash (dtuple2 a (fun (x: a) -> squash (b x)))))
+ : Tot (squash (dtuple2 a b))squash is functorial; a ghost function can be mapped over a squash
val map_squash (#a #b: Type) (x: squash a) (f: (a -> GTot b)) : Tot (squash b)squash is a monad: double squashing is redundant and can be removed.
val join_squash (#a: Type) (x: squash (squash a)) : Tot (squash a)fsdoc: no-summary-found
-fsdoc: no-comment-found
+Opens module FStar.Constructive
+Opens module FStar.Squash
+val join_squash : #a:Type -> squash (squash a) -> GTot (squash a)
+let join_squash #a s = bind_squash #(squash a) #a s (fun x -> x)val squash_arrow : #a:Type -> #p:(a -> Type) ->
+ $f:(x:a -> GTot (squash (p x))) -> GTot (squash (x:a -> GTot (p x)))
+let squash_arrow #a #p f = squash_double_arrow (return_squash f)val forall_intro : #a:Type -> #p:(a -> Type) ->
+ $f:(x:a -> Lemma (p x)) -> Lemma (x:a -> GTot (p x))(* (forall (x:a). p x) *)
+let forall_intro #a #p f =
+ let ff : (x:a -> GTot (squash (p x))) = (fun x -> f x; get_proof (p x)) in
+ give_proof #(x:a -> GTot (p x)) (squash_arrow #a #p ff)currently unused +val squash_elim : a:Type -> #b:Type -> t1:b -> t2:b -> +( a -> Tot (ceq t1 t2)) -> +Tot (squash a -> Tot (ceq t1 t2))
+assume val tt (t:Type) : squash t
+assume val squash_mem_elim : a:Type -> #b:Type -> t1:b -> t2:b ->
+(x:squash a -> t:(squash a -> Type) -> Tot (t ())) ->
+Tot (x:squash a -> t:(squash a -> Type) -> Tot (t x))
+get_proof and give_proof are phrased in terms of squash
+The whole point of defining squash is to soundly allow define excluded_middle; +here this follows from get_proof and give_proof
+val bool_of_or : #p:Type -> #q:Type -> c_or p q ->
+ Tot (b:bool{(b ==> p) /\ (not(b) ==> q)})
+let bool_of_or #p #q t =
+ match t with
+ | Left _ -> true
+ | Right _ -> falseval excluded_middle : p:Type -> GTot (squash (b:bool{b <==> p}))
+let excluded_middle (p:Type) = map_squash (join_squash (get_proof (p \/ (~p)))) bool_of_orval excluded_middle_squash : p:Type0 -> GTot (p \/ ~p)
+let excluded_middle_squash p =
+ bind_squash (excluded_middle p) (fun x ->
+ if x then
+ map_squash (get_proof p) (Left #p)
+ else
+ return_squash (Right #_ #(~p) (return_squash (fun (h:p) ->
+ give_proof (return_squash h);
+ false_elim #False ()))))we thought we might prove proof irrelevance by Berardi ... but didn't manage
+Conditional on any Type -- unused below
+val ifProp: #p:Type0 -> b:Type0 -> e1:squash p -> e2:squash p -> GTot (squash p)
+let ifProp #p b e1 e2 =
+ bind_squash (excluded_middle_squash b)
+ (fun (x:c_or b (~ b)) ->
+ match x with
+ | Left _ -> e1
+ | Right _ -> e2)The powerset operator
+type pow (p:Type) = p -> GTot boolnoeq type retract 'a 'b : Type =
+ | MkR: i:('a -> GTot 'b) ->
+ j:('b -> GTot 'a) ->
+ inv:(x:'a -> GTot (ceq (j (i x)) x)) ->
+ retract 'a 'bnoeq type retract_cond 'a 'b : Type =
+ | MkC: i2:('a -> GTot 'b) ->
+ j2:('b -> GTot 'a) ->
+ inv2:(retract 'a 'b -> x:'a -> GTot (ceq (j2 (i2 x)) x)) ->
+ retract_cond 'a 'bunused below
+val ac: r:retract_cond 'a 'b -> retract 'a 'b -> x:'a ->
+ GTot (ceq ((MkC?.j2 r) (MkC?.i2 r x)) x)
+let ac (MkC _ _ inv2) = inv2let false_elim (#a:Type) (f:False) : Tot a
+ = match f withval l1: (a:Type0) -> (b:Type0) -> GTot (squash (retract_cond (pow a) (pow b)))
+let l1 (a:Type) (b:Type) =
+ bind_squash (excluded_middle_squash (retract (pow a) (pow b)))
+ (fun (x:c_or (retract (pow a) (pow b)) (~ (retract (pow a) (pow b)))) ->
+ match x with
+ | Left (MkR f0 g0 e) ->
+ return_squash (MkC f0 g0 (fun _ -> e))
+ | Right nr ->
+ let f0 (x:pow a) (y:b) = false in
+ let g0 (x:pow b) (y:a) = false in
+ map_squash nr (fun (f:(retract (pow a) (pow b) -> GTot False)) ->
+ MkC f0 g0 (fun r x -> false_elim (f r))))The paradoxical set
+type u = p:Type -> Tot (squash (pow p))NS: FAILS TO CHECK BEYOND HERE ... TODO, revisit
+Bijection between U and (pow U)
+assume val f : u -> Tot (squash (pow u))
+#set-options "--print_universes"let f x = x u *) //fails here without a means of denoting univers
+val g : squash (pow U) -> Tot U +let g sh = fun (x:Type) -> +let (slX:squash (pow U -> Tot (pow x))) = map_squash (l1 x U) MkC?.j2 in +let (srU:squash (pow U -> Tot (pow U))) = map_squash (l1 U U) MkC?.i2 in +bind_squash srU (fun rU -> +bind_squash slX (fun lX -> +bind_squash sh (fun h -> +return_squash (lX (rU h)))))
+(* This only works if importing FStar.All.fst, which is nonsense *) +val r : U +let r = +let ff : (U -> Tot (squash bool)) = +(fun (u:U) -> map_squash (u U) (fun uu -> not (uu u))) in +g (squash_arrow ff)
+CH: stopped here
+val not_has_fixpoint : squash (ceq (r U r) (not (r U r)))
+let not_has_fixpoint = Refl #bool #(r U r)
+otherwise we could assume proof irrelevance as an axiom; +note that proof relevance shouldn't be derivable for squash types +val not_provable : unit ->
+Tot (cnot (ceq (return_squash true) (return_squash false)))
+val not_provable : unit ->
+Tot (squash (cnot (ceq (return_squash true) (return_squash false))))
+type cheq (#a:Type) (x:a) : #b:Type -> b -> Type = +| HRefl : cheq #a x #a x
+val not_provable : unit ->
+Tot (cimp (cheq (return_squash #(b:bool{b=true}) true)
+(return_squash #(b:bool{b=false}) false)) (squash cfalse))
+let not_provable () =
+(fun h -> match h with
+| HRefl ->
+assert(return_squash #(b:bool{b=true}) true ==
+return_squash #(b:bool{b=false}) false);
+bind_squash (return_squash #(b:bool{b=true}) true) (fun btrue ->
+bind_squash (return_squash #(b:bool{b=false}) false) (fun bfalse ->
+assert (btrue <> bfalse); magic())))
+TODO:
+fsdoc: no-summary-found
-fsdoc: no-comment-found
+String is a primitive type in F*.
+Most of the functions in this interface have a special status in +that they are:
+All the total functions in this module are handled by F*'s +normalizers and can be reduced during typechecking
+All the total functions, plus two functions in the ML effect, +have native OCaml implementations in FStar_String.ml
+These functions are, however, not suitable for use in Low* code, +since many of them incur implicit allocations that must be garbage +collected.
+For strings in Low*, see LowStar.String, LowStar.Literal etc.
+type char = FStar.Char.char+++
list_of_stringandstring_of_list: A pair of coercions to +expose and pack a string as a list of characters
val list_of_string : string -> Tot (list char)
+val string_of_list : list char -> Tot string++A pair
+
val string_of_list_of_string (s:string)
+ : Lemma (string_of_list (list_of_string s) == s)
+val list_of_string_of_list (l:list char)
+ : Lemma (list_of_string (string_of_list l) == l)+++
strlen scounts the number of utf8 values in a string +It is not the byte length of a string
let strlen s = List.length (list_of_string s)+++
length, an alias forstrlen
unfold
+let length s = strlen s+++
maxlen: When applied to a literal s of less than n characters, +maxlen s nreduces toTruebefore going to the SMT solver. +Otherwise, the left disjunct reduces partially but the right +disjunct remains as is, allowing to keepstrlen s <= nin the +context.
unfold
+let maxlen s n = b2t (normalize_term (strlen s <= n)) \/ strlen s <= n+++
make l c: builds a string of lengthlwith each character set +toc
val make: l:nat -> char -> Tot (s:string {length s = l})+++
string_of_char: A convenient abbreviation formake 1 c
let string_of_char (c:char) : Tot string = make 1 c+++
split cs s: splits the string by delimiters incs
val split: list char -> string -> Tot (list string)+++
concat s lconcatentates the strings inldelimited bys
val concat: string -> list string -> Tot string+++
compare s0 s1: lexicographic ordering on strings
val compare: string -> string -> Tot int+++
lowercase: transform each character to its lowercase variant
val lowercase: string -> Tot string+++
uppercase: transform each character to its uppercase variant
val uppercase: string -> Tot string+++
index s n: returns the nth character ins
val index: s:string -> n:nat {n < length s} -> Tot char+++
index_of s c: +The first index ofcins+returns -1 if the char is not found, for compatibility with C
val index_of: string -> char -> Tot int+++
sub s i len+Second argument is a length, not an index. +Returns a substring of lengthlenbeginning ati
val sub: s:string -> i:nat -> l:nat{i + l <= length s} -> Tot (r: string {length r = l})+++
collect f s: mapsfover each character ofs+from left to right, appending and flattening the result
[@@(deprecated "FStar.String.collect can be defined using list_of_string and List.collect")]
+val collect: (char -> FStar.All.ML string) -> string -> FStar.All.ML string+++
substring s i len+A partial variant ofsub s i lenwithout bounds checks. +May fail with index out of bounds
val substring: string -> int -> int -> Ex string+++
get s i: Similar toindexexcept it may fail +ifiis out of bounds
val get: string -> int -> Ex char++Some lemmas (admitted for now as we don't have a model)
+
val concat_length (s1 s2: string): Lemma
+ (ensures length (s1 ^ s2) = length s1 + length s2)val list_of_concat (s1 s2: string): Lemma
+ (ensures list_of_string (s1 ^ s2) == list_of_string s1 @ list_of_string s2)val index_string_of_list (l:list char) (i : nat{i < List.Tot.length l}) :
+ Lemma (*) list_of_string_of_list l; // necessary to get equality between the lengt
+index (string_of_list l) i == List.Tot.index l i)let index_list_of_string (s:string) (i : nat{i < length s}) :
+ Lemma (List.Tot.index (list_of_string s) i == index s i) =
+ index_string_of_list (list_of_string s) i;
+ string_of_list_of_string sfsdoc: no-summary-found
-fsdoc: no-comment-found
+WARNING: this breaks parametricity; use with care
+assume val strong_excluded_middle : p:Type0 -> GTot (b:bool{b = true <==> p})fsdoc: no-summary-found
+Propositional sets (on any types): membership is a predicate
+P
+F
+#set-options "--initial_fuel 0 --max_fuel 0 --initial_ifuel 0 --max_ifuel 0"[@@must_erase_for_extraction]
+val set (a:Type u#a) : Type u#(max 1 a)val equal (#a:Type) (s1:set a) (s2:set a) : Type0destructors
+val mem : 'a -> set 'a -> Tot Type0constructors
+val empty : #a:Type -> Tot (set a)
+val singleton : #a:Type -> x:a -> Tot (set a)
+val union : #a:Type -> x:set a -> y:set a -> Tot (set a)
+val intersect : #a:Type -> x:set a -> y:set a -> Tot (set a)
+val complement : #a:Type -> x:set a -> Tot (set a)ops
+let subset (#a:Type) (s1:set a) (s2:set a) : Type0 = forall x. mem x s1 ==> mem x s2Properties
+val mem_empty: #a:Type -> x:a -> Lemma
+ (requires True)
+ (ensures (~ (mem x empty)))
+ [SMTPat (mem x empty)]val mem_singleton: #a:Type -> x:a -> y:a -> Lemma
+ (requires True)
+ (ensures (mem y (singleton x) <==> (x==y)))
+ [SMTPat (mem y (singleton x))]val mem_union: #a:Type -> x:a -> s1:set a -> s2:set a -> Lemma
+ (requires True)
+ (ensures (mem x (union s1 s2) == (mem x s1 \/ mem x s2)))
+ [SMTPat (mem x (union s1 s2))]val mem_intersect: #a:Type -> x:a -> s1:set a -> s2:set a -> Lemma
+ (requires True)
+ (ensures (mem x (intersect s1 s2) == (mem x s1 /\ mem x s2)))
+ [SMTPat (mem x (intersect s1 s2))]val mem_complement: #a:Type -> x:a -> s:set a -> Lemma
+ (requires True)
+ (ensures (mem x (complement s) == ~(mem x s)))
+ [SMTPat (mem x (complement s))]val mem_subset: #a:Type -> s1:set a -> s2:set a -> Lemma
+ (requires (forall x. mem x s1 ==> mem x s2))
+ (ensures (subset s1 s2))
+ [SMTPat (subset s1 s2)]val subset_mem: #a:Type -> s1:set a -> s2:set a -> Lemma
+ (requires (subset s1 s2))
+ (ensures (forall x. mem x s1 ==> mem x s2))
+ [SMTPat (subset s1 s2)]extensionality
+val lemma_equal_intro: #a:Type -> s1:set a -> s2:set a -> Lemma
+ (requires (forall x. mem x s1 <==> mem x s2))
+ (ensures (equal s1 s2))
+ [SMTPat (equal s1 s2)]val lemma_equal_elim: #a:Type -> s1:set a -> s2:set a -> Lemma
+ (requires (equal s1 s2))
+ (ensures (s1 == s2))
+ [SMTPat (equal s1 s2)]val lemma_equal_refl: #a:Type -> s1:set a -> s2:set a -> Lemma
+ (requires (s1 == s2))
+ (ensures (equal s1 s2))
+ [SMTPat (equal s1 s2)]val tset_of_set (#a:eqtype) (s:Set.set a) : Tot (set a)val lemma_mem_tset_of_set (#a:eqtype) (s:Set.set a) (x:a)
+ :Lemma (requires True)
+ (ensures (Set.mem x s <==> mem x (tset_of_set s)))
+ [SMTPat (mem x (tset_of_set s))]val filter (#a:Type) (f:a -> Type0) (s:set a) : Tot (set a)val lemma_mem_filter (#a:Type) (f:(a -> Type0)) (s:set a) (x:a)
+ :Lemma (requires True)
+ (ensures (mem x (filter f s) <==> mem x s /\ f x))
+ [SMTPat (mem x (filter f s))]val map (#a:Type) (#b:Type) (f:a -> Tot b) (s:set a) : Tot (set b)val lemma_mem_map (#a:Type) (#b:Type) (f:(a -> Tot b)) (s:set a) (x:b)
+ :Lemma ((exists (y:a). {:pattern (mem y s)} mem y s /\ x == f y) <==> mem x (map f s))
+ [SMTPat (mem x (map f s))]#reset-options
+let rec as_set' (#a:Type) (l:list a) : Tot (set a) =
+ match l with
+ | [] -> empty
+ | hd::tl -> union (singleton hd) (as_set' tl)unfold let as_set (#a:Type) (l:list a) : set a =
+Prims.norm zeta; iota; delta_only "FStar.TSet.as_set'"`` (as_set' l)
fsdoc: no-summary-found
-fsdoc: no-comment-found
+decide if the current goal is arith, drop the built representation of it
+let is_arith_goal () : Tac bool =
+ let g = cur_goal () in
+ match run_tm (is_arith_prop g) with
+ | Inr _ -> true
+ | _ -> falseval split_arith : unit -> Tac unit
+let rec split_arith () =
+ if is_arith_goal () then
+ begin
+ prune "";
+ addns "Prims";
+ smt ()
+ end
+ else begin
+ let g = cur_goal () in
+ match term_as_formula g with
+ | True_ ->
+ trivial ()
+ | And l r ->
+ seq FStar.Tactics.split split_arith
+ | Implies p q ->
+ let _ = implies_intro () in
+ seq split_arith l_revert
+ | Forall x p ->
+ let bs = forall_intros () in
+ seq split_arith (fun () -> l_revert_all bs)
+ | _ ->
+ ()
+ endfsdoc: no-summary-found
-fsdoc: no-comment-found
+using uint_t' instead of uint_t breaks the tactic (goes to inl).
+Congruence lemmas
+val cong_bvand : #n:pos -> (#w:bv_t n) -> (#x:bv_t n) ->
+ (#y:bv_t n) -> (#z:bv_t n) ->
+ squash (w == y) -> squash (x == z) ->
+ Lemma (bvand #n w x == bvand #n y z)
+let cong_bvand #n #w #x #y #z pf1 pf2 = ()val cong_bvxor : #n:pos -> (#w:bv_t n) -> (#x:bv_t n) ->
+ (#y:bv_t n) -> (#z:bv_t n) ->
+ squash (w == y) -> squash (x == z) ->
+ Lemma (bvxor w x == bvxor y z)
+let cong_bvxor #n #w #x #y #z pf1 pf2 = ()val cong_bvor : #n:pos -> (#w:bv_t n) -> (#x:bv_t n) ->
+ (#y:bv_t n) -> (#z:bv_t n) ->
+ squash (w == y) -> squash (x == z) ->
+ Lemma (bvor w x == bvor y z)
+let cong_bvor #n #w #x #y #z pf1 pf2 = ()val cong_bvshl : #n:pos -> (#w:bv_t n) -> (#x:uint_t n) ->
+ (#y:bv_t n) -> squash (w == y) ->
+ Lemma (bvshl w x == bvshl y x)
+let cong_bvshl #n #w #x #y pf = ()val cong_bvshr : #n:pos -> #w:bv_t n -> (#x:uint_t n) ->
+ #y:bv_t n -> squash (w == y) ->
+ Lemma (bvshr #n w x == bvshr #n y x)
+let cong_bvshr #n #w #x #y pf = ()val cong_bvdiv : #n:pos -> #w:bv_t n -> (#x:uint_t n{x <> 0}) ->
+ #y:bv_t n -> squash (w == y) ->
+ Lemma (bvdiv #n w x == bvdiv #n y x)
+let cong_bvdiv #n #w #x #y pf = ()val cong_bvmod : #n:pos -> #w:bv_t n -> (#x:uint_t n{x <> 0}) ->
+ #y:bv_t n -> squash (w == y) ->
+ Lemma (bvmod #n w x == bvmod #n y x)
+let cong_bvmod #n #w #x #y pf = ()val cong_bvmul : #n:pos -> #w:bv_t n -> (#x:uint_t n) ->
+ #y:bv_t n -> squash (w == y) ->
+ Lemma (bvmul #n w x == bvmul #n y x)
+let cong_bvmul #n #w #x #y pf = ()val cong_bvadd : #n:pos -> (#w:bv_t n) -> (#x:bv_t n) ->
+ (#y:bv_t n) -> (#z:bv_t n) ->
+ squash (w == y) -> squash (x == z) ->
+ Lemma (bvadd w x == bvadd y z)
+let cong_bvadd #n #w #x #y #z pf1 pf2 = ()val cong_bvsub : #n:pos -> (#w:bv_t n) -> (#x:bv_t n) ->
+ (#y:bv_t n) -> (#z:bv_t n) ->
+ squash (w == y) -> squash (x == z) ->
+ Lemma (bvsub w x == bvsub y z)
+let cong_bvsub #n #w #x #y #z pf1 pf2 = ()Used to reduce the initial equation to an equation on bitvectors
+val eq_to_bv: #n:pos -> (#x:uint_t n) -> (#y:uint_t n) ->
+ squash (int2bv #n x == int2bv #n y) -> Lemma (x == y)
+let eq_to_bv #n #x #y pf = int2bv_lemma_2 #n x yval lt_to_bv: #n:pos -> (#x:uint_t n) -> (#y:uint_t n) ->
+ (b2t (bvult #n (int2bv #n x) (int2bv #n y))) -> Lemma (x < y)
+let lt_to_bv #n #x #y pf = int2bv_lemma_ult_2 #n x yCreates two fresh variables and two equations of the form int2bv +x = z /\ int2bv y = w. The above lemmas transform these two +equations before finally instantiating them through reflexivity, +leaving Z3 to solve z = w
+val trans: #n:pos -> (#x:bv_t n) -> (#y:bv_t n) -> (#z:bv_t n) -> (#w:bv_t n) ->
+ squash (x == z) -> squash (y == w) -> squash (z == w) ->
+ Lemma (x == y)
+let trans #n #x #y #z #w pf1 pf2 pf3 = ()val trans_lt: #n:pos -> (#x:bv_t n) -> (#y:bv_t n) -> (#z:bv_t n) -> (#w:bv_t n) ->
+ (eq2 #(bv_t n) x z) -> (eq2 #(bv_t n) y w) -> squash (bvult #n z w) ->
+ Lemma (bvult #n x y)
+let trans_lt #n #x #y #z #w pf1 pf2 pf3 = ()val trans_lt2: #n:pos -> (#x:uint_t n) -> (#y:uint_t n) -> (#z:bv_t n) -> (#w:bv_t n) ->
+ squash (int2bv #n x == z) -> squash (int2bv #n y == w) -> (b2t (bvult #n z w)) ->
+ Lemma (x < y)
+let trans_lt2 #n #x #y #z #w pf1 pf2 pf3 = int2bv_lemma_ult_2 x ylet rec arith_expr_to_bv (e:expr) : Tac unit =
+ match e with
+ | NatToBv (MulMod e1 _) | MulMod e1 _ ->
+ apply_lemma (`int2bv_mul);
+ apply_lemma (`cong_bvmul);
+ arith_expr_to_bv e1
+ | NatToBv (Umod e1 _) | Umod e1 _ ->
+ apply_lemma (`int2bv_mod);
+ apply_lemma (`cong_bvmod);
+ arith_expr_to_bv e1
+ | NatToBv (Udiv e1 _) | Udiv e1 _ ->
+ apply_lemma (`int2bv_div);
+ apply_lemma (`cong_bvdiv);
+ arith_expr_to_bv e1
+ | NatToBv (Shl e1 _) | Shl e1 _ ->
+ apply_lemma (`int2bv_shl);
+ apply_lemma (`cong_bvshl);
+ arith_expr_to_bv e1
+ | NatToBv (Shr e1 _) | Shr e1 _ ->
+ apply_lemma (`int2bv_shr);
+ apply_lemma (`cong_bvshr);
+ arith_expr_to_bv e1
+ | NatToBv (Land e1 e2) | (Land e1 e2) ->
+ apply_lemma (`int2bv_logand);
+ apply_lemma (`cong_bvand);
+ arith_expr_to_bv e1;
+ arith_expr_to_bv e2
+ | NatToBv (Lxor e1 e2) | (Lxor e1 e2) ->
+ apply_lemma (`int2bv_logxor);
+ apply_lemma (`cong_bvxor);
+ arith_expr_to_bv e1;
+ arith_expr_to_bv e2
+ | NatToBv (Lor e1 e2) | (Lor e1 e2) ->
+ apply_lemma (`int2bv_logor);
+ apply_lemma (`cong_bvor);
+ arith_expr_to_bv e1;
+ arith_expr_to_bv e2
+ | NatToBv (Ladd e1 e2) | (Ladd e1 e2) ->
+ apply_lemma (`int2bv_add);
+ apply_lemma (`cong_bvadd);
+ arith_expr_to_bv e1;
+ arith_expr_to_bv e2
+ | NatToBv (Lsub e1 e2) | (Lsub e1 e2) ->
+ apply_lemma (`int2bv_sub);
+ apply_lemma (`cong_bvsub);
+ arith_expr_to_bv e1;
+ arith_expr_to_bv e2
+ | _ ->
+ trefl ()let arith_to_bv_tac () : Tac unit = focus (fun () ->
+ norm [delta_only ["FStar.BV.bvult"]];
+ let g = cur_goal () in
+ let f = term_as_formula g in
+ match f with
+ | Comp (Eq _) l r ->
+ begin match run_tm (as_arith_expr l) with
+ | Inl s ->
+ dump s;
+ trefl ()
+ | Inr e ->dump "inr arith_to_bv";
+ seq (fun () -> arith_expr_to_bv e) trefl
+ end
+ | _ ->
+ fail ("arith_to_bv_tac: unexpected: " ^ term_to_string g)
+)As things are right now, we need to be able to parse NatToBv +too. This can be useful, if we have mixed expressions so I'll leave it +as is for now
+let bv_tac () = focus (fun () ->
+ mapply (`eq_to_bv);
+ mapply (`trans);
+ arith_to_bv_tac ();
+ arith_to_bv_tac ();
+ set_options "--smtencoding.elim_box true";
+ norm [delta] ;
+ smt ()
+)let bv_tac_lt n = focus (fun () ->
+ let nn = pack_ln (Tv_Const (C_Int n)) in
+ let t = mk_app (`trans_lt2) [(nn, Q_Implicit)] in
+ apply_lemma t;
+ arith_to_bv_tac ();
+ arith_to_bv_tac ();
+ set_options "--smtencoding.elim_box true";
+ smt ()
+)let to_bv_tac () = focus (fun () ->
+ apply_lemma (`eq_to_bv);
+ apply_lemma (`trans);
+ arith_to_bv_tac ();
+ arith_to_bv_tac ()
+)Tactic primitives
-Every tactic primitive, i.e., those built into the compiler
-val top_env:Unidentified product: [unit] (Tac env)[top_env] returns the environment where the tactic started running. * This works even if no goals are present.
-val push_binder:Unidentified product: [env] Unidentified product: [binder] env[push_binder] extends the environment with a single binder. This is useful as one traverses the syntax of a term, pushing binders as one traverses a binder in a lambda, match, etc. Note, the environment here is disconnected to (though perhaps derived from) the environment in the proofstate
-val fresh:Unidentified product: [unit] (Tac int)[fresh ()] returns a fresh integer. It does not get reset when catching a failure.
-val refine_intro:Unidentified product: [unit] (Tac unit)[refine_intro] will turn a goal of shape [w : x:t{phi}] into [w : t] and [phi{w/x}]
-val tc:Unidentified product: [env] Unidentified product: [term] (Tac term)[tc] returns the type of a term in [env], or fails if it is untypeable.
-val tcc:Unidentified product: [env] Unidentified product: [term] (Tac comp)[tcc] like [tc], but returns the full computation type with the effect label and its arguments (WPs, etc) as well
-val unshelve:Unidentified product: [term] (Tac unit)[unshelve] creates a goal from a term for its given type. It can be used when the system decided not to present a goal, but you want one anyway. For example, if you request a uvar through [uvar_env] or [fresh_uvar], you might want to instantiate it explicitly.
-val unquote:Unidentified product: [#a:Type] Unidentified product: [term] (Tac a)[unquote t] with turn a quoted term [t] into an actual value, of any type. This will fail at tactic runtime if the quoted term does not typecheck to type [a].
-val catch:Unidentified product: [#a:Type] Unidentified product: [(Unidentified product: [unit] (Tac a))] (TacS (either exn a))[catch t] will attempt to run [t] and allow to recover from a failure. If [t] succeeds with return value [a], [catch t] returns [Inr a]. On failure, it returns [Inl msg], where [msg] is the error [t] raised. See also [or_else].
-val trivial:Unidentified product: [unit] (Tac unit)[trivial] will discharge the goal if it's exactly [True] after doing normalization and simplification of it.
-val norm:Unidentified product: [list norm_step] (Tac unit)[norm steps] will call the normalizer on the current goal's type and witness, with its reduction behaviour parameterized by the flags in [steps]. Currently, the flags (provided in Prims) are [simpl] (do logical simplifications) [whnf] (only reduce until weak head-normal-form) [primops] (performing primitive reductions, such as arithmetic and string operations) [delta] (unfold names) [zeta] (inline let bindings) [iota] (reduce match statements over constructors) [delta_only] (restrict delta to only unfold this list of fully-qualfied identifiers)
-val norm_term_env:Unidentified product: [env] Unidentified product: [list norm_step] Unidentified product: [term] (Tac term)[norm_term_env e steps t] will call the normalizer on the term [t] using the list of steps [steps], over environment [e]. The list has the same meaning as for [norm].
-val norm_binder_type:Unidentified product: [list norm_step] Unidentified product: [binder] (Tac unit)[norm_binder_type steps b] will call the normalizer on the type of the [b] binder for the current goal. Notably, this cannot be done via binder_retype and norm, because of uvars being resolved to lambda-abstractions.
-val intro:Unidentified product: [unit] (Tac binder)[intro] pushes the first argument of an arrow goal into the environment, turning [Gamma |- ?u : x:a -> b] into [Gamma, x:a |- ?u' : b]. Note that this does not work for logical implications/forall. See FStar.Tactics.Logic for that.
-val intro_rec:Unidentified product: [unit] (Tac (*(binder, binder)))Similar to intros, but allows to build a recursive function. Currently broken (c.f. issue #1103)
-val rename_to:Unidentified product: [binder] Unidentified product: [string] (Tac unit)[rename_to b nm] will rename the binder [b] to [nm] in the environment, goal, and witness in a safe manner. The only use of this is to make goals and terms more user readable.
-val revert:Unidentified product: [unit] (Tac unit)[revert] pushes out a binder from the environment into the goal type, so a behaviour opposite to [intros].
-val binder_retype:Unidentified product: [binder] (Tac unit)[binder_retype] changes the type of a binder in the context. After calling it with a binder of type t, the user is presented with a goal of the form t == ?u to be filled. The original goal (following that one) has the type of b in the context replaced by ?u.
val clear_top:Unidentified product: [unit] (Tac unit)[clear_top] will drop the outermost binder from the environment. Can only be used if the goal does not at all depend on it.
-val clear:Unidentified product: [binder] (Tac unit)[clear] will drop the given binder from the context, is nothing depends on it.
-val rewrite:Unidentified product: [binder] (Tac unit)If [b] is a binder of type [v == r], [rewrite b] will rewrite the variable [v] for [r] everywhere in the current goal type and witness/
-val t_exact:Unidentified product: [bool] Unidentified product: [bool] Unidentified product: [term] (Tac unit)First boolean is whether to attempt to intrpoduce a refinement before solving. In that case, a goal for the refinement formula will be added. Second boolean is whether to set the expected type internally. Just use exact from FStar.Tactics.Derived if you don't know what's up with all this.
val t_apply:Unidentified product: [bool] Unidentified product: [bool] Unidentified product: [term] (Tac unit)Inner primitive for [apply], takes a boolean specifying whether to not ask for implicits that appear free in posterior goals. Example: when the boolean is true, applying transitivity to [|- a = c] will give two goals, [|- a = ?u] and [|- ?u = c] without asking to instantiate [?u] since it will most likely be constrained later by solving these goals. In any case, we track [?u] and will fail if it's not solved later.
-You probably want [apply] from FStar.Tactics.Derived.
-val apply_lemma:Unidentified product: [term] (Tac unit)[apply_lemma l] will solve a goal of type [squash phi] when [l] is a Lemma ensuring [phi]. The arguments to [l] and its requires clause are introduced as new goals. As a small optimization, [unit] arguments are discharged by the engine.
-val print:Unidentified product: [string] (Tac unit)[print str] has no effect on the proofstate, but will have the side effect of printing [str] on the compiler's standard output.
-val debugging:Unidentified product: [unit] (Tac bool)[debugging ()] returns true if the current module has the debug flag on, i.e. when [--debug MyModule --debug_level Tac] was passed in.
-val dump:Unidentified product: [string] (Tac unit)Similar to [print], but will dump a text representation of the proofstate along with the message.
-val trefl:Unidentified product: [unit] (Tac unit)Solves a goal [Gamma |= squash (l == r)] by attempting to unify [l] with [r]. This currently only exists because of some universe problems when trying to [apply] a reflexivity lemma.
-val t_pointwise:Unidentified product: [direction] Unidentified product: [(Unidentified product: [unit] (Tac unit))] (Tac unit)(TODO: explain bettter) When running [pointwise tau] For every subterm [t'] of the goal's type [t], the engine will build a goal [Gamma |= t' == ?u] and run [tau] on it. When the tactic proves the goal, the engine will rewrite [t'] for [?u] in the original goal type. This is done for every subterm, bottom-up. This allows to recurse over an unknown goal type. By inspecting the goal, the [tau] can then decide what to do (to not do anything, use [trefl]).
-val topdown_rewrite:Unidentified product: [(Unidentified product: [ctrl:term] (Tac (*(bool, int))))] Unidentified product: [(Unidentified product: [rw:unit] (Tac unit))] (Tac unit)[topdown_rewrite ctrl rw] is used to rewrite those sub-terms [t] of the goal on which [fst (ctrl t)] returns true.
-On each such sub-term, [rw] is presented with an equality of goal
-of the form [Gamma |= t == ?u]. When [rw] proves the goal,
-the engine will rewrite [t] for [?u] in the original goal
-type.
-
-The goal formula is traversed top-down and the traversal can be
-controlled by [snd (ctrl t)]:
-
-When [snd (ctrl t) = 0], the traversal continues down through the
-position in the goal term.
-
-When [snd (ctrl t) = 1], the traversal continues to the next
-sub-tree of the goal.
-
-When [snd (ctrl t) = 2], no more rewrites are performed in the
-goal.
-val dup:Unidentified product: [unit] (Tac unit)Given the current goal [Gamma |- w : t], [dup] will turn this goal into [Gamma |- ?u : t] and [Gamma |= ?u == w]. It can thus be used to change a goal's witness in any way needed, by choosing some [?u] (possibly with exact) and then solving the other goal.
-val prune:Unidentified product: [string] (Tac unit)[prune "A.B.C"] will mark all top-level definitions in module [A.B.C] (and submodules of it) to not be encoded to the SMT, for the current goal. The string is a namespace prefix. [prune ""] will prune everything, but note that [prune "FStar.S"] will not prune ["FStar.Set"].
-val addns:Unidentified product: [string] (Tac unit)The opposite operation of [prune]. The latest one takes precedence.
-val t_destruct:Unidentified product: [term] (Tac (list (*(fv, nat))))Destruct a value of an inductive type by matching on it. The generated match has one branch for each constructor and is therefore trivially exhaustive, no VC is generated for that purpose. It returns a list with the fvars of each constructor and their arities, in the order they appear as goals.
-val set_options:Unidentified product: [string] (Tac unit)Set command line options for the current goal. Mostly useful to change SMT encoding options such as [set_options "--z3rlimit 20"].
-val uvar_env:Unidentified product: [env] Unidentified product: [option typ] (Tac term)Creates a new, unconstrained unification variable in environment [env]. The type of the uvar can optionally be provided in [o]. If not provided, a second uvar is created for the type.
-val unify_env:Unidentified product: [env] Unidentified product: [term] Unidentified product: [term] (Tac bool)Call the unifier on two terms. The returned boolean specifies whether unification was possible. When the tactic returns true, the terms have been unified, instantiating uvars as needed. When false, unification was not possible and no change to uvars occurs.
-val launch_process:Unidentified product: [string] Unidentified product: [list string] Unidentified product: [string] (Tac string)Launches an external process [prog] with arguments [args] and input [input] and returns the output. For security reasons, this can only be performed when the --unsafe_tactic_exec options was provided for the current F* invocation. The tactic will fail if this is not so.
val fresh_bv_named:Unidentified product: [string] Unidentified product: [typ] (Tac bv)Get a fresh bv of some name and type. The name is only useful for pretty-printing, since there is a fresh unaccessible integer within the bv too.
-val change:Unidentified product: [typ] (Tac unit)Change the goal to another type, given that it is convertible to the current type.
-val get_guard_policy:Unidentified product: [unit] (Tac guard_policy)Get the current guard policy. The guard policy specifies what should be done when a VC arises internally from the tactic engine. Options are SMT (mark it as an SMT goal), Goal (add it as an extra goal) and Force (only allow trivial guards, that need no SMT.
-val set_guard_policy:Unidentified product: [guard_policy] (Tac unit)Every tactic primitive, i.e., those built into the compiler +@summary Tactic primitives
+top_env returns the environment where the tactic started running.
val top_env : unit -> Tac envfresh () returns a fresh integer. It does not get reset when
+catching a failure.
val fresh : unit -> Tac intrefine_intro will turn a goal of shape w : x:t{phi}
+into w : t and phi{w/x}
val refine_intro : unit -> Tac unittc returns the type of a term in env,
+or fails if it is untypeable.
val tc : env -> term -> Tac termtcc like tc, but returns the full computation type
+with the effect label and its arguments (WPs, etc) as well
val tcc : env -> term -> Tac compunshelve creates a goal from a term for its given type.
+It can be used when the system decided not to present a goal, but
+you want one anyway. For example, if you request a uvar through
+uvar_env or fresh_uvar, you might want to instantiate it
+explicitly.
val unshelve : term -> Tac unitunquote t with turn a quoted term t into an actual value, of
+any type. This will fail at tactic runtime if the quoted term does not
+typecheck to type a.
val unquote : #a:Type -> term -> Tac acatch t will attempt to run t and allow to recover from a
+failure. If t succeeds with return value a, catch t returns Inr a. On failure, it returns Inl msg, where msg is the error t
+raised, and all unionfind effects are reverted. See also recover and
+or_else.
val catch : #a:Type -> (unit -> Tac a) -> TacS (either exn a)Like catch t, but will not discard unionfind effects on failure.
val recover : #a:Type -> (unit -> Tac a) -> TacS (either exn a)norm steps will call the normalizer on the current goal's
+type and witness, with its reduction behaviour parameterized
+by the flags in steps.
+Currently, the flags (provided in Prims) are
+simpl (do logical simplifications)
+whnf (only reduce until weak head-normal-form)
+primops (performing primitive reductions, such as arithmetic and
+string operations)
+delta (unfold names)
+zeta (unroll let rec bindings, but with heuristics to avoid loops)
+zeta_full (unroll let rec bindings fully)
+iota (reduce match statements over constructors)
+delta_only (restrict delta to only unfold this list of fully-qualfied identifiers)
val norm : list norm_step -> Tac unitnorm_term_env e steps t will call the normalizer on the term t
+using the list of steps steps, over environment e. The list has the same meaning as for norm.
val norm_term_env : env -> list norm_step -> term -> Tac termnorm_binder_type steps b will call the normalizer on the type of the b
+binder for the current goal. Notably, this cannot be done via binder_retype and norm,
+because of uvars being resolved to lambda-abstractions.
val norm_binder_type : list norm_step -> binder -> Tac unitintro pushes the first argument of an arrow goal into the
+environment, turning Gamma |- ?u : x:a -> b into Gamma, x:a |- ?u' : b.
+Note that this does not work for logical implications/forall. See
+FStar.Tactics.Logic for that.
val intro : unit -> Tac binderSimilar to intros, but allows to build a recursive function. +Currently broken (c.f. issue #1103)
+val intro_rec : unit -> Tac (binder * binder)rename_to b nm will rename the binder b to nm in
+the environment, goal, and witness in a safe manner. The only use of this
+is to make goals and terms more user readable. The primitive returns
+the new binder, since the old one disappears from the context.
val rename_to : binder -> string -> Tac binderrevert pushes out a binder from the environment into the goal type,
+so a behaviour opposite to intros.
val revert : unit -> Tac unitbinder_retype changes the type of a binder in the context. After calling it
+with a binder of type t, the user is presented with a goal of the form t == ?u
+to be filled. The original goal (following that one) has the type of b in the
+context replaced by ?u.
val binder_retype : binder -> Tac unitclear_top will drop the outermost binder from the environment.
+Can only be used if the goal does not at all depend on it.
val clear_top : unit -> Tac unitclear will drop the given binder from the context, is
+nothing depends on it.
val clear : binder -> Tac unitIf b is a binder of type v == r, rewrite b will rewrite
+the variable v for r everywhere in the current goal type and witness/
val rewrite : binder -> Tac unitFirst boolean is whether to attempt to intrpoduce a refinement
+before solving. In that case, a goal for the refinement formula will be
+added. Second boolean is whether to set the expected type internally.
+Just use exact from FStar.Tactics.Derived if you don't know what's up
+with all this.
val t_exact : bool -> bool -> term -> Tac unitInner primitive for apply, takes a boolean specifying whether
+to not ask for implicits that appear free in posterior goals, and a
+boolean specifying whether it's forbidden to instantiate uvars in the
+goal.
val t_apply : uopt:bool -> noinst:bool -> term -> Tac unitExample: when uopt is true, applying transitivity to |- a = c
+will give two goals, |- a = ?u and |- ?u = c without asking to
+instantiate ?u since it will most likely be constrained later by
+solving these goals. In any case, we track ?u and will fail if it's
+not solved later.
Example: when noinst is true, applying a function returning
+1 = 2 will fail on a goal of the shape 1 = ?u since it must
+instantiate ?u. We use this in typeclass resolution.
You may want apply from FStar.Tactics.Derived, or one of
+the other user facing variants.
t_apply_lemma ni nilhs l will solve a goal of type squash phi
+when l is a Lemma ensuring phi. The arguments to l and its
+requires clause are introduced as new goals. As a small optimization,
+unit arguments are discharged by the engine. For the meanining of
+the noinst boolean arg see t_apply, briefly, it does not allow to
+instantiate uvars in the goal. The noinst_lhs flag is similar, it
+forbids instantiating uvars but only on the LHS of the goal, provided
+the goal is an equality. It is meant to be useful for rewrite-goals, of
+the shape X = ?u. Setting noinst means noinst_lhs is ignored.
val t_apply_lemma : noinst:bool -> noinst_lhs:bool -> term -> Tac unitTODO: do the unit thing too for apply.
print str has no effect on the proofstate, but will have the side effect
+of printing str on the compiler's standard output.
val print : string -> Tac unitdebugging () returns true if the current module has the debug flag
+on, i.e. when --debug MyModule --debug_level Tac was passed in.
val debugging : unit -> Tac boolSimilar to print, but will dump a text representation of the proofstate
+along with the message.
val dump : string -> Tac unitSimilar to dump, but will print every unsolved implicit
+in the proofstate, not only the visible/focused goals. When the
+print_resolved boolean is true, it will also print every solved goal.
+Warning, these can be a lot.
val dump_all : print_resolved:bool -> string -> Tac unitWill print a goal for every unresolved implicit in the provided goal.
+val dump_uvars_of : goal -> string -> Tac unitSolves a goal Gamma |= squash (l == r) by attempting to unify
+l with r. This currently only exists because of some universe
+problems when trying to apply a reflexivity lemma. When allow_guards
+is true, it is allowed that (some) guards are raised during the
+unification process and added as a single goal to be discharged later.
+Currently, the only guards allowed here are for equating refinement
+types (e.g. x:int{x>0} and x:int{0<x}.
val t_trefl : allow_guards:bool -> Tac unitProvides a proof for the equality +[(match e with ... | pi -> ei ...) a1 .. an +== (match e with ... | pi -> e1 a1 .. an)]. +This is particularly useful to rewrite the expression on the left to the +one on the right when the RHS is actually a unification variable.
+val t_commute_applied_match : unit -> Tac unitctrl_rewrite will traverse the current goal, and call ctrl
ctrl t returns (true, _), therw with a goal of type t = ?u, which oncet to the solution of ?u. No goal isctrl t returns (false, _).ctrl specifiesTopDown or BottomUp. ItBottomUp a Skip means to stop trying to rewrite everythingctrl_rewrite BottomUp (fun _ -> (true, Skip)) t will call t
+pointwise and topdown_rewrite for more friendly versions.val ctrl_rewrite :
+ direction ->
+ (ctrl : term -> Tac (bool & ctrl_flag)) ->
+ (rw:unit -> Tac unit) ->
+ Tac unitGiven the current goal Gamma |- w : t,
+dup will turn this goal into
+Gamma |- ?u : t and
+Gamma |= ?u == w. It can thus be used to change
+a goal's witness in any way needed, by choosing
+some ?u (possibly with exact) and then solving the other goal.
val dup : unit -> Tac unitProof namespace management
+prune "A.B.C" will mark all top-level definitions in module
+A.B.C (and submodules of it) to not be encoded to the SMT, for the current goal.
+The string is a namespace prefix. prune "" will prune everything, but note
+that prune "FStar.S" will not prune "FStar.Set".
val prune : string -> Tac unitThe opposite operation of prune. The latest one takes precedence.
val addns : string -> Tac unitDestruct a value of an inductive type by matching on it. The generated +match has one branch for each constructor and is therefore trivially +exhaustive, no VC is generated for that purpose. It returns a list +with the fvars of each constructor and their arities, in the order +they appear as goals.
+val t_destruct : term -> Tac (list (fv * nat))Set command line options for the current goal. Mostly useful to
+change SMT encoding options such as set_options "--z3rlimit 20".
val set_options : string -> Tac unitCreates a new, unconstrained unification variable in environment
+env. The type of the uvar can optionally be provided in o. If not
+provided, a second uvar is created for the type.
val uvar_env : env -> option typ -> Tac termCall the unifier on two terms. The returned boolean specifies +whether unification was possible. When the tactic returns true, the +terms have been unified, instantiating uvars as needed. When false, +unification was not possible and no change to uvars occurs.
+val unify_env : env -> t1:term -> t2:term -> Tac boolSimilar to unify_env, but allows for some guards to be raised
+during unification (see t_trefl for an explanation). Will add a new
+goal with the guard.
val unify_guard_env : env -> t1:term -> t2:term -> Tac boolCheck if t1 matches t2, i.e., whether t2 can have its uvars
+instantiated into unifying with t1. When the tactic returns true, the
+terms have been unified, instantiating uvars as needed. When false,
+matching was not possible and no change to uvars occurs.
val match_env : env -> t1:term -> t2:term -> Tac boolLaunches an external process prog with arguments args and input
+input and returns the output. For security reasons, this can only be
+performed when the --unsafe_tactic_exec options was provided for the
+current F* invocation. The tactic will fail if this is not so.
val launch_process : string -> list string -> string -> Tac stringGet a fresh bv of some name and type. The name is only useful +for pretty-printing, since there is a fresh unaccessible integer within +the bv too.
+val fresh_bv_named : string -> typ -> Tac bvChange the goal to another type, given that it is convertible +to the current type.
+val change : typ -> Tac unitGet the current guard policy. The guard policy specifies what should +be done when a VC arises internally from the tactic engine. Options +are SMT (mark it as an SMT goal), Goal (add it as an extra goal) +and Force (only allow trivial guards, that need no SMT.
+val get_guard_policy : unit -> Tac guard_policySet the current guard policy. See [get_guard_policy} for an explanation
-val lax_on:Unidentified product: [unit] (Tac bool)[lax_on] returns true iff the current environment has the --lax option set, and thus drops all verification conditions.
val tadmit_t:Unidentified product: [term] (Tac unit)Admit the current goal and set the witness to the given term. Absolutely unsafe. Raises a warning.
-val inspect:Unidentified product: [term] (Tac term_view)val set_guard_policy : guard_policy -> Tac unitlax_on returns true iff the current environment has the
+--lax option set, and thus drops all verification conditions.
val lax_on : unit -> Tac boolAdmit the current goal and set the witness to the given term. +Absolutely unsafe. Raises a warning.
+val tadmit_t : term -> Tac unitView a term in a fully-named representation
-val pack:Unidentified product: [term_view] (Tac term)val inspect : term -> Tac term_viewPack a term view on a fully-named representation back into a term
-val join:Unidentified product: [unit] (Tac unit)Join the first two goals, which must be irrelevant, in a single one by finding a maximal prefix of their environment and reverting appropriately. Useful to minimize SMT queries that share internal obligations.
-val set_goals:Unidentified product: [list goal] (Tac unit)Set the current set of active goals at will. Obligations remain in the implicits.
-val set_smt_goals:Unidentified product: [list goal] (Tac unit)Set the current set of SMT goals at will. Obligations remain in the implicits. TODO: This is a really bad name, there's no special "SMT" about these goals.
-val curms:Unidentified product: [unit] (Tac int)[curms ()] returns the current (wall) time in millseconds
+val pack : term_view -> Tac termJoin the first two goals, which must be irrelevant, in a single +one by finding a maximal prefix of their environment and reverting +appropriately. Useful to minimize SMT queries that share internal +obligations.
+val join : unit -> Tac unitLocal metastate via a string-keyed map. lget fails if the
+found element is not typeable at the requested type.
val lget : #a:Type -> string -> Tac a
+val lset : #a:Type -> string -> a -> Tac unitSet the current set of active goals at will. Obligations remain +in the implicits.
+val set_goals : list goal -> Tac unitSet the current set of SMT goals at will. Obligations remain in the +implicits. TODO: This is a really bad name, there's no special "SMT" +about these goals.
+val set_smt_goals : list goal -> Tac unitcurms () returns the current (wall) time in millseconds
val curms : unit -> Tac intset_urgency u sets the urgency of error messages. Usually set just
+before raising an exception (see e.g. fail_silently).
val set_urgency : int -> Tac unitfsdoc: no-summary-found
-fsdoc: no-comment-found
+O
+private
+val distr : (#x : int) -> (#y : int) -> (#z : int) -> Lemma (x * (y + z) == x * y + x * z)
+private
+let distr #x #y #z = ()private
+val distl : (#x : int) -> (#y : int) -> (#z : int) -> Lemma ((x + y) * z == x * z + y * z)
+private
+let distl #x #y #z = ()private
+val ass_plus_l : (#x : int) -> (#y : int) -> (#z : int) -> Lemma (x + (y + z) == (x + y) + z)
+private
+let ass_plus_l #x #y #z = ()private
+val ass_mult_l : (#x : int) -> (#y : int) -> (#z : int) -> Lemma (x * (y * z) == (x * y) * z)
+private
+let ass_mult_l #x #y #z = ()private
+val comm_plus : (#x : int) -> (#y : int) -> Lemma (x + y == y + x)
+private
+let comm_plus #x #y = ()private
+val sw_plus : (#x : int) -> (#y : int) -> (#z : int) -> Lemma ((x + y) + z == (x + z) + y)
+private
+let sw_plus #x #y #z = ()private
+val sw_mult : (#x : int) -> (#y : int) -> (#z : int) -> Lemma ((x * y) * z == (x * z) * y)
+private
+let sw_mult #x #y #z = ()private
+val comm_mult : (#x : int) -> (#y : int) -> Lemma (x * y == y * x)
+private
+let comm_mult #x #y = ()private
+val trans : (#a:Type) -> (#x:a) -> (#z:a) -> (#y:a) ->
+ squash (x == y) -> squash (y == z) -> Lemma (x == z)
+private
+let trans #a #x #z #y e1 e2 = ()private
+val cong_plus : (#w:int) -> (#x:int) -> (#y:int) -> (#z:int) ->
+ squash (w == y) -> squash (x == z) ->
+ Lemma (w + x == y + z)
+private
+let cong_plus #w #x #y #z p q = ()private
+val cong_mult : (#w:int) -> (#x:int) -> (#y:int) -> (#z:int) ->
+ squash (w == y) -> squash (x == z) ->
+ Lemma (w * x == y * z)
+private
+let cong_mult #w #x #y #z p q = ()private
+val neg_minus_one : (#x:int) -> Lemma (-x == (-1) * x)
+private
+let neg_minus_one #x = ()private
+val x_plus_zero : (#x:int) -> Lemma (x + 0 == x)
+private
+let x_plus_zero #x = ()private
+val zero_plus_x : (#x:int) -> Lemma (0 + x == x)
+private
+let zero_plus_x #x = ()private
+val x_mult_zero : (#x:int) -> Lemma (x * 0 == 0)
+private
+let x_mult_zero #x = ()private
+val zero_mult_x : (#x:int) -> Lemma (0 * x == 0)
+private
+let zero_mult_x #x = ()private
+val x_mult_one : (#x:int) -> Lemma (x * 1 == x)
+private
+let x_mult_one #x = ()private
+val one_mult_x : (#x:int) -> Lemma (1 * x == x)
+private
+let one_mult_x #x = ()private
+val minus_is_plus : (#x : int) -> (#y : int) -> Lemma (x - y == x + (-y))
+private
+let minus_is_plus #x #y = ()private
+let step (t : unit -> Tac unit) : Tac unit =
+ apply_lemma (`trans);
+ t ()private
+let step_lemma (lem : term) : Tac unit =
+ step (fun () -> apply_lemma lem)private val canon_point : expr -> Tac expr
+private let rec canon_point e =
+ let skip () : Tac expr =
+ trefl (); e
+ in
+ match e withEvaluate constants
+| Plus (Lit a) (Lit b) ->
+ norm [primops];
+ trefl ();
+ Lit (a + b)| Mult (Lit a) (Lit b) ->
+ norm [delta; primops]; // Need delta to turn op_Star into op_Multiply, as there's no primop for it
+ trefl ();
+ Lit (a * b)Forget about negations
+| Neg e ->
+ step_lemma (`neg_minus_one);
+ canon_point (Mult (Lit (-1)) e)Distribute
+| Mult a (Plus b c) ->
+ step_lemma (`distr);
+ step_lemma (`cong_plus);
+ let l = canon_point (Mult a b) in
+ let r = canon_point (Mult a c) in
+ canon_point (Plus l r)| Mult (Plus a b) c ->
+ step_lemma (`distl);
+ step_lemma (`cong_plus);
+ let l = canon_point (Mult a c) in
+ let r = canon_point (Mult b c) in
+ canon_point (Plus l r)Associate to the left
+| Mult a (Mult b c) ->
+ step_lemma (`ass_mult_l);
+ step_lemma (`cong_mult);
+ let l = canon_point (Mult a b) in
+ let r = canon_point c in
+ canon_point (Mult l r)| Plus a (Plus b c) ->
+ step_lemma (`ass_plus_l);
+ step_lemma (`cong_plus);
+ let l = canon_point (Plus a b) in
+ let r = canon_point c in
+ canon_point (Plus l r)| Plus (Plus a b) c ->
+ if O.gt (compare_expr b c)
+ then begin
+ step_lemma (`sw_plus);
+ apply_lemma (`cong_plus);
+ let l = canon_point (Plus a c) in
+ trefl() ;
+ Plus l b
+ end
+ else skip ()| Mult (Mult a b) c ->
+ if O.gt (compare_expr b c)
+ then begin
+ step_lemma (`sw_mult);
+ apply_lemma (`cong_mult);
+ let l = canon_point (Mult a c) in
+ trefl ();
+ Mult l b
+ end
+ else skip ()| Plus a (Lit 0) ->
+ apply_lemma (`x_plus_zero);
+ a| Plus (Lit 0) b ->
+ apply_lemma (`zero_plus_x);
+ b| Plus a b ->
+ if O.gt (compare_expr a b)
+ then (apply_lemma (`comm_plus); Plus b a)
+ else skip ()| Mult (Lit 0) _ ->
+ apply_lemma (`zero_mult_x);
+ Lit 0| Mult _ (Lit 0) ->
+ apply_lemma (`x_mult_zero);
+ Lit 0| Mult (Lit 1) r ->
+ apply_lemma (`one_mult_x);
+ r| Mult l (Lit 1) ->
+ apply_lemma (`x_mult_one);
+ l| Mult a b ->
+ if O.gt (compare_expr a b)
+ then (apply_lemma (`comm_mult); Mult b a)
+ else skip ()Forget about subtraction
+| Minus a b ->
+ step_lemma (`minus_is_plus);
+ step_lemma (`cong_plus);
+ trefl ();
+ let r = canon_point (Neg b) in
+ canon_point (Plus a r)| _ ->
+ skip ()On canon_point_entry, we interpret the LHS of the goal as an +arithmetic expression, of which we keep track in canon_point so we +avoid reinterpreting the goal, which gives a good speedup.
+However, we are repeating work between canon_point_entry calls, since +in (L + R), we are called once for L, once for R, and once for the +sum which traverses both (their canonized forms, actually).
+The proper way to solve this is have some state-passing in pointwise, +maybe having the inner tactic be of type (list a -> tactic a), where +the list is the collected results for all child calls.
+let canon_point_entry () : Tac unit =
+ norm [];
+ let g = cur_goal () in
+ match term_as_formula g with
+ | Comp (Eq _) l r ->
+ begin match run_tm (is_arith_expr l) with
+ | Inr e -> (let _e = canon_point e in ())
+ | Inl _ -> trefl ()
+ end
+ | _ ->
+ fail ("impossible: " ^ term_to_string g)let canon () : Tac unit =
+ pointwise canon_point_entryfsdoc: no-summary-found
-fsdoc: no-comment-found
-let canon_attr:()++A tactic to solve equalities on a commutative semiring (a, +, *, 0, 1)
+The tactic
+canon_semiringis parameterized by the base typeaand +a semiring theorycr a. This requires:+
+- A commutative monoid (a, +, 0) for addition +That is, + is associative, commutative and has identity element 0
+- An additive inverse operator for (a, +, 0), making it an Abelian group +That is, a + -a = 0
+- A commutative monoid (a, *, 1) for multiplication +That is, * is associative, commutative and has identity element 1
+- Multipication left-distributes over addition +That is, a * (b + c) == a * b + a * c
+- 0 is an absorbing element of multiplication +That is, 0 * a = 0
+In contrast to the previous version of FStar.Tactics.CanonCommSemiring, +the tactic defined here canonizes products, additions and additive inverses, +collects coefficients in monomials, and eliminates trivial expressions.
+This is based on the legacy (second) version of Coq's ring tactic:
+ +See also the newest ring tactic in Coq, which is even more general +and efficient:
+ +
An attribute for marking definitions to unfold by the tactic
-let (norm_fully (#a:Type) (x:a)):xirreducible let canon_attr = ()++Commutative semiring theory
+
let distribute_left_lemma (a:Type) (cm_add:cm a) (cm_mult:cm a) =
+ let ( + ) = cm_add.mult in
+ let ( * ) = cm_mult.mult in
+ x:a -> y:a -> z:a -> Lemma (x * (y + z) == x * y + x * z)let distribute_right_lemma (a:Type) (cm_add:cm a) (cm_mult:cm a) =
+ let ( + ) = cm_add.mult in
+ let ( * ) = cm_mult.mult in
+ x:a -> y:a -> z:a -> Lemma ((x + y) * z == x * z + y * z)let mult_zero_l_lemma (a:Type) (cm_add:cm a) (cm_mult:cm a) =
+ x:a -> Lemma (cm_mult.mult cm_add.unit x == cm_add.unit)let add_opp_r_lemma (a:Type) (cm_add:cm a) (opp:(a -> a)) =
+ let ( + ) = cm_add.mult in
+ x:a -> Lemma (x + opp x == cm_add.unit)[@@canon_attr]
+unopteq
+type cr (a:Type) =
+ | CR :
+ cm_add: cm a ->
+ cm_mult: cm a ->
+ opp: (a -> a) ->
+ add_opp: add_opp_r_lemma a cm_add opp ->
+ distribute: distribute_left_lemma a cm_add cm_mult ->
+ mult_zero_l: mult_zero_l_lemma a cm_add cm_mult ->
+ cr alet distribute_right (#a:Type) (r:cr a) : distribute_right_lemma a r.cm_add r.cm_mult =
+ fun x y z ->
+ r.cm_mult.commutativity (r.cm_add.mult x y) z;
+ r.distribute z x y;
+ r.cm_mult.commutativity x z;
+ r.cm_mult.commutativity y z++Syntax of canonical ring expressions
+
[@@canon_attr]
+unfold let norm_fully (#a:Type) (x:a) = xlet index: eqtype = nattype varlist =
+ | Nil_var : varlist
+ | Cons_var : index -> varlist -> varlisttype canonical_sum a =
+ | Nil_monom : canonical_sum a
+ | Cons_monom : a -> varlist -> canonical_sum a -> canonical_sum a
+ | Cons_varlist : varlist -> canonical_sum a -> canonical_sum a[@@canon_attr]
+let rec varlist_lt (x y:varlist) : bool =
+ match x, y with
+ | Nil_var, Cons_var _ _ -> true
+ | Cons_var i xs, Cons_var j ys ->
+ if i < j then true else i = j && varlist_lt xs ys
+ | _, _ -> false[@@canon_attr]
+val varlist_merge: l1:varlist -> l2:varlist -> Tot varlist (decreases %[l1; l2; 0])[@@canon_attr]
+val vm_aux: index -> t1:varlist -> l2:varlist -> Tot varlist (decreases %[t1; l2; 1])Merges two lists of variables, preserving sortedness
+[@@canon_attr]
+let rec varlist_merge l1 l2 =
+ match l1, l2 with
+ | _, Nil_var -> l1
+ | Nil_var, _ -> l2
+ | Cons_var v1 t1, Cons_var v2 t2 -> vm_aux v1 t1 l2
+and vm_aux v1 t1 l2 =
+ match l2 with
+ | Cons_var v2 t2 ->
+ if v1 < v2
+ then Cons_var v1 (varlist_merge t1 l2)
+ else Cons_var v2 (vm_aux v1 t1 t2)
+ | _ -> Cons_var v1 t1a is eqtype for better reasons later.a inval spolynomial_normalize:Unidentified product: [#a:eqtype] Unidentified product: [cr a] Unidentified product: [spolynomial a] canonical_sum a[@@canon_attr]
+val canonical_sum_merge : #a:eqtype -> cr a
+ -> s1:canonical_sum a -> s2:canonical_sum a
+ -> Tot (canonical_sum a) (decreases %[s1; s2; 0])[@@canon_attr]
+val csm_aux: #a:eqtype -> r:cr a -> c1:a -> l1:varlist -> t1:canonical_sum a
+ -> s2:canonical_sum a -> Tot (canonical_sum a) (decreases %[t1; s2; 1])[@@canon_attr]
+let rec canonical_sum_merge #a r s1 s2 =
+ let aplus = r.cm_add.mult in
+ let aone = r.cm_mult.unit in
+ match s1 with
+ | Cons_monom c1 l1 t1 -> csm_aux r c1 l1 t1 s2
+ | Cons_varlist l1 t1 -> csm_aux r aone l1 t1 s2
+ | Nil_monom -> s2and csm_aux #a r c1 l1 t1 s2 =
+ let aplus = r.cm_add.mult in
+ let aone = r.cm_mult.unit in
+ match s2 with
+ | Cons_monom c2 l2 t2 ->
+ if l1 = l2
+ then Cons_monom (norm_fully (aplus c1 c2)) l1 (canonical_sum_merge r t1 t2)
+ else
+ if varlist_lt l1 l2
+ then Cons_monom c1 l1 (canonical_sum_merge r t1 s2)
+ else Cons_monom c2 l2 (csm_aux #a r c1 l1 t1 t2)
+ | Cons_varlist l2 t2 ->
+ if l1 = l2
+ then Cons_monom (norm_fully (aplus c1 aone)) l1 (canonical_sum_merge r t1 t2)
+ else
+ if varlist_lt l1 l2
+ then Cons_monom c1 l1 (canonical_sum_merge r t1 s2)
+ else Cons_varlist l2 (csm_aux r c1 l1 t1 t2)
+ | Nil_monom ->if c1 = aone then Cons_varlist l1 t1 else
+Cons_monom c1 l1 t1Inserts a monomial into the apropriate position in a canonical sum
+val monom_insert: #a:eqtype -> r:cr a
+ -> c1:a -> l1:varlist -> s2:canonical_sum a -> canonical_sum a[@@canon_attr]
+let rec monom_insert #a r c1 l1 s2 =
+ let aplus = r.cm_add.mult in
+ let aone = r.cm_mult.unit in
+ match s2 with
+ | Cons_monom c2 l2 t2 ->
+ if l1 = l2
+ then Cons_monom (norm_fully (aplus c1 c2)) l1 t2
+ else
+ if varlist_lt l1 l2
+ then Cons_monom c1 l1 s2
+ else Cons_monom c2 l2 (monom_insert r c1 l1 t2)
+ | Cons_varlist l2 t2 ->
+ if l1 = l2
+ then Cons_monom (norm_fully (aplus c1 aone)) l1 t2
+ else
+ if varlist_lt l1 l2
+ then Cons_monom c1 l1 s2
+ else Cons_varlist l2 (monom_insert r c1 l1 t2)
+ | Nil_monom ->
+ if c1 = aone
+ then Cons_varlist l1 Nil_monom
+ else Cons_monom c1 l1 Nil_monomInserts a monomial without scalar into a canonical sum
+val varlist_insert: #a:eqtype -> cr a -> varlist -> canonical_sum a -> canonical_sum a[@@canon_attr]
+let varlist_insert #a r l1 s2 =
+ let aone = r.cm_mult.unit in
+ monom_insert r aone l1 s2Multiplies a sum by a scalar c0
+val canonical_sum_scalar: #a:Type -> cr a -> a -> canonical_sum a -> canonical_sum a[@@canon_attr]
+let rec canonical_sum_scalar #a r c0 s =
+ let amult = r.cm_mult.mult in
+ match s with
+ | Cons_monom c l t -> Cons_monom (norm_fully (amult c0 c)) l (canonical_sum_scalar r c0 t)
+ | Cons_varlist l t -> Cons_monom c0 l (canonical_sum_scalar r c0 t)
+ | Nil_monom -> Nil_monomMultiplies a sum by a monomial without scalar
+val canonical_sum_scalar2: #a:eqtype -> cr a -> varlist
+ -> canonical_sum a -> canonical_sum a[@@canon_attr]
+let rec canonical_sum_scalar2 #a r l0 s =
+ match s with
+ | Cons_monom c l t ->
+ monom_insert r c (varlist_merge l0 l) (canonical_sum_scalar2 r l0 t)
+ | Cons_varlist l t ->
+ varlist_insert r (varlist_merge l0 l) (canonical_sum_scalar2 r l0 t)
+ | Nil_monom -> Nil_monomMultiplies a sum by a monomial with scalar
+val canonical_sum_scalar3: #a:eqtype -> cr a -> a -> varlist
+ -> canonical_sum a -> canonical_sum a[@@canon_attr]
+let rec canonical_sum_scalar3 #a r c0 l0 s =
+ let amult = r.cm_mult.mult in
+ match s with
+ | Cons_monom c l t ->
+ monom_insert r (norm_fully (amult c0 c)) (varlist_merge l0 l)
+ (canonical_sum_scalar3 r c0 l0 t)
+ | Cons_varlist l t ->
+ monom_insert r c0 (varlist_merge l0 l)
+ (canonical_sum_scalar3 r c0 l0 t)
+ | Nil_monom -> sMultiplies two canonical sums
+val canonical_sum_prod: #a:eqtype -> cr a
+ -> canonical_sum a -> canonical_sum a -> canonical_sum a[@@canon_attr]
+let rec canonical_sum_prod #a r s1 s2 =
+ match s1 with
+ | Cons_monom c1 l1 t1 ->
+ canonical_sum_merge r (canonical_sum_scalar3 r c1 l1 s2)
+ (canonical_sum_prod r t1 s2)
+ | Cons_varlist l1 t1 ->
+ canonical_sum_merge r (canonical_sum_scalar2 r l1 s2)
+ (canonical_sum_prod r t1 s2)
+ | Nil_monom -> s1++Syntax of concrete semiring polynomials
+
This is the type where we reflect expressions before normalization
+type spolynomial a =
+ | SPvar : index -> spolynomial a
+ | SPconst : a -> spolynomial a
+ | SPplus : spolynomial a -> spolynomial a -> spolynomial a
+ | SPmult : spolynomial a -> spolynomial a -> spolynomial aCanonize a reflected expression
-val canonical_sum_simplify:Unidentified product: [#a:eqtype] Unidentified product: [cr a] Unidentified product: [canonical_sum a] canonical_sum aval spolynomial_normalize: #a:eqtype -> cr a -> spolynomial a -> canonical_sum a[@@canon_attr]
+let rec spolynomial_normalize #a r p =
+ match p with
+ | SPvar i -> Cons_varlist (Cons_var i Nil_var) Nil_monom
+ | SPconst c -> Cons_monom c Nil_var Nil_monom
+ | SPplus l q ->
+ canonical_sum_merge r (spolynomial_normalize r l) (spolynomial_normalize r q)
+ | SPmult l q ->
+ canonical_sum_prod r (spolynomial_normalize r l) (spolynomial_normalize r q)val spolynomial_simplify:Unidentified product: [#a:eqtype] Unidentified product: [cr a] Unidentified product: [spolynomial a] canonical_sum aval canonical_sum_simplify: #a:eqtype -> cr a -> canonical_sum a -> canonical_sum a[@@canon_attr]
+let rec canonical_sum_simplify #a r s =
+ let azero = r.cm_add.unit in
+ let aone = r.cm_mult.unit in
+ let aplus = r.cm_add.mult in
+ match s with
+ | Cons_monom c l t ->
+ if norm_fully (c = azero) then canonical_sum_simplify r t
+ else
+ if norm_fully (c = aone)
+ then Cons_varlist l (canonical_sum_simplify r t)
+ else Cons_monom c l (canonical_sum_simplify r t)
+ | Cons_varlist l t -> Cons_varlist l (canonical_sum_simplify r t)
+ | Nil_monom -> slet (vmap a):*(list (*(var, a)), a)val spolynomial_simplify: #a:eqtype -> cr a -> spolynomial a -> canonical_sum a[@@canon_attr]
+let spolynomial_simplify #a r p =
+ canonical_sum_simplify r
+ (spolynomial_normalize r p)++Interpretation of varlists, monomials and canonical sums
+
let ((update (#a:Type) (x:var) (xa:a) (vm:vmap a)):vmap a):let (l, y) = vm in (FStar.Pervasives.Native.Mktuple2 (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 x xa)) l) y)let vmap a = list (var * a) * aAdd a new entry in a variable map
-let ((quote_list (#a:Type) (ta:term) (quotea:Unidentified product: [a] (Tac term)) (xs:list a)):(Tac term)):match xs with [] -> mk_app ((`(Nil))) (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 ta Q_Implicit)) (Prims.Nil )) | (Prims.Cons x xs') -> mk_app ((`(Cons))) (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 ta Q_Implicit)) (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 quotea x Q_Explicit)) (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 quote_list ta quotea xs' Q_Explicit)) (Prims.Nil ))))let update (#a:Type) (x:var) (xa:a) (vm:vmap a) : vmap a =
+ let l, y = vm in (x, xa) :: l, yQuotes a list
-let ((quote_vm (#a:Type) (ta:term) (quotea:Unidentified product: [a] (Tac term)) (vm:vmap a)):(Tac term)):let ((quote_map_entry (p:(*(nat, a)))):(Tac term)) = mk_app ((`(Mktuple2))) (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 (`(nat)) Q_Implicit)) (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 ta Q_Implicit)) (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 pack ((Tv_Const ((C_Int (fst p))))) Q_Explicit)) (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 quotea (snd p) Q_Explicit)) (Prims.Nil ))))) in let tyentry = mk_e_app ((`(tuple2))) (Prims.Cons ((`(nat))) (Prims.Cons ta (Prims.Nil ))) in let tlist = quote_list tyentry quote_map_entry (fst vm) in let tylist = mk_e_app ((`(list))) (Prims.Cons tyentry (Prims.Nil )) in mk_app ((`(Mktuple2))) (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 tylist Q_Implicit)) (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 ta Q_Implicit)) (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 tlist Q_Explicit)) (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 quotea (snd vm) Q_Explicit)) (Prims.Nil )))))let rec quote_list (#a:Type) (ta:term) (quotea:a -> Tac term) (xs:list a) :
+ Tac term =
+ match xs with
+ | [] -> mk_app (`Nil) [(ta, Q_Implicit)]
+ | x::xs' -> mk_app (`Cons) [(ta, Q_Implicit);
+ (quotea x, Q_Explicit);
+ (quote_list ta quotea xs', Q_Explicit)]Quotes a variable map
-let (interp_var (#a:Type) (vm:vmap a) (i:index)):match List.Tot.assoc i (fst vm) with (Some x) -> x | _ -> snd vmlet quote_vm (#a:Type) (ta: term) (quotea:a -> Tac term) (vm:vmap a) : Tac term =
+ let quote_map_entry (p:(nat * a)) : Tac term =
+ mk_app (`Mktuple2) [(`nat, Q_Implicit); (ta, Q_Implicit);
+ (pack (Tv_Const (C_Int (fst p))), Q_Explicit);
+ (quotea (snd p), Q_Explicit)] in
+ let tyentry = mk_e_app (`tuple2) [(`nat); ta] in
+ let tlist = quote_list tyentry quote_map_entry (fst vm) in
+ let tylist = mk_e_app (`list) [tyentry] in
+ mk_app (`Mktuple2) [(tylist, Q_Implicit); (ta, Q_Implicit);
+ (tlist, Q_Explicit); (quotea (snd vm), Q_Explicit)]let ((interp_cs (#a:Type) (r:cr a) (vm:vmap a) (s:canonical_sum a)):a):let azero = r.cm_add.unit in match s with Nil_monom -> azero | (Cons_varlist l t) -> ics_aux r vm (interp_vl r vm l) t | (Cons_monom c l t) -> ics_aux r vm (interp_m r vm c l) t[@@canon_attr]
+let interp_var (#a:Type) (vm:vmap a) (i:index) =
+ match List.Tot.Base.assoc i (fst vm) with
+ | Some x -> x
+ | _ -> snd vm[@@canon_attr]
+private
+let rec ivl_aux (#a:Type) (r:cr a) (vm:vmap a) (x:index) (t:varlist)
+ : Tot a (decreases t) =
+ let amult = r.cm_mult.mult in
+ match t with
+ | Nil_var -> interp_var vm x
+ | Cons_var x' t' -> amult (interp_var vm x) (ivl_aux r vm x' t')[@@canon_attr]
+let interp_vl (#a:Type) (r:cr a) (vm:vmap a) (l:varlist) =
+ let aone = r.cm_mult.unit in
+ match l with
+ | Nil_var -> aone
+ | Cons_var x t -> ivl_aux r vm x t[@@canon_attr]
+let interp_m (#a:Type) (r:cr a) (vm:vmap a) (c:a) (l:varlist) =
+ let amult = r.cm_mult.mult in
+ match l with
+ | Nil_var -> c
+ | Cons_var x t -> amult c (ivl_aux r vm x t)[@@canon_attr]
+let rec ics_aux (#a:Type) (r:cr a) (vm:vmap a) (x:a) (s:canonical_sum a)
+ : Tot a (decreases s) =
+ let aplus = r.cm_add.mult in
+ match s with
+ | Nil_monom -> x
+ | Cons_varlist l t -> aplus x (ics_aux r vm (interp_vl r vm l) t)
+ | Cons_monom c l t -> aplus x (ics_aux r vm (interp_m r vm c l) t)Interpretation of a canonical sum
-let ((interp_sp (#a:Type) (r:cr a) (vm:vmap a) (p:spolynomial a)):a):let aplus = r.cm_add.mult in let amult = r.cm_mult.mult in match p with (SPconst c) -> c | (SPvar i) -> interp_var vm i | (SPplus p1 p2) -> aplus (interp_sp r vm p1) (interp_sp r vm p2) | (SPmult p1 p2) -> amult (interp_sp r vm p1) (interp_sp r vm p2)[@@canon_attr]
+let interp_cs (#a:Type) (r:cr a) (vm:vmap a) (s:canonical_sum a) : a =
+ let azero = r.cm_add.unit in
+ match s with
+ | Nil_monom -> azero
+ | Cons_varlist l t -> ics_aux r vm (interp_vl r vm l) t
+ | Cons_monom c l t -> ics_aux r vm (interp_m r vm c l) tInterpretation of a polynomial
-typepolynomial = Pvar:Unidentified product: [index] polynomial a | Pconst:Unidentified product: [a] polynomial a | Pplus:Unidentified product: [polynomial a] Unidentified product: [polynomial a] polynomial a | Pmult:Unidentified product: [polynomial a] Unidentified product: [polynomial a] polynomial a | Popp:Unidentified product: [polynomial a] polynomial a [@@canon_attr]
+let rec interp_sp (#a:Type) (r:cr a) (vm:vmap a) (p:spolynomial a) : a =
+ let aplus = r.cm_add.mult in
+ let amult = r.cm_mult.mult in
+ match p with
+ | SPconst c -> c
+ | SPvar i -> interp_var vm i
+ | SPplus p1 p2 -> aplus (interp_sp r vm p1) (interp_sp r vm p2)
+ | SPmult p1 p2 -> amult (interp_sp r vm p1) (interp_sp r vm p2)++Proof of correctness
+
val mult_one_l (#a:Type) (r:cr a) (x:a) :
+ Lemma (r.cm_mult.mult r.cm_mult.unit x == x)
+ [SMTPat (r.cm_mult.mult r.cm_mult.unit x)]
+let mult_one_l #a r x =
+ r.cm_mult.identity xval mult_one_r (#a:Type) (r:cr a) (x:a) :
+ Lemma (r.cm_mult.mult x r.cm_mult.unit == x)
+ [SMTPat (r.cm_mult.mult x r.cm_mult.unit)]
+let mult_one_r #a r x =
+ r.cm_mult.commutativity r.cm_mult.unit xval mult_zero_l (#a:Type) (r:cr a) (x:a) :
+ Lemma (r.cm_mult.mult r.cm_add.unit x == r.cm_add.unit)
+ [SMTPat (r.cm_mult.mult r.cm_add.unit x)]
+let mult_zero_l #a r x =
+ r.mult_zero_l xval mult_zero_r (#a:Type) (r:cr a) (x:a) :
+ Lemma (r.cm_mult.mult x r.cm_add.unit == r.cm_add.unit)
+ [SMTPat (r.cm_mult.mult x r.cm_add.unit)]
+let mult_zero_r #a r x =
+ r.cm_mult.commutativity x r.cm_add.unitval add_zero_l (#a:Type) (r:cr a) (x:a) :
+ Lemma (r.cm_add.mult r.cm_add.unit x == x)
+ [SMTPat (r.cm_add.mult r.cm_add.unit x)]
+let add_zero_l #a r x =
+ r.cm_add.identity xval add_zero_r (#a:Type) (r:cr a) (x:a) :
+ Lemma (r.cm_add.mult x r.cm_add.unit == x)
+ [SMTPat (r.cm_add.mult x r.cm_add.unit)]
+let add_zero_r #a r x =
+ r.cm_add.commutativity r.cm_add.unit xval opp_unique (#a:Type) (r:cr a) (x y:a) : Lemma
+ (requires r.cm_add.mult x y == r.cm_add.unit)
+ (ensures y == r.opp x)
+let opp_unique #a r x y =
+ let ( + ) = r.cm_add.mult in
+ let zero = r.cm_add.unit in
+ calc (==) {
+ y;
+ == { r.add_opp x }
+ y + (x + r.opp x);
+ == { r.cm_add.associativity y x (r.opp x) }
+ (y + x) + r.opp x;
+ == { r.cm_add.commutativity x y }
+ zero + r.opp x;
+ == { }
+ r.opp x;
+ }val add_mult_opp (#a:Type) (r:cr a) (x:a) : Lemma
+ (r.cm_add.mult x (r.cm_mult.mult (r.opp r.cm_mult.unit) x) == r.cm_add.unit)
+let add_mult_opp #a r x =
+ let ( + ) = r.cm_add.mult in
+ let ( * ) = r.cm_mult.mult in
+ let zero = r.cm_add.unit in
+ let one = r.cm_mult.unit in
+ calc (==) {
+ x + r.opp one * x;
+ == { }
+ one * x + r.opp one * x;
+ == { distribute_right r one (r.opp one) x }
+ (one + r.opp one) * x;
+ == { r.add_opp one }
+ zero * x;
+ == { }
+ zero;
+ }val ivl_aux_ok (#a:Type) (r:cr a) (vm:vmap a) (v:varlist) (i:index) : Lemma
+ (ivl_aux r vm i v == r.cm_mult.mult (interp_var vm i) (interp_vl r vm v))
+let ivl_aux_ok #a r vm v i = ()val vm_aux_ok (#a:eqtype) (r:cr a) (vm:vmap a) (v:index) (t l:varlist) :
+ Lemma
+ (ensures
+ interp_vl r vm (vm_aux v t l) ==
+ r.cm_mult.mult (interp_vl r vm (Cons_var v t)) (interp_vl r vm l))
+ (decreases %[t; l; 1])val varlist_merge_ok (#a:eqtype) (r:cr a) (vm:vmap a) (x y:varlist) :
+ Lemma
+ (ensures
+ interp_vl r vm (varlist_merge x y) ==
+ r.cm_mult.mult (interp_vl r vm x) (interp_vl r vm y))
+ (decreases %[x; y; 0])let rec varlist_merge_ok #a r vm x y =
+ let amult = r.cm_mult.mult in
+ match x, y with
+ | Cons_var v1 t1, Nil_var -> ()
+ | Cons_var v1 t1, Cons_var v2 t2 ->
+ if v1 < v2
+ then
+ begin
+ varlist_merge_ok r vm t1 y;
+ assert (
+ interp_vl r vm (varlist_merge x y) ==
+ amult (interp_var vm v1) (amult (interp_vl r vm t1) (interp_vl r vm y)));
+ r.cm_mult.associativity
+ (interp_var vm v1) (interp_vl r vm t1) (interp_vl r vm y)
+ end
+ else
+ vm_aux_ok r vm v1 t1 y
+ | Nil_var, _ -> ()
+and vm_aux_ok #a r vm v1 t1 l2 =
+ match l2 with
+ | Cons_var v2 t2 ->
+ if v1 < v2
+ then
+ begin
+ varlist_merge_ok r vm t1 l2;
+ r.cm_mult.associativity
+ (interp_var vm v1) (interp_vl r vm t1) (interp_vl r vm l2)
+ end
+ else
+ begin
+ vm_aux_ok r vm v1 t1 t2;
+ calc (==) {
+ interp_vl r vm (Cons_var v2 (vm_aux v1 t1 t2));
+ == { }
+ ivl_aux r vm v2 (vm_aux v1 t1 t2);
+ == { }
+ r.cm_mult.mult (interp_var vm v2) (interp_vl r vm (vm_aux v1 t1 t2));
+ == { }
+ r.cm_mult.mult (interp_var vm v2) (r.cm_mult.mult (interp_vl r vm (Cons_var v1 t1)) (interp_vl r vm t2));
+ == { r.cm_mult.commutativity
+ (interp_vl r vm (Cons_var v1 t1)) (interp_vl r vm t2) }
+ r.cm_mult.mult (interp_var vm v2)
+ (r.cm_mult.mult (interp_vl r vm t2) (interp_vl r vm (Cons_var v1 t1)) );
+ == { r.cm_mult.associativity
+ (interp_var vm v2)
+ (interp_vl r vm t2) (interp_vl r vm (Cons_var v1 t1)) }
+ r.cm_mult.mult
+ (r.cm_mult.mult (interp_var vm v2) (interp_vl r vm t2))
+ (interp_vl r vm (Cons_var v1 t1));
+ == { r.cm_mult.commutativity
+ (interp_vl r vm (Cons_var v1 t1)) (interp_vl r vm (Cons_var v2 t2)) }
+ r.cm_mult.mult (interp_vl r vm (Cons_var v1 t1)) (interp_vl r vm (Cons_var v2 t2));
+ }
+ end
+ | _ -> ()val ics_aux_ok: #a:eqtype -> r:cr a -> vm:vmap a -> x:a -> s:canonical_sum a ->
+ Lemma (ensures ics_aux r vm x s == r.cm_add.mult x (interp_cs r vm s))
+ (decreases s)
+let rec ics_aux_ok #a r vm x s =
+ match s with
+ | Nil_monom -> ()
+ | Cons_varlist l t ->
+ ics_aux_ok r vm (interp_vl r vm l) t
+ | Cons_monom c l t ->
+ ics_aux_ok r vm (interp_m r vm c l) tval interp_m_ok: #a:eqtype -> r:cr a -> vm:vmap a -> x:a -> l:varlist ->
+ Lemma (interp_m r vm x l == r.cm_mult.mult x (interp_vl r vm l))
+let interp_m_ok #a r vm x l = ()val aplus_assoc_4: #a:Type -> r:cr a -> w:a -> x:a -> y:a -> z:a -> Lemma
+ (let aplus = r.cm_add.mult in
+ aplus (aplus w x) (aplus y z) == aplus (aplus w y) (aplus x z))
+let aplus_assoc_4 #a r w x y z =
+ let aplus = r.cm_add.mult in
+ let assoc = r.cm_add.associativity in
+ let comm = r.cm_add.commutativity in
+ calc (==) {
+ aplus (aplus w x) (aplus y z);
+ == { assoc w x (aplus y z) }
+ aplus w (aplus x (aplus y z));
+ == { comm x (aplus y z) }
+ aplus w (aplus (aplus y z) x);
+ == { assoc w (aplus y z) x }
+ aplus (aplus w (aplus y z)) x;
+ == { assoc w y z }
+ aplus (aplus (aplus w y) z) x;
+ == { assoc (aplus w y) z x }
+ aplus (aplus w y) (aplus z x);
+ == { comm z x }
+ aplus (aplus w y) (aplus x z);
+ }val canonical_sum_merge_ok: #a:eqtype -> r:cr a -> vm:vmap a
+ -> s1:canonical_sum a -> s2:canonical_sum a ->
+ Lemma
+ (ensures
+ interp_cs r vm (canonical_sum_merge r s1 s2) ==
+ r.cm_add.mult (interp_cs r vm s1) (interp_cs r vm s2))
+ (decreases %[s1; s2; 0])val csm_aux_ok: #a:eqtype -> r:cr a -> vm:vmap a
+ -> c1:a -> l1:varlist -> t1:canonical_sum a -> s2:canonical_sum a ->
+ Lemma
+ (ensures
+ interp_cs r vm (csm_aux r c1 l1 t1 s2) ==
+ r.cm_add.mult (interp_cs r vm (Cons_monom c1 l1 t1)) (interp_cs r vm s2))
+ (decreases %[t1; s2; 1])let rec canonical_sum_merge_ok #a r vm s1 s2 =
+ let aone = r.cm_mult.unit in
+ let aplus = r.cm_add.mult in
+ let amult = r.cm_mult.mult in
+ match s1 with
+ | Cons_monom c1 l1 t1 -> csm_aux_ok #a r vm c1 l1 t1 s2
+ | Cons_varlist l1 t1 ->
+ calc (==) {
+ interp_cs r vm (canonical_sum_merge r s1 s2);
+ == { }
+ interp_cs r vm (csm_aux r aone l1 t1 s2);
+ == { csm_aux_ok #a r vm aone l1 t1 s2 }
+ aplus (interp_cs r vm (Cons_monom aone l1 t1))
+ (interp_cs r vm s2);
+ == { ics_aux_ok r vm (interp_vl r vm l1) t1 }
+ aplus (interp_cs r vm (Cons_varlist l1 t1))
+ (interp_cs r vm s2);
+ }
+ | Nil_monom -> ()
+and csm_aux_ok #a r vm c1 l1 t1 s2 =
+ let aplus = r.cm_add.mult in
+ let aone = r.cm_mult.unit in
+ let amult = r.cm_mult.mult in
+ match s2 with
+ | Nil_monom -> ()
+ | Cons_monom c2 l2 t2 ->
+ let s1 = Cons_monom c1 l1 t1 in
+ if l1 = l2 then
+ begin
+ calc (==) {
+ interp_cs r vm (csm_aux r c1 l1 t1 s2);
+ == { }
+ ics_aux r vm (interp_m r vm (aplus c1 c2) l1)
+ (canonical_sum_merge r t1 t2);
+ == { ics_aux_ok r vm (interp_m r vm (aplus c1 c2) l1)
+ (canonical_sum_merge r t1 t2) }
+ aplus (interp_m r vm (aplus c1 c2) l1)
+ (interp_cs r vm (canonical_sum_merge r t1 t2));
+ == { interp_m_ok r vm (aplus c1 c2) l1 }
+ aplus (amult (aplus c1 c2) (interp_vl r vm l1))
+ (interp_cs r vm (canonical_sum_merge r t1 t2));
+ == { canonical_sum_merge_ok r vm t1 t2 }
+ aplus (amult (aplus c1 c2) (interp_vl r vm l1))
+ (aplus (interp_cs r vm t1) (interp_cs r vm t2));
+ == { distribute_right r c1 c2 (interp_vl r vm l1) }
+ aplus (aplus (amult c1 (interp_vl r vm l1))
+ (amult c2 (interp_vl r vm l2)))
+ (aplus (interp_cs r vm t1)
+ (interp_cs r vm t2));
+ == { aplus_assoc_4 r
+ (amult c1 (interp_vl r vm l1))
+ (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm t1)
+ (interp_cs r vm t2) }
+ aplus (aplus (amult c1 (interp_vl r vm l1)) (interp_cs r vm t1))
+ (aplus (amult c2 (interp_vl r vm l2)) (interp_cs r vm t2));
+ == { ics_aux_ok r vm (amult c1 (interp_vl r vm l1)) t1;
+ interp_m_ok r vm c1 l1 }
+ aplus (interp_cs r vm s1)
+ (aplus (amult c2 (interp_vl r vm l2)) (interp_cs r vm t2));
+ == { ics_aux_ok r vm (amult c2 (interp_vl r vm l2)) t2;
+ interp_m_ok r vm c2 l2 }
+ aplus (interp_cs r vm s1) (interp_cs r vm s2);
+ }
+ end
+ else if varlist_lt l1 l2 then
+ begin
+ calc (==) {
+ interp_cs r vm (canonical_sum_merge r s1 s2);
+ == { }
+ ics_aux r vm (interp_m r vm c1 l1)
+ (canonical_sum_merge r t1 s2);
+ == { ics_aux_ok r vm (interp_m r vm c1 l1)
+ (canonical_sum_merge r t1 s2) }
+ aplus (interp_m r vm c1 l1)
+ (interp_cs r vm (canonical_sum_merge r t1 s2));
+ == { interp_m_ok r vm c1 l1 }
+ aplus (amult c1 (interp_vl r vm l1))
+ (interp_cs r vm (canonical_sum_merge r t1 s2));
+ == { canonical_sum_merge_ok r vm t1 s2 }
+ aplus (amult c1 (interp_vl r vm l1))
+ (aplus (interp_cs r vm t1) (interp_cs r vm s2));
+ == { r.cm_add.associativity
+ (amult c1 (interp_vl r vm l1))
+ (interp_cs r vm t1)
+ (interp_cs r vm s2)
+ }
+ aplus (aplus (amult c1 (interp_vl r vm l1))
+ (interp_cs r vm t1))
+ (interp_cs r vm s2);
+ == { ics_aux_ok r vm (amult c1 (interp_vl r vm l1)) t1;
+ interp_m_ok r vm c1 l1 }
+ aplus (interp_cs r vm s1) (interp_cs r vm s2);
+ }
+ end
+ else
+ begin
+ calc (==) {
+ interp_cs r vm (csm_aux r c1 l1 t1 s2);
+ == { }
+ ics_aux r vm (interp_m r vm c2 l2)
+ (csm_aux r c1 l1 t1 t2);
+ == { ics_aux_ok r vm (interp_m r vm c2 l2)
+ (csm_aux r c1 l1 t1 t2) }
+ aplus (interp_m r vm c2 l2)
+ (interp_cs r vm (csm_aux r c1 l1 t1 t2));
+ == { interp_m_ok r vm c2 l2 }
+ aplus (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm (csm_aux r c1 l1 t1 t2));
+ == { csm_aux_ok r vm c1 l1 t1 t2 }
+ aplus (amult c2 (interp_vl r vm l2))
+ (aplus (interp_cs r vm s1) (interp_cs r vm t2));
+ == { r.cm_add.commutativity (interp_cs r vm s1) (interp_cs r vm t2) }
+ aplus (amult c2 (interp_vl r vm l2))
+ (aplus (interp_cs r vm t2) (interp_cs r vm s1));
+ == { r.cm_add.associativity
+ (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm t2)
+ (interp_cs r vm s1)
+ }
+ aplus (aplus (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm t2))
+ (interp_cs r vm s1);
+ == { ics_aux_ok r vm (amult c1 (interp_vl r vm l1)) t1;
+ interp_m_ok r vm c1 l1 }
+ aplus (interp_cs r vm s2) (interp_cs r vm s1);
+ == { r.cm_add.commutativity (interp_cs r vm s1) (interp_cs r vm s2) }
+ aplus (interp_cs r vm s1) (interp_cs r vm s2);
+ }
+ end
+ | Cons_varlist l2 t2 -> // Same as Cons_monom with c2 = aone
+ let c2 = aone in
+ let s1 = Cons_monom c1 l1 t1 in
+ if l1 = l2 then
+ begin
+ calc (==) {
+ interp_cs r vm (csm_aux r c1 l1 t1 s2);
+ == { }
+ ics_aux r vm (interp_m r vm (aplus c1 c2) l1)
+ (canonical_sum_merge r t1 t2);
+ == { ics_aux_ok r vm (interp_m r vm (aplus c1 c2) l1)
+ (canonical_sum_merge r t1 t2) }
+ aplus (interp_m r vm (aplus c1 c2) l1)
+ (interp_cs r vm (canonical_sum_merge r t1 t2));
+ == { interp_m_ok r vm (aplus c1 c2) l1 }
+ aplus (amult (aplus c1 c2) (interp_vl r vm l1))
+ (interp_cs r vm (canonical_sum_merge r t1 t2));
+ == { canonical_sum_merge_ok r vm t1 t2 }
+ aplus (amult (aplus c1 c2) (interp_vl r vm l1))
+ (aplus (interp_cs r vm t1) (interp_cs r vm t2));
+ == { distribute_right r c1 c2 (interp_vl r vm l1) }
+ aplus (aplus (amult c1 (interp_vl r vm l1))
+ (amult c2 (interp_vl r vm l2)))
+ (aplus (interp_cs r vm t1)
+ (interp_cs r vm t2));
+ == { aplus_assoc_4 r
+ (amult c1 (interp_vl r vm l1))
+ (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm t1)
+ (interp_cs r vm t2) }
+ aplus (aplus (amult c1 (interp_vl r vm l1)) (interp_cs r vm t1))
+ (aplus (amult c2 (interp_vl r vm l2)) (interp_cs r vm t2));
+ == { ics_aux_ok r vm (amult c1 (interp_vl r vm l1)) t1;
+ interp_m_ok r vm c1 l1 }
+ aplus (interp_cs r vm s1)
+ (aplus (amult c2 (interp_vl r vm l2)) (interp_cs r vm t2));
+ == { ics_aux_ok r vm (amult c2 (interp_vl r vm l2)) t2;
+ interp_m_ok r vm c2 l2 }
+ aplus (interp_cs r vm s1) (interp_cs r vm s2);
+ }
+ end
+ else if varlist_lt l1 l2 then
+ begin
+ calc (==) {
+ interp_cs r vm (canonical_sum_merge r s1 s2);
+ == { }
+ ics_aux r vm (interp_m r vm c1 l1)
+ (canonical_sum_merge r t1 s2);
+ == { ics_aux_ok r vm (interp_m r vm c1 l1)
+ (canonical_sum_merge r t1 s2) }
+ aplus (interp_m r vm c1 l1)
+ (interp_cs r vm (canonical_sum_merge r t1 s2));
+ == { interp_m_ok r vm c1 l1 }
+ aplus (amult c1 (interp_vl r vm l1))
+ (interp_cs r vm (canonical_sum_merge r t1 s2));
+ == { canonical_sum_merge_ok r vm t1 s2 }
+ aplus (amult c1 (interp_vl r vm l1))
+ (aplus (interp_cs r vm t1) (interp_cs r vm s2));
+ == { r.cm_add.associativity
+ (amult c1 (interp_vl r vm l1))
+ (interp_cs r vm t1)
+ (interp_cs r vm s2)
+ }
+ aplus (aplus (amult c1 (interp_vl r vm l1))
+ (interp_cs r vm t1))
+ (interp_cs r vm s2);
+ == { ics_aux_ok r vm (amult c1 (interp_vl r vm l1)) t1;
+ interp_m_ok r vm c1 l1 }
+ aplus (interp_cs r vm s1) (interp_cs r vm s2);
+ }
+ end
+ else
+ begin
+ calc (==) {
+ interp_cs r vm (csm_aux r c1 l1 t1 s2);
+ == { }
+ ics_aux r vm (interp_m r vm c2 l2)
+ (csm_aux r c1 l1 t1 t2);
+ == { ics_aux_ok r vm (interp_m r vm c2 l2)
+ (csm_aux r c1 l1 t1 t2) }
+ aplus (interp_m r vm c2 l2)
+ (interp_cs r vm (csm_aux r c1 l1 t1 t2));
+ == { interp_m_ok r vm c2 l2 }
+ aplus (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm (csm_aux r c1 l1 t1 t2));
+ == { csm_aux_ok r vm c1 l1 t1 t2 }
+ aplus (amult c2 (interp_vl r vm l2))
+ (aplus (interp_cs r vm s1) (interp_cs r vm t2));
+ == { r.cm_add.commutativity (interp_cs r vm s1) (interp_cs r vm t2) }
+ aplus (amult c2 (interp_vl r vm l2))
+ (aplus (interp_cs r vm t2) (interp_cs r vm s1));
+ == { r.cm_add.associativity
+ (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm t2)
+ (interp_cs r vm s1)
+ }
+ aplus (aplus (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm t2))
+ (interp_cs r vm s1);
+ == { ics_aux_ok r vm (amult c1 (interp_vl r vm l1)) t1;
+ interp_m_ok r vm c1 l1 }
+ aplus (interp_cs r vm s2) (interp_cs r vm s1);
+ == { r.cm_add.commutativity (interp_cs r vm s1) (interp_cs r vm s2) }
+ aplus (interp_cs r vm s1) (interp_cs r vm s2);
+ }
+ endval monom_insert_ok: #a:eqtype -> r:cr a -> vm:vmap a
+ -> c1:a -> l1:varlist -> s2:canonical_sum a ->
+ Lemma
+ (interp_cs r vm (monom_insert r c1 l1 s2) ==
+ r.cm_add.mult (r.cm_mult.mult c1 (interp_vl r vm l1)) (interp_cs r vm s2))
+let rec monom_insert_ok #a r vm c1 l1 s2 =
+ let aplus = r.cm_add.mult in
+ let amult = r.cm_mult.mult in
+ let aone = r.cm_mult.unit in
+ match s2 with
+ | Cons_monom c2 l2 t2 ->
+ if l1 = l2
+ then
+ calc (==) {
+ interp_cs r vm (monom_insert r c1 l1 s2);
+ == { }
+ interp_cs r vm (Cons_monom (aplus c1 c2) l1 t2);
+ == { }
+ ics_aux r vm (interp_m r vm (aplus c1 c2) l1) t2;
+ == { ics_aux_ok r vm (interp_m r vm (aplus c1 c2) l1) t2 }
+ aplus (interp_m r vm (aplus c1 c2) l1) (interp_cs r vm t2);
+ == { interp_m_ok r vm (aplus c1 c2) l1 }
+ aplus (amult (aplus c1 c2) (interp_vl r vm l2)) (interp_cs r vm t2);
+ == { distribute_right r c1 c2 (interp_vl r vm l2) }
+ aplus (aplus (amult c1 (interp_vl r vm l1))
+ (amult c2 (interp_vl r vm l2)))
+ (interp_cs r vm t2);
+ == { r.cm_add.associativity
+ (amult c1 (interp_vl r vm l1))
+ (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm t2) }
+ aplus (amult c1 (interp_vl r vm l1))
+ (aplus (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm t2));
+ == { ics_aux_ok r vm (interp_m r vm c2 l2) t2 }
+ aplus (amult c1 (interp_vl r vm l1)) (interp_cs r vm s2);
+ }
+ else
+ if varlist_lt l1 l2 then ()
+ else
+ calc (==) {
+ interp_cs r vm (monom_insert r c1 l1 s2);
+ == { }
+ interp_cs r vm (Cons_monom c2 l2 (monom_insert r c1 l1 t2));
+ == { }
+ aplus (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm (monom_insert r c1 l1 t2));
+ == { monom_insert_ok r vm c1 l1 t2 }
+ aplus (amult c2 (interp_vl r vm l2))
+ (aplus (amult c1 (interp_vl r vm l1))
+ (interp_cs r vm t2));
+ == { r.cm_add.commutativity
+ (amult c1 (interp_vl r vm l1))
+ (interp_cs r vm t2) }
+ aplus (amult c2 (interp_vl r vm l2))
+ (aplus (interp_cs r vm t2)
+ (amult c1 (interp_vl r vm l1)));
+ == { r.cm_add.associativity
+ (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm t2)
+ (amult c1 (interp_vl r vm l1)) }
+ aplus (aplus (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm t2))
+ (amult c1 (interp_vl r vm l1));
+ == { ics_aux_ok r vm (interp_m r vm c2 l2) t2 }
+ aplus (interp_cs r vm s2) (amult c1 (interp_vl r vm l1));
+ == { r.cm_add.commutativity
+ (interp_cs r vm s2)
+ (amult c1 (interp_vl r vm l1)) }
+ aplus (amult c1 (interp_vl r vm l1)) (interp_cs r vm s2);
+ }
+ | Cons_varlist l2 t2 -> // Same as Cons_monom with c2 = aone
+ let c2 = aone in
+ if l1 = l2
+ then
+ calc (==) {
+ interp_cs r vm (monom_insert r c1 l1 s2);
+ == { }
+ interp_cs r vm (Cons_monom (aplus c1 c2) l1 t2);
+ == { }
+ ics_aux r vm (interp_m r vm (aplus c1 c2) l1) t2;
+ == { ics_aux_ok r vm (interp_m r vm (aplus c1 c2) l1) t2 }
+ aplus (interp_m r vm (aplus c1 c2) l1) (interp_cs r vm t2);
+ == { interp_m_ok r vm (aplus c1 c2) l1 }
+ aplus (amult (aplus c1 c2) (interp_vl r vm l2)) (interp_cs r vm t2);
+ == { distribute_right r c1 c2 (interp_vl r vm l2) }
+ aplus (aplus (amult c1 (interp_vl r vm l1))
+ (amult c2 (interp_vl r vm l2)))
+ (interp_cs r vm t2);
+ == { r.cm_add.associativity
+ (amult c1 (interp_vl r vm l1))
+ (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm t2) }
+ aplus (amult c1 (interp_vl r vm l1))
+ (aplus (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm t2));
+ == { ics_aux_ok r vm (interp_m r vm c2 l2) t2 }
+ aplus (amult c1 (interp_vl r vm l1)) (interp_cs r vm s2);
+ }
+ else
+ if varlist_lt l1 l2 then ()
+ else
+ calc (==) {
+ interp_cs r vm (monom_insert r c1 l1 s2);
+ == { }
+ interp_cs r vm (Cons_monom c2 l2 (monom_insert r c1 l1 t2));
+ == { }
+ aplus (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm (monom_insert r c1 l1 t2));
+ == { monom_insert_ok r vm c1 l1 t2 }
+ aplus (amult c2 (interp_vl r vm l2))
+ (aplus (amult c1 (interp_vl r vm l1))
+ (interp_cs r vm t2));
+ == { r.cm_add.commutativity
+ (amult c1 (interp_vl r vm l1))
+ (interp_cs r vm t2) }
+ aplus (amult c2 (interp_vl r vm l2))
+ (aplus (interp_cs r vm t2)
+ (amult c1 (interp_vl r vm l1)));
+ == { r.cm_add.associativity
+ (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm t2)
+ (amult c1 (interp_vl r vm l1)) }
+ aplus (aplus (amult c2 (interp_vl r vm l2))
+ (interp_cs r vm t2))
+ (amult c1 (interp_vl r vm l1));
+ == { ics_aux_ok r vm (interp_m r vm c2 l2) t2 }
+ aplus (interp_cs r vm s2) (amult c1 (interp_vl r vm l1));
+ == { r.cm_add.commutativity
+ (interp_cs r vm s2)
+ (amult c1 (interp_vl r vm l1)) }
+ aplus (amult c1 (interp_vl r vm l1)) (interp_cs r vm s2);
+ }
+ | Nil_monom -> ()val varlist_insert_ok: #a:eqtype -> r:cr a -> vm:vmap a
+ -> l1:varlist -> s2:canonical_sum a ->
+ Lemma (interp_cs r vm (varlist_insert r l1 s2) ==
+ r.cm_add.mult (interp_vl r vm l1) (interp_cs r vm s2))
+let varlist_insert_ok #a r vm l1 s2 =
+ let aone = r.cm_mult.unit in
+ monom_insert_ok r vm aone l1 s2val canonical_sum_scalar_ok: #a:eqtype -> r:cr a -> vm:vmap a
+ -> c0:a -> s:canonical_sum a ->
+ Lemma (
+ interp_cs r vm (canonical_sum_scalar r c0 s) ==
+ r.cm_mult.mult c0 (interp_cs r vm s))
+let rec canonical_sum_scalar_ok #a r vm c0 s =
+ let aone = r.cm_mult.unit in
+ let aplus = r.cm_add.mult in
+ let amult = r.cm_mult.mult in
+ match s with
+ | Cons_monom c l t ->
+ calc (==) {
+ interp_cs r vm (canonical_sum_scalar r c0 s);
+ == { }
+ interp_cs r vm (Cons_monom (amult c0 c) l (canonical_sum_scalar r c0 t));
+ == { }
+ aplus (amult (amult c0 c) (interp_vl r vm l))
+ (interp_cs r vm (canonical_sum_scalar r c0 t));
+ == { r.cm_mult.associativity c0 c (interp_vl r vm l) }
+ aplus (amult c0 (amult c (interp_vl r vm l)))
+ (interp_cs r vm (canonical_sum_scalar r c0 t));
+ == { canonical_sum_scalar_ok r vm c0 t }
+ aplus (amult c0 (amult c (interp_vl r vm l)))
+ (amult c0 (interp_cs r vm t));
+ == { r.distribute c0 (amult c (interp_vl r vm l))
+ (interp_cs r vm t) }
+ amult c0 (aplus (amult c (interp_vl r vm l)) (interp_cs r vm t));
+ == { }
+ amult c0 (interp_cs r vm s);
+ }
+ | Cons_varlist l t -> // Same as Cons_monom c l t with c = r.cm_mult.unit
+ let c = aone in
+ calc (==) {
+ interp_cs r vm (canonical_sum_scalar r c0 s);
+ == { }
+ interp_cs r vm (Cons_monom (amult c0 c) l (canonical_sum_scalar r c0 t));
+ == { }
+ aplus (amult (amult c0 c) (interp_vl r vm l))
+ (interp_cs r vm (canonical_sum_scalar r c0 t));
+ == { r.cm_mult.associativity c0 c (interp_vl r vm l) }
+ aplus (amult c0 (amult c (interp_vl r vm l)))
+ (interp_cs r vm (canonical_sum_scalar r c0 t));
+ == { canonical_sum_scalar_ok r vm c0 t }
+ aplus (amult c0 (amult c (interp_vl r vm l)))
+ (amult c0 (interp_cs r vm t));
+ == { r.distribute c0 (amult c (interp_vl r vm l))
+ (interp_cs r vm t) }
+ amult c0 (aplus (amult c (interp_vl r vm l)) (interp_cs r vm t));
+ == { }
+ amult c0 (interp_cs r vm s);
+ }
+ | Nil_monom -> ()val canonical_sum_scalar2_ok: #a:eqtype -> r:cr a -> vm:vmap a
+ -> l0:varlist -> s:canonical_sum a ->
+ Lemma (
+ interp_cs r vm (canonical_sum_scalar2 r l0 s) ==
+ r.cm_mult.mult (interp_vl r vm l0) (interp_cs r vm s))
+let rec canonical_sum_scalar2_ok #a r vm l0 s =
+ let aone = r.cm_mult.unit in
+ let aplus = r.cm_add.mult in
+ let amult = r.cm_mult.mult in
+ match s with
+ | Cons_monom c l t ->
+ calc (==) {
+ interp_cs r vm (canonical_sum_scalar2 r l0 s);
+ == { }
+ interp_cs r vm
+ (monom_insert r c (varlist_merge l0 l) (canonical_sum_scalar2 r l0 t));
+ == { monom_insert_ok r vm c (varlist_merge l0 l) (canonical_sum_scalar2 r l0 t) }
+ aplus (amult c (interp_vl r vm (varlist_merge l0 l)))
+ (interp_cs r vm (canonical_sum_scalar2 r l0 t));
+ == { varlist_merge_ok r vm l0 l }
+ aplus (amult c (amult (interp_vl r vm l0) (interp_vl r vm l)))
+ (interp_cs r vm (canonical_sum_scalar2 r l0 t));
+ == { canonical_sum_scalar2_ok r vm l0 t }
+ aplus (amult c (amult (interp_vl r vm l0) (interp_vl r vm l)))
+ (amult (interp_vl r vm l0) (interp_cs r vm t));
+ == { r.cm_mult.associativity c (interp_vl r vm l0)
+ (interp_vl r vm l) }
+ aplus (amult (amult c (interp_vl r vm l0)) (interp_vl r vm l))
+ (amult (interp_vl r vm l0) (interp_cs r vm t));
+ == { r.cm_mult.commutativity (interp_vl r vm l0) c }
+ aplus (amult (amult (interp_vl r vm l0) c) (interp_vl r vm l))
+ (amult (interp_vl r vm l0) (interp_cs r vm t));
+ == { r.cm_mult.associativity (interp_vl r vm l0) c (interp_vl r vm l) }
+ aplus (amult (interp_vl r vm l0) (amult c (interp_vl r vm l)))
+ (amult (interp_vl r vm l0) (interp_cs r vm t));
+ == { r.distribute (interp_vl r vm l0)
+ (amult c (interp_vl r vm l)) (interp_cs r vm t) }
+ amult (interp_vl r vm l0)
+ (aplus (amult c (interp_vl r vm l)) (interp_cs r vm t));
+ == { }
+ amult (interp_vl r vm l0) (interp_cs r vm s);
+ }
+ | Cons_varlist l t -> // Same as Cons_monom c l t with c = aone
+ let c = aone in
+ calc (==) {
+ interp_cs r vm (canonical_sum_scalar2 r l0 s);
+ == { }
+ interp_cs r vm
+ (monom_insert r c (varlist_merge l0 l) (canonical_sum_scalar2 r l0 t));
+ == { monom_insert_ok r vm c (varlist_merge l0 l) (canonical_sum_scalar2 r l0 t) }
+ aplus (amult c (interp_vl r vm (varlist_merge l0 l)))
+ (interp_cs r vm (canonical_sum_scalar2 r l0 t));
+ == { varlist_merge_ok r vm l0 l }
+ aplus (amult c (amult (interp_vl r vm l0) (interp_vl r vm l)))
+ (interp_cs r vm (canonical_sum_scalar2 r l0 t));
+ == { canonical_sum_scalar2_ok r vm l0 t }
+ aplus (amult c (amult (interp_vl r vm l0) (interp_vl r vm l)))
+ (amult (interp_vl r vm l0) (interp_cs r vm t));
+ == { r.cm_mult.associativity c (interp_vl r vm l0)
+ (interp_vl r vm l) }
+ aplus (amult (amult c (interp_vl r vm l0)) (interp_vl r vm l))
+ (amult (interp_vl r vm l0) (interp_cs r vm t));
+ == { r.cm_mult.commutativity (interp_vl r vm l0) c }
+ aplus (amult (amult (interp_vl r vm l0) c) (interp_vl r vm l))
+ (amult (interp_vl r vm l0) (interp_cs r vm t));
+ == { r.cm_mult.associativity (interp_vl r vm l0) c (interp_vl r vm l) }
+ aplus (amult (interp_vl r vm l0) (amult c (interp_vl r vm l)))
+ (amult (interp_vl r vm l0) (interp_cs r vm t));
+ == { r.distribute (interp_vl r vm l0)
+ (amult c (interp_vl r vm l)) (interp_cs r vm t) }
+ amult (interp_vl r vm l0)
+ (aplus (amult c (interp_vl r vm l)) (interp_cs r vm t));
+ == { }
+ amult (interp_vl r vm l0) (interp_cs r vm s);
+ }
+ | Nil_monom -> ()val canonical_sum_scalar3_ok: #a:eqtype -> r:cr a -> vm:vmap a
+ -> c0:a -> l0:varlist -> s:canonical_sum a ->
+ Lemma (
+ interp_cs r vm (canonical_sum_scalar3 r c0 l0 s) ==
+ r.cm_mult.mult (r.cm_mult.mult c0 (interp_vl r vm l0)) (interp_cs r vm s))
+let rec canonical_sum_scalar3_ok #a r vm c0 l0 s =
+ let aone = r.cm_mult.unit in
+ let aplus = r.cm_add.mult in
+ let amult = r.cm_mult.mult in
+ match s with
+ | Cons_monom c l t ->
+ calc (==) {
+ interp_cs r vm (canonical_sum_scalar3 r c0 l0 s);
+ == { }
+ interp_cs r vm
+ (monom_insert r (amult c0 c) (varlist_merge l0 l)
+ (canonical_sum_scalar3 r c0 l0 t));
+ == { monom_insert_ok r vm (amult c0 c) (varlist_merge l0 l) (canonical_sum_scalar3 r c0 l0 t) }
+ aplus (amult (amult c0 c) (interp_vl r vm (varlist_merge l0 l)))
+ (interp_cs r vm (canonical_sum_scalar3 r c0 l0 t));
+ == { varlist_merge_ok r vm l0 l }
+ aplus (amult (amult c0 c) (amult (interp_vl r vm l0) (interp_vl r vm l)))
+ (interp_cs r vm (canonical_sum_scalar3 r c0 l0 t));
+ == { canonical_sum_scalar3_ok r vm c0 l0 t }
+ aplus (amult (amult c0 c) (amult (interp_vl r vm l0) (interp_vl r vm l)))
+ (amult (amult c0 (interp_vl r vm l0)) (interp_cs r vm t));
+ == { r.cm_mult.associativity (amult c0 c)
+ (interp_vl r vm l0) (interp_vl r vm l) }
+ aplus (amult (amult (amult c0 c) (interp_vl r vm l0)) (interp_vl r vm l))
+ (amult (amult c0 (interp_vl r vm l0)) (interp_cs r vm t));
+ == { r.cm_mult.commutativity c0 c }
+ aplus (amult (amult (amult c c0) (interp_vl r vm l0)) (interp_vl r vm l))
+ (amult (amult c0 (interp_vl r vm l0)) (interp_cs r vm t));
+ == { r.cm_mult.associativity c c0 (interp_vl r vm l0) }
+ aplus (amult (amult c (amult c0 (interp_vl r vm l0))) (interp_vl r vm l))
+ (amult (amult c0 (interp_vl r vm l0)) (interp_cs r vm t));
+ == { r.cm_mult.commutativity c (amult c0 (interp_vl r vm l0)) }
+ aplus (amult (amult (amult c0 (interp_vl r vm l0)) c) (interp_vl r vm l))
+ (amult (amult c0 (interp_vl r vm l0)) (interp_cs r vm t));
+ == { r.cm_mult.associativity (amult c0 (interp_vl r vm l0)) c (interp_vl r vm l) }
+ aplus (amult (amult c0 (interp_vl r vm l0)) (amult c (interp_vl r vm l)))
+ (amult (amult c0 (interp_vl r vm l0)) (interp_cs r vm t));
+ == { r.distribute (amult c0 (interp_vl r vm l0))
+ (amult c (interp_vl r vm l)) (interp_cs r vm t) }
+ amult (amult c0 (interp_vl r vm l0))
+ (aplus (amult c (interp_vl r vm l)) (interp_cs r vm t));
+ == { }
+ amult (amult c0 (interp_vl r vm l0)) (interp_cs r vm s);
+ }
+ | Cons_varlist l t -> // Same as Cons_monom c l t with c = aone
+ let c = aone in
+ calc (==) {
+ interp_cs r vm (canonical_sum_scalar3 r c0 l0 s);
+ == { }
+ interp_cs r vm
+ (monom_insert r (amult c0 c) (varlist_merge l0 l)
+ (canonical_sum_scalar3 r c0 l0 t));
+ == { monom_insert_ok r vm (amult c0 c) (varlist_merge l0 l) (canonical_sum_scalar3 r c0 l0 t) }
+ aplus (amult (amult c0 c) (interp_vl r vm (varlist_merge l0 l)))
+ (interp_cs r vm (canonical_sum_scalar3 r c0 l0 t));
+ == { varlist_merge_ok r vm l0 l }
+ aplus (amult (amult c0 c) (amult (interp_vl r vm l0) (interp_vl r vm l)))
+ (interp_cs r vm (canonical_sum_scalar3 r c0 l0 t));
+ == { canonical_sum_scalar3_ok r vm c0 l0 t }
+ aplus (amult (amult c0 c) (amult (interp_vl r vm l0) (interp_vl r vm l)))
+ (amult (amult c0 (interp_vl r vm l0)) (interp_cs r vm t));
+ == { r.cm_mult.associativity (amult c0 c)
+ (interp_vl r vm l0) (interp_vl r vm l) }
+ aplus (amult (amult (amult c0 c) (interp_vl r vm l0)) (interp_vl r vm l))
+ (amult (amult c0 (interp_vl r vm l0)) (interp_cs r vm t));
+ == { r.cm_mult.commutativity c0 c }
+ aplus (amult (amult (amult c c0) (interp_vl r vm l0)) (interp_vl r vm l))
+ (amult (amult c0 (interp_vl r vm l0)) (interp_cs r vm t));
+ == { r.cm_mult.associativity c c0 (interp_vl r vm l0) }
+ aplus (amult (amult c (amult c0 (interp_vl r vm l0))) (interp_vl r vm l))
+ (amult (amult c0 (interp_vl r vm l0)) (interp_cs r vm t));
+ == { r.cm_mult.commutativity c (amult c0 (interp_vl r vm l0)) }
+ aplus (amult (amult (amult c0 (interp_vl r vm l0)) c) (interp_vl r vm l))
+ (amult (amult c0 (interp_vl r vm l0)) (interp_cs r vm t));
+ == { r.cm_mult.associativity (amult c0 (interp_vl r vm l0)) c (interp_vl r vm l) }
+ aplus (amult (amult c0 (interp_vl r vm l0)) (amult c (interp_vl r vm l)))
+ (amult (amult c0 (interp_vl r vm l0)) (interp_cs r vm t));
+ == { r.distribute (amult c0 (interp_vl r vm l0))
+ (amult c (interp_vl r vm l)) (interp_cs r vm t) }
+ amult (amult c0 (interp_vl r vm l0))
+ (aplus (amult c (interp_vl r vm l)) (interp_cs r vm t));
+ == { }
+ amult (amult c0 (interp_vl r vm l0)) (interp_cs r vm s);
+ }
+ | Nil_monom -> ()val canonical_sum_prod_ok: #a:eqtype -> r:cr a -> vm:vmap a ->
+ s1:canonical_sum a -> s2:canonical_sum a ->
+ Lemma (interp_cs r vm (canonical_sum_prod r s1 s2) ==
+ r.cm_mult.mult (interp_cs r vm s1) (interp_cs r vm s2))
+let rec canonical_sum_prod_ok #a r vm s1 s2 =
+ let aone = r.cm_mult.unit in
+ let aplus = r.cm_add.mult in
+ let amult = r.cm_mult.mult in
+ match s1 with
+ | Cons_monom c1 l1 t1 ->
+ calc (==) {
+ interp_cs r vm (canonical_sum_prod r s1 s2);
+ == { }
+ interp_cs r vm
+ (canonical_sum_merge r (canonical_sum_scalar3 r c1 l1 s2)
+ (canonical_sum_prod r t1 s2));
+ == { canonical_sum_merge_ok r vm
+ (canonical_sum_scalar3 r c1 l1 s2)
+ (canonical_sum_prod r t1 s2) }
+ aplus (interp_cs r vm (canonical_sum_scalar3 r c1 l1 s2))
+ (interp_cs r vm (canonical_sum_prod r t1 s2));
+ == { canonical_sum_scalar3_ok r vm c1 l1 s2;
+ canonical_sum_prod_ok r vm t1 s2 }
+ aplus (amult (amult c1 (interp_vl r vm l1)) (interp_cs r vm s2))
+ (amult (interp_cs r vm t1) (interp_cs r vm s2));
+ == { distribute_right r (amult c1 (interp_vl r vm l1))
+ (interp_cs r vm t1) (interp_cs r vm s2) }
+ amult (aplus (amult c1 (interp_vl r vm l1)) (interp_cs r vm t1))
+ (interp_cs r vm s2);
+ == { }
+ amult (interp_cs r vm s1) (interp_cs r vm s2);
+ }
+ | Cons_varlist l1 t1 ->
+ calc (==) {
+ interp_cs r vm (canonical_sum_prod r s1 s2);
+ == { }
+ interp_cs r vm
+ (canonical_sum_merge r (canonical_sum_scalar2 r l1 s2)
+ (canonical_sum_prod r t1 s2));
+ == { canonical_sum_merge_ok r vm
+ (canonical_sum_scalar2 r l1 s2)
+ (canonical_sum_prod r t1 s2) }
+ aplus (interp_cs r vm (canonical_sum_scalar2 r l1 s2))
+ (interp_cs r vm (canonical_sum_prod r t1 s2));
+ == { canonical_sum_scalar2_ok r vm l1 s2;
+ canonical_sum_prod_ok r vm t1 s2 }
+ aplus (amult (interp_vl r vm l1) (interp_cs r vm s2))
+ (amult (interp_cs r vm t1) (interp_cs r vm s2));
+ == { distribute_right r (interp_vl r vm l1)
+ (interp_cs r vm t1) (interp_cs r vm s2) }
+ amult (aplus (interp_vl r vm l1) (interp_cs r vm t1))
+ (interp_cs r vm s2);
+ == { }
+ amult (interp_cs r vm s1) (interp_cs r vm s2);
+ }
+ | Nil_monom -> ()val spolynomial_normalize_ok: #a:eqtype -> r:cr a -> vm:vmap a -> p:spolynomial a ->
+ Lemma (interp_cs r vm (spolynomial_normalize r p) == interp_sp r vm p)
+let rec spolynomial_normalize_ok #a r vm p =
+ match p with
+ | SPvar _ -> ()
+ | SPconst _ -> ()
+ | SPplus l q ->
+ canonical_sum_merge_ok r vm
+ (spolynomial_normalize r l) (spolynomial_normalize r q);
+ spolynomial_normalize_ok r vm l;
+ spolynomial_normalize_ok r vm q
+ | SPmult l q ->
+ canonical_sum_prod_ok r vm
+ (spolynomial_normalize r l) (spolynomial_normalize r q);
+ spolynomial_normalize_ok r vm l;
+ spolynomial_normalize_ok r vm qval canonical_sum_simplify_ok: #a:eqtype -> r:cr a -> vm:vmap a -> s:canonical_sum a ->
+ Lemma (interp_cs r vm (canonical_sum_simplify r s) == interp_cs r vm s)
+let rec canonical_sum_simplify_ok #a r vm s =
+ let azero = r.cm_add.unit in
+ let aone = r.cm_mult.unit in
+ match s with
+ | Cons_monom c _ t -> canonical_sum_simplify_ok r vm t
+ | Cons_varlist _ t -> canonical_sum_simplify_ok r vm t
+ | Nil_monom -> ()val spolynomial_simplify_ok: #a:eqtype -> r:cr a -> vm:vmap a -> p:spolynomial a ->
+ Lemma (interp_cs r vm (spolynomial_simplify r p) == interp_sp r vm p)
+let spolynomial_simplify_ok #a r vm p =
+ canonical_sum_simplify_ok r vm (spolynomial_normalize r p);
+ spolynomial_normalize_ok r vm pval polynomial_normalize:Unidentified product: [#a:eqtype] Unidentified product: [cr a] Unidentified product: [polynomial a] canonical_sum atype polynomial a =
+ | Pvar : index -> polynomial a
+ | Pconst : a -> polynomial a
+ | Pplus : polynomial a -> polynomial a -> polynomial a
+ | Pmult : polynomial a -> polynomial a -> polynomial a
+ | Popp : polynomial a -> polynomial aCanonize a reflected expression
-val spolynomial_of:Unidentified product: [#a:eqtype] Unidentified product: [cr a] Unidentified product: [polynomial a] spolynomial aval polynomial_normalize: #a:eqtype -> cr a -> polynomial a -> canonical_sum a[@@canon_attr]
+let rec polynomial_normalize #a r p =
+ match p with
+ | Pvar i -> Cons_varlist (Cons_var i Nil_var) Nil_monom
+ | Pconst c -> Cons_monom c Nil_var Nil_monom
+ | Pplus l q ->
+ canonical_sum_merge r (polynomial_normalize r l) (polynomial_normalize r q)
+ | Pmult l q ->
+ canonical_sum_prod r (polynomial_normalize r l) (polynomial_normalize r q)
+ | Popp p ->
+ canonical_sum_scalar3 r (r.opp r.cm_mult.unit) Nil_var (polynomial_normalize r p)val polynomial_simplify: #a:eqtype -> cr a -> polynomial a -> canonical_sum a[@@canon_attr]
+let polynomial_simplify #a r p =
+ canonical_sum_simplify r
+ (polynomial_normalize r p)Translate to a representation without additive inverses
-let ((interp_p (#a:Type) (r:cr a) (vm:vmap a) (p:polynomial a)):a):let aplus = r.cm_add.mult in let amult = r.cm_mult.mult in match p with (Pconst c) -> c | (Pvar i) -> interp_var vm i | (Pplus p1 p2) -> aplus (interp_p r vm p1) (interp_p r vm p2) | (Pmult p1 p2) -> amult (interp_p r vm p1) (interp_p r vm p2) | (Popp p) -> r.opp (interp_p r vm p)val spolynomial_of: #a:eqtype -> cr a -> polynomial a -> spolynomial a[@@canon_attr]
+let rec spolynomial_of #a r p =
+ match p with
+ | Pvar i -> SPvar i
+ | Pconst c -> SPconst c
+ | Pplus l q -> SPplus (spolynomial_of r l) (spolynomial_of r q)
+ | Pmult l q -> SPmult (spolynomial_of r l) (spolynomial_of r q)
+ | Popp p -> SPmult (SPconst (r.opp r.cm_mult.unit)) (spolynomial_of r p)Interpretation of a polynomial
-let ((find_aux (n:nat) (x:term) (xs:list term)):(Tot (option nat) (decreases xs))):match xs with [] -> None | (Prims.Cons x' xs') -> if term_eq x x' then (Some n) else find_aux (+(n, 1)) x xs'[@@canon_attr]
+let rec interp_p (#a:Type) (r:cr a) (vm:vmap a) (p:polynomial a) : a =
+ let aplus = r.cm_add.mult in
+ let amult = r.cm_mult.mult in
+ match p with
+ | Pconst c -> c
+ | Pvar i -> interp_var vm i
+ | Pplus p1 p2 -> aplus (interp_p r vm p1) (interp_p r vm p2)
+ | Pmult p1 p2 -> amult (interp_p r vm p1) (interp_p r vm p2)
+ | Popp p -> r.opp (interp_p r vm p)val spolynomial_of_ok: #a:eqtype -> r:cr a -> vm:vmap a -> p:polynomial a ->
+ Lemma (interp_p r vm p == interp_sp r vm (spolynomial_of r p))
+let rec spolynomial_of_ok #a r vm p =
+ match p with
+ | Pconst c -> ()
+ | Pvar i -> ()
+ | Pplus p1 p2 ->
+ spolynomial_of_ok r vm p1;
+ spolynomial_of_ok r vm p2
+ | Pmult p1 p2 ->
+ spolynomial_of_ok r vm p1;
+ spolynomial_of_ok r vm p2
+ | Popp p ->
+ spolynomial_of_ok r vm p;
+ let x = interp_sp r vm (spolynomial_of r p) in
+ let y = r.cm_mult.mult (r.opp r.cm_mult.unit) x in
+ add_mult_opp r x;
+ opp_unique r x yval polynomial_normalize_ok: #a:eqtype -> r:cr a -> vm:vmap a -> p:polynomial a ->
+ Lemma (interp_cs r vm (polynomial_normalize r p) ==
+ interp_cs r vm (spolynomial_normalize r (spolynomial_of r p)))
+let rec polynomial_normalize_ok #a r vm p =
+ match p with
+ | Pvar _ -> ()
+ | Pconst _ -> ()
+ | Pplus l q ->
+ canonical_sum_merge_ok r vm
+ (polynomial_normalize r l)
+ (polynomial_normalize r q);
+ canonical_sum_merge_ok r vm
+ (spolynomial_normalize r (spolynomial_of r l))
+ (spolynomial_normalize r (spolynomial_of r q));
+ polynomial_normalize_ok r vm l;
+ polynomial_normalize_ok r vm q| Pmult l q ->
+ canonical_sum_prod_ok r vm
+ (polynomial_normalize r l)
+ (polynomial_normalize r q);
+ canonical_sum_prod_ok r vm
+ (spolynomial_normalize r (spolynomial_of r l))
+ (spolynomial_normalize r (spolynomial_of r q));
+ polynomial_normalize_ok r vm l;
+ polynomial_normalize_ok r vm q| Popp p1 ->
+ let l = SPconst (r.opp r.cm_mult.unit) in
+ polynomial_normalize_ok r vm p1;
+ canonical_sum_prod_ok r vm
+ (spolynomial_normalize r l)
+ (polynomial_normalize r p1);
+ canonical_sum_prod_ok r vm
+ (spolynomial_normalize r l)
+ (spolynomial_normalize r (spolynomial_of r p1))val polynomial_simplify_ok: #a:eqtype -> r:cr a -> vm:vmap a -> p:polynomial a ->
+ Lemma (interp_cs r vm (polynomial_simplify r p) == interp_p r vm p)
+let polynomial_simplify_ok #a r vm p =
+ calc (==) {
+ interp_cs r vm (polynomial_simplify r p);
+ == { }
+ interp_cs r vm (canonical_sum_simplify r (polynomial_normalize r p));
+ == { canonical_sum_simplify_ok r vm (polynomial_normalize r p) }
+ interp_cs r vm (polynomial_normalize r p);
+ == { polynomial_normalize_ok r vm p }
+ interp_cs r vm (spolynomial_normalize r (spolynomial_of r p));
+ == { spolynomial_normalize_ok r vm (spolynomial_of r p) }
+ interp_sp r vm (spolynomial_of r p);
+ == { spolynomial_of_ok r vm p }
+ interp_p r vm p;
+ }++Tactic definition
+
Only dump when debugging is on
+let ddump m = if debugging () then dump mlet ((reification_aux (#a:Type) (unquotea:Unidentified product: [term] (Tac a)) (ts:list term) (vm:vmap a) (add:term) (opp:term) (mone:term) (mult:term) (t:term)):(Tac (*(*(polynomial a, list term), vmap a)))):let (hd, tl) = collect_app_ref t in match (FStar.Pervasives.Native.Mktuple2 inspect hd list_unref tl) with ((Tv_FVar fv), [(t1, _); (t2, _)]) -> let ((binop (op:Unidentified product: [polynomial a] Unidentified product: [polynomial a] polynomial a)):(Tac (*(*(polynomial a, list term), vmap a)))) = let (e1, ts, vm) = reification_aux unquotea ts vm add opp mone mult t1 in let (e2, ts, vm) = reification_aux unquotea ts vm add opp mone mult t2 in ((FStar.Pervasives.Native.Mktuple3 op e1 e2 ts vm)) in if term_eq (pack ((Tv_FVar fv))) add then binop Pplus else if term_eq (pack ((Tv_FVar fv))) mult then binop Pmult else make_fvar t unquotea ts vm | ((Tv_FVar fv), [(t1, _)]) -> let ((monop (op:Unidentified product: [polynomial a] polynomial a)):(Tac (*(*(polynomial a, list term), vmap a)))) = let (e, ts, vm) = reification_aux unquotea ts vm add opp mone mult t1 in ((FStar.Pervasives.Native.Mktuple3 op e ts vm)) in if term_eq (pack ((Tv_FVar fv))) opp then monop Popp else make_fvar t unquotea ts vm | ((Tv_Const _), []) -> (FStar.Pervasives.Native.Mktuple3 (Pconst (unquotea t)) ts vm) | (_, _) -> make_fvar t unquotea ts vmlet rec find_aux (n:nat) (x:term) (xs:list term) : Tot (option nat) (decreases xs) =
+ match xs with
+ | [] -> None
+ | x'::xs' -> if term_eq x x' then Some n else find_aux (n+1) x xs'let find = find_aux 0let make_fvar (#a:Type) (t:term) (unquotea:term -> Tac a) (ts:list term)
+ (vm:vmap a) : Tac (polynomial a * list term * vmap a) =
+ match find t ts with
+ | Some v -> (Pvar v, ts, vm)
+ | None ->
+ let vfresh = length ts in
+ let z = unquotea t in
+ (Pvar vfresh, ts @ [t], update vfresh z vm)This expects that add, opp, mone mult, and t have already been normalized
-let steps:(Prims.Cons primops (Prims.Cons iota (Prims.Cons zeta (Prims.Cons delta_attr (Prims.Cons `%%canon_attr (Prims.Nil )) (Prims.Cons delta_only (Prims.Cons `%%FStar.Mul.op_Star (Prims.Cons `%%FStar.Algebra.CommMonoid.int_plus_cm (Prims.Cons `%%FStar.Algebra.CommMonoid.int_multiply_cm (Prims.Cons `%%FStar.Algebra.CommMonoid.__proj__CM__item__mult (Prims.Cons `%%FStar.Algebra.CommMonoid.__proj__CM__item__unit (Prims.Cons `%%__proj__CR__item__cm_add (Prims.Cons `%%__proj__CR__item__opp (Prims.Cons `%%__proj__CR__item__cm_mult (Prims.Cons `%%FStar.List.Tot.Base.assoc (Prims.Cons `%%FStar.Pervasives.Native.fst (Prims.Cons `%%FStar.Pervasives.Native.snd (Prims.Cons `%%FStar.Pervasives.Native.__proj__Mktuple2__item___1 (Prims.Cons `%%FStar.Pervasives.Native.__proj__Mktuple2__item___2 (Prims.Cons `%%FStar.List.Tot.Base.op_At (Prims.Cons `%%FStar.List.Tot.Base.append (Prims.Nil )))))))))))))))) (Prims.Nil ))))))let rec reification_aux (#a:Type) (unquotea:term -> Tac a) (ts:list term) (vm:vmap a) (add opp mone mult t: term) : Tac (polynomial a * list term * vmap a) =ddump ("term = " ^ term_to_string t ^ "\n"); +ddump ("add = " ^ term_to_string add ^ " +\nmul = " ^ term_to_string mult); +ddump ("fv = " ^ term_to_string (pack (Tv_FVar fv)));
+let hd, tl = collect_app_ref t in
+match inspect hd, list_unref tl with
+| Tv_FVar fv, [(t1, _) ; (t2, _)] ->
+ let binop (op:polynomial a -> polynomial a -> polynomial a) : Tac (polynomial a * list term * vmap a) =
+ let (e1, ts, vm) = reification_aux unquotea ts vm add opp mone mult t1 in
+ let (e2, ts, vm) = reification_aux unquotea ts vm add opp mone mult t2 in
+ (op e1 e2, ts, vm)
+ in
+ if term_eq (pack (Tv_FVar fv)) add then binop Pplus else
+ if term_eq (pack (Tv_FVar fv)) mult then binop Pmult else
+ make_fvar t unquotea ts vm
+| Tv_FVar fv, [(t1, _)] ->
+ let monop (op:polynomial a -> polynomial a) : Tac (polynomial a * list term * vmap a) =
+ let (e, ts, vm) = reification_aux unquotea ts vm add opp mone mult t1 in
+ (op e, ts, vm)
+ in
+ if term_eq (pack (Tv_FVar fv)) opp then monop Popp else
+ make_fvar t unquotea ts vm
+| Tv_Const _, [] -> Pconst (unquotea t), ts, vm
+| _, _ -> make_fvar t unquotea ts vmlet steps =
+ [
+ primops;
+ iota;
+ zeta;
+ delta_attr [`%canon_attr];
+ delta_only [
+ `%FStar.Mul.op_Star; // For integer ring
+ `%FStar.Algebra.CommMonoid.int_plus_cm; // For integer ring
+ `%FStar.Algebra.CommMonoid.int_multiply_cm; // For integer ring
+ `%FStar.Algebra.CommMonoid.__proj__CM__item__mult;
+ `%FStar.Algebra.CommMonoid.__proj__CM__item__unit;
+ `%__proj__CR__item__cm_add;
+ `%__proj__CR__item__opp;
+ `%__proj__CR__item__cm_mult;
+ `%FStar.List.Tot.Base.assoc;
+ `%FStar.Pervasives.Native.fst;
+ `%FStar.Pervasives.Native.snd;
+ `%FStar.Pervasives.Native.__proj__Mktuple2__item___1;
+ `%FStar.Pervasives.Native.__proj__Mktuple2__item___2;
+ `%FStar.List.Tot.Base.op_At;
+ `%FStar.List.Tot.Base.append;
+ ]
+ ]let canon_norm () : Tac unit = norm stepslet reification (#a:Type)
+ (unquotea:term -> Tac a) (quotea:a -> Tac term) (tadd topp tmone tmult:term) (munit:a) (ts:list term) : Tac (list (polynomial a) * vmap a) =Be careful not to normalize operations too much +E.g. we don't want to turn ( +% ) into (a + b) % prime +or we won't be able to spot ring operations +ddump ("add = " ^ term_to_string add ^ "\nmult = " ^ term_to_string mult);
+let add = tadd in
+let opp = topp in
+let mone = tmone in
+let mult = tmult in
+let ts = Tactics.Util.map (norm_term steps) ts in
+let (es, _, vm) =
+ Tactics.Util.fold_left
+ (fun (es, vs, vm) t ->
+ let (e, vs, vm) = reification_aux unquotea vs vm add opp mone mult t
+ in (e::es, vs, vm))
+ ([],[], ([], munit)) ts
+in (List.Tot.Base.rev es, vm)The implicit argument in the application of Pconst is crucial
let rec quote_polynomial (#a:Type) (ta:term) (quotea:a -> Tac term) (e:polynomial a) : Tac term =
+ match e with
+ | Pconst c -> mk_app (`Pconst) [(ta, Q_Implicit); (quotea c, Q_Explicit)]
+ | Pvar x -> mk_e_app (`Pvar) [pack (Tv_Const (C_Int x))]
+ | Pplus e1 e2 ->
+ mk_e_app (`Pplus) [quote_polynomial ta quotea e1; quote_polynomial ta quotea e2]
+ | Pmult e1 e2 ->
+ mk_e_app (`Pmult) [quote_polynomial ta quotea e1; quote_polynomial ta quotea e2]
+ | Popp e -> mk_e_app (`Popp) [quote_polynomial ta quotea e]Constructs the 3 main goals of the tactic
+let semiring_reflect (#a:eqtype) (r:cr a) (vm:vmap a) (e1 e2:polynomial a) (a1 a2:a)
+ (_ : squash (
+ interp_cs r vm (polynomial_simplify r e1) ==
+ interp_cs r vm (polynomial_simplify r e2)))
+ (_ : squash (a1 == interp_p r vm e1))
+ (_ : squash (a2 == interp_p r vm e2)) :
+ squash (a1 == a2)
+ =
+ polynomial_simplify_ok r vm e1;
+ polynomial_simplify_ok r vm e2@@plugin
let canon_semiring_aux
+ (a: Type) (ta: term) (unquotea: term -> Tac a) (quotea: a -> Tac term)
+ (tr tadd topp tmone tmult: term)
+ (munit: a)
+ : Tac unit
+=
+ focus (fun () ->
+ norm []; // Do not normalize anything implicitly
+ let g = cur_goal () in
+ match term_as_formula g with
+ | Comp (Eq (Some t)) t1 t2 ->
+ beginddump ("t1 = " ^ term_to_string t1 ^ "\nt2 = " ^ term_to_string t2);
+ ddump (term_to_string t1);
+ ddump (term_to_string t2);
+ let r : cr a = unquote tr in
+ ddump ("vm = " ^ term_to_string (quote vm) ^ "\n" ^
+ "before = " ^ term_to_string (norm_term steps
+ (quote (interp_p r vm e1 == interp_p r vm e2))));
+ dump ("expected after = " ^ term_to_string (norm_term steps
+ (quote (
+ interp_cs r vm (polynomial_simplify r e1) ==
+ interp_cs r vm (polynomial_simplify r e2)))));
+
+ddump ("te1 = " ^ term_to_string te1); +ddump ("te2 = " ^ term_to_string te2); +ddump "Before canonization"; +ddump "After canonization"; +ddump "Before normalizing left-hand side"; +ddump "After normalizing left-hand side"; +ddump "Before normalizing right-hand side"; +ddump "After normalizing right-hand side";
+ if term_eq t ta then
+ begin
+ match reification unquotea quotea tadd topp tmone tmult munit [t1; t2] with
+ | ([e1; e2], vm) ->
+ let tvm = quote_vm ta quotea vm in
+ let te1 = quote_polynomial ta quotea e1 in
+ let te2 = quote_polynomial ta quotea e2 in
+ mapply (`(semiring_reflect
+ #(`#ta) (`#tr) (`#tvm) (`#te1) (`#te2) (`#t1) (`#t2)));
+ canon_norm ();
+ later ();
+ canon_norm ();
+ trefl ();
+ canon_norm ();
+ trefl ()
+ | _ -> fail "Unexpected"
+ end
+ else fail "Found equality, but terms do not have the expected type"
+ end
+| _ -> fail "Goal should be an equality")let canon_semiring (#a:eqtype) (r:cr a) : Tac unit =
+ canon_semiring_aux a
+ (quote a) (unquote #a) (fun (x:a) -> quote x) (quote r)
+ (norm_term steps (quote r.cm_add.mult))
+ (norm_term steps (quote r.opp))
+ (norm_term steps (quote (r.opp r.cm_mult.unit)))
+ (norm_term steps (quote r.cm_mult.mult))
+ r.cm_add.unit++Ring of integers
+
[@@canon_attr]
+let int_cr : cr int =
+ CR int_plus_cm int_multiply_cm op_Minus (fun x -> ()) (fun x y z -> ()) (fun x -> ())private
+let eq_nat_via_int (a b : nat) (eq : squash (eq2 #int a b)) : Lemma (eq2 #nat a b) = ()let int_semiring () : Tac unit =Check to see if goal is a nat equality, change the equality to int beforehand
match term_as_formula (cur_goal ()) with
+| Comp (Eq (Some t)) _ _ ->
+ if term_eq t (`Prims.nat)
+ then (apply_lemma (`eq_nat_via_int); canon_semiring int_cr)
+ else canon_semiring int_cr
+| _ ->
+ canon_semiring int_cr#set-options "--tactic_trace_d 0 --no_smt"let test (a:int) =
+ let open FStar.Mul in
+ assert (a + - a + 2 * a + - a == -a + 2 * a) by (int_semiring ())fsdoc: no-summary-found
-fsdoc: no-comment-found
+let swap (n:nat) :Type = x:nat{x < n-1}let rec apply_swap_aux (#a:Type) (n:nat) (xs:list a) (s:swap (length xs + n)) :
+ Pure (list a) (requires True)
+ (ensures (fun zs -> length zs == length xs)) (decreases xs) =
+ match xs with
+ | [] | [_] -> xs
+ | x1 :: x2 :: xs' -> if n = (s <: nat)
+ then x2 :: x1 :: xs'
+ else x1 :: apply_swap_aux (n+1) (x2 :: xs') slet apply_swap (#a:Type) = apply_swap_aux #a 0let rec apply_swaps (#a:Type) (xs:list a) (ss:list (swap (length xs))) :
+ Pure (list a) (requires True)
+ (ensures (fun zs -> length zs == length xs)) (decreases ss) =
+ match ss with
+ | [] -> xs
+ | s::ss' -> apply_swaps (apply_swap xs s) ss'let equal_counts (#a:eqtype) (xs ys:list a) : Type0 =
+ (forall (e:a).{:pattern (count e xs) \/ (count e ys)} count e xs == count e ys)let extend_equal_counts (#a:eqtype) (h:a) (xs ys:list a) : Lemma
+ (requires equal_counts xs ys)
+ (ensures equal_counts (h::xs) (h::ys))
+ =
+ ()let retract_equal_counts (#a:eqtype) (h:a) (xs ys:list a) : Lemma
+ (requires equal_counts (h::xs) (h::ys))
+ (ensures equal_counts xs ys)
+ =
+ assert (forall (e:a).{:pattern (count e xs) \/ (count e ys)} count e (h::xs) == count e (h::ys))unfold let swap_for (#a:eqtype) (xs:list a) = swap (length xs)
+unfold let swaps_for (#a:eqtype) (xs:list a) = list (swap_for xs)let rec append_swaps (#a:eqtype) (xs:list a) (ss1 ss2:swaps_for xs) : Lemma
+ (ensures apply_swaps xs (ss1 @ ss2) == apply_swaps (apply_swaps xs ss1) ss2)
+ (decreases ss1)
+ =
+ match ss1 with
+ | [] -> ()
+ | h::t -> append_swaps (apply_swap xs h) t ss2let rec lift_swap_cons (#a:eqtype) (n:nat) (h:a) (xs:list a) (s:swap (length xs + n)) : Lemma
+ (requires n <= s)
+ (ensures apply_swap_aux n (h::xs) (s + 1) == h::(apply_swap_aux n xs s))
+ (decreases xs)
+ =
+ match xs with
+ | [] -> ()
+ | x::xt -> if n < s then lift_swap_cons (n + 1) x xt slet rec lift_swaps_cons (#a:eqtype) (h:a) (xs:list a) (ss:swaps_for xs) : Pure (swaps_for (h::xs))
+ (requires True)
+ (ensures (fun ss' ->
+ apply_swaps (h::xs) ss' == h::(apply_swaps xs ss)
+ ))
+ (decreases ss)
+ =
+ match ss with
+ | [] -> []
+ | s::st ->
+ (
+ lift_swap_cons 0 h xs s;
+ (s + 1)::(lift_swaps_cons h (apply_swap xs s) st)
+ )let rec swap_to_front (#a:eqtype) (h:a) (xs:list a) : Pure (swaps_for xs)
+ (requires count h xs >= 1)
+ (ensures (fun ss ->
+ let ys = apply_swaps xs ss in
+ equal_counts xs ys /\
+ Cons? ys /\
+ hd ys == h
+ ))
+ =
+ match xs with
+ | [] -> []
+ | x::xt ->
+ (
+ if x = h then []
+ else
+ (
+ let ss = swap_to_front h xt in // ss turns xt into h::xt'
+ let ss' = lift_swaps_cons x xt ss in // ss' turns x::xt into x::h::xt'
+ let s:swap_for xs = 0 in
+ append_swaps xs ss' [s];
+ ss' @ [s]
+ )
+ )let rec equal_counts_implies_swaps (#a:eqtype) (xs ys:list a) : Pure (swaps_for xs)
+ (requires equal_counts xs ys)
+ (ensures (fun ss -> ys == apply_swaps xs ss))
+ (decreases ys)
+ =
+ match ys with
+ | [] ->
+ (
+ match xs with
+ | [] -> []
+ | x::xt ->
+ (
+ assert (count x xs >= 1);
+ []
+ )
+ )
+ | y::yt ->
+ (
+ assert (count y ys >= 1);
+ assert (count y xs >= 1);
+ let ss0 = swap_to_front y xs in // find y in xs, swap it to the front
+ let xs' = apply_swaps xs ss0 in // hd xs' == y
+ let xt = tl xs' in // xs' == y::xt
+ retract_equal_counts y xt yt; // prove (equal_counts xt yt)
+ let ss1 = equal_counts_implies_swaps xt yt in // prove (yt == apply_swaps xt ss1)
+ let ss1' = lift_swaps_cons y xt ss1 in // y::yt == apply_swaps (y::xt) ss1'ys == apply_swaps (apply_swaps xs ss0) ss1'
+ append_swaps xs ss0 ss1';
+ ss0 @ ss1'
+)fsdoc: no-summary-found
-fsdoc: no-comment-found
+Only dump when debugging is on
+let dump m = if debugging () then dump m"A Monoid Expression Simplifier" ported from +http://adam.chlipala.net/cpdt/html/Cpdt.Reflection.html
+type exp (a:Type) : Type =
+ | Unit : exp a
+ | Var : a -> exp a
+ | Mult : exp a -> exp a -> exp alet rec exp_to_string (#a:Type) (a_to_string:a->string) (e:exp a) =
+ match e with
+ | Unit -> "Unit"
+ | Var x -> "Var " ^ a_to_string x
+ | Mult e1 e2 -> "Mult (" ^ exp_to_string a_to_string e1
+ ^ ") (" ^ exp_to_string a_to_string e2 ^ ")"let rec mdenote (#a:Type) (m:monoid a) (e:exp a) : a =
+ match e with
+ | Unit -> Monoid?.unit m
+ | Var x -> x
+ | Mult e1 e2 -> Monoid?.mult m (mdenote m e1) (mdenote m e2)let rec mldenote (#a:Type) (m:monoid a) (xs:list a) : a =
+ match xs with
+ | [] -> Monoid?.unit m
+ | [x] -> x
+ | x::xs' -> Monoid?.mult m x (mldenote m xs')let rec flatten (#a:Type) (e:exp a) : list a =
+ match e with
+ | Unit -> []
+ | Var x -> [x]
+ | Mult e1 e2 -> flatten e1 @ flatten e2This proof internally uses the monoid laws; the SMT solver picks up +on them because they are written as squashed formulas in the +definition of monoid; need to be careful with this since these are +quantified formulas without any patterns. Dangerous stuff!
+let rec flatten_correct_aux (#a:Type) (m:monoid a) ml1 ml2 :
+ Lemma (mldenote m (ml1 @ ml2) == Monoid?.mult m (mldenote m ml1)
+ (mldenote m ml2)) =
+ match ml1 with
+ | [] -> ()
+ | e::es1' -> flatten_correct_aux m es1' ml2let rec flatten_correct (#a:Type) (m:monoid a) (e:exp a) :
+ Lemma (mdenote m e == mldenote m (flatten e)) =
+ match e with
+ | Unit | Var _ -> ()
+ | Mult e1 e2 -> flatten_correct_aux m (flatten e1) (flatten e2);
+ flatten_correct m e1; flatten_correct m e2let monoid_reflect (#a:Type) (m:monoid a) (e1 e2:exp a)
+ (_ : squash (mldenote m (flatten e1) == mldenote m (flatten e2)))
+ : squash (mdenote m e1 == mdenote m e2) =
+ flatten_correct m e1; flatten_correct m e2This expects that mult, unit, and me have already been normalized
+let rec reification_aux (#a:Type) (mult unit me : term) : Tac (exp a) =
+ let hd, tl = collect_app_ref me in
+ let tl = list_unref tl in
+ match inspect hd, tl with
+ | Tv_FVar fv, [(me1, Q_Explicit) ; (me2, Q_Explicit)] ->
+ if term_eq (pack (Tv_FVar fv)) mult
+ then Mult (reification_aux mult unit me1) (reification_aux mult unit me2)
+ else Var (unquote me)
+ | _, _ ->
+ if term_eq me unit
+ then Unit
+ else Var (unquote me)let reification (#a:Type) (m:monoid a) (me:term) : Tac (exp a) =
+ let mult = norm_term [delta;zeta;iota] (quote (Monoid?.mult m)) in
+ let unit = norm_term [delta;zeta;iota] (quote (Monoid?.unit m)) in
+ let me = norm_term [delta;zeta;iota] me indump ("mult = " ^ term_to_string mult ^ +"; unit = " ^ term_to_string unit ^ +"; me = " ^ term_to_string me);
+reification_aux mult unit melet canon_monoid (#a:Type) (m:monoid a) : Tac unit =
+ norm [];
+ let g = cur_goal () in
+ match term_as_formula g with
+ | Comp (Eq (Some t)) me1 me2 ->
+ if term_eq t (quote a) then
+ let r1 = reification m me1 in
+ let r2 = reification m me2 in
+ change_sq (quote (mdenote m r1 == mdenote m r2));
+ apply (`monoid_reflect);
+ norm [delta_only ["CanonMonoid.mldenote";
+ "CanonMonoid.flatten";
+ "FStar.List.Tot.Base.op_At";
+ "FStar.List.Tot.Base.append"]]
+ else fail "Goal should be an equality at the right monoid type"
+ | _ -> fail "Goal should be an equality"let lem0 (a b c d : int) =
+ assert_by_tactic (0 + a + b + c + d == (0 + a) + (b + c + 0) + (d + 0))
+ (fun _ -> canon_monoid int_plus_monoid (* string_of_int *); trefl())TODO: would be nice to just find all terms of monoid type in the +goal and replace them with their canonicalization; +basically use flatten_correct instead of monoid_reflect +- even better, the user would have control over the place(s) +where the canonicalization is done
+ diff --git a/docs/FStar.Tactics.Common.html b/docs/FStar.Tactics.Common.html new file mode 100644 index 0000000..464c2bc --- /dev/null +++ b/docs/FStar.Tactics.Common.html @@ -0,0 +1,19 @@ + + + + +This module is realized by FStar.Tactics.Common in the F* sources. +Any change must be reflected there.
+exception NotAListLiteralWe should attempt to not use this one and define more exceptions +above.
+exception TacticFailure of stringfsdoc: no-summary-found
-fsdoc: no-comment-found
-let ((_cur_goal ()):(Tac goal)):match goals () with [] -> fail "no more goals" | (Prims.Cons g _) -> gL
+exception Goal_not_triviallet goals () : Tac (list goal) = goals_of (get ())
+let smt_goals () : Tac (list goal) = smt_goals_of (get ())let fail (#a:Type) (m:string) =
+ raise #a (TacticFailure m)let fail_silently (#a:Type) (m:string) =
+ set_urgency 0;
+ raise #a (TacticFailure m)Return the current goal, not its type. (Ignores SMT goals)
-let ((cur_env ()):(Tac env)):goal_env (_cur_goal ())[cur_env] returns the current goal's environment
-let ((cur_goal ()):(Tac typ)):goal_type (_cur_goal ())[cur_goal] returns the current goal's type
-let ((cur_witness ()):(Tac term)):goal_witness (_cur_goal ())[cur_witness] returns the current goal's witness
-let ((cur_goal_safe ()):(TacH goal ((requires ((fun ps -> ~((==(goals_of ps, (Prims.Nil )))))))) ((ensures ((fun ps0 r -> exists g.{:pattern } ==(r, (Success g ps0)))))))):match goals_of (get ()) with (Prims.Cons g _) -> g[cur_goal_safe] will always return the current goal, without failing. It must be statically verified that there indeed is a goal in order to call it.
-let ((cur_binders ()):(Tac binders)):binders_of_env (cur_env ())[cur_binders] returns the list of binders in the current goal.
-let ((with_policy pol (f:Unidentified product: [unit] (Tac 'a))):(Tac 'a)):let old_pol = get_guard_policy () in set_guard_policy pol; let r = f () in set_guard_policy old_pol; rlet _cur_goal () : Tac goal =
+ match goals () with
+ | [] -> fail "no more goals"
+ | g::_ -> gcur_env returns the current goal's environment
let cur_env () : Tac env = goal_env (_cur_goal ())cur_goal returns the current goal's type
let cur_goal () : Tac typ = goal_type (_cur_goal ())cur_witness returns the current goal's witness
let cur_witness () : Tac term = goal_witness (_cur_goal ())cur_goal_safe will always return the current goal, without failing.
+It must be statically verified that there indeed is a goal in order to
+call it.
let cur_goal_safe () : TacH goal (requires (fun ps -> ~(goals_of ps == [])))
+ (ensures (fun ps0 r -> exists g. r == Success g ps0))
+ = match goals_of (get ()) with
+ | g :: _ -> gcur_binders returns the list of binders in the current goal.
let cur_binders () : Tac binders =
+ binders_of_env (cur_env ())Set the guard policy only locally, without affecting calling code
-let ((dismiss ()):(Tac unit)):match goals () with [] -> fail "dismiss: no more goals" | (Prims.Cons _ gs) -> set_goals gsIgnore the current goal. If left unproven, this will fail after the tactic finishes.
-let ((flip ()):(Tac unit)):let gs = goals () in match goals () with []|
- [_] -> fail "flip: less than two goals" | (Prims.Cons g1 (Prims.Cons g2 gs)) -> set_goals ((Prims.Cons g2 (Prims.Cons g1 gs)))let with_policy pol (f : unit -> Tac 'a) : Tac 'a =
+ let old_pol = get_guard_policy () in
+ set_guard_policy pol;
+ let r = f () in
+ set_guard_policy old_pol;
+ rexact e will solve a goal Gamma |- w : t if e has type exactly
+t in Gamma.
let exact (t : term) : Tac unit =
+ with_policy SMT (fun () -> t_exact true false t)exact_with_ref e will solve a goal Gamma |- w : t if e has
+type t' where t' is a subtype of t in Gamma. This is a more
+flexible variant of exact.
let exact_with_ref (t : term) : Tac unit =
+ with_policy SMT (fun () -> t_exact true true t)let trivial () : Tac unit =
+ norm [iota; zeta; reify_; delta; primops; simplify; unmeta];
+ let g = cur_goal () in
+ match term_as_formula g with
+ | True_ -> exact (`())
+ | _ -> raise Goal_not_trivialAnother hook to just run a tactic without goals, just by reusing with_tactic
let run_tactic (t:unit -> Tac unit)
+ : Pure unit
+ (requires (set_range_of (with_tactic (fun () -> trivial (); t ()) (squash True)) (range_of t)))
+ (ensures (fun _ -> True))
+ = ()Ignore the current goal. If left unproven, this will fail after +the tactic finishes.
+let dismiss () : Tac unit =
+ match goals () with
+ | [] -> fail "dismiss: no more goals"
+ | _::gs -> set_goals gsFlip the order of the first two goals.
-let ((qed ()):(Tac unit)):match goals () with [] -> () | _ -> fail "qed: not done!"let flip () : Tac unit =
+ let gs = goals () in
+ match goals () with
+ | [] | [_] -> fail "flip: less than two goals"
+ | g1::g2::gs -> set_goals (g2::g1::gs)Succeed if there are no more goals left, and fail otherwise.
-let ((debug (m:string)):(Tac unit)):if debugging () then print m else ()[debug str] is similar to [print str], but will only print the message if the [--debug] option was given for the current module AND [--debug_level Tac] is on.
-let ((smt ()):(Tac unit)):match (FStar.Pervasives.Native.Mktuple2 goals () smt_goals ()) with ([], _) -> fail "smt: no active goals" | ((Prims.Cons g gs), gs') -> set_goals gs; set_smt_goals ((Prims.Cons g gs'))[smt] will mark the current goal for being solved through the SMT. This does not immediately run the SMT: it just dumps the goal in the SMT bin. Note, if you dump a proof-relevant goal there, the engine will later raise an error.
-let ((later ()):(Tac unit)):match goals () with (Prims.Cons g gs) -> set_goals (@(gs, (Prims.Cons g (Prims.Nil )))) | _ -> fail "later: no goals"let qed () : Tac unit =
+ match goals () with
+ | [] -> ()
+ | _ -> fail "qed: not done!"debug str is similar to print str, but will only print the message
+if the --debug option was given for the current module AND
+--debug_level Tac is on.
let debug (m:string) : Tac unit =
+ if debugging () then print msmt will mark the current goal for being solved through the SMT.
+This does not immediately run the SMT: it just dumps the goal in the
+SMT bin. Note, if you dump a proof-relevant goal there, the engine will
+later raise an error.
let smt () : Tac unit =
+ match goals (), smt_goals () with
+ | [], _ -> fail "smt: no active goals"
+ | g::gs, gs' ->
+ begin
+ set_goals gs;
+ set_smt_goals (g :: gs')
+ endlet idtac () : Tac unit = ()Push the current goal to the back.
-let ((exact (t:term)):(Tac unit)):with_policy SMT ((fun () -> t_exact true false t))[exact e] will solve a goal [Gamma |- w : t] if [e] has type exactly [t] in [Gamma].
-let ((exact_with_ref (t:term)):(Tac unit)):with_policy SMT ((fun () -> t_exact true true t))[exact_with_ref e] will solve a goal [Gamma |- w : t] if [e] has type [t'] where [t'] is a subtype of [t] in [Gamma]. This is a more flexible variant of [exact].
-let ((apply (t:term)):(Tac unit)):t_apply true false t[apply f] will attempt to produce a solution to the goal by an application of [f] to any amount of arguments (which need to be solved as further goals). The amount of arguments introduced is the least such that [f a_i] unifies with the goal's type.
-let ((apply_raw (t:term)):(Tac unit)):t_apply false false t[apply_raw f] is like [apply], but will ask for all arguments regardless of whether they appear free in further goals. See the explanation in [t_apply].
-let ((exact_guard (t:term)):(Tac unit)):with_policy Goal ((fun () -> t_exact true false t))Like [exact], but allows for the term [e] to have a type [t] only under some guard [g], adding the guard as a goal.
-let ((divide (n:int) (l:Unidentified product: [unit] (Tac 'a)) (r:Unidentified product: [unit] (Tac 'b))):(Tac (*('a, 'b)))):if <(n, 0) then fail "divide: negative n" else (); let (gs, sgs) = (FStar.Pervasives.Native.Mktuple2 goals () smt_goals ()) in let (gs1, gs2) = List.Tot.splitAt n gs in set_goals gs1; set_smt_goals (Prims.Nil ); let x = l () in let (gsl, sgsl) = (FStar.Pervasives.Native.Mktuple2 goals () smt_goals ()) in set_goals gs2; set_smt_goals (Prims.Nil ); let y = r () in let (gsr, sgsr) = (FStar.Pervasives.Native.Mktuple2 goals () smt_goals ()) in set_goals (@(gsl, gsr)); set_smt_goals (@(sgs, @(sgsl, sgsr))); ((FStar.Pervasives.Native.Mktuple2 x y))[divide n t1 t2] will split the current set of goals into the [n] first ones, and the rest. It then runs [t1] on the first set, and [t2] on the second, returning both results (and concatenating remaining goals).
-let ((focus (t:Unidentified product: [unit] (Tac 'a))):(Tac 'a)):match goals () with [] -> fail "focus: no goals" | (Prims.Cons g gs) -> let sgs = smt_goals () in set_goals (Prims.Cons g (Prims.Nil )); set_smt_goals (Prims.Nil ); let x = t () in set_goals (@(goals (), gs)); set_smt_goals (@(smt_goals (), sgs)); x[focus t] runs [t ()] on the current active goal, hiding all others and restoring them at the end.
-let (dump1 (m:string)):focus ((fun () -> dump m))Similar to [dump], but only dumping the current goal.
-let ((seq (f:Unidentified product: [unit] (Tac unit)) (g:Unidentified product: [unit] (Tac unit))):(Tac unit)):focus ((fun () -> f (); iterAll g))Runs tactic [t1] on the current goal, and then tactic [t2] on each subgoal produced by [t1]. Each invocation of [t2] runs on a proofstate with a single goal (they're "focused").
-let ((ngoals ()):(Tac int)):List.length (goals ())[ngoals ()] returns the number of goals
-let ((ngoals_smt ()):(Tac int)):List.length (smt_goals ())[ngoals_smt ()] returns the number of SMT goals
-let (join_all_smt_goals ()):let (gs, sgs) = (FStar.Pervasives.Native.Mktuple2 goals () smt_goals ()) in set_smt_goals (Prims.Nil ); set_goals sgs; repeat' join; let sgs' = goals () in set_goals gs; set_smt_goals sgs'Join all of the SMT goals into one. This helps when all of them are expected to be similar, and therefore easier to prove at once by the SMT solver. TODO: would be nice to try to join them in a more meaningful way, as the order can matter.
-let ((is_guard ()):(Tac bool)):Tactics.Types.is_guard (_cur_goal ())[is_guard] returns whether the current goal arised from a typechecking guard
-let ((rewrite' (b:binder)):(Tac unit)):(<|>(<|>(((fun () -> rewrite b)), ((fun () -> binder_retype b; apply_lemma ((`(__eq_sym))); rewrite b))), ((fun () -> fail "rewrite' failed")))) ()Like [rewrite], but works with equalities [v == e] and [e == v]
-let ((l_to_r (lems:list term)):(Tac unit)):let ((first_or_trefl ()):(Tac unit)) = fold_left ((fun k l () -> or_else ((fun () -> apply_lemma l)) k)) trefl lems () in pointwise first_or_treflRewrites left-to-right, and bottom-up, given a set of lemmas stating equalities. The lemmas need to prove propositional equalities, that is, using [==].
-let ((grewrite_eq (b:binder)):(Tac unit)):match term_as_formula (type_of_binder b) with (Comp (Eq _) l r) -> grewrite l r; iseq (Prims.Cons idtac (Prims.Cons ((fun () -> exact (binder_to_term b))) (Prims.Nil ))) | _ -> fail "failed in grewrite_eq"A wrapper to [grewrite] which takes a binder of an equality type
-let ((branch_on_match ()):(Tac unit)):focus ((fun () -> let x = get_match_body () in let _ = t_destruct x in iterAll ((fun () -> let bs = repeat intro in let b = last bs in grewrite_eq b; norm (Prims.Cons iota (Prims.Nil ))))))When the goal is [match e with | p1 -> e1 ... | pn -> en], destruct it into [n] goals for each possible case, including an hypothesis for [e] matching the correposnding pattern.
+let later () : Tac unit =
+ match goals () with
+ | g::gs -> set_goals (gs @ [g])
+ | _ -> fail "later: no goals"apply f will attempt to produce a solution to the goal by an application
+of f to any amount of arguments (which need to be solved as further goals).
+The amount of arguments introduced is the least such that f a_i unifies
+with the goal's type.
let apply (t : term) : Tac unit =
+ t_apply true false tlet apply_noinst (t : term) : Tac unit =
+ t_apply true true tapply_lemma l will solve a goal of type squash phi when l is a
+Lemma ensuring phi. The arguments to l and its requires clause are
+introduced as new goals. As a small optimization, unit arguments are
+discharged by the engine. Just a thin wrapper around t_apply_lemma.
let apply_lemma (t : term) : Tac unit =
+ t_apply_lemma false false tSee docs for t_trefl
let trefl () : Tac unit =
+ t_trefl falseSee docs for t_trefl
let trefl_guard () : Tac unit =
+ t_trefl trueSee docs for t_commute_applied_match
let commute_applied_match () : Tac unit =
+ t_commute_applied_match ()Similar to apply_lemma, but will not instantiate uvars in the
+goal while applying.
let apply_lemma_noinst (t : term) : Tac unit =
+ t_apply_lemma true false tlet apply_lemma_rw (t : term) : Tac unit =
+ t_apply_lemma false true tapply_raw f is like apply, but will ask for all arguments
+regardless of whether they appear free in further goals. See the
+explanation in t_apply.
let apply_raw (t : term) : Tac unit =
+ t_apply false false tLike exact, but allows for the term e to have a type t only
+under some guard g, adding the guard as a goal.
let exact_guard (t : term) : Tac unit =
+ with_policy Goal (fun () -> t_exact true false t)(TODO: explain bettter) When running pointwise tau For every
+subterm t' of the goal's type t, the engine will build a goal Gamma |= t' == ?u and run tau on it. When the tactic proves the goal,
+the engine will rewrite t' for ?u in the original goal type. This
+is done for every subterm, bottom-up. This allows to recurse over an
+unknown goal type. By inspecting the goal, the tau can then decide
+what to do (to not do anything, use trefl).
let t_pointwise (d:direction) (tau : unit -> Tac unit) : Tac unit =
+ let ctrl (t:term) : Tac (bool & ctrl_flag) =
+ true, Continue
+ in
+ let rw () : Tac unit =
+ tau ()
+ in
+ ctrl_rewrite d ctrl rwtopdown_rewrite ctrl rw is used to rewrite those sub-terms t
+of the goal on which fst (ctrl t) returns true.
let topdown_rewrite (ctrl : term -> Tac (bool * int))
+ (rw:unit -> Tac unit) : Tac unit
+ = let ctrl' (t:term) : Tac (bool & ctrl_flag) =
+ let b, i = ctrl t in
+ let f =
+ match i with
+ | 0 -> Continue
+ | 1 -> Skip
+ | 2 -> Abort
+ | _ -> fail "topdown_rewrite: bad value from ctrl"
+ in
+ b, f
+ in
+ ctrl_rewrite TopDown ctrl' rwOn each such sub-term, rw is presented with an equality of goal
+of the form Gamma |= t == ?u. When rw proves the goal,
+the engine will rewrite t for ?u in the original goal
+type.
The goal formula is traversed top-down and the traversal can be
+controlled by snd (ctrl t):
When snd (ctrl t) = 0, the traversal continues down through the
+position in the goal term.
When snd (ctrl t) = 1, the traversal continues to the next
+sub-tree of the goal.
When snd (ctrl t) = 2, no more rewrites are performed in the
+goal.
let pointwise (tau : unit -> Tac unit) : Tac unit = t_pointwise BottomUp tau
+let pointwise' (tau : unit -> Tac unit) : Tac unit = t_pointwise TopDown taulet cur_module () : Tac name =
+ moduleof (top_env ())let open_modules () : Tac (list name) =
+ env_open_modules (top_env ())let rec repeatn (#a:Type) (n : int) (t : unit -> Tac a) : Tac (list a) =
+ if n <= 0
+ then []
+ else t () :: repeatn (n - 1) tlet fresh_uvar (o : option typ) : Tac term =
+ let e = cur_env () in
+ uvar_env e olet unify (t1 t2 : term) : Tac bool =
+ let e = cur_env () in
+ unify_env e t1 t2let unify_guard (t1 t2 : term) : Tac bool =
+ let e = cur_env () in
+ unify_guard_env e t1 t2let tmatch (t1 t2 : term) : Tac bool =
+ let e = cur_env () in
+ match_env e t1 t2divide n t1 t2 will split the current set of goals into the n
+first ones, and the rest. It then runs t1 on the first set, and t2
+on the second, returning both results (and concatenating remaining goals).
let divide (n:int) (l : unit -> Tac 'a) (r : unit -> Tac 'b) : Tac ('a * 'b) =
+ if n < 0 then
+ fail "divide: negative n";
+ let gs, sgs = goals (), smt_goals () in
+ let gs1, gs2 = List.Tot.Base.splitAt n gs inset_goals gs1; set_smt_goals [];
+let x = l () in
+let gsl, sgsl = goals (), smt_goals () inset_goals gs2; set_smt_goals [];
+let y = r () in
+let gsr, sgsr = goals (), smt_goals () inset_goals (gsl @ gsr); set_smt_goals (sgs @ sgsl @ sgsr);
+(x, y)let rec iseq (ts : list (unit -> Tac unit)) : Tac unit =
+ match ts with
+ | t::ts -> let _ = divide 1 t (fun () -> iseq ts) in ()
+ | [] -> ()focus t runs t () on the current active goal, hiding all others
+and restoring them at the end.
let focus (t : unit -> Tac 'a) : Tac 'a =
+ match goals () with
+ | [] -> fail "focus: no goals"
+ | g::gs ->
+ let sgs = smt_goals () in
+ set_goals [g]; set_smt_goals [];
+ let x = t () in
+ set_goals (goals () @ gs); set_smt_goals (smt_goals () @ sgs);
+ xSimilar to dump, but only dumping the current goal.
let dump1 (m : string) = focus (fun () -> dump m)let rec mapAll (t : unit -> Tac 'a) : Tac (list 'a) =
+ match goals () with
+ | [] -> []
+ | _::_ -> let (h, t) = divide 1 t (fun () -> mapAll t) in h::tlet rec iterAll (t : unit -> Tac unit) : Tac unit =Could use mapAll, but why even build that list
+match goals () with
+| [] -> ()
+| _::_ -> let _ = divide 1 t (fun () -> iterAll t) in ()let iterAllSMT (t : unit -> Tac unit) : Tac unit =
+ let gs, sgs = goals (), smt_goals () in
+ set_goals sgs;
+ set_smt_goals [];
+ iterAll t;
+ let gs', sgs' = goals (), smt_goals () in
+ set_goals gs;
+ set_smt_goals (gs'@sgs')Runs tactic t1 on the current goal, and then tactic t2 on each
+subgoal produced by t1. Each invocation of t2 runs on a proofstate
+with a single goal (they're "focused").
let seq (f : unit -> Tac unit) (g : unit -> Tac unit) : Tac unit =
+ focus (fun () -> f (); iterAll g)let exact_args (qs : list aqualv) (t : term) : Tac unit =
+ focus (fun () ->
+ let n = List.Tot.Base.length qs in
+ let uvs = repeatn n (fun () -> fresh_uvar None) in
+ let t' = mk_app t (zip uvs qs) in
+ exact t';
+ iter (fun uv -> if is_uvar uv
+ then unshelve uv
+ else ()) (L.rev uvs)
+ )let exact_n (n : int) (t : term) : Tac unit =
+ exact_args (repeatn n (fun () -> Q_Explicit)) tngoals () returns the number of goals
let ngoals () : Tac int = List.Tot.Base.length (goals ())ngoals_smt () returns the number of SMT goals
let ngoals_smt () : Tac int = List.Tot.Base.length (smt_goals ())let fresh_bv t : Tac bv =These bvs are fresh anyway through a separate counter, +* but adding the integer allows for more readability when +* generating code
+let i = fresh () in
+fresh_bv_named ("x" ^ string_of_int i) tlet fresh_binder_named nm t : Tac binder =
+ mk_binder (fresh_bv_named nm t)let fresh_binder t : Tac binder =See comment in fresh_bv
+let i = fresh () in
+fresh_binder_named ("x" ^ string_of_int i) tlet fresh_implicit_binder_named nm t : Tac binder =
+ mk_implicit_binder (fresh_bv_named nm t)let fresh_implicit_binder t : Tac binder =See comment in fresh_bv
+let i = fresh () in
+fresh_implicit_binder_named ("x" ^ string_of_int i) tlet guard (b : bool) : TacH unit (requires (fun _ -> True))
+ (ensures (fun ps r -> if b
+ then Success? r /\ Success?.ps r == ps
+ else Failed? r))^ the proofstate on failure is not exactly equal (has the psc set)
+=
+if not b then
+ fail "guard failed"
+else ()let try_with (f : unit -> Tac 'a) (h : exn -> Tac 'a) : Tac 'a =
+ match catch f with
+ | Inl e -> h e
+ | Inr x -> xlet trytac (t : unit -> Tac 'a) : Tac (option 'a) =
+ try Some (t ())
+ with
+ | _ -> Nonelet or_else (#a:Type) (t1 : unit -> Tac a) (t2 : unit -> Tac a) : Tac a =
+ try t1 ()
+ with | _ -> t2 ()val (<|>) : (unit -> Tac 'a) ->
+ (unit -> Tac 'a) ->
+ (unit -> Tac 'a)
+let (<|>) t1 t2 = fun () -> or_else t1 t2let first (ts : list (unit -> Tac 'a)) : Tac 'a =
+ L.fold_right (<|>) ts (fun () -> fail "no tactics to try") ()let rec repeat (#a:Type) (t : unit -> Tac a) : Tac (list a) =
+ match catch t with
+ | Inl _ -> []
+ | Inr x -> x :: repeat tlet repeat1 (#a:Type) (t : unit -> Tac a) : Tac (list a) =
+ t () :: repeat tlet repeat' (f : unit -> Tac 'a) : Tac unit =
+ let _ = repeat f in ()let norm_term (s : list norm_step) (t : term) : Tac term =
+ let e =
+ try cur_env ()
+ with | _ -> top_env ()
+ in
+ norm_term_env e s tJoin all of the SMT goals into one. This helps when all of them are +expected to be similar, and therefore easier to prove at once by the SMT +solver. TODO: would be nice to try to join them in a more meaningful +way, as the order can matter.
+let join_all_smt_goals () =
+ let gs, sgs = goals (), smt_goals () in
+ set_smt_goals [];
+ set_goals sgs;
+ repeat' join;
+ let sgs' = goals () in // should be a single one
+ set_goals gs;
+ set_smt_goals sgs'let discard (tau : unit -> Tac 'a) : unit -> Tac unit =
+ fun () -> let _ = tau () in ()TODO: do we want some value out of this?
+let rec repeatseq (#a:Type) (t : unit -> Tac a) : Tac unit =
+ let _ = trytac (fun () -> (discard t) `seq` (discard (fun () -> repeatseq t))) in ()let tadmit () = tadmit_t (`())let admit1 () : Tac unit =
+ tadmit ()let admit_all () : Tac unit =
+ let _ = repeat tadmit in
+ ()is_guard returns whether the current goal arised from a typechecking guard
let is_guard () : Tac bool =
+ Tactics.Types.is_guard (_cur_goal ())let skip_guard () : Tac unit =
+ if is_guard ()
+ then smt ()
+ else fail ""let guards_to_smt () : Tac unit =
+ let _ = repeat skip_guard in
+ ()let simpl () : Tac unit = norm [simplify; primops]
+let whnf () : Tac unit = norm [weak; hnf; primops; delta]
+let compute () : Tac unit = norm [primops; iota; delta; zeta]let intros () : Tac (list binder) = repeat introlet intros' () : Tac unit = let _ = intros () in ()
+let destruct tm : Tac unit = let _ = t_destruct tm in ()
+let destruct_intros tm : Tac unit = seq (fun () -> let _ = t_destruct tm in ()) intros'private val __cut : (a:Type) -> (b:Type) -> (a -> b) -> a -> b
+private let __cut a b f x = f xlet tcut (t:term) : Tac binder =
+ let g = cur_goal () in
+ let tt = mk_e_app (`__cut) [t; g] in
+ apply tt;
+ intro ()let pose (t:term) : Tac binder =
+ apply (`__cut);
+ flip ();
+ exact t;
+ intro ()let intro_as (s:string) : Tac binder =
+ let b = intro () in
+ rename_to b slet pose_as (s:string) (t:term) : Tac binder =
+ let b = pose t in
+ rename_to b slet for_each_binder (f : binder -> Tac 'a) : Tac (list 'a) =
+ map f (cur_binders ())let rec revert_all (bs:binders) : Tac unit =
+ match bs with
+ | [] -> ()
+ | _::tl -> revert ();
+ revert_all tlSome syntax utility functions
+let bv_to_term (bv : bv) : Tac term = pack (Tv_Var bv)
+let binder_to_term (b : binder) : Tac term = let bv, _ = inspect_binder b in bv_to_term bvCannot define this inside assumption due to #1091
private
+let rec __assumption_aux (bs : binders) : Tac unit =
+ match bs with
+ | [] ->
+ fail "no assumption matches goal"
+ | b::bs ->
+ let t = binder_to_term b in
+ try exact t with | _ ->
+ try (apply (`FStar.Squash.return_squash);
+ exact t) with | _ ->
+ __assumption_aux bslet assumption () : Tac unit =
+ __assumption_aux (cur_binders ())let destruct_equality_implication (t:term) : Tac (option (formula * term)) =
+ match term_as_formula t with
+ | Implies lhs rhs ->
+ let lhs = term_as_formula' lhs in
+ begin match lhs with
+ | Comp (Eq _) _ _ -> Some (lhs, rhs)
+ | _ -> None
+ end
+ | _ -> Noneprivate
+let __eq_sym #t (a b : t) : Lemma ((a == b) == (b == a)) =
+ FStar.PropositionalExtensionality.apply (a==b) (b==a)Like rewrite, but works with equalities v == e and e == v
let rewrite' (b:binder) : Tac unit =
+ ((fun () -> rewrite b)
+ <|> (fun () -> binder_retype b;
+ apply_lemma (`__eq_sym);
+ rewrite b)
+ <|> (fun () -> fail "rewrite' failed"))
+ ()let rec try_rewrite_equality (x:term) (bs:binders) : Tac unit =
+ match bs with
+ | [] -> ()
+ | x_t::bs ->
+ begin match term_as_formula (type_of_binder x_t) with
+ | Comp (Eq _) y _ ->
+ if term_eq x y
+ then rewrite x_t
+ else try_rewrite_equality x bs
+ | _ ->
+ try_rewrite_equality x bs
+ endlet rec rewrite_all_context_equalities (bs:binders) : Tac unit =
+ match bs with
+ | [] -> ()
+ | x_t::bs -> begin
+ (try rewrite x_t with | _ -> ());
+ rewrite_all_context_equalities bs
+ endlet rewrite_eqs_from_context () : Tac unit =
+ rewrite_all_context_equalities (cur_binders ())let rewrite_equality (t:term) : Tac unit =
+ try_rewrite_equality t (cur_binders ())let unfold_def (t:term) : Tac unit =
+ match inspect t with
+ | Tv_FVar fv ->
+ let n = implode_qn (inspect_fv fv) in
+ norm [delta_fully [n]]
+ | _ -> fail "unfold_def: term is not a fv"Rewrites left-to-right, and bottom-up, given a set of lemmas stating
+equalities. The lemmas need to prove propositional equalities, that
+is, using ==.
let l_to_r (lems:list term) : Tac unit =
+ let first_or_trefl () : Tac unit =
+ fold_left (fun k l () ->
+ (fun () -> apply_lemma_rw l)
+ `or_else` k)
+ trefl lems () in
+ pointwise first_or_trefllet mk_squash (t : term) : term =
+ let sq : term = pack_ln (Tv_FVar (pack_fv squash_qn)) in
+ mk_e_app sq [t]let mk_sq_eq (t1 t2 : term) : term =
+ let eq : term = pack_ln (Tv_FVar (pack_fv eq2_qn)) in
+ mk_squash (mk_e_app eq [t1; t2])let grewrite (t1 t2 : term) : Tac unit =
+ let e = tcut (mk_sq_eq t1 t2) in
+ let e = pack_ln (Tv_Var (bv_of_binder e)) in
+ pointwise (fun () -> try exact e with | _ -> trefl ())private
+let __un_sq_eq (#a:Type) (x y : a) (_ : (x == y)) : Lemma (x == y) = ()A wrapper to grewrite which takes a binder of an equality type
let grewrite_eq (b:binder) : Tac unit =
+ match term_as_formula (type_of_binder b) with
+ | Comp (Eq _) l r ->
+ grewrite l r;
+ iseq [idtac; (fun () -> exact (binder_to_term b))]
+ | _ ->
+ begin match term_as_formula' (type_of_binder b) with
+ | Comp (Eq _) l r ->
+ grewrite l r;
+ iseq [idtac; (fun () -> apply_lemma (`__un_sq_eq);
+ exact (binder_to_term b))]
+ | _ ->
+ fail "grewrite_eq: binder type is not an equality"
+ endprivate val push1 : (#p:Type) -> (#q:Type) ->
+ squash (p ==> q) ->
+ squash p ->
+ squash q
+private let push1 #p #q f u = ()private val push1' : (#p:Type) -> (#q:Type) ->
+ (p ==> q) ->
+ squash p ->
+ squash q
+private let push1' #p #q f u = ()Before anything, try a vanilla apply and apply_lemma
+val apply_squash_or_lem : d:nat -> term -> Tac unit
+let rec apply_squash_or_lem d t =try apply t with | _ ->
+try apply (`FStar.Squash.return_squash); apply t with | _ ->
+try apply_lemma t with | _ ->Fuel cutoff, just in case.
+if d <= 0 then fail "mapply: out of fuel" else beginlet ty = tc (cur_env ()) t in
+let tys, c = collect_arr ty in
+match inspect_comp c with
+| C_Lemma pre post _ ->
+ begin
+ let post = `((`#post) ()) in (* unthunk *)
+ let post = norm_term [] post inIs the lemma an implication? We can try to intro
+match term_as_formula' post with
+| Implies p q ->
+ apply_lemma (`push1);
+ apply_squash_or_lem (d-1) t | _ ->
+ fail "mapply: can't apply (1)"
+ end
+| C_Total rt _ ->
+ begin match unsquash rt withIf the function returns a squash, just apply it, since our goals are squashed
+| Some rt ->DUPLICATED, refactor! +Is the lemma an implication? We can try to intro
+begin
+let rt = norm_term [] rt inmatch term_as_formula' rt with
+| Implies p q ->
+ apply_lemma (`push1);
+ apply_squash_or_lem (d-1) t| _ ->
+ fail "mapply: can't apply (1)"
+endIf not, we can try to introduce the squash ourselves first
+| None ->DUPLICATED, refactor! +Is the lemma an implication? We can try to intro
+begin
+let rt = norm_term [] rt inmatch term_as_formula' rt with
+| Implies p q ->
+ apply_lemma (`push1);
+ apply_squash_or_lem (d-1) t | _ ->
+ apply (`FStar.Squash.return_squash);
+ apply t
+ end
+ end
+| _ -> fail "mapply: can't apply (2)"
+endm is for magic
let mapply (t : term) : Tac unit =
+ apply_squash_or_lem 10 tprivate
+let admit_dump_t () : Tac unit =
+ dump "Admitting";
+ apply (`admit)val admit_dump : #a:Type -> (#[admit_dump_t ()] x : (unit -> Admit a)) -> unit -> Admit a
+let admit_dump #a #x () = x ()private
+let magic_dump_t () : Tac unit =
+ dump "Admitting";
+ apply (`magic);
+ exact (`());
+ ()val magic_dump : #a:Type -> (#[magic_dump_t ()] x : a) -> unit -> Tot a
+let magic_dump #a #x () = xlet change_with t1 t2 : Tac unit =
+ focus (fun () ->
+ grewrite t1 t2;
+ iseq [idtac; trivial]
+ )let change_sq (t1 : term) : Tac unit =
+ change (mk_e_app (`squash) [t1])let finish_by (t : unit -> Tac 'a) : Tac 'a =
+ let x = t () in
+ or_else qed (fun () -> fail "finish_by: not finished");
+ xlet solve_then #a #b (t1 : unit -> Tac a) (t2 : a -> Tac b) : Tac b =
+ dup ();
+ let x = focus (fun () -> finish_by t1) in
+ let y = t2 x in
+ trefl ();
+ ylet add_elem (t : unit -> Tac 'a) : Tac 'a = focus (fun () ->
+ apply (`Cons);
+ focus (fun () ->
+ let x = t () in
+ qed ();
+ x
+ )
+ )Specialize a function by partially evaluating it
+For example:
+let rec foo (l:list int) (x:int) :St int = +match l with +| `` -> x +| hd::tl -> x + foo tl x
+let f :int -> St int = synth_by_tactic (specialize (foo 1; 2) %foo`)
would make the definition of f as x + x + x
+f is the term that needs to be specialized
+l is the list of names to be delta-ed
+let specialize (#a:Type) (f:a) (l:list string) :unit -> Tac unit
+ = fun () -> solve_then (fun () -> exact (quote f)) (fun () -> norm [delta_only l; iota; zeta])let tlabel (l:string) =
+ match goals () with
+ | [] -> fail "tlabel: no goals"
+ | h::t ->
+ set_goals (set_label l h :: t)let tlabel' (l:string) =
+ match goals () with
+ | [] -> fail "tlabel': no goals"
+ | h::t ->
+ let h = set_label (l ^ get_label h) h in
+ set_goals (h :: t)let focus_all () : Tac unit =
+ set_goals (goals () @ smt_goals ());
+ set_smt_goals []private
+let rec extract_nth (n:nat) (l : list 'a) : option ('a * list 'a) =
+ match n, l with
+ | _, [] -> None
+ | 0, hd::tl -> Some (hd, tl)
+ | _, hd::tl -> begin
+ match extract_nth (n-1) tl with
+ | Some (hd', tl') -> Some (hd', hd::tl')
+ | None -> None
+ endlet bump_nth (n:pos) : Tac unit =n-1 since goal numbering begins at 1
+match extract_nth (n - 1) (goals ()) with
+| None -> fail "bump_nth: not that many goals"
+| Some (h, t) -> set_goals (h :: t)let on_sort_bv (f : term -> Tac term) (xbv:bv) : Tac bv =
+ let bvv = inspect_bv xbv in
+ let bvv = { bvv with bv_sort = f bvv.bv_sort } in
+ let bv = pack_bv bvv in
+ bvlet on_sort_binder (f : term -> Tac term) (b:binder) : Tac binder =
+ let bv, (q, attrs) = inspect_binder b in
+ let bv = on_sort_bv f bv in
+ let b = pack_binder bv q attrs in
+ blet rec visit_tm (ff : term -> Tac term) (t : term) : Tac term =
+ let tv = inspect_ln t in
+ let tv' =
+ match tv with
+ | Tv_FVar _ -> tv
+ | Tv_Var bv ->
+ let bv = on_sort_bv (visit_tm ff) bv in
+ Tv_Var bv| Tv_BVar bv ->
+ let bv = on_sort_bv (visit_tm ff) bv in
+ Tv_BVar bv | Tv_Type () -> Tv_Type ()
+ | Tv_Const c -> Tv_Const c
+ | Tv_Uvar i u -> Tv_Uvar i u
+ | Tv_Unknown -> Tv_Unknown
+ | Tv_Arrow b c ->
+ let b = on_sort_binder (visit_tm ff) b in
+ let c = visit_comp ff c in
+ Tv_Arrow b c
+ | Tv_Abs b t ->
+ let b = on_sort_binder (visit_tm ff) b in
+ let t = visit_tm ff t in
+ Tv_Abs b t
+ | Tv_App l (r, q) ->
+ let l = visit_tm ff l in
+ let r = visit_tm ff r in
+ Tv_App l (r, q)
+ | Tv_Refine b r ->
+ let b = on_sort_bv (visit_tm ff) b in
+ let r = visit_tm ff r in
+ Tv_Refine b r
+ | Tv_Let r attrs b def t ->
+ let b = on_sort_bv (visit_tm ff) b in
+ let def = visit_tm ff def in
+ let t = visit_tm ff t in
+ Tv_Let r attrs b def t
+ | Tv_Match sc ret_opt brs ->
+ let sc = visit_tm ff sc in
+ let ret_opt = map_opt (fun ret ->
+ match ret with
+ | Inl t, tacopt -> Inl (visit_tm ff t), map_opt (visit_tm ff) tacopt
+ | Inr c, tacopt -> Inr (visit_comp ff c), map_opt (visit_tm ff) tacopt) ret_opt in
+ let brs = map (visit_br ff) brs in
+ Tv_Match sc ret_opt brs
+ | Tv_AscribedT e t topt ->
+ let e = visit_tm ff e in
+ let t = visit_tm ff t in
+ Tv_AscribedT e t topt
+ | Tv_AscribedC e c topt ->
+ let e = visit_tm ff e in
+ Tv_AscribedC e c topt
+ in
+ ff (pack_ln tv')
+and visit_br (ff : term -> Tac term) (b:branch) : Tac branch =
+ let (p, t) = b in
+ let p = visit_pat ff p in
+ let t = visit_tm ff t in
+ (p, t)
+and visit_pat (ff : term -> Tac term) (p:pattern) : Tac pattern =
+ match p with
+ | Pat_Constant c -> p
+ | Pat_Cons fv l ->
+ let l = (map (fun(p,b) -> (visit_pat ff p, b)) l) in
+ Pat_Cons fv l
+ | Pat_Var bv ->
+ let bv = on_sort_bv (visit_tm ff) bv in
+ Pat_Var bv
+ | Pat_Wild bv ->
+ let bv = on_sort_bv (visit_tm ff) bv in
+ Pat_Wild bv
+ | Pat_Dot_Term bv term ->
+ let bv = on_sort_bv (visit_tm ff) bv in
+ let term = visit_tm ff term in
+ Pat_Dot_Term bv term
+and visit_comp (ff : term -> Tac term) (c : comp) : Tac comp =
+ let cv = inspect_comp c in
+ let cv' =
+ match cv with
+ | C_Total ret decr ->
+ let ret = visit_tm ff ret in
+ let decr = map (visit_tm ff) decr in
+ C_Total ret decr| C_GTotal ret decr ->
+ let ret = visit_tm ff ret in
+ let decr = map (visit_tm ff) decr in
+ C_GTotal ret decr| C_Lemma pre post pats ->
+ let pre = visit_tm ff pre in
+ let post = visit_tm ff post in
+ let pats = visit_tm ff pats in
+ C_Lemma pre post pats | C_Eff us eff res args ->
+ let res = visit_tm ff res in
+ let args = map (fun (a, q) -> (visit_tm ff a, q)) args in
+ C_Eff us eff res args
+in
+pack_comp cv'let rec destruct_list (t : term) : Tac (list term) =
+ let head, args = collect_app t in
+ match inspect_ln head, args with
+ | Tv_FVar fv, [(a1, Q_Explicit); (a2, Q_Explicit)]
+ | Tv_FVar fv, [(_, Q_Implicit); (a1, Q_Explicit); (a2, Q_Explicit)] ->
+ if inspect_fv fv = cons_qn
+ then a1 :: destruct_list a2
+ else raise NotAListLiteral
+ | Tv_FVar fv, _ ->
+ if inspect_fv fv = nil_qn
+ then []
+ else raise NotAListLiteral
+ | _ ->
+ raise NotAListLiteralprivate let get_match_body () : Tac term =
+ match FStar.Reflection.Formula.unsquash (cur_goal ()) with
+ | None -> fail ""
+ | Some t -> match inspect t with
+ | Tv_Match sc _ _ -> sc
+ | _ -> fail "Goal is not a match"private let rec last (x : list 'a) : Tac 'a =
+ match x with
+ | [] -> fail "last: empty list"
+ | [x] -> x
+ | _::xs -> last xsWhen the goal is match e with | p1 -> e1 ... | pn -> en,
+destruct it into n goals for each possible case, including an
+hypothesis for e matching the correposnding pattern.
let branch_on_match () : Tac unit =
+ focus (fun () ->
+ let x = get_match_body () in
+ let _ = t_destruct x in
+ iterAll (fun () ->
+ let bs = repeat intro in
+ let b = last bs in (* this one is the equality *)
+ grewrite_eq b;
+ norm [iota])
+ )When the argument i is non-negative, nth_binder grabs the nth
+binder in the current goal. When it is negative, it grabs the (-i-1)th
+binder counting from the end of the goal. That is, nth_binder (-1)
+will return the last binder, nth_binder (-2) the second to last, and
+so on.
let nth_binder (i:int) : Tac binder =
+ let bs = cur_binders () in
+ let k : int = if i >= 0 then i else List.Tot.Base.length bs + i in
+ let k : nat = if k < 0 then fail "not enough binders" else k in
+ match List.Tot.Base.nth bs k with
+ | None -> fail "not enough binders"
+ | Some b -> bexception AppearsDecides whether a top-level name nm syntactically
+appears in the term t.
let name_appears_in (nm:name) (t:term) : Tac bool =
+ let ff (t : term) : Tac term =
+ match t with
+ | Tv_FVar fv ->
+ if inspect_fv fv = nm then
+ raise Appears;
+ t
+ | t -> t
+ in
+ try ignore (visit_tm ff t); false with
+ | Appears -> true
+ | e -> raise emk_abs x1; ...; xn t returns the term fun x1 ... xn -> t
let rec mk_abs (args : list binder) (t : term) : Tac term (decreases args) =
+ match args with
+ | [] -> t
+ | a :: args' ->
+ let t' = mk_abs args' t in
+ pack (Tv_Abs a t')fsdoc: no-summary-found
-fsdoc: no-comment-found
+This module is extracted, don't add any assume vals or extraction
synth_by_tactic is fine)private
+let __tac (a:Type) = proofstate -> M (__result a)monadic return
+private
+let __ret (a:Type) (x:a) : __tac a = fun (s:proofstate) -> Success x smonadic bind
+private
+let __bind (a:Type) (b:Type) (r1 r2:range) (t1:__tac a) (t2:a -> __tac b) : __tac b =
+ fun ps ->
+ let ps = set_proofstate_range ps (FStar.Range.prims_to_fstar_range r1) in
+ let ps = incr_depth ps in
+ let r = t1 ps in
+ match r with
+ | Success a ps' ->
+ let ps' = set_proofstate_range ps' (FStar.Range.prims_to_fstar_range r2) inForce evaluation of __tracepoint q even on the interpreter
+ begin match tracepoint ps' with
+ | true -> t2 a (decr_depth ps')
+ end
+| Failed e ps' -> Failed e ps'Actions
+private
+let __get () : __tac proofstate = fun s0 -> Success s0 s0private
+let __raise (a:Type0) (e:exn) : __tac a = fun (ps:proofstate) -> Failed #a e psprivate
+let __tac_wp a = proofstate -> (__result a -> Tot Type0) -> Tot Type0The DMFF-generated bind_wp doesn't the contain the "don't
bind_wp for the effect with an efficient one.private
+unfold let g_bind (a:Type) (b:Type) (wp:__tac_wp a) (f:a -> __tac_wp b) = fun ps post ->
+ wp ps (fun m' -> match m' with
+ | Success a q -> f a q post
+ | Failed e q -> post (Failed e q))private
+unfold let g_compact (a:Type) (wp:__tac_wp a) : __tac_wp a =
+ fun ps post -> forall k. (forall (r:__result a).{:pattern (guard_free (k r))} post r ==> k r) ==> wp ps kprivate
+unfold let __TAC_eff_override_bind_wp (a:Type) (b:Type) (wp:__tac_wp a) (f:a -> __tac_wp b) =
+ g_compact b (g_bind a b wp f)[@@ dm4f_bind_range ]
+new_effect {
+ TAC : a:Type -> Effect
+ with repr = __tac
+ ; bind = __bind
+ ; return = __ret
+ ; __raise = __raise
+ ; __get = __get
+}Hoare variant
+effect TacH (a:Type) (pre : proofstate -> Tot Type0) (post : proofstate -> __result a -> Tot Type0) =
+ TAC a (fun ps post' -> pre ps /\ (forall r. post ps r ==> post' r))"Total" variant
+effect Tac (a:Type) = TacH a (requires (fun _ -> True)) (ensures (fun _ _ -> True))Metaprograms that succeed
+effect TacS (a:Type) = TacH a (requires (fun _ -> True)) (ensures (fun _ps r -> Success? r))A variant that doesn't prove totality (nor type safety!)
+effect TacF (a:Type) = TacH a (requires (fun _ -> False)) (ensures (fun _ _ -> True))unfold
+let lift_div_tac (a:Type) (wp:pure_wp a) : __tac_wp a =
+ fun ps p -> wp (fun x -> p (Success x ps))sub_effect DIV ~> TAC = lift_div_taclet get = TAC?.__get
+let raise (#a:Type) (e:exn) = TAC?.__raise a eval with_tactic (t : unit -> Tac unit) (p:Type u#a) : Type u#aThis syntactic marker will generate a goal of the shape x == ?u for
+t to solve this goal.t, the uvar was solved and only trivial goals remainrewrite_with_tactic t x will be replacedval rewrite_with_tactic (t:unit -> Tac unit) (#a:Type) (x:a) : aThis will run the tactic in order to (try to) produce a term of type
+val synth_by_tactic : (#t:Type) -> (unit -> Tac unit) -> Tot tval assert_by_tactic (p:Type) (t:unit -> Tac unit)
+ : Pure unit
+ (requires (set_range_of (with_tactic t (squash p)) (range_of t)))
+ (ensures (fun _ -> p))We don't peel off all with_tactics in negative positions, so give
val by_tactic_seman : tau:(unit -> Tac unit) -> phi:Type -> Lemma (with_tactic tau phi ==> phi)
+ [SMTPat (with_tactic tau phi)]One can always bypass the well-formedness of metaprograms. It does
+let assume_safe (#a:Type) (tau:unit -> TacF a) : Tac a = admit (); tau ()private let tac a b = a -> Tac b
+private let tactic a = tac unit aA hook to preprocess a definition before it is typechecked and
+tau will be called on a quoting of the definition of the letval preprocess_with (tau : term -> Tac term) : Tot unitA hook to postprocess a definition, after typechecking, and rewrite
+let x = E, thetau metaprogram is presented with a goal of the shape E == ?u for?u. The metaprogram should then both instantiate ?u
+val postprocess_with (tau : unit -> Tac unit) : Tot unitSimilar semantics to postprocess_with, but the metaprogram only
val postprocess_for_extraction_with (tau : unit -> Tac unit) : Tot unit#set-options "--no_tactics"val unfold_with_tactic (t:unit -> Tac unit) (p:Type)
+ : Lemma (requires p)
+ (ensures (with_tactic t p))val unfold_rewrite_with_tactic (t:unit -> Tac unit) (#a:Type) (p:a)
+ : Lemma (rewrite_with_tactic t p == p)fsdoc: no-summary-found
-fsdoc: no-comment-found
+let cur_formula () : Tac formula = term_as_formula (cur_goal ())private val revert_squash : (#a:Type) -> (#b : (a -> Type)) ->
+ (squash (forall (x:a). b x)) ->
+ x:a -> squash (b x)
+let revert_squash #a #b s x = let x : (_:unit{forall x. b x}) = s in ()let l_revert () : Tac unit =
+ revert ();
+ apply (`revert_squash)let rec l_revert_all (bs:binders) : Tac unit =
+ match bs with
+ | [] -> ()
+ | _::tl -> begin l_revert (); l_revert_all tl endprivate let fa_intro_lem (#a:Type) (#p:a -> Type) (f:(x:a -> squash (p x))) : Lemma (forall (x:a). p x) =
+ FStar.Classical.lemma_forall_intro_gtot
+ ((fun x -> FStar.IndefiniteDescription.elim_squash (f x)) <: (x:a -> GTot (p x)))let forall_intro () : Tac binder =
+ apply_lemma (`fa_intro_lem);
+ intro ()let forall_intro_as (s:string) : Tac binder =
+ apply_lemma (`fa_intro_lem);
+ intro_as slet forall_intros () : Tac binders = repeat1 forall_introprivate val split_lem : (#a:Type) -> (#b:Type) ->
+ squash a -> squash b -> Lemma (a /\ b)
+let split_lem #a #b sa sb = ()let split () : Tac unit =
+ try apply_lemma (`split_lem)
+ with | _ -> fail "Could not split goal"private val imp_intro_lem : (#a:Type) -> (#b : Type) ->
+ (a -> squash b) ->
+ Lemma (a ==> b)
+let imp_intro_lem #a #b f =
+ FStar.Classical.give_witness (FStar.Classical.arrow_to_impl (fun (x:squash a) -> FStar.Squash.bind_squash x f))let implies_intro () : Tac binder =
+ apply_lemma (`imp_intro_lem);
+ intro ()let implies_intros () : Tac binders = repeat1 implies_introlet l_intro () = forall_intro `or_else` implies_intro
+let l_intros () = repeat l_introThis should be next to mapply... bring mapply here?
+let mintro () : Tac binder =
+ first [intro; implies_intro; forall_intro; (fun () -> fail "cannot intro")]let mintros () : Tac (list binder) =
+ repeat mintrolet squash_intro () : Tac unit =
+ apply (`FStar.Squash.return_squash)let l_exact (t:term) =
+ try exact t with
+ | _ -> (squash_intro (); exact t)let hyp (b:binder) : Tac unit = l_exact (binder_to_term b)private
+let __lemma_to_squash #req #ens (_ : squash req) (h : (unit -> Lemma (requires req) (ensures ens))) : squash ens =
+ h ()let pose_lemma (t : term) : Tac binder =
+ let c = tcc (cur_env ()) t in
+ let pre, post =
+ match inspect_comp c with
+ | C_Lemma pre post _ -> pre, post
+ | _ -> fail ""
+ in
+ let post = `((`#post) ()) in (* unthunk *)
+ let post = norm_term [] post inIf the precondition is trivial, do not cut by it
+match term_as_formula' pre with
+| True_ ->
+ pose (`(__lemma_to_squash #(`#pre) #(`#post) () (fun () -> (`#t))))
+| _ ->
+ let reqb = tcut (`squash (`#pre)) inlet b = pose (`(__lemma_to_squash #(`#pre) #(`#post) (`#(binder_to_term reqb)) (fun () -> (`#t)))) in
+flip ();
+ignore (trytac trivial);
+blet explode () : Tac unit =
+ ignore (
+ repeatseq (fun () -> first [(fun () -> ignore (l_intro ()));
+ (fun () -> ignore (split ()))]))let rec visit (callback:unit -> Tac unit) : Tac unit =
+ focus (fun () ->
+ or_else callback
+ (fun () ->
+ let g = cur_goal () in
+ match term_as_formula g with
+ | Forall b phi ->
+ let binders = forall_intros () in
+ seq (fun () -> visit callback) (fun () -> l_revert_all binders)
+ | And p q ->
+ seq split (fun () -> visit callback)
+ | Implies p q ->
+ let _ = implies_intro () in
+ seq (fun () -> visit callback) l_revert
+ | _ ->
+ ()
+ )
+ )let rec simplify_eq_implication () : Tac unit =
+ let e = cur_env () in
+ let g = cur_goal () in
+ let r = destruct_equality_implication g in
+ match r with
+ | None ->
+ fail "Not an equality implication"
+ | Some (_, rhs) ->
+ let eq_h = implies_intro () in // G, eq_h:x=e |- P
+ rewrite eq_h; // G, eq_h:x=e |- P[e/x]
+ clear_top (); // G |- P[e/x]
+ visit simplify_eq_implicationlet rewrite_all_equalities () : Tac unit =
+ visit simplify_eq_implicationlet rec unfold_definition_and_simplify_eq (tm:term) : Tac unit =
+ let g = cur_goal () in
+ match term_as_formula g with
+ | App hd arg ->
+ if term_eq hd tm
+ then trivial ()
+ else ()
+ | _ -> begin
+ let r = destruct_equality_implication g in
+ match r with
+ | None -> fail "Not an equality implication"
+ | Some (_, rhs) ->
+ let eq_h = implies_intro () in
+ rewrite eq_h;
+ clear_top ();
+ visit (fun () -> unfold_definition_and_simplify_eq tm)
+ endprivate val vbind : (#p:Type) -> (#q:Type) -> squash p -> (p -> squash q) -> Lemma q
+let vbind #p #q sq f = FStar.Classical.give_witness_from_squash (FStar.Squash.bind_squash sq f)let unsquash (t:term) : Tac term =
+ let v = `vbind in
+ apply_lemma (mk_e_app v [t]);
+ let b = intro () in
+ pack_ln (Tv_Var (bv_of_binder b))private val or_ind : (#p:Type) -> (#q:Type) -> (#phi:Type) ->
+ (p \/ q) ->
+ (squash (p ==> phi)) ->
+ (squash (q ==> phi)) ->
+ Lemma phi
+let or_ind #p #q #phi o l r = ()let cases_or (o:term) : Tac unit =
+ apply_lemma (mk_e_app (`or_ind) [o])private val bool_ind : (b:bool) -> (phi:Type) -> (squash (b == true ==> phi)) ->
+ (squash (b == false ==> phi)) ->
+ Lemma phi
+let bool_ind b phi l r = ()let cases_bool (b:term) : Tac unit =
+ let bi = `bool_ind in
+ seq (fun () -> apply_lemma (mk_e_app bi [b]))
+ (fun () -> let _ = trytac (fun () -> let b = implies_intro () in rewrite b; clear_top ()) in ())private val or_intro_1 : (#p:Type) -> (#q:Type) -> squash p -> Lemma (p \/ q)
+let or_intro_1 #p #q _ = ()private val or_intro_2 : (#p:Type) -> (#q:Type) -> squash q -> Lemma (p \/ q)
+let or_intro_2 #p #q _ = ()let left () : Tac unit =
+ apply_lemma (`or_intro_1)let right () : Tac unit =
+ apply_lemma (`or_intro_2)private val __and_elim : (#p:Type) -> (#q:Type) -> (#phi:Type) ->
+ (p /\ q) ->
+ squash (p ==> q ==> phi) ->
+ Lemma phi
+let __and_elim #p #q #phi p_and_q f = ()private val __and_elim' : (#p:Type) -> (#q:Type) -> (#phi:Type) ->
+ squash (p /\ q) ->
+ squash (p ==> q ==> phi) ->
+ Lemma phi
+let __and_elim' #p #q #phi p_and_q f = ()let and_elim (t : term) : Tac unit =
+ begin
+ try apply_lemma (`(__and_elim (`#t)))
+ with | _ -> apply_lemma (`(__and_elim' (`#t)))
+ endlet destruct_and (t : term) : Tac (binder * binder) =
+ and_elim t;
+ (implies_intro (), implies_intro ())private val __witness : (#a:Type) -> (x:a) -> (#p:(a -> Type)) -> squash (p x) -> squash (exists (x:a). p x)
+private let __witness #a x #p _ = ()let witness (t : term) : Tac unit =
+ apply_raw (`__witness);
+ exact tprivate
+let __elim_exists' #t (#pred : t -> Type0) #goal (h : (exists x. pred x))
+ (k : (x:t -> pred x -> squash goal)) : squash goal =
+ FStar.Squash.bind_squash #(x:t & pred x) h (fun (|x, pf|) -> k x pf)returns witness and proof as binders
+let elim_exists (t : term) : Tac (binder & binder) =
+ apply_lemma (`(__elim_exists' (`#(t))));
+ let x = intro () in
+ let pf = intro () in
+ (x, pf)private
+let __forall_inst #t (#pred : t -> Type0) (h : (forall x. pred x)) (x : t) : squash (pred x) =
+ ()GM: annoying that this doesn't just work by SMT
+private
+let __forall_inst_sq #t (#pred : t -> Type0) (h : squash (forall x. pred x)) (x : t) : squash (pred x) =
+ FStar.Squash.bind_squash h (fun (f : (forall x. pred x)) -> __forall_inst f x)let instantiate (fa : term) (x : term) : Tac binder =
+ try pose (`__forall_inst_sq (`#fa) (`#x)) with | _ ->
+ try pose (`__forall_inst (`#fa) (`#x)) with | _ ->
+ fail "could not instantiate"private
+let sklem0 (#a:Type) (#p : a -> Type0) ($v : (exists (x:a). p x)) (phi:Type0) :
+ Lemma (requires (forall x. p x ==> phi))
+ (ensures phi) = ()private
+let rec sk_binder' (acc:binders) (b:binder) : Tac (binders * binder) =
+ focus (fun () ->
+ try
+ apply_lemma (`(sklem0 (`#(binder_to_term b))));
+ if ngoals () <> 1 then fail "no";
+ clear b;
+ let bx = forall_intro () in
+ let b' = implies_intro () in
+ sk_binder' (bx::acc) b' (* We might have introduced a new existential, so possibly recurse *)
+ with | _ -> (acc, b) (* If the above failed, just return *)
+ )Skolemizes a given binder for an existential, returning the introduced new binders
+let sk_binder b = sk_binder' [] blet skolem () =
+ let bs = binders_of_env (cur_env ()) in
+ map sk_binder bsprivate
+val lemma_from_squash : #a:Type -> #b:(a -> Type) -> (x:a -> squash (b x)) -> x:a -> Lemma (b x)
+private
+let lemma_from_squash #a #b f x = let _ = f x in assert (b x)private
+let easy_fill () =
+ let _ = repeat intro inIf the goal is a -> Lemma b, intro will fail, try to use this switch
let _ = trytac (fun () -> apply (`lemma_from_squash); intro ()) in
+smt ()val easy : #a:Type -> (#[easy_fill ()] _ : a) -> a
+let easy #a #x = xfsdoc: no-summary-found
-fsdoc: no-comment-found
-let ((mustfail #a (t:Unidentified product: [unit] (Tac a)) (message:string)):(Tac unit)):match trytac t with (Some _) -> fail message | None -> ()+++========================== +Pattern-matching tactics
+:Author: Clément Pit-Claudel +:Contact: clement.pitclaudel@live.com +:Date: 2017-10-13
+
+++Contents
+1 Contents +2 Motivation +3 Some utility functions +4 Pattern types +5 Pattern matching exceptions +5.1 Types of exceptions +5.2 The exception monad +5.3 Liftings +6 Pattern interpretation +7 Pattern-matching problems +7.1 Definitions +7.2 Resolution +8 A DSL for pattern-matching +8.1 Pattern notations +8.2 Problem notations +8.3 Continuations +9 Putting it all together +10 Examples +10.1 Simple examples +10.2 A real-life example +11 Possible extensions +12 Notes
++Motivation
+Suppose you have a goal of the form
+squash (a == b). How do you capture +aandbfor further inspection?Here's a basic (but cumbersome!) implementation:
+
let fetch_eq_side () : Tac (term * term) =
+ let g = cur_goal () in
+ match inspect g with
+ | Tv_App squash (g, _) ->
+ (match inspect squash with
+ | Tv_FVar squash ->
+ if fv_to_string squash = flatten_name squash_qn then
+ (match inspect g with
+ | Tv_App eq_type_x (y, _) ->
+ (match inspect eq_type_x with
+ | Tv_App eq_type (x, _) ->
+ (match inspect eq_type with
+ | Tv_App eq (typ, _) ->
+ (match inspect eq with
+ | Tv_FVar eq ->
+ if fv_to_string eq = flatten_name eq2_qn then
+ (x, y)
+ else fail "not an equality"
+ | _ -> fail "not an app2 of fvar: ")
+ | _ -> fail "not an app3")
+ | _ -> fail "not an app2")
+ | _ -> fail "not an app under squash")
+ else fail "not a squash"
+ | _ -> fail "not an app of fvar at top level")
+ | _ -> fail "not an app at top level"++…and here's how you could use it:
+
let _ =
+assert_by_tactic (1 + 1 == 2)
+(fun () -> let l, r = fetch_eq_side () in
+print (term_to_string l ^ " / " ^ term_to_string r))
+++This file defines pattern-matching primitives that let you write the same +thing like this…
+.. code:: fstar
+let fetch_eq_side' #a () : Tac (term * term) = +gpm (fun (left right: a) (g: pm_goal (squash (left == right))) -> +(quote left, quote right) <: Tac (term * term))
+let _ = +assert_by_tactic (1 + 1 == 2) +(fun () -> let l, r = fetch_eq_side' #int () in +print (term_to_string l ^ " / " ^ term_to_string r))
+…or, more succinctly, like this:
+.. code:: fstar
+let _ = +assert_by_tactic (1 + 1 == 2) +(gpm (fun (left right: int) (g: pm_goal (squash (left == right))) -> +let l, r = quote left, quote right in +print (term_to_string l ^ " / " ^ term_to_string r) <: Tac unit))
+
Many of the tactics are written in the Tac effect, which isn't
+well-supported in SMT. FIXME: remove this once Tac is marked as a stable
+effect.
+GM: Tac is now stable, but some VCs are still tough on z3, so there are a few admit()s.
+++Some utility functions
+(Skip over this part on a quick read — these are just convenience functions)
+
Ensure that tactic t fails. *
let ((exact_hyp (a:Type0) (h:binder)):(Tac unit)):let hd = quote ((FStar.Squash.return_squash #a)) in exact (mk_app hd (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 (pack ((Tv_Var (bv_of_binder h)))) Q_Explicit)) (Prims.Nil )))let mustfail #a (t: unit -> Tac a) (message: string) : Tac unit =
+ match trytac t with
+ | Some _ -> fail message
+ | None -> ()++The following two tactics are needed because of issues with the
+Tac+effect.
let implies_intro' () : Tac unit =
+ let _ = implies_intro () in ()let repeat' #a (f: unit -> Tac a) : Tac unit =
+ let _ = repeat f in ()let and_elim' (h: binder) : Tac unit =
+ and_elim (pack (Tv_Var (bv_of_binder h)));
+ clear hUse a hypothesis at type a to satisfy a goal at type squash a
-let ((exact_hyp' (h:binder)):(Tac unit)):exact (pack ((Tv_Var (bv_of_binder h))))let exact_hyp (a: Type0) (h: binder) : Tac unit =
+ let hd = quote (FStar.Squash.return_squash #a) in
+ exact (mk_app hd [((pack (Tv_Var (bv_of_binder h))), Q_Explicit)])Use a hypothesis h (of type a) to satisfy a goal at type a
-let ((interp_pattern_aux (pat:pattern) (cur_bindings:bindings) (tm:term)):(Tac (match_res bindings))):admit (); let (interp_any () cur_bindings tm) = return (Prims.Nil ) in let (interp_var (v:varname) cur_bindings tm) = match List.Tot.assoc v cur_bindings with (Some tm') -> if term_eq tm tm' then return cur_bindings else raise ((NonLinearMismatch ((FStar.Pervasives.Native.Mktuple3 v tm tm')))) | None -> return ((Prims.Cons ((FStar.Pervasives.Native.Mktuple2 v tm)) cur_bindings)) in let (interp_qn (qn:qn) cur_bindings tm) = match inspect tm with (Tv_FVar fv) -> if =(fv_to_string fv, qn) then return cur_bindings else raise ((NameMismatch ((FStar.Pervasives.Native.Mktuple2 qn (fv_to_string fv))))) | _ -> raise ((SimpleMismatch ((FStar.Pervasives.Native.Mktuple2 pat tm)))) in let (interp_type cur_bindings tm) = match inspect tm with (Tv_Type ()) -> return cur_bindings | _ -> raise ((SimpleMismatch ((FStar.Pervasives.Native.Mktuple2 pat tm)))) in let (interp_app (p_hd:(p:pattern:{<<(p, pat)})) (p_arg:(p:pattern:{<<(p, pat)})) cur_bindings tm) = match inspect tm with (Tv_App hd (arg, _)) -> with_hd <- interp_pattern_aux p_hd cur_bindings hd; with_arg <- interp_pattern_aux p_arg with_hd arg; return with_arg | _ -> raise ((SimpleMismatch ((FStar.Pervasives.Native.Mktuple2 pat tm)))) in match pat with PAny -> interp_any () cur_bindings tm | (PVar var) -> interp_var var cur_bindings tm | (PQn qn) -> interp_qn qn cur_bindings tm | PType -> interp_type cur_bindings tm | (PApp p_hd p_arg) -> interp_app p_hd p_arg cur_bindings tm | _ -> fail "?"Match a pattern against a term. cur_bindings is a list of bindings collected while matching previous parts of the pattern. Returns a result in the exception monad. *
let ((interp_pattern (pat:pattern)):Unidentified product: [term] (Tac (match_res bindings))):(fun (tm:term) -> rev_bindings <- interp_pattern_aux pat (Prims.Nil ) tm; return (List.Tot.rev rev_bindings))Match a pattern pat against a term. Returns a result in the exception monad. *
let ((match_term pat (tm:term)):(Tac bindings)):match interp_pattern pat (norm_term (Prims.Nil ) tm) with (Success bb) -> bb | (Failure ex) -> Tactics.fail (string_of_match_exception ex)Match a term tm against a pattern pat. Raises an exception if the match fails. This is mostly useful for debugging: use mgw to capture matches. *
let ((assoc_varname_fail (#b:Type) (key:varname) (ls:list (*(varname, b)))):(Tac b)):match List.Tot.assoc key ls with None -> fail (^("Not found: ", key)) | (Some x) -> xlet exact_hyp' (h: binder): Tac unit =
+ exact (pack (Tv_Var (bv_of_binder h)))+++Pattern types
+Patterns are defined using a simple inductive type, mirroring the structure +of
+term_view.
type varname = stringtype qn = stringtype pattern =
+| PAny: pattern
+| PVar: name: varname -> pattern
+| PQn: qn: qn -> pattern
+| PType: pattern
+| PApp: hd: pattern -> arg: pattern -> patternlet desc_of_pattern = function
+| PAny -> "anything"
+| PVar _ -> "a variable"
+| PQn qn -> "a constant (" ^ qn ^ ")"
+| PType -> "Type"
+| PApp _ _ -> "a function application"let rec string_of_pattern = function
+| PAny -> "__"
+| PVar x -> "?" ^ x
+| PQn qn -> qn
+| PType -> "Type"
+| PApp l r -> "(" ^ string_of_pattern l ^ " "
+ ^ string_of_pattern r ^ ")"+++Pattern matching exceptions
+Pattern-matching is defined as a pure, monadic function (because of issues +with combining DM4F effects, but also because it helps with debugging). +This section defines the exception monad.
++Types of exceptions
+
noeq type match_exception =
+| NameMismatch of qn * qn
+| SimpleMismatch of pattern * term
+| NonLinearMismatch of varname * term * term
+| UnsupportedTermInPattern of term
+| IncorrectTypeInAbsPatBinder of typlet term_head t : Tac string =
+ match inspect t with
+ | Tv_Var bv -> "Tv_Var"
+ | Tv_BVar fv -> "Tv_BVar"
+ | Tv_FVar fv -> "Tv_FVar"
+ | Tv_App f x -> "Tv_App"
+ | Tv_Abs x t -> "Tv_Abs"
+ | Tv_Arrow x t -> "Tv_Arrow"
+ | Tv_Type () -> "Tv_Type"
+ | Tv_Refine x t -> "Tv_Refine"
+ | Tv_Const cst -> "Tv_Const"
+ | Tv_Uvar i t -> "Tv_Uvar"
+ | Tv_Let r attrs b t1 t2 -> "Tv_Let"
+ | Tv_Match t _ branches -> "Tv_Match"
+ | Tv_AscribedT _ _ _ -> "Tv_AscribedT"
+ | Tv_AscribedC _ _ _ -> "Tv_AscribedC"
+ | Tv_Unknown -> "Tv_Unknown"let string_of_match_exception = function
+ | NameMismatch (qn1, qn2) ->
+ "Match failure (name mismatch): expecting " ^
+ qn1 ^ ", found " ^ qn2
+ | SimpleMismatch (pat, tm) ->
+ "Match failure (sort mismatch): expecting " ^
+ desc_of_pattern pat ^ ", got " ^ term_to_string tm
+ | NonLinearMismatch (nm, t1, t2) ->
+ "Match failure (nonlinear mismatch): variable " ^ nm ^
+ " needs to match both " ^ (term_to_string t1) ^
+ " and " ^ (term_to_string t2)
+ | UnsupportedTermInPattern tm ->
+ "Match failure (unsupported term in pattern): " ^
+ term_to_string tm ^ " (" ^ term_head tm ^ ")"
+ | IncorrectTypeInAbsPatBinder typ ->
+ "Incorrect type in pattern-matching binder: " ^
+ term_to_string typ ^ " (use one of ``var``, ``hyp …``, or ``goal …``)"+++The exception monad
+
noeq type match_res a =
+| Success of a
+| Failure of match_exceptionlet return #a (x: a) : match_res a =
+ Success xlet bind (#a #b: Type)
+ (f: match_res a)
+ (g: a -> Tac (match_res b))
+ : Tac (match_res b) =
+ match f with
+ | Success aa -> g aa
+ | Failure ex -> Failure exlet raise #a (ex: match_exception) : match_res a =
+ Failure ex+++Liftings
+There's a natural lifting from the exception monad into the tactic effect:
+
let lift_exn_tac #a #b (f: a -> match_res b) (aa: a) : Tac b =
+ match f aa with
+ | Success bb -> bb
+ | Failure ex -> Tactics.fail (string_of_match_exception ex)let lift_exn_tactic #a #b (f: a -> match_res b) (aa: a) : Tac b =
+ match f aa with
+ | Success bb -> bb
+ | Failure ex -> Tactics.fail (string_of_match_exception ex)+++Pattern interpretation
+This section implement pattern-matching. This is strictly a one term, one +pattern implementation — handling cases in which mutliple hypotheses match +the same pattern is done later.
+
type bindings = list (varname * term)
+let string_of_bindings (bindings: bindings) =
+ String.concat "\n"
+ (map (fun (nm, tm) -> (">> " ^ nm ^ ": " ^ term_to_string tm))
+ bindings)Match a pattern against a term.
+cur_bindings is a list of bindings collected while matching previous parts of
+the pattern. Returns a result in the exception monad. *
let rec interp_pattern_aux (pat: pattern) (cur_bindings: bindings) (tm:term)
+ : Tac (match_res bindings) =
+ admit();
+ let interp_any () cur_bindings tm =
+ return [] in
+ let interp_var (v: varname) cur_bindings tm =
+ match List.Tot.Base.assoc v cur_bindings with
+ | Some tm' -> if term_eq tm tm' then return cur_bindings
+ else raise (NonLinearMismatch (v, tm, tm'))
+ | None -> return ((v, tm) :: cur_bindings) in
+ let interp_qn (qn: qn) cur_bindings tm =
+ match inspect tm with
+ | Tv_FVar fv ->
+ if fv_to_string fv = qn then return cur_bindings
+ else raise (NameMismatch (qn, (fv_to_string fv)))
+ | _ -> raise (SimpleMismatch (pat, tm)) in
+ let interp_type cur_bindings tm =
+ match inspect tm with
+ | Tv_Type () -> return cur_bindings
+ | _ -> raise (SimpleMismatch (pat, tm)) in
+ let interp_app (p_hd p_arg: (p:pattern{p << pat})) cur_bindings tm =
+ match inspect tm with
+ | Tv_App hd (arg, _) ->
+ with_hd <-- interp_pattern_aux p_hd cur_bindings hd;
+ with_arg <-- interp_pattern_aux p_arg with_hd arg;
+ return with_arg
+ | _ -> raise (SimpleMismatch (pat, tm)) in
+ match pat with
+ | PAny -> interp_any () cur_bindings tm
+ | PVar var -> interp_var var cur_bindings tm
+ | PQn qn -> interp_qn qn cur_bindings tm
+ | PType -> interp_type cur_bindings tm
+ | PApp p_hd p_arg -> interp_app p_hd p_arg cur_bindings tmGM: Jul 11 2018, sadly this is needed, seems this monad layered +on top of Tac causesq queries to be hard on Z3
+| _ -> fail "?"Match a pattern pat against a term.
+Returns a result in the exception monad. *
let interp_pattern (pat: pattern) : term -> Tac (match_res bindings) =
+ fun (tm: term) ->
+ rev_bindings <-- interp_pattern_aux pat [] tm;
+ return (List.Tot.Base.rev rev_bindings)Match a term tm against a pattern pat.
+Raises an exception if the match fails. This is mostly useful for debugging:
+use mgw to capture matches. *
let match_term pat (tm : term) : Tac bindings =
+ match interp_pattern pat (norm_term [] tm) with
+ | Success bb -> bb
+ | Failure ex -> Tactics.fail (string_of_match_exception ex)+++Pattern-matching problems
+Generalizing past single-term single-pattern problems, we obtain the +following notions of pattern-matching problems and solutions:
+
let debug msg : Tac unit = () // print msg+++Definitions
+
let absvar = binder
+type hypothesis = binder++A matching problem is composed of holes (
+mp_vars), hypothesis patterns +(mp_hyps), and a goal pattern (mp_goal).
noeq type matching_problem =
+ { mp_vars: list varname;
+ mp_hyps: list (varname * pattern);
+ mp_goal: option pattern }let string_of_matching_problem mp =
+ let vars =
+ String.concat ", " mp.mp_vars in
+ let hyps =
+ String.concat "\n "
+ (List.Tot.Base.map (fun (nm, pat) ->
+ nm ^ ": " ^ (string_of_pattern pat)) mp.mp_hyps) in
+ let goal = match mp.mp_goal with
+ | None -> "_"
+ | Some pat -> string_of_pattern pat in
+ "\n{ vars: " ^ vars ^ "\n" ^
+ " hyps: " ^ hyps ^ "\n" ^
+ " goal: " ^ goal ^ " }"++A solution is composed of terms captured to mach the holes, and binders +captured to match hypothesis patterns.
+
noeq type matching_solution =
+ { ms_vars: list (varname * term);
+ ms_hyps: list (varname * hypothesis) }let string_of_matching_solution ms =
+ let vars =
+ String.concat "\n "
+ (map (fun (varname, tm) ->
+ varname ^ ": " ^ (term_to_string tm)) ms.ms_vars) in
+ let hyps =
+ String.concat "\n "
+ (map (fun (nm, binder) ->
+ nm ^ ": " ^ (binder_to_string binder)) ms.ms_hyps) in
+ "\n{ vars: " ^ vars ^ "\n" ^
+ " hyps: " ^ hyps ^ " }"Find a varname in an association list; fail if it can't be found. *
-let ((solve_mp_for_single_hyp #a (name:varname) (pat:pattern) (hypotheses:list hypothesis) (body:Unidentified product: [matching_solution] (Tac a)) (part_sol:matching_solution)):(Tac a)):match hypotheses with [] -> fail #a "No matching hypothesis" | (Prims.Cons h hs) -> or_else ((fun () -> match interp_pattern_aux pat part_sol.ms_vars (type_of_binder h) with (Failure ex) -> fail (^("Failed to match hyp: ", (string_of_match_exception ex))) | (Success bindings) -> let ms_hyps = (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 name h)) part_sol.ms_hyps) in body ({part_sol with ms_vars=bindings ms_hyps=ms_hyps}))) ((fun () -> solve_mp_for_single_hyp name pat hs body part_sol))let assoc_varname_fail (#b: Type) (key: varname) (ls: list (varname * b))
+ : Tac b =
+ match List.Tot.Base.assoc key ls with
+ | None -> fail ("Not found: " ^ key)
+ | Some x -> xlet ms_locate_hyp (a: Type) (solution: matching_solution)
+ (name: varname) : Tac binder =
+ assoc_varname_fail name solution.ms_hypslet ms_locate_var (a: Type) (solution: matching_solution)
+ (name: varname) : Tac a =
+ unquote #a (assoc_varname_fail name solution.ms_vars)let ms_locate_unit (a: Type) _solution _binder_name : Tac unit =
+ ()+++Resolution
+Solving a matching problem is a two-steps process: find an initial +assignment for holes based on the goal pattern, then find a set of +hypotheses matching hypothesis patterns.
+Note that the implementation takes a continuation of type +
+matching_solution -> Tac a. This continuation is needed because we want +users to be able to provide extra criteria on matching solutions (most +commonly, this criterion is that a particular tactic should run +successfuly).This makes it easy to implement a simple for of search through the context, +where one can find a hypothesis matching a particular predicate by +constructing a trivial matching problem and passing the predicate as the +continuation.
+
Scan hypotheses for a match for pat that lets body succeed.
name is used to refer to the hypothesis matched in the final solution. part_sol includes bindings gathered while matching previous solutions. *
let ((solve_mp_for_hyps #a (mp_hyps:list (*(varname, pattern))) (hypotheses:list hypothesis) (body:Unidentified product: [matching_solution] (Tac a)) (partial_solution:matching_solution)):(Tac a)):match mp_hyps with [] -> body partial_solution | (Prims.Cons (name, pat) pats) -> solve_mp_for_single_hyp name pat hypotheses (solve_mp_for_hyps pats hypotheses body) partial_solutionScan hypotheses for matches for mp_hyps that lets body succeed. *
let ((solve_mp #a (problem:matching_problem) (hypotheses:binders) (goal:term) (body:Unidentified product: [matching_solution] (Tac a))):(Tac a)):let goal_ps = match problem.mp_goal with None -> {ms_vars=(Prims.Nil ) ms_hyps=(Prims.Nil )} | (Some pat) -> match interp_pattern pat goal with (Failure ex) -> fail (^("Failed to match goal: ", (string_of_match_exception ex))) | (Success bindings) -> {ms_vars=bindings ms_hyps=(Prims.Nil )} in solve_mp_for_hyps #a problem.mp_hyps hypotheses body goal_pslet rec solve_mp_for_single_hyp #a
+ (name: varname)
+ (pat: pattern)
+ (hypotheses: list hypothesis)
+ (body: matching_solution -> Tac a)
+ (part_sol: matching_solution)
+ : Tac a =
+ match hypotheses with
+ | [] ->
+ fail #a "No matching hypothesis"
+ | h :: hs ->
+ or_else // Must be in ``Tac`` here to run `body`
+ (fun () ->
+ match interp_pattern_aux pat part_sol.ms_vars (type_of_binder h) with
+ | Failure ex ->
+ fail ("Failed to match hyp: " ^ (string_of_match_exception ex))
+ | Success bindings ->
+ let ms_hyps = (name, h) :: part_sol.ms_hyps in
+ body ({ part_sol with ms_vars = bindings; ms_hyps = ms_hyps }))
+ (fun () ->
+ solve_mp_for_single_hyp name pat hs body part_sol)name is used to refer to the hypothesis matched in the final solution.
+part_sol includes bindings gathered while matching previous solutions. *
Scan hypotheses for matches for mp_hyps that lets body
+succeed. *
let rec solve_mp_for_hyps #a
+ (mp_hyps: list (varname * pattern))
+ (hypotheses: list hypothesis)
+ (body: matching_solution -> Tac a)
+ (partial_solution: matching_solution)
+ : Tac a =
+ match mp_hyps with
+ | [] -> body partial_solution
+ | (name, pat) :: pats ->
+ solve_mp_for_single_hyp name pat hypotheses
+ (solve_mp_for_hyps pats hypotheses body)
+ partial_solutionSolve a matching problem.
-The solution returned is constructed to ensure that the continuation body succeeds: this implements the usual backtracking-match semantics. *
let ((pattern_of_term_ex tm):(Tac (match_res pattern))):match inspect tm with (Tv_Var bv) -> return ((PVar (name_of_bv bv))) | (Tv_FVar fv) -> let qn = fv_to_string fv in return (if =(qn, any_qn) then PAny else (PQn qn)) | (Tv_Type ()) -> return PType | (Tv_App f (x, _)) -> let is_any = match inspect f with (Tv_FVar fv) -> =(fv_to_string fv, any_qn) | _ -> false in if is_any then return PAny else (fpat <- pattern_of_term_ex f; xpat <- pattern_of_term_ex x; return ((PApp fpat xpat))) | _ -> raise ((UnsupportedTermInPattern tm))let solve_mp #a (problem: matching_problem)
+ (hypotheses: binders) (goal: term)
+ (body: matching_solution -> Tac a)
+ : Tac a =
+ let goal_ps =
+ match problem.mp_goal with
+ | None -> { ms_vars = []; ms_hyps = [] }
+ | Some pat ->
+ match interp_pattern pat goal with
+ | Failure ex -> fail ("Failed to match goal: " ^ (string_of_match_exception ex))
+ | Success bindings -> { ms_vars = bindings; ms_hyps = [] } in
+ solve_mp_for_hyps #a problem.mp_hyps hypotheses body goal_psThe solution returned is constructed to ensure that the continuation body
+succeeds: this implements the usual backtracking-match semantics. *
+++A DSL for pattern-matching
+Using pattern-matching problems as defined above is relatively cumbersome, +so we now introduce a lightweight notation, in two steps: pattern notations, +and matching-problem notations.
++Pattern notations
+The first part of our pattern-matching syntax is pattern notations: we +provide a reflective function which constructs a pattern from a term: +variables are holes, free variables are constants, and applications are +application patterns.
+
This is a hack to allow users to capture anything.
+assume val __ : #t:Type -> t
+let any_qn = `%__Compile a term tm into a pattern. *
let ((beta_reduce (tm:term)):(Tac term)):norm_term (Prims.Nil ) tmβ-reduce a term tm. This is useful to remove needles function applications introduced by F, like (fun a b c -> a) 1 2 3.
let ((pattern_of_term tm):(Tac pattern)):match pattern_of_term_ex tm with (Success bb) -> bb | (Failure ex) -> Tactics.fail (string_of_match_exception ex)let rec pattern_of_term_ex tm : Tac (match_res pattern) =
+ match inspect tm with
+ | Tv_Var bv ->
+ return (PVar (name_of_bv bv))
+ | Tv_FVar fv ->
+ let qn = fv_to_string fv in
+ return (if qn = any_qn then PAny else PQn qn)
+ | Tv_Type () ->
+ return PType
+ | Tv_App f (x, _) ->
+ let is_any = match inspect f with
+ | Tv_FVar fv -> fv_to_string fv = any_qn
+ | _ -> false in
+ if is_any then
+ return PAny
+ else
+ (fpat <-- pattern_of_term_ex f;
+ xpat <-- pattern_of_term_ex x;
+ return (PApp fpat xpat))
+ | _ -> raise (UnsupportedTermInPattern tm)β-reduce a term tm.
+This is useful to remove needles function applications introduced by F*, like
+(fun a b c -> a) 1 2 3. *
let beta_reduce (tm: term) : Tac term =
+ norm_term [] tmCompile a term tm into a pattern. *
let ((binders_and_body_of_abs tm):(Tac (*(binders, term)))):match inspect tm with (Tv_Abs binder tm) -> let (binders, body) = binders_and_body_of_abs tm in (FStar.Pervasives.Native.Mktuple2 (Prims.Cons binder binders) body) | _ -> (FStar.Pervasives.Native.Mktuple2 (Prims.Nil ) tm)let pattern_of_term tm : Tac pattern =
+ match pattern_of_term_ex tm with
+ | Success bb -> bb
+ | Failure ex -> Tactics.fail (string_of_match_exception ex)+++Problem notations
+We then introduce a DSL for matching problems, best explained on the +following example::
+(fun (a b c: ①) (h1 h2 h3: hyp ②) (g: pm_goal ③) → ④)
+This notation is intended to express a pattern-matching problems with three +holes
+a,b, andcof type â‘ , matching hypothesesh1,h2, +andh3against pattern â‘¡ and the goal against the pattern â‘¢. The body +of the notation (â‘£) is then run with appropriate terms bound toa, +b, andc, appropriate binders bound toh1,h2, andh3, +and()bound tog.We call these patterns
+abspats (abstraction patterns), and we provide +facilities to parse them into matching problems, and to run their bodies +against a particular matching solution.
We used to annotate variables with an explicit 'var' marker, but then that +var annotation leaked into the types of other hypotheses due to type +inference, requiring non-trivial normalization.
+let var (a: Type) = a
+let hyp (a: Type) = binder
+let pm_goal (a: Type) = unitlet hyp_qn = `%hyp
+let goal_qn = `%pm_goalnoeq type abspat_binder_kind =
+| ABKVar of typ
+| ABKHyp
+| ABKGoallet string_of_abspat_binder_kind = function
+ | ABKVar _ -> "varname"
+ | ABKHyp -> "hyp"
+ | ABKGoal -> "goal"noeq type abspat_argspec =
+ { asa_name: absvar;
+ asa_kind: abspat_binder_kind }We must store this continuation, because recomputing it yields different +names when the binders are re-opened.
+type abspat_continuation =
+ list abspat_argspec * termlet classify_abspat_binder binder : Tac (abspat_binder_kind * term) =
+ let varname = "v" in
+ let hyp_pat = PApp (PQn hyp_qn) (PVar varname) in
+ let goal_pat = PApp (PQn goal_qn) (PVar varname) inlet typ = type_of_binder binder in
+match interp_pattern hyp_pat typ with
+| Success [(_, hyp_typ)] -> ABKHyp, hyp_typ
+| Success _ -> fail "classifiy_abspat_binder: impossible (1)"
+| Failure _ ->
+ match interp_pattern goal_pat typ with
+ | Success [(_, goal_typ)] -> ABKGoal, goal_typ
+ | Success _ -> fail "classifiy_abspat_binder: impossible (2)"
+ | Failure _ -> ABKVar typ, typSplit an abstraction tm into a list of binders and a body. *
let ((matching_problem_of_abs (tm:term)):(Tac (*(matching_problem, abspat_continuation)))):let (binders, body) = binders_and_body_of_abs (cleanup_abspat tm) in debug (^("Got binders: ", (String.concat ", " (map ((fun b -> (name_of_binder b : (Tac string)))) binders)))); let classified_binders = map ((fun binder -> let bv_name = name_of_binder binder in debug (^("Got binder: ", ^(bv_name, ^("; type is ", term_to_string (type_of_binder binder))))); let (binder_kind, typ) = classify_abspat_binder binder in ((FStar.Pervasives.Native.Mktuple4 binder bv_name binder_kind typ)))) binders in let problem = fold_left ((fun problem (binder, bv_name, binder_kind, typ) -> debug (^("Compiling binder ", ^(name_of_binder binder, ^(", classified as ", ^(string_of_abspat_binder_kind binder_kind, ^(", with type ", term_to_string typ)))))); match binder_kind with (ABKVar _) -> {problem with mp_vars=(Prims.Cons bv_name problem.mp_vars)} | ABKHyp -> {problem with mp_hyps=(Prims.Cons ((FStar.Pervasives.Native.Mktuple2 bv_name (pattern_of_term typ))) problem.mp_hyps)} | ABKGoal -> {problem with mp_goal=(Some (pattern_of_term typ))})) ({mp_vars=(Prims.Nil ) mp_hyps=(Prims.Nil ) mp_goal=None}) classified_binders in let continuation = let ((abspat_argspec_of_binder xx):(Tac abspat_argspec)) = match xx with (binder, xx, binder_kind, yy) -> {asa_name=binder asa_kind=binder_kind} in ((FStar.Pervasives.Native.Mktuple2 map abspat_argspec_of_binder classified_binders tm)) in let mp = {mp_vars=List.rev #varname problem.mp_vars mp_hyps=List.rev #(*(varname, pattern)) problem.mp_hyps mp_goal=problem.mp_goal} in debug (^("Got matching problem: ", (string_of_matching_problem mp))); (FStar.Pervasives.Native.Mktuple2 mp continuation)let rec binders_and_body_of_abs tm : Tac (binders * term) =
+ match inspect tm with
+ | Tv_Abs binder tm ->
+ let binders, body = binders_and_body_of_abs tm in
+ binder :: binders, body
+ | _ -> [], tmlet cleanup_abspat (t: term) : Tac term =
+ norm_term [] tParse a notation into a matching problem and a continuation.
-Pattern-matching notations are of the form (fun binders… -> continuation), where binders are of one of the forms var …, hyp …, or goal …. var binders are typed holes to be used in other binders; hyp binders indicate a pattern to be matched against hypotheses; and goal binders match the goal.
A reduction phase is run to ensure that the pattern looks reasonable; it is needed because F* tends to infer arguments in β-expanded form.
-The continuation returned can't directly be applied to a pattern-matching solution; see interp_abspat_continuation below for that. *
let ((arg_type_of_binder_kind binder_kind):(Tac term)):match binder_kind with (ABKVar typ) -> typ | ABKHyp -> (`(binder)) | ABKGoal -> (`(unit))let matching_problem_of_abs (tm: term)
+ : Tac (matching_problem * abspat_continuation) =Pattern-matching notations are of the form (fun binders… -> continuation),
+where binders are of one of the forms var …, hyp …, or goal ….
+var binders are typed holes to be used in other binders; hyp binders
+indicate a pattern to be matched against hypotheses; and goal binders match
+the goal.
A reduction phase is run to ensure that the pattern looks reasonable; it is +needed because F* tends to infer arguments in β-expanded form.
+The continuation returned can't directly be applied to a pattern-matching
+solution; see [interp_abspat_continuation](#interp_abspat_continuation) below for that. *
let binders, body = binders_and_body_of_abs (cleanup_abspat tm) in
+debug ("Got binders: " ^ (String.concat ", "
+ (map (fun b -> name_of_binder b <: Tac string) binders)));let classified_binders =
+ map (fun binder ->
+ let bv_name = name_of_binder binder in
+ debug ("Got binder: " ^ bv_name ^ "; type is " ^
+ term_to_string (type_of_binder binder));
+ let binder_kind, typ = classify_abspat_binder binder in
+ (binder, bv_name, binder_kind, typ))
+ binders inlet problem =
+ fold_left
+ (fun problem (binder, bv_name, binder_kind, typ) ->
+ debug ("Compiling binder " ^ name_of_binder binder ^
+ ", classified as " ^ string_of_abspat_binder_kind binder_kind ^
+ ", with type " ^ term_to_string typ);
+ match binder_kind with
+ | ABKVar _ -> { problem with mp_vars = bv_name :: problem.mp_vars }
+ | ABKHyp -> { problem with mp_hyps = (bv_name, (pattern_of_term typ))
+ :: problem.mp_hyps }
+ | ABKGoal -> { problem with mp_goal = Some (pattern_of_term typ) })
+ ({ mp_vars = []; mp_hyps = []; mp_goal = None })
+ classified_binders inlet continuation =
+ let abspat_argspec_of_binder xx : Tac abspat_argspec =
+ match xx with | (binder, xx, binder_kind, yy) ->
+ { asa_name = binder; asa_kind = binder_kind } in
+ (map abspat_argspec_of_binder classified_binders, tm) inlet mp =
+ { mp_vars = List.Tot.Base.rev #varname problem.mp_vars;
+ mp_hyps = List.Tot.Base.rev #(varname * pattern) problem.mp_hyps;
+ mp_goal = problem.mp_goal } indebug ("Got matching problem: " ^ (string_of_matching_problem mp));
+mp, continuation+++Continuations
+Parsing an abspat yields a matching problem and a continuation of type +
+abspat_continuation, which is essentially just a list of binders and a +term (the body of the abstraction pattern).
Get the (quoted) type expected by a specific kind of abspat binder. *
-let (locate_fn_of_binder_kind binder_kind):match binder_kind with (ABKVar _) -> (`(ms_locate_var)) | ABKHyp -> (`(ms_locate_hyp)) | ABKGoal -> (`(ms_locate_unit))let arg_type_of_binder_kind binder_kind : Tac term =
+ match binder_kind with
+ | ABKVar typ -> typ
+ | ABKHyp -> `binder
+ | ABKGoal -> `unitRetrieve the function used to locate a value for a given abspat binder. *
-let ((abspat_arg_of_abspat_argspec solution_term (argspec:abspat_argspec)):(Tac term)):let loc_fn = locate_fn_of_binder_kind argspec.asa_kind in let name_tm = pack ((Tv_Const ((C_String (name_of_binder argspec.asa_name))))) in let locate_args = (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 arg_type_of_binder_kind argspec.asa_kind Q_Explicit)) (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 solution_term Q_Explicit)) (Prims.Cons ((FStar.Pervasives.Native.Mktuple2 name_tm Q_Explicit)) (Prims.Nil )))) in mk_app loc_fn locate_argsConstruct a term fetching the value of an abspat argument from a quoted matching solution solution_term. *
let ((specialize_abspat_continuation' (continuation:abspat_continuation) (solution_term:term)):(Tac term)):let (mk_arg argspec) = ((FStar.Pervasives.Native.Mktuple2 abspat_arg_of_abspat_argspec solution_term argspec Q_Explicit)) in let (argspecs, body) = continuation in mk_app body (map mk_arg argspecs)Specialize a continuation of type abspat_continuation. This constructs a fully applied version of continuation, but it requires a quoted solution to be passed in. *
let ((specialize_abspat_continuation (continuation:abspat_continuation)):(Tac term)):let solution_binder = fresh_binder ((`(matching_solution))) in let solution_term = pack ((Tv_Var (bv_of_binder solution_binder))) in let applied = specialize_abspat_continuation' continuation solution_term in let thunked = pack ((Tv_Abs solution_binder applied)) in debug (^("Specialized into ", (term_to_string thunked))); let normalized = beta_reduce thunked in debug (^("… which reduces to ", (term_to_string normalized))); thunkedSpecialize a continuation of type abspat_continuation. This yields a quoted function taking a matching solution and running its body with appropriate bindings. *
let ((interp_abspat_continuation (a:Type0) (continuation:abspat_continuation)):(Tac (Unidentified product: [matching_solution] (Tac a)))):let applied = specialize_abspat_continuation continuation in unquote #(Unidentified product: [matching_solution] (Tac a)) appliedInterpret a continuation of type abspat_continuation. This yields a function taking a matching solution and running the body of the continuation with appropriate bindings. *
let ((interp_abspat #a (abspat:a)):(Tac (*(matching_problem, abspat_continuation)))):matching_problem_of_abs (quote (abspat))let locate_fn_of_binder_kind binder_kind =
+ match binder_kind with
+ | ABKVar _ -> `ms_locate_var
+ | ABKHyp -> `ms_locate_hyp
+ | ABKGoal -> `ms_locate_unitConstruct a term fetching the value of an abspat argument from a quoted
+matching solution solution_term. *
let abspat_arg_of_abspat_argspec solution_term (argspec: abspat_argspec)
+ : Tac term =
+ let loc_fn = locate_fn_of_binder_kind argspec.asa_kind in
+ let name_tm = pack (Tv_Const (C_String (name_of_binder argspec.asa_name))) in
+ let locate_args = [(arg_type_of_binder_kind argspec.asa_kind, Q_Explicit);
+ (solution_term, Q_Explicit); (name_tm, Q_Explicit)] in
+ mk_app loc_fn locate_argsSpecialize a continuation of type abspat_continuation.
+This constructs a fully applied version of continuation, but it requires a
+quoted solution to be passed in. *
let specialize_abspat_continuation' (continuation: abspat_continuation)
+ (solution_term:term)
+ : Tac term =
+ let mk_arg argspec =
+ (abspat_arg_of_abspat_argspec solution_term argspec, Q_Explicit) in
+ let argspecs, body = continuation in
+ mk_app body (map mk_arg argspecs)Specialize a continuation of type abspat_continuation. This yields a
+quoted function taking a matching solution and running its body with appropriate
+bindings. *
let specialize_abspat_continuation (continuation: abspat_continuation)
+ : Tac term =
+ let solution_binder = fresh_binder (`matching_solution) in
+ let solution_term = pack (Tv_Var (bv_of_binder solution_binder)) in
+ let applied = specialize_abspat_continuation' continuation solution_term in
+ let thunked = pack (Tv_Abs solution_binder applied) in
+ debug ("Specialized into " ^ (term_to_string thunked));
+ let normalized = beta_reduce thunked in
+ debug ("… which reduces to " ^ (term_to_string normalized));
+ thunkedInterpret a continuation of type abspat_continuation.
+This yields a function taking a matching solution and running the body of the
+continuation with appropriate bindings. *
let interp_abspat_continuation (a:Type0) (continuation: abspat_continuation)
+ : Tac (matching_solution -> Tac a) =
+ let applied = specialize_abspat_continuation continuation in
+ unquote #(matching_solution -> Tac a) applied+++Putting it all together
+We now have all we need to use pattern-matching, short of a few convenience functions:
+
Construct a matching problem from an abspat. *
-let ((match_abspat #b #a (abspat:a) (k:Unidentified product: [abspat_continuation] (Tac (Unidentified product: [matching_solution] (Tac b))))):(Tac b)):let goal = cur_goal () in let hypotheses = binders_of_env (cur_env ()) in let (problem, continuation) = interp_abspat abspat in admit (); solve_mp #matching_solution problem hypotheses goal (k continuation)Construct an solve a matching problem. This higher-order function isn't very usable on its own — it's mostly a convenience function to avoid duplicating the problem-parsing code. *
-let ((inspect_abspat_problem #a (abspat:a)):(Tac matching_problem)):fst (interp_abspat #a abspat)let interp_abspat #a (abspat: a)
+ : Tac (matching_problem * abspat_continuation) =
+ matching_problem_of_abs (quote abspat)Construct an solve a matching problem. +This higher-order function isn't very usable on its own — it's mostly a +convenience function to avoid duplicating the problem-parsing code. *
+let match_abspat #b #a (abspat: a)
+ (k: abspat_continuation -> Tac (matching_solution -> Tac b))
+ : Tac b =
+ let goal = cur_goal () in
+ let hypotheses = binders_of_env (cur_env ()) in
+ let problem, continuation = interp_abspat abspat in
+ admit(); //NS: imprecision in the encoding of the impure result function type
+ solve_mp #matching_solution problem hypotheses goal (k continuation)Inspect the matching problem produced by parsing an abspat. *
-let ((inspect_abspat_solution #a (abspat:a)):(Tac matching_solution)):match_abspat abspat ((fun _ -> (((fun solution -> solution)) : (Tac _))))let inspect_abspat_problem #a (abspat: a) : Tac matching_problem =
+ fst (interp_abspat #a abspat)Inspect the matching solution produced by parsing and solving an abspat. *
-let ((gpm #b #a (abspat:a) ()):(Tac b)):let (continuation, solution) = match_abspat abspat tpair in interp_abspat_continuation b continuation solutionSolve a greedy pattern-matching problem and run its continuation. This if for pattern-matching problems in the Tac effect. *
let ((pm #b #a (abspat:a)):(Tac b)):match_abspat abspat (interp_abspat_continuation b)Solve a greedy pattern-matching problem and run its continuation. This if for pattern-matching problems in the Tac effect. *
let inspect_abspat_solution #a (abspat: a) : Tac matching_solution =
+ match_abspat abspat (fun _ -> (fun solution -> solution <: Tac _) <: Tac _)let tpair #a #b (x : a) : Tac (b -> Tac (a * b)) =
+ fun (y: b) -> (x, y)++Our first convenient entry point!
+This takes an abspat, parses it, computes a solution, and runs the body of +the abspat with appropriate bindings. It implements what others call ‘lazy’ +pattern-matching, so called because the success of the body of the pattern +isn't taken into account when deciding whether a particular set of matched +hypothesis should be retained. In other words, it picks the first matching +set of hypotheses, and commits to it.
+If you think that sounds like a greedy algorithm, it does. That's why it's +called ‘gpm’ below: greedy pattern-matching.
+
Solve a greedy pattern-matching problem and run its continuation.
+This if for pattern-matching problems in the Tac effect. *
let gpm #b #a (abspat: a) () : Tac b =
+ let continuation, solution = match_abspat abspat tpair in
+ interp_abspat_continuation b continuation solution++And here's the non-greedy version of the same. It's informative to compare +the implementations! This one will only find assignments that let the body +run successfuly.
+
Solve a greedy pattern-matching problem and run its continuation.
+This if for pattern-matching problems in the Tac effect. *
let pm #b #a (abspat: a) : Tac b =
+ match_abspat abspat (interp_abspat_continuation b)+++Examples
+We conclude with a small set of examples.
+
+++Simple examples
+Here's the example from the intro, which we can now run!
+
let fetch_eq_side' #a : Tac (term * term) =
+ gpm (fun (left right: a) (g: pm_goal (squash (left == right))) ->
+ (quote left, quote right)) ()TODO: GM: The following definition breaks extraction with
+FStar.Tactics.Effect.fst(20,16-20,21): (Error 76) Ill-typed application: application is FStar.Tactics.PatternMatching.fetch_eq_side' (FStar.Tactics.Types.incr_depth (FStar.Tactics.Types.set_proofstate_range +ps +(FStar.Range.prims_to_fstar_range FStar.Tactics.PatternMatching.fst(811,26-811,45)))) +remaining args are FStar.Tactics.Types.incr_depth (FStar.Tactics.Types.set_proofstate_range ps +(FStar.Range.prims_to_fstar_range FStar.Tactics.PatternMatching.fst(811,26-811,45))) +ml type of head is (FStar_Reflection_Types.term * FStar_Reflection_Types.term)
+let _ =
+assert_by_tactic (1 + 1 == 2)
+(fun () -> let l, r = fetch_eq_side' #int in
+print (term_to_string l ^ " / " ^ term_to_string r))
+let _ =
+assert_by_tactic (1 + 1 == 2)
+(gpm (fun (left right: int) (g: pm_goal (squash (left == right))) ->
+let l, r = quote left, quote right in
+print (term_to_string l ^ " / " ^ term_to_string r) <: Tac unit))
+++Commenting out the following example and comparing
+[pm](#pm)and[gpm](#gpm)can be +instructive:
let test_bt (a: Type0) (b: Type0) (c: Type0) (d: Type0) = +assert_by_tactic ((a ==> d) ==> (b ==> d) ==> (c ==> d) ==> a ==> d) +(fun () -> repeat' implies_intro'; +gpm (fun (a b: Type0) (h: hyp (a ==> b)) -> +print (binder_to_string h); +fail "fail here" <: Tac unit); +qed ())
++++A real-life example
+The following tactics combines mutliple simple building blocks to solve a +goal. Each use of
+lpmrecognizes a specific pattern; and each tactic is +tried in succession, until one succeeds. The whole process is repeated as +long as at least one tactic succeeds.
let example (#a:Type0) (#b:Type0) (#c:Type0) :unit =
+ assert_by_tactic (a /\ b ==> c == b ==> c)
+ (fun () -> repeat' (fun () ->
+ gpm #unit (fun (a: Type) (h: hyp (squash a)) ->
+ clear h <: Tac unit) `or_else`
+ (fun () -> gpm #unit (fun (a b: Type0) (g: pm_goal (squash (a ==> b))) ->
+ implies_intro' () <: Tac unit) `or_else`
+ (fun () -> gpm #unit (fun (a b: Type0) (h: hyp (a /\ b)) ->
+ and_elim' h <: Tac unit) `or_else`
+ (fun () -> gpm #unit (fun (a b: Type0) (h: hyp (a == b)) (g: pm_goal (squash a)) ->
+ rewrite h <: Tac unit) `or_else`
+ (fun () -> gpm #unit (fun (a: Type0) (h: hyp a) (g: pm_goal (squash a)) ->
+ exact_hyp a h <: Tac unit) ())))));
+ qed ())++ diff --git a/docs/FStar.Tactics.Print.html b/docs/FStar.Tactics.Print.html new file mode 100644 index 0000000..31bfd06 --- /dev/null +++ b/docs/FStar.Tactics.Print.html @@ -0,0 +1,89 @@ + + + + ++Possible extensions
+The following tasks would make for interesting extensions of this +experiment:
++
+- Handling multiple goal patterns (easy)
+- Extending the matching language (match under binders?)
+- Introducing specialized syntax
+- Thinking about a sound way of supporting ‘match-anything’ patterns in +abspat notations
+- Using the normalizer to partially-evaluated pattern-matching tactics
+- Migrating to a compile-time version of
+quote+
private
+let paren (s:string) : string = "(" ^ s ^ ")"TODO: making this a local definition in print_list fails to extract.
+private
+let rec print_list_aux (f:'a -> Tac string) (xs:list 'a) : Tac string =
+ match xs with
+ | [] -> ""
+ | [x] -> f x
+ | x::xs -> f x ^ "; " ^ print_list_aux f xsprivate
+let print_list (f:'a -> Tac string) (l:list 'a) : Tac string =
+ "[" ^ print_list_aux f l ^ "]"let rec term_to_ast_string (t:term) : Tac string =
+ match inspect t with
+ | Tv_Var bv -> "Tv_Var " ^ bv_to_string bv
+ | Tv_BVar bv -> "Tv_BVar " ^ bv_to_string bv
+ | Tv_FVar fv -> "Tv_FVar " ^ fv_to_string fv
+ | Tv_App hd (a, _) -> "Tv_App " ^ paren (term_to_ast_string hd ^ ", " ^ term_to_ast_string a)
+ | Tv_Abs x e -> "Tv_Abs " ^ paren (binder_to_string x ^ ", " ^ term_to_ast_string e)
+ | Tv_Arrow x c -> "Tv_Arrow " ^ paren (binder_to_string x ^ ", " ^ comp_to_ast_string c)
+ | Tv_Type _ -> "Type"
+ | Tv_Refine x e -> "Tv_Refine " ^ paren (bv_to_string x ^ ", " ^ term_to_ast_string e)
+ | Tv_Const c -> const_to_ast_string c
+ | Tv_Uvar i _ -> "Tv_Uvar " ^ string_of_int i
+ | Tv_Let recf _ x e1 e2 ->
+ "Tv_Let " ^ paren (string_of_bool recf ^ ", " ^
+ bv_to_string x ^ ", " ^
+ term_to_ast_string e1 ^ ", " ^
+ term_to_ast_string e2)
+ | Tv_Match e ret_opt brs ->
+ let tacopt_to_string tacopt : Tac string =
+ match tacopt with
+ | None -> ""
+ | Some tac -> " by " ^ (term_to_ast_string tac) in
+ "Tv_Match " ^
+ paren (
+ term_to_ast_string e ^
+ ", " ^
+ (match ret_opt with
+ | None -> "None"
+ | Some (Inl t, tacopt) -> (term_to_ast_string t) ^ (tacopt_to_string tacopt)
+ | Some (Inr c, tacopt) -> (comp_to_ast_string c) ^ (tacopt_to_string tacopt)) ^
+ ", " ^
+ branches_to_ast_string brs)
+ | Tv_AscribedT e t _ -> "Tv_AscribedT " ^ paren (term_to_ast_string e ^ ", " ^ term_to_ast_string t)
+ | Tv_AscribedC e c _ -> "Tv_AscribedC " ^ paren (term_to_ast_string e ^ ", " ^ comp_to_ast_string c)
+ | Tv_Unknown -> "_"and branches_to_ast_string (brs:list branch) : Tac string =
+ print_list branch_to_ast_string brsand branch_to_ast_string (b:branch) : Tac string =
+ let p, e = b in
+ paren ("_pat, " ^ term_to_ast_string e)and comp_to_ast_string (c:comp) : Tac string =
+ match inspect_comp c with
+ | C_Total t _ -> "Tot " ^ term_to_ast_string t
+ | C_GTotal t _ -> "GTot " ^ term_to_ast_string t
+ | C_Lemma pre post _ -> "Lemma " ^ term_to_ast_string pre ^ " " ^ term_to_ast_string post
+ | C_Eff _ eff res _ -> "Effect " ^ paren (implode_qn eff ^ ", " ^ term_to_ast_string res)and const_to_ast_string (c:vconst) : Tac string =
+ match c with
+ | C_Unit -> "C_Unit"
+ | C_Int i -> "C_Int " ^ string_of_int i
+ | C_True -> "C_True"
+ | C_False -> "C_False"
+ | C_String s -> "C_String " ^ s
+ | C_Range _ -> "C_Range _"
+ | C_Reify -> "C_Reify"
+ | C_Reflect name -> "C_Reflect " ^ implode_qn namefsdoc: no-summary-found
-fsdoc: no-comment-found
+This file is never extracted. It's a copy of the one with the same name in +the compiler. It lives here so that one doesn't need to adjust their load +path to use tactics from ulib.
+noeq type __result a =
+ | Success : v:a -> ps:proofstate -> __result a
+ | Failed : exn:exn (* Error *)
+ -> ps:proofstate (* The proofstate at time of failure *)
+ -> __result aA bit of help for the SMT, unsure if still needed
+let result_split #a (r:__result a)
+ : Lemma (Success? r \/ Failed? r)
+ [SMTPat (Success? r); SMTPat (Failed? r)]
+ = ()fsdoc: no-summary-found
-fsdoc: no-comment-found
+A correct-by-construction logical simplifier +*
+norm simpl``, that's cheating!val lem_iff_refl : #a:Type -> Lemma (a <==> a)
+let lem_iff_refl #a = ()val lem_iff_trans : #a:Type -> #b:Type -> #c:Type -> squash (a <==> b) -> squash (b <==> c)
+ -> Lemma (a <==> c)
+let lem_iff_trans #a #b #c _ _ = ()let tiff () : Tac unit =
+ apply_lemma (`lem_iff_refl)let step () : Tac unit =
+ apply_lemma (`lem_iff_trans)val lem_true_and_p : #p:Type -> Lemma ((True /\ p) <==> p)
+let lem_true_and_p #p = ()val lem_p_and_true : #p:Type -> Lemma ((p /\ True) <==> p)
+let lem_p_and_true #p = ()val lem_false_and_p : #p:Type -> Lemma ((False /\ p) <==> False)
+let lem_false_and_p #p = ()val lem_p_and_false : #p:Type -> Lemma ((p /\ False) <==> False)
+let lem_p_and_false #p = ()val lem_true_or_p : #p:Type -> Lemma ((True \/ p) <==> True)
+let lem_true_or_p #p = ()val lem_p_or_true : #p:Type -> Lemma ((p \/ True) <==> True)
+let lem_p_or_true #p = ()val lem_false_or_p : #p:Type -> Lemma ((False \/ p) <==> p)
+let lem_false_or_p #p = ()val lem_p_or_false : #p:Type -> Lemma ((p \/ False) <==> p)
+let lem_p_or_false #p = ()val lem_true_imp_p : #p:Type -> Lemma ((True ==> p) <==> p)
+let lem_true_imp_p #p = ()val lem_p_imp_true : #p:Type -> Lemma ((p ==> True) <==> True)
+let lem_p_imp_true #p = ()val lem_false_imp_p : #p:Type -> Lemma ((False ==> p) <==> True)
+let lem_false_imp_p #p = ()val lem_fa_true : #a:Type -> Lemma ((forall (x:a). True) <==> True)
+let lem_fa_true #a = ()val lem_fa_false : #a:Type -> (x:a) -> Lemma ((forall (x:a). False) <==> False)
+let lem_fa_false #a x = ()val lem_ex_false : #a:Type -> Lemma ((exists (x:a). False) <==> False)
+let lem_ex_false #a = ()val lem_ex_true : #a:Type -> (x:a) -> Lemma ((exists (x:a). True) <==> True)
+let lem_ex_true #a x = ()val lem_neg_false : unit -> Lemma (~False <==> True)
+let lem_neg_false () = ()val lem_neg_true : unit -> Lemma (~True <==> False)
+let lem_neg_true () = ()val lem_true_iff_p : #p:Type -> Lemma ((True <==> p) <==> p)
+let lem_true_iff_p #p = ()val lem_false_iff_p : #p:Type -> Lemma ((False <==> p) <==> ~p)
+let lem_false_iff_p #p = ()val lem_p_iff_true : #p:Type -> Lemma ((p <==> True) <==> p)
+let lem_p_iff_true #p = ()val lem_p_iff_false : #p:Type -> Lemma ((p <==> False) <==> ~p)
+let lem_p_iff_false #p = ()val and_cong (#p #q #p' #q' : Type) : squash (p <==> p') ->
+ squash (q <==> q') ->
+ Lemma ((p /\ q) <==> (p' /\ q'))
+let and_cong #p #q #p' #q' _ _ = ()val or_cong (#p #q #p' #q' : Type) : squash (p <==> p') ->
+ squash (q <==> q') ->
+ Lemma ((p \/ q) <==> (p' \/ q'))
+let or_cong #p #q #p' #q' _ _ = ()val imp_cong (#p #q #p' #q' : Type) : squash (p <==> p') ->
+ squash (q <==> q') ->
+ Lemma ((p ==> q) <==> (p' ==> q'))
+let imp_cong #p #q #p' #q' _ _ = ()val fa_cong (#a : Type) (#p #q : a -> Type) :
+ (x:a -> squash (p x <==> q x)) ->
+ Lemma ((forall (x:a). p x) <==> (forall (x:a). q x))
+let fa_cong #a #p #q f = admit() //fix, this should certainly be provableval ex_cong (#a : Type) (#p #q : a -> Type) :
+ (x:a -> squash (p x <==> q x)) ->
+ Lemma ((exists (x:a). p x) <==> (exists (x:a). q x))
+let ex_cong #a #p #q f = admit() //fix, this should certainly be provableval neg_cong (#p #q:Type) : squash (p <==> q) -> Lemma (~p <==> ~q)
+let neg_cong #p #q _ = ()val iff_cong (#p #p' #q #q' : Type) : squash (p <==> p') -> squash (q <==> q') -> Lemma ((p <==> q) <==> (p' <==> q'))
+let iff_cong #p #p' #q #q' _ _ = ()Absolutely hideous, do something about normalization
+val is_true : term -> Tac bool
+let is_true t =
+ begin match term_as_formula' t with
+ | True_ -> true
+ | _ -> begin match inspect_ln t with
+ | Tv_App l r ->
+ begin match inspect_ln l with
+ | Tv_Abs b t ->
+ begin match term_as_formula' t with
+ | True_ -> true
+ | _ -> false
+ end
+ | _ -> false
+ end
+ | _ -> false
+ end
+ endval is_false : term -> Tac bool
+let is_false t =
+ begin match term_as_formula' t with
+ | False_ -> true
+ | _ -> begin match inspect_ln t with
+ | Tv_App l r ->
+ begin match inspect_ln l with
+ | Tv_Abs b t ->
+ begin match term_as_formula' t with
+ | False_ -> true
+ | _ -> false
+ end
+ | _ -> false
+ end
+ | _ -> false
+ end
+ endval inhabit : unit -> Tac unit
+let inhabit () =
+ let t = cur_goal () in
+ match inspect_ln t with
+ | Tv_FVar fv ->
+ let qn = inspect_fv fv in
+ if qn = int_lid then exact (`42)
+ else if qn = bool_lid then exact (`true)
+ else if qn = unit_lid then exact (`())
+ else fail ""
+ | _ -> fail ""val simplify_point : unit -> Tac unit
+val recurse : unit -> Tac unitlet rec simplify_point () =dump "1 ALIVE";
+recurse ();
+norm [];
+let g = cur_goal () in
+let f = term_as_formula g inprint ("1 g = " ^ term_to_string g);
+print ("1 f = " ^ formula_to_string f);
+match f with
+| Iff l r ->
+ begin match term_as_formula' l with
+ | And p q ->
+ if is_true p then apply_lemma (`lem_true_and_p)
+ else if is_true q then apply_lemma (`lem_p_and_true)
+ else if is_false p then apply_lemma (`lem_false_and_p)
+ else if is_false q then apply_lemma (`lem_p_and_false)
+ else tiff ()| Or p q ->
+ if is_true p then apply_lemma (`lem_true_or_p)
+ else if is_true q then apply_lemma (`lem_p_or_true)
+ else if is_false p then apply_lemma (`lem_false_or_p)
+ else if is_false q then apply_lemma (`lem_p_or_false)
+ else tiff ()| Implies p q ->
+ if is_true p then apply_lemma (`lem_true_imp_p)
+ else if is_true q then apply_lemma (`lem_p_imp_true)
+ else if is_false p then apply_lemma (`lem_false_imp_p)
+ else tiff ()| Forall b p ->
+ if is_true p then apply_lemma (`lem_fa_true)
+ else if is_false p then or_else (fun () -> apply_lemma (`lem_fa_false); inhabit ()) tiff
+ else tiff ()| Exists b p ->
+ if is_false p then apply_lemma (`lem_ex_false)
+ else if is_true p then or_else (fun () -> apply_lemma (`lem_ex_true); inhabit ()) tiff
+ else tiff ()| Not p ->
+ if is_true p then apply_lemma (`lem_neg_true)
+ else if is_false p then apply_lemma (`lem_neg_false)
+ else tiff ()| Iff p q ->After applying the lemma, we might still have more simpl to do, +so add an intermediate step.
+step ();
+ if is_true p then apply_lemma (`lem_true_iff_p)
+else if is_true q then apply_lemma (`lem_p_iff_true)
+else if is_false p then apply_lemma (`lem_false_iff_p)
+else if is_false q then apply_lemma (`lem_p_iff_false)
+else tiff ();
+simplify_point () | _ -> tiff ()
+ end
+| _ -> fail "simplify_point: failed precondition: goal should be `g <==> ?u`"and recurse () : Tac unit =dump "2 ALIVE";
+step ();
+norm [];
+let g = cur_goal () in
+let f = term_as_formula g inprint ("2 g = " ^ term_to_string g);
+print ("2 f = " ^ formula_to_string f);
+match f with
+| Iff l r ->
+ begin match term_as_formula' l with
+ | And _ _ ->
+ seq (fun () -> apply_lemma (`and_cong)) simplify_point| Or _ _ ->
+ seq (fun () -> apply_lemma (`or_cong)) simplify_point| Implies _ _ ->
+ seq (fun () -> apply_lemma (`imp_cong)) simplify_point| Forall _ _ ->
+ apply_lemma (`fa_cong);
+ let _ = intro () in
+ simplify_point ()| Exists _ _ ->
+ apply_lemma (`ex_cong);
+ let _ = intro () in
+ simplify_point ()| Not _ ->
+ apply_lemma (`neg_cong);
+ simplify_point ()| Iff _ _ ->
+ seq (fun () -> apply_lemma (`iff_cong)) simplify_point | _ -> tiff ()
+ end
+| _ -> fail "recurse: failed precondition: goal should be `g <==> ?u`"val equiv : #p:Type -> #q:Type -> squash (p <==> q) -> squash q -> Lemma p
+let equiv #p #q _ _ = ()let simplify () : Tac unit =
+ apply_lemma (`equiv);
+ simplify_point ()fsdoc: no-summary-found
-fsdoc: no-comment-found
+These are fully-named variants of functions found in FStar.Reflection
+private
+let rec collect_arr' (bs : list binder) (c : comp) : Tac (list binder * comp) =
+ begin match inspect_comp c with
+ | C_Total t _ ->
+ begin match inspect t with
+ | Tv_Arrow b c ->
+ collect_arr' (b::bs) c
+ | _ ->
+ (bs, c)
+ end
+ | _ -> (bs, c)
+ endval collect_arr_bs : typ -> Tac (list binder * comp)
+let collect_arr_bs t =
+ let (bs, c) = collect_arr' [] (pack_comp (C_Total t [])) in
+ (List.Tot.Base.rev bs, c)val collect_arr : typ -> Tac (list typ * comp)
+let collect_arr t =
+ let (bs, c) = collect_arr' [] (pack_comp (C_Total t [])) in
+ let ts = List.Tot.Base.map type_of_binder bs in
+ (List.Tot.Base.rev ts, c)private
+let rec collect_abs' (bs : list binder) (t : term) : Tac (list binder * term) (decreases t) =
+ match inspect t with
+ | Tv_Abs b t' ->
+ collect_abs' (b::bs) t'
+ | _ -> (bs, t)val collect_abs : term -> Tac (list binder * term)
+let collect_abs t =
+ let (bs, t') = collect_abs' [] t in
+ (List.Tot.Base.rev bs, t')Copied from FStar.Tactics.Derived
+private
+let fail (#a:Type) (m:string) = raise #a (TacticFailure m)let rec mk_arr (bs: list binder) (cod : comp) : Tac term =
+ match bs with
+ | [] -> fail "mk_arr, empty binders"
+ | [b] -> pack (Tv_Arrow b cod)
+ | (b::bs) -> pack (Tv_Arrow b (pack_comp (C_Total (mk_arr bs cod) [])))let rec mk_tot_arr (bs: list binder) (cod : term) : Tac term =
+ match bs with
+ | [] -> cod
+ | (b::bs) -> pack (Tv_Arrow b (pack_comp (C_Total (mk_tot_arr bs cod) [])))let lookup_lb_view (lbs:list letbinding) (nm:name) : Tac lb_view =
+ let o = FStar.List.Tot.Base.find
+ (fun lb ->
+ let lbv = inspect_lb lb in
+ (inspect_fv lbv.lb_fv) = nm)
+ lbs
+ in
+ match o with
+ | Some lb -> inspect_lb lb
+ | None -> fail "lookup_lb_view: Name not in let group"fsdoc: no-summary-found
-fsdoc: no-comment-found
-** Generating methods from a class ***T
+TODO: This must be in the FStar.Tactics.* namespace or we fail to buildThe attribute that marks instances
+irreducible
+let tcinstance : unit = ()let rec first (f : 'a -> Tac 'b) (l : list 'a) : Tac 'b =
+ match l with
+ | [] -> fail "no cands"
+ | x::xs -> (fun () -> f x) `or_else` (fun () -> first f xs)TODO: memoization?. And better errors.
+private
+let rec tcresolve' (seen:list term) (fuel:int) : Tac unit =
+ if fuel <= 0 then
+ fail "out of fuel";
+ debug ("fuel = " ^ string_of_int fuel);
+ let g = cur_goal () in
+ if FStar.List.Tot.Base.existsb (term_eq g) seen then
+ fail "loop";
+ let seen = g :: seen in
+ local seen fuel `or_else` (fun () -> global seen fuel `or_else` (fun () -> fail ("could not solve constraint: " ^ term_to_string g)))
+and local (seen:list term) (fuel:int) () : Tac unit =
+ let bs = binders_of_env (cur_env ()) in
+ first (fun b -> trywith seen fuel (pack (Tv_Var (bv_of_binder b)))) bs
+and global (seen:list term) (fuel:int) () : Tac unit =
+ let cands = lookup_attr (`tcinstance) (cur_env ()) in
+ first (fun fv -> trywith seen fuel (pack (Tv_FVar fv))) cands
+and trywith (seen:list term) (fuel:int) (t:term) : Tac unit =
+ debug ("Trying to apply hypothesis/instance: " ^ term_to_string t);
+ (fun () -> apply_noinst t) `seq` (fun () -> tcresolve' seen (fuel-1))[@@plugin]
+let tcresolve () : Tac unit =
+ try tcresolve' [] 16
+ with
+ | TacticFailure s -> fail ("Typeclass resolution failed: " ^ s)
+ | e -> raise eSolve an explicit argument by typeclass resolution
+unfold let solve (#a:Type) (#[tcresolve ()] ev : a) : Tot a = evIn TAC, not Tot
+let rec mk_abs (bs : list binder) (body : term) : Tac term (decreases bs) =
+ match bs with
+ | [] -> body
+ | b::bs -> pack (Tv_Abs b (mk_abs bs body))let rec last (l : list 'a) : Tac 'a =
+ match l with
+ | [] -> fail "last: empty list"
+ | [x] -> x
+ | _::xs -> last xs[@@plugin]
+let mk_class (nm:string) : Tac decls =
+ let ns = explode_qn nm in
+ let r = lookup_typ (top_env ()) ns in
+ guard (Some? r);
+ let Some se = r in
+ let to_propagate = List.Tot.filter (function Inline_for_extraction | NoExtract -> true | _ -> false) (sigelt_quals se) in
+ let sv = inspect_sigelt se in
+ guard (Sg_Inductive? sv);
+ let Sg_Inductive name us params ty ctors = sv indump ("got it, name = " ^ implode_qn name);
+dump ("got it, ty = " ^ term_to_string ty);
+let ctor_name = last name inMust have a single constructor +dump ("got ctor " ^ implode_qn c_name ^ " of type " ^ term_to_string ty);
+guard (List.Tot.Base.length ctors = 1);
+let [(c_name, ty)] = ctors inlet bs, cod = collect_arr_bs ty in
+let r = inspect_comp cod in
+guard (C_Total? r);
+let C_Total cod _ = r in (* must be total *)print ("n_univs = " ^ string_of_int (List.Tot.Base.length us));
+let base : string = "__proj__Mk" ^ ctor_name ^ "__item__" inMake a sigelt for each method
+T.map (fun b ->dump ("b = " ^ term_to_string (type_of_binder b));
+let s = name_of_binder b indump ("b = " ^ s);
+let ns = cur_module () in
+let sfv = pack_fv (ns @ [s]) in
+let dbv = fresh_bv_named "d" cod in
+let tcr = (`tcresolve) in
+let tcdict = pack_binder dbv (Q_Meta tcr) [] in
+let proj_name = cur_module () @ [base ^ s] in
+let proj = pack (Tv_FVar (pack_fv proj_name)) inlet proj_ty =
+ match lookup_typ (top_env ()) proj_name with
+ | None -> fail "mk_class: proj not found?"
+ | Some se ->
+ match inspect_sigelt se with
+ | Sg_Let _ lbs -> begin
+ let ({lb_fv=_;lb_us=_;lb_typ=typ;lb_def=_}) =
+ lookup_lb_view lbs proj_name in typ
+ end
+ | _ -> fail "mk_class: proj not Sg_Let?"
+indump ("proj_ty = " ^ term_to_string proj_ty);
+let ty =
+ let bs, cod = collect_arr_bs proj_ty in
+ let ps, bs = List.Tot.Base.splitAt (List.Tot.Base.length params) bs in
+ match bs with
+ | [] -> fail "mk_class: impossible, no binders"
+ | b1::bs' ->
+ let (bv, aq) = inspect_binder b1 in
+ let b1 = pack_binder bv (Q_Meta tcr) [] in
+ mk_arr (ps@(b1::bs')) cod
+inlet def : term =
+ let bs = (map (fun b -> binder_set_qual Q_Implicit b) params)
+ @ [tcdict] in
+ mk_abs bs (mk_e_app proj [binder_to_term tcdict])
+indump ("def = " ^ term_to_string def); +dump ("ty = " ^ term_to_string ty);
+let ty : term = ty in
+let def : term = def in
+let sfv : fv = sfv inlet lbv = {lb_fv=sfv;lb_us=us;lb_typ=ty;lb_def=def} in
+let lb = pack_lb lbv in
+let se = pack_sigelt (Sg_Let false [lb]) in
+let se = set_sigelt_quals to_propagate se in
+let _, (_, attrs) = inspect_binder b in
+let se = set_sigelt_attrs attrs se indump ("trying to return : " ^ term_to_string (quote se));
+ se
+) bsfsdoc: no-summary-found
-fsdoc: no-comment-found
+assume new type proofstate
+assume new type goalReturns the active goals
+val goals_of : proofstate -> list goalReturns the goals marked for SMT
+val smt_goals_of : proofstate -> list goalInspecting a goal
+val goal_env : goal -> env
+val goal_type : goal -> typ
+val goal_witness : goal -> term
+val is_guard : goal -> bool (* A bit of helper info: did this goal come from a VC guard? *)val get_label : goal -> string
+val set_label : string -> goal -> goalTracing
+val incr_depth : proofstate -> proofstate
+val decr_depth : proofstate -> proofstatetracepoint always returns true. We do not use unit to prevent
+erasure.
val tracepoint : proofstate -> b:bool{b == true}
+val set_proofstate_range : proofstate -> FStar.Range.range -> proofstatetype direction =
+ | TopDown
+ | BottomUptype ctrl_flag =
+ | Continue
+ | Skip
+ | Aborttype guard_policy =
+ | SMT
+ | Goal
+ | Force
+ | Drop // unsound! careful!fsdoc: no-summary-found
-fsdoc: no-comment-found
+#set-options "--z3rlimit 25 --fuel 0 --ifuel 0"Tac list functions, since there's no effect polymorphism
+val map: ('a -> Tac 'b) -> list 'a -> Tac (list 'b)
+#push-options "--ifuel 1"
+let rec map f x = match x with
+ | [] -> []
+ | a::tl -> f a::map f tl
+#pop-optionsval __mapi: nat -> (nat -> 'a -> Tac 'b) -> list 'a -> Tac (list 'b)
+#push-options "--ifuel 1"
+let rec __mapi i f x = match x with
+ | [] -> []
+ | a::tl -> f i a::__mapi (i+1) f tl
+#pop-optionsval mapi: (nat -> 'a -> Tac 'b) -> list 'a -> Tac (list 'b)
+let mapi f l = __mapi 0 f lval iter : ('a -> Tac unit) -> list 'a -> Tac unit
+#push-options "--ifuel 1"
+let rec iter f x = match x with
+ | [] -> ()
+ | a::tl -> f a; iter f tl
+#pop-optionsval iteri_aux: int -> (int -> 'a -> Tac unit) -> list 'a -> Tac unit
+#push-options "--ifuel 1"
+let rec iteri_aux i f x = match x with
+ | [] -> ()
+ | a::tl -> f i a; iteri_aux (i+1) f tl
+#pop-optionsval iteri: (int -> 'a -> Tac unit) -> list 'a -> Tac unit
+let iteri f x = iteri_aux 0 f xval fold_left: ('a -> 'b -> Tac 'a) -> 'a -> l:list 'b -> Tac 'a
+#push-options "--ifuel 1"
+let rec fold_left f x l = match l with
+ | [] -> x
+ | hd::tl -> fold_left f (f x hd) tl
+#pop-optionsval fold_right: ('a -> 'b -> Tac 'b) -> list 'a -> 'b -> Tac 'b
+#push-options "--ifuel 1"
+let rec fold_right f l x = match l with
+ | [] -> x
+ | hd::tl -> f hd (fold_right f tl x)
+#pop-optionsThere's no unconditionally total zip like this in Tot.Base, why? Anyway use this
+val zip : (#a:Type) -> (#b:Type) -> list a -> list b -> Tac (list (a * b))
+#push-options "--ifuel 1"
+let rec zip #a #b l1 l2 = match l1, l2 with
+ | x::xs, y::ys -> (x,y) :: (zip xs ys)
+ | _ -> []
+#pop-optionsval filter: ('a -> Tac bool) -> list 'a -> Tac (list 'a)
+#push-options "--ifuel 1"
+let rec filter f = function
+ | [] -> []
+ | hd::tl -> if f hd then hd::(filter f tl) else filter f tl
+#pop-options#push-options "--ifuel 1"
+private let rec filter_map_acc (f:'a -> Tac (option 'b)) (acc:list 'b) (l:list 'a)
+ : Tac (list 'b) =
+ match l with
+ | [] ->
+ rev acc
+ | hd :: tl ->
+ match f hd with
+ | Some hd ->
+ filter_map_acc f (hd :: acc) tl
+ | None ->
+ filter_map_acc f acc tl
+#pop-optionslet filter_map (f:'a -> Tac (option 'b)) (l:list 'a) : Tac (list 'b) =
+ filter_map_acc f [] lval tryPick: ('a -> Tac (option 'b)) -> list 'a -> Tac (option 'b)
+#push-options "--ifuel 1"
+let rec tryPick f l = match l with
+ | [] -> None
+ | hd::tl ->
+ match f hd with
+ | Some x -> Some x
+ | None -> tryPick f tl
+#pop-optionslet map_opt (f:'a -> Tac 'b) (x:option 'a) : Tac (option 'b) =
+ match x with
+ | None -> None
+ | Some x -> Some (f x)fsdoc: no-summary-found
-fsdoc: no-comment-found
+I don't expect many uses of tactics without syntax handling
+Includes module FStar.Reflection.Types
+Includes module FStar.Reflection.Data
+Includes module FStar.Reflection.Builtins
+Includes module FStar.Reflection.Derived
+Includes module FStar.Reflection.Formula
+Includes module FStar.Reflection.Const
+Includes module FStar.Tactics.Types
+Includes module FStar.Tactics.Effect
+Includes module FStar.Tactics.Builtins
+Includes module FStar.Tactics.Derived
+Includes module FStar.Tactics.Logic
+Includes module FStar.Tactics.Util
+Includes module FStar.Tactics.SyntaxHelpers
+Includes module FStar.Tactics.Print
+fsdoc: no-summary-found
-fsdoc: no-comment-found
+new val networkStream: eqtype
+new val tcpListener: Type0val set_nonblock: networkStream -> unit
+val clear_nonblock: networkStream -> unitServer side
+val listen: string -> nat -> EXT tcpListener
+val acceptTimeout: nat -> tcpListener -> EXT networkStream
+val accept: tcpListener -> EXT networkStream
+val stop: tcpListener -> EXT unitClient side
+val connectTimeout: nat -> string -> nat -> EXT networkStream
+val connect: string -> nat -> EXT networkStreamInput/Output
+adding support for (potentially) non-blocking I/O +NB for now, send fails on partial writes, and loops on EAGAIN/EWOULDBLOCK.
+type recv_result (max:nat) =
+ | RecvWouldBlock
+ | RecvError of string
+ | Received of b:bytes {length b <= max}
+val recv_async: networkStream -> max:nat -> EXT (recv_result max)val recv: networkStream -> max:nat -> EXT (optResult string (b:bytes {length b <= max}))
+val send: networkStream -> bytes -> EXT (optResult string unit)
+val close: networkStream -> EXT unitCreate a network stream from a given stream. +Only used by the application interface TLSharp. +assume val create: System.IO.Stream -> NetworkStream
+ diff --git a/docs/FStar.UInt.html b/docs/FStar.UInt.html index 1f20390..c0f0014 100644 --- a/docs/FStar.UInt.html +++ b/docs/FStar.UInt.html @@ -1,59 +1,471 @@ - - + + - - -fsdoc: no-summary-found
-fsdoc: no-comment-found
-val to_vec_mod_pow2:Unidentified product: [#n:nat] Unidentified product: [a:uint_t n] Unidentified product: [m:pos] Unidentified product: [i:i:nat:{/\(<=(-(n, m), i), <(i, n))}] (Lemma ((requires (==(%(a, pow2 m), 0)))) ((ensures (==(index (to_vec a) i, false)))) (Prims.Cons (SMTPat (index (to_vec #n a) i)) (Prims.Cons (SMTPat (==(%(a, pow2 m), 0))) (Prims.Nil ))))Used in the next two lemmas
-val to_vec_lt_pow2:Unidentified product: [#n:nat] Unidentified product: [a:uint_t n] Unidentified product: [m:nat] Unidentified product: [i:i:nat:{<(i, -(n, m))}] (Lemma ((requires (<(a, pow2 m)))) ((ensures (==(index (to_vec a) i, false)))) (Prims.Cons (SMTPat (index (to_vec #n a) i)) (Prims.Cons (SMTPat (<(a, pow2 m))) (Prims.Nil ))))Used in the next two lemmas
-pragmaNOTE: anything that you fix/update here should be reflected in FStar.Int.fsti, which is mostly
a copy-paste of this module.
+Opens module FStar.Mul
+Opens module FStar.BitVector
+Opens module FStar.Math.Lemmas
+val pow2_values: x:nat -> Lemma
+ (let p = pow2 x in
+ match x with
+ | 0 -> p=1
+ | 1 -> p=2
+ | 8 -> p=256
+ | 16 -> p=65536
+ | 31 -> p=2147483648
+ | 32 -> p=4294967296
+ | 63 -> p=9223372036854775808
+ | 64 -> p=18446744073709551616
+ | 128 -> p=0x100000000000000000000000000000000
+ | _ -> True)
+ [SMTPat (pow2 x)]++Specs
+Note: lacking any type of functors for F*, this is a copy/paste of
+FStar.Int.fst, where the relevant bits that changed are:+
+- definition of max and min
+- use of regular integer modulus instead of wrap-around modulus
+
let max_int (n:nat) : Tot int = pow2 n - 1
+let min_int (n:nat) : Tot int = 0let fits (x:int) (n:nat) : Tot bool = min_int n <= x && x <= max_int n
+let size (x:int) (n:nat) : Tot Type0 = b2t(fits x n)Machine integer type
+type uint_t (n:nat) = x:int{size x n}++Constants
+
let zero (n:nat) : Tot (uint_t n) = 0let pow2_n (#n:pos) (p:nat{p < n}) : Tot (uint_t n) =
+ pow2_le_compat (n - 1) p; pow2 plet one (n:pos) : Tot (uint_t n) = 1let ones (n:nat) : Tot (uint_t n) = max_int nIncrement and decrement
+let incr (#n:nat) (a:uint_t n) : Pure (uint_t n)
+ (requires (b2t (a < max_int n))) (ensures (fun _ -> True))
+ = a + 1let decr (#n:nat) (a:uint_t n) : Pure (uint_t n)
+ (requires (b2t (a > min_int n))) (ensures (fun _ -> True))
+ = a - 1val incr_underspec: #n:nat -> a:uint_t n -> Pure (uint_t n)
+ (requires (b2t (a < max_int n)))
+ (ensures (fun b -> a + 1 = b))val decr_underspec: #n:nat -> a:uint_t n -> Pure (uint_t n)
+ (requires (b2t (a > min_int n)))
+ (ensures (fun b -> a - 1 = b))let incr_mod (#n:nat) (a:uint_t n) : Tot (uint_t n) = (a + 1) % (pow2 n)let decr_mod (#n:nat) (a:uint_t n) : Tot (uint_t n) = (a - 1) % (pow2 n)Addition primitives
+let add (#n:nat) (a:uint_t n) (b:uint_t n) : Pure (uint_t n)
+ (requires (size (a + b) n))
+ (ensures (fun _ -> True))
+ = a + bval add_underspec: #n:nat -> a:uint_t n -> b:uint_t n -> Pure (uint_t n)
+ (requires True)
+ (ensures (fun c ->
+ size (a + b) n ==> a + b = c))let add_mod (#n:nat) (a:uint_t n) (b:uint_t n) : Tot (uint_t n) =
+ (a + b) % (pow2 n)Subtraction primitives
+let sub (#n:nat) (a:uint_t n) (b:uint_t n) : Pure (uint_t n)
+ (requires (size (a - b) n))
+ (ensures (fun _ -> True))
+ = a - bval sub_underspec: #n:nat -> a:uint_t n -> b:uint_t n -> Pure (uint_t n)
+ (requires True)
+ (ensures (fun c ->
+ size (a - b) n ==> a - b = c))let sub_mod (#n:nat) (a:uint_t n) (b:uint_t n) : Tot (uint_t n) =
+ (a - b) % (pow2 n)Multiplication primitives
+let mul (#n:nat) (a:uint_t n) (b:uint_t n) : Pure (uint_t n)
+ (requires (size (a * b) n))
+ (ensures (fun _ -> True))
+ = a * bval mul_underspec: #n:nat -> a:uint_t n -> b:uint_t n -> Pure (uint_t n)
+ (requires True)
+ (ensures (fun c ->
+ size (a * b) n ==> a * b = c))let mul_mod (#n:nat) (a:uint_t n) (b:uint_t n) : Tot (uint_t n) =
+ (a * b) % (pow2 n)private
+val lt_square_div_lt (a:nat) (b:pos) : Lemma
+ (requires (a < b * b))
+ (ensures (a / b < b))#push-options "--fuel 0 --ifuel 0"
+let mul_div (#n:nat) (a:uint_t n) (b:uint_t n) : Tot (uint_t n) =
+ FStar.Math.Lemmas.lemma_mult_lt_sqr a b (pow2 n);
+ lt_square_div_lt (a * b) (pow2 n);
+ (a * b) / (pow2 n)
+#pop-optionsDivision primitives
+let div (#n:nat) (a:uint_t n) (b:uint_t n{b <> 0}) : Pure (uint_t n)
+ (requires (size (a / b) n))
+ (ensures (fun c -> b <> 0 ==> a / b = c))
+ = a / bval div_underspec: #n:nat -> a:uint_t n -> b:uint_t n{b <> 0} -> Pure (uint_t n)
+ (requires True)
+ (ensures (fun c ->
+ (b <> 0 /\ size (a / b) n) ==> a / b = c))val div_size: #n:pos -> a:uint_t n -> b:uint_t n{b <> 0} ->
+ Lemma (requires (size a n)) (ensures (size (a / b) n))let udiv (#n:pos) (a:uint_t n) (b:uint_t n{b <> 0}) : Tot (c:uint_t n{b <> 0 ==> a / b = c}) =
+ div_size #n a b;
+ a / bModulo primitives
+let mod (#n:nat) (a:uint_t n) (b:uint_t n{b <> 0}) : Tot (uint_t n) =
+ a - ((a/b) * b)Comparison operators
+let eq #n (a:uint_t n) (b:uint_t n) : Tot bool = (a = b)
+let gt #n (a:uint_t n) (b:uint_t n) : Tot bool = (a > b)
+let gte #n (a:uint_t n) (b:uint_t n) : Tot bool = (a >= b)
+let lt #n (a:uint_t n) (b:uint_t n) : Tot bool = (a < b)
+let lte #n (a:uint_t n) (b:uint_t n) : Tot bool = (a <= b)++Casts
+
let to_uint_t (m:nat) (a:int) : Tot (uint_t m) = a % pow2 mWARNING: Mind the big endian vs little endian definition
+Casts
+let rec to_vec (#n:nat) (num:uint_t n) : Tot (bv_t n) =
+ if n = 0 then Seq.empty #bool
+ else Seq.append (to_vec #(n - 1) (num / 2)) (Seq.create 1 (num % 2 = 1))let rec from_vec (#n:nat) (vec:bv_t n) : Tot (uint_t n) =
+ if n = 0 then 0
+ else 2 * from_vec #(n - 1) (slice vec 0 (n - 1)) + (if index vec (n - 1) then 1 else 0)val to_vec_lemma_1: #n:nat -> a:uint_t n -> b:uint_t n ->
+ Lemma (requires a = b) (ensures equal (to_vec a) (to_vec b))val to_vec_lemma_2: #n:nat -> a:uint_t n -> b:uint_t n ->
+ Lemma (requires equal (to_vec a) (to_vec b)) (ensures a = b)val inverse_aux: #n:nat -> vec:bv_t n -> i:nat{i < n} ->
+ Lemma (requires True) (ensures index vec i = index (to_vec (from_vec vec)) i)
+ [SMTPat (index (to_vec (from_vec vec)) i)]val inverse_vec_lemma: #n:nat -> vec:bv_t n ->
+ Lemma (requires True) (ensures equal vec (to_vec (from_vec vec)))
+ [SMTPat (to_vec (from_vec vec))]val inverse_num_lemma: #n:nat -> num:uint_t n ->
+ Lemma (requires True) (ensures num = from_vec (to_vec num))
+ [SMTPat (from_vec (to_vec num))]val from_vec_lemma_1: #n:nat -> a:bv_t n -> b:bv_t n ->
+ Lemma (requires equal a b) (ensures from_vec a = from_vec b)val from_vec_lemma_2: #n:nat -> a:bv_t n -> b:bv_t n ->
+ Lemma (requires from_vec a = from_vec b) (ensures equal a b)val from_vec_aux: #n:nat -> a:bv_t n -> s1:nat{s1 < n} -> s2:nat{s2 < s1} ->
+ Lemma (requires True)
+ (ensures (from_vec #s2 (slice a 0 s2)) * pow2 (n - s2) + (from_vec #(s1 - s2) (slice a s2 s1)) * pow2 (n - s1) + (from_vec #(n - s1) (slice a s1 n)) = ((from_vec #s2 (slice a 0 s2)) * pow2 (s1 - s2) + (from_vec #(s1 - s2) (slice a s2 s1))) * pow2 (n - s1) + (from_vec #(n - s1) (slice a s1 n)))val seq_slice_lemma: #n:nat -> a:bv_t n -> s1:nat{s1 < n} -> t1:nat{t1 >= s1 && t1 <= n} -> s2:nat{s2 < t1 - s1} -> t2:nat{t2 >= s2 && t2 <= t1 - s1} ->
+ Lemma (equal (slice (slice a s1 t1) s2 t2) (slice a (s1 + s2) (s1 + t2)))val from_vec_propriety: #n:pos -> a:bv_t n -> s:nat{s < n} ->
+ Lemma (requires True)
+ (ensures from_vec a = (from_vec #s (slice a 0 s)) * pow2 (n - s) + from_vec #(n - s) (slice a s n))
+ (decreases (n - s))val append_lemma: #n:pos -> #m:pos -> a:bv_t n -> b:bv_t m ->
+ Lemma (from_vec #(n + m) (append a b) = (from_vec #n a) * pow2 m + (from_vec #m b))val slice_left_lemma: #n:pos -> a:bv_t n -> s:pos{s < n} ->
+ Lemma (requires True)
+ (ensures from_vec #s (slice a 0 s) = (from_vec #n a) / (pow2 (n - s)))val slice_right_lemma: #n:pos -> a:bv_t n -> s:pos{s < n} ->
+ Lemma (requires True)
+ (ensures from_vec #s (slice a (n - s) n) = (from_vec #n a) % (pow2 s))Relations between constants in BitVector and in UInt.
+val zero_to_vec_lemma: #n:pos -> i:nat{i < n} ->
+ Lemma (requires True) (ensures index (to_vec (zero n)) i = index (zero_vec #n) i)
+ [SMTPat (index (to_vec (zero n)) i)]val zero_from_vec_lemma: #n:pos ->
+ Lemma (requires True) (ensures from_vec (zero_vec #n) = zero n)
+ [SMTPat (from_vec (zero_vec #n))]val one_to_vec_lemma: #n:pos -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures index (to_vec (one n)) i = index (elem_vec #n (n - 1)) i)
+ [SMTPat (index (to_vec (one n)) i)]val pow2_to_vec_lemma: #n:pos -> p:nat{p < n} -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures index (to_vec (pow2_n #n p)) i = index (elem_vec #n (n - p - 1)) i)
+ [SMTPat (index (to_vec (pow2_n #n p)) i)]val pow2_from_vec_lemma: #n:pos -> p:nat{p < n} ->
+ Lemma (requires True) (ensures from_vec (elem_vec #n p) = pow2_n #n (n - p - 1))
+ [SMTPat (from_vec (elem_vec #n p))]val ones_to_vec_lemma: #n:pos -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures index (to_vec (ones n)) i = index (ones_vec #n) i)
+ [SMTPat (index (to_vec (ones n)) i)]val ones_from_vec_lemma: #n:pos ->
+ Lemma (requires True) (ensures from_vec (ones_vec #n) = ones n)
+ [SMTPat (from_vec (ones_vec #n))](nth a i) returns a boolean indicating the i-th bit of a.
+let nth (#n:pos) (a:uint_t n) (i:nat{i < n}) : Tot bool =
+ index (to_vec #n a) ival nth_lemma: #n:pos -> a:uint_t n -> b:uint_t n ->
+ Lemma (requires forall (i:nat{i < n}). nth a i = nth b i)
+ (ensures a = b)Lemmas for constants
+val zero_nth_lemma: #n:pos -> i:nat{i < n} ->
+ Lemma (requires True) (ensures nth (zero n) i = false)
+ [SMTPat (nth (zero n) i)]val pow2_nth_lemma: #n:pos -> p:nat{p < n} -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures (i = n - p - 1 ==> nth (pow2_n #n p) i = true) /\
+ (i <> n - p - 1 ==> nth (pow2_n #n p) i = false))
+ [SMTPat (nth (pow2_n #n p) i)]val one_nth_lemma: #n:pos -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures (i = n - 1 ==> nth (one n) i = true) /\
+ (i < n - 1 ==> nth (one n) i = false))
+ [SMTPat (nth (one n) i)]val ones_nth_lemma: #n:pos -> i:nat{i < n} ->
+ Lemma (requires True) (ensures (nth (ones n) i) = true)
+ [SMTPat (nth (ones n) i)]Bitwise operators
+let logand (#n:pos) (a:uint_t n) (b:uint_t n) : Tot (uint_t n) =
+ from_vec #n (logand_vec #n (to_vec #n a) (to_vec #n b))let logxor (#n:pos) (a:uint_t n) (b:uint_t n) : Tot (uint_t n) =
+ from_vec #n (logxor_vec #n (to_vec #n a) (to_vec #n b))let logor (#n:pos) (a:uint_t n) (b:uint_t n) : Tot (uint_t n) =
+ from_vec #n (logor_vec #n (to_vec #n a) (to_vec #n b))let lognot (#n:pos) (a:uint_t n) : Tot (uint_t n) =
+ from_vec #n (lognot_vec #n (to_vec #n a))Bitwise operators definitions
+val logand_definition: #n:pos -> a:uint_t n -> b:uint_t n -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures (nth (logand a b) i = (nth a i && nth b i)))
+ [SMTPat (nth (logand a b) i)]val logxor_definition: #n:pos -> a:uint_t n -> b:uint_t n -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures (nth (logxor a b) i = (nth a i <> nth b i)))
+ [SMTPat (nth (logxor a b) i)]val logor_definition: #n:pos -> a:uint_t n -> b:uint_t n -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures (nth (logor a b) i = (nth a i || nth b i)))
+ [SMTPat (nth (logor a b) i)]val lognot_definition: #n:pos -> a:uint_t n -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures (nth (lognot a) i = not(nth a i)))
+ [SMTPat (nth (lognot a) i)]Two's complement unary minus
+inline_for_extraction
+let minus (#n:pos) (a:uint_t n) : Tot (uint_t n) =
+ add_mod (lognot a) 1Bitwise operators lemmas
+TODO: lemmas about the relations between different operators
+Bitwise AND operator
+val logand_commutative: #n:pos -> a:uint_t n -> b:uint_t n ->
+ Lemma (requires True) (ensures (logand #n a b = logand #n b a))val logand_associative: #n:pos -> a:uint_t n -> b:uint_t n -> c:uint_t n ->
+ Lemma (requires True)
+ (ensures (logand #n (logand #n a b) c = logand #n a (logand #n b c)))val logand_self: #n:pos -> a:uint_t n ->
+ Lemma (requires True) (ensures (logand #n a a = a))val logand_lemma_1: #n:pos -> a:uint_t n ->
+ Lemma (requires True) (ensures (logand #n a (zero n) = zero n))val logand_lemma_2: #n:pos -> a:uint_t n ->
+ Lemma (requires True) (ensures (logand #n a (ones n) = a))subset_vec_le_lemma proves that a subset of bits is numerically smaller or equal.
+val subset_vec_le_lemma: #n:pos -> a:bv_t n -> b:bv_t n ->
+ Lemma (requires is_subset_vec #n a b) (ensures (from_vec a) <= (from_vec b))logand_le proves the the result of AND is less than or equal to both arguments.
+val logand_le: #n:pos -> a:uint_t n -> b:uint_t n ->
+ Lemma (requires True)
+ (ensures (logand a b) <= a /\ (logand a b) <= b)Bitwise XOR operator
+val logxor_commutative: #n:pos -> a:uint_t n -> b:uint_t n ->
+ Lemma (requires True) (ensures (logxor #n a b = logxor #n b a))val logxor_associative: #n:pos -> a:uint_t n -> b:uint_t n -> c:uint_t n ->
+ Lemma (requires True) (ensures (logxor #n (logxor #n a b) c = logxor #n a (logxor #n b c)))val logxor_self: #n:pos -> a:uint_t n ->
+ Lemma (requires True) (ensures (logxor #n a a = zero n))val logxor_lemma_1: #n:pos -> a:uint_t n ->
+ Lemma (requires True) (ensures (logxor #n a (zero n) = a))val logxor_lemma_2: #n:pos -> a:uint_t n ->
+ Lemma (requires True) (ensures (logxor #n a (ones n) = lognot #n a))private let xor (b:bool) (b':bool) : Tot bool = b <> b'private val xor_lemma (a:bool) (b:bool) : Lemma
+ (requires (True))
+ (ensures (xor (xor a b) b = a))
+ [SMTPat (xor (xor a b) b)]val logxor_inv: #n:pos -> a:uint_t n -> b:uint_t n -> Lemma
+ (a = logxor #n (logxor #n a b) b)val logxor_neq_nonzero: #n:pos -> a:uint_t n -> b:uint_t n -> Lemma
+ (a <> b ==> logxor a b <> 0)Bitwise OR operators
+val logor_commutative: #n:pos -> a:uint_t n -> b:uint_t n ->
+ Lemma (requires True) (ensures (logor #n a b = logor #n b a))val logor_associative: #n:pos -> a:uint_t n -> b:uint_t n -> c:uint_t n ->
+ Lemma (requires True)
+ (ensures (logor #n (logor #n a b) c = logor #n a (logor #n b c)))val logor_self: #n:pos -> a:uint_t n ->
+ Lemma (requires True) (ensures (logor #n a a = a))val logor_lemma_1: #n:pos -> a:uint_t n ->
+ Lemma (requires True) (ensures (logor #n a (zero n) = a))val logor_lemma_2: #n:pos -> a:uint_t n ->
+ Lemma (requires True) (ensures (logor #n a (ones n) = ones n))superset_vec_le_lemma proves that a superset of bits is numerically greater than or equal.
+val superset_vec_ge_lemma: #n:pos -> a:bv_t n -> b:bv_t n ->
+ Lemma (requires is_superset_vec #n a b)
+ (ensures (from_vec a) >= (from_vec b))logor_ge proves that the result of an OR is greater than or equal to both arguments.
+val logor_ge: #n:pos -> a:uint_t n -> b:uint_t n ->
+ Lemma (requires True)
+ (ensures (logor a b) >= a /\ (logor a b) >= b)Bitwise NOT operator
+val lognot_self: #n:pos -> a:uint_t n ->
+ Lemma (requires True) (ensures (lognot #n (lognot #n a) = a))val lognot_lemma_1: #n:pos ->
+ Lemma (requires True) (ensures (lognot #n (zero n) = ones n))Used in the next two lemmas
+private val index_to_vec_ones: #n:pos -> m:nat{m <= n} -> i:nat{i < n} ->
+ Lemma (requires True)
+ (ensures (pow2 m <= pow2 n /\
+ (i < n - m ==> index (to_vec #n (pow2 m - 1)) i == false) /\
+ (n - m <= i ==> index (to_vec #n (pow2 m - 1)) i == true)))
+ [SMTPat (index (to_vec #n (pow2 m - 1)) i)]val logor_disjoint: #n:pos -> a:uint_t n -> b:uint_t n -> m:pos{m < n} ->
+ Lemma (requires (a % pow2 m == 0 /\ b < pow2 m))
+ (ensures (logor #n a b == a + b))val logand_mask: #n:pos -> a:uint_t n -> m:pos{m < n} ->
+ Lemma (pow2 m < pow2 n /\ logand #n a (pow2 m - 1) == a % pow2 m)Shift operators
+let shift_left (#n:pos) (a:uint_t n) (s:nat) : Tot (uint_t n) =
+ from_vec (shift_left_vec #n (to_vec #n a) s)let shift_right (#n:pos) (a:uint_t n) (s:nat) : Tot (uint_t n) =
+ from_vec (shift_right_vec #n (to_vec #n a) s)Shift operators lemmas
+val shift_left_lemma_1: #n:pos -> a:uint_t n -> s:nat -> i:nat{i < n && i >= n - s} ->
+ Lemma (requires True)
+ (ensures (nth (shift_left #n a s) i = false))
+ [SMTPat (nth (shift_left #n a s) i)]val shift_left_lemma_2: #n:pos -> a:uint_t n -> s:nat -> i:nat{i < n && i < n - s} ->
+ Lemma (requires True)
+ (ensures (nth (shift_left #n a s) i = nth #n a (i + s)))
+ [SMTPat (nth (shift_left #n a s) i)]val shift_right_lemma_1: #n:pos -> a:uint_t n -> s:nat -> i:nat{i < n && i < s} ->
+ Lemma (requires True)
+ (ensures (nth (shift_right #n a s) i = false))
+ [SMTPat (nth (shift_right #n a s) i)]val shift_right_lemma_2: #n:pos -> a:uint_t n -> s:nat -> i:nat{i < n && i >= s} ->
+ Lemma (requires True)
+ (ensures (nth (shift_right #n a s) i = nth #n a (i - s)))
+ [SMTPat (nth (shift_right #n a s) i)]Lemmas with shift operators and bitwise operators
+val shift_left_logand_lemma: #n:pos -> a:uint_t n -> b:uint_t n -> s:nat ->
+ Lemma (requires True)
+ (ensures (shift_left #n (logand #n a b) s = logand #n (shift_left #n a s) (shift_left #n b s)))val shift_right_logand_lemma: #n:pos -> a:uint_t n -> b:uint_t n -> s:nat ->
+ Lemma (requires True)
+ (ensures (shift_right #n (logand #n a b) s = logand #n (shift_right #n a s) (shift_right #n b s)))val shift_left_logxor_lemma: #n:pos -> a:uint_t n -> b:uint_t n -> s:nat ->
+ Lemma (requires True)
+ (ensures (shift_left #n (logxor #n a b) s = logxor #n (shift_left #n a s) (shift_left #n b s)))val shift_right_logxor_lemma: #n:pos -> a:uint_t n -> b:uint_t n -> s:nat ->
+ Lemma (requires True)
+ (ensures (shift_right #n (logxor #n a b) s = logxor #n (shift_right #n a s) (shift_right #n b s)))val shift_left_logor_lemma: #n:pos -> a:uint_t n -> b:uint_t n -> s:nat ->
+ Lemma (requires True)
+ (ensures (shift_left #n (logor #n a b) s = logor #n (shift_left #n a s) (shift_left #n b s)))val shift_right_logor_lemma: #n:pos -> a:uint_t n -> b:uint_t n -> s:nat ->
+ Lemma (requires True)
+ (ensures (shift_right #n (logor #n a b) s = logor #n (shift_right #n a s) (shift_right #n b s)))Lemmas about value after shift operations
+val shift_left_value_aux_1: #n:pos -> a:uint_t n -> s:nat{s >= n} ->
+ Lemma (requires True)
+ (ensures shift_left #n a s = (a * pow2 s) % pow2 n)val shift_left_value_aux_2: #n:pos -> a:uint_t n ->
+ Lemma (requires True)
+ (ensures shift_left #n a 0 = (a * pow2 0) % pow2 n)val shift_left_value_aux_3: #n:pos -> a:uint_t n -> s:pos{s < n} ->
+ Lemma (requires True)
+ (ensures shift_left #n a s = (a * pow2 s) % pow2 n)val shift_left_value_lemma: #n:pos -> a:uint_t n -> s:nat ->
+ Lemma (requires True)
+ (ensures shift_left #n a s = (a * pow2 s) % pow2 n)
+ [SMTPat (shift_left #n a s)]val shift_right_value_aux_1: #n:pos -> a:uint_t n -> s:nat{s >= n} ->
+ Lemma (requires True)
+ (ensures shift_right #n a s = a / pow2 s)val shift_right_value_aux_2: #n:pos -> a:uint_t n ->
+ Lemma (requires True)
+ (ensures shift_right #n a 0 = a / pow2 0)val shift_right_value_aux_3: #n:pos -> a:uint_t n -> s:pos{s < n} ->
+ Lemma (requires True)
+ (ensures shift_right #n a s = a / pow2 s)val shift_right_value_lemma: #n:pos -> a:uint_t n -> s:nat ->
+ Lemma (requires True)
+ (ensures shift_right #n a s = a / pow2 s)
+ [SMTPat (shift_right #n a s)]Lemmas about the most significant bit in various situations
+let msb (#n:pos) (a:uint_t n) : Tot bool = nth a 0val lemma_msb_pow2: #n:pos -> a:uint_t n ->
+ Lemma (msb a <==> a >= pow2 (n-1))val lemma_minus_zero: #n:pos -> a:uint_t n ->
+ Lemma (minus a = 0 ==> a = 0)val lemma_msb_gte: #n:pos{n > 1} -> a:uint_t n -> b:uint_t n ->
+ Lemma ((a >= b && not (msb a)) ==> not (msb b))Lemmas toward showing ~n + 1 = -a
+val lemma_uint_mod: #n:pos -> a:uint_t n ->
+ Lemma (a = a % pow2 n)val lemma_add_sub_cancel: #n:pos -> a:uint_t n -> b:uint_t n ->
+ Lemma (add_mod (sub_mod a b) b = a)val lemma_mod_sub_distr_l: a:int -> b:int -> p:pos ->
+ Lemma ((a - b) % p = ((a % p) - b) % p)val lemma_sub_add_cancel: #n:pos -> a:uint_t n -> b:uint_t n ->
+ Lemma (sub_mod (add_mod a b) b = a)let zero_extend_vec (#n:pos) (a:BitVector.bv_t n): Tot (BitVector.bv_t (n+1)) = Seq.append (Seq.create 1 false) a
+let one_extend_vec (#n:pos) (a:BitVector.bv_t n): Tot (BitVector.bv_t (n+1)) = Seq.append (Seq.create 1 true) alet zero_extend (#n:pos) (a:uint_t n): Tot (uint_t (n+1)) = from_vec (zero_extend_vec (to_vec a))
+let one_extend (#n:pos) (a:uint_t n): Tot (uint_t (n+1)) = from_vec (one_extend_vec (to_vec a))val lemma_zero_extend: #n:pos -> a:uint_t n ->
+ Lemma (zero_extend a = a)
+ [SMTPat (zero_extend a)]val lemma_one_extend: #n:pos -> a:uint_t n ->
+ Lemma (one_extend a = pow2 n + a)
+ [SMTPat (one_extend a)]val lemma_lognot_zero_ext: #n:pos -> a:uint_t n ->
+ Lemma (lognot #(n+1) (zero_extend a) = pow2 n + (lognot #n a))val lemma_lognot_one_ext: #n:pos -> a:uint_t n ->
+ Lemma (lognot #(n+1) (one_extend a) = lognot #n a)val lemma_lognot_value_mod: #n:pos -> a:uint_t n ->
+ Lemma
+ (requires True)
+ (ensures (lognot a = pow2 n - a - 1))
+ (decreases n)val lemma_lognot_value_zero: #n:pos -> a:uint_t n{a = 0} ->
+ Lemma (lognot a = sub_mod (sub_mod 0 a) 1)val lemma_one_mod_pow2: #n:pos ->
+ Lemma (1 = 1 % (pow2 n))val lemma_lognot_value_nonzero: #n:pos -> a:uint_t n{a <> 0} ->
+ Lemma (lognot a = sub_mod (sub_mod 0 a) 1)val lemma_lognot_value: #n:pos -> a:uint_t n ->
+ Lemma (lognot #n a = sub_mod (sub_mod 0 a) 1)val lemma_minus_eq_zero_sub: #n:pos -> a:uint_t n ->
+ Lemma (minus #n a = sub_mod #n 0 a)fsdoc: no-summary-found
-fsdoc: no-comment-found
+Opens module FStar.UInt
+Opens module FStar.Mul
+Aliases module FStar.UInt32 as U32
Aliases module FStar.UInt64 as U64
noextract
+let n = 128val t: (x:Type0{hasEq x})[@@ noextract_to "Kremlin"]
+val v (x:t) : Tot (uint_t n)[@@ noextract_to "Kremlin"]
+val uint_to_t: x:uint_t n -> Pure t
+ (requires True)
+ (ensures (fun y -> v y = x))val v_inj (x1 x2: t): Lemma (requires (v x1 == v x2)) (ensures (x1 == x2))val add: a:t -> b:t -> Pure t
+ (requires (size (v a + v b) n))
+ (ensures (fun c -> v a + v b = v c))val add_underspec: a:t -> b:t -> Pure t
+ (requires True)
+ (ensures (fun c ->
+ size (v a + v b) n ==> v a + v b = v c))val add_mod: a:t -> b:t -> Pure t
+ (requires True)
+ (ensures (fun c -> (v a + v b) % pow2 n = v c))Subtraction primitives
+val sub: a:t -> b:t -> Pure t
+ (requires (size (v a - v b) n))
+ (ensures (fun c -> v a - v b = v c))val sub_underspec: a:t -> b:t -> Pure t
+ (requires True)
+ (ensures (fun c ->
+ size (v a - v b) n ==> v a - v b = v c))val sub_mod: a:t -> b:t -> Pure t
+ (requires True)
+ (ensures (fun c -> (v a - v b) % pow2 n = v c))Bitwise operators
+val logand: a:t -> b:t -> Pure t
+ (requires True)
+ (ensures (fun r -> v r == logand (v a) (v b)))val logxor: a:t -> b:t -> Pure t
+ (requires True)
+ (ensures (fun r -> v r == logxor (v a) (v b)))val logor: a:t -> b:t -> Pure t
+ (requires True)
+ (ensures (fun r -> v r == logor (v a) (v b)))val lognot: a:t -> Pure t
+ (requires True)
+ (ensures (fun r -> v r == lognot (v a)))This private primitive is used internally by the +compiler to translate bounded integer constants +with a desugaring-time check of the size of the number, +rather than an expensive verifiation check. +Since it is marked private, client programs cannot call it directly +Since it is marked unfold, it eagerly reduces, +eliminating the verification overhead of the wrapper
+private
+unfold
+let __uint_to_t (x:int) : Tot t =
+ assume (fits x 128);
+ uint_to_t xShift operators
+val shift_left: a:t -> s:UInt32.t -> Pure t
+ (requires (U32.v s < n))
+ (ensures (fun c -> v c = ((v a * pow2 (UInt32.v s)) % pow2 n)))val shift_right: a:t -> s:UInt32.t -> Pure t
+ (requires (U32.v s < n))
+ (ensures (fun c -> v c = (v a / (pow2 (UInt32.v s)))))Comparison operators
+val eq (a:t) (b:t) : Pure bool
+ (requires True)
+ (ensures (fun r -> r == eq #n (v a) (v b)))val gt (a:t) (b:t) : Pure bool
+ (requires True)
+ (ensures (fun r -> r == gt #n (v a) (v b)))val lt (a:t) (b:t) : Pure bool
+ (requires True)
+ (ensures (fun r -> r == lt #n (v a) (v b)))val gte (a:t) (b:t) : Pure bool
+ (requires True)
+ (ensures (fun r -> r == gte #n (v a) (v b)))val lte (a:t) (b:t) : Pure bool
+ (requires True)
+ (ensures (fun r -> r == lte #n (v a) (v b)))val eq_mask: a:t -> b:t -> Tot (c:t{(v a = v b ==> v c = pow2 n - 1) /\ (v a <> v b ==> v c = 0)})
+val gte_mask: a:t -> b:t -> Tot (c:t{(v a >= v b ==> v c = pow2 n - 1) /\ (v a < v b ==> v c = 0)})Casts
+val uint64_to_uint128: a:U64.t -> b:t{v b == U64.v a}
+val uint128_to_uint64: a:t -> b:U64.t{U64.v b == v a % pow2 64}To input / output constants
+TODO: assume these without implementations
+val to_string: t -> Tot string +val of_string: string -> Tot t
+Infix notations
+inline_for_extraction noextract let op_Plus_Hat = add
+inline_for_extraction noextract let op_Plus_Question_Hat = add_underspec
+inline_for_extraction noextract let op_Plus_Percent_Hat = add_mod
+inline_for_extraction noextract let op_Subtraction_Hat = sub
+inline_for_extraction noextract let op_Subtraction_Question_Hat = sub_underspec
+inline_for_extraction noextract let op_Subtraction_Percent_Hat = sub_mod
+inline_for_extraction noextract let op_Amp_Hat = logand
+inline_for_extraction noextract let op_Hat_Hat = logxor
+inline_for_extraction noextract let op_Bar_Hat = logor
+inline_for_extraction noextract let op_Less_Less_Hat = shift_left
+inline_for_extraction noextract let op_Greater_Greater_Hat = shift_right
+inline_for_extraction noextract let op_Equals_Hat = eq
+inline_for_extraction noextract let op_Greater_Hat = gt
+inline_for_extraction noextract let op_Less_Hat = lt
+inline_for_extraction noextract let op_Greater_Equals_Hat = gte
+inline_for_extraction noextract let op_Less_Equals_Hat = lteMultiplication primitives
+Note that unlike UIntN, we do not provide uint128 * uint128 primitives (mul, +mul_underspec, mul_mod, and mul_div)
+val mul32: x:U64.t -> y:U32.t -> Pure t
+ (requires True)
+ (ensures (fun r -> v r == U64.v x * U32.v y))val mul_wide: x:U64.t -> y:U64.t -> Pure t
+ (requires True)
+ (ensures (fun r -> v r == U64.v x * U64.v y))fsdoc: no-summary-found
-fsdoc: no-comment-found
+mk_int.sh, DO NOT EDIT DIRECTLY ***unfold let n = 16++For FStar.UIntN.fstp: anything that you fix/update here should be +reflected in
+FStar.IntN.fstp, which is mostly a copy-paste of +this module.Except, as compared to
+FStar.IntN.fstp, here:+
+- every occurrence of
+int_thas been replaced withuint_t+- every occurrence of
+@%has been replaced with%.- some functions (e.g., add_underspec, etc.) are only defined here, not on signed integers
+
++This module provides an abstract type for machine integers of a +given signedness and width. The interface is designed to be safe +with respect to arithmetic underflow and overflow.
+
++Note, we have attempted several times to re-design this module to +make it more amenable to normalization and to impose less overhead +on the SMT solver when reasoning about machine integer +arithmetic. The following github issue reports on the current +status of that work.
+ +
#set-options "--max_fuel 0 --max_ifuel 0"Abstract type of machine integers, with an underlying +representation using a bounded mathematical integer
+new val t : eqtypeA coercion that projects a bounded mathematical integer from a +machine integer
+val v (x:t) : Tot (uint_t n)A coercion that injects a bounded mathematical integers into a +machine integer
+val uint_to_t (x:uint_t n) : Pure t
+ (requires True)
+ (ensures (fun y -> v y = x))Injection/projection inverse
+val uv_inv (x : t) : Lemma
+ (ensures (uint_to_t (v x) == x))
+ [SMTPat (v x)]Projection/injection inverse
+val vu_inv (x : uint_t n) : Lemma
+ (ensures (v (uint_to_t x) == x))
+ [SMTPat (uint_to_t x)]An alternate form of the injectivity of the v projection
val v_inj (x1 x2: t): Lemma
+ (requires (v x1 == v x2))
+ (ensures (x1 == x2))Constants 0 and 1
+val zero : x:t{v x = 0}val one : x:t{v x = 1}Bounds-respecting addition
+val add (a:t) (b:t) : Pure t
+ (requires (size (v a + v b) n))
+ (ensures (fun c -> v a + v b = v c))The precondition enforces that the sum does not overflow, +expressing the bound as an addition on mathematical integers
+Underspecified, possibly overflowing addition:
+val add_underspec (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c ->
+ size (v a + v b) n ==> v a + v b = v c))The postcondition only enures that the result is the sum of the +arguments in case there is no overflow
+Addition modulo 2^n
val add_mod (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c -> FStar.UInt.add_mod (v a) (v b) = v c))Machine integers can always be added, but the postcondition is now
+in terms of addition modulo 2^n on mathematical integers
Bounds-respecting subtraction
+val sub (a:t) (b:t) : Pure t
+ (requires (size (v a - v b) n))
+ (ensures (fun c -> v a - v b = v c))The precondition enforces that the difference does not underflow, +expressing the bound as a difference on mathematical integers
+Underspecified, possibly overflowing subtraction:
+val sub_underspec (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c ->
+ size (v a - v b) n ==> v a - v b = v c))The postcondition only enures that the result is the difference of +the arguments in case there is no underflow
+Subtraction modulo 2^n
val sub_mod (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c -> FStar.UInt.sub_mod (v a) (v b) = v c))Machine integers can always be subtractd, but the postcondition is
+now in terms of subtraction modulo 2^n on mathematical integers
Bounds-respecting multiplication
+val mul (a:t) (b:t) : Pure t
+ (requires (size (v a * v b) n))
+ (ensures (fun c -> v a * v b = v c))The precondition enforces that the product does not overflow, +expressing the bound as a product on mathematical integers
+Underspecified, possibly overflowing product
+val mul_underspec (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c ->
+ size (v a * v b) n ==> v a * v b = v c))The postcondition only enures that the result is the product of +the arguments in case there is no overflow
+Multiplication modulo 2^n
val mul_mod (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c -> FStar.UInt.mul_mod (v a) (v b) = v c))Machine integers can always be multiplied, but the postcondition
+is now in terms of product modulo 2^n on mathematical integers
Euclidean division of a and b, with b non-zero
val div (a:t) (b:t{v b <> 0}) : Pure t
+ (requires (True))
+ (ensures (fun c -> v a / v b = v c))Euclidean remainder
+val rem (a:t) (b:t{v b <> 0}) : Pure t
+ (requires True)
+ (ensures (fun c -> FStar.UInt.mod (v a) (v b) = v c))The result is the modulus of a with respect to a non-zero b
++Also see FStar.BV
+
Bitwise logical conjunction
+val logand (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logand` v y = v z))Bitwise logical exclusive-or
+val logxor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logxor` v y == v z))Bitwise logical disjunction
+val logor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logor` v y == v z))Bitwise logical negation
+val lognot (x:t) : Pure t
+ (requires True)
+ (ensures (fun z -> lognot (v x) == v z))Shift right with zero fill, shifting at most the integer width
+val shift_right (a:t) (s:UInt32.t) : Pure t
+ (requires (UInt32.v s < n))
+ (ensures (fun c -> FStar.UInt.shift_right (v a) (UInt32.v s) = v c))Shift left with zero fill, shifting at most the integer width
+val shift_left (a:t) (s:UInt32.t) : Pure t
+ (requires (UInt32.v s < n))
+ (ensures (fun c -> FStar.UInt.shift_left (v a) (UInt32.v s) = v c))Equality
+let eq (a:t) (b:t) : Tot bool = eq #n (v a) (v b)Note, it is safe to also use the polymorphic decidable equality
+operator =
Greater than
+let gt (a:t) (b:t) : Tot bool = gt #n (v a) (v b)Greater than or equal
+let gte (a:t) (b:t) : Tot bool = gte #n (v a) (v b)Less than
+let lt (a:t) (b:t) : Tot bool = lt #n (v a) (v b)Less than or equal
+let lte (a:t) (b:t) : Tot bool = lte #n (v a) (v b)Unary negation
+inline_for_extraction
+let minus (a:t) = add_mod (lognot a) (uint_to_t 1)The maximum value for this type
+inline_for_extraction
+let n_minus_one = UInt32.uint_to_t (n - 1)#set-options "--z3rlimit 80 --initial_fuel 1 --max_fuel 1"A constant-time way to compute the equality of +two machine integers.
+let eq_mask (a:t) (b:t)
+ : Pure t
+ (requires True)
+ (ensures (fun c -> (v a = v b ==> v c = pow2 n - 1) /\
+ (v a <> v b ==> v c = 0)))
+ = let x = logxor a b in
+ let minus_x = minus x in
+ let x_or_minus_x = logor x minus_x in
+ let xnx = shift_right x_or_minus_x n_minus_one in
+ let c = sub_mod xnx (uint_to_t 1) in
+ if a = b then
+ begin
+ logxor_self (v a);
+ lognot_lemma_1 #n;
+ logor_lemma_1 (v x);
+ assert (v x = 0 /\ v minus_x = 0 /\
+ v x_or_minus_x = 0 /\ v xnx = 0);
+ assert (v c = ones n)
+ end
+ else
+ begin
+ logxor_neq_nonzero (v a) (v b);
+ lemma_msb_pow2 #n (v (lognot x));
+ lemma_msb_pow2 #n (v minus_x);
+ lemma_minus_zero #n (v x);
+ assert (v c = FStar.UInt.zero n)
+ end;
+ cWith inspiration from https://git.zx2c4.com/WireGuard/commit/src/crypto/curve25519-hacl64.h?id=2e60bb395c1f589a398ec606d611132ef9ef764b
+Note, the branching on a=b is just for proof-purposes.
private
+val lemma_sub_msbs (a:t) (b:t)
+ : Lemma ((msb (v a) = msb (v b)) ==> (v a < v b <==> msb (v (sub_mod a b))))A constant-time way to compute the >= inequality of
+two machine integers.
let gte_mask (a:t) (b:t)
+ : Pure t
+ (requires True)
+ (ensures (fun c -> (v a >= v b ==> v c = pow2 n - 1) /\
+ (v a < v b ==> v c = 0)))
+ = let x = a in
+ let y = b in
+ let x_xor_y = logxor x y in
+ let x_sub_y = sub_mod x y in
+ let x_sub_y_xor_y = logxor x_sub_y y in
+ let q = logor x_xor_y x_sub_y_xor_y in
+ let x_xor_q = logxor x q in
+ let x_xor_q_ = shift_right x_xor_q n_minus_one in
+ let c = sub_mod x_xor_q_ (uint_to_t 1) in
+ lemma_sub_msbs x y;
+ lemma_msb_gte (v x) (v y);
+ lemma_msb_gte (v y) (v x);
+ c
+#reset-optionsWith inspiration from https://git.zx2c4.com/WireGuard/commit/src/crypto/curve25519-hacl64.h?id=0a483a9b431d87eca1b275463c632f8d5551978a
+unfold let op_Plus_Hat = add
+unfold let op_Plus_Question_Hat = add_underspec
+unfold let op_Plus_Percent_Hat = add_mod
+unfold let op_Subtraction_Hat = sub
+unfold let op_Subtraction_Question_Hat = sub_underspec
+unfold let op_Subtraction_Percent_Hat = sub_mod
+unfold let op_Star_Hat = mul
+unfold let op_Star_Question_Hat = mul_underspec
+unfold let op_Star_Percent_Hat = mul_mod
+unfold let op_Slash_Hat = div
+unfold let op_Percent_Hat = rem
+unfold let op_Hat_Hat = logxor
+unfold let op_Amp_Hat = logand
+unfold let op_Bar_Hat = logor
+unfold let op_Less_Less_Hat = shift_left
+unfold let op_Greater_Greater_Hat = shift_right
+unfold let op_Equals_Hat = eq
+unfold let op_Greater_Hat = gt
+unfold let op_Greater_Equals_Hat = gte
+unfold let op_Less_Hat = lt
+unfold let op_Less_Equals_Hat = lteIn decimal representation
+val to_string: t -> Tot stringIn hex representation (with leading 0x)
+val to_string_hex: t -> Tot stringIn fixed-width hex representation (left-padded with zeroes, no leading 0x)
+val to_string_hex_pad: t -> Tot stringval of_string: string -> Tot t#set-options "--lax"This private primitive is used internally by the +compiler to translate bounded integer constants +with a desugaring-time check of the size of the number, +rather than an expensive verification check. +Since it is marked private, client programs cannot call it directly +Since it is marked unfold, it eagerly reduces, +eliminating the verification overhead of the wrapper
+private
+unfold
+let __uint_to_t (x:int) : Tot t
+ = uint_to_t x
+#reset-optionsfsdoc: no-summary-found
-fsdoc: no-comment-found
+mk_int.sh, DO NOT EDIT DIRECTLY ***unfold let n = 32++For FStar.UIntN.fstp: anything that you fix/update here should be +reflected in
+FStar.IntN.fstp, which is mostly a copy-paste of +this module.Except, as compared to
+FStar.IntN.fstp, here:+
+- every occurrence of
+int_thas been replaced withuint_t+- every occurrence of
+@%has been replaced with%.- some functions (e.g., add_underspec, etc.) are only defined here, not on signed integers
+
++This module provides an abstract type for machine integers of a +given signedness and width. The interface is designed to be safe +with respect to arithmetic underflow and overflow.
+
++Note, we have attempted several times to re-design this module to +make it more amenable to normalization and to impose less overhead +on the SMT solver when reasoning about machine integer +arithmetic. The following github issue reports on the current +status of that work.
+ +
#set-options "--max_fuel 0 --max_ifuel 0"Abstract type of machine integers, with an underlying +representation using a bounded mathematical integer
+new val t : eqtypeA coercion that projects a bounded mathematical integer from a +machine integer
+val v (x:t) : Tot (uint_t n)A coercion that injects a bounded mathematical integers into a +machine integer
+val uint_to_t (x:uint_t n) : Pure t
+ (requires True)
+ (ensures (fun y -> v y = x))Injection/projection inverse
+val uv_inv (x : t) : Lemma
+ (ensures (uint_to_t (v x) == x))
+ [SMTPat (v x)]Projection/injection inverse
+val vu_inv (x : uint_t n) : Lemma
+ (ensures (v (uint_to_t x) == x))
+ [SMTPat (uint_to_t x)]An alternate form of the injectivity of the v projection
val v_inj (x1 x2: t): Lemma
+ (requires (v x1 == v x2))
+ (ensures (x1 == x2))Constants 0 and 1
+val zero : x:t{v x = 0}val one : x:t{v x = 1}Bounds-respecting addition
+val add (a:t) (b:t) : Pure t
+ (requires (size (v a + v b) n))
+ (ensures (fun c -> v a + v b = v c))The precondition enforces that the sum does not overflow, +expressing the bound as an addition on mathematical integers
+Underspecified, possibly overflowing addition:
+val add_underspec (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c ->
+ size (v a + v b) n ==> v a + v b = v c))The postcondition only enures that the result is the sum of the +arguments in case there is no overflow
+Addition modulo 2^n
val add_mod (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c -> FStar.UInt.add_mod (v a) (v b) = v c))Machine integers can always be added, but the postcondition is now
+in terms of addition modulo 2^n on mathematical integers
Bounds-respecting subtraction
+val sub (a:t) (b:t) : Pure t
+ (requires (size (v a - v b) n))
+ (ensures (fun c -> v a - v b = v c))The precondition enforces that the difference does not underflow, +expressing the bound as a difference on mathematical integers
+Underspecified, possibly overflowing subtraction:
+val sub_underspec (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c ->
+ size (v a - v b) n ==> v a - v b = v c))The postcondition only enures that the result is the difference of +the arguments in case there is no underflow
+Subtraction modulo 2^n
val sub_mod (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c -> FStar.UInt.sub_mod (v a) (v b) = v c))Machine integers can always be subtractd, but the postcondition is
+now in terms of subtraction modulo 2^n on mathematical integers
Bounds-respecting multiplication
+val mul (a:t) (b:t) : Pure t
+ (requires (size (v a * v b) n))
+ (ensures (fun c -> v a * v b = v c))The precondition enforces that the product does not overflow, +expressing the bound as a product on mathematical integers
+Underspecified, possibly overflowing product
+val mul_underspec (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c ->
+ size (v a * v b) n ==> v a * v b = v c))The postcondition only enures that the result is the product of +the arguments in case there is no overflow
+Multiplication modulo 2^n
val mul_mod (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c -> FStar.UInt.mul_mod (v a) (v b) = v c))Machine integers can always be multiplied, but the postcondition
+is now in terms of product modulo 2^n on mathematical integers
Euclidean division of a and b, with b non-zero
val div (a:t) (b:t{v b <> 0}) : Pure t
+ (requires (True))
+ (ensures (fun c -> v a / v b = v c))Euclidean remainder
+val rem (a:t) (b:t{v b <> 0}) : Pure t
+ (requires True)
+ (ensures (fun c -> FStar.UInt.mod (v a) (v b) = v c))The result is the modulus of a with respect to a non-zero b
++Also see FStar.BV
+
Bitwise logical conjunction
+val logand (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logand` v y = v z))Bitwise logical exclusive-or
+val logxor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logxor` v y == v z))Bitwise logical disjunction
+val logor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logor` v y == v z))Bitwise logical negation
+val lognot (x:t) : Pure t
+ (requires True)
+ (ensures (fun z -> lognot (v x) == v z))Shift right with zero fill, shifting at most the integer width
+val shift_right (a:t) (s:t) : Pure t
+ (requires (v s < n))
+ (ensures (fun c -> FStar.UInt.shift_right (v a) (v s) = v c))Shift left with zero fill, shifting at most the integer width
+val shift_left (a:t) (s:t) : Pure t
+ (requires (v s < n))
+ (ensures (fun c -> FStar.UInt.shift_left (v a) (v s) = v c))Equality
+let eq (a:t) (b:t) : Tot bool = eq #n (v a) (v b)Note, it is safe to also use the polymorphic decidable equality
+operator =
Greater than
+let gt (a:t) (b:t) : Tot bool = gt #n (v a) (v b)Greater than or equal
+let gte (a:t) (b:t) : Tot bool = gte #n (v a) (v b)Less than
+let lt (a:t) (b:t) : Tot bool = lt #n (v a) (v b)Less than or equal
+let lte (a:t) (b:t) : Tot bool = lte #n (v a) (v b)Unary negation
+inline_for_extraction
+let minus (a:t) = add_mod (lognot a) (uint_to_t 1)The maximum value for this type
+inline_for_extraction
+let n_minus_one = uint_to_t (n - 1)#set-options "--z3rlimit 80 --initial_fuel 1 --max_fuel 1"A constant-time way to compute the equality of +two machine integers.
+let eq_mask (a:t) (b:t)
+ : Pure t
+ (requires True)
+ (ensures (fun c -> (v a = v b ==> v c = pow2 n - 1) /\
+ (v a <> v b ==> v c = 0)))
+ = let x = logxor a b in
+ let minus_x = minus x in
+ let x_or_minus_x = logor x minus_x in
+ let xnx = shift_right x_or_minus_x n_minus_one in
+ let c = sub_mod xnx (uint_to_t 1) in
+ if a = b then
+ begin
+ logxor_self (v a);
+ lognot_lemma_1 #n;
+ logor_lemma_1 (v x);
+ assert (v x = 0 /\ v minus_x = 0 /\
+ v x_or_minus_x = 0 /\ v xnx = 0);
+ assert (v c = ones n)
+ end
+ else
+ begin
+ logxor_neq_nonzero (v a) (v b);
+ lemma_msb_pow2 #n (v (lognot x));
+ lemma_msb_pow2 #n (v minus_x);
+ lemma_minus_zero #n (v x);
+ assert (v c = FStar.UInt.zero n)
+ end;
+ cWith inspiration from https://git.zx2c4.com/WireGuard/commit/src/crypto/curve25519-hacl64.h?id=2e60bb395c1f589a398ec606d611132ef9ef764b
+Note, the branching on a=b is just for proof-purposes.
private
+val lemma_sub_msbs (a:t) (b:t)
+ : Lemma ((msb (v a) = msb (v b)) ==> (v a < v b <==> msb (v (sub_mod a b))))A constant-time way to compute the >= inequality of
+two machine integers.
let gte_mask (a:t) (b:t)
+ : Pure t
+ (requires True)
+ (ensures (fun c -> (v a >= v b ==> v c = pow2 n - 1) /\
+ (v a < v b ==> v c = 0)))
+ = let x = a in
+ let y = b in
+ let x_xor_y = logxor x y in
+ let x_sub_y = sub_mod x y in
+ let x_sub_y_xor_y = logxor x_sub_y y in
+ let q = logor x_xor_y x_sub_y_xor_y in
+ let x_xor_q = logxor x q in
+ let x_xor_q_ = shift_right x_xor_q n_minus_one in
+ let c = sub_mod x_xor_q_ (uint_to_t 1) in
+ lemma_sub_msbs x y;
+ lemma_msb_gte (v x) (v y);
+ lemma_msb_gte (v y) (v x);
+ c
+#reset-optionsWith inspiration from https://git.zx2c4.com/WireGuard/commit/src/crypto/curve25519-hacl64.h?id=0a483a9b431d87eca1b275463c632f8d5551978a
+unfold let op_Plus_Hat = add
+unfold let op_Plus_Question_Hat = add_underspec
+unfold let op_Plus_Percent_Hat = add_mod
+unfold let op_Subtraction_Hat = sub
+unfold let op_Subtraction_Question_Hat = sub_underspec
+unfold let op_Subtraction_Percent_Hat = sub_mod
+unfold let op_Star_Hat = mul
+unfold let op_Star_Question_Hat = mul_underspec
+unfold let op_Star_Percent_Hat = mul_mod
+unfold let op_Slash_Hat = div
+unfold let op_Percent_Hat = rem
+unfold let op_Hat_Hat = logxor
+unfold let op_Amp_Hat = logand
+unfold let op_Bar_Hat = logor
+unfold let op_Less_Less_Hat = shift_left
+unfold let op_Greater_Greater_Hat = shift_right
+unfold let op_Equals_Hat = eq
+unfold let op_Greater_Hat = gt
+unfold let op_Greater_Equals_Hat = gte
+unfold let op_Less_Hat = lt
+unfold let op_Less_Equals_Hat = lteIn decimal representation
+val to_string: t -> Tot stringIn hex representation (with leading 0x)
+val to_string_hex: t -> Tot stringIn fixed-width hex representation (left-padded with zeroes, no leading 0x)
+val to_string_hex_pad: t -> Tot stringval of_string: string -> Tot t#set-options "--lax"This private primitive is used internally by the +compiler to translate bounded integer constants +with a desugaring-time check of the size of the number, +rather than an expensive verification check. +Since it is marked private, client programs cannot call it directly +Since it is marked unfold, it eagerly reduces, +eliminating the verification overhead of the wrapper
+private
+unfold
+let __uint_to_t (x:int) : Tot t
+ = uint_to_t x
+#reset-optionsfsdoc: no-summary-found
-fsdoc: no-comment-found
+mk_int.sh, DO NOT EDIT DIRECTLY ***unfold let n = 64++For FStar.UIntN.fstp: anything that you fix/update here should be +reflected in
+FStar.IntN.fstp, which is mostly a copy-paste of +this module.Except, as compared to
+FStar.IntN.fstp, here:+
+- every occurrence of
+int_thas been replaced withuint_t+- every occurrence of
+@%has been replaced with%.- some functions (e.g., add_underspec, etc.) are only defined here, not on signed integers
+
++This module provides an abstract type for machine integers of a +given signedness and width. The interface is designed to be safe +with respect to arithmetic underflow and overflow.
+
++Note, we have attempted several times to re-design this module to +make it more amenable to normalization and to impose less overhead +on the SMT solver when reasoning about machine integer +arithmetic. The following github issue reports on the current +status of that work.
+ +
#set-options "--max_fuel 0 --max_ifuel 0"Abstract type of machine integers, with an underlying +representation using a bounded mathematical integer
+new val t : eqtypeA coercion that projects a bounded mathematical integer from a +machine integer
+val v (x:t) : Tot (uint_t n)A coercion that injects a bounded mathematical integers into a +machine integer
+val uint_to_t (x:uint_t n) : Pure t
+ (requires True)
+ (ensures (fun y -> v y = x))Injection/projection inverse
+val uv_inv (x : t) : Lemma
+ (ensures (uint_to_t (v x) == x))
+ [SMTPat (v x)]Projection/injection inverse
+val vu_inv (x : uint_t n) : Lemma
+ (ensures (v (uint_to_t x) == x))
+ [SMTPat (uint_to_t x)]An alternate form of the injectivity of the v projection
val v_inj (x1 x2: t): Lemma
+ (requires (v x1 == v x2))
+ (ensures (x1 == x2))Constants 0 and 1
+val zero : x:t{v x = 0}val one : x:t{v x = 1}Bounds-respecting addition
+val add (a:t) (b:t) : Pure t
+ (requires (size (v a + v b) n))
+ (ensures (fun c -> v a + v b = v c))The precondition enforces that the sum does not overflow, +expressing the bound as an addition on mathematical integers
+Underspecified, possibly overflowing addition:
+val add_underspec (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c ->
+ size (v a + v b) n ==> v a + v b = v c))The postcondition only enures that the result is the sum of the +arguments in case there is no overflow
+Addition modulo 2^n
val add_mod (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c -> FStar.UInt.add_mod (v a) (v b) = v c))Machine integers can always be added, but the postcondition is now
+in terms of addition modulo 2^n on mathematical integers
Bounds-respecting subtraction
+val sub (a:t) (b:t) : Pure t
+ (requires (size (v a - v b) n))
+ (ensures (fun c -> v a - v b = v c))The precondition enforces that the difference does not underflow, +expressing the bound as a difference on mathematical integers
+Underspecified, possibly overflowing subtraction:
+val sub_underspec (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c ->
+ size (v a - v b) n ==> v a - v b = v c))The postcondition only enures that the result is the difference of +the arguments in case there is no underflow
+Subtraction modulo 2^n
val sub_mod (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c -> FStar.UInt.sub_mod (v a) (v b) = v c))Machine integers can always be subtractd, but the postcondition is
+now in terms of subtraction modulo 2^n on mathematical integers
Bounds-respecting multiplication
+val mul (a:t) (b:t) : Pure t
+ (requires (size (v a * v b) n))
+ (ensures (fun c -> v a * v b = v c))The precondition enforces that the product does not overflow, +expressing the bound as a product on mathematical integers
+Underspecified, possibly overflowing product
+val mul_underspec (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c ->
+ size (v a * v b) n ==> v a * v b = v c))The postcondition only enures that the result is the product of +the arguments in case there is no overflow
+Multiplication modulo 2^n
val mul_mod (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c -> FStar.UInt.mul_mod (v a) (v b) = v c))Machine integers can always be multiplied, but the postcondition
+is now in terms of product modulo 2^n on mathematical integers
Euclidean division of a and b, with b non-zero
val div (a:t) (b:t{v b <> 0}) : Pure t
+ (requires (True))
+ (ensures (fun c -> v a / v b = v c))Euclidean remainder
+val rem (a:t) (b:t{v b <> 0}) : Pure t
+ (requires True)
+ (ensures (fun c -> FStar.UInt.mod (v a) (v b) = v c))The result is the modulus of a with respect to a non-zero b
++Also see FStar.BV
+
Bitwise logical conjunction
+val logand (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logand` v y = v z))Bitwise logical exclusive-or
+val logxor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logxor` v y == v z))Bitwise logical disjunction
+val logor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logor` v y == v z))Bitwise logical negation
+val lognot (x:t) : Pure t
+ (requires True)
+ (ensures (fun z -> lognot (v x) == v z))Shift right with zero fill, shifting at most the integer width
+val shift_right (a:t) (s:UInt32.t) : Pure t
+ (requires (UInt32.v s < n))
+ (ensures (fun c -> FStar.UInt.shift_right (v a) (UInt32.v s) = v c))Shift left with zero fill, shifting at most the integer width
+val shift_left (a:t) (s:UInt32.t) : Pure t
+ (requires (UInt32.v s < n))
+ (ensures (fun c -> FStar.UInt.shift_left (v a) (UInt32.v s) = v c))Equality
+let eq (a:t) (b:t) : Tot bool = eq #n (v a) (v b)Note, it is safe to also use the polymorphic decidable equality
+operator =
Greater than
+let gt (a:t) (b:t) : Tot bool = gt #n (v a) (v b)Greater than or equal
+let gte (a:t) (b:t) : Tot bool = gte #n (v a) (v b)Less than
+let lt (a:t) (b:t) : Tot bool = lt #n (v a) (v b)Less than or equal
+let lte (a:t) (b:t) : Tot bool = lte #n (v a) (v b)Unary negation
+inline_for_extraction
+let minus (a:t) = add_mod (lognot a) (uint_to_t 1)The maximum value for this type
+inline_for_extraction
+let n_minus_one = UInt32.uint_to_t (n - 1)#set-options "--z3rlimit 80 --initial_fuel 1 --max_fuel 1"A constant-time way to compute the equality of +two machine integers.
+let eq_mask (a:t) (b:t)
+ : Pure t
+ (requires True)
+ (ensures (fun c -> (v a = v b ==> v c = pow2 n - 1) /\
+ (v a <> v b ==> v c = 0)))
+ = let x = logxor a b in
+ let minus_x = minus x in
+ let x_or_minus_x = logor x minus_x in
+ let xnx = shift_right x_or_minus_x n_minus_one in
+ let c = sub_mod xnx (uint_to_t 1) in
+ if a = b then
+ begin
+ logxor_self (v a);
+ lognot_lemma_1 #n;
+ logor_lemma_1 (v x);
+ assert (v x = 0 /\ v minus_x = 0 /\
+ v x_or_minus_x = 0 /\ v xnx = 0);
+ assert (v c = ones n)
+ end
+ else
+ begin
+ logxor_neq_nonzero (v a) (v b);
+ lemma_msb_pow2 #n (v (lognot x));
+ lemma_msb_pow2 #n (v minus_x);
+ lemma_minus_zero #n (v x);
+ assert (v c = FStar.UInt.zero n)
+ end;
+ cWith inspiration from https://git.zx2c4.com/WireGuard/commit/src/crypto/curve25519-hacl64.h?id=2e60bb395c1f589a398ec606d611132ef9ef764b
+Note, the branching on a=b is just for proof-purposes.
private
+val lemma_sub_msbs (a:t) (b:t)
+ : Lemma ((msb (v a) = msb (v b)) ==> (v a < v b <==> msb (v (sub_mod a b))))A constant-time way to compute the >= inequality of
+two machine integers.
let gte_mask (a:t) (b:t)
+ : Pure t
+ (requires True)
+ (ensures (fun c -> (v a >= v b ==> v c = pow2 n - 1) /\
+ (v a < v b ==> v c = 0)))
+ = let x = a in
+ let y = b in
+ let x_xor_y = logxor x y in
+ let x_sub_y = sub_mod x y in
+ let x_sub_y_xor_y = logxor x_sub_y y in
+ let q = logor x_xor_y x_sub_y_xor_y in
+ let x_xor_q = logxor x q in
+ let x_xor_q_ = shift_right x_xor_q n_minus_one in
+ let c = sub_mod x_xor_q_ (uint_to_t 1) in
+ lemma_sub_msbs x y;
+ lemma_msb_gte (v x) (v y);
+ lemma_msb_gte (v y) (v x);
+ c
+#reset-optionsWith inspiration from https://git.zx2c4.com/WireGuard/commit/src/crypto/curve25519-hacl64.h?id=0a483a9b431d87eca1b275463c632f8d5551978a
+unfold let op_Plus_Hat = add
+unfold let op_Plus_Question_Hat = add_underspec
+unfold let op_Plus_Percent_Hat = add_mod
+unfold let op_Subtraction_Hat = sub
+unfold let op_Subtraction_Question_Hat = sub_underspec
+unfold let op_Subtraction_Percent_Hat = sub_mod
+unfold let op_Star_Hat = mul
+unfold let op_Star_Question_Hat = mul_underspec
+unfold let op_Star_Percent_Hat = mul_mod
+unfold let op_Slash_Hat = div
+unfold let op_Percent_Hat = rem
+unfold let op_Hat_Hat = logxor
+unfold let op_Amp_Hat = logand
+unfold let op_Bar_Hat = logor
+unfold let op_Less_Less_Hat = shift_left
+unfold let op_Greater_Greater_Hat = shift_right
+unfold let op_Equals_Hat = eq
+unfold let op_Greater_Hat = gt
+unfold let op_Greater_Equals_Hat = gte
+unfold let op_Less_Hat = lt
+unfold let op_Less_Equals_Hat = lteIn decimal representation
+val to_string: t -> Tot stringIn hex representation (with leading 0x)
+val to_string_hex: t -> Tot stringIn fixed-width hex representation (left-padded with zeroes, no leading 0x)
+val to_string_hex_pad: t -> Tot stringval of_string: string -> Tot t#set-options "--lax"This private primitive is used internally by the +compiler to translate bounded integer constants +with a desugaring-time check of the size of the number, +rather than an expensive verification check. +Since it is marked private, client programs cannot call it directly +Since it is marked unfold, it eagerly reduces, +eliminating the verification overhead of the wrapper
+private
+unfold
+let __uint_to_t (x:int) : Tot t
+ = uint_to_t x
+#reset-optionsfsdoc: no-summary-found
-fsdoc: no-comment-found
+mk_int.sh, DO NOT EDIT DIRECTLY ***unfold let n = 8++For FStar.UIntN.fstp: anything that you fix/update here should be +reflected in
+FStar.IntN.fstp, which is mostly a copy-paste of +this module.Except, as compared to
+FStar.IntN.fstp, here:+
+- every occurrence of
+int_thas been replaced withuint_t+- every occurrence of
+@%has been replaced with%.- some functions (e.g., add_underspec, etc.) are only defined here, not on signed integers
+
++This module provides an abstract type for machine integers of a +given signedness and width. The interface is designed to be safe +with respect to arithmetic underflow and overflow.
+
++Note, we have attempted several times to re-design this module to +make it more amenable to normalization and to impose less overhead +on the SMT solver when reasoning about machine integer +arithmetic. The following github issue reports on the current +status of that work.
+ +
#set-options "--max_fuel 0 --max_ifuel 0"Abstract type of machine integers, with an underlying +representation using a bounded mathematical integer
+new val t : eqtypeA coercion that projects a bounded mathematical integer from a +machine integer
+val v (x:t) : Tot (uint_t n)A coercion that injects a bounded mathematical integers into a +machine integer
+val uint_to_t (x:uint_t n) : Pure t
+ (requires True)
+ (ensures (fun y -> v y = x))Injection/projection inverse
+val uv_inv (x : t) : Lemma
+ (ensures (uint_to_t (v x) == x))
+ [SMTPat (v x)]Projection/injection inverse
+val vu_inv (x : uint_t n) : Lemma
+ (ensures (v (uint_to_t x) == x))
+ [SMTPat (uint_to_t x)]An alternate form of the injectivity of the v projection
val v_inj (x1 x2: t): Lemma
+ (requires (v x1 == v x2))
+ (ensures (x1 == x2))Constants 0 and 1
+val zero : x:t{v x = 0}val one : x:t{v x = 1}Bounds-respecting addition
+val add (a:t) (b:t) : Pure t
+ (requires (size (v a + v b) n))
+ (ensures (fun c -> v a + v b = v c))The precondition enforces that the sum does not overflow, +expressing the bound as an addition on mathematical integers
+Underspecified, possibly overflowing addition:
+val add_underspec (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c ->
+ size (v a + v b) n ==> v a + v b = v c))The postcondition only enures that the result is the sum of the +arguments in case there is no overflow
+Addition modulo 2^n
val add_mod (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c -> FStar.UInt.add_mod (v a) (v b) = v c))Machine integers can always be added, but the postcondition is now
+in terms of addition modulo 2^n on mathematical integers
Bounds-respecting subtraction
+val sub (a:t) (b:t) : Pure t
+ (requires (size (v a - v b) n))
+ (ensures (fun c -> v a - v b = v c))The precondition enforces that the difference does not underflow, +expressing the bound as a difference on mathematical integers
+Underspecified, possibly overflowing subtraction:
+val sub_underspec (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c ->
+ size (v a - v b) n ==> v a - v b = v c))The postcondition only enures that the result is the difference of +the arguments in case there is no underflow
+Subtraction modulo 2^n
val sub_mod (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c -> FStar.UInt.sub_mod (v a) (v b) = v c))Machine integers can always be subtractd, but the postcondition is
+now in terms of subtraction modulo 2^n on mathematical integers
Bounds-respecting multiplication
+val mul (a:t) (b:t) : Pure t
+ (requires (size (v a * v b) n))
+ (ensures (fun c -> v a * v b = v c))The precondition enforces that the product does not overflow, +expressing the bound as a product on mathematical integers
+Underspecified, possibly overflowing product
+val mul_underspec (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c ->
+ size (v a * v b) n ==> v a * v b = v c))The postcondition only enures that the result is the product of +the arguments in case there is no overflow
+Multiplication modulo 2^n
val mul_mod (a:t) (b:t) : Pure t
+ (requires True)
+ (ensures (fun c -> FStar.UInt.mul_mod (v a) (v b) = v c))Machine integers can always be multiplied, but the postcondition
+is now in terms of product modulo 2^n on mathematical integers
Euclidean division of a and b, with b non-zero
val div (a:t) (b:t{v b <> 0}) : Pure t
+ (requires (True))
+ (ensures (fun c -> v a / v b = v c))Euclidean remainder
+val rem (a:t) (b:t{v b <> 0}) : Pure t
+ (requires True)
+ (ensures (fun c -> FStar.UInt.mod (v a) (v b) = v c))The result is the modulus of a with respect to a non-zero b
++Also see FStar.BV
+
Bitwise logical conjunction
+val logand (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logand` v y = v z))Bitwise logical exclusive-or
+val logxor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logxor` v y == v z))Bitwise logical disjunction
+val logor (x:t) (y:t) : Pure t
+ (requires True)
+ (ensures (fun z -> v x `logor` v y == v z))Bitwise logical negation
+val lognot (x:t) : Pure t
+ (requires True)
+ (ensures (fun z -> lognot (v x) == v z))Shift right with zero fill, shifting at most the integer width
+val shift_right (a:t) (s:UInt32.t) : Pure t
+ (requires (UInt32.v s < n))
+ (ensures (fun c -> FStar.UInt.shift_right (v a) (UInt32.v s) = v c))Shift left with zero fill, shifting at most the integer width
+val shift_left (a:t) (s:UInt32.t) : Pure t
+ (requires (UInt32.v s < n))
+ (ensures (fun c -> FStar.UInt.shift_left (v a) (UInt32.v s) = v c))Equality
+let eq (a:t) (b:t) : Tot bool = eq #n (v a) (v b)Note, it is safe to also use the polymorphic decidable equality
+operator =
Greater than
+let gt (a:t) (b:t) : Tot bool = gt #n (v a) (v b)Greater than or equal
+let gte (a:t) (b:t) : Tot bool = gte #n (v a) (v b)Less than
+let lt (a:t) (b:t) : Tot bool = lt #n (v a) (v b)Less than or equal
+let lte (a:t) (b:t) : Tot bool = lte #n (v a) (v b)Unary negation
+inline_for_extraction
+let minus (a:t) = add_mod (lognot a) (uint_to_t 1)The maximum value for this type
+inline_for_extraction
+let n_minus_one = UInt32.uint_to_t (n - 1)#set-options "--z3rlimit 80 --initial_fuel 1 --max_fuel 1"A constant-time way to compute the equality of +two machine integers.
+let eq_mask (a:t) (b:t)
+ : Pure t
+ (requires True)
+ (ensures (fun c -> (v a = v b ==> v c = pow2 n - 1) /\
+ (v a <> v b ==> v c = 0)))
+ = let x = logxor a b in
+ let minus_x = minus x in
+ let x_or_minus_x = logor x minus_x in
+ let xnx = shift_right x_or_minus_x n_minus_one in
+ let c = sub_mod xnx (uint_to_t 1) in
+ if a = b then
+ begin
+ logxor_self (v a);
+ lognot_lemma_1 #n;
+ logor_lemma_1 (v x);
+ assert (v x = 0 /\ v minus_x = 0 /\
+ v x_or_minus_x = 0 /\ v xnx = 0);
+ assert (v c = ones n)
+ end
+ else
+ begin
+ logxor_neq_nonzero (v a) (v b);
+ lemma_msb_pow2 #n (v (lognot x));
+ lemma_msb_pow2 #n (v minus_x);
+ lemma_minus_zero #n (v x);
+ assert (v c = FStar.UInt.zero n)
+ end;
+ cWith inspiration from https://git.zx2c4.com/WireGuard/commit/src/crypto/curve25519-hacl64.h?id=2e60bb395c1f589a398ec606d611132ef9ef764b
+Note, the branching on a=b is just for proof-purposes.
private
+val lemma_sub_msbs (a:t) (b:t)
+ : Lemma ((msb (v a) = msb (v b)) ==> (v a < v b <==> msb (v (sub_mod a b))))A constant-time way to compute the >= inequality of
+two machine integers.
let gte_mask (a:t) (b:t)
+ : Pure t
+ (requires True)
+ (ensures (fun c -> (v a >= v b ==> v c = pow2 n - 1) /\
+ (v a < v b ==> v c = 0)))
+ = let x = a in
+ let y = b in
+ let x_xor_y = logxor x y in
+ let x_sub_y = sub_mod x y in
+ let x_sub_y_xor_y = logxor x_sub_y y in
+ let q = logor x_xor_y x_sub_y_xor_y in
+ let x_xor_q = logxor x q in
+ let x_xor_q_ = shift_right x_xor_q n_minus_one in
+ let c = sub_mod x_xor_q_ (uint_to_t 1) in
+ lemma_sub_msbs x y;
+ lemma_msb_gte (v x) (v y);
+ lemma_msb_gte (v y) (v x);
+ c
+#reset-optionsWith inspiration from https://git.zx2c4.com/WireGuard/commit/src/crypto/curve25519-hacl64.h?id=0a483a9b431d87eca1b275463c632f8d5551978a
+unfold let op_Plus_Hat = add
+unfold let op_Plus_Question_Hat = add_underspec
+unfold let op_Plus_Percent_Hat = add_mod
+unfold let op_Subtraction_Hat = sub
+unfold let op_Subtraction_Question_Hat = sub_underspec
+unfold let op_Subtraction_Percent_Hat = sub_mod
+unfold let op_Star_Hat = mul
+unfold let op_Star_Question_Hat = mul_underspec
+unfold let op_Star_Percent_Hat = mul_mod
+unfold let op_Slash_Hat = div
+unfold let op_Percent_Hat = rem
+unfold let op_Hat_Hat = logxor
+unfold let op_Amp_Hat = logand
+unfold let op_Bar_Hat = logor
+unfold let op_Less_Less_Hat = shift_left
+unfold let op_Greater_Greater_Hat = shift_right
+unfold let op_Equals_Hat = eq
+unfold let op_Greater_Hat = gt
+unfold let op_Greater_Equals_Hat = gte
+unfold let op_Less_Hat = lt
+unfold let op_Less_Equals_Hat = lteIn decimal representation
+val to_string: t -> Tot stringIn hex representation (with leading 0x)
+val to_string_hex: t -> Tot stringIn fixed-width hex representation (left-padded with zeroes, no leading 0x)
+val to_string_hex_pad: t -> Tot stringval of_string: string -> Tot t#set-options "--lax"This private primitive is used internally by the +compiler to translate bounded integer constants +with a desugaring-time check of the size of the number, +rather than an expensive verification check. +Since it is marked private, client programs cannot call it directly +Since it is marked unfold, it eagerly reduces, +eliminating the verification overhead of the wrapper
+private
+unfold
+let __uint_to_t (x:int) : Tot t
+ = uint_to_t x
+#reset-options
+unfold inline_for_extraction type byte = tfsdoc: no-summary-found
-fsdoc: no-comment-found
+Type declarations
+new val socket: eqtype
+new val sock_in_channel: Type0
+new val sock_out_channel: Type0
+new val udpListener: Type0Server side
+val listen: string -> nat -> EXT udpListener
+val accept: udpListener -> EXT socket
+val stop: udpListener -> EXT unitClient side
+val connect: string -> nat -> EXT socketInput/Output
+val recv: socket -> nat -> EXT (optResult string bytes)
+val send: socket -> bytes -> EXT (optResult string unit)
+val close: socket -> EXT unitHelper functions
+val socket_split: socket -> EXT (sock_in_channel * sock_out_channel)
+val flush: sock_out_channel -> EXT unitUnimplemented
+assume val connectTimeout: nat -> string -> nat -> EXT socket +assume val acceptTimeout: nat -> tcpListener -> EXT socket
+ diff --git a/docs/FStar.Universe.html b/docs/FStar.Universe.html index 1f6a6e7..72a7385 100644 --- a/docs/FStar.Universe.html +++ b/docs/FStar.Universe.html @@ -1,63 +1,47 @@ - - + + - - -fsdoc: no-summary-found
-fsdoc: no-comment-found
- This module implements some basic facilities to raise the universe of a type *
- * The type [raise_t a] is supposed to be isomorphic to [a] but in a higher *
- * universe. The two functions [raise_val] and [dowgrade_val] allow to coerce *
- * from [a] to [raise_t a] and back. *val raise_t:Unidentified product: [(Type a)] (Type max a b)[raise_t a] is an isomorphic copy of [a] (living in universe 'ua) in universe [max 'ua 'ub] *
-val raise_val:Unidentified product: [#a:(Type a)] Unidentified product: [x:a] raise_t a b a[raise_val x] injects a value [x] of type [a] to [raise_t a] *
-val downgrade_val:Unidentified product: [#a:(Type a)] Unidentified product: [x:raise_t a b a] a[downgrade_val x] projects a value [x] of type [raise_t a] to [a] *
+This module implements some basic facilities to raise the universe of a type *
+raise_t a is supposed to be isomorphic to a but in a higher *raise_val and dowgrade_val allow to coerce *a to raise_t a and back. *raise_t a is an isomorphic copy of a (living in universe 'ua) in universe max 'ua 'ub *
val raise_t : Type u#a -> Type u#(max a b)raise_val x injects a value x of type a to raise_t a *
val raise_val : #a:Type u#a -> x:a -> raise_t u#a u#b adowngrade_val x projects a value x of type raise_t a to a *
val downgrade_val : #a:Type u#a -> x:raise_t u#a u#b a -> aval downgrade_val_raise_val
+ (#a: Type u#a)
+ (x: a)
+: Lemma
+ (downgrade_val u#a u#b (raise_val x) == x)
+ [SMTPat (downgrade_val u#a u#b (raise_val x))]val raise_val_downgrade_val
+ (#a: Type u#a)
+ (x: raise_t u#a u#b a)
+: Lemma
+ (raise_val (downgrade_val x) == x)
+ [SMTPat (raise_val u#a u#b (downgrade_val x))]let lift_dom #a #b (q:a -> b) : raise_t a -> b =
+ fun v -> q (downgrade_val v)let lift_codom #a #b (q:a -> b) : a -> raise_t b =
+ fun v -> raise_val (q v)fsdoc: no-summary-found
-fsdoc: no-comment-found
+2016-11-22: the following MUST be defined here AFTER the above open', since they are used in op_At_Plus_At` below
let op_Plus_Plus x y = TSet.union x y
+let op_Plus_Plus_Hat x y = x ++ (TSet.singleton y)
+let op_Hat_Plus_Hat x y = (TSet.singleton x) ++ (TSet.singleton y)let op_At_Plus_At (#a:Type) (#b:Type) (x:reference a) (y:reference b) =
+ Set.union (Set.singleton (as_addr x)) (Set.singleton (as_addr y))
+let op_Plus_Plus_At (#a:Type) (x:Set.set nat) (y:reference a) = Set.union x (Set.singleton (as_addr y))This type represents the set of verification-relevant options used +to check a particular definition. It can be read from tactics via +sigelt_opts and set via the check_with attribute.
+type vconfig = {
+ initial_fuel : int;
+ max_fuel : int;
+ initial_ifuel : int;
+ max_ifuel : int;
+ detail_errors : bool;
+ detail_hint_replay : bool;
+ no_smt : bool;
+ quake_lo : int;
+ quake_hi : int;
+ quake_keep : bool;
+ retry : bool;
+ smtencoding_elim_box : bool;
+ smtencoding_nl_arith_repr : string;
+ smtencoding_l_arith_repr : string;
+ smtencoding_valid_intro : bool;
+ smtencoding_valid_elim : bool;
+ tcnorm : bool;
+ no_plugins : bool;
+ no_tactics : bool;
+ vcgen_optimize_bind_as_seq : option string;
+ z3cliopt : list string;
+ z3refresh : bool;
+ z3rlimit : int;
+ z3rlimit_factor : int;
+ z3seed : int;
+ trivial_pre_for_unannotated_effectful_fns : bool;
+ reuse_hint_for : option string;
+}This type, and the whole module, mirror FStar.VConfig in F* sources.
+ + + diff --git a/docs/FStar.Vector.Base.html b/docs/FStar.Vector.Base.html index 126b5d4..98ca9bb 100644 --- a/docs/FStar.Vector.Base.html +++ b/docs/FStar.Vector.Base.html @@ -1,57 +1,368 @@ - - + + - - -fsdoc: no-summary-found
-fsdoc: no-comment-found
-
- Abstractly, a `vec a l` is just a sequence whose length is `U32.v l`.
- `reveal` and `hide` build an isomorphism establishing this
-*A library for vectors, i.e., immutable arrays, whose length is +representable by a machine integer, FStar.UInt32.t.
+This is closely related to FStar.Seq, with the following main +differences:
+The type raw a l: A raw vector
Raw vectors receive special treatment during extraction,
+especially by KreMLin, which extracts a vector to a raw C
+pointer. When extracing to OCaml, a raw a l is a
+Batteries.Vect t a
The length of a vector is representable in a U32.t
+The interface is designed around a length-indexed type: this +enables the compilation to raw pointers, since this ensures +that all functions that manipulate vectors always have a U32 +variable describing that vector's length in scope.
+A length-indexed interface is also suitable for clients for whom +proving properties about the length is a primary concern: the +signatures in this interface carry intrinsic proofs about length +properties, simplifying proof obligations in client code.
+Raw vectors lack decidable equality (since that cannot be +implemented given the representation choice in KreMLin)
+The type t a: A dynamically sized vector
Conceptually, a t a is a pair of a len:U32.t and a raw a len. They are implemented as such by KreMLin. When extracting
+to OCaml, t a is identical to raw a _, i.e., it is still
+extracted to a Batteries.Vect.t a
Unlike raw vectors, t a supports decidable equality when it
+is supported by a. This is the main reason t a is provided
+at an abstract type, rather than being exposed as a pair of a
+U32 and a raw vector, since the latter does not support
+decidable equality.
@summary Immutable vectors whose length is less than pow2 32
U32
+S
+//////////////////////////////////////////////////////////////////////////////
+++The basic model of raw vectors as u32-length sequences
+
//////////////////////////////////////////////////////////////////////////////
+++The length of a vector fits in 32 bits
+
let len_t = U32.t++A raw vector.
++
+- +
+vector a nis extracted to ana*in C by KreMLin- Does not support decidable equality
+
val raw:
+ a:Type u#a
+ -> l:len_t
+ -> Type u#a++A convenience to use
+natfor the length of vector in specs and proofs
let raw_length (#a:Type) (#l:len_t) (v:raw a l) : GTot nat = U32.v lAbstractly, a `vec a l` is just a sequence whose length is `U32.v l`.
+`reveal` and `hide` build an isomorphism establishing this
+
+val reveal:
+ #a:Type
+ -> #l:len_t
+ -> v:raw a l
+ -> GTot (s:S.seq a{S.length s = raw_length v})val hide:
+ #a:Type
+ -> s:S.seq a{S.length s < pow2 32}
+ -> GTot (raw a (U32.uint_to_t (S.length s)))val hide_reveal:
+ #a:Type
+ -> #l:len_t
+ -> v:raw a l
+ -> Lemma (ensures (hide (reveal v) == v))
+ [SMTPat (reveal v)]val reveal_hide:
+ #a:Type
+ -> s:S.seq a{S.length s < pow2 32}
+ -> Lemma (ensures (reveal (hide s) == s))
+ [SMTPat (hide s)]++Extensional equality for vectors
+
let equal (#a:Type) (#l:len_t) (v1:raw a l) (v2:raw a l) =
+ Seq.equal (reveal v1) (reveal v2)++Extensional equality can be used to prove syntactic equality
+
val extensionality:
+ #a:Type
+ -> #l:len_t
+ -> v1:raw a l
+ -> v2:raw a l
+ -> Lemma (requires (equal v1 v2))
+ (ensures (v1 == v2))//////////////////////////////////////////////////////////////////////////////
+++end of the basic model
+
//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+++A small set of basic operations on raw vectors, corresponding to the operations +on sequences. Other operations can be derived from these, as we do for seq. +-- init, index, update, append, slice
+
//////////////////////////////////////////////////////////////////////////////
++++
index_t v: is the type of a within-bounds index ofv
let index_t (#a:Type) (#l:len_t) (v:raw a l) =
+ m:len_t{U32.v m < U32.v l}+++
init l contents: +initialize anl-sized vector usingcontents ifor theith element
val init:
+ #a:Type
+ -> l:len_t
+ -> contents: (i:nat { i < U32.v l } -> Tot a)
+ -> Tot (raw a l)+++
index v i: get theith element ofv
val index:
+ #a:Type
+ -> #l:len_t
+ -> v:raw a l
+ -> i:index_t v
+ -> Tot a+++
v.i`` is shorthand forindex v i
unfold let op_String_Access #a #l = index #a #l+++
update v i x: +- a new vector that differs fromvonly at indexi, where it containsx. +- Incurs a full copy in KreMLin +- In OCaml, the new vector shares as much as possible withv
val update:
+ #a:Type
+ -> #l:len_t
+ -> v:raw a l
+ -> i:index_t v
+ -> x:a
+ -> Tot (raw a l)+++
v.i<- xis shorthand forupdate v i x
unfold let op_String_Assignment #a #l = update #a #l+++
append v1 v2: +- requires proving that the sum of the lengths of v1 and v2 still fit in a u32 +- Incurs a full copy in KreMLin +- Amortized constant time in OCaml
val append:
+ #a:Type
+ -> #l1:len_t
+ -> #l2:len_t
+ -> v1:raw a l1
+ -> v2:raw a l2{UInt.size U32.(v l1 + v l2) U32.n}
+ -> Tot (raw a U32.(l1 +^ l2))+++
v1 @| v2: shorthand forappend v1 v2
unfold let (@|) #a #l1 #l2 = append #a #l1 #l2+++
sub v i j: +- the sub-vector ofvstarting from indexiup to, but not including,j+- Constant time in KreMLin (just an addition on a pointer) +- Worst-case (log l) time in OCaml
val sub:
+ #a:Type
+ -> #l:len_t
+ -> v:raw a l
+ -> i:len_t
+ -> j:len_t{U32.(v i <= v j /\ v j <= v l)}
+ -> Tot (raw a U32.(j -^ i))//////////////////////////////////////////////////////////////////////////////
+++Lemmas about the basic operations, all rather boring +-- Each is just a lifting specifying the corresponding operation on seq
+
//////////////////////////////////////////////////////////////////////////////
+val reveal_init:
+ #a:Type
+ -> l:len_t
+ -> contents: (i:nat { i < U32.v l } -> Tot a)
+ -> Lemma
+ (ensures (reveal (init l contents) == Seq.init (U32.v l) contents))
+ [SMTPat (init l contents)]val reveal_index:
+ #a:Type
+ -> #l:len_t
+ -> v:raw a l
+ -> i:index_t v
+ -> Lemma
+ (ensures (v.[i] == Seq.index (reveal v) (U32.v i)))
+ [SMTPat (v.[i])]val reveal_update:
+ #a:Type
+ -> #l:len_t
+ -> v:raw a l
+ -> i:index_t v
+ -> x:a
+ -> Lemma
+ (ensures (reveal (v.[i] <- x) == Seq.upd (reveal v) (U32.v i) x))
+ [SMTPat (v.[i] <- x)]val reveal_append:
+ #a:Type
+ -> #l1:len_t
+ -> #l2:len_t
+ -> v1:raw a l1
+ -> v2:raw a l2{UInt.size U32.(v l1 + v l2) U32.n}
+ -> Lemma
+ (ensures (reveal (v1 @| v2) == Seq.append (reveal v1) (reveal v2)))
+ [SMTPat (v1 @| v2)]val reveal_sub:
+ #a:Type
+ -> #l:len_t
+ -> v:raw a l
+ -> i:len_t
+ -> j:len_t{U32.(v i <= v j /\ v j <= v l)}
+ -> Lemma
+ (ensures (reveal (sub v i j) == S.slice (reveal v) (U32.v i) (U32.v j)))
+ [SMTPat (sub v i j)]//////////////////////////////////////////////////////////////////////////////
+++Now, we have
+Vector.Base.t, abstractly, a raw vector paired with its u32 length
//////////////////////////////////////////////////////////////////////////////
+val t:
+ a:Type u#a
+ -> Type u#a++Unlike raw vectors, t-vectors support decidable equality
+
val t_has_eq:
+ a:Type u#a
+ -> Lemma
+ (requires (hasEq a))
+ (ensures (hasEq (t a)))
+ [SMTPat (hasEq (t a))]++The length of a t-vector is a dynamically computable u32
+
val len:
+ #a:Type
+ -> t a
+ -> len_t++A convenience to access the length of a t-vector as a nat
+
[@@"deprecated: this will be moved to the ghost effect"]
+let length (#a:Type) (x:t a) : nat = U32.v (len x)++Access the underlying raw vector
+
val as_raw:
+ #a:Type
+ -> x:t a
+ -> raw a (len x)++Promote a raw vector
+
val from_raw:
+ #a:Type
+ -> #l:len_t
+ -> v:raw a l
+ -> x:t a{len x = l}++as_raw and from_raw are mutual inverses
+
val as_raw_from_raw:
+ #a:Type
+ -> #l:len_t
+ -> v:raw a l
+ -> Lemma (ensures (as_raw (from_raw v) == v))
+ [SMTPat (from_raw v)]val from_raw_as_raw:
+ #a:Type
+ -> x:t a
+ -> Lemma (ensures (from_raw (as_raw x) == x))
+ [SMTPat (as_raw x)]+++
v.(i)accesses the ith element of v
unfold
+let op_Array_Access
+ (#a:Type)
+ (x:t a)
+ (i:index_t (as_raw x))
+ : Tot a
+ = (as_raw x).[i]+++
v.(i) <- xis a new t-vector that differs from v only at i
unfold
+let op_Array_Assignment
+ (#a:Type)
+ (x:t a)
+ (i:index_t (as_raw x))
+ (v:a)
+ : Tot (t a)
+ = from_raw ((as_raw x).[i] <- v)+++
v1 @@ v2: appending t-vectors
unfold
+let (@@)
+ (#a:Type)
+ (x1:t a)
+ (x2:t a{UInt.size (length x1 + length x2) U32.n})
+ : Tot (t a)
+ = from_raw (as_raw x1 @| as_raw x2)+++
slice v i j: +the sub-vector ofvstarting from indexiup to, but not including,j
unfold
+let slice
+ (#a:Type)
+ (x:t a)
+ (i:len_t)
+ (j:len_t{U32.(v i <= v j /\ v j <= length x)})
+ : Tot (t a)
+ = from_raw (sub (as_raw x) i j)val dummy : unitfsdoc: no-summary-found
-fsdoc: no-comment-found
+S
+U32
+++This coercion seems to be necessary in some places
+For example, when trying to treat a
+raw a (l1 +^ l2)+as araw a (m1 +^ m2)+F* type inference tries matches on the head symbol of the index +and tries to provel1 = m1 /\ l2 = m2+which is often too strong. +This coercion is a workaround for in such cases
unfold
+let coerce
+ (#a:Type)
+ (#l:len_t)
+ (v:raw a l)
+ (m:len_t{l == m})
+ : Tot (raw a m)
+ = v++An abbreviation that states that some binary arithmetic +operation on len_t's respects bouns
+
unfold
+let ok
+ (op:int -> int -> int)
+ (l1:len_t)
+ (l2:len_t)
+ : Type
+ = UInt.size U32.(op (v l1) (v l2)) U32.n++Most lemmas from FStar.Seq.Properties can just be lifted +to vectors, although the lengths have to be bounds checked
+
let append_inj
+ (#a:Type)
+ (#l1:len_t)
+ (#l2:len_t)
+ (#m1:len_t)
+ (#m2:len_t)
+ (u1:raw a l1)
+ (u2:raw a l2{ok (+) l1 l2})
+ (v1:raw a m1)
+ (v2:raw a m2{ok (+) m1 m2})
+ : Lemma
+ (requires (let open U32 in
+ m1 +^ m2 = l1 +^ l2 /\
+ equal (u1@|u2) (coerce (v1@|v2) (l1 +^ l2)) /\
+ (l1 == m1 \/ l2 == m2)))
+ (ensures (l1 = m1 /\
+ l2 = m2 /\
+ equal u1 v1 /\
+ equal u2 v2))
+ = FStar.Seq.lemma_append_inj (reveal u1) (reveal u2) (reveal v1) (reveal v2)let head (#a:Type) (#l:len_t{l <> 0ul}) (v:raw a l)
+ : Tot a
+ = v.[0ul]let tail (#a:Type) (#l:len_t{l <> 0ul}) (v:raw a l)
+ : Tot (raw a U32.(l -^ 1ul))
+ = sub v 1ul llet head_append
+ (#a:Type)
+ (#l1:len_t)
+ (#l2:len_t)
+ (v1:raw a l1{l1 <> 0ul})
+ (v2:raw a l2{ok (+) l1 l2})
+ : Lemma
+ (ensures (head (v1@|v2) == head v1))
+ = ()let tail_append
+ (#a:Type)
+ (#l1:len_t)
+ (#l2:len_t)
+ (v1:raw a l1{l1 <> 0ul})
+ (v2:raw a l2{ok (+) l1 l2})
+ : Lemma
+ (ensures (tail (v1@|v2) == tail v1@|v2))
+ = Seq.lemma_tail_append (reveal v1) (reveal v2)++ diff --git a/docs/FStar.Vector.html b/docs/FStar.Vector.html index 864a1c1..5d2a694 100644 --- a/docs/FStar.Vector.html +++ b/docs/FStar.Vector.html @@ -1,16 +1,19 @@ - - + + - - -and so on ...
+
fsdoc: no-summary-found
-fsdoc: no-comment-found
+fsdoc: no-summary-found
-fsdoc: no-comment-found
+Copyright 2015 Chantal Keller and Catalin Hritcu, Microsoft Research and Inria
+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.
+Defining accessibility predicates and well-founded recursion like in Coq +https://coq.inria.fr/library/Coq.Init.Wf.html
+Opens module FStar.Preorder
+The accessibility relation
+noeq
+type acc (#a:Type) (r:relation a) (x:a) : Type =
+ | AccIntro : (y:a -> r y x -> acc r y) -> acc r xlet well_founded (#a:Type) (r:relation a) = x:a -> acc r xlet acc_inv (#aa:Type) (#r:relation aa) (x:aa) (a:acc r x)
+ : (e:(y:aa -> r y x -> acc r y){e << a})
+ = match a with | AccIntro h1 -> h1let rec fix_F (#aa:Type) (#r:relation aa) (#p:(aa -> Type))
+ (f: (x:aa -> (y:aa -> r y x -> p y) -> p x))
+ (x:aa) (a:acc r x)
+ : Tot (p x) (decreases a)
+ = f x (fun y h -> fix_F f y (acc_inv x a y h))let fix (#aa:Type) (#r:relation aa) (rwf:well_founded r)
+ (p:aa -> Type) (f:(x:aa -> (y:aa -> r y x -> p y) -> p x))
+ (x:aa)
+ : p x
+ = fix_F f x (rwf x)[@@ erasable]
+noeq
+type acc_g (#a:Type) (r:relation a) (x:a) : Type =
+ | AccIntro_g : (y:a -> r y x -> acc_g r y) -> acc_g r xtype is_well_founded (#a:Type) (rel:relation a) =
+ forall (x:a). squash (acc_g rel x)type well_founded_relation (a:Type) = rel:relation a{is_well_founded rel}#push-options "--warn_error -271"
+unfold
+let as_well_founded (#a:Type) (#rel:relation a) (f:(x:a -> acc_g rel x))
+ : well_founded_relation a
+ = let aux (x:a)
+ : Lemma (squash (acc_g rel x))
+ [SMTPat ()]
+ = FStar.Squash.return_squash (f x) in
+ rel
+#pop-optionsOpens module FStar.IndefiniteDescription
+Proofs that subrelation and inverse image commute with well-foundedness
+Reference: Constructing Recursion Operators in Type Theory, L. Paulson JSC (1986) 2, 325-355
+let subrelation_wf (#a:Type) (#r #sub_r:relation a)
+ (sub_w:(x:a -> y:a -> sub_r x y -> r x y))
+ (r_wf:well_founded r)
+ : well_founded sub_r
+ = let rec aux (x:a) (acc_r:acc r x) : Tot (acc sub_r x) (decreases acc_r) =
+ AccIntro (fun y sub_r_y_x ->
+ aux y
+ (match acc_r with
+ | AccIntro f -> f y (sub_w y x sub_r_y_x))) in
+ fun x -> aux x (r_wf x)#push-options "--warn_error -271"
+let subrelation_squash_wf (#a:Type) (#r #sub_r:relation a)
+ (sub_w:(x:a -> y:a -> sub_r x y -> squash (r x y)))
+ (r_wf:well_founded r)
+ : Lemma (is_well_founded sub_r)
+ = let aux (x:a)
+ : Lemma (squash (acc_g sub_r x))
+ [SMTPat ()]
+ = let rec acc_y (x:a) (acc_r:acc r x) (y:a) (p:sub_r y x)
+ : Tot (acc_g sub_r y)
+ (decreases acc_r)
+ = AccIntro_g (acc_y y
+ (match acc_r with
+ | AccIntro f -> f y (elim_squash (sub_w y x p)))) in
+ Squash.return_squash (AccIntro_g (acc_y x (r_wf x)))
+ in
+ ()
+#pop-optionsunfold
+let subrelation_as_wf (#a:Type) (#r #sub_r:relation a)
+ (sub_w:(x:a -> y:a -> sub_r x y -> squash (r x y)))
+ (r_wf:well_founded r)
+ : well_founded_relation a
+ = subrelation_squash_wf sub_w r_wf;
+ sub_rlet inverse_image (#a #b:Type) (r_b:relation b) (f:a -> b) : relation a =
+ fun x y -> r_b (f x) (f y)let inverse_image_wf (#a #b:Type) (#r_b:relation b)
+ (f:a -> b)
+ (r_b_wf:well_founded r_b)
+ : well_founded (inverse_image r_b f)
+ = let rec aux (x:a) (acc_r_b:acc r_b (f x))
+ : Tot (acc (inverse_image r_b f) x)
+ (decreases acc_r_b) =
+ let get_acc_r_b_y (y:a) (p:(inverse_image r_b f) y x)
+ : Tot (acc_r_b_y:acc r_b (f y){acc_r_b_y << acc_r_b})
+ = match acc_r_b with
+ | AccIntro g -> g (f y) p in
+ AccIntro (fun y p -> aux y (get_acc_r_b_y y p)) in
+ fun x -> aux x (r_b_wf (f x))FStar.Algebra.CommMonoid.Equiv
+ + + + + + +FStar.FunctionalExtensionality
+ - - - + + - - + + + - + - - @@ -63,111 +67,79 @@ + - - - - + +FStar.PropositionalExtensionality
- + + + - + - -FStar.Reflection.Derived.Lemmas
+FStar.Reflection.Derived.Lemmas
- +FStar.ReflexiveTransitiveClosure
+ + - + + - -FStar.Tactics.CanonCommMonoidSimple
FStar.Tactics.CanonCommSemiring
+ + + - + - - + + + - - - - - - - - - - - -FStar.FunctionalExtensionality
- - - - - - - - - - - - - - - - -FStar.ReflexiveTransitiveClosure
- - - - - - - - - + From aa586ca61db5f28e928381d07cf7a93f99b219a0 Mon Sep 17 00:00:00 2001 From: Mark GritterFStar.Algebra.CommMonoid.Equiv
- - - - +FStar.BV -- This module defines an abstract type of length-indexed bit vectors.
+FStar.BaseTypes -- This module aggregates commonly used primitive type constants into a single module, providing abbreviations for them.
+FStar.BigOps -- This library provides propositional connectives over finite sets expressed as lists, aka "big operators", in analogy with LaTeX usage for \bigand, \bigor, etc.
+FStar.BitVector -- This module defines a bit vector as a sequence of booleans of a given length, and provides various utilities.
- - - - - +FStar.Char -- This module provides the char type, an abstract type representing UTF-8 characters.
FStar.Classical -- This module provides various utilities to manipulate the squashed logical connectives ==>, /\, \/, forall, exists and ==, defined in Prims in terms of the squash type.
FStar.Classical.Sugar -- This module provides a few combinators that are targeted by the desugaring phase of the F* front end
+FStar.Date -- A module providing primitives for dates and times
+FStar.DependentMap -- This module provides an abstract type of maps whose co-domain depends on the value of each key.
- +FStar.Endianness -- A library of lemmas for reasoning about sequences of machine integers and their (little|big)-endian representation as a sequence of bytes.
- - -FStar.FunctionalExtensionality
+FStar.Fin -- This module is supposed to contain various lemmas about finiteness.
+FStar.Float -- Support for floating point numbers in F* is nearly non-existent.
+FStar.FunctionalExtensionality -- Functional extensionality asserts the equality of pointwise-equal functions.
- +FStar.Ghost -- This module provides an erased type to abstract computationally irrelevant values.
- +FStar.IFC -- FStar.IFC provides a simple, generic abstraction for monadic information-flow control based on a user-defined (semi-)lattice of information flow labels.
- +FStar.IndefiniteDescription -- Indefinite description is an axiom that allows picking a witness for existentially quantified predicate.
@@ -45,7 +47,7 @@ - +FStar.LexicographicOrdering -- This module proves that lexicographic ordering is well-founded (i.e. every element is accessible)
@@ -92,7 +94,7 @@FStar.Reflection.Derived.Lemmas
-FStar.ReflexiveTransitiveClosure
+FStar.ReflexiveTransitiveClosure -- This module defines the reflexive transitive closure of a relation.
@@ -100,7 +102,7 @@ - +FStar.Squash -- The module provides an interface to work with squash types, F*'s representation for proof-irrelevant propositions.
FStar.Tactics.CanonCommSemiring
+FStar.Tactics.CanonCommSemiring -- A tactic to solve equalities on a commutative semiring (a, +, *, 0, 1)
From 06840b4f5c655b25ced2189a552251c6d3945c6c Mon Sep 17 00:00:00 2001 From: Mark Gritter