diff --git a/.github/workflows/nix_macos_apple_silicon.yml b/.github/workflows/nix_macos_apple_silicon.yml index 734f06ac4d..619bc9c8a6 100644 --- a/.github/workflows/nix_macos_apple_silicon.yml +++ b/.github/workflows/nix_macos_apple_silicon.yml @@ -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 diff --git a/Cargo.lock b/Cargo.lock index 8bb08028b6..b3802c120d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Earthfile b/Earthfile index 148b7ecac3..d0ab4f1977 100644 --- a/Earthfile +++ b/Earthfile @@ -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 diff --git a/crates/ast/src/module.rs b/crates/ast/src/module.rs index 0e95fea098..ae9d265b57 100644 --- a/crates/ast/src/module.rs +++ b/crates/ast/src/module.rs @@ -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, diff --git a/crates/bindgen/src/bindgen_rs.rs b/crates/bindgen/src/bindgen_rs.rs index 2d58334780..758995e6c5 100644 --- a/crates/bindgen/src/bindgen_rs.rs +++ b/crates/bindgen/src/bindgen_rs.rs @@ -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, 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 = do_not_recurse.into(); + + do_not_recurse.push(*content); + + has_float_help(types.get_type(*content), types, &do_not_recurse) + } + } + } +} diff --git a/crates/bindgen/src/load.rs b/crates/bindgen/src/load.rs index 528d3ac17c..7bf2bbc717 100644 --- a/crates/bindgen/src/load.rs +++ b/crates/bindgen/src/load.rs @@ -10,7 +10,6 @@ use target_lexicon::Triple; pub fn load_types( full_file_path: PathBuf, - dir: PathBuf, threading: Threading, ) -> Result, 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, diff --git a/crates/bindgen/src/main.rs b/crates/bindgen/src/main.rs index 9a1c2d5b2a..2586623974 100644 --- a/crates/bindgen/src/main.rs +++ b/crates/bindgen/src/main.rs @@ -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!( diff --git a/crates/bindgen/src/types.rs b/crates/bindgen/src/types.rs index b2fe28449e..c062677ed2 100644 --- a/crates/bindgen/src/types.rs +++ b/crates/bindgen/src/types.rs @@ -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), } #[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 = 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 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!() diff --git a/crates/bindgen/tests/helpers/mod.rs b/crates/bindgen/tests/helpers/mod.rs index d1cf822fb8..b58612531e 100644 --- a/crates/bindgen/tests/helpers/mod.rs +++ b/crates/bindgen/tests/helpers/mod.rs @@ -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"); diff --git a/crates/cli/src/build.rs b/crates/cli/src/build.rs index 5cf4a7106e..dfd363f05d 100644 --- a/crates/cli/src/build.rs +++ b/crates/cli/src/build.rs @@ -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? diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index baca984119..e374426de8 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -353,8 +353,6 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result { ); } - 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 { 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, diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 9faf14ae90..537c0ccc47 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -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::().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.", diff --git a/crates/compiler/builtins/bitcode/src/dec.zig b/crates/compiler/builtins/bitcode/src/dec.zig index 93df19cbd8..92738c7351 100644 --- a/crates/compiler/builtins/bitcode/src/dec.zig +++ b/crates/compiler/builtins/bitcode/src/dec.zig @@ -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"); } diff --git a/crates/compiler/builtins/bitcode/src/main.zig b/crates/compiler/builtins/bitcode/src/main.zig index 7262c4c076..f9db40efc6 100644 --- a/crates/compiler/builtins/bitcode/src/main.zig +++ b/crates/compiler/builtins/bitcode/src/main.zig @@ -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."); } } diff --git a/crates/compiler/builtins/bitcode/src/str.zig b/crates/compiler/builtins/bitcode/src/str.zig index 74fe6fb6d2..b785094871 100644 --- a/crates/compiler/builtins/bitcode/src/str.zig +++ b/crates/compiler/builtins/bitcode/src/str.zig @@ -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, + }; } } diff --git a/crates/compiler/builtins/roc/List.roc b/crates/compiler/builtins/roc/List.roc index a8a237cd55..b613478d7d 100644 --- a/crates/compiler/builtins/roc/List.roc +++ b/crates/compiler/builtins/roc/List.roc @@ -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 diff --git a/crates/compiler/builtins/src/bitcode.rs b/crates/compiler/builtins/src/bitcode.rs index e48006c75f..5ce98255ec 100644 --- a/crates/compiler/builtins/src/bitcode.rs +++ b/crates/compiler/builtins/src/bitcode.rs @@ -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"; diff --git a/crates/compiler/can/src/abilities.rs b/crates/compiler/can/src/abilities.rs index 18a644a77c..5afa4675e8 100644 --- a/crates/compiler/can/src/abilities.rs +++ b/crates/compiler/can/src/abilities.rs @@ -359,9 +359,6 @@ impl IAbilitiesStore { } 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 { ); } - 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 { self.resolved_specializations.get(&id).copied() } diff --git a/crates/compiler/mono/src/copy.rs b/crates/compiler/can/src/copy.rs similarity index 97% rename from crates/compiler/mono/src/copy.rs rename to crates/compiler/can/src/copy.rs index 0417046c8c..5e5e8c4e30 100644 --- a/crates/compiler/mono/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -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) -> SubsSlice; + fn clone_lambda_names(&mut self, lambda_names: SubsSlice) -> SubsSlice; + fn clone_record_fields( &mut self, record_fields: SubsSlice>, @@ -104,6 +109,11 @@ impl CopyEnv for Subs { tag_names } + #[inline(always)] + fn clone_lambda_names(&mut self, lambda_names: SubsSlice) -> SubsSlice { + 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) -> SubsSlice { + 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( 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( #[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()); diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index ba4b003395..c066871a3a 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -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>, diff --git a/crates/compiler/can/src/lib.rs b/crates/compiler/can/src/lib.rs index b140f93267..f00abbd5a1 100644 --- a/crates/compiler/can/src/lib.rs +++ b/crates/compiler/can/src/lib.rs @@ -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; diff --git a/crates/compiler/derive/src/encoding.rs b/crates/compiler/derive/src/encoding.rs index 59983034a0..043255a665 100644 --- a/crates/compiler/derive/src/encoding.rs +++ b/crates/compiler/derive/src/encoding.rs @@ -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 { diff --git a/crates/compiler/derive/src/lib.rs b/crates/compiler/derive/src/lib.rs index 7fad9eece5..107525a14a 100644 --- a/crates/compiler/derive/src/lib.rs +++ b/crates/compiler/derive/src/lib.rs @@ -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; - -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, 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 { - #[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 { + 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>; diff --git a/crates/compiler/derive_key/Cargo.toml b/crates/compiler/derive_key/Cargo.toml index 53551245e0..e674089371 100644 --- a/crates/compiler/derive_key/Cargo.toml +++ b/crates/compiler/derive_key/Cargo.toml @@ -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" } diff --git a/crates/compiler/derive_key/src/lib.rs b/crates/compiler/derive_key/src/lib.rs index 927f0b19d8..f2ebb3e806 100644 --- a/crates/compiler/derive_key/src/lib.rs +++ b/crates/compiler/derive_key/src/lib.rs @@ -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, - 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>; diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 6be72b6c02..90ca1bfcfd 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -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!(), } } diff --git a/crates/compiler/gen_llvm/src/llvm/build_str.rs b/crates/compiler/gen_llvm/src/llvm/build_str.rs index acd4d759f9..8053f32415 100644 --- a/crates/compiler/gen_llvm/src/llvm/build_str.rs +++ b/crates/compiler/gen_llvm/src/llvm/build_str.rs @@ -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 diff --git a/crates/compiler/gen_wasm/src/low_level.rs b/crates/compiler/gen_wasm/src/low_level.rs index 1a1a1e83df..be22defa97 100644 --- a/crates/compiler/gen_wasm/src/low_level.rs +++ b/crates/compiler/gen_wasm/src/low_level.rs @@ -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), } diff --git a/crates/compiler/late_solve/Cargo.toml b/crates/compiler/late_solve/Cargo.toml index 5d72dda7ea..f213049b40 100644 --- a/crates/compiler/late_solve/Cargo.toml +++ b/crates/compiler/late_solve/Cargo.toml @@ -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"] } diff --git a/crates/compiler/late_solve/src/lib.rs b/crates/compiler/late_solve/src/lib.rs index cf9a0fc89e..ef787ef717 100644 --- a/crates/compiler/late_solve/src/lib.rs +++ b/crates/compiler/late_solve/src/lib.rs @@ -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( + &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(&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. diff --git a/crates/compiler/load/src/lib.rs b/crates/compiler/load/src/lib.rs index cee9864eac..2367fc02a8 100644 --- a/crates/compiler/load/src/lib.rs +++ b/crates/compiler/load/src/lib.rs @@ -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, 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> { 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, diff --git a/crates/compiler/load_internal/Cargo.toml b/crates/compiler/load_internal/Cargo.toml index 3f84288f21..93bfd87c65 100644 --- a/crates/compiler/load_internal/Cargo.toml +++ b/crates/compiler/load_internal/Cargo.toml @@ -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" } diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 367bcc7978..068a14c21b 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -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>>, pub arc_shorthands: Arc>>>, #[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>, 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> { 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, packages: &'a [Loc>], provides: &'a [Loc>], @@ -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)>, rigid_vars: &mut Vec, ) -> (Vec, 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, 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>]) -> &'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, 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, )), }?; diff --git a/crates/compiler/load_internal/src/work.rs b/crates/compiler/load_internal/src/work.rs index aa2359b20a..b0e5b2c8ce 100644 --- a/crates/compiler/load_internal/src/work.rs +++ b/crates/compiler/load_internal/src/work.rs @@ -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, + /// Whether this module depends on specializations being made for another module + has_pred: bool, +} + +#[derive(Debug)] +struct MakeSpecializationsDependents(MutMap); + +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) { + // 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 = 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, MutSet>>, notifies: MutMap, MutSet>>, status: MutMap, Status>, - /// module -> modules to make specializations after, whether a module comes before us - make_specializations_dependents: MutMap, 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 diff --git a/crates/compiler/load_internal/tests/test_load.rs b/crates/compiler/load_internal/tests/test_load.rs index 95446b4292..33079f3905 100644 --- a/crates/compiler/load_internal/tests/test_load.rs +++ b/crates/compiler/load_internal/tests/test_load.rs @@ -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 { 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; diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index 3e73910f78..150b661b80 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -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); 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) } diff --git a/crates/compiler/mono/Cargo.toml b/crates/compiler/mono/Cargo.toml index 02c2c095d3..c1fa81d366 100644 --- a/crates/compiler/mono/Cargo.toml +++ b/crates/compiler/mono/Cargo.toml @@ -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" } diff --git a/crates/compiler/mono/src/borrow.rs b/crates/compiler/mono/src/borrow.rs index f46b0ff022..0bb6e2796a 100644 --- a/crates/compiler/mono/src/borrow.rs +++ b/crates/compiler/mono/src/borrow.rs @@ -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]), diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 0ea8364404..6ad3c450d3 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -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>, 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); diff --git a/crates/compiler/mono/src/lib.rs b/crates/compiler/mono/src/lib.rs index aa8dfba4c8..14b55c5bd6 100644 --- a/crates/compiler/mono/src/lib.rs +++ b/crates/compiler/mono/src/lib.rs @@ -4,7 +4,6 @@ pub mod borrow; pub mod code_gen_help; -mod copy; pub mod inc_dec; pub mod ir; pub mod layout; diff --git a/crates/compiler/solve/Cargo.toml b/crates/compiler/solve/Cargo.toml index 7cf9f08f03..217ba875c8 100644 --- a/crates/compiler/solve/Cargo.toml +++ b/crates/compiler/solve/Cargo.toml @@ -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" diff --git a/crates/compiler/solve/src/ability.rs b/crates/compiler/solve/src/ability.rs index aa9f37f820..32340b6ac4 100644 --- a/crates/compiler/solve/src/ability.rs +++ b/crates/compiler/solve/src/ability.rs @@ -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, diff --git a/crates/compiler/solve/src/module.rs b/crates/compiler/solve/src/module.rs index 7aab8fba67..f4194a8b65 100644 --- a/crates/compiler/solve/src/module.rs +++ b/crates/compiler/solve/src/module.rs @@ -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, 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) diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 4ec39aa9de..bf215bfa21 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -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, 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, 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, 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( 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( 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( // 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( } #[must_use] +#[allow(clippy::too_many_arguments)] fn compact_lambda_set( 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( 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( + 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 { + 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) } } } diff --git a/crates/compiler/test_derive/src/encoding.rs b/crates/compiler/test_derive/src/encoding.rs index e3968586c5..1d1d4fcb2b 100644 --- a/crates/compiler/test_derive/src/encoding.rs +++ b/crates/compiler/test_derive/src/encoding.rs @@ -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()); diff --git a/crates/compiler/test_gen/src/gen_abilities.rs b/crates/compiler/test_gen/src/gen_abilities.rs index 587bd93198..cfa606a908 100644 --- a/crates/compiler/test_gen/src/gen_abilities.rs +++ b/crates/compiler/test_gen/src/gen_abilities.rs @@ -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 + _ -> "" + "# + ), + 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 + _ -> "" + "# + ), + 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 + _ -> "" + "# + ), + 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 + _ -> "" + "# + ), + 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 + _ -> "" + "# + ), + RocStr::from(r#"{"a":{"b":"bar",},}"#), + RocStr + ) +} diff --git a/crates/compiler/test_gen/src/gen_num.rs b/crates/compiler/test_gen/src/gen_num.rs index 41fd7a0797..52da522fc5 100644 --- a/crates/compiler/test_gen/src/gen_num.rs +++ b/crates/compiler/test_gen/src/gen_num.rs @@ -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() { diff --git a/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt b/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt new file mode 100644 index 0000000000..c9101af616 --- /dev/null +++ b/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt @@ -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 = ""; + ret Test.4; diff --git a/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt b/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt new file mode 100644 index 0000000000..da4a59c260 --- /dev/null +++ b/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt @@ -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 = ""; + ret Test.4; diff --git a/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt b/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt new file mode 100644 index 0000000000..927b5590f8 --- /dev/null +++ b/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt @@ -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 = ""; + ret Test.4; diff --git a/crates/compiler/test_mono/generated/encode_derived_string.txt b/crates/compiler/test_mono/generated/encode_derived_string.txt new file mode 100644 index 0000000000..20db244a30 --- /dev/null +++ b/crates/compiler/test_mono/generated/encode_derived_string.txt @@ -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 = ""; + ret Test.4; diff --git a/crates/compiler/test_mono/generated/list_sort_asc.txt b/crates/compiler/test_mono/generated/list_sort_asc.txt index 0299a6d4e6..7f9606cd60 100644 --- a/crates/compiler/test_mono/generated/list_sort_asc.txt +++ b/crates/compiler/test_mono/generated/list_sort_asc.txt @@ -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; diff --git a/crates/compiler/test_mono/src/tests.rs b/crates/compiler/test_mono/src/tests.rs index c7ef0e7450..4b127d5e0c 100644 --- a/crates/compiler/test_mono/src/tests.rs +++ b/crates/compiler/test_mono/src/tests.rs @@ -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 + _ -> "" + "# + ) +} + +#[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 _ -> "" @@ -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 + _ -> "" + "# + ) +} + +#[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 + _ -> "" + "# + ) +} + +#[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 + _ -> "" + "# + ) +} + +#[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 + _ -> "" + "# + ) +} diff --git a/crates/compiler/types/src/subs.rs b/crates/compiler/types/src/subs.rs index c23291f326..e2b96c56d7 100644 --- a/crates/compiler/types/src/subs.rs +++ b/crates/compiler/types/src/subs.rs @@ -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, source: &'a Subs, target: &'a mut Subs, diff --git a/crates/compiler/unify/src/unify.rs b/crates/compiler/unify/src/unify.rs index b5cec1a6ab..81c319689d 100644 --- a/crates/compiler/unify/src/unify.rs +++ b/crates/compiler/unify/src/unify.rs @@ -1062,12 +1062,12 @@ struct SeparatedUnionLambdas { joined: Vec<(Symbol, VariableSubsSlice)>, } -fn separate_union_lambdas( +fn separate_union_lambdas( subs: &mut Subs, pool: &mut Pool, fields1: UnionLambdas, fields2: UnionLambdas, -) -> SeparatedUnionLambdas { +) -> (Outcome, 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::(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( @@ -1214,7 +1219,7 @@ fn unify_unspecialized_lambdas( pool: &mut Pool, uls1: SubsSlice, uls2: SubsSlice, -) -> Result, Outcome> { +) -> Result<(SubsSlice, Outcome), Outcome> { // 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( // 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( // 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( 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( } } - 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( "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( }; 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( 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( 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( 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( 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( // 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( 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( 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 } } diff --git a/crates/docs/src/lib.rs b/crates/docs/src/lib.rs index 0af885947e..266aa8ef7c 100644 --- a/crates/docs/src/lib.rs +++ b/crates/docs/src/lib.rs @@ -432,13 +432,9 @@ pub fn load_modules_for_files(filenames: Vec) -> Vec { 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, diff --git a/crates/repl_cli/src/lib.rs b/crates/repl_cli/src/lib.rs index b52306de6b..0650650ae0 100644 --- a/crates/repl_cli/src/lib.rs +++ b/crates/repl_cli/src/lib.rs @@ -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, ); diff --git a/crates/repl_eval/src/eval.rs b/crates/repl_eval/src/eval.rs index 3a33bb45fc..4bfb55a9ea 100644 --- a/crates/repl_eval/src/eval.rs +++ b/crates/repl_eval/src/eval.rs @@ -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, 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 diff --git a/crates/repl_expect/src/lib.rs b/crates/repl_expect/src/lib.rs index e7119bff96..1532be7e83 100644 --- a/crates/repl_expect/src/lib.rs +++ b/crates/repl_expect/src/lib.rs @@ -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, ) }?; diff --git a/crates/repl_test/src/tests.rs b/crates/repl_test/src/tests.rs index 7302cb7c9a..ab802472e8 100644 --- a/crates/repl_test/src/tests.rs +++ b/crates/repl_test/src/tests.rs @@ -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)", ); } diff --git a/crates/repl_wasm/src/repl.rs b/crates/repl_wasm/src/repl.rs index 52164d496b..f449589750 100644 --- a/crates/repl_wasm/src/repl.rs +++ b/crates/repl_wasm/src/repl.rs @@ -251,6 +251,7 @@ pub async fn entrypoint_from_js(src: String) -> Result { main_fn_layout, content, &subs, + &interns, target_info, ); diff --git a/crates/reporting/Cargo.toml b/crates/reporting/Cargo.toml index e1af05fbc6..9a96cc43c6 100644 --- a/crates/reporting/Cargo.toml +++ b/crates/reporting/Cargo.toml @@ -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" } diff --git a/crates/reporting/tests/helpers/mod.rs b/crates/reporting/tests/helpers/mod.rs index a507cfb012..d4e06f37c7 100644 --- a/crates/reporting/tests/helpers/mod.rs +++ b/crates/reporting/tests/helpers/mod.rs @@ -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); diff --git a/crates/reporting/tests/test_reporting.rs b/crates/reporting/tests/test_reporting.rs index 758cbab1c1..ffcb243eea 100644 --- a/crates/reporting/tests/test_reporting.rs +++ b/crates/reporting/tests/test_reporting.rs @@ -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!( diff --git a/examples/interactive/cli-platform/InternalTask.roc b/examples/interactive/cli-platform/InternalTask.roc index 738e59e86d..e0e556f57c 100644 --- a/examples/interactive/cli-platform/InternalTask.roc +++ b/examples/interactive/cli-platform/InternalTask.roc @@ -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) diff --git a/examples/interactive/cli-platform/Task.roc b/examples/interactive/cli-platform/Task.roc index bfb04336f2..f71cffc470 100644 --- a/examples/interactive/cli-platform/Task.roc +++ b/examples/interactive/cli-platform/Task.roc @@ -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