mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-10 18:08:55 +03:00
Merge remote-tracking branch 'origin/trunk' into more-tyck
This commit is contained in:
commit
c161140aec
@ -50,14 +50,47 @@ If you plan on using `nix-shell` regularly, check out [direnv](https://direnv.ne
|
|||||||
The editor is a :construction:WIP:construction: and not ready yet to replace your favorite editor, although if you want to try it out on nix, read on.
|
The editor is a :construction:WIP:construction: and not ready yet to replace your favorite editor, although if you want to try it out on nix, read on.
|
||||||
`cargo run edit` should work from NixOS, if you use a nix-shell from inside another OS, follow the instructions below.
|
`cargo run edit` should work from NixOS, if you use a nix-shell from inside another OS, follow the instructions below.
|
||||||
|
|
||||||
#### Nvidia GPU
|
#### from nix flake
|
||||||
|
|
||||||
|
Running the ediotr may fail using the classic nix-shell, we recommend using the nix flake, see [enabling nix flakes](https://nixos.wiki/wiki/Flakes).
|
||||||
|
|
||||||
|
start a nix shell using `nix develop` and follow the instructions below for your graphics configuration.
|
||||||
|
|
||||||
|
##### Nvidia GPU
|
||||||
|
|
||||||
|
```
|
||||||
|
nix run --override-input nixpkgs nixpkgs/nixos-21.11 --impure github:guibou/nixGL#nixVulkanNvidia -- cargo run edit
|
||||||
|
```
|
||||||
|
|
||||||
|
If you get an error like:
|
||||||
|
```
|
||||||
|
error: unable to execute '/nix/store/qk6...wjla-nixVulkanNvidia-470.103.01/bin/nixVulkanNvidia': No such file or directory
|
||||||
|
```
|
||||||
|
The intel command should work:
|
||||||
|
```
|
||||||
|
nix run --override-input nixpkgs nixpkgs/nixos-21.11 --impure github:guibou/nixGL#nixVulkanIntel -- cargo run edit
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Integrated Intel Graphics
|
||||||
|
|
||||||
|
```
|
||||||
|
nix run --override-input nixpkgs nixpkgs/nixos-21.11 --impure github:guibou/nixGL#nixVulkanIntel -- cargo run edit
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Other configs
|
||||||
|
|
||||||
|
Check the [nixGL repo](https://github.com/guibou/nixGL) for other graphics configurations. Feel free to ask us for help if you get stuck.
|
||||||
|
|
||||||
|
#### using a classic nix-shell
|
||||||
|
|
||||||
|
##### Nvidia GPU
|
||||||
|
|
||||||
Outside of a nix shell, execute the following:
|
Outside of a nix shell, execute the following:
|
||||||
```
|
```
|
||||||
nix-channel --add https://github.com/guibou/nixGL/archive/main.tar.gz nixgl && nix-channel --update
|
nix-channel --add https://github.com/guibou/nixGL/archive/main.tar.gz nixgl && nix-channel --update
|
||||||
nix-env -iA nixgl.auto.nixVulkanNvidia
|
nix-env -iA nixgl.auto.nixVulkanNvidia
|
||||||
```
|
```
|
||||||
Running the editor does not work with `nix-shell --pure`.
|
Running the editor does not work with `nix-shell --pure`, instead run:
|
||||||
```
|
```
|
||||||
nix-shell
|
nix-shell
|
||||||
```
|
```
|
||||||
@ -66,25 +99,11 @@ nix-shell
|
|||||||
nixVulkanNvidia-460.91.03 cargo run edit
|
nixVulkanNvidia-460.91.03 cargo run edit
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Integrated Intel Graphics
|
##### Integrated Intel Graphics
|
||||||
|
|
||||||
:exclamation: ** Our Nix setup currently cannot run the editor with integrated intel graphics, see #1856 ** :exclamation:
|
nix-shell does not work here, use the flake instead; check the section "Integrated Intel Graphics" under "from nix flake".
|
||||||
|
|
||||||
Outside of a nix shell, run:
|
##### Other configs
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/guibou/nixGL
|
|
||||||
cd nixGL
|
|
||||||
nix-env -f ./ -iA nixVulkanIntel
|
|
||||||
```
|
|
||||||
|
|
||||||
cd to the roc repo, and run (without --pure):
|
|
||||||
```
|
|
||||||
nix-shell
|
|
||||||
nixVulkanIntel cargo run edit
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Other configs
|
|
||||||
|
|
||||||
Check the [nixGL repo](https://github.com/guibou/nixGL) for other graphics configurations.
|
Check the [nixGL repo](https://github.com/guibou/nixGL) for other graphics configurations.
|
||||||
|
|
||||||
|
@ -29,8 +29,8 @@ bumpalo = { version = "3.8.0", features = ["collections"] }
|
|||||||
ven_graph = { path = "../vendor/pathfinding" }
|
ven_graph = { path = "../vendor/pathfinding" }
|
||||||
target-lexicon = "0.12.3"
|
target-lexicon = "0.12.3"
|
||||||
clap = { version = "3.1.15", default-features = false, features = ["std", "color", "suggestions", "derive"] }
|
clap = { version = "3.1.15", default-features = false, features = ["std", "color", "suggestions", "derive"] }
|
||||||
|
indoc = "1.0.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.0.0"
|
pretty_assertions = "1.0.0"
|
||||||
indoc = "1.0.3"
|
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
use crate::structs::Structs;
|
use crate::structs::Structs;
|
||||||
use crate::types::{TypeId, Types};
|
use crate::types::{TypeId, Types};
|
||||||
use crate::{enums::Enums, types::RocType};
|
use crate::{enums::Enums, types::RocType};
|
||||||
@ -299,29 +297,82 @@ fn add_tag_union(
|
|||||||
// 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 tags = tags
|
let tags: Vec<_> =
|
||||||
.into_iter()
|
tags.into_iter()
|
||||||
.map(|(tag_name, payload_vars)| {
|
.map(|(tag_name, payload_vars)| {
|
||||||
let payloads = payload_vars
|
match payload_vars.len() {
|
||||||
.iter()
|
0 => {
|
||||||
.map(|payload_var| add_type(env, *payload_var, types))
|
// no payload
|
||||||
.collect::<Vec<TypeId>>();
|
(tag_name, None)
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
// there's 1 payload item, so it doesn't need its own
|
||||||
|
// struct - e.g. for `[ Foo Str, Bar Str ]` both of them
|
||||||
|
// can have payloads of plain old Str, no struct wrapper needed.
|
||||||
|
let payload_var = payload_vars.get(0).unwrap();
|
||||||
|
let payload_id = add_type(env, *payload_var, types);
|
||||||
|
|
||||||
(tag_name, payloads)
|
(tag_name, Some(payload_id))
|
||||||
})
|
}
|
||||||
.collect();
|
_ => {
|
||||||
|
// create a struct type for the payload and save it
|
||||||
|
|
||||||
|
let struct_name = format!("{}_{}", name, tag_name); // e.g. "MyUnion_MyVariant"
|
||||||
|
|
||||||
|
let fields = payload_vars.iter().enumerate().map(|(index, payload_var)| {
|
||||||
|
(format!("f{}", index).into(), *payload_var)
|
||||||
|
});
|
||||||
|
let struct_id = add_struct(env, struct_name, fields, types);
|
||||||
|
|
||||||
|
(tag_name, Some(struct_id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
let typ = match env.layout_cache.from_var(env.arena, var, subs).unwrap() {
|
let typ = match env.layout_cache.from_var(env.arena, var, subs).unwrap() {
|
||||||
Layout::Struct { .. } => {
|
Layout::Struct { .. } => {
|
||||||
// a single-tag union with multiple payload values, e.g. [ Foo Str Str ]
|
// a single-tag union with multiple payload values, e.g. [ Foo Str Str ]
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
Layout::Union(_) => todo!(),
|
Layout::Union(union_layout) => {
|
||||||
|
use roc_mono::layout::UnionLayout::*;
|
||||||
|
|
||||||
|
match union_layout {
|
||||||
|
// A non-recursive tag union
|
||||||
|
// e.g. `Result ok err : [ Ok ok, Err err ]`
|
||||||
|
NonRecursive(_) => RocType::TagUnion { name, tags },
|
||||||
|
// A recursive tag union (general case)
|
||||||
|
// e.g. `Expr : [ Sym Str, Add Expr Expr ]`
|
||||||
|
Recursive(_) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
// 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)) ]`
|
||||||
|
NonNullableUnwrapped(_) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
// 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 { .. } => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
// 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 { .. } => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Layout::Builtin(builtin) => match builtin {
|
Layout::Builtin(builtin) => match builtin {
|
||||||
Builtin::Int(int_width) => RocType::TagUnion {
|
Builtin::Int(_) => RocType::Enumeration {
|
||||||
tag_bytes: int_width.stack_size().try_into().unwrap(),
|
|
||||||
name,
|
name,
|
||||||
tags,
|
tags: tags.into_iter().map(|(tag_name, _)| tag_name).collect(),
|
||||||
},
|
},
|
||||||
Builtin::Bool => RocType::Bool,
|
Builtin::Bool => RocType::Bool,
|
||||||
Builtin::Float(_)
|
Builtin::Float(_)
|
||||||
|
@ -1,49 +1,36 @@
|
|||||||
|
use roc_mono::layout::UnionLayout;
|
||||||
|
|
||||||
|
use indoc::indoc;
|
||||||
|
|
||||||
use crate::types::{RocType, TypeId, Types};
|
use crate::types::{RocType, TypeId, Types};
|
||||||
use std::fmt::{self, Write};
|
use std::{
|
||||||
|
convert::TryInto,
|
||||||
|
fmt::{self, Write},
|
||||||
|
};
|
||||||
|
|
||||||
pub static TEMPLATE: &[u8] = include_bytes!("../templates/template.rs");
|
pub static TEMPLATE: &[u8] = include_bytes!("../templates/template.rs");
|
||||||
pub static HEADER: &[u8] = include_bytes!("../templates/header.rs");
|
pub static HEADER: &[u8] = include_bytes!("../templates/header.rs");
|
||||||
static INDENT: &str = " ";
|
const INDENT: &str = " ";
|
||||||
|
|
||||||
pub fn write_types(types: &Types, buf: &mut String) -> fmt::Result {
|
pub fn write_types(types: &Types, buf: &mut String) -> fmt::Result {
|
||||||
for id in types.sorted_ids() {
|
for id in types.sorted_ids() {
|
||||||
match types.get(id) {
|
match types.get(id) {
|
||||||
RocType::Struct { name, fields } => write_struct(name, fields, id, types, buf)?,
|
RocType::Struct { name, fields } => write_struct(name, fields, id, types, buf)?,
|
||||||
RocType::TagUnion {
|
RocType::Enumeration { tags, name } => {
|
||||||
tags,
|
if tags.len() == 1 {
|
||||||
name,
|
// An enumeration with one tag is a zero-sized unit type, so
|
||||||
tag_bytes,
|
// represent it as a zero-sized struct (e.g. "struct Foo()").
|
||||||
} => {
|
write_deriving(types.get(id), types, buf)?;
|
||||||
let is_enumeration = tags.iter().all(|(_, payloads)| payloads.is_empty());
|
writeln!(buf, "\nstruct {}();", type_name(id, types))?;
|
||||||
|
} else {
|
||||||
match tags.len() {
|
write_enumeration(name, types.get(id), tags.iter(), types, buf)?;
|
||||||
0 => {
|
}
|
||||||
// Empty tag unions can never come up at runtime,
|
}
|
||||||
// and so don't need declared types.
|
RocType::TagUnion { tags, name } => {
|
||||||
}
|
// Empty tag unions can never come up at runtime,
|
||||||
1 => {
|
// and so don't need declared types.
|
||||||
if is_enumeration {
|
if !tags.is_empty() {
|
||||||
// A tag union with one tag is a zero-sized unit type, so
|
write_tag_union(name, id, tags, types, buf)?;
|
||||||
// represent it as a zero-sized struct (e.g. "struct Foo()").
|
|
||||||
write_deriving(id, types, buf)?;
|
|
||||||
buf.write_str("\nstruct ")?;
|
|
||||||
write_type_name(id, types, buf)?;
|
|
||||||
buf.write_str("();\n")?;
|
|
||||||
} else {
|
|
||||||
// if it wasn't an enumeration
|
|
||||||
// this is a newtype wrapper around something,
|
|
||||||
// so write an alias for its contents
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
if is_enumeration {
|
|
||||||
write_deriving(id, types, buf)?;
|
|
||||||
write_enum(name, tags.iter().map(|(name, _)| name), *tag_bytes, buf)?;
|
|
||||||
} else {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RocType::RecursiveTagUnion { .. } => {
|
RocType::RecursiveTagUnion { .. } => {
|
||||||
@ -71,10 +58,13 @@ pub fn write_types(types: &Types, buf: &mut String) -> fmt::Result {
|
|||||||
| RocType::RocList(_)
|
| RocType::RocList(_)
|
||||||
| RocType::RocBox(_) => {}
|
| RocType::RocBox(_) => {}
|
||||||
RocType::TransparentWrapper { name, content } => {
|
RocType::TransparentWrapper { name, content } => {
|
||||||
write_deriving(id, types, buf)?;
|
write_deriving(types.get(id), types, buf)?;
|
||||||
write!(buf, "#[repr(transparent)]\npub struct {}(", name)?;
|
writeln!(
|
||||||
write_type_name(*content, types, buf)?;
|
buf,
|
||||||
buf.write_str(");\n")?;
|
"#[repr(transparent)]\npub struct {}({});",
|
||||||
|
name,
|
||||||
|
type_name(*content, types)
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,17 +72,624 @@ pub fn write_types(types: &Types, buf: &mut String) -> fmt::Result {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_enum<I: IntoIterator<Item = S>, S: AsRef<str>>(
|
fn write_tag_union(
|
||||||
name: &str,
|
name: &str,
|
||||||
tags: I,
|
type_id: TypeId,
|
||||||
tag_bytes: u8,
|
tags: &[(String, Option<TypeId>)],
|
||||||
|
types: &Types,
|
||||||
buf: &mut String,
|
buf: &mut String,
|
||||||
) -> fmt::Result {
|
) -> fmt::Result {
|
||||||
|
// The tag union's discriminant, e.g.
|
||||||
|
//
|
||||||
|
// #[repr(u8)]
|
||||||
|
// pub enum tag_MyTagUnion {
|
||||||
|
// Bar,
|
||||||
|
// Foo,
|
||||||
|
// }
|
||||||
|
let discriminant_name = format!("tag_{}", name);
|
||||||
|
let tag_names = tags.iter().map(|(name, _)| name);
|
||||||
|
let discriminant_type = RocType::Enumeration {
|
||||||
|
name: discriminant_name.clone(),
|
||||||
|
tags: tag_names.clone().cloned().collect(),
|
||||||
|
};
|
||||||
|
let typ = types.get(type_id);
|
||||||
|
|
||||||
|
write_enumeration(
|
||||||
|
&discriminant_name,
|
||||||
|
&discriminant_type,
|
||||||
|
tag_names,
|
||||||
|
types,
|
||||||
|
buf,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// The tag union's variant union, e.g.
|
||||||
|
//
|
||||||
|
// #[repr(C)]
|
||||||
|
// union union_MyTagUnion {
|
||||||
|
// Bar: u128,
|
||||||
|
// Foo: core::mem::ManuallyDrop<roc_std::RocStr>,
|
||||||
|
// }
|
||||||
|
let variant_name = format!("union_{}", name);
|
||||||
|
|
||||||
|
{
|
||||||
|
// No deriving for unions; we have to add the impls ourselves!
|
||||||
|
|
||||||
|
writeln!(buf, "\n#[repr(C)]\npub union {} {{", variant_name)?;
|
||||||
|
|
||||||
|
for (tag_name, opt_payload_id) in tags {
|
||||||
|
// If there's no payload, we don't need a variant for it.
|
||||||
|
if let Some(payload_id) = opt_payload_id {
|
||||||
|
let payload_type = types.get(*payload_id);
|
||||||
|
|
||||||
|
write!(buf, "{}{}: ", INDENT, tag_name)?;
|
||||||
|
|
||||||
|
if payload_type.has_pointer(types) {
|
||||||
|
// types with pointers need ManuallyDrop
|
||||||
|
// because rust unions don't (and can't)
|
||||||
|
// know how to drop them automatically!
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
"core::mem::ManuallyDrop<{}>,",
|
||||||
|
type_name(*payload_id, types)
|
||||||
|
)?;
|
||||||
|
} else {
|
||||||
|
writeln!(buf, "{},", type_name(*payload_id, types))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.write_str("}\n")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The tag union struct itself, e.g.
|
||||||
|
//
|
||||||
|
// #[repr(C)]
|
||||||
|
// pub struct MyTagUnion {
|
||||||
|
// variant: variant_MyTagUnion,
|
||||||
|
// tag: tag_MyTagUnion,
|
||||||
|
// }
|
||||||
|
{
|
||||||
|
// no deriving because it contains a union; we have to
|
||||||
|
// generate the impls explicitly!
|
||||||
|
|
||||||
|
write!(
|
||||||
|
buf,
|
||||||
|
"\n#[repr(C)]\npub struct {} {{\n{}variant: {},\n{}tag: {},\n}}\n",
|
||||||
|
name, INDENT, variant_name, INDENT, discriminant_name
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The impl for the tag union
|
||||||
|
{
|
||||||
|
write!(
|
||||||
|
buf,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
|
||||||
|
impl MyTagUnion {{
|
||||||
|
pub fn tag(&self) -> {} {{
|
||||||
|
self.tag
|
||||||
|
}}
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
discriminant_name
|
||||||
|
)?;
|
||||||
|
|
||||||
|
for (tag_name, opt_payload_id) in tags {
|
||||||
|
// Add a convenience constructor function to the impl, e.g.
|
||||||
|
//
|
||||||
|
// /// Construct a tag named Foo, with the appropriate payload
|
||||||
|
// pub fn Foo(payload: roc_std::RocStr) -> Self {
|
||||||
|
// Self {
|
||||||
|
// tag: tag_MyTagUnion::Foo,
|
||||||
|
// variant: variant_MyTagUnion {
|
||||||
|
// Foo: core::mem::ManuallyDrop::new(payload),
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
if let Some(payload_id) = opt_payload_id {
|
||||||
|
let payload_type = types.get(*payload_id);
|
||||||
|
let payload_type_name = type_name(*payload_id, types);
|
||||||
|
|
||||||
|
let (init_payload, get_payload, deref_for_as, self_for_into) =
|
||||||
|
if payload_type.has_pointer(types) {
|
||||||
|
(
|
||||||
|
"core::mem::ManuallyDrop::new(payload)",
|
||||||
|
format!(
|
||||||
|
"core::mem::ManuallyDrop::take(&mut self.variant.{})",
|
||||||
|
tag_name,
|
||||||
|
),
|
||||||
|
// Since this is a ManuallyDrop, our `as_` method will need
|
||||||
|
// to dereference the variant (e.g. `&self.variant.Foo`)
|
||||||
|
"&",
|
||||||
|
// we need `mut self` for the argument because of ManuallyDrop
|
||||||
|
"mut self",
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
"payload",
|
||||||
|
format!("self.variant.{}", tag_name),
|
||||||
|
// Since this is not a ManuallyDrop, our `as_` method will not
|
||||||
|
// want to dereference the variant (e.g. `self.variant.Foo` with no '&')
|
||||||
|
"",
|
||||||
|
// we don't need `mut self` unless we need ManuallyDrop
|
||||||
|
"self",
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
// Don't use indoc because this must be indented once!
|
||||||
|
r#"
|
||||||
|
/// Construct a tag named {}, with the appropriate payload
|
||||||
|
pub fn {}(payload: {}) -> Self {{
|
||||||
|
Self {{
|
||||||
|
tag: {}::{},
|
||||||
|
variant: {} {{
|
||||||
|
{}: {}
|
||||||
|
}},
|
||||||
|
}}
|
||||||
|
}}"#,
|
||||||
|
tag_name,
|
||||||
|
tag_name,
|
||||||
|
payload_type_name,
|
||||||
|
discriminant_name,
|
||||||
|
tag_name,
|
||||||
|
variant_name,
|
||||||
|
tag_name,
|
||||||
|
init_payload
|
||||||
|
)?;
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
// Don't use indoc because this must be indented once!
|
||||||
|
r#"
|
||||||
|
/// Unsafely assume the given {} has a .tag() of {} and convert it to {}'s payload.
|
||||||
|
/// (always examine .tag() first to make sure this is the correct variant!)
|
||||||
|
pub unsafe fn into_{}({}) -> {} {{
|
||||||
|
{}
|
||||||
|
}}"#,
|
||||||
|
name,
|
||||||
|
tag_name,
|
||||||
|
tag_name,
|
||||||
|
tag_name,
|
||||||
|
self_for_into,
|
||||||
|
payload_type_name,
|
||||||
|
get_payload,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
// Don't use indoc because this must be indented once!
|
||||||
|
r#"
|
||||||
|
/// Unsafely assume the given {} has a .tag() of {} and return its payload.
|
||||||
|
/// (always examine .tag() first to make sure this is the correct variant!)
|
||||||
|
pub unsafe fn as_{}(&self) -> {}{} {{
|
||||||
|
{}self.variant.{}
|
||||||
|
}}"#,
|
||||||
|
name,
|
||||||
|
tag_name,
|
||||||
|
tag_name,
|
||||||
|
deref_for_as,
|
||||||
|
payload_type_name,
|
||||||
|
deref_for_as,
|
||||||
|
tag_name
|
||||||
|
)?;
|
||||||
|
} else {
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
// Don't use indoc because this must be indented once!
|
||||||
|
r#"
|
||||||
|
/// Construct a tag named {}
|
||||||
|
pub fn {}() -> Self {{
|
||||||
|
Self {{
|
||||||
|
tag: {}::{},
|
||||||
|
variant: unsafe {{
|
||||||
|
core::mem::transmute::<
|
||||||
|
core::mem::MaybeUninit<{}>,
|
||||||
|
{},
|
||||||
|
>(core::mem::MaybeUninit::uninit())
|
||||||
|
}},
|
||||||
|
}}
|
||||||
|
}}"#,
|
||||||
|
tag_name, tag_name, discriminant_name, tag_name, variant_name, variant_name,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
// Don't use indoc because this must be indented once!
|
||||||
|
r#"
|
||||||
|
/// Other `into_` methods return a payload, but since the {} tag
|
||||||
|
/// has no payload, this does nothing and is only here for completeness.
|
||||||
|
pub fn into_{}(self) -> () {{
|
||||||
|
()
|
||||||
|
}}"#,
|
||||||
|
tag_name, tag_name
|
||||||
|
)?;
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
// Don't use indoc because this must be indented once!
|
||||||
|
r#"
|
||||||
|
/// Other `as` methods return a payload, but since the {} tag
|
||||||
|
/// has no payload, this does nothing and is only here for completeness.
|
||||||
|
pub unsafe fn as_{}(&self) -> () {{
|
||||||
|
()
|
||||||
|
}}"#,
|
||||||
|
tag_name, tag_name
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.write_str("}\n")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Drop impl for the tag union
|
||||||
|
{
|
||||||
|
write!(
|
||||||
|
buf,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
|
||||||
|
impl Drop for {} {{
|
||||||
|
fn drop(&mut self) {{
|
||||||
|
match self.tag {{
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
name
|
||||||
|
)?;
|
||||||
|
|
||||||
|
write_impl_tags(
|
||||||
|
3,
|
||||||
|
tags.iter(),
|
||||||
|
&discriminant_name,
|
||||||
|
buf,
|
||||||
|
|tag_name, opt_payload_id| {
|
||||||
|
match opt_payload_id {
|
||||||
|
Some(payload_id) if types.get(payload_id).has_pointer(types) => {
|
||||||
|
format!(
|
||||||
|
"unsafe {{ core::mem::ManuallyDrop::drop(&mut self.variant.{}) }},",
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The PartialEq impl for the tag union
|
||||||
|
{
|
||||||
|
write!(
|
||||||
|
buf,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
impl PartialEq for {} {{
|
||||||
|
fn eq(&self, other: &Self) -> bool {{
|
||||||
|
if self.tag != other.tag {{
|
||||||
|
return false;
|
||||||
|
}}
|
||||||
|
|
||||||
|
unsafe {{
|
||||||
|
match self.tag {{
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
name
|
||||||
|
)?;
|
||||||
|
|
||||||
|
write_impl_tags(
|
||||||
|
4,
|
||||||
|
tags.iter(),
|
||||||
|
&discriminant_name,
|
||||||
|
buf,
|
||||||
|
|tag_name, opt_payload_id| {
|
||||||
|
if opt_payload_id.is_some() {
|
||||||
|
format!("self.variant.{} == other.variant.{},", tag_name, 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()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !typ.has_float(types) {
|
||||||
|
writeln!(buf, "impl Eq for {} {{}}\n", name)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The PartialOrd impl for the tag union
|
||||||
|
{
|
||||||
|
write!(
|
||||||
|
buf,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
impl PartialOrd for {} {{
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {{
|
||||||
|
match self.tag.partial_cmp(&other.tag) {{
|
||||||
|
Some(core::cmp::Ordering::Equal) => {{}}
|
||||||
|
not_eq => return not_eq,
|
||||||
|
}}
|
||||||
|
|
||||||
|
unsafe {{
|
||||||
|
match self.tag {{
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
name
|
||||||
|
)?;
|
||||||
|
|
||||||
|
write_impl_tags(
|
||||||
|
4,
|
||||||
|
tags.iter(),
|
||||||
|
&discriminant_name,
|
||||||
|
buf,
|
||||||
|
|tag_name, opt_payload_id| {
|
||||||
|
if opt_payload_id.is_some() {
|
||||||
|
format!(
|
||||||
|
"self.variant.{}.partial_cmp(&other.variant.{}),",
|
||||||
|
tag_name, 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()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Ord impl for the tag union
|
||||||
|
{
|
||||||
|
write!(
|
||||||
|
buf,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
impl Ord for {} {{
|
||||||
|
fn cmp(&self, other: &Self) -> core::cmp::Ordering {{
|
||||||
|
match self.tag.cmp(&other.tag) {{
|
||||||
|
core::cmp::Ordering::Equal => {{}}
|
||||||
|
not_eq => return not_eq,
|
||||||
|
}}
|
||||||
|
|
||||||
|
unsafe {{
|
||||||
|
match self.tag {{
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
name
|
||||||
|
)?;
|
||||||
|
|
||||||
|
write_impl_tags(
|
||||||
|
4,
|
||||||
|
tags.iter(),
|
||||||
|
&discriminant_name,
|
||||||
|
buf,
|
||||||
|
|tag_name, opt_payload_id| {
|
||||||
|
if opt_payload_id.is_some() {
|
||||||
|
format!(
|
||||||
|
"self.variant.{}.cmp(&other.variant.{}),",
|
||||||
|
tag_name, 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()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Clone impl for the tag union
|
||||||
|
{
|
||||||
|
write!(
|
||||||
|
buf,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
impl Clone for {} {{
|
||||||
|
fn clone(&self) -> Self {{
|
||||||
|
match self.tag {{
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
name
|
||||||
|
)?;
|
||||||
|
|
||||||
|
write_impl_tags(
|
||||||
|
3,
|
||||||
|
tags.iter(),
|
||||||
|
&discriminant_name,
|
||||||
|
buf,
|
||||||
|
|tag_name, opt_payload_id| {
|
||||||
|
if opt_payload_id.is_some() {
|
||||||
|
format!(
|
||||||
|
r#"Self {{
|
||||||
|
variant: {} {{
|
||||||
|
{}: unsafe {{ self.variant.{}.clone() }},
|
||||||
|
}},
|
||||||
|
tag: {}::{},
|
||||||
|
}},"#,
|
||||||
|
variant_name, tag_name, tag_name, discriminant_name, tag_name
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// when there's no payload, we set the clone's `variant` field to
|
||||||
|
// garbage memory
|
||||||
|
format!(
|
||||||
|
r#"Self {{
|
||||||
|
variant: unsafe {{
|
||||||
|
core::mem::transmute::<
|
||||||
|
core::mem::MaybeUninit<{}>,
|
||||||
|
{},
|
||||||
|
>(core::mem::MaybeUninit::uninit())
|
||||||
|
}},
|
||||||
|
tag: {}::{},
|
||||||
|
}},"#,
|
||||||
|
variant_name, variant_name, discriminant_name, tag_name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !typ.has_pointer(types) {
|
||||||
|
writeln!(buf, "impl Copy for {} {{}}\n", name)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Debug impl for the tag union
|
||||||
|
{
|
||||||
|
write!(
|
||||||
|
buf,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
impl core::fmt::Debug for {} {{
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {{
|
||||||
|
f.write_str("{}::")?;
|
||||||
|
|
||||||
|
unsafe {{
|
||||||
|
match self.tag {{
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
name, name
|
||||||
|
)?;
|
||||||
|
|
||||||
|
write_impl_tags(
|
||||||
|
4,
|
||||||
|
tags.iter(),
|
||||||
|
&discriminant_name,
|
||||||
|
buf,
|
||||||
|
|tag_name, opt_payload_id| {
|
||||||
|
if opt_payload_id.is_some() {
|
||||||
|
format!(
|
||||||
|
r#"f.debug_tuple("{}").field(&self.variant.{}).finish(),"#,
|
||||||
|
tag_name, tag_name
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!(r#"f.write_str("{}"),"#, tag_name)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_impl_tags<
|
||||||
|
'a,
|
||||||
|
I: IntoIterator<Item = &'a (String, Option<TypeId>)>,
|
||||||
|
F: Fn(&str, Option<TypeId>) -> String,
|
||||||
|
>(
|
||||||
|
indentations: usize,
|
||||||
|
tags: I,
|
||||||
|
discriminant_name: &str,
|
||||||
|
buf: &mut String,
|
||||||
|
to_branch_str: F,
|
||||||
|
) -> fmt::Result {
|
||||||
|
for (tag_name, opt_payload_id) in tags {
|
||||||
|
let branch_str = to_branch_str(tag_name, *opt_payload_id);
|
||||||
|
|
||||||
|
for _ in 0..indentations {
|
||||||
|
buf.write_str(INDENT)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(buf, "{}::{} => {}", discriminant_name, tag_name, branch_str)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_enumeration<I: ExactSizeIterator<Item = S>, S: AsRef<str>>(
|
||||||
|
name: &str,
|
||||||
|
typ: &RocType,
|
||||||
|
tags: I,
|
||||||
|
types: &Types,
|
||||||
|
buf: &mut String,
|
||||||
|
) -> fmt::Result {
|
||||||
|
let tag_bytes: usize = UnionLayout::discriminant_size(tags.len())
|
||||||
|
.stack_size()
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
write_deriving(typ, types, buf)?;
|
||||||
|
|
||||||
// e.g. "#[repr(u8)]\npub enum Foo {\n"
|
// e.g. "#[repr(u8)]\npub enum Foo {\n"
|
||||||
writeln!(buf, "#[repr(u{})]\npub enum {} {{", tag_bytes * 8, name)?;
|
writeln!(buf, "#[repr(u{})]\npub enum {} {{", tag_bytes * 8, name)?;
|
||||||
|
|
||||||
for name in tags {
|
for (index, name) in tags.enumerate() {
|
||||||
writeln!(buf, "{}{},", INDENT, name.as_ref())?;
|
writeln!(buf, "{}{} = {},", INDENT, name.as_ref(), index)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.write_str("}\n")
|
buf.write_str("}\n")
|
||||||
@ -105,76 +702,65 @@ fn write_struct(
|
|||||||
types: &Types,
|
types: &Types,
|
||||||
buf: &mut String,
|
buf: &mut String,
|
||||||
) -> fmt::Result {
|
) -> fmt::Result {
|
||||||
write_deriving(struct_id, types, buf)?;
|
write_deriving(types.get(struct_id), types, buf)?;
|
||||||
|
|
||||||
writeln!(buf, "#[repr(C)]\npub struct {} {{", name)?;
|
writeln!(buf, "#[repr(C)]\npub struct {} {{", name)?;
|
||||||
|
|
||||||
for (label, field_id) in fields {
|
for (label, field_id) in fields {
|
||||||
write!(buf, "{}{}: ", INDENT, label.as_str())?;
|
writeln!(
|
||||||
write_type_name(*field_id, types, buf)?;
|
buf,
|
||||||
buf.write_str(",\n")?;
|
"{}{}: {},",
|
||||||
|
INDENT,
|
||||||
|
label.as_str(),
|
||||||
|
type_name(*field_id, types)
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.write_str("}\n")
|
buf.write_str("}\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_type_name(id: TypeId, types: &Types, buf: &mut String) -> fmt::Result {
|
fn type_name(id: TypeId, types: &Types) -> String {
|
||||||
match types.get(id) {
|
match types.get(id) {
|
||||||
RocType::U8 => buf.write_str("u8"),
|
RocType::U8 => "u8".to_string(),
|
||||||
RocType::U16 => buf.write_str("u16"),
|
RocType::U16 => "u16".to_string(),
|
||||||
RocType::U32 => buf.write_str("u32"),
|
RocType::U32 => "u32".to_string(),
|
||||||
RocType::U64 => buf.write_str("u64"),
|
RocType::U64 => "u64".to_string(),
|
||||||
RocType::U128 => buf.write_str("u128"),
|
RocType::U128 => "u128".to_string(),
|
||||||
RocType::I8 => buf.write_str("i8"),
|
RocType::I8 => "i8".to_string(),
|
||||||
RocType::I16 => buf.write_str("i16"),
|
RocType::I16 => "i16".to_string(),
|
||||||
RocType::I32 => buf.write_str("i32"),
|
RocType::I32 => "i32".to_string(),
|
||||||
RocType::I64 => buf.write_str("i64"),
|
RocType::I64 => "i64".to_string(),
|
||||||
RocType::I128 => buf.write_str("i128"),
|
RocType::I128 => "i128".to_string(),
|
||||||
RocType::F32 => buf.write_str("f32"),
|
RocType::F32 => "f32".to_string(),
|
||||||
RocType::F64 => buf.write_str("f64"),
|
RocType::F64 => "f64".to_string(),
|
||||||
RocType::F128 => buf.write_str("f128"),
|
RocType::F128 => "f128".to_string(),
|
||||||
RocType::Bool => buf.write_str("bool"),
|
RocType::Bool => "bool".to_string(),
|
||||||
RocType::RocDec => buf.write_str("roc_std::RocDec"),
|
RocType::RocDec => "roc_std::RocDec".to_string(),
|
||||||
RocType::RocStr => buf.write_str("roc_std::RocStr"),
|
RocType::RocStr => "roc_std::RocStr".to_string(),
|
||||||
RocType::RocDict(key_id, val_id) => {
|
RocType::RocDict(key_id, val_id) => format!(
|
||||||
buf.write_str("roc_std::RocDict<")?;
|
"roc_std::RocDict<{}, {}>",
|
||||||
write_type_name(*key_id, types, buf)?;
|
type_name(*key_id, types),
|
||||||
buf.write_str(", ")?;
|
type_name(*val_id, types)
|
||||||
write_type_name(*val_id, types, buf)?;
|
),
|
||||||
buf.write_char('>')
|
RocType::RocSet(elem_id) => format!("roc_std::RocSet<{}>", type_name(*elem_id, types)),
|
||||||
}
|
RocType::RocList(elem_id) => format!("roc_std::RocList<{}>", type_name(*elem_id, types)),
|
||||||
RocType::RocSet(elem_id) => {
|
RocType::RocBox(elem_id) => format!("roc_std::RocBox<{}>", type_name(*elem_id, types)),
|
||||||
buf.write_str("roc_std::RocSet<")?;
|
|
||||||
write_type_name(*elem_id, types, buf)?;
|
|
||||||
buf.write_char('>')
|
|
||||||
}
|
|
||||||
RocType::RocList(elem_id) => {
|
|
||||||
buf.write_str("roc_std::RocList<")?;
|
|
||||||
write_type_name(*elem_id, types, buf)?;
|
|
||||||
buf.write_char('>')
|
|
||||||
}
|
|
||||||
RocType::RocBox(elem_id) => {
|
|
||||||
buf.write_str("roc_std::RocBox<")?;
|
|
||||||
write_type_name(*elem_id, types, buf)?;
|
|
||||||
buf.write_char('>')
|
|
||||||
}
|
|
||||||
RocType::Struct { name, .. }
|
RocType::Struct { name, .. }
|
||||||
| RocType::TagUnion { name, .. }
|
| RocType::TagUnion { name, .. }
|
||||||
| RocType::TransparentWrapper { name, .. }
|
| RocType::TransparentWrapper { name, .. }
|
||||||
| RocType::RecursiveTagUnion { name, .. } => buf.write_str(name),
|
| RocType::Enumeration { name, .. }
|
||||||
|
| RocType::RecursiveTagUnion { name, .. } => name.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_deriving(id: TypeId, types: &Types, buf: &mut String) -> fmt::Result {
|
fn write_deriving(typ: &RocType, types: &Types, buf: &mut String) -> fmt::Result {
|
||||||
let typ = types.get(id);
|
|
||||||
|
|
||||||
buf.write_str("\n#[derive(Clone, PartialEq, PartialOrd, ")?;
|
buf.write_str("\n#[derive(Clone, PartialEq, PartialOrd, ")?;
|
||||||
|
|
||||||
if !typ.has_pointer(types) {
|
if !typ.has_pointer(types) {
|
||||||
buf.write_str("Copy, ")?;
|
buf.write_str("Copy, ")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !typ.has_tag_union(types) {
|
if !typ.has_enumeration(types) {
|
||||||
buf.write_str("Default, ")?;
|
buf.write_str("Default, ")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
use core::mem::align_of;
|
use core::mem::align_of;
|
||||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
use roc_collections::VecMap;
|
use roc_collections::VecMap;
|
||||||
|
use roc_mono::layout::UnionLayout;
|
||||||
use roc_std::RocDec;
|
use roc_std::RocDec;
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
|
use std::convert::TryInto;
|
||||||
use ven_graph::topological_sort;
|
use ven_graph::topological_sort;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
@ -118,12 +120,15 @@ pub enum RocType {
|
|||||||
RocBox(TypeId),
|
RocBox(TypeId),
|
||||||
RecursiveTagUnion {
|
RecursiveTagUnion {
|
||||||
name: String,
|
name: String,
|
||||||
tags: Vec<(String, Vec<TypeId>)>,
|
tags: Vec<(String, Option<TypeId>)>,
|
||||||
|
},
|
||||||
|
Enumeration {
|
||||||
|
name: String,
|
||||||
|
tags: Vec<String>,
|
||||||
},
|
},
|
||||||
TagUnion {
|
TagUnion {
|
||||||
tag_bytes: u8,
|
|
||||||
name: String,
|
name: String,
|
||||||
tags: Vec<(String, Vec<TypeId>)>,
|
tags: Vec<(String, Option<TypeId>)>,
|
||||||
},
|
},
|
||||||
Struct {
|
Struct {
|
||||||
name: String,
|
name: String,
|
||||||
@ -154,6 +159,7 @@ impl RocType {
|
|||||||
| RocType::F32
|
| RocType::F32
|
||||||
| RocType::F64
|
| RocType::F64
|
||||||
| RocType::F128
|
| RocType::F128
|
||||||
|
| RocType::Enumeration { .. }
|
||||||
| RocType::RocDec => false,
|
| RocType::RocDec => false,
|
||||||
RocType::RocStr
|
RocType::RocStr
|
||||||
| RocType::RocList(_)
|
| RocType::RocList(_)
|
||||||
@ -187,7 +193,8 @@ impl RocType {
|
|||||||
| RocType::U64
|
| RocType::U64
|
||||||
| RocType::I128
|
| RocType::I128
|
||||||
| RocType::U128
|
| RocType::U128
|
||||||
| RocType::RocDec => false,
|
| RocType::RocDec
|
||||||
|
| RocType::Enumeration { .. } => false,
|
||||||
RocType::RocList(id) | RocType::RocSet(id) | RocType::RocBox(id) => {
|
RocType::RocList(id) | RocType::RocSet(id) | RocType::RocBox(id) => {
|
||||||
types.get(*id).has_float(types)
|
types.get(*id).has_float(types)
|
||||||
}
|
}
|
||||||
@ -205,9 +212,11 @@ impl RocType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Useful when determining whether to derive Default in a Rust type.
|
/// Useful when determining whether to derive Default in a Rust type.
|
||||||
pub fn has_tag_union(&self, types: &Types) -> bool {
|
pub fn has_enumeration(&self, types: &Types) -> bool {
|
||||||
match self {
|
match self {
|
||||||
RocType::RecursiveTagUnion { .. } | RocType::TagUnion { .. } => true,
|
RocType::RecursiveTagUnion { .. }
|
||||||
|
| RocType::TagUnion { .. }
|
||||||
|
| RocType::Enumeration { .. } => true,
|
||||||
RocType::RocStr
|
RocType::RocStr
|
||||||
| RocType::Bool
|
| RocType::Bool
|
||||||
| RocType::I8
|
| RocType::I8
|
||||||
@ -225,15 +234,18 @@ impl RocType {
|
|||||||
| RocType::F128
|
| RocType::F128
|
||||||
| RocType::RocDec => false,
|
| RocType::RocDec => false,
|
||||||
RocType::RocList(id) | RocType::RocSet(id) | RocType::RocBox(id) => {
|
RocType::RocList(id) | RocType::RocSet(id) | RocType::RocBox(id) => {
|
||||||
types.get(*id).has_tag_union(types)
|
types.get(*id).has_enumeration(types)
|
||||||
}
|
}
|
||||||
RocType::RocDict(key_id, val_id) => {
|
RocType::RocDict(key_id, val_id) => {
|
||||||
types.get(*key_id).has_tag_union(types) || types.get(*val_id).has_tag_union(types)
|
types.get(*key_id).has_enumeration(types)
|
||||||
|
|| types.get(*val_id).has_enumeration(types)
|
||||||
}
|
}
|
||||||
RocType::Struct { fields, .. } => fields
|
RocType::Struct { fields, .. } => fields
|
||||||
.iter()
|
.iter()
|
||||||
.any(|(_, id)| types.get(*id).has_tag_union(types)),
|
.any(|(_, id)| types.get(*id).has_enumeration(types)),
|
||||||
RocType::TransparentWrapper { content, .. } => types.get(*content).has_tag_union(types),
|
RocType::TransparentWrapper { content, .. } => {
|
||||||
|
types.get(*content).has_enumeration(types)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,6 +306,10 @@ impl RocType {
|
|||||||
RocType::TransparentWrapper { content, .. } => {
|
RocType::TransparentWrapper { content, .. } => {
|
||||||
types.get(*content).alignment(types, target_info)
|
types.get(*content).alignment(types, target_info)
|
||||||
}
|
}
|
||||||
|
RocType::Enumeration { tags, .. } => UnionLayout::discriminant_size(tags.len())
|
||||||
|
.stack_size()
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,3 +4,4 @@
|
|||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(clippy::undocumented_unsafe_blocks)]
|
||||||
|
@ -170,14 +170,13 @@ fn nested_record_anonymous() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
fn tag_union_aliased() {
|
fn tag_union_aliased() {
|
||||||
let module = indoc!(
|
let module = indoc!(
|
||||||
r#"
|
r#"
|
||||||
MyTagUnion : [ Foo U64, Bar U128 ]
|
MyTagUnion : [ Foo Str, Bar U128, Blah I32, Baz ]
|
||||||
|
|
||||||
main : MyTagUnion
|
main : MyTagUnion
|
||||||
main = Foo 123
|
main = Foo "blah"
|
||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -187,30 +186,26 @@ fn tag_union_aliased() {
|
|||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
#[repr(C)]
|
#[derive(Clone, PartialEq, PartialOrd, Copy, Eq, Ord, Hash, Debug)]
|
||||||
pub struct MyTagUnion {
|
|
||||||
tag: tag_MyTagUnion,
|
|
||||||
variant: variant_MyTagUnion,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
union variant_MyTagUnion {
|
|
||||||
Bar: u128,
|
|
||||||
Foo: std::mem::ManuallyDrop<Payload2<roc_std::RocStr, i32>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Payload2<V0, V1> {
|
|
||||||
_0: V0,
|
|
||||||
_1: V1,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum tag_MyTagUnion {
|
pub enum tag_MyTagUnion {
|
||||||
Bar,
|
Bar = 0,
|
||||||
Foo,
|
Baz = 1,
|
||||||
|
Blah = 2,
|
||||||
|
Foo = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub union union_MyTagUnion {
|
||||||
|
Bar: u128,
|
||||||
|
Blah: i32,
|
||||||
|
Foo: core::mem::ManuallyDrop<roc_std::RocStr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct MyTagUnion {
|
||||||
|
variant: union_MyTagUnion,
|
||||||
|
tag: tag_MyTagUnion,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MyTagUnion {
|
impl MyTagUnion {
|
||||||
@ -218,50 +213,105 @@ fn tag_union_aliased() {
|
|||||||
self.tag
|
self.tag
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assume this is the tag named Foo, and return a reference to its payload.
|
/// Construct a tag named Bar, with the appropriate payload
|
||||||
pub unsafe fn as_Foo(&self) -> &Payload2<roc_std::RocStr, i32> {
|
pub fn Bar(payload: u128) -> Self {
|
||||||
&*self.variant.Foo
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Assume this is the tag named Foo, and return a mutable reference to its payload.
|
|
||||||
pub unsafe fn as_mut_Foo(&mut self) -> &mut Payload2<roc_std::RocStr, i32> {
|
|
||||||
&mut *self.variant.Foo
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Assume this is the tag named Bar, and return a reference to its payload.
|
|
||||||
pub unsafe fn as_Bar(&self) -> u128 {
|
|
||||||
self.variant.Bar
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Assume this is the tag named Bar, and return a mutable reference to its payload.
|
|
||||||
pub unsafe fn as_mut_Bar(&mut self) -> &mut u128 {
|
|
||||||
&mut self.variant.Bar
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a tag named Foo, with the appropriate payload
|
|
||||||
pub fn Foo(_0: roc_std::RocStr, _1: i32) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
tag: tag_MyTagUnion::Foo,
|
tag: tag_MyTagUnion::Bar,
|
||||||
variant: variant_MyTagUnion {
|
variant: union_MyTagUnion {
|
||||||
Foo: std::mem::ManuallyDrop::new(Payload2 { _0, _1 }),
|
Bar: payload
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a tag named Bar, with the appropriate payload
|
/// Unsafely assume the given MyTagUnion has a .tag() of Bar and convert it to Bar's payload.
|
||||||
pub fn Bar(arg0: u128) -> Self {
|
/// (always examine .tag() first to make sure this is the correct variant!)
|
||||||
|
pub unsafe fn into_Bar(self) -> u128 {
|
||||||
|
self.variant.Bar
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unsafely assume the given MyTagUnion has a .tag() of Bar and return its payload.
|
||||||
|
/// (always examine .tag() first to make sure this is the correct variant!)
|
||||||
|
pub unsafe fn as_Bar(&self) -> u128 {
|
||||||
|
self.variant.Bar
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a tag named Baz
|
||||||
|
pub fn Baz() -> Self {
|
||||||
Self {
|
Self {
|
||||||
tag: tag_MyTagUnion::Bar,
|
tag: tag_MyTagUnion::Baz,
|
||||||
variant: variant_MyTagUnion { Bar: arg0 },
|
variant: unsafe {
|
||||||
|
core::mem::transmute::<
|
||||||
|
core::mem::MaybeUninit<union_MyTagUnion>,
|
||||||
|
union_MyTagUnion,
|
||||||
|
>(core::mem::MaybeUninit::uninit())
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Other `into_` methods return a payload, but since the Baz tag
|
||||||
|
/// has no payload, this does nothing and is only here for completeness.
|
||||||
|
pub fn into_Baz(self) -> () {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Other `as` methods return a payload, but since the Baz tag
|
||||||
|
/// has no payload, this does nothing and is only here for completeness.
|
||||||
|
pub unsafe fn as_Baz(&self) -> () {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a tag named Blah, with the appropriate payload
|
||||||
|
pub fn Blah(payload: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
tag: tag_MyTagUnion::Blah,
|
||||||
|
variant: union_MyTagUnion {
|
||||||
|
Blah: payload
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unsafely assume the given MyTagUnion has a .tag() of Blah and convert it to Blah's payload.
|
||||||
|
/// (always examine .tag() first to make sure this is the correct variant!)
|
||||||
|
pub unsafe fn into_Blah(self) -> i32 {
|
||||||
|
self.variant.Blah
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unsafely assume the given MyTagUnion has a .tag() of Blah and return its payload.
|
||||||
|
/// (always examine .tag() first to make sure this is the correct variant!)
|
||||||
|
pub unsafe fn as_Blah(&self) -> i32 {
|
||||||
|
self.variant.Blah
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a tag named Foo, with the appropriate payload
|
||||||
|
pub fn Foo(payload: roc_std::RocStr) -> Self {
|
||||||
|
Self {
|
||||||
|
tag: tag_MyTagUnion::Foo,
|
||||||
|
variant: union_MyTagUnion {
|
||||||
|
Foo: core::mem::ManuallyDrop::new(payload)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unsafely assume the given MyTagUnion has a .tag() of Foo and convert it to Foo's payload.
|
||||||
|
/// (always examine .tag() first to make sure this is the correct variant!)
|
||||||
|
pub unsafe fn into_Foo(mut self) -> roc_std::RocStr {
|
||||||
|
core::mem::ManuallyDrop::take(&mut self.variant.Foo)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unsafely assume the given MyTagUnion has a .tag() of Foo and return its payload.
|
||||||
|
/// (always examine .tag() first to make sure this is the correct variant!)
|
||||||
|
pub unsafe fn as_Foo(&self) -> &roc_std::RocStr {
|
||||||
|
&self.variant.Foo
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for MyTagUnion {
|
impl Drop for MyTagUnion {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
match self.tag {
|
match self.tag {
|
||||||
tag_MyTagUnion::Bar => {}
|
tag_MyTagUnion::Bar => {}
|
||||||
tag_MyTagUnion::Foo => unsafe { std::mem::ManuallyDrop::drop(&mut self.variant.Foo) },
|
tag_MyTagUnion::Baz => {}
|
||||||
|
tag_MyTagUnion::Blah => {}
|
||||||
|
tag_MyTagUnion::Foo => unsafe { core::mem::ManuallyDrop::drop(&mut self.variant.Foo) },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -275,6 +325,8 @@ fn tag_union_aliased() {
|
|||||||
unsafe {
|
unsafe {
|
||||||
match self.tag {
|
match self.tag {
|
||||||
tag_MyTagUnion::Bar => self.variant.Bar == other.variant.Bar,
|
tag_MyTagUnion::Bar => self.variant.Bar == other.variant.Bar,
|
||||||
|
tag_MyTagUnion::Baz => true,
|
||||||
|
tag_MyTagUnion::Blah => self.variant.Blah == other.variant.Blah,
|
||||||
tag_MyTagUnion::Foo => self.variant.Foo == other.variant.Foo,
|
tag_MyTagUnion::Foo => self.variant.Foo == other.variant.Foo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,6 +345,8 @@ fn tag_union_aliased() {
|
|||||||
unsafe {
|
unsafe {
|
||||||
match self.tag {
|
match self.tag {
|
||||||
tag_MyTagUnion::Bar => self.variant.Bar.partial_cmp(&other.variant.Bar),
|
tag_MyTagUnion::Bar => self.variant.Bar.partial_cmp(&other.variant.Bar),
|
||||||
|
tag_MyTagUnion::Baz => Some(core::cmp::Ordering::Equal),
|
||||||
|
tag_MyTagUnion::Blah => self.variant.Blah.partial_cmp(&other.variant.Blah),
|
||||||
tag_MyTagUnion::Foo => self.variant.Foo.partial_cmp(&other.variant.Foo),
|
tag_MyTagUnion::Foo => self.variant.Foo.partial_cmp(&other.variant.Foo),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -300,7 +354,7 @@ fn tag_union_aliased() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Ord for MyTagUnion {
|
impl Ord for MyTagUnion {
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
|
||||||
match self.tag.cmp(&other.tag) {
|
match self.tag.cmp(&other.tag) {
|
||||||
core::cmp::Ordering::Equal => {}
|
core::cmp::Ordering::Equal => {}
|
||||||
not_eq => return not_eq,
|
not_eq => return not_eq,
|
||||||
@ -309,11 +363,63 @@ fn tag_union_aliased() {
|
|||||||
unsafe {
|
unsafe {
|
||||||
match self.tag {
|
match self.tag {
|
||||||
tag_MyTagUnion::Bar => self.variant.Bar.cmp(&other.variant.Bar),
|
tag_MyTagUnion::Bar => self.variant.Bar.cmp(&other.variant.Bar),
|
||||||
|
tag_MyTagUnion::Baz => core::cmp::Ordering::Equal,
|
||||||
|
tag_MyTagUnion::Blah => self.variant.Blah.cmp(&other.variant.Blah),
|
||||||
tag_MyTagUnion::Foo => self.variant.Foo.cmp(&other.variant.Foo),
|
tag_MyTagUnion::Foo => self.variant.Foo.cmp(&other.variant.Foo),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for MyTagUnion {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match self.tag {
|
||||||
|
tag_MyTagUnion::Bar => Self {
|
||||||
|
variant: union_MyTagUnion {
|
||||||
|
Bar: unsafe { self.variant.Bar.clone() },
|
||||||
|
},
|
||||||
|
tag: tag_MyTagUnion::Bar,
|
||||||
|
},
|
||||||
|
tag_MyTagUnion::Baz => Self {
|
||||||
|
variant: unsafe {
|
||||||
|
core::mem::transmute::<
|
||||||
|
core::mem::MaybeUninit<union_MyTagUnion>,
|
||||||
|
union_MyTagUnion,
|
||||||
|
>(core::mem::MaybeUninit::uninit())
|
||||||
|
},
|
||||||
|
tag: tag_MyTagUnion::Baz,
|
||||||
|
},
|
||||||
|
tag_MyTagUnion::Blah => Self {
|
||||||
|
variant: union_MyTagUnion {
|
||||||
|
Blah: unsafe { self.variant.Blah.clone() },
|
||||||
|
},
|
||||||
|
tag: tag_MyTagUnion::Blah,
|
||||||
|
},
|
||||||
|
tag_MyTagUnion::Foo => Self {
|
||||||
|
variant: union_MyTagUnion {
|
||||||
|
Foo: unsafe { self.variant.Foo.clone() },
|
||||||
|
},
|
||||||
|
tag: tag_MyTagUnion::Foo,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Debug for MyTagUnion {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.write_str("MyTagUnion::")?;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
match self.tag {
|
||||||
|
tag_MyTagUnion::Bar => f.debug_tuple("Bar").field(&self.variant.Bar).finish(),
|
||||||
|
tag_MyTagUnion::Baz => f.write_str("Baz"),
|
||||||
|
tag_MyTagUnion::Blah => f.debug_tuple("Blah").field(&self.variant.Blah).finish(),
|
||||||
|
tag_MyTagUnion::Foo => f.debug_tuple("Foo").field(&self.variant.Foo).finish(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -339,9 +445,9 @@ fn tag_union_enumeration() {
|
|||||||
#[derive(Clone, PartialEq, PartialOrd, Copy, Eq, Ord, Hash, Debug)]
|
#[derive(Clone, PartialEq, PartialOrd, Copy, Eq, Ord, Hash, Debug)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum MyTagUnion {
|
pub enum MyTagUnion {
|
||||||
Bar,
|
Bar = 0,
|
||||||
Blah,
|
Blah = 1,
|
||||||
Foo,
|
Foo = 2,
|
||||||
}
|
}
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
|
@ -59,7 +59,7 @@ roc_editor = { path = "../editor", optional = true }
|
|||||||
roc_linker = { path = "../linker" }
|
roc_linker = { path = "../linker" }
|
||||||
roc_repl_cli = { path = "../repl_cli", optional = true }
|
roc_repl_cli = { path = "../repl_cli", optional = true }
|
||||||
clap = { version = "3.1.15", default-features = false, features = ["std", "color", "suggestions"] }
|
clap = { version = "3.1.15", default-features = false, features = ["std", "color", "suggestions"] }
|
||||||
const_format = "0.2.22"
|
const_format = { version = "0.2.23", features = ["const_generics"] }
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
mimalloc = { version = "0.1.26", default-features = false }
|
mimalloc = { version = "0.1.26", default-features = false }
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ serde = { version = "1.0.130", features = ["derive"] }
|
|||||||
serde-xml-rs = "0.5.1"
|
serde-xml-rs = "0.5.1"
|
||||||
strip-ansi-escapes = "0.1.1"
|
strip-ansi-escapes = "0.1.1"
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
const_format = "0.2.22"
|
const_format = { version = "0.2.23", features = ["const_generics"] }
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
rlimit = "0.6.2"
|
rlimit = "0.6.2"
|
||||||
|
@ -1729,30 +1729,27 @@ fn constrain_def(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a let-constraint for a non-recursive def.
|
||||||
|
/// Recursive defs should always use `constrain_recursive_defs`.
|
||||||
pub fn constrain_def_make_constraint(
|
pub fn constrain_def_make_constraint(
|
||||||
constraints: &mut Constraints,
|
constraints: &mut Constraints,
|
||||||
new_rigid_variables: impl Iterator<Item = Variable>,
|
annotation_rigid_variables: impl Iterator<Item = Variable>,
|
||||||
new_infer_variables: impl Iterator<Item = Variable>,
|
annotation_infer_variables: impl Iterator<Item = Variable>,
|
||||||
expr_con: Constraint,
|
def_expr_con: Constraint,
|
||||||
body_con: Constraint,
|
after_def_con: Constraint,
|
||||||
def_pattern_state: PatternState,
|
def_pattern_state: PatternState,
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
let and_constraint = constraints.and_constraint(def_pattern_state.constraints);
|
let all_flex_variables = (def_pattern_state.vars.into_iter()).chain(annotation_infer_variables);
|
||||||
|
|
||||||
let def_con = constraints.let_constraint(
|
let pattern_constraints = constraints.and_constraint(def_pattern_state.constraints);
|
||||||
[],
|
let def_pattern_and_body_con = constraints.and_constraint([pattern_constraints, def_expr_con]);
|
||||||
new_infer_variables,
|
|
||||||
[], // empty, because our functions have no arguments!
|
|
||||||
and_constraint,
|
|
||||||
expr_con,
|
|
||||||
);
|
|
||||||
|
|
||||||
constraints.let_constraint(
|
constraints.let_constraint(
|
||||||
new_rigid_variables,
|
annotation_rigid_variables,
|
||||||
def_pattern_state.vars,
|
all_flex_variables,
|
||||||
def_pattern_state.headers,
|
def_pattern_state.headers,
|
||||||
def_con,
|
def_pattern_and_body_con,
|
||||||
body_con,
|
after_def_con,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2126,15 +2123,35 @@ pub fn rec_defs_help(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let flex_constraints = constraints.and_constraint(flex_info.constraints);
|
// Strategy for recursive defs:
|
||||||
let inner_inner = constraints.let_constraint(
|
// 1. Let-generalize the type annotations we know; these are the source of truth we'll solve
|
||||||
|
// everything else with. If there are circular type errors here, they will be caught during
|
||||||
|
// the let-generalization.
|
||||||
|
// 2. Introduce all symbols of the untyped defs, but don't generalize them yet. Now, solve
|
||||||
|
// the untyped defs' bodies. This way, when checking something like
|
||||||
|
// f = \x -> f [ x ]
|
||||||
|
// we introduce `f: b -> c`, then constrain the call `f [ x ]`,
|
||||||
|
// forcing `b -> c ~ List b -> c` and correctly picking up a recursion error.
|
||||||
|
// Had we generalized `b -> c`, the call `f [ x ]` would have been generalized, and this
|
||||||
|
// error would not be found.
|
||||||
|
// 3. Now properly let-generalize the untyped body defs, since we now know their types and
|
||||||
|
// that they don't have circular type errors.
|
||||||
|
// 4. Solve the bodies of the typed body defs, and check that they agree the types of the type
|
||||||
|
// annotation.
|
||||||
|
// 5. Solve the rest of the program that happens after this recursive def block.
|
||||||
|
|
||||||
|
// 2. Solve untyped defs without generalization of their symbols.
|
||||||
|
let untyped_body_constraints = constraints.and_constraint(flex_info.constraints);
|
||||||
|
let untyped_def_symbols_constr = constraints.let_constraint(
|
||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
flex_info.def_types.clone(),
|
flex_info.def_types.clone(),
|
||||||
Constraint::True,
|
Constraint::True,
|
||||||
flex_constraints,
|
untyped_body_constraints,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// an extra constraint that propagates information to the solver to check for invalid recursion
|
||||||
|
// and generate a good error message there.
|
||||||
let (loc_symbols, expr_regions): (Vec<_>, Vec<_>) = defs
|
let (loc_symbols, expr_regions): (Vec<_>, Vec<_>) = defs
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|def| {
|
.flat_map(|def| {
|
||||||
@ -2145,22 +2162,21 @@ pub fn rec_defs_help(
|
|||||||
|
|
||||||
let cycle_constraint = constraints.check_cycle(loc_symbols, expr_regions, cycle_mark);
|
let cycle_constraint = constraints.check_cycle(loc_symbols, expr_regions, cycle_mark);
|
||||||
|
|
||||||
let rigid_constraints = {
|
let typed_body_constraints = constraints.and_constraint(rigid_info.constraints);
|
||||||
let mut temp = rigid_info.constraints;
|
let typed_body_and_final_constr =
|
||||||
temp.push(cycle_constraint);
|
constraints.and_constraint([typed_body_constraints, cycle_constraint, body_con]);
|
||||||
temp.push(body_con);
|
|
||||||
|
|
||||||
constraints.and_constraint(temp)
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// 3. Properly generalize untyped defs after solving them.
|
||||||
let inner = constraints.let_constraint(
|
let inner = constraints.let_constraint(
|
||||||
[],
|
[],
|
||||||
flex_info.vars,
|
flex_info.vars,
|
||||||
flex_info.def_types,
|
flex_info.def_types,
|
||||||
inner_inner,
|
untyped_def_symbols_constr,
|
||||||
rigid_constraints,
|
// 4 + 5. Solve the typed body defs, and the rest of the program.
|
||||||
|
typed_body_and_final_constr,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 1. Let-generalize annotations we know.
|
||||||
constraints.let_constraint(
|
constraints.let_constraint(
|
||||||
rigid_info.vars,
|
rigid_info.vars,
|
||||||
[],
|
[],
|
||||||
|
@ -417,11 +417,11 @@ impl<'a> UnionLayout<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tag_id_builtin_help(union_size: usize) -> Builtin<'a> {
|
pub fn discriminant_size(num_tags: usize) -> IntWidth {
|
||||||
if union_size <= u8::MAX as usize {
|
if num_tags <= u8::MAX as usize {
|
||||||
Builtin::Int(IntWidth::U8)
|
IntWidth::U8
|
||||||
} else if union_size <= u16::MAX as usize {
|
} else if num_tags <= u16::MAX as usize {
|
||||||
Builtin::Int(IntWidth::U16)
|
IntWidth::U16
|
||||||
} else {
|
} else {
|
||||||
panic!("tag union is too big")
|
panic!("tag union is too big")
|
||||||
}
|
}
|
||||||
@ -431,16 +431,16 @@ impl<'a> UnionLayout<'a> {
|
|||||||
match self {
|
match self {
|
||||||
UnionLayout::NonRecursive(tags) => {
|
UnionLayout::NonRecursive(tags) => {
|
||||||
let union_size = tags.len();
|
let union_size = tags.len();
|
||||||
Self::tag_id_builtin_help(union_size)
|
Builtin::Int(Self::discriminant_size(union_size))
|
||||||
}
|
}
|
||||||
UnionLayout::Recursive(tags) => {
|
UnionLayout::Recursive(tags) => {
|
||||||
let union_size = tags.len();
|
let union_size = tags.len();
|
||||||
|
|
||||||
Self::tag_id_builtin_help(union_size)
|
Builtin::Int(Self::discriminant_size(union_size))
|
||||||
}
|
}
|
||||||
|
|
||||||
UnionLayout::NullableWrapped { other_tags, .. } => {
|
UnionLayout::NullableWrapped { other_tags, .. } => {
|
||||||
Self::tag_id_builtin_help(other_tags.len() + 1)
|
Builtin::Int(Self::discriminant_size(other_tags.len() + 1))
|
||||||
}
|
}
|
||||||
UnionLayout::NonNullableUnwrapped(_) => Builtin::Bool,
|
UnionLayout::NonNullableUnwrapped(_) => Builtin::Bool,
|
||||||
UnionLayout::NullableUnwrapped { .. } => Builtin::Bool,
|
UnionLayout::NullableUnwrapped { .. } => Builtin::Bool,
|
||||||
|
@ -3756,7 +3756,6 @@ mod solve_expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
fn sorting() {
|
fn sorting() {
|
||||||
// based on https://github.com/elm/compiler/issues/2057
|
// based on https://github.com/elm/compiler/issues/2057
|
||||||
// Roc seems to do this correctly, tracking to make sure it stays that way
|
// Roc seems to do this correctly, tracking to make sure it stays that way
|
||||||
@ -3790,7 +3789,6 @@ mod solve_expr {
|
|||||||
g = \bs ->
|
g = \bs ->
|
||||||
when bs is
|
when bs is
|
||||||
bx -> f bx
|
bx -> f bx
|
||||||
_ -> Nil
|
|
||||||
|
|
||||||
always Nil (f list)
|
always Nil (f list)
|
||||||
|
|
||||||
@ -4300,7 +4298,6 @@ mod solve_expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
fn rbtree_full_remove_min() {
|
fn rbtree_full_remove_min() {
|
||||||
infer_eq_without_problem(
|
infer_eq_without_problem(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -12,7 +12,9 @@
|
|||||||
flake-utils.lib.eachDefaultSystem (system:
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
let
|
let
|
||||||
overlays = [ (import rust-overlay) ];
|
overlays = [ (import rust-overlay) ];
|
||||||
pkgs = import nixpkgs { inherit system overlays; };
|
pkgs = import nixpkgs {
|
||||||
|
inherit system overlays;
|
||||||
|
};
|
||||||
llvmPkgs = pkgs.llvmPackages_13;
|
llvmPkgs = pkgs.llvmPackages_13;
|
||||||
|
|
||||||
# get current working directory
|
# get current working directory
|
||||||
@ -33,7 +35,7 @@
|
|||||||
alsa-lib
|
alsa-lib
|
||||||
];
|
];
|
||||||
|
|
||||||
# zig 0.8.1 from pkgs is broken on aarch64-darwin, hence the workaround
|
# zig 0.9.1 from pkgs is broken on aarch64-darwin, hence the workaround
|
||||||
zig-toolchain = zig.packages.${system}."0.9.1";
|
zig-toolchain = zig.packages.${system}."0.9.1";
|
||||||
|
|
||||||
sharedInputs = (with pkgs; [
|
sharedInputs = (with pkgs; [
|
||||||
@ -70,6 +72,7 @@
|
|||||||
LD_LIBRARY_PATH = with pkgs;
|
LD_LIBRARY_PATH = with pkgs;
|
||||||
lib.makeLibraryPath
|
lib.makeLibraryPath
|
||||||
([ pkg-config stdenv.cc.cc.lib libffi ncurses zlib ] ++ linuxInputs);
|
([ pkg-config stdenv.cc.cc.lib libffi ncurses zlib ] ++ linuxInputs);
|
||||||
|
NIXPKGS_ALLOW_UNFREE = 1; # to run the editor with NVIDIA's closed source drivers
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ target-x86_64 = ["roc_build/target-x86_64"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bumpalo = {version = "3.8.0", features = ["collections"]}
|
bumpalo = {version = "3.8.0", features = ["collections"]}
|
||||||
const_format = "0.2.22"
|
const_format = { version = "0.2.23", features = ["const_generics"] }
|
||||||
inkwell = {path = "../vendor/inkwell"}
|
inkwell = {path = "../vendor/inkwell"}
|
||||||
libloading = "0.7.1"
|
libloading = "0.7.1"
|
||||||
rustyline = {git = "https://github.com/rtfeldman/rustyline", rev = "e74333c"}
|
rustyline = {git = "https://github.com/rtfeldman/rustyline", rev = "e74333c"}
|
||||||
|
@ -1206,6 +1206,128 @@ mod test_reporting {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn polymorphic_mutual_recursion() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
f = \x -> g x
|
||||||
|
g = \x -> f [x]
|
||||||
|
|
||||||
|
f
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
|
I'm inferring a weird self-referential type for `f`:
|
||||||
|
|
||||||
|
1│ f = \x -> g x
|
||||||
|
^
|
||||||
|
|
||||||
|
Here is my best effort at writing down the type. You will see ∞ for
|
||||||
|
parts of the type that repeat something already printed out
|
||||||
|
infinitely.
|
||||||
|
|
||||||
|
List ∞ -> a
|
||||||
|
|
||||||
|
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
|
I'm inferring a weird self-referential type for `g`:
|
||||||
|
|
||||||
|
2│ g = \x -> f [x]
|
||||||
|
^
|
||||||
|
|
||||||
|
Here is my best effort at writing down the type. You will see ∞ for
|
||||||
|
parts of the type that repeat something already printed out
|
||||||
|
infinitely.
|
||||||
|
|
||||||
|
List ∞ -> a
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn polymorphic_mutual_recursion_annotated() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
f : a -> List a
|
||||||
|
f = \x -> g x
|
||||||
|
g = \x -> f [x]
|
||||||
|
|
||||||
|
f
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
|
This expression is used in an unexpected way:
|
||||||
|
|
||||||
|
2│ f = \x -> g x
|
||||||
|
^^^
|
||||||
|
|
||||||
|
This `g` call produces:
|
||||||
|
|
||||||
|
List List a
|
||||||
|
|
||||||
|
But you are trying to use it as:
|
||||||
|
|
||||||
|
List a
|
||||||
|
|
||||||
|
Tip: The type annotation uses the type variable `a` to say that this
|
||||||
|
definition can produce any type of value. But in the body I see that
|
||||||
|
it will only produce a `List` value of a single specific type. Maybe
|
||||||
|
change the type annotation to be more specific? Maybe change the code
|
||||||
|
to be more general?
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn polymorphic_mutual_recursion_dually_annotated_lie() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
f : a -> List a
|
||||||
|
f = \x -> g x
|
||||||
|
g : b -> List b
|
||||||
|
g = \x -> f [x]
|
||||||
|
|
||||||
|
f
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
|
This expression is used in an unexpected way:
|
||||||
|
|
||||||
|
4│ g = \x -> f [x]
|
||||||
|
^^^^^
|
||||||
|
|
||||||
|
This `f` call produces:
|
||||||
|
|
||||||
|
List List b
|
||||||
|
|
||||||
|
But you are trying to use it as:
|
||||||
|
|
||||||
|
List b
|
||||||
|
|
||||||
|
Tip: The type annotation uses the type variable `b` to say that this
|
||||||
|
definition can produce any type of value. But in the body I see that
|
||||||
|
it will only produce a `List` value of a single specific type. Maybe
|
||||||
|
change the type annotation to be more specific? Maybe change the code
|
||||||
|
to be more general?
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn record_field_mismatch() {
|
fn record_field_mismatch() {
|
||||||
report_problem_as(
|
report_problem_as(
|
||||||
|
Loading…
Reference in New Issue
Block a user