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
constr:(...) builds ill-typed terms (or loses universe constraints) #5996
Comments
When reinterpreting a constr matched under binders such as
or
I would propose a wontfix unless some better proposal exists. |
Lacking a check seems basically fine, even potentially quite useful for performance (though I seem to recall that Goal Type.
let c := constr:(prod nat nat) in
let c' := (eval pattern nat in c) in
let c' := lazymatch c' with ?f _ => f end in
let c'' := lazymatch c' with fun x : Set => ?f => constr:(forall x : nat, f) end in
(* Error:
Cannot reinterpret "(_UNBOUND_REL_1 * _UNBOUND_REL_1)%type" in the current environment. *)
exact c''.
Defined. So I'm guessing that there used to be no check, and then a check was added, but the adder of the check neglected to handle universes? Or, perhaps, the reinterpretation happens in uconstr-land, and conversion to constr-land checks types but not universes?
No, your proposals side-step the error by not doing what I want to do. However, changing from
I would be satisfied with a Goal Type.
let c := constr:(prod nat nat) in
let c' := (eval pattern nat in c) in
let c' := lazymatch c' with ?f _ => f end in
let c'' := lazymatch c' with fun x : Set => ?f => uconstr:(forall x : Type, f) end in
let c'' := refresh_universes c'' in
exact c''.
Defined. Note that going from Goal Type.
let c := constr:(prod nat nat) in
let c' := (eval pattern nat in c) in
let c' := lazymatch c' with ?f _ => f end in
let c'' := lazymatch c' with fun x : Set => ?f => uconstr:(forall x : Type, f) end in
let c'' := constr:(c'') in
exact c''.
Defined. (* same error *) |
There is no check, only an attempt to quickly re-infer the type of the term, assuming well-typing but not checking that it is well-typed (so-called As for the going from Maybe the failure of universe polymorphism with |
Note that this will solve only half of my problem. I maintain that replacing |
What about:
|
There is a Goal Type.
Proof.
let c := constr:(prod nat nat) in
let c' := (eval pattern nat in c) in
let c' := lazymatch c' with ?f _ => f end in
let c'' := refresh_universes_and_set c'' in
exact c''.
Defined. |
@herbelin Oooh, great, thanks! I'm wonder if @ppedrot will hate me for making code depend on this side effect of @SkySkimmer That seems like the right thing to do moving forward, though |
Adding a file fixing coq#5996 and which uses this feature.
@JasonGross Although not documented, that's actually the expected semantics of |
Adding a file fixing coq#5996 and which uses this feature.
Oops, it looks like I forgot to reply to this for a while. Replying with some overlap with #8455 (comment) (since I think discussion should happen here): |
Ltac2 suffers from the same problem, unluckily. I have thought about several ways to work around this without sacrificing efficiency, but I don't quite know how to do so for now. Best I can think of is to represent terms with a cache storing an environment and an evarmap in which they are known to be typeable, and check by pointer equality that if they have changed in the meantime, we need to recompute the type. But that's still quite expensive probably. |
Regarding the environment part: Regarding the evarmap part: What's an example of code exploiting the problem we have now? I thought Ltac2 lived in the evarmap monad or something, and there's no way to modify the evarmap except by refinement from within Ltac2? |
For the environment, the problem is that your proposal doesn't scale to complex datatypes containing types. Morally you need some preseaf theory or something to handle that, but that means that types carry additional data that cannot be erased and thus you depart from the ML execution model. For the evarmap, the problem is that you can return data through backtracking exceptions, so that if this data is a term that relies on some universe constraint that has been introduced in the meantime, you're fried. Mtac has the same problem actually, and they solve it quite elegantly thanks to the fact that evars in the object and meta language coincide (they are the same). Another solution for Ltac would be to disallow exceptions returning data, but then you also have the same problem with non-backtracking mutable state, and it looks like we really want that one. Once again, the solution would be some richer type-and-runtime system, but then this does not fit within the backwards compatibility requirements of Ltac2, so that we have to resort to dynamic checks instead. |
Can you be more specific about how it doesn't scale to complex datatypes? I see that general environment transformations don't scale (this is just a slight variant of my My current picture is that for any term For the evarmap, what about making the default way of catching exceptions retype any constrs that live in the exception data (which may be expensive for exception data containing constr, but should be free for, e.g., string-data exceptions), and provide an unsafe way of catching exceptions (or an unsafe wrapper for exception data that says "don't retype me, here be dragons"). Would this work? My goal is that Ltac2 should allow users who want fast code and know what they're doing to get fast code, and should also make it hard for users who don't know what they're doing to shoot themselves in the foot (at least behavior-wise) without noticing. |
How do you perform variable expansion on closures returning or taking terms as arguments? Every type needs to come with a way to lift the variables it recursively contains.
That won't be sufficient. Remember the dreaded list of
I think that the cache solution is the easiest one that satisfies both criteria. You can always provide a function that produces a term whose cache is the current state, and that blindly trusts the user about this. |
@JasonGross |
@jonleivent Does that work? It seems like a neat trick if it does ... I guess |
@JasonGross Works for me in 8.12. The question is, is it an Ltac hack or legit. |
@JasonGross I just tried this trick in the last example the ExampleMoreParamtricity.v file in your reification-by-parametricity project, and it works if both nat and list nat are cast as Types in the pattern - allowing for the removal of the adjust_first_binder_to_type and swap_first_two_binders tactics. |
Note that in sufficiently old versions of Coq, the |
Version
The Coq Proof Assistant, version 8.7.0 (October 2017)
compiled on Oct 18 2017 11:37:05 with OCaml 4.02.3
Operating system
Linux jgross-Leopard-WS 4.4.0-66-generic #87-Ubuntu SMP Fri Mar 3 15:29:05 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
Distributor ID: Ubuntu
Description: Ubuntu 16.04.2 LTS
Release: 16.04
Codename: xenial
Description of the problem
The text was updated successfully, but these errors were encountered: