Add RocTagUnion::SingleTagUnion

This commit is contained in:
Richard Feldman 2022-08-05 13:49:24 -04:00
parent e9966e6551
commit 314d7a7952
No known key found for this signature in database
GPG key ID: 7E4127D1E4241798
2 changed files with 107 additions and 22 deletions

View file

@ -148,27 +148,15 @@ fn add_type(target_info: TargetInfo, id: TypeId, types: &Types, impls: &mut Impl
}
RocType::TagUnion(tag_union) => {
match tag_union {
RocTagUnion::Enumeration { tags, name, size } => {
if tags.len() == 1 {
// An enumeration with one tag is a zero-sized unit type, so
// represent it as a zero-sized struct (e.g. "struct Foo()").
let derive = derive_str(types.get_type(id), types, true);
let struct_name = type_name(id, types);
let body = format!("{derive}\nstruct {struct_name}();");
add_decl(impls, None, target_info, body);
} else {
add_enumeration(
name,
target_info,
types.get_type(id),
tags.iter(),
*size,
types,
impls,
)
}
}
RocTagUnion::Enumeration { tags, name, size } => add_enumeration(
name,
target_info,
types.get_type(id),
tags.iter(),
*size,
types,
impls,
),
RocTagUnion::NonRecursive {
tags,
name,
@ -236,6 +224,75 @@ fn add_type(target_info: TargetInfo, id: TypeId, types: &Types, impls: &mut Impl
RocTagUnion::NonNullableUnwrapped { .. } => {
todo!();
}
RocTagUnion::SingleTagUnion { name, tag_name } => {
// An enumeration with one tag is a zero-sized unit type, so
// represent it as a zero-sized struct (e.g. "struct Foo()").
let derive = derive_str(
&RocType::Struct {
// Deriving doesn't depend on the struct's name,
// so no need to clone name here.
name: String::new(),
fields: Vec::new(),
},
types,
false,
);
let body = format!("{derive}\npub struct {name}();");
add_decl(impls, None, target_info, body);
let opt_impl = Some(format!("impl {name}"));
add_decl(
impls,
opt_impl.clone(),
target_info,
format!(
r#"/// A tag named {tag_name}, which has no payload.
pub const {tag_name}: Self = Self();"#,
),
);
add_decl(
impls,
opt_impl.clone(),
target_info,
format!(
r#"/// Other `into_` methods return a payload, but since the {tag_name} tag
/// has no payload, this does nothing and is only here for completeness.
pub fn into_{tag_name}(self) {{
()
}}"#,
),
);
add_decl(
impls,
opt_impl.clone(),
target_info,
format!(
r#"/// Other `as` methods return a payload, but since the {tag_name} tag
/// has no payload, this does nothing and is only here for completeness.
pub unsafe fn as_{tag_name}(&self) {{
()
}}"#,
),
);
// The Debug impl for the single-tag union
{
let opt_impl = Some(format!("impl core::fmt::Debug for {name}"));
let buf = format!(
r#"fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {{
f.write_str("{name}::")?;
f.write_str("{tag_name}")
}}"#
);
add_decl(impls, opt_impl, target_info, buf);
}
}
}
}
// These types don't need to be declared in Rust.
@ -1294,6 +1351,7 @@ fn type_name(id: TypeId, types: &Types) -> String {
RocType::Struct { name, .. }
| RocType::TagUnionPayload { name, .. }
| RocType::TagUnion(RocTagUnion::NonRecursive { name, .. })
| RocType::TagUnion(RocTagUnion::SingleTagUnion { name, .. })
| RocType::TagUnion(RocTagUnion::Recursive { name, .. })
| RocType::TagUnion(RocTagUnion::Enumeration { name, .. })
| RocType::TagUnion(RocTagUnion::NullableWrapped { name, .. })
@ -1909,6 +1967,7 @@ fn cannot_derive_copy(roc_type: &RocType, types: &Types) -> bool {
| RocType::Bool
| RocType::Num(_)
| RocType::TagUnion(RocTagUnion::Enumeration { .. })
| RocType::TagUnion(RocTagUnion::SingleTagUnion { .. })
| RocType::Function { .. } => false,
RocType::RocStr
| RocType::RocList(_)
@ -1960,6 +2019,7 @@ fn has_float_help(roc_type: &RocType, types: &Types, do_not_recurse: &[TypeId])
| RocType::RocStr
| RocType::Bool
| RocType::TagUnion(RocTagUnion::Enumeration { .. })
| RocType::TagUnion(RocTagUnion::SingleTagUnion { .. })
| RocType::Function { .. } => false,
RocType::RocList(id) | RocType::RocSet(id) | RocType::RocBox(id) => {
has_float_help(types.get_type(*id), types, do_not_recurse)

View file

@ -85,6 +85,14 @@ impl Types {
use RocTagUnion::*;
match (union_a, union_b) {
(
SingleTagUnion {
tag_name: tag_a, ..
},
SingleTagUnion {
tag_name: tag_b, ..
},
) => tag_a == tag_b,
(Enumeration { tags: tags_a, .. }, Enumeration { tags: tags_b, .. }) => {
tags_a == tags_b
}
@ -199,7 +207,9 @@ impl Types {
}
// These are all listed explicitly so that if we ever add a new variant,
// we'll get an exhaustiveness error here.
(Enumeration { .. }, _)
(SingleTagUnion { .. }, _)
| (_, SingleTagUnion { .. })
| (Enumeration { .. }, _)
| (_, Enumeration { .. })
| (NonRecursive { .. }, _)
| (_, NonRecursive { .. })
@ -515,6 +525,10 @@ pub enum RocTagUnion {
tags: Vec<String>,
size: u32,
},
SingleTagUnion {
name: String,
tag_name: String,
},
/// A non-recursive tag union
/// e.g. `Result a e : [Ok a, Err e]`
NonRecursive {
@ -1109,6 +1123,17 @@ fn add_tag_union<'a>(
tags: tags.into_iter().map(|(tag_name, _)| tag_name).collect(),
size: int_width.stack_size(),
},
Layout::Struct { field_layouts, .. } if field_layouts.is_empty() => {
// This should be a single-tag union.
debug_assert_eq!(tags.len(), 1);
let (tag_name, _) = tags.pop().unwrap();
RocTagUnion::SingleTagUnion {
name: name.clone(),
tag_name,
}
}
Layout::Builtin(_)
| Layout::Struct { .. }
| Layout::Boxed(_)