mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-20 07:17:50 +03:00
Merge branch 'trunk' of github.com:rtfeldman/roc into bench-false-positives
This commit is contained in:
commit
aeb9422fef
@ -332,4 +332,17 @@ mod cli_run {
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(effect)]
|
||||
fn run_effect() {
|
||||
check_output_with_stdin(
|
||||
&example_file("effect", "Main.roc"),
|
||||
"hello world how are you",
|
||||
"effect-example",
|
||||
&[],
|
||||
"hello world how are you\n",
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
use morphic_lib::TypeContext;
|
||||
use morphic_lib::{
|
||||
BlockExpr, BlockId, CalleeSpecVar, ConstDefBuilder, ConstName, EntryPointName, ExprContext,
|
||||
FuncDef, FuncDefBuilder, FuncName, ModDefBuilder, ModName, ProgramBuilder, Result, TypeId,
|
||||
UpdateModeVar, ValueId,
|
||||
FuncDef, FuncDefBuilder, FuncName, ModDefBuilder, ModName, ProgramBuilder, Result,
|
||||
TypeDefBuilder, TypeId, TypeName, UpdateModeVar, ValueId,
|
||||
};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::Symbol;
|
||||
use std::convert::TryFrom;
|
||||
@ -26,6 +26,26 @@ pub fn func_name_bytes(proc: &Proc) -> [u8; SIZE] {
|
||||
const DEBUG: bool = false;
|
||||
const SIZE: usize = if DEBUG { 50 } else { 16 };
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash)]
|
||||
struct TagUnionId(u64);
|
||||
|
||||
fn recursive_tag_union_name_bytes(union_layout: &UnionLayout) -> TagUnionId {
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::Hash;
|
||||
use std::hash::Hasher;
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
union_layout.hash(&mut hasher);
|
||||
|
||||
TagUnionId(hasher.finish())
|
||||
}
|
||||
|
||||
impl TagUnionId {
|
||||
const fn as_bytes(&self) -> [u8; 8] {
|
||||
self.0.to_ne_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn func_name_bytes_help<'a, I>(
|
||||
symbol: Symbol,
|
||||
argument_layouts: I,
|
||||
@ -134,6 +154,8 @@ where
|
||||
let entry_point_name = FuncName(ENTRY_POINT_NAME);
|
||||
m.add_func(entry_point_name, entry_point_function)?;
|
||||
|
||||
let mut type_definitions = MutSet::default();
|
||||
|
||||
// all other functions
|
||||
for proc in procs {
|
||||
let bytes = func_name_bytes(proc);
|
||||
@ -148,11 +170,32 @@ where
|
||||
);
|
||||
}
|
||||
|
||||
let spec = proc_spec(proc)?;
|
||||
let (spec, type_names) = proc_spec(proc)?;
|
||||
|
||||
type_definitions.extend(type_names);
|
||||
|
||||
m.add_func(func_name, spec)?;
|
||||
}
|
||||
|
||||
for union_layout in type_definitions {
|
||||
let type_name_bytes = recursive_tag_union_name_bytes(&union_layout).as_bytes();
|
||||
let type_name = TypeName(&type_name_bytes);
|
||||
|
||||
let mut builder = TypeDefBuilder::new();
|
||||
|
||||
let variant_types = build_variant_types(&mut builder, &union_layout)?;
|
||||
let root_type = if let UnionLayout::NonNullableUnwrapped(_) = union_layout {
|
||||
debug_assert_eq!(variant_types.len(), 1);
|
||||
variant_types[0]
|
||||
} else {
|
||||
builder.add_union_type(&variant_types)?
|
||||
};
|
||||
|
||||
let type_def = builder.build(root_type)?;
|
||||
|
||||
m.add_named_type(type_name, type_def)?;
|
||||
}
|
||||
|
||||
m.build()?
|
||||
};
|
||||
|
||||
@ -195,7 +238,7 @@ fn build_entry_point(layout: crate::ir::ProcLayout, func_name: FuncName) -> Resu
|
||||
Ok(spec)
|
||||
}
|
||||
|
||||
fn proc_spec(proc: &Proc) -> Result<FuncDef> {
|
||||
fn proc_spec<'a>(proc: &Proc<'a>) -> Result<(FuncDef, MutSet<UnionLayout<'a>>)> {
|
||||
let mut builder = FuncDefBuilder::new();
|
||||
let mut env = Env::default();
|
||||
|
||||
@ -218,21 +261,22 @@ fn proc_spec(proc: &Proc) -> Result<FuncDef> {
|
||||
|
||||
let spec = builder.build(arg_type_id, ret_type_id, root)?;
|
||||
|
||||
Ok(spec)
|
||||
Ok((spec, env.type_names))
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Env {
|
||||
struct Env<'a> {
|
||||
symbols: MutMap<Symbol, ValueId>,
|
||||
join_points: MutMap<crate::ir::JoinPointId, morphic_lib::ContinuationId>,
|
||||
type_names: MutSet<UnionLayout<'a>>,
|
||||
}
|
||||
|
||||
fn stmt_spec(
|
||||
fn stmt_spec<'a>(
|
||||
builder: &mut FuncDefBuilder,
|
||||
env: &mut Env,
|
||||
env: &mut Env<'a>,
|
||||
block: BlockId,
|
||||
layout: &Layout,
|
||||
stmt: &Stmt,
|
||||
stmt: &Stmt<'a>,
|
||||
) -> Result<ValueId> {
|
||||
use Stmt::*;
|
||||
|
||||
@ -420,7 +464,27 @@ fn build_tuple_value(
|
||||
builder.add_make_tuple(block, &value_ids)
|
||||
}
|
||||
|
||||
fn build_tuple_type(builder: &mut FuncDefBuilder, layouts: &[Layout]) -> Result<TypeId> {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum WhenRecursive<'a> {
|
||||
Unreachable,
|
||||
Loop(UnionLayout<'a>),
|
||||
}
|
||||
|
||||
fn build_recursive_tuple_type(
|
||||
builder: &mut impl TypeContext,
|
||||
layouts: &[Layout],
|
||||
when_recursive: &WhenRecursive,
|
||||
) -> Result<TypeId> {
|
||||
let mut field_types = Vec::new();
|
||||
|
||||
for field in layouts.iter() {
|
||||
field_types.push(layout_spec_help(builder, field, when_recursive)?);
|
||||
}
|
||||
|
||||
builder.add_tuple_type(&field_types)
|
||||
}
|
||||
|
||||
fn build_tuple_type(builder: &mut impl TypeContext, layouts: &[Layout]) -> Result<TypeId> {
|
||||
let mut field_types = Vec::new();
|
||||
|
||||
for field in layouts.iter() {
|
||||
@ -854,46 +918,95 @@ fn lowlevel_spec(
|
||||
}
|
||||
}
|
||||
|
||||
fn recursive_tag_variant(
|
||||
builder: &mut impl TypeContext,
|
||||
union_layout: &UnionLayout,
|
||||
fields: &[Layout],
|
||||
) -> Result<TypeId> {
|
||||
let when_recursive = WhenRecursive::Loop(*union_layout);
|
||||
|
||||
let data_id = build_recursive_tuple_type(builder, fields, &when_recursive)?;
|
||||
let cell_id = builder.add_heap_cell_type();
|
||||
|
||||
builder.add_tuple_type(&[cell_id, data_id])
|
||||
}
|
||||
|
||||
fn build_variant_types(
|
||||
builder: &mut FuncDefBuilder,
|
||||
builder: &mut impl TypeContext,
|
||||
union_layout: &UnionLayout,
|
||||
) -> Result<Vec<TypeId>> {
|
||||
use UnionLayout::*;
|
||||
|
||||
let mut result = Vec::new();
|
||||
let mut result;
|
||||
|
||||
match union_layout {
|
||||
NonRecursive(tags) => {
|
||||
result = Vec::with_capacity(tags.len());
|
||||
|
||||
for tag in tags.iter() {
|
||||
result.push(build_tuple_type(builder, tag)?);
|
||||
}
|
||||
}
|
||||
Recursive(_) => unreachable!(),
|
||||
NonNullableUnwrapped(_) => unreachable!(),
|
||||
Recursive(tags) => {
|
||||
result = Vec::with_capacity(tags.len());
|
||||
|
||||
for tag in tags.iter() {
|
||||
result.push(recursive_tag_variant(builder, union_layout, tag)?);
|
||||
}
|
||||
}
|
||||
NonNullableUnwrapped(fields) => {
|
||||
result = vec![recursive_tag_variant(builder, union_layout, fields)?];
|
||||
}
|
||||
NullableWrapped {
|
||||
nullable_id: _,
|
||||
other_tags: _,
|
||||
} => unreachable!(),
|
||||
nullable_id,
|
||||
other_tags: tags,
|
||||
} => {
|
||||
result = Vec::with_capacity(tags.len() + 1);
|
||||
|
||||
let cutoff = *nullable_id as usize;
|
||||
|
||||
for tag in tags[..cutoff].iter() {
|
||||
result.push(recursive_tag_variant(builder, union_layout, tag)?);
|
||||
}
|
||||
|
||||
let unit = builder.add_tuple_type(&[])?;
|
||||
result.push(unit);
|
||||
|
||||
for tag in tags[cutoff..].iter() {
|
||||
result.push(recursive_tag_variant(builder, union_layout, tag)?);
|
||||
}
|
||||
}
|
||||
NullableUnwrapped {
|
||||
nullable_id: _,
|
||||
other_fields: _,
|
||||
} => unreachable!(),
|
||||
nullable_id,
|
||||
other_fields: fields,
|
||||
} => {
|
||||
let unit = builder.add_tuple_type(&[])?;
|
||||
let other_type = recursive_tag_variant(builder, union_layout, fields)?;
|
||||
|
||||
if *nullable_id {
|
||||
// nullable_id == 1
|
||||
result = vec![other_type, unit];
|
||||
} else {
|
||||
result = vec![unit, other_type];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn worst_case_type(context: &mut impl TypeContext) -> Result<TypeId> {
|
||||
let cell = context.add_heap_cell_type();
|
||||
context.add_bag_type(cell)
|
||||
}
|
||||
|
||||
fn expr_spec(
|
||||
fn expr_spec<'a>(
|
||||
builder: &mut FuncDefBuilder,
|
||||
env: &mut Env,
|
||||
env: &mut Env<'a>,
|
||||
block: BlockId,
|
||||
layout: &Layout,
|
||||
expr: &Expr,
|
||||
layout: &Layout<'a>,
|
||||
expr: &Expr<'a>,
|
||||
) -> Result<ValueId> {
|
||||
use Expr::*;
|
||||
|
||||
@ -912,21 +1025,54 @@ fn expr_spec(
|
||||
tag_name: _,
|
||||
tag_id,
|
||||
arguments,
|
||||
} => match tag_layout {
|
||||
UnionLayout::NonRecursive(_) => {
|
||||
let value_id = build_tuple_value(builder, env, block, arguments)?;
|
||||
let variant_types = build_variant_types(builder, tag_layout)?;
|
||||
builder.add_make_union(block, &variant_types, *tag_id as u32, value_id)
|
||||
}
|
||||
UnionLayout::Recursive(_)
|
||||
| UnionLayout::NonNullableUnwrapped(_)
|
||||
| UnionLayout::NullableWrapped { .. }
|
||||
| UnionLayout::NullableUnwrapped { .. } => {
|
||||
let result_type = worst_case_type(builder)?;
|
||||
let value_id = build_tuple_value(builder, env, block, arguments)?;
|
||||
builder.add_unknown_with(block, &[value_id], result_type)
|
||||
}
|
||||
},
|
||||
} => {
|
||||
let variant_types = build_variant_types(builder, tag_layout)?;
|
||||
|
||||
let data_id = build_tuple_value(builder, env, block, arguments)?;
|
||||
let cell_id = builder.add_new_heap_cell(block)?;
|
||||
|
||||
let value_id = match tag_layout {
|
||||
UnionLayout::NonRecursive(_) => {
|
||||
let value_id = build_tuple_value(builder, env, block, arguments)?;
|
||||
return builder.add_make_union(block, &variant_types, *tag_id as u32, value_id);
|
||||
}
|
||||
UnionLayout::NonNullableUnwrapped(_) => {
|
||||
let value_id = builder.add_make_tuple(block, &[cell_id, data_id])?;
|
||||
|
||||
let type_name_bytes = recursive_tag_union_name_bytes(tag_layout).as_bytes();
|
||||
let type_name = TypeName(&type_name_bytes);
|
||||
|
||||
env.type_names.insert(*tag_layout);
|
||||
|
||||
return builder.add_make_named(block, MOD_APP, type_name, value_id);
|
||||
}
|
||||
UnionLayout::Recursive(_) => builder.add_make_tuple(block, &[cell_id, data_id])?,
|
||||
UnionLayout::NullableWrapped { nullable_id, .. } => {
|
||||
if *tag_id == *nullable_id as u8 {
|
||||
data_id
|
||||
} else {
|
||||
builder.add_make_tuple(block, &[cell_id, data_id])?
|
||||
}
|
||||
}
|
||||
UnionLayout::NullableUnwrapped { nullable_id, .. } => {
|
||||
if *tag_id == *nullable_id as u8 {
|
||||
data_id
|
||||
} else {
|
||||
builder.add_make_tuple(block, &[cell_id, data_id])?
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let union_id =
|
||||
builder.add_make_union(block, &variant_types, *tag_id as u32, value_id)?;
|
||||
|
||||
let type_name_bytes = recursive_tag_union_name_bytes(tag_layout).as_bytes();
|
||||
let type_name = TypeName(&type_name_bytes);
|
||||
|
||||
env.type_names.insert(*tag_layout);
|
||||
|
||||
builder.add_make_named(block, MOD_APP, type_name, union_id)
|
||||
}
|
||||
Struct(fields) => build_tuple_value(builder, env, block, fields),
|
||||
UnionAtIndex {
|
||||
index,
|
||||
@ -942,11 +1088,45 @@ fn expr_spec(
|
||||
|
||||
builder.add_get_tuple_field(block, tuple_value_id, index)
|
||||
}
|
||||
_ => {
|
||||
// for the moment recursive tag unions don't quite work
|
||||
let value_id = env.symbols[structure];
|
||||
let result_type = layout_spec(builder, layout)?;
|
||||
builder.add_unknown_with(block, &[value_id], result_type)
|
||||
UnionLayout::Recursive(_)
|
||||
| UnionLayout::NullableUnwrapped { .. }
|
||||
| UnionLayout::NullableWrapped { .. } => {
|
||||
let index = (*index) as u32;
|
||||
let tag_value_id = env.symbols[structure];
|
||||
|
||||
let type_name_bytes = recursive_tag_union_name_bytes(&union_layout).as_bytes();
|
||||
let type_name = TypeName(&type_name_bytes);
|
||||
|
||||
let union_id = builder.add_unwrap_named(block, MOD_APP, type_name, tag_value_id)?;
|
||||
let variant_id = builder.add_unwrap_union(block, union_id, *tag_id as u32)?;
|
||||
|
||||
// we're reading from this value, so touch the heap cell
|
||||
let heap_cell = builder.add_get_tuple_field(block, variant_id, 0)?;
|
||||
builder.add_touch(block, heap_cell)?;
|
||||
|
||||
let tuple_value_id = builder.add_get_tuple_field(block, variant_id, 1)?;
|
||||
|
||||
builder.add_get_tuple_field(block, tuple_value_id, index)
|
||||
}
|
||||
UnionLayout::NonNullableUnwrapped { .. } => {
|
||||
let index = (*index) as u32;
|
||||
debug_assert!(*tag_id == 0);
|
||||
|
||||
let tag_value_id = env.symbols[structure];
|
||||
|
||||
let type_name_bytes = recursive_tag_union_name_bytes(&union_layout).as_bytes();
|
||||
let type_name = TypeName(&type_name_bytes);
|
||||
|
||||
let variant_id =
|
||||
builder.add_unwrap_named(block, MOD_APP, type_name, tag_value_id)?;
|
||||
|
||||
// we're reading from this value, so touch the heap cell
|
||||
let heap_cell = builder.add_get_tuple_field(block, variant_id, 0)?;
|
||||
builder.add_touch(block, heap_cell)?;
|
||||
|
||||
let tuple_value_id = builder.add_get_tuple_field(block, variant_id, 1)?;
|
||||
|
||||
builder.add_get_tuple_field(block, tuple_value_id, index)
|
||||
}
|
||||
},
|
||||
StructAtIndex {
|
||||
@ -1017,34 +1197,66 @@ fn literal_spec(
|
||||
}
|
||||
}
|
||||
|
||||
fn layout_spec(builder: &mut FuncDefBuilder, layout: &Layout) -> Result<TypeId> {
|
||||
fn layout_spec(builder: &mut impl TypeContext, layout: &Layout) -> Result<TypeId> {
|
||||
layout_spec_help(builder, layout, &WhenRecursive::Unreachable)
|
||||
}
|
||||
|
||||
fn layout_spec_help(
|
||||
builder: &mut impl TypeContext,
|
||||
layout: &Layout,
|
||||
when_recursive: &WhenRecursive,
|
||||
) -> Result<TypeId> {
|
||||
use Layout::*;
|
||||
|
||||
match layout {
|
||||
Builtin(builtin) => builtin_spec(builder, builtin),
|
||||
Struct(fields) => build_tuple_type(builder, fields),
|
||||
Union(union_layout) => match union_layout {
|
||||
UnionLayout::NonRecursive(_) => {
|
||||
let variant_types = build_variant_types(builder, union_layout)?;
|
||||
builder.add_union_type(&variant_types)
|
||||
Builtin(builtin) => builtin_spec(builder, builtin, when_recursive),
|
||||
Struct(fields) => build_recursive_tuple_type(builder, fields, when_recursive),
|
||||
Union(union_layout) => {
|
||||
let variant_types = build_variant_types(builder, union_layout)?;
|
||||
|
||||
match union_layout {
|
||||
UnionLayout::NonRecursive(_) => builder.add_union_type(&variant_types),
|
||||
UnionLayout::Recursive(_)
|
||||
| UnionLayout::NullableUnwrapped { .. }
|
||||
| UnionLayout::NullableWrapped { .. }
|
||||
| UnionLayout::NonNullableUnwrapped(_) => {
|
||||
let type_name_bytes = recursive_tag_union_name_bytes(&union_layout).as_bytes();
|
||||
let type_name = TypeName(&type_name_bytes);
|
||||
|
||||
Ok(builder.add_named_type(MOD_APP, type_name))
|
||||
}
|
||||
}
|
||||
UnionLayout::Recursive(_) => worst_case_type(builder),
|
||||
UnionLayout::NonNullableUnwrapped(_) => worst_case_type(builder),
|
||||
UnionLayout::NullableWrapped {
|
||||
nullable_id: _,
|
||||
other_tags: _,
|
||||
} => worst_case_type(builder),
|
||||
UnionLayout::NullableUnwrapped {
|
||||
nullable_id: _,
|
||||
other_fields: _,
|
||||
} => worst_case_type(builder),
|
||||
}
|
||||
RecursivePointer => match when_recursive {
|
||||
WhenRecursive::Unreachable => {
|
||||
unreachable!()
|
||||
}
|
||||
WhenRecursive::Loop(union_layout) => match union_layout {
|
||||
UnionLayout::NonRecursive(_) => unreachable!(),
|
||||
UnionLayout::Recursive(_)
|
||||
| UnionLayout::NullableUnwrapped { .. }
|
||||
| UnionLayout::NullableWrapped { .. }
|
||||
| UnionLayout::NonNullableUnwrapped(_) => {
|
||||
let type_name_bytes = recursive_tag_union_name_bytes(union_layout).as_bytes();
|
||||
let type_name = TypeName(&type_name_bytes);
|
||||
|
||||
Ok(builder.add_named_type(MOD_APP, type_name))
|
||||
}
|
||||
},
|
||||
},
|
||||
RecursivePointer => worst_case_type(builder),
|
||||
Closure(_, lambda_set, _) => layout_spec(builder, &lambda_set.runtime_representation()),
|
||||
Closure(_, lambda_set, _) => layout_spec_help(
|
||||
builder,
|
||||
&lambda_set.runtime_representation(),
|
||||
when_recursive,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn builtin_spec(builder: &mut FuncDefBuilder, builtin: &Builtin) -> Result<TypeId> {
|
||||
fn builtin_spec(
|
||||
builder: &mut impl TypeContext,
|
||||
builtin: &Builtin,
|
||||
when_recursive: &WhenRecursive,
|
||||
) -> Result<TypeId> {
|
||||
use Builtin::*;
|
||||
|
||||
match builtin {
|
||||
@ -1052,8 +1264,8 @@ fn builtin_spec(builder: &mut FuncDefBuilder, builtin: &Builtin) -> Result<TypeI
|
||||
Float128 | Float64 | Float32 | Float16 => builder.add_tuple_type(&[]),
|
||||
Str | EmptyStr => str_type(builder),
|
||||
Dict(key_layout, value_layout) => {
|
||||
let value_type = layout_spec(builder, value_layout)?;
|
||||
let key_type = layout_spec(builder, key_layout)?;
|
||||
let value_type = layout_spec_help(builder, value_layout, when_recursive)?;
|
||||
let key_type = layout_spec_help(builder, key_layout, when_recursive)?;
|
||||
let element_type = builder.add_tuple_type(&[key_type, value_type])?;
|
||||
|
||||
let cell = builder.add_heap_cell_type();
|
||||
@ -1062,7 +1274,7 @@ fn builtin_spec(builder: &mut FuncDefBuilder, builtin: &Builtin) -> Result<TypeI
|
||||
}
|
||||
Set(key_layout) => {
|
||||
let value_type = builder.add_tuple_type(&[])?;
|
||||
let key_type = layout_spec(builder, key_layout)?;
|
||||
let key_type = layout_spec_help(builder, key_layout, when_recursive)?;
|
||||
let element_type = builder.add_tuple_type(&[key_type, value_type])?;
|
||||
|
||||
let cell = builder.add_heap_cell_type();
|
||||
@ -1070,7 +1282,7 @@ fn builtin_spec(builder: &mut FuncDefBuilder, builtin: &Builtin) -> Result<TypeI
|
||||
builder.add_tuple_type(&[cell, bag])
|
||||
}
|
||||
List(element_layout) => {
|
||||
let element_type = layout_spec(builder, element_layout)?;
|
||||
let element_type = layout_spec_help(builder, element_layout, when_recursive)?;
|
||||
|
||||
let cell = builder.add_heap_cell_type();
|
||||
let bag = builder.add_bag_type(element_type)?;
|
||||
|
@ -968,15 +968,6 @@ impl<'a> Context<'a> {
|
||||
// live vars of the whole expression
|
||||
let invoke_live_vars = collect_stmt(stmt, &self.jp_live_vars, MutSet::default());
|
||||
|
||||
// the result of an invoke should not be touched in the fail branch
|
||||
// but it should be present in the pass branch (otherwise it would be dead)
|
||||
// NOTE: we cheat a bit here to allow `invoke` when generating code for `expect`
|
||||
let is_dead = !invoke_live_vars.contains(symbol);
|
||||
|
||||
if is_dead && layout.is_refcounted() {
|
||||
panic!("A variable of a reference-counted layout is dead; that's a bug!");
|
||||
}
|
||||
|
||||
let fail = {
|
||||
// TODO should we use ctor info like Lean?
|
||||
let ctx = self.clone();
|
||||
|
@ -4191,13 +4191,23 @@ fn convert_tag_union<'a>(
|
||||
let field_symbols_temp = sorted_field_symbols(env, procs, layout_cache, args);
|
||||
|
||||
let field_symbols;
|
||||
let opt_tag_id_symbol;
|
||||
|
||||
// we must derive the union layout from the whole_var, building it up
|
||||
// from `layouts` would unroll recursive tag unions, and that leads to
|
||||
// problems down the line because we hash layouts and an unrolled
|
||||
// version is not the same as the minimal version.
|
||||
let union_layout = match return_on_layout_error!(
|
||||
env,
|
||||
layout_cache.from_var(env.arena, variant_var, env.subs)
|
||||
) {
|
||||
Layout::Union(ul) => ul,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
use WrappedVariant::*;
|
||||
let (tag, union_layout) = match variant {
|
||||
Recursive { sorted_tag_layouts } => {
|
||||
debug_assert!(sorted_tag_layouts.len() > 1);
|
||||
opt_tag_id_symbol = None;
|
||||
|
||||
field_symbols = {
|
||||
let mut temp = Vec::with_capacity_in(field_symbols_temp.len() + 1, arena);
|
||||
@ -4214,9 +4224,6 @@ fn convert_tag_union<'a>(
|
||||
layouts.push(arg_layouts);
|
||||
}
|
||||
|
||||
debug_assert!(layouts.len() > 1);
|
||||
let union_layout = UnionLayout::Recursive(layouts.into_bump_slice());
|
||||
|
||||
let tag = Expr::Tag {
|
||||
tag_layout: union_layout,
|
||||
tag_name,
|
||||
@ -4227,13 +4234,11 @@ fn convert_tag_union<'a>(
|
||||
(tag, union_layout)
|
||||
}
|
||||
NonNullableUnwrapped {
|
||||
fields,
|
||||
tag_name: wrapped_tag_name,
|
||||
..
|
||||
} => {
|
||||
debug_assert_eq!(tag_name, wrapped_tag_name);
|
||||
|
||||
opt_tag_id_symbol = None;
|
||||
|
||||
field_symbols = {
|
||||
let mut temp = Vec::with_capacity_in(field_symbols_temp.len(), arena);
|
||||
|
||||
@ -4242,8 +4247,6 @@ fn convert_tag_union<'a>(
|
||||
temp.into_bump_slice()
|
||||
};
|
||||
|
||||
let union_layout = UnionLayout::NonNullableUnwrapped(fields);
|
||||
|
||||
let tag = Expr::Tag {
|
||||
tag_layout: union_layout,
|
||||
tag_name,
|
||||
@ -4254,8 +4257,6 @@ fn convert_tag_union<'a>(
|
||||
(tag, union_layout)
|
||||
}
|
||||
NonRecursive { sorted_tag_layouts } => {
|
||||
opt_tag_id_symbol = None;
|
||||
|
||||
field_symbols = {
|
||||
let mut temp = Vec::with_capacity_in(field_symbols_temp.len(), arena);
|
||||
|
||||
@ -4271,8 +4272,6 @@ fn convert_tag_union<'a>(
|
||||
layouts.push(arg_layouts);
|
||||
}
|
||||
|
||||
let union_layout = UnionLayout::NonRecursive(layouts.into_bump_slice());
|
||||
|
||||
let tag = Expr::Tag {
|
||||
tag_layout: union_layout,
|
||||
tag_name,
|
||||
@ -4283,12 +4282,8 @@ fn convert_tag_union<'a>(
|
||||
(tag, union_layout)
|
||||
}
|
||||
NullableWrapped {
|
||||
nullable_id,
|
||||
nullable_name: _,
|
||||
sorted_tag_layouts,
|
||||
sorted_tag_layouts, ..
|
||||
} => {
|
||||
opt_tag_id_symbol = None;
|
||||
|
||||
field_symbols = {
|
||||
let mut temp = Vec::with_capacity_in(field_symbols_temp.len() + 1, arena);
|
||||
|
||||
@ -4304,11 +4299,6 @@ fn convert_tag_union<'a>(
|
||||
layouts.push(arg_layouts);
|
||||
}
|
||||
|
||||
let union_layout = UnionLayout::NullableWrapped {
|
||||
nullable_id,
|
||||
other_tags: layouts.into_bump_slice(),
|
||||
};
|
||||
|
||||
let tag = Expr::Tag {
|
||||
tag_layout: union_layout,
|
||||
tag_name,
|
||||
@ -4318,16 +4308,7 @@ fn convert_tag_union<'a>(
|
||||
|
||||
(tag, union_layout)
|
||||
}
|
||||
NullableUnwrapped {
|
||||
nullable_id,
|
||||
nullable_name: _,
|
||||
other_name: _,
|
||||
other_fields,
|
||||
} => {
|
||||
// FIXME drop tag
|
||||
let tag_id_symbol = env.unique_symbol();
|
||||
opt_tag_id_symbol = Some(tag_id_symbol);
|
||||
|
||||
NullableUnwrapped { .. } => {
|
||||
field_symbols = {
|
||||
let mut temp = Vec::with_capacity_in(field_symbols_temp.len() + 1, arena);
|
||||
|
||||
@ -4336,11 +4317,6 @@ fn convert_tag_union<'a>(
|
||||
temp.into_bump_slice()
|
||||
};
|
||||
|
||||
let union_layout = UnionLayout::NullableUnwrapped {
|
||||
nullable_id,
|
||||
other_fields,
|
||||
};
|
||||
|
||||
let tag = Expr::Tag {
|
||||
tag_layout: union_layout,
|
||||
tag_name,
|
||||
@ -4352,26 +4328,14 @@ fn convert_tag_union<'a>(
|
||||
}
|
||||
};
|
||||
|
||||
let mut stmt = Stmt::Let(assigned, tag, Layout::Union(union_layout), hole);
|
||||
let stmt = Stmt::Let(assigned, tag, Layout::Union(union_layout), hole);
|
||||
let iter = field_symbols_temp
|
||||
.into_iter()
|
||||
.map(|x| x.2 .0)
|
||||
.rev()
|
||||
.zip(field_symbols.iter().rev());
|
||||
|
||||
stmt = assign_to_symbols(env, procs, layout_cache, iter, stmt);
|
||||
|
||||
if let Some(tag_id_symbol) = opt_tag_id_symbol {
|
||||
// define the tag id
|
||||
stmt = Stmt::Let(
|
||||
tag_id_symbol,
|
||||
Expr::Literal(Literal::Int(tag_id as i128)),
|
||||
union_layout.tag_id_layout(),
|
||||
arena.alloc(stmt),
|
||||
);
|
||||
}
|
||||
|
||||
stmt
|
||||
assign_to_symbols(env, procs, layout_cache, iter, stmt)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7047,6 +7011,15 @@ fn from_can_pattern_help<'a>(
|
||||
temp
|
||||
};
|
||||
|
||||
// we must derive the union layout from the whole_var, building it up
|
||||
// from `layouts` would unroll recursive tag unions, and that leads to
|
||||
// problems down the line because we hash layouts and an unrolled
|
||||
// version is not the same as the minimal version.
|
||||
let layout = match layout_cache.from_var(env.arena, *whole_var, env.subs) {
|
||||
Ok(Layout::Union(ul)) => ul,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
use WrappedVariant::*;
|
||||
match variant {
|
||||
NonRecursive {
|
||||
@ -7091,18 +7064,6 @@ fn from_can_pattern_help<'a>(
|
||||
));
|
||||
}
|
||||
|
||||
let layouts: Vec<&'a [Layout<'a>]> = {
|
||||
let mut temp = Vec::with_capacity_in(tags.len(), env.arena);
|
||||
|
||||
for (_, arg_layouts) in tags.into_iter() {
|
||||
temp.push(*arg_layouts);
|
||||
}
|
||||
|
||||
temp
|
||||
};
|
||||
|
||||
let layout = UnionLayout::NonRecursive(layouts.into_bump_slice());
|
||||
|
||||
Pattern::AppliedTag {
|
||||
tag_name: tag_name.clone(),
|
||||
tag_id: tag_id as u8,
|
||||
@ -7148,19 +7109,6 @@ fn from_can_pattern_help<'a>(
|
||||
));
|
||||
}
|
||||
|
||||
let layouts: Vec<&'a [Layout<'a>]> = {
|
||||
let mut temp = Vec::with_capacity_in(tags.len(), env.arena);
|
||||
|
||||
for (_, arg_layouts) in tags.into_iter() {
|
||||
temp.push(*arg_layouts);
|
||||
}
|
||||
|
||||
temp
|
||||
};
|
||||
|
||||
debug_assert!(layouts.len() > 1);
|
||||
let layout = UnionLayout::Recursive(layouts.into_bump_slice());
|
||||
|
||||
Pattern::AppliedTag {
|
||||
tag_name: tag_name.clone(),
|
||||
tag_id: tag_id as u8,
|
||||
@ -7204,8 +7152,6 @@ fn from_can_pattern_help<'a>(
|
||||
));
|
||||
}
|
||||
|
||||
let layout = UnionLayout::NonNullableUnwrapped(fields);
|
||||
|
||||
Pattern::AppliedTag {
|
||||
tag_name: tag_name.clone(),
|
||||
tag_id: tag_id as u8,
|
||||
@ -7279,21 +7225,6 @@ fn from_can_pattern_help<'a>(
|
||||
));
|
||||
}
|
||||
|
||||
let layouts: Vec<&'a [Layout<'a>]> = {
|
||||
let mut temp = Vec::with_capacity_in(tags.len(), env.arena);
|
||||
|
||||
for (_, arg_layouts) in tags.into_iter() {
|
||||
temp.push(*arg_layouts);
|
||||
}
|
||||
|
||||
temp
|
||||
};
|
||||
|
||||
let layout = UnionLayout::NullableWrapped {
|
||||
nullable_id,
|
||||
other_tags: layouts.into_bump_slice(),
|
||||
};
|
||||
|
||||
Pattern::AppliedTag {
|
||||
tag_name: tag_name.clone(),
|
||||
tag_id: tag_id as u8,
|
||||
@ -7350,11 +7281,6 @@ fn from_can_pattern_help<'a>(
|
||||
));
|
||||
}
|
||||
|
||||
let layout = UnionLayout::NullableUnwrapped {
|
||||
nullable_id,
|
||||
other_fields,
|
||||
};
|
||||
|
||||
Pattern::AppliedTag {
|
||||
tag_name: tag_name.clone(),
|
||||
tag_id: tag_id as u8,
|
||||
|
@ -3239,7 +3239,6 @@ fn to_requires_report<'a>(
|
||||
ERequires::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
|
||||
|
||||
ERequires::ListStart(row, col) => {
|
||||
dbg!(row, col);
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||
let region = Region::from_row_col(row, col);
|
||||
|
||||
@ -3263,6 +3262,34 @@ fn to_requires_report<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
ERequires::Rigid(row, col) => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||
let region = Region::from_row_col(row, col);
|
||||
|
||||
let doc = alloc.stack(vec![
|
||||
alloc.reflow(r"I am partway through parsing a header, but I got stuck here:"),
|
||||
alloc.region_with_subregion(surroundings, region),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("I am expecting a list of rigids like "),
|
||||
alloc.keyword("{}"),
|
||||
alloc.reflow(" or "),
|
||||
alloc.keyword("{model=>Model}"),
|
||||
alloc.reflow(" next. A full "),
|
||||
alloc.keyword("requires"),
|
||||
alloc.reflow(" definition looks like"),
|
||||
]),
|
||||
alloc
|
||||
.parser_suggestion("requires {model=>Model, msg=>Msg} {main : Effect {}}")
|
||||
.indent(4),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "BAD REQUIRES RIGIDS".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
_ => todo!("unhandled parse error {:?}", parse_problem),
|
||||
}
|
||||
}
|
||||
@ -3368,6 +3395,7 @@ fn to_space_report<'a>(
|
||||
title: "TAB CHARACTER".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
_ => todo!("unhandled type parse error: {:?}", &parse_problem),
|
||||
}
|
||||
}
|
||||
|
@ -5929,6 +5929,44 @@ mod test_reporting {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn platform_requires_rigids() {
|
||||
report_header_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
platform folkertdev/foo
|
||||
requires { main : Effect {} }
|
||||
exposes []
|
||||
packages {}
|
||||
imports [Task]
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect
|
||||
{
|
||||
putChar : I64 -> Effect {},
|
||||
putLine : Str -> Effect {},
|
||||
getLine : Effect Str
|
||||
}
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── BAD REQUIRES RIGIDS ─────────────────────────────────────────────────────────
|
||||
|
||||
I am partway through parsing a header, but I got stuck here:
|
||||
|
||||
1│ platform folkertdev/foo
|
||||
2│ requires { main : Effect {} }
|
||||
^
|
||||
|
||||
I am expecting a list of rigids like `{}` or `{model=>Model}` next. A full
|
||||
`requires` definition looks like
|
||||
|
||||
requires {model=>Model, msg=>Msg} {main : Effect {}}
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exposes_identifier() {
|
||||
report_header_problem_as(
|
||||
|
@ -21,10 +21,8 @@ procedure Test.3 (Test.4):
|
||||
jump Test.13 Test.4;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.28 = 0i64;
|
||||
let Test.30 = 3i64;
|
||||
let Test.26 = Just Test.30;
|
||||
let Test.29 = 1i64;
|
||||
let Test.28 = 3i64;
|
||||
let Test.26 = Just Test.28;
|
||||
let Test.27 = Nil ;
|
||||
let Test.12 = Cons Test.26 Test.27;
|
||||
let Test.11 = CallByName Test.3 Test.12;
|
||||
|
@ -10,9 +10,7 @@ procedure Test.2 (Test.3):
|
||||
ret Test.11;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.17 = 0i64;
|
||||
let Test.15 = 2i64;
|
||||
let Test.18 = 1i64;
|
||||
let Test.16 = Nil ;
|
||||
let Test.9 = Cons Test.15 Test.16;
|
||||
let Test.8 = CallByName Test.2 Test.9;
|
||||
|
@ -17,7 +17,6 @@ procedure Test.3 (Test.5):
|
||||
ret Test.14;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.21 = 1i64;
|
||||
let Test.2 = Nil ;
|
||||
let Test.8 = CallByName Test.3 Test.2;
|
||||
let Test.9 = CallByName Test.3 Test.2;
|
||||
|
@ -1,10 +1,6 @@
|
||||
procedure Test.0 ():
|
||||
let Test.9 = 0i64;
|
||||
let Test.11 = 0i64;
|
||||
let Test.13 = 0i64;
|
||||
let Test.14 = 1i64;
|
||||
let Test.12 = Z ;
|
||||
let Test.10 = S Test.12;
|
||||
let Test.8 = S Test.10;
|
||||
let Test.10 = Z ;
|
||||
let Test.9 = S Test.10;
|
||||
let Test.8 = S Test.9;
|
||||
let Test.2 = S Test.8;
|
||||
ret Test.2;
|
||||
|
@ -1,11 +1,7 @@
|
||||
procedure Test.0 ():
|
||||
let Test.13 = 0i64;
|
||||
let Test.15 = 0i64;
|
||||
let Test.17 = 0i64;
|
||||
let Test.18 = 1i64;
|
||||
let Test.16 = Z ;
|
||||
let Test.14 = S Test.16;
|
||||
let Test.12 = S Test.14;
|
||||
let Test.14 = Z ;
|
||||
let Test.13 = S Test.14;
|
||||
let Test.12 = S Test.13;
|
||||
let Test.2 = S Test.12;
|
||||
let Test.9 = 1i64;
|
||||
let Test.10 = GetTagId Test.2;
|
||||
|
@ -1,11 +1,7 @@
|
||||
procedure Test.0 ():
|
||||
let Test.19 = 0i64;
|
||||
let Test.21 = 0i64;
|
||||
let Test.23 = 0i64;
|
||||
let Test.24 = 1i64;
|
||||
let Test.22 = Z ;
|
||||
let Test.20 = S Test.22;
|
||||
let Test.18 = S Test.20;
|
||||
let Test.20 = Z ;
|
||||
let Test.19 = S Test.20;
|
||||
let Test.18 = S Test.19;
|
||||
let Test.2 = S Test.18;
|
||||
let Test.15 = 0i64;
|
||||
let Test.16 = GetTagId Test.2;
|
||||
|
@ -1,8 +1,8 @@
|
||||
app "effect-example"
|
||||
packages { base: "thing/platform-dir" }
|
||||
imports [base.Task]
|
||||
imports [fx.Effect]
|
||||
provides [ main ] to base
|
||||
|
||||
main : Task.Task {} []
|
||||
main : Effect.Effect {}
|
||||
main =
|
||||
Task.after Task.getLine \lineThisThing -> Task.putLine lineThisThing
|
||||
Effect.after Effect.getLine \lineThisThing -> Effect.putLine lineThisThing
|
||||
|
23
examples/effect/thing/platform-dir/Cargo.lock
generated
23
examples/effect/thing/platform-dir/Cargo.lock
generated
@ -1,23 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "host"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"roc_std 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "roc_std"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)" = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
|
@ -1,14 +0,0 @@
|
||||
[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" }
|
||||
|
||||
[workspace]
|
@ -1,16 +1,16 @@
|
||||
platform folkertdev/foo
|
||||
requires { main : Effect {} }
|
||||
requires {model=>Model, msg=>Msg} {main : Effect {}}
|
||||
exposes []
|
||||
packages {}
|
||||
imports [Task]
|
||||
imports [fx.Effect]
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect
|
||||
{
|
||||
putChar : I64 -> Effect {},
|
||||
putLine : Str -> Effect {},
|
||||
getLine : Effect Str
|
||||
}
|
||||
|
||||
|
||||
mainForHost : Task.Task {} [] as Fx
|
||||
|
||||
mainForHost : Effect.Effect {} as Fx
|
||||
mainForHost = main
|
||||
|
@ -1,31 +0,0 @@
|
||||
interface Task
|
||||
exposes [ Task, after, always, fail, map, putLine, getLine ]
|
||||
imports [ Effect ]
|
||||
|
||||
Task a err : Effect.Effect (Result a err)
|
||||
|
||||
always : a -> Task a *
|
||||
always = \x -> Effect.always (Ok x)
|
||||
|
||||
fail : err -> Task * err
|
||||
fail = \x -> Effect.always (Err x)
|
||||
|
||||
getLine : Task Str *
|
||||
getLine = Effect.after Effect.getLine always
|
||||
|
||||
putLine : Str -> Task {} *
|
||||
putLine = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {})
|
||||
|
||||
map : Task a err, (a -> b) -> Task b err
|
||||
map = \task, transform ->
|
||||
Effect.map task \res ->
|
||||
when res is
|
||||
Ok x -> Ok (transform x)
|
||||
Err e -> Err e
|
||||
|
||||
after : Task a err, (a -> Task b err) -> Task b err
|
||||
after = \task, transform ->
|
||||
Effect.after task \res ->
|
||||
when res is
|
||||
Ok x -> transform x
|
||||
Err e -> Task.fail e
|
@ -1,7 +0,0 @@
|
||||
#include <stdio.h>
|
||||
|
||||
extern int rust_main();
|
||||
|
||||
int main() {
|
||||
return rust_main();
|
||||
}
|
184
examples/effect/thing/platform-dir/host.zig
Normal file
184
examples/effect/thing/platform-dir/host.zig
Normal file
@ -0,0 +1,184 @@
|
||||
const std = @import("std");
|
||||
const str = @import("str");
|
||||
const RocStr = str.RocStr;
|
||||
const testing = std.testing;
|
||||
const expectEqual = testing.expectEqual;
|
||||
const expect = testing.expect;
|
||||
const maxInt = std.math.maxInt;
|
||||
|
||||
comptime {
|
||||
// This is a workaround for https://github.com/ziglang/zig/issues/8218
|
||||
// which is only necessary on macOS.
|
||||
//
|
||||
// Once that issue is fixed, we can undo the changes in
|
||||
// 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing
|
||||
// -fcompiler-rt in link.rs instead of doing this. Note that this
|
||||
// workaround is present in many host.zig files, so make sure to undo
|
||||
// it everywhere!
|
||||
if (std.builtin.os.tag == .macos) {
|
||||
_ = @import("compiler_rt");
|
||||
}
|
||||
}
|
||||
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
extern fn roc__mainForHost_1_exposed([*]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 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;
|
||||
|
||||
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void {
|
||||
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);
|
||||
}
|
||||
|
||||
export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
||||
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||
}
|
||||
|
||||
const Unit = extern struct {};
|
||||
|
||||
pub export fn main() u8 {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
|
||||
const size = @intCast(usize, roc__mainForHost_size());
|
||||
const raw_output = std.heap.c_allocator.alloc(u8, size) catch unreachable;
|
||||
var output = @ptrCast([*]u8, raw_output);
|
||||
|
||||
defer {
|
||||
std.heap.c_allocator.free(raw_output);
|
||||
}
|
||||
|
||||
var ts1: std.os.timespec = undefined;
|
||||
std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts1) catch unreachable;
|
||||
|
||||
roc__mainForHost_1_exposed(output);
|
||||
|
||||
const elements = @ptrCast([*]u64, @alignCast(8, output));
|
||||
|
||||
var flag = elements[0];
|
||||
|
||||
if (flag == 0) {
|
||||
// all is well
|
||||
const closure_data_pointer = @ptrCast([*]u8, output[8..size]);
|
||||
|
||||
call_the_closure(closure_data_pointer);
|
||||
} else {
|
||||
const msg = @intToPtr([*:0]const u8, elements[1]);
|
||||
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
var ts2: std.os.timespec = undefined;
|
||||
std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts2) catch unreachable;
|
||||
|
||||
const delta = to_seconds(ts2) - to_seconds(ts1);
|
||||
|
||||
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn to_seconds(tms: std.os.timespec) f64 {
|
||||
return @intToFloat(f64, tms.tv_sec) + (@intToFloat(f64, tms.tv_nsec) / 1_000_000_000.0);
|
||||
}
|
||||
|
||||
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;
|
||||
var output = @ptrCast([*]u8, raw_output);
|
||||
|
||||
defer {
|
||||
std.heap.c_allocator.free(raw_output);
|
||||
}
|
||||
|
||||
const flags: u8 = 0;
|
||||
|
||||
roc__mainForHost_1_Fx_caller(&flags, closure_data_pointer, output);
|
||||
|
||||
const elements = @ptrCast([*]u64, @alignCast(8, output));
|
||||
|
||||
var flag = elements[0];
|
||||
|
||||
if (flag == 0) {
|
||||
return;
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
pub export fn roc_fx_getLine() str.RocStr {
|
||||
if (roc_fx_getLine_help()) |value| {
|
||||
return value;
|
||||
} else |err| {
|
||||
return str.RocStr.empty();
|
||||
}
|
||||
}
|
||||
|
||||
fn roc_fx_getLine_help() !RocStr {
|
||||
const stdin = std.io.getStdIn().reader();
|
||||
var buf: [400]u8 = undefined;
|
||||
|
||||
const line: []u8 = (try stdin.readUntilDelimiterOrEof(&buf, '\n')) orelse "";
|
||||
|
||||
return str.RocStr.init(@ptrCast([*]const u8, line), line.len);
|
||||
}
|
||||
|
||||
pub export fn roc_fx_putLine(rocPath: str.RocStr) i64 {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
|
||||
for (rocPath.asSlice()) |char| {
|
||||
stdout.print("{c}", .{char}) catch unreachable;
|
||||
}
|
||||
|
||||
stdout.print("\n", .{}) catch unreachable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const GetInt = extern struct {
|
||||
value: i64,
|
||||
error_code: u8,
|
||||
is_error: bool,
|
||||
};
|
||||
|
||||
pub export fn roc_fx_getInt() GetInt {
|
||||
if (roc_fx_getInt_help()) |value| {
|
||||
const get_int = GetInt{ .is_error = false, .value = value, .error_code = 0 };
|
||||
return get_int;
|
||||
} else |err| switch (err) {
|
||||
error.InvalidCharacter => {
|
||||
return GetInt{ .is_error = true, .value = 0, .error_code = 0 };
|
||||
},
|
||||
else => {
|
||||
return GetInt{ .is_error = true, .value = 0, .error_code = 1 };
|
||||
},
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn roc_fx_getInt_help() !i64 {
|
||||
const stdin = std.io.getStdIn().reader();
|
||||
var buf: [40]u8 = undefined;
|
||||
|
||||
const line: []u8 = (try stdin.readUntilDelimiterOrEof(&buf, '\n')) orelse "";
|
||||
|
||||
return std.fmt.parseInt(i64, line, 10);
|
||||
}
|
||||
|
||||
fn readLine() []u8 {
|
||||
const stdin = std.io.getStdIn().reader();
|
||||
return (stdin.readUntilDelimiterOrEof(&line_buf, '\n') catch unreachable) orelse "";
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use roc_std::alloca;
|
||||
use roc_std::RocCallResult;
|
||||
use roc_std::RocStr;
|
||||
use std::alloc::Layout;
|
||||
use std::ffi::c_void;
|
||||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed"]
|
||||
fn roc_main(output: *mut u8) -> ();
|
||||
|
||||
#[link_name = "roc__mainForHost_1_size"]
|
||||
fn roc_main_size() -> i64;
|
||||
|
||||
#[link_name = "roc__mainForHost_1_Fx_caller"]
|
||||
fn call_Fx(
|
||||
flags: &(),
|
||||
function_pointer: *const u8,
|
||||
closure_data: *const u8,
|
||||
output: *mut u8,
|
||||
) -> ();
|
||||
|
||||
#[link_name = "roc__mainForHost_1_Fx_size"]
|
||||
fn size_Fx() -> i64;
|
||||
|
||||
#[link_name = "roc__mainForHost_1_Fx_result_size"]
|
||||
fn size_Fx_result() -> i64;
|
||||
|
||||
fn malloc(size: usize) -> *mut c_void;
|
||||
fn realloc(c_ptr: *mut c_void, size: usize) -> *mut c_void;
|
||||
fn free(c_ptr: *mut c_void);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
||||
return 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 {
|
||||
return realloc(c_ptr, new_size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||
return free(c_ptr);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn roc_fx_putChar(foo: i64) -> () {
|
||||
let character = foo as u8 as char;
|
||||
print!("{}", character);
|
||||
|
||||
()
|
||||
}
|
||||
|
||||
#[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);
|
||||
|
||||
()
|
||||
}
|
||||
|
||||
#[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())
|
||||
}
|
||||
|
||||
unsafe fn call_the_closure(function_pointer: *const u8, closure_data_ptr: *const u8) -> i64 {
|
||||
let size = size_Fx_result() as usize;
|
||||
|
||||
alloca::with_stack_bytes(size, |buffer| {
|
||||
let buffer: *mut std::ffi::c_void = buffer;
|
||||
let buffer: *mut u8 = buffer as *mut u8;
|
||||
|
||||
call_Fx(
|
||||
&(),
|
||||
function_pointer,
|
||||
closure_data_ptr as *const u8,
|
||||
buffer as *mut u8,
|
||||
);
|
||||
|
||||
let output = &*(buffer as *mut RocCallResult<()>);
|
||||
|
||||
match output.into() {
|
||||
Ok(_) => 0,
|
||||
Err(e) => panic!("failed with {}", e),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn rust_main() -> isize {
|
||||
eprintln!("Running Roc closure");
|
||||
let start_time = SystemTime::now();
|
||||
|
||||
let size = unsafe { roc_main_size() } as usize;
|
||||
let layout = Layout::array::<u8>(size).unwrap();
|
||||
let answer = unsafe {
|
||||
let buffer = std::alloc::alloc(layout);
|
||||
|
||||
roc_main(buffer);
|
||||
|
||||
let output = &*(buffer as *mut RocCallResult<()>);
|
||||
|
||||
match output.into() {
|
||||
Ok(()) => {
|
||||
let function_pointer = {
|
||||
// this is a pointer to the location where the function pointer is stored
|
||||
// we pass just the function pointer
|
||||
let temp = buffer.offset(8) as *const i64;
|
||||
|
||||
(*temp) as *const u8
|
||||
};
|
||||
|
||||
let closure_data_ptr = buffer.offset(16);
|
||||
|
||||
let result =
|
||||
call_the_closure(function_pointer as *const u8, 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
let end_time = SystemTime::now();
|
||||
let duration = end_time.duration_since(start_time).unwrap();
|
||||
|
||||
eprintln!(
|
||||
"Roc closure took {:.4} ms to compute this answer: {:?}",
|
||||
duration.as_secs_f64() * 1000.0,
|
||||
// truncate the answer, so stdout is not swamped
|
||||
answer
|
||||
);
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
Loading…
Reference in New Issue
Block a user