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