mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 00:24:34 +00:00
Merge pull request #5093 from roc-lang/glue-getters-rtfeldman
Glue for functions and closures
This commit is contained in:
commit
2276c78d9f
64 changed files with 3864 additions and 1101 deletions
24
.github/workflows/basic_cli_build_release.yml
vendored
24
.github/workflows/basic_cli_build_release.yml
vendored
|
@ -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
1
Cargo.lock
generated
|
@ -3495,6 +3495,7 @@ dependencies = [
|
|||
"roc_collections",
|
||||
"roc_error_macros",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
"roc_packaging",
|
||||
"roc_reporting",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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>(
|
|||
¶m_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
|
||||
(¶ms[..], ¶m_types[..])
|
||||
}
|
||||
(RocReturn::ByPointer, CCReturn::Void) => {
|
||||
// the roc function returns a unit value. like `{}` or `{ { {}, {} }, {} }`.
|
||||
// In C, this is modelled as a function returning void
|
||||
(
|
||||
¶ms[..],
|
||||
// ¶m_types[..param_types.len().saturating_sub(1)],
|
||||
¶m_types[..],
|
||||
¶m_types[..param_types.len().saturating_sub(1)],
|
||||
)
|
||||
}
|
||||
_ => (¶ms[..], ¶m_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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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>>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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<_>>();
|
||||
|
|
|
@ -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 = "";
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -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#"
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
|
|
10
crates/glue/tests/fixtures/dict/app.roc
vendored
10
crates/glue/tests/fixtures/dict/app.roc
vendored
|
@ -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"
|
9
crates/glue/tests/fixtures/dict/platform.roc
vendored
9
crates/glue/tests/fixtures/dict/platform.roc
vendored
|
@ -1,9 +0,0 @@
|
|||
platform "test-platform"
|
||||
requires {} { main : _ }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Dict Str Str
|
||||
mainForHost = main
|
93
crates/glue/tests/fixtures/dict/src/lib.rs
vendored
93
crates/glue/tests/fixtures/dict/src/lib.rs
vendored
|
@ -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)
|
||||
}
|
11
crates/glue/tests/fixtures/set/app.roc
vendored
11
crates/glue/tests/fixtures/set/app.roc
vendored
|
@ -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"
|
9
crates/glue/tests/fixtures/set/platform.roc
vendored
9
crates/glue/tests/fixtures/set/platform.roc
vendored
|
@ -1,9 +0,0 @@
|
|||
platform "test-platform"
|
||||
requires {} { main : _ }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Set Str
|
||||
mainForHost = main
|
92
crates/glue/tests/fixtures/set/src/lib.rs
vendored
92
crates/glue/tests/fixtures/set/src/lib.rs
vendored
|
@ -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)
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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<_>>(),
|
||||
|
|
|
@ -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>>,
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
4
examples/.gitignore
vendored
|
@ -2,7 +2,7 @@
|
|||
libhost.a
|
||||
libapp.so
|
||||
dynhost
|
||||
*.rm1
|
||||
*.rh1
|
||||
*.rm
|
||||
*.rh
|
||||
|
||||
helloWorld
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
9
examples/glue/glue.roc
Normal 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
|
2
examples/glue/rust-platform/.cargo/config
Normal file
2
examples/glue/rust-platform/.cargo/config
Normal file
|
@ -0,0 +1,2 @@
|
|||
[target.x86_64-pc-windows-gnu]
|
||||
linker = "zig-cc"
|
37
examples/glue/rust-platform/Cargo.lock
generated
Normal file
37
examples/glue/rust-platform/Cargo.lock
generated
Normal 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"
|
22
examples/glue/rust-platform/Cargo.toml
Normal file
22
examples/glue/rust-platform/Cargo.toml
Normal 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]
|
9
examples/glue/rust-platform/build.rs
Normal file
9
examples/glue/rust-platform/build.rs
Normal 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=.");
|
||||
}
|
3
examples/glue/rust-platform/host.c
Normal file
3
examples/glue/rust-platform/host.c
Normal file
|
@ -0,0 +1,3 @@
|
|||
extern int rust_main();
|
||||
|
||||
int main() { return rust_main(); }
|
17
examples/glue/rust-platform/main.roc
Normal file
17
examples/glue/rust-platform/main.roc
Normal 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 }
|
BIN
examples/glue/rust-platform/rocLovesRust
Executable file
BIN
examples/glue/rust-platform/rocLovesRust
Executable file
Binary file not shown.
9
examples/glue/rust-platform/rust-toolchain.toml
Normal file
9
examples/glue/rust-platform/rust-toolchain.toml
Normal 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"
|
||||
]
|
744
examples/glue/rust-platform/src/glue.rs
Normal file
744
examples/glue/rust-platform/src/glue.rs
Normal 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
132
examples/glue/rust-platform/src/lib.rs
Normal file
132
examples/glue/rust-platform/src/lib.rs
Normal 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
|
||||
}
|
3
examples/glue/rust-platform/src/main.rs
Normal file
3
examples/glue/rust-platform/src/main.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
std::process::exit(host::rust_main() as _);
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(unused)]
|
||||
|
||||
mod graphics;
|
||||
mod gui;
|
||||
mod roc;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 [
|
||||
|
|
744
examples/platform-switching/rust-platform/src/glue.rs
Normal file
744
examples/platform-switching/rust-platform/src/glue.rs
Normal 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue