Split back out NullableUnwrapped

This commit is contained in:
Richard Feldman 2022-08-08 15:01:28 -04:00
parent 1a10174abb
commit d965a43fe9
No known key found for this signature in database
GPG key ID: 7E4127D1E4241798
2 changed files with 487 additions and 297 deletions

View file

@ -13,18 +13,18 @@ type Impl = Option<String>;
/// Recursive tag unions need a custom Clone which bumps refcount.
const RECURSIVE_TAG_UNION_CLONE: &str = r#"fn clone(&self) -> Self {
if let Some(storage) = self.storage() {
let mut new_storage = storage.get();
if !new_storage.is_readonly() {
new_storage.increment_reference_count();
storage.set(new_storage);
if let Some(storage) = self.storage() {
let mut new_storage = storage.get();
if !new_storage.is_readonly() {
new_storage.increment_reference_count();
storage.set(new_storage);
}
}
}
Self {
pointer: self.pointer
}
}"#;
Self {
pointer: self.pointer
}
}"#;
const RECURSIVE_TAG_UNION_STORAGE: &str = r#"#[inline(always)]
fn storage(&self) -> Option<&core::cell::Cell<roc_std::Storage>> {
@ -245,20 +245,36 @@ fn add_type(target_info: TargetInfo, id: TypeId, types: &Types, impls: &mut Impl
types,
impls,
),
RocTagUnion::SingleTag {
RocTagUnion::SingleTagStruct {
name,
tag_name,
payload_fields,
is_recursive,
} => {
add_single_tag_union(
add_single_tag_struct(
name,
tag_name,
payload_fields,
types,
impls,
target_info,
*is_recursive,
);
}
RocTagUnion::NonNullableUnwrapped {
name,
tag_name,
payload,
} => {
add_tag_union(
Recursiveness::Recursive,
name,
target_info,
id,
&[(tag_name.clone(), Some(*payload))],
None,
0,
0,
types,
impls,
);
}
}
@ -284,15 +300,16 @@ fn add_type(target_info: TargetInfo, id: TypeId, types: &Types, impls: &mut Impl
}
}
fn add_single_tag_union(
fn add_single_tag_struct(
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,
) {
let unused = (); // TODO need to make this work for more single-tag structs!
// Store single-tag unions as structs rather than enums,
// because they have only one alternative. However, still
// offer the usual tag union APIs.
@ -314,9 +331,6 @@ fn add_single_tag_union(
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");
@ -486,7 +500,7 @@ fn add_discriminant(
discriminant_name
}
#[derive(Copy, Clone)]
#[derive(Copy, Clone, PartialEq, Debug)]
enum Recursiveness {
Recursive,
NonRecursive,
@ -510,14 +524,19 @@ fn add_tag_union(
debug_assert_ne!(tags.len(), 0);
let tag_names = tags.iter().map(|(name, _)| name).cloned().collect();
let discriminant_name = add_discriminant(
name,
target_info,
tag_names,
discriminant_size,
types,
impls,
);
let discriminant_name = if discriminant_size > 0 {
add_discriminant(
name,
target_info,
tag_names,
discriminant_size,
types,
impls,
)
} else {
// If there's no discriminant, use an empty string for the name.
String::new()
};
let typ = types.get_type(type_id);
let size_rounded_to_alignment = types.size_rounded_to_alignment(type_id);
let (actual_self, actual_self_mut, actual_other, union_name) = match recursiveness {
@ -611,35 +630,57 @@ pub struct {name} {{
match recursiveness {
Recursiveness::Recursive => {
add_decl(
impls,
opt_impl.clone(),
target_info,
RECURSIVE_TAG_UNION_STORAGE.to_string(),
);
let storage_fn = if discriminant_size == 0 {
r#"fn storage(&self) -> Option<&core::cell::Cell<roc_std::Storage>> {
let untagged = self.pointer as *const core::cell::Cell<roc_std::Storage>;
unsafe {
Some(&*untagged.sub(1))
}
}"#
.to_string()
} else {
RECURSIVE_TAG_UNION_STORAGE.to_string()
};
add_decl(impls, opt_impl.clone(), target_info, storage_fn);
if tags.len() <= max_pointer_tagged_variants(target_info.architecture) {
bitmask = format!("{:#b}", tagged_pointer_bitmask(target_info.architecture));
add_decl(
impls,
opt_impl.clone(),
target_info,
format!(
r#"{DISCRIMINANT_DOC_COMMENT}
if discriminant_size == 0 {
add_decl(
impls,
opt_impl.clone(),
target_info,
format!(
r#"/// This is a single-tag union, so it has no alternatives
/// to discriminate bewteen. This method is only included for completeness.
pub fn discriminant(&self) -> () {{
()
}}"#
),
);
} else {
add_decl(
impls,
opt_impl.clone(),
target_info,
format!(
r#"{DISCRIMINANT_DOC_COMMENT}
pub fn discriminant(&self) -> {discriminant_name} {{
// The discriminant is stored in the unused bytes at the end of the recursive pointer
unsafe {{ core::mem::transmute::<u8, {discriminant_name}>((self.pointer as u8) & {bitmask}) }}
}}"#
),
);
),
);
add_decl(
impls,
opt_impl.clone(),
target_info,
format!(
r#"/// Internal helper
add_decl(
impls,
opt_impl.clone(),
target_info,
format!(
r#"/// Internal helper
fn tag_discriminant(pointer: *mut {union_name}, discriminant: {discriminant_name}) -> *mut {union_name} {{
// The discriminant is stored in the unused bytes at the end of the union pointer
let untagged = (pointer as usize) & (!{bitmask} as usize);
@ -647,21 +688,22 @@ pub struct {name} {{
tagged as *mut {union_name}
}}"#
),
);
),
);
add_decl(
impls,
opt_impl.clone(),
target_info,
format!(
r#"/// Internal helper
add_decl(
impls,
opt_impl.clone(),
target_info,
format!(
r#"/// Internal helper
fn union_pointer(&self) -> *mut {union_name} {{
// The discriminant is stored in the unused bytes at the end of the union pointer
((self.pointer as usize) & (!{bitmask} as usize)) as *mut {union_name}
}}"#
),
);
),
);
}
} else {
todo!(
"Support {} tags in a recursive tag union on target_info {:?}. (This is too many tags for pointer tagging to work, so we need to generate different glue.)",
@ -752,10 +794,13 @@ pub struct {name} {{
r#"{{
let ptr = (self.pointer as usize & !{bitmask}) as *mut {union_name};
let mut uninitialized = core::mem::MaybeUninit::uninit();
let swapped = core::mem::replace(
&mut (*ptr).{tag_name},
core::mem::ManuallyDrop::new(uninitialized.assume_init()),
);
let swapped = unsafe {{
core::mem::replace(
&mut (*ptr).{tag_name},
core::mem::ManuallyDrop::new(uninitialized.assume_init()),
)
}};
core::mem::forget(self);
core::mem::ManuallyDrop::into_inner(swapped)
@ -765,7 +810,7 @@ pub struct {name} {{
r#"{{
let ptr = (self.pointer as usize & !{bitmask}) as *mut {union_name};
&(*ptr).{tag_name}
unsafe {{ &(*ptr).{tag_name} }}
}}"#
);
// we need `mut self` for the argument because of ManuallyDrop
@ -794,10 +839,13 @@ pub struct {name} {{
owned_get_payload = format!(
r#"{{
let mut uninitialized = core::mem::MaybeUninit::uninit();
let swapped = core::mem::replace(
&mut self.{tag_name},
core::mem::ManuallyDrop::new(uninitialized.assume_init()),
);
let swapped = unsafe {{
core::mem::replace(
&mut self.{tag_name},
core::mem::ManuallyDrop::new(uninitialized.assume_init()),
);
}}
core::mem::forget(self);
core::mem::ManuallyDrop::into_inner(swapped)
@ -865,36 +913,45 @@ pub struct {name} {{
};
{
let note_to_self = (); // IDEA: make Recursiveness be a 3-way tag, move discriminant info into there
let body = match recursiveness {
Recursiveness::Recursive => {
let pointer_val = if discriminant_size == 0 {
"ptr".to_string()
} else {
format!(
"Self::tag_discriminant(ptr, {discriminant_name}::{tag_name})"
)
};
format!(
r#"
let size = core::mem::size_of::<{union_name}>();
let align = core::mem::align_of::<{union_name}>() as u32;
let size = core::mem::size_of::<{union_name}>();
let align = core::mem::align_of::<{union_name}>() as u32;
unsafe {{
let ptr = roc_std::roc_alloc_refcounted::<{union_name}>();
unsafe {{
let ptr = roc_std::roc_alloc_refcounted::<{union_name}>();
*ptr = {union_name} {{
{tag_name}: {args_to_payload}
}};
*ptr = {union_name} {{
{tag_name}: {args_to_payload}
}};
Self {{
pointer: Self::tag_discriminant(ptr, {discriminant_name}::{tag_name}),
}}
}}"#
Self {{
pointer: {pointer_val},
}}
}}"#
)
}
Recursiveness::NonRecursive => {
format!(
r#"
let mut answer = Self {{
{tag_name}: {args_to_payload}
}};
let mut answer = Self {{
{tag_name}: {args_to_payload}
}};
answer.set_discriminant({discriminant_name}::{tag_name});
answer.set_discriminant({discriminant_name}::{tag_name});
answer"#
answer"#
)
}
};
@ -911,41 +968,67 @@ pub struct {name} {{
);
}
add_decl(
impls,
opt_impl.clone(),
target_info,
format!(
r#"/// Unsafely assume the given `{name}` has a `.discriminant()` of `{tag_name}` and convert it to `{tag_name}`'s payload.
/// (Always examine `.discriminant()` first to make sure this is the correct variant!)
/// Panics in debug builds if the `.discriminant()` doesn't return `{tag_name}`.
pub unsafe fn into_{tag_name}({self_for_into}) -> {owned_ret_type} {{
debug_assert_eq!(self.discriminant(), {discriminant_name}::{tag_name});
{
let fn_heading = if discriminant_size == 0 {
format!(
r#"/// Since `{name}` only has one tag (namely, `{tag_name}`),
/// convert it to `{tag_name}`'s payload.
pub fn into_{tag_name}({self_for_into}) -> {owned_ret_type} {{"#
)
} else {
format!(
r#"/// Unsafely assume the given `{name}` has a `.discriminant()` of `{tag_name}` and convert it to `{tag_name}`'s payload.
/// (Always examine `.discriminant()` first to make sure this is the correct variant!)
/// Panics in debug builds if the `.discriminant()` doesn't return `{tag_name}`.
pub unsafe fn into_{tag_name}({self_for_into}) -> {owned_ret_type} {{
debug_assert_eq!(self.discriminant(), {discriminant_name}::{tag_name});"#
)
};
add_decl(
impls,
opt_impl.clone(),
target_info,
format!(
r#"{fn_heading}
let payload = {owned_get_payload};
{owned_ret}
}}"#,
),
);
),
);
}
add_decl(
impls,
opt_impl.clone(),
target_info,
format!(
r#"/// Unsafely assume the given `{name}` has a `.discriminant()` of `{tag_name}` and return its payload.
/// (Always examine `.discriminant()` first to make sure this is the correct variant!)
/// Panics in debug builds if the `.discriminant()` doesn't return `{tag_name}`.
pub unsafe fn as_{tag_name}(&self) -> {borrowed_ret_type} {{
debug_assert_eq!(self.discriminant(), {discriminant_name}::{tag_name});
{
let fn_heading = if discriminant_size == 0 {
format!(
r#"/// Since `{name}` only has one tag (namely, `{tag_name}`),
/// convert it to `{tag_name}`'s payload.
pub fn as_{tag_name}(&self) -> {borrowed_ret_type} {{"#
)
} else {
format!(
r#"/// Unsafely assume the given `{name}` has a `.discriminant()` of `{tag_name}` and return its payload.
/// (Always examine `.discriminant()` first to make sure this is the correct variant!)
/// Panics in debug builds if the `.discriminant()` doesn't return `{tag_name}`.
pub unsafe fn as_{tag_name}(&self) -> {borrowed_ret_type} {{
debug_assert_eq!(self.discriminant(), {discriminant_name}::{tag_name});"#
)
};
add_decl(
impls,
opt_impl.clone(),
target_info,
format!(
r#"{fn_heading}
let payload = {borrowed_get_payload};
{borrowed_ret}
}}"#,
),
);
),
);
}
} else if Some(tag_index) == null_tag_index {
// The null tag index only occurs for nullable-wrapped tag unions,
// and it always has no payload. This is the one scenario where
@ -1012,24 +1095,36 @@ pub struct {name} {{
let opt_impl = Some(format!("impl Drop for {name}"));
let mut drop_payload = String::new();
write_impl_tags(
3,
tags.iter(),
&discriminant_name,
&mut drop_payload,
|tag_name, opt_payload_id| {
match opt_payload_id {
Some(payload_id) if cannot_derive_copy(types.get_type(payload_id), types) => {
format!("unsafe {{ core::mem::ManuallyDrop::drop(&mut {actual_self_mut}.{tag_name}) }},",)
if discriminant_size == 0 {
let (tag_name, _) = tags.first().unwrap();
// There's only one tag, so there's no discriminant and no need to match;
// just drop the pointer.
drop_payload.push_str(&format!(
r#"unsafe {{ core::mem::ManuallyDrop::drop(&mut core::ptr::read(self.pointer).{tag_name}); }}"#
));
} else {
write_impl_tags(
3,
tags.iter(),
&discriminant_name,
&mut drop_payload,
|tag_name, opt_payload_id| {
match opt_payload_id {
Some(payload_id)
if cannot_derive_copy(types.get_type(payload_id), types) =>
{
format!("unsafe {{ core::mem::ManuallyDrop::drop(&mut {actual_self_mut}.{tag_name}) }},",)
}
_ => {
// If it had no payload, or if the payload had no pointers,
// there's nothing to clean up, so do `=> {}` for the branch.
"{}".to_string()
}
}
_ => {
// If it had no payload, or if the payload had no pointers,
// there's nothing to clean up, so do `=> {}` for the branch.
"{}".to_string()
}
}
},
);
},
);
}
// Drop works differently for recursive vs non-recursive tag unions.
let drop_fn = match recursiveness {
@ -1089,22 +1184,33 @@ pub struct {name} {{
"#
.to_string();
write_impl_tags(
3,
tags.iter(),
&discriminant_name,
&mut buf,
|tag_name, opt_payload_id| {
if opt_payload_id.is_some() {
format!("{actual_self}.{tag_name} == {actual_other}.{tag_name},")
} else {
// if the tags themselves had been unequal, we already would have
// early-returned with false, so this means the tags were equal
// and there's no payload; return true!
"true,".to_string()
}
},
);
if discriminant_size == 0 {
let (tag_name, _) = tags.first().unwrap();
// There's only one tag, so there's no discriminant and no need to match;
// just return whether my payload equals the other one.
buf.push_str(&format!(
r#"{INDENT}{INDENT}{INDENT}{INDENT}(*self.pointer).{tag_name} == (*other.pointer).{tag_name}
"#
));
} else {
write_impl_tags(
3,
tags.iter(),
&discriminant_name,
&mut buf,
|tag_name, opt_payload_id| {
if opt_payload_id.is_some() {
format!("{actual_self}.{tag_name} == {actual_other}.{tag_name},")
} else {
// if the tags themselves had been unequal, we already would have
// early-returned with false, so this means the tags were equal
// and there's no payload; return true!
"true,".to_string()
}
},
);
}
buf.push_str(INDENT);
buf.push_str(INDENT);
@ -1118,7 +1224,21 @@ pub struct {name} {{
// The PartialOrd impl for the tag union
{
let opt_impl = Some(format!("impl PartialOrd for {name}"));
let mut buf = r#"fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
let body = if discriminant_size == 0 {
let (tag_name, _) = tags.first().unwrap();
format!(
r#"fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {{
unsafe {{
(&*self.pointer).{tag_name}.partial_cmp(&(*other.pointer).{tag_name})
}}
}}
"#
)
.to_string()
} else {
let mut buf = r#"fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
match self.discriminant().partial_cmp(&other.discriminant()) {
Some(core::cmp::Ordering::Equal) => {}
not_eq => return not_eq,
@ -1126,38 +1246,55 @@ pub struct {name} {{
unsafe {
"#
.to_string();
.to_string();
write_impl_tags(
3,
tags.iter(),
&discriminant_name,
&mut buf,
|tag_name, opt_payload_id| {
if opt_payload_id.is_some() {
format!("{actual_self}.{tag_name}.partial_cmp(&{actual_other}.{tag_name}),",)
} else {
// if the tags themselves had been unequal, we already would have
// early-returned, so this means the tags were equal and there's
// no payload; return Equal!
"Some(core::cmp::Ordering::Equal),".to_string()
}
},
);
write_impl_tags(
3,
tags.iter(),
&discriminant_name,
&mut buf,
|tag_name, opt_payload_id| {
if opt_payload_id.is_some() {
format!("{actual_self}.{tag_name}.partial_cmp(&{actual_other}.{tag_name}),",)
} else {
// if the tags themselves had been unequal, we already would have
// early-returned, so this means the tags were equal and there's
// no payload; return Equal!
"Some(core::cmp::Ordering::Equal),".to_string()
}
},
);
buf.push_str(INDENT);
buf.push_str(INDENT);
buf.push_str("}\n");
buf.push_str(INDENT);
buf.push('}');
buf.push_str(INDENT);
buf.push_str(INDENT);
buf.push_str("}\n");
buf.push_str(INDENT);
buf.push('}');
add_decl(impls, opt_impl, target_info, buf);
buf
};
add_decl(impls, opt_impl, target_info, body);
}
// The Ord impl for the tag union
if !has_float(typ, types) {
let opt_impl = Some(format!("impl Ord for {name}"));
let mut buf = r#"fn cmp(&self, other: &Self) -> core::cmp::Ordering {
let body = if discriminant_size == 0 {
let (tag_name, _) = tags.first().unwrap();
format!(
r#"fn cmp(&self, other: &Self) -> core::cmp::Ordering {{
unsafe {{
(&*self.pointer).{tag_name}.cmp(&(*other.pointer).{tag_name})
}}
}}
"#
)
.to_string()
} else {
let mut buf = r#"fn cmp(&self, other: &Self) -> core::cmp::Ordering {
match self.discriminant().cmp(&other.discriminant()) {
core::cmp::Ordering::Equal => {}
not_eq => return not_eq,
@ -1165,32 +1302,35 @@ pub struct {name} {{
unsafe {
"#
.to_string();
.to_string();
write_impl_tags(
3,
tags.iter(),
&discriminant_name,
&mut buf,
|tag_name, opt_payload_id| {
if opt_payload_id.is_some() {
format!("{actual_self}.{tag_name}.cmp(&{actual_other}.{tag_name}),",)
} else {
// if the tags themselves had been unequal, we already would have
// early-returned, so this means the tags were equal and there's
// no payload; return Equal!
"core::cmp::Ordering::Equal,".to_string()
}
},
);
write_impl_tags(
3,
tags.iter(),
&discriminant_name,
&mut buf,
|tag_name, opt_payload_id| {
if opt_payload_id.is_some() {
format!("{actual_self}.{tag_name}.cmp(&{actual_other}.{tag_name}),",)
} else {
// if the tags themselves had been unequal, we already would have
// early-returned, so this means the tags were equal and there's
// no payload; return Equal!
"core::cmp::Ordering::Equal,".to_string()
}
},
);
buf.push_str(INDENT);
buf.push_str(INDENT);
buf.push_str("}\n");
buf.push_str(INDENT);
buf.push('}');
buf.push_str(INDENT);
buf.push_str(INDENT);
buf.push_str("}\n");
buf.push_str(INDENT);
buf.push('}');
add_decl(impls, opt_impl, target_info, buf);
buf
};
add_decl(impls, opt_impl, target_info, body);
}
// The Clone impl for the tag union
@ -1269,26 +1409,39 @@ pub struct {name} {{
let opt_impl = Some(format!("impl core::hash::Hash for {name}"));
let mut buf = r#"fn hash<H: core::hash::Hasher>(&self, state: &mut H) {"#.to_string();
write_impl_tags(
2,
tags.iter(),
&discriminant_name,
&mut buf,
|tag_name, opt_payload_id| {
let hash_tag = format!("{discriminant_name}::{tag_name}.hash(state)");
if discriminant_size == 0 {
let (tag_name, _) = tags.first().unwrap();
if opt_payload_id.is_some() {
format!(
r#"unsafe {{
// There's only one tag, so there's no discriminant and no need to match;
// just return whether my payload equals the other one.
buf.push_str(&format!(
r#"
unsafe {{
(*self.pointer).{tag_name}.hash(state)
}}"#
));
} else {
write_impl_tags(
2,
tags.iter(),
&discriminant_name,
&mut buf,
|tag_name, opt_payload_id| {
let hash_tag = format!("{discriminant_name}::{tag_name}.hash(state)");
if opt_payload_id.is_some() {
format!(
r#"unsafe {{
{hash_tag};
{actual_self}.{tag_name}.hash(state);
}},"#
)
} else {
format!("{},", hash_tag)
}
},
);
)
} else {
format!("{},", hash_tag)
}
},
);
};
buf.push_str(INDENT);
buf.push('}');
@ -1307,63 +1460,75 @@ pub struct {name} {{
"#
);
write_impl_tags(
3,
tags.iter(),
&discriminant_name,
&mut buf,
|tag_name, opt_payload_id| match opt_payload_id {
Some(payload_id) => {
// If it's a ManuallyDrop, we need a `*` prefix to dereference it
// (because otherwise we're using ManuallyDrop's Debug instance
// rather than the Debug instance of the value it wraps).
let payload_type = types.get_type(payload_id);
let deref_str = if cannot_derive_copy(payload_type, types) {
"&*"
} else {
"&"
};
if discriminant_size == 0 {
let (tag_name, _) = tags.first().unwrap();
let fields_str = match payload_type {
RocType::Unit
| RocType::EmptyTagUnion
| RocType::RocStr
| RocType::Bool
| RocType::Num(_)
| RocType::RocList(_)
| RocType::RocDict(_, _)
| RocType::RocSet(_)
| RocType::RocBox(_)
| RocType::TagUnion(_)
| RocType::RocResult(_, _)
| RocType::Struct { .. }
| RocType::RecursivePointer { .. } => {
format!(".field({deref_str}{actual_self}.{tag_name})")
}
RocType::TagUnionPayload { fields, .. } => {
let mut buf = Vec::new();
// There's only one tag, so there's no discriminant and no need to match;
// just return whether my payload equals the other one.
buf.push_str(&format!(
r#"f.debug_tuple("{tag_name}")
.field(&(*self.pointer).{tag_name})
.finish()"#,
));
} else {
write_impl_tags(
3,
tags.iter(),
&discriminant_name,
&mut buf,
|tag_name, opt_payload_id| match opt_payload_id {
Some(payload_id) => {
// If it's a ManuallyDrop, we need a `*` prefix to dereference it
// (because otherwise we're using ManuallyDrop's Debug instance
// rather than the Debug instance of the value it wraps).
let payload_type = types.get_type(payload_id);
let deref_str = if cannot_derive_copy(payload_type, types) {
"&*"
} else {
"&"
};
for (label, _) in fields {
// Needs an "f" prefix
buf.push(format!(
".field(&({deref_str}{actual_self}.{tag_name}).f{label})"
));
let fields_str = match payload_type {
RocType::Unit
| RocType::EmptyTagUnion
| RocType::RocStr
| RocType::Bool
| RocType::Num(_)
| RocType::RocList(_)
| RocType::RocDict(_, _)
| RocType::RocSet(_)
| RocType::RocBox(_)
| RocType::TagUnion(_)
| RocType::RocResult(_, _)
| RocType::Struct { .. }
| RocType::RecursivePointer { .. } => {
format!(".field({deref_str}{actual_self}.{tag_name})")
}
RocType::TagUnionPayload { fields, .. } => {
let mut buf = Vec::new();
buf.join("\n")
}
RocType::Function { .. } => todo!(),
};
for (label, _) in fields {
// Needs an "f" prefix
buf.push(format!(
".field(&({deref_str}{actual_self}.{tag_name}).f{label})"
));
}
format!(
r#"f.debug_tuple("{tag_name}")
buf.join("\n")
}
RocType::Function { .. } => todo!(),
};
format!(
r#"f.debug_tuple("{tag_name}")
{fields_str}
.finish(),"#,
)
}
None => format!(r#"f.write_str("{tag_name}"),"#),
},
);
)
}
None => format!(r#"f.write_str("{tag_name}"),"#),
},
);
}
buf.push_str(INDENT);
buf.push_str(INDENT);
@ -1522,7 +1687,8 @@ fn type_name(id: TypeId, types: &Types) -> String {
| RocType::TagUnion(RocTagUnion::Enumeration { name, .. })
| RocType::TagUnion(RocTagUnion::NullableWrapped { name, .. })
| RocType::TagUnion(RocTagUnion::NullableUnwrapped { name, .. })
| RocType::TagUnion(RocTagUnion::SingleTag { name, .. }) => name.clone(),
| RocType::TagUnion(RocTagUnion::NonNullableUnwrapped { name, .. })
| RocType::TagUnion(RocTagUnion::SingleTagStruct { name, .. }) => name.clone(),
RocType::RecursivePointer(content) => type_name(*content, types),
RocType::Function { name, .. } => name.clone(),
}
@ -1727,10 +1893,13 @@ pub struct {name} {{
let assign_payload = if cannot_derive_copy {
r#"{{
let mut uninitialized = core::mem::MaybeUninit::uninit();
let swapped = core::mem::replace(
&mut *self.pointer,
core::mem::ManuallyDrop::new(uninitialized.assume_init()),
);
let swapped = unsafe {{
core::mem::replace(
&mut *self.pointer,
core::mem::ManuallyDrop::new(uninitialized.assume_init()),
)
}};
core::mem::forget(self);
core::mem::ManuallyDrop::into_inner(swapped)
@ -2150,14 +2319,8 @@ fn cannot_derive_copy(roc_type: &RocType, types: &Types) -> bool {
| RocType::TagUnion(RocTagUnion::NullableWrapped { .. })
| RocType::TagUnion(RocTagUnion::Recursive { .. })
| RocType::RecursivePointer { .. }
| RocType::TagUnion(RocTagUnion::SingleTag {
is_recursive: true, ..
}) => true,
RocType::TagUnion(RocTagUnion::SingleTag {
is_recursive: false,
payload_fields,
..
}) => payload_fields
| RocType::TagUnion(RocTagUnion::NonNullableUnwrapped { .. }) => true,
RocType::TagUnion(RocTagUnion::SingleTagStruct { payload_fields, .. }) => payload_fields
.iter()
.any(|type_id| cannot_derive_copy(types.get_type(*type_id), types)),
RocType::TagUnion(RocTagUnion::NonRecursive { tags, .. }) => {
@ -2218,7 +2381,7 @@ fn has_float_help(roc_type: &RocType, types: &Types, do_not_recurse: &[TypeId])
RocType::TagUnionPayload { fields, .. } => fields
.iter()
.any(|(_, type_id)| has_float_help(types.get_type(*type_id), types, do_not_recurse)),
RocType::TagUnion(RocTagUnion::SingleTag { payload_fields, .. }) => payload_fields
RocType::TagUnion(RocTagUnion::SingleTagStruct { payload_fields, .. }) => payload_fields
.iter()
.any(|type_id| has_float_help(types.get_type(*type_id), types, do_not_recurse)),
RocType::TagUnion(RocTagUnion::Recursive { tags, .. })
@ -2236,19 +2399,20 @@ fn has_float_help(roc_type: &RocType, types: &Types, do_not_recurse: &[TypeId])
.any(|id| has_float_help(types.get_type(*id), types, do_not_recurse))
})
}
RocType::TagUnion(RocTagUnion::NullableUnwrapped {
non_null_payload: content,
RocType::TagUnion(RocTagUnion::NonNullableUnwrapped { payload, .. })
| RocType::TagUnion(RocTagUnion::NullableUnwrapped {
non_null_payload: payload,
..
})
| RocType::RecursivePointer(content) => {
if do_not_recurse.contains(content) {
| RocType::RecursivePointer(payload) => {
if do_not_recurse.contains(payload) {
false
} else {
let mut do_not_recurse: Vec<TypeId> = do_not_recurse.into();
do_not_recurse.push(*content);
do_not_recurse.push(*payload);
has_float_help(types.get_type(*content), types, &do_not_recurse)
has_float_help(types.get_type(*payload), types, &do_not_recurse)
}
}
}