Skip to content

Commit c63b7df

Browse files
committed
TS backend phase 0 - Typed lambda
This is a split from #8118 and explains the most basic part of the entire work. Therefore, this should provide sufficient explanation to the LLM agents and should not conflict with other backend modifications (e.g., sourcemap) yet. - Before: Gentype; a separated pipeline that parses and reanalyzes serialized type information. - After: Attach the full type information to the Lambda IR and pass it directly to the codegen backend. Although the compiler's intermediate layer still does not use type information, passing through it simplifying the type-related code generation as it can directly access already computed type information without any extra parsing. Next phases: - 1. Implementing dts file emission: Provide it as an additional option. Stabilizes the dts format independently of the existing JS IR and deprecates Gentype. - 2. Rewriting codegen backend entirely: New, fully-typed, well-structured codegen IR that unifies all the fragmented codegen logic. It should covers JS/TS/d.ts in a single path (Need research. It may not be possible or complicated) - Implementation only: JavaScript output - Type only: d.ts output - Implementation + Type: TypeScript output
1 parent 516e650 commit c63b7df

45 files changed

Lines changed: 590 additions & 261 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

compiler/bsc/rescript_compiler_main.ml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,12 @@ let command_line_flags : (string * Bsc_args.spec * string) array =
397397
("-dtypedtree", set Clflags.dump_typedtree, "*internal* debug typedtree");
398398
("-dparsetree", set Clflags.dump_parsetree, "*internal* debug parsetree");
399399
("-drawlambda", set Clflags.dump_rawlambda, "*internal* debug raw lambda");
400+
( "-dlamtypes",
401+
set Clflags.dump_lamtypes,
402+
"*internal* dump Lam IR type annotations" );
403+
( "-emit-typedefs",
404+
set Clflags.emit_typedefs,
405+
"*internal* emit .d.ts declarations" );
400406
("-dsource", set Clflags.dump_source, "*internal* print source");
401407
( "-reprint-source",
402408
string_call reprint_source_file,

compiler/core/js_implementation.ml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ let after_parsing_impl ppf outputprefix (ast : Parsetree.structure) =
133133
Lam_compile_env.reset ();
134134
let env = Res_compmisc.initial_env ~modulename () in
135135
Env.set_unit_name modulename;
136-
let typedtree, coercion, _, _ =
136+
let typedtree, coercion, finalenv, _ =
137137
Typemod.type_implementation_more
138138
?check_exists:(if !Js_config.force_cmi then None else Some ())
139139
!Location.input_name outputprefix modulename env ast
@@ -143,8 +143,9 @@ let after_parsing_impl ppf outputprefix (ast : Parsetree.structure) =
143143
Printtyped.implementation_with_coercion typedtree_coercion;
144144
(if !Js_config.cmi_only then Warnings.check_fatal ()
145145
else
146-
let lambda, exports =
147-
Translmod.transl_implementation modulename typedtree_coercion
146+
let env, lambda, exports =
147+
Translmod.transl_implementation modulename finalenv
148+
typedtree_coercion
148149
in
149150
let js_program =
150151
print_if_pipe ppf Clflags.dump_rawlambda Printlambda.lambda lambda

compiler/core/lam.ml

Lines changed: 65 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ type ap_info = {
3131
ap_status: apply_status;
3232
}
3333

34-
module Types = struct
34+
module Lam_types = struct
3535
type lambda_switch = {
3636
sw_consts_full: bool;
3737
(* TODO: refine its representation *)
@@ -47,6 +47,7 @@ module Types = struct
4747
params: ident list;
4848
body: t;
4949
attr: Lambda.function_attribute;
50+
ty: Types.type_expr option;
5051
}
5152

5253
(*
@@ -86,6 +87,7 @@ module Types = struct
8687
ap_args: t list;
8788
ap_info: ap_info;
8889
ap_transformed_jsx: bool;
90+
ap_result_type: Types.type_expr option;
8991
}
9092

9193
and t =
@@ -94,7 +96,7 @@ module Types = struct
9496
| Lconst of Lam_constant.t
9597
| Lapply of apply
9698
| Lfunction of lfunction
97-
| Llet of Lam_compat.let_kind * ident * t * t
99+
| Llet of Lam_compat.let_kind * ident * Types.type_expr option * t * t
98100
| Lletrec of (ident * t) list * t
99101
| Lprim of prim_info
100102
| Lswitch of t * lambda_switch
@@ -115,7 +117,7 @@ module Types = struct
115117
end
116118

117119
module X = struct
118-
type lambda_switch = Types.lambda_switch = {
120+
type lambda_switch = Lam_types.lambda_switch = {
119121
sw_consts_full: bool;
120122
sw_consts: (int * t) list;
121123
sw_blocks_full: bool;
@@ -124,33 +126,35 @@ module X = struct
124126
sw_names: Ast_untagged_variants.switch_names option;
125127
}
126128

127-
and prim_info = Types.prim_info = {
129+
and prim_info = Lam_types.prim_info = {
128130
primitive: Lam_primitive.t;
129131
args: t list;
130132
loc: Location.t;
131133
}
132134

133-
and apply = Types.apply = {
135+
and apply = Lam_types.apply = {
134136
ap_func: t;
135137
ap_args: t list;
136138
ap_info: ap_info;
137139
ap_transformed_jsx: bool;
140+
ap_result_type: Types.type_expr option;
138141
}
139142

140-
and lfunction = Types.lfunction = {
143+
and lfunction = Lam_types.lfunction = {
141144
arity: int;
142145
params: ident list;
143146
body: t;
144147
attr: Lambda.function_attribute;
148+
ty: Types.type_expr option;
145149
}
146150

147-
and t = Types.t =
151+
and t = Lam_types.t =
148152
| Lvar of ident
149153
| Lglobal_module of ident * bool
150154
| Lconst of Lam_constant.t
151155
| Lapply of apply
152156
| Lfunction of lfunction
153-
| Llet of Lam_compat.let_kind * ident * t * t
157+
| Llet of Lam_compat.let_kind * ident * Types.type_expr option * t * t
154158
| Lletrec of (ident * t) list * t
155159
| Lprim of prim_info
156160
| Lswitch of t * lambda_switch
@@ -170,24 +174,24 @@ module X = struct
170174
(* | Lsend of Lam_compat.meth_kind * t * t * t list * Location.t *)
171175
end
172176

173-
include Types
177+
include Lam_types
174178

175179
(** apply [f] to direct successor which has type [Lam.t] *)
176180

177181
let inner_map (l : t) (f : t -> X.t) : X.t =
178182
match l with
179183
| Lvar (_ : ident) | Lconst (_ : Lam_constant.t) -> ((* Obj.magic *) l : X.t)
180-
| Lapply {ap_func; ap_args; ap_info; ap_transformed_jsx} ->
184+
| Lapply {ap_func; ap_args; ap_info; ap_transformed_jsx; ap_result_type} ->
181185
let ap_func = f ap_func in
182186
let ap_args = Ext_list.map ap_args f in
183-
Lapply {ap_func; ap_args; ap_info; ap_transformed_jsx}
184-
| Lfunction {body; arity; params; attr} ->
187+
Lapply {ap_func; ap_args; ap_info; ap_transformed_jsx; ap_result_type}
188+
| Lfunction {body; arity; params; attr; ty} ->
185189
let body = f body in
186-
Lfunction {body; arity; params; attr}
187-
| Llet (str, id, arg, body) ->
190+
Lfunction {body; arity; params; attr; ty}
191+
| Llet (str, id, ty, arg, body) ->
188192
let arg = f arg in
189193
let body = f body in
190-
Llet (str, id, arg, body)
194+
Llet (str, id, ty, arg, body)
191195
| Lletrec (decl, body) ->
192196
let body = f body in
193197
let decl = Ext_list.map_snd decl f in
@@ -307,7 +311,8 @@ let rec is_eta_conversion_exn params inner_args outer_args : t list =
307311
| _, _, _ -> raise_notrace Not_simple_form
308312

309313
(** FIXME: more robust inlining check later, we should inline it before we add stub code*)
310-
let rec apply ?(ap_transformed_jsx = false) fn args (ap_info : ap_info) : t =
314+
let rec apply ?(ap_transformed_jsx = false)
315+
~(ap_result_type : Types.type_expr option) fn args (ap_info : ap_info) : t =
311316
match fn with
312317
| Lfunction
313318
{
@@ -328,7 +333,14 @@ let rec apply ?(ap_transformed_jsx = false) fn args (ap_info : ap_info) : t =
328333
Lprim
329334
{primitive = wrap; args = [Lprim {primitive_call with args; loc}]; loc}
330335
| exception Not_simple_form ->
331-
Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx})
336+
Lapply
337+
{
338+
ap_func = fn;
339+
ap_args = args;
340+
ap_info;
341+
ap_transformed_jsx;
342+
ap_result_type;
343+
})
332344
| Lfunction
333345
{
334346
params;
@@ -337,7 +349,14 @@ let rec apply ?(ap_transformed_jsx = false) fn args (ap_info : ap_info) : t =
337349
match is_eta_conversion_exn params inner_args args with
338350
| args -> Lprim {primitive_call with args; loc = ap_info.ap_loc}
339351
| exception _ ->
340-
Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx})
352+
Lapply
353+
{
354+
ap_func = fn;
355+
ap_args = args;
356+
ap_info;
357+
ap_transformed_jsx;
358+
ap_result_type;
359+
})
341360
| Lfunction
342361
{
343362
params;
@@ -350,17 +369,37 @@ let rec apply ?(ap_transformed_jsx = false) fn args (ap_info : ap_info) : t =
350369
| args ->
351370
Lsequence (Lprim {primitive_call with args; loc = ap_info.ap_loc}, const)
352371
| exception _ ->
353-
Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx}
372+
Lapply
373+
{
374+
ap_func = fn;
375+
ap_args = args;
376+
ap_info;
377+
ap_transformed_jsx;
378+
ap_result_type;
379+
}
354380
(* | Lfunction {params;body} when Ext_list.same_length params args ->
355381
Ext_list.fold_right2 (fun p arg acc ->
356382
Llet(Strict,p,arg,acc)
357383
) params args body *)
358384
(* TODO: more rigirous analysis on [let_kind] *))
359-
| Llet (kind, id, e, (Lfunction _ as fn)) ->
360-
Llet (kind, id, e, apply fn args ap_info ~ap_transformed_jsx)
385+
| Llet (kind, id, ty, e, (Lfunction _ as fn)) ->
386+
Llet
387+
( kind,
388+
id,
389+
ty,
390+
e,
391+
apply fn args ap_info ~ap_transformed_jsx ~ap_result_type )
361392
(* | Llet (kind0, id0, e0, Llet (kind,id, e, (Lfunction _ as fn))) ->
362393
Llet(kind0,id0,e0,Llet (kind, id, e, apply fn args loc status)) *)
363-
| _ -> Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx}
394+
| _ ->
395+
Lapply
396+
{
397+
ap_func = fn;
398+
ap_args = args;
399+
ap_info;
400+
ap_transformed_jsx;
401+
ap_result_type;
402+
}
364403

365404
let rec eq_approx (l1 : t) (l2 : t) =
366405
match l1 with
@@ -419,7 +458,7 @@ let rec eq_approx (l1 : t) (l2 : t) =
419458
(fun ((k : string), v) (k2, v2) -> k = k2 && eq_approx v v2)
420459
| _ -> false)
421460
| Lfunction _
422-
| Llet (_, _, _, _)
461+
| Llet (_, _, _, _, _)
423462
| Lletrec _ | Lswitch _ | Lstaticcatch _ | Ltrywith _
424463
| Lfor (_, _, _, _, _)
425464
| Lfor_of (_, _, _)
@@ -475,10 +514,10 @@ let global_module ?(dynamic_import = false) id =
475514
Lglobal_module (id, dynamic_import)
476515
let const ct : t = Lconst ct
477516

478-
let function_ ~attr ~arity ~params ~body : t =
479-
Lfunction {arity; params; body; attr}
517+
let function_ ~attr ~arity ~params ~body ~ty : t =
518+
Lfunction {arity; params; body; attr; ty}
480519

481-
let let_ kind id e body : t = Llet (kind, id, e, body)
520+
let let_ kind id ty e body : t = Llet (kind, id, ty, e, body)
482521
let letrec bindings body : t = Lletrec (bindings, body)
483522
let while_ a b : t = Lwhile (a, b)
484523
let try_ body id handler : t = Ltrywith (body, id, handler)

compiler/core/lam.mli

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,15 @@ and apply = private {
4646
ap_args: t list;
4747
ap_info: ap_info;
4848
ap_transformed_jsx: bool;
49+
ap_result_type: Types.type_expr option;
4950
}
5051

5152
and lfunction = {
5253
arity: int;
5354
params: ident list;
5455
body: t;
5556
attr: Lambda.function_attribute;
57+
ty: Types.type_expr option;
5658
}
5759

5860
and prim_info = private {
@@ -67,7 +69,7 @@ and t = private
6769
| Lconst of Lam_constant.t
6870
| Lapply of apply
6971
| Lfunction of lfunction
70-
| Llet of Lam_compat.let_kind * ident * t * t
72+
| Llet of Lam_compat.let_kind * ident * Types.type_expr option * t * t
7173
| Lletrec of (ident * t) list * t
7274
| Lprim of prim_info
7375
| Lswitch of t * lambda_switch
@@ -113,16 +115,23 @@ val global_module : ?dynamic_import:bool -> ident -> t
113115

114116
val const : Lam_constant.t -> t
115117

116-
val apply : ?ap_transformed_jsx:bool -> t -> t list -> ap_info -> t
118+
val apply :
119+
?ap_transformed_jsx:bool ->
120+
ap_result_type:Types.type_expr option ->
121+
t ->
122+
t list ->
123+
ap_info ->
124+
t
117125

118126
val function_ :
119127
attr:Lambda.function_attribute ->
120128
arity:int ->
121129
params:ident list ->
122130
body:t ->
131+
ty:Types.type_expr option ->
123132
t
124133

125-
val let_ : Lam_compat.let_kind -> ident -> t -> t -> t
134+
val let_ : Lam_compat.let_kind -> ident -> Types.type_expr option -> t -> t -> t
126135

127136
val letrec : (ident * t) list -> t -> t
128137

compiler/core/lam_analysis.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ let rec no_side_effects (lam : Lam.t) : bool =
102102
(* byte swap *)
103103
| Parraysets | Parraysetu | Poffsetref _ | Praise | Psetfield _ ->
104104
false)
105-
| Llet (_, _, arg, body) -> no_side_effects arg && no_side_effects body
105+
| Llet (_, _, _, arg, body) -> no_side_effects arg && no_side_effects body
106106
| Lswitch (_, _) -> false
107107
| Lstringswitch (_, _, _) -> false
108108
| Lstaticraise _ -> false
@@ -149,7 +149,7 @@ let rec size (lam : Lam.t) =
149149
match lam with
150150
| Lvar _ -> 1
151151
| Lconst c -> size_constant c
152-
| Llet (_, _, l1, l2) -> 1 + size l1 + size l2
152+
| Llet (_, _, _, l1, l2) -> 1 + size l1 + size l2
153153
| Lletrec _ -> really_big ()
154154
| Lprim
155155
{

compiler/core/lam_arity_analysis.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ let rec get_arity (meta : Lam_stats.t) (lam : Lam.t) : Lam_arity.t =
3939
match lam with
4040
| Lvar v -> arity_of_var meta v
4141
| Lconst _ -> Lam_arity.non_function_arity_info
42-
| Llet (_, _, _, l) -> get_arity meta l
42+
| Llet (_, _, _, _, l) -> get_arity meta l
4343
| Lprim
4444
{
4545
primitive = Pfield (_, Fld_module {name});

compiler/core/lam_beta_reduce_util.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ let simple_beta_reduce params body args =
113113
| _ -> f
114114
in
115115
let result =
116-
Hash_ident.fold param_hash (Lam.apply f new_args ap_info)
116+
Hash_ident.fold param_hash
117+
(Lam.apply ~ap_result_type:None f new_args ap_info)
117118
(fun _param stat acc ->
118119
let {lambda; used} = stat in
119120
if not used then Lam.seq lambda acc else acc)

compiler/core/lam_bounded_vars.ml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,11 @@ let rewrite (map : _ Hash_ident.t) (lam : Lam.t) : Lam.t =
7575
and aux (lam : Lam.t) : Lam.t =
7676
match lam with
7777
| Lvar v -> Hash_ident.find_default map v lam
78-
| Llet (str, v, l1, l2) ->
78+
| Llet (str, v, ty, l1, l2) ->
7979
let v = rebind v in
8080
let l1 = aux l1 in
8181
let l2 = aux l2 in
82-
Lam.let_ str v l1 l2
82+
Lam.let_ str v ty l1 l2
8383
| Lletrec (bindings, body) ->
8484
(*order matters see GPR #405*)
8585
let vars = Ext_list.map bindings (fun (k, _) -> rebind k) in
@@ -88,10 +88,10 @@ let rewrite (map : _ Hash_ident.t) (lam : Lam.t) : Lam.t =
8888
in
8989
let body = aux body in
9090
Lam.letrec bindings body
91-
| Lfunction {arity; params; body; attr} ->
91+
| Lfunction {arity; params; body; attr; ty} ->
9292
let params = Ext_list.map params rebind in
9393
let body = aux body in
94-
Lam.function_ ~arity ~params ~body ~attr
94+
Lam.function_ ~arity ~params ~body ~attr ~ty
9595
| Lstaticcatch (l1, (i, xs), l2) ->
9696
let l1 = aux l1 in
9797
let xs = Ext_list.map xs rebind in
@@ -118,10 +118,10 @@ let rewrite (map : _ Hash_ident.t) (lam : Lam.t) : Lam.t =
118118
(* here it makes sure that global vars are not rebound *)
119119
Lam.prim ~primitive ~args:(Ext_list.map args aux) loc
120120
| Lglobal_module _ -> lam
121-
| Lapply {ap_func; ap_args; ap_info; ap_transformed_jsx} ->
121+
| Lapply {ap_func; ap_args; ap_info; ap_transformed_jsx; ap_result_type} ->
122122
let fn = aux ap_func in
123123
let args = Ext_list.map ap_args aux in
124-
Lam.apply ~ap_transformed_jsx fn args ap_info
124+
Lam.apply ~ap_transformed_jsx ~ap_result_type fn args ap_info
125125
| Lswitch
126126
( l,
127127
{

0 commit comments

Comments
 (0)