mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
clean up tag pattern match conversion
This commit is contained in:
parent
a7b5768c47
commit
b9613fcdc5
4 changed files with 121 additions and 139 deletions
|
@ -446,13 +446,13 @@ mod test_gen {
|
||||||
// parsing the source, so that there's no chance their passing
|
// parsing the source, so that there's no chance their passing
|
||||||
// or failing depends on leftover state from the previous one.
|
// or failing depends on leftover state from the previous one.
|
||||||
{
|
{
|
||||||
assert_crane_evals_to!($src, $expected, $ty, (|val| val));
|
// assert_crane_evals_to!($src, $expected, $ty, (|val| val));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
assert_llvm_evals_to!($src, $expected, $ty, (|val| val));
|
assert_llvm_evals_to!($src, $expected, $ty, (|val| val));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
assert_opt_evals_to!($src, $expected, $ty, (|val| val));
|
// assert_opt_evals_to!($src, $expected, $ty, (|val| val));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||||
|
@ -1532,7 +1532,7 @@ mod test_gen {
|
||||||
r#"
|
r#"
|
||||||
Maybe a : [ Nothing, Just a ]
|
Maybe a : [ Nothing, Just a ]
|
||||||
|
|
||||||
x : Maybe (Maybe a)
|
x : Maybe (Maybe Int)
|
||||||
x = Just (Just 41)
|
x = Just (Just 41)
|
||||||
|
|
||||||
when x is
|
when x is
|
||||||
|
|
|
@ -842,7 +842,7 @@ fn store_pattern<'a>(
|
||||||
}
|
}
|
||||||
Underscore => {
|
Underscore => {
|
||||||
// Since _ is never read, it's safe to reassign it.
|
// Since _ is never read, it's safe to reassign it.
|
||||||
stored.push((Symbol::UNDERSCORE, layout, Expr::Load(outer_symbol)))
|
// stored.push((Symbol::UNDERSCORE, layout, Expr::Load(outer_symbol)))
|
||||||
}
|
}
|
||||||
IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral(_) => {}
|
IntLiteral(_) | FloatLiteral(_) | EnumLiteral { .. } | BitLiteral(_) => {}
|
||||||
AppliedTag {
|
AppliedTag {
|
||||||
|
@ -1335,26 +1335,59 @@ fn from_can_pattern<'a>(
|
||||||
arguments,
|
arguments,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let mut fields = std::vec::Vec::new();
|
use crate::layout::UnionVariant::*;
|
||||||
|
|
||||||
match roc_types::pretty_print::chase_ext_tag_union(env.subs, *whole_var, &mut fields) {
|
let variant =
|
||||||
Ok(()) | Err((_, Content::FlexVar(_))) => {}
|
crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs, env.pointer_size);
|
||||||
Err(content) => panic!("invalid content in ext_var: {:?}", content),
|
|
||||||
}
|
|
||||||
|
|
||||||
fields.sort();
|
match variant {
|
||||||
let tag_id = fields
|
Never => unreachable!("there is no pattern of type `[]`"),
|
||||||
|
Unit => Pattern::EnumLiteral {
|
||||||
|
tag_id: 0,
|
||||||
|
enum_size: 1,
|
||||||
|
},
|
||||||
|
BoolUnion { ttrue, .. } => Pattern::BitLiteral(tag_name == &ttrue),
|
||||||
|
ByteUnion(tag_names) => {
|
||||||
|
let tag_id = tag_names
|
||||||
.iter()
|
.iter()
|
||||||
.position(|(key, _)| key == tag_name)
|
.position(|key| key == tag_name)
|
||||||
.expect("tag must be in its own type");
|
.expect("tag must be in its own type");
|
||||||
|
|
||||||
let enum_size = fields.len();
|
Pattern::EnumLiteral {
|
||||||
|
tag_id: tag_id as u8,
|
||||||
|
enum_size: tag_names.len() as u8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Unwrapped(field_layouts) => {
|
||||||
|
let union = crate::pattern::Union {
|
||||||
|
alternatives: vec![Ctor {
|
||||||
|
name: tag_name.clone(),
|
||||||
|
arity: field_layouts.len(),
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
let mut ctors = std::vec::Vec::with_capacity(fields.len());
|
let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||||
for (tag_name, args) in &fields {
|
for ((_, loc_pat), layout) in arguments.iter().zip(field_layouts.iter()) {
|
||||||
|
mono_args.push((from_can_pattern(env, &loc_pat.value), layout.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let layout = Layout::Struct(field_layouts.into_bump_slice());
|
||||||
|
|
||||||
|
Pattern::AppliedTag {
|
||||||
|
tag_name: tag_name.clone(),
|
||||||
|
tag_id: 0,
|
||||||
|
arguments: mono_args,
|
||||||
|
union,
|
||||||
|
layout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Wrapped(tags) => {
|
||||||
|
let mut ctors = std::vec::Vec::with_capacity(tags.len());
|
||||||
|
for (tag_name, args) in &tags {
|
||||||
ctors.push(Ctor {
|
ctors.push(Ctor {
|
||||||
name: tag_name.clone(),
|
name: tag_name.clone(),
|
||||||
arity: args.len(),
|
// don't include tag discriminant in arity
|
||||||
|
arity: args.len() - 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1362,31 +1395,28 @@ fn from_can_pattern<'a>(
|
||||||
alternatives: ctors,
|
alternatives: ctors,
|
||||||
};
|
};
|
||||||
|
|
||||||
let fields_map: MutMap<_, _> = fields.into_iter().collect();
|
let (tag_id, (_, argument_layouts)) = tags
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, (key, _))| key == tag_name)
|
||||||
|
.expect("tag must be in its own type");
|
||||||
|
|
||||||
match crate::layout::layout_from_tag_union(
|
|
||||||
env.arena,
|
|
||||||
&fields_map,
|
|
||||||
env.subs,
|
|
||||||
env.pointer_size,
|
|
||||||
) {
|
|
||||||
Ok(Layout::Builtin(Builtin::Bool)) => Pattern::BitLiteral(tag_id != 0),
|
|
||||||
Ok(Layout::Builtin(Builtin::Byte)) => Pattern::EnumLiteral {
|
|
||||||
tag_id: tag_id as u8,
|
|
||||||
enum_size: enum_size as u8,
|
|
||||||
},
|
|
||||||
Ok(layout) => {
|
|
||||||
let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena);
|
let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||||
for (pat_var, loc_pat) in arguments {
|
// disregard the tag discriminant layout
|
||||||
let layout =
|
let it = argument_layouts[1..].iter();
|
||||||
Layout::from_var(env.arena, *pat_var, env.subs, env.pointer_size)
|
for ((_, loc_pat), layout) in arguments.iter().zip(it) {
|
||||||
.unwrap_or_else(|err| {
|
mono_args.push((from_can_pattern(env, &loc_pat.value), layout.clone()));
|
||||||
panic!("TODO turn pat_var into a RuntimeError {:?}", err)
|
|
||||||
});
|
|
||||||
|
|
||||||
mono_args.push((from_can_pattern(env, &loc_pat.value), layout));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut layouts: Vec<&'a [Layout<'a>]> =
|
||||||
|
Vec::with_capacity_in(tags.len(), env.arena);
|
||||||
|
|
||||||
|
for (_, arg_layouts) in tags.into_iter() {
|
||||||
|
layouts.push(arg_layouts);
|
||||||
|
}
|
||||||
|
|
||||||
|
let layout = Layout::Union(layouts.into_bump_slice());
|
||||||
|
|
||||||
Pattern::AppliedTag {
|
Pattern::AppliedTag {
|
||||||
tag_name: tag_name.clone(),
|
tag_name: tag_name.clone(),
|
||||||
tag_id: tag_id as u8,
|
tag_id: tag_id as u8,
|
||||||
|
@ -1395,7 +1425,6 @@ fn from_can_pattern<'a>(
|
||||||
layout,
|
layout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(()) => panic!("Invalid layout"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -287,7 +287,7 @@ fn layout_from_flat_type<'a>(
|
||||||
TagUnion(tags, ext_var) => {
|
TagUnion(tags, ext_var) => {
|
||||||
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
|
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
|
||||||
|
|
||||||
layout_from_tag_union(arena, &tags, subs, pointer_size)
|
Ok(layout_from_tag_union(arena, tags, subs, pointer_size))
|
||||||
}
|
}
|
||||||
RecursiveTagUnion(_, _, _) => {
|
RecursiveTagUnion(_, _, _) => {
|
||||||
panic!("TODO make Layout for non-empty Tag Union");
|
panic!("TODO make Layout for non-empty Tag Union");
|
||||||
|
@ -394,18 +394,27 @@ fn union_sorted_tags_help<'a>(
|
||||||
UnionVariant::ByteUnion(tag_names)
|
UnionVariant::ByteUnion(tag_names)
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
|
// special-case NUM_AT_NUM: if its argument is a FlexVar, make it Int
|
||||||
|
let (tag_name, arguments) = tags_vec.remove(0);
|
||||||
|
|
||||||
// just one tag in the union (but with arguments) can be a struct
|
// just one tag in the union (but with arguments) can be a struct
|
||||||
let mut layouts = Vec::with_capacity_in(tags_vec.len(), arena);
|
let mut layouts = Vec::with_capacity_in(tags_vec.len(), arena);
|
||||||
|
|
||||||
let arguments = tags_vec.remove(0).1;
|
match tag_name {
|
||||||
|
TagName::Private(Symbol::NUM_AT_NUM) => {
|
||||||
|
layouts.push(unwrap_num_tag(subs, arguments[0]).expect("invalid num layout"));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
for var in arguments.iter() {
|
for var in arguments.iter() {
|
||||||
let layout = Layout::from_var(arena, *var, subs, pointer_size)
|
let layout = Layout::from_var(arena, *var, subs, pointer_size)
|
||||||
.expect("invalid layout from var");
|
.expect("invalid layout from var");
|
||||||
layouts.push(layout);
|
layouts.push(layout);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
UnionVariant::Unwrapped(layouts)
|
UnionVariant::Unwrapped(layouts)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
// default path
|
// default path
|
||||||
let mut result = Vec::with_capacity_in(tags_vec.len(), arena);
|
let mut result = Vec::with_capacity_in(tags_vec.len(), arena);
|
||||||
|
@ -432,94 +441,38 @@ fn union_sorted_tags_help<'a>(
|
||||||
|
|
||||||
pub fn layout_from_tag_union<'a>(
|
pub fn layout_from_tag_union<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
tags: &MutMap<TagName, std::vec::Vec<Variable>>,
|
tags: MutMap<TagName, std::vec::Vec<Variable>>,
|
||||||
subs: &Subs,
|
subs: &Subs,
|
||||||
pointer_size: u32,
|
pointer_size: u32,
|
||||||
) -> Result<Layout<'a>, ()> {
|
) -> Layout<'a> {
|
||||||
match tags.len() {
|
use UnionVariant::*;
|
||||||
0 => {
|
|
||||||
panic!("TODO gracefully handle trying to instantiate Never");
|
|
||||||
}
|
|
||||||
// We can only unwrap a wrapper if it never becomes part of a bigger union
|
|
||||||
// therefore, the ext_var must be the literal empty tag union
|
|
||||||
1 => {
|
|
||||||
// This is a wrapper. Unwrap it!
|
|
||||||
let (tag_name, arguments) = tags.iter().next().unwrap();
|
|
||||||
|
|
||||||
match &tag_name {
|
let tags_vec: std::vec::Vec<_> = tags.into_iter().collect();
|
||||||
|
let first_tag = tags_vec[0].clone();
|
||||||
|
let variant = union_sorted_tags_help(arena, tags_vec, subs, pointer_size);
|
||||||
|
|
||||||
|
match variant {
|
||||||
|
Never => panic!("TODO gracefully handle trying to instantiate Never"),
|
||||||
|
Unit => Layout::Struct(&[]),
|
||||||
|
BoolUnion { .. } => Layout::Builtin(Builtin::Bool),
|
||||||
|
ByteUnion(_) => Layout::Builtin(Builtin::Byte),
|
||||||
|
Unwrapped(field_layouts) => match first_tag.0 {
|
||||||
TagName::Private(Symbol::NUM_AT_NUM) => {
|
TagName::Private(Symbol::NUM_AT_NUM) => {
|
||||||
|
let arguments = first_tag.1;
|
||||||
debug_assert!(arguments.len() == 1);
|
debug_assert!(arguments.len() == 1);
|
||||||
|
|
||||||
let var = arguments.iter().next().unwrap();
|
let var = arguments.iter().next().unwrap();
|
||||||
|
|
||||||
unwrap_num_tag(subs, *var)
|
unwrap_num_tag(subs, *var).expect("invalid Num argument")
|
||||||
}
|
}
|
||||||
TagName::Private(_) | TagName::Global(_) => {
|
_ => Layout::Struct(field_layouts.into_bump_slice()),
|
||||||
let mut arg_layouts = Vec::with_capacity_in(arguments.len(), arena);
|
},
|
||||||
|
Wrapped(tags) => {
|
||||||
|
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
|
||||||
|
|
||||||
for arg in arguments {
|
for (_, tag_layout) in tags {
|
||||||
arg_layouts.push(Layout::from_var(arena, *arg, subs, pointer_size)?);
|
tag_layouts.push(tag_layout);
|
||||||
}
|
|
||||||
|
|
||||||
let layouts = [arg_layouts.into_bump_slice()];
|
|
||||||
|
|
||||||
Ok(Layout::Union(arena.alloc(layouts)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// Check if we can turn this tag union into an enum
|
|
||||||
// The arguments of all tags must have size 0.
|
|
||||||
// That is trivially the case when there are no arguments
|
|
||||||
//
|
|
||||||
// [ Orange, Apple, Banana ]
|
|
||||||
//
|
|
||||||
// But when one-tag tag unions are optimized away, we can also use an enum for
|
|
||||||
//
|
|
||||||
// [ Foo [ Unit ], Bar [ Unit ] ]
|
|
||||||
|
|
||||||
let arguments_have_size_0 = || {
|
|
||||||
tags.iter().all(|(_, args)| {
|
|
||||||
args.iter().all(|var| {
|
|
||||||
Layout::from_var(arena, *var, subs, pointer_size)
|
|
||||||
.map(|v| v.stack_size(pointer_size))
|
|
||||||
== Ok(0)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
// up to 256 enum keys can be stored in a byte
|
|
||||||
if tags.len() <= std::u8::MAX as usize + 1 && arguments_have_size_0() {
|
|
||||||
if tags.len() <= 2 {
|
|
||||||
Ok(Layout::Builtin(Builtin::Bool))
|
|
||||||
} else {
|
|
||||||
// up to 256 enum tags can be stored in a byte
|
|
||||||
Ok(Layout::Builtin(Builtin::Byte))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let add_discriminant = tags.len() != 1;
|
|
||||||
let mut layouts = Vec::with_capacity_in(tags.len(), arena);
|
|
||||||
|
|
||||||
for arguments in tags.values() {
|
|
||||||
// add a field for the discriminant if there is more than one tag in the union
|
|
||||||
let mut arg_layouts = if add_discriminant {
|
|
||||||
let discriminant = Layout::Builtin(Builtin::Int64);
|
|
||||||
let mut result = Vec::with_capacity_in(arguments.len() + 1, arena);
|
|
||||||
result.push(discriminant);
|
|
||||||
result
|
|
||||||
} else {
|
|
||||||
Vec::with_capacity_in(arguments.len(), arena)
|
|
||||||
};
|
|
||||||
|
|
||||||
for arg in arguments {
|
|
||||||
arg_layouts.push(Layout::from_var(arena, *arg, subs, pointer_size)?);
|
|
||||||
}
|
|
||||||
|
|
||||||
layouts.push(arg_layouts.into_bump_slice());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Layout::Union(arena.alloc(layouts)))
|
|
||||||
}
|
}
|
||||||
|
Layout::Union(tag_layouts.into_bump_slice())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -281,7 +281,7 @@ mod test_mono {
|
||||||
CallByName(gen_symbol_4, &[(Int(4), Builtin(Int64))]),
|
CallByName(gen_symbol_4, &[(Int(4), Builtin(Int64))]),
|
||||||
Builtin(Int64),
|
Builtin(Int64),
|
||||||
)]),
|
)]),
|
||||||
Layout::Struct(&[("x".into(), Builtin(Int64))]),
|
Layout::Struct(&[Builtin(Int64)]),
|
||||||
)],
|
)],
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue