mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-20 07:17:50 +03:00
Merge branch 'trunk' into store-dec-as-str
This commit is contained in:
commit
ffeaa1ac08
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -3918,9 +3918,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.10.0"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d"
|
||||
checksum = "d9bffcddbc2458fa3e6058414599e3c838a022abae82e5c67b4f7f80298d5bff"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
|
@ -67,7 +67,7 @@ libc = "0.2"
|
||||
libloading = "0.6"
|
||||
|
||||
inkwell = { path = "../vendor/inkwell", optional = true }
|
||||
target-lexicon = "0.10"
|
||||
target-lexicon = "0.12.2"
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -222,7 +222,8 @@ fn jit_to_ast_help<'a>(
|
||||
let tags_map: roc_collections::all::MutMap<_, _> =
|
||||
tags_vec.iter().cloned().collect();
|
||||
|
||||
let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs);
|
||||
let union_variant =
|
||||
union_sorted_tags_help(env.arena, tags_vec, None, env.subs, env.ptr_bytes);
|
||||
|
||||
let size = layout.stack_size(env.ptr_bytes);
|
||||
use roc_mono::layout::WrappedVariant::*;
|
||||
@ -886,7 +887,8 @@ fn byte_to_ast<'a>(env: &Env<'a, '_>, value: u8, content: &Content) -> Expr<'a>
|
||||
.map(|(a, b)| (a.clone(), b.to_vec()))
|
||||
.collect();
|
||||
|
||||
let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs);
|
||||
let union_variant =
|
||||
union_sorted_tags_help(env.arena, tags_vec, None, env.subs, env.ptr_bytes);
|
||||
|
||||
match union_variant {
|
||||
UnionVariant::ByteUnion(tagnames) => {
|
||||
|
@ -223,13 +223,13 @@ mod cli_run {
|
||||
// expected_ending: "",
|
||||
// use_valgrind: true,
|
||||
// },
|
||||
// cli:"cli" => Example {
|
||||
// filename: "Echo.roc",
|
||||
// executable_filename: "echo",
|
||||
// stdin: &["Giovanni\n", "Giorgio\n"],
|
||||
// expected_ending: "Giovanni Giorgio!\n",
|
||||
// use_valgrind: true,
|
||||
// },
|
||||
cli:"cli" => Example {
|
||||
filename: "Echo.roc",
|
||||
executable_filename: "echo",
|
||||
stdin: &["Giovanni\n", "Giorgio\n"],
|
||||
expected_ending: "Hi, Giovanni Giorgio!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
// custom_malloc:"custom-malloc" => Example {
|
||||
// filename: "Main.roc",
|
||||
// executable_filename: "custom-malloc-example",
|
||||
|
@ -30,7 +30,7 @@ libloading = "0.6"
|
||||
tempfile = "3.1.0"
|
||||
serde_json = "1.0"
|
||||
inkwell = { path = "../../vendor/inkwell", optional = true }
|
||||
target-lexicon = "0.10"
|
||||
target-lexicon = "0.12.2"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "0.5.1"
|
||||
|
@ -21,7 +21,7 @@ roc_mono = { path = "../mono" }
|
||||
im = "14" # im and im-rc should always have the same version!
|
||||
im-rc = "14" # im and im-rc should always have the same version!
|
||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||
target-lexicon = "0.10"
|
||||
target-lexicon = "0.12.2"
|
||||
libloading = "0.6"
|
||||
object = { version = "0.24", features = ["write"] }
|
||||
|
||||
|
@ -22,7 +22,7 @@ im = "14" # im and im-rc should always have the same version!
|
||||
im-rc = "14" # im and im-rc should always have the same version!
|
||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||
inkwell = { path = "../../vendor/inkwell" }
|
||||
target-lexicon = "0.10"
|
||||
target-lexicon = "0.12.2"
|
||||
|
||||
[dev-dependencies]
|
||||
roc_can = { path = "../can" }
|
||||
|
@ -3935,6 +3935,8 @@ pub fn get_call_conventions(cc: target_lexicon::CallingConvention) -> u32 {
|
||||
SystemV => C_CALL_CONV,
|
||||
WasmBasicCAbi => C_CALL_CONV,
|
||||
WindowsFastcall => C_CALL_CONV,
|
||||
AppleAarch64 => C_CALL_CONV,
|
||||
_ => C_CALL_CONV,
|
||||
}
|
||||
}
|
||||
|
||||
@ -5174,89 +5176,171 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||
}
|
||||
}
|
||||
|
||||
fn build_foreign_symbol_return_result<'a, 'ctx, 'env>(
|
||||
/// A type that is valid according to the C ABI
|
||||
///
|
||||
/// As an example, structs that fit inside an integer type should
|
||||
/// (this does not currently happen here) be coerced to that integer type.
|
||||
fn to_cc_type<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &mut Scope<'a, 'ctx>,
|
||||
foreign: &roc_module::ident::ForeignSymbol,
|
||||
arguments: &[Symbol],
|
||||
return_type: BasicTypeEnum<'ctx>,
|
||||
) -> (FunctionValue<'ctx>, &'a [BasicValueEnum<'ctx>]) {
|
||||
let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||
let mut arg_types = Vec::with_capacity_in(arguments.len() + 1, env.arena);
|
||||
|
||||
for arg in arguments.iter() {
|
||||
let (value, layout) = load_symbol_and_layout(scope, arg);
|
||||
arg_vals.push(value);
|
||||
let arg_type = basic_type_from_layout(env, layout);
|
||||
debug_assert_eq!(arg_type, value.get_type());
|
||||
arg_types.push(arg_type);
|
||||
layout: &Layout<'a>,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
match layout {
|
||||
Layout::Builtin(builtin) => to_cc_type_builtin(env, builtin),
|
||||
_ => {
|
||||
// TODO this is almost certainly incorrect for bigger structs
|
||||
basic_type_from_layout(env, layout)
|
||||
}
|
||||
}
|
||||
|
||||
let function_type = return_type.fn_type(&arg_types, false);
|
||||
let function = get_foreign_symbol(env, foreign.clone(), function_type);
|
||||
|
||||
(function, arg_vals.into_bump_slice())
|
||||
}
|
||||
|
||||
fn build_foreign_symbol_write_result_into_ptr<'a, 'ctx, 'env>(
|
||||
fn to_cc_type_builtin<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &mut Scope<'a, 'ctx>,
|
||||
foreign: &roc_module::ident::ForeignSymbol,
|
||||
arguments: &[Symbol],
|
||||
return_pointer: PointerValue<'ctx>,
|
||||
) -> (FunctionValue<'ctx>, &'a [BasicValueEnum<'ctx>]) {
|
||||
let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||
let mut arg_types = Vec::with_capacity_in(arguments.len() + 1, env.arena);
|
||||
|
||||
arg_vals.push(return_pointer.into());
|
||||
arg_types.push(return_pointer.get_type().into());
|
||||
|
||||
for arg in arguments.iter() {
|
||||
let (value, layout) = load_symbol_and_layout(scope, arg);
|
||||
arg_vals.push(value);
|
||||
let arg_type = basic_type_from_layout(env, layout);
|
||||
debug_assert_eq!(arg_type, value.get_type());
|
||||
arg_types.push(arg_type);
|
||||
builtin: &Builtin<'a>,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
match builtin {
|
||||
Builtin::Int128
|
||||
| Builtin::Int64
|
||||
| Builtin::Int32
|
||||
| Builtin::Int16
|
||||
| Builtin::Int8
|
||||
| Builtin::Int1
|
||||
| Builtin::Usize
|
||||
| Builtin::Decimal
|
||||
| Builtin::Float128
|
||||
| Builtin::Float64
|
||||
| Builtin::Float32
|
||||
| Builtin::Float16 => basic_type_from_builtin(env, builtin),
|
||||
Builtin::Str | Builtin::EmptyStr | Builtin::List(_) | Builtin::EmptyList => {
|
||||
env.str_list_c_abi().into()
|
||||
}
|
||||
Builtin::Dict(_, _) | Builtin::Set(_) | Builtin::EmptyDict | Builtin::EmptySet => {
|
||||
// TODO verify this is what actually happens
|
||||
basic_type_from_builtin(env, builtin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum CCReturn {
|
||||
/// Return as normal
|
||||
Return,
|
||||
/// require an extra argument, a pointer
|
||||
/// where the result is written into
|
||||
/// returns void
|
||||
ByPointer,
|
||||
/// The return type is zero-sized
|
||||
Void,
|
||||
}
|
||||
|
||||
/// According to the C ABI, how should we return a value with the given layout?
|
||||
fn to_cc_return<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, layout: &Layout<'a>) -> CCReturn {
|
||||
let return_size = layout.stack_size(env.ptr_bytes);
|
||||
let pass_result_by_pointer = return_size > 2 * env.ptr_bytes;
|
||||
|
||||
if return_size == 0 {
|
||||
CCReturn::Void
|
||||
} else if pass_result_by_pointer {
|
||||
CCReturn::ByPointer
|
||||
} else {
|
||||
CCReturn::Return
|
||||
}
|
||||
|
||||
let function_type = env.context.void_type().fn_type(&arg_types, false);
|
||||
let function = get_foreign_symbol(env, foreign.clone(), function_type);
|
||||
|
||||
(function, arg_vals.into_bump_slice())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn build_foreign_symbol<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &mut Scope<'a, 'ctx>,
|
||||
foreign: &roc_module::ident::ForeignSymbol,
|
||||
arguments: &[Symbol],
|
||||
argument_symbols: &[Symbol],
|
||||
ret_layout: &Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let ret_type = basic_type_from_layout(env, ret_layout);
|
||||
let return_pointer = env.builder.build_alloca(ret_type, "return_value");
|
||||
let builder = env.builder;
|
||||
let context = env.context;
|
||||
|
||||
// crude approximation of the C calling convention
|
||||
let pass_result_by_pointer = ret_layout.stack_size(env.ptr_bytes) > 2 * env.ptr_bytes;
|
||||
// Here we build two functions:
|
||||
//
|
||||
// - an C_CALL_CONV extern that will be provided by the host, e.g. `roc_fx_putLine`
|
||||
// This is just a type signature that we make available to the linker,
|
||||
// and can use in the wrapper
|
||||
// - a FAST_CALL_CONV wrapper that we make here, e.g. `roc_fx_putLine_fastcc_wrapper`
|
||||
|
||||
let (function, arguments) = if pass_result_by_pointer {
|
||||
build_foreign_symbol_write_result_into_ptr(env, scope, foreign, arguments, return_pointer)
|
||||
} else {
|
||||
build_foreign_symbol_return_result(env, scope, foreign, arguments, ret_type)
|
||||
let return_type = basic_type_from_layout(env, ret_layout);
|
||||
let cc_return = to_cc_return(env, ret_layout);
|
||||
|
||||
let mut cc_argument_types = Vec::with_capacity_in(argument_symbols.len() + 1, env.arena);
|
||||
let mut fastcc_argument_types = Vec::with_capacity_in(argument_symbols.len(), env.arena);
|
||||
let mut arguments = Vec::with_capacity_in(argument_symbols.len(), env.arena);
|
||||
|
||||
for symbol in argument_symbols {
|
||||
let (value, layout) = load_symbol_and_layout(scope, symbol);
|
||||
|
||||
cc_argument_types.push(to_cc_type(env, layout));
|
||||
|
||||
let basic_type = basic_type_from_layout(env, layout);
|
||||
fastcc_argument_types.push(basic_type);
|
||||
|
||||
arguments.push(value);
|
||||
}
|
||||
|
||||
let cc_type = match cc_return {
|
||||
CCReturn::Void => env.context.void_type().fn_type(&cc_argument_types, false),
|
||||
CCReturn::ByPointer => {
|
||||
cc_argument_types.push(return_type.ptr_type(AddressSpace::Generic).into());
|
||||
env.context.void_type().fn_type(&cc_argument_types, false)
|
||||
}
|
||||
CCReturn::Return => return_type.fn_type(&cc_argument_types, false),
|
||||
};
|
||||
|
||||
let call = env.builder.build_call(function, arguments, "tmp");
|
||||
let cc_function = get_foreign_symbol(env, foreign.clone(), cc_type);
|
||||
|
||||
// this is a foreign function, use c calling convention
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
let fastcc_type = return_type.fn_type(&fastcc_argument_types, false);
|
||||
|
||||
call.try_as_basic_value();
|
||||
let fastcc_function = add_func(
|
||||
env.module,
|
||||
&format!("{}_fastcc_wrapper", foreign.as_str()),
|
||||
fastcc_type,
|
||||
Linkage::Private,
|
||||
FAST_CALL_CONV,
|
||||
);
|
||||
|
||||
if pass_result_by_pointer {
|
||||
env.builder.build_load(return_pointer, "read_result")
|
||||
} else {
|
||||
call.try_as_basic_value().left().unwrap()
|
||||
let old = builder.get_insert_block().unwrap();
|
||||
|
||||
let entry = context.append_basic_block(fastcc_function, "entry");
|
||||
{
|
||||
builder.position_at_end(entry);
|
||||
let return_pointer = env.builder.build_alloca(return_type, "return_value");
|
||||
|
||||
let fastcc_parameters = fastcc_function.get_params();
|
||||
let mut cc_arguments = Vec::with_capacity_in(fastcc_parameters.len() + 1, env.arena);
|
||||
|
||||
for (param, cc_type) in fastcc_parameters.into_iter().zip(cc_argument_types.iter()) {
|
||||
if param.get_type() == *cc_type {
|
||||
cc_arguments.push(param);
|
||||
} else {
|
||||
let as_cc_type = complex_bitcast(env.builder, param, *cc_type, "to_cc_type");
|
||||
cc_arguments.push(as_cc_type);
|
||||
}
|
||||
}
|
||||
|
||||
if let CCReturn::ByPointer = cc_return {
|
||||
cc_arguments.push(return_pointer.into());
|
||||
}
|
||||
|
||||
let call = env.builder.build_call(cc_function, &cc_arguments, "tmp");
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
|
||||
let return_value = match cc_return {
|
||||
CCReturn::Return => call.try_as_basic_value().left().unwrap(),
|
||||
|
||||
CCReturn::ByPointer => env.builder.build_load(return_pointer, "read_result"),
|
||||
CCReturn::Void => return_type.const_zero(),
|
||||
};
|
||||
|
||||
builder.build_return(Some(&return_value));
|
||||
}
|
||||
|
||||
builder.position_at_end(old);
|
||||
let call = env.builder.build_call(fastcc_function, &arguments, "tmp");
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
return call.try_as_basic_value().left().unwrap();
|
||||
}
|
||||
|
||||
fn throw_on_overflow<'a, 'ctx, 'env>(
|
||||
|
@ -832,6 +832,7 @@ struct State<'a> {
|
||||
pub exposed_types: SubsByModule,
|
||||
pub output_path: Option<&'a str>,
|
||||
pub platform_path: PlatformPath<'a>,
|
||||
pub ptr_bytes: u32,
|
||||
|
||||
pub headers_parsed: MutSet<ModuleId>,
|
||||
|
||||
@ -1467,6 +1468,7 @@ where
|
||||
|
||||
let mut state = State {
|
||||
root_id,
|
||||
ptr_bytes,
|
||||
platform_data: None,
|
||||
goal_phase,
|
||||
stdlib,
|
||||
@ -1978,7 +1980,10 @@ fn update<'a>(
|
||||
);
|
||||
|
||||
if state.goal_phase > Phase::SolveTypes {
|
||||
let layout_cache = state.layout_caches.pop().unwrap_or_default();
|
||||
let layout_cache = state
|
||||
.layout_caches
|
||||
.pop()
|
||||
.unwrap_or_else(|| LayoutCache::new(state.ptr_bytes));
|
||||
|
||||
let typechecked = TypeCheckedModule {
|
||||
module_id,
|
||||
|
@ -3124,7 +3124,8 @@ pub fn with_hole<'a>(
|
||||
mut fields,
|
||||
..
|
||||
} => {
|
||||
let sorted_fields = crate::layout::sort_record_fields(env.arena, record_var, env.subs);
|
||||
let sorted_fields =
|
||||
crate::layout::sort_record_fields(env.arena, record_var, env.subs, env.ptr_bytes);
|
||||
|
||||
let mut field_symbols = Vec::with_capacity_in(fields.len(), env.arena);
|
||||
let mut can_fields = Vec::with_capacity_in(fields.len(), env.arena);
|
||||
@ -3459,7 +3460,8 @@ pub fn with_hole<'a>(
|
||||
loc_expr,
|
||||
..
|
||||
} => {
|
||||
let sorted_fields = crate::layout::sort_record_fields(env.arena, record_var, env.subs);
|
||||
let sorted_fields =
|
||||
crate::layout::sort_record_fields(env.arena, record_var, env.subs, env.ptr_bytes);
|
||||
|
||||
let mut index = None;
|
||||
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
||||
@ -3601,7 +3603,8 @@ pub fn with_hole<'a>(
|
||||
// This has the benefit that we don't need to do anything special for reference
|
||||
// counting
|
||||
|
||||
let sorted_fields = crate::layout::sort_record_fields(env.arena, record_var, env.subs);
|
||||
let sorted_fields =
|
||||
crate::layout::sort_record_fields(env.arena, record_var, env.subs, env.ptr_bytes);
|
||||
|
||||
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
||||
|
||||
@ -4205,7 +4208,8 @@ fn convert_tag_union<'a>(
|
||||
arena: &'a Bump,
|
||||
) -> Stmt<'a> {
|
||||
use crate::layout::UnionVariant::*;
|
||||
let res_variant = crate::layout::union_sorted_tags(env.arena, variant_var, env.subs);
|
||||
let res_variant =
|
||||
crate::layout::union_sorted_tags(env.arena, variant_var, env.subs, env.ptr_bytes);
|
||||
let variant = match res_variant {
|
||||
Ok(cached) => cached,
|
||||
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
||||
@ -4541,7 +4545,7 @@ fn sorted_field_symbols<'a>(
|
||||
}
|
||||
};
|
||||
|
||||
let alignment = layout.alignment_bytes(8);
|
||||
let alignment = layout.alignment_bytes(env.ptr_bytes);
|
||||
|
||||
let symbol = possible_reuse_symbol(env, procs, &arg.value);
|
||||
field_symbols_temp.push((alignment, symbol, ((var, arg), &*env.arena.alloc(symbol))));
|
||||
@ -6978,7 +6982,8 @@ fn from_can_pattern_help<'a>(
|
||||
use crate::exhaustive::Union;
|
||||
use crate::layout::UnionVariant::*;
|
||||
|
||||
let res_variant = crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs);
|
||||
let res_variant =
|
||||
crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs, env.ptr_bytes);
|
||||
|
||||
let variant = match res_variant {
|
||||
Ok(cached) => cached,
|
||||
@ -7397,7 +7402,8 @@ fn from_can_pattern_help<'a>(
|
||||
..
|
||||
} => {
|
||||
// sorted fields based on the type
|
||||
let sorted_fields = crate::layout::sort_record_fields(env.arena, *whole_var, env.subs);
|
||||
let sorted_fields =
|
||||
crate::layout::sort_record_fields(env.arena, *whole_var, env.subs, env.ptr_bytes);
|
||||
|
||||
// sorted fields based on the destruct
|
||||
let mut mono_destructs = Vec::with_capacity_in(destructs.len(), env.arena);
|
||||
|
@ -138,7 +138,8 @@ impl<'a> RawFunctionLayout<'a> {
|
||||
let fn_args = fn_args.into_bump_slice();
|
||||
let ret = arena.alloc(ret);
|
||||
|
||||
let lambda_set = LambdaSet::from_var(env.arena, env.subs, closure_var)?;
|
||||
let lambda_set =
|
||||
LambdaSet::from_var(env.arena, env.subs, closure_var, env.ptr_bytes)?;
|
||||
|
||||
Ok(Self::Function(fn_args, lambda_set, ret))
|
||||
}
|
||||
@ -516,6 +517,7 @@ impl<'a> LambdaSet<'a> {
|
||||
arena: &'a Bump,
|
||||
subs: &Subs,
|
||||
closure_var: Variable,
|
||||
ptr_bytes: u32,
|
||||
) -> Result<Self, LayoutProblem> {
|
||||
let mut tags = std::vec::Vec::new();
|
||||
match roc_types::pretty_print::chase_ext_tag_union(subs, closure_var, &mut tags) {
|
||||
@ -529,6 +531,7 @@ impl<'a> LambdaSet<'a> {
|
||||
arena,
|
||||
subs,
|
||||
seen: Vec::new_in(arena),
|
||||
ptr_bytes,
|
||||
};
|
||||
|
||||
for (tag_name, variables) in tags.iter() {
|
||||
@ -545,7 +548,8 @@ impl<'a> LambdaSet<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let representation = arena.alloc(Self::make_representation(arena, subs, tags));
|
||||
let representation =
|
||||
arena.alloc(Self::make_representation(arena, subs, tags, ptr_bytes));
|
||||
|
||||
Ok(LambdaSet {
|
||||
set: set.into_bump_slice(),
|
||||
@ -568,9 +572,10 @@ impl<'a> LambdaSet<'a> {
|
||||
arena: &'a Bump,
|
||||
subs: &Subs,
|
||||
tags: std::vec::Vec<(TagName, std::vec::Vec<Variable>)>,
|
||||
ptr_bytes: u32,
|
||||
) -> Layout<'a> {
|
||||
// otherwise, this is a closure with a payload
|
||||
let variant = union_sorted_tags_help(arena, tags, None, subs);
|
||||
let variant = union_sorted_tags_help(arena, tags, None, subs, ptr_bytes);
|
||||
|
||||
use UnionVariant::*;
|
||||
match variant {
|
||||
@ -648,6 +653,7 @@ pub enum Builtin<'a> {
|
||||
}
|
||||
|
||||
pub struct Env<'a, 'b> {
|
||||
ptr_bytes: u32,
|
||||
arena: &'a Bump,
|
||||
seen: Vec<'a, Variable>,
|
||||
subs: &'b Subs,
|
||||
@ -972,8 +978,9 @@ impl<'a> Layout<'a> {
|
||||
/// e.g. `identity : a -> a` could be specialized to `Bool -> Bool` or `Str -> Str`.
|
||||
/// Therefore in general it's invalid to store a map from variables to layouts
|
||||
/// But if we're careful when to invalidate certain keys, we still get some benefit
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct LayoutCache<'a> {
|
||||
ptr_bytes: u32,
|
||||
_marker: std::marker::PhantomData<&'a u8>,
|
||||
}
|
||||
|
||||
@ -985,6 +992,13 @@ pub enum CachedLayout<'a> {
|
||||
}
|
||||
|
||||
impl<'a> LayoutCache<'a> {
|
||||
pub fn new(ptr_bytes: u32) -> Self {
|
||||
Self {
|
||||
ptr_bytes,
|
||||
_marker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_var(
|
||||
&mut self,
|
||||
arena: &'a Bump,
|
||||
@ -998,6 +1012,7 @@ impl<'a> LayoutCache<'a> {
|
||||
arena,
|
||||
subs,
|
||||
seen: Vec::new_in(arena),
|
||||
ptr_bytes: self.ptr_bytes,
|
||||
};
|
||||
|
||||
Layout::from_var(&mut env, var)
|
||||
@ -1016,6 +1031,7 @@ impl<'a> LayoutCache<'a> {
|
||||
arena,
|
||||
subs,
|
||||
seen: Vec::new_in(arena),
|
||||
ptr_bytes: self.ptr_bytes,
|
||||
};
|
||||
|
||||
RawFunctionLayout::from_var(&mut env, var)
|
||||
@ -1182,6 +1198,7 @@ fn layout_from_flat_type<'a>(
|
||||
|
||||
let arena = env.arena;
|
||||
let subs = env.subs;
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
|
||||
match flat_type {
|
||||
Apply(symbol, args) => {
|
||||
@ -1273,7 +1290,7 @@ fn layout_from_flat_type<'a>(
|
||||
}
|
||||
}
|
||||
Func(_, closure_var, _) => {
|
||||
let lambda_set = LambdaSet::from_var(env.arena, env.subs, closure_var)?;
|
||||
let lambda_set = LambdaSet::from_var(env.arena, env.subs, closure_var, env.ptr_bytes)?;
|
||||
|
||||
Ok(lambda_set.runtime_representation())
|
||||
}
|
||||
@ -1299,8 +1316,6 @@ fn layout_from_flat_type<'a>(
|
||||
let mut pairs = Vec::from_iter_in(pairs_it, arena);
|
||||
|
||||
pairs.sort_by(|(label1, layout1), (label2, layout2)| {
|
||||
let ptr_bytes = 8;
|
||||
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
@ -1320,14 +1335,14 @@ fn layout_from_flat_type<'a>(
|
||||
TagUnion(tags, ext_var) => {
|
||||
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
|
||||
|
||||
Ok(layout_from_tag_union(arena, tags, subs))
|
||||
Ok(layout_from_tag_union(arena, tags, subs, env.ptr_bytes))
|
||||
}
|
||||
FunctionOrTagUnion(tag_name, _, ext_var) => {
|
||||
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
|
||||
|
||||
let tags = UnionTags::from_tag_name_index(tag_name);
|
||||
|
||||
Ok(layout_from_tag_union(arena, tags, subs))
|
||||
Ok(layout_from_tag_union(arena, tags, subs, env.ptr_bytes))
|
||||
}
|
||||
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
|
||||
@ -1377,8 +1392,6 @@ fn layout_from_flat_type<'a>(
|
||||
}
|
||||
|
||||
tag_layout.sort_by(|layout1, layout2| {
|
||||
let ptr_bytes = 8;
|
||||
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
@ -1425,11 +1438,13 @@ pub fn sort_record_fields<'a>(
|
||||
arena: &'a Bump,
|
||||
var: Variable,
|
||||
subs: &Subs,
|
||||
ptr_bytes: u32,
|
||||
) -> Vec<'a, (Lowercase, Variable, Result<Layout<'a>, Layout<'a>>)> {
|
||||
let mut env = Env {
|
||||
arena,
|
||||
subs,
|
||||
seen: Vec::new_in(arena),
|
||||
ptr_bytes,
|
||||
};
|
||||
|
||||
let (it, _) = gather_fields_unsorted_iter(subs, RecordFields::empty(), var);
|
||||
@ -1445,6 +1460,8 @@ fn sort_record_fields_help<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
fields_map: impl Iterator<Item = (Lowercase, RecordField<Variable>)>,
|
||||
) -> Vec<'a, (Lowercase, Variable, Result<Layout<'a>, Layout<'a>>)> {
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
|
||||
// Sort the fields by label
|
||||
let mut sorted_fields = Vec::with_capacity_in(fields_map.size_hint().0, env.arena);
|
||||
|
||||
@ -1468,8 +1485,6 @@ fn sort_record_fields_help<'a>(
|
||||
|(label1, _, res_layout1), (label2, _, res_layout2)| match res_layout1 {
|
||||
Ok(layout1) | Err(layout1) => match res_layout2 {
|
||||
Ok(layout2) | Err(layout2) => {
|
||||
let ptr_bytes = 8;
|
||||
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
@ -1605,6 +1620,7 @@ pub fn union_sorted_tags<'a>(
|
||||
arena: &'a Bump,
|
||||
var: Variable,
|
||||
subs: &Subs,
|
||||
ptr_bytes: u32,
|
||||
) -> Result<UnionVariant<'a>, LayoutProblem> {
|
||||
let var =
|
||||
if let Content::RecursionVar { structure, .. } = subs.get_content_without_compacting(var) {
|
||||
@ -1617,7 +1633,7 @@ pub fn union_sorted_tags<'a>(
|
||||
let result = match roc_types::pretty_print::chase_ext_tag_union(subs, var, &mut tags_vec) {
|
||||
Ok(()) | Err((_, Content::FlexVar(_))) | Err((_, Content::RecursionVar { .. })) => {
|
||||
let opt_rec_var = get_recursion_var(subs, var);
|
||||
union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs)
|
||||
union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs, ptr_bytes)
|
||||
}
|
||||
Err((_, Content::Error)) => return Err(LayoutProblem::Erroneous),
|
||||
Err(other) => panic!("invalid content in tag union variable: {:?}", other),
|
||||
@ -1651,6 +1667,7 @@ fn union_sorted_tags_help_new<'a>(
|
||||
mut tags_vec: Vec<(&'_ TagName, VariableSubsSlice)>,
|
||||
opt_rec_var: Option<Variable>,
|
||||
subs: &Subs,
|
||||
ptr_bytes: u32,
|
||||
) -> UnionVariant<'a> {
|
||||
// sort up front; make sure the ordering stays intact!
|
||||
tags_vec.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
|
||||
@ -1659,6 +1676,7 @@ fn union_sorted_tags_help_new<'a>(
|
||||
arena,
|
||||
subs,
|
||||
seen: Vec::new_in(arena),
|
||||
ptr_bytes,
|
||||
};
|
||||
|
||||
match tags_vec.len() {
|
||||
@ -1708,8 +1726,6 @@ fn union_sorted_tags_help_new<'a>(
|
||||
}
|
||||
|
||||
layouts.sort_by(|layout1, layout2| {
|
||||
let ptr_bytes = 8;
|
||||
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
@ -1793,8 +1809,6 @@ fn union_sorted_tags_help_new<'a>(
|
||||
}
|
||||
|
||||
arg_layouts.sort_by(|layout1, layout2| {
|
||||
let ptr_bytes = 8;
|
||||
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
@ -1867,6 +1881,7 @@ pub fn union_sorted_tags_help<'a>(
|
||||
mut tags_vec: std::vec::Vec<(TagName, std::vec::Vec<Variable>)>,
|
||||
opt_rec_var: Option<Variable>,
|
||||
subs: &Subs,
|
||||
ptr_bytes: u32,
|
||||
) -> UnionVariant<'a> {
|
||||
// sort up front; make sure the ordering stays intact!
|
||||
tags_vec.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
|
||||
@ -1875,6 +1890,7 @@ pub fn union_sorted_tags_help<'a>(
|
||||
arena,
|
||||
subs,
|
||||
seen: Vec::new_in(arena),
|
||||
ptr_bytes,
|
||||
};
|
||||
|
||||
match tags_vec.len() {
|
||||
@ -1921,8 +1937,6 @@ pub fn union_sorted_tags_help<'a>(
|
||||
}
|
||||
|
||||
layouts.sort_by(|layout1, layout2| {
|
||||
let ptr_bytes = 8;
|
||||
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
@ -2005,8 +2019,6 @@ pub fn union_sorted_tags_help<'a>(
|
||||
}
|
||||
|
||||
arg_layouts.sort_by(|layout1, layout2| {
|
||||
let ptr_bytes = 8;
|
||||
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
@ -2091,7 +2103,12 @@ fn cheap_sort_tags<'a, 'b>(
|
||||
tags_vec
|
||||
}
|
||||
|
||||
fn layout_from_newtype<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> Layout<'a> {
|
||||
fn layout_from_newtype<'a>(
|
||||
arena: &'a Bump,
|
||||
tags: UnionTags,
|
||||
subs: &Subs,
|
||||
ptr_bytes: u32,
|
||||
) -> Layout<'a> {
|
||||
debug_assert!(tags.is_newtype_wrapper(subs));
|
||||
|
||||
let slice_index = tags.variables().into_iter().next().unwrap();
|
||||
@ -2109,6 +2126,7 @@ fn layout_from_newtype<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> Lay
|
||||
arena,
|
||||
subs,
|
||||
seen: Vec::new_in(arena),
|
||||
ptr_bytes,
|
||||
};
|
||||
|
||||
match Layout::from_var(&mut env, var) {
|
||||
@ -2128,11 +2146,16 @@ fn layout_from_newtype<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> Lay
|
||||
}
|
||||
}
|
||||
|
||||
fn layout_from_tag_union<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> Layout<'a> {
|
||||
fn layout_from_tag_union<'a>(
|
||||
arena: &'a Bump,
|
||||
tags: UnionTags,
|
||||
subs: &Subs,
|
||||
ptr_bytes: u32,
|
||||
) -> Layout<'a> {
|
||||
use UnionVariant::*;
|
||||
|
||||
if tags.is_newtype_wrapper(subs) {
|
||||
return layout_from_newtype(arena, tags, subs);
|
||||
return layout_from_newtype(arena, tags, subs, ptr_bytes);
|
||||
}
|
||||
|
||||
let tags_vec = cheap_sort_tags(arena, tags, subs);
|
||||
@ -2148,7 +2171,7 @@ fn layout_from_tag_union<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> L
|
||||
}
|
||||
_ => {
|
||||
let opt_rec_var = None;
|
||||
let variant = union_sorted_tags_help_new(arena, tags_vec, opt_rec_var, subs);
|
||||
let variant = union_sorted_tags_help_new(arena, tags_vec, opt_rec_var, subs, ptr_bytes);
|
||||
|
||||
match variant {
|
||||
Never => Layout::Union(UnionLayout::NonRecursive(&[])),
|
||||
|
@ -92,14 +92,15 @@ mod test_reporting {
|
||||
let mut ident_ids = interns.all_ident_ids.remove(&home).unwrap();
|
||||
|
||||
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
||||
let mut layout_cache = LayoutCache::default();
|
||||
let ptr_bytes = 8;
|
||||
let mut layout_cache = LayoutCache::new(ptr_bytes);
|
||||
let mut mono_env = roc_mono::ir::Env {
|
||||
arena: &arena,
|
||||
subs: &mut subs,
|
||||
problems: &mut mono_problems,
|
||||
home,
|
||||
ident_ids: &mut ident_ids,
|
||||
ptr_bytes: 8,
|
||||
ptr_bytes,
|
||||
update_mode_counter: 0,
|
||||
// call_specialization_counter=0 is reserved
|
||||
call_specialization_counter: 1,
|
||||
|
@ -30,7 +30,7 @@ either = "1.6.1"
|
||||
indoc = "0.3.3"
|
||||
libc = "0.2"
|
||||
inkwell = { path = "../../vendor/inkwell" }
|
||||
target-lexicon = "0.10"
|
||||
target-lexicon = "0.12.2"
|
||||
libloading = "0.6"
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -28,7 +28,7 @@ bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||
either = "1.6.1"
|
||||
indoc = "0.3.3"
|
||||
libc = "0.2"
|
||||
target-lexicon = "0.10"
|
||||
target-lexicon = "0.12.2"
|
||||
libloading = "0.6"
|
||||
|
||||
[dev-dependencies]
|
||||
|
1
examples/.gitignore
vendored
1
examples/.gitignore
vendored
@ -4,4 +4,3 @@ app
|
||||
libhost.a
|
||||
roc_app.ll
|
||||
roc_app.bc
|
||||
effect-example
|
||||
|
@ -29,23 +29,33 @@ 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 malloc(size: usize) callconv(.C) ?*c_void;
|
||||
extern fn realloc(c_ptr: [*]align(@alignOf(u128)) u8, size: usize) callconv(.C) ?*c_void;
|
||||
extern fn free(c_ptr: [*]align(@alignOf(u128)) u8) callconv(.C) void;
|
||||
const Align = extern struct { a: usize, b: usize };
|
||||
extern fn malloc(size: usize) callconv(.C) ?*align(@alignOf(Align)) c_void;
|
||||
extern fn realloc(c_ptr: [*]align(@alignOf(Align)) u8, size: usize) callconv(.C) ?*c_void;
|
||||
extern fn free(c_ptr: [*]align(@alignOf(Align)) u8) callconv(.C) void;
|
||||
|
||||
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void {
|
||||
_ = alignment;
|
||||
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
export fn roc_realloc(c_ptr: *c_void, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*c_void {
|
||||
return realloc(@alignCast(16, @ptrCast([*]u8, c_ptr)), new_size);
|
||||
_ = old_size;
|
||||
_ = alignment;
|
||||
|
||||
return realloc(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)), new_size);
|
||||
}
|
||||
|
||||
export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
||||
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||
_ = alignment;
|
||||
|
||||
free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)));
|
||||
}
|
||||
|
||||
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||
_ = tag_id;
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||
@ -54,12 +64,9 @@ export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||
|
||||
const Unit = extern struct {};
|
||||
|
||||
pub export fn main() u8 {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
|
||||
pub fn main() u8 {
|
||||
const size = @intCast(usize, roc__mainForHost_size());
|
||||
const raw_output = std.heap.c_allocator.alloc(u8, size) catch unreachable;
|
||||
const raw_output = std.heap.c_allocator.allocAdvanced(u8, @alignOf(u64), @intCast(usize, size), .at_least) catch unreachable;
|
||||
var output = @ptrCast([*]u8, raw_output);
|
||||
|
||||
defer {
|
||||
@ -71,17 +78,17 @@ pub export fn main() u8 {
|
||||
|
||||
roc__mainForHost_1_exposed(output);
|
||||
|
||||
const elements = @ptrCast([*]u64, @alignCast(8, output));
|
||||
|
||||
var flag = elements[0];
|
||||
const flag = @ptrCast(*u64, @alignCast(@alignOf(u64), output)).*;
|
||||
|
||||
if (flag == 0) {
|
||||
// all is well
|
||||
const closure_data_pointer = @ptrCast([*]u8, output[8..size]);
|
||||
const closure_data_pointer = @ptrCast([*]u8, output[@sizeOf(u64)..size]);
|
||||
|
||||
call_the_closure(closure_data_pointer);
|
||||
} else {
|
||||
const msg = @intToPtr([*:0]const u8, elements[1]);
|
||||
const ptr = @ptrCast(*u32, output + @sizeOf(u64));
|
||||
const msg = @intToPtr([*:0]const u8, ptr.*);
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||
|
||||
return 0;
|
||||
@ -92,6 +99,7 @@ pub export fn main() u8 {
|
||||
|
||||
const delta = to_seconds(ts2) - to_seconds(ts1);
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
|
||||
|
||||
return 0;
|
||||
@ -103,7 +111,7 @@ fn to_seconds(tms: std.os.timespec) f64 {
|
||||
|
||||
fn call_the_closure(closure_data_pointer: [*]u8) void {
|
||||
const size = roc__mainForHost_1_Fx_result_size();
|
||||
const raw_output = std.heap.c_allocator.alloc(u8, @intCast(usize, size)) catch unreachable;
|
||||
const raw_output = std.heap.c_allocator.allocAdvanced(u8, @alignOf(u64), @intCast(usize, size), .at_least) catch unreachable;
|
||||
var output = @ptrCast([*]u8, raw_output);
|
||||
|
||||
defer {
|
||||
@ -135,7 +143,7 @@ pub export fn roc_fx_putInt(int: i64) i64 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub export fn roc_fx_putLine(rocPath: str.RocStr) i64 {
|
||||
export fn roc_fx_putLine(rocPath: str.RocStr) callconv(.C) void {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
|
||||
for (rocPath.asSlice()) |char| {
|
||||
@ -143,8 +151,6 @@ pub export fn roc_fx_putLine(rocPath: str.RocStr) i64 {
|
||||
}
|
||||
|
||||
stdout.print("\n", .{}) catch unreachable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const GetInt = extern struct {
|
||||
|
1
examples/cli/.gitignore
vendored
Normal file
1
examples/cli/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
echo
|
18
examples/cli/Echo.roc
Normal file
18
examples/cli/Echo.roc
Normal file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env roc
|
||||
|
||||
app "echo"
|
||||
packages { base: "platform" }
|
||||
imports [ base.Task.{ Task, await }, base.Stdout, base.Stdin ]
|
||||
provides [ main ] to base
|
||||
|
||||
main : Task {} *
|
||||
main =
|
||||
{} <- await (Stdout.line "What's your first name?")
|
||||
|
||||
firstName <- await Stdin.line
|
||||
|
||||
{} <- await (Stdout.line "What's your last name?")
|
||||
|
||||
lastName <- await Stdin.line
|
||||
|
||||
Stdout.line "Hi, \(firstName) \(lastName)!"
|
BIN
examples/cli/cli-example
Executable file
BIN
examples/cli/cli-example
Executable file
Binary file not shown.
BIN
examples/cli/hello-world
Executable file
BIN
examples/cli/hello-world
Executable file
Binary file not shown.
21
examples/cli/platform/Cargo.lock
generated
Normal file
21
examples/cli/platform/Cargo.lock
generated
Normal file
@ -0,0 +1,21 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "host"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"roc_std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
|
||||
|
||||
[[package]]
|
||||
name = "roc_std"
|
||||
version = "0.1.0"
|
15
examples/cli/platform/Cargo.toml
Normal file
15
examples/cli/platform/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "host"
|
||||
version = "0.1.0"
|
||||
authors = ["The Roc Contributors"]
|
||||
license = "UPL-1.0"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
roc_std = { path = "../../../roc_std" }
|
||||
libc = "0.2"
|
||||
|
||||
[workspace]
|
14
examples/cli/platform/Package-Config.roc
Normal file
14
examples/cli/platform/Package-Config.roc
Normal file
@ -0,0 +1,14 @@
|
||||
platform examples/cli
|
||||
requires {}{ main : Task {} [] } # TODO FIXME
|
||||
exposes []
|
||||
packages {}
|
||||
imports [ Task.{ Task } ]
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect
|
||||
{
|
||||
putLine : Str -> Effect {},
|
||||
getLine : Effect Str
|
||||
}
|
||||
|
||||
mainForHost : Task {} [] as Fx
|
||||
mainForHost = main
|
6
examples/cli/platform/Stdin.roc
Normal file
6
examples/cli/platform/Stdin.roc
Normal file
@ -0,0 +1,6 @@
|
||||
interface Stdin
|
||||
exposes [ line ]
|
||||
imports [ fx.Effect, Task ]
|
||||
|
||||
line : Task.Task Str *
|
||||
line = Effect.after Effect.getLine Task.succeed # TODO FIXME Effect.getLine should suffice
|
9
examples/cli/platform/Stdout.roc
Normal file
9
examples/cli/platform/Stdout.roc
Normal file
@ -0,0 +1,9 @@
|
||||
interface Stdout
|
||||
exposes [ line ]
|
||||
imports [ fx.Effect, Task.{ Task } ]
|
||||
|
||||
# line : Str -> Task.Task {} *
|
||||
# line = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {})
|
||||
|
||||
line : Str -> Task {} *
|
||||
line = \str -> Effect.map (Effect.putLine str) (\_ -> Ok {})
|
44
examples/cli/platform/Task.roc
Normal file
44
examples/cli/platform/Task.roc
Normal file
@ -0,0 +1,44 @@
|
||||
interface Task
|
||||
exposes [ Task, succeed, fail, await, map, onFail, attempt ]
|
||||
imports [ fx.Effect ]
|
||||
|
||||
|
||||
Task ok err : Effect.Effect (Result ok err)
|
||||
|
||||
|
||||
succeed : val -> Task val *
|
||||
succeed = \val ->
|
||||
Effect.always (Ok val)
|
||||
|
||||
|
||||
fail : err -> Task * err
|
||||
fail = \val ->
|
||||
Effect.always (Err val)
|
||||
|
||||
attempt : Task a b, (Result a b -> Task c d) -> Task c d
|
||||
attempt = \effect, transform ->
|
||||
Effect.after effect \result ->
|
||||
when result is
|
||||
Ok ok -> transform (Ok ok)
|
||||
Err err -> transform (Err err)
|
||||
|
||||
await : Task a err, (a -> Task b err) -> Task b err
|
||||
await = \effect, transform ->
|
||||
Effect.after effect \result ->
|
||||
when result is
|
||||
Ok a -> transform a
|
||||
Err err -> Task.fail err
|
||||
|
||||
onFail : Task ok a, (a -> Task ok b) -> Task ok b
|
||||
onFail = \effect, transform ->
|
||||
Effect.after effect \result ->
|
||||
when result is
|
||||
Ok a -> Task.succeed a
|
||||
Err err -> transform err
|
||||
|
||||
map : Task a err, (a -> b) -> Task b err
|
||||
map = \effect, transform ->
|
||||
Effect.after effect \result ->
|
||||
when result is
|
||||
Ok a -> Task.succeed (transform a)
|
||||
Err err -> Task.fail err
|
7
examples/cli/platform/host.c
Normal file
7
examples/cli/platform/host.c
Normal file
@ -0,0 +1,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
extern int rust_main();
|
||||
|
||||
int main() {
|
||||
return rust_main();
|
||||
}
|
139
examples/cli/platform/src/lib.rs
Normal file
139
examples/cli/platform/src/lib.rs
Normal file
@ -0,0 +1,139 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use core::alloc::Layout;
|
||||
use core::ffi::c_void;
|
||||
use core::mem::MaybeUninit;
|
||||
use libc;
|
||||
use roc_std::{RocCallResult, RocStr};
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed"]
|
||||
fn roc_main(output: *mut u8) -> ();
|
||||
|
||||
#[link_name = "roc__mainForHost_size"]
|
||||
fn roc_main_size() -> i64;
|
||||
|
||||
#[link_name = "roc__mainForHost_1_Fx_caller"]
|
||||
fn call_Fx(flags: *const u8, closure_data: *const u8, output: *mut u8) -> ();
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[link_name = "roc__mainForHost_1_Fx_size"]
|
||||
fn size_Fx() -> i64;
|
||||
|
||||
#[link_name = "roc__mainForHost_1_Fx_result_size"]
|
||||
fn size_Fx_result() -> i64;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
||||
libc::malloc(size)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn roc_realloc(
|
||||
c_ptr: *mut c_void,
|
||||
new_size: usize,
|
||||
_old_size: usize,
|
||||
_alignment: u32,
|
||||
) -> *mut c_void {
|
||||
libc::realloc(c_ptr, new_size)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||
libc::free(c_ptr)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe 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 fn rust_main() -> isize {
|
||||
let size = unsafe { roc_main_size() } as usize;
|
||||
let layout = Layout::array::<u8>(size).unwrap();
|
||||
|
||||
unsafe {
|
||||
// TODO allocate on the stack if it's under a certain size
|
||||
let buffer = std::alloc::alloc(layout);
|
||||
|
||||
roc_main(buffer);
|
||||
|
||||
let output = buffer as *mut RocCallResult<()>;
|
||||
|
||||
match (&*output).into() {
|
||||
Ok(()) => {
|
||||
let closure_data_ptr = buffer.offset(8);
|
||||
let result = call_the_closure(closure_data_ptr as *const u8);
|
||||
|
||||
std::alloc::dealloc(buffer, layout);
|
||||
|
||||
result
|
||||
}
|
||||
Err(msg) => {
|
||||
std::alloc::dealloc(buffer, layout);
|
||||
|
||||
panic!("Roc failed with message: {}", msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
||||
|
||||
unsafe fn call_the_closure(closure_data_ptr: *const u8) -> i64 {
|
||||
let size = size_Fx_result() as usize;
|
||||
let layout = Layout::array::<u8>(size).unwrap();
|
||||
let buffer = std::alloc::alloc(layout) as *mut u8;
|
||||
|
||||
call_Fx(
|
||||
// This flags pointer will never get dereferenced
|
||||
MaybeUninit::uninit().as_ptr(),
|
||||
closure_data_ptr as *const u8,
|
||||
buffer as *mut u8,
|
||||
);
|
||||
|
||||
let output = &*(buffer as *mut RocCallResult<()>);
|
||||
|
||||
match output.into() {
|
||||
Ok(_) => {
|
||||
std::alloc::dealloc(buffer, layout);
|
||||
0
|
||||
}
|
||||
Err(e) => panic!("failed with {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn roc_fx_getLine() -> RocStr {
|
||||
use std::io::{self, BufRead};
|
||||
|
||||
let stdin = io::stdin();
|
||||
let line1 = stdin.lock().lines().next().unwrap().unwrap();
|
||||
|
||||
RocStr::from_slice(line1.as_bytes())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn roc_fx_putLine(line: RocStr) -> () {
|
||||
let bytes = line.as_slice();
|
||||
let string = unsafe { std::str::from_utf8_unchecked(bytes) };
|
||||
println!("{}", string);
|
||||
|
||||
// don't mess with the refcount!
|
||||
core::mem::forget(line);
|
||||
|
||||
()
|
||||
}
|
@ -19,23 +19,32 @@ comptime {
|
||||
}
|
||||
}
|
||||
|
||||
extern fn malloc(size: usize) callconv(.C) ?*c_void;
|
||||
extern fn realloc(c_ptr: [*]align(@alignOf(u128)) u8, size: usize) callconv(.C) ?*c_void;
|
||||
extern fn free(c_ptr: [*]align(@alignOf(u128)) u8) callconv(.C) void;
|
||||
const Align = extern struct { a: usize, b: usize };
|
||||
extern fn malloc(size: usize) callconv(.C) ?*align(@alignOf(Align)) c_void;
|
||||
extern fn realloc(c_ptr: [*]align(@alignOf(Align)) u8, size: usize) callconv(.C) ?*c_void;
|
||||
extern fn free(c_ptr: [*]align(@alignOf(Align)) u8) callconv(.C) void;
|
||||
|
||||
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void {
|
||||
_ = alignment;
|
||||
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
export fn roc_realloc(c_ptr: *c_void, old_size: usize, new_size: usize, alignment: u32) callconv(.C) ?*c_void {
|
||||
return realloc(@alignCast(16, @ptrCast([*]u8, c_ptr)), new_size);
|
||||
export fn roc_realloc(c_ptr: *c_void, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*c_void {
|
||||
_ = old_size;
|
||||
_ = alignment;
|
||||
|
||||
return realloc(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)), new_size);
|
||||
}
|
||||
|
||||
export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
||||
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||
_ = alignment;
|
||||
|
||||
free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)));
|
||||
}
|
||||
|
||||
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||
_ = tag_id;
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||
@ -47,11 +56,11 @@ const Allocator = mem.Allocator;
|
||||
|
||||
extern fn roc__mainForHost_1_exposed(*RocCallResult) void;
|
||||
|
||||
const RocCallResult = extern struct { flag: usize, content: RocStr };
|
||||
const RocCallResult = extern struct { flag: u64, content: RocStr };
|
||||
|
||||
const Unit = extern struct {};
|
||||
|
||||
pub export fn main() i32 {
|
||||
pub fn main() u8 {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
|
||||
|
@ -584,6 +584,10 @@ impl RocStr {
|
||||
|
||||
let raw_ptr = Self::get_element_ptr(raw_ptr as *mut u8);
|
||||
|
||||
// write the refcount
|
||||
let refcount_ptr = raw_ptr as *mut isize;
|
||||
*(refcount_ptr.offset(-1)) = isize::MIN;
|
||||
|
||||
{
|
||||
// NOTE: using a memcpy here causes weird issues
|
||||
let target_ptr = raw_ptr as *mut u8;
|
||||
|
Loading…
Reference in New Issue
Block a user