Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removing eta equivalence from F* #2294

Merged
merged 39 commits into from
May 13, 2021
Merged

Removing eta equivalence from F* #2294

merged 39 commits into from
May 13, 2021

Conversation

nikswamy
Copy link
Collaborator

@nikswamy nikswamy commented May 13, 2021

Joint work with @aseemr

From CHANGES.md:

  • Since 2686888, F* no longer
    supports eta equivalence. Dominique Unruh observed that the
    primitive pointwise tactic (which treats provable equality as a
    congruence) allows proving functional extensionality, which is
    unsound in conjunction with subtyping in F* (see below for more
    discussion of that). It turns out that a crucial step in that
    unsoundness proof is the use of eta equivalence (See Bug1966a.fst
    for a proof of that, with thanks due there also to Catalin Hritcu,
    who writes about it here).

    To fix this, we removed eta equivalence. One rough intuition for
    why eta reduction is incompatible with subtyping is that it can
    change the type of a function, widening its domain, e.g., for
    f:int -> int, reducing (fun (x:nat) -> f x) to f changes its
    type. Restricting eta reductions to only those that are type
    preserving turns out to not feasible in the presence of
    abstraction and subtyping.

    With the removal of eta, functional extensionality is now a
    theorem in F* at least for eta-expanded functions, no longer an axiom,
    which is an improvement. The
    removal of eta equivalence introduced regressions in some proofs
    that were implicitly relying on it. See, for example,
    Changes in support of an F* fix removing eta equivalence hacl-star/hacl-star#442

aseemr and others added 30 commits May 10, 2021 17:58
…olution in Rel to not abstract in the flex rigid case if not necessary
@@ -149,7 +149,8 @@ let lem_on_comp #a #b #c (f:b -> c) (g:a -> b)
= ()

/// We also rely on eta being a provable equality
let eta (f:'a -> 'b) : Lemma (f == (fun x -> f x)) = ()
/// AR: 05/11, eta is no longer provable, so admitting it
let eta (f:'a -> 'b) : Lemma (f == (fun x -> f x)) = admit ()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is just a sketch with many assumes in it already.

It's not worth fixing the proofs here to not use eta.

Lemma (ensures (fun (x:a) -> f x) == (fun (x:a) -> g x)) =
assert ((fun (x:a) -> f x) == (fun (x:a) -> g x))
by (l_to_r [quote (q_as_lem x)];
trefl())
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a hand-rolled use of functional extensionality. We didn't need to use the on_domain stuff on restricted functions from FStar.FunctionalExtensionality

// returns Some t'
// if (fun xs -> body) is an eta-expansion of t'
// else returns None
let is_an_eta_expansion env vars body =
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no more eta reduction in SMT encoding

@@ -218,6 +218,26 @@ and on_subterms
: tac<(term * ctrl_flag)>
= let recurse env tm = ctrl_fold_env g0 d controller rewriter env tm in
let rr = recurse env in (* recurse on current env *)
let rec descend_binders orig accum_binders accum_flag env bs t rebuild =
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Part of the change in this PR is to enhance the CtrlRewrite (the underlying code for the pointwise tactic and variants like l_to_r) to actually descend in the binders of an lambda, a refinement type etc.

Note, while this is an improvement, it is still incomplete ... there are other parts of the syntax of terms that this does not descend into.

As a separate PR, I will provide a more complete implementation of this function


So, introduce needless eta expansions here would lead to unification
failures elsewhere
*)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One key change: Do not introduce needless etas

let is_abs t = match t.n with
| Tm_abs _ -> true
| _ -> false in
let maybe_eta t =
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another key change---don't bake in eta equivalence

let congruence_fun #a (#b:a -> Type) (f g:(x:a -> b x)) :
Lemma (requires (forall x. f x == g x))
(ensures (fun (x:a) -> f x) == (fun (x:a) -> g x)) = admit()
(ensures (fun (x:a) -> f x) == (fun (x:a) -> g x)) = congruence_fun' f g ()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where congruence was previously assumed, it can now be proven


private
let __forall_inst #t (#pred : t -> Type0) (h : (forall x. pred x)) (x : t) : squash (pred x) = ()

assume
val eta (#a:_) (#b:a -> Type) (f: (x:a -> b x)) (_:unit) : Lemma (f == (fun x -> f x))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but we need eta as an axiom to prove fun_ext for arbitrary f, g as shown below

FStar.Squash.return_squash Refl
// (* To begin with, eta is provable *)
// let eta_is_eq #a (#b:a -> Type) (f: (x:a -> b x)) : (f == (fun (x:a) -> f x)) =
// FStar.Squash.return_squash Refl
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope, not anymore

(sq_feq : squash (feq f g))
: Lemma (ensures on_domain a f == on_domain a g)
= admit()
// let open FStar.Tactics in
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are actually provable with tactics.

But, using tactics in funext leads to circularities in bootstrapping the compiler.

A separate PR will fix those bootstrapping problems and remove these admits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants