mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
Collect tags from extension variables during monomorphization
Fixes #2365
This commit is contained in:
parent
2b3f347eec
commit
b2f2fcd6a8
5 changed files with 194 additions and 85 deletions
|
@ -7,7 +7,7 @@ use roc_module::ident::{Lowercase, TagName};
|
|||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_problem::can::RuntimeError;
|
||||
use roc_types::subs::{
|
||||
Content, FlatType, RecordFields, Subs, UnionTags, Variable, VariableSubsSlice,
|
||||
Content, FlatType, RecordFields, Subs, UnionTags, UnsortedUnionTags, Variable,
|
||||
};
|
||||
use roc_types::types::{gather_fields_unsorted_iter, RecordField};
|
||||
use std::collections::hash_map::Entry;
|
||||
|
@ -1571,18 +1571,26 @@ fn layout_from_flat_type<'a>(
|
|||
}
|
||||
}
|
||||
TagUnion(tags, ext_var) => {
|
||||
let (tags, ext_var) = tags.unsorted_tags_and_ext(subs, ext_var);
|
||||
|
||||
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
|
||||
|
||||
Ok(layout_from_tag_union(arena, tags, subs, env.ptr_bytes))
|
||||
Ok(layout_from_tag_union(arena, &tags, subs, env.ptr_bytes))
|
||||
}
|
||||
FunctionOrTagUnion(tag_name, _, ext_var) => {
|
||||
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
|
||||
debug_assert!(
|
||||
ext_var_is_empty_tag_union(subs, ext_var),
|
||||
"If ext_var wasn't empty, this wouldn't be a FunctionOrTagUnion!"
|
||||
);
|
||||
|
||||
let tags = UnionTags::from_tag_name_index(tag_name);
|
||||
let union_tags = UnionTags::from_tag_name_index(tag_name);
|
||||
let (tags, _) = union_tags.unsorted_tags_and_ext(subs, ext_var);
|
||||
|
||||
Ok(layout_from_tag_union(arena, tags, subs, env.ptr_bytes))
|
||||
Ok(layout_from_tag_union(arena, &tags, subs, env.ptr_bytes))
|
||||
}
|
||||
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||
let (tags, ext_var) = tags.unsorted_tags_and_ext(subs, ext_var);
|
||||
|
||||
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
|
||||
|
||||
// some observations
|
||||
|
@ -1594,9 +1602,9 @@ fn layout_from_flat_type<'a>(
|
|||
// That means none of the optimizations for enums or single tag tag unions apply
|
||||
|
||||
let rec_var = subs.get_root_key_without_compacting(rec_var);
|
||||
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
|
||||
let mut tag_layouts = Vec::with_capacity_in(tags.tags.len(), arena);
|
||||
|
||||
let tags_vec = cheap_sort_tags(arena, tags, subs);
|
||||
let tags_vec = cheap_sort_tags(&tags);
|
||||
|
||||
let mut nullable = None;
|
||||
|
||||
|
@ -1610,7 +1618,7 @@ fn layout_from_flat_type<'a>(
|
|||
}
|
||||
|
||||
env.insert_seen(rec_var);
|
||||
for (index, (_name, variables)) in tags_vec.into_iter().enumerate() {
|
||||
for (index, &(_name, variables)) in tags_vec.iter().enumerate() {
|
||||
if matches!(nullable, Some(i) if i == index as TagIdIntType) {
|
||||
// don't add the nullable case
|
||||
continue;
|
||||
|
@ -1618,8 +1626,7 @@ fn layout_from_flat_type<'a>(
|
|||
|
||||
let mut tag_layout = Vec::with_capacity_in(variables.len() + 1, arena);
|
||||
|
||||
for var_index in variables {
|
||||
let var = subs[var_index];
|
||||
for &var in variables {
|
||||
// TODO does this cause problems with mutually recursive unions?
|
||||
if rec_var == subs.get_root_key_without_compacting(var) {
|
||||
tag_layout.push(Layout::RecursivePointer);
|
||||
|
@ -1902,13 +1909,14 @@ fn is_recursive_tag_union(layout: &Layout) -> bool {
|
|||
|
||||
fn union_sorted_tags_help_new<'a>(
|
||||
arena: &'a Bump,
|
||||
mut tags_vec: Vec<(&'_ TagName, VariableSubsSlice)>,
|
||||
tags_list: &[(&'_ TagName, &[Variable])],
|
||||
opt_rec_var: Option<Variable>,
|
||||
subs: &Subs,
|
||||
ptr_bytes: u32,
|
||||
) -> UnionVariant<'a> {
|
||||
// sort up front; make sure the ordering stays intact!
|
||||
tags_vec.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
|
||||
let mut tags_list = Vec::from_iter_in(tags_list.iter(), arena);
|
||||
tags_list.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
|
||||
|
||||
let mut env = Env {
|
||||
arena,
|
||||
|
@ -1917,27 +1925,26 @@ fn union_sorted_tags_help_new<'a>(
|
|||
ptr_bytes,
|
||||
};
|
||||
|
||||
match tags_vec.len() {
|
||||
match tags_list.len() {
|
||||
0 => {
|
||||
// trying to instantiate a type with no values
|
||||
UnionVariant::Never
|
||||
}
|
||||
1 => {
|
||||
let (tag_name, arguments) = tags_vec.remove(0);
|
||||
let &(tag_name, arguments) = tags_list.remove(0);
|
||||
let tag_name = tag_name.clone();
|
||||
|
||||
// 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_list.len(), arena);
|
||||
|
||||
// special-case NUM_AT_NUM: if its argument is a FlexVar, make it Int
|
||||
match tag_name {
|
||||
TagName::Private(Symbol::NUM_AT_NUM) => {
|
||||
let var = subs[arguments.into_iter().next().unwrap()];
|
||||
let var = arguments[0];
|
||||
layouts.push(unwrap_num_tag(subs, var, ptr_bytes).expect("invalid num layout"));
|
||||
}
|
||||
_ => {
|
||||
for var_index in arguments {
|
||||
let var = subs[var_index];
|
||||
for &var in arguments {
|
||||
match Layout::from_var(&mut env, var) {
|
||||
Ok(layout) => {
|
||||
layouts.push(layout);
|
||||
|
@ -1980,7 +1987,7 @@ fn union_sorted_tags_help_new<'a>(
|
|||
}
|
||||
num_tags => {
|
||||
// default path
|
||||
let mut answer = Vec::with_capacity_in(tags_vec.len(), arena);
|
||||
let mut answer = Vec::with_capacity_in(tags_list.len(), arena);
|
||||
let mut has_any_arguments = false;
|
||||
|
||||
let mut nullable: Option<(TagIdIntType, TagName)> = None;
|
||||
|
@ -1988,7 +1995,7 @@ fn union_sorted_tags_help_new<'a>(
|
|||
// only recursive tag unions can be nullable
|
||||
let is_recursive = opt_rec_var.is_some();
|
||||
if is_recursive && GENERATE_NULLABLE {
|
||||
for (index, (name, variables)) in tags_vec.iter().enumerate() {
|
||||
for (index, (name, variables)) in tags_list.iter().enumerate() {
|
||||
if variables.is_empty() {
|
||||
nullable = Some((index as TagIdIntType, (*name).clone()));
|
||||
break;
|
||||
|
@ -1996,7 +2003,7 @@ fn union_sorted_tags_help_new<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
for (index, (tag_name, arguments)) in tags_vec.into_iter().enumerate() {
|
||||
for (index, &(tag_name, arguments)) in tags_list.into_iter().enumerate() {
|
||||
// reserve space for the tag discriminant
|
||||
if matches!(nullable, Some((i, _)) if i as usize == index) {
|
||||
debug_assert!(arguments.is_empty());
|
||||
|
@ -2005,8 +2012,7 @@ fn union_sorted_tags_help_new<'a>(
|
|||
|
||||
let mut arg_layouts = Vec::with_capacity_in(arguments.len() + 1, arena);
|
||||
|
||||
for var_index in arguments {
|
||||
let var = subs[var_index];
|
||||
for &var in arguments {
|
||||
match Layout::from_var(&mut env, var) {
|
||||
Ok(layout) => {
|
||||
has_any_arguments = true;
|
||||
|
@ -2317,38 +2323,19 @@ pub fn union_sorted_tags_help<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn cheap_sort_tags<'a, 'b>(
|
||||
arena: &'a Bump,
|
||||
tags: UnionTags,
|
||||
subs: &'b Subs,
|
||||
) -> Vec<'a, (&'b TagName, VariableSubsSlice)> {
|
||||
let mut tags_vec = Vec::with_capacity_in(tags.len(), arena);
|
||||
|
||||
for (tag_index, index) in tags.iter_all() {
|
||||
let tag = &subs[tag_index];
|
||||
let slice = subs[index];
|
||||
|
||||
tags_vec.push((tag, slice));
|
||||
}
|
||||
|
||||
tags_vec
|
||||
fn cheap_sort_tags<'a>(tags: &'a UnsortedUnionTags) -> &'a [(&'a TagName, &'a [Variable])] {
|
||||
&tags.tags
|
||||
}
|
||||
|
||||
fn layout_from_newtype<'a>(
|
||||
arena: &'a Bump,
|
||||
tags: UnionTags,
|
||||
tags: &UnsortedUnionTags,
|
||||
subs: &Subs,
|
||||
ptr_bytes: u32,
|
||||
) -> Layout<'a> {
|
||||
debug_assert!(tags.is_newtype_wrapper(subs));
|
||||
|
||||
let slice_index = tags.variables().into_iter().next().unwrap();
|
||||
let slice = subs[slice_index];
|
||||
let var_index = slice.into_iter().next().unwrap();
|
||||
let var = subs[var_index];
|
||||
|
||||
let tag_name_index = tags.tag_names().into_iter().next().unwrap();
|
||||
let tag_name = &subs[tag_name_index];
|
||||
let (tag_name, var) = tags.get_newtype(subs);
|
||||
|
||||
if tag_name == &TagName::Private(Symbol::NUM_AT_NUM) {
|
||||
unwrap_num_tag(subs, var, ptr_bytes).expect("invalid Num argument")
|
||||
|
@ -2379,7 +2366,7 @@ fn layout_from_newtype<'a>(
|
|||
|
||||
fn layout_from_tag_union<'a>(
|
||||
arena: &'a Bump,
|
||||
tags: UnionTags,
|
||||
tags: &UnsortedUnionTags,
|
||||
subs: &Subs,
|
||||
ptr_bytes: u32,
|
||||
) -> Layout<'a> {
|
||||
|
@ -2389,14 +2376,13 @@ fn layout_from_tag_union<'a>(
|
|||
return layout_from_newtype(arena, tags, subs, ptr_bytes);
|
||||
}
|
||||
|
||||
let tags_vec = cheap_sort_tags(arena, tags, subs);
|
||||
let tags_vec = cheap_sort_tags(tags);
|
||||
|
||||
match tags_vec.get(0) {
|
||||
Some((tag_name, arguments)) if *tag_name == &TagName::Private(Symbol::NUM_AT_NUM) => {
|
||||
debug_assert_eq!(arguments.len(), 1);
|
||||
|
||||
let var_index = arguments.into_iter().next().unwrap();
|
||||
let var = subs[var_index];
|
||||
let &var = arguments.iter().next().unwrap();
|
||||
|
||||
unwrap_num_tag(subs, var, ptr_bytes).expect("invalid Num argument")
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue