Skip to content

Commit 72b2139

Browse files
feat(compiler)!: Remove arbitrary per-file compiler flags, add acceptable options as module attributes (#1804)
Co-authored-by: Oscar Spencer <oscar@grain-lang.org>
1 parent e64f2ff commit 72b2139

Some content is hidden

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

66 files changed

+563
-306
lines changed

cli/bin/grain.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,6 @@ class GrainCommand extends commander.Command {
108108
cmd.forwardOption("--import-memory", "import the memory from `env.memory`");
109109
cmd.option("--dir <dir...>", "directory to preopen");
110110
cmd.option("--env <env...>", "WASI environment variables");
111-
cmd.forwardOption(
112-
"--compilation-mode <mode>",
113-
"compilation mode (advanced use only)"
114-
);
115111
cmd.forwardOption(
116112
"--elide-type-info",
117113
"don't include runtime type information used by toString/print"

compiler/src/compile.re

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -88,25 +88,6 @@ let log_state = state =>
8888
prerr_string("\n\n");
8989
};
9090

91-
let apply_inline_flags = (prog: Parsetree.parsed_program) => {
92-
switch (prog.comments) {
93-
| [Block({cmt_content, cmt_loc}), ..._] =>
94-
Grain_utils.Config.apply_inline_flags(
95-
~on_error=
96-
err => {
97-
switch (err) {
98-
| `Help =>
99-
raise(InlineFlagsError(cmt_loc, Cannot_use_help_or_version))
100-
| `Message(msg) =>
101-
raise(InlineFlagsError(cmt_loc, Cannot_parse_inline_flags(msg)))
102-
}
103-
},
104-
cmt_content,
105-
)
106-
| _ => ()
107-
};
108-
};
109-
11091
let next_state = (~is_root_file=false, {cstate_desc, cstate_filename} as cs) => {
11192
let cstate_desc =
11293
switch (cstate_desc) {
@@ -145,7 +126,15 @@ let next_state = (~is_root_file=false, {cstate_desc, cstate_filename} as cs) =>
145126
cleanup();
146127
Parsed(parsed);
147128
| Parsed(p) =>
148-
apply_inline_flags(p);
129+
let has_attr = name =>
130+
List.exists(
131+
attr => attr.Asttypes.attr_name.txt == name,
132+
p.attributes,
133+
);
134+
Grain_utils.Config.apply_attribute_flags(
135+
~no_pervasives=has_attr("noPervasives"),
136+
~runtime_mode=has_attr("runtimeMode"),
137+
);
149138
if (is_root_file) {
150139
Grain_utils.Config.set_root_config();
151140
};
@@ -266,7 +255,7 @@ let compile_wasi_polyfill = () => {
266255
switch (Grain_utils.Config.wasi_polyfill^) {
267256
| Some(file) =>
268257
Grain_utils.Config.preserve_config(() => {
269-
Grain_utils.Config.compilation_mode := Some("runtime");
258+
Grain_utils.Config.compilation_mode := Grain_utils.Config.Runtime;
270259
let cstate = {
271260
cstate_desc: Initial(InputFile(file)),
272261
cstate_filename: Some(file),

compiler/src/formatting/fmt.re

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3991,12 +3991,53 @@ let print_program = (fmt, parsed_program) => {
39913991
)
39923992
};
39933993

3994-
group @@
3995-
fmt.print_comment_range(
3996-
fmt,
3997-
enclosing_start_location(parsed_program.prog_loc),
3998-
parsed_program.module_name.loc,
3999-
)
3994+
let attributes =
3995+
switch (parsed_program.attributes) {
3996+
| [] =>
3997+
group(
3998+
fmt.print_comment_range(
3999+
fmt,
4000+
enclosing_start_location(parsed_program.prog_loc),
4001+
parsed_program.module_name.loc,
4002+
),
4003+
)
4004+
| _ =>
4005+
group @@
4006+
concat_map(
4007+
~lead=
4008+
first =>
4009+
fmt.print_comment_range(
4010+
fmt,
4011+
~trail=hardline,
4012+
enclosing_start_location(parsed_program.prog_loc),
4013+
first.attr_loc,
4014+
),
4015+
~sep=
4016+
(prev, next) =>
4017+
fmt.print_comment_range(
4018+
fmt,
4019+
~none=hardline,
4020+
~lead=space,
4021+
~trail=hardline,
4022+
prev.Asttypes.attr_loc,
4023+
next.attr_loc,
4024+
),
4025+
~trail=
4026+
prev =>
4027+
fmt.print_comment_range(
4028+
fmt,
4029+
~none=hardline,
4030+
~lead=space,
4031+
~trail=hardline,
4032+
prev.Asttypes.attr_loc,
4033+
parsed_program.prog_core_loc,
4034+
),
4035+
~f=(~final, a) => fmt.print_attribute(fmt, a),
4036+
parsed_program.attributes,
4037+
)
4038+
};
4039+
4040+
attributes
40004041
++ string("module ")
40014042
++ string(parsed_program.module_name.txt)
40024043
++ toplevel;

compiler/src/parsing/driver.re

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ let parse = (~name=?, lexbuf, source): Parsetree.parsed_program => {
150150
let read_imports = (program: Parsetree.parsed_program) => {
151151
open Parsetree_iter;
152152

153+
let module_has_attr = name =>
154+
List.exists(
155+
attr => attr.Asttypes.attr_name.txt == name,
156+
program.attributes,
157+
);
153158
let implicit_opens =
154159
List.map(
155160
o => {
@@ -159,15 +164,11 @@ let read_imports = (program: Parsetree.parsed_program) => {
159164
| Grain_utils.Config.Gc_mod => Location.mknoloc("runtime/gc.gr")
160165
}
161166
},
162-
switch (program.comments) {
163-
| [Block({cmt_content}), ..._] =>
164-
Grain_utils.Config.with_inline_flags(
165-
~on_error=_ => (),
166-
cmt_content,
167-
Grain_utils.Config.get_implicit_opens,
168-
)
169-
| _ => Grain_utils.Config.get_implicit_opens()
170-
},
167+
Grain_utils.Config.with_attribute_flags(
168+
~no_pervasives=module_has_attr("noPervasives"),
169+
~runtime_mode=module_has_attr("runtimeMode"),
170+
Grain_utils.Config.get_implicit_opens,
171+
),
171172
);
172173
let found_includes = ref([]);
173174

compiler/src/parsing/parser.messages

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,42 @@ program: EOL YIELD
2424
## In state 3, spurious reduction of production nonempty_list(eol) -> EOL
2525
## In state 6, spurious reduction of production eols -> nonempty_list(eol)
2626
##
27+
program: AT LIDENT TYPE
28+
##
29+
## Ends in an error in state: 49.
30+
##
31+
## program -> option(attribute) . module_header eos toplevel_stmts EOF [ # ]
32+
## program -> option(attribute) . module_header option(eos) EOF [ # ]
33+
##
34+
## The known suffix of the stack is as follows:
35+
## option(attribute)
36+
##
37+
## WARNING: This example involves spurious reductions.
38+
## This implies that, although the LR(1) items shown above provide an
39+
## accurate view of the past (what has been recognized so far), they
40+
## may provide an INCOMPLETE view of the future (what was expected next).
41+
## In state 34, spurious reduction of production loption(attribute_arguments) ->
42+
## In state 45, spurious reduction of production attribute -> AT id_str loption(attribute_arguments)
43+
## In state 873, spurious reduction of production option(attribute) -> attribute
44+
##
45+
program: EOL AT LIDENT TYPE
46+
##
47+
## Ends in an error in state: 866.
48+
##
49+
## program -> eols option(attribute) . module_header eos toplevel_stmts EOF [ # ]
50+
## program -> eols option(attribute) . module_header option(eos) EOF [ # ]
51+
##
52+
## The known suffix of the stack is as follows:
53+
## eols option(attribute)
54+
##
55+
## WARNING: This example involves spurious reductions.
56+
## This implies that, although the LR(1) items shown above provide an
57+
## accurate view of the past (what has been recognized so far), they
58+
## may provide an INCOMPLETE view of the future (what was expected next).
59+
## In state 34, spurious reduction of production loption(attribute_arguments) ->
60+
## In state 45, spurious reduction of production attribute -> AT id_str loption(attribute_arguments)
61+
## In state 873, spurious reduction of production option(attribute) -> attribute
62+
##
2763

2864
Expected a module header, e.g. `module Main`.
2965

compiler/src/parsing/parser.mly

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -744,5 +744,5 @@ module_header:
744744
| MODULE UIDENT { mkstr $loc($2) $2 }
745745

746746
program:
747-
| opt_eols module_header eos toplevel_stmts EOF { make_program ~loc:(to_loc $sloc) $2 $4 }
748-
| opt_eols module_header eos? EOF { make_program ~loc:(to_loc $sloc) $2 [] }
747+
| opt_eols attributes module_header eos toplevel_stmts EOF { make_program ~loc:(to_loc $sloc) ~core_loc:(to_loc (fst $loc($3), snd $loc)) ~attributes:$2 $3 $5 }
748+
| opt_eols attributes module_header eos? EOF { make_program ~loc:(to_loc $sloc) ~core_loc:(to_loc (fst $loc($3), snd $loc)) ~attributes:$2 $3 [] }

compiler/src/parsing/parser_header.re

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,20 @@ let make_include_alias = ident => {
8989
};
9090
};
9191

92-
let make_program = (~loc, module_name, statements) => {
92+
let make_program = (~loc, ~core_loc, ~attributes, module_name, statements) => {
9393
// Ensure the program loc starts at the beginning of the file even if
9494
// there's whitespace or comments
9595
let loc_start = {...loc.loc_start, pos_lnum: 1, pos_cnum: 0, pos_bol: 0};
9696
let prog_loc = {...loc, loc_start};
9797

98-
{module_name, statements, comments: [], prog_loc};
98+
{
99+
attributes,
100+
module_name,
101+
statements,
102+
comments: [],
103+
prog_loc,
104+
prog_core_loc: core_loc,
105+
};
99106
};
100107

101108
let parse_program = (program, token, lexbuf) => {

compiler/src/parsing/parsetree.re

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -693,9 +693,12 @@ type comment =
693693

694694
[@deriving (sexp, yojson)]
695695
type parsed_program = {
696+
attributes,
696697
module_name: loc(string),
697698
statements: list(toplevel_stmt),
698699
comments: list(comment),
699700
[@sexp_drop_if sexp_locs_disabled]
700-
prog_loc: Location.t,
701+
prog_loc: Location.t, // The full location of the program
702+
[@sexp_drop_if sexp_locs_disabled]
703+
prog_core_loc: Location.t // The core location, without attributes
701704
};

compiler/src/parsing/parsetree_iter.re

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ let iter_attributes = (hooks, attrs) => {
8787

8888
let rec iter_parsed_program = (hooks, {statements} as program) => {
8989
hooks.enter_parsed_program(program);
90+
iter_attributes(hooks, program.attributes);
9091
iter_toplevel_stmts(hooks, statements);
9192
hooks.leave_parsed_program(program);
9293
}

compiler/src/parsing/well_formedness.re

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ type wferr =
1111
| RHSLetRecMayOnlyBeFunction(Location.t)
1212
| NoLetRecMut(Location.t)
1313
| RationalZeroDenominator(Location.t)
14-
| UnknownAttribute(string, Location.t)
14+
| UnknownAttribute(string, string, Location.t)
1515
| InvalidAttributeArity(string, int, Location.t)
1616
| AttributeDisallowed(string, Location.t)
1717
| LoopControlOutsideLoop(string, Location.t)
@@ -49,8 +49,8 @@ let prepare_error =
4949
errorf(~loc, "let rec may not be used with the `mut` keyword.")
5050
| RationalZeroDenominator(loc) =>
5151
errorf(~loc, "Rational numbers may not have a denominator of zero.")
52-
| UnknownAttribute(attr, loc) =>
53-
errorf(~loc, "Unknown attribute `%s`.", attr)
52+
| UnknownAttribute(attr_context, attr, loc) =>
53+
errorf(~loc, "Unknown %s attribute `%s`.", attr_context, attr)
5454
| InvalidAttributeArity(attr, arity, loc) =>
5555
switch (arity) {
5656
| 0 => errorf(~loc, "Attribute `%s` expects no arguments.", attr)
@@ -300,35 +300,28 @@ type known_attribute = {
300300
arity: int,
301301
};
302302

303-
let known_attributes = [
304-
{name: "disableGC", arity: 0},
305-
{name: "unsafe", arity: 0},
306-
{name: "externalName", arity: 1},
307-
];
308-
309-
let valid_attributes = (errs, super) => {
310-
let enter_attribute =
311-
({Asttypes.attr_name: {txt, loc}, attr_args: args} as attr) => {
312-
switch (List.find_opt(({name}) => name == txt, known_attributes)) {
313-
| Some({arity}) when List.length(args) != arity =>
314-
errs := [InvalidAttributeArity(txt, arity, loc), ...errs^]
315-
| None => errs := [UnknownAttribute(txt, loc), ...errs^]
316-
| _ => ()
317-
};
318-
super.enter_attribute(attr);
303+
let disallowed_attributes = (errs, super) => {
304+
let validate_against_known = (attrs, known_attributes, context) => {
305+
List.iter(
306+
({Asttypes.attr_name: {txt, loc}, attr_args: args}) => {
307+
switch (List.find_opt(({name}) => name == txt, known_attributes)) {
308+
| Some({arity}) when List.length(args) != arity =>
309+
errs := [InvalidAttributeArity(txt, arity, loc), ...errs^]
310+
| None => errs := [UnknownAttribute(context, txt, loc), ...errs^]
311+
| _ => ()
312+
}
313+
},
314+
attrs,
315+
);
319316
};
320317

321-
{
322-
errs,
323-
iter_hooks: {
324-
...super,
325-
enter_attribute,
326-
},
327-
};
328-
};
318+
let known_expr_attributes = [
319+
{name: "disableGC", arity: 0},
320+
{name: "unsafe", arity: 0},
321+
{name: "externalName", arity: 1},
322+
];
329323

330-
let disallowed_attributes = (errs, super) => {
331-
let enter_expression = ({pexp_desc: desc, pexp_attributes: attrs} as e) => {
324+
let enter_expression = ({pexp_attributes: attrs} as e) => {
332325
switch (
333326
List.find_opt(
334327
({Asttypes.attr_name: {txt}}) => txt == "externalName",
@@ -345,8 +338,10 @@ let disallowed_attributes = (errs, super) => {
345338
]
346339
| None => ()
347340
};
341+
validate_against_known(attrs, known_expr_attributes, "expression");
348342
super.enter_expression(e);
349343
};
344+
350345
let enter_toplevel_stmt =
351346
({ptop_desc: desc, ptop_attributes: attrs} as top) => {
352347
switch (
@@ -399,15 +394,26 @@ let disallowed_attributes = (errs, super) => {
399394
}
400395
| None => ()
401396
};
397+
validate_against_known(attrs, known_expr_attributes, "top-level");
402398
super.enter_toplevel_stmt(top);
403399
};
404400

401+
let enter_parsed_program = ({attributes} as prog) => {
402+
let known_module_attributes = [
403+
{name: "runtimeMode", arity: 0},
404+
{name: "noPervasives", arity: 0},
405+
];
406+
validate_against_known(attributes, known_module_attributes, "module");
407+
super.enter_parsed_program(prog);
408+
};
409+
405410
{
406411
errs,
407412
iter_hooks: {
408413
...super,
409414
enter_expression,
410415
enter_toplevel_stmt,
416+
enter_parsed_program,
411417
},
412418
};
413419
};
@@ -842,7 +848,6 @@ let well_formedness_checks = [
842848
only_functions_oh_rhs_letrec,
843849
no_letrec_mut,
844850
no_zero_denominator_rational,
845-
valid_attributes,
846851
disallowed_attributes,
847852
no_loop_control_statement_outside_of_loop,
848853
malformed_return_statements,

0 commit comments

Comments
 (0)