diff --git a/.gitignore b/.gitignore
index a8d712e50..c9d2ab4b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,4 +46,5 @@ META
*.cache
apps/coercion/src/coq_elpi_coercion_hook.ml
-.filestoinstall
\ No newline at end of file
+.filestoinstall
+apps/tc/src/coq_elpi_tc_hook.ml
diff --git a/Makefile b/Makefile
index 8adfefa18..8c91fd0ab 100644
--- a/Makefile
+++ b/Makefile
@@ -21,7 +21,7 @@ export ELPIDIR
DEPS=$(ELPIDIR)/elpi.cmxa $(ELPIDIR)/elpi.cma
-APPS=$(addprefix apps/, derive eltac NES locker coercion)
+APPS=$(addprefix apps/, derive eltac NES locker coercion tc)
ifeq "$(COQ_ELPI_ALREADY_INSTALLED)" ""
DOCDEP=build
diff --git a/_CoqProject b/_CoqProject
index 1e0d91b72..f6956fa6a 100644
--- a/_CoqProject
+++ b/_CoqProject
@@ -8,23 +8,42 @@
-Q tests elpi.tests
-Q elpi elpi
+# Derive
-R apps/derive/theories elpi.apps
-R apps/derive/tests elpi.apps.derive.tests
-R apps/derive/examples elpi.apps.derive.examples
+
+# NES
-R apps/NES/theories elpi.apps
-R apps/NES/tests elpi.apps.NES.tests
-R apps/NES/examples elpi.apps.NES.examples
+
+# Eltac
-R apps/eltac/theories elpi.apps.eltac
-R apps/eltac/tests elpi.apps.eltac.tests
-R apps/eltac/examples elpi.apps.eltac.examples
--R apps/coercion/theories elpi.apps.coercion
+# Coercion
+-R apps/coercion/theories elpi.apps.coercion
+-R apps/coercion/tests elpi.apps.tc.coercion
+-I apps/coercion/src
+
+# Type classes
+-R apps/tc/theories elpi.apps
+-R apps/tc/tests elpi.apps.tc.tests
+-R apps/tc/elpi elpi.apps.tc
+-I apps/tc/src
+
+# Coq-elpi
theories/elpi.v
theories/wip/memoization.v
-I src
+
src/META.coq-elpi
+src/coq_elpi_graph.mli
+src/coq_elpi_graph.ml
src/coq_elpi_vernacular_syntax.mlg
src/coq_elpi_vernacular.ml
src/coq_elpi_vernacular.mli
diff --git a/_CoqProject.test b/_CoqProject.test
index ccbfd241e..4093a2ebc 100644
--- a/_CoqProject.test
+++ b/_CoqProject.test
@@ -53,3 +53,4 @@ tests/test_link_order_import1.v
tests/test_link_order_import2.v
tests/test_link_order_import3.v
tests/test_query_extra_dep.v
+tests/test_toposort.v
diff --git a/apps/tc/Makefile b/apps/tc/Makefile
new file mode 100644
index 000000000..9b84ee407
--- /dev/null
+++ b/apps/tc/Makefile
@@ -0,0 +1,40 @@
+# detection of coq
+ifeq "$(COQBIN)" ""
+COQBIN := $(shell which coqc >/dev/null 2>&1 && dirname `which coqc`)
+endif
+ifeq "$(COQBIN)" ""
+$(error Coq not found, make sure it is installed in your PATH or set COQBIN)
+else
+$(info Using coq found in $(COQBIN), from COQBIN or PATH)
+endif
+export COQBIN := $(COQBIN)/
+
+all: build test
+
+build: Makefile.coq
+ @$(MAKE) --no-print-directory -f Makefile.coq
+
+test: Makefile.test.coq
+ @$(MAKE) --no-print-directory -f Makefile.test.coq
+
+theories/%.vo: force
+ @$(MAKE) --no-print-directory -f Makefile.coq $@
+tests/%.vo: force build Makefile.test.coq
+ @$(MAKE) --no-print-directory -f Makefile.test.coq $@
+examples/%.vo: force build Makefile.test.coq
+ @$(MAKE) --no-print-directory -f Makefile.test.coq $@
+
+Makefile.coq Makefile.coq.conf: _CoqProject
+ @$(COQBIN)/coq_makefile -f _CoqProject -o Makefile.coq
+ @$(MAKE) --no-print-directory -f Makefile.coq .merlin
+Makefile.test.coq Makefile.test.coq.conf: _CoqProject.test
+ @$(COQBIN)/coq_makefile -f _CoqProject.test -o Makefile.test.coq
+
+clean: Makefile.coq Makefile.test.coq
+ @$(MAKE) -f Makefile.coq $@
+ @$(MAKE) -f Makefile.test.coq $@
+
+.PHONY: force all build test
+
+install:
+ @$(MAKE) -f Makefile.coq $@
diff --git a/apps/tc/Makefile.coq.local b/apps/tc/Makefile.coq.local
new file mode 100644
index 000000000..f120308b2
--- /dev/null
+++ b/apps/tc/Makefile.coq.local
@@ -0,0 +1,3 @@
+CAMLPKGS+= -package coq-elpi.elpi
+OCAMLPATH:=../../src/:$(OCAMLPATH)
+export OCAMLPATH
\ No newline at end of file
diff --git a/apps/tc/README.md b/apps/tc/README.md
new file mode 100644
index 000000000..bf03a8a5c
--- /dev/null
+++ b/apps/tc/README.md
@@ -0,0 +1,524 @@
+# Type class solver
+
+This folder contains an alternative implementation of a type class solver for
+coq written in elpi. This solver is composed by two main parts, the **compiler**
+and the **solver**. The former takes coq classes and instances and "translates"
+them into the elpi representation, whereas the latter is the elpi tactic aiming
+to make instance search on coq goals.
+
+- [The compiler](#the-compiler)
+ - [Class compilation](#class-compilation)
+ - [Deterministic search](#deterministic-search)
+ - [Instance compilation](#instance-compilation)
+ - [Instance priorities](#instance-priorities)
+ - [Technical details](#technical-details)
+ - [Instance locality](#instance-locality)
+- [Goal resolution](#goal-resolution)
+- [Commands](#commands)
+- [Flags](#flags)
+- [WIP](#wip)
+
+
+## The compiler
+
+In our implementation by compiler we mean the set of rules abstracting coq
+terms, *1.* classes and *2* instances, in the elpi world. In the next two
+paragraphs, we briefly explain these two phases of the compilation, where,
+intuitively, a type class can be seen as a prolog predicate and the instances of
+a type class $C$ as rule (clause or fact) of the elpi predicate for $C$.
+
+For instance, if
+
+```coq
+Class Eqb (T: Type) := {
+ eqb : T -> T -> bool;
+ eq_leibniz : forall (A B: T), eqb A B = true -> A = B
+}.
+```
+
+is the type class representing the leibniz equality between two objects of type
+$T$, and
+
+```coq
+Program Instance eqBool : Eqb bool := {
+ eqb A B := if A then B else negb B
+}.
+Next Obligation. now intros [] []. Qed.
+```
+
+is an implementation of `Eqb` for the type `bool`, their corresponding elpi
+representation will be:
+
+```prolog
+ pred tc-Eqb i:term, o:term.
+ tc-Eqb {{bool}} {{eqBool}}.
+```
+
+### Class compilation
+
+The compilation of a type class creates dynamically (thanks to the
+`coq.elpi.add-predicate` API) a new predicate called `tc-Path.tc-ClassName` with
+$N + 1$ terms where:
+
+- `Path` is the is the logical path in which the type class `ClassName` is
+ located
+- $N$ is the number of parameter of the `ClassName`. In particular, if a type
+ class $C$ as the parameters $P_1,\dots, P_n$ then the corresponding predicate
+ will have $N$ parameters of type `term` ($1$ per parameter) and a last
+ parameter in output mode containing the result of the instance search.
+ By default, all the first $P_1,\dots,P_n$ parameters are in output mode.
+
+The set of rules allowing to add new type-class predicates in elpi are grouped
+in [create_tc_predicate.elpi](elpi/create_tc_predicate.elpi).
+
+### Deterministic search
+
+Sometimes, it could be interesting to disable the backtracking search for some
+type classes, for performances issues or design choices. In coq the flag
+*Typeclasses Unique Instances* (see
+[here](https://coq.inria.fr/refman/addendum/type-classes.html#coq:flag.Typeclasses-Unique-Instances))
+allows to block any kind of a backtrack on instance search: in this case type
+classes are supposed to be canonical.
+
+In the example below, we want the `NoBacktrack` type class not to backtrack if
+a solution is found.
+
+```coq
+Class NoBacktrack (n: nat).
+Elpi TC.Set_deterministic NoBacktrack.
+(* Ideally
+ #[backtrack(off)] Class NoBacktrack (n : nat).
+ *)
+Class A (n: nat).
+
+Instance a0 : A 0. Qed.
+Instance nb0 : NoBacktrack 0. Qed.
+Instance nb1 : NoBacktrack 1. Qed.
+Instance a3 n : NoBacktrack n -> A n -> A 3. Qed.
+
+Goal A 3. Fail apply _. Abort.
+```
+
+The goal `A 3` fails since the only instance matching it is `a3`, but we are not
+able to satisfy both its premises. In particular, the instance `nb1` is applied
+first, which fixes the parameter `n` of `a3` to `1`. Then the algorithm tries to
+find a solution for `A 1` (the second premise), but no implementation of `A` can
+solve it. In the classic approach, the type class solver would backtrack on the
+premise `NoBacktrack n` and try to apply `nb0` (this would find a solution), but
+since the type class `NoBacktrack` is deterministic, then `nb0` is discarded.
+
+In this implementation, the elpi rule for the instance `a3` is:
+
+```elpi
+ tc-A {{3}} {{a3 lp:A lp:B lp:C}} :-
+ do-once (tc-NoBacktrack A B),
+ tc-A A C.
+```
+
+The predicate `do-once i:prop` has
+
+```prolog
+do-once P :- P, !.
+```
+
+as implementation. The cut (`!`) operator is in charge to avoid backtracking on
+the query `tc-NoBacktrack A B`
+
+### Instance compilation
+
+Instances are compiled in elpi from their type. In particular, since the
+$\forall$-quantification and the left hand side of implications of coq are both
+represented with the `prod` type in elpi, we can say that the type of an
+instance $I$ is essentially a tower of
+
+
+prod N_1 T_1 (x_1\
+ prod N_2 T_2 (x_2\
+ ...
+ prod N_n T_n (x_n\
+ app [global GR, A_1, A_2, ... A_M])))
+
+
+where $\forall i \in [1, N],\ T_i$ is the type of the quantified variable $x_i$.
+Each $x_1$ represents a premise $P$ of the current instance and, if its type
+$T_i$ is a type class, then $P$ is recursively compiled and added to the final
+clause as a premise. The last `prod` contains `app [global GR, A_1, ..., A_M]`
+where `GR` is the gref of the type class implemented by $I$ and each $A_j$ is an
+argument applied to `GR` which sees every $x_i$. Note that during the
+compilation of the instance the binders $x_i$ are recursively replaced by fresh
+`pi` elpi variables.
+
+For example, the instance `eqBool` showed before, has type
+
+`Eqb bool`, it has no quantified variable and it is directly compiled in the
+clause `tc-Eqb {{bool}} {{eqBool}}`.
+
+On the other hand, if we take the instance below,
+
+```coq
+Instance eqProd (A B: Type) : Eqb A -> Eqb B -> Eqb (A * B) := { ... }
+```
+
+we see that its type is
+
+```
+prod `A` (sort (typ eqProd.u0»)) c0 \
+ prod `B` (sort (typ eqProd.u1»)) c1 \
+ prod `H0` (app [global (indt «Eqb»), c0]) c2 \
+ prod `H1` (app [global (indt «Eqb»), c1]) c3 \
+ app [global (indt «Eqb»), app [global (indt «prod»), c0, c1]]
+```
+
+there are in fact four variables that produce the elpi clause:
+
+```
+pi x0 x1 x2 x3\
+ tc-Eqb {{prod lp:A lp:B}} Sol :-
+ tc-Eqb A S1, tc-Eqb B S2,
+ Sol = {{eqProd lp:A lp:B lp:S1 lp:S2}}.
+```
+
+the four variable $c_0,...,c_3$ are quantified with `pi`, the two premises
+`H0` and `H1` are compiled as premises of the current goal (we need to find a
+proof that there exists an implementation of the class `Eqb` for the types
+of $c_0$ and $c_1$). Then the application of `«Eqb»` is used to create the head of
+the clause with its arguments and `eqProd`, the gref of the current instance,
+is used as the solution of the current goal applied to all of the quantified
+variables.
+
+The set of rules allowing to compile instances in elpi are grouped in
+[compiler.elpi](elpi/compiler.elpi).
+****
+### Instance priorities
+
+To reproduce coq behavior, instances need to respect a notion of priority:
+sometime multiple instances can be applied on a goal, but, for sake of
+performances, the order in which they are tried is essential.
+
+In the previous example, the goal `Eqb ?V` where `?V` is a meta-variable, it
+is important to first use the rules `eqBool` and then `eqProd`, the latter
+causing an infinite loop.
+
+In elpi, we have the possibility to create rules with names and, then, new rules
+can be added with respect to a particular grafting (see
+[here](https://github.com/FissoreD/coq-elpi/blob/a11558758de0a1283bd9224b618cc75e40f118fb/coq-builtin.elpi#L1679)).
+
+Our strategy of instance insertion in the elpi database reposes on a predicate
+`pred hook o:string` having, by default, $1.001$ implementations each of them
+having a name going from `"0"` to `"1000"` (bounds included). Roughly what we
+have is the following:
+
+```prolog
+:name "0" hook "0".
+:name "1" hook "1".
+...
+:name "999" hook "999".
+:name "1000" hook "1000".
+```
+
+In this way an instance can be added at the wanting position to respect its
+priority. In particular, the priority of an instance can be defined in two
+different ways by the user by coq and we retrieve this piece of
+information via the `Event` listener from `coq` (see
+[here](https://github.com/coq/coq/blob/f022d5d194cb42c2321ea91cecbcce703a9bcad3/vernac/classes.mli#L81)).
+This event contains either a class or an instance and in the latter case we can
+get its priority (see
+[here](https://github.com/FissoreD/coq-elpi/blob/a11558758de0a1283bd9224b618cc75e40f118fb/apps/tc/src/coq_elpi_tc_register.ml#L57)).
+
+#### Technical details
+
+1. If the instance has no user defined priority, the attribute containing the
+ priority of the instance is set to `None`. In this case, the priority is
+ computed as the number of premises the instance has. For example, `eqBool`
+ has priority $2$, since it has two hypothesis triggering recursive instance
+ search.
+2. If $P$ is the priority of both the instance $I_1$ and the instance $I_2$ of
+ a class $C$, then the instance that should be tried before is the one which
+ has been defined later (this is coq default behavior). To respect this order,
+ the grafting we use is `after P` for both instances, in this way, elpi will
+ put the second-defined instance before the first one.
+3. The number of hook in elpi is bounded to $1.000$, it is however possible to
+ extend it via the command `Elpi TC.AddHook G OldName NewName` where `G` is
+ either after or before and `NewName` is the new hook that will be grafted
+ after\before the hook called `OldName`. For instance, `Elpi TC.AddHook after
+ 1000 1002` creates a hook named `1002` after `1000` and `Elpi TC.AddHook before
+ 1002 1001` insert the hook `1001` before `1002`. Note that `OldName` should
+ be an existing name, otherwise, a blocking error will be thrown at the next
+ invocation of an elpi code.
+4. The event listener for instance/class creation can be extended with new
+ elpi programs via the command `Elpi Register TC Compiler PROG`, where `PROG`
+ is the name of the new elpi program called by the `Event` listener of coq.
+ Note that in the case of the creation of a
+ - Type class $C$, `PROG` is called with `[str C]` as argument where `C` is the
+ name of the class
+ - Instance $I$, `PROG` is called with `[str I, str C, str Loc, int Prio]`
+ where `I` is the name of the instance, `C` the name of the class it
+ implements, `Loc` is its `Locality` (one among `Local`, `Global`, `Export`)
+ and `Prio` is its priority.
+
+ The default elpi program for instance and class insertion is called
+ `auto_compiler` (see [here](https://github.com/FissoreD/coq-elpi/blob/a11558758de0a1283bd9224b618cc75e40f118fb/apps/tc/theories/tc.v#L61))
+5. A registered event listener for instance/class can be deactivated, activated
+ respectively with
+ 1. `Elpi TC Activate Observer PROG.`
+ 2. `Elpi TC Deactivate Observer PROG.`
+
+ by default, once registered, the elpi program `PROG` is activated
+
+### Instance locality
+
+The instances in the elpi database respect the locality given by the user. This
+is possible thanks to the attributes from
+[here](https://github.com/FissoreD/coq-elpi/blob/ac036a71f359bc1c1ee3893949d3371df10b0aef/coq-builtin.elpi#L355).
+When an instance is created the `Event` listener transfer the locality of the
+instance to the elpi program in charge to make the insertion (see
+[here](https://github.com/FissoreD/coq-elpi/blob/ac036a71f359bc1c1ee3893949d3371df10b0aef/apps/tc/elpi/compiler.elpi#L154)
+and
+[here](https://github.com/FissoreD/coq-elpi/blob/ac036a71f359bc1c1ee3893949d3371df10b0aef/apps/tc/src/coq_elpi_tc_register.ml#L37)).
+
+As a small remark, we should consider that instances depending on section
+variables should be *recompiled* on section end in order to abstract them.
+In the example below
+
+```coq
+Section Foo.
+ Variable (A B: Type) (HA : Eqb A) (HB : Eqb B).
+ Global Instance eqProd' : Eqb (A * B) := {...}.
+
+ Elpi TC.Print_instances eqb.
+ (* Here the elpi database has the instances HA, HB and eqProd' *)
+ (*
+ And the rules for eqProd' is
+ tc-Eqb {{prod A B}} {{eqProd'}}.
+
+ Remark: Here A and B are not elpi variables, but the coq variables from the
+ context
+ *)
+End Foo.
+
+Elpi TC.Print_instances eqb.
+(*
+ Here HA and HB are removed since local to Foo and
+ eqProd' has been recompiled abstracting and A, B, HA and HB. They are now
+ arguments of this instance
+*)
+(*
+ The new rules for eqProd' is now
+ tc-Eqb {{prod lp:A lp:B}} {{eqProd' lp:A lp:B lp:HA lp:HB}} :-
+ tc-Eqb A HA, tc-Eqb B HB.
+
+ Remark: Here A and B are elpi variables and HA, PB are the proof that we can
+ prove {{Eqb lp:A}} and {{Eqb lp:B}}
+*)
+```
+
+Concretely, in a section, we consider all instances as **local** in elpi. On
+section end, the `Event` listener for instance creation triggers a new call to
+the elpi program for instance compilation. This trigger contains the same event
+as the one for the instance creation, but now elpi is capable to compile the
+instance abstracting the section variable. Finally, if we are not in a section,
+instance locality will depend on the "real" locality of that instance:
+
+1. If the instance is *local*, then we accumulate the attribute *@local! =>*
+2. If the instance is *global*, then we accumulate the attribute *@global! =>*
+3. If the instance is in *export* mode, then we pass no attribute, since by default,
+ elpi rules have this particular locality
+
+
+## Goal resolution
+
+The resolution of type class goals is done via the `TC_solver` tactic (see
+[here](https://github.com/FissoreD/coq-elpi/blob/d674089e5f5773d5d922f185e2ff058e595fa8b8/apps/tc/theories/tc.v#L29)
+and [here](elpi/solver.elpi)). This tactic take the goal and start by
+introducing the quantified variables if any, then it compiles the hypotheses
+whose type is a type class and finally start by solving the goal by looking for
+the instances in the elpi database. Note that the tactic, per se, is not
+complicated since the search of instances is based on a DFS backtracking on
+failure which is the builtin search mode of query resolution in elpi.
+
+The elpi tactic can be called by the classic `elpi TC_solver` on the current
+goal, however, this can be done implicitly done using the classic tactics of coq
+doing type class resolution. In particular, we want to make our solver and coq
+one coexist. The user may whish the elpi solver to solve `Only` goals concerning
+particular type classes (for example, those defined in its library) and leave
+coq to solve the other otherwise. To do so we can call the command `Elpi
+Override TC TC_solver Only Eqb` which activates the resolution of goal of goal
+concerning `Eqb` which the solver `TC_solver`. Note that multiple solvers can be
+created and activated to solve different tasks. To do so, we take advantage of
+the `Typeclasses.set_solve_all_instances` function from coq (see
+[here](https://github.com/coq/coq/blob/f022d5d194cb42c2321ea91cecbcce703a9bcad3/pretyping/typeclasses.mli#L141))
+which allows to set a solver to be called on type class goals. We have taken the
+file [`classes.ml`] from
+[here](https://github.com/coq/coq/blob/f022d5d194cb42c2321ea91cecbcce703a9bcad3/vernac/classes.ml#L1)
+and slightly modified the function
+[`resolve_all_evars`](https://github.com/FissoreD/coq-elpi/blob/17d1f20d3d4f37abfeee7edcf31f3757fd515ff3/apps/tc/src/coq_elpi_class_tactics_hacked.ml#L1165).
+Now that function, before solving a goal verifies if the current goal contains
+only type classes overriden by the user and if so, it uses the elpi solver for
+its resolution, otherwise, it calls the default coq solver. Note that the choice
+of using elpi or coq solver is done
+[here](src/coq_elpi_class_tactics_takeover.ml). Moreover, we provide different
+commands to
+
+1. Override all type class goals and solve them by the solver of elpi, that
+ command is `Elpi Override TC TC_solver All`.
+2. Override only some type classes, that command is `Elpi Override TC TC_solver
+ Only ClassQualid+` where `ClassQualid+` is a non empty list of type class
+ names. A valid call to this command is, for example, `Elpi Override TC
+ TC_solver Only Eqb Decidable`.
+3. Override no type class, *i.e.* solve all goals with coq solver with the
+ command `Elpi Override TC TC_solver None`.
+4. Blacklist some type classes from elpi solver, `Elpi Override TC -
+ ClassQualid+`. For instance `Elpi Override TC TC_solver Only Eqb Decidable.
+ Elpi Override TC - Decidable` in equivalent to `Elpi Override TC TC_solver
+ Only Eqb`.
+5. Add type classes to be solved by the solver of elpi `Elpi Override TC +
+ ClassQualid+`. For instance, `Elpi Override TC TC_solver Only Eqb. Elpi
+ Override TC + Decidable` is equivalent to `Elpi Override TC TC_solver Only
+ Eqb Decidable`.
+
+All of these commands are meant to dynamically change the resolution of type
+classes goal in `.v` files.
+
+## Commands
+
+A small recap of the available elpi commands:
+
+
+
+ TC.Print_instances
(click to expand)
+
+
+ This commands prints the list of instances inside the elpi database grouped by
+ type class and in order of priority. Note that custom rules will not appear
+ in this list. This command can also be called with the name of a type class
+ to print only the implementation of that type class in elpi. An example of the
+ result for the command `Elpi print_instance Eqb.`
+
+ ```
+ Instances list for const «Eqb» is:
+ const «eqBool»
+ const «eqProd»
+ ```
+
+
+
+
+
+ TC.Set_deterministic
(click to expand)
+
+
+ Take the name of a type class in parameter and sets the search mode of that
+ class to deterministic (see [here](#deterministic-search))
+
+
+
+
+
+ TC.Get_class_info ClassName
(click to expand)
+
+
+ Prints the name of the predicate associated to the class `ClassName`
+ and its search mode (`deterministic|classic`). This command is useful
+ especially when you want to add a new custom rule for a goal resolution and
+ want to know the name of the predicate of the targeted class.
+
+ Example:
+
+ ```coq
+ Elpi TC.Get_class_info Eqb.
+
+ (* Output:
+ The predicate of indt «Eqb» is tc-Eqb and search mode is classic *)
+ ```
+
+
+
+
+
+ TC.AddHook G OldName NewName
(click to expand)
+
+
+ See [](#technical-details)
+
+
+
+**Note:** in a new library you may wish to automatically compile into your elpi
+database the existing classes and instances on which you library depends. To
+do so, the $4$ following commands may be useful:
+
+- `TC.AddAllClasses`: look for all the defined classes and creates their predicate
+- `TC.AddClasses ClassName+`: compile the predicate for the classes in argument
+- `TC.AddAllInstances`: look for all the defined instances and compile them
+- `TC.AddInstances InstName+`: compiles al the instances passed in argument
+
+**Note:** it is important to create the predicate of type classes (if not already done)
+before the insertion of instances otherwise this would throw an exception.
+
+## Flags
+
+Here the list of the flags available (all of them are `off` by default):
+
+
+
+ TC IgnoreEtaReduction
(click to expand)
+
+
+ Solves the goal ignoring eta-reduction, in that case it will no longer possible
+ to unify `fun x => F x` with `F`
+
+
+
+
+ TC ResolutionTime
(click to expand)
+
+
+ Print the time taken to solve a goal by looking into the set of rules in the
+ database of elpi
+
+
+
+
+ TC NameShortPath
(click to expand)
+
+
+ Experimental and discouraged, it can be used to compile the predicate of type
+ classes without putting the `tc-Path.` prefix before `tc-ClassName` (see
+ [here](#class-compilation)). For example, the type class `Decidable` from
+ `Coq.Classes` is compiled into the predicate
+ `tc-Coq.Classes.DecidableClass.tc-Decidable`. For small tests, if you want a
+ predicate called simply `tc-Decidable` you can either use the namespace of
+ elpi (see
+ [here](https://github.com/LPCIC/elpi/blob/master/ELPI.md#namespaces)) or
+ activate the option `NameShortPat` which creates the predicate with the
+ short name `tc-Decidable`
+
+
+
+
+ TC TimeRefine
(click to expand)
+
+
+ Prints the time taken by coq to refine the elpi solution in to the coq term
+
+
+
+
+ Experimental: TC CompilerWithPatternFragment
(click to expand)
+
+
+ Compile instances using the pattern fragment unification of elpi: the coq
+ term applications (`app [HD | TL]`) are replaced with the elpi application
+ `(HDe TLe)` where `HDe` is the elpi representation of `HD` (similarly for `TLe`)
+
+
+
+## WIP
+
+1. Mode management:
+ - Classes with a single user defined should be taken into account to use the
+ elpi modes
+ - Classes with multiple modes ??
+2. Clarify pattern fragment unification
+3. Topological sort of premises in modes are activated
+4. Option to disable auto compiler (maybe)
+
diff --git a/apps/tc/_CoqProject b/apps/tc/_CoqProject
new file mode 100644
index 000000000..50e07f4cf
--- /dev/null
+++ b/apps/tc/_CoqProject
@@ -0,0 +1,22 @@
+# Hack to see Coq-Elpi even if it is not installed yet
+-Q ../../theories elpi
+-I ../../src
+-docroot elpi.apps
+
+-R theories elpi.apps
+-R elpi elpi.apps.tc
+-R tests elpi.apps.tc.tests
+
+src/coq_elpi_tc_register.ml
+src/coq_elpi_tc_hook.mlg
+src/coq_elpi_class_tactics_takeover.ml
+src/coq_elpi_class_tactics_hacked.ml
+src/elpi_tc_plugin.mlpack
+
+-I src/
+src/META.coq-elpi-tc
+
+theories/db.v
+theories/add_commands.v
+theories/tc.v
+theories/wip.v
diff --git a/apps/tc/_CoqProject.test b/apps/tc/_CoqProject.test
new file mode 100644
index 000000000..2eb1886e8
--- /dev/null
+++ b/apps/tc/_CoqProject.test
@@ -0,0 +1,52 @@
+-arg -w -arg -Not-added
+
+# Hack to see Coq-Elpi even if it is not installed yet
+-Q ../../theories elpi
+-I ../../src
+
+-Q elpi elpi.apps.tc
+-R theories elpi.apps
+-R tests elpi.apps.tc.tests
+-I src
+
+
+# Register (de-)activation
+tests/register/f1.v
+tests/register/f2.v
+tests/register/f3.v
+
+tests/hook_test.v
+
+tests/auto_compile.v
+
+# Import order of instances
+tests/importOrder/sameOrderCommand.v
+tests/importOrder/f1.v
+tests/importOrder/f2a.v
+tests/importOrder/f2b.v
+tests/importOrder/f3a.v
+tests/importOrder/f3b.v
+tests/importOrder/f3c.v
+tests/importOrder/f3d.v
+tests/importOrder/f3e.v
+tests/importOrder/f3f.v
+tests/importOrder/f3g.v
+
+tests/nobacktrack.v
+tests/patternFragment.v
+tests/contextDeepHierarchy.v
+# tests/test_commands_API.v
+tests/section_in_out.v
+tests/eqSimplDef.v
+
+tests/injTest.v
+# Test with light version of base.v of stdpp
+tests/stdppInj.v
+tests/stdppInjClassic.v
+tests/test.v
+
+tests/indt_to_inst.v
+
+tests/bigTest.v
+
+examples/tutorial.v
\ No newline at end of file
diff --git a/apps/tc/elpi/WIP/modes.elpi b/apps/tc/elpi/WIP/modes.elpi
new file mode 100644
index 000000000..220e971af
--- /dev/null
+++ b/apps/tc/elpi/WIP/modes.elpi
@@ -0,0 +1,44 @@
+/* license: GNU Lesser General Public License Version 2.1 or later */
+/* ------------------------------------------------------------------------- */
+
+% pred make-modes-cl i:gref, i:list term, i:term, i:list (list hint-mode), i:list (list term), o:prop.
+% make-modes-cl T V (prod _ _ X) HintModes L (pi x\ C x):-
+% std.map HintModes (x\r\ [r|_] = x) FST,
+% std.map HintModes (x\r\ [_|r] = x) LAST,
+% pi x\ sigma NewL\
+% std.map2 L FST (l\m\r\ if (m = mode-input) (r = [x | l]) (r = l)) NewL,
+% make-modes-cl T [x | V] (X x) LAST NewL (C x).
+% make-modes-cl T V _ _ L Clause :-
+% Ty = {coq.mk-app (global T) {std.rev V}},
+% Clause = (pi s\ tc T Ty s :- std.forall L (x\ std.exists x var), !, coq.error "Invalid mode for" Ty).
+
+% takes the type of a class and build a list
+% of hint mode where the last element is mandatory
+pred make-last-hint-mode-input i:term, o:list hint-mode.
+make-last-hint-mode-input (prod _ _ (x\ (prod _ _ _) as T)) [mode-output | L] :-
+ pi x\ make-last-hint-mode-input (T x) L.
+make-last-hint-mode-input (prod _ _ _) [mode-input].
+make-last-hint-mode-input (sort _) [].
+
+% build a list of the seme langht as the the passed one
+% where all the elements are []
+pred build-empty-list i:list B, o:list (list A).
+build-empty-list [] [].
+build-empty-list [_ | TL] [[] | L] :-
+ build-empty-list TL L.
+
+% add the hint modes of a Class to the database.
+% note that if the Class has not specified hint mode
+% then we assume the hint mode to be - - - ... !
+pred add-modes i:gref.
+:if "add-modes"
+add-modes GR :-
+ % the hint mode is added only if not exists
+ if (not (class GR _ _)) (
+ coq.env.typeof GR Ty,
+ coq.hints.modes GR "typeclass_instances" ModesProv,
+ if (ModesProv = []) (Modes = [{make-last-hint-mode-input Ty}]) (Modes = ModesProv),
+ % make-modes-cl GR [] Ty Modes {build-empty-list Modes} Cl,
+ % add-tc-db _ (after "firstHook") Cl,
+ ) true.
+add-modes _.
\ No newline at end of file
diff --git a/apps/tc/elpi/alias.elpi b/apps/tc/elpi/alias.elpi
new file mode 100644
index 000000000..7f8844038
--- /dev/null
+++ b/apps/tc/elpi/alias.elpi
@@ -0,0 +1,19 @@
+/* license: GNU Lesser General Public License Version 2.1 or later */
+/* ------------------------------------------------------------------------- */
+
+pred alias i:term, o:term.
+
+pred replace-with-alias.aux i:list term, o:list term, o:bool.
+replace-with-alias.aux [] [] ff.
+replace-with-alias.aux [X | Xs] [Y | Ys] B :-
+ replace-with-alias X Y B',
+ replace-with-alias.aux Xs Ys B'',
+ or B' B'' B.
+
+% [replace-with-alias T T1 Changed] T1 is T where aliases are replaced
+pred replace-with-alias i:term, o:term, o:bool.
+replace-with-alias A Sol tt :- alias A Sol',
+ replace-with-alias Sol' Sol _.
+replace-with-alias (app ToReplace) (app Sol) A :-
+ replace-with-alias.aux ToReplace Sol A.
+replace-with-alias A A ff.
\ No newline at end of file
diff --git a/apps/tc/elpi/base.elpi b/apps/tc/elpi/base.elpi
new file mode 100644
index 000000000..531193a9a
--- /dev/null
+++ b/apps/tc/elpi/base.elpi
@@ -0,0 +1,62 @@
+/* license: GNU Lesser General Public License Version 2.1 or later */
+/* ------------------------------------------------------------------------- */
+
+% [count L X R] counts the occurrences of X in L
+pred count i:list A, i:A, o:int.
+count [] _ 0.
+count [A | TL] A R :- count TL A X, R is (X + 1).
+count [_ | TL] A R :- count TL A R.
+
+pred expected-found i:A, i:A.
+expected-found Expected Found :-
+ Expected = Found;
+ halt "Assertion error"
+ "\nExpected :" Expected
+ "\nFound :" Found.
+
+pred last-no-error i:list A, o:A.
+last-no-error A B :-
+ (std.last [] _ :- !, fail) => std.last A B.
+
+% [find L F R] returns the first R in L such that (F R) is valid
+pred find i:list A, i:(A -> prop), o:A.
+find [] _ _ :- std.fatal-error "find element not found".
+find [R | _] F R :- F R.
+find [_ | L] F R :- find L F R.
+
+pred find-opt i:list A, i:(A -> prop), o:(option A).
+find-opt [] _ none.
+find-opt [R | _] F (some R) :- F R.
+find-opt [_ | L] F R :- find-opt L F R.
+
+pred list-init i:int, i:(int -> A -> prop), o:list A.
+list-init N _ _ :- N < 0, std.fatal-error "list-init negative length".
+list-init 0 _ [] :- !.
+list-init N F [A | TL] :-
+ F N A, N1 is N - 1, list-init N1 F TL.
+
+pred args->str-list i:list argument, o: list string.
+args->str-list L Res :-
+ std.map L (x\r\ str r = x) Res.
+
+pred or i:bool, i:bool, o:bool.
+or ff ff ff :- !.
+or _ _ tt.
+
+pred neg i:bool, o:bool.
+neg tt ff.
+neg ff tt.
+
+pred fail->bool i:prop, o:bool.
+fail->bool P ff :- P, !.
+fail->bool _ tt.
+
+pred sep.
+sep :- coq.say "---------------------------------".
+
+pred do i:list prop.
+do [].
+do [P|PS] :- P, do PS.
+
+pred do-once i:prop.
+do-once A :- A, !.
\ No newline at end of file
diff --git a/apps/tc/elpi/compiler.elpi b/apps/tc/elpi/compiler.elpi
new file mode 100644
index 000000000..6c9b2c5bb
--- /dev/null
+++ b/apps/tc/elpi/compiler.elpi
@@ -0,0 +1,237 @@
+/* license: GNU Lesser General Public License Version 2.1 or later */
+/* ------------------------------------------------------------------------- */
+
+% returns the classes on which the current gref depends
+pred get-class-dependencies i:gref, o:list gref.
+get-class-dependencies GR Res :-
+ coq.env.dependencies GR _ DepSet,
+ coq.gref.set.elements DepSet DepList,
+ std.filter DepList coq.TC.class? Res.
+
+pred unify-fo i:list name, i:list term, i:list (term -> term), o:term, i:list term, o:term.
+unify-fo [Name | Names] [Ty | Tys] [Fun | Funs] (fun Name Ty Fun) [X|Xs] R :-
+ var R, !,
+ unify-fo Names Tys Funs (Fun X) Xs R.
+unify-fo _ _ _ F L R :- var R, !, coq.mk-app F L R.
+unify-fo _ _ _ F L (app R) :-
+ std.appendR H L R,
+ if (H = [X]) (F = X) (F = app H).
+unify-fo _ _ _ F [] F.
+
+pred remove-ho-unification i:prop, i:bool, i:int, i:term, i:term, i:list prop, i:list term, i:list name, i:list term, i:list (term -> term), i:list term, i:list prop, o:prop.
+:name "remove-ho-unification:start"
+remove-ho-unification IsHead IsPositive 0 Ty AppInst Premises [] _ _ _ _ Fixes Clause :- !,
+ copy Ty Ty1,
+ copy AppInst AppInst1,
+ if (IsPositive = tt)
+ (make-tc IsHead Ty1 AppInst1 {std.append Fixes Premises} Clause)
+ (make-tc IsHead Ty1 AppInst1 Premises Clause1, std.append Fixes [Clause1] L, Clause = do L).
+remove-ho-unification IsHead IsPositive 0 Ty AppInst Premises [(app [X | Y] as G) | TL] Names Types Funs [Gen | GensTL] Fixes P2 :- !,
+ std.length Y Arity,
+ std.split-at Arity Types SubTypes TypesTL,
+ std.split-at Arity Names SubNames NamesTL,
+ std.split-at Arity Funs SubFuns FunsTL,
+ P1 = (unify-fo SubNames SubTypes SubFuns X Y Gen),
+ copy G Gen =>
+ remove-ho-unification IsHead IsPositive 0 Ty AppInst Premises TL NamesTL TypesTL FunsTL GensTL [P1 | Fixes] P2.
+
+remove-ho-unification IsHead tt N Ty AppInst Premises LT NameL TypeL FunL GenL Fixes (pi ty f name gen\ Clause ty f name gen) :-
+ N > 0,
+ N1 is N - 1,
+ pi name ty f gen\ remove-ho-unification IsHead tt N1 Ty AppInst Premises LT
+ [name | NameL] [ty | TypeL] [f | FunL]
+ [gen | GenL] Fixes (Clause ty f name gen).
+
+remove-ho-unification IsHead ff N Ty AppInst Premises LT NameL TypeL FunL GenL Fixes (sigma ty f name gen\ Clause ty f name gen) :-
+ N > 0,
+ N1 is N - 1,
+ pi name ty f gen\ remove-ho-unification IsHead ff N1 Ty AppInst Premises LT
+ [name | NameL] [ty | TypeL] [f | FunL]
+ [gen | GenL] Fixes (Clause ty f name gen).
+
+pred pattern-fragment? i:term.
+pattern-fragment? (app [HD|TL]) :-
+ not (HD = global _), distinct_names [HD | TL].
+
+pred get-pattern-fragment i:term, o:list term.
+get-pattern-fragment T1 TL :- !,
+ (pi L G GL\ fold-map (app L as G) GL G [G | GL] :- distinct_names L) =>
+ % (pi G GL\ fold-map (app _ as G) GL G GL) =>
+ (pi G GL\ fold-map (prod _ _ _ as G) GL G GL) =>
+ fold-map T1 [] _ TL.
+
+/*
+compile-aux [Ty Inst Premises PiAccRev UnivL IsPositive Clause No-Premises]
+Ty : the type of the instance
+Inst : the instance term on the form (global InstGref)
+Premises : list of constraints/premises of an instances found from its type
+PiAccRev : the list of pi variables accumulated from the (prod _ _ Bo) of the
+ type of Inst. The will be used on the solution of the clause
+ coq.mk-app Inst {std.rev PiAccRev} Sol
+UnivL : the list of universes of types inside Ty that are replaced with
+ a fresh variable to simplify unification
+IsPositive : bring the information about the positivity of the current sub-term
+ e.g. if T = A -> (B -> C) -> D, then:
+ D is positive in T, (B -> C) is negative, A is positive in T
+ C is positivie in (B -> C), B is negative in (B -> C)
+ IsPositive is used to know how to accumulate sigma Xi\ and pi x\ in the
+ current clause
+IsHead : a prop [true|false] to know if we are compiling the head of the
+ instance or one hypothesis of its premises
+Clause : the solution to be returned
+No-Premises : a boolean saying if the returned clause as no premises that is an
+ instance with no hypothesis
+*/
+pred compile-aux i:term, i:term, i:list prop, i:list term, i:list univ, i:bool, i:prop, o:prop, o:bool.
+:name "compiler-aux:start"
+compile-aux Ty I [] [] [X | XS] IsPositive IsHead (pi x\ C x) IsLeaf :- !,
+ pi x\ copy (sort (typ X)) (sort (typ x)) => copy Ty (Ty1 x),
+ compile-aux (Ty1 x) I [] [] XS IsPositive IsHead (C x) IsLeaf.
+compile-aux (prod N T F) I RevPremises ListVar [] IsPositive IsHead Clause ff :- !,
+ (if (IsPositive = tt) (Clause = pi x\ C x) (Clause = (pi x\ decl x N T => C x))),
+ pi p\ sigma NewPremise TC L\
+ if (get-TC-of-inst-type T TC, coq.TC.class? TC /*, not (occurs p (F p))*/)
+ (compile-aux T p [] [] [] {neg IsPositive} false NewPremise _,
+ if (class TC _ deterministic)
+ (L = [do-once NewPremise | RevPremises])
+ (L = [NewPremise | RevPremises])) (L = RevPremises),
+ compile-aux (F p) I L [p | ListVar] [] IsPositive IsHead (C p) _.
+compile-aux Ty I RevPremises ListVar [] _ IsHead Clause tt :-
+ not (is-option-active {oTC-use-pattern-fragment-compiler}), !,
+ std.rev RevPremises RevRHS,
+ coq.mk-app I {std.rev ListVar} AppInst,
+ make-tc IsHead Ty AppInst RevRHS Clause.
+compile-aux Ty I RevPremises ListVar [] IsPositive IsHead Clause tt :- !,
+ std.rev RevPremises Premises,
+ coq.mk-app I {std.rev ListVar} AppInst,
+ std.append {get-pattern-fragment Ty} {get-pattern-fragment AppInst} Term-to-be-fixed,
+ std.fold Term-to-be-fixed 0 (e\acc\r\ sigma L X\ e = app X, std.length X L, r is acc + L - 1) Len,
+ remove-ho-unification IsHead IsPositive Len Ty AppInst Premises Term-to-be-fixed [] [] [] [] [] Clause.
+
+% build a list of Clauses of type tc to be temporarly added to the
+% database, used in theorems having assumptions.
+pred compile-ctx i:goal-ctx, o:list prop.
+compile-ctx [] [].
+compile-ctx [X | Xs] [Clause | ResTl] :-
+ (decl Var _ Ty = X; def Var _ Ty _ = X),
+ is-instance-term Ty,
+ compile-ty Ty Var _ _ Clause,
+ compile-ctx Xs ResTl.
+compile-ctx [_ | Tl] L :- compile-ctx Tl L.
+
+pred compile-ty i:term, i:term, o:bool, o:gref, o:prop.
+compile-ty Ty Inst IsLeaf TC-of-Inst Clause:-
+ if (get-TC-of-inst-type Ty TC-of-Inst)(
+ @redflags! coq.redflags.beta => coq.reduction.lazy.norm Ty Ty1,
+ coq.univ.variable.set.elements {coq.univ.variable.of-term Ty1} L,
+ std.map L (x\r\ coq.univ.variable r x) L1,
+ compile-aux Ty1 Inst [] [] L1 tt true Clause IsLeaf)
+ % (coq.warning "" "" "Adding polymorphic Instance" Inst).
+ true.
+
+pred compile i:gref, o:bool, o:gref, o:prop.
+compile InstGR IsLeaf TC-of-Inst Clause:-
+ coq.env.typeof InstGR Ty,
+ compile-ty Ty (global InstGR) IsLeaf TC-of-Inst Clause.
+
+% if an instance depends on multiple TC then a warning is raised.
+pred warn-multiple-deps i:gref, i:list gref.
+warn-multiple-deps Inst Dep :-
+ if (fail, {std.length Dep} > 1) (
+ coq.warning "add-inst-with-multiple-deps" "TC-warning"
+ "Adding" Inst "which dependes on mulitple class dependencies:" Dep)
+ true.
+
+pred has-context-deps i:gref.
+has-context-deps GR :-
+ coq.env.section SectionVars,
+ coq.env.dependencies GR _ Deps,
+ std.exists SectionVars (x\ coq.gref.set.mem (const x) Deps).
+
+pred is-local.
+is-local :- std.mem {attributes} (attribute "local" _).
+
+pred get-locality i:string, o:list prop.
+get-locality "Local" [@local!].
+get-locality _ [@local!] :- coq.env.current-section-path [_ | _].
+get-locality "Global" [@global!].
+get-locality "Export" [].
+
+pred add-inst i:gref, i:gref, i:string, i:int.
+add-inst Inst TC Locality Prio :-
+ coq.env.current-section-path SectionPath,
+ compile Inst _ TC Clause,
+ % TODO: a clause is flexible if an instance is polimorphic (pglobal)
+ not (var Clause),
+ if (Prio = -1) (get-inst-prio Inst Prio1) (Prio1 = Prio),
+ Graft is after (int_to_string Prio1),
+ get-full-path Inst ClauseName,
+ get-locality Locality LocalityProp,
+ LocalityProp => (add-tc-db ClauseName Graft Clause, add-tc-db _ Graft (instance SectionPath Inst TC)).
+add-inst Inst _ _ _ :-
+ coq.warning "Not-added" "TC_solver" "Warning : Cannot compile " Inst "since it is pglobal".
+
+% [add-inst->db IgnoreClassDepL ForceAdd Inst] add the Inst to
+% the database except those depending on at least one
+% inside IgnoreClassDepL
+pred add-inst->db i:list gref, i:bool, i:gref.
+:name "add-inst->db:start"
+add-inst->db IgnoreClassDepL ForceAdd Inst :-
+ coq.env.current-section-path SectionPath,
+ get-class-dependencies Inst Dep,
+ warn-multiple-deps Inst Dep,
+ if ((ForceAdd = tt; not (instance _ Inst _)),
+ not (std.exists Dep (std.mem IgnoreClassDepL)), not (banned Inst))
+ (
+ compile Inst _IsLeaf TC-of-Inst Clause,
+ % TODO: a clause is flexible if an instance is polimorphic (pglobal)
+ not (var Clause),
+ Graft is after (int_to_string {get-inst-prio Inst}),
+ get-full-path Inst ClauseName,
+ if (is-local) (Visibility = [@local!])
+ (if (has-context-deps Inst)
+ (@local! => add-tc-db _ Graft (instance SectionPath Inst TC-of-Inst))
+ (@global! => add-tc-db _ Graft (instance [] Inst TC-of-Inst)), Visibility = [@global!]),
+ Visibility => add-tc-db ClauseName Graft Clause
+ )
+ true; @global! => add-tc-db _ _ (banned Inst),
+ coq.warning "Not-added" "TC_solver" "Warning : Cannot compile " Inst "since it is pglobal".
+
+% add all the instances of a TC
+pred add-inst-of-tc i:list gref, i:list gref, i:gref.
+add-inst-of-tc IgnoreDepClassGR IgnoreInstsGR GR:-
+ get-inst-by-tc-name GR InstL,
+ std.filter InstL (x\ not (std.mem IgnoreInstsGR x)) InstLF,
+ std.forall InstLF (add-inst->db IgnoreDepClassGR ff).
+
+pred add-tc-or-inst-gr i:list string, i:list string, i:list gref.
+add-tc-or-inst-gr IgnoreDepClass IgnoreInsts Names :-
+ std.map IgnoreDepClass coq.locate IgnoreDepClassGR,
+ std.map IgnoreInsts coq.locate IgnoreInstsGR,
+ std.forall Names (GR\
+ if2 (coq.TC.class? GR)(add-inst-of-tc IgnoreDepClassGR IgnoreInstsGR GR)
+ (is-instance-gr GR)(add-inst->db IgnoreDepClassGR ff GR)
+ (coq.warning "not-inst-nor-tc" "TC-warning" GR "is neither a TC nor a instance")
+ ).
+
+% [add-tc-or-inst IgnoreDepClass ClassName] look
+% for all the instances of ClassName and call the pred
+% add-inst->db
+pred add-tc-or-inst i:list string, i:list string, i:list string.
+add-tc-or-inst IgnoreDepClass IgnoreInsts Names :-
+ std.map Names coq.locate L,
+ add-tc-or-inst-gr IgnoreDepClass IgnoreInsts L.
+
+% takes a Path and a GR and returns if
+% the GR is located in Path
+pred is-in-path i:string, i:gref.
+is-in-path Path GR :-
+ std.mem {coq.gref->path GR} Path.
+
+% Look for the instances of ClassName
+% that are located in Path.
+pred add-path i:string, i:string.
+add-path ClassName Path :-
+ coq.locate ClassName GR,
+ std.filter {get-inst-by-tc-name GR} (is-in-path Path) InstInPath,
+ std.forall InstInPath (add-inst->db [] ff).
\ No newline at end of file
diff --git a/apps/tc/elpi/create_tc_predicate.elpi b/apps/tc/elpi/create_tc_predicate.elpi
new file mode 100644
index 000000000..28ee4ea93
--- /dev/null
+++ b/apps/tc/elpi/create_tc_predicate.elpi
@@ -0,0 +1,49 @@
+/* license: GNU Lesser General Public License Version 2.1 or later */
+/* ------------------------------------------------------------------------- */
+
+pred bool->mode-term i:bool, o:pair argument_mode string.
+% TODO: here every mode is declared to O;term.
+% If you want to make it work as intended,
+% replace the output of tt with "i:term"
+:name "bool->mode-term"
+bool->mode-term tt (pr in "term").
+bool->mode-term ff (pr out "term").
+
+pred modes->string i:list bool, o:list (pair argument_mode string).
+modes->string L S :-
+ std.map L bool->mode-term S.
+
+pred make-tc-modes i:int, o:list (pair argument_mode string).
+make-tc-modes NB_args ModesStr :-
+ list-init NB_args (x\r\ r = ff) ModesBool,
+ modes->string ModesBool ModesStr.
+
+pred build-modes i:gref, o:list (pair argument_mode string).
+build-modes ClassGr Modes :-
+ is-option-active {oTC-addModes},
+ coq.hints.modes ClassGr "typeclass_instances" ModesProv,
+ not (ModesProv = []),
+ coq.hints.modes ClassGr "typeclass_instances" ModesProv,
+ std.assert! (ModesProv = [HintModes]) "At the moment we only allow TC with one Hint Mode",
+ std.map {std.append HintModes [mode-output]} (x\r\ if (x = mode-output) (r = ff) (r = tt)) ModesBool,
+ modes->string ModesBool Modes.
+build-modes ClassGr Modes :-
+ coq.env.typeof ClassGr ClassTy,
+ coq.count-prods ClassTy N',
+ N is N' + 1, % Plus one for the solution
+ make-tc-modes N Modes.
+
+pred add-class-gr i:search-mode, i:gref.
+add-class-gr SearchMode ClassGr :-
+ std.assert! (coq.TC.class? ClassGr) "Only gref of type classes can be added as new predicates",
+ if (class ClassGr _ _) true
+ (build-modes ClassGr Modes,
+ gref->pred-name ClassGr PredName,
+ coq.elpi.add-predicate "tc.db" _ PredName Modes,
+ add-tc-db _ _ (tc-mode ClassGr Modes),
+ @global! => coq.elpi.accumulate _ "tc.db" (clause _ _ (class ClassGr PredName SearchMode :- !))).
+
+pred add-class-str i:search-mode, i:string.
+add-class-str SearchMode TC_Name :-
+ coq.locate TC_Name TC_Gr,
+ add-class-gr SearchMode TC_Gr.
\ No newline at end of file
diff --git a/apps/tc/elpi/parser_addInstances.elpi b/apps/tc/elpi/parser_addInstances.elpi
new file mode 100644
index 000000000..386dbc766
--- /dev/null
+++ b/apps/tc/elpi/parser_addInstances.elpi
@@ -0,0 +1,34 @@
+/* license: GNU Lesser General Public License Version 2.1 or later */
+/* ------------------------------------------------------------------------- */
+
+kind enum type.
+type path string -> string -> enum.
+type addInstPrio int -> string -> enum.
+type tcOrInst list string -> enum.
+type ignoreInstances, ignoreClasses string -> list string -> enum.
+
+pred parse i:list argument, o:enum.
+parse [str ClassName, str "path", str Path] (path ClassName Path).
+parse [str ClassName, str "ignoreInstances" | InstNames] (ignoreInstances ClassName Res) :-
+ args->str-list InstNames Res.
+parse [str ClassName, str "ignoreClasses" | ClassNames] (ignoreClasses ClassName Res) :-
+ args->str-list ClassNames Res.
+parse ClassNames (tcOrInst Res) :- args->str-list ClassNames Res.
+parse [int N, str Instance] (addInstPrio N Instance).
+
+pred run-command i:enum.
+:if "debug"
+run-command A :- coq.say A, fail.
+run-command (ignoreClasses ClassName IgnoreClasses) :-
+ add-tc-or-inst IgnoreClasses [] [ClassName].
+run-command (tcOrInst InstNames) :-
+ add-tc-or-inst [] [] InstNames.
+run-command (path ClassName Path):-
+ add-path ClassName Path.
+run-command (ignoreInstances ClassName InstNames):-
+ add-tc-or-inst [] InstNames [ClassName].
+run-command (addInstPrio Prio InstanceName) :-
+ coq.locate InstanceName InstGr,
+ compile InstGr _ _ C,
+ S is int_to_string Prio,
+ add-tc-db _ (before S) C.
\ No newline at end of file
diff --git a/apps/tc/elpi/rewrite_forward.elpi b/apps/tc/elpi/rewrite_forward.elpi
new file mode 100644
index 000000000..b602a9a13
--- /dev/null
+++ b/apps/tc/elpi/rewrite_forward.elpi
@@ -0,0 +1,76 @@
+/* license: GNU Lesser General Public License Version 2.1 or later */
+/* ------------------------------------------------------------------------- */
+
+pred forward i:term, o:term, o:list (pair (list term) term).
+
+% Auxiliary function for rewrite-forward
+pred rewrite-forward->list i:term, i:name, i:prop, o:list prop.
+rewrite-forward->list P N (forward _ Lemma RewL) L :-
+ coq.mk-app Lemma [P] LemmaApp,
+ % coq.typecheck LemmaApp T ok,
+ % coq.say T,
+ std.map RewL (x\r\ sigma ProjL Ty Pr\
+ pr ProjL Ty = x,
+ make-proj-app ProjL LemmaApp Pr,
+ r = decl Pr N Ty) L.
+
+% Takes a decl from the context and returns the list of its atomic
+% representations by looking in the forward clauses
+pred rewrite-forward i:prop, o:list prop.
+rewrite-forward (decl P N Ty) L :-
+ std.findall (forward Ty _ _) FwdL,
+ std.map FwdL (rewrite-forward->list P N) RewFdw,
+ std.flatten RewFdw L.
+rewrite-forward _ [].
+
+% Takes a list of projections ([proj1|proj2]*) and a term T
+% and returns the coq's term (projX (projY (... (projZ T))))
+% Note that app [Proj, _, _, Rest] has two holes for the types
+% of the left and right side of Rest
+pred make-proj-app i:list term, i:term, o:term.
+make-proj-app [Proj | Projs] T (app [Proj, L, R, Rest]) :-
+ make-proj-app Projs T Rest,
+ % TODO: here we do a naive typecheck to get the types L and R of Rest,
+ % it can be done in a faster way
+ coq.typecheck Rest {{and lp:L lp:R}} ok.
+make-proj-app [] T T.
+
+% Takes a conjunction C of terms and []. It returns a list of pair:
+% The paths to the conjunct c in C and the path to reach it in C
+pred rec-split-and i:term, i:(list term), o:list (pair (list term) term).
+rec-split-and {{lp:A /\ lp:B}} DL L :-
+ LEFT = [{{proj1}} | DL],
+ RIGHT = [{{proj2}} | DL],
+ rec-split-and A LEFT AL,
+ rec-split-and B RIGHT BL,
+ std.append AL BL L.
+rec-split-and A P [pr P A].
+
+% It takes a rewriting-lemma and abstract it into elpi in a forward
+% clause of type forward. The base case wants a ∀(x : T).f x, since
+% we want to keep trace of the type T of x.
+pred compile-rewrite i:term, i:term, i:list term, o:prop.
+compile-rewrite (prod _ Ty ((x\ app _) as Bo)) Lemma L (pi x\ forward Ty LemmaApp (TL x)) :-
+ pi x\
+ coq.mk-app Lemma {std.rev L} LemmaApp,
+ rec-split-and (Bo x) [] (TL x).
+compile-rewrite (prod _ _ Bo) Lemma L (pi x\ C x) :-
+ pi x\ compile-rewrite (Bo x) Lemma [x | L] (C x).
+
+% Takes a string (the name of a rewriting-lemma),
+% compiles and adds it as a forward clause in tc.db
+pred add-lemma->forward i:string.
+add-lemma->forward Lemma :-
+ coq.locate Lemma Gr,
+ coq.env.typeof Gr Type,
+ compile-rewrite Type (global Gr) [] Cl,
+ coq.elpi.accumulate _ "tc.db" (clause Lemma _ Cl).
+
+% TODO: @FissoreD @gares should make a set in output?
+pred rewrite-dep i:list prop, o:list prop.
+rewrite-dep [] [].
+rewrite-dep [A | B] L :-
+ rewrite-forward A NewA, not (NewA = []),
+ std.append NewA B ToTreat,
+ rewrite-dep ToTreat L.
+rewrite-dep [A | TL] [A | L] :- rewrite-dep TL L.
\ No newline at end of file
diff --git a/apps/tc/elpi/solver.elpi b/apps/tc/elpi/solver.elpi
new file mode 100644
index 000000000..8aeb3c19e
--- /dev/null
+++ b/apps/tc/elpi/solver.elpi
@@ -0,0 +1,82 @@
+/* license: GNU Lesser General Public License Version 2.1 or later */
+/* ------------------------------------------------------------------------- */
+
+msolve L N :- !,
+ coq.ltac.all (coq.ltac.open solve) {std.rev L} N.
+
+pred build-context-clauses i:list prop, o:list prop.
+% Add the section's definition to the given context
+% and atomize context hypothesis if needed
+:name "build-context-clauses"
+build-context-clauses Ctx Clauses :-
+ std.map {coq.env.section}
+ (x\r\ sigma F\ coq.env.typeof (const x) F,
+ r = (decl (global (const x)) _ F)) SectionCtx,
+ std.append Ctx SectionCtx CtxAndSection,
+ compile-ctx CtxAndSection Clauses.
+
+pred tc-recursive-search i:term, o:term.
+tc-recursive-search Ty Sol :-
+ std.time (coq.safe-dest-app Ty (global TC) TL',
+ std.append TL' [Sol] TL,
+ coq.elpi.predicate {gref->pred-name TC} TL Q, Q) Time,
+ if (is-option-active {oTC-resolution-time}) (coq.say "Instance search" Time) true.
+
+:if "solve-print-goal"
+solve (goal Ctx _ Ty _ _) _ :-
+ coq.say "Ctx" Ctx "Ty" Ty, fail.
+
+% solve (goal C _ (prod N Ty F) S _ as _G) _L GL :- !,
+% @pi-decl N Ty x\
+% declare-evar [decl x N Ty|C] (Raw x) (F x) (Sol x),
+% solve (goal [decl x N Ty|C] (Raw x) (F x) (Sol x) []) _L GL,
+% if (Sol x = app [HD, x]) (S = HD) (S = fun N Ty Sol).
+% solve (goal C _ (prod N Ty F) XX _ as G) _L GL :- !,
+% % intros_if_needed Prod C []
+% (@pi-decl N Ty x\
+% declare-evar [decl x N Ty|C] (Raw x) (F x) (Sol x),
+% solve (goal [decl x N Ty|C] (Raw x) (F x) (Sol x) []) _L _,
+% coq.safe-dest-app (Sol x) Hd (Args x)),
+% if (pi x\ last-no-error (Args x) x, std.drop-last 1 (Args x) NewArgs)
+% (coq.mk-app Hd NewArgs Out, refine Out G GL) (
+% % coq.say "Not eta" (Sol x) x (fun N Ty Sol),
+% XX = (fun N Ty Sol)).
+% solve (goal C _ (prod N _ _ as P) _ A as G) _L GL :- !,
+% declare-evar C T P S',
+% G' = (goal C T P S' A),
+% refine (fun N _ _) G' GL1,
+% coq.ltac.all (coq.ltac.open solve) GL1 _,
+% refine S' G GL.
+solve (goal C _ (prod N Ty F) _ _ as G) GL :- !,
+ (@pi-decl N Ty x\
+ declare-evar [decl x N Ty|C] (Raw x) (F x) (Sol x),
+ solve (goal [decl x N Ty|C] (Raw x) (F x) (Sol x) []) _),
+ if (pi x\
+ % also check the head does not contain x
+ coq.safe-dest-app (Sol x) Hd (Args x),
+ last-no-error (Args x) x,
+ std.drop-last 1 (Args x) NewArgs)
+ (coq.mk-app Hd NewArgs Out, refine Out G GL1) (refine (fun N _ _) G GL1),
+ coq.ltac.all (coq.ltac.open solve) GL1 GL.
+% solve (goal _ _ (prod N _ _) _ _ as G) GL :- !,
+% refine (fun N _ _) G GL1,
+% coq.ltac.all (coq.ltac.open solve) GL1 GL.
+solve (goal Ctx _ Ty Sol _ as G) GL :-
+ var Sol,
+ build-context-clauses Ctx Clauses,
+ % @redflags! coq.redflags.beta => coq.reduction.lazy.norm Ty Ty1,
+ Clauses => if (tc-recursive-search Ty Proof)
+ (
+ % @no-tc! => coq.elaborate-skeleton X _ X' ok,
+ % coq.say "Solution " X "end" X' "caio",
+ % std.assert! (ground_term X') "solution not complete",
+ % (pi F\ (copy (fun _ _ x\ (app [F, x])) F :- !)) => copy X X',
+ if (is-option-active {oTC-ignore-eta-reduction})
+ (Proof' = Proof) (coq.reduction.eta-contract Proof Proof'),
+ std.time (refine Proof' G GL) Refine-Time,
+ if (is-option-active {oTC-time-refine}) (coq.say "Refine Time" Refine-Time) true;
+ coq.error "illtyped solution:" {coq.term->string Proof}
+ )
+ (GL = [seal G]).
+
+main _.
\ No newline at end of file
diff --git a/apps/tc/elpi/tc_aux.elpi b/apps/tc/elpi/tc_aux.elpi
new file mode 100644
index 000000000..3cb72af47
--- /dev/null
+++ b/apps/tc/elpi/tc_aux.elpi
@@ -0,0 +1,112 @@
+/* license: GNU Lesser General Public License Version 2.1 or later */
+/* ------------------------------------------------------------------------- */
+
+% Contains the list of classes that
+% cannot be compiled
+
+% returns the TC from the type of an instance
+pred get-TC-of-inst-type i:term, o:gref.
+get-TC-of-inst-type (prod _ _ A) GR:-
+ pi x\ get-TC-of-inst-type (A x) GR.
+get-TC-of-inst-type (app [global TC | _]) TC.
+get-TC-of-inst-type (global TC) TC.
+
+pred drop-last i:list A, i:list A, o:list A.
+drop-last [X | XS] [Y | YS] L :-
+ same_term X Y, !, drop-last XS YS L.
+drop-last L [] L' :- std.rev L L'.
+
+pred instances-of-current-section o:list gref.
+:name "MySectionEndHook"
+instances-of-current-section InstsFiltered :-
+ coq.env.current-section-path SectionPath,
+ std.findall (instance SectionPath _ _) Insts,
+ coq.env.section SectionVars,
+ std.map-filter Insts (x\r\ sigma X\ instance _ r _ = x, const X = r, not(std.mem SectionVars X)) InstsFiltered.
+
+pred is-instance-gr i:gref.
+is-instance-gr GR :-
+ coq.env.typeof GR Ty,
+ is-instance-term Ty.
+
+pred is-instance-term i:term.
+is-instance-term T :-
+ get-TC-of-inst-type T TC,
+ coq.TC.class? TC.
+
+% adds a clause to the tc.db DB at the passed grafting
+pred add-tc-db o:id, o:grafting, i:prop.
+add-tc-db ClauseName Graft PR :-
+ coq.elpi.accumulate _ "tc.db"
+ (clause ClauseName Graft PR); coq.error "cannot add " PR " to tc.db".
+
+% takes a tc-instance and return the gref of the inst
+pred inst->gref i:tc-instance, o:gref.
+inst->gref (tc-instance Res _) Res.
+
+% returns all the instances of the passed ClassName
+pred get-inst-by-tc-name i:gref, o:list gref.
+get-inst-by-tc-name TC GRL:-
+ coq.TC.db-for TC Inst,
+ std.map Inst inst->gref GRL',
+ std.rev GRL' GRL.
+
+pred app-has-class i:term.
+app-has-class T :-
+ get-TC-of-inst-type T Hd,
+ coq.TC.class? Hd.
+
+% input (∀ a, b, c ... => app [A, B, ..., Last])
+% returns Last
+pred get-last i:term, o:term.
+get-last (prod _ _ Bo) R :-
+ pi x\ get-last (Bo x) R.
+get-last (app L) R :-
+ std.last L R.
+
+% TC preds are on the form tc-[PATH_TO_TC].tc-[TC-Name]
+pred gref->pred-name i:gref, o:string.
+gref->pred-name Gr S :-
+ if (is-option-active {oTC-clauseNameShortName})
+ (Path = "")
+ (coq.gref->path Gr [Hd | Tl],
+ std.fold Tl Hd (x\acc\r\ r is acc ^ "." ^ x) Path',
+ Path is Path' ^ ".tc-"),
+ S is "tc-" ^ Path ^ {coq.gref->id Gr}.
+
+pred no-backtrack i:list prop, o:list prop.
+no-backtrack [] [].
+no-backtrack [do X | XS] [std.do! [(std.do! X') | XS']] :- !,
+ no-backtrack X X', no-backtrack XS XS'.
+no-backtrack [X | XS] [std.do! [X | XS']] :- !, no-backtrack XS XS'.
+
+pred make-tc i:prop, i:term, i:term, i:list prop, o:prop.
+make-tc _IsHead Ty Inst Hyp Clause :-
+ coq.safe-dest-app Ty (global TC) TL,
+ gref->pred-name TC TC_Str,
+ std.append TL [Inst] Args,
+ coq.elpi.predicate TC_Str Args Q,
+ if2 (Hyp = []) (Clause = Q)
+ (Hyp = [Hd]) (Clause = (Q :- Hd))
+ (Clause = (Q :- Hyp)).
+
+% returns the priority of an instance from the gref of an instance
+pred get-inst-prio i:gref, o:int.
+get-inst-prio InstGr Prio :-
+ coq.env.typeof InstGr InstTy,
+ get-TC-of-inst-type InstTy TC,
+ find-opt {coq.TC.db-for TC}
+ (x\ tc-instance InstGr (tc-priority-given Prio) = x ;
+ tc-instance InstGr (tc-priority-computed Prio) = x)
+ (some _), !.
+get-inst-prio _ 0.
+
+% TODO: @FissoreD improve this method complexity
+pred get-full-path i:gref, o:string.
+% :if "get-full-path"
+get-full-path Gr Res' :-
+ coq.gref->string Gr Path,
+ coq.env.current-section-path SectionPath,
+ std.fold SectionPath "" (e\acc\r\ r is acc ^ "." ^ e) Res,
+ Res' is Res ^ Path.
+% get-full-path _ _.
\ No newline at end of file
diff --git a/apps/tc/examples/tutorial.v b/apps/tc/examples/tutorial.v
new file mode 100644
index 000000000..2379c2a46
--- /dev/null
+++ b/apps/tc/examples/tutorial.v
@@ -0,0 +1,96 @@
+Require Import Bool.
+From elpi.apps Require Import tc.
+
+Class Eqb (T: Type) := {
+ eqb : T -> T -> bool;
+ eqb_leibniz A B: eqb A B = true <-> A = B
+}.
+
+#[refine] Instance eqBool : Eqb bool := {
+ eqb x y := if x then y else negb y
+}.
+Proof. intros [] []; intuition. Qed.
+
+#[refine] Instance eqProd (A B : Type) : Eqb A -> Eqb B -> Eqb (A * B) := {
+ eqb x y := eqb (fst x) (fst y) && eqb (snd x) (snd y)
+}.
+Proof.
+ intros [] []. split. intros; simpl in H.
+ * case (eqb a a0) eqn:aB, (eqb b b0) eqn:bB; try easy.
+ apply pair_equal_spec; destruct e, e0; split.
+ apply eqb_leibniz0; auto.
+ apply eqb_leibniz1; auto.
+ * intros. apply pair_equal_spec in H; destruct H; subst. simpl.
+ apply andb_true_intro; destruct e, e0; split.
+ apply eqb_leibniz0; auto.
+ apply eqb_leibniz1; auto.
+Qed.
+
+TC.Print_instances.
+TC.Get_class_info Eqb.
+
+(* Abstraction of elpi context variable *)
+Section Foo.
+ Variable (A B: Type) (HA : Eqb A) (HB : Eqb B).
+ #[refine] Global Instance eqProd' : Eqb (A * B) := {
+ eqb x y := eqb (fst x) (fst y) && eqb (snd x) (snd y)
+ }.
+ Proof.
+ intros [] []; simpl; split; intros.
+ apply eqb_leibniz. destruct H.
+ replace (eqb (a, b) (a0, b0)) with (eqb a a0 && eqb b b0); auto. admit.
+ apply andb_true_intro; apply pair_equal_spec in H; split;
+ apply eqb_leibniz; easy.
+ Admitted.
+
+ (* Here we see that HA and HB are compiled in elpi since their type is a class *)
+ TC.Print_instances Eqb.
+
+ (* The rules for eqProd' is as follows
+
+ shorten tc-tutorial.{tc-Eqb}.
+ tc-Eqb {{prod A B}} {{eqProd'}}.
+
+ Remark: Here A and B are not elpi variables, but the coq variables from the
+ context
+ *)
+
+ Elpi Print TC.Solver.
+End Foo.
+
+(*
+ On section end the local instances are removed (i.e. HA and HB disappears)
+ and eqProd' is recompiled
+*)
+TC.Print_instances Eqb.
+(*
+ the rules for eqProd' is as follows
+
+ shorten tc-tutorial.{tc-Eqb}.
+ tc-Eqb {{prod lp:A lp:B}} {{eqProd' lp:A lp:B lp:PA lp:PB}} :-
+ tc-Eqb A PA, tc-Eqb B PB.
+
+ Remark: Here A and B are elpi variables and PA, PB are the proof that we can
+ prove {{Eqb lp:A}} and {{Eqb lp:B}}
+*)
+
+TC.Get_class_info Eqb.
+
+Module Backtrack.
+ Elpi Override TC TC.Solver All.
+ Class NoBacktrack (n: nat).
+ TC.Set_deterministic NoBacktrack.
+ Class A (n: nat).
+
+ Instance a0 : A 0. Qed.
+ Instance nb0 : NoBacktrack 0. Qed.
+ Instance nb1 : NoBacktrack 1. Qed.
+ Instance a3 n : NoBacktrack n -> A n -> A 3. Qed.
+
+ Goal A 3. Fail apply _. Abort.
+
+ Elpi Print TC.Solver.
+End Backtrack.
+
+TC.Print_instances.
+TC.Get_class_info DecidableClass.Decidable.
diff --git a/apps/tc/src/META.coq-elpi-tc b/apps/tc/src/META.coq-elpi-tc
new file mode 100644
index 000000000..5c3045ab8
--- /dev/null
+++ b/apps/tc/src/META.coq-elpi-tc
@@ -0,0 +1,10 @@
+
+package "plugin" (
+ directory = "."
+ requires = "coq-core.plugins.ltac coq-elpi.elpi"
+ archive(byte) = "elpi_tc_plugin.cma"
+ archive(native) = "elpi_tc_plugin.cmxa"
+ plugin(byte) = "elpi_tc_plugin.cma"
+ plugin(native) = "elpi_tc_plugin.cmxs"
+)
+directory = "."
diff --git a/apps/tc/src/coq_elpi_class_tactics_hacked.ml b/apps/tc/src/coq_elpi_class_tactics_hacked.ml
new file mode 100644
index 000000000..1b9ff8113
--- /dev/null
+++ b/apps/tc/src/coq_elpi_class_tactics_hacked.ml
@@ -0,0 +1,1319 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * Copyright INRIA, CNRS and contributors *)
+(* (unit -> Pp.t) -> unit
+
+ val get_debug : unit -> int
+
+ val set_typeclasses_debug : bool -> unit
+end = struct
+ let typeclasses_debug = ref 0
+
+ let set_typeclasses_debug d = (:=) typeclasses_debug (if d then 1 else 0)
+ let get_typeclasses_debug () = if !typeclasses_debug > 0 then true else false
+
+ let set_typeclasses_verbose = function
+ | None -> typeclasses_debug := 0
+ | Some n -> typeclasses_debug := n
+ let get_typeclasses_verbose () =
+ if !typeclasses_debug = 0 then None else Some !typeclasses_debug
+
+ let () =
+ let open Goptions in
+ declare_bool_option
+ { optstage = Summary.Stage.Interp;
+ optdepr = None;
+ optkey = ["Typeclassess";"Debug"];
+ optread = get_typeclasses_debug;
+ optwrite = set_typeclasses_debug; }
+
+ let () =
+ let open Goptions in
+ declare_int_option
+ { optstage = Summary.Stage.Interp;
+ optdepr = None;
+ optkey = ["Typeclassess";"Debug";"Verbosity"];
+ optread = get_typeclasses_verbose;
+ optwrite = set_typeclasses_verbose; }
+
+ let ppdebug lvl pp =
+ if !typeclasses_debug > lvl then Feedback.msg_debug (pp())
+
+ let get_debug () = !typeclasses_debug
+end
+open Debug
+let set_typeclasses_debug = set_typeclasses_debug
+
+type search_strategy = Dfs | Bfs
+
+let set_typeclasses_strategy = function
+ | Dfs -> Goptions.set_bool_option_value iterative_deepening_opt_name false
+ | Bfs -> Goptions.set_bool_option_value iterative_deepening_opt_name true
+
+let pr_ev evs ev =
+ let evi = Evd.find_undefined evs ev in
+ let env = Evd.evar_filtered_env (Global.env ()) evi in
+ Printer.pr_econstr_env env evs (Evd.evar_concl evi)
+
+let pr_ev_with_id evs ev =
+ Evar.print ev ++ str " : " ++ pr_ev evs ev
+
+ (** Typeclasses instance search tactic / eauto *)
+
+open Auto
+open Unification
+
+let auto_core_unif_flags st allowed_evars = {
+ modulo_conv_on_closed_terms = Some st;
+ use_metas_eagerly_in_conv_on_closed_terms = true;
+ use_evars_eagerly_in_conv_on_closed_terms = false;
+ modulo_delta = st;
+ modulo_delta_types = st;
+ check_applied_meta_types = false;
+ use_pattern_unification = true;
+ use_meta_bound_pattern_unification = true;
+ allowed_evars;
+ restrict_conv_on_strict_subterms = false; (* ? *)
+ modulo_betaiota = true;
+ modulo_eta = false;
+}
+
+let auto_unif_flags ?(allowed_evars = Evarsolve.AllowedEvars.all) st =
+ let fl = auto_core_unif_flags st allowed_evars in
+ { core_unify_flags = fl;
+ merge_unify_flags = fl;
+ subterm_unify_flags = fl;
+ allow_K_in_toplevel_higher_order_unification = false;
+ resolve_evars = false
+}
+
+let e_give_exact flags h =
+ let open Tacmach in
+ Proofview.Goal.enter begin fun gl ->
+ let env = Proofview.Goal.env gl in
+ let sigma = project gl in
+ let sigma, c = Hints.fresh_hint env sigma h in
+ let (sigma, t1) = Typing.type_of (pf_env gl) sigma c in
+ Proofview.Unsafe.tclEVARS sigma <*>
+ Clenv.unify ~flags t1 <*> exact_no_check c
+ end
+
+let unify_resolve ~with_evars flags h diff = match diff with
+| None ->
+ Hints.hint_res_pf ~with_evars ~with_classes:false ~flags h
+| Some (diff, ty) ->
+ let () = assert (Option.is_empty (fst @@ hint_as_term @@ h)) in
+ Proofview.Goal.enter begin fun gl ->
+ let env = Proofview.Goal.env gl in
+ let sigma = Tacmach.project gl in
+ let sigma, c = Hints.fresh_hint env sigma h in
+ let clenv = Clenv.mk_clenv_from_n env sigma diff (c, ty) in
+ Clenv.res_pf ~with_evars ~with_classes:false ~flags clenv
+ end
+
+(** Dealing with goals of the form A -> B and hints of the form
+ C -> A -> B.
+*)
+let with_prods nprods h f =
+ if get_typeclasses_limit_intros () then
+ Proofview.Goal.enter begin fun gl ->
+ if Option.has_some (fst @@ hint_as_term h) || Int.equal nprods 0 then f None
+ else
+ let sigma = Tacmach.project gl in
+ let ty = Retyping.get_type_of (Proofview.Goal.env gl) sigma (snd @@ hint_as_term h) in
+ let diff = nb_prod sigma ty - nprods in
+ if (>=) diff 0 then f (Some (diff, ty))
+ else Tacticals.tclZEROMSG (str"Not enough premisses")
+ end
+ else Proofview.Goal.enter
+ begin fun gl ->
+ if Int.equal nprods 0 then f None
+ else Tacticals.tclZEROMSG (str"Not enough premisses") end
+
+(** Semantics of type class resolution lemma application:
+
+ - Use unification to find a well-typed substitution. There might
+ be evars in the goal and the lemma. Evars in the goal can get refined.
+ - Independent evars are turned into goals, whatever their kind is.
+ - Dependent evars of the lemma corresponding to arguments which appear
+ in independent goals or the conclusion are turned into subgoals iff
+ they are of typeclass kind.
+ - The remaining dependent evars not of typeclass type are shelved,
+ and resolution must fill them for it to succeed, otherwise we
+ backtrack.
+ *)
+
+let pr_gls sigma gls =
+ prlist_with_sep spc
+ (fun ev -> int (Evar.repr ev) ++ spc () ++ pr_ev sigma ev) gls
+
+(** Ensure the dependent subgoals are shelved after an apply/eapply. *)
+let shelve_dependencies gls =
+ let open Proofview in
+ if CList.is_empty gls then tclUNIT ()
+ else
+ tclEVARMAP >>= fun sigma ->
+ ppdebug 1 (fun () -> str" shelving dependent subgoals: " ++ pr_gls sigma gls);
+ shelve_goals gls
+
+let hintmap_of env sigma hdc secvars concl =
+ match hdc with
+ | None -> fun db -> ModeMatch (NoMode, Hint_db.map_none ~secvars db)
+ | Some hdc ->
+ fun db -> Hint_db.map_eauto env sigma ~secvars hdc concl db
+
+(** Hack to properly solve dependent evars that are typeclasses *)
+let rec e_trivial_fail_db only_classes db_list local_db secvars =
+ let open Tacticals in
+ let open Tacmach in
+ let trivial_fail =
+ Proofview.Goal.enter
+ begin fun gl ->
+ let env = Proofview.Goal.env gl in
+ let sigma = Tacmach.project gl in
+ let d = NamedDecl.get_id @@ pf_last_hyp gl in
+ let hints = push_resolve_hyp env sigma d local_db in
+ e_trivial_fail_db only_classes db_list hints secvars
+ end
+ in
+ let trivial_resolve =
+ Proofview.Goal.enter
+ begin fun gl ->
+ let tacs = e_trivial_resolve db_list local_db secvars only_classes
+ (pf_env gl) (project gl) (pf_concl gl) in
+ tclFIRST (List.map (fun (x,_,_,_,_) -> x) tacs)
+ end
+ in
+ let tacl =
+ Eauto.e_assumption ::
+ (tclTHEN Tactics.intro trivial_fail :: [trivial_resolve])
+ in
+ tclSOLVE tacl
+
+and e_my_find_search db_list local_db secvars hdc complete only_classes env sigma concl0 =
+ let prods, concl = EConstr.decompose_prod_decls sigma concl0 in
+ let nprods = List.length prods in
+ let allowed_evars =
+ let all = Evarsolve.AllowedEvars.all in
+ try
+ match hdc with
+ | Some (hd,_) when only_classes ->
+ begin match Typeclasses.class_info hd with
+ | Some cl ->
+ if cl.cl_strict then
+ let undefined = lazy (Evarutil.undefined_evars_of_term sigma concl) in
+ let allowed evk = not (Evar.Set.mem evk (Lazy.force undefined)) in
+ Evarsolve.AllowedEvars.from_pred allowed
+ else all
+ | None -> all
+ end
+ | _ -> all
+ with e when CErrors.noncritical e -> all
+ in
+ let tac_of_hint =
+ fun (flags, h) ->
+ let name = FullHint.name h in
+ let tac = function
+ | Res_pf h ->
+ let tac =
+ with_prods nprods h (unify_resolve ~with_evars:false flags h) in
+ Proofview.tclBIND (Proofview.with_shelf tac)
+ (fun (gls, ()) -> shelve_dependencies gls)
+ | ERes_pf h ->
+ let tac =
+ with_prods nprods h (unify_resolve ~with_evars:true flags h) in
+ Proofview.tclBIND (Proofview.with_shelf tac)
+ (fun (gls, ()) -> shelve_dependencies gls)
+ | Give_exact h ->
+ e_give_exact flags h
+ | Res_pf_THEN_trivial_fail h ->
+ let fst = with_prods nprods h (unify_resolve ~with_evars:true flags h) in
+ let snd = if complete then Tacticals.tclIDTAC
+ else e_trivial_fail_db only_classes db_list local_db secvars in
+ Tacticals.tclTHEN fst snd
+ | Unfold_nth c ->
+ Proofview.tclPROGRESS (unfold_in_concl [AllOccurrences,c])
+ | Extern (p, tacast) -> conclPattern concl0 p tacast
+ in
+ let tac = FullHint.run h tac in
+ let tac = if complete then Tacticals.tclCOMPLETE tac else tac in
+ let extern = match FullHint.repr h with
+ | Extern _ -> true
+ | _ -> false
+ in
+ (tac, FullHint.priority h, extern, name, lazy (FullHint.print env sigma h))
+ in
+ let hint_of_db = hintmap_of env sigma hdc secvars concl in
+ let hintl = List.map_filter (fun db -> match hint_of_db db with
+ | ModeMatch (m, l) -> Some (db, m, l)
+ | ModeMismatch -> None)
+ (local_db :: db_list)
+ in
+ (* In case there is a mode mismatch in all the databases we get stuck.
+ Otherwise we consider the hints that match.
+ Recall the local database uses the union of all the modes in the other databases. *)
+ if List.is_empty hintl then None
+ else
+ let hintl =
+ CList.map
+ (fun (db, m, tacs) ->
+ let flags = auto_unif_flags ~allowed_evars (Hint_db.transparent_state db) in
+ m, List.map (fun x -> tac_of_hint (flags, x)) tacs)
+ hintl
+ in
+ let modes, hintl = List.split hintl in
+ let all_mode_match = List.for_all (fun m -> m != NoMode) modes in
+ let hintl = match hintl with
+ (* Optim: only sort if multiple hint sources were involved *)
+ | [hintl] -> hintl
+ | _ ->
+ let hintl = List.flatten hintl in
+ let hintl = List.stable_sort
+ (fun (_, pri1, _, _, _) (_, pri2, _, _, _) -> Int.compare pri1 pri2)
+ hintl
+ in
+ hintl
+ in
+ Some (all_mode_match, hintl)
+
+and e_trivial_resolve db_list local_db secvars only_classes env sigma concl =
+ let hd = try Some (decompose_app_bound sigma concl) with Bound -> None in
+ try
+ (match e_my_find_search db_list local_db secvars hd true only_classes env sigma concl with
+ | Some (_,l) -> l
+ | None -> [])
+ with Not_found -> []
+
+let e_possible_resolve db_list local_db secvars only_classes env sigma concl =
+ let hd = try Some (decompose_app_bound sigma concl) with Bound -> None in
+ try
+ e_my_find_search db_list local_db secvars hd false only_classes env sigma concl
+ with Not_found -> Some (true, [])
+
+let cut_of_hints h =
+ List.fold_left (fun cut db -> PathOr (Hint_db.cut db, cut)) PathEmpty h
+
+let pr_depth l =
+ let rec fmt elts =
+ match elts with
+ | [] -> []
+ | [n] -> [string_of_int n]
+ | n1::n2::rest ->
+ (string_of_int n1 ^ "." ^ string_of_int n2) :: fmt rest
+ in
+ prlist_with_sep (fun () -> str "-") str (fmt (List.rev l))
+
+let is_Prop env sigma concl =
+ let ty = Retyping.get_type_of env sigma concl in
+ match EConstr.kind sigma ty with
+ | Sort s ->
+ begin match ESorts.kind sigma s with
+ | Prop -> true
+ | _ -> false
+ end
+ | _ -> false
+
+let is_unique env sigma concl =
+ try
+ let (cl,u), args = dest_class_app env sigma concl in
+ cl.cl_unique
+ with e when CErrors.noncritical e -> false
+
+(** Sort the undefined variables from the least-dependent to most dependent. *)
+let top_sort evm undefs =
+ let l' = ref [] in
+ let tosee = ref undefs in
+ let cache = Evarutil.create_undefined_evars_cache () in
+ let rec visit ev evi =
+ let evs = Evarutil.filtered_undefined_evars_of_evar_info ~cache evm evi in
+ tosee := Evar.Set.remove ev !tosee;
+ Evar.Set.iter (fun ev ->
+ if Evar.Set.mem ev !tosee then
+ visit ev (Evd.find_undefined evm ev)) evs;
+ l' := ev :: !l';
+ in
+ while not (Evar.Set.is_empty !tosee) do
+ let ev = Evar.Set.choose !tosee in
+ visit ev (Evd.find_undefined evm ev)
+ done;
+ List.rev !l'
+
+(** We transform the evars that are concerned by this resolution
+ (according to predicate p) into goals.
+ Invariant: function p only manipulates and returns undefined evars
+*)
+
+let evars_to_goals p evm =
+ let goals, nongoals = Evar.Set.partition (p evm) (Evd.get_typeclass_evars evm) in
+ if Evar.Set.is_empty goals then None
+ else Some (goals, nongoals)
+
+(** Making local hints *)
+let make_resolve_hyp env sigma st only_classes decl db =
+ let id = NamedDecl.get_id decl in
+ let cty = Evarutil.nf_evar sigma (NamedDecl.get_type decl) in
+ let rec iscl env ty =
+ let ctx, ar = decompose_prod_decls sigma ty in
+ match EConstr.kind sigma (fst (decompose_app sigma ar)) with
+ | Const (c,_) -> is_class (GlobRef.ConstRef c)
+ | Ind (i,_) -> is_class (GlobRef.IndRef i)
+ | _ ->
+ let env' = push_rel_context ctx env in
+ let ty' = Reductionops.whd_all env' sigma ar in
+ if not (EConstr.eq_constr sigma ty' ar) then iscl env' ty'
+ else false
+ in
+ let is_class = iscl env cty in
+ let keep = not only_classes || is_class in
+ if keep then
+ let id = GlobRef.VarRef id in
+ push_resolves env sigma id db
+ else db
+
+let make_hints env sigma (modes,st) only_classes sign =
+ let db = Hint_db.add_modes modes @@ Hint_db.empty st true in
+ List.fold_right
+ (fun hyp hints ->
+ let consider =
+ not only_classes ||
+ try let t = hyp |> NamedDecl.get_id |> Global.lookup_named |> NamedDecl.get_type in
+ (* Section variable, reindex only if the type changed *)
+ not (EConstr.eq_constr sigma (EConstr.of_constr t) (NamedDecl.get_type hyp))
+ with Not_found -> true
+ in
+ if consider then
+ make_resolve_hyp env sigma st only_classes hyp hints
+ else hints)
+ sign db
+
+module Search = struct
+ type autoinfo =
+ { search_depth : int list;
+ last_tac : Pp.t Lazy.t;
+ search_dep : bool;
+ search_only_classes : bool;
+ search_cut : hints_path;
+ search_hints : hint_db;
+ search_best_effort : bool;
+ }
+
+ (** Local hints *)
+ let autogoal_cache = Summary.ref ~name:"autogoal_cachee"
+ (DirPath.empty, true, Context.Named.empty, GlobRef.Map.empty,
+ Hint_db.empty TransparentState.full true)
+
+ let make_autogoal_hints only_classes (modes,st as mst) gl =
+ let env = Proofview.Goal.env gl in
+ let sigma = Proofview.Goal.sigma gl in
+ let sign = EConstr.named_context env in
+ let (dir, onlyc, sign', cached_modes, cached_hints) = !autogoal_cache in
+ let cwd = Lib.cwd () in
+ let eq c1 c2 = EConstr.eq_constr sigma c1 c2 in
+ if DirPath.equal cwd dir &&
+ (onlyc == only_classes) &&
+ Context.Named.equal eq sign sign' &&
+ cached_modes == modes
+ then cached_hints
+ else
+ let hints = make_hints env sigma mst only_classes sign in
+ autogoal_cache := (cwd, only_classes, sign, modes, hints); hints
+
+ let make_autogoal mst only_classes dep cut best_effort i g =
+ let hints = make_autogoal_hints only_classes mst g in
+ { search_hints = hints;
+ search_depth = [i]; last_tac = lazy (str"none");
+ search_dep = dep;
+ search_only_classes = only_classes;
+ search_cut = cut;
+ search_best_effort = best_effort }
+
+ (** In the proof engine failures are represented as exceptions *)
+ exception ReachedLimit
+ exception NoApplicableHint
+ exception StuckGoal
+
+ (** ReachedLimit has priority over NoApplicableHint to handle
+ iterative deepening: it should fail when no hints are applicable,
+ but go to a deeper depth otherwise. *)
+ let merge_exceptions e e' =
+ match fst e, fst e' with
+ | ReachedLimit, _ -> e
+ | _, ReachedLimit -> e'
+ | _, _ -> e
+
+ (** Determine if backtracking is needed for this goal.
+ If the type class is unique or in Prop
+ and there are no evars in the goal then we do
+ NOT backtrack. *)
+ let needs_backtrack env evd unique concl =
+ if unique || is_Prop env evd concl then
+ occur_existential evd concl
+ else true
+
+ exception NonStuckFailure
+ (* exception Backtrack *)
+
+ let pr_goals s =
+ let open Proofview in
+ if get_debug() > 1 then
+ tclEVARMAP >>= fun sigma ->
+ Unsafe.tclGETGOALS >>= fun gls ->
+ let gls = CList.map Proofview.drop_state gls in
+ let j = List.length gls in
+ let pr_goal gl = pr_ev_with_id sigma gl in
+ Feedback.msg_debug
+ (s ++ int j ++ str" goals:" ++ spc () ++
+ prlist_with_sep Pp.fnl pr_goal gls);
+ tclUNIT ()
+ else
+ tclUNIT ()
+
+ let _ = CErrors.register_handler begin function
+ | NonStuckFailure -> Some (str "NonStuckFailure")
+ | NoApplicableHint -> Some (str "NoApplicableHint")
+ | _ -> None
+ end
+
+ (**
+ For each success of tac1 try tac2.
+ If tac2 raises NonStuckFailure, try the next success of tac1 until depleted.
+ If tac1 finally fails, returns the result of the first tac1 success, if any.
+ *)
+
+ type goal_status =
+ | IsInitial
+ | IsStuckGoal
+ | IsNonStuckFailure
+
+ let pr_goal_status = function
+ | IsInitial -> str "initial"
+ | IsStuckGoal -> str "stuck"
+ | IsNonStuckFailure -> str "stuck failure"
+
+
+ let pr_search_goal sigma (glid, ev, status, _) =
+ str"Goal " ++ int glid ++ str" evar: " ++ Evar.print ev ++ str " status: " ++ pr_goal_status status
+
+ let pr_search_goals sigma =
+ prlist_with_sep fnl (pr_search_goal sigma)
+
+ let search_fixpoint ~best_effort ~allow_out_of_order tacs =
+ let open Pp in
+ let open Proofview in
+ let open Proofview.Notations in
+ let rec fixpoint progress tacs stuck fk =
+ let next (glid, ev, status, tac) tacs stuck =
+ let () = ppdebug 1 (fun () ->
+ str "considering goal " ++ int glid ++
+ str " of status " ++ pr_goal_status status)
+ in
+ let rec kont = function
+ | Fail ((NonStuckFailure | StuckGoal as exn), info) when allow_out_of_order ->
+ let () = ppdebug 1 (fun () ->
+ str "Goal " ++ int glid ++
+ str" is stuck or failed without being stuck, trying other tactics.")
+ in
+ let status =
+ match exn with
+ | NonStuckFailure -> IsNonStuckFailure
+ | StuckGoal -> IsStuckGoal
+ | _ -> assert false
+ in
+ cycle 1 (* Puts the first goal last *) <*>
+ fixpoint progress tacs ((glid, ev, status, tac) :: stuck) fk (* Launches the search on the rest of the goals *)
+ | Fail (e, info) ->
+ let () = ppdebug 1 (fun () ->
+ str "Goal " ++ int glid ++ str" has no more solutions, returning exception: "
+ ++ CErrors.iprint (e, info))
+ in
+ fk (e, info)
+ | Next (res, fk') ->
+ let () = ppdebug 1 (fun () ->
+ str "Goal " ++ int glid ++ str" has a success, continuing resolution")
+ in
+ (* We try to solve the rest of the constraints, and if that fails
+ we backtrack to the next result of tac, etc.... Ultimately if none of the solutions
+ for tac work, we will come back to the failure continuation fk in one of
+ the above cases *)
+ fixpoint true tacs stuck (fun e -> tclCASE (fk' e) >>= kont)
+ in tclCASE tac >>= kont
+ in
+ tclEVARMAP >>= fun sigma ->
+ let () = ppdebug 1 (fun () ->
+ let stuck, failed = List.partition (fun (_, _, status, _) -> status = IsStuckGoal) stuck in
+ str"Calling fixpoint on : " ++
+ int (List.length tacs) ++ str" initial goals" ++
+ str", " ++ int (List.length stuck) ++ str" stuck goals" ++
+ str" and " ++ int (List.length failed) ++ str" non-stuck failures kept" ++
+ str" with " ++ str(if progress then "" else "no ") ++
+ str"progress made in this run." ++ fnl () ++
+ str "Stuck: " ++ pr_search_goals sigma stuck ++ fnl () ++
+ str "Failed: " ++ pr_search_goals sigma failed ++ fnl () ++
+ str "Initial: " ++ pr_search_goals sigma tacs)
+ in
+ tclCHECKINTERRUPT <*>
+ match tacs with
+ | tac :: tacs -> next tac tacs stuck
+ | [] -> (* All remaining goals are stuck *)
+ match stuck with
+ | [] ->
+ (* We found a solution! Great, but in case it's not good for the rest of the proof search,
+ we might have other solutions available through fk. *)
+ tclOR (tclUNIT ()) fk
+ | stuck ->
+ if progress then fixpoint false stuck [] fk
+ else (* No progress can be made on the stuck goals arising from this resolution,
+ try a different solution on the non-stuck goals, if any. *)
+ begin
+ tclORELSE (fk (NoApplicableHint, Exninfo.null))
+ (fun (e, info) ->
+ let () = ppdebug 1 (fun () -> int (List.length stuck) ++ str " remaining goals left, no progress, calling continuation failed")
+ in
+ (* We keep the stuck goals to display to the user *)
+ if best_effort then
+ let stuckgls, failedgls = List.partition (fun (_, _, status, _) ->
+ match status with
+ | IsStuckGoal -> true
+ | IsNonStuckFailure -> false
+ (* There should remain no initial goals at this point *)
+ | IsInitial -> assert false)
+ stuck
+ in
+ pr_goals (str "best_effort is on and remaining goals are: ") <*>
+ (* We shelve the stuck goals but we keep the non-stuck failures in the goal list.
+ This is for compat with Coq 8.12 but might not be the wisest choice in the long run.
+ *)
+ let to_shelve = List.map (fun (glid, ev, _, _) -> ev) stuckgls in
+ let () = ppdebug 1 (fun () ->
+ str "Shelving subgoals: " ++
+ prlist_with_sep spc Evar.print to_shelve)
+ in
+ Unsafe.tclNEWSHELVED to_shelve
+ else tclZERO ~info e)
+ end
+ in
+ pr_goals (str"Launching resolution fixpoint on ") <*>
+ Unsafe.tclGETGOALS >>= fun gls ->
+ (* We wrap all goals with their associated tactic.
+ It might happen that an initial goal is solved during the resolution of another goal,
+ hence the `tclUNIT` in case there is no goal for the tactic to apply anymore. *)
+ let tacs = List.map2_i
+ (fun i gls tac -> (succ i, Proofview.drop_state gls, IsInitial, tclFOCUS ~nosuchgoal:(tclUNIT ()) 1 1 tac))
+ 0 gls tacs
+ in
+ fixpoint false tacs [] (fun (e, info) -> tclZERO ~info e) <*>
+ pr_goals (str "Result goals after fixpoint: ")
+
+
+ (** The general hint application tactic.
+ tac1 + tac2 .... The choice of OR or ORELSE is determined
+ depending on the dependencies of the goal and the unique/Prop
+ status *)
+ let hints_tac_gl hints info kont gl : unit Proofview.tactic =
+ let open Proofview in
+ let open Proofview.Notations in
+ let env = Goal.env gl in
+ let concl = Goal.concl gl in
+ let sigma = Goal.sigma gl in
+ let unique = not info.search_dep || is_unique env sigma concl in
+ let backtrack = needs_backtrack env sigma unique concl in
+ let () = ppdebug 0 (fun () ->
+ pr_depth info.search_depth ++ str": looking for " ++
+ Printer.pr_econstr_env (Goal.env gl) sigma concl ++
+ (if backtrack then str" with backtracking"
+ else str" without backtracking"))
+ in
+ let secvars = compute_secvars gl in
+ match e_possible_resolve hints info.search_hints secvars
+ info.search_only_classes env sigma concl with
+ | None ->
+ Proofview.tclZERO StuckGoal
+ | Some (all_mode_match, poss) ->
+ (* If no goal depends on the solution of this one or the
+ instances are irrelevant/assumed to be unique, then
+ we don't need to backtrack, as long as no evar appears in the goal
+ This is an overapproximation. Evars could appear in this goal only
+ and not any other *)
+ let ortac = if backtrack then Proofview.tclOR else Proofview.tclORELSE in
+ let idx = ref 1 in
+ let foundone = ref false in
+ let rec onetac e (tac, pat, b, name, pp) tl =
+ let derivs = path_derivate info.search_cut name in
+ let pr_error ie =
+ ppdebug 1 (fun () ->
+ let idx = if fst ie == NoApplicableHint then pred !idx else !idx in
+ let header =
+ pr_depth (idx :: info.search_depth) ++ str": " ++
+ Lazy.force pp ++
+ (if !foundone != true then
+ str" on" ++ spc () ++ pr_ev sigma (Proofview.Goal.goal gl)
+ else mt ())
+ in
+ let msg =
+ match fst ie with
+ | ReachedLimit -> str "Proof-search reached its limit."
+ | NoApplicableHint -> str "Proof-search failed."
+ | StuckGoal | NonStuckFailure -> str "Proof-search got stuck."
+ | e -> CErrors.iprint ie
+ in
+ (header ++ str " failed with: " ++ msg))
+ in
+ let tac_of gls i j = Goal.enter begin fun gl' ->
+ let sigma' = Goal.sigma gl' in
+ let () = ppdebug 0 (fun () ->
+ pr_depth (succ j :: i :: info.search_depth) ++ str" : " ++
+ pr_ev sigma' (Proofview.Goal.goal gl'))
+ in
+ let eq c1 c2 = EConstr.eq_constr sigma' c1 c2 in
+ let hints' =
+ if b && not (Context.Named.equal eq (Goal.hyps gl') (Goal.hyps gl))
+ then
+ let st = Hint_db.transparent_state info.search_hints in
+ let modes = Hint_db.modes info.search_hints in
+ make_autogoal_hints info.search_only_classes (modes,st) gl'
+ else info.search_hints
+ in
+ let dep' = info.search_dep || Proofview.unifiable sigma' (Goal.goal gl') gls in
+ let info' =
+ { search_depth = succ j :: i :: info.search_depth;
+ last_tac = pp;
+ search_dep = dep';
+ search_only_classes = info.search_only_classes;
+ search_hints = hints';
+ search_cut = derivs;
+ search_best_effort = info.search_best_effort }
+ in kont info' end
+ in
+ let rec result (shelf, ()) i k =
+ foundone := true;
+ Proofview.Unsafe.tclGETGOALS >>= fun gls ->
+ let gls = CList.map Proofview.drop_state gls in
+ let j = List.length gls in
+ let () = ppdebug 0 (fun () ->
+ pr_depth (i :: info.search_depth) ++ str": " ++ Lazy.force pp
+ ++ str" on" ++ spc () ++ pr_ev sigma (Proofview.Goal.goal gl)
+ ++ str", " ++ int j ++ str" subgoal(s)" ++
+ (Option.cata (fun k -> str " in addition to the first " ++ int k)
+ (mt()) k))
+ in
+ let res =
+ if j = 0 then tclUNIT ()
+ else search_fixpoint ~best_effort:false ~allow_out_of_order:false
+ (List.init j (fun j' -> (tac_of gls i (Option.default 0 k + j'))))
+ in
+ let finish nestedshelf sigma =
+ let filter ev =
+ try
+ let evi = Evd.find_undefined sigma ev in
+ if info.search_only_classes then
+ Some (ev, not (is_class_evar sigma evi))
+ else Some (ev, true)
+ with Not_found -> None
+ in
+ let remaining = CList.map_filter filter shelf in
+ let () = ppdebug 1 (fun () ->
+ let prunsolved (ev, _) = int (Evar.repr ev) ++ spc () ++ pr_ev sigma ev in
+ let unsolved = prlist_with_sep spc prunsolved remaining in
+ pr_depth (i :: info.search_depth) ++
+ str": after " ++ Lazy.force pp ++ str" finished, " ++
+ int (List.length remaining) ++
+ str " goals are shelved and unsolved ( " ++
+ unsolved ++ str")")
+ in
+ begin
+ (* Some existentials produced by the original tactic were not solved
+ in the subgoals, turn them into subgoals now. *)
+ let shelved, goals = List.partition (fun (ev, s) -> s) remaining in
+ let shelved = List.map fst shelved @ nestedshelf and goals = List.map fst goals in
+ let () = if not (List.is_empty shelved && List.is_empty goals) then
+ ppdebug 1 (fun () ->
+ str"Adding shelved subgoals to the search: " ++
+ prlist_with_sep spc (pr_ev sigma) goals ++
+ str" while shelving " ++
+ prlist_with_sep spc (pr_ev sigma) shelved)
+ in
+ shelve_goals shelved <*>
+ if List.is_empty goals then tclUNIT ()
+ else
+ let make_unresolvables = tclEVARMAP >>= fun sigma ->
+ let sigma = make_unresolvables (fun x -> List.mem_f Evar.equal x goals) sigma in
+ Unsafe.tclEVARS sigma
+ in
+ let goals = CList.map Proofview.with_empty_state goals in
+ with_shelf (make_unresolvables <*> Unsafe.tclNEWGOALS goals) >>= fun s ->
+ result s i (Some (Option.default 0 k + j))
+ end
+ in
+ with_shelf res >>= fun (sh, ()) ->
+ tclEVARMAP >>= finish sh
+ in
+ if path_matches derivs [] then aux e tl
+ else
+ ortac
+ (with_shelf tac >>= fun s ->
+ let i = !idx in incr idx; result s i None)
+ (fun e' ->
+ (pr_error e'; aux (merge_exceptions e e') tl))
+ and aux e = function
+ | tac :: tacs -> onetac e tac tacs
+ | [] ->
+ let () = if !foundone == false then
+ ppdebug 0 (fun () ->
+ pr_depth info.search_depth ++ str": no match for " ++
+ Printer.pr_econstr_env (Goal.env gl) sigma concl ++
+ str ", " ++ int (List.length poss) ++
+ str" possibilities")
+ in
+ match e with
+ | (ReachedLimit,ie) -> Proofview.tclZERO ~info:ie ReachedLimit
+ | (StuckGoal,ie) -> Proofview.tclZERO ~info:ie StuckGoal
+ | (NoApplicableHint,ie) ->
+ (* If the constraint abides by the (non-trivial) modes but no
+ solution could be found, we consider it a failed goal, and let
+ proof search proceed on the rest of the
+ constraints, thus giving a more precise error message. *)
+ if all_mode_match &&
+ info.search_best_effort then
+ Proofview.tclZERO ~info:ie NonStuckFailure
+ else Proofview.tclZERO ~info:ie NoApplicableHint
+ | (_,ie) -> Proofview.tclZERO ~info:ie NoApplicableHint
+ in
+ if backtrack then aux (NoApplicableHint,Exninfo.null) poss
+ else tclONCE (aux (NoApplicableHint,Exninfo.null) poss)
+
+ let hints_tac hints info kont : unit Proofview.tactic =
+ Proofview.Goal.enter
+ (fun gl -> hints_tac_gl hints info kont gl)
+
+ let intro_tac info kont gl =
+ let open Proofview in
+ let env = Goal.env gl in
+ let sigma = Goal.sigma gl in
+ let decl = Tacmach.pf_last_hyp gl in
+ let ldb =
+ make_resolve_hyp env sigma (Hint_db.transparent_state info.search_hints)
+ info.search_only_classes decl info.search_hints in
+ let info' =
+ { info with search_hints = ldb; last_tac = lazy (str"intro");
+ search_depth = 1 :: 1 :: info.search_depth }
+ in kont info'
+
+ let intro info kont =
+ Proofview.tclBIND Tactics.intro
+ (fun _ -> Proofview.Goal.enter (fun gl -> intro_tac info kont gl))
+
+ let rec search_tac hints limit depth =
+ let kont info =
+ Proofview.numgoals >>= fun i ->
+ let () = ppdebug 1 (fun () ->
+ str "calling eauto recursively at depth " ++ int (succ depth) ++
+ str " on " ++ int i ++ str " subgoals")
+ in
+ search_tac hints limit (succ depth) info
+ in
+ fun info ->
+ if Int.equal depth (succ limit) then
+ let info = Exninfo.reify () in
+ Proofview.tclZERO ~info ReachedLimit
+ else
+ Proofview.tclOR (hints_tac hints info kont)
+ (fun e -> Proofview.tclOR (intro info kont)
+ (fun e' -> let (e, info) = merge_exceptions e e' in
+ Proofview.tclZERO ~info e))
+
+ let search_tac_gl mst only_classes dep hints best_effort depth i sigma gls gl :
+ unit Proofview.tactic =
+ let open Proofview in
+ let dep = dep || Proofview.unifiable sigma (Goal.goal gl) gls in
+ let info = make_autogoal mst only_classes dep (cut_of_hints hints)
+ best_effort i gl in
+ search_tac hints depth 1 info
+
+ let search_tac mst only_classes best_effort dep hints depth =
+ let open Proofview in
+ let tac sigma gls i =
+ Goal.enter
+ begin fun gl ->
+ search_tac_gl mst only_classes dep hints best_effort depth (succ i) sigma gls gl end
+ in
+ Proofview.Unsafe.tclGETGOALS >>= fun gls ->
+ let gls = CList.map Proofview.drop_state gls in
+ Proofview.tclEVARMAP >>= fun sigma ->
+ let j = List.length gls in
+ search_fixpoint ~best_effort ~allow_out_of_order:true (List.init j (fun i -> tac sigma gls i))
+
+ let fix_iterative t =
+ let rec aux depth =
+ Proofview.tclOR
+ (t depth)
+ (function
+ | (ReachedLimit,_) -> aux (succ depth)
+ | (e,ie) -> Proofview.tclZERO ~info:ie e)
+ in aux 1
+
+ let fix_iterative_limit limit t =
+ let open Proofview in
+ let rec aux depth =
+ if Int.equal depth (succ limit)
+ then
+ let info = Exninfo.reify () in
+ tclZERO ~info ReachedLimit
+ else tclOR (t depth) (function
+ | (ReachedLimit, _) -> aux (succ depth)
+ | (e,ie) -> Proofview.tclZERO ~info:ie e)
+ in aux 1
+
+ let eauto_tac_stuck mst ?(unique=false)
+ ~only_classes
+ ~best_effort
+ ?strategy ~depth ~dep hints =
+ let open Proofview in
+ let tac =
+ let search = search_tac mst only_classes best_effort dep hints in
+ let dfs =
+ match strategy with
+ | None -> not (get_typeclasses_iterative_deepening ())
+ | Some Dfs -> true
+ | Some Bfs -> false
+ in
+ if dfs then
+ let depth = match depth with None -> -1 | Some d -> d in
+ search depth
+ else
+ match depth with
+ | None -> fix_iterative search
+ | Some l -> fix_iterative_limit l search
+ in
+ let error (e, info) =
+ match e with
+ | ReachedLimit ->
+ Tacticals.tclFAIL ~info (str"Proof search reached its limit")
+ | NoApplicableHint ->
+ Tacticals.tclFAIL ~info (str"Proof search failed" ++
+ (if Option.is_empty depth then mt()
+ else str" without reaching its limit"))
+ | Proofview.MoreThanOneSuccess ->
+ Tacticals.tclFAIL ~info (str"Proof search failed: " ++
+ str"more than one success found")
+ | e -> Proofview.tclZERO ~info e
+ in
+ let tac = Proofview.tclOR tac error in
+ let tac =
+ if unique then
+ Proofview.tclEXACTLY_ONCE Proofview.MoreThanOneSuccess tac
+ else tac
+ in
+ with_shelf numgoals >>= fun (initshelf, i) ->
+ let () = ppdebug 1 (fun () ->
+ str"Starting resolution with " ++ int i ++
+ str" goal(s) under focus and " ++
+ int (List.length initshelf) ++ str " shelved goal(s)" ++
+ (if only_classes then str " in only_classes mode" else str " in regular mode") ++
+ match depth with
+ | None -> str ", unbounded"
+ | Some i -> str ", with depth limit " ++ int i)
+ in
+ tac <*> pr_goals (str "after eauto_tac_stuck: ")
+
+ let eauto_tac mst ?unique ~only_classes ~best_effort ?strategy ~depth ~dep hints =
+ Hints.wrap_hint_warning @@
+ (eauto_tac_stuck mst ?unique ~only_classes
+ ~best_effort ?strategy ~depth ~dep hints)
+
+ let run_on_goals env evm p tac goals nongoals =
+ let goalsl =
+ if get_typeclasses_dependency_order () then
+ top_sort evm goals
+ else Evar.Set.elements goals
+ in
+ let goalsl = List.map Proofview.with_empty_state goalsl in
+ let tac = Proofview.Unsafe.tclNEWGOALS goalsl <*> tac in
+ let evm = Evd.set_typeclass_evars evm Evar.Set.empty in
+ let evm = Evd.push_future_goals evm in
+ let _, pv = Proofview.init evm [] in
+ (* Instance may try to call this before a proof is set up!
+ Thus, give_me_the_proof will fail. Beware! *)
+ let name, poly =
+ (* try
+ * let Proof.{ name; poly } = Proof.data Proof_global.(give_me_the_proof ()) in
+ * name, poly
+ * with | Proof_global.NoCurrentProof -> *)
+ Id.of_string "instance", false
+ in
+ let tac =
+ if get_debug () > 1 then Proofview.Trace.record_info_trace tac
+ else tac
+ in
+ let (), pv', unsafe, info =
+ try Proofview.apply ~name ~poly env tac pv
+ with Logic_monad.TacticFailure _ -> raise Not_found
+ in
+ let () =
+ ppdebug 1 (fun () ->
+ str"The tactic trace is: " ++ hov 0 (Proofview.Trace.pr_info env evm ~lvl:1 info))
+ in
+ let finished = Proofview.finished pv' in
+ let evm' = Proofview.return pv' in
+ let _, evm' = Evd.pop_future_goals evm' in
+ let () = ppdebug 1 (fun () ->
+ str"Finished resolution with " ++ str(if finished then "a complete" else "an incomplete") ++
+ str" solution." ++ fnl() ++
+ str"Old typeclass evars not concerned by this resolution = " ++
+ hov 0 (prlist_with_sep spc (pr_ev_with_id evm')
+ (Evar.Set.elements (Evd.get_typeclass_evars evm'))) ++ fnl() ++
+ str"Shelf = " ++
+ hov 0 (prlist_with_sep spc (pr_ev_with_id evm')
+ (Evar.Set.elements (Evd.get_typeclass_evars evm'))))
+ in
+ let nongoals' =
+ Evar.Set.fold (fun ev acc -> match Evarutil.advance evm' ev with
+ | Some ev' -> Evar.Set.add ev acc
+ | None -> acc) (Evar.Set.union goals nongoals) (Evd.get_typeclass_evars evm')
+ in
+ (* FIXME: the need to merge metas seems to come from this being called
+ internally from Unification. It should be handled there instead. *)
+ let evm' = Evd.meta_merge (Evd.meta_list evm) (Evd.clear_metas evm') in
+ let evm' = Evd.set_typeclass_evars evm' nongoals' in
+ let () = ppdebug 1 (fun () ->
+ str"New typeclass evars are: " ++
+ hov 0 (prlist_with_sep spc (pr_ev_with_id evm') (Evar.Set.elements nongoals')))
+ in
+ Some (finished, evm')
+
+ let run_on_evars env evm p tac =
+ match evars_to_goals p evm with
+ | None -> None (* This happens only because there's no evar having p *)
+ | Some (goals, nongoals) ->
+ run_on_goals env evm p tac goals nongoals
+ let evars_eauto env evd depth only_classes ~best_effort unique dep mst hints p =
+ let eauto_tac = eauto_tac_stuck mst ~unique ~only_classes
+ ~best_effort
+ ~depth ~dep:(unique || dep) hints in
+ run_on_evars env evd p eauto_tac
+
+ let typeclasses_eauto env evd ?depth unique ~best_effort st hints p =
+ evars_eauto env evd depth true ~best_effort unique false st hints p
+ (** Typeclasses eauto is an eauto which tries to resolve only
+ goals of typeclass type, and assumes that the initially selected
+ evars in evd are independent of the rest of the evars *)
+
+ let typeclasses_resolve env evd depth unique ~best_effort p =
+ let db = searchtable_map typeclasses_db in
+ let st = Hint_db.transparent_state db in
+ let modes = Hint_db.modes db in
+ typeclasses_eauto env evd ?depth ~best_effort unique (modes,st) [db] p
+end
+
+let typeclasses_eauto ?(only_classes=false)
+ ?(best_effort=false)
+ ?(st=TransparentState.full)
+ ?strategy ~depth dbs =
+ let dbs = List.map_filter
+ (fun db -> try Some (searchtable_map db)
+ with e when CErrors.noncritical e -> None)
+ dbs
+ in
+ let st = match dbs with x :: _ -> Hint_db.transparent_state x | _ -> st in
+ let modes = List.map Hint_db.modes dbs in
+ let modes = List.fold_left (GlobRef.Map.union (fun _ m1 m2 -> Some (m1@m2))) GlobRef.Map.empty modes in
+ let depth = match depth with None -> get_typeclasses_depth () | Some l -> Some l in
+ Proofview.tclIGNORE
+ (Search.eauto_tac (modes,st) ~only_classes ?strategy
+ ~best_effort ~depth ~dep:true dbs)
+ (* Stuck goals can remain here, we could shelve them, but this way
+ the user can use `solve [typeclasses eauto]` to check there are
+ no stuck goals remaining, or use [typeclasses eauto; shelve] himself. *)
+
+(** We compute dependencies via a union-find algorithm.
+ Beware of the imperative effects on the partition structure,
+ it should not be shared, but only used locally. *)
+
+module Intpart = Unionfind.Make(Evar.Set)(Evar.Map)
+
+let deps_of_constraints cstrs evm p =
+ List.iter (fun (_, _, x, y) ->
+ let evx = Evarutil.undefined_evars_of_term evm x in
+ let evy = Evarutil.undefined_evars_of_term evm y in
+ Intpart.union_set (Evar.Set.union evx evy) p)
+ cstrs
+
+let evar_dependencies pred evm p =
+ let cache = Evarutil.create_undefined_evars_cache () in
+ Evd.fold_undefined
+ (fun ev evi _ ->
+ if Evd.is_typeclass_evar evm ev && pred evm ev evi then
+ let evars = Evar.Set.add ev (Evarutil.filtered_undefined_evars_of_evar_info ~cache evm evi)
+ in Intpart.union_set evars p
+ else ())
+ evm ()
+
+(** [split_evars] returns groups of undefined evars according to dependencies *)
+
+let split_evars pred evm =
+ let p = Intpart.create () in
+ evar_dependencies pred evm p;
+ deps_of_constraints (snd (extract_all_conv_pbs evm)) evm p;
+ Intpart.partition p
+
+let is_inference_forced p evd ev =
+ try
+ if Evar.Set.mem ev (Evd.get_typeclass_evars evd) && p ev
+ then
+ let (loc, k) = evar_source (Evd.find_undefined evd ev) in
+ match k with
+ | Evar_kinds.ImplicitArg (_, _, b) -> b
+ | Evar_kinds.QuestionMark _ -> false
+ | _ -> true
+ else true
+ with Not_found -> assert false
+
+let is_mandatory p comp evd =
+ Evar.Set.exists (is_inference_forced p evd) comp
+
+(** Check if an evar is concerned by the current resolution attempt,
+ (and in particular is in the current component).
+ Invariant : this should only be applied to undefined evars. *)
+
+let select_and_update_evars p oevd in_comp evd ev =
+ try
+ if Evd.is_typeclass_evar oevd ev then
+ (in_comp ev && p evd ev (Evd.find_undefined evd ev))
+ else false
+ with Not_found -> false
+
+(** Do we still have unresolved evars that should be resolved ? *)
+
+let has_undefined p oevd evd =
+ let check ev evi = p oevd ev in
+ Evar.Map.exists check (Evd.undefined_map evd)
+let find_undefined p oevd evd =
+ let check ev evi = p oevd ev in
+ Evar.Map.domain (Evar.Map.filter check (Evd.undefined_map evd))
+
+exception Unresolved of evar_map
+
+type solver_type = Environ.env -> evar_map ->
+ metavariable option -> prefix_of_inductive_support_flag ->
+ best_effort:prefix_of_inductive_support_flag ->
+ (evar_map -> Evar.t -> prefix_of_inductive_support_flag) ->
+ (prefix_of_inductive_support_flag * evar_map) option
+
+(** If [do_split] is [true], we try to separate the problem in
+ several components and then solve them separately *)
+let resolve_all_evars depth unique env p oevd fail =
+ let () =
+ ppdebug 0 (fun () ->
+ str"Calling typeclass resolution with flags: "++
+ str"depth = " ++ (match depth with None -> str "∞" | Some d -> int d) ++ str"," ++
+ str"unique = " ++ bool unique ++ str"," ++
+ str"fail = " ++ bool fail);
+ ppdebug 2 (fun () ->
+ str"Initial evar map: " ++
+ Termops.pr_evar_map ~with_univs:!Detyping.print_universes None env oevd)
+ in
+ let split = split_evars p oevd in
+ let split_solver = List.map (Coq_elpi_class_tactics_takeover.handle_takeover Search.typeclasses_resolve env oevd) split in
+ let in_comp comp ev = Evar.Set.mem ev comp in
+ let rec docomp evd = function
+ | [] ->
+ let () = ppdebug 2 (fun () ->
+ str"Final evar map: " ++
+ Termops.pr_evar_map ~with_univs:!Detyping.print_universes None env evd)
+ in
+ evd
+ | ((solver: solver_type), comp) :: comps ->
+ let p = select_and_update_evars p oevd (in_comp comp) in
+ try
+ (try
+ let res = solver env evd depth
+ ~best_effort:true unique p in
+ match res with
+ | Some (finished, evd') ->
+ if has_undefined p oevd evd' then
+ let () = if finished then ppdebug 1 (fun () ->
+ str"Proof is finished but there remain undefined evars: " ++
+ prlist_with_sep spc (pr_ev evd')
+ (Evar.Set.elements (find_undefined p oevd evd')))
+ in
+ raise (Unresolved evd')
+ else docomp evd' comps
+ | None -> docomp evd comps (* No typeclass evars left in this component *)
+ with Not_found ->
+ (* Typeclass resolution failed *)
+ raise (Unresolved evd))
+ with Unresolved evd' ->
+ if fail && is_mandatory (p evd') comp evd'
+ then (* Unable to satisfy the constraints. *)
+ error_unresolvable env evd' comp
+ else (* Best effort: use the best found solution on this component *)
+ docomp evd' comps
+ in docomp oevd split_solver
+
+let initial_select_evars filter =
+ fun evd ev evi ->
+ filter ev (Lazy.from_val (snd (Evd.evar_source evi))) &&
+ (* Typeclass evars can contain evars whose conclusion is not
+ yet determined to be a class or not. *)
+ Typeclasses.is_class_evar evd evi
+
+
+let classes_transparent_state () =
+ try Hint_db.transparent_state (searchtable_map typeclasses_db)
+ with Not_found -> TransparentState.empty
+
+let resolve_typeclass_evars depth unique env evd filter fail =
+ let evd =
+ try Evarconv.solve_unif_constraints_with_heuristics
+ ~flags:(Evarconv.default_flags_of (classes_transparent_state())) env evd
+ with e when CErrors.noncritical e -> evd
+ in
+ resolve_all_evars depth unique env
+ (initial_select_evars filter) evd fail
+
+let solve_inst env evd filter unique fail =
+ let ((), sigma) = Hints.wrap_hint_warning_fun env evd begin fun evd ->
+ (), resolve_typeclass_evars
+ (get_typeclasses_depth ())
+ unique env evd filter fail
+ end in
+ sigma
+
+let () =
+ Typeclasses.set_solve_all_instances solve_inst
+
+let resolve_one_typeclass env ?(sigma=Evd.from_env env) concl unique =
+ let (term, sigma) = Hints.wrap_hint_warning_fun env sigma begin fun sigma ->
+ let hints = searchtable_map typeclasses_db in
+ let st = Hint_db.transparent_state hints in
+ let modes = Hint_db.modes hints in
+ let depth = get_typeclasses_depth () in
+ let tac = Tacticals.tclCOMPLETE (Search.eauto_tac (modes,st)
+ ~only_classes:true ~best_effort:false
+ ~depth [hints] ~dep:true)
+ in
+ let entry, pv = Proofview.init sigma [env, concl] in
+ let pv =
+ let name = Names.Id.of_string "legacy_pe" in
+ match Proofview.apply ~name ~poly:false (Global.env ()) tac pv with
+ | (_, final, _, _) -> final
+ | exception (Logic_monad.TacticFailure (Tacticals.FailError _)) ->
+ raise Not_found
+ in
+ let evd = Proofview.return pv in
+ let term = match Proofview.partial_proof entry pv with [t] -> t | _ -> assert false in
+ term, evd
+ end in
+ (sigma, term)
+
+let () =
+ Typeclasses.set_solve_one_instance
+ (fun x y z w -> resolve_one_typeclass x ~sigma:y z w)
+
+(** Take the head of the arity of a constr.
+ Used in the partial application tactic. *)
+
+let rec head_of_constr sigma t =
+ let t = strip_outer_cast sigma t in
+ match EConstr.kind sigma t with
+ | Prod (_,_,c2) -> head_of_constr sigma c2
+ | LetIn (_,_,_,c2) -> head_of_constr sigma c2
+ | App (f,args) -> head_of_constr sigma f
+ | _ -> t
+
+let head_of_constr h c =
+ Proofview.tclEVARMAP >>= fun sigma ->
+ let c = head_of_constr sigma c in
+ letin_tac None (Name h) c None Locusops.allHyps
+
+let not_evar c =
+ Proofview.tclEVARMAP >>= fun sigma ->
+ match EConstr.kind sigma c with
+ | Evar _ -> Tacticals.tclFAIL (str"Evar")
+ | _ -> Proofview.tclUNIT ()
+
+let is_ground c =
+ let open Tacticals in
+ Proofview.tclEVARMAP >>= fun sigma ->
+ if Evarutil.is_ground_term sigma c then tclIDTAC
+ else tclFAIL (str"Not ground")
+
+let autoapply c i =
+ let open Proofview.Notations in
+ Hints.wrap_hint_warning @@
+ Proofview.Goal.enter begin fun gl ->
+ let hintdb = try Hints.searchtable_map i with Not_found ->
+ CErrors.user_err (Pp.str ("Unknown hint database " ^ i ^ "."))
+ in
+ let flags = auto_unif_flags
+ (Hints.Hint_db.transparent_state hintdb) in
+ let cty = Tacmach.pf_get_type_of gl c in
+ let env = Proofview.Goal.env gl in
+ let sigma = Proofview.Goal.sigma gl in
+ let ce = Clenv.mk_clenv_from env sigma (c,cty) in
+ Clenv.res_pf ~with_evars:true ~with_classes:false ~flags ce <*>
+ Proofview.tclEVARMAP >>= (fun sigma ->
+ let sigma = Typeclasses.make_unresolvables
+ (fun ev -> Typeclasses.all_goals ev (Lazy.from_val (snd (Evd.evar_source (Evd.find_undefined sigma ev))))) sigma in
+ Proofview.Unsafe.tclEVARS sigma) end
diff --git a/apps/tc/src/coq_elpi_class_tactics_takeover.ml b/apps/tc/src/coq_elpi_class_tactics_takeover.ml
new file mode 100644
index 000000000..24462edcf
--- /dev/null
+++ b/apps/tc/src/coq_elpi_class_tactics_takeover.ml
@@ -0,0 +1,176 @@
+(* license: GNU Lesser General Public License Version 2.1 or later *)
+(* ------------------------------------------------------------------------- *)
+
+open Util
+open Names
+open Typeclasses
+
+open Elpi
+
+module Intpart = Unionfind.Make(Evar.Set)(Evar.Map)
+
+open Elpi_plugin
+open Coq_elpi_utils
+
+type override =
+ | AllButFor of Names.GlobRef.Set.t
+ | Only of Names.GlobRef.Set.t
+
+type action =
+ | Set of Coq_elpi_utils.qualified_name * override
+ | Add of GlobRef.t list
+ | Rm of GlobRef.t list
+
+let elpi_solver = Summary.ref ~name:"tc_takeover" None
+
+let takeover action =
+ let open Names.GlobRef in
+ match !elpi_solver, action with
+ | _, Set(solver,mode) ->
+ elpi_solver := Some (mode,solver)
+ | None, (Add _ | Rm _) ->
+ CErrors.user_err Pp.(str "Set the override program first")
+ | Some(AllButFor s,solver), Add grl ->
+ let s' = List.fold_right Set.add grl Set.empty in
+ elpi_solver := Some (AllButFor (Set.diff s s'),solver)
+ | Some(AllButFor s,solver), Rm grl ->
+ let s' = List.fold_right Set.add grl Set.empty in
+ elpi_solver := Some (AllButFor (Set.union s s'),solver)
+ | Some(Only s,solver), Add grl ->
+ let s' = List.fold_right Set.add grl Set.empty in
+ elpi_solver := Some (Only (Set.union s s'),solver)
+ | Some(Only s,solver), Rm grl ->
+ let s' = List.fold_right Set.add grl Set.empty in
+ elpi_solver := Some (Only (Set.diff s s'),solver)
+
+let inTakeover =
+ let cache x = takeover x in
+ Libobject.(declare_object (superglobal_object_nodischarge "TC_HACK_OVERRIDE" ~cache ~subst:None))
+
+let takeover isNone l solver =
+ let open Names.GlobRef in
+ let l = List.map Coq_elpi_utils.locate_simple_qualid l in
+ let s = List.fold_right Set.add l Set.empty in
+ let mode = if isNone then Only Set.empty else if Set.is_empty s then AllButFor s else Only s in
+ Lib.add_leaf (inTakeover (Set(solver,mode)))
+
+let takeover_add l =
+ let l = List.map Coq_elpi_utils.locate_simple_qualid l in
+ Lib.add_leaf (inTakeover (Add l))
+
+let takeover_rm l =
+ let l = List.map Coq_elpi_utils.locate_simple_qualid l in
+ Lib.add_leaf (inTakeover (Rm l))
+
+let path2str = List.fold_left (fun acc e -> Printf.sprintf "%s/%s" acc e) ""
+let debug_covered_gref = CDebug.create ~name:"tc_current_gref" ()
+
+let covered1 env sigma classes i default=
+ let ei = Evd.find_undefined sigma i in
+ let ty = Evd.evar_concl ei in
+ match Typeclasses.class_of_constr env sigma ty with
+ | Some (_,(((cl: typeclass),_),_)) ->
+ let cl_impl = cl.Typeclasses.cl_impl in
+ debug_covered_gref (fun () -> Pp.(str "The current gref is: " ++
+ Printer.pr_global cl_impl ++ str ", with path: " ++ str (path2str (gr2path cl_impl))));
+ Names.GlobRef.Set.mem cl_impl classes
+ | None -> default
+
+let covered env sigma omode s =
+ match omode with
+ | AllButFor blacklist ->
+ Evar.Set.for_all (fun x -> not (covered1 env sigma blacklist x false)) s
+ | Only whitelist ->
+ Evar.Set.for_all (fun x -> covered1 env sigma whitelist x true) s
+
+let debug_handle_takeover = CDebug.create ~name:"handle_takeover" ()
+
+let elpi_fails program_name =
+ let open Pp in
+ let kind = "tactic/command" in
+ let name = show_qualified_name program_name in
+ CErrors.user_err (strbrk (String.concat " " [
+ "The elpi"; kind; name ; "failed without giving a specific error message.";
+ "Please report this inconvenience to the authors of the program."
+ ]))
+let solve_TC program env sigma depth unique ~best_effort filter =
+ let loc = API.Ast.Loc.initial "(unknown)" in
+ let atts = [] in
+ let glss, _ = Evar.Set.partition (filter sigma) (Evd.get_typeclass_evars sigma) in
+ let gls = Evar.Set.elements glss in
+ (* TODO: activate following row to compute new gls
+ this row to make goal sort in msolve *)
+ (* let evar_deps = List.map (fun e ->
+ let evar_info = Evd.find_undefined sigma e in
+ let evar_deps = Evarutil.filtered_undefined_evars_of_evar_info sigma evar_info in
+ e, Evar.Set.elements evar_deps
+ ) gls in *)
+ (* let g = Graph.build_graph evar_deps in *)
+ (* let gls = List.map (fun (e: 'a Graph.node) -> e.name ) (Graph.topo_sort g) in *)
+ let query ~depth state =
+ let state, (loc, q), gls =
+ Coq_elpi_HOAS.goals2query sigma gls loc ~main:(Coq_elpi_HOAS.Solve [])
+ ~in_elpi_tac_arg:Coq_elpi_arg_HOAS.in_elpi_tac ~depth state in
+ let state, qatts = Coq_elpi_vernacular.atts2impl loc ~depth state atts q in
+ let state = API.State.set Coq_elpi_builtins.tactic_mode state true in
+ state, (loc, qatts), gls
+ in
+ let cprogram, _ = Coq_elpi_vernacular.get_and_compile program in
+ match Coq_elpi_vernacular.run ~static_check:false cprogram (`Fun query) with
+ | API.Execute.Success solution ->
+ let sigma, _, _ = Coq_elpi_HOAS.solution2evd sigma solution glss in
+ Some(false,sigma)
+ | API.Execute.NoMoreSteps -> CErrors.user_err Pp.(str "elpi run out of steps")
+ | API.Execute.Failure -> elpi_fails program
+ | exception (Coq_elpi_utils.LtacFail (level, msg)) -> elpi_fails program
+
+let handle_takeover coq_solver env sigma (cl: Intpart.set) =
+ let t = Unix.gettimeofday () in
+ let is_elpi, res =
+ match !elpi_solver with
+ | Some(omode,solver) when covered env sigma omode cl ->
+ true, solve_TC solver
+ | _ -> false, coq_solver in
+ let is_elpi_text = if is_elpi then "Elpi" else "Coq" in
+ debug_handle_takeover (fun () ->
+ let len = (Evar.Set.cardinal cl) in Pp.str @@
+ Printf.sprintf "handle_takeover for %s - Class : %d - Time : %f"
+ is_elpi_text len (Unix.gettimeofday () -. t));
+ res, cl
+
+let assert_same_generated_TC = Goptions.declare_bool_option_and_ref
+ ~depr:(Deprecation.make ()) ~key:["assert_same_generated_TC"] ~value:false
+
+(* let same_solution evd1 evd2 i =
+ let print_discrepancy a b =
+ CErrors.anomaly Pp.(str
+ "Discrepancy in same solution: \n" ++
+ str"Expected : " ++ a ++ str"\n" ++
+ str"Found : " ++ b)
+ in
+ let get_types evd t = EConstr.to_constr ~abort_on_undefined_evars:false evd t in
+ try (
+ let t1 = Evd.find evd1 i in
+ let t2 = Evd.find evd2 i |> Evd.evar_body in
+ match t1, t2 with
+ | Evd.Evar_defined t1, Evd.Evar_defined t2 ->
+ let t1, t2 = get_types evd1 t1, get_types evd2 t2 in
+ let b = try Constr.eq_constr_nounivs t1 t2 with Not_found ->
+ CErrors.anomaly Pp.(str "Discrepancy in same solution: problem with universes") in
+ if (not b) then
+ print_discrepancy (Printer.pr_constr_env (Global.env ()) evd1 t1) (Printer.pr_constr_env (Global.env ()) evd2 t2)
+ else
+ b
+ | Evd.Evar_empty, Evd.Evar_empty -> true
+ | Evd.Evar_defined t1, Evar_empty ->
+ let t1 = get_types evd1 t1 in
+ print_discrepancy (Printer.pr_constr_env (Global.env ()) evd1 t1) (Pp.str "Nothing")
+ | Evd.Evar_empty, Evd.Evar_defined t2 ->
+ let t2 = get_types evd2 t2 in
+ print_discrepancy (Pp.str "Nothing") (Printer.pr_constr_env (Global.env ()) evd2 t2)
+ ) with Not_found ->
+ CErrors.anomaly Pp.(str "Discrepancy in same solution: Not found All") *)
+
+
+(* let same_solution comp evd1 evd2 =
+ Evar.Set.for_all (same_solution evd1 evd2) comp *)
diff --git a/apps/tc/src/coq_elpi_tc_hook.mlg b/apps/tc/src/coq_elpi_tc_hook.mlg
new file mode 100644
index 000000000..7003bdf38
--- /dev/null
+++ b/apps/tc/src/coq_elpi_tc_hook.mlg
@@ -0,0 +1,46 @@
+(* license: GNU Lesser General Public License Version 2.1 or later *)
+(* ------------------------------------------------------------------------- *)
+
+DECLARE PLUGIN "coq-elpi-tc.plugin"
+
+{
+open Stdarg
+open Elpi_plugin
+open Coq_elpi_arg_syntax
+open Coq_elpi_tc_register
+open Coq_elpi_class_tactics_takeover
+}
+
+VERNAC COMMAND EXTEND ElpiTypeclasses CLASSIFIED AS SIDEFF
+
+| #[ atts = any_attribute ] [ "Elpi" "Register" "TC" "Compiler" qualified_name(p) ] -> {
+ let () = ignore_unknown_attributes atts in
+ register_observer (fst p, snd p, atts) }
+
+| #[ atts = any_attribute ] [ "Elpi" "TC" "Activate" "Observer" qualified_name(p) ] -> {
+ let () = ignore_unknown_attributes atts in
+ activate_observer (snd p) }
+
+| #[ atts = any_attribute ] [ "Elpi" "TC" "Deactivate" "Observer" qualified_name(p) ] -> {
+ let () = ignore_unknown_attributes atts in
+ deactivate_observer (snd p) }
+
+| #[ atts = any_attribute ] [ "Elpi" "Override" "TC" qualified_name(p) "All" ] -> {
+ let () = ignore_unknown_attributes atts in
+ takeover false [] (snd p) }
+| #[ atts = any_attribute ] [ "Elpi" "Override" "TC" qualified_name(p) "None" ] -> {
+ let () = ignore_unknown_attributes atts in
+ takeover true [] (snd p) }
+
+
+| #[ atts = any_attribute ] [ "Elpi" "Override" "TC" qualified_name(p) "Only" ne_reference_list(cs) ] -> {
+ let () = ignore_unknown_attributes atts in
+ takeover false cs (snd p) }
+| #[ atts = any_attribute ] [ "Elpi" "Override" "TC" "+" reference_list(cs) ] -> {
+ let () = ignore_unknown_attributes atts in
+ takeover_add cs }
+| #[ atts = any_attribute ] [ "Elpi" "Override" "TC" "-" reference_list(cs) ] -> {
+ let () = ignore_unknown_attributes atts in
+ takeover_rm cs }
+
+END
\ No newline at end of file
diff --git a/apps/tc/src/coq_elpi_tc_register.ml b/apps/tc/src/coq_elpi_tc_register.ml
new file mode 100644
index 000000000..702d58a2f
--- /dev/null
+++ b/apps/tc/src/coq_elpi_tc_register.ml
@@ -0,0 +1,105 @@
+(* license: GNU Lesser General Public License Version 2.1 or later *)
+(* ------------------------------------------------------------------------- *)
+
+open Elpi_plugin
+open Classes
+open Coq_elpi_arg_HOAS
+open Names
+
+type qualified_name = Coq_elpi_utils.qualified_name
+
+type loc_name_atts = (Loc.t * qualified_name * Attributes.vernac_flags)
+
+(* Hack to convert a Coq GlobRef into an elpi string *)
+let gref2elpi_term (gref: GlobRef.t) : Cmd.raw =
+ let gref_2_string gref = Pp.string_of_ppcmds (Printer.pr_global gref) in
+ Cmd.String (gref_2_string gref)
+ (* TODO: maybe returning an elpi term is cleaner, but this creates a loop in
+ stdppInj test *)
+ (* Cmd.Term (CAst.make @@ Constrexpr.CRef(
+ Libnames.qualid_of_string @@ gref_2_string gref,None)) *)
+
+(* Returns the elpi term representing the type class received in argument *)
+let observer_class (x : Typeclasses.typeclass) : Coq_elpi_arg_HOAS.Cmd.raw list =
+ [gref2elpi_term x.cl_impl]
+
+(**
+ Returns the list of Cmd.raw arguments to be passed to the elpi program in charge
+ to compile instances. The arguments are [Inst, TC, Locality, Prio] where:
+ - Inst : is the elpi Term for the current instance
+ - TC : is the elpi Term for the type class implemented by Inst
+ - Locality : is the elpi String [Local|Global|Export] for the locality of Inst
+ - Prio : is the elpi Int N representing the priority of the instance. N is:
+ | -1 if the instance has no user-defined priority
+ | N if the instance has the user-defined priority N
+*)
+let observer_instance ({locality; instance; info; class_name} : instance) : Coq_elpi_arg_HOAS.Cmd.raw list =
+ let locality2elpi_string loc =
+ let hint2string = function
+ | Hints.Local -> "Local"
+ | Export -> "Export"
+ | SuperGlobal -> "Global" in
+ Cmd.String (hint2string loc) in
+ let prio2elpi_int (prio: Typeclasses.hint_info) =
+ Cmd.Int (Option.default (-1) prio.hint_priority) in
+ [
+ gref2elpi_term instance;
+ gref2elpi_term class_name;
+ locality2elpi_string locality;
+ prio2elpi_int info
+ ]
+
+let inObservation =
+ Libobject.declare_object @@
+ Libobject.local_object "TC_HACK_OBSERVER2"
+ ~cache:(fun (run,inst) -> run @@ observer_instance inst)
+ ~discharge:(fun (_,inst as x) -> if inst.locality = Local then None else Some x)
+
+let observer_evt ((loc, name, atts) : loc_name_atts) (x : Event.t) =
+ let open Coq_elpi_vernacular in
+ let run_program e = run_program loc name ~atts e in
+ match x with
+ | Event.NewClass cl -> run_program @@ observer_class cl
+ | Event.NewInstance inst -> Lib.add_leaf (inObservation (run_program,inst))
+
+module StringMap = Map.Make(String)
+
+type observers = observer StringMap.t
+
+let observers : observers ref = Summary.ref StringMap.empty ~name:"tc_observers"
+
+let build_observer_name (observer : qualified_name) =
+ String.concat "." observer
+
+type action =
+ | Create of string * loc_name_atts
+ | Activate of qualified_name
+ | Deactivate of qualified_name
+
+let action_manager = function
+ | Create (name, loc_name_atts) ->
+ let observer = Classes.register_observer ~name (observer_evt loc_name_atts) in
+ observers := StringMap.add name observer !observers;
+ Classes.activate_observer observer
+ | Activate observer ->
+ Classes.activate_observer (StringMap.find (build_observer_name observer) !observers)
+ | Deactivate observer ->
+ Classes.deactivate_observer (StringMap.find (build_observer_name observer) !observers)
+
+
+(* Take an action and execute it with the action manager *)
+let inTakeover =
+ let cache = action_manager
+ in Libobject.(declare_object
+ (superglobal_object_nodischarge "TC_HACK_OBSERVER" ~cache ~subst:None))
+
+(* Adds a new observer in coq and activate it *)
+let register_observer ((_, name, _) as lna : loc_name_atts) =
+ let obs_name = build_observer_name name in
+ Lib.add_leaf (inTakeover (Create (obs_name, lna)))
+
+let activate_observer (observer : qualified_name) =
+ Lib.add_leaf (inTakeover (Activate observer))
+
+let deactivate_observer (observer : qualified_name) =
+ Lib.add_leaf (inTakeover (Deactivate observer))
\ No newline at end of file
diff --git a/apps/tc/src/elpi_tc_plugin.mlpack b/apps/tc/src/elpi_tc_plugin.mlpack
new file mode 100644
index 000000000..b9c0c8e3b
--- /dev/null
+++ b/apps/tc/src/elpi_tc_plugin.mlpack
@@ -0,0 +1,4 @@
+Coq_elpi_tc_register
+Coq_elpi_class_tactics_takeover
+Coq_elpi_class_tactics_hacked
+Coq_elpi_tc_hook
\ No newline at end of file
diff --git a/apps/tc/tests/WIP/add_alias.v b/apps/tc/tests/WIP/add_alias.v
new file mode 100644
index 000000000..be23a47df
--- /dev/null
+++ b/apps/tc/tests/WIP/add_alias.v
@@ -0,0 +1,30 @@
+From elpi.apps Require Import tc.
+Elpi Override TC TC.Solver All.
+Elpi Debug "use-alias".
+
+Class foo (A : Type) := f : Type.
+
+Global Instance fooNat : foo nat := {f := nat}.
+Global Instance fooBool : foo bool := {f := bool}.
+
+Elpi AddClasses foo.
+Elpi AddInstances foo.
+
+Definition nat' := nat.
+
+
+Goal foo nat. apply _. Qed.
+Goal foo bool. apply _. Qed.
+Goal foo nat'. Fail apply _. Abort.
+
+Module A.
+ Elpi Accumulate TC.Solver lp:{{
+ alias {{nat'}} {{nat}}.
+ }}.
+ Goal foo nat'. apply _. Qed.
+End A.
+
+Definition nat'' := nat'.
+
+Elpi AddAlias (nat'') (nat').
+Goal foo nat''. apply _. Qed.
diff --git a/apps/tc/tests/WIP/cyclicTC_jarl.v b/apps/tc/tests/WIP/cyclicTC_jarl.v
new file mode 100644
index 000000000..75154e2fc
--- /dev/null
+++ b/apps/tc/tests/WIP/cyclicTC_jarl.v
@@ -0,0 +1,69 @@
+From elpi.apps Require Import tc.
+Elpi Debug "simple-compiler".
+Set TC NameShortPath.
+
+Elpi Override TC TC.Solver All.
+
+Class A (T1 : Type).
+Class B (T1 : Type).
+
+Global Instance instA' (T1 : Type) (T2 : Type) : A bool. Qed.
+Global Instance instA (T1 : Type) `(B T1) : A T1. Qed.
+
+Global Instance instB (T1 : Type) `(A T1) : B T1. Qed.
+Global Instance instB' : B nat . Qed.
+
+Elpi Accumulate tc.db lp:{{
+ pred explored_gref o:gref.
+
+ pred should_fail i:list gref, i:gref, i:gref.
+ should_fail [] _ _.
+ should_fail [Current | Tl] Current BlackElt :- !,
+ if (std.mem Tl BlackElt) fail true.
+ should_fail [_ | Tl] Current BlackElt :- !,
+ should_fail Tl Current BlackElt.
+
+ pred already_explored i:gref, i:gref.
+ already_explored Current BlackElt :-
+ std.findall (explored_gref _) As,
+ std.map As (x\r\ x = explored_gref r) As',
+ should_fail As' Current BlackElt.
+
+ pred get_other i:gref, o:gref.
+
+ pred under_extra i:gref, i:list prop, o:list prop.
+ under_extra A B C :- std.map B (x\r\ (explored_gref A => x) = r) C1,
+ C = [sigma x\ get_other A x, already_explored A x | C1].
+
+ :after "firstHook"
+ make-tc IsHead Ty Inst Hyp Clause :- !,
+ app [global TC | TL] = Ty,
+ gref->pred-name TC TC_Str,
+ std.append TL [Inst] Args,
+ coq.elpi.predicate TC_Str Args Q,
+ if (not IsHead) (Hyp = Hyp') (under_extra TC Hyp Hyp'),
+ Clause = (Q :- Hyp').
+}}.
+Elpi Typecheck TC.Solver.
+
+Elpi AddAllClasses.
+Elpi AddAllInstances.
+
+Elpi Command AddRecursivelyDependantTC.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate lp:{{
+ main [trm (global A), trm (global B)] :-
+ coq.elpi.accumulate _ "tc.db"
+ (clause _ _ (get_other A B)),
+ coq.elpi.accumulate _ "tc.db"
+ (clause _ _ (get_other B A)).
+ main L :- coq.say L.
+}}.
+Elpi Typecheck.
+
+Elpi AddRecursivelyDependantTC (A) (B).
+
+Elpi Bound Steps 10000.
+Check (_ : B bool).
+Check (_ : A nat).
+
diff --git a/apps/tc/tests/WIP/eqSimpl.v b/apps/tc/tests/WIP/eqSimpl.v
new file mode 100644
index 000000000..4a29cc63f
--- /dev/null
+++ b/apps/tc/tests/WIP/eqSimpl.v
@@ -0,0 +1,26 @@
+(*
+ TODO: modes don't work, since, when compiled, instance does
+ not already know them
+*)
+Require Import Bool.
+From elpi.apps Require Import tc.
+
+Elpi Debug "simple-compiler".
+Set TC AddModes.
+
+Class MyEqb A : Type := eqb : A -> A -> bool.
+Global Hint Mode MyEqb + : typeclass_instances.
+
+Notation " x == y " := (eqb x y) (no associativity, at level 70).
+
+Global Instance eqU : MyEqb unit := { eqb x y := true }.
+Global Instance eqB : MyEqb bool := { eqb x y := if x then y else negb y }.
+Global Instance eqP {A B} `{MyEqb A} `{MyEqb B} : MyEqb (A * B) := {
+ eqb x y := (fst x == fst y) && (snd x == snd y) }.
+
+Fail Check (fun n m : _ => eqb n m).
+
+Goal (tt, (tt, true)) == (tt, (tt, true)) = true.
+ easy.
+Qed.
+
diff --git a/apps/tc/tests/WIP/included_proof.v b/apps/tc/tests/WIP/included_proof.v
new file mode 100644
index 000000000..ab7f9c79d
--- /dev/null
+++ b/apps/tc/tests/WIP/included_proof.v
@@ -0,0 +1,30 @@
+From elpi.apps Require Import tc.
+
+Class EqDec (A : Type) :=
+ { eqb : A -> A -> bool ;
+ eqb_leibniz : forall x y, eqb x y = true -> x = y }.
+
+Generalizable Variables A.
+
+Class Ord `(E : EqDec A) := { le : A -> A -> bool }.
+
+Class C (A : Set).
+
+Elpi Override TC TC.Solver All.
+Global Instance cInst `{e: EqDec nat} : Ord e -> C nat. Admitted.
+
+(*
+ We want to be sure that cInst when compiled has only one hypothesis: (Ord e).
+ We don't want the hypothesis {e : EqDec nat} since it will be verified by (Ord e)
+*)
+(* TODO: it should not fail *)
+Fail Elpi Query TC.Solver lp:{{
+ compile {{:gref cInst}} _ _ CL,
+ CL = (pi a\ pi b\ (_ :- (Hyp a b))),
+ coq.say Hyp,
+ pi a b\
+ expected-found (do _) (Hyp a b).
+}}.
+
+
+
diff --git a/apps/tc/tests/WIP/premisesSort/sort1.v b/apps/tc/tests/WIP/premisesSort/sort1.v
new file mode 100644
index 000000000..78ed6b833
--- /dev/null
+++ b/apps/tc/tests/WIP/premisesSort/sort1.v
@@ -0,0 +1,18 @@
+From elpi.apps.tc.tests.premisesSort Require Import sortCode.
+
+Set Warnings "+elpi".
+Class A (S : Type).
+Class B (S : Type).
+Class C (S : Type).
+
+Global Instance A1 : A nat. Admitted.
+Global Instance A2 : A bool. Admitted.
+
+Global Instance B1 : B nat. Admitted.
+
+Global Instance C1 {T : Type} `{A T, B T} : C bool. Admitted.
+
+(* Simpl example where we do backtrack *)
+Goal C bool.
+ apply _.
+Qed.
\ No newline at end of file
diff --git a/apps/tc/tests/WIP/premisesSort/sort2.v b/apps/tc/tests/WIP/premisesSort/sort2.v
new file mode 100644
index 000000000..9ecd88d2a
--- /dev/null
+++ b/apps/tc/tests/WIP/premisesSort/sort2.v
@@ -0,0 +1,32 @@
+From elpi.apps.tc.tests.premisesSort Require Import sortCode.
+Elpi Debug "simple-compiler".
+Set TC AddModes.
+
+Class A (S : Type).
+Class B (S : Type).
+Class C (S : Type).
+
+Global Hint Mode A + : typeclass_instances.
+
+Global Instance A1 : A nat. Admitted.
+
+Global Instance B1 : B nat. Admitted.
+
+(*
+ Here since the output of T is input in A, we want to reorder
+ the goals such that the proof of be is computed first.
+ Question do we want to raise an error or try to rearrange
+ subgoals in C1. We can try to make an analysis in the compiling
+ phase to raise the error.
+*)
+Global Instance C1 {T : Type} `{e : A T, B T} : C bool. Admitted.
+
+Elpi AddAllClasses.
+Elpi AddAllInstances.
+
+Elpi Override TC TC.Solver All.
+
+Elpi Print TC.Solver.
+Goal C bool.
+ apply _.
+Qed.
\ No newline at end of file
diff --git a/apps/tc/tests/WIP/premisesSort/sort3.v b/apps/tc/tests/WIP/premisesSort/sort3.v
new file mode 100644
index 000000000..878ba06dd
--- /dev/null
+++ b/apps/tc/tests/WIP/premisesSort/sort3.v
@@ -0,0 +1,28 @@
+From elpi.apps.tc.tests.premisesSort Require Import sortCode.
+Elpi Debug "simple-compiler".
+
+Class A (S : Type) (T : Type).
+Class B (S : Type) (T : Type).
+Class C (S : Type).
+
+Global Hint Mode A + - : typeclass_instances.
+Global Hint Mode B + - : typeclass_instances.
+Elpi AddAllClasses.
+
+Global Instance A1 : A nat nat. Admitted.
+Global Instance B1 : B nat nat. Admitted.
+
+Global Instance C1 {S T : Type} `{B S T, A T S} : C T. Admitted.
+
+Elpi AddAllInstances.
+Elpi Override TC TC.Solver All.
+Goal C nat.
+ apply _.
+Qed.
+
+(* Following has a cyclic dependecy, therefore error *)
+(* Global Instance C2 {S T : Type} `{A T S, B S T} : C bool. Admitted. *)
+(* Elpi AddInstances C2. *)
+
+(* Global Instance C3 {S T : Type} `{B T S} : C S. Admitted. *)
+(* Elpi AddInstances C3. *)
\ No newline at end of file
diff --git a/apps/tc/tests/WIP/premisesSort/sort4.v b/apps/tc/tests/WIP/premisesSort/sort4.v
new file mode 100644
index 000000000..0309430c2
--- /dev/null
+++ b/apps/tc/tests/WIP/premisesSort/sort4.v
@@ -0,0 +1,59 @@
+From elpi.apps.tc.tests.premisesSort Require Import sortCode.
+Elpi Debug "simple-compiler".
+Set TC AddModes.
+
+Class A (S : Type) (T : Type).
+Class C (S : Type) (T : Type).
+Class B (S : Type) (T : Type) `(A S T, C S T) := f : forall (x : S), x = x.
+
+Global Hint Mode A + + : typeclass_instances.
+Global Hint Mode C + + : typeclass_instances.
+
+Global Instance A1 : A nat nat. Admitted.
+Global Instance C1 : C nat nat. Admitted.
+Global Instance B1 (S : Type) (T : Type) (a : A S T) (c : C S T) : B S T a c. Admitted.
+
+Elpi AddAllClasses.
+Elpi AddAllInstances.
+Elpi Override TC TC.Solver All.
+
+Elpi Accumulate tc.db lp:{{
+ pred get-inout-sealed-goal i:argument_mode, i:sealed-goal, o:list term.
+ get-inout-sealed-goal AMode (seal (goal _ _ (app [global GR | L]) Sol _)) Res :-
+ tc-mode GR Modes, std.append L [Sol] L',
+ std.map2-filter L' Modes (t\m\r\ pr AMode _ = m, var t, r = t) Res.
+ get-inout-sealed-goal out (seal (goal _ _ _ Sol _)) [Sol].
+ get-inout-sealed-goal _ _ [].
+
+ pred sort-goals i:list sealed-goal, o:list int.
+ sort-goals L NL :-
+ std.map-i L (i\x\r\ r = pr x i) LookupList,
+ std.map L (x\r\ sigma M\ get-inout-sealed-goal in x M, r = pr x M) InputModes,
+ std.map L (x\r\ sigma Output Deps\
+ get-inout-sealed-goal out x Output,
+ std.map-filter InputModes (x\r\
+ sigma Fst Snd\ pr Fst Snd = x,
+ std.exists Output (v\ std.exists Snd (v1\ occurs_var v v1)), r = Fst) Deps, % O(N^2)
+ sigma Output2Nb Deps2Nb\
+ std.lookup! LookupList x Output2Nb,
+ std.map Deps (std.lookup! LookupList) Deps2Nb,
+ r = pr Output2Nb Deps2Nb) Graph,
+ coq.toposort Graph NL.
+
+ pred sort-sealed-goals i:list sealed-goal, o:list sealed-goal.
+ sort-sealed-goals SGL SortedSGL :-
+ sort-goals SGL SGLIndexes,
+ std.map SGLIndexes (x\r\ std.nth x SGL r) SortedSGL.
+
+ :after "firstHook" msolve L N :- !,
+ sort-sealed-goals L LSort,
+ coq.say LSort,
+ coq.ltac.all (coq.ltac.open solve) LSort N.
+
+ :after "firstHook" msolve A _ :- coq.say A, sep, fail.
+}}.
+Elpi Typecheck TC.Solver.
+
+Goal 3 = 3.
+ Fail apply f.
+Abort.
\ No newline at end of file
diff --git a/apps/tc/tests/WIP/premisesSort/sortCode.v b/apps/tc/tests/WIP/premisesSort/sortCode.v
new file mode 100644
index 000000000..720d4ce0c
--- /dev/null
+++ b/apps/tc/tests/WIP/premisesSort/sortCode.v
@@ -0,0 +1,90 @@
+
+From elpi Require Import tc.
+
+Elpi Accumulate tc.db lp:{{
+ pred get-pattern-fragment i:term, o:list term.
+
+ pred get-inout i:argument_mode, i:term, o:list term.
+ % TODO: the second arg may not be an (app L)
+ get-inout AMode (app [global GR | L]) Res :-
+ std.drop-last 1 {tc-mode GR} Modes,
+ std.map2-filter L Modes (t\m\r\ pr AMode _ = m, r = t) Res.
+ get-inout _ _ [].
+
+ pred input-must-have-predecessor i:term, i:term, i:list term, i:list term.
+ input-must-have-predecessor _ _ [] _ :- !.
+ input-must-have-predecessor Instance Premise [Mode | Modes] Premises :-
+ std.exists Premises (p\ sigma MOut\
+ get-inout out p MOut, std.mem MOut Mode),
+ input-must-have-predecessor Instance Premise Modes Premises.
+ input-must-have-predecessor Instance Premise [Mode | _] _ :-
+ coq.error "Input mode" Mode "of"
+ Premise "cannot be inferred from the other premises of the instance"
+ Instance.
+
+
+ % CurrentType is the type of the current instance to get its input variables,
+ % These variables should not create edges in the graph
+ pred sort-hypothesis i:term, i:term, i:list term, o:list int.
+ sort-hypothesis Instance (app [_ | InputCurrentType]) L NL :-
+ std.map-i L (i\x\r\ r = pr x i) LookupList,
+ std.map L (premise\r\ sigma M M'\ get-inout in premise M,
+ std.filter M (x\ not (std.mem InputCurrentType x)) M',
+ input-must-have-predecessor Instance premise M' L,
+ r = pr premise M') InputModes,
+ % foreach goal, we associate those goals having a dependency on it,
+ % in particular a goal G2 depends on G1 if a variable V is in
+ % output mode for G1 and in input mode for G2 (the dependency graph will
+ % and edge going from G1 to G2 : G1 -> G2)
+ std.map L (x\r\ sigma Output Deps\ % O(N^3 * check of occurs)
+ % the list of variable in input of the current goal G
+ get-inout out x Output,
+ % for each output modes of all goals, we keep those having an output
+ % which exists in the input variable of G
+ std.map-filter InputModes (x\r\
+ sigma Fst Snd\ pr Fst Snd = x,
+ std.exists Output (v\ std.exists Snd (v1\ occurs v v1)), r = Fst) Deps, % O(N^2)
+ sigma Output2Nb Deps2Nb\
+ std.lookup! LookupList x Output2Nb,
+ std.map Deps (std.lookup! LookupList) Deps2Nb,
+ r = pr Output2Nb Deps2Nb) Graph,
+ coq.toposort Graph NL.
+
+ pred sort-and-compile-premises i:term, i:term, i:list term, i:list term, i:prop, o:list prop.
+ sort-and-compile-premises Instance CurrentType Types Vars IsPositive Premises :-
+ sort-hypothesis Instance CurrentType Types TypesSortedIndexes, % O (n^3)
+ % std.map-i Types (i\e\r\ r = i) TypesSortedIndexes,
+ std.map TypesSortedIndexes (x\r\ std.nth x Vars r) SortedVars, % O (n^2)
+ std.map TypesSortedIndexes (x\r\ std.nth x Types r) SortedTypes, % O (n^2)
+ std.map2-filter SortedTypes SortedVars (t\v\r\
+ compile-aux1 t v [] [] [] (not IsPositive) false r _) Premises.
+
+ pred compile-aux1 i:term, i:term, i:list term, i:list univ, i:list term, i:prop, i:prop, o:prop, o:bool.
+ :name "compiler-aux:start"
+ compile-aux1 Ty I [] [X | XS] [] IsPositive IsHead (pi x\ C x) IsLeaf :- !,
+ pi x\ copy (sort (typ X)) (sort (typ x)) => copy Ty (Ty1 x),
+ compile-aux1 (Ty1 x) I [] XS [] IsPositive IsHead (C x) IsLeaf.
+ compile-aux1 (prod N T F) I ListVar [] Types IsPositive IsHead Clause ff :- !,
+ (if IsPositive (Clause = pi x\ C x) (Clause = (pi x\ decl x N T => C x))),
+ pi p\ sigma Type\
+ if (app-has-class T, not (occurs p (F p))) (Type = T) (Type = app []),
+ compile-aux1 (F p) I [p | ListVar] [] [Type | Types] IsPositive IsHead (C p) _.
+ :if "simple-compiler"
+ % TODO: here we don't do pattern fragment unification
+ compile-aux1 Ty I ListVar [] Types IsPositive IsHead Clause tt :- !,
+ sort-and-compile-premises I Ty Types ListVar IsPositive Premises,
+ coq.mk-app I {std.rev ListVar} AppInst,
+ make-tc IsHead Ty AppInst Premises Clause.
+ compile-aux1 Ty I ListVar [] Types IsPositive IsHead Clause tt :- !,
+ sort-and-compile-premises I Ty Types ListVar IsPositive Premises,
+ coq.mk-app I {std.rev ListVar} AppInst,
+ std.append {get-pattern-fragment Ty} {get-pattern-fragment AppInst} Term-to-be-fixed,
+ std.fold Term-to-be-fixed 0 (e\acc\r\ sigma L X\ e = app X, std.length X L, r is acc + L - 1) Len,
+ if (IsPositive) (IsPositiveBool = tt) (IsPositiveBool = ff),
+ remove-ho-unification IsHead IsPositiveBool Len Ty AppInst Premises Term-to-be-fixed [] [] [] [] [] Clause.
+
+ :after "firstHook"
+ compile-aux Ty Inst _Premises _VarAcc UnivL IsPositive IsHead Clause NoPremises :- !,
+ compile-aux1 Ty Inst [] UnivL [] (IsPositive = tt, true; false) IsHead Clause NoPremises.
+}}.
+Elpi Typecheck TC.Solver.
\ No newline at end of file
diff --git a/apps/tc/tests/auto_compile.v b/apps/tc/tests/auto_compile.v
new file mode 100644
index 000000000..aa3c44c5c
--- /dev/null
+++ b/apps/tc/tests/auto_compile.v
@@ -0,0 +1,73 @@
+From elpi.apps Require Import tc.
+
+Elpi Override TC TC.Solver All.
+
+Require Import Bool.
+
+(* TODO: How to add the #[deterministic] pragma in front of the class? *)
+(* #[deterministic] Class A (T : Type) := {succ : T -> T}. *)
+Class A (T : Type) := {succ : T -> T}.
+#[local] Instance B : A nat := {succ n := S n}.
+Instance C : A bool := {succ b := negb b}.
+Instance Prod (X Y: Type) `(A X, A Y) : A (X * Y) :=
+ {succ b := match b with (a, b) => (succ a, succ b) end}.
+
+Elpi Accumulate TC.Solver lp:{{
+ :after "firstHook"
+ solve _ _ :- coq.say "Solving in ELPI!", fail.
+}}.
+
+Goal A (nat * (nat * bool)). apply _. Qed.
+
+Module M.
+ Class B (T : nat).
+ Section A.
+ Instance X : B 1. Qed.
+ Goal B 1. apply _. Qed.
+
+ Global Instance Y : B 2. Qed.
+ Goal B 2. apply _. Qed.
+ End A.
+ Goal B 1. Proof. Fail apply _. Abort.
+ Goal B 2. Proof. apply _. Qed.
+
+ Section B.
+ Variable V : nat.
+ Global Instance Z : `(B 0) -> B V. Qed.
+ Global Instance W : B 0. Qed.
+ End B.
+
+ Goal B 0. apply _. Qed.
+ Goal B 10. apply _. Qed.
+End M.
+
+Goal M.B 1. apply M.X. Qed.
+Goal M.B 0. apply _. Qed.
+Goal M.B 10. apply _. Qed.
+
+Elpi Query TC.Solver lp:{{
+ % Small test for instance order
+ sigma I L\
+ std.findall (instance _ _ _) I,
+ std.map-filter I (x\y\ x = instance _ y {{:gref M.B}})
+ [{{:gref M.W}}, {{:gref M.Y}}, {{:gref M.Z}}].
+}}.
+
+Module S.
+ Class Cl (i: nat).
+ #[local] Instance Cl1 : Cl 1. Qed.
+ #[global] Instance Cl2 : Cl 2. Qed.
+ #[export] Instance Cl3 : Cl 3. Qed.
+End S.
+
+Elpi Override TC TC.Solver None.
+Goal S.Cl 1 /\ S.Cl 2 /\ S.Cl 3.
+Proof.
+ split. all:cycle 1.
+ split.
+ apply _.
+ Fail apply _.
+ Import S.
+ apply _.
+ Fail apply _.
+Abort.
\ No newline at end of file
diff --git a/apps/tc/tests/bigTest.v b/apps/tc/tests/bigTest.v
new file mode 100644
index 000000000..499f07b9a
--- /dev/null
+++ b/apps/tc/tests/bigTest.v
@@ -0,0 +1,1719 @@
+From elpi.apps Require Import tc.
+Elpi Override TC TC.Solver All.
+
+(** This file collects type class interfaces, notations, and general theorems
+that are used throughout the whole development. Most importantly it contains
+abstract interfaces for ordered structures, sets, and various other data
+structures. *)
+
+(* We want to ensure that [le] and [lt] refer to operations on [nat].
+These two functions being defined both in [Coq.Bool] and in [Coq.Peano],
+we must export [Coq.Peano] later than any export of [Coq.Bool]. *)
+(* We also want to ensure that notations from [Coq.Utf8] take precedence
+over the ones of [Coq.Peano] (see Coq PR#12950), so we import [Utf8] last. *)
+From Coq Require Export Morphisms RelationClasses List Bool Setoid Peano Utf8.
+From Coq Require Import Permutation.
+Export ListNotations.
+From Coq.Program Require Export Basics Syntax.
+
+TC.AddAllClasses.
+TC.AddAllInstances.
+
+(** This notation is necessary to prevent [length] from being printed
+as [strings.length] if strings.v is imported and later base.v. See
+also strings.v and
+https://gitlab.mpi-sws.org/iris/stdpp/-/merge_requests/144 and
+https://gitlab.mpi-sws.org/iris/stdpp/-/merge_requests/129. *)
+Notation length := Datatypes.length.
+
+(** * Enable implicit generalization. *)
+(** This option enables implicit generalization in arguments of the form
+ [`{...}] (i.e., anonymous arguments). Unfortunately, it also enables
+ implicit generalization in [Instance]. We think that the fact that both
+ behaviors are coupled together is a [bug in
+ Coq](https://github.com/coq/coq/issues/6030). *)
+Global Generalizable All Variables.
+
+(** * Tweak program *)
+(** 1. Since we only use Program to solve logical side-conditions, they should
+always be made Opaque, otherwise we end up with performance problems due to
+Coq blindly unfolding them.
+
+Note that in most cases we use [Next Obligation. (* ... *) Qed.], for which
+this option does not matter. However, sometimes we write things like
+[Solve Obligations with naive_solver (* ... *)], and then the obligations
+should surely be opaque. *)
+Global Unset Transparent Obligations.
+
+(** 2. Do not let Program automatically simplify obligations. The default
+obligation tactic is [Tactics.program_simpl], which, among other things,
+introduces all variables and gives them fresh names. As such, it becomes
+impossible to refer to hypotheses in a robust way. *)
+Global Obligation Tactic := idtac.
+
+(** 3. Hide obligations and unsealing lemmas from the results of the [Search]
+commands. *)
+Add Search Blacklist "_obligation_".
+Add Search Blacklist "_unseal".
+
+(** * Sealing off definitions *)
+#[projections(primitive=yes)]
+Record seal {A} (f : A) := { unseal : A; seal_eq : unseal = f }.
+Global Arguments unseal {_ _} _ : assert.
+Global Arguments seal_eq {_ _} _ : assert.
+
+(** * Solving type class instances *)
+(** The tactic [tc_solve] is used to solve type class goals by invoking type
+class search. It is similar to [apply _], but it is more robust since it does
+not affect unrelated goals/evars due to https://github.com/coq/coq/issues/6583.
+
+The tactic [tc_solve] is particularly useful when building custom tactics that
+need tight control over when type class search is invoked. In Iris, many of the
+proof mode tactics make use of [notypeclasses refine] and use [tc_solve] to
+manually invoke type class search.
+
+Note that [typeclasses eauto] is multi-success. That means, whenever subsequent
+tactics fail, it will backtrack to [typeclasses eauto] to try the next type
+class instance. This is almost always undesired and can lead to poor performance
+and horrible error messages. Hence, we wrap it in a [once]. *)
+Ltac tc_solve :=
+ solve [once (typeclasses eauto)].
+
+(** * Non-backtracking type classes *)
+(** The type class [TCNoBackTrack P] can be used to establish [P] without ever
+backtracking on the instance of [P] that has been found. Backtracking may
+normally happen when [P] contains evars that could be instanciated in different
+ways depending on which instance is picked, and type class search somewhere else
+depends on this evar.
+
+The proper way of handling this would be by setting Coq's option
+`Typeclasses Unique Instances`. However, this option seems to be broken, see Coq
+issue #6714.
+
+See https://gitlab.mpi-sws.org/FP/iris-coq/merge_requests/112 for a rationale
+of this type class. *)
+Class TCNoBackTrack (P : Prop) := TCNoBackTrack_intro { tc_no_backtrack : P }.
+Global Hint Extern 0 (TCNoBackTrack _) =>
+ notypeclasses refine (TCNoBackTrack_intro _ _); tc_solve : typeclass_instances.
+
+(* A conditional at the type class level. Note that [TCIf P Q R] is not the same
+as [TCOr (TCAnd P Q) R]: the latter will backtrack to [R] if it fails to
+establish [Q], i.e. does not have the behavior of a conditional. Furthermore,
+note that [TCOr (TCAnd P Q) (TCAnd (TCNot P) R)] would not work; we generally
+would not be able to prove the negation of [P]. *)
+Inductive TCIf (P Q R : Prop) : Prop :=
+ | TCIf_true : P → Q → TCIf P Q R
+ | TCIf_false : R → TCIf P Q R.
+Existing Class TCIf.
+
+Global Hint Extern 0 (TCIf _ _ _) =>
+ first [notypeclasses refine (TCIf_true _ _ _ _ _); [tc_solve|]
+ |notypeclasses refine (TCIf_false _ _ _ _)] : typeclass_instances.
+
+(** * Typeclass opaque definitions *)
+(** The constant [tc_opaque] is used to make definitions opaque for just type
+class search. Note that [simpl] is set up to always unfold [tc_opaque]. *)
+Definition tc_opaque {A} (x : A) : A := x.
+Global Typeclasses Opaque tc_opaque.
+Global Arguments tc_opaque {_} _ /.
+
+(** Below we define type class versions of the common logical operators. It is
+important to note that we duplicate the definitions, and do not declare the
+existing logical operators as type classes. That is, we do not say:
+
+ Existing Class or.
+ Existing Class and.
+
+If we could define the existing logical operators as classes, there is no way
+of disambiguating whether a premise of a lemma should be solved by type class
+resolution or not.
+
+These classes are useful for two purposes: writing complicated type class
+premises in a more concise way, and for efficiency. For example, using the [Or]
+class, instead of defining two instances [P → Q1 → R] and [P → Q2 → R] we could
+have one instance [P → Or Q1 Q2 → R]. When we declare the instance that way, we
+avoid the need to derive [P] twice. *)
+Inductive TCOr (P1 P2 : Prop) : Prop :=
+ | TCOr_l : P1 → TCOr P1 P2
+ | TCOr_r : P2 → TCOr P1 P2.
+Existing Class TCOr.
+Global Existing Instance TCOr_l | 9.
+Global Existing Instance TCOr_r | 10.
+Global Hint Mode TCOr ! ! : typeclass_instances.
+
+Inductive TCAnd (P1 P2 : Prop) : Prop := TCAnd_intro : P1 → P2 → TCAnd P1 P2.
+Existing Class TCAnd.
+Global Existing Instance TCAnd_intro.
+Global Hint Mode TCAnd ! ! : typeclass_instances.
+
+Inductive TCTrue : Prop := TCTrue_intro : TCTrue.
+Existing Class TCTrue.
+Global Existing Instance TCTrue_intro.
+
+(** The class [TCFalse] is not stricly necessary as one could also use
+[False]. However, users might expect that TCFalse exists and if it
+does not, it can cause hard to diagnose bugs due to automatic
+generalization. *)
+Inductive TCFalse : Prop :=.
+Existing Class TCFalse.
+
+(** The class [TCUnless] can be used to check that search for [P]
+fails. This is useful as a guard for certain instances together with
+classes like [TCFastDone] (see [tactics.v]) to prevent infinite loops
+(e.g. when saturating the context). *)
+Notation TCUnless P := (TCIf P TCFalse TCTrue).
+
+Inductive TCForall {A} (P : A → Prop) : list A → Prop :=
+ | TCForall_nil : TCForall P []
+ | TCForall_cons x xs : P x → TCForall P xs → TCForall P (x :: xs).
+Existing Class TCForall.
+Global Existing Instance TCForall_nil.
+Global Existing Instance TCForall_cons.
+Global Hint Mode TCForall ! ! ! : typeclass_instances.
+
+(** The class [TCForall2 P l k] is commonly used to transform an input list [l]
+into an output list [k], or the converse. Therefore there are two modes, either
+[l] input and [k] output, or [k] input and [l] input. *)
+Inductive TCForall2 {A B} (P : A → B → Prop) : list A → list B → Prop :=
+ | TCForall2_nil : TCForall2 P [] []
+ | TCForall2_cons x y xs ys :
+ P x y → TCForall2 P xs ys → TCForall2 P (x :: xs) (y :: ys).
+Existing Class TCForall2.
+Global Existing Instance TCForall2_nil.
+Global Existing Instance TCForall2_cons.
+Global Hint Mode TCForall2 ! ! ! ! - : typeclass_instances.
+Global Hint Mode TCForall2 ! ! ! - ! : typeclass_instances.
+
+Inductive TCExists {A} (P : A → Prop) : list A → Prop :=
+ | TCExists_cons_hd x l : P x → TCExists P (x :: l)
+ | TCExists_cons_tl x l: TCExists P l → TCExists P (x :: l).
+Existing Class TCExists.
+Global Existing Instance TCExists_cons_hd | 10.
+Global Existing Instance TCExists_cons_tl | 20.
+Global Hint Mode TCExists ! ! ! : typeclass_instances.
+
+Inductive TCElemOf {A} (x : A) : list A → Prop :=
+ | TCElemOf_here xs : TCElemOf x (x :: xs)
+ | TCElemOf_further y xs : TCElemOf x xs → TCElemOf x (y :: xs).
+Existing Class TCElemOf.
+Global Existing Instance TCElemOf_here.
+Global Existing Instance TCElemOf_further.
+Global Hint Mode TCElemOf ! ! ! : typeclass_instances.
+
+(** The intended use of [TCEq x y] is to use [x] as input and [y] as output, but
+this is not enforced. We use output mode [-] (instead of [!]) for [x] to ensure
+that type class search succeed on goals like [TCEq (if ? then e1 else e2) ?y],
+see https://gitlab.mpi-sws.org/iris/iris/merge_requests/391 for a use case.
+Mode [-] is harmless, the only instance of [TCEq] is [TCEq_refl] below, so we
+cannot create loops. *)
+Inductive TCEq {A} (x : A) : A → Prop := TCEq_refl : TCEq x x.
+Existing Class TCEq.
+Global Existing Instance TCEq_refl.
+Global Hint Mode TCEq ! - - : typeclass_instances.
+
+Lemma TCEq_eq {A} (x1 x2 : A) : TCEq x1 x2 ↔ x1 = x2.
+Proof. split; destruct 1; reflexivity. Qed.
+
+(** The [TCSimpl x y] type class is similar to [TCEq] but performs [simpl]
+before proving the goal by reflexivity. Similar to [TCEq], the argument [x]
+is the input and [y] the output. When solving [TCEq x y], the argument [x]
+should be a concrete term and [y] an evar for the [simpl]ed result. *)
+Class TCSimpl {A} (x x' : A) := TCSimpl_TCEq : TCEq x x'.
+Global Hint Extern 0 (TCSimpl _ _) =>
+ (* Since the second argument should be an evar, we can call [simpl] on the
+ whole goal. *)
+ simpl; notypeclasses refine (TCEq_refl _) : typeclass_instances.
+Global Hint Mode TCSimpl ! - - : typeclass_instances.
+
+Lemma TCSimpl_eq {A} (x1 x2 : A) : TCSimpl x1 x2 ↔ x1 = x2.
+Proof. apply TCEq_eq. Qed.
+
+Inductive TCDiag {A} (C : A → Prop) : A → A → Prop :=
+ | TCDiag_diag x : C x → TCDiag C x x.
+Existing Class TCDiag.
+Global Existing Instance TCDiag_diag.
+Global Hint Mode TCDiag ! ! ! - : typeclass_instances.
+Global Hint Mode TCDiag ! ! - ! : typeclass_instances.
+
+(** Given a proposition [P] that is a type class, [tc_to_bool P] will return
+[true] iff there is an instance of [P]. It is often useful in Ltac programming,
+where one can do [lazymatch tc_to_bool P with true => .. | false => .. end]. *)
+Definition tc_to_bool (P : Prop)
+ {p : bool} `{TCIf P (TCEq p true) (TCEq p false)} : bool := p.
+
+(** Throughout this development we use [stdpp_scope] for all general purpose
+notations that do not belong to a more specific scope. *)
+Declare Scope stdpp_scope.
+Delimit Scope stdpp_scope with stdpp.
+Global Open Scope stdpp_scope.
+
+(** Change [True] and [False] into notations in order to enable overloading.
+We will use this to give [True] and [False] a different interpretation for
+embedded logics. *)
+Notation "'True'" := True (format "True") : type_scope.
+Notation "'False'" := False (format "False") : type_scope.
+
+(** Change [forall] into a notation in order to enable overloading. *)
+Notation "'forall' x .. y , P" := (forall x, .. (forall y, P) ..)
+ (at level 200, x binder, y binder, right associativity,
+ only parsing) : type_scope.
+
+
+(** * Equality *)
+(** Introduce some Haskell style like notations. *)
+Notation "(=)" := eq (only parsing) : stdpp_scope.
+Notation "( x =.)" := (eq x) (only parsing) : stdpp_scope.
+Notation "(.= x )" := (λ y, eq y x) (only parsing) : stdpp_scope.
+Notation "(≠)" := (λ x y, x ≠ y) (only parsing) : stdpp_scope.
+Notation "( x ≠.)" := (λ y, x ≠ y) (only parsing) : stdpp_scope.
+Notation "(.≠ x )" := (λ y, y ≠ x) (only parsing) : stdpp_scope.
+
+Infix "=@{ A }" := (@eq A)
+ (at level 70, only parsing, no associativity) : stdpp_scope.
+Notation "(=@{ A } )" := (@eq A) (only parsing) : stdpp_scope.
+Notation "(≠@{ A } )" := (λ X Y, ¬X =@{A} Y) (only parsing) : stdpp_scope.
+Notation "X ≠@{ A } Y":= (¬X =@{ A } Y)
+ (at level 70, only parsing, no associativity) : stdpp_scope.
+
+Global Hint Extern 0 (_ = _) => reflexivity : core.
+Global Hint Extern 100 (_ ≠ _) => discriminate : core.
+
+Global Instance: ∀ A, PreOrder (=@{A}).
+Proof. split; repeat intro; congruence. Qed.
+
+(** ** Setoid equality *)
+(** We define an operational type class for setoid equality, i.e., the
+"canonical" equivalence for a type. The typeclass is tied to the \equiv
+symbol. This is based on (Spitters/van der Weegen, 2011). *)
+Class Equiv A := equiv: relation A.
+Global Hint Mode Equiv ! : typeclass_instances.
+
+(** We instruct setoid rewriting to infer [equiv] as a relation on
+type [A] when needed. This allows setoid_rewrite to solve constraints
+of shape [Proper (eq ==> ?R) f] using [Proper (eq ==> (equiv (A:=A))) f]
+when an equivalence relation is available on type [A]. We put this instance
+at level 150 so it does not take precedence over Coq's stdlib instances,
+favoring inference of [eq] (all Coq functions are automatically morphisms
+for [eq]). We have [eq] (at 100) < [≡] (at 150) < [⊑] (at 200). *)
+Global Instance equiv_rewrite_relation `{Equiv A} :
+ RewriteRelation (@equiv A _) | 150 := {}.
+
+Infix "≡" := equiv (at level 70, no associativity) : stdpp_scope.
+Infix "≡@{ A }" := (@equiv A _)
+ (at level 70, only parsing, no associativity) : stdpp_scope.
+
+Notation "(≡)" := equiv (only parsing) : stdpp_scope.
+Notation "( X ≡.)" := (equiv X) (only parsing) : stdpp_scope.
+Notation "(.≡ X )" := (λ Y, Y ≡ X) (only parsing) : stdpp_scope.
+Notation "(≢)" := (λ X Y, ¬X ≡ Y) (only parsing) : stdpp_scope.
+Notation "X ≢ Y":= (¬X ≡ Y) (at level 70, no associativity) : stdpp_scope.
+Notation "( X ≢.)" := (λ Y, X ≢ Y) (only parsing) : stdpp_scope.
+Notation "(.≢ X )" := (λ Y, Y ≢ X) (only parsing) : stdpp_scope.
+
+Notation "(≡@{ A } )" := (@equiv A _) (only parsing) : stdpp_scope.
+Notation "(≢@{ A } )" := (λ X Y, ¬X ≡@{A} Y) (only parsing) : stdpp_scope.
+Notation "X ≢@{ A } Y":= (¬X ≡@{ A } Y)
+ (at level 70, only parsing, no associativity) : stdpp_scope.
+
+(** The type class [LeibnizEquiv] collects setoid equalities that coincide
+with Leibniz equality. We provide the tactic [fold_leibniz] to transform such
+setoid equalities into Leibniz equalities, and [unfold_leibniz] for the
+reverse.
+
+Various std++ tactics assume that this class is only instantiated if [≡]
+is an equivalence relation. *)
+Class LeibnizEquiv A `{Equiv A} :=
+ leibniz_equiv (x y : A) : x ≡ y → x = y.
+Global Hint Mode LeibnizEquiv ! ! : typeclass_instances.
+
+Lemma leibniz_equiv_iff `{LeibnizEquiv A, !Reflexive (≡@{A})} (x y : A) :
+ x ≡ y ↔ x = y.
+Proof. split; [apply leibniz_equiv|]. intros ->; reflexivity. Qed.
+
+Ltac fold_leibniz := repeat
+ match goal with
+ | H : context [ _ ≡@{?A} _ ] |- _ =>
+ setoid_rewrite (leibniz_equiv_iff (A:=A)) in H
+ | |- context [ _ ≡@{?A} _ ] =>
+ setoid_rewrite (leibniz_equiv_iff (A:=A))
+ end.
+Ltac unfold_leibniz := repeat
+ match goal with
+ | H : context [ _ =@{?A} _ ] |- _ =>
+ setoid_rewrite <-(leibniz_equiv_iff (A:=A)) in H
+ | |- context [ _ =@{?A} _ ] =>
+ setoid_rewrite <-(leibniz_equiv_iff (A:=A))
+ end.
+
+Definition equivL {A} : Equiv A := (=).
+
+(** A [Params f n] instance forces the setoid rewriting mechanism not to
+rewrite in the first [n] arguments of the function [f]. We will declare such
+instances for all operational type classes in this development. *)
+Global Instance: Params (@equiv) 2 := {}.
+
+(** The following instance forces [setoid_replace] to use setoid equality
+(for types that have an [Equiv] instance) rather than the standard Leibniz
+equality. *)
+Global Instance equiv_default_relation `{Equiv A} :
+ DefaultRelation (≡@{A}) | 3 := {}.
+Global Hint Extern 0 (_ ≡ _) => reflexivity : core.
+Global Hint Extern 0 (_ ≡ _) => symmetry; assumption : core.
+
+
+(** * Type classes *)
+(** ** Decidable propositions *)
+(** This type class by (Spitters/van der Weegen, 2011) collects decidable
+propositions. *)
+Class Decision (P : Prop) := decide : {P} + {¬P}.
+Global Hint Mode Decision ! : typeclass_instances.
+Global Arguments decide _ {_} : simpl never, assert.
+
+(** Although [RelDecision R] is just [∀ x y, Decision (R x y)], we make this
+an explicit class instead of a notation for two reasons:
+
+- It allows us to control [Hint Mode] more precisely. In particular, if it were
+ defined as a notation, the above [Hint Mode] for [Decision] would not prevent
+ diverging instance search when looking for [RelDecision (@eq ?A)], which would
+ result in it looking for [Decision (@eq ?A x y)], i.e. an instance where the
+ head position of [Decision] is not en evar.
+- We use it to avoid inefficient computation due to eager evaluation of
+ propositions by [vm_compute]. This inefficiency arises for example if
+ [(x = y) := (f x = f y)]. Since [decide (x = y)] evaluates to
+ [decide (f x = f y)], this would then lead to evaluation of [f x] and [f y].
+ Using the [RelDecision], the [f] is hidden under a lambda, which prevents
+ unnecessary evaluation. *)
+Class RelDecision {A B} (R : A → B → Prop) :=
+ decide_rel x y :> Decision (R x y).
+Global Hint Mode RelDecision ! ! ! : typeclass_instances.
+Global Arguments decide_rel {_ _} _ {_} _ _ : simpl never, assert.
+Notation EqDecision A := (RelDecision (=@{A})).
+
+(** ** Inhabited types *)
+(** This type class collects types that are inhabited. *)
+Class Inhabited (A : Type) : Type := populate { inhabitant : A }.
+Global Hint Mode Inhabited ! : typeclass_instances.
+Global Arguments populate {_} _ : assert.
+
+(** ** Proof irrelevant types *)
+(** This type class collects types that are proof irrelevant. That means, all
+elements of the type are equal. We use this notion only used for propositions,
+but by universe polymorphism we can generalize it. *)
+Class ProofIrrel (A : Type) : Prop := proof_irrel (x y : A) : x = y.
+Global Hint Mode ProofIrrel ! : typeclass_instances.
+
+(** ** Common properties *)
+(** These operational type classes allow us to refer to common mathematical
+properties in a generic way. For example, for injectivity of [(k ++.)] it
+allows us to write [inj (k ++.)] instead of [app_inv_head k]. *)
+Class Inj {A B} (R : relation A) (S : relation B) (f : A → B) : Prop :=
+ inj x y : S (f x) (f y) → R x y.
+Class Inj2 {A B C} (R1 : relation A) (R2 : relation B)
+ (S : relation C) (f : A → B → C) : Prop :=
+ inj2 x1 x2 y1 y2 : S (f x1 x2) (f y1 y2) → R1 x1 y1 ∧ R2 x2 y2.
+Class Cancel {A B} (S : relation B) (f : A → B) (g : B → A) : Prop :=
+ cancel x : S (f (g x)) x.
+Class Surj {A B} (R : relation B) (f : A → B) :=
+ surj y : ∃ x, R (f x) y.
+Class IdemP {A} (R : relation A) (f : A → A → A) : Prop :=
+ idemp x : R (f x x) x.
+Class Comm {A B} (R : relation A) (f : B → B → A) : Prop :=
+ comm x y : R (f x y) (f y x).
+Class LeftId {A} (R : relation A) (i : A) (f : A → A → A) : Prop :=
+ left_id x : R (f i x) x.
+Class RightId {A} (R : relation A) (i : A) (f : A → A → A) : Prop :=
+ right_id x : R (f x i) x.
+Class Assoc {A} (R : relation A) (f : A → A → A) : Prop :=
+ assoc x y z : R (f x (f y z)) (f (f x y) z).
+Class LeftAbsorb {A} (R : relation A) (i : A) (f : A → A → A) : Prop :=
+ left_absorb x : R (f i x) i.
+Class RightAbsorb {A} (R : relation A) (i : A) (f : A → A → A) : Prop :=
+ right_absorb x : R (f x i) i.
+Class AntiSymm {A} (R S : relation A) : Prop :=
+ anti_symm x y : S x y → S y x → R x y.
+Class Total {A} (R : relation A) := total x y : R x y ∨ R y x.
+Class Trichotomy {A} (R : relation A) :=
+ trichotomy x y : R x y ∨ x = y ∨ R y x.
+Class TrichotomyT {A} (R : relation A) :=
+ trichotomyT x y : {R x y} + {x = y} + {R y x}.
+
+Notation Involutive R f := (Cancel R f f).
+Lemma involutive {A} {R : relation A} (f : A → A) `{Involutive R f} x :
+ R (f (f x)) x.
+Proof. auto. Qed.
+
+Global Arguments irreflexivity {_} _ {_} _ _ : assert.
+Global Arguments inj {_ _ _ _} _ {_} _ _ _ : assert.
+Global Arguments inj2 {_ _ _ _ _ _} _ {_} _ _ _ _ _: assert.
+Global Arguments cancel {_ _ _} _ _ {_} _ : assert.
+Global Arguments surj {_ _ _} _ {_} _ : assert.
+Global Arguments idemp {_ _} _ {_} _ : assert.
+Global Arguments comm {_ _ _} _ {_} _ _ : assert.
+Global Arguments left_id {_ _} _ _ {_} _ : assert.
+Global Arguments right_id {_ _} _ _ {_} _ : assert.
+Global Arguments assoc {_ _} _ {_} _ _ _ : assert.
+Global Arguments left_absorb {_ _} _ _ {_} _ : assert.
+Global Arguments right_absorb {_ _} _ _ {_} _ : assert.
+Global Arguments anti_symm {_ _} _ {_} _ _ _ _ : assert.
+Global Arguments total {_} _ {_} _ _ : assert.
+Global Arguments trichotomy {_} _ {_} _ _ : assert.
+Global Arguments trichotomyT {_} _ {_} _ _ : assert.
+
+Lemma not_symmetry `{R : relation A, !Symmetric R} x y : ¬R x y → ¬R y x.
+Proof. intuition. Qed.
+Lemma symmetry_iff `(R : relation A) `{!Symmetric R} x y : R x y ↔ R y x.
+Proof. intuition. Qed.
+
+Lemma not_inj `{Inj A B R R' f} x y : ¬R x y → ¬R' (f x) (f y).
+Proof. intuition. Qed.
+Lemma not_inj2_1 `{Inj2 A B C R R' R'' f} x1 x2 y1 y2 :
+ ¬R x1 x2 → ¬R'' (f x1 y1) (f x2 y2).
+Proof. intros HR HR''. destruct (inj2 f x1 y1 x2 y2); auto. Qed.
+Lemma not_inj2_2 `{Inj2 A B C R R' R'' f} x1 x2 y1 y2 :
+ ¬R' y1 y2 → ¬R'' (f x1 y1) (f x2 y2).
+Proof. intros HR' HR''. destruct (inj2 f x1 y1 x2 y2); auto. Qed.
+
+Lemma inj_iff {A B} {R : relation A} {S : relation B} (f : A → B)
+ `{!Inj R S f} `{!Proper (R ==> S) f} x y : S (f x) (f y) ↔ R x y.
+Proof. firstorder. Qed.
+Global Instance inj2_inj_1 `{Inj2 A B C R1 R2 R3 f} y : Inj R1 R3 (λ x, f x y).
+Proof. repeat intro; edestruct (inj2 f); eauto. Qed.
+Global Instance inj2_inj_2 `{Inj2 A B C R1 R2 R3 f} x : Inj R2 R3 (f x).
+Proof. repeat intro; edestruct (inj2 f); eauto. Qed.
+Elpi Override TC - ProperProxy.
+
+Lemma cancel_inj `{Cancel A B R1 f g, !Equivalence R1, !Proper (R2 ==> R1) f} :
+ Inj R1 R2 g.
+Proof.
+ intros x y E. rewrite <-(cancel f g x), <-(cancel f g y), E. reflexivity.
+Qed.
+Lemma cancel_surj `{Cancel A B R1 f g} : Surj R1 f.
+Proof. intros y. exists (g y). auto. Qed.
+
+(** The following lemmas are specific versions of the projections of the above
+type classes for Leibniz equality. These lemmas allow us to enforce Coq not to
+use the setoid rewriting mechanism. *)
+Lemma idemp_L {A} f `{!@IdemP A (=) f} x : f x x = x.
+Proof. auto. Qed.
+Lemma comm_L {A B} f `{!@Comm A B (=) f} x y : f x y = f y x.
+Proof. auto. Qed.
+Lemma left_id_L {A} i f `{!@LeftId A (=) i f} x : f i x = x.
+Proof. auto. Qed.
+Lemma right_id_L {A} i f `{!@RightId A (=) i f} x : f x i = x.
+Proof. auto. Qed.
+Lemma assoc_L {A} f `{!@Assoc A (=) f} x y z : f x (f y z) = f (f x y) z.
+Proof. auto. Qed.
+Lemma left_absorb_L {A} i f `{!@LeftAbsorb A (=) i f} x : f i x = i.
+Proof. auto. Qed.
+Lemma right_absorb_L {A} i f `{!@RightAbsorb A (=) i f} x : f x i = i.
+Proof. auto. Qed.
+
+(** ** Generic orders *)
+(** The classes [PreOrder], [PartialOrder], and [TotalOrder] use an arbitrary
+relation [R] instead of [⊆] to support multiple orders on the same type. *)
+Definition strict {A} (R : relation A) : relation A := λ X Y, R X Y ∧ ¬R Y X.
+Global Instance: Params (@strict) 2 := {}.
+
+Class PartialOrder {A} (R : relation A) : Prop := {
+ partial_order_pre :> PreOrder R;
+ partial_order_anti_symm :> AntiSymm (=) R
+}.
+Global Hint Mode PartialOrder ! ! : typeclass_instances.
+
+Class TotalOrder {A} (R : relation A) : Prop := {
+ total_order_partial :> PartialOrder R;
+ total_order_trichotomy :> Trichotomy (strict R)
+}.
+Global Hint Mode TotalOrder ! ! : typeclass_instances.
+
+(** * Logic *)
+Global Instance prop_inhabited : Inhabited Prop := populate True.
+
+Notation "(∧)" := and (only parsing) : stdpp_scope.
+Notation "( A ∧.)" := (and A) (only parsing) : stdpp_scope.
+Notation "(.∧ B )" := (λ A, A ∧ B) (only parsing) : stdpp_scope.
+
+Notation "(∨)" := or (only parsing) : stdpp_scope.
+Notation "( A ∨.)" := (or A) (only parsing) : stdpp_scope.
+Notation "(.∨ B )" := (λ A, A ∨ B) (only parsing) : stdpp_scope.
+
+Notation "(↔)" := iff (only parsing) : stdpp_scope.
+Notation "( A ↔.)" := (iff A) (only parsing) : stdpp_scope.
+Notation "(.↔ B )" := (λ A, A ↔ B) (only parsing) : stdpp_scope.
+
+Global Hint Extern 0 (_ ↔ _) => reflexivity : core.
+Global Hint Extern 0 (_ ↔ _) => symmetry; assumption : core.
+
+Lemma or_l P Q : ¬Q → P ∨ Q ↔ P.
+Proof. tauto. Qed.
+Lemma or_r P Q : ¬P → P ∨ Q ↔ Q.
+Proof. tauto. Qed.
+Lemma and_wlog_l (P Q : Prop) : (Q → P) → Q → (P ∧ Q).
+Proof. tauto. Qed.
+Lemma and_wlog_r (P Q : Prop) : P → (P → Q) → (P ∧ Q).
+Proof. tauto. Qed.
+Lemma impl_transitive (P Q R : Prop) : (P → Q) → (Q → R) → (P → R).
+Proof. tauto. Qed.
+Lemma forall_proper {A} (P Q : A → Prop) :
+ (∀ x, P x ↔ Q x) → (∀ x, P x) ↔ (∀ x, Q x).
+Proof. firstorder. Qed.
+Lemma exist_proper {A} (P Q : A → Prop) :
+ (∀ x, P x ↔ Q x) → (∃ x, P x) ↔ (∃ x, Q x).
+Proof. firstorder. Qed.
+
+Global Instance eq_comm {A} : Comm (↔) (=@{A}).
+Proof. red; intuition. Qed.
+Global Instance flip_eq_comm {A} : Comm (↔) (λ x y, y =@{A} x).
+Proof. red; intuition. Qed.
+Global Instance iff_comm : Comm (↔) (↔).
+Proof. red; intuition. Qed.
+Global Instance and_comm : Comm (↔) (∧).
+Proof. red; intuition. Qed.
+Global Instance and_assoc : Assoc (↔) (∧).
+Proof. red; intuition. Qed.
+Global Instance and_idemp : IdemP (↔) (∧).
+Proof. red; intuition. Qed.
+Global Instance or_comm : Comm (↔) (∨).
+Proof. red; intuition. Qed.
+Global Instance or_assoc : Assoc (↔) (∨).
+Proof. red; intuition. Qed.
+Global Instance or_idemp : IdemP (↔) (∨).
+Proof. red; intuition. Qed.
+Global Instance True_and : LeftId (↔) True (∧).
+Proof. red; intuition. Qed.
+Global Instance and_True : RightId (↔) True (∧).
+Proof. red; intuition. Qed.
+Global Instance False_and : LeftAbsorb (↔) False (∧).
+Proof. red; intuition. Qed.
+Global Instance and_False : RightAbsorb (↔) False (∧).
+Proof. red; intuition. Qed.
+Global Instance False_or : LeftId (↔) False (∨).
+Proof. red; intuition. Qed.
+Global Instance or_False : RightId (↔) False (∨).
+Proof. red; intuition. Qed.
+Global Instance True_or : LeftAbsorb (↔) True (∨).
+Proof. red; intuition. Qed.
+Global Instance or_True : RightAbsorb (↔) True (∨).
+Proof. red; intuition. Qed.
+Global Instance True_impl : LeftId (↔) True impl.
+Proof. unfold impl. red; intuition. Qed.
+Global Instance impl_True : RightAbsorb (↔) True impl.
+Proof. unfold impl. red; intuition. Qed.
+
+
+(** * Common data types *)
+(** ** Functions *)
+Notation "(→)" := (λ A B, A → B) (only parsing) : stdpp_scope.
+Notation "( A →.)" := (λ B, A → B) (only parsing) : stdpp_scope.
+Notation "(.→ B )" := (λ A, A → B) (only parsing) : stdpp_scope.
+
+Notation "t $ r" := (t r)
+ (at level 65, right associativity, only parsing) : stdpp_scope.
+Notation "($)" := (λ f x, f x) (only parsing) : stdpp_scope.
+Notation "(.$ x )" := (λ f, f x) (only parsing) : stdpp_scope.
+
+Infix "∘" := compose : stdpp_scope.
+Notation "(∘)" := compose (only parsing) : stdpp_scope.
+Notation "( f ∘.)" := (compose f) (only parsing) : stdpp_scope.
+Notation "(.∘ f )" := (λ g, compose g f) (only parsing) : stdpp_scope.
+
+Global Instance impl_inhabited {A} `{Inhabited B} : Inhabited (A → B) :=
+ populate (λ _, inhabitant).
+
+(** Ensure that [simpl] unfolds [id], [compose], and [flip] when fully
+applied. *)
+Global Arguments id _ _ / : assert.
+Global Arguments compose _ _ _ _ _ _ / : assert.
+Global Arguments flip _ _ _ _ _ _ / : assert.
+Global Arguments const _ _ _ _ / : assert.
+Global Typeclasses Transparent id compose flip const.
+
+Definition fun_map {A A' B B'} (f: A' → A) (g: B → B') (h : A → B) : A' → B' :=
+ g ∘ h ∘ f.
+
+Global Instance const_proper `{R1 : relation A, R2 : relation B} (x : B) :
+ Reflexive R2 → Proper (R1 ==> R2) (λ _, x).
+Proof. intros ? y1 y2; reflexivity. Qed.
+
+Global Instance id_inj {A} : Inj (=) (=) (@id A).
+Proof. intros ??; auto. Qed.
+Global Instance compose_inj {A B C} R1 R2 R3 (f : A → B) (g : B → C) :
+ Inj R1 R2 f → Inj R2 R3 g → Inj R1 R3 (g ∘ f).
+Proof. red; intuition. Qed.
+
+Global Instance id_surj {A} : Surj (=) (@id A).
+Proof. intros y; exists y; reflexivity. Qed.
+Global Instance compose_surj {A B C} R (f : A → B) (g : B → C) :
+ Surj (=) f → Surj R g → Surj R (g ∘ f).
+Proof.
+ intros ?? x. unfold compose. destruct (surj g x) as [y ?].
+ destruct (surj f y) as [z ?]. exists z. congruence.
+Qed.
+
+Global Instance const2_comm {A B} (x : B) : Comm (=) (λ _ _ : A, x).
+Proof. intros ?; reflexivity. Qed.
+Global Instance const2_assoc {A} (x : A) : Assoc (=) (λ _ _ : A, x).
+Proof. intros ???; reflexivity. Qed.
+Global Instance id1_assoc {A} : Assoc (=) (λ x _ : A, x).
+Proof. intros ???; reflexivity. Qed.
+Global Instance id2_assoc {A} : Assoc (=) (λ _ x : A, x).
+Proof. intros ???; reflexivity. Qed.
+Global Instance id1_idemp {A} : IdemP (=) (λ x _ : A, x).
+Proof. intros ?; reflexivity. Qed.
+Global Instance id2_idemp {A} : IdemP (=) (λ _ x : A, x).
+Proof. intros ?; reflexivity. Qed.
+
+(** ** Lists *)
+Global Instance list_inhabited {A} : Inhabited (list A) := populate [].
+
+Definition zip_with {A B C} (f : A → B → C) : list A → list B → list C :=
+ fix go l1 l2 :=
+ match l1, l2 with x1 :: l1, x2 :: l2 => f x1 x2 :: go l1 l2 | _ , _ => [] end.
+Notation zip := (zip_with pair).
+
+(** ** Booleans *)
+(** The following coercion allows us to use Booleans as propositions. *)
+Coercion Is_true : bool >-> Sortclass.
+Global Hint Unfold Is_true : core.
+Global Hint Immediate Is_true_eq_left : core.
+Global Hint Resolve orb_prop_intro andb_prop_intro : core.
+Notation "(&&)" := andb (only parsing).
+Notation "(||)" := orb (only parsing).
+Infix "&&*" := (zip_with (&&)) (at level 40).
+Infix "||*" := (zip_with (||)) (at level 50).
+
+Global Instance bool_inhabated : Inhabited bool := populate true.
+
+Definition bool_le (β1 β2 : bool) : Prop := negb β1 || β2.
+Infix "=.>" := bool_le (at level 70).
+Infix "=.>*" := (Forall2 bool_le) (at level 70).
+Global Instance: PartialOrder bool_le.
+Proof. repeat split; repeat intros [|]; compute; tauto. Qed.
+
+Lemma andb_True b1 b2 : b1 && b2 ↔ b1 ∧ b2.
+Proof. destruct b1, b2; simpl; tauto. Qed.
+Lemma orb_True b1 b2 : b1 || b2 ↔ b1 ∨ b2.
+Proof. destruct b1, b2; simpl; tauto. Qed.
+Lemma negb_True b : negb b ↔ ¬b.
+Proof. destruct b; simpl; tauto. Qed.
+Lemma Is_true_true (b : bool) : b ↔ b = true.
+Proof. now destruct b. Qed.
+Lemma Is_true_true_1 (b : bool) : b → b = true.
+Proof. apply Is_true_true. Qed.
+Lemma Is_true_true_2 (b : bool) : b = true → b.
+Proof. apply Is_true_true. Qed.
+Lemma Is_true_false (b : bool) : ¬ b ↔ b = false.
+Proof. now destruct b; simpl. Qed.
+Lemma Is_true_false_1 (b : bool) : ¬b → b = false.
+Proof. apply Is_true_false. Qed.
+Lemma Is_true_false_2 (b : bool) : b = false → ¬b.
+Proof. apply Is_true_false. Qed.
+
+(** ** Unit *)
+Global Instance unit_equiv : Equiv unit := λ _ _, True.
+Global Instance unit_equivalence : Equivalence (≡@{unit}).
+Proof. repeat split. Qed.
+Global Instance unit_leibniz : LeibnizEquiv unit.
+Proof. intros [] []; reflexivity. Qed.
+Global Instance unit_inhabited: Inhabited unit := populate ().
+
+(** ** Empty *)
+Global Instance Empty_set_equiv : Equiv Empty_set := λ _ _, True.
+Global Instance Empty_set_equivalence : Equivalence (≡@{Empty_set}).
+Proof. repeat split. Qed.
+Global Instance Empty_set_leibniz : LeibnizEquiv Empty_set.
+Proof. intros [] []; reflexivity. Qed.
+
+(** ** Products *)
+Notation "( x ,.)" := (pair x) (only parsing) : stdpp_scope.
+Notation "(., y )" := (λ x, (x,y)) (only parsing) : stdpp_scope.
+
+Notation "p .1" := (fst p) (at level 2, left associativity, format "p .1").
+Notation "p .2" := (snd p) (at level 2, left associativity, format "p .2").
+
+Global Instance: Params (@pair) 2 := {}.
+Global Instance: Params (@fst) 2 := {}.
+Global Instance: Params (@snd) 2 := {}.
+
+Global Instance: Params (@curry) 3 := {}.
+Global Instance: Params (@uncurry) 3 := {}.
+
+Definition uncurry3 {A B C D} (f : A → B → C → D) (p : A * B * C) : D :=
+ let '(a,b,c) := p in f a b c.
+Global Instance: Params (@uncurry3) 4 := {}.
+Definition uncurry4 {A B C D E} (f : A → B → C → D → E) (p : A * B * C * D) : E :=
+ let '(a,b,c,d) := p in f a b c d.
+Global Instance: Params (@uncurry4) 5 := {}.
+
+Definition curry3 {A B C D} (f : A * B * C → D) (a : A) (b : B) (c : C) : D :=
+ f (a, b, c).
+Global Instance: Params (@curry3) 4 := {}.
+Definition curry4 {A B C D E} (f : A * B * C * D → E)
+ (a : A) (b : B) (c : C) (d : D) : E := f (a, b, c, d).
+Global Instance: Params (@curry4) 5 := {}.
+
+Definition prod_map {A A' B B'} (f: A → A') (g: B → B') (p : A * B) : A' * B' :=
+ (f (p.1), g (p.2)).
+Global Instance: Params (@prod_map) 4 := {}.
+Global Arguments prod_map {_ _ _ _} _ _ !_ / : assert.
+
+Definition prod_zip {A A' A'' B B' B''} (f : A → A' → A'') (g : B → B' → B'')
+ (p : A * B) (q : A' * B') : A'' * B'' := (f (p.1) (q.1), g (p.2) (q.2)).
+Global Instance: Params (@prod_zip) 6 := {}.
+Global Arguments prod_zip {_ _ _ _ _ _} _ _ !_ !_ / : assert.
+
+Definition prod_swap {A B} (p : A * B) : B * A := (p.2, p.1).
+Global Arguments prod_swap {_ _} !_ /.
+Global Instance: Params (@prod_swap) 2 := {}.
+
+Global Instance prod_inhabited {A B} (iA : Inhabited A)
+ (iB : Inhabited B) : Inhabited (A * B) :=
+ match iA, iB with populate x, populate y => populate (x,y) end.
+
+(** Note that we need eta for products for the [uncurry_curry] lemmas to hold
+in non-applied form ([uncurry (curry f) = f]). *)
+Lemma curry_uncurry {A B C} (f : A → B → C) : curry (uncurry f) = f.
+Proof. reflexivity. Qed.
+Lemma uncurry_curry {A B C} (f : A * B → C) p : uncurry (curry f) p = f p.
+Proof. destruct p; reflexivity. Qed.
+Lemma curry3_uncurry3 {A B C D} (f : A → B → C → D) : curry3 (uncurry3 f) = f.
+Proof. reflexivity. Qed.
+Lemma uncurry3_curry3 {A B C D} (f : A * B * C → D) p :
+ uncurry3 (curry3 f) p = f p.
+Proof. destruct p as [[??] ?]; reflexivity. Qed.
+Lemma curry4_uncurry4 {A B C D E} (f : A → B → C → D → E) :
+ curry4 (uncurry4 f) = f.
+Proof. reflexivity. Qed.
+Lemma uncurry4_curry4 {A B C D E} (f : A * B * C * D → E) p :
+ uncurry4 (curry4 f) p = f p.
+Proof. destruct p as [[[??] ?] ?]; reflexivity. Qed.
+
+(** [pair_eq] as a name is more consistent with our usual naming. *)
+Lemma pair_eq {A B} (a1 a2 : A) (b1 b2 : B) :
+ (a1, b1) = (a2, b2) ↔ a1 = a2 ∧ b1 = b2.
+Proof. apply pair_equal_spec. Qed.
+
+Global Instance pair_inj {A B} : Inj2 (=) (=) (=) (@pair A B).
+Proof. injection 1; auto. Qed.
+Global Instance prod_map_inj {A A' B B'} (f : A → A') (g : B → B') :
+ Inj (=) (=) f → Inj (=) (=) g → Inj (=) (=) (prod_map f g).
+Proof.
+ intros ?? [??] [??] ?; simpl in *; f_equal;
+ [apply (inj f)|apply (inj g)]; congruence.
+Qed.
+
+Elpi Override TC - ProperProxy Proper.
+
+Global Instance prod_swap_cancel {A B} :
+ Cancel (=) (@prod_swap A B) (@prod_swap B A).
+Proof. intros [??]; reflexivity. Qed.
+Global Instance prod_swap_inj {A B} : Inj (=) (=) (@prod_swap A B).
+Proof. apply cancel_inj. Qed.
+Global Instance prod_swap_surj {A B} : Surj (=) (@prod_swap A B).
+Proof. apply cancel_surj. Qed.
+
+Definition prod_relation {A B} (R1 : relation A) (R2 : relation B) :
+ relation (A * B) := λ x y, R1 (x.1) (y.1) ∧ R2 (x.2) (y.2).
+
+Section prod_relation.
+ Context `{RA : relation A, RB : relation B}.
+
+ Global Instance prod_relation_refl :
+ Reflexive RA → Reflexive RB → Reflexive (prod_relation RA RB).
+ Proof. firstorder eauto. Qed.
+ Global Instance prod_relation_sym :
+ Symmetric RA → Symmetric RB → Symmetric (prod_relation RA RB).
+ Proof. firstorder eauto. Qed.
+ Global Instance prod_relation_trans :
+ Transitive RA → Transitive RB → Transitive (prod_relation RA RB).
+ Proof. firstorder eauto. Qed.
+ Global Instance prod_relation_equiv :
+ Equivalence RA → Equivalence RB → Equivalence (prod_relation RA RB).
+ Proof. split; apply _. Qed.
+
+ Global Instance pair_proper' : Proper (RA ==> RB ==> prod_relation RA RB) pair.
+ Proof. firstorder eauto. Qed.
+ Global Instance pair_inj' : Inj2 RA RB (prod_relation RA RB) pair.
+ Proof. inversion_clear 1; eauto. Qed.
+ Global Instance fst_proper' : Proper (prod_relation RA RB ==> RA) fst.
+ Proof. firstorder eauto. Qed.
+ Global Instance snd_proper' : Proper (prod_relation RA RB ==> RB) snd.
+ Proof. firstorder eauto. Qed.
+
+ Global Instance prod_swap_proper' :
+ Proper (prod_relation RA RB ==> prod_relation RB RA) prod_swap.
+ Proof. firstorder eauto. Qed.
+
+ Global Instance curry_proper' `{RC : relation C} :
+ Proper ((prod_relation RA RB ==> RC) ==> RA ==> RB ==> RC) curry.
+ Proof. firstorder eauto. Qed.
+ Global Instance uncurry_proper' `{RC : relation C} :
+ Proper ((RA ==> RB ==> RC) ==> prod_relation RA RB ==> RC) uncurry.
+ Proof. intros f1 f2 Hf [x1 y1] [x2 y2] []; apply Hf; assumption. Qed.
+
+ Global Instance curry3_proper' `{RC : relation C, RD : relation D} :
+ Proper ((prod_relation (prod_relation RA RB) RC ==> RD) ==>
+ RA ==> RB ==> RC ==> RD) curry3.
+ Proof. firstorder eauto. Qed.
+ Global Instance uncurry3_proper' `{RC : relation C, RD : relation D} :
+ Proper ((RA ==> RB ==> RC ==> RD) ==>
+ prod_relation (prod_relation RA RB) RC ==> RD) uncurry3.
+ Proof. intros f1 f2 Hf [[??] ?] [[??] ?] [[??] ?]; apply Hf; assumption. Qed.
+
+ Global Instance curry4_proper' `{RC : relation C, RD : relation D, RE : relation E} :
+ Proper ((prod_relation (prod_relation (prod_relation RA RB) RC) RD ==> RE) ==>
+ RA ==> RB ==> RC ==> RD ==> RE) curry4.
+ Proof. firstorder eauto. Qed.
+ Global Instance uncurry4_proper' `{RC : relation C, RD : relation D, RE : relation E} :
+ Proper ((RA ==> RB ==> RC ==> RD ==> RE) ==>
+ prod_relation (prod_relation (prod_relation RA RB) RC) RD ==> RE) uncurry4.
+ Proof.
+ intros f1 f2 Hf [[[??] ?] ?] [[[??] ?] ?] [[[??] ?] ?]; apply Hf; assumption.
+ Qed.
+End prod_relation.
+
+Global Instance prod_equiv `{Equiv A,Equiv B} : Equiv (A * B) :=
+ prod_relation (≡) (≡).
+
+(** Below we make [prod_equiv] type class opaque, so we first lift all
+instances *)
+Section prod_setoid.
+ Context `{Equiv A, Equiv B}.
+ Elpi Accumulate TC.Solver lp:{{
+ shorten tc-Coq.Classes.RelationClasses.{tc-Equivalence}.
+ :after "lastHook"
+ tc-Equivalence A RA R :-
+ RA = {{@equiv _ (@prod_equiv _ _ _ _)}},
+ RA' = {{@prod_relation _ _ _ _}},
+ coq.unify-eq RA RA' ok,
+ % coq.say A RA,
+ tc-Equivalence A RA' R.
+ }}.
+ (* Elpi Typecheck TC.Solver. *)
+
+ Global Instance prod_equivalence@{i} (C D: Type@{i}) `{Equiv C, Equiv D}:
+ @Equivalence C (≡@{C}) → @Equivalence D (≡@{D}) → @Equivalence (C * D) (≡@{C * D}) := _.
+
+ Elpi Accumulate TC.Solver lp:{{
+
+ pred remove_equiv_prod_equiv i:term, o:term.
+ remove_equiv_prod_equiv T1 T3 :-
+ T1 = {{@equiv _ (@prod_equiv _ _ _ _)}}, !,
+ T2 = {{@prod_relation lp:F lp:G lp:A lp:B}},
+ coq.unify-eq T1 T2 ok,
+ remove_equiv_prod_equiv A X,
+ remove_equiv_prod_equiv B Y,
+ {{@prod_relation lp:F lp:G lp:X lp:Y}} = T3.
+ remove_equiv_prod_equiv (app L1) (app L2) :- !,
+ std.map L1 remove_equiv_prod_equiv L2.
+ remove_equiv_prod_equiv A A.
+
+ shorten tc-Coq.Classes.Morphisms.{tc-Proper}.
+
+ :after "lastHook"
+ tc-Proper A B C R :-
+ B = {{ @respectful _ _ _ _ }},
+ remove_equiv_prod_equiv B B1,
+ tc-Proper A B1 C R.
+
+ tc-Proper A {{@respectful lp:K1 lp:K2 lp:B1 (@respectful lp:K3 lp:K4 lp:B2 lp:B3)}} C S :-
+ C1 = {{ @equiv _ _ }},
+ C2 = {{ @equiv _ _ }},
+ C3 = {{ @prod_relation _ _ _ _ }},
+ coq.unify-eq B1 C1 ok,
+ coq.unify-eq B2 C2 ok,
+ coq.unify-eq B3 C3 ok,
+ tc-Proper A {{@respectful lp:K1 lp:K2 lp:C1 (@respectful lp:K3 lp:K4 lp:C2 lp:C3)}} C S.
+
+ }}.
+ Elpi Typecheck TC.Solver.
+
+ Global Instance pair_proper : Proper ((≡) ==> (≡) ==> (≡@{A*B})) pair := _.
+
+ Elpi Accumulate TC.Solver lp:{{
+ shorten tc-elpi.apps.tc.tests.bigTest.{tc-Inj2}.
+ % shorten tc-bigTest.{tc-Inj2}.
+ :after "lastHook"
+ tc-Inj2 A B C RA RB RC F S :-
+ RC = app [global {coq.locate "equiv"} | _],
+ remove_equiv_prod_equiv RC RC',
+ tc-Inj2 A B C RA RB RC' F S.
+ }}.
+ Elpi Typecheck TC.Solver.
+
+ Global Instance pair_equiv_inj : Inj2 (≡) (≡) (≡@{A*B}) pair := _.
+ Global Instance fst_proper : Proper ((≡@{A*B}) ==> (≡)) fst := _.
+ Global Instance snd_proper : Proper ((≡@{A*B}) ==> (≡)) snd := _.
+
+ Global Instance prod_swap_proper :
+ Proper ((≡@{A*B}) ==> (≡@{B*A})) prod_swap := _.
+
+ Global Instance curry_proper `{Equiv C} :
+ Proper (((≡@{A*B}) ==> (≡@{C})) ==> (≡) ==> (≡) ==> (≡)) curry := _.
+ Global Instance uncurry_proper `{Equiv C} :
+ Proper (((≡) ==> (≡) ==> (≡)) ==> (≡@{A*B}) ==> (≡@{C})) uncurry := _.
+
+ Global Instance curry3_proper `{Equiv C, Equiv D} :
+ Proper (((≡@{A*B*C}) ==> (≡@{D})) ==>
+ (≡) ==> (≡) ==> (≡) ==> (≡)) curry3 := _.
+ Global Instance uncurry3_proper `{Equiv C, Equiv D} :
+ Proper (((≡) ==> (≡) ==> (≡) ==> (≡)) ==>
+ (≡@{A*B*C}) ==> (≡@{D})) uncurry3 := _.
+
+ Global Instance curry4_proper `{Equiv C, Equiv D, Equiv E} :
+ Proper (((≡@{A*B*C*D}) ==> (≡@{E})) ==>
+ (≡) ==> (≡) ==> (≡) ==> (≡) ==> (≡)) curry4 := _.
+ Global Instance uncurry4_proper `{Equiv C, Equiv D, Equiv E} :
+ Proper (((≡) ==> (≡) ==> (≡) ==> (≡) ==> (≡)) ==>
+ (≡@{A*B*C*D}) ==> (≡@{E})) uncurry4 := _.
+
+ Lemma pair_equiv (a1 a2 : A) (b1 b2 : B) :
+ (a1, b1) ≡ (a2, b2) ↔ a1 ≡ a2 ∧ b1 ≡ b2.
+ Proof. reflexivity. Qed.
+End prod_setoid.
+
+Global Typeclasses Opaque prod_equiv.
+
+Global Instance prod_leibniz `{LeibnizEquiv A, LeibnizEquiv B} :
+ LeibnizEquiv (A * B).
+Proof. intros [??] [??] [??]; f_equal; apply leibniz_equiv; auto. Qed.
+
+(** ** Sums *)
+Definition sum_map {A A' B B'} (f: A → A') (g: B → B') (xy : A + B) : A' + B' :=
+ match xy with inl x => inl (f x) | inr y => inr (g y) end.
+Global Arguments sum_map {_ _ _ _} _ _ !_ / : assert.
+
+Global Instance sum_inhabited_l {A B} (iA : Inhabited A) : Inhabited (A + B) :=
+ match iA with populate x => populate (inl x) end.
+Global Instance sum_inhabited_r {A B} (iB : Inhabited B) : Inhabited (A + B) :=
+ match iB with populate y => populate (inr y) end.
+
+Global Instance inl_inj {A B} : Inj (=) (=) (@inl A B).
+Proof. injection 1; auto. Qed.
+Global Instance inr_inj {A B} : Inj (=) (=) (@inr A B).
+Proof. injection 1; auto. Qed.
+
+Global Instance sum_map_inj {A A' B B'} (f : A → A') (g : B → B') :
+ Inj (=) (=) f → Inj (=) (=) g → Inj (=) (=) (sum_map f g).
+Proof. intros ?? [?|?] [?|?] [=]; f_equal; apply (inj _); auto. Qed.
+
+Inductive sum_relation {A B}
+ (RA : relation A) (RB : relation B) : relation (A + B) :=
+ | inl_related x1 x2 : RA x1 x2 → sum_relation RA RB (inl x1) (inl x2)
+ | inr_related y1 y2 : RB y1 y2 → sum_relation RA RB (inr y1) (inr y2).
+
+Section sum_relation.
+ Context `{RA : relation A, RB : relation B}.
+ Global Instance sum_relation_refl :
+ Reflexive RA → Reflexive RB → Reflexive (sum_relation RA RB).
+ Proof. intros ?? [?|?]; constructor; reflexivity. Qed.
+ Global Instance sum_relation_sym :
+ Symmetric RA → Symmetric RB → Symmetric (sum_relation RA RB).
+ Proof. destruct 3; constructor; eauto. Qed.
+ Global Instance sum_relation_trans :
+ Transitive RA → Transitive RB → Transitive (sum_relation RA RB).
+ Proof. destruct 3; inversion_clear 1; constructor; eauto. Qed.
+ Global Instance sum_relation_equiv :
+ Equivalence RA → Equivalence RB → Equivalence (sum_relation RA RB).
+ Proof. split; apply _. Qed.
+ Global Instance inl_proper' : Proper (RA ==> sum_relation RA RB) inl.
+ Proof. constructor; auto. Qed.
+ Global Instance inr_proper' : Proper (RB ==> sum_relation RA RB) inr.
+ Proof. constructor; auto. Qed.
+ Global Instance inl_inj' : Inj RA (sum_relation RA RB) inl.
+ Proof. inversion_clear 1; auto. Qed.
+ Global Instance inr_inj' : Inj RB (sum_relation RA RB) inr.
+ Proof. inversion_clear 1; auto. Qed.
+End sum_relation.
+
+Global Instance sum_equiv `{Equiv A, Equiv B} : Equiv (A + B) := sum_relation (≡) (≡).
+
+Elpi Accumulate TC.Solver lp:{{
+ pred remove_equiv_sum_equiv i:term, o:term.
+ remove_equiv_sum_equiv T1 T3 :-
+ T1 = {{@equiv _ (@sum_equiv _ _ _ _)}}, !,
+ T2 = {{@sum_relation lp:F lp:G lp:A lp:B}},
+ coq.unify-eq T1 T2 ok,
+ remove_equiv_sum_equiv A X,
+ remove_equiv_sum_equiv B Y,
+ {{@sum_relation lp:F lp:G lp:X lp:Y}} = T3.
+ remove_equiv_sum_equiv (app L1) (app L2) :- !,
+ std.map L1 remove_equiv_sum_equiv L2.
+ remove_equiv_sum_equiv A A.
+
+ shorten tc-Coq.Classes.Morphisms.{tc-Proper}.
+ :after "lastHook"
+ tc-Proper A B C R :-
+ B = {{ @respectful _ _ _ _ }},
+ remove_equiv_sum_equiv B B1,
+ tc-Proper A B1 C R.
+}}.
+Elpi Typecheck TC.Solver.
+
+
+Global Instance inl_proper `{Equiv A, Equiv B} : Proper ((≡) ==> (≡)) (@inl A B) := _.
+Global Instance inr_proper `{Equiv A, Equiv B} : Proper ((≡) ==> (≡)) (@inr A B) := _.
+
+
+Elpi Accumulate TC.Solver lp:{{
+ shorten tc-elpi.apps.tc.tests.bigTest.{tc-Inj}.
+ % shorten tc-bigTest.{tc-Inj}.
+ :after "lastHook"
+ tc-Inj A B R1 R2 S C :-
+ R2 = {{@equiv (sum _ _) sum_equiv}},
+ R2' = {{sum_relation _ _}},
+ coq.unify-eq R2 R2' ok,
+ tc-Inj A B R1 R2' S C.
+}}.
+Elpi Typecheck TC.Solver.
+
+Global Instance inl_equiv_inj `{Equiv A, Equiv B} : Inj (≡) (≡) (@inl A B) := _.
+Global Instance inr_equiv_inj `{Equiv A, Equiv B} : Inj (≡) (≡) (@inr A B) := _.
+Global Typeclasses Opaque sum_equiv.
+
+(** ** Option *)
+Global Instance option_inhabited {A} : Inhabited (option A) := populate None.
+
+(** ** Sigma types *)
+Global Arguments existT {_ _} _ _ : assert.
+Global Arguments projT1 {_ _} _ : assert.
+Global Arguments projT2 {_ _} _ : assert.
+
+Global Arguments exist {_} _ _ _ : assert.
+Global Arguments proj1_sig {_ _} _ : assert.
+Global Arguments proj2_sig {_ _} _ : assert.
+Notation "x ↾ p" := (exist _ x p) (at level 20) : stdpp_scope.
+Notation "` x" := (proj1_sig x) (at level 10, format "` x") : stdpp_scope.
+
+Lemma proj1_sig_inj {A} (P : A → Prop) x (Px : P x) y (Py : P y) :
+ x↾Px = y↾Py → x = y.
+Proof. injection 1; trivial. Qed.
+
+Section sig_map.
+ Context `{P : A → Prop} `{Q : B → Prop} (f : A → B) (Hf : ∀ x, P x → Q (f x)).
+ Definition sig_map (x : sig P) : sig Q := f (`x) ↾ Hf _ (proj2_sig x).
+ Global Instance sig_map_inj:
+ (∀ x, ProofIrrel (P x)) → Inj (=) (=) f → Inj (=) (=) sig_map.
+ Proof.
+ intros ?? [x Hx] [y Hy]. injection 1. intros Hxy.
+ apply (inj f) in Hxy; subst. rewrite (proof_irrel _ Hy). auto.
+ Qed.
+End sig_map.
+Global Arguments sig_map _ _ _ _ _ _ !_ / : assert.
+
+Definition proj1_ex {P : Prop} {Q : P → Prop} (p : ∃ x, Q x) : P :=
+ let '(ex_intro _ x _) := p in x.
+Definition proj2_ex {P : Prop} {Q : P → Prop} (p : ∃ x, Q x) : Q (proj1_ex p) :=
+ let '(ex_intro _ x H) := p in H.
+
+(** * Operations on sets *)
+(** We define operational type classes for the traditional operations and
+relations on sets: the empty set [∅], the union [(∪)],
+intersection [(∩)], and difference [(∖)], the singleton [{[_]}], the subset
+[(⊆)] and element of [(∈)] relation, and disjointess [(##)]. *)
+Class Empty A := empty: A.
+Global Hint Mode Empty ! : typeclass_instances.
+Notation "∅" := empty (format "∅") : stdpp_scope.
+
+Global Instance empty_inhabited `(Empty A) : Inhabited A := populate ∅.
+
+Class Union A := union: A → A → A.
+Global Hint Mode Union ! : typeclass_instances.
+Global Instance: Params (@union) 2 := {}.
+Infix "∪" := union (at level 50, left associativity) : stdpp_scope.
+Notation "(∪)" := union (only parsing) : stdpp_scope.
+Notation "( x ∪.)" := (union x) (only parsing) : stdpp_scope.
+Notation "(.∪ x )" := (λ y, union y x) (only parsing) : stdpp_scope.
+Infix "∪*" := (zip_with (∪)) (at level 50, left associativity) : stdpp_scope.
+Notation "(∪*)" := (zip_with (∪)) (only parsing) : stdpp_scope.
+
+Definition union_list `{Empty A} `{Union A} : list A → A := fold_right (∪) ∅.
+Global Arguments union_list _ _ _ !_ / : assert.
+Notation "⋃ l" := (union_list l) (at level 20, format "⋃ l") : stdpp_scope.
+
+Class Intersection A := intersection: A → A → A.
+Global Hint Mode Intersection ! : typeclass_instances.
+Global Instance: Params (@intersection) 2 := {}.
+Infix "∩" := intersection (at level 40) : stdpp_scope.
+Notation "(∩)" := intersection (only parsing) : stdpp_scope.
+Notation "( x ∩.)" := (intersection x) (only parsing) : stdpp_scope.
+Notation "(.∩ x )" := (λ y, intersection y x) (only parsing) : stdpp_scope.
+
+Class Difference A := difference: A → A → A.
+Global Hint Mode Difference ! : typeclass_instances.
+Global Instance: Params (@difference) 2 := {}.
+Infix "∖" := difference (at level 40, left associativity) : stdpp_scope.
+Notation "(∖)" := difference (only parsing) : stdpp_scope.
+Notation "( x ∖.)" := (difference x) (only parsing) : stdpp_scope.
+Notation "(.∖ x )" := (λ y, difference y x) (only parsing) : stdpp_scope.
+Infix "∖*" := (zip_with (∖)) (at level 40, left associativity) : stdpp_scope.
+Notation "(∖*)" := (zip_with (∖)) (only parsing) : stdpp_scope.
+
+Class Singleton A B := singleton: A → B.
+Global Hint Mode Singleton - ! : typeclass_instances.
+Global Instance: Params (@singleton) 3 := {}.
+Notation "{[ x ]}" := (singleton x) (at level 1) : stdpp_scope.
+Notation "{[ x ; y ; .. ; z ]}" :=
+ (union .. (union (singleton x) (singleton y)) .. (singleton z))
+ (at level 1) : stdpp_scope.
+
+Class SubsetEq A := subseteq: relation A.
+Global Hint Mode SubsetEq ! : typeclass_instances.
+Global Instance: Params (@subseteq) 2 := {}.
+Infix "⊆" := subseteq (at level 70) : stdpp_scope.
+Notation "(⊆)" := subseteq (only parsing) : stdpp_scope.
+Notation "( X ⊆.)" := (subseteq X) (only parsing) : stdpp_scope.
+Notation "(.⊆ X )" := (λ Y, Y ⊆ X) (only parsing) : stdpp_scope.
+Notation "X ⊈ Y" := (¬X ⊆ Y) (at level 70) : stdpp_scope.
+Notation "(⊈)" := (λ X Y, X ⊈ Y) (only parsing) : stdpp_scope.
+Notation "( X ⊈.)" := (λ Y, X ⊈ Y) (only parsing) : stdpp_scope.
+Notation "(.⊈ X )" := (λ Y, Y ⊈ X) (only parsing) : stdpp_scope.
+
+Infix "⊆@{ A }" := (@subseteq A _) (at level 70, only parsing) : stdpp_scope.
+Notation "(⊆@{ A } )" := (@subseteq A _) (only parsing) : stdpp_scope.
+
+Infix "⊆*" := (Forall2 (⊆)) (at level 70) : stdpp_scope.
+Notation "(⊆*)" := (Forall2 (⊆)) (only parsing) : stdpp_scope.
+
+Global Hint Extern 0 (_ ⊆ _) => reflexivity : core.
+Global Hint Extern 0 (_ ⊆* _) => reflexivity : core.
+
+Infix "⊂" := (strict (⊆)) (at level 70) : stdpp_scope.
+Notation "(⊂)" := (strict (⊆)) (only parsing) : stdpp_scope.
+Notation "( X ⊂.)" := (strict (⊆) X) (only parsing) : stdpp_scope.
+Notation "(.⊂ X )" := (λ Y, Y ⊂ X) (only parsing) : stdpp_scope.
+Notation "X ⊄ Y" := (¬X ⊂ Y) (at level 70) : stdpp_scope.
+Notation "(⊄)" := (λ X Y, X ⊄ Y) (only parsing) : stdpp_scope.
+Notation "( X ⊄.)" := (λ Y, X ⊄ Y) (only parsing) : stdpp_scope.
+Notation "(.⊄ X )" := (λ Y, Y ⊄ X) (only parsing) : stdpp_scope.
+
+Infix "⊂@{ A }" := (strict (⊆@{A})) (at level 70, only parsing) : stdpp_scope.
+Notation "(⊂@{ A } )" := (strict (⊆@{A})) (only parsing) : stdpp_scope.
+
+Notation "X ⊆ Y ⊆ Z" := (X ⊆ Y ∧ Y ⊆ Z) (at level 70, Y at next level) : stdpp_scope.
+Notation "X ⊆ Y ⊂ Z" := (X ⊆ Y ∧ Y ⊂ Z) (at level 70, Y at next level) : stdpp_scope.
+Notation "X ⊂ Y ⊆ Z" := (X ⊂ Y ∧ Y ⊆ Z) (at level 70, Y at next level) : stdpp_scope.
+Notation "X ⊂ Y ⊂ Z" := (X ⊂ Y ∧ Y ⊂ Z) (at level 70, Y at next level) : stdpp_scope.
+
+(** We define type classes for multisets: disjoint union [⊎] and the multiset
+singleton [{[+ _ +]}]. Multiset literals [{[+ x1; ..; xn +]}] are defined in
+terms of iterated disjoint union [{[+ x1 +]} ⊎ .. ⊎ {[+ xn +]}], and are thus
+different from set literals [{[ x1; ..; xn ]}], which use [∪].
+
+Note that in principle we could reuse the set singleton [{[ _ ]}] for multisets,
+and define [{[+ x1; ..; xn +]}] as [{[ x1 ]} ⊎ .. ⊎ {[ xn ]}]. However, this
+would risk accidentally using [{[ x1; ..; xn ]}] for multisets (leading to
+unexpected results) and lead to ambigious pretty printing for [{[+ x +]}]. *)
+Class DisjUnion A := disj_union: A → A → A.
+Global Hint Mode DisjUnion ! : typeclass_instances.
+Global Instance: Params (@disj_union) 2 := {}.
+Infix "⊎" := disj_union (at level 50, left associativity) : stdpp_scope.
+Notation "(⊎)" := disj_union (only parsing) : stdpp_scope.
+Notation "( x ⊎.)" := (disj_union x) (only parsing) : stdpp_scope.
+Notation "(.⊎ x )" := (λ y, disj_union y x) (only parsing) : stdpp_scope.
+
+Class SingletonMS A B := singletonMS: A → B.
+Global Hint Mode SingletonMS - ! : typeclass_instances.
+Global Instance: Params (@singletonMS) 3 := {}.
+Notation "{[+ x +]}" := (singletonMS x)
+ (at level 1, format "{[+ x +]}") : stdpp_scope.
+Notation "{[+ x ; y ; .. ; z +]}" :=
+ (disj_union .. (disj_union (singletonMS x) (singletonMS y)) .. (singletonMS z))
+ (at level 1, format "{[+ x ; y ; .. ; z +]}") : stdpp_scope.
+
+Definition option_to_set `{Singleton A C, Empty C} (mx : option A) : C :=
+ match mx with None => ∅ | Some x => {[ x ]} end.
+Fixpoint list_to_set `{Singleton A C, Empty C, Union C} (l : list A) : C :=
+ match l with [] => ∅ | x :: l => {[ x ]} ∪ list_to_set l end.
+Fixpoint list_to_set_disj `{SingletonMS A C, Empty C, DisjUnion C} (l : list A) : C :=
+ match l with [] => ∅ | x :: l => {[+ x +]} ⊎ list_to_set_disj l end.
+
+Class ScalarMul N A := scalar_mul : N → A → A.
+Global Hint Mode ScalarMul - ! : typeclass_instances.
+(** The [N] arguments is typically [nat] or [Z], so we do not want to rewrite
+in that. Hence, the value of [Params] is 3. *)
+Global Instance: Params (@scalar_mul) 3 := {}.
+(** The notation [*:] and level is taken from ssreflect, see
+https://github.com/math-comp/math-comp/blob/master/mathcomp/ssreflect/ssrnotations.v *)
+Infix "*:" := scalar_mul (at level 40) : stdpp_scope.
+Notation "(*:)" := scalar_mul (only parsing) : stdpp_scope.
+Notation "( x *:.)" := (scalar_mul x) (only parsing) : stdpp_scope.
+Notation "(.*: x )" := (λ y, scalar_mul y x) (only parsing) : stdpp_scope.
+
+(** The class [Lexico A] is used for the lexicographic order on [A]. This order
+is used to create finite maps, finite sets, etc, and is typically different from
+the order [(⊆)]. *)
+Class Lexico A := lexico: relation A.
+Global Hint Mode Lexico ! : typeclass_instances.
+
+Class ElemOf A B := elem_of: A → B → Prop.
+Global Hint Mode ElemOf - ! : typeclass_instances.
+Global Instance: Params (@elem_of) 3 := {}.
+Infix "∈" := elem_of (at level 70) : stdpp_scope.
+Notation "(∈)" := elem_of (only parsing) : stdpp_scope.
+Notation "( x ∈.)" := (elem_of x) (only parsing) : stdpp_scope.
+Notation "(.∈ X )" := (λ x, elem_of x X) (only parsing) : stdpp_scope.
+Notation "x ∉ X" := (¬x ∈ X) (at level 80) : stdpp_scope.
+Notation "(∉)" := (λ x X, x ∉ X) (only parsing) : stdpp_scope.
+Notation "( x ∉.)" := (λ X, x ∉ X) (only parsing) : stdpp_scope.
+Notation "(.∉ X )" := (λ x, x ∉ X) (only parsing) : stdpp_scope.
+
+Infix "∈@{ B }" := (@elem_of _ B _) (at level 70, only parsing) : stdpp_scope.
+Notation "(∈@{ B } )" := (@elem_of _ B _) (only parsing) : stdpp_scope.
+
+Notation "x ∉@{ B } X" := (¬x ∈@{B} X) (at level 80, only parsing) : stdpp_scope.
+Notation "(∉@{ B } )" := (λ x X, x ∉@{B} X) (only parsing) : stdpp_scope.
+
+Class Disjoint A := disjoint : A → A → Prop.
+Global Hint Mode Disjoint ! : typeclass_instances.
+Global Instance: Params (@disjoint) 2 := {}.
+Infix "##" := disjoint (at level 70) : stdpp_scope.
+Notation "(##)" := disjoint (only parsing) : stdpp_scope.
+Notation "( X ##.)" := (disjoint X) (only parsing) : stdpp_scope.
+Notation "(.## X )" := (λ Y, Y ## X) (only parsing) : stdpp_scope.
+
+Infix "##@{ A }" := (@disjoint A _) (at level 70, only parsing) : stdpp_scope.
+Notation "(##@{ A } )" := (@disjoint A _) (only parsing) : stdpp_scope.
+
+Infix "##*" := (Forall2 (##)) (at level 70) : stdpp_scope.
+Notation "(##*)" := (Forall2 (##)) (only parsing) : stdpp_scope.
+
+Global Hint Extern 0 (_ ## _) => symmetry; eassumption : core.
+Global Hint Extern 0 (_ ##* _) => symmetry; eassumption : core.
+
+Class Filter A B := filter: ∀ (P : A → Prop) `{∀ x, Decision (P x)}, B → B.
+Global Hint Mode Filter - ! : typeclass_instances.
+
+Class UpClose A B := up_close : A → B.
+Global Hint Mode UpClose - ! : typeclass_instances.
+Notation "↑ x" := (up_close x) (at level 20, format "↑ x").
+
+(** * Monadic operations *)
+(** We define operational type classes for the monadic operations bind, join
+and fmap. We use these type classes merely for convenient overloading of
+notations and do not formalize any theory on monads (we do not even define a
+class with the monad laws). *)
+Class MRet (M : Type → Type) := mret: ∀ {A}, A → M A.
+Global Arguments mret {_ _ _} _ : assert.
+Global Instance: Params (@mret) 3 := {}.
+Global Hint Mode MRet ! : typeclass_instances.
+
+Class MBind (M : Type → Type) := mbind : ∀ {A B}, (A → M B) → M A → M B.
+Global Arguments mbind {_ _ _ _} _ !_ / : assert.
+Global Instance: Params (@mbind) 4 := {}.
+Global Hint Mode MBind ! : typeclass_instances.
+
+Class MJoin (M : Type → Type) := mjoin: ∀ {A}, M (M A) → M A.
+Global Arguments mjoin {_ _ _} !_ / : assert.
+Global Instance: Params (@mjoin) 3 := {}.
+Global Hint Mode MJoin ! : typeclass_instances.
+
+Class FMap (M : Type → Type) := fmap : ∀ {A B}, (A → B) → M A → M B.
+Global Arguments fmap {_ _ _ _} _ !_ / : assert.
+Global Instance: Params (@fmap) 4 := {}.
+Global Hint Mode FMap ! : typeclass_instances.
+
+Class OMap (M : Type → Type) := omap: ∀ {A B}, (A → option B) → M A → M B.
+Global Arguments omap {_ _ _ _} _ !_ / : assert.
+Global Instance: Params (@omap) 4 := {}.
+Global Hint Mode OMap ! : typeclass_instances.
+
+Notation "m ≫= f" := (mbind f m) (at level 60, right associativity) : stdpp_scope.
+Notation "( m ≫=.)" := (λ f, mbind f m) (only parsing) : stdpp_scope.
+Notation "(.≫= f )" := (mbind f) (only parsing) : stdpp_scope.
+Notation "(≫=)" := (λ m f, mbind f m) (only parsing) : stdpp_scope.
+
+Notation "x ← y ; z" := (y ≫= (λ x : _, z))
+ (at level 20, y at level 100, z at level 200, only parsing) : stdpp_scope.
+
+Notation "' x ← y ; z" := (y ≫= (λ x : _, z))
+ (at level 20, x pattern, y at level 100, z at level 200, only parsing) : stdpp_scope.
+
+Infix "<$>" := fmap (at level 61, left associativity) : stdpp_scope.
+
+Notation "x ;; z" := (x ≫= λ _, z)
+ (at level 100, z at level 200, only parsing, right associativity): stdpp_scope.
+
+Notation "ps .*1" := (fmap (M:=list) fst ps)
+ (at level 2, left associativity, format "ps .*1").
+Notation "ps .*2" := (fmap (M:=list) snd ps)
+ (at level 2, left associativity, format "ps .*2").
+
+(** For any monad that has a builtin way to throw an exception/error *)
+Class MThrow (E : Type) (M : Type → Type) := mthrow : ∀ {A}, E → M A.
+Global Arguments mthrow {_ _ _ _} _ : assert.
+Global Instance: Params (@mthrow) 4 := {}.
+Global Hint Mode MThrow ! ! : typeclass_instances.
+
+(** We use unit as the error content for monads that can only report an error
+ without any payload like an option *)
+Global Notation MFail := (MThrow ()).
+Global Notation mfail := (mthrow ()).
+
+Definition guard_or {E} (e : E) `{MThrow E M, MRet M} P `{Decision P} : M P :=
+ match decide P with
+ | left H => mret H
+ | right _ => mthrow e
+ end.
+Global Notation guard := (guard_or ()).
+
+
+(** * Operations on maps *)
+(** In this section we define operational type classes for the operations
+on maps. In the file [fin_maps] we will axiomatize finite maps.
+The function look up [m !! k] should yield the element at key [k] in [m]. *)
+Class Lookup (K A M : Type) := lookup: K → M → option A.
+Global Hint Mode Lookup - - ! : typeclass_instances.
+Global Instance: Params (@lookup) 5 := {}.
+Notation "m !! i" := (lookup i m) (at level 20) : stdpp_scope.
+Notation "(!!)" := lookup (only parsing) : stdpp_scope.
+Notation "( m !!.)" := (λ i, m !! i) (only parsing) : stdpp_scope.
+Notation "(.!! i )" := (lookup i) (only parsing) : stdpp_scope.
+Global Arguments lookup _ _ _ _ !_ !_ / : simpl nomatch, assert.
+
+(** The function [lookup_total] should be the total over-approximation
+of the partial [lookup] function. *)
+Class LookupTotal (K A M : Type) := lookup_total : K → M → A.
+Global Hint Mode LookupTotal - - ! : typeclass_instances.
+Global Instance: Params (@lookup_total) 5 := {}.
+Notation "m !!! i" := (lookup_total i m) (at level 20) : stdpp_scope.
+Notation "(!!!)" := lookup_total (only parsing) : stdpp_scope.
+Notation "( m !!!.)" := (λ i, m !!! i) (only parsing) : stdpp_scope.
+Notation "(.!!! i )" := (lookup_total i) (only parsing) : stdpp_scope.
+Global Arguments lookup_total _ _ _ _ !_ !_ / : simpl nomatch, assert.
+
+(** The singleton map *)
+Class SingletonM K A M := singletonM: K → A → M.
+Global Hint Mode SingletonM - - ! : typeclass_instances.
+Global Instance: Params (@singletonM) 5 := {}.
+Notation "{[ k := a ]}" := (singletonM k a) (at level 1) : stdpp_scope.
+
+(** The function insert [<[k:=a]>m] should update the element at key [k] with
+value [a] in [m]. *)
+Class Insert (K A M : Type) := insert: K → A → M → M.
+Global Hint Mode Insert - - ! : typeclass_instances.
+Global Instance: Params (@insert) 5 := {}.
+Notation "<[ k := a ]>" := (insert k a)
+ (at level 5, right associativity, format "<[ k := a ]>") : stdpp_scope.
+Global Arguments insert _ _ _ _ !_ _ !_ / : simpl nomatch, assert.
+
+(** Notation for more elements (up to 13) *)
+(* Defining a generic notation does not seem possible with Coq's
+ recursive notation system, so we define individual notations
+ for some cases relevant in practice. *)
+(* The "format" makes sure that linebreaks are placed after the separating semicola [;] when printing. *)
+(* TODO : we are using parantheses in the "de-sugaring" of the notation instead of [$] because Coq 8.12
+ and earlier have trouble with using the notation for printing otherwise.
+ Once support for Coq 8.12 is dropped, this can be replaced with [$]. *)
+Notation "{[ k1 := a1 ; k2 := a2 ]}" :=
+ (<[ k1 := a1 ]>{[ k2 := a2 ]})
+ (at level 1, format
+ "{[ '[hv' '[' k1 := a1 ; ']' '/' '[' k2 := a2 ']' ']' ]}") : stdpp_scope.
+Notation "{[ k1 := a1 ; k2 := a2 ; k3 := a3 ]}" :=
+ (<[ k1 := a1 ]> ( <[ k2 := a2 ]>{[ k3 := a3 ]}))
+ (at level 1, format
+ "{[ '[hv' '[' k1 := a1 ; ']' '/' '[' k2 := a2 ; ']' '/' '[' k3 := a3 ']' ']' ]}") : stdpp_scope.
+Notation "{[ k1 := a1 ; k2 := a2 ; k3 := a3 ; k4 := a4 ]}" :=
+ (<[ k1 := a1 ]> ( <[ k2 := a2 ]> ( <[ k3 := a3 ]>{[ k4 := a4 ]})))
+ (at level 1, format
+ "{[ '[hv' '[' k1 := a1 ; ']' '/' '[' k2 := a2 ; ']' '/' '[' k3 := a3 ; ']' '/' '[' k4 := a4 ']' ']' ]}") : stdpp_scope.
+Notation "{[ k1 := a1 ; k2 := a2 ; k3 := a3 ; k4 := a4 ; k5 := a5 ]}" :=
+ (<[ k1 := a1 ]> ( <[ k2 := a2 ]> ( <[ k3 := a3 ]> ( <[ k4 := a4 ]>{[ k5 := a5 ]}))))
+ (at level 1, format
+ "{[ '[hv' '[' k1 := a1 ; ']' '/' '[' k2 := a2 ; ']' '/' '[' k3 := a3 ; ']' '/' '[' k4 := a4 ; ']' '/' '[' k5 := a5 ']' ']' ]}") : stdpp_scope.
+Notation "{[ k1 := a1 ; k2 := a2 ; k3 := a3 ; k4 := a4 ; k5 := a5 ; k6 := a6 ]}" :=
+ (<[ k1 := a1 ]> ( <[ k2 := a2 ]> ( <[ k3 := a3 ]> ( <[ k4 := a4 ]> (
+ <[ k5 := a5 ]>{[ k6 := a6 ]})))))
+ (at level 1, format
+ "{[ '[hv' '[' k1 := a1 ; ']' '/' '[' k2 := a2 ; ']' '/' '[' k3 := a3 ; ']' '/' '[' k4 := a4 ; ']' '/' '[' k5 := a5 ; ']' '/' '[' k6 := a6 ']' ']' ]}") : stdpp_scope.
+Notation "{[ k1 := a1 ; k2 := a2 ; k3 := a3 ; k4 := a4 ; k5 := a5 ; k6 := a6 ; k7 := a7 ]}" :=
+ (<[ k1 := a1 ]> ( <[ k2 := a2 ]> ( <[ k3 := a3 ]> ( <[ k4 := a4 ]> (
+ <[ k5 := a5 ]> ( <[ k6 := a6 ]>{[ k7 := a7 ]}))))))
+ (at level 1, format
+ "{[ '[hv' '[' k1 := a1 ; ']' '/' '[' k2 := a2 ; ']' '/' '[' k3 := a3 ; ']' '/' '[' k4 := a4 ; ']' '/' '[' k5 := a5 ; ']' '/' '[' k6 := a6 ; ']' '/' '[' k7 := a7 ']' ']' ]}") : stdpp_scope.
+Notation "{[ k1 := a1 ; k2 := a2 ; k3 := a3 ; k4 := a4 ; k5 := a5 ; k6 := a6 ; k7 := a7 ; k8 := a8 ]}" :=
+ (<[ k1 := a1 ]> ( <[ k2 := a2 ]> ( <[ k3 := a3 ]> ( <[ k4 := a4 ]> (
+ <[ k5 := a5 ]> ( <[ k6 := a6 ]> ( <[ k7 := a7 ]>{[ k8 := a8 ]})))))))
+ (at level 1, format
+ "{[ '[hv' '[' k1 := a1 ; ']' '/' '[' k2 := a2 ; ']' '/' '[' k3 := a3 ; ']' '/' '[' k4 := a4 ; ']' '/' '[' k5 := a5 ; ']' '/' '[' k6 := a6 ; ']' '/' '[' k7 := a7 ; ']' '/' '[' k8 := a8 ']' ']' ]}") : stdpp_scope.
+Notation "{[ k1 := a1 ; k2 := a2 ; k3 := a3 ; k4 := a4 ; k5 := a5 ; k6 := a6 ; k7 := a7 ; k8 := a8 ; k9 := a9 ]}" :=
+ (<[ k1 := a1 ]> ( <[ k2 := a2 ]> ( <[ k3 := a3 ]> ( <[ k4 := a4 ]> (
+ <[ k5 := a5 ]> ( <[ k6 := a6 ]> ( <[ k7 := a7 ]> ( <[ k8 := a8 ]>{[ k9 := a9 ]}))))))))
+ (at level 1, format
+ "{[ '[hv' '[' k1 := a1 ; ']' '/' '[' k2 := a2 ; ']' '/' '[' k3 := a3 ; ']' '/' '[' k4 := a4 ; ']' '/' '[' k5 := a5 ; ']' '/' '[' k6 := a6 ; ']' '/' '[' k7 := a7 ; ']' '/' '[' k8 := a8 ; ']' '/' '[' k9 := a9 ']' ']' ]}") : stdpp_scope.
+Notation "{[ k1 := a1 ; k2 := a2 ; k3 := a3 ; k4 := a4 ; k5 := a5 ; k6 := a6 ; k7 := a7 ; k8 := a8 ; k9 := a9 ; k10 := a10 ]}" :=
+ (<[ k1 := a1 ]> ( <[ k2 := a2 ]> ( <[ k3 := a3 ]> ( <[ k4 := a4 ]> (
+ <[ k5 := a5 ]> ( <[ k6 := a6 ]> ( <[ k7 := a7 ]> ( <[ k8 := a8 ]> (
+ <[ k9 := a9 ]>{[ k10 := a10 ]})))))))))
+ (at level 1, format
+ "{[ '[hv' '[' k1 := a1 ; ']' '/' '[' k2 := a2 ; ']' '/' '[' k3 := a3 ; ']' '/' '[' k4 := a4 ; ']' '/' '[' k5 := a5 ; ']' '/' '[' k6 := a6 ; ']' '/' '[' k7 := a7 ; ']' '/' '[' k8 := a8 ; ']' '/' '[' k9 := a9 ; ']' '/' '[' k10 := a10 ']' ']' ]}") : stdpp_scope.
+Notation "{[ k1 := a1 ; k2 := a2 ; k3 := a3 ; k4 := a4 ; k5 := a5 ; k6 := a6 ; k7 := a7 ; k8 := a8 ; k9 := a9 ; k10 := a10 ; k11 := a11 ]}" :=
+ (<[ k1 := a1 ]> ( <[ k2 := a2 ]> ( <[ k3 := a3 ]> ( <[ k4 := a4 ]> (
+ <[ k5 := a5 ]> ( <[ k6 := a6 ]> ( <[ k7 := a7 ]> ( <[ k8 := a8 ]> (
+ <[ k9 := a9 ]> ( <[ k10 := a10 ]>{[ k11 := a11 ]}))))))))))
+ (at level 1, format
+ "{[ '[hv' '[' k1 := a1 ; ']' '/' '[' k2 := a2 ; ']' '/' '[' k3 := a3 ; ']' '/' '[' k4 := a4 ; ']' '/' '[' k5 := a5 ; ']' '/' '[' k6 := a6 ; ']' '/' '[' k7 := a7 ; ']' '/' '[' k8 := a8 ; ']' '/' '[' k9 := a9 ; ']' '/' '[' k10 := a10 ; ']' '/' '[' k11 := a11 ']' ']' ]}") : stdpp_scope.
+Notation "{[ k1 := a1 ; k2 := a2 ; k3 := a3 ; k4 := a4 ; k5 := a5 ; k6 := a6 ; k7 := a7 ; k8 := a8 ; k9 := a9 ; k10 := a10 ; k11 := a11 ; k12 := a12 ]}" :=
+ (<[ k1 := a1 ]> ( <[ k2 := a2 ]> ( <[ k3 := a3 ]> ( <[ k4 := a4 ]> (
+ <[ k5 := a5 ]> ( <[ k6 := a6 ]> ( <[ k7 := a7 ]> ( <[ k8 := a8 ]> (
+ <[ k9 := a9 ]> ( <[ k10 := a10 ]> ( <[ k11 := a11 ]>{[ k12 := a12 ]})))))))))))
+ (at level 1, format
+ "{[ '[hv' '[' k1 := a1 ; ']' '/' '[' k2 := a2 ; ']' '/' '[' k3 := a3 ; ']' '/' '[' k4 := a4 ; ']' '/' '[' k5 := a5 ; ']' '/' '[' k6 := a6 ; ']' '/' '[' k7 := a7 ; ']' '/' '[' k8 := a8 ; ']' '/' '[' k9 := a9 ; ']' '/' '[' k10 := a10 ; ']' '/' '[' k11 := a11 ; ']' '/' '[' k12 := a12 ']' ']' ]}") : stdpp_scope.
+Notation "{[ k1 := a1 ; k2 := a2 ; k3 := a3 ; k4 := a4 ; k5 := a5 ; k6 := a6 ; k7 := a7 ; k8 := a8 ; k9 := a9 ; k10 := a10 ; k11 := a11 ; k12 := a12 ; k13 := a13 ]}" :=
+ (<[ k1 := a1 ]> ( <[ k2 := a2 ]> ( <[ k3 := a3 ]> ( <[ k4 := a4 ]> (
+ <[ k5 := a5 ]> ( <[ k6 := a6 ]> ( <[ k7 := a7 ]> ( <[ k8 := a8 ]> (
+ <[ k9 := a9 ]> ( <[ k10 := a10 ]> ( <[ k11 := a11 ]> ( <[ k12 := a12 ]>{[ k13 := a13 ]}))))))))))))
+ (at level 1, format
+ "{[ '[hv' '[' k1 := a1 ; ']' '/' '[' k2 := a2 ; ']' '/' '[' k3 := a3 ; ']' '/' '[' k4 := a4 ; ']' '/' '[' k5 := a5 ; ']' '/' '[' k6 := a6 ; ']' '/' '[' k7 := a7 ; ']' '/' '[' k8 := a8 ; ']' '/' '[' k9 := a9 ; ']' '/' '[' k10 := a10 ; ']' '/' '[' k11 := a11 ; ']' '/' '[' k12 := a12 ; ']' '/' '[' k13 := a13 ']' ']' ]}") : stdpp_scope.
+
+(** The function delete [delete k m] should delete the value at key [k] in
+[m]. If the key [k] is not a member of [m], the original map should be
+returned. *)
+Class Delete (K M : Type) := delete: K → M → M.
+Global Hint Mode Delete - ! : typeclass_instances.
+Global Instance: Params (@delete) 4 := {}.
+Global Arguments delete _ _ _ !_ !_ / : simpl nomatch, assert.
+
+(** The function [alter f k m] should update the value at key [k] using the
+function [f], which is called with the original value. *)
+Class Alter (K A M : Type) := alter: (A → A) → K → M → M.
+Global Hint Mode Alter - - ! : typeclass_instances.
+Global Instance: Params (@alter) 4 := {}.
+Global Arguments alter {_ _ _ _} _ !_ !_ / : simpl nomatch, assert.
+
+(** The function [partial_alter f k m] should update the value at key [k] using the
+function [f], which is called with the original value at key [k] or [None]
+if [k] is not a member of [m]. The value at [k] should be deleted if [f]
+yields [None]. *)
+Class PartialAlter (K A M : Type) :=
+ partial_alter: (option A → option A) → K → M → M.
+Global Hint Mode PartialAlter - - ! : typeclass_instances.
+Global Instance: Params (@partial_alter) 4 := {}.
+Global Arguments partial_alter _ _ _ _ _ !_ !_ / : simpl nomatch, assert.
+
+(** The function [dom m] should yield the domain of [m]. That is a finite
+set of type [D] that contains the keys that are a member of [m].
+[D] is an output of the typeclass, i.e., there can be only one instance per map
+type [M]. *)
+Class Dom (M D : Type) := dom: M → D.
+Global Hint Mode Dom ! - : typeclass_instances.
+Global Instance: Params (@dom) 3 := {}.
+Global Arguments dom : clear implicits.
+Global Arguments dom {_ _ _} !_ / : simpl nomatch, assert.
+
+(** The function [merge f m1 m2] should merge the maps [m1] and [m2] by
+constructing a new map whose value at key [k] is [f (m1 !! k) (m2 !! k)].*)
+Class Merge (M : Type → Type) :=
+ merge: ∀ {A B C}, (option A → option B → option C) → M A → M B → M C.
+Global Hint Mode Merge ! : typeclass_instances.
+Global Instance: Params (@merge) 4 := {}.
+Global Arguments merge _ _ _ _ _ _ !_ !_ / : simpl nomatch, assert.
+
+(** The function [union_with f m1 m2] is supposed to yield the union of [m1]
+and [m2] using the function [f] to combine values of members that are in
+both [m1] and [m2]. *)
+Class UnionWith (A M : Type) :=
+ union_with: (A → A → option A) → M → M → M.
+Global Hint Mode UnionWith - ! : typeclass_instances.
+Global Instance: Params (@union_with) 3 := {}.
+Global Arguments union_with {_ _ _} _ !_ !_ / : simpl nomatch, assert.
+
+(** Similarly for intersection and difference. *)
+Class IntersectionWith (A M : Type) :=
+ intersection_with: (A → A → option A) → M → M → M.
+Global Hint Mode IntersectionWith - ! : typeclass_instances.
+Global Instance: Params (@intersection_with) 3 := {}.
+Global Arguments intersection_with {_ _ _} _ !_ !_ / : simpl nomatch, assert.
+
+Class DifferenceWith (A M : Type) :=
+ difference_with: (A → A → option A) → M → M → M.
+Global Hint Mode DifferenceWith - ! : typeclass_instances.
+Global Instance: Params (@difference_with) 3 := {}.
+Global Arguments difference_with {_ _ _} _ !_ !_ / : simpl nomatch, assert.
+
+Definition intersection_with_list `{IntersectionWith A M}
+ (f : A → A → option A) : M → list M → M := fold_right (intersection_with f).
+Global Arguments intersection_with_list _ _ _ _ _ !_ / : assert.
+
+(** * Notations for lattices. *)
+(** SqSubsetEq registers the "canonical" partial order for a type, and is used
+for the \sqsubseteq symbol. *)
+Class SqSubsetEq A := sqsubseteq: relation A.
+Global Hint Mode SqSubsetEq ! : typeclass_instances.
+Global Instance: Params (@sqsubseteq) 2 := {}.
+Infix "⊑" := sqsubseteq (at level 70) : stdpp_scope.
+Notation "(⊑)" := sqsubseteq (only parsing) : stdpp_scope.
+Notation "( x ⊑.)" := (sqsubseteq x) (only parsing) : stdpp_scope.
+Notation "(.⊑ y )" := (λ x, sqsubseteq x y) (only parsing) : stdpp_scope.
+
+Infix "⊑@{ A }" := (@sqsubseteq A _) (at level 70, only parsing) : stdpp_scope.
+Notation "(⊑@{ A } )" := (@sqsubseteq A _) (only parsing) : stdpp_scope.
+
+(** [sqsubseteq] does not take precedence over the stdlib's instances (like [eq],
+[impl], [iff]) or std++'s [equiv].
+We have [eq] (at 100) < [≡] (at 150) < [⊑] (at 200). *)
+Global Instance sqsubseteq_rewrite `{SqSubsetEq A} : RewriteRelation (⊑@{A}) | 200 := {}.
+
+Global Hint Extern 0 (_ ⊑ _) => reflexivity : core.
+
+Class Meet A := meet: A → A → A.
+Global Hint Mode Meet ! : typeclass_instances.
+Global Instance: Params (@meet) 2 := {}.
+Infix "⊓" := meet (at level 40) : stdpp_scope.
+Notation "(⊓)" := meet (only parsing) : stdpp_scope.
+Notation "( x ⊓.)" := (meet x) (only parsing) : stdpp_scope.
+Notation "(.⊓ y )" := (λ x, meet x y) (only parsing) : stdpp_scope.
+
+Class Join A := join: A → A → A.
+Global Hint Mode Join ! : typeclass_instances.
+Global Instance: Params (@join) 2 := {}.
+Infix "⊔" := join (at level 50) : stdpp_scope.
+Notation "(⊔)" := join (only parsing) : stdpp_scope.
+Notation "( x ⊔.)" := (join x) (only parsing) : stdpp_scope.
+Notation "(.⊔ y )" := (λ x, join x y) (only parsing) : stdpp_scope.
+
+Class Top A := top : A.
+Global Hint Mode Top ! : typeclass_instances.
+Notation "⊤" := top (format "⊤") : stdpp_scope.
+
+Class Bottom A := bottom : A.
+Global Hint Mode Bottom ! : typeclass_instances.
+Notation "⊥" := bottom (format "⊥") : stdpp_scope.
+
+
+(** * Axiomatization of sets *)
+(** The classes [SemiSet A C], [Set_ A C], and [TopSet A C] axiomatize sets of
+type [C] with elements of type [A]. The first class, [SemiSet] does not include
+intersection and difference. It is useful for the case of lists, where decidable
+equality is needed to implement intersection and difference, but not union.
+
+Note that we cannot use the name [Set] since that is a reserved keyword. Hence
+we use [Set_]. *)
+Class SemiSet A C `{ElemOf A C,
+ Empty C, Singleton A C, Union C} : Prop := {
+ not_elem_of_empty (x : A) : x ∉@{C} ∅; (* We prove
+ [elem_of_empty : x ∈@{C} ∅ ↔ False] in [sets.v], which is more convenient for
+ rewriting. *)
+ elem_of_singleton (x y : A) : x ∈@{C} {[ y ]} ↔ x = y;
+ elem_of_union (X Y : C) (x : A) : x ∈ X ∪ Y ↔ x ∈ X ∨ x ∈ Y
+}.
+Global Hint Mode SemiSet - ! - - - - : typeclass_instances.
+
+Class Set_ A C `{ElemOf A C, Empty C, Singleton A C,
+ Union C, Intersection C, Difference C} : Prop := {
+ set_semi_set :> SemiSet A C;
+ elem_of_intersection (X Y : C) (x : A) : x ∈ X ∩ Y ↔ x ∈ X ∧ x ∈ Y;
+ elem_of_difference (X Y : C) (x : A) : x ∈ X ∖ Y ↔ x ∈ X ∧ x ∉ Y
+}.
+Global Hint Mode Set_ - ! - - - - - - : typeclass_instances.
+
+Class TopSet A C `{ElemOf A C, Empty C, Top C, Singleton A C,
+ Union C, Intersection C, Difference C} : Prop := {
+ top_set_set :> Set_ A C;
+ elem_of_top' (x : A) : x ∈@{C} ⊤; (* We prove [elem_of_top : x ∈@{C} ⊤ ↔ True]
+ in [sets.v], which is more convenient for rewriting. *)
+}.
+Global Hint Mode TopSet - ! - - - - - - - : typeclass_instances.
+
+(** We axiomative a finite set as a set whose elements can be
+enumerated as a list. These elements, given by the [elements] function, may be
+in any order and should not contain duplicates. *)
+Class Elements A C := elements: C → list A.
+Global Hint Mode Elements - ! : typeclass_instances.
+Global Instance: Params (@elements) 3 := {}.
+
+(** We redefine the standard library's [In] and [NoDup] using type classes. *)
+Inductive elem_of_list {A} : ElemOf A (list A) :=
+ | elem_of_list_here (x : A) l : x ∈ x :: l
+ | elem_of_list_further (x y : A) l : x ∈ l → x ∈ y :: l.
+Global Existing Instance elem_of_list.
+
+Lemma elem_of_list_In {A} (l : list A) x : x ∈ l ↔ In x l.
+Proof.
+ split.
+ - induction 1; simpl; auto.
+ - induction l; destruct 1; subst; constructor; auto.
+Qed.
+
+Inductive NoDup {A} : list A → Prop :=
+ | NoDup_nil_2 : NoDup []
+ | NoDup_cons_2 x l : x ∉ l → NoDup l → NoDup (x :: l).
+
+Lemma NoDup_ListNoDup {A} (l : list A) : NoDup l ↔ List.NoDup l.
+Proof.
+ split.
+ - induction 1; constructor; rewrite <-?elem_of_list_In; auto.
+ - induction 1; constructor; rewrite ?elem_of_list_In; auto.
+Qed.
+
+(** Decidability of equality of the carrier set is admissible, but we add it
+anyway so as to avoid cycles in type class search. *)
+Class FinSet A C `{ElemOf A C, Empty C, Singleton A C, Union C,
+ Intersection C, Difference C, Elements A C, EqDecision A} : Prop := {
+ fin_set_set :> Set_ A C;
+ elem_of_elements (X : C) x : x ∈ elements X ↔ x ∈ X;
+ NoDup_elements (X : C) : NoDup (elements X)
+}.
+Global Hint Mode FinSet - ! - - - - - - - - : typeclass_instances.
+
+Class Size C := size: C → nat.
+Global Hint Mode Size ! : typeclass_instances.
+Global Arguments size {_ _} !_ / : simpl nomatch, assert.
+Global Instance: Params (@size) 2 := {}.
+
+(** The class [MonadSet M] axiomatizes a type constructor [M] that can be
+used to construct a set [M A] with elements of type [A]. The advantage
+of this class, compared to [Set_], is that it also axiomatizes the
+the monadic operations. The disadvantage is that not many inhabitants are
+possible: we will only provide as inhabitants [propset] and [listset], which are
+represented respectively using Boolean functions and lists with duplicates.
+
+More interesting implementations typically need
+decidable equality, or a total order on the elements, which do not fit
+in a type constructor of type [Type → Type]. *)
+Class MonadSet M `{∀ A, ElemOf A (M A),
+ ∀ A, Empty (M A), ∀ A, Singleton A (M A), ∀ A, Union (M A),
+ !MBind M, !MRet M, !FMap M, !MJoin M} : Prop := {
+ monad_set_semi_set A :> SemiSet A (M A);
+ elem_of_bind {A B} (f : A → M B) (X : M A) (x : B) :
+ x ∈ X ≫= f ↔ ∃ y, x ∈ f y ∧ y ∈ X;
+ elem_of_ret {A} (x y : A) : x ∈@{M A} mret y ↔ x = y;
+ elem_of_fmap {A B} (f : A → B) (X : M A) (x : B) :
+ x ∈ f <$> X ↔ ∃ y, x = f y ∧ y ∈ X;
+ elem_of_join {A} (X : M (M A)) (x : A) :
+ x ∈ mjoin X ↔ ∃ Y : M A, x ∈ Y ∧ Y ∈ X
+}.
+
+(** The [Infinite A] class axiomatizes types [A] with infinitely many elements.
+It contains a function [fresh : list A → A] that given a list [xs] gives an
+element [fresh xs ∉ xs].
+
+We do not directly make [fresh] a field of the [Infinite] class, but use a
+separate operational type class [Fresh] for it. That way we can overload [fresh]
+to pick fresh elements from other data structure like sets. See the file
+[fin_sets], where we define [fresh : C → A] for any finite set implementation
+[FinSet C A].
+
+Note: we require [fresh] to respect permutations, which is needed to define the
+aforementioned [fresh] function on finite sets that respects set equality.
+
+Instead of instantiating [Infinite] directly, consider using [max_infinite] or
+[inj_infinite] from the [infinite] module. *)
+Class Fresh A C := fresh: C → A.
+Global Hint Mode Fresh - ! : typeclass_instances.
+Global Instance: Params (@fresh) 3 := {}.
+Global Arguments fresh : simpl never.
+
+Class Infinite A := {
+ infinite_fresh :> Fresh A (list A);
+ infinite_is_fresh (xs : list A) : fresh xs ∉ xs;
+ infinite_fresh_Permutation :> Proper (@Permutation A ==> (=)) fresh;
+}.
+Global Hint Mode Infinite ! : typeclass_instances.
+Global Arguments infinite_fresh : simpl never.
+
+(** * Miscellaneous *)
+Class Half A := half: A → A.
+Global Hint Mode Half ! : typeclass_instances.
+Notation "½" := half (format "½") : stdpp_scope.
+Notation "½*" := (fmap (M:=list) half) : stdpp_scope.
diff --git a/apps/tc/tests/compile_add_pred.v b/apps/tc/tests/compile_add_pred.v
new file mode 100644
index 000000000..6c56855dd
--- /dev/null
+++ b/apps/tc/tests/compile_add_pred.v
@@ -0,0 +1,127 @@
+From elpi Require Import elpi.
+
+Elpi Db tc.db lp:{{
+ pred classes i:gref.
+
+ pred bool->mode-term i:bool, o:string.
+ bool->mode-term tt "i:term".
+ bool->mode-term ff "o:term".
+
+ pred modes->string i:list bool, o:string.
+ modes->string L S :-
+ std.map L bool->mode-term L',
+ std.string.concat "," L' S.
+
+ pred list-init i:int, i:(int -> A -> prop), o:list A.
+ list-init N _ _ :- N < 0, std.fatal-error "list-init negative length".
+ list-init 0 _ [] :- !.
+ list-init N F [A | TL] :-
+ F N A, N1 is N - 1, list-init N1 F TL.
+
+ pred fail->bool i:prop, o:bool.
+ fail->bool P ff :- P, !.
+ fail->bool _ tt.
+
+ pred make-tc-modes i:int, o:string.
+ make-tc-modes NB_args ModesStr :-
+ list-init NB_args (x\r\ fail->bool (x = 1) r) ModesBool,
+ modes->string ModesBool ModesStr.
+
+ pred gref->pred-name i:gref, o:string.
+ gref->pred-name Gr S :-
+ coq.gref->id Gr S',
+ S is "tc-" ^ S'.
+
+ pred add-tc-pred i:gref, i:int.
+ add-tc-pred Gr NbArgs :-
+ not (classes Gr),
+ make-tc-modes NbArgs Modes,
+ gref->pred-name Gr GrStr,
+ D is "pred " ^ GrStr ^ " " ^ Modes ^ ".",
+ coq.elpi.add-predicate "tc.db" D,
+ coq.elpi.accumulate _ "tc.db" (clause _ _ (classes Gr)).
+ add-tc-pred _ _.
+
+ pred make-tc i:term, i:term, i:list prop, o:prop.
+ make-tc Ty Inst Hyp Clause :-
+ app [global TC | TL] = Ty,
+ gref->pred-name TC TC_Str,
+ std.append TL [Inst] Args,
+ std.length Args ArgsLen,
+ add-tc-pred TC ArgsLen,
+ coq.elpi.predicate TC_Str Args Q,
+ Clause = (Q :- Hyp).
+
+ pred app-has-class i:term, o:gref.
+ app-has-class (prod _ _ T) C :- pi x\ app-has-class (T x) C.
+ app-has-class (app [global T|_]) T :- coq.TC.class? T.
+
+ pred compile i:term, i:term, i:list prop, i:list term, o:prop.
+ compile (prod _ T F) I ListRHS ListVar (pi x\ C x) :- !,
+ pi p cond\ sigma Clause L\
+ if (app-has-class T _) (compile T p [] [] Clause, L = [Clause | ListRHS]) (L = ListRHS),
+ compile (F p) I L [p | ListVar] (C p).
+ compile Ty I Premises ListVar Clause :- !,
+ std.rev Premises PremisesRev,
+ coq.mk-app I {std.rev ListVar} AppInst,
+ make-tc Ty AppInst PremisesRev Clause.
+}}.
+
+Elpi Command addClass.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate lp:{{
+ main [str TC_Name] :-
+ coq.locate TC_Name TC_Gr,
+ coq.env.typeof TC_Gr TC_Ty,
+ coq.count-prods TC_Ty N',
+ N is N' + 1, % Plus one for the solution
+ add-tc-pred TC_Gr N.
+}}.
+Elpi Typecheck.
+
+Elpi Command compile.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate lp:{{
+ main [str InstName] :-
+ coq.locate InstName InstGr,
+ coq.env.typeof InstGr InstTy,
+ compile InstTy (global InstGr) [] [] Cl,
+ coq.say Cl,
+ coq.elpi.accumulate _ "tc.db" (clause _ _ Cl).
+}}.
+Elpi Typecheck.
+
+Elpi Tactic solver.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate lp:{{
+ msolve L N :- !,
+ coq.ltac.all (coq.ltac.open solve) {std.rev L} N.
+
+ solve (goal _ _ Ty Sol _ as G) GL :-
+ var Sol,
+ Ty = app [global TC | TL'],
+ std.append TL' [X] TL,
+ if (coq.elpi.predicate {gref->pred-name TC} TL Q, Q)
+ (
+ refine X G GL;
+ coq.say "illtyped solution:" {coq.term->string X}
+ )
+ (GL = [seal G]).
+}}.
+Elpi Typecheck.
+
+Class EqSimpl (T : Type) := {eqb : T -> T -> bool}.
+
+Global Instance EqU : EqSimpl unit := { eqb A B := true }.
+Global Instance EqP {A B: Type} `(EqSimpl A, EqSimpl B) : EqSimpl (A * B) := { eqb A B := true }.
+
+Elpi addClass EqSimpl.
+Elpi compile EqU.
+Elpi compile EqP.
+
+Elpi Override TC solver All.
+
+Check (_ : EqSimpl unit).
+Check (_ : EqSimpl (unit * unit)).
+
+
diff --git a/apps/tc/tests/contextDeepHierarchy.v b/apps/tc/tests/contextDeepHierarchy.v
new file mode 100644
index 000000000..b215775d3
--- /dev/null
+++ b/apps/tc/tests/contextDeepHierarchy.v
@@ -0,0 +1,19 @@
+From elpi.apps Require Import tc.
+Unset Typeclass Resolution For Conversion.
+Set TC NameShortPath.
+Elpi Override TC TC.Solver All.
+
+
+Class X (A: Type).
+Class Y (A: Type).
+Class Z (A: Type).
+
+Local Instance Inst1@{i} {A: Type@{i}} : X A -> Y A. Qed.
+Local Instance Inst2@{i} (A : Type@{i}): (forall A : Type@{i}, X A -> Y A) -> Z A. Qed.
+
+(* TODO: here Elpi Trace Fails... *)
+
+Goal forall A, Z A.
+ intros.
+ apply _.
+Qed.
\ No newline at end of file
diff --git a/apps/tc/tests/eqSimplDef.v b/apps/tc/tests/eqSimplDef.v
new file mode 100644
index 000000000..916cde199
--- /dev/null
+++ b/apps/tc/tests/eqSimplDef.v
@@ -0,0 +1,11 @@
+Require Import Bool Arith List.
+
+Class Eqb A : Type := eqb : A -> A -> bool.
+Global Hint Mode Eqb + : typeclass_instances.
+
+Notation " x == y " := (eqb x y) (no associativity, at level 70).
+
+Global Instance eqU : Eqb unit := { eqb x y := true }.
+Global Instance eqB : Eqb bool := { eqb x y := if x then y else negb y }.
+Global Instance eqP {A B} `{Eqb A} `{Eqb B} : Eqb (A * B) := {
+ eqb x y := (fst x == fst y) && (snd x == snd y) }.
\ No newline at end of file
diff --git a/apps/tc/tests/hook_test.v b/apps/tc/tests/hook_test.v
new file mode 100644
index 000000000..9d847f6e2
--- /dev/null
+++ b/apps/tc/tests/hook_test.v
@@ -0,0 +1,18 @@
+From elpi.apps Require Import tc.
+Elpi Override TC TC.Solver All.
+
+Elpi TC.AddHook after 1000 1513.
+Elpi TC.AddHook before 1513 1512.
+
+Class A (n : nat).
+Instance Inst1 : A 3 | 1513. Qed.
+Instance Inst2 : A 100 | 1512. Qed.
+
+Elpi Query TC.Solver lp:{{
+ sigma InstL GrefL\
+ std.findall (instance _ _ {{:gref A}}) InstL,
+ std.map InstL (x\r\ x = instance _ r _) GrefL,
+ GrefL = [{{:gref Inst2}}, {{:gref Inst1}}].
+}}.
+
+
diff --git a/apps/tc/tests/importOrder/f1.v b/apps/tc/tests/importOrder/f1.v
new file mode 100644
index 000000000..70c4c1c1c
--- /dev/null
+++ b/apps/tc/tests/importOrder/f1.v
@@ -0,0 +1,5 @@
+From elpi.apps.tc.tests.importOrder Require Export sameOrderCommand.
+
+Class A (T : Set) := f : T -> T.
+
+Elpi SameOrderImport.
\ No newline at end of file
diff --git a/apps/tc/tests/importOrder/f2a.v b/apps/tc/tests/importOrder/f2a.v
new file mode 100644
index 000000000..9c3839098
--- /dev/null
+++ b/apps/tc/tests/importOrder/f2a.v
@@ -0,0 +1,10 @@
+From elpi.apps.tc.tests.importOrder Require Export f1.
+From elpi.apps.tc.tests.importOrder Require Export sameOrderCommand.
+
+
+Global Instance f2aa : A nat := {f x := x}.
+Global Instance f2ab : A nat := {f x := x}.
+Global Instance f2ac : A nat := {f x := x}.
+Global Instance f2ad : A nat := {f x := x}.
+
+Elpi SameOrderImport.
\ No newline at end of file
diff --git a/apps/tc/tests/importOrder/f2b.v b/apps/tc/tests/importOrder/f2b.v
new file mode 100644
index 000000000..b7ec3d03e
--- /dev/null
+++ b/apps/tc/tests/importOrder/f2b.v
@@ -0,0 +1,9 @@
+From elpi.apps.tc.tests.importOrder Require Export f1.
+
+Global Instance f2ba : A nat := {f x := x}.
+Global Instance f2bb : A nat := {f x := x}.
+Global Instance f2bc : A nat := {f x := x}.
+Global Instance f2bd : A nat := {f x := x}.
+
+
+(* Elpi SameOrderImport. *)
diff --git a/apps/tc/tests/importOrder/f3a.v b/apps/tc/tests/importOrder/f3a.v
new file mode 100644
index 000000000..58d5444fd
--- /dev/null
+++ b/apps/tc/tests/importOrder/f3a.v
@@ -0,0 +1,4 @@
+From elpi.apps.tc.tests.importOrder Require Import f2a.
+From elpi.apps.tc.tests.importOrder Require Import f2b.
+
+Elpi SameOrderImport.
diff --git a/apps/tc/tests/importOrder/f3b.v b/apps/tc/tests/importOrder/f3b.v
new file mode 100644
index 000000000..41f84e7aa
--- /dev/null
+++ b/apps/tc/tests/importOrder/f3b.v
@@ -0,0 +1,4 @@
+From elpi.apps.tc.tests.importOrder Require Import f2b.
+From elpi.apps.tc.tests.importOrder Require Import f2a.
+
+Elpi SameOrderImport.
\ No newline at end of file
diff --git a/apps/tc/tests/importOrder/f3c.v b/apps/tc/tests/importOrder/f3c.v
new file mode 100644
index 000000000..2c94dfcb2
--- /dev/null
+++ b/apps/tc/tests/importOrder/f3c.v
@@ -0,0 +1,41 @@
+From elpi.apps.tc.tests.importOrder Require Export f1.
+
+Global Instance f3a : A nat := {f x := x}.
+Global Instance f3b : A nat := {f x := x}.
+Global Instance f3c : A nat := {f x := x}.
+
+Elpi SameOrderImport.
+
+Section S1. Variable X : Type.
+ Local Instance f3d : A nat := {f x := x}.
+ Global Instance f3e : A nat := {f x := x}.
+ Global Instance f3f : A nat := {f x := x}.
+
+ Elpi SameOrderImport.
+End S1.
+
+
+Elpi SameOrderImport.
+
+Section S2.
+ Context (T : Set).
+ Global Instance f3g : A T := {f x := x}.
+
+ Elpi SameOrderImport.
+End S2.
+
+Elpi SameOrderImport.
+
+Section S3.
+ Context (T : Set).
+ Global Instance f3g2 : A (T: Set) := {f x := x}.
+
+ Global Instance f3h T1 T2 `(A T1, A T2) : A (T1 * T2) := {f x := x}.
+
+ Global Instance f3g3 : A (T: Set) := {f x := x}.
+ Global Instance f3g4 : A (T: Set) | 10 := {f x := x}.
+
+ Elpi SameOrderImport.
+End S3.
+
+Elpi SameOrderImport.
\ No newline at end of file
diff --git a/apps/tc/tests/importOrder/f3d.v b/apps/tc/tests/importOrder/f3d.v
new file mode 100644
index 000000000..84c4b3b45
--- /dev/null
+++ b/apps/tc/tests/importOrder/f3d.v
@@ -0,0 +1,32 @@
+From elpi.apps.tc.tests.importOrder Require Export f1.
+From elpi.apps.tc.tests.importOrder Require Import f2b.
+Elpi SameOrderImport.
+
+Global Instance f3a' T1 T2 `(A T1, A T2) : A (T1 * T2) := {f x := x}.
+
+
+Elpi SameOrderImport.
+Module M4'.
+ (* From elpi.apps.tc.tests.importOrder Require Import f2a. *)
+ Elpi SameOrderImport.
+
+ Global Instance f3a : A nat := {f x := x}.
+
+
+ Section S1. Variable X : Type.
+ Global Instance f3b : A nat := {f x := x}.
+
+ Section S1'. Variable Y : Type.
+ Global Instance f3c : A nat := {f x := x}.
+
+ End S1'.
+ End S1.
+
+ Elpi SameOrderImport.
+
+ Section S2. Variable X : Type.
+ Global Instance f3h T1 T2 `(A T1, A T2) : A (T1 * T2) := {f x := x}.
+ End S2.
+End M4'.
+
+Elpi SameOrderImport.
diff --git a/apps/tc/tests/importOrder/f3e.v b/apps/tc/tests/importOrder/f3e.v
new file mode 100644
index 000000000..0de8467ce
--- /dev/null
+++ b/apps/tc/tests/importOrder/f3e.v
@@ -0,0 +1,24 @@
+From elpi.apps.tc.tests.importOrder Require Export f1.
+From elpi.apps.tc.tests.importOrder Require Import f2b.
+From elpi.apps.tc.tests.importOrder Require Import f2a.
+
+Global Instance f3a' T1 T2 `(A T1, A T2) : A (T1 * T2) := {f x := x}.
+
+Elpi SameOrderImport.
+Module M4'.
+ Global Instance f3a : A nat := {f x := x}.
+
+ Section S1. Variable X : Type.
+ Global Instance f3b : A nat := {f x := x}.
+ Section S1'. Variable Y : Type.
+ Global Instance f3c : A nat := {f x := x}.
+ End S1'.
+ End S1.
+
+ Section S2. Variable X : Type.
+ Global Instance f3h T1 T2 `(A T1, A T2) : A (T1 * T2) | 100 := {f x := x}.
+ End S2.
+End M4'.
+
+
+Elpi SameOrderImport.
\ No newline at end of file
diff --git a/apps/tc/tests/importOrder/f3f.v b/apps/tc/tests/importOrder/f3f.v
new file mode 100644
index 000000000..2b0db45ee
--- /dev/null
+++ b/apps/tc/tests/importOrder/f3f.v
@@ -0,0 +1,17 @@
+From elpi.apps.tc.tests.importOrder Require Import f1.
+
+Section S1.
+ Context (T : Set).
+ Global Instance f3a : A T := {f x := x}.
+
+ Elpi SameOrderImport.
+
+ Section S2.
+ Context (T1 : Set).
+ Global Instance f3b : A T1 := {f x := x}.
+
+ End S2.
+
+ Elpi SameOrderImport.
+End S1.
+Elpi SameOrderImport.
\ No newline at end of file
diff --git a/apps/tc/tests/importOrder/f3g.v b/apps/tc/tests/importOrder/f3g.v
new file mode 100644
index 000000000..608efd7f1
--- /dev/null
+++ b/apps/tc/tests/importOrder/f3g.v
@@ -0,0 +1,9 @@
+From elpi.apps.tc.tests.importOrder Require Export f1.
+
+Module M8.
+ Class Classe (A: Type) (B: Type).
+
+ Global Instance I (a b c d: Type): Classe a a -> Classe b c. Admitted.
+
+ Elpi SameOrderImport.
+End M8.
diff --git a/apps/tc/tests/importOrder/f4.v b/apps/tc/tests/importOrder/f4.v
new file mode 100644
index 000000000..0d73340cd
--- /dev/null
+++ b/apps/tc/tests/importOrder/f4.v
@@ -0,0 +1,8 @@
+From elpi.apps.tc.tests.importOrder Require Import f3f.
+From elpi.apps.tc.tests.importOrder Require Import f2a.
+From elpi.apps.tc.tests.importOrder Require Import f2b.
+From elpi.apps.tc.tests.importOrder Require Import f3c.
+From elpi.apps.tc.tests.importOrder Require Import f3d.
+From elpi.apps.tc.tests.importOrder Require Import f3g.
+
+Elpi SameOrderImport.
\ No newline at end of file
diff --git a/apps/tc/tests/importOrder/sameOrderCommand.v b/apps/tc/tests/importOrder/sameOrderCommand.v
new file mode 100644
index 000000000..04aa228d1
--- /dev/null
+++ b/apps/tc/tests/importOrder/sameOrderCommand.v
@@ -0,0 +1,12 @@
+From elpi.apps Require Export tc.
+
+From elpi.apps.tc Extra Dependency "base.elpi" as base.
+From elpi.apps.tc.tests.importOrder Extra Dependency "tc_same_order.elpi" as tc_same_order.
+
+Elpi Command SameOrderImport.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate File base.
+Elpi Accumulate File tc_same_order.
+Elpi Typecheck.
+
+Elpi Override TC TC.Solver All.
\ No newline at end of file
diff --git a/apps/tc/tests/importOrder/tc_same_order.elpi b/apps/tc/tests/importOrder/tc_same_order.elpi
new file mode 100644
index 000000000..4d4607123
--- /dev/null
+++ b/apps/tc/tests/importOrder/tc_same_order.elpi
@@ -0,0 +1,24 @@
+% [Typeclass, Coq Instances, Elpi Instances]
+% the instances of the given typeclass should be in the same order as Coq
+pred correct_instance_order_aux i:gref, i:(list tc-instance), i:(list gref).
+:name "tc-correct-instance-order-aux"
+correct_instance_order_aux _ [] [].
+correct_instance_order_aux TC [tc-instance I1 _ | TL1] [I1 | TL2] :-
+ correct_instance_order_aux TC TL1 TL2.
+
+% [Typeclasses of Coq, Elpi Instances]
+pred correct_instance_order i:(list gref), i:(list prop).
+:name "tc-correct-instance-order"
+correct_instance_order [] _.
+correct_instance_order [TC | TL] ElpiInst :-
+ coq.TC.db-for TC CoqInst,
+ std.map-filter ElpiInst (x\r\ sigma I\ x = instance _ I TC, r = I) ElpiInstTC,
+ if (correct_instance_order_aux TC CoqInst ElpiInstTC)
+ (correct_instance_order TL ElpiInst)
+ (coq.error "Error in import order\n"
+ "Expected :" CoqInst "\nFound :" ElpiInstTC).
+
+:name "tc-same-order-main"
+main _ :-
+ std.findall (instance _ _ _) ElpiInst,
+ correct_instance_order {coq.TC.db-tc} ElpiInst.
\ No newline at end of file
diff --git a/apps/tc/tests/indt_to_inst.v b/apps/tc/tests/indt_to_inst.v
new file mode 100644
index 000000000..a7fbd9eb4
--- /dev/null
+++ b/apps/tc/tests/indt_to_inst.v
@@ -0,0 +1,32 @@
+From Coq Require Export List.
+From elpi.apps Require Export tc.
+Global Generalizable All Variables.
+
+Elpi Override TC TC.Solver All.
+
+Class ElemOf A B := elem_of: A -> B -> Prop.
+Class Elements A C := elements: C -> list A.
+
+Inductive elem_of_list {A} : ElemOf A (list A) :=
+ | elem_of_list_here (x : A) l : elem_of x (x :: l)
+ | elem_of_list_further (x y : A) l : elem_of x l -> elem_of x (y :: l).
+Global Existing Instance elem_of_list.
+
+Inductive NoDup {A} : list A -> Prop :=
+ | NoDup_nil_2 : NoDup nil
+ | NoDup_cons_2 x l : not (elem_of x l) -> NoDup l -> NoDup (x :: l).
+
+Module A.
+ Class FinSet1 A C `{ElemOf A C,Elements A C} : Prop := {
+ NoDup_elements (X : C) : NoDup (elements X)
+ }.
+End A.
+
+Module B.
+
+ Class FinSet2 A C `{ElemOf A C, Elements A C} : Prop := {
+ elem_of_elements2 (X : C) x : iff (elem_of x (elements X)) (elem_of x X);
+ NoDup_elements2 (X : C) : NoDup (elements X)
+ }.
+
+End B.
\ No newline at end of file
diff --git a/apps/tc/tests/injTest.v b/apps/tc/tests/injTest.v
new file mode 100644
index 000000000..2cc47114d
--- /dev/null
+++ b/apps/tc/tests/injTest.v
@@ -0,0 +1,119 @@
+From elpi.apps Require Import tc.
+From Coq Require Import Morphisms RelationClasses List Bool Setoid Peano Utf8.
+
+Generalizable All Variables.
+Elpi Override TC TC.Solver All.
+
+Class Inj {A B} (R : relation A) (S : relation B) (f : A -> B) :=
+ inj x y : S (f x) (f y) -> R x y.
+
+Class Inj2 {A B C} (R1 : relation A) (R2 : relation B)
+ (S : relation C) (f : A → B → C) : Prop :=
+ inj2 x1 x2 y1 y2 : S (f x1 x2) (f y1 y2) → R1 x1 y1 ∧ R2 x2 y2.
+
+(* Elpi Override TC TC.Solver Only Inj Inj2. *)
+
+Definition gInj x := x + 1.
+Definition fInj x := x * 3.
+
+Axiom eq1 : relation nat.
+Axiom eq2 : relation nat.
+Axiom eq3 : relation nat.
+
+Local Instance isInjg : Inj eq3 eq1 gInj. Admitted.
+
+Local Instance isInjf : Inj eq1 eq3 fInj. Admitted.
+
+Local Instance isInjf_old : Inj eq1 eq2 fInj. Admitted.
+Local Instance isInjg_old : Inj eq2 eq3 gInj. Admitted.
+
+Local Instance isInjf_eq : Inj eq eq fInj. Admitted.
+Local Instance isInjg_eq : Inj eq eq gInj. Admitted.
+
+Local Instance id_inj {A} : Inj eq eq (@id A). Admitted.
+Local Instance inl_inj {A B} : Inj eq eq (@inl A B). Admitted.
+Local Instance inr_inj {A B} : Inj eq eq (@inr A B). Admitted.
+
+Definition compose {T1 T2 T3: Type} (g: T2 -> T3) (f : T1 -> T2) (x: T1) := g(f x).
+Local Instance compose_inj {A B C} R1 R2 R3 (f : A -> B) (g : B -> C) :
+ Inj R1 R2 f -> Inj R2 R3 g -> Inj R1 R3 (compose g f).
+Admitted.
+
+Goal exists A B, Inj A B (compose gInj fInj). Admitted.
+
+Goal forall (T1 T2 : Type) (f: T1 -> T2),
+ let r := Inj eq eq f in
+ let x := true in
+ (if x then r else r) -> Inj eq eq f.
+ intros ? ? f r x H.
+ unfold x, r in H.
+ apply _.
+Qed.
+
+Goal forall (T1 T2 : Type) (f: T1 -> T2),
+ let r := Inj eq eq f in
+ let b := true in
+ let cond := (match b with
+ | true => r
+ | false => f = f end) in
+ cond -> Inj eq eq f.
+ intros.
+ unfold cond in H.
+ simpl in H.
+ unfold r in H.
+ apply _.
+Qed.
+
+Elpi Override TC TC.Solver All.
+(* Elpi Print TC.Solver. *)
+Local Instance inj2_inj_1 `{Inj2 A B C R1 R2 R3 ff} y : Inj R1 R3 (λ x, ff x y).
+Admitted.
+
+Global Instance inj2_inj_2 `{Inj2 A B C R1 R2 R3 ff} x : Inj R2 R3 (ff x).
+Admitted.
+
+Goal Inj2 eq eq eq Nat.mul -> Inj eq eq (Nat.mul 0).
+ intros.
+ apply _.
+Qed.
+
+Goal Inj2 eq eq eq Nat.add -> Inj eq eq (fun x => Nat.add x 0).
+intros.
+apply _.
+Qed.
+
+Definition p (T : Type) := @pair T T.
+
+Goal Inj eq eq (compose fInj gInj).
+Proof.
+apply _.
+Qed.
+
+Elpi Print TC.Solver.
+Set Warnings "+elpi".
+
+
+Elpi Accumulate tc.db lp:{{
+ shorten tc-elpi.apps.tc.tests.injTest.{tc-Inj}.
+ % shorten tc-injTest.{tc-Inj}.
+ tc-Inj T1 T2 R1 R3 F S :-
+ F = (fun _ _ _),
+ G = {{ compose _ _ }},
+ coq.unify-eq G F ok,
+ tc-Inj T1 T2 R1 R3 G S.
+}}.
+
+Elpi Typecheck TC.Solver.
+
+Goal Inj eq eq (compose fInj gInj). apply _. Qed.
+Goal Inj eq eq (fun x => fInj (gInj x)). apply _. Qed.
+
+Goal forall (A: Type) (x: A -> A),
+ let y := Inj eq eq x in
+ let z := y in z ->
+ Inj eq eq (compose x x).
+Proof.
+ intros T x y z H.
+ unfold z, y in H.
+ apply _.
+Qed.
\ No newline at end of file
diff --git a/apps/tc/tests/nobacktrack.v b/apps/tc/tests/nobacktrack.v
new file mode 100644
index 000000000..207caafe8
--- /dev/null
+++ b/apps/tc/tests/nobacktrack.v
@@ -0,0 +1,42 @@
+From elpi.apps Require Import tc.
+
+Elpi Debug "simple-compiler".
+Set TC NameShortPath.
+
+Module A.
+
+ Class C (n : nat) := {}.
+ Elpi TC.Set_deterministic C.
+ Elpi TC.Get_class_info C.
+ Local Instance c_1 : C 1 | 10 := {}.
+ Local Instance c_2 : C 2 | 1 := {}.
+
+ Class D (n : nat) := {}.
+ Local Instance d_1 : D 1 := {}.
+
+ Class E (n : nat) := {}.
+ Local Instance foo {n} : C n -> D n -> E n := {}.
+
+ Elpi Override TC TC.Solver All.
+
+ Goal exists n, E n.
+ eexists.
+ Fail apply _.
+ Abort.
+
+End A.
+
+Module B.
+
+ Class A (T : Set) := f : T -> T.
+ Elpi TC.Set_deterministic A.
+
+ Global Instance A1 : A bool := {f x := x}.
+ Global Instance A2 `(A bool) : A (bool * bool) :=
+ {f x := x}.
+ Global Instance A3 `(A nat) : A (bool * bool) :=
+ {f x := x}.
+
+ Goal A (bool * bool). apply _. Qed.
+
+End B.
\ No newline at end of file
diff --git a/apps/tc/tests/patternFragment.v b/apps/tc/tests/patternFragment.v
new file mode 100644
index 000000000..9336f46f8
--- /dev/null
+++ b/apps/tc/tests/patternFragment.v
@@ -0,0 +1,76 @@
+From elpi.apps Require Import tc.
+Elpi Override TC TC.Solver All.
+Set TC NameShortPath.
+Set TC CompilerWithPatternFragment.
+
+Class Y (A: Type).
+Class Z (A: Type).
+Class Ex (P : Type -> Type) (A: Type).
+
+Module M4.
+Local Instance Inst2 A F: (forall (a : Type) (b c : nat), Y (F a b) -> Y (F a c)) -> Z A. Qed.
+Goal Z bool.
+
+Elpi Override TC TC.Solver None.
+ Fail apply _.
+Elpi Override TC TC.Solver All.
+ apply _.
+ Show Proof.
+ Unshelve. assumption. (* we keep a, the first arg of F *)
+ Show Proof. Qed.
+
+Local Instance Inst1: Y (bool * bool). Qed.
+
+Goal Z bool.
+
+Elpi Override TC TC.Solver None.
+ Succeed apply _.
+Elpi Override TC TC.Solver All.
+ apply _.
+
+ Show Proof.
+ Unshelve. apply bool.
+ Show Proof. Qed.
+
+End M4.
+
+Module M5.
+Local Instance Inst1: Y (bool * bool). Qed.
+Local Instance Inst2 A F (R: Type -> Type -> Type): forall x,
+ (forall (a : Type), Y (F a)) -> Ex (R x) A. Qed.
+Goal forall (A:Type) x (R: Type -> Type -> Type ->Type), Ex (R x x) A. apply _. Qed.
+End M5.
+
+Module M1.
+Local Instance Inst1: Y (bool * bool). Qed.
+Local Instance Inst2 A F: (forall (a : Type), Y (F a)) -> Z A. Qed.
+
+Goal forall (A:Type), Z A. apply _. Qed.
+End M1.
+
+Module M2.
+Local Instance Inst1: Y (bool * bool). Qed.
+Local Instance Inst2 A F: (forall (a: Type), Y (F a)) -> Z A. Qed.
+Goal Z bool. apply _. Qed.
+End M2.
+
+Module M3.
+Local Instance Inst1: Y (bool * bool). Qed.
+Local Instance Inst2 A F: (forall (a b c d: Type), Y (F b c d)) -> Z A. Qed.
+Goal Z bool. apply _. Qed.
+End M3.
+
+Module M6.
+Local Instance Inst1: Y (bool * bool). Qed.
+Local Instance Inst2 A F: (forall (a b c d e f g: Type), Y (F a b c d) -> Y (F e f g a)) -> Z A. Qed.
+Goal Z bool. apply _. Unshelve. apply nat. Qed.
+End M6.
+
+Module M1b.
+Local Instance Inst2 A F: (forall (a : Type), Y (F a)) -> Ex F A. Qed.
+Goal forall (A:Type) (f : Type -> Type), (forall x, Y (f x)) -> exists f, Ex f A. intros. eexists. apply _.
+ Unshelve.
+ apply A.
+Qed.
+End M1b.
+
diff --git a/apps/tc/tests/register/f1.v b/apps/tc/tests/register/f1.v
new file mode 100644
index 000000000..89a8006af
--- /dev/null
+++ b/apps/tc/tests/register/f1.v
@@ -0,0 +1,8 @@
+From elpi.apps Require Import tc.
+
+Elpi Override TC TC.Solver All.
+
+Class A (n : nat).
+Instance I1 : A 1. Qed.
+
+Goal A 1. apply _. Qed.
\ No newline at end of file
diff --git a/apps/tc/tests/register/f2.v b/apps/tc/tests/register/f2.v
new file mode 100644
index 000000000..5410ffea2
--- /dev/null
+++ b/apps/tc/tests/register/f2.v
@@ -0,0 +1,9 @@
+From elpi.apps.tc.tests.register Require Export f1.
+
+Goal A 1. apply _. Qed.
+
+Elpi TC Deactivate Observer TC.Compiler.
+
+Instance I2 : A 2. Qed.
+
+Goal A 2. Fail apply _. Abort.
\ No newline at end of file
diff --git a/apps/tc/tests/register/f3.v b/apps/tc/tests/register/f3.v
new file mode 100644
index 000000000..ad91b9b21
--- /dev/null
+++ b/apps/tc/tests/register/f3.v
@@ -0,0 +1,28 @@
+From elpi.apps.tc.tests.register Require Import f2.
+
+(*
+ Note that in f2, TC.Compiler has been deactivated,
+ therefore I3 should not be added
+*)
+
+Instance I3 : A 3. Qed.
+
+Goal A 3. Fail apply _. Abort.
+
+Elpi Command custom_observer.
+Elpi Accumulate lp:{{
+ main L :-
+ coq.say "Received the following event" L.
+}}.
+
+Elpi TC Activate Observer TC.Compiler.
+Elpi Register TC Compiler custom_observer.
+Elpi TC Activate Observer custom_observer.
+
+(* Here we have two active event listener for the instance creation:
+ custom observer which simply prints the received event and
+ TC.Compiler that adds I4 to the db
+*)
+Instance I4 : A 4. Qed.
+
+Goal A 4. apply _. Qed.
diff --git a/apps/tc/tests/section_in_out.v b/apps/tc/tests/section_in_out.v
new file mode 100644
index 000000000..45d11cff5
--- /dev/null
+++ b/apps/tc/tests/section_in_out.v
@@ -0,0 +1,56 @@
+From elpi.apps Require Import tc.
+From elpi.apps.tc Extra Dependency "base.elpi" as base.
+
+Elpi Accumulate tc.db lp:{{
+ pred origial_tc o:int.
+}}.
+
+Elpi Command len_test.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate File base.
+Elpi Accumulate lp:{{
+ % contains the number of instances that are not
+ % imported from other files
+ main [int Len] :-
+ std.findall (instance _ _ _) Insts,
+ std.map Insts (x\r\ instance _ r _ = x) R,
+ WantedLength is {origial_tc} + Len,
+ std.assert! ({std.length R} = WantedLength)
+ "Unexpected number of instances",
+ std.forall R (x\ sigma L\
+ std.assert! (count R x L, L = 1) "Duplicates in instances").
+}}.
+
+Elpi Query TC.Solver lp:{{
+ std.findall (instance _ _ _) Rules,
+ std.length Rules Len,
+ coq.elpi.accumulate _ "tc.db" (clause _ _ (origial_tc Len)).
+}}.
+
+Class Eqb A:= eqb: A -> A -> bool.
+Global Instance eqA : Eqb unit := { eqb x y := true }.
+
+Elpi len_test 1.
+
+Section A.
+ Context (A : Type).
+ Global Instance eqB : Eqb bool := { eqb x y := if x then y else negb y }.
+ Elpi len_test 2.
+
+ Global Instance eqC : Eqb A := {eqb _ _ := true}.
+ Elpi len_test 3.
+
+ Section B.
+ Context (B : Type).
+ Global Instance eqD : Eqb B := {eqb _ _ := true}.
+ Elpi len_test 4.
+ End B.
+
+ Elpi len_test 4.
+
+End A.
+
+Elpi len_test 4.
+
+
+
diff --git a/apps/tc/tests/stdppInj.v b/apps/tc/tests/stdppInj.v
new file mode 100644
index 000000000..2bfc84e50
--- /dev/null
+++ b/apps/tc/tests/stdppInj.v
@@ -0,0 +1,266 @@
+(* Test inspired from https://gitlab.mpi-sws.org/iris/stdpp/-/blob/8c98553ad0ca2029b30cf18b58e321ec3a79172b/stdpp/base.v *)
+
+From Coq Require Export Morphisms RelationClasses List Bool Setoid Peano Utf8.
+From Coq Require Import Permutation.
+Export ListNotations.
+From Coq.Program Require Export Basics Syntax.
+
+From elpi.apps Require Import tc.
+Elpi Override TC TC.Solver All.
+Elpi TC.AddAllClasses.
+Elpi TC.AddAllInstances.
+Notation length := Datatypes.length.
+Global Generalizable All Variables.
+Global Unset Transparent Obligations.
+
+Definition tc_opaque {A} (x : A) : A := x.
+(* Typeclasses Opaque tc_opaque. *)
+
+Global Arguments tc_opaque {_} _ /.
+Declare Scope stdpp_scope.
+Delimit Scope stdpp_scope with stdpp.
+Global Open Scope stdpp_scope.
+Notation "(=)" := eq (only parsing) : stdpp_scope.
+Notation "( x =.)" := (eq x) (only parsing) : stdpp_scope.
+Notation "(.= x )" := (λ y, eq y x) (only parsing) : stdpp_scope.
+Notation "(≠)" := (λ x y, x ≠ y) (only parsing) : stdpp_scope.
+Notation "( x ≠.)" := (λ y, x ≠ y) (only parsing) : stdpp_scope.
+Notation "(.≠ x )" := (λ y, y ≠ x) (only parsing) : stdpp_scope.
+Infix "=@{ A }" := (@eq A)
+ (at level 70, only parsing, no associativity) : stdpp_scope.
+Notation "(=@{ A } )" := (@eq A) (only parsing) : stdpp_scope.
+Notation "(≠@{ A } )" := (λ X Y, ¬X =@{A} Y) (only parsing) : stdpp_scope.
+Notation "X ≠@{ A } Y":= (¬X =@{ A } Y)
+ (at level 70, only parsing, no associativity) : stdpp_scope.
+
+Global Hint Extern 0 (_ = _) => reflexivity : core.
+Global Hint Extern 100 (_ ≠ _) => discriminate : core.
+
+Global Instance: ∀ A, PreOrder (=@{A}).
+Proof. split; repeat intro; congruence. Qed.
+Class Equiv A := equiv: relation A.
+Global Instance equiv_rewrite_relation `{Equiv A} :
+ RewriteRelation (@equiv A _) | 150 := {}.
+
+Infix "≡" := equiv (at level 70, no associativity) : stdpp_scope.
+Infix "≡@{ A }" := (@equiv A _)
+ (at level 70, only parsing, no associativity) : stdpp_scope.
+
+Notation "(≡)" := equiv (only parsing) : stdpp_scope.
+Notation "( X ≡.)" := (equiv X) (only parsing) : stdpp_scope.
+Notation "(.≡ X )" := (λ Y, Y ≡ X) (only parsing) : stdpp_scope.
+Notation "(≢)" := (λ X Y, ¬X ≡ Y) (only parsing) : stdpp_scope.
+Notation "X ≢ Y":= (¬X ≡ Y) (at level 70, no associativity) : stdpp_scope.
+Notation "( X ≢.)" := (λ Y, X ≢ Y) (only parsing) : stdpp_scope.
+Notation "(.≢ X )" := (λ Y, Y ≢ X) (only parsing) : stdpp_scope.
+
+Notation "(≡@{ A } )" := (@equiv A _) (only parsing) : stdpp_scope.
+Notation "(≢@{ A } )" := (λ X Y, ¬X ≡@{A} Y) (only parsing) : stdpp_scope.
+Notation "X ≢@{ A } Y":= (¬X ≡@{ A } Y)
+ (at level 70, only parsing, no associativity) : stdpp_scope.
+Class LeibnizEquiv A `{Equiv A} :=
+ leibniz_equiv (x y : A) : x ≡ y → x = y.
+Global Hint Mode LeibnizEquiv ! - : typeclass_instances.
+
+Global Instance: Params (@equiv) 2 := {}.
+Global Instance equiv_default_relation `{Equiv A} :
+ DefaultRelation (≡@{A}) | 3 := {}.
+Global Hint Extern 0 (_ ≡ _) => reflexivity : core.
+Global Hint Extern 0 (_ ≡ _) => symmetry; assumption : core.
+
+
+Class Inj {A B} (R : relation A) (S : relation B) (f : A → B) : Prop :=
+ inj x y : S (f x) (f y) → R x y.
+
+Class Inj2 {A B C} (R1 : relation A) (R2 : relation B)
+ (S : relation C) (f : A → B → C) : Prop :=
+ inj2 x1 x2 y1 y2 : S (f x1 x2) (f y1 y2) → R1 x1 y1 ∧ R2 x2 y2.
+
+Global Arguments irreflexivity {_} _ {_} _ _ : assert.
+Global Arguments inj {_ _ _ _} _ {_} _ _ _ : assert.
+Global Arguments inj2 {_ _ _ _ _ _} _ {_} _ _ _ _ _: assert.
+
+Global Instance inj2_inj_1 `{Inj2 A B C R1 R2 R3 f} y : Inj R1 R3 (λ x, f x y).
+Proof. repeat intro; edestruct (inj2 f); eauto. Qed.
+Global Instance inj2_inj_2 `{Inj2 A B C R1 R2 R3 f} x : Inj R2 R3 (f x).
+Proof. repeat intro; edestruct (inj2 f); eauto. Qed.
+
+Notation "(∧)" := and (only parsing) : stdpp_scope.
+Notation "( A ∧.)" := (and A) (only parsing) : stdpp_scope.
+Notation "(.∧ B )" := (λ A, A ∧ B) (only parsing) : stdpp_scope.
+
+Notation "(∨)" := or (only parsing) : stdpp_scope.
+Notation "( A ∨.)" := (or A) (only parsing) : stdpp_scope.
+Notation "(.∨ B )" := (λ A, A ∨ B) (only parsing) : stdpp_scope.
+
+Notation "(↔)" := iff (only parsing) : stdpp_scope.
+Notation "( A ↔.)" := (iff A) (only parsing) : stdpp_scope.
+Notation "(.↔ B )" := (λ A, A ↔ B) (only parsing) : stdpp_scope.
+
+Global Hint Extern 0 (_ ↔ _) => reflexivity : core.
+Global Hint Extern 0 (_ ↔ _) => symmetry; assumption : core.
+
+Notation "(→)" := (λ A B, A → B) (only parsing) : stdpp_scope.
+Notation "( A →.)" := (λ B, A → B) (only parsing) : stdpp_scope.
+Notation "(.→ B )" := (λ A, A → B) (only parsing) : stdpp_scope.
+
+Notation "t $ r" := (t r)
+ (at level 65, right associativity, only parsing) : stdpp_scope.
+Notation "($)" := (λ f x, f x) (only parsing) : stdpp_scope.
+Notation "(.$ x )" := (λ f, f x) (only parsing) : stdpp_scope.
+
+Infix "∘" := compose : stdpp_scope.
+Notation "(∘)" := compose (only parsing) : stdpp_scope.
+Notation "( f ∘.)" := (compose f) (only parsing) : stdpp_scope.
+Notation "(.∘ f )" := (λ g, compose g f) (only parsing) : stdpp_scope.
+(** Ensure that [simpl] unfolds [id], [compose], and [flip] when fully
+applied. *)
+Global Arguments id _ _ / : assert.
+Global Arguments compose _ _ _ _ _ _ / : assert.
+Global Arguments flip _ _ _ _ _ _ / : assert.
+Global Arguments const _ _ _ _ / : assert.
+
+Definition fun_map {A A' B B'} (f: A' → A) (g: B → B') (h : A → B) : A' → B' :=
+ g ∘ h ∘ f.
+
+Global Instance id_inj {A} : Inj (=) (=) (@id A).
+Proof. intros ??; auto. Qed.
+Global Instance compose_inj {A B C} R1 R2 R3 (f : A → B) (g : B → C) :
+ Inj R1 R2 f → Inj R2 R3 g → Inj R1 R3 (g ∘ f).
+Proof. red; intuition. Qed.
+
+(** ** Products *)
+Notation "( x ,.)" := (pair x) (only parsing) : stdpp_scope.
+Notation "(., y )" := (λ x, (x,y)) (only parsing) : stdpp_scope.
+
+Notation "p .1" := (fst p) (at level 2, left associativity, format "p .1").
+Notation "p .2" := (snd p) (at level 2, left associativity, format "p .2").
+
+Definition prod_map {A A' B B'} (f: A → A') (g: B → B') (p : A * B) : A' * B' :=
+ (f (p.1), g (p.2)).
+Global Arguments prod_map {_ _ _ _} _ _ !_ / : assert.
+
+Global Instance pair_inj {A B} : Inj2 (=) (=) (=) (@pair A B).
+Proof. injection 1; auto. Qed.
+Global Instance prod_map_inj {A A' B B'} (f : A → A') (g : B → B') :
+ Inj (=) (=) f → Inj (=) (=) g → Inj (=) (=) (prod_map f g).
+Proof.
+ intros ?? [??] [??] ?; simpl in *; f_equal;
+ [apply (inj f)|apply (inj g)]; congruence.
+Qed.
+
+Definition prod_relation {A B} (R1 : relation A) (R2 : relation B) :
+ relation (A * B) := λ x y, R1 (x.1) (y.1) ∧ R2 (x.2) (y.2).
+
+Section prod_relation.
+ Context `{RA : relation A, RB : relation B}.
+ Global Instance pair_inj' : Inj2 RA RB (prod_relation RA RB) pair.
+ Proof. inversion_clear 1; eauto. Qed.
+End prod_relation.
+
+Global Instance prod_equiv `{Equiv A,Equiv B} : Equiv (A * B) :=
+ prod_relation (≡) (≡).
+
+Section prod_setoid.
+ Context `{Equiv A, Equiv B}.
+
+ Elpi Accumulate TC.Solver lp:{{
+ shorten tc-elpi.apps.tc.tests.stdppInj.{tc-Inj2}.
+ % shorten tc-stdppInj.{tc-Inj2}.
+ tc-Inj2 A B C RA RB RC F S :-
+ RC = app [global {coq.locate "equiv"} | _],
+ Res = {{prod_relation _ _}},
+ coq.unify-eq RC Res ok,
+ tc-Inj2 A B C RA RB Res F S.
+ }}.
+ Elpi Typecheck TC.Solver.
+
+ Global Instance pair_equiv_inj : Inj2 (≡) (≡) (≡@{A*B}) pair := _.
+End prod_setoid.
+
+(* Typeclasses Opaque prod_equiv. *)
+
+(** ** Sums *)
+Definition sum_map {A A' B B'} (f: A → A') (g: B → B') (xy : A + B) : A' + B' :=
+ match xy with inl x => inl (f x) | inr y => inr (g y) end.
+Global Arguments sum_map {_ _ _ _} _ _ !_ / : assert.
+
+Global Instance inl_inj {A B} : Inj (=) (=) (@inl A B).
+Proof. injection 1; auto. Qed.
+Global Instance inr_inj {A B} : Inj (=) (=) (@inr A B).
+Proof. injection 1; auto. Qed.
+
+Global Instance sum_map_inj {A A' B B'} (f : A → A') (g : B → B') :
+ Inj (=) (=) f → Inj (=) (=) g → Inj (=) (=) (sum_map f g).
+Proof. intros ?? [?|?] [?|?] [=]; f_equal; apply (inj _); auto. Qed.
+
+Inductive sum_relation {A B}
+ (RA : relation A) (RB : relation B) : relation (A + B) :=
+ | inl_related x1 x2 : RA x1 x2 → sum_relation RA RB (inl x1) (inl x2)
+ | inr_related y1 y2 : RB y1 y2 → sum_relation RA RB (inr y1) (inr y2).
+
+Section sum_relation.
+ Context `{RA : relation A, RB : relation B}.
+ Global Instance inl_inj' : Inj RA (sum_relation RA RB) inl.
+ Proof. inversion_clear 1; auto. Qed.
+ Global Instance inr_inj' : Inj RB (sum_relation RA RB) inr.
+ Proof. inversion_clear 1; auto. Qed.
+End sum_relation.
+
+Global Instance sum_equiv `{Equiv A, Equiv B} : Equiv (A + B) := sum_relation (≡) (≡).
+
+Elpi Accumulate TC.Solver lp:{{
+ shorten tc-elpi.apps.tc.tests.stdppInj.{tc-Inj}.
+ % shorten tc-stdppInj.{tc-Inj}.
+ tc-Inj A B RA {{@equiv (sum _ _) (@sum_equiv _ _ _ _)}} S C :-
+ tc-Inj A B RA {{sum_relation _ _}} S C.
+}}.
+Elpi Typecheck TC.Solver.
+
+Global Instance inl_equiv_inj `{Equiv A, Equiv B} : Inj (≡) (≡) (@inl A B) := _.
+Global Instance inr_equiv_inj `{Equiv A, Equiv B} : Inj (≡) (≡) (@inr A B) := _.
+
+Notation "` x" := (proj1_sig x) (at level 10, format "` x") : stdpp_scope.
+
+Elpi Accumulate TC.Solver lp:{{
+ shorten tc-elpi.apps.tc.tests.stdppInj.{tc-Inj}.
+ tc-Inj A B RA RB F X :-
+ F = fun _ _ _,
+ G = {{@compose _ _ _ _ _}},
+ coq.unify-eq G F ok,
+ tc-Inj A B RA RB G X.
+}}.
+Elpi Typecheck TC.Solver.
+
+Definition f := Nat.add 0.
+Global Instance h: Inj eq eq f.
+ unfold f. simpl. easy.
+Qed.
+
+Elpi Accumulate TC.Solver lp:{{
+ shorten tc-elpi.apps.tc.tests.stdppInj.{tc-Inj}.
+ :after "lastHook"
+ tc-Inj A B RA RB F S :-
+ F = (fun _ _ _), !,
+ G = {{ compose _ _ }},
+ coq.unify-eq G F ok,
+ tc-Inj A B RA RB G S.
+}}.
+Set Warnings "+elpi".
+
+Elpi Typecheck TC.Solver.
+Goal Inj eq eq (compose (@id nat) id).
+apply _.
+Qed.
+
+Goal Inj eq eq (compose (compose (@id nat) id) id).
+apply _.
+Qed.
+
+Goal Inj eq eq (fun (x:nat) => id (id x)).
+ apply _.
+Qed.
+
+Goal Inj eq eq (fun (x: nat) => (compose id id) (id x)).
+apply (compose_inj eq eq); apply _.
+Qed.
\ No newline at end of file
diff --git a/apps/tc/tests/stdppInjClassic.v b/apps/tc/tests/stdppInjClassic.v
new file mode 100644
index 000000000..eaea31c59
--- /dev/null
+++ b/apps/tc/tests/stdppInjClassic.v
@@ -0,0 +1,219 @@
+(* File inspired from https://gitlab.mpi-sws.org/iris/stdpp/-/blob/8c98553ad0ca2029b30cf18b58e321ec3a79172b/stdpp/base.v *)
+From Coq Require Export Morphisms RelationClasses List Bool Setoid Peano Utf8.
+From Coq Require Import Permutation.
+Export ListNotations.
+From Coq.Program Require Export Basics Syntax.
+
+Notation length := Datatypes.length.
+Global Generalizable All Variables.
+Global Unset Transparent Obligations.
+
+Definition tc_opaque {A} (x : A) : A := x.
+(* Typeclasses Opaque tc_opaque. *)
+
+Global Arguments tc_opaque {_} _ /.
+Declare Scope stdpp_scope.
+Delimit Scope stdpp_scope with stdpp.
+Global Open Scope stdpp_scope.
+Notation "(=)" := eq (only parsing) : stdpp_scope.
+Notation "( x =.)" := (eq x) (only parsing) : stdpp_scope.
+Notation "(.= x )" := (λ y, eq y x) (only parsing) : stdpp_scope.
+Notation "(≠)" := (λ x y, x ≠ y) (only parsing) : stdpp_scope.
+Notation "( x ≠.)" := (λ y, x ≠ y) (only parsing) : stdpp_scope.
+Notation "(.≠ x )" := (λ y, y ≠ x) (only parsing) : stdpp_scope.
+Infix "=@{ A }" := (@eq A)
+ (at level 70, only parsing, no associativity) : stdpp_scope.
+Notation "(=@{ A } )" := (@eq A) (only parsing) : stdpp_scope.
+Notation "(≠@{ A } )" := (λ X Y, ¬X =@{A} Y) (only parsing) : stdpp_scope.
+Notation "X ≠@{ A } Y":= (¬X =@{ A } Y)
+ (at level 70, only parsing, no associativity) : stdpp_scope.
+
+Global Hint Extern 0 (_ = _) => reflexivity : core.
+Global Hint Extern 100 (_ ≠ _) => discriminate : core.
+
+Global Instance: ∀ A, PreOrder (=@{A}).
+Proof. split; repeat intro; congruence. Qed.
+Class Equiv A := equiv: relation A.
+Global Instance equiv_rewrite_relation `{Equiv A} :
+ RewriteRelation (@equiv A _) | 150 := {}.
+
+Infix "≡" := equiv (at level 70, no associativity) : stdpp_scope.
+Infix "≡@{ A }" := (@equiv A _)
+ (at level 70, only parsing, no associativity) : stdpp_scope.
+
+Notation "(≡)" := equiv (only parsing) : stdpp_scope.
+Notation "( X ≡.)" := (equiv X) (only parsing) : stdpp_scope.
+Notation "(.≡ X )" := (λ Y, Y ≡ X) (only parsing) : stdpp_scope.
+Notation "(≢)" := (λ X Y, ¬X ≡ Y) (only parsing) : stdpp_scope.
+Notation "X ≢ Y":= (¬X ≡ Y) (at level 70, no associativity) : stdpp_scope.
+Notation "( X ≢.)" := (λ Y, X ≢ Y) (only parsing) : stdpp_scope.
+Notation "(.≢ X )" := (λ Y, Y ≢ X) (only parsing) : stdpp_scope.
+
+Notation "(≡@{ A } )" := (@equiv A _) (only parsing) : stdpp_scope.
+Notation "(≢@{ A } )" := (λ X Y, ¬X ≡@{A} Y) (only parsing) : stdpp_scope.
+Notation "X ≢@{ A } Y":= (¬X ≡@{ A } Y)
+ (at level 70, only parsing, no associativity) : stdpp_scope.
+Class LeibnizEquiv A `{Equiv A} :=
+ leibniz_equiv (x y : A) : x ≡ y → x = y.
+Global Hint Mode LeibnizEquiv ! - : typeclass_instances.
+
+Global Instance: Params (@equiv) 2 := {}.
+Global Instance equiv_default_relation `{Equiv A} :
+ DefaultRelation (≡@{A}) | 3 := {}.
+Global Hint Extern 0 (_ ≡ _) => reflexivity : core.
+Global Hint Extern 0 (_ ≡ _) => symmetry; assumption : core.
+
+
+Class Inj {A B} (R : relation A) (S : relation B) (f : A → B) : Prop :=
+ inj x y : S (f x) (f y) → R x y.
+
+Class Inj2 {A B C} (R1 : relation A) (R2 : relation B)
+ (S : relation C) (f : A → B → C) : Prop :=
+ inj2 x1 x2 y1 y2 : S (f x1 x2) (f y1 y2) → R1 x1 y1 ∧ R2 x2 y2.
+
+Global Arguments irreflexivity {_} _ {_} _ _ : assert.
+Global Arguments inj {_ _ _ _} _ {_} _ _ _ : assert.
+Global Arguments inj2 {_ _ _ _ _ _} _ {_} _ _ _ _ _: assert.
+
+Global Instance inj2_inj_1 `{Inj2 A B C R1 R2 R3 f} y : Inj R1 R3 (λ x, f x y).
+Proof. repeat intro; edestruct (inj2 f); eauto. Qed.
+Global Instance inj2_inj_2 `{Inj2 A B C R1 R2 R3 f} x : Inj R2 R3 (f x).
+Proof. repeat intro; edestruct (inj2 f); eauto. Qed.
+
+Notation "(∧)" := and (only parsing) : stdpp_scope.
+Notation "( A ∧.)" := (and A) (only parsing) : stdpp_scope.
+Notation "(.∧ B )" := (λ A, A ∧ B) (only parsing) : stdpp_scope.
+
+Notation "(∨)" := or (only parsing) : stdpp_scope.
+Notation "( A ∨.)" := (or A) (only parsing) : stdpp_scope.
+Notation "(.∨ B )" := (λ A, A ∨ B) (only parsing) : stdpp_scope.
+
+Notation "(↔)" := iff (only parsing) : stdpp_scope.
+Notation "( A ↔.)" := (iff A) (only parsing) : stdpp_scope.
+Notation "(.↔ B )" := (λ A, A ↔ B) (only parsing) : stdpp_scope.
+
+Global Hint Extern 0 (_ ↔ _) => reflexivity : core.
+Global Hint Extern 0 (_ ↔ _) => symmetry; assumption : core.
+
+Notation "(→)" := (λ A B, A → B) (only parsing) : stdpp_scope.
+Notation "( A →.)" := (λ B, A → B) (only parsing) : stdpp_scope.
+Notation "(.→ B )" := (λ A, A → B) (only parsing) : stdpp_scope.
+
+Notation "t $ r" := (t r)
+ (at level 65, right associativity, only parsing) : stdpp_scope.
+Notation "($)" := (λ f x, f x) (only parsing) : stdpp_scope.
+Notation "(.$ x )" := (λ f, f x) (only parsing) : stdpp_scope.
+
+Infix "∘" := compose : stdpp_scope.
+Notation "(∘)" := compose (only parsing) : stdpp_scope.
+Notation "( f ∘.)" := (compose f) (only parsing) : stdpp_scope.
+Notation "(.∘ f )" := (λ g, compose g f) (only parsing) : stdpp_scope.
+(** Ensure that [simpl] unfolds [id], [compose], and [flip] when fully
+applied. *)
+Global Arguments id _ _ / : assert.
+Global Arguments compose _ _ _ _ _ _ / : assert.
+Global Arguments flip _ _ _ _ _ _ / : assert.
+Global Arguments const _ _ _ _ / : assert.
+
+Definition fun_map {A A' B B'} (f: A' → A) (g: B → B') (h : A → B) : A' → B' :=
+ g ∘ h ∘ f.
+
+Global Instance id_inj {A} : Inj (=) (=) (@id A).
+Proof. intros ??; auto. Qed.
+Global Instance compose_inj {A B C} R1 R2 R3 (f : A → B) (g : B → C) :
+ Inj R1 R2 f → Inj R2 R3 g → Inj R1 R3 (g ∘ f).
+Proof. red; intuition. Qed.
+
+(** ** Products *)
+Notation "( x ,.)" := (pair x) (only parsing) : stdpp_scope.
+Notation "(., y )" := (λ x, (x,y)) (only parsing) : stdpp_scope.
+
+Notation "p .1" := (fst p) (at level 2, left associativity, format "p .1").
+Notation "p .2" := (snd p) (at level 2, left associativity, format "p .2").
+
+Definition prod_map {A A' B B'} (f: A → A') (g: B → B') (p : A * B) : A' * B' :=
+ (f (p.1), g (p.2)).
+Global Arguments prod_map {_ _ _ _} _ _ !_ / : assert.
+
+Global Instance pair_inj {A B} : Inj2 (=) (=) (=) (@pair A B).
+Proof. injection 1; auto. Qed.
+Global Instance prod_map_inj {A A' B B'} (f : A → A') (g : B → B') :
+ Inj (=) (=) f → Inj (=) (=) g → Inj (=) (=) (prod_map f g).
+Proof.
+ intros ?? [??] [??] ?; simpl in *; f_equal;
+ [apply (inj f)|apply (inj g)]; congruence.
+Qed.
+
+Definition prod_relation {A B} (R1 : relation A) (R2 : relation B) :
+ relation (A * B) := λ x y, R1 (x.1) (y.1) ∧ R2 (x.2) (y.2).
+
+Section prod_relation.
+ Context `{RA : relation A, RB : relation B}.
+ Global Instance pair_inj' : Inj2 RA RB (prod_relation RA RB) pair.
+ Proof. inversion_clear 1; eauto. Qed.
+End prod_relation.
+
+Global Instance prod_equiv `{Equiv A,Equiv B} : Equiv (A * B) :=
+ prod_relation (≡) (≡).
+
+Section prod_setoid.
+ Context `{Equiv A, Equiv B}.
+ Global Instance pair_equiv_inj : Inj2 (≡) (≡) (≡@{A*B}) pair := _.
+End prod_setoid.
+
+(* Typeclasses Opaque prod_equiv. *)
+
+(** ** Sums *)
+Definition sum_map {A A' B B'} (f: A → A') (g: B → B') (xy : A + B) : A' + B' :=
+ match xy with inl x => inl (f x) | inr y => inr (g y) end.
+Global Arguments sum_map {_ _ _ _} _ _ !_ / : assert.
+
+Global Instance inl_inj {A B} : Inj (=) (=) (@inl A B).
+Proof. injection 1; auto. Qed.
+Global Instance inr_inj {A B} : Inj (=) (=) (@inr A B).
+Proof. injection 1; auto. Qed.
+
+Global Instance sum_map_inj {A A' B B'} (f : A → A') (g : B → B') :
+ Inj (=) (=) f → Inj (=) (=) g → Inj (=) (=) (sum_map f g).
+Proof. intros ?? [?|?] [?|?] [=]; f_equal; apply (inj _); auto. Qed.
+
+Inductive sum_relation {A B}
+ (RA : relation A) (RB : relation B) : relation (A + B) :=
+ | inl_related x1 x2 : RA x1 x2 → sum_relation RA RB (inl x1) (inl x2)
+ | inr_related y1 y2 : RB y1 y2 → sum_relation RA RB (inr y1) (inr y2).
+
+Section sum_relation.
+ Context `{RA : relation A, RB : relation B}.
+ Global Instance inl_inj' : Inj RA (sum_relation RA RB) inl.
+ Proof. inversion_clear 1; auto. Qed.
+ Global Instance inr_inj' : Inj RB (sum_relation RA RB) inr.
+ Proof. inversion_clear 1; auto. Qed.
+End sum_relation.
+
+Global Instance sum_equiv `{Equiv A, Equiv B} : Equiv (A + B) := sum_relation (≡) (≡).
+
+Global Instance inl_equiv_inj `{Equiv A, Equiv B} : Inj (≡) (≡) (@inl A B) := _.
+Global Instance inr_equiv_inj `{Equiv A, Equiv B} : Inj (≡) (≡) (@inr A B) := _.
+
+Notation "` x" := (proj1_sig x) (at level 10, format "` x") : stdpp_scope.
+
+Definition f := Nat.add 0.
+Global Instance h: Inj eq eq f.
+ unfold f. simpl. easy.
+Qed.
+
+Goal Inj eq eq (compose (@id nat) id).
+apply _.
+Qed.
+
+Goal Inj eq eq (compose (compose (@id nat) id) id).
+apply _.
+Qed.
+
+(* Goal Inj eq eq (fun (x:nat) => id (id x)).
+apply _.
+Qed. *)
+
+Goal Inj eq eq (fun (x: nat) => (compose id id) (id x)).
+apply (compose_inj eq eq); apply _.
+Qed.
\ No newline at end of file
diff --git a/apps/tc/tests/test.v b/apps/tc/tests/test.v
new file mode 100644
index 000000000..8f25cef5f
--- /dev/null
+++ b/apps/tc/tests/test.v
@@ -0,0 +1,21 @@
+From elpi.apps.tc.tests Require Import stdppInj.
+Elpi TC.Solver. Set TC TimeRefine. Set TC ResolutionTime. Set Debug "elpitime".
+Elpi Accumulate TC.Solver lp:{{
+ shorten tc-elpi.apps.tc.tests.stdppInj.{tc-Inj}.
+ :after "firstHook"
+ tc-Inj A B RA RB {{@compose lp:A lp:A lp:A lp:FL lp:FL}} Sol :- !,
+ tc-Inj A B RA RB FL Sol1,
+ coq.typecheck A TA ok,
+ coq.typecheck RA TRA ok,
+ coq.typecheck FL TFL ok,
+ coq.typecheck Sol1 TSol1 ok,
+ Sol = {{
+ let a : lp:TA := lp:A in
+ let sol : lp:TSol1 := lp:Sol1 in
+ let ra : lp:TRA := lp:RA in
+ let fl : lp:TFL := lp:FL in
+ @compose_inj a a a ra ra ra fl fl sol sol}}.
+}}.
+Elpi Typecheck TC.Solver.
+
+Goal Inj eq eq((compose (compose (compose f f )(compose f f ))(compose (compose f f )(compose f f )))). Time apply _. Qed.
diff --git a/apps/tc/tests/test_commands_API.v b/apps/tc/tests/test_commands_API.v
new file mode 100644
index 000000000..f45d8d289
--- /dev/null
+++ b/apps/tc/tests/test_commands_API.v
@@ -0,0 +1,58 @@
+From elpi.apps Require Import tc.
+From elpi.apps.tc.tests Require Import eqSimplDef.
+
+Elpi Command len_test.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate lp:{{
+ pred count i:gref, i:int.
+ count GR Len :-
+ if (const _ = GR)
+ (std.findall (instance _ _ GR) Cl,
+ std.assert! ({std.length Cl} = Len)
+ "Unexpected number of instances")
+ true.
+
+ main [str E, int Len] :-
+ coq.locate E GR,
+ count GR Len.
+}}.
+Elpi Typecheck.
+
+Elpi AddClasses Eqb.
+
+Module test1.
+ Elpi AddInstances Eqb ignoreInstances eqP.
+ Elpi len_test Eqb 2.
+End test1.
+Reset test1.
+
+Module test2.
+ Elpi len_test Eqb 0.
+End test2.
+Reset test2.
+
+Module test3.
+ Elpi AddInstances Eqb.
+ Elpi len_test Eqb 3.
+End test3.
+Reset test3.
+
+
+(* About RewriteRelation.
+
+About RelationClasses.RewriteRelation.
+
+
+Elpi Query TC.Solver lp:{{
+ coq.gref->id {{:gref RelationClasses.RewriteRelation}} L.
+}}. *)
+
+Module test4.
+ Elpi AddAllClasses.
+ Elpi AddAllInstances eqU.
+
+ Elpi Query TC.Solver lp:{{
+ EqP = {{:gref eqU}},
+ std.assert! (not (instance _ EqP _)) "EqP should not be in the DB".
+ }}.
+End test4.
\ No newline at end of file
diff --git a/apps/tc/tests/test_tc.v b/apps/tc/tests/test_tc.v
new file mode 100644
index 000000000..8b4970ee3
--- /dev/null
+++ b/apps/tc/tests/test_tc.v
@@ -0,0 +1,12 @@
+From elpi.apps Require Import tc.
+
+Elpi Override TC TC.Solver All.
+
+Class a (N: nat).
+Instance b : a 3. Qed.
+Instance c : a 4. Qed.
+
+Elpi AddAllClasses.
+Elpi AddAllInstances.
+
+Goal a 4. apply _. Qed.
diff --git a/apps/tc/theories/add_commands.v b/apps/tc/theories/add_commands.v
new file mode 100644
index 000000000..6e4457582
--- /dev/null
+++ b/apps/tc/theories/add_commands.v
@@ -0,0 +1,102 @@
+(* license: GNU Lesser General Public License Version 2.1 or later *)
+(* ------------------------------------------------------------------------- *)
+
+From elpi.apps Require Import db.
+
+From elpi.apps.tc Extra Dependency "base.elpi" as base.
+From elpi.apps.tc Extra Dependency "compiler.elpi" as compiler.
+From elpi.apps.tc Extra Dependency "parser_addInstances.elpi" as parser_addInstances.
+From elpi.apps.tc Extra Dependency "solver.elpi" as solver.
+From elpi.apps.tc Extra Dependency "tc_aux.elpi" as tc_aux.
+From elpi.apps.tc Extra Dependency "create_tc_predicate.elpi" as create_tc_predicate.
+
+Elpi Command TC.AddAllInstances.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate Db tc_options.db.
+Elpi Accumulate File base.
+Elpi Accumulate File tc_aux.
+Elpi Accumulate File compiler.
+Elpi Accumulate lp:{{
+ main L :-
+ args->str-list L L1,
+ std.forall {coq.TC.db-tc} (x\ add-tc-or-inst-gr [] L1 [x]).
+}}.
+Elpi Typecheck.
+
+Elpi Command TC.AddInstances.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate Db tc_options.db.
+Elpi Accumulate File base.
+Elpi Accumulate File tc_aux.
+Elpi Accumulate File compiler.
+Elpi Accumulate File parser_addInstances.
+Elpi Accumulate lp:{{
+ main Arguments :-
+ parse Arguments Res, run-command Res.
+}}.
+Elpi Typecheck.
+
+Elpi Command TC.AddAllClasses.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate Db tc_options.db.
+Elpi Accumulate File base.
+Elpi Accumulate File tc_aux.
+Elpi Accumulate File create_tc_predicate.
+Elpi Accumulate lp:{{
+ main _ :-
+ coq.TC.db-tc TC,
+ std.forall TC (add-class-gr classic).
+}}.
+Elpi Typecheck.
+
+Elpi Command TC.AddClasses.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate Db tc_options.db.
+Elpi Accumulate File base.
+Elpi Accumulate File tc_aux.
+Elpi Accumulate File create_tc_predicate.
+Elpi Accumulate lp:{{
+ main L :-
+ std.mem {attributes} (attribute "deterministic" _),
+ std.forall {args->str-list L} (add-class-str deterministic).
+ main L :- std.forall {args->str-list L} (add-class-str classic).
+ main _ :- coq.error "This commands accepts: [classic|deterministic]? TC-names*".
+}}.
+Elpi Typecheck.
+
+Elpi Command TC.AddHook.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate Db tc_options.db.
+Elpi Accumulate File base.
+Elpi Accumulate File tc_aux.
+Elpi Accumulate lp:{{
+ pred addHook i:grafting, i:string.
+ addHook Grafting NewName :-
+ @global! => add-tc-db NewName Grafting (hook NewName).
+
+ main [str "before", str OldHook, str NewHook] :-
+ addHook (before OldHook) NewHook.
+
+ main [str "after", str OldHook, str NewHook] :-
+ addHook (after OldHook) NewHook.
+
+ main [Graft, int OldHook, NewHook] :-
+ main [Graft, str {calc (int_to_string OldHook)}, NewHook].
+
+ main [Graft, OldHook, int NewHook] :-
+ main [Graft, OldHook, str {calc (int_to_string NewHook)}].
+
+ main _ :-
+ coq.error "Invalid call to command AddHook. A valid call looks like"
+ "[ElpiAddHook Pos OldName NewName] where:"
+ " - Pos is either after or before"
+ " - OldName is the name of an existing hook"
+ " - NewName is the name of the new hook".
+}}.
+Elpi Typecheck.
+
+Elpi Export TC.AddAllClasses.
+Elpi Export TC.AddAllInstances.
+Elpi Export TC.AddClasses.
+Elpi Export TC.AddInstances.
+Elpi Export TC.AddHook.
\ No newline at end of file
diff --git a/apps/tc/theories/db.v b/apps/tc/theories/db.v
new file mode 100644
index 000000000..2e76b2ef3
--- /dev/null
+++ b/apps/tc/theories/db.v
@@ -0,0 +1,64 @@
+(* license: GNU Lesser General Public License Version 2.1 or later *)
+(* ------------------------------------------------------------------------- *)
+
+From elpi Require Import elpi.
+
+(*
+ tc_option.db contains the set of options used by the solver of tc.
+ all the options are set to false by default
+*)
+Elpi Db tc_options.db lp:{{
+ pred oTC-ignore-eta-reduction o:list string.
+ oTC-ignore-eta-reduction ["TC", "IgnoreEtaReduction"].
+
+ pred oTC-resolution-time o:list string.
+ oTC-resolution-time ["TC", "ResolutionTime"].
+
+ pred oTC-clauseNameShortName o:list string.
+ oTC-clauseNameShortName ["TC", "NameShortPath"].
+
+ pred oTC-time-refine o:list string.
+ oTC-time-refine ["TC", "TimeRefine"].
+
+ pred oTC-debug o:list string.
+ oTC-debug ["TC", "Debug"].
+
+ pred oTC-addModes o:list string.
+ oTC-addModes ["TC", "AddModes"].
+
+ pred oTC-use-pattern-fragment-compiler o:list string.
+ oTC-use-pattern-fragment-compiler ["TC", "CompilerWithPatternFragment"].
+
+ pred is-option-active i:list string.
+ is-option-active Opt :-
+ coq.option.get Opt (coq.option.bool tt).
+}}.
+
+Elpi Db tc.db lp:{{
+ % the type of search for a typeclass
+ % deterministic :- no backtrack after having found a solution/fail
+ % classic :- the classic search, if a path is failing, we backtrack
+ kind search-mode type.
+ type deterministic search-mode.
+ type classic search-mode.
+
+ % [instance Path InstGR ClassGR], ClassGR is the class implemented by InstGR
+ pred instance o:list string, o:gref, o:gref.
+
+ % [class ClassGR PredName SearchMode], for each class GR, it contains
+ % the name of its predicate and its SearchMode
+ pred class o:gref, o:string, o:search-mode.
+
+ % pred on which we graft instances in the database
+ pred hook o:string.
+ :name "firstHook" hook "firstHook".
+ :name "lastHook" hook "lastHook".
+
+ % the set of instances that we are not yet able to compile,
+ % in majority they use universe polimorphism
+ pred banned o:gref.
+
+ % [tc-signature TC Modes], returns for each Typeclass TC
+ % its Modes
+ pred tc-mode i:gref, o:list (pair argument_mode string).
+}}.
\ No newline at end of file
diff --git a/apps/tc/theories/tc.v b/apps/tc/theories/tc.v
new file mode 100644
index 000000000..94f98e0f4
--- /dev/null
+++ b/apps/tc/theories/tc.v
@@ -0,0 +1,127 @@
+(* license: GNU Lesser General Public License Version 2.1 or later *)
+(* ------------------------------------------------------------------------- *)
+
+Declare ML Module "coq-elpi-tc.plugin".
+
+From elpi.apps.tc Extra Dependency "base.elpi" as base.
+From elpi.apps.tc Extra Dependency "compiler.elpi" as compiler.
+From elpi.apps.tc Extra Dependency "parser_addInstances.elpi" as parser_addInstances.
+From elpi.apps.tc Extra Dependency "solver.elpi" as solver.
+From elpi.apps.tc Extra Dependency "tc_aux.elpi" as tc_aux.
+From elpi.apps.tc Extra Dependency "create_tc_predicate.elpi" as create_tc_predicate.
+
+From elpi.apps Require Import db.
+From elpi.apps Require Export add_commands.
+
+Elpi Command TC.Print_instances.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate lp:{{
+ pred list-printer i:gref, i:list prop.
+ list-printer _ [].
+ list-printer ClassGR Instances :-
+ std.map Instances (x\r\ x = instance _ r _) InstancesGR,
+ coq.say "Instances list for" ClassGR "is:",
+ std.forall InstancesGR (x\ coq.say " " x).
+
+ main [str Class] :-
+ std.assert! (coq.locate Class ClassGR) "The entered TC not exists",
+ std.findall (instance _ _ ClassGR) Rules,
+ list-printer ClassGR Rules.
+ main [] :-
+ std.forall {coq.TC.db-tc} (ClassGR\ sigma Rules\
+ std.findall (instance _ _ ClassGR) Rules,
+ list-printer ClassGR Rules
+ ).
+}}.
+Elpi Typecheck.
+
+Elpi Tactic TC.Solver.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate Db tc_options.db.
+Elpi Accumulate File base.
+Elpi Accumulate File tc_aux.
+Elpi Accumulate File compiler.
+Elpi Accumulate File create_tc_predicate.
+Elpi Accumulate File solver.
+Elpi Query lp:{{
+ sigma Options\
+ Options = [oTC-ignore-eta-reduction, oTC-resolution-time,
+ oTC-clauseNameShortName, oTC-time-refine, oTC-debug, oTC-addModes,
+ oTC-use-pattern-fragment-compiler],
+ std.forall Options (x\ sigma Args\ x Args,
+ coq.option.add Args (coq.option.bool ff) ff).
+}}.
+Elpi Typecheck.
+
+Elpi Query lp:{{
+ sigma Nums\
+ std.iota 1001 Nums,
+ std.forall Nums (x\ sigma NumStr\
+ NumStr is int_to_string x,
+ @global! => add-tc-db NumStr (before "lastHook") (hook NumStr)
+ )
+}}.
+
+Elpi Command TC.Compiler.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate Db tc_options.db.
+Elpi Accumulate File base.
+Elpi Accumulate File tc_aux.
+Elpi Accumulate File create_tc_predicate.
+Elpi Accumulate File compiler.
+Elpi Accumulate lp:{{
+ main [str Inst, str Cl, str Locality, int Prio] :- !,
+ % coq.safe-dest-app Inst (global GRInst) _,
+ % coq.safe-dest-app Cl (global GRCl) _,
+ coq.locate Cl GRCl,
+ coq.locate Inst GRInst,
+ add-inst GRInst GRCl Locality Prio.
+
+ main [str Cl] :- !,
+ % coq.safe-dest-app Cl (global GR) _,
+ coq.locate Cl GR,
+ add-class-gr classic GR.
+
+ main A :- coq.error "Fail in TC.Compiler: not a valid input entry" A.
+}}.
+Elpi Typecheck.
+
+(* Command allowing to set if a TC is deterministic. *)
+Elpi Command TC.Set_deterministic.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate Db tc_options.db.
+Elpi Accumulate File base.
+Elpi Accumulate File tc_aux.
+Elpi Accumulate lp:{{
+ main [str ClStr] :-
+ coq.locate ClStr Gr,
+ std.assert! (coq.TC.class? Gr) "Should pass the name of a type class",
+ std.assert! (class Gr PredName _) "Cannot find `class GR _ _` in the db",
+ add-tc-db _ (after "0") (class Gr PredName deterministic :- !).
+}}.
+Elpi Typecheck.
+
+Elpi Command TC.Get_class_info.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate lp:{{
+ main [str ClassStr] :-
+ coq.locate ClassStr ClassGR,
+ class ClassGR PredName SearchMode,
+ coq.say "The predicate of" ClassGR "is" PredName "and search mode is" SearchMode.
+ main [str C] :- coq.error C "is not found in elpi db".
+ main [A] :- std.assert! (str _ = A) true "first argument should be a str".
+ main [_|_] :- coq.error "get_class_info accepts only one argument of type str".
+ main L :- coq.error "Uncaught error on input" L.
+}}.
+Elpi Override TC TC.Solver All.
+
+Elpi Register TC Compiler TC.Compiler.
+
+Elpi Export TC.Print_instances.
+Elpi Export TC.Solver.
+Elpi Export TC.Compiler.
+Elpi Export TC.Get_class_info.
+Elpi Export TC.Set_deterministic.
+
+Elpi TC.AddAllClasses.
+Elpi TC.AddAllInstances.
\ No newline at end of file
diff --git a/apps/tc/theories/wip.v b/apps/tc/theories/wip.v
new file mode 100644
index 000000000..a676988a6
--- /dev/null
+++ b/apps/tc/theories/wip.v
@@ -0,0 +1,77 @@
+(* license: GNU Lesser General Public License Version 2.1 or later *)
+(* --------------------------------------------------------------------------*)
+
+Declare ML Module "coq-elpi-tc.plugin".
+From elpi Require Import elpi.
+
+From elpi.apps.tc Extra Dependency "base.elpi" as base.
+From elpi.apps.tc Extra Dependency "compiler.elpi" as compiler.
+From elpi.apps.tc Extra Dependency "parser_addInstances.elpi" as parser_addInstances.
+From elpi.apps.tc Extra Dependency "alias.elpi" as alias.
+From elpi.apps.tc Extra Dependency "solver.elpi" as solver.
+From elpi.apps.tc Extra Dependency "rewrite_forward.elpi" as rforward.
+From elpi.apps.tc Extra Dependency "tc_aux.elpi" as tc_aux.
+From elpi.apps.tc Extra Dependency "create_tc_predicate.elpi" as create_tc_predicate.
+
+From elpi.apps Require Import tc.
+
+Elpi Command AddForwardRewriting.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate Db tc_options.db.
+Elpi Accumulate File base.
+Elpi Accumulate File tc_aux.
+Elpi Accumulate File compiler.
+Elpi Accumulate File create_tc_predicate.
+Elpi Accumulate File solver.
+Elpi Accumulate File tc_aux.
+Elpi Accumulate File rforward.
+Elpi Accumulate lp:{{
+ :before "build-context-clauses"
+ build-context-clauses Ctx Clauses :- !,
+ std.map {coq.env.section}
+ (x\r\ sigma F\ coq.env.typeof (const x) F,
+ r = (decl (global (const x)) _ F)) SectionCtx,
+ std.append Ctx SectionCtx CtxAndSection,
+ compile-ctx {rewrite-dep CtxAndSection} Clauses.
+
+ main L :-
+ std.forall {args->str-list L} add-lemma->forward.
+}}.
+Elpi Typecheck.
+
+Elpi Command AddAlias.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate Db tc_options.db.
+Elpi Accumulate File base.
+Elpi Accumulate File tc_aux.
+Elpi Accumulate File alias.
+Elpi Accumulate lp:{{
+
+ main [trm New, trm Old] :-
+ add-tc-db _ _ (alias New Old).
+}}.
+Elpi Typecheck.
+
+
+(*
+Elpi Command TC.declare.
+Elpi Accumulate Db tc.db.
+Elpi Accumulate Db tc_options.db.
+Elpi Accumulate File base.
+Elpi Accumulate File tc_aux.
+Elpi Accumulate File create_tc_predicate.
+Elpi Accumulate lp:{{
+ main [indt-decl D] :-
+ attributes A,
+ coq.say A,
+ Opts =>
+ get-option "modes" [tt,ff,tt],
+ coq.env.add-indt D I,
+ coq.say I.
+}}.
+Elpi Typecheck.
+Elpi Export TC.declare.
+
+#[mode(i,i,o)] TC.declare
+ Class foo (A : Type) .
+*)
diff --git a/coq-builtin.elpi b/coq-builtin.elpi
index f0fd40a23..9787d4f50 100644
--- a/coq-builtin.elpi
+++ b/coq-builtin.elpi
@@ -1125,9 +1125,18 @@ external pred coq.CS.db-for i:gref, i:cs-pattern, o:list cs-instance.
% [coq.TC.declare-class GR] Declare GR as a type class
external pred coq.TC.declare-class i:gref.
+% [coq.elpi.toposort Graph Nodes in toposort order] takes a graph and
+% returns the nodes in topological order
+external pred coq.elpi.toposort i:list (pair A (list A)), o:list A.
+
+% Type class instance priority
+kind tc-priority type.
+type tc-priority-given int -> tc-priority. % User given priority
+type tc-priority-computed int -> tc-priority. % Coq computed priority
+
% Type class instance with priority
kind tc-instance type.
-type tc-instance gref -> int -> tc-instance.
+type tc-instance gref -> tc-priority -> tc-instance.
% [coq.TC.declare-instance GR Priority] Declare GR as a Global type class
% instance with Priority.
@@ -1141,7 +1150,8 @@ external pred coq.TC.db o:list tc-instance.
% [coq.TC.db-tc TypeClasses] reads all type classes
external pred coq.TC.db-tc o:list gref.
-% [coq.TC.db-for GR Db] reads all instances of the given class GR
+% [coq.TC.db-for GR InstanceList] reads all instances of the given class GR.
+% Instances are in their precedence order.
external pred coq.TC.db-for i:gref, o:list tc-instance.
% [coq.TC.class? GR] checks if GR is a class
diff --git a/src/coq_elpi_builtins.ml b/src/coq_elpi_builtins.ml
index 28a45a10b..d09bb7931 100644
--- a/src/coq_elpi_builtins.ml
+++ b/src/coq_elpi_builtins.ml
@@ -442,17 +442,95 @@ let cs_instance = let open Conv in let open API.AlgebraicData in let open Struct
]
} |> CConv.(!<)
-let tc_instance = let open Conv in let open API.AlgebraicData in let open Typeclasses in declare {
+
+type tc_priority = Computed of int | UserGiven of int
+
+let tc_priority = let open Conv in let open API.AlgebraicData in declare {
+ ty = TyName "tc-priority";
+ doc = "Type class instance priority";
+ pp = (fun fmt _ -> Format.fprintf fmt "");
+ constructors = [
+ K("tc-priority-given","User given priority",A(int,N),
+ B (fun i -> UserGiven i),
+ M (fun ~ok ~ko -> function UserGiven i -> ok i | _ -> ko ()));
+ K("tc-priority-computed","Coq computed priority", A(int,N),
+ B (fun i -> Computed i),
+ M (fun ~ok ~ko -> function Computed i -> ok i | _ -> ko ()));
+]} |> CConv.(!<)
+
+type type_class_instance = {
+ implementation : GlobRef.t;
+ priority : tc_priority;
+}
+
+let tc_instance = let open Conv in let open API.AlgebraicData in declare {
ty = TyName "tc-instance";
doc = "Type class instance with priority";
pp = (fun fmt _ -> Format.fprintf fmt "");
constructors = [
- K("tc-instance","",A(gref,A(int,N)),
- B (fun g p -> nYI "lp2instance"),
- M (fun ~ok ~ko i ->
- ok (instance_impl i) (Option.default 0 (hint_priority i))));
+ K("tc-instance","",A(gref,A(tc_priority,N)),
+ B (fun implementation priority -> { implementation; priority }),
+ M (fun ~ok ~ko { implementation; priority } -> ok implementation priority));
]} |> CConv.(!<)
+let get_instance_prio gr env sigma (info : 'a Typeclasses.hint_info_gen) : tc_priority =
+ match info.hint_priority with
+ | Some p -> UserGiven p
+ | None ->
+ let rec nb_hyp sigma c = match EConstr.kind sigma c with
+ | Prod(_,_,c2) -> if EConstr.Vars.noccurn sigma 1 c2 then 1+(nb_hyp sigma c2) else nb_hyp sigma c2
+ | _ -> 0 in
+ let merge_context_set_opt sigma ctx =
+ match ctx with
+ | None -> sigma
+ | Some ctx -> Evd.merge_context_set Evd.univ_flexible sigma ctx
+ in
+ let fresh_global_or_constr env sigma =
+ let (c, ctx) = UnivGen.fresh_global_instance env gr in
+ let ctx = if Environ.is_polymorphic env gr then Some ctx else None in
+ (EConstr.of_constr c, ctx) in
+ let c, ctx = fresh_global_or_constr env sigma in
+ let cty = Retyping.get_type_of env sigma c in
+ let cty = Reductionops.nf_betaiota env sigma cty in
+ let sigma' = merge_context_set_opt sigma ctx in
+ let ce = Clenv.mk_clenv_from env sigma' (c,cty) in
+ let miss = Clenv.clenv_missing ce in
+ let nmiss = List.length miss in
+ let hyps = nb_hyp sigma' cty in
+ Computed (hyps + nmiss)
+
+let get_instances (env: Environ.env) (emap: Evd.evar_map) tc : type_class_instance list =
+ let hint_db = Hints.searchtable_map "typeclass_instances" in
+ let secvars : Names.Id.Pred.t = Names.Id.Pred.full in
+ let full_hints = Hints.Hint_db.map_all ~secvars:secvars tc hint_db in
+ let hint_asts = List.map Hints.FullHint.repr full_hints in
+ let hints = List.filter_map (function
+ | Hints.Res_pf a -> Some a
+ | ERes_pf a -> Some a
+ | Extern (a, b) -> None
+ | Give_exact a -> Some a
+ | Res_pf_THEN_trivial_fail e -> Some e
+ | Unfold_nth e -> None) hint_asts in
+ let sigma, _ =
+ let env = Global.env () in Evd.(from_env env, env) in
+ let constrs = List.map (fun a -> Hints.hint_as_term a |> snd) hints in
+ (* Printer.pr_global tc |> Pp.string_of_ppcmds |> Printf.printf "%s\n"; *)
+ let instances_grefs = List.filter_map (fun e ->
+ match EConstr.kind sigma e with
+ | Constr.Ind (a, _) -> Some (Names.GlobRef.IndRef a)
+ | Constr.Const (a, _) -> Some (Names.GlobRef.ConstRef a)
+ | Constr.Construct (a, _) -> Some (Names.GlobRef.ConstructRef a)
+ | _ -> None) constrs in
+ let inst_of_tc =
+ Typeclasses.instances_exn env emap tc |>
+ List.fold_left (fun m i -> GlobRef.Map.add i.Typeclasses.is_impl i m) GlobRef.Map.empty in
+ let instances_grefs2istance x =
+ let open Typeclasses in
+ let inst = GlobRef.Map.find x inst_of_tc in
+ let priority = get_instance_prio x env sigma inst.is_info in
+ { implementation = x; priority } in
+ List.map instances_grefs2istance instances_grefs
+
type scope = ExecutionSite | CurrentModule | Library
let scope = let open Conv in let open API.AlgebraicData in declare {
@@ -1235,8 +1313,6 @@ let eta_contract env sigma t =
map env t
-
-
(*****************************************************************************)
(*****************************************************************************)
(*****************************************************************************)
@@ -2688,6 +2764,18 @@ Supported attributes:
state, (), []))),
DocAbove);
+ MLCode(Pred("coq.elpi.toposort",
+ In(B.list (pair (B.poly "A") (B.list (B.poly "A"))), "Graph",
+ Out(B.list (B.poly "A"), "Nodes in toposort order",
+ Read(global,"takes a graph and returns the nodes in topological order"))),
+ (fun graph _ ~depth { options } _ _ ->
+ let graph = Coq_elpi_graph.Graph.build graph in
+ let topo_sort = Coq_elpi_graph.Graph.topo_sort graph in
+ (* Coq_elpi_graph.Graph.print string_of_int graph; *)
+ !: topo_sort)),
+ DocAbove);
+
+ MLData tc_priority;
MLData tc_instance;
MLCode(Pred("coq.TC.declare-instance",
@@ -2706,8 +2794,12 @@ Supported attributes:
MLCode(Pred("coq.TC.db",
Out(list tc_instance, "Instances",
- Easy "reads all type class instances"),
- (fun _ ~depth -> !: (Typeclasses.all_instances ()))),
+ Read(global, "reads all type class instances")),
+ (fun _ ~depth { env } _ state ->
+ let sigma = get_sigma state in
+ let x = Typeclasses.typeclasses () in
+ let classes = List.map (fun x -> x.Typeclasses.cl_impl) x in
+ !: (classes |> List.map (get_instances env sigma) |> List.concat))),
DocAbove);
MLCode(Pred("coq.TC.db-tc",
@@ -2719,12 +2811,11 @@ Supported attributes:
l))),
DocAbove);
- MLCode(Pred("coq.TC.db-for",
+ MLCode(Pred("coq.TC.db-for",
In(gref, "GR",
- Out(list tc_instance, "Db",
- Read(global,"reads all instances of the given class GR"))),
- (fun gr _ ~depth { env } _ state ->
- !: (Typeclasses.instances_exn env (get_sigma state) gr))),
+ Out(list tc_instance, "InstanceList",
+ Read (global, "reads all instances of the given class GR. Instances are in their precedence order."))),
+ (fun gr _ ~depth { env } _ state -> !: (get_instances env (get_sigma state) gr))),
DocAbove);
MLCode(Pred("coq.TC.class?",
diff --git a/src/coq_elpi_graph.ml b/src/coq_elpi_graph.ml
new file mode 100644
index 000000000..8ce0b0270
--- /dev/null
+++ b/src/coq_elpi_graph.ml
@@ -0,0 +1,97 @@
+module Node = struct
+ type 'a t = {
+ mutable pred : 'a t list;
+ name : 'a;
+ mutable succ : 'a t list;
+ }
+
+ let init name = { pred = []; name; succ = [] }
+
+ let remove_succ rem_node n =
+ n.succ <- List.filter (fun succ -> succ.name <> rem_node.name) n.succ
+
+ let add_succ current succ =
+ current.succ <- succ :: current.succ;
+ succ.pred <- current :: succ.pred
+
+ let remove_pred rem_node n =
+ n.pred <- List.filter (fun pred -> pred.name <> rem_node.name) n.pred
+
+ let clear node = node.pred <- []; node.succ <- []
+
+ let remove current =
+ List.iter (fun succ -> remove_pred current succ) current.succ;
+ List.iter (fun pred -> remove_succ current pred) current.pred;
+ clear current
+
+ let print pf n =
+ let pf e = Printf.sprintf "%s " (pf e.name) in
+ let pf_fold e = List.fold_left (fun acc e -> Printf.sprintf "%s%s, " acc (pf e)) "" e in
+ Printf.printf "%s : [ succ: %s ;; pred : %s ]\n%!"
+ (pf n) (pf_fold n.succ) (pf_fold n.pred);
+end
+
+module Graph = struct
+
+ type 'a graph = { nodes : ('a, 'a Node.t) Hashtbl.t }
+
+ exception Invalid_graph of string
+
+ let rec queue_to_list q =
+ if Queue.is_empty q then []
+ else
+ let elt = Queue.pop q in
+ elt :: queue_to_list q
+
+ let add_node graph node_name =
+ if Hashtbl.mem graph.nodes node_name then
+ raise (Invalid_graph "The nodes of the graph should be unique")
+ else
+ let node = Node.init node_name in
+ Hashtbl.add graph.nodes node_name node;
+ node
+
+ (*
+ We can build the graph from a list of type (A, B) : ('a * 'a list)
+ where A is a node and B is the list of its successors
+ *)
+ let build l : 'a graph =
+ let graph = { nodes = Hashtbl.create (List.length l) } in
+ List.iter (fun (node, _) -> add_node graph node |> ignore) l;
+ List.iter
+ (fun (current_name, succ_names) ->
+ let current = Hashtbl.find graph.nodes current_name in
+ List.iter
+ (fun succ_name ->
+ let succ =
+ try Hashtbl.find graph.nodes succ_name
+ with Not_found -> add_node graph succ_name in
+ Node.add_succ current succ
+ )
+ succ_names)
+ l;
+ graph
+
+ let topo_sort graph : 'a list =
+ let res = Queue.create () in
+ let to_treat = Queue.create () in
+ Hashtbl.iter
+ (fun _ (n: 'a Node.t) -> if List.length n.pred = 0 then Queue.add n to_treat)
+ graph.nodes;
+ while Queue.is_empty to_treat |> not do
+ let current_node = Queue.pop to_treat in
+ Queue.push current_node.name res;
+ let succ = current_node.succ in
+ Node.remove current_node;
+ List.iter
+ (fun (succ: 'a Node.t) -> if List.length succ.pred = 0 then Queue.push succ to_treat)
+ succ
+ done;
+ if Queue.length res <> Hashtbl.length graph.nodes then
+ raise (Invalid_graph "Cannot do topological sort on cyclic graph")
+ else queue_to_list res
+
+ let print pf g =
+ Hashtbl.iter (fun _ n -> Node.print pf n)
+ g.nodes
+end
\ No newline at end of file
diff --git a/src/coq_elpi_graph.mli b/src/coq_elpi_graph.mli
new file mode 100644
index 000000000..98c265aed
--- /dev/null
+++ b/src/coq_elpi_graph.mli
@@ -0,0 +1,8 @@
+module Graph :
+ sig
+ type 'a graph
+ exception Invalid_graph of string
+ val build : ('a * 'a list) list -> 'a graph
+ val topo_sort : 'a graph -> 'a list
+ val print : ('a -> string) -> 'a graph -> unit
+ end
diff --git a/src/elpi_plugin.mlpack b/src/elpi_plugin.mlpack
index 03435ace6..7c8692070 100644
--- a/src/elpi_plugin.mlpack
+++ b/src/elpi_plugin.mlpack
@@ -1,5 +1,6 @@
Coq_elpi_config
Coq_elpi_utils
+Coq_elpi_graph
Coq_elpi_HOAS
Coq_elpi_glob_quotation
Coq_elpi_name_quotation
diff --git a/tests/test_toposort.v b/tests/test_toposort.v
new file mode 100644
index 000000000..d983fedb3
--- /dev/null
+++ b/tests/test_toposort.v
@@ -0,0 +1,8 @@
+From elpi Require Import elpi.
+
+Elpi Command GraphToposort.
+
+Elpi Query lp:{{
+ coq.elpi.toposort [pr "a" ["b"], pr "c" ["a"]] ["c", "a", "b"],
+ coq.elpi.toposort [pr 1 [2], pr 3 [1]] [3, 1, 2].
+}}.