diff options
Diffstat (limited to 'kernel/inductive.ml')
-rw-r--r-- | kernel/inductive.ml | 966 |
1 files changed, 732 insertions, 234 deletions
diff --git a/kernel/inductive.ml b/kernel/inductive.ml index 6cd04f76f..06219f084 100644 --- a/kernel/inductive.ml +++ b/kernel/inductive.ml @@ -16,6 +16,44 @@ open Sign open Declarations open Environ open Reduction +open Type_errors + +exception Induc + +(* raise Induc if not an inductive type *) +let lookup_mind_specif env (sp,tyi) = + let mib = + try Environ.lookup_mind sp env + with Not_found -> raise Induc in + if tyi >= Array.length mib.mind_packets then + error "Inductive.lookup_mind_specif: invalid inductive index"; + (mib, mib.mind_packets.(tyi)) + +let lookup_recargs env ind = + let (mib,mip) = lookup_mind_specif env ind in + Array.map (fun mip -> mip.mind_listrec) mib.mind_packets + +let find_rectype env c = + let (t, l) = decompose_app (whd_betadeltaiota env c) in + match kind_of_term t with + | Ind ind -> (ind, l) + | _ -> raise Induc + +let find_inductive env c = + let (t, l) = decompose_app (whd_betadeltaiota env c) in + match kind_of_term t with + | Ind ind + when (fst (lookup_mind_specif env ind)).mind_finite -> (ind, l) + | _ -> raise Induc + +let find_coinductive env c = + let (t, l) = decompose_app (whd_betadeltaiota env c) in + match kind_of_term t with + | Ind ind + when not (fst (lookup_mind_specif env ind)).mind_finite -> (ind, l) + | _ -> raise Induc + +(***********************************************************************) type inductive_instance = { mis_sp : section_path; @@ -23,189 +61,95 @@ type inductive_instance = { mis_tyi : int; mis_mip : one_inductive_body } - -let build_mis (sp,tyi) mib = - { mis_sp = sp; mis_mib = mib; mis_tyi = tyi; - mis_mip = mind_nth_type_packet mib tyi } - -let mis_ntypes mis = mis.mis_mib.mind_ntypes -let mis_nparams mis = mis.mis_mip.mind_nparams - -let mis_index mis = mis.mis_tyi - let mis_nconstr mis = Array.length (mis.mis_mip.mind_consnames) -let mis_nrealargs mis = mis.mis_mip.mind_nrealargs -let mis_kelim mis = mis.mis_mip.mind_kelim -let mis_recargs mis = - Array.map (fun mip -> mip.mind_listrec) mis.mis_mib.mind_packets -let mis_recarg mis = mis.mis_mip.mind_listrec -let mis_typename mis = mis.mis_mip.mind_typename -let mis_typepath mis = - make_path (dirpath mis.mis_sp) mis.mis_mip.mind_typename CCI -let mis_consnames mis = mis.mis_mip.mind_consnames -let mis_conspaths mis = - let dir = dirpath mis.mis_sp in - Array.map (fun id -> make_path dir id CCI) mis.mis_mip.mind_consnames let mis_inductive mis = (mis.mis_sp,mis.mis_tyi) -let mis_finite mis = mis.mis_mip.mind_finite - -let mis_typed_nf_lc mis = - let sign = mis.mis_mib.mind_hyps in - mis.mis_mip.mind_nf_lc - -let mis_nf_lc mis = Array.map body_of_type (mis_typed_nf_lc mis) - -let mis_user_lc mis = - let sign = mis.mis_mib.mind_hyps in - (mind_user_lc mis.mis_mip) - -(* gives the vector of constructors and of - types of constructors of an inductive definition - correctly instanciated *) - -let mis_type_mconstructs mispec = - let specif = Array.map body_of_type (mis_user_lc mispec) - and ntypes = mis_ntypes mispec - and nconstr = mis_nconstr mispec in - let make_Ik k = mkMutInd (mispec.mis_sp,ntypes-k-1) - and make_Ck k = - mkMutConstruct ((mispec.mis_sp,mispec.mis_tyi),k+1) in - (Array.init nconstr make_Ck, - Array.map (substl (list_tabulate make_Ik ntypes)) specif) - -let mis_nf_constructor_type i mispec = - let specif = mis_nf_lc mispec - and ntypes = mis_ntypes mispec - and nconstr = mis_nconstr mispec in - let make_Ik k = mkMutInd (mispec.mis_sp,ntypes-k-1) in - if i > nconstr then error "Not enough constructors in the type"; - substl (list_tabulate make_Ik ntypes) specif.(i-1) - -let mis_constructor_type i mispec = - let specif = mis_user_lc mispec - and ntypes = mis_ntypes mispec - and nconstr = mis_nconstr mispec in - let make_Ik k = mkMutInd (mispec.mis_sp,ntypes-k-1) in - if i > nconstr then error "Not enough constructors in the type"; - substl (list_tabulate make_Ik ntypes) specif.(i-1) - -let mis_arity mis = - let hyps = mis.mis_mib.mind_hyps in - mind_user_arity mis.mis_mip - -let mis_nf_arity mis = - let hyps = mis.mis_mib.mind_hyps in - mis.mis_mip.mind_nf_arity -let mis_params_ctxt mis = mis.mis_mip.mind_params_ctxt -(* - let paramsign,_ = - decompose_prod_n_assum mis.mis_mip.mind_nparams - (body_of_type (mis_nf_arity mis)) - in paramsign -*) +let lookup_mind_instance (sp,tyi) env = + let (mib,mip) = lookup_mind_specif env (sp,tyi) in + { mis_sp = sp; mis_mib = mib; mis_tyi = tyi; mis_mip = mip } -let mis_sort mispec = mispec.mis_mip.mind_sort +(* Build the substitution that replaces Rels by the appropriate *) +(* inductives *) +let ind_subst mispec = + let ntypes = mispec.mis_mib.mind_ntypes in + let make_Ik k = mkInd (mispec.mis_sp,ntypes-k-1) in + (list_tabulate make_Ik ntypes) -(* [inductive_family] = [inductive_instance] applied to global parameters *) -type inductive_family = IndFamily of inductive_instance * constr list +(* Instantiate both section variables and inductives *) +let constructor_instantiate mispec = + let s = ind_subst mispec in + substl s -type inductive_type = IndType of inductive_family * constr list +(* Instantiate the parameters of the inductive type *) +let instantiate_params t args sign = + let rec inst s t = function + | ((_,None,_)::ctxt,a::args) -> + (match kind_of_term t with + | Prod(_,_,t) -> inst (a::s) t (ctxt,args) + | _ -> anomaly"instantiate_params: type, ctxt and args mismatch") + | ((_,(Some b),_)::ctxt,args) -> + (match kind_of_term t with + | LetIn(_,_,_,t) -> inst ((substl s b)::s) t (ctxt,args) + | _ -> anomaly"instantiate_params: type, ctxt and args mismatch") + | [], [] -> substl s t + | _ -> anomaly"instantiate_params: type, ctxt and args mismatch" + in inst [] t (List.rev sign,args) -let liftn_inductive_family n d (IndFamily (mis,params)) = - IndFamily (mis, List.map (liftn n d) params) -let lift_inductive_family n = liftn_inductive_family n 1 +let full_inductive_instantiate (mispec,params) t = + instantiate_params t params mispec.mis_mip.mind_params_ctxt -let liftn_inductive_type n d (IndType (indf, realargs)) = - IndType (liftn_inductive_family n d indf, List.map (liftn n d) realargs) -let lift_inductive_type n = liftn_inductive_type n 1 +let full_constructor_instantiate (mispec,params) = + let inst_ind = constructor_instantiate mispec in + (fun t -> + instantiate_params (inst_ind t) params mispec.mis_mip.mind_params_ctxt) -let substnl_ind_family l n (IndFamily (mis,params)) = - IndFamily (mis, List.map (substnl l n) params) +(***********************************************************************) +(***********************************************************************) -let substnl_ind_type l n (IndType (indf,realargs)) = - IndType (substnl_ind_family l n indf, List.map (substnl l n) realargs) +(* Functions to build standard types related to inductive *) -let make_ind_family (mis, params) = IndFamily (mis,params) -let dest_ind_family (IndFamily (mis,params)) = (mis,params) +(* Type of an inductive type *) -let make_ind_type (indf, realargs) = IndType (indf,realargs) -let dest_ind_type (IndType (indf,realargs)) = (indf,realargs) +let type_of_inductive env i = + let mis = lookup_mind_instance i env in + let hyps = mis.mis_mib.mind_hyps in + mis.mis_mip.mind_user_arity -let mkAppliedInd (IndType (IndFamily (mis,params), realargs)) = - applist (mkMutInd (mis_inductive mis),params@realargs) +(* The same, with parameters instantiated *) +let get_arity (mispec,params as indf) = + let arity = mispec.mis_mip.mind_nf_arity in + destArity (full_inductive_instantiate indf arity) -let mis_is_recursive_subset listind mis = - let rec one_is_rec rvec = - List.exists - (function - | Mrec i -> List.mem i listind - | Imbr(_,lvec) -> one_is_rec lvec - | Norec -> false - | Param _ -> false) rvec - in - array_exists one_is_rec (mis_recarg mis) +(***********************************************************************) +(* Type of a constructor *) + +let type_of_constructor env cstr = + let ind = inductive_of_constructor cstr in + let mispec = lookup_mind_instance ind env in + let specif = mispec.mis_mip.mind_user_lc in + let i = index_of_constructor cstr in + let nconstr = mis_nconstr mispec in + if i > nconstr then error "Not enough constructors in the type"; + constructor_instantiate mispec specif.(i-1) -let mis_is_recursive mis = - mis_is_recursive_subset (interval 0 ((mis_ntypes mis)-1)) mis +let arities_of_constructors env ind = + let mispec = lookup_mind_instance ind env in + let specif = mispec.mis_mip.mind_user_lc in + Array.map (constructor_instantiate mispec) specif -(* Annotation for cases *) -let make_case_info mis style pats_source = -(* let constr_lengths = Array.map List.length (mis_recarg mis) in*) - let indsp = (mis.mis_sp,mis.mis_tyi) in - let print_info = - (indsp,mis_consnames mis,mis.mis_mip.mind_nrealargs,style,pats_source) in - ((*constr_lengths*) mis_nparams mis,print_info) -let make_default_case_info mis = - make_case_info mis None (Array.init (mis_nconstr mis) (fun _ -> RegularPat)) +(* gives the vector of constructors and of + types of constructors of an inductive definition + correctly instanciated *) +let mis_nf_constructor_type i mispec = + let nconstr = mis_nconstr mispec in + if i > nconstr then error "Not enough constructors in the type"; + constructor_instantiate mispec mispec.mis_mip.mind_nf_lc.(i-1) + (*s Useful functions *) -let inductive_of_constructor (ind_sp,i) = ind_sp -let index_of_constructor (ind_sp,i) = i -let ith_constructor_of_inductive ind_sp i = (ind_sp,i) - -exception Induc - -let extract_mrectype t = - let (t, l) = whd_stack t in - match kind_of_term t with - | IsMutInd ind -> (ind, l) - | _ -> raise Induc - -let find_mrectype env sigma c = - let (t, l) = whd_betadeltaiota_stack env sigma c in - match kind_of_term t with - | IsMutInd ind -> (ind, l) - | _ -> raise Induc - -let find_inductive env sigma c = - let (t, l) = whd_betadeltaiota_stack env sigma c in - match kind_of_term t with - | IsMutInd ((sp,i) as ind) - when mind_type_finite (lookup_mind sp env) i -> (ind, l) - | _ -> raise Induc - -let find_coinductive env sigma c = - let (t, l) = whd_betadeltaiota_stack env sigma c in - match kind_of_term t with - | IsMutInd ((sp,i) as ind) - when not (mind_type_finite (lookup_mind sp env) i) -> (ind, l) - | _ -> raise Induc - -(* raise Induc if not an inductive type *) -let lookup_mind_specif ((sp,tyi) as ind) env = - build_mis ind (lookup_mind sp env) - -let find_rectype env sigma ty = - let (mind,largs) = find_mrectype env sigma ty in - let mispec = lookup_mind_specif mind env in - let nparams = mis_nparams mispec in - let (params,realargs) = list_chop nparams largs in - make_ind_type (make_ind_family (mispec,params),realargs) - type constructor_summary = { cs_cstr : constructor; cs_params : constr list; @@ -214,63 +158,24 @@ type constructor_summary = { cs_concl_realargs : constr array } -let lift_constructor n cs = { - cs_cstr = cs.cs_cstr; - cs_params = List.map (lift n) cs.cs_params; - cs_nargs = cs.cs_nargs; - cs_args = lift_rel_context n cs.cs_args; - cs_concl_realargs = Array.map (liftn n (cs.cs_nargs+1)) cs.cs_concl_realargs -} - -let instantiate_params t args sign = - let rec inst s t = function - | ((_,None,_)::ctxt,a::args) -> - (match kind_of_term t with - | IsProd(_,_,t) -> inst (a::s) t (ctxt,args) - | _ -> anomaly"instantiate_params: type, ctxt and args mismatch") - | ((_,(Some b),_)::ctxt,args) -> - (match kind_of_term t with - | IsLetIn(_,_,_,t) -> inst ((substl s b)::s) t (ctxt,args) - | _ -> anomaly"instantiate_params: type, ctxt and args mismatch") - | [], [] -> substl s t - | _ -> anomaly"instantiate_params: type, ctxt and args mismatch" - in inst [] t (List.rev sign,args) - -let get_constructor_type (IndFamily (mispec,params)) j = - assert (j <= mis_nconstr mispec); - let typi = mis_constructor_type j mispec in - instantiate_params typi params (mis_params_ctxt mispec) - -let get_constructors_types (IndFamily (mispec,params) as indf) = - Array.init (mis_nconstr mispec) (fun j -> get_constructor_type indf (j+1)) - -let get_constructor (IndFamily (mispec,params) as indf) j = - assert (j <= mis_nconstr mispec); - let typi = mis_nf_constructor_type j mispec in - let typi = instantiate_params typi params (mis_params_ctxt mispec) in +let process_constructor ((mispec,params) as indf) j typi = + let typi = full_constructor_instantiate indf typi in let (args,ccl) = decompose_prod_assum typi in - let (_,allargs) = whd_stack ccl in - let (_,vargs) = list_chop (mis_nparams mispec) allargs in - { cs_cstr = ith_constructor_of_inductive (mis_inductive mispec) j; + let (_,allargs) = decompose_app ccl in + let (_,vargs) = list_chop mispec.mis_mip.mind_nparams allargs in + { cs_cstr = ith_constructor_of_inductive (mis_inductive mispec) (j+1); cs_params = params; cs_nargs = rel_context_length args; cs_args = args; cs_concl_realargs = Array.of_list vargs } -let get_constructors (IndFamily (mispec,params) as indf) = - Array.init (mis_nconstr mispec) (fun j -> get_constructor indf (j+1)) - -let get_arity_type (IndFamily (mispec,params)) = - let arity = body_of_type (mis_arity mispec) in -(* instantiate_params arity params (mis_params_ctxt mispec) *) - prod_applist arity params +let get_constructors ((mispec,params) as indf) = + let constr_tys = mispec.mis_mip.mind_nf_lc in + Array.mapi (process_constructor indf) constr_tys -let get_arity (IndFamily (mispec,params)) = - let arity = body_of_type (mis_nf_arity mispec) in -(* instantiate_params arity params (mis_params_ctxt mispec) *) - destArity (prod_applist arity params) +(***********************************************************************) -(* Functions to build standard types related to inductive *) +(* Type of case branches *) let local_rels = let rec relrec acc n = function (* more recent arg in front *) @@ -281,34 +186,627 @@ let local_rels = let build_dependent_constructor cs = applist - (mkMutConstruct cs.cs_cstr, + (mkConstruct cs.cs_cstr, (List.map (lift cs.cs_nargs) cs.cs_params)@(local_rels cs.cs_args)) -let build_dependent_inductive (IndFamily (mis, params) as indf) = +let build_dependent_inductive ((mis, params) as indf) = let arsign,_ = get_arity indf in - let nrealargs = mis_nrealargs mis in + let nrealargs = mis.mis_mip.mind_nrealargs in applist - (mkMutInd (mis_inductive mis), + (mkInd (mis_inductive mis), (List.map (lift nrealargs) params)@(local_rels arsign)) -(* builds the arity of an elimination predicate in sort [s] *) -let make_arity env dep indf s = - let (arsign,_) = get_arity indf in - if dep then - (* We need names everywhere *) - it_mkProd_or_LetIn_name env - (mkArrow (build_dependent_inductive indf) (mkSort s)) arsign - else - (* No need to enforce names *) - it_mkProd_or_LetIn (mkSort s) arsign - (* [p] is the predicate and [cs] a constructor summary *) -let build_branch_type env dep p cs = - let base = appvect (lift cs.cs_nargs p, cs.cs_concl_realargs) in - if dep then - it_mkProd_or_LetIn_name env - (applist (base,[build_dependent_constructor cs])) - cs.cs_args - else - it_mkProd_or_LetIn base cs.cs_args +let build_branch_type dep p cs = + let args = + if dep then + Array.append cs.cs_concl_realargs [|build_dependent_constructor cs|] + else + cs.cs_concl_realargs in + let base = beta_appvect (lift cs.cs_nargs p) args in + it_mkProd_or_LetIn base cs.cs_args + + +let is_info_arity env c = + match dest_arity env c with + | (_,Prop Null) -> false + | (_,Prop Pos) -> true + | (_,Type _) -> true + +let error_elim_expln env kp ki = + if is_info_arity env kp && not (is_info_arity env ki) then + "non-informative objects may not construct informative ones." + else + match (kind_of_term kp,kind_of_term ki) with + | Sort (Type _), Sort (Prop _) -> + "strong elimination on non-small inductive types leads to paradoxes." + | _ -> "wrong arity" + +exception Arity of (constr * constr * string) option + + +let is_correct_arity env kelim (c,pj) indf t = + let rec srec (pt,t) u = + let pt' = whd_betadeltaiota env pt in + let t' = whd_betadeltaiota env t in + match kind_of_term pt', kind_of_term t' with + | Prod (_,a1,a2), Prod (_,a1',a2') -> + let univ = + try conv env a1 a1' + with NotConvertible -> raise (Arity None) in + srec (a2,a2') (Constraint.union u univ) + | Prod (_,a1,a2), _ -> + let k = whd_betadeltaiota env a2 in + let ksort = match kind_of_term k with + | Sort s -> family_of_sort s + | _ -> raise (Arity None) in + let ind = build_dependent_inductive indf in + let univ = + try conv env a1 ind + with NotConvertible -> raise (Arity None) in + if List.exists ((=) ksort) kelim then + ((true,k), Constraint.union u univ) + else + raise (Arity (Some(k,t',error_elim_expln env k t'))) + | k, Prod (_,_,_) -> + raise (Arity None) + | k, ki -> + let ksort = match k with + | Sort s -> family_of_sort s + | _ -> raise (Arity None) in + if List.exists ((=) ksort) kelim then + (false, pt'), u + else + raise (Arity (Some(pt',t',error_elim_expln env pt' t'))) + in + try srec (pj.uj_type,t) Constraint.empty + with Arity kinds -> + let create_sort = function + | InProp -> mkProp + | InSet -> mkSet + | InType -> mkType (Univ.new_univ ()) in + let listarity = List.map create_sort kelim +(* let listarity = + (List.map (fun s -> make_arity env true indf (create_sort s)) kelim) + @(List.map (fun s -> make_arity env false indf (create_sort s)) kelim)*) + in + let ind = mis_inductive (fst indf) in + error_elim_arity env ind listarity c pj kinds + + +let find_case_dep_nparams env (c,pj) (ind,params) = + let indf = lookup_mind_instance ind env in + let kelim = indf.mis_mip.mind_kelim in + let arsign,s = get_arity (indf,params) in + let glob_t = it_mkProd_or_LetIn (mkSort s) arsign in + let ((dep,_),univ) = + is_correct_arity env kelim (c,pj) (indf,params) glob_t in + (dep,univ) + + +let type_case_branches env (mind,largs) pj c = + let mispec = lookup_mind_instance mind env in + let nparams = mispec.mis_mip.mind_nparams in + let (params,realargs) = list_chop nparams largs in + let indf = (mispec,params) in + let p = pj.uj_val in + let (dep,univ) = find_case_dep_nparams env (c,pj) (mind,params) in + let constructs = get_constructors indf in + let lc = Array.map (build_branch_type dep p) constructs in + let args = if dep then realargs@[c] else realargs in + (lc, beta_appvect p (Array.of_list args), univ) + + +let check_case_info env indsp ci = + let (mib,mip) = lookup_mind_specif env indsp in + if + (indsp <> ci.ci_ind) or + (mip.mind_nparams <> ci.ci_npar) + then raise (TypeError(env,WrongCaseInfo(indsp,ci))) + +(***********************************************************************) +(***********************************************************************) + +(* Guard conditions for fix and cofix-points *) + +(* Check if t is a subterm of Rel n, and gives its specification, + assuming lst already gives index of + subterms with corresponding specifications of recursive arguments *) + +(* A powerful notion of subterm *) + +let find_sorted_assoc p = + let rec findrec = function + | (a,ta)::l -> + if a < p then findrec l else if a = p then ta else raise Not_found + | _ -> raise Not_found + in + findrec + +let map_lift_fst_n m = List.map (function (n,t)->(n+m,t)) +let map_lift_fst = map_lift_fst_n 1 + +let rec instantiate_recarg sp lrc ra = + match ra with + | Mrec(j) -> Imbr((sp,j),lrc) + | Imbr(ind_sp,l) -> Imbr(ind_sp, List.map (instantiate_recarg sp lrc) l) + | Norec -> Norec + | Param(k) -> List.nth lrc k + +(* To each inductive definition corresponds an array describing the + structure of recursive arguments for each constructor, we call it + the recursive spec of the type (it has type recargs vect). For + checking the guard, we start from the decreasing argument (Rel n) + with its recursive spec. During checking the guardness condition, + we collect patterns variables corresponding to subterms of n, each + of them with its recursive spec. They are organised in a list lst + of type (int * recargs) list which is sorted with respect to the + first argument. +*) + +(* + f is a function of type + env -> int -> (int * recargs) list -> constr -> 'a + + c is a branch of an inductive definition corresponding to the spec + lrec. mind_recvec is the recursive spec of the inductive + definition of the decreasing argument n. + + check_term env mind_recvec f n lst (lrec,c) will pass the lambdas + of c corresponding to pattern variables and collect possibly new + subterms variables and apply f to the body of the branch with the + correct env and decreasing arg. +*) + +let check_term env mind_recvec f = + let rec crec env n lst (lrec,c) = + let c' = strip_outer_cast c in + match lrec, kind_of_term c' with + (ra::lr,Lambda (x,a,b)) -> + let lst' = map_lift_fst lst + and env' = push_rel (x,None,a) env + and n'=n+1 + in begin match ra with + Mrec(i) -> crec env' n' ((1,mind_recvec.(i))::lst') (lr,b) + | Imbr((sp,i) as ind_sp,lrc) -> + let sprecargs = lookup_recargs env ind_sp in + let lc = Array.map + (List.map (instantiate_recarg sp lrc)) sprecargs.(i) + in crec env' n' ((1,lc)::lst') (lr,b) + | _ -> crec env' n' lst' (lr,b) end + | (_,_) -> f env n lst c' + in crec env + +(* c is supposed to be in beta-delta-iota head normal form *) + +let is_inst_var k c = + match kind_of_term (fst (decompose_app c)) with + | Rel n -> n=k + | _ -> false + +(* + is_subterm_specif env lcx mind_recvec n lst c + + n is the principal arg and has recursive spec lcx, lst is the list + of subterms of n with spec. is_subterm_specif should test if c is + a subterm of n and fails with Not_found if not. In case it is, it + should send its recursive specification. This recursive spec + should be the same size as the number of constructors of the type + of c. A problem occurs when c is built by contradiction. In that + case no spec is given. +*) +let is_subterm_specif env lcx mind_recvec = + let rec crec env n lst c = + let f,l = decompose_app (whd_betadeltaiota env c) in + match kind_of_term f with + | Rel k -> Some (find_sorted_assoc k lst) + + | Case ( _,_,c,br) -> + if Array.length br = 0 then None + + else + let def = Array.create (Array.length br) [] + in let lcv = + (try + if is_inst_var n c then lcx + else match crec env n lst c with Some lr -> lr | None -> def + with Not_found -> def) + in + assert (Array.length br = Array.length lcv); + let stl = + array_map2 + (fun lc a -> + check_term env mind_recvec crec n lst (lc,a)) lcv br + in let stl0 = stl.(0) in + if array_for_all (fun st -> st=stl0) stl then stl0 + else None + + | Fix ((recindxs,i),(_,typarray,bodies as recdef)) -> + let nbfix = Array.length typarray in + let decrArg = recindxs.(i) in + let theBody = bodies.(i) in + let sign,strippedBody = decompose_lam_n_assum (decrArg+1) theBody in + let nbOfAbst = nbfix+decrArg+1 in +(* when proving that the fixpoint f(x)=e is less than n, it is enough + to prove that e is less than n assuming f is less than n + furthermore when f is applied to a term which is strictly less than + n, one may assume that x itself is strictly less than n +*) + let newlst = + let lst' = (nbOfAbst,lcx) :: (map_lift_fst_n nbOfAbst lst) in + if List.length l < (decrArg+1) then lst' + else + let theDecrArg = List.nth l decrArg in + try + match crec env n lst theDecrArg with + (Some recArgsDecrArg) -> (1,recArgsDecrArg) :: lst' + | None -> lst' + with Not_found -> lst' in + let env' = push_rec_types recdef env in + let env'' = push_rel_context sign env' in + crec env'' (n+nbOfAbst) newlst strippedBody + + | Lambda (x,a,b) when l=[] -> + let lst' = map_lift_fst lst in + crec (push_rel (x, None, a) env) (n+1) lst' b + + (*** Experimental change *************************) + | Meta _ -> None + | _ -> raise Not_found + in + crec env + +let spec_subterm_strict env lcx mind_recvec n lst c nb = + try match is_subterm_specif env lcx mind_recvec n lst c + with Some lr -> lr | None -> Array.create nb [] + with Not_found -> Array.create nb [] + +let spec_subterm_large env lcx mind_recvec n lst c nb = + if is_inst_var n c then lcx + else spec_subterm_strict env lcx mind_recvec n lst c nb + + +let is_subterm env lcx mind_recvec n lst c = + try + let _ = is_subterm_specif env lcx mind_recvec n lst c in true + with Not_found -> + false + +(***********************************************************************) + +exception FixGuardError of guard_error + +(* Auxiliary function: it checks a condition f depending on a deBrujin + index for a certain number of abstractions *) + +let rec check_subterm_rec_meta env vectn k def = + (let nfi = Array.length vectn in + (* check fi does not appear in the k+1 first abstractions, + gives the type of the k+1-eme abstraction *) + let rec check_occur env n def = + match kind_of_term (strip_outer_cast def) with + | Lambda (x,a,b) -> + if noccur_with_meta n nfi a then + let env' = push_rel (x, None, a) env in + if n = k+1 then (env', lift 1 a, b) + else check_occur env' (n+1) b + else + anomaly "check_subterm_rec_meta: Bad occurrence of recursive call" + | _ -> raise (FixGuardError NotEnoughAbstractionInFixBody) in + let (env',c,d) = check_occur env 1 def in + let ((sp,tyi) as mind, largs) = + try find_inductive env' c + with Induc -> raise (FixGuardError RecursionNotOnInductiveType) in + let mind_recvec = lookup_recargs env' (sp,tyi) in + let lcx = mind_recvec.(tyi) in + (* n = decreasing argument in the definition; + lst = a mapping var |-> recargs + t = the term to be checked + *) + let rec check_rec_call env n lst t = + (* n gives the index of the recursive variable *) + (noccur_with_meta (n+k+1) nfi t) or + (* no recursive call in the term *) + (let f,l = hnf_stack env t in + match kind_of_term f with + | Rel p -> + if n+k+1 <= p & p < n+k+nfi+1 then + (* recursive call *) + let glob = nfi+n+k-p in (* the index of the recursive call *) + let np = vectn.(glob) in (* the decreasing arg of the rec call *) + if List.length l > np then + (match list_chop np l with + (la,(z::lrest)) -> + if (is_subterm env lcx mind_recvec n lst z) + then List.for_all (check_rec_call env n lst) (la@lrest) + else raise (FixGuardError RecursionOnIllegalTerm) + | _ -> assert false) + else raise (FixGuardError NotEnoughArgumentsForFixCall) + else List.for_all (check_rec_call env n lst) l + + | Case (ci,p,c_0,lrest) -> + let lc = spec_subterm_large env lcx mind_recvec n lst c_0 + (Array.length lrest) + in + (array_for_all2 + (fun c0 a -> + check_term env mind_recvec check_rec_call n lst (c0,a)) + lc lrest) + && (List.for_all (check_rec_call env n lst) (c_0::p::l)) + + (* Enables to traverse Fixpoint definitions in a more intelligent + way, ie, the rule : + + if - g = Fix g/1 := [y1:T1]...[yp:Tp]e & + - f is guarded with respect to the set of pattern variables S + in a1 ... am & + - f is guarded with respect to the set of pattern variables S + in T1 ... Tp & + - ap is a sub-term of the formal argument of f & + - f is guarded with respect to the set of pattern variables S+{yp} + in e + then f is guarded with respect to S in (g a1 ... am). + + Eduardo 7/9/98 *) + + | Fix ((recindxs,i),(_,typarray,bodies as recdef)) -> + (List.for_all (check_rec_call env n lst) l) && + (array_for_all (check_rec_call env n lst) typarray) && + let nbfix = Array.length typarray in + let decrArg = recindxs.(i) + and env' = push_rec_types recdef env + and n' = n+nbfix + and lst' = map_lift_fst_n nbfix lst + in + if (List.length l < (decrArg+1)) then + array_for_all (check_rec_call env' n' lst') bodies + else + let theDecrArg = List.nth l decrArg in + (try + match + is_subterm_specif env lcx mind_recvec n lst theDecrArg + with + Some recArgsDecrArg -> + let theBody = bodies.(i) in + check_rec_call_fix_body + env' n' lst' (decrArg+1) recArgsDecrArg theBody + | None -> + array_for_all (check_rec_call env' n' lst') bodies + with Not_found -> + array_for_all (check_rec_call env' n' lst') bodies) + + | Cast (a,b) -> + (check_rec_call env n lst a) && + (check_rec_call env n lst b) && + (List.for_all (check_rec_call env n lst) l) + + | Lambda (x,a,b) -> + (check_rec_call env n lst a) && + (check_rec_call (push_rel (x, None, a) env) + (n+1) (map_lift_fst lst) b) && + (List.for_all (check_rec_call env n lst) l) + + | Prod (x,a,b) -> + (check_rec_call env n lst a) && + (check_rec_call (push_rel (x, None, a) env) + (n+1) (map_lift_fst lst) b) && + (List.for_all (check_rec_call env n lst) l) + + | LetIn (x,a,b,c) -> + anomaly "check_rec_call: should have been reduced" + + | Ind _ -> + (List.for_all (check_rec_call env n lst) l) + + | Construct _ -> + (List.for_all (check_rec_call env n lst) l) + + | Const sp as c -> + (try + (List.for_all (check_rec_call env n lst) l) + with (FixGuardError _ ) as e + -> if evaluable_constant env sp then + check_rec_call env n lst (whd_betadeltaiota env t) + else raise e) + + | App (f,la) -> + (check_rec_call env n lst f) && + (array_for_all (check_rec_call env n lst) la) && + (List.for_all (check_rec_call env n lst) l) + + | CoFix (i,(_,typarray,bodies as recdef)) -> + let nbfix = Array.length typarray in + let env' = push_rec_types recdef env in + (array_for_all (check_rec_call env n lst) typarray) && + (List.for_all (check_rec_call env n lst) l) && + (array_for_all + (check_rec_call env' (n+nbfix) (map_lift_fst_n nbfix lst)) + bodies) + + | Evar (_,la) -> + (array_for_all (check_rec_call env n lst) la) && + (List.for_all (check_rec_call env n lst) l) + + | Meta _ -> true + + | Var _ | Sort _ -> List.for_all (check_rec_call env n lst) l + ) + + and check_rec_call_fix_body env n lst decr recArgsDecrArg body = + if decr = 0 then + check_rec_call env n ((1,recArgsDecrArg)::lst) body + else + match kind_of_term body with + | Lambda (x,a,b) -> + (check_rec_call env n lst a) & + (check_rec_call_fix_body + (push_rel (x, None, a) env) (n+1) + (map_lift_fst lst) (decr-1) recArgsDecrArg b) + | _ -> anomaly "Not enough abstractions in fix body" + + in + check_rec_call env' 1 [] d) + +(* vargs is supposed to be built from A1;..Ak;[f1]..[fk][|d1;..;dk|] +and vdeft is [|t1;..;tk|] such that f1:A1,..,fk:Ak |- di:ti +nvect is [|n1;..;nk|] which gives for each recursive definition +the inductive-decreasing index +the function checks the convertibility of ti with Ai *) + +let check_fix env ((nvect,bodynum),(names,types,bodies as recdef)) = + let nbfix = Array.length bodies in + if nbfix = 0 + or Array.length nvect <> nbfix + or Array.length types <> nbfix + or Array.length names <> nbfix + or bodynum < 0 + or bodynum >= nbfix + then anomaly "Ill-formed fix term"; + for i = 0 to nbfix - 1 do + let fixenv = push_rec_types recdef env in + if nvect.(i) < 0 then anomaly "negative recarg position"; + try + let _ = check_subterm_rec_meta fixenv nvect nvect.(i) bodies.(i) + in () + with FixGuardError err -> + error_ill_formed_rec_body fixenv err names i bodies + done + +(* +let cfkey = Profile.declare_profile "check_fix";; +let check_fix env fix = Profile.profile3 cfkey check_fix env fix;; +*) + +(***********************************************************************) +(* Co-fixpoints. *) + +exception CoFixGuardError of guard_error + +let anomaly_ill_typed () = + anomaly "check_guard_rec_meta: too many arguments applied to constructor" + + +let check_guard_rec_meta env nbfix def deftype = + let rec codomain_is_coind env c = + let b = whd_betadeltaiota env (strip_outer_cast c) in + match kind_of_term b with + | Prod (x,a,b) -> + codomain_is_coind (push_rel (x, None, a) env) b + | _ -> + try + find_coinductive env b + with Induc -> + raise (CoFixGuardError (CodomainNotInductiveType b)) + in + let (mind, _) = codomain_is_coind env deftype in + let (sp,tyi) = mind in + let lvlra = lookup_recargs env (sp,tyi) in + let vlra = lvlra.(tyi) in + let rec check_rec_call env alreadygrd n vlra t = + if noccur_with_meta n nbfix t then + true + else + let c,args = decompose_app (whd_betadeltaiota env t) in + match kind_of_term c with + | Meta _ -> true + + | Rel p -> + if n <= p && p < n+nbfix then + (* recursive call *) + if alreadygrd then + if List.for_all (noccur_with_meta n nbfix) args then + true + else + raise (CoFixGuardError NestedRecursiveOccurrences) + else + raise (CoFixGuardError (UnguardedRecursiveCall t)) + else + error "check_guard_rec_meta: ???" (* ??? *) + + | Construct (_,i as cstr_sp) -> + let lra =vlra.(i-1) in + let mI = inductive_of_constructor cstr_sp in + let (mib,mip) = lookup_mind_specif env mI in + let _,realargs = list_chop mip.mind_nparams args in + let rec process_args_of_constr l lra = + match l with + | [] -> true + | t::lr -> + (match lra with + | [] -> anomaly_ill_typed () + | (Mrec i)::lrar -> + let newvlra = lvlra.(i) in + (check_rec_call env true n newvlra t) && + (process_args_of_constr lr lrar) + + | (Imbr((sp,i) as ind_sp,lrc)::lrar) -> + let sprecargs = lookup_recargs env ind_sp in + let lc = (Array.map + (List.map + (instantiate_recarg sp lrc)) + sprecargs.(i)) + in (check_rec_call env true n lc t) & + (process_args_of_constr lr lrar) + + | _::lrar -> + if (noccur_with_meta n nbfix t) + then (process_args_of_constr lr lrar) + else raise (CoFixGuardError + (RecCallInNonRecArgOfConstructor t))) + in (process_args_of_constr realargs lra) + + + | Lambda (x,a,b) -> + assert (args = []); + if (noccur_with_meta n nbfix a) then + check_rec_call (push_rel (x, None, a) env) + alreadygrd (n+1) vlra b + else + raise (CoFixGuardError (RecCallInTypeOfAbstraction t)) + + | CoFix (j,(_,varit,vdefs as recdef)) -> + if (List.for_all (noccur_with_meta n nbfix) args) + then + let nbfix = Array.length vdefs in + if (array_for_all (noccur_with_meta n nbfix) varit) then + let env' = push_rec_types recdef env in + (array_for_all + (check_rec_call env' alreadygrd (n+1) vlra) vdefs) + && + (List.for_all (check_rec_call env alreadygrd (n+1) vlra) args) + else + raise (CoFixGuardError (RecCallInTypeOfDef c)) + else + raise (CoFixGuardError (UnguardedRecursiveCall c)) + + | Case (_,p,tm,vrest) -> + if (noccur_with_meta n nbfix p) then + if (noccur_with_meta n nbfix tm) then + if (List.for_all (noccur_with_meta n nbfix) args) then + (array_for_all (check_rec_call env alreadygrd n vlra) vrest) + else + raise (CoFixGuardError (RecCallInCaseFun c)) + else + raise (CoFixGuardError (RecCallInCaseArg c)) + else + raise (CoFixGuardError (RecCallInCasePred c)) + + | _ -> raise (CoFixGuardError NotGuardedForm) + + in + check_rec_call env false 1 vlra def + +(* The function which checks that the whole block of definitions + satisfies the guarded condition *) + +let check_cofix env (bodynum,(names,types,bodies as recdef)) = + let nbfix = Array.length bodies in + for i = 0 to nbfix-1 do + let fixenv = push_rec_types recdef env in + try + let _ = check_guard_rec_meta fixenv nbfix bodies.(i) types.(i) + in () + with CoFixGuardError err -> + error_ill_formed_rec_body fixenv err names i bodies + done |