-
Co-authored-by:
Rodolphe Lepigre <lepigre@mpi-sws.org>
Co-authored-by:
Rodolphe Lepigre <lepigre@mpi-sws.org>
- The type system gets stuck on a sidecondition which contains an evar (i.e. protected (...)), what should I do?
- How do I add additional simplification rules?
- How should I structure my annotations such that the automatic instantiation mechanism for evars works well?
- How do I debug the simplification mechanism?
- Why does ContainsProtected contain an evar?
- Why don't I get as an hypothesis that an integer parameter is in range?
The type system gets stuck on a sidecondition which contains an evar (i.e. protected (...)), what should I do?
Sometimes the automation gets stuck on a goal of the form P ∧ ...
where P
is some pure proposition that contains evars (marked with
protected
). When such a goal is reached, the system automatically
applies simplification rules in the hope of reducing P
to an
equality x = y
which can be solved by unifying x
and y
. If the
type system does not know how to further simplify P
, but it is not
yet reduced to an unifiable equality, it gets stuck. In this case
there are two options:
- Add additional simplification rules to further simplify
P
and reduce it to an equality - Restructure the annotations such that it becomes easier for the type system to figure out the instantiation
Currently RefinedC does not provide a way to give a manual instantiation of existential quantifiers, but it can be added if necessary.
See further points in this FAQ for explanation of these options.
How do I add additional simplification rules?
The simplification rules can be extended by the user through special
typeclasses such as SimplBoth
, SimplAnd
and SimplImpl
. See the
file theories/typing/automation.v
for the definition and many
instances. Keep in mind the simplification rules should in general be
bi-implications to avoid accidentally turning a provable goal into an
unprovable.
How should I structure my annotations such that the automatic instantiation mechanism for evars works well?
Automatic instantiation of evars is a tricky problem. Some tools like Verifast put syntactic restrictions on where evars can be introduced to guarantee that they can always be automatically instantiated. RefinedC does not enforce such restrictions and allows evars to be basically used everywhere, but in turn it cannot give guarantees that it will always be able to automatically figure out the right way to instantiate these evars. Thus it is important to keep this problem in mind when writing complex RefinedC annotations which involve evars. In particular, one should think of the following:
-
Have a sense where evars will arise during type checking. The most common causes for evars are the
∃ x. ty
type andexists
clauses of loop invariants. Sometimes (but not so often) the implicit existential quantifier in a refinement type without a refinement can also cause problems. Note that the first occurence of an evar will usually instantiate it so later occurences don't have to be considered. -
Have a strategy how these evars should be instantiated. The best (and most common) way to force the instantiation of an evar is to directly use it as a refinement (we call such evars good evars). For example,
∃ x. x @ int<size_t>
is a good evar and it will be very easily instantiated sincesubsume (n @ int<size_t>) (?x @ int<size_t>)
gets reduced ton = ?x
which instantiates?x
ton
.∃ x. (x + 1) @ int<size_t>
is not a good evar because the unification problem will become the more trickyn = ?x + 1
where one needs to use a simplification rule. In general, you should try to ensure that all your evars are good evars. This might require restructuring annotations and adding new evars or constraints. For example, this is not a good evar:∃ l. (length l) @ int<size_t>, {something which uses l}
and should be rewritten to
∃ x l. x @ int<size_t>, {something which uses l} & {length l = x}
If is is not possible to restructure the annotations to turn all evars into good evars (this can happen e.g. arrays), it is still important to avoid partial instantion of evars as this will require simplification rules. For example, instead of
∃ l, array<size_t, l.*1 `at_type` int<size_t>>, {something which uses l}
one should write
∃ l1 l2, array<size_t, l1 `at_type` int<size_t>>, {something which uses zip l1 l2**
to avoid needing to partially instantiate
?l
to something likezip l ?l2
.
The main take-away is the following: Prefer many small evars with a clear path to instantiation and linked with additional constraints to few large evars which require complicated reasoning to figure out how they should be instantiated.
How do I debug the simplification mechanism?
When adding such simplification rules, the system may still get stuck and it may be useful to understand why. To this aim, you can step through the proof manually until it gets stuck
repeat do_step; do_finish.
and then enable typeclass debugging.
Set Typeclasses Debug.
(*Set Typeclasses Debug Verbosity 2.*)
try do_step.
Another option is to apply the instances manually. To start with, use the following tactic, and then apply the tactics mentionned in the debugging log.
notypeclasses refine (apply_simpl_and _ _ _ _ _)
ContainsProtected
contain an evar?
Why does Simplification rules will sometimes have an argument of the following form:
`{!ContainsProtected (some coq term)}
Do not forget the !
here. Otherwise weird things happen.
Why don't I get as an hypothesis that an integer parameter is in range?
The hypotheses that the integer parameters are in range are only added to the context on the first time the parameter is accessed. If such an hypothesis is required prior to a first access, you can use the following macro to make it available.
rc_unfold_int(i);