diff --git a/cli/bin/grain.js b/cli/bin/grain.js
index 3deafe7ca9..6b7683d148 100755
--- a/cli/bin/grain.js
+++ b/cli/bin/grain.js
@@ -108,10 +108,6 @@ class GrainCommand extends commander.Command {
cmd.forwardOption("--import-memory", "import the memory from `env.memory`");
cmd.option("--dir
", "directory to preopen");
cmd.option("--env ", "WASI environment variables");
- cmd.forwardOption(
- "--compilation-mode ",
- "compilation mode (advanced use only)"
- );
cmd.forwardOption(
"--elide-type-info",
"don't include runtime type information used by toString/print"
diff --git a/compiler/src/compile.re b/compiler/src/compile.re
index f072adf473..48d298309b 100644
--- a/compiler/src/compile.re
+++ b/compiler/src/compile.re
@@ -89,25 +89,6 @@ let log_state = state =>
prerr_string("\n\n");
};
-let apply_inline_flags = (prog: Parsetree.parsed_program) => {
- switch (prog.comments) {
- | [Block({cmt_content, cmt_loc}), ..._] =>
- Grain_utils.Config.apply_inline_flags(
- ~on_error=
- err => {
- switch (err) {
- | `Help =>
- raise(InlineFlagsError(cmt_loc, Cannot_use_help_or_version))
- | `Message(msg) =>
- raise(InlineFlagsError(cmt_loc, Cannot_parse_inline_flags(msg)))
- }
- },
- cmt_content,
- )
- | _ => ()
- };
-};
-
let next_state = (~is_root_file=false, {cstate_desc, cstate_filename} as cs) => {
let cstate_desc =
switch (cstate_desc) {
@@ -146,7 +127,10 @@ let next_state = (~is_root_file=false, {cstate_desc, cstate_filename} as cs) =>
cleanup();
Parsed(parsed);
| Parsed(p) =>
- apply_inline_flags(p);
+ Grain_utils.Config.apply_attribute_flags(
+ ~no_pervasives=Option.is_some(p.attributes.no_pervasives),
+ ~runtime_mode=Option.is_some(p.attributes.runtime_mode),
+ );
if (is_root_file) {
Grain_utils.Config.set_root_config();
};
@@ -267,7 +251,7 @@ let compile_wasi_polyfill = () => {
switch (Grain_utils.Config.wasi_polyfill^) {
| Some(file) =>
Grain_utils.Config.preserve_config(() => {
- Grain_utils.Config.compilation_mode := Some("runtime");
+ Grain_utils.Config.compilation_mode := Grain_utils.Config.Runtime;
let cstate = {
cstate_desc: Initial(InputFile(file)),
cstate_filename: Some(file),
diff --git a/compiler/src/formatting/fmt.re b/compiler/src/formatting/fmt.re
index a1751597d9..7688147ac6 100644
--- a/compiler/src/formatting/fmt.re
+++ b/compiler/src/formatting/fmt.re
@@ -3921,6 +3921,16 @@ let print_comment_range =
};
let print_program = (fmt, parsed_program) => {
+ let attrs =
+ Option.to_list(parsed_program.attributes.no_pervasives)
+ @ Option.to_list(parsed_program.attributes.runtime_mode);
+
+ let first_loc =
+ switch (attrs) {
+ | [fst, ..._] => fst.attr_loc
+ | _ => parsed_program.prog_core_loc
+ };
+
let toplevel =
switch (parsed_program.statements) {
| [] =>
@@ -3988,8 +3998,35 @@ let print_program = (fmt, parsed_program) => {
fmt.print_comment_range(
fmt,
enclosing_start_location(parsed_program.prog_loc),
- parsed_program.module_name.loc,
+ first_loc,
)
+ ++ group(
+ concat_map(
+ ~lead=_ => empty,
+ ~sep=
+ (prev, next) =>
+ fmt.print_comment_range(
+ fmt,
+ ~none=hardline,
+ ~lead=space,
+ ~trail=hardline,
+ prev.Asttypes.attr_loc,
+ next.attr_loc,
+ ),
+ ~trail=
+ prev =>
+ fmt.print_comment_range(
+ fmt,
+ ~none=hardline,
+ ~lead=space,
+ ~trail=hardline,
+ prev.Asttypes.attr_loc,
+ parsed_program.prog_core_loc,
+ ),
+ ~f=(~final, a) => fmt.print_attribute(fmt, a),
+ attrs,
+ ),
+ )
++ string("module ")
++ string(parsed_program.module_name.txt)
++ toplevel;
diff --git a/compiler/src/parsing/driver.re b/compiler/src/parsing/driver.re
index 839e96f800..ea08748c59 100644
--- a/compiler/src/parsing/driver.re
+++ b/compiler/src/parsing/driver.re
@@ -158,15 +158,12 @@ let read_imports = (program: Parsetree.parsed_program) => {
| Grain_utils.Config.Gc_mod => Location.mknoloc("runtime/gc")
}
},
- switch (program.comments) {
- | [Block({cmt_content}), ..._] =>
- Grain_utils.Config.with_inline_flags(
- ~on_error=_ => (),
- cmt_content,
- Grain_utils.Config.get_implicit_opens,
- )
- | _ => Grain_utils.Config.get_implicit_opens()
- },
+ Grain_utils.Config.with_attribute_flags(
+ ~on_error=_ => (),
+ ~no_pervasives=Option.is_some(program.attributes.no_pervasives),
+ ~runtime_mode=Option.is_some(program.attributes.runtime_mode),
+ Grain_utils.Config.get_implicit_opens,
+ ),
);
let found_includes = ref([]);
diff --git a/compiler/src/parsing/parser.messages b/compiler/src/parsing/parser.messages
index 8906e55693..a9f7ab3350 100644
--- a/compiler/src/parsing/parser.messages
+++ b/compiler/src/parsing/parser.messages
@@ -24,6 +24,42 @@ program: EOL YIELD
## In state 3, spurious reduction of production nonempty_list(eol) -> EOL
## In state 6, spurious reduction of production eols -> nonempty_list(eol)
##
+program: AT LIDENT TYPE
+##
+## Ends in an error in state: 49.
+##
+## program -> option(attribute) . module_header eos toplevel_stmts EOF [ # ]
+## program -> option(attribute) . module_header option(eos) EOF [ # ]
+##
+## The known suffix of the stack is as follows:
+## option(attribute)
+##
+## WARNING: This example involves spurious reductions.
+## This implies that, although the LR(1) items shown above provide an
+## accurate view of the past (what has been recognized so far), they
+## may provide an INCOMPLETE view of the future (what was expected next).
+## In state 34, spurious reduction of production loption(attribute_arguments) ->
+## In state 45, spurious reduction of production attribute -> AT id_str loption(attribute_arguments)
+## In state 873, spurious reduction of production option(attribute) -> attribute
+##
+program: EOL AT LIDENT TYPE
+##
+## Ends in an error in state: 866.
+##
+## program -> eols option(attribute) . module_header eos toplevel_stmts EOF [ # ]
+## program -> eols option(attribute) . module_header option(eos) EOF [ # ]
+##
+## The known suffix of the stack is as follows:
+## eols option(attribute)
+##
+## WARNING: This example involves spurious reductions.
+## This implies that, although the LR(1) items shown above provide an
+## accurate view of the past (what has been recognized so far), they
+## may provide an INCOMPLETE view of the future (what was expected next).
+## In state 34, spurious reduction of production loption(attribute_arguments) ->
+## In state 45, spurious reduction of production attribute -> AT id_str loption(attribute_arguments)
+## In state 873, spurious reduction of production option(attribute) -> attribute
+##
Expected a module header, e.g. `module Main`.
diff --git a/compiler/src/parsing/parser.mly b/compiler/src/parsing/parser.mly
index e0e3139115..505937ccca 100644
--- a/compiler/src/parsing/parser.mly
+++ b/compiler/src/parsing/parser.mly
@@ -555,10 +555,13 @@ attribute_arguments:
| lparen lseparated_list(comma, attribute_argument) rparen { $2 }
attribute:
- | AT id_str loption(attribute_arguments) opt_eols { Attribute.mk ~loc:(to_loc $loc) $2 $3 }
+ | AT id_str loption(attribute_arguments) { Attribute.mk ~loc:(to_loc $loc) $2 $3 }
+
+attr_with_opt_eols:
+ | attribute opt_eols { $1 }
attributes:
- | attribute* { $1 }
+ | attr_with_opt_eols* { $1 }
let_expr:
| attributes LET REC value_binds { Expression.let_ ~loc:(to_loc $sloc) ~core_loc:(to_loc (fst $loc($2), snd $loc)) ~attributes:$1 Recursive Immutable $4 }
@@ -744,5 +747,8 @@ module_header:
| MODULE UIDENT { mkstr $loc($2) $2 }
program:
- | opt_eols module_header eos toplevel_stmts EOF { make_program ~loc:(to_loc $sloc) $2 $4 }
- | opt_eols module_header eos? EOF { make_program ~loc:(to_loc $sloc) $2 [] }
+ | opt_eols attributes program_base EOF { make_program ~loc:(to_loc $sloc) ~attributes:$2 ~core_loc:(fst $3) (fst (snd $3)) (snd (snd $3)) }
+
+%inline program_base:
+ | module_header eos toplevel_stmts { (to_loc $sloc), ($1, $3) }
+ | module_header eos? { (to_loc $sloc), ($1, []) }
diff --git a/compiler/src/parsing/parser_header.re b/compiler/src/parsing/parser_header.re
index 789d6959d7..65a035822f 100644
--- a/compiler/src/parsing/parser_header.re
+++ b/compiler/src/parsing/parser_header.re
@@ -116,13 +116,47 @@ let make_include_alias = ident => {
};
};
-let make_program = (~loc, module_name, statements) => {
+let make_program = (~loc, ~core_loc, ~attributes, module_name, statements) => {
// Ensure the program loc starts at the beginning of the file even if
// there's whitespace or comments
let loc_start = {...loc.loc_start, pos_lnum: 1, pos_cnum: 0, pos_bol: 0};
let prog_loc = {...loc, loc_start};
+ let (no_pervasives, runtime_mode) =
+ List.fold_left(
+ ((no_pervasives, runtime_mode), attr) => {
+ switch (attr) {
+ | Asttypes.{attr_name: {txt: "noPervasives"}, attr_args: []} => (
+ Some(attr),
+ runtime_mode,
+ )
+ | {attr_name: {txt: "runtimeMode"}, attr_args: []} => (
+ no_pervasives,
+ Some(attr),
+ )
+ | {attr_name: {loc}} =>
+ raise(
+ SyntaxError(
+ loc,
+ "@noPervasives and @runtimeMode are the only valid top-level module attributes.",
+ ),
+ )
+ }
+ },
+ (None, None),
+ attributes,
+ );
- fix_blocks({module_name, statements, comments: [], prog_loc});
+ fix_blocks({
+ attributes: {
+ no_pervasives,
+ runtime_mode,
+ },
+ module_name,
+ statements,
+ comments: [],
+ prog_loc,
+ prog_core_loc: core_loc,
+ });
};
let parse_program = (program, token, lexbuf) => {
diff --git a/compiler/src/parsing/parsetree.re b/compiler/src/parsing/parsetree.re
index 8da79675f6..82a9585cc6 100644
--- a/compiler/src/parsing/parsetree.re
+++ b/compiler/src/parsing/parsetree.re
@@ -691,11 +691,20 @@ type comment =
/** The type for parsed programs */
+[@deriving (sexp, yojson)]
+type module_attributes = {
+ no_pervasives: option(attribute),
+ runtime_mode: option(attribute),
+};
+
[@deriving (sexp, yojson)]
type parsed_program = {
+ attributes: module_attributes,
module_name: loc(string),
statements: list(toplevel_stmt),
comments: list(comment),
[@sexp_drop_if sexp_locs_disabled]
- prog_loc: Location.t,
+ prog_loc: Location.t, // The full location, including attributes
+ [@sexp_drop_if sexp_locs_disabled]
+ prog_core_loc: Location.t // The core location, without attributes
};
diff --git a/compiler/src/typed/env.re b/compiler/src/typed/env.re
index 8d8b95d28c..39ac91d43d 100644
--- a/compiler/src/typed/env.re
+++ b/compiler/src/typed/env.re
@@ -668,11 +668,7 @@ let get_components = c =>
| Some(c) => c
};
-type compilation_mode =
- | Normal /* Standard compilation with regular bells and whistles */
- | Runtime /* GC doesn't exist yet, allocations happen in runtime heap */;
-
-let current_unit = ref(("", "", Normal));
+let current_unit = ref(("", "", Grain_utils.Config.Normal));
let set_unit = unit => current_unit := unit;
diff --git a/compiler/src/typed/env.rei b/compiler/src/typed/env.rei
index f7edd39063..5390b7c863 100644
--- a/compiler/src/typed/env.rei
+++ b/compiler/src/typed/env.rei
@@ -162,13 +162,8 @@ let add_local_type: (Path.t, type_declaration, t) => t;
let add_item: (signature_item, t) => t;
let add_signature: (signature, t) => t;
-/* Remember the current compilation unit: modname * filename * compilation mode. */
-type compilation_mode =
- | Normal
- | Runtime;
-
-let set_unit: ((string, string, compilation_mode)) => unit;
-let get_unit: unit => (string, string, compilation_mode);
+let set_unit: ((string, string, Grain_utils.Config.compilation_mode)) => unit;
+let get_unit: unit => (string, string, Grain_utils.Config.compilation_mode);
let is_runtime_mode: unit => bool;
/* Insertion of a module */
diff --git a/compiler/src/typed/typed_well_formedness.re b/compiler/src/typed/typed_well_formedness.re
index ac6956737e..31bb973470 100644
--- a/compiler/src/typed/typed_well_formedness.re
+++ b/compiler/src/typed/typed_well_formedness.re
@@ -337,11 +337,10 @@ module WellFormednessArg: TypedtreeIter.IteratorArgument = {
};
// For now, we only raise the error inside of functions.
if (exp_is_wasm_unsafe(exp)
- && !(
- Grain_utils.Config.no_gc^
- || Grain_utils.Config.compilation_mode^ == Some("runtime")
- || is_unsafe()
- )) {
+ && !
+ Grain_utils.Config.(
+ no_gc^ || compilation_mode^ == Runtime || is_unsafe()
+ )) {
raise(Error(exp_loc, WasmOutsideDisableGc));
};
};
diff --git a/compiler/src/typed/typemod.re b/compiler/src/typed/typemod.re
index ee34b78afb..f94f16b8fe 100644
--- a/compiler/src/typed/typemod.re
+++ b/compiler/src/typed/typemod.re
@@ -1040,17 +1040,14 @@ let initial_env = () => {
);
};
-let get_compilation_mode = () => {
- switch (Grain_utils.Config.compilation_mode^) {
- | Some("runtime") => Env.Runtime
- | _ => Env.Normal
- };
-};
-
let type_implementation = prog => {
let sourcefile = prog.prog_loc.loc_start.pos_fname;
let module_name = prog.module_name.txt;
- Env.set_unit((module_name, sourcefile, get_compilation_mode()));
+ Env.set_unit((
+ module_name,
+ sourcefile,
+ Grain_utils.Config.compilation_mode^,
+ ));
let initenv = initial_env();
let (statements, sg, finalenv) = type_module(initenv, prog.statements);
let simple_sg = simplify_signature(sg);
diff --git a/compiler/src/utils/config.re b/compiler/src/utils/config.re
index d0405fde4c..b56358f62f 100644
--- a/compiler/src/utils/config.re
+++ b/compiler/src/utils/config.re
@@ -305,73 +305,6 @@ let preserve_config = thunk => {
let preserve_all_configs = thunk =>
preserve_root_config(() => preserve_config(thunk));
-let with_cli_options = (term: 'a): Cmdliner.Term.t('a) => {
- open Cmdliner;
- open Term;
- let process_option = acc =>
- fun
- | Spec(arg, names, box) =>
- const((a, b) => {
- box := a;
- b;
- })
- $ Arg.value(arg)
- $ acc;
- let folded = List.fold_left(process_option, const(term), specs^);
- folded;
-};
-
-let with_unapplied_cli_options = (term: 'a): Cmdliner.Term.t('a) => {
- open Cmdliner;
- open Term;
- let process_option = acc =>
- fun
- | Spec(arg, names, box) => const((a, b) => {b}) $ Arg.value(arg) $ acc;
- let folded = List.fold_left(process_option, const(term), specs^);
- folded;
-};
-
-let process_used_cli_options = term => {
- open Cmdliner;
- open Term;
- let process_option = acc =>
- fun
- | Spec(arg, names, box) => {
- const((a, (_, used), b) => {
- if (List.fold_left(
- (acc, name) =>
- acc
- || List.mem("--" ++ name, used)
- || List.mem("-" ++ name, used),
- false,
- names,
- )) {
- box := a;
- };
- b;
- })
- $ Arg.value(arg)
- $ with_used_args(term)
- $ acc;
- };
- let folded = List.fold_left(process_option, term, specs^);
- folded;
-};
-
-let apply_inline_flags = (~err, flag_string) => {
- open Cmdliner;
- let cmd =
- Cmd.v(
- Cmd.info("grainc"),
- process_used_cli_options(with_unapplied_cli_options(Term.const())),
- );
- // Remove grainc-flags prefix
- let len = String.length(flag_string) - 12;
- let flag_string = String.sub(flag_string, 12, len);
- let argv = Array.of_list(String.split_on_char(' ', flag_string));
- Cmd.eval_value(~argv, ~err, cmd);
-};
-
let option_conv = ((prsr, prntr)) => (
x =>
switch (prsr(x)) {
@@ -449,16 +382,11 @@ let import_memory =
false,
);
-let compilation_mode =
- opt(
- ~names=["compilation-mode"],
- ~conv=
- option_conv(
- Cmdliner.Arg.enum([("normal", "normal"), ("runtime", "runtime")]),
- ),
- ~doc="Compilation mode (advanced use only)",
- None,
- );
+type compilation_mode =
+ | Normal /* Standard compilation with regular bells and whistles */
+ | Runtime /* GC doesn't exist yet, allocations happen in runtime heap */;
+
+let compilation_mode = internal_opt(Normal, Digestable);
let statically_link =
toggle_flag(~names=["no-link"], ~doc="Disable static linking", true);
@@ -557,6 +485,23 @@ let source_map =
let print_warnings = internal_opt(true, NotDigestable);
+let with_cli_options = (term: 'a): Cmdliner.Term.t('a) => {
+ open Cmdliner;
+ open Term;
+ let process_option = acc =>
+ fun
+ | Spec(arg, names, box) =>
+ const((a, b) => {
+ box := a;
+ b;
+ })
+ $ Arg.value(arg)
+ $ acc;
+ let folded = List.fold_left(process_option, const(term), specs^);
+ compilation_mode := Normal;
+ folded;
+};
+
let stdlib_directory = (): option(string) =>
Option.map(
path => Filepath.(to_string(String.derelativize(path))),
@@ -576,24 +521,21 @@ let module_search_path = () => {
};
};
-let apply_inline_flags = (~on_error, cmt_content) =>
- if (Str.string_match(Str.regexp_string("grainc-flags"), cmt_content, 0)) {
- let err_buf = Buffer.create(80);
- let err = Format.formatter_of_buffer(err_buf);
- let result = apply_inline_flags(~err, cmt_content);
- switch (result) {
- | Ok(`Ok(_)) => ()
- | Ok(`Version)
- | Ok(`Help) => on_error(`Help)
- | Error(_) =>
- Format.pp_print_flush(err, ());
- on_error(`Message(Buffer.contents(err_buf)));
- };
+let apply_attribute_flags = (~no_pervasives as np, ~runtime_mode as rm) => {
+ // Only apply options if attributes were explicitly given so as to not
+ // unintentionally override options set previously e.g. compiling a
+ // wasi-polyfill file in non-runtime-mode if @runtimeMode is not specified
+ if (np) {
+ no_pervasives := true;
+ };
+ if (rm) {
+ compilation_mode := Runtime;
};
+};
-let with_inline_flags = (~on_error, cmt_content, thunk) => {
+let with_attribute_flags = (~on_error, ~no_pervasives, ~runtime_mode, thunk) => {
preserve_config(() => {
- apply_inline_flags(~on_error, cmt_content);
+ apply_attribute_flags(~no_pervasives, ~runtime_mode);
thunk();
});
};
@@ -609,7 +551,7 @@ let get_implicit_opens = () => {
} else {
[Pervasives_mod];
};
- if (compilation_mode^ == Some("runtime")) {
+ if (compilation_mode^ == Runtime) {
[];
} else {
// Pervasives goes first, just for good measure.
diff --git a/compiler/src/utils/config.rei b/compiler/src/utils/config.rei
index 03c69dcd4c..23ab59080e 100644
--- a/compiler/src/utils/config.rei
+++ b/compiler/src/utils/config.rei
@@ -1,6 +1,10 @@
type profile =
| Release;
+type compilation_mode =
+ | Normal /* Standard compilation with regular bells and whistles */
+ | Runtime /* GC doesn't exist yet, allocations happen in runtime heap */;
+
/** The Grain stdlib directory, based on the current configuration */
let stdlib_directory: unit => option(string);
@@ -78,9 +82,9 @@ let maximum_memory_pages: ref(option(int));
let import_memory: ref(bool);
-/** Compilation mode to use when compiling */
+/** Whether this module should be compiled in runtime mode */
-let compilation_mode: ref(option(string));
+let compilation_mode: ref(compilation_mode);
/** Statically link modules after compilation */
@@ -174,13 +178,17 @@ let preserve_all_configs: (unit => 'a) => 'a;
let with_cli_options: 'a => Cmdliner.Term.t('a);
-/** Applies compile flags provided at the start of a file */
+/** Applies compile flags provided as module attributes */
-let apply_inline_flags:
- (~on_error: [> | `Help | `Message(string)] => unit, string) => unit;
+let apply_attribute_flags: (~no_pervasives: bool, ~runtime_mode: bool) => unit;
-let with_inline_flags:
- (~on_error: [> | `Help | `Message(string)] => unit, string, unit => 'a) =>
+let with_attribute_flags:
+ (
+ ~on_error: [> | `Help | `Message(string)] => unit,
+ ~no_pervasives: bool,
+ ~runtime_mode: bool,
+ unit => 'a
+ ) =>
'a;
type implicit_opens =
diff --git a/compiler/test/grainfmt/comments.expected.gr b/compiler/test/grainfmt/comments.expected.gr
index 680233f56d..4c706a165f 100644
--- a/compiler/test/grainfmt/comments.expected.gr
+++ b/compiler/test/grainfmt/comments.expected.gr
@@ -1,3 +1,9 @@
+// Comment before first attr
+/* Block comment before attr */@noPervasives // Line comment on attr
+// Comment after first attr
+@runtimeMode
+// Comment after second attr
+
module Comments
/* Let's start with a block
@@ -276,6 +282,13 @@ match (5) {
},
}
+// comment before attr
+@attr("foo", "bar") // line comment on attr
+// comment after attr
+/* block comment attr2 */
+@attr2
+// comment after second attr
+let _ = 1
@attr(/* foo */ "foo", /* bar */ "bar")
let _ = 1
@attr( // foo
@@ -330,7 +343,7 @@ foo += /* foo */ /* foo */ 1
foo += /* foo */ /* foo */ 1
{
- @attr
+ @attr // foo
let // foo
_ = 1
@attr(
@@ -338,8 +351,7 @@ foo += /* foo */ /* foo */ 1
"foo",
// foo
// goo
- // bar
- )
+ ) // bar
let /* doo */ // foo
// goo
// bar
@@ -599,15 +611,14 @@ provide {
module Foo as /* Foo */ /* as */ Bar,
}
-@attr
+@attr // foo
let _ = 1
@attr(
"foo", /* doo */
"foo",
// foo
// goo
- // bar
-)
+) // bar
let _ = 1
provide /* provide */ enum Foo {
diff --git a/compiler/test/grainfmt/comments.input.gr b/compiler/test/grainfmt/comments.input.gr
index f52a7c89d8..7d5a9c994d 100644
--- a/compiler/test/grainfmt/comments.input.gr
+++ b/compiler/test/grainfmt/comments.input.gr
@@ -1,3 +1,9 @@
+// Comment before first attr
+/* Block comment before attr */ @noPervasives // Line comment on attr
+// Comment after first attr
+@runtimeMode
+// Comment after second attr
+
module Comments
/* Let's start with a block
@@ -249,6 +255,12 @@ match (5) {
5 },
}
+// comment before attr
+@attr("foo", "bar") // line comment on attr
+// comment after attr
+/* block comment attr2 */@attr2
+// comment after second attr
+let _ = 1
@attr(/* foo */ "foo", /* bar */ "bar") let _ = 1
@attr(// foo
"foo", /* bar */ "bar") let _ = 1
diff --git a/compiler/test/grainfmt/lets.expected.gr b/compiler/test/grainfmt/lets.expected.gr
index 82e1ad225f..9515b1ef00 100644
--- a/compiler/test/grainfmt/lets.expected.gr
+++ b/compiler/test/grainfmt/lets.expected.gr
@@ -1,3 +1,5 @@
+@noPervasives
+@runtimeMode
module Lets
let rec myFun1 = x => x + 1
diff --git a/compiler/test/grainfmt/lets.input.gr b/compiler/test/grainfmt/lets.input.gr
index c10f79a1ea..c804f57daf 100644
--- a/compiler/test/grainfmt/lets.input.gr
+++ b/compiler/test/grainfmt/lets.input.gr
@@ -1,3 +1,5 @@
+@noPervasives @runtimeMode
+
module Lets
let rec myFun1 = x => x + 1 and myFun2 = x => x + 1
diff --git a/compiler/test/input/mallocTight.gr b/compiler/test/input/mallocTight.gr
index cc8616ab79..3d6670a23e 100644
--- a/compiler/test/input/mallocTight.gr
+++ b/compiler/test/input/mallocTight.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --compilation-mode=runtime */
+@runtimeMode
module MallocTight
from "runtime/malloc" include Malloc
diff --git a/compiler/test/input/memoryBase/asserts.gr b/compiler/test/input/memoryBase/asserts.gr
index dfe4d4abd4..ccab0119b9 100644
--- a/compiler/test/input/memoryBase/asserts.gr
+++ b/compiler/test/input/memoryBase/asserts.gr
@@ -1,4 +1,3 @@
-/* grainc-flags --memory-base 0x110000 */
module Asserts
from "runtime/unsafe/wasmi32" include WasmI32
diff --git a/compiler/test/input/memoryBase/sameAsDefault.gr b/compiler/test/input/memoryBase/sameAsDefault.gr
index 4604864c08..d5cbfbfc28 100644
--- a/compiler/test/input/memoryBase/sameAsDefault.gr
+++ b/compiler/test/input/memoryBase/sameAsDefault.gr
@@ -1,4 +1,3 @@
-/* grainc-flags --memory-base 0x400 */
module SameAsDefault
enum Thing {
diff --git a/compiler/test/input/memoryBase/slightlyHigher.gr b/compiler/test/input/memoryBase/slightlyHigher.gr
index 9a61d77bf2..3ff0c08249 100644
--- a/compiler/test/input/memoryBase/slightlyHigher.gr
+++ b/compiler/test/input/memoryBase/slightlyHigher.gr
@@ -1,4 +1,3 @@
-/* grainc-flags --memory-base 0x800 */
module SlightlyHigher
enum Thing {
diff --git a/compiler/test/input/memoryBase/veryLarge.gr b/compiler/test/input/memoryBase/veryLarge.gr
index e21685c218..f1ddf93268 100644
--- a/compiler/test/input/memoryBase/veryLarge.gr
+++ b/compiler/test/input/memoryBase/veryLarge.gr
@@ -1,4 +1,3 @@
-/* grainc-flags --memory-base 0x110000 */
module VeryLarge
enum Thing {
diff --git a/compiler/test/input/memoryBase/zero.gr b/compiler/test/input/memoryBase/zero.gr
index 3b6b1232ec..3fce29f01b 100644
--- a/compiler/test/input/memoryBase/zero.gr
+++ b/compiler/test/input/memoryBase/zero.gr
@@ -1,4 +1,3 @@
-/* grainc-flags --memory-base 0x0 */
module Zero
enum Thing {
diff --git a/compiler/test/input/unsafeWasmGlobals.gr b/compiler/test/input/unsafeWasmGlobals.gr
index b044b95356..72f0fc064a 100644
--- a/compiler/test/input/unsafeWasmGlobals.gr
+++ b/compiler/test/input/unsafeWasmGlobals.gr
@@ -1,4 +1,3 @@
-/* grainc-flags --no-gc */
module UnsafeWasmGlobals
from "runtime/debugPrint" include DebugPrint
diff --git a/compiler/test/runner.re b/compiler/test/runner.re
index 254812f991..ddd5b0db20 100644
--- a/compiler/test/runner.re
+++ b/compiler/test/runner.re
@@ -215,6 +215,12 @@ let doc = (file, arguments) => {
let module_header = "module Test; ";
+let create_module_attributes = attributes =>
+ Grain_parsing.Parsetree.(
+ (Option.is_some(attributes.no_pervasives) ? "@noPervasives\n" : "")
+ ++ (Option.is_some(attributes.runtime_mode) ? "@runtimeMode\n" : "")
+ );
+
let makeSnapshotRunner = (~config_fn=?, test, name, prog) => {
test(name, ({expect}) => {
Config.preserve_all_configs(() => {
@@ -242,18 +248,22 @@ let makeFilesizeRunner = (test, ~config_fn=?, name, prog, size) => {
});
};
-let makeSnapshotFileRunner = (test, name, filename) => {
- test(
- name,
- ({expect}) => {
+let makeSnapshotFileRunner = (test, ~config_fn=?, name, filename) => {
+ test(name, ({expect}) => {
+ Config.preserve_all_configs(() => {
let infile = grainfile(filename);
let outfile = wasmfile(name);
ignore @@
- compile_file(~hook=stop_after_object_file_emitted, infile, outfile);
+ compile_file(
+ ~hook=stop_after_object_file_emitted,
+ ~config_fn?,
+ infile,
+ outfile,
+ );
let file = watfile(name);
expect.file(file).toMatchSnapshot();
- },
- );
+ })
+ });
};
let makeCompileErrorRunner = (test, name, prog, msg) => {
@@ -295,10 +305,25 @@ let makeNoWarningRunner = (test, name, prog) => {
};
let makeRunner =
- (test, ~num_pages=?, ~config_fn=?, ~extra_args=?, name, prog, expected) => {
+ (
+ test,
+ ~num_pages=?,
+ ~config_fn=?,
+ ~extra_args=?,
+ ~attributes=Test_utils.default_module_attributes,
+ name,
+ prog,
+ expected,
+ ) => {
test(name, ({expect}) => {
Config.preserve_all_configs(() => {
- ignore @@ compile(~num_pages?, ~config_fn?, name, module_header ++ prog);
+ ignore @@
+ compile(
+ ~num_pages?,
+ ~config_fn?,
+ name,
+ create_module_attributes(attributes) ++ module_header ++ prog,
+ );
let (result, _) = run(~num_pages?, ~extra_args?, wasmfile(name));
expect.string(result).toEqual(expected);
})
@@ -442,8 +467,11 @@ let makeParseRunner =
};
};
let strip_locs =
- ({module_name, statements, comments}: Parsetree.parsed_program) =>
+ (
+ {attributes, module_name, statements, comments}: Parsetree.parsed_program,
+ ) =>
Parsetree.{
+ attributes,
module_name: {
...module_name,
loc: Location.dummy_loc,
@@ -455,6 +483,7 @@ let makeParseRunner =
),
comments: List.map(comment_loc_stripper, comments),
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
};
let parsed =
if (keep_locs) {
diff --git a/compiler/test/suites/arrays.re b/compiler/test/suites/arrays.re
index 54062c9038..93f5f3f9b7 100644
--- a/compiler/test/suites/arrays.re
+++ b/compiler/test/suites/arrays.re
@@ -130,6 +130,7 @@ describe("arrays", ({test, testSkip}) => {
state[0] =
5",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [
Toplevel.expr(
@@ -180,6 +181,7 @@ describe("arrays", ({test, testSkip}) => {
],
comments: [],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
)
)
diff --git a/compiler/test/suites/basic_functionality.re b/compiler/test/suites/basic_functionality.re
index d2fc136e1c..6d8dc9c429 100644
--- a/compiler/test/suites/basic_functionality.re
+++ b/compiler/test/suites/basic_functionality.re
@@ -244,7 +244,11 @@ describe("basic functionality", ({test, testSkip}) => {
"Failure: boo",
);
assertSnapshotFile("toplevel_statements", "toplevelStatements");
- assertSnapshotFile("unsafe_wasm_globals", "unsafeWasmGlobals");
+ assertSnapshotFile(
+ ~config_fn=() => {Grain_utils.Config.no_gc := true},
+ "unsafe_wasm_globals",
+ "unsafeWasmGlobals",
+ );
assertSnapshotFile("pattern_match_unsafe_wasm", "patternMatchUnsafeWasm");
/* Unicode support */
Grain_parsing.(
@@ -265,6 +269,7 @@ describe("basic functionality", ({test, testSkip}) => {
type Γber = Number
|},
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [
Toplevel.data(
@@ -352,6 +357,7 @@ describe("basic functionality", ({test, testSkip}) => {
],
comments: [],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
)
)
diff --git a/compiler/test/suites/blocks.re b/compiler/test/suites/blocks.re
index 611000b835..2cb379d28e 100644
--- a/compiler/test/suites/blocks.re
+++ b/compiler/test/suites/blocks.re
@@ -12,6 +12,7 @@ describe("blocks", ({test}) => {
"block_parse_lone_no_args_enum",
"module Test; { Foo }",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [
Toplevel.expr(
@@ -34,6 +35,7 @@ describe("blocks", ({test}) => {
],
comments: [],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
)
)
diff --git a/compiler/test/suites/chars.re b/compiler/test/suites/chars.re
index 04ebeba3c7..9776a32140 100644
--- a/compiler/test/suites/chars.re
+++ b/compiler/test/suites/chars.re
@@ -91,6 +91,7 @@ Did you mean to create the string "\{\\"test\\": 1\}" instead?|},
"char_loc_simple",
"module Test\n'a'",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name:
Location.mkloc(
"Test",
@@ -105,12 +106,14 @@ Did you mean to create the string "\{\\"test\\": 1\}" instead?|},
],
comments: [],
prog_loc: mk_loc("char_loc_simple", (1, 0, 0), (2, 15, 12)),
+ prog_core_loc: mk_loc("char_loc_simple", (1, 0, 0), (2, 15, 12)),
},
);
assertParseWithLocs(
"char_loc_code",
"module Test\n'\\u{1F3F4}'",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name:
Location.mkloc(
"Test",
@@ -125,12 +128,14 @@ Did you mean to create the string "\{\\"test\\": 1\}" instead?|},
],
comments: [],
prog_loc: mk_loc("char_loc_code", (1, 0, 0), (2, 23, 12)),
+ prog_core_loc: mk_loc("char_loc_code", (1, 0, 0), (2, 23, 12)),
},
);
assertParseWithLocs(
"char_loc_emoji",
"module Test\n'π―'",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name:
Location.mkloc(
"Test",
@@ -145,6 +150,7 @@ Did you mean to create the string "\{\\"test\\": 1\}" instead?|},
],
comments: [],
prog_loc: mk_loc("char_loc_emoji", (1, 0, 0), (2, 15, 12)),
+ prog_core_loc: mk_loc("char_loc_emoji", (1, 0, 0), (2, 15, 12)),
},
);
});
diff --git a/compiler/test/suites/comments.re b/compiler/test/suites/comments.re
index 4f07160adb..be551a07f7 100644
--- a/compiler/test/suites/comments.re
+++ b/compiler/test/suites/comments.re
@@ -12,6 +12,7 @@ describe("comments", ({test}) => {
"comment_parse_1",
"// Test\nmodule Test",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [],
comments: [
@@ -22,12 +23,14 @@ describe("comments", ({test}) => {
}),
],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"comment_parse_2",
"/* Test */module Test",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [],
comments: [
@@ -38,12 +41,14 @@ describe("comments", ({test}) => {
}),
],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"comment_parse_block_multiline_trim",
"/* Test\n Weird indent\n Normal indent */module Test",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [],
comments: [
@@ -54,12 +59,14 @@ describe("comments", ({test}) => {
}),
],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"comment_parse_block_multiline_trim2",
"/* Test\r\n Weird indent\r\n Normal indent */module Test",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [],
comments: [
@@ -70,12 +77,14 @@ describe("comments", ({test}) => {
}),
],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"comment_parse_3",
"/** Test */module Test",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [],
comments: [
@@ -86,12 +95,14 @@ describe("comments", ({test}) => {
}),
],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"comment_parse_doc_multiline_trim_all_same_indent",
"/**\n Test\n Weird indent\n Normal indent */module Test",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [],
comments: [
@@ -102,12 +113,14 @@ describe("comments", ({test}) => {
}),
],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"comment_parse_doc_multiline_trim_keeps_differnt_indent",
"/** Test\n Weird indent\n Normal indent */module Test",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [],
comments: [
@@ -118,6 +131,7 @@ describe("comments", ({test}) => {
}),
],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
@@ -125,6 +139,7 @@ describe("comments", ({test}) => {
// Note: There are explicit tab characters in this string to test them
"/**\n Test\r\n Weird indent\r\n Normal indent */module Test",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [],
comments: [
@@ -135,12 +150,14 @@ describe("comments", ({test}) => {
}),
],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"comment_parse_4",
"#!/bin/grain\nmodule Test",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [],
comments: [
@@ -151,12 +168,14 @@ describe("comments", ({test}) => {
}),
],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"comment_parse_block_deasterisk",
"/* Test\n* no space before\n * space before\n * tab before\n *no space after */module Test",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [],
comments: [
@@ -167,12 +186,14 @@ describe("comments", ({test}) => {
}),
],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"comment_parse_doc_deasterisk",
"/** Test\n* no space before\n * space before\n * tab before\n *no space after */module Test",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [],
comments: [
@@ -183,12 +204,14 @@ describe("comments", ({test}) => {
}),
],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"comment_parse_doc_deasterisk2",
"/** Test\n* no space before\n * space before\n * tab before\n * trailing space after */module Test",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [],
comments: [
@@ -199,6 +222,7 @@ describe("comments", ({test}) => {
}),
],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertCompileError(
diff --git a/compiler/test/suites/memory_base.re b/compiler/test/suites/memory_base.re
index 893e93bb28..4615680f87 100644
--- a/compiler/test/suites/memory_base.re
+++ b/compiler/test/suites/memory_base.re
@@ -9,16 +9,33 @@ describe("memory_base", ({test, testSkip}) => {
// Non-snapshots since we want to track functional issues.
assertFileRun("basecase", "memoryBase/basecase", "HelloWorld\n");
assertFileRun(
+ ~config_fn=() => {Grain_utils.Config.memory_base := Some(0x400)},
"same_as_default",
"memoryBase/sameAsDefault",
"HelloWorld\n",
);
- assertFileRun("zero", "memoryBase/zero", "HelloWorld\n");
assertFileRun(
+ ~config_fn=() => {Grain_utils.Config.memory_base := Some(0x0)},
+ "zero",
+ "memoryBase/zero",
+ "HelloWorld\n",
+ );
+ assertFileRun(
+ ~config_fn=() => {Grain_utils.Config.memory_base := Some(0x800)},
"slightly_higher",
"memoryBase/slightlyHigher",
"HelloWorld\n",
);
- assertFileRun("very_large", "memoryBase/veryLarge", "HelloWorld\n");
- assertFileRun("asserts", "memoryBase/asserts", "");
+ assertFileRun(
+ ~config_fn=() => {Grain_utils.Config.memory_base := Some(0x110000)},
+ "very_large",
+ "memoryBase/veryLarge",
+ "HelloWorld\n",
+ );
+ assertFileRun(
+ ~config_fn=() => {Grain_utils.Config.memory_base := Some(0x110000)},
+ "asserts",
+ "memoryBase/asserts",
+ "",
+ );
});
diff --git a/compiler/test/suites/optimizations.re b/compiler/test/suites/optimizations.re
index b949b575b1..e98f51beee 100644
--- a/compiler/test/suites/optimizations.re
+++ b/compiler/test/suites/optimizations.re
@@ -22,42 +22,34 @@ describe("optimizations", ({test, testSkip}) => {
expected: Grain_middle_end.Anftree.anf_expression,
) => {
test(outfile, ({expect}) => {
- Grain_utils.Config.preserve_all_configs(() => {
- Config.with_config(
- Config.empty,
- () => {
- switch (config_fn) {
- | None => ()
- | Some(fn) => fn()
- };
- open Grain_middle_end;
- let final_anf =
- Anf_utils.clear_locations @@
- compile_string_to_final_anf(
- outfile,
- "module Test; " ++ program_str,
- );
- let saved_disabled = Grain_typed.Ident.disable_stamps^;
- let (result, expected) =
- try(
- {
- Grain_typed.Ident.disable_stamps := true;
- let result =
- Sexplib.Sexp.to_string_hum @@
- Anftree.sexp_of_anf_expression(final_anf.body);
- let expected =
- Sexplib.Sexp.to_string_hum @@
- Anftree.sexp_of_anf_expression(expected);
- (result, expected);
- }
- ) {
- | e =>
- Grain_typed.Ident.disable_stamps := saved_disabled;
- raise(e);
- };
- expect.string(result).toEqual(expected);
- },
- )
+ Config.preserve_all_configs(() => {
+ switch (config_fn) {
+ | None => ()
+ | Some(fn) => fn()
+ };
+ open Grain_middle_end;
+ let final_anf =
+ Anf_utils.clear_locations @@
+ compile_string_to_final_anf(outfile, "module Test; " ++ program_str);
+ let saved_disabled = Grain_typed.Ident.disable_stamps^;
+ let (result, expected) =
+ try(
+ {
+ Grain_typed.Ident.disable_stamps := true;
+ let result =
+ Sexplib.Sexp.to_string_hum @@
+ Anftree.sexp_of_anf_expression(final_anf.body);
+ let expected =
+ Sexplib.Sexp.to_string_hum @@
+ Anftree.sexp_of_anf_expression(expected);
+ (result, expected);
+ }
+ ) {
+ | e =>
+ Grain_typed.Ident.disable_stamps := saved_disabled;
+ raise(e);
+ };
+ expect.string(result).toEqual(expected);
})
});
};
@@ -414,8 +406,8 @@ describe("optimizations", ({test, testSkip}) => {
// Removal of manual memory management calls
assertAnf(
"test_manual_gc_calls_removed",
+ ~config_fn=() => {Grain_utils.Config.no_gc := true},
{|
- /* grainc-flags --no-gc */
from "runtime/unsafe/memory" include Memory
from "runtime/unsafe/wasmi32" include WasmI32
@disableGC
diff --git a/compiler/test/suites/parsing.re b/compiler/test/suites/parsing.re
index 81ad5dd79b..b681273600 100644
--- a/compiler/test/suites/parsing.re
+++ b/compiler/test/suites/parsing.re
@@ -46,6 +46,7 @@ describe("parsing", ({test, testSkip}) => {
op,
"module Test; a " ++ op ++ " b",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [
Toplevel.expr(
@@ -67,6 +68,7 @@ describe("parsing", ({test, testSkip}) => {
],
comments: [],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
@@ -121,6 +123,7 @@ describe("parsing", ({test, testSkip}) => {
"custom_op_precedence_1",
"module Test; a +++ b *** c",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [
Toplevel.expr(
@@ -158,12 +161,14 @@ describe("parsing", ({test, testSkip}) => {
],
comments: [],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"custom_op_precedence_2",
"module Test; a &&-- b &-- c",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [
Toplevel.expr(
@@ -201,12 +206,14 @@ describe("parsing", ({test, testSkip}) => {
],
comments: [],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"custom_op_precedence_3",
"module Test; a ||-- b |-- c",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [
Toplevel.expr(
@@ -244,12 +251,14 @@ describe("parsing", ({test, testSkip}) => {
],
comments: [],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"regression_issue_1473",
"module Test; a << b >> c",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [
Toplevel.expr(
@@ -287,12 +296,14 @@ describe("parsing", ({test, testSkip}) => {
],
comments: [],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"regression_issue_1609",
"module Test; return -1",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [
Toplevel.expr(
@@ -323,6 +334,7 @@ describe("parsing", ({test, testSkip}) => {
],
comments: [],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
@@ -360,10 +372,12 @@ describe("parsing", ({test, testSkip}) => {
\xe2\x80\xa9
",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [],
comments: [],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
@@ -392,6 +406,7 @@ describe("parsing", ({test, testSkip}) => {
"end_of_statement_linefeed",
"module Test; a\x0ab",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [
Toplevel.expr(
@@ -407,12 +422,14 @@ describe("parsing", ({test, testSkip}) => {
],
comments: [],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"end_of_statement_formfeed",
"module Test; a\x0cb",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [
Toplevel.expr(
@@ -428,12 +445,14 @@ describe("parsing", ({test, testSkip}) => {
],
comments: [],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"end_of_statement_carriagereturn",
"module Test; a\x0db",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [
Toplevel.expr(
@@ -449,12 +468,14 @@ describe("parsing", ({test, testSkip}) => {
],
comments: [],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"end_of_statement_crlf",
"module Test; a\x0d\x0ab",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [
Toplevel.expr(
@@ -470,12 +491,14 @@ describe("parsing", ({test, testSkip}) => {
],
comments: [],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"end_of_statement_nextline",
"module Test; a\xc2\x85b",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [
Toplevel.expr(
@@ -491,12 +514,14 @@ describe("parsing", ({test, testSkip}) => {
],
comments: [],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"end_of_statement_lineseparator",
"module Test; a\xe2\x80\xa8b",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [
Toplevel.expr(
@@ -512,12 +537,14 @@ describe("parsing", ({test, testSkip}) => {
],
comments: [],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
assertParse(
"end_of_statement_paragraphseparator",
"module Test; a\xe2\x80\xa9b",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name: Location.mknoloc("Test"),
statements: [
Toplevel.expr(
@@ -533,6 +560,7 @@ describe("parsing", ({test, testSkip}) => {
],
comments: [],
prog_loc: Location.dummy_loc,
+ prog_core_loc: Location.dummy_loc,
},
);
});
diff --git a/compiler/test/suites/print.re b/compiler/test/suites/print.re
index 12b40ded72..1884867d3d 100644
--- a/compiler/test/suites/print.re
+++ b/compiler/test/suites/print.re
@@ -8,13 +8,15 @@ describe("print", ({test, testSkip}) => {
let assertRun = makeRunner(test_or_skip);
assertRun(
+ ~config_fn=() => {Grain_utils.Config.elide_type_info := true},
"elided_type_info_1",
- "/* grainc-flags --elide-type-info */ enum Foo { Foo }; print(Foo)",
+ "enum Foo { Foo }; print(Foo)",
"\n",
);
assertRun(
+ ~config_fn=() => {Grain_utils.Config.elide_type_info := true},
"elided_type_info_2",
- "/* grainc-flags --elide-type-info */ record Foo { foo: String }; print({ foo: \"foo\" })",
+ "record Foo { foo: String }; print({ foo: \"foo\" })",
"\n",
);
assertRun(
diff --git a/compiler/test/suites/strings.re b/compiler/test/suites/strings.re
index 6d1dfaabe3..e5fd3bb1d4 100644
--- a/compiler/test/suites/strings.re
+++ b/compiler/test/suites/strings.re
@@ -23,6 +23,7 @@ describe("strings", ({test, testSkip}) => {
"string_parse_dqs1",
"module Test; \"foo\"",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name:
Location.mkloc(
"Test",
@@ -36,12 +37,14 @@ describe("strings", ({test, testSkip}) => {
],
comments: [],
prog_loc: mk_loc("string_parse_dqs1", (1, 0, 0), (1, 18, 0)),
+ prog_core_loc: mk_loc("string_parse_dqs1", (1, 0, 0), (1, 18, 0)),
},
);
assertParseWithLocs(
"string_parse_dqs2",
"module Test; \"bar\\nbaz\"",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name:
Location.mkloc(
"Test",
@@ -55,12 +58,14 @@ describe("strings", ({test, testSkip}) => {
],
comments: [],
prog_loc: mk_loc("string_parse_dqs2", (1, 0, 0), (1, 23, 0)),
+ prog_core_loc: mk_loc("string_parse_dqs2", (1, 0, 0), (1, 23, 0)),
},
);
assertParseWithLocs(
"string_parse_sqs1",
"module Test; \"foobar\"",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name:
Location.mkloc(
"Test",
@@ -74,12 +79,14 @@ describe("strings", ({test, testSkip}) => {
],
comments: [],
prog_loc: mk_loc("string_parse_sqs1", (1, 0, 0), (1, 21, 0)),
+ prog_core_loc: mk_loc("string_parse_sqs1", (1, 0, 0), (1, 21, 0)),
},
);
assertParseWithLocs(
"string_parse_sqs2",
"module Test; \"bar\\u{41}\"",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name:
Location.mkloc(
"Test",
@@ -93,12 +100,14 @@ describe("strings", ({test, testSkip}) => {
],
comments: [],
prog_loc: mk_loc("string_parse_sqs2", (1, 0, 0), (1, 24, 0)),
+ prog_core_loc: mk_loc("string_parse_sqs2", (1, 0, 0), (1, 24, 0)),
},
);
assertParseWithLocs(
"string_parse_sqs3",
"module Test; \"bar\\x41\"",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name:
Location.mkloc(
"Test",
@@ -112,12 +121,14 @@ describe("strings", ({test, testSkip}) => {
],
comments: [],
prog_loc: mk_loc("string_parse_sqs3", (1, 0, 0), (1, 22, 0)),
+ prog_core_loc: mk_loc("string_parse_sqs3", (1, 0, 0), (1, 22, 0)),
},
);
assertParseWithLocs(
"string_parse_sqs4",
"module Test; \"bar\\101\"",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name:
Location.mkloc(
"Test",
@@ -131,12 +142,14 @@ describe("strings", ({test, testSkip}) => {
],
comments: [],
prog_loc: mk_loc("string_parse_sqs4", (1, 0, 0), (1, 22, 0)),
+ prog_core_loc: mk_loc("string_parse_sqs4", (1, 0, 0), (1, 22, 0)),
},
);
assertParseWithLocs(
"string_parse_sqs5",
"module Test; \"bar\\u0041\"",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name:
Location.mkloc(
"Test",
@@ -150,12 +163,14 @@ describe("strings", ({test, testSkip}) => {
],
comments: [],
prog_loc: mk_loc("string_parse_sqs5", (1, 0, 0), (1, 24, 0)),
+ prog_core_loc: mk_loc("string_parse_sqs5", (1, 0, 0), (1, 24, 0)),
},
);
assertParseWithLocs(
"string_parse_emoji_escape",
"module Test; \"π\"",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name:
Location.mkloc(
"Test",
@@ -169,12 +184,15 @@ describe("strings", ({test, testSkip}) => {
],
comments: [],
prog_loc: mk_loc("string_parse_emoji_escape", (1, 0, 0), (1, 16, 0)),
+ prog_core_loc:
+ mk_loc("string_parse_emoji_escape", (1, 0, 0), (1, 16, 0)),
},
);
assertParseWithLocs(
"string_parse_emoji_literal",
"module Test; \"π―\"",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name:
Location.mkloc(
"Test",
@@ -188,6 +206,8 @@ describe("strings", ({test, testSkip}) => {
],
comments: [],
prog_loc: mk_loc("string_parse_emoji_literal", (1, 0, 0), (1, 16, 0)),
+ prog_core_loc:
+ mk_loc("string_parse_emoji_literal", (1, 0, 0), (1, 16, 0)),
},
);
/* String parse locations */
@@ -195,6 +215,7 @@ describe("strings", ({test, testSkip}) => {
"string_loc_single_line",
"module Test\n\"foo\"",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name:
Location.mkloc(
"Test",
@@ -208,12 +229,15 @@ describe("strings", ({test, testSkip}) => {
],
comments: [],
prog_loc: mk_loc("string_loc_single_line", (1, 0, 0), (2, 17, 12)),
+ prog_core_loc:
+ mk_loc("string_loc_single_line", (1, 0, 0), (2, 17, 12)),
},
);
assertParseWithLocs(
"string_loc_multi_line",
"module Test\n\"foo\nbar\nbaz\nqux\nquux\"",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name:
Location.mkloc(
"Test",
@@ -227,12 +251,15 @@ describe("strings", ({test, testSkip}) => {
],
comments: [],
prog_loc: mk_loc("string_loc_multi_line", (1, 0, 0), (6, 34, 29)),
+ prog_core_loc:
+ mk_loc("string_loc_multi_line", (1, 0, 0), (6, 34, 29)),
},
);
assertParseWithLocs(
"string_loc_single_line_emoji",
"module Test\n\"π―\"",
{
+ attributes: Grain_tests.Test_utils.default_module_attributes,
module_name:
Location.mkloc(
"Test",
@@ -252,6 +279,8 @@ describe("strings", ({test, testSkip}) => {
comments: [],
prog_loc:
mk_loc("string_loc_single_line_emoji", (1, 0, 0), (2, 15, 12)),
+ prog_core_loc:
+ mk_loc("string_loc_single_line_emoji", (1, 0, 0), (2, 15, 12)),
},
);
assertSnapshot("string1", "\"foo\"");
diff --git a/compiler/test/test_utils.re b/compiler/test/test_utils.re
index e566f527ef..8f759d2717 100644
--- a/compiler/test/test_utils.re
+++ b/compiler/test/test_utils.re
@@ -39,3 +39,6 @@ let mk_loc =
loc_ghost: false,
};
};
+
+let default_module_attributes =
+ Grain_parsing.Parsetree.{no_pervasives: None, runtime_mode: None};
diff --git a/docs/contributor/memory_management.md b/docs/contributor/memory_management.md
index 95340a3d0f..f4266f9497 100644
--- a/docs/contributor/memory_management.md
+++ b/docs/contributor/memory_management.md
@@ -127,16 +127,9 @@ Note that `load_swap` is one of the aforementioned exceptions which does not `in
There are times in which the garbage collector is not desired in Grain code. The typical example of this is the implementation of the core runtime (the garbage collector and memory allocator). Generally, even for many parts of the runtime, the garbage collector should not be disabled as it is fairly easy to cause memory leaks or memory corruption. That said, Grain provides two mechanisms for disabling the garbage collector, either on a per-function or a per-file/program basis.
-First, it is possible to annotate functions with `@disableGC`. This will compile the annotated function with no garbage collection instructions. It should be noted that this is not necessary to write low-level Grain code, and you can view the document on [@unsafe and low-level programming](./low_level_programming.md) to learn more. Second, the following compiler flags impact the emission of GC instructions:
+First, it is possible to annotate functions with `@disableGC`. This will compile the annotated function with no garbage collection instructions. It should be noted that this is not necessary to write low-level Grain code, and you can view the document on [@unsafe and low-level programming](./low_level_programming.md) to learn more. Second, the `--no-gc` compiler flag disables the garbage collector, performing allocations via `malloc`.
-- `--no-gc`: disables the garbage collector, allocations are still done via `malloc`
-- `--compilation-mode=runtime`: GC doesn't exist (so no GC instructions will be emitted); implicit allocations can't be reclaimed. See `runtime.md` for more details.
-
-These flags are typically used by placing something like this at the beginning of the file:
-
-```grain
-/* grainc-flags --no-gc */
-```
+Additionally, the `@runtimeMode` attribute can be placed onto the top-level `module` statement of a file to compile a file in runtime mode, where GC doesn't exist (so no GC instructions will be emitted) and implicit allocations can't be reclaimed. See `runtime.md` for more details.
When this flag is enabled, no functions in the file will be compiled with garbage collection instructions.
diff --git a/docs/contributor/runtime.md b/docs/contributor/runtime.md
index 779fce6c37..35a30d7584 100644
--- a/docs/contributor/runtime.md
+++ b/docs/contributor/runtime.md
@@ -1,6 +1,6 @@
# The Grain Runtime
-When we speak of the Grain runtime, we largely mean the memory allocator, garbage collector, and anything else that needs to exist before these faculties are available (those are the modules in the stdlib/runtime folder). These are essential to all Grain programs, and some care must be taken to compile them. For that reason, these modules are compiled with the `--compilation-mode=runtime` flag. In this mode, there is no access to Pervasives and all allocations happen in the runtime heap.
+When we speak of the Grain runtime, we largely mean the memory allocator, garbage collector, and anything else that needs to exist before these faculties are available (those are the modules in the stdlib/runtime folder). These are essential to all Grain programs, and some care must be taken to compile them. For that reason, these modules are compiled with the `@runtimeMode` module attribute. In this mode, there is no access to Pervasives and all allocations happen in the runtime heap.
## The Runtime Heap
diff --git a/stdlib/pervasives.gr b/stdlib/pervasives.gr
index 22b3b7f01d..04a8e1b0da 100644
--- a/stdlib/pervasives.gr
+++ b/stdlib/pervasives.gr
@@ -1,5 +1,3 @@
-/* grainc-flags --no-pervasives */
-
/**
* This module is automatically imported into every Grain program. You can think of it as the global environment. Although it is automatically imported, it can still be imported manually.
*
@@ -7,6 +5,7 @@
*
* @since v0.1.0
*/
+@noPervasives
module Pervasives
from "runtime/exception" include Exception
diff --git a/stdlib/runtime/atof/common.gr b/stdlib/runtime/atof/common.gr
index b0840bd83b..86e744c0c6 100644
--- a/stdlib/runtime/atof/common.gr
+++ b/stdlib/runtime/atof/common.gr
@@ -1,5 +1,3 @@
-/* grainc-flags --no-pervasives */
-
// This module was based on Rust's dec2flt
// https://github.com/rust-lang/rust/blob/1cbc45942d5c0f6eb5d94e3b10762ba541958035/library/core/src/num/dec2flt/common.rs
// Rust's MIT license is provided below:
@@ -28,6 +26,7 @@
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
+@noPervasives
module Common
from "runtime/unsafe/wasmi32" include WasmI32
diff --git a/stdlib/runtime/atof/decimal.gr b/stdlib/runtime/atof/decimal.gr
index 088f0e0204..6d0ab13bb9 100644
--- a/stdlib/runtime/atof/decimal.gr
+++ b/stdlib/runtime/atof/decimal.gr
@@ -1,5 +1,3 @@
-/* grainc-flags --no-pervasives */
-
// This module was based on Rust's dec2flt
// https://github.com/rust-lang/rust/blob/1cbc45942d5c0f6eb5d94e3b10762ba541958035/library/core/src/num/dec2flt/decimal.rs
// Rust's MIT license is provided below:
@@ -28,6 +26,7 @@
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
+@noPervasives
module Decimal
from "runtime/unsafe/wasmi32" include WasmI32
diff --git a/stdlib/runtime/atof/lemire.gr b/stdlib/runtime/atof/lemire.gr
index b459a650ff..016b46cf0c 100644
--- a/stdlib/runtime/atof/lemire.gr
+++ b/stdlib/runtime/atof/lemire.gr
@@ -1,5 +1,3 @@
-/* grainc-flags --no-pervasives */
-
// This module was based on Rust's dec2flt
// https://github.com/rust-lang/rust/blob/1cbc45942d5c0f6eb5d94e3b10762ba541958035/library/core/src/num/dec2flt/lemire.rs
// Rust's MIT license is provided below:
@@ -28,6 +26,7 @@
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
+@noPervasives
module Lemire
from "runtime/unsafe/wasmi32" include WasmI32
diff --git a/stdlib/runtime/atof/table.gr b/stdlib/runtime/atof/table.gr
index d91ed7afaa..41090c72f7 100644
--- a/stdlib/runtime/atof/table.gr
+++ b/stdlib/runtime/atof/table.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --no-pervasives */
+@noPervasives
module Table
from "runtime/unsafe/wasmi32" include WasmI32
diff --git a/stdlib/runtime/atoi/parse.gr b/stdlib/runtime/atoi/parse.gr
index 2171bc24a1..ba189562ba 100644
--- a/stdlib/runtime/atoi/parse.gr
+++ b/stdlib/runtime/atoi/parse.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --no-pervasives */
+@noPervasives
module Parse
from "runtime/unsafe/wasmi32" include WasmI32
diff --git a/stdlib/runtime/bigint.gr b/stdlib/runtime/bigint.gr
index 931aaaf263..bc2d94b29c 100644
--- a/stdlib/runtime/bigint.gr
+++ b/stdlib/runtime/bigint.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --no-pervasives */
+@noPervasives
module Bigint
/**
diff --git a/stdlib/runtime/compare.gr b/stdlib/runtime/compare.gr
index 28b3f700ec..74af2b795b 100644
--- a/stdlib/runtime/compare.gr
+++ b/stdlib/runtime/compare.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --no-pervasives */
+@noPervasives
module Compare
from "runtime/unsafe/wasmi32" include WasmI32
diff --git a/stdlib/runtime/dataStructures.gr b/stdlib/runtime/dataStructures.gr
index d9b99c9caa..507c7ea7ba 100644
--- a/stdlib/runtime/dataStructures.gr
+++ b/stdlib/runtime/dataStructures.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --no-pervasives */
+@noPervasives
module DataStructures
/**
diff --git a/stdlib/runtime/equal.gr b/stdlib/runtime/equal.gr
index a0023eab5a..f1c2869d56 100644
--- a/stdlib/runtime/equal.gr
+++ b/stdlib/runtime/equal.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --no-pervasives */
+@noPervasives
module Equal
from "runtime/unsafe/memory" include Memory
diff --git a/stdlib/runtime/exception.gr b/stdlib/runtime/exception.gr
index 197649c082..e0a35e7fb6 100644
--- a/stdlib/runtime/exception.gr
+++ b/stdlib/runtime/exception.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --compilation-mode=runtime */
+@runtimeMode
module Exception
from "runtime/unsafe/wasmi32" include WasmI32
diff --git a/stdlib/runtime/gc.gr b/stdlib/runtime/gc.gr
index 66644dd21f..a9c764ef01 100644
--- a/stdlib/runtime/gc.gr
+++ b/stdlib/runtime/gc.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --compilation-mode=runtime */
+@runtimeMode
module GC
/* Notes:
diff --git a/stdlib/runtime/malloc.gr b/stdlib/runtime/malloc.gr
index 10a77ad72f..c80ea1674b 100644
--- a/stdlib/runtime/malloc.gr
+++ b/stdlib/runtime/malloc.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --compilation-mode=runtime */
+@runtimeMode
module Malloc
/*
diff --git a/stdlib/runtime/numberUtils.gr b/stdlib/runtime/numberUtils.gr
index e457072ae6..cab19daecb 100644
--- a/stdlib/runtime/numberUtils.gr
+++ b/stdlib/runtime/numberUtils.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --no-pervasives */
+@noPervasives
module NumberUtils
/*
diff --git a/stdlib/runtime/numbers.gr b/stdlib/runtime/numbers.gr
index 09d63de3e1..80a790a3b5 100644
--- a/stdlib/runtime/numbers.gr
+++ b/stdlib/runtime/numbers.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --no-pervasives */
+@noPervasives
module Numbers
from "runtime/unsafe/memory" include Memory
diff --git a/stdlib/runtime/string.gr b/stdlib/runtime/string.gr
index 0c78ac178d..50d75564bc 100644
--- a/stdlib/runtime/string.gr
+++ b/stdlib/runtime/string.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --no-pervasives */
+@noPervasives
module String
from "runtime/unsafe/wasmi32" include WasmI32
diff --git a/stdlib/runtime/unsafe/constants.gr b/stdlib/runtime/unsafe/constants.gr
index 6accfc1752..cb082b8d70 100644
--- a/stdlib/runtime/unsafe/constants.gr
+++ b/stdlib/runtime/unsafe/constants.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --compilation-mode=runtime */
+@runtimeMode
module Constants
// Signed/Unsigned Min/Max Constants
diff --git a/stdlib/runtime/unsafe/memory.gr b/stdlib/runtime/unsafe/memory.gr
index 104b96c759..f87d6ee1f3 100644
--- a/stdlib/runtime/unsafe/memory.gr
+++ b/stdlib/runtime/unsafe/memory.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --compilation-mode=runtime */
+@runtimeMode
module Memory
from "runtime/gc" include GC
diff --git a/stdlib/runtime/unsafe/tags.gr b/stdlib/runtime/unsafe/tags.gr
index 512529cf86..505df6b377 100644
--- a/stdlib/runtime/unsafe/tags.gr
+++ b/stdlib/runtime/unsafe/tags.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --compilation-mode=runtime */
+@runtimeMode
module Tags
provide let _GRAIN_NUMBER_TAG_TYPE = 0b0001n
diff --git a/stdlib/runtime/unsafe/wasmf32.gr b/stdlib/runtime/unsafe/wasmf32.gr
index fb23eb9557..d499e11771 100644
--- a/stdlib/runtime/unsafe/wasmf32.gr
+++ b/stdlib/runtime/unsafe/wasmf32.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --compilation-mode=runtime */
+@runtimeMode
module WasmF32
// WebAssembly Memory Instructions
diff --git a/stdlib/runtime/unsafe/wasmf64.gr b/stdlib/runtime/unsafe/wasmf64.gr
index 7c86f817d9..e920137fe9 100644
--- a/stdlib/runtime/unsafe/wasmf64.gr
+++ b/stdlib/runtime/unsafe/wasmf64.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --compilation-mode=runtime */
+@runtimeMode
module WasmF64
// WebAssembly Memory Instructions
diff --git a/stdlib/runtime/unsafe/wasmi32.gr b/stdlib/runtime/unsafe/wasmi32.gr
index e17a9a562d..68160a9462 100644
--- a/stdlib/runtime/unsafe/wasmi32.gr
+++ b/stdlib/runtime/unsafe/wasmi32.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --compilation-mode=runtime */
+@runtimeMode
module WasmI32
// WebAssembly Memory Instructions
diff --git a/stdlib/runtime/unsafe/wasmi64.gr b/stdlib/runtime/unsafe/wasmi64.gr
index 6d89ce1de1..8b36919214 100644
--- a/stdlib/runtime/unsafe/wasmi64.gr
+++ b/stdlib/runtime/unsafe/wasmi64.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --compilation-mode=runtime */
+@runtimeMode
module WasmI64
// WebAssembly Memory Instructions
diff --git a/stdlib/runtime/utils/printing.gr b/stdlib/runtime/utils/printing.gr
index 328f87aca3..96b267a9cb 100644
--- a/stdlib/runtime/utils/printing.gr
+++ b/stdlib/runtime/utils/printing.gr
@@ -1,4 +1,4 @@
-/* grainc-flags --no-pervasives */
+@noPervasives
module Printing
// Printing utilities for runtime code (primarily for debugging)
diff --git a/stdlib/runtime/wasi.gr b/stdlib/runtime/wasi.gr
index 4a408e55fd..86151192eb 100644
--- a/stdlib/runtime/wasi.gr
+++ b/stdlib/runtime/wasi.gr
@@ -1,8 +1,9 @@
-/* grainc-flags --no-gc */
module Wasi
from "runtime/unsafe/wasmi32" include WasmI32
from "exception" include Exception
+from "runtime/string" include String
+use String.{ concat as (++), toString }
// env
provide foreign wasm args_get:
@@ -108,120 +109,219 @@ provide foreign wasm path_rename:
(WasmI32, WasmI32, WasmI32, WasmI32, WasmI32, WasmI32) => WasmI32 from "wasi_snapshot_preview1"
// clocks
+@unsafe
provide let _CLOCK_REALTIME = 0n
+@unsafe
provide let _CLOCK_MONOTONIC = 1n
+@unsafe
provide let _CLOCK_PROCESS_CPUTIME = 2n
+@unsafe
provide let _CLOCK_THREAD_CPUTIME = 3n
// time
+@unsafe
provide let _TIME_SET_ATIM = 1n
+@unsafe
provide let _TIME_SET_ATIM_NOW = 2n
+@unsafe
provide let _TIME_SET_MTIM = 4n
+@unsafe
provide let _TIME_SET_MTIM_NOW = 8n
// lookup flags
+@unsafe
provide let _LOOKUP_FLAG_SYMLINK_FOLLOW = 1n
// open flags
+@unsafe
provide let _OPEN_FLAG_CREAT = 1n
+@unsafe
provide let _OPEN_FLAG_DIRECTORY = 2n
+@unsafe
provide let _OPEN_FLAG_EXCL = 4n
+@unsafe
provide let _OPEN_FLAG_TRUNC = 8n
// fdflags
+@unsafe
provide let _FDFLAG_APPEND = 1n
+@unsafe
provide let _FDFLAG_DSYNC = 2n
+@unsafe
provide let _FDFLAG_NONBLOCK = 4n
+@unsafe
provide let _FDFLAG_RSYNC = 8n
+@unsafe
provide let _FDFLAG_SYNC = 16n
// whence
+@unsafe
provide let _WHENCE_SET = 0n
+@unsafe
provide let _WHENCE_CUR = 1n
+@unsafe
provide let _WHENCE_END = 2n
// error codes
+@unsafe
provide let _ESUCCESS = 0n
+@unsafe
provide let _ETOOBIG = 1n
+@unsafe
provide let _EACCES = 2n
+@unsafe
provide let _EADDRINUSE = 3n
+@unsafe
provide let _EADDRNOTAVAIL = 4n
+@unsafe
provide let _EAFNOSUPPORT = 5n
+@unsafe
provide let _EAGAIN = 6n
+@unsafe
provide let _EALREADY = 7n
+@unsafe
provide let _EBADF = 8n
+@unsafe
provide let _EBADMSG = 9n
+@unsafe
provide let _EBUSY = 10n
+@unsafe
provide let _ECANCELED = 11n
+@unsafe
provide let _ECHILD = 12n
+@unsafe
provide let _ECONNABORTED = 13n
+@unsafe
provide let _ECONNREFUSED = 14n
+@unsafe
provide let _ECONNRESET = 15n
+@unsafe
provide let _EDEADLK = 16n
+@unsafe
provide let _EDESTADDRREQ = 17n
+@unsafe
provide let _EDOM = 18n
+@unsafe
provide let _EDQUOT = 19n
+@unsafe
provide let _EEXIST = 20n
+@unsafe
provide let _EFAULT = 21n
+@unsafe
provide let _EFBIG = 22n
+@unsafe
provide let _EHOSTUNREACH = 23n
+@unsafe
provide let _EIDRM = 24n
+@unsafe
provide let _EILSEQ = 25n
+@unsafe
provide let _EINPROGRESS = 26n
+@unsafe
provide let _EINTR = 27n
+@unsafe
provide let _EINVAL = 28n
+@unsafe
provide let _EIO = 29n
+@unsafe
provide let _EISCONN = 30n
+@unsafe
provide let _EISDIR = 31n
+@unsafe
provide let _ELOOP = 32n
+@unsafe
provide let _EMFILE = 33n
+@unsafe
provide let _EMLINK = 34n
+@unsafe
provide let _EMSGSIZE = 35n
+@unsafe
provide let _EMULTIHOP = 36n
+@unsafe
provide let _ENAMETOOLONG = 37n
+@unsafe
provide let _ENETDOWN = 38n
+@unsafe
provide let _ENETRESET = 39n
+@unsafe
provide let _ENETUNREACH = 40n
+@unsafe
provide let _ENFILE = 41n
+@unsafe
provide let _ENOBUFS = 42n
+@unsafe
provide let _ENODEV = 43n
+@unsafe
provide let _ENOENT = 44n
+@unsafe
provide let _ENOEXEC = 45n
+@unsafe
provide let _ENOLCK = 46n
+@unsafe
provide let _ENOLINK = 47n
+@unsafe
provide let _ENOMEM = 48n
+@unsafe
provide let _ENOMSG = 49n
+@unsafe
provide let _ENOPROTOOPT = 50n
+@unsafe
provide let _ENOSPC = 51n
+@unsafe
provide let _ENOSYS = 52n
+@unsafe
provide let _ENOTCONN = 53n
+@unsafe
provide let _ENOTDIR = 54n
+@unsafe
provide let _ENOTEMPTY = 55n
+@unsafe
provide let _ENOTRECOVERABLE = 56n
+@unsafe
provide let _ENOTSOCK = 57n
+@unsafe
provide let _ENOTSUP = 58n
+@unsafe
provide let _ENOTTY = 59n
+@unsafe
provide let _ENXIO = 60n
+@unsafe
provide let _EOVERFLOW = 61n
+@unsafe
provide let _EOWNERDEAD = 62n
+@unsafe
provide let _EPERM = 63n
+@unsafe
provide let _EPIPE = 64n
+@unsafe
provide let _EPROTO = 65n
+@unsafe
provide let _EPROTONOSUPPORT = 66n
+@unsafe
provide let _EPROTOTYPE = 67n
+@unsafe
provide let _ERANGE = 68n
+@unsafe
provide let _EROFS = 69n
+@unsafe
provide let _ESPIPE = 70n
+@unsafe
provide let _ESRCH = 71n
+@unsafe
provide let _ESTALE = 72n
+@unsafe
provide let _ETIMEDOUT = 73n
+@unsafe
provide let _ETXTBSY = 74n
+@unsafe
provide let _EXDEV = 75n
+@unsafe
provide let _ENOTCAPABLE = 76n
provide exception SystemError(Number)
+@disableGC
provide let stringOfSystemError = code => {
use WasmI32.{ (==), (>>) }
let n = WasmI32.fromGrain(code) >> 1n