From 7f74f30536d0b3f43720cee286f50a75fe17dedd Mon Sep 17 00:00:00 2001 From: Joachim Breitner Date: Tue, 5 Feb 2019 11:02:17 +0100 Subject: [PATCH 1/4] Tidying interpret.mli it turns out that outside `Interpret`, all fields of `Interpret.env` are always empty, with the exception of `val_env`. So it is silly and confusing to even expose that in `interpret.mli`. No user of `Interpret` needs to know about the `labs`, `rets` or `async` fields. This makes the interface a bit more narrow. --- src/interpret.ml | 29 +++++++++++++++++++---------- src/interpret.mli | 23 +++++------------------ src/main.ml | 10 +--------- src/pipeline.ml | 20 +++++--------------- src/pipeline.mli | 2 +- 5 files changed, 31 insertions(+), 53 deletions(-) diff --git a/src/interpret.ml b/src/interpret.ml index b13f4fb8b2b..ff356de8d61 100644 --- a/src/interpret.ml +++ b/src/interpret.ml @@ -20,11 +20,13 @@ type env = async : bool } -let adjoin_vals c ve = {c with vals = V.Env.adjoin c.vals ve} -let adjoin = adjoin_vals +let adjoin_scope s ve = V.Env.adjoin s ve +let adjoin_vals c ve = {c with vals = adjoin_scope c.vals ve} -let empty_env = - { vals = V.Env.empty; +let empty_scope = V.Env.empty + +let env_of_scope ve = + { vals = ve; labs = V.Env.empty; rets = None; async = false; @@ -42,7 +44,6 @@ let find id env = with Not_found -> trap no_region "unbound identifier %s" id - (* Tracing *) let trace_depth = ref 0 @@ -59,12 +60,19 @@ let string_of_arg = function (* Debugging aids *) -let last_env = ref empty_env +let last_env = ref (env_of_scope empty_scope) let last_region = ref Source.no_region -let get_last_env () = !last_env -let get_last_region () = !last_region - +let print_exn exn = + Printf.printf "%!"; + let at = Source.string_of_region !last_region in + Printf.eprintf "%s: internal error, %s\n" at (Printexc.to_string exn); + Printf.eprintf "\nLast environment:\n"; + Value.Env.iter (fun x d -> Printf.eprintf "%s = %s\n" x (Value.string_of_def d)) + (!last_env.vals); + Printf.eprintf "\n"; + Printexc.print_backtrace stderr; + Printf.eprintf "%!" (* Scheduling *) @@ -713,7 +721,8 @@ and interpret_func env id pat f v (k : V.value V.cont) = (* Programs *) -let interpret_prog env p : V.value option * scope = +let interpret_prog scope p : V.value option * scope = + let env = env_of_scope scope in trace_depth := 0; let vo = ref None in let ve = ref V.Env.empty in diff --git a/src/interpret.mli b/src/interpret.mli index ea826863b37..29eada87616 100644 --- a/src/interpret.mli +++ b/src/interpret.mli @@ -1,26 +1,13 @@ module V = Value module T = Type -type val_env = V.def V.Env.t -type lab_env = V.value V.cont V.Env.t -type ret_env = V.value V.cont option - -type scope = val_env - -type env = - { vals : val_env; - labs : lab_env; - rets : ret_env; - async : bool - } - -val empty_env : env -val adjoin : env -> scope -> env +type scope = V.def V.Env.t +val empty_scope : scope +val adjoin_scope : scope -> scope -> scope exception Trap of Source.region * string -val interpret_prog : env -> Syntax.prog -> V.value option * val_env +val interpret_prog : scope -> Syntax.prog -> V.value option * scope -val get_last_region : unit -> Source.region -val get_last_env : unit -> env +val print_exn : exn -> unit diff --git a/src/main.ml b/src/main.ml index 46894bdd5b0..2f8660cb3a1 100644 --- a/src/main.ml +++ b/src/main.ml @@ -98,12 +98,4 @@ let () = if !mode = Default then mode := (if !args = [] then Interact else Compile); process_files !args with exn -> - printf "%!"; - let at = Source.string_of_region (Interpret.get_last_region ()) in - eprintf "%s: internal error, %s\n" at (Printexc.to_string exn); - eprintf "\nLast environment:\n"; - Value.Env.iter (fun x d -> eprintf "%s = %s\n" x (Value.string_of_def d)) - Interpret.((get_last_env ()).vals); - eprintf "\n"; - Printexc.print_backtrace stderr; - eprintf "%!" + Interpret.print_exn exn diff --git a/src/pipeline.ml b/src/pipeline.ml index fc36ed21435..563851a0e63 100644 --- a/src/pipeline.ml +++ b/src/pipeline.ml @@ -6,7 +6,7 @@ module Async = Async module Tailcall = Tailcall type stat_env = Typing.scope -type dyn_env = Interpret.env +type dyn_env = Interpret.scope type env = stat_env * dyn_env (* Diagnostics *) @@ -39,11 +39,6 @@ let print_dyn_ve scope = (Type.string_of_typ t') (Value.string_of_def d) ) -let eprint_dyn_ve_untyped = - Value.Env.iter (fun x d -> - eprintf "%s = %s\n%!" x (Value.string_of_def d) - ) - let print_scope senv scope dve = print_ce scope.Typing.con_env; print_dyn_ve senv dve @@ -168,12 +163,7 @@ let interpret_prog denv name prog : (Value.value * Interpret.scope) option = | Some v -> Some (v, scope) with exn -> (* For debugging, should never happen. *) - Diag.print_message (Diag.fatal_error (Interpret.get_last_region ()) (Printexc.to_string exn)); - eprintf "\nLast environment:\n%!"; - eprint_dyn_ve_untyped Interpret.((get_last_env ()).vals); - eprintf "\n"; - Printexc.print_backtrace stderr; - eprintf "%!"; + Interpret.print_exn exn; None let interpret_with check (senv, denv) name : interpret_result = @@ -221,10 +211,10 @@ let check_prelude () : Syntax.prog * stat_env = let prelude, initial_stat_env = check_prelude () let run_prelude () : dyn_env = - match interpret_prog Interpret.empty_env prelude_name prelude with + match interpret_prog Interpret.empty_scope prelude_name prelude with | None -> prelude_error "initializing" [] | Some (_v, dscope) -> - Interpret.adjoin Interpret.empty_env dscope + Interpret.adjoin_scope Interpret.empty_scope dscope let initial_dyn_env = run_prelude () let initial_env = (initial_stat_env, initial_dyn_env) @@ -253,7 +243,7 @@ let run_with interpret output ((senv, denv) as env) name : run_result = | Some (prog, t, v, sscope, dscope) -> phase "Finished" name; let senv' = Typing.adjoin_scope senv sscope in - let denv' = Interpret.adjoin denv dscope in + let denv' = Interpret.adjoin_scope denv dscope in let env' = (senv', denv') in (* TBR: hack *) let t', v' = diff --git a/src/pipeline.mli b/src/pipeline.mli index 92677d228fe..6ee84a62e64 100644 --- a/src/pipeline.mli +++ b/src/pipeline.mli @@ -1,5 +1,5 @@ type stat_env = Typing.scope -type dyn_env = Interpret.env +type dyn_env = Interpret.scope type env = stat_env * dyn_env val initial_stat_env : stat_env From 8639653e11513ddbd9434267009c7d6c93a3f91e Mon Sep 17 00:00:00 2001 From: Joachim Breitner Date: Tue, 5 Feb 2019 11:53:25 +0100 Subject: [PATCH 2/4] An interpreter for the IR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit this is currently a pretty straight-forward copy of `interpret.ml` to the new IR. The test suite ensures that we get identical output with either `Interpret` or `Desugar`+`Interpret_ir` (and I checked that the test suite would report the differences if there were any). This is still uses the CPS style, and I think this is unavoidable if we want to be able to interpret our code at any stage of the pipeline, including before and after the await-translation. Also, for code resuse in `Value` (which assumes a CPS’ed function type). Fixes #141. --- src/flags.ml | 1 + src/interpret_ir.ml | 703 ++++++++++++++++++ src/interpret_ir.mli | 13 + src/main.ml | 1 + src/pipeline.ml | 7 +- .../ok/array-out-of-bounds.run-ir.ok | 2 + .../run-dfinity/ok/async-loop-while.run-ir.ok | 1 + test/run-dfinity/ok/async-loop.run-ir.ok | 1 + test/run-dfinity/ok/async-new-obj.run-ir.ok | 5 + test/run-dfinity/ok/async-obj-mut.run-ir.ok | 3 + test/run-dfinity/ok/async-while.run-ir.ok | 1 + test/run-dfinity/ok/chat.run-ir.ok | 18 + test/run-dfinity/ok/chatpp.run-ir.ok | 15 + test/run-dfinity/ok/closure-params.run-ir.ok | 10 + test/run-dfinity/ok/counter.run-ir.ok | 1 + test/run-dfinity/ok/data-params.run-ir.ok | 15 + test/run-dfinity/ok/fac.run-ir.ok | 1 + .../ok/flatten-awaitables.run-ir.ok | 29 + .../run-dfinity/ok/generic-tail-rec.run-ir.ok | 4 + .../ok/hello-concat-world.run-ir.ok | 1 + .../ok/hello-world-async.run-ir.ok | 1 + .../ok/hello-world-await.run-ir.ok | 1 + test/run-dfinity/ok/hello-world.run-ir.ok | 1 + test/run-dfinity/ok/hello-world2.run-ir.ok | 1 + test/run-dfinity/ok/hello-world3.run-ir.ok | 1 + .../run-dfinity/ok/indirect-counter.run-ir.ok | 1 + test/run-dfinity/ok/nary-async.run-ir.ok | 11 + test/run-dfinity/ok/overflow.diff-ir.ok | 11 + test/run-dfinity/ok/overflow.run-ir.ok | 6 + .../run-dfinity/ok/reference-params.run-ir.ok | 5 + test/run-dfinity/ok/selftail.run-ir.ok | 2 + test/run-dfinity/ok/tailpositions.run-ir.ok | 7 + test/run-dfinity/ok/the-answer.run-ir.ok | 1 + test/run.sh | 10 +- test/run/ok/actors.run-ir.ok | 2 + test/run/ok/array-bounds.run-ir.ok | 1 + test/run/ok/assertFalse.run-ir.ok | 1 + test/run/ok/asyncreturn.run-ir.ok | 1 + test/run/ok/await.run-ir.ok | 16 + test/run/ok/bank-example.diff-ir.ok | 4 + test/run/ok/bank-example.run-ir.ok | 1 + test/run/ok/block.run-ir.ok | 1 + test/run/ok/coverage.run-ir.ok | 26 + test/run/ok/for.run-ir.ok | 1 + test/run/ok/is.diff-ir.ok | 4 + test/run/ok/is.run-ir.ok | 1 + test/run/ok/overflow.diff-ir.ok | 5 + test/run/ok/overflow.run-ir.ok | 1 + test/run/ok/switch.run-ir.ok | 6 + 49 files changed, 960 insertions(+), 2 deletions(-) create mode 100644 src/interpret_ir.ml create mode 100644 src/interpret_ir.mli create mode 100644 test/run-dfinity/ok/array-out-of-bounds.run-ir.ok create mode 100644 test/run-dfinity/ok/async-loop-while.run-ir.ok create mode 100644 test/run-dfinity/ok/async-loop.run-ir.ok create mode 100644 test/run-dfinity/ok/async-new-obj.run-ir.ok create mode 100644 test/run-dfinity/ok/async-obj-mut.run-ir.ok create mode 100644 test/run-dfinity/ok/async-while.run-ir.ok create mode 100644 test/run-dfinity/ok/chat.run-ir.ok create mode 100644 test/run-dfinity/ok/chatpp.run-ir.ok create mode 100644 test/run-dfinity/ok/closure-params.run-ir.ok create mode 100644 test/run-dfinity/ok/counter.run-ir.ok create mode 100644 test/run-dfinity/ok/data-params.run-ir.ok create mode 100644 test/run-dfinity/ok/fac.run-ir.ok create mode 100644 test/run-dfinity/ok/flatten-awaitables.run-ir.ok create mode 100644 test/run-dfinity/ok/generic-tail-rec.run-ir.ok create mode 100644 test/run-dfinity/ok/hello-concat-world.run-ir.ok create mode 100644 test/run-dfinity/ok/hello-world-async.run-ir.ok create mode 100644 test/run-dfinity/ok/hello-world-await.run-ir.ok create mode 100644 test/run-dfinity/ok/hello-world.run-ir.ok create mode 100644 test/run-dfinity/ok/hello-world2.run-ir.ok create mode 100644 test/run-dfinity/ok/hello-world3.run-ir.ok create mode 100644 test/run-dfinity/ok/indirect-counter.run-ir.ok create mode 100644 test/run-dfinity/ok/nary-async.run-ir.ok create mode 100644 test/run-dfinity/ok/overflow.diff-ir.ok create mode 100644 test/run-dfinity/ok/overflow.run-ir.ok create mode 100644 test/run-dfinity/ok/reference-params.run-ir.ok create mode 100644 test/run-dfinity/ok/selftail.run-ir.ok create mode 100644 test/run-dfinity/ok/tailpositions.run-ir.ok create mode 100644 test/run-dfinity/ok/the-answer.run-ir.ok create mode 100644 test/run/ok/actors.run-ir.ok create mode 100644 test/run/ok/array-bounds.run-ir.ok create mode 100644 test/run/ok/assertFalse.run-ir.ok create mode 100644 test/run/ok/asyncreturn.run-ir.ok create mode 100644 test/run/ok/await.run-ir.ok create mode 100644 test/run/ok/bank-example.diff-ir.ok create mode 100644 test/run/ok/bank-example.run-ir.ok create mode 100644 test/run/ok/block.run-ir.ok create mode 100644 test/run/ok/coverage.run-ir.ok create mode 100644 test/run/ok/for.run-ir.ok create mode 100644 test/run/ok/is.diff-ir.ok create mode 100644 test/run/ok/is.run-ir.ok create mode 100644 test/run/ok/overflow.diff-ir.ok create mode 100644 test/run/ok/overflow.run-ir.ok create mode 100644 test/run/ok/switch.run-ir.ok diff --git a/src/flags.ml b/src/flags.ml index be6b879d32a..9a9ca618434 100644 --- a/src/flags.ml +++ b/src/flags.ml @@ -6,5 +6,6 @@ let async_lowering = ref false let dump_parse = ref false let dump_tc = ref false let dump_lowering = ref false +let interpret_ir = ref false let source_map = ref false let prelude = ref true diff --git a/src/interpret_ir.ml b/src/interpret_ir.ml new file mode 100644 index 00000000000..4f8e50b4242 --- /dev/null +++ b/src/interpret_ir.ml @@ -0,0 +1,703 @@ +open Ir +open Source + +module V = Value +module T = Type + + +(* Context *) + +type val_env = V.def V.Env.t +type lab_env = V.value V.cont V.Env.t +type ret_env = V.value V.cont option + +type scope = val_env + +type env = + { vals : val_env; + labs : lab_env; + rets : ret_env; + async : bool + } + +let adjoin_scope s ve = V.Env.adjoin s ve +let adjoin_vals c ve = {c with vals = adjoin_scope c.vals ve} + +let empty_scope = V.Env.empty + +let env_of_scope ve = + { vals = ve; + labs = V.Env.empty; + rets = None; + async = false; + } + + +(* Error handling *) + +exception Trap of Source.region * string + +let trap at fmt = Printf.ksprintf (fun s -> raise (Trap (at, s))) fmt + +let find id env = + try V.Env.find id env + with Not_found -> + trap no_region "unbound identifier %s" id + +(* Tracing *) + +let trace_depth = ref 0 + +let trace fmt = + Printf.ksprintf (fun s -> + Printf.printf "%s%s\n%!" (String.make (2 * !trace_depth) ' ') s + ) fmt + +let string_of_arg = function + | V.Tup _ as v -> V.string_of_val v + | v -> "(" ^ V.string_of_val v ^ ")" + + +(* Debugging aids *) + +let last_env = ref (env_of_scope empty_scope) +let last_region = ref Source.no_region + +let print_exn exn = + Printf.printf "%!"; + let at = Source.string_of_region !last_region in + Printf.eprintf "%s: internal error, %s\n" at (Printexc.to_string exn); + Printf.eprintf "\nLast environment:\n"; + Value.Env.iter (fun x d -> Printf.eprintf "%s = %s\n" x (Value.string_of_def d)) + (!last_env.vals); + Printf.eprintf "\n"; + Printexc.print_backtrace stderr; + Printf.eprintf "%!" + +(* Scheduling *) + +module Scheduler = +struct + let q : (unit -> unit) Queue.t = Queue.create () + + let queue work = Queue.add work q + let yield () = + trace_depth := 0; + try Queue.take q () with Trap (at, msg) -> + Printf.printf "%s: execution error, %s\n" (Source.string_of_region at) msg + + let rec run () = + if not (Queue.is_empty q) then (yield (); run ()) +end + + +(* Async auxiliary functions *) + +let make_async () : V.async = + {V.result = Lib.Promise.make (); waiters = []} + +let get_async async (k : V.value V.cont) = + match Lib.Promise.value_opt async.V.result with + | Some v -> k v + | None -> async.V.waiters <- k::async.V.waiters + +let set_async async v = + List.iter (fun k -> Scheduler.queue (fun () -> k v)) async.V.waiters; + Lib.Promise.fulfill async.V.result v; + async.V.waiters <- [] + +let fulfill async v = + Scheduler.queue (fun () -> set_async async v) + + +let async at (f: (V.value V.cont) -> unit) (k : V.value V.cont) = + let async = make_async () in + (* let k' = fun v1 -> set_async async v1 in *) + let k' = fun v1 -> fulfill async v1 in + if !Flags.trace then trace "-> async %s" (string_of_region at); + Scheduler.queue (fun () -> + if !Flags.trace then trace "<- async %s" (string_of_region at); + incr trace_depth; + f (fun v -> + if !Flags.trace then trace "<= %s" (V.string_of_val v); + decr trace_depth; + k' v) + ); + k (V.Async async) + +let await at async k = + if !Flags.trace then trace "=> await %s" (string_of_region at); + decr trace_depth; + get_async async (fun v -> + Scheduler.queue (fun () -> + if !Flags.trace then + trace "<- await %s%s" (string_of_region at) (string_of_arg v); + incr trace_depth; + k v + ) + ) +(*; Scheduler.yield () *) + +let actor_msg id f v (k : V.value V.cont) = + if !Flags.trace then trace "-> message %s%s" id (string_of_arg v); + Scheduler.queue (fun () -> + if !Flags.trace then trace "<- message %s%s" id (string_of_arg v); + incr trace_depth; + f v k + ) + +let make_unit_message id v = + let _, call_conv, f = V.as_func v in + match call_conv with + | {V.sort = T.Call T.Sharable; V.n_res = 0; _} -> + Value.message_func call_conv.V.n_args (fun v k -> + actor_msg id f v (fun _ -> ()); + k V.unit + ) + | _ -> + failwith ("unexpected call_conv " ^ (V.string_of_call_conv call_conv)) +(* assert (false) *) + +let make_async_message id v = + assert (not !Flags.async_lowering); + let _, call_conv, f = V.as_func v in + match call_conv with + | {V.sort = T.Call T.Sharable; V.control = T.Promises; V.n_res = 1; _} -> + Value.async_func call_conv.V.n_args (fun v k -> + let async = make_async () in + actor_msg id f v (fun v_async -> + get_async (V.as_async v_async) (fun v_r -> set_async async v_r) + ); + k (V.Async async) + ) + | _ -> + failwith ("unexpected call_conv " ^ (V.string_of_call_conv call_conv)) + (* assert (false) *) + + +let make_message id t v : V.value = + match t with + | T.Func (_, _, _, _, []) -> + make_unit_message id.it v + | T.Func (_, _, _, _, [T.Async _]) -> + assert (not !Flags.async_lowering); + make_async_message id.it v + | _ -> + failwith (Printf.sprintf "actorfield: %s %s" id.it (T.string_of_typ t)) + (* assert false *) + + +let extended_prim s typ at = + match s with + | "@async" -> + assert (!Flags.await_lowering && not !Flags.async_lowering); + (fun v k -> + let (call, _, f) = V.as_func v in + match typ with + | T.Func(_, _, _, [T.Func(_, _, _, [f_dom], _)], _) -> + let call_conv = Value.call_conv_of_typ f_dom in + async at + (fun k' -> + let k' = Value.Func (None, call_conv, fun v _ -> k' v) in + f k' V.as_unit + ) k + | _ -> assert false + ) + | "@await" -> + assert(!Flags.await_lowering && not !Flags.async_lowering); + fun v k -> + (match V.as_tup v with + | [async; w] -> + let (_, _, f) = V.as_func w in + await at (V.as_async async) (fun v -> f v k) + | _ -> assert false + ) + | _ -> Prelude.prim s + + +(* Literals *) + +let interpret_lit env lit : V.value = + let open Syntax in + match lit with + | NullLit -> V.Null + | BoolLit b -> V.Bool b + | NatLit n -> V.Int n + | IntLit i -> V.Int i + | Word8Lit w -> V.Word8 w + | Word16Lit w -> V.Word16 w + | Word32Lit w -> V.Word32 w + | Word64Lit w -> V.Word64 w + | FloatLit f -> V.Float f + | CharLit c -> V.Char c + | TextLit s -> V.Text s + | PreLit _ -> assert false + + +(* Expressions *) + +let check_call_conv exp call_conv = + let exp_call_conv = V.call_conv_of_typ exp.note.Syntax.note_typ in + if not (exp_call_conv = call_conv) then + failwith (Printf.sprintf "call_conv mismatch: function %s of type %s expecting %s, found %s" + (Wasm.Sexpr.to_string 80 (Arrange_ir.exp exp)) + (T.string_of_typ exp.note.Syntax.note_typ) + (V.string_of_call_conv exp_call_conv) + (V.string_of_call_conv call_conv)) + +let check_call_conv_arg exp v call_conv = + if call_conv.V.n_args <> 1 then + let es = try V.as_tup v + with Invalid_argument _ -> + failwith (Printf.sprintf "call %s: calling convention %s cannot handle non-tuple value %s" + (Wasm.Sexpr.to_string 80 (Arrange_ir.exp exp)) + (V.string_of_call_conv call_conv) + (V.string_of_val v)) in + if List.length es <> call_conv.V.n_args then + failwith (Printf.sprintf "call %s: calling convention %s got tuple of wrong length %s" + (Wasm.Sexpr.to_string 80 (Arrange_ir.exp exp)) + (V.string_of_call_conv call_conv) + (V.string_of_val v)) + + +let rec interpret_exp env exp (k : V.value V.cont) = + interpret_exp_mut env exp (function V.Mut r -> k !r | v -> k v) + +and interpret_exp_mut env exp (k : V.value V.cont) = + last_region := exp.at; + last_env := env; + match exp.it with + | PrimE s -> + let at = exp.at in + let t = exp.note.Syntax.note_typ in + let cc = V.call_conv_of_typ t in + k (V.Func (None, cc, extended_prim s t at)) + | VarE id -> + (match Lib.Promise.value_opt (find id.it env.vals) with + | Some v -> k v + | None -> trap exp.at "accessing identifier before its definition" + ) + | LitE lit -> + k (interpret_lit env lit) + | UnE (ot, op, exp1) -> + interpret_exp env exp1 (fun v1 -> k (Operator.unop ot op v1)) + | BinE (ot, exp1, op, exp2) -> + interpret_exp env exp1 (fun v1 -> + interpret_exp env exp2 (fun v2 -> + k (try Operator.binop ot op v1 v2 with _ -> + trap exp.at "arithmetic overflow") + ) + ) + | RelE (ot, exp1, op, exp2) -> + interpret_exp env exp1 (fun v1 -> + interpret_exp env exp2 (fun v2 -> + k (Operator.relop ot op v1 v2) + ) + ) + | TupE exps -> + interpret_exps env exps [] (fun vs -> k (V.Tup vs)) + | OptE exp1 -> + interpret_exp env exp1 (fun v1 -> k (V.Opt v1)) + | ProjE (exp1, n) -> + interpret_exp env exp1 (fun v1 -> k (List.nth (V.as_tup v1) n)) + | ActorE (id, fields, _) -> + interpret_obj env id fields k + | DotE (exp1, {it = Syntax.Name n; _}) + | ActorDotE (exp1, {it = Syntax.Name n; _}) -> + interpret_exp env exp1 (fun v1 -> + let _, fs = V.as_obj v1 in + k (try find n fs with _ -> assert false) + ) + | AssignE (exp1, exp2) -> + interpret_exp_mut env exp1 (fun v1 -> + interpret_exp env exp2 (fun v2 -> + V.as_mut v1 := v2; k V.unit + ) + ) + | ArrayE (mut, _, exps) -> + interpret_exps env exps [] (fun vs -> + let vs' = + match mut.it with + | Syntax.Var -> List.map (fun v -> V.Mut (ref v)) vs + | Syntax.Const -> vs + in k (V.Array (Array.of_list vs')) + ) + | IdxE (exp1, exp2) -> + interpret_exp env exp1 (fun v1 -> + interpret_exp env exp2 (fun v2 -> + k (try (V.as_array v1).(V.Int.to_int (V.as_int v2)) + with Invalid_argument s -> trap exp.at "%s" s) + ) + ) + | CallE (_cc, exp1, typs, exp2) -> + interpret_exp env exp1 (fun v1 -> + interpret_exp env exp2 (fun v2 -> + let _, call_conv, f = V.as_func v1 in + check_call_conv exp1 call_conv; + check_call_conv_arg exp v2 call_conv; + f v2 k + +(* + try + let _, f = V.as_func v1 in f v2 k + with Invalid_argument s -> + trap exp.at "%s" s +*) + ) + ) + | BlockE (decs, _)-> + interpret_block env decs None k + | IfE (exp1, exp2, exp3) -> + interpret_exp env exp1 (fun v1 -> + if V.as_bool v1 + then interpret_exp env exp2 k + else interpret_exp env exp3 k + ) + | SwitchE (exp1, cases) -> + interpret_exp env exp1 (fun v1 -> + interpret_cases env cases exp.at v1 k + ) + | WhileE (exp1, exp2) -> + let k_continue = fun v -> V.as_unit v; interpret_exp env exp k in + interpret_exp env exp1 (fun v1 -> + if V.as_bool v1 + then interpret_exp env exp2 k_continue + else k V.unit + ) + | LoopE (exp1, None) -> + interpret_exp env exp1 (fun v -> V.as_unit v; interpret_exp env exp k) + | LoopE (exp1, Some exp2) -> + interpret_exp env exp1 (fun v1 -> + V.as_unit v1; + interpret_exp env exp2 (fun v2 -> + if V.as_bool v2 + then interpret_exp env exp k + else k V.unit + ) + ) + | ForE (pat, exp1, exp2) -> + interpret_exp env exp1 (fun v1 -> + let _, fs = V.as_obj v1 in + let _, _, next = V.as_func (find "next" fs) in + let rec k_continue = fun v -> + V.as_unit v; + next V.unit (fun v' -> + match v' with + | V.Opt v1 -> + (match match_pat pat v1 with + | None -> + trap pat.at "value %s does not match pattern" (V.string_of_val v') + | Some ve -> + interpret_exp (adjoin_vals env ve) exp2 k_continue + ) + | V.Null -> k V.unit + | _ -> assert false + ) + in k_continue V.unit + ) + | LabelE (id, _typ, exp1) -> + let env' = {env with labs = V.Env.add id.it k env.labs} in + interpret_exp env' exp1 k + | BreakE (id, exp1) -> + interpret_exp env exp1 (find id.it env.labs) + | RetE exp1 -> + interpret_exp env exp1 (Lib.Option.value env.rets) + | AsyncE exp1 -> + assert(not(!Flags.await_lowering)); + async + exp.at + (fun k' -> + let env' = {env with labs = V.Env.empty; rets = Some k'; async = true} + in interpret_exp env' exp1 k') + k + + | AwaitE exp1 -> + assert(not(!Flags.await_lowering)); + interpret_exp env exp1 + (fun v1 -> await exp.at (V.as_async v1) k) + | AssertE exp1 -> + interpret_exp env exp1 (fun v -> + if V.as_bool v + then k V.unit + else trap exp.at "assertion failure" + ) + | IsE (exp1, exp2) -> + interpret_exp env exp1 (fun v1 -> + interpret_exp env exp2 (fun v2 -> + let b = + match v1 with + | V.Obj (Some c1, _) -> + let c2, _, _ = V.as_func v2 in + Some c1 = c2 + | _ -> false + in k (V.Bool b) + ) + ) + | DeclareE (id, typ, exp1) -> + let env = adjoin_vals env (declare_id id) in + interpret_exp env exp1 k + | DefineE (id, mut, exp1) -> + interpret_exp env exp1 (fun v -> + let v' = + match mut.it with + | Syntax.Const -> v + | Syntax.Var -> V.Mut (ref v) + in + define_id env id v'; + k V.unit + ) + | NewObjE (sort, ids, _) -> + let ve = + List.fold_left + (fun ve (name, id) -> + V.Env.disjoint_add (Syntax.string_of_name name.it) + (Lib.Promise.value (find id.it env.vals)) ve + ) V.Env.empty ids + in k (V.Obj (None, ve)) + + + +and interpret_exps env exps vs (k : V.value list V.cont) = + match exps with + | [] -> k (List.rev vs) + | exp::exps' -> + interpret_exp env exp (fun v -> interpret_exps env exps' (v::vs) k) + + +(* Cases *) + +and interpret_cases env cases at v (k : V.value V.cont) = + match cases with + | [] -> + trap at "switch value %s does not match any case" (V.string_of_val v) + | {it = {pat; exp}; at; _}::cases' -> + match match_pat pat v with + | Some ve -> interpret_exp (adjoin_vals env ve) exp k + | None -> interpret_cases env cases' at v k + + +(* Patterns *) + +and declare_id id = + V.Env.singleton id.it (Lib.Promise.make ()) + +and declare_pat pat : val_env = + match pat.it with + | WildP | LitP _ -> V.Env.empty + | VarP id -> declare_id id + | TupP pats -> declare_pats pats V.Env.empty + | OptP pat1 -> declare_pat pat1 + | AltP (pat1, pat2) -> declare_pat pat1 + +and declare_pats pats ve : val_env = + match pats with + | [] -> ve + | pat::pats' -> + let ve' = declare_pat pat in + declare_pats pats' (V.Env.adjoin ve ve') + + +and define_id env id v = + Lib.Promise.fulfill (find id.it env.vals) v + +and define_pat env pat v = + match pat.it with + | WildP -> () + | LitP _ | AltP _ -> + if match_pat pat v = None + then trap pat.at "value %s does not match pattern" (V.string_of_val v) + else () + | VarP id -> define_id env id v + | TupP pats -> define_pats env pats (V.as_tup v) + | OptP pat1 -> + (match v with + | V.Opt v1 -> define_pat env pat1 v1 + | V.Null -> + trap pat.at "value %s does not match pattern" (V.string_of_val v) + | _ -> assert false + ) + +and define_pats env pats vs = + List.iter2 (define_pat env) pats vs + + +and match_lit lit v : bool = + let open Syntax in + match lit, v with + | NullLit, V.Null -> true + | BoolLit b, V.Bool b' -> b = b' + | NatLit n, V.Int n' -> V.Int.eq n n' + | IntLit i, V.Int i' -> V.Int.eq i i' + | Word8Lit w, V.Word8 w' -> w = w' + | Word16Lit w, V.Word16 w' -> w = w' + | Word32Lit w, V.Word32 w' -> w = w' + | Word64Lit w, V.Word64 w' -> w = w' + | FloatLit z, V.Float z' -> z = z' + | CharLit c, V.Char c' -> c = c' + | TextLit u, V.Text u' -> u = u' + | PreLit _, _ -> assert false + | _ -> false + +and match_pat pat v : val_env option = + match pat.it with + | WildP -> Some V.Env.empty + | VarP id -> Some (V.Env.singleton id.it (Lib.Promise.make_fulfilled v)) + | LitP lit -> + if match_lit lit v + then Some V.Env.empty + else None + | TupP pats -> + match_pats pats (V.as_tup v) V.Env.empty + | OptP pat1 -> + (match v with + | V.Opt v1 -> match_pat pat1 v1 + | V.Null -> None + | _ -> assert false + ) + | AltP (pat1, pat2) -> + (match match_pat pat1 v with + | None -> match_pat pat2 v + | some -> some + ) + +and match_pats pats vs ve : val_env option = + match pats, vs with + | [], [] -> Some ve + | pat::pats', v::vs' -> + (match match_pat pat v with + | Some ve' -> match_pats pats' vs' (V.Env.adjoin ve ve') + | None -> None + ) + | _ -> assert false + + +(* Actors *) + +and interpret_obj env id fields (k : V.value V.cont) = + let ve = declare_exp_fields fields (declare_id id) in + let env' = adjoin_vals env ve in + interpret_fields env' fields V.Env.empty (fun v -> + define_id env' id v; + k v + ) + +and declare_exp_fields fields ve : val_env = + match fields with + | [] -> ve + | {it = {id; name; mut; priv; _}; _}::fields' -> + let p = Lib.Promise.make () in + let ve' = V.Env.singleton id.it p in + declare_exp_fields fields' (V.Env.adjoin ve ve') + + +and interpret_fields env fields ve (k : V.value V.cont) = + match fields with + | [] -> k (V.Obj (None, V.Env.map Lib.Promise.value ve)) + | {it = {id; name; mut; priv; exp}; _}::fields' -> + interpret_exp env exp (fun v -> + let v' = + match mut.it with + | Syntax.Const -> v + | Syntax.Var -> V.Mut (ref v) + in + define_id env id v'; + let ve' = + if priv.it = Syntax.Private + then ve + else V.Env.add (Syntax.string_of_name name.it) (V.Env.find id.it env.vals) ve + in interpret_fields env fields' ve' k + ) + +(* Blocks and Declarations *) + +and interpret_block env decs ro (k : V.value V.cont) = + let ve = declare_decs decs V.Env.empty in + Lib.Option.app (fun r -> r := ve) ro; + interpret_decs (adjoin_vals env ve) decs k + + +and declare_dec dec : val_env = + match dec.it with + | ExpD _ | TypD _ -> V.Env.empty + | LetD (pat, _) -> declare_pat pat + | VarD (id, _) | FuncD (_, id, _, _, _, _) -> declare_id id + +and declare_decs decs ve : val_env = + match decs with + | [] -> ve + | dec::decs' -> + let ve' = declare_dec dec in + declare_decs decs' (V.Env.adjoin ve ve') + + +and interpret_dec env dec (k : V.value V.cont) = + match dec.it with + | ExpD exp -> + interpret_exp env exp k + | LetD (pat, exp) -> + interpret_exp env exp (fun v -> + define_pat env pat v; + k V.unit + ) + | VarD (id, exp) -> + interpret_exp env exp (fun v -> + define_id env id (V.Mut (ref v)); + k V.unit + ) + | TypD _ -> + k V.unit + | FuncD (cc, id, _typbinds, pat, _typ, exp) -> + let f = interpret_func env id pat + (fun env' -> interpret_exp env' exp) in + let v = V.Func (None, V.call_conv_of_typ dec.note.Syntax.note_typ, f) in + let v = + match cc.Value.sort with + | T.Call T.Sharable -> + make_message id dec.note.Syntax.note_typ v + | _-> v + in + define_id env id v; + k v + +and interpret_decs env decs (k : V.value V.cont) = + match decs with + | [] -> k V.unit + | [dec] -> interpret_dec env dec k + | dec::decs' -> + interpret_dec env dec (fun _v -> interpret_decs env decs' k) + + +and interpret_func env id pat f v (k : V.value V.cont) = + if !Flags.trace then trace "%s%s" id.it (string_of_arg v); + match match_pat pat v with + | None -> + trap pat.at "argument value %s does not match parameter list" + (V.string_of_val v) + | Some ve -> + incr trace_depth; + let k' = fun v' -> + if !Flags.trace then trace "<= %s" (V.string_of_val v'); + decr trace_depth; + k v' + in + let env' = + { vals = V.Env.adjoin env.vals ve; + labs = V.Env.empty; + rets = Some k'; + async = false + } + in f env' k' + + +(* Programs *) + +let interpret_prog scope p : V.value option * scope = + let env = env_of_scope scope in + trace_depth := 0; + let vo = ref None in + let ve = ref V.Env.empty in + Scheduler.queue (fun () -> + interpret_block env p.it (Some ve) (fun v -> vo := Some v) + ); + Scheduler.run (); + !vo, !ve diff --git a/src/interpret_ir.mli b/src/interpret_ir.mli new file mode 100644 index 00000000000..3beea733f96 --- /dev/null +++ b/src/interpret_ir.mli @@ -0,0 +1,13 @@ +module V = Value +module T = Type + +type scope = V.def V.Env.t + +val empty_scope : scope +val adjoin_scope : scope -> scope -> scope + +exception Trap of Source.region * string + +val interpret_prog : scope -> Ir.prog -> V.value option * scope + +val print_exn : exn -> unit diff --git a/src/main.ml b/src/main.ml index 2f8660cb3a1..ac4849f3835 100644 --- a/src/main.ml +++ b/src/main.ml @@ -34,6 +34,7 @@ let argspec = Arg.align "-p", Arg.Set_int Flags.print_depth, " set print depth"; "-a", Arg.Set Flags.await_lowering, " translate async/await (implies -r)"; "-A", Arg.Set Flags.async_lowering, " translate async (implies -r)"; + "-iR", Arg.Set Flags.interpret_ir, " interpret the lowered code"; "-dp", Arg.Set Flags.dump_parse, " dump parse"; "-dt", Arg.Set Flags.dump_tc, " dump type-checked AST"; "-dl", Arg.Set Flags.dump_lowering, " dump lowering (requires -a)"; diff --git a/src/pipeline.ml b/src/pipeline.ml index 563851a0e63..3812bbad719 100644 --- a/src/pipeline.ml +++ b/src/pipeline.ml @@ -157,7 +157,12 @@ type interpret_result = let interpret_prog denv name prog : (Value.value * Interpret.scope) option = try phase "Interpreting" name; - let vo, scope = Interpret.interpret_prog denv prog in + let vo, scope = + if !Flags.interpret_ir + then + let prog_ir = Desugar.prog prog in + Interpret_ir.interpret_prog denv prog_ir + else Interpret.interpret_prog denv prog in match vo with | None -> None | Some v -> Some (v, scope) diff --git a/test/run-dfinity/ok/array-out-of-bounds.run-ir.ok b/test/run-dfinity/ok/array-out-of-bounds.run-ir.ok new file mode 100644 index 00000000000..48afd57d5df --- /dev/null +++ b/test/run-dfinity/ok/array-out-of-bounds.run-ir.ok @@ -0,0 +1,2 @@ +array-out-of-bounds.as:5.10-5.14: execution error, index out of bounds +array-out-of-bounds.as:9.10-9.14: execution error, index out of bounds diff --git a/test/run-dfinity/ok/async-loop-while.run-ir.ok b/test/run-dfinity/ok/async-loop-while.run-ir.ok new file mode 100644 index 00000000000..ac6ac408923 --- /dev/null +++ b/test/run-dfinity/ok/async-loop-while.run-ir.ok @@ -0,0 +1 @@ +012345678910012345678910012345678910012345678910 diff --git a/test/run-dfinity/ok/async-loop.run-ir.ok b/test/run-dfinity/ok/async-loop.run-ir.ok new file mode 100644 index 00000000000..ac6ac408923 --- /dev/null +++ b/test/run-dfinity/ok/async-loop.run-ir.ok @@ -0,0 +1 @@ +012345678910012345678910012345678910012345678910 diff --git a/test/run-dfinity/ok/async-new-obj.run-ir.ok b/test/run-dfinity/ok/async-new-obj.run-ir.ok new file mode 100644 index 00000000000..e07c8c8b770 --- /dev/null +++ b/test/run-dfinity/ok/async-new-obj.run-ir.ok @@ -0,0 +1,5 @@ +aaab +babb +dadb +eaeb +cacb diff --git a/test/run-dfinity/ok/async-obj-mut.run-ir.ok b/test/run-dfinity/ok/async-obj-mut.run-ir.ok new file mode 100644 index 00000000000..98f29c526f9 --- /dev/null +++ b/test/run-dfinity/ok/async-obj-mut.run-ir.ok @@ -0,0 +1,3 @@ +123 +done creating +345 diff --git a/test/run-dfinity/ok/async-while.run-ir.ok b/test/run-dfinity/ok/async-while.run-ir.ok new file mode 100644 index 00000000000..ac6ac408923 --- /dev/null +++ b/test/run-dfinity/ok/async-while.run-ir.ok @@ -0,0 +1 @@ +012345678910012345678910012345678910012345678910 diff --git a/test/run-dfinity/ok/chat.run-ir.ok b/test/run-dfinity/ok/chat.run-ir.ok new file mode 100644 index 00000000000..e21c87c7842 --- /dev/null +++ b/test/run-dfinity/ok/chat.run-ir.ok @@ -0,0 +1,18 @@ +charlie received hello from bob +charlie received hello from alice +charlie received hello from charlie +alice received hello from bob +alice received hello from alice +alice received hello from charlie +bob received hello from bob +bob received hello from alice +bob received hello from charlie +charlie received goodbye from bob +charlie received goodbye from alice +charlie received goodbye from charlie +alice received goodbye from bob +alice received goodbye from alice +alice received goodbye from charlie +bob received goodbye from bob +bob received goodbye from alice +bob received goodbye from charlie diff --git a/test/run-dfinity/ok/chatpp.run-ir.ok b/test/run-dfinity/ok/chatpp.run-ir.ok new file mode 100644 index 00000000000..4e43328a0b0 --- /dev/null +++ b/test/run-dfinity/ok/chatpp.run-ir.ok @@ -0,0 +1,15 @@ +charlie received hello from bob +alice received hello from bob +charlie received hello from alice +bob received hello from alice +alice received hello from charlie +bob received hello from charlie +charlie received goodbye from bob +alice received goodbye from bob +charlie received goodbye from alice +bob received goodbye from alice +alice received goodbye from charlie +bob received goodbye from charlie +(unsubscribe 0) +(unsubscribe 1) +(unsubscribe 2) diff --git a/test/run-dfinity/ok/closure-params.run-ir.ok b/test/run-dfinity/ok/closure-params.run-ir.ok new file mode 100644 index 00000000000..3372de22a99 --- /dev/null +++ b/test/run-dfinity/ok/closure-params.run-ir.ok @@ -0,0 +1,10 @@ +1 +3 +6 +10 +15 +1 +3 +6 +10 +15 diff --git a/test/run-dfinity/ok/counter.run-ir.ok b/test/run-dfinity/ok/counter.run-ir.ok new file mode 100644 index 00000000000..dc01807c8fe --- /dev/null +++ b/test/run-dfinity/ok/counter.run-ir.ok @@ -0,0 +1 @@ +2344 diff --git a/test/run-dfinity/ok/data-params.run-ir.ok b/test/run-dfinity/ok/data-params.run-ir.ok new file mode 100644 index 00000000000..e77c77a5d0a --- /dev/null +++ b/test/run-dfinity/ok/data-params.run-ir.ok @@ -0,0 +1,15 @@ +1 +3 +6 +10 +1010 +1021 +6021 +6045 +6091 +1006091 +1006105 +1006136 +1006171 +1006171 +Foo: 1006171 diff --git a/test/run-dfinity/ok/fac.run-ir.ok b/test/run-dfinity/ok/fac.run-ir.ok new file mode 100644 index 00000000000..52bd8e43afb --- /dev/null +++ b/test/run-dfinity/ok/fac.run-ir.ok @@ -0,0 +1 @@ +120 diff --git a/test/run-dfinity/ok/flatten-awaitables.run-ir.ok b/test/run-dfinity/ok/flatten-awaitables.run-ir.ok new file mode 100644 index 00000000000..421cb9c1b5e --- /dev/null +++ b/test/run-dfinity/ok/flatten-awaitables.run-ir.ok @@ -0,0 +1,29 @@ +flatten-awaitables.as:43.9-43.10: warning, this pattern does not cover all possible values +flatten-awaitables.as:45.9-45.17: warning, this pattern does not cover all possible values +flatten-awaitables.as:47.9-47.25: warning, this pattern does not cover all possible values +flatten-awaitables.as:52.9-52.10: warning, this pattern does not cover all possible values +flatten-awaitables.as:54.9-54.17: warning, this pattern does not cover all possible values +flatten-awaitables.as:56.9-56.25: warning, this pattern does not cover all possible values +flatten-awaitables.as:66.9-66.10: warning, this pattern does not cover all possible values +flatten-awaitables.as:68.9-68.17: warning, this pattern does not cover all possible values +flatten-awaitables.as:70.9-70.25: warning, this pattern does not cover all possible values +flatten-awaitables.as:75.9-75.10: warning, this pattern does not cover all possible values +flatten-awaitables.as:77.9-77.17: warning, this pattern does not cover all possible values +flatten-awaitables.as:79.9-79.25: warning, this pattern does not cover all possible values +flatten-awaitables.as:85.9-85.10: warning, this pattern does not cover all possible values +flatten-awaitables.as:87.9-87.17: warning, this pattern does not cover all possible values +flatten-awaitables.as:89.9-89.25: warning, this pattern does not cover all possible values +flatten-awaitables.as:94.9-94.10: warning, this pattern does not cover all possible values +flatten-awaitables.as:96.9-96.17: warning, this pattern does not cover all possible values +flatten-awaitables.as:98.9-98.25: warning, this pattern does not cover all possible values +flatten-awaitables.as:103.9-103.10: warning, this pattern does not cover all possible values +flatten-awaitables.as:105.9-105.17: warning, this pattern does not cover all possible values +flatten-awaitables.as:107.9-107.25: warning, this pattern does not cover all possible values +flatten-awaitables.as:112.9-112.10: warning, this pattern does not cover all possible values +flatten-awaitables.as:114.9-114.17: warning, this pattern does not cover all possible values +flatten-awaitables.as:116.9-116.25: warning, this pattern does not cover all possible values + +first-order +,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, +higher-order +,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, diff --git a/test/run-dfinity/ok/generic-tail-rec.run-ir.ok b/test/run-dfinity/ok/generic-tail-rec.run-ir.ok new file mode 100644 index 00000000000..6d96c93ceef --- /dev/null +++ b/test/run-dfinity/ok/generic-tail-rec.run-ir.ok @@ -0,0 +1,4 @@ +done 1 +done 2 +done 3 +done 4 diff --git a/test/run-dfinity/ok/hello-concat-world.run-ir.ok b/test/run-dfinity/ok/hello-concat-world.run-ir.ok new file mode 100644 index 00000000000..980a0d5f19a --- /dev/null +++ b/test/run-dfinity/ok/hello-concat-world.run-ir.ok @@ -0,0 +1 @@ +Hello World! diff --git a/test/run-dfinity/ok/hello-world-async.run-ir.ok b/test/run-dfinity/ok/hello-world-async.run-ir.ok new file mode 100644 index 00000000000..980a0d5f19a --- /dev/null +++ b/test/run-dfinity/ok/hello-world-async.run-ir.ok @@ -0,0 +1 @@ +Hello World! diff --git a/test/run-dfinity/ok/hello-world-await.run-ir.ok b/test/run-dfinity/ok/hello-world-await.run-ir.ok new file mode 100644 index 00000000000..980a0d5f19a --- /dev/null +++ b/test/run-dfinity/ok/hello-world-await.run-ir.ok @@ -0,0 +1 @@ +Hello World! diff --git a/test/run-dfinity/ok/hello-world.run-ir.ok b/test/run-dfinity/ok/hello-world.run-ir.ok new file mode 100644 index 00000000000..980a0d5f19a --- /dev/null +++ b/test/run-dfinity/ok/hello-world.run-ir.ok @@ -0,0 +1 @@ +Hello World! diff --git a/test/run-dfinity/ok/hello-world2.run-ir.ok b/test/run-dfinity/ok/hello-world2.run-ir.ok new file mode 100644 index 00000000000..980a0d5f19a --- /dev/null +++ b/test/run-dfinity/ok/hello-world2.run-ir.ok @@ -0,0 +1 @@ +Hello World! diff --git a/test/run-dfinity/ok/hello-world3.run-ir.ok b/test/run-dfinity/ok/hello-world3.run-ir.ok new file mode 100644 index 00000000000..980a0d5f19a --- /dev/null +++ b/test/run-dfinity/ok/hello-world3.run-ir.ok @@ -0,0 +1 @@ +Hello World! diff --git a/test/run-dfinity/ok/indirect-counter.run-ir.ok b/test/run-dfinity/ok/indirect-counter.run-ir.ok new file mode 100644 index 00000000000..dc01807c8fe --- /dev/null +++ b/test/run-dfinity/ok/indirect-counter.run-ir.ok @@ -0,0 +1 @@ +2344 diff --git a/test/run-dfinity/ok/nary-async.run-ir.ok b/test/run-dfinity/ok/nary-async.run-ir.ok new file mode 100644 index 00000000000..5b2baf03eb2 --- /dev/null +++ b/test/run-dfinity/ok/nary-async.run-ir.ok @@ -0,0 +1,11 @@ +0_0 +1_0 +2_0 +3_0 +0_0 +0_1 +0_2 +0_3 +!! +<()> +<(Int,Bool)> diff --git a/test/run-dfinity/ok/overflow.diff-ir.ok b/test/run-dfinity/ok/overflow.diff-ir.ok new file mode 100644 index 00000000000..b87c9311e18 --- /dev/null +++ b/test/run-dfinity/ok/overflow.diff-ir.ok @@ -0,0 +1,11 @@ +--- _out/overflow.run 2019-02-05 11:52:43.906832489 +0100 ++++ _out/overflow.run-ir 2019-02-05 11:52:43.926832554 +0100 +@@ -1,6 +1,6 @@ + This is reachable. + This is reachable. +-overflow.as:14.14-14.17: execution error, arithmetic overflow +-overflow.as:18.14-18.17: execution error, arithmetic overflow ++overflow.as:14.13-14.22: execution error, arithmetic overflow ++overflow.as:18.13-18.22: execution error, arithmetic overflow + This is reachable. + This is reachable. diff --git a/test/run-dfinity/ok/overflow.run-ir.ok b/test/run-dfinity/ok/overflow.run-ir.ok new file mode 100644 index 00000000000..f500785c833 --- /dev/null +++ b/test/run-dfinity/ok/overflow.run-ir.ok @@ -0,0 +1,6 @@ +This is reachable. +This is reachable. +overflow.as:14.13-14.22: execution error, arithmetic overflow +overflow.as:18.13-18.22: execution error, arithmetic overflow +This is reachable. +This is reachable. diff --git a/test/run-dfinity/ok/reference-params.run-ir.ok b/test/run-dfinity/ok/reference-params.run-ir.ok new file mode 100644 index 00000000000..8650e0ef3bf --- /dev/null +++ b/test/run-dfinity/ok/reference-params.run-ir.ok @@ -0,0 +1,5 @@ +Hello World! +Hello World! +Hello World! +Hello Universe! +Hello Galaxy! diff --git a/test/run-dfinity/ok/selftail.run-ir.ok b/test/run-dfinity/ok/selftail.run-ir.ok new file mode 100644 index 00000000000..acb397e19f2 --- /dev/null +++ b/test/run-dfinity/ok/selftail.run-ir.ok @@ -0,0 +1,2 @@ +ok1 +ok2 diff --git a/test/run-dfinity/ok/tailpositions.run-ir.ok b/test/run-dfinity/ok/tailpositions.run-ir.ok new file mode 100644 index 00000000000..025c12b0e9a --- /dev/null +++ b/test/run-dfinity/ok/tailpositions.run-ir.ok @@ -0,0 +1,7 @@ +done 1 +done 2 +done 3 +done 4 +done 5 +done 6 +done 7 diff --git a/test/run-dfinity/ok/the-answer.run-ir.ok b/test/run-dfinity/ok/the-answer.run-ir.ok new file mode 100644 index 00000000000..d81cc0710eb --- /dev/null +++ b/test/run-dfinity/ok/the-answer.run-ir.ok @@ -0,0 +1 @@ +42 diff --git a/test/run.sh b/test/run.sh index 0d58e057a84..52b2e3bb140 100755 --- a/test/run.sh +++ b/test/run.sh @@ -85,9 +85,17 @@ do diff_files="$diff_files $base.run-low" # Diff interpretations without/with lowering - echo -n " [diff-low]" diff -u -N $out/$base.run $out/$base.run-low > $out/$base.diff-low diff_files="$diff_files $base.diff-low" + + # Interpret IR + echo -n " [run-ir]" + $ASC $ASC_FLAGS -r -iR $base.as > $out/$base.run-ir 2>&1 + diff_files="$diff_files $base.run-ir" + + # Diff interpretations without/with lowering + diff -u -N $out/$base.run $out/$base.run-ir > $out/$base.diff-ir + diff_files="$diff_files $base.diff-ir" fi # Compile diff --git a/test/run/ok/actors.run-ir.ok b/test/run/ok/actors.run-ir.ok new file mode 100644 index 00000000000..1b2727b5701 --- /dev/null +++ b/test/run/ok/actors.run-ir.ok @@ -0,0 +1,2 @@ +actors.as:22.9-22.12: execution error, arithmetic overflow +actors.as:21.15-21.18: execution error, arithmetic overflow diff --git a/test/run/ok/array-bounds.run-ir.ok b/test/run/ok/array-bounds.run-ir.ok new file mode 100644 index 00000000000..62abb269136 --- /dev/null +++ b/test/run/ok/array-bounds.run-ir.ok @@ -0,0 +1 @@ +array-bounds.as:5.15-5.19: execution error, index out of bounds diff --git a/test/run/ok/assertFalse.run-ir.ok b/test/run/ok/assertFalse.run-ir.ok new file mode 100644 index 00000000000..1ade64fd09c --- /dev/null +++ b/test/run/ok/assertFalse.run-ir.ok @@ -0,0 +1 @@ +assertFalse.as:1.1-1.14: execution error, assertion failure diff --git a/test/run/ok/asyncreturn.run-ir.ok b/test/run/ok/asyncreturn.run-ir.ok new file mode 100644 index 00000000000..d81cc0710eb --- /dev/null +++ b/test/run/ok/asyncreturn.run-ir.ok @@ -0,0 +1 @@ +42 diff --git a/test/run/ok/await.run-ir.ok b/test/run/ok/await.run-ir.ok new file mode 100644 index 00000000000..08e3e459bc4 --- /dev/null +++ b/test/run/ok/await.run-ir.ok @@ -0,0 +1,16 @@ +abcdegholye-whileg-label.abcnt: 0 i: 0 +cnt: 1 i: 1 +cnt: 2 i: 2 +cnt: 3 i: 4 +cnt: 4 i: 5 +cnt: 5 i: 10 +.cnt: 6 i: 3 +cnt: 7 i: 6 +cnt: 8 i: 11 +.cnt: 9 i: 7 +cnt: 10 i: 12 +.cnt: 11 i: 8 +cnt: 12 i: 13 +.cnt: 13 i: 9 +cnt: 14 i: 14 +e-exitg-exit diff --git a/test/run/ok/bank-example.diff-ir.ok b/test/run/ok/bank-example.diff-ir.ok new file mode 100644 index 00000000000..d51050b5caa --- /dev/null +++ b/test/run/ok/bank-example.diff-ir.ok @@ -0,0 +1,4 @@ +--- _out/bank-example.run 2019-02-05 11:51:44.674640106 +0100 ++++ _out/bank-example.run-ir 2019-02-05 11:51:44.710640223 +0100 +@@ -0,0 +1 @@ ++bank-example.as:28.5-28.31: execution error, assertion failure diff --git a/test/run/ok/bank-example.run-ir.ok b/test/run/ok/bank-example.run-ir.ok new file mode 100644 index 00000000000..400d5c6c474 --- /dev/null +++ b/test/run/ok/bank-example.run-ir.ok @@ -0,0 +1 @@ +bank-example.as:28.5-28.31: execution error, assertion failure diff --git a/test/run/ok/block.run-ir.ok b/test/run/ok/block.run-ir.ok new file mode 100644 index 00000000000..456ed210645 --- /dev/null +++ b/test/run/ok/block.run-ir.ok @@ -0,0 +1 @@ +a1b11a2b2a3b3356 diff --git a/test/run/ok/coverage.run-ir.ok b/test/run/ok/coverage.run-ir.ok new file mode 100644 index 00000000000..2d5667ab33d --- /dev/null +++ b/test/run/ok/coverage.run-ir.ok @@ -0,0 +1,26 @@ +coverage.as:5.13-5.14: warning, this pattern is never matched +coverage.as:7.13-7.14: warning, this pattern is never matched +coverage.as:8.13-8.14: warning, this pattern is never matched +coverage.as:16.13-16.14: warning, this pattern is never matched +coverage.as:18.13-18.14: warning, this pattern is never matched +coverage.as:19.14-19.15: warning, this pattern is never matched +coverage.as:24.25-24.34: warning, this case is never reached +coverage.as:27.25-27.34: warning, this case is never reached +coverage.as:28.25-28.34: warning, this case is never reached +coverage.as:29.25-29.34: warning, this case is never reached +coverage.as:30.25-30.34: warning, this case is never reached +coverage.as:31.25-31.34: warning, this case is never reached +coverage.as:32.43-32.44: warning, this pattern is never matched +coverage.as:33.35-33.49: warning, this case is never reached +coverage.as:34.42-34.51: warning, this case is never reached +coverage.as:4.7-4.8: warning, this pattern does not cover all possible values +coverage.as:5.8-5.14: warning, this pattern does not cover all possible values +coverage.as:9.7-9.9: warning, this pattern does not cover all possible values +coverage.as:10.7-10.9: warning, this pattern does not cover all possible values +coverage.as:11.7-11.9: warning, this pattern does not cover all possible values +coverage.as:15.8-15.9: warning, this pattern does not cover all possible values +coverage.as:16.8-16.14: warning, this pattern does not cover all possible values +coverage.as:23.3-23.25: warning, the cases in this switch do not cover all possible values +coverage.as:24.3-24.36: warning, the cases in this switch do not cover all possible values +coverage.as:32.3-32.50: warning, the cases in this switch do not cover all possible values +coverage.as:35.3-35.51: warning, the cases in this switch do not cover all possible values diff --git a/test/run/ok/for.run-ir.ok b/test/run/ok/for.run-ir.ok new file mode 100644 index 00000000000..ac6ac408923 --- /dev/null +++ b/test/run/ok/for.run-ir.ok @@ -0,0 +1 @@ +012345678910012345678910012345678910012345678910 diff --git a/test/run/ok/is.diff-ir.ok b/test/run/ok/is.diff-ir.ok new file mode 100644 index 00000000000..3b899c6d0ab --- /dev/null +++ b/test/run/ok/is.diff-ir.ok @@ -0,0 +1,4 @@ +--- _out/is.run 2019-02-05 11:51:46.650646582 +0100 ++++ _out/is.run-ir 2019-02-05 11:51:46.682646688 +0100 +@@ -0,0 +1 @@ ++is.as:6.1-6.17: execution error, assertion failure diff --git a/test/run/ok/is.run-ir.ok b/test/run/ok/is.run-ir.ok new file mode 100644 index 00000000000..e8356c98d9a --- /dev/null +++ b/test/run/ok/is.run-ir.ok @@ -0,0 +1 @@ +is.as:6.1-6.17: execution error, assertion failure diff --git a/test/run/ok/overflow.diff-ir.ok b/test/run/ok/overflow.diff-ir.ok new file mode 100644 index 00000000000..495d4adb14c --- /dev/null +++ b/test/run/ok/overflow.diff-ir.ok @@ -0,0 +1,5 @@ +--- _out/overflow.run 2019-02-05 11:51:49.690656539 +0100 ++++ _out/overflow.run-ir 2019-02-05 11:51:49.706656591 +0100 +@@ -1 +1 @@ +-overflow.as:17.9-17.24: execution error, arithmetic overflow ++overflow.as:17.9-17.30: execution error, arithmetic overflow diff --git a/test/run/ok/overflow.run-ir.ok b/test/run/ok/overflow.run-ir.ok new file mode 100644 index 00000000000..2df157cb450 --- /dev/null +++ b/test/run/ok/overflow.run-ir.ok @@ -0,0 +1 @@ +overflow.as:17.9-17.30: execution error, arithmetic overflow diff --git a/test/run/ok/switch.run-ir.ok b/test/run/ok/switch.run-ir.ok new file mode 100644 index 00000000000..574eb979021 --- /dev/null +++ b/test/run/ok/switch.run-ir.ok @@ -0,0 +1,6 @@ +switch.as:87.33-87.34: warning, this pattern is never matched +switch.as:75.11-77.2: warning, the cases in this switch do not cover all possible values +switch.as:81.3-81.14: warning, the cases in this switch do not cover all possible values +switch.as:87.3-87.40: warning, the cases in this switch do not cover all possible values +switch.as:92.11-94.2: warning, the cases in this switch do not cover all possible values +switch.as:97.11-99.2: warning, the cases in this switch do not cover all possible values From a44dd8fe5e8a5da51b0b952e38bf58601cc08e86 Mon Sep 17 00:00:00 2001 From: Joachim Breitner Date: Tue, 5 Feb 2019 12:17:06 +0100 Subject: [PATCH 3/4] Keep inner region when desugaring AnnotE this makes the output from the two interpreters match in more cases (otherwise the error messages would have different source locations.) --- src/desugar.ml | 8 ++++++-- test/run-dfinity/ok/overflow.diff-ir.ok | 11 ----------- test/run-dfinity/ok/overflow.run-ir.ok | 4 ++-- test/run/ok/bank-example.diff-ir.ok | 4 ++-- test/run/ok/is.diff-ir.ok | 4 ++-- test/run/ok/overflow.diff-ir.ok | 5 ----- test/run/ok/overflow.run-ir.ok | 2 +- 7 files changed, 13 insertions(+), 25 deletions(-) delete mode 100644 test/run-dfinity/ok/overflow.diff-ir.ok delete mode 100644 test/run/ok/overflow.diff-ir.ok diff --git a/src/desugar.ml b/src/desugar.ml index b5183963dea..5487ba5f769 100644 --- a/src/desugar.ml +++ b/src/desugar.ml @@ -29,7 +29,11 @@ let phrase' f x = {x with it = f x.at x.note x.it} let rec exps es = List.map (exp) es - and exp e = phrase' exp' e + and exp e = + (* We short-cut AnnotE here, so that we get the position of the inner expression *) + match e.it with + | S.AnnotE (e,_) -> exp e + | _ -> phrase' exp' e and exp' at note = function | S.PrimE p -> I.PrimE p | S.VarE i -> I.VarE i @@ -86,7 +90,7 @@ let | S.AwaitE e -> I.AwaitE (exp e) | S.AssertE e -> I.AssertE (exp e) | S.IsE (e1, e2) -> I.IsE (exp e1, exp e2) - | S.AnnotE (e, _) -> exp' at note e.it + | S.AnnotE (_, _) -> assert false | S.DecE (d, ot) -> I.BlockE (decs [d], !ot) | S.DeclareE (i, t, e) -> I.DeclareE (i, t, exp e) | S.DefineE (i, m, e) -> I.DefineE (i, m, exp e) diff --git a/test/run-dfinity/ok/overflow.diff-ir.ok b/test/run-dfinity/ok/overflow.diff-ir.ok deleted file mode 100644 index b87c9311e18..00000000000 --- a/test/run-dfinity/ok/overflow.diff-ir.ok +++ /dev/null @@ -1,11 +0,0 @@ ---- _out/overflow.run 2019-02-05 11:52:43.906832489 +0100 -+++ _out/overflow.run-ir 2019-02-05 11:52:43.926832554 +0100 -@@ -1,6 +1,6 @@ - This is reachable. - This is reachable. --overflow.as:14.14-14.17: execution error, arithmetic overflow --overflow.as:18.14-18.17: execution error, arithmetic overflow -+overflow.as:14.13-14.22: execution error, arithmetic overflow -+overflow.as:18.13-18.22: execution error, arithmetic overflow - This is reachable. - This is reachable. diff --git a/test/run-dfinity/ok/overflow.run-ir.ok b/test/run-dfinity/ok/overflow.run-ir.ok index f500785c833..99ef5a02d51 100644 --- a/test/run-dfinity/ok/overflow.run-ir.ok +++ b/test/run-dfinity/ok/overflow.run-ir.ok @@ -1,6 +1,6 @@ This is reachable. This is reachable. -overflow.as:14.13-14.22: execution error, arithmetic overflow -overflow.as:18.13-18.22: execution error, arithmetic overflow +overflow.as:14.14-14.17: execution error, arithmetic overflow +overflow.as:18.14-18.17: execution error, arithmetic overflow This is reachable. This is reachable. diff --git a/test/run/ok/bank-example.diff-ir.ok b/test/run/ok/bank-example.diff-ir.ok index d51050b5caa..91e458cf48d 100644 --- a/test/run/ok/bank-example.diff-ir.ok +++ b/test/run/ok/bank-example.diff-ir.ok @@ -1,4 +1,4 @@ ---- _out/bank-example.run 2019-02-05 11:51:44.674640106 +0100 -+++ _out/bank-example.run-ir 2019-02-05 11:51:44.710640223 +0100 +--- _out/bank-example.run 2019-02-05 12:15:09.010731583 +0100 ++++ _out/bank-example.run-ir 2019-02-05 12:15:09.046731683 +0100 @@ -0,0 +1 @@ +bank-example.as:28.5-28.31: execution error, assertion failure diff --git a/test/run/ok/is.diff-ir.ok b/test/run/ok/is.diff-ir.ok index 3b899c6d0ab..779c9d6fb07 100644 --- a/test/run/ok/is.diff-ir.ok +++ b/test/run/ok/is.diff-ir.ok @@ -1,4 +1,4 @@ ---- _out/is.run 2019-02-05 11:51:46.650646582 +0100 -+++ _out/is.run-ir 2019-02-05 11:51:46.682646688 +0100 +--- _out/is.run 2019-02-05 12:15:10.986737038 +0100 ++++ _out/is.run-ir 2019-02-05 12:15:11.018737127 +0100 @@ -0,0 +1 @@ +is.as:6.1-6.17: execution error, assertion failure diff --git a/test/run/ok/overflow.diff-ir.ok b/test/run/ok/overflow.diff-ir.ok deleted file mode 100644 index 495d4adb14c..00000000000 --- a/test/run/ok/overflow.diff-ir.ok +++ /dev/null @@ -1,5 +0,0 @@ ---- _out/overflow.run 2019-02-05 11:51:49.690656539 +0100 -+++ _out/overflow.run-ir 2019-02-05 11:51:49.706656591 +0100 -@@ -1 +1 @@ --overflow.as:17.9-17.24: execution error, arithmetic overflow -+overflow.as:17.9-17.30: execution error, arithmetic overflow diff --git a/test/run/ok/overflow.run-ir.ok b/test/run/ok/overflow.run-ir.ok index 2df157cb450..fa160f7b0c0 100644 --- a/test/run/ok/overflow.run-ir.ok +++ b/test/run/ok/overflow.run-ir.ok @@ -1 +1 @@ -overflow.as:17.9-17.30: execution error, arithmetic overflow +overflow.as:17.9-17.24: execution error, arithmetic overflow From 7590fe8e8a3186179f5f60ce28f9d6c6b4fd49b7 Mon Sep 17 00:00:00 2001 From: Joachim Breitner Date: Tue, 5 Feb 2019 12:22:27 +0100 Subject: [PATCH 4/4] Test suite: Avoid leaking time-stamps into the `ok/*.diff-*.ok` files. --- test/run.sh | 6 +++--- test/run/ok/bank-example.diff-ir.ok | 4 ++-- test/run/ok/is.diff-ir.ok | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/run.sh b/test/run.sh index 52b2e3bb140..0e7587c4071 100755 --- a/test/run.sh +++ b/test/run.sh @@ -85,7 +85,7 @@ do diff_files="$diff_files $base.run-low" # Diff interpretations without/with lowering - diff -u -N $out/$base.run $out/$base.run-low > $out/$base.diff-low + diff -u -N --label "$base.run" $out/$base.run --label "$base.run-low" $out/$base.run-low > $out/$base.diff-low diff_files="$diff_files $base.diff-low" # Interpret IR @@ -94,7 +94,7 @@ do diff_files="$diff_files $base.run-ir" # Diff interpretations without/with lowering - diff -u -N $out/$base.run $out/$base.run-ir > $out/$base.diff-ir + diff -u -N --label "$base.run" $out/$base.run --label "$base.run-ir" $out/$base.run-ir > $out/$base.diff-ir diff_files="$diff_files $base.diff-ir" fi @@ -155,7 +155,7 @@ do else for file in $diff_files do - diff -a -u -N $ok/$file.ok $out/$file + diff -a -u -N --label "$file (expected)" $ok/$file.ok --label "$file (actual)" $out/$file if [ $? != 0 ]; then failures=yes; fi done fi diff --git a/test/run/ok/bank-example.diff-ir.ok b/test/run/ok/bank-example.diff-ir.ok index 91e458cf48d..549a80b0421 100644 --- a/test/run/ok/bank-example.diff-ir.ok +++ b/test/run/ok/bank-example.diff-ir.ok @@ -1,4 +1,4 @@ ---- _out/bank-example.run 2019-02-05 12:15:09.010731583 +0100 -+++ _out/bank-example.run-ir 2019-02-05 12:15:09.046731683 +0100 +--- bank-example.run ++++ bank-example.run-ir @@ -0,0 +1 @@ +bank-example.as:28.5-28.31: execution error, assertion failure diff --git a/test/run/ok/is.diff-ir.ok b/test/run/ok/is.diff-ir.ok index 779c9d6fb07..477c449f431 100644 --- a/test/run/ok/is.diff-ir.ok +++ b/test/run/ok/is.diff-ir.ok @@ -1,4 +1,4 @@ ---- _out/is.run 2019-02-05 12:15:10.986737038 +0100 -+++ _out/is.run-ir 2019-02-05 12:15:11.018737127 +0100 +--- is.run ++++ is.run-ir @@ -0,0 +1 @@ +is.as:6.1-6.17: execution error, assertion failure