From a5cb759a8211df2f0d40e787e84138fc8247211f Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 4 Oct 2022 18:32:06 +0200 Subject: [PATCH 01/31] rename --- crates/cli/src/lib.rs | 2 +- crates/repl_expect/src/run.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index a72fe26d5b..5a6d5325bc 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -412,7 +412,7 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result { let mut writer = std::io::stdout(); - let (failed, passed) = roc_repl_expect::run::run_expects( + let (failed, passed) = roc_repl_expect::run::run_toplevel_expects( &mut writer, roc_reporting::report::RenderTarget::ColorTerminal, arena, diff --git a/crates/repl_expect/src/run.rs b/crates/repl_expect/src/run.rs index c60869d478..248c2560da 100644 --- a/crates/repl_expect/src/run.rs +++ b/crates/repl_expect/src/run.rs @@ -81,7 +81,7 @@ impl<'a> ExpectMemory<'a> { } #[allow(clippy::too_many_arguments)] -pub fn run_expects<'a, W: std::io::Write>( +pub fn run_toplevel_expects<'a, W: std::io::Write>( writer: &mut W, render_target: RenderTarget, arena: &'a Bump, From 67494e9df2bdfb7230f1328d0b58fc422f196b01 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 5 Oct 2022 14:48:57 +0200 Subject: [PATCH 02/31] thread layout interner to repl_expect --- Cargo.lock | 1 + crates/cli/Cargo.toml | 1 + crates/cli/src/build.rs | 8 +- crates/cli/src/lib.rs | 126 +++++++++++++++++++++++++----- crates/compiler/intern/src/lib.rs | 2 +- crates/repl_expect/src/run.rs | 10 ++- 6 files changed, 123 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d973816c6f..15800eb10a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3452,6 +3452,7 @@ dependencies = [ "roc_fmt", "roc_gen_llvm", "roc_glue", + "roc_intern", "roc_linker", "roc_load", "roc_module", diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 594a582d3a..d57b994a2f 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -60,6 +60,7 @@ roc_editor = { path = "../editor", optional = true } roc_linker = { path = "../linker" } roc_repl_cli = { path = "../repl_cli", optional = true } roc_tracing = { path = "../tracing" } +roc_intern = { path = "../compiler/intern" } clap = { version = "3.2.20", default-features = false, features = ["std", "color", "suggestions"] } const_format = { version = "0.2.23", features = ["const_generics"] } bumpalo = { version = "3.11.0", features = ["collections"] } diff --git a/crates/cli/src/build.rs b/crates/cli/src/build.rs index 475317ff1c..a06f862695 100644 --- a/crates/cli/src/build.rs +++ b/crates/cli/src/build.rs @@ -5,6 +5,7 @@ use roc_build::{ }; use roc_builtins::bitcode; use roc_collections::VecMap; +use roc_intern::SingleThreadedInterner; use roc_load::{ EntryPoint, ExecutionMode, Expectations, LoadConfig, LoadMonomorphizedError, LoadedModule, LoadingProblem, Threading, @@ -30,12 +31,13 @@ fn report_timing(buf: &mut String, label: &str, duration: Duration) { .unwrap() } -pub struct BuiltFile { +pub struct BuiltFile<'a> { pub binary_path: PathBuf, pub problems: Problems, pub total_time: Duration, pub expectations: VecMap, pub interns: Interns, + pub layout_interner: SingleThreadedInterner<'a, roc_mono::layout::Layout<'a>>, } pub enum BuildOrdering { @@ -69,7 +71,7 @@ pub fn build_file<'a>( threading: Threading, wasm_dev_stack_bytes: Option, order: BuildOrdering, -) -> Result> { +) -> Result, BuildFileError<'a>> { let compilation_start = Instant::now(); let target_info = TargetInfo::from(target); @@ -242,6 +244,7 @@ pub fn build_file<'a>( let mut loaded = loaded; let problems = program::report_problems_monomorphized(&mut loaded); let expectations = std::mem::take(&mut loaded.expectations); + let layout_interner = loaded.layout_interner.clone(); let loaded = loaded; let interns = loaded.interns.clone(); @@ -388,6 +391,7 @@ pub fn build_file<'a>( total_time, interns, expectations, + layout_interner, }) } diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 5a6d5325bc..0280867251 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -8,9 +8,11 @@ use roc_build::link::{LinkType, LinkingStrategy}; use roc_build::program::Problems; use roc_collections::VecMap; use roc_error_macros::{internal_error, user_error}; +use roc_intern::SingleThreadedInterner; use roc_load::{Expectations, LoadingProblem, Threading}; use roc_module::symbol::{Interns, ModuleId}; use roc_mono::ir::OptLevel; +use roc_mono::layout::Layout; use std::env; use std::ffi::{CString, OsStr}; use std::io; @@ -561,6 +563,7 @@ pub fn build( total_time, expectations, interns, + layout_interner, }) => { match config { BuildOnly => { @@ -573,7 +576,7 @@ pub fn build( // No need to waste time freeing this memory, // since the process is about to exit anyway. - std::mem::forget(arena); + // std::mem::forget(arena); print_problems(problems, total_time); println!(" while successfully building:\n\n {generated_filename}"); @@ -596,7 +599,16 @@ pub fn build( // ManuallyDrop will leak the bytes because we don't drop manually let bytes = &ManuallyDrop::new(std::fs::read(&binary_path).unwrap()); - roc_run(arena, opt_level, triple, args, bytes, expectations, interns) + roc_run( + &arena, + opt_level, + triple, + args, + bytes, + expectations, + interns, + layout_interner, + ) } BuildAndRunIfNoErrors => { debug_assert!( @@ -617,7 +629,16 @@ pub fn build( // ManuallyDrop will leak the bytes because we don't drop manually let bytes = &ManuallyDrop::new(std::fs::read(&binary_path).unwrap()); - roc_run(arena, opt_level, triple, args, bytes, expectations, interns) + roc_run( + &arena, + opt_level, + triple, + args, + bytes, + expectations, + interns, + layout_interner, + ) } } } @@ -683,13 +704,14 @@ fn print_problems(problems: Problems, total_time: std::time::Duration) { } fn roc_run<'a, I: IntoIterator>( - arena: Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it! + arena: &Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it! opt_level: OptLevel, triple: Triple, args: I, binary_bytes: &[u8], expectations: VecMap, interns: Interns, + layout_interner: SingleThreadedInterner, ) -> io::Result { match triple.architecture { Architecture::Wasm32 => { @@ -728,7 +750,15 @@ fn roc_run<'a, I: IntoIterator>( Ok(0) } - _ => roc_run_native(arena, opt_level, args, binary_bytes, expectations, interns), + _ => roc_run_native( + arena, + opt_level, + args, + binary_bytes, + expectations, + interns, + layout_interner, + ), } } @@ -777,12 +807,13 @@ fn make_argv_envp<'a, I: IntoIterator, S: AsRef>( /// Run on the native OS (not on wasm) #[cfg(target_family = "unix")] fn roc_run_native, S: AsRef>( - arena: Bump, + arena: &Bump, opt_level: OptLevel, args: I, binary_bytes: &[u8], expectations: VecMap, interns: Interns, + layout_interner: SingleThreadedInterner, ) -> std::io::Result { use bumpalo::collections::CollectIn; @@ -794,18 +825,24 @@ fn roc_run_native, S: AsRef>( .iter() .map(|s| s.as_ptr()) .chain([std::ptr::null()]) - .collect_in(&arena); + .collect_in(arena); let envp: bumpalo::collections::Vec<*const c_char> = envp_cstrings .iter() .map(|s| s.as_ptr()) .chain([std::ptr::null()]) - .collect_in(&arena); + .collect_in(arena); match opt_level { - OptLevel::Development => { - roc_run_native_debug(executable, &argv, &envp, expectations, interns) - } + OptLevel::Development => roc_run_native_debug( + arena, + executable, + argv, + envp, + expectations, + interns, + layout_interner, + ), OptLevel::Normal | OptLevel::Size | OptLevel::Optimize => { roc_run_native_fast(executable, &argv, &envp); } @@ -882,14 +919,67 @@ impl ExecutableFile { // with Expect #[cfg(target_family = "unix")] -unsafe fn roc_run_native_debug( - _executable: ExecutableFile, - _argv: &[*const c_char], - _envp: &[*const c_char], - _expectations: VecMap, - _interns: Interns, +fn roc_run_native_debug( + arena: &Bump, + executable: ExecutableFile, + argv: bumpalo::collections::Vec<*const c_char>, + envp: bumpalo::collections::Vec<*const c_char>, + mut expectations: VecMap, + interns: Interns, + layout_interner: SingleThreadedInterner, ) { - todo!() + use roc_repl_expect::run::ExpectMemory; + use signal_hook::{consts::signal::SIGCHLD, consts::signal::SIGUSR1, iterator::Signals}; + + let mut signals = Signals::new(&[SIGCHLD, SIGUSR1]).unwrap(); + + let shm_name = format!("/roc_expect_buffer_{}", std::process::id()); + let memory = ExpectMemory::create_or_reuse_mmap(&shm_name); + + let layout_interner = layout_interner.into_global(); + + let mut writer = std::io::stdout(); + + match unsafe { libc::fork() } { + 0 => unsafe { + // we are the child + + executable.execve(&argv, &envp); + }, + -1 => { + // something failed + + // Display a human-friendly error message + println!("Error {:?}", std::io::Error::last_os_error()); + + std::process::exit(1) + } + 1.. => { + for sig in &mut signals { + match sig { + SIGCHLD => { + // done! + return; + } + SIGUSR1 => { + // this is the signal we use for an expect failure. Let's see what the child told us + + roc_repl_expect::run::render_expects_in_memory( + &mut writer, + arena, + &mut expectations, + &interns, + &layout_interner, + &memory, + ) + .unwrap(); + } + _ => println!("received signal {}", sig), + } + } + } + _ => unreachable!(), + } } #[cfg(target_os = "linux")] diff --git a/crates/compiler/intern/src/lib.rs b/crates/compiler/intern/src/lib.rs index ef8c6b0f74..c1c1261ec5 100644 --- a/crates/compiler/intern/src/lib.rs +++ b/crates/compiler/intern/src/lib.rs @@ -60,7 +60,7 @@ pub struct ThreadLocalInterner<'a, K> { /// /// The only way to construct such an interner is to collapse a shared [GlobalInterner] into /// a [SingleThreadedInterner], via [GlobalInterner::unwrap]. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct SingleThreadedInterner<'a, K> { map: BumpMap<&'a K, Interned>, vec: Vec<&'a K>, diff --git a/crates/repl_expect/src/run.rs b/crates/repl_expect/src/run.rs index 248c2560da..b948a40e79 100644 --- a/crates/repl_expect/src/run.rs +++ b/crates/repl_expect/src/run.rs @@ -19,7 +19,7 @@ use roc_reporting::{error::expect::Renderer, report::RenderTarget}; use roc_target::TargetInfo; use target_lexicon::Triple; -pub(crate) struct ExpectMemory<'a> { +pub struct ExpectMemory<'a> { ptr: *mut u8, length: usize, shm_name: Option, @@ -39,7 +39,7 @@ impl<'a> ExpectMemory<'a> { } } - fn create_or_reuse_mmap(shm_name: &str) -> Self { + pub fn create_or_reuse_mmap(shm_name: &str) -> Self { let cstring = std::ffi::CString::new(shm_name).unwrap(); Self::mmap_help(cstring, libc::O_RDWR | libc::O_CREAT) } @@ -323,14 +323,16 @@ fn run_expect_fx<'a, W: std::io::Write>( } } -pub fn roc_dev_expect<'a>( +pub fn render_expects_in_memory<'a>( writer: &mut impl std::io::Write, arena: &'a Bump, expectations: &mut VecMap, interns: &'a Interns, layout_interner: &Arc>>, - shared_ptr: *mut u8, + memory: &ExpectMemory, ) -> std::io::Result { + let shared_ptr = memory.ptr; + let frame = ExpectFrame::at_offset(shared_ptr, ExpectSequence::START_OFFSET); let module_id = frame.module_id; From 8c4a2f58fc31f85a79ef03d7902130b7495924e5 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 5 Oct 2022 20:57:37 +0200 Subject: [PATCH 03/31] working expects in roc dev --- crates/cli/src/lib.rs | 25 +++++++++---- crates/compiler/build/src/program.rs | 2 +- .../compiler/builtins/bitcode/src/expect.zig | 30 ++++++++++++++++ crates/compiler/builtins/bitcode/src/main.zig | 4 +++ crates/compiler/builtins/build.rs | 8 ++--- crates/compiler/builtins/src/bitcode.rs | 1 + crates/compiler/gen_llvm/src/llvm/build.rs | 20 +++++++++-- crates/compiler/gen_llvm/src/llvm/expect.rs | 20 +++++++++++ crates/repl_expect/src/run.rs | 35 +++++++++++++++++-- examples/benchmarks/platform/host.zig | 5 +-- 10 files changed, 129 insertions(+), 21 deletions(-) diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 0280867251..4d482f3d3a 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -565,6 +565,7 @@ pub fn build( interns, layout_interner, }) => { + dbg!(expectations.len()); match config { BuildOnly => { // If possible, report the generated executable name relative to the current dir. @@ -792,12 +793,17 @@ fn make_argv_envp<'a, I: IntoIterator, S: AsRef>( // envp is an array of pointers to strings, conventionally of the // form key=value, which are passed as the environment of the new // program. The envp array must be terminated by a NULL pointer. + let mut buffer = Vec::with_capacity(100); let envp_cstrings: bumpalo::collections::Vec = std::env::vars_os() - .flat_map(|(k, v)| { - [ - CString::new(k.as_bytes()).unwrap(), - CString::new(v.as_bytes()).unwrap(), - ] + .map(|(k, v)| { + buffer.clear(); + + use std::io::Write; + buffer.write_all(k.as_bytes()).unwrap(); + buffer.write_all(b"=").unwrap(); + buffer.write_all(v.as_bytes()).unwrap(); + + CString::new(buffer.as_slice()).unwrap() }) .collect_in(arena); @@ -819,7 +825,7 @@ fn roc_run_native, S: AsRef>( unsafe { let executable = roc_run_executable_file_path(binary_bytes)?; - let (argv_cstrings, envp_cstrings) = make_argv_envp(&arena, &executable, args); + let (argv_cstrings, envp_cstrings) = make_argv_envp(arena, &executable, args); let argv: bumpalo::collections::Vec<*const c_char> = argv_cstrings .iter() @@ -833,6 +839,8 @@ fn roc_run_native, S: AsRef>( .chain([std::ptr::null()]) .collect_in(arena); + let opt_level = OptLevel::Development; + match opt_level { OptLevel::Development => roc_run_native_debug( arena, @@ -933,7 +941,8 @@ fn roc_run_native_debug( let mut signals = Signals::new(&[SIGCHLD, SIGUSR1]).unwrap(); - let shm_name = format!("/roc_expect_buffer_{}", std::process::id()); + // let shm_name = format!("/roc_expect_buffer_{}", std::process::id()); + let shm_name = "/roc_expect_buffer"; let memory = ExpectMemory::create_or_reuse_mmap(&shm_name); let layout_interner = layout_interner.into_global(); @@ -980,6 +989,8 @@ fn roc_run_native_debug( } _ => unreachable!(), } + + println!("done?"); } #[cfg(target_os = "linux")] diff --git a/crates/compiler/build/src/program.rs b/crates/compiler/build/src/program.rs index b731677ba0..050b3038b1 100644 --- a/crates/compiler/build/src/program.rs +++ b/crates/compiler/build/src/program.rs @@ -277,7 +277,7 @@ fn gen_from_mono_module_llvm( interns: loaded.interns, module, target_info, - mode: LlvmBackendMode::Binary, + mode: LlvmBackendMode::BinaryDev, exposed_to_host: loaded.exposed_to_host.values.keys().copied().collect(), }; diff --git a/crates/compiler/builtins/bitcode/src/expect.zig b/crates/compiler/builtins/bitcode/src/expect.zig index 64bc0ef748..9aab2081a2 100644 --- a/crates/compiler/builtins/bitcode/src/expect.zig +++ b/crates/compiler/builtins/bitcode/src/expect.zig @@ -21,3 +21,33 @@ pub fn setSharedBuffer(ptr: [*]u8, length: usize) callconv(.C) usize { pub fn expectFailedStart() callconv(.C) [*]u8 { return SHARED_BUFFER.ptr; } + +extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int; +extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque; +extern fn kill(pid: c_int, sig: c_int) c_int; +extern fn getppid() c_int; + +pub fn readSharedBufferEnv() callconv(.C) void { + const name = "/roc_expect_buffer"; // IMPORTANT: shared memory object names must begin with / and contain no other slashes! + const shared_fd = shm_open(@ptrCast(*const i8, name), O_RDWR | O_CREAT, 0o666); + const length = 4096; + + const shared_ptr = mmap( + null, + length, + PROT_WRITE, + MAP_SHARED, + shared_fd, + 0, + ); + + const ptr = @ptrCast([*]u8, shared_ptr); + + SHARED_BUFFER = ptr[0..length]; +} + +pub fn expectFailedFinalize() callconv(.C) void { + const parent_pid = getppid(); + + _ = kill(parent_pid, SIGUSR1); +} diff --git a/crates/compiler/builtins/bitcode/src/main.zig b/crates/compiler/builtins/bitcode/src/main.zig index 44ad0d99eb..eff2e48c97 100644 --- a/crates/compiler/builtins/bitcode/src/main.zig +++ b/crates/compiler/builtins/bitcode/src/main.zig @@ -168,9 +168,13 @@ comptime { if (builtin.target.cpu.arch != .wasm32) { exportUtilsFn(expect.expectFailedStart, "expect_failed_start"); + exportUtilsFn(expect.expectFailedFinalize, "expect_failed_finalize"); // sets the buffer used for expect failures @export(expect.setSharedBuffer, .{ .name = "set_shared_buffer", .linkage = .Weak }); + // + + exportUtilsFn(expect.readSharedBufferEnv, "read_env_shared_buffer"); } if (builtin.target.cpu.arch == .aarch64) { diff --git a/crates/compiler/builtins/build.rs b/crates/compiler/builtins/build.rs index 63b8ceb4cf..bf748c29de 100644 --- a/crates/compiler/builtins/build.rs +++ b/crates/compiler/builtins/build.rs @@ -12,7 +12,7 @@ use std::str; use tempfile::tempdir; /// To debug the zig code with debug prints, we need to disable the wasm code gen -const DEBUG: bool = false; +const DEBUG: bool = true; fn zig_executable() -> String { match std::env::var("ROC_ZIG") { @@ -45,11 +45,7 @@ fn main() { generate_bc_file(&bitcode_path, "ir-i386", "builtins-i386"); generate_bc_file(&bitcode_path, "ir-x86_64", "builtins-x86_64"); - generate_bc_file( - &bitcode_path, - "ir-windows-x86_64", - "builtins-windows-x86_64", - ); + // generate_bc_file( &bitcode_path, "ir-windows-x86_64", "builtins-windows-x86_64",); // OBJECT FILES #[cfg(windows)] diff --git a/crates/compiler/builtins/src/bitcode.rs b/crates/compiler/builtins/src/bitcode.rs index 52878f2b3a..2cde884fe5 100644 --- a/crates/compiler/builtins/src/bitcode.rs +++ b/crates/compiler/builtins/src/bitcode.rs @@ -404,6 +404,7 @@ pub const UTILS_DECREF_CHECK_NULL: &str = "roc_builtins.utils.decref_check_null" pub const UTILS_EXPECT_FAILED_START: &str = "roc_builtins.utils.expect_failed_start"; pub const UTILS_EXPECT_FAILED_FINALIZE: &str = "roc_builtins.utils.expect_failed_finalize"; +pub const UTILS_EXPECT_READ_ENV_SHARED_BUFFER: &str = "roc_builtins.utils.read_env_shared_buffer"; pub const UTILS_LONGJMP: &str = "longjmp"; pub const UTILS_SETJMP: &str = "setjmp"; diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 9747c640da..4a1e3c3c63 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -163,6 +163,7 @@ impl<'a, 'ctx> Scope<'a, 'ctx> { pub enum LlvmBackendMode { /// Assumes primitives (roc_alloc, roc_panic, etc) are provided by the host Binary, + BinaryDev, /// Creates a test wrapper around the main roc function to catch and report panics. /// Provides a testing implementation of primitives (roc_alloc, roc_panic, etc) GenTest, @@ -174,6 +175,7 @@ impl LlvmBackendMode { pub(crate) fn has_host(self) -> bool { match self { LlvmBackendMode::Binary => true, + LlvmBackendMode::BinaryDev => true, LlvmBackendMode::GenTest => false, LlvmBackendMode::WasmGenTest => true, LlvmBackendMode::CliTest => false, @@ -184,6 +186,7 @@ impl LlvmBackendMode { fn returns_roc_result(self) -> bool { match self { LlvmBackendMode::Binary => false, + LlvmBackendMode::BinaryDev => false, LlvmBackendMode::GenTest => true, LlvmBackendMode::WasmGenTest => true, LlvmBackendMode::CliTest => true, @@ -193,6 +196,7 @@ impl LlvmBackendMode { fn runs_expects(self) -> bool { match self { LlvmBackendMode::Binary => false, + LlvmBackendMode::BinaryDev => true, LlvmBackendMode::GenTest => false, LlvmBackendMode::WasmGenTest => false, LlvmBackendMode::CliTest => true, @@ -2820,6 +2824,10 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( if env.mode.runs_expects() { bd.position_at_end(throw_block); + if let LlvmBackendMode::BinaryDev = env.mode { + crate::llvm::expect::read_env_shared_buffer(env); + } + match env.target_info.ptr_width() { roc_target::PtrWidth::Bytes8 => { clone_to_shared_memory( @@ -2831,6 +2839,10 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( lookups, ); + if let LlvmBackendMode::BinaryDev = env.mode { + crate::llvm::expect::finalize(env); + } + bd.build_unconditional_branch(then_block); } roc_target::PtrWidth::Bytes4 => { @@ -3935,7 +3947,7 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>( ) } - LlvmBackendMode::Binary => {} + LlvmBackendMode::Binary | LlvmBackendMode::BinaryDev => {} } // a generic version that writes the result into a passed *u8 pointer @@ -3986,7 +3998,9 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>( roc_result_type(env, roc_function.get_type().get_return_type().unwrap()).into() } - LlvmBackendMode::Binary => basic_type_from_layout(env, &return_layout), + LlvmBackendMode::Binary | LlvmBackendMode::BinaryDev => { + basic_type_from_layout(env, &return_layout) + } }; let size: BasicValueEnum = return_type.size_of().unwrap().into(); @@ -4958,7 +4972,7 @@ pub fn build_proc<'a, 'ctx, 'env>( GenTest | WasmGenTest | CliTest => { /* no host, or exposing types is not supported */ } - Binary => { + Binary | BinaryDev => { for (alias_name, (generated_function, top_level, layout)) in aliases.iter() { expose_alias_to_host( env, diff --git a/crates/compiler/gen_llvm/src/llvm/expect.rs b/crates/compiler/gen_llvm/src/llvm/expect.rs index 55262d559c..53cf2f55bd 100644 --- a/crates/compiler/gen_llvm/src/llvm/expect.rs +++ b/crates/compiler/gen_llvm/src/llvm/expect.rs @@ -93,6 +93,26 @@ fn write_state<'a, 'ctx, 'env>( env.builder.build_store(offset_ptr, offset); } +pub(crate) fn read_env_shared_buffer(env: &Env) { + let func = env + .module + .get_function(bitcode::UTILS_EXPECT_READ_ENV_SHARED_BUFFER) + .unwrap(); + + env.builder + .build_call(func, &[], "call_expect_read_env_shared_buffer"); +} + +pub(crate) fn finalize(env: &Env) { + let func = env + .module + .get_function(bitcode::UTILS_EXPECT_FAILED_FINALIZE) + .unwrap(); + + env.builder + .build_call(func, &[], "call_expect_failed_finalize"); +} + pub(crate) fn clone_to_shared_memory<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, scope: &Scope<'a, 'ctx>, diff --git a/crates/repl_expect/src/run.rs b/crates/repl_expect/src/run.rs index b948a40e79..568422c609 100644 --- a/crates/repl_expect/src/run.rs +++ b/crates/repl_expect/src/run.rs @@ -20,8 +20,8 @@ use roc_target::TargetInfo; use target_lexicon::Triple; pub struct ExpectMemory<'a> { - ptr: *mut u8, - length: usize, + pub ptr: *mut u8, + pub length: usize, shm_name: Option, _marker: std::marker::PhantomData<&'a ()>, } @@ -53,6 +53,7 @@ impl<'a> ExpectMemory<'a> { let ptr = unsafe { let shared_fd = libc::shm_open(cstring.as_ptr().cast(), shm_flags, 0o666); + libc::ftruncate(shared_fd, 0); libc::ftruncate(shared_fd, Self::SHM_SIZE as _); libc::mmap( @@ -65,6 +66,9 @@ impl<'a> ExpectMemory<'a> { ) }; + // puts in the initial header + let _ = ExpectSequence::new(ptr as *mut u8); + Self { ptr: ptr.cast(), length: Self::SHM_SIZE, @@ -80,6 +84,33 @@ impl<'a> ExpectMemory<'a> { } } +#[allow(clippy::too_many_arguments)] +pub fn run_inline_expects<'a, W: std::io::Write>( + writer: &mut W, + render_target: RenderTarget, + arena: &'a Bump, + interns: &'a Interns, + layout_interner: &Arc>>, + lib: &libloading::Library, + expectations: &mut VecMap, + expects: ExpectFunctions<'_>, +) -> std::io::Result<(usize, usize)> { + let shm_name = format!("/roc_expect_buffer_{}", std::process::id()); + let mut memory = ExpectMemory::create_or_reuse_mmap(&shm_name); + + run_expects_with_memory( + writer, + render_target, + arena, + interns, + layout_interner, + lib, + expectations, + expects, + &mut memory, + ) +} + #[allow(clippy::too_many_arguments)] pub fn run_toplevel_expects<'a, W: std::io::Write>( writer: &mut W, diff --git a/examples/benchmarks/platform/host.zig b/examples/benchmarks/platform/host.zig index 1d5915be92..209ea9e06b 100644 --- a/examples/benchmarks/platform/host.zig +++ b/examples/benchmarks/platform/host.zig @@ -87,7 +87,9 @@ export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { const Unit = extern struct {}; -pub export fn main() callconv(.C) u8 { +pub fn main() !u8 { + const stderr = std.io.getStdErr().writer(); + // The size might be zero; if so, make it at least 8 so that we don't have a nullptr const size = std.math.max(@intCast(usize, roc__mainForHost_size()), 8); const raw_output = roc_alloc(@intCast(usize, size), @alignOf(u64)).?; @@ -108,7 +110,6 @@ pub export fn main() callconv(.C) u8 { const nanos = timer.read(); const seconds = (@intToFloat(f64, nanos) / 1_000_000_000.0); - const stderr = std.io.getStdErr().writer(); stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable; return 0; From e911d35416edc4852bd4621d5cc9a00fad9d0015 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 5 Oct 2022 21:18:14 +0200 Subject: [PATCH 04/31] return a success error code --- crates/cli/src/lib.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 4d482f3d3a..28604e2725 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -565,7 +565,6 @@ pub fn build( interns, layout_interner, }) => { - dbg!(expectations.len()); match config { BuildOnly => { // If possible, report the generated executable name relative to the current dir. @@ -935,7 +934,7 @@ fn roc_run_native_debug( mut expectations: VecMap, interns: Interns, layout_interner: SingleThreadedInterner, -) { +) -> ! { use roc_repl_expect::run::ExpectMemory; use signal_hook::{consts::signal::SIGCHLD, consts::signal::SIGUSR1, iterator::Signals}; @@ -954,6 +953,11 @@ fn roc_run_native_debug( // we are the child executable.execve(&argv, &envp); + + // Display a human-friendly error message + println!("Error {:?}", std::io::Error::last_os_error()); + + std::process::exit(1); }, -1 => { // something failed @@ -966,10 +970,7 @@ fn roc_run_native_debug( 1.. => { for sig in &mut signals { match sig { - SIGCHLD => { - // done! - return; - } + SIGCHLD => break, SIGUSR1 => { // this is the signal we use for an expect failure. Let's see what the child told us @@ -986,11 +987,11 @@ fn roc_run_native_debug( _ => println!("received signal {}", sig), } } + + std::process::exit(0) } _ => unreachable!(), } - - println!("done?"); } #[cfg(target_os = "linux")] From 4d726925bde9c5425dbbec24d47a831c7d24b9a5 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 5 Oct 2022 21:48:28 +0200 Subject: [PATCH 05/31] clippy --- crates/cli/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 28604e2725..b85076ae54 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -942,7 +942,7 @@ fn roc_run_native_debug( // let shm_name = format!("/roc_expect_buffer_{}", std::process::id()); let shm_name = "/roc_expect_buffer"; - let memory = ExpectMemory::create_or_reuse_mmap(&shm_name); + let memory = ExpectMemory::create_or_reuse_mmap(shm_name); let layout_interner = layout_interner.into_global(); From f838a94c0086bbf0763c575633510b666ba69080 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 5 Oct 2022 23:19:44 +0200 Subject: [PATCH 06/31] extra target/codegen config --- crates/cli/src/build.rs | 14 ++++---- crates/cli/src/lib.rs | 50 +++++++++++++++++++--------- crates/compiler/build/src/program.rs | 38 ++++++++++++++------- 3 files changed, 66 insertions(+), 36 deletions(-) diff --git a/crates/cli/src/build.rs b/crates/cli/src/build.rs index a06f862695..dfa4f92e07 100644 --- a/crates/cli/src/build.rs +++ b/crates/cli/src/build.rs @@ -1,7 +1,7 @@ use bumpalo::Bump; use roc_build::{ link::{link, preprocess_host_wasm32, rebuild_host, LinkType, LinkingStrategy}, - program::{self, Problems}, + program::{self, CodeGenOptions, Problems}, }; use roc_builtins::bitcode; use roc_collections::VecMap; @@ -62,8 +62,7 @@ pub fn build_file<'a>( arena: &'a Bump, target: &Triple, app_module_path: PathBuf, - opt_level: OptLevel, - emit_debug_info: bool, + code_gen_options: CodeGenOptions, emit_timings: bool, link_type: LinkType, linking_strategy: LinkingStrategy, @@ -123,7 +122,7 @@ pub fn build_file<'a>( match roc_target::OperatingSystem::from(target.operating_system) { Wasi => { - if matches!(opt_level, OptLevel::Development) { + if matches!(code_gen_options.opt_level, OptLevel::Development) { ("wasm", "wasm", Some("wasm")) } else { ("zig", "bc", Some("wasm")) @@ -182,7 +181,7 @@ pub fn build_file<'a>( }; let rebuild_thread = spawn_rebuild_thread( - opt_level, + code_gen_options.opt_level, linking_strategy, prebuilt, host_input_path.clone(), @@ -272,8 +271,7 @@ pub fn build_file<'a>( loaded, &app_module_path, target, - opt_level, - emit_debug_info, + code_gen_options, &preprocessed_host_path, wasm_dev_stack_bytes, ); @@ -352,7 +350,7 @@ pub fn build_file<'a>( let str_host_obj_path = bitcode::get_builtins_host_obj_path(); - if matches!(opt_level, OptLevel::Development) { + if matches!(code_gen_options.backend, program::CodeGenBackend::Assembly) { inputs.push(&str_host_obj_path); } diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index b85076ae54..02c3cf130b 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -5,7 +5,7 @@ use build::BuiltFile; use bumpalo::Bump; use clap::{Arg, ArgMatches, Command, ValueSource}; use roc_build::link::{LinkType, LinkingStrategy}; -use roc_build::program::Problems; +use roc_build::program::{CodeGenBackend, CodeGenOptions, Problems}; use roc_collections::VecMap; use roc_error_macros::{internal_error, user_error}; use roc_intern::SingleThreadedInterner; @@ -465,16 +465,30 @@ pub fn build( let arena = Bump::new(); let filename = matches.value_of_os(ROC_FILE).unwrap(); - let opt_level = match ( - matches.is_present(FLAG_OPTIMIZE), - matches.is_present(FLAG_OPT_SIZE), - matches.is_present(FLAG_DEV), - ) { - (true, false, false) => OptLevel::Optimize, - (false, true, false) => OptLevel::Size, - (false, false, true) => OptLevel::Development, - (false, false, false) => OptLevel::Normal, - _ => user_error!("build can be only one of `--dev`, `--optimize`, or `--opt-size`"), + + let code_gen_backend = if matches!(triple.architecture, Architecture::Wasm32) { + CodeGenBackend::Wasm + } else { + match matches.is_present(FLAG_DEV) { + true => CodeGenBackend::Assembly, + false => CodeGenBackend::Llvm, + } + }; + + let opt_level = if let BuildConfig::BuildAndRunIfNoErrors = config { + OptLevel::Development + } else { + match ( + matches.is_present(FLAG_OPTIMIZE), + matches.is_present(FLAG_OPT_SIZE), + ) { + (true, false) => OptLevel::Optimize, + (false, true) => OptLevel::Size, + (false, false) => OptLevel::Normal, + (true, true) => { + user_error!("build can be only one of `--optimize` and `--opt-size`") + } + } }; let emit_debug_info = matches.is_present(FLAG_DEBUG); let emit_timings = matches.is_present(FLAG_TIME); @@ -490,7 +504,7 @@ pub fn build( }; let wasm_dev_backend = matches!(opt_level, OptLevel::Development) - && matches!(triple.architecture, Architecture::Wasm32); + && matches!(code_gen_backend, CodeGenBackend::Wasm); let linking_strategy = if wasm_dev_backend { LinkingStrategy::Additive @@ -541,12 +555,18 @@ pub fn build( BuildAndRunIfNoErrors => BuildOrdering::BuildIfChecks, _ => BuildOrdering::AlwaysBuild, }; + + let code_gen_options = CodeGenOptions { + backend: code_gen_backend, + opt_level, + emit_debug_info, + }; + let res_binary_path = build_file( &arena, &triple, path.to_path_buf(), - opt_level, - emit_debug_info, + code_gen_options, emit_timings, link_type, linking_strategy, @@ -838,8 +858,6 @@ fn roc_run_native, S: AsRef>( .chain([std::ptr::null()]) .collect_in(arena); - let opt_level = OptLevel::Development; - match opt_level { OptLevel::Development => roc_run_native_debug( arena, diff --git a/crates/compiler/build/src/program.rs b/crates/compiler/build/src/program.rs index 050b3038b1..a35724030c 100644 --- a/crates/compiler/build/src/program.rs +++ b/crates/compiler/build/src/program.rs @@ -173,33 +173,47 @@ impl Deref for CodeObject { } } +#[derive(Debug, Clone, Copy)] +pub enum CodeGenBackend { + Assembly, + Llvm, + Wasm, +} + +#[derive(Debug, Clone, Copy)] +pub struct CodeGenOptions { + pub backend: CodeGenBackend, + pub opt_level: OptLevel, + pub emit_debug_info: bool, +} + #[allow(clippy::too_many_arguments)] pub fn gen_from_mono_module( arena: &bumpalo::Bump, loaded: MonomorphizedModule, roc_file_path: &Path, target: &target_lexicon::Triple, - opt_level: OptLevel, - emit_debug_info: bool, + code_gen_options: CodeGenOptions, preprocessed_host_path: &Path, wasm_dev_stack_bytes: Option, ) -> (CodeObject, CodeGenTiming) { - match opt_level { - OptLevel::Normal | OptLevel::Size | OptLevel::Optimize => gen_from_mono_module_llvm( - arena, - loaded, - roc_file_path, - target, - opt_level, - emit_debug_info, - ), - OptLevel::Development => gen_from_mono_module_dev( + match code_gen_options.backend { + CodeGenBackend::Assembly => gen_from_mono_module_dev( arena, loaded, target, preprocessed_host_path, wasm_dev_stack_bytes, ), + CodeGenBackend::Llvm => gen_from_mono_module_llvm( + arena, + loaded, + roc_file_path, + target, + code_gen_options.opt_level, + code_gen_options.emit_debug_info, + ), + CodeGenBackend::Wasm => unreachable!(), } } From fe9da8316d0d582fcebacc8a4730a6e33bffb6f9 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 5 Oct 2022 23:45:31 +0200 Subject: [PATCH 07/31] clone less --- crates/cli/src/build.rs | 19 ++---- crates/cli/src/lib.rs | 72 +++++------------------ crates/compiler/build/src/program.rs | 72 +++++++++++++++-------- crates/compiler/load/src/lib.rs | 4 +- crates/compiler/load_internal/src/file.rs | 7 +++ 5 files changed, 75 insertions(+), 99 deletions(-) diff --git a/crates/cli/src/build.rs b/crates/cli/src/build.rs index dfa4f92e07..96e79aca9e 100644 --- a/crates/cli/src/build.rs +++ b/crates/cli/src/build.rs @@ -4,13 +4,10 @@ use roc_build::{ program::{self, CodeGenOptions, Problems}, }; use roc_builtins::bitcode; -use roc_collections::VecMap; -use roc_intern::SingleThreadedInterner; use roc_load::{ - EntryPoint, ExecutionMode, Expectations, LoadConfig, LoadMonomorphizedError, LoadedModule, + EntryPoint, ExecutionMode, ExpectMetadata, LoadConfig, LoadMonomorphizedError, LoadedModule, LoadingProblem, Threading, }; -use roc_module::symbol::{Interns, ModuleId}; use roc_mono::ir::OptLevel; use roc_reporting::report::RenderTarget; use roc_target::TargetInfo; @@ -35,9 +32,7 @@ pub struct BuiltFile<'a> { pub binary_path: PathBuf, pub problems: Problems, pub total_time: Duration, - pub expectations: VecMap, - pub interns: Interns, - pub layout_interner: SingleThreadedInterner<'a, roc_mono::layout::Layout<'a>>, + pub expect_metadata: ExpectMetadata<'a>, } pub enum BuildOrdering { @@ -242,12 +237,8 @@ pub fn build_file<'a>( // inside a nested scope without causing a borrow error! let mut loaded = loaded; let problems = program::report_problems_monomorphized(&mut loaded); - let expectations = std::mem::take(&mut loaded.expectations); - let layout_interner = loaded.layout_interner.clone(); let loaded = loaded; - let interns = loaded.interns.clone(); - enum HostRebuildTiming { BeforeApp(u128), ConcurrentWithApp(JoinHandle), @@ -266,7 +257,7 @@ pub fn build_file<'a>( HostRebuildTiming::ConcurrentWithApp(rebuild_thread) }; - let (roc_app_bytes, code_gen_timing) = program::gen_from_mono_module( + let (roc_app_bytes, code_gen_timing, expect_metadata) = program::gen_from_mono_module( arena, loaded, &app_module_path, @@ -387,9 +378,7 @@ pub fn build_file<'a>( binary_path, problems, total_time, - interns, - expectations, - layout_interner, + expect_metadata, }) } diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 02c3cf130b..8c36d73355 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -6,13 +6,9 @@ use bumpalo::Bump; use clap::{Arg, ArgMatches, Command, ValueSource}; use roc_build::link::{LinkType, LinkingStrategy}; use roc_build::program::{CodeGenBackend, CodeGenOptions, Problems}; -use roc_collections::VecMap; use roc_error_macros::{internal_error, user_error}; -use roc_intern::SingleThreadedInterner; -use roc_load::{Expectations, LoadingProblem, Threading}; -use roc_module::symbol::{Interns, ModuleId}; +use roc_load::{ExpectMetadata, LoadingProblem, Threading}; use roc_mono::ir::OptLevel; -use roc_mono::layout::Layout; use std::env; use std::ffi::{CString, OsStr}; use std::io; @@ -581,9 +577,7 @@ pub fn build( binary_path, problems, total_time, - expectations, - interns, - layout_interner, + expect_metadata, }) => { match config { BuildOnly => { @@ -619,16 +613,7 @@ pub fn build( // ManuallyDrop will leak the bytes because we don't drop manually let bytes = &ManuallyDrop::new(std::fs::read(&binary_path).unwrap()); - roc_run( - &arena, - opt_level, - triple, - args, - bytes, - expectations, - interns, - layout_interner, - ) + roc_run(&arena, opt_level, triple, args, bytes, expect_metadata) } BuildAndRunIfNoErrors => { debug_assert!( @@ -649,16 +634,7 @@ pub fn build( // ManuallyDrop will leak the bytes because we don't drop manually let bytes = &ManuallyDrop::new(std::fs::read(&binary_path).unwrap()); - roc_run( - &arena, - opt_level, - triple, - args, - bytes, - expectations, - interns, - layout_interner, - ) + roc_run(&arena, opt_level, triple, args, bytes, expect_metadata) } } } @@ -729,9 +705,7 @@ fn roc_run<'a, I: IntoIterator>( triple: Triple, args: I, binary_bytes: &[u8], - expectations: VecMap, - interns: Interns, - layout_interner: SingleThreadedInterner, + expect_metadata: ExpectMetadata, ) -> io::Result { match triple.architecture { Architecture::Wasm32 => { @@ -770,15 +744,7 @@ fn roc_run<'a, I: IntoIterator>( Ok(0) } - _ => roc_run_native( - arena, - opt_level, - args, - binary_bytes, - expectations, - interns, - layout_interner, - ), + _ => roc_run_native(arena, opt_level, args, binary_bytes, expect_metadata), } } @@ -836,9 +802,7 @@ fn roc_run_native, S: AsRef>( opt_level: OptLevel, args: I, binary_bytes: &[u8], - expectations: VecMap, - interns: Interns, - layout_interner: SingleThreadedInterner, + expect_metadata: ExpectMetadata, ) -> std::io::Result { use bumpalo::collections::CollectIn; @@ -859,15 +823,7 @@ fn roc_run_native, S: AsRef>( .collect_in(arena); match opt_level { - OptLevel::Development => roc_run_native_debug( - arena, - executable, - argv, - envp, - expectations, - interns, - layout_interner, - ), + OptLevel::Development => roc_dev_native(arena, executable, argv, envp, expect_metadata), OptLevel::Normal | OptLevel::Size | OptLevel::Optimize => { roc_run_native_fast(executable, &argv, &envp); } @@ -944,18 +900,22 @@ impl ExecutableFile { // with Expect #[cfg(target_family = "unix")] -fn roc_run_native_debug( +fn roc_dev_native( arena: &Bump, executable: ExecutableFile, argv: bumpalo::collections::Vec<*const c_char>, envp: bumpalo::collections::Vec<*const c_char>, - mut expectations: VecMap, - interns: Interns, - layout_interner: SingleThreadedInterner, + expect_metadata: ExpectMetadata, ) -> ! { use roc_repl_expect::run::ExpectMemory; use signal_hook::{consts::signal::SIGCHLD, consts::signal::SIGUSR1, iterator::Signals}; + let ExpectMetadata { + mut expectations, + interns, + layout_interner, + } = expect_metadata; + let mut signals = Signals::new(&[SIGCHLD, SIGUSR1]).unwrap(); // let shm_name = format!("/roc_expect_buffer_{}", std::process::id()); diff --git a/crates/compiler/build/src/program.rs b/crates/compiler/build/src/program.rs index a35724030c..db37b90de2 100644 --- a/crates/compiler/build/src/program.rs +++ b/crates/compiler/build/src/program.rs @@ -2,7 +2,7 @@ use inkwell::memory_buffer::MemoryBuffer; pub use roc_gen_llvm::llvm::build::FunctionIterator; use roc_gen_llvm::llvm::build::{module_from_builtins, LlvmBackendMode}; use roc_gen_llvm::llvm::externs::add_default_roc_externs; -use roc_load::{EntryPoint, LoadedModule, MonomorphizedModule}; +use roc_load::{EntryPoint, ExpectMetadata, LoadedModule, MonomorphizedModule}; use roc_module::symbol::{Interns, ModuleId}; use roc_mono::ir::OptLevel; use roc_region::all::LineInfo; @@ -187,16 +187,18 @@ pub struct CodeGenOptions { pub emit_debug_info: bool, } +type GenFromMono<'a> = (CodeObject, CodeGenTiming, ExpectMetadata<'a>); + #[allow(clippy::too_many_arguments)] -pub fn gen_from_mono_module( - arena: &bumpalo::Bump, - loaded: MonomorphizedModule, +pub fn gen_from_mono_module<'a>( + arena: &'a bumpalo::Bump, + loaded: MonomorphizedModule<'a>, roc_file_path: &Path, target: &target_lexicon::Triple, code_gen_options: CodeGenOptions, preprocessed_host_path: &Path, wasm_dev_stack_bytes: Option, -) -> (CodeObject, CodeGenTiming) { +) -> GenFromMono<'a> { match code_gen_options.backend { CodeGenBackend::Assembly => gen_from_mono_module_dev( arena, @@ -220,14 +222,14 @@ pub fn gen_from_mono_module( // TODO how should imported modules factor into this? What if those use builtins too? // TODO this should probably use more helper functions // TODO make this polymorphic in the llvm functions so it can be reused for another backend. -fn gen_from_mono_module_llvm( - arena: &bumpalo::Bump, - loaded: MonomorphizedModule, +fn gen_from_mono_module_llvm<'a>( + arena: &'a bumpalo::Bump, + loaded: MonomorphizedModule<'a>, roc_file_path: &Path, target: &target_lexicon::Triple, opt_level: OptLevel, emit_debug_info: bool, -) -> (CodeObject, CodeGenTiming) { +) -> GenFromMono<'a> { use crate::target::{self, convert_opt_level}; use inkwell::attributes::{Attribute, AttributeLoc}; use inkwell::context::Context; @@ -443,17 +445,22 @@ fn gen_from_mono_module_llvm( ( CodeObject::MemoryBuffer(memory_buffer), CodeGenTiming { code_gen }, + ExpectMetadata { + interns: env.interns, + layout_interner: loaded.layout_interner, + expectations: loaded.expectations, + }, ) } #[cfg(feature = "target-wasm32")] -fn gen_from_mono_module_dev( - arena: &bumpalo::Bump, - loaded: MonomorphizedModule, +fn gen_from_mono_module_dev<'a>( + arena: &'a bumpalo::Bump, + loaded: MonomorphizedModule<'a>, target: &target_lexicon::Triple, preprocessed_host_path: &Path, wasm_dev_stack_bytes: Option, -) -> (CodeObject, CodeGenTiming) { +) -> GenFromMono<'a> { use target_lexicon::Architecture; match target.architecture { @@ -471,13 +478,13 @@ fn gen_from_mono_module_dev( } #[cfg(not(feature = "target-wasm32"))] -pub fn gen_from_mono_module_dev( - arena: &bumpalo::Bump, - loaded: MonomorphizedModule, +pub fn gen_from_mono_module_dev<'a>( + arena: &'a bumpalo::Bump, + loaded: MonomorphizedModule<'a>, target: &target_lexicon::Triple, _host_input_path: &Path, _wasm_dev_stack_bytes: Option, -) -> (CodeObject, CodeGenTiming) { +) -> GenFromMono<'a> { use target_lexicon::Architecture; match target.architecture { @@ -489,12 +496,12 @@ pub fn gen_from_mono_module_dev( } #[cfg(feature = "target-wasm32")] -fn gen_from_mono_module_dev_wasm32( - arena: &bumpalo::Bump, - loaded: MonomorphizedModule, +fn gen_from_mono_module_dev_wasm32<'a>( + arena: &'a bumpalo::Bump, + loaded: MonomorphizedModule<'a>, preprocessed_host_path: &Path, wasm_dev_stack_bytes: Option, -) -> (CodeObject, CodeGenTiming) { +) -> GenFromMono<'a> { let code_gen_start = Instant::now(); let MonomorphizedModule { module_id, @@ -543,14 +550,19 @@ fn gen_from_mono_module_dev_wasm32( ( CodeObject::Vector(final_binary_bytes), CodeGenTiming { code_gen }, + ExpectMetadata { + interns, + layout_interner, + expectations: loaded.expectations, + }, ) } -fn gen_from_mono_module_dev_assembly( - arena: &bumpalo::Bump, - loaded: MonomorphizedModule, +fn gen_from_mono_module_dev_assembly<'a>( + arena: &'a bumpalo::Bump, + loaded: MonomorphizedModule<'a>, target: &target_lexicon::Triple, -) -> (CodeObject, CodeGenTiming) { +) -> GenFromMono<'a> { let code_gen_start = Instant::now(); let lazy_literals = true; @@ -582,5 +594,13 @@ fn gen_from_mono_module_dev_assembly( .write() .expect("failed to build output object"); - (CodeObject::Vector(module_out), CodeGenTiming { code_gen }) + ( + CodeObject::Vector(module_out), + CodeGenTiming { code_gen }, + ExpectMetadata { + interns, + layout_interner, + expectations: loaded.expectations, + }, + ) } diff --git a/crates/compiler/load/src/lib.rs b/crates/compiler/load/src/lib.rs index 527b164fcc..2cbcd6f626 100644 --- a/crates/compiler/load/src/lib.rs +++ b/crates/compiler/load/src/lib.rs @@ -16,8 +16,8 @@ const SKIP_SUBS_CACHE: bool = { pub use roc_load_internal::docs; pub use roc_load_internal::file::{ - EntryPoint, ExecutionMode, Expectations, LoadConfig, LoadResult, LoadStart, LoadedModule, - LoadingProblem, MonomorphizedModule, Phase, Threading, + EntryPoint, ExecutionMode, ExpectMetadata, Expectations, LoadConfig, LoadResult, LoadStart, + LoadedModule, LoadingProblem, MonomorphizedModule, Phase, Threading, }; #[allow(clippy::too_many_arguments)] diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index af1024f28a..70334f4baf 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -702,6 +702,13 @@ pub struct MonomorphizedModule<'a> { pub expectations: VecMap, } +/// Values used to render expect output +pub struct ExpectMetadata<'a> { + pub interns: Interns, + pub layout_interner: SingleThreadedInterner<'a, Layout<'a>>, + pub expectations: VecMap, +} + #[derive(Debug)] pub enum EntryPoint<'a> { Executable { From b09c631895ae8139658662e5dd7bdac0fd478348 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 5 Oct 2022 23:49:25 +0200 Subject: [PATCH 08/31] cleanup --- crates/cli/src/lib.rs | 9 ++++++--- crates/compiler/builtins/build.rs | 8 ++++++-- crates/compiler/intern/src/lib.rs | 2 +- crates/repl_expect/src/run.rs | 4 ++-- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 8c36d73355..45913d1231 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -459,8 +459,9 @@ pub fn build( use build::build_file; use BuildConfig::*; - let arena = Bump::new(); - let filename = matches.value_of_os(ROC_FILE).unwrap(); + // the process will end after this function, + // so we don't want to spend time freeing these values + let arena = ManuallyDrop::new(Bump::new()); let code_gen_backend = if matches!(triple.architecture, Architecture::Wasm32) { CodeGenBackend::Wasm @@ -520,6 +521,8 @@ pub fn build( // We make an exception for Wasm, because cross-compiling is the norm in that case. triple != Triple::host() && !matches!(triple.architecture, Architecture::Wasm32) }; + + let filename = matches.value_of_os(ROC_FILE).unwrap(); let path = Path::new(filename); // Spawn the root task @@ -700,7 +703,7 @@ fn print_problems(problems: Problems, total_time: std::time::Duration) { } fn roc_run<'a, I: IntoIterator>( - arena: &Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it! + arena: &Bump, opt_level: OptLevel, triple: Triple, args: I, diff --git a/crates/compiler/builtins/build.rs b/crates/compiler/builtins/build.rs index bf748c29de..63b8ceb4cf 100644 --- a/crates/compiler/builtins/build.rs +++ b/crates/compiler/builtins/build.rs @@ -12,7 +12,7 @@ use std::str; use tempfile::tempdir; /// To debug the zig code with debug prints, we need to disable the wasm code gen -const DEBUG: bool = true; +const DEBUG: bool = false; fn zig_executable() -> String { match std::env::var("ROC_ZIG") { @@ -45,7 +45,11 @@ fn main() { generate_bc_file(&bitcode_path, "ir-i386", "builtins-i386"); generate_bc_file(&bitcode_path, "ir-x86_64", "builtins-x86_64"); - // generate_bc_file( &bitcode_path, "ir-windows-x86_64", "builtins-windows-x86_64",); + generate_bc_file( + &bitcode_path, + "ir-windows-x86_64", + "builtins-windows-x86_64", + ); // OBJECT FILES #[cfg(windows)] diff --git a/crates/compiler/intern/src/lib.rs b/crates/compiler/intern/src/lib.rs index c1c1261ec5..ef8c6b0f74 100644 --- a/crates/compiler/intern/src/lib.rs +++ b/crates/compiler/intern/src/lib.rs @@ -60,7 +60,7 @@ pub struct ThreadLocalInterner<'a, K> { /// /// The only way to construct such an interner is to collapse a shared [GlobalInterner] into /// a [SingleThreadedInterner], via [GlobalInterner::unwrap]. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct SingleThreadedInterner<'a, K> { map: BumpMap<&'a K, Interned>, vec: Vec<&'a K>, diff --git a/crates/repl_expect/src/run.rs b/crates/repl_expect/src/run.rs index 568422c609..fb1615b5cd 100644 --- a/crates/repl_expect/src/run.rs +++ b/crates/repl_expect/src/run.rs @@ -20,8 +20,8 @@ use roc_target::TargetInfo; use target_lexicon::Triple; pub struct ExpectMemory<'a> { - pub ptr: *mut u8, - pub length: usize, + ptr: *mut u8, + length: usize, shm_name: Option, _marker: std::marker::PhantomData<&'a ()>, } From 61c26dfd4d9efe254920f060ab7160ea69922efa Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 6 Oct 2022 00:06:43 +0200 Subject: [PATCH 09/31] pick whether to do expects based on the opt level --- crates/compiler/build/src/program.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/crates/compiler/build/src/program.rs b/crates/compiler/build/src/program.rs index db37b90de2..74127add7a 100644 --- a/crates/compiler/build/src/program.rs +++ b/crates/compiler/build/src/program.rs @@ -207,14 +207,9 @@ pub fn gen_from_mono_module<'a>( preprocessed_host_path, wasm_dev_stack_bytes, ), - CodeGenBackend::Llvm => gen_from_mono_module_llvm( - arena, - loaded, - roc_file_path, - target, - code_gen_options.opt_level, - code_gen_options.emit_debug_info, - ), + CodeGenBackend::Llvm => { + gen_from_mono_module_llvm(arena, loaded, roc_file_path, target, code_gen_options) + } CodeGenBackend::Wasm => unreachable!(), } } @@ -227,8 +222,7 @@ fn gen_from_mono_module_llvm<'a>( loaded: MonomorphizedModule<'a>, roc_file_path: &Path, target: &target_lexicon::Triple, - opt_level: OptLevel, - emit_debug_info: bool, + code_gen_options: CodeGenOptions, ) -> GenFromMono<'a> { use crate::target::{self, convert_opt_level}; use inkwell::attributes::{Attribute, AttributeLoc}; @@ -278,6 +272,12 @@ fn gen_from_mono_module_llvm<'a>( } } + let CodeGenOptions { + backend: _, + opt_level, + emit_debug_info, + } = code_gen_options; + let builder = context.create_builder(); let (dibuilder, compile_unit) = roc_gen_llvm::llvm::build::Env::new_debug_info(module); let (mpm, _fpm) = roc_gen_llvm::llvm::build::construct_optimization_passes(module, opt_level); @@ -293,7 +293,11 @@ fn gen_from_mono_module_llvm<'a>( interns: loaded.interns, module, target_info, - mode: LlvmBackendMode::BinaryDev, + mode: match opt_level { + OptLevel::Development => LlvmBackendMode::BinaryDev, + OptLevel::Normal | OptLevel::Size | OptLevel::Optimize => LlvmBackendMode::Binary, + }, + exposed_to_host: loaded.exposed_to_host.values.keys().copied().collect(), }; From d27eff1b36b8a29ae85922b1549190673c56470a Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 9 Oct 2022 19:00:34 +0200 Subject: [PATCH 10/31] fix test compilation --- crates/compiler/test_gen/src/helpers/llvm.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/compiler/test_gen/src/helpers/llvm.rs b/crates/compiler/test_gen/src/helpers/llvm.rs index 707584fc47..5d4f7e3558 100644 --- a/crates/compiler/test_gen/src/helpers/llvm.rs +++ b/crates/compiler/test_gen/src/helpers/llvm.rs @@ -243,6 +243,7 @@ fn create_llvm_module<'a>( }; let (main_fn_name, main_fn) = match config.mode { LlvmBackendMode::Binary => unreachable!(), + LlvmBackendMode::BinaryDev => unreachable!(), LlvmBackendMode::CliTest => unreachable!(), LlvmBackendMode::WasmGenTest => roc_gen_llvm::llvm::build::build_wasm_test_wrapper( &env, From 19ea3489cba446cbba989d68b1634b07e42f0b97 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 12 Oct 2022 20:59:18 +0200 Subject: [PATCH 11/31] provide more libc functions for the expect implementation --- .../fixtures/multi-dep-str/platform/host.zig | 28 ++++++++++++ .../multi-dep-thunk/platform/host.zig | 28 ++++++++++++ .../algorithms/fibonacci-platform/host.zig | 30 ++++++++++++- .../algorithms/quicksort-platform/host.zig | 28 ++++++++++++ .../benchmarks/platform/host.zig | 30 ++++++++++++- .../platform-switching/c-platform/host.c | 6 +++ .../rust-platform/src/lib.rs | 35 +++++++++++++++ .../platform-switching/zig-platform/host.zig | 30 ++++++++++++- .../compiler/builtins/bitcode/src/expect.zig | 43 +++++++++++-------- crates/linker/src/elf.rs | 24 ++++++++--- examples/cli/cli-platform/src/lib.rs | 35 +++++++++++++++ examples/cli/effects-platform/host.zig | 30 ++++++++++++- .../cli/false-interpreter/platform/src/lib.rs | 35 +++++++++++++++ examples/cli/tui-platform/host.zig | 30 ++++++++++++- examples/parser/platform/host.c | 5 +++ examples/ruby-interop/platform/host.c | 5 +++ examples/static-site-gen/platform/src/lib.rs | 35 +++++++++++++++ 17 files changed, 426 insertions(+), 31 deletions(-) diff --git a/crates/cli/tests/fixtures/multi-dep-str/platform/host.zig b/crates/cli/tests/fixtures/multi-dep-str/platform/host.zig index d1e0e912fa..eaed7abb87 100644 --- a/crates/cli/tests/fixtures/multi-dep-str/platform/host.zig +++ b/crates/cli/tests/fixtures/multi-dep-str/platform/host.zig @@ -65,6 +65,34 @@ export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void { std.process.exit(0); } +extern fn kill(pid: c_int, sig: c_int) c_int; +extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int; +extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque; +extern fn getppid() c_int; + +fn roc_getppid() callconv(.C) c_int { + return getppid(); +} + +fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int { + return kill(pid, sig); +} +fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int { + return shm_open(name, oflag, mode); +} +fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque { + return mmap(addr, length, prot, flags, fd, offset); +} + +comptime { + if (builtin.os.tag == .macos or builtin.os.tag == .linux) { + @export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong }); + @export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong }); + @export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong }); + @export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong }); + } +} + const Unit = extern struct {}; pub export fn main() i32 { diff --git a/crates/cli/tests/fixtures/multi-dep-thunk/platform/host.zig b/crates/cli/tests/fixtures/multi-dep-thunk/platform/host.zig index 1bf51a90e9..935f24ea62 100644 --- a/crates/cli/tests/fixtures/multi-dep-thunk/platform/host.zig +++ b/crates/cli/tests/fixtures/multi-dep-thunk/platform/host.zig @@ -64,6 +64,34 @@ export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void { std.process.exit(0); } +extern fn kill(pid: c_int, sig: c_int) c_int; +extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int; +extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque; +extern fn getppid() c_int; + +fn roc_getppid() callconv(.C) c_int { + return getppid(); +} + +fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int { + return kill(pid, sig); +} +fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int { + return shm_open(name, oflag, mode); +} +fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque { + return mmap(addr, length, prot, flags, fd, offset); +} + +comptime { + if (builtin.os.tag == .macos or builtin.os.tag == .linux) { + @export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong }); + @export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong }); + @export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong }); + @export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong }); + } +} + const Unit = extern struct {}; pub export fn main() i32 { diff --git a/crates/cli_testing_examples/algorithms/fibonacci-platform/host.zig b/crates/cli_testing_examples/algorithms/fibonacci-platform/host.zig index 55f55e2658..004663313b 100644 --- a/crates/cli_testing_examples/algorithms/fibonacci-platform/host.zig +++ b/crates/cli_testing_examples/algorithms/fibonacci-platform/host.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; @@ -13,7 +14,6 @@ comptime { // -fcompiler-rt in link.rs instead of doing this. Note that this // workaround is present in many host.zig files, so make sure to undo // it everywhere! - const builtin = @import("builtin"); if (builtin.os.tag == .macos) { _ = @import("compiler_rt"); } @@ -81,6 +81,34 @@ export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { return memset(dst, value, size); } +extern fn kill(pid: c_int, sig: c_int) c_int; +extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int; +extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque; +extern fn getppid() c_int; + +fn roc_getppid() callconv(.C) c_int { + return getppid(); +} + +fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int { + return kill(pid, sig); +} +fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int { + return shm_open(name, oflag, mode); +} +fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque { + return mmap(addr, length, prot, flags, fd, offset); +} + +comptime { + if (builtin.os.tag == .macos or builtin.os.tag == .linux) { + @export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong }); + @export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong }); + @export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong }); + @export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong }); + } +} + pub export fn main() u8 { const stdout = std.io.getStdOut().writer(); const stderr = std.io.getStdErr().writer(); diff --git a/crates/cli_testing_examples/algorithms/quicksort-platform/host.zig b/crates/cli_testing_examples/algorithms/quicksort-platform/host.zig index c2e70f3d31..3047ed327e 100644 --- a/crates/cli_testing_examples/algorithms/quicksort-platform/host.zig +++ b/crates/cli_testing_examples/algorithms/quicksort-platform/host.zig @@ -80,6 +80,34 @@ export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { return memset(dst, value, size); } +extern fn kill(pid: c_int, sig: c_int) c_int; +extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int; +extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque; +extern fn getppid() c_int; + +fn roc_getppid() callconv(.C) c_int { + return getppid(); +} + +fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int { + return kill(pid, sig); +} +fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int { + return shm_open(name, oflag, mode); +} +fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque { + return mmap(addr, length, prot, flags, fd, offset); +} + +comptime { + if (builtin.os.tag == .macos or builtin.os.tag == .linux) { + @export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong }); + @export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong }); + @export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong }); + @export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong }); + } +} + // warning! the array is currently stack-allocated so don't make this too big const NUM_NUMS = 100; diff --git a/crates/cli_testing_examples/benchmarks/platform/host.zig b/crates/cli_testing_examples/benchmarks/platform/host.zig index 209ea9e06b..d8010a3de1 100644 --- a/crates/cli_testing_examples/benchmarks/platform/host.zig +++ b/crates/cli_testing_examples/benchmarks/platform/host.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); const str = @import("str"); const RocStr = str.RocStr; const testing = std.testing; @@ -15,7 +16,6 @@ comptime { // -fcompiler-rt in link.rs instead of doing this. Note that this // workaround is present in many host.zig files, so make sure to undo // it everywhere! - const builtin = @import("builtin"); if (builtin.os.tag == .macos) { _ = @import("compiler_rt"); } @@ -85,6 +85,34 @@ export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { return memset(dst, value, size); } +extern fn kill(pid: c_int, sig: c_int) c_int; +extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int; +extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque; +extern fn getppid() c_int; + +fn roc_getppid() callconv(.C) c_int { + return getppid(); +} + +fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int { + return kill(pid, sig); +} +fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int { + return shm_open(name, oflag, mode); +} +fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque { + return mmap(addr, length, prot, flags, fd, offset); +} + +comptime { + if (builtin.os.tag == .macos or builtin.os.tag == .linux) { + @export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong }); + @export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong }); + @export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong }); + @export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong }); + } +} + const Unit = extern struct {}; pub fn main() !u8 { diff --git a/crates/cli_testing_examples/platform-switching/c-platform/host.c b/crates/cli_testing_examples/platform-switching/c-platform/host.c index 3f9a63c2a2..553af25c4c 100644 --- a/crates/cli_testing_examples/platform-switching/c-platform/host.c +++ b/crates/cli_testing_examples/platform-switching/c-platform/host.c @@ -27,6 +27,12 @@ void* roc_memcpy(void* dest, const void* src, size_t n) { void* roc_memset(void* str, int c, size_t n) { return memset(str, c, n); } + +int roc_send_signal(int pid, int sig) { return kill(pid, sig); } +int roc_shm_open(char* name, int oflag, int mode) { return shm_open(name, oflag, mode); } +void* roc_mmap(void* addr, int length, int prot, int flags, int fd, int offset) { return mmap(addr, length, prot, flags, fd, offset); } +int roc_getppid() { return getppid(); } + struct RocStr { char* bytes; size_t len; diff --git a/crates/cli_testing_examples/platform-switching/rust-platform/src/lib.rs b/crates/cli_testing_examples/platform-switching/rust-platform/src/lib.rs index 303f5fb6f1..ce2bf4fc0a 100644 --- a/crates/cli_testing_examples/platform-switching/rust-platform/src/lib.rs +++ b/crates/cli_testing_examples/platform-switching/rust-platform/src/lib.rs @@ -54,6 +54,41 @@ pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut libc::memset(dst, c, n) } +#[cfg(unix)] +#[no_mangle] +pub unsafe extern "C" fn roc_getppid() -> libc::pid_t { + libc::getppid() +} + +#[cfg(unix)] +#[no_mangle] +pub unsafe extern "C" fn roc_mmap( + addr: *mut libc::c_void, + len: libc::size_t, + prot: libc::c_int, + flags: libc::c_int, + fd: libc::c_int, + offset: libc::off_t, +) -> *mut libc::c_void { + libc::mmap(addr, len, prot, flags, fd, offset) +} + +#[cfg(unix)] +#[no_mangle] +pub unsafe extern "C" fn roc_shm_open( + name: *const libc::c_char, + oflag: libc::c_int, + mode: libc::mode_t, +) -> libc::c_int { + libc::shm_open(name, oflag, mode) +} + +#[cfg(unix)] +#[no_mangle] +pub unsafe extern "C" fn roc_send_signal(pid: libc::pid_t, sig: libc::c_int) -> libc::c_int { + libc::kill(pid, sig) +} + #[no_mangle] pub extern "C" fn rust_main() -> i32 { unsafe { diff --git a/crates/cli_testing_examples/platform-switching/zig-platform/host.zig b/crates/cli_testing_examples/platform-switching/zig-platform/host.zig index d03e803047..e9f6cc83e4 100644 --- a/crates/cli_testing_examples/platform-switching/zig-platform/host.zig +++ b/crates/cli_testing_examples/platform-switching/zig-platform/host.zig @@ -75,6 +75,34 @@ export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { return memset(dst, value, size); } +extern fn kill(pid: c_int, sig: c_int) c_int; +extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int; +extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque; +extern fn getppid() c_int; + +fn roc_getppid() callconv(.C) c_int { + return getppid(); +} + +fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int { + return kill(pid, sig); +} +fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int { + return shm_open(name, oflag, mode); +} +fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque { + return mmap(addr, length, prot, flags, fd, offset); +} + +comptime { + if (builtin.os.tag == .macos or builtin.os.tag == .linux) { + @export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong }); + @export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong }); + @export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong }); + @export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong }); + } +} + const mem = std.mem; const Allocator = mem.Allocator; @@ -103,4 +131,4 @@ pub fn main() u8 { stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable; return 0; -} \ No newline at end of file +} diff --git a/crates/compiler/builtins/bitcode/src/expect.zig b/crates/compiler/builtins/bitcode/src/expect.zig index 9aab2081a2..f6a7e19c73 100644 --- a/crates/compiler/builtins/bitcode/src/expect.zig +++ b/crates/compiler/builtins/bitcode/src/expect.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); const SIGUSR1: c_int = 10; @@ -22,32 +23,36 @@ pub fn expectFailedStart() callconv(.C) [*]u8 { return SHARED_BUFFER.ptr; } -extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int; -extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque; -extern fn kill(pid: c_int, sig: c_int) c_int; -extern fn getppid() c_int; +extern fn roc_send_signal(pid: c_int, sig: c_int) c_int; +extern fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int; +extern fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque; +extern fn roc_getppid() c_int; pub fn readSharedBufferEnv() callconv(.C) void { - const name = "/roc_expect_buffer"; // IMPORTANT: shared memory object names must begin with / and contain no other slashes! - const shared_fd = shm_open(@ptrCast(*const i8, name), O_RDWR | O_CREAT, 0o666); - const length = 4096; + if (builtin.os.tag == .macos or builtin.os.tag == .linux) { + const name = "/roc_expect_buffer"; // IMPORTANT: shared memory object names must begin with / and contain no other slashes! + const shared_fd = roc_shm_open(@ptrCast(*const i8, name), O_RDWR | O_CREAT, 0o666); + const length = 4096; - const shared_ptr = mmap( - null, - length, - PROT_WRITE, - MAP_SHARED, - shared_fd, - 0, - ); + const shared_ptr = roc_mmap( + null, + length, + PROT_WRITE, + MAP_SHARED, + shared_fd, + 0, + ); - const ptr = @ptrCast([*]u8, shared_ptr); + const ptr = @ptrCast([*]u8, shared_ptr); - SHARED_BUFFER = ptr[0..length]; + SHARED_BUFFER = ptr[0..length]; + } } pub fn expectFailedFinalize() callconv(.C) void { - const parent_pid = getppid(); + if (builtin.os.tag == .macos or builtin.os.tag == .linux) { + const parent_pid = roc_getppid(); - _ = kill(parent_pid, SIGUSR1); + _ = roc_send_signal(parent_pid, SIGUSR1); + } } diff --git a/crates/linker/src/elf.rs b/crates/linker/src/elf.rs index 11db80e4da..637054a173 100644 --- a/crates/linker/src/elf.rs +++ b/crates/linker/src/elf.rs @@ -77,13 +77,23 @@ fn collect_roc_definitions<'a>(object: &object::File<'a, &'a [u8]>) -> MutMap Some("memcpy"), + "roc_memset" => Some("memset"), + "roc_memmove" => Some("memmove"), + + // for expects + "roc_mmap" => Some("mmap"), + "roc_getppid" => Some("getppid"), + "roc_send_signal" => Some("kill"), + "roc_shm_open" => Some("shm_open"), + + _ => None, + }; + + if let Some(libc_symbol) = direct_mapping { + vaddresses.insert(libc_symbol.to_string(), address); } vaddresses.insert(name.to_string(), address); diff --git a/examples/cli/cli-platform/src/lib.rs b/examples/cli/cli-platform/src/lib.rs index 51abf949eb..d8b8787d2f 100644 --- a/examples/cli/cli-platform/src/lib.rs +++ b/examples/cli/cli-platform/src/lib.rs @@ -73,6 +73,41 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) { } } +#[cfg(unix)] +#[no_mangle] +pub unsafe extern "C" fn roc_getppid() -> libc::pid_t { + libc::getppid() +} + +#[cfg(unix)] +#[no_mangle] +pub unsafe extern "C" fn roc_mmap( + addr: *mut libc::c_void, + len: libc::size_t, + prot: libc::c_int, + flags: libc::c_int, + fd: libc::c_int, + offset: libc::off_t, +) -> *mut libc::c_void { + libc::mmap(addr, len, prot, flags, fd, offset) +} + +#[cfg(unix)] +#[no_mangle] +pub unsafe extern "C" fn roc_shm_open( + name: *const libc::c_char, + oflag: libc::c_int, + mode: libc::mode_t, +) -> libc::c_int { + libc::shm_open(name, oflag, mode) +} + +#[cfg(unix)] +#[no_mangle] +pub unsafe extern "C" fn roc_send_signal(pid: libc::pid_t, sig: libc::c_int) -> libc::c_int { + libc::kill(pid, sig) +} + fn print_backtrace() { eprintln!("Here is the call stack that led to the crash:\n"); diff --git a/examples/cli/effects-platform/host.zig b/examples/cli/effects-platform/host.zig index 8489b36425..afb4604076 100644 --- a/examples/cli/effects-platform/host.zig +++ b/examples/cli/effects-platform/host.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); const str = @import("str"); const RocStr = str.RocStr; const testing = std.testing; @@ -15,7 +16,6 @@ comptime { // -fcompiler-rt in link.rs instead of doing this. Note that this // workaround is present in many host.zig files, so make sure to undo // it everywhere! - const builtin = @import("builtin"); if (builtin.os.tag == .macos) { _ = @import("compiler_rt"); } @@ -85,6 +85,34 @@ export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { return memset(dst, value, size); } +extern fn kill(pid: c_int, sig: c_int) c_int; +extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int; +extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque; +extern fn getppid() c_int; + +fn roc_getppid() callconv(.C) c_int { + return getppid(); +} + +fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int { + return kill(pid, sig); +} +fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int { + return shm_open(name, oflag, mode); +} +fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque { + return mmap(addr, length, prot, flags, fd, offset); +} + +comptime { + if (builtin.os.tag == .macos or builtin.os.tag == .linux) { + @export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong }); + @export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong }); + @export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong }); + @export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong }); + } +} + const Unit = extern struct {}; pub export fn main() u8 { diff --git a/examples/cli/false-interpreter/platform/src/lib.rs b/examples/cli/false-interpreter/platform/src/lib.rs index 64e1a92c1a..1d0dfcf63a 100644 --- a/examples/cli/false-interpreter/platform/src/lib.rs +++ b/examples/cli/false-interpreter/platform/src/lib.rs @@ -72,6 +72,41 @@ pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut libc::memset(dst, c, n) } +#[cfg(unix)] +#[no_mangle] +pub unsafe extern "C" fn roc_getppid() -> libc::pid_t { + libc::getppid() +} + +#[cfg(unix)] +#[no_mangle] +pub unsafe extern "C" fn roc_mmap( + addr: *mut libc::c_void, + len: libc::size_t, + prot: libc::c_int, + flags: libc::c_int, + fd: libc::c_int, + offset: libc::off_t, +) -> *mut libc::c_void { + libc::mmap(addr, len, prot, flags, fd, offset) +} + +#[cfg(unix)] +#[no_mangle] +pub unsafe extern "C" fn roc_shm_open( + name: *const libc::c_char, + oflag: libc::c_int, + mode: libc::mode_t, +) -> libc::c_int { + libc::shm_open(name, oflag, mode) +} + +#[cfg(unix)] +#[no_mangle] +pub unsafe extern "C" fn roc_send_signal(pid: libc::pid_t, sig: libc::c_int) -> libc::c_int { + libc::kill(pid, sig) +} + #[no_mangle] pub extern "C" fn rust_main() -> i32 { let arg = env::args() diff --git a/examples/cli/tui-platform/host.zig b/examples/cli/tui-platform/host.zig index fea7652bc7..5120c4aaf7 100644 --- a/examples/cli/tui-platform/host.zig +++ b/examples/cli/tui-platform/host.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); const str = @import("str"); const RocStr = str.RocStr; const testing = std.testing; @@ -15,7 +16,6 @@ comptime { // -fcompiler-rt in link.rs instead of doing this. Note that this // workaround is present in many host.zig files, so make sure to undo // it everywhere! - const builtin = @import("builtin"); if (builtin.os.tag == .macos) { _ = @import("compiler_rt"); } @@ -134,6 +134,34 @@ export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { return memset(dst, value, size); } +extern fn kill(pid: c_int, sig: c_int) c_int; +extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int; +extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque; +extern fn getppid() c_int; + +fn roc_getppid() callconv(.C) c_int { + return getppid(); +} + +fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int { + return kill(pid, sig); +} +fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int { + return shm_open(name, oflag, mode); +} +fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque { + return mmap(addr, length, prot, flags, fd, offset); +} + +comptime { + if (builtin.os.tag == .macos or builtin.os.tag == .linux) { + @export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong }); + @export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong }); + @export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong }); + @export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong }); + } +} + const Unit = extern struct {}; pub export fn main() callconv(.C) u8 { diff --git a/examples/parser/platform/host.c b/examples/parser/platform/host.c index 56465121be..6bbd8c76c0 100644 --- a/examples/parser/platform/host.c +++ b/examples/parser/platform/host.c @@ -95,6 +95,11 @@ void *roc_memset(void *str, int c, size_t n) { return memset(str, c, n); } +int roc_send_signal(int pid, int sig) { return kill(pid, sig); } +int roc_shm_open(char* name, int oflag, int mode) { return shm_open(name, oflag, mode); } +void* roc_mmap(void* addr, int length, int prot, int flags, int fd, int offset) { return mmap(addr, length, prot, flags, fd, offset); } +int roc_getppid() { return getppid(); } + struct RocStr { char *bytes; size_t len; diff --git a/examples/ruby-interop/platform/host.c b/examples/ruby-interop/platform/host.c index 3f9a63c2a2..3141892345 100644 --- a/examples/ruby-interop/platform/host.c +++ b/examples/ruby-interop/platform/host.c @@ -27,6 +27,11 @@ void* roc_memcpy(void* dest, const void* src, size_t n) { void* roc_memset(void* str, int c, size_t n) { return memset(str, c, n); } +int roc_send_signal(int pid, int sig) { return kill(pid, sig); } +int roc_shm_open(char* name, int oflag, int mode) { return shm_open(name, oflag, mode); } +void* roc_mmap(void* addr, int length, int prot, int flags, int fd, int offset) { return mmap(addr, length, prot, flags, fd, offset); } +int roc_getppid() { return getppid(); } + struct RocStr { char* bytes; size_t len; diff --git a/examples/static-site-gen/platform/src/lib.rs b/examples/static-site-gen/platform/src/lib.rs index 40349b3a17..f3a36167c2 100644 --- a/examples/static-site-gen/platform/src/lib.rs +++ b/examples/static-site-gen/platform/src/lib.rs @@ -33,6 +33,41 @@ pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) { libc::free(c_ptr) } +#[cfg(unix)] +#[no_mangle] +pub unsafe extern "C" fn roc_getppid() -> libc::pid_t { + libc::getppid() +} + +#[cfg(unix)] +#[no_mangle] +pub unsafe extern "C" fn roc_mmap( + addr: *mut libc::c_void, + len: libc::size_t, + prot: libc::c_int, + flags: libc::c_int, + fd: libc::c_int, + offset: libc::off_t, +) -> *mut libc::c_void { + libc::mmap(addr, len, prot, flags, fd, offset) +} + +#[cfg(unix)] +#[no_mangle] +pub unsafe extern "C" fn roc_shm_open( + name: *const libc::c_char, + oflag: libc::c_int, + mode: libc::mode_t, +) -> libc::c_int { + libc::shm_open(name, oflag, mode) +} + +#[cfg(unix)] +#[no_mangle] +pub unsafe extern "C" fn roc_send_signal(pid: libc::pid_t, sig: libc::c_int) -> libc::c_int { + libc::kill(pid, sig) +} + #[no_mangle] pub extern "C" fn rust_main() -> i32 { let args: Vec = env::args().collect(); From 2b60cb1dcd1d610cb41368fab2a9412ebab3cc34 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 12 Oct 2022 21:06:44 +0200 Subject: [PATCH 12/31] include process id in expect buffer name --- crates/cli/src/lib.rs | 6 +++--- crates/compiler/builtins/bitcode/src/expect.zig | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 45913d1231..15f160dfe6 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -921,9 +921,9 @@ fn roc_dev_native( let mut signals = Signals::new(&[SIGCHLD, SIGUSR1]).unwrap(); - // let shm_name = format!("/roc_expect_buffer_{}", std::process::id()); - let shm_name = "/roc_expect_buffer"; - let memory = ExpectMemory::create_or_reuse_mmap(shm_name); + // let shm_name = + let shm_name = format!("/roc_expect_buffer_{}", std::process::id()); + let memory = ExpectMemory::create_or_reuse_mmap(&shm_name); let layout_interner = layout_interner.into_global(); diff --git a/crates/compiler/builtins/bitcode/src/expect.zig b/crates/compiler/builtins/bitcode/src/expect.zig index f6a7e19c73..69bb821332 100644 --- a/crates/compiler/builtins/bitcode/src/expect.zig +++ b/crates/compiler/builtins/bitcode/src/expect.zig @@ -30,8 +30,13 @@ extern fn roc_getppid() c_int; pub fn readSharedBufferEnv() callconv(.C) void { if (builtin.os.tag == .macos or builtin.os.tag == .linux) { - const name = "/roc_expect_buffer"; // IMPORTANT: shared memory object names must begin with / and contain no other slashes! - const shared_fd = roc_shm_open(@ptrCast(*const i8, name), O_RDWR | O_CREAT, 0o666); + // const name = "/roc_expect_buffer"; + + // IMPORTANT: shared memory object names must begin with / and contain no other slashes! + var name: [100]u8 = undefined; + _ = std.fmt.bufPrint(name[0..100], "/roc_expect_buffer_{}\x00", .{roc_getppid()}) catch unreachable; + + const shared_fd = roc_shm_open(@ptrCast(*const i8, &name), O_RDWR | O_CREAT, 0o666); const length = 4096; const shared_ptr = roc_mmap( From d9f9183009c126aa3599df9036d733ce1ec42fa3 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 12 Oct 2022 21:15:48 +0200 Subject: [PATCH 13/31] fix zig tests by providing new libc functions --- .../compiler/builtins/bitcode/src/utils.zig | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/crates/compiler/builtins/bitcode/src/utils.zig b/crates/compiler/builtins/bitcode/src/utils.zig index 818e531165..0ff07cdca7 100644 --- a/crates/compiler/builtins/bitcode/src/utils.zig +++ b/crates/compiler/builtins/bitcode/src/utils.zig @@ -22,6 +22,24 @@ extern fn roc_panic(c_ptr: *const anyopaque, tag_id: u32) callconv(.C) void; // should work just like libc memcpy (we can't assume libc is present) extern fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void; +extern fn kill(pid: c_int, sig: c_int) c_int; +extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int; +extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque; +extern fn getppid() c_int; + +fn testing_roc_getppid() callconv(.C) c_int { + return getppid(); +} +fn testing_roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int { + return kill(pid, sig); +} +fn testing_roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int { + return shm_open(name, oflag, mode); +} +fn testing_roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque { + return mmap(addr, length, prot, flags, fd, offset); +} + comptime { const builtin = @import("builtin"); // During tests, use the testing allocators to satisfy these functions. @@ -31,6 +49,13 @@ comptime { @export(testing_roc_dealloc, .{ .name = "roc_dealloc", .linkage = .Strong }); @export(testing_roc_panic, .{ .name = "roc_panic", .linkage = .Strong }); @export(testing_roc_memcpy, .{ .name = "roc_memcpy", .linkage = .Strong }); + + if (builtin.os.tag == .macos or builtin.os.tag == .linux) { + @export(testing_roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong }); + @export(testing_roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong }); + @export(testing_roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong }); + @export(testing_roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong }); + } } } From 3a9dbec14ba78585e2ebb5b513b2fc7db3ab6d65 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 12 Oct 2022 21:17:41 +0200 Subject: [PATCH 14/31] clippy --- crates/cli/src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 15f160dfe6..986b40c412 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -719,10 +719,6 @@ fn roc_run<'a, I: IntoIterator>( .strip_prefix(env::current_dir().unwrap()) .unwrap_or(path); - // No need to waste time freeing this memory, - // since the process is about to exit anyway. - std::mem::forget(arena); - #[cfg(target_family = "unix")] { use std::os::unix::ffi::OsStrExt; From faf20295108bfc9c132bf5d7371c575762d8c23b Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 16 Oct 2022 14:23:50 +0200 Subject: [PATCH 15/31] fix windows compilation problem --- crates/cli/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index a9255a7fde..124625bc28 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -1051,8 +1051,7 @@ fn roc_run_native, S: AsRef>( opt_level: OptLevel, _args: I, binary_bytes: &[u8], - _expectations: VecMap, - _interns: Interns, + _expect_metadata: ExpectMetadata, ) -> io::Result { use bumpalo::collections::CollectIn; @@ -1079,7 +1078,7 @@ fn roc_run_native, S: AsRef>( match opt_level { OptLevel::Development => { // roc_run_native_debug(executable, &argv, &envp, expectations, interns) - todo!() + internal_error!("running `expect`s does not currently work on windows") } OptLevel::Normal | OptLevel::Size | OptLevel::Optimize => { roc_run_native_fast(executable, &argv, &envp); From 7143abdedb9780620df1909f2145401582f6c5ac Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 15 Oct 2022 20:30:59 +0200 Subject: [PATCH 16/31] remove unused import on windows --- crates/linker/src/elf.rs | 2 +- crates/linker/src/macho.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/linker/src/elf.rs b/crates/linker/src/elf.rs index 1e5c517402..76e8e7d02d 100644 --- a/crates/linker/src/elf.rs +++ b/crates/linker/src/elf.rs @@ -10,7 +10,6 @@ use roc_collections::all::MutMap; use roc_error_macros::{internal_error, user_error}; use std::convert::TryFrom; use std::ffi::CStr; -use std::fs; use std::mem; use std::os::raw::c_char; use std::path::Path; @@ -1040,6 +1039,7 @@ pub(crate) fn surgery_elf( // Make sure the final executable has permision to execute. #[cfg(target_family = "unix")] { + use std::fs; use std::os::unix::fs::PermissionsExt; let mut perms = fs::metadata(executable_path) diff --git a/crates/linker/src/macho.rs b/crates/linker/src/macho.rs index f713d51dca..33e304120f 100644 --- a/crates/linker/src/macho.rs +++ b/crates/linker/src/macho.rs @@ -10,7 +10,6 @@ use object::{ use roc_collections::all::MutMap; use roc_error_macros::internal_error; use std::ffi::CStr; -use std::fs; use std::mem; use std::path::Path; use std::time::{Duration, Instant}; @@ -1189,6 +1188,7 @@ pub(crate) fn surgery_macho( // Make sure the final executable has permision to execute. #[cfg(target_family = "unix")] { + use std::fs; use std::os::unix::fs::PermissionsExt; let mut perms = fs::metadata(executable_path) From 13fcef3e2c8c2e8c79af34689670efacf740bc37 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 16 Oct 2022 14:28:54 +0200 Subject: [PATCH 17/31] one more windows fix --- crates/cli/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 124625bc28..a49f7c1062 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -1047,7 +1047,7 @@ fn roc_run_executable_file_path(binary_bytes: &[u8]) -> std::io::Result, S: AsRef>( - arena: Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it! + arena: &Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it! opt_level: OptLevel, _args: I, binary_bytes: &[u8], @@ -1060,20 +1060,20 @@ fn roc_run_native, S: AsRef>( // TODO forward the arguments // let (argv_cstrings, envp_cstrings) = make_argv_envp(&arena, &executable, args); - let argv_cstrings = bumpalo::vec![ in &arena; CString::default()]; - let envp_cstrings = bumpalo::vec![ in &arena; CString::default()]; + let argv_cstrings = bumpalo::vec![ in arena; CString::default()]; + let envp_cstrings = bumpalo::vec![ in arena; CString::default()]; let argv: bumpalo::collections::Vec<*const c_char> = argv_cstrings .iter() .map(|s| s.as_ptr()) .chain([std::ptr::null()]) - .collect_in(&arena); + .collect_in(arena); let envp: bumpalo::collections::Vec<*const c_char> = envp_cstrings .iter() .map(|s| s.as_ptr()) .chain([std::ptr::null()]) - .collect_in(&arena); + .collect_in(arena); match opt_level { OptLevel::Development => { From 82ca7fc8c5fd56ecfcaf342672c94c628aaa70f4 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 16 Oct 2022 15:56:24 +0200 Subject: [PATCH 18/31] an attempt to fix the repl tests --- crates/compiler/gen_llvm/src/llvm/externs.rs | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/crates/compiler/gen_llvm/src/llvm/externs.rs b/crates/compiler/gen_llvm/src/llvm/externs.rs index 1cb9298341..680e684653 100644 --- a/crates/compiler/gen_llvm/src/llvm/externs.rs +++ b/crates/compiler/gen_llvm/src/llvm/externs.rs @@ -155,10 +155,32 @@ pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) { } } + unreachable_function(env, "roc_getppid"); + unreachable_function(env, "roc_mmap"); + unreachable_function(env, "roc_send_signal"); + unreachable_function(env, "roc_shm_open"); + add_sjlj_roc_panic(env) } } +fn unreachable_function(env: &Env, name: &str) { + // The type of this function (but not the implementation) should have + // already been defined by the builtins, which rely on it. + let fn_val = env.module.get_function(name).unwrap(); + + // Add a basic block for the entry point + let entry = env.context.append_basic_block(fn_val, "entry"); + + env.builder.position_at_end(entry); + + env.builder.build_unreachable(); + + if cfg!(debug_assertions) { + crate::llvm::build::verify_fn(fn_val); + } +} + pub fn add_sjlj_roc_panic(env: &Env<'_, '_, '_>) { let ctx = env.context; let module = env.module; From 92afe5a46e8f6e147d779cc7c40c5aaa1d7a21cf Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 16 Oct 2022 17:02:23 +0200 Subject: [PATCH 19/31] add case to emit wasm via llvm --- crates/compiler/build/src/program.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/compiler/build/src/program.rs b/crates/compiler/build/src/program.rs index cb438f50b2..74a9564df0 100644 --- a/crates/compiler/build/src/program.rs +++ b/crates/compiler/build/src/program.rs @@ -211,7 +211,10 @@ pub fn gen_from_mono_module<'a>( CodeGenBackend::Llvm => { gen_from_mono_module_llvm(arena, loaded, roc_file_path, target, code_gen_options) } - CodeGenBackend::Wasm => unreachable!(), + CodeGenBackend::Wasm => { + // emit wasm via the llvm backend + gen_from_mono_module_llvm(arena, loaded, roc_file_path, target, code_gen_options) + } } } From f562d615058d1d902038b23c340ef007b0cd81b1 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 16 Oct 2022 18:32:55 +0200 Subject: [PATCH 20/31] int cast on macos --- .../platform-switching/rust-platform/src/lib.rs | 2 +- examples/cli/cli-platform/src/lib.rs | 4 ++-- examples/cli/false-interpreter/platform/src/lib.rs | 2 +- examples/static-site-gen/platform/src/lib.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/cli_testing_examples/platform-switching/rust-platform/src/lib.rs b/crates/cli_testing_examples/platform-switching/rust-platform/src/lib.rs index ce2bf4fc0a..7a423753f7 100644 --- a/crates/cli_testing_examples/platform-switching/rust-platform/src/lib.rs +++ b/crates/cli_testing_examples/platform-switching/rust-platform/src/lib.rs @@ -80,7 +80,7 @@ pub unsafe extern "C" fn roc_shm_open( oflag: libc::c_int, mode: libc::mode_t, ) -> libc::c_int { - libc::shm_open(name, oflag, mode) + libc::shm_open(name, oflag, mode as _) } #[cfg(unix)] diff --git a/examples/cli/cli-platform/src/lib.rs b/examples/cli/cli-platform/src/lib.rs index d8b8787d2f..81d9666732 100644 --- a/examples/cli/cli-platform/src/lib.rs +++ b/examples/cli/cli-platform/src/lib.rs @@ -99,7 +99,7 @@ pub unsafe extern "C" fn roc_shm_open( oflag: libc::c_int, mode: libc::mode_t, ) -> libc::c_int { - libc::shm_open(name, oflag, mode) + libc::shm_open(name, oflag, mode as _) } #[cfg(unix)] @@ -298,7 +298,7 @@ pub extern "C" fn roc_fx_exePath(roc_str: &RocStr) -> RocResult, ()> #[no_mangle] pub extern "C" fn roc_fx_stdinLine() -> RocStr { - use std::io::{self, BufRead}; + use std::io::BufRead; let stdin = io::stdin(); let line1 = stdin.lock().lines().next().unwrap().unwrap(); diff --git a/examples/cli/false-interpreter/platform/src/lib.rs b/examples/cli/false-interpreter/platform/src/lib.rs index 1d0dfcf63a..b0fa94a555 100644 --- a/examples/cli/false-interpreter/platform/src/lib.rs +++ b/examples/cli/false-interpreter/platform/src/lib.rs @@ -98,7 +98,7 @@ pub unsafe extern "C" fn roc_shm_open( oflag: libc::c_int, mode: libc::mode_t, ) -> libc::c_int { - libc::shm_open(name, oflag, mode) + libc::shm_open(name, oflag, mode as _) } #[cfg(unix)] diff --git a/examples/static-site-gen/platform/src/lib.rs b/examples/static-site-gen/platform/src/lib.rs index f3a36167c2..383ae6ce20 100644 --- a/examples/static-site-gen/platform/src/lib.rs +++ b/examples/static-site-gen/platform/src/lib.rs @@ -59,7 +59,7 @@ pub unsafe extern "C" fn roc_shm_open( oflag: libc::c_int, mode: libc::mode_t, ) -> libc::c_int { - libc::shm_open(name, oflag, mode) + libc::shm_open(name, oflag, mode as _) } #[cfg(unix)] From 54f653865fef64eb59bbf97482a81b60bdc028c1 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 16 Oct 2022 19:00:19 +0200 Subject: [PATCH 21/31] just only check modified files until we have an approach --- .github/workflows/markdown_link_check.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/markdown_link_check.yml b/.github/workflows/markdown_link_check.yml index 70568ae433..2e72934d3b 100644 --- a/.github/workflows/markdown_link_check.yml +++ b/.github/workflows/markdown_link_check.yml @@ -15,4 +15,5 @@ jobs: with: use-quiet-mode: 'yes' use-verbose-mode: 'yes' + check-modified-files-only: 'yes' base-branch: 'main' From bb1693556b7166a975fee795aff04f68a681085a Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sun, 16 Oct 2022 18:36:22 -0400 Subject: [PATCH 22/31] Drop some unnecessary imports --- examples/cli/false-interpreter/platform/src/lib.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/cli/false-interpreter/platform/src/lib.rs b/examples/cli/false-interpreter/platform/src/lib.rs index b0fa94a555..d1b1b2db34 100644 --- a/examples/cli/false-interpreter/platform/src/lib.rs +++ b/examples/cli/false-interpreter/platform/src/lib.rs @@ -2,7 +2,7 @@ use core::alloc::Layout; use core::ffi::c_void; -use core::mem::{ManuallyDrop, MaybeUninit}; +use core::mem::MaybeUninit; use libc; use roc_std::{RocList, RocStr}; use std::env; @@ -157,9 +157,7 @@ unsafe fn call_the_closure(closure_data_ptr: *const u8) -> i64 { #[no_mangle] pub extern "C" fn roc_fx_getLine() -> RocStr { - use std::io::{self, BufRead}; - - let stdin = io::stdin(); + let stdin = std::io::stdin(); let line1 = stdin.lock().lines().next().unwrap().unwrap(); RocStr::from(line1.as_str()) @@ -167,11 +165,10 @@ pub extern "C" fn roc_fx_getLine() -> RocStr { #[no_mangle] pub extern "C" fn roc_fx_getChar() -> u8 { - use std::io::{self, BufRead}; let mut buffer = [0]; - if let Err(ioerr) = io::stdin().lock().read_exact(&mut buffer[..]) { - if ioerr.kind() == io::ErrorKind::UnexpectedEof { + if let Err(ioerr) = std::io::stdin().lock().read_exact(&mut buffer[..]) { + if ioerr.kind() == std::io::ErrorKind::UnexpectedEof { u8::MAX } else { panic!("Got an unexpected error while reading char from stdin"); From e6fd0c62fd0515c84d78747ee413fc47f177b4df Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sun, 16 Oct 2022 18:43:03 -0400 Subject: [PATCH 23/31] Do an explicit cast that doesn't make sense I don't know why this is what the compiler wants, given that the docs say it shouldn't need a cast, but this is for some reason what the compiler wants. --- .../platform-switching/rust-platform/src/lib.rs | 2 +- examples/cli/cli-platform/src/lib.rs | 2 +- examples/cli/false-interpreter/platform/src/lib.rs | 2 +- examples/static-site-gen/platform/src/lib.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/cli_testing_examples/platform-switching/rust-platform/src/lib.rs b/crates/cli_testing_examples/platform-switching/rust-platform/src/lib.rs index 7a423753f7..9a1bfa3dec 100644 --- a/crates/cli_testing_examples/platform-switching/rust-platform/src/lib.rs +++ b/crates/cli_testing_examples/platform-switching/rust-platform/src/lib.rs @@ -80,7 +80,7 @@ pub unsafe extern "C" fn roc_shm_open( oflag: libc::c_int, mode: libc::mode_t, ) -> libc::c_int { - libc::shm_open(name, oflag, mode as _) + libc::shm_open(name, oflag, mode as libc::c_uint) } #[cfg(unix)] diff --git a/examples/cli/cli-platform/src/lib.rs b/examples/cli/cli-platform/src/lib.rs index 81d9666732..905783c787 100644 --- a/examples/cli/cli-platform/src/lib.rs +++ b/examples/cli/cli-platform/src/lib.rs @@ -99,7 +99,7 @@ pub unsafe extern "C" fn roc_shm_open( oflag: libc::c_int, mode: libc::mode_t, ) -> libc::c_int { - libc::shm_open(name, oflag, mode as _) + libc::shm_open(name, oflag, mode as libc::c_uint) } #[cfg(unix)] diff --git a/examples/cli/false-interpreter/platform/src/lib.rs b/examples/cli/false-interpreter/platform/src/lib.rs index d1b1b2db34..9087b87646 100644 --- a/examples/cli/false-interpreter/platform/src/lib.rs +++ b/examples/cli/false-interpreter/platform/src/lib.rs @@ -98,7 +98,7 @@ pub unsafe extern "C" fn roc_shm_open( oflag: libc::c_int, mode: libc::mode_t, ) -> libc::c_int { - libc::shm_open(name, oflag, mode as _) + libc::shm_open(name, oflag, mode as libc::c_uint) } #[cfg(unix)] diff --git a/examples/static-site-gen/platform/src/lib.rs b/examples/static-site-gen/platform/src/lib.rs index 383ae6ce20..5c5d11c088 100644 --- a/examples/static-site-gen/platform/src/lib.rs +++ b/examples/static-site-gen/platform/src/lib.rs @@ -59,7 +59,7 @@ pub unsafe extern "C" fn roc_shm_open( oflag: libc::c_int, mode: libc::mode_t, ) -> libc::c_int { - libc::shm_open(name, oflag, mode as _) + libc::shm_open(name, oflag, mode as libc::c_uint) } #[cfg(unix)] From c5cdab1ff9bd7305d4a0d4a2569224ad302e9733 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Mon, 17 Oct 2022 09:45:34 -0500 Subject: [PATCH 24/31] Only ftruncate shm_open'd file once, and add some error messages --- Cargo.lock | 1 + crates/repl_expect/Cargo.toml | 1 + crates/repl_expect/src/run.rs | 24 ++++++++++++++++++++---- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f6481b6d2a..17b8113323 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3961,6 +3961,7 @@ dependencies = [ "roc_build", "roc_builtins", "roc_collections", + "roc_error_macros", "roc_gen_llvm", "roc_intern", "roc_load", diff --git a/crates/repl_expect/Cargo.toml b/crates/repl_expect/Cargo.toml index e819722eab..942279e7f7 100644 --- a/crates/repl_expect/Cargo.toml +++ b/crates/repl_expect/Cargo.toml @@ -24,6 +24,7 @@ roc_types = {path = "../compiler/types"} roc_gen_llvm = {path = "../compiler/gen_llvm"} roc_region = { path = "../compiler/region" } roc_build = { path = "../compiler/build" } +roc_error_macros = { path = "../error_macros" } libloading = "0.7.1" inkwell = { path = "../vendor/inkwell" } diff --git a/crates/repl_expect/src/run.rs b/crates/repl_expect/src/run.rs index fb1615b5cd..6da92e74a0 100644 --- a/crates/repl_expect/src/run.rs +++ b/crates/repl_expect/src/run.rs @@ -5,6 +5,7 @@ use bumpalo::Bump; use inkwell::context::Context; use roc_build::link::llvm_module_to_dylib; use roc_collections::{MutSet, VecMap}; +use roc_error_macros::internal_error; use roc_gen_llvm::{ llvm::{build::LlvmBackendMode, externs::add_default_roc_externs}, run_roc::RocCallResult, @@ -52,18 +53,33 @@ impl<'a> ExpectMemory<'a> { fn mmap_help(cstring: std::ffi::CString, shm_flags: i32) -> Self { let ptr = unsafe { let shared_fd = libc::shm_open(cstring.as_ptr().cast(), shm_flags, 0o666); + if shared_fd == -1 { + internal_error!("failed to shm_open fd"); + } - libc::ftruncate(shared_fd, 0); - libc::ftruncate(shared_fd, Self::SHM_SIZE as _); + let mut stat: libc::stat = std::mem::zeroed(); + if libc::fstat(shared_fd, &mut stat) == -1 { + internal_error!("failed to stat shared file, does it exist?"); + } + if stat.st_size < Self::SHM_SIZE as _ { + if libc::ftruncate(shared_fd, Self::SHM_SIZE as _) == -1 { + internal_error!("failed to truncate shared file, are the permissions wrong?"); + } + } - libc::mmap( + let ptr = libc::mmap( std::ptr::null_mut(), Self::SHM_SIZE, libc::PROT_WRITE | libc::PROT_READ, libc::MAP_SHARED, shared_fd, 0, - ) + ); + if ptr as usize == usize::MAX { + // ptr = -1 + roc_error_macros::internal_error!("failed to mmap shared pointer") + } + ptr }; // puts in the initial header From 84178c66e1a026cb431193935f66877e8e90226e Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 18 Oct 2022 00:20:01 +0200 Subject: [PATCH 25/31] move around some libc code --- crates/repl_expect/src/run.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/repl_expect/src/run.rs b/crates/repl_expect/src/run.rs index 6da92e74a0..a2a1289f23 100644 --- a/crates/repl_expect/src/run.rs +++ b/crates/repl_expect/src/run.rs @@ -57,14 +57,9 @@ impl<'a> ExpectMemory<'a> { internal_error!("failed to shm_open fd"); } - let mut stat: libc::stat = std::mem::zeroed(); - if libc::fstat(shared_fd, &mut stat) == -1 { - internal_error!("failed to stat shared file, does it exist?"); - } - if stat.st_size < Self::SHM_SIZE as _ { - if libc::ftruncate(shared_fd, Self::SHM_SIZE as _) == -1 { - internal_error!("failed to truncate shared file, are the permissions wrong?"); - } + // NOTE: we can only call `ftruncate` once on this file descriptor on mac + if libc::ftruncate(shared_fd, Self::SHM_SIZE as _) == -1 { + internal_error!("failed to truncate shared file, are the permissions wrong?"); } let ptr = libc::mmap( @@ -75,10 +70,15 @@ impl<'a> ExpectMemory<'a> { shared_fd, 0, ); + if ptr as usize == usize::MAX { // ptr = -1 roc_error_macros::internal_error!("failed to mmap shared pointer") } + + // fill the buffer with a fill pattern + libc::memset(ptr, 0xAA, Self::SHM_SIZE); + ptr }; From 185795a8eec0f6748e416e83cba77342b7ff1493 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Mon, 17 Oct 2022 17:28:54 -0500 Subject: [PATCH 26/31] Add symbols' localized type variable to the AST --- crates/compiler/can/src/builtins.rs | 38 ++--- crates/compiler/can/src/copy.rs | 4 +- crates/compiler/can/src/effect_module.rs | 86 ++++++---- crates/compiler/can/src/expr.rs | 154 ++++++++++-------- crates/compiler/can/src/module.rs | 2 +- crates/compiler/constrain/src/expr.rs | 12 +- crates/compiler/derive/src/decoding.rs | 36 ++-- crates/compiler/derive/src/encoding.rs | 18 +- crates/compiler/derive/src/hash.rs | 22 ++- crates/compiler/mono/src/ir.rs | 23 +-- .../compiler/test_derive/src/pretty_print.rs | 2 +- 11 files changed, 230 insertions(+), 167 deletions(-) diff --git a/crates/compiler/can/src/builtins.rs b/crates/compiler/can/src/builtins.rs index 81b222553c..d847eb1fe0 100644 --- a/crates/compiler/can/src/builtins.rs +++ b/crates/compiler/can/src/builtins.rs @@ -247,7 +247,7 @@ fn lowlevel_1(symbol: Symbol, op: LowLevel, var_store: &mut VarStore) -> Def { let body = RunLowLevel { op, - args: vec![(arg1_var, Var(Symbol::ARG_1))], + args: vec![(arg1_var, Var(Symbol::ARG_1, arg1_var))], ret_var, }; @@ -268,8 +268,8 @@ fn lowlevel_2(symbol: Symbol, op: LowLevel, var_store: &mut VarStore) -> Def { let body = RunLowLevel { op, args: vec![ - (arg1_var, Var(Symbol::ARG_1)), - (arg2_var, Var(Symbol::ARG_2)), + (arg1_var, Var(Symbol::ARG_1, arg1_var)), + (arg2_var, Var(Symbol::ARG_2, arg2_var)), ], ret_var, }; @@ -292,9 +292,9 @@ fn lowlevel_3(symbol: Symbol, op: LowLevel, var_store: &mut VarStore) -> Def { let body = RunLowLevel { op, args: vec![ - (arg1_var, Var(Symbol::ARG_1)), - (arg2_var, Var(Symbol::ARG_2)), - (arg3_var, Var(Symbol::ARG_3)), + (arg1_var, Var(Symbol::ARG_1, arg1_var)), + (arg2_var, Var(Symbol::ARG_2, arg2_var)), + (arg3_var, Var(Symbol::ARG_3, arg3_var)), ], ret_var, }; @@ -322,10 +322,10 @@ fn lowlevel_4(symbol: Symbol, op: LowLevel, var_store: &mut VarStore) -> Def { let body = RunLowLevel { op, args: vec![ - (arg1_var, Var(Symbol::ARG_1)), - (arg2_var, Var(Symbol::ARG_2)), - (arg3_var, Var(Symbol::ARG_3)), - (arg4_var, Var(Symbol::ARG_4)), + (arg1_var, Var(Symbol::ARG_1, arg1_var)), + (arg2_var, Var(Symbol::ARG_2, arg2_var)), + (arg3_var, Var(Symbol::ARG_3, arg3_var)), + (arg4_var, Var(Symbol::ARG_4, arg4_var)), ], ret_var, }; @@ -355,11 +355,11 @@ fn lowlevel_5(symbol: Symbol, op: LowLevel, var_store: &mut VarStore) -> Def { let body = RunLowLevel { op, args: vec![ - (arg1_var, Var(Symbol::ARG_1)), - (arg2_var, Var(Symbol::ARG_2)), - (arg3_var, Var(Symbol::ARG_3)), - (arg4_var, Var(Symbol::ARG_4)), - (arg5_var, Var(Symbol::ARG_5)), + (arg1_var, Var(Symbol::ARG_1, arg1_var)), + (arg2_var, Var(Symbol::ARG_2, arg2_var)), + (arg3_var, Var(Symbol::ARG_3, arg3_var)), + (arg4_var, Var(Symbol::ARG_4, arg4_var)), + (arg5_var, Var(Symbol::ARG_5, arg5_var)), ], ret_var, }; @@ -486,7 +486,7 @@ fn to_num_checked(symbol: Symbol, var_store: &mut VarStore, lowlevel: LowLevel) ext_var: var_store.fresh(), field: "b".into(), field_var: var_store.fresh(), - loc_expr: Box::new(no_region(Var(Symbol::ARG_2))), + loc_expr: Box::new(no_region(Var(Symbol::ARG_2, var_store.fresh()))), }, ), // out of bounds! @@ -509,7 +509,7 @@ fn to_num_checked(symbol: Symbol, var_store: &mut VarStore, lowlevel: LowLevel) ext_var: var_store.fresh(), field: "a".into(), field_var: num_var_2, - loc_expr: Box::new(no_region(Var(Symbol::ARG_2))), + loc_expr: Box::new(no_region(Var(Symbol::ARG_2, var_store.fresh()))), }, ], var_store, @@ -523,7 +523,7 @@ fn to_num_checked(symbol: Symbol, var_store: &mut VarStore, lowlevel: LowLevel) loc_pattern: no_region(Pattern::Identifier(Symbol::ARG_2)), loc_expr: no_region(RunLowLevel { op: lowlevel, - args: vec![(num_var_1, Var(Symbol::ARG_1))], + args: vec![(num_var_1, Var(Symbol::ARG_1, var_store.fresh()))], ret_var: record_var, }), expr_var: record_var, @@ -549,7 +549,7 @@ fn to_num_is_zero(symbol: Symbol, var_store: &mut VarStore) -> Def { let body = Expr::RunLowLevel { op: LowLevel::Eq, args: vec![ - (num_var, Var(Symbol::ARG_1)), + (num_var, Var(Symbol::ARG_1, num_var)), ( num_var, Num( diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index 787e3a1741..3f5ed43b4c 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -255,9 +255,9 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr elem_var: sub!(*elem_var), loc_elems: loc_elems.iter().map(|le| le.map(|e| go_help!(e))).collect(), }, - Var(sym) => Var(*sym), + Var(sym, var) => Var(*sym, sub!(*var)), &AbilityMember(sym, specialization, specialization_var) => { - AbilityMember(sym, specialization, specialization_var) + AbilityMember(sym, specialization, sub!(specialization_var)) } When { loc_cond, diff --git a/crates/compiler/can/src/effect_module.rs b/crates/compiler/can/src/effect_module.rs index b67fb03a21..dcc21b70fd 100644 --- a/crates/compiler/can/src/effect_module.rs +++ b/crates/compiler/can/src/effect_module.rs @@ -124,14 +124,15 @@ fn build_effect_always( Loc::at_zero(empty_record_pattern(var_store)), )]; - let body = Expr::Var(value_symbol); + let value_var = var_store.fresh(); + let body = Expr::Var(value_symbol, value_var); Expr::Closure(ClosureData { function_type: var_store.fresh(), closure_type: var_store.fresh(), return_type: var_store.fresh(), name: inner_closure_symbol, - captured_symbols: vec![(value_symbol, var_store.fresh())], + captured_symbols: vec![(value_symbol, value_var)], recursive: Recursive::NotRecursive, arguments, loc_body: Box::new(Loc::at_zero(body)), @@ -231,20 +232,22 @@ fn build_effect_map( .introduce("effect_map_thunk".into(), Region::zero()) .unwrap() }; + let thunk_var = var_store.fresh(); let mapper_symbol = { scope .introduce("effect_map_mapper".into(), Region::zero()) .unwrap() }; + let mapper_var = var_store.fresh(); let map_symbol = { scope.introduce("map".into(), Region::zero()).unwrap() }; // `thunk {}` let force_thunk_call = { let boxed = ( - var_store.fresh(), - Loc::at_zero(Expr::Var(thunk_symbol)), + thunk_var, + Loc::at_zero(Expr::Var(thunk_symbol, thunk_var)), var_store.fresh(), var_store.fresh(), ); @@ -256,8 +259,8 @@ fn build_effect_map( // `toEffect (thunk {})` let mapper_call = { let boxed = ( - var_store.fresh(), - Loc::at_zero(Expr::Var(mapper_symbol)), + mapper_var, + Loc::at_zero(Expr::Var(mapper_symbol, mapper_var)), var_store.fresh(), var_store.fresh(), ); @@ -411,9 +414,9 @@ fn build_effect_map( (map_symbol, def) } -fn force_thunk(expr: Expr, var_store: &mut VarStore) -> Expr { +fn force_thunk(expr: Expr, thunk_var: Variable, var_store: &mut VarStore) -> Expr { let boxed = ( - var_store.fresh(), + thunk_var, Loc::at_zero(expr), var_store.fresh(), var_store.fresh(), @@ -441,13 +444,19 @@ fn build_effect_after( let outer_closure_symbol = new_symbol!(scope, "effect_after_inner"); // `effect {}` - let force_effect_call = force_thunk(Expr::Var(effect_symbol), var_store); + let force_effect_var = var_store.fresh(); + let force_effect_call = force_thunk( + Expr::Var(effect_symbol, force_effect_var), + force_effect_var, + var_store, + ); // `toEffect (effect {})` + let to_effect_var = var_store.fresh(); let to_effect_call = { let boxed = ( - var_store.fresh(), - Loc::at_zero(Expr::Var(to_effect_symbol)), + to_effect_var, + Loc::at_zero(Expr::Var(to_effect_symbol, to_effect_var)), var_store.fresh(), var_store.fresh(), ); @@ -459,7 +468,12 @@ fn build_effect_after( // let @Effect thunk = toEffect (effect {}) in thunk {} let let_effect_thunk = { // `thunk {}` - let force_inner_thunk_call = force_thunk(Expr::Var(thunk_symbol), var_store); + let force_inner_thunk_var = var_store.fresh(); + let force_inner_thunk_call = force_thunk( + Expr::Var(thunk_symbol, force_inner_thunk_var), + force_inner_thunk_var, + var_store, + ); let (specialized_def_type, type_arguments, lambda_set_variables) = build_fresh_opaque_variables(var_store); @@ -702,9 +716,10 @@ fn force_effect( let ret_var = var_store.fresh(); let force_thunk_call = { + let thunk_var = var_store.fresh(); let boxed = ( - var_store.fresh(), - Loc::at_zero(Expr::Var(thunk_symbol)), + thunk_var, + Loc::at_zero(Expr::Var(thunk_symbol, thunk_var)), var_store.fresh(), ret_var, ); @@ -884,6 +899,7 @@ fn build_effect_forever_inner_body( effect: Symbol, var_store: &mut VarStore, ) -> Expr { + let thunk1_var = var_store.fresh(); let thunk1_symbol = { scope.introduce("thunk1".into(), Region::zero()).unwrap() }; let thunk2_symbol = { scope.introduce("thunk2".into(), Region::zero()).unwrap() }; @@ -909,7 +925,7 @@ fn build_effect_forever_inner_body( Def { loc_pattern: Loc::at_zero(pattern), - loc_expr: Loc::at_zero(Expr::Var(effect)), + loc_expr: Loc::at_zero(Expr::Var(effect, var_store.fresh())), expr_var: var_store.fresh(), pattern_vars, annotation: None, @@ -920,8 +936,8 @@ fn build_effect_forever_inner_body( let force_thunk_call = { let ret_var = var_store.fresh(); let boxed = ( - var_store.fresh(), - Loc::at_zero(Expr::Var(thunk1_symbol)), + thunk1_var, + Loc::at_zero(Expr::Var(thunk1_symbol, thunk1_var)), var_store.fresh(), ret_var, ); @@ -945,12 +961,13 @@ fn build_effect_forever_inner_body( let forever_effect = { let boxed = ( var_store.fresh(), - Loc::at_zero(Expr::Var(forever_symbol)), + Loc::at_zero(Expr::Var(forever_symbol, var_store.fresh())), var_store.fresh(), var_store.fresh(), ); - let arguments = vec![(var_store.fresh(), Loc::at_zero(Expr::Var(effect)))]; + let effect_var = var_store.fresh(); + let arguments = vec![(effect_var, Loc::at_zero(Expr::Var(effect, effect_var)))]; Expr::Call(Box::new(boxed), arguments, CalledVia::Space) }; @@ -1198,14 +1215,16 @@ fn build_effect_loop_inner_body( // `step state` let rhs = { + let step_var = var_store.fresh(); let boxed = ( - var_store.fresh(), - Loc::at_zero(Expr::Var(step_symbol)), + step_var, + Loc::at_zero(Expr::Var(step_symbol, step_var)), var_store.fresh(), var_store.fresh(), ); - let arguments = vec![(var_store.fresh(), Loc::at_zero(Expr::Var(state_symbol)))]; + let state_var = var_store.fresh(); + let arguments = vec![(state_var, Loc::at_zero(Expr::Var(state_symbol, state_var)))]; Expr::Call(Box::new(boxed), arguments, CalledVia::Space) }; @@ -1220,10 +1239,11 @@ fn build_effect_loop_inner_body( // thunk1 {} let force_thunk_call = { + let thunk1_var = var_store.fresh(); let ret_var = var_store.fresh(); let boxed = ( - var_store.fresh(), - Loc::at_zero(Expr::Var(thunk1_symbol)), + thunk1_var, + Loc::at_zero(Expr::Var(thunk1_symbol, thunk1_var)), var_store.fresh(), ret_var, ); @@ -1236,16 +1256,22 @@ fn build_effect_loop_inner_body( // recursive call `loop newState step` let loop_new_state_step = { + let loop_var = var_store.fresh(); let boxed = ( - var_store.fresh(), - Loc::at_zero(Expr::Var(loop_symbol)), + loop_var, + Loc::at_zero(Expr::Var(loop_symbol, loop_var)), var_store.fresh(), var_store.fresh(), ); + let new_state_var = var_store.fresh(); + let step_var = var_store.fresh(); let arguments = vec![ - (var_store.fresh(), Loc::at_zero(Expr::Var(new_state_symbol))), - (var_store.fresh(), Loc::at_zero(Expr::Var(step_symbol))), + ( + new_state_var, + Loc::at_zero(Expr::Var(new_state_symbol, new_state_var)), + ), + (step_var, Loc::at_zero(Expr::Var(step_symbol, step_var))), ]; Expr::Call(Box::new(boxed), arguments, CalledVia::Space) }; @@ -1283,7 +1309,7 @@ fn build_effect_loop_inner_body( crate::expr::WhenBranch { patterns: vec![done_pattern], - value: Loc::at_zero(Expr::Var(done_symbol)), + value: Loc::at_zero(Expr::Var(done_symbol, var_store.fresh())), guard: None, redundant: RedundantMark::new(var_store), } @@ -1351,7 +1377,7 @@ pub fn build_host_exposed_def( )); captured_symbols.push((arg_symbol, arg_var)); - linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol))); + linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol, arg_var))); } let foreign_symbol_name = format!("roc_fx_{}", ident); diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 1483e190db..c2befe9a1b 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -100,7 +100,7 @@ pub enum Expr { }, // Lookups - Var(Symbol), + Var(Symbol, Variable), AbilityMember( /// Actual member name Symbol, @@ -256,7 +256,7 @@ impl Expr { Self::Str(..) => Category::Str, Self::SingleQuote(..) => Category::Character, Self::List { .. } => Category::List, - &Self::Var(sym) => Category::Lookup(sym), + &Self::Var(sym, _) => Category::Lookup(sym), &Self::AbilityMember(sym, _, _) => Category::Lookup(sym), Self::When { .. } => Category::When, Self::If { .. } => Category::If, @@ -372,7 +372,7 @@ impl AccessorData { record_var, ext_var, field_var, - loc_expr: Box::new(Loc::at_zero(Expr::Var(record_symbol))), + loc_expr: Box::new(Loc::at_zero(Expr::Var(record_symbol, record_var))), field, }; @@ -440,7 +440,10 @@ impl OpaqueWrapFunctionData { let body = Expr::OpaqueRef { opaque_var, name: opaque_name, - argument: Box::new((argument_var, Loc::at_zero(Expr::Var(argument_symbol)))), + argument: Box::new(( + argument_var, + Loc::at_zero(Expr::Var(argument_symbol, argument_var)), + )), specialized_def_type: Box::new(specialized_def_type), type_arguments, lambda_set_variables, @@ -592,7 +595,7 @@ pub fn canonicalize_expr<'a>( } => { let (can_update, update_out) = canonicalize_expr(env, var_store, scope, loc_update.region, &loc_update.value); - if let Var(symbol) = &can_update.value { + if let Var(symbol, _) = &can_update.value { match canonicalize_fields(env, var_store, scope, region, fields.items) { Ok((can_fields, mut output)) => { output.references.union_mut(&update_out.references); @@ -765,7 +768,7 @@ pub fn canonicalize_expr<'a>( output.tail_call = None; let expr = match fn_expr.value { - Var(symbol) => { + Var(symbol, _) => { output.references.insert_call(symbol); // we're tail-calling a symbol by name, check if it's the tail-callable symbol @@ -994,7 +997,7 @@ pub fn canonicalize_expr<'a>( // Get all the lookups that were referenced in the condition, // so we can print their values later. - let lookups_in_cond = get_lookup_symbols(&loc_condition.value, var_store); + let lookups_in_cond = get_lookup_symbols(&loc_condition.value); let (loc_continuation, output2) = canonicalize_expr( env, @@ -1600,7 +1603,7 @@ fn canonicalize_var_lookup( var_store.fresh(), ) } else { - Var(symbol) + Var(symbol, var_store.fresh()) } } Err(problem) => { @@ -1623,7 +1626,7 @@ fn canonicalize_var_lookup( var_store.fresh(), ) } else { - Var(symbol) + Var(symbol, var_store.fresh()) } } Err(problem) => { @@ -1657,7 +1660,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> | other @ EmptyRecord | other @ Accessor { .. } | other @ Update { .. } - | other @ Var(_) + | other @ Var(..) | other @ AbilityMember(..) | other @ RunLowLevel { .. } | other @ TypedHole { .. } @@ -1960,67 +1963,71 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> let (fn_var, loc_expr, closure_var, expr_var) = *boxed_tuple; match loc_expr.value { - Var(symbol) if symbol.is_builtin() => match builtin_defs_map(symbol, var_store) { - Some(Def { - loc_expr: - Loc { - value: - Closure(ClosureData { - recursive, - arguments: params, - loc_body: boxed_body, - .. - }), - .. - }, - .. - }) => { - debug_assert_eq!(recursive, Recursive::NotRecursive); + Var(symbol, _) if symbol.is_builtin() => { + match builtin_defs_map(symbol, var_store) { + Some(Def { + loc_expr: + Loc { + value: + Closure(ClosureData { + recursive, + arguments: params, + loc_body: boxed_body, + .. + }), + .. + }, + .. + }) => { + debug_assert_eq!(recursive, Recursive::NotRecursive); - // Since this is a canonicalized Expr, we should have - // already detected any arity mismatches and replaced this - // with a RuntimeError if there was a mismatch. - debug_assert_eq!(params.len(), args.len()); + // Since this is a canonicalized Expr, we should have + // already detected any arity mismatches and replaced this + // with a RuntimeError if there was a mismatch. + debug_assert_eq!(params.len(), args.len()); - // Start with the function's body as the answer. - let mut loc_answer = *boxed_body; + // Start with the function's body as the answer. + let mut loc_answer = *boxed_body; - // Wrap the body in one LetNonRec for each argument, - // such that at the end we have all the arguments in - // scope with the values the caller provided. - for ((_param_var, _exhaustive_mark, loc_pattern), (expr_var, loc_expr)) in - params.iter().cloned().zip(args.into_iter()).rev() - { - // TODO get the correct vars into here. - // Not sure if param_var should be involved. - let pattern_vars = SendMap::default(); + // Wrap the body in one LetNonRec for each argument, + // such that at the end we have all the arguments in + // scope with the values the caller provided. + for ( + (_param_var, _exhaustive_mark, loc_pattern), + (expr_var, loc_expr), + ) in params.iter().cloned().zip(args.into_iter()).rev() + { + // TODO get the correct vars into here. + // Not sure if param_var should be involved. + let pattern_vars = SendMap::default(); - let def = Def { - loc_pattern, - loc_expr, - expr_var, - pattern_vars, - annotation: None, - }; + let def = Def { + loc_pattern, + loc_expr, + expr_var, + pattern_vars, + annotation: None, + }; - loc_answer = Loc { - region: Region::zero(), - value: LetNonRec(Box::new(def), Box::new(loc_answer)), - }; + loc_answer = Loc { + region: Region::zero(), + value: LetNonRec(Box::new(def), Box::new(loc_answer)), + }; + } + + loc_answer.value + } + Some(_) => { + unreachable!("Tried to inline a non-function"); + } + None => { + unreachable!( + "Tried to inline a builtin that wasn't registered: {:?}", + symbol + ); } - - loc_answer.value } - Some(_) => { - unreachable!("Tried to inline a non-function"); - } - None => { - unreachable!( - "Tried to inline a builtin that wasn't registered: {:?}", - symbol - ); - } - }, + } _ => { // For now, we only inline calls to builtins. Leave this alone! Call( @@ -2172,7 +2179,10 @@ fn desugar_str_segments(var_store: &mut VarStore, segments: Vec) -> Interpolation(loc_interpolated_expr) => loc_interpolated_expr, }; - let fn_expr = Loc::at(Region::zero(), Expr::Var(Symbol::STR_CONCAT)); + let fn_expr = Loc::at( + Region::zero(), + Expr::Var(Symbol::STR_CONCAT, var_store.fresh()), + ); let expr = Expr::Call( Box::new(( var_store.fresh(), @@ -2615,16 +2625,22 @@ pub struct DestructureDef { pub pattern_vars: VecMap, } -fn get_lookup_symbols(expr: &Expr, var_store: &mut VarStore) -> Vec<(Symbol, Variable)> { +fn get_lookup_symbols(expr: &Expr) -> Vec<(Symbol, Variable)> { let mut stack: Vec<&Expr> = vec![expr]; let mut symbols = Vec::new(); while let Some(expr) = stack.pop() { match expr { - Expr::Var(symbol) | Expr::Update { symbol, .. } | Expr::AbilityMember(symbol, _, _) => { + Expr::Var(symbol, var) + | Expr::Update { + symbol, + record_var: var, + .. + } + | Expr::AbilityMember(symbol, _, var) => { // Don't introduce duplicates, or make unused variables if !symbols.iter().any(|(sym, _)| sym == symbol) { - symbols.push((*symbol, var_store.fresh())); + symbols.push((*symbol, *var)); } } Expr::List { loc_elems, .. } => { @@ -2665,7 +2681,7 @@ fn get_lookup_symbols(expr: &Expr, var_store: &mut VarStore) -> Vec<(Symbol, Var stack.reserve(1 + args.len()); match &boxed_expr.1.value { - Expr::Var(_) => { + Expr::Var(_, _) => { // do nothing } function_expr => { diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index 6435fc65f2..07c5caa6df 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -1040,7 +1040,7 @@ fn fix_values_captured_in_closure_expr( | Float(..) | Str(_) | SingleQuote(..) - | Var(_) + | Var(..) | AbilityMember(..) | EmptyRecord | TypedHole { .. } diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 12990f3d88..8f44b78562 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -351,7 +351,7 @@ pub fn constrain_expr( let (fn_var, loc_fn, closure_var, ret_var) = &**boxed; // The expression that evaluates to the function being called, e.g. `foo` in // (foo) bar baz - let opt_symbol = if let Var(symbol) | AbilityMember(symbol, _, _) = loc_fn.value { + let opt_symbol = if let Var(symbol, _) | AbilityMember(symbol, _, _) = loc_fn.value { Some(symbol) } else { None @@ -425,9 +425,13 @@ pub fn constrain_expr( let and_constraint = constraints.and_constraint(and_cons); constraints.exists(vars, and_constraint) } - Var(symbol) => { - // make lookup constraint to lookup this symbol's type in the environment - constraints.lookup(*symbol, expected, region) + Var(symbol, variable) => { + // Save the expectation in the variable, then lookup the symbol's type in the environment + let store_expected = + constraints.store(expected.get_type_ref().clone(), *variable, file!(), line!()); + let lookup_constr = + constraints.lookup(*symbol, expected.replace(Type::Variable(*variable)), region); + constraints.and_constraint([store_expected, lookup_constr]) } &AbilityMember(symbol, specialization_id, specialization_var) => { // Save the expectation in the `specialization_var` so we know what to specialize, then diff --git a/crates/compiler/derive/src/decoding.rs b/crates/compiler/derive/src/decoding.rs index 0671d7fdf3..8bd2b643d4 100644 --- a/crates/compiler/derive/src/decoding.rs +++ b/crates/compiler/derive/src/decoding.rs @@ -357,7 +357,7 @@ fn decoder_record_step_field( name: "Ok".into(), arguments: vec![( field_var, - Loc::at_zero(Expr::Var(ok_val_symbol)), + Loc::at_zero(Expr::Var(ok_val_symbol, field_var)), )], })), }, @@ -417,7 +417,7 @@ fn decoder_record_step_field( name: "Err".into(), arguments: vec![( decode_err_var, - Loc::at_zero(Expr::Var(err_val_symbol)), + Loc::at_zero(Expr::Var(err_val_symbol, decode_err_var)), )], }), guard: None, @@ -433,7 +433,7 @@ fn decoder_record_step_field( record_var: rec_var, ext_var: env.new_ext_var(ExtensionKind::Record), field_var: rec_dot_result, - loc_expr: Box::new(Loc::at_zero(Expr::Var(rec_symbol))), + loc_expr: Box::new(Loc::at_zero(Expr::Var(rec_symbol, rec_var))), field: "result".into(), })), cond_var: rec_dot_result, @@ -462,7 +462,7 @@ fn decoder_record_step_field( record_var: rec_var, ext_var: env.new_ext_var(ExtensionKind::Record), field_var: Variable::LIST_U8, - loc_expr: Box::new(Loc::at_zero(Expr::Var(rec_symbol))), + loc_expr: Box::new(Loc::at_zero(Expr::Var(rec_symbol, rec_var))), field: "rest".into(), })), }, @@ -499,12 +499,15 @@ fn decoder_record_step_field( let condition_expr = Expr::Call( Box::new(( this_decode_with_var, - Loc::at_zero(Expr::Var(Symbol::DECODE_DECODE_WITH)), + Loc::at_zero(Expr::Var(Symbol::DECODE_DECODE_WITH, this_decode_with_var)), lambda_set_var, rec_var, )), vec![ - (Variable::LIST_U8, Loc::at_zero(Expr::Var(bytes_arg_symbol))), + ( + Variable::LIST_U8, + Loc::at_zero(Expr::Var(bytes_arg_symbol, Variable::LIST_U8)), + ), ( decoder_var, Loc::at_zero(Expr::AbilityMember( @@ -513,7 +516,10 @@ fn decoder_record_step_field( decoder_var, )), ), - (fmt_arg_var, Loc::at_zero(Expr::Var(fmt_arg_symbol))), + ( + fmt_arg_var, + Loc::at_zero(Expr::Var(fmt_arg_symbol, fmt_arg_var)), + ), ], CalledVia::Space, ); @@ -600,7 +606,7 @@ fn decoder_record_step_field( Expr::Call( Box::new(( this_decode_custom_var, - Loc::at_zero(Expr::Var(Symbol::DECODE_CUSTOM)), + Loc::at_zero(Expr::Var(Symbol::DECODE_CUSTOM, this_decode_custom_var)), decode_custom_closure_var, decode_custom_ret_var, )), @@ -676,7 +682,7 @@ fn decoder_record_step_field( // when field is let body = Expr::When { - loc_cond: Box::new(Loc::at_zero(Expr::Var(field_arg_symbol))), + loc_cond: Box::new(Loc::at_zero(Expr::Var(field_arg_symbol, Variable::STR))), cond_var: Variable::STR, expr_var: keep_or_skip_var, region: Region::zero(), @@ -764,7 +770,7 @@ fn decoder_record_finalizer( pattern_symbols.push(symbol); - let field_expr = Expr::Var(symbol); + let field_expr = Expr::Var(symbol, field_var); let field = Field { var: field_var, region: Region::zero(), @@ -827,7 +833,7 @@ fn decoder_record_finalizer( record_var: state_record_var, ext_var: env.new_ext_var(ExtensionKind::Record), field_var: result_field_var, - loc_expr: Box::new(Loc::at_zero(Expr::Var(state_arg_symbol))), + loc_expr: Box::new(Loc::at_zero(Expr::Var(state_arg_symbol, state_record_var))), field: field_name.clone(), }; @@ -1126,7 +1132,7 @@ fn wrap_in_decode_custom_decode_with( // ~ bytes, Decoder (List elem) fmt, fmt -> DecoderResult (List val) env.unify(decode_with_type, this_decode_with_fn_var); - let decode_with_var = Var(Symbol::DECODE_DECODE_WITH); + let decode_with_var = Var(Symbol::DECODE_DECODE_WITH, this_decode_with_fn_var); let decode_with_fn = Box::new(( this_decode_with_fn_var, Loc::at_zero(decode_with_var), @@ -1137,9 +1143,9 @@ fn wrap_in_decode_custom_decode_with( decode_with_fn, vec![ // bytes inner_decoder fmt - (bytes_var, Loc::at_zero(Var(bytes_sym))), + (bytes_var, Loc::at_zero(Var(bytes_sym, bytes_var))), (inner_decoder_var, Loc::at_zero(inner_decoder)), - (fmt_var, Loc::at_zero(Var(fmt_sym))), + (fmt_var, Loc::at_zero(Var(fmt_sym, fmt_var))), ], CalledVia::Space, ); @@ -1231,7 +1237,7 @@ fn wrap_in_decode_custom_decode_with( // ~ (List U8, fmt -> DecodeResult (List elem)) -> Decoder (List elem) fmt env.unify(decode_custom_type, this_decode_custom_fn_var); - let decode_custom_var = Var(Symbol::DECODE_CUSTOM); + let decode_custom_var = Var(Symbol::DECODE_CUSTOM, this_decode_custom_fn_var); let decode_custom_fn = Box::new(( this_decode_custom_fn_var, Loc::at_zero(decode_custom_var), diff --git a/crates/compiler/derive/src/encoding.rs b/crates/compiler/derive/src/encoding.rs index 006b99e925..affd4d6acd 100644 --- a/crates/compiler/derive/src/encoding.rs +++ b/crates/compiler/derive/src/encoding.rs @@ -134,7 +134,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { // toEncoder elem let to_encoder_call = Call( to_encoder_fn, - vec![(elem_var, Loc::at_zero(Var(elem_sym)))], + vec![(elem_var, Loc::at_zero(Var(elem_sym, elem_var)))], CalledVia::Space, ); @@ -217,7 +217,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) { let encode_list_call = Call( encode_list_fn, vec![ - (list_var, Loc::at_zero(Var(lst_sym))), + (list_var, Loc::at_zero(Var(lst_sym, list_var))), (to_elem_encoder_fn_var, Loc::at_zero(to_elem_encoder)), ], CalledVia::Space, @@ -314,7 +314,7 @@ fn to_encoder_record( record_var, ext_var: env.subs.fresh_unnamed_flex_var(), field_var, - loc_expr: Box::new(Loc::at_zero(Var(rcd_sym))), + loc_expr: Box::new(Loc::at_zero(Var(rcd_sym, record_var))), field: field_name, }; @@ -572,7 +572,7 @@ fn to_encoder_tag_union( // toEncoder rcd.a let to_encoder_call = Call( to_encoder_fn, - vec![(sym_var, Loc::at_zero(Var(sym)))], + vec![(sym_var, Loc::at_zero(Var(sym, sym_var)))], CalledVia::Space, ); @@ -662,7 +662,7 @@ fn to_encoder_tag_union( // A v1 v2 -> Encode.tag "A" [ Encode.toEncoder v1, Encode.toEncoder v2 ] // B v3 -> Encode.tag "B" [ Encode.toEncoder v3 ] let when_branches = When { - loc_cond: Box::new(Loc::at_zero(Var(tag_sym))), + loc_cond: Box::new(Loc::at_zero(Var(tag_sym, tag_union_var))), cond_var: tag_union_var, expr_var: whole_tag_encoders_var, region: Region::zero(), @@ -778,7 +778,7 @@ fn wrap_in_encode_custom( // Encode.appendWith : List U8, encoder_var, fmt -[appendWith]-> List U8 | fmt has EncoderFormatting let append_with_fn = Box::new(( this_append_with_fn_var, - Loc::at_zero(Var(Symbol::ENCODE_APPEND_WITH)), + Loc::at_zero(Var(Symbol::ENCODE_APPEND_WITH, this_append_with_fn_var)), this_append_with_clos_var, Variable::LIST_U8, )); @@ -788,11 +788,11 @@ fn wrap_in_encode_custom( append_with_fn, vec![ // (bytes_var, bytes) - (bytes_var, Loc::at_zero(Var(bytes_sym))), + (bytes_var, Loc::at_zero(Var(bytes_sym, bytes_var))), // (encoder_var, encoder) (encoder_var, Loc::at_zero(encoder)), // (fmt, fmt_var) - (fmt_var, Loc::at_zero(Var(fmt_sym))), + (fmt_var, Loc::at_zero(Var(fmt_sym, fmt_var))), ], CalledVia::Space, ); @@ -869,7 +869,7 @@ fn wrap_in_encode_custom( // Encode.custom : (List U8, fmt -> List U8) -> Encoder fmt | fmt has EncoderFormatting let custom_fn = Box::new(( this_custom_fn_var, - Loc::at_zero(Var(Symbol::ENCODE_CUSTOM)), + Loc::at_zero(Var(Symbol::ENCODE_CUSTOM, this_custom_fn_var)), this_custom_clos_var, // -[clos]-> this_custom_encoder_var, // t' ~ Encoder fmt )); diff --git a/crates/compiler/derive/src/hash.rs b/crates/compiler/derive/src/hash.rs index d06084243a..678da5ee6c 100644 --- a/crates/compiler/derive/src/hash.rs +++ b/crates/compiler/derive/src/hash.rs @@ -90,7 +90,7 @@ fn hash_record(env: &mut Env<'_>, fn_name: Symbol, fields: Vec) -> (V let hasher_var = synth_var(env.subs, Content::FlexAbleVar(None, Symbol::HASH_HASHER)); let (body_var, body) = record_fields.iter_all().fold( - (hasher_var, Expr::Var(hasher_sym)), + (hasher_var, Expr::Var(hasher_sym, hasher_var)), |total_hasher, (field_name, field_var, _)| { let field_name = env.subs[field_name].clone(); let field_var = env.subs[field_var]; @@ -99,7 +99,7 @@ fn hash_record(env: &mut Env<'_>, fn_name: Symbol, fields: Vec) -> (V record_var, field_var, ext_var: env.subs.fresh_unnamed_flex_var(), - loc_expr: Box::new(Loc::at_zero(Expr::Var(rcd_sym))), + loc_expr: Box::new(Loc::at_zero(Expr::Var(rcd_sym, record_var))), field: field_name, }; @@ -215,7 +215,7 @@ fn hash_tag_union( let (discr_hasher_var, disc_hasher_expr) = call_hash_ability_member( env, hash_discr_member, - (hasher_var, Expr::Var(hasher_sym)), + (hasher_var, Expr::Var(hasher_sym, hasher_var)), ( discr_num_var, Expr::Int( @@ -232,7 +232,11 @@ fn hash_tag_union( let (body_var, body_expr) = (payload_vars.into_iter()).zip(payload_syms).fold( (discr_hasher_var, disc_hasher_expr), |total_hasher, (payload_var, payload_sym)| { - call_hash_hash(env, total_hasher, (payload_var, Expr::Var(payload_sym))) + call_hash_hash( + env, + total_hasher, + (payload_var, Expr::Var(payload_sym, payload_var)), + ) }, ); @@ -251,7 +255,7 @@ fn hash_tag_union( // ... let when_var = whole_hasher_var; let when_expr = Expr::When { - loc_cond: Box::new(Loc::at_zero(Expr::Var(union_sym))), + loc_cond: Box::new(Loc::at_zero(Expr::Var(union_sym, union_var))), cond_var: union_var, expr_var: when_var, region: Region::zero(), @@ -338,9 +342,13 @@ fn hash_newtype_tag_union( // Fold up `Hash.hash (... (Hash.hash discrHasher x11) ...) x1n` let (body_var, body_expr) = (payload_vars.into_iter()).zip(payload_syms).fold( - (hasher_var, Expr::Var(hasher_sym)), + (hasher_var, Expr::Var(hasher_sym, hasher_var)), |total_hasher, (payload_var, payload_sym)| { - call_hash_hash(env, total_hasher, (payload_var, Expr::Var(payload_sym))) + call_hash_hash( + env, + total_hasher, + (payload_var, Expr::Var(payload_sym, payload_var)), + ) }, ); diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index f2180db47b..6b27df1bbb 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -2376,7 +2376,7 @@ fn from_can_let<'a>( lower_rest!(variable, cont.value) } - Var(original) | AbilityMember(original, _, _) => { + Var(original, _) | AbilityMember(original, _, _) => { // a variable is aliased, e.g. // // foo = bar @@ -2605,7 +2605,7 @@ fn from_can_let<'a>( } match def.loc_expr.value { - roc_can::expr::Expr::Var(outer_symbol) if !procs.is_module_thunk(outer_symbol) => { + roc_can::expr::Expr::Var(outer_symbol, _) if !procs.is_module_thunk(outer_symbol) => { store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt) } _ => { @@ -2747,7 +2747,7 @@ fn pattern_to_when<'a>( cond_var: pattern_var, expr_var: body_var, region: Region::zero(), - loc_cond: Box::new(Loc::at_zero(Var(symbol))), + loc_cond: Box::new(Loc::at_zero(Var(symbol, pattern_var))), branches: vec![WhenBranch { patterns: vec![WhenBranchPattern { pattern, @@ -4074,11 +4074,14 @@ pub fn with_hole<'a>( hole, ) } - Var(mut symbol) => { + Var(mut symbol, _) => { // If this symbol is a raw value, find the real name we gave to its specialized usage. - if let ReuseSymbol::Value(_symbol) = - can_reuse_symbol(env, procs, &roc_can::expr::Expr::Var(symbol), variable) - { + if let ReuseSymbol::Value(_symbol) = can_reuse_symbol( + env, + procs, + &roc_can::expr::Expr::Var(symbol, variable), + variable, + ) { let real_symbol = procs .symbol_specializations @@ -5046,7 +5049,7 @@ pub fn with_hole<'a>( }; match loc_expr.value { - roc_can::expr::Expr::Var(proc_name) if is_known(proc_name) => { + roc_can::expr::Expr::Var(proc_name, _) if is_known(proc_name) => { // a call by a known name call_by_name( env, @@ -6010,7 +6013,7 @@ fn tag_union_to_function<'a>( let loc_pattern = Loc::at_zero(roc_can::pattern::Pattern::Identifier(arg_symbol)); - let loc_expr = Loc::at_zero(roc_can::expr::Expr::Var(arg_symbol)); + let loc_expr = Loc::at_zero(roc_can::expr::Expr::Var(arg_symbol, arg_var)); loc_pattern_args.push((arg_var, AnnotatedMark::known_exhaustive(), loc_pattern)); loc_expr_args.push((arg_var, loc_expr)); @@ -7513,7 +7516,7 @@ fn can_reuse_symbol<'a>( AbilityMember(member, specialization_id, _) => { late_resolve_ability_specialization(env, *member, *specialization_id, expr_var) } - Var(symbol) => *symbol, + Var(symbol, _) => *symbol, _ => return NotASymbol, }; diff --git a/crates/compiler/test_derive/src/pretty_print.rs b/crates/compiler/test_derive/src/pretty_print.rs index ec3ce4f70e..0eaa70ce72 100644 --- a/crates/compiler/test_derive/src/pretty_print.rs +++ b/crates/compiler/test_derive/src/pretty_print.rs @@ -87,7 +87,7 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a, .append("]") .group(), ), - Var(sym) | AbilityMember(sym, _, _) => f.text(format!( + Var(sym, _) | AbilityMember(sym, _, _) => f.text(format!( "{}.{}", sym.module_string(c.interns), sym.as_str(c.interns), From 2a21cd8c6e410cc7b56f034873a84f5edd9982be Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Mon, 17 Oct 2022 17:56:48 -0500 Subject: [PATCH 27/31] Resolve ability specializations looked up in expects --- Cargo.lock | 1 + crates/compiler/can/src/expr.rs | 53 +++++++++++++++++------ crates/compiler/can/src/module.rs | 6 +-- crates/compiler/constrain/src/expr.rs | 18 ++++++-- crates/compiler/load_internal/src/file.rs | 6 +-- crates/compiler/mono/src/ir.rs | 42 +++++++++++++++--- crates/repl_expect/Cargo.toml | 1 + crates/repl_expect/src/run.rs | 12 ++++- 8 files changed, 108 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 17b8113323..4197578f8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3960,6 +3960,7 @@ dependencies = [ "pretty_assertions", "roc_build", "roc_builtins", + "roc_can", "roc_collections", "roc_error_macros", "roc_gen_llvm", diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index c2befe9a1b..4e7e9d13d4 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -230,14 +230,14 @@ pub enum Expr { Expect { loc_condition: Box>, loc_continuation: Box>, - lookups_in_cond: Vec<(Symbol, Variable)>, + lookups_in_cond: Vec, }, // not parsed, but is generated when lowering toplevel effectful expects ExpectFx { loc_condition: Box>, loc_continuation: Box>, - lookups_in_cond: Vec<(Symbol, Variable)>, + lookups_in_cond: Vec, }, /// Rendered as empty box in editor @@ -247,6 +247,13 @@ pub enum Expr { RuntimeError(RuntimeError), } +#[derive(Clone, Copy, Debug)] +pub struct ExpectLookup { + pub symbol: Symbol, + pub var: Variable, + pub ability_info: Option, +} + impl Expr { pub fn category(&self) -> Category { match self { @@ -2541,7 +2548,7 @@ impl Declarations { }) } - pub fn expects(&self) -> VecMap> { + pub fn expects(&self) -> VecMap> { let mut collector = ExpectCollector { expects: VecMap::default(), }; @@ -2625,9 +2632,9 @@ pub struct DestructureDef { pub pattern_vars: VecMap, } -fn get_lookup_symbols(expr: &Expr) -> Vec<(Symbol, Variable)> { +fn get_lookup_symbols(expr: &Expr) -> Vec { let mut stack: Vec<&Expr> = vec![expr]; - let mut symbols = Vec::new(); + let mut lookups: Vec = Vec::new(); while let Some(expr) = stack.pop() { match expr { @@ -2636,11 +2643,23 @@ fn get_lookup_symbols(expr: &Expr) -> Vec<(Symbol, Variable)> { symbol, record_var: var, .. - } - | Expr::AbilityMember(symbol, _, var) => { + } => { // Don't introduce duplicates, or make unused variables - if !symbols.iter().any(|(sym, _)| sym == symbol) { - symbols.push((*symbol, *var)); + if !lookups.iter().any(|l| l.symbol == *symbol) { + lookups.push(ExpectLookup { + symbol: *symbol, + var: *var, + ability_info: None, + }); + } + } + Expr::AbilityMember(symbol, spec_id, var) => { + if !lookups.iter().any(|l| l.symbol == *symbol) { + lookups.push(ExpectLookup { + symbol: *symbol, + var: *var, + ability_info: *spec_id, + }); } } Expr::List { loc_elems, .. } => { @@ -2737,7 +2756,7 @@ fn get_lookup_symbols(expr: &Expr) -> Vec<(Symbol, Variable)> { } } - symbols + lookups } /// Here we transform @@ -2784,14 +2803,22 @@ fn toplevel_expect_to_inline_expect_help(mut loc_expr: Loc, has_effects: b loop { match loc_expr.value { Expr::LetNonRec(boxed_def, remainder) => { - lookups_in_cond.extend(boxed_def.pattern_vars.iter().map(|(a, b)| (*a, *b))); + lookups_in_cond.extend(boxed_def.pattern_vars.iter().map(|(a, b)| ExpectLookup { + symbol: *a, + var: *b, + ability_info: None, + })); stack.push(StoredDef::NonRecursive(loc_expr.region, boxed_def)); loc_expr = *remainder; } Expr::LetRec(defs, remainder, mark) => { for def in &defs { - lookups_in_cond.extend(def.pattern_vars.iter().map(|(a, b)| (*a, *b))); + lookups_in_cond.extend(def.pattern_vars.iter().map(|(a, b)| ExpectLookup { + symbol: *a, + var: *b, + ability_info: None, + })); } stack.push(StoredDef::Recursive(loc_expr.region, defs, mark)); @@ -2834,7 +2861,7 @@ fn toplevel_expect_to_inline_expect_help(mut loc_expr: Loc, has_effects: b } struct ExpectCollector { - expects: VecMap>, + expects: VecMap>, } impl crate::traverse::Visitor for ExpectCollector { diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index 07c5caa6df..5a6381dd23 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -3,7 +3,7 @@ use crate::annotation::canonicalize_annotation; use crate::def::{canonicalize_defs, Def}; use crate::effect_module::HostedGeneratedFunctions; use crate::env::Env; -use crate::expr::{ClosureData, Declarations, Expr, Output, PendingDerives}; +use crate::expr::{ClosureData, Declarations, ExpectLookup, Expr, Output, PendingDerives}; use crate::pattern::{BindingsFromPattern, Pattern}; use crate::scope::Scope; use bumpalo::Bump; @@ -130,7 +130,7 @@ pub struct Module { pub aliases: MutMap, pub rigid_variables: RigidVariables, pub abilities_store: PendingAbilitiesStore, - pub loc_expects: VecMap>, + pub loc_expects: VecMap>, } #[derive(Debug, Default)] @@ -152,7 +152,7 @@ pub struct ModuleOutput { pub symbols_from_requires: Vec<(Loc, Loc)>, pub pending_derives: PendingDerives, pub scope: Scope, - pub loc_expects: VecMap>, + pub loc_expects: VecMap>, } fn validate_generate_with<'a>( diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 8f44b78562..c1e8b1738a 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -13,8 +13,8 @@ use roc_can::expected::Expected::{self, *}; use roc_can::expected::PExpected; use roc_can::expr::Expr::{self, *}; use roc_can::expr::{ - AccessorData, AnnotatedMark, ClosureData, DeclarationTag, Declarations, DestructureDef, Field, - FunctionDef, OpaqueWrapFunctionData, WhenBranch, + AccessorData, AnnotatedMark, ClosureData, DeclarationTag, Declarations, DestructureDef, + ExpectLookup, Field, FunctionDef, OpaqueWrapFunctionData, WhenBranch, }; use roc_can::pattern::Pattern; use roc_can::traverse::symbols_introduced_from_pattern; @@ -519,7 +519,12 @@ pub fn constrain_expr( let mut vars = Vec::with_capacity(lookups_in_cond.len()); - for (symbol, var) in lookups_in_cond.iter() { + for ExpectLookup { + symbol, + var, + ability_info: _, + } in lookups_in_cond.iter() + { vars.push(*var); all_constraints.push(constraints.lookup( @@ -566,7 +571,12 @@ pub fn constrain_expr( let mut vars = Vec::with_capacity(lookups_in_cond.len()); - for (symbol, var) in lookups_in_cond.iter() { + for ExpectLookup { + symbol, + var, + ability_info: _, + } in lookups_in_cond.iter() + { vars.push(*var); all_constraints.push(constraints.lookup( diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 7251ef224c..a44ae46faa 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -7,8 +7,8 @@ use parking_lot::Mutex; use roc_builtins::roc::module_source; use roc_can::abilities::{AbilitiesStore, PendingAbilitiesStore, ResolvedImpl}; use roc_can::constraint::{Constraint as ConstraintSoa, Constraints}; -use roc_can::expr::Declarations; use roc_can::expr::PendingDerives; +use roc_can::expr::{Declarations, ExpectLookup}; use roc_can::module::{ canonicalize_module_defs, ExposedByModule, ExposedForModule, ExposedModuleTypes, Module, ResolvedImplementations, TypeState, @@ -726,7 +726,7 @@ pub enum EntryPoint<'a> { pub struct Expectations { pub subs: roc_types::subs::Subs, pub path: PathBuf, - pub expectations: VecMap>, + pub expectations: VecMap>, pub ident_ids: IdentIds, } @@ -770,7 +770,7 @@ struct ParsedModule<'a> { header_for: HeaderFor<'a>, } -type LocExpects = VecMap>; +type LocExpects = VecMap>; /// A message sent out _from_ a worker thread, /// representing a result of work done, or a request for further work diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 6b27df1bbb..4f2dbfb2bf 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -9,7 +9,7 @@ use bumpalo::collections::{CollectIn, Vec}; use bumpalo::Bump; use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_can::abilities::SpecializationId; -use roc_can::expr::{AnnotatedMark, ClosureData, IntValue}; +use roc_can::expr::{AnnotatedMark, ClosureData, ExpectLookup, IntValue}; use roc_can::module::ExposedByModule; use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap}; use roc_collections::VecMap; @@ -6350,11 +6350,25 @@ pub fn from_can<'a>( let rest = from_can(env, variable, loc_continuation.value, procs, layout_cache); let cond_symbol = env.unique_symbol(); - let lookups = Vec::from_iter_in(lookups_in_cond.iter().map(|t| t.0), env.arena); - + let mut lookups = Vec::with_capacity_in(lookups_in_cond.len(), env.arena); let mut layouts = Vec::with_capacity_in(lookups_in_cond.len(), env.arena); - for (_, var) in lookups_in_cond { + for ExpectLookup { + symbol, + var, + ability_info, + } in lookups_in_cond + { + let symbol = match ability_info { + Some(specialization_id) => late_resolve_ability_specialization( + env, + symbol, + Some(specialization_id), + var, + ), + None => symbol, + }; + lookups.push(symbol); let res_layout = layout_cache.from_var(env.arena, var, env.subs); let layout = return_on_layout_error!(env, res_layout, "Expect"); layouts.push(layout); @@ -6389,11 +6403,25 @@ pub fn from_can<'a>( let rest = from_can(env, variable, loc_continuation.value, procs, layout_cache); let cond_symbol = env.unique_symbol(); - let lookups = Vec::from_iter_in(lookups_in_cond.iter().map(|t| t.0), env.arena); - + let mut lookups = Vec::with_capacity_in(lookups_in_cond.len(), env.arena); let mut layouts = Vec::with_capacity_in(lookups_in_cond.len(), env.arena); - for (_, var) in lookups_in_cond { + for ExpectLookup { + symbol, + var, + ability_info, + } in lookups_in_cond + { + let symbol = match ability_info { + Some(specialization_id) => late_resolve_ability_specialization( + env, + symbol, + Some(specialization_id), + var, + ), + None => symbol, + }; + lookups.push(symbol); let res_layout = layout_cache.from_var(env.arena, var, env.subs); let layout = return_on_layout_error!(env, res_layout, "Expect"); layouts.push(layout); diff --git a/crates/repl_expect/Cargo.toml b/crates/repl_expect/Cargo.toml index 942279e7f7..43f014e413 100644 --- a/crates/repl_expect/Cargo.toml +++ b/crates/repl_expect/Cargo.toml @@ -10,6 +10,7 @@ bumpalo = {version = "3.11.0", features = ["collections"]} target-lexicon = "0.12.2" roc_builtins = {path = "../compiler/builtins"} +roc_can = {path = "../compiler/can"} roc_collections = {path = "../compiler/collections"} roc_intern = {path = "../compiler/intern"} roc_load = {path = "../compiler/load"} diff --git a/crates/repl_expect/src/run.rs b/crates/repl_expect/src/run.rs index a2a1289f23..b2d422fecd 100644 --- a/crates/repl_expect/src/run.rs +++ b/crates/repl_expect/src/run.rs @@ -4,6 +4,7 @@ use bumpalo::collections::Vec as BumpVec; use bumpalo::Bump; use inkwell::context::Context; use roc_build::link::llvm_module_to_dylib; +use roc_can::expr::ExpectLookup; use roc_collections::{MutSet, VecMap}; use roc_error_macros::internal_error; use roc_gen_llvm::{ @@ -438,7 +439,16 @@ fn render_expect_failure<'a>( }; let subs = arena.alloc(&mut data.subs); - let (symbols, variables): (Vec<_>, Vec<_>) = current.iter().map(|(a, b)| (*a, *b)).unzip(); + let (symbols, variables): (Vec<_>, Vec<_>) = current + .iter() + .map( + |ExpectLookup { + symbol, + var, + ability_info: _, + }| (*symbol, *var), + ) + .unzip(); let (offset, expressions) = crate::get_values( target_info, From f58b3cbf5c7977da97cd81999f88d4d455b7f53e Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 3 Nov 2022 16:13:25 +0100 Subject: [PATCH 28/31] remove duplicate import --- crates/cli_testing_examples/benchmarks/platform/host.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/cli_testing_examples/benchmarks/platform/host.zig b/crates/cli_testing_examples/benchmarks/platform/host.zig index d6586e2dbe..d2d15f7d96 100644 --- a/crates/cli_testing_examples/benchmarks/platform/host.zig +++ b/crates/cli_testing_examples/benchmarks/platform/host.zig @@ -1,7 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); const str = @import("str"); -const builtin = @import("builtin"); const RocStr = str.RocStr; const testing = std.testing; const expectEqual = testing.expectEqual; From 94cc2971a3e1dabfcff04ea764d583f32134e0bf Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Nov 2022 14:08:19 +0100 Subject: [PATCH 29/31] don't use a global on the zig side (surgical linking can't do those yet) --- .../compiler/builtins/bitcode/src/expect.zig | 30 +++++++++++++++++-- crates/compiler/builtins/bitcode/src/main.zig | 4 +-- crates/compiler/builtins/src/bitcode.rs | 5 +++- crates/compiler/gen_llvm/src/llvm/build.rs | 4 --- crates/compiler/gen_llvm/src/llvm/expect.rs | 24 ++++++--------- 5 files changed, 43 insertions(+), 24 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/expect.zig b/crates/compiler/builtins/bitcode/src/expect.zig index 69bb821332..d6b1f12df0 100644 --- a/crates/compiler/builtins/bitcode/src/expect.zig +++ b/crates/compiler/builtins/bitcode/src/expect.zig @@ -19,10 +19,37 @@ pub fn setSharedBuffer(ptr: [*]u8, length: usize) callconv(.C) usize { return 0; } -pub fn expectFailedStart() callconv(.C) [*]u8 { +pub fn expectFailedStartSharedBuffer() callconv(.C) [*]u8 { return SHARED_BUFFER.ptr; } +pub fn expectFailedStartSharedFile() callconv(.C) [*]u8 { + // IMPORTANT: shared memory object names must begin with / and contain no other slashes! + var name: [100]u8 = undefined; + _ = std.fmt.bufPrint(name[0..100], "/roc_expect_buffer_{}\x00", .{roc_getppid()}) catch unreachable; + + if (builtin.os.tag == .macos or builtin.os.tag == .linux) { + const shared_fd = roc_shm_open(@ptrCast(*const i8, &name), O_RDWR | O_CREAT, 0o666); + + const length = 4096; + + const shared_ptr = roc_mmap( + null, + length, + PROT_WRITE, + MAP_SHARED, + shared_fd, + 0, + ); + + const ptr = @ptrCast([*]u8, shared_ptr); + + return ptr; + } else { + unreachable; + } +} + extern fn roc_send_signal(pid: c_int, sig: c_int) c_int; extern fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int; extern fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque; @@ -30,7 +57,6 @@ extern fn roc_getppid() c_int; pub fn readSharedBufferEnv() callconv(.C) void { if (builtin.os.tag == .macos or builtin.os.tag == .linux) { - // const name = "/roc_expect_buffer"; // IMPORTANT: shared memory object names must begin with / and contain no other slashes! var name: [100]u8 = undefined; diff --git a/crates/compiler/builtins/bitcode/src/main.zig b/crates/compiler/builtins/bitcode/src/main.zig index 3679d2b99b..152284aebc 100644 --- a/crates/compiler/builtins/bitcode/src/main.zig +++ b/crates/compiler/builtins/bitcode/src/main.zig @@ -169,12 +169,12 @@ comptime { @export(utils.panic, .{ .name = "roc_builtins.utils." ++ "panic", .linkage = .Weak }); if (builtin.target.cpu.arch != .wasm32) { - exportUtilsFn(expect.expectFailedStart, "expect_failed_start"); + exportUtilsFn(expect.expectFailedStartSharedBuffer, "expect_failed_start_shared_buffer"); + exportUtilsFn(expect.expectFailedStartSharedFile, "expect_failed_start_shared_file"); exportUtilsFn(expect.expectFailedFinalize, "expect_failed_finalize"); // sets the buffer used for expect failures @export(expect.setSharedBuffer, .{ .name = "set_shared_buffer", .linkage = .Weak }); - // exportUtilsFn(expect.readSharedBufferEnv, "read_env_shared_buffer"); } diff --git a/crates/compiler/builtins/src/bitcode.rs b/crates/compiler/builtins/src/bitcode.rs index 362e5fe1f9..13f3769045 100644 --- a/crates/compiler/builtins/src/bitcode.rs +++ b/crates/compiler/builtins/src/bitcode.rs @@ -404,7 +404,10 @@ pub const UTILS_INCREF: &str = "roc_builtins.utils.incref"; pub const UTILS_DECREF: &str = "roc_builtins.utils.decref"; pub const UTILS_DECREF_CHECK_NULL: &str = "roc_builtins.utils.decref_check_null"; -pub const UTILS_EXPECT_FAILED_START: &str = "roc_builtins.utils.expect_failed_start"; +pub const UTILS_EXPECT_FAILED_START_SHARED_BUFFER: &str = + "roc_builtins.utils.expect_failed_start_shared_buffer"; +pub const UTILS_EXPECT_FAILED_START_SHARED_FILE: &str = + "roc_builtins.utils.expect_failed_start_shared_file"; pub const UTILS_EXPECT_FAILED_FINALIZE: &str = "roc_builtins.utils.expect_failed_finalize"; pub const UTILS_EXPECT_READ_ENV_SHARED_BUFFER: &str = "roc_builtins.utils.read_env_shared_buffer"; diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index ce06201eeb..ac55450238 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -2817,10 +2817,6 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( if env.mode.runs_expects() { bd.position_at_end(throw_block); - if let LlvmBackendMode::BinaryDev = env.mode { - crate::llvm::expect::read_env_shared_buffer(env); - } - match env.target_info.ptr_width() { roc_target::PtrWidth::Bytes8 => { clone_to_shared_memory( diff --git a/crates/compiler/gen_llvm/src/llvm/expect.rs b/crates/compiler/gen_llvm/src/llvm/expect.rs index 53cf2f55bd..5fa64a01c0 100644 --- a/crates/compiler/gen_llvm/src/llvm/expect.rs +++ b/crates/compiler/gen_llvm/src/llvm/expect.rs @@ -14,7 +14,8 @@ use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout}; use roc_region::all::Region; use super::build::{ - add_func, load_roc_value, load_symbol_and_layout, use_roc_value, FunctionSpec, Scope, + add_func, load_roc_value, load_symbol_and_layout, use_roc_value, FunctionSpec, LlvmBackendMode, + Scope, }; #[derive(Debug, Clone, Copy)] @@ -93,16 +94,6 @@ fn write_state<'a, 'ctx, 'env>( env.builder.build_store(offset_ptr, offset); } -pub(crate) fn read_env_shared_buffer(env: &Env) { - let func = env - .module - .get_function(bitcode::UTILS_EXPECT_READ_ENV_SHARED_BUFFER) - .unwrap(); - - env.builder - .build_call(func, &[], "call_expect_read_env_shared_buffer"); -} - pub(crate) fn finalize(env: &Env) { let func = env .module @@ -121,10 +112,13 @@ pub(crate) fn clone_to_shared_memory<'a, 'ctx, 'env>( region: Region, lookups: &[Symbol], ) { - let func = env - .module - .get_function(bitcode::UTILS_EXPECT_FAILED_START) - .unwrap(); + let start_function = if let LlvmBackendMode::BinaryDev = env.mode { + bitcode::UTILS_EXPECT_FAILED_START_SHARED_FILE + } else { + bitcode::UTILS_EXPECT_FAILED_START_SHARED_BUFFER + }; + + let func = env.module.get_function(start_function).unwrap(); let call_result = env .builder From 64584384bfe8d31ebb2e677e1be92554392e7c51 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Nov 2022 15:05:40 +0100 Subject: [PATCH 30/31] remove duplicate import --- examples/cli/effects-platform/host.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/cli/effects-platform/host.zig b/examples/cli/effects-platform/host.zig index 274d4193e6..181e83e45e 100644 --- a/examples/cli/effects-platform/host.zig +++ b/examples/cli/effects-platform/host.zig @@ -1,7 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); const str = @import("str"); -const builtin = @import("builtin"); const RocStr = str.RocStr; const testing = std.testing; const expectEqual = testing.expectEqual; From 2a41f72ad670c916ed89d135c803456acb2e3ff6 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Nov 2022 17:11:58 +0100 Subject: [PATCH 31/31] remove another duplicate import --- examples/cli/tui-platform/host.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/cli/tui-platform/host.zig b/examples/cli/tui-platform/host.zig index fa7eaa21a8..7dfe2da01f 100644 --- a/examples/cli/tui-platform/host.zig +++ b/examples/cli/tui-platform/host.zig @@ -1,7 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); const str = @import("str"); -const builtin = @import("builtin"); const RocStr = str.RocStr; const testing = std.testing; const expectEqual = testing.expectEqual;