simple implementation of NullableUnwrapped

This commit is contained in:
Folkert 2021-01-17 00:34:08 +01:00
parent 2ce35cca28
commit c1cf43eda3
4 changed files with 261 additions and 44 deletions

View file

@ -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,

View file

@ -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(),

View file

@ -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,
}
}
} }
} }
}; };

View file

@ -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,11 +1499,23 @@ 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 {
if answer.len() == 1 {
let (other_name, other_arguments) = answer.drain(..).next().unwrap();
let nullable_id = nullable_id != 0;
WrappedVariant::NullableUnwrapped {
nullable_id,
nullable_name,
other_name,
other_fields: other_arguments,
}
} else {
WrappedVariant::NullableWrapped { WrappedVariant::NullableWrapped {
nullable_id, nullable_id,
nullable_name, nullable_name,
sorted_tag_layouts: answer, sorted_tag_layouts: answer,
} }
}
} else if is_recursive { } else if is_recursive {
WrappedVariant::Recursive { WrappedVariant::Recursive {
sorted_tag_layouts: answer, sorted_tag_layouts: answer,