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"
version = "0.1.0"
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]]
name = "inkwell"
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 = [
"either",
"inkwell_internals",
@ -1789,7 +1789,7 @@ dependencies = [
[[package]]
name = "inkwell_internals"
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 = [
"proc-macro2",
"quote",

View file

@ -1511,6 +1511,16 @@ fn expr_spec<'a>(
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),
UnionAtIndex {
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 {
WhenRecursive::Unreachable => {
unreachable!()
@ -1787,6 +1804,10 @@ const LIST_BAG_INDEX: u32 = 1;
const DICT_CELL_INDEX: u32 = LIST_CELL_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_DATA_INDEX: u32 = 1;

View file

@ -890,14 +890,23 @@ pub fn listSublist(
}
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;
while (i < drop_len) : (i += 1) {
while (i < drop_start_len) : (i += 1) {
const element = source_ptr + i * element_width;
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 target_ptr = output.bytes orelse unreachable;

View file

@ -3,10 +3,10 @@ use roc_module::ident::TagName;
use roc_module::symbol::Symbol;
use roc_region::all::Region;
use roc_types::builtin_aliases::{
bool_type, dec_type, dict_type, f32_type, f64_type, float_type, i128_type, i16_type, i32_type,
i64_type, i8_type, int_type, list_type, nat_type, num_type, ordering_type, result_type,
set_type, str_type, str_utf8_byte_problem_type, u128_type, u16_type, u32_type, u64_type,
u8_type,
bool_type, box_type, dec_type, dict_type, f32_type, f64_type, float_type, i128_type, i16_type,
i32_type, i64_type, i8_type, int_type, list_type, nat_type, num_type, ordering_type,
result_type, set_type, str_type, str_utf8_byte_problem_type, u128_type, u16_type, u32_type,
u64_type, u8_type,
};
use roc_types::solved_types::SolvedType;
use roc_types::subs::VarId;
@ -1782,6 +1782,20 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
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
}

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_IS_OK => result_is_ok,
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)]
fn defn_help(
fn_name: Symbol,

View file

@ -117,6 +117,7 @@ impl<'a> Formattable for Expr<'a> {
) {
use self::Expr::*;
//dbg!(self);
let format_newlines = newlines == Newlines::Yes;
let apply_needs_parens = parens == Parens::InApply;
@ -453,14 +454,17 @@ fn fmt_bin_ops<'a, 'buf>(
|| (&loc_right_side.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 {
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 {
buf.newline();
buf.indent(indent + INDENT);
curr_indent = indent + INDENT;
buf.indent(curr_indent);
} else {
buf.spaces(1);
}

View file

@ -2595,7 +2595,7 @@ mod test_fmt {
}
#[test]
fn pipline_apply_lambda() {
fn pipline_apply_lambda_1() {
expr_formats_same(indoc!(
r#"
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
#[test]

View file

@ -875,6 +875,12 @@ trait Backend<'a> {
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) => {
for sym in *syms {
self.set_last_seen(*sym, stmt);

View file

@ -260,8 +260,12 @@ fn build_has_tag_id_help<'a, 'ctx, 'env>(
tag_value.into(),
);
env.builder
.build_int_cast(tag_id_i64, env.context.i16_type(), "to_i16")
env.builder.build_int_cast_sign_flag(
tag_id_i64,
env.context.i16_type(),
true,
"to_i16",
)
};
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::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,
};
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),
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, .. } => {
let (tag_ptr, layout) = load_symbol_and_layout(scope, symbol);
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");
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>(
@ -2553,6 +2577,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
let align_bytes = layout.alignment_bytes(env.target_info);
if align_bytes > 0 {
debug_assert!(value.is_pointer_value(), "{:?}\n{:?}", value, layout);
let value_ptr = value.into_pointer_value();
// 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);
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);
}
@ -5864,8 +5889,11 @@ fn run_low_level<'a, 'ctx, 'env>(
let arg = load_symbol(scope, &args[0]).into_int_value();
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 => {
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")
}
BoxExpr | UnboxExpr => {
unreachable!("The {:?} operation is turned into mono Expr", op)
}
PtrCast | RefCountInc | RefCountDec => {
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_val: BasicValueEnum<'ctx> = env
.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();
let return_type =

View file

@ -67,7 +67,12 @@ pub fn dict_len<'a, 'ctx, 'env>(
);
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()
}
_ => 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::{get_tag_id, FAST_CALL_CONV, TAG_DATA_INDEX};
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 inkwell::values::{
BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue,
@ -13,6 +13,8 @@ use roc_builtins::bitcode;
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
use super::convert::argument_type_from_union_layout;
#[derive(Clone, Debug)]
enum WhenRecursive<'a> {
Unreachable,
@ -68,8 +70,11 @@ fn build_hash_layout<'a, 'ctx, 'env>(
when_recursive,
),
Layout::Union(union_layout) => {
build_hash_tag(env, layout_ids, layout, union_layout, seed, val)
Layout::Union(union_layout) => build_hash_tag(env, layout_ids, 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 {
@ -87,14 +92,7 @@ fn build_hash_layout<'a, 'ctx, 'env>(
.build_bitcast(val, bt, "i64_to_opaque")
.into_pointer_value();
build_hash_tag(
env,
layout_ids,
&layout,
&union_layout,
seed,
field_cast.into(),
)
build_hash_tag(env, layout_ids, &union_layout, seed, field_cast.into())
}
},
}
@ -308,7 +306,6 @@ fn hash_struct<'a, 'ctx, 'env>(
fn build_hash_tag<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
layout: &Layout<'a>,
union_layout: &UnionLayout<'a>,
seed: IntValue<'ctx>,
value: BasicValueEnum<'ctx>,
@ -318,7 +315,7 @@ fn build_hash_tag<'a, 'ctx, 'env>(
let symbol = Symbol::GENERIC_HASH;
let fn_name = layout_ids
.get(symbol, layout)
.get(symbol, &Layout::Union(*union_layout))
.to_symbol_string(symbol, &env.interns);
let function = match env.module.get_function(fn_name.as_str()) {
@ -326,7 +323,7 @@ fn build_hash_tag<'a, 'ctx, 'env>(
None => {
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(
env,
@ -805,7 +802,7 @@ fn hash_ptr_to_struct<'a, 'ctx, 'env>(
) -> IntValue<'ctx> {
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
let wrapper_ptr = env

View file

@ -82,7 +82,7 @@ fn pass_element_as_opaque<'a, 'ctx, 'env>(
env.builder.build_bitcast(
element_ptr,
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(
ptr,
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!(),
};
let default_ptr = if default_layout.is_passed_by_reference() {
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 {
ListWalk::Walk | ListWalk::WalkBackwards => {
@ -494,7 +503,11 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
}
}
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)

View file

@ -170,7 +170,7 @@ pub fn str_number_of_bytes<'a, 'ctx, 'env>(
// cast to the appropriate usize of the current build
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

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_list::{list_len, load_list_ptr};
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 inkwell::types::BasicType;
use inkwell::values::{
@ -15,6 +15,7 @@ use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
use super::build::load_roc_value;
use super::convert::argument_type_from_union_layout;
#[derive(Clone, Debug)]
enum WhenRecursive<'a> {
@ -176,12 +177,21 @@ fn build_eq<'a, 'ctx, 'env>(
env,
layout_ids,
when_recursive,
lhs_layout,
union_layout,
lhs_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 {
WhenRecursive::Unreachable => {
unreachable!("recursion pointers should never be compared directly")
@ -207,7 +217,6 @@ fn build_eq<'a, 'ctx, 'env>(
env,
layout_ids,
WhenRecursive::Loop(union_layout),
&layout,
&union_layout,
field1_cast.into(),
field2_cast.into(),
@ -345,12 +354,12 @@ fn build_neq<'a, 'ctx, 'env>(
result.into()
}
Layout::Union(union_layout) => {
let is_equal = build_tag_eq(
env,
layout_ids,
when_recursive,
lhs_layout,
union_layout,
lhs_val,
rhs_val,
@ -362,6 +371,23 @@ fn build_neq<'a, 'ctx, 'env>(
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 => {
unreachable!("recursion pointers should never be compared directly")
}
@ -764,7 +790,6 @@ fn build_tag_eq<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
when_recursive: WhenRecursive<'a>,
tag_layout: &Layout<'a>,
union_layout: &UnionLayout<'a>,
tag1: 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 di_location = env.builder.get_current_debug_location().unwrap();
let tag_layout = Layout::Union(*union_layout);
let symbol = Symbol::GENERIC_EQ;
let fn_name = layout_ids
.get(symbol, tag_layout)
.get(symbol, &tag_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_1(env, tag_layout);
let arg_type = argument_type_from_union_layout(env, union_layout);
let function_value = crate::llvm::refcounting::build_header_help(
env,
@ -1101,8 +1127,10 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
let i8_type = env.context.i8_type();
let sum = env.builder.build_int_add(
env.builder.build_int_cast(is_null_1, i8_type, "to_u8"),
env.builder.build_int_cast(is_null_2, i8_type, "to_u8"),
env.builder
.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",
);
@ -1252,3 +1280,141 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>(
)
.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 inkwell::context::Context;
use inkwell::types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType};
@ -7,7 +8,7 @@ use roc_mono::layout::{Builtin, Layout, UnionLayout};
use roc_target::TargetInfo;
fn basic_type_from_record<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
env: &Env<'a, 'ctx, 'env>,
fields: &[Layout<'_>],
) -> BasicTypeEnum<'ctx> {
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>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
env: &Env<'a, 'ctx, 'env>,
layout: &Layout<'_>,
) -> BasicTypeEnum<'ctx> {
use Layout::*;
@ -33,7 +34,26 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
..
} => basic_type_from_record(env, sorted_fields),
LambdaSet(lambda_set) => basic_type_from_layout(env, &lambda_set.runtime_representation()),
Union(union_layout) => {
Boxed(inner_layout) => {
let inner_type = basic_type_from_layout(env, inner_layout);
inner_type.ptr_type(AddressSpace::Generic).into()
}
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),
}
}
pub fn basic_type_from_union_layout<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
union_layout: &UnionLayout<'_>,
) -> BasicTypeEnum<'ctx> {
use UnionLayout::*;
let tag_id_type = basic_type_from_layout(env, &union_layout.tag_id_layout());
@ -60,8 +80,7 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
}
}
NullableUnwrapped { other_fields, .. } => {
let block =
block_of_memory_slices(env.context, &[other_fields], env.target_info);
let block = block_of_memory_slices(env.context, &[other_fields], env.target_info);
block.ptr_type(AddressSpace::Generic).into()
}
NonNullableUnwrapped(fields) => {
@ -69,85 +88,10 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
block.ptr_type(AddressSpace::Generic).into()
}
}
}
RecursivePointer => {
// TODO make this dynamic
env.context
.i64_type()
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum()
}
Builtin(builtin) => basic_type_from_builtin(env, builtin),
}
}
pub fn basic_type_from_layout_1<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
layout: &Layout<'_>,
) -> BasicTypeEnum<'ctx> {
use Layout::*;
match layout {
Struct {
field_layouts: sorted_fields,
..
} => basic_type_from_record(env, sorted_fields),
LambdaSet(lambda_set) => {
basic_type_from_layout_1(env, &lambda_set.runtime_representation())
}
Union(union_layout) => {
use UnionLayout::*;
let tag_id_type = basic_type_from_layout(env, &union_layout.tag_id_layout());
match union_layout {
NonRecursive(tags) => {
let data = block_of_memory_slices(env.context, tags, env.target_info);
let struct_type = env.context.struct_type(&[data, tag_id_type], false);
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 => {
// TODO make this dynamic
env.context
.i64_type()
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum()
}
Builtin(builtin) => basic_type_from_builtin(env, builtin),
}
}
pub fn basic_type_from_builtin<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
env: &Env<'a, 'ctx, 'env>,
builtin: &Builtin<'_>,
) -> BasicTypeEnum<'ctx> {
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>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
env: &Env<'a, 'ctx, 'env>,
int_width: IntWidth,
) -> IntType<'ctx> {
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>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
env: &Env<'a, 'ctx, 'env>,
float_width: FloatWidth,
) -> FloatType<'ctx> {
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>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
) -> StructType<'ctx> {
pub fn zig_dict_type<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
env.module.get_struct_type("dict.RocDict").unwrap()
}
pub fn zig_list_type<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
) -> StructType<'ctx> {
pub fn zig_list_type<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
env.module.get_struct_type("list.RocList").unwrap()
}
pub fn zig_str_type<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
) -> StructType<'ctx> {
pub fn zig_str_type<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
env.module.get_struct_type("str.RocStr").unwrap()
}
pub fn zig_has_tag_id_type<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
) -> StructType<'ctx> {
pub fn zig_has_tag_id_type<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
env.module.get_struct_type("list.HasTagId").unwrap()
}
pub fn zig_with_overflow_roc_dec<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
) -> StructType<'ctx> {
pub fn zig_with_overflow_roc_dec<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> StructType<'ctx> {
env.module
.get_struct_type("utils.WithOverflow(dec.RocDec)")
.unwrap()

View file

@ -5,7 +5,7 @@ use crate::llvm::build::{
FAST_CALL_CONV, TAG_DATA_INDEX, TAG_ID_INDEX,
};
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 inkwell::basic_block::BasicBlock;
use inkwell::context::Context;
@ -20,6 +20,8 @@ use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
use roc_target::TargetInfo;
use super::convert::argument_type_from_union_layout;
/// "Infinite" reference count, for static values
/// Ref counts are encoded as negative numbers where isize::MIN represents 1
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)
}
Boxed(inner) => {
let function = modify_refcount_boxed(env, layout_ids, mode, inner);
Some(function)
}
Union(variant) => {
use UnionLayout::*;
@ -891,6 +899,76 @@ fn modify_refcount_str_help<'a, 'ctx, 'env>(
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)]
fn modify_refcount_dict<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
@ -1619,7 +1697,8 @@ fn modify_refcount_union<'a, 'ctx, 'env>(
when_recursive: &WhenRecursive<'a>,
fields: &'a [&'a [Layout<'a>]],
) -> 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 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()) {
Some(function_value) => function_value,
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);
modify_refcount_union_help(
@ -1700,9 +1779,9 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
.build_load(tag_id_ptr, "load_tag_id")
.into_int_value();
let tag_id_u8 = env
.builder
.build_int_cast(tag_id, env.context.i8_type(), "tag_id_u8");
let tag_id_u8 =
env.builder
.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
let mut cases = Vec::with_capacity_in(tags.len(), env.arena);

View file

@ -594,6 +594,10 @@ impl<'a> WasmBackend<'a> {
index,
} => 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(_) => {
todo!("Expression `{}`", expr.to_pretty(100))
}

View file

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

View file

@ -688,6 +688,10 @@ impl<'a> LowLevelCall<'a> {
Hash => todo!("{:?}", self.lowlevel),
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 => {
internal_error!(
"Tried to apply `==` to RecursivePointer values {:?}",

View file

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

View file

@ -1198,6 +1198,12 @@ define_builtins! {
13 SET_WALK_USER_FUNCTION: "#walk_user_function"
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
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, .. } => {
self.own_var(z);
self.own_var(*x);
@ -1011,6 +1028,10 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
ExpectTrue => arena.alloc_slice_copy(&[irrelevant]),
BoxExpr | UnboxExpr => {
unreachable!("These lowlevel operations are turned into mono Expr's")
}
PtrCast | RefCountInc | RefCountDec => {
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::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::Boxed(inner_layout) => eq_boxed(root, ident_ids, ctx, inner_layout),
Layout::LambdaSet(_) => unreachable!("`==` is not defined on functions"),
Layout::RecursivePointer => {
unreachable!(
@ -528,6 +529,15 @@ fn eq_tag_fields<'a>(
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
/// 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`.

View file

@ -378,7 +378,13 @@ impl<'a> CodeGenHelp<'a> {
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) => {
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::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)
}
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())
}
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);
}
ExprBox { symbol } | ExprUnbox { symbol } => {
result.insert(*symbol);
}
EmptyArray | RuntimeErrorFunction(_) | Literal(_) => {}
GetTagId {
@ -756,6 +760,28 @@ impl<'a> Context<'a> {
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 is always stack-allocated
// function pointers are persistent

View file

@ -1505,6 +1505,14 @@ pub enum Expr<'a> {
},
EmptyArray,
ExprBox {
symbol: Symbol,
},
ExprUnbox {
symbol: Symbol,
},
Reuse {
symbol: Symbol,
update_tag_id: bool,
@ -1682,6 +1690,10 @@ impl<'a> Expr<'a> {
.text("GetTagId ")
.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 {
tag_id,
structure,
@ -4615,6 +4627,18 @@ pub fn with_hole<'a>(
let xs = arg_symbols[0];
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 {
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 {
index,
structure,

View file

@ -250,6 +250,7 @@ pub enum Layout<'a> {
field_order_hash: FieldOrderHash,
field_layouts: &'a [Layout<'a>],
},
Boxed(&'a Layout<'a>),
Union(UnionLayout<'a>),
LambdaSet(LambdaSet<'a>),
RecursivePointer,
@ -997,7 +998,7 @@ impl<'a> Layout<'a> {
}
}
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!
false
}
@ -1066,6 +1067,7 @@ impl<'a> Layout<'a> {
.runtime_representation()
.stack_size_without_alignment(target_info),
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),
Layout::Builtin(builtin) => builtin.alignment_bytes(target_info),
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()
.allocation_alignment_bytes(target_info),
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(),
RecursivePointer => true,
Boxed(_) => true,
}
}
@ -1196,6 +1201,10 @@ impl<'a> Layout<'a> {
Union(union_layout) => union_layout.to_doc(alloc, parens),
LambdaSet(lambda_set) => lambda_set.runtime_representation().to_doc(alloc, parens),
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::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::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!(
"TODO layout_from_flat_type for Apply({:?}, {:?})",

View file

@ -239,6 +239,12 @@ fn insert_reset<'a>(
stack.push((symbol, expr, expr_layout));
stmt = rest;
}
ExprBox { .. } | ExprUnbox { .. } => {
// TODO
break;
}
Literal(_)
| Call(_)
| Tag { .. }
@ -620,6 +626,8 @@ fn has_live_var_expr<'a>(expr: &'a Expr<'a>, needle: Symbol) -> bool {
symbol, arguments, ..
} => needle == *symbol || arguments.iter().any(|s| *s == needle),
Expr::Reset { symbol, .. } => needle == *symbol,
Expr::ExprBox { symbol, .. } => needle == *symbol,
Expr::ExprUnbox { symbol, .. } => needle == *symbol,
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]
#[cfg(any(feature = "gen-llvm"))]
fn list_walk_until_even_prefix_sum() {

View file

@ -2781,3 +2781,35 @@ fn to_float_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
)
}
#[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)]
fn result_alias_content(a: SolvedType, e: SolvedType) -> SolvedType {
SolvedType::TagUnion(

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(
gpu_device: &wgpu::Device,
@ -59,7 +59,7 @@ pub fn create_rect_buffers(
});
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,
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
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
Event::WindowEvent {

View file

@ -438,6 +438,7 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
unreachable!("RecursivePointers can only be inside structures")
}
Layout::LambdaSet(_) => Ok(OPAQUE_FUNCTION),
Layout::Boxed(_inner) => todo!(),
};
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.
3 Booly : [ Yes, No, Maybe ]
^^^^^^^^^^^^^^^^^^^^^^^^^^
1 Booly : [ Yes, No ]
^^^^^^^^^^^^^^^^^^^
If you didn't intend on using `Booly` then remove it so future readers
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.
1 Booly : [ Yes, No ]
^^^^^^^^^^^^^^^^^^^
3 Booly : [ Yes, No, Maybe ]
^^^^^^^^^^^^^^^^^^^^^^^^^^
If you didn't intend on using `Booly` then remove it so future readers
of your code don't wonder why it is there.
@ -1544,10 +1544,10 @@ mod test_reporting {
Did you mean one of these?
Box
Bool
U8
F64
Nat
"#
),
)
@ -2003,8 +2003,8 @@ mod test_reporting {
Ok
U8
Box
f
I8
"#
),
)
@ -3727,10 +3727,10 @@ mod test_reporting {
Is there an import missing? Perhaps there is a typo. Did you mean one
of these?
Box
Bool
Num
Set
Str
"#
),
)
@ -7984,18 +7984,18 @@ I need all branches in an `if` to have the same type!
r#"
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:
Bar
Foo
Bar
Recursion in aliases is only allowed if recursion happens behind a
@ -8185,7 +8185,7 @@ I need all branches in an `if` to have the same type!
Test
List
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,
# 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.
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]
target-arm = []