]> matita.cs.unibo.it Git - helm.git/blob - helm/ocaml/cic_unification/cicMetaSubst.ml
Bug fixed: the canonical contexts were traversed in the wrong direction
[helm.git] / helm / ocaml / cic_unification / cicMetaSubst.ml
1
2 open Printf
3
4 exception AssertFailure of string
5 exception MetaSubstFailure of string
6
7 let debug_print = prerr_endline
8
9 type substitution = (int * Cic.term) list
10
11 let ppsubst subst =
12   String.concat "\n"
13     (List.map
14       (fun (idx, term) -> Printf.sprintf "?%d := %s" idx (CicPp.ppterm term))
15       subst)
16
17 (**** DELIFT ****)
18 (* the delift function takes in input a metavariable index, an ordered list of
19  * optional terms [t1,...,tn] and a term t, and substitutes every tk = Some
20  * (rel(nk)) with rel(k).  Typically, the list of optional terms is the explicit
21  * substitution that is applied to a metavariable occurrence and the result of
22  * the delift function is a term the implicit variable can be substituted with
23  * to make the term [t] unifiable with the metavariable occurrence.  In general,
24  * the problem is undecidable if we consider equivalence in place of alpha
25  * convertibility. Our implementation, though, is even weaker than alpha
26  * convertibility, since it replace the term [tk] if and only if [tk] is a Rel
27  * (missing all the other cases). Does this matter in practice?
28  * The metavariable index is the index of the metavariable that must not occur
29  * in the term (for occur check).
30  *)
31
32 exception NotInTheList;;
33
34 let position n =
35   let rec aux k =
36    function 
37        [] -> raise NotInTheList
38      | (Some (Cic.Rel m))::_ when m=n -> k
39      | _::tl -> aux (k+1) tl in
40   aux 1
41 ;;
42
43 exception Occur;;
44
45 let rec force_does_not_occur k subst to_be_restricted t =
46  let module C = Cic in
47  let more_to_be_restricted = ref [] in
48  let rec aux k = function
49       C.Rel r when List.mem (r - k) to_be_restricted -> raise Occur
50     | C.Rel _
51     | C.Sort _ as t -> t
52     | C.Implicit -> assert false
53     | C.Meta (n, l) ->
54        (* we do not retrieve the term associated to ?n in subst since *)
55        (* in this way we can restrict if something goes wrong         *)
56        let l' =
57          let i = ref 0 in
58          List.map
59            (function t ->
60              incr i ;
61              match t with
62                 None -> None
63               | Some t ->
64                  try
65                    Some (aux k t)
66                  with Occur ->
67                    more_to_be_restricted := (n,!i) :: !more_to_be_restricted;
68                    None)
69            l
70        in
71         C.Meta (n, l')
72     | C.Cast (te,ty) -> C.Cast (aux k te, aux k ty)
73     | C.Prod (name,so,dest) -> C.Prod (name, aux k so, aux (k+1) dest)
74     | C.Lambda (name,so,dest) -> C.Lambda (name, aux k so, aux (k+1) dest)
75     | C.LetIn (name,so,dest) -> C.LetIn (name, aux k so, aux (k+1) dest)
76     | C.Appl l -> C.Appl (List.map (aux k) l)
77     | C.Var (uri,exp_named_subst) ->
78         let exp_named_subst' =
79           List.map (fun (uri,t) -> (uri, aux k t)) exp_named_subst
80         in
81         C.Var (uri, exp_named_subst')
82     | C.Const (uri, exp_named_subst) ->
83         let exp_named_subst' =
84           List.map (fun (uri,t) -> (uri, aux k t)) exp_named_subst
85         in
86         C.Const (uri, exp_named_subst')
87     | C.MutInd (uri,tyno,exp_named_subst) ->
88         let exp_named_subst' =
89           List.map (fun (uri,t) -> (uri, aux k t)) exp_named_subst
90         in
91         C.MutInd (uri, tyno, exp_named_subst')
92     | C.MutConstruct (uri,tyno,consno,exp_named_subst) ->
93         let exp_named_subst' =
94           List.map (fun (uri,t) -> (uri, aux k t)) exp_named_subst
95         in
96         C.MutConstruct (uri, tyno, consno, exp_named_subst')
97     | C.MutCase (uri,tyno,out,te,pl) ->
98         C.MutCase (uri, tyno, aux k out, aux k te, List.map (aux k) pl)
99     | C.Fix (i,fl) ->
100        let len = List.length fl in
101        let k_plus_len = k + len in
102        let fl' =
103          List.map
104           (fun (name,j,ty,bo) -> (name, j, aux k ty, aux k_plus_len bo)) fl
105        in
106        C.Fix (i, fl')
107     | C.CoFix (i,fl) ->
108        let len = List.length fl in
109        let k_plus_len = k + len in
110        let fl' =
111          List.map
112           (fun (name,ty,bo) -> (name, aux k ty, aux k_plus_len bo)) fl
113        in
114        C.CoFix (i, fl')
115  in
116  let res = aux k t in
117  (!more_to_be_restricted, res)
118  
119 let rec restrict subst to_be_restricted metasenv =
120   let names_of_context_indexes context indexes =
121     String.concat ", "
122       (List.map
123         (fun i ->
124           match List.nth context i with
125           | None -> assert false
126           | Some (n, _) -> CicPp.ppname n)
127         indexes)
128   in
129   let force_does_not_occur_in_context k to_be_restricted = function
130     | None -> [], None
131     | Some (name, Cic.Decl t) ->
132         let (more_to_be_restricted, t') =
133           force_does_not_occur k subst to_be_restricted t
134         in
135         more_to_be_restricted, Some (name, Cic.Decl t')
136     | Some (name, Cic.Def (bo, ty)) ->
137         let (more_to_be_restricted, bo') =
138           force_does_not_occur k subst to_be_restricted bo
139         in
140         let more_to_be_restricted, ty' =
141           match ty with
142           | None ->  more_to_be_restricted, None
143           | Some ty ->
144               let more_to_be_restricted', ty' =
145                 force_does_not_occur k subst to_be_restricted ty
146               in
147               more_to_be_restricted @ more_to_be_restricted',
148               Some ty'
149         in
150         more_to_be_restricted, Some (name, Cic.Def (bo', ty'))
151   in
152   let rec erase i to_be_restricted n = function
153     | [] -> [], to_be_restricted, []
154     | hd::tl ->
155         let more_to_be_restricted,restricted,tl' =
156          erase (i+1) to_be_restricted n tl
157         in
158         let restrict_me = List.mem i restricted in
159         if restrict_me then
160          more_to_be_restricted, restricted, None:: tl'
161         else
162           (try
163             let more_to_be_restricted', hd' =
164               force_does_not_occur_in_context i restricted hd
165             in
166              more_to_be_restricted @ more_to_be_restricted',
167              restricted, hd' :: tl'
168           with Occur ->
169             more_to_be_restricted, (i :: restricted), None :: tl')
170   in
171   let (more_to_be_restricted, metasenv, subst) =
172     List.fold_right
173       (fun (n, context, t) (more, metasenv, subst) ->
174         let to_be_restricted =
175           List.map snd (List.filter (fun (m, _) -> m = n) to_be_restricted)
176         in
177         let (more_to_be_restricted, restricted, context') =
178          (* just an optimization *)
179          if to_be_restricted = [] then
180           [],[],context
181          else
182           erase 1 to_be_restricted n context
183         in
184         try
185           let more_to_be_restricted', t' =
186             force_does_not_occur 0 subst restricted t
187           in
188           let metasenv' = (n, context', t') :: metasenv in
189           (try
190             let s = List.assoc n subst in
191             try
192               let more_to_be_restricted'', s' =
193                 force_does_not_occur 0 subst restricted s
194               in
195               let subst' = (n, s') :: (List.remove_assoc n subst) in
196               let more =
197                 more @ more_to_be_restricted @ more_to_be_restricted' @
198                   more_to_be_restricted''
199               in
200               (more, metasenv', subst')
201             with Occur ->
202               raise (MetaSubstFailure (sprintf
203                 "Cannot restrict the context of the metavariable ?%d over the hypotheses %s since ?%d is already instantiated with %s and at least one of the hypotheses occurs in the substituted term"
204                 n (names_of_context_indexes context to_be_restricted) n
205                 (CicPp.ppterm s)))
206            with Not_found -> (more @ more_to_be_restricted @ more_to_be_restricted', metasenv', subst))
207         with Occur ->
208           raise (MetaSubstFailure (sprintf
209             "Cannot restrict the context of the metavariable ?%d over the hypotheses %s since metavariable's type depends on at least one of them"
210             n (names_of_context_indexes context to_be_restricted))))
211       metasenv ([], [], subst)
212   in
213   match more_to_be_restricted with
214   | [] -> (metasenv, subst)
215   | _ -> restrict subst more_to_be_restricted metasenv
216 ;;
217
218 (*CSC: maybe we should rename delift in abstract, as I did in my dissertation *)
219 let delift n subst context metasenv l t =
220  let module S = CicSubstitution in
221 (* THIS CODE SHOULD NOT BE USEFUL AT ALL
222   let l =
223    let (_, canonical_context, _) = CicUtil.lookup_meta n metasenv in
224    List.map2 (fun ct lt ->
225      match (ct, lt) with
226      | None, _ -> None
227      | Some _, _ -> lt)
228      canonical_context l
229   in
230 *)
231   let to_be_restricted = ref [] in
232   let rec deliftaux k =
233    let module C = Cic in
234     function
235        C.Rel m -> 
236          if m <=k then
237           C.Rel m   (*CSC: che succede se c'e' un Def? Dovrebbe averlo gia' *)
238                     (*CSC: deliftato la regola per il LetIn                 *)
239                     (*CSC: FALSO! La regola per il LetIn non lo fa          *)
240          else
241           (match List.nth context (m-k-1) with
242             Some (_,C.Def (t,_)) ->
243              (*CSC: Hmmm. This bit of reduction is not in the spirit of    *)
244              (*CSC: first order unification. Does it help or does it harm? *)
245              deliftaux k (S.lift m t)
246           | Some (_,C.Decl t) ->
247              (*CSC: The following check seems to be wrong!             *)
248              (*CSC: B:Set |- ?2 : Set                                  *)
249              (*CSC: A:Set ; x:?2[A/B] |- ?1[x/A] =?= x                 *)
250              (*CSC: Why should I restrict ?2 over B? The instantiation *)
251              (*CSC: ?1 := A is perfectly reasonable and well-typed.    *)
252              (*CSC: Thus I comment out the following two lines that    *)
253              (*CSC: are the incriminated ones.                         *)
254              (*(* It may augment to_be_restricted *)
255                ignore (deliftaux k (S.lift m t)) ;*)
256              (*CSC: end of bug commented out                           *)
257              C.Rel ((position (m-k) l) + k)
258           | None -> raise (MetaSubstFailure "RelToHiddenHypothesis"))
259      | C.Var (uri,exp_named_subst) ->
260         let exp_named_subst' =
261          List.map (function (uri,t) -> uri,deliftaux k t) exp_named_subst
262         in
263          C.Var (uri,exp_named_subst')
264      | C.Meta (i, l1) as t -> 
265         if i = n then
266           raise (MetaSubstFailure (sprintf
267             "Cannot unify the metavariable ?%d with a term that has as subterm %s in which the same metavariable occurs (occur check)"
268             i (CicPp.ppterm t)))
269         else
270          (* I do not consider the term associated to ?i in subst since *)
271          (* in this way I can restrict if something goes wrong.        *)
272           let rec deliftl j =
273            function
274               [] -> []
275             | None::tl -> None::(deliftl (j+1) tl)
276             | (Some t)::tl ->
277                let l1' = (deliftl (j+1) tl) in
278                 try
279                  Some (deliftaux k t)::l1'
280                 with
281                    NotInTheList
282                  | MetaSubstFailure _ ->
283                     to_be_restricted := (i,j)::!to_be_restricted ; None::l1'
284           in
285            let l' = deliftl 1 l1 in
286             C.Meta(i,l')
287      | C.Sort _ as t -> t
288      | C.Implicit as t -> t
289      | C.Cast (te,ty) -> C.Cast (deliftaux k te, deliftaux k ty)
290      | C.Prod (n,s,t) -> C.Prod (n, deliftaux k s, deliftaux (k+1) t)
291      | C.Lambda (n,s,t) -> C.Lambda (n, deliftaux k s, deliftaux (k+1) t)
292      | C.LetIn (n,s,t) -> C.LetIn (n, deliftaux k s, deliftaux (k+1) t)
293      | C.Appl l -> C.Appl (List.map (deliftaux k) l)
294      | C.Const (uri,exp_named_subst) ->
295         let exp_named_subst' =
296          List.map (function (uri,t) -> uri,deliftaux k t) exp_named_subst
297         in
298          C.Const (uri,exp_named_subst')
299      | C.MutInd (uri,typeno,exp_named_subst) ->
300         let exp_named_subst' =
301          List.map (function (uri,t) -> uri,deliftaux k t) exp_named_subst
302         in
303          C.MutInd (uri,typeno,exp_named_subst')
304      | C.MutConstruct (uri,typeno,consno,exp_named_subst) ->
305         let exp_named_subst' =
306          List.map (function (uri,t) -> uri,deliftaux k t) exp_named_subst
307         in
308          C.MutConstruct (uri,typeno,consno,exp_named_subst')
309      | C.MutCase (sp,i,outty,t,pl) ->
310         C.MutCase (sp, i, deliftaux k outty, deliftaux k t,
311          List.map (deliftaux k) pl)
312      | C.Fix (i, fl) ->
313         let len = List.length fl in
314         let liftedfl =
315          List.map
316           (fun (name, i, ty, bo) ->
317            (name, i, deliftaux k ty, deliftaux (k+len) bo))
318            fl
319         in
320          C.Fix (i, liftedfl)
321      | C.CoFix (i, fl) ->
322         let len = List.length fl in
323         let liftedfl =
324          List.map
325           (fun (name, ty, bo) -> (name, deliftaux k ty, deliftaux (k+len) bo))
326            fl
327         in
328          C.CoFix (i, liftedfl)
329   in
330    let res =
331     try
332      deliftaux 0 t
333     with
334      NotInTheList ->
335       (* This is the case where we fail even first order unification. *)
336       (* The reason is that our delift function is weaker than first  *)
337       (* order (in the sense of alpha-conversion). See comment above  *)
338       (* related to the delift function.                              *)
339 debug_print "!!!!!!!!!!! First Order UnificationFailure, but maybe it could have been successful even in a first order setting (no conversion, only alpha convertibility)! Please, implement a better delift function !!!!!!!!!!!!!!!!" ;
340       raise (MetaSubstFailure (sprintf
341         "Error trying to abstract %s over [%s]: the algorithm only tried to abstract over bound variables"
342         (CicPp.ppterm t)
343         (String.concat "; "
344           (List.map
345             (function Some t -> CicPp.ppterm t | None -> "_")
346             l))))
347    in
348    let (metasenv, subst) = restrict subst !to_be_restricted metasenv in
349     res, metasenv, subst
350 ;;
351
352 (**** END OF DELIFT ****)
353
354 let apply_subst_gen ~appl_fun subst term =
355  let rec um_aux =
356   let module C = Cic in
357   let module S = CicSubstitution in 
358    function
359       C.Rel _ as t -> t
360     | C.Var _  as t -> t
361     | C.Meta (i, l) -> 
362         (try
363           let t = List.assoc i subst in
364           um_aux (S.lift_meta l t)
365         with Not_found -> (* not constrained variable, i.e. free in subst*)
366           let l' =
367             List.map (function None -> None | Some t -> Some (um_aux t)) l
368           in
369            C.Meta (i,l'))
370     | C.Sort _ as t -> t
371     | C.Implicit -> assert false
372     | C.Cast (te,ty) -> C.Cast (um_aux te, um_aux ty)
373     | C.Prod (n,s,t) -> C.Prod (n, um_aux s, um_aux t)
374     | C.Lambda (n,s,t) -> C.Lambda (n, um_aux s, um_aux t)
375     | C.LetIn (n,s,t) -> C.LetIn (n, um_aux s, um_aux t)
376     | C.Appl (hd :: tl) -> appl_fun um_aux hd tl
377     | C.Appl _ -> assert false
378     | C.Const (uri,exp_named_subst) ->
379        let exp_named_subst' =
380          List.map (fun (uri, t) -> (uri, um_aux t)) exp_named_subst
381        in
382        C.Const (uri, exp_named_subst')
383     | C.MutInd (uri,typeno,exp_named_subst) ->
384        let exp_named_subst' =
385          List.map (fun (uri, t) -> (uri, um_aux t)) exp_named_subst
386        in
387        C.MutInd (uri,typeno,exp_named_subst')
388     | C.MutConstruct (uri,typeno,consno,exp_named_subst) ->
389        let exp_named_subst' =
390          List.map (fun (uri, t) -> (uri, um_aux t)) exp_named_subst
391        in
392        C.MutConstruct (uri,typeno,consno,exp_named_subst')
393     | C.MutCase (sp,i,outty,t,pl) ->
394        let pl' = List.map um_aux pl in
395        C.MutCase (sp, i, um_aux outty, um_aux t, pl')
396     | C.Fix (i, fl) ->
397        let fl' =
398          List.map (fun (name, i, ty, bo) -> (name, i, um_aux ty, um_aux bo)) fl
399        in
400        C.Fix (i, fl')
401     | C.CoFix (i, fl) ->
402        let fl' =
403          List.map (fun (name, ty, bo) -> (name, um_aux ty, um_aux bo)) fl
404        in
405        C.CoFix (i, fl')
406  in
407  um_aux term
408
409 let apply_subst =
410   let appl_fun um_aux he tl =
411     let tl' = List.map um_aux tl in
412       begin
413        match um_aux he with
414           Cic.Appl l -> Cic.Appl (l@tl')
415         | he' -> Cic.Appl (he'::tl')
416       end
417   in
418   apply_subst_gen ~appl_fun
419
420 let ppterm subst term = CicPp.ppterm (apply_subst subst term)
421
422 (* apply_subst_reducing subst (Some (mtr,reductions_no)) t              *)
423 (* performs as (apply_subst subst t) until it finds an application of   *)
424 (* (META [meta_to_reduce]) that, once unwinding is performed, creates   *)
425 (* a new beta-redex; in this case up to [reductions_no] consecutive     *)
426 (* beta-reductions are performed.                                       *)
427 (* Hint: this function is usually called when [reductions_no]           *)
428 (*  eta-expansions have been performed and the head of the new          *)
429 (*  application has been unified with (META [meta_to_reduce]):          *)
430 (*  during the unwinding the eta-expansions are undone.                 *)
431
432 let apply_subst_reducing meta_to_reduce =
433   let appl_fun um_aux he tl =
434     let tl' = List.map um_aux tl in
435     let t' =
436      match um_aux he with
437         Cic.Appl l -> Cic.Appl (l@tl')
438       | he' -> Cic.Appl (he'::tl')
439     in
440      begin
441       match meta_to_reduce, he with
442          Some (mtr,reductions_no), Cic.Meta (m,_) when m = mtr ->
443           let rec beta_reduce =
444            function
445               (n,(Cic.Appl (Cic.Lambda (_,_,t)::he'::tl'))) when n > 0 ->
446                 let he'' = CicSubstitution.subst he' t in
447                  if tl' = [] then
448                   he''
449                  else
450                   beta_reduce (n-1,Cic.Appl(he''::tl'))
451             | (_,t) -> t
452           in
453            beta_reduce (reductions_no,t')
454        | _,_ -> t'
455      end
456   in
457   apply_subst_gen ~appl_fun
458
459 let rec apply_subst_context subst context =
460   List.fold_right
461     (fun item context ->
462       match item with
463       | Some (n, Cic.Decl t) ->
464           let t' = apply_subst subst t in
465           Some (n, Cic.Decl t') :: context
466       | Some (n, Cic.Def (t, ty)) ->
467           let ty' =
468             match ty with
469             | None -> None
470             | Some ty -> Some (apply_subst subst ty)
471           in
472           let t' = apply_subst subst t in
473           Some (n, Cic.Def (t', ty')) :: context
474       | None -> None :: context)
475     context []
476
477 let apply_subst_metasenv subst metasenv =
478   List.map
479     (fun (n, context, ty) ->
480       (n, apply_subst_context subst context, apply_subst subst ty))
481     (List.filter
482       (fun (i, _, _) -> not (List.exists (fun (j, _) -> (j = i)) subst))
483       metasenv)
484
485 let ppterm subst term = CicPp.ppterm (apply_subst subst term)
486
487 let ppterm_in_context subst term name_context =
488  CicPp.pp (apply_subst subst term) name_context
489
490 let ppcontext' ?(sep = "\n") subst context =
491  let separate s = if s = "" then "" else s ^ sep in
492   List.fold_right 
493    (fun context_entry (i,name_context) ->
494      match context_entry with
495         Some (n,Cic.Decl t) ->
496          sprintf "%s%s : %s" (separate i) (CicPp.ppname n)
497           (ppterm_in_context subst t name_context), (Some n)::name_context
498       | Some (n,Cic.Def (bo,ty)) ->
499          sprintf "%s%s : %s := %s" (separate i) (CicPp.ppname n)
500           (match ty with
501               None -> "_"
502             | Some ty -> ppterm_in_context subst ty name_context)
503           (ppterm_in_context subst bo name_context), (Some n)::name_context
504        | None ->
505           sprintf "%s_ :? _" (separate i), None::name_context
506     ) context ("",[])
507
508 let ppcontext ?sep subst context = fst (ppcontext' ?sep subst context)
509
510 let ppmetasenv ?(sep = "\n") metasenv subst =
511   String.concat sep
512     (List.map
513       (fun (i, c, t) ->
514         let context,name_context = ppcontext' ~sep:"; " subst c in
515          sprintf "%s |- ?%d: %s" context i
516           (ppterm_in_context subst t name_context))
517       (List.filter
518         (fun (i, _, _) -> not (List.exists (fun (j, _) -> (j = i)) subst))
519         metasenv))
520
521 (* UNWIND THE MGU INSIDE THE MGU *)
522 (*
523 let unwind_subst metasenv subst =
524   List.fold_left
525    (fun (unwinded,metasenv) (i,_) ->
526      let (_,canonical_context,_) = CicUtil.lookup_meta i metasenv in
527      let identity_relocation_list =
528       CicMkImplicit.identity_relocation_list_for_metavariable canonical_context
529      in
530       let (_,metasenv',subst') =
531        unwind metasenv subst unwinded (Cic.Meta (i,identity_relocation_list))
532       in
533        subst',metasenv'
534    ) ([],metasenv) subst
535 *)
536
537 (* From now on we recreate a kernel abstraction where substitutions are part of
538  * the calculus *)
539
540 let lift subst n term =
541   let term = apply_subst subst term in
542   try
543     CicSubstitution.lift n term
544   with e ->
545     raise (MetaSubstFailure ("Lift failure: " ^ Printexc.to_string e))
546
547 let subst subst t1 t2 =
548   let t1 = apply_subst subst t1 in
549   let t2 = apply_subst subst t2 in
550   try
551     CicSubstitution.subst t1 t2
552   with e ->
553     raise (MetaSubstFailure ("Subst failure: " ^ Printexc.to_string e))
554
555 let whd subst context term =
556   let term = apply_subst subst term in
557   let context = apply_subst_context subst context in
558   try
559     CicReduction.whd context term
560   with e ->
561     raise (MetaSubstFailure ("Weak head reduction failure: " ^
562       Printexc.to_string e))
563
564 let are_convertible subst context t1 t2 =
565   let context = apply_subst_context subst context in
566   let t1 = apply_subst subst t1 in
567   let t2 = apply_subst subst t2 in
568   CicReduction.are_convertible context t1 t2
569
570 let tempi_type_of_aux_subst = ref 0.0;;
571 let tempi_type_of_aux = ref 0.0;;
572
573 let type_of_aux' metasenv subst context term =
574 let time1 = Unix.gettimeofday () in
575   let term = apply_subst subst term in
576   let context = apply_subst_context subst context in
577   let metasenv =
578     List.map
579       (fun (i, c, t) -> (i, apply_subst_context subst c, apply_subst subst t))
580       (List.filter
581         (fun (i, _, _) -> not (List.exists (fun (j, _) -> (j = i)) subst))
582         metasenv)
583   in
584 let time2 = Unix.gettimeofday () in
585 let res =
586   try
587     CicTypeChecker.type_of_aux' metasenv context term
588   with CicTypeChecker.TypeCheckerFailure msg ->
589     raise (MetaSubstFailure ("Type checker failure: " ^ msg))
590 in
591 let time3 = Unix.gettimeofday () in
592  tempi_type_of_aux_subst := !tempi_type_of_aux_subst +. time3 -. time1 ; 
593  tempi_type_of_aux := !tempi_type_of_aux +. time2 -. time1 ; 
594  res