Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 25eba52

Browse files
committedJun 8, 2023
Ltac2 preterm antiquotation $preterm:x
1 parent 6e739ce commit 25eba52

File tree

13 files changed

+302
-49
lines changed

13 files changed

+302
-49
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
overlay serapi https://github.com/SkySkimmer/coq-serapi tac2-preterm-antiquot 17359
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
- **Added:**
2+
Ltac2 preterm antiquotation `$preterm:`
3+
(`#17359 <https://github.com/coq/coq/pull/17359>`_,
4+
fixes `#13977 <https://github.com/coq/coq/issues/13977>`_,
5+
by Gaëtan Gilbert).

‎doc/sphinx/proof-engine/ltac2.rst

+10
Original file line numberDiff line numberDiff line change
@@ -745,10 +745,20 @@ the notation section.
745745

746746
.. prodn:: term += $@lident
747747

748+
or equivalently
749+
750+
.. prodn:: term += $constr:@lident
751+
748752
In a Coq term, writing :g:`$x` is semantically equivalent to
749753
:g:`ltac2:(Control.refine (fun () => x))`, up to re-typechecking. It allows to
750754
insert in a concise way an Ltac2 variable of type :n:`constr` into a Coq term.
751755

756+
Similarly variables of type `preterm` have an antiquotation
757+
758+
.. prodn:: term += $preterm:@lident
759+
760+
It is equivalent to pretyping the preterm with the appropriate typing constraint.
761+
752762
Match over terms
753763
~~~~~~~~~~~~~~~~
754764

‎plugins/ltac2/g_ltac2.mlg

+19-2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ let test_dollar_ident =
6565
lk_kw "$" >> lk_ident >> check_no_space
6666
end
6767

68+
let test_dollar_ident_colon_ident =
69+
let open Pcoq.Lookahead in
70+
to_entry "test_dollar_ident_colon_ident" begin
71+
lk_kw "$" >> lk_ident >> lk_kw ":" >> lk_ident >> check_no_space
72+
end
73+
6874
let test_ltac1_env =
6975
let open Pcoq.Lookahead in
7076
to_entry "test_ltac1_env" begin
@@ -939,8 +945,19 @@ let rules = [
939945
Production.make
940946
(Rule.stop ++ Symbol.nterm test_dollar_ident ++ Symbol.token (PKEYWORD "$") ++ Symbol.nterm Prim.ident)
941947
begin fun id _ _ loc ->
942-
let id = Loc.tag ~loc id in
943-
let arg = Genarg.in_gen (Genarg.rawwit Tac2env.wit_ltac2_quotation) id in
948+
let id = CAst.make ~loc id in
949+
let arg = Genarg.in_gen (Genarg.rawwit Tac2env.wit_ltac2_var_quotation) (None, id) in
950+
CAst.make ~loc (CGenarg arg)
951+
end
952+
);
953+
954+
Pcoq.(
955+
Production.make
956+
(Rule.stop ++ Symbol.nterm test_dollar_ident_colon_ident ++
957+
Symbol.token (PKEYWORD "$") ++ Symbol.nterm Prim.identref ++
958+
Symbol.token (PKEYWORD ":") ++ Symbol.nterm Prim.identref)
959+
begin fun id _ kind _ _ loc ->
960+
let arg = Genarg.in_gen (Genarg.rawwit Tac2env.wit_ltac2_var_quotation) (Some kind, id) in
944961
CAst.make ~loc (CGenarg arg)
945962
end
946963
);

‎plugins/ltac2/tac2core.ml

+79-23
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,18 @@ let open_constr_no_classes_flags =
4343
polymorphic = false;
4444
}
4545

46+
let preterm_flags =
47+
let open Pretyping in
48+
{
49+
use_coercions = true;
50+
use_typeclasses = Pretyping.NoUseTC;
51+
solve_unification_constraints = true;
52+
fail_evar = false;
53+
expand_evars = false;
54+
program_mode = false;
55+
polymorphic = false;
56+
}
57+
4658
(** Standard values *)
4759

4860
module Value = Tac2ffi
@@ -1641,8 +1653,12 @@ let intern_constr self ist c =
16411653
let (_, (c, _)) = Genintern.intern Stdarg.wit_constr ist c in
16421654
let v = match DAst.get c with
16431655
| GGenarg (GenArg (Glbwit tag, v)) ->
1644-
begin match genarg_type_eq tag wit_ltac2_quotation with
1645-
| Some Refl -> GlbTacexpr (GTacVar v)
1656+
begin match genarg_type_eq tag wit_ltac2_var_quotation with
1657+
| Some Refl ->
1658+
begin match (fst v) with
1659+
| ConstrVar -> GlbTacexpr (GTacVar (snd v))
1660+
| _ -> GlbVal c
1661+
end
16461662
| None -> GlbVal c
16471663
end
16481664
| _ -> GlbVal c
@@ -1904,32 +1920,72 @@ let () =
19041920
in
19051921
GlobEnv.register_constr_interp0 wit_ltac2_constr interp
19061922

1923+
let interp_constr_var_as_constr ?loc env sigma tycon id =
1924+
let ist = Tac2interp.get_env @@ GlobEnv.lfun env in
1925+
let env = GlobEnv.renamed_env env in
1926+
let c = Id.Map.find id ist.env_ist in
1927+
let c = Value.to_constr c in
1928+
let t = Retyping.get_type_of env sigma c in
1929+
let j = { Environ.uj_val = c; uj_type = t } in
1930+
match tycon with
1931+
| None ->
1932+
j, sigma
1933+
| Some ty ->
1934+
let sigma =
1935+
try Evarconv.unify_leq_delay env sigma t ty
1936+
with Evarconv.UnableToUnify (sigma,e) ->
1937+
Pretype_errors.error_actual_type ?loc env sigma j ty e
1938+
in
1939+
{j with Environ.uj_type = ty}, sigma
1940+
1941+
let interp_preterm_var_as_constr ?loc env sigma tycon id =
1942+
let open Ltac_pretype in
1943+
let ist = Tac2interp.get_env @@ GlobEnv.lfun env in
1944+
let env = GlobEnv.renamed_env env in
1945+
let c = Id.Map.find id ist.env_ist in
1946+
let {closure; term} = Value.to_ext Value.val_preterm c in
1947+
let vars = {
1948+
ltac_constrs = closure.typed;
1949+
ltac_uconstrs = closure.untyped;
1950+
ltac_idents = closure.idents;
1951+
ltac_genargs = closure.genargs;
1952+
}
1953+
in
1954+
let flags = preterm_flags in
1955+
let tycon = let open Pretyping in match tycon with
1956+
| Some ty -> OfType ty
1957+
| None -> WithoutTypeConstraint
1958+
in
1959+
let sigma, t, ty = Pretyping.understand_ltac_ty flags env sigma vars tycon term in
1960+
Environ.make_judge t ty, sigma
1961+
19071962
let () =
1908-
let interp ?loc ~poly env sigma tycon id =
1909-
let ist = Tac2interp.get_env @@ GlobEnv.lfun env in
1910-
let env = GlobEnv.renamed_env env in
1911-
let c = Id.Map.find id ist.env_ist in
1912-
let c = Value.to_constr c in
1913-
let t = Retyping.get_type_of env sigma c in
1914-
let j = { Environ.uj_val = c; uj_type = t } in
1915-
match tycon with
1916-
| None ->
1917-
j, sigma
1918-
| Some ty ->
1919-
let sigma =
1920-
try Evarconv.unify_leq_delay env sigma t ty
1921-
with Evarconv.UnableToUnify (sigma,e) ->
1922-
Pretype_errors.error_actual_type ?loc env sigma j ty e
1923-
in
1924-
{j with Environ.uj_type = ty}, sigma
1963+
let interp ?loc ~poly env sigma tycon (kind,id) =
1964+
let f = match kind with
1965+
| ConstrVar -> interp_constr_var_as_constr
1966+
| PretermVar -> interp_preterm_var_as_constr
1967+
in
1968+
f ?loc env sigma tycon id
19251969
in
1926-
GlobEnv.register_constr_interp0 wit_ltac2_quotation interp
1970+
GlobEnv.register_constr_interp0 wit_ltac2_var_quotation interp
19271971

19281972
let () =
1929-
let pr_raw id = Genprint.PrinterBasic (fun _env _sigma -> assert false) in
1930-
let pr_glb id = Genprint.PrinterBasic (fun _env _sigma -> str "$" ++ Id.print id) in
1973+
let pr_raw (kind,id) = Genprint.PrinterBasic (fun _env _sigma ->
1974+
let ppkind =
1975+
match kind with
1976+
| None -> mt()
1977+
| Some kind -> Id.print kind.CAst.v ++ str ":"
1978+
in
1979+
str "$" ++ ppkind ++ Id.print id.CAst.v)
1980+
in
1981+
let pr_glb (kind,id) = Genprint.PrinterBasic (fun _env _sigma ->
1982+
let ppkind = match kind with
1983+
| ConstrVar -> mt()
1984+
| PretermVar -> str "preterm:"
1985+
in
1986+
str "$" ++ ppkind ++ Id.print id) in
19311987
let pr_top x = Util.Empty.abort x in
1932-
Genprint.register_print0 wit_ltac2_quotation pr_raw pr_glb pr_top
1988+
Genprint.register_print0 wit_ltac2_var_quotation pr_raw pr_glb pr_top
19331989

19341990
let () =
19351991
let subs avoid globs (ids, tac) =

‎plugins/ltac2/tac2env.ml

+6-2
Original file line numberDiff line numberDiff line change
@@ -308,16 +308,20 @@ let ltac1_prefix =
308308

309309
(** Generic arguments *)
310310

311+
type var_quotation_kind =
312+
| ConstrVar
313+
| PretermVar
314+
311315
let wit_ltac2in1 = Genarg.make0 "ltac2in1"
312316
let wit_ltac2in1_val = Genarg.make0 "ltac2in1val"
313317
let wit_ltac2_constr = Genarg.make0 "ltac2:in-constr"
314-
let wit_ltac2_quotation = Genarg.make0 "ltac2:quotation"
318+
let wit_ltac2_var_quotation = Genarg.make0 "ltac2:quotation"
315319
let wit_ltac2_val = Genarg.make0 "ltac2:value"
316320

317321
let () = Geninterp.register_val0 wit_ltac2in1 None
318322
let () = Geninterp.register_val0 wit_ltac2in1_val None
319323
let () = Geninterp.register_val0 wit_ltac2_constr None
320-
let () = Geninterp.register_val0 wit_ltac2_quotation None
324+
let () = Geninterp.register_val0 wit_ltac2_var_quotation None
321325

322326
let is_constructor_id id =
323327
let id = Id.to_string id in

‎plugins/ltac2/tac2env.mli

+7-2
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,13 @@ val wit_ltac2in1_val : (Id.t CAst.t list * raw_tacexpr, glb_tacexpr, Util.Empty.
165165
val wit_ltac2_constr : (raw_tacexpr, Id.Set.t * glb_tacexpr, Util.Empty.t) genarg_type
166166
(** Ltac2 quotations in Gallina terms *)
167167

168-
val wit_ltac2_quotation : (Id.t Loc.located, Id.t, Util.Empty.t) genarg_type
169-
(** Ltac2 quotations for variables "$x" in Gallina terms *)
168+
type var_quotation_kind =
169+
| ConstrVar
170+
| PretermVar
171+
172+
val wit_ltac2_var_quotation : (lident option * lident, var_quotation_kind * Id.t, Util.Empty.t) genarg_type
173+
(** Ltac2 quotations for variables "$x" or "$kind:foo" in Gallina terms.
174+
NB: "$x" means "$constr:x" *)
170175

171176
val wit_ltac2_val : (Util.Empty.t, unit, Util.Empty.t) genarg_type
172177
(** Embedding Ltac2 closures of type [Ltac1.t -> Ltac1.t] inside Ltac1. There is

‎plugins/ltac2/tac2intern.ml

+32-20
Original file line numberDiff line numberDiff line change
@@ -1904,30 +1904,42 @@ let () = Genintern.register_subst0 wit_ltac2in1 (fun s (ids, e) -> ids, subst_ex
19041904
let () = Genintern.register_subst0 wit_ltac2in1_val subst_expr
19051905
let () = Genintern.register_subst0 wit_ltac2_constr (fun s (ids, e) -> ids, subst_expr s e)
19061906

1907-
let () =
1907+
let intern_var_quotation ist (kind, { CAst.v = id; loc }) =
19081908
let open Genintern in
1909-
let intern ist (loc, id) =
1910-
let env = match Genintern.Store.get ist.extra ltac2_env with
1909+
let kind = match kind with
1910+
| None -> ConstrVar
1911+
| Some kind -> match Id.to_string kind.CAst.v with
1912+
| "constr" -> ConstrVar
1913+
| "preterm" -> PretermVar
1914+
| _ ->
1915+
CErrors.user_err ?loc:kind.loc
1916+
Pp.(str "Unknown Ltac2 variable quotation kind" ++ spc() ++ Id.print kind.v)
1917+
in
1918+
let typ = match kind with
1919+
| ConstrVar -> t_constr
1920+
| PretermVar -> t_preterm
1921+
in
1922+
let env = match Genintern.Store.get ist.extra ltac2_env with
19111923
| None ->
19121924
(* Only happens when Ltac2 is called from a constr or ltac1 quotation *)
19131925
empty_env ~strict:ist.strict_check ()
19141926
| Some env -> env
1915-
in
1916-
(* Special handling of notation variables *)
1917-
let () =
1918-
if Id.Map.mem id ist.intern_sign.notation_variable_status then
1919-
(* Always fail *)
1920-
unify ?loc env (GTypRef (Other t_preterm, [])) (GTypRef (Other t_constr, []))
1921-
in
1922-
let t =
1923-
try find_var id env
1924-
with Not_found ->
1925-
CErrors.user_err ?loc (str "Unbound value " ++ Id.print id)
1926-
in
1927-
let t = fresh_mix_type_scheme env t in
1928-
let () = unify ?loc env t (GTypRef (Other t_constr, [])) in
1929-
(ist, id)
19301927
in
1931-
Genintern.register_intern0 wit_ltac2_quotation intern
1928+
(* Special handling of notation variables *)
1929+
let () =
1930+
if Id.Map.mem id ist.intern_sign.notation_variable_status then
1931+
(* Always fail for constr, never for preterm *)
1932+
unify ?loc env (GTypRef (Other t_preterm, [])) (GTypRef (Other typ, []))
1933+
in
1934+
let t =
1935+
try find_var id env
1936+
with Not_found ->
1937+
CErrors.user_err ?loc (str "Unbound value " ++ Id.print id)
1938+
in
1939+
let t = fresh_mix_type_scheme env t in
1940+
let () = unify ?loc env t (GTypRef (Other typ, [])) in
1941+
(ist, (kind, id))
1942+
1943+
let () = Genintern.register_intern0 wit_ltac2_var_quotation intern_var_quotation
19321944

1933-
let () = Genintern.register_subst0 wit_ltac2_quotation (fun _ id -> id)
1945+
let () = Genintern.register_subst0 wit_ltac2_var_quotation (fun _ v -> v)

‎pretyping/pretyping.ml

+3
Original file line numberDiff line numberDiff line change
@@ -1451,6 +1451,9 @@ let understand_ltac flags env sigma lvar kind c =
14511451
let (sigma, c, _) = ise_pretype_gen flags env sigma lvar kind c in
14521452
(sigma, c)
14531453

1454+
let understand_ltac_ty flags env sigma lvar kind c =
1455+
ise_pretype_gen flags env sigma lvar kind c
1456+
14541457
(* Fully evaluate an untyped constr *)
14551458
let understand_uconstr ?(flags = all_and_fail_flags)
14561459
?(expected_type = WithoutTypeConstraint) env sigma c =

‎pretyping/pretyping.mli

+4
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ val understand_ltac : inference_flags ->
9898
env -> evar_map -> ltac_var_map ->
9999
typing_constraint -> glob_constr -> evar_map * EConstr.t
100100

101+
val understand_ltac_ty : inference_flags ->
102+
env -> evar_map -> ltac_var_map ->
103+
typing_constraint -> glob_constr -> evar_map * EConstr.t * EConstr.types
104+
101105
(** Standard call to get a constr from a glob_constr, resolving
102106
implicit arguments and coercions, and compiling pattern-matching;
103107
the default inference_flags tells to use type classes and

‎test-suite/ltac2/preterm_antiquot.v

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
Require Import ZArith.
2+
Open Scope Z_scope.
3+
4+
Require Import Ltac2.Ltac2.
5+
6+
Ltac2 rec z2nat_preterm x :=
7+
let b := eval cbv in (Z.leb $x 0) in
8+
lazy_match! b with
9+
| true => preterm:(O)
10+
| false =>
11+
let pred := eval cbv in (Z.pred $x) in
12+
let r := z2nat_preterm pred in
13+
preterm:(S $preterm:r)
14+
end.
15+
16+
17+
Ltac2 rec z2nat_constr x :=
18+
let b := eval cbv in (Z.leb $x 0) in
19+
lazy_match! b with
20+
| true => constr:(O)
21+
| false => let pred := eval cbv in (Z.pred $x) in
22+
let r := z2nat_constr pred in
23+
constr:(S $r)
24+
end.
25+
26+
Ltac2 mk_app(a: constr)(b: constr) :=
27+
Constr.Unsafe.make (Constr.Unsafe.App a [| b |]).
28+
29+
Ltac2 rec z2nat_mk_app x :=
30+
let b := eval cbv in (Z.leb $x 0) in
31+
lazy_match! b with
32+
| true => Env.instantiate reference:(O)
33+
| false => let pred := eval cbv in (Z.pred $x) in
34+
mk_app (Env.instantiate reference:(S)) (z2nat_mk_app pred)
35+
end.
36+
37+
(* Time Ltac2 Eval *)
38+
(* let c := z2nat_constr constr:(10000) in *)
39+
(* Message.print (Message.of_constr c). (* 9 secs *) *)
40+
41+
(* Time Ltac2 Eval *)
42+
(* let c := z2nat_mk_app constr:(10000) in *)
43+
(* Message.print (Message.of_constr c). (* <1 secs *) *)
44+
45+
Time Ltac2 Eval
46+
let c := z2nat_preterm constr:(10000) in
47+
let c := constr:($preterm:c) in
48+
Message.print (Message.of_constr c). (* 6 secs *)
49+
50+
(* Time Ltac2 Eval *)
51+
(* let c := z2nat_constr constr:(20000) in *)
52+
(* Message.print (Message.of_constr c). (* 22 secs *) *)
53+
54+
(* Time Ltac2 Eval *)
55+
(* let c := z2nat_mk_app constr:(20000) in *)
56+
(* Message.print (Message.of_constr c). (* 1.6 secs *) *)
57+
58+
(* a bit close to stack overflow *)
59+
(* Time Ltac2 Eval *)
60+
(* let c := z2nat_preterm constr:(20000) in *)
61+
(* let c := Constr.pretype c in *)
62+
(* Message.print (Message.of_constr c). (* 32 secs *) *)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
2+
3+
Coq < Coq < Toplevel input, characters 7-9:
4+
> Check $x.
5+
> ^^
6+
Error: Unbound value x
7+
8+
Coq < Coq < Toplevel input, characters 16-17:
9+
> Check $preterm:x.
10+
> ^
11+
Error: Unbound value x
12+
13+
Coq < Coq < Toplevel input, characters 7-8:
14+
> Check $ x.
15+
> ^
16+
Error: Syntax error: [lconstr] expected after 'Check' (in [query_command]).
17+
18+
Coq < Coq < Toplevel input, characters 7-8:
19+
> Check $ preterm:x.
20+
> ^
21+
Error: Syntax error: [lconstr] expected after 'Check' (in [query_command]).
22+
23+
Coq < Coq < Toplevel input, characters 7-8:
24+
> Check $ preterm : x.
25+
> ^
26+
Error: Syntax error: [lconstr] expected after 'Check' (in [query_command]).
27+
28+
Coq < Coq < Toplevel input, characters 18-19:
29+
> Check $preterm : x.
30+
> ^
31+
Error: The reference x was not found in the current environment.
32+
33+
Coq < Coq < Toplevel input, characters 16-24:
34+
> Check fun x => $preterm : x.
35+
> ^^^^^^^^
36+
Error: Unbound value preterm
37+
38+
Coq < Coq < Toplevel input, characters 17-18:
39+
> Check $preterm: x.
40+
> ^
41+
Error: The reference x was not found in the current environment.
42+
43+
Coq < Coq < Toplevel input, characters 16-24:
44+
> Check fun x => $preterm: x.
45+
> ^^^^^^^^
46+
Error: Unbound value preterm
47+
48+
Coq < Coq < Toplevel input, characters 7-8:
49+
> Check $ preterm :x.
50+
> ^
51+
Error: Syntax error: [lconstr] expected after 'Check' (in [query_command]).
52+
53+
Coq <
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Require Import Ltac2.Ltac2.
2+
3+
Check $x.
4+
5+
Check $preterm:x.
6+
7+
Check $ x.
8+
9+
Check $ preterm:x.
10+
11+
Check $ preterm : x.
12+
13+
Check $preterm : x.
14+
15+
Check fun x => $preterm : x.
16+
17+
Check $preterm: x.
18+
19+
Check fun x => $preterm: x.
20+
21+
Check $ preterm :x.

0 commit comments

Comments
 (0)
Please sign in to comment.