Skip to content

Commit 43082f5

Browse files
alex-snezhkophatedospencer
authored
feat(compiler)!: Inline record constructors (#1586)
chore(compiler): Rework a bunch of oprint to print inline records correctly Co-authored-by: Blaine Bublitz <blaine.bublitz@gmail.com> Co-authored-by: Oscar Spencer <oscar@grain-lang.org>
1 parent b437c13 commit 43082f5

Some content is hidden

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

45 files changed

+1392
-469
lines changed

compiler/src/codegen/compcore.re

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3178,7 +3178,7 @@ and compile_instr = (wasm_mod, env, instr) =>
31783178
};
31793179

31803180
type type_metadata =
3181-
| ADTMeta(int, list((int, string)))
3181+
| ADTMeta(int, list((int, string, Types.adt_constructor_type)))
31823182
| RecordMeta(int, list(string));
31833183

31843184
let compile_type_metadata = (wasm_mod, env, type_metadata) => {
@@ -3218,7 +3218,11 @@ let compile_type_metadata = (wasm_mod, env, type_metadata) => {
32183218
List.map(
32193219
meta => {
32203220
switch (meta) {
3221-
| ExceptionMetadata(_, variant, name) => (variant, name)
3221+
| ExceptionMetadata(_, variant, name) => (
3222+
variant,
3223+
name,
3224+
TupleConstructor,
3225+
)
32223226
| _ => failwith("impossible by partition")
32233227
}
32243228
},
@@ -3247,26 +3251,77 @@ let compile_type_metadata = (wasm_mod, env, type_metadata) => {
32473251
meta => {
32483252
switch (meta) {
32493253
| ADTMeta(id, cstrs) =>
3254+
// For inline record constructors, store field names after other ADT info
3255+
let extra_required =
3256+
List.map(
3257+
((_, _, cstr_type)) =>
3258+
switch (cstr_type) {
3259+
| TupleConstructor => 0
3260+
| RecordConstructor(fields) =>
3261+
List.fold_left(
3262+
(total, field) =>
3263+
total + 8 + round_to_8(String.length(field)),
3264+
0,
3265+
fields,
3266+
)
3267+
},
3268+
cstrs,
3269+
);
3270+
32503271
let section_length =
3251-
List.fold_left(
3252-
(total, (_, cstr)) =>
3253-
total + 12 + round_to_8(String.length(cstr)),
3272+
List.fold_left2(
3273+
(total, (_, cstr, cstr_type), extra) => {
3274+
total + 16 + round_to_8(String.length(cstr)) + extra
3275+
},
32543276
8,
32553277
cstrs,
3278+
extra_required,
32563279
);
32573280
Buffer.add_int32_le(buf, Int32.of_int(section_length));
32583281
Buffer.add_int32_le(buf, Int32.of_int(id));
3259-
List.iter(
3260-
((id, cstr)) => {
3282+
List.iter2(
3283+
((id, cstr, cstr_type), fields_section_length) => {
32613284
let length = String.length(cstr);
32623285
let aligned_length = round_to_8(length);
3263-
Buffer.add_int32_le(buf, Int32.of_int(aligned_length + 12));
3286+
let constr_length = aligned_length + 16;
3287+
Buffer.add_int32_le(
3288+
buf,
3289+
Int32.of_int(constr_length + fields_section_length),
3290+
);
3291+
// Indicates offset to field data; special value of 0 can be interpreted
3292+
// to indicate that this is not a record variant
3293+
Buffer.add_int32_le(
3294+
buf,
3295+
Int32.of_int(
3296+
if (cstr_type == TupleConstructor) {
3297+
0;
3298+
} else {
3299+
constr_length;
3300+
},
3301+
),
3302+
);
32643303
Buffer.add_int32_le(buf, Int32.of_int(id));
32653304
Buffer.add_int32_le(buf, Int32.of_int(length));
32663305
Buffer.add_string(buf, cstr);
32673306
alignBuffer(aligned_length - length);
3307+
switch (cstr_type) {
3308+
| TupleConstructor => ()
3309+
| RecordConstructor(fields) =>
3310+
List.iter(
3311+
field => {
3312+
let length = String.length(field);
3313+
let aligned_length = round_to_8(length);
3314+
Buffer.add_int32_le(buf, Int32.of_int(aligned_length + 8));
3315+
Buffer.add_int32_le(buf, Int32.of_int(length));
3316+
Buffer.add_string(buf, field);
3317+
alignBuffer(aligned_length - length);
3318+
},
3319+
fields,
3320+
)
3321+
};
32683322
},
32693323
cstrs,
3324+
extra_required,
32703325
);
32713326
| RecordMeta(id, fields) =>
32723327
let section_length =

compiler/src/formatting/format.re

Lines changed: 104 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,7 +1125,7 @@ and resugar_pattern_list_inner = (patterns: list(Parsetree.pattern)) => {
11251125
switch (patterns) {
11261126
| [arg1, arg2, ..._] =>
11271127
switch (arg2.ppat_desc) {
1128-
| PPatConstruct(innercstr, innerpatterns) =>
1128+
| PPatConstruct(innercstr, PPatConstrTuple(innerpatterns)) =>
11291129
let cstr =
11301130
switch (innercstr.txt) {
11311131
| IdentName({txt: name}) => name
@@ -1309,10 +1309,15 @@ and resugar_list_inner = (expressions: list(Parsetree.expression)) =>
13091309
switch (expressions) {
13101310
| [arg1, arg2] =>
13111311
switch (arg2.pexp_desc) {
1312-
| PExpConstruct({txt: IdentName({txt: "[...]"})}, innerexpressions) =>
1312+
| PExpConstruct(
1313+
{txt: IdentName({txt: "[...]"})},
1314+
PExpConstrTuple(innerexpressions),
1315+
) =>
13131316
let inner = resugar_list_inner(innerexpressions);
13141317
List.append([Regular(arg1)], inner);
1315-
| PExpConstruct({txt: IdentName({txt: "[]"})}, _) => [Regular(arg1)]
1318+
| PExpConstruct({txt: IdentName({txt: "[]"})}, PExpConstrTuple(_)) => [
1319+
Regular(arg1),
1320+
]
13161321
| _ => [Regular(arg1), Spread(arg2)]
13171322
}
13181323
| _ =>
@@ -1338,6 +1343,7 @@ and print_record_pattern =
13381343
) => {
13391344
let close =
13401345
switch (closedflag) {
1346+
| Open when patternlocs == [] => Doc.text("_")
13411347
| Open => Doc.concat([Doc.text(","), Doc.space, Doc.text("_")])
13421348
| Closed => Doc.nil
13431349
};
@@ -1462,7 +1468,7 @@ and print_pattern =
14621468
]),
14631469
false,
14641470
)
1465-
| PPatConstruct(location, patterns) =>
1471+
| PPatConstruct(location, PPatConstrTuple(patterns)) =>
14661472
let func =
14671473
switch (location.txt) {
14681474
| IdentName({txt: name}) => name
@@ -1502,7 +1508,20 @@ and print_pattern =
15021508
false,
15031509
);
15041510
};
1505-
1511+
| PPatConstruct(location, PPatConstrRecord(patternlocs, closedflag)) => (
1512+
Doc.concat([
1513+
print_ident(location.txt),
1514+
print_record_pattern(
1515+
~patternlocs,
1516+
~closedflag,
1517+
~original_source,
1518+
~comments,
1519+
~next_loc,
1520+
pat.ppat_loc,
1521+
),
1522+
]),
1523+
false,
1524+
)
15061525
| PPatOr(pattern1, pattern2) => (
15071526
Doc.group(
15081527
Doc.concat([
@@ -3604,12 +3623,18 @@ and print_expression_inner =
36043623
~comments=comments_in_expression,
36053624
func,
36063625
);
3607-
| PExpConstruct({txt: IdentName({txt: "[...]"})}, expressions) =>
3626+
| PExpConstruct(
3627+
{txt: IdentName({txt: "[...]"})},
3628+
PExpConstrTuple(expressions),
3629+
) =>
36083630
resugar_list(~original_source, ~comments, expressions)
3609-
| PExpConstruct({txt: IdentName({txt: "[]"})}, expressions) =>
3631+
| PExpConstruct(
3632+
{txt: IdentName({txt: "[]"})},
3633+
PExpConstrTuple(expressions),
3634+
) =>
36103635
Doc.text("[]")
3611-
| PExpConstruct({txt: id}, []) => print_ident(id)
3612-
| PExpConstruct(constr, expressions) =>
3636+
| PExpConstruct({txt: id}, PExpConstrTuple([])) => print_ident(id)
3637+
| PExpConstruct(constr, PExpConstrTuple(expressions)) =>
36133638
let comments_in_expression =
36143639
Comment_utils.get_comments_inside_location(
36153640
~location=expr.pexp_loc,
@@ -3622,6 +3647,17 @@ and print_expression_inner =
36223647
~comments=comments_in_expression,
36233648
Ast_helper.Exp.ident(constr),
36243649
);
3650+
| PExpConstruct(id, PExpConstrRecord(record)) =>
3651+
Doc.concat([
3652+
print_ident(id.txt),
3653+
print_record(
3654+
~base=None,
3655+
~fields=record,
3656+
~original_source,
3657+
~comments,
3658+
expr.pexp_loc,
3659+
),
3660+
])
36253661
| PExpBlock(expressions) =>
36263662
switch (expressions) {
36273663
| [] =>
@@ -4223,6 +4259,61 @@ let rec print_data =
42234259
]),
42244260
);
42254261
}
4262+
| PConstrRecord(label_declarations) =>
4263+
let get_loc = (lbl: Parsetree.label_declaration) => {
4264+
lbl.pld_loc;
4265+
};
4266+
4267+
let print_item = (~comments, lbl: Parsetree.label_declaration) => {
4268+
Doc.concat([
4269+
print_ident(lbl.pld_name.txt),
4270+
Doc.text(":"),
4271+
Doc.space,
4272+
print_type(~original_source, ~comments, lbl.pld_type),
4273+
]);
4274+
};
4275+
4276+
let pre_brace_comments = []; // We can't determine from AST if comment comes before or after brace
4277+
4278+
let remaining_comments =
4279+
remove_used_comments(
4280+
~remove_comments=pre_brace_comments,
4281+
comments,
4282+
);
4283+
4284+
let after_brace_comments =
4285+
Comment_utils.get_after_brace_comments(
4286+
~loc=data.pdata_loc,
4287+
remaining_comments,
4288+
);
4289+
4290+
let cleaned_comments =
4291+
remove_used_comments(
4292+
~remove_comments=after_brace_comments,
4293+
remaining_comments,
4294+
);
4295+
4296+
let decl_items =
4297+
item_iterator(
4298+
~get_loc,
4299+
~print_item,
4300+
~comments=cleaned_comments,
4301+
~iterated_item=IteratedRecordLabels,
4302+
label_declarations,
4303+
);
4304+
let printed_decls = Doc.join(~sep=Doc.hardLine, decl_items);
4305+
let printed_decls_after_brace =
4306+
Doc.concat([Doc.hardLine, printed_decls]);
4307+
4308+
Doc.group(
4309+
Doc.concat([
4310+
Doc.lbrace,
4311+
Comment_utils.single_line_of_comments(after_brace_comments),
4312+
Doc.indent(printed_decls_after_brace),
4313+
Doc.hardLine,
4314+
Doc.rbrace,
4315+
]),
4316+
);
42264317
| PConstrSingleton => Doc.nil
42274318
},
42284319
]),
@@ -4820,6 +4911,10 @@ let toplevel_print =
48204911
} else {
48214912
Doc.nil;
48224913
}
4914+
| PConstrRecord(_) =>
4915+
failwith(
4916+
"Impossible: exception should not have a record constructor",
4917+
)
48234918
}
48244919

48254920
| PExtRebind(lid) => print_ident(lid.txt)

compiler/src/middle_end/linearize.re

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -900,12 +900,27 @@ let rec transl_imm =
900900
Imm.id(~loc, ~env, tmp),
901901
(exp_setup @ setup) @ [BLet(tmp, ans, Nonglobal)],
902902
);
903-
| TExpConstruct(_, {cstr_tag}, args) =>
903+
| TExpConstruct(_, {cstr_tag}, arg) =>
904904
let tmp = gensym("adt");
905905
let (_, typath, _) = Ctype.extract_concrete_typedecl(env, typ);
906906
let ty_id = get_type_id(typath, env);
907907
let compiled_tag = compile_constructor_tag(cstr_tag);
908-
let (new_args, new_setup) = List.split(List.map(transl_imm, args));
908+
let (new_args, new_setup) =
909+
switch (arg) {
910+
| TExpConstrRecord(fields) =>
911+
List.split(
912+
List.map(
913+
field =>
914+
switch (field) {
915+
| (_, Kept) =>
916+
failwith("Impossible: inline record variant with Kept field")
917+
| (_, Overridden({txt: name, loc}, expr)) => transl_imm(expr)
918+
},
919+
Array.to_list(fields),
920+
),
921+
)
922+
| TExpConstrTuple(args) => List.split(List.map(transl_imm, args))
923+
};
909924
let imm_tytag =
910925
Imm.const(
911926
~loc,
@@ -999,7 +1014,7 @@ and transl_comp_expression =
9991014
TExpConstruct(
10001015
assertion_error_identifier,
10011016
assertion_error,
1002-
[error_message],
1017+
TExpConstrTuple([error_message]),
10031018
),
10041019
},
10051020
),
@@ -1613,6 +1628,23 @@ let gather_type_metadata = statements => {
16131628
(
16141629
compile_constructor_tag(cstr.cstr_tag),
16151630
cstr.cstr_name,
1631+
switch (cstr.cstr_inlined) {
1632+
| None => TupleConstructor
1633+
| Some(t) =>
1634+
let label_names =
1635+
switch (t.type_kind) {
1636+
| TDataRecord(rfs) =>
1637+
List.map(
1638+
rf => Ident.name(rf.Types.rf_name),
1639+
rfs,
1640+
)
1641+
| _ =>
1642+
failwith(
1643+
"Impossible: inlined record constructor with non-record underlying type",
1644+
)
1645+
};
1646+
RecordConstructor(label_names);
1647+
},
16161648
),
16171649
descrs,
16181650
);

compiler/src/middle_end/matchcomp.re

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -609,9 +609,23 @@ let rec compile_matrix = mtx =>
609609
let constructors = matrix_head_constructors(mtx);
610610
/* Printf.eprintf "constructors:\n%s\n" (Sexplib.Sexp.to_string_hum ((Sexplib.Conv.sexp_of_list sexp_of_constructor_description) constructors)); */
611611
let handle_constructor = ((_, switch_branches), cstr) => {
612-
let arity = cstr.cstr_arity;
613612
let specialized = specialize_matrix(cstr, alias, mtx);
614-
let result = compile_matrix(specialized);
613+
let (arity, mtx) =
614+
switch (cstr.cstr_inlined) {
615+
| None => (cstr.cstr_arity, specialized)
616+
| Some(t) =>
617+
switch (t.type_kind) {
618+
| TDataRecord(rfs) =>
619+
let arity = List.length(rfs);
620+
let mtx = flatten_matrix(arity, alias, specialized);
621+
(arity, mtx);
622+
| _ =>
623+
failwith(
624+
"Impossible: inlined record constructor pattern with non-record data",
625+
)
626+
}
627+
};
628+
let result = compile_matrix(mtx);
615629
let final_tree =
616630
Explode(ConstructorMatrix(Some(arity)), alias, result);
617631
(

0 commit comments

Comments
 (0)