From 1b0cd14715d3fe33c43a6c48b01859c2ebdfc707 Mon Sep 17 00:00:00 2001 From: Luke Boswell Date: Mon, 4 Nov 2024 15:37:34 +1100 Subject: [PATCH] add script to prebuild platform-switching rust --- .../workflows/nightly_macos_apple_silicon.yml | 20 +- .github/workflows/nix_macos_apple_silicon.yml | 6 - .github/workflows/nix_macos_x86_64.yml | 6 - .../test_nightly_macos_apple_silicon.yml | 4 +- .github/workflows/ubuntu_x86_64.yml | 5 +- Cargo.lock | 8 + Cargo.toml | 1 + ci/basic_nightly_test.sh | 7 +- crates/cli/tests/cli_tests.rs | 25 +- .../rust-platform/Cargo.toml | 13 +- .../platform-switching/rust-platform/build.rs | 40 - .../platform-switching/rust-platform/build.sh | 11 + .../platform-switching/rust-platform/host.c | 3 - .../rust-platform/libapp.roc | 3 - .../rust-platform/src/glue.rs | 744 ------------------ .../rust-platform/src/lib.rs | 3 +- .../rust-platform/src/main.rs | 3 - 17 files changed, 57 insertions(+), 845 deletions(-) delete mode 100644 examples/platform-switching/rust-platform/build.rs create mode 100644 examples/platform-switching/rust-platform/build.sh delete mode 100644 examples/platform-switching/rust-platform/host.c delete mode 100644 examples/platform-switching/rust-platform/libapp.roc delete mode 100644 examples/platform-switching/rust-platform/src/glue.rs delete mode 100644 examples/platform-switching/rust-platform/src/main.rs diff --git a/.github/workflows/nightly_macos_apple_silicon.yml b/.github/workflows/nightly_macos_apple_silicon.yml index b4d12d700f..3c98e58f8f 100644 --- a/.github/workflows/nightly_macos_apple_silicon.yml +++ b/.github/workflows/nightly_macos_apple_silicon.yml @@ -2,7 +2,7 @@ on: #pull_request: workflow_dispatch: schedule: - - cron: '0 9 * * *' + - cron: "0 9 * * *" name: Nightly Release macOS Apple Silicon @@ -30,15 +30,15 @@ jobs: run: cargo test --locked --release - name: get commit SHA - run: echo "SHA=$(git rev-parse --short "$GITHUB_SHA")" >> $GITHUB_ENV + run: echo "SHA=$(git rev-parse --short "$GITHUB_SHA")" >> $GITHUB_ENV - name: get date run: echo "DATE=$(date "+%Y-%m-%d")" >> $GITHUB_ENV - name: build file name env: - DATE: ${{ env.DATE }} - SHA: ${{ env.SHA }} + DATE: ${{ env.DATE }} + SHA: ${{ env.SHA }} run: echo "RELEASE_FOLDER_NAME=roc_nightly-macos_apple_silicon-$DATE-$SHA" >> $GITHUB_ENV - name: write version to file @@ -56,8 +56,10 @@ jobs: - name: extract tar for a quick test run: ls | grep tar | xargs tar -xf - - name: test with rust platform - run: cd ${{ env.RELEASE_FOLDER_NAME }} && ./roc examples/platform-switching/rocLovesRust.roc + - name: test with rust platform (prebuild the host first) + run: | + examples/platform-switching/rust-platform/build.sh + cd ${{ env.RELEASE_FOLDER_NAME }} && ./roc examples/platform-switching/rocLovesRust.roc - name: print short commit SHA run: git rev-parse --short "$GITHUB_SHA" @@ -66,6 +68,6 @@ jobs: - name: Upload artifact Actually uploading to github releases has to be done manually uses: actions/upload-artifact@v4 with: - name: ${{ env.RELEASE_FOLDER_NAME }}.tar.gz - path: ${{ env.RELEASE_FOLDER_NAME }}.tar.gz - retention-days: 4 + name: ${{ env.RELEASE_FOLDER_NAME }}.tar.gz + path: ${{ env.RELEASE_FOLDER_NAME }}.tar.gz + retention-days: 4 diff --git a/.github/workflows/nix_macos_apple_silicon.yml b/.github/workflows/nix_macos_apple_silicon.yml index f78ca0683d..23b8ce9dcd 100644 --- a/.github/workflows/nix_macos_apple_silicon.yml +++ b/.github/workflows/nix_macos_apple_silicon.yml @@ -39,12 +39,6 @@ jobs: - name: roc test all builtins run: nix develop -c ./ci/roc_test_builtins.sh - - name: make a libapp.so for the next step - run: nix develop -c cargo run -- build --lib examples/platform-switching/rust-platform/libapp.roc - - - name: check that the platform`s produced dylib is loadable - run: cd examples/platform-switching/rust-platform && nix develop -c cargo test --release --locked - - name: test aarch64 dev backend run: nix develop -c cargo nextest-gen-dev --locked --release --no-fail-fast diff --git a/.github/workflows/nix_macos_x86_64.yml b/.github/workflows/nix_macos_x86_64.yml index 1321fad81a..862543f591 100644 --- a/.github/workflows/nix_macos_x86_64.yml +++ b/.github/workflows/nix_macos_x86_64.yml @@ -22,9 +22,3 @@ jobs: - name: roc test all builtins run: nix develop -c ./ci/roc_test_builtins.sh - - - name: make a libapp.so for the next step - run: nix develop -c cargo run -- build --lib examples/platform-switching/rust-platform/libapp.roc - - - name: check that the platform`s produced dylib is loadable - run: cd examples/platform-switching/rust-platform && nix develop -c cargo test --release --locked diff --git a/.github/workflows/test_nightly_macos_apple_silicon.yml b/.github/workflows/test_nightly_macos_apple_silicon.yml index 69bb520c67..018382283b 100644 --- a/.github/workflows/test_nightly_macos_apple_silicon.yml +++ b/.github/workflows/test_nightly_macos_apple_silicon.yml @@ -30,7 +30,9 @@ jobs: run: cd roc_nightly && ./roc examples/helloWorld.roc - name: test platform switching rust - run: cd roc_nightly && ./roc examples/platform-switching/rocLovesRust.roc + run: | + examples/platform-switching/rust-platform/build.sh + cd roc_nightly && ./roc examples/platform-switching/rocLovesRust.roc - name: test platform switching zig run: cd roc_nightly && ./roc examples/platform-switching/rocLovesZig.roc diff --git a/.github/workflows/ubuntu_x86_64.yml b/.github/workflows/ubuntu_x86_64.yml index 29e9ca0170..e9784a403f 100644 --- a/.github/workflows/ubuntu_x86_64.yml +++ b/.github/workflows/ubuntu_x86_64.yml @@ -40,10 +40,7 @@ jobs: run: cargo test --locked --release -- --skip cli_tests::expects_dev_and_test - name: tests examples in docs - run: cargo test --doc --release - - - name: check that the platform`s produced dylib is loadable - run: cd examples/platform-switching/rust-platform && LD_LIBRARY_PATH=. cargo test --release --locked + run: cargo test --doc --release - name: test the dev backend # these tests require an explicit feature flag run: cargo test --locked --release --package test_gen --no-default-features --features gen-dev diff --git a/Cargo.lock b/Cargo.lock index 726abb6f81..8cce42822c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3267,6 +3267,14 @@ dependencies = [ "roc_work", ] +[[package]] +name = "rust-platform" +version = "0.0.1" +dependencies = [ + "libc", + "roc_std", +] + [[package]] name = "rustc-demangle" version = "0.1.23" diff --git a/Cargo.toml b/Cargo.toml index dc6e58b7b1..e3add5768c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ members = [ "crates/wasm_interp", "crates/language_server", "crates/roc_std_heap", + "examples/platform-switching/rust-platform", ] exclude = [ diff --git a/ci/basic_nightly_test.sh b/ci/basic_nightly_test.sh index be13a2ed46..66640cdd2b 100755 --- a/ci/basic_nightly_test.sh +++ b/ci/basic_nightly_test.sh @@ -5,14 +5,14 @@ set -euxo pipefail # if to prevent unset vars errror if [ -n "$(ls | grep -v "roc_nightly.*tar\.gz" | grep -v "^ci$")" ]; then - + # Remove everything in this dir except the tar and ci folder. # We want to test like a user who would have downloaded the release, so we clean up all files from the repo checkout. to_delete=$(ls | grep -v "roc_nightly.*tar\.gz" | grep -v "^ci$") for file_or_dir in $to_delete do - echo "Removing: $file_or_dir" + echo "Removing: $file_or_dir" rm -rf "$file_or_dir" done fi @@ -31,7 +31,8 @@ cd roc_nightly # test roc hello world ./roc examples/helloWorld.roc -# test rust platform +# test rust platform (first prebuild the host) +examples/platform-switching/rust-platform/build.sh ./roc examples/platform-switching/rocLovesRust.roc # test zig platform diff --git a/crates/cli/tests/cli_tests.rs b/crates/cli/tests/cli_tests.rs index c2d235d842..c555e7a500 100644 --- a/crates/cli/tests/cli_tests.rs +++ b/crates/cli/tests/cli_tests.rs @@ -52,22 +52,25 @@ mod cli_tests { #[test] #[cfg_attr(windows, ignore)] fn platform_switching_rust() { + // pre-build the platform + std::process::Command::new("bash") + .arg(file_from_root( + "examples/platform-switching/rust-platform", + "build.sh", + )) + .status() + .unwrap(); + let cli_build = ExecCli::new( - CMD_BUILD, + roc_cli::CMD_RUN, file_from_root("examples/platform-switching", "rocLovesRust.roc"), - ) - .arg(BUILD_HOST_FLAG) - .arg(SUPPRESS_BUILD_HOST_WARNING_FLAG); + ); let expected_output = "Roc <3 Rust!\n"; - cli_build.full_check_build_and_run( - expected_output, - TEST_LEGACY_LINKER, - ALLOW_VALGRIND, - None, - None, - ); + let output = cli_build.run(); + + output.assert_clean_stdout(expected_output); } #[test] diff --git a/examples/platform-switching/rust-platform/Cargo.toml b/examples/platform-switching/rust-platform/Cargo.toml index c5dca2b05a..25e41a8149 100644 --- a/examples/platform-switching/rust-platform/Cargo.toml +++ b/examples/platform-switching/rust-platform/Cargo.toml @@ -1,22 +1,15 @@ [package] -name = "host" +name = "rust-platform" authors = ["The Roc Contributors"] edition = "2021" license = "UPL-1.0" -links = "app" version = "0.0.1" [lib] -name = "host" +name = "rustplatform" path = "src/lib.rs" -crate-type = ["staticlib", "lib"] - -[[bin]] -name = "host" -path = "src/main.rs" +crate-type = ["staticlib"] [dependencies] libc = "0.2" roc_std = { path = "../../../crates/roc_std" } - -[workspace] diff --git a/examples/platform-switching/rust-platform/build.rs b/examples/platform-switching/rust-platform/build.rs deleted file mode 100644 index 01af87a5f5..0000000000 --- a/examples/platform-switching/rust-platform/build.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::path::{Path, PathBuf}; - -fn main() { - #[cfg(not(windows))] - println!("cargo:rustc-link-lib=dylib=app"); - - #[cfg(windows)] - println!("cargo:rustc-link-lib=dylib=libapp"); - - #[cfg(target_os = "macos")] - let dylib_file_name = "libapp.dylib"; - - #[cfg(target_os = "linux")] - let dylib_file_name = "libapp.so"; - - #[cfg(target_os = "windows")] - let dylib_file_name = "libapp.dll"; - - // Get the build cache directory (OUT_DIR) - let out_dir = std::env::var("OUT_DIR").unwrap(); - - let lib_app = workspace_root() - .join("examples") - .join("platform-switching") - .join("rust-platform") - .join(dylib_file_name); - - let out_path = Path::new(&out_dir).join(dylib_file_name); - - // copy the dylib to the output build cache - std::fs::copy(lib_app, out_path).unwrap(); - - // Search for the dylib in the cache directory - println!("cargo:rustc-link-search={out_dir}"); -} - -pub fn workspace_root() -> PathBuf { - let root = std::env::var("ROC_WORKSPACE_DIR").expect("Can't find the ROC_WORKSPACE_DIR variable expected to be set in .cargo/config.toml. Are you running tests outside of cargo?"); - PathBuf::from(root) -} diff --git a/examples/platform-switching/rust-platform/build.sh b/examples/platform-switching/rust-platform/build.sh new file mode 100644 index 0000000000..3403f38574 --- /dev/null +++ b/examples/platform-switching/rust-platform/build.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ +set -euxo pipefail + +# set the working directory to the directory of the script +cd "$(dirname "$0")" + +cargo build -p rust-platform + +cp ../../../target/debug/librustplatform.a ./libhost.a diff --git a/examples/platform-switching/rust-platform/host.c b/examples/platform-switching/rust-platform/host.c deleted file mode 100644 index b9214bcf33..0000000000 --- a/examples/platform-switching/rust-platform/host.c +++ /dev/null @@ -1,3 +0,0 @@ -extern int rust_main(); - -int main() { return rust_main(); } \ No newline at end of file diff --git a/examples/platform-switching/rust-platform/libapp.roc b/examples/platform-switching/rust-platform/libapp.roc deleted file mode 100644 index 393c65b6d4..0000000000 --- a/examples/platform-switching/rust-platform/libapp.roc +++ /dev/null @@ -1,3 +0,0 @@ -app [main] { pf: platform "main.roc" } - -main = "STUBBED APP" diff --git a/examples/platform-switching/rust-platform/src/glue.rs b/examples/platform-switching/rust-platform/src/glue.rs deleted file mode 100644 index e3e2e524b7..0000000000 --- a/examples/platform-switching/rust-platform/src/glue.rs +++ /dev/null @@ -1,744 +0,0 @@ -// ⚠️ GENERATED CODE ⚠️ - this entire file was generated by the `roc glue` CLI command - -#![allow(unused_unsafe)] -#![allow(unused_variables)] -#![allow(dead_code)] -#![allow(unused_mut)] -#![allow(non_snake_case)] -#![allow(non_camel_case_types)] -#![allow(non_upper_case_globals)] -#![allow(clippy::undocumented_unsafe_blocks)] -#![allow(clippy::redundant_static_lifetimes)] -#![allow(clippy::unused_unit)] -#![allow(clippy::missing_safety_doc)] -#![allow(clippy::let_and_return)] -#![allow(clippy::missing_safety_doc)] -#![allow(clippy::redundant_static_lifetimes)] -#![allow(clippy::needless_borrow)] -#![allow(clippy::clone_on_copy)] - -type Op_StderrWrite = roc_std::RocStr; -type Op_StdoutWrite = roc_std::RocStr; -type TODO_roc_function_69 = roc_std::RocStr; -type TODO_roc_function_70 = roc_std::RocStr; - -#[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" -))] -#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)] -#[repr(u8)] -pub enum discriminant_Op { - Done = 0, - StderrWrite = 1, - StdoutWrite = 2, -} - -impl core::fmt::Debug for discriminant_Op { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Self::Done => f.write_str("discriminant_Op::Done"), - Self::StderrWrite => f.write_str("discriminant_Op::StderrWrite"), - Self::StdoutWrite => f.write_str("discriminant_Op::StdoutWrite"), - } - } -} - -#[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" -))] -#[repr(transparent)] -pub struct Op { - pointer: *mut union_Op, -} - -#[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" -))] -#[repr(C)] -union union_Op { - StderrWrite: core::mem::ManuallyDrop, - StdoutWrite: core::mem::ManuallyDrop, - _sizer: [u8; 8], -} - -#[cfg(any( - target_arch = "arm", - target_arch = "arm", - target_arch = "aarch64", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86", - target_arch = "x86_64", - target_arch = "x86_64" -))] -//TODO HAS CLOSURE 2 -#[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" -))] -#[repr(C)] -pub struct RocFunction_66 { - pub closure_data: roc_std::RocList, -} - -impl RocFunction_66 { - pub fn force_thunk(mut self, arg_0: ()) -> Op { - extern "C" { - fn roc__mainForHost_0_caller(arg_0: &(), closure_data: *mut u8, output: *mut Op); - } - - let mut output = std::mem::MaybeUninit::uninit(); - let ptr = self.closure_data.as_mut_ptr(); - unsafe { roc__mainForHost_0_caller(&arg_0, ptr, output.as_mut_ptr()) }; - unsafe { output.assume_init() } - } -} - -#[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" -))] -#[repr(C)] -pub struct RocFunction_67 { - pub closure_data: roc_std::RocList, -} - -impl RocFunction_67 { - pub fn force_thunk(mut self, arg_0: ()) -> Op { - extern "C" { - fn roc__mainForHost_1_caller(arg_0: &(), closure_data: *mut u8, output: *mut Op); - } - - let mut output = std::mem::MaybeUninit::uninit(); - let ptr = self.closure_data.as_mut_ptr(); - unsafe { roc__mainForHost_1_caller(&arg_0, ptr, output.as_mut_ptr()) }; - unsafe { output.assume_init() } - } -} - -impl Op { - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - #[inline(always)] - fn storage(&self) -> Option<&core::cell::Cell> { - let mask = match std::mem::size_of::() { - 4 => 0b11, - 8 => 0b111, - _ => unreachable!(), - }; - - // NOTE: pointer provenance is probably lost here - let unmasked_address = (self.pointer as usize) & !mask; - let untagged = unmasked_address as *const core::cell::Cell; - - if untagged.is_null() { - None - } else { - unsafe { Some(&*untagged.sub(1)) } - } - } - - #[cfg(any(target_arch = "arm", target_arch = "wasm32", target_arch = "x86"))] - /// Returns which variant this tag union holds. Note that this never includes a payload! - pub fn discriminant(&self) -> discriminant_Op { - // The discriminant is stored in the unused bytes at the end of the recursive pointer - unsafe { core::mem::transmute::((self.pointer as u8) & 0b11) } - } - - #[cfg(any(target_arch = "arm", target_arch = "wasm32", target_arch = "x86"))] - /// Internal helper - fn tag_discriminant(pointer: *mut union_Op, discriminant: discriminant_Op) -> *mut union_Op { - // The discriminant is stored in the unused bytes at the end of the union pointer - let untagged = (pointer as usize) & (!0b11 as usize); - let tagged = untagged | (discriminant as usize); - - tagged as *mut union_Op - } - - #[cfg(any(target_arch = "arm", target_arch = "wasm32", target_arch = "x86"))] - /// Internal helper - fn union_pointer(&self) -> *mut union_Op { - // The discriminant is stored in the unused bytes at the end of the union pointer - ((self.pointer as usize) & (!0b11 as usize)) as *mut union_Op - } - - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - /// A tag named Done, which has no payload. - pub const Done: Self = Self { - pointer: core::ptr::null_mut(), - }; - - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StderrWrite` and return its payload at index 0. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StderrWrite`. - pub unsafe fn get_StderrWrite_0(&self) -> roc_std::RocStr { - debug_assert_eq!(self.discriminant(), discriminant_Op::StderrWrite); - - extern "C" { - #[link_name = "roc__getter__2_generic"] - fn getter(_: *mut roc_std::RocStr, _: *const Op); - } - - let mut ret = core::mem::MaybeUninit::uninit(); - getter(ret.as_mut_ptr(), self); - ret.assume_init() - } - - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StderrWrite` and return its payload at index 1. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StderrWrite`. - pub unsafe fn get_StderrWrite_1(&self) -> RocFunction_67 { - debug_assert_eq!(self.discriminant(), discriminant_Op::StderrWrite); - - extern "C" { - #[link_name = "roc__getter__3_size"] - fn size() -> usize; - - #[link_name = "roc__getter__3_generic"] - fn getter(_: *mut u8, _: *const Op); - } - - // allocate memory to store this variably-sized value - // allocates with roc_alloc, but that likely still uses the heap - let it = std::iter::repeat(0xAAu8).take(size()); - let mut bytes = roc_std::RocList::from_iter(it); - - getter(bytes.as_mut_ptr(), self); - - RocFunction_67 { - closure_data: bytes, - } - } - - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - /// Construct a tag named `StderrWrite`, with the appropriate payload - pub fn StderrWrite(arg: Op_StderrWrite) -> Self { - let size = core::mem::size_of::(); - let align = core::mem::align_of::() as u32; - - unsafe { - let ptr = roc_std::roc_alloc_refcounted::(); - - *ptr = union_Op { - StderrWrite: core::mem::ManuallyDrop::new(arg), - }; - - Self { - pointer: Self::tag_discriminant(ptr, discriminant_Op::StderrWrite), - } - } - } - - #[cfg(any(target_arch = "arm", target_arch = "wasm32", target_arch = "x86"))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StderrWrite` and convert it to `StderrWrite`'s payload. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StderrWrite`. - pub unsafe fn into_StderrWrite(mut self) -> Op_StderrWrite { - debug_assert_eq!(self.discriminant(), discriminant_Op::StderrWrite); - let payload = { - let ptr = (self.pointer as usize & !0b11) as *mut union_Op; - let mut uninitialized = core::mem::MaybeUninit::uninit(); - let swapped = unsafe { - core::mem::replace( - &mut (*ptr).StderrWrite, - core::mem::ManuallyDrop::new(uninitialized.assume_init()), - ) - }; - - core::mem::forget(self); - - core::mem::ManuallyDrop::into_inner(swapped) - }; - - payload - } - - #[cfg(any(target_arch = "arm", target_arch = "wasm32", target_arch = "x86"))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StderrWrite` and return its payload. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StderrWrite`. - pub unsafe fn as_StderrWrite(&self) -> &Op_StderrWrite { - debug_assert_eq!(self.discriminant(), discriminant_Op::StderrWrite); - let payload = { - let ptr = (self.pointer as usize & !0b11) as *mut union_Op; - - unsafe { &(*ptr).StderrWrite } - }; - - &payload - } - - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StdoutWrite` and return its payload at index 0. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StdoutWrite`. - pub unsafe fn get_StdoutWrite_0(&self) -> roc_std::RocStr { - debug_assert_eq!(self.discriminant(), discriminant_Op::StdoutWrite); - - extern "C" { - #[link_name = "roc__getter__2_generic"] - fn getter(_: *mut roc_std::RocStr, _: *const Op); - } - - let mut ret = core::mem::MaybeUninit::uninit(); - getter(ret.as_mut_ptr(), self); - ret.assume_init() - } - - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StdoutWrite` and return its payload at index 1. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StdoutWrite`. - pub unsafe fn get_StdoutWrite_1(&self) -> RocFunction_66 { - debug_assert_eq!(self.discriminant(), discriminant_Op::StdoutWrite); - - extern "C" { - #[link_name = "roc__getter__3_size"] - fn size() -> usize; - - #[link_name = "roc__getter__3_generic"] - fn getter(_: *mut u8, _: *const Op); - } - - // allocate memory to store this variably-sized value - // allocates with roc_alloc, but that likely still uses the heap - let it = std::iter::repeat(0xAAu8).take(size()); - let mut bytes = roc_std::RocList::from_iter(it); - - getter(bytes.as_mut_ptr(), self); - - RocFunction_66 { - closure_data: bytes, - } - } - - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - /// Construct a tag named `StdoutWrite`, with the appropriate payload - pub fn StdoutWrite(arg: Op_StdoutWrite) -> Self { - let size = core::mem::size_of::(); - let align = core::mem::align_of::() as u32; - - unsafe { - let ptr = roc_std::roc_alloc_refcounted::(); - - *ptr = union_Op { - StdoutWrite: core::mem::ManuallyDrop::new(arg), - }; - - Self { - pointer: Self::tag_discriminant(ptr, discriminant_Op::StdoutWrite), - } - } - } - - #[cfg(any(target_arch = "arm", target_arch = "wasm32", target_arch = "x86"))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StdoutWrite` and convert it to `StdoutWrite`'s payload. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StdoutWrite`. - pub unsafe fn into_StdoutWrite(mut self) -> Op_StdoutWrite { - debug_assert_eq!(self.discriminant(), discriminant_Op::StdoutWrite); - let payload = { - let ptr = (self.pointer as usize & !0b11) as *mut union_Op; - let mut uninitialized = core::mem::MaybeUninit::uninit(); - let swapped = unsafe { - core::mem::replace( - &mut (*ptr).StdoutWrite, - core::mem::ManuallyDrop::new(uninitialized.assume_init()), - ) - }; - - core::mem::forget(self); - - core::mem::ManuallyDrop::into_inner(swapped) - }; - - payload - } - - #[cfg(any(target_arch = "arm", target_arch = "wasm32", target_arch = "x86"))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StdoutWrite` and return its payload. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StdoutWrite`. - pub unsafe fn as_StdoutWrite(&self) -> &Op_StdoutWrite { - debug_assert_eq!(self.discriminant(), discriminant_Op::StdoutWrite); - let payload = { - let ptr = (self.pointer as usize & !0b11) as *mut union_Op; - - unsafe { &(*ptr).StdoutWrite } - }; - - &payload - } - - #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] - /// Returns which variant this tag union holds. Note that this never includes a payload! - pub fn discriminant(&self) -> discriminant_Op { - // The discriminant is stored in the unused bytes at the end of the recursive pointer - unsafe { core::mem::transmute::((self.pointer as u8) & 0b111) } - } - - #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] - /// Internal helper - fn tag_discriminant(pointer: *mut union_Op, discriminant: discriminant_Op) -> *mut union_Op { - // The discriminant is stored in the unused bytes at the end of the union pointer - let untagged = (pointer as usize) & (!0b111 as usize); - let tagged = untagged | (discriminant as usize); - - tagged as *mut union_Op - } - - #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] - /// Internal helper - fn union_pointer(&self) -> *mut union_Op { - // The discriminant is stored in the unused bytes at the end of the union pointer - ((self.pointer as usize) & (!0b111 as usize)) as *mut union_Op - } - - #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StderrWrite` and convert it to `StderrWrite`'s payload. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StderrWrite`. - pub unsafe fn into_StderrWrite(mut self) -> Op_StderrWrite { - debug_assert_eq!(self.discriminant(), discriminant_Op::StderrWrite); - let payload = { - let ptr = (self.pointer as usize & !0b111) as *mut union_Op; - let mut uninitialized = core::mem::MaybeUninit::uninit(); - let swapped = unsafe { - core::mem::replace( - &mut (*ptr).StderrWrite, - core::mem::ManuallyDrop::new(uninitialized.assume_init()), - ) - }; - - core::mem::forget(self); - - core::mem::ManuallyDrop::into_inner(swapped) - }; - - payload - } - - #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StderrWrite` and return its payload. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StderrWrite`. - pub unsafe fn as_StderrWrite(&self) -> &Op_StderrWrite { - debug_assert_eq!(self.discriminant(), discriminant_Op::StderrWrite); - let payload = { - let ptr = (self.pointer as usize & !0b111) as *mut union_Op; - - unsafe { &(*ptr).StderrWrite } - }; - - &payload - } - - #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StdoutWrite` and convert it to `StdoutWrite`'s payload. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StdoutWrite`. - pub unsafe fn into_StdoutWrite(mut self) -> Op_StdoutWrite { - debug_assert_eq!(self.discriminant(), discriminant_Op::StdoutWrite); - let payload = { - let ptr = (self.pointer as usize & !0b111) as *mut union_Op; - let mut uninitialized = core::mem::MaybeUninit::uninit(); - let swapped = unsafe { - core::mem::replace( - &mut (*ptr).StdoutWrite, - core::mem::ManuallyDrop::new(uninitialized.assume_init()), - ) - }; - - core::mem::forget(self); - - core::mem::ManuallyDrop::into_inner(swapped) - }; - - payload - } - - #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] - /// Unsafely assume this `Op` has a `.discriminant()` of `StdoutWrite` and return its payload. - /// (Always examine `.discriminant()` first to make sure this is the correct variant!) - /// Panics in debug builds if the `.discriminant()` doesn't return `StdoutWrite`. - pub unsafe fn as_StdoutWrite(&self) -> &Op_StdoutWrite { - debug_assert_eq!(self.discriminant(), discriminant_Op::StdoutWrite); - let payload = { - let ptr = (self.pointer as usize & !0b111) as *mut union_Op; - - unsafe { &(*ptr).StdoutWrite } - }; - - &payload - } -} - -impl Drop for Op { - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - fn drop(&mut self) { - // We only need to do any work if there's actually a heap-allocated payload. - if let Some(storage) = self.storage() { - let mut new_storage = storage.get(); - - // Decrement the refcount - let needs_dealloc = !new_storage.is_readonly() && new_storage.decrease(); - - if needs_dealloc { - // Drop the payload first. - match self.discriminant() { - discriminant_Op::Done => {} - discriminant_Op::StderrWrite => unsafe { - core::mem::ManuallyDrop::drop(&mut (&mut *self.union_pointer()).StderrWrite) - }, - discriminant_Op::StdoutWrite => unsafe { - core::mem::ManuallyDrop::drop(&mut (&mut *self.union_pointer()).StdoutWrite) - }, - } - - // Dealloc the pointer - let alignment = - core::mem::align_of::().max(core::mem::align_of::()); - - unsafe { - crate::roc_dealloc(storage.as_ptr().cast(), alignment as u32); - } - } else { - // Write the storage back. - storage.set(new_storage); - } - } - } -} - -impl Eq for Op {} - -impl PartialEq for Op { - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - fn eq(&self, other: &Self) -> bool { - if self.discriminant() != other.discriminant() { - return false; - } - - unsafe { - match self.discriminant() { - discriminant_Op::Done => true, - discriminant_Op::StderrWrite => { - (&*self.union_pointer()).StderrWrite == (&*other.union_pointer()).StderrWrite - } - discriminant_Op::StdoutWrite => { - (&*self.union_pointer()).StdoutWrite == (&*other.union_pointer()).StdoutWrite - } - } - } - } -} - -impl PartialOrd for Op { - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - fn partial_cmp(&self, other: &Self) -> Option { - match self.discriminant().partial_cmp(&other.discriminant()) { - Some(core::cmp::Ordering::Equal) => {} - not_eq => return not_eq, - } - - unsafe { - match self.discriminant() { - discriminant_Op::Done => Some(core::cmp::Ordering::Equal), - discriminant_Op::StderrWrite => (&*self.union_pointer()) - .StderrWrite - .partial_cmp(&(&*other.union_pointer()).StderrWrite), - discriminant_Op::StdoutWrite => (&*self.union_pointer()) - .StdoutWrite - .partial_cmp(&(&*other.union_pointer()).StdoutWrite), - } - } - } -} - -impl Ord for Op { - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - match self.discriminant().cmp(&other.discriminant()) { - core::cmp::Ordering::Equal => {} - not_eq => return not_eq, - } - - unsafe { - match self.discriminant() { - discriminant_Op::Done => core::cmp::Ordering::Equal, - discriminant_Op::StderrWrite => (&*self.union_pointer()) - .StderrWrite - .cmp(&(&*other.union_pointer()).StderrWrite), - discriminant_Op::StdoutWrite => (&*self.union_pointer()) - .StdoutWrite - .cmp(&(&*other.union_pointer()).StdoutWrite), - } - } - } -} - -impl Clone for Op { - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - fn clone(&self) -> Self { - if let Some(storage) = self.storage() { - let mut new_storage = storage.get(); - if !new_storage.is_readonly() { - new_storage.increment_reference_count(); - storage.set(new_storage); - } - } - - Self { - pointer: self.pointer, - } - } -} - -impl core::hash::Hash for Op { - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - fn hash(&self, state: &mut H) { - match self.discriminant() { - discriminant_Op::Done => discriminant_Op::Done.hash(state), - discriminant_Op::StderrWrite => unsafe { - discriminant_Op::StderrWrite.hash(state); - (&*self.union_pointer()).StderrWrite.hash(state); - }, - discriminant_Op::StdoutWrite => unsafe { - discriminant_Op::StdoutWrite.hash(state); - (&*self.union_pointer()).StdoutWrite.hash(state); - }, - } - } -} - -impl core::fmt::Debug for Op { - #[cfg(any( - target_arch = "arm", - target_arch = "aarch64", - target_arch = "wasm32", - target_arch = "x86", - target_arch = "x86_64" - ))] - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.write_str("Op::")?; - - unsafe { - match self.discriminant() { - discriminant_Op::Done => f.write_str("Done"), - discriminant_Op::StderrWrite => f - .debug_tuple("StderrWrite") - // TODO HAS CLOSURE - .finish(), - discriminant_Op::StdoutWrite => f - .debug_tuple("StdoutWrite") - // TODO HAS CLOSURE - .finish(), - } - } - } -} diff --git a/examples/platform-switching/rust-platform/src/lib.rs b/examples/platform-switching/rust-platform/src/lib.rs index 94dacefe6d..11959f31d2 100644 --- a/examples/platform-switching/rust-platform/src/lib.rs +++ b/examples/platform-switching/rust-platform/src/lib.rs @@ -113,7 +113,7 @@ pub unsafe extern "C" fn roc_shm_open( /// /// TODO #[no_mangle] -pub extern "C" fn rust_main() -> i32 { +pub extern "C" fn main() -> i32 { let mut roc_str = RocStr::default(); unsafe { roc_main(&mut roc_str) }; @@ -126,6 +126,5 @@ pub extern "C" fn rust_main() -> i32 { panic!("Failed to flush stdout: {:?}", e); } - // Exit code 0 } diff --git a/examples/platform-switching/rust-platform/src/main.rs b/examples/platform-switching/rust-platform/src/main.rs deleted file mode 100644 index 0765384f29..0000000000 --- a/examples/platform-switching/rust-platform/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - std::process::exit(host::rust_main() as _); -}