Testing and debugging closure conversion (#637)

This commit is contained in:
Louis Gesbert 2024-06-21 17:57:02 +02:00 committed by GitHub
commit 8a6206363e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 362 additions and 230 deletions

View File

@ -299,7 +299,8 @@ BRANCH = $(shell git branch --show-current 2>/dev/null || echo master)
# its usage. # its usage.
local_tmp_clone = { \ local_tmp_clone = { \
rm -rf $1.tmp && \ rm -rf $1.tmp && \
trap "rm -rf $1.tmp" EXIT && \ CLEANUP_TMP_GIT_CLONES="$${CLEANUP_TMP_GIT_CLONES}rm -rf $1.tmp; " && \
trap "$$CLEANUP_TMP_GIT_CLONES" EXIT && \
git clone https://github.com/CatalaLang/$1 \ git clone https://github.com/CatalaLang/$1 \
--depth 1 --reference-if-able ../$1 \ --depth 1 --reference-if-able ../$1 \
$1.tmp -b $(BRANCH) || \ $1.tmp -b $(BRANCH) || \
@ -336,8 +337,12 @@ alltest: dependencies-python
bench_ocaml \ bench_ocaml \
bench_js \ bench_js \
bench_python && \ bench_python && \
printf "\n# \e[42;30m[ ALL TESTS PASSED ]\e[m \e[32m☺\e[m\n" || \ printf "\n# Full Catala testsuite:\t\t\e[42;30m ALL TESTS PASSED \e[m\t\t\e[32m☺\e[m\n" || \
{ printf "\n# \e[41;30m[ TESTS FAILED ]\e[m \e[31m☹\e[m\n" ; exit 1; } { printf "\n# Full Catala testsuite:\t\t\e[41;30m TESTS FAILED \e[m\t\t\e[31m☹\e[m\n" ; exit 1; }
#> alltest- : Like 'alltest', but skips doc building and is much faster
alltest-:
@$(MAKE) alltest NODOC=1
#> clean : Clean build artifacts #> clean : Clean build artifacts
clean: clean:

View File

@ -488,7 +488,9 @@ let base_bindings catala_exe catala_flags build_dir include_dirs test_flags =
let catala_flags_ocaml = let catala_flags_ocaml =
List.filter List.filter
(function (function
| "--avoid-exceptions" | "-O" | "--optimize" -> true | _ -> false) | "--avoid-exceptions" | "-O" | "--optimize" | "--closure-conversion" ->
true
| _ -> false)
test_flags test_flags
in in
let catala_flags_python = let catala_flags_python =

View File

@ -111,21 +111,9 @@ let get_diff p1 p2 =
File.process_out ~check_exit:(fun _ -> ()) cmd (args @ [f1; f2]) File.process_out ~check_exit:(fun _ -> ()) cmd (args @ [f1; f2])
let catala_commands_with_output_flag = let catala_commands_with_output_flag =
[ ["makefile"; "html"; "latex"; "ocaml"; "python"; "r"; "c"]
"makefile";
"html";
"latex";
"scopelang";
"dcalc";
"lcalc";
"ocaml";
"scalc";
"python";
"r";
"c";
]
let display ~build_dir ppf t = let display ~build_dir file ppf t =
let pfile f = let pfile f =
f f
|> String.remove_prefix ~prefix:(build_dir ^ Filename.dir_sep) |> String.remove_prefix ~prefix:(build_dir ^ Filename.dir_sep)
@ -135,6 +123,10 @@ let display ~build_dir ppf t =
List.filter_map List.filter_map
(fun s -> if s = "--directory=" ^ build_dir then None else Some (pfile s)) (fun s -> if s = "--directory=" ^ build_dir then None else Some (pfile s))
t.command_line t.command_line
|> (function
| catala :: cmd :: args ->
catala :: cmd :: "-I" :: Filename.dirname file :: args
| cl -> cl)
|> function |> function
| catala :: cmd :: args | catala :: cmd :: args
when List.mem when List.mem
@ -180,7 +172,7 @@ let display_file ~build_dir ppf t =
in in
Format.pp_print_break ppf 0 3; Format.pp_print_break ppf 0 3;
Format.pp_open_vbox ppf 0; Format.pp_open_vbox ppf 0;
Format.pp_print_list (display ~build_dir) ppf tests; Format.pp_print_list (display ~build_dir t.name) ppf tests;
Format.pp_close_box ppf () Format.pp_close_box ppf ()
in in
if t.successful = t.total then ( if t.successful = t.total then (

View File

@ -34,7 +34,7 @@ type file = { name : File.t; successful : int; total : int; tests : test list }
val write_to : File.t -> file -> unit val write_to : File.t -> file -> unit
val read_from : File.t -> file val read_from : File.t -> file
val read_many : File.t -> file list val read_many : File.t -> file list
val display : build_dir:File.t -> Format.formatter -> test -> unit val display : build_dir:File.t -> File.t -> Format.formatter -> test -> unit
val summary : build_dir:File.t -> file list -> bool val summary : build_dir:File.t -> file list -> bool
(** Displays a summary to stdout; returns true if all tests succeeded *) (** Displays a summary to stdout; returns true if all tests succeeded *)

View File

@ -76,7 +76,7 @@ let catala_test_command test_flags catala_exe catala_opts args out =
let cmd0, flags = let cmd0, flags =
match String.lowercase_ascii cmd0, flags, test_flags with match String.lowercase_ascii cmd0, flags, test_flags with
| "test-scope", scope_name :: flags, test_flags -> | "test-scope", scope_name :: flags, test_flags ->
"interpret", (("--scope=" ^ scope_name) :: flags) @ test_flags "interpret", flags @ test_flags @ ["--scope=" ^ scope_name]
| "test-scope", [], _ -> | "test-scope", [], _ ->
out_line out out_line out
"[INVALID TEST] Invalid test command syntax, the 'test-scope' \ "[INVALID TEST] Invalid test command syntax, the 'test-scope' \
@ -126,7 +126,7 @@ let run_catala_test filename cmd program expected out_line =
out_line result_line; out_line result_line;
match Seq.uncons expected with match Seq.uncons expected with
| Some (l, expected) -> success && String.equal result_line l, expected | Some (l, expected) -> success && String.equal result_line l, expected
| None -> false, expected) | None -> false, Seq.empty)
(true, expected) out_lines (true, expected) out_lines
in in
let return_code = let return_code =
@ -142,7 +142,7 @@ let run_catala_test filename cmd program expected out_line =
match Seq.uncons expected with match Seq.uncons expected with
| Some (l, expected) when String.equal l line -> success, expected | Some (l, expected) when String.equal l line -> success, expected
| Some (_, expected) -> false, expected | Some (_, expected) -> false, expected
| None -> false, expected | None -> false, Seq.empty
in in
success && Seq.is_empty expected success && Seq.is_empty expected
@ -171,7 +171,7 @@ let run_tests ~catala_exe ~catala_opts ~test_flags ~report ~out filename =
| Some ((l, tok, _), lines) -> | Some ((l, tok, _), lines) ->
push_line l; push_line l;
if tok = L.LINE_BLOCK_END then lines else skip_block lines if tok = L.LINE_BLOCK_END then lines else skip_block lines
| None -> lines | None -> Seq.empty
in in
let rec get_block acc lines = let rec get_block acc lines =
let return lines acc = let return lines acc =
@ -189,7 +189,7 @@ let run_tests ~catala_exe ~catala_opts ~test_flags ~report ~out filename =
lines, block, (startpos, endpos) lines, block, (startpos, endpos)
in in
match Seq.uncons lines with match Seq.uncons lines with
| None -> return lines acc | None -> return Seq.empty acc
| Some ((_, L.LINE_BLOCK_END, _), lines) -> return lines acc | Some ((_, L.LINE_BLOCK_END, _), lines) -> return lines acc
| Some (li, lines) -> get_block (li :: acc) lines | Some (li, lines) -> get_block (li :: acc) lines
in in
@ -213,7 +213,7 @@ let run_tests ~catala_exe ~catala_opts ~test_flags ~report ~out filename =
"[INVALID TEST] Missing test command, use '$ catala <args>'\n" "[INVALID TEST] Missing test command, use '$ catala <args>'\n"
in in
rtests := t :: !rtests; rtests := t :: !rtests;
None, lines None, Seq.empty
| Some ((str, L.LINE_BLOCK_END, _), lines) -> | Some ((str, L.LINE_BLOCK_END, _), lines) ->
let t = let t =
broken_test broken_test

View File

@ -47,6 +47,7 @@ end = struct
type nonrec t = t type nonrec t = t
let pass k ~avoid_exceptions ~closure_conversion ~monomorphize_types = let pass k ~avoid_exceptions ~closure_conversion ~monomorphize_types =
let avoid_exceptions = avoid_exceptions || closure_conversion in
(* Should not affect the call convention or actual interfaces: include, (* Should not affect the call convention or actual interfaces: include,
optimize, check_invariants, typed *) optimize, check_invariants, typed *)
!(avoid_exceptions : bool) !(avoid_exceptions : bool)

View File

@ -202,15 +202,9 @@ module Passes = struct
in in
let (prg : ty Dcalc.Ast.program) = let (prg : ty Dcalc.Ast.program) =
match typed with match typed with
| Typed _ -> ( | Typed _ ->
Message.debug "Typechecking again..."; Message.debug "Typechecking again...";
try Typing.program prg Typing.program ~internal_check:true prg
with Message.CompilerError error_content ->
let bt = Printexc.get_raw_backtrace () in
Printexc.raise_with_backtrace
(Message.CompilerError
(Message.Content.to_internal_error error_content))
bt)
| Untyped _ -> prg | Untyped _ -> prg
| Custom _ -> assert false | Custom _ -> assert false
in in
@ -269,7 +263,7 @@ module Passes = struct
let prg = let prg =
if not closure_conversion then ( if not closure_conversion then (
Message.debug "Retyping lambda calculus..."; Message.debug "Retyping lambda calculus...";
Typing.program ~fail_on_any:false prg) Typing.program ~fail_on_any:false ~internal_check:true prg)
else ( else (
Message.debug "Performing closure conversion..."; Message.debug "Performing closure conversion...";
let prg = Lcalc.Closure_conversion.closure_conversion prg in let prg = Lcalc.Closure_conversion.closure_conversion prg in
@ -280,14 +274,17 @@ module Passes = struct
else prg else prg
in in
Message.debug "Retyping lambda calculus..."; Message.debug "Retyping lambda calculus...";
Typing.program ~fail_on_any:false prg) Typing.program ~fail_on_any:false ~internal_check:true prg)
in in
let prg, type_ordering = let prg, type_ordering =
if monomorphize_types then ( if monomorphize_types then (
Message.debug "Monomorphizing types..."; Message.debug "Monomorphizing types...";
let prg, type_ordering = Lcalc.Monomorphize.program prg in let prg, type_ordering = Lcalc.Monomorphize.program prg in
Message.debug "Retyping lambda calculus..."; Message.debug "Retyping lambda calculus...";
let prg = Typing.program ~fail_on_any:false ~assume_op_types:true prg in let prg =
Typing.program ~fail_on_any:false ~assume_op_types:true
~internal_check:true prg
in
prg, type_ordering) prg, type_ordering)
else prg, type_ordering else prg, type_ordering
in in
@ -837,10 +834,11 @@ module Commands = struct
optimize optimize
check_invariants check_invariants
avoid_exceptions avoid_exceptions
closure_conversion
ex_scope_opt = ex_scope_opt =
let prg, type_ordering = let prg, type_ordering =
Passes.lcalc options ~includes ~optimize ~check_invariants Passes.lcalc options ~includes ~optimize ~check_invariants
~avoid_exceptions ~typed:Expr.typed ~closure_conversion:false ~avoid_exceptions ~typed:Expr.typed ~closure_conversion
~monomorphize_types:false ~monomorphize_types:false
in in
let output_file, with_output = let output_file, with_output =
@ -853,7 +851,7 @@ module Commands = struct
(Option.value ~default:"stdout" output_file); (Option.value ~default:"stdout" output_file);
let exec_scope = Option.map (get_scope_uid prg.decl_ctx) ex_scope_opt in let exec_scope = Option.map (get_scope_uid prg.decl_ctx) ex_scope_opt in
let hashf = let hashf =
Hash.finalise ~avoid_exceptions ~closure_conversion:false Hash.finalise ~avoid_exceptions ~closure_conversion
~monomorphize_types:false ~monomorphize_types:false
in in
Lcalc.To_ocaml.format_program fmt prg ?exec_scope ~hashf type_ordering Lcalc.To_ocaml.format_program fmt prg ?exec_scope ~hashf type_ordering
@ -870,6 +868,7 @@ module Commands = struct
$ Cli.Flags.optimize $ Cli.Flags.optimize
$ Cli.Flags.check_invariants $ Cli.Flags.check_invariants
$ Cli.Flags.avoid_exceptions $ Cli.Flags.avoid_exceptions
$ Cli.Flags.closure_conversion
$ Cli.Flags.ex_scope_opt) $ Cli.Flags.ex_scope_opt)
let scalc let scalc

View File

@ -19,11 +19,20 @@ open Shared_ast
open Ast open Ast
module D = Dcalc.Ast module D = Dcalc.Ast
type name_context = { prefix : string; mutable counter : int }
type 'm ctx = { type 'm ctx = {
name_context : string; decl_ctx : decl_ctx;
name_context : name_context;
globally_bound_vars : ('m expr, typ) Var.Map.t; globally_bound_vars : ('m expr, typ) Var.Map.t;
} }
let new_var ?(pfx = "") name_context =
name_context.counter <- name_context.counter + 1;
Var.make (pfx ^ name_context.prefix ^ string_of_int name_context.counter)
let new_context prefix = { prefix; counter = 0 }
(** Function types will be transformed in this way throughout, including in (** Function types will be transformed in this way throughout, including in
[decl_ctx] *) [decl_ctx] *)
let rec translate_type t = let rec translate_type t =
@ -52,6 +61,76 @@ let join_vars : ('a, 'x) Var.Map.t -> ('a, 'x) Var.Map.t -> ('a, 'x) Var.Map.t =
(** {1 Transforming closures}*) (** {1 Transforming closures}*)
let build_closure :
type m.
m ctx ->
(m expr Var.t * m mark) list ->
m expr boxed ->
m expr Var.t array ->
typ list ->
m mark ->
m expr boxed =
fun ctx free_vars body args tys m ->
(* λ x.t *)
let pos = Expr.mark_pos m in
let mark_ty ty = Expr.with_ty m ty in
let free_vars_types = List.map (fun (_, m) -> Expr.maybe_ty m) free_vars in
(* x1, ..., xn *)
let code_var = new_var ctx.name_context in
(* code *)
let closure_env_arg_var = Var.make "env" in
let closure_env_var = Var.make "env" in
let env_ty = TTuple free_vars_types, pos in
(* let env = from_closure_env env in let arg0 = env.0 in ... *)
let new_closure_body =
Expr.make_let_in closure_env_var env_ty
(Expr.eappop
~op:(Operator.FromClosureEnv, pos)
~tys:[TClosureEnv, pos]
~args:[Expr.evar closure_env_arg_var (mark_ty (TClosureEnv, pos))]
(mark_ty env_ty))
(Expr.make_multiple_let_in
(Array.of_list (List.map fst free_vars))
free_vars_types
(List.mapi
(fun i _ ->
Expr.make_tupleaccess
(Expr.evar closure_env_var (mark_ty env_ty))
i (List.length free_vars) pos)
free_vars)
body pos)
pos
in
(* fun env arg0 ... -> new_closure_body *)
let new_closure =
Expr.make_abs
(Array.append [| closure_env_arg_var |] args)
new_closure_body
((TClosureEnv, pos) :: tys)
pos
in
let new_closure_ty = Expr.maybe_ty (Mark.get new_closure) in
Expr.make_let_in code_var new_closure_ty new_closure
(Expr.make_tuple
((Bindlib.box_var code_var, mark_ty new_closure_ty)
:: [
Expr.eappop
~op:(Operator.ToClosureEnv, pos)
~tys:[TTuple free_vars_types, pos]
~args:
[
Expr.etuple
(List.map
(fun (extra_var, m) ->
Bindlib.box_var extra_var, Expr.with_pos pos m)
free_vars)
(mark_ty (TTuple free_vars_types, pos));
]
(mark_ty (TClosureEnv, pos));
])
m)
pos
(** Returns the expression with closed closures and the set of free variables (** Returns the expression with closed closures and the set of free variables
inside this new expression. Implementation guided by inside this new expression. Implementation guided by
http://gallium.inria.fr/~fpottier/mpri/cours04.pdf#page=10 http://gallium.inria.fr/~fpottier/mpri/cours04.pdf#page=10
@ -63,44 +142,60 @@ let rec transform_closures_expr :
let m = Mark.get e in let m = Mark.get e in
match Mark.remove e with match Mark.remove e with
| EStruct _ | EStructAccess _ | ETuple _ | ETupleAccess _ | EInj _ | EArray _ | EStruct _ | EStructAccess _ | ETuple _ | ETupleAccess _ | EInj _ | EArray _
| ELit _ | EExternal _ | EAssert _ | EFatalError _ | EIfThenElse _ | ELit _ | EAssert _ | EFatalError _ | EIfThenElse _ | ERaiseEmpty
| ERaiseEmpty | ECatchEmpty _ -> | ECatchEmpty _ ->
Expr.map_gather ~acc:Var.Map.empty ~join:join_vars Expr.map_gather ~acc:Var.Map.empty ~join:join_vars
~f:(transform_closures_expr ctx) ~f:(transform_closures_expr ctx)
e e
| EVar v -> ( | (EVar _ | EExternal _) as e -> (
match Var.Map.find_opt v ctx.globally_bound_vars with let body, (free_vars, fty) =
| None -> Var.Map.singleton v m, (Bindlib.box_var v, m) match e with
| Some (TArrow (targs, tret), _) -> | EVar v -> (
( Bindlib.box_var v,
match Var.Map.find_opt v ctx.globally_bound_vars with
| None -> Var.Map.singleton v m, None
| Some ((TArrow (targs, tret), _) as fty) ->
Var.Map.empty, Some (targs, tret, fty)
| Some _ -> Var.Map.empty, None ))
| EExternal { name = External_value td, _ } as e ->
( Bindlib.box e,
( Var.Map.empty,
match TopdefName.Map.find td ctx.decl_ctx.ctx_topdefs with
| (TArrow (targs, tret), _) as fty -> Some (targs, tret, fty)
| _ -> None ) )
| EExternal { name = External_scope s, pos } ->
let fty =
let si = ScopeName.Map.find s ctx.decl_ctx.ctx_scopes in
let t_in = TStruct si.in_struct_name, pos in
let t_out = TStruct si.out_struct_name, pos in
[t_in], t_out, (TArrow ([t_in], t_out), pos)
in
Bindlib.box e, (Var.Map.empty, Some fty)
| _ -> assert false
in
match fty with
| None -> free_vars, (body, m)
| Some (targs, tret, fty) ->
(* Here we eta-expand the argument to make sure function pointers are (* Here we eta-expand the argument to make sure function pointers are
correctly casted as closures *) correctly casted as closures *)
let args = Array.init (List.length targs) (fun _ -> Var.make "eta_arg") in let args =
Array.init (List.length targs) (fun i ->
Var.make ("x" ^ string_of_int i))
in
let arg_vars = let arg_vars =
List.map2 List.map2
(fun v ty -> Expr.evar v (Expr.with_ty m ty)) (fun v ty -> Expr.evar v (Expr.with_ty m ty))
(Array.to_list args) targs (Array.to_list args) targs
in in
let e = let closure =
Expr.eabs let body =
(Expr.bind args Expr.eapp
(Expr.eapp ~f:(Expr.rebox e) ~args:arg_vars ~tys:targs ~f:(body, Expr.with_ty m fty)
(Expr.with_ty m tret))) ~args:arg_vars ~tys:targs (Expr.with_ty m tret)
targs m
in
let boxed =
let ctx =
(* We hide the type of the toplevel definition so that the function
doesn't loop *)
{
ctx with
globally_bound_vars =
Var.Map.add v (Expr.maybe_ty m) ctx.globally_bound_vars;
}
in in
Bindlib.box_apply (transform_closures_expr ctx) (Expr.Box.lift e) build_closure ctx [] body args targs m
in in
Bindlib.unbox boxed Var.Map.empty, closure)
| Some _ -> Var.Map.empty, (Bindlib.box_var v, m))
| EMatch { e; cases; name } -> | EMatch { e; cases; name } ->
let free_vars, new_e = (transform_closures_expr ctx) e in let free_vars, new_e = (transform_closures_expr ctx) e in
(* We do not close the clotures inside the arms of the match expression, (* We do not close the clotures inside the arms of the match expression,
@ -147,92 +242,18 @@ let rec transform_closures_expr :
~f:(Expr.eabs new_binder (List.map translate_type tys) e1_pos) ~f:(Expr.eabs new_binder (List.map translate_type tys) e1_pos)
~args:new_args ~tys m ) ~args:new_args ~tys m )
| EAbs { binder; tys } -> | EAbs { binder; tys } ->
(* λ x.t *)
let binder_pos = Expr.mark_pos m in
let mark_ty ty = Expr.with_ty m ty in
(* Converting the closure. *) (* Converting the closure. *)
let vars, body = Bindlib.unmbind binder in let vars, body = Bindlib.unmbind binder in
(* t *) (* t *)
let body_vars, new_body = (transform_closures_expr ctx) body in let free_vars, body = (transform_closures_expr ctx) body in
(* [[t]] *) (* [[t]] *)
let extra_vars = let free_vars =
Array.fold_left (fun m v -> Var.Map.remove v m) body_vars vars Array.fold_left (fun m v -> Var.Map.remove v m) free_vars vars
in in
let extra_vars_list = Var.Map.bindings extra_vars in free_vars, build_closure ctx (Var.Map.bindings free_vars) body vars tys m
let extra_vars_types =
List.map (fun (_, m) -> Expr.maybe_ty m) extra_vars_list
in
(* x1, ..., xn *)
let code_var = Var.make ctx.name_context in
(* code *)
let closure_env_arg_var = Var.make "env" in
let closure_env_var = Var.make "env" in
let env_ty = TTuple extra_vars_types, binder_pos in
(* let env = from_closure_env env in let arg0 = env.0 in ... *)
let new_closure_body =
Expr.make_let_in closure_env_var env_ty
(Expr.eappop
~op:(Operator.FromClosureEnv, binder_pos)
~tys:[TClosureEnv, binder_pos]
~args:
[Expr.evar closure_env_arg_var (mark_ty (TClosureEnv, binder_pos))]
(mark_ty env_ty))
(Expr.make_multiple_let_in
(Array.of_list (List.map fst extra_vars_list))
extra_vars_types
(List.mapi
(fun i _ ->
Expr.make_tupleaccess
(Expr.evar closure_env_var (mark_ty env_ty))
i
(List.length extra_vars_list)
binder_pos)
extra_vars_list)
new_body binder_pos)
binder_pos
in
(* fun env arg0 ... -> new_closure_body *)
let new_closure =
Expr.make_abs
(Array.append [| closure_env_arg_var |] vars)
new_closure_body
((TClosureEnv, binder_pos) :: tys)
(Expr.pos e)
in
let new_closure_ty = Expr.maybe_ty (Mark.get new_closure) in
( extra_vars,
Expr.make_let_in code_var new_closure_ty new_closure
(Expr.make_tuple
((Bindlib.box_var code_var, mark_ty new_closure_ty)
:: [
Expr.eappop
~op:(Operator.ToClosureEnv, binder_pos)
~tys:
[
( (if extra_vars_list = [] then TLit TUnit
else TTuple extra_vars_types),
binder_pos );
]
~args:
[
(if extra_vars_list = [] then
Expr.elit LUnit (mark_ty (TLit TUnit, binder_pos))
else
Expr.etuple
(List.map
(fun (extra_var, m) ->
( Bindlib.box_var extra_var,
Expr.with_pos binder_pos m ))
extra_vars_list)
(mark_ty (TTuple extra_vars_types, binder_pos)));
]
(mark_ty (TClosureEnv, binder_pos));
])
m)
(Expr.pos e) )
| EAppOp | EAppOp
{ {
op = ((HandleDefaultOpt | Fold | Map | Filter | Reduce), _) as op; op = ((HandleDefaultOpt | Fold | Map | Map2 | Filter | Reduce), _) as op;
tys; tys;
args; args;
} -> } ->
@ -318,10 +339,7 @@ let rec transform_closures_expr :
pos) pos)
pos pos
in in
( free_vars, free_vars, Expr.make_let_in code_env_var (TAny, pos) new_e1 call_expr pos
Expr.make_let_in code_env_var
(TAny, Expr.pos e)
new_e1 call_expr (Expr.pos e) )
| _ -> . | _ -> .
let transform_closures_scope_let ctx scope_body_expr = let transform_closures_scope_let ctx scope_body_expr =
@ -329,7 +347,7 @@ let transform_closures_scope_let ctx scope_body_expr =
~f:(fun var_next scope_let -> ~f:(fun var_next scope_let ->
let _free_vars, new_scope_let_expr = let _free_vars, new_scope_let_expr =
(transform_closures_expr (transform_closures_expr
{ ctx with name_context = Bindlib.name_of var_next }) { ctx with name_context = new_context (Bindlib.name_of var_next) })
scope_let.scope_let_expr scope_let.scope_let_expr
in in
( var_next, ( var_next,
@ -360,7 +378,8 @@ let transform_closures_program (p : 'm program) : 'm program Bindlib.box =
in in
let ctx = let ctx =
{ {
name_context = Mark.remove (ScopeName.get_info name); decl_ctx = p.decl_ctx;
name_context = new_context (Mark.remove (ScopeName.get_info name));
globally_bound_vars = toplevel_vars; globally_bound_vars = toplevel_vars;
} }
in in
@ -387,7 +406,9 @@ let transform_closures_program (p : 'm program) : 'm program Bindlib.box =
let v, expr = Bindlib.unmbind binder in let v, expr = Bindlib.unmbind binder in
let ctx = let ctx =
{ {
name_context = Mark.remove (TopdefName.get_info name); decl_ctx = p.decl_ctx;
name_context =
new_context (Mark.remove (TopdefName.get_info name));
globally_bound_vars = toplevel_vars; globally_bound_vars = toplevel_vars;
} }
in in
@ -401,7 +422,9 @@ let transform_closures_program (p : 'm program) : 'm program Bindlib.box =
| Topdef (name, ty, expr) -> | Topdef (name, ty, expr) ->
let ctx = let ctx =
{ {
name_context = Mark.remove (TopdefName.get_info name); decl_ctx = p.decl_ctx;
name_context =
new_context (Mark.remove (TopdefName.get_info name));
globally_bound_vars = toplevel_vars; globally_bound_vars = toplevel_vars;
} }
in in
@ -462,7 +485,7 @@ type 'm hoisted_closure = {
} }
let rec hoist_closures_expr : let rec hoist_closures_expr :
type m. string -> m expr -> m hoisted_closure list * m expr boxed = type m. name_context -> m expr -> m hoisted_closure list * m expr boxed =
fun name_context e -> fun name_context e ->
let m = Mark.get e in let m = Mark.get e in
match Mark.remove e with match Mark.remove e with
@ -541,7 +564,7 @@ let rec hoist_closures_expr :
collected_closures, Expr.eappop ~op ~args:new_args ~tys (Mark.get e) collected_closures, Expr.eappop ~op ~args:new_args ~tys (Mark.get e)
| EAbs { tys; _ } -> | EAbs { tys; _ } ->
(* this is the closure we want to hoist *) (* this is the closure we want to hoist *)
let closure_var = Var.make ("closure_" ^ name_context) in let closure_var = new_var ~pfx:"closure_" name_context in
(* TODO: This will end up as a toplevel name. However for now we assume (* TODO: This will end up as a toplevel name. However for now we assume
toplevel names are unique, but this breaks this assertions and can lead toplevel names are unique, but this breaks this assertions and can lead
to name wrangling in the backends. We need to have a better system for to name wrangling in the backends. We need to have a better system for
@ -555,14 +578,14 @@ let rec hoist_closures_expr :
| EArray _ | ELit _ | EAssert _ | EFatalError _ | EAppOp _ | EIfThenElse _ | EArray _ | ELit _ | EAssert _ | EFatalError _ | EAppOp _ | EIfThenElse _
| ERaiseEmpty | ECatchEmpty _ | EVar _ -> | ERaiseEmpty | ECatchEmpty _ | EVar _ ->
Expr.map_gather ~acc:[] ~join:( @ ) ~f:(hoist_closures_expr name_context) e Expr.map_gather ~acc:[] ~join:( @ ) ~f:(hoist_closures_expr name_context) e
| EExternal _ -> failwith "unimplemented" | EExternal { name } -> [], Expr.box (EExternal { name }, m)
| _ -> . | _ -> .
let hoist_closures_scope_let name_context scope_body_expr = let hoist_closures_scope_let name_context scope_body_expr =
BoundList.fold_right BoundList.fold_right
~f:(fun scope_let var_next (hoisted_closures, next_scope_lets) -> ~f:(fun scope_let var_next (hoisted_closures, next_scope_lets) ->
let new_hoisted_closures, new_scope_let_expr = let new_hoisted_closures, new_scope_let_expr =
(hoist_closures_expr (Bindlib.name_of var_next)) (hoist_closures_expr (new_context (Bindlib.name_of var_next)))
scope_let.scope_let_expr scope_let.scope_let_expr
in in
( new_hoisted_closures @ hoisted_closures, ( new_hoisted_closures @ hoisted_closures,
@ -599,7 +622,7 @@ let rec hoist_closures_code_item_list
in in
let new_hoisted_closures, new_scope_lets = let new_hoisted_closures, new_scope_lets =
hoist_closures_scope_let hoist_closures_scope_let
(fst (ScopeName.get_info name)) (new_context (fst (ScopeName.get_info name)))
scope_body_expr scope_body_expr
in in
let new_scope_body_expr = let new_scope_body_expr =
@ -613,7 +636,9 @@ let rec hoist_closures_code_item_list
| Topdef (name, ty, (EAbs { binder; tys }, m)) -> | Topdef (name, ty, (EAbs { binder; tys }, m)) ->
let v, expr = Bindlib.unmbind binder in let v, expr = Bindlib.unmbind binder in
let new_hoisted_closures, new_expr = let new_hoisted_closures, new_expr =
hoist_closures_expr (Mark.remove (TopdefName.get_info name)) expr hoist_closures_expr
(new_context (Mark.remove (TopdefName.get_info name)))
expr
in in
let new_binder = Expr.bind v new_expr in let new_binder = Expr.bind v new_expr in
( new_hoisted_closures, ( new_hoisted_closures,
@ -622,7 +647,9 @@ let rec hoist_closures_code_item_list
(Expr.Box.lift (Expr.eabs new_binder tys m)) ) (Expr.Box.lift (Expr.eabs new_binder tys m)) )
| Topdef (name, ty, expr) -> | Topdef (name, ty, expr) ->
let new_hoisted_closures, new_expr = let new_hoisted_closures, new_expr =
hoist_closures_expr (Mark.remove (TopdefName.get_info name)) expr hoist_closures_expr
(new_context (Mark.remove (TopdefName.get_info name)))
expr
in in
( new_hoisted_closures, ( new_hoisted_closures,
Bindlib.box_apply Bindlib.box_apply
@ -674,6 +701,4 @@ let hoist_closures_program (p : 'm program) : 'm program Bindlib.box =
let closure_conversion (p : 'm program) : 'm program = let closure_conversion (p : 'm program) : 'm program =
let new_p = transform_closures_program p in let new_p = transform_closures_program p in
let new_p = hoist_closures_program (Bindlib.unbox new_p) in let new_p = hoist_closures_program (Bindlib.unbox new_p) in
(* FIXME: either fix the types of the marks, or remove the types annotations
during the main processing (rather than requiring a new traversal) *)
Bindlib.unbox new_p Bindlib.unbox new_p

View File

@ -219,6 +219,7 @@ let rec format_typ (fmt : Format.formatter) (typ : typ) : unit =
in in
match Mark.remove typ with match Mark.remove typ with
| TLit l -> Format.fprintf fmt "%a" Print.tlit l | TLit l -> Format.fprintf fmt "%a" Print.tlit l
| TTuple [] -> Format.fprintf fmt "unit"
| TTuple ts -> | TTuple ts ->
Format.fprintf fmt "@[<hov 2>(%a)@]" Format.fprintf fmt "@[<hov 2>(%a)@]"
(Format.pp_print_list (Format.pp_print_list
@ -239,7 +240,7 @@ let rec format_typ (fmt : Format.formatter) (typ : typ) : unit =
(t1 @ [t2]) (t1 @ [t2])
| TArray t1 -> Format.fprintf fmt "@[%a@ array@]" format_typ_with_parens t1 | TArray t1 -> Format.fprintf fmt "@[%a@ array@]" format_typ_with_parens t1
| TAny -> Format.fprintf fmt "_" | TAny -> Format.fprintf fmt "_"
| TClosureEnv -> failwith "unimplemented!" | TClosureEnv -> Format.fprintf fmt "Obj.t"
let format_var_str (fmt : Format.formatter) (v : string) : unit = let format_var_str (fmt : Format.formatter) (v : string) : unit =
let lowercase_name = String.to_snake_case (String.to_ascii v) in let lowercase_name = String.to_snake_case (String.to_ascii v) in

View File

@ -223,11 +223,11 @@ type typ = naked_typ Mark.pos
and naked_typ = and naked_typ =
| TLit of typ_lit | TLit of typ_lit
| TArrow of typ list * typ
| TTuple of typ list | TTuple of typ list
| TStruct of StructName.t | TStruct of StructName.t
| TEnum of EnumName.t | TEnum of EnumName.t
| TOption of typ | TOption of typ
| TArrow of typ list * typ
| TArray of typ | TArray of typ
| TDefault of typ | TDefault of typ
| TAny | TAny

View File

@ -566,7 +566,11 @@ let rec runtime_to_val :
let e = runtime_to_val eval_expr ctx m ty (Obj.field o 0) in let e = runtime_to_val eval_expr ctx m ty (Obj.field o 0) in
EInj { name = Expr.option_enum; cons = Expr.some_constr; e }, m EInj { name = Expr.option_enum; cons = Expr.some_constr; e }, m
| _ -> assert false) | _ -> assert false)
| TClosureEnv -> assert false | TClosureEnv ->
(* By construction, a closure environment can only be consumed from the same
scope where it was built (compiled or not) ; for this reason, we can
safely avoid converting in depth here *)
Obj.obj o, m
| TArray ty -> | TArray ty ->
( EArray ( EArray
(List.map (List.map
@ -656,6 +660,11 @@ and val_to_runtime :
in in
curry [] targs curry [] targs
| TDefault ty, _ -> val_to_runtime eval_expr ctx ty v | TDefault ty, _ -> val_to_runtime eval_expr ctx ty v
| TClosureEnv, v ->
(* By construction, a closure environment can only be consumed from the same
scope where it was built (compiled or not) ; for this reason, we can
safely avoid converting in depth here *)
Obj.repr v
| _ -> | _ ->
Message.error ~internal:true Message.error ~internal:true
"Could not convert value of type %a@ to@ runtime:@ %a" (Print.typ ctx) ty "Could not convert value of type %a@ to@ runtime:@ %a" (Print.typ ctx) ty

View File

@ -102,7 +102,7 @@ let rec typ_gen
Format.pp_open_hvbox fmt 2; Format.pp_open_hvbox fmt 2;
pp_color_string (List.hd colors) fmt "("; pp_color_string (List.hd colors) fmt "(";
(Format.pp_print_list (Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt " %a@ " op_style "*") ~pp_sep:(fun fmt () -> Format.fprintf fmt "%a@ " op_style ",")
(typ ~colors:(List.tl colors))) (typ ~colors:(List.tl colors)))
fmt ts; fmt ts;
Format.pp_close_box fmt (); Format.pp_close_box fmt ();
@ -1113,6 +1113,8 @@ module UserFacing = struct
~pp_sep:(fun ppf () -> Format.fprintf ppf ";@ ") ~pp_sep:(fun ppf () -> Format.fprintf ppf ";@ ")
(value ~fallback lang)) (value ~fallback lang))
l l
| ETuple [(EAbs { tys = (TClosureEnv, _) :: _; _ }, _); _] ->
Format.pp_print_string ppf "<function>"
| ETuple l -> | ETuple l ->
Format.fprintf ppf "@[<hv 2>(@,@[<hov>%a@]@;<0 -2>)@]" Format.fprintf ppf "@[<hv 2>(@,@[<hov>%a@]@;<0 -2>)@]"
(Format.pp_print_list (Format.pp_print_list

View File

@ -1103,6 +1103,25 @@ let program ?fail_on_any ?assume_op_types prg =
}; };
} }
let program ?fail_on_any ?assume_op_types prg = let program ?fail_on_any ?assume_op_types ?(internal_check = false) prg =
Message.with_delayed_errors (fun () -> let wrap =
program ?fail_on_any ?assume_op_types prg) if internal_check then (fun f ->
try Message.with_delayed_errors f
with (Message.CompilerError _ | Message.CompilerErrors _) as exc ->
let bt = Printexc.get_raw_backtrace () in
let err =
match exc with
| Message.CompilerError err ->
Message.CompilerError (Message.Content.to_internal_error err)
| Message.CompilerErrors errs ->
Message.CompilerErrors
(List.map Message.Content.to_internal_error errs)
| _ -> assert false
in
Message.debug "Faulty intermediate program:@ %a"
(Print.program ~debug:true)
prg;
Printexc.raise_with_backtrace err bt)
else fun f -> Message.with_delayed_errors f
in
wrap @@ fun () -> program ?fail_on_any ?assume_op_types prg

View File

@ -97,11 +97,15 @@ val check_expr :
val program : val program :
?fail_on_any:bool -> ?fail_on_any:bool ->
?assume_op_types:bool -> ?assume_op_types:bool ->
?internal_check:bool ->
('a, 'm) gexpr program -> ('a, 'm) gexpr program ->
('a, typed) gexpr program ('a, typed) gexpr program
(** Typing on whole programs (as defined in Shared_ast.program, i.e. for the (** Typing on whole programs (as defined in Shared_ast.program, i.e. for the
later dcalc/lcalc stages. later dcalc/lcalc stages).
Any existing type annotations are checked for unification. Use Any existing type annotations are checked for unification. Use
[Program.untype] to remove them beforehand if this is not the desired [Program.untype] to remove them beforehand if this is not the desired
behaviour. *) behaviour.
If [internal_check] is set to [true], typing errors will be marked as
internal, and the faulty program will be printed if '--debug' is set. *)

View File

@ -884,6 +884,8 @@ module Oper = struct
let o_eq_dur_dur pos d1 d2 = equal_periods pos d1 d2 let o_eq_dur_dur pos d1 d2 = equal_periods pos d1 d2
let o_eq_dat_dat d1 d2 = Dates_calc.Dates.compare_dates d1 d2 = 0 let o_eq_dat_dat d1 d2 = Dates_calc.Dates.compare_dates d1 d2 = 0
let o_fold = Array.fold_left let o_fold = Array.fold_left
let o_toclosureenv = Obj.repr
let o_fromclosureenv = Obj.obj
end end
include Oper include Oper

View File

@ -432,6 +432,8 @@ module Oper : sig
val o_eq_dur_dur : source_position -> duration -> duration -> bool val o_eq_dur_dur : source_position -> duration -> duration -> bool
val o_eq_dat_dat : date -> date -> bool val o_eq_dat_dat : date -> date -> bool
val o_fold : ('a -> 'b -> 'a) -> 'a -> 'b array -> 'a val o_fold : ('a -> 'b -> 'a) -> 'a -> 'b array -> 'a
val o_toclosureenv : 'a -> Obj.t
val o_fromclosureenv : Obj.t -> 'a
end end
include module type of Oper include module type of Oper

View File

@ -76,14 +76,14 @@ let scope S (x: integer|internal|output) =
10. 10.
map (λ (i: integer) → to_rat i) [1; 2; 3]) map (λ (i: integer) → to_rat i) [1; 2; 3])
= 3.; = 3.;
assert (let weights : list of (integer * decimal) = assert (let weights : list of (integer, decimal) =
map (λ (i: integer) → map (λ (i: integer) →
(i, let i1 : integer = i in (i, let i1 : integer = i in
to_rat ((2 - i1) * (2 - i1)))) to_rat ((2 - i1) * (2 - i1))))
[1; 2; 3] [1; 2; 3]
in in
reduce reduce
(λ (x1: (integer * decimal)) (x2: (integer * decimal)) → (λ (x1: (integer, decimal)) (x2: (integer, decimal)) →
if x1.1 < x2.1 then x1 else x2) if x1.1 < x2.1 then x1 else x2)
let i : integer = 42 in let i : integer = 42 in
(i, let i1 : integer = i in (i, let i1 : integer = i in

View File

@ -29,15 +29,81 @@ type Eoption = | ENone of unit | ESome of any
type S_in = { x_in: bool; } type S_in = { x_in: bool; }
type S = { z: integer; } type S = { z: integer; }
let topval closure_f : (closure_env, integer) → integer = let topval closure_f1 : (closure_env, integer) → integer =
λ (env: closure_env) (y: integer) → λ (env: closure_env) (y: integer) →
if (from_closure_env env).0 then y else - y if (from_closure_env env).0 then y else - y
let scope S (S_in: S_in {x_in: bool}): S {z: integer} = let scope S (S_in: S_in {x_in: bool}): S {z: integer} =
let get x : bool = S_in.x_in in let get x : bool = S_in.x_in in
let set f : ((closure_env, integer) → integer * closure_env) = let set f : ((closure_env, integer) → integer, closure_env) =
(closure_f, to_closure_env (x)) (closure_f1, to_closure_env (x))
in in
let set z : integer = f.0 f.1 -1 in let set z : integer = f.0 f.1 -1 in
return { S z = z; } return { S z = z; }
``` ```
```catala
declaration scope S2:
output dummy content boolean
input output cfun2 content decimal depends on x content integer
scope S2:
definition dummy equals false
declaration scope S2Use:
internal fun content decimal depends on y content integer
output o content (S2, S2)
declaration fun2 content decimal depends on y content integer equals y / 3
scope S2Use:
definition fun of y equals y / 2
definition o equals
(output of S2 with { -- cfun2: fun },
output of S2 with { -- cfun2: fun2 })
```
```catala-test-inline
$ catala Lcalc --avoid-exceptions -O --closure-conversion -s S2Use
let scope S2Use
(S2Use_in: S2Use_in)
: S2Use {
o:
(S2 {
dummy: bool;
cfun2: ((closure_env, integer) → decimal, closure_env)
},
S2 {
dummy: bool;
cfun2: ((closure_env, integer) → decimal, closure_env)
})
}
=
let set fun : ((closure_env, integer) → decimal, closure_env) =
(closure_fun1, to_closure_env ())
in
let set o :
(S2 {
dummy: bool;
cfun2: ((closure_env, integer) → decimal, closure_env)
},
S2 {
dummy: bool;
cfun2: ((closure_env, integer) → decimal, closure_env)
}) =
(let result : S2 = S2 { S2_in cfun2_in = fun; } in
{ S2
dummy = result.dummy;
cfun2 = (closure_o1, to_closure_env (result));
},
let result : S2 =
S2 { S2_in cfun2_in = (closure_o3, to_closure_env ()); }
in
{ S2
dummy = result.dummy;
cfun2 = (closure_o2, to_closure_env (result));
})
in
return { S2Use o = o; }
```

View File

@ -29,7 +29,7 @@ let scope S (S_in: S_in {x_in: list of integer}): S {y: integer} =
let get x : list of integer = S_in.x_in in let get x : list of integer = S_in.x_in in
let set y : integer = let set y : integer =
(reduce (reduce
(λ (x1: (integer * integer)) (x2: (integer * integer)) → (λ (x1: (integer, integer)) (x2: (integer, integer)) →
if x1.1 < x2.1 then x1 else x2) if x1.1 < x2.1 then x1 else x2)
(-1, -1) (-1, -1)
map (λ (potential_max: integer) → (potential_max, potential_max)) x).0 map (λ (potential_max: integer) → (potential_max, potential_max)) x).0
@ -60,7 +60,7 @@ let scope S (S_in: S_in {x_in: list of integer}): S {y: integer} =
(λ () → true) (λ () → true)
(λ () → (λ () →
ESome ESome
(let weights : list of (integer * integer) = (let weights : list of (integer, integer) =
map (λ (potential_max: integer) → map (λ (potential_max: integer) →
(potential_max, (potential_max,
let potential_max1 : integer = potential_max in let potential_max1 : integer = potential_max in
@ -68,7 +68,7 @@ let scope S (S_in: S_in {x_in: list of integer}): S {y: integer} =
x x
in in
reduce reduce
(λ (x1: (integer * integer)) (x2: (integer * integer)) → (λ (x1: (integer, integer)) (x2: (integer, integer)) →
if x1.1 < x2.1 then x1 else x2) if x1.1 < x2.1 then x1 else x2)
let potential_max : integer = -1 in let potential_max : integer = -1 in
(potential_max, (potential_max,

View File

@ -25,18 +25,18 @@ $ catala Typecheck --check-invariants
$ catala Lcalc --avoid-exceptions -O --closure-conversion $ catala Lcalc --avoid-exceptions -O --closure-conversion
type Eoption = | ENone of unit | ESome of any type Eoption = | ENone of unit | ESome of any
type S_in = { x_in: bool; } type S_in = { x_in: bool; }
type S = { f: ((closure_env, integer) → integer * closure_env); } type S = { f: ((closure_env, integer) → integer, closure_env); }
let topval closure_f : (closure_env, integer) → integer = let topval closure_f1 : (closure_env, integer) → integer =
λ (env: closure_env) (y: integer) → λ (env: closure_env) (y: integer) →
if (from_closure_env env).0 then y else - y if (from_closure_env env).0 then y else - y
let scope S let scope S
(S_in: S_in {x_in: bool}) (S_in: S_in {x_in: bool})
: S {f: ((closure_env, integer) → integer * closure_env)} : S {f: ((closure_env, integer) → integer, closure_env)}
= =
let get x : bool = S_in.x_in in let get x : bool = S_in.x_in in
let set f : ((closure_env, integer) → integer * closure_env) = let set f : ((closure_env, integer) → integer, closure_env) =
(closure_f, to_closure_env (x)) (closure_f1, to_closure_env (x))
in in
return { S f = f; } return { S f = f; }

View File

@ -32,11 +32,11 @@ $ catala Typecheck --check-invariants
```catala-test-inline ```catala-test-inline
$ catala Lcalc -s T --avoid-exceptions -O --closure-conversion $ catala Lcalc -s T --avoid-exceptions -O --closure-conversion
let scope T (T_in: T_in): T {y: integer} = let scope T (T_in: T_in): T {y: integer} =
let set s : S {f: ((closure_env, integer) → integer * closure_env)} = let set s : S {f: ((closure_env, integer) → integer, closure_env)} =
{ S f = (closure_s, to_closure_env ()); } { S f = (closure_s1, to_closure_env ()); }
in in
let set y : integer = let set y : integer =
let code_and_env : ((closure_env, integer) → integer * closure_env) = let code_and_env : ((closure_env, integer) → integer, closure_env) =
s.f s.f
in in
code_and_env.0 code_and_env.1 2 code_and_env.0 code_and_env.1 2

View File

@ -56,72 +56,72 @@ $ catala Typecheck --check-invariants
$ catala Lcalc --avoid-exceptions -O --closure-conversion $ catala Lcalc --avoid-exceptions -O --closure-conversion
type Eoption = | ENone of unit | ESome of any type Eoption = | ENone of unit | ESome of any
type Result = { type Result = {
r: ((closure_env, integer) → integer * closure_env); r: ((closure_env, integer) → integer, closure_env);
q: integer; q: integer;
} }
type SubFoo1_in = { x_in: integer; } type SubFoo1_in = { x_in: integer; }
type SubFoo1 = { type SubFoo1 = {
x: integer; x: integer;
y: ((closure_env, integer) → integer * closure_env); y: ((closure_env, integer) → integer, closure_env);
} }
type SubFoo2_in = { x1_in: integer; x2_in: integer; } type SubFoo2_in = { x1_in: integer; x2_in: integer; }
type SubFoo2 = { type SubFoo2 = {
x1: integer; x1: integer;
y: ((closure_env, integer) → integer * closure_env); y: ((closure_env, integer) → integer, closure_env);
} }
type Foo_in = { b_in: ((closure_env, unit) → eoption bool * closure_env); } type Foo_in = { b_in: ((closure_env, unit) → eoption bool, closure_env); }
type Foo = { z: integer; } type Foo = { z: integer; }
let topval closure_y : (closure_env, integer) → integer = let topval closure_y1 : (closure_env, integer) → integer =
λ (env: closure_env) (z: integer) → λ (env: closure_env) (z: integer) →
(from_closure_env env).0 + z (from_closure_env env).0 + z
let scope SubFoo1 let scope SubFoo1
(SubFoo1_in: SubFoo1_in {x_in: integer}) (SubFoo1_in: SubFoo1_in {x_in: integer})
: SubFoo1 { : SubFoo1 {
x: integer; x: integer;
y: ((closure_env, integer) → integer * closure_env) y: ((closure_env, integer) → integer, closure_env)
} }
= =
let get x : integer = SubFoo1_in.x_in in let get x : integer = SubFoo1_in.x_in in
let set y : ((closure_env, integer) → integer * closure_env) = let set y : ((closure_env, integer) → integer, closure_env) =
(closure_y, to_closure_env (x)) (closure_y1, to_closure_env (x))
in in
return { SubFoo1 x = x; y = y; } return { SubFoo1 x = x; y = y; }
let topval closure_y : (closure_env, integer) → integer = let topval closure_y1 : (closure_env, integer) → integer =
λ (env: closure_env) (z: integer) → λ (env: closure_env) (z: integer) →
let env1 : (integer * integer) = from_closure_env env in let env1 : (integer, integer) = from_closure_env env in
((env1.1 + env1.0 + z)) ((env1.1 + env1.0 + z))
let scope SubFoo2 let scope SubFoo2
(SubFoo2_in: SubFoo2_in {x1_in: integer; x2_in: integer}) (SubFoo2_in: SubFoo2_in {x1_in: integer; x2_in: integer})
: SubFoo2 { : SubFoo2 {
x1: integer; x1: integer;
y: ((closure_env, integer) → integer * closure_env) y: ((closure_env, integer) → integer, closure_env)
} }
= =
let get x1 : integer = SubFoo2_in.x1_in in let get x1 : integer = SubFoo2_in.x1_in in
let get x2 : integer = SubFoo2_in.x2_in in let get x2 : integer = SubFoo2_in.x2_in in
let set y : ((closure_env, integer) → integer * closure_env) = let set y : ((closure_env, integer) → integer, closure_env) =
(closure_y, to_closure_env (x2, x1)) (closure_y1, to_closure_env (x2, x1))
in in
return { SubFoo2 x1 = x1; y = y; } return { SubFoo2 x1 = x1; y = y; }
let topval closure_r : (closure_env, integer) → integer = let topval closure_r2 : (closure_env, integer) → integer =
λ (env: closure_env) (param0: integer) → λ (env: closure_env) (param0: integer) →
let code_and_env : ((closure_env, integer) → integer * closure_env) = let code_and_env : ((closure_env, integer) → integer, closure_env) =
(from_closure_env env).0.y (from_closure_env env).0.y
in in
code_and_env.0 code_and_env.1 param0 code_and_env.0 code_and_env.1 param0
let topval closure_r : (closure_env, integer) → integer = let topval closure_r1 : (closure_env, integer) → integer =
λ (env: closure_env) (param0: integer) → λ (env: closure_env) (param0: integer) →
let code_and_env : ((closure_env, integer) → integer * closure_env) = let code_and_env : ((closure_env, integer) → integer, closure_env) =
(from_closure_env env).0.y (from_closure_env env).0.y
in in
code_and_env.0 code_and_env.1 param0 code_and_env.0 code_and_env.1 param0
let scope Foo let scope Foo
(Foo_in: (Foo_in:
Foo_in {b_in: ((closure_env, unit) → eoption bool * closure_env)}) Foo_in {b_in: ((closure_env, unit) → eoption bool, closure_env)})
: Foo {z: integer} : Foo {z: integer}
= =
let get b : ((closure_env, unit) → eoption bool * closure_env) = let get b : ((closure_env, unit) → eoption bool, closure_env) =
Foo_in.b_in Foo_in.b_in
in in
let set b : bool = let set b : bool =
@ -133,13 +133,13 @@ let scope Foo
in in
let set r : let set r :
Result { Result {
r: ((closure_env, integer) → integer * closure_env); r: ((closure_env, integer) → integer, closure_env);
q: integer q: integer
} = } =
if b then if b then
let f : SubFoo1 = let f : SubFoo1 =
let result : SubFoo1 = SubFoo1 { SubFoo1_in x_in = 10; } in let result : SubFoo1 = SubFoo1 { SubFoo1_in x_in = 10; } in
{ SubFoo1 x = result.x; y = (closure_r, to_closure_env (result)); } { SubFoo1 x = result.x; y = (closure_r1, to_closure_env (result)); }
in in
{ Result r = f.y; q = f.x; } { Result r = f.y; q = f.x; }
else else
@ -147,12 +147,15 @@ let scope Foo
let result : SubFoo2 = let result : SubFoo2 =
SubFoo2 { SubFoo2_in x1_in = 10; x2_in = 10; } SubFoo2 { SubFoo2_in x1_in = 10; x2_in = 10; }
in in
{ SubFoo2 x1 = result.x1; y = (closure_r, to_closure_env (result)); } { SubFoo2
x1 = result.x1;
y = (closure_r2, to_closure_env (result));
}
in in
{ Result r = f.y; q = f.x1; } { Result r = f.y; q = f.x1; }
in in
let set z : integer = let set z : integer =
let code_and_env : ((closure_env, integer) → integer * closure_env) = let code_and_env : ((closure_env, integer) → integer, closure_env) =
r.r r.r
in in
code_and_env.0 code_and_env.1 1 code_and_env.0 code_and_env.1 1

View File

@ -99,55 +99,55 @@ in
let lis3 : list of money = let lis3 : list of money =
[¤20.00; ¤200.00; ¤10.00; ¤23.00; ¤25.00; ¤12.00] [¤20.00; ¤200.00; ¤10.00; ¤23.00; ¤25.00; ¤12.00]
in in
let grok : (decimal, money, money) → (money * decimal) = let grok : (decimal, money, money) → (money, decimal) =
λ (dec: decimal) (mon1: money) (mon2: money) → λ (dec: decimal) (mon1: money) (mon2: money) →
(mon1 * dec, mon1 / mon2) (mon1 * dec, mon1 / mon2)
in in
let tlist : list of (decimal * money * money) = let tlist : list of (decimal, money, money) =
map2 map2
(λ (a: decimal) (b_c: (money * money)) → (a, b_c.0, b_c.1)) (λ (a: decimal) (b_c: (money, money)) → (a, b_c.0, b_c.1))
lis1 lis1
map2 (λ (b: money) (c: money) → (b, c)) lis2 lis3 map2 (λ (b: money) (c: money) → (b, c)) lis2 lis3
in in
let S : S_in → S = let S : S_in → S =
λ (S_in: S_in) → λ (S_in: S_in) →
let r1 : list of (money * decimal) = let r1 : list of (money, decimal) =
map (λ (x: (decimal * money * money)) → grok x.0 x.1 x.2) tlist map (λ (x: (decimal, money, money)) → grok x.0 x.1 x.2) tlist
in in
let r2 : list of (money * decimal) = let r2 : list of (money, decimal) =
map2 map2
(λ (x: decimal) (zip: (money * money)) → (λ (x: decimal) (zip: (money, money)) →
let x1 : (decimal * money * money) = (x, zip.0, zip.1) in let x1 : (decimal, money, money) = (x, zip.0, zip.1) in
grok x1.0 x1.1 x1.2) grok x1.0 x1.1 x1.2)
lis1 lis1
map2 (λ (x: money) (zip: money) → (x, zip)) lis2 lis3 map2 (λ (x: money) (zip: money) → (x, zip)) lis2 lis3
in in
let r3 : list of (money * decimal) = let r3 : list of (money, decimal) =
map2 map2
(λ (x: decimal) (y_z: (money * money)) → (λ (x: decimal) (y_z: (money, money)) →
let x_y_z : (decimal * money * money) = (x, y_z.0, y_z.1) in let x_y_z : (decimal, money, money) = (x, y_z.0, y_z.1) in
grok x_y_z.0 x_y_z.1 x_y_z.2) grok x_y_z.0 x_y_z.1 x_y_z.2)
lis1 lis1
map2 (λ (y: money) (z: money) → (y, z)) lis2 lis3 map2 (λ (y: money) (z: money) → (y, z)) lis2 lis3
in in
let r4 : list of (money * decimal) = let r4 : list of (money, decimal) =
map (λ (x_y_z: (decimal * money * money)) → map (λ (x_y_z: (decimal, money, money)) →
(x_y_z.1 * x_y_z.0, x_y_z.1 / x_y_z.2)) (x_y_z.1 * x_y_z.0, x_y_z.1 / x_y_z.2))
tlist tlist
in in
let r5 : list of (money * decimal) = let r5 : list of (money, decimal) =
map2 map2
(λ (x: decimal) (y_z: (money * money)) → (λ (x: decimal) (y_z: (money, money)) →
let x_y_z : (decimal * money * money) = (x, y_z.0, y_z.1) in let x_y_z : (decimal, money, money) = (x, y_z.0, y_z.1) in
(x_y_z.1 * x_y_z.0, x_y_z.1 / x_y_z.2)) (x_y_z.1 * x_y_z.0, x_y_z.1 / x_y_z.2))
lis1 lis1
map2 (λ (y: money) (z: money) → (y, z)) lis2 lis3 map2 (λ (y: money) (z: money) → (y, z)) lis2 lis3
in in
let r6 : list of (money * decimal) = let r6 : list of (money, decimal) =
map2 map2
(λ (xy: (decimal * money)) (z: money) → (λ (xy: (decimal, money)) (z: money) →
let xy_z : ((decimal * money) * money) = (xy, z) in let xy_z : ((decimal, money), money) = (xy, z) in
let xy1 : (decimal * money) = xy_z.0 in let xy1 : (decimal, money) = xy_z.0 in
let z1 : money = xy_z.1 in let z1 : money = xy_z.1 in
(xy1.1 * xy1.0, xy1.1 / z1)) (xy1.1 * xy1.0, xy1.1 / z1))
map2 (λ (x: decimal) (y: money) → (x, y)) lis1 lis2 map2 (λ (x: decimal) (y: money) → (x, y)) lis1 lis2