Merge branch 'trunk' into assoc-list-dict

This commit is contained in:
Folkert de Vries 2022-07-14 16:47:50 +02:00 committed by GitHub
commit 1b1b63aad0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
66 changed files with 2681 additions and 780 deletions

View file

@ -19,5 +19,15 @@ jobs:
with:
clean: "true"
- name: check code style with clippy
run: nix develop -c cargo clippy --workspace --tests -- --deny warnings
- name: check code style with clippy --release
run: cargo clippy --workspace --tests --release -- --deny warnings
# this needs to be done after clippy because of code generation in wasi-libc-sys
- name: check formatting with rustfmt
run: nix develop -c cargo fmt --all -- --check
- name: execute tests with --release
run: nix develop -c cargo test --locked --release

10
Cargo.lock generated
View file

@ -3520,6 +3520,7 @@ dependencies = [
name = "roc_derive_key"
version = "0.1.0"
dependencies = [
"roc_can",
"roc_collections",
"roc_error_macros",
"roc_module",
@ -3714,7 +3715,8 @@ dependencies = [
"bumpalo",
"roc_can",
"roc_collections",
"roc_derive_key",
"roc_derive",
"roc_error_macros",
"roc_module",
"roc_solve",
"roc_types",
@ -3769,6 +3771,7 @@ dependencies = [
"roc_collections",
"roc_constrain",
"roc_debug_flags",
"roc_derive",
"roc_derive_key",
"roc_error_macros",
"roc_late_solve",
@ -3810,7 +3813,7 @@ dependencies = [
"roc_can",
"roc_collections",
"roc_debug_flags",
"roc_derive_key",
"roc_derive",
"roc_error_macros",
"roc_exhaustive",
"roc_late_solve",
@ -3959,7 +3962,7 @@ dependencies = [
"roc_can",
"roc_collections",
"roc_constrain",
"roc_derive_key",
"roc_derive",
"roc_exhaustive",
"roc_load",
"roc_module",
@ -3988,6 +3991,7 @@ dependencies = [
"roc_can",
"roc_collections",
"roc_debug_flags",
"roc_derive",
"roc_derive_key",
"roc_error_macros",
"roc_exhaustive",

View file

@ -12,7 +12,7 @@ install-other-libs:
RUN apt -y install libunwind-dev pkg-config libx11-dev zlib1g-dev
RUN apt -y install unzip # for www/build.sh
install-zig-llvm-valgrind-clippy-rustfmt:
install-zig-llvm-valgrind:
FROM +install-other-libs
# editor
RUN apt -y install libxkbcommon-dev
@ -35,10 +35,6 @@ install-zig-llvm-valgrind-clippy-rustfmt:
ENV RUSTFLAGS="-C link-arg=-fuse-ld=lld -C target-cpu=native"
# valgrind
RUN apt -y install valgrind
# clippy
RUN rustup component add clippy
# rustfmt
RUN rustup component add rustfmt
# wasm repl & tests
RUN rustup target add wasm32-unknown-unknown wasm32-wasi
RUN apt -y install libssl-dev
@ -53,11 +49,11 @@ install-zig-llvm-valgrind-clippy-rustfmt:
ENV CARGO_INCREMENTAL=0 # no need to recompile package when using new function
copy-dirs:
FROM +install-zig-llvm-valgrind-clippy-rustfmt
FROM +install-zig-llvm-valgrind
COPY --dir crates examples Cargo.toml Cargo.lock version.txt www ./
test-zig:
FROM +install-zig-llvm-valgrind-clippy-rustfmt
FROM +install-zig-llvm-valgrind
COPY --dir crates/compiler/builtins/bitcode ./
RUN cd bitcode && ./run-tests.sh && ./run-wasm-tests.sh
@ -70,19 +66,6 @@ build-rust-test:
RUN --mount=type=cache,target=$SCCACHE_DIR \
cargo test --locked --release --features with_sound --workspace --no-run && sccache --show-stats
check-clippy:
FROM +build-rust-test
RUN cargo clippy -V
RUN --mount=type=cache,target=$SCCACHE_DIR \
cargo clippy --workspace --tests -- --deny warnings
RUN --mount=type=cache,target=$SCCACHE_DIR \
cargo clippy --workspace --tests --release -- --deny warnings
check-rustfmt:
FROM +build-rust-test
RUN cargo fmt --version
RUN cargo fmt --all -- --check
check-typos:
RUN cargo install typos-cli --version 1.0.11 # version set to prevent confusion if the version is updated automatically
COPY --dir .github ci crates examples nightly_benches www *.md LEGAL_DETAILS flake.nix version.txt ./
@ -132,8 +115,6 @@ verify-no-git-changes:
test-all:
BUILD +test-zig
BUILD +check-rustfmt
BUILD +check-clippy
BUILD +test-rust
BUILD +verify-no-git-changes

View file

@ -10,15 +10,6 @@ pub fn load_module(src_file: &Path, threading: Threading) -> LoadedModule {
let loaded = roc_load::load_and_typecheck(
&arena,
src_file.to_path_buf(),
src_file
.parent()
.unwrap_or_else(|| {
panic!(
"src_file {:?} did not have a parent directory but I need to have one.",
src_file
)
})
.to_path_buf(),
subs_by_module,
TargetInfo::default_x86_64(),
roc_reporting::report::RenderTarget::ColorTerminal,

View file

@ -266,6 +266,9 @@ fn add_type(target_info: TargetInfo, id: TypeId, types: &Types, impls: &mut Impl
// This is recursively pointing to a type that should already have been added,
// so no extra work needs to happen.
}
RocType::Function(_, _) => {
// TODO actually bindgen functions!
}
}
}
@ -375,7 +378,7 @@ pub struct {name} {{
write!(buf, "{INDENT}{tag_name}: ").unwrap();
if payload_type.has_pointer(types) {
if cannot_derive_copy(payload_type, types) {
// types with pointers need ManuallyDrop
// because rust unions don't (and can't)
// know how to drop them automatically!
@ -553,7 +556,7 @@ pub struct {name} {{
match recursiveness {
Recursiveness::Recursive => {
if payload_type.has_pointer(types) {
if cannot_derive_copy(payload_type, types) {
owned_get_payload = format!(
r#"{{
let ptr = (self.pointer as usize & !{bitmask}) as *mut {union_name};
@ -590,7 +593,7 @@ pub struct {name} {{
};
}
Recursiveness::NonRecursive => {
if payload_type.has_pointer(types) {
if cannot_derive_copy(payload_type, types) {
owned_get_payload =
format!("core::mem::ManuallyDrop::take(&mut self.{tag_name})");
borrowed_get_payload = format!("&self.{tag_name}");
@ -620,7 +623,7 @@ pub struct {name} {{
owned_ret = "payload".to_string();
borrowed_ret = format!("&{owned_ret}");
payload_args = format!("arg: {owned_ret_type}");
args_to_payload = if payload_type.has_pointer(types) {
args_to_payload = if cannot_derive_copy(payload_type, types) {
"core::mem::ManuallyDrop::new(arg)".to_string()
} else {
"arg".to_string()
@ -648,6 +651,7 @@ pub struct {name} {{
payload_args = answer.payload_args;
args_to_payload = answer.args_to_payload;
}
RocType::Function(_, _) => todo!(),
};
{
@ -790,7 +794,7 @@ pub struct {name} {{
&mut drop_payload,
|tag_name, opt_payload_id| {
match opt_payload_id {
Some(payload_id) if types.get_type(payload_id).has_pointer(types) => {
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}) }},",)
}
_ => {
@ -845,7 +849,7 @@ pub struct {name} {{
// The PartialEq impl for the tag union
{
let opt_impl_prefix = if typ.has_float(types) {
let opt_impl_prefix = if has_float(typ, types) {
String::new()
} else {
format!("impl Eq for {name} {{}}\n\n")
@ -966,7 +970,7 @@ pub struct {name} {{
// The Clone impl for the tag union
{
let opt_impl_prefix = if typ.has_pointer(types) {
let opt_impl_prefix = if cannot_derive_copy(typ, types) {
String::new()
} else {
format!("impl Copy for {name} {{}}\n\n")
@ -1089,7 +1093,7 @@ pub struct {name} {{
// (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 payload_type.has_pointer(types) {
let deref_str = if cannot_derive_copy(payload_type, types) {
"&*"
} else {
"&"
@ -1120,6 +1124,7 @@ pub struct {name} {{
buf.join("\n")
}
RocType::Function(_, _) => todo!(),
};
format!(
@ -1286,6 +1291,7 @@ fn type_name(id: TypeId, types: &Types) -> String {
| RocType::TagUnion(RocTagUnion::NullableUnwrapped { name, .. })
| RocType::TagUnion(RocTagUnion::NonNullableUnwrapped { name, .. }) => name.clone(),
RocType::RecursivePointer(content) => type_name(*content, types),
RocType::Function(_, _) => todo!(),
}
}
@ -1295,7 +1301,7 @@ fn type_name(id: TypeId, types: &Types) -> String {
fn derive_str(typ: &RocType, types: &Types, include_debug: bool) -> String {
let mut buf = "#[derive(Clone, ".to_string();
if !typ.has_pointer(types) {
if !cannot_derive_copy(typ, types) {
buf.push_str("Copy, ");
}
@ -1303,11 +1309,11 @@ fn derive_str(typ: &RocType, types: &Types, include_debug: bool) -> String {
buf.push_str("Debug, ");
}
if !typ.has_enumeration(types) {
if !cannot_derive_default(typ, types) {
buf.push_str("Default, ");
}
if !typ.has_float(types) {
if !has_float(typ, types) {
buf.push_str("Eq, Ord, Hash, ");
}
@ -1335,13 +1341,13 @@ fn add_nullable_unwrapped(
let discriminant_name = add_discriminant(name, target_info, tag_names, types, impls);
let payload_type = types.get_type(non_null_payload);
let payload_type_name = type_name(non_null_payload, types);
let has_pointer = payload_type.has_pointer(types);
let cannot_derive_copy = cannot_derive_copy(payload_type, types);
// The opaque struct for the tag union
{
// This struct needs its own Clone impl because it has
// a refcount to bump
let derive_extras = if types.get_type(id).has_float(types) {
let derive_extras = if has_float(types.get_type(id), types) {
""
} else {
", Eq, Ord, Hash"
@ -1430,6 +1436,7 @@ pub struct {name} {{
owned_ret_type = answer.owned_ret_type;
borrowed_ret_type = answer.borrowed_ret_type;
}
RocType::Function(_, _) => todo!(),
};
// Add a convenience constructor function for the tag with the payload, e.g.
@ -1481,7 +1488,7 @@ pub struct {name} {{
);
{
let assign_payload = if has_pointer {
let assign_payload = if cannot_derive_copy {
"core::mem::ManuallyDrop::take(&mut *self.pointer)"
} else {
"*self.pointer"
@ -1624,7 +1631,7 @@ pub struct {name} {{
// The Debug impl for the tag union
{
let opt_impl = Some(format!("impl core::fmt::Debug for {name}"));
let extra_deref = if has_pointer { "*" } else { "" };
let extra_deref = if cannot_derive_copy { "*" } else { "" };
let fields_str = match payload_type {
RocType::RocStr
@ -1659,6 +1666,7 @@ pub struct {name} {{
buf.join(&format!("\n{INDENT}{INDENT}{INDENT}{INDENT}{INDENT}"))
}
RocType::Function(_, _) => todo!(),
};
let body = format!(
@ -1852,3 +1860,124 @@ fn tag_union_struct_help<'a, I: Iterator<Item = &'a (L, TypeId)>, L: Display + P
borrowed_ret_type,
}
}
fn cannot_derive_default(roc_type: &RocType, types: &Types) -> bool {
match roc_type {
RocType::TagUnion { .. } | RocType::RecursivePointer { .. } | RocType::Function(_, _) => {
true
}
RocType::RocStr | RocType::Bool | RocType::Num(_) => false,
RocType::RocList(id) | RocType::RocSet(id) | RocType::RocBox(id) => {
cannot_derive_default(types.get_type(*id), types)
}
RocType::RocDict(key_id, val_id) => {
cannot_derive_default(types.get_type(*key_id), types)
|| cannot_derive_default(types.get_type(*val_id), types)
}
RocType::Struct { fields, .. } => fields
.iter()
.any(|(_, type_id)| cannot_derive_default(types.get_type(*type_id), types)),
RocType::TagUnionPayload { fields, .. } => fields
.iter()
.any(|(_, type_id)| cannot_derive_default(types.get_type(*type_id), types)),
}
}
/// Useful when determining whether to derive Copy in a Rust type.
fn cannot_derive_copy(roc_type: &RocType, types: &Types) -> bool {
match roc_type {
RocType::Bool
| RocType::Num(_)
| RocType::TagUnion(RocTagUnion::Enumeration { .. })
| RocType::Function(_, _) => false,
RocType::RocStr
| RocType::RocList(_)
| RocType::RocDict(_, _)
| RocType::RocSet(_)
| RocType::RocBox(_)
| RocType::TagUnion(RocTagUnion::NonNullableUnwrapped { .. })
| RocType::TagUnion(RocTagUnion::NullableUnwrapped { .. })
| RocType::TagUnion(RocTagUnion::NullableWrapped { .. })
| RocType::TagUnion(RocTagUnion::Recursive { .. })
| RocType::RecursivePointer { .. } => true,
RocType::TagUnion(RocTagUnion::NonRecursive { tags, .. }) => {
tags.iter().any(|(_, payloads)| {
payloads
.iter()
.any(|id| cannot_derive_copy(types.get_type(*id), types))
})
}
RocType::Struct { fields, .. } => fields
.iter()
.any(|(_, type_id)| cannot_derive_copy(types.get_type(*type_id), types)),
RocType::TagUnionPayload { fields, .. } => fields
.iter()
.any(|(_, type_id)| cannot_derive_copy(types.get_type(*type_id), types)),
}
}
/// Useful when determining whether to derive Eq, Ord, and Hash in a Rust type.
fn has_float(roc_type: &RocType, types: &Types) -> bool {
has_float_help(roc_type, types, &[])
}
fn has_float_help(roc_type: &RocType, types: &Types, do_not_recurse: &[TypeId]) -> bool {
match roc_type {
RocType::Num(num) => {
use RocNum::*;
match num {
F32 | F64 | F128 => true,
I8 | U8 | I16 | U16 | I32 | U32 | I64 | U64 | I128 | U128 | Dec => false,
}
}
RocType::RocStr
| RocType::Bool
| RocType::TagUnion(RocTagUnion::Enumeration { .. })
| RocType::Function(_, _) => false,
RocType::RocList(id) | RocType::RocSet(id) | RocType::RocBox(id) => {
has_float_help(types.get_type(*id), types, do_not_recurse)
}
RocType::RocDict(key_id, val_id) => {
has_float_help(types.get_type(*key_id), types, do_not_recurse)
|| has_float_help(types.get_type(*val_id), types, do_not_recurse)
}
RocType::Struct { fields, .. } => fields
.iter()
.any(|(_, type_id)| has_float_help(types.get_type(*type_id), types, do_not_recurse)),
RocType::TagUnionPayload { fields, .. } => fields
.iter()
.any(|(_, type_id)| has_float_help(types.get_type(*type_id), types, do_not_recurse)),
RocType::TagUnion(RocTagUnion::Recursive { tags, .. })
| RocType::TagUnion(RocTagUnion::NonRecursive { tags, .. }) => {
tags.iter().any(|(_, payloads)| {
payloads
.iter()
.any(|id| has_float_help(types.get_type(*id), types, do_not_recurse))
})
}
RocType::TagUnion(RocTagUnion::NullableWrapped { non_null_tags, .. }) => {
non_null_tags.iter().any(|(_, _, payloads)| {
payloads
.iter()
.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 { content, .. })
| RocType::RecursivePointer(content) => {
if do_not_recurse.contains(content) {
false
} else {
let mut do_not_recurse: Vec<TypeId> = do_not_recurse.into();
do_not_recurse.push(*content);
has_float_help(types.get_type(*content), types, &do_not_recurse)
}
}
}
}

View file

@ -10,7 +10,6 @@ use target_lexicon::Triple;
pub fn load_types(
full_file_path: PathBuf,
dir: PathBuf,
threading: Threading,
) -> Result<Vec<(Types, TargetInfo)>, io::Error> {
let target_info = (&Triple::host()).into();
@ -28,7 +27,6 @@ pub fn load_types(
} = roc_load::load_and_typecheck(
arena,
full_file_path,
dir,
subs_by_module,
target_info,
RenderTarget::Generic,

View file

@ -33,7 +33,6 @@ enum OutputType {
pub fn main() {
let opts = Opts::parse();
let input_path = opts.platform_module;
let cwd = std::env::current_dir().unwrap();
let output_path = opts.dest;
let output_type = match output_path.extension().and_then(OsStr::to_str) {
Some("rs") => OutputType::Rust,
@ -56,7 +55,7 @@ pub fn main() {
}
};
match load_types(input_path.clone(), cwd, Threading::AllAvailable) {
match load_types(input_path.clone(), Threading::AllAvailable) {
Ok(types_and_targets) => {
let mut file = File::create(output_path.clone()).unwrap_or_else(|err| {
eprintln!(

View file

@ -156,6 +156,7 @@ pub enum RocType {
/// this would be the field of Cons containing the (recursive) StrConsList type,
/// and the TypeId is the TypeId of StrConsList itself.
RecursivePointer(TypeId),
Function(Vec<TypeId>, TypeId),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
@ -202,136 +203,6 @@ impl RocNum {
}
}
impl RocType {
/// Useful when determining whether to derive Copy in a Rust type.
pub fn has_pointer(&self, types: &Types) -> bool {
match self {
RocType::Bool
| RocType::Num(_)
| RocType::TagUnion(RocTagUnion::Enumeration { .. }) => false,
RocType::RocStr
| RocType::RocList(_)
| RocType::RocDict(_, _)
| RocType::RocSet(_)
| RocType::RocBox(_)
| RocType::TagUnion(RocTagUnion::NonNullableUnwrapped { .. })
| RocType::TagUnion(RocTagUnion::NullableUnwrapped { .. })
| RocType::TagUnion(RocTagUnion::NullableWrapped { .. })
| RocType::TagUnion(RocTagUnion::Recursive { .. })
| RocType::RecursivePointer { .. } => true,
RocType::TagUnion(RocTagUnion::NonRecursive { tags, .. }) => {
tags.iter().any(|(_, payloads)| {
payloads
.iter()
.any(|id| types.get_type(*id).has_pointer(types))
})
}
RocType::Struct { fields, .. } => fields
.iter()
.any(|(_, type_id)| types.get_type(*type_id).has_pointer(types)),
RocType::TagUnionPayload { fields, .. } => fields
.iter()
.any(|(_, type_id)| types.get_type(*type_id).has_pointer(types)),
}
}
/// Useful when determining whether to derive Eq, Ord, and Hash in a Rust type.
pub fn has_float(&self, types: &Types) -> bool {
self.has_float_help(types, &[])
}
fn has_float_help(&self, types: &Types, do_not_recurse: &[TypeId]) -> bool {
match self {
RocType::Num(num) => {
use RocNum::*;
match num {
F32 | F64 | F128 => true,
I8 | U8 | I16 | U16 | I32 | U32 | I64 | U64 | I128 | U128 | Dec => false,
}
}
RocType::RocStr
| RocType::Bool
| RocType::TagUnion(RocTagUnion::Enumeration { .. }) => false,
RocType::RocList(id) | RocType::RocSet(id) | RocType::RocBox(id) => {
types.get_type(*id).has_float_help(types, do_not_recurse)
}
RocType::RocDict(key_id, val_id) => {
types
.get_type(*key_id)
.has_float_help(types, do_not_recurse)
|| types
.get_type(*val_id)
.has_float_help(types, do_not_recurse)
}
RocType::Struct { fields, .. } => fields.iter().any(|(_, type_id)| {
types
.get_type(*type_id)
.has_float_help(types, do_not_recurse)
}),
RocType::TagUnionPayload { fields, .. } => fields.iter().any(|(_, type_id)| {
types
.get_type(*type_id)
.has_float_help(types, do_not_recurse)
}),
RocType::TagUnion(RocTagUnion::Recursive { tags, .. })
| RocType::TagUnion(RocTagUnion::NonRecursive { tags, .. }) => {
tags.iter().any(|(_, payloads)| {
payloads
.iter()
.any(|id| types.get_type(*id).has_float_help(types, do_not_recurse))
})
}
RocType::TagUnion(RocTagUnion::NullableWrapped { non_null_tags, .. }) => {
non_null_tags.iter().any(|(_, _, payloads)| {
payloads
.iter()
.any(|id| types.get_type(*id).has_float_help(types, do_not_recurse))
})
}
RocType::TagUnion(RocTagUnion::NullableUnwrapped {
non_null_payload: content,
..
})
| RocType::TagUnion(RocTagUnion::NonNullableUnwrapped { content, .. })
| RocType::RecursivePointer(content) => {
if do_not_recurse.contains(content) {
false
} else {
let mut do_not_recurse: Vec<TypeId> = do_not_recurse.into();
do_not_recurse.push(*content);
types
.get_type(*content)
.has_float_help(types, &do_not_recurse)
}
}
}
}
/// Useful when determining whether to derive Default in a Rust type.
pub fn has_enumeration(&self, types: &Types) -> bool {
match self {
RocType::TagUnion { .. } | RocType::RecursivePointer { .. } => true,
RocType::RocStr | RocType::Bool | RocType::Num(_) => false,
RocType::RocList(id) | RocType::RocSet(id) | RocType::RocBox(id) => {
types.get_type(*id).has_enumeration(types)
}
RocType::RocDict(key_id, val_id) => {
types.get_type(*key_id).has_enumeration(types)
|| types.get_type(*val_id).has_enumeration(types)
}
RocType::Struct { fields, .. } => fields
.iter()
.any(|(_, type_id)| types.get_type(*type_id).has_enumeration(types)),
RocType::TagUnionPayload { fields, .. } => fields
.iter()
.any(|(_, type_id)| types.get_type(*type_id).has_enumeration(types)),
}
}
}
impl From<IntWidth> for RocNum {
fn from(width: IntWidth) -> Self {
match width {
@ -558,8 +429,38 @@ fn add_type_help<'a>(
}
}
},
Content::Structure(FlatType::Func(_, _, _)) => {
todo!()
Content::Structure(FlatType::Func(args, _closure_var, ret_var)) => {
let args = env.subs.get_subs_slice(*args);
let mut arg_type_ids = Vec::with_capacity(args.len());
for arg_var in args {
let arg_layout = env
.layout_cache
.from_var(env.arena, *arg_var, env.subs)
.expect("Something weird ended up in the content");
arg_type_ids.push(add_type_help(env, arg_layout, *arg_var, None, types));
}
let ret_type_id = {
let ret_layout = env
.layout_cache
.from_var(env.arena, *ret_var, env.subs)
.expect("Something weird ended up in the content");
add_type_help(env, ret_layout, *ret_var, None, types)
};
let fn_type_id =
types.add(RocType::Function(arg_type_ids.clone(), ret_type_id), layout);
types.depends(fn_type_id, ret_type_id);
for arg_type_id in arg_type_ids {
types.depends(fn_type_id, arg_type_id);
}
fn_type_id
}
Content::Structure(FlatType::FunctionOrTagUnion(_, _, _)) => {
todo!()

View file

@ -33,7 +33,7 @@ pub fn generate_bindings(decl_src: &str) -> String {
let mut file = File::create(file_path).unwrap();
writeln!(file, "{}", &src).unwrap();
let result = load_types(full_file_path, dir.path().to_path_buf(), Threading::Single);
let result = load_types(full_file_path, Threading::Single);
dir.close().expect("Unable to close tempdir");

View file

@ -35,7 +35,6 @@ pub struct BuiltFile {
pub fn build_file<'a>(
arena: &'a Bump,
target: &Triple,
src_dir: PathBuf,
app_module_path: PathBuf,
opt_level: OptLevel,
emit_debug_info: bool,
@ -55,7 +54,6 @@ pub fn build_file<'a>(
let loaded = roc_load::load_and_monomorphize(
arena,
app_module_path.clone(),
src_dir,
subs_by_module,
target_info,
// TODO: expose this from CLI?
@ -419,7 +417,6 @@ fn spawn_rebuild_thread(
#[allow(clippy::too_many_arguments)]
pub fn check_file(
arena: &Bump,
src_dir: PathBuf,
roc_file_path: PathBuf,
emit_timings: bool,
threading: Threading,
@ -436,7 +433,6 @@ pub fn check_file(
let mut loaded = roc_load::load_and_typecheck(
arena,
roc_file_path,
src_dir,
subs_by_module,
target_info,
// TODO: expose this from CLI?

View file

@ -353,8 +353,6 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
);
}
let src_dir = path.parent().unwrap().canonicalize().unwrap();
// let target_valgrind = matches.is_present(FLAG_VALGRIND);
let arena = &arena;
@ -368,7 +366,6 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
let loaded = roc_load::load_and_monomorphize(
arena,
path,
src_dir,
subs_by_module,
target_info,
// TODO: expose this from CLI?
@ -494,12 +491,10 @@ pub fn build(
}
});
let src_dir = path.parent().unwrap().canonicalize().unwrap();
let target_valgrind = matches.is_present(FLAG_VALGRIND);
let res_binary_path = build_file(
&arena,
&triple,
src_dir,
path,
opt_level,
emit_debug_info,

View file

@ -90,8 +90,6 @@ fn main() -> io::Result<()> {
let emit_timings = matches.is_present(FLAG_TIME);
let filename = matches.value_of_os(ROC_FILE).unwrap();
let roc_file_path = PathBuf::from(filename);
let src_dir = roc_file_path.parent().unwrap().to_owned();
let threading = match matches
.value_of(roc_cli::FLAG_MAX_THREADS)
.and_then(|s| s.parse::<usize>().ok())
@ -102,7 +100,7 @@ fn main() -> io::Result<()> {
Some(n) => Threading::AtMost(n),
};
match check_file(&arena, src_dir, roc_file_path, emit_timings, threading) {
match check_file(&arena, roc_file_path, emit_timings, threading) {
Ok((problems, total_time)) => {
println!(
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.",

View file

@ -131,7 +131,7 @@ pub const RocDec = extern struct {
return (c -% 48) <= 9;
}
pub fn toStr(self: RocDec) ?RocStr {
pub fn toStr(self: RocDec) RocStr {
// Special case
if (self.num == 0) {
return RocStr.init("0.0", 3);
@ -843,7 +843,7 @@ test "toStr: 123.45" {
var res_roc_str = dec.toStr();
const res_slice: []const u8 = "123.45"[0..];
try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice());
try expectEqualSlices(u8, res_slice, res_roc_str.asSlice());
}
test "toStr: -123.45" {
@ -851,7 +851,7 @@ test "toStr: -123.45" {
var res_roc_str = dec.toStr();
const res_slice: []const u8 = "-123.45"[0..];
try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice());
try expectEqualSlices(u8, res_slice, res_roc_str.asSlice());
}
test "toStr: 123.0" {
@ -859,7 +859,7 @@ test "toStr: 123.0" {
var res_roc_str = dec.toStr();
const res_slice: []const u8 = "123.0"[0..];
try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice());
try expectEqualSlices(u8, res_slice, res_roc_str.asSlice());
}
test "toStr: -123.0" {
@ -867,7 +867,7 @@ test "toStr: -123.0" {
var res_roc_str = dec.toStr();
const res_slice: []const u8 = "-123.0"[0..];
try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice());
try expectEqualSlices(u8, res_slice, res_roc_str.asSlice());
}
test "toStr: 0.45" {
@ -875,7 +875,7 @@ test "toStr: 0.45" {
var res_roc_str = dec.toStr();
const res_slice: []const u8 = "0.45"[0..];
try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice());
try expectEqualSlices(u8, res_slice, res_roc_str.asSlice());
}
test "toStr: -0.45" {
@ -883,7 +883,7 @@ test "toStr: -0.45" {
var res_roc_str = dec.toStr();
const res_slice: []const u8 = "-0.45"[0..];
try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice());
try expectEqualSlices(u8, res_slice, res_roc_str.asSlice());
}
test "toStr: 0.00045" {
@ -891,7 +891,7 @@ test "toStr: 0.00045" {
var res_roc_str = dec.toStr();
const res_slice: []const u8 = "0.00045"[0..];
try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice());
try expectEqualSlices(u8, res_slice, res_roc_str.asSlice());
}
test "toStr: -0.00045" {
@ -899,7 +899,7 @@ test "toStr: -0.00045" {
var res_roc_str = dec.toStr();
const res_slice: []const u8 = "-0.00045"[0..];
try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice());
try expectEqualSlices(u8, res_slice, res_roc_str.asSlice());
}
test "toStr: -111.123456" {
@ -907,7 +907,7 @@ test "toStr: -111.123456" {
var res_roc_str = dec.toStr();
const res_slice: []const u8 = "-111.123456"[0..];
try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice());
try expectEqualSlices(u8, res_slice, res_roc_str.asSlice());
}
test "toStr: 123.1111111" {
@ -915,57 +915,57 @@ test "toStr: 123.1111111" {
var res_roc_str = dec.toStr();
const res_slice: []const u8 = "123.1111111"[0..];
try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice());
try expectEqualSlices(u8, res_slice, res_roc_str.asSlice());
}
test "toStr: 123.1111111111111 (big str)" {
var dec: RocDec = .{ .num = 123111111111111000000 };
var res_roc_str = dec.toStr();
errdefer res_roc_str.?.deinit();
defer res_roc_str.?.deinit();
errdefer res_roc_str.deinit();
defer res_roc_str.deinit();
const res_slice: []const u8 = "123.111111111111"[0..];
try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice());
try expectEqualSlices(u8, res_slice, res_roc_str.asSlice());
}
test "toStr: 123.111111111111444444 (max number of decimal places)" {
var dec: RocDec = .{ .num = 123111111111111444444 };
var res_roc_str = dec.toStr();
errdefer res_roc_str.?.deinit();
defer res_roc_str.?.deinit();
errdefer res_roc_str.deinit();
defer res_roc_str.deinit();
const res_slice: []const u8 = "123.111111111111444444"[0..];
try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice());
try expectEqualSlices(u8, res_slice, res_roc_str.asSlice());
}
test "toStr: 12345678912345678912.111111111111111111 (max number of digits)" {
var dec: RocDec = .{ .num = 12345678912345678912111111111111111111 };
var res_roc_str = dec.toStr();
errdefer res_roc_str.?.deinit();
defer res_roc_str.?.deinit();
errdefer res_roc_str.deinit();
defer res_roc_str.deinit();
const res_slice: []const u8 = "12345678912345678912.111111111111111111"[0..];
try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice());
try expectEqualSlices(u8, res_slice, res_roc_str.asSlice());
}
test "toStr: std.math.maxInt" {
var dec: RocDec = .{ .num = std.math.maxInt(i128) };
var res_roc_str = dec.toStr();
errdefer res_roc_str.?.deinit();
defer res_roc_str.?.deinit();
errdefer res_roc_str.deinit();
defer res_roc_str.deinit();
const res_slice: []const u8 = "170141183460469231731.687303715884105727"[0..];
try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice());
try expectEqualSlices(u8, res_slice, res_roc_str.asSlice());
}
test "toStr: std.math.minInt" {
var dec: RocDec = .{ .num = std.math.minInt(i128) };
var res_roc_str = dec.toStr();
errdefer res_roc_str.?.deinit();
defer res_roc_str.?.deinit();
errdefer res_roc_str.deinit();
defer res_roc_str.deinit();
const res_slice: []const u8 = "-170141183460469231731.687303715884105728"[0..];
try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice());
try expectEqualSlices(u8, res_slice, res_roc_str.asSlice());
}
test "toStr: 0" {
@ -973,7 +973,7 @@ test "toStr: 0" {
var res_roc_str = dec.toStr();
const res_slice: []const u8 = "0.0"[0..];
try expectEqualSlices(u8, res_slice, res_roc_str.?.asSlice());
try expectEqualSlices(u8, res_slice, res_roc_str.asSlice());
}
test "add: 0" {
@ -1065,6 +1065,10 @@ pub fn fromStr(arg: RocStr) callconv(.C) num_.NumParseResult(i128) {
}
}
pub fn toStr(arg: RocDec) callconv(.C) RocStr {
return @call(.{ .modifier = always_inline }, RocDec.toStr, .{arg});
}
pub fn fromF64C(arg: f64) callconv(.C) i128 {
return if (@call(.{ .modifier = always_inline }, RocDec.fromF64, .{arg})) |dec| dec.num else @panic("TODO runtime exception failing convert f64 to RocDec");
}

View file

@ -12,6 +12,7 @@ const dec = @import("dec.zig");
comptime {
exportDecFn(dec.fromStr, "from_str");
exportDecFn(dec.toStr, "to_str");
exportDecFn(dec.fromF64C, "from_f64");
exportDecFn(dec.eqC, "eq");
exportDecFn(dec.neqC, "neq");
@ -128,7 +129,6 @@ comptime {
exportStrFn(str.strConcatC, "concat");
exportStrFn(str.strJoinWithC, "joinWith");
exportStrFn(str.strNumberOfBytes, "number_of_bytes");
exportStrFn(str.strFromFloatC, "from_float");
exportStrFn(str.strEqual, "equal");
exportStrFn(str.substringUnsafe, "substring_unsafe");
exportStrFn(str.getUnsafe, "get_unsafe");
@ -149,6 +149,7 @@ comptime {
}
inline for (FLOATS) |T| {
str.exportFromFloat(T, ROC_BUILTINS ++ "." ++ STR ++ ".from_float.");
num.exportParseFloat(T, ROC_BUILTINS ++ "." ++ STR ++ ".to_float.");
}
}

View file

@ -749,12 +749,18 @@ fn strFromIntHelp(comptime T: type, int: T) RocStr {
}
// Str.fromFloat
pub fn strFromFloatC(float: f64) callconv(.C) RocStr {
return @call(.{ .modifier = always_inline }, strFromFloatHelp, .{ f64, float });
pub fn exportFromFloat(comptime T: type, comptime name: []const u8) void {
comptime var f = struct {
fn func(float: T) callconv(.C) RocStr {
return @call(.{ .modifier = always_inline }, strFromFloatHelp, .{ T, float });
}
}.func;
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
}
fn strFromFloatHelp(comptime T: type, float: T) RocStr {
var buf: [100]u8 = undefined;
var buf: [400]u8 = undefined;
const result = std.fmt.bufPrint(&buf, "{d}", .{float}) catch unreachable;
return RocStr.init(&buf, result.len);
@ -1674,16 +1680,31 @@ pub fn fromUtf8Range(arg: RocList, start: usize, count: usize, update_mode: Upda
.problem_code = Utf8ByteProblem.InvalidStartByte,
};
} else {
// turn the bytes into a small string
const string = RocStr.init(@ptrCast([*]const u8, bytes), count);
// decref the list
utils.decref(arg.bytes, arg.len(), 1);
return FromUtf8Result{
.is_ok = true,
.string = RocStr.init(@ptrCast([*]const u8, bytes), count),
.string = string,
.byte_index = 0,
.problem_code = Utf8ByteProblem.InvalidStartByte,
};
}
} else {
const temp = errorToProblem(@ptrCast([*]u8, arg.bytes), arg.length);
return FromUtf8Result{ .is_ok = false, .string = RocStr.empty(), .byte_index = temp.index, .problem_code = temp.problem };
// decref the list
utils.decref(arg.bytes, arg.len(), 1);
return FromUtf8Result{
.is_ok = false,
.string = RocStr.empty(),
.byte_index = temp.index,
.problem_code = temp.problem,
};
}
}

View file

@ -820,7 +820,10 @@ findIndex = \list, matcher ->
## Some languages have a function called **`slice`** which works similarly to this.
sublist : List elem, { start : Nat, len : Nat } -> List elem
sublist = \list, config ->
sublistLowlevel list config.start config.len
if config.len == 0 then
[]
else
sublistLowlevel list config.start config.len
## low-level slicing operation that does no bounds checking
sublistLowlevel : List elem, Nat, Nat -> List elem

View file

@ -320,7 +320,7 @@ pub const STR_STARTS_WITH_SCALAR: &str = "roc_builtins.str.starts_with_scalar";
pub const STR_ENDS_WITH: &str = "roc_builtins.str.ends_with";
pub const STR_NUMBER_OF_BYTES: &str = "roc_builtins.str.number_of_bytes";
pub const STR_FROM_INT: IntrinsicName = int_intrinsic!("roc_builtins.str.from_int");
pub const STR_FROM_FLOAT: &str = "roc_builtins.str.from_float";
pub const STR_FROM_FLOAT: IntrinsicName = float_intrinsic!("roc_builtins.str.from_float");
pub const STR_TO_INT: IntrinsicName = int_intrinsic!("roc_builtins.str.to_int");
pub const STR_TO_FLOAT: IntrinsicName = float_intrinsic!("roc_builtins.str.to_float");
pub const STR_TO_DECIMAL: &str = "roc_builtins.str.to_decimal";
@ -355,6 +355,7 @@ pub const LIST_APPEND_UNSAFE: &str = "roc_builtins.list.append_unsafe";
pub const LIST_RESERVE: &str = "roc_builtins.list.reserve";
pub const DEC_FROM_STR: &str = "roc_builtins.dec.from_str";
pub const DEC_TO_STR: &str = "roc_builtins.dec.to_str";
pub const DEC_FROM_F64: &str = "roc_builtins.dec.from_f64";
pub const DEC_EQ: &str = "roc_builtins.dec.eq";
pub const DEC_NEQ: &str = "roc_builtins.dec.neq";

View file

@ -359,9 +359,6 @@ impl IAbilitiesStore<Resolved> {
}
pub fn insert_resolved(&mut self, id: SpecializationId, specialization: Symbol) {
// May not be a thing in mono
// debug_assert!(self.is_specialization_name(specialization));
let old_specialization = self.resolved_specializations.insert(id, specialization);
debug_assert!(
@ -371,15 +368,6 @@ impl IAbilitiesStore<Resolved> {
);
}
pub fn remove_resolved(&mut self, id: SpecializationId) {
let old_specialization = self.resolved_specializations.remove(&id);
debug_assert!(
old_specialization.is_some(),
"Trying to remove a resolved specialization that was never there!",
);
}
pub fn get_resolved(&self, id: SpecializationId) -> Option<Symbol> {
self.resolved_specializations.get(&id).copied()
}

View file

@ -1,8 +1,11 @@
use roc_can::{
use crate::{
def::Def,
expr::{AccessorData, ClosureData, Expr, Field, OpaqueWrapFunctionData, WhenBranch},
};
use roc_module::ident::{Lowercase, TagName};
use roc_module::{
ident::{Lowercase, TagName},
symbol::Symbol,
};
use roc_types::{
subs::{
self, AliasVariables, Descriptor, GetSubsSlice, OptVariable, RecordFields, Subs, SubsIndex,
@ -62,6 +65,8 @@ trait CopyEnv {
fn clone_tag_names(&mut self, tag_names: SubsSlice<TagName>) -> SubsSlice<TagName>;
fn clone_lambda_names(&mut self, lambda_names: SubsSlice<Symbol>) -> SubsSlice<Symbol>;
fn clone_record_fields(
&mut self,
record_fields: SubsSlice<RecordField<()>>,
@ -104,6 +109,11 @@ impl CopyEnv for Subs {
tag_names
}
#[inline(always)]
fn clone_lambda_names(&mut self, lambda_names: SubsSlice<Symbol>) -> SubsSlice<Symbol> {
lambda_names
}
#[inline(always)]
fn clone_record_fields(
&mut self,
@ -160,6 +170,14 @@ impl<'a> CopyEnv for AcrossSubs<'a> {
)
}
#[inline(always)]
fn clone_lambda_names(&mut self, lambda_names: SubsSlice<Symbol>) -> SubsSlice<Symbol> {
SubsSlice::extend_new(
&mut self.target.closure_names,
self.source.get_subs_slice(lambda_names).iter().cloned(),
)
}
#[inline(always)]
fn clone_record_fields(
&mut self,
@ -186,9 +204,9 @@ pub fn deep_copy_expr_across_subs(
target: &mut Subs,
var: Variable,
expr: &Expr,
) -> Option<(Variable, Expr)> {
) -> (Variable, Expr) {
let mut across_subs = AcrossSubs { source, target };
deep_copy_type_vars_into_expr_help(&mut across_subs, var, expr)
deep_copy_type_vars_into_expr_help(&mut across_subs, var, expr).unwrap()
}
/// Deep copies all type variables in [`expr`].
@ -849,8 +867,10 @@ fn deep_copy_type_vars<C: CopyEnv>(
env.target().variable_slices[target_index] = new_variables;
}
let new_solved_labels = env.clone_lambda_names(solved.labels());
let new_solved =
UnionLambdas::from_slices(solved.labels(), new_variable_slices);
UnionLambdas::from_slices(new_solved_labels, new_variable_slices);
let new_unspecialized =
SubsSlice::reserve_uls_slice(env.target(), unspecialized.len());
@ -887,10 +907,12 @@ fn deep_copy_type_vars<C: CopyEnv>(
#[cfg(test)]
mod test {
use crate::copy::{deep_copy_type_vars_into_expr, AcrossSubs};
use crate::{
copy::{deep_copy_type_vars_into_expr, AcrossSubs},
expr::Expr,
};
use super::{deep_copy_expr_across_subs, deep_copy_type_vars};
use roc_can::expr::Expr;
use roc_error_macros::internal_error;
use roc_module::{ident::TagName, symbol::Symbol};
use roc_region::all::Loc;
@ -1105,8 +1127,7 @@ mod test {
)],
};
let (var, expr) =
deep_copy_expr_across_subs(&mut source, &mut target, var1, &expr).unwrap();
let (var, expr) = deep_copy_expr_across_subs(&mut source, &mut target, var1, &expr);
assert!(source.get_copy(var1).is_none());
assert!(source.get_copy(var2).is_none());

View file

@ -471,6 +471,15 @@ pub enum Recursive {
TailRecursive = 2,
}
impl Recursive {
pub fn is_recursive(&self) -> bool {
match self {
Recursive::NotRecursive => false,
Recursive::Recursive | Recursive::TailRecursive => true,
}
}
}
#[derive(Clone, Debug)]
pub struct WhenBranch {
pub patterns: Vec<Loc<Pattern>>,

View file

@ -5,6 +5,7 @@ pub mod abilities;
pub mod annotation;
pub mod builtins;
pub mod constraint;
pub mod copy;
pub mod def;
pub mod effect_module;
pub mod env;

View file

@ -20,7 +20,7 @@ use roc_types::subs::{
};
use roc_types::types::{AliasKind, RecordField};
use crate::{synth_var, DerivedBody, DERIVED_MODULE};
use crate::{synth_var, DerivedBody, DERIVED_SYNTH};
macro_rules! bad_input {
($subs:expr, $var:expr) => {
@ -64,7 +64,7 @@ impl Env<'_> {
let ident_id = self.derived_ident_ids.get_or_insert(&debug_name);
Symbol::new(DERIVED_MODULE, ident_id)
Symbol::new(DERIVED_SYNTH, ident_id)
} else {
self.unique_symbol()
}
@ -72,7 +72,7 @@ impl Env<'_> {
fn unique_symbol(&mut self) -> Symbol {
let ident_id = self.derived_ident_ids.gen_unique();
Symbol::new(DERIVED_MODULE, ident_id)
Symbol::new(DERIVED_SYNTH, ident_id)
}
fn import_encode_symbol(&mut self, symbol: Symbol) -> Variable {

View file

@ -3,6 +3,7 @@
use std::iter::once;
use std::sync::{Arc, Mutex};
use roc_can::abilities::SpecializationLambdaSets;
use roc_can::expr::Expr;
use roc_can::pattern::Pattern;
use roc_can::{def::Def, module::ExposedByModule};
@ -16,9 +17,7 @@ use roc_types::subs::{
mod encoding;
type SpecializationLambdaSets = VecMap<u8, Variable>;
pub(crate) const DERIVED_MODULE: ModuleId = ModuleId::DERIVED;
pub(crate) const DERIVED_SYNTH: ModuleId = ModuleId::DERIVED_SYNTH;
pub fn synth_var(subs: &mut Subs, content: Content) -> Variable {
let descriptor = Descriptor {
@ -34,20 +33,13 @@ pub fn synth_var(subs: &mut Subs, content: Content) -> Variable {
}
/// Map of [`DeriveKey`]s to their derived symbols.
///
/// This represents the [`Derived_synth`][Symbol::DERIVED_SYNTH] module.
#[derive(Debug, Default)]
pub struct DerivedModule {
map: MutMap<DeriveKey, (Symbol, Def, SpecializationLambdaSets)>,
subs: Subs,
derived_ident_ids: IdentIds,
/// Has someone stolen subs/ident ids from us?
#[cfg(debug_assertions)]
stolen: bool,
}
pub struct StolenFromDerived {
pub subs: Subs,
pub ident_ids: IdentIds,
}
pub(crate) struct DerivedBody {
@ -98,51 +90,45 @@ impl DerivedModule {
exposed_by_module: &ExposedByModule,
key: DeriveKey,
) -> &(Symbol, Def, SpecializationLambdaSets) {
#[cfg(debug_assertions)]
{
debug_assert!(!self.stolen, "attempting to add to stolen symbols!");
match self.map.get(&key) {
Some(entry) => {
// rustc won't let us return an immutable reference *and* continue using
// `self.map` immutably below, but this is safe, because we are not returning
// an immutable reference to the entry.
return unsafe { std::mem::transmute(entry) };
}
None => {}
}
// TODO: can we get rid of the clone?
let entry = self.map.entry(key.clone());
let ident_id = if cfg!(debug_assertions) || cfg!(feature = "debug-derived-symbols") {
let debug_name = key.debug_name();
let ident_id = self.derived_ident_ids.get_or_insert(&debug_name);
entry.or_insert_with(|| {
let ident_id = if cfg!(any(
debug_assertions,
test,
feature = "debug-derived-symbols"
)) {
let debug_name = key.debug_name();
debug_assert!(
self.derived_ident_ids.get_id(&debug_name).is_none(),
"duplicate debug name for different derive key"
);
self.derived_ident_ids.get_or_insert(&debug_name)
} else {
self.derived_ident_ids.gen_unique()
};
// This is expensive, but yields much better symbols when debugging.
// TODO: hide behind debug_flags?
DERIVED_SYNTH.register_debug_idents(&self.derived_ident_ids);
let derived_symbol = Symbol::new(DERIVED_MODULE, ident_id);
let (derived_def, specialization_lsets) = build_derived_body(
&mut self.subs,
&mut self.derived_ident_ids,
exposed_by_module,
derived_symbol,
key.clone(),
);
ident_id
} else {
self.derived_ident_ids.gen_unique()
};
(derived_symbol, derived_def, specialization_lsets)
})
let derived_symbol = Symbol::new(DERIVED_SYNTH, ident_id);
let (derived_def, specialization_lsets) = build_derived_body(
&mut self.subs,
&mut self.derived_ident_ids,
exposed_by_module,
derived_symbol,
key.clone(),
);
let triple = (derived_symbol, derived_def, specialization_lsets);
self.map.entry(key).or_insert(triple)
}
pub fn iter_all(
&self,
) -> impl Iterator<Item = (&DeriveKey, &(Symbol, Def, SpecializationLambdaSets))> {
#[cfg(debug_assertions)]
{
debug_assert!(!self.stolen);
}
self.map.iter()
}
@ -150,57 +136,25 @@ impl DerivedModule {
/// module; other modules should use [`Self::get_or_insert`] to generate a symbol for a derived
/// ability member usage.
pub fn gen_unique(&mut self) -> Symbol {
#[cfg(debug_assertions)]
{
debug_assert!(!self.stolen);
}
let ident_id = self.derived_ident_ids.gen_unique();
Symbol::new(DERIVED_MODULE, ident_id)
Symbol::new(DERIVED_SYNTH, ident_id)
}
/// Steal all created derived ident Ids.
/// After this is called, [`Self::get_or_insert`] may no longer be called.
pub fn steal(&mut self) -> StolenFromDerived {
let mut ident_ids = Default::default();
std::mem::swap(&mut self.derived_ident_ids, &mut ident_ids);
let mut subs = Default::default();
std::mem::swap(&mut self.subs, &mut subs);
#[cfg(debug_assertions)]
{
debug_assert!(!self.stolen);
self.stolen = true;
}
StolenFromDerived { subs, ident_ids }
}
pub fn return_stolen(&mut self, stolen: StolenFromDerived) {
#[cfg(debug_assertions)]
{
debug_assert!(self.stolen);
self.stolen = false;
}
let StolenFromDerived { subs, ident_ids } = stolen;
self.subs = subs;
self.derived_ident_ids = ident_ids;
}
pub fn copy_lambda_set_var_to_subs(&self, var: Variable, target: &mut Subs) -> Variable {
#[cfg(debug_assertions)]
{
debug_assert!(!self.stolen);
}
// TODO: just pass and copy the ambient function directly, don't pass the lambda set var.
pub fn copy_lambda_set_ambient_function_to_subs(
&self,
lambda_set_var: Variable,
target: &mut Subs,
_target_rank: Rank,
) -> Variable {
let ambient_function_var = self.subs.get_lambda_set(lambda_set_var).ambient_function;
let copied_import = copy_import_to(
&self.subs,
target,
// bookkeep unspecialized lambda sets of var - I think don't want this here
false,
var,
// bookkeep unspecialized lambda sets of var - I think we want this here
true,
ambient_function_var,
// TODO: I think this is okay because the only use of `copy_lambda_set_var_to_subs`
// (at least right now) is for lambda set compaction, which will automatically unify
// and lower ranks, and never generalize.
@ -208,11 +162,52 @@ impl DerivedModule {
// However this is a bad coupling and maybe not a good assumption, we should revisit
// this when possible.
Rank::import(),
// target_rank,
);
copied_import.variable
}
/// Gets the derived defs that should be loaded into the derived gen module, skipping over the
/// defs that have already been loaded.
pub fn iter_load_for_gen_module(
&mut self,
gen_subs: &mut Subs,
should_load_def: impl Fn(Symbol) -> bool,
) -> VecMap<Symbol, Expr> {
self.map
.values()
.filter_map(|(symbol, def, _)| {
if should_load_def(*symbol) {
let (_new_expr_var, new_expr) = roc_can::copy::deep_copy_expr_across_subs(
&mut self.subs,
gen_subs,
def.expr_var,
&def.loc_expr.value,
);
Some((*symbol, new_expr))
} else {
None
}
})
.collect()
}
/// # Safety
///
/// Prefer using a fresh Derived module with [`Derived::default`]. Use this only in testing.
pub unsafe fn from_components(subs: Subs, ident_ids: IdentIds) -> Self {
Self {
map: Default::default(),
subs,
derived_ident_ids: ident_ids,
}
}
pub fn decompose(self) -> (Subs, IdentIds) {
(self.subs, self.derived_ident_ids)
}
}
/// Thread-sharable [`DerivedMethods`].
/// Thread-sharable [`DerivedModule`].
pub type SharedDerivedModule = Arc<Mutex<DerivedModule>>;

View file

@ -11,7 +11,4 @@ roc_error_macros = { path = "../../error_macros" }
roc_region = { path = "../region" }
roc_module = { path = "../module" }
roc_types = { path = "../types" }
[features]
default = []
debug-derived-symbols = []
roc_can = { path = "../can" }

View file

@ -15,12 +15,9 @@
pub mod encoding;
use std::sync::{Arc, Mutex};
use encoding::{FlatEncodable, FlatEncodableKey};
use roc_collections::MutMap;
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
use roc_module::symbol::Symbol;
use roc_types::subs::{Subs, Variable};
#[derive(Debug, PartialEq)]
@ -68,54 +65,3 @@ impl Derived {
}
}
}
/// Map of [`DeriveKey`]s to their derived symbols.
#[derive(Debug, Default)]
pub struct DerivedSymbols {
map: MutMap<DeriveKey, Symbol>,
derived_ident_ids: IdentIds,
#[cfg(debug_assertions)]
stolen: bool,
}
impl DerivedSymbols {
pub fn get_or_insert(&mut self, key: DeriveKey) -> Symbol {
#[cfg(debug_assertions)]
{
debug_assert!(!self.stolen, "attempting to add to stolen symbols!");
}
let symbol = self.map.entry(key).or_insert_with_key(|key| {
let ident_id = if cfg!(debug_assertions) || cfg!(feature = "debug-derived-symbols") {
let debug_name = key.debug_name();
debug_assert!(
self.derived_ident_ids.get_id(&debug_name).is_none(),
"duplicate debug name for different derive key"
);
self.derived_ident_ids.get_or_insert(&debug_name)
} else {
self.derived_ident_ids.gen_unique()
};
Symbol::new(ModuleId::DERIVED, ident_id)
});
*symbol
}
/// Steal all created derived ident Ids.
/// After this is called, [`Self::get_or_insert`] may no longer be called.
pub fn steal(&mut self) -> IdentIds {
let mut ident_ids = Default::default();
std::mem::swap(&mut self.derived_ident_ids, &mut ident_ids);
#[cfg(debug_assertions)]
{
self.stolen = true;
}
ident_ids
}
}
/// Thread-sharable [`DerivedMethods`].
pub type GlobalDerivedSymbols = Arc<Mutex<DerivedSymbols>>;

View file

@ -8,7 +8,7 @@ use crate::llvm::build_list::{
list_prepend, list_replace_unsafe, list_reserve, list_sort_with, list_sublist, list_swap,
list_symbol_to_c_abi, list_to_c_abi, list_with_capacity, pass_update_mode,
};
use crate::llvm::build_str::{str_from_float, str_from_int};
use crate::llvm::build_str::{dec_to_str, str_from_float, str_from_int};
use crate::llvm::compare::{generic_eq, generic_neq};
use crate::llvm::convert::{
self, argument_type_from_layout, basic_type_from_builtin, basic_type_from_layout,
@ -5434,7 +5434,14 @@ fn run_low_level<'a, 'ctx, 'env>(
// Str.fromFloat : Float * -> Str
debug_assert_eq!(args.len(), 1);
str_from_float(env, scope, args[0])
let (float, float_layout) = load_symbol_and_layout(scope, &args[0]);
let float_width = match float_layout {
Layout::Builtin(Builtin::Float(float_width)) => *float_width,
_ => unreachable!(),
};
str_from_float(env, float, float_width)
}
StrFromUtf8Range => {
debug_assert_eq!(args.len(), 3);
@ -5768,9 +5775,10 @@ fn run_low_level<'a, 'ctx, 'env>(
str_from_int(env, int, *int_width)
}
Layout::Builtin(Builtin::Float(_float_width)) => {
str_from_float(env, scope, args[0])
Layout::Builtin(Builtin::Float(float_width)) => {
str_from_float(env, num, *float_width)
}
Layout::Builtin(Builtin::Decimal) => dec_to_str(env, num),
_ => unreachable!(),
}
}

View file

@ -3,7 +3,7 @@ use crate::llvm::build::{Env, Scope};
use inkwell::builder::Builder;
use inkwell::values::{BasicValueEnum, IntValue, PointerValue, StructValue};
use inkwell::AddressSpace;
use roc_builtins::bitcode::{self, IntWidth};
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout};
use roc_target::PtrWidth;
@ -103,15 +103,37 @@ pub fn decode_from_utf8_result<'a, 'ctx, 'env>(
}
}
/// Str.fromFloat : Int -> Str
/// Str.fromFloat : Float * -> Str
pub fn str_from_float<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
int_symbol: Symbol,
float: BasicValueEnum<'ctx>,
float_width: FloatWidth,
) -> BasicValueEnum<'ctx> {
let float = load_symbol(scope, &int_symbol);
call_str_bitcode_fn(env, &[float], &bitcode::STR_FROM_FLOAT[float_width])
}
call_str_bitcode_fn(env, &[float], bitcode::STR_FROM_FLOAT)
/// Dec.toStr : Dec -> Str
pub fn dec_to_str<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
dec: BasicValueEnum<'ctx>,
) -> BasicValueEnum<'ctx> {
let dec = dec.into_int_value();
let int_64 = env.context.i128_type().const_int(64, false);
let int_64_type = env.context.i64_type();
let dec_right_shift = env
.builder
.build_right_shift(dec, int_64, false, "dec_left_bits");
let right_bits = env.builder.build_int_cast(dec, int_64_type, "");
let left_bits = env.builder.build_int_cast(dec_right_shift, int_64_type, "");
call_str_bitcode_fn(
env,
&[right_bits.into(), left_bits.into()],
bitcode::DEC_TO_STR,
)
}
/// Str.equal : Str, Str -> Bool

View file

@ -1983,15 +1983,15 @@ impl<'a> LowLevelCall<'a> {
FloatWidth::F32 => {
self.load_args(backend);
backend.code_builder.f64_promote_f32();
self.load_args_and_call_zig(backend, bitcode::STR_FROM_FLOAT);
self.load_args_and_call_zig(backend, &bitcode::STR_FROM_FLOAT[width]);
}
FloatWidth::F64 => {
self.load_args_and_call_zig(backend, bitcode::STR_FROM_FLOAT);
self.load_args_and_call_zig(backend, &bitcode::STR_FROM_FLOAT[width]);
}
FloatWidth::F128 => todo!("F128 to Str"),
},
Layout::Builtin(Builtin::Decimal) => {
todo!("Decimal to Str")
self.load_args_and_call_zig(backend, bitcode::DEC_TO_STR)
}
x => internal_error!("NumToStr is not defined for {:?}", x),
}

View file

@ -8,9 +8,10 @@ edition = "2021"
[dependencies]
roc_types = { path = "../types" }
roc_can = { path = "../can" }
roc_derive_key = { path = "../derive_key" }
roc_derive = { path = "../derive" }
roc_module = { path = "../module" }
roc_unify = { path = "../unify" }
roc_solve = { path = "../solve" }
roc_collections = { path = "../collections" }
roc_error_macros = { path = "../../error_macros" }
bumpalo = { version = "3.8.0", features = ["collections"] }

View file

@ -5,8 +5,10 @@ use std::sync::{Arc, RwLock};
use bumpalo::Bump;
use roc_can::abilities::AbilitiesStore;
use roc_can::module::ExposedByModule;
use roc_collections::MutMap;
use roc_derive_key::GlobalDerivedSymbols;
use roc_derive::SharedDerivedModule;
use roc_error_macros::internal_error;
use roc_module::symbol::ModuleId;
use roc_solve::solve::{compact_lambda_sets_of_vars, Phase, Pools};
use roc_types::subs::{Content, FlatType, LambdaSet};
@ -45,6 +47,30 @@ impl WorldAbilities {
debug_assert!(old_store.is_none(), "{:?} abilities not new", module);
}
#[inline(always)]
pub fn with_module_exposed_type<T>(
&mut self,
module: ModuleId,
mut f: impl FnMut(&mut ExposedTypesStorageSubs) -> T,
) -> T {
let mut world = self.world.write().unwrap();
let (_, exposed_types) = world.get_mut(&module).expect("module not in the world");
f(exposed_types)
}
#[inline(always)]
pub fn with_module_abilities_store<T, F>(&self, module: ModuleId, mut f: F) -> T
where
F: FnMut(&AbilitiesStore) -> T,
{
let world = self.world.read().unwrap();
let (module_store, _module_types) = world
.get(&module)
.unwrap_or_else(|| internal_error!("Module {:?} not found in world abilities", module));
f(module_store)
}
pub fn clone_ref(&self) -> Self {
Self {
world: Arc::clone(&self.world),
@ -53,7 +79,7 @@ impl WorldAbilities {
}
pub enum AbilitiesView<'a> {
World(WorldAbilities),
World(&'a WorldAbilities),
Module(&'a AbilitiesStore),
}
@ -64,11 +90,7 @@ impl AbilitiesView<'_> {
F: FnMut(&AbilitiesStore) -> T,
{
match self {
AbilitiesView::World(wa) => {
let world = wa.world.read().unwrap();
let (module_store, _module_types) = world.get(&module).unwrap();
f(module_store)
}
AbilitiesView::World(wa) => wa.with_module_abilities_store(module, f),
AbilitiesView::Module(store) => f(store),
}
}
@ -142,16 +164,24 @@ impl Phase for LatePhase<'_> {
/// Unifies two variables and performs lambda set compaction.
/// Ranks and other ability demands are disregarded.
#[allow(clippy::too_many_arguments)]
pub fn unify(
home: ModuleId,
arena: &Bump,
subs: &mut Subs,
abilities: &AbilitiesView,
derived_symbols: &GlobalDerivedSymbols,
derived_module: &SharedDerivedModule,
exposed_by_module: &ExposedByModule,
left: Variable,
right: Variable,
) -> Result<(), UnificationFailed> {
debug_assert_ne!(
home,
ModuleId::DERIVED_SYNTH,
"derived module can only unify its subs in its own context!"
);
let unified = unify_unify(subs, left, right, Mode::EQ);
match unified {
Unified::Success {
vars: _,
@ -165,11 +195,12 @@ pub fn unify(
let must_implement_constraints = compact_lambda_sets_of_vars(
subs,
derived_module,
arena,
&mut pools,
lambda_sets_to_specialize,
&late_phase,
derived_symbols,
exposed_by_module,
);
// At this point we can't do anything with must-implement constraints, since we're no
// longer solving. We must assume that they were totally caught during solving.

View file

@ -92,7 +92,6 @@ pub fn load_and_monomorphize_from_str<'a>(
pub fn load_and_monomorphize(
arena: &Bump,
filename: PathBuf,
src_dir: PathBuf,
exposed_types: ExposedByModule,
target_info: TargetInfo,
render: RenderTarget,
@ -100,7 +99,7 @@ pub fn load_and_monomorphize(
) -> Result<MonomorphizedModule<'_>, LoadingProblem<'_>> {
use LoadResult::*;
let load_start = LoadStart::from_path(arena, src_dir, filename, render)?;
let load_start = LoadStart::from_path(arena, filename, render)?;
match load(
arena,
@ -119,7 +118,6 @@ pub fn load_and_monomorphize(
pub fn load_and_typecheck(
arena: &Bump,
filename: PathBuf,
src_dir: PathBuf,
exposed_types: ExposedByModule,
target_info: TargetInfo,
render: RenderTarget,
@ -127,7 +125,7 @@ pub fn load_and_typecheck(
) -> Result<LoadedModule, LoadingProblem<'_>> {
use LoadResult::*;
let load_start = LoadStart::from_path(arena, src_dir, filename, render)?;
let load_start = LoadStart::from_path(arena, filename, render)?;
match load(
arena,

View file

@ -14,6 +14,7 @@ roc_types = { path = "../types" }
roc_can = { path = "../can" }
roc_constrain = { path = "../constrain" }
roc_derive_key = { path = "../derive_key" }
roc_derive = { path = "../derive" }
roc_builtins = { path = "../builtins" }
roc_problem = { path = "../problem" }
roc_unify = { path = "../unify" }

View file

@ -20,7 +20,7 @@ use roc_debug_flags::{
ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION,
ROC_PRINT_LOAD_LOG,
};
use roc_derive_key::GlobalDerivedSymbols;
use roc_derive::SharedDerivedModule;
use roc_error_macros::internal_error;
use roc_late_solve::{AbilitiesView, WorldAbilities};
use roc_module::ident::{Ident, ModuleName, QualifiedModuleName};
@ -370,7 +370,7 @@ fn start_phase<'a>(
..
} = constrained;
let derived_symbols = GlobalDerivedSymbols::clone(&state.derived_symbols);
let derived_module = SharedDerivedModule::clone(&state.derived_module);
BuildTask::solve_module(
module,
@ -385,7 +385,7 @@ fn start_phase<'a>(
dep_idents,
declarations,
state.cached_subs.clone(),
derived_symbols,
derived_module,
)
}
Phase::FindSpecializations => {
@ -413,7 +413,7 @@ fn start_phase<'a>(
}
}
let derived_symbols = GlobalDerivedSymbols::clone(&state.derived_symbols);
let derived_module = SharedDerivedModule::clone(&state.derived_module);
BuildTask::BuildPendingSpecializations {
layout_cache,
@ -425,18 +425,44 @@ fn start_phase<'a>(
ident_ids,
exposed_to_host: state.exposed_to_host.clone(),
abilities_store,
derived_symbols,
// TODO: awful, how can we get rid of the clone?
exposed_by_module: state.exposed_types.clone(),
derived_module,
}
}
Phase::MakeSpecializations => {
let specializations_we_must_make = state
let mut specializations_we_must_make = state
.module_cache
.external_specializations_requested
.remove(&module_id)
.unwrap_or_default();
let (ident_ids, subs, procs_base, layout_cache, module_timing) =
if state.make_specializations_pass.current_pass() == 1 {
if module_id == ModuleId::DERIVED_GEN {
// The derived gen module must also fulfill also specializations asked of the
// derived synth module.
let derived_synth_specializations = state
.module_cache
.external_specializations_requested
.remove(&ModuleId::DERIVED_SYNTH)
.unwrap_or_default();
specializations_we_must_make.extend(derived_synth_specializations)
}
let (mut ident_ids, mut subs, mut procs_base, layout_cache, mut module_timing) =
if state.make_specializations_pass.current_pass() == 1
&& module_id == ModuleId::DERIVED_GEN
{
// This is the first time the derived module is introduced into the load
// graph. It has no abilities of its own or anything, just generate fresh
// information for it.
(
IdentIds::default(),
Subs::default(),
ProcsBase::default(),
LayoutCache::new(state.target_info),
ModuleTiming::new(SystemTime::now()),
)
} else if state.make_specializations_pass.current_pass() == 1 {
let found_specializations = state
.module_cache
.found_specializations
@ -452,12 +478,13 @@ fn start_phase<'a>(
abilities_store,
} = found_specializations;
// Safety: by this point every module should have been solved, so there is no need
// for our exposed types anymore, but the world does need them.
let our_exposed_types = unsafe { state.exposed_types.remove(&module_id) }
let our_exposed_types = state
.exposed_types
.get(&module_id)
.unwrap_or_else(|| {
internal_error!("Exposed types for {:?} missing", module_id)
});
})
.clone();
// Add our abilities to the world.
state.world_abilities.insert(
@ -483,7 +510,22 @@ fn start_phase<'a>(
(ident_ids, subs, procs_base, layout_cache, module_timing)
};
let derived_symbols = GlobalDerivedSymbols::clone(&state.derived_symbols);
if module_id == ModuleId::DERIVED_GEN {
load_derived_partial_procs(
module_id,
arena,
&mut subs,
&mut ident_ids,
&state.derived_module,
&mut module_timing,
state.target_info,
&state.exposed_types,
&mut procs_base,
&mut state.world_abilities,
);
}
let derived_module = SharedDerivedModule::clone(&state.derived_module);
BuildTask::MakeSpecializations {
module_id,
@ -494,7 +536,9 @@ fn start_phase<'a>(
specializations_we_must_make,
module_timing,
world_abilities: state.world_abilities.clone_ref(),
derived_symbols,
// TODO: awful, how can we get rid of the clone?
exposed_by_module: state.exposed_types.clone(),
derived_module,
}
}
}
@ -808,7 +852,7 @@ struct State<'a> {
pub arc_modules: Arc<Mutex<PackageModuleIds<'a>>>,
pub arc_shorthands: Arc<Mutex<MutMap<&'a str, PackageName<'a>>>>,
#[allow(unused)]
pub derived_symbols: GlobalDerivedSymbols,
pub derived_module: SharedDerivedModule,
pub ident_ids_by_module: SharedIdentIdsByModule,
@ -853,6 +897,8 @@ impl<'a> State<'a> {
) -> Self {
let arc_shorthands = Arc::new(Mutex::new(MutMap::default()));
let dependencies = Dependencies::new(goal_phase);
Self {
root_id,
root_subs: None,
@ -862,14 +908,14 @@ impl<'a> State<'a> {
output_path: None,
platform_path: PlatformPath::NotSpecified,
module_cache: ModuleCache::default(),
dependencies: Dependencies::default(),
dependencies,
procedures: MutMap::default(),
toplevel_expects: Vec::new(),
exposed_to_host: ExposedToHost::default(),
exposed_types,
arc_modules,
arc_shorthands,
derived_symbols: Default::default(),
derived_module: Default::default(),
constrained_ident_ids: IdentIds::exposed_builtins(0),
ident_ids_by_module,
declarations_by_id: MutMap::default(),
@ -988,7 +1034,7 @@ enum BuildTask<'a> {
declarations: Declarations,
dep_idents: IdentIdsByModule,
cached_subs: CachedSubs,
derived_symbols: GlobalDerivedSymbols,
derived_module: SharedDerivedModule,
},
BuildPendingSpecializations {
module_timing: ModuleTiming,
@ -999,8 +1045,9 @@ enum BuildTask<'a> {
ident_ids: IdentIds,
decls: Declarations,
exposed_to_host: ExposedToHost,
exposed_by_module: ExposedByModule,
abilities_store: AbilitiesStore,
derived_symbols: GlobalDerivedSymbols,
derived_module: SharedDerivedModule,
},
MakeSpecializations {
module_id: ModuleId,
@ -1010,8 +1057,9 @@ enum BuildTask<'a> {
layout_cache: LayoutCache<'a>,
specializations_we_must_make: Vec<ExternalSpecializations<'a>>,
module_timing: ModuleTiming,
exposed_by_module: ExposedByModule,
world_abilities: WorldAbilities,
derived_symbols: GlobalDerivedSymbols,
derived_module: SharedDerivedModule,
},
}
@ -1114,13 +1162,13 @@ pub struct LoadStart<'a> {
impl<'a> LoadStart<'a> {
pub fn from_path(
arena: &'a Bump,
mut src_dir: PathBuf,
filename: PathBuf,
render: RenderTarget,
) -> Result<Self, LoadingProblem<'a>> {
let arc_modules = Arc::new(Mutex::new(PackageModuleIds::default()));
let root_exposed_ident_ids = IdentIds::exposed_builtins(0);
let ident_ids_by_module = Arc::new(Mutex::new(root_exposed_ident_ids));
let mut src_dir = filename.parent().unwrap().to_path_buf();
// Load the root module synchronously; we can't proceed until we have its id.
let (root_id, root_msg) = {
@ -2637,7 +2685,16 @@ fn finish_specialization(
.into_inner()
.into_module_ids();
let all_ident_ids = state.constrained_ident_ids;
let mut all_ident_ids = state.constrained_ident_ids;
// Associate the ident IDs from the derived synth module
let (_, derived_synth_ident_ids) = Arc::try_unwrap(state.derived_module)
.unwrap_or_else(|_| internal_error!("Outstanding references to the derived module"))
.into_inner()
.unwrap()
.decompose();
ModuleId::DERIVED_SYNTH.register_debug_idents(&derived_synth_ident_ids);
all_ident_ids.insert(ModuleId::DERIVED_SYNTH, derived_synth_ident_ids);
let interns = Interns {
module_ids,
@ -2755,12 +2812,16 @@ fn finish(
.into_inner()
.into_module_ids();
// Steal the derived symbols and put them in the global ident ids
let derived_ident_ids = state.derived_symbols.lock().unwrap().steal();
ModuleId::DERIVED.register_debug_idents(&derived_ident_ids);
// Associate the ident IDs from the derived synth module
let (_, derived_synth_ident_ids) = Arc::try_unwrap(state.derived_module)
.unwrap_or_else(|_| internal_error!("Outstanding references to the derived module"))
.into_inner()
.unwrap()
.decompose();
ModuleId::DERIVED_SYNTH.register_debug_idents(&derived_synth_ident_ids);
state
.constrained_ident_ids
.insert(ModuleId::DERIVED, derived_ident_ids);
.insert(ModuleId::DERIVED_SYNTH, derived_synth_ident_ids);
let interns = Interns {
module_ids,
@ -2847,7 +2908,7 @@ fn load_platform_module<'a>(
// make a `platform` module that ultimately exposes `main` to the host
let platform_module_msg = fabricate_platform_module(
arena,
shorthand,
Some(shorthand),
Some(app_module_id),
filename.to_path_buf(),
parser_state,
@ -3245,19 +3306,17 @@ fn parse_header<'a>(
To::NewPackage(_package_name) => Ok((module_id, app_module_header_msg)),
}
}
Ok((ast::Module::Platform { header }, parse_state)) => {
Ok(fabricate_platform_module(
arena,
"", // Use a shorthand of "" - it will be fine for `roc check` and bindgen
None,
filename,
parse_state,
module_ids.clone(),
ident_ids_by_module,
&header,
module_timing,
))
}
Ok((ast::Module::Platform { header }, parse_state)) => Ok(fabricate_platform_module(
arena,
None,
None,
filename,
parse_state,
module_ids.clone(),
ident_ids_by_module,
&header,
module_timing,
)),
Err(fail) => Err(LoadingProblem::ParsingFailed(
fail.map_problem(SyntaxError::Header)
@ -3542,7 +3601,7 @@ fn send_header<'a>(
struct PlatformHeaderInfo<'a> {
filename: PathBuf,
is_root_module: bool,
shorthand: &'a str,
opt_shorthand: Option<&'a str>,
opt_app_module_id: Option<ModuleId>,
packages: &'a [Loc<PackageEntry<'a>>],
provides: &'a [Loc<ExposedName<'a>>],
@ -3562,7 +3621,7 @@ fn send_header_two<'a>(
) -> (ModuleId, Msg<'a>) {
let PlatformHeaderInfo {
filename,
shorthand,
opt_shorthand,
is_root_module,
opt_app_module_id,
packages,
@ -3617,7 +3676,10 @@ fn send_header_two<'a>(
let mut module_ids = (*module_ids).lock();
let mut ident_ids_by_module = (*ident_ids_by_module).lock();
let name = PQModuleName::Qualified(shorthand, declared_name);
let name = match opt_shorthand {
Some(shorthand) => PQModuleName::Qualified(shorthand, declared_name),
None => PQModuleName::Unqualified(declared_name),
};
home = module_ids.get_or_insert(&name);
// Ensure this module has an entry in the exposed_ident_ids map.
@ -3631,8 +3693,13 @@ fn send_header_two<'a>(
for (qualified_module_name, exposed_idents, region) in imported.into_iter() {
let cloned_module_name = qualified_module_name.module.clone();
let pq_module_name = match qualified_module_name.opt_package {
None => PQModuleName::Qualified(shorthand, qualified_module_name.module),
Some(package) => PQModuleName::Qualified(package, cloned_module_name.clone()),
None => match opt_shorthand {
Some(shorthand) => {
PQModuleName::Qualified(shorthand, qualified_module_name.module)
}
None => PQModuleName::Unqualified(qualified_module_name.module),
},
Some(package) => PQModuleName::Qualified(package, cloned_module_name),
};
let module_id = module_ids.get_or_insert(&pq_module_name);
@ -3741,7 +3808,8 @@ fn send_header_two<'a>(
};
let extra = HeaderFor::Platform {
config_shorthand: shorthand,
// A config_shorthand of "" should be fine
config_shorthand: opt_shorthand.unwrap_or_default(),
platform_main_type: requires[0].value,
main_for_host,
};
@ -3798,7 +3866,7 @@ impl<'a> BuildTask<'a> {
dep_idents: IdentIdsByModule,
declarations: Declarations,
cached_subs: CachedSubs,
derived_symbols: GlobalDerivedSymbols,
derived_module: SharedDerivedModule,
) -> Self {
let exposed_by_module = exposed_types.retain_modules(imported_modules.keys());
@ -3818,7 +3886,7 @@ impl<'a> BuildTask<'a> {
dep_idents,
module_timing,
cached_subs,
derived_symbols,
derived_module,
}
}
}
@ -3866,7 +3934,7 @@ pub fn add_imports(
my_module: ModuleId,
subs: &mut Subs,
mut pending_abilities: PendingAbilitiesStore,
mut exposed_for_module: ExposedForModule,
exposed_for_module: &ExposedForModule,
def_types: &mut Vec<(Symbol, Loc<roc_types::types::Type>)>,
rigid_vars: &mut Vec<Variable>,
) -> (Vec<Variable>, AbilitiesStore) {
@ -3879,7 +3947,7 @@ pub fn add_imports(
macro_rules! import_var_for_symbol {
($subs:expr, $exposed_by_module:expr, $symbol:ident, $break:stmt) => {
let module_id = $symbol.module_id();
match $exposed_by_module.get_mut(&module_id) {
match $exposed_by_module.get(&module_id) {
Some(ExposedModuleTypes {
exposed_types_storage_subs: exposed_types,
resolved_specializations: _,
@ -3922,7 +3990,7 @@ pub fn add_imports(
}
}
for symbol in exposed_for_module.imported_values {
for &symbol in &exposed_for_module.imported_values {
import_var_for_symbol!(subs, exposed_for_module.exposed_by_module, symbol, continue);
}
@ -3948,14 +4016,14 @@ pub fn add_imports(
struct Ctx<'a> {
subs: &'a mut Subs,
exposed_by_module: &'a mut ExposedByModule,
exposed_by_module: &'a ExposedByModule,
}
let abilities_store = pending_abilities.resolve_for_module(
my_module,
&mut Ctx {
subs,
exposed_by_module: &mut exposed_for_module.exposed_by_module,
exposed_by_module: &exposed_for_module.exposed_by_module,
},
|ctx, symbol| match cached_symbol_vars.get(&symbol).copied() {
Some(var) => var,
@ -3969,7 +4037,7 @@ pub fn add_imports(
*cached_symbol_vars.get(&symbol).unwrap()
}
},
|ctx, module, lset_var| match ctx.exposed_by_module.get_mut(&module) {
|ctx, module, lset_var| match ctx.exposed_by_module.get(&module) {
Some(ExposedModuleTypes {
exposed_types_storage_subs: exposed_types,
resolved_specializations: _,
@ -4000,7 +4068,7 @@ fn run_solve_solve(
pending_derives: PendingDerives,
var_store: VarStore,
module: Module,
derived_symbols: GlobalDerivedSymbols,
derived_module: SharedDerivedModule,
) -> (
Solved<Subs>,
ResolvedSpecializations,
@ -4025,7 +4093,7 @@ fn run_solve_solve(
module.module_id,
&mut subs,
pending_abilities,
exposed_for_module,
&exposed_for_module,
&mut def_types,
&mut rigid_vars,
);
@ -4040,7 +4108,10 @@ fn run_solve_solve(
}
let (solved_subs, solved_specializations, exposed_vars_by_symbol, problems, abilities_store) = {
let module_id = module.module_id;
let (solved_subs, solved_env, problems, abilities_store) = roc_solve::module::run_solve(
module_id,
&constraints,
actual_constraint,
rigid_variables,
@ -4048,10 +4119,10 @@ fn run_solve_solve(
solve_aliases,
abilities_store,
pending_derives,
derived_symbols,
&exposed_for_module.exposed_by_module,
derived_module,
);
let module_id = module.module_id;
// Figure out what specializations belong to this module
let solved_specializations: ResolvedSpecializations = abilities_store
.iter_specializations()
@ -4104,7 +4175,7 @@ fn run_solve<'a>(
decls: Declarations,
dep_idents: IdentIdsByModule,
cached_subs: CachedSubs,
derived_symbols: GlobalDerivedSymbols,
derived_module: SharedDerivedModule,
) -> Msg<'a> {
let solve_start = SystemTime::now();
@ -4123,7 +4194,7 @@ fn run_solve<'a>(
pending_derives,
var_store,
module,
derived_symbols,
derived_module,
),
Some((subs, exposed_vars_by_symbol)) => {
(
@ -4145,7 +4216,7 @@ fn run_solve<'a>(
pending_derives,
var_store,
module,
derived_symbols,
derived_module,
)
}
};
@ -4195,7 +4266,7 @@ fn unspace<'a, T: Copy>(arena: &'a Bump, items: &[Loc<Spaced<'a, T>>]) -> &'a [L
#[allow(clippy::too_many_arguments)]
fn fabricate_platform_module<'a>(
arena: &'a Bump,
shorthand: &'a str,
opt_shorthand: Option<&'a str>,
opt_app_module_id: Option<ModuleId>,
filename: PathBuf,
parse_state: roc_parse::state::State<'a>,
@ -4211,7 +4282,7 @@ fn fabricate_platform_module<'a>(
let info = PlatformHeaderInfo {
filename,
is_root_module,
shorthand,
opt_shorthand,
opt_app_module_id,
packages: &[],
provides: unspace(arena, header.provides.items),
@ -4507,7 +4578,8 @@ fn make_specializations<'a>(
mut module_timing: ModuleTiming,
target_info: TargetInfo,
world_abilities: WorldAbilities,
derived_symbols: GlobalDerivedSymbols,
exposed_by_module: &ExposedByModule,
derived_module: SharedDerivedModule,
) -> Msg<'a> {
let make_specializations_start = SystemTime::now();
let mut update_mode_ids = UpdateModeIds::new();
@ -4521,8 +4593,9 @@ fn make_specializations<'a>(
update_mode_ids: &mut update_mode_ids,
// call_specialization_counter=0 is reserved
call_specialization_counter: 1,
abilities: AbilitiesView::World(world_abilities),
derived_symbols: &derived_symbols,
abilities: AbilitiesView::World(&world_abilities),
exposed_by_module,
derived_module: &derived_module,
};
let mut procs = Procs::new_in(arena);
@ -4584,8 +4657,9 @@ fn build_pending_specializations<'a>(
mut layout_cache: LayoutCache<'a>,
target_info: TargetInfo,
exposed_to_host: ExposedToHost,
exposed_by_module: &ExposedByModule,
abilities_store: AbilitiesStore,
derived_symbols: GlobalDerivedSymbols,
derived_module: SharedDerivedModule,
) -> Msg<'a> {
let find_specializations_start = SystemTime::now();
@ -4615,7 +4689,8 @@ fn build_pending_specializations<'a>(
// to know the types and abilities in our modules. Only for building *all* specializations
// do we need a global view.
abilities: AbilitiesView::Module(&abilities_store),
derived_symbols: &derived_symbols,
exposed_by_module,
derived_module: &derived_module,
};
// Add modules' decls to Procs
@ -4913,6 +4988,100 @@ fn build_pending_specializations<'a>(
}
}
/// Loads derived ability members up for specialization into the Derived module, prior to making
/// their specializations.
// TODO: right now, this runs sequentially, and no other modules are mono'd in parallel to the
// derived module.
#[allow(clippy::too_many_arguments)]
fn load_derived_partial_procs<'a>(
home: ModuleId,
arena: &'a Bump,
subs: &mut Subs,
ident_ids: &mut IdentIds,
derived_module: &SharedDerivedModule,
module_timing: &mut ModuleTiming,
target_info: TargetInfo,
exposed_by_module: &ExposedByModule,
procs_base: &mut ProcsBase<'a>,
world_abilities: &mut WorldAbilities,
) {
debug_assert_eq!(home, ModuleId::DERIVED_GEN);
let load_derived_procs_start = SystemTime::now();
let mut new_module_thunks = bumpalo::collections::Vec::new_in(arena);
let mut update_mode_ids = UpdateModeIds::new();
let derives_to_add = {
let mut derived_module = derived_module.lock().unwrap();
derived_module.iter_load_for_gen_module(subs, |symbol| {
!procs_base.partial_procs.contains_key(&symbol)
})
};
// TODO: we can be even lazier here if we move `add_def_to_module` to happen in mono. Also, the
// timings would be more accurate.
for (derived_symbol, derived_expr) in derives_to_add.into_iter() {
let mut mono_env = roc_mono::ir::Env {
arena,
subs,
home,
ident_ids,
target_info,
update_mode_ids: &mut update_mode_ids,
// call_specialization_counter=0 is reserved
call_specialization_counter: 1,
// NB: for getting pending specializations the module view is enough because we only need
// to know the types and abilities in our modules. Only for building *all* specializations
// do we need a global view.
abilities: AbilitiesView::World(world_abilities),
exposed_by_module,
derived_module,
};
let partial_proc = match derived_expr {
roc_can::expr::Expr::Closure(roc_can::expr::ClosureData {
function_type,
arguments,
loc_body,
captured_symbols,
return_type,
recursive,
..
}) => {
debug_assert!(captured_symbols.is_empty());
PartialProc::from_named_function(
&mut mono_env,
function_type,
arguments.clone(),
*loc_body,
CapturedSymbols::None,
recursive.is_recursive(),
return_type,
)
}
_ => internal_error!("Expected only functions to be derived"),
};
procs_base
.partial_procs
.insert(derived_symbol, partial_proc);
}
if !new_module_thunks.is_empty() {
new_module_thunks.extend(procs_base.module_thunks);
procs_base.module_thunks = new_module_thunks.into_bump_slice();
}
let load_derived_procs_end = SystemTime::now();
module_timing.find_specializations = load_derived_procs_end
.duration_since(load_derived_procs_start)
.unwrap();
}
fn run_task<'a>(
task: BuildTask<'a>,
arena: &'a Bump,
@ -4972,7 +5141,7 @@ fn run_task<'a>(
declarations,
dep_idents,
cached_subs,
derived_symbols,
derived_module,
} => Ok(run_solve(
module,
ident_ids,
@ -4985,7 +5154,7 @@ fn run_task<'a>(
declarations,
dep_idents,
cached_subs,
derived_symbols,
derived_module,
)),
BuildPendingSpecializations {
module_id,
@ -4997,7 +5166,8 @@ fn run_task<'a>(
imported_module_thunks,
exposed_to_host,
abilities_store,
derived_symbols,
exposed_by_module,
derived_module,
} => Ok(build_pending_specializations(
arena,
solved_subs,
@ -5009,8 +5179,9 @@ fn run_task<'a>(
layout_cache,
target_info,
exposed_to_host,
&exposed_by_module,
abilities_store,
derived_symbols,
derived_module,
)),
MakeSpecializations {
module_id,
@ -5021,7 +5192,8 @@ fn run_task<'a>(
specializations_we_must_make,
module_timing,
world_abilities,
derived_symbols,
exposed_by_module,
derived_module,
} => Ok(make_specializations(
arena,
module_id,
@ -5033,7 +5205,8 @@ fn run_task<'a>(
module_timing,
target_info,
world_abilities,
derived_symbols,
&exposed_by_module,
derived_module,
)),
}?;

View file

@ -39,16 +39,87 @@ enum Job<'a> {
}
#[derive(Default, Debug)]
struct MakeSpecializationInfo {
/// Modules to make specializations for after they are made for this module
succ: MutSet<ModuleId>,
/// Whether this module depends on specializations being made for another module
has_pred: bool,
}
#[derive(Debug)]
struct MakeSpecializationsDependents(MutMap<ModuleId, MakeSpecializationInfo>);
impl MakeSpecializationsDependents {
/// Gets the info entry for a module, or creates a default one.
fn entry(&mut self, module_id: ModuleId) -> &mut MakeSpecializationInfo {
self.0.entry(module_id).or_default()
}
fn mark_has_pred(&mut self, module_id: ModuleId) {
self.entry(module_id).has_pred = true;
}
fn add_succ(&mut self, module_id: ModuleId, succ: impl IntoIterator<Item = ModuleId>) {
// Add make specialization dependents
let entry = self.entry(module_id);
debug_assert!(
entry.succ.is_empty(),
"already added successors for this module"
);
entry.succ.extend(succ.into_iter());
// The module for derives implicitly depends on every other module
entry.succ.insert(ModuleId::DERIVED_GEN);
}
}
impl Default for MakeSpecializationsDependents {
fn default() -> Self {
let mut map: MutMap<ModuleId, MakeSpecializationInfo> = Default::default();
// The module for derives is always at the base as the last module to specialize
map.insert(
ModuleId::DERIVED_GEN,
MakeSpecializationInfo {
succ: Default::default(),
// NB: invariant - the derived module depends on every other module, and
// work can never be initiated for just the derived module!
has_pred: true,
},
);
Self(map)
}
}
#[derive(Debug)]
pub struct Dependencies<'a> {
waiting_for: MutMap<Job<'a>, MutSet<Job<'a>>>,
notifies: MutMap<Job<'a>, MutSet<Job<'a>>>,
status: MutMap<Job<'a>, Status>,
/// module -> modules to make specializations after, whether a module comes before us
make_specializations_dependents: MutMap<ModuleId, (MutSet<ModuleId>, bool)>,
make_specializations_dependents: MakeSpecializationsDependents,
}
impl<'a> Dependencies<'a> {
pub fn new(goal_phase: Phase) -> Self {
let mut deps = Self {
waiting_for: Default::default(),
notifies: Default::default(),
status: Default::default(),
make_specializations_dependents: Default::default(),
};
if goal_phase >= Phase::MakeSpecializations {
// Module for deriving is always implicitly loaded into the work graph, but it only
// comes into play for make specializations.
deps.add_to_status_for_phase(ModuleId::DERIVED_GEN, Phase::MakeSpecializations);
}
deps
}
/// Add all the dependencies for a module, return (module, phase) pairs that can make progress
pub fn add_module(
&mut self,
@ -86,24 +157,19 @@ impl<'a> Dependencies<'a> {
if goal_phase >= MakeSpecializations {
self.add_dependency(dep, module_id, Phase::MakeSpecializations);
// The module for derives implicitly depends on every other module
self.add_dependency(ModuleId::DERIVED_GEN, module_id, Phase::MakeSpecializations);
let dep_entry = self
.make_specializations_dependents
.entry(dep)
.or_insert((MutSet::default(), false));
dep_entry.1 = true;
// `dep` depends on `module_id` making specializations first
self.make_specializations_dependents.mark_has_pred(dep);
}
}
// Add make specialization dependents
let entry = self
.make_specializations_dependents
.entry(module_id)
.or_insert((MutSet::default(), false));
debug_assert!(entry.0.is_empty(), "already seen this dep");
entry
.0
.extend(dependencies.iter().map(|dep| *dep.as_inner()));
if goal_phase >= MakeSpecializations {
// Add make specialization dependents
self.make_specializations_dependents
.add_succ(module_id, dependencies.iter().map(|dep| *dep.as_inner()));
}
// add dependencies for self
// phase i + 1 of a file always depends on phase i being completed
@ -115,20 +181,26 @@ impl<'a> Dependencies<'a> {
}
}
self.add_to_status(module_id, goal_phase);
self.add_to_status_for_all_phases(module_id, goal_phase);
output
}
fn add_to_status(&mut self, module_id: ModuleId, goal_phase: Phase) {
/// Adds a status for the given module for exactly one phase.
fn add_to_status_for_phase(&mut self, module_id: ModuleId, phase: Phase) {
if let Entry::Vacant(entry) = self.status.entry(Job::Step(module_id, phase)) {
entry.insert(Status::NotStarted);
}
}
/// Adds a status for the given module for all phases up to and including the goal phase.
fn add_to_status_for_all_phases(&mut self, module_id: ModuleId, goal_phase: Phase) {
for phase in PHASES.iter() {
if *phase > goal_phase {
break;
}
if let Entry::Vacant(entry) = self.status.entry(Job::Step(module_id, *phase)) {
entry.insert(Status::NotStarted);
}
self.add_to_status_for_phase(module_id, *phase);
}
}
@ -305,13 +377,14 @@ impl<'a> Dependencies<'a> {
pub fn reload_make_specialization_pass(&mut self) -> MutSet<(ModuleId, Phase)> {
let mut output = MutSet::default();
let mut make_specializations_dependents = Default::default();
let mut make_specializations_dependents = MakeSpecializationsDependents::default();
let default_make_specializations_dependents_len = make_specializations_dependents.0.len();
std::mem::swap(
&mut self.make_specializations_dependents,
&mut make_specializations_dependents,
);
for (&module, _) in make_specializations_dependents.iter() {
for (&module, _) in make_specializations_dependents.0.iter() {
let job = Job::Step(module, Phase::MakeSpecializations);
let status = self.status.get_mut(&job).unwrap();
debug_assert!(
@ -324,12 +397,14 @@ impl<'a> Dependencies<'a> {
// `add_dependency` borrows self as mut so we move `make_specializations_dependents` out
// for our local use. `add_dependency` should never grow the make specializations
// dependency graph.
for (&module, (succ, has_pred)) in make_specializations_dependents.iter() {
for (&module, MakeSpecializationInfo { succ, has_pred }) in
make_specializations_dependents.0.iter()
{
for &dependent in succ {
self.add_dependency(dependent, module, Phase::MakeSpecializations);
}
self.add_to_status(module, Phase::MakeSpecializations);
self.add_to_status_for_phase(module, Phase::MakeSpecializations);
if !has_pred {
output.insert((module, Phase::MakeSpecializations));
}
@ -339,9 +414,11 @@ impl<'a> Dependencies<'a> {
&mut self.make_specializations_dependents,
&mut make_specializations_dependents,
);
debug_assert!(
make_specializations_dependents.is_empty(),
"more modules were added to the graph"
debug_assert_eq!(
make_specializations_dependents.0.len(),
default_make_specializations_dependents_len,
"more modules were added to the graph: {:?}",
make_specializations_dependents
);
output

View file

@ -35,13 +35,12 @@ use std::path::PathBuf;
fn load_and_typecheck(
arena: &Bump,
filename: PathBuf,
src_dir: PathBuf,
exposed_types: ExposedByModule,
target_info: TargetInfo,
) -> Result<LoadedModule, LoadingProblem> {
use LoadResult::*;
let load_start = LoadStart::from_path(arena, src_dir, filename, RenderTarget::Generic)?;
let load_start = LoadStart::from_path(arena, filename, RenderTarget::Generic)?;
match roc_load_internal::file::load(
arena,
@ -163,13 +162,7 @@ fn multiple_modules_help<'a>(
writeln!(file, "{}", source)?;
file_handles.push(file);
load_and_typecheck(
arena,
full_file_path,
dir.path().to_path_buf(),
Default::default(),
TARGET_INFO,
)
load_and_typecheck(arena, full_file_path, Default::default(), TARGET_INFO)
};
Ok(result)
@ -183,7 +176,7 @@ fn load_fixture(
let src_dir = fixtures_dir().join(dir_name);
let filename = src_dir.join(format!("{}.roc", module_name));
let arena = Bump::new();
let loaded = load_and_typecheck(&arena, filename, src_dir, subs_by_module, TARGET_INFO);
let loaded = load_and_typecheck(&arena, filename, subs_by_module, TARGET_INFO);
let mut loaded_module = match loaded {
Ok(x) => x,
Err(roc_load_internal::file::LoadingProblem::FormattedReport(report)) => {
@ -339,7 +332,7 @@ fn interface_with_deps() {
let src_dir = fixtures_dir().join("interface_with_deps");
let filename = src_dir.join("Primary.roc");
let arena = Bump::new();
let loaded = load_and_typecheck(&arena, filename, src_dir, subs_by_module, TARGET_INFO);
let loaded = load_and_typecheck(&arena, filename, subs_by_module, TARGET_INFO);
let mut loaded_module = loaded.expect("Test module failed to load");
let home = loaded_module.module_id;

View file

@ -249,7 +249,7 @@ lazy_static! {
std::sync::Mutex::new(roc_collections::SmallStringInterner::with_capacity(10));
}
#[derive(Debug, Default)]
#[derive(Debug, Default, Clone)]
pub struct Interns {
pub module_ids: ModuleIds,
pub all_ident_ids: IdentIdsByModule,
@ -663,7 +663,7 @@ impl IdentIds {
}
}
#[derive(Debug, Default)]
#[derive(Debug, Default, Clone)]
pub struct IdentIdsByModule(VecMap<ModuleId, IdentIds>);
impl IdentIdsByModule {
@ -1001,10 +1001,13 @@ define_builtins! {
31 ATTR_INVALID: "#attr_invalid"
}
// Fake module for storing derived function symbols
1 DERIVED: "#Derived" => {
// Fake module for synthesizing and storing derived implementations
1 DERIVED_SYNTH: "#Derived" => {
}
2 NUM: "Num" => {
// Fake module from which derived implementations are code-generated
2 DERIVED_GEN: "#Derived_gen" => {
}
3 NUM: "Num" => {
0 NUM_NUM: "Num" // the Num.Num type alias
1 NUM_I128: "I128" // the Num.I128 type alias
2 NUM_U128: "U128" // the Num.U128 type alias
@ -1152,7 +1155,7 @@ define_builtins! {
144 NUM_BYTES_TO_U16_LOWLEVEL: "bytesToU16Lowlevel"
145 NUM_BYTES_TO_U32_LOWLEVEL: "bytesToU32Lowlevel"
}
3 BOOL: "Bool" => {
4 BOOL: "Bool" => {
0 BOOL_BOOL: "Bool" // the Bool.Bool type alias
1 BOOL_FALSE: "False" imported // Bool.Bool = [False, True]
// NB: not strictly needed; used for finding tag names in error suggestions
@ -1165,7 +1168,7 @@ define_builtins! {
7 BOOL_EQ: "isEq"
8 BOOL_NEQ: "isNotEq"
}
4 STR: "Str" => {
5 STR: "Str" => {
0 STR_STR: "Str" imported // the Str.Str type alias
1 STR_IS_EMPTY: "isEmpty"
2 STR_APPEND: "#append" // unused
@ -1217,7 +1220,7 @@ define_builtins! {
48 STR_FROM_UTF8_RANGE_LOWLEVEL: "fromUtf8RangeLowlevel"
49 STR_CAPACITY: "capacity"
}
5 LIST: "List" => {
6 LIST: "List" => {
0 LIST_LIST: "List" imported // the List.List type alias
1 LIST_IS_EMPTY: "isEmpty"
2 LIST_GET: "get"
@ -1288,7 +1291,7 @@ define_builtins! {
67 LIST_SUBLIST_LOWLEVEL: "sublistLowlevel"
68 LIST_CAPACITY: "capacity"
}
6 RESULT: "Result" => {
7 RESULT: "Result" => {
0 RESULT_RESULT: "Result" // the Result.Result type alias
1 RESULT_OK: "Ok" imported // Result.Result a e = [Ok a, Err e]
// NB: not strictly needed; used for finding tag names in error suggestions
@ -1302,7 +1305,7 @@ define_builtins! {
8 RESULT_IS_ERR: "isErr"
9 RESULT_AFTER_ERR: "afterErr"
}
7 DICT: "Dict" => {
8 DICT: "Dict" => {
0 DICT_DICT: "Dict" // the Dict.Dict type alias
1 DICT_EMPTY: "empty"
2 DICT_SINGLE: "single"
@ -1324,7 +1327,7 @@ define_builtins! {
15 DICT_WITH_CAPACITY: "withCapacity"
16 DICT_CAPACITY: "capacity"
}
8 SET: "Set" => {
9 SET: "Set" => {
0 SET_SET: "Set" // the Set.Set type alias
1 SET_EMPTY: "empty"
2 SET_SINGLE: "single"
@ -1342,12 +1345,12 @@ define_builtins! {
14 SET_TO_DICT: "toDict"
15 SET_CAPACITY: "capacity"
}
9 BOX: "Box" => {
10 BOX: "Box" => {
0 BOX_BOX_TYPE: "Box" imported // the Box.Box opaque type
1 BOX_BOX_FUNCTION: "box" // Box.box
2 BOX_UNBOX: "unbox"
}
10 ENCODE: "Encode" => {
11 ENCODE: "Encode" => {
0 ENCODE_ENCODER: "Encoder"
1 ENCODE_ENCODING: "Encoding"
2 ENCODE_TO_ENCODER: "toEncoder"
@ -1375,9 +1378,9 @@ define_builtins! {
24 ENCODE_APPEND: "append"
25 ENCODE_TO_BYTES: "toBytes"
}
11 JSON: "Json" => {
12 JSON: "Json" => {
0 JSON_JSON: "Json"
}
num_modules: 12 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro)
num_modules: 13 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro)
}

View file

@ -12,7 +12,7 @@ roc_region = { path = "../region" }
roc_module = { path = "../module" }
roc_types = { path = "../types" }
roc_can = { path = "../can" }
roc_derive_key = { path = "../derive_key" }
roc_derive = { path = "../derive" }
roc_late_solve = { path = "../late_solve" }
roc_std = { path = "../../roc_std", default-features = false }
roc_problem = { path = "../problem" }

View file

@ -922,7 +922,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
NumBytesToU32 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[borrowed, borrowed]),
StrStartsWithScalar => arena.alloc_slice_copy(&[borrowed, irrelevant]),
StrFromUtf8Range => arena.alloc_slice_copy(&[borrowed, irrelevant, irrelevant]),
StrFromUtf8Range => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
StrToUtf8 => arena.alloc_slice_copy(&[owned]),
StrRepeat => arena.alloc_slice_copy(&[borrowed, irrelevant]),
StrFromInt | StrFromFloat => arena.alloc_slice_copy(&[irrelevant]),

View file

@ -9,6 +9,7 @@ use bumpalo::Bump;
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_can::abilities::SpecializationId;
use roc_can::expr::{AnnotatedMark, ClosureData, IntValue};
use roc_can::module::ExposedByModule;
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
use roc_collections::VecMap;
use roc_debug_flags::dbg_do;
@ -17,7 +18,7 @@ use roc_debug_flags::{
ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION,
ROC_PRINT_RUNTIME_ERROR_GEN,
};
use roc_derive_key::GlobalDerivedSymbols;
use roc_derive::SharedDerivedModule;
use roc_error_macros::{internal_error, todo_abilities};
use roc_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId};
use roc_late_solve::{resolve_ability_specialization, AbilitiesView, Resolved, UnificationFailed};
@ -918,7 +919,7 @@ impl<'a> SymbolSpecializations<'a> {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub struct ProcsBase<'a> {
pub partial_procs: BumpMap<Symbol, PartialProc<'a>>,
pub module_thunks: &'a [Symbol],
@ -1293,8 +1294,10 @@ pub struct Env<'a, 'i> {
pub target_info: TargetInfo,
pub update_mode_ids: &'i mut UpdateModeIds,
pub call_specialization_counter: u32,
// TODO: WorldAbilities and exposed_by_module share things, think about how to combine them
pub abilities: AbilitiesView<'i>,
pub derived_symbols: &'i GlobalDerivedSymbols,
pub exposed_by_module: &'i ExposedByModule,
pub derived_module: &'i SharedDerivedModule,
}
impl<'a, 'i> Env<'a, 'i> {
@ -1319,18 +1322,43 @@ impl<'a, 'i> Env<'a, 'i> {
}
pub fn is_imported_symbol(&self, symbol: Symbol) -> bool {
symbol.module_id() != self.home
let sym_module = symbol.module_id();
sym_module != self.home
// The Derived_gen module takes responsibility for code-generating symbols in the
// Derived_synth module.
&& !(self.home == ModuleId::DERIVED_GEN && sym_module == ModuleId::DERIVED_SYNTH)
}
/// While specializing the Derived_gen module, derived implementation symbols from the
/// Derived_synth module may be discovered. These implementations may not have yet been loaded
/// into the Derived_gen module, because we only load them before making specializations, and
/// not during mono itself (yet).
///
/// When this procedure returns `true`, the symbol should be marked as an external specialization,
/// so that a subsequent specializations pass loads the derived implementation into Derived_gen
/// and then code-generates appropriately.
pub fn is_unloaded_derived_symbol(&self, symbol: Symbol, procs: &Procs<'a>) -> bool {
self.home == ModuleId::DERIVED_GEN
&& symbol.module_id() == ModuleId::DERIVED_SYNTH
&& !procs.partial_procs.contains_key(symbol)
}
/// Unifies two variables and performs lambda set compaction.
/// Use this rather than [roc_unify::unify] directly!
fn unify(&mut self, left: Variable, right: Variable) -> Result<(), UnificationFailed> {
debug_assert_ne!(
self.home,
ModuleId::DERIVED_SYNTH,
"should never be monomorphizing the derived synth module!"
);
roc_late_solve::unify(
self.home,
self.arena,
self.subs,
&self.abilities,
self.derived_symbols,
self.derived_module,
self.exposed_by_module,
left,
right,
)
@ -2347,7 +2375,7 @@ fn from_can_let<'a>(
for (_specialization_mark, (var, specialized_symbol)) in
needed_specializations
{
use crate::copy::deep_copy_type_vars_into_expr;
use roc_can::copy::deep_copy_type_vars_into_expr;
let (new_def_expr_var, specialized_expr) = deep_copy_type_vars_into_expr(
env.subs,
@ -2625,6 +2653,7 @@ fn specialize_suspended<'a>(
None => {
// TODO this assumes the specialization is done by another module
// make sure this does not become a problem down the road!
debug_assert!(name.name().module_id() != name.name().module_id());
continue;
}
}
@ -7682,7 +7711,9 @@ fn call_by_name_help<'a>(
assigned,
hole,
)
} else if env.is_imported_symbol(proc_name.name()) {
} else if env.is_imported_symbol(proc_name.name())
|| env.is_unloaded_derived_symbol(proc_name.name(), procs)
{
add_needed_external(procs, env, original_fn_var, proc_name);
debug_assert_ne!(proc_name.name().module_id(), ModuleId::ATTR);

View file

@ -4,7 +4,6 @@
pub mod borrow;
pub mod code_gen_help;
mod copy;
pub mod inc_dec;
pub mod ir;
pub mod layout;

View file

@ -14,6 +14,7 @@ roc_module = { path = "../module" }
roc_types = { path = "../types" }
roc_can = { path = "../can" }
roc_derive_key = { path = "../derive_key" }
roc_derive = { path = "../derive" }
roc_problem = { path = "../problem" }
roc_unify = { path = "../unify" }
roc_debug_flags = { path = "../debug_flags" }
@ -28,7 +29,7 @@ roc_parse = { path = "../parse" }
roc_solve = { path = "../solve" }
roc_target = { path = "../roc_target" }
roc_reporting = { path = "../../reporting" }
roc_derive_key = { path = "../derive_key", features = ["debug-derived-symbols"] }
roc_derive = { path = "../derive", features = ["debug-derived-symbols"] }
pretty_assertions = "1.0.0"
indoc = "1.0.3"
tempfile = "3.2.0"

View file

@ -55,7 +55,7 @@ pub enum Unfulfilled {
},
}
/// Indexes a deriving of an ability for an opaque type.
/// Indexes a requested deriving of an ability for an opaque type.
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct RequestedDeriveKey {
pub opaque: Symbol,

View file

@ -2,12 +2,12 @@ use crate::solve::{self, Aliases};
use roc_can::abilities::{AbilitiesStore, ResolvedSpecializations};
use roc_can::constraint::{Constraint as ConstraintSoa, Constraints};
use roc_can::expr::PendingDerives;
use roc_can::module::RigidVariables;
use roc_can::module::{ExposedByModule, RigidVariables};
use roc_collections::all::MutMap;
use roc_collections::VecMap;
use roc_derive_key::GlobalDerivedSymbols;
use roc_derive::SharedDerivedModule;
use roc_error_macros::internal_error;
use roc_module::symbol::Symbol;
use roc_module::symbol::{ModuleId, Symbol};
use roc_types::subs::{Content, ExposedTypesStorageSubs, FlatType, StorageSubs, Subs, Variable};
use roc_types::types::Alias;
@ -54,6 +54,7 @@ pub struct SolvedModule {
#[allow(clippy::too_many_arguments)] // TODO: put params in a context/env var
pub fn run_solve(
home: ModuleId,
constraints: &Constraints,
constraint: ConstraintSoa,
rigid_variables: RigidVariables,
@ -61,7 +62,8 @@ pub fn run_solve(
mut aliases: Aliases,
mut abilities_store: AbilitiesStore,
pending_derives: PendingDerives,
derived_symbols: GlobalDerivedSymbols,
exposed_by_module: &ExposedByModule,
derived_module: SharedDerivedModule,
) -> (
Solved<Subs>,
solve::Env,
@ -86,6 +88,7 @@ pub fn run_solve(
// Run the solver to populate Subs.
let (solved_subs, solved_env) = solve::run(
home,
constraints,
&mut problems,
subs,
@ -93,7 +96,8 @@ pub fn run_solve(
&constraint,
pending_derives,
&mut abilities_store,
derived_symbols,
exposed_by_module,
derived_module,
);
(solved_subs, solved_env, problems, abilities_store)

View file

@ -9,11 +9,13 @@ use roc_can::constraint::Constraint::{self, *};
use roc_can::constraint::{Constraints, Cycle, LetConstraint, OpportunisticResolve};
use roc_can::expected::{Expected, PExpected};
use roc_can::expr::PendingDerives;
use roc_can::module::ExposedByModule;
use roc_collections::all::MutMap;
use roc_debug_flags::dbg_do;
#[cfg(debug_assertions)]
use roc_debug_flags::{ROC_TRACE_COMPACTION, ROC_VERIFY_RIGID_LET_GENERALIZED};
use roc_derive_key::{DeriveError, Derived, GlobalDerivedSymbols};
use roc_derive::SharedDerivedModule;
use roc_derive_key::{DeriveError, DeriveKey};
use roc_error_macros::internal_error;
use roc_module::ident::TagName;
use roc_module::symbol::{ModuleId, Symbol};
@ -580,6 +582,7 @@ struct State {
#[allow(clippy::too_many_arguments)] // TODO: put params in a context/env var
pub fn run(
home: ModuleId,
constraints: &Constraints,
problems: &mut Vec<TypeError>,
mut subs: Subs,
@ -587,9 +590,11 @@ pub fn run(
constraint: &Constraint,
pending_derives: PendingDerives,
abilities_store: &mut AbilitiesStore,
derived_symbols: GlobalDerivedSymbols,
exposed_by_module: &ExposedByModule,
derived_module: SharedDerivedModule,
) -> (Solved<Subs>, Env) {
let env = run_in_place(
home,
constraints,
problems,
&mut subs,
@ -597,7 +602,8 @@ pub fn run(
constraint,
pending_derives,
abilities_store,
derived_symbols,
exposed_by_module,
derived_module,
);
(Solved(subs), env)
@ -606,6 +612,7 @@ pub fn run(
/// Modify an existing subs in-place instead
#[allow(clippy::too_many_arguments)] // TODO: put params in a context/env var
fn run_in_place(
_home: ModuleId, // TODO: remove me?
constraints: &Constraints,
problems: &mut Vec<TypeError>,
subs: &mut Subs,
@ -613,7 +620,8 @@ fn run_in_place(
constraint: &Constraint,
pending_derives: PendingDerives,
abilities_store: &mut AbilitiesStore,
derived_symbols: GlobalDerivedSymbols,
exposed_by_module: &ExposedByModule,
derived_module: SharedDerivedModule,
) -> Env {
let mut pools = Pools::default();
@ -651,11 +659,12 @@ fn run_in_place(
// are legal, which we need to register.
let new_must_implement = compact_lambda_sets_of_vars(
subs,
&derived_module,
&arena,
&mut pools,
deferred_uls_to_resolve,
&SolvePhase { abilities_store },
&derived_symbols,
exposed_by_module,
);
deferred_obligations.add(new_must_implement, AbilityImplError::IncompleteAbility);
@ -1913,11 +1922,12 @@ fn unique_unspecialized_lambda(subs: &Subs, c_a: Variable, uls: &[Uls]) -> Optio
#[must_use]
pub fn compact_lambda_sets_of_vars<P: Phase>(
subs: &mut Subs,
derived_module: &SharedDerivedModule,
arena: &Bump,
pools: &mut Pools,
uls_of_var: UlsOfVar,
phase: &P,
derived_symbols: &GlobalDerivedSymbols,
exposed_by_module: &ExposedByModule,
) -> MustImplementConstraints {
// let mut seen = VecSet::default();
let mut must_implement = MustImplementConstraints::default();
@ -2026,6 +2036,7 @@ pub fn compact_lambda_sets_of_vars<P: Phase>(
ord => ord,
}
});
trace_compact!(2. subs, &uls_a);
// 3. For each `l` in `uls_a` with unique unspecialized lambda `C:f:r`:
@ -2041,8 +2052,16 @@ pub fn compact_lambda_sets_of_vars<P: Phase>(
// continue;
// }
let (new_must_implement, new_uls_of_var) =
compact_lambda_set(subs, arena, pools, c_a, l, phase, derived_symbols);
let (new_must_implement, new_uls_of_var) = compact_lambda_set(
subs,
derived_module,
arena,
pools,
c_a,
l,
phase,
exposed_by_module,
);
must_implement.extend(new_must_implement);
uls_of_var_queue.extend(new_uls_of_var.drain());
@ -2055,14 +2074,16 @@ pub fn compact_lambda_sets_of_vars<P: Phase>(
}
#[must_use]
#[allow(clippy::too_many_arguments)]
fn compact_lambda_set<P: Phase>(
subs: &mut Subs,
derived_module: &SharedDerivedModule,
arena: &Bump,
pools: &mut Pools,
resolved_concrete: Variable,
this_lambda_set: Variable,
phase: &P,
derived_symbols: &GlobalDerivedSymbols,
exposed_by_module: &ExposedByModule,
) -> (MustImplementConstraints, UlsOfVar) {
// 3. For each `l` in `uls_a` with unique unspecialized lambda `C:f:r`:
// 1. Let `t_f1` be the directly ambient function of the lambda set containing `C:f:r`. Remove `C:f:r` from `t_f1`'s lambda set.
@ -2108,143 +2129,172 @@ fn compact_lambda_set<P: Phase>(
Content::LambdaSet(t_f1_lambda_set_without_concrete),
);
enum Spec {
// 2. Let `t_f2` be the directly ambient function of the specialization lambda set resolved by `C:f:r`.
Some { t_f2: Variable },
// The specialized lambda set should actually just be dropped, not resolved and unified.
Drop,
}
let specialization_decision = make_specialization_decision(subs, c);
let spec = match subs.get_content_without_compacting(c) {
Content::Structure(_) | Content::Alias(_, _, _, AliasKind::Structural) => {
let specialization_key = match specialization_decision {
SpecializeDecision::Specialize(key) => key,
SpecializeDecision::Drop => {
// Do nothing other than to remove the concrete lambda to drop from the lambda set,
// which we already did in 1b above.
trace_compact!(3iter_end_skipped. subs, t_f1);
return (Default::default(), Default::default());
}
};
let specialization_ambient_function_var = get_specialization_lambda_set_ambient_function(
subs,
derived_module,
phase,
f,
r,
specialization_key,
exposed_by_module,
target_rank,
);
let t_f2 = match specialization_ambient_function_var {
Ok(lset) => lset,
Err(()) => {
// Do nothing other than to remove the concrete lambda to drop from the lambda set,
// which we already did in 1b above.
trace_compact!(3iter_end_skipped. subs, t_f1);
return (Default::default(), Default::default());
}
};
// Ensure the specialized ambient function we'll unify with is not a generalized one, but one
// at the rank of the lambda set being compacted.
let t_f2 = deep_copy_var_in(subs, target_rank, pools, t_f2, arena);
// 3. Unify `t_f1 ~ t_f2`.
trace_compact!(3iter_start. subs, this_lambda_set, t_f1, t_f2);
let (vars, new_must_implement_ability, new_lambda_sets_to_specialize, _meta) =
unify(subs, t_f1, t_f2, Mode::EQ).expect_success("ambient functions don't unify");
trace_compact!(3iter_end. subs, t_f1);
introduce(subs, target_rank, pools, &vars);
(new_must_implement_ability, new_lambda_sets_to_specialize)
}
enum SpecializationTypeKey {
Opaque(Symbol),
Derived(DeriveKey),
}
enum SpecializeDecision {
Specialize(SpecializationTypeKey),
Drop,
}
fn make_specialization_decision(subs: &Subs, var: Variable) -> SpecializeDecision {
use Content::*;
use SpecializationTypeKey::*;
match subs.get_content_without_compacting(var) {
Structure(_) | Alias(_, _, _, AliasKind::Structural) => {
// This is a structural type, find the name of the derived ability function it
// should use.
match Derived::encoding(subs, c) {
Ok(derived) => {
let specialization_symbol = match derived {
Derived::Immediate(symbol) => symbol,
Derived::Key(derive_key) => {
let mut derived_symbols = derived_symbols.lock().unwrap();
derived_symbols.get_or_insert(derive_key)
}
};
let specialization_symbol_slice =
UnionLabels::insert_into_subs(subs, vec![(specialization_symbol, vec![])]);
// TODO: This is WRONG, fix it!
let ambient_function = Variable::NULL;
let _lambda_set_for_derived = subs.fresh(Descriptor {
content: Content::LambdaSet(subs::LambdaSet {
solved: specialization_symbol_slice,
recursion_var: OptVariable::NONE,
unspecialized: SubsSlice::default(),
ambient_function,
}),
rank: target_rank,
mark: Mark::NONE,
copy: OptVariable::NONE,
});
Spec::Some {
t_f2: ambient_function,
match roc_derive_key::Derived::encoding(subs, var) {
Ok(derived) => match derived {
roc_derive_key::Derived::Immediate(_) => {
todo!("deal with lambda set extraction from immediates")
}
}
roc_derive_key::Derived::Key(derive_key) => {
SpecializeDecision::Specialize(Derived(derive_key))
}
},
Err(DeriveError::UnboundVar) => {
// not specialized yet, but that also means that it can't possibly be derivable
// at this point?
// TODO: is this right? Revisit if it causes us problems in the future.
Spec::Drop
SpecializeDecision::Drop
}
Err(DeriveError::Underivable) => {
// we should have reported an error for this; drop the lambda set.
Spec::Drop
SpecializeDecision::Drop
}
}
}
Content::Alias(opaque, _, _, AliasKind::Opaque) => {
Alias(opaque, _, _, AliasKind::Opaque) => SpecializeDecision::Specialize(Opaque(*opaque)),
Error => SpecializeDecision::Drop,
FlexAbleVar(_, _)
| RigidAbleVar(..)
| FlexVar(..)
| RigidVar(..)
| RecursionVar { .. }
| LambdaSet(..)
| RangedNumber(..) => {
internal_error!("unexpected")
}
}
}
#[allow(clippy::too_many_arguments)]
fn get_specialization_lambda_set_ambient_function<P: Phase>(
subs: &mut Subs,
derived_module: &SharedDerivedModule,
phase: &P,
ability_member: Symbol,
lset_region: u8,
specialization_key: SpecializationTypeKey,
exposed_by_module: &ExposedByModule,
target_rank: Rank,
) -> Result<Variable, ()> {
match specialization_key {
SpecializationTypeKey::Opaque(opaque) => {
let opaque_home = opaque.module_id();
let opt_lambda_set =
let external_specialized_lset =
phase.with_module_abilities_store(opaque_home, |abilities_store| {
let opt_specialization = abilities_store.get_specialization(f, *opaque);
let opt_specialization =
abilities_store.get_specialization(ability_member, opaque);
match (P::IS_LATE, opt_specialization) {
(false, None) => {
// doesn't specialize, we'll have reported an error for this
None
Err(())
}
(true, None) => {
internal_error!(
"expected to know a specialization for {:?}#{:?}, but it wasn't found",
opaque,
f,
ability_member,
);
}
(_, Some(specialization)) => {
let specialized_lambda_set = *specialization
.specialization_lambda_sets
.get(&r)
.unwrap_or_else(|| {
internal_error!(
"lambda set region ({:?}, {}) not resolved",
f,
r
)
});
Some(specialized_lambda_set)
.get(&lset_region)
.expect("lambda set region not resolved");
Ok(specialized_lambda_set)
}
}
});
match opt_lambda_set {
Some(lambda_set_var) => {
// Get the ambient function type
let spec_ambient_function = phase
.copy_lambda_set_ambient_function_to_home_subs(
lambda_set_var,
opaque_home,
subs,
);
})?;
Spec::Some {
t_f2: spec_ambient_function,
}
}
None => Spec::Drop,
}
let specialized_ambient = phase.copy_lambda_set_ambient_function_to_home_subs(
external_specialized_lset,
opaque_home,
subs,
);
Ok(specialized_ambient)
}
Content::Error => Spec::Drop,
SpecializationTypeKey::Derived(derive_key) => {
let mut derived_module = derived_module.lock().unwrap();
Content::FlexAbleVar(..)
| Content::RigidAbleVar(..)
| Content::FlexVar(..)
| Content::RigidVar(..)
| Content::RecursionVar { .. }
| Content::LambdaSet(..)
| Content::RangedNumber(..) => {
internal_error!("unexpected")
}
};
let (_, _, specialization_lambda_sets) =
derived_module.get_or_insert(exposed_by_module, derive_key);
match spec {
Spec::Some { t_f2 } => {
// Ensure the specialized ambient function we'll unify with is not a generalized one, but one
// at the rank of the lambda set being compacted.
let t_f2 = deep_copy_var_in(subs, target_rank, pools, t_f2, arena);
let specialized_lambda_set = *specialization_lambda_sets
.get(&lset_region)
.expect("lambda set region not resolved");
// 3. Unify `t_f1 ~ t_f2`.
trace_compact!(3iter_start. subs, this_lambda_set, t_f1, t_f2);
let (vars, new_must_implement_ability, new_lambda_sets_to_specialize, _meta) =
unify(subs, t_f1, t_f2, Mode::EQ).expect_success("ambient functions don't unify");
trace_compact!(3iter_end. subs, t_f1);
let specialized_ambient = derived_module.copy_lambda_set_ambient_function_to_subs(
specialized_lambda_set,
subs,
target_rank,
);
introduce(subs, target_rank, pools, &vars);
(new_must_implement_ability, new_lambda_sets_to_specialize)
}
Spec::Drop => {
// Do nothing other than to remove the concrete lambda to drop from the lambda set,
// which we already did in 1b above.
trace_compact!(3iter_end_skipped. subs, t_f1);
(Default::default(), Default::default())
Ok(specialized_ambient)
}
}
}

View file

@ -22,12 +22,12 @@ use roc_can::{
use roc_collections::VecSet;
use roc_constrain::expr::constrain_decls;
use roc_debug_flags::dbg_do;
use roc_derive::{synth_var, DerivedModule, StolenFromDerived};
use roc_derive::{synth_var, DerivedModule};
use roc_derive_key::{DeriveKey, Derived};
use roc_load_internal::file::{add_imports, default_aliases, LoadedModule, Threading};
use roc_module::{
ident::TagName,
symbol::{Interns, ModuleId, Symbol},
symbol::{IdentIds, Interns, ModuleId, Symbol},
};
use roc_region::all::LineInfo;
use roc_reporting::report::{type_problem, RocDocAllocator};
@ -40,7 +40,7 @@ use roc_types::{
types::{AliasKind, RecordField},
};
const DERIVED_MODULE: ModuleId = ModuleId::DERIVED;
const DERIVED_MODULE: ModuleId = ModuleId::DERIVED_SYNTH;
fn encode_path() -> PathBuf {
let repo_root = std::env::var("ROC_WORKSPACE_DIR").expect("are you running with `cargo test`?");
@ -145,7 +145,7 @@ fn check_derived_typechecks_and_golden(
test_module,
&mut test_subs,
pending_abilities,
exposed_for_module,
&exposed_for_module,
&mut def_types,
&mut rigid_vars,
);
@ -158,6 +158,7 @@ fn check_derived_typechecks_and_golden(
std::env::set_var(roc_debug_flags::ROC_PRINT_UNIFICATIONS_DERIVED, "1")
);
let (mut solved_subs, _, problems, _) = roc_solve::module::run_solve(
test_module,
&constraints,
constr,
RigidVariables::default(),
@ -165,6 +166,7 @@ fn check_derived_typechecks_and_golden(
default_aliases(),
abilities_store,
Default::default(),
&exposed_for_module.exposed_by_module,
Default::default(),
);
let subs = solved_subs.inner_mut();
@ -242,12 +244,12 @@ where
)
.unwrap();
let mut derived_module = DerivedModule::default();
let mut subs = Subs::new();
let ident_ids = IdentIds::default();
let source_var = synth_input(&mut subs);
let key = get_key(&subs, source_var);
let mut stolen = derived_module.steal();
let source_var = synth_input(&mut stolen.subs);
let key = get_key(&stolen.subs, source_var);
derived_module.return_stolen(stolen);
let mut derived_module = unsafe { DerivedModule::from_components(subs, ident_ids) };
let mut exposed_by_module = ExposedByModule::default();
exposed_by_module.insert(
@ -263,7 +265,7 @@ where
let specialization_lsets = specialization_lsets.clone();
let derived_def = derived_def.clone();
let StolenFromDerived { ident_ids, subs } = derived_module.steal();
let (subs, ident_ids) = derived_module.decompose();
interns.all_ident_ids.insert(DERIVED_MODULE, ident_ids);
DERIVED_MODULE.register_debug_idents(interns.all_ident_ids.get(&DERIVED_MODULE).unwrap());

View file

@ -375,3 +375,122 @@ fn encode_use_stdlib_without_wrapping_custom() {
RocStr
)
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn to_encoder_encode_custom_has_capture() {
assert_evals_to!(
indoc!(
r#"
app "test"
imports [Encode.{ toEncoder }, Json]
provides [main] to "./platform"
HelloWorld := Str
toEncoder = \@HelloWorld s1 ->
Encode.custom \bytes, fmt ->
bytes
|> Encode.appendWith (Encode.string s1) fmt
main =
result = Str.fromUtf8 (Encode.toBytes (@HelloWorld "Hello, World!\n") Json.format)
when result is
Ok s -> s
_ -> "<bad>"
"#
),
RocStr::from("\"Hello, World!\n\""),
RocStr
)
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn encode_derived_string() {
assert_evals_to!(
indoc!(
r#"
app "test"
imports [Encode.{ toEncoder }, Json]
provides [main] to "./platform"
main =
result = Str.fromUtf8 (Encode.toBytes "foo" Json.format)
when result is
Ok s -> s
_ -> "<bad>"
"#
),
RocStr::from("\"foo\""),
RocStr
)
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn encode_derived_record_one_field_string() {
assert_evals_to!(
indoc!(
r#"
app "test"
imports [Encode.{ toEncoder }, Json]
provides [main] to "./platform"
main =
result = Str.fromUtf8 (Encode.toBytes {a: "foo"} Json.format)
when result is
Ok s -> s
_ -> "<bad>"
"#
),
RocStr::from(r#"{"a":"foo",}"#),
RocStr
)
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn encode_derived_record_two_fields_strings() {
assert_evals_to!(
indoc!(
r#"
app "test"
imports [Encode.{ toEncoder }, Json]
provides [main] to "./platform"
main =
rcd = {a: "foo", b: "bar"}
result = Str.fromUtf8 (Encode.toBytes rcd Json.format)
when result is
Ok s -> s
_ -> "<bad>"
"#
),
RocStr::from(r#"{"a":"foo","b":"bar",}"#),
RocStr
)
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn encode_derived_nested_record_string() {
assert_evals_to!(
indoc!(
r#"
app "test"
imports [Encode.{ toEncoder }, Json]
provides [main] to "./platform"
main =
rcd = {a: {b: "bar"}}
encoded = Encode.toBytes rcd Json.format
result = Str.fromUtf8 encoded
when result is
Ok s -> s
_ -> "<bad>"
"#
),
RocStr::from(r#"{"a":{"b":"bar",},}"#),
RocStr
)
}

View file

@ -3054,6 +3054,78 @@ fn num_to_str_i64() {
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn num_to_str_f32() {
use roc_std::RocStr;
assert_evals_to!(r#"Num.toStr -10.75f32"#, RocStr::from("-10.75"), RocStr);
assert_evals_to!(r#"Num.toStr -1.75f32"#, RocStr::from("-1.75"), RocStr);
assert_evals_to!(r#"Num.toStr 0f32"#, RocStr::from("0"), RocStr);
assert_evals_to!(r#"Num.toStr 1.75f32"#, RocStr::from("1.75"), RocStr);
assert_evals_to!(r#"Num.toStr 10.75f32"#, RocStr::from("10.75"), RocStr);
assert_evals_to!(
r#"Num.toStr Num.maxF32"#,
RocStr::from("340282346638528860000000000000000000000"),
RocStr
);
assert_evals_to!(
r#"Num.toStr Num.minF32"#,
RocStr::from("-340282346638528860000000000000000000000"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn num_to_str_f64() {
use roc_std::RocStr;
assert_evals_to!(r#"Num.toStr -10.75f64"#, RocStr::from("-10.75"), RocStr);
assert_evals_to!(r#"Num.toStr -1.75f64"#, RocStr::from("-1.75"), RocStr);
assert_evals_to!(r#"Num.toStr 0f64"#, RocStr::from("0"), RocStr);
assert_evals_to!(r#"Num.toStr 1.75f64"#, RocStr::from("1.75"), RocStr);
assert_evals_to!(r#"Num.toStr 10.75f64"#, RocStr::from("10.75"), RocStr);
assert_evals_to!(
r#"Num.toStr Num.maxF64"#,
RocStr::from(f64::MAX.to_string().as_str()),
RocStr
);
assert_evals_to!(
r#"Num.toStr Num.minF64"#,
RocStr::from(f64::MIN.to_string().as_str()),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn num_to_str_dec() {
use roc_std::RocStr;
assert_evals_to!(r#"Num.toStr -10.75dec"#, RocStr::from("-10.75"), RocStr);
assert_evals_to!(r#"Num.toStr -1.75dec"#, RocStr::from("-1.75"), RocStr);
assert_evals_to!(r#"Num.toStr 0dec"#, RocStr::from("0.0"), RocStr);
assert_evals_to!(r#"Num.toStr 1.75dec"#, RocStr::from("1.75"), RocStr);
assert_evals_to!(r#"Num.toStr 10.75dec"#, RocStr::from("10.75"), RocStr);
assert_evals_to!(
r#"Num.toStr 170141183460469.105727dec"#,
RocStr::from("170141183460469.105727"),
RocStr
);
assert_evals_to!(
r#"Num.toStr -170141183460469.105727dec"#,
RocStr::from("-170141183460469.105727"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn u8_addition_greater_than_i8() {

View file

@ -0,0 +1,465 @@
procedure #Derived.0 (#Derived.1):
let #Derived_gen.1 : {Str} = Struct {#Derived.1};
let #Derived_gen.0 : {Str} = CallByName Encode.22 #Derived_gen.1;
ret #Derived_gen.0;
procedure #Derived.10 (#Derived.11):
let #Derived_gen.29 : {Str} = Struct {#Derived.11};
let #Derived_gen.28 : {Str} = CallByName Encode.22 #Derived_gen.29;
ret #Derived_gen.28;
procedure #Derived.12 (#Derived.13, #Derived.14, #Attr.12):
let #Derived.11 : Str = StructAtIndex 0 #Attr.12;
inc #Derived.11;
dec #Attr.12;
let #Derived_gen.32 : {Str} = CallByName Json.17 #Derived.11;
let #Derived_gen.31 : List U8 = CallByName Encode.23 #Derived.13 #Derived_gen.32 #Derived.14;
ret #Derived_gen.31;
procedure #Derived.2 (#Derived.3, #Derived.4, #Attr.12):
let #Derived.1 : Str = StructAtIndex 0 #Attr.12;
inc #Derived.1;
dec #Attr.12;
let #Derived_gen.7 : Str = "a";
let #Derived_gen.8 : {Str} = CallByName #Derived.5 #Derived.1;
let #Derived_gen.6 : {Str, {Str}} = Struct {#Derived_gen.7, #Derived_gen.8};
let #Derived_gen.5 : List {Str, {Str}} = Array [#Derived_gen.6];
let #Derived_gen.4 : {List {Str, {Str}}} = CallByName Json.19 #Derived_gen.5;
let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.3 #Derived_gen.4 #Derived.4;
ret #Derived_gen.3;
procedure #Derived.5 (#Derived.6):
let #Derived_gen.15 : {Str} = Struct {#Derived.6};
let #Derived_gen.14 : {Str} = CallByName Encode.22 #Derived_gen.15;
ret #Derived_gen.14;
procedure #Derived.7 (#Derived.8, #Derived.9, #Attr.12):
let #Derived.6 : Str = StructAtIndex 0 #Attr.12;
inc #Derived.6;
dec #Attr.12;
let #Derived_gen.21 : Str = "b";
let #Derived_gen.22 : {Str} = CallByName #Derived.10 #Derived.6;
let #Derived_gen.20 : {Str, {Str}} = Struct {#Derived_gen.21, #Derived_gen.22};
let #Derived_gen.19 : List {Str, {Str}} = Array [#Derived_gen.20];
let #Derived_gen.18 : {List {Str, {Str}}} = CallByName Json.19 #Derived_gen.19;
let #Derived_gen.17 : List U8 = CallByName Encode.23 #Derived.8 #Derived_gen.18 #Derived.9;
ret #Derived_gen.17;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.106 : List U8 = CallByName #Derived.2 Encode.94 Encode.96 Encode.102;
ret Encode.106;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.113 : List U8 = CallByName Json.77 Encode.94 Encode.96 Encode.102;
ret Encode.113;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.115 : List U8 = CallByName #Derived.7 Encode.94 Encode.96 Encode.102;
ret Encode.115;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.125 : List U8 = CallByName Json.77 Encode.94 Encode.96 Encode.102;
ret Encode.125;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.127 : List U8 = CallByName #Derived.12 Encode.94 Encode.96 Encode.102;
ret Encode.127;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.137 : List U8 = CallByName Json.65 Encode.94 Encode.96 Encode.102;
ret Encode.137;
procedure Encode.25 (Encode.100, Encode.101):
let Encode.104 : List U8 = Array [];
let Encode.105 : {Str} = CallByName #Derived.0 Encode.100;
let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101;
ret Encode.103;
procedure Json.1 ():
let Json.102 : {} = Struct {};
ret Json.102;
procedure Json.17 (Json.64):
let Json.186 : {Str} = Struct {Json.64};
let Json.185 : {Str} = CallByName Encode.22 Json.186;
ret Json.185;
procedure Json.19 (Json.76):
let Json.104 : {List {Str, {Str}}} = Struct {Json.76};
let Json.103 : {List {Str, {Str}}} = CallByName Encode.22 Json.104;
ret Json.103;
procedure Json.19 (Json.76):
let Json.146 : {List {Str, {Str}}} = Struct {Json.76};
let Json.145 : {List {Str, {Str}}} = CallByName Encode.22 Json.146;
ret Json.145;
procedure Json.65 (Json.66, Json.187, #Attr.12):
let Json.64 : Str = StructAtIndex 0 #Attr.12;
inc Json.64;
dec #Attr.12;
let Json.196 : I32 = 34i64;
let Json.195 : U8 = CallByName Num.123 Json.196;
let Json.193 : List U8 = CallByName List.4 Json.66 Json.195;
let Json.194 : List U8 = CallByName Str.12 Json.64;
let Json.190 : List U8 = CallByName List.8 Json.193 Json.194;
let Json.192 : I32 = 34i64;
let Json.191 : U8 = CallByName Num.123 Json.192;
let Json.189 : List U8 = CallByName List.4 Json.190 Json.191;
ret Json.189;
procedure Json.77 (Json.78, Json.105, #Attr.12):
let Json.76 : List {Str, {Str}} = StructAtIndex 0 #Attr.12;
inc Json.76;
dec #Attr.12;
let Json.138 : I32 = 123i64;
let Json.137 : U8 = CallByName Num.123 Json.138;
let Json.80 : List U8 = CallByName List.4 Json.78 Json.137;
let Json.136 : U64 = CallByName List.6 Json.76;
let Json.113 : {List U8, U64} = Struct {Json.80, Json.136};
let Json.114 : {} = Struct {};
let Json.112 : {List U8, U64} = CallByName List.18 Json.76 Json.113 Json.114;
dec Json.76;
let Json.82 : List U8 = StructAtIndex 0 Json.112;
inc Json.82;
dec Json.112;
let Json.111 : I32 = 125i64;
let Json.110 : U8 = CallByName Num.123 Json.111;
let Json.109 : List U8 = CallByName List.4 Json.82 Json.110;
ret Json.109;
procedure Json.77 (Json.78, Json.105, #Attr.12):
let Json.76 : List {Str, {Str}} = StructAtIndex 0 #Attr.12;
inc Json.76;
dec #Attr.12;
let Json.178 : I32 = 123i64;
let Json.177 : U8 = CallByName Num.123 Json.178;
let Json.80 : List U8 = CallByName List.4 Json.78 Json.177;
let Json.176 : U64 = CallByName List.6 Json.76;
let Json.153 : {List U8, U64} = Struct {Json.80, Json.176};
let Json.154 : {} = Struct {};
let Json.152 : {List U8, U64} = CallByName List.18 Json.76 Json.153 Json.154;
dec Json.76;
let Json.82 : List U8 = StructAtIndex 0 Json.152;
inc Json.82;
dec Json.152;
let Json.151 : I32 = 125i64;
let Json.150 : U8 = CallByName Num.123 Json.151;
let Json.149 : List U8 = CallByName List.4 Json.82 Json.150;
ret Json.149;
procedure Json.79 (Json.107, Json.108):
let Json.85 : Str = StructAtIndex 0 Json.108;
inc Json.85;
let Json.86 : {Str} = StructAtIndex 1 Json.108;
inc Json.86;
dec Json.108;
let Json.83 : List U8 = StructAtIndex 0 Json.107;
inc Json.83;
let Json.84 : U64 = StructAtIndex 1 Json.107;
dec Json.107;
let Json.135 : I32 = 34i64;
let Json.134 : U8 = CallByName Num.123 Json.135;
let Json.132 : List U8 = CallByName List.4 Json.83 Json.134;
let Json.133 : List U8 = CallByName Str.12 Json.85;
let Json.129 : List U8 = CallByName List.8 Json.132 Json.133;
let Json.131 : I32 = 34i64;
let Json.130 : U8 = CallByName Num.123 Json.131;
let Json.126 : List U8 = CallByName List.4 Json.129 Json.130;
let Json.128 : I32 = 58i64;
let Json.127 : U8 = CallByName Num.123 Json.128;
let Json.124 : List U8 = CallByName List.4 Json.126 Json.127;
let Json.125 : {} = Struct {};
let Json.87 : List U8 = CallByName Encode.23 Json.124 Json.86 Json.125;
joinpoint Json.119 Json.88:
let Json.117 : U64 = 1i64;
let Json.116 : U64 = CallByName Num.20 Json.84 Json.117;
let Json.115 : {List U8, U64} = Struct {Json.88, Json.116};
ret Json.115;
in
let Json.123 : U64 = 0i64;
let Json.120 : Int1 = CallByName Num.24 Json.84 Json.123;
if Json.120 then
let Json.122 : I32 = 44i64;
let Json.121 : U8 = CallByName Num.123 Json.122;
let Json.118 : List U8 = CallByName List.4 Json.87 Json.121;
jump Json.119 Json.118;
else
jump Json.119 Json.87;
procedure Json.79 (Json.107, Json.108):
let Json.85 : Str = StructAtIndex 0 Json.108;
inc Json.85;
let Json.86 : {Str} = StructAtIndex 1 Json.108;
inc Json.86;
dec Json.108;
let Json.83 : List U8 = StructAtIndex 0 Json.107;
inc Json.83;
let Json.84 : U64 = StructAtIndex 1 Json.107;
dec Json.107;
let Json.175 : I32 = 34i64;
let Json.174 : U8 = CallByName Num.123 Json.175;
let Json.172 : List U8 = CallByName List.4 Json.83 Json.174;
let Json.173 : List U8 = CallByName Str.12 Json.85;
let Json.169 : List U8 = CallByName List.8 Json.172 Json.173;
let Json.171 : I32 = 34i64;
let Json.170 : U8 = CallByName Num.123 Json.171;
let Json.166 : List U8 = CallByName List.4 Json.169 Json.170;
let Json.168 : I32 = 58i64;
let Json.167 : U8 = CallByName Num.123 Json.168;
let Json.164 : List U8 = CallByName List.4 Json.166 Json.167;
let Json.165 : {} = Struct {};
let Json.87 : List U8 = CallByName Encode.23 Json.164 Json.86 Json.165;
joinpoint Json.159 Json.88:
let Json.157 : U64 = 1i64;
let Json.156 : U64 = CallByName Num.20 Json.84 Json.157;
let Json.155 : {List U8, U64} = Struct {Json.88, Json.156};
ret Json.155;
in
let Json.163 : U64 = 0i64;
let Json.160 : Int1 = CallByName Num.24 Json.84 Json.163;
if Json.160 then
let Json.162 : I32 = 44i64;
let Json.161 : U8 = CallByName Num.123 Json.162;
let Json.158 : List U8 = CallByName List.4 Json.87 Json.161;
jump Json.159 Json.158;
else
jump Json.159 Json.87;
procedure List.121 (List.122, List.123, #Attr.12):
let List.120 : {} = StructAtIndex 0 #Attr.12;
let List.344 : {List U8, U64} = CallByName Json.79 List.122 List.123;
let List.343 : [C [], C {List U8, U64}] = TagId(1) List.344;
ret List.343;
procedure List.121 (List.122, List.123, #Attr.12):
let List.120 : {} = StructAtIndex 0 #Attr.12;
let List.418 : {List U8, U64} = CallByName Json.79 List.122 List.123;
let List.417 : [C [], C {List U8, U64}] = TagId(1) List.418;
ret List.417;
procedure List.18 (List.118, List.119, List.120):
let List.321 : {{}} = Struct {List.120};
let List.315 : [C [], C {List U8, U64}] = CallByName List.63 List.118 List.119 List.321;
let List.318 : U8 = 1i64;
let List.319 : U8 = GetTagId List.315;
let List.320 : Int1 = lowlevel Eq List.318 List.319;
if List.320 then
let List.125 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.315;
inc List.125;
dec List.315;
ret List.125;
else
let List.126 : [] = UnionAtIndex (Id 0) (Index 0) List.315;
dec List.315;
let List.317 : {List U8, U64} = CallByName List.64 List.126;
ret List.317;
procedure List.18 (List.118, List.119, List.120):
let List.395 : {{}} = Struct {List.120};
let List.389 : [C [], C {List U8, U64}] = CallByName List.63 List.118 List.119 List.395;
let List.392 : U8 = 1i64;
let List.393 : U8 = GetTagId List.389;
let List.394 : Int1 = lowlevel Eq List.392 List.393;
if List.394 then
let List.125 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.389;
inc List.125;
dec List.389;
ret List.125;
else
let List.126 : [] = UnionAtIndex (Id 0) (Index 0) List.389;
dec List.389;
let List.391 : {List U8, U64} = CallByName List.64 List.126;
ret List.391;
procedure List.4 (List.89, List.90):
let List.450 : U64 = 1i64;
let List.449 : List U8 = CallByName List.65 List.89 List.450;
let List.448 : List U8 = CallByName List.66 List.449 List.90;
ret List.448;
procedure List.6 (#Attr.2):
let List.295 : U64 = lowlevel ListLen #Attr.2;
ret List.295;
procedure List.6 (#Attr.2):
let List.323 : U64 = lowlevel ListLen #Attr.2;
ret List.323;
procedure List.6 (#Attr.2):
let List.397 : U64 = lowlevel ListLen #Attr.2;
ret List.397;
procedure List.60 (#Attr.2, #Attr.3):
let List.342 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.342;
procedure List.60 (#Attr.2, #Attr.3):
let List.416 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.416;
procedure List.63 (List.283, List.284, List.285):
let List.328 : U64 = 0i64;
let List.329 : U64 = CallByName List.6 List.283;
let List.327 : [C [], C {List U8, U64}] = CallByName List.76 List.283 List.284 List.285 List.328 List.329;
ret List.327;
procedure List.63 (List.283, List.284, List.285):
let List.402 : U64 = 0i64;
let List.403 : U64 = CallByName List.6 List.283;
let List.401 : [C [], C {List U8, U64}] = CallByName List.76 List.283 List.284 List.285 List.402 List.403;
ret List.401;
procedure List.64 (#Attr.2):
let List.400 : {List U8, U64} = lowlevel Unreachable #Attr.2;
ret List.400;
procedure List.65 (#Attr.2, #Attr.3):
let List.453 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
ret List.453;
procedure List.66 (#Attr.2, #Attr.3):
let List.452 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.452;
procedure List.76 (List.361, List.362, List.363, List.364, List.365):
joinpoint List.330 List.286 List.287 List.288 List.289 List.290:
let List.332 : Int1 = CallByName Num.22 List.289 List.290;
if List.332 then
let List.341 : {Str, {Str}} = CallByName List.60 List.286 List.289;
let List.333 : [C [], C {List U8, U64}] = CallByName List.121 List.287 List.341 List.288;
let List.338 : U8 = 1i64;
let List.339 : U8 = GetTagId List.333;
let List.340 : Int1 = lowlevel Eq List.338 List.339;
if List.340 then
let List.291 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.333;
inc List.291;
dec List.333;
let List.336 : U64 = 1i64;
let List.335 : U64 = CallByName Num.19 List.289 List.336;
jump List.330 List.286 List.291 List.288 List.335 List.290;
else
let List.292 : [] = UnionAtIndex (Id 0) (Index 0) List.333;
dec List.333;
let List.337 : [C [], C {List U8, U64}] = TagId(0) List.292;
ret List.337;
else
let List.331 : [C [], C {List U8, U64}] = TagId(1) List.287;
ret List.331;
in
jump List.330 List.361 List.362 List.363 List.364 List.365;
procedure List.76 (List.435, List.436, List.437, List.438, List.439):
joinpoint List.404 List.286 List.287 List.288 List.289 List.290:
let List.406 : Int1 = CallByName Num.22 List.289 List.290;
if List.406 then
let List.415 : {Str, {Str}} = CallByName List.60 List.286 List.289;
let List.407 : [C [], C {List U8, U64}] = CallByName List.121 List.287 List.415 List.288;
let List.412 : U8 = 1i64;
let List.413 : U8 = GetTagId List.407;
let List.414 : Int1 = lowlevel Eq List.412 List.413;
if List.414 then
let List.291 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.407;
inc List.291;
dec List.407;
let List.410 : U64 = 1i64;
let List.409 : U64 = CallByName Num.19 List.289 List.410;
jump List.404 List.286 List.291 List.288 List.409 List.290;
else
let List.292 : [] = UnionAtIndex (Id 0) (Index 0) List.407;
dec List.407;
let List.411 : [C [], C {List U8, U64}] = TagId(0) List.292;
ret List.411;
else
let List.405 : [C [], C {List U8, U64}] = TagId(1) List.287;
ret List.405;
in
jump List.404 List.435 List.436 List.437 List.438 List.439;
procedure List.8 (#Attr.2, #Attr.3):
let List.451 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.451;
procedure Num.123 (#Attr.2):
let Num.296 : U8 = lowlevel NumIntCast #Attr.2;
ret Num.296;
procedure Num.19 (#Attr.2, #Attr.3):
let Num.284 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.284;
procedure Num.20 (#Attr.2, #Attr.3):
let Num.282 : U64 = lowlevel NumSub #Attr.2 #Attr.3;
ret Num.282;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.285 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.285;
procedure Num.24 (#Attr.2, #Attr.3):
let Num.283 : Int1 = lowlevel NumGt #Attr.2 #Attr.3;
ret Num.283;
procedure Str.12 (#Attr.2):
let Str.212 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.212;
procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
let Str.203 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
ret Str.203;
procedure Str.9 (Str.68):
let Str.201 : U64 = 0i64;
let Str.202 : U64 = CallByName List.6 Str.68;
let Str.69 : {U64, Str, Int1, U8} = CallByName Str.48 Str.68 Str.201 Str.202;
let Str.198 : Int1 = StructAtIndex 2 Str.69;
if Str.198 then
let Str.200 : Str = StructAtIndex 1 Str.69;
inc Str.200;
dec Str.69;
let Str.199 : [C {U64, U8}, C Str] = TagId(1) Str.200;
ret Str.199;
else
let Str.196 : U8 = StructAtIndex 3 Str.69;
let Str.197 : U64 = StructAtIndex 0 Str.69;
dec Str.69;
let Str.195 : {U64, U8} = Struct {Str.197, Str.196};
let Str.194 : [C {U64, U8}, C Str] = TagId(0) Str.195;
ret Str.194;
procedure Test.0 ():
let Test.12 : Str = "bar";
let Test.10 : {} = CallByName Json.1;
let Test.8 : List U8 = CallByName Encode.25 Test.12 Test.10;
let Test.1 : [C {U64, U8}, C Str] = CallByName Str.9 Test.8;
dec Test.8;
let Test.5 : U8 = 1i64;
let Test.6 : U8 = GetTagId Test.1;
let Test.7 : Int1 = lowlevel Eq Test.5 Test.6;
if Test.7 then
let Test.2 : Str = UnionAtIndex (Id 1) (Index 0) Test.1;
inc Test.2;
dec Test.1;
ret Test.2;
else
dec Test.1;
let Test.4 : Str = "<bad>";
ret Test.4;

View file

@ -0,0 +1,306 @@
procedure #Derived.0 (#Derived.1):
let #Derived_gen.1 : {Str} = Struct {#Derived.1};
let #Derived_gen.0 : {Str} = CallByName Encode.22 #Derived_gen.1;
ret #Derived_gen.0;
procedure #Derived.2 (#Derived.3, #Derived.4, #Attr.12):
let #Derived.1 : Str = StructAtIndex 0 #Attr.12;
inc #Derived.1;
dec #Attr.12;
let #Derived_gen.7 : Str = "a";
let #Derived_gen.8 : {Str} = CallByName #Derived.5 #Derived.1;
let #Derived_gen.6 : {Str, {Str}} = Struct {#Derived_gen.7, #Derived_gen.8};
let #Derived_gen.5 : List {Str, {Str}} = Array [#Derived_gen.6];
let #Derived_gen.4 : {List {Str, {Str}}} = CallByName Json.19 #Derived_gen.5;
let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.3 #Derived_gen.4 #Derived.4;
ret #Derived_gen.3;
procedure #Derived.5 (#Derived.6):
let #Derived_gen.15 : {Str} = Struct {#Derived.6};
let #Derived_gen.14 : {Str} = CallByName Encode.22 #Derived_gen.15;
ret #Derived_gen.14;
procedure #Derived.7 (#Derived.8, #Derived.9, #Attr.12):
let #Derived.6 : Str = StructAtIndex 0 #Attr.12;
inc #Derived.6;
dec #Attr.12;
let #Derived_gen.18 : {Str} = CallByName Json.17 #Derived.6;
let #Derived_gen.17 : List U8 = CallByName Encode.23 #Derived.8 #Derived_gen.18 #Derived.9;
ret #Derived_gen.17;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.106 : List U8 = CallByName #Derived.2 Encode.94 Encode.96 Encode.102;
ret Encode.106;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.113 : List U8 = CallByName Json.77 Encode.94 Encode.96 Encode.102;
ret Encode.113;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.115 : List U8 = CallByName #Derived.7 Encode.94 Encode.96 Encode.102;
ret Encode.115;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.125 : List U8 = CallByName Json.65 Encode.94 Encode.96 Encode.102;
ret Encode.125;
procedure Encode.25 (Encode.100, Encode.101):
let Encode.104 : List U8 = Array [];
let Encode.105 : {Str} = CallByName #Derived.0 Encode.100;
let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101;
ret Encode.103;
procedure Json.1 ():
let Json.102 : {} = Struct {};
ret Json.102;
procedure Json.17 (Json.64):
let Json.146 : {Str} = Struct {Json.64};
let Json.145 : {Str} = CallByName Encode.22 Json.146;
ret Json.145;
procedure Json.19 (Json.76):
let Json.104 : {List {Str, {Str}}} = Struct {Json.76};
let Json.103 : {List {Str, {Str}}} = CallByName Encode.22 Json.104;
ret Json.103;
procedure Json.65 (Json.66, Json.147, #Attr.12):
let Json.64 : Str = StructAtIndex 0 #Attr.12;
inc Json.64;
dec #Attr.12;
let Json.156 : I32 = 34i64;
let Json.155 : U8 = CallByName Num.123 Json.156;
let Json.153 : List U8 = CallByName List.4 Json.66 Json.155;
let Json.154 : List U8 = CallByName Str.12 Json.64;
let Json.150 : List U8 = CallByName List.8 Json.153 Json.154;
let Json.152 : I32 = 34i64;
let Json.151 : U8 = CallByName Num.123 Json.152;
let Json.149 : List U8 = CallByName List.4 Json.150 Json.151;
ret Json.149;
procedure Json.77 (Json.78, Json.105, #Attr.12):
let Json.76 : List {Str, {Str}} = StructAtIndex 0 #Attr.12;
inc Json.76;
dec #Attr.12;
let Json.138 : I32 = 123i64;
let Json.137 : U8 = CallByName Num.123 Json.138;
let Json.80 : List U8 = CallByName List.4 Json.78 Json.137;
let Json.136 : U64 = CallByName List.6 Json.76;
let Json.113 : {List U8, U64} = Struct {Json.80, Json.136};
let Json.114 : {} = Struct {};
let Json.112 : {List U8, U64} = CallByName List.18 Json.76 Json.113 Json.114;
dec Json.76;
let Json.82 : List U8 = StructAtIndex 0 Json.112;
inc Json.82;
dec Json.112;
let Json.111 : I32 = 125i64;
let Json.110 : U8 = CallByName Num.123 Json.111;
let Json.109 : List U8 = CallByName List.4 Json.82 Json.110;
ret Json.109;
procedure Json.79 (Json.107, Json.108):
let Json.85 : Str = StructAtIndex 0 Json.108;
inc Json.85;
let Json.86 : {Str} = StructAtIndex 1 Json.108;
inc Json.86;
dec Json.108;
let Json.83 : List U8 = StructAtIndex 0 Json.107;
inc Json.83;
let Json.84 : U64 = StructAtIndex 1 Json.107;
dec Json.107;
let Json.135 : I32 = 34i64;
let Json.134 : U8 = CallByName Num.123 Json.135;
let Json.132 : List U8 = CallByName List.4 Json.83 Json.134;
let Json.133 : List U8 = CallByName Str.12 Json.85;
let Json.129 : List U8 = CallByName List.8 Json.132 Json.133;
let Json.131 : I32 = 34i64;
let Json.130 : U8 = CallByName Num.123 Json.131;
let Json.126 : List U8 = CallByName List.4 Json.129 Json.130;
let Json.128 : I32 = 58i64;
let Json.127 : U8 = CallByName Num.123 Json.128;
let Json.124 : List U8 = CallByName List.4 Json.126 Json.127;
let Json.125 : {} = Struct {};
let Json.87 : List U8 = CallByName Encode.23 Json.124 Json.86 Json.125;
joinpoint Json.119 Json.88:
let Json.117 : U64 = 1i64;
let Json.116 : U64 = CallByName Num.20 Json.84 Json.117;
let Json.115 : {List U8, U64} = Struct {Json.88, Json.116};
ret Json.115;
in
let Json.123 : U64 = 0i64;
let Json.120 : Int1 = CallByName Num.24 Json.84 Json.123;
if Json.120 then
let Json.122 : I32 = 44i64;
let Json.121 : U8 = CallByName Num.123 Json.122;
let Json.118 : List U8 = CallByName List.4 Json.87 Json.121;
jump Json.119 Json.118;
else
jump Json.119 Json.87;
procedure List.121 (List.122, List.123, #Attr.12):
let List.120 : {} = StructAtIndex 0 #Attr.12;
let List.344 : {List U8, U64} = CallByName Json.79 List.122 List.123;
let List.343 : [C [], C {List U8, U64}] = TagId(1) List.344;
ret List.343;
procedure List.18 (List.118, List.119, List.120):
let List.321 : {{}} = Struct {List.120};
let List.315 : [C [], C {List U8, U64}] = CallByName List.63 List.118 List.119 List.321;
let List.318 : U8 = 1i64;
let List.319 : U8 = GetTagId List.315;
let List.320 : Int1 = lowlevel Eq List.318 List.319;
if List.320 then
let List.125 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.315;
inc List.125;
dec List.315;
ret List.125;
else
let List.126 : [] = UnionAtIndex (Id 0) (Index 0) List.315;
dec List.315;
let List.317 : {List U8, U64} = CallByName List.64 List.126;
ret List.317;
procedure List.4 (List.89, List.90):
let List.376 : U64 = 1i64;
let List.375 : List U8 = CallByName List.65 List.89 List.376;
let List.374 : List U8 = CallByName List.66 List.375 List.90;
ret List.374;
procedure List.6 (#Attr.2):
let List.295 : U64 = lowlevel ListLen #Attr.2;
ret List.295;
procedure List.6 (#Attr.2):
let List.323 : U64 = lowlevel ListLen #Attr.2;
ret List.323;
procedure List.60 (#Attr.2, #Attr.3):
let List.342 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.342;
procedure List.63 (List.283, List.284, List.285):
let List.328 : U64 = 0i64;
let List.329 : U64 = CallByName List.6 List.283;
let List.327 : [C [], C {List U8, U64}] = CallByName List.76 List.283 List.284 List.285 List.328 List.329;
ret List.327;
procedure List.64 (#Attr.2):
let List.326 : {List U8, U64} = lowlevel Unreachable #Attr.2;
ret List.326;
procedure List.65 (#Attr.2, #Attr.3):
let List.379 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
ret List.379;
procedure List.66 (#Attr.2, #Attr.3):
let List.378 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.378;
procedure List.76 (List.361, List.362, List.363, List.364, List.365):
joinpoint List.330 List.286 List.287 List.288 List.289 List.290:
let List.332 : Int1 = CallByName Num.22 List.289 List.290;
if List.332 then
let List.341 : {Str, {Str}} = CallByName List.60 List.286 List.289;
let List.333 : [C [], C {List U8, U64}] = CallByName List.121 List.287 List.341 List.288;
let List.338 : U8 = 1i64;
let List.339 : U8 = GetTagId List.333;
let List.340 : Int1 = lowlevel Eq List.338 List.339;
if List.340 then
let List.291 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.333;
inc List.291;
dec List.333;
let List.336 : U64 = 1i64;
let List.335 : U64 = CallByName Num.19 List.289 List.336;
jump List.330 List.286 List.291 List.288 List.335 List.290;
else
let List.292 : [] = UnionAtIndex (Id 0) (Index 0) List.333;
dec List.333;
let List.337 : [C [], C {List U8, U64}] = TagId(0) List.292;
ret List.337;
else
let List.331 : [C [], C {List U8, U64}] = TagId(1) List.287;
ret List.331;
in
jump List.330 List.361 List.362 List.363 List.364 List.365;
procedure List.8 (#Attr.2, #Attr.3):
let List.377 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.377;
procedure Num.123 (#Attr.2):
let Num.277 : U8 = lowlevel NumIntCast #Attr.2;
ret Num.277;
procedure Num.19 (#Attr.2, #Attr.3):
let Num.265 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.265;
procedure Num.20 (#Attr.2, #Attr.3):
let Num.263 : U64 = lowlevel NumSub #Attr.2 #Attr.3;
ret Num.263;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.266 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.266;
procedure Num.24 (#Attr.2, #Attr.3):
let Num.264 : Int1 = lowlevel NumGt #Attr.2 #Attr.3;
ret Num.264;
procedure Str.12 (#Attr.2):
let Str.210 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.210;
procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
let Str.203 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
ret Str.203;
procedure Str.9 (Str.68):
let Str.201 : U64 = 0i64;
let Str.202 : U64 = CallByName List.6 Str.68;
let Str.69 : {U64, Str, Int1, U8} = CallByName Str.48 Str.68 Str.201 Str.202;
let Str.198 : Int1 = StructAtIndex 2 Str.69;
if Str.198 then
let Str.200 : Str = StructAtIndex 1 Str.69;
inc Str.200;
dec Str.69;
let Str.199 : [C {U64, U8}, C Str] = TagId(1) Str.200;
ret Str.199;
else
let Str.196 : U8 = StructAtIndex 3 Str.69;
let Str.197 : U64 = StructAtIndex 0 Str.69;
dec Str.69;
let Str.195 : {U64, U8} = Struct {Str.197, Str.196};
let Str.194 : [C {U64, U8}, C Str] = TagId(0) Str.195;
ret Str.194;
procedure Test.0 ():
let Test.11 : Str = "foo";
let Test.10 : {} = CallByName Json.1;
let Test.8 : List U8 = CallByName Encode.25 Test.11 Test.10;
let Test.1 : [C {U64, U8}, C Str] = CallByName Str.9 Test.8;
dec Test.8;
let Test.5 : U8 = 1i64;
let Test.6 : U8 = GetTagId Test.1;
let Test.7 : Int1 = lowlevel Eq Test.5 Test.6;
if Test.7 then
let Test.2 : Str = UnionAtIndex (Id 1) (Index 0) Test.1;
inc Test.2;
dec Test.1;
ret Test.2;
else
dec Test.1;
let Test.4 : Str = "<bad>";
ret Test.4;

View file

@ -0,0 +1,316 @@
procedure #Derived.0 (#Derived.1):
let #Derived_gen.1 : {{Str, Str}} = Struct {#Derived.1};
let #Derived_gen.0 : {{Str, Str}} = CallByName Encode.22 #Derived_gen.1;
ret #Derived_gen.0;
procedure #Derived.2 (#Derived.3, #Derived.4, #Attr.12):
let #Derived.1 : {Str, Str} = StructAtIndex 0 #Attr.12;
inc #Derived.1;
dec #Attr.12;
let #Derived_gen.11 : Str = "a";
let #Derived_gen.13 : Str = StructAtIndex 0 #Derived.1;
inc #Derived_gen.13;
let #Derived_gen.12 : {Str} = CallByName #Derived.5 #Derived_gen.13;
let #Derived_gen.6 : {Str, {Str}} = Struct {#Derived_gen.11, #Derived_gen.12};
let #Derived_gen.8 : Str = "b";
let #Derived_gen.10 : Str = StructAtIndex 1 #Derived.1;
inc #Derived_gen.10;
dec #Derived.1;
let #Derived_gen.9 : {Str} = CallByName #Derived.5 #Derived_gen.10;
let #Derived_gen.7 : {Str, {Str}} = Struct {#Derived_gen.8, #Derived_gen.9};
let #Derived_gen.5 : List {Str, {Str}} = Array [#Derived_gen.6, #Derived_gen.7];
let #Derived_gen.4 : {List {Str, {Str}}} = CallByName Json.19 #Derived_gen.5;
let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.3 #Derived_gen.4 #Derived.4;
ret #Derived_gen.3;
procedure #Derived.5 (#Derived.6):
let #Derived_gen.21 : {Str} = Struct {#Derived.6};
let #Derived_gen.20 : {Str} = CallByName Encode.22 #Derived_gen.21;
ret #Derived_gen.20;
procedure #Derived.7 (#Derived.8, #Derived.9, #Attr.12):
let #Derived.6 : Str = StructAtIndex 0 #Attr.12;
inc #Derived.6;
dec #Attr.12;
let #Derived_gen.24 : {Str} = CallByName Json.17 #Derived.6;
let #Derived_gen.23 : List U8 = CallByName Encode.23 #Derived.8 #Derived_gen.24 #Derived.9;
ret #Derived_gen.23;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.106 : List U8 = CallByName #Derived.2 Encode.94 Encode.96 Encode.102;
ret Encode.106;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.113 : List U8 = CallByName Json.77 Encode.94 Encode.96 Encode.102;
ret Encode.113;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.115 : List U8 = CallByName #Derived.7 Encode.94 Encode.96 Encode.102;
ret Encode.115;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.126 : List U8 = CallByName Json.65 Encode.94 Encode.96 Encode.102;
ret Encode.126;
procedure Encode.25 (Encode.100, Encode.101):
let Encode.104 : List U8 = Array [];
let Encode.105 : {{Str, Str}} = CallByName #Derived.0 Encode.100;
let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101;
ret Encode.103;
procedure Json.1 ():
let Json.102 : {} = Struct {};
ret Json.102;
procedure Json.17 (Json.64):
let Json.146 : {Str} = Struct {Json.64};
let Json.145 : {Str} = CallByName Encode.22 Json.146;
ret Json.145;
procedure Json.19 (Json.76):
let Json.104 : {List {Str, {Str}}} = Struct {Json.76};
let Json.103 : {List {Str, {Str}}} = CallByName Encode.22 Json.104;
ret Json.103;
procedure Json.65 (Json.66, Json.147, #Attr.12):
let Json.64 : Str = StructAtIndex 0 #Attr.12;
inc Json.64;
dec #Attr.12;
let Json.156 : I32 = 34i64;
let Json.155 : U8 = CallByName Num.123 Json.156;
let Json.153 : List U8 = CallByName List.4 Json.66 Json.155;
let Json.154 : List U8 = CallByName Str.12 Json.64;
let Json.150 : List U8 = CallByName List.8 Json.153 Json.154;
let Json.152 : I32 = 34i64;
let Json.151 : U8 = CallByName Num.123 Json.152;
let Json.149 : List U8 = CallByName List.4 Json.150 Json.151;
ret Json.149;
procedure Json.77 (Json.78, Json.105, #Attr.12):
let Json.76 : List {Str, {Str}} = StructAtIndex 0 #Attr.12;
inc Json.76;
dec #Attr.12;
let Json.138 : I32 = 123i64;
let Json.137 : U8 = CallByName Num.123 Json.138;
let Json.80 : List U8 = CallByName List.4 Json.78 Json.137;
let Json.136 : U64 = CallByName List.6 Json.76;
let Json.113 : {List U8, U64} = Struct {Json.80, Json.136};
let Json.114 : {} = Struct {};
let Json.112 : {List U8, U64} = CallByName List.18 Json.76 Json.113 Json.114;
dec Json.76;
let Json.82 : List U8 = StructAtIndex 0 Json.112;
inc Json.82;
dec Json.112;
let Json.111 : I32 = 125i64;
let Json.110 : U8 = CallByName Num.123 Json.111;
let Json.109 : List U8 = CallByName List.4 Json.82 Json.110;
ret Json.109;
procedure Json.79 (Json.107, Json.108):
let Json.85 : Str = StructAtIndex 0 Json.108;
inc Json.85;
let Json.86 : {Str} = StructAtIndex 1 Json.108;
inc Json.86;
dec Json.108;
let Json.83 : List U8 = StructAtIndex 0 Json.107;
inc Json.83;
let Json.84 : U64 = StructAtIndex 1 Json.107;
dec Json.107;
let Json.135 : I32 = 34i64;
let Json.134 : U8 = CallByName Num.123 Json.135;
let Json.132 : List U8 = CallByName List.4 Json.83 Json.134;
let Json.133 : List U8 = CallByName Str.12 Json.85;
let Json.129 : List U8 = CallByName List.8 Json.132 Json.133;
let Json.131 : I32 = 34i64;
let Json.130 : U8 = CallByName Num.123 Json.131;
let Json.126 : List U8 = CallByName List.4 Json.129 Json.130;
let Json.128 : I32 = 58i64;
let Json.127 : U8 = CallByName Num.123 Json.128;
let Json.124 : List U8 = CallByName List.4 Json.126 Json.127;
let Json.125 : {} = Struct {};
let Json.87 : List U8 = CallByName Encode.23 Json.124 Json.86 Json.125;
joinpoint Json.119 Json.88:
let Json.117 : U64 = 1i64;
let Json.116 : U64 = CallByName Num.20 Json.84 Json.117;
let Json.115 : {List U8, U64} = Struct {Json.88, Json.116};
ret Json.115;
in
let Json.123 : U64 = 0i64;
let Json.120 : Int1 = CallByName Num.24 Json.84 Json.123;
if Json.120 then
let Json.122 : I32 = 44i64;
let Json.121 : U8 = CallByName Num.123 Json.122;
let Json.118 : List U8 = CallByName List.4 Json.87 Json.121;
jump Json.119 Json.118;
else
jump Json.119 Json.87;
procedure List.121 (List.122, List.123, #Attr.12):
let List.120 : {} = StructAtIndex 0 #Attr.12;
let List.344 : {List U8, U64} = CallByName Json.79 List.122 List.123;
let List.343 : [C [], C {List U8, U64}] = TagId(1) List.344;
ret List.343;
procedure List.18 (List.118, List.119, List.120):
let List.321 : {{}} = Struct {List.120};
let List.315 : [C [], C {List U8, U64}] = CallByName List.63 List.118 List.119 List.321;
let List.318 : U8 = 1i64;
let List.319 : U8 = GetTagId List.315;
let List.320 : Int1 = lowlevel Eq List.318 List.319;
if List.320 then
let List.125 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.315;
inc List.125;
dec List.315;
ret List.125;
else
let List.126 : [] = UnionAtIndex (Id 0) (Index 0) List.315;
dec List.315;
let List.317 : {List U8, U64} = CallByName List.64 List.126;
ret List.317;
procedure List.4 (List.89, List.90):
let List.376 : U64 = 1i64;
let List.375 : List U8 = CallByName List.65 List.89 List.376;
let List.374 : List U8 = CallByName List.66 List.375 List.90;
ret List.374;
procedure List.6 (#Attr.2):
let List.295 : U64 = lowlevel ListLen #Attr.2;
ret List.295;
procedure List.6 (#Attr.2):
let List.323 : U64 = lowlevel ListLen #Attr.2;
ret List.323;
procedure List.60 (#Attr.2, #Attr.3):
let List.342 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.342;
procedure List.63 (List.283, List.284, List.285):
let List.328 : U64 = 0i64;
let List.329 : U64 = CallByName List.6 List.283;
let List.327 : [C [], C {List U8, U64}] = CallByName List.76 List.283 List.284 List.285 List.328 List.329;
ret List.327;
procedure List.64 (#Attr.2):
let List.326 : {List U8, U64} = lowlevel Unreachable #Attr.2;
ret List.326;
procedure List.65 (#Attr.2, #Attr.3):
let List.379 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
ret List.379;
procedure List.66 (#Attr.2, #Attr.3):
let List.378 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.378;
procedure List.76 (List.361, List.362, List.363, List.364, List.365):
joinpoint List.330 List.286 List.287 List.288 List.289 List.290:
let List.332 : Int1 = CallByName Num.22 List.289 List.290;
if List.332 then
let List.341 : {Str, {Str}} = CallByName List.60 List.286 List.289;
let List.333 : [C [], C {List U8, U64}] = CallByName List.121 List.287 List.341 List.288;
let List.338 : U8 = 1i64;
let List.339 : U8 = GetTagId List.333;
let List.340 : Int1 = lowlevel Eq List.338 List.339;
if List.340 then
let List.291 : {List U8, U64} = UnionAtIndex (Id 1) (Index 0) List.333;
inc List.291;
dec List.333;
let List.336 : U64 = 1i64;
let List.335 : U64 = CallByName Num.19 List.289 List.336;
jump List.330 List.286 List.291 List.288 List.335 List.290;
else
let List.292 : [] = UnionAtIndex (Id 0) (Index 0) List.333;
dec List.333;
let List.337 : [C [], C {List U8, U64}] = TagId(0) List.292;
ret List.337;
else
let List.331 : [C [], C {List U8, U64}] = TagId(1) List.287;
ret List.331;
in
jump List.330 List.361 List.362 List.363 List.364 List.365;
procedure List.8 (#Attr.2, #Attr.3):
let List.377 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.377;
procedure Num.123 (#Attr.2):
let Num.277 : U8 = lowlevel NumIntCast #Attr.2;
ret Num.277;
procedure Num.19 (#Attr.2, #Attr.3):
let Num.265 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.265;
procedure Num.20 (#Attr.2, #Attr.3):
let Num.263 : U64 = lowlevel NumSub #Attr.2 #Attr.3;
ret Num.263;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.266 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.266;
procedure Num.24 (#Attr.2, #Attr.3):
let Num.264 : Int1 = lowlevel NumGt #Attr.2 #Attr.3;
ret Num.264;
procedure Str.12 (#Attr.2):
let Str.210 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.210;
procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
let Str.203 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
ret Str.203;
procedure Str.9 (Str.68):
let Str.201 : U64 = 0i64;
let Str.202 : U64 = CallByName List.6 Str.68;
let Str.69 : {U64, Str, Int1, U8} = CallByName Str.48 Str.68 Str.201 Str.202;
let Str.198 : Int1 = StructAtIndex 2 Str.69;
if Str.198 then
let Str.200 : Str = StructAtIndex 1 Str.69;
inc Str.200;
dec Str.69;
let Str.199 : [C {U64, U8}, C Str] = TagId(1) Str.200;
ret Str.199;
else
let Str.196 : U8 = StructAtIndex 3 Str.69;
let Str.197 : U64 = StructAtIndex 0 Str.69;
dec Str.69;
let Str.195 : {U64, U8} = Struct {Str.197, Str.196};
let Str.194 : [C {U64, U8}, C Str] = TagId(0) Str.195;
ret Str.194;
procedure Test.0 ():
let Test.11 : Str = "foo";
let Test.12 : Str = "bar";
let Test.9 : {Str, Str} = Struct {Test.11, Test.12};
let Test.10 : {} = CallByName Json.1;
let Test.8 : List U8 = CallByName Encode.25 Test.9 Test.10;
let Test.1 : [C {U64, U8}, C Str] = CallByName Str.9 Test.8;
dec Test.8;
let Test.5 : U8 = 1i64;
let Test.6 : U8 = GetTagId Test.1;
let Test.7 : Int1 = lowlevel Eq Test.5 Test.6;
if Test.7 then
let Test.2 : Str = UnionAtIndex (Id 1) (Index 0) Test.1;
inc Test.2;
dec Test.1;
ret Test.2;
else
dec Test.1;
let Test.4 : Str = "<bad>";
ret Test.4;

View file

@ -0,0 +1,127 @@
procedure #Derived.0 (#Derived.1):
let #Derived_gen.1 : {Str} = Struct {#Derived.1};
let #Derived_gen.0 : {Str} = CallByName Encode.22 #Derived_gen.1;
ret #Derived_gen.0;
procedure #Derived.2 (#Derived.3, #Derived.4, #Attr.12):
let #Derived.1 : Str = StructAtIndex 0 #Attr.12;
inc #Derived.1;
dec #Attr.12;
let #Derived_gen.4 : {Str} = CallByName Json.17 #Derived.1;
let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.3 #Derived_gen.4 #Derived.4;
ret #Derived_gen.3;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.106 : List U8 = CallByName #Derived.2 Encode.94 Encode.96 Encode.102;
ret Encode.106;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.113 : List U8 = CallByName Json.65 Encode.94 Encode.96 Encode.102;
ret Encode.113;
procedure Encode.25 (Encode.100, Encode.101):
let Encode.104 : List U8 = Array [];
let Encode.105 : {Str} = CallByName #Derived.0 Encode.100;
let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101;
ret Encode.103;
procedure Json.1 ():
let Json.102 : {} = Struct {};
ret Json.102;
procedure Json.17 (Json.64):
let Json.104 : {Str} = Struct {Json.64};
let Json.103 : {Str} = CallByName Encode.22 Json.104;
ret Json.103;
procedure Json.65 (Json.66, Json.105, #Attr.12):
let Json.64 : Str = StructAtIndex 0 #Attr.12;
inc Json.64;
dec #Attr.12;
let Json.114 : I32 = 34i64;
let Json.113 : U8 = CallByName Num.123 Json.114;
let Json.111 : List U8 = CallByName List.4 Json.66 Json.113;
let Json.112 : List U8 = CallByName Str.12 Json.64;
let Json.108 : List U8 = CallByName List.8 Json.111 Json.112;
let Json.110 : I32 = 34i64;
let Json.109 : U8 = CallByName Num.123 Json.110;
let Json.107 : List U8 = CallByName List.4 Json.108 Json.109;
ret Json.107;
procedure List.4 (List.89, List.90):
let List.302 : U64 = 1i64;
let List.301 : List U8 = CallByName List.65 List.89 List.302;
let List.300 : List U8 = CallByName List.66 List.301 List.90;
ret List.300;
procedure List.6 (#Attr.2):
let List.295 : U64 = lowlevel ListLen #Attr.2;
ret List.295;
procedure List.65 (#Attr.2, #Attr.3):
let List.305 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
ret List.305;
procedure List.66 (#Attr.2, #Attr.3):
let List.304 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.304;
procedure List.8 (#Attr.2, #Attr.3):
let List.303 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.303;
procedure Num.123 (#Attr.2):
let Num.258 : U8 = lowlevel NumIntCast #Attr.2;
ret Num.258;
procedure Str.12 (#Attr.2):
let Str.208 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.208;
procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
let Str.203 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
ret Str.203;
procedure Str.9 (Str.68):
let Str.201 : U64 = 0i64;
let Str.202 : U64 = CallByName List.6 Str.68;
let Str.69 : {U64, Str, Int1, U8} = CallByName Str.48 Str.68 Str.201 Str.202;
let Str.198 : Int1 = StructAtIndex 2 Str.69;
if Str.198 then
let Str.200 : Str = StructAtIndex 1 Str.69;
inc Str.200;
dec Str.69;
let Str.199 : [C {U64, U8}, C Str] = TagId(1) Str.200;
ret Str.199;
else
let Str.196 : U8 = StructAtIndex 3 Str.69;
let Str.197 : U64 = StructAtIndex 0 Str.69;
dec Str.69;
let Str.195 : {U64, U8} = Struct {Str.197, Str.196};
let Str.194 : [C {U64, U8}, C Str] = TagId(0) Str.195;
ret Str.194;
procedure Test.0 ():
let Test.9 : Str = "abc";
let Test.10 : {} = CallByName Json.1;
let Test.8 : List U8 = CallByName Encode.25 Test.9 Test.10;
let Test.1 : [C {U64, U8}, C Str] = CallByName Str.9 Test.8;
dec Test.8;
let Test.5 : U8 = 1i64;
let Test.6 : U8 = GetTagId Test.1;
let Test.7 : Int1 = lowlevel Eq Test.5 Test.6;
if Test.7 then
let Test.2 : Str = UnionAtIndex (Id 1) (Index 0) Test.1;
inc Test.2;
dec Test.1;
ret Test.2;
else
dec Test.1;
let Test.4 : Str = "<bad>";
ret Test.4;

View file

@ -1,7 +1,7 @@
procedure List.28 (#Attr.2, #Attr.3):
let List.297 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3;
let Bool.9 : Int1 = lowlevel ListIsUnique #Attr.2;
if Bool.9 then
let #Derived_gen.0 : Int1 = lowlevel ListIsUnique #Attr.2;
if #Derived_gen.0 then
ret List.297;
else
decref #Attr.2;

View file

@ -1467,7 +1467,24 @@ fn encode_custom_type() {
}
#[mono_test]
#[ignore]
fn encode_derived_string() {
indoc!(
r#"
app "test"
imports [Encode.{ toEncoder }, Json]
provides [main] to "./platform"
main =
result = Str.fromUtf8 (Encode.toBytes "abc" Json.format)
when result is
Ok s -> s
_ -> "<bad>"
"#
)
}
#[mono_test]
#[ignore = "TODO"]
fn encode_derived_record() {
indoc!(
r#"
@ -1476,7 +1493,7 @@ fn encode_derived_record() {
provides [main] to "./platform"
main =
result = Str.fromUtf8 (Encode.toBytes {a: "fieldA", b: "fieldB"} Json.format)
result = Str.fromUtf8 (Encode.toBytes {a: "a"} Json.format)
when result is
Ok s -> s
_ -> "<bad>"
@ -1780,3 +1797,74 @@ fn instantiate_annotated_as_recursive_alias_multiple_polymorphic_expr() {
"#
)
}
#[mono_test]
fn encode_derived_record_one_field_string() {
indoc!(
r#"
app "test"
imports [Encode.{ toEncoder }, Json]
provides [main] to "./platform"
main =
result = Str.fromUtf8 (Encode.toBytes {a: "foo"} Json.format)
when result is
Ok s -> s
_ -> "<bad>"
"#
)
}
#[mono_test]
fn encode_derived_record_two_field_strings() {
indoc!(
r#"
app "test"
imports [Encode.{ toEncoder }, Json]
provides [main] to "./platform"
main =
result = Str.fromUtf8 (Encode.toBytes {a: "foo", b: "bar"} Json.format)
when result is
Ok s -> s
_ -> "<bad>"
"#
)
}
#[mono_test]
fn encode_derived_nested_record_string() {
indoc!(
r#"
app "test"
imports [Encode.{ toEncoder }, Json]
provides [main] to "./platform"
main =
result = Str.fromUtf8 (Encode.toBytes {a: {b: "bar"}} Json.format)
when result is
Ok s -> s
_ -> "<bad>"
"#
)
}
#[mono_test]
#[ignore = "TODO make this work (one ULS var is missing)"]
fn encode_derived_tag_one_field_string() {
indoc!(
r#"
app "test"
imports [Encode.{ toEncoder }, Json]
provides [main] to "./platform"
main =
x : [A Str]
x = A "foo"
result = Str.fromUtf8 (Encode.toBytes x Json.format)
when result is
Ok s -> s
_ -> "<bad>"
"#
)
}

View file

@ -4666,6 +4666,7 @@ pub struct CopiedImport {
struct CopyImportEnv<'a> {
visited: bumpalo::collections::Vec<'a, Variable>,
/// source variable -> target variable
copy_table: &'a mut VecMap<Variable, Variable>,
source: &'a Subs,
target: &'a mut Subs,

View file

@ -1062,12 +1062,12 @@ struct SeparatedUnionLambdas {
joined: Vec<(Symbol, VariableSubsSlice)>,
}
fn separate_union_lambdas(
fn separate_union_lambdas<M: MetaCollector>(
subs: &mut Subs,
pool: &mut Pool,
fields1: UnionLambdas,
fields2: UnionLambdas,
) -> SeparatedUnionLambdas {
) -> (Outcome<M>, SeparatedUnionLambdas) {
debug_assert!(
fields1.is_sorted_allow_duplicates(subs),
"not sorted: {:?}",
@ -1124,6 +1124,7 @@ fn separate_union_lambdas(
}
}
let mut whole_outcome = Outcome::default();
let mut only_in_left = Vec::with_capacity(fields1.len());
let mut only_in_right = Vec::with_capacity(fields2.len());
let mut joined = Vec::with_capacity(fields1.len() + fields2.len());
@ -1172,13 +1173,14 @@ fn separate_union_lambdas(
maybe_mark_union_recursive(subs, var1);
maybe_mark_union_recursive(subs, var2);
let outcome =
unify_pool::<NoCollector>(subs, pool, var1, var2, Mode::EQ);
let outcome = unify_pool(subs, pool, var1, var2, Mode::EQ);
if !outcome.mismatches.is_empty() {
subs.rollback_to(snapshot);
continue 'try_next_right;
}
whole_outcome.union(outcome);
}
// All the variables unified, so we can join the left + right.
@ -1202,11 +1204,14 @@ fn separate_union_lambdas(
}
}
SeparatedUnionLambdas {
only_in_left,
only_in_right,
joined,
}
(
whole_outcome,
SeparatedUnionLambdas {
only_in_left,
only_in_right,
joined,
},
)
}
fn unify_unspecialized_lambdas<M: MetaCollector>(
@ -1214,7 +1219,7 @@ fn unify_unspecialized_lambdas<M: MetaCollector>(
pool: &mut Pool,
uls1: SubsSlice<Uls>,
uls2: SubsSlice<Uls>,
) -> Result<SubsSlice<Uls>, Outcome<M>> {
) -> Result<(SubsSlice<Uls>, Outcome<M>), Outcome<M>> {
// For now we merge all variables of unspecialized lambdas in a lambda set that share the same
// ability member/region.
// See the section "A property that's lost, and how we can hold on to it" of
@ -1225,9 +1230,9 @@ fn unify_unspecialized_lambdas<M: MetaCollector>(
// resolves to itself or re-points to lset2.
// In either case the merged unspecialized lambda sets will be there.
match (uls1.is_empty(), uls2.is_empty()) {
(true, true) => Ok(SubsSlice::default()),
(false, true) => Ok(uls1),
(true, false) => Ok(uls2),
(true, true) => Ok((SubsSlice::default(), Default::default())),
(false, true) => Ok((uls1, Default::default())),
(true, false) => Ok((uls2, Default::default())),
(false, false) => {
let mut all_uls = (subs.get_subs_slice(uls1).iter())
.chain(subs.get_subs_slice(uls2))
@ -1241,6 +1246,7 @@ fn unify_unspecialized_lambdas<M: MetaCollector>(
// Now merge the variables of unspecialized lambdas pointing to the same
// member/region.
let mut whole_outcome = Outcome::default();
let mut j = 1;
while j < all_uls.len() {
let i = j - 1;
@ -1251,6 +1257,7 @@ fn unify_unspecialized_lambdas<M: MetaCollector>(
if !outcome.mismatches.is_empty() {
return Err(outcome);
}
whole_outcome.union(outcome);
// Keep the Uls in position `i` and remove the one in position `j`.
all_uls.remove(j);
} else {
@ -1259,9 +1266,9 @@ fn unify_unspecialized_lambdas<M: MetaCollector>(
}
}
Ok(SubsSlice::extend_new(
&mut subs.unspecialized_lambda_sets,
all_uls,
Ok((
SubsSlice::extend_new(&mut subs.unspecialized_lambda_sets, all_uls),
whole_outcome,
))
}
}
@ -1302,11 +1309,14 @@ fn unify_lambda_set_help<M: MetaCollector>(
"Recursion var is present, but it doesn't have a recursive content!"
);
let SeparatedUnionLambdas {
only_in_left,
only_in_right,
joined,
} = separate_union_lambdas(subs, pool, solved1, solved2);
let (
mut whole_outcome,
SeparatedUnionLambdas {
only_in_left,
only_in_right,
joined,
},
) = separate_union_lambdas(subs, pool, solved1, solved2);
let all_lambdas = joined
.into_iter()
@ -1334,7 +1344,10 @@ fn unify_lambda_set_help<M: MetaCollector>(
};
let merged_unspecialized = match unify_unspecialized_lambdas(subs, pool, uls1, uls2) {
Ok(merged) => merged,
Ok((merged, outcome)) => {
whole_outcome.union(outcome);
merged
}
Err(outcome) => {
debug_assert!(!outcome.mismatches.is_empty());
return outcome;
@ -1349,7 +1362,9 @@ fn unify_lambda_set_help<M: MetaCollector>(
ambient_function: ambient_function_var,
});
merge(subs, ctx, new_lambda_set)
let merge_outcome = merge(subs, ctx, new_lambda_set);
whole_outcome.union(merge_outcome);
whole_outcome
}
/// Ensures that a non-recursive tag union, when unified with a recursion var to become a recursive
@ -1882,6 +1897,8 @@ fn unify_tag_unions<M: MetaCollector>(
let flat_type = FlatType::TagUnion(unique_tags1, ext1);
let sub_record = fresh(subs, pool, ctx, Structure(flat_type));
let mut total_outcome = Outcome::default();
// In a presence context, we don't care about ext2 being equal to tags1
if ctx.mode.is_eq() {
let ext_outcome = unify_pool(subs, pool, sub_record, ext2, ctx.mode);
@ -1889,9 +1906,10 @@ fn unify_tag_unions<M: MetaCollector>(
if !ext_outcome.mismatches.is_empty() {
return ext_outcome;
}
total_outcome.union(ext_outcome);
}
unify_shared_tags_new(
let shared_tags_outcome = unify_shared_tags_new(
subs,
pool,
ctx,
@ -1899,7 +1917,9 @@ fn unify_tag_unions<M: MetaCollector>(
OtherTags2::Empty,
sub_record,
recursion_var,
)
);
total_outcome.union(shared_tags_outcome);
total_outcome
} else {
let other_tags = OtherTags2::Union(separate.only_in_1.clone(), separate.only_in_2.clone());
@ -1932,6 +1952,7 @@ fn unify_tag_unions<M: MetaCollector>(
// without rolling back, the mismatch is between `[Blue, Red, Green]a` and `[Red, Green]`.
// TODO is this also required for the other cases?
let mut total_outcome = Outcome::default();
let snapshot = subs.snapshot();
let ext1_outcome = unify_pool(subs, pool, ext1, sub2, ctx.mode);
@ -1939,6 +1960,7 @@ fn unify_tag_unions<M: MetaCollector>(
subs.rollback_to(snapshot);
return ext1_outcome;
}
total_outcome.union(ext1_outcome);
if ctx.mode.is_eq() {
let ext2_outcome = unify_pool(subs, pool, sub1, ext2, ctx.mode);
@ -1946,11 +1968,15 @@ fn unify_tag_unions<M: MetaCollector>(
subs.rollback_to(snapshot);
return ext2_outcome;
}
total_outcome.union(ext2_outcome);
}
subs.commit_snapshot(snapshot);
unify_shared_tags_new(subs, pool, ctx, shared_tags, other_tags, ext, recursion_var)
let shared_tags_outcome =
unify_shared_tags_new(subs, pool, ctx, shared_tags, other_tags, ext, recursion_var);
total_outcome.union(shared_tags_outcome);
total_outcome
}
}

View file

@ -432,13 +432,9 @@ pub fn load_modules_for_files(filenames: Vec<PathBuf>) -> Vec<LoadedModule> {
let mut modules = Vec::with_capacity(filenames.len());
for filename in filenames {
let mut src_dir = filename.clone();
src_dir.pop();
match roc_load::load_and_typecheck(
&arena,
filename,
src_dir,
Default::default(),
roc_target::TargetInfo::default_x86_64(), // This is just type-checking for docs, so "target" doesn't matter
roc_reporting::report::RenderTarget::ColorTerminal,

View file

@ -395,6 +395,8 @@ fn gen_and_eval_llvm<'a>(
}
};
let interns = loaded.interns.clone();
let (lib, main_fn_name, subs) =
mono_module_to_dylib(&arena, target, loaded, opt_level).expect("we produce a valid Dylib");
@ -407,6 +409,7 @@ fn gen_and_eval_llvm<'a>(
main_fn_layout,
&content,
&subs,
&interns,
target_info,
);

View file

@ -1,12 +1,13 @@
use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_types::types::AliasKind;
use std::cmp::{max_by_key, min_by_key};
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_collections::all::MutMap;
use roc_module::called_via::CalledVia;
use roc_module::ident::TagName;
use roc_module::symbol::Symbol;
use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_mono::ir::ProcLayout;
use roc_mono::layout::{
union_sorted_tags_help, Builtin, Layout, LayoutCache, UnionLayout, UnionVariant, WrappedVariant,
@ -23,6 +24,7 @@ struct Env<'a, 'env> {
arena: &'a Bump,
subs: &'env Subs,
target_info: TargetInfo,
interns: &'a Interns,
}
pub enum ToAstProblem {
@ -45,12 +47,14 @@ pub fn jit_to_ast<'a, A: ReplApp<'a>>(
layout: ProcLayout<'a>,
content: &'a Content,
subs: &'a Subs,
interns: &'a Interns,
target_info: TargetInfo,
) -> Result<Expr<'a>, ToAstProblem> {
let env = Env {
arena,
subs,
target_info,
interns,
};
match layout {
@ -70,6 +74,7 @@ pub fn jit_to_ast<'a, A: ReplApp<'a>>(
enum NewtypeKind<'a> {
Tag(&'a TagName),
RecordField(&'a str),
Opaque(Symbol),
}
/// Unrolls types that are newtypes. These include
@ -115,7 +120,7 @@ fn unroll_newtypes_and_aliases<'a>(
));
content = env.subs.get_content_without_compacting(field.into_inner());
}
Content::Alias(_, _, real_var, _) => {
Content::Alias(name, _, real_var, kind) => {
// We need to pass through aliases too, because their underlying types may have
// unrolled newtypes. For example,
// T : { a : Str }
@ -126,6 +131,9 @@ fn unroll_newtypes_and_aliases<'a>(
//
// At the end of the day what we should show to the user is the alias content, not
// what's inside, so keep that around too.
if *kind == AliasKind::Opaque && name.module_id() != ModuleId::NUM {
newtype_containers.push(NewtypeKind::Opaque(*name));
}
alias_content = Some(content);
content = env.subs.get_content_without_compacting(*real_var);
}
@ -157,6 +165,13 @@ fn apply_newtypes<'a>(
let field = Loc::at_zero(AssignedField::RequiredValue(label, &[], field_val));
expr = Expr::Record(Collection::with_items(&*arena.alloc([field])))
}
NewtypeKind::Opaque(name) => {
let opaque_name = arena.alloc(format!("@{}", name.as_str(env.interns)));
let opaque_ref = &*arena.alloc(Loc::at_zero(Expr::OpaqueRef(opaque_name)));
let loc_arg_expr = &*arena.alloc(Loc::at_zero(expr));
let loc_arg_exprs = arena.alloc_slice_copy(&[loc_arg_expr]);
expr = Expr::Apply(opaque_ref, loc_arg_exprs, CalledVia::Space);
}
}
}
expr

View file

@ -1,3 +1,4 @@
use roc_module::symbol::Interns;
use roc_mono::{
ir::ProcLayout,
layout::{CapturesNiche, LayoutCache},
@ -16,6 +17,7 @@ pub fn get_values<'a>(
target_info: TargetInfo,
arena: &'a bumpalo::Bump,
subs: &'a Subs,
interns: &'a Interns,
start: *const u8,
mut start_offset: usize,
variables: &[Variable],
@ -52,6 +54,7 @@ pub fn get_values<'a>(
proc_layout,
content,
subs,
interns,
target_info,
)
}?;

View file

@ -1024,7 +1024,7 @@ fn opaque_apply() {
@Age 23
"#
),
"23 : Age",
"@Age 23 : Age",
)
}
@ -1038,7 +1038,7 @@ fn opaque_apply_polymorphic() {
@F (Package "" { a: "" })
"#
),
r#"Package "" { a: "" } : F Str { a : Str }"#,
r#"@F (Package "" { a: "" }) : F Str { a : Str }"#,
)
}
@ -1054,7 +1054,7 @@ fn opaque_pattern_and_call() {
f (@F (Package A {}))
"#
),
r#"Package {} A : F {} [A]*"#,
r#"@F (Package {} A) : F {} [A]*"#,
)
}
@ -1189,6 +1189,6 @@ fn opaque_wrap_function() {
List.map [1u8, 2u8, 3u8] @A
"#
),
"[1, 2, 3] : List (A U8)",
"[@A 1, @A 2, @A 3] : List (A U8)",
);
}

View file

@ -251,6 +251,7 @@ pub async fn entrypoint_from_js(src: String) -> Result<String, String> {
main_fn_layout,
content,
&subs,
&interns,
target_info,
);

View file

@ -22,7 +22,7 @@ bumpalo = { version = "3.8.0", features = ["collections"] }
[dev-dependencies]
roc_constrain = { path = "../compiler/constrain" }
roc_derive_key = { path = "../compiler/derive_key" }
roc_derive = { path = "../compiler/derive" }
roc_builtins = { path = "../compiler/builtins" }
roc_load = { path = "../compiler/load" }
roc_problem = { path = "../compiler/problem" }

View file

@ -10,7 +10,7 @@ use roc_can::operator;
use roc_can::scope::Scope;
use roc_collections::all::{ImMap, MutMap, SendSet};
use roc_constrain::expr::constrain_expr;
use roc_derive_key::GlobalDerivedSymbols;
use roc_derive::SharedDerivedModule;
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds};
use roc_parse::parser::{SourceError, SyntaxError};
use roc_problem::can::Problem;
@ -35,10 +35,11 @@ pub fn infer_expr(
pending_derives: PendingDerives,
aliases: &mut Aliases,
abilities_store: &mut AbilitiesStore,
derived_symbols: GlobalDerivedSymbols,
derived_module: SharedDerivedModule,
expr_var: Variable,
) -> (Content, Subs) {
let (solved, _) = solve::run(
ModuleId::ATTR,
constraints,
problems,
subs,
@ -46,7 +47,8 @@ pub fn infer_expr(
constraint,
pending_derives,
abilities_store,
derived_symbols,
&Default::default(),
derived_module,
);
let content = *solved.inner().get_content_without_compacting(expr_var);

View file

@ -86,7 +86,6 @@ mod test_reporting {
let result = roc_load::load_and_typecheck(
arena,
full_file_path,
dir.path().to_path_buf(),
exposed_types,
roc_target::TargetInfo::default_x86_64(),
RenderTarget::Generic,
@ -9055,39 +9054,6 @@ All branches in an `if` must have the same type!
"#
);
test_report!(
unbound_type_in_record_does_not_implement_encoding,
indoc!(
r#"
app "test" imports [Encode] provides [main] to "./platform"
main = \x -> Encode.toEncoder { x: x }
"#
),
@r#"
TYPE MISMATCH /code/proj/Main.roc
This expression has a type that does not implement the abilities it's expected to:
3 main = \x -> Encode.toEncoder { x: x }
^^^^^^^^
Roc can't generate an implementation of the `Encode.Encoding` ability
for
{ x : a }
In particular, an implementation for
a
cannot be generated.
Tip: This type variable is not bound to `Encoding`. Consider adding a
`has` clause to bind the type variable, like `| a has Encode.Encoding`
"#
);
test_report!(
nested_opaque_does_not_implement_encoding,
indoc!(
@ -9098,7 +9064,9 @@ All branches in an `if` must have the same type!
main = Encode.toEncoder { x: @A {} }
"#
),
@r#"
// TODO: this error message is quite unfortunate. We should remove the duplication, and
// also support regions that point to things in other modules. See also https://github.com/rtfeldman/roc/issues/3056.
@r###"
TYPE MISMATCH /code/proj/Main.roc
This expression has a type that does not implement the abilities it's expected to:
@ -9119,7 +9087,17 @@ All branches in an `if` must have the same type!
Tip: `A` does not implement `Encoding`. Consider adding a custom
implementation or `has Encode.Encoding` to the definition of `A`.
"#
INCOMPLETE ABILITY IMPLEMENTATION /code/proj/Main.roc
The type `A` does not fully implement the ability `Encoding`. The
following specializations are missing:
A specialization for `toEncoder`, which is defined here:
5
^^^^^^^^^
"###
);
test_report!(

View file

@ -1,6 +1,6 @@
interface InternalTask
exposes [Task, fromEffect, toEffect]
imports [pf.Effect.{ Effect }]
imports [Effect.{ Effect }]
Task ok err fx := Effect (Result ok err)

View file

@ -1,6 +1,6 @@
interface Task
exposes [Task, succeed, fail, await, map, onFail, attempt, forever, loop]
imports [pf.Effect, pf.InternalTask]
imports [Effect, InternalTask]
Task ok err fx : InternalTask.Task ok err fx