Merge branch 'trunk' of github.com:rtfeldman/roc into list-str-capacity

This commit is contained in:
Brian Carroll 2022-03-11 19:28:13 +00:00
commit 456d8ff9cb
41 changed files with 790 additions and 210 deletions

6
Cargo.lock generated
View file

@ -1770,13 +1770,13 @@ dependencies = [
name = "inkwell" name = "inkwell"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"inkwell 0.1.0 (git+https://github.com/rtfeldman/inkwell?tag=llvm13-0.release1)", "inkwell 0.1.0 (git+https://github.com/rtfeldman/inkwell?branch=master)",
] ]
[[package]] [[package]]
name = "inkwell" name = "inkwell"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/rtfeldman/inkwell?tag=llvm13-0.release1#e15d665227b2acad4ca949820d80048e09f3f4e5" source = "git+https://github.com/rtfeldman/inkwell?branch=master#b3fb82c653ffe754e078ac778d7fd9a619a26c0c"
dependencies = [ dependencies = [
"either", "either",
"inkwell_internals", "inkwell_internals",
@ -1789,7 +1789,7 @@ dependencies = [
[[package]] [[package]]
name = "inkwell_internals" name = "inkwell_internals"
version = "0.5.0" version = "0.5.0"
source = "git+https://github.com/rtfeldman/inkwell?tag=llvm13-0.release1#e15d665227b2acad4ca949820d80048e09f3f4e5" source = "git+https://github.com/rtfeldman/inkwell?branch=master#b3fb82c653ffe754e078ac778d7fd9a619a26c0c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View file

@ -1511,6 +1511,16 @@ fn expr_spec<'a>(
builder.add_make_named(block, MOD_APP, type_name, tag_value_id) builder.add_make_named(block, MOD_APP, type_name, tag_value_id)
} }
ExprBox { symbol } => {
let value_id = env.symbols[symbol];
with_new_heap_cell(builder, block, value_id)
}
ExprUnbox { symbol } => {
let tuple_id = env.symbols[symbol];
builder.add_get_tuple_field(block, tuple_id, BOX_VALUE_INDEX)
}
Struct(fields) => build_tuple_value(builder, env, block, fields), Struct(fields) => build_tuple_value(builder, env, block, fields),
UnionAtIndex { UnionAtIndex {
index, index,
@ -1705,6 +1715,13 @@ fn layout_spec_help(
} }
} }
} }
Boxed(inner_layout) => {
let inner_type = layout_spec_help(builder, inner_layout, when_recursive)?;
let cell_type = builder.add_heap_cell_type();
builder.add_tuple_type(&[cell_type, inner_type])
}
RecursivePointer => match when_recursive { RecursivePointer => match when_recursive {
WhenRecursive::Unreachable => { WhenRecursive::Unreachable => {
unreachable!() unreachable!()
@ -1787,6 +1804,10 @@ const LIST_BAG_INDEX: u32 = 1;
const DICT_CELL_INDEX: u32 = LIST_CELL_INDEX; const DICT_CELL_INDEX: u32 = LIST_CELL_INDEX;
const DICT_BAG_INDEX: u32 = LIST_BAG_INDEX; const DICT_BAG_INDEX: u32 = LIST_BAG_INDEX;
#[allow(dead_code)]
const BOX_CELL_INDEX: u32 = LIST_CELL_INDEX;
const BOX_VALUE_INDEX: u32 = LIST_BAG_INDEX;
const TAG_CELL_INDEX: u32 = 0; const TAG_CELL_INDEX: u32 = 0;
const TAG_DATA_INDEX: u32 = 1; const TAG_DATA_INDEX: u32 = 1;

View file

@ -890,14 +890,23 @@ pub fn listSublist(
} }
const keep_len = std.math.min(len, size - start); const keep_len = std.math.min(len, size - start);
const drop_len = std.math.max(start, 0); const drop_start_len = start;
const drop_end_len = size - (start + keep_len);
// Decrement the reference counts of elements before `start`.
var i: usize = 0; var i: usize = 0;
while (i < drop_len) : (i += 1) { while (i < drop_start_len) : (i += 1) {
const element = source_ptr + i * element_width; const element = source_ptr + i * element_width;
dec(element); dec(element);
} }
// Decrement the reference counts of elements after `start + keep_len`.
i = 0;
while (i < drop_end_len) : (i += 1) {
const element = source_ptr + (start + keep_len + i) * element_width;
dec(element);
}
const output = RocList.allocate(alignment, keep_len, element_width); const output = RocList.allocate(alignment, keep_len, element_width);
const target_ptr = output.bytes orelse unreachable; const target_ptr = output.bytes orelse unreachable;

View file

@ -3,10 +3,10 @@ use roc_module::ident::TagName;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::Region; use roc_region::all::Region;
use roc_types::builtin_aliases::{ use roc_types::builtin_aliases::{
bool_type, dec_type, dict_type, f32_type, f64_type, float_type, i128_type, i16_type, i32_type, bool_type, box_type, dec_type, dict_type, f32_type, f64_type, float_type, i128_type, i16_type,
i64_type, i8_type, int_type, list_type, nat_type, num_type, ordering_type, result_type, i32_type, i64_type, i8_type, int_type, list_type, nat_type, num_type, ordering_type,
set_type, str_type, str_utf8_byte_problem_type, u128_type, u16_type, u32_type, u64_type, result_type, set_type, str_type, str_utf8_byte_problem_type, u128_type, u16_type, u32_type,
u8_type, u64_type, u8_type,
}; };
use roc_types::solved_types::SolvedType; use roc_types::solved_types::SolvedType;
use roc_types::subs::VarId; use roc_types::subs::VarId;
@ -1782,6 +1782,20 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
Box::new(bool_type()), Box::new(bool_type()),
); );
// Box.box : a -> Box a
add_top_level_function_type!(
Symbol::BOX_BOX_FUNCTION,
vec![flex(TVAR1)],
Box::new(box_type(flex(TVAR1))),
);
// Box.unbox : Box a -> a
add_top_level_function_type!(
Symbol::BOX_UNBOX,
vec![box_type(flex(TVAR1))],
Box::new(flex(TVAR1)),
);
types types
} }

View file

@ -271,6 +271,8 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
RESULT_WITH_DEFAULT => result_with_default, RESULT_WITH_DEFAULT => result_with_default,
RESULT_IS_OK => result_is_ok, RESULT_IS_OK => result_is_ok,
RESULT_IS_ERR => result_is_err, RESULT_IS_ERR => result_is_err,
BOX_BOX_FUNCTION => box_box,
BOX_UNBOX => box_unbox,
} }
} }
@ -5326,6 +5328,16 @@ fn num_bytes_to(symbol: Symbol, var_store: &mut VarStore, offset: i64, low_level
) )
} }
/// Box.box : a -> Box a
fn box_box(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_1(symbol, LowLevel::BoxExpr, var_store)
}
/// Box.unbox : Box a -> a
fn box_unbox(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_1(symbol, LowLevel::UnboxExpr, var_store)
}
#[inline(always)] #[inline(always)]
fn defn_help( fn defn_help(
fn_name: Symbol, fn_name: Symbol,

View file

@ -117,6 +117,7 @@ impl<'a> Formattable for Expr<'a> {
) { ) {
use self::Expr::*; use self::Expr::*;
//dbg!(self);
let format_newlines = newlines == Newlines::Yes; let format_newlines = newlines == Newlines::Yes;
let apply_needs_parens = parens == Parens::InApply; let apply_needs_parens = parens == Parens::InApply;
@ -453,14 +454,17 @@ fn fmt_bin_ops<'a, 'buf>(
|| (&loc_right_side.value).is_multiline() || (&loc_right_side.value).is_multiline()
|| lefts.iter().any(|(expr, _)| expr.value.is_multiline()); || lefts.iter().any(|(expr, _)| expr.value.is_multiline());
let mut curr_indent = indent;
for (loc_left_side, loc_bin_op) in lefts { for (loc_left_side, loc_bin_op) in lefts {
let bin_op = loc_bin_op.value; let bin_op = loc_bin_op.value;
loc_left_side.format_with_options(buf, apply_needs_parens, Newlines::No, indent); loc_left_side.format_with_options(buf, apply_needs_parens, Newlines::No, curr_indent);
if is_multiline { if is_multiline {
buf.newline(); buf.newline();
buf.indent(indent + INDENT); curr_indent = indent + INDENT;
buf.indent(curr_indent);
} else { } else {
buf.spaces(1); buf.spaces(1);
} }

View file

@ -2595,7 +2595,7 @@ mod test_fmt {
} }
#[test] #[test]
fn pipline_apply_lambda() { fn pipline_apply_lambda_1() {
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
shout shout
@ -2606,6 +2606,19 @@ mod test_fmt {
)); ));
} }
#[test]
fn pipline_apply_lambda_2() {
expr_formats_same(indoc!(
r#"
shout
|> List.map
xs
(\i -> i)
|> List.join
"#
));
}
// MODULES // MODULES
#[test] #[test]

View file

@ -875,6 +875,12 @@ trait Backend<'a> {
self.set_last_seen(*sym, stmt); self.set_last_seen(*sym, stmt);
} }
} }
Expr::ExprBox { symbol } => {
self.set_last_seen(*symbol, stmt);
}
Expr::ExprUnbox { symbol } => {
self.set_last_seen(*symbol, stmt);
}
Expr::Struct(syms) => { Expr::Struct(syms) => {
for sym in *syms { for sym in *syms {
self.set_last_seen(*sym, stmt); self.set_last_seen(*sym, stmt);

View file

@ -260,8 +260,12 @@ fn build_has_tag_id_help<'a, 'ctx, 'env>(
tag_value.into(), tag_value.into(),
); );
env.builder env.builder.build_int_cast_sign_flag(
.build_int_cast(tag_id_i64, env.context.i16_type(), "to_i16") tag_id_i64,
env.context.i16_type(),
true,
"to_i16",
)
}; };
let answer = env.builder.build_int_compare( let answer = env.builder.build_int_compare(

View file

@ -24,7 +24,7 @@ use crate::llvm::build_str::{
}; };
use crate::llvm::compare::{generic_eq, generic_neq}; use crate::llvm::compare::{generic_eq, generic_neq};
use crate::llvm::convert::{ use crate::llvm::convert::{
self, basic_type_from_builtin, basic_type_from_layout, basic_type_from_layout_1, self, argument_type_from_layout, basic_type_from_builtin, basic_type_from_layout,
block_of_memory_slices, zig_str_type, block_of_memory_slices, zig_str_type,
}; };
use crate::llvm::refcounting::{ use crate::llvm::refcounting::{
@ -1100,6 +1100,30 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
.. ..
} => build_tag(env, scope, union_layout, *tag_id, arguments, None, parent), } => build_tag(env, scope, union_layout, *tag_id, arguments, None, parent),
ExprBox { symbol } => {
let (value, layout) = load_symbol_and_layout(scope, symbol);
let basic_type = basic_type_from_layout(env, layout);
let allocation = reserve_with_refcount_help(
env,
basic_type,
layout.stack_size(env.target_info),
layout.alignment_bytes(env.target_info),
);
env.builder.build_store(allocation, value);
allocation.into()
}
ExprUnbox { symbol } => {
let value = load_symbol(scope, symbol);
debug_assert!(value.is_pointer_value());
env.builder
.build_load(value.into_pointer_value(), "load_boxed_value")
}
Reset { symbol, .. } => { Reset { symbol, .. } => {
let (tag_ptr, layout) = load_symbol_and_layout(scope, symbol); let (tag_ptr, layout) = load_symbol_and_layout(scope, symbol);
let tag_ptr = tag_ptr.into_pointer_value(); let tag_ptr = tag_ptr.into_pointer_value();
@ -1798,7 +1822,7 @@ pub fn tag_pointer_read_tag_id<'a, 'ctx, 'env>(
let masked = env.builder.build_and(as_int, mask_intval, "mask"); let masked = env.builder.build_and(as_int, mask_intval, "mask");
env.builder env.builder
.build_int_cast(masked, env.context.i8_type(), "to_u8") .build_int_cast_sign_flag(masked, env.context.i8_type(), false, "to_u8")
} }
pub fn tag_pointer_clear_tag_id<'a, 'ctx, 'env>( pub fn tag_pointer_clear_tag_id<'a, 'ctx, 'env>(
@ -2553,6 +2577,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
let align_bytes = layout.alignment_bytes(env.target_info); let align_bytes = layout.alignment_bytes(env.target_info);
if align_bytes > 0 { if align_bytes > 0 {
debug_assert!(value.is_pointer_value(), "{:?}\n{:?}", value, layout);
let value_ptr = value.into_pointer_value(); let value_ptr = value.into_pointer_value();
// We can only do this if the function itself writes data into this // We can only do this if the function itself writes data into this
@ -4197,7 +4222,7 @@ fn build_proc_header<'a, 'ctx, 'env>(
let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena); let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena);
for (layout, _) in args.iter() { for (layout, _) in args.iter() {
let arg_type = basic_type_from_layout_1(env, layout); let arg_type = argument_type_from_layout(env, layout);
arg_basic_types.push(arg_type); arg_basic_types.push(arg_type);
} }
@ -5864,8 +5889,11 @@ fn run_low_level<'a, 'ctx, 'env>(
let arg = load_symbol(scope, &args[0]).into_int_value(); let arg = load_symbol(scope, &args[0]).into_int_value();
let to = basic_type_from_layout(env, layout).into_int_type(); let to = basic_type_from_layout(env, layout).into_int_type();
let to_signed = intwidth_from_layout(*layout).is_signed();
env.builder.build_int_cast(arg, to, "inc_cast").into() env.builder
.build_int_cast_sign_flag(arg, to, to_signed, "inc_cast")
.into()
} }
Eq => { Eq => {
debug_assert_eq!(args.len(), 2); debug_assert_eq!(args.len(), 2);
@ -6091,6 +6119,10 @@ fn run_low_level<'a, 'ctx, 'env>(
unreachable!("these are higher order, and are handled elsewhere") unreachable!("these are higher order, and are handled elsewhere")
} }
BoxExpr | UnboxExpr => {
unreachable!("The {:?} operation is turned into mono Expr", op)
}
PtrCast | RefCountInc | RefCountDec => { PtrCast | RefCountInc | RefCountDec => {
unreachable!("Not used in LLVM backend: {:?}", op); unreachable!("Not used in LLVM backend: {:?}", op);
} }
@ -6969,7 +7001,12 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
let target_int_type = convert::int_type_from_int_width(env, target_int_width); let target_int_type = convert::int_type_from_int_width(env, target_int_width);
let target_int_val: BasicValueEnum<'ctx> = env let target_int_val: BasicValueEnum<'ctx> = env
.builder .builder
.build_int_cast(arg, target_int_type, "int_cast") .build_int_cast_sign_flag(
arg,
target_int_type,
target_int_width.is_signed(),
"int_cast",
)
.into(); .into();
let return_type = let return_type =

View file

@ -67,7 +67,12 @@ pub fn dict_len<'a, 'ctx, 'env>(
); );
env.builder env.builder
.build_int_cast(length_i64.into_int_value(), env.ptr_int(), "to_usize") .build_int_cast_sign_flag(
length_i64.into_int_value(),
env.ptr_int(),
false,
"to_usize",
)
.into() .into()
} }
_ => unreachable!("Invalid layout given to Dict.len : {:?}", dict_layout), _ => unreachable!("Invalid layout given to Dict.len : {:?}", dict_layout),

View file

@ -4,7 +4,7 @@ use crate::llvm::build::tag_pointer_clear_tag_id;
use crate::llvm::build::Env; use crate::llvm::build::Env;
use crate::llvm::build::{get_tag_id, FAST_CALL_CONV, TAG_DATA_INDEX}; use crate::llvm::build::{get_tag_id, FAST_CALL_CONV, TAG_DATA_INDEX};
use crate::llvm::build_str; use crate::llvm::build_str;
use crate::llvm::convert::{basic_type_from_layout, basic_type_from_layout_1}; use crate::llvm::convert::basic_type_from_layout;
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use inkwell::values::{ use inkwell::values::{
BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue, BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue,
@ -13,6 +13,8 @@ use roc_builtins::bitcode;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout}; use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
use super::convert::argument_type_from_union_layout;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum WhenRecursive<'a> { enum WhenRecursive<'a> {
Unreachable, Unreachable,
@ -68,8 +70,11 @@ fn build_hash_layout<'a, 'ctx, 'env>(
when_recursive, when_recursive,
), ),
Layout::Union(union_layout) => { Layout::Union(union_layout) => build_hash_tag(env, layout_ids, union_layout, seed, val),
build_hash_tag(env, layout_ids, layout, union_layout, seed, val)
Layout::Boxed(_inner_layout) => {
// build_hash_box(env, layout_ids, layout, inner_layout, seed, val)
todo!()
} }
Layout::RecursivePointer => match when_recursive { Layout::RecursivePointer => match when_recursive {
@ -87,14 +92,7 @@ fn build_hash_layout<'a, 'ctx, 'env>(
.build_bitcast(val, bt, "i64_to_opaque") .build_bitcast(val, bt, "i64_to_opaque")
.into_pointer_value(); .into_pointer_value();
build_hash_tag( build_hash_tag(env, layout_ids, &union_layout, seed, field_cast.into())
env,
layout_ids,
&layout,
&union_layout,
seed,
field_cast.into(),
)
} }
}, },
} }
@ -308,7 +306,6 @@ fn hash_struct<'a, 'ctx, 'env>(
fn build_hash_tag<'a, 'ctx, 'env>( fn build_hash_tag<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>, layout_ids: &mut LayoutIds<'a>,
layout: &Layout<'a>,
union_layout: &UnionLayout<'a>, union_layout: &UnionLayout<'a>,
seed: IntValue<'ctx>, seed: IntValue<'ctx>,
value: BasicValueEnum<'ctx>, value: BasicValueEnum<'ctx>,
@ -318,7 +315,7 @@ fn build_hash_tag<'a, 'ctx, 'env>(
let symbol = Symbol::GENERIC_HASH; let symbol = Symbol::GENERIC_HASH;
let fn_name = layout_ids let fn_name = layout_ids
.get(symbol, layout) .get(symbol, &Layout::Union(*union_layout))
.to_symbol_string(symbol, &env.interns); .to_symbol_string(symbol, &env.interns);
let function = match env.module.get_function(fn_name.as_str()) { let function = match env.module.get_function(fn_name.as_str()) {
@ -326,7 +323,7 @@ fn build_hash_tag<'a, 'ctx, 'env>(
None => { None => {
let seed_type = env.context.i64_type(); let seed_type = env.context.i64_type();
let arg_type = basic_type_from_layout_1(env, layout); let arg_type = argument_type_from_union_layout(env, union_layout);
let function_value = crate::llvm::refcounting::build_header_help( let function_value = crate::llvm::refcounting::build_header_help(
env, env,
@ -805,7 +802,7 @@ fn hash_ptr_to_struct<'a, 'ctx, 'env>(
) -> IntValue<'ctx> { ) -> IntValue<'ctx> {
use inkwell::types::BasicType; use inkwell::types::BasicType;
let wrapper_type = basic_type_from_layout_1(env, &Layout::Union(*union_layout)); let wrapper_type = argument_type_from_union_layout(env, union_layout);
// cast the opaque pointer to a pointer of the correct shape // cast the opaque pointer to a pointer of the correct shape
let wrapper_ptr = env let wrapper_ptr = env

View file

@ -82,7 +82,7 @@ fn pass_element_as_opaque<'a, 'ctx, 'env>(
env.builder.build_bitcast( env.builder.build_bitcast(
element_ptr, element_ptr,
env.context.i8_type().ptr_type(AddressSpace::Generic), env.context.i8_type().ptr_type(AddressSpace::Generic),
"to_opaque", "pass_element_as_opaque",
) )
} }
@ -102,7 +102,7 @@ pub fn pass_as_opaque<'a, 'ctx, 'env>(
env.builder.build_bitcast( env.builder.build_bitcast(
ptr, ptr,
env.context.i8_type().ptr_type(AddressSpace::Generic), env.context.i8_type().ptr_type(AddressSpace::Generic),
"to_opaque", "pass_as_opaque",
) )
} }
@ -434,10 +434,19 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
ListWalk::WalkBackwardsUntil => todo!(), ListWalk::WalkBackwardsUntil => todo!(),
}; };
let default_ptr = builder.build_alloca(default.get_type(), "default_ptr"); let default_ptr = if default_layout.is_passed_by_reference() {
env.builder.build_store(default_ptr, default); debug_assert!(default.is_pointer_value());
default.into_pointer_value()
} else {
let default_ptr = builder.build_alloca(default.get_type(), "default_ptr");
env.builder.build_store(default_ptr, default);
default_ptr
};
let result_ptr = env.builder.build_alloca(default.get_type(), "result"); let result_ptr = {
let basic_type = basic_type_from_layout(env, default_layout);
env.builder.build_alloca(basic_type, "result")
};
match variant { match variant {
ListWalk::Walk | ListWalk::WalkBackwards => { ListWalk::Walk | ListWalk::WalkBackwards => {
@ -494,7 +503,11 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
} }
} }
env.builder.build_load(result_ptr, "load_result") if default_layout.is_passed_by_reference() {
result_ptr.into()
} else {
env.builder.build_load(result_ptr, "load_result")
}
} }
/// List.range : Int a, Int a -> List (Int a) /// List.range : Int a, Int a -> List (Int a)

View file

@ -170,7 +170,7 @@ pub fn str_number_of_bytes<'a, 'ctx, 'env>(
// cast to the appropriate usize of the current build // cast to the appropriate usize of the current build
env.builder env.builder
.build_int_cast(length, env.ptr_int(), "len_as_usize") .build_int_cast_sign_flag(length, env.ptr_int(), false, "len_as_usize")
} }
/// Str.startsWith : Str, Str -> Bool /// Str.startsWith : Str, Str -> Bool

View file

@ -2,7 +2,7 @@ use crate::llvm::bitcode::call_bitcode_fn;
use crate::llvm::build::{get_tag_id, tag_pointer_clear_tag_id, Env, FAST_CALL_CONV}; use crate::llvm::build::{get_tag_id, tag_pointer_clear_tag_id, Env, FAST_CALL_CONV};
use crate::llvm::build_list::{list_len, load_list_ptr}; use crate::llvm::build_list::{list_len, load_list_ptr};
use crate::llvm::build_str::str_equal; use crate::llvm::build_str::str_equal;
use crate::llvm::convert::{basic_type_from_layout, basic_type_from_layout_1}; use crate::llvm::convert::basic_type_from_layout;
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use inkwell::types::BasicType; use inkwell::types::BasicType;
use inkwell::values::{ use inkwell::values::{
@ -15,6 +15,7 @@ use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout}; use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
use super::build::load_roc_value; use super::build::load_roc_value;
use super::convert::argument_type_from_union_layout;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum WhenRecursive<'a> { enum WhenRecursive<'a> {
@ -176,12 +177,21 @@ fn build_eq<'a, 'ctx, 'env>(
env, env,
layout_ids, layout_ids,
when_recursive, when_recursive,
lhs_layout,
union_layout, union_layout,
lhs_val, lhs_val,
rhs_val, rhs_val,
), ),
Layout::Boxed(inner_layout) => build_box_eq(
env,
layout_ids,
when_recursive,
lhs_layout,
inner_layout,
lhs_val,
rhs_val,
),
Layout::RecursivePointer => match when_recursive { Layout::RecursivePointer => match when_recursive {
WhenRecursive::Unreachable => { WhenRecursive::Unreachable => {
unreachable!("recursion pointers should never be compared directly") unreachable!("recursion pointers should never be compared directly")
@ -207,7 +217,6 @@ fn build_eq<'a, 'ctx, 'env>(
env, env,
layout_ids, layout_ids,
WhenRecursive::Loop(union_layout), WhenRecursive::Loop(union_layout),
&layout,
&union_layout, &union_layout,
field1_cast.into(), field1_cast.into(),
field2_cast.into(), field2_cast.into(),
@ -345,12 +354,12 @@ fn build_neq<'a, 'ctx, 'env>(
result.into() result.into()
} }
Layout::Union(union_layout) => { Layout::Union(union_layout) => {
let is_equal = build_tag_eq( let is_equal = build_tag_eq(
env, env,
layout_ids, layout_ids,
when_recursive, when_recursive,
lhs_layout,
union_layout, union_layout,
lhs_val, lhs_val,
rhs_val, rhs_val,
@ -362,6 +371,23 @@ fn build_neq<'a, 'ctx, 'env>(
result.into() result.into()
} }
Layout::Boxed(inner_layout) => {
let is_equal = build_box_eq(
env,
layout_ids,
when_recursive,
lhs_layout,
inner_layout,
lhs_val,
rhs_val,
)
.into_int_value();
let result: IntValue = env.builder.build_not(is_equal, "negate");
result.into()
}
Layout::RecursivePointer => { Layout::RecursivePointer => {
unreachable!("recursion pointers should never be compared directly") unreachable!("recursion pointers should never be compared directly")
} }
@ -764,7 +790,6 @@ fn build_tag_eq<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>, layout_ids: &mut LayoutIds<'a>,
when_recursive: WhenRecursive<'a>, when_recursive: WhenRecursive<'a>,
tag_layout: &Layout<'a>,
union_layout: &UnionLayout<'a>, union_layout: &UnionLayout<'a>,
tag1: BasicValueEnum<'ctx>, tag1: BasicValueEnum<'ctx>,
tag2: BasicValueEnum<'ctx>, tag2: BasicValueEnum<'ctx>,
@ -772,15 +797,16 @@ fn build_tag_eq<'a, 'ctx, 'env>(
let block = env.builder.get_insert_block().expect("to be in a function"); let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap(); let di_location = env.builder.get_current_debug_location().unwrap();
let tag_layout = Layout::Union(*union_layout);
let symbol = Symbol::GENERIC_EQ; let symbol = Symbol::GENERIC_EQ;
let fn_name = layout_ids let fn_name = layout_ids
.get(symbol, tag_layout) .get(symbol, &tag_layout)
.to_symbol_string(symbol, &env.interns); .to_symbol_string(symbol, &env.interns);
let function = match env.module.get_function(fn_name.as_str()) { let function = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value, Some(function_value) => function_value,
None => { None => {
let arg_type = basic_type_from_layout_1(env, tag_layout); let arg_type = argument_type_from_union_layout(env, union_layout);
let function_value = crate::llvm::refcounting::build_header_help( let function_value = crate::llvm::refcounting::build_header_help(
env, env,
@ -1101,8 +1127,10 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
let i8_type = env.context.i8_type(); let i8_type = env.context.i8_type();
let sum = env.builder.build_int_add( let sum = env.builder.build_int_add(
env.builder.build_int_cast(is_null_1, i8_type, "to_u8"), env.builder
env.builder.build_int_cast(is_null_2, i8_type, "to_u8"), .build_int_cast_sign_flag(is_null_1, i8_type, false, "to_u8"),
env.builder
.build_int_cast_sign_flag(is_null_2, i8_type, false, "to_u8"),
"sum_is_null", "sum_is_null",
); );
@ -1252,3 +1280,141 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>(
) )
.into_int_value() .into_int_value()
} }
/// ----
fn build_box_eq<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
when_recursive: WhenRecursive<'a>,
box_layout: &Layout<'a>,
inner_layout: &Layout<'a>,
tag1: BasicValueEnum<'ctx>,
tag2: BasicValueEnum<'ctx>,
) -> BasicValueEnum<'ctx> {
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let symbol = Symbol::GENERIC_EQ;
let fn_name = layout_ids
.get(symbol, box_layout)
.to_symbol_string(symbol, &env.interns);
let function = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value,
None => {
let arg_type = basic_type_from_layout(env, box_layout);
let function_value = crate::llvm::refcounting::build_header_help(
env,
&fn_name,
env.context.bool_type().into(),
&[arg_type, arg_type],
);
build_box_eq_help(
env,
layout_ids,
when_recursive,
function_value,
inner_layout,
);
function_value
}
};
env.builder.position_at_end(block);
env.builder
.set_current_debug_location(env.context, di_location);
let call = env
.builder
.build_call(function, &[tag1.into(), tag2.into()], "tag_eq");
call.set_call_convention(FAST_CALL_CONV);
call.try_as_basic_value().left().unwrap()
}
fn build_box_eq_help<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
when_recursive: WhenRecursive<'a>,
parent: FunctionValue<'ctx>,
inner_layout: &Layout<'a>,
) {
let ctx = env.context;
let builder = env.builder;
{
use inkwell::debug_info::AsDIScope;
let func_scope = parent.get_subprogram().unwrap();
let lexical_block = env.dibuilder.create_lexical_block(
/* scope */ func_scope.as_debug_info_scope(),
/* file */ env.compile_unit.get_file(),
/* line_no */ 0,
/* column_no */ 0,
);
let loc = env.dibuilder.create_debug_location(
ctx,
/* line */ 0,
/* column */ 0,
/* current_scope */ lexical_block.as_debug_info_scope(),
/* inlined_at */ None,
);
builder.set_current_debug_location(ctx, loc);
}
// Add args to scope
let mut it = parent.get_param_iter();
let box1 = it.next().unwrap();
let box2 = it.next().unwrap();
box1.set_name(Symbol::ARG_1.as_str(&env.interns));
box2.set_name(Symbol::ARG_2.as_str(&env.interns));
let return_true = ctx.append_basic_block(parent, "return_true");
env.builder.position_at_end(return_true);
env.builder
.build_return(Some(&env.context.bool_type().const_all_ones()));
let entry = ctx.append_basic_block(parent, "entry");
env.builder.position_at_end(entry);
let ptr_equal = env.builder.build_int_compare(
IntPredicate::EQ,
env.builder
.build_ptr_to_int(box1.into_pointer_value(), env.ptr_int(), "pti"),
env.builder
.build_ptr_to_int(box2.into_pointer_value(), env.ptr_int(), "pti"),
"compare_pointers",
);
let compare_inner_value = ctx.append_basic_block(parent, "compare_inner_value");
env.builder
.build_conditional_branch(ptr_equal, return_true, compare_inner_value);
env.builder.position_at_end(compare_inner_value);
// clear the tag_id so we get a pointer to the actual data
let box1 = box1.into_pointer_value();
let box2 = box2.into_pointer_value();
let value1 = env.builder.build_load(box1, "load_box1");
let value2 = env.builder.build_load(box2, "load_box2");
let is_equal = build_eq(
env,
layout_ids,
value1,
value2,
inner_layout,
inner_layout,
when_recursive,
);
env.builder.build_return(Some(&is_equal));
}

View file

@ -1,3 +1,4 @@
use crate::llvm::build::Env;
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use inkwell::context::Context; use inkwell::context::Context;
use inkwell::types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType}; use inkwell::types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType};
@ -7,7 +8,7 @@ use roc_mono::layout::{Builtin, Layout, UnionLayout};
use roc_target::TargetInfo; use roc_target::TargetInfo;
fn basic_type_from_record<'a, 'ctx, 'env>( fn basic_type_from_record<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
fields: &[Layout<'_>], fields: &[Layout<'_>],
) -> BasicTypeEnum<'ctx> { ) -> BasicTypeEnum<'ctx> {
let mut field_types = Vec::with_capacity_in(fields.len(), env.arena); let mut field_types = Vec::with_capacity_in(fields.len(), env.arena);
@ -22,7 +23,7 @@ fn basic_type_from_record<'a, 'ctx, 'env>(
} }
pub fn basic_type_from_layout<'a, 'ctx, 'env>( pub fn basic_type_from_layout<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
layout: &Layout<'_>, layout: &Layout<'_>,
) -> BasicTypeEnum<'ctx> { ) -> BasicTypeEnum<'ctx> {
use Layout::*; use Layout::*;
@ -33,121 +34,64 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
.. ..
} => basic_type_from_record(env, sorted_fields), } => basic_type_from_record(env, sorted_fields),
LambdaSet(lambda_set) => basic_type_from_layout(env, &lambda_set.runtime_representation()), LambdaSet(lambda_set) => basic_type_from_layout(env, &lambda_set.runtime_representation()),
Union(union_layout) => { Boxed(inner_layout) => {
use UnionLayout::*; let inner_type = basic_type_from_layout(env, inner_layout);
let tag_id_type = basic_type_from_layout(env, &union_layout.tag_id_layout()); inner_type.ptr_type(AddressSpace::Generic).into()
match union_layout {
NonRecursive(tags) => {
let data = block_of_memory_slices(env.context, tags, env.target_info);
env.context.struct_type(&[data, tag_id_type], false).into()
}
Recursive(tags)
| NullableWrapped {
other_tags: tags, ..
} => {
let data = block_of_memory_slices(env.context, tags, env.target_info);
if union_layout.stores_tag_id_as_data(env.target_info) {
env.context
.struct_type(&[data, tag_id_type], false)
.ptr_type(AddressSpace::Generic)
.into()
} else {
data.ptr_type(AddressSpace::Generic).into()
}
}
NullableUnwrapped { other_fields, .. } => {
let block =
block_of_memory_slices(env.context, &[other_fields], env.target_info);
block.ptr_type(AddressSpace::Generic).into()
}
NonNullableUnwrapped(fields) => {
let block = block_of_memory_slices(env.context, &[fields], env.target_info);
block.ptr_type(AddressSpace::Generic).into()
}
}
}
RecursivePointer => {
// TODO make this dynamic
env.context
.i64_type()
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum()
} }
Union(union_layout) => basic_type_from_union_layout(env, union_layout),
RecursivePointer => env
.context
.i64_type()
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum(),
Builtin(builtin) => basic_type_from_builtin(env, builtin), Builtin(builtin) => basic_type_from_builtin(env, builtin),
} }
} }
pub fn basic_type_from_layout_1<'a, 'ctx, 'env>( pub fn basic_type_from_union_layout<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
layout: &Layout<'_>, union_layout: &UnionLayout<'_>,
) -> BasicTypeEnum<'ctx> { ) -> BasicTypeEnum<'ctx> {
use Layout::*; use UnionLayout::*;
match layout { let tag_id_type = basic_type_from_layout(env, &union_layout.tag_id_layout());
Struct {
field_layouts: sorted_fields, match union_layout {
.. NonRecursive(tags) => {
} => basic_type_from_record(env, sorted_fields), let data = block_of_memory_slices(env.context, tags, env.target_info);
LambdaSet(lambda_set) => {
basic_type_from_layout_1(env, &lambda_set.runtime_representation()) env.context.struct_type(&[data, tag_id_type], false).into()
} }
Union(union_layout) => { Recursive(tags)
use UnionLayout::*; | NullableWrapped {
other_tags: tags, ..
} => {
let data = block_of_memory_slices(env.context, tags, env.target_info);
let tag_id_type = basic_type_from_layout(env, &union_layout.tag_id_layout()); if union_layout.stores_tag_id_as_data(env.target_info) {
env.context
match union_layout { .struct_type(&[data, tag_id_type], false)
NonRecursive(tags) => { .ptr_type(AddressSpace::Generic)
let data = block_of_memory_slices(env.context, tags, env.target_info); .into()
let struct_type = env.context.struct_type(&[data, tag_id_type], false); } else {
data.ptr_type(AddressSpace::Generic).into()
struct_type.ptr_type(AddressSpace::Generic).into()
}
Recursive(tags)
| NullableWrapped {
other_tags: tags, ..
} => {
let data = block_of_memory_slices(env.context, tags, env.target_info);
if union_layout.stores_tag_id_as_data(env.target_info) {
env.context
.struct_type(&[data, tag_id_type], false)
.ptr_type(AddressSpace::Generic)
.into()
} else {
data.ptr_type(AddressSpace::Generic).into()
}
}
NullableUnwrapped { other_fields, .. } => {
let block =
block_of_memory_slices(env.context, &[other_fields], env.target_info);
block.ptr_type(AddressSpace::Generic).into()
}
NonNullableUnwrapped(fields) => {
let block = block_of_memory_slices(env.context, &[fields], env.target_info);
block.ptr_type(AddressSpace::Generic).into()
}
} }
} }
RecursivePointer => { NullableUnwrapped { other_fields, .. } => {
// TODO make this dynamic let block = block_of_memory_slices(env.context, &[other_fields], env.target_info);
env.context block.ptr_type(AddressSpace::Generic).into()
.i64_type() }
.ptr_type(AddressSpace::Generic) NonNullableUnwrapped(fields) => {
.as_basic_type_enum() let block = block_of_memory_slices(env.context, &[fields], env.target_info);
block.ptr_type(AddressSpace::Generic).into()
} }
Builtin(builtin) => basic_type_from_builtin(env, builtin),
} }
} }
pub fn basic_type_from_builtin<'a, 'ctx, 'env>( pub fn basic_type_from_builtin<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
builtin: &Builtin<'_>, builtin: &Builtin<'_>,
) -> BasicTypeEnum<'ctx> { ) -> BasicTypeEnum<'ctx> {
use Builtin::*; use Builtin::*;
@ -166,8 +110,48 @@ pub fn basic_type_from_builtin<'a, 'ctx, 'env>(
} }
} }
/// Turn a layout into a BasicType that we use in LLVM function arguments.
///
/// This makes it possible to pass values as something different from how they are typically stored.
/// Current differences
///
/// - tag unions are passed by-reference. That means that
/// * `f : [ Some I64, None ] -> I64` is typed `{ { i64, i8 }, i64 }* -> i64`
/// * `f : { x : [ Some I64, None ] } -> I64 is typed `{ { { i64, i8 }, i64 } } -> i64`
///
/// Ideas exist to have (bigger than 2 register) records also be passed by-reference, but this
/// is not currently implemented
pub fn argument_type_from_layout<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout: &Layout<'_>,
) -> BasicTypeEnum<'ctx> {
use Layout::*;
match layout {
LambdaSet(lambda_set) => {
argument_type_from_layout(env, &lambda_set.runtime_representation())
}
Union(union_layout) => argument_type_from_union_layout(env, union_layout),
other => basic_type_from_layout(env, other),
}
}
/// Non-recursive tag unions are stored on the stack, but passed by-reference
pub fn argument_type_from_union_layout<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
union_layout: &UnionLayout<'_>,
) -> BasicTypeEnum<'ctx> {
let heap_type = basic_type_from_union_layout(env, union_layout);
if let UnionLayout::NonRecursive(_) = union_layout {
heap_type.ptr_type(AddressSpace::Generic).into()
} else {
heap_type
}
}
pub fn int_type_from_int_width<'a, 'ctx, 'env>( pub fn int_type_from_int_width<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
int_width: IntWidth, int_width: IntWidth,
) -> IntType<'ctx> { ) -> IntType<'ctx> {
use IntWidth::*; use IntWidth::*;
@ -182,7 +166,7 @@ pub fn int_type_from_int_width<'a, 'ctx, 'env>(
} }
pub fn float_type_from_float_width<'a, 'ctx, 'env>( pub fn float_type_from_float_width<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
float_width: FloatWidth, float_width: FloatWidth,
) -> FloatType<'ctx> { ) -> FloatType<'ctx> {
use FloatWidth::*; use FloatWidth::*;
@ -267,33 +251,23 @@ pub fn str_list_int(ctx: &Context, target_info: TargetInfo) -> IntType<'_> {
} }
} }
pub fn zig_dict_type<'a, 'ctx, 'env>( pub fn zig_dict_type<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
) -> StructType<'ctx> {
env.module.get_struct_type("dict.RocDict").unwrap() env.module.get_struct_type("dict.RocDict").unwrap()
} }
pub fn zig_list_type<'a, 'ctx, 'env>( pub fn zig_list_type<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
) -> StructType<'ctx> {
env.module.get_struct_type("list.RocList").unwrap() env.module.get_struct_type("list.RocList").unwrap()
} }
pub fn zig_str_type<'a, 'ctx, 'env>( pub fn zig_str_type<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
) -> StructType<'ctx> {
env.module.get_struct_type("str.RocStr").unwrap() env.module.get_struct_type("str.RocStr").unwrap()
} }
pub fn zig_has_tag_id_type<'a, 'ctx, 'env>( pub fn zig_has_tag_id_type<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
) -> StructType<'ctx> {
env.module.get_struct_type("list.HasTagId").unwrap() env.module.get_struct_type("list.HasTagId").unwrap()
} }
pub fn zig_with_overflow_roc_dec<'a, 'ctx, 'env>( pub fn zig_with_overflow_roc_dec<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
) -> StructType<'ctx> {
env.module env.module
.get_struct_type("utils.WithOverflow(dec.RocDec)") .get_struct_type("utils.WithOverflow(dec.RocDec)")
.unwrap() .unwrap()

View file

@ -5,7 +5,7 @@ use crate::llvm::build::{
FAST_CALL_CONV, TAG_DATA_INDEX, TAG_ID_INDEX, FAST_CALL_CONV, TAG_DATA_INDEX, TAG_ID_INDEX,
}; };
use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list}; use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list};
use crate::llvm::convert::{basic_type_from_layout, basic_type_from_layout_1}; use crate::llvm::convert::basic_type_from_layout;
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use inkwell::basic_block::BasicBlock; use inkwell::basic_block::BasicBlock;
use inkwell::context::Context; use inkwell::context::Context;
@ -20,6 +20,8 @@ use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout}; use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
use roc_target::TargetInfo; use roc_target::TargetInfo;
use super::convert::argument_type_from_union_layout;
/// "Infinite" reference count, for static values /// "Infinite" reference count, for static values
/// Ref counts are encoded as negative numbers where isize::MIN represents 1 /// Ref counts are encoded as negative numbers where isize::MIN represents 1
pub const REFCOUNT_MAX: usize = 0_usize; pub const REFCOUNT_MAX: usize = 0_usize;
@ -589,6 +591,12 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>(
modify_refcount_builtin(env, layout_ids, mode, when_recursive, layout, builtin) modify_refcount_builtin(env, layout_ids, mode, when_recursive, layout, builtin)
} }
Boxed(inner) => {
let function = modify_refcount_boxed(env, layout_ids, mode, inner);
Some(function)
}
Union(variant) => { Union(variant) => {
use UnionLayout::*; use UnionLayout::*;
@ -891,6 +899,76 @@ fn modify_refcount_str_help<'a, 'ctx, 'env>(
builder.build_return(None); builder.build_return(None);
} }
fn modify_refcount_boxed<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
mode: Mode,
inner_layout: &'a Layout<'a>,
) -> FunctionValue<'ctx> {
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let boxed_layout = env.arena.alloc(Layout::Boxed(inner_layout));
let (_, fn_name) = function_name_from_mode(
layout_ids,
&env.interns,
"increment_boxed",
"decrement_boxed",
boxed_layout,
mode,
);
let function = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value,
None => {
let basic_type = basic_type_from_layout(env, boxed_layout);
let function_value = build_header(env, basic_type, mode, &fn_name);
modify_refcount_box_help(env, mode, inner_layout, function_value);
function_value
}
};
env.builder.position_at_end(block);
env.builder
.set_current_debug_location(env.context, di_location);
function
}
fn modify_refcount_box_help<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
mode: Mode,
inner_layout: &Layout<'a>,
fn_val: FunctionValue<'ctx>,
) {
let builder = env.builder;
let ctx = env.context;
// Add a basic block for the entry point
let entry = ctx.append_basic_block(fn_val, "entry");
builder.position_at_end(entry);
debug_info_init!(env, fn_val);
// Add args to scope
let arg_symbol = Symbol::ARG_1;
let arg_val = fn_val.get_param_iter().next().unwrap();
arg_val.set_name(arg_symbol.as_str(&env.interns));
let boxed = arg_val.into_pointer_value();
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, boxed);
let call_mode = mode_to_call_mode(fn_val, mode);
let boxed_layout = Layout::Boxed(inner_layout);
refcount_ptr.modify(call_mode, &boxed_layout, env);
// this function returns void
builder.build_return(None);
}
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn modify_refcount_dict<'a, 'ctx, 'env>( fn modify_refcount_dict<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
@ -1619,7 +1697,8 @@ fn modify_refcount_union<'a, 'ctx, 'env>(
when_recursive: &WhenRecursive<'a>, when_recursive: &WhenRecursive<'a>,
fields: &'a [&'a [Layout<'a>]], fields: &'a [&'a [Layout<'a>]],
) -> FunctionValue<'ctx> { ) -> FunctionValue<'ctx> {
let layout = Layout::Union(UnionLayout::NonRecursive(fields)); let union_layout = UnionLayout::NonRecursive(fields);
let layout = Layout::Union(union_layout);
let block = env.builder.get_insert_block().expect("to be in a function"); let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap(); let di_location = env.builder.get_current_debug_location().unwrap();
@ -1636,7 +1715,7 @@ fn modify_refcount_union<'a, 'ctx, 'env>(
let function = match env.module.get_function(fn_name.as_str()) { let function = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value, Some(function_value) => function_value,
None => { None => {
let basic_type = basic_type_from_layout_1(env, &layout); let basic_type = argument_type_from_union_layout(env, &union_layout);
let function_value = build_header(env, basic_type, mode, &fn_name); let function_value = build_header(env, basic_type, mode, &fn_name);
modify_refcount_union_help( modify_refcount_union_help(
@ -1700,9 +1779,9 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
.build_load(tag_id_ptr, "load_tag_id") .build_load(tag_id_ptr, "load_tag_id")
.into_int_value(); .into_int_value();
let tag_id_u8 = env let tag_id_u8 =
.builder env.builder
.build_int_cast(tag_id, env.context.i8_type(), "tag_id_u8"); .build_int_cast_sign_flag(tag_id, env.context.i8_type(), false, "tag_id_u8");
// next, make a jump table for all possible values of the tag_id // next, make a jump table for all possible values of the tag_id
let mut cases = Vec::with_capacity_in(tags.len(), env.arena); let mut cases = Vec::with_capacity_in(tags.len(), env.arena);

View file

@ -594,6 +594,10 @@ impl<'a> WasmBackend<'a> {
index, index,
} => self.expr_union_at_index(*structure, *tag_id, union_layout, *index, sym), } => self.expr_union_at_index(*structure, *tag_id, union_layout, *index, sym),
Expr::ExprBox { .. } | Expr::ExprUnbox { .. } => {
todo!("Expression `{}`", expr.to_pretty(100))
}
Expr::Reuse { .. } | Expr::Reset { .. } | Expr::RuntimeErrorFunction(_) => { Expr::Reuse { .. } | Expr::Reset { .. } | Expr::RuntimeErrorFunction(_) => {
todo!("Expression `{}`", expr.to_pretty(100)) todo!("Expression `{}`", expr.to_pretty(100))
} }

View file

@ -102,6 +102,7 @@ impl WasmLayout {
| NullableWrapped { .. } | NullableWrapped { .. }
| NullableUnwrapped { .. }, | NullableUnwrapped { .. },
) )
| Layout::Boxed(_)
| Layout::RecursivePointer => Self::Primitive(PTR_TYPE, PTR_SIZE), | Layout::RecursivePointer => Self::Primitive(PTR_TYPE, PTR_SIZE),
} }
} }

View file

@ -688,6 +688,10 @@ impl<'a> LowLevelCall<'a> {
Hash => todo!("{:?}", self.lowlevel), Hash => todo!("{:?}", self.lowlevel),
Eq | NotEq => self.eq_or_neq(backend), Eq | NotEq => self.eq_or_neq(backend),
BoxExpr | UnboxExpr => {
unreachable!("The {:?} operation is turned into mono Expr", self.lowlevel)
}
} }
} }
@ -748,6 +752,8 @@ impl<'a> LowLevelCall<'a> {
} }
} }
Layout::Boxed(_) => todo!(),
Layout::RecursivePointer => { Layout::RecursivePointer => {
internal_error!( internal_error!(
"Tried to apply `==` to RecursivePointer values {:?}", "Tried to apply `==` to RecursivePointer values {:?}",

View file

@ -123,6 +123,8 @@ pub enum LowLevel {
PtrCast, PtrCast,
RefCountInc, RefCountInc,
RefCountDec, RefCountDec,
BoxExpr,
UnboxExpr,
} }
macro_rules! higher_order { macro_rules! higher_order {

View file

@ -1198,6 +1198,12 @@ define_builtins! {
13 SET_WALK_USER_FUNCTION: "#walk_user_function" 13 SET_WALK_USER_FUNCTION: "#walk_user_function"
14 SET_CONTAINS: "contains" 14 SET_CONTAINS: "contains"
} }
8 BOX: "Box" => {
0 BOX_BOX_TYPE: "Box" imported // the Box.Box opaque type
1 BOX_BOX_FUNCTION: "box" // Box.box
2 BOX_UNBOX: "unbox"
num_modules: 8 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro) }
num_modules: 9 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro)
} }

View file

@ -724,6 +724,23 @@ impl<'a> BorrowInfState<'a> {
// the function must take it as an owned parameter // the function must take it as an owned parameter
self.own_args_if_param(xs); self.own_args_if_param(xs);
} }
ExprBox { symbol: x } => {
self.own_var(z);
// if the used symbol is an argument to the current function,
// the function must take it as an owned parameter
self.own_args_if_param(&[*x]);
}
ExprUnbox { symbol: x } => {
// if the boxed value is owned, the box is
self.if_is_owned_then_own(*x, z);
// if the extracted value is owned, the structure must be too
self.if_is_owned_then_own(z, *x);
}
Reset { symbol: x, .. } => { Reset { symbol: x, .. } => {
self.own_var(z); self.own_var(z);
self.own_var(*x); self.own_var(*x);
@ -1011,6 +1028,10 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
ExpectTrue => arena.alloc_slice_copy(&[irrelevant]), ExpectTrue => arena.alloc_slice_copy(&[irrelevant]),
BoxExpr | UnboxExpr => {
unreachable!("These lowlevel operations are turned into mono Expr's")
}
PtrCast | RefCountInc | RefCountDec => { PtrCast | RefCountInc | RefCountDec => {
unreachable!("Only inserted *after* borrow checking: {:?}", op); unreachable!("Only inserted *after* borrow checking: {:?}", op);
} }

View file

@ -34,6 +34,7 @@ pub fn eq_generic<'a>(
Layout::Builtin(Builtin::List(elem_layout)) => eq_list(root, ident_ids, ctx, elem_layout), Layout::Builtin(Builtin::List(elem_layout)) => eq_list(root, ident_ids, ctx, elem_layout),
Layout::Struct { field_layouts, .. } => eq_struct(root, ident_ids, ctx, field_layouts), Layout::Struct { field_layouts, .. } => eq_struct(root, ident_ids, ctx, field_layouts),
Layout::Union(union_layout) => eq_tag_union(root, ident_ids, ctx, union_layout), Layout::Union(union_layout) => eq_tag_union(root, ident_ids, ctx, union_layout),
Layout::Boxed(inner_layout) => eq_boxed(root, ident_ids, ctx, inner_layout),
Layout::LambdaSet(_) => unreachable!("`==` is not defined on functions"), Layout::LambdaSet(_) => unreachable!("`==` is not defined on functions"),
Layout::RecursivePointer => { Layout::RecursivePointer => {
unreachable!( unreachable!(
@ -528,6 +529,15 @@ fn eq_tag_fields<'a>(
stmt stmt
} }
fn eq_boxed<'a>(
_root: &mut CodeGenHelp<'a>,
_ident_ids: &mut IdentIds,
_ctx: &mut Context<'a>,
_inner_layout: &'a Layout<'a>,
) -> Stmt<'a> {
todo!()
}
/// List equality /// List equality
/// We can't use `ListGetUnsafe` because it increments the refcount, and we don't want that. /// We can't use `ListGetUnsafe` because it increments the refcount, and we don't want that.
/// Another way to dereference a heap pointer is to use `Expr::UnionAtIndex`. /// Another way to dereference a heap pointer is to use `Expr::UnionAtIndex`.

View file

@ -378,7 +378,13 @@ impl<'a> CodeGenHelp<'a> {
Layout::Union(UnionLayout::NonRecursive(new_tags.into_bump_slice())) Layout::Union(UnionLayout::NonRecursive(new_tags.into_bump_slice()))
} }
Layout::Union(_) => layout, Layout::Union(_) => {
// we always fully unroll recursive types. That means tha when we find a
// recursive tag union we can replace it with the layout
layout
}
Layout::Boxed(inner) => self.replace_rec_ptr(ctx, *inner),
Layout::LambdaSet(lambda_set) => { Layout::LambdaSet(lambda_set) => {
self.replace_rec_ptr(ctx, lambda_set.runtime_representation()) self.replace_rec_ptr(ctx, lambda_set.runtime_representation())
@ -476,5 +482,7 @@ fn layout_needs_helper_proc(layout: &Layout, op: HelperOp) -> bool {
Layout::Union(_) => true, Layout::Union(_) => true,
Layout::LambdaSet(_) | Layout::RecursivePointer => false, Layout::LambdaSet(_) | Layout::RecursivePointer => false,
Layout::Boxed(_) => true,
} }
} }

View file

@ -122,6 +122,7 @@ pub fn refcount_generic<'a>(
refcount_generic(root, ident_ids, ctx, runtime_layout, structure) refcount_generic(root, ident_ids, ctx, runtime_layout, structure)
} }
Layout::RecursivePointer => rc_todo(), Layout::RecursivePointer => rc_todo(),
Layout::Boxed(_) => rc_todo(),
} }
} }
@ -155,6 +156,7 @@ pub fn is_rc_implemented_yet(layout: &Layout) -> bool {
is_rc_implemented_yet(&lambda_set.runtime_representation()) is_rc_implemented_yet(&lambda_set.runtime_representation())
} }
Layout::RecursivePointer => true, Layout::RecursivePointer => true,
Layout::Boxed(_) => false,
} }
} }

View file

@ -114,6 +114,10 @@ pub fn occurring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
result.insert(*x); result.insert(*x);
} }
ExprBox { symbol } | ExprUnbox { symbol } => {
result.insert(*symbol);
}
EmptyArray | RuntimeErrorFunction(_) | Literal(_) => {} EmptyArray | RuntimeErrorFunction(_) | Literal(_) => {}
GetTagId { GetTagId {
@ -756,6 +760,28 @@ impl<'a> Context<'a> {
self.arena.alloc(Stmt::Let(z, v, l, b)) self.arena.alloc(Stmt::Let(z, v, l, b))
} }
ExprBox { symbol: x } => {
// mimics Tag
self.add_inc_before_consume_all(
&[x],
self.arena.alloc(Stmt::Let(z, v, l, b)),
b_live_vars,
)
}
ExprUnbox { symbol: x } => {
// mimics UnionAtIndex
let b = self.add_dec_if_needed(x, b, b_live_vars);
let info_x = self.get_var_info(x);
let b = if info_x.consume {
self.add_inc(z, 1, b)
} else {
b
};
self.arena.alloc(Stmt::Let(z, v, l, b))
}
EmptyArray | Literal(_) | Reset { .. } | RuntimeErrorFunction(_) => { EmptyArray | Literal(_) | Reset { .. } | RuntimeErrorFunction(_) => {
// EmptyArray is always stack-allocated // EmptyArray is always stack-allocated
// function pointers are persistent // function pointers are persistent

View file

@ -1505,6 +1505,14 @@ pub enum Expr<'a> {
}, },
EmptyArray, EmptyArray,
ExprBox {
symbol: Symbol,
},
ExprUnbox {
symbol: Symbol,
},
Reuse { Reuse {
symbol: Symbol, symbol: Symbol,
update_tag_id: bool, update_tag_id: bool,
@ -1682,6 +1690,10 @@ impl<'a> Expr<'a> {
.text("GetTagId ") .text("GetTagId ")
.append(symbol_to_doc(alloc, *structure)), .append(symbol_to_doc(alloc, *structure)),
ExprBox { symbol, .. } => alloc.text("Box ").append(symbol_to_doc(alloc, *symbol)),
ExprUnbox { symbol, .. } => alloc.text("Unbox ").append(symbol_to_doc(alloc, *symbol)),
UnionAtIndex { UnionAtIndex {
tag_id, tag_id,
structure, structure,
@ -4615,6 +4627,18 @@ pub fn with_hole<'a>(
let xs = arg_symbols[0]; let xs = arg_symbols[0];
match_on_closure_argument!(ListFindUnsafe, [xs]) match_on_closure_argument!(ListFindUnsafe, [xs])
} }
BoxExpr => {
debug_assert_eq!(arg_symbols.len(), 1);
let x = arg_symbols[0];
Stmt::Let(assigned, Expr::ExprBox { symbol: x }, layout, hole)
}
UnboxExpr => {
debug_assert_eq!(arg_symbols.len(), 1);
let x = arg_symbols[0];
Stmt::Let(assigned, Expr::ExprUnbox { symbol: x }, layout, hole)
}
_ => { _ => {
let call = self::Call { let call = self::Call {
call_type: CallType::LowLevel { call_type: CallType::LowLevel {
@ -6179,6 +6203,14 @@ fn substitute_in_expr<'a>(
} }
} }
ExprBox { symbol } => {
substitute(subs, *symbol).map(|new_symbol| ExprBox { symbol: new_symbol })
}
ExprUnbox { symbol } => {
substitute(subs, *symbol).map(|new_symbol| ExprUnbox { symbol: new_symbol })
}
StructAtIndex { StructAtIndex {
index, index,
structure, structure,

View file

@ -250,6 +250,7 @@ pub enum Layout<'a> {
field_order_hash: FieldOrderHash, field_order_hash: FieldOrderHash,
field_layouts: &'a [Layout<'a>], field_layouts: &'a [Layout<'a>],
}, },
Boxed(&'a Layout<'a>),
Union(UnionLayout<'a>), Union(UnionLayout<'a>),
LambdaSet(LambdaSet<'a>), LambdaSet(LambdaSet<'a>),
RecursivePointer, RecursivePointer,
@ -997,7 +998,7 @@ impl<'a> Layout<'a> {
} }
} }
LambdaSet(lambda_set) => lambda_set.runtime_representation().safe_to_memcpy(), LambdaSet(lambda_set) => lambda_set.runtime_representation().safe_to_memcpy(),
RecursivePointer => { Boxed(_) | RecursivePointer => {
// We cannot memcpy pointers, because then we would have the same pointer in multiple places! // We cannot memcpy pointers, because then we would have the same pointer in multiple places!
false false
} }
@ -1066,6 +1067,7 @@ impl<'a> Layout<'a> {
.runtime_representation() .runtime_representation()
.stack_size_without_alignment(target_info), .stack_size_without_alignment(target_info),
RecursivePointer => target_info.ptr_width() as u32, RecursivePointer => target_info.ptr_width() as u32,
Boxed(_) => target_info.ptr_width() as u32,
} }
} }
@ -1114,6 +1116,7 @@ impl<'a> Layout<'a> {
.alignment_bytes(target_info), .alignment_bytes(target_info),
Layout::Builtin(builtin) => builtin.alignment_bytes(target_info), Layout::Builtin(builtin) => builtin.alignment_bytes(target_info),
Layout::RecursivePointer => target_info.ptr_width() as u32, Layout::RecursivePointer => target_info.ptr_width() as u32,
Layout::Boxed(_) => target_info.ptr_width() as u32,
} }
} }
@ -1126,6 +1129,7 @@ impl<'a> Layout<'a> {
.runtime_representation() .runtime_representation()
.allocation_alignment_bytes(target_info), .allocation_alignment_bytes(target_info),
Layout::RecursivePointer => unreachable!("should be looked up to get an actual layout"), Layout::RecursivePointer => unreachable!("should be looked up to get an actual layout"),
Layout::Boxed(inner) => inner.allocation_alignment_bytes(target_info),
} }
} }
@ -1172,6 +1176,7 @@ impl<'a> Layout<'a> {
} }
LambdaSet(lambda_set) => lambda_set.runtime_representation().contains_refcounted(), LambdaSet(lambda_set) => lambda_set.runtime_representation().contains_refcounted(),
RecursivePointer => true, RecursivePointer => true,
Boxed(_) => true,
} }
} }
@ -1196,6 +1201,10 @@ impl<'a> Layout<'a> {
Union(union_layout) => union_layout.to_doc(alloc, parens), Union(union_layout) => union_layout.to_doc(alloc, parens),
LambdaSet(lambda_set) => lambda_set.runtime_representation().to_doc(alloc, parens), LambdaSet(lambda_set) => lambda_set.runtime_representation().to_doc(alloc, parens),
RecursivePointer => alloc.text("*self"), RecursivePointer => alloc.text("*self"),
Boxed(inner) => alloc
.text("Boxed(")
.append(inner.to_doc(alloc, parens))
.append(")"),
} }
} }
@ -1615,6 +1624,15 @@ fn layout_from_flat_type<'a>(
Symbol::LIST_LIST => list_layout_from_elem(env, args[0]), Symbol::LIST_LIST => list_layout_from_elem(env, args[0]),
Symbol::DICT_DICT => dict_layout_from_key_value(env, args[0], args[1]), Symbol::DICT_DICT => dict_layout_from_key_value(env, args[0], args[1]),
Symbol::SET_SET => dict_layout_from_key_value(env, args[0], Variable::EMPTY_RECORD), Symbol::SET_SET => dict_layout_from_key_value(env, args[0], Variable::EMPTY_RECORD),
Symbol::BOX_BOX_TYPE => {
// Num.Num should only ever have 1 argument, e.g. Num.Num Int.Integer
debug_assert_eq!(args.len(), 1);
let inner_var = args[0];
let inner_layout = Layout::from_var(env, inner_var)?;
Ok(Layout::Boxed(env.arena.alloc(inner_layout)))
}
_ => { _ => {
panic!( panic!(
"TODO layout_from_flat_type for Apply({:?}, {:?})", "TODO layout_from_flat_type for Apply({:?}, {:?})",

View file

@ -239,6 +239,12 @@ fn insert_reset<'a>(
stack.push((symbol, expr, expr_layout)); stack.push((symbol, expr, expr_layout));
stmt = rest; stmt = rest;
} }
ExprBox { .. } | ExprUnbox { .. } => {
// TODO
break;
}
Literal(_) Literal(_)
| Call(_) | Call(_)
| Tag { .. } | Tag { .. }
@ -620,6 +626,8 @@ fn has_live_var_expr<'a>(expr: &'a Expr<'a>, needle: Symbol) -> bool {
symbol, arguments, .. symbol, arguments, ..
} => needle == *symbol || arguments.iter().any(|s| *s == needle), } => needle == *symbol || arguments.iter().any(|s| *s == needle),
Expr::Reset { symbol, .. } => needle == *symbol, Expr::Reset { symbol, .. } => needle == *symbol,
Expr::ExprBox { symbol, .. } => needle == *symbol,
Expr::ExprUnbox { symbol, .. } => needle == *symbol,
Expr::RuntimeErrorFunction(_) => false, Expr::RuntimeErrorFunction(_) => false,
} }
} }

View file

@ -798,6 +798,34 @@ fn list_walk_until_sum() {
); );
} }
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn list_walk_imlements_position() {
assert_evals_to!(
r#"
Option a : [ Some a, None ]
find : List a, a -> Option Nat
find = \list, needle ->
findHelp list needle
|> .v
findHelp = \list, needle ->
List.walkUntil list { n: 0, v: None } \{ n, v }, element ->
if element == needle then
Stop { n, v: Some n }
else
Continue { n: n + 1, v }
when find [ 1, 2, 3 ] 3 is
None -> 0
Some v -> v
"#,
2,
i64
);
}
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm"))]
fn list_walk_until_even_prefix_sum() { fn list_walk_until_even_prefix_sum() {

View file

@ -2781,3 +2781,35 @@ fn to_float_f64() {
f64 f64
) )
} }
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
// https://github.com/rtfeldman/roc/issues/2696
fn upcast_of_int_is_zext() {
assert_evals_to!(
indoc!(
r#"
Num.toU16 0b1000_0000u8
"#
),
128,
u16
)
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
// https://github.com/rtfeldman/roc/issues/2696
fn upcast_of_int_checked_is_zext() {
assert_evals_to!(
indoc!(
r#"
when Num.toU16Checked 0b1000_0000u8 is
Ok 128u16 -> 1u8
_ -> 0u8
"#
),
1,
u16
)
}

View file

@ -3228,3 +3228,19 @@ fn issue_2322() {
i64 i64
) )
} }
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn box_and_unbox_string() {
assert_evals_to!(
indoc!(
r#"
Box.unbox (Box.box (Str.concat "Leverage " "agile frameworks to provide a robust synopsis for high level overviews"))
"#
),
RocStr::from(
"Leverage agile frameworks to provide a robust synopsis for high level overviews"
),
RocStr
)
}

View file

@ -969,6 +969,11 @@ pub fn result_type(a: SolvedType, e: SolvedType) -> SolvedType {
) )
} }
#[inline(always)]
pub fn box_type(a: SolvedType) -> SolvedType {
SolvedType::Apply(Symbol::BOX_BOX_TYPE, vec![a])
}
#[inline(always)] #[inline(always)]
fn result_alias_content(a: SolvedType, e: SolvedType) -> SolvedType { fn result_alias_content(a: SolvedType, e: SolvedType) -> SolvedType {
SolvedType::TagUnion( SolvedType::TagUnion(

View file

@ -25,14 +25,14 @@ cheapestOpen = \costFn, model ->
model.openSet model.openSet
|> Set.toList |> Set.toList
|> List.keepOks |> List.keepOks
(\position -> (\position ->
when Dict.get model.costs position is when Dict.get model.costs position is
Err _ -> Err _ ->
Err {} Err {}
Ok cost -> Ok cost ->
Ok { cost: cost + costFn position, position } Ok { cost: cost + costFn position, position }
) )
|> Quicksort.sortBy .cost |> Quicksort.sortBy .cost
|> List.first |> List.first
|> Result.map .position |> Result.map .position

View file

@ -39,7 +39,7 @@ const QUAD_VERTS: [Vertex; 4] = [
}, },
]; ];
pub const MAX_QUADS: usize = 100_000; pub const MAX_QUADS: usize = 1_000;
pub fn create_rect_buffers( pub fn create_rect_buffers(
gpu_device: &wgpu::Device, gpu_device: &wgpu::Device,
@ -59,7 +59,7 @@ pub fn create_rect_buffers(
}); });
let quad_buffer = gpu_device.create_buffer(&wgpu::BufferDescriptor { let quad_buffer = gpu_device.create_buffer(&wgpu::BufferDescriptor {
label: Some("iced_wgpu::quad instance buffer"), label: None,
size: mem::size_of::<Quad>() as u64 * MAX_QUADS as u64, size: mem::size_of::<Quad>() as u64 * MAX_QUADS as u64,
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false, mapped_at_creation: false,

View file

@ -178,7 +178,7 @@ fn run_event_loop(title: &str, root: RocElem) -> Result<(), Box<dyn Error>> {
// } // }
// } // }
// } // }
todo!("TODO handle keyboard input"); // TODO todo!("TODO handle keyboard input");
} }
//Modifiers Changed //Modifiers Changed
Event::WindowEvent { Event::WindowEvent {

View file

@ -23,11 +23,11 @@ echo = \shout ->
shout shout
|> Str.toUtf8 |> Str.toUtf8
|> List.mapWithIndex |> List.mapWithIndex
(\_, i -> (\_, i ->
length = (List.len (Str.toUtf8 shout) - i) length = (List.len (Str.toUtf8 shout) - i)
phrase = (List.split (Str.toUtf8 shout) length).before phrase = (List.split (Str.toUtf8 shout) length).before
List.concat (silence (if i == 0 then 2 * length else length)) phrase) List.concat (silence (if i == 0 then 2 * length else length)) phrase)
|> List.join |> List.join
|> Str.fromUtf8 |> Str.fromUtf8
|> Result.withDefault "" |> Result.withDefault ""

View file

@ -438,6 +438,7 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
unreachable!("RecursivePointers can only be inside structures") unreachable!("RecursivePointers can only be inside structures")
} }
Layout::LambdaSet(_) => Ok(OPAQUE_FUNCTION), Layout::LambdaSet(_) => Ok(OPAQUE_FUNCTION),
Layout::Boxed(_inner) => todo!(),
}; };
result.map(|e| apply_newtypes(env, newtype_containers, e)) result.map(|e| apply_newtypes(env, newtype_containers, e))
} }

View file

@ -426,8 +426,8 @@ mod test_reporting {
`Booly` is not used anywhere in your code. `Booly` is not used anywhere in your code.
3 Booly : [ Yes, No, Maybe ] 1 Booly : [ Yes, No ]
^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
If you didn't intend on using `Booly` then remove it so future readers If you didn't intend on using `Booly` then remove it so future readers
of your code don't wonder why it is there. of your code don't wonder why it is there.
@ -436,8 +436,8 @@ mod test_reporting {
`Booly` is not used anywhere in your code. `Booly` is not used anywhere in your code.
1 Booly : [ Yes, No ] 3 Booly : [ Yes, No, Maybe ]
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
If you didn't intend on using `Booly` then remove it so future readers If you didn't intend on using `Booly` then remove it so future readers
of your code don't wonder why it is there. of your code don't wonder why it is there.
@ -1544,10 +1544,10 @@ mod test_reporting {
Did you mean one of these? Did you mean one of these?
Box
Bool Bool
U8 U8
F64 F64
Nat
"# "#
), ),
) )
@ -2003,8 +2003,8 @@ mod test_reporting {
Ok Ok
U8 U8
Box
f f
I8
"# "#
), ),
) )
@ -3727,10 +3727,10 @@ mod test_reporting {
Is there an import missing? Perhaps there is a typo. Did you mean one Is there an import missing? Perhaps there is a typo. Did you mean one
of these? of these?
Box
Bool Bool
Num Num
Set Set
Str
"# "#
), ),
) )
@ -7983,21 +7983,21 @@ I need all branches in an `if` to have the same type!
indoc!( indoc!(
r#" r#"
CYCLIC ALIAS CYCLIC ALIAS
The `Bar` alias is recursive in an invalid way: The `Foo` alias is recursive in an invalid way:
2 Bar a : [ Stuff (Foo a) ] 1 Foo a : [ Thing (Bar a) ]
^^^^^ ^^^
The `Bar` alias depends on itself through the following chain of The `Foo` alias depends on itself through the following chain of
definitions: definitions:
Bar
Foo Foo
Bar
Recursion in aliases is only allowed if recursion happens behind a Recursion in aliases is only allowed if recursion happens behind a
tagged union, at least one variant of which is not recursive. tagged union, at least one variant of which is not recursive.
"# "#
@ -8185,7 +8185,7 @@ I need all branches in an `if` to have the same type!
Test Test
List List
Num Num
Set Box
"# "#
), ),
) )

View file

@ -23,7 +23,7 @@ edition = "2018"
# commit of TheDan64/inkwell, push a new tag which points to the latest commit, # commit of TheDan64/inkwell, push a new tag which points to the latest commit,
# change the tag value in this Cargo.toml to point to that tag, and `cargo update`. # change the tag value in this Cargo.toml to point to that tag, and `cargo update`.
# This way, GitHub Actions works and nobody's builds get broken. # This way, GitHub Actions works and nobody's builds get broken.
inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm13-0.release1", features = [ "llvm12-0" ] } inkwell = { git = "https://github.com/rtfeldman/inkwell", branch = "master", features = [ "llvm12-0" ] }
[features] [features]
target-arm = [] target-arm = []