(************************************************************************) (* * The Rocq Prover / The Rocq Development Team *) (* v * Copyright INRIA, CNRS and contributors *) (* <O___,, * (see version control and CREDITS file for authors & dates) *) (* \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 Util open CAst open Names open Nameops open Glob_term open Evar_kinds
(* Untyped intermediate terms, after ASTs and before constr. *)
let cases_pattern_loc c = c.CAst.loc
let alias_of_pat pat = DAst.with_val (function
| PatVar name -> name
| PatCstr(_,_,name) -> name
) pat
let set_pat_alias id = DAst.map (function
| PatVar Anonymous -> PatVar (Name id)
| PatCstr (cstr,patl,Anonymous) -> PatCstr (cstr,patl,Name id)
| pat -> assert false)
let tomatch_tuple_eq f (c1, p1) (c2, p2) = let eqp {CAst.v=(i1, na1)} {CAst.v=(i2, na2)} =
Ind.CanOrd.equal i1 i2 && List.equal Name.equal na1 na2 in let eq_pred (n1, o1) (n2, o2) = Name.equal n1 n2 && Option.equal eqp o1 o2 in
f c1 c2 && eq_pred p1 p2
and cases_clause_eq f g {CAst.v=(id1, p1, c1)} {CAst.v=(id2, p2, c2)} = (* In principle, id1 and id2 canonically derive from p1 and p2 *) List.equal (mk_cases_pattern_eq g) p1 p2 && f c1 c2
let glob_decl_eq f (na1, r1, bk1, c1, t1) (na2, r2, bk2, c2, t2) =
Name.equal na1 na2 && relevance_info_eq r1 r2 && binding_kind_eq bk1 bk2 && Option.equal f c1 c2 && f t1 t2
let rec glob_constr_eq c = mk_glob_constr_eq glob_constr_eq (fun na1 na2 _ _ -> Name.equal na1 na2) c
let cases_pattern_eq c = mk_cases_pattern_eq Name.equal c
let rec map_case_pattern_binders f = DAst.map (function
| PatVar na as x -> let r = f na in if r == na then x else PatVar r
| PatCstr (c,ps,na) as x -> let rna = f na in let rps =
CList.Smart.map (fun p -> map_case_pattern_binders f p) ps in if rna == na && rps == ps then x else PatCstr(c,rps,rna)
)
let map_glob_constr_left_to_right_with_names f g = DAst.map (function
| GApp (g,args) -> let comp1 = f g in let comp2 = Util.List.map_left f args in
GApp (comp1,comp2)
| GLambda (na,r,bk,ty,c) -> let comp1 = f ty in let comp2 = f c in
GLambda (g na,r,bk,comp1,comp2)
| GProd (na,r,bk,ty,c) -> let comp1 = f ty in let comp2 = f c in
GProd (g na,r,bk,comp1,comp2)
| GLetIn (na,r,b,t,c) -> let comp1 = f b in let compt = Option.map f t in let comp2 = f c in
GLetIn (g na,r,comp1,compt,comp2)
| GCases (sty,rtntypopt,tml,pl) -> let comp1 = Option.map f rtntypopt in let comp2 = Util.List.map_left (fun (tm,(x,indxl)) -> (f tm,(g x,Option.map (CAst.map (fun (ind,xl) -> (ind,List.map g xl))) indxl))) tml in let comp3 = Util.List.map_left (CAst.map (fun (idl,p,c) -> (List.map (fun id -> Name.get_id (g (Name id))) idl,List.map (map_case_pattern_binders g) p,f c))) pl in
GCases (sty,comp1,comp2,comp3)
| GLetTuple (nal,(na,po),b,c) -> let comp1 = Option.map f po in let comp2 = f b in let comp3 = f c in
GLetTuple (List.map g nal,(g na,comp1),comp2,comp3)
| GIf (c,(na,po),b1,b2) -> let comp1 = Option.map f po in let comp2 = f b1 in let comp3 = f b2 in
GIf (f c,(g na,comp1),comp2,comp3)
| GRec (fk,idl,bl,tyl,bv) -> let comp1 = Array.map (Util.List.map_left (map_glob_decl_left_to_right f)) bl in let comp2 = Array.map f tyl in let comp3 = Array.map f bv in let g id = Name.get_id (g (Name id)) in
GRec (fk,Array.map g idl,comp1,comp2,comp3)
| GCast (c,k,t) -> let c = f c in let t = f t in
GCast (c,k,t)
| GProj (p,args,c) -> let comp1 = Util.List.map_left f args in let comp2 = f c in
GProj (p,comp1,comp2)
| GArray (u,t,def,ty) -> let comp1 = Array.map_left f t in let comp2 = f def in let comp3 = f ty in
GArray (u,comp1,comp2,comp3)
| (GVar _ | GSort _ | GHole _ | GGenarg _ | GRef _ | GEvar _ | GPatVar _ | GInt _ | GFloat _ | GString _) as x -> x
)
let map_glob_constr_left_to_right f = map_glob_constr_left_to_right_with_names f (fun na -> na)
let map_glob_constr = map_glob_constr_left_to_right
let fold_return_type f acc (na,tyopt) = Option.fold_left f acc tyopt
let fold_glob_constr f acc = DAst.with_val (function
| GVar _ -> acc
| GApp (c,args) -> List.fold_left f (f acc c) args
| GLambda (_,_,_,b,c) | GProd (_,_,_,b,c) ->
f (f acc b) c
| GLetIn (_,_,b,t,c) ->
f (Option.fold_left f (f acc b) t) c
| GCases (_,rtntypopt,tml,pl) -> let fold_pattern acc {CAst.v=(idl,p,c)} = f acc c in List.fold_left fold_pattern
(List.fold_left f (Option.fold_left f acc rtntypopt) (List.map fst tml))
pl
| GLetTuple (_,rtntyp,b,c) ->
f (f (fold_return_type f acc rtntyp) b) c
| GIf (c,rtntyp,b1,b2) ->
f (f (f (fold_return_type f acc rtntyp) c) b1) b2
| GRec (_,_,bl,tyl,bv) -> let acc = Array.fold_left
(List.fold_left (fun acc (_,_,_,bbd,bty) ->
f (Option.fold_left f acc bbd) bty)) acc bl in
Array.fold_left f (Array.fold_left f acc tyl) bv
| GCast (c,k,t) -> let acc = f acc t in
f acc c
| GProj (p,args,c) ->
f (List.fold_left f acc args) c
| GArray (_u,t,def,ty) -> f (f (Array.fold_left f acc t) def) ty
| (GSort _ | GHole _ | GGenarg _ | GRef _ | GEvar _ | GPatVar _ | GInt _ | GFloat _ | GString _) -> acc
) let fold_return_type_with_binders f g v acc (na,tyopt) = (* eta expansion is important if g has effects, eg bound_glob_vars below, see #11959 *) Option.fold_left (fun acc -> f (Name.fold_right g na v) acc) acc tyopt
let fold_glob_constr_with_binders g f v acc = DAst.(with_val (function
| GVar _ -> acc
| GApp (c,args) -> List.fold_left (f v) (f v acc c) args
| GLambda (na,_,_,b,c) | GProd (na,_,_,b,c) ->
f (Name.fold_right g na v) (f v acc b) c
| GLetIn (na,_,b,t,c) ->
f (Name.fold_right g na v) (Option.fold_left (f v) (f v acc b) t) c
| GCases (_,rtntypopt,tml,pl) -> let fold_pattern acc {v=(idl,p,c)} = f (List.fold_right g idl v) acc c in let fold_tomatch (v',acc) (tm,(na,onal)) =
((if rtntypopt = None then v' else Option.fold_left (fun v'' {v=(_,nal)} -> List.fold_right (Name.fold_right g) nal v'')
(Name.fold_right g na v') onal),
f v acc tm) in let (v',acc) = List.fold_left fold_tomatch (v,acc) tml in let acc = Option.fold_left (f v') acc rtntypopt in List.fold_left fold_pattern acc pl
| GLetTuple (nal,rtntyp,b,c) ->
f (List.fold_right (Name.fold_right g) nal v)
(f v (fold_return_type_with_binders f g v acc rtntyp) b) c
| GIf (c,rtntyp,b1,b2) ->
f v (f v (f v (fold_return_type_with_binders f g v acc rtntyp) c) b1) b2
| GRec (_,idl,bll,tyl,bv) -> let v' = Array.fold_right g idl v in let f' i acc fid = let v,acc = List.fold_left
(fun (v,acc) (na,_,_,bbd,bty) ->
(Name.fold_right g na v, f v (Option.fold_left (f v) acc bbd) bty))
(v,acc)
bll.(i) in
f v' (f v acc tyl.(i)) (bv.(i)) in
Array.fold_left_i f' acc idl
| GCast (c,k,t) -> let acc = f v acc t in
f v acc c
| GProj (p,args,c) ->
f v (List.fold_left (f v) acc args) c
| GArray (_u, t, def, ty) -> f v (f v (Array.fold_left (f v) acc t) def) ty
| (GSort _ | GHole _ | GGenarg _ | GRef _ | GEvar _ | GPatVar _ | GInt _ | GFloat _ | GString _) -> acc))
let iter_glob_constr f = fold_glob_constr (fun () -> f) ()
let occur_glob_constr id = let rec occur barred acc c = match DAst.get c with
| GVar id' -> Id.equal id id'
| _ -> (* [g] looks if [id] appears in a binding position, in which
case, we don't have to look in the corresponding subterm *) let g id' barred = barred || Id.equal id id'in let f barred acc c = acc || not barred && occur false acc c in
fold_glob_constr_with_binders g f barred acc c in
occur falsefalse
let free_glob_vars = let rec vars bound vs c = match DAst.get c with
| GVar id' -> if Id.Set.mem id' bound then vs else Id.Set.add id' vs
| _ -> fold_glob_constr_with_binders Id.Set.add vars bound vs c in fun rt -> let vs = vars Id.Set.empty Id.Set.empty rt in
vs
let glob_visible_short_qualid c = let rec aux acc c = match DAst.get c with
| GRef (c,_) -> let qualid = Nametab.shortest_qualid_of_global Id.Set.empty c in let dir,id = Libnames.repr_qualid qualid in if DirPath.is_empty dir then Id.Set.add id acc else acc
| _ ->
fold_glob_constr aux acc c in aux Id.Set.empty c
let warn_variable_collision = letopen Pp in
CWarnings.create ~name:"variable-collision" ~category:CWarnings.CoreCategories.ltac
(fun name ->
strbrk "Collision between bound variables of name " ++ Id.print name)
let add_and_check_ident id set = if Id.Set.mem id setthen warn_variable_collision id;
Id.Set.add id set
let bound_glob_vars = let rec vars bound =
fold_glob_constr_with_binders
(fun id () -> bound := add_and_check_ident id !bound)
(fun () () -> vars bound)
() () in fun rt -> let bound = ref Id.Set.empty in
vars bound rt;
!bound
(** Mapping of names in binders *)
(* spiwack: I used a smart-style kind of mapping here, because the operationwillbetheidentityalmostallofthetime(withany termoutsideofLtactobeginwith).Buttobehonest,therewould probablybenosignificantpenaltyindoingreallocationas
pattern-matching expressions are usually rather small. *)
let map_inpattern_binders f ({loc;v=(id,nal)} as x) = let r = CList.Smart.map f nal in if r == nal then x else CAst.make ?loc (id,r)
let map_tomatch_binders f ((c,(na,inp)) as x) : tomatch_tuple = let r = Option.Smart.map (fun p -> map_inpattern_binders f p) inp in if r == inp then x else c,(f na, r)
let map_cases_branch_binders f ({CAst.loc;v=(il,cll,rhs)} as x) : cases_clause = (* spiwack: not sure if I must do something with the list of idents. Itisintendedtobeasupersetofthefreevariableofthe right-handside,ifIunderstandcorrectly.ButI'mnotsurewhen
or how they are used. *) let r = List.Smart.map (fun cl -> map_case_pattern_binders f cl) cll in if r == cll then x else CAst.make ?loc (il,r,rhs)
let map_pattern_binders f tomatch branches =
CList.Smart.map (fun tm -> map_tomatch_binders f tm) tomatch,
CList.Smart.map (fun br -> map_cases_branch_binders f br) branches
(** /mapping of names in binders *)
let map_tomatch f (c,pp) : tomatch_tuple = f c , pp
let map_cases_branch f =
CAst.map (fun (il,cll,rhs) -> (il , cll , f rhs))
let map_pattern f tomatch branches = List.map (fun tm -> map_tomatch f tm) tomatch, List.map (fun br -> map_cases_branch f br) branches
let collide_id l id = List.exists (fun (id',id'') -> Id.equal id id' || Id.equal id id'') l let test_id l id = if collide_id l id thenraise UnsoundRenaming let test_na l na = Name.iter (test_id l) na
let update_subst na l = let in_range id l = List.exists (fun (_,id') -> Id.equal id id') l in let l' = Name.fold_right Id.List.remove_assoc na l in
Name.fold_right
(fun id _ -> if in_range id l' then let id' = Namegen.next_ident_away_from id (fun id' -> in_range id' l') in
Name id', (id,id')::l else na,l)
na (na,l)
let rename_var l id = try let id' = Id.List.assoc id l in (* Check that no other earlier binding hides the one found *) let _,(id'',_) = List.extract_first (fun (_,id) -> Id.equal id id') l in if Id.equal id id''then id' else raise UnsoundRenaming with Not_found -> ifList.exists (fun (_,id') -> Id.equal id id') l thenraise UnsoundRenaming else id
let force c = DAst.make ?loc:c.CAst.loc (DAst.get c)
let rec rename_glob_vars l c = force @@ DAst.map_with_loc (fun ?loc -> function
| GVar id as r -> let id' = rename_var l id in if id == id' then r else GVar id'
| GRef (GlobRef.VarRef id,_) as r -> ifList.exists (fun (_,id') -> Id.equal id id') l thenraise UnsoundRenaming else r
| GProd (na,r,bk,t,c) -> let na',l' = update_subst na l in
GProd (na',r,bk,rename_glob_vars l t,rename_glob_vars l' c)
| GLambda (na,r,bk,t,c) -> let na',l' = update_subst na l in
GLambda (na',r,bk,rename_glob_vars l t,rename_glob_vars l' c)
| GLetIn (na,r,b,t,c) -> let na',l' = update_subst na l in
GLetIn (na',r,rename_glob_vars l b,Option.map (rename_glob_vars l) t,rename_glob_vars l' c) (* Lazy strategy: we fail if a collision with renaming occurs, rather than renaming further *)
| GCases (ci,po,tomatchl,cls) -> let test_pred_pat (na,ino) =
test_na l na; Option.iter (fun {v=(_,nal)} -> List.iter (test_na l) nal) ino in let test_clause idl = List.iter (test_id l) idl in let po = Option.map (rename_glob_vars l) po in let tomatchl = Util.List.map_left (fun (tm,x) -> test_pred_pat x; (rename_glob_vars l tm,x)) tomatchl in let cls = Util.List.map_left (CAst.map (fun (idl,p,c) -> test_clause idl; (idl,p,rename_glob_vars l c))) cls in
GCases (ci,po,tomatchl,cls)
| GLetTuple (nal,(na,po),c,b) -> List.iter (test_na l) (na::nal);
GLetTuple (nal,(na,Option.map (rename_glob_vars l) po),
rename_glob_vars l c,rename_glob_vars l b)
| GIf (c,(na,po),b1,b2) ->
test_na l na;
GIf (rename_glob_vars l c,(na,Option.map (rename_glob_vars l) po),
rename_glob_vars l b1,rename_glob_vars l b2)
| GRec (k,idl,decls,bs,ts) ->
Array.iter (test_id l) idl;
GRec (k,idl,
Array.map (List.map (fun (na,r,k,bbd,bty) ->
test_na l na;
(na,r,k,Option.map (rename_glob_vars l) bbd,rename_glob_vars l bty))) decls,
Array.map (rename_glob_vars l) bs,
Array.map (rename_glob_vars l) ts)
| _ -> DAst.get (map_glob_constr (rename_glob_vars l) c)
) c
(**********************************************************************) (* Conversion from glob_constr to cases pattern, if possible *)
let is_gvar id c = match DAst.get c with
| GVar id' -> Id.equal id id'
| _ -> false
let rec cases_pattern_of_glob_constr env na c = (* Forcing evaluation to ensure that the possible raising of
Not_found is not delayed *) let c = DAst.force c in
DAst.map (function
| GVar id -> beginmatch na with
| Name _ -> (* Unable to manage the presence of both an alias and a variable *) raise Not_found
| Anonymous -> PatVar (Name id) end
| GHole _ -> PatVar na
| GGenarg _ -> PatVar na (* XXX does this really make sense? *)
| GRef (GlobRef.VarRef id,_) -> PatVar (Name id)
| GRef (GlobRef.ConstructRef cstr,_) -> PatCstr (cstr,[],na)
| GApp (c, l) -> beginmatch DAst.get c with
| GRef (GlobRef.ConstructRef cstr,_) -> let nparams = Inductiveops.inductive_nparams env (fst cstr) in let _,l = List.chop nparams l in
PatCstr (cstr,List.map (cases_pattern_of_glob_constr env Anonymous) l,na)
| _ -> raise Not_found end
| GLetIn (Name id as na',_,b,None,e) when is_gvar id e && na = Anonymous -> (* A canonical encoding of aliases *)
DAst.get (cases_pattern_of_glob_constr env na' b)
| _ -> raise Not_found
) c
open Declarations open Context
(* Keep only patterns which are not bound to a local definitions *) let drop_local_defs params decls args = let decls = List.skipn (Rel.length params) (List.rev decls) in let rec aux decls args = match decls, args with
| [], [] -> []
| Rel.Declaration.LocalDef _ :: decls, pat :: args -> begin match DAst.get pat with
| PatVar Anonymous -> aux decls args
| _ -> raise Not_found (* The pattern is used, one cannot drop it *) end
| Rel.Declaration.LocalAssum _ :: decls, a :: args -> a :: aux decls args
| _ -> assert falsein
aux decls args
let add_patterns_for_params_remove_local_defs env (ind,j) l = let (mib,mip) = Inductive.lookup_mind_specif env ind in let nparams = mib.Declarations.mind_nparams in let l = if mip.mind_consnrealdecls.(j-1) = mip.mind_consnrealargs.(j-1) then (* Optimisation *) l else let (ctx, _) = mip.mind_nf_lc.(j - 1) in
drop_local_defs mib.mind_params_ctxt ctx l in
Util.List.addn nparams (DAst.make @@ PatVar Anonymous) l
let add_alias ?loc na c = match na with
| Anonymous -> c
| Name id -> GLetIn (na,None,DAst.make ?loc c,None,DAst.make ?loc (GVar id))
(* Turn a closed cases pattern into a glob_constr *) let rec glob_constr_of_cases_pattern_aux env isclosed x = DAst.map_with_loc (fun ?loc -> function
| PatCstr (cstr,[],na) -> add_alias ?loc na (GRef (GlobRef.ConstructRef cstr,None))
| PatCstr (cstr,l,na) -> letref = DAst.make ?loc @@ GRef (GlobRef.ConstructRef cstr,None) in let l = add_patterns_for_params_remove_local_defs env cstr l in
add_alias ?loc na (GApp (ref, List.map (glob_constr_of_cases_pattern_aux env isclosed) l))
| PatVar (Name id) when not isclosed ->
GVar id
| PatVar Anonymous when not isclosed ->
GHole (GQuestionMark {
Evar_kinds.default_question_mark with Evar_kinds.qm_obligation=Define false;
})
| _ -> raise Not_found
) x
let glob_constr_of_closed_cases_pattern env p = match DAst.get p with
| PatCstr (cstr,l,na) -> let loc = p.CAst.loc in
na,glob_constr_of_cases_pattern_aux env true (DAst.make ?loc @@ PatCstr (cstr,l,Anonymous))
| _ -> raise Not_found
let glob_constr_of_cases_pattern env p = glob_constr_of_cases_pattern_aux env false p
let kind_of_glob_kind = function
| GImplicitArg (gr, p, b) -> ImplicitArg (gr, p, b)
| GBinderType na -> BinderType na
| GNamedHole (fresh, id) -> NamedHole id
| GQuestionMark qm -> QuestionMark qm
| GCasesType -> CasesType false
| GInternalHole -> InternalHole
| GImpossibleCase -> ImpossibleCase
let naming_of_glob_kind = function
| GNamedHole (true, id) -> Namegen.IntroFresh id
| GNamedHole (false, id) -> Namegen.IntroIdentifier id
| _ -> Namegen.IntroAnonymous
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.