Skip to content

Migrating code to universes

Catalin Hritcu edited this page May 1, 2017 · 2 revisions

0. If you ever need to go back...

Universes are now the default. If you want to move back to stratified, you can only do that by first checking out branch stratified_last and then

0.1 Add this to your .emacs:

(setq fstar-subp-prover-args '("--stratified"))

0.2 Call fstar with --stratified.

This pulls in the libraries in lib and contrib (universes mode pulls in ulib and ucontrib).

1. Naming conventions are more strictly enforced

The only identifiers that may begin with capital letters are module names, namespace names, and data constructor names. Every other identifier must begin with a lower-case letter or underscore. Note, the identifier prefix 'uu__' is reserved for internal use by the compiler.

This means that a type like TLSError.Result is now named TLSError.result.

In several cases, we were using capital identifiers for type names and lower-case variants of the same identifier for value names. Since there is no longer any distinction, in such cases, the type name usually contains an '_t' suffix. For example, kefAlg_t and kefAlg.

2. Implicit argument annotations are more strictly enforced

We used to allow the following:

    val foo: #a:Type -> x:a -> Tot a
    let foo x = x

Now, you must write

    val foo: #a:Type -> x:a -> Tot a
    let foo #a x = x

I.e., the implicit arguments in the val declaration must also be bound in the corresponding let.

An exception to this rule is if you use ML-style type variables. I.e, you can write

    val bar: x:'a -> Tot 'a
    let bar x = x

We used to also allow implicit argument annotations to be inconsistent between val and let. For example, you used to be able to write

    val baz: #i:id -> alg i -> Tot unit
    let baz i a = ()

Now, you must write

    val baz: #i:id -> alg i -> Tot unit
    let baz #i a = ()

3. Implicit arguments must be instantiated with Tot terms

Instantiation of implicit arguments is more carefully checked in --universes. The version in the old, stratified type-checker has some subtle unsoundnesses.

Note, when automatically instantiating a implicit argument, F* insists that the instantiation be a term with Tot effect. I.e., it has to be unconditionally pure. The rationale for this decision is twofold:

a. It would be very surprising to have the effect of a program depend on a term that the programmer didn't write.

b. Producing a VC in which every implicit argument could potentially have a non-trivial pre-condition is extremely expensive.

Consider:

0. assume val good: id -> Tot bool
1. assume val foo: #i:id{good i} -> t i -> unit
2. let bar (i:id) (x:t i) =
3.   if good i
4.   then foo x
5.   else ()

In this case, F* initially computes the implicit argument of foo at line 4 to be i, but the expected type there is i:id{good i}. It then rejects the program, because i only conditionally has the expected type, i.e., i is not a Tot (i:id{good i}).

There are two ways to workaround this:

a. Provide the argument explicitly

b. In this particular case, you could have written assume val foo: #i:id -> _:t i{good i} -> unit Although this trick may not always be applicable.

This restriction came up in only one place, as far as I can remember---the post-condition of StatefulLHAE.decrypt, where just the well-formedness of the post-condition has quite a subtle argument.

4. The arguments of short-circuiting operators must be pure or ghost

When you write

     e1 && e2, e1 || e2, e1 /\ e2 etc.

both e1 and e2 are required to have an effect that is at most 'Ghost'.

In some cases, we were writing things like

    b && !x = y

This is no longer allowed. You must write:

     let v = !x in b && v=y

Maybe it really should be allowed, but it is quite painful and error-prone to implement.

5. Workaround a current bug in type-checker for mutually recursive types

A bug in the type-checker causes the universe of mutually recursive datatypes to be more polymorphic than necessary. In one case, TLSExtensions.fst, I needed to add an explicit 'Type0' annnotation to force the universe to be Type0. This annotation will eventually be removed.

6. No more 'opaque' qualifiers

Instead, use:

-- inline:

   To mark a pure definition that you want F* to always
   inline before encoding to Z3 or when generating code.

   Use sparingly. A general rule of thumb is to mark definitions
   that make use of higher-order logical constructs as
   'inline'. These are the only cases so far, in mitls-fstar:
       Connection.fst:inline let seq_forall (#a:Type) (p: a -> Type) (s:seq a) =
       TLSConstants.fst:inline type lemma_inverse_g_f (#a:Type) (#b:Type) (=f:(a -> Tot b)) (=g:(b -> Tot (result a))) (x:a) = 
       TLSConstants.fst:inline type lemma_pinverse_f_g (#a:Type) (#b:Type) (r:b -> b -> Type) (=f:(a -> Tot b)) (=g:(b -> Tot (result a))) (y:b) =
       TLSConstants.fst:inline type require_some (#a:Type) (#b:Type) (=f:(a -> Tot (option b))) = 

-- irreducible:

   To mark a definition as irreducible ... unused so far in mitls-fstar.

-- unfoldable:

   This is the default annotation on any definition, so you should
   never have to write it. It doesn't appear anywhere in
   mitls-fstar.

7. 'abstract' and 'private'

To mark a definition as visible only within a module, mark it as abstract. The symbol will still be visible outside the module, but only its type is known, not its definition.

To prevent a top-level name in a module from polluting the global namespace, mark it private.

8. The * operator is reserved for tuples, by default

If you write a * b, by default, this is desugared as tuple2 a b.

If you want to use * for integer multiplication, e.g., 1 * 2, then you must either write: op_Multiply 1 2

Or,

    let op_Star = op_Multiply in
    1 * 2

Our goal is to eventually allow you to use unicode '×' for multiplication. But, the OCaml version of fstar does not yet support unicode lexing.

9. ref vs alloc

The overload between ref: Type -> Type and ref: 'a -> ref 'a is gone. Use ref for the former and alloc for the latter.

10. Matches with a single branch now need a pipe |

Need to change this

 	   match (step_preserves_halting e e' s_e_e') with
	     Conj pa pb -> pa ht in

into this

 	   match (step_preserves_halting e e' s_e_e') with
	   |  Conj pa pb -> pa ht in

11. by_induction_on is gone, need to write explicit proof

Clone this wiki locally