mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 00:24:34 +00:00
simple implementation of NullableUnwrapped
This commit is contained in:
parent
2ce35cca28
commit
c1cf43eda3
4 changed files with 261 additions and 44 deletions
|
@ -9,8 +9,8 @@ use crate::llvm::build_str::{
|
||||||
};
|
};
|
||||||
use crate::llvm::compare::{build_eq, build_neq};
|
use crate::llvm::compare::{build_eq, build_neq};
|
||||||
use crate::llvm::convert::{
|
use crate::llvm::convert::{
|
||||||
basic_type_from_builtin, basic_type_from_layout, block_of_memory, collection, get_fn_type,
|
basic_type_from_builtin, basic_type_from_layout, block_of_memory, block_of_memory_slices,
|
||||||
get_ptr_type, ptr_int,
|
collection, get_fn_type, get_ptr_type, ptr_int,
|
||||||
};
|
};
|
||||||
use crate::llvm::refcounting::{
|
use crate::llvm::refcounting::{
|
||||||
decrement_refcount_layout, increment_refcount_layout, refcount_is_one_comparison,
|
decrement_refcount_layout, increment_refcount_layout, refcount_is_one_comparison,
|
||||||
|
@ -1136,6 +1136,109 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
data_ptr.into()
|
data_ptr.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Tag {
|
||||||
|
arguments,
|
||||||
|
tag_layout:
|
||||||
|
Layout::Union(UnionLayout::NullableUnwrapped {
|
||||||
|
nullable_id,
|
||||||
|
other_fields,
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
union_size,
|
||||||
|
tag_id,
|
||||||
|
tag_name,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let tag_struct_type =
|
||||||
|
block_of_memory_slices(env.context, &[other_fields], env.ptr_bytes);
|
||||||
|
|
||||||
|
if *tag_id == *nullable_id as u8 {
|
||||||
|
let output_type = tag_struct_type.ptr_type(AddressSpace::Generic);
|
||||||
|
|
||||||
|
return output_type.const_null().into();
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert!(*union_size > 1);
|
||||||
|
let ptr_size = env.ptr_bytes;
|
||||||
|
|
||||||
|
let ctx = env.context;
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
// Determine types
|
||||||
|
let num_fields = arguments.len() + 1;
|
||||||
|
let mut field_types = Vec::with_capacity_in(num_fields, env.arena);
|
||||||
|
let mut field_vals = Vec::with_capacity_in(num_fields, env.arena);
|
||||||
|
|
||||||
|
let tag_field_layouts = if let TagName::Closure(_) = tag_name {
|
||||||
|
// closures ignore (and do not store) the discriminant
|
||||||
|
panic!()
|
||||||
|
} else {
|
||||||
|
if (*tag_id != 0) == *nullable_id {
|
||||||
|
&[] as &[_]
|
||||||
|
} else {
|
||||||
|
other_fields
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
debug_assert_eq!(arguments.len(), tag_field_layouts.len());
|
||||||
|
|
||||||
|
for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) {
|
||||||
|
let val = load_symbol(env, scope, field_symbol);
|
||||||
|
|
||||||
|
// Zero-sized fields have no runtime representation.
|
||||||
|
// The layout of the struct expects them to be dropped!
|
||||||
|
if !tag_field_layout.is_dropped_because_empty() {
|
||||||
|
let field_type =
|
||||||
|
basic_type_from_layout(env.arena, env.context, tag_field_layout, ptr_size);
|
||||||
|
|
||||||
|
field_types.push(field_type);
|
||||||
|
|
||||||
|
if let Layout::RecursivePointer = tag_field_layout {
|
||||||
|
debug_assert!(val.is_pointer_value());
|
||||||
|
|
||||||
|
// we store recursive pointers as `i64*`
|
||||||
|
let ptr = cast_basic_basic(
|
||||||
|
builder,
|
||||||
|
val,
|
||||||
|
ctx.i64_type().ptr_type(AddressSpace::Generic).into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
field_vals.push(ptr);
|
||||||
|
} else {
|
||||||
|
// this check fails for recursive tag unions, but can be helpful while debugging
|
||||||
|
// debug_assert_eq!(tag_field_layout, val_layout);
|
||||||
|
|
||||||
|
field_vals.push(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the struct_type
|
||||||
|
let data_ptr = reserve_with_refcount(
|
||||||
|
env,
|
||||||
|
&Layout::Union(UnionLayout::NonRecursive(&[other_fields])),
|
||||||
|
);
|
||||||
|
|
||||||
|
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||||
|
let struct_ptr = cast_basic_basic(
|
||||||
|
builder,
|
||||||
|
data_ptr.into(),
|
||||||
|
struct_type.ptr_type(AddressSpace::Generic).into(),
|
||||||
|
)
|
||||||
|
.into_pointer_value();
|
||||||
|
|
||||||
|
// Insert field exprs into struct_val
|
||||||
|
for (index, field_val) in field_vals.into_iter().enumerate() {
|
||||||
|
let field_ptr = builder
|
||||||
|
.build_struct_gep(struct_ptr, index as u32, "struct_gep")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
builder.build_store(field_ptr, field_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
data_ptr.into()
|
||||||
|
}
|
||||||
|
|
||||||
Tag { .. } => unreachable!("tags should have a Union or RecursiveUnion layout"),
|
Tag { .. } => unreachable!("tags should have a Union or RecursiveUnion layout"),
|
||||||
|
|
||||||
Reset(_) => todo!(),
|
Reset(_) => todo!(),
|
||||||
|
@ -1250,11 +1353,9 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
PointerValue(value) => {
|
PointerValue(value) => {
|
||||||
match structure_layout {
|
match structure_layout {
|
||||||
Layout::Union(UnionLayout::NullableWrapped {
|
Layout::Union(UnionLayout::NullableWrapped { nullable_id, .. })
|
||||||
nullable_id,
|
if *index == 0 =>
|
||||||
other_tags: fields,
|
{
|
||||||
..
|
|
||||||
}) if *index == 0 => {
|
|
||||||
let ptr = value;
|
let ptr = value;
|
||||||
let is_null = env.builder.build_is_null(ptr, "is_null");
|
let is_null = env.builder.build_is_null(ptr, "is_null");
|
||||||
|
|
||||||
|
@ -1280,16 +1381,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
{
|
{
|
||||||
env.builder.position_at_end(else_block);
|
env.builder.position_at_end(else_block);
|
||||||
// if there is only one other tag besides then nullable_id, we know that id statically
|
let tag_id = extract_tag_discriminant_ptr(env, ptr);
|
||||||
let tag_id = if fields.len() == 1 {
|
|
||||||
if *nullable_id == 1 {
|
|
||||||
ctx.i64_type().const_int(0, false)
|
|
||||||
} else {
|
|
||||||
ctx.i64_type().const_int(1, false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
extract_tag_discriminant_ptr(env, ptr)
|
|
||||||
};
|
|
||||||
env.builder.build_store(result, tag_id);
|
env.builder.build_store(result, tag_id);
|
||||||
env.builder.build_unconditional_branch(cont_block);
|
env.builder.build_unconditional_branch(cont_block);
|
||||||
}
|
}
|
||||||
|
@ -1298,6 +1390,23 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
env.builder.build_load(result, "load_result")
|
env.builder.build_load(result, "load_result")
|
||||||
}
|
}
|
||||||
|
Layout::Union(UnionLayout::NullableUnwrapped { nullable_id, .. })
|
||||||
|
if *index == 0 =>
|
||||||
|
{
|
||||||
|
let is_null = env.builder.build_is_null(value, "is_null");
|
||||||
|
|
||||||
|
let ctx = env.context;
|
||||||
|
|
||||||
|
let then_value = ctx.i64_type().const_int(*nullable_id as u64, false);
|
||||||
|
let else_value = ctx.i64_type().const_int(!*nullable_id as u64, false);
|
||||||
|
|
||||||
|
env.builder.build_select(
|
||||||
|
is_null,
|
||||||
|
then_value,
|
||||||
|
else_value,
|
||||||
|
"select_tag_id",
|
||||||
|
)
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let ptr = cast_basic_basic(
|
let ptr = cast_basic_basic(
|
||||||
builder,
|
builder,
|
||||||
|
|
|
@ -229,7 +229,7 @@ fn flatten<'a>(
|
||||||
tag_name,
|
tag_name,
|
||||||
layout,
|
layout,
|
||||||
} if union.alternatives.len() == 1
|
} if union.alternatives.len() == 1
|
||||||
&& !matches!(layout, Layout::Union(UnionLayout::NullableWrapped { .. })) =>
|
&& !matches!(layout, Layout::Union(UnionLayout::NullableWrapped { .. })| Layout::Union(UnionLayout::NullableUnwrapped { .. })) =>
|
||||||
{
|
{
|
||||||
// TODO ^ do we need to check that guard.is_none() here?
|
// TODO ^ do we need to check that guard.is_none() here?
|
||||||
|
|
||||||
|
@ -1020,9 +1020,8 @@ fn path_to_expr_help<'a>(
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
dbg!(nullable_id, tag_id);
|
dbg!(nullable_id, tag_id);
|
||||||
match (*tag_id as usize).cmp(&(*nullable_id as usize)) {
|
match (*tag_id as usize).cmp(&(*nullable_id as usize)) {
|
||||||
Ordering::Equal =>
|
Ordering::Equal => {
|
||||||
// the nullable tag is going to predent it stores a tag id
|
// the nullable tag is going to pretend it stores a tag id
|
||||||
{
|
|
||||||
&*env
|
&*env
|
||||||
.arena
|
.arena
|
||||||
.alloc([Layout::Builtin(crate::layout::TAG_SIZE)])
|
.alloc([Layout::Builtin(crate::layout::TAG_SIZE)])
|
||||||
|
@ -1033,16 +1032,15 @@ fn path_to_expr_help<'a>(
|
||||||
}
|
}
|
||||||
NullableUnwrapped {
|
NullableUnwrapped {
|
||||||
nullable_id,
|
nullable_id,
|
||||||
other_id: _,
|
|
||||||
other_fields,
|
other_fields,
|
||||||
} => {
|
} => {
|
||||||
let tag_id = *tag_id == 0;
|
let tag_id = *tag_id != 0;
|
||||||
|
|
||||||
if tag_id == *nullable_id {
|
if tag_id == *nullable_id {
|
||||||
// the nullable tag is going to predent it stores a tag id
|
// the nullable tag is going to pretend it stores a tag id
|
||||||
&[] as &[_]
|
&*env.arena.alloc([Layout::Builtin(crate::layout::TAG_SIZE)])
|
||||||
} else {
|
} else {
|
||||||
other_fields
|
*other_fields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1052,7 +1050,14 @@ fn path_to_expr_help<'a>(
|
||||||
other => env.arena.alloc([other.clone()]),
|
other => env.arena.alloc([other.clone()]),
|
||||||
};
|
};
|
||||||
|
|
||||||
debug_assert!(*index < field_layouts.len() as u64);
|
debug_assert!(
|
||||||
|
*index < field_layouts.len() as u64,
|
||||||
|
"{} {:?} {:?} {:?}",
|
||||||
|
index,
|
||||||
|
field_layouts,
|
||||||
|
&layout,
|
||||||
|
tag_id,
|
||||||
|
);
|
||||||
|
|
||||||
let inner_layout = match &field_layouts[*index as usize] {
|
let inner_layout = match &field_layouts[*index as usize] {
|
||||||
Layout::RecursivePointer => layout.clone(),
|
Layout::RecursivePointer => layout.clone(),
|
||||||
|
|
|
@ -2838,7 +2838,42 @@ pub fn with_hole<'a>(
|
||||||
|
|
||||||
(tag, layout)
|
(tag, layout)
|
||||||
}
|
}
|
||||||
NullableUnwrapped { .. } => todo!(),
|
NullableUnwrapped {
|
||||||
|
nullable_id,
|
||||||
|
nullable_name: _,
|
||||||
|
other_name: _,
|
||||||
|
other_fields,
|
||||||
|
} => {
|
||||||
|
// FIXME drop tag
|
||||||
|
let tag_id_symbol = env.unique_symbol();
|
||||||
|
opt_tag_id_symbol = Some(tag_id_symbol);
|
||||||
|
|
||||||
|
field_symbols = {
|
||||||
|
let mut temp =
|
||||||
|
Vec::with_capacity_in(field_symbols_temp.len() + 1, arena);
|
||||||
|
// FIXME drop tag
|
||||||
|
temp.push(tag_id_symbol);
|
||||||
|
|
||||||
|
temp.extend(field_symbols_temp.iter().map(|r| r.1));
|
||||||
|
|
||||||
|
temp.into_bump_slice()
|
||||||
|
};
|
||||||
|
|
||||||
|
let layout = Layout::Union(UnionLayout::NullableUnwrapped {
|
||||||
|
nullable_id,
|
||||||
|
other_fields,
|
||||||
|
});
|
||||||
|
|
||||||
|
let tag = Expr::Tag {
|
||||||
|
tag_layout: layout.clone(),
|
||||||
|
tag_name,
|
||||||
|
tag_id: tag_id as u8,
|
||||||
|
union_size,
|
||||||
|
arguments: field_symbols,
|
||||||
|
};
|
||||||
|
|
||||||
|
(tag, layout)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut stmt = Stmt::Let(assigned, tag, layout, hole);
|
let mut stmt = Stmt::Let(assigned, tag, layout, hole);
|
||||||
|
@ -6182,7 +6217,68 @@ fn from_can_pattern_help<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NullableUnwrapped { .. } => todo!(),
|
NullableUnwrapped {
|
||||||
|
other_fields,
|
||||||
|
nullable_id,
|
||||||
|
nullable_name,
|
||||||
|
other_name: _,
|
||||||
|
} => {
|
||||||
|
debug_assert!(!other_fields.is_empty());
|
||||||
|
|
||||||
|
dbg!(nullable_id, &nullable_name);
|
||||||
|
|
||||||
|
ctors.push(Ctor {
|
||||||
|
tag_id: TagId(nullable_id as u8),
|
||||||
|
name: nullable_name.clone(),
|
||||||
|
arity: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
ctors.push(Ctor {
|
||||||
|
tag_id: TagId(!nullable_id as u8),
|
||||||
|
name: nullable_name.clone(),
|
||||||
|
// FIXME drop tag
|
||||||
|
arity: other_fields.len() - 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
let union = crate::exhaustive::Union {
|
||||||
|
render_as: RenderAs::Tag,
|
||||||
|
alternatives: ctors,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||||
|
|
||||||
|
let it = if tag_name == &nullable_name {
|
||||||
|
[].iter()
|
||||||
|
} else {
|
||||||
|
// FIXME drop tag
|
||||||
|
argument_layouts[1..].iter()
|
||||||
|
};
|
||||||
|
|
||||||
|
for ((_, loc_pat), layout) in arguments.iter().zip(it) {
|
||||||
|
mono_args.push((
|
||||||
|
from_can_pattern_help(
|
||||||
|
env,
|
||||||
|
layout_cache,
|
||||||
|
&loc_pat.value,
|
||||||
|
assignments,
|
||||||
|
)?,
|
||||||
|
layout.clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let layout = Layout::Union(UnionLayout::NullableUnwrapped {
|
||||||
|
nullable_id,
|
||||||
|
other_fields,
|
||||||
|
});
|
||||||
|
|
||||||
|
Pattern::AppliedTag {
|
||||||
|
tag_name: tag_name.clone(),
|
||||||
|
tag_id: tag_id as u8,
|
||||||
|
arguments: mono_args,
|
||||||
|
union,
|
||||||
|
layout,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -56,7 +56,6 @@ pub enum UnionLayout<'a> {
|
||||||
/// e.g. `ConsList a : [ Nil, Cons a (ConsList a) ]`
|
/// e.g. `ConsList a : [ Nil, Cons a (ConsList a) ]`
|
||||||
NullableUnwrapped {
|
NullableUnwrapped {
|
||||||
nullable_id: bool,
|
nullable_id: bool,
|
||||||
other_id: bool,
|
|
||||||
other_fields: &'a [Layout<'a>],
|
other_fields: &'a [Layout<'a>],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1104,13 +1103,11 @@ fn layout_from_flat_type<'a>(
|
||||||
|
|
||||||
let union_layout = if let Some(tag_id) = nullable {
|
let union_layout = if let Some(tag_id) = nullable {
|
||||||
match tag_layouts.into_bump_slice() {
|
match tag_layouts.into_bump_slice() {
|
||||||
[one] if false => {
|
[one] => {
|
||||||
let nullable_id = tag_id == 0;
|
let nullable_id = tag_id != 0;
|
||||||
let other_id = !nullable_id;
|
|
||||||
|
|
||||||
UnionLayout::NullableUnwrapped {
|
UnionLayout::NullableUnwrapped {
|
||||||
nullable_id,
|
nullable_id,
|
||||||
other_id,
|
|
||||||
other_fields: one,
|
other_fields: one,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1224,11 +1221,10 @@ pub enum WrappedVariant<'a> {
|
||||||
sorted_tag_layouts: Vec<'a, (TagName, &'a [Layout<'a>])>,
|
sorted_tag_layouts: Vec<'a, (TagName, &'a [Layout<'a>])>,
|
||||||
},
|
},
|
||||||
NullableUnwrapped {
|
NullableUnwrapped {
|
||||||
nullable_id: i64,
|
nullable_id: bool,
|
||||||
nullable_name: TagName,
|
nullable_name: TagName,
|
||||||
other_id: i64,
|
|
||||||
other_name: TagName,
|
other_name: TagName,
|
||||||
other_arguments: &'a [Layout<'a>],
|
other_fields: &'a [Layout<'a>],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1274,16 +1270,15 @@ impl<'a> WrappedVariant<'a> {
|
||||||
NullableUnwrapped {
|
NullableUnwrapped {
|
||||||
nullable_id,
|
nullable_id,
|
||||||
nullable_name,
|
nullable_name,
|
||||||
other_id,
|
|
||||||
other_name,
|
other_name,
|
||||||
other_arguments,
|
other_fields,
|
||||||
} => {
|
} => {
|
||||||
if tag_name == nullable_name {
|
if tag_name == nullable_name {
|
||||||
(*nullable_id as u8, &[] as &[_])
|
(*nullable_id as u8, &[] as &[_])
|
||||||
} else {
|
} else {
|
||||||
debug_assert_eq!(other_name, tag_name);
|
debug_assert_eq!(other_name, tag_name);
|
||||||
|
|
||||||
(*other_id as u8, other_arguments.clone())
|
(!*nullable_id as u8, other_fields.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1504,10 +1499,22 @@ pub fn union_sorted_tags_help<'a>(
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let variant = if let Some((nullable_id, nullable_name)) = nullable {
|
let variant = if let Some((nullable_id, nullable_name)) = nullable {
|
||||||
WrappedVariant::NullableWrapped {
|
if answer.len() == 1 {
|
||||||
nullable_id,
|
let (other_name, other_arguments) = answer.drain(..).next().unwrap();
|
||||||
nullable_name,
|
let nullable_id = nullable_id != 0;
|
||||||
sorted_tag_layouts: answer,
|
|
||||||
|
WrappedVariant::NullableUnwrapped {
|
||||||
|
nullable_id,
|
||||||
|
nullable_name,
|
||||||
|
other_name,
|
||||||
|
other_fields: other_arguments,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
WrappedVariant::NullableWrapped {
|
||||||
|
nullable_id,
|
||||||
|
nullable_name,
|
||||||
|
sorted_tag_layouts: answer,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if is_recursive {
|
} else if is_recursive {
|
||||||
WrappedVariant::Recursive {
|
WrappedVariant::Recursive {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue