diff options
Diffstat (limited to 'interp/constrintern.ml')
| -rw-r--r-- | interp/constrintern.ml | 2445 |
1 files changed, 2445 insertions, 0 deletions
diff --git a/interp/constrintern.ml b/interp/constrintern.ml new file mode 100644 index 0000000000..7aa85a0810 --- /dev/null +++ b/interp/constrintern.ml @@ -0,0 +1,2445 @@ +(************************************************************************) +(* * The Coq Proof Assistant / The Coq Development Team *) +(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *) +(* <O___,, * (see CREDITS file for the list of authors) *) +(* \VV/ **************************************************************) +(* // * This file is distributed under the terms of the *) +(* * GNU Lesser General Public License Version 2.1 *) +(* * (see LICENSE file for the text of the license) *) +(************************************************************************) + +open Pp +open CErrors +open Util +open CAst +open Names +open Nameops +open Namegen +open Constr +open Libnames +open Globnames +open Impargs +open Glob_term +open Glob_ops +open Patternops +open Pretyping +open Cases +open Constrexpr +open Constrexpr_ops +open Notation_term +open Notation_ops +open Notation +open Inductiveops +open Decl_kinds +open Context.Rel.Declaration + +(** constr_expr -> glob_constr translation: + - it adds holes for implicit arguments + - it replaces notations by their value (scopes stuff are here) + - it recognizes global vars from local ones + - it prepares pattern matching problems (a pattern becomes a tree + where nodes are constructor/variable pairs and leafs are variables) + + All that at once, fasten your seatbelt! +*) + +(* To interpret implicits and arg scopes of variables in inductive + types and recursive definitions and of projection names in records *) + +type var_internalization_type = + | Inductive of Id.t list (* list of params *) * bool (* true = check for possible capture *) + | Recursive + | Method + | Variable + +type var_internalization_data = + (* type of the "free" variable, for coqdoc, e.g. while typing the + constructor of JMeq, "JMeq" behaves as a variable of type Inductive *) + var_internalization_type * + (* impargs to automatically add to the variable, e.g. for "JMeq A a B b" + in implicit mode, this is [A;B] and this adds (A:=A) and (B:=B) *) + Id.t list * + (* signature of impargs of the variable *) + Impargs.implicit_status list * + (* subscopes of the args of the variable *) + scope_name option list + +type internalization_env = + (var_internalization_data) Id.Map.t + +type ltac_sign = { + ltac_vars : Id.Set.t; + ltac_bound : Id.Set.t; + ltac_extra : Genintern.Store.t; +} + +let interning_grammar = ref false + +(* Historically for parsing grammar rules, but in fact used only for + translator, v7 parsing, and unstrict tactic internalization *) +let for_grammar f x = + interning_grammar := true; + let a = f x in + interning_grammar := false; + a + +(**********************************************************************) +(* Locating reference, possibly via an abbreviation *) + +let locate_reference qid = + Smartlocate.global_of_extended_global (Nametab.locate_extended qid) + +let is_global id = + try + let _ = locate_reference (qualid_of_ident id) in true + with Not_found -> + false + +let global_reference_of_reference qid = + locate_reference qid + +let global_reference id = + locate_reference (qualid_of_ident id) + +let construct_reference ctx id = + try + VarRef (let _ = Context.Named.lookup id ctx in id) + with Not_found -> + global_reference id + +let global_reference_in_absolute_module dir id = + Nametab.global_of_path (Libnames.make_path dir id) + +(**********************************************************************) +(* Internalization errors *) + +type internalization_error = + | VariableCapture of Id.t * Id.t + | IllegalMetavariable + | NotAConstructor of qualid + | UnboundFixName of bool * Id.t + | NonLinearPattern of Id.t + | BadPatternsNumber of int * int + | NotAProjection of qualid + | NotAProjectionOf of qualid * qualid + | ProjectionsOfDifferentRecords of qualid * qualid + +exception InternalizationError of internalization_error Loc.located + +let explain_variable_capture id id' = + Id.print id ++ str " is dependent in the type of " ++ Id.print id' ++ + strbrk ": cannot interpret both of them with the same type" + +let explain_illegal_metavariable = + str "Metavariables allowed only in patterns" + +let explain_not_a_constructor qid = + str "Unknown constructor: " ++ pr_qualid qid + +let explain_unbound_fix_name is_cofix id = + str "The name" ++ spc () ++ Id.print id ++ + spc () ++ str "is not bound in the corresponding" ++ spc () ++ + str (if is_cofix then "co" else "") ++ str "fixpoint definition" + +let explain_non_linear_pattern id = + str "The variable " ++ Id.print id ++ str " is bound several times in pattern" + +let explain_bad_patterns_number n1 n2 = + str "Expecting " ++ int n1 ++ str (String.plural n1 " pattern") ++ + str " but found " ++ int n2 + +let explain_field_not_a_projection field_id = + pr_qualid field_id ++ str ": Not a projection" + +let explain_field_not_a_projection_of field_id inductive_id = + pr_qualid field_id ++ str ": Not a projection of inductive " ++ pr_qualid inductive_id + +let explain_projections_of_diff_records inductive1_id inductive2_id = + str "This record contains fields of both " ++ pr_qualid inductive1_id ++ + str " and " ++ pr_qualid inductive2_id + +let explain_internalization_error e = + let pp = match e with + | VariableCapture (id,id') -> explain_variable_capture id id' + | IllegalMetavariable -> explain_illegal_metavariable + | NotAConstructor ref -> explain_not_a_constructor ref + | UnboundFixName (iscofix,id) -> explain_unbound_fix_name iscofix id + | NonLinearPattern id -> explain_non_linear_pattern id + | BadPatternsNumber (n1,n2) -> explain_bad_patterns_number n1 n2 + | NotAProjection field_id -> explain_field_not_a_projection field_id + | NotAProjectionOf (field_id, inductive_id) -> + explain_field_not_a_projection_of field_id inductive_id + | ProjectionsOfDifferentRecords (inductive1_id, inductive2_id) -> + explain_projections_of_diff_records inductive1_id inductive2_id + in pp ++ str "." + +let error_bad_inductive_type ?loc = + user_err ?loc (str + "This should be an inductive type applied to patterns.") + +let error_parameter_not_implicit ?loc = + user_err ?loc (str + "The parameters do not bind in patterns;" ++ spc () ++ str + "they must be replaced by '_'.") + +let error_ldots_var ?loc = + user_err ?loc (str "Special token " ++ Id.print ldots_var ++ + str " is for use in the Notation command.") + +(**********************************************************************) +(* Pre-computing the implicit arguments and arguments scopes needed *) +(* for interpretation *) + +let parsing_explicit = ref false + +let empty_internalization_env = Id.Map.empty + +let compute_explicitable_implicit imps = function + | Inductive (params,_) -> + (* In inductive types, the parameters are fixed implicit arguments *) + let sub_impl,_ = List.chop (List.length params) imps in + let sub_impl' = List.filter is_status_implicit sub_impl in + List.map name_of_implicit sub_impl' + | Recursive | Method | Variable -> + (* Unable to know in advance what the implicit arguments will be *) + [] + +let compute_internalization_data env sigma ty typ impl = + let impl = compute_implicits_with_manual env sigma typ (is_implicit_args()) impl in + let expls_impl = compute_explicitable_implicit impl ty in + (ty, expls_impl, impl, compute_arguments_scope sigma typ) + +let compute_internalization_env env sigma ?(impls=empty_internalization_env) ty = + List.fold_left3 + (fun map id typ impl -> Id.Map.add id (compute_internalization_data env sigma ty typ impl) map) + impls + +(**********************************************************************) +(* Contracting "{ _ }" in notations *) + +let rec wildcards ntn n = + if Int.equal n (String.length ntn) then [] + else let l = spaces ntn (n+1) in if ntn.[n] == '_' then n::l else l +and spaces ntn n = + if Int.equal n (String.length ntn) then [] + else if ntn.[n] == ' ' then wildcards ntn (n+1) else spaces ntn (n+1) + +let expand_notation_string ntn n = + let pos = List.nth (wildcards ntn 0) n in + let hd = if Int.equal pos 0 then "" else String.sub ntn 0 pos in + let tl = + if Int.equal pos (String.length ntn) then "" + else String.sub ntn (pos+1) (String.length ntn - pos -1) in + hd ^ "{ _ }" ^ tl + +(* This contracts the special case of "{ _ }" for sumbool, sumor notations *) +(* Remark: expansion of squash at definition is done in metasyntax.ml *) +let contract_curly_brackets ntn (l,ll,bl,bll) = + match ntn with + | InCustomEntryLevel _,_ -> ntn,(l,ll,bl,bll) + | InConstrEntrySomeLevel, ntn -> + let ntn' = ref ntn in + let rec contract_squash n = function + | [] -> [] + | { CAst.v = CNotation ((InConstrEntrySomeLevel,"{ _ }"),([a],[],[],[])) } :: l -> + ntn' := expand_notation_string !ntn' n; + contract_squash n (a::l) + | a :: l -> + a::contract_squash (n+1) l in + let l = contract_squash 0 l in + (* side effect; don't inline *) + (InConstrEntrySomeLevel,!ntn'),(l,ll,bl,bll) + +let contract_curly_brackets_pat ntn (l,ll) = + match ntn with + | InCustomEntryLevel _,_ -> ntn,(l,ll) + | InConstrEntrySomeLevel, ntn -> + let ntn' = ref ntn in + let rec contract_squash n = function + | [] -> [] + | { CAst.v = CPatNotation ((InConstrEntrySomeLevel,"{ _ }"),([a],[]),[]) } :: l -> + ntn' := expand_notation_string !ntn' n; + contract_squash n (a::l) + | a :: l -> + a::contract_squash (n+1) l in + let l = contract_squash 0 l in + (* side effect; don't inline *) + (InConstrEntrySomeLevel,!ntn'),(l,ll) + +type intern_env = { + ids: Names.Id.Set.t; + unb: bool; + tmp_scope: Notation_term.tmp_scope_name option; + scopes: Notation_term.scope_name list; + impls: internalization_env } + +(**********************************************************************) +(* Remembering the parsing scope of variables in notations *) + +let make_current_scope tmp scopes = match tmp, scopes with +| Some tmp_scope, (sc :: _) when String.equal sc tmp_scope -> scopes +| Some tmp_scope, scopes -> tmp_scope :: scopes +| None, scopes -> scopes + +let pr_scope_stack = function + | [] -> str "the empty scope stack" + | [a] -> str "scope " ++ str a + | l -> str "scope stack " ++ + str "[" ++ prlist_with_sep pr_comma str l ++ str "]" + +let error_inconsistent_scope ?loc id scopes1 scopes2 = + user_err ?loc ~hdr:"set_var_scope" + (Id.print id ++ str " is here used in " ++ + pr_scope_stack scopes2 ++ strbrk " while it was elsewhere used in " ++ + pr_scope_stack scopes1) + +let error_expect_binder_notation_type ?loc id = + user_err ?loc + (Id.print id ++ + str " is expected to occur in binding position in the right-hand side.") + +let set_var_scope ?loc id istermvar (tmp_scope,subscopes as scopes) ntnvars = + try + let used_as_binder,idscopes,typ = Id.Map.find id ntnvars in + if not istermvar then used_as_binder := true; + let () = if istermvar then + (* scopes have no effect on the interpretation of identifiers *) + begin match !idscopes with + | None -> idscopes := Some scopes + | Some (tmp_scope', subscopes') -> + let s' = make_current_scope tmp_scope' subscopes' in + let s = make_current_scope tmp_scope subscopes in + if not (List.equal String.equal s' s) then error_inconsistent_scope ?loc id s' s + end + in + match typ with + | Notation_term.NtnInternTypeOnlyBinder -> + if istermvar then error_expect_binder_notation_type ?loc id + | Notation_term.NtnInternTypeAny -> () + with Not_found -> + (* Not in a notation *) + () + +let set_type_scope env = {env with tmp_scope = Notation.current_type_scope_name ()} + +let reset_tmp_scope env = {env with tmp_scope = None} + +let set_env_scopes env (scopt,subscopes) = + {env with tmp_scope = scopt; scopes = subscopes @ env.scopes} + +let mkGProd ?loc (na,bk,t) body = DAst.make ?loc @@ GProd (na, bk, t, body) +let mkGLambda ?loc (na,bk,t) body = DAst.make ?loc @@ GLambda (na, bk, t, body) + +(**********************************************************************) +(* Utilities for binders *) +let build_impls = function + |Implicit -> (function + |Name id -> Some (id, Impargs.Manual, (true,true)) + |Anonymous -> Some (Id.of_string "_", Impargs.Manual, (true,true))) + |Explicit -> fun _ -> None + +let impls_type_list ?(args = []) = + let rec aux acc c = match DAst.get c with + | GProd (na,bk,_,c) -> aux ((build_impls bk na)::acc) c + | _ -> (Variable,[],List.append args (List.rev acc),[]) + in aux [] + +let impls_term_list ?(args = []) = + let rec aux acc c = match DAst.get c with + | GLambda (na,bk,_,c) -> aux ((build_impls bk na)::acc) c + | GRec (fix_kind, nas, args, tys, bds) -> + let nb = match fix_kind with |GFix (_, n) -> n | GCoFix n -> n in + let acc' = List.fold_left (fun a (na, bk, _, _) -> (build_impls bk na)::a) acc args.(nb) in + aux acc' bds.(nb) + |_ -> (Variable,[],List.append args (List.rev acc),[]) + in aux [] + +(* Check if in binder "(x1 x2 .. xn : t)", none of x1 .. xn-1 occurs in t *) +let rec check_capture ty = let open CAst in function + | { loc; v = Name id } :: { v = Name id' } :: _ when occur_glob_constr id ty -> + raise (InternalizationError (loc,VariableCapture (id,id'))) + | _::nal -> + check_capture ty nal + | [] -> + () + +let locate_if_hole ?loc na c = match DAst.get c with + | GHole (_,naming,arg) -> + (try match na with + | Name id -> glob_constr_of_notation_constr ?loc + (Reserve.find_reserved_type id) + | Anonymous -> raise Not_found + with Not_found -> DAst.make ?loc @@ GHole (Evar_kinds.BinderType na, naming, arg)) + | _ -> c + +let reset_hidden_inductive_implicit_test env = + { env with impls = Id.Map.map (function + | (Inductive (params,_),b,c,d) -> (Inductive (params,false),b,c,d) + | x -> x) env.impls } + +let check_hidden_implicit_parameters ?loc id impls = + if Id.Map.exists (fun _ -> function + | (Inductive (indparams,check),_,_,_) when check -> Id.List.mem id indparams + | _ -> false) impls + then + user_err ?loc (Id.print id ++ strbrk " is already used as name of " ++ + strbrk "a parameter of the inductive type; bound variables in " ++ + strbrk "the type of a constructor shall use a different name.") + +let push_name_env ?(global_level=false) ntnvars implargs env = + let open CAst in + function + | { loc; v = Anonymous } -> + if global_level then + user_err ?loc (str "Anonymous variables not allowed"); + env + | { loc; v = Name id } -> + check_hidden_implicit_parameters ?loc id env.impls ; + if Id.Map.is_empty ntnvars && Id.equal id ldots_var + then error_ldots_var ?loc; + set_var_scope ?loc id false (env.tmp_scope,env.scopes) ntnvars; + if global_level then Dumpglob.dump_definition CAst.(make ?loc id) true "var" + else Dumpglob.dump_binding ?loc id; + {env with ids = Id.Set.add id env.ids; impls = Id.Map.add id implargs env.impls} + +let intern_generalized_binder ?(global_level=false) intern_type ntnvars + env {loc;v=na} b b' t ty = + let ids = (match na with Anonymous -> fun x -> x | Name na -> Id.Set.add na) env.ids in + let ty, ids' = + if t then ty, ids else + Implicit_quantifiers.implicit_application ids + Implicit_quantifiers.combine_params_freevar ty + in + let ty' = intern_type {env with ids = ids; unb = true} ty in + let fvs = Implicit_quantifiers.generalizable_vars_of_glob_constr ~bound:ids ~allowed:ids' ty' in + let env' = List.fold_left + (fun env {loc;v=x} -> push_name_env ~global_level ntnvars (Variable,[],[],[])(*?*) env (make ?loc @@ Name x)) + env fvs in + let bl = List.map + CAst.(map (fun id -> + (Name id, b, DAst.make ?loc @@ GHole (Evar_kinds.BinderType (Name id), IntroAnonymous, None)))) + fvs + in + let na = match na with + | Anonymous -> + if global_level then na + else + let name = + let id = + match ty with + | { v = CApp ((_, { v = CRef (qid,_) } ), _) } when qualid_is_ident qid -> + qualid_basename qid + | _ -> default_non_dependent_ident + in Implicit_quantifiers.make_fresh ids' (Global.env ()) id + in Name name + | _ -> na + in (push_name_env ~global_level ntnvars (impls_type_list ty')(*?*) env' (make ?loc na)), (make ?loc (na,b',ty')) :: List.rev bl + +let intern_assumption intern ntnvars env nal bk ty = + let intern_type env = intern (set_type_scope env) in + match bk with + | Default k -> + let ty = intern_type env ty in + check_capture ty nal; + let impls = impls_type_list ty in + List.fold_left + (fun (env, bl) ({loc;v=na} as locna) -> + (push_name_env ntnvars impls env locna, + (make ?loc (na,k,locate_if_hole ?loc na ty))::bl)) + (env, []) nal + | Generalized (b,b',t) -> + let env, b = intern_generalized_binder intern_type ntnvars env (List.hd nal) b b' t ty in + env, b + +let glob_local_binder_of_extended = DAst.with_loc_val (fun ?loc -> function + | GLocalAssum (na,bk,t) -> (na,bk,None,t) + | GLocalDef (na,bk,c,Some t) -> (na,bk,Some c,t) + | GLocalDef (na,bk,c,None) -> + let t = DAst.make ?loc @@ GHole(Evar_kinds.BinderType na,IntroAnonymous,None) in + (na,bk,Some c,t) + | GLocalPattern (_,_,_,_) -> + Loc.raise ?loc (Stream.Error "pattern with quote not allowed here") + ) + +let intern_cases_pattern_fwd = ref (fun _ -> failwith "intern_cases_pattern_fwd") + +let intern_letin_binder intern ntnvars env (({loc;v=na} as locna),def,ty) = + let term = intern env def in + let ty = Option.map (intern env) ty in + (push_name_env ntnvars (impls_term_list term) env locna, + (na,Explicit,term,ty)) + +let intern_cases_pattern_as_binder ?loc ntnvars env p = + let il,disjpat = + let (il, subst_disjpat) = !intern_cases_pattern_fwd ntnvars (None,env.scopes) p in + let substl,disjpat = List.split subst_disjpat in + if not (List.for_all (fun subst -> Id.Map.equal Id.equal subst Id.Map.empty) substl) then + user_err ?loc (str "Unsupported nested \"as\" clause."); + il,disjpat + in + let env = List.fold_right (fun {loc;v=id} env -> push_name_env ntnvars (Variable,[],[],[]) env (make ?loc @@ Name id)) il env in + let na = alias_of_pat (List.hd disjpat) in + let ienv = Name.fold_right Id.Set.remove na env.ids in + let id = Namegen.next_name_away_with_default "pat" na ienv in + let na = make ?loc @@ Name id in + env,((disjpat,il),id),na + +let intern_local_binder_aux ?(global_level=false) intern ntnvars (env,bl) = function + | CLocalAssum(nal,bk,ty) -> + let env, bl' = intern_assumption intern ntnvars env nal bk ty in + let bl' = List.map (fun {loc;v=(na,c,t)} -> DAst.make ?loc @@ GLocalAssum (na,c,t)) bl' in + env, bl' @ bl + | CLocalDef( {loc; v=na} as locna,def,ty) -> + let env,(na,bk,def,ty) = intern_letin_binder intern ntnvars env (locna,def,ty) in + env, (DAst.make ?loc @@ GLocalDef (na,bk,def,ty)) :: bl + | CLocalPattern {loc;v=(p,ty)} -> + let tyc = + match ty with + | Some ty -> ty + | None -> CAst.make ?loc @@ CHole(None,IntroAnonymous,None) + in + let env, ((disjpat,il),id),na = intern_cases_pattern_as_binder ?loc ntnvars env p in + let bk = Default Explicit in + let _, bl' = intern_assumption intern ntnvars env [na] bk tyc in + let {v=(_,bk,t)} = List.hd bl' in + (env, (DAst.make ?loc @@ GLocalPattern((disjpat,List.map (fun x -> x.v) il),id,bk,t)) :: bl) + +let intern_generalization intern env ntnvars loc bk ak c = + let c = intern {env with unb = true} c in + let fvs = Implicit_quantifiers.generalizable_vars_of_glob_constr ~bound:env.ids c in + let env', c' = + let abs = + let pi = match ak with + | Some AbsPi -> true + | Some _ -> false + | None -> + match Notation.current_type_scope_name () with + | Some type_scope -> + let is_type_scope = match env.tmp_scope with + | None -> false + | Some sc -> String.equal sc type_scope + in + is_type_scope || + String.List.mem type_scope env.scopes + | None -> false + in + if pi then + (fun {loc=loc';v=id} acc -> + DAst.make ?loc:(Loc.merge_opt loc' loc) @@ + GProd (Name id, bk, DAst.make ?loc:loc' @@ GHole (Evar_kinds.BinderType (Name id), IntroAnonymous, None), acc)) + else + (fun {loc=loc';v=id} acc -> + DAst.make ?loc:(Loc.merge_opt loc' loc) @@ + GLambda (Name id, bk, DAst.make ?loc:loc' @@ GHole (Evar_kinds.BinderType (Name id), IntroAnonymous, None), acc)) + in + List.fold_right (fun ({loc;v=id} as lid) (env, acc) -> + let env' = push_name_env ntnvars (Variable,[],[],[]) env CAst.(make @@ Name id) in + (env', abs lid acc)) fvs (env,c) + in c' + +let rec expand_binders ?loc mk bl c = + match bl with + | [] -> c + | b :: bl -> + match DAst.get b with + | GLocalDef (n, bk, b, oty) -> + expand_binders ?loc mk bl (DAst.make ?loc @@ GLetIn (n, b, oty, c)) + | GLocalAssum (n, bk, t) -> + expand_binders ?loc mk bl (mk ?loc (n,bk,t) c) + | GLocalPattern ((disjpat,ids), id, bk, ty) -> + let tm = DAst.make ?loc (GVar id) in + (* Distribute the disjunctive patterns over the shared right-hand side *) + let eqnl = List.map (fun pat -> CAst.make ?loc (ids,[pat],c)) disjpat in + let c = DAst.make ?loc @@ GCases (LetPatternStyle, None, [tm,(Anonymous,None)], eqnl) in + expand_binders ?loc mk bl (mk ?loc (Name id,Explicit,ty) c) + +(**********************************************************************) +(* Syntax extensions *) + +let option_mem_assoc id = function + | Some (id',c) -> Id.equal id id' + | None -> false + +let find_fresh_name renaming (terms,termlists,binders,binderlists) avoid id = + let fold1 _ (c, _) accu = Id.Set.union (free_vars_of_constr_expr c) accu in + let fold2 _ (l, _) accu = + let fold accu c = Id.Set.union (free_vars_of_constr_expr c) accu in + List.fold_left fold accu l + in + let fold3 _ x accu = Id.Set.add x accu in + let fvs1 = Id.Map.fold fold1 terms avoid in + let fvs2 = Id.Map.fold fold2 termlists fvs1 in + let fvs3 = Id.Map.fold fold3 renaming fvs2 in + (* TODO binders *) + next_ident_away_from id (fun id -> Id.Set.mem id fvs3) + +let is_var store pat = + match DAst.get pat with + | PatVar na -> ignore(store na); true + | _ -> false + +let out_var pat = + match pat.v with + | CPatAtom (Some qid) when qualid_is_ident qid -> + Name (qualid_basename qid) + | CPatAtom None -> Anonymous + | _ -> assert false + +let term_of_name = function + | Name id -> DAst.make (GVar id) + | Anonymous -> + let st = Evar_kinds.Define (not (Program.get_proofs_transparency ())) in + DAst.make (GHole (Evar_kinds.QuestionMark { Evar_kinds.default_question_mark with Evar_kinds.qm_obligation=st }, IntroAnonymous, None)) + +let traverse_binder intern_pat ntnvars (terms,_,binders,_ as subst) avoid (renaming,env) = function + | Anonymous -> (renaming,env), None, Anonymous + | Name id -> + let store,get = set_temporary_memory () in + try + (* We instantiate binder name with patterns which may be parsed as terms *) + let pat = coerce_to_cases_pattern_expr (fst (Id.Map.find id terms)) in + let env,((disjpat,ids),id),na = intern_pat ntnvars env pat in + let pat, na = match disjpat with + | [pat] when is_var store pat -> let na = get () in None, na + | _ -> Some ((List.map (fun x -> x.v) ids,disjpat),id), na.v in + (renaming,env), pat, na + with Not_found -> + try + (* Trying to associate a pattern *) + let pat,(onlyident,scopes) = Id.Map.find id binders in + let env = set_env_scopes env scopes in + if onlyident then + (* Do not try to interpret a variable as a constructor *) + let na = out_var pat in + let env = push_name_env ntnvars (Variable,[],[],[]) env (make ?loc:pat.loc na) in + (renaming,env), None, na + else + (* Interpret as a pattern *) + let env,((disjpat,ids),id),na = intern_pat ntnvars env pat in + let pat, na = + match disjpat with + | [pat] when is_var store pat -> let na = get () in None, na + | _ -> Some ((List.map (fun x -> x.v) ids,disjpat),id), na.v in + (renaming,env), pat, na + with Not_found -> + (* Binders not bound in the notation do not capture variables *) + (* outside the notation (i.e. in the substitution) *) + let id' = find_fresh_name renaming subst avoid id in + let renaming' = + if Id.equal id id' then renaming else Id.Map.add id id' renaming + in + (renaming',env), None, Name id' + +type binder_action = +| AddLetIn of lname * constr_expr * constr_expr option +| AddTermIter of (constr_expr * subscopes) Names.Id.Map.t +| AddPreBinderIter of Id.t * local_binder_expr (* A binder to be internalized *) +| AddBinderIter of Id.t * extended_glob_local_binder (* A binder already internalized - used for generalized binders *) + +let dmap_with_loc f n = + CAst.map_with_loc (fun ?loc c -> f ?loc (DAst.get_thunk c)) n + +let error_cannot_coerce_wildcard_term ?loc () = + user_err ?loc Pp.(str "Cannot turn \"_\" into a term.") + +let error_cannot_coerce_disjunctive_pattern_term ?loc () = + user_err ?loc Pp.(str "Cannot turn a disjunctive pattern into a term.") + +let terms_of_binders bl = + let rec term_of_pat pt = dmap_with_loc (fun ?loc -> function + | PatVar (Name id) -> CRef (qualid_of_ident id, None) + | PatVar (Anonymous) -> error_cannot_coerce_wildcard_term ?loc () + | PatCstr (c,l,_) -> + let qid = qualid_of_path ?loc (Nametab.path_of_global (ConstructRef c)) in + let hole = CAst.make ?loc @@ CHole (None,IntroAnonymous,None) in + let params = List.make (Inductiveops.inductive_nparams (fst c)) hole in + CAppExpl ((None,qid,None),params @ List.map term_of_pat l)) pt in + let rec extract_variables l = match l with + | bnd :: l -> + let loc = bnd.loc in + begin match DAst.get bnd with + | GLocalAssum (Name id,_,_) -> (CAst.make ?loc @@ CRef (qualid_of_ident ?loc id, None)) :: extract_variables l + | GLocalDef (Name id,_,_,_) -> extract_variables l + | GLocalDef (Anonymous,_,_,_) + | GLocalAssum (Anonymous,_,_) -> user_err Pp.(str "Cannot turn \"_\" into a term.") + | GLocalPattern (([u],_),_,_,_) -> term_of_pat u :: extract_variables l + | GLocalPattern ((_,_),_,_,_) -> error_cannot_coerce_disjunctive_pattern_term ?loc () + end + | [] -> [] in + extract_variables bl + +let flatten_generalized_binders_if_any y l = + match List.rev l with + | [] -> assert false + | a::l -> a, List.map (fun a -> AddBinderIter (y,a)) l (* if l not empty, this means we had a generalized binder *) + +let flatten_binders bl = + let dispatch = function + | CLocalAssum (nal,bk,t) -> List.map (fun na -> CLocalAssum ([na],bk,t)) nal + | a -> [a] in + List.flatten (List.map dispatch bl) + +let instantiate_notation_constr loc intern intern_pat ntnvars subst infos c = + let (terms,termlists,binders,binderlists) = subst in + (* when called while defining a notation, avoid capturing the private binders + of the expression by variables bound by the notation (see #3892) *) + let avoid = Id.Map.domain ntnvars in + let rec aux (terms,binderopt,iteropt as subst') (renaming,env) c = + let subinfos = renaming,{env with tmp_scope = None} in + match c with + | NVar id when Id.equal id ldots_var -> + let rec aux_letin env = function + | [],terminator,_ -> aux (terms,None,None) (renaming,env) terminator + | AddPreBinderIter (y,binder)::rest,terminator,iter -> + let env,binders = intern_local_binder_aux intern ntnvars (env,[]) binder in + let binder,extra = flatten_generalized_binders_if_any y binders in + aux (terms,Some (y,binder),Some (extra@rest,terminator,iter)) (renaming,env) iter + | AddBinderIter (y,binder)::rest,terminator,iter -> + aux (terms,Some (y,binder),Some (rest,terminator,iter)) (renaming,env) iter + | AddTermIter nterms::rest,terminator,iter -> + aux (nterms,None,Some (rest,terminator,iter)) (renaming,env) iter + | AddLetIn (na,c,t)::rest,terminator,iter -> + let env,(na,_,c,t) = intern_letin_binder intern ntnvars env (na,c,t) in + DAst.make ?loc (GLetIn (na,c,t,aux_letin env (rest,terminator,iter))) in + aux_letin env (Option.get iteropt) + | NVar id -> subst_var subst' (renaming, env) id + | NList (x,y,iter,terminator,revert) -> + let l,(scopt,subscopes) = + (* All elements of the list are in scopes (scopt,subscopes) *) + try + let l,scopes = Id.Map.find x termlists in + (if revert then List.rev l else l),scopes + with Not_found -> + try + let (bl,(scopt,subscopes)) = Id.Map.find x binderlists in + let env,bl' = List.fold_left (intern_local_binder_aux intern ntnvars) (env,[]) bl in + terms_of_binders (if revert then bl' else List.rev bl'),(None,[]) + with Not_found -> + anomaly (Pp.str "Inconsistent substitution of recursive notation.") in + let l = List.map (fun a -> AddTermIter ((Id.Map.add y (a,(scopt,subscopes)) terms))) l in + aux (terms,None,Some (l,terminator,iter)) subinfos (NVar ldots_var) + | NHole (knd, naming, arg) -> + let knd = match knd with + | Evar_kinds.BinderType (Name id as na) -> + let na = + try (coerce_to_name (fst (Id.Map.find id terms))).v + with Not_found -> + try Name (Id.Map.find id renaming) + with Not_found -> na + in + Evar_kinds.BinderType na + | _ -> knd + in + let arg = match arg with + | None -> None + | Some arg -> + let mk_env id (c, (tmp_scope, subscopes)) map = + let nenv = {env with tmp_scope; scopes = subscopes @ env.scopes} in + try + let gc = intern nenv c in + Id.Map.add id (gc, None) map + with Nametab.GlobalizationError _ -> map + in + let mk_env' (c, (onlyident,(tmp_scope,subscopes))) = + let nenv = {env with tmp_scope; scopes = subscopes @ env.scopes} in + if onlyident then + let na = out_var c in term_of_name na, None + else + let _,((disjpat,_),_),_ = intern_pat ntnvars nenv c in + match disjpat with + | [pat] -> (glob_constr_of_cases_pattern pat, None) + | _ -> error_cannot_coerce_disjunctive_pattern_term ?loc:c.loc () + in + let terms = Id.Map.fold mk_env terms Id.Map.empty in + let binders = Id.Map.map mk_env' binders in + let bindings = Id.Map.fold Id.Map.add terms binders in + Some (Genintern.generic_substitute_notation bindings arg) + in + DAst.make ?loc @@ GHole (knd, naming, arg) + | NBinderList (x,y,iter,terminator,revert) -> + (try + (* All elements of the list are in scopes (scopt,subscopes) *) + let (bl,(scopt,subscopes)) = Id.Map.find x binderlists in + (* We flatten binders so that we can interpret them at substitution time *) + let bl = flatten_binders bl in + let bl = if revert then List.rev bl else bl in + (* We isolate let-ins which do not contribute to the repeated pattern *) + let l = List.map (function | CLocalDef (na,c,t) -> AddLetIn (na,c,t) + | binder -> AddPreBinderIter (y,binder)) bl in + (* We stack the binders to iterate or let-ins to insert *) + aux (terms,None,Some (l,terminator,iter)) subinfos (NVar ldots_var) + with Not_found -> + anomaly (Pp.str "Inconsistent substitution of recursive notation.")) + | NProd (Name id, NHole _, c') when option_mem_assoc id binderopt -> + let binder = snd (Option.get binderopt) in + expand_binders ?loc mkGProd [binder] (aux subst' (renaming,env) c') + | NLambda (Name id,NHole _,c') when option_mem_assoc id binderopt -> + let binder = snd (Option.get binderopt) in + expand_binders ?loc mkGLambda [binder] (aux subst' (renaming,env) c') + (* Two special cases to keep binder name synchronous with BinderType *) + | NProd (na,NHole(Evar_kinds.BinderType na',naming,arg),c') + when Name.equal na na' -> + let subinfos,disjpat,na = traverse_binder intern_pat ntnvars subst avoid subinfos na in + let ty = DAst.make ?loc @@ GHole (Evar_kinds.BinderType na,naming,arg) in + DAst.make ?loc @@ GProd (na,Explicit,ty,Option.fold_right apply_cases_pattern disjpat (aux subst' subinfos c')) + | NLambda (na,NHole(Evar_kinds.BinderType na',naming,arg),c') + when Name.equal na na' -> + let subinfos,disjpat,na = traverse_binder intern_pat ntnvars subst avoid subinfos na in + let ty = DAst.make ?loc @@ GHole (Evar_kinds.BinderType na,naming,arg) in + DAst.make ?loc @@ GLambda (na,Explicit,ty,Option.fold_right apply_cases_pattern disjpat (aux subst' subinfos c')) + | t -> + glob_constr_of_notation_constr_with_binders ?loc + (traverse_binder intern_pat ntnvars subst avoid) (aux subst') subinfos t + and subst_var (terms, binderopt, _terminopt) (renaming, env) id = + (* subst remembers the delimiters stack in the interpretation *) + (* of the notations *) + try + let (a,(scopt,subscopes)) = Id.Map.find id terms in + intern {env with tmp_scope = scopt; + scopes = subscopes @ env.scopes} a + with Not_found -> + try + let pat,(onlyident,scopes) = Id.Map.find id binders in + let env = set_env_scopes env scopes in + (* We deactivate impls to avoid the check on hidden parameters *) + (* and since we are only interested in the pattern as a term *) + let env = reset_hidden_inductive_implicit_test env in + if onlyident then + term_of_name (out_var pat) + else + let env,((disjpat,ids),id),na = intern_pat ntnvars env pat in + match disjpat with + | [pat] -> glob_constr_of_cases_pattern pat + | _ -> user_err Pp.(str "Cannot turn a disjunctive pattern into a term.") + with Not_found -> + try + match binderopt with + | Some (x,binder) when Id.equal x id -> + let terms = terms_of_binders [binder] in + assert (List.length terms = 1); + intern env (List.hd terms) + | _ -> raise Not_found + with Not_found -> + DAst.make ?loc ( + try + GVar (Id.Map.find id renaming) + with Not_found -> + (* Happens for local notation joint with inductive/fixpoint defs *) + GVar id) + in aux (terms,None,None) infos c + +(* Turning substitution coming from parsing and based on production + into a substitution for interpretation and based on binding/constr + distinction *) + +let cases_pattern_of_name {loc;v=na} = + let atom = match na with Name id -> Some (qualid_of_ident ?loc id) | Anonymous -> None in + CAst.make ?loc (CPatAtom atom) + +let split_by_type ids subst = + let bind id scl l s = + match l with + | [] -> assert false + | a::l -> l, Id.Map.add id (a,scl) s in + let (terms,termlists,binders,binderlists),subst = + List.fold_left (fun ((terms,termlists,binders,binderlists),(terms',termlists',binders',binderlists')) (id,((_,scl),typ)) -> + match typ with + | NtnTypeConstr -> + let terms,terms' = bind id scl terms terms' in + (terms,termlists,binders,binderlists),(terms',termlists',binders',binderlists') + | NtnTypeBinder NtnBinderParsedAsConstr (AsIdentOrPattern | AsStrictPattern) -> + let a,terms = match terms with a::terms -> a,terms | _ -> assert false in + let binders' = Id.Map.add id (coerce_to_cases_pattern_expr a,(false,scl)) binders' in + (terms,termlists,binders,binderlists),(terms',termlists',binders',binderlists') + | NtnTypeBinder NtnBinderParsedAsConstr AsIdent -> + let a,terms = match terms with a::terms -> a,terms | _ -> assert false in + let binders' = Id.Map.add id (cases_pattern_of_name (coerce_to_name a),(true,scl)) binders' in + (terms,termlists,binders,binderlists),(terms',termlists',binders',binderlists') + | NtnTypeBinder (NtnParsedAsIdent | NtnParsedAsPattern _ as x) -> + let onlyident = (x = NtnParsedAsIdent) in + let binders,binders' = bind id (onlyident,scl) binders binders' in + (terms,termlists,binders,binderlists),(terms',termlists',binders',binderlists') + | NtnTypeConstrList -> + let termlists,termlists' = bind id scl termlists termlists' in + (terms,termlists,binders,binderlists),(terms',termlists',binders',binderlists') + | NtnTypeBinderList -> + let binderlists,binderlists' = bind id scl binderlists binderlists' in + (terms,termlists,binders,binderlists),(terms',termlists',binders',binderlists')) + (subst,(Id.Map.empty,Id.Map.empty,Id.Map.empty,Id.Map.empty)) ids in + assert (terms = [] && termlists = [] && binders = [] && binderlists = []); + subst + +let split_by_type_pat ?loc ids subst = + let bind id (_,scopes) l s = + match l with + | [] -> assert false + | a::l -> l, Id.Map.add id (a,scopes) s in + let (terms,termlists),subst = + List.fold_left (fun ((terms,termlists),(terms',termlists')) (id,(scl,typ)) -> + match typ with + | NtnTypeConstr | NtnTypeBinder _ -> + let terms,terms' = bind id scl terms terms' in + (terms,termlists),(terms',termlists') + | NtnTypeConstrList -> + let termlists,termlists' = bind id scl termlists termlists' in + (terms,termlists),(terms',termlists') + | NtnTypeBinderList -> error_invalid_pattern_notation ?loc ()) + (subst,(Id.Map.empty,Id.Map.empty)) ids in + assert (terms = [] && termlists = []); + subst + +let make_subst ids l = + let fold accu (id, scopes) a = Id.Map.add id (a, scopes) accu in + List.fold_left2 fold Id.Map.empty ids l + +let intern_notation intern env ntnvars loc ntn fullargs = + (* Adjust to parsing of { } *) + let ntn,fullargs = contract_curly_brackets ntn fullargs in + (* Recover interpretation { } *) + let ((ids,c),df) = interp_notation ?loc ntn (env.tmp_scope,env.scopes) in + Dumpglob.dump_notation_location (ntn_loc ?loc fullargs ntn) ntn df; + (* Dispatch parsing substitution to an interpretation substitution *) + let subst = split_by_type ids fullargs in + (* Instantiate the notation *) + instantiate_notation_constr loc intern intern_cases_pattern_as_binder ntnvars subst (Id.Map.empty, env) c + +(**********************************************************************) +(* Discriminating between bound variables and global references *) + +let string_of_ty = function + | Inductive _ -> "ind" + | Recursive -> "def" + | Method -> "meth" + | Variable -> "var" + +let gvar (loc, id) us = match us with +| None -> DAst.make ?loc @@ GVar id +| Some _ -> + user_err ?loc (str "Variable " ++ Id.print id ++ + str " cannot have a universe instance") + +let intern_var env (ltacvars,ntnvars) namedctx loc id us = + (* Is [id] a notation variable *) + if Id.Map.mem id ntnvars then + begin + if not (Id.Map.mem id env.impls) then set_var_scope ?loc id true (env.tmp_scope,env.scopes) ntnvars; + gvar (loc,id) us, [], [], [] + end + else + (* Is [id] registered with implicit arguments *) + try + let ty,expl_impls,impls,argsc = Id.Map.find id env.impls in + let expl_impls = List.map + (fun id -> CAst.make ?loc @@ CRef (qualid_of_ident ?loc id,None), Some (make ?loc @@ ExplByName id)) expl_impls in + let tys = string_of_ty ty in + Dumpglob.dump_reference ?loc "<>" (Id.to_string id) tys; + gvar (loc,id) us, make_implicits_list impls, argsc, expl_impls + with Not_found -> + (* Is [id] bound in current term or is an ltac var bound to constr *) + if Id.Set.mem id env.ids || Id.Set.mem id ltacvars.ltac_vars + then + gvar (loc,id) us, [], [], [] + else if Id.equal id ldots_var + (* Is [id] the special variable for recursive notations? *) + then if Id.Map.is_empty ntnvars + then error_ldots_var ?loc + else gvar (loc,id) us, [], [], [] + else if Id.Set.mem id ltacvars.ltac_bound then + (* Is [id] bound to a free name in ltac (this is an ltac error message) *) + user_err ?loc ~hdr:"intern_var" + (str "variable " ++ Id.print id ++ str " should be bound to a term.") + else + (* Is [id] a goal or section variable *) + let _ = Context.Named.lookup id namedctx in + try + (* [id] a section variable *) + (* Redundant: could be done in intern_qualid *) + let ref = VarRef id in + let impls = implicits_of_global ref in + let scopes = find_arguments_scope ref in + Dumpglob.dump_reference ?loc "<>" (string_of_qualid (Decls.variable_secpath id)) "var"; + DAst.make ?loc @@ GRef (ref, us), impls, scopes, [] + with e when CErrors.noncritical e -> + (* [id] a goal variable *) + gvar (loc,id) us, [], [], [] + +let find_appl_head_data c = + match DAst.get c with + | GRef (ref,_) -> + let impls = implicits_of_global ref in + let scopes = find_arguments_scope ref in + c, impls, scopes, [] + | GApp (r, l) -> + begin match DAst.get r with + | GRef (ref,_) when l != [] -> + let n = List.length l in + let impls = implicits_of_global ref in + let scopes = find_arguments_scope ref in + c, List.map (drop_first_implicits n) impls, + List.skipn_at_least n scopes,[] + | _ -> c,[],[],[] + end + | _ -> c,[],[],[] + +let error_not_enough_arguments ?loc = + user_err ?loc (str "Abbreviation is not applied enough.") + +let check_no_explicitation l = + let is_unset (a, b) = match b with None -> false | Some _ -> true in + let l = List.filter is_unset l in + match l with + | [] -> () + | (_, None) :: _ -> assert false + | (_, Some {loc}) :: _ -> + user_err ?loc (str"Unexpected explicitation of the argument of an abbreviation.") + +let dump_extended_global loc = function + | TrueGlobal ref -> (*feedback_global loc ref;*) Dumpglob.add_glob ?loc ref + | SynDef sp -> Dumpglob.add_glob_kn ?loc sp + +let intern_extended_global_of_qualid qid = + let r = Nametab.locate_extended qid in dump_extended_global qid.CAst.loc r; r + +let intern_reference qid = + let r = + try intern_extended_global_of_qualid qid + with Not_found -> Nametab.error_global_not_found qid + in + Smartlocate.global_of_extended_global r + +let sort_info_of_level_info (info: level_info) : (Libnames.qualid * int) option = + match info with + | UAnonymous -> None + | UUnknown -> None + | UNamed id -> Some (id, 0) + +let glob_sort_of_level (level: glob_level) : glob_sort = + match level with + | GProp -> GProp + | GSet -> GSet + | GType info -> GType [sort_info_of_level_info info] + +(* Is it a global reference or a syntactic definition? *) +let intern_qualid ?(no_secvar=false) qid intern env ntnvars us args = + let loc = qid.loc in + match intern_extended_global_of_qualid qid with + | TrueGlobal (VarRef _) when no_secvar -> + (* Rule out section vars since these should have been found by intern_var *) + raise Not_found + | TrueGlobal ref -> (DAst.make ?loc @@ GRef (ref, us)), true, args + | SynDef sp -> + let (ids,c) = Syntax_def.search_syntactic_definition ?loc sp in + let nids = List.length ids in + if List.length args < nids then error_not_enough_arguments ?loc; + let args1,args2 = List.chop nids args in + check_no_explicitation args1; + let terms = make_subst ids (List.map fst args1) in + let subst = (terms, Id.Map.empty, Id.Map.empty, Id.Map.empty) in + let infos = (Id.Map.empty, env) in + let projapp = match c with NRef _ -> true | _ -> false in + let c = instantiate_notation_constr loc intern intern_cases_pattern_as_binder ntnvars subst infos c in + let loc = c.loc in + let err () = + user_err ?loc (str "Notation " ++ pr_qualid qid + ++ str " cannot have a universe instance," + ++ str " its expanded head does not start with a reference") + in + let c = match us, DAst.get c with + | None, _ -> c + | Some _, GRef (ref, None) -> DAst.make ?loc @@ GRef (ref, us) + | Some _, GApp (r, arg) -> + let loc' = r.CAst.loc in + begin match DAst.get r with + | GRef (ref, None) -> + DAst.make ?loc @@ GApp (DAst.make ?loc:loc' @@ GRef (ref, us), arg) + | _ -> err () + end + | Some [s], GSort (GType []) -> DAst.make ?loc @@ GSort (glob_sort_of_level s) + | Some [_old_level], GSort _new_sort -> + (* TODO: add old_level and new_sort to the error message *) + user_err ?loc (str "Cannot change universe level of notation " ++ pr_qualid qid) + | Some _, _ -> err () + in + c, projapp, args2 + +let intern_applied_reference intern env namedctx (_, ntnvars as lvar) us args qid = + let loc = qid.CAst.loc in + if qualid_is_ident qid then + try intern_var env lvar namedctx loc (qualid_basename qid) us, args + with Not_found -> + try + let r, projapp, args2 = intern_qualid ~no_secvar:true qid intern env ntnvars us args in + let x, imp, scopes, l = find_appl_head_data r in + (x,imp,scopes,l), args2 + with Not_found -> + (* Extra allowance for non globalizing functions *) + if !interning_grammar || env.unb then + (gvar (loc,qualid_basename qid) us, [], [], []), args + else Nametab.error_global_not_found qid + else + let r,projapp,args2 = + try intern_qualid qid intern env ntnvars us args + with Not_found -> Nametab.error_global_not_found qid + in + let x, imp, scopes, l = find_appl_head_data r in + (x,imp,scopes,l), args2 + +let interp_reference vars r = + let (r,_,_,_),_ = + intern_applied_reference (fun _ -> error_not_enough_arguments ?loc:None) + {ids = Id.Set.empty; unb = false ; + tmp_scope = None; scopes = []; impls = empty_internalization_env} [] + (vars, Id.Map.empty) None [] r + in r + +(**********************************************************************) +(** {5 Cases } *) + +(** Private internalization patterns *) +type 'a raw_cases_pattern_expr_r = + | RCPatAlias of 'a raw_cases_pattern_expr * lname + | RCPatCstr of GlobRef.t + * 'a raw_cases_pattern_expr list * 'a raw_cases_pattern_expr list + (** [RCPatCstr (loc, c, l1, l2)] represents [((@ c l1) l2)] *) + | RCPatAtom of (lident * (Notation_term.tmp_scope_name option * Notation_term.scope_name list)) option + | RCPatOr of 'a raw_cases_pattern_expr list +and 'a raw_cases_pattern_expr = ('a raw_cases_pattern_expr_r, 'a) DAst.t + +(** {6 Elementary bricks } *) +let apply_scope_env env = function + | [] -> {env with tmp_scope = None}, [] + | sc::scl -> {env with tmp_scope = sc}, scl + +let rec simple_adjust_scopes n scopes = + (* Note: they can be less scopes than arguments but also more scopes *) + (* than arguments because extra scopes are used in the presence of *) + (* coercions to funclass *) + if Int.equal n 0 then [] else match scopes with + | [] -> None :: simple_adjust_scopes (n-1) [] + | sc::scopes -> sc :: simple_adjust_scopes (n-1) scopes + +let find_remaining_scopes pl1 pl2 ref = + let impls_st = implicits_of_global ref in + let len_pl1 = List.length pl1 in + let len_pl2 = List.length pl2 in + let impl_list = if Int.equal len_pl1 0 + then select_impargs_size len_pl2 impls_st + else List.skipn_at_least len_pl1 (select_stronger_impargs impls_st) in + let allscs = find_arguments_scope ref in + let scope_list = List.skipn_at_least len_pl1 allscs in + let rec aux = function + |[],l -> l + |_,[] -> [] + |h::t,_::tt when is_status_implicit h -> aux (t,tt) + |_::t,h::tt -> h :: aux (t,tt) + in ((try List.firstn len_pl1 allscs with Failure _ -> simple_adjust_scopes len_pl1 allscs), + simple_adjust_scopes len_pl2 (aux (impl_list,scope_list))) + +(* @return the first variable that occurs twice in a pattern + +naive n^2 algo *) +let rec has_duplicate = function + | [] -> None + | x::l -> if Id.List.mem x l then (Some x) else has_duplicate l + +let loc_of_multiple_pattern pl = + Loc.merge_opt (cases_pattern_expr_loc (List.hd pl)) (cases_pattern_expr_loc (List.last pl)) + +let loc_of_lhs lhs = + Loc.merge_opt (loc_of_multiple_pattern (List.hd lhs)) (loc_of_multiple_pattern (List.last lhs)) + +let check_linearity lhs ids = + match has_duplicate ids with + | Some id -> + raise (InternalizationError (loc_of_lhs lhs,NonLinearPattern id)) + | None -> + () + +(* Match the number of pattern against the number of matched args *) +let check_number_of_pattern loc n l = + let p = List.length l in + if not (Int.equal n p) then raise (InternalizationError (loc,BadPatternsNumber (n,p))) + +let check_or_pat_variables loc ids idsl = + let eq_id {v=id} {v=id'} = Id.equal id id' in + (* Collect remaining patterns which do not have the same variables as the first pattern *) + let idsl = List.filter (fun ids' -> not (List.eq_set eq_id ids ids')) idsl in + match idsl with + | ids'::_ -> + (* Look for an [id] which is either in [ids] and not in [ids'] or in [ids'] and not in [ids] *) + let ids'' = List.subtract eq_id ids ids' in + let ids'' = if ids'' = [] then List.subtract eq_id ids' ids else ids'' in + user_err ?loc + (strbrk "The components of this disjunctive pattern must bind the same variables (" ++ + Id.print (List.hd ids'').v ++ strbrk " is not bound in all patterns).") + | [] -> () + +(** Use only when params were NOT asked to the user. + @return if letin are included *) +let check_constructor_length env loc cstr len_pl pl0 = + let n = len_pl + List.length pl0 in + if Int.equal n (Inductiveops.constructor_nallargs cstr) then false else + (Int.equal n (Inductiveops.constructor_nalldecls cstr) || + (error_wrong_numarg_constructor ?loc env cstr + (Inductiveops.constructor_nrealargs cstr))) + +open Term +open Declarations + +(* Similar to Cases.adjust_local_defs but on RCPat *) +let insert_local_defs_in_pattern (ind,j) l = + let (mib,mip) = Global.lookup_inductive ind in + if mip.mind_consnrealdecls.(j-1) = mip.mind_consnrealargs.(j-1) then + (* Optimisation *) l + else + let typi = mip.mind_nf_lc.(j-1) in + let (_,typi) = decompose_prod_n_assum (Context.Rel.length mib.mind_params_ctxt) typi in + let (decls,_) = decompose_prod_assum typi in + let rec aux decls args = + match decls, args with + | Context.Rel.Declaration.LocalDef _ :: decls, args -> (DAst.make @@ RCPatAtom None) :: aux decls args + | _, [] -> [] (* In particular, if there were trailing local defs, they have been inserted *) + | Context.Rel.Declaration.LocalAssum _ :: decls, a :: args -> a :: aux decls args + | _ -> assert false in + aux (List.rev decls) l + +let add_local_defs_and_check_length loc env g pl args = match g with + | ConstructRef cstr -> + (* We consider that no variables corresponding to local binders + have been given in the "explicit" arguments, which come from a + "@C args" notation or from a custom user notation *) + let pl' = insert_local_defs_in_pattern cstr pl in + let maxargs = Inductiveops.constructor_nalldecls cstr in + if List.length pl' + List.length args > maxargs then + error_wrong_numarg_constructor ?loc env cstr (Inductiveops.constructor_nrealargs cstr); + (* Two possibilities: either the args are given with explict + variables for local definitions, then we give the explicit args + extended with local defs, so that there is nothing more to be + added later on; or the args are not enough to have all arguments, + which a priori means local defs to add in the [args] part, so we + postpone the insertion of local defs in the explicit args *) + (* Note: further checks done later by check_constructor_length *) + if List.length pl' + List.length args = maxargs then pl' else pl + | _ -> pl + +let add_implicits_check_length fail nargs nargs_with_letin impls_st len_pl1 pl2 = + let impl_list = if Int.equal len_pl1 0 + then select_impargs_size (List.length pl2) impls_st + else List.skipn_at_least len_pl1 (select_stronger_impargs impls_st) in + let remaining_args = List.fold_left (fun i x -> if is_status_implicit x then i else succ i) in + let rec aux i = function + |[],l -> let args_len = List.length l + List.length impl_list + len_pl1 in + ((if Int.equal args_len nargs then false + else Int.equal args_len nargs_with_letin || (fst (fail (nargs - List.length impl_list + i)))) + ,l) + |imp::q as il,[] -> if is_status_implicit imp && maximal_insertion_of imp + then let (b,out) = aux i (q,[]) in (b,(DAst.make @@ RCPatAtom None)::out) + else fail (remaining_args (len_pl1+i) il) + |imp::q,(hh::tt as l) -> if is_status_implicit imp + then let (b,out) = aux i (q,l) in (b,(DAst.make @@ RCPatAtom None)::out) + else let (b,out) = aux (succ i) (q,tt) in (b,hh::out) + in aux 0 (impl_list,pl2) + +let add_implicits_check_constructor_length env loc c len_pl1 pl2 = + let nargs = Inductiveops.constructor_nallargs c in + let nargs' = Inductiveops.constructor_nalldecls c in + let impls_st = implicits_of_global (ConstructRef c) in + add_implicits_check_length (error_wrong_numarg_constructor ?loc env c) + nargs nargs' impls_st len_pl1 pl2 + +let add_implicits_check_ind_length env loc c len_pl1 pl2 = + let nallargs = inductive_nallargs_env env c in + let nalldecls = inductive_nalldecls_env env c in + let impls_st = implicits_of_global (IndRef c) in + add_implicits_check_length (error_wrong_numarg_inductive ?loc env c) + nallargs nalldecls impls_st len_pl1 pl2 + +(** Do not raise NotEnoughArguments thanks to preconditions*) +let chop_params_pattern loc ind args with_letin = + let nparams = if with_letin + then Inductiveops.inductive_nparamdecls ind + else Inductiveops.inductive_nparams ind in + assert (nparams <= List.length args); + let params,args = List.chop nparams args in + List.iter (fun c -> match DAst.get c with + | PatVar Anonymous -> () + | PatVar _ | PatCstr(_,_,_) -> error_parameter_not_implicit ?loc:c.CAst.loc) params; + args + +let find_constructor loc add_params ref = + let (ind,_ as cstr) = match ref with + | ConstructRef cstr -> cstr + | IndRef _ -> + let error = str "There is an inductive name deep in a \"in\" clause." in + user_err ?loc ~hdr:"find_constructor" error + | ConstRef _ | VarRef _ -> + let error = str "This reference is not a constructor." in + user_err ?loc ~hdr:"find_constructor" error + in + cstr, match add_params with + | Some nb_args -> + let nb = + if Int.equal nb_args (Inductiveops.constructor_nrealdecls cstr) + then Inductiveops.inductive_nparamdecls ind + else Inductiveops.inductive_nparams ind + in + List.make nb ([], [(Id.Map.empty, DAst.make @@ PatVar Anonymous)]) + | None -> [] + +let find_pattern_variable qid = + if qualid_is_ident qid then qualid_basename qid + else raise (InternalizationError(qid.CAst.loc,NotAConstructor qid)) + +let check_duplicate loc fields = + let eq (ref1, _) (ref2, _) = qualid_eq ref1 ref2 in + let dups = List.duplicates eq fields in + match dups with + | [] -> () + | (r, _) :: _ -> + user_err ?loc (str "This record defines several times the field " ++ + pr_qualid r ++ str ".") + +let inductive_of_record loc record = + let inductive = IndRef (inductive_of_constructor record.Recordops.s_CONST) in + Nametab.shortest_qualid_of_global ?loc Id.Set.empty inductive + +(** [sort_fields ~complete loc fields completer] expects a list + [fields] of field assignments [f = e1; g = e2; ...], where [f, g] + are fields of a record and [e1] are "values" (either terms, when + interning a record construction, or patterns, when intering record + pattern-matching). It will sort the fields according to the record + declaration order (which is important when type-checking them in + presence of dependencies between fields). If the parameter + [complete] is true, we require the assignment to be complete: all + the fields of the record must be present in the + assignment. Otherwise the record assignment may be partial + (in a pattern, we may match on some fields only), and we call the + function [completer] to fill the missing fields; the returned + field assignment list is always complete. *) +let sort_fields ~complete loc fields completer = + match fields with + | [] -> None + | (first_field_ref, first_field_value):: other_fields -> + let (first_field_glob_ref, record) = + try + let gr = global_reference_of_reference first_field_ref in + (gr, Recordops.find_projection gr) + with Not_found -> + raise (InternalizationError(loc, NotAProjection first_field_ref)) + in + (* the number of parameters *) + let nparams = record.Recordops.s_EXPECTEDPARAM in + (* the reference constructor of the record *) + let base_constructor = + let global_record_id = ConstructRef record.Recordops.s_CONST in + try Nametab.shortest_qualid_of_global ?loc Id.Set.empty global_record_id + with Not_found -> + anomaly (str "Environment corruption for records.") in + let () = check_duplicate loc fields in + let (end_index, (* one past the last field index *) + first_field_index, (* index of the first field of the record *) + proj_list) (* list of projections *) + = + (* eliminate the first field from the projections, + but keep its index *) + let rec build_proj_list projs proj_kinds idx ~acc_first_idx acc = + match projs with + | [] -> (idx, acc_first_idx, acc) + | (Some field_glob_id) :: projs -> + let field_glob_ref = ConstRef field_glob_id in + let first_field = GlobRef.equal field_glob_ref first_field_glob_ref in + begin match proj_kinds with + | [] -> anomaly (Pp.str "Number of projections mismatch.") + | (_, regular) :: proj_kinds -> + (* "regular" is false when the field is defined + by a let-in in the record declaration + (its value is fixed from other fields). *) + if first_field && not regular && complete then + user_err ?loc (str "No local fields allowed in a record construction.") + else if first_field then + build_proj_list projs proj_kinds (idx+1) ~acc_first_idx:idx acc + else if not regular && complete then + (* skip non-regular fields *) + build_proj_list projs proj_kinds idx ~acc_first_idx acc + else + build_proj_list projs proj_kinds (idx+1) ~acc_first_idx + ((idx, field_glob_id) :: acc) + end + | None :: projs -> + if complete then + (* we don't want anonymous fields *) + user_err ?loc (str "This record contains anonymous fields.") + else + (* anonymous arguments don't appear in proj_kinds *) + build_proj_list projs proj_kinds (idx+1) ~acc_first_idx acc + in + build_proj_list record.Recordops.s_PROJ record.Recordops.s_PROJKIND 1 ~acc_first_idx:0 [] + in + (* now we want to have all fields assignments indexed by their place in + the constructor *) + let rec index_fields fields remaining_projs acc = + match fields with + | (field_ref, field_value) :: fields -> + let field_glob_ref = try global_reference_of_reference field_ref + with Not_found -> + user_err ?loc ~hdr:"intern" + (str "The field \"" ++ pr_qualid field_ref ++ str "\" does not exist.") in + let this_field_record = try Recordops.find_projection field_glob_ref + with Not_found -> + let inductive_ref = inductive_of_record loc record in + raise (InternalizationError(loc, NotAProjectionOf (field_ref, inductive_ref))) + in + let remaining_projs, (field_index, _) = + let the_proj (idx, glob_id) = GlobRef.equal field_glob_ref (ConstRef glob_id) in + try CList.extract_first the_proj remaining_projs + with Not_found -> + let ind1 = inductive_of_record loc record in + let ind2 = inductive_of_record loc this_field_record in + raise (InternalizationError(loc, ProjectionsOfDifferentRecords (ind1, ind2))) + in + index_fields fields remaining_projs ((field_index, field_value) :: acc) + | [] -> + (* the order does not matter as we sort them next, + List.rev_* is just for efficiency *) + let remaining_fields = + let complete_field (idx, field_ref) = (idx, + completer idx field_ref record.Recordops.s_CONST) in + List.rev_map complete_field remaining_projs in + List.rev_append remaining_fields acc + in + let unsorted_indexed_fields = + index_fields other_fields proj_list + [(first_field_index, first_field_value)] in + let sorted_indexed_fields = + let cmp_by_index (i, _) (j, _) = Int.compare i j in + List.sort cmp_by_index unsorted_indexed_fields in + let sorted_fields = List.map snd sorted_indexed_fields in + Some (nparams, base_constructor, sorted_fields) + +(** {6 Manage multiple aliases} *) + +type alias = { + alias_ids : lident list; + alias_map : Id.t Id.Map.t; +} + +let empty_alias = { + alias_ids = []; + alias_map = Id.Map.empty; +} + + (* [merge_aliases] returns the sets of all aliases encountered at this + point and a substitution mapping extra aliases to the first one *) +let merge_aliases aliases {loc;v=na} = + match na with + | Anonymous -> aliases + | Name id -> + let alias_ids = aliases.alias_ids @ [make ?loc id] in + let alias_map = match aliases.alias_ids with + | [] -> aliases.alias_map + | {v=id'} :: _ -> Id.Map.add id id' aliases.alias_map + in + { alias_ids; alias_map; } + +let alias_of als = match als.alias_ids with +| [] -> Anonymous +| {v=id} :: _ -> Name id + +(** {6 Expanding notations } + + @returns a raw_case_pattern_expr : + - no notations and syntactic definition + - global reference and identifeir instead of reference + +*) + +let is_zero s = + let rec aux i = + Int.equal (String.length s) i || (s.[i] == '0' && aux (i+1)) + in aux 0 + +let merge_subst s1 s2 = Id.Map.fold Id.Map.add s1 s2 + +let product_of_cases_patterns aliases idspl = + (* each [pl] is a disjunction of patterns over common identifiers [ids] *) + (* We stepwise build a disjunction of patterns [ptaill] over common [ids'] *) + List.fold_right (fun (ids,pl) (ids',ptaill) -> + (ids @ ids', + (* Cartesian prod of the or-pats for the nth arg and the tail args *) + List.flatten ( + List.map (fun (subst,p) -> + List.map (fun (subst',ptail) -> (merge_subst subst subst',p::ptail)) ptaill) pl))) + idspl (aliases.alias_ids,[aliases.alias_map,[]]) + +let rec subst_pat_iterator y t = DAst.(map (function + | RCPatAtom id as p -> + begin match id with Some ({v=x},_) when Id.equal x y -> DAst.get t | _ -> p end + | RCPatCstr (id,l1,l2) -> + RCPatCstr (id,List.map (subst_pat_iterator y t) l1, + List.map (subst_pat_iterator y t) l2) + | RCPatAlias (p,a) -> RCPatAlias (subst_pat_iterator y t p,a) + | RCPatOr pl -> RCPatOr (List.map (subst_pat_iterator y t) pl))) + +let is_non_zero c = match c with +| { CAst.v = CPrim (Numeral (p, true)) } -> not (is_zero p) +| _ -> false + +let is_non_zero_pat c = match c with +| { CAst.v = CPatPrim (Numeral (p, true)) } -> not (is_zero p) +| _ -> false + +let get_asymmetric_patterns = Goptions.declare_bool_option_and_ref + ~depr:false + ~name:"no parameters in constructors" + ~key:["Asymmetric";"Patterns"] + ~value:false + +let drop_notations_pattern looked_for genv = + (* At toplevel, Constructors and Inductives are accepted, in recursive calls + only constructor are allowed *) + let ensure_kind top loc g = + try + if top then looked_for g else + match g with ConstructRef _ -> () | _ -> raise Not_found + with Not_found -> + error_invalid_pattern_notation ?loc () + in + let test_kind top = + if top then looked_for else function ConstructRef _ -> () | _ -> raise Not_found + in + (* [rcp_of_glob] : from [glob_constr] to [raw_cases_pattern_expr] *) + let rec rcp_of_glob scopes x = DAst.(map (function + | GVar id -> RCPatAtom (Some (CAst.make ?loc:x.loc id,scopes)) + | GHole (_,_,_) -> RCPatAtom (None) + | GRef (g,_) -> RCPatCstr (g,[],[]) + | GApp (r, l) -> + begin match DAst.get r with + | GRef (g,_) -> + let allscs = find_arguments_scope g in + let allscs = simple_adjust_scopes (List.length l) allscs in (* TO CHECK *) + RCPatCstr (g, List.map2 (fun sc a -> rcp_of_glob (sc,snd scopes) a) allscs l,[]) + | _ -> + CErrors.anomaly Pp.(str "Invalid return pattern from Notation.interp_prim_token_cases_pattern_expr.") + end + | _ -> CErrors.anomaly Pp.(str "Invalid return pattern from Notation.interp_prim_token_cases_pattern_expr."))) x + in + let rec drop_syndef top scopes qid pats = + try + match Nametab.locate_extended qid with + | SynDef sp -> + let filter (vars,a) = + try match a with + | NRef g -> + (* Convention: do not deactivate implicit arguments and scopes for further arguments *) + test_kind top g; + let () = assert (List.is_empty vars) in + let (_,argscs) = find_remaining_scopes [] pats g in + Some (g, [], List.map2 (in_pat_sc scopes) argscs pats) + | NApp (NRef g,[]) -> (* special case: Syndef for @Cstr, this deactivates *) + test_kind top g; + let () = assert (List.is_empty vars) in + Some (g, List.map (in_pat false scopes) pats, []) + | NApp (NRef g,args) -> + (* Convention: do not deactivate implicit arguments and scopes for further arguments *) + test_kind top g; + let nvars = List.length vars in + if List.length pats < nvars then error_not_enough_arguments ?loc:qid.loc; + let pats1,pats2 = List.chop nvars pats in + let subst = make_subst vars pats1 in + let idspl1 = List.map (in_not false qid.loc scopes (subst, Id.Map.empty) []) args in + let (_,argscs) = find_remaining_scopes pats1 pats2 g in + Some (g, idspl1, List.map2 (in_pat_sc scopes) argscs pats2) + | _ -> raise Not_found + with Not_found -> None in + Syntax_def.search_filtered_syntactic_definition filter sp + | TrueGlobal g -> + test_kind top g; + Dumpglob.add_glob ?loc:qid.loc g; + let (_,argscs) = find_remaining_scopes [] pats g in + Some (g,[],List.map2 (fun x -> in_pat false (x,snd scopes)) argscs pats) + with Not_found -> None + and in_pat top scopes pt = + let open CAst in + let loc = pt.loc in + match pt.v with + | CPatAlias (p, id) -> DAst.make ?loc @@ RCPatAlias (in_pat top scopes p, id) + | CPatRecord l -> + let sorted_fields = + sort_fields ~complete:false loc l (fun _idx fieldname constructor -> CAst.make ?loc @@ CPatAtom None) in + begin match sorted_fields with + | None -> DAst.make ?loc @@ RCPatAtom None + | Some (n, head, pl) -> + let pl = + if get_asymmetric_patterns () then pl else + let pars = List.make n (CAst.make ?loc @@ CPatAtom None) in + List.rev_append pars pl in + match drop_syndef top scopes head pl with + | Some (a,b,c) -> DAst.make ?loc @@ RCPatCstr(a, b, c) + | None -> raise (InternalizationError (loc,NotAConstructor head)) + end + | CPatCstr (head, None, pl) -> + begin + match drop_syndef top scopes head pl with + | Some (a,b,c) -> DAst.make ?loc @@ RCPatCstr(a, b, c) + | None -> raise (InternalizationError (loc,NotAConstructor head)) + end + | CPatCstr (qid, Some expl_pl, pl) -> + let g = try Nametab.locate qid + with Not_found -> + raise (InternalizationError (loc,NotAConstructor qid)) in + if expl_pl == [] then + (* Convention: (@r) deactivates all further implicit arguments and scopes *) + DAst.make ?loc @@ RCPatCstr (g, List.map (in_pat false scopes) pl, []) + else + (* Convention: (@r expl_pl) deactivates implicit arguments in expl_pl and in pl *) + (* but not scopes in expl_pl *) + let (argscs1,_) = find_remaining_scopes expl_pl pl g in + DAst.make ?loc @@ RCPatCstr (g, List.map2 (in_pat_sc scopes) argscs1 expl_pl @ List.map (in_pat false scopes) pl, []) + | CPatNotation ((InConstrEntrySomeLevel,"- _"),([a],[]),[]) when is_non_zero_pat a -> + let p = match a.CAst.v with CPatPrim (Numeral (p, _)) -> p | _ -> assert false in + let pat, _df = Notation.interp_prim_token_cases_pattern_expr ?loc (ensure_kind false loc) (Numeral (p,false)) scopes in + rcp_of_glob scopes pat + | CPatNotation ((InConstrEntrySomeLevel,"( _ )"),([a],[]),[]) -> + in_pat top scopes a + | CPatNotation (ntn,fullargs,extrargs) -> + let ntn,(terms,termlists) = contract_curly_brackets_pat ntn fullargs in + let ((ids',c),df) = Notation.interp_notation ?loc ntn scopes in + let (terms,termlists) = split_by_type_pat ?loc ids' (terms,termlists) in + Dumpglob.dump_notation_location (patntn_loc ?loc fullargs ntn) ntn df; + in_not top loc scopes (terms,termlists) extrargs c + | CPatDelimiters (key, e) -> + in_pat top (None,find_delimiters_scope ?loc key::snd scopes) e + | CPatPrim p -> + let pat, _df = Notation.interp_prim_token_cases_pattern_expr ?loc (test_kind false) p scopes in + rcp_of_glob scopes pat + | CPatAtom (Some id) -> + begin + match drop_syndef top scopes id [] with + | Some (a,b,c) -> DAst.make ?loc @@ RCPatCstr (a, b, c) + | None -> DAst.make ?loc @@ RCPatAtom (Some ((make ?loc @@ find_pattern_variable id),scopes)) + end + | CPatAtom None -> DAst.make ?loc @@ RCPatAtom None + | CPatOr pl -> DAst.make ?loc @@ RCPatOr (List.map (in_pat top scopes) pl) + | CPatCast (_,_) -> + (* We raise an error if the pattern contains a cast, due to + current restrictions on casts in patterns. Cast in patterns + are supportted only in local binders and only at top + level. In fact, they are currently eliminated by the + parser. The only reason why they are in the + [cases_pattern_expr] type is that the parser needs to factor + the "(c : t)" notation with user defined notations (such as + the pair). In the long term, we will try to support such + casts everywhere, and use them to print the domains of + lambdas in the encoding of match in constr. This check is + here and not in the parser because it would require + duplicating the levels of the [pattern] rule. *) + CErrors.user_err ?loc ~hdr:"drop_notations_pattern" + (Pp.strbrk "Casts are not supported in this pattern.") + and in_pat_sc scopes x = in_pat false (x,snd scopes) + and in_not top loc scopes (subst,substlist as fullsubst) args = function + | NVar id -> + let () = assert (List.is_empty args) in + begin + (* subst remembers the delimiters stack in the interpretation *) + (* of the notations *) + try + let (a,(scopt,subscopes)) = Id.Map.find id subst in + in_pat top (scopt,subscopes@snd scopes) a + with Not_found -> + if Id.equal id ldots_var then DAst.make ?loc @@ RCPatAtom (Some ((make ?loc id),scopes)) else + anomaly (str "Unbound pattern notation variable: " ++ Id.print id ++ str ".") + end + | NRef g -> + ensure_kind top loc g; + let (_,argscs) = find_remaining_scopes [] args g in + DAst.make ?loc @@ RCPatCstr (g, [], List.map2 (in_pat_sc scopes) argscs args) + | NApp (NRef g,pl) -> + ensure_kind top loc g; + let (argscs1,argscs2) = find_remaining_scopes pl args g in + let pl = List.map2 (fun x -> in_not false loc (x,snd scopes) fullsubst []) argscs1 pl in + let pl = add_local_defs_and_check_length loc genv g pl args in + DAst.make ?loc @@ RCPatCstr (g, pl @ List.map (in_pat false scopes) args, []) + | NList (x,y,iter,terminator,revert) -> + if not (List.is_empty args) then user_err ?loc + (strbrk "Application of arguments to a recursive notation not supported in patterns."); + (try + (* All elements of the list are in scopes (scopt,subscopes) *) + let (l,(scopt,subscopes)) = Id.Map.find x substlist in + let termin = in_not top loc scopes fullsubst [] terminator in + List.fold_right (fun a t -> + let nsubst = Id.Map.add y (a, (scopt, subscopes)) subst in + let u = in_not false loc scopes (nsubst, substlist) [] iter in + subst_pat_iterator ldots_var t u) + (if revert then List.rev l else l) termin + with Not_found -> + anomaly (Pp.str "Inconsistent substitution of recursive notation.")) + | NHole _ -> + let () = assert (List.is_empty args) in + DAst.make ?loc @@ RCPatAtom None + | t -> error_invalid_pattern_notation ?loc () + in in_pat true + +let rec intern_pat genv ntnvars aliases pat = + let intern_cstr_with_all_args loc c with_letin idslpl1 pl2 = + let idslpl2 = List.map (intern_pat genv ntnvars empty_alias) pl2 in + let (ids',pll) = product_of_cases_patterns aliases (idslpl1@idslpl2) in + let pl' = List.map (fun (asubst,pl) -> + (asubst, DAst.make ?loc @@ PatCstr (c,chop_params_pattern loc (fst c) pl with_letin,alias_of aliases))) pll in + ids',pl' in + let loc = pat.loc in + match DAst.get pat with + | RCPatAlias (p, id) -> + let aliases' = merge_aliases aliases id in + intern_pat genv ntnvars aliases' p + | RCPatCstr (head, expl_pl, pl) -> + if get_asymmetric_patterns () then + let len = if List.is_empty expl_pl then Some (List.length pl) else None in + let c,idslpl1 = find_constructor loc len head in + let with_letin = + check_constructor_length genv loc c (List.length idslpl1 + List.length expl_pl) pl in + intern_cstr_with_all_args loc c with_letin idslpl1 (expl_pl@pl) + else + let c,idslpl1 = find_constructor loc None head in + let with_letin, pl2 = + add_implicits_check_constructor_length genv loc c (List.length idslpl1 + List.length expl_pl) pl in + intern_cstr_with_all_args loc c with_letin idslpl1 (expl_pl@pl2) + | RCPatAtom (Some ({loc;v=id},scopes)) -> + let aliases = merge_aliases aliases (make ?loc @@ Name id) in + set_var_scope ?loc id false scopes ntnvars; + (aliases.alias_ids,[aliases.alias_map, DAst.make ?loc @@ PatVar (alias_of aliases)]) (* TO CHECK: aura-t-on id? *) + | RCPatAtom (None) -> + let { alias_ids = ids; alias_map = asubst; } = aliases in + (ids, [asubst, DAst.make ?loc @@ PatVar (alias_of aliases)]) + | RCPatOr pl -> + assert (not (List.is_empty pl)); + let pl' = List.map (intern_pat genv ntnvars aliases) pl in + let (idsl,pl') = List.split pl' in + let ids = List.hd idsl in + check_or_pat_variables loc ids (List.tl idsl); + (ids,List.flatten pl') + +let intern_cases_pattern genv ntnvars scopes aliases pat = + intern_pat genv ntnvars aliases + (drop_notations_pattern (function ConstructRef _ -> () | _ -> raise Not_found) genv scopes pat) + +let _ = + intern_cases_pattern_fwd := + fun ntnvars scopes p -> intern_cases_pattern (Global.env ()) ntnvars scopes empty_alias p + +let intern_ind_pattern genv ntnvars scopes pat = + let no_not = + try + drop_notations_pattern (function (IndRef _ | ConstructRef _) -> () | _ -> raise Not_found) genv scopes pat + with InternalizationError(loc,NotAConstructor _) -> error_bad_inductive_type ?loc + in + let loc = no_not.CAst.loc in + match DAst.get no_not with + | RCPatCstr (head, expl_pl, pl) -> + let c = (function IndRef ind -> ind | _ -> error_bad_inductive_type ?loc) head in + let with_letin, pl2 = add_implicits_check_ind_length genv loc c + (List.length expl_pl) pl in + let idslpl = List.map (intern_pat genv ntnvars empty_alias) (expl_pl@pl2) in + (with_letin, + match product_of_cases_patterns empty_alias idslpl with + | _,[_,pl] -> (c,chop_params_pattern loc c pl with_letin) + | _ -> error_bad_inductive_type ?loc) + | x -> error_bad_inductive_type ?loc + +(**********************************************************************) +(* Utilities for application *) + +let merge_impargs l args = + let test x = function + | (_, Some {v=y}) -> explicitation_eq x y + | _ -> false + in + List.fold_right (fun a l -> + match a with + | (_, Some {v=ExplByName id as x}) when + List.exists (test x) args -> l + | _ -> a::l) + l args + +let get_implicit_name n imps = + Some (Impargs.name_of_implicit (List.nth imps (n-1))) + +let set_hole_implicit i b c = + let loc = c.CAst.loc in + match DAst.get c with + | GRef (r, _) -> Loc.tag ?loc (Evar_kinds.ImplicitArg (r,i,b),IntroAnonymous,None) + | GApp (r, _) -> + let loc = r.CAst.loc in + begin match DAst.get r with + | GRef (r, _) -> + Loc.tag ?loc (Evar_kinds.ImplicitArg (r,i,b),IntroAnonymous,None) + | _ -> anomaly (Pp.str "Only refs have implicits.") + end + | GVar id -> Loc.tag ?loc (Evar_kinds.ImplicitArg (VarRef id,i,b),IntroAnonymous,None) + | _ -> anomaly (Pp.str "Only refs have implicits.") + +let exists_implicit_name id = + List.exists (fun imp -> is_status_implicit imp && Id.equal id (name_of_implicit imp)) + +let extract_explicit_arg imps args = + let rec aux = function + | [] -> Id.Map.empty, [] + | (a,e)::l -> + let (eargs,rargs) = aux l in + match e with + | None -> (eargs,a::rargs) + | Some {loc;v=pos} -> + let id = match pos with + | ExplByName id -> + if not (exists_implicit_name id imps) then + user_err ?loc + (str "Wrong argument name: " ++ Id.print id ++ str "."); + if Id.Map.mem id eargs then + user_err ?loc (str "Argument name " ++ Id.print id + ++ str " occurs more than once."); + id + | ExplByPos (p,_id) -> + let id = + try + let imp = List.nth imps (p-1) in + if not (is_status_implicit imp) then failwith "imp"; + name_of_implicit imp + with Failure _ (* "nth" | "imp" *) -> + user_err ?loc + (str"Wrong argument position: " ++ int p ++ str ".") + in + if Id.Map.mem id eargs then + user_err ?loc (str"Argument at position " ++ int p ++ + str " is mentioned more than once."); + id in + (Id.Map.add id (loc, a) eargs, rargs) + in aux args + +(**********************************************************************) +(* Main loop *) + +let internalize globalenv env pattern_mode (_, ntnvars as lvar) c = + let rec intern env = CAst.with_loc_val (fun ?loc -> function + | CRef (ref,us) -> + let (c,imp,subscopes,l),_ = + intern_applied_reference intern env (Environ.named_context globalenv) + lvar us [] ref + in + apply_impargs c env imp subscopes l loc + + | CFix ({ CAst.loc = locid; v = iddef}, dl) -> + let lf = List.map (fun ({CAst.v = id},_,_,_,_) -> id) dl in + let dl = Array.of_list dl in + let n = + try List.index0 Id.equal iddef lf + with Not_found -> + raise (InternalizationError (locid,UnboundFixName (false,iddef))) + in + let idl_temp = Array.map + (fun (id,(n,order),bl,ty,_) -> + let intern_ro_arg f = + let before, after = split_at_annot bl n in + let (env',rbefore) = List.fold_left intern_local_binder (env,[]) before in + let ro = f (intern env') in + let n' = Option.map (fun _ -> List.count (fun c -> match DAst.get c with + | GLocalAssum _ -> true + | _ -> false (* remove let-ins *)) + rbefore) n in + n', ro, List.fold_left intern_local_binder (env',rbefore) after + in + let n, ro, (env',rbl) = + match order with + | CStructRec -> + intern_ro_arg (fun _ -> GStructRec) + | CWfRec c -> + intern_ro_arg (fun f -> GWfRec (f c)) + | CMeasureRec (m,r) -> + intern_ro_arg (fun f -> GMeasureRec (f m, Option.map f r)) + in + let bl = List.rev (List.map glob_local_binder_of_extended rbl) in + ((n, ro), bl, intern_type env' ty, env')) dl in + let idl = Array.map2 (fun (_,_,_,_,bd) (a,b,c,env') -> + let env'' = List.fold_left_i (fun i en name -> + let (_,bli,tyi,_) = idl_temp.(i) in + let fix_args = (List.map (fun (na, bk, _, _) -> (build_impls bk na)) bli) in + push_name_env ntnvars (impls_type_list ~args:fix_args tyi) + en (CAst.make @@ Name name)) 0 env' lf in + (a,b,c,intern {env'' with tmp_scope = None} bd)) dl idl_temp in + DAst.make ?loc @@ + GRec (GFix + (Array.map (fun (ro,_,_,_) -> ro) idl,n), + Array.of_list lf, + Array.map (fun (_,bl,_,_) -> bl) idl, + Array.map (fun (_,_,ty,_) -> ty) idl, + Array.map (fun (_,_,_,bd) -> bd) idl) + | CCoFix ({ CAst.loc = locid; v = iddef }, dl) -> + let lf = List.map (fun ({CAst.v = id},_,_,_) -> id) dl in + let dl = Array.of_list dl in + let n = + try List.index0 Id.equal iddef lf + with Not_found -> + raise (InternalizationError (locid,UnboundFixName (true,iddef))) + in + let idl_tmp = Array.map + (fun ({ CAst.loc; v = id },bl,ty,_) -> + let (env',rbl) = List.fold_left intern_local_binder (env,[]) bl in + (List.rev (List.map glob_local_binder_of_extended rbl), + intern_type env' ty,env')) dl in + let idl = Array.map2 (fun (_,_,_,bd) (b,c,env') -> + let env'' = List.fold_left_i (fun i en name -> + let (bli,tyi,_) = idl_tmp.(i) in + let cofix_args = List.map (fun (na, bk, _, _) -> (build_impls bk na)) bli in + push_name_env ntnvars (impls_type_list ~args:cofix_args tyi) + en (CAst.make @@ Name name)) 0 env' lf in + (b,c,intern {env'' with tmp_scope = None} bd)) dl idl_tmp in + DAst.make ?loc @@ + GRec (GCoFix n, + Array.of_list lf, + Array.map (fun (bl,_,_) -> bl) idl, + Array.map (fun (_,ty,_) -> ty) idl, + Array.map (fun (_,_,bd) -> bd) idl) + | CProdN ([],c2) -> anomaly (Pp.str "The AST is malformed, found prod without binders.") + | CProdN (bl,c2) -> + let (env',bl) = List.fold_left intern_local_binder (env,[]) bl in + expand_binders ?loc mkGProd bl (intern_type env' c2) + | CLambdaN ([],c2) -> anomaly (Pp.str "The AST is malformed, found lambda without binders.") + | CLambdaN (bl,c2) -> + let (env',bl) = List.fold_left intern_local_binder (reset_tmp_scope env,[]) bl in + expand_binders ?loc mkGLambda bl (intern env' c2) + | CLetIn (na,c1,t,c2) -> + let inc1 = intern (reset_tmp_scope env) c1 in + let int = Option.map (intern_type env) t in + DAst.make ?loc @@ + GLetIn (na.CAst.v, inc1, int, + intern (push_name_env ntnvars (impls_term_list inc1) env na) c2) + | CNotation ((InConstrEntrySomeLevel,"- _"), ([a],[],[],[])) when is_non_zero a -> + let p = match a.CAst.v with CPrim (Numeral (p, _)) -> p | _ -> assert false in + intern env (CAst.make ?loc @@ CPrim (Numeral (p,false))) + | CNotation ((InConstrEntrySomeLevel,"( _ )"),([a],[],[],[])) -> intern env a + | CNotation (ntn,args) -> + intern_notation intern env ntnvars loc ntn args + | CGeneralization (b,a,c) -> + intern_generalization intern env ntnvars loc b a c + | CPrim p -> + fst (Notation.interp_prim_token ?loc p (env.tmp_scope,env.scopes)) + | CDelimiters (key, e) -> + intern {env with tmp_scope = None; + scopes = find_delimiters_scope ?loc key :: env.scopes} e + | CAppExpl ((isproj,ref,us), args) -> + let (f,_,args_scopes,_),args = + let args = List.map (fun a -> (a,None)) args in + intern_applied_reference intern env (Environ.named_context globalenv) + lvar us args ref + in + (* Rem: GApp(_,f,[]) stands for @f *) + if args = [] then DAst.make ?loc @@ GApp (f,[]) else + smart_gapp f loc (intern_args env args_scopes (List.map fst args)) + + | CApp ((isproj,f), args) -> + let f,args = match f.CAst.v with + (* Compact notations like "t.(f args') args" *) + | CApp ((Some _,f), args') when not (Option.has_some isproj) -> + f,args'@args + (* Don't compact "(f args') args" to resolve implicits separately *) + | _ -> f,args in + let (c,impargs,args_scopes,l),args = + match f.CAst.v with + | CRef (ref,us) -> + intern_applied_reference intern env + (Environ.named_context globalenv) lvar us args ref + | CNotation (ntn,([],[],[],[])) -> + let c = intern_notation intern env ntnvars loc ntn ([],[],[],[]) in + let x, impl, scopes, l = find_appl_head_data c in + (x,impl,scopes,l), args + | _ -> (intern env f,[],[],[]), args in + apply_impargs c env impargs args_scopes + (merge_impargs l args) loc + + | CRecord fs -> + let st = Evar_kinds.Define (not (Program.get_proofs_transparency ())) in + let fields = + sort_fields ~complete:true loc fs + (fun _idx fieldname constructorname -> + let open Evar_kinds in + let fieldinfo : Evar_kinds.record_field = + {fieldname=fieldname; recordname=inductive_of_constructor constructorname} + in + CAst.make ?loc @@ CHole (Some + (Evar_kinds.QuestionMark { Evar_kinds.default_question_mark with + Evar_kinds.qm_obligation=st; + Evar_kinds.qm_record_field=Some fieldinfo + }) , IntroAnonymous, None)) + in + begin + match fields with + | None -> user_err ?loc ~hdr:"intern" (str"No constructor inference.") + | Some (n, constrname, args) -> + let pars = List.make n (CAst.make ?loc @@ CHole (None, IntroAnonymous, None)) in + let app = CAst.make ?loc @@ CAppExpl ((None, constrname,None), List.rev_append pars args) in + intern env app + end + | CCases (sty, rtnpo, tms, eqns) -> + let as_in_vars = List.fold_left (fun acc (_,na,inb) -> + Option.fold_left (fun acc tt -> Id.Set.union (ids_of_cases_indtype tt) acc) + (Option.fold_left (fun acc { CAst.v = y } -> Name.fold_right Id.Set.add y acc) acc na) + inb) Id.Set.empty tms in + (* as, in & return vars *) + let forbidden_vars = Option.cata free_vars_of_constr_expr as_in_vars rtnpo in + let tms,ex_ids,match_from_in = List.fold_right + (fun citm (inds,ex_ids,matchs) -> + let ((tm,ind),extra_id,match_td) = intern_case_item env forbidden_vars citm in + (tm,ind)::inds, Option.fold_right Id.Set.add extra_id ex_ids, List.rev_append match_td matchs) + tms ([],Id.Set.empty,[]) in + let env' = Id.Set.fold + (fun var bli -> push_name_env ntnvars (Variable,[],[],[]) bli (CAst.make @@ Name var)) + (Id.Set.union ex_ids as_in_vars) (reset_hidden_inductive_implicit_test env) in + (* PatVars before a real pattern do not need to be matched *) + let stripped_match_from_in = + let is_patvar c = match DAst.get c with + | PatVar _ -> true + | _ -> false + in + let rec aux = function + | [] -> [] + | (_, c) :: q when is_patvar c -> aux q + | l -> l + in aux match_from_in in + let rtnpo = match stripped_match_from_in with + | [] -> Option.map (intern_type env') rtnpo (* Only PatVar in "in" clauses *) + | l -> + (* Build a return predicate by expansion of the patterns of the "in" clause *) + let thevars, thepats = List.split l in + let sub_rtn = (* Some (GSort (Loc.ghost,GType None)) *) None in + let sub_tms = List.map (fun id -> (DAst.make @@ GVar id),(Name id,None)) thevars (* "match v1,..,vn" *) in + let main_sub_eqn = CAst.make @@ + ([],thepats, (* "|p1,..,pn" *) + Option.cata (intern_type env') + (DAst.make ?loc @@ GHole(Evar_kinds.CasesType false,IntroAnonymous,None)) + rtnpo) (* "=> P" if there were a return predicate P, and "=> _" otherwise *) in + let catch_all_sub_eqn = + if List.for_all (irrefutable globalenv) thepats then [] else + [CAst.make @@ ([],List.make (List.length thepats) (DAst.make @@ PatVar Anonymous), (* "|_,..,_" *) + DAst.make @@ GHole(Evar_kinds.ImpossibleCase,IntroAnonymous,None))] (* "=> _" *) in + Some (DAst.make @@ GCases(RegularStyle,sub_rtn,sub_tms,main_sub_eqn::catch_all_sub_eqn)) + in + let eqns' = List.map (intern_eqn (List.length tms) env) eqns in + DAst.make ?loc @@ + GCases (sty, rtnpo, tms, List.flatten eqns') + | CLetTuple (nal, (na,po), b, c) -> + let env' = reset_tmp_scope env in + (* "in" is None so no match to add *) + let ((b',(na',_)),_,_) = intern_case_item env' Id.Set.empty (b,na,None) in + let p' = Option.map (fun u -> + let env'' = push_name_env ntnvars (Variable,[],[],[]) (reset_hidden_inductive_implicit_test env') + (CAst.make na') in + intern_type env'' u) po in + DAst.make ?loc @@ + GLetTuple (List.map (fun { CAst.v } -> v) nal, (na', p'), b', + intern (List.fold_left (push_name_env ntnvars (Variable,[],[],[])) (reset_hidden_inductive_implicit_test env) nal) c) + | CIf (c, (na,po), b1, b2) -> + let env' = reset_tmp_scope env in + let ((c',(na',_)),_,_) = intern_case_item env' Id.Set.empty (c,na,None) in (* no "in" no match to ad too *) + let p' = Option.map (fun p -> + let env'' = push_name_env ntnvars (Variable,[],[],[]) (reset_hidden_inductive_implicit_test env) + (CAst.make na') in + intern_type env'' p) po in + DAst.make ?loc @@ + GIf (c', (na', p'), intern env b1, intern env b2) + | CHole (k, naming, solve) -> + let k = match k with + | None -> + let st = Evar_kinds.Define (not (Program.get_proofs_transparency ())) in + (match naming with + | IntroIdentifier id -> Evar_kinds.NamedHole id + | _ -> Evar_kinds.QuestionMark { Evar_kinds.default_question_mark with Evar_kinds.qm_obligation=st; }) + | Some k -> k + in + let solve = match solve with + | None -> None + | Some gen -> + let (ltacvars, ntnvars) = lvar in + (* Preventively declare notation variables in ltac as non-bindings *) + Id.Map.iter (fun x (used_as_binder,_,_) -> used_as_binder := false) ntnvars; + let extra = ltacvars.ltac_extra in + (* We inform ltac that the interning vars and the notation vars are bound *) + (* but we could instead rely on the "intern_sign" *) + let lvars = Id.Set.union ltacvars.ltac_bound ltacvars.ltac_vars in + let lvars = Id.Set.union lvars (Id.Map.domain ntnvars) in + let ltacvars = Id.Set.union lvars env.ids in + (* Propagating enough information for mutual interning with tac-in-term *) + let intern_sign = { + Genintern.intern_ids = env.ids; + Genintern.notation_variable_status = ntnvars + } in + let ist = { + Genintern.genv = globalenv; + ltacvars; + extra; + intern_sign; + } in + let (_, glb) = Genintern.generic_intern ist gen in + Some glb + in + DAst.make ?loc @@ + GHole (k, naming, solve) + (* Parsing pattern variables *) + | CPatVar n when pattern_mode -> + DAst.make ?loc @@ + GPatVar (Evar_kinds.SecondOrderPatVar n) + | CEvar (n, []) when pattern_mode -> + DAst.make ?loc @@ + GPatVar (Evar_kinds.FirstOrderPatVar n) + (* end *) + (* Parsing existential variables *) + | CEvar (n, l) -> + DAst.make ?loc @@ + GEvar (n, List.map (on_snd (intern env)) l) + | CPatVar _ -> + raise (InternalizationError (loc,IllegalMetavariable)) + (* end *) + | CSort s -> + DAst.make ?loc @@ + GSort s + | CCast (c1, c2) -> + DAst.make ?loc @@ + GCast (intern env c1, map_cast_type (intern_type env) c2) + ) + and intern_type env = intern (set_type_scope env) + + and intern_local_binder env bind : intern_env * Glob_term.extended_glob_local_binder list = + intern_local_binder_aux intern ntnvars env bind + + (* Expands a multiple pattern into a disjunction of multiple patterns *) + and intern_multiple_pattern env n pl = + let idsl_pll = List.map (intern_cases_pattern globalenv ntnvars (None,env.scopes) empty_alias) pl in + let loc = loc_of_multiple_pattern pl in + check_number_of_pattern loc n pl; + product_of_cases_patterns empty_alias idsl_pll + + (* Expands a disjunction of multiple pattern *) + and intern_disjunctive_multiple_pattern env loc n mpl = + assert (not (List.is_empty mpl)); + let mpl' = List.map (intern_multiple_pattern env n) mpl in + let (idsl,mpl') = List.split mpl' in + let ids = List.hd idsl in + check_or_pat_variables loc ids (List.tl idsl); + (ids,List.flatten mpl') + + (* Expands a pattern-matching clause [lhs => rhs] *) + and intern_eqn n env {loc;v=(lhs,rhs)} = + let eqn_ids,pll = intern_disjunctive_multiple_pattern env loc n lhs in + (* Linearity implies the order in ids is irrelevant *) + let eqn_ids = List.map (fun x -> x.v) eqn_ids in + check_linearity lhs eqn_ids; + let env_ids = List.fold_right Id.Set.add eqn_ids env.ids in + List.map (fun (asubst,pl) -> + let rhs = replace_vars_constr_expr asubst rhs in + let rhs' = intern {env with ids = env_ids} rhs in + CAst.make ?loc (eqn_ids,pl,rhs')) pll + + and intern_case_item env forbidden_names_for_gen (tm,na,t) = + (* the "match" part *) + let tm' = intern env tm in + (* the "as" part *) + let extra_id,na = + let loc = tm'.CAst.loc in + match DAst.get tm', na with + | GVar id, None when not (Id.Map.mem id (snd lvar)) -> Some id, CAst.make ?loc @@ Name id + | GRef (VarRef id, _), None -> Some id, CAst.make ?loc @@ Name id + | _, None -> None, CAst.make Anonymous + | _, Some ({ CAst.loc; v = na } as lna) -> None, lna in + (* the "in" part *) + let match_td,typ = match t with + | Some t -> + let with_letin,(ind,l) = intern_ind_pattern globalenv ntnvars (None,env.scopes) t in + let (mib,mip) = Inductive.lookup_mind_specif globalenv ind in + let nparams = (List.length (mib.Declarations.mind_params_ctxt)) in + (* for "in Vect n", we answer (["n","n"],[(loc,"n")]) + + for "in Vect (S n)", we answer ((match over "m", relevant branch is "S + n"), abstract over "m") = ([("m","S n")],[(loc,"m")]) where "m" is + generated from the canonical name of the inductive and outside of + {forbidden_names_for_gen} *) + let (match_to_do,nal) = + let rec canonize_args case_rel_ctxt arg_pats forbidden_names match_acc var_acc = + let add_name l = function + | { CAst.v = Anonymous } -> l + | { CAst.loc; v = (Name y as x) } -> (y, DAst.make ?loc @@ PatVar x) :: l in + match case_rel_ctxt,arg_pats with + (* LetIn in the rel_context *) + | LocalDef _ :: t, l when not with_letin -> + canonize_args t l forbidden_names match_acc ((CAst.make Anonymous)::var_acc) + | [],[] -> + (add_name match_acc na, var_acc) + | (LocalAssum (cano_name,ty) | LocalDef (cano_name,_,ty)) :: t, c::tt -> + begin match DAst.get c with + | PatVar x -> + let loc = c.CAst.loc in + canonize_args t tt forbidden_names + (add_name match_acc CAst.(make ?loc x)) (CAst.make ?loc x::var_acc) + | _ -> + let fresh = + Namegen.next_name_away_with_default_using_types "iV" cano_name forbidden_names (EConstr.of_constr ty) in + canonize_args t tt (Id.Set.add fresh forbidden_names) + ((fresh,c)::match_acc) ((CAst.make ?loc:(cases_pattern_loc c) @@ Name fresh)::var_acc) + end + | _ -> assert false in + let _,args_rel = + List.chop nparams (List.rev mip.Declarations.mind_arity_ctxt) in + canonize_args args_rel l forbidden_names_for_gen [] [] in + match_to_do, Some (CAst.make ?loc:(cases_pattern_expr_loc t) (ind,List.rev_map (fun x -> x.v) nal)) + | None -> + [], None in + (tm',(na.CAst.v, typ)), extra_id, match_td + + and intern_impargs c env l subscopes args = + let eargs, rargs = extract_explicit_arg l args in + if !parsing_explicit then + if Id.Map.is_empty eargs then intern_args env subscopes rargs + else user_err Pp.(str "Arguments given by name or position not supported in explicit mode.") + else + let rec aux n impl subscopes eargs rargs = + let (enva,subscopes') = apply_scope_env env subscopes in + match (impl,rargs) with + | (imp::impl', rargs) when is_status_implicit imp -> + begin try + let id = name_of_implicit imp in + let (_,a) = Id.Map.find id eargs in + let eargs' = Id.Map.remove id eargs in + intern enva a :: aux (n+1) impl' subscopes' eargs' rargs + with Not_found -> + if List.is_empty rargs && Id.Map.is_empty eargs && not (maximal_insertion_of imp) then + (* Less regular arguments than expected: complete *) + (* with implicit arguments if maximal insertion is set *) + [] + else + (DAst.map_from_loc (fun ?loc (a,b,c) -> GHole(a,b,c)) + (set_hole_implicit (n,get_implicit_name n l) (force_inference_of imp) c) + ) :: aux (n+1) impl' subscopes' eargs rargs + end + | (imp::impl', a::rargs') -> + intern enva a :: aux (n+1) impl' subscopes' eargs rargs' + | (imp::impl', []) -> + if not (Id.Map.is_empty eargs) then + (let (id,(loc,_)) = Id.Map.choose eargs in + user_err ?loc (str "Not enough non implicit \ + arguments to accept the argument bound to " ++ + Id.print id ++ str".")); + [] + | ([], rargs) -> + assert (Id.Map.is_empty eargs); + intern_args env subscopes rargs + in aux 1 l subscopes eargs rargs + + and apply_impargs c env imp subscopes l loc = + let imp = select_impargs_size (List.length (List.filter (fun (_,x) -> x == None) l)) imp in + let l = intern_impargs c env imp subscopes l in + smart_gapp c loc l + + and smart_gapp f loc = function + | [] -> f + | l -> + let loc' = f.CAst.loc in + match DAst.get f with + | GApp (g, args) -> DAst.make ?loc:(Loc.merge_opt loc' loc) @@ GApp (g, args@l) + | _ -> DAst.make ?loc:(Loc.merge_opt (loc_of_glob_constr f) loc) @@ GApp (f, l) + + and intern_args env subscopes = function + | [] -> [] + | a::args -> + let (enva,subscopes) = apply_scope_env env subscopes in + (intern enva a) :: (intern_args env subscopes args) + + in + try + intern env c + with + InternalizationError (loc,e) -> + user_err ?loc ~hdr:"internalize" + (explain_internalization_error e) + +(**************************************************************************) +(* Functions to translate constr_expr into glob_constr *) +(**************************************************************************) + +let extract_ids env = + List.fold_right Id.Set.add + (Termops.ids_of_rel_context (Environ.rel_context env)) + Id.Set.empty + +let scope_of_type_kind sigma = function + | IsType -> Notation.current_type_scope_name () + | OfType typ -> compute_type_scope sigma typ + | WithoutTypeConstraint -> None + +let empty_ltac_sign = { + ltac_vars = Id.Set.empty; + ltac_bound = Id.Set.empty; + ltac_extra = Genintern.Store.empty; +} + +let intern_gen kind env sigma + ?(impls=empty_internalization_env) ?(pattern_mode=false) ?(ltacvars=empty_ltac_sign) + c = + let tmp_scope = scope_of_type_kind sigma kind in + internalize env {ids = extract_ids env; unb = false; + tmp_scope = tmp_scope; scopes = []; + impls = impls} + pattern_mode (ltacvars, Id.Map.empty) c + +let intern_constr env sigma c = intern_gen WithoutTypeConstraint env sigma c +let intern_type env sigma c = intern_gen IsType env sigma c +let intern_pattern globalenv patt = + try + intern_cases_pattern globalenv Id.Map.empty (None,[]) empty_alias patt + with + InternalizationError (loc,e) -> + user_err ?loc ~hdr:"internalize" (explain_internalization_error e) + + +(*********************************************************************) +(* Functions to parse and interpret constructions *) + +(* All evars resolved *) + +let interp_gen kind env sigma ?(impls=empty_internalization_env) c = + let c = intern_gen kind ~impls env sigma c in + understand ~expected_type:kind env sigma c + +let interp_constr env sigma ?(impls=empty_internalization_env) c = + interp_gen WithoutTypeConstraint env sigma c + +let interp_type env sigma ?(impls=empty_internalization_env) c = + interp_gen IsType env sigma ~impls c + +let interp_casted_constr env sigma ?(impls=empty_internalization_env) c typ = + interp_gen (OfType typ) env sigma ~impls c + +(* Not all evars expected to be resolved *) + +let interp_open_constr env sigma c = + understand_tcc env sigma (intern_constr env sigma c) + +(* Not all evars expected to be resolved and computation of implicit args *) + +let interp_constr_evars_gen_impls env sigma + ?(impls=empty_internalization_env) expected_type c = + let c = intern_gen expected_type ~impls env sigma c in + let imps = Implicit_quantifiers.implicits_of_glob_constr ~with_products:(expected_type == IsType) c in + let sigma, c = understand_tcc env sigma ~expected_type c in + sigma, (c, imps) + +let interp_constr_evars_impls env sigma ?(impls=empty_internalization_env) c = + interp_constr_evars_gen_impls env sigma ~impls WithoutTypeConstraint c + +let interp_casted_constr_evars_impls env evdref ?(impls=empty_internalization_env) c typ = + interp_constr_evars_gen_impls env evdref ~impls (OfType typ) c + +let interp_type_evars_impls env sigma ?(impls=empty_internalization_env) c = + interp_constr_evars_gen_impls env sigma ~impls IsType c + +(* Not all evars expected to be resolved, with side-effect on evars *) + +let interp_constr_evars_gen env sigma ?(impls=empty_internalization_env) expected_type c = + let c = intern_gen expected_type ~impls env sigma c in + understand_tcc env sigma ~expected_type c + +let interp_constr_evars env evdref ?(impls=empty_internalization_env) c = + interp_constr_evars_gen env evdref WithoutTypeConstraint ~impls c + +let interp_casted_constr_evars env sigma ?(impls=empty_internalization_env) c typ = + interp_constr_evars_gen env sigma ~impls (OfType typ) c + +let interp_type_evars env sigma ?(impls=empty_internalization_env) c = + interp_constr_evars_gen env sigma IsType ~impls c + +(* Miscellaneous *) + +let intern_constr_pattern env sigma ?(as_type=false) ?(ltacvars=empty_ltac_sign) c = + let c = intern_gen (if as_type then IsType else WithoutTypeConstraint) + ~pattern_mode:true ~ltacvars env sigma c in + pattern_of_glob_constr c + +let intern_core kind env sigma ?(pattern_mode=false) ?(ltacvars=empty_ltac_sign) + { Genintern.intern_ids = ids; Genintern.notation_variable_status = vl } c = + let tmp_scope = scope_of_type_kind sigma kind in + let impls = empty_internalization_env in + internalize env {ids; unb = false; tmp_scope; scopes = []; impls} + pattern_mode (ltacvars, vl) c + +let interp_notation_constr env ?(impls=empty_internalization_env) nenv a = + let ids = extract_ids env in + (* [vl] is intended to remember the scope of the free variables of [a] *) + let vl = Id.Map.map (fun typ -> (ref false, ref None, typ)) nenv.ninterp_var_type in + let impls = Id.Map.fold (fun id _ impls -> Id.Map.remove id impls) nenv.ninterp_var_type impls in + let c = internalize env {ids; unb = false; tmp_scope = None; scopes = []; impls} + false (empty_ltac_sign, vl) a in + (* Splits variables into those that are binding, bound, or both *) + (* Translate and check that [c] has all its free variables bound in [vars] *) + let a, reversible = notation_constr_of_glob_constr nenv c in + (* binding and bound *) + let out_scope = function None -> None,[] | Some (a,l) -> a,l in + let unused = match reversible with NonInjective ids -> ids | _ -> [] in + let vars = Id.Map.mapi (fun id (used_as_binder, sc, typ) -> + (!used_as_binder && not (List.mem_f Id.equal id unused), out_scope !sc)) vl in + (* Returns [a] and the ordered list of variables with their scopes *) + vars, a, reversible + +(* Interpret binders and contexts *) + +let interp_binder env sigma na t = + let t = intern_gen IsType env sigma t in + let t' = locate_if_hole ?loc:(loc_of_glob_constr t) na t in + understand ~expected_type:IsType env sigma t' + +let interp_binder_evars env sigma na t = + let t = intern_gen IsType env sigma t in + let t' = locate_if_hole ?loc:(loc_of_glob_constr t) na t in + understand_tcc env sigma ~expected_type:IsType t' + +let my_intern_constr env lvar acc c = + internalize env acc false lvar c + +let intern_context global_level env impl_env binders = + try + let lvar = (empty_ltac_sign, Id.Map.empty) in + let lenv, bl = List.fold_left + (fun (lenv, bl) b -> + let (env, bl) = intern_local_binder_aux ~global_level (my_intern_constr env lvar) Id.Map.empty (lenv, bl) b in + (env, bl)) + ({ids = extract_ids env; unb = false; + tmp_scope = None; scopes = []; impls = impl_env}, []) binders in + (lenv.impls, List.map glob_local_binder_of_extended bl) + with InternalizationError (loc,e) -> + user_err ?loc ~hdr:"internalize" (explain_internalization_error e) + +let interp_glob_context_evars env sigma k bl = + let open EConstr in + let env, sigma, par, _, impls = + List.fold_left + (fun (env,sigma,params,n,impls) (na, k, b, t) -> + let t' = + if Option.is_empty b then locate_if_hole ?loc:(loc_of_glob_constr t) na t + else t + in + let sigma, t = understand_tcc env sigma ~expected_type:IsType t' in + match b with + None -> + let d = LocalAssum (na,t) in + let impls = + if k == Implicit then + let na = match na with Name n -> Some n | Anonymous -> None in + (ExplByPos (n, na), (true, true, true)) :: impls + else impls + in + (push_rel d env, sigma, d::params, succ n, impls) + | Some b -> + let sigma, c = understand_tcc env sigma ~expected_type:(OfType t) b in + let d = LocalDef (na, c, t) in + (push_rel d env, sigma, d::params, n, impls)) + (env,sigma,[],k+1,[]) (List.rev bl) + in sigma, ((env, par), impls) + +let interp_context_evars ?(global_level=false) ?(impl_env=empty_internalization_env) ?(shift=0) env sigma params = + let int_env,bl = intern_context global_level env impl_env params in + let sigma, x = interp_glob_context_evars env sigma shift bl in + sigma, (int_env, x) |
