Jean-Alexandre Barszcz pushed to branch ja-barszcz at Stefan / Typer
Commits: 5483f630 by Jean-Alexandre Barszcz at 2020-06-09T13:26:36-04:00 WIP WIP WIP
- - - - -
2 changed files:
- src/elab.ml - src/unification.ml
Changes:
===================================== src/elab.ml ===================================== @@ -61,6 +61,8 @@ module Unif = Unification module OL = Opslexp module EL = Elexp
+type match_res = Impossible | Possible | Match + (* dummies *) let dloc = dummy_location
@@ -621,16 +623,94 @@ and get_implicit_arg ctx loc oname t = | None -> newMetavar (ectx_to_lctx ctx) (ectx_to_scope_level ctx) (loc, oname) t
+and is_typeclass t lctx = true (* TODO *) + (* Build the list of implicit arguments to instantiate. *) and instantiate_implicit e t ctx = let rec instantiate t args = match OL.lexp_whnf t (ectx_to_lctx ctx) with + | Arrow ((Aerasable | Aimplicit) as ak, (_, v), t1, _, t2) when is_typeclass t1 ctx + -> let lctx = ectx_to_lctx ctx in + let sl = ectx_to_scope_level ctx in + (* FIXME Should it be a special instance metavar? Or do we + know from its type? If we know from the type then + instantiation is the same for both cases (is_typeclass or + not) *) + let arg = newMetavar lctx sl (lexp_location e, v) t1 in + instantiate (mkSusp t2 (S.substitute arg)) ((ak, arg)::args) | Arrow ((Aerasable | Aimplicit) as ak, (_, v), t1, _, t2) -> let arg = get_implicit_arg ctx (lexp_location e) v t1 in instantiate (mkSusp t2 (S.substitute arg)) ((ak, arg)::args) | _ -> (mkCall (e, List.rev args), t) in instantiate t []
+and myers_filter_map_index (f : int -> 'a -> 'b option) (m : 'a M.myers) + : ('b M.myers) + = snd (M.fold_right + (fun x (i, l') -> + match (f i x) with + | Some y -> (i - 1, M.cons y l') + | None -> (i - 1, l')) + m (M.length m - 1, M.nil)) + +and check_typeclass_match t1 t2 lctx sl = + match Unif.unify ~checking:sl t1 t2 lctx with + | [] -> Match + | constraints when List.exists (function | (Unif.CKimpossible,_,_,_) -> true + | _ -> false) + constraints -> Impossible + | _ -> Possible + +and search_instance (ctx : elab_context) (loc : location) (t : ltype) : lexp * sform_type = + Log.log_debug ~loc ("Searching for t = `" ^ (lexp_string t) ^ "`"); + let lctx = (ectx_to_lctx ctx) in + let sl = (ectx_to_scope_level ctx) in (* FIXME should we create a new scope ? *) + let env_elem_match (i : int) (elem : DB.env_elem) : (int * DB.env_elem * lexp * ltype) option = + let (vname, _, t') = elem in + let var = mkVar (vname, i) in (* Change the location maybe *) + let t' = mkSusp t' (S.shift (i + 1)) in + let (e, t') = instantiate_implicit var t' ctx in + (* All candidates should have a type that is a typeclass *) + if not (is_typeclass t' ctx) then None else + match check_typeclass_match t t' lctx sl with + | Impossible -> None + (* | Possible -> None *) + | (Possible | Match) -> Some (i, elem, e, t') in + let candidates = + myers_filter_map_index env_elem_match lctx in + Log.log_debug ("Candidates for instance of type `" ^ lexp_string t ^ "`:") + ~print_action:(fun () -> + M.iter (fun (i, ((_, so),_,t'),_, _) -> + lalign_print_int i 4; + lalign_print_string (match so with | Some s -> s | None -> "<none>") 10; + print_endline (lexp_string t')) candidates); + match M.safe_car candidates with + | None -> + (error ~loc + ("No instance found in context for type `" ^ lexp_string t ^ "`"); + sform_dummy_ret ctx loc) + | Some (i, (vname, _, t'),e,t) -> + let t' = mkSusp t' (S.shift (i + 1)) in + Log.log_debug ~loc + ("Found candidate at index " ^ (string_of_int i) ^ ": `" ^ + (lexp_string (Var (vname, i))) ^ " : " ^ (lexp_string t') ^ "`"); + (e, Inferred t) + +and resolve_instances ctx e = + (* TODO + + For all instance metavars, we have to do the instance search in + their context. Maybe the elab_context is not necessary, only the + right lctx and scope level. + + Can we get a metavar's lctx from the current ctx and the + metavar's substitution? If yes then we can use OL.fv, otherwise + structural recursion on lexps MEH *) + failwith "TODO" + +and resolve_instances_and_generalize ctx e = + generalize ctx (resolve_instances ctx e) + and infer_type pexp ectx var = (* We could also use lexp_check with an argument of the form * Sort (?s), but in most cases the metavar would be allocated @@ -1822,14 +1902,6 @@ let sform_load usr_elctx loc sargs ot =
(tuple',Lazy)
-let myers_filter_index (f : int -> 'a -> bool) (m : 'a M.myers) - : ((int * 'a) M.myers) - = snd (M.fold_right - (fun x (i, l') -> - if (f i x) then (i - 1, M.cons (i, x) l') - else (i - 1, l')) - m (M.length m - 1, M.nil)) - (** Draft of a special form "instance" that gets refers to a variable of the requested type in the context. Ultimately, this search @@ -1837,43 +1909,10 @@ let myers_filter_index (f : int -> 'a -> bool) (m : 'a M.myers) of argument (instance arguments) similarly to implicit arguments. **) let sform_instance ctx loc sargs ot = - let search (t : ltype) : lexp * sform_type = - Log.log_debug ~loc ("Searching for t = `" ^ (lexp_string t) ^ "`"); - let lctx = (ectx_to_lctx ctx) in - let env_elem_match (i : int) (e : DB.env_elem) : bool = - let (_, _, t') = e in - let t' = mkSusp t' (S.shift (i + 1)) in - (* TODO decompose the arrows to match on the result type and - then recursively search for the parameters' types *) - let constraints = Unif.unify ~checking:true t' t lctx in - List.for_all (function | (Unif.CKimpossible,_,_,_) -> false - | (Unif.CKresidual,_,_,_) -> - false (* Should be true maybe ? *) - | _ -> true) - constraints in - let candidates = - myers_filter_index env_elem_match lctx in - Log.log_debug ("Candidates for instance of type `" ^ lexp_string t ^ "`:") - ~print_action:(fun () -> - M.iter (fun (i, ((_, so),_,t')) -> - lalign_print_int i 4; - lalign_print_string (match so with | Some s -> s | None -> "<none>") 10; - print_endline (lexp_string t')) candidates); - match M.safe_car candidates with - | None -> - (error ~loc - ("No instance found in context for type `" ^ lexp_string t ^ "`"); - sform_dummy_ret ctx loc) - | Some (i, (vname, _, t')) -> - let t' = mkSusp t' (S.shift (i + 1)) in - Log.log_debug ~loc - ("Found candidate at index " ^ (string_of_int i) ^ ": `" ^ - (lexp_string (Var (vname, i))) ^ " : " ^ (lexp_string t') ^ "`"); - (Var (vname, i), Inferred t') (* FIXME : What location should we use here ? *) - in + (* TODO replace with instance metavars *) match sargs, ot with - | ([se; _], _) -> search (infer_type se ctx (loc, None)) - | ([_], Some t) -> search t (* Dummy param to trigger the special form *) + | ([se; _], _) -> search_instance ctx loc (infer_type se ctx (loc, None)) + | ([_], Some t) -> search_instance ctx loc t (* Dummy param to trigger the special form *) | _ -> (sexp_error loc "##instance expects a type argument if not checked"; sform_dummy_ret ctx loc)
===================================== src/unification.ml ===================================== @@ -175,7 +175,7 @@ let rec s_offset s = match s with
The metavar unifier is the end rule, it can't call unify with its parameter (changing their order) *) -let rec unify ?(checking=false) +let rec unify ?checking (e1: lexp) (e2: lexp) (ctx : DB.lexp_context) : return_type = @@ -183,7 +183,7 @@ let rec unify ?(checking=false)
and unify' (e1: lexp) (e2: lexp) (ctx : DB.lexp_context) (vs : OL.set_plexp) - (c : bool) (* checking mode ? *) + (c : scope_level option) (* checking mode scope level *) : return_type = if e1 == e2 then [] else let e1' = OL.lexp_whnf e1 ctx in @@ -233,7 +233,7 @@ and unify' (e1: lexp) (e2: lexp) else None - (_, _) -> None *) -and unify_arrow (checking : bool) (arrow: lexp) (lxp: lexp) ctx vs +and unify_arrow (checking : scope_level option) (arrow: lexp) (lxp: lexp) ctx vs : return_type = match (arrow, lxp) with | (Arrow (var_kind1, v1, ltype1, _, lexp1), @@ -251,7 +251,7 @@ and unify_arrow (checking : bool) (arrow: lexp) (lxp: lexp) ctx vs then UNIFY ltype & lxp else ERROR - Lambda , _ -> Impossible *) -and unify_lambda (checking : bool) (lambda: lexp) (lxp: lexp) ctx vs : return_type = +and unify_lambda (checking : scope_level option) (lambda: lexp) (lxp: lexp) ctx vs : return_type = match (lambda, lxp) with | (Lambda (var_kind1, v1, ltype1, lexp1), Lambda (var_kind2, _, ltype2, lexp2)) @@ -268,37 +268,40 @@ and unify_lambda (checking : bool) (lambda: lexp) (lxp: lexp) ctx vs : return_ty - metavar , metavar -> inverse subst (both sides) - metavar , lexp -> inverse subst *) -and unify_metavar (checking : bool) ctx idx s1 (lxp1: lexp) (lxp2: lexp) +and unify_metavar (checking : scope_level option) ctx idx s1 (lxp1: lexp) (lxp2: lexp) : return_type = let unif idx s lxp = - let t = match metavar_lookup idx with + let t, sl = match metavar_lookup idx with | MVal _ -> Log.internal_error "`lexp_whnf` returned an instantiated metavar!!" - | MVar (_, t, _) -> push_susp t s in + | MVar (_, t, sl) -> push_susp t s, sl in match Inverse_subst.apply_inv_subst lxp s with | exception Inverse_subst.Not_invertible - -> log_info ?loc:None ("Unification of metavar failed:\n " - ^ "?[" ^ subst_string s ^ "]" - ^ "\nAgainst:\n " - ^ lexp_string lxp ^ "\n"); + -> log_info ~loc:(lexp_location lxp) + ("Unification of metavar failed:\n " + ^ "?[" ^ subst_string s ^ "]" + ^ "\nAgainst:\n " + ^ lexp_string lxp ^ "\n"); [(CKresidual, ctx, lxp1, lxp2)] | lxp' when occurs_in idx lxp' -> [(CKimpossible, ctx, lxp1, lxp2)] | lxp' - -> if checking then [(CKassoc, ctx, lxp1, lxp2)] else ( - metavar_table := associate idx lxp' (!metavar_table); - match unify t (OL.get_type ctx lxp) ctx with - | [] as r -> r - (* FIXME: Let's ignore the error for now. *) - | _ - -> log_info ?loc:None - ("Unification of metavar type failed:\n " - ^ lexp_string t ^ " != " - ^ lexp_string (OL.get_type ctx lxp) - ^ "\n" ^ "for " ^ lexp_string lxp ^ "\n"); - [(CKresidual, ctx, lxp1, lxp2)]) in + -> match checking with + | Some l when l >= sl -> [(CKassoc, ctx, lxp1, lxp2)] + | _ -> ( + metavar_table := associate idx lxp' (!metavar_table); + match unify t (OL.get_type ctx lxp) ctx with + | [] as r -> r + (* FIXME: Let's ignore the error for now. *) + | _ + -> log_info ?loc:None + ("Unification of metavar type failed:\n " + ^ lexp_string t ^ " != " + ^ lexp_string (OL.get_type ctx lxp) + ^ "\n" ^ "for " ^ lexp_string lxp ^ "\n"); + [(CKresidual, ctx, lxp1, lxp2)]) in match lxp2 with | Metavar (idx2, s2, name) - -> if idx = idx2 && not checking then + -> if idx = idx2 && checking == None then match common_subset ctx s1 s2 with | S.Identity 0 -> [] (* Optimization! *) (* ¡ s1 != s2 ! @@ -374,7 +377,7 @@ and unify_var (var: lexp) (lxp: lexp) ctx vs - Call , Call -> UNIFY - Call , lexp -> CONSTRAINT *) -and unify_call (checking : bool) (call: lexp) (lxp: lexp) ctx vs +and unify_call (checking : scope_level option) (call: lexp) (lxp: lexp) ctx vs : return_type = match (call, lxp) with | (Call (lxp_left, lxp_list1), Call (lxp_right, lxp_list2)) @@ -444,7 +447,7 @@ and unify_call (checking : bool) (call: lexp) (lxp: lexp) ctx vs - SortLevel, SortLevel -> if SortLevel ~= SortLevel then OK else ERROR - SortLevel, _ -> ERROR *) -and unify_sortlvl (checking : bool) (sortlvl: lexp) (lxp: lexp) ctx vs : return_type = +and unify_sortlvl (checking : scope_level option) (sortlvl: lexp) (lxp: lexp) ctx vs : return_type = match sortlvl, lxp with | (SortLevel s, SortLevel s2) -> (match s, s2 with | SLz, SLz -> [] @@ -460,7 +463,7 @@ and unify_sortlvl (checking : bool) (sortlvl: lexp) (lxp: lexp) ctx vs : return_ - Sort, Sort -> if Sort ~= Sort then OK else ERROR - Sort, lexp -> ERROR *) -and unify_sort (checking : bool) (sort_: lexp) (lxp: lexp) ctx vs : return_type = +and unify_sort (checking : scope_level option) (sort_: lexp) (lxp: lexp) ctx vs : return_type = match sort_, lxp with | (Sort (_, srt), Sort (_, srt2)) -> (match srt, srt2 with | Stype lxp1, Stype lxp2 -> unify' lxp1 lxp2 ctx vs checking @@ -517,7 +520,7 @@ and is_same arglist arglist2 = * | None -> test e subst) * ) None lst *)
-and unify_inductive (checking : bool) ctx vs args1 args2 consts1 consts2 e1 e2 = +and unify_inductive (checking : scope_level option) ctx vs args1 args2 consts1 consts2 e1 e2 = let unif_formals ctx vs args1 args2 = if not (List.length args1 == List.length args2) then (ctx, vs, [(CKimpossible, ctx, e1, e2)])
View it on GitLab: https://gitlab.com/monnier/typer/-/commit/5483f630fbe10b950d7be014d875e14f3d...
Afficher les réponses par date
- if not (is_typeclass t' ctx) then None else
match check_typeclass_match t t' lctx sl with
| Impossible -> None
(* | Possible -> None *)
| (Possible | Match) -> Some (i, elem, e, t') in
IIRC currently unification can have side-effects (instantiation of metavars) even if it fails, so we may need to be careful to undo them if we end up not using that unification's result.
Stefan
Yes, the unification done by `check_typeclass_match` might instantiate some metavariables, but I've changed it so that it only instantiates the metavariables that we create during the instance search (those in the current scope level). Since those new metavars can only appear in the candidate instance, and nowhere else, the side-effects are not "visible" outside of the instance search and we do not need to worry about undoing anything.
Jean-Alexandre ________________________________ From: Typer typer-bounces@IRO.UMontreal.CA on behalf of Stefan Monnier monnier@IRO.UMontreal.CA Sent: Tuesday, June 9, 2020 2:02 PM To: Jean-Alexandre Barszcz via Typer typer@IRO.UMontreal.CA Subject: Re: [Typer] [Git][monnier/typer][ja-barszcz] WIP WIP WIP
- if not (is_typeclass t' ctx) then None else
match check_typeclass_match t t' lctx sl with
| Impossible -> None
(* | Possible -> None *)
| (Possible | Match) -> Some (i, elem, e, t') in
IIRC currently unification can have side-effects (instantiation of metavars) even if it fails, so we may need to be careful to undo them if we end up not using that unification's result.
Stefan
_______________________________________________ Typer mailing list Typer@iro.umontreal.ca https://mailman.iro.umontreal.ca/cgi-bin/mailman/listinfo/typer
Yes, the unification done by `check_typeclass_match` might instantiate some metavariables, but I've changed it so that it only instantiates the metavariables that we create during the instance search (those in the current scope level). Since those new metavars can only appear in the candidate instance, and nowhere else, the side-effects are not "visible" outside of the instance search and we do not need to worry about undoing anything.
Great! Please mention this in a comment in the code.
Stefan