diff --git a/.gitignore b/.gitignore index f7d6ddc499..c52a9fda70 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ myocamlbuild.ml /*.gr _esy/ pkg/ +target/ # Autogenerated .messages file from Menhir *.messages.generated diff --git a/cli/bin/grain.js b/cli/bin/grain.js index 97549c78b8..d2feb00747 100755 --- a/cli/bin/grain.js +++ b/cli/bin/grain.js @@ -1,11 +1,21 @@ #!/usr/bin/env node const commander = require("commander"); +const path = require("path"); const exec = require("./exec.js"); const pkgJson = require("../package.json"); const stdlibPath = require("@grain/stdlib"); +function defaultWasmLocation(file, options) { + const targetDir = options.targetDir + ? path.resolve(options.targetDir) + : path.resolve("target"); + const profile = options.release ? "release" : "debug"; + const basename = path.basename(file).replace(/\.gr$/, ".wasm"); + return path.join(targetDir, profile, basename); +} + function list(val) { return val.split(","); } @@ -89,12 +99,23 @@ class GrainCommand extends commander.Command { list, [], ); + cmd.forwardOption( + "-L, --library ", + "load libraries: -L name1=dir1,name2=dir2", + list, + [], + ); cmd.forwardOption( "-S, --stdlib ", "override the standard library with your own", null, stdlibPath, ); + cmd.forwardOption( + "--target-dir ", + "directory where build artifacts are written", + ); + cmd.forwardOption("--project-root ", "project root directory"); cmd.forwardOption( "--initial-memory-pages ", "initial number of WebAssembly memory pages", @@ -180,7 +201,7 @@ program .action(function (file, options, program) { const success = exec.grainc(file, options, program); if (success) { - const outFile = options.o ?? file.replace(/\.gr$/, ".wasm"); + const outFile = options.o ?? defaultWasmLocation(file, options); exec.grainrun(unprocessedArgs, outFile, options, program); } }); diff --git a/compiler/grainc/grainc.re b/compiler/grainc/grainc.re index 5b834e94ea..f81ae5f991 100644 --- a/compiler/grainc/grainc.re +++ b/compiler/grainc/grainc.re @@ -2,8 +2,6 @@ open Grain; open Grain_typed; open Compile; open Printf; -open Lexing; -open Filename; open Cmdliner; let () = @@ -49,36 +47,33 @@ let error_wrapped = f => exit(2); }; -let compile_file = (~outfile=?, filename) => { - let outfile = - Option.value( - ~default=Compile.default_object_filename(filename), - outfile, - ); - ignore(Compile.compile_file(~outfile, filename)); -}; -let compile_file = (~outfile=?, filename) => - error_wrapped(() => compile_file(~outfile?, filename)); +let compile_file = (~object_outfile=?, filename) => + error_wrapped(() => + ignore(Compile.compile_file(~object_outfile?, filename)) + ); -let grainc = (single_file_mode, name, outfile) => { +let grainc = (single_file_mode, input_path, object_outfile) => { Grain_utils.Config.set_root_config(); if (!Printexc.backtrace_status() && Grain_utils.Config.verbose^) { Printexc.record_backtrace(true); }; + let name = Fp.toString(input_path); + if (single_file_mode) { - compile_file(~outfile?, name); + compile_file(~object_outfile?, name); } else { switch (Grain_utils.Config.wasi_polyfill^) { - | Some(name) => + | Some(polyfill) => + let poyfill = Fp.toString(polyfill); Grain_utils.Config.preserve_config(() => { Grain_utils.Config.compilation_mode := Grain_utils.Config.Runtime; - Module_resolution.load_dependency_graph(name); + Module_resolution.load_dependency_graph(poyfill); let to_compile = Module_resolution.get_out_of_date_dependencies(); List.iter(compile_file, to_compile); - compile_file(name); - }) + compile_file(poyfill); + }); | None => () }; @@ -90,21 +85,20 @@ let grainc = (single_file_mode, name, outfile) => { if (Grain_utils.Config.statically_link^) { let main_object = Compile.default_object_filename(name); let outfile = - Option.value(~default=Compile.default_wasm_filename(name), outfile); + Option.value( + ~default=Compile.default_wasm_filename(name), + object_outfile, + ); let dependencies = Module_resolution.get_dependencies(); error_wrapped(() => Link.link(~main_object, ~outfile, dependencies)); }; }; - - `Ok(); }; -/** Converter which checks that the given output filename is valid */ - let output_file_conv = { let parse = s => { - let s_dir = dirname(s); + let s_dir = Filename.dirname(s); Sys.file_exists(s_dir) ? if (Sys.is_directory(s_dir)) { `Ok(s); @@ -116,19 +110,17 @@ let output_file_conv = { (parse, Format.pp_print_string); }; -let input_file_conv = { - open Arg; - let (prsr, prntr) = non_dir_file; - - (filename => prsr(filename), prntr); -}; - let input_filename = { let doc = sprintf("Grain source file to compile"); let docv = "FILE"; Arg.( required - & pos(~rev=true, 0, some(input_file_conv), None) + & pos( + ~rev=true, + 0, + some(Grain_utils.Filepath.Args.ExistingFile.cmdliner_converter), + None, + ) & info([], ~docv, ~doc) ); }; @@ -156,12 +148,10 @@ let cmd = { Cmd.v( Cmd.info(Sys.argv[0], ~version, ~doc), Term.( - ret( - Grain_utils.Config.with_cli_options(grainc) - $ single_file_mode - $ input_filename - $ output_filename, - ) + Grain_utils.Config.with_cli_options(grainc) + $ single_file_mode + $ input_filename + $ output_filename ), ); }; diff --git a/compiler/graindoc/graindoc.re b/compiler/graindoc/graindoc.re index 4b8586eeba..c8bd6fb36b 100644 --- a/compiler/graindoc/graindoc.re +++ b/compiler/graindoc/graindoc.re @@ -51,14 +51,7 @@ let compile_typed = file => { Module_resolution.load_dependency_graph(file); let to_compile = Module_resolution.get_out_of_date_dependencies(); List.iter( - file => - ignore( - compile_file( - ~hook=stop_after_object_emitted, - ~outfile=Compile.default_wasm_filename(file), - file, - ), - ), + file => ignore(compile_file(~hook=stop_after_object_emitted, file)), to_compile, ); compile_file(~hook=stop_after_typed, file); diff --git a/compiler/src/codegen/compcore.re b/compiler/src/codegen/compcore.re index d2cef90bca..b0bee9b085 100644 --- a/compiler/src/codegen/compcore.re +++ b/compiler/src/codegen/compcore.re @@ -3317,8 +3317,8 @@ let compile_wasm_module = }; switch (Config.profile^) { - | Some(Release) => Optimize_mod.optimize(wasm_mod) - | None => () + | Debug => () + | Release => Optimize_mod.optimize(wasm_mod) }; wasm_mod; }; diff --git a/compiler/src/codegen/data_representations.re b/compiler/src/codegen/data_representations.re index c13f3aeac5..1d9c1a0838 100644 --- a/compiler/src/codegen/data_representations.re +++ b/compiler/src/codegen/data_representations.re @@ -33,7 +33,7 @@ let build_core_data_representations = (wasm_mod: Module.t) => { switch (Type_builder.build_and_dispose(builder)) { | Ok([ty]) => switch (name) { - | Some(name) when Config.profile^ != Some(Release) => + | Some(name) when Config.profile^ != Release => Heap_type.set_type_name(wasm_mod, ty, name); List.iteri( (index, (name, _)) => { diff --git a/compiler/src/codegen/emitmod.re b/compiler/src/codegen/emitmod.re index c447d5dcec..cec877af1c 100644 --- a/compiler/src/codegen/emitmod.re +++ b/compiler/src/codegen/emitmod.re @@ -27,7 +27,7 @@ let emit_object = (mashed: mash_program, outfile) => { close_out(oc); }; - let oc = open_out_bin(outfile); + let oc = Fs_access.open_file_for_writing(outfile); output_bytes(oc, Cmi_format.magic); let version_length = String.length(Config.version); @@ -106,8 +106,8 @@ let emit_binary = (asm, signature, outfile) => { close_out(oc); }; switch (Config.profile^) { - | Some(Release) => Binaryen.Settings.set_debug_info(false) - | _ => Binaryen.Settings.set_debug_info(true) + | Debug => Binaryen.Settings.set_debug_info(true) + | Release => Binaryen.Settings.set_debug_info(false) }; let source_map_name = if (Config.source_map^) { diff --git a/compiler/src/codegen/linkedtree.re b/compiler/src/codegen/linkedtree.re index c0aec06180..83a32a537b 100644 --- a/compiler/src/codegen/linkedtree.re +++ b/compiler/src/codegen/linkedtree.re @@ -35,15 +35,13 @@ let internal_name = (id, dep_id) => { }; let link = (~main_object, dependencies) => { - let new_base_dir = Filepath.String.dirname; - let resolve = (~base_dir=?, mod_name) => Module_resolution.locate_unit_object_file(~base_dir?, mod_name); let wasi_polyfill = Option.map( - Module_resolution.get_object_name, - Config.wasi_polyfill_path(), + p => Module_resolution.get_object_name(Fp.toString(p)), + Config.wasi_polyfill^, ); let dependencies = @@ -75,6 +73,8 @@ let link = (~main_object, dependencies) => { let process_mashtree = (~main, dep, tree) => { let globals = tree.mash_code.globals; + let dep_base_dir = + Fp.toString(Fp.dirName(Filepath.String.derelativize(dep))); let imports = List.fold_left( @@ -114,7 +114,7 @@ let link = (~main_object, dependencies) => { } | MImportGrain => let resolved_module = - resolve(~base_dir=new_base_dir(dep), import.mimp_mod); + resolve(~base_dir=dep_base_dir, import.mimp_mod); process_import(resolved_module); imports; }; diff --git a/compiler/src/compile.re b/compiler/src/compile.re index 8197d8b83d..6141838d84 100644 --- a/compiler/src/compile.re +++ b/compiler/src/compile.re @@ -24,7 +24,6 @@ type compilation_state = { cstate_desc: compilation_state_desc, cstate_filename: option(string), cstate_object_outfile: option(string), - cstate_wasm_outfile: option(string), }; type compilation_action = @@ -32,16 +31,16 @@ type compilation_action = | Stop; let default_wasm_filename = name => - Filepath.String.replace_extension(name, "wasm"); + Module_resolution.source_output_filename(~ext="wasm", name); let default_object_filename = name => - Filepath.String.replace_extension(name, "gro"); + Module_resolution.source_artifact_filename(~ext="gro", name); let default_mashtree_filename = name => - Filepath.String.replace_extension(name, "mashtree"); + Module_resolution.source_artifact_filename(~ext="mashtree", name); -let save_mashed = (mashed, outfile) => { - switch (outfile) { - | Some(outfile) => - let outfile = default_mashtree_filename(outfile); +let save_mashed = (mashed, filename) => { + switch (filename) { + | Some(filename) => + let outfile = default_mashtree_filename(filename); Grain_utils.Fs_access.ensure_parent_directory_exists(outfile); let mash_string = Sexplib.Sexp.to_string_hum @@ Mashtree.sexp_of_mash_program(mashed); @@ -148,7 +147,7 @@ let next_state = ({cstate_desc, cstate_filename} as cs) => { | Optimized(optimized) => let mashed = Transl_anf.transl_anf_program(optimized); if (Config.debug^) { - save_mashed(mashed, cs.cstate_object_outfile); + save_mashed(mashed, cs.cstate_filename); }; Mashed(mashed); | Mashed(mashed) => @@ -225,23 +224,6 @@ let stop_after_object_emitted = | {cstate_desc: ObjectEmitted} => Stop | s => Continue(s); -let compile_wasi_polyfill = () => { - switch (Grain_utils.Config.wasi_polyfill^) { - | Some(file) => - Grain_utils.Config.preserve_config(() => { - Grain_utils.Config.compilation_mode := Grain_utils.Config.Runtime; - let cstate = { - cstate_desc: Initial(InputFile(file)), - cstate_filename: Some(file), - cstate_wasm_outfile: Some(default_wasm_filename(file)), - cstate_object_outfile: Some(default_object_filename(file)), - }; - ignore(compile_resume(~hook=stop_after_object_emitted, cstate)); - }) - | None => () - }; -}; - let reset_compiler_state = () => { Driver.reset(); Location.reset_exceptions(); @@ -253,26 +235,29 @@ let reset_compiler_state = () => { Grain_utils.Warnings.reset_warnings(); }; -let compile_string = (~hook=?, ~name=?, ~outfile=?, str) => { +let compile_string = (~hook=?, ~name=?, ~object_outfile=?, str) => { Ident.setup(); let cstate = { cstate_desc: Initial(InputString(str)), cstate_filename: name, - cstate_wasm_outfile: outfile, - cstate_object_outfile: Option.map(default_object_filename, outfile), + cstate_object_outfile: object_outfile, }; Grain_utils.Config.preserve_all_configs(() => compile_resume(~hook?, cstate) ); }; -let compile_file = (~hook=?, ~outfile=?, filename) => { +let compile_file = (~hook=?, ~object_outfile=?, filename) => { Ident.setup(); + let object_outfile = + switch (object_outfile) { + | Some(_) as o => o + | None => Some(default_object_filename(filename)) + }; let cstate = { cstate_desc: Initial(InputFile(filename)), cstate_filename: Some(filename), - cstate_wasm_outfile: outfile, - cstate_object_outfile: Option.map(default_object_filename, outfile), + cstate_object_outfile: object_outfile, }; Grain_utils.Config.preserve_all_configs(() => compile_resume(~hook?, cstate) diff --git a/compiler/src/compile.rei b/compiler/src/compile.rei index 57dac5c243..ebe04310c7 100644 --- a/compiler/src/compile.rei +++ b/compiler/src/compile.rei @@ -22,7 +22,6 @@ type compilation_state = { cstate_desc: compilation_state_desc, cstate_filename: option(string), cstate_object_outfile: option(string), - cstate_wasm_outfile: option(string), }; type compilation_action = @@ -50,13 +49,11 @@ let stop_after_object_emitted: compilation_state => compilation_action; let reset_compiler_state: unit => unit; -let compile_wasi_polyfill: unit => unit; - let compile_string: ( ~hook: compilation_state => compilation_action=?, ~name: string=?, - ~outfile: string=?, + ~object_outfile: string=?, string ) => compilation_state; @@ -64,7 +61,7 @@ let compile_string: let compile_file: ( ~hook: compilation_state => compilation_action=?, - ~outfile: string=?, + ~object_outfile: string=?, string ) => compilation_state; diff --git a/compiler/src/language_server/code_file.re b/compiler/src/language_server/code_file.re index 56ec230518..1ed582fa03 100644 --- a/compiler/src/language_server/code_file.re +++ b/compiler/src/language_server/code_file.re @@ -51,14 +51,7 @@ let compile = (file, src) => { reset_compiler_state(); Module_resolution.load_dependency_graph_from_string(file, src); let to_compile = Module_resolution.get_out_of_date_dependencies(); - List.iter( - file => { - ignore( - compile_file(~outfile=Compile.default_object_filename(file), file), - ) - }, - to_compile, - ); + List.iter(file => ignore(compile_file(file)), to_compile); compile_string(~hook=stop_after_typed_well_formed, ~name=file, src); }; diff --git a/compiler/src/language_server/initialize.re b/compiler/src/language_server/initialize.re index a87f3132ec..fae0e12dc3 100644 --- a/compiler/src/language_server/initialize.re +++ b/compiler/src/language_server/initialize.re @@ -1,3 +1,4 @@ +open Grain_utils; open Grain_typed; // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initializeParams @@ -88,6 +89,18 @@ module ResponseResult = { }; }; +let set_project_root_from_uri = root_uri => { + switch (root_uri) { + | Some(uri) => + let path = Utils.uri_to_filename(uri); + switch (Fp.absoluteCurrentPlatform(path)) { + | Some(abs) => Config.project_root := abs + | None => () + }; + | None => () + }; +}; + let process = ( ~id: Protocol.message_id, @@ -97,6 +110,7 @@ let process = ) => { // The initialize request can set up the initial trace level Trace.set_level(params.trace); + set_project_root_from_uri(params.root_uri); Protocol.response( ~id, ResponseResult.to_yojson({capabilities: ResponseResult.capabilities}), diff --git a/compiler/src/typed/env.re b/compiler/src/typed/env.re index 319cea56ef..f01b743e6a 100644 --- a/compiler/src/typed/env.re +++ b/compiler/src/typed/env.re @@ -732,6 +732,7 @@ type pers_struct = { ps_crcs: list((string, Digest.t)), ps_crc: Digest.t, ps_filename: string, + ps_base_dir: string, }; let persistent_structures: Hashtbl.t(string, option(pers_struct)) = @@ -776,7 +777,7 @@ let check_consistency = ps => ((name, crc)) => { let resolved_file_name = Module_resolution.locate_unit_object_file( - ~base_dir=Filepath.String.dirname(ps.ps_filename), + ~base_dir=ps.ps_base_dir, name, ); Consistbl.check(crc_units, resolved_file_name, crc, ps.ps_filename); @@ -862,6 +863,8 @@ let acknowledge_pers_struct = (check, {Persistent_signature.filename, cmi}) => { TModSignature(sign), ); + let ps_base_dir = + Fp.toString(Fp.dirName(Filepath.String.derelativize(filename))); let ps = { ps_name: name, ps_sig: lazy(Subst.signature(Subst.identity, sign)), @@ -869,6 +872,7 @@ let acknowledge_pers_struct = (check, {Persistent_signature.filename, cmi}) => { ps_crcs: crcs, ps_crc: crc, ps_filename: filename, + ps_base_dir, }; if (check) { @@ -2287,16 +2291,20 @@ let add_cmi_to_persistent_structures = (filename, cmi) => { TModSignature(cmi.cmi_sign), ); + let ps_filename = + Filepath.String.derelativize( + Module_resolution.source_artifact_filename(~ext="gro", filename), + ); + let ps_base_dir = Fp.toString(Fp.dirName(ps_filename)); + let ps_filename = Fp.toString(ps_filename); let ps = { ps_name: cmi.cmi_name, ps_sig: lazy(Subst.signature(Subst.identity, cmi.cmi_sign)), ps_comps: comps, ps_crcs: cmi.cmi_crcs, ps_crc: cmi.cmi_crc, - ps_filename: - Module_resolution.get_object_name( - Filepath.to_string(Filepath.String.derelativize(filename)), - ), + ps_filename, + ps_base_dir, }; save_pers_struct(ps); diff --git a/compiler/src/typed/module_resolution.re b/compiler/src/typed/module_resolution.re index 58072faacc..9fc3d1ab85 100644 --- a/compiler/src/typed/module_resolution.re +++ b/compiler/src/typed/module_resolution.re @@ -3,15 +3,62 @@ open Grain_utils; open Cmi_format; type error = - | No_module_file(Location.t, string, option(string)); + | No_module_file(Location.t, string, option(string)) + | Source_outside_roots(Fp.t(Fp.absolute)); exception Error(error); let error = err => raise(Error(err)); +type origin = + | Project + | Stdlib + | Library(string); + +type source_root = { + origin, + root: Fp.t(Fp.absolute), +}; + +let object_search_path = () => Config.include_dirs^; + +let source_roots = () => { + let libs = + List.map( + ((name, path)) => + { + origin: Library(name), + root: path, + }, + Config.libraries^, + ); + let stdlib = + switch (Config.stdlib_dir^) { + | Some(p) => [ + { + origin: Stdlib, + root: p, + }, + ] + | None => [] + }; + let project = [ + { + origin: Project, + root: Config.project_root^, + }, + ]; + stdlib @ libs @ project; +}; + type module_location_result = - | GrainModule(string, option(string)) /* Grain Source file, Compiled object */ - | ObjectFile(string); /* Compiled object */ + | GrainModule({ + source: Fp.t(Fp.absolute), + compiled_object: option(Fp.t(Fp.absolute)), + origin, + root: Fp.t(Fp.absolute), + }) + | ObjectFile({compiled_object: Fp.t(Fp.absolute)}); let current_filename: ref(unit => string) = ref(() => failwith("current_filename should be filled in by env.re")); @@ -34,43 +81,86 @@ let read_file_cmi = f => { }; }; -let get_object_name = name => Filepath.String.replace_extension(name, "gro"); +let profile_dir_name = () => + switch (Config.profile^) { + | Config.Debug => "debug" + | Config.Release => "release" + }; -let find_ext_in_dir = (dir, name) => { - let fullname = Filepath.String.concat(dir, name); - let rec process_ext = +let source_to_artifact_path = + (~ext, ~origin, ~root, srcpath: Fp.t(Fp.absolute)): Fp.t(Fp.absolute) => { + let path = + Filepath.replace_extension( + Fp.relativizeExn(~source=root, ~dest=srcpath), + ext, + ); + let base = Fp.At.(Config.target_dir^ / profile_dir_name()); + let dir = + switch (origin) { + | Project => Fp.At.(base / "build") + | Stdlib => Fp.At.(base / "deps" / "stdlib") + | Library(name) => Fp.At.(base / "deps" / name) + }; + Fp.join(dir, path); +}; + +let classify_source = (srcpath: Fp.t(Fp.absolute)) => { + let roots = source_roots(); + let rec find = fun | [] => None - | [ext, ...tl] => { - let with_ext = Filepath.String.replace_extension(fullname, ext); - if (file_exists(with_ext)) { - Some((with_ext, dir, name)); - } else { - process_ext(tl); - }; - }; - process_ext; + | [{origin, root}, ..._] when Filepath.is_under(~root, srcpath) => + Some((origin, root)) + | [_, ...rest] => find(rest); + find(roots); }; -let find_in_path_uncap = - (~check_src=false, ~check_object=false, base_dir, path, name) => { - let exts = (check_src ? ["gr"] : []) @ (check_object ? ["gro"] : []); - let rec try_dir = - fun - | [] => raise(Not_found) - | [dir, ...rem] => { - switch (find_ext_in_dir(dir, name, exts)) { - | Some(path) => path - | None => try_dir(rem) - }; - }; - if (!Filepath.String.is_relative(name) && Fs_access.file_exists(name)) { - (name, Filepath.String.dirname(name), Filepath.String.basename(name)); - } else if (Filepath.String.is_relpath(name)) { - try_dir([base_dir]); - } else { - try(try_dir(path)) { - | Not_found => raise(Not_found) +let object_file_for_source = (~origin, ~root, srcpath) => { + let target_obj = + source_to_artifact_path(~ext="gro", ~origin, ~root, srcpath); + let cached = + file_exists(Fp.toString(target_obj)) ? Some(target_obj) : None; + GrainModule({ + source: srcpath, + compiled_object: cached, + origin, + root, + }); +}; + +let find_object_in_path = (path, unit_name) => { + switch (Fp.relative(unit_name)) { + | None => None + | Some(rel) => + let rel = Filepath.replace_extension(rel, "gro"); + let rec try_dir = ( + fun + | [] => None + | [dir, ...rest] => { + let candidate = Fp.join(dir, rel); + if (Filepath.is_under(~root=dir, candidate) + && file_exists(Fp.toString(candidate))) { + Some(candidate); + } else { + try_dir(rest); + }; + } + ); + try_dir(path); + }; +}; + +let find_source_under_root = (root, unit_name) => { + switch (Fp.relative(unit_name)) { + | None => None + | Some(rel) => + let rel = Filepath.replace_extension(rel, "gr"); + let candidate = Fp.join(root, rel); + if (Filepath.is_under(~root, candidate) + && file_exists(Fp.toString(candidate))) { + Some(candidate); + } else { + None; }; }; }; @@ -79,44 +169,19 @@ module PathTbl = { type t('a) = Hashtbl.t(string, 'a); let create: int => t('a) = Hashtbl.create; - let add: (t('a), (string, string), 'a) => unit = - (tbl, (dir, unit_name), v) => { - let dir = Filepath.String.realpath_quick(dir); - Hashtbl.add(tbl, Filepath.String.smart_cat(dir, unit_name), v); - }; + let key = (dir: Fp.t(Fp.absolute), unit_name) => + Format.sprintf("%s::%s", Fp.toString(dir), unit_name); - let find_opt: - (~disable_relpath: bool=?, t('a), string, list(string), string) => - option('a) = - (~disable_relpath=false, tbl, base_path, path, unit_name) => - if (!disable_relpath && Filepath.String.is_relpath(unit_name)) { - Hashtbl.find_opt( - tbl, - Filepath.String.canonicalize_relpath(base_path, unit_name), - ); - } else { - List.fold_left( - (acc, elt) => { - switch (acc) { - | Some(_) => acc - | None => - Hashtbl.find_opt( - tbl, - Filepath.String.(smart_cat(realpath_quick(elt), unit_name)), - ) - } - }, - None, - path, - ); - }; + let add = (tbl, dir, unit_name, v) => + Hashtbl.add(tbl, key(dir, unit_name), v); + + let find_opt = (tbl, dir, unit_name) => + Hashtbl.find_opt(tbl, key(dir, unit_name)); }; let located_module_cache: Hashtbl.t(string, PathTbl.t(module_location_result)) = Hashtbl.create(16); -let resolutions: Hashtbl.t(string, PathTbl.t(string)) = Hashtbl.create(16); - let current_located_module_cache = () => { switch (Hashtbl.find_opt(located_module_cache, current_filename^())) { | Some(v) => v @@ -126,169 +191,235 @@ let current_located_module_cache = () => { new_table; }; }; -let current_resolution_table = () => { - switch (Hashtbl.find_opt(resolutions, current_filename^())) { - | Some(v) => v - | None => - let new_table = PathTbl.create(12); - Hashtbl.add(resolutions, current_filename^(), new_table); - new_table; +let to_absolute = path => + switch (Fp.testForPath(path)) { + | Some(Absolute(p)) => p + | Some(Relative(rel)) => Fp.join(Filepath.get_cwd(), rel) + | None => failwith("to_absolute: invalid path: " ++ path) }; + +let base_dir_of_current_file = () => { + Fp.dirName(to_absolute(current_filename^())); }; -let log_resolution = (unit_name, dir, basename) => { - let resolution = - Filepath.( - to_string @@ String.derelativize @@ String.concat(dir, basename) +let from_source = srcpath => + if (file_exists(Fp.toString(srcpath))) { + switch (classify_source(srcpath)) { + | Some((origin, root)) => object_file_for_source(~origin, ~root, srcpath) + | None => error(Source_outside_roots(srcpath)) + }; + } else { + raise(Not_found); + }; + +let resolve_library = unit_name => + switch (Filepath.String.first_segment(unit_name)) { + | Some((first, rest)) when List.mem_assoc(first, Config.libraries^) => + let lib_root = List.assoc(first, Config.libraries^); + Option.map( + srcpath => + object_file_for_source( + ~origin=Library(first), + ~root=lib_root, + srcpath, + ), + find_source_under_root(lib_root, rest), ); - PathTbl.add(current_resolution_table(), (dir, unit_name), resolution); - resolution; -}; + | _ => None + }; -let resolve_unit = (~search_path=?, ~cache=true, ~base_dir=?, unit_name) => { - let base_dir = - switch (base_dir) { - | None => Filepath.String.dirname(current_filename^()) - | Some(bd) => bd +let resolve_object = unit_name => + Option.map( + objpath => ObjectFile({compiled_object: objpath}), + find_object_in_path(object_search_path(), unit_name), + ); + +let resolve_stdlib = unit_name => + switch (Config.stdlib_dir^) { + | Some(stdlib_root) => + Option.map( + srcpath => + object_file_for_source(~origin=Stdlib, ~root=stdlib_root, srcpath), + find_source_under_root(stdlib_root, unit_name), + ) + | None => None + }; + +let resolve_module_uncached = (base_dir, unit_name) => + if (!Filename.is_relative(unit_name)) { + // Absolute path + switch (Fp.absoluteCurrentPlatform(unit_name)) { + | Some(p) => from_source(p) + | None => raise(Not_found) }; - let path = - switch (search_path) { - | None => Config.module_search_path() - | Some(p) => p + } else if (!Filename.is_implicit(unit_name)) { + // Relative local path, i.e. "./foo.gr" or "../foo.gr" + switch (Fp.relative(unit_name)) { + | None => raise(Not_found) + | Some(rel) => + let candidate = Fp.join(base_dir, rel); + if (file_exists(Fp.toString(candidate))) { + from_source(candidate); + } else { + raise(Not_found); + }; }; - switch ( - cache, - PathTbl.find_opt(current_resolution_table(), base_dir, path, unit_name), - ) { - | (true, Some(res)) => res - | _ => - let (_, dir, basename) = - find_in_path_uncap( - ~check_src=true, - ~check_object=true, - base_dir, - path, - unit_name, - ); - if (cache) { - log_resolution(unit_name, dir, basename); - } else { - Filepath.( - to_string @@ String.derelativize @@ String.concat(dir, basename) - ); + } else { + // Library path, i.e. "foo/bar" + switch (resolve_stdlib(unit_name)) { + | Some(result) => result + | None => + switch (resolve_library(unit_name)) { + | Some(result) => result + | None => raise(Not_found) + } }; }; -}; -let locate_module = (~disable_relpath=false, base_dir, path, unit_name) => { +let locate_module = + (base_dir: Fp.t(Fp.absolute), unit_name): module_location_result => { switch ( - PathTbl.find_opt( - ~disable_relpath, - current_located_module_cache(), - base_dir, - path, - unit_name, - ) + PathTbl.find_opt(current_located_module_cache(), base_dir, unit_name) ) { | Some(m) => m | None => - let (dir, m) = - switch ( - find_in_path_uncap(~check_object=true, base_dir, path, unit_name) - ) { - | (objpath, dir, basename) => - ignore(log_resolution(unit_name, dir, basename)); - let file = find_ext_in_dir(dir, basename, ["gr"]); - switch (file) { - | Some((srcpath, _, _)) => ( - dir, - GrainModule(srcpath, Some(objpath)), - ) - | None => (dir, ObjectFile(objpath)) - }; - | exception Not_found => - let (srcpath, dir, _) = - find_in_path_uncap(~check_src=true, base_dir, path, unit_name); - (dir, GrainModule(srcpath, None)); - }; - PathTbl.add(current_located_module_cache(), (dir, unit_name), m); - m; + let result = resolve_module_uncached(base_dir, unit_name); + PathTbl.add(current_located_module_cache(), base_dir, unit_name, result); + result; }; }; -let try_locate_module = - (~disable_relpath=false, base_dir, active_search_path, name, loc) => { - let locate = locate_module(~disable_relpath, base_dir, active_search_path); - Filepath.String.( - try(locate(name)) { - | Not_found => - if (check_suffix(name, ".gr")) { - let no_extension = chop_suffix(name, ".gr"); - switch (locate(no_extension)) { - | exception Not_found => error(No_module_file(loc, name, None)) - | _ => - let name = !is_relpath(name) ? no_extension : name; - // The filepath might have come in as `.gr.gr` so we need to chop again - let module_name = chop_suffix(no_extension, ".gr"); - error( - No_module_file( - loc, - name, - Some("did you mean \"" ++ module_name ++ "\"?"), - ), - ); +let is_explicit_relpath = name => + Filename.is_relative(name) && !Filename.is_implicit(name); + +let locate_object = (base_dir, unit_name) => + try(locate_module(base_dir, unit_name)) { + | Not_found => + let from_relpath = + if (is_explicit_relpath(unit_name) + && Filename.check_suffix(unit_name, ".gr")) { + switch (Fp.relative(unit_name)) { + | Some(rel) => + let obj_path = + Filepath.replace_extension(Fp.join(base_dir, rel), "gro"); + if (file_exists(Fp.toString(obj_path))) { + Some(ObjectFile({compiled_object: obj_path})); + } else { + None; + }; + | None => None }; } else { - switch (locate(name ++ ".gr")) { - | exception Not_found => error(No_module_file(loc, name, None)) - | _ => - error( - No_module_file( - loc, - name, - Some("did you mean \"" ++ name ++ ".gr\"?"), - ), - ) - }; + None; + }; + switch (from_relpath) { + | Some(result) => result + | None => + switch (resolve_object(unit_name)) { + | Some(result) => result + | None => raise(Not_found) } + }; + }; + +let try_locate_module = (base_dir: Fp.t(Fp.absolute), name: string, loc) => + try(locate_object(base_dir, name)) { + | Not_found => + if (Filename.check_suffix(name, ".gr")) { + let no_extension = Filename.chop_suffix(name, ".gr"); + switch (locate_module(base_dir, no_extension)) { + | exception Not_found => error(No_module_file(loc, name, None)) + | _ => + let reported_name = is_explicit_relpath(name) ? name : no_extension; + let module_name = + if (Filename.check_suffix(no_extension, ".gr")) { + Filename.chop_suffix(no_extension, ".gr"); + } else { + no_extension; + }; + error( + No_module_file( + loc, + reported_name, + Some("did you mean \"" ++ module_name ++ "\"?"), + ), + ); + }; + } else { + switch (locate_module(base_dir, name ++ ".gr")) { + | exception Not_found => error(No_module_file(loc, name, None)) + | _ => + error( + No_module_file( + loc, + name, + Some("did you mean \"" ++ name ++ ".gr\"?"), + ), + ) + }; } - ); -}; + }; type dependency_node = { - // dn_unit_name is a hashtable because we may have a situation - // where A depends on B and C, and both B and C depend on D. - // D will then have two unit names, corresponding to the "view" from B and C. - dn_unit_name: Hashtbl.t(option(dependency_node), string), // <- node_where_imported: name_of_unit_where_imported + dn_unit_name: Hashtbl.t(option(dependency_node), string), dn_file_name: string, - dn_up_to_date: ref(bool), // cached up_to_date check + dn_up_to_date: ref(bool), dn_latest_resolution: ref(option(module_location_result)), }; -let located_to_object_file_name = (~base=?, located) => { - let ret = - switch (located) { - | GrainModule(srcpath, None) => get_object_name(srcpath) - | GrainModule(_, Some(outpath)) - | ObjectFile(outpath) => outpath +let located_to_object_file_path = located => + switch (located) { + | GrainModule({source: srcpath, compiled_object: _, origin, root}) => + source_to_artifact_path(~ext="gro", ~origin, ~root, srcpath) + | ObjectFile({compiled_object: outpath}) => outpath + }; + +let located_to_object_file_name = located => + Fp.toString(located_to_object_file_path(located)); + +let locate_unit_object_file = (~base_dir=?, unit_name) => { + let base_dir = + switch (base_dir) { + | None => base_dir_of_current_file() + | Some(bd) => to_absolute(bd) }; - Filepath.to_string(Filepath.String.derelativize(~base?, ret)); + located_to_object_file_name(locate_object(base_dir, unit_name)); }; -let locate_unit_object_file = (~path=?, ~base_dir=?, unit_name) => { +let resolve_unit = (~cache as _=true, ~base_dir=?, unit_name) => { let base_dir = switch (base_dir) { - | None => Filepath.String.dirname(current_filename^()) - | Some(bd) => bd - }; - let path = - switch (path) { - | Some(p) => p - | None => Config.module_search_path() + | None => base_dir_of_current_file() + | Some(bd) => to_absolute(bd) }; - located_to_object_file_name(locate_module(base_dir, path, unit_name)); + Fp.toString( + located_to_object_file_path(locate_object(base_dir, unit_name)), + ); +}; + +let source_artifact_filename = (~ext, name) => { + let abs = to_absolute(name); + switch (classify_source(abs)) { + | Some((origin, root)) => + Fp.toString(source_to_artifact_path(~ext, ~origin, ~root, abs)) + | None => error(Source_outside_roots(abs)) + }; }; +let source_output_filename = (~ext, name) => { + let abs = to_absolute(name); + switch (classify_source(abs)) { + | Some(_) => + let basename = + Filepath.String.replace_extension(Option.get(Fp.baseName(abs)), ext); + let base = Fp.At.(Config.target_dir^ / profile_dir_name()); + Fp.toString(Fp.append(base, basename)); + | None => error(Source_outside_roots(abs)) + }; +}; + +let get_object_name = name => source_artifact_filename(~ext="gro", name); + module Dependency_graph = Dependency_graph.Make({ type t = dependency_node; @@ -303,88 +434,94 @@ module Dependency_graph = | None => failwith("impossible: get_srcname > No resolution") | Some(ObjectFile(_)) => failwith("impossible: get_srcname > No source") - | Some(GrainModule(srcpath, _)) => - Filepath.to_string(Filepath.String.derelativize(srcpath)) + | Some(GrainModule({source: srcpath, _})) => Fp.toString(srcpath) }; }; let get_filename = dn => dn.dn_file_name; let rec get_dependencies: (t, string => option(t)) => list(t) = (dn, lookup) => { - let base_dir = Filepath.String.dirname(dn.dn_file_name); - let active_search_path = Config.module_search_path(); let located = dn.dn_latest_resolution^; - let from_srcpath = srcpath => { + let make_dep_node = (~locate, unit_name, loc) => { + let located = locate(unit_name, loc); + let out_file_name = located_to_object_file_name(located); + let existing_dependency = lookup(out_file_name); + switch (existing_dependency) { + | Some(ed) => + Hashtbl.add(ed.dn_unit_name, Some(dn), unit_name); + ed; + | None => + let tbl = Hashtbl.create(8); + Hashtbl.add(tbl, Some(dn), unit_name); + { + dn_unit_name: tbl, + dn_file_name: out_file_name, + dn_up_to_date: ref(false), + dn_latest_resolution: ref(Some(located)), + }; + }; + }; + let from_srcpath = (~base_dir, srcpath_str) => List.map( - name => { - let located = - try_locate_module( - base_dir, - active_search_path, - name.Location.txt, - name.Location.loc, - ); - let out_file_name = located_to_object_file_name(located); - let existing_dependency = lookup(out_file_name); - switch (existing_dependency) { - | Some(ed) => - Hashtbl.add(ed.dn_unit_name, Some(dn), name.Location.txt); - ed; - | None => - let tbl = Hashtbl.create(8); - Hashtbl.add(tbl, Some(dn), name.Location.txt); - { - dn_unit_name: tbl, - dn_file_name: out_file_name, - dn_up_to_date: ref(false), // <- needs to be checked - dn_latest_resolution: ref(Some(located)), - }; - }; - }, - Grain_parsing.Driver.scan_for_imports(srcpath), + name => + make_dep_node( + ~locate=try_locate_module(base_dir), + name.Location.txt, + name.Location.loc, + ), + Grain_parsing.Driver.scan_for_imports(srcpath_str), ); - }; - - // For the moment, from the dependency graph's perspective, we assume that - // nothing uses --no-pervasives or --no-gc. - switch (located) { - | None => failwith("get_dependencies: Should be impossible") - | Some(ObjectFile(_)) => [] - | Some(GrainModule(srcpath, None)) => from_srcpath(srcpath) - | Some(GrainModule(srcpath, Some(objpath))) => - switch (read_file_cmi(objpath)) { - | exception (Cmi_format.Error(_)) => from_srcpath(srcpath) + let from_cmi = (~locate, objpath_str) => + switch (read_file_cmi(objpath_str)) { + | exception (Cmi_format.Error(_)) => None | cmi => - List.map( - ((name, _)) => { - let located = - try_locate_module( - base_dir, - active_search_path, + Some( + List.map( + ((name, _)) => + make_dep_node( + ~locate, name, Location.in_file(dn.dn_file_name), - ); - let out_file_name = located_to_object_file_name(located); - let existing_dependency = lookup(out_file_name); - switch (existing_dependency) { - | Some(ed) => - Hashtbl.add(ed.dn_unit_name, Some(dn), name); - ed; - | None => - let tbl = Hashtbl.create(8); - Hashtbl.add(tbl, Some(dn), name); - { - dn_unit_name: tbl, - dn_file_name: out_file_name, - dn_up_to_date: ref(false), // <- needs to be checked - dn_latest_resolution: ref(Some(located)), - }; - }; - }, - cmi.cmi_crcs, + ), + cmi.cmi_crcs, + ), + ) + }; + + switch (located) { + | None => failwith("get_dependencies: Should be impossible") + | Some(ObjectFile({compiled_object: objpath})) => + let base_dir = Fp.dirName(objpath); + switch ( + from_cmi( + ~locate=try_locate_module(base_dir), + Fp.toString(objpath), ) - } + ) { + | Some(deps) => deps + | None => [] + }; + | Some(GrainModule({source: srcpath, compiled_object: None, _})) => + let base_dir = Fp.dirName(srcpath); + from_srcpath(~base_dir, Fp.toString(srcpath)); + | Some( + GrainModule({ + source: srcpath, + compiled_object: Some(objpath), + _, + }), + ) => + let base_dir = Fp.dirName(srcpath); + switch ( + from_cmi( + ~locate=try_locate_module(base_dir), + Fp.toString(objpath), + ) + ) { + | Some(deps) => deps + | None => from_srcpath(~base_dir, Fp.toString(srcpath)) + }; }; }; @@ -393,35 +530,44 @@ module Dependency_graph = switch (dn.dn_up_to_date^, dn.dn_latest_resolution^) { | (true, _) => () | (false, None) - | (false, Some(GrainModule(_, None))) => - // File isn't compiled, so it's not up-to-date yet. + | (false, Some(GrainModule({compiled_object: None, _}))) => dn.dn_up_to_date := false - | (false, Some(ObjectFile(_))) => - // WASM modules are always up-to-date + | (false, Some(ObjectFile({compiled_object: _}))) => dn.dn_up_to_date := true - | (false, Some(GrainModule(srcpath, Some(objpath)))) => - // Compiled file is up-to-date if the srcpath is older than the objpath, - // all dependencies have expected CRC, and the module was compiled with - // the current compiler configuration. Otherwise, we need to recompile. + | ( + false, + Some( + GrainModule({ + source: srcpath, + compiled_object: Some(objpath), + _, + }), + ), + ) => let config_sum = Cmi_format.config_sum(); - let base_dir = Filepath.String.dirname(srcpath); + let base_dir = Fp.dirName(srcpath); + let srcpath = Fp.toString(srcpath); + let objpath = Fp.toString(objpath); let up_to_date = switch (read_file_cmi(objpath)) { - // Treat corrupted CMI as invalid | exception (Cmi_format.Error(_)) => false | cmi => config_sum == cmi.cmi_config_sum && file_older(srcpath, objpath) && List.for_all( ((name, crc)) => { - let resolved = resolve_unit(~base_dir, name); - let object_name = get_object_name(resolved); - Fs_access.file_exists(object_name) - && ( - try(read_file_cmi(object_name).cmi_crc == crc) { - | _ => false - } - ); + switch (locate_module(base_dir, name)) { + | exception _ => false + | located => + let object_name = + Fp.toString(located_to_object_file_path(located)); + file_exists(object_name) + && ( + try(read_file_cmi(object_name).cmi_crc == crc) { + | _ => false + } + ); + } }, cmi.cmi_crcs, ) @@ -435,18 +581,15 @@ module Dependency_graph = }; }); -let locate_object_file = (~loc, ~disable_relpath=false, unit_name) => { - let base_dir = Filepath.String.dirname(current_filename^()); - let path = Config.module_search_path(); - let located = - try_locate_module(~disable_relpath, base_dir, path, unit_name, loc); +let locate_object_file = (~loc, unit_name) => { + let base_dir = base_dir_of_current_file(); + let located = try_locate_module(base_dir, unit_name, loc); located_to_object_file_name(located); }; let process_dependency = (~loc, ~base_file, unit_name) => { - let base_dir = Filepath.String.dirname(base_file); - let path = Config.module_search_path(); - let located = try_locate_module(base_dir, path, unit_name, loc); + let base_dir = Fp.dirName(to_absolute(base_file)); + let located = try_locate_module(base_dir, unit_name, loc); let object_file = located_to_object_file_name(located); let current_dep_node = Dependency_graph.lookup_filename(base_file); let existing_dependency = Dependency_graph.lookup_filename(object_file); @@ -461,7 +604,7 @@ let process_dependency = (~loc, ~base_file, unit_name) => { { dn_unit_name: tbl, dn_file_name: object_file, - dn_up_to_date: ref(false), // <- needs to be checked + dn_up_to_date: ref(false), dn_latest_resolution: ref(Some(located)), }; }; @@ -511,16 +654,10 @@ let () = { Hashtbl.remove(located_module_cache), () => Hashtbl.clear(located_module_cache), )); - Fs_access.register_cache_flusher(( - Hashtbl.remove(resolutions), - () => Hashtbl.clear(resolutions), - )); }; let dump_dependency_graph = Dependency_graph.dump; -/* Error report */ - open Format; let report_error = ppf => @@ -528,7 +665,14 @@ let report_error = ppf => | No_module_file(_, m, None) => fprintf(ppf, "Missing file for module \"%s\"", m) | No_module_file(_, m, Some(msg)) => - fprintf(ppf, "Missing file for module \"%s\": %s", m, msg); + fprintf(ppf, "Missing file for module \"%s\": %s", m, msg) + | Source_outside_roots(p) => { + fprintf( + ppf, + "Source file %s is not within the project or any configured library.", + Fp.toString(p), + ); + }; let () = Location.register_error_of_exn( diff --git a/compiler/src/typed/module_resolution.rei b/compiler/src/typed/module_resolution.rei index 048c514338..4dd0dcbb15 100644 --- a/compiler/src/typed/module_resolution.rei +++ b/compiler/src/typed/module_resolution.rei @@ -1,19 +1,14 @@ let get_object_name: string => string; -let locate_object_file: - (~loc: Grain_parsing.Location.t, ~disable_relpath: bool=?, string) => string; - -let locate_unit_object_file: - (~path: list(string)=?, ~base_dir: string=?, string) => string; - -let resolve_unit: - ( - ~search_path: list(string)=?, - ~cache: bool=?, - ~base_dir: string=?, - string - ) => - string; +let source_artifact_filename: (~ext: string, string) => string; + +let source_output_filename: (~ext: string, string) => string; + +let locate_object_file: (~loc: Grain_parsing.Location.t, string) => string; + +let locate_unit_object_file: (~base_dir: string=?, string) => string; + +let resolve_unit: (~cache: bool=?, ~base_dir: string=?, string) => string; let load_dependency_graph: string => unit; let load_dependency_graph_from_string: (string, string) => unit; diff --git a/compiler/src/typed/typemod.re b/compiler/src/typed/typemod.re index c5d50d85b4..63ae7af593 100644 --- a/compiler/src/typed/typemod.re +++ b/compiler/src/typed/typemod.re @@ -1108,16 +1108,24 @@ open Printtyp; let report_error = ppf => fun - | Include_module_name_mismatch(path, provided_name, actual_name) => - fprintf( - ppf, - "This statement includes module %s, but the file at the path defines module %s. Did you mean `from \"%s\" include %s as %s`?", - provided_name, - actual_name, - path, - actual_name, - provided_name, - ) + | Include_module_name_mismatch(path, provided_name, actual_name) => { + let path = + if (!Grain_utils.Filepath.String.is_relpath(path) + && Grain_utils.Filepath.String.check_suffix(path, ".gr")) { + Grain_utils.Filepath.String.chop_suffix(path, ".gr"); + } else { + path; + }; + fprintf( + ppf, + "This statement includes module %s, but the file at the path defines module %s. Did you mean `from \"%s\" include %s as %s`?", + provided_name, + actual_name, + path, + actual_name, + provided_name, + ); + } | Signature_expected => fprintf(ppf, "This module type is not a signature") | Structure_expected(mty) => fprintf( diff --git a/compiler/src/utils/config.re b/compiler/src/utils/config.re index 03629f3c8a..b03b1b203d 100644 --- a/compiler/src/utils/config.re +++ b/compiler/src/utils/config.re @@ -328,15 +328,16 @@ let option_conv = ((prsr, prntr)) => ( ); type profile = + | Debug | Release; let profile = opt( ~doc="Set a compilation profile.", ~names=["profile"], - ~conv=option_conv(Cmdliner.Arg.enum([("release", Release)])), + ~conv=Cmdliner.Arg.enum([("debug", Debug), ("release", Release)]), ~digestible=Digestible, - None, + Debug, ); let default_memory_base = 0x400; @@ -353,8 +354,9 @@ let memory_base = let include_dirs = opt( ~names=["I", "include-dirs"], - ~conv=Cmdliner.Arg.(list(dir)), - ~doc="Extra library include directories", + ~conv= + Cmdliner.Arg.(list(Filepath.Args.ExistingDirectory.cmdliner_converter)), + ~doc="Directories of precompiled object files", ~docv="DIR", ~digestible=NotDigestible, [], @@ -363,13 +365,43 @@ let include_dirs = let stdlib_dir = opt( ~names=["stdlib"], - ~conv=option_conv(Cmdliner.Arg.string), + ~conv=option_conv(Filepath.Args.ExistingDirectory.cmdliner_converter), ~doc="Path to the standard library (stdlib) directory", ~env="GRAIN_STDLIB", ~digestible=NotDigestible, None, ); +let project_root = + opt( + ~names=["project-root"], + ~conv=Filepath.Args.ExistingDirectory.cmdliner_converter, + ~doc="The root of the project.", + ~docv="DIR", + ~digestible=NotDigestible, + Filepath.get_cwd(), + ); + +let target_dir = + opt( + ~names=["target-dir"], + ~conv=Filepath.Args.MaybeExistingDirectory.cmdliner_converter, + ~doc="Directory where build artifacts are written.", + ~docv="DIR", + ~digestible=NotDigestible, + Fp.At.(Filepath.get_cwd() / "target"), + ); + +let libraries = + opt( + ~names=["L", "library"], + ~conv=Cmdliner.Arg.(list(Filepath.Args.NameEqualsDir.cmdliner_converter)), + ~doc="Include libraries: `-L name=path`.", + ~docv="NAME=DIR", + ~digestible=NotDigestible, + [], + ); + let color_enabled = toggle_flag( ~names=["no-color"], @@ -492,7 +524,7 @@ let bulk_memory = let wasi_polyfill = opt( ~names=["wasi-polyfill"], - ~conv=option_conv(Cmdliner.Arg.string), + ~conv=option_conv(Filepath.Args.ExistingFile.cmdliner_converter), ~doc="Custom WASI implementation", ~digestible=NotDigestible, None, @@ -541,25 +573,6 @@ let with_cli_options = (term: 'a): Cmdliner.Term.t('a) => { folded; }; -let stdlib_directory = (): option(string) => - Option.map( - path => Filepath.(to_string(String.derelativize(path))), - stdlib_dir^, - ); - -let wasi_polyfill_path = (): option(string) => - Option.map( - path => Filepath.(to_string(String.derelativize(path))), - wasi_polyfill^, - ); - -let module_search_path = () => { - switch (stdlib_directory()) { - | Some(x) => include_dirs^ @ [x] /* stdlib goes last */ - | None => include_dirs^ - }; -}; - let apply_attribute_flags = (~no_pervasives as np, ~runtime_mode as rm, ~no_exception_mod as ne) => { // Only apply options if attributes were explicitly given so as to not diff --git a/compiler/src/utils/config.rei b/compiler/src/utils/config.rei index ea6aa4c6ca..633d8c2be4 100644 --- a/compiler/src/utils/config.rei +++ b/compiler/src/utils/config.rei @@ -1,4 +1,5 @@ type profile = + | Debug | Release; [@deriving sexp] @@ -9,16 +10,6 @@ type compilation_mode = /** The version of the Grain compiler */ let version: string; -/** The Grain stdlib directory, based on the current configuration */ -let stdlib_directory: unit => option(string); - -/** The WASI polyfill path, based on the current configuration */ -let wasi_polyfill_path: unit => option(string); - -/** The list of directories to search for modules in, based on the current configuration */ - -let module_search_path: unit => list(string); - /** Whether verbose output should be written */ let verbose: ref(bool); @@ -38,7 +29,7 @@ let bulk_memory: ref(bool); /** Custom WASI implementation */ -let wasi_polyfill: ref(option(string)); +let wasi_polyfill: ref(option(Fp.t(Fp.absolute))); /** Whether to replace the _start export with a start section during linking */ @@ -46,7 +37,7 @@ let use_start_section: ref(bool); /** Compilation profile, e.g. release for production builds */ -let profile: ref(option(profile)); +let profile: ref(profile); // [NOTE] This default is here because it is used in multiple locations, // and it doesn't make sense for it to be "owned" by any of them. @@ -58,13 +49,25 @@ let default_memory_base: int; let memory_base: ref(option(int)); -/** The path to find modules on */ +/** Directories of precompiled object files. */ + +let include_dirs: ref(list(Fp.t(Fp.absolute))); + +/** The location of the standard library. */ + +let stdlib_dir: ref(option(Fp.t(Fp.absolute))); + +/** The root of the project. */ + +let project_root: ref(Fp.t(Fp.absolute)); + +/** Directory where build artifacts are written. */ -let include_dirs: ref(list(string)); +let target_dir: ref(Fp.t(Fp.absolute)); -/** The location of the pervasives module. */ +/** Included libraries. */ -let stdlib_dir: ref(option(string)); +let libraries: ref(list((string, Fp.t(Fp.absolute)))); /** Whether color output should be enabled */ diff --git a/compiler/src/utils/filepath.re b/compiler/src/utils/filepath.re index 051eec4247..9162246a80 100644 --- a/compiler/src/utils/filepath.re +++ b/compiler/src/utils/filepath.re @@ -26,6 +26,29 @@ let derelativize = (~base=?, fname: Fp.firstClass) => { }; }; +/** + Replaces the extension of the basename of [path] with [new_ext]. If the + basename has no extension, [new_ext] is appended. Preserves the + relative/absoluteness of [path]. +*/ +let replace_extension = (path, new_ext) => { + switch (Fp.baseName(path)) { + | Some(base) => + let stem = + switch (String.rindex_opt(base, '.')) { + | Some(i) => String.sub(base, 0, i) + | None => base + }; + Fp.append(Fp.dirName(path), stem ++ "." ++ new_ext); + | None => path + }; +}; + +/** + Returns true if [path] is a descendent of [root] (or equal to [root]). +*/ +let is_under = (~root, path) => Fp.isDescendent(~ofPath=root, path); + // All uses of `Filename` from OCaml should be constrained to this file because we need to do // normalization on the filepaths those functions produce. module String = { @@ -101,6 +124,16 @@ module String = { // TODO(#216): We should consider switching to type safe Fp.t where ever filepaths are used let chop_suffix = Filename.chop_suffix; + let first_segment = path => + switch (String.index_opt(path, '/')) { + | None + | Some(0) => None + | Some(i) => + let first = String.sub(path, 0, i); + let rest = String.sub(path, i + 1, String.length(path) - i - 1); + Some((first, rest)); + }; + // TODO(#216): We should consider switching to type safe Fp.t where ever filepaths are used let extension = Filename.extension; @@ -319,4 +352,95 @@ module Args = { let cmdliner_converter = (prsr, prntr); }; + + module ExistingDirectory = { + type t = Fp.t(Fp.absolute); + + let prsr = fname => { + switch (ExistingFileOrDirectory.query(fname)) { + | Ok(Directory(path)) => `Ok(path) + | Ok(File(path)) => + `Error( + Format.sprintf("%s is a file, not a directory", to_string(path)), + ) + | Error(NotExists(path)) => + `Error(Format.sprintf("%s does not exist", to_string(path))) + | Error(InvalidFileType(path)) => + `Error(Format.sprintf("%s is not a directory", to_string(path))) + | Error(InvalidPath(fname)) => + `Error(Format.sprintf("Invalid path: %s", fname)) + }; + }; + + let prntr = (formatter, value) => { + Format.fprintf(formatter, "Directory: %s", to_string(value)); + }; + + let cmdliner_converter = (prsr, prntr); + }; + + module MaybeExistingDirectory = { + type t = Fp.t(Fp.absolute); + + let prsr = fname => { + switch (ExistingFileOrDirectory.query(fname)) { + | Ok(Directory(path)) => `Ok(path) + | Ok(File(path)) => + `Error( + Format.sprintf("%s is a file, not a directory", to_string(path)), + ) + | Error(NotExists(path)) => `Ok(path) + | Error(InvalidFileType(path)) => + `Error(Format.sprintf("%s is not a directory", to_string(path))) + | Error(InvalidPath(fname)) => + `Error(Format.sprintf("Invalid path: %s", fname)) + }; + }; + + let prntr = (formatter, value) => { + Format.fprintf(formatter, "Directory: %s", to_string(value)); + }; + + let cmdliner_converter = (prsr, prntr); + }; + + module NameEqualsDir = { + type t = (string, Fp.t(Fp.absolute)); + + let valid_name = name => + Stdlib.String.length(name) > 0 + && !Stdlib.String.contains(name, '/') + && !Stdlib.String.contains(name, '\\') + && name != "." + && name != ".."; + + let prsr = arg => { + switch (Stdlib.String.index_opt(arg, '=')) { + | None => `Error(Format.sprintf("Expected NAME=DIR, got `%s'", arg)) + | Some(i) => + let name = Stdlib.String.sub(arg, 0, i); + let path = + Stdlib.String.sub(arg, i + 1, Stdlib.String.length(arg) - i - 1); + if (!valid_name(name)) { + `Error( + Format.sprintf( + "Invalid library name `%s' (must be a single segment with no slashes)", + name, + ), + ); + } else { + switch (ExistingDirectory.prsr(path)) { + | `Ok(dir) => `Ok((name, dir)) + | `Error(msg) => `Error(msg) + }; + }; + }; + }; + + let prntr = (formatter, (name, path)) => { + Format.fprintf(formatter, "%s=%s", name, to_string(path)); + }; + + let cmdliner_converter = (prsr, prntr); + }; }; diff --git a/compiler/test/TestFramework.re b/compiler/test/TestFramework.re index 4665d522d0..accd3d77e7 100644 --- a/compiler/test/TestFramework.re +++ b/compiler/test/TestFramework.re @@ -29,29 +29,15 @@ let test_output_dir = Fp.At.(test_dir / "output"); let test_stdlib_dir = Fp.At.(test_dir / "stdlib"); let test_runtime_dir = Fp.At.(test_dir / "runtime"); let test_snapshots_dir = Fp.At.(test_dir / "__snapshots__"); +let test_target_dir = Fp.At.(test_dir / "target"); let test_grainfmt_dir = Fp.At.(test_dir / "grainfmt"); let test_graindoc_dir = Fp.At.(test_dir / "graindoc"); -let clean_grain_output = stdlib_dir => - Array.iter( - file => { - let filename = Filepath.to_string(file); - if (Filepath.String.check_suffix(filename, ".wasm") - || Filepath.String.check_suffix(filename, ".wat") - || Filepath.String.check_suffix(filename, ".gro") - || Filepath.String.check_suffix(filename, ".mashtree") - || Filepath.String.check_suffix(filename, ".modsig")) { - Fs.rmExn(file); - }; - }, - Fs_access.readdir(stdlib_dir), - ); - -let clean_output = output => - if (Sys.file_exists(Filepath.to_string(output))) { - Array.iter(Fs.rmExn, Fs_access.readdir(output)); +let clean_dir = dir => + if (Sys.file_exists(Filepath.to_string(dir))) { + Array.iter(Fs.rmExn, Fs_access.readdir(dir)); }; let () = { @@ -62,14 +48,14 @@ let () = { if (github_actions) { Pastel.setMode(Pastel.Terminal); }; - /*** Override default stdlib location to use development version of stdlib */ let stdlib_dir = Unix.getenv("GRAIN_STDLIB"); let stdlib_dir = Filepath.String.derelativize(stdlib_dir); - Config.stdlib_dir := Some(Filepath.to_string(stdlib_dir)); - clean_grain_output(test_input_dir); - clean_grain_output(stdlib_dir); - clean_grain_output(test_libs_dir); - clean_output(test_output_dir); + Config.stdlib_dir := Some(stdlib_dir); + Config.target_dir := test_target_dir; + Config.project_root := test_dir; + Config.libraries := [("test-libs", test_libs_dir)]; + clean_dir(test_target_dir); + clean_dir(test_output_dir); Config.debug := true; Config.wat := true; Config.color_enabled := false; diff --git a/compiler/test/__snapshots__/basic_functionality.1bf5759c.0.snapshot b/compiler/test/__snapshots__/basic_functionality.1bf5759c.0.snapshot index 009dcb3db7..67cbdf0322 100644 --- a/compiler/test/__snapshots__/basic_functionality.1bf5759c.0.snapshot +++ b/compiler/test/__snapshots__/basic_functionality.1bf5759c.0.snapshot @@ -2,51 +2,51 @@ basic functionality › unsafe_wasm_globals ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1083) (name _F64_VAL))) - (mimp_mod unsafeWasmGlobalsExports.gr) (mimp_name _F64_VAL) + (((mimp_id ((stamp 1159) (name _F64_VAL))) + (mimp_mod test-libs/unsafeWasmGlobalsExports.gr) (mimp_name _F64_VAL) (mimp_type (MGlobalImport (WasmValue WasmF64) true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) - ((mimp_id ((stamp 1082) (name printF64))) + ((mimp_id ((stamp 1158) (name printF64))) (mimp_mod runtime/debugPrint.gr) (mimp_name printF64) (mimp_type (MFuncImport (GrainValue (WasmValue WasmF64)) (GrainValue))) (mimp_kind MImportGrain) (mimp_setup MSetupNone) (mimp_used true)) - ((mimp_id ((stamp 1082) (name printF64))) + ((mimp_id ((stamp 1158) (name printF64))) (mimp_mod runtime/debugPrint.gr) (mimp_name printF64) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) - ((mimp_id ((stamp 1081) (name _F32_VAL))) - (mimp_mod unsafeWasmGlobalsExports.gr) (mimp_name _F32_VAL) + ((mimp_id ((stamp 1157) (name _F32_VAL))) + (mimp_mod test-libs/unsafeWasmGlobalsExports.gr) (mimp_name _F32_VAL) (mimp_type (MGlobalImport (WasmValue WasmF32) true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) - ((mimp_id ((stamp 1080) (name printF32))) + ((mimp_id ((stamp 1156) (name printF32))) (mimp_mod runtime/debugPrint.gr) (mimp_name printF32) (mimp_type (MFuncImport (GrainValue (WasmValue WasmF32)) (GrainValue))) (mimp_kind MImportGrain) (mimp_setup MSetupNone) (mimp_used true)) - ((mimp_id ((stamp 1080) (name printF32))) + ((mimp_id ((stamp 1156) (name printF32))) (mimp_mod runtime/debugPrint.gr) (mimp_name printF32) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) - ((mimp_id ((stamp 1079) (name _I64_VAL))) - (mimp_mod unsafeWasmGlobalsExports.gr) (mimp_name _I64_VAL) + ((mimp_id ((stamp 1155) (name _I64_VAL))) + (mimp_mod test-libs/unsafeWasmGlobalsExports.gr) (mimp_name _I64_VAL) (mimp_type (MGlobalImport (WasmValue WasmI64) true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) - ((mimp_id ((stamp 1078) (name printI64))) + ((mimp_id ((stamp 1154) (name printI64))) (mimp_mod runtime/debugPrint.gr) (mimp_name printI64) (mimp_type (MFuncImport (GrainValue (WasmValue WasmI64)) (GrainValue))) (mimp_kind MImportGrain) (mimp_setup MSetupNone) (mimp_used true)) - ((mimp_id ((stamp 1078) (name printI64))) + ((mimp_id ((stamp 1154) (name printI64))) (mimp_mod runtime/debugPrint.gr) (mimp_name printI64) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) - ((mimp_id ((stamp 1077) (name _I32_VAL))) - (mimp_mod unsafeWasmGlobalsExports.gr) (mimp_name _I32_VAL) + ((mimp_id ((stamp 1153) (name _I32_VAL))) + (mimp_mod test-libs/unsafeWasmGlobalsExports.gr) (mimp_name _I32_VAL) (mimp_type (MGlobalImport (WasmValue WasmI32) true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) - ((mimp_id ((stamp 1076) (name printI32))) + ((mimp_id ((stamp 1152) (name printI32))) (mimp_mod runtime/debugPrint.gr) (mimp_name printI32) (mimp_type (MFuncImport (GrainValue (WasmValue WasmI32)) (GrainValue))) (mimp_kind MImportGrain) (mimp_setup MSetupNone) (mimp_used true)) - ((mimp_id ((stamp 1076) (name printI32))) + ((mimp_id ((stamp 1152) (name printI32))) (mimp_mod runtime/debugPrint.gr) (mimp_name printI32) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) @@ -55,56 +55,56 @@ basic functionality › unsafe_wasm_globals (((instr_desc (MDrop ((instr_desc - (MCallKnown (func printI32_1076) + (MCallKnown (func printI32_1152) (closure ((immediate_desc - (MImmBinding (MGlobalBind printI32_1076 GrainValue))) + (MImmBinding (MGlobalBind printI32_1152 GrainValue))) (immediate_analyses ((last_usage Unknown))))) (func_type (((WasmValue WasmI32)) (GrainValue))) (args (((immediate_desc - (MImmBinding (MGlobalBind _I32_VAL_1077 (WasmValue WasmI32)))) + (MImmBinding (MGlobalBind _I32_VAL_1153 (WasmValue WasmI32)))) (immediate_analyses ((last_usage Unknown)))))))))))) ((instr_desc (MDrop ((instr_desc - (MCallKnown (func printI64_1078) + (MCallKnown (func printI64_1154) (closure ((immediate_desc - (MImmBinding (MGlobalBind printI64_1078 GrainValue))) + (MImmBinding (MGlobalBind printI64_1154 GrainValue))) (immediate_analyses ((last_usage Unknown))))) (func_type (((WasmValue WasmI64)) (GrainValue))) (args (((immediate_desc - (MImmBinding (MGlobalBind _I64_VAL_1079 (WasmValue WasmI64)))) + (MImmBinding (MGlobalBind _I64_VAL_1155 (WasmValue WasmI64)))) (immediate_analyses ((last_usage Unknown)))))))))))) ((instr_desc (MDrop ((instr_desc - (MCallKnown (func printF32_1080) + (MCallKnown (func printF32_1156) (closure ((immediate_desc - (MImmBinding (MGlobalBind printF32_1080 GrainValue))) + (MImmBinding (MGlobalBind printF32_1156 GrainValue))) (immediate_analyses ((last_usage Unknown))))) (func_type (((WasmValue WasmF32)) (GrainValue))) (args (((immediate_desc - (MImmBinding (MGlobalBind _F32_VAL_1081 (WasmValue WasmF32)))) + (MImmBinding (MGlobalBind _F32_VAL_1157 (WasmValue WasmF32)))) (immediate_analyses ((last_usage Unknown)))))))))))) ((instr_desc (MStore (((MLocalBind 0 GrainValue) ((instr_desc - (MCallKnown (func printF64_1082) + (MCallKnown (func printF64_1158) (closure ((immediate_desc - (MImmBinding (MGlobalBind printF64_1082 GrainValue))) + (MImmBinding (MGlobalBind printF64_1158 GrainValue))) (immediate_analyses ((last_usage Unknown))))) (func_type (((WasmValue WasmF64)) (GrainValue))) (args (((immediate_desc (MImmBinding - (MGlobalBind _F64_VAL_1083 (WasmValue WasmF64)))) + (MGlobalBind _F64_VAL_1159 (WasmValue WasmF64)))) (immediate_analyses ((last_usage Unknown)))))))))))))) ((instr_desc (MImmediate diff --git a/compiler/test/__snapshots__/basic_functionality.2bcc447b.0.snapshot b/compiler/test/__snapshots__/basic_functionality.2bcc447b.0.snapshot index 02dd1707d0..1692a8bcad 100644 --- a/compiler/test/__snapshots__/basic_functionality.2bcc447b.0.snapshot +++ b/compiler/test/__snapshots__/basic_functionality.2bcc447b.0.snapshot @@ -65,7 +65,7 @@ basic functionality › assert2 ((instr_desc (MAllocate (MString - \"AssertionError: Assertion failed in assert2, line 1\"))))))))) + \"AssertionError: Assertion failed in test/input/assert2.gr, line 1\"))))))))) ((instr_desc (MStore (((MLocalBind 3 GrainValue) diff --git a/compiler/test/__snapshots__/includes.46f78654.0.snapshot b/compiler/test/__snapshots__/includes.46f78654.0.snapshot index fd4d2d3320..dc358bfeb2 100644 --- a/compiler/test/__snapshots__/includes.46f78654.0.snapshot +++ b/compiler/test/__snapshots__/includes.46f78654.0.snapshot @@ -2,15 +2,15 @@ includes › include_some_multiple ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1123) (name x))) (mimp_mod provideAll.gr) + (((mimp_id ((stamp 1123) (name x))) (mimp_mod test-libs/provideAll.gr) (mimp_name x) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) - ((mimp_id ((stamp 1122) (name y))) (mimp_mod provideAll.gr) + ((mimp_id ((stamp 1122) (name y))) (mimp_mod test-libs/provideAll.gr) (mimp_name y) (mimp_type (MFuncImport (GrainValue (WasmValue WasmRef)) ((WasmValue WasmRef)))) (mimp_kind MImportGrain) (mimp_setup MSetupNone) (mimp_used true)) - ((mimp_id ((stamp 1122) (name y))) (mimp_mod provideAll.gr) + ((mimp_id ((stamp 1122) (name y))) (mimp_mod test-libs/provideAll.gr) (mimp_name y) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) (exports ()) diff --git a/compiler/test/__snapshots__/includes.5dfba7dd.0.snapshot b/compiler/test/__snapshots__/includes.5dfba7dd.0.snapshot index 873f4c9221..eba9928484 100644 --- a/compiler/test/__snapshots__/includes.5dfba7dd.0.snapshot +++ b/compiler/test/__snapshots__/includes.5dfba7dd.0.snapshot @@ -2,7 +2,7 @@ includes › include_alias ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1122) (name x))) (mimp_mod provideAll.gr) + (((mimp_id ((stamp 1122) (name x))) (mimp_mod test-libs/provideAll.gr) (mimp_name x) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) (exports ()) diff --git a/compiler/test/__snapshots__/includes.6c8d23dc.0.snapshot b/compiler/test/__snapshots__/includes.6c8d23dc.0.snapshot index 5037105cfd..f4d6806601 100644 --- a/compiler/test/__snapshots__/includes.6c8d23dc.0.snapshot +++ b/compiler/test/__snapshots__/includes.6c8d23dc.0.snapshot @@ -2,15 +2,15 @@ includes › include_some_multiple_trailing2 ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1123) (name x))) (mimp_mod provideAll.gr) + (((mimp_id ((stamp 1123) (name x))) (mimp_mod test-libs/provideAll.gr) (mimp_name x) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) - ((mimp_id ((stamp 1122) (name y))) (mimp_mod provideAll.gr) + ((mimp_id ((stamp 1122) (name y))) (mimp_mod test-libs/provideAll.gr) (mimp_name y) (mimp_type (MFuncImport (GrainValue (WasmValue WasmRef)) ((WasmValue WasmRef)))) (mimp_kind MImportGrain) (mimp_setup MSetupNone) (mimp_used true)) - ((mimp_id ((stamp 1122) (name y))) (mimp_mod provideAll.gr) + ((mimp_id ((stamp 1122) (name y))) (mimp_mod test-libs/provideAll.gr) (mimp_name y) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) (exports ()) diff --git a/compiler/test/__snapshots__/includes.6e78c003.0.snapshot b/compiler/test/__snapshots__/includes.6e78c003.0.snapshot index 0859a351a5..27e32c9bd1 100644 --- a/compiler/test/__snapshots__/includes.6e78c003.0.snapshot +++ b/compiler/test/__snapshots__/includes.6e78c003.0.snapshot @@ -2,15 +2,15 @@ includes › include_some_multiple_trailing ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1123) (name x))) (mimp_mod provideAll.gr) + (((mimp_id ((stamp 1123) (name x))) (mimp_mod test-libs/provideAll.gr) (mimp_name x) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) - ((mimp_id ((stamp 1122) (name y))) (mimp_mod provideAll.gr) + ((mimp_id ((stamp 1122) (name y))) (mimp_mod test-libs/provideAll.gr) (mimp_name y) (mimp_type (MFuncImport (GrainValue (WasmValue WasmRef)) ((WasmValue WasmRef)))) (mimp_kind MImportGrain) (mimp_setup MSetupNone) (mimp_used true)) - ((mimp_id ((stamp 1122) (name y))) (mimp_mod provideAll.gr) + ((mimp_id ((stamp 1122) (name y))) (mimp_mod test-libs/provideAll.gr) (mimp_name y) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) (exports ()) diff --git a/compiler/test/__snapshots__/includes.7afbe731.0.snapshot b/compiler/test/__snapshots__/includes.7afbe731.0.snapshot index 9de1bbef90..4751f7d9ae 100644 --- a/compiler/test/__snapshots__/includes.7afbe731.0.snapshot +++ b/compiler/test/__snapshots__/includes.7afbe731.0.snapshot @@ -2,7 +2,7 @@ includes › include_some ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1122) (name x))) (mimp_mod provideAll.gr) + (((mimp_id ((stamp 1122) (name x))) (mimp_mod test-libs/provideAll.gr) (mimp_name x) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) (exports ()) diff --git a/compiler/test/__snapshots__/includes.8222ee98.0.snapshot b/compiler/test/__snapshots__/includes.8222ee98.0.snapshot index 92fe021469..e44f98f02c 100644 --- a/compiler/test/__snapshots__/includes.8222ee98.0.snapshot +++ b/compiler/test/__snapshots__/includes.8222ee98.0.snapshot @@ -2,7 +2,7 @@ includes › include_module ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1122) (name x))) (mimp_mod provideAll.gr) + (((mimp_id ((stamp 1122) (name x))) (mimp_mod test-libs/provideAll.gr) (mimp_name x) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) (exports ()) diff --git a/compiler/test/__snapshots__/includes.86ff4075.0.snapshot b/compiler/test/__snapshots__/includes.86ff4075.0.snapshot index 9d4862ad99..b765df9135 100644 --- a/compiler/test/__snapshots__/includes.86ff4075.0.snapshot +++ b/compiler/test/__snapshots__/includes.86ff4075.0.snapshot @@ -2,15 +2,15 @@ includes › include_alias_multiple ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1123) (name x))) (mimp_mod provideAll.gr) + (((mimp_id ((stamp 1123) (name x))) (mimp_mod test-libs/provideAll.gr) (mimp_name x) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) - ((mimp_id ((stamp 1122) (name y))) (mimp_mod provideAll.gr) + ((mimp_id ((stamp 1122) (name y))) (mimp_mod test-libs/provideAll.gr) (mimp_name y) (mimp_type (MFuncImport (GrainValue (WasmValue WasmRef)) ((WasmValue WasmRef)))) (mimp_kind MImportGrain) (mimp_setup MSetupNone) (mimp_used true)) - ((mimp_id ((stamp 1122) (name y))) (mimp_mod provideAll.gr) + ((mimp_id ((stamp 1122) (name y))) (mimp_mod test-libs/provideAll.gr) (mimp_name y) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) (exports ()) diff --git a/compiler/test/__snapshots__/includes.a3212bd0.0.snapshot b/compiler/test/__snapshots__/includes.a3212bd0.0.snapshot index 877853265a..8592847249 100644 --- a/compiler/test/__snapshots__/includes.a3212bd0.0.snapshot +++ b/compiler/test/__snapshots__/includes.a3212bd0.0.snapshot @@ -2,14 +2,15 @@ includes › include_relative_path3 ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1040) (name j))) (mimp_mod nested/nested.gr) - (mimp_name j) (mimp_type (MGlobalImport GrainValue true)) - (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) + (((mimp_id ((stamp 1116) (name j))) + (mimp_mod ../test-libs/nested/nested.gr) (mimp_name j) + (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) + (mimp_setup MCallGetter) (mimp_used true)))) (exports ()) (main_body (((instr_desc (MImmediate - ((immediate_desc (MImmBinding (MGlobalBind j_1040 GrainValue))) + ((immediate_desc (MImmBinding (MGlobalBind j_1116 GrainValue))) (immediate_analyses ((last_usage Unknown))))))))) (main_body_stack_size ((stack_size_ref 0) (stack_size_i32 0) (stack_size_i64 0) diff --git a/compiler/test/__snapshots__/includes.bd3eb3af.0.snapshot b/compiler/test/__snapshots__/includes.bd3eb3af.0.snapshot index f9eff87fa7..4eb1449e4f 100644 --- a/compiler/test/__snapshots__/includes.bd3eb3af.0.snapshot +++ b/compiler/test/__snapshots__/includes.bd3eb3af.0.snapshot @@ -2,12 +2,12 @@ includes › include_some_mixed ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1126) (name sum))) (mimp_mod tlists.gr) + (((mimp_id ((stamp 1126) (name sum))) (mimp_mod test-libs/tlists.gr) (mimp_name sum) (mimp_type (MFuncImport (GrainValue (WasmValue WasmRef)) ((WasmValue WasmRef)))) (mimp_kind MImportGrain) (mimp_setup MSetupNone) (mimp_used true)) - ((mimp_id ((stamp 1126) (name sum))) (mimp_mod tlists.gr) + ((mimp_id ((stamp 1126) (name sum))) (mimp_mod test-libs/tlists.gr) (mimp_name sum) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) (exports ()) diff --git a/compiler/test/__snapshots__/includes.c0c0d5ca.0.snapshot b/compiler/test/__snapshots__/includes.c0c0d5ca.0.snapshot index 9bbabeb89b..d52e93fb8b 100644 --- a/compiler/test/__snapshots__/includes.c0c0d5ca.0.snapshot +++ b/compiler/test/__snapshots__/includes.c0c0d5ca.0.snapshot @@ -2,21 +2,21 @@ includes › include_relative_path4 ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1043) (name bar))) (mimp_mod ./bar/bar.gr) + (((mimp_id ((stamp 1119) (name bar))) (mimp_mod ./bar/bar.gr) (mimp_name bar) (mimp_type (MFuncImport (GrainValue (WasmValue WasmRef)) ((WasmValue WasmRef)))) (mimp_kind MImportGrain) (mimp_setup MSetupNone) (mimp_used true)) - ((mimp_id ((stamp 1043) (name bar))) (mimp_mod ./bar/bar.gr) + ((mimp_id ((stamp 1119) (name bar))) (mimp_mod ./bar/bar.gr) (mimp_name bar) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) - ((mimp_id ((stamp 1040) (name print))) (mimp_mod pervasives.gr) + ((mimp_id ((stamp 1116) (name print))) (mimp_mod pervasives.gr) (mimp_name print) (mimp_type (MFuncImport (GrainValue (WasmValue WasmRef) (WasmValue WasmRef)) (GrainValue))) (mimp_kind MImportGrain) (mimp_setup MSetupNone) (mimp_used true)) - ((mimp_id ((stamp 1040) (name print))) (mimp_mod pervasives.gr) + ((mimp_id ((stamp 1116) (name print))) (mimp_mod pervasives.gr) (mimp_name print) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) (exports ()) @@ -38,19 +38,19 @@ includes › include_relative_path4 (MStore (((MLocalBind 1 GrainValue) ((instr_desc - (MCallKnown (func bar_1043) + (MCallKnown (func bar_1119) (closure ((immediate_desc - (MImmBinding (MGlobalBind bar_1043 GrainValue))) + (MImmBinding (MGlobalBind bar_1119 GrainValue))) (immediate_analyses ((last_usage Unknown))))) (func_type ((GrainValue) (GrainValue))) (args (((immediate_desc (MImmConst (MConstSimpleNumber 2))) (immediate_analyses ((last_usage Unknown)))))))))))))) ((instr_desc - (MReturnCallKnown (func print_1040) + (MReturnCallKnown (func print_1116) (closure - ((immediate_desc (MImmBinding (MGlobalBind print_1040 GrainValue))) + ((immediate_desc (MImmBinding (MGlobalBind print_1116 GrainValue))) (immediate_analyses ((last_usage Unknown))))) (func_type ((GrainValue GrainValue) (GrainValue))) (args diff --git a/compiler/test/__snapshots__/includes.c62f45f8.0.snapshot b/compiler/test/__snapshots__/includes.c62f45f8.0.snapshot index 6c37d29447..188270cf1d 100644 --- a/compiler/test/__snapshots__/includes.c62f45f8.0.snapshot +++ b/compiler/test/__snapshots__/includes.c62f45f8.0.snapshot @@ -2,7 +2,7 @@ includes › include_muliple_modules ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1136) (name x))) (mimp_mod provideAll.gr) + (((mimp_id ((stamp 1136) (name x))) (mimp_mod test-libs/provideAll.gr) (mimp_name x) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) (exports ()) diff --git a/compiler/test/__snapshots__/includes.f2bf866b.0.snapshot b/compiler/test/__snapshots__/includes.f2bf866b.0.snapshot index 010405c96d..849452c459 100644 --- a/compiler/test/__snapshots__/includes.f2bf866b.0.snapshot +++ b/compiler/test/__snapshots__/includes.f2bf866b.0.snapshot @@ -10,7 +10,7 @@ includes › include_all_constructor (MADT ((immediate_desc (MImmConst (MConstSimpleNumber 72322404))) (immediate_analyses ((last_usage Unknown)))) - ((immediate_desc (MImmConst (MConstSimpleNumber 1045))) + ((immediate_desc (MImmConst (MConstSimpleNumber 1121))) (immediate_analyses ((last_usage Unknown)))) ((immediate_desc (MImmConst (MConstSimpleNumber 0))) (immediate_analyses ((last_usage Unknown)))) @@ -20,7 +20,7 @@ includes › include_all_constructor (MADT ((immediate_desc (MImmConst (MConstSimpleNumber 72322404))) (immediate_analyses ((last_usage Unknown)))) - ((immediate_desc (MImmConst (MConstSimpleNumber 1045))) + ((immediate_desc (MImmConst (MConstSimpleNumber 1121))) (immediate_analyses ((last_usage Unknown)))) ((immediate_desc (MImmConst (MConstSimpleNumber 1))) (immediate_analyses ((last_usage Unknown)))) diff --git a/compiler/test/__snapshots__/includes.f4ba5583.0.snapshot b/compiler/test/__snapshots__/includes.f4ba5583.0.snapshot index 0ea52eb29a..9f2f5e60e4 100644 --- a/compiler/test/__snapshots__/includes.f4ba5583.0.snapshot +++ b/compiler/test/__snapshots__/includes.f4ba5583.0.snapshot @@ -2,15 +2,15 @@ includes › include_module2 ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1123) (name x))) (mimp_mod provideAll.gr) + (((mimp_id ((stamp 1123) (name x))) (mimp_mod test-libs/provideAll.gr) (mimp_name x) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) - ((mimp_id ((stamp 1122) (name y))) (mimp_mod provideAll.gr) + ((mimp_id ((stamp 1122) (name y))) (mimp_mod test-libs/provideAll.gr) (mimp_name y) (mimp_type (MFuncImport (GrainValue (WasmValue WasmRef)) ((WasmValue WasmRef)))) (mimp_kind MImportGrain) (mimp_setup MSetupNone) (mimp_used true)) - ((mimp_id ((stamp 1122) (name y))) (mimp_mod provideAll.gr) + ((mimp_id ((stamp 1122) (name y))) (mimp_mod test-libs/provideAll.gr) (mimp_name y) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) (exports ()) diff --git a/compiler/test/__snapshots__/modules.b59b0085.0.snapshot b/compiler/test/__snapshots__/modules.b59b0085.0.snapshot index ae59ea542e..0205a79334 100644 --- a/compiler/test/__snapshots__/modules.b59b0085.0.snapshot +++ b/compiler/test/__snapshots__/modules.b59b0085.0.snapshot @@ -2,23 +2,25 @@ modules › reprovided_module ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1050) (name foo))) (mimp_mod simpleModule.gr) - (mimp_name Simple.foo) (mimp_type (MGlobalImport GrainValue true)) - (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) - ((mimp_id ((stamp 1049) (name func))) (mimp_mod simpleModule.gr) - (mimp_name Simple.func) + (((mimp_id ((stamp 1126) (name foo))) + (mimp_mod test-libs/simpleModule.gr) (mimp_name Simple.foo) + (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) + (mimp_setup MCallGetter) (mimp_used true)) + ((mimp_id ((stamp 1125) (name func))) + (mimp_mod test-libs/simpleModule.gr) (mimp_name Simple.func) (mimp_type (MFuncImport (GrainValue) ((WasmValue WasmRef)))) (mimp_kind MImportGrain) (mimp_setup MSetupNone) (mimp_used true)) - ((mimp_id ((stamp 1049) (name func))) (mimp_mod simpleModule.gr) - (mimp_name Simple.func) (mimp_type (MGlobalImport GrainValue true)) - (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) + ((mimp_id ((stamp 1125) (name func))) + (mimp_mod test-libs/simpleModule.gr) (mimp_name Simple.func) + (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) + (mimp_setup MCallGetter) (mimp_used true)))) (exports ((WasmFunctionExport (ex_function_name Simple.Simple.func) - (ex_function_internal_name func_1049)) + (ex_function_internal_name func_1125)) (WasmGlobalExport (ex_global_name Simple.Simple.func) - (ex_global_internal_name func_1049)) + (ex_global_internal_name func_1125)) (WasmGlobalExport (ex_global_name Simple.Simple.foo) - (ex_global_internal_name foo_1050)))) + (ex_global_internal_name foo_1126)))) (main_body (((instr_desc (MImmediate diff --git a/compiler/test/__snapshots__/provides.0ef7e7b3.0.snapshot b/compiler/test/__snapshots__/provides.0ef7e7b3.0.snapshot index 3935b7b782..9f127c73ad 100644 --- a/compiler/test/__snapshots__/provides.0ef7e7b3.0.snapshot +++ b/compiler/test/__snapshots__/provides.0ef7e7b3.0.snapshot @@ -2,14 +2,14 @@ provides › provide7 ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1046) (name x))) (mimp_mod provideAll.gr) + (((mimp_id ((stamp 1122) (name x))) (mimp_mod test-libs/provideAll.gr) (mimp_name x) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) (exports ()) (main_body (((instr_desc (MImmediate - ((immediate_desc (MImmBinding (MGlobalBind x_1046 GrainValue))) + ((immediate_desc (MImmBinding (MGlobalBind x_1122 GrainValue))) (immediate_analyses ((last_usage Unknown))))))))) (main_body_stack_size ((stack_size_ref 0) (stack_size_i32 0) (stack_size_i64 0) diff --git a/compiler/test/__snapshots__/provides.10f4f118.0.snapshot b/compiler/test/__snapshots__/provides.10f4f118.0.snapshot index 3ef8ab4f2e..7e684a976e 100644 --- a/compiler/test/__snapshots__/provides.10f4f118.0.snapshot +++ b/compiler/test/__snapshots__/provides.10f4f118.0.snapshot @@ -2,15 +2,15 @@ provides › provide9 ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1123) (name z))) (mimp_mod provideAll.gr) + (((mimp_id ((stamp 1123) (name z))) (mimp_mod test-libs/provideAll.gr) (mimp_name z) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) - ((mimp_id ((stamp 1122) (name y))) (mimp_mod provideAll.gr) + ((mimp_id ((stamp 1122) (name y))) (mimp_mod test-libs/provideAll.gr) (mimp_name y) (mimp_type (MFuncImport (GrainValue (WasmValue WasmRef)) ((WasmValue WasmRef)))) (mimp_kind MImportGrain) (mimp_setup MSetupNone) (mimp_used true)) - ((mimp_id ((stamp 1122) (name y))) (mimp_mod provideAll.gr) + ((mimp_id ((stamp 1122) (name y))) (mimp_mod test-libs/provideAll.gr) (mimp_name y) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) (exports ()) diff --git a/compiler/test/__snapshots__/provides.82c10ab4.0.snapshot b/compiler/test/__snapshots__/provides.82c10ab4.0.snapshot index bf93b573c1..7f9fcb8cd2 100644 --- a/compiler/test/__snapshots__/provides.82c10ab4.0.snapshot +++ b/compiler/test/__snapshots__/provides.82c10ab4.0.snapshot @@ -1,7 +1,7 @@ provides › provide12 ((mash_code ((functions - (((id ((stamp 1126) (name lam_lambda))) (name ()) + (((id ((stamp 1202) (name lam_lambda))) (name ()) (args (GrainValue GrainValue)) (return_type (GrainValue)) (closure (0)) (body (((instr_desc @@ -22,10 +22,10 @@ provides › provide12 (((MLocalBind 1 GrainValue) ((instr_desc (MAllocate (MString ok))))))))) ((instr_desc - (MReturnCallKnown (func print_1127) + (MReturnCallKnown (func print_1203) (closure ((immediate_desc - (MImmBinding (MGlobalBind print_1127 GrainValue))) + (MImmBinding (MGlobalBind print_1203 GrainValue))) (immediate_analyses ((last_usage Unknown))))) (func_type ((GrainValue GrainValue) (GrainValue))) (args @@ -38,22 +38,23 @@ provides › provide12 (stack_size_f32 0) (stack_size_f64 0))) (attrs ())))) (imports - (((mimp_id ((stamp 1127) (name print))) (mimp_mod pervasives.gr) + (((mimp_id ((stamp 1203) (name print))) (mimp_mod pervasives.gr) (mimp_name print) (mimp_type (MFuncImport (GrainValue (WasmValue WasmRef) (WasmValue WasmRef)) (GrainValue))) (mimp_kind MImportGrain) (mimp_setup MSetupNone) (mimp_used true)) - ((mimp_id ((stamp 1127) (name print))) (mimp_mod pervasives.gr) + ((mimp_id ((stamp 1203) (name print))) (mimp_mod pervasives.gr) (mimp_name print) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) - ((mimp_id ((stamp 1125) (name apply))) (mimp_mod providedType.gr) - (mimp_name apply) + ((mimp_id ((stamp 1201) (name apply))) + (mimp_mod test-libs/providedType.gr) (mimp_name apply) (mimp_type (MFuncImport (GrainValue (WasmValue WasmRef)) (GrainValue))) (mimp_kind MImportGrain) (mimp_setup MSetupNone) (mimp_used true)) - ((mimp_id ((stamp 1125) (name apply))) (mimp_mod providedType.gr) - (mimp_name apply) (mimp_type (MGlobalImport GrainValue true)) - (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) + ((mimp_id ((stamp 1201) (name apply))) + (mimp_mod test-libs/providedType.gr) (mimp_name apply) + (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) + (mimp_setup MCallGetter) (mimp_used true)))) (exports ()) (main_body (((instr_desc @@ -62,13 +63,13 @@ provides › provide12 ((instr_desc (MAllocate (MClosure - ((func_id (((stamp 1126) (name lam_lambda)))) + ((func_id (((stamp 1202) (name lam_lambda)))) (arg_types (GrainValue GrainValue)) (ret_types (GrainValue)) (variables ()))))))))))) ((instr_desc - (MReturnCallKnown (func apply_1125) + (MReturnCallKnown (func apply_1201) (closure - ((immediate_desc (MImmBinding (MGlobalBind apply_1125 GrainValue))) + ((immediate_desc (MImmBinding (MGlobalBind apply_1201 GrainValue))) (immediate_analyses ((last_usage Unknown))))) (func_type ((GrainValue) (GrainValue))) (args diff --git a/compiler/test/__snapshots__/provides.c3bb4eff.0.snapshot b/compiler/test/__snapshots__/provides.c3bb4eff.0.snapshot index a6fe42d978..dbf036e0bc 100644 --- a/compiler/test/__snapshots__/provides.c3bb4eff.0.snapshot +++ b/compiler/test/__snapshots__/provides.c3bb4eff.0.snapshot @@ -2,15 +2,15 @@ provides › provide8 ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1125) (name y))) (mimp_mod provideAll.gr) + (((mimp_id ((stamp 1125) (name y))) (mimp_mod test-libs/provideAll.gr) (mimp_name y) (mimp_type (MFuncImport (GrainValue (WasmValue WasmRef)) ((WasmValue WasmRef)))) (mimp_kind MImportGrain) (mimp_setup MSetupNone) (mimp_used true)) - ((mimp_id ((stamp 1125) (name y))) (mimp_mod provideAll.gr) + ((mimp_id ((stamp 1125) (name y))) (mimp_mod test-libs/provideAll.gr) (mimp_name y) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) - ((mimp_id ((stamp 1123) (name x))) (mimp_mod provideAll.gr) + ((mimp_id ((stamp 1123) (name x))) (mimp_mod test-libs/provideAll.gr) (mimp_name x) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)) ((mimp_id ((stamp 1122) (name +))) (mimp_mod pervasives.gr) diff --git a/compiler/test/__snapshots__/provides.f378d570.0.snapshot b/compiler/test/__snapshots__/provides.f378d570.0.snapshot index 78c0c0f179..a60be4d9a6 100644 --- a/compiler/test/__snapshots__/provides.f378d570.0.snapshot +++ b/compiler/test/__snapshots__/provides.f378d570.0.snapshot @@ -2,14 +2,14 @@ provides › provide4 ((mash_code ((functions ()) (imports - (((mimp_id ((stamp 1040) (name x))) (mimp_mod onlyXProvided.gr) + (((mimp_id ((stamp 1116) (name x))) (mimp_mod test-libs/onlyXProvided.gr) (mimp_name x) (mimp_type (MGlobalImport GrainValue true)) (mimp_kind MImportGrain) (mimp_setup MCallGetter) (mimp_used true)))) (exports ()) (main_body (((instr_desc (MImmediate - ((immediate_desc (MImmBinding (MGlobalBind x_1040 GrainValue))) + ((immediate_desc (MImmBinding (MGlobalBind x_1116 GrainValue))) (immediate_analyses ((last_usage Unknown))))))))) (main_body_stack_size ((stack_size_ref 0) (stack_size_i32 0) (stack_size_i64 0) diff --git a/compiler/test/input/relativeInclude3.gr b/compiler/test/input/relativeInclude3.gr index 1fffe788a9..858172d8a3 100644 --- a/compiler/test/input/relativeInclude3.gr +++ b/compiler/test/input/relativeInclude3.gr @@ -1,5 +1,5 @@ module RelativeImport3 -from "nested/nested" include Nested +from "../test-libs/nested/nested.gr" include Nested use Nested.* j diff --git a/compiler/test/input/unsafeWasmGlobals.gr b/compiler/test/input/unsafeWasmGlobals.gr index 1d3b06952b..f925874fd7 100644 --- a/compiler/test/input/unsafeWasmGlobals.gr +++ b/compiler/test/input/unsafeWasmGlobals.gr @@ -2,7 +2,7 @@ module UnsafeWasmGlobals from "runtime/debugPrint" include DebugPrint -from "unsafeWasmGlobalsExports" include UnsafeWasmGlobalsExports +from "test-libs/unsafeWasmGlobalsExports" include UnsafeWasmGlobalsExports use UnsafeWasmGlobalsExports.{ _I32_VAL, _I64_VAL, _F32_VAL, _F64_VAL } @unsafe diff --git a/compiler/test/runner.re b/compiler/test/runner.re index 1e8e8b47a5..192885b793 100644 --- a/compiler/test/runner.re +++ b/compiler/test/runner.re @@ -19,16 +19,27 @@ let customMatchers = createMatcher => { let grainfile = name => Filepath.to_string(Fp.At.(test_input_dir / (name ++ ".gr"))); +let grainfile_relative = name => + Filepath.String.concat( + "test", + Filepath.String.concat("input", name ++ ".gr"), + ); let stdlibfile = name => Filepath.to_string(Fp.At.(test_stdlib_dir / (name ++ ".gr"))); let runtimefile = name => Filepath.to_string(Fp.At.(test_runtime_dir / (name ++ ".gr"))); let objectfile = name => - Filepath.to_string(Fp.At.(test_input_dir / (name ++ ".gro"))); + Filepath.to_string( + Fp.At.(test_target_dir / "debug" / "build" / "input" / (name ++ ".gro")), + ); let wasmfile = name => Filepath.to_string(Fp.At.(test_output_dir / (name ++ ".wasm"))); let mashfile = name => - Filepath.to_string(Fp.At.(test_input_dir / (name ++ ".mashtree"))); + Filepath.to_string( + Fp.At.( + test_target_dir / "debug" / "build" / "input" / (name ++ ".mashtree") + ), + ); let grainfmt_out_file = name => Filepath.to_string(Fp.At.(test_grainfmt_dir / (name ++ ".expected.gr"))); @@ -43,9 +54,15 @@ let graindoc_in_file = name => Filepath.to_string(Fp.At.(test_graindoc_dir / (name ++ ".input.gr"))); let compile_dependency = filename => { - let outfile = default_object_filename(filename); let hook = stop_after_object_emitted; - ignore(compile_file(~hook, ~outfile, filename)); + Env.clear_persistent_structures(); + ignore(compile_file(~hook, filename)); +}; + +let test_setup = () => { + Config.target_dir := test_target_dir; + Config.project_root := test_dir; + Config.libraries := [("test-libs", test_libs_dir)]; }; let compile = @@ -62,6 +79,7 @@ let compile = Config.with_config( Config.empty, () => { + test_setup(); switch (config_fn) { | Some(fn) => fn() | None => () @@ -71,8 +89,6 @@ let compile = | None => () }; Config.maximum_memory_pages := max_pages; - Config.include_dirs := - [Filepath.to_string(test_libs_dir), ...Config.include_dirs^]; let outfile = wasmfile(name); Config.set_root_config(); @@ -80,23 +96,33 @@ let compile = switch (Config.wasi_polyfill^) { | Some(name) => + let name = Fp.toString(name); Config.preserve_config(() => { Config.compilation_mode := Grain_utils.Config.Runtime; Module_resolution.load_dependency_graph(name); let to_compile = Module_resolution.get_out_of_date_dependencies(); List.iter(compile_dependency, to_compile); compile_dependency(name); - }) + }); | None => () }; - Module_resolution.load_dependency_graph_from_string(name, prog); + Module_resolution.load_dependency_graph_from_string( + grainfile(name), + prog, + ); let to_compile = Module_resolution.get_out_of_date_dependencies(); List.iter(compile_dependency, to_compile); + Env.clear_persistent_structures(); let main_object = default_object_filename(grainfile(name)); let cstate = - compile_string(~hook?, ~name, ~outfile=main_object, prog); + compile_string( + ~hook?, + ~name=grainfile_relative(name), + ~object_outfile=main_object, + prog, + ); if (link) { let dependencies = Module_resolution.get_dependencies(); @@ -123,6 +149,7 @@ let compile_file = Config.with_config( Config.empty, () => { + test_setup(); switch (config_fn) { | Some(fn) => fn() | None => () @@ -132,30 +159,30 @@ let compile_file = | None => () }; Config.maximum_memory_pages := max_pages; - Config.include_dirs := - [Filepath.to_string(test_libs_dir), ...Config.include_dirs^]; Config.set_root_config(); reset_compiler_state(); switch (Config.wasi_polyfill^) { | Some(name) => + let name = Fp.toString(name); Config.preserve_config(() => { Config.compilation_mode := Grain_utils.Config.Runtime; Module_resolution.load_dependency_graph(name); let to_compile = Module_resolution.get_out_of_date_dependencies(); List.iter(compile_dependency, to_compile); compile_dependency(name); - }) + }); | None => () }; Module_resolution.load_dependency_graph(filename); let to_compile = Module_resolution.get_out_of_date_dependencies(); List.iter(compile_dependency, to_compile); + Env.clear_persistent_structures(); let main_object = default_object_filename(filename); - let cstate = compile_file(~hook?, ~outfile=main_object, filename); + let cstate = compile_file(~hook?, filename); if (link) { let dependencies = Module_resolution.get_dependencies(); @@ -243,7 +270,8 @@ let open_process = (~stdin_input=?, args) => { }; let run = (~extra_args=[||], file) => { - let stdlib = Option.get(Grain_utils.Config.stdlib_dir^); + let stdlib = + Filepath.to_string(Option.get(Grain_utils.Config.stdlib_dir^)); let preopen = Printf.sprintf( @@ -283,7 +311,17 @@ let format = file => { }; let doc = (file, arguments) => { - let cmd = Array.concat([[|"grain", "doc", file|], arguments]); + let cmd = + Array.concat([ + [| + "grain", + "doc", + "--target-dir", + Filepath.to_string(test_target_dir), + file, + |], + arguments, + ]); let (code, out, err) = open_process(cmd); @@ -291,7 +329,12 @@ let doc = (file, arguments) => { }; let lsp = stdin_input => { - let cmd = [|"grain", "lsp"|]; + let cmd = [| + "grain", + "lsp", + "--target-dir", + Filepath.to_string(test_target_dir), + |]; let (code, out, err) = open_process(~stdin_input, cmd); @@ -508,7 +551,7 @@ let makeStdlibRunner = (test, ~code=0, name) => { test(name, ({expect}) => { Config.preserve_all_configs(() => { // Run stdlib suites in release mode - Config.profile := Some(Release); + Config.profile := Release; let infile = stdlibfile(name); let outfile = wasmfile(name); ignore @@ compile_file(~link=true, infile, outfile); @@ -523,7 +566,7 @@ let makeRuntimeRunner = (test, ~code=0, name) => { test(name, ({expect}) => { Config.preserve_all_configs(() => { // Run stdlib suites in release mode - Config.profile := Some(Release); + Config.profile := Release; let infile = runtimefile(name); let outfile = wasmfile(name); ignore @@ compile_file(~link=true, infile, outfile); diff --git a/compiler/test/suites/basic_functionality.re b/compiler/test/suites/basic_functionality.re index 4675ea523c..2389d0465d 100644 --- a/compiler/test/suites/basic_functionality.re +++ b/compiler/test/suites/basic_functionality.re @@ -53,7 +53,7 @@ describe("basic functionality", ({test, testSkip}) => { }; let smallestFileConfig = () => { Grain_utils.Config.elide_type_info := true; - Grain_utils.Config.profile := Some(Grain_utils.Config.Release); + Grain_utils.Config.profile := Grain_utils.Config.Release; }; assertSnapshot("nil", ""); @@ -281,12 +281,12 @@ describe("basic functionality", ({test, testSkip}) => { assertRunError( "assert3", "assert false", - "AssertionError: Assertion failed in assert3, line 1", + "AssertionError: Assertion failed in test/input/assert3\\.gr, line 1", ); assertRunError( "assert4", "assert 4 - 1 == 14", - "AssertionError: Assertion failed in assert4, line 1", + "AssertionError: Assertion failed in test/input/assert4\\.gr, line 1", ); /* Failures */ assertRunError("fail1", "ignore(fail \"boo\")", "Failure: boo"); diff --git a/compiler/test/suites/includes.re b/compiler/test/suites/includes.re index be5050b20e..543c942d4e 100644 --- a/compiler/test/suites/includes.re +++ b/compiler/test/suites/includes.re @@ -15,99 +15,99 @@ describe("includes", ({test, testSkip}) => { /* use * tests */ assertRun( "include_all", - "from \"provideAll\" include ProvideAll; use ProvideAll.*; {print(x); print(y(4)); print(z)}", + "from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.*; {print(x); print(y(4)); print(z)}", "5\n4\nfoo\n", ); assertSnapshot( "include_all_constructor", - "from \"tlists\" include TLists; use TLists.*; Cons(2, Empty)", + "from \"test-libs/tlists\" include TLists; use TLists.*; Cons(2, Empty)", ); /* use {} tests */ assertSnapshot( "include_some", - "from \"provideAll\" include ProvideAll; use ProvideAll.{x}; x", + "from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.{x}; x", ); assertSnapshot( "include_some_multiple", - "from \"provideAll\" include ProvideAll; use ProvideAll.{x, y}; y(x)", + "from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.{x, y}; y(x)", ); assertSnapshot( "include_some_multiple_trailing", - "from \"provideAll\" include ProvideAll; use ProvideAll.{x, y,}; y(x)", + "from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.{x, y,}; y(x)", ); assertSnapshot( "include_some_multiple_trailing2", - "from \"provideAll\" include ProvideAll; use ProvideAll.{ + "from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.{ x, y, }; y(x)", ); assertSnapshot( "include_some_constructor", - "from \"tlists\" include TLists; use TLists.{type TList}; Cons(5, Empty)", + "from \"test-libs/tlists\" include TLists; use TLists.{type TList}; Cons(5, Empty)", ); assertSnapshot( "include_some_mixed", - "from \"tlists\" include TLists; use TLists.{type TList, sum}; sum(Cons(5, Empty))", + "from \"test-libs/tlists\" include TLists; use TLists.{type TList, sum}; sum(Cons(5, Empty))", ); assertSnapshot( "include_alias", - "from \"provideAll\" include ProvideAll; use ProvideAll.{x as y}; y", + "from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.{x as y}; y", ); assertSnapshot( "include_alias_multiple", - "from \"provideAll\" include ProvideAll; use ProvideAll.{x as y, y as x}; x(y)", + "from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.{x as y, y as x}; x(y)", ); /* use {} errors */ assertCompileError( "include_some_error", - "from \"provideAll\" include ProvideAll; use ProvideAll.{a}; a", + "from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.{a}; a", "Unbound value a in module ProvideAll", ); assertCompileError( "include_some_error2", - "from \"provideAll\" include ProvideAll; use ProvideAll.{x, a}; a", + "from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.{x, a}; a", "Unbound value a in module ProvideAll", ); assertCompileError( "include_some_error3", - "from \"provideAll\" include ProvideAll; use ProvideAll.{module Foo}", + "from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.{module Foo}", "Unbound module Foo in module ProvideAll", ); assertCompileError( "include_some_error4", - "from \"provideAll\" include ProvideAll; use ProvideAll.{x, module Foo}", + "from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.{x, module Foo}", "Unbound module Foo in module ProvideAll", ); assertCompileError( "include_some_error5", - "from \"provideAll\" include ProvideAll; use ProvideAll.{Foo}", + "from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.{Foo}", "Expected a lowercase identifier to use a value, the keyword `module` followed by an uppercase identifier to use a module, or the keyword `type` followed by an uppercase identifier to use a type.", ); assertCompileError( "include_some_error6", - "from \"provideAll\" include ProvideAll; use ProvideAll.{a, Foo}", + "from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.{a, Foo}", "Expected a lowercase identifier to use a value, the keyword `module` followed by an uppercase identifier to use a module, the keyword `type` followed by an uppercase identifier to use a type, or `}` to end the use statement.", ); /* include module tests */ assertSnapshot( "include_module", - "from \"provideAll\" include ProvideAll as Foo; Foo.x", + "from \"test-libs/provideAll\" include ProvideAll as Foo; Foo.x", ); assertSnapshot( "include_module2", - "from \"provideAll\" include ProvideAll as Foo; Foo.y(Foo.x)", + "from \"test-libs/provideAll\" include ProvideAll as Foo; Foo.y(Foo.x)", ); /* include module errors */ assertCompileError( "include_module_error", - "from \"provideAll\" include ProvideAll as Foo; Foo.foo", + "from \"test-libs/provideAll\" include ProvideAll as Foo; Foo.foo", "Unbound value foo in module Foo", ); assertCompileError( "include_module_error", - {|from "provideAll" include Foo; Foo.foo|}, - {|This statement includes module Foo, but the file at the path defines module ProvideAll. Did you mean `from "provideAll.gr" include ProvideAll as Foo`?|}, + {|from "test-libs/provideAll" include Foo; Foo.foo|}, + {|This statement includes module Foo, but the file at the path defines module ProvideAll. Did you mean `from "test-libs/provideAll" include ProvideAll as Foo`?|}, ); /* use well-formedness errors */ assertCompileError( @@ -138,12 +138,12 @@ describe("includes", ({test, testSkip}) => { /* include multiple modules tests */ assertSnapshot( "include_muliple_modules", - "from \"tlists\" include TLists; use TLists.*; from \"provideAll\" include ProvideAll; use ProvideAll.*; Cons(x, Empty)", + "from \"test-libs/tlists\" include TLists; use TLists.*; from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.*; Cons(x, Empty)", ); /* include same module tests */ assertSnapshot( "include_same_module_unify", - "from \"tlists\" include TLists; use TLists.*; Cons(5, TLists.Empty)", + "from \"test-libs/tlists\" include TLists; use TLists.*; Cons(5, TLists.Empty)", ); /* include filepath tests */ assertFileSnapshot("include_relative_path1", "relativeInclude1"); @@ -164,13 +164,13 @@ describe("includes", ({test, testSkip}) => { /* Misc include tests */ assertCompileError( "test_bad_import", - "{let x = (1, 2); from \"tlists\" include TLists; x}", + "{let x = (1, 2); from \"test-libs/tlists\" include TLists; x}", "error", ); assertFileRun("test_file_same_name", "list", "OK\n"); assertSnapshot( "annotation_across_import", - "from \"tlists\" include TLists; use TLists.{ type TList }; let foo : TLists.TList = Empty; foo", + "from \"test-libs/tlists\" include TLists; use TLists.{ type TList }; let foo : TLists.TList = Empty; foo", ); assertFileRun( "relative_include_linking", @@ -189,24 +189,51 @@ describe("includes", ({test, testSkip}) => { ); assertCompileError( "include_extension2", - "from \"brokenRelativeInclude\" include BrokenRelativeInclude", + "from \"test-libs/brokenRelativeInclude\" include BrokenRelativeInclude", "Missing file for module \"./data\": did you mean \"./data.gr\"?", ); assertRun( "reprovide_values", - "from \"reprovideContents\" include ReprovideContents; use ReprovideContents.{ type Type, module Mod }; print(A); print(Mod.val)", + "from \"test-libs/reprovideContents\" include ReprovideContents; use ReprovideContents.{ type Type, module Mod }; print(A); print(Mod.val)", "A\n123\n", ); assertRun( "reprovide_type2", - "from \"reprovideContents\" include ReprovideContents; use ReprovideContents.{ type OtherT as TT, val }; print(val); print({ x: 2 })", + "from \"test-libs/reprovideContents\" include ReprovideContents; use ReprovideContents.{ type OtherT as TT, val }; print(val); print({ x: 2 })", "{\n x: 1\n}\n{\n x: 2\n}\n", ); assertRun( "reprovide_type3", - "from \"reprovideContents\" include ReprovideContents; use ReprovideContents.{ type OtherT as Other }; print({ x: 1 }: Other)", + "from \"test-libs/reprovideContents\" include ReprovideContents; use ReprovideContents.{ type OtherT as Other }; print({ x: 1 }: Other)", "{\n x: 1\n}\n", ); + test_or_skip("only_include_dirs", ({expect}) => { + let prog = {| + module OnlyIncludeDirs + from "test-libs/reprovideException" include ReprovideException + void + |}; + ignore @@ compile("setup_artifacts", prog); + + let deps_dir = Fp.At.(test_target_dir / "debug" / "deps"); + ignore @@ + compile( + ~link=true, + ~config_fn= + () => { + Grain_utils.Config.libraries := []; + Grain_utils.Config.include_dirs := [deps_dir]; + assert( + List.assoc_opt("test-libs", Grain_utils.Config.libraries^) == None, + ); + }, + "only_include_dirs", + prog, + ); + let (result, _) = run(wasmfile("only_include_dirs")); + expect.string(result).toEqual(""); + }); + /* Duplicate imports */ test("dedupe_includes", ({expect}) => { let name = "dedupe_includes"; @@ -255,4 +282,28 @@ describe("includes", ({test, testSkip}) => { "test", )); }); + + test("stdlib_preceeds_library", ({expect}) => { + let library_dir = Fp.At.(test_libs_dir / "runtime"); + ignore @@ + compile( + ~config_fn= + () => { + Grain_utils.Config.libraries := + [("runtime", library_dir), ...Grain_utils.Config.libraries^] + }, + "stdlib_preceeds_library", + {| + module StdlibPreceedsLibrary + from "runtime/string" include String + void + |}, + ); + let obj_path = + Grain_typed.Module_resolution.locate_unit_object_file( + ~base_dir=Grain_utils.Filepath.to_string(Fp.At.(test_input_dir)), + "runtime/string", + ); + expect.string(obj_path).toMatch("deps/stdlib/"); + }); }); diff --git a/compiler/test/suites/let_mut.re b/compiler/test/suites/let_mut.re index ce92b0b95b..96948d9673 100644 --- a/compiler/test/suites/let_mut.re +++ b/compiler/test/suites/let_mut.re @@ -99,7 +99,7 @@ describe("let mut", ({test, testSkip}) => { /* Exported let mut */ assertRun( "let-mut_export1", - "from \"letMutProvide\" include LetMutProvide; use LetMutProvide.{ x }; print(x); x = 5; x = 6; print(x)", + "from \"test-libs/letMutProvide\" include LetMutProvide; use LetMutProvide.{ x }; print(x); x = 5; x = 6; print(x)", "3\n6\n", ); /* unsafe let mut in a loop */ diff --git a/compiler/test/suites/linking.re b/compiler/test/suites/linking.re index 83ca926d6d..a3544cdaff 100644 --- a/compiler/test/suites/linking.re +++ b/compiler/test/suites/linking.re @@ -1,5 +1,6 @@ open Grain_tests.TestFramework; open Grain_tests.Runner; +open Grain_utils; describe("linking", ({test, testSkip}) => { let test_or_skip = @@ -8,7 +9,11 @@ describe("linking", ({test, testSkip}) => { let assertRunError = makeErrorRunner(test_or_skip); let assertWasiPolyfillRun = file => makeRunner( - ~config_fn=() => {Grain_utils.Config.wasi_polyfill := Some(file)}, + ~config_fn= + () => { + let abs = Fp.join(Grain_utils.Filepath.get_cwd(), file); + Grain_utils.Config.wasi_polyfill := Some(abs); + }, test_or_skip, ); @@ -36,13 +41,13 @@ describe("linking", ({test, testSkip}) => { ); // --wasi-polyfill assertWasiPolyfillRun( - "test/input/wasiPolyfill.gr", + Fp.At.(Fp.dot / "test" / "input" / "wasiPolyfill.gr"), "wasi_polyfill", {|print("foo")|}, "foo\nfoo\nfoo\n", ); assertWasiPolyfillRun( - "test/input/wasiPolyfillNoop.gr", + Fp.At.(Fp.dot / "test" / "input" / "wasiPolyfillNoop.gr"), "wasi_polyfill_noop", {|print("foo")|}, "", diff --git a/compiler/test/suites/modules.re b/compiler/test/suites/modules.re index 17fc8a322a..1817f743c8 100644 --- a/compiler/test/suites/modules.re +++ b/compiler/test/suites/modules.re @@ -131,7 +131,7 @@ describe("modules", ({test, testSkip}) => { assertSnapshot( "reprovided_module", {| - from "simpleModule" include Simple + from "test-libs/simpleModule" include Simple provide { module Simple } |}, ); diff --git a/compiler/test/suites/provides.re b/compiler/test/suites/provides.re index 8cd9cc529c..b03bb70cc7 100644 --- a/compiler/test/suites/provides.re +++ b/compiler/test/suites/provides.re @@ -55,48 +55,48 @@ describe("provides", ({test, testSkip}) => { assertCompileError( "provide1", - "from \"noProvides\" include NoProvides; use NoProvides.*; x", + "from \"test-libs/noProvides\" include NoProvides; use NoProvides.*; x", "Unbound value x", ); assertCompileError( "provide2", - "from \"noProvides\" include NoProvides; use NoProvides.*; y", + "from \"test-libs/noProvides\" include NoProvides; use NoProvides.*; y", "Unbound value y", ); assertCompileError( "provide3", - "from \"noProvides\" include NoProvides; use NoProvides.*; z", + "from \"test-libs/noProvides\" include NoProvides; use NoProvides.*; z", "Unbound value z", ); assertSnapshot( "provide4", - "from \"onlyXProvided\" include OnlyXProvided; use OnlyXProvided.*; x", + "from \"test-libs/onlyXProvided\" include OnlyXProvided; use OnlyXProvided.*; x", ); assertCompileError( "provide5", - "from \"onlyXProvided\" include OnlyXProvided; use OnlyXProvided.*; y", + "from \"test-libs/onlyXProvided\" include OnlyXProvided; use OnlyXProvided.*; y", "Unbound value y", ); assertCompileError( "provide6", - "from \"onlyXProvided\" include OnlyXProvided; use OnlyXProvided.*; z", + "from \"test-libs/onlyXProvided\" include OnlyXProvided; use OnlyXProvided.*; z", "Unbound value z", ); assertSnapshot( "provide7", - "from \"provideAll\" include ProvideAll; use ProvideAll.*; x", + "from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.*; x", ); assertSnapshot( "provide8", - "from \"provideAll\" include ProvideAll; use ProvideAll.*; x + y(4)", + "from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.*; x + y(4)", ); assertSnapshot( "provide9", - "from \"provideAll\" include ProvideAll; use ProvideAll.*; y(z)", + "from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.*; y(z)", ); assertCompileError( "provide10", - "from \"provideAll\" include ProvideAll; use ProvideAll.*; y(secret)", + "from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.*; y(secret)", "Unbound value secret", ); assertCompileError( @@ -107,7 +107,7 @@ describe("provides", ({test, testSkip}) => { assertSnapshot( "provide12", {| - from "providedType" include ProvidedType + from "test-libs/providedType" include ProvidedType ProvidedType.apply((arg) => print("ok")) |}, ); @@ -172,23 +172,23 @@ describe("provides", ({test, testSkip}) => { ); assertRunError( "provide_exceptions1", - "from \"provideException\" include ProvideException; let f = () => if (true) { throw ProvideException.MyException }; f()", + "from \"test-libs/provideException\" include ProvideException; let f = () => if (true) { throw ProvideException.MyException }; f()", "OriginalException", ); assertRunError( "provide_exceptions2", - "from \"reprovideException\" include ReprovideException; use ReprovideException.*; let f = () => if (true) { throw MyException }; f()", + "from \"test-libs/reprovideException\" include ReprovideException; use ReprovideException.*; let f = () => if (true) { throw MyException }; f()", "OriginalException", ); assertRunError( "provide_exceptions3", - "from \"reprovideException\" include ReprovideException; use ReprovideException.{ exception MyException as E }; let f = () => if (true) { throw E }; f()", + "from \"test-libs/reprovideException\" include ReprovideException; use ReprovideException.{ exception MyException as E }; let f = () => if (true) { throw E }; f()", "OriginalException", ); assertRun( "provide_exceptions4", {| - from "reprovideException" include ReprovideException + from "test-libs/reprovideException" include ReprovideException use ReprovideException.{ exception MyException, excVal1, excVal2 } match (excVal1) { MyException => print("good1"), @@ -247,12 +247,12 @@ describe("provides", ({test, testSkip}) => { ); assertHasWasmExport( "provide_from_import", - "module Test; from \"provideAll\" include ProvideAll; use ProvideAll.*; provide { y }", + "module Test; from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.*; provide { y }", [("y", Wasm_utils.WasmFunction)], ); assertHasWasmExport( "provide_from_import_with_rebind", - "module Test; from \"provideAll\" include ProvideAll; use ProvideAll.*; provide let z = y", + "module Test; from \"test-libs/provideAll\" include ProvideAll; use ProvideAll.*; provide let z = y", [("z", Wasm_utils.WasmFunction)], ); assertFileRun( diff --git a/compiler/test/suites/records.re b/compiler/test/suites/records.re index c11947ddd7..868c14cb6f 100644 --- a/compiler/test/suites/records.re +++ b/compiler/test/suites/records.re @@ -183,7 +183,7 @@ describe("records", ({test, testSkip}) => { assertRun( "export_import_record_issue_665", {| - from "data" include Data + from "test-libs/data" include Data use Data.{ type Foo } provide enum Bar { Baz(Foo) } print(Baz({ bar: 1 })) diff --git a/compiler/test/suites/types.re b/compiler/test/suites/types.re index 0776ebfc68..9fab03676e 100644 --- a/compiler/test/suites/types.re +++ b/compiler/test/suites/types.re @@ -120,7 +120,7 @@ describe("aliased types", ({test, testSkip}) => { assertRun( "import_type_alias_1", {| - from "aliases" include Aliases + from "test-libs/aliases" include Aliases use Aliases.* let foo1 = 123 : Foo let foo2: Foo = 234 @@ -132,7 +132,7 @@ describe("aliased types", ({test, testSkip}) => { assertRun( "import_type_alias_2", {| - from "aliases" include Aliases + from "test-libs/aliases" include Aliases use Aliases.* let foo1 = [234] : (Bar) let foo2: Bar = [123, ...foo1] @@ -143,7 +143,7 @@ describe("aliased types", ({test, testSkip}) => { assertRun( "import_type_alias_3", {| - from "aliases" include Aliases + from "test-libs/aliases" include Aliases use Aliases.* let foo: Baz = baz print(foo: Baz) @@ -153,7 +153,7 @@ describe("aliased types", ({test, testSkip}) => { assertRun( "import_type_alias_4", {| - from "aliases" include Aliases + from "test-libs/aliases" include Aliases use Aliases.{ type Foo } let foo: Foo = 5 print(foo) @@ -163,7 +163,7 @@ describe("aliased types", ({test, testSkip}) => { assertRun( "import_type_alias_5", {| - from "aliases" include Aliases + from "test-libs/aliases" include Aliases use Aliases.* let foo: Qux = qux print(foo: Qux) @@ -173,7 +173,7 @@ describe("aliased types", ({test, testSkip}) => { assertCompileError( "err_import_type_alias_1", {| - from "aliases" include Aliases + from "test-libs/aliases" include Aliases use Aliases.* let bar = 5: Baz |}, @@ -183,7 +183,7 @@ describe("aliased types", ({test, testSkip}) => { assertCompileError( "err_import_type_alias_2", {| - from "aliases" include Aliases + from "test-libs/aliases" include Aliases use Aliases.* let bar: Qux = 5 |}, @@ -193,7 +193,7 @@ describe("aliased types", ({test, testSkip}) => { assertCompileError( "err_import_type_alias_3", {| - from "aliases" include Aliases + from "test-libs/aliases" include Aliases use Aliases.{ type Foo, baz } let foo: Foo = baz |}, @@ -380,7 +380,7 @@ describe("abstract types", ({test, testSkip}) => { assertRun( "regression_annotated_func_export", {| - from "funcAliasProvide" include FuncAliasProvide as A + from "test-libs/funcAliasProvide" include FuncAliasProvide as A print(A.function()) |}, "abc\n", diff --git a/compiler/test/test-libs/runtime/string.gr b/compiler/test/test-libs/runtime/string.gr new file mode 100644 index 0000000000..777bb3c0a3 --- /dev/null +++ b/compiler/test/test-libs/runtime/string.gr @@ -0,0 +1,3 @@ +module String + +provide let shadow = "library"