diff --git a/crates/ast/src/solve_type.rs b/crates/ast/src/solve_type.rs index 90e562ad2a0..96873f37f9a 100644 --- a/crates/ast/src/solve_type.rs +++ b/crates/ast/src/solve_type.rs @@ -606,7 +606,10 @@ fn solve<'a>( let result = offenders.len(); if result > 0 { - dbg!(&subs, &offenders, &let_con.def_types); + eprintln!( + "subs: {:?}\n\noffenders: {:?}\n\nlet_con.def_types: {:?}\n", + &subs, &offenders, &let_con.def_types + ); } result 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 4248e171a8c..2a4fb112849 100644 --- a/crates/cli/tests/fixtures/multi-dep-str/platform/host.zig +++ b/crates/cli/tests/fixtures/multi-dep-str/platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -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! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; 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 18bb5d00749..a7cff59a0b1 100644 --- a/crates/cli/tests/fixtures/multi-dep-thunk/platform/host.zig +++ b/crates/cli/tests/fixtures/multi-dep-thunk/platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -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! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/crates/cli/tests/fixtures/packages/platform/host.zig b/crates/cli/tests/fixtures/packages/platform/host.zig index 4248e171a8c..2a4fb112849 100644 --- a/crates/cli/tests/fixtures/packages/platform/host.zig +++ b/crates/cli/tests/fixtures/packages/platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -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! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/crates/cli_testing_examples/algorithms/fibonacci-platform/host.zig b/crates/cli_testing_examples/algorithms/fibonacci-platform/host.zig index c27916c26f2..6e2d2e6b393 100644 --- a/crates/cli_testing_examples/algorithms/fibonacci-platform/host.zig +++ b/crates/cli_testing_examples/algorithms/fibonacci-platform/host.zig @@ -5,20 +5,6 @@ const expectEqual = testing.expectEqual; const expect = testing.expect; const maxInt = std.math.maxInt; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -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! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/crates/cli_testing_examples/algorithms/quicksort-platform/host.zig b/crates/cli_testing_examples/algorithms/quicksort-platform/host.zig index c402674a231..1ae4b1cc0e6 100644 --- a/crates/cli_testing_examples/algorithms/quicksort-platform/host.zig +++ b/crates/cli_testing_examples/algorithms/quicksort-platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -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! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/crates/cli_testing_examples/benchmarks/platform/host.zig b/crates/cli_testing_examples/benchmarks/platform/host.zig index 85f343c061b..7902302f451 100644 --- a/crates/cli_testing_examples/benchmarks/platform/host.zig +++ b/crates/cli_testing_examples/benchmarks/platform/host.zig @@ -7,20 +7,6 @@ const expectEqual = testing.expectEqual; const expect = testing.expect; const maxInt = std.math.maxInt; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -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! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/crates/cli_testing_examples/expects/zig-platform/host.zig b/crates/cli_testing_examples/expects/zig-platform/host.zig index 20c09320d90..33c1995d8df 100644 --- a/crates/cli_testing_examples/expects/zig-platform/host.zig +++ b/crates/cli_testing_examples/expects/zig-platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -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! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const Align = 2 * @alignOf(usize); extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque; extern fn realloc(c_ptr: [*]align(Align) u8, size: usize) callconv(.C) ?*anyopaque; diff --git a/crates/compiler/build/src/link.rs b/crates/compiler/build/src/link.rs index 94dc1bbca44..519b88bca83 100644 --- a/crates/compiler/build/src/link.rs +++ b/crates/compiler/build/src/link.rs @@ -126,7 +126,7 @@ fn find_wasi_libc_path() -> PathBuf { internal_error!("cannot find `wasi-libc.a`") } -#[cfg(all(unix, not(target_os = "macos")))] +#[cfg(unix)] #[allow(clippy::too_many_arguments)] pub fn build_zig_host_native( env_path: &str, @@ -257,99 +257,6 @@ pub fn build_zig_host_native( zig_cmd } -#[cfg(target_os = "macos")] -#[allow(clippy::too_many_arguments)] -pub fn build_zig_host_native( - env_path: &str, - env_home: &str, - emit_bin: &str, - zig_host_src: &str, - _target: &str, - opt_level: OptLevel, - shared_lib_path: Option<&Path>, - builtins_host_path: &Path, - // For compatibility with the non-macOS def above. Keep these in sync. -) -> Command { - use serde_json::Value; - - // Run `zig env` to find the location of zig's std/ directory - let zig_env_output = zig().args(["env"]).output().unwrap(); - - let zig_env_json = if zig_env_output.status.success() { - std::str::from_utf8(&zig_env_output.stdout).unwrap_or_else(|utf8_err| { - internal_error!( - "`zig env` failed; its stderr output was invalid utf8 ({:?})", - utf8_err - ); - }) - } else { - match std::str::from_utf8(&zig_env_output.stderr) { - Ok(stderr) => internal_error!("`zig env` failed - stderr output was: {:?}", stderr), - Err(utf8_err) => internal_error!( - "`zig env` failed; its stderr output was invalid utf8 ({:?})", - utf8_err - ), - } - }; - - let mut zig_compiler_rt_path = match serde_json::from_str(zig_env_json) { - Ok(Value::Object(map)) => match map.get("std_dir") { - Some(Value::String(std_dir)) => PathBuf::from(Path::new(std_dir)), - _ => { - internal_error!("Expected JSON containing a `std_dir` String field from `zig env`, but got: {:?}", zig_env_json); - } - }, - _ => { - internal_error!( - "Expected JSON containing a `std_dir` field from `zig env`, but got: {:?}", - zig_env_json - ); - } - }; - - zig_compiler_rt_path.push("special"); - zig_compiler_rt_path.push("compiler_rt.zig"); - - let mut zig_cmd = zig(); - zig_cmd - .env_clear() - .env("PATH", env_path) - .env("HOME", env_home); - if let Some(shared_lib_path) = shared_lib_path { - zig_cmd.args([ - "build-exe", - "-fPIE", - shared_lib_path.to_str().unwrap(), - builtins_host_path.to_str().unwrap(), - ]); - } else { - zig_cmd.args(["build-obj"]); - } - zig_cmd.args([ - zig_host_src, - &format!("-femit-bin={}", emit_bin), - "--pkg-begin", - "glue", - find_zig_glue_path().to_str().unwrap(), - "--pkg-end", - // include the zig runtime - "--pkg-begin", - "compiler_rt", - zig_compiler_rt_path.to_str().unwrap(), - "--pkg-end", - // include libc - "--library", - "c", - ]); - if matches!(opt_level, OptLevel::Optimize) { - zig_cmd.args(["-O", "ReleaseSafe"]); - } else if matches!(opt_level, OptLevel::Size) { - zig_cmd.args(["-O", "ReleaseSmall"]); - } - - zig_cmd -} - pub fn build_zig_host_wasm32( env_path: &str, env_home: &str, diff --git a/crates/compiler/build/src/program.rs b/crates/compiler/build/src/program.rs index b249f2e6cee..083b07cbbab 100644 --- a/crates/compiler/build/src/program.rs +++ b/crates/compiler/build/src/program.rs @@ -166,7 +166,7 @@ fn gen_from_mono_module_llvm<'a>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let enum_attr = context.create_enum_attribute(kind_id, 1); + let enum_attr = context.create_enum_attribute(kind_id, 0); for function in module.get_functions() { let name = function.get_name().to_str().unwrap(); diff --git a/crates/compiler/build/src/target.rs b/crates/compiler/build/src/target.rs index 6abe50cb349..4acd2ce8c4a 100644 --- a/crates/compiler/build/src/target.rs +++ b/crates/compiler/build/src/target.rs @@ -87,12 +87,12 @@ pub fn target_zig_str(target: &Triple) -> &'static str { architecture: Architecture::X86_64, operating_system: OperatingSystem::Darwin, .. - } => "x86_64-apple-darwin", + } => "x86_64-macos-gnu", Triple { architecture: Architecture::Aarch64(_), operating_system: OperatingSystem::Darwin, .. - } => "aarch64-apple-darwin", + } => "aarch64-macos-gnu", _ => internal_error!("TODO gracefully handle unsupported target: {:?}", target), } } diff --git a/crates/compiler/builtins/bitcode/src/compiler_rt.zig b/crates/compiler/builtins/bitcode/src/compiler_rt.zig index b00961fa218..e4bc02da9bf 100644 --- a/crates/compiler/builtins/bitcode/src/compiler_rt.zig +++ b/crates/compiler/builtins/bitcode/src/compiler_rt.zig @@ -18,6 +18,7 @@ const v2u64 = @Vector(2, u64); // Export it as weak incase it is already linked in by something else. comptime { @export(__muloti4, .{ .name = "__muloti4", .linkage = .Weak }); + @export(__lshrti3, .{ .name = "__lshrti3", .linkage = .Weak }); if (want_windows_v2u64_abi) { @export(__divti3_windows_x86_64, .{ .name = "__divti3", .linkage = .Weak }); @export(__modti3_windows_x86_64, .{ .name = "__modti3", .linkage = .Weak }); @@ -440,3 +441,47 @@ pub inline fn floatFractionalBits(comptime T: type) comptime_int { else => @compileError("unknown floating point type " ++ @typeName(T)), }; } + +pub fn __lshrti3(a: i128, b: i32) callconv(.C) i128 { + return lshrXi3(i128, a, b); +} + +// Logical shift right: shift in 0 from left to right +// Precondition: 0 <= b < T.bit_count +inline fn lshrXi3(comptime T: type, a: T, b: i32) T { + const word_t = HalveInt(T, false); + const S = std.math.Log2Int(word_t.HalfT); + + const input = word_t{ .all = a }; + var output: word_t = undefined; + + if (b >= word_t.bits) { + output.s.high = 0; + output.s.low = input.s.high >> @intCast(S, b - word_t.bits); + } else if (b == 0) { + return a; + } else { + output.s.high = input.s.high >> @intCast(S, b); + output.s.low = input.s.high << @intCast(S, word_t.bits - b); + output.s.low |= input.s.low >> @intCast(S, b); + } + + return output.all; +} + +/// Allows to access underlying bits as two equally sized lower and higher +/// signed or unsigned integers. +fn HalveInt(comptime T: type, comptime signed_half: bool) type { + return extern union { + pub const bits = @divExact(@typeInfo(T).Int.bits, 2); + pub const HalfTU = std.meta.Int(.unsigned, bits); + pub const HalfTS = std.meta.Int(.signed, bits); + pub const HalfT = if (signed_half) HalfTS else HalfTU; + + all: T, + s: if (native_endian == .Little) + extern struct { low: HalfT, high: HalfT } + else + extern struct { high: HalfT, low: HalfT }, + }; +} diff --git a/crates/compiler/gen_llvm/src/llvm/bitcode.rs b/crates/compiler/gen_llvm/src/llvm/bitcode.rs index 89e75b35824..af4f7cd1d1e 100644 --- a/crates/compiler/gen_llvm/src/llvm/bitcode.rs +++ b/crates/compiler/gen_llvm/src/llvm/bitcode.rs @@ -222,7 +222,7 @@ fn build_transform_caller_help<'a, 'ctx>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let attr = env.context.create_enum_attribute(kind_id, 1); + let attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, attr); let entry = env.context.append_basic_block(function_value, "entry"); @@ -405,7 +405,7 @@ fn build_rc_wrapper<'a, 'ctx>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let attr = env.context.create_enum_attribute(kind_id, 1); + let attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, attr); let entry = env.context.append_basic_block(function_value, "entry"); @@ -494,7 +494,7 @@ pub fn build_eq_wrapper<'a, 'ctx>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let attr = env.context.create_enum_attribute(kind_id, 1); + let attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, attr); let entry = env.context.append_basic_block(function_value, "entry"); @@ -595,7 +595,7 @@ pub fn build_compare_wrapper<'a, 'ctx>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let attr = env.context.create_enum_attribute(kind_id, 1); + let attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, attr); let entry = env.context.append_basic_block(function_value, "entry"); diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index ba34c66c5d3..6c26c5da05c 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -1935,8 +1935,12 @@ fn tag_pointer_set_tag_id<'ctx>( // NOTE: assumes the lower bits of `cast_pointer` are all 0 let indexed_pointer = unsafe { - env.builder - .build_in_bounds_gep(cast_pointer, &[tag_id_intval], "indexed_pointer") + env.builder.new_build_in_bounds_gep( + env.context.i8_type(), + cast_pointer, + &[tag_id_intval], + "indexed_pointer", + ) }; env.builder @@ -1988,7 +1992,14 @@ pub fn tag_pointer_clear_tag_id<'ctx>( "cast_to_i8_ptr", ); - let indexed_pointer = unsafe { env.builder.build_gep(cast_pointer, &[index], "new_ptr") }; + let indexed_pointer = unsafe { + env.builder.new_build_in_bounds_gep( + env.context.i8_type(), + cast_pointer, + &[index], + "new_ptr", + ) + }; env.builder .build_pointer_cast(indexed_pointer, pointer.get_type(), "cast_from_i8_ptr") @@ -3945,11 +3956,9 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx>( arguments_for_call.push(*arg); } else { match layout_interner.get_repr(*layout) { - LayoutRepr::Builtin(Builtin::List(_)) => { - let list_type = arg_type - .into_pointer_type() - .get_element_type() - .into_struct_type(); + repr @ LayoutRepr::Builtin(Builtin::List(_)) => { + let list_type = basic_type_from_layout(env, layout_interner, repr); + let loaded = env.builder.new_build_load( list_type, arg.into_pointer_value(), @@ -5198,14 +5207,14 @@ fn build_proc_header<'a, 'ctx>( if false { let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let enum_attr = env.context.create_enum_attribute(kind_id, 1); + let enum_attr = env.context.create_enum_attribute(kind_id, 0); fn_val.add_attribute(AttributeLoc::Function, enum_attr); } if false { let kind_id = Attribute::get_named_enum_kind_id("noinline"); debug_assert!(kind_id > 0); - let enum_attr = env.context.create_enum_attribute(kind_id, 1); + let enum_attr = env.context.create_enum_attribute(kind_id, 0); fn_val.add_attribute(AttributeLoc::Function, enum_attr); } diff --git a/crates/compiler/gen_llvm/src/llvm/lowlevel.rs b/crates/compiler/gen_llvm/src/llvm/lowlevel.rs index 0cb0d493a03..edf1e105a44 100644 --- a/crates/compiler/gen_llvm/src/llvm/lowlevel.rs +++ b/crates/compiler/gen_llvm/src/llvm/lowlevel.rs @@ -1844,19 +1844,19 @@ fn throw_because_overflow(env: &Env<'_, '_, '_>, message: &str) { // prevent inlining of this function let kind_id = Attribute::get_named_enum_kind_id("noinline"); debug_assert!(kind_id > 0); - let enum_attr = env.context.create_enum_attribute(kind_id, 1); + let enum_attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, enum_attr); // calling this function is unlikely let kind_id = Attribute::get_named_enum_kind_id("cold"); debug_assert!(kind_id > 0); - let enum_attr = env.context.create_enum_attribute(kind_id, 1); + let enum_attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, enum_attr); // this function never returns let kind_id = Attribute::get_named_enum_kind_id("noreturn"); debug_assert!(kind_id > 0); - let enum_attr = env.context.create_enum_attribute(kind_id, 1); + let enum_attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, enum_attr); // Add a basic block for the entry point diff --git a/crates/compiler/gen_llvm/src/llvm/struct_.rs b/crates/compiler/gen_llvm/src/llvm/struct_.rs index f520d2f3c06..5f26b0ec0a9 100644 --- a/crates/compiler/gen_llvm/src/llvm/struct_.rs +++ b/crates/compiler/gen_llvm/src/llvm/struct_.rs @@ -92,7 +92,15 @@ impl<'ctx> RocStruct<'ctx> { index_struct_value(env, layout_interner, field_layouts, *argument, index) } (Self::ByReference(ptr), LayoutRepr::Struct(field_layouts)) => { - index_struct_ptr(env, layout_interner, field_layouts, *ptr, index) + let struct_type = basic_type_from_layout(env, layout_interner, struct_layout); + index_struct_ptr( + env, + layout_interner, + struct_type.into_struct_type(), + field_layouts, + *ptr, + index, + ) } (other, layout) => { unreachable!( @@ -135,26 +143,26 @@ fn index_struct_value<'a, 'ctx>( fn index_struct_ptr<'a, 'ctx>( env: &Env<'a, 'ctx, '_>, layout_interner: &STLayoutInterner<'a>, + struct_type: StructType<'ctx>, field_layouts: &[InLayout<'a>], ptr: PointerValue<'ctx>, index: u64, ) -> BasicValueEnum<'ctx> { debug_assert!(!field_layouts.is_empty()); - let field_value = get_field_from_ptr( - env, - ptr, - index as _, - env.arena - .alloc(format!("struct_field_access_record_{index}")), - ); - let field_layout = field_layouts[index as usize]; + let field_repr = layout_interner.get_repr(field_layout); + + let name = format!("struct_field_access_record_{index}"); + let field_value = env + .builder + .new_build_struct_gep(struct_type, ptr, index as u32, &name) + .unwrap(); load_roc_value( env, layout_interner, - layout_interner.get_repr(field_layout), + field_repr, field_value, "struct_field", ) @@ -171,15 +179,6 @@ fn get_field_from_value<'ctx>( .unwrap() } -fn get_field_from_ptr<'ctx>( - env: &Env<'_, 'ctx, '_>, - ptr: PointerValue<'ctx>, - index: u32, - name: &str, -) -> PointerValue<'ctx> { - env.builder.build_struct_gep(ptr, index, name).unwrap() -} - struct BuildStruct<'ctx> { struct_type: StructType<'ctx>, struct_val: StructValue<'ctx>, diff --git a/crates/compiler/test_gen/src/helpers/llvm.rs b/crates/compiler/test_gen/src/helpers/llvm.rs index 20a7bff7130..ad1a7bcd0ad 100644 --- a/crates/compiler/test_gen/src/helpers/llvm.rs +++ b/crates/compiler/test_gen/src/helpers/llvm.rs @@ -192,7 +192,7 @@ fn create_llvm_module<'a>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let attr = context.create_enum_attribute(kind_id, 1); + let attr = context.create_enum_attribute(kind_id, 0); for function in module.get_functions() { let name = function.get_name().to_str().unwrap(); diff --git a/crates/glue/src/RustGlue.roc b/crates/glue/src/RustGlue.roc index 5e3fcc71089..66ef96d1db4 100644 --- a/crates/glue/src/RustGlue.roc +++ b/crates/glue/src/RustGlue.roc @@ -1,6 +1,17 @@ app "rust-glue" packages { pf: "../platform/main.roc" } - imports [pf.Types.{ Types }, pf.Shape.{ Shape, RocFn }, pf.File.{ File }, pf.TypeId.{ TypeId }] + imports [ + pf.Types.{ Types }, pf.Shape.{ Shape, RocFn }, pf.File.{ File }, pf.TypeId.{ TypeId }, + "../static/Cargo.toml" as rocAppCargoToml : Str, + "../../roc_std/Cargo.toml" as rocStdCargoToml : Str, + "../../roc_std/src/lib.rs" as rocStdLib : Str, + "../../roc_std/src/roc_box.rs" as rocStdBox : Str, + "../../roc_std/src/roc_list.rs" as rocStdList : Str, + "../../roc_std/src/roc_dict.rs" as rocStdDict : Str, + "../../roc_std/src/roc_set.rs" as rocStdSet : Str, + "../../roc_std/src/roc_str.rs" as rocStdStr : Str, + "../../roc_std/src/storage.rs" as rocStdStorage : Str, + ] provides [makeGlue] to pf makeGlue : List Types -> Result (List File) Str @@ -22,9 +33,25 @@ makeGlue = \typesByArch -> typesByArch |> List.map convertTypesToFile - |> List.append { name: "mod.rs", content: modFileContent } + |> List.append { name: "roc_app/src/lib.rs", content: modFileContent } + |> List.concat staticFiles |> Ok +## These are always included, and don't depend on the specifics of the app. +staticFiles : List File +staticFiles = + [ + { name: "roc_app/Cargo.toml", content: rocAppCargoToml }, + { name: "roc_std/Cargo.toml", content: rocStdCargoToml }, + { name: "roc_std/src/lib.rs", content: rocStdLib }, + { name: "roc_std/src/roc_box.rs", content: rocStdBox }, + { name: "roc_std/src/roc_list.rs", content: rocStdList }, + { name: "roc_std/src/roc_dict.rs", content: rocStdDict }, + { name: "roc_std/src/roc_set.rs", content: rocStdSet }, + { name: "roc_std/src/roc_str.rs", content: rocStdStr }, + { name: "roc_std/src/storage.rs", content: rocStdStorage }, + ] + convertTypesToFile : Types -> File convertTypesToFile = \types -> content = @@ -94,7 +121,7 @@ convertTypesToFile = \types -> archStr = archName arch { - name: "\(archStr).rs", + name: "roc_app/src/\(archStr).rs", content: content |> generateEntryPoints types, } @@ -1288,7 +1315,7 @@ generateNullableUnwrapped = \buf, types, tagUnionid, name, nullTag, nonNullTag, FirstTagIsNull -> """ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - enum discriminant_\(name) { + pub enum discriminant_\(name) { \(nullTag) = 0, \(nonNullTag) = 1, } @@ -1297,7 +1324,7 @@ generateNullableUnwrapped = \buf, types, tagUnionid, name, nullTag, nonNullTag, SecondTagIsNull -> """ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - enum discriminant_\(name) { + pub enum discriminant_\(name) { \(nonNullTag) = 0, \(nullTag) = 1, } diff --git a/crates/glue/src/types.rs b/crates/glue/src/types.rs index 4c3c951ec6e..c882117188f 100644 --- a/crates/glue/src/types.rs +++ b/crates/glue/src/types.rs @@ -2227,7 +2227,13 @@ fn single_tag_payload_fields<'a, 'b>( env.glue_procs_by_layout.get(&layout).is_some(), env.layout_cache .interner - .has_varying_stack_size(in_layout, env.arena) + .has_varying_stack_size(in_layout, env.arena), + "glue_procs_by_layout for {:?} was {:?}, but the layout cache said its has_varying_stack_size was {}", + &layout, + env.glue_procs_by_layout.get(&layout), + env.layout_cache + .interner + .has_varying_stack_size(in_layout, env.arena) ); let (tag_name, payload_vars) = single_tag_payload(union_tags, subs); diff --git a/crates/glue/static/Cargo.toml b/crates/glue/static/Cargo.toml new file mode 100644 index 00000000000..ff900959690 --- /dev/null +++ b/crates/glue/static/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "roc_app" +description = "This was generated by `roc glue`. It provides glue between a specific Roc platform and a Rust host." +version = "1.0.0" +edition = "2021" + +[dependencies] +roc_std = { path = "../roc_std" } diff --git a/crates/glue/tests/fixture-templates/rust/Cargo.toml b/crates/glue/tests/fixture-templates/rust/Cargo.toml index 9697079aa5a..56f73f42ff6 100644 --- a/crates/glue/tests/fixture-templates/rust/Cargo.toml +++ b/crates/glue/tests/fixture-templates/rust/Cargo.toml @@ -27,7 +27,8 @@ name = "host" path = "src/main.rs" [dependencies] -roc_std = { path = "../../../../roc_std" } +roc_std = { path = "test_glue/roc_std" } +roc_app = { path = "test_glue/roc_app" } libc = "0.2" indoc = "1.0.6" diff --git a/crates/glue/tests/fixture-templates/rust/build.rs b/crates/glue/tests/fixture-templates/rust/build.rs index 7dcf8da4e95..6c70d142f01 100644 --- a/crates/glue/tests/fixture-templates/rust/build.rs +++ b/crates/glue/tests/fixture-templates/rust/build.rs @@ -10,6 +10,11 @@ // file with the same name in the fixture-templates/ directory. fn main() { + #[cfg(not(windows))] println!("cargo:rustc-link-lib=dylib=app"); + + #[cfg(windows)] + println!("cargo:rustc-link-lib=dylib=libapp"); + println!("cargo:rustc-link-search=."); } diff --git a/crates/glue/tests/fixtures/advanced-recursive-union/src/lib.rs b/crates/glue/tests/fixtures/advanced-recursive-union/src/lib.rs index e5c6e0c71f8..85af141dcbd 100644 --- a/crates/glue/tests/fixtures/advanced-recursive-union/src/lib.rs +++ b/crates/glue/tests/fixtures/advanced-recursive-union/src/lib.rs @@ -1,14 +1,13 @@ -mod test_glue; +use roc_app; use indoc::indoc; -// use test_glue::Rbt; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(()); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/arguments/src/lib.rs b/crates/glue/tests/fixtures/arguments/src/lib.rs index e20cb959598..9b5d52ac9b3 100644 --- a/crates/glue/tests/fixtures/arguments/src/lib.rs +++ b/crates/glue/tests/fixtures/arguments/src/lib.rs @@ -1,8 +1,8 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { - let answer = test_glue::mainForHost(42i64); + let answer = roc_app::mainForHost(42i64); println!("Answer was: {:?}", answer); // Debug diff --git a/crates/glue/tests/fixtures/basic-record/src/lib.rs b/crates/glue/tests/fixtures/basic-record/src/lib.rs index 4adf2de4c6f..d5cc2ea1e0b 100644 --- a/crates/glue/tests/fixtures/basic-record/src/lib.rs +++ b/crates/glue/tests/fixtures/basic-record/src/lib.rs @@ -1,11 +1,11 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let record = test_glue::mainForHost(); + let record = roc_app::mainForHost(); // Verify that the record has all the expected traits. diff --git a/crates/glue/tests/fixtures/basic-recursive-union/src/lib.rs b/crates/glue/tests/fixtures/basic-recursive-union/src/lib.rs index 299f9c802c6..ff0293bf68d 100644 --- a/crates/glue/tests/fixtures/basic-recursive-union/src/lib.rs +++ b/crates/glue/tests/fixtures/basic-recursive-union/src/lib.rs @@ -1,19 +1,12 @@ -mod test_glue; - use indoc::indoc; -use test_glue::Expr; - -extern "C" { - #[link_name = "roc__mainForHost_1_exposed_generic"] - fn roc_main(_: *mut Expr); -} +use roc_app::{self, Expr}; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(()); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/enumeration/src/lib.rs b/crates/glue/tests/fixtures/enumeration/src/lib.rs index 395b1296433..59704b6be9c 100644 --- a/crates/glue/tests/fixtures/enumeration/src/lib.rs +++ b/crates/glue/tests/fixtures/enumeration/src/lib.rs @@ -1,11 +1,11 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(); + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. @@ -28,8 +28,8 @@ pub extern "C" fn rust_main() -> i32 { println!( "tag_union was: {:?}, Bar is: {:?}, Baz is: {:?}", tag_union, - test_glue::MyEnum::Bar, - test_glue::MyEnum::Baz, + roc_app::MyEnum::Bar, + roc_app::MyEnum::Baz, ); // Debug // Exit code diff --git a/crates/glue/tests/fixtures/list-recursive-union/src/lib.rs b/crates/glue/tests/fixtures/list-recursive-union/src/lib.rs index e494b94e25b..0b598c19d93 100644 --- a/crates/glue/tests/fixtures/list-recursive-union/src/lib.rs +++ b/crates/glue/tests/fixtures/list-recursive-union/src/lib.rs @@ -1,25 +1,14 @@ -mod test_glue; +use roc_app; use indoc::indoc; -use test_glue::Rbt; - -extern "C" { - #[link_name = "roc__mainForHost_1_exposed_generic"] - fn roc_main(_: *mut Rbt); -} +use roc_app::Rbt; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = unsafe { - let mut ret: core::mem::MaybeUninit = core::mem::MaybeUninit::uninit(); - - roc_main(ret.as_mut_ptr()); - - ret.assume_init() - }; + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/multiple-modules/src/lib.rs b/crates/glue/tests/fixtures/multiple-modules/src/lib.rs index 74c44d6f528..e26415e7101 100644 --- a/crates/glue/tests/fixtures/multiple-modules/src/lib.rs +++ b/crates/glue/tests/fixtures/multiple-modules/src/lib.rs @@ -1,13 +1,12 @@ -mod test_glue; - use indoc::indoc; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(); + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/nested-record/src/lib.rs b/crates/glue/tests/fixtures/nested-record/src/lib.rs index 161b103780b..92deb035b25 100644 --- a/crates/glue/tests/fixtures/nested-record/src/lib.rs +++ b/crates/glue/tests/fixtures/nested-record/src/lib.rs @@ -1,10 +1,10 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; - let outer = test_glue::mainForHost(); + let outer = roc_app::mainForHost(); // Verify that `inner` has all the expected traits. { diff --git a/crates/glue/tests/fixtures/nonnullable-unwrapped/src/lib.rs b/crates/glue/tests/fixtures/nonnullable-unwrapped/src/lib.rs index ed7286e47ae..fcae9742c22 100644 --- a/crates/glue/tests/fixtures/nonnullable-unwrapped/src/lib.rs +++ b/crates/glue/tests/fixtures/nonnullable-unwrapped/src/lib.rs @@ -1,8 +1,8 @@ -mod test_glue; +use roc_app; use indoc::indoc; +use roc_app::StrRoseTree; use roc_std::{RocList, RocStr}; -use test_glue::StrRoseTree; extern "C" { #[link_name = "roc__mainForHost_1_exposed_generic"] diff --git a/crates/glue/tests/fixtures/nullable-unwrapped/platform.roc b/crates/glue/tests/fixtures/nullable-unwrapped/platform.roc index c5e54d6f594..13992876507 100644 --- a/crates/glue/tests/fixtures/nullable-unwrapped/platform.roc +++ b/crates/glue/tests/fixtures/nullable-unwrapped/platform.roc @@ -7,5 +7,5 @@ platform "test-platform" StrConsList : [Nil, Cons Str StrConsList] -mainForHost : {} -> StrConsList -mainForHost = \{} -> main +mainForHost : StrConsList +mainForHost = main diff --git a/crates/glue/tests/fixtures/nullable-unwrapped/src/lib.rs b/crates/glue/tests/fixtures/nullable-unwrapped/src/lib.rs index 22ece28b599..f2bf716c7fd 100644 --- a/crates/glue/tests/fixtures/nullable-unwrapped/src/lib.rs +++ b/crates/glue/tests/fixtures/nullable-unwrapped/src/lib.rs @@ -1,19 +1,14 @@ -mod test_glue; +use roc_app; use indoc::indoc; -use test_glue::StrConsList; - -extern "C" { - #[link_name = "roc__mainForHost_1_exposed_generic"] - fn roc_main(_: *mut StrConsList); -} +use roc_app::StrConsList; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/nullable-wrapped/src/lib.rs b/crates/glue/tests/fixtures/nullable-wrapped/src/lib.rs index 8a288f4d304..7cb25db13be 100644 --- a/crates/glue/tests/fixtures/nullable-wrapped/src/lib.rs +++ b/crates/glue/tests/fixtures/nullable-wrapped/src/lib.rs @@ -1,8 +1,8 @@ -mod test_glue; +use roc_app; use indoc::indoc; +use roc_app::StrFingerTree; use roc_std::RocStr; -use test_glue::StrFingerTree; extern "C" { #[link_name = "roc__mainForHost_1_exposed_generic"] @@ -14,7 +14,7 @@ pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(()); // Eq assert!(StrFingerTree::Empty() == StrFingerTree::Empty()); diff --git a/crates/glue/tests/fixtures/option/src/lib.rs b/crates/glue/tests/fixtures/option/src/lib.rs index 697013032c6..3f7ee45b88e 100644 --- a/crates/glue/tests/fixtures/option/src/lib.rs +++ b/crates/glue/tests/fixtures/option/src/lib.rs @@ -1,11 +1,11 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { - let string = test_glue::mainForHost(true); + let string = roc_app::mainForHost(true); println!("Answer was: {:?}", string.unwrap_Some()); // Debug // - let integer = test_glue::mainForHost(false); + let integer = roc_app::mainForHost(false); println!("Answer was: {:?}", integer.discriminant()); // Debug // Exit code diff --git a/crates/glue/tests/fixtures/return-function/src/lib.rs b/crates/glue/tests/fixtures/return-function/src/lib.rs index fad8f088059..396bed19b2e 100644 --- a/crates/glue/tests/fixtures/return-function/src/lib.rs +++ b/crates/glue/tests/fixtures/return-function/src/lib.rs @@ -1,8 +1,8 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { - let record = test_glue::mainForHost(); + let record = roc_app::mainForHost(); let answer1 = record.f.force_thunk(42i64, 1); let answer2 = record.g.force_thunk(42i64, 1); diff --git a/crates/glue/tests/fixtures/rocresult/src/lib.rs b/crates/glue/tests/fixtures/rocresult/src/lib.rs index dad518783b5..e07fbee768e 100644 --- a/crates/glue/tests/fixtures/rocresult/src/lib.rs +++ b/crates/glue/tests/fixtures/rocresult/src/lib.rs @@ -1,11 +1,11 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { - let string = test_glue::mainForHost(true); + let string = roc_app::mainForHost(true); println!("Answer was: {:?}", string); // Debug // - let integer = test_glue::mainForHost(false); + let integer = roc_app::mainForHost(false); println!("Answer was: {:?}", integer); // Debug // Exit code diff --git a/crates/glue/tests/fixtures/single-tag-union/src/lib.rs b/crates/glue/tests/fixtures/single-tag-union/src/lib.rs index a5b8aa1e3e9..cc2e1ae01da 100644 --- a/crates/glue/tests/fixtures/single-tag-union/src/lib.rs +++ b/crates/glue/tests/fixtures/single-tag-union/src/lib.rs @@ -1,14 +1,14 @@ -mod test_glue; +use roc_app; use indoc::indoc; -use test_glue::SingleTagUnion; +use roc_app::SingleTagUnion; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(); + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/union-with-padding/src/lib.rs b/crates/glue/tests/fixtures/union-with-padding/src/lib.rs index 31f209efa99..0c22b185fbe 100644 --- a/crates/glue/tests/fixtures/union-with-padding/src/lib.rs +++ b/crates/glue/tests/fixtures/union-with-padding/src/lib.rs @@ -1,6 +1,6 @@ -mod test_glue; +use roc_app; -use test_glue::NonRecursive; +use roc_app::NonRecursive; extern "C" { #[link_name = "roc__mainForHost_1_exposed_generic"] @@ -12,7 +12,7 @@ pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(()); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/union-without-padding/src/lib.rs b/crates/glue/tests/fixtures/union-without-padding/src/lib.rs index 6e5bbb2a2f1..e4fb9a53047 100644 --- a/crates/glue/tests/fixtures/union-without-padding/src/lib.rs +++ b/crates/glue/tests/fixtures/union-without-padding/src/lib.rs @@ -1,8 +1,8 @@ -mod test_glue; +use roc_app; extern "C" { #[link_name = "roc__mainForHost_1_exposed_generic"] - fn roc_main(_: *mut test_glue::NonRecursive); + fn roc_main(_: *mut roc_app::NonRecursive); } #[no_mangle] @@ -10,7 +10,7 @@ pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(()); // Verify that it has all the expected traits. @@ -23,10 +23,10 @@ pub extern "C" fn rust_main() -> i32 { println!( "tag_union was: {:?}\n`Foo \"small str\"` is: {:?}\n`Bar 123` is: {:?}\n`Baz` is: {:?}\n`Blah 456` is: {:?}", tag_union, - test_glue::NonRecursive::Foo("small str".into()), - test_glue::NonRecursive::Bar(123), - test_glue::NonRecursive::Baz(), - test_glue::NonRecursive::Blah(456), + roc_app::NonRecursive::Foo("small str".into()), + roc_app::NonRecursive::Bar(123), + roc_app::NonRecursive::Baz(), + roc_app::NonRecursive::Blah(456), ); // Debug let mut set = HashSet::new(); diff --git a/crates/glue/tests/test_glue_cli.rs b/crates/glue/tests/test_glue_cli.rs index 3e7d3c890be..6397c28369c 100644 --- a/crates/glue/tests/test_glue_cli.rs +++ b/crates/glue/tests/test_glue_cli.rs @@ -173,7 +173,7 @@ mod glue_cli_run { args: I, ) -> Out { let platform_module_path = platform_dir.join("platform.roc"); - let glue_dir = platform_dir.join("src").join("test_glue"); + let glue_dir = platform_dir.join("test_glue"); let fixture_templates_dir = platform_dir .parent() .unwrap() diff --git a/crates/linker/src/pe.rs b/crates/linker/src/pe.rs index ea7bd52c8e0..34a9f295147 100644 --- a/crates/linker/src/pe.rs +++ b/crates/linker/src/pe.rs @@ -444,6 +444,7 @@ pub(crate) fn surgery_pe(executable_path: &Path, metadata_path: &Path, roc_app_b "__fixsfti", "__fixunsdfti", "__fixunssfti", + "__lshrti3", "memcpy_decision", ] .contains(&name.as_str()); diff --git a/crates/roc_std/Cargo.toml b/crates/roc_std/Cargo.toml index 8c7b41cb9df..8318bdad415 100644 --- a/crates/roc_std/Cargo.toml +++ b/crates/roc_std/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "roc_std" description = "Rust representations of Roc data structures" -readme = "README.md" authors = ["The Roc Contributors"] edition = "2021" @@ -26,4 +25,4 @@ serde = ["dep:serde"] std = [] [package.metadata.cargo-udeps.ignore] -development = ["quickcheck_macros", "serde_json"] \ No newline at end of file +development = ["quickcheck_macros", "serde_json"] diff --git a/crates/valgrind/zig-platform/host.zig b/crates/valgrind/zig-platform/host.zig index 6025d07d157..8ba1179e4ff 100644 --- a/crates/valgrind/zig-platform/host.zig +++ b/crates/valgrind/zig-platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -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! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const Align = 2 * @alignOf(usize); extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque; extern fn realloc(c_ptr: [*]align(Align) u8, size: usize) callconv(.C) ?*anyopaque; diff --git a/examples/cli/effects-platform/host.zig b/examples/cli/effects-platform/host.zig index a0780797b31..e0e100278ff 100644 --- a/examples/cli/effects-platform/host.zig +++ b/examples/cli/effects-platform/host.zig @@ -7,20 +7,6 @@ const expectEqual = testing.expectEqual; const expect = testing.expect; const maxInt = std.math.maxInt; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -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! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/examples/cli/tui-platform/host.zig b/examples/cli/tui-platform/host.zig index 0eb4717d8c7..6e1c1869e93 100644 --- a/examples/cli/tui-platform/host.zig +++ b/examples/cli/tui-platform/host.zig @@ -7,20 +7,6 @@ const expectEqual = testing.expectEqual; const expect = testing.expect; const maxInt = std.math.maxInt; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -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! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/examples/glue/rust-platform/src/lib.rs b/examples/glue/rust-platform/src/lib.rs index a91550458b1..fe5398b6475 100644 --- a/examples/glue/rust-platform/src/lib.rs +++ b/examples/glue/rust-platform/src/lib.rs @@ -87,7 +87,7 @@ pub extern "C" fn rust_main() -> i32 { let mut op: Op = roc_main(); loop { - match dbg!(op.discriminant()) { + match op.discriminant() { StdoutWrite => { let stdout_write = op.get_StdoutWrite(); let output: RocStr = stdout_write.f0; diff --git a/examples/platform-switching/zig-platform/host.zig b/examples/platform-switching/zig-platform/host.zig index 6025d07d157..8ba1179e4ff 100644 --- a/examples/platform-switching/zig-platform/host.zig +++ b/examples/platform-switching/zig-platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -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! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const Align = 2 * @alignOf(usize); extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque; extern fn realloc(c_ptr: [*]align(Align) u8, size: usize) callconv(.C) ?*anyopaque; diff --git a/examples/webserver/.gitignore b/examples/webserver/.gitignore new file mode 100644 index 00000000000..d0e2c82835f --- /dev/null +++ b/examples/webserver/.gitignore @@ -0,0 +1,2 @@ +platform/glue +app diff --git a/examples/webserver/Http.roc b/examples/webserver/Http.roc new file mode 100644 index 00000000000..bdb8a14eba3 --- /dev/null +++ b/examples/webserver/Http.roc @@ -0,0 +1,4 @@ +interface Http exposes [request] imports [HttpInternal] + +request : Str -> Str +request = \req -> HttpInternal.request req diff --git a/examples/webserver/HttpInternal.roc b/examples/webserver/HttpInternal.roc new file mode 100644 index 00000000000..0201a2d2723 --- /dev/null +++ b/examples/webserver/HttpInternal.roc @@ -0,0 +1,4 @@ +interface HttpInternal exposes [request] imports [] + +request : Str -> Str +request = \req -> req diff --git a/examples/webserver/build.sh b/examples/webserver/build.sh new file mode 100755 index 00000000000..46790466f83 --- /dev/null +++ b/examples/webserver/build.sh @@ -0,0 +1,2 @@ +roc glue ../../crates/glue/src/RustGlue.roc platform/glue platform/main.roc +roc build diff --git a/examples/webserver/main.roc b/examples/webserver/main.roc new file mode 100644 index 00000000000..d5660386553 --- /dev/null +++ b/examples/webserver/main.roc @@ -0,0 +1,6 @@ +app "app" + packages { pf: "platform/main.roc" } + imports [] + provides [main] to pf + +main = \str -> "hi, \(str)!!" diff --git a/examples/webserver/platform/Cargo.toml b/examples/webserver/platform/Cargo.toml new file mode 100644 index 00000000000..867603fafc2 --- /dev/null +++ b/examples/webserver/platform/Cargo.toml @@ -0,0 +1,38 @@ +# ⚠️ READ THIS BEFORE MODIFYING THIS FILE! ⚠️ +# +# This file is a fixture template. If the file you're looking at is +# in the fixture-templates/ directory, then you're all set - go ahead +# and modify it, and it will modify all the fixture tests. +# +# If this file is in the fixtures/ directory, on the other hand, then +# it is gitignored and will be overwritten the next time tests run. +# So you probably don't want to modify it by hand! Instead, modify the +# file with the same name in the fixture-templates/ directory. + +[package] +name = "host" +version = "0.0.1" +authors = ["The Roc Contributors"] +license = "UPL-1.0" +edition = "2018" +links = "app" + +[lib] +name = "host" +path = "src/lib.rs" +crate-type = ["staticlib", "rlib"] + +[[bin]] +name = "host" +path = "src/main.rs" + +[dependencies] +roc_std = { path = "glue/roc_std", features = ["std"] } +roc_app = { path = "glue/roc_app" } +libc = "0.2" +hyper = { version = "0.14", features = ["http1", "http2", "client", "server", "runtime", "backports", "deprecated"] } +tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } +futures = "0.3" +bytes = "1.0" + +[workspace] diff --git a/examples/webserver/platform/build.rs b/examples/webserver/platform/build.rs new file mode 100644 index 00000000000..47763b34c39 --- /dev/null +++ b/examples/webserver/platform/build.rs @@ -0,0 +1,9 @@ +fn main() { + #[cfg(not(windows))] + println!("cargo:rustc-link-lib=dylib=app"); + + #[cfg(windows)] + println!("cargo:rustc-link-lib=dylib=libapp"); + + println!("cargo:rustc-link-search=."); +} diff --git a/examples/webserver/platform/host.c b/examples/webserver/platform/host.c new file mode 100644 index 00000000000..3914d3f6eed --- /dev/null +++ b/examples/webserver/platform/host.c @@ -0,0 +1,14 @@ +// ⚠️ READ THIS BEFORE MODIFYING THIS FILE! ⚠️ +// +// This file is a fixture template. If the file you're looking at is +// in the fixture-templates/ directory, then you're all set - go ahead +// and modify it, and it will modify all the fixture tests. +// +// If this file is in the fixtures/ directory, on the other hand, then +// it is gitignored and will be overwritten the next time tests run. +// So you probably don't want to modify it by hand! Instead, modify the +// file with the same name in the fixture-templates/ directory. + +extern int rust_main(); + +int main() { return rust_main(); } diff --git a/examples/webserver/platform/main.roc b/examples/webserver/platform/main.roc new file mode 100644 index 00000000000..c01047b7575 --- /dev/null +++ b/examples/webserver/platform/main.roc @@ -0,0 +1,9 @@ +platform "webserver-platform" + requires {} { main : _ } + exposes [] + packages {} + imports [] + provides [mainForHost] + +mainForHost : Str -> Str +mainForHost = \str -> main str diff --git a/examples/webserver/platform/src/lib.rs b/examples/webserver/platform/src/lib.rs new file mode 100644 index 00000000000..ad54ace3592 --- /dev/null +++ b/examples/webserver/platform/src/lib.rs @@ -0,0 +1,210 @@ +use futures::{Future, FutureExt}; +use hyper::{Body, Request, Response, Server, StatusCode}; +use roc_app; +use roc_std::RocStr; +use std::cell::RefCell; +use std::convert::Infallible; +use std::net::SocketAddr; +use std::os::raw::{c_int, c_long, c_void}; +use std::panic::AssertUnwindSafe; +use tokio::task::spawn_blocking; +use libc::{sigaction, siginfo_t, sigemptyset, SIGBUS, SIGFPE, SIGILL, SIGSEGV, sighandler_t, sigset_t, SA_SIGINFO, SIG_DFL}; + +const DEFAULT_PORT: u16 = 8000; + +// If we have a roc_panic or a segfault, these will be used to record where to jump back to +// (a point at which we can return a different response). +thread_local! { + // 64 is the biggest jmp_buf in setjmp.h + static SETJMP_ENV: RefCell<[c_long; 64]> = RefCell::new([0 as c_long; 64]); + static ROC_CRASH_MSG: RefCell = RefCell::new(RocStr::empty()); + static SIGNAL_CAUGHT: RefCell = RefCell::new(0); +} + +extern "C" { + #[link_name = "setjmp"] + pub fn setjmp(env: *mut c_void) -> c_int; + + #[link_name = "longjmp"] + pub fn longjmp(env: *mut c_void, val: c_int); +} + +unsafe extern "C" fn signal_handler(sig: c_int, _: *mut siginfo_t, _: *mut libc::c_void) { + SIGNAL_CAUGHT.with(|val| { + *val.borrow_mut() = sig; + }); + + SETJMP_ENV.with(|env| { + longjmp(env.borrow_mut().as_mut_ptr().cast(), 1); + }); +} + +fn setup_signal(sig: c_int) { + let sa = libc::sigaction { + sa_sigaction: signal_handler as sighandler_t, + sa_mask: sigset_t::default(), + sa_flags: SA_SIGINFO, + }; + + let mut old_sa = libc::sigaction { + sa_sigaction: SIG_DFL, + sa_mask: sigset_t::default(), + sa_flags: 0, + }; + + unsafe { + sigemptyset(&mut old_sa.sa_mask as *mut sigset_t); + sigaction(sig, &sa, &mut old_sa); + } +} + +fn call_roc(req_bytes: &[u8]) -> Response { + let mut setjmp_result = 0; + + SETJMP_ENV.with(|env| { + setjmp_result = unsafe { setjmp(env.borrow_mut().as_mut_ptr().cast()) }; + }); + + if setjmp_result == 0 { + setup_signal(SIGSEGV); + setup_signal(SIGILL); + setup_signal(SIGFPE); + setup_signal(SIGBUS); + + let req_str: &str = std::str::from_utf8(req_bytes).unwrap(); // TODO don't unwrap + let resp: String = roc_app::mainForHost(req_str.into()).as_str().into(); + + Response::builder() + .status(StatusCode::OK) // TODO get status code from Roc too + .body(Body::from(resp)) + .unwrap() // TODO don't unwrap() here + } else { + let mut crash_msg: String = String::new(); + let mut sig: c_int = 0; + + SIGNAL_CAUGHT.with(|val| { + sig = *val.borrow(); + }); + + if sig == 0 { + ROC_CRASH_MSG.with(|env| { + crash_msg = env.borrow().as_str().into(); + }); + } else { + crash_msg = "Roc crashed with signal {sig}".into(); // TODO print the name of the signal + } + + Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Body::from(crash_msg)) + .unwrap() // TODO don't unwrap() here + } +} + +async fn handle_req(req: Request) -> Response { + match hyper::body::to_bytes(req.into_body()).await { + Ok(req_body) => { + spawn_blocking(move || call_roc(&req_body)) + .then(|resp| async { + resp.unwrap() // TODO don't unwrap here + }) + .await + } + Err(_) => todo!(), // TODO + } +} + +/// Translate Rust panics in the given Future into 500 errors +async fn handle_panics( + fut: impl Future>, +) -> Result, Infallible> { + match AssertUnwindSafe(fut).catch_unwind().await { + Ok(response) => Ok(response), + Err(_panic) => { + let error = Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body("Panic detected!".into()) + .unwrap(); // TODO don't unwrap here + + Ok(error) + } + } +} + +const LOCALHOST: [u8; 4] = [127, 0, 0, 1]; + +async fn run_server(port: u16) -> i32 { + let addr = SocketAddr::from((LOCALHOST, port)); + let server = Server::bind(&addr).serve(hyper::service::make_service_fn(|_conn| async { + Ok::<_, Infallible>(hyper::service::service_fn(|req| handle_panics(handle_req(req)))) + })); + + println!("Listening on "); + + match server.await { + Ok(_) => 0, + Err(err) => { + eprintln!("Error initializing Rust `hyper` server: {}", err); // TODO improve this + + 1 + } + } +} + +#[no_mangle] +pub extern "C" fn rust_main() -> i32 { + match tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + { + Ok(runtime) => runtime.block_on(async { run_server(DEFAULT_PORT).await }), + Err(err) => { + eprintln!("Error initializing tokio multithreaded runtime: {}", err); // TODO improve this + + 1 + } + } +} + +// Externs required by roc_std and by the Roc app + +#[no_mangle] +pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void { + return libc::malloc(size); +} + +#[no_mangle] +pub unsafe extern "C" fn roc_realloc( + c_ptr: *mut c_void, + new_size: usize, + _old_size: usize, + _alignment: u32, +) -> *mut c_void { + return libc::realloc(c_ptr, new_size); +} + +#[no_mangle] +pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) { + return libc::free(c_ptr); +} + +#[no_mangle] +pub unsafe extern "C" fn roc_panic(msg: RocStr) { + // Set the last caught signal to 0, so we don't mistake this for a signal. + SIGNAL_CAUGHT.with(|val| { + *val.borrow_mut() = 0; + }); + + ROC_CRASH_MSG.with(|val| { + *val.borrow_mut() = msg; + }); + + SETJMP_ENV.with(|env| { + longjmp(env.borrow_mut().as_mut_ptr().cast(), 1); + }); +} + +#[no_mangle] +pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { + libc::memset(dst, c, n) +} diff --git a/examples/webserver/platform/src/main.rs b/examples/webserver/platform/src/main.rs new file mode 100644 index 00000000000..0765384f29f --- /dev/null +++ b/examples/webserver/platform/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + std::process::exit(host::rust_main() as _); +} diff --git a/www/build.sh b/www/build.sh index e2df683f4b8..cdd14f3cfd2 100755 --- a/www/build.sh +++ b/www/build.sh @@ -115,4 +115,13 @@ mkdir -p $BASIC_CLI_PACKAGE_DIR rm generated-docs/*.* # we already copied over the *.js and *.css files earlier, so just drop these. mv generated-docs/* $BASIC_CLI_PACKAGE_DIR # move all the folders to build/packages/basic-cli +# set up docs for basic-cli 0.3.2 +BASIC_CLI_DIR_0_3_2=$BASIC_CLI_PACKAGE_DIR/0-3-2 +mkdir -p $BASIC_CLI_DIR_0_3_2 +curl -fL --output $BASIC_CLI_DIR_0_3_2/docs.tar.gz https://github.com/roc-lang/basic-cli/releases/download/0.3.2/docs.tar.gz +tar -xf $BASIC_CLI_DIR_0_3_2/docs.tar.gz -C $BASIC_CLI_DIR_0_3_2/ +rm $BASIC_CLI_DIR_0_3_2/docs.tar.gz +mv $BASIC_CLI_DIR_0_3_2/generated-docs/* $BASIC_CLI_DIR_0_3_2 +rm -rf $BASIC_CLI_DIR_0_3_2/generated-docs + popd