aboutsummaryrefslogtreecommitdiff
path: root/user-contrib
diff options
context:
space:
mode:
authorPierre-Marie Pédrot2019-10-02 09:31:29 +0200
committerPierre-Marie Pédrot2019-10-29 13:48:57 +0100
commit5651d47395c1a88b8d5a1549f14c99f81bbcb551 (patch)
tree8f674737cb1dce8b91f1f8930047ee155c994baf /user-contrib
parent5370e79c1128dd96a75e3c37569daa3fd98dcd86 (diff)
Fix #10615: Notation substitution for Ltac2 terms.
We implement a new type of "preterms" that represent untyped ASTs, corresponding to glob_expr in the ML implementations. Ltac2 quotations inside notations are provided with such preterms, and have to pretype them in order to do anything of relevance with them.
Diffstat (limited to 'user-contrib')
-rw-r--r--user-contrib/Ltac2/Constr.v3
-rw-r--r--user-contrib/Ltac2/Init.v1
-rw-r--r--user-contrib/Ltac2/g_ltac2.mlg8
-rw-r--r--user-contrib/Ltac2/tac2core.ml122
-rw-r--r--user-contrib/Ltac2/tac2env.ml1
-rw-r--r--user-contrib/Ltac2/tac2env.mli6
-rw-r--r--user-contrib/Ltac2/tac2ffi.ml1
-rw-r--r--user-contrib/Ltac2/tac2ffi.mli1
-rw-r--r--user-contrib/Ltac2/tac2intern.ml38
-rw-r--r--user-contrib/Ltac2/tac2quote.ml1
-rw-r--r--user-contrib/Ltac2/tac2quote.mli2
11 files changed, 152 insertions, 32 deletions
diff --git a/user-contrib/Ltac2/Constr.v b/user-contrib/Ltac2/Constr.v
index 1e330b06d7..942cbe8916 100644
--- a/user-contrib/Ltac2/Constr.v
+++ b/user-contrib/Ltac2/Constr.v
@@ -77,3 +77,6 @@ Ltac2 @ external in_context : ident -> constr -> (unit -> unit) -> constr := "lt
(** On a focused goal [Γ ⊢ A], [in_context id c tac] evaluates [tac] in a
focused goal [Γ, id : c ⊢ ?X] and returns [fun (id : c) => t] where [t] is
the proof built by the tactic. *)
+
+Ltac2 @ external pretype : preterm -> constr := "ltac2" "constr_pretype".
+(** Pretype the provided preterm. Assumes the goal to be focussed. *)
diff --git a/user-contrib/Ltac2/Init.v b/user-contrib/Ltac2/Init.v
index 88454ff2fb..6eed261554 100644
--- a/user-contrib/Ltac2/Init.v
+++ b/user-contrib/Ltac2/Init.v
@@ -30,6 +30,7 @@ Ltac2 Type constructor.
Ltac2 Type projection.
Ltac2 Type pattern.
Ltac2 Type constr.
+Ltac2 Type preterm.
Ltac2 Type message.
Ltac2 Type exn := [ .. ].
diff --git a/user-contrib/Ltac2/g_ltac2.mlg b/user-contrib/Ltac2/g_ltac2.mlg
index 8a878bb0d0..9d4a3706f4 100644
--- a/user-contrib/Ltac2/g_ltac2.mlg
+++ b/user-contrib/Ltac2/g_ltac2.mlg
@@ -838,11 +838,11 @@ END
GRAMMAR EXTEND Gram
Pcoq.Constr.operconstr: LEVEL "0"
[ [ IDENT "ltac2"; ":"; "("; tac = tac2expr; ")" ->
- { let arg = Genarg.in_gen (Genarg.rawwit Tac2env.wit_ltac2) tac in
+ { let arg = Genarg.in_gen (Genarg.rawwit Tac2env.wit_ltac2_constr) tac in
CAst.make ~loc (CHole (None, Namegen.IntroAnonymous, Some arg)) }
| test_ampersand_ident; "&"; id = Prim.ident ->
{ let tac = Tac2quote.of_exact_hyp ~loc (CAst.make ~loc id) in
- let arg = Genarg.in_gen (Genarg.rawwit Tac2env.wit_ltac2) tac in
+ let arg = Genarg.in_gen (Genarg.rawwit Tac2env.wit_ltac2_constr) tac in
CAst.make ~loc (CHole (None, Namegen.IntroAnonymous, Some arg)) }
| test_dollar_ident; "$"; id = Prim.ident ->
{ let id = Loc.tag ~loc id in
@@ -873,7 +873,7 @@ let rules = [
Stop ++ Aentry test_ampersand_ident ++ Atoken (PKEYWORD "&") ++ Aentry Prim.ident,
begin fun id _ _ loc ->
let tac = Tac2quote.of_exact_hyp ~loc (CAst.make ~loc id) in
- let arg = Genarg.in_gen (Genarg.rawwit Tac2env.wit_ltac2) ([], tac) in
+ let arg = Genarg.in_gen (Genarg.rawwit Tac2env.wit_ltac2_constr) tac in
CAst.make ~loc (CHole (None, Namegen.IntroAnonymous, Some arg))
end
);
@@ -882,7 +882,7 @@ let rules = [
Stop ++ Atoken (PIDENT (Some "ltac2")) ++ Atoken (PKEYWORD ":") ++
Atoken (PKEYWORD "(") ++ Aentry tac2expr ++ Atoken (PKEYWORD ")"),
begin fun _ tac _ _ _ loc ->
- let arg = Genarg.in_gen (Genarg.rawwit Tac2env.wit_ltac2) ([], tac) in
+ let arg = Genarg.in_gen (Genarg.rawwit Tac2env.wit_ltac2_constr) tac in
CAst.make ~loc (CHole (None, Namegen.IntroAnonymous, Some arg))
end
)
diff --git a/user-contrib/Ltac2/tac2core.ml b/user-contrib/Ltac2/tac2core.ml
index 34870345a5..0268e8f9ef 100644
--- a/user-contrib/Ltac2/tac2core.ml
+++ b/user-contrib/Ltac2/tac2core.ml
@@ -17,6 +17,28 @@ open Tac2expr
open Tac2entries.Pltac
open Proofview.Notations
+let constr_flags =
+ let open Pretyping in
+ {
+ use_typeclasses = true;
+ solve_unification_constraints = true;
+ fail_evar = true;
+ expand_evars = true;
+ program_mode = false;
+ polymorphic = false;
+ }
+
+let open_constr_no_classes_flags =
+ let open Pretyping in
+ {
+ use_typeclasses = false;
+ solve_unification_constraints = true;
+ fail_evar = false;
+ expand_evars = true;
+ program_mode = false;
+ polymorphic = false;
+ }
+
(** Standard values *)
module Value = Tac2ffi
@@ -587,6 +609,30 @@ let () = define3 "constr_in_context" ident constr closure begin fun id t c ->
throw err_notfocussed
end
+(** preterm -> constr *)
+let () = define1 "constr_pretype" (repr_ext val_preterm) begin fun c ->
+ let open Pretyping in
+ let open Ltac_pretype in
+ let pretype env sigma =
+ Proofview.V82.wrap_exceptions begin fun () ->
+ (* For now there are no primitives to create preterms with a non-empty
+ closure. I do not know whether [closed_glob_constr] is really the type
+ we want but it does not hurt in the meantime. *)
+ let { closure; term } = c in
+ let vars = {
+ ltac_constrs = closure.typed;
+ ltac_uconstrs = closure.untyped;
+ ltac_idents = closure.idents;
+ ltac_genargs = Id.Map.empty;
+ } in
+ let flags = constr_flags in
+ let sigma, t = understand_ltac flags env sigma vars WithoutTypeConstraint term in
+ let t = Value.of_constr t in
+ Proofview.Unsafe.tclEVARS sigma <*> Proofview.tclUNIT t
+ end in
+ pf_apply pretype
+end
+
(** Patterns *)
let empty_context = EConstr.mkMeta Constr_matching.special_meta
@@ -976,28 +1022,6 @@ end
(** ML types *)
-let constr_flags () =
- let open Pretyping in
- {
- use_typeclasses = true;
- solve_unification_constraints = true;
- fail_evar = true;
- expand_evars = true;
- program_mode = false;
- polymorphic = false;
- }
-
-let open_constr_no_classes_flags () =
- let open Pretyping in
- {
- use_typeclasses = false;
- solve_unification_constraints = true;
- fail_evar = false;
- expand_evars = true;
- program_mode = false;
- polymorphic = false;
- }
-
(** Embed all Ltac2 data into Values *)
let to_lvar ist =
let open Glob_ops in
@@ -1033,7 +1057,7 @@ let interp_constr flags ist c =
let () =
let intern = intern_constr in
- let interp ist c = interp_constr (constr_flags ()) ist c in
+ let interp ist c = interp_constr constr_flags ist c in
let print env c = str "constr:(" ++ Printer.pr_lglob_constr_env env c ++ str ")" in
let subst subst c = Detyping.subst_glob_constr (Global.env()) subst c in
let obj = {
@@ -1046,7 +1070,7 @@ let () =
let () =
let intern = intern_constr in
- let interp ist c = interp_constr (open_constr_no_classes_flags ()) ist c in
+ let interp ist c = interp_constr open_constr_no_classes_flags ist c in
let print env c = str "open_constr:(" ++ Printer.pr_lglob_constr_env env c ++ str ")" in
let subst subst c = Detyping.subst_glob_constr (Global.env()) subst c in
let obj = {
@@ -1092,6 +1116,27 @@ let () =
define_ml_object Tac2quote.wit_pattern obj
let () =
+ let interp _ c =
+ let open Ltac_pretype in
+ let closure = {
+ idents = Id.Map.empty;
+ typed = Id.Map.empty;
+ untyped = Id.Map.empty;
+ } in
+ let c = { closure; term = c } in
+ return (Value.of_ext val_preterm c)
+ in
+ let subst subst c = Detyping.subst_glob_constr (Global.env()) subst c in
+ let print env c = str "preterm:(" ++ Printer.pr_lglob_constr_env env c ++ str ")" in
+ let obj = {
+ ml_intern = (fun _ _ e -> Empty.abort e);
+ ml_interp = interp;
+ ml_subst = subst;
+ ml_print = print;
+ } in
+ define_ml_object Tac2quote.wit_preterm obj
+
+let () =
let intern self ist ref = match ref.CAst.v with
| Tac2qexpr.QHypothesis id ->
GlbVal (GlobRef.VarRef id), gtypref t_reference
@@ -1221,15 +1266,15 @@ let () =
let () =
let interp ist poly env sigma concl (ids, tac) =
- (* Syntax prevents bound variables in constr quotations *)
- let () = assert (List.is_empty ids) in
+ (* Syntax prevents bound notation variables in constr quotations *)
+ let () = assert (Id.Set.is_empty ids) in
let ist = Tac2interp.get_env ist in
let tac = Proofview.tclIGNORE (Tac2interp.interp ist tac) in
let name, poly = Id.of_string "ltac2", poly in
let c, sigma = Pfedit.refine_by_tactic ~name ~poly env sigma concl tac in
(EConstr.of_constr c, sigma)
in
- GlobEnv.register_constr_interp0 wit_ltac2 interp
+ GlobEnv.register_constr_interp0 wit_ltac2_constr interp
let () =
let interp ist poly env sigma concl id =
@@ -1247,6 +1292,29 @@ let () =
let pr_top _ = Genprint.TopPrinterBasic mt in
Genprint.register_print0 wit_ltac2_quotation pr_raw pr_glb pr_top
+let () =
+ let subs globs (ids, tac) =
+ (* Let-bind the notation terms inside the tactic *)
+ let fold id (c, _) (rem, accu) =
+ let c = GTacExt (Tac2quote.wit_preterm, c) in
+ let rem = Id.Set.remove id rem in
+ rem, (Name id, c) :: accu
+ in
+ let rem, bnd = Id.Map.fold fold globs (ids, []) in
+ let () = if not @@ Id.Set.is_empty rem then
+ (* FIXME: provide a reasonable middle-ground with the behaviour
+ introduced by 8d9b66b. We should be able to pass mere syntax to
+ term notation without facing the wrath of the internalization. *)
+ let plural = if Id.Set.cardinal rem <= 1 then " " else "s " in
+ CErrors.user_err (str "Missing notation term for variable" ++ str plural ++
+ pr_sequence Id.print (Id.Set.elements rem) ++
+ str ", probably an ill-typed expression")
+ in
+ let tac = if List.is_empty bnd then tac else GTacLet (false, bnd, tac) in
+ (Id.Set.empty, tac)
+ in
+ Genintern.register_ntn_subst0 wit_ltac2_constr subs
+
(** Ltac2 in Ltac1 *)
let () =
diff --git a/user-contrib/Ltac2/tac2env.ml b/user-contrib/Ltac2/tac2env.ml
index 963c3aa37f..959a912ad2 100644
--- a/user-contrib/Ltac2/tac2env.ml
+++ b/user-contrib/Ltac2/tac2env.ml
@@ -284,6 +284,7 @@ let ltac1_prefix =
(** Generic arguments *)
let wit_ltac2 = Genarg.make0 "ltac2:value"
+let wit_ltac2_constr = Genarg.make0 "ltac2:in-constr"
let wit_ltac2_quotation = Genarg.make0 "ltac2:quotation"
let () = Geninterp.register_val0 wit_ltac2 None
let () = Geninterp.register_val0 wit_ltac2_quotation None
diff --git a/user-contrib/Ltac2/tac2env.mli b/user-contrib/Ltac2/tac2env.mli
index 2f4a49a0f5..1dfc3400a1 100644
--- a/user-contrib/Ltac2/tac2env.mli
+++ b/user-contrib/Ltac2/tac2env.mli
@@ -141,7 +141,13 @@ val ltac1_prefix : ModPath.t
(** {5 Generic arguments} *)
val wit_ltac2 : (Id.t CAst.t list * raw_tacexpr, Id.t list * glb_tacexpr, Util.Empty.t) genarg_type
+(** Ltac2 quotations in Ltac1 code *)
+
+val wit_ltac2_constr : (raw_tacexpr, Id.Set.t * glb_tacexpr, Util.Empty.t) genarg_type
+(** Ltac2 quotations in Gallina terms *)
+
val wit_ltac2_quotation : (Id.t Loc.located, Id.t, Util.Empty.t) genarg_type
+(** Ltac2 quotations for variables "$x" in Gallina terms *)
(** {5 Helper functions} *)
diff --git a/user-contrib/Ltac2/tac2ffi.ml b/user-contrib/Ltac2/tac2ffi.ml
index 0e6fb94095..7c9613f31b 100644
--- a/user-contrib/Ltac2/tac2ffi.ml
+++ b/user-contrib/Ltac2/tac2ffi.ml
@@ -89,6 +89,7 @@ let val_exn = Val.create "exn"
let val_constr = Val.create "constr"
let val_ident = Val.create "ident"
let val_pattern = Val.create "pattern"
+let val_preterm = Val.create "preterm"
let val_pp = Val.create "pp"
let val_sort = Val.create "sort"
let val_cast = Val.create "cast"
diff --git a/user-contrib/Ltac2/tac2ffi.mli b/user-contrib/Ltac2/tac2ffi.mli
index 480eee51fc..d3c9596e8f 100644
--- a/user-contrib/Ltac2/tac2ffi.mli
+++ b/user-contrib/Ltac2/tac2ffi.mli
@@ -165,6 +165,7 @@ val valexpr : valexpr repr
val val_constr : EConstr.t Val.tag
val val_ident : Id.t Val.tag
val val_pattern : Pattern.constr_pattern Val.tag
+val val_preterm : Ltac_pretype.closed_glob_constr Val.tag
val val_pp : Pp.t Val.tag
val val_sort : ESorts.t Val.tag
val val_cast : Constr.cast_kind Val.tag
diff --git a/user-contrib/Ltac2/tac2intern.ml b/user-contrib/Ltac2/tac2intern.ml
index 5b3aa799a1..4e39b21c53 100644
--- a/user-contrib/Ltac2/tac2intern.ml
+++ b/user-contrib/Ltac2/tac2intern.ml
@@ -28,6 +28,7 @@ let t_int = coq_type "int"
let t_string = coq_type "string"
let t_constr = coq_type "constr"
let t_ltac1 = ltac1_type "t"
+let t_preterm = coq_type "preterm"
(** Union find *)
@@ -1511,7 +1512,7 @@ let () =
let ids = List.map (fun { CAst.v = id } -> id) ids in
let env = match Genintern.Store.get ist.extra ltac2_env with
| None ->
- (* Only happens when Ltac2 is called from a constr or ltac1 quotation *)
+ (* Only happens when Ltac2 is called from a toplevel ltac1 quotation *)
let env = empty_env () in
if !Ltac_plugin.Tacintern.strict_check then env
else { env with env_str = false }
@@ -1527,7 +1528,36 @@ let () =
(ist, (ids, tac))
in
Genintern.register_intern0 wit_ltac2 intern
+
+let () =
+ let open Genintern in
+ let intern ist tac =
+ let env = match Genintern.Store.get ist.extra ltac2_env with
+ | None ->
+ (* Only happens when Ltac2 is called from a constr quotation *)
+ let env = empty_env () in
+ if !Ltac_plugin.Tacintern.strict_check then env
+ else { env with env_str = false }
+ | Some env -> env
+ in
+ (* Special handling of notation variables *)
+ let fold id _ (ids, env) =
+ let () = assert (not @@ Id.Map.mem id env.env_var) in
+ let t = monomorphic (GTypRef (Other t_preterm, [])) in
+ let env = push_name (Name id) t env in
+ (Id.Set.add id ids, env)
+ in
+ let ntn_vars = ist.intern_sign.notation_variable_status in
+ let ids, env = Id.Map.fold fold ntn_vars (Id.Set.empty, env) in
+ let loc = tac.loc in
+ let (tac, t) = intern_rec env tac in
+ let () = check_elt_unit loc env t in
+ (ist, (ids, tac))
+ in
+ Genintern.register_intern0 wit_ltac2_constr intern
+
let () = Genintern.register_subst0 wit_ltac2 (fun s (ids, e) -> ids, subst_expr s e)
+let () = Genintern.register_subst0 wit_ltac2_constr (fun s (ids, e) -> ids, subst_expr s e)
let () =
let open Genintern in
@@ -1540,6 +1570,12 @@ let () =
else { env with env_str = false }
| Some env -> env
in
+ (* Special handling of notation variables *)
+ let () =
+ if Id.Map.mem id ist.intern_sign.notation_variable_status then
+ (* Always fail *)
+ unify ?loc env (GTypRef (Other t_preterm, [])) (GTypRef (Other t_constr, []))
+ in
let t =
try Id.Map.find id env.env_var
with Not_found ->
diff --git a/user-contrib/Ltac2/tac2quote.ml b/user-contrib/Ltac2/tac2quote.ml
index 405c80fa9b..645b92c302 100644
--- a/user-contrib/Ltac2/tac2quote.ml
+++ b/user-contrib/Ltac2/tac2quote.ml
@@ -23,6 +23,7 @@ let wit_reference = Arg.create "reference"
let wit_ident = Arg.create "ident"
let wit_constr = Arg.create "constr"
let wit_open_constr = Arg.create "open_constr"
+let wit_preterm = Arg.create "preterm"
let wit_ltac1 = Arg.create "ltac1"
let wit_ltac1val = Arg.create "ltac1val"
diff --git a/user-contrib/Ltac2/tac2quote.mli b/user-contrib/Ltac2/tac2quote.mli
index da28e04df0..f1564cd443 100644
--- a/user-contrib/Ltac2/tac2quote.mli
+++ b/user-contrib/Ltac2/tac2quote.mli
@@ -97,6 +97,8 @@ val wit_constr : (Constrexpr.constr_expr, Glob_term.glob_constr) Arg.tag
val wit_open_constr : (Constrexpr.constr_expr, Glob_term.glob_constr) Arg.tag
+val wit_preterm : (Util.Empty.t, Glob_term.glob_constr) Arg.tag
+
val wit_ltac1 : (Id.t CAst.t list * Ltac_plugin.Tacexpr.raw_tactic_expr, Id.t list * Ltac_plugin.Tacexpr.glob_tactic_expr) Arg.tag
(** Ltac1 AST quotation, seen as a 'tactic'. Its type is unit in Ltac2. *)