Merge pull request #9216 from gasche/lambda-duplicate

extend Lambda.subst on bound variables, add Lambda.duplicate
master
Gabriel Scherer 2020-07-02 22:55:49 +02:00 committed by GitHub
commit 50806ced7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 119 additions and 59 deletions

View File

@ -183,6 +183,9 @@ Working version
### Internal/compiler-libs changes:
- #9216: add Lambda.duplicate which refreshes bound identifiers
(Gabriel Scherer, review by Pierre Chambart and Vincent Laviron)
- #9493, #9520, #9563, #9599, #9608: refactor the pattern-matching compiler
(Thomas Refis and Gabriel Scherer, review by Florian Angeletti)

View File

@ -684,79 +684,120 @@ let rec make_sequence fn = function
Assumes that the image of the substitution is out of reach
of the bound variables of the lambda-term (no capture). *)
let subst update_env s lam =
let rec subst s lam =
let remove_list l s =
List.fold_left (fun s (id, _kind) -> Ident.Map.remove id s) s l
in
let subst update_env ?(freshen_bound_variables = false) s input_lam =
(* [s] contains a partial substitution for the free variables of the
input term [input_lam].
During our traversal of the term we maintain a second environment
[l] with all the bound variables of [input_lam] in the current
scope, mapped to either themselves or freshened versions of
themselves when [freshen_bound_variables] is set. *)
let bind id l =
let id' = if not freshen_bound_variables then id else Ident.rename id in
id', Ident.Map.add id id' l
in
let bind_many ids l =
List.fold_right (fun (id, rhs) (ids', l) ->
let id', l = bind id l in
((id', rhs) :: ids' , l)
) ids ([], l)
in
let rec subst s l lam =
match lam with
| Lvar id as l ->
begin try Ident.Map.find id s with Not_found -> l end
| Lvar id as lam ->
begin match Ident.Map.find id l with
| id' -> Lvar id'
| exception Not_found ->
(* note: as this point we know [id] is not a bound
variable of the input term, otherwise it would belong
to [l]; it is a free variable of the input term. *)
begin try Ident.Map.find id s with Not_found -> lam end
end
| Lconst _ as l -> l
| Lapply ap ->
Lapply{ap with ap_func = subst s ap.ap_func;
ap_args = subst_list s ap.ap_args}
Lapply{ap with ap_func = subst s l ap.ap_func;
ap_args = subst_list s l ap.ap_args}
| Lfunction lf ->
let s =
List.fold_right
(fun (id, _) s -> Ident.Map.remove id s)
lf.params s
in
Lfunction {lf with body = subst s lf.body}
let params, l' = bind_many lf.params l in
Lfunction {lf with params; body = subst s l' lf.body}
| Llet(str, k, id, arg, body) ->
Llet(str, k, id, subst s arg, subst (Ident.Map.remove id s) body)
let id, l' = bind id l in
Llet(str, k, id, subst s l arg, subst s l' body)
| Lletrec(decl, body) ->
let s =
List.fold_left (fun s (id, _) -> Ident.Map.remove id s)
s decl
in
Lletrec(List.map (subst_decl s) decl, subst s body)
| Lprim(p, args, loc) -> Lprim(p, subst_list s args, loc)
let decl, l' = bind_many decl l in
Lletrec(List.map (subst_decl s l') decl, subst s l' body)
| Lprim(p, args, loc) -> Lprim(p, subst_list s l args, loc)
| Lswitch(arg, sw, loc) ->
Lswitch(subst s arg,
{sw with sw_consts = List.map (subst_case s) sw.sw_consts;
sw_blocks = List.map (subst_case s) sw.sw_blocks;
sw_failaction = subst_opt s sw.sw_failaction; },
Lswitch(subst s l arg,
{sw with sw_consts = List.map (subst_case s l) sw.sw_consts;
sw_blocks = List.map (subst_case s l) sw.sw_blocks;
sw_failaction = subst_opt s l sw.sw_failaction; },
loc)
| Lstringswitch (arg,cases,default,loc) ->
Lstringswitch
(subst s arg,List.map (subst_strcase s) cases,subst_opt s default,loc)
| Lstaticraise (i,args) -> Lstaticraise (i, subst_list s args)
(subst s l arg,
List.map (subst_strcase s l) cases,
subst_opt s l default,
loc)
| Lstaticraise (i,args) -> Lstaticraise (i, subst_list s l args)
| Lstaticcatch(body, (id, params), handler) ->
Lstaticcatch(subst s body, (id, params),
subst (remove_list params s) handler)
let params, l' = bind_many params l in
Lstaticcatch(subst s l body, (id, params),
subst s l' handler)
| Ltrywith(body, exn, handler) ->
Ltrywith(subst s body, exn, subst (Ident.Map.remove exn s) handler)
| Lifthenelse(e1, e2, e3) -> Lifthenelse(subst s e1, subst s e2, subst s e3)
| Lsequence(e1, e2) -> Lsequence(subst s e1, subst s e2)
| Lwhile(e1, e2) -> Lwhile(subst s e1, subst s e2)
let exn, l' = bind exn l in
Ltrywith(subst s l body, exn, subst s l' handler)
| Lifthenelse(e1, e2, e3) ->
Lifthenelse(subst s l e1, subst s l e2, subst s l e3)
| Lsequence(e1, e2) -> Lsequence(subst s l e1, subst s l e2)
| Lwhile(e1, e2) -> Lwhile(subst s l e1, subst s l e2)
| Lfor(v, lo, hi, dir, body) ->
Lfor(v, subst s lo, subst s hi, dir,
subst (Ident.Map.remove v s) body)
let v, l' = bind v l in
Lfor(v, subst s l lo, subst s l hi, dir, subst s l' body)
| Lassign(id, e) ->
assert(not (Ident.Map.mem id s));
Lassign(id, subst s e)
assert (not (Ident.Map.mem id s));
let id = try Ident.Map.find id l with Not_found -> id in
Lassign(id, subst s l e)
| Lsend (k, met, obj, args, loc) ->
Lsend (k, subst s met, subst s obj, subst_list s args, loc)
Lsend (k, subst s l met, subst s l obj, subst_list s l args, loc)
| Levent (lam, evt) ->
let lev_env =
Ident.Map.fold (fun id _ env ->
match Env.find_value (Path.Pident id) evt.lev_env with
| exception Not_found -> env
| vd -> update_env id vd env
) s evt.lev_env
let old_env = evt.lev_env in
let env_updates =
let find_in_old id = Env.find_value (Path.Pident id) old_env in
let rebind id id' new_env =
match find_in_old id with
| exception Not_found -> new_env
| vd -> Env.add_value id' vd new_env
in
let update_free id new_env =
match find_in_old id with
| exception Not_found -> new_env
| vd -> update_env id vd new_env
in
Ident.Map.merge (fun id bound free ->
match bound, free with
| Some id', _ ->
if Ident.equal id id' then None else Some (rebind id id')
| None, Some _ -> Some (update_free id)
| None, None -> None
) l s
in
Levent (subst s lam, { evt with lev_env })
| Lifused (v, e) -> Lifused (v, subst s e)
and subst_list s l = List.map (subst s) l
and subst_decl s (id, exp) = (id, subst s exp)
and subst_case s (key, case) = (key, subst s case)
and subst_strcase s (key, case) = (key, subst s case)
and subst_opt s = function
let new_env =
Ident.Map.fold (fun _id update env -> update env) env_updates old_env
in
Levent (subst s l lam, { evt with lev_env = new_env })
| Lifused (id, e) ->
let id = try Ident.Map.find id l with Not_found -> id in
Lifused (id, subst s l e)
and subst_list s l li = List.map (subst s l) li
and subst_decl s l (id, exp) = (id, subst s l exp)
and subst_case s l (key, case) = (key, subst s l case)
and subst_strcase s l (key, case) = (key, subst s l case)
and subst_opt s l = function
| None -> None
| Some e -> Some (subst s e)
| Some e -> Some (subst s l e)
in
subst s lam
subst s Ident.Map.empty input_lam
let rename idmap lam =
let update_env oldid vd env =
@ -766,6 +807,13 @@ let rename idmap lam =
let s = Ident.Map.map (fun new_id -> Lvar new_id) idmap in
subst update_env s lam
let duplicate lam =
subst
(fun _ _ env -> env)
~freshen_bound_variables:true
Ident.Map.empty
lam
let shallow_map f = function
| Lvar _
| Lconst _ as lam -> lam

View File

@ -379,21 +379,30 @@ val transl_class_path: scoped_location -> Env.t -> Path.t -> lambda
val make_sequence: ('a -> lambda) -> 'a list -> lambda
val subst: (Ident.t -> Types.value_description -> Env.t -> Env.t) ->
val subst:
(Ident.t -> Types.value_description -> Env.t -> Env.t) ->
?freshen_bound_variables:bool ->
lambda Ident.Map.t -> lambda -> lambda
(** [subst env_update_fun s lt] applies a substitution [s] to the lambda-term
[lt].
(** [subst update_env ?freshen_bound_variables s lt]
applies a substitution [s] to the lambda-term [lt].
Assumes that the image of the substitution is out of reach
of the bound variables of the lambda-term (no capture).
[env_update_fun] is used to refresh the environment contained in debug
events. *)
[update_env] is used to refresh the environment contained in debug
events.
[freshen_bound_variables], which defaults to [false], freshens
the bound variables within [lt].
*)
val rename : Ident.t Ident.Map.t -> lambda -> lambda
(** A version of [subst] specialized for the case where we're just renaming
idents. *)
val duplicate : lambda -> lambda
(** Duplicate a term, freshening all locally-bound identifiers. *)
val map : (lambda -> lambda) -> lambda -> lambda
(** Bottom-up rewriting, applying the function on
each node from the leaves to the root. *)