Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[work in progress] Coroutines for Haxe #11554

Draft
wants to merge 47 commits into
base: development
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
86cebca
try a different approach
Simn Feb 6, 2024
bbd726b
fix
Simn Feb 6, 2024
ffd5860
Merge branch 'development' into coroutines_2024
Simn Feb 8, 2024
4126322
Merge branch 'development' into coroutines_2024
Simn Feb 9, 2024
de3e495
Merge branch 'development' into coroutines_2024
Simn Feb 14, 2024
b327ecd
merge
Simn Feb 14, 2024
3a49bf2
remove analyzer changes
Simn Feb 14, 2024
cfe1b31
clean up next state setting
Simn Feb 14, 2024
5a394b0
turn coro state into a record
Simn Feb 14, 2024
e8e19b8
most importantly: dot graphs
Simn Feb 14, 2024
b963967
get something working on JVM
Simn Feb 14, 2024
fb857ab
somehow make suspend work on JVM
Simn Feb 14, 2024
0ff8e29
run it on eval too
Simn Feb 14, 2024
913e74a
add expand_coro_type
Simn Feb 14, 2024
04d73eb
introduce haxe.coro package
Simn Feb 14, 2024
6d986dc
add haxe.coro.Continuation
Simn Feb 14, 2024
5698a41
just @:keep for now
Simn Feb 14, 2024
2d9ba3a
don't put Void in result
Simn Feb 14, 2024
e09e259
run on C++, mostly
Simn Feb 14, 2024
de7e54c
use asys branch for now
Simn Feb 14, 2024
0e2f532
use custom branch
Simn Feb 14, 2024
2ae66e8
no .exe
Simn Feb 14, 2024
b8d8e6f
deal with `this` properly
Simn Feb 15, 2024
60c8e64
add some unreachable block sanity checks
Simn Feb 15, 2024
e857bbe
give blocks IDs and track fall-through connections
Simn Feb 15, 2024
3e91fcd
track all CFG edges
Simn Feb 15, 2024
679c7cc
use block IDs as state IDs
Simn Feb 15, 2024
9b30386
skip forwarding states
Simn Feb 15, 2024
62bdf66
ignore next after unconditional termination
Simn Feb 15, 2024
9f0238f
add yield tests from https://github.com/nadako/haxe-coroutines/pull/6
Simn Feb 15, 2024
9ff9964
add Coroutine special case to gencpp visit_type
Simn Feb 15, 2024
7377cc3
invert do try to try do
Simn Feb 15, 2024
28fdaa4
still replace `this` inside TFunction
Simn Feb 15, 2024
d1d01fe
set rethrow state before rethrowing
Simn Feb 16, 2024
22dfa2e
deal with `this`, again
Simn Feb 16, 2024
d509e86
implement simon-exceptions
Simn Feb 16, 2024
a71536b
Warnings
kLabz Feb 16, 2024
d75e37d
[macro] get build order into control
kLabz Feb 16, 2024
bf3d81a
[tests] use coroutines for server tests
kLabz Feb 16, 2024
72a202c
[ci] use utest fork coro branch for now
kLabz Feb 16, 2024
b0796e1
add build-hl.hxml
Simn Feb 16, 2024
4213732
don't assign e_no_value
Simn Feb 16, 2024
f430997
Merge branch 'coroutines_2024' into coroutines_2024_server_tests
Simn Feb 16, 2024
d52d1b6
Merge branch 'development' into coroutines_2024
Simn Feb 16, 2024
ba7604b
Merge branch 'coroutines_2024_server_tests' into coroutines_2024
Simn Feb 16, 2024
78a7ced
Make hl works for tests
yuxiaomao Mar 6, 2024
ce6e328
run coro tests on HL CI
Simn Mar 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,4 @@ lib.sexp
src/compiler/version.ml
tests/party
tests/misc/projects/Issue10863/error.log
tests/misc/coroutines/dump
6 changes: 6 additions & 0 deletions src-json/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@
"targets": ["TAbstract"],
"links": ["https://haxe.org/manual/types-abstract-core-type.html"]
},
{
"name": "Coroutine",
"metadata": ":coroutine",
"doc": "Transform function into a coroutine",
"targets": ["TClassField"]
},
{
"name": "CppFileCode",
"metadata": ":cppFileCode",
Expand Down
10 changes: 5 additions & 5 deletions src/codegen/codegen.ml
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,9 @@ let rec find_field com c f =
f

let fix_override com c f fd =
let f2 = (try Some (find_field com c f) with Not_found -> None) in
match f2,fd with
| Some (f2), Some(fd) ->
let targs, tret = (match follow f2.cf_type with TFun (args,ret) -> args, ret | _ -> die "" __LOC__) in
let f2 = find_field com c f in
match follow f2.cf_type,fd with
| TFun(targs,tret), Some(fd) ->
let changed_args = ref [] in
let prefix = "_tmp_" in
let nargs = List.map2 (fun ((v,ct) as cur) (_,_,t2) ->
Expand Down Expand Up @@ -148,13 +147,14 @@ let fix_override com c f fd =
let fde = (match f.cf_expr with None -> die "" __LOC__ | Some e -> e) in
f.cf_expr <- Some { fde with eexpr = TFunction fd2 };
f.cf_type <- TFun(targs,tret);
| Some(f2), None when (has_class_flag c CInterface) ->
| _, None when (has_class_flag c CInterface) ->
let targs, tret = (match follow f2.cf_type with TFun (args,ret) -> args, ret | _ -> die "" __LOC__) in
f.cf_type <- TFun(targs,tret)
| _ ->
()

let fix_overrides com t =
let fix_override com c f fd = try fix_override com c f fd with Not_found -> () in
match t with
| TClassDecl c ->
(* overrides can be removed from interfaces *)
Expand Down
8 changes: 8 additions & 0 deletions src/context/common.ml
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,7 @@ let create compilation_step cs version args display_mode =
tstring = mk_mono();
tnull = (fun _ -> die "Could use locate abstract Null<T> (was it redefined?)" __LOC__);
tarray = (fun _ -> die "Could not locate class Array<T> (was it redefined?)" __LOC__);
tcoro = (fun _ -> die "Could not locate abstract Coroutine<T> (was it redefined?)" __LOC__);
};
std = null_class;
file_keys = new file_keys;
Expand Down Expand Up @@ -1218,3 +1219,10 @@ let get_entry_point com =
let e = Option.get com.main.main_expr in (* must be present at this point *)
(snd path, c, e)
) com.main.main_class

let expand_coro_type basic args ret =
let ret_type = if ExtType.is_void (follow ret) then t_dynamic else ret in
let tcontinuation = tfun [ret_type; t_dynamic] basic.tvoid in
let args = args @ [("_hx_continuation",false,tcontinuation)] in
let ret = tfun [t_dynamic; t_dynamic] basic.tvoid in
(args,ret)
13 changes: 13 additions & 0 deletions src/context/dotGraph.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
open Common

let get_dump_path com path name =
(dump_path com) :: [platform_name_macro com] @ (fst path) @ [Printf.sprintf "%s.%s" (snd path) name]

let start_graph ?(graph_config=[]) base_path suffix =
let ch = Path.create_file false suffix [] base_path in
Printf.fprintf ch "digraph graphname {\n";
List.iter (fun s -> Printf.fprintf ch "%s;\n" s) graph_config;
ch,(fun () ->
Printf.fprintf ch "}\n";
close_out ch
)
22 changes: 18 additions & 4 deletions src/context/typecore.ml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ type typer_pass_tasks = {
mutable tasks : (unit -> unit) list;
}

type function_mode =
| FunFunction
| FunCoroutine
| FunNotFunction

type typer_globals = {
mutable delayed : typer_pass_tasks Array.t;
mutable delayed_min_index : int;
Expand Down Expand Up @@ -138,7 +143,7 @@ type typer_globals = {
is shared by local TFunctions. *)
and typer_expr = {
curfun : current_fun;
in_function : bool;
function_mode : function_mode;
mutable ret : t;
mutable opened : anon_status ref list;
mutable monomorphs : monomorphs;
Expand Down Expand Up @@ -235,10 +240,10 @@ module TyperManager = struct
in_call_args = false;
}

let create_ctx_e curfun in_function =
let create_ctx_e curfun function_mode =
{
curfun;
in_function;
function_mode;
ret = t_dynamic;
opened = [];
monomorphs = {
Expand Down Expand Up @@ -291,8 +296,17 @@ module TyperManager = struct

let clone_for_type_parameter_expression ctx =
let f = create_ctx_f ctx.f.curfield in
let e = create_ctx_e ctx.e.curfun false in
let e = create_ctx_e ctx.e.curfun FunNotFunction in
create ctx ctx.m ctx.c f e PTypeField ctx.type_params

let is_coroutine_context ctx =
ctx.e.function_mode = FunCoroutine

let is_function_context ctx = match ctx.e.function_mode with
| FunFunction | FunCoroutine ->
true
| FunNotFunction ->
false
end

type field_host =
Expand Down
15 changes: 15 additions & 0 deletions src/core/tFunctions.ml
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,21 @@ let rec follow_lazy_and_mono t = match t with
| _ ->
t

type maybe_coro =
| Coro of tsignature
| NotCoro of t

let follow_with_coro t = match follow t with
| TAbstract({a_path = (["haxe";"coro"],"Coroutine")},[t]) ->
begin match follow t with
| TFun(args,ret) ->
Coro (args,ret)
| t ->
NotCoro t
end
| t ->
NotCoro t

let rec ambiguate_funs t =
match follow t with
| TFun _ -> TFun ([], t_dynamic)
Expand Down
1 change: 1 addition & 0 deletions src/core/tType.ml
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ type basic_types = {
mutable tnull : t -> t;
mutable tstring : t;
mutable tarray : t -> t;
mutable tcoro : (string * bool * t) list -> t -> t;
}

type class_field_scope =
Expand Down
28 changes: 28 additions & 0 deletions src/core/texpr.ml
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,16 @@ module Builder = struct
let index basic e index t p =
mk (TArray (e,mk (TConst (TInt (Int32.of_int index))) basic.tint p)) t p

let default_value t p = match follow_without_null t with
| TAbstract({a_path = ([],"Int")},[]) ->
mk (TConst (TInt (Int32.zero))) t p
| TAbstract({a_path = ([],"Float")},[]) ->
mk (TConst (TFloat "0.0")) t p
| TAbstract({a_path = ([],"Bool")},[]) ->
mk (TConst (TBool false)) t p
| _ ->
mk (TConst TNull) t p

let resolve_and_make_static_call c name args p =
ignore(c.cl_build());
let cf = try
Expand Down Expand Up @@ -653,6 +663,24 @@ let for_remap basic v e1 e2 p =
mk (TWhile((mk (TParenthesis ehasnext) ehasnext.etype ehasnext.epos),ebody,NormalWhile)) basic.tvoid e1.epos;
]) basic.tvoid p

let not_while_true_to_while_true basic e1 e2 flag t p =
let e_break = mk TBreak t_dynamic p in
let e_not = mk (TUnop(Not,Prefix,Builder.mk_parent e1)) e1.etype e1.epos in
let e_if eo = mk (TIf(e_not,e_break,eo)) basic.tvoid p in
let rec map_continue e = match e.eexpr with
| TContinue ->
duplicate_tvars e_identity (e_if (Some e))
| TWhile _ | TFor _ ->
e
| _ ->
map_expr map_continue e
in
let e2 = if flag = NormalWhile then e2 else map_continue e2 in
let e_if = e_if None in
let e_block = if flag = NormalWhile then concat e_if e2 else concat e2 e_if in
let e_true = mk (TConst (TBool true)) basic.tbool p in
mk (TWhile(e_true,e_block,NormalWhile)) t p

(* -------------------------------------------------------------------------- *)
(* BUILD META DATA OBJECT *)

Expand Down
35 changes: 35 additions & 0 deletions src/coro/coro.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
open Globals
open Type
open CoroTypes
open CoroFunctions

let fun_to_coro ctx e tf =
let p = e.epos in
let v_result = alloc_var VGenerated "_hx_result" t_dynamic p in
let v_error = alloc_var VGenerated "_hx_error" t_dynamic p in
let cb_root = make_block ctx (Some(e.etype,p)) in
ignore(CoroFromTexpr.expr_to_coro ctx (v_result,v_error) cb_root tf.tf_expr);
let ret_type = if ExtType.is_void (follow tf.tf_type) then t_dynamic else tf.tf_type in
let vcontinuation = alloc_var VGenerated "_hx_continuation" (tfun [ret_type; t_dynamic] ctx.com.basic.tvoid) p in
let tf_expr = CoroToTexpr.block_to_texpr_coroutine ctx cb_root vcontinuation v_result v_error e.epos in
let tf_args = tf.tf_args @ [(vcontinuation,None)] in
let tf_type = tfun [t_dynamic; t_dynamic] ctx.com.basic.tvoid in
if ctx.coro_debug then begin
print_endline ("BEFORE:\n" ^ (s_expr_debug e));
CoroDebug.create_dotgraph (DotGraph.get_dump_path ctx.com ([],e.epos.pfile) (Printf.sprintf "pos_%i" e.epos.pmin)) cb_root
end;
let e = {e with eexpr = TFunction {tf_args; tf_expr; tf_type}} in
if ctx.coro_debug then print_endline ("AFTER:\n" ^ (s_expr_debug e));
e

let create_coro_context com meta =
let ctx = {
com;
coro_debug = Meta.has (Meta.Custom ":coroutine.debug") meta;
vthis = None;
next_block_id = 0;
cb_unreachable = Obj.magic "";
current_catch = None;
} in
ctx.cb_unreachable <- make_block ctx None;
ctx
91 changes: 91 additions & 0 deletions src/coro/coroDebug.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@

open CoroTypes
open Type

let create_dotgraph path cb =
print_endline (String.concat "." path);
let ch,close = DotGraph.start_graph path "coro" in
let pctx = print_context() in
let st = s_type pctx in
let se = s_expr_pretty true "" false st in
let edges = DynArray.create () in
let rec block cb =
let edge_block label cb_target =
block cb_target;
DynArray.add edges (cb.cb_id,cb_target.cb_id,label,true);
in
let s = String.concat "\n" (DynArray.to_list (DynArray.map se cb.cb_el)) in
let s = if s = "" then Printf.sprintf "(%i)" cb.cb_id else Printf.sprintf "(%i)\n%s" cb.cb_id s in
let snext = match cb.cb_next.next_kind with
| NextUnknown ->
None
| NextSub(cb_sub,cb_next) ->
edge_block "next" cb_next;
edge_block "sub" cb_sub;
None
| NextBreak cb_break ->
DynArray.add edges (cb.cb_id,cb_break.cb_id,"goto",false);
Some "break"
| NextContinue cb_continue ->
DynArray.add edges (cb.cb_id,cb_continue.cb_id,"goto",false);
Some "continue"
| NextReturnVoid ->
Some "return"
| NextReturn e ->
Some ("return " ^ se e)
| NextThrow e ->
Some ("throw " ^ se e)
| NextIfThen(e,cb_then,cb_next) ->
edge_block "next" cb_next;
edge_block "then" cb_then;
Some ("if " ^ se e)
| NextIfThenElse(e,cb_then,cb_else,cb_next) ->
edge_block "next" cb_next;
edge_block "then" cb_then;
edge_block "else" cb_else;
Some ("if " ^ se e)
| NextSwitch(switch,cb_next) ->
edge_block "next" cb_next;
List.iter (fun (el,cb_case) ->
edge_block (String.concat " | " (List.map se el)) cb_case
) switch.cs_cases;
Option.may (fun cb_default -> edge_block "default" cb_default) switch.cs_default;
Some ("switch " ^ se switch.cs_subject)
| NextWhile(e,cb_body,cb_next) ->
edge_block "next" cb_next;
edge_block "body" cb_body;
Some ("while " ^ se e)
| NextTry(cb_try,catch,cb_next) ->
edge_block "next" cb_next;
edge_block "try" cb_try;
DynArray.add edges (cb_try.cb_id,catch.cc_cb.cb_id,"catch",true);
Printf.fprintf ch "n%i [shape=box,label=\"(%i)\"];\n" catch.cc_cb.cb_id catch.cc_cb.cb_id;
List.iter (fun (v,cb_catch) ->
block cb_catch;
DynArray.add edges (catch.cc_cb.cb_id,cb_catch.cb_id,(st v.v_type),true);
) catch.cc_catches;
None
| NextSuspend(suspend,cb_next) ->
edge_block "next" cb_next;
Some (Printf.sprintf "%s(%s)" (se suspend.cs_fun) (String.concat ", " (List.map se suspend.cs_args)))
| NextFallThrough cb_next ->
DynArray.add edges (cb.cb_id,cb_next.cb_id,"fall-through",false);
None
| NextGoto cb_next ->
DynArray.add edges (cb.cb_id,cb_next.cb_id,"goto",false);
None
in
let s = match snext with
| None ->
s
| Some snext ->
if s = "" then snext else s ^ "\n" ^ snext
in
Printf.fprintf ch "n%i [shape=box,label=\"%s\"];\n" cb.cb_id (StringHelper.s_escape s);
in
ignore(block cb);
DynArray.iter (fun (id_from,id_to,label,tree_edge) ->
let style = if tree_edge then "style=\"solid\",color=\"black\"" else "style=\"dashed\", color=\"lightgray\"" in
Printf.fprintf ch "n%i -> n%i[%s label=\"%s\"];\n" id_from id_to style (StringHelper.s_escape label);
) edges;
close();
Loading