Emit runtime error when tag unions have an error type

Closes #3266
This commit is contained in:
Ayaz Hafiz 2022-07-22 15:46:09 -04:00
parent 53e7a41f27
commit 5ad04dcd2c
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
6 changed files with 80 additions and 31 deletions

View file

@ -1084,7 +1084,8 @@ fn type_to_union_tags<'a>(
let ext = { let ext = {
let (it, ext) = let (it, ext) =
roc_types::types::gather_tags_unsorted_iter(subs, UnionTags::default(), temp_ext_var); roc_types::types::gather_tags_unsorted_iter(subs, UnionTags::default(), temp_ext_var)
.expect("not a tag union");
tag_vars.extend(it.map(|(n, v)| (n.clone(), v))); tag_vars.extend(it.map(|(n, v)| (n.clone(), v)));
tag_vars.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); tag_vars.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));

View file

@ -3056,7 +3056,9 @@ pub fn ext_var_is_empty_tag_union(subs: &Subs, ext_var: Variable) -> bool {
// the ext_var is empty // the ext_var is empty
let mut ext_fields = std::vec::Vec::new(); let mut ext_fields = std::vec::Vec::new();
match roc_types::pretty_print::chase_ext_tag_union(subs, ext_var, &mut ext_fields) { match roc_types::pretty_print::chase_ext_tag_union(subs, ext_var, &mut ext_fields) {
Ok(()) | Err((_, Content::FlexVar(_) | Content::RigidVar(_))) => ext_fields.is_empty(), Ok(()) | Err((_, Content::FlexVar(_) | Content::RigidVar(_) | Content::Error)) => {
ext_fields.is_empty()
}
Err(content) => panic!("invalid content in ext_var: {:?}", content), Err(content) => panic!("invalid content in ext_var: {:?}", content),
} }
} }

View file

@ -2807,7 +2807,8 @@ fn type_to_variable<'a>(
subs, subs,
UnionTags::default(), UnionTags::default(),
temp_ext_var, temp_ext_var,
); )
.expect("extension var could not be seen as a tag union");
for _ in it { for _ in it {
unreachable!("we assert that the ext var is empty; otherwise we'd already know it was a tag union!"); unreachable!("we assert that the ext var is empty; otherwise we'd already know it was a tag union!");
@ -3351,7 +3352,8 @@ fn type_to_union_tags<'a>(
subs, subs,
UnionTags::default(), UnionTags::default(),
temp_ext_var, temp_ext_var,
); )
.expect("extension var could not be seen as tag union");
tag_vars.extend(it.map(|(n, v)| (n.clone(), v))); tag_vars.extend(it.map(|(n, v)| (n.clone(), v)));

View file

@ -1861,3 +1861,27 @@ fn issue_3560_newtype_tag_constructor_has_nested_constructor_with_no_payload() {
RocStr RocStr
) )
} }
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[should_panic(expected = r#"Roc failed with message: "Erroneous: Expr::Closure""#)]
fn error_type_in_tag_union_payload() {
assert_evals_to!(
indoc!(
r#"
f : ([] -> Bool) -> Bool
f = \fun ->
if True then
fun 42
else
False
f (\x -> x)
"#
),
0,
u8,
|x| x,
true // ignore type errors
)
}

View file

@ -2702,7 +2702,8 @@ impl UnionTags {
subs: &'a Subs, subs: &'a Subs,
ext: Variable, ext: Variable,
) -> impl Iterator<Item = (&TagName, &[Variable])> + 'a { ) -> impl Iterator<Item = (&TagName, &[Variable])> + 'a {
let (it, _) = crate::types::gather_tags_unsorted_iter(subs, *self, ext); let (it, _) =
crate::types::gather_tags_unsorted_iter(subs, *self, ext).expect("not a tag union");
let f = move |(label, slice): (_, SubsSlice<Variable>)| (label, subs.get_subs_slice(slice)); let f = move |(label, slice): (_, SubsSlice<Variable>)| (label, subs.get_subs_slice(slice));
@ -2715,7 +2716,8 @@ impl UnionTags {
subs: &'a Subs, subs: &'a Subs,
ext: Variable, ext: Variable,
) -> (UnsortedUnionLabels<'a, TagName>, Variable) { ) -> (UnsortedUnionLabels<'a, TagName>, Variable) {
let (it, ext) = crate::types::gather_tags_unsorted_iter(subs, *self, ext); let (it, ext) =
crate::types::gather_tags_unsorted_iter(subs, *self, ext).expect("not a tag union");
let f = move |(label, slice): (_, SubsSlice<Variable>)| (label, subs.get_subs_slice(slice)); let f = move |(label, slice): (_, SubsSlice<Variable>)| (label, subs.get_subs_slice(slice));
let it = it.map(f); let it = it.map(f);
@ -2741,7 +2743,8 @@ impl UnionTags {
ext, ext,
) )
} else { } else {
let union_structure = crate::types::gather_tags(subs, *self, ext); let union_structure =
crate::types::gather_tags(subs, *self, ext).expect("not a tag union");
( (
Box::new(union_structure.fields.into_iter()), Box::new(union_structure.fields.into_iter()),
@ -2767,7 +2770,8 @@ impl UnionTags {
ext, ext,
) )
} else { } else {
let (fields, ext) = crate::types::gather_tags_slices(subs, *self, ext); let (fields, ext) =
crate::types::gather_tags_slices(subs, *self, ext).expect("not a tag union");
(Box::new(fields.into_iter()), ext) (Box::new(fields.into_iter()), ext)
} }

View file

@ -2674,14 +2674,28 @@ pub fn gather_fields(
}) })
} }
#[derive(Debug)]
pub enum GatherTagsError {
NotATagUnion(Variable),
}
/// Gathers tag payloads of a type, assuming it is a tag.
///
/// If the given type is unbound or an error, no payloads are returned.
///
/// If the given type cannot be seen as a tag, unbound type, or error, this
/// function returns an error.
pub fn gather_tags_unsorted_iter( pub fn gather_tags_unsorted_iter(
subs: &Subs, subs: &Subs,
other_fields: UnionTags, other_fields: UnionTags,
mut var: Variable, mut var: Variable,
) -> ( ) -> Result<
(
impl Iterator<Item = (&TagName, VariableSubsSlice)> + '_, impl Iterator<Item = (&TagName, VariableSubsSlice)> + '_,
Variable, Variable,
) { ),
GatherTagsError,
> {
use crate::subs::Content::*; use crate::subs::Content::*;
use crate::subs::FlatType::*; use crate::subs::FlatType::*;
@ -2711,20 +2725,18 @@ pub fn gather_tags_unsorted_iter(
} }
Alias(_, _, actual_var, _) => { Alias(_, _, actual_var, _) => {
// TODO according to elm/compiler: "TODO may be dropping useful alias info here"
var = *actual_var; var = *actual_var;
} }
Structure(EmptyTagUnion) => break, Structure(EmptyTagUnion) => break,
FlexVar(_) => break, FlexVar(_) => break,
// TODO investigate this likely can happen when there is a type error // TODO investigate, this likely can happen when there is a type error
RigidVar(_) => break, RigidVar(_) => break,
other => unreachable!( Error => break,
"something weird ended up in a tag union type: {:?} at {:?}",
other, var _ => return Err(GatherTagsError::NotATagUnion(var)),
),
} }
} }
@ -2738,15 +2750,15 @@ pub fn gather_tags_unsorted_iter(
(tag_name, subs_slice) (tag_name, subs_slice)
}); });
(it, var) Ok((it, var))
} }
pub fn gather_tags_slices( pub fn gather_tags_slices(
subs: &Subs, subs: &Subs,
other_fields: UnionTags, other_fields: UnionTags,
var: Variable, var: Variable,
) -> (Vec<(TagName, VariableSubsSlice)>, Variable) { ) -> Result<(Vec<(TagName, VariableSubsSlice)>, Variable), GatherTagsError> {
let (it, ext) = gather_tags_unsorted_iter(subs, other_fields, var); let (it, ext) = gather_tags_unsorted_iter(subs, other_fields, var)?;
let mut result: Vec<_> = it let mut result: Vec<_> = it
.map(|(ref_label, field): (_, VariableSubsSlice)| (ref_label.clone(), field)) .map(|(ref_label, field): (_, VariableSubsSlice)| (ref_label.clone(), field))
@ -2754,11 +2766,15 @@ pub fn gather_tags_slices(
result.sort_by(|(a, _), (b, _)| a.cmp(b)); result.sort_by(|(a, _), (b, _)| a.cmp(b));
(result, ext) Ok((result, ext))
} }
pub fn gather_tags(subs: &Subs, other_fields: UnionTags, var: Variable) -> TagUnionStructure { pub fn gather_tags(
let (it, ext) = gather_tags_unsorted_iter(subs, other_fields, var); subs: &Subs,
other_fields: UnionTags,
var: Variable,
) -> Result<TagUnionStructure, GatherTagsError> {
let (it, ext) = gather_tags_unsorted_iter(subs, other_fields, var)?;
let mut result: Vec<_> = it let mut result: Vec<_> = it
.map(|(ref_label, field): (_, VariableSubsSlice)| { .map(|(ref_label, field): (_, VariableSubsSlice)| {
@ -2768,10 +2784,10 @@ pub fn gather_tags(subs: &Subs, other_fields: UnionTags, var: Variable) -> TagUn
result.sort_by(|(a, _), (b, _)| a.cmp(b)); result.sort_by(|(a, _), (b, _)| a.cmp(b));
TagUnionStructure { Ok(TagUnionStructure {
fields: result, fields: result,
ext, ext,
} })
} }
fn instantiate_lambda_sets_as_unspecialized( fn instantiate_lambda_sets_as_unspecialized(