mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
Combine all single-tag unions into RocTagUnion::SingleTag
This commit is contained in:
parent
4ac79772df
commit
1a10174abb
3 changed files with 495 additions and 329 deletions
|
@ -245,122 +245,21 @@ fn add_type(target_info: TargetInfo, id: TypeId, types: &Types, impls: &mut Impl
|
||||||
types,
|
types,
|
||||||
impls,
|
impls,
|
||||||
),
|
),
|
||||||
RocTagUnion::RecursiveSingleTag {
|
RocTagUnion::SingleTag {
|
||||||
name,
|
name,
|
||||||
tag_name,
|
tag_name,
|
||||||
payload_fields,
|
payload_fields,
|
||||||
|
is_recursive,
|
||||||
} => {
|
} => {
|
||||||
// TODO
|
add_single_tag_union(
|
||||||
todo!();
|
name,
|
||||||
}
|
tag_name,
|
||||||
RocTagUnion::NonRecursiveSingleTag {
|
payload_fields,
|
||||||
name,
|
|
||||||
tag_name,
|
|
||||||
payload: Some(payload_id),
|
|
||||||
} => {
|
|
||||||
// Store single-tag unions as structs rather than enums,
|
|
||||||
// because they have only one alternative. However, still
|
|
||||||
// offer the usual tag union APIs.
|
|
||||||
{
|
|
||||||
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![(tag_name.clone(), *payload_id)],
|
|
||||||
},
|
|
||||||
types,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
add_decl(
|
|
||||||
impls,
|
|
||||||
None,
|
|
||||||
target_info,
|
|
||||||
format!(
|
|
||||||
r#"#[repr(C)]
|
|
||||||
{derive}
|
|
||||||
pub struct {name} {{
|
|
||||||
{tag_name}: *mut {}
|
|
||||||
}}
|
|
||||||
"#,
|
|
||||||
type_name(*payload_id, types)
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RocTagUnion::NonRecursiveSingleTag {
|
|
||||||
name,
|
|
||||||
tag_name,
|
|
||||||
payload: None,
|
|
||||||
} => {
|
|
||||||
// A single tag with no payload 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,
|
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,
|
impls,
|
||||||
opt_impl.clone(),
|
|
||||||
target_info,
|
target_info,
|
||||||
format!(
|
*is_recursive,
|
||||||
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,
|
|
||||||
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 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -385,6 +284,173 @@ pub struct {name} {{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_single_tag_union(
|
||||||
|
name: &str,
|
||||||
|
tag_name: &str,
|
||||||
|
payload_fields: &[TypeId],
|
||||||
|
types: &Types,
|
||||||
|
impls: &mut IndexMap<Option<String>, IndexMap<String, Vec<TargetInfo>>>,
|
||||||
|
target_info: TargetInfo,
|
||||||
|
is_recursive: bool,
|
||||||
|
) {
|
||||||
|
// Store single-tag unions as structs rather than enums,
|
||||||
|
// because they have only one alternative. However, still
|
||||||
|
// offer the usual tag union APIs.
|
||||||
|
{
|
||||||
|
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: payload_fields
|
||||||
|
.iter()
|
||||||
|
.map(|type_id| (String::new(), *type_id))
|
||||||
|
.collect(),
|
||||||
|
},
|
||||||
|
types,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut body = format!("#[repr(C)]\n{derive}\npub struct {name} ");
|
||||||
|
|
||||||
|
if payload_fields.is_empty() {
|
||||||
|
// If there are no payload fields, this should not have been labeled recursive!
|
||||||
|
debug_assert!(!is_recursive);
|
||||||
|
|
||||||
|
// A single tag with no payload is a zero-sized unit type, so
|
||||||
|
// represent it as a zero-sized struct (e.g. "struct Foo()").
|
||||||
|
body.push_str("();\n");
|
||||||
|
} else {
|
||||||
|
body.push_str("{\n");
|
||||||
|
|
||||||
|
for (index, field_id) in payload_fields.iter().enumerate() {
|
||||||
|
let field_type = type_name(*field_id, types);
|
||||||
|
|
||||||
|
// These are all private fields, since this is a tag union.
|
||||||
|
body.push_str(&format!("{INDENT}f{index}: {field_type},\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
body.push_str("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
add_decl(impls, None, target_info, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the impl for the single-tag union itself
|
||||||
|
{
|
||||||
|
let opt_impl = Some(format!("impl {name}"));
|
||||||
|
|
||||||
|
if payload_fields.is_empty() {
|
||||||
|
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,
|
||||||
|
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 fn as_{tag_name}(&self) {{
|
||||||
|
()
|
||||||
|
}}"#,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
{
|
||||||
|
let mut args: Vec<String> = Vec::with_capacity(payload_fields.len());
|
||||||
|
let mut fields: Vec<String> = Vec::with_capacity(payload_fields.len());
|
||||||
|
|
||||||
|
for (index, field_id) in payload_fields.iter().enumerate() {
|
||||||
|
let field_type = type_name(*field_id, types);
|
||||||
|
|
||||||
|
args.push(format!("f{index}: {field_type}"));
|
||||||
|
fields.push(format!("{INDENT}{INDENT}{INDENT}f{index},"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = args.join(", ");
|
||||||
|
let fields = fields.join("\n");
|
||||||
|
|
||||||
|
add_decl(
|
||||||
|
impls,
|
||||||
|
opt_impl.clone(),
|
||||||
|
target_info,
|
||||||
|
format!(
|
||||||
|
r#"/// A tag named {tag_name}, with the given payload.
|
||||||
|
pub fn {tag_name}({args}) -> Self {{
|
||||||
|
Self {{
|
||||||
|
{fields}
|
||||||
|
}}
|
||||||
|
}}"#,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
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 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn add_discriminant(
|
fn add_discriminant(
|
||||||
name: &str,
|
name: &str,
|
||||||
target_info: TargetInfo,
|
target_info: TargetInfo,
|
||||||
|
@ -1452,12 +1518,11 @@ fn type_name(id: TypeId, types: &Types) -> String {
|
||||||
RocType::Struct { name, .. }
|
RocType::Struct { name, .. }
|
||||||
| RocType::TagUnionPayload { name, .. }
|
| RocType::TagUnionPayload { name, .. }
|
||||||
| RocType::TagUnion(RocTagUnion::NonRecursive { name, .. })
|
| RocType::TagUnion(RocTagUnion::NonRecursive { name, .. })
|
||||||
| RocType::TagUnion(RocTagUnion::NonRecursiveSingleTag { name, .. })
|
|
||||||
| RocType::TagUnion(RocTagUnion::Recursive { name, .. })
|
| RocType::TagUnion(RocTagUnion::Recursive { name, .. })
|
||||||
| RocType::TagUnion(RocTagUnion::Enumeration { name, .. })
|
| RocType::TagUnion(RocTagUnion::Enumeration { name, .. })
|
||||||
| RocType::TagUnion(RocTagUnion::NullableWrapped { name, .. })
|
| RocType::TagUnion(RocTagUnion::NullableWrapped { name, .. })
|
||||||
| RocType::TagUnion(RocTagUnion::NullableUnwrapped { name, .. })
|
| RocType::TagUnion(RocTagUnion::NullableUnwrapped { name, .. })
|
||||||
| RocType::TagUnion(RocTagUnion::RecursiveSingleTag { name, .. }) => name.clone(),
|
| RocType::TagUnion(RocTagUnion::SingleTag { name, .. }) => name.clone(),
|
||||||
RocType::RecursivePointer(content) => type_name(*content, types),
|
RocType::RecursivePointer(content) => type_name(*content, types),
|
||||||
RocType::Function { name, .. } => name.clone(),
|
RocType::Function { name, .. } => name.clone(),
|
||||||
}
|
}
|
||||||
|
@ -2075,18 +2140,26 @@ fn cannot_derive_copy(roc_type: &RocType, types: &Types) -> bool {
|
||||||
| RocType::Bool
|
| RocType::Bool
|
||||||
| RocType::Num(_)
|
| RocType::Num(_)
|
||||||
| RocType::TagUnion(RocTagUnion::Enumeration { .. })
|
| RocType::TagUnion(RocTagUnion::Enumeration { .. })
|
||||||
| RocType::TagUnion(RocTagUnion::NonRecursiveSingleTag { .. })
|
|
||||||
| RocType::Function { .. } => false,
|
| RocType::Function { .. } => false,
|
||||||
RocType::RocStr
|
RocType::RocStr
|
||||||
| RocType::RocList(_)
|
| RocType::RocList(_)
|
||||||
| RocType::RocDict(_, _)
|
| RocType::RocDict(_, _)
|
||||||
| RocType::RocSet(_)
|
| RocType::RocSet(_)
|
||||||
| RocType::RocBox(_)
|
| RocType::RocBox(_)
|
||||||
| RocType::TagUnion(RocTagUnion::RecursiveSingleTag { .. })
|
|
||||||
| RocType::TagUnion(RocTagUnion::NullableUnwrapped { .. })
|
| RocType::TagUnion(RocTagUnion::NullableUnwrapped { .. })
|
||||||
| RocType::TagUnion(RocTagUnion::NullableWrapped { .. })
|
| RocType::TagUnion(RocTagUnion::NullableWrapped { .. })
|
||||||
| RocType::TagUnion(RocTagUnion::Recursive { .. })
|
| RocType::TagUnion(RocTagUnion::Recursive { .. })
|
||||||
| RocType::RecursivePointer { .. } => true,
|
| RocType::RecursivePointer { .. }
|
||||||
|
| RocType::TagUnion(RocTagUnion::SingleTag {
|
||||||
|
is_recursive: true, ..
|
||||||
|
}) => true,
|
||||||
|
RocType::TagUnion(RocTagUnion::SingleTag {
|
||||||
|
is_recursive: false,
|
||||||
|
payload_fields,
|
||||||
|
..
|
||||||
|
}) => payload_fields
|
||||||
|
.iter()
|
||||||
|
.any(|type_id| cannot_derive_copy(types.get_type(*type_id), types)),
|
||||||
RocType::TagUnion(RocTagUnion::NonRecursive { tags, .. }) => {
|
RocType::TagUnion(RocTagUnion::NonRecursive { tags, .. }) => {
|
||||||
tags.iter().any(|(_, payloads)| {
|
tags.iter().any(|(_, payloads)| {
|
||||||
payloads
|
payloads
|
||||||
|
@ -2127,7 +2200,6 @@ fn has_float_help(roc_type: &RocType, types: &Types, do_not_recurse: &[TypeId])
|
||||||
| RocType::RocStr
|
| RocType::RocStr
|
||||||
| RocType::Bool
|
| RocType::Bool
|
||||||
| RocType::TagUnion(RocTagUnion::Enumeration { .. })
|
| RocType::TagUnion(RocTagUnion::Enumeration { .. })
|
||||||
| RocType::TagUnion(RocTagUnion::NonRecursiveSingleTag { .. })
|
|
||||||
| RocType::Function { .. } => false,
|
| RocType::Function { .. } => false,
|
||||||
RocType::RocList(id) | RocType::RocSet(id) | RocType::RocBox(id) => {
|
RocType::RocList(id) | RocType::RocSet(id) | RocType::RocBox(id) => {
|
||||||
has_float_help(types.get_type(*id), types, do_not_recurse)
|
has_float_help(types.get_type(*id), types, do_not_recurse)
|
||||||
|
@ -2146,7 +2218,7 @@ fn has_float_help(roc_type: &RocType, types: &Types, do_not_recurse: &[TypeId])
|
||||||
RocType::TagUnionPayload { fields, .. } => fields
|
RocType::TagUnionPayload { fields, .. } => fields
|
||||||
.iter()
|
.iter()
|
||||||
.any(|(_, type_id)| has_float_help(types.get_type(*type_id), types, do_not_recurse)),
|
.any(|(_, type_id)| has_float_help(types.get_type(*type_id), types, do_not_recurse)),
|
||||||
RocType::TagUnion(RocTagUnion::RecursiveSingleTag { payload_fields, .. }) => payload_fields
|
RocType::TagUnion(RocTagUnion::SingleTag { payload_fields, .. }) => payload_fields
|
||||||
.iter()
|
.iter()
|
||||||
.any(|type_id| has_float_help(types.get_type(*type_id), types, do_not_recurse)),
|
.any(|type_id| has_float_help(types.get_type(*type_id), types, do_not_recurse)),
|
||||||
RocType::TagUnion(RocTagUnion::Recursive { tags, .. })
|
RocType::TagUnion(RocTagUnion::Recursive { tags, .. })
|
||||||
|
|
|
@ -7,14 +7,17 @@ use roc_builtins::bitcode::{
|
||||||
IntWidth::{self, *},
|
IntWidth::{self, *},
|
||||||
};
|
};
|
||||||
use roc_collections::VecMap;
|
use roc_collections::VecMap;
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
use roc_module::{
|
||||||
|
ident::TagName,
|
||||||
|
symbol::{Interns, Symbol},
|
||||||
|
};
|
||||||
use roc_mono::layout::{
|
use roc_mono::layout::{
|
||||||
cmp_fields, ext_var_is_empty_tag_union, round_up_to_alignment, Builtin, Discriminant, Layout,
|
cmp_fields, ext_var_is_empty_tag_union, round_up_to_alignment, Builtin, Discriminant, Layout,
|
||||||
LayoutCache, UnionLayout,
|
LayoutCache, UnionLayout,
|
||||||
};
|
};
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
use roc_types::{
|
use roc_types::{
|
||||||
subs::{Content, FlatType, GetSubsSlice, Subs, UnionTags, Variable},
|
subs::{Content, FlatType, GetSubsSlice, Subs, UnionLabels, UnionTags, Variable},
|
||||||
types::RecordField,
|
types::RecordField,
|
||||||
};
|
};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
@ -86,13 +89,19 @@ impl Types {
|
||||||
|
|
||||||
match (union_a, union_b) {
|
match (union_a, union_b) {
|
||||||
(
|
(
|
||||||
NonRecursiveSingleTag {
|
SingleTag {
|
||||||
tag_name: tag_a, ..
|
name: _,
|
||||||
|
tag_name: tag_name_a,
|
||||||
|
payload_fields: payload_fields_a,
|
||||||
|
is_recursive: _,
|
||||||
},
|
},
|
||||||
NonRecursiveSingleTag {
|
SingleTag {
|
||||||
tag_name: tag_b, ..
|
name: _,
|
||||||
|
tag_name: tag_name_b,
|
||||||
|
payload_fields: payload_fields_b,
|
||||||
|
is_recursive: _,
|
||||||
},
|
},
|
||||||
) => tag_a == tag_b,
|
) => tag_name_a == tag_name_b && payload_fields_a == payload_fields_b,
|
||||||
(Enumeration { tags: tags_a, .. }, Enumeration { tags: tags_b, .. }) => {
|
(Enumeration { tags: tags_a, .. }, Enumeration { tags: tags_b, .. }) => {
|
||||||
tags_a == tags_b
|
tags_a == tags_b
|
||||||
}
|
}
|
||||||
|
@ -145,19 +154,6 @@ impl Types {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(
|
|
||||||
RecursiveSingleTag {
|
|
||||||
name: _,
|
|
||||||
tag_name: tag_name_a,
|
|
||||||
payload_fields: payload_fields_a,
|
|
||||||
},
|
|
||||||
RecursiveSingleTag {
|
|
||||||
name: _,
|
|
||||||
tag_name: tag_name_b,
|
|
||||||
payload_fields: payload_fields_b,
|
|
||||||
},
|
|
||||||
) => tag_name_a == tag_name_b && payload_fields_a == payload_fields_b,
|
|
||||||
|
|
||||||
(
|
(
|
||||||
NullableWrapped { tags: tags_a, .. },
|
NullableWrapped { tags: tags_a, .. },
|
||||||
NullableWrapped { tags: tags_b, .. },
|
NullableWrapped { tags: tags_b, .. },
|
||||||
|
@ -203,16 +199,14 @@ impl Types {
|
||||||
}
|
}
|
||||||
// These are all listed explicitly so that if we ever add a new variant,
|
// These are all listed explicitly so that if we ever add a new variant,
|
||||||
// we'll get an exhaustiveness error here.
|
// we'll get an exhaustiveness error here.
|
||||||
(NonRecursiveSingleTag { .. }, _)
|
(SingleTag { .. }, _)
|
||||||
| (_, NonRecursiveSingleTag { .. })
|
| (_, SingleTag { .. })
|
||||||
| (Enumeration { .. }, _)
|
| (Enumeration { .. }, _)
|
||||||
| (_, Enumeration { .. })
|
| (_, Enumeration { .. })
|
||||||
| (NonRecursive { .. }, _)
|
| (NonRecursive { .. }, _)
|
||||||
| (_, NonRecursive { .. })
|
| (_, NonRecursive { .. })
|
||||||
| (Recursive { .. }, _)
|
| (Recursive { .. }, _)
|
||||||
| (_, Recursive { .. })
|
| (_, Recursive { .. })
|
||||||
| (RecursiveSingleTag { .. }, _)
|
|
||||||
| (_, RecursiveSingleTag { .. })
|
|
||||||
| (NullableUnwrapped { .. }, _)
|
| (NullableUnwrapped { .. }, _)
|
||||||
| (_, NullableUnwrapped { .. }) => false,
|
| (_, NullableUnwrapped { .. }) => false,
|
||||||
}
|
}
|
||||||
|
@ -521,11 +515,6 @@ pub enum RocTagUnion {
|
||||||
tags: Vec<String>,
|
tags: Vec<String>,
|
||||||
size: u32,
|
size: u32,
|
||||||
},
|
},
|
||||||
NonRecursiveSingleTag {
|
|
||||||
name: String,
|
|
||||||
tag_name: String,
|
|
||||||
payload: Option<TypeId>,
|
|
||||||
},
|
|
||||||
/// A non-recursive tag union
|
/// A non-recursive tag union
|
||||||
/// e.g. `Result a e : [Ok a, Err e]`
|
/// e.g. `Result a e : [Ok a, Err e]`
|
||||||
NonRecursive {
|
NonRecursive {
|
||||||
|
@ -542,15 +531,14 @@ pub enum RocTagUnion {
|
||||||
discriminant_size: u32,
|
discriminant_size: u32,
|
||||||
discriminant_offset: u32,
|
discriminant_offset: u32,
|
||||||
},
|
},
|
||||||
/// A recursive tag union with just one constructor
|
|
||||||
/// Optimization: No need to store a tag ID (the payload is "unwrapped")
|
/// Optimization: No need to store a tag ID (the payload is "unwrapped")
|
||||||
/// e.g. `RoseTree a : [Tree a (List (RoseTree a))]`
|
/// e.g. `RoseTree a : [Tree a (List (RoseTree a))]`
|
||||||
RecursiveSingleTag {
|
SingleTag {
|
||||||
name: String,
|
name: String,
|
||||||
tag_name: String,
|
tag_name: String,
|
||||||
payload_fields: Vec<TypeId>,
|
payload_fields: Vec<TypeId>,
|
||||||
|
is_recursive: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// A recursive tag union that has an empty variant
|
/// A recursive tag union that has an empty variant
|
||||||
/// Optimization: Represent the empty variant as null pointer => no memory usage & fast comparison
|
/// Optimization: Represent the empty variant as null pointer => no memory usage & fast comparison
|
||||||
/// It has more than one other variant, so they need tag IDs (payloads are "wrapped")
|
/// It has more than one other variant, so they need tag IDs (payloads are "wrapped")
|
||||||
|
@ -982,43 +970,259 @@ fn add_tag_union<'a>(
|
||||||
None => env.enum_names.get_name(var),
|
None => env.enum_names.get_name(var),
|
||||||
};
|
};
|
||||||
|
|
||||||
// This one needs an early return. That's because the the outermost representation
|
let is_recursive;
|
||||||
// of an unwrapped, non-nullable tag union is a struct, not a pointer. We must not
|
let tag_union_type = match layout {
|
||||||
// create structs for its payloads like we do with other tag union types,
|
Layout::Union(union_layout) => {
|
||||||
// because this one *is* the struct!
|
use UnionLayout::*;
|
||||||
if let Layout::Union(UnionLayout::NonNullableUnwrapped(payload_field_layouts)) = layout {
|
|
||||||
let mut iter = union_tags.iter_from_subs(subs);
|
|
||||||
let (tag_name, payload_vars) = iter.next().unwrap();
|
|
||||||
|
|
||||||
// NonNullableUnwrapped should always have exactly 1 payload.
|
match union_layout {
|
||||||
debug_assert_eq!(iter.next(), None);
|
// A non-recursive tag union
|
||||||
|
// e.g. `Result ok err : [Ok ok, Err err]`
|
||||||
|
NonRecursive(_) => {
|
||||||
|
is_recursive = false;
|
||||||
|
|
||||||
let payload_fields: Vec<TypeId> = payload_vars
|
let tags = union_tags_to_types(
|
||||||
.into_iter()
|
&name,
|
||||||
.zip(payload_field_layouts.into_iter())
|
union_tags,
|
||||||
.map(|(field_var, field_layout)| {
|
subs,
|
||||||
add_type_help(env, *field_layout, *field_var, None, types)
|
env,
|
||||||
})
|
types,
|
||||||
.collect();
|
layout,
|
||||||
|
is_recursive,
|
||||||
|
);
|
||||||
|
// TODO deal with empty tag union
|
||||||
|
let discriminant_size = Discriminant::from_number_of_tags(tags.len())
|
||||||
|
.stack_size()
|
||||||
|
.max(1);
|
||||||
|
let discriminant_offset = union_layout.tag_id_offset(env.target).unwrap();
|
||||||
|
|
||||||
// A recursive tag union with just one constructor
|
RocTagUnion::NonRecursive {
|
||||||
// Optimization: No need to store a tag ID (the payload is "unwrapped")
|
name: name.clone(),
|
||||||
// e.g. `RoseTree a : [Tree a (List (RoseTree a))]`
|
tags,
|
||||||
let tag_union_type = RocTagUnion::RecursiveSingleTag {
|
discriminant_size,
|
||||||
name: name.clone(),
|
discriminant_offset,
|
||||||
tag_name: tag_name.0.as_str().to_string(),
|
}
|
||||||
payload_fields,
|
}
|
||||||
};
|
// A recursive tag union (general case)
|
||||||
|
// e.g. `Expr : [Sym Str, Add Expr Expr]`
|
||||||
|
Recursive(_) => {
|
||||||
|
is_recursive = true;
|
||||||
|
|
||||||
let typ = RocType::TagUnion(tag_union_type);
|
let tags = union_tags_to_types(
|
||||||
let type_id = types.add_named(name, typ, layout);
|
&name,
|
||||||
|
union_tags,
|
||||||
|
subs,
|
||||||
|
env,
|
||||||
|
types,
|
||||||
|
layout,
|
||||||
|
is_recursive,
|
||||||
|
);
|
||||||
|
let discriminant_size =
|
||||||
|
Discriminant::from_number_of_tags(tags.len()).stack_size();
|
||||||
|
let discriminant_offset = union_layout.tag_id_offset(env.target).unwrap();
|
||||||
|
|
||||||
|
RocTagUnion::Recursive {
|
||||||
|
name: name.clone(),
|
||||||
|
tags,
|
||||||
|
discriminant_size,
|
||||||
|
discriminant_offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NonNullableUnwrapped(field_layouts) => {
|
||||||
|
is_recursive = true;
|
||||||
|
|
||||||
|
// The the outermost representation of an unwrapped, non-nullable tag union
|
||||||
|
// is a struct, not a pointer. We must not create structs for its payloads
|
||||||
|
// like we do with other tag union types, because this one *is* the struct!
|
||||||
|
let (tag_name, payload_fields) =
|
||||||
|
single_tag_payload_fields(union_tags, subs, field_layouts, env, types);
|
||||||
|
|
||||||
|
// A recursive tag union with just one constructor
|
||||||
|
// Optimization: No need to store a tag ID (the payload is "unwrapped")
|
||||||
|
// e.g. `RoseTree a : [Tree a (List (RoseTree a))]`
|
||||||
|
RocTagUnion::SingleTag {
|
||||||
|
name: name.clone(),
|
||||||
|
tag_name,
|
||||||
|
payload_fields,
|
||||||
|
is_recursive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// A recursive tag union that has an empty variant
|
||||||
|
// Optimization: Represent the empty variant as null pointer => no memory usage & fast comparison
|
||||||
|
// It has more than one other variant, so they need tag IDs (payloads are "wrapped")
|
||||||
|
// e.g. `FingerTree a : [Empty, Single a, More (Some a) (FingerTree (Tuple a)) (Some a)]`
|
||||||
|
// see also: https://youtu.be/ip92VMpf_-A?t=164
|
||||||
|
NullableWrapped {
|
||||||
|
nullable_id,
|
||||||
|
other_tags,
|
||||||
|
} => {
|
||||||
|
is_recursive = true;
|
||||||
|
|
||||||
|
let tags = union_tags_to_types(
|
||||||
|
&name,
|
||||||
|
union_tags,
|
||||||
|
subs,
|
||||||
|
env,
|
||||||
|
types,
|
||||||
|
layout,
|
||||||
|
is_recursive,
|
||||||
|
);
|
||||||
|
let discriminant_size =
|
||||||
|
Discriminant::from_number_of_tags(other_tags.len()).stack_size();
|
||||||
|
let discriminant_offset = union_layout.tag_id_offset(env.target).unwrap();
|
||||||
|
|
||||||
|
// nullable_id refers to the index of the tag that is represented at runtime as NULL.
|
||||||
|
// For example, in `FingerTree a : [Empty, Single a, More (Some a) (FingerTree (Tuple a)) (Some a)]`,
|
||||||
|
// the ids would be Empty = 0, More = 1, Single = 2, because that's how those tags are
|
||||||
|
// ordered alphabetically. Since the Empty tag will be represented at runtime as NULL,
|
||||||
|
// and since Empty's tag id is 0, here nullable_id would be 0.
|
||||||
|
RocTagUnion::NullableWrapped {
|
||||||
|
name: name.clone(),
|
||||||
|
index_of_null_tag: nullable_id,
|
||||||
|
tags,
|
||||||
|
discriminant_size,
|
||||||
|
discriminant_offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// A recursive tag union with only two variants, where one is empty.
|
||||||
|
// Optimizations: Use null for the empty variant AND don't store a tag ID for the other variant.
|
||||||
|
// e.g. `ConsList a : [Nil, Cons a (ConsList a)]`
|
||||||
|
NullableUnwrapped {
|
||||||
|
nullable_id: null_represents_first_tag,
|
||||||
|
other_fields: _, // TODO use this!
|
||||||
|
} => {
|
||||||
|
is_recursive = true;
|
||||||
|
|
||||||
|
let mut tags = union_tags_to_types(
|
||||||
|
&name,
|
||||||
|
union_tags,
|
||||||
|
subs,
|
||||||
|
env,
|
||||||
|
types,
|
||||||
|
layout,
|
||||||
|
is_recursive,
|
||||||
|
);
|
||||||
|
// NullableUnwrapped tag unions should always have exactly 2 tags.
|
||||||
|
debug_assert_eq!(tags.len(), 2);
|
||||||
|
|
||||||
|
let null_tag;
|
||||||
|
let non_null;
|
||||||
|
|
||||||
|
if null_represents_first_tag {
|
||||||
|
// If nullable_id is true, then the null tag is second, which means
|
||||||
|
// pop() will return it because it's at the end of the vec.
|
||||||
|
null_tag = tags.pop().unwrap().0;
|
||||||
|
non_null = tags.pop().unwrap();
|
||||||
|
} else {
|
||||||
|
// The null tag is first, which means the tag with the payload is second.
|
||||||
|
non_null = tags.pop().unwrap();
|
||||||
|
null_tag = tags.pop().unwrap().0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (non_null_tag, non_null_payload) = non_null;
|
||||||
|
|
||||||
|
RocTagUnion::NullableUnwrapped {
|
||||||
|
name: name.clone(),
|
||||||
|
null_tag,
|
||||||
|
non_null_tag,
|
||||||
|
non_null_payload: non_null_payload.unwrap(),
|
||||||
|
null_represents_first_tag,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Layout::Builtin(Builtin::Int(int_width)) => {
|
||||||
|
is_recursive = false;
|
||||||
|
|
||||||
|
let tags: Vec<String> = union_tags
|
||||||
|
.iter_from_subs(subs)
|
||||||
|
.map(|(tag_name, _)| tag_name.0.as_str().to_string())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
RocTagUnion::Enumeration {
|
||||||
|
name: name.clone(),
|
||||||
|
tags,
|
||||||
|
size: int_width.stack_size(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Layout::Struct { field_layouts, .. } => {
|
||||||
|
// This is a single-tag union, but it's unclear whether it's recursive,
|
||||||
|
// since one of its fields could hold a recursive type.
|
||||||
|
is_recursive = is_recursive_tag_union(layout);
|
||||||
|
|
||||||
|
let (tag_name, payload_fields) =
|
||||||
|
single_tag_payload_fields(union_tags, subs, field_layouts, env, types);
|
||||||
|
|
||||||
|
// A recursive tag union with just one constructor
|
||||||
|
// Optimization: No need to store a tag ID (the payload is "unwrapped")
|
||||||
|
// e.g. `RoseTree a : [Tree a (List (RoseTree a))]`
|
||||||
|
RocTagUnion::SingleTag {
|
||||||
|
name: name.clone(),
|
||||||
|
tag_name,
|
||||||
|
payload_fields,
|
||||||
|
is_recursive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Layout::Builtin(builtin) => {
|
||||||
|
// This is a single-tag union, but it's unclear whether it's recursive,
|
||||||
|
// since it could be (for example) a single-tag union wrapping a List.
|
||||||
|
is_recursive = is_recursive_tag_union(layout);
|
||||||
|
|
||||||
|
let type_id = add_builtin_type(env, builtin, var, opt_name, types, layout);
|
||||||
|
let (tag_name, _) = single_tag_payload(union_tags, subs);
|
||||||
|
|
||||||
|
RocTagUnion::SingleTag {
|
||||||
|
name: name.clone(),
|
||||||
|
tag_name,
|
||||||
|
payload_fields: vec![type_id],
|
||||||
|
is_recursive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Layout::Boxed(elem_layout) => {
|
||||||
|
// This is a single-tag union, but it's unclear whether it's recursive,
|
||||||
|
// since it could be (for example) a single-tag union wrapping a Box that contains
|
||||||
|
// the union somewhere in its eleemnt.
|
||||||
|
is_recursive = is_recursive_tag_union(layout);
|
||||||
|
|
||||||
|
let (tag_name, payload_fields) =
|
||||||
|
single_tag_payload_fields(union_tags, subs, &[*elem_layout], env, types);
|
||||||
|
|
||||||
|
RocTagUnion::SingleTag {
|
||||||
|
name: name.clone(),
|
||||||
|
tag_name,
|
||||||
|
payload_fields,
|
||||||
|
is_recursive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Layout::LambdaSet(_) => {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
Layout::RecursivePointer => {
|
||||||
|
// A single-tag union which only wraps itself is erroneous and should have
|
||||||
|
// been turned into an error earlier in the process.
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let typ = RocType::TagUnion(tag_union_type);
|
||||||
|
let type_id = types.add_named(name, typ, layout);
|
||||||
|
|
||||||
|
if is_recursive {
|
||||||
env.known_recursive_types.insert(layout, type_id);
|
env.known_recursive_types.insert(layout, type_id);
|
||||||
|
|
||||||
// Do an early return because we've already done everything we needed to do.
|
|
||||||
return type_id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type_id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn union_tags_to_types(
|
||||||
|
name: &str,
|
||||||
|
union_tags: &UnionLabels<TagName>,
|
||||||
|
subs: &Subs,
|
||||||
|
env: &mut Env,
|
||||||
|
types: &mut Types,
|
||||||
|
layout: Layout,
|
||||||
|
is_recursive: bool,
|
||||||
|
) -> Vec<(String, Option<TypeId>)> {
|
||||||
let mut tags: Vec<(String, Vec<Variable>)> = union_tags
|
let mut tags: Vec<(String, Vec<Variable>)> = union_tags
|
||||||
.iter_from_subs(subs)
|
.iter_from_subs(subs)
|
||||||
.map(|(tag_name, payload_vars)| {
|
.map(|(tag_name, payload_vars)| {
|
||||||
|
@ -1027,14 +1231,51 @@ fn add_tag_union<'a>(
|
||||||
(name_str, payload_vars.to_vec())
|
(name_str, payload_vars.to_vec())
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Sort tags alphabetically by tag name
|
// Sort tags alphabetically by tag name
|
||||||
tags.sort_by(|(name1, _), (name2, _)| name1.cmp(name2));
|
tags.sort_by(|(name1, _), (name2, _)| name1.cmp(name2));
|
||||||
|
|
||||||
let is_recursive = is_recursive_tag_union(layout);
|
tags_to_types(name, tags, env, types, layout, is_recursive)
|
||||||
|
}
|
||||||
|
|
||||||
let mut tags: Vec<_> = tags
|
fn single_tag_payload<'a>(
|
||||||
|
union_tags: &'a UnionLabels<TagName>,
|
||||||
|
subs: &'a Subs,
|
||||||
|
) -> (String, &'a [Variable]) {
|
||||||
|
let mut iter = union_tags.iter_from_subs(subs);
|
||||||
|
let (tag_name, payload_vars) = iter.next().unwrap();
|
||||||
|
// This should be a single-tag union.
|
||||||
|
debug_assert_eq!(iter.next(), None);
|
||||||
|
|
||||||
|
(tag_name.0.as_str().to_string(), payload_vars)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn single_tag_payload_fields<'a>(
|
||||||
|
union_tags: &UnionLabels<TagName>,
|
||||||
|
subs: &Subs,
|
||||||
|
field_layouts: &[Layout<'a>],
|
||||||
|
env: &mut Env<'a>,
|
||||||
|
types: &mut Types,
|
||||||
|
) -> (String, Vec<TypeId>) {
|
||||||
|
let (tag_name, payload_vars) = single_tag_payload(union_tags, subs);
|
||||||
|
|
||||||
|
let payload_fields: Vec<TypeId> = payload_vars
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
.zip(field_layouts.into_iter())
|
||||||
|
.map(|(field_var, field_layout)| add_type_help(env, *field_layout, *field_var, None, types))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
(tag_name, payload_fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tags_to_types(
|
||||||
|
name: &str,
|
||||||
|
tags: Vec<(String, Vec<Variable>)>,
|
||||||
|
env: &mut Env,
|
||||||
|
types: &mut Types,
|
||||||
|
layout: Layout,
|
||||||
|
is_recursive: bool,
|
||||||
|
) -> Vec<(String, Option<TypeId>)> {
|
||||||
|
tags.into_iter()
|
||||||
.map(|(tag_name, payload_vars)| {
|
.map(|(tag_name, payload_vars)| {
|
||||||
match struct_fields_needed(env, payload_vars.iter().copied()) {
|
match struct_fields_needed(env, payload_vars.iter().copied()) {
|
||||||
0 => {
|
0 => {
|
||||||
|
@ -1067,154 +1308,7 @@ fn add_tag_union<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect()
|
||||||
|
|
||||||
let tag_union_type = match layout {
|
|
||||||
Layout::Union(union_layout) => {
|
|
||||||
use UnionLayout::*;
|
|
||||||
|
|
||||||
match union_layout {
|
|
||||||
// A non-recursive tag union
|
|
||||||
// e.g. `Result ok err : [Ok ok, Err err]`
|
|
||||||
NonRecursive(_) => {
|
|
||||||
// TODO deal with empty tag union
|
|
||||||
let discriminant_size = Discriminant::from_number_of_tags(tags.len())
|
|
||||||
.stack_size()
|
|
||||||
.max(1);
|
|
||||||
let discriminant_offset = union_layout.tag_id_offset(env.target).unwrap();
|
|
||||||
|
|
||||||
RocTagUnion::NonRecursive {
|
|
||||||
name: name.clone(),
|
|
||||||
tags,
|
|
||||||
discriminant_size,
|
|
||||||
discriminant_offset,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// A recursive tag union (general case)
|
|
||||||
// e.g. `Expr : [Sym Str, Add Expr Expr]`
|
|
||||||
Recursive(_) => {
|
|
||||||
let discriminant_size =
|
|
||||||
Discriminant::from_number_of_tags(tags.len()).stack_size();
|
|
||||||
let discriminant_offset = union_layout.tag_id_offset(env.target).unwrap();
|
|
||||||
|
|
||||||
RocTagUnion::Recursive {
|
|
||||||
name: name.clone(),
|
|
||||||
tags,
|
|
||||||
discriminant_size,
|
|
||||||
discriminant_offset,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NonNullableUnwrapped(_) => {
|
|
||||||
// This was already special-cased with an early return, above.
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
// A recursive tag union that has an empty variant
|
|
||||||
// Optimization: Represent the empty variant as null pointer => no memory usage & fast comparison
|
|
||||||
// It has more than one other variant, so they need tag IDs (payloads are "wrapped")
|
|
||||||
// e.g. `FingerTree a : [Empty, Single a, More (Some a) (FingerTree (Tuple a)) (Some a)]`
|
|
||||||
// see also: https://youtu.be/ip92VMpf_-A?t=164
|
|
||||||
NullableWrapped {
|
|
||||||
nullable_id,
|
|
||||||
other_tags,
|
|
||||||
} => {
|
|
||||||
let discriminant_size =
|
|
||||||
Discriminant::from_number_of_tags(other_tags.len()).stack_size();
|
|
||||||
let discriminant_offset = union_layout.tag_id_offset(env.target).unwrap();
|
|
||||||
|
|
||||||
// nullable_id refers to the index of the tag that is represented at runtime as NULL.
|
|
||||||
// For example, in `FingerTree a : [Empty, Single a, More (Some a) (FingerTree (Tuple a)) (Some a)]`,
|
|
||||||
// the ids would be Empty = 0, More = 1, Single = 2, because that's how those tags are
|
|
||||||
// ordered alphabetically. Since the Empty tag will be represented at runtime as NULL,
|
|
||||||
// and since Empty's tag id is 0, here nullable_id would be 0.
|
|
||||||
RocTagUnion::NullableWrapped {
|
|
||||||
name: name.clone(),
|
|
||||||
index_of_null_tag: nullable_id,
|
|
||||||
tags,
|
|
||||||
discriminant_size,
|
|
||||||
discriminant_offset,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// A recursive tag union with only two variants, where one is empty.
|
|
||||||
// Optimizations: Use null for the empty variant AND don't store a tag ID for the other variant.
|
|
||||||
// e.g. `ConsList a : [Nil, Cons a (ConsList a)]`
|
|
||||||
NullableUnwrapped {
|
|
||||||
nullable_id: null_represents_first_tag,
|
|
||||||
other_fields: _, // TODO use this!
|
|
||||||
} => {
|
|
||||||
// NullableUnwrapped tag unions should always have exactly 2 tags.
|
|
||||||
debug_assert_eq!(tags.len(), 2);
|
|
||||||
|
|
||||||
let null_tag;
|
|
||||||
let non_null;
|
|
||||||
|
|
||||||
if null_represents_first_tag {
|
|
||||||
// If nullable_id is true, then the null tag is second, which means
|
|
||||||
// pop() will return it because it's at the end of the vec.
|
|
||||||
null_tag = tags.pop().unwrap().0;
|
|
||||||
non_null = tags.pop().unwrap();
|
|
||||||
} else {
|
|
||||||
// The null tag is first, which means the tag with the payload is second.
|
|
||||||
non_null = tags.pop().unwrap();
|
|
||||||
null_tag = tags.pop().unwrap().0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (non_null_tag, non_null_payload) = non_null;
|
|
||||||
|
|
||||||
RocTagUnion::NullableUnwrapped {
|
|
||||||
name: name.clone(),
|
|
||||||
null_tag,
|
|
||||||
non_null_tag,
|
|
||||||
non_null_payload: non_null_payload.unwrap(),
|
|
||||||
null_represents_first_tag,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Layout::Builtin(Builtin::Int(int_width)) => RocTagUnion::Enumeration {
|
|
||||||
name: name.clone(),
|
|
||||||
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, payload) = tags.pop().unwrap();
|
|
||||||
|
|
||||||
RocTagUnion::NonRecursiveSingleTag {
|
|
||||||
name: name.clone(),
|
|
||||||
tag_name,
|
|
||||||
payload,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Layout::Builtin(_)
|
|
||||||
| Layout::Struct { .. }
|
|
||||||
| Layout::Boxed(_)
|
|
||||||
| Layout::LambdaSet(_)
|
|
||||||
| Layout::RecursivePointer => {
|
|
||||||
// These must be single-tag wrappers. Generate ordinary nonrecursive
|
|
||||||
// tag unions for them, and let the generator do any unwrapping.
|
|
||||||
//
|
|
||||||
// This should be a very rare use case, and it's not worth overcomplicating
|
|
||||||
// the rest of glue to make it do something different.
|
|
||||||
RocTagUnion::NonRecursive {
|
|
||||||
name: name.clone(),
|
|
||||||
tags,
|
|
||||||
// These actually have no discriminant, since there's only one tag.
|
|
||||||
discriminant_size: 1,
|
|
||||||
discriminant_offset: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let typ = RocType::TagUnion(tag_union_type);
|
|
||||||
let type_id = types.add_named(name, typ, layout);
|
|
||||||
|
|
||||||
if is_recursive {
|
|
||||||
env.known_recursive_types.insert(layout, type_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
type_id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_recursive_tag_union(layout: Layout) -> bool {
|
fn is_recursive_tag_union(layout: Layout) -> bool {
|
||||||
|
|
|
@ -24,8 +24,8 @@ pub extern "C" fn rust_main() -> i32 {
|
||||||
|
|
||||||
// Verify that it has all the expected traits.
|
// Verify that it has all the expected traits.
|
||||||
|
|
||||||
// assert!(tag_union == tag_union); // PartialEq
|
assert!(tag_union == tag_union); // PartialEq
|
||||||
// assert!(tag_union.clone() == tag_union.clone()); // Clone
|
// assert!(tag_union.clone() == tag_union.clone()); // Clone
|
||||||
|
|
||||||
// assert!(tag_union.partial_cmp(&tag_union) == Some(Ordering::Equal)); // PartialOrd
|
// assert!(tag_union.partial_cmp(&tag_union) == Some(Ordering::Equal)); // PartialOrd
|
||||||
// assert!(tag_union.cmp(&tag_union) == Ordering::Equal); // Ord
|
// assert!(tag_union.cmp(&tag_union) == Ordering::Equal); // Ord
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue