From 72b21393b0c82669ff9005730cafb0b345a8a992 Mon Sep 17 00:00:00 2001 From: Alex Snezhko Date: Sun, 3 Mar 2024 16:07:09 -0800 Subject: [PATCH] feat(compiler)!: Remove arbitrary per-file compiler flags, add acceptable options as module attributes (#1804) Co-authored-by: Oscar Spencer --- cli/bin/grain.js | 4 - compiler/src/compile.re | 31 ++--- compiler/src/formatting/fmt.re | 53 +++++++- compiler/src/parsing/driver.re | 19 +-- compiler/src/parsing/parser.messages | 36 +++++ compiler/src/parsing/parser.mly | 4 +- compiler/src/parsing/parser_header.re | 11 +- compiler/src/parsing/parsetree.re | 5 +- compiler/src/parsing/parsetree_iter.re | 1 + compiler/src/parsing/well_formedness.re | 65 +++++---- compiler/src/typed/env.re | 6 +- compiler/src/typed/env.rei | 9 +- compiler/src/typed/typed_well_formedness.re | 9 +- compiler/src/typed/typemod.re | 13 +- compiler/src/utils/config.re | 128 +++++------------- compiler/src/utils/config.rei | 18 +-- compiler/test/grainfmt/comments.expected.gr | 13 ++ compiler/test/grainfmt/comments.input.gr | 11 ++ compiler/test/grainfmt/lets.expected.gr | 2 + compiler/test/grainfmt/lets.input.gr | 2 + compiler/test/input/mallocTight.gr | 2 +- compiler/test/input/memoryBase/asserts.gr | 1 - .../test/input/memoryBase/sameAsDefault.gr | 1 - .../test/input/memoryBase/slightlyHigher.gr | 1 - compiler/test/input/memoryBase/veryLarge.gr | 1 - compiler/test/input/memoryBase/zero.gr | 1 - compiler/test/input/unsafeWasmGlobals.gr | 1 - compiler/test/runner.re | 50 +++++-- compiler/test/suites/arrays.re | 2 + compiler/test/suites/basic_functionality.re | 8 +- compiler/test/suites/blocks.re | 2 + compiler/test/suites/chars.re | 6 + compiler/test/suites/comments.re | 24 ++++ compiler/test/suites/functions.re | 10 +- compiler/test/suites/memory_base.re | 23 +++- compiler/test/suites/optimizations.re | 66 ++++----- compiler/test/suites/parsing.re | 28 ++++ compiler/test/suites/print.re | 6 +- compiler/test/suites/strings.re | 29 ++++ docs/contributor/memory_management.md | 11 +- docs/contributor/runtime.md | 2 +- stdlib/pervasives.gr | 3 +- stdlib/runtime/atof/common.gr | 3 +- stdlib/runtime/atof/decimal.gr | 3 +- stdlib/runtime/atof/lemire.gr | 3 +- stdlib/runtime/atof/table.gr | 2 +- stdlib/runtime/atoi/parse.gr | 2 +- stdlib/runtime/bigint.gr | 2 +- stdlib/runtime/compare.gr | 2 +- stdlib/runtime/dataStructures.gr | 2 +- stdlib/runtime/equal.gr | 2 +- stdlib/runtime/exception.gr | 2 +- stdlib/runtime/gc.gr | 2 +- stdlib/runtime/malloc.gr | 2 +- stdlib/runtime/numberUtils.gr | 2 +- stdlib/runtime/numbers.gr | 2 +- stdlib/runtime/string.gr | 2 +- stdlib/runtime/unsafe/constants.gr | 2 +- stdlib/runtime/unsafe/memory.gr | 2 +- stdlib/runtime/unsafe/tags.gr | 2 +- stdlib/runtime/unsafe/wasmf32.gr | 2 +- stdlib/runtime/unsafe/wasmf64.gr | 2 +- stdlib/runtime/unsafe/wasmi32.gr | 2 +- stdlib/runtime/unsafe/wasmi64.gr | 2 +- stdlib/runtime/utils/printing.gr | 2 +- stdlib/runtime/wasi.gr | 102 +++++++++++++- 66 files changed, 563 insertions(+), 306 deletions(-) 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 b7e96ae6b2..b459191089 100644 --- a/compiler/src/compile.re +++ b/compiler/src/compile.re @@ -88,25 +88,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) { @@ -145,7 +126,15 @@ let next_state = (~is_root_file=false, {cstate_desc, cstate_filename} as cs) => cleanup(); Parsed(parsed); | Parsed(p) => - apply_inline_flags(p); + let has_attr = name => + List.exists( + attr => attr.Asttypes.attr_name.txt == name, + p.attributes, + ); + Grain_utils.Config.apply_attribute_flags( + ~no_pervasives=has_attr("noPervasives"), + ~runtime_mode=has_attr("runtimeMode"), + ); if (is_root_file) { Grain_utils.Config.set_root_config(); }; @@ -266,7 +255,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 0ed77044cc..e5c2293fdf 100644 --- a/compiler/src/formatting/fmt.re +++ b/compiler/src/formatting/fmt.re @@ -3991,12 +3991,53 @@ let print_program = (fmt, parsed_program) => { ) }; - group @@ - fmt.print_comment_range( - fmt, - enclosing_start_location(parsed_program.prog_loc), - parsed_program.module_name.loc, - ) + let attributes = + switch (parsed_program.attributes) { + | [] => + group( + fmt.print_comment_range( + fmt, + enclosing_start_location(parsed_program.prog_loc), + parsed_program.module_name.loc, + ), + ) + | _ => + group @@ + concat_map( + ~lead= + first => + fmt.print_comment_range( + fmt, + ~trail=hardline, + enclosing_start_location(parsed_program.prog_loc), + first.attr_loc, + ), + ~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), + parsed_program.attributes, + ) + }; + + attributes ++ string("module ") ++ string(parsed_program.module_name.txt) ++ toplevel; diff --git a/compiler/src/parsing/driver.re b/compiler/src/parsing/driver.re index 72cd607514..33af6de1d4 100644 --- a/compiler/src/parsing/driver.re +++ b/compiler/src/parsing/driver.re @@ -150,6 +150,11 @@ let parse = (~name=?, lexbuf, source): Parsetree.parsed_program => { let read_imports = (program: Parsetree.parsed_program) => { open Parsetree_iter; + let module_has_attr = name => + List.exists( + attr => attr.Asttypes.attr_name.txt == name, + program.attributes, + ); let implicit_opens = List.map( o => { @@ -159,15 +164,11 @@ let read_imports = (program: Parsetree.parsed_program) => { | Grain_utils.Config.Gc_mod => Location.mknoloc("runtime/gc.gr") } }, - 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( + ~no_pervasives=module_has_attr("noPervasives"), + ~runtime_mode=module_has_attr("runtimeMode"), + 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 2410ec7300..7334ace845 100644 --- a/compiler/src/parsing/parser.mly +++ b/compiler/src/parsing/parser.mly @@ -744,5 +744,5 @@ 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 module_header eos toplevel_stmts EOF { make_program ~loc:(to_loc $sloc) ~core_loc:(to_loc (fst $loc($3), snd $loc)) ~attributes:$2 $3 $5 } + | opt_eols attributes module_header eos? EOF { make_program ~loc:(to_loc $sloc) ~core_loc:(to_loc (fst $loc($3), snd $loc)) ~attributes:$2 $3 [] } diff --git a/compiler/src/parsing/parser_header.re b/compiler/src/parsing/parser_header.re index 435b625d67..cfb3319836 100644 --- a/compiler/src/parsing/parser_header.re +++ b/compiler/src/parsing/parser_header.re @@ -89,13 +89,20 @@ 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}; - {module_name, statements, comments: [], prog_loc}; + { + attributes, + 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..b6f4bee5d9 100644 --- a/compiler/src/parsing/parsetree.re +++ b/compiler/src/parsing/parsetree.re @@ -693,9 +693,12 @@ type comment = [@deriving (sexp, yojson)] type parsed_program = { + 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 of the program + [@sexp_drop_if sexp_locs_disabled] + prog_core_loc: Location.t // The core location, without attributes }; diff --git a/compiler/src/parsing/parsetree_iter.re b/compiler/src/parsing/parsetree_iter.re index fcb6d9e3c8..815130e4d5 100644 --- a/compiler/src/parsing/parsetree_iter.re +++ b/compiler/src/parsing/parsetree_iter.re @@ -87,6 +87,7 @@ let iter_attributes = (hooks, attrs) => { let rec iter_parsed_program = (hooks, {statements} as program) => { hooks.enter_parsed_program(program); + iter_attributes(hooks, program.attributes); iter_toplevel_stmts(hooks, statements); hooks.leave_parsed_program(program); } diff --git a/compiler/src/parsing/well_formedness.re b/compiler/src/parsing/well_formedness.re index fdabd97ade..59c3b599fe 100644 --- a/compiler/src/parsing/well_formedness.re +++ b/compiler/src/parsing/well_formedness.re @@ -11,7 +11,7 @@ type wferr = | RHSLetRecMayOnlyBeFunction(Location.t) | NoLetRecMut(Location.t) | RationalZeroDenominator(Location.t) - | UnknownAttribute(string, Location.t) + | UnknownAttribute(string, string, Location.t) | InvalidAttributeArity(string, int, Location.t) | AttributeDisallowed(string, Location.t) | LoopControlOutsideLoop(string, Location.t) @@ -49,8 +49,8 @@ let prepare_error = errorf(~loc, "let rec may not be used with the `mut` keyword.") | RationalZeroDenominator(loc) => errorf(~loc, "Rational numbers may not have a denominator of zero.") - | UnknownAttribute(attr, loc) => - errorf(~loc, "Unknown attribute `%s`.", attr) + | UnknownAttribute(attr_context, attr, loc) => + errorf(~loc, "Unknown %s attribute `%s`.", attr_context, attr) | InvalidAttributeArity(attr, arity, loc) => switch (arity) { | 0 => errorf(~loc, "Attribute `%s` expects no arguments.", attr) @@ -300,35 +300,28 @@ type known_attribute = { arity: int, }; -let known_attributes = [ - {name: "disableGC", arity: 0}, - {name: "unsafe", arity: 0}, - {name: "externalName", arity: 1}, -]; - -let valid_attributes = (errs, super) => { - let enter_attribute = - ({Asttypes.attr_name: {txt, loc}, attr_args: args} as attr) => { - switch (List.find_opt(({name}) => name == txt, known_attributes)) { - | Some({arity}) when List.length(args) != arity => - errs := [InvalidAttributeArity(txt, arity, loc), ...errs^] - | None => errs := [UnknownAttribute(txt, loc), ...errs^] - | _ => () - }; - super.enter_attribute(attr); +let disallowed_attributes = (errs, super) => { + let validate_against_known = (attrs, known_attributes, context) => { + List.iter( + ({Asttypes.attr_name: {txt, loc}, attr_args: args}) => { + switch (List.find_opt(({name}) => name == txt, known_attributes)) { + | Some({arity}) when List.length(args) != arity => + errs := [InvalidAttributeArity(txt, arity, loc), ...errs^] + | None => errs := [UnknownAttribute(context, txt, loc), ...errs^] + | _ => () + } + }, + attrs, + ); }; - { - errs, - iter_hooks: { - ...super, - enter_attribute, - }, - }; -}; + let known_expr_attributes = [ + {name: "disableGC", arity: 0}, + {name: "unsafe", arity: 0}, + {name: "externalName", arity: 1}, + ]; -let disallowed_attributes = (errs, super) => { - let enter_expression = ({pexp_desc: desc, pexp_attributes: attrs} as e) => { + let enter_expression = ({pexp_attributes: attrs} as e) => { switch ( List.find_opt( ({Asttypes.attr_name: {txt}}) => txt == "externalName", @@ -345,8 +338,10 @@ let disallowed_attributes = (errs, super) => { ] | None => () }; + validate_against_known(attrs, known_expr_attributes, "expression"); super.enter_expression(e); }; + let enter_toplevel_stmt = ({ptop_desc: desc, ptop_attributes: attrs} as top) => { switch ( @@ -399,15 +394,26 @@ let disallowed_attributes = (errs, super) => { } | None => () }; + validate_against_known(attrs, known_expr_attributes, "top-level"); super.enter_toplevel_stmt(top); }; + let enter_parsed_program = ({attributes} as prog) => { + let known_module_attributes = [ + {name: "runtimeMode", arity: 0}, + {name: "noPervasives", arity: 0}, + ]; + validate_against_known(attributes, known_module_attributes, "module"); + super.enter_parsed_program(prog); + }; + { errs, iter_hooks: { ...super, enter_expression, enter_toplevel_stmt, + enter_parsed_program, }, }; }; @@ -842,7 +848,6 @@ let well_formedness_checks = [ only_functions_oh_rhs_letrec, no_letrec_mut, no_zero_denominator_rational, - valid_attributes, disallowed_attributes, no_loop_control_statement_outside_of_loop, malformed_return_statements, diff --git a/compiler/src/typed/env.re b/compiler/src/typed/env.re index e3939d457e..8bb9894a30 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 6a74a72aba..8680722a69 100644 --- a/compiler/src/typed/typed_well_formedness.re +++ b/compiler/src/typed/typed_well_formedness.re @@ -385,11 +385,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 b417900844..0816009c04 100644 --- a/compiler/src/typed/typemod.re +++ b/compiler/src/typed/typemod.re @@ -1049,17 +1049,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..8bcca63f96 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 = (~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..bab3999d17 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,14 +178,12 @@ 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) => - 'a; +let with_attribute_flags: + (~no_pervasives: bool, ~runtime_mode: bool, unit => 'a) => 'a; type implicit_opens = | Pervasives_mod diff --git a/compiler/test/grainfmt/comments.expected.gr b/compiler/test/grainfmt/comments.expected.gr index eac951934b..0064cd972a 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 diff --git a/compiler/test/grainfmt/comments.input.gr b/compiler/test/grainfmt/comments.input.gr index f52a7c89d8..1688661d1c 100644 --- a/compiler/test/grainfmt/comments.input.gr +++ b/compiler/test/grainfmt/comments.input.gr @@ -1,3 +1,8 @@ +// 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 +254,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..2000d1b027 100644 --- a/compiler/test/runner.re +++ b/compiler/test/runner.re @@ -215,7 +215,8 @@ let doc = (file, arguments) => { let module_header = "module Test; "; -let makeSnapshotRunner = (~config_fn=?, test, name, prog) => { +let makeSnapshotRunner = + (~config_fn=?, test, ~module_header=module_header, name, prog) => { test(name, ({expect}) => { Config.preserve_all_configs(() => { ignore @@ @@ -230,7 +231,8 @@ let makeSnapshotRunner = (~config_fn=?, test, name, prog) => { }); }; -let makeFilesizeRunner = (test, ~config_fn=?, name, prog, size) => { +let makeFilesizeRunner = + (test, ~config_fn=?, ~module_header=module_header, name, prog, size) => { test(name, ({expect}) => { Config.preserve_all_configs(() => { ignore @@ compile(~config_fn?, name, module_header ++ prog); @@ -242,21 +244,26 @@ 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) => { +let makeCompileErrorRunner = + (test, ~module_header=module_header, name, prog, msg) => { test( name, ({expect}) => { @@ -274,7 +281,8 @@ let makeCompileErrorRunner = (test, name, prog, msg) => { ); }; -let makeWarningRunner = (test, name, prog, warning) => { +let makeWarningRunner = + (test, ~module_header=module_header, name, prog, warning) => { test(name, ({expect}) => { Config.preserve_all_configs(() => { Config.print_warnings := false; @@ -284,7 +292,7 @@ let makeWarningRunner = (test, name, prog, warning) => { }); }; -let makeNoWarningRunner = (test, name, prog) => { +let makeNoWarningRunner = (test, ~module_header=module_header, name, prog) => { test(name, ({expect}) => { Config.preserve_all_configs(() => { Config.print_warnings := false; @@ -295,7 +303,16 @@ let makeNoWarningRunner = (test, name, prog) => { }; let makeRunner = - (test, ~num_pages=?, ~config_fn=?, ~extra_args=?, name, prog, expected) => { + ( + test, + ~num_pages=?, + ~config_fn=?, + ~extra_args=?, + ~module_header=module_header, + name, + prog, + expected, + ) => { test(name, ({expect}) => { Config.preserve_all_configs(() => { ignore @@ compile(~num_pages?, ~config_fn?, name, module_header ++ prog); @@ -311,6 +328,7 @@ let makeErrorRunner = ~check_exists=true, ~num_pages=?, ~config_fn=?, + ~module_header=module_header, name, prog, expected, @@ -442,8 +460,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 +476,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..78db8998ac 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: [], 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..61c0db13cb 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: [], 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..84b5b0fd26 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: [], 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..eaf6095e2c 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: [], 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: [], 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: [], 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..418e0e5cd9 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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/functions.re b/compiler/test/suites/functions.re index 16ade7c53a..0d9f0136e0 100644 --- a/compiler/test/suites/functions.re +++ b/compiler/test/suites/functions.re @@ -150,9 +150,15 @@ describe("functions", ({test, testSkip}) => { assertCompileError("nonfunction_1", "let x = 5; x(3)", "type"); assertSnapshot("lambda_pat_any", "let x = (_) => 5; x(\"foo\")"); assertCompileError( - "unknown_attribute", + "unknown_toplevel_attribute", "@unknown let x = () => 5", - "Unknown attribute", + "Unknown top-level attribute", + ); + assertCompileError( + ~module_header="@unsafe module Test", + "unknown_module_attribute", + "", + "Unknown module attribute", ); assertSnapshot( "func_record_associativity1", 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 d9d07cc53d..fec048a60b 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.gr", + ~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..673ce81a0f 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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..41f19cfb56 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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: [], 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/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 b75fc625bc..cf433a6606 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 81d6736a52..63a69fdfd5 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 901b12bc12..57fdb26470 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