-
Notifications
You must be signed in to change notification settings - Fork 685
Convert ltac2 chapter to use prodn, update syntax #12085
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
keep the production and description in the ltac2 chapter, insert linking nonterminals such as
ltac2_XXX
in the other chapters where needed so the added productions are discoverable but it's fairly apparent that they are for ltac2
Sounds like the right thing to do in many cases
Another issue is whether any material should be shared between the 2 Ltac chapters. That sounds like it could be complicated. It may be better to have them independent.
Yes, it would be better to keep the two chapters independent. However, we also have the issue that grammar rules for tactics are added in the Ltac1 and Ltac2 plugins, but we shouldn't have to repeat the documentation for those.
That may mean the index has to qualify repeated items, e.g.
match (Ltac1)
andmatch (Ltac2)
.
This is perfectly reasonable.
@@ -448,7 +448,7 @@ Definition by cases: match | |||
.. insertprodn term_match pattern0 | |||
|
|||
.. prodn:: | |||
term_match ::= match {+, @case_item } {? return @term100 } with {? %| } {*| @eqn } end | |||
term_match ::= match {+, @case_item } {? return @term100 } with {| {? %| } {*| @eqn } | | %| {+| @branch } | {+| @branch } } end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There might be some inappropriate factorization (probably a non-terminal to rename). It is not normal that Ltac2 could modify the syntax of the match
construct. (However, like Ltac1, it has its own match
construct.)
doc/tools/docgram/fullGrammar
Outdated
] | ||
|
||
induction_clause: [ | ||
| destruction_arg as_or_and_ipat eqn_ipat opt_clause | ||
| destruction_arg as_or_and_ipat eqn_ipat OPT clause |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are some grammar rules that are both in the Ltac1 and Ltac2 plugin but which are grammar rules for tactics themselves, so independent of which version of Ltac is used. It would be interesting to figure out if it is possible to factorize them, but I'm not so hopeful.
doc/tools/docgram/fullGrammar
Outdated
] | ||
|
||
oriented_rewriter: [ | ||
| orient_rw rewriter | ||
| ltac2_orient rewriter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In some cases (again grammar rules for tactics), the rules are repeated but with other non-terminal names, making it harder to verify that these are exactly the same rules. That being said, I think this particular example is a case where you were the one to rename the non-terminal, to avoid duplicate names.
doc/tools/docgram/fullGrammar
Outdated
by_tactic: [ | ||
| "by" tactic_expr3 | ||
| | ||
| "by" tac2expr6 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an example where it makes sense that it is not exactly the same non-terminal that follows the by
clause: in one case it is an Ltac1 expr, whereas in the other it is an Ltac2 expr.
doc/tools/docgram/fullGrammar
Outdated
as_name: [ | ||
| "as" ident | ||
| | ||
| "as" ident_or_anti | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, Ltac2 adds a second possibility after an as
clause.
doc/tools/docgram/fullGrammar
Outdated
| "as" or_and_intropattern_loc | ||
| | ||
| "as" or_and_intropattern |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a surprising difference. With the current duplication of code, there is always a risk of divergence between the two grammars.
doc/tools/docgram/fullGrammar
Outdated
| Prim.integer | ||
| Prim.string | ||
| Prim.qualid | ||
| "@" Prim.ident | ||
| "&" lident | ||
| "'" Constr.constr | ||
| "constr" ":" "(" Constr.lconstr ")" | ||
| "open_constr" ":" "(" Constr.lconstr ")" | ||
| "ident" ":" "(" lident ")" | ||
| "pattern" ":" "(" Constr.lconstr_pattern ")" | ||
| "reference" ":" "(" globref ")" | ||
| "ltac1" ":" "(" ltac1_expr_in_env ")" | ||
| "ltac1val" ":" "(" ltac1_expr_in_env ")" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems likely that these extensions of tactic_atom
should have been added to some other non-terminal instead...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC you were eager to get this chapter updated for 8.12 so it doesn't have productionlists. I'm not sure that's really feasible within the next couple weeks. Perhaps it's better to clean up the syntax for other chapters first, in particular doing as much of the tactics chapter as we can for 8.12. And doing much less rewriting.
The issues are:
First, that Ltac2 modifies nonterminals that are documented in other chapters--mostly in the Ltac1 chapter but also things that are elsewhere, e.g. small additions to patterns and intropatterns and tactics that are only available if ltac2 is loaded. Some things are AFAICT fully shared with Ltac1, while tactic_atom
has a dozen new productions plus there are other scattered changes. Making the two chapters fully independent would be quite a lot of work. We could include some added productions, such as for patterns and tactics and mark which things are only for ltac2.
Second, it's not really clear where to fit all the Ltac2 productions into the current text. The old grammar is rather different from the current grammar. It seems like there were big changes to the grammar earlier that didn't make it to the doc. Some things don't fit into obvious places in the text.
Third, the current document has very little, if any, explanation on a production-by-production basis.
We could do something much simpler that's not very good but better than nothing, which is just to ensure that all the Ltac2 grammar appears in that chapter without explanation, including in some crude way the additions to nonterminals that apply to things in other chapters. That might be doable by the end of June. And at least we'd be showing the correct grammar.
I'd probably need to extend doc_grammar to get the documented grammar correct and keep the editing/updating sane and maintainable.
I pushed an update that reflects the Ltac1 chapter updates. It doesn't make sense to fix the missing reference warnings from Sphinx until we have a plan.
Let me know your thoughts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps it's better to clean up the syntax for other chapters first, in particular doing as much of the tactics chapter as we can for 8.12. And doing much less rewriting.
I insist that there is no rush in fixing the syntax in the tactics chapter. You keep coming back to the idea of fixing the syntax with very little rewriting but experience has shown again and again that updating the syntax without doing the associated rewriting is not possible or would create major regressions in the documentation.
Let me be clear: I'd rather focus on other improvements to the refman for 8.12 and won't review any significant PR about the tactics chapter in the coming weeks. If we don't finish replacing the productionlist
for 8.12, fine, but let's then focus on coordinating an effort to improve the pages that are new in 8.12.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to defer reviewing further syntax updates until after 8.12 ships, that's fine. I think we need more discussion before I can do much on Ltac2. We can defer that discussion, too. I think I could make a lot of progress on the tactics chapter in the meantime. If you want my help for other doc improvements for 8.12, let me know what you have in mind.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will be more a matter of helping the volunteers if we can find some.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A worry that I have had for quite some time during the syntax revisions and that hits harder now with the Ltac2 chapter question (and that should also appear when attacking the SSR chapter) is that we haven't really managed yet to have a proper way of documenting the grammar of Coq as an extensible grammar (which it fundamentally is). The fact that a plugin may extend the syntax not just by adding new commands, tactics and nonterminals, but even by modifying some parsing rules for existing commands and nonterminals is something that we don't sufficiently acknowledge in the current documentation. The original prodn
had support for +=
which was all about this AFAICT, but it is not really supported by doc_grammar
IINM.
@@ -60,7 +60,7 @@ Definition by cases: match | |||
.. insertprodn term_match pattern0 | |||
|
|||
.. prodn:: | |||
term_match ::= match {+, @case_item } {? return @term100 } with {? %| } {*| @eqn } end | |||
term_match ::= match {+, @case_item } {? return @term100 } with {? {| {? %| } {*| @eqn } | %| {+| @branch } | {+| @branch } } } end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ltac2 extends the match
syntax on terms!?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I see the problem: Ltac2 defines its own branches
and branch
non-terminals, it doesn't extend the existing ones.
doc/sphinx/proof-engine/ltac.rst
Outdated
| @ @ident | ||
| & @lident | ||
| ' @term | ||
| constr : ( @term ) | ||
| open_constr : ( @term ) | ||
| ident : ( @lident ) | ||
| pattern : ( @cpattern ) | ||
| reference : ( @globref ) | ||
| ltac1 : ( @ltac1_expr_in_env ) | ||
| ltac1val : ( @ltac1_expr_in_env ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My suggestion was that this should all be extracted into a single ltac2_atom
nonterminal, so that it would be clear when reading the Ltac1 chapter that this is irrelevant, and to document this ltac2_atom
in the Ltac2 chapter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense.
This has always been on my list but so far we haven't had to make a decision. There are only a couple choices: I prefer "a", I would avoid "c" and I think we'll end up with "d". I think "b" makes sense when there are a bunch of productions added to an existing symbol; if it's only one or two added productions that can be explained succinctly, not so much. The problem with I'm not sure if there are tactics or commands with syntax extensions; if so, we'd need to figure that out, too. If you prefer, we can continue this discussion some weeks from now. |
PS You may recall that doc_grammar already tracks where productions come from. This info is still printed in
|
FWIW I agree with your analysis. And yes, it would probably be useful to show where production comes from. |
e874972
to
50afb6a
Compare
I added "Ltac2" tags in prodns. They appear on every production derived from the ltac2 mlg. Examples below. What do you think? ltac2.rst: tactics.rst: variants.rst: In this case, |
You probably missed my answer regarding this: there is a confusion because some non-terminal names are reused, but Ltac2 should not and does not modify the (term) |
When it is clear that this is an Ltac2 production, e.g. because the non-terminal itself (and not just the rule) is defined in Ltac2, then I think these annotations are not necessary and may be a bit much. |
The case of https://github.com/coq/coq/blob/90345eb092f9e3fc7ef1bdfe7f61cb913cb53d18/vernac/g_vernac.mlg#L88 ), then these non-terminals should be considered distinct. Maybe that's something that you should consider handling in |
A good example showing these annotations would probably be the |
OK, I see those symbols are local and independent. The generated code, If you think annotations such as "Ltac2" will never be useful, I'll drop that change entirely. What do you think? (If annotations are done on a case-by-case basis, it would need to be revised anyway.) |
I think they might be useful, but I would need to see a concrete use case. Keep the code for now, we might end up encountering a good use case. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not much textual change to this chapter, contrary to the Ltac1 chapter. Is this deliberate?
Well, it's still marked as a draft PR. The real work begins when the previous PR (i.e. Ltac1) is merged. This chapter is only partially in a reference manual style. I'm not sure how much time I want to spend now in revising. Also some concern that it is still experimental; I'd prefer to focus on the parts that are most likely final. I'm doubtful it will make 8.12.0, but maybe it will be ready for 8.12.1. Inclusion is up to the release managers. It would great if you can review the PR as it evolves, answer questions and help generate examples. As we proceed I'll give some guidance on what to focus on. |
Feel free to mention me with questions, I'll do my best to review as it evolves |
Fully documenting the Ltac2 syntax looks like it could be a big project for which More problematic are the (re)definitions of ~130 tactics and Ltac1 constructs. Determining whether they match the versions hard-wired into the grammar is a non-trivial and unautomated task. Re-documenting any that differ just for Ltac2 is not a happy thought. I would lean toward explaining the redefinitions in general terms without comparing them exhaustively. |
OK, I'm baffled why I get this error every time from Sphinx when CoqIDE 8.12 usually processes this input. (I say "usually" because I think backtracking in CoqIDE may not always fully undo things for Ltac2 (somehow) and I've seen the same error sometimes--but I haven't found a way to reliably repeat it.) So two problems. The Sphinx problem may block creating examples in the doc for Ltac2.
Also on this one:
|
I pushed an update. I think most of the syntax is in the right place--would you take a look at that? I'm not ready to deal with a lot of text comments, but if there are typos or grammatical errors feel free to point them out. I spent a fair bit of time correcting how doc_grammar handles local nonterminals. However, now that I look at result, I think a number of Ltac2 productions should be merged with existing similar nonterminals, tagging the productions that come from Ltac2 (as in my recent experiment) and adding verbiage that explains they're only available with Ltac2. I suppose I could suppress the Ltac2 tags within the Ltac2. The alternative would seem to be the 4 pages of productions at the end of the Notations/Syntactic Classes section. Ugh. Ltac2 redefines the syntax for a great many tactics in Ltac2/Notations.v (139 "Ltac2 Notation"s) using Syntatic classes. This presents some challenges:
@JasonGross Do you have any Ltac2 code I can peruse? Also, if faster execution is a benefit of Ltac2, it would be interesting to give an example. Would that fit into you performance evaluations efforts? I don't know how many examples I'll add. It may be very few or none. As I noted, the |
You can mostly ignore those. It's an implementation detail (that I'd like to get rid of) that the syntax for tactics are defined twice (once in each LtacX plugin). |
Here are some examples of Ltac2 code:
You can also find a great deal of (relatively low-level) Ltac2 in Here are some Ltac2 examples that probably won't compile on the current version of Ltac2, because they're from an earlier version, but I expect them to be able to compile with only minor changes:
There's some discussion of performance of Ltac2 in our reification-by-parametricity paper (see especially the graphs on pages 13 and 15, where direct translation from Ltac to Ltac2 (see the "Ltac2" line) seems to incur a slowdown in our particular example (we didn't debug where the slowdown came from; we had to change management of free/fresh variables, management of recurring under binders, and had to move the final term back and forth between Ltac1 and Ltac2, which might have incurred overhead), but where using Ltac2 allowed access to much more low-level term matching and construction primitives (mostly in So in general I would not expect Ltac2 execution to be faster when just straightforwardly translating from Ltac1, nor when you are just writing your tactic script by hand in the goal. The primary benefits, I believe, are that (a) the language is more principled and not cobbled-together like Ltac1, and (b) it's possible to have much more fine-grained control over constr building and matching, allowing massive performance improvements. I think there'll also be a performance benefit in the future that we can potentially get access to the fast data-structures of OCaml, like array, maps and sets, ref cells, etc. (Btw, we should make sure we're clear in the documentation how mutation in Ltac2 (of array, ref, etc) interacts with backtracking / exceptions.) Googling also gives @tchajed 's tutorial at https://github.com/tchajed/ltac2-tutorial |
(untested) Ltac2 lia := ltac1:(lia). |
I get Separately, I notice that there's no UPDATE: |
To make lia available as a tactic, you need to do this trick: From Ltac2 Require Import Ltac2.
From Coq Require Import Lia.
Local Ltac2 lia_ltac1 () := ltac1:(lia).
Ltac2 Notation "lia" := lia_ltac1 ().
Goal forall n m, n + m = m + n.
Proof.
lia.
Qed. |
@jfehrle I think you might be running into a bug where backtracking across the For me the biggest benefit of Ltac2 has been the ability to go back and forth between strings and identifiers, which was not possible in Ltac1 without a plugin. We convert strings to identifiers to support a feature in the Iris Proof Mode; see https://gitlab.mpi-sws.org/iris/string-ident/-/blob/master/theories/ltac2_string_ident.v for the implementation. Koika does the reverse, going from identifiers to strings: https://github.com/mit-plv/koika/blob/master/coq/IdentParsing.v. |
Updated. Jason, would approve this? @Zimmi48 do you want to review it one more time? Will you handle the merge? |
For your complete information, the job test-suite:4.11+trunk+dune in allow failure mode has failed for commit eeef3a2: Review #5 |
@JasonGross I think @Zimmi48 would like to rely on your approval before merging this. Hope you can do that soon. Maybe will take you less than 5 minutes? |
doc/sphinx/proof-engine/ltac2.rst
Outdated
- A convenient way to build terms with casts through the low-level API because the | ||
cast type is opaque. You can use a variation of this code as a workaround: | ||
|
||
.. coqtop:: none | ||
|
||
From Ltac2 Require Import Ltac2. | ||
|
||
.. coqtop:: in | ||
|
||
Ltac2 get_vm_cast () := | ||
match Constr.Unsafe.kind '(I <: True) with | ||
| Constr.Unsafe.Cast _ cst _ => cst | ||
| _ => Control.throw Not_found | ||
end. | ||
|
||
- Missing low-level primitives that are convenient for writing automation, such as: | ||
|
||
- An easy way to get the number of constructors of an inductive type. | ||
Currently only way to do this is to destruct a variable of the inductive type | ||
and count the number of goals that result. | ||
|
||
- Building terms with casts currently requires an awkward construction like the | ||
following, which also incurs extra overhead to repeat typechecking for each | ||
call to `get_vm_cast`: `Constr.Unsafe.make (Constr.Unsafe.Cast 'I (get_vm_cast ()) 'True)` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think the bullet points should be combined, e.g., like this:
- A convenient way to build terms with casts through the low-level API because the | |
cast type is opaque. You can use a variation of this code as a workaround: | |
.. coqtop:: none | |
From Ltac2 Require Import Ltac2. | |
.. coqtop:: in | |
Ltac2 get_vm_cast () := | |
match Constr.Unsafe.kind '(I <: True) with | |
| Constr.Unsafe.Cast _ cst _ => cst | |
| _ => Control.throw Not_found | |
end. | |
- Missing low-level primitives that are convenient for writing automation, such as: | |
- An easy way to get the number of constructors of an inductive type. | |
Currently only way to do this is to destruct a variable of the inductive type | |
and count the number of goals that result. | |
- Building terms with casts currently requires an awkward construction like the | |
following, which also incurs extra overhead to repeat typechecking for each | |
call to `get_vm_cast`: `Constr.Unsafe.make (Constr.Unsafe.Cast 'I (get_vm_cast ()) 'True)` | |
- A convenient way to build terms with casts through the low-level API. Because the | |
cast type is opaque, building terms with casts currently requires an awkward construction like the | |
following, which also incurs extra overhead to repeat typechecking for each | |
call to `get_vm_cast`: `Constr.Unsafe.make (Constr.Unsafe.Cast 'I (get_vm_cast ()) 'True)` | |
with: | |
.. coqtop:: none | |
From Ltac2 Require Import Ltac2. | |
.. coqtop:: in | |
Ltac2 get_vm_cast () := | |
match Constr.Unsafe.kind '(I <: True) with | |
| Constr.Unsafe.Cast _ cst _ => cst | |
| _ => Control.throw Not_found | |
end. | |
- Missing low-level primitives that are convenient for writing automation, such as: | |
- An easy way to get the number of constructors of an inductive type. | |
Currently only way to do this is to destruct a variable of the inductive type | |
and count the number of goals that result. |
Feel free to merge with or without this, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got, it thanks.
Updated. @Zimmi48 time for a final review if you wish and, if ready, a merge. |
For your complete information, the job test-suite:4.11+trunk+dune in allow failure mode has failed for commit be3da73: Review #6 |
For your complete information, the job test-suite:base+async in allow failure mode has failed for commit be3da73: Review #6 |
@JasonGross Thanks for all your help! @Zimmi48 I believe the failures are unrelated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't try to review the Ltac2 chapter changes, only some of the other changes.
Didn't you intend to squash some commits before the merge?
@@ -227,6 +227,7 @@ rest of the |Coq| manual: :term:`terms <term>` and :term:`types | |||
| @term_match | |||
| @term_record | |||
| @term_generalizing | |||
| [| {*; @term } %| @term {? : @type } |] {? @univ_annot } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this supposed to be?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Primitive arrays, added by #11604.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Probably worth refining in a separate PR then. However, this raises the question whether this PR is actually backportable to v8.12
. This line should be removed in the backport since it's 8.13-only syntax, but what about the documented features of Ltac2? Is any of what's newly documented in this PR 8.13-specific @JasonGross?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you copy this PR locally onto 8.12 and run 'make doc_gram_rsts', that would identify and partly reverse 8.13 grammar changes. And then probably a quick review to back out any related textual changes. I can create that if you like after this is merged. I guess just make the new PR go against 8.12 instead of master?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I guess I will try following your advice and backporting myself first. And if I don't manage, I'll ask you to do it.
Updated. Made 2 small changes and squashed. |
Updated. |
One last question. The second commit introduces the ability to tag productions coming from plugins (Ltac2 in particular) but in the end, it doesn't look like this is used anywhere. Should we still keep this commit as part of this PR (as opposed to moving it to a new PR) since there isn't really much to test it against (and I can't really review the changes abstractly either)? |
You saw what the output looks like with tags a while ago. We decided to use them sparingly; definitely not showing them for every production in the Ltac2 chapter. It would difficult, I think infeasible, to create an automated test. So I'm not sure what would be gained by making it a separate PR. What else would you do to review it? Splitting it out won't make it any easier to understand the changes. Also, the commit would have to be split. The I suggest we keep this as it is. |
OK, fine by me. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@coqbot: merge now
Thanks for your help, Théo. |
(cherry picked from commit 461e0d6)
Take a look at fullGrammar. Including ltac2 changes the definition of about 25 existing nonterminals, also adding about 90 new nonterminals. For example,
"$" ident
is added in a couple places.For the documentation, it's awkward to show the additional productions for the 25 existing nonterminals. Here are some possibilities:
ltac2_XXX
in the other chapters where needed so the added productions are discoverable but it's fairly apparent that they are for ltac2Another issue is whether any material should be shared between the 2 Ltac chapters. That sounds like it could be complicated. It may be better to have them independent. That may mean the index has to qualify repeated items, e.g.
match
(Ltac1) andmatch (Ltac2)
.There will be more details. Doing the Ltac chapter first will help.