Merge pull request #3611 from rtfeldman/i3266

Emit runtime error when tag unions have an error type
This commit is contained in:
Folkert de Vries 2022-07-23 01:28:55 +02:00 committed by GitHub
commit d55d1b00d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 80 additions and 31 deletions

View file

@ -1084,7 +1084,8 @@ fn type_to_union_tags<'a>(
let 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.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
let mut ext_fields = std::vec::Vec::new();
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),
}
}

View file

@ -2807,7 +2807,8 @@ fn type_to_variable<'a>(
subs,
UnionTags::default(),
temp_ext_var,
);
)
.expect("extension var could not be seen as a tag union");
for _ in it {
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,
UnionTags::default(),
temp_ext_var,
);
)
.expect("extension var could not be seen as tag union");
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
)
}
#[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,
ext: Variable,
) -> 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));
@ -2715,7 +2716,8 @@ impl UnionTags {
subs: &'a Subs,
ext: 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 it = it.map(f);
@ -2741,7 +2743,8 @@ impl UnionTags {
ext,
)
} 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()),
@ -2767,7 +2770,8 @@ impl UnionTags {
ext,
)
} 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)
}

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(
subs: &Subs,
other_fields: UnionTags,
mut var: Variable,
) -> (
impl Iterator<Item = (&TagName, VariableSubsSlice)> + '_,
Variable,
) {
) -> Result<
(
impl Iterator<Item = (&TagName, VariableSubsSlice)> + '_,
Variable,
),
GatherTagsError,
> {
use crate::subs::Content::*;
use crate::subs::FlatType::*;
@ -2697,34 +2711,32 @@ pub fn gather_tags_unsorted_iter(
Structure(FunctionOrTagUnion(_tag_name_index, _, _sub_ext)) => {
todo!("this variant does not use SOA yet, and therefore this case is unreachable right now")
// let sub_fields: UnionTags = (*tag_name_index).into();
// stack.push(sub_fields);
// let sub_fields: UnionTags = (*tag_name_index).into();
// stack.push(sub_fields);
//
// var = *sub_ext;
// var = *sub_ext;
}
Structure(RecursiveTagUnion(_, _sub_fields, _sub_ext)) => {
todo!("this variant does not use SOA yet, and therefore this case is unreachable right now")
// stack.push(*sub_fields);
// stack.push(*sub_fields);
//
// var = *sub_ext;
// var = *sub_ext;
}
Alias(_, _, actual_var, _) => {
// TODO according to elm/compiler: "TODO may be dropping useful alias info here"
var = *actual_var;
}
Structure(EmptyTagUnion) => 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,
other => unreachable!(
"something weird ended up in a tag union type: {:?} at {:?}",
other, var
),
Error => break,
_ => return Err(GatherTagsError::NotATagUnion(var)),
}
}
@ -2738,15 +2750,15 @@ pub fn gather_tags_unsorted_iter(
(tag_name, subs_slice)
});
(it, var)
Ok((it, var))
}
pub fn gather_tags_slices(
subs: &Subs,
other_fields: UnionTags,
var: Variable,
) -> (Vec<(TagName, VariableSubsSlice)>, Variable) {
let (it, ext) = gather_tags_unsorted_iter(subs, other_fields, var);
) -> Result<(Vec<(TagName, VariableSubsSlice)>, Variable), GatherTagsError> {
let (it, ext) = gather_tags_unsorted_iter(subs, other_fields, var)?;
let mut result: Vec<_> = it
.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, ext)
Ok((result, ext))
}
pub fn gather_tags(subs: &Subs, other_fields: UnionTags, var: Variable) -> TagUnionStructure {
let (it, ext) = gather_tags_unsorted_iter(subs, other_fields, var);
pub fn gather_tags(
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
.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));
TagUnionStructure {
Ok(TagUnionStructure {
fields: result,
ext,
}
})
}
fn instantiate_lambda_sets_as_unspecialized(