Merge pull request #5093 from roc-lang/glue-getters-rtfeldman

Glue for functions and closures
This commit is contained in:
Richard Feldman 2023-03-21 08:12:19 -04:00 committed by GitHub
commit 2276c78d9f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
64 changed files with 3864 additions and 1101 deletions

View file

@ -18,9 +18,9 @@ jobs:
run: |
./ci/get_releases_json.sh
- run: curl -OL $(./ci/get_latest_release_url.sh linux_x86_64)
- run: curl -OL $(./ci/get_latest_release_url.sh macos_x86_64)
- run: curl -OL $(./ci/get_latest_release_url.sh silicon)
- run: curl -OL $(./ci/get_latest_release_url.sh linuxTESTING_x86_64)
- run: curl -OL $(./ci/get_latest_release_url.sh macosTESTING_x86_64)
- run: curl -OL $(./ci/get_latest_release_url.sh macosTESTING_apple_silicon)
- name: Save roc_nightly archives
uses: actions/upload-artifact@v3
@ -39,15 +39,15 @@ jobs:
- name: build basic-cli with surgical linker and also with legacy linker
env:
CARGO_BUILD_TARGET: x86_64-unknown-linux-musl
run: ./ci/build_basic_cli.sh linux_x86_64 "--linker legacy"
run: ./ci/build_basic_cli.sh linuxTESTING_x86_64 "--linker legacy"
- name: Save .rh1, .rm2 and .o file
- name: Save .rh, .rm and .o file
uses: actions/upload-artifact@v3
with:
name: linux-x86_64-files
path: |
basic-cli/src/metadata_linux-x86_64.rm2
basic-cli/src/linux-x86_64.rh1
basic-cli/src/metadata_linux-x86_64.rm
basic-cli/src/linux-x86_64.rh
basic-cli/src/linux-x86_64.o
build-macos-x86_64-files:
@ -59,7 +59,7 @@ jobs:
- name: Download the previously uploaded roc_nightly archives
uses: actions/download-artifact@v3
- run: ./ci/build_basic_cli.sh macos_x86_64
- run: ./ci/build_basic_cli.sh macosTESTING_x86_64
- name: Save .o files
uses: actions/upload-artifact@v3
@ -78,7 +78,7 @@ jobs:
- name: Download the previously uploaded roc_nightly archives
uses: actions/download-artifact@v3
- run: ./ci/build_basic_cli.sh silicon
- run: ./ci/build_basic_cli.sh macosTESTING_apple_silicon
- name: Save macos-arm64.o file
uses: actions/upload-artifact@v3
@ -120,13 +120,9 @@ jobs:
- run: cp macos-x86_64-files/* ./basic-cli/src
# gz for quick test, br takes longer
# change to tar.gz for fast test build
- run: |
if [ "$GITHUB_EVENT_NAME" == 'workflow_dispatch' ]; then
echo "ARCHIVE_FORMAT='.tar.br'" >> "$GITHUB_ENV"
else
echo "ARCHIVE_FORMAT='.tar.gz'" >> "$GITHUB_ENV"
fi
- run: ./roc_nightly/roc build --bundle=${{ env.ARCHIVE_FORMAT }} ./basic-cli/src/main.roc

1
Cargo.lock generated
View file

@ -3495,6 +3495,7 @@ dependencies = [
"roc_collections",
"roc_error_macros",
"roc_load",
"roc_module",
"roc_mono",
"roc_packaging",
"roc_reporting",

View file

@ -5,6 +5,10 @@ set -euxo pipefail
git clone https://github.com/roc-lang/basic-cli.git
cd basic-cli
git checkout new-release
cd ..
if [ "$(uname -m)" == "x86_64" ] && [ "$(uname -s)" == "Linux" ]; then
sudo apt-get install musl-tools
cd basic-cli/src # we cd to install the target for the right rust version

View file

@ -474,6 +474,7 @@ mod cli_run {
}
#[test]
#[serial(basic_cli_url)]
#[cfg_attr(windows, ignore)]
fn hello_world() {
test_roc_app_slim(
@ -879,6 +880,7 @@ mod cli_run {
#[test]
#[serial(parser_package)]
#[serial(basic_cli_url)]
#[cfg_attr(windows, ignore)]
fn parse_letter_counts() {
test_roc_app_slim(

View file

@ -25,10 +25,10 @@ const mem = std.mem;
const Allocator = mem.Allocator;
extern fn roc__mainForHost_1_exposed_generic([*]u8) void;
extern fn roc__mainForHost_size() i64;
extern fn roc__mainForHost_1__Fx_caller(*const u8, [*]u8, [*]u8) void;
extern fn roc__mainForHost_1__Fx_size() i64;
extern fn roc__mainForHost_1__Fx_result_size() i64;
extern fn roc__mainForHost_1_exposed_size() i64;
extern fn roc__mainForHost_0_caller(*const u8, [*]u8, [*]u8) void;
extern fn roc__mainForHost_0_size() i64;
extern fn roc__mainForHost_0_result_size() i64;
const Align = 2 * @alignOf(usize);
extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque;
@ -123,7 +123,7 @@ 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 size = std.math.max(@intCast(usize, roc__mainForHost_1_exposed_size()), 8);
const raw_output = roc_alloc(@intCast(usize, size), @alignOf(u64)).?;
var output = @ptrCast([*]u8, raw_output);
@ -155,7 +155,7 @@ fn call_the_closure(closure_data_pointer: [*]u8) void {
const allocator = std.heap.page_allocator;
// 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(roc__mainForHost_1__Fx_result_size(), 8);
const size = std.math.max(roc__mainForHost_0_result_size(), 8);
const raw_output = allocator.allocAdvanced(u8, @alignOf(u64), @intCast(usize, size), .at_least) catch unreachable;
var output = @ptrCast([*]u8, raw_output);
@ -165,7 +165,7 @@ fn call_the_closure(closure_data_pointer: [*]u8) void {
const flags: u8 = 0;
roc__mainForHost_1__Fx_caller(&flags, closure_data_pointer, output);
roc__mainForHost_0_caller(&flags, closure_data_pointer, output);
// The closure returns result, nothing interesting to do with it
return;

View file

@ -196,7 +196,12 @@ fn gen_from_mono_module_llvm<'a>(
OptLevel::Normal | OptLevel::Size | OptLevel::Optimize => LlvmBackendMode::Binary,
},
exposed_to_host: loaded.exposed_to_host.values.keys().copied().collect(),
exposed_to_host: loaded
.exposed_to_host
.top_level_values
.keys()
.copied()
.collect(),
};
// does not add any externs for this mode (we have a host) but cleans up some functions around
@ -224,6 +229,7 @@ fn gen_from_mono_module_llvm<'a>(
loaded.procedures,
entry_point,
Some(&app_ll_file),
&loaded.glue_layouts,
);
env.dibuilder.finalize();
@ -500,7 +506,7 @@ fn gen_from_mono_module_dev_wasm32<'a>(
let exposed_to_host = loaded
.exposed_to_host
.values
.top_level_values
.keys()
.copied()
.collect::<MutSet<_>>();
@ -571,7 +577,7 @@ fn gen_from_mono_module_dev_assembly<'a>(
let env = roc_gen_dev::Env {
arena,
module_id,
exposed_to_host: exposed_to_host.values.keys().copied().collect(),
exposed_to_host: exposed_to_host.top_level_values.keys().copied().collect(),
lazy_literals,
generate_allocators,
};
@ -771,25 +777,10 @@ fn build_loaded_file<'a>(
// Also, we should no longer need to do this once we have platforms on
// a package repository, as we can then get prebuilt platforms from there.
let exposed_values = loaded
.exposed_to_host
.values
.keys()
.map(|x| x.as_str(&loaded.interns).to_string())
.collect();
let exposed_closure_types = loaded
.exposed_to_host
.closure_types
.iter()
.map(|x| {
format!(
"{}_{}",
x.module_string(&loaded.interns),
x.as_str(&loaded.interns)
)
})
.collect();
let dll_stub_symbols = roc_linker::ExposedSymbols::from_exposed_to_host(
&loaded.interns,
&loaded.exposed_to_host,
);
let join_handle = spawn_rebuild_thread(
code_gen_options.opt_level,
@ -798,8 +789,7 @@ fn build_loaded_file<'a>(
preprocessed_host_path.clone(),
output_exe_path.clone(),
target,
exposed_values,
exposed_closure_types,
dll_stub_symbols,
);
Some(join_handle)
@ -993,6 +983,13 @@ fn invalid_prebuilt_platform(prebuilt_requested: bool, preprocessed_host_path: P
false => "",
};
let preprocessed_host_path_str = preprocessed_host_path.to_string_lossy();
let extra_err_msg = if preprocessed_host_path_str.ends_with(".rh") {
"\n\n\tNote: If the platform does have an .rh1 file but no .rh file, it's because it's been built with an older version of roc. Contact the author to release a new build of the platform using a roc release newer than March 21 2023.\n"
} else {
""
};
eprintln!(
indoc::indoc!(
r#"
@ -1000,13 +997,14 @@ fn invalid_prebuilt_platform(prebuilt_requested: bool, preprocessed_host_path: P
{}
However, it was not there!
However, it was not there!{}
If you have the platform's source code locally, you may be able to generate it by re-running this command with --prebuilt-platform=false
"#
),
prefix,
preprocessed_host_path.to_string_lossy(),
extra_err_msg
);
}
@ -1018,8 +1016,7 @@ fn spawn_rebuild_thread(
preprocessed_host_path: PathBuf,
output_exe_path: PathBuf,
target: &Triple,
exported_symbols: Vec<String>,
exported_closure_types: Vec<String>,
dll_stub_symbols: Vec<String>,
) -> std::thread::JoinHandle<u128> {
let thread_local_target = target.clone();
std::thread::spawn(move || {
@ -1042,13 +1039,12 @@ fn spawn_rebuild_thread(
preprocess_host_wasm32(host_dest.as_path(), &preprocessed_host_path);
}
LinkingStrategy::Surgical => {
build_and_preprocess_host(
build_and_preprocess_host_lowlevel(
opt_level,
&thread_local_target,
platform_main_roc.as_path(),
preprocessed_host_path.as_path(),
exported_symbols,
exported_closure_types,
&dll_stub_symbols,
);
// Copy preprocessed host to executable location.
@ -1074,15 +1070,31 @@ pub fn build_and_preprocess_host(
target: &Triple,
platform_main_roc: &Path,
preprocessed_host_path: &Path,
exposed_to_host: Vec<String>,
exported_closure_types: Vec<String>,
exposed_symbols: roc_linker::ExposedSymbols,
) {
let (stub_lib, stub_dll_symbols) = roc_linker::generate_stub_lib_from_loaded(
let stub_dll_symbols = exposed_symbols.stub_dll_symbols();
build_and_preprocess_host_lowlevel(
opt_level,
target,
platform_main_roc,
exposed_to_host,
exported_closure_types,
);
preprocessed_host_path,
&stub_dll_symbols,
)
}
fn build_and_preprocess_host_lowlevel(
opt_level: OptLevel,
target: &Triple,
platform_main_roc: &Path,
preprocessed_host_path: &Path,
stub_dll_symbols: &[String],
) {
let stub_lib =
roc_linker::generate_stub_lib_from_loaded(target, platform_main_roc, stub_dll_symbols);
debug_assert!(stub_lib.exists());
rebuild_host(opt_level, target, platform_main_roc, Some(&stub_lib));
roc_linker::preprocess_host(
@ -1090,7 +1102,7 @@ pub fn build_and_preprocess_host(
platform_main_roc,
preprocessed_host_path,
&stub_lib,
&stub_dll_symbols,
stub_dll_symbols,
)
}

View file

@ -39,7 +39,7 @@ use roc_debug_flags::dbg_do;
use roc_debug_flags::ROC_PRINT_LLVM_FN_VERIFICATION;
use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_mono::ir::{
BranchInfo, CallType, CrashTag, EntryPoint, HostExposedLambdaSet, JoinPointId,
BranchInfo, CallType, CrashTag, EntryPoint, GlueLayouts, HostExposedLambdaSet, JoinPointId,
ListLiteralElement, ModifyRc, OptLevel, ProcLayout, SingleEntryPoint,
};
use roc_mono::layout::{
@ -3841,6 +3841,7 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx, 'env>(
Some(env.context.i64_type().as_basic_type_enum()),
&[],
);
let size_function_name: String = format!("roc__{}_size", ident_string);
let size_function = add_func(
@ -3972,13 +3973,17 @@ fn expose_function_to_host_help_c_abi_v2<'a, 'ctx, 'env>(
&param_types[..param_types.len().saturating_sub(1)],
)
}
(RocReturn::Return | RocReturn::ByPointer, CCReturn::Void) => {
(RocReturn::Return, CCReturn::Void) => {
// the roc function returns a unit value. like `{}` or `{ { {}, {} }, {} }`.
// In C, this is modelled as a function returning void
(&params[..], &param_types[..])
}
(RocReturn::ByPointer, CCReturn::Void) => {
// the roc function returns a unit value. like `{}` or `{ { {}, {} }, {} }`.
// In C, this is modelled as a function returning void
(
&params[..],
// &param_types[..param_types.len().saturating_sub(1)],
&param_types[..],
&param_types[..param_types.len().saturating_sub(1)],
)
}
_ => (&params[..], &param_types[..]),
@ -4149,7 +4154,7 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>(
Some(env.context.i64_type().as_basic_type_enum()),
&[],
);
let size_function_name: String = format!("roc__{}_size", ident_string);
let size_function_name: String = format!("{}_size", c_function_name);
let size_function = add_func(
env.context,
@ -4609,8 +4614,9 @@ pub fn build_procedures<'a, 'ctx, 'env>(
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
entry_point: EntryPoint<'a>,
debug_output_file: Option<&Path>,
glue_layouts: &GlueLayouts<'a>,
) {
build_procedures_help(
let mod_solutions = build_procedures_help(
env,
layout_interner,
opt_level,
@ -4618,6 +4624,43 @@ pub fn build_procedures<'a, 'ctx, 'env>(
entry_point,
debug_output_file,
);
let niche = Niche::NONE;
for (symbol, top_level) in glue_layouts.getters.iter().copied() {
let it = top_level.arguments.iter().copied();
let bytes = roc_alias_analysis::func_name_bytes_help(symbol, it, niche, top_level.result);
let func_name = FuncName(&bytes);
let func_solutions = mod_solutions.func_solutions(func_name).unwrap();
let mut it = func_solutions.specs();
let Some(func_spec) = it.next() else {
// TODO this means a function was not considered host-exposed in mono
continue;
};
debug_assert!(
it.next().is_none(),
"we expect only one specialization of this symbol"
);
// NOTE fake layout; it is only used for debug prints
let getter_fn =
function_value_by_func_spec(env, *func_spec, symbol, &[], niche, Layout::UNIT);
let name = getter_fn.get_name().to_str().unwrap();
let getter_name = symbol.as_str(&env.interns);
// Add the getter function to the module.
let _ = expose_function_to_host_help_c_abi(
env,
layout_interner,
name,
getter_fn,
top_level.arguments,
top_level.result,
getter_name,
);
}
}
pub fn build_wasm_test_wrapper<'a, 'ctx, 'env>(
@ -4937,13 +4980,10 @@ fn expose_alias_to_host<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_interner: &mut STLayoutInterner<'a>,
mod_solutions: &'a ModSolutions,
proc_name: LambdaName,
fn_name: &str,
alias_symbol: Symbol,
hels: &HostExposedLambdaSet<'a>,
) {
let ident_string = proc_name.name().as_str(&env.interns);
let fn_name: String = format!("{}_1", ident_string);
match hels.raw_function_layout {
RawFunctionLayout::Function(arguments, closure, result) => {
// define closure size and return value size, e.g.
@ -4989,7 +5029,7 @@ fn expose_alias_to_host<'a, 'ctx, 'env>(
build_closure_caller(
env,
layout_interner,
&fn_name,
fn_name,
evaluator,
alias_symbol,
arguments,
@ -5008,7 +5048,7 @@ fn expose_alias_to_host<'a, 'ctx, 'env>(
build_host_exposed_alias_size_help(
env,
&fn_name,
fn_name,
alias_symbol,
Some("result"),
result_type,
@ -5055,13 +5095,8 @@ fn build_closure_caller<'a, 'ctx, 'env>(
// STEP 1: build function header
// e.g. `roc__main_1_Fx_caller`
let function_name = format!(
"roc__{}_{}_{}_caller",
def_name,
alias_symbol.module_string(&env.interns),
alias_symbol.as_str(&env.interns)
);
// e.g. `roc__mainForHost_0_caller` (def_name is `mainForHost_0`)
let function_name = format!("roc__{}_caller", def_name);
let function_spec = FunctionSpec::cconv(env, CCReturn::Void, None, &argument_types);
@ -5172,7 +5207,7 @@ fn build_host_exposed_alias_size<'a, 'r, 'ctx, 'env>(
fn build_host_exposed_alias_size_help<'a, 'ctx, 'env>(
env: &'a Env<'a, 'ctx, 'env>,
def_name: &str,
alias_symbol: Symbol,
_alias_symbol: Symbol,
opt_label: Option<&str>,
basic_type: BasicTypeEnum<'ctx>,
) {
@ -5182,20 +5217,9 @@ fn build_host_exposed_alias_size_help<'a, 'ctx, 'env>(
let i64 = env.context.i64_type().as_basic_type_enum();
let size_function_spec = FunctionSpec::cconv(env, CCReturn::Return, Some(i64), &[]);
let size_function_name: String = if let Some(label) = opt_label {
format!(
"roc__{}_{}_{}_{}_size",
def_name,
alias_symbol.module_string(&env.interns),
alias_symbol.as_str(&env.interns),
label
)
format!("roc__{}_{}_size", def_name, label)
} else {
format!(
"roc__{}_{}_{}_size",
def_name,
alias_symbol.module_string(&env.interns),
alias_symbol.as_str(&env.interns)
)
format!("roc__{}_size", def_name,)
};
let size_function = add_func(
@ -5214,7 +5238,7 @@ fn build_host_exposed_alias_size_help<'a, 'ctx, 'env>(
builder.build_return(Some(&size));
}
pub fn build_proc<'a, 'ctx, 'env>(
fn build_proc<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_interner: &mut STLayoutInterner<'a>,
mod_solutions: &'a ModSolutions,
@ -5237,11 +5261,14 @@ pub fn build_proc<'a, 'ctx, 'env>(
}
Binary | BinaryDev => {
for (alias_name, hels) in aliases.iter() {
let ident_string = proc.name.name().as_str(&env.interns);
let fn_name: String = format!("{}_{}", ident_string, hels.id.0);
expose_alias_to_host(
env,
layout_interner,
mod_solutions,
proc.name,
&fn_name,
*alias_name,
hels,
)
@ -6106,7 +6133,7 @@ pub fn add_func<'ctx>(
) -> FunctionValue<'ctx> {
if cfg!(debug_assertions) {
if let Some(func) = module.get_function(name) {
panic!("Attempting to redefine LLVM function {}, which was already defined in this module as:\n\n{:?}", name, func);
panic!("Attempting to redefine LLVM function {}, which was already defined in this module as:\n\n{:#?}", name, func);
}
}

View file

@ -18,8 +18,8 @@ const SKIP_SUBS_CACHE: bool = {
pub use roc_load_internal::docs;
pub use roc_load_internal::file::{
EntryPoint, ExecutionMode, ExpectMetadata, Expectations, LoadConfig, LoadResult, LoadStart,
LoadedModule, LoadingProblem, MonomorphizedModule, Phase, Threading,
EntryPoint, ExecutionMode, ExpectMetadata, Expectations, ExposedToHost, LoadConfig, LoadResult,
LoadStart, LoadedModule, LoadingProblem, MonomorphizedModule, Phase, Threading,
};
#[allow(clippy::too_many_arguments)]

View file

@ -1,7 +1,7 @@
#![allow(clippy::too_many_arguments)]
use crate::docs::ModuleDocumentation;
use bumpalo::Bump;
use bumpalo::{collections::CollectIn, Bump};
use crossbeam::channel::{bounded, Sender};
use crossbeam::deque::{Injector, Stealer, Worker};
use crossbeam::thread;
@ -31,9 +31,10 @@ use roc_module::symbol::{
PackageQualified, Symbol,
};
use roc_mono::ir::{
CapturedSymbols, ExternalSpecializations, PartialProc, Proc, ProcLayout, Procs, ProcsBase,
UpdateModeIds,
CapturedSymbols, ExternalSpecializations, GlueLayouts, LambdaSetId, PartialProc, Proc,
ProcLayout, Procs, ProcsBase, UpdateModeIds,
};
use roc_mono::layout::LayoutInterner;
use roc_mono::layout::{
GlobalLayoutInterner, LambdaName, Layout, LayoutCache, LayoutProblem, Niche, STLayoutInterner,
};
@ -116,11 +117,11 @@ pub enum ExecutionMode {
impl ExecutionMode {
fn goal_phase(&self) -> Phase {
use ExecutionMode::*;
match self {
ExecutionMode::Executable => Phase::MakeSpecializations,
ExecutionMode::Check | ExecutionMode::ExecutableIfCheck | ExecutionMode::Test => {
Phase::SolveTypes
}
Executable => Phase::MakeSpecializations,
Check | ExecutableIfCheck | Test => Phase::SolveTypes,
}
}
@ -765,6 +766,7 @@ pub struct MonomorphizedModule<'a> {
pub timings: MutMap<ModuleId, ModuleTiming>,
pub expectations: VecMap<ModuleId, Expectations>,
pub uses_prebuilt_platform: bool,
pub glue_layouts: GlueLayouts<'a>,
}
/// Values used to render expect output
@ -795,9 +797,12 @@ pub struct Expectations {
#[derive(Clone, Debug, Default)]
pub struct ExposedToHost {
/// usually `mainForHost`
pub values: MutMap<Symbol, Variable>,
pub top_level_values: MutMap<Symbol, Variable>,
/// exposed closure types, typically `Fx`
pub closure_types: Vec<Symbol>,
/// lambda_sets
pub lambda_sets: Vec<(Symbol, LambdaSetId)>,
pub getters: Vec<Symbol>,
}
impl<'a> MonomorphizedModule<'a> {
@ -2775,7 +2780,7 @@ fn update<'a>(
!matches!(state.exec_mode, ExecutionMode::Test);
if add_to_host_exposed {
state.exposed_to_host.values.extend(
state.exposed_to_host.top_level_values.extend(
solved_module
.exposed_vars_by_symbol
.iter()
@ -3102,7 +3107,7 @@ fn update<'a>(
debug_print_ir!(state, &layout_interner, ROC_PRINT_IR_AFTER_RESET_REUSE);
let host_exposed_procs = bumpalo::collections::Vec::from_iter_in(
state.exposed_to_host.values.keys().copied(),
state.exposed_to_host.top_level_values.keys().copied(),
arena,
);
@ -3272,8 +3277,8 @@ fn finish_specialization<'a>(
arena: &'a Bump,
state: State<'a>,
subs: Subs,
layout_interner: STLayoutInterner<'a>,
exposed_to_host: ExposedToHost,
mut layout_interner: STLayoutInterner<'a>,
mut exposed_to_host: ExposedToHost,
module_expectations: VecMap<ModuleId, Expectations>,
) -> Result<MonomorphizedModule<'a>, LoadingProblem<'a>> {
if false {
@ -3303,38 +3308,16 @@ fn finish_specialization<'a>(
all_ident_ids,
};
let State {
toplevel_expects,
procedures,
module_cache,
output_path,
platform_path,
platform_data,
exec_mode,
..
} = state;
let ModuleCache {
type_problems,
can_problems,
sources,
..
} = module_cache;
let sources: MutMap<ModuleId, (PathBuf, Box<str>)> = sources
.into_iter()
.map(|(id, (path, src))| (id, (path, src.into())))
.collect();
let entry_point = {
match exec_mode {
ExecutionMode::Test => EntryPoint::Test,
let interns: &mut Interns = &mut interns;
match state.exec_mode {
ExecutionMode::Test => Ok(EntryPoint::Test),
ExecutionMode::Executable | ExecutionMode::ExecutableIfCheck => {
use PlatformPath::*;
let platform_path = match platform_path {
let platform_path = match &state.platform_path {
Valid(To::ExistingPackage(shorthand)) => {
match (*state.arc_shorthands).lock().get(shorthand) {
match state.arc_shorthands.lock().get(shorthand) {
Some(shorthand_path) => shorthand_path.root_module().to_path_buf(),
None => unreachable!(),
}
@ -3346,13 +3329,14 @@ fn finish_specialization<'a>(
}
};
let exposed_symbols_and_layouts = match platform_data {
let exposed_symbols_and_layouts = match state.platform_data {
None => {
let src = &exposed_to_host.values;
let src = &state.exposed_to_host.top_level_values;
let mut buf = bumpalo::collections::Vec::with_capacity_in(src.len(), arena);
for &symbol in src.keys() {
let proc_layout = proc_layout_for(procedures.keys().copied(), symbol);
let proc_layout =
proc_layout_for(state.procedures.keys().copied(), symbol);
buf.push((symbol, proc_layout));
}
@ -3371,7 +3355,8 @@ fn finish_specialization<'a>(
for (loc_name, _loc_typed_ident) in provides {
let ident_id = ident_ids.get_or_insert(loc_name.value.as_str());
let symbol = Symbol::new(module_id, ident_id);
let proc_layout = proc_layout_for(procedures.keys().copied(), symbol);
let proc_layout =
proc_layout_for(state.procedures.keys().copied(), symbol);
buf.push((symbol, proc_layout));
}
@ -3380,14 +3365,84 @@ fn finish_specialization<'a>(
}
};
EntryPoint::Executable {
Ok(EntryPoint::Executable {
exposed_to_host: exposed_symbols_and_layouts,
platform_path,
}
})
}
ExecutionMode::Check => unreachable!(),
}
};
}?;
let State {
toplevel_expects,
procedures,
module_cache,
output_path,
platform_data,
..
} = state;
let ModuleCache {
type_problems,
can_problems,
sources,
..
} = module_cache;
let sources: MutMap<ModuleId, (PathBuf, Box<str>)> = sources
.into_iter()
.map(|(id, (path, src))| (id, (path, src.into())))
.collect();
let module_id = state.root_id;
let mut glue_getters = Vec::new();
// the REPL does not have any platform data
if let (
EntryPoint::Executable {
exposed_to_host: exposed_top_levels,
..
},
Some(platform_data),
) = (&entry_point, platform_data.as_ref())
{
// Expose glue for the platform, not for the app module!
let module_id = platform_data.module_id;
for (_name, proc_layout) in exposed_top_levels.iter() {
let ret = &proc_layout.result;
for in_layout in proc_layout.arguments.iter().chain([ret]) {
let layout = layout_interner.get(*in_layout);
let ident_ids = interns.all_ident_ids.get_mut(&module_id).unwrap();
let all_glue_procs = roc_mono::ir::generate_glue_procs(
module_id,
ident_ids,
arena,
&mut layout_interner,
arena.alloc(layout),
);
let lambda_set_names = all_glue_procs
.extern_names
.iter()
.map(|(lambda_set_id, _)| (*_name, *lambda_set_id));
exposed_to_host.lambda_sets.extend(lambda_set_names);
let getter_names = all_glue_procs
.getters
.iter()
.flat_map(|(_, glue_procs)| glue_procs.iter().map(|glue_proc| glue_proc.name));
exposed_to_host.getters.extend(getter_names);
glue_getters.extend(all_glue_procs.getters.iter().flat_map(|(_, glue_procs)| {
glue_procs
.iter()
.map(|glue_proc| (glue_proc.name, glue_proc.proc_layout))
}));
}
}
}
let output_path = match output_path {
Some(path_str) => Path::new(path_str).into(),
@ -3407,7 +3462,7 @@ fn finish_specialization<'a>(
output_path,
expectations: module_expectations,
exposed_to_host,
module_id: state.root_id,
module_id,
subs,
interns,
layout_interner,
@ -3416,6 +3471,9 @@ fn finish_specialization<'a>(
sources,
timings: state.timings,
toplevel_expects,
glue_layouts: GlueLayouts {
getters: glue_getters,
},
uses_prebuilt_platform,
})
}
@ -3438,6 +3496,7 @@ fn proc_layout_for<'a>(
}
}
#[allow(clippy::too_many_arguments)]
fn finish(
mut state: State,
solved: Solved<Subs>,
@ -5637,10 +5696,14 @@ fn make_specializations<'a>(
let mut procs = Procs::new_in(arena);
let host_exposed_symbols: bumpalo::collections::Vec<_> =
procs_base.get_host_exposed_symbols().collect_in(arena);
for (symbol, partial_proc) in procs_base.partial_procs.into_iter() {
procs.partial_procs.insert(symbol, partial_proc);
}
procs.host_exposed_symbols = host_exposed_symbols.into_bump_slice();
procs.module_thunks = procs_base.module_thunks;
procs.runtime_errors = procs_base.runtime_errors;
procs.imported_module_thunks = procs_base.imported_module_thunks;
@ -5740,7 +5803,7 @@ fn build_pending_specializations<'a>(
let symbol = declarations.symbols[index].value;
let expr_var = declarations.variables[index];
let is_host_exposed = exposed_to_host.values.contains_key(&symbol);
let is_host_exposed = exposed_to_host.top_level_values.contains_key(&symbol);
// TODO remove clones (with drain)
let annotation = declarations.annotations[index].clone();
@ -6647,7 +6710,7 @@ fn to_parse_problem_report<'a>(
buf
}
fn to_missing_platform_report(module_id: ModuleId, other: PlatformPath) -> String {
fn to_missing_platform_report(module_id: ModuleId, other: &PlatformPath) -> String {
use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
use ven_pretty::DocAllocator;
use PlatformPath::*;

View file

@ -944,6 +944,16 @@ pub struct ProcsBase<'a> {
pub imported_module_thunks: &'a [Symbol],
}
impl<'a> ProcsBase<'a> {
pub fn get_host_exposed_symbols(&self) -> impl Iterator<Item = Symbol> + '_ {
self.host_specializations
.symbol_or_lambdas
.iter()
.copied()
.map(|n| n.name())
}
}
/// The current set of functions under specialization. They form a stack where the latest
/// specialization to be seen is at the head of the stack.
#[derive(Clone, Debug)]
@ -962,14 +972,16 @@ impl<'a> SpecializationStack<'a> {
pub struct Procs<'a> {
pub partial_procs: PartialProcs<'a>,
ability_member_aliases: AbilityAliases,
pub imported_module_thunks: &'a [Symbol],
pub module_thunks: &'a [Symbol],
pending_specializations: PendingSpecializations<'a>,
specialized: Specialized<'a>,
pub runtime_errors: BumpMap<Symbol, &'a str>,
pub externals_we_need: BumpMap<ModuleId, ExternalSpecializations<'a>>,
symbol_specializations: SymbolSpecializations<'a>,
specialization_stack: SpecializationStack<'a>,
pub imported_module_thunks: &'a [Symbol],
pub module_thunks: &'a [Symbol],
pub host_exposed_symbols: &'a [Symbol],
}
impl<'a> Procs<'a> {
@ -977,14 +989,16 @@ impl<'a> Procs<'a> {
Self {
partial_procs: PartialProcs::new_in(arena),
ability_member_aliases: AbilityAliases::new_in(arena),
imported_module_thunks: &[],
module_thunks: &[],
pending_specializations: PendingSpecializations::Finding(Suspended::new_in(arena)),
specialized: Specialized::default(),
runtime_errors: BumpMap::new_in(arena),
externals_we_need: BumpMap::new_in(arena),
symbol_specializations: Default::default(),
specialization_stack: SpecializationStack(Vec::with_capacity_in(16, arena)),
imported_module_thunks: &[],
module_thunks: &[],
host_exposed_symbols: &[],
}
}
@ -3002,15 +3016,8 @@ fn specialize_host_specializations<'a>(
let offset_variable = StorageSubs::merge_into(store, env.subs);
for (symbol, variable, host_exposed_aliases) in it {
specialize_external_help(
env,
procs,
layout_cache,
symbol,
offset_variable(variable),
&host_exposed_aliases,
)
for (symbol, variable, _host_exposed_aliases) in it {
specialize_external_help(env, procs, layout_cache, symbol, offset_variable(variable))
}
}
@ -3035,7 +3042,7 @@ fn specialize_external_specializations<'a>(
// duplicate specializations, and the insertion into a hash map
// below will deduplicate them.
specialize_external_help(env, procs, layout_cache, symbol, imported_variable, &[])
specialize_external_help(env, procs, layout_cache, symbol, imported_variable)
}
}
}
@ -3046,7 +3053,6 @@ fn specialize_external_help<'a>(
layout_cache: &mut LayoutCache<'a>,
name: LambdaName<'a>,
variable: Variable,
host_exposed_aliases: &[(Symbol, Variable)],
) {
let partial_proc_id = match procs.partial_procs.symbol_to_id(name.name()) {
Some(v) => v,
@ -3066,7 +3072,7 @@ fn specialize_external_help<'a>(
debug_assert!(top_level.arguments.is_empty());
}
if !host_exposed_aliases.is_empty() {
if procs.host_exposed_symbols.contains(&proc.name.name()) {
// layouts that are (transitively) used in the type of `mainForHost`.
let mut host_exposed_layouts: Vec<_> = top_level
.arguments
@ -3079,7 +3085,6 @@ fn specialize_external_help<'a>(
host_exposed_layouts.sort();
host_exposed_layouts.dedup();
// TODO: In the future, we will generate glue procs here
for in_layout in host_exposed_layouts {
let layout = layout_cache.interner.get(in_layout);
@ -3093,16 +3098,27 @@ fn specialize_external_help<'a>(
// for now, getters are not processed here
let GlueProcs {
getters: _,
getters,
extern_names,
} = all_glue_procs;
for (_layout, glue_procs) in getters {
for glue_proc in glue_procs {
procs.specialized.insert_specialized(
glue_proc.proc.name.name(),
glue_proc.proc_layout,
glue_proc.proc,
);
}
}
let mut aliases = BumpMap::default();
for (id, mut raw_function_layout) in extern_names {
let symbol = env.unique_symbol();
let lambda_name = LambdaName::no_niche(symbol);
// fix the recursion in the rocLovesRust example
if false {
raw_function_layout = match raw_function_layout {
RawFunctionLayout::Function(a, mut lambda_set, _) => {
@ -3137,37 +3153,6 @@ fn specialize_external_help<'a>(
aliases.insert(key, hels);
}
// pre-glue: generate named callers for as-exposed aliases
for (alias_name, variable) in host_exposed_aliases {
let raw_function_layout = layout_cache
.raw_from_var(env.arena, *variable, env.subs)
.unwrap();
let symbol = env.unique_symbol();
let lambda_name = LambdaName::no_niche(symbol);
let (proc_name, (proc_layout, proc)) = generate_host_exposed_function(
env,
procs,
layout_cache,
lambda_name,
raw_function_layout,
);
procs
.specialized
.insert_specialized(proc_name, proc_layout, proc);
let hels = HostExposedLambdaSet {
id: LambdaSetId::default(),
symbol: proc_name,
proc_layout,
raw_function_layout,
};
aliases.insert(*alias_name, hels);
}
match &mut proc.host_exposed_layouts {
HostExposedLayouts::HostExposed { aliases: old, .. } => old.extend(aliases),
hep @ HostExposedLayouts::NotHostExposed => {
@ -11178,12 +11163,14 @@ where
}
}
#[derive(Debug, Default)]
pub struct GlueLayouts<'a> {
pub getters: std::vec::Vec<(Symbol, ProcLayout<'a>)>,
}
type GlueProcId = u16;
#[derive(Debug)]
pub struct GlueProc<'a> {
pub name: Symbol,
pub proc_layout: ProcLayout<'a>,
@ -11204,10 +11191,6 @@ impl LambdaSetId {
debug_assert!(self.0 < u32::MAX);
Self(self.0 + 1)
}
pub const fn invalid() -> Self {
Self(u32::MAX)
}
}
pub fn generate_glue_procs<'a, 'i, I>(
@ -11230,34 +11213,8 @@ where
let mut stack: Vec<'a, Layout<'a>> = Vec::from_iter_in([*layout], arena);
let mut next_unique_id = 0;
macro_rules! handle_struct_field_layouts {
($field_layouts: expr) => {{
if $field_layouts.iter().any(|l| {
layout_interner
.get(*l)
.has_varying_stack_size(layout_interner, arena)
}) {
let procs = generate_glue_procs_for_struct_fields(
layout_interner,
home,
&mut next_unique_id,
ident_ids,
arena,
layout,
$field_layouts,
);
answer.getters.push((*layout, procs));
}
for in_layout in $field_layouts.iter().rev() {
stack.push(layout_interner.get(*in_layout));
}
}};
}
macro_rules! handle_tag_field_layouts {
($tag_id:expr, $union_layout:expr, $field_layouts: expr) => {{
($tag_id:expr, $layout:expr, $union_layout:expr, $field_layouts: expr) => {{
if $field_layouts.iter().any(|l| {
layout_interner
.get(*l)
@ -11270,12 +11227,12 @@ where
ident_ids,
arena,
$tag_id,
layout,
&$layout,
$union_layout,
$field_layouts,
);
answer.getters.push((*layout, procs));
answer.getters.push(($layout, procs));
}
for in_layout in $field_layouts.iter().rev() {
@ -11295,7 +11252,27 @@ where
Builtin::List(element) => stack.push(layout_interner.get(element)),
},
Layout::Struct { field_layouts, .. } => {
handle_struct_field_layouts!(field_layouts);
if field_layouts.iter().any(|l| {
layout_interner
.get(*l)
.has_varying_stack_size(layout_interner, arena)
}) {
let procs = generate_glue_procs_for_struct_fields(
layout_interner,
home,
&mut next_unique_id,
ident_ids,
arena,
&layout,
field_layouts,
);
answer.getters.push((layout, procs));
}
for in_layout in field_layouts.iter().rev() {
stack.push(layout_interner.get(*in_layout));
}
}
Layout::Boxed(boxed) => {
stack.push(layout_interner.get(boxed));
@ -11312,7 +11289,7 @@ where
}
}
UnionLayout::NonNullableUnwrapped(field_layouts) => {
handle_tag_field_layouts!(0, union_layout, field_layouts);
handle_tag_field_layouts!(0, layout, union_layout, field_layouts);
}
UnionLayout::NullableWrapped {
other_tags,
@ -11321,7 +11298,7 @@ where
let tag_ids =
(0..nullable_id).chain(nullable_id + 1..other_tags.len() as u16 + 1);
for (i, field_layouts) in tag_ids.zip(other_tags) {
handle_tag_field_layouts!(i, union_layout, *field_layouts);
handle_tag_field_layouts!(i, layout, union_layout, *field_layouts);
}
}
UnionLayout::NullableUnwrapped { other_fields, .. } => {
@ -11357,8 +11334,8 @@ fn generate_glue_procs_for_struct_fields<'a, 'i, I>(
next_unique_id: &mut GlueProcId,
ident_ids: &mut IdentIds,
arena: &'a Bump,
unboxed_struct_layout: &'a Layout<'a>,
field_layouts: &'a [InLayout<'a>],
unboxed_struct_layout: &Layout<'a>,
field_layouts: &[InLayout<'a>],
) -> Vec<'a, GlueProc<'a>>
where
I: LayoutInterner<'a>,
@ -11368,6 +11345,17 @@ where
let boxed_struct_layout = layout_interner.insert(boxed_struct_layout);
let mut answer = bumpalo::collections::Vec::with_capacity_in(field_layouts.len(), arena);
let field_layouts = match layout_interner.get(interned_unboxed_struct_layout) {
Layout::Struct { field_layouts, .. } => field_layouts,
other => {
unreachable!(
"{:?} {:?}",
layout_interner.dbg(interned_unboxed_struct_layout),
other
)
}
};
for (index, field) in field_layouts.iter().enumerate() {
let proc_layout = ProcLayout {
arguments: arena.alloc([boxed_struct_layout]),
@ -11443,7 +11431,8 @@ fn unique_glue_symbol(
let _result = write!(&mut string, "roc__getter_{}_{}", module_name, unique_id);
debug_assert_eq!(_result, Ok(())); // This should never fail, but doesn't hurt to debug-check!
let ident_id = ident_ids.get_or_insert(string.into_bump_str());
let bump_string = string.into_bump_str();
let ident_id = ident_ids.get_or_insert(bump_string);
Symbol::new(home, ident_id)
}
@ -11456,7 +11445,7 @@ fn generate_glue_procs_for_tag_fields<'a, 'i, I>(
ident_ids: &mut IdentIds,
arena: &'a Bump,
tag_id: TagIdIntType,
unboxed_struct_layout: &'a Layout<'a>,
unboxed_struct_layout: &Layout<'a>,
union_layout: UnionLayout<'a>,
field_layouts: &'a [InLayout<'a>],
) -> Vec<'a, GlueProc<'a>>

View file

@ -2862,6 +2862,18 @@ impl<'a> Layout<'a> {
Dec => Layout::DEC,
}
}
pub fn is_recursive_tag_union(self) -> bool {
matches!(
self,
Layout::Union(
UnionLayout::NullableUnwrapped { .. }
| UnionLayout::Recursive(_)
| UnionLayout::NullableWrapped { .. }
| UnionLayout::NonNullableUnwrapped { .. },
)
)
}
}
impl<'a> Builtin<'a> {
@ -3597,18 +3609,6 @@ fn get_recursion_var(subs: &Subs, var: Variable) -> Option<Variable> {
}
}
fn is_recursive_tag_union(layout: &Layout) -> bool {
matches!(
layout,
Layout::Union(
UnionLayout::NullableUnwrapped { .. }
| UnionLayout::Recursive(_)
| UnionLayout::NullableWrapped { .. }
| UnionLayout::NonNullableUnwrapped { .. },
)
)
}
fn union_sorted_non_recursive_tags_help<'a, L>(
env: &mut Env<'a, '_>,
tags_list: &[(&'_ L, &[Variable])],
@ -3901,7 +3901,7 @@ where
== env
.subs
.get_root_key_without_compacting(opt_rec_var.unwrap())
&& is_recursive_tag_union(&layout);
&& layout.is_recursive_tag_union();
let arg_layout = if self_recursion {
Layout::NAKED_RECURSIVE_PTR

View file

@ -103,7 +103,7 @@ pub fn helper(
// println!("=================================\n");
}
debug_assert_eq!(exposed_to_host.values.len(), 1);
debug_assert_eq!(exposed_to_host.top_level_values.len(), 1);
let entry_point = match loaded.entry_point {
EntryPoint::Executable {
exposed_to_host,
@ -188,7 +188,7 @@ pub fn helper(
let env = roc_gen_dev::Env {
arena,
module_id,
exposed_to_host: exposed_to_host.values.keys().copied().collect(),
exposed_to_host: exposed_to_host.top_level_values.keys().copied().collect(),
lazy_literals,
generate_allocators: true, // Needed for testing, since we don't have a platform
};

View file

@ -114,10 +114,10 @@ fn compile_roc_to_wasm_bytes<'a, T: Wasm32Result>(
..
} = loaded;
debug_assert_eq!(exposed_to_host.values.len(), 1);
debug_assert_eq!(exposed_to_host.top_level_values.len(), 1);
let exposed_to_host = exposed_to_host
.values
.top_level_values
.keys()
.copied()
.collect::<MutSet<_>>();

View file

@ -2,6 +2,10 @@ procedure Test.1 (Test.4):
inc Test.4;
ret Test.4;
procedure Test.21 (Test.23, #Attr.12):
let Test.22 : Str = CallByName Test.5 Test.23 #Attr.12;
ret Test.22;
procedure Test.5 (Test.12, Test.4):
dec Test.4;
let Test.14 : Str = "";

View file

@ -6,6 +6,22 @@ procedure Test.1 (Test.2, Test.3):
let Test.21 : {U16, {}} = Struct {Test.2, Test.3};
ret Test.21;
procedure Test.30 (Test.31):
let Test.32 : {U8, {}} = Unbox Test.31;
dec Test.31;
let Test.33 : U8 = StructAtIndex 0 Test.32;
ret Test.33;
procedure Test.34 (Test.35):
let Test.36 : {U8, {}} = Unbox Test.35;
dec Test.35;
let Test.37 : {} = StructAtIndex 1 Test.36;
ret Test.37;
procedure Test.38 (Test.40, #Attr.12):
let Test.39 : Str = CallByName Test.4 Test.40 #Attr.12;
ret Test.39;
procedure Test.4 (Test.13, #Attr.12):
let Test.3 : {} = StructAtIndex 1 #Attr.12;
let Test.2 : U16 = StructAtIndex 0 #Attr.12;
@ -22,6 +38,10 @@ procedure Test.4 (Test.13, #Attr.12):
let Test.15 : Str = CallByName Test.4 Test.16 Test.5;
ret Test.15;
procedure Test.41 (Test.43, #Attr.12):
let Test.42 : {U16, {}} = CallByName Test.6 Test.43;
ret Test.42;
procedure Test.6 (Test.17):
let Test.19 : U16 = 1i64;
let Test.20 : {} = Struct {};

View file

@ -6,6 +6,22 @@ procedure Test.1 (Test.2, Test.3):
let Test.21 : {U8, {}} = Struct {Test.2, Test.3};
ret Test.21;
procedure Test.30 (Test.31):
let Test.32 : {U8, {}} = Unbox Test.31;
dec Test.31;
let Test.33 : U8 = StructAtIndex 0 Test.32;
ret Test.33;
procedure Test.34 (Test.35):
let Test.36 : {U8, {}} = Unbox Test.35;
dec Test.35;
let Test.37 : {} = StructAtIndex 1 Test.36;
ret Test.37;
procedure Test.38 (Test.40, #Attr.12):
let Test.39 : Str = CallByName Test.4 Test.40 #Attr.12;
ret Test.39;
procedure Test.4 (Test.13, #Attr.12):
let Test.3 : {} = StructAtIndex 1 #Attr.12;
let Test.2 : U8 = StructAtIndex 0 #Attr.12;
@ -22,6 +38,10 @@ procedure Test.4 (Test.13, #Attr.12):
let Test.24 : Str = CallByName Test.8 Test.25;
ret Test.24;
procedure Test.41 (Test.43, #Attr.12):
let Test.42 : {U8, {}} = CallByName Test.6 Test.43;
ret Test.42;
procedure Test.6 (Test.17):
let Test.19 : U8 = 1i64;
let Test.20 : {} = Struct {};

View file

@ -148,7 +148,7 @@ fn compiles_to_ir(test_name: &str, src: &str, mode: &str, no_check: bool) {
assert!(type_problems.is_empty());
let main_fn_symbol = exposed_to_host.values.keys().copied().next();
let main_fn_symbol = exposed_to_host.top_level_values.keys().copied().next();
if !no_check {
check_procedures(arena, &interns, &mut layout_interner, &procedures);
@ -2553,6 +2553,7 @@ fn recursively_build_effect() {
}
#[mono_test]
#[ignore = "roc glue code generation cannot handle a type that this test generates"]
fn recursive_lambda_set_has_nested_non_recursive_lambda_sets_issue_5026() {
indoc!(
r#"

View file

@ -1,11 +1,14 @@
use crate::rust_glue;
use crate::types::{Env, Types};
use crate::types::Types;
use bumpalo::Bump;
use roc_collections::MutMap;
use roc_load::{ExecutionMode, LoadConfig, LoadedModule, LoadingProblem, Threading};
use roc_mono::layout::GlobalLayoutInterner;
use roc_mono::ir::{generate_glue_procs, GlueProc};
use roc_mono::layout::{GlobalLayoutInterner, LayoutCache, LayoutInterner};
use roc_packaging::cache::{self, RocCacheDir};
use roc_reporting::report::{RenderTarget, DEFAULT_PALETTE};
use roc_target::{Architecture, OperatingSystem, TargetInfo};
use roc_target::{Architecture, TargetInfo};
use roc_types::subs::{Subs, Variable};
use std::fs::File;
use std::io::{self, ErrorKind, Write};
use std::path::{Path, PathBuf};
@ -77,6 +80,120 @@ pub fn generate(input_path: &Path, output_path: &Path) -> io::Result<i32> {
}
}
fn number_lambda_sets(subs: &Subs, initial: Variable) -> Vec<Variable> {
let mut lambda_sets = vec![];
let mut stack = vec![initial];
macro_rules! var_slice {
($variable_subs_slice:expr) => {{
let slice = $variable_subs_slice;
subs.variables[slice.indices()].iter().rev()
}};
}
while let Some(var) = stack.pop() {
use roc_types::subs::Content::*;
use roc_types::subs::FlatType::*;
use roc_types::subs::GetSubsSlice;
use roc_types::types::Uls;
match subs.get_content_without_compacting(var) {
RigidVar(_) | RigidAbleVar(_, _) | FlexVar(_) | FlexAbleVar(_, _) | Error => (),
RecursionVar { .. } => {
// we got here, so we've treated this type already
}
Structure(flat_type) => match flat_type {
Apply(_, args) => {
stack.extend(var_slice!(*args));
}
Func(arg_vars, closure_var, ret_var) => {
lambda_sets.push(subs.get_root_key_without_compacting(*closure_var));
stack.push(*ret_var);
stack.push(*closure_var);
stack.extend(var_slice!(arg_vars));
}
EmptyRecord => (),
EmptyTagUnion => (),
EmptyTuple => (),
Record(fields, ext) => {
let fields = *fields;
let ext = *ext;
stack.push(ext);
stack.extend(var_slice!(fields.variables()));
}
Tuple(_, _) => todo!(),
TagUnion(tags, ext) => {
let tags = *tags;
let ext = *ext;
stack.push(ext.var());
for slice_index in tags.variables() {
let slice = subs.variable_slices[slice_index.index as usize];
stack.extend(var_slice!(slice));
}
}
FunctionOrTagUnion(_, _, ext) => {
stack.push(ext.var());
}
RecursiveTagUnion(rec_var, tags, ext) => {
let tags = *tags;
let ext = *ext;
let rec_var = *rec_var;
stack.push(ext.var());
for slice_index in tags.variables() {
let slice = subs.variable_slices[slice_index.index as usize];
stack.extend(var_slice!(slice));
}
stack.push(rec_var);
}
},
Alias(_, args, var, _) => {
let var = *var;
let args = *args;
stack.extend(var_slice!(args.all_variables()));
stack.push(var);
}
LambdaSet(roc_types::subs::LambdaSet {
solved,
recursion_var,
unspecialized,
ambient_function: _,
}) => {
for slice_index in solved.variables() {
let slice = subs.variable_slices[slice_index.index as usize];
stack.extend(var_slice!(slice));
}
if let Some(rec_var) = recursion_var.into_variable() {
stack.push(rec_var);
}
for Uls(var, _, _) in subs.get_subs_slice(*unspecialized) {
stack.push(*var);
}
}
&RangedNumber(_) => {}
}
}
lambda_sets
}
pub fn load_types(
full_file_path: PathBuf,
threading: Threading,
@ -91,6 +208,7 @@ pub fn load_types(
mut declarations_by_id,
mut solved,
interns,
exposed_to_host,
..
} = roc_load::load_and_typecheck(
arena,
@ -129,42 +247,90 @@ pub fn load_types(
);
}
// Get the variables for all the exposed_to_host symbols
let variables = (0..decls.len()).filter_map(|index| {
use roc_can::expr::DeclarationTag::*;
match decls.declarations[index] {
Value | Function(_) | Recursive(_) | TailRecursive(_) => Some(decls.variables[index]),
Destructure(_) => {
// figure out if we need to export non-identifier defs - when would that
// happen?
if exposed_to_host.contains_key(&decls.symbols[index].value) {
Some(decls.variables[index])
} else {
None
}
MutualRecursion { .. } => {
// handled by future iterations
None
}
Expectation | ExpectationFx => {
// not publicly visible
None
}
}
});
let layout_interner = GlobalLayoutInterner::with_capacity(128, target_info);
let operating_system = target_info.operating_system;
let architectures = Architecture::iter();
let mut types_and_targets = Vec::with_capacity(architectures.len());
for arch in architectures {
let layout_interner = GlobalLayoutInterner::with_capacity(128, target_info);
for architecture in architectures {
let mut interns = interns.clone(); // TODO there may be a way to avoid this.
let target_info = TargetInfo {
architecture: arch,
operating_system: OperatingSystem::Unix,
architecture,
operating_system,
};
let mut layout_cache = LayoutCache::new(layout_interner.fork(), target_info);
let mut glue_procs_by_layout = MutMap::default();
let types = {
let mut env = Env::new(arena, subs, &interns, layout_interner.fork(), target_info);
let mut extern_names = MutMap::default();
env.vars_to_types(variables.clone())
};
// Populate glue getters/setters for all relevant variables
for var in variables.clone() {
for (i, v) in number_lambda_sets(subs, var).iter().enumerate() {
extern_names.insert(*v, i.to_string());
}
let in_layout = layout_cache
.from_var(arena, var, subs)
.expect("Something weird ended up in the content");
let layout = layout_cache.interner.get(in_layout);
// dbg!(layout);
if layout.has_varying_stack_size(&layout_cache.interner, arena) {
let ident_ids = interns.all_ident_ids.get_mut(&home).unwrap();
let answer = generate_glue_procs(
home,
ident_ids,
arena,
&mut layout_interner.fork(),
arena.alloc(layout),
);
// Even though generate_glue_procs does more work than we need it to,
// it's important that we use it in order to make sure we get exactly
// the same names that mono::ir did for code gen!
for (layout, glue_procs) in answer.getters {
let mut names =
bumpalo::collections::Vec::with_capacity_in(glue_procs.len(), arena);
// Record all the getter/setter names associated with this layout
for GlueProc { name, .. } in glue_procs {
// Given a struct layout (including lambda sets!) the offsets - and therefore
// getters/setters - are deterministic, so we can use layout as the hash key
// for these getters/setters. We also only need to store the name because
// since they are getters and setters, we can know their types (from a
// TypeId perspective) deterministically based on knowing the types of
// the structs and fields.
//
// Store them as strings, because symbols won't be useful to glue generators!
names.push(name.as_str(&interns).to_string());
}
glue_procs_by_layout.insert(layout, names.into_bump_slice());
}
}
}
let types = Types::new(
arena,
subs,
variables.clone(),
arena.alloc(interns),
glue_procs_by_layout,
layout_cache,
target_info,
);
types_and_targets.push((types, target_info));
}

View file

@ -1,4 +1,7 @@
use crate::types::{RocNum, RocTagUnion, RocType, TypeId, Types};
use crate::types::{
Accessors, RocFn, RocNum, RocSingleTagPayload, RocStructFields, RocTagUnion, RocType, TypeId,
Types,
};
use indexmap::IndexMap;
use roc_target::{Architecture, TargetInfo};
use std::fmt::{Display, Write};
@ -248,16 +251,9 @@ fn add_type(target_info: TargetInfo, id: TypeId, types: &Types, impls: &mut Impl
RocTagUnion::SingleTagStruct {
name,
tag_name,
payload_fields,
payload,
} => {
add_single_tag_struct(
name,
tag_name,
payload_fields,
types,
impls,
target_info,
);
add_single_tag_struct(name, tag_name, payload, types, impls, target_info);
}
RocTagUnion::NonNullableUnwrapped {
name,
@ -289,21 +285,20 @@ fn add_type(target_info: TargetInfo, id: TypeId, types: &Types, impls: &mut Impl
| RocType::RocDict(_, _)
| RocType::RocSet(_)
| RocType::RocList(_)
| RocType::RocBox(_) => {}
| RocType::RocBox(_)
| RocType::Unsized => {}
RocType::RecursivePointer { .. } => {
// This is recursively pointing to a type that should already have been added,
// so no extra work needs to happen.
}
RocType::Function { .. } => {
// TODO actually generate glue functions!
}
RocType::Function(roc_fn) => add_function(target_info, roc_fn, types, impls),
}
}
fn add_single_tag_struct(
name: &str,
tag_name: &str,
payload_fields: &[TypeId],
payload: &RocSingleTagPayload,
types: &Types,
impls: &mut IndexMap<Option<String>, IndexMap<String, Vec<TargetInfo>>>,
target_info: TargetInfo,
@ -314,45 +309,93 @@ fn add_single_tag_struct(
// because they have only one alternative. However, still
// offer the usual tag union APIs.
{
let derive = derive_str(
&RocType::Struct {
let mut buf = String::new();
// Make a dummy RocType::Struct so that we can pass it to deriving
// and have that work out as normal.
let struct_type = match payload {
RocSingleTagPayload::HasClosure { payload_getters } => {
{
if payload_getters.is_empty() {
// A single tag with no payload is a zero-sized unit type, so
// represent it as a zero-sized struct (e.g. "struct Foo()").
buf.push_str("();\n");
} else {
buf.push_str("{\n");
for (_index, _getter_fn) in payload_getters.iter().enumerate() {
// TODO these should be added as separate functions in the impl!
todo!("TODO generate payload getters");
}
buf.push_str("}\n");
}
let fields = payload_getters
.iter()
.map(|(type_id, getter)| {
(
String::new(),
*type_id,
Accessors {
getter: getter.clone(),
},
)
})
.collect();
RocType::Struct {
// Deriving doesn't depend on the struct's name,
// so no need to clone name here.
name: String::new(),
fields: payload_fields
.iter()
.map(|type_id| (String::new(), *type_id))
.collect(),
},
types,
false,
);
let mut body = format!("#[repr(transparent)]\n{derive}\npub struct {name} ");
if payload_fields.is_empty() {
fields: RocStructFields::HasClosure { fields },
}
}
}
RocSingleTagPayload::HasNoClosure {
payload_fields: payloads,
} => {
if payloads.is_empty() {
// A single tag with no payload is a zero-sized unit type, so
// represent it as a zero-sized struct (e.g. "struct Foo()").
body.push_str("();\n");
buf.push_str("();\n");
} else {
body.push_str("{\n");
buf.push_str("{\n");
for (index, field_id) in payload_fields.iter().enumerate() {
for (index, field_id) in payloads.iter().enumerate() {
let field_type = type_name(*field_id, types);
// These are all private fields, since this is a tag union.
// ignore returned result, writeln can not fail as it is used here
let _ = writeln!(body, "{INDENT}f{index}: {field_type},");
let _ = writeln!(buf, "{INDENT}f{index}: {field_type},");
}
body.push_str("}\n");
buf.push_str("}\n");
}
let fields = payloads
.iter()
.map(|type_id| (String::new(), *type_id))
.collect();
RocType::Struct {
// Deriving doesn't depend on the struct's name,
// so no need to clone name here.
name: String::new(),
fields: RocStructFields::HasNoClosure { fields },
}
}
};
let derive = derive_str(&struct_type, types, false);
let body = format!("#[repr(transparent)]\n{derive}\npub struct {name} {buf}");
add_decl(impls, None, target_info, body);
}
// the impl for the single-tag union itself
{
match payload {
RocSingleTagPayload::HasNoClosure { payload_fields } => {
let opt_impl = Some(format!("impl {name}"));
if payload_fields.is_empty() {
@ -502,13 +545,16 @@ fn add_single_tag_struct(
}
}
}
RocSingleTagPayload::HasClosure { payload_getters: _ } => todo!(),
}
// The Debug impl for the single-tag union
{
match payload {
RocSingleTagPayload::HasNoClosure { payload_fields } => {
let opt_impl = Some(format!("impl core::fmt::Debug for {name}"));
let mut buf =
"fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {".to_string();
let mut buf = "fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {"
.to_string();
if payload_fields.is_empty() {
// ignore returned result, write can not fail as it is used here
@ -533,6 +579,8 @@ fn add_single_tag_struct(
add_decl(impls, opt_impl, target_info, buf);
}
RocSingleTagPayload::HasClosure { payload_getters: _ } => todo!(),
}
}
fn add_discriminant(
@ -958,9 +1006,11 @@ pub struct {name} {{
"arg".to_string()
};
}
RocType::Struct { fields, name } => {
let answer =
tag_union_struct_help(name, fields.iter(), *payload_id, types, false);
RocType::Struct {
fields: RocStructFields::HasNoClosure { fields },
name,
} => {
let answer = tag_union_struct_help(name, fields, *payload_id, types, false);
owned_ret = answer.owned_ret;
borrowed_ret = answer.borrowed_ret;
@ -969,9 +1019,11 @@ pub struct {name} {{
payload_args = answer.payload_args;
args_to_payload = answer.args_to_payload;
}
RocType::TagUnionPayload { fields, name } => {
let answer =
tag_union_struct_help(name, fields.iter(), *payload_id, types, true);
RocType::TagUnionPayload {
fields: RocStructFields::HasNoClosure { fields },
name,
} => {
let answer = tag_union_struct_help(name, fields, *payload_id, types, true);
owned_ret = answer.owned_ret;
borrowed_ret = answer.borrowed_ret;
@ -980,7 +1032,102 @@ pub struct {name} {{
payload_args = answer.payload_args;
args_to_payload = answer.args_to_payload;
}
RocType::Function { .. } => todo!(),
RocType::Struct {
fields: RocStructFields::HasClosure { fields: _ },
name: _,
} => {
todo!("struct");
}
RocType::TagUnionPayload {
fields: RocStructFields::HasClosure { fields },
name: _, // TODO call this payload_struct_name and use it to define the struct...or don't define it at all, maybe, since there are only getters and setters?
} => {
// TODO don't generate op.into_StdoutWrite() - only getters/setters instead!
for (field_name, field, accessor) in fields {
let getter_name = &accessor.getter;
let ret = type_name(*field, types);
let returns_via_pointer = true;
let body = if let RocType::Function(_) = types.get_type(*field) {
format!(
r#"
extern "C" {{
#[link_name = "{getter_name}_size"]
fn size() -> usize;
#[link_name = "{getter_name}_generic"]
fn getter(_: *mut u8, _: *const {name});
}}
// 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);
{ret} {{
closure_data: bytes,
}}
"#
)
} else if returns_via_pointer {
format!(
r#"
extern "C" {{
#[link_name = "{getter_name}_generic"]
fn getter(_: *mut {ret}, _: *const {name});
}}
let mut ret = core::mem::MaybeUninit::uninit();
getter(ret.as_mut_ptr(), self);
ret.assume_init()
"#
)
} else {
format!(
r#"
extern "C" {{
#[link_name = "{getter_name}"]
fn getter(_: *const {name}) -> {ret};
}}
getter(self)
"#
)
};
add_decl(
impls,
opt_impl.clone(),
target_info,
format!(
r#"/// Unsafely assume this `{name}` has a `.discriminant()` of `{tag_name}` and return its payload at index {field_name}.
/// (Always examine `.discriminant()` first to make sure this is the correct variant!)
/// Panics in debug builds if the `.discriminant()` doesn't return `{tag_name}`.
pub unsafe fn get_{tag_name}_{field_name}(&self) -> {ret} {{
debug_assert_eq!(self.discriminant(), {discriminant_name}::{tag_name});
{body}
}}"#,
),
);
}
// TODO revise these - they're all copy/pasted from somewhere else
owned_ret_type = type_name(*payload_id, types);
borrowed_ret_type = format!("&{}", owned_ret_type);
owned_ret = "payload".to_string();
borrowed_ret = format!("&{owned_ret}");
payload_args = format!("arg: {owned_ret_type}");
args_to_payload = if cannot_derive_copy(payload_type, types) {
"core::mem::ManuallyDrop::new(arg)".to_string()
} else {
"arg".to_string()
};
}
RocType::Unsized => todo!(),
RocType::Function(RocFn { .. }) => todo!(),
};
{
@ -1047,7 +1194,7 @@ pub struct {name} {{
)
} else {
format!(
r#"/// Unsafely assume the given `{name}` has a `.discriminant()` of `{tag_name}` and convert it to `{tag_name}`'s payload.
r#"/// Unsafely assume this `{name}` has a `.discriminant()` of `{tag_name}` and convert it to `{tag_name}`'s payload.
/// (Always examine `.discriminant()` first to make sure this is the correct variant!)
/// Panics in debug builds if the `.discriminant()` doesn't return `{tag_name}`.
pub unsafe fn into_{tag_name}({self_for_into}) -> {owned_ret_type} {{
@ -1078,7 +1225,7 @@ pub struct {name} {{
)
} else {
format!(
r#"/// Unsafely assume the given `{name}` has a `.discriminant()` of `{tag_name}` and return its payload.
r#"/// Unsafely assume this `{name}` has a `.discriminant()` of `{tag_name}` 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 `{tag_name}`.
pub unsafe fn as_{tag_name}(&self) -> {borrowed_ret_type} {{
@ -1579,16 +1726,24 @@ pub struct {name} {{
RocType::TagUnionPayload { fields, .. } => {
let mut buf = Vec::new();
match fields {
RocStructFields::HasNoClosure { fields } => {
for (label, _) in fields {
// Needs an "f" prefix
buf.push(format!(
".field(&({deref_str}{actual_self}.{tag_name}).f{label})"
));
}
}
RocStructFields::HasClosure { fields: _ } => {
buf.push("// TODO HAS CLOSURE".to_string());
}
}
buf.join("\n")
}
RocType::Function { .. } => todo!(),
RocType::Unsized => todo!(),
RocType::Function(RocFn { .. }) => todo!(),
};
format!(
@ -1681,10 +1836,104 @@ fn add_enumeration<I: ExactSizeIterator<Item = S>, S: AsRef<str> + Display>(
add_decl(impls, None, target_info, buf);
}
fn add_struct<S: Display>(
fn add_function(
// name: &str,
target_info: TargetInfo,
roc_fn: &RocFn,
types: &Types,
impls: &mut Impls,
) {
let name = escape_kw(roc_fn.function_name.to_string());
// let derive = derive_str(types.get_type(struct_id), types, true);
let derive = "";
let pub_str = "pub ";
let mut buf;
let repr = "C";
buf = format!("{derive}\n#[repr({repr})]\n{pub_str}struct {name} {{\n");
let fields = [("closure_data", &roc_fn.lambda_set)];
for (label, type_id) in fields {
let type_str = type_name(*type_id, types);
// Tag union payloads have numbered fields, so we prefix them
// with an "f" because Rust doesn't allow struct fields to be numbers.
let label = escape_kw(label.to_string());
writeln!(buf, "{INDENT}pub {label}: {type_str},",).unwrap();
}
buf.push('}');
buf.push('\n');
buf.push('\n');
let extern_name = &roc_fn.extern_name;
let return_type_str = type_name(roc_fn.ret, types);
writeln!(buf, "impl {name} {{").unwrap();
write!(buf, "{INDENT}pub fn force_thunk(mut self").unwrap();
for (i, argument_type) in roc_fn.args.iter().enumerate() {
write!(buf, ", arg_{i}: {}", type_name(*argument_type, types)).unwrap();
}
writeln!(buf, ") -> {return_type_str} {{").unwrap();
writeln!(buf, "{INDENT}{INDENT}extern \"C\" {{").unwrap();
// fn extern_name(output: *mut return_type, arg1: arg1_type, ..., closure_data: *mut u8);
write!(buf, "{INDENT}{INDENT}{INDENT} fn {extern_name}(").unwrap();
for (i, argument_type) in roc_fn.args.iter().enumerate() {
write!(buf, "arg_{i}: &{}, ", type_name(*argument_type, types)).unwrap();
}
writeln!(
buf,
"closure_data: *mut u8, output: *mut {return_type_str});"
)
.unwrap();
// {argument_types} "
writeln!(buf, "{INDENT}{INDENT}}}").unwrap();
writeln!(buf).unwrap();
writeln!(
buf,
"{INDENT}{INDENT}let mut output = std::mem::MaybeUninit::uninit();"
)
.unwrap();
writeln!(
buf,
"{INDENT}{INDENT}let ptr = self.closure_data.as_mut_ptr();"
)
.unwrap();
write!(buf, "{INDENT}{INDENT}unsafe {{ {extern_name}(").unwrap();
for (i, _) in roc_fn.args.iter().enumerate() {
write!(buf, "&arg_{i}, ").unwrap();
}
writeln!(buf, "ptr, output.as_mut_ptr()) }};").unwrap();
writeln!(buf, "{INDENT}{INDENT}unsafe {{ output.assume_init() }}").unwrap();
writeln!(buf, "{INDENT}}}").unwrap();
buf.push('}');
add_decl(impls, None, target_info, buf);
}
fn add_struct(
name: &str,
target_info: TargetInfo,
fields: &[(S, TypeId)],
fields: &RocStructFields,
struct_id: TypeId,
types: &Types,
impls: &mut Impls,
@ -1693,12 +1942,17 @@ fn add_struct<S: Display>(
let name = escape_kw(name.to_string());
let derive = derive_str(types.get_type(struct_id), types, true);
let pub_str = if is_tag_union_payload { "" } else { "pub " };
let mut buf;
match fields {
RocStructFields::HasNoClosure { fields } => {
let repr = if fields.len() == 1 {
"transparent"
} else {
"C"
};
let mut buf = format!("{derive}\n#[repr({repr})]\n{pub_str}struct {name} {{\n");
buf = format!("{derive}\n#[repr({repr})]\n{pub_str}struct {name} {{\n");
for (label, type_id) in fields {
let type_str = type_name(*type_id, types);
@ -1715,6 +1969,11 @@ fn add_struct<S: Display>(
}
buf.push('}');
}
RocStructFields::HasClosure { fields: _ } => {
buf = "//TODO HAS CLOSURE 2".to_string();
}
}
add_decl(impls, None, target_info, buf);
}
@ -1746,6 +2005,7 @@ fn type_name(id: TypeId, types: &Types) -> String {
RocType::RocSet(elem_id) => format!("roc_std::RocSet<{}>", type_name(*elem_id, types)),
RocType::RocList(elem_id) => format!("roc_std::RocList<{}>", type_name(*elem_id, types)),
RocType::RocBox(elem_id) => format!("roc_std::RocBox<{}>", type_name(*elem_id, types)),
RocType::Unsized => "roc_std::RocList<u8>".to_string(),
RocType::RocResult(ok_id, err_id) => {
format!(
"roc_std::RocResult<{}, {}>",
@ -1763,7 +2023,7 @@ fn type_name(id: TypeId, types: &Types) -> String {
| RocType::TagUnion(RocTagUnion::NonNullableUnwrapped { name, .. })
| RocType::TagUnion(RocTagUnion::SingleTagStruct { name, .. }) => escape_kw(name.clone()),
RocType::RecursivePointer(content) => type_name(*content, types),
RocType::Function { name, .. } => escape_kw(name.clone()),
RocType::Function(RocFn { function_name, .. }) => escape_kw(function_name.clone()),
}
}
@ -1771,27 +2031,136 @@ fn type_name(id: TypeId, types: &Types) -> String {
/// case of a struct that's a payload for a recursive tag union, typ.has_enumeration()
/// will return true, but actually we want to derive Debug here anyway.
fn derive_str(typ: &RocType, types: &Types, include_debug: bool) -> String {
let mut buf = "#[derive(Clone, ".to_string();
let mut derives = vec!["Clone"];
if !cannot_derive_copy(typ, types) {
buf.push_str("Copy, ");
derives.push("Copy");
}
if include_debug {
buf.push_str("Debug, ");
derives.push("Debug");
}
if !has_functions(typ, types) {
derives.push("PartialEq");
derives.push("PartialOrd");
if !has_float(typ, types) {
derives.push("Eq");
derives.push("Ord");
derives.push("Hash");
}
if !cannot_derive_default(typ, types) {
buf.push_str("Default, ");
derives.push("Default");
}
}
if !has_float(typ, types) {
buf.push_str("Eq, Ord, Hash, ");
derives.sort();
format!("#[derive({})]", derives.join(", "))
}
fn has_functions(start: &RocType, types: &Types) -> bool {
let mut seen: Vec<TypeId> = vec![];
let mut stack = vec![start];
macro_rules! push {
($id:expr) => {{
if !seen.contains($id) {
seen.push(*$id);
stack.push(types.get_type(*$id));
}
}};
}
buf.push_str("PartialEq, PartialOrd)]");
while let Some(typ) = stack.pop() {
match typ {
RocType::RocStr
| RocType::Bool
| RocType::Unit
| RocType::Num(_)
| RocType::EmptyTagUnion
| RocType::Unsized
| RocType::TagUnion(RocTagUnion::Enumeration { .. }) => { /* terminal */ }
buf
RocType::TagUnion(RocTagUnion::NonRecursive { tags, .. })
| RocType::TagUnion(RocTagUnion::Recursive { tags, .. })
| RocType::TagUnion(RocTagUnion::NullableWrapped { tags, .. }) => {
for (_, opt_payload_id) in tags.iter() {
if let Some(payload_id) = opt_payload_id {
push!(payload_id);
}
}
}
RocType::RocBox(type_id)
| RocType::RocList(type_id)
| RocType::RocSet(type_id)
| RocType::TagUnion(RocTagUnion::NullableUnwrapped {
non_null_payload: type_id,
..
})
| RocType::TagUnion(RocTagUnion::NonNullableUnwrapped {
payload: type_id, ..
}) => {
push!(type_id);
}
RocType::TagUnion(RocTagUnion::SingleTagStruct {
payload: RocSingleTagPayload::HasNoClosure { payload_fields },
..
}) => {
for payload_id in payload_fields {
push!(payload_id);
}
}
RocType::TagUnion(RocTagUnion::SingleTagStruct {
payload: RocSingleTagPayload::HasClosure { payload_getters },
..
}) => {
for (payload_id, _) in payload_getters {
push!(payload_id);
}
}
RocType::TagUnionPayload {
fields: RocStructFields::HasNoClosure { fields },
..
}
| RocType::Struct {
fields: RocStructFields::HasNoClosure { fields },
..
} => {
for (_, type_id) in fields {
push!(type_id);
}
}
RocType::TagUnionPayload {
fields: RocStructFields::HasClosure { fields },
..
}
| RocType::Struct {
fields: RocStructFields::HasClosure { fields },
..
} => {
for (_, type_id, _) in fields {
push!(type_id);
}
}
RocType::RecursivePointer(type_id) => {
push!(type_id);
}
RocType::RocResult(id1, id2) | RocType::RocDict(id1, id2) => {
push!(id1);
push!(id2);
}
RocType::Function(_) => return true,
}
}
false
}
#[allow(clippy::too_many_arguments)]
@ -1889,9 +2258,10 @@ pub struct {name} {{
owned_ret = "payload".to_string();
borrowed_ret = format!("&{owned_ret}");
}
RocType::Struct { fields, name } => {
RocType::Struct { fields, name } => match fields {
RocStructFields::HasNoClosure { fields } => {
let answer =
tag_union_struct_help(name, fields.iter(), non_null_payload, types, false);
tag_union_struct_help(name, fields, non_null_payload, types, false);
payload_args = answer.payload_args;
args_to_payload = answer.args_to_payload;
@ -1900,9 +2270,12 @@ pub struct {name} {{
owned_ret_type = answer.owned_ret_type;
borrowed_ret_type = answer.borrowed_ret_type;
}
RocType::TagUnionPayload { fields, name } => {
let answer =
tag_union_struct_help(name, fields.iter(), non_null_payload, types, true);
RocStructFields::HasClosure { .. } => todo!(),
},
RocType::TagUnionPayload { fields, name } => match fields {
RocStructFields::HasNoClosure { fields } => {
let answer = tag_union_struct_help(name, fields, non_null_payload, types, true);
payload_args = answer.payload_args;
args_to_payload = answer.args_to_payload;
@ -1911,7 +2284,10 @@ pub struct {name} {{
owned_ret_type = answer.owned_ret_type;
borrowed_ret_type = answer.borrowed_ret_type;
}
RocStructFields::HasClosure { .. } => todo!(),
},
RocType::Function { .. } => todo!(),
RocType::Unsized => todo!(),
};
// Add a convenience constructor function for the tag with the payload, e.g.
@ -1986,7 +2362,7 @@ pub struct {name} {{
opt_impl.clone(),
target_info,
format!(
r#"/// Unsafely assume the given `{name}` has a `.discriminant()` of `{non_null_tag}` and convert it to `{non_null_tag}`'s payload.
r#"/// Unsafely assume this `{name}` has a `.discriminant()` of `{non_null_tag}` and convert it to `{non_null_tag}`'s payload.
/// (Always examine `.discriminant()` first to make sure this is the correct variant!)
/// Panics in debug builds if the `.discriminant()` doesn't return {non_null_tag}.
pub unsafe fn into_{non_null_tag}(self) -> {owned_ret_type} {{
@ -2005,7 +2381,7 @@ pub struct {name} {{
opt_impl.clone(),
target_info,
format!(
r#"/// Unsafely assume the given `{name}` has a `.discriminant()` of `{non_null_tag}` and return its payload.
r#"/// Unsafely assume this `{name}` has a `.discriminant()` of `{non_null_tag}` 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 `{non_null_tag}`.
pub unsafe fn as_{non_null_tag}(&self) -> {borrowed_ret_type} {{
@ -2138,22 +2514,33 @@ pub struct {name} {{
RocType::Struct { fields, .. } => {
let mut buf = Vec::new();
match fields {
RocStructFields::HasNoClosure { fields } => {
for (label, _) in fields {
buf.push(format!(".field(&(&*{extra_deref}self.pointer).{label})"));
}
}
RocStructFields::HasClosure { fields: _ } => todo!(),
}
buf.join(&format!("\n{INDENT}{INDENT}{INDENT}{INDENT}{INDENT}"))
}
RocType::TagUnionPayload { fields, .. } => {
let mut buf = Vec::new();
match fields {
RocStructFields::HasNoClosure { fields } => {
for (label, _) in fields {
// Needs an "f" prefix
buf.push(format!(".field(&(&*{extra_deref}self.pointer).f{label})"));
}
}
RocStructFields::HasClosure { fields: _ } => todo!(),
}
buf.join(&format!("\n{INDENT}{INDENT}{INDENT}{INDENT}{INDENT}"))
}
RocType::Unsized => todo!(),
RocType::Function { .. } => todo!(),
};
@ -2221,17 +2608,13 @@ struct StructIngredients {
borrowed_ret_type: String,
}
fn tag_union_struct_help<'a, I: Iterator<Item = &'a (L, TypeId)>, L: Display + PartialOrd + 'a>(
fn tag_union_struct_help(
name: &str,
fields: I,
fields: &[(String, TypeId)],
payload_id: TypeId,
types: &Types,
is_tag_union_payload: bool,
) -> StructIngredients {
let mut sorted_fields = fields.collect::<Vec<&(L, TypeId)>>();
sorted_fields.sort_by(|(label1, _), (label2, _)| label1.partial_cmp(label2).unwrap());
let mut ret_types = if is_tag_union_payload {
// This will be a tuple we create when iterating over the fields.
Vec::new()
@ -2241,13 +2624,13 @@ fn tag_union_struct_help<'a, I: Iterator<Item = &'a (L, TypeId)>, L: Display + P
let mut ret_values = Vec::new();
for (label, type_id) in sorted_fields.iter() {
for (label, type_id) in fields.iter() {
let label = if is_tag_union_payload {
// Tag union payload fields need "f" prefix
// because they're numbers
format!("f{}", label)
} else {
escape_kw(format!("{}", label))
escape_kw(label.to_string())
};
ret_values.push(format!("payload.{label}"));
@ -2266,7 +2649,7 @@ fn tag_union_struct_help<'a, I: Iterator<Item = &'a (L, TypeId)>, L: Display + P
.collect::<Vec<String>>()
.join(", ");
let args_to_payload = if is_tag_union_payload {
let prefixed_fields = sorted_fields
let prefixed_fields = fields
.iter()
.enumerate()
.map(|(index, (label, _))| {
@ -2361,7 +2744,16 @@ fn cannot_derive_default(roc_type: &RocType, types: &Types) -> bool {
| RocType::TagUnion { .. }
| RocType::RocResult(_, _)
| RocType::RecursivePointer { .. }
| RocType::Function { .. } => true,
| RocType::Struct {
fields: RocStructFields::HasClosure { .. },
..
}
| RocType::TagUnionPayload {
fields: RocStructFields::HasClosure { .. },
..
}
| RocType::Unsized => true,
RocType::Function { .. } => true,
RocType::RocStr | RocType::Bool | RocType::Num(_) => false,
RocType::RocList(id) | RocType::RocSet(id) | RocType::RocBox(id) => {
cannot_derive_default(types.get_type(*id), types)
@ -2370,10 +2762,16 @@ fn cannot_derive_default(roc_type: &RocType, types: &Types) -> bool {
cannot_derive_default(types.get_type(*key_id), types)
|| cannot_derive_default(types.get_type(*val_id), types)
}
RocType::Struct { fields, .. } => fields
RocType::Struct {
fields: RocStructFields::HasNoClosure { fields },
..
} => fields
.iter()
.any(|(_, type_id)| cannot_derive_default(types.get_type(*type_id), types)),
RocType::TagUnionPayload { fields, .. } => fields
RocType::TagUnionPayload {
fields: RocStructFields::HasNoClosure { fields },
..
} => fields
.iter()
.any(|(_, type_id)| cannot_derive_default(types.get_type(*type_id), types)),
}
@ -2387,6 +2785,7 @@ fn cannot_derive_copy(roc_type: &RocType, types: &Types) -> bool {
| RocType::Bool
| RocType::Num(_)
| RocType::TagUnion(RocTagUnion::Enumeration { .. })
| RocType::Unsized
| RocType::Function { .. } => false,
RocType::RocStr
| RocType::RocList(_)
@ -2398,9 +2797,18 @@ fn cannot_derive_copy(roc_type: &RocType, types: &Types) -> bool {
| RocType::TagUnion(RocTagUnion::Recursive { .. })
| RocType::RecursivePointer { .. }
| RocType::TagUnion(RocTagUnion::NonNullableUnwrapped { .. }) => true,
RocType::TagUnion(RocTagUnion::SingleTagStruct { payload_fields, .. }) => payload_fields
RocType::TagUnion(RocTagUnion::SingleTagStruct {
payload: RocSingleTagPayload::HasNoClosure { payload_fields },
..
}) => payload_fields
.iter()
.any(|type_id| cannot_derive_copy(types.get_type(*type_id), types)),
RocType::TagUnion(RocTagUnion::SingleTagStruct {
payload: RocSingleTagPayload::HasClosure { payload_getters },
..
}) => payload_getters
.iter()
.any(|(type_id, _)| cannot_derive_copy(types.get_type(*type_id), types)),
RocType::TagUnion(RocTagUnion::NonRecursive { tags, .. }) => {
tags.iter().any(|(_, payloads)| {
payloads
@ -2412,12 +2820,26 @@ fn cannot_derive_copy(roc_type: &RocType, types: &Types) -> bool {
cannot_derive_copy(types.get_type(*ok_id), types)
|| cannot_derive_copy(types.get_type(*err_id), types)
}
RocType::Struct { fields, .. } => fields
RocType::Struct {
fields: RocStructFields::HasNoClosure { fields },
..
}
| RocType::TagUnionPayload {
fields: RocStructFields::HasNoClosure { fields },
..
} => fields
.iter()
.any(|(_, type_id)| cannot_derive_copy(types.get_type(*type_id), types)),
RocType::TagUnionPayload { fields, .. } => fields
RocType::Struct {
fields: RocStructFields::HasClosure { fields },
..
}
| RocType::TagUnionPayload {
fields: RocStructFields::HasClosure { fields },
..
} => fields
.iter()
.any(|(_, type_id)| cannot_derive_copy(types.get_type(*type_id), types)),
.any(|(_, type_id, _)| cannot_derive_copy(types.get_type(*type_id), types)),
}
}
@ -2441,7 +2863,8 @@ fn has_float_help(roc_type: &RocType, types: &Types, do_not_recurse: &[TypeId])
| RocType::RocStr
| RocType::Bool
| RocType::TagUnion(RocTagUnion::Enumeration { .. })
| RocType::Function { .. } => false,
| RocType::Function { .. }
| RocType::Unsized => false,
RocType::RocList(id) | RocType::RocSet(id) | RocType::RocBox(id) => {
has_float_help(types.get_type(*id), types, do_not_recurse)
}
@ -2453,30 +2876,61 @@ fn has_float_help(roc_type: &RocType, types: &Types, do_not_recurse: &[TypeId])
has_float_help(types.get_type(*key_id), types, do_not_recurse)
|| has_float_help(types.get_type(*val_id), types, do_not_recurse)
}
RocType::Struct { fields, .. } => fields
RocType::TagUnionPayload {
fields: RocStructFields::HasNoClosure { fields },
name: _,
}
| RocType::Struct {
fields: RocStructFields::HasNoClosure { fields },
name: _,
} => fields
.iter()
.any(|(_, type_id)| has_float_help(types.get_type(*type_id), types, do_not_recurse)),
RocType::TagUnionPayload { fields, .. } => fields
RocType::TagUnionPayload {
fields: RocStructFields::HasClosure { fields },
name: _,
}
| RocType::Struct {
fields: RocStructFields::HasClosure { fields },
name: _,
} => fields
.iter()
.any(|(_, type_id)| has_float_help(types.get_type(*type_id), types, do_not_recurse)),
RocType::TagUnion(RocTagUnion::SingleTagStruct { payload_fields, .. }) => payload_fields
.any(|(_, type_id, _)| has_float_help(types.get_type(*type_id), types, do_not_recurse)),
RocType::TagUnion(RocTagUnion::SingleTagStruct {
payload: RocSingleTagPayload::HasNoClosure { payload_fields },
..
}) => payload_fields
.iter()
.any(|type_id| has_float_help(types.get_type(*type_id), types, do_not_recurse)),
RocType::TagUnion(RocTagUnion::Recursive { tags, .. })
| RocType::TagUnion(RocTagUnion::NonRecursive { tags, .. }) => {
tags.iter().any(|(_, payloads)| {
RocType::TagUnion(RocTagUnion::SingleTagStruct {
payload: RocSingleTagPayload::HasClosure { payload_getters },
..
}) => payload_getters
.iter()
.any(|(type_id, _)| has_float_help(types.get_type(*type_id), types, do_not_recurse)),
RocType::TagUnion(RocTagUnion::Recursive {
tags,
name: _,
discriminant_offset: _,
discriminant_size: _,
})
| RocType::TagUnion(RocTagUnion::NonRecursive {
tags,
name: _,
discriminant_offset: _,
discriminant_size: _,
})
| RocType::TagUnion(RocTagUnion::NullableWrapped {
tags,
name: _,
index_of_null_tag: _,
discriminant_size: _,
discriminant_offset: _,
}) => tags.iter().any(|(_, payloads)| {
payloads
.iter()
.any(|id| has_float_help(types.get_type(*id), types, do_not_recurse))
})
}
RocType::TagUnion(RocTagUnion::NullableWrapped { tags, .. }) => {
tags.iter().any(|(_, payloads)| {
payloads
.iter()
.any(|id| has_float_help(types.get_type(*id), types, do_not_recurse))
})
}
}),
RocType::TagUnion(RocTagUnion::NonNullableUnwrapped { payload, .. })
| RocType::TagUnion(RocTagUnion::NullableUnwrapped {
non_null_payload: payload,

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
// ⚠️ 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)]
@ -15,3 +16,8 @@
#![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;

View file

@ -1,10 +0,0 @@
app "app"
packages { pf: "platform.roc" }
imports []
provides [main] to pf
main =
Dict.empty {}
|> Dict.insert "foo" "this will be overwritten"
|> Dict.insert "baz" "blah"
|> Dict.insert "foo" "bar"

View file

@ -1,9 +0,0 @@
platform "test-platform"
requires {} { main : _ }
exposes []
packages {}
imports []
provides [mainForHost]
mainForHost : Dict Str Str
mainForHost = main

View file

@ -1,93 +0,0 @@
mod test_glue;
use roc_std::{RocDict, RocStr};
extern "C" {
#[link_name = "roc__mainForHost_1_exposed_generic"]
fn roc_main(_: *mut RocDict<RocStr, RocStr>);
}
#[no_mangle]
pub extern "C" fn rust_main() -> i32 {
use std::cmp::Ordering;
use std::collections::hash_set::HashSet;
let dict = unsafe {
let mut ret: core::mem::MaybeUninit<RocDict<RocStr, RocStr>> =
core::mem::MaybeUninit::uninit();
roc_main(ret.as_mut_ptr());
ret.assume_init()
};
// Verify that it has all the expected traits.
assert!(dict == dict); // PartialEq
assert_eq!(dict.len(), 2); // len
assert!(dict.clone() == dict.clone()); // Clone
assert!(dict.partial_cmp(&dict) == Some(Ordering::Equal)); // PartialOrd
assert!(dict.cmp(&dict) == Ordering::Equal); // Ord
let mut set = HashSet::new();
set.insert(dict.clone()); // Eq, Hash
set.insert(dict.clone());
assert_eq!(set.len(), 1);
println!("dict was: {:?}", dict); // Debug
// Exit code
0
}
// Externs required by roc_std and by the Roc app
use core::ffi::c_void;
use std::ffi::CStr;
use std::os::raw::c_char;
#[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(c_ptr: *mut c_void, tag_id: u32) {
match tag_id {
0 => {
let slice = CStr::from_ptr(c_ptr as *const c_char);
let string = slice.to_str().unwrap();
eprintln!("Roc hit a panic: {}", string);
std::process::exit(1);
}
_ => todo!(),
}
}
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[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)
}

View file

@ -1,11 +0,0 @@
app "app"
packages { pf: "platform.roc" }
imports []
provides [main] to pf
main =
Set.empty {}
|> Set.insert "foo"
|> Set.insert "bar"
|> Set.insert "foo"
|> Set.insert "baz"

View file

@ -1,9 +0,0 @@
platform "test-platform"
requires {} { main : _ }
exposes []
packages {}
imports []
provides [mainForHost]
mainForHost : Set Str
mainForHost = main

View file

@ -1,92 +0,0 @@
mod test_glue;
use roc_std::{RocSet, RocStr};
extern "C" {
#[link_name = "roc__mainForHost_1_exposed_generic"]
fn roc_main(_: *mut RocSet<RocStr>);
}
#[no_mangle]
pub extern "C" fn rust_main() -> i32 {
use std::cmp::Ordering;
use std::collections::hash_set::HashSet;
let set = unsafe {
let mut ret: core::mem::MaybeUninit<RocSet<RocStr>> = core::mem::MaybeUninit::uninit();
roc_main(ret.as_mut_ptr());
ret.assume_init()
};
// Verify that it has all the expected traits.
assert!(set == set); // PartialEq
assert_eq!(set.len(), 3); // len
assert!(set.clone() == set.clone()); // Clone
assert!(set.partial_cmp(&set) == Some(Ordering::Equal)); // PartialOrd
assert!(set.cmp(&set) == Ordering::Equal); // Ord
let mut hash_set = HashSet::new();
hash_set.insert(set.clone()); // Eq, Hash
hash_set.insert(set.clone());
assert_eq!(hash_set.len(), 1);
println!("set was: {:?}", set); // Debug
// Exit code
0
}
// Externs required by roc_std and by the Roc app
use core::ffi::c_void;
use std::ffi::CStr;
use std::os::raw::c_char;
#[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(c_ptr: *mut c_void, tag_id: u32) {
match tag_id {
0 => {
let slice = CStr::from_ptr(c_ptr as *const c_char);
let string = slice.to_str().unwrap();
eprintln!("Roc hit a panic: {}", string);
std::process::exit(1);
}
_ => todo!(),
}
}
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[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)
}

View file

@ -34,7 +34,7 @@ mod test_gen_rs {
target_arch = "x86",
target_arch = "x86_64"
))]
#[derive(Clone, Copy, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)]
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(C)]
pub struct MyRcd {
pub b: roc_std::I128,
@ -124,7 +124,7 @@ mod test_gen_rs {
target_arch = "x86",
target_arch = "x86_64"
))]
#[derive(Clone, Copy, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)]
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(C)]
pub struct R1 {
pub b: roc_std::U128,
@ -212,7 +212,7 @@ mod test_gen_rs {
target_arch = "x86",
target_arch = "x86_64"
))]
#[derive(Clone, Copy, Eq, Ord, Hash, PartialEq, PartialOrd)]
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(u8)]
pub enum Enumeration {
Bar = 0,

View file

@ -72,12 +72,6 @@ mod glue_cli_run {
fixtures! {
basic_record:"basic-record" => "Record was: MyRcd { b: 42, a: 1995 }\n",
nested_record:"nested-record" => "Record was: Outer { y: \"foo\", z: [1, 2], x: Inner { b: 24.0, a: 5 } }\n",
dict:"dict" => indoc!(r#"
dict was: RocDict {"foo": "bar", "baz": "blah"}
"#),
set:"set" => indoc!(r#"
set was: RocSet {"foo", "bar", "baz"}
"#),
enumeration:"enumeration" => "tag_union was: MyEnum::Foo, Bar is: MyEnum::Bar, Baz is: MyEnum::Baz\n",
union_with_padding:"union-with-padding" => indoc!(r#"
tag_union was: NonRecursive::Foo("This is a test")

View file

@ -15,6 +15,7 @@ path = "src/lib.rs"
[dependencies]
roc_collections = { path = "../compiler/collections" }
roc_error_macros = { path = "../error_macros" }
roc_module = { path = "../compiler/module" }
roc_load = { path = "../compiler/load" }
roc_mono = { path = "../compiler/mono" }
roc_packaging = { path = "../packaging" }

View file

@ -1532,7 +1532,16 @@ fn surgery_elf_help(
let func_virt_offset = match app_func_vaddr_map.get(func_name) {
Some(offset) => *offset as u64,
None => {
internal_error!("Function, {}, was not defined by the app", &func_name);
eprintln!("Error:");
eprintln!("\n\tFunction, {}, was not defined by the app.", &func_name);
eprintln!("\nPotential causes:");
eprintln!("\n\t- because the platform was built with a non-compatible version of roc compared to the one you are running.");
eprintln!("\n\t\tsolutions:");
eprintln!("\t\t\t+ Downgrade your roc version to the one that was used to build the platform.");
eprintln!("\t\t\t+ Or ask the platform author to release a new version of the platform using a current roc release.");
eprintln!("\n\t- This can also occur due to a bug in the compiler. In that case, file an issue here: https://github.com/roc-lang/roc/issues/new/choose");
std::process::exit(1);
}
};
if verbose {

View file

@ -6,7 +6,8 @@
use memmap2::{Mmap, MmapMut};
use object::Object;
use roc_error_macros::internal_error;
use roc_load::{EntryPoint, ExecutionMode, LoadConfig, Threading};
use roc_load::{EntryPoint, ExecutionMode, ExposedToHost, LoadConfig, Threading};
use roc_module::symbol::Interns;
use roc_packaging::cache::RocCacheDir;
use roc_reporting::report::{RenderTarget, DEFAULT_PALETTE};
use roc_target::get_target_triple_str;
@ -61,7 +62,7 @@ pub fn supported(link_type: LinkType, target: &Triple) -> bool {
}
}
pub const PRECOMPILED_HOST_EXT: &str = "rh1"; // Short for "roc host version 1" (so we can change format in the future)
pub const PRECOMPILED_HOST_EXT: &str = "rh"; // Short for "roc host"
pub fn preprocessed_host_filename(target: &Triple) -> Option<String> {
roc_target::get_target_triple_str(target).map(|x| format!("{}.{}", x, PRECOMPILED_HOST_EXT))
@ -70,7 +71,7 @@ pub fn preprocessed_host_filename(target: &Triple) -> Option<String> {
fn metadata_file_name(target: &Triple) -> String {
let target_triple_str = get_target_triple_str(target);
format!("metadata_{}.rm2", target_triple_str.unwrap_or("unknown"))
format!("metadata_{}.rm", target_triple_str.unwrap_or("unknown"))
}
pub fn link_preprocessed_host(
@ -111,7 +112,7 @@ pub fn generate_stub_lib(
let exposed_to_host = loaded
.exposed_to_host
.values
.top_level_values
.keys()
.map(|x| x.as_str(&loaded.interns).to_string())
.collect();
@ -129,6 +130,11 @@ pub fn generate_stub_lib(
})
.collect();
let exposed_symbols = ExposedSymbols {
top_level_values: exposed_to_host,
exported_closure_types,
};
if let EntryPoint::Executable { platform_path, .. } = &loaded.entry_point {
let stub_lib = if let target_lexicon::OperatingSystem::Windows = triple.operating_system {
platform_path.with_file_name("libapp.obj")
@ -136,7 +142,7 @@ pub fn generate_stub_lib(
platform_path.with_file_name("libapp.so")
};
let stub_dll_symbols = make_stub_dll_symbols(exposed_to_host, exported_closure_types);
let stub_dll_symbols = exposed_symbols.stub_dll_symbols();
generate_dynamic_lib(triple, &stub_dll_symbols, &stub_lib);
} else {
unreachable!();
@ -147,35 +153,87 @@ pub fn generate_stub_lib(
pub fn generate_stub_lib_from_loaded(
target: &Triple,
platform_main_roc: &Path,
exposed_to_host: Vec<String>,
exported_closure_types: Vec<String>,
) -> (PathBuf, Vec<String>) {
let stub_lib = if let target_lexicon::OperatingSystem::Windows = target.operating_system {
stub_dll_symbols: &[String],
) -> PathBuf {
let stub_lib_path = if let target_lexicon::OperatingSystem::Windows = target.operating_system {
platform_main_roc.with_file_name("libapp.dll")
} else {
platform_main_roc.with_file_name("libapp.so")
};
let stub_dll_symbols = make_stub_dll_symbols(exposed_to_host, exported_closure_types);
generate_dynamic_lib(target, &stub_dll_symbols, &stub_lib);
generate_dynamic_lib(target, stub_dll_symbols, &stub_lib_path);
(stub_lib, stub_dll_symbols)
stub_lib_path
}
fn make_stub_dll_symbols(
exposed_to_host: Vec<String>,
exported_closure_types: Vec<String>,
) -> Vec<String> {
pub struct ExposedSymbols {
// usually just `mainForhost`
pub top_level_values: Vec<String>,
// old type exposing mechanism
pub exported_closure_types: Vec<String>,
}
impl ExposedSymbols {
pub fn from_exposed_to_host(interns: &Interns, exposed_to_host: &ExposedToHost) -> Vec<String> {
let mut custom_names = Vec::new();
for sym in exposed_to_host {
for x in exposed_to_host.top_level_values.keys() {
let sym = x.as_str(interns);
custom_names.extend([
format!("roc__{}_1_exposed", sym),
format!("roc__{}_1_exposed_generic", sym),
format!("roc__{}_1_exposed_size", sym),
]);
let exported_closure_types = exposed_to_host
.closure_types
.iter()
.map(|x| format!("{}_{}", x.module_string(interns), x.as_str(interns)));
for (i, _) in exported_closure_types.enumerate() {
custom_names.extend([
format!("roc__{}_{i}_caller", sym),
format!("roc__{}_{i}_size", sym),
format!("roc__{}_{i}_result_size", sym),
]);
}
}
for x in &exposed_to_host.getters {
let sym = x.as_str(interns);
custom_names.extend([
sym.to_string(),
format!("{sym}_generic"),
format!("{sym}_size"),
]);
}
for (top_level_value, lambda_set_id) in &exposed_to_host.lambda_sets {
let sym = top_level_value.as_str(interns);
let id = lambda_set_id.0;
custom_names.extend([format!("roc__{sym}_{id}_caller")]);
}
// on windows (PE) binary search is used on the symbols,
// so they must be in alphabetical order
custom_names.sort_unstable();
custom_names
}
pub fn stub_dll_symbols(&self) -> Vec<String> {
let mut custom_names = Vec::new();
for sym in &self.top_level_values {
custom_names.extend([
format!("roc__{}_1_exposed", sym),
format!("roc__{}_1_exposed_generic", sym),
format!("roc__{}_size", sym),
]);
for closure_type in &exported_closure_types {
for closure_type in &self.exported_closure_types {
custom_names.extend([
format!("roc__{}_1_{}_caller", sym, closure_type),
format!("roc__{}_1_{}_size", sym, closure_type),
@ -189,6 +247,7 @@ fn make_stub_dll_symbols(
custom_names.sort_unstable();
custom_names
}
}
fn generate_dynamic_lib(target: &Triple, stub_dll_symbols: &[String], stub_lib_path: &Path) {

View file

@ -55,21 +55,15 @@ impl Metadata {
}
pub fn read_from_file(metadata_filename: &Path) -> Self {
let input =
std::fs::File::open(metadata_filename)
.unwrap_or_else(
|e| internal_error!(r#"
let input = std::fs::File::open(metadata_filename).unwrap_or_else(|e| {
internal_error!(
r#"
Error:
{}
> This may occur when using a release of roc that relies on a specific metadata format like 'rm2' and the imported platform only has an older metadata format available, like rm1.
The platform you are using can be found in the header of your main.roc: `packages {{ pf: <PLATFORM>}}`.
You should check if a more recent version of the platform is available.
If not, you should notify the author of the platform about this issue.
"#, e)
);
{}\n"#,
e
)
});
match deserialize_from(BufReader::new(input)) {
Ok(data) => data,

View file

@ -151,9 +151,9 @@ fn write_archive<W: Write>(path: &Path, writer: W) -> io::Result<()> {
if [
// surgical linker format
Some("rh1"),
Some("rh"),
// metadata file
Some("rm2"),
Some("rm"),
// legacy linker formats
Some("o"),
Some("obj"),

View file

@ -42,8 +42,13 @@ pub fn gen_and_eval_llvm<'a, I: Iterator<Item = &'a str>>(
}
};
debug_assert_eq!(loaded.exposed_to_host.values.len(), 1);
let (main_fn_symbol, main_fn_var) = loaded.exposed_to_host.values.iter().next().unwrap();
debug_assert_eq!(loaded.exposed_to_host.top_level_values.len(), 1);
let (main_fn_symbol, main_fn_var) = loaded
.exposed_to_host
.top_level_values
.iter()
.next()
.unwrap();
let main_fn_symbol = *main_fn_symbol;
let main_fn_var = *main_fn_var;

View file

@ -217,8 +217,8 @@ pub async fn entrypoint_from_js(src: String) -> Result<String, String> {
..
} = mono;
debug_assert_eq!(exposed_to_host.values.len(), 1);
let (main_fn_symbol, main_fn_var) = exposed_to_host.values.iter().next().unwrap();
debug_assert_eq!(exposed_to_host.top_level_values.len(), 1);
let (main_fn_symbol, main_fn_var) = exposed_to_host.top_level_values.iter().next().unwrap();
let main_fn_symbol = *main_fn_symbol;
let main_fn_var = *main_fn_var;
@ -242,7 +242,7 @@ pub async fn entrypoint_from_js(src: String) -> Result<String, String> {
module_id,
stack_bytes: roc_gen_wasm::Env::DEFAULT_STACK_BYTES,
exposed_to_host: exposed_to_host
.values
.top_level_values
.keys()
.copied()
.collect::<MutSet<_>>(),

View file

@ -129,6 +129,7 @@ impl<K: Debug, V: Debug> Debug for RocDict<K, V> {
/// contiguously in memory. If we separate them at some point, we'll need to
/// change this implementation drastically!
#[derive(Eq)]
#[repr(C)]
union RocDictItem<K, V> {
key_first: ManuallyDrop<KeyFirst<K, V>>,
value_first: ManuallyDrop<ValueFirst<K, V>>,

View file

@ -9,7 +9,7 @@ use core::{
intrinsics::copy_nonoverlapping,
iter::FromIterator,
mem::{self, ManuallyDrop},
ops::Deref,
ops::{Deref, DerefMut},
ptr::{self, NonNull},
};
@ -131,6 +131,14 @@ impl<T> RocList<T> {
}
}
pub fn as_mut_ptr(&mut self) -> *mut T {
self.as_mut_slice().as_mut_ptr()
}
pub fn as_ptr(&self) -> *const T {
self.as_slice().as_ptr()
}
/// Marks a list as readonly. This means that it will be leaked.
/// For constants passed in from platform to application, this may be reasonable.
///
@ -164,6 +172,19 @@ impl<T> RocList<T> {
self
}
/// Note that there is no way to convert directly to a Vec.
///
/// This is because RocList values are not allocated using the system allocator, so
/// handing off any heap-allocated bytes to a Vec would not work because its Drop
/// implementation would try to free those bytes using the wrong allocator.
///
/// Instead, if you want a Rust Vec, you need to do a fresh allocation and copy the
/// bytes over - in other words, calling this `as_slice` method and then calling `to_vec`
/// on that.
pub fn as_mut_slice(&mut self) -> &mut [T] {
&mut *self
}
#[inline(always)]
fn elements_and_storage(&self) -> Option<(NonNull<ManuallyDrop<T>>, &Cell<Storage>)> {
let elements = self.elements?;
@ -403,6 +424,19 @@ impl<T> Deref for RocList<T> {
}
}
impl<T> DerefMut for RocList<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
if let Some(elements) = self.elements {
let ptr = elements.as_ptr().cast::<T>();
let elements = ptr::slice_from_raw_parts_mut(ptr, self.length);
unsafe { &mut *elements }
} else {
&mut []
}
}
}
impl<T> Default for RocList<T> {
fn default() -> Self {
Self::empty()

View file

@ -26,8 +26,10 @@ fn build_host() {
&target,
&platform_main_roc,
&preprocessed_host_path,
vec![String::from("mainForHost")],
vec![],
roc_linker::ExposedSymbols {
top_level_values: vec![String::from("mainForHost")],
exported_closure_types: vec![],
},
);
}

4
examples/.gitignore vendored
View file

@ -2,7 +2,7 @@
libhost.a
libapp.so
dynhost
*.rm1
*.rh1
*.rm
*.rh
helloWorld

View file

@ -9,10 +9,9 @@ use core::mem::MaybeUninit;
use glue::Metadata;
use roc_std::{RocDict, RocList, RocResult, RocStr};
use std::borrow::{Borrow, Cow};
use std::ffi::{CStr, OsStr};
use std::ffi::{ OsStr};
use std::fs::File;
use std::io::Write;
use std::os::raw::c_char;
use std::path::Path;
use std::time::Duration;
@ -23,17 +22,17 @@ extern "C" {
#[link_name = "roc__mainForHost_1_exposed_generic"]
fn roc_main(output: *mut u8);
#[link_name = "roc__mainForHost_size"]
#[link_name = "roc__mainForHost_1_exposed_size"]
fn roc_main_size() -> i64;
#[link_name = "roc__mainForHost_1__Fx_caller"]
#[link_name = "roc__mainForHost_0_caller"]
fn call_Fx(flags: *const u8, closure_data: *const u8, output: *mut u8);
#[allow(dead_code)]
#[link_name = "roc__mainForHost_1__Fx_size"]
#[link_name = "roc__mainForHost_0_size"]
fn size_Fx() -> i64;
#[link_name = "roc__mainForHost_1__Fx_result_size"]
#[link_name = "roc__mainForHost_0_result_size"]
fn size_Fx_result() -> i64;
}

View file

@ -25,10 +25,10 @@ const mem = std.mem;
const Allocator = mem.Allocator;
extern fn roc__mainForHost_1_exposed_generic([*]u8) void;
extern fn roc__mainForHost_size() i64;
extern fn roc__mainForHost_1__Fx_caller(*const u8, [*]u8, [*]u8) void;
extern fn roc__mainForHost_1__Fx_size() i64;
extern fn roc__mainForHost_1__Fx_result_size() i64;
extern fn roc__mainForHost_1_exposed_size() i64;
extern fn roc__mainForHost_0_caller(*const u8, [*]u8, [*]u8) void;
extern fn roc__mainForHost_0_size() i64;
extern fn roc__mainForHost_0_result_size() i64;
const Align = 2 * @alignOf(usize);
extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque;
@ -123,7 +123,7 @@ pub export fn main() u8 {
const stderr = std.io.getStdErr().writer();
// NOTE the return size can be zero, which will segfault. Always allocate at least 8 bytes
const size = std.math.max(8, @intCast(usize, roc__mainForHost_size()));
const size = std.math.max(8, @intCast(usize, roc__mainForHost_1_exposed_size()));
const raw_output = allocator.allocAdvanced(u8, @alignOf(u64), @intCast(usize, size), .at_least) catch unreachable;
var output = @ptrCast([*]u8, raw_output);
@ -152,7 +152,7 @@ fn to_seconds(tms: std.os.timespec) f64 {
fn call_the_closure(closure_data_pointer: [*]u8) void {
const allocator = std.heap.page_allocator;
const size = roc__mainForHost_1__Fx_result_size();
const size = roc__mainForHost_0_result_size();
if (size == 0) {
// the function call returns an empty record
@ -160,7 +160,7 @@ fn call_the_closure(closure_data_pointer: [*]u8) void {
// So it's special-cased
const flags: u8 = 0;
var result: [1]u8 = .{0};
roc__mainForHost_1__Fx_caller(&flags, closure_data_pointer, &result);
roc__mainForHost_0_caller(&flags, closure_data_pointer, &result);
return;
}
@ -173,7 +173,7 @@ fn call_the_closure(closure_data_pointer: [*]u8) void {
}
const flags: u8 = 0;
roc__mainForHost_1__Fx_caller(&flags, closure_data_pointer, output);
roc__mainForHost_0_caller(&flags, closure_data_pointer, output);
return;
}

View file

@ -14,17 +14,17 @@ extern "C" {
#[link_name = "roc__mainForHost_1_exposed_generic"]
fn roc_main(output: *mut u8, args: &RocStr);
#[link_name = "roc__mainForHost_size"]
#[link_name = "roc__mainForHost_1_exposed_size"]
fn roc_main_size() -> i64;
#[link_name = "roc__mainForHost_1__Fx_caller"]
#[link_name = "roc__mainForHost_0_caller"]
fn call_Fx(flags: *const u8, closure_data: *const u8, output: *mut u8);
#[allow(dead_code)]
#[link_name = "roc__mainForHost_1__Fx_size"]
#[link_name = "roc__mainForHost_0_size"]
fn size_Fx() -> i64;
#[link_name = "roc__mainForHost_1__Fx_result_size"]
#[link_name = "roc__mainForHost_0_result_size"]
fn size_Fx_result() -> i64;
}
@ -171,14 +171,14 @@ pub extern "C" fn roc_fx_getChar() -> u8 {
pub extern "C" fn roc_fx_putLine(line: &RocStr) {
let string = line.as_str();
println!("{}", string);
std::io::stdout().lock().flush();
let _ = std::io::stdout().lock().flush();
}
#[no_mangle]
pub extern "C" fn roc_fx_putRaw(line: &RocStr) {
let string = line.as_str();
print!("{}", string);
std::io::stdout().lock().flush();
let _ = std::io::stdout().lock().flush();
}
#[no_mangle]
@ -207,7 +207,8 @@ pub extern "C" fn roc_fx_getFileBytes(br_ptr: *mut BufReader<File>) -> RocList<u
#[no_mangle]
pub extern "C" fn roc_fx_closeFile(br_ptr: *mut BufReader<File>) {
unsafe {
Box::from_raw(br_ptr);
let boxed = Box::from_raw(br_ptr);
drop(boxed)
}
}
@ -227,7 +228,7 @@ pub extern "C" fn roc_fx_openFile(name: &RocStr) -> *mut BufReader<File> {
}
#[no_mangle]
pub extern "C" fn roc_fx_withFileOpen(name: &RocStr, buffer: *const u8) {
pub extern "C" fn roc_fx_withFileOpen(_name: &RocStr, _buffer: *const u8) {
// TODO: figure out accepting a closure in an fx and passing data to it.
// let f = File::open(name.as_str()).expect("Unable to open file");
// let mut br = BufReader::new(f);

View file

@ -32,12 +32,12 @@ extern fn roc__mainForHost_size() i64;
const ConstModel = [*]const u8;
const MutModel = [*]u8;
extern fn roc__mainForHost_1__Init_caller([*]u8, [*]u8, MutModel) void;
extern fn roc__mainForHost_1__Init_size() i64;
extern fn roc__mainForHost_1__Init_result_size() i64;
extern fn roc__mainForHost_0_caller([*]u8, [*]u8, MutModel) void;
extern fn roc__mainForHost_0_size() i64;
extern fn roc__mainForHost_0_result_size() i64;
fn allocate_model(allocator: *Allocator) MutModel {
const size = roc__mainForHost_1__Init_result_size();
const size = roc__mainForHost_0_result_size();
const raw_output = allocator.allocAdvanced(u8, @alignOf(u64), @intCast(usize, size), .at_least) catch unreachable;
var output = @ptrCast([*]u8, raw_output);
@ -48,33 +48,33 @@ fn init(allocator: *Allocator) ConstModel {
const closure: [*]u8 = undefined;
const output = allocate_model(allocator);
roc__mainForHost_1__Init_caller(closure, closure, output);
roc__mainForHost_0_caller(closure, closure, output);
return output;
}
extern fn roc__mainForHost_1__Update_caller(ConstModel, *const RocStr, [*]u8, MutModel) void;
extern fn roc__mainForHost_1__Update_size() i64;
extern fn roc__mainForHost_1__Update_result_size() i64;
extern fn roc__mainForHost_1_caller(ConstModel, *const RocStr, [*]u8, MutModel) void;
extern fn roc__mainForHost_1_size() i64;
extern fn roc__mainForHost_1_result_size() i64;
fn update(allocator: *Allocator, model: ConstModel, msg: RocStr) ConstModel {
const closure: [*]u8 = undefined;
const output = allocate_model(allocator);
roc__mainForHost_1__Update_caller(model, &msg, closure, output);
roc__mainForHost_1_caller(model, &msg, closure, output);
return output;
}
extern fn roc__mainForHost_1__View_caller(ConstModel, [*]u8, *RocStr) void;
extern fn roc__mainForHost_1__View_size() i64;
extern fn roc__mainForHost_1__View_result_size() i64;
extern fn roc__mainForHost_2_caller(ConstModel, [*]u8, *RocStr) void;
extern fn roc__mainForHost_2_size() i64;
extern fn roc__mainForHost_2_result_size() i64;
fn view(input: ConstModel) RocStr {
const closure: [*]u8 = undefined;
var output: RocStr = undefined;
roc__mainForHost_1__View_caller(input, closure, &output);
roc__mainForHost_2_caller(input, closure, &output);
return output;
}

9
examples/glue/glue.roc Normal file
View file

@ -0,0 +1,9 @@
app "rocLovesRust"
packages { pf: "rust-platform/main.roc" }
imports []
provides [main] to pf
main =
StdoutWrite "Roc <3 Rust!\n" \{} ->
StdoutWrite "Roc <3 Rust!\n" \{} ->
Done

View file

@ -0,0 +1,2 @@
[target.x86_64-pc-windows-gnu]
linker = "zig-cc"

37
examples/glue/rust-platform/Cargo.lock generated Normal file
View file

@ -0,0 +1,37 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "arrayvec"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
[[package]]
name = "host"
version = "0.0.1"
dependencies = [
"libc",
"roc_std",
]
[[package]]
name = "libc"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714"
[[package]]
name = "roc_std"
version = "0.0.1"
dependencies = [
"arrayvec",
"static_assertions",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"

View file

@ -0,0 +1,22 @@
[package]
name = "host"
version = "0.0.1"
authors = ["The Roc Contributors"]
license = "UPL-1.0"
edition = "2021"
links = "app"
[lib]
name = "host"
path = "src/lib.rs"
crate-type = ["staticlib", "rlib"]
[[bin]]
name = "host"
path = "src/main.rs"
[dependencies]
roc_std = { path = "../../../crates/roc_std" }
libc = "0.2"
[workspace]

View file

@ -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=.");
}

View file

@ -0,0 +1,3 @@
extern int rust_main();
int main() { return rust_main(); }

View file

@ -0,0 +1,17 @@
platform "echo-in-rust"
requires {} { main : _ }
exposes []
packages {}
imports []
provides [mainForHost]
# mainForHost : [StdoutWrite Str (({} -> Op) as Fx0), StderrWrite Str (({} -> Op) as Fx1), Done] as Op
mainForHost : [StdoutWrite Str ({} -> Op), StderrWrite Str ({} -> Op), Done] as Op
mainForHost = main
# mainForHost : { x: Str, y: {} -> Str }
# mainForHost =
# y = "foo"
#
# when main is
# _ -> { x: "bar", y: \{} -> y }

Binary file not shown.

View file

@ -0,0 +1,9 @@
[toolchain]
channel = "1.65.0"
profile = "default"
components = [
# for usages of rust-analyzer or similar tools inside `nix develop`
"rust-src"
]

View file

@ -0,0 +1,744 @@
// ⚠️ 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<Op_StderrWrite>,
StdoutWrite: core::mem::ManuallyDrop<Op_StdoutWrite>,
_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<u8>,
}
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<u8>,
}
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<roc_std::Storage>> {
let mask = match std::mem::size_of::<usize>() {
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<roc_std::Storage>;
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::<u8, discriminant_Op>((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::<union_Op>();
let align = core::mem::align_of::<union_Op>() as u32;
unsafe {
let ptr = roc_std::roc_alloc_refcounted::<union_Op>();
*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::<union_Op>();
let align = core::mem::align_of::<union_Op>() as u32;
unsafe {
let ptr = roc_std::roc_alloc_refcounted::<union_Op>();
*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::<u8, discriminant_Op>((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::<Self>().max(core::mem::align_of::<roc_std::Storage>());
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<core::cmp::Ordering> {
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<H: core::hash::Hasher>(&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(),
}
}
}
}

View file

@ -0,0 +1,132 @@
#![allow(non_snake_case)]
mod glue;
use core::ffi::c_void;
use glue::Op;
use roc_std::RocStr;
use std::ffi::CStr;
use std::io::Write;
use std::mem::MaybeUninit;
use std::os::raw::c_char;
extern "C" {
#[link_name = "roc__mainForHost_1_exposed_generic"]
fn roc_main(_: *mut Op);
}
#[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(c_ptr: *mut c_void, tag_id: u32) {
match tag_id {
0 => {
let slice = CStr::from_ptr(c_ptr as *const c_char);
let string = slice.to_str().unwrap();
eprintln!("Roc hit a panic: {}", string);
std::process::exit(1);
}
_ => todo!(),
}
}
#[no_mangle]
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
libc::memcpy(dst, src, n)
}
#[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)
}
#[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 as libc::c_uint)
}
#[no_mangle]
pub extern "C" fn rust_main() -> i32 {
use glue::discriminant_Op::*;
println!("Let's do things!");
let mut op: Op = unsafe {
let mut mem = MaybeUninit::uninit();
roc_main(mem.as_mut_ptr());
mem.assume_init()
};
loop {
match dbg!(op.discriminant()) {
StdoutWrite => {
let output: RocStr = unsafe { op.get_StdoutWrite_0() };
op = unsafe { op.get_StdoutWrite_1().force_thunk(()) };
if let Err(e) = std::io::stdout().write_all(output.as_bytes()) {
panic!("Writing to stdout failed! {:?}", e);
}
}
StderrWrite => {
let output: RocStr = unsafe { op.get_StderrWrite_0() };
op = unsafe { op.get_StderrWrite_1().force_thunk(()) };
if let Err(e) = std::io::stderr().write_all(output.as_bytes()) {
panic!("Writing to stdout failed! {:?}", e);
}
}
Done => {
break;
}
}
}
println!("Done!");
// Exit code
0
}

View file

@ -0,0 +1,3 @@
fn main() {
std::process::exit(host::rust_main() as _);
}

View file

@ -1,3 +1,5 @@
#![allow(unused)]
mod graphics;
mod gui;
mod roc;

View file

@ -13,26 +13,26 @@ use winit::event::VirtualKeyCode;
extern "C" {
// program
#[link_name = "roc__programForHost_1_exposed_generic"]
fn roc_program();
// #[link_name = "roc__programForHost_1_exposed_generic"]
// fn roc_program();
#[link_name = "roc__programForHost_size"]
fn roc_program_size() -> i64;
// #[link_name = "roc__programForHost_1_exposed_size"]
// fn roc_program_size() -> i64;
// init
#[link_name = "roc__programForHost_1__Init_caller"]
#[link_name = "roc__programForHost_0_caller"]
fn call_init(size: *const Bounds, closure_data: *const u8, output: *mut Model);
#[link_name = "roc__programForHost_1__Init_size"]
#[link_name = "roc__programForHost_0_size"]
fn init_size() -> i64;
#[link_name = "roc__programForHost_1__Init_result_size"]
#[link_name = "roc__programForHost_0_result_size"]
fn init_result_size() -> i64;
// update
#[link_name = "roc__programForHost_1__Update_caller"]
#[link_name = "roc__programForHost_1_caller"]
fn call_update(
model: *const Model,
event: *const RocEvent,
@ -40,18 +40,18 @@ extern "C" {
output: *mut Model,
);
#[link_name = "roc__programForHost_1__Update_size"]
#[link_name = "roc__programForHost_1_size"]
fn update_size() -> i64;
#[link_name = "roc__programForHost_1__Update_result_size"]
#[link_name = "roc__programForHost_1_result_size"]
fn update_result_size() -> i64;
// render
#[link_name = "roc__programForHost_1__Render_caller"]
#[link_name = "roc__programForHost_2_caller"]
fn call_render(model: *const Model, closure_data: *const u8, output: *mut RocList<RocElem>);
#[link_name = "roc__programForHost_1__Render_size"]
#[link_name = "roc__programForHost_2_size"]
fn roc_render_size() -> i64;
}

View file

@ -1,5 +1,5 @@
app "helloWorld"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.2.1/wx1N6qhU3kKva-4YqsVJde3fho34NqiLD3m620zZ-OI.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.3/5CcipdhTTAtISf4FwlBNHmyu1unYAV8b0MKRwYiEHys.tar.br" }
imports [pf.Stdout]
provides [main] to pf

View file

@ -1,6 +1,6 @@
app "example"
packages {
cli: "https://github.com/roc-lang/basic-cli/releases/download/0.2.1/wx1N6qhU3kKva-4YqsVJde3fho34NqiLD3m620zZ-OI.tar.br",
cli: "https://github.com/roc-lang/basic-cli/releases/download/0.3/5CcipdhTTAtISf4FwlBNHmyu1unYAV8b0MKRwYiEHys.tar.br",
parser: "../package/main.roc",
}
imports [

View file

@ -0,0 +1,744 @@
// ⚠️ 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<Op_StderrWrite>,
StdoutWrite: core::mem::ManuallyDrop<Op_StdoutWrite>,
_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<u8>,
}
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<u8>,
}
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<roc_std::Storage>> {
let mask = match std::mem::size_of::<usize>() {
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<roc_std::Storage>;
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::<u8, discriminant_Op>((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::<union_Op>();
let align = core::mem::align_of::<union_Op>() as u32;
unsafe {
let ptr = roc_std::roc_alloc_refcounted::<union_Op>();
*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::<union_Op>();
let align = core::mem::align_of::<union_Op>() as u32;
unsafe {
let ptr = roc_std::roc_alloc_refcounted::<union_Op>();
*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::<u8, discriminant_Op>((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::<Self>().max(core::mem::align_of::<roc_std::Storage>());
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<core::cmp::Ordering> {
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<H: core::hash::Hasher>(&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(),
}
}
}
}

View file

@ -307,11 +307,16 @@ fn find_files(dir: &Path, file_paths: &mut Vec<PathBuf>) -> std::io::Result<()>
/// and there seems to be no good way to strip it. So we resort to some string manipulation.
pub fn strip_windows_prefix(path_buf: PathBuf) -> std::path::PathBuf {
#[cfg(not(windows))]
return path_buf;
{
path_buf
}
#[cfg(windows)]
{
let path_str = path_buf.display().to_string();
std::path::Path::new(path_str.trim_start_matches(r"\\?\")).to_path_buf()
}
}
fn is_roc_code_block(cbk: &pulldown_cmark::CodeBlockKind) -> bool {

View file

@ -126,7 +126,7 @@ Make a file named `main.roc` and put this in it:
```roc
app "hello"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.2.1/wx1N6qhU3kKva-4YqsVJde3fho34NqiLD3m620zZ-OI.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.3/5CcipdhTTAtISf4FwlBNHmyu1unYAV8b0MKRwYiEHys.tar.br" }
imports [pf.Stdout]
provides [main] to pf
@ -1368,7 +1368,7 @@ Let's take a closer look at the part of `main.roc` above the `main` def:
```roc
app "hello"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.2.1/wx1N6qhU3kKva-4YqsVJde3fho34NqiLD3m620zZ-OI.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.3/5CcipdhTTAtISf4FwlBNHmyu1unYAV8b0MKRwYiEHys.tar.br" }
imports [pf.Stdout]
provides main to pf
```
@ -1380,7 +1380,7 @@ The line `app "hello"` states that this module defines a Roc application, and th
The remaining lines all involve the [platform](https://github.com/roc-lang/roc/wiki/Roc-concepts-explained#platform) this application is built on:
```roc
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.2.1/wx1N6qhU3kKva-4YqsVJde3fho34NqiLD3m620zZ-OI.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.3/5CcipdhTTAtISf4FwlBNHmyu1unYAV8b0MKRwYiEHys.tar.br" }
imports [pf.Stdout]
provides [main] to pf
```
@ -1466,7 +1466,7 @@ Let's start with a basic "Hello World" program.
```roc
app "cli-tutorial"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.2.1/wx1N6qhU3kKva-4YqsVJde3fho34NqiLD3m620zZ-OI.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.3/5CcipdhTTAtISf4FwlBNHmyu1unYAV8b0MKRwYiEHys.tar.br" }
imports [pf.Stdout]
provides [main] to pf
@ -1496,7 +1496,7 @@ Let's change `main` to read a line from `stdin`, and then print it back out agai
```roc
app "cli-tutorial"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.2.1/wx1N6qhU3kKva-4YqsVJde3fho34NqiLD3m620zZ-OI.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.3/5CcipdhTTAtISf4FwlBNHmyu1unYAV8b0MKRwYiEHys.tar.br" }
imports [pf.Stdout, pf.Stdin, pf.Task]
provides [main] to pf
@ -1537,7 +1537,7 @@ This works, but we can make it a little nicer to read. Let's change it to the fo
```roc
app "cli-tutorial"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.2.1/wx1N6qhU3kKva-4YqsVJde3fho34NqiLD3m620zZ-OI.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.3/5CcipdhTTAtISf4FwlBNHmyu1unYAV8b0MKRwYiEHys.tar.br" }
imports [pf.Stdout, pf.Stdin, pf.Task.{ await }]
provides [main] to pf