diff --git a/.github/workflows/windows_release_build.yml b/.github/workflows/windows_release_build.yml index 86cca232e1f..a889900b0cf 100644 --- a/.github/workflows/windows_release_build.yml +++ b/.github/workflows/windows_release_build.yml @@ -32,8 +32,8 @@ jobs: - name: zig version run: zig version - - name: install rust nightly 1.66 - run: rustup install nightly-2022-10-30 + - name: install rust nightly 1.67 + run: rustup install nightly-2022-12-09 - name: set up llvm 13 run: | diff --git a/.github/workflows/windows_tests.yml b/.github/workflows/windows_tests.yml index 9ccf82d3ea7..2fe095b711b 100644 --- a/.github/workflows/windows_tests.yml +++ b/.github/workflows/windows_tests.yml @@ -36,8 +36,8 @@ jobs: - name: zig version run: zig version - - name: install rust nightly 1.66 - run: rustup install nightly-2022-10-30 + - name: install rust nightly 1.67 + run: rustup install nightly-2022-12-09 - name: set up llvm 13 run: | diff --git a/Cargo.lock b/Cargo.lock index 256cbe02628..38bacbfed90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3669,6 +3669,7 @@ dependencies = [ name = "roc_mono" version = "0.0.1" dependencies = [ + "arrayvec 0.7.2", "bitvec", "bumpalo", "hashbrown 0.13.2", @@ -3758,6 +3759,7 @@ dependencies = [ "roc_build", "roc_builtins", "roc_collections", + "roc_error_macros", "roc_gen_llvm", "roc_load", "roc_module", diff --git a/README.md b/README.md index 1887ea37ec7..3b6283d4b8d 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,20 @@ Roc is not ready for a 0.1 release yet, but we do have: - [**installation** guide](https://github.com/roc-lang/roc/tree/main/getting_started) - [**tutorial**](https://roc-lang.org/tutorial) -- [**docs** for the standard library](https://www.roc-lang.org/builtins/Str) +- [**docs** for the standard library](https://www.roc-lang.org/builtins) - [**examples**](https://github.com/roc-lang/examples/tree/main/examples) -- [frequently asked questions](https://github.com/roc-lang/roc/blob/main/FAQ.md) -- [Group chat](https://roc.zulipchat.com) for help, questions and discussions +- [**faq**: frequently asked questions](https://github.com/roc-lang/roc/blob/main/FAQ.md) +- [**group chat**](https://roc.zulipchat.com) for help, questions and discussions If you'd like to contribute, check out [good first issues](https://github.com/roc-lang/roc/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). Don't hesitate to ask for help on our [group chat](https://roc.zulipchat.com), we're friendly! ## Sponsors -We are very grateful for our corporate sponsors [Vendr](https://www.vendr.com/), [RWX](https://www.rwx.com), and [Tweede golf](https://tweedegolf.nl/en). +You can 💜 **sponsor** 💜 Roc on: +- [GitHub](https://github.com/sponsors/roc-lang) +- [Liberapay](https://liberapay.com/roc_lang) + +We are very grateful for our corporate sponsors [Vendr](https://www.vendr.com/), [RWX](https://www.rwx.com), and [Tweede golf](https://tweedegolf.nl/en): [Vendr logo](https://www.vendr.com)      diff --git a/crates/ast/src/ast_error.rs b/crates/ast/src/ast_error.rs index c733ca0ca50..dd7dfe19a91 100644 --- a/crates/ast/src/ast_error.rs +++ b/crates/ast/src/ast_error.rs @@ -59,7 +59,7 @@ impl From for ASTError { impl From<(Region, Loc)> for ASTError { fn from(ident_exists_err: (Region, Loc)) -> Self { Self::IdentExistsError { - msg: format!("{:?}", ident_exists_err), + msg: format!("{ident_exists_err:?}"), } } } @@ -67,7 +67,7 @@ impl From<(Region, Loc)> for ASTError { impl<'a> From> for ASTError { fn from(syntax_err: SyntaxError) -> Self { Self::SyntaxErrorNoBacktrace { - msg: format!("{:?}", syntax_err), + msg: format!("{syntax_err:?}"), } } } diff --git a/crates/ast/src/builtin_aliases.rs b/crates/ast/src/builtin_aliases.rs index ebb95d304c3..1d70b867f8d 100644 --- a/crates/ast/src/builtin_aliases.rs +++ b/crates/ast/src/builtin_aliases.rs @@ -94,8 +94,7 @@ pub fn aliases() -> MutMap { let mut add_alias = |symbol, alias| { debug_assert!( !aliases.contains_key(&symbol), - "Duplicate alias definition for {:?}", - symbol + "Duplicate alias definition for {symbol:?}" ); // TODO instead of using Region::zero for all of these, diff --git a/crates/ast/src/constrain.rs b/crates/ast/src/constrain.rs index ae2ad75b9c4..c7467723bbc 100644 --- a/crates/ast/src/constrain.rs +++ b/crates/ast/src/constrain.rs @@ -2065,7 +2065,7 @@ pub mod test_constrain { assert_eq!(actual_str, expected_str); } - Err(e) => panic!("syntax error {:?}", e), + Err(e) => panic!("syntax error {e:?}"), } } diff --git a/crates/ast/src/lang/core/expr/expr2_to_string.rs b/crates/ast/src/lang/core/expr/expr2_to_string.rs index 2162ccc299f..cb94e8af716 100644 --- a/crates/ast/src/lang/core/expr/expr2_to_string.rs +++ b/crates/ast/src/lang/core/expr/expr2_to_string.rs @@ -130,7 +130,7 @@ fn expr2_to_string_helper( ); } Expr2::Call { .. } => { - let _ = write!(out_string, "Call({:?})", expr2); + let _ = write!(out_string, "Call({expr2:?})"); } Expr2::Closure { args, .. } => { out_string.push_str("Closure:\n"); @@ -148,7 +148,7 @@ fn expr2_to_string_helper( } } &Expr2::Var { .. } => { - let _ = write!(out_string, "{:?}", expr2); + let _ = write!(out_string, "{expr2:?}"); } Expr2::RuntimeError { .. } => { out_string.push_str("RuntimeError\n"); diff --git a/crates/ast/src/lang/core/expr/expr_to_expr2.rs b/crates/ast/src/lang/core/expr/expr_to_expr2.rs index cdbc12a054f..b494abfc7e4 100644 --- a/crates/ast/src/lang/core/expr/expr_to_expr2.rs +++ b/crates/ast/src/lang/core/expr/expr_to_expr2.rs @@ -665,37 +665,25 @@ pub fn expr_to_expr2<'a>( ident, } => canonicalize_lookup(env, scope, module_name, ident, region), + ParensAround(sub_expr) => expr_to_expr2(env, scope, sub_expr, region), + // Below this point, we shouln't see any of these nodes anymore because // operator desugaring should have removed them! - bad_expr @ ParensAround(_) => { - panic!( - "A ParensAround did not get removed during operator desugaring somehow: {:#?}", - bad_expr - ); - } bad_expr @ SpaceBefore(_, _) => { panic!( - "A SpaceBefore did not get removed during operator desugaring somehow: {:#?}", - bad_expr + "A SpaceBefore did not get removed during operator desugaring somehow: {bad_expr:#?}" ); } bad_expr @ SpaceAfter(_, _) => { panic!( - "A SpaceAfter did not get removed during operator desugaring somehow: {:#?}", - bad_expr + "A SpaceAfter did not get removed during operator desugaring somehow: {bad_expr:#?}" ); } bad_expr @ BinOps { .. } => { - panic!( - "A binary operator chain did not get desugared somehow: {:#?}", - bad_expr - ); + panic!("A binary operator chain did not get desugared somehow: {bad_expr:#?}"); } bad_expr @ UnaryOp(_, _) => { - panic!( - "A unary operator did not get desugared somehow: {:#?}", - bad_expr - ); + panic!("A unary operator did not get desugared somehow: {bad_expr:#?}"); } rest => todo!("not yet implemented {:?}", rest), diff --git a/crates/ast/src/lang/core/str.rs b/crates/ast/src/lang/core/str.rs index 9d5bb3e3d1e..c5e53b06910 100644 --- a/crates/ast/src/lang/core/str.rs +++ b/crates/ast/src/lang/core/str.rs @@ -227,7 +227,7 @@ pub fn update_str_expr( Expr2::Str(old_pool_str) => Either::OldPoolStr(*old_pool_str), other => UnexpectedASTNodeSnafu { required_node_type: "SmallStr or Str", - encountered_node_type: format!("{:?}", other), + encountered_node_type: format!("{other:?}"), } .fail()?, }; diff --git a/crates/ast/src/lang/env.rs b/crates/ast/src/lang/env.rs index c86b212c9af..8a40df3a7ee 100644 --- a/crates/ast/src/lang/env.rs +++ b/crates/ast/src/lang/env.rs @@ -102,8 +102,7 @@ impl<'a> Env<'a> { ) -> Result { debug_assert!( !module_name.is_empty(), - "Called env.qualified_lookup with an unqualified ident: {:?}", - ident + "Called env.qualified_lookup with an unqualified ident: {ident:?}" ); let module_name: ModuleName = module_name.into(); diff --git a/crates/ast/src/module.rs b/crates/ast/src/module.rs index 61352cd1eac..f88649ad7de 100644 --- a/crates/ast/src/module.rs +++ b/crates/ast/src/module.rs @@ -25,14 +25,8 @@ pub fn load_module( match loaded { Ok(x) => x, Err(roc_load::LoadingProblem::FormattedReport(report)) => { - panic!( - "Failed to load module from src_file: {:?}. Report: {}", - src_file, report - ); + panic!("Failed to load module from src_file: {src_file:?}. Report: {report}"); } - Err(e) => panic!( - "Failed to load module from src_file {:?}: {:?}", - src_file, e - ), + Err(e) => panic!("Failed to load module from src_file {src_file:?}: {e:?}"), } } diff --git a/crates/ast/src/solve_type.rs b/crates/ast/src/solve_type.rs index 90e562ad2a0..96873f37f9a 100644 --- a/crates/ast/src/solve_type.rs +++ b/crates/ast/src/solve_type.rs @@ -606,7 +606,10 @@ fn solve<'a>( let result = offenders.len(); if result > 0 { - dbg!(&subs, &offenders, &let_con.def_types); + eprintln!( + "subs: {:?}\n\noffenders: {:?}\n\nlet_con.def_types: {:?}\n", + &subs, &offenders, &let_con.def_types + ); } result diff --git a/crates/cli/src/format.rs b/crates/cli/src/format.rs index 7200109d618..6c62ef65846 100644 --- a/crates/cli/src/format.rs +++ b/crates/cli/src/format.rs @@ -93,18 +93,18 @@ pub fn format(files: std::vec::Vec, mode: FormatMode) -> Result<(), Str // the PartialEq implementation is returning `false` even when the Debug-formatted impl is exactly the same. // I don't have the patience to debug this right now, so let's leave it for another day... // TODO: fix PartialEq impl on ast types - if format!("{:?}", ast_normalized) != format!("{:?}", reparsed_ast_normalized) { + if format!("{ast_normalized:?}") != format!("{reparsed_ast_normalized:?}") { let mut fail_file = file.clone(); fail_file.set_extension("roc-format-failed"); std::fs::write(&fail_file, buf.as_str()).unwrap(); let mut before_file = file.clone(); before_file.set_extension("roc-format-failed-ast-before"); - std::fs::write(&before_file, format!("{:#?}\n", ast_normalized)).unwrap(); + std::fs::write(&before_file, format!("{ast_normalized:#?}\n")).unwrap(); let mut after_file = file.clone(); after_file.set_extension("roc-format-failed-ast-after"); - std::fs::write(&after_file, format!("{:#?}\n", reparsed_ast_normalized)).unwrap(); + std::fs::write(&after_file, format!("{reparsed_ast_normalized:#?}\n")).unwrap(); internal_error!( "Formatting bug; formatting didn't reparse as the same tree\n\n\ diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index d360a8e691b..50fa9b60f87 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -414,12 +414,10 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result { match matches.value_source(ROC_FILE) { Some(ValueSource::DefaultValue) => { eprintln!( - "\nThe current directory ({}) does not contain a {} file to use as a default.\n\nYou can run `roc help` for more information on how to provide a .roc file.\n", - current_dir_string, - DEFAULT_ROC_FILENAME + "\nThe current directory ({current_dir_string}) does not contain a {DEFAULT_ROC_FILENAME} file to use as a default.\n\nYou can run `roc help` for more information on how to provide a .roc file.\n" ) } - _ => eprintln!("\nThis file was not found: {}\n\nYou can run `roc help` for more information on how to provide a .roc file.\n", expected_file_path_string), + _ => eprintln!("\nThis file was not found: {expected_file_path_string}\n\nYou can run `roc help` for more information on how to provide a .roc file.\n"), } process::exit(1); @@ -563,16 +561,13 @@ pub fn build( match matches.value_source(ROC_FILE) { Some(ValueSource::DefaultValue) => { eprintln!( - "\nThe current directory ({}) does not contain a {} file to use as a default.\n\nYou can run `roc help` for more information on how to provide a .roc file.\n", - current_dir_string, - DEFAULT_ROC_FILENAME + "\nThe current directory ({current_dir_string}) does not contain a {DEFAULT_ROC_FILENAME} file to use as a default.\n\nYou can run `roc help` for more information on how to provide a .roc file.\n" ) } _ => { let mut error_lines = Vec::new(); error_lines.push(format!( - "This file was not found: {}", - expected_file_path_string + "This file was not found: {expected_file_path_string}" )); // Add some additional hints if run as `roc [FILENAME]`. if matches.subcommand().is_none() { @@ -582,8 +577,7 @@ pub fn build( nearest_match(possible_typo, subcommands) { error_lines.push(format!( - "Did you mean to use the {} subcommand?", - nearest_command + "Did you mean to use the {nearest_command} subcommand?" )); } } @@ -1142,7 +1136,7 @@ fn roc_run_executable_file_path(binary_bytes: &[u8]) -> std::io::Result io::Result<()> { } Err(LoadingProblem::FormattedReport(report)) => { - print!("{}", report); + print!("{report}"); Ok(1) } Err(other) => { - panic!("build_file failed with error:\n{:?}", other); + panic!("build_file failed with error:\n{other:?}"); } } } @@ -273,7 +273,7 @@ fn main() -> io::Result<()> { let format_exit_code = match format(roc_files, format_mode) { Ok(_) => 0, Err(message) => { - eprintln!("{}", message); + eprintln!("{message}"); 1 } }; diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index 826c8c146b4..e187a218f47 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -90,7 +90,7 @@ mod cli_run { // e.g. "1 error and 0 warnings found in 123 ms." let (before_first_digit, _) = err.split_at(err.rfind("found in ").unwrap()); - let err = format!("{}found in ms.", before_first_digit); + let err = format!("{before_first_digit}found in ms."); // make paths consistent let err = err.replace('\\', "/"); @@ -230,7 +230,7 @@ mod cli_run { what: _, xwhat, } = error; - println!("Valgrind Error: {}\n", kind); + println!("Valgrind Error: {kind}\n"); if let Some(ValgrindErrorXWhat { text, @@ -238,14 +238,14 @@ mod cli_run { leakedblocks: _, }) = xwhat { - println!(" {}", text); + println!(" {text}"); } } panic!("Valgrind reported memory errors"); } } else { let exit_code = match valgrind_out.status.code() { - Some(code) => format!("exit code {}", code), + Some(code) => format!("exit code {code}"), None => "no exit code".to_string(), }; @@ -301,7 +301,7 @@ mod cli_run { // e.g. "1 failed and 0 passed in 123 ms." if let Some(split) = actual.rfind("passed in ") { let (before_first_digit, _) = actual.split_at(split); - actual = format!("{}passed in ms.", before_first_digit); + actual = format!("{before_first_digit}passed in ms."); } let self_path = file.display().to_string(); @@ -397,8 +397,7 @@ mod cli_run { "swiftui" | "rocLovesSwift" => { if cfg!(not(target_os = "macos")) { eprintln!( - "WARNING: skipping testing example {} because it only works on MacOS.", - roc_filename + "WARNING: skipping testing example {roc_filename} because it only works on MacOS." ); return; } else { @@ -409,8 +408,7 @@ mod cli_run { "rocLovesWebAssembly" => { // this is a web assembly example, but we don't test with JS at the moment eprintln!( - "WARNING: skipping testing example {} because it only works in a browser!", - roc_filename + "WARNING: skipping testing example {roc_filename} because it only works in a browser!" ); return; } @@ -965,16 +963,14 @@ mod cli_run { match roc_filename { "QuicksortApp.roc" => { eprintln!( - "WARNING: skipping testing benchmark {} because the test is broken right now!", - roc_filename + "WARNING: skipping testing benchmark {roc_filename} because the test is broken right now!" ); return; } "TestAStar.roc" => { if cfg!(feature = "wasm32-cli-run") { eprintln!( - "WARNING: skipping testing benchmark {} because it currently does not work on wasm32 due to dictionaries.", - roc_filename + "WARNING: skipping testing benchmark {roc_filename} because it currently does not work on wasm32 due to dictionaries." ); return; } diff --git a/crates/cli/tests/editor.rs b/crates/cli/tests/editor.rs index f6ec81e579d..8680e5de6aa 100644 --- a/crates/cli/tests/editor.rs +++ b/crates/cli/tests/editor.rs @@ -30,7 +30,7 @@ mod editor_launch_test { // The editor expects to be run from the root of the repo, so it can find the cli-platform to init a new project folder. env::set_current_dir(&root_dir) - .unwrap_or_else(|_| panic!("Failed to set current dir to {:?}", root_dir)); + .unwrap_or_else(|_| panic!("Failed to set current dir to {root_dir:?}")); let roc_binary_path = build_roc_bin(&["--features", "editor"]); diff --git a/crates/cli/tests/fixtures/multi-dep-str/platform/host.zig b/crates/cli/tests/fixtures/multi-dep-str/platform/host.zig index 4248e171a8c..2a4fb112849 100644 --- a/crates/cli/tests/fixtures/multi-dep-str/platform/host.zig +++ b/crates/cli/tests/fixtures/multi-dep-str/platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/crates/cli/tests/fixtures/multi-dep-thunk/platform/host.zig b/crates/cli/tests/fixtures/multi-dep-thunk/platform/host.zig index 18bb5d00749..a7cff59a0b1 100644 --- a/crates/cli/tests/fixtures/multi-dep-thunk/platform/host.zig +++ b/crates/cli/tests/fixtures/multi-dep-thunk/platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/crates/cli/tests/fixtures/packages/platform/host.zig b/crates/cli/tests/fixtures/packages/platform/host.zig index 4248e171a8c..2a4fb112849 100644 --- a/crates/cli/tests/fixtures/packages/platform/host.zig +++ b/crates/cli/tests/fixtures/packages/platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/crates/cli_testing_examples/algorithms/fibonacci-platform/host.zig b/crates/cli_testing_examples/algorithms/fibonacci-platform/host.zig index c27916c26f2..6e2d2e6b393 100644 --- a/crates/cli_testing_examples/algorithms/fibonacci-platform/host.zig +++ b/crates/cli_testing_examples/algorithms/fibonacci-platform/host.zig @@ -5,20 +5,6 @@ const expectEqual = testing.expectEqual; const expect = testing.expect; const maxInt = std.math.maxInt; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/crates/cli_testing_examples/algorithms/quicksort-platform/host.zig b/crates/cli_testing_examples/algorithms/quicksort-platform/host.zig index c402674a231..1ae4b1cc0e6 100644 --- a/crates/cli_testing_examples/algorithms/quicksort-platform/host.zig +++ b/crates/cli_testing_examples/algorithms/quicksort-platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/crates/cli_testing_examples/benchmarks/CFold.roc b/crates/cli_testing_examples/benchmarks/CFold.roc index 8fbb087e337..e4b1e2423dd 100644 --- a/crates/cli_testing_examples/benchmarks/CFold.roc +++ b/crates/cli_testing_examples/benchmarks/CFold.roc @@ -21,7 +21,7 @@ main = |> Task.putLine Err GetIntError -> - Task.putLine "Error: Failed to get Integer from stdin." + Task.putLine "Error: Failed to get Integer from stdin." Expr : [ Add Expr Expr, @@ -100,35 +100,27 @@ constFolding = \e -> x1 = constFolding e1 x2 = constFolding e2 - when Pair x1 x2 is - Pair (Val a) (Val b) -> - Val (a + b) + when x1 is + Val a -> + when x2 is + Val b -> Val (a + b) + Add (Val b) x | Add x (Val b) -> Add (Val (a + b)) x + _ -> Add x1 x2 - Pair (Val a) (Add (Val b) x) -> - Add (Val (a + b)) x - - Pair (Val a) (Add x (Val b)) -> - Add (Val (a + b)) x - - Pair y1 y2 -> - Add y1 y2 + _ -> Add x1 x2 Mul e1 e2 -> x1 = constFolding e1 x2 = constFolding e2 - when Pair x1 x2 is - Pair (Val a) (Val b) -> - Val (a * b) - - Pair (Val a) (Mul (Val b) x) -> - Mul (Val (a * b)) x - - Pair (Val a) (Mul x (Val b)) -> - Mul (Val (a * b)) x + when x1 is + Val a -> + when x2 is + Val b -> Val (a * b) + Mul (Val b) x | Mul x (Val b) -> Mul (Val (a * b)) x + _ -> Mul x1 x2 - Pair y1 y2 -> - Add y1 y2 + _ -> Mul x1 x2 _ -> e diff --git a/crates/cli_testing_examples/benchmarks/Deriv.roc b/crates/cli_testing_examples/benchmarks/Deriv.roc index 5ad3f3b9d0c..974c1ada668 100644 --- a/crates/cli_testing_examples/benchmarks/Deriv.roc +++ b/crates/cli_testing_examples/benchmarks/Deriv.roc @@ -23,7 +23,6 @@ main = Err GetIntError -> Task.putLine "Error: Failed to get Integer from stdin." - nest : (I64, Expr -> IO Expr), I64, Expr -> IO Expr nest = \f, n, e -> Task.loop { s: n, f, m: n, x: e } nestHelp diff --git a/crates/cli_testing_examples/benchmarks/NQueens.roc b/crates/cli_testing_examples/benchmarks/NQueens.roc index e1a94144282..15593e37216 100644 --- a/crates/cli_testing_examples/benchmarks/NQueens.roc +++ b/crates/cli_testing_examples/benchmarks/NQueens.roc @@ -10,8 +10,8 @@ main = when inputResult is Ok n -> queens n # original koka 13 - |> Num.toStr - |> Task.putLine + |> Num.toStr + |> Task.putLine Err GetIntError -> Task.putLine "Error: Failed to get Integer from stdin." @@ -21,7 +21,8 @@ ConsList a : [Nil, Cons a (ConsList a)] queens = \n -> length (findSolutions n n) findSolutions = \n, k -> - if k <= 0 then # should we use U64 as input type here instead? + if k <= 0 then + # should we use U64 as input type here instead? Cons Nil Nil else extend n Nil (findSolutions n (k - 1)) @@ -40,14 +41,15 @@ appendSafe = \k, soln, solns -> else appendSafe (k - 1) soln solns - safe : I64, I64, ConsList I64 -> Bool safe = \queen, diagonal, xs -> when xs is Nil -> Bool.true Cons q t -> - queen != q && queen != q + diagonal && queen != q - diagonal && safe queen (diagonal + 1) t - + if queen != q && queen != q + diagonal && queen != q - diagonal then + safe queen (diagonal + 1) t + else + Bool.false length : ConsList a -> I64 length = \xs -> diff --git a/crates/cli_testing_examples/benchmarks/QuicksortApp.roc b/crates/cli_testing_examples/benchmarks/QuicksortApp.roc index cc38026216c..67766bc9820 100644 --- a/crates/cli_testing_examples/benchmarks/QuicksortApp.roc +++ b/crates/cli_testing_examples/benchmarks/QuicksortApp.roc @@ -23,7 +23,6 @@ main = Err GetIntError -> Task.putLine "Error: Failed to get Integer from stdin." - sort : List I64 -> List I64 sort = \list -> diff --git a/crates/cli_testing_examples/benchmarks/RBTreeCk.roc b/crates/cli_testing_examples/benchmarks/RBTreeCk.roc index 254be6d0ebf..5c685b195e6 100644 --- a/crates/cli_testing_examples/benchmarks/RBTreeCk.roc +++ b/crates/cli_testing_examples/benchmarks/RBTreeCk.roc @@ -93,9 +93,15 @@ ins = \tree, kx, vx -> Node Black a ky vy b -> if lt kx ky then - (if isRed a then balance1 (Node Black Leaf ky vy b) (ins a kx vx) else Node Black (ins a kx vx) ky vy b) + if isRed a then + balance1 (Node Black Leaf ky vy b) (ins a kx vx) + else + Node Black (ins a kx vx) ky vy b else if lt ky kx then - (if isRed b then balance2 (Node Black a ky vy Leaf) (ins b kx vx) else Node Black a ky vy (ins b kx vx)) + if isRed b then + balance2 (Node Black a ky vy Leaf) (ins b kx vx) + else + Node Black a ky vy (ins b kx vx) else Node Black a kx vx b diff --git a/crates/cli_testing_examples/benchmarks/RBTreeDel.roc b/crates/cli_testing_examples/benchmarks/RBTreeDel.roc index 37f0523ecac..7f5e940b73d 100644 --- a/crates/cli_testing_examples/benchmarks/RBTreeDel.roc +++ b/crates/cli_testing_examples/benchmarks/RBTreeDel.roc @@ -26,7 +26,6 @@ main = Err GetIntError -> Task.putLine "Error: Failed to get Integer from stdin." - boom : Str -> a boom = \_ -> boom "" diff --git a/crates/cli_testing_examples/benchmarks/platform/host.zig b/crates/cli_testing_examples/benchmarks/platform/host.zig index 85f343c061b..7902302f451 100644 --- a/crates/cli_testing_examples/benchmarks/platform/host.zig +++ b/crates/cli_testing_examples/benchmarks/platform/host.zig @@ -7,20 +7,6 @@ const expectEqual = testing.expectEqual; const expect = testing.expect; const maxInt = std.math.maxInt; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/crates/cli_testing_examples/expects/zig-platform/host.zig b/crates/cli_testing_examples/expects/zig-platform/host.zig index 20c09320d90..33c1995d8df 100644 --- a/crates/cli_testing_examples/expects/zig-platform/host.zig +++ b/crates/cli_testing_examples/expects/zig-platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const Align = 2 * @alignOf(usize); extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque; extern fn realloc(c_ptr: [*]align(Align) u8, size: usize) callconv(.C) ?*anyopaque; diff --git a/crates/cli_utils/src/bench_utils.rs b/crates/cli_utils/src/bench_utils.rs index 31da15a8e78..ddac5232bff 100644 --- a/crates/cli_utils/src/bench_utils.rs +++ b/crates/cli_utils/src/bench_utils.rs @@ -25,8 +25,7 @@ fn exec_bench_w_input( assert!( compile_out.status.success(), - "build ended with bad status {:?}", - compile_out + "build ended with bad status {compile_out:?}" ); check_cmd_output(file, stdin_str, executable_filename, expected_ending); @@ -58,10 +57,7 @@ fn check_cmd_output( }; if !&out.stdout.ends_with(expected_ending) { - panic!( - "expected output to end with {:?} but instead got {:#?}", - expected_ending, out - ); + panic!("expected output to end with {expected_ending:?} but instead got {out:#?}"); } assert!(out.status.success()); } @@ -96,7 +92,7 @@ fn bench_cmd( } if let Some(bench_group) = bench_group_opt { - bench_group.bench_function(&format!("Benchmarking {:?}", executable_filename), |b| { + bench_group.bench_function(&format!("Benchmarking {executable_filename:?}"), |b| { b.iter(|| run_cmd(black_box(&cmd_str), black_box([stdin_str]), &[], [])) }); } else { diff --git a/crates/cli_utils/src/helpers.rs b/crates/cli_utils/src/helpers.rs index dbc28659f34..1239e0bcbd4 100644 --- a/crates/cli_utils/src/helpers.rs +++ b/crates/cli_utils/src/helpers.rs @@ -216,7 +216,7 @@ pub fn build_roc_bin(extra_args: &[&str]) -> PathBuf { cargo_cmd.current_dir(root_project_dir).args(&args); - let cargo_cmd_str = format!("{:?}", cargo_cmd); + let cargo_cmd_str = format!("{cargo_cmd:?}"); let cargo_output = cargo_cmd.output().unwrap(); @@ -255,7 +255,7 @@ pub fn run_cmd<'a, I: IntoIterator, E: IntoIterator, E: IntoIterator St let node = mark_node_pool.get(root_node_id); - writeln!(full_string, "{} mn_id {}\n", node, root_node_id).unwrap(); + writeln!(full_string, "{node} mn_id {root_node_id}\n").unwrap(); tree_as_string_helper(node, 1, &mut full_string, mark_node_pool); @@ -399,7 +399,7 @@ fn tree_as_string_helper( .join("") .to_owned(); - writeln!(full_str, "{} mn_id {}", child_str, child_id).unwrap(); + writeln!(full_str, "{child_str} mn_id {child_id}").unwrap(); tree_string.push_str(&full_str); diff --git a/crates/code_markup/src/markup_error.rs b/crates/code_markup/src/markup_error.rs index 5e888652d24..80abf3d004e 100644 --- a/crates/code_markup/src/markup_error.rs +++ b/crates/code_markup/src/markup_error.rs @@ -56,7 +56,7 @@ pub type MarkResult = std::result::Result; impl From for MarkError { fn from(util_err: UtilError) -> Self { - let msg = format!("{}", util_err); + let msg = format!("{util_err}"); // hack to handle MarkError derive let dummy_res: Result<(), NoneError> = Err(NoneError {}); diff --git a/crates/code_markup/src/slow_pool.rs b/crates/code_markup/src/slow_pool.rs index 9699e8dc9df..a5526eca134 100644 --- a/crates/code_markup/src/slow_pool.rs +++ b/crates/code_markup/src/slow_pool.rs @@ -39,8 +39,8 @@ impl SlowPool { for (mark_node_id, node) in self.nodes.iter().enumerate() { let ast_node_id_str = match mark_id_ast_id_map.get(mark_node_id) { - Ok(ast_id) => format!("{:?}", ast_id), - Err(err) => format!("{:?}", err), + Ok(ast_id) => format!("{ast_id:?}"), + Err(err) => format!("{err:?}"), }; let ast_node_id: String = ast_node_id_str .chars() @@ -52,7 +52,7 @@ impl SlowPool { let node_children = node.get_children_ids(); if !node_children.is_empty() { - child_str = format!("children: {:?}", node_children); + child_str = format!("children: {node_children:?}"); } write!( diff --git a/crates/compiler/alias_analysis/src/lib.rs b/crates/compiler/alias_analysis/src/lib.rs index 855b54f86e0..d6d843cf2f4 100644 --- a/crates/compiler/alias_analysis/src/lib.rs +++ b/crates/compiler/alias_analysis/src/lib.rs @@ -117,7 +117,7 @@ where } if debug() { - for (i, c) in (format!("{:?}", symbol)).chars().take(25).enumerate() { + for (i, c) in (format!("{symbol:?}")).chars().take(25).enumerate() { name_bytes[25 + i] = c as u8; } } @@ -131,7 +131,7 @@ fn bytes_as_ascii(bytes: &[u8]) -> String { let mut buf = String::new(); for byte in bytes { - write!(buf, "{:02X}", byte).unwrap(); + write!(buf, "{byte:02X}").unwrap(); } buf @@ -510,6 +510,12 @@ fn apply_refcount_operation( builder.add_recursive_touch(block, argument)?; } ModifyRc::DecRef(symbol) => { + // this is almost certainly suboptimal, but not incorrect + let argument = env.symbols[symbol]; + builder.add_recursive_touch(block, argument)?; + } + ModifyRc::Free(symbol) => { + // this is almost certainly suboptimal, but not incorrect let argument = env.symbols[symbol]; builder.add_recursive_touch(block, argument)?; } @@ -1309,16 +1315,11 @@ fn expr_spec<'a>( builder.add_unknown_with(block, &[], pointer_type) } Call(call) => call_spec(builder, interner, env, block, layout, call), - Reuse { - tag_layout, - tag_id, - arguments, - .. - } - | Tag { + Tag { tag_layout, tag_id, arguments, + reuse: _, } => { let data_id = build_tuple_value(builder, env, block, arguments)?; @@ -1357,16 +1358,6 @@ fn expr_spec<'a>( builder.add_make_named(block, MOD_APP, type_name, tag_value_id) } - ExprBox { symbol } => { - let value_id = env.symbols[symbol]; - - with_new_heap_cell(builder, block, value_id) - } - ExprUnbox { symbol } => { - let tuple_id = env.symbols[symbol]; - - builder.add_get_tuple_field(block, tuple_id, BOX_VALUE_INDEX) - } Struct(fields) => build_tuple_value(builder, env, block, fields), UnionAtIndex { index, @@ -1422,6 +1413,38 @@ fn expr_spec<'a>( builder.add_get_tuple_field(block, variant_id, index) } }, + UnionFieldPtrAtIndex { + index, + tag_id, + structure, + union_layout, + } => { + let index = (*index) as u32; + let tag_value_id = env.symbols[structure]; + + let type_name_bytes = recursive_tag_union_name_bytes(union_layout).as_bytes(); + let type_name = TypeName(&type_name_bytes); + + // unwrap the named wrapper + let union_id = builder.add_unwrap_named(block, MOD_APP, type_name, tag_value_id)?; + + // now we have a tuple (cell, union { ... }); decompose + let heap_cell = builder.add_get_tuple_field(block, union_id, TAG_CELL_INDEX)?; + let union_data = builder.add_get_tuple_field(block, union_id, TAG_DATA_INDEX)?; + + // we're reading from this value, so touch the heap cell + builder.add_touch(block, heap_cell)?; + + // next, unwrap the union at the tag id that we've got + let variant_id = builder.add_unwrap_union(block, union_data, *tag_id as u32)?; + + let value = builder.add_get_tuple_field(block, variant_id, index)?; + + // construct the box. Here the heap_cell of the tag is re-used, I'm hoping that that + // conveys to morphic that we're borrowing into the existing tag?! + builder.add_make_tuple(block, &[heap_cell, value]) + } + StructAtIndex { index, structure, .. } => { @@ -1589,13 +1612,14 @@ fn layout_spec_help<'a>( } } - Boxed(inner_layout) => { + Ptr(inner_layout) => { let inner_type = layout_spec_help(env, builder, interner, interner.get_repr(inner_layout))?; let cell_type = builder.add_heap_cell_type(); builder.add_tuple_type(&[cell_type, inner_type]) } + // TODO(recursive-layouts): update once we have recursive pointer loops RecursivePointer(union_layout) => match interner.get_repr(union_layout) { LayoutRepr::Union(union_layout) => { @@ -1650,10 +1674,6 @@ fn static_list_type(builder: &mut TC) -> Result { const LIST_CELL_INDEX: u32 = 0; const LIST_BAG_INDEX: u32 = 1; -#[allow(dead_code)] -const BOX_CELL_INDEX: u32 = LIST_CELL_INDEX; -const BOX_VALUE_INDEX: u32 = LIST_BAG_INDEX; - const TAG_CELL_INDEX: u32 = 0; const TAG_DATA_INDEX: u32 = 1; diff --git a/crates/compiler/build/src/link.rs b/crates/compiler/build/src/link.rs index 2c69ced034f..519b88bca83 100644 --- a/crates/compiler/build/src/link.rs +++ b/crates/compiler/build/src/link.rs @@ -126,7 +126,7 @@ fn find_wasi_libc_path() -> PathBuf { internal_error!("cannot find `wasi-libc.a`") } -#[cfg(all(unix, not(target_os = "macos")))] +#[cfg(unix)] #[allow(clippy::too_many_arguments)] pub fn build_zig_host_native( env_path: &str, @@ -161,7 +161,7 @@ pub fn build_zig_host_native( zig_cmd.args([ zig_host_src, - &format!("-femit-bin={}", emit_bin), + &format!("-femit-bin={emit_bin}"), "--pkg-begin", "glue", find_zig_glue_path().to_str().unwrap(), @@ -257,99 +257,6 @@ pub fn build_zig_host_native( zig_cmd } -#[cfg(target_os = "macos")] -#[allow(clippy::too_many_arguments)] -pub fn build_zig_host_native( - env_path: &str, - env_home: &str, - emit_bin: &str, - zig_host_src: &str, - _target: &str, - opt_level: OptLevel, - shared_lib_path: Option<&Path>, - builtins_host_path: &Path, - // For compatibility with the non-macOS def above. Keep these in sync. -) -> Command { - use serde_json::Value; - - // Run `zig env` to find the location of zig's std/ directory - let zig_env_output = zig().args(["env"]).output().unwrap(); - - let zig_env_json = if zig_env_output.status.success() { - std::str::from_utf8(&zig_env_output.stdout).unwrap_or_else(|utf8_err| { - internal_error!( - "`zig env` failed; its stderr output was invalid utf8 ({:?})", - utf8_err - ); - }) - } else { - match std::str::from_utf8(&zig_env_output.stderr) { - Ok(stderr) => internal_error!("`zig env` failed - stderr output was: {:?}", stderr), - Err(utf8_err) => internal_error!( - "`zig env` failed; its stderr output was invalid utf8 ({:?})", - utf8_err - ), - } - }; - - let mut zig_compiler_rt_path = match serde_json::from_str(zig_env_json) { - Ok(Value::Object(map)) => match map.get("std_dir") { - Some(Value::String(std_dir)) => PathBuf::from(Path::new(std_dir)), - _ => { - internal_error!("Expected JSON containing a `std_dir` String field from `zig env`, but got: {:?}", zig_env_json); - } - }, - _ => { - internal_error!( - "Expected JSON containing a `std_dir` field from `zig env`, but got: {:?}", - zig_env_json - ); - } - }; - - zig_compiler_rt_path.push("special"); - zig_compiler_rt_path.push("compiler_rt.zig"); - - let mut zig_cmd = zig(); - zig_cmd - .env_clear() - .env("PATH", env_path) - .env("HOME", env_home); - if let Some(shared_lib_path) = shared_lib_path { - zig_cmd.args([ - "build-exe", - "-fPIE", - shared_lib_path.to_str().unwrap(), - builtins_host_path.to_str().unwrap(), - ]); - } else { - zig_cmd.args(["build-obj"]); - } - zig_cmd.args([ - zig_host_src, - &format!("-femit-bin={}", emit_bin), - "--pkg-begin", - "glue", - find_zig_glue_path().to_str().unwrap(), - "--pkg-end", - // include the zig runtime - "--pkg-begin", - "compiler_rt", - zig_compiler_rt_path.to_str().unwrap(), - "--pkg-end", - // include libc - "--library", - "c", - ]); - if matches!(opt_level, OptLevel::Optimize) { - zig_cmd.args(["-O", "ReleaseSafe"]); - } else if matches!(opt_level, OptLevel::Size) { - zig_cmd.args(["-O", "ReleaseSmall"]); - } - - zig_cmd -} - pub fn build_zig_host_wasm32( env_path: &str, env_home: &str, @@ -492,7 +399,7 @@ pub fn build_swift_host_native( match arch { Architecture::Aarch64(_) => command.arg("-arm64"), - _ => command.arg(format!("-{}", arch)), + _ => command.arg(format!("-{arch}")), }; command @@ -623,7 +530,7 @@ pub fn rebuild_host( // on windows, we need the nightly toolchain so we can use `-Z export-executable-symbols` // using `+nightly` only works when running cargo through rustup let mut cmd = rustup(); - cmd.args(["run", "nightly-2022-10-30", "cargo"]); + cmd.args(["run", "nightly-2022-12-09", "cargo"]); cmd } else { @@ -1021,7 +928,7 @@ fn link_linux( .map(|segments| segments.join("/")) .collect::>() .join("\n"); - eprintln!("We looked in the following directories:\n{}", dirs); + eprintln!("We looked in the following directories:\n{dirs}"); process::exit(1); } }; @@ -1178,8 +1085,8 @@ fn link_macos( let sdk_path = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib"; if Path::new(sdk_path).exists() { - ld_command.arg(format!("-L{}", sdk_path)); - ld_command.arg(format!("-L{}/swift", sdk_path)); + ld_command.arg(format!("-L{sdk_path}")); + ld_command.arg(format!("-L{sdk_path}/swift")); }; let roc_link_flags = match env::var("ROC_LINK_FLAGS") { @@ -1381,9 +1288,7 @@ pub fn llvm_module_to_dylib( assert!( exit_status.success(), - "\n___________\nLinking command failed with status {:?}:\n\n {:?}\n___________\n", - exit_status, - child + "\n___________\nLinking command failed with status {exit_status:?}:\n\n {child:?}\n___________\n" ); // Load the dylib diff --git a/crates/compiler/build/src/program.rs b/crates/compiler/build/src/program.rs index 09f8123f261..083b07cbbab 100644 --- a/crates/compiler/build/src/program.rs +++ b/crates/compiler/build/src/program.rs @@ -166,7 +166,7 @@ fn gen_from_mono_module_llvm<'a>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let enum_attr = context.create_enum_attribute(kind_id, 1); + let enum_attr = context.create_enum_attribute(kind_id, 0); for function in module.get_functions() { let name = function.get_name().to_str().unwrap(); @@ -320,10 +320,10 @@ fn gen_from_mono_module_llvm<'a>( if !unrecognized.is_empty() { let out = unrecognized .iter() - .map(|x| format!("{:?}", x)) + .map(|x| format!("{x:?}")) .collect::>() .join(", "); - eprintln!("Unrecognized sanitizer: {}\nSupported options are \"address\", \"memory\", \"thread\", \"cargo-fuzz\", and \"afl.rs\".", out); + eprintln!("Unrecognized sanitizer: {out}\nSupported options are \"address\", \"memory\", \"thread\", \"cargo-fuzz\", and \"afl.rs\"."); eprintln!("Note: \"cargo-fuzz\" and \"afl.rs\" both enable sanitizer coverage for fuzzing. They just use different parameters to match the respective libraries.") } @@ -340,7 +340,7 @@ fn gen_from_mono_module_llvm<'a>( } let opt = opt.output().unwrap(); - assert!(opt.stderr.is_empty(), "{:#?}", opt); + assert!(opt.stderr.is_empty(), "{opt:#?}"); // write the .o file. Note that this builds the .o for the local machine, // and ignores the `target_machine` entirely. @@ -358,7 +358,7 @@ fn gen_from_mono_module_llvm<'a>( .output() .unwrap(); - assert!(bc_to_object.status.success(), "{:#?}", bc_to_object); + assert!(bc_to_object.status.success(), "{bc_to_object:#?}"); MemoryBuffer::create_from_file(&app_o_file).expect("memory buffer creation works") } else if emit_debug_info { @@ -414,7 +414,7 @@ fn gen_from_mono_module_llvm<'a>( .output() .unwrap(); - assert!(ll_to_object.stderr.is_empty(), "{:#?}", ll_to_object); + assert!(ll_to_object.stderr.is_empty(), "{ll_to_object:#?}"); } _ => unreachable!(), } @@ -716,13 +716,13 @@ pub fn handle_error_module( pub fn handle_loading_problem(problem: LoadingProblem) -> std::io::Result { match problem { LoadingProblem::FormattedReport(report) => { - print!("{}", report); + print!("{report}"); Ok(1) } _ => { // TODO: tighten up the types here, we should always end up with a // formatted report from load. - print!("Failed with error: {:?}", problem); + print!("Failed with error: {problem:?}"); Ok(1) } } @@ -889,7 +889,7 @@ fn build_loaded_file<'a>( buf.push('\n'); use std::fmt::Write; - write!(buf, "{}", module_timing).unwrap(); + write!(buf, "{module_timing}").unwrap(); if it.peek().is_some() { buf.push('\n'); @@ -914,10 +914,7 @@ fn build_loaded_file<'a>( .expect("Failed to (re)build platform."); if emit_timings && !is_platform_prebuilt { - println!( - "Finished rebuilding the platform in {} ms\n", - rebuild_duration - ); + println!("Finished rebuilding the platform in {rebuild_duration} ms\n"); } Some(HostRebuildTiming::BeforeApp(rebuild_duration)) @@ -957,8 +954,7 @@ fn build_loaded_file<'a>( if emit_timings { println!( - "\n\nCompilation finished!\n\nHere's how long each module took to compile:\n\n{}", - buf + "\n\nCompilation finished!\n\nHere's how long each module took to compile:\n\n{buf}" ); println!( @@ -972,10 +968,7 @@ fn build_loaded_file<'a>( let rebuild_duration = thread.join().expect("Failed to (re)build platform."); if emit_timings && !is_platform_prebuilt { - println!( - "Finished rebuilding the platform in {} ms\n", - rebuild_duration - ); + println!("Finished rebuilding the platform in {rebuild_duration} ms\n"); } } @@ -1007,7 +1000,7 @@ fn build_loaded_file<'a>( }; let app_o_file = tempfile::Builder::new() .prefix("roc_app") - .suffix(&format!(".{}", extension)) + .suffix(&format!(".{extension}")) .tempfile() .map_err(|err| todo!("TODO Gracefully handle tempfile creation error {:?}", err))?; let app_o_file = app_o_file.path(); @@ -1257,8 +1250,7 @@ pub fn check_file<'a>( if emit_timings { println!( - "\n\nCompilation finished!\n\nHere's how long each module took to compile:\n\n{}", - buf + "\n\nCompilation finished!\n\nHere's how long each module took to compile:\n\n{buf}" ); println!("Finished checking in {} ms\n", compilation_end.as_millis(),); diff --git a/crates/compiler/build/src/target.rs b/crates/compiler/build/src/target.rs index 3105170bf04..5246634598c 100644 --- a/crates/compiler/build/src/target.rs +++ b/crates/compiler/build/src/target.rs @@ -87,12 +87,12 @@ pub fn target_zig_str(target: &Triple) -> &'static str { architecture: Architecture::X86_64, operating_system: OperatingSystem::Darwin, .. - } => "x86_64-apple-darwin", + } => "x86_64-macos-gnu", Triple { architecture: Architecture::Aarch64(_), operating_system: OperatingSystem::Darwin, .. - } => "aarch64-apple-darwin", + } => "aarch64-macos-gnu", _ => internal_error!("TODO gracefully handle unsupported target: {:?}", target), } } @@ -149,13 +149,27 @@ pub fn target_machine( init_arch(target); + // We used to have a problem that LLVM 12 would not compile our programs without a large code model. + // The reason was not totally clear to us, but one guess is a few special-cases in + // llvm/lib/Target/AArch64/AArch64ISelLowering.cpp (instructions) + // llvm/lib/Target/AArch64/AArch64Subtarget.cpp (GoT tables) + // Revisit when upgrading to LLVM 13. + // + // Most recently, we seem to only see this problem on macOS ARM64; removing this + // failed macOS CI here: https://github.com/roc-lang/roc/pull/5644 + #[cfg(all(target_arch = "aarch64", target_os = "macos"))] + let code_model = CodeModel::Large; + + #[cfg(not(all(target_arch = "aarch64", target_os = "macos")))] + let code_model = CodeModel::Default; + Target::from_name(arch).unwrap().create_target_machine( &TargetTriple::create(target_triple_str(target)), "generic", "", opt, reloc, - CodeModel::Default, + code_model, ) } diff --git a/crates/compiler/builtins/bitcode/bc/build.rs b/crates/compiler/builtins/bitcode/bc/build.rs index 130342b1bc6..38bdbd75c08 100644 --- a/crates/compiler/builtins/bitcode/bc/build.rs +++ b/crates/compiler/builtins/bitcode/bc/build.rs @@ -62,12 +62,12 @@ fn generate_bc_file(bitcode_path: &Path, zig_object: &str, file_name: &str) { ll_path.set_extension("ll"); let dest_ir_host = ll_path.to_str().expect("Invalid dest ir path"); - println!("Compiling host ir to: {}", dest_ir_host); + println!("Compiling host ir to: {dest_ir_host}"); let mut bc_path = bitcode_path.join(file_name); bc_path.set_extension("bc"); let dest_bc_64bit = bc_path.to_str().expect("Invalid dest bc path"); - println!("Compiling 64-bit bitcode to: {}", dest_bc_64bit); + println!("Compiling 64-bit bitcode to: {dest_bc_64bit}"); // workaround for github.com/ziglang/zig/issues/9711 #[cfg(target_os = "macos")] @@ -105,7 +105,7 @@ fn run_command(mut command: Command, flaky_fail_counter: usize) { false => { let error_str = match str::from_utf8(&output.stderr) { Ok(stderr) => stderr.to_string(), - Err(_) => format!("Failed to run \"{}\"", command_str), + Err(_) => format!("Failed to run \"{command_str}\""), }; // Flaky test errors that only occur sometimes on MacOS ci server. diff --git a/crates/compiler/builtins/bitcode/build.rs b/crates/compiler/builtins/bitcode/build.rs index 3a55837ed36..d957ee0a8c8 100644 --- a/crates/compiler/builtins/bitcode/build.rs +++ b/crates/compiler/builtins/bitcode/build.rs @@ -66,7 +66,7 @@ fn generate_object_file(bitcode_path: &Path, zig_object: &str, object_file_name: let src_obj_path = bitcode_path.join(object_file_name); let src_obj = src_obj_path.to_str().expect("Invalid src object path"); - println!("Compiling zig object `{}` to: {}", zig_object, src_obj); + println!("Compiling zig object `{zig_object}` to: {src_obj}"); if !DEBUG { let mut zig_cmd = zig(); @@ -77,7 +77,7 @@ fn generate_object_file(bitcode_path: &Path, zig_object: &str, object_file_name: run_command(zig_cmd, 0); - println!("Moving zig object `{}` to: {}", zig_object, dest_obj); + println!("Moving zig object `{zig_object}` to: {dest_obj}"); // we store this .o file in rust's `target` folder (for wasm we need to leave a copy here too) fs::copy(src_obj, dest_obj).unwrap_or_else(|err| { @@ -167,7 +167,7 @@ fn run_command(mut command: Command, flaky_fail_counter: usize) { false => { let error_str = match str::from_utf8(&output.stderr) { Ok(stderr) => stderr.to_string(), - Err(_) => format!("Failed to run \"{}\"", command_str), + Err(_) => format!("Failed to run \"{command_str}\""), }; // Flaky test errors that only occur sometimes on MacOS ci server. diff --git a/crates/compiler/builtins/bitcode/src/compiler_rt.zig b/crates/compiler/builtins/bitcode/src/compiler_rt.zig index b00961fa218..e4bc02da9bf 100644 --- a/crates/compiler/builtins/bitcode/src/compiler_rt.zig +++ b/crates/compiler/builtins/bitcode/src/compiler_rt.zig @@ -18,6 +18,7 @@ const v2u64 = @Vector(2, u64); // Export it as weak incase it is already linked in by something else. comptime { @export(__muloti4, .{ .name = "__muloti4", .linkage = .Weak }); + @export(__lshrti3, .{ .name = "__lshrti3", .linkage = .Weak }); if (want_windows_v2u64_abi) { @export(__divti3_windows_x86_64, .{ .name = "__divti3", .linkage = .Weak }); @export(__modti3_windows_x86_64, .{ .name = "__modti3", .linkage = .Weak }); @@ -440,3 +441,47 @@ pub inline fn floatFractionalBits(comptime T: type) comptime_int { else => @compileError("unknown floating point type " ++ @typeName(T)), }; } + +pub fn __lshrti3(a: i128, b: i32) callconv(.C) i128 { + return lshrXi3(i128, a, b); +} + +// Logical shift right: shift in 0 from left to right +// Precondition: 0 <= b < T.bit_count +inline fn lshrXi3(comptime T: type, a: T, b: i32) T { + const word_t = HalveInt(T, false); + const S = std.math.Log2Int(word_t.HalfT); + + const input = word_t{ .all = a }; + var output: word_t = undefined; + + if (b >= word_t.bits) { + output.s.high = 0; + output.s.low = input.s.high >> @intCast(S, b - word_t.bits); + } else if (b == 0) { + return a; + } else { + output.s.high = input.s.high >> @intCast(S, b); + output.s.low = input.s.high << @intCast(S, word_t.bits - b); + output.s.low |= input.s.low >> @intCast(S, b); + } + + return output.all; +} + +/// Allows to access underlying bits as two equally sized lower and higher +/// signed or unsigned integers. +fn HalveInt(comptime T: type, comptime signed_half: bool) type { + return extern union { + pub const bits = @divExact(@typeInfo(T).Int.bits, 2); + pub const HalfTU = std.meta.Int(.unsigned, bits); + pub const HalfTS = std.meta.Int(.signed, bits); + pub const HalfT = if (signed_half) HalfTS else HalfTU; + + all: T, + s: if (native_endian == .Little) + extern struct { low: HalfT, high: HalfT } + else + extern struct { high: HalfT, low: HalfT }, + }; +} diff --git a/crates/compiler/builtins/bitcode/src/main.zig b/crates/compiler/builtins/bitcode/src/main.zig index 82bb7237563..a349ce26c31 100644 --- a/crates/compiler/builtins/bitcode/src/main.zig +++ b/crates/compiler/builtins/bitcode/src/main.zig @@ -195,8 +195,10 @@ comptime { exportUtilsFn(utils.test_panic, "test_panic"); exportUtilsFn(utils.increfRcPtrC, "incref_rc_ptr"); exportUtilsFn(utils.decrefRcPtrC, "decref_rc_ptr"); + exportUtilsFn(utils.freeRcPtrC, "free_rc_ptr"); exportUtilsFn(utils.increfDataPtrC, "incref_data_ptr"); exportUtilsFn(utils.decrefDataPtrC, "decref_data_ptr"); + exportUtilsFn(utils.freeDataPtrC, "free_data_ptr"); exportUtilsFn(utils.isUnique, "is_unique"); exportUtilsFn(utils.decrefCheckNullC, "decref_check_null"); exportUtilsFn(utils.allocateWithRefcountC, "allocate_with_refcount"); diff --git a/crates/compiler/builtins/bitcode/src/utils.zig b/crates/compiler/builtins/bitcode/src/utils.zig index 3a2e07b72da..d609aefbf82 100644 --- a/crates/compiler/builtins/bitcode/src/utils.zig +++ b/crates/compiler/builtins/bitcode/src/utils.zig @@ -220,6 +220,29 @@ pub fn increfDataPtrC( return increfRcPtrC(isizes, inc_amount); } +pub fn freeDataPtrC( + bytes_or_null: ?[*]isize, + alignment: u32, +) callconv(.C) void { + var bytes = bytes_or_null orelse return; + + const ptr = @ptrToInt(bytes); + const tag_mask: usize = if (@sizeOf(usize) == 8) 0b111 else 0b11; + const masked_ptr = ptr & ~tag_mask; + + const isizes: [*]isize = @intToPtr([*]isize, masked_ptr); + + return freeRcPtrC(isizes - 1, alignment); +} + +pub fn freeRcPtrC( + bytes_or_null: ?[*]isize, + alignment: u32, +) callconv(.C) void { + var bytes = bytes_or_null orelse return; + return free_ptr_to_refcount(bytes, alignment); +} + pub fn decref( bytes_or_null: ?[*]u8, data_bytes: usize, @@ -236,13 +259,23 @@ pub fn decref( decref_ptr_to_refcount(isizes - 1, alignment); } -inline fn decref_ptr_to_refcount( +inline fn free_ptr_to_refcount( refcount_ptr: [*]isize, alignment: u32, ) void { if (RC_TYPE == Refcount.none) return; const extra_bytes = std.math.max(alignment, @sizeOf(usize)); + // NOTE: we don't even check whether the refcount is "infinity" here! + dealloc(@ptrCast([*]u8, refcount_ptr) - (extra_bytes - @sizeOf(usize)), alignment); +} + +inline fn decref_ptr_to_refcount( + refcount_ptr: [*]isize, + alignment: u32, +) void { + if (RC_TYPE == Refcount.none) return; + if (DEBUG_INCDEC and builtin.target.cpu.arch != .wasm32) { std.debug.print("| decrement {*}: ", .{refcount_ptr}); } @@ -264,13 +297,13 @@ inline fn decref_ptr_to_refcount( } if (refcount == REFCOUNT_ONE_ISIZE) { - dealloc(@ptrCast([*]u8, refcount_ptr) - (extra_bytes - @sizeOf(usize)), alignment); + free_ptr_to_refcount(refcount_ptr, alignment); } }, Refcount.atomic => { var last = @atomicRmw(isize, &refcount_ptr[0], std.builtin.AtomicRmwOp.Sub, 1, Monotonic); if (last == REFCOUNT_ONE_ISIZE) { - dealloc(@ptrCast([*]u8, refcount_ptr) - (extra_bytes - @sizeOf(usize)), alignment); + free_ptr_to_refcount(refcount_ptr, alignment); } }, Refcount.none => unreachable, diff --git a/crates/compiler/builtins/roc/Dict.roc b/crates/compiler/builtins/roc/Dict.roc index 097b0942109..ec316cf351b 100644 --- a/crates/compiler/builtins/roc/Dict.roc +++ b/crates/compiler/builtins/roc/Dict.roc @@ -7,6 +7,7 @@ interface Dict clear, capacity, len, + isEmpty, get, contains, insert, @@ -21,6 +22,8 @@ interface Dict insertAll, keepShared, removeAll, + map, + joinMap, ] imports [ Bool.{ Bool, Eq }, @@ -98,14 +101,14 @@ Dict k v := { data : List (k, v), size : Nat, } | k has Hash & Eq - has [ - Eq { - isEq, - }, - Hash { - hash: hashDict, - }, - ] + has [ + Eq { + isEq, + }, + Hash { + hash: hashDict, + }, + ] isEq : Dict k v, Dict k v -> Bool | k has Hash & Eq, v has Eq isEq = \xs, ys -> @@ -139,12 +142,12 @@ empty = \{} -> ## Returns the max number of elements the dictionary can hold before requiring a rehash. ## ``` ## foodDict = -## Dict.empty {} -## |> Dict.insert "apple" "fruit" +## Dict.empty {} +## |> Dict.insert "apple" "fruit" ## ## capacityOfDict = Dict.capacity foodDict ## ``` -capacity : Dict k v -> Nat | k has Hash & Eq +capacity : Dict * * -> Nat capacity = \@Dict { dataIndices } -> cap = List.len dataIndices @@ -192,10 +195,20 @@ fromList = \data -> ## |> Dict.len ## |> Bool.isEq 3 ## ``` -len : Dict k v -> Nat | k has Hash & Eq +len : Dict * * -> Nat len = \@Dict { size } -> size +## Check if the dictinoary is empty. +## ``` +## Dict.isEmpty (Dict.empty {} |> Dict.insert "key" 42) +## +## Dict.isEmpty (Dict.empty {}) +## ``` +isEmpty : Dict * * -> Bool +isEmpty = \@Dict { size } -> + size == 0 + ## Clears all elements from a dictionary keeping around the allocation if it isn't huge. ## ``` ## songs = @@ -225,6 +238,28 @@ clear = \@Dict { metadata, dataIndices, data } -> size: 0, } +## Convert each value in the dictionary to something new, by calling a conversion +## function on each of them which receives both the key and the old value. Then return a +## new dictionary containing the same keys and the converted values. +map : Dict k a, (k, a -> b) -> Dict k b | k has Hash & Eq, b has Hash & Eq +map = \dict, transform -> + init = withCapacity (capacity dict) + + walk dict init \answer, k, v -> + insert answer k (transform k v) + +## Like [Dict.map], except the transformation function wraps the return value +## in a dictionary. At the end, all the dictionaries get joined together +## (using [Dict.insertAll]) into one dictionary. +## +## You may know a similar function named `concatMap` in other languages. +joinMap : Dict a b, (a, b -> Dict x y) -> Dict x y | a has Hash & Eq, x has Hash & Eq +joinMap = \dict, transform -> + init = withCapacity (capacity dict) # Might be a pessimization + + walk dict init \answer, k, v -> + insertAll answer (transform k v) + ## Iterate through the keys and values in the dictionary and call the provided ## function with signature `state, k, v -> state` for each value, with an ## initial `state` value provided for the first call. @@ -976,16 +1011,16 @@ expect # TODO: Add a builtin to distinguish big endian systems and change loading orders. # TODO: Switch out Wymum on systems with slow 128bit multiplication. LowLevelHasher := { originalSeed : U64, state : U64 } has [ - Hasher { - addBytes, - addU8, - addU16, - addU32, - addU64, - addU128, - complete, - }, - ] + Hasher { + addBytes, + addU8, + addU16, + addU32, + addU64, + addU128, + complete, + }, + ] # unsafe primitive that does not perform a bounds check # TODO hide behind an InternalList.roc module diff --git a/crates/compiler/builtins/roc/List.roc b/crates/compiler/builtins/roc/List.roc index c9ed2875050..76447c10169 100644 --- a/crates/compiler/builtins/roc/List.roc +++ b/crates/compiler/builtins/roc/List.roc @@ -208,6 +208,9 @@ interface List ## * Even when copying is faster, other list operations may still be slightly slower with persistent data structures. For example, even if it were a persistent data structure, [List.map], [List.walk], and [List.keepIf] would all need to traverse every element in the list and build up the result from scratch. These operations are all ## * Roc's compiler optimizes many list operations into in-place mutations behind the scenes, depending on how the list is being used. For example, [List.map], [List.keepIf], and [List.set] can all be optimized to perform in-place mutations. ## * If possible, it is usually best for performance to use large lists in a way where the optimizer can turn them into in-place mutations. If this is not possible, a persistent data structure might be faster - but this is a rare enough scenario that it would not be good for the average Roc program's performance if this were the way [List] worked by default. Instead, you can look outside Roc's standard modules for an implementation of a persistent data structure - likely built using [List] under the hood! + +# separator so List.isEmpty doesn't absorb the above into its doc comment + ## Check if the list is empty. ## ``` ## List.isEmpty [1, 2, 3] diff --git a/crates/compiler/builtins/roc/Result.roc b/crates/compiler/builtins/roc/Result.roc index a9075aebbf3..7ff20351768 100644 --- a/crates/compiler/builtins/roc/Result.roc +++ b/crates/compiler/builtins/roc/Result.roc @@ -42,8 +42,8 @@ withDefault = \result, default -> ## function on it. Then returns a new `Ok` holding the transformed value. If the ## result is `Err`, this has no effect. Use [mapErr] to transform an `Err`. ## ``` -## Result.map (Ok 12) Num.negate -## Result.map (Err "yipes!") Num.negate +## Result.map (Ok 12) Num.neg +## Result.map (Err "yipes!") Num.neg ## ``` ## ## Functions like `map` are common in Roc; see for example [List.map], diff --git a/crates/compiler/builtins/roc/Set.roc b/crates/compiler/builtins/roc/Set.roc index 24c54df999f..7fc76c98078 100644 --- a/crates/compiler/builtins/roc/Set.roc +++ b/crates/compiler/builtins/roc/Set.roc @@ -7,6 +7,8 @@ interface Set walkUntil, insert, len, + isEmpty, + capacity, remove, contains, toList, @@ -14,6 +16,8 @@ interface Set union, intersection, difference, + map, + joinMap, ] imports [ List, @@ -26,14 +30,14 @@ interface Set ## Provides a [set](https://en.wikipedia.org/wiki/Set_(abstract_data_type)) ## type which stores a collection of unique values, without any ordering Set k := Dict.Dict k {} | k has Hash & Eq - has [ - Eq { - isEq, - }, - Hash { - hash: hashSet, - }, - ] + has [ + Eq { + isEq, + }, + Hash { + hash: hashSet, + }, + ] isEq : Set k, Set k -> Bool | k has Hash & Eq isEq = \xs, ys -> @@ -59,6 +63,13 @@ hashSet = \hasher, @Set inner -> Hash.hash hasher inner empty : {} -> Set k | k has Hash & Eq empty = \{} -> @Set (Dict.empty {}) +## Return a dictionary with space allocated for a number of entries. This +## may provide a performance optimization if you know how many entries will be +## inserted. +withCapacity : Nat -> Set k | k has Hash & Eq +withCapacity = \cap -> + @Set (Dict.withCapacity cap) + ## Creates a new `Set` with a single value. ## ``` ## singleItemSet = Set.single "Apple" @@ -115,10 +126,32 @@ expect ## ## expect countValues == 3 ## ``` -len : Set k -> Nat | k has Hash & Eq +len : Set * -> Nat len = \@Set dict -> Dict.len dict +## Returns the max number of elements the set can hold before requiring a rehash. +## ``` +## foodSet = +## Set.empty {} +## |> Set.insert "apple" +## +## capacityOfSet = Set.capacity foodSet +## ``` +capacity : Set * -> Nat +capacity = \@Set dict -> + Dict.capacity dict + +## Check if the set is empty. +## ``` +## Set.isEmpty (Set.empty {} |> Set.insert 42) +## +## Set.isEmpty (Set.empty {}) +## ``` +isEmpty : Set * -> Bool +isEmpty = \@Set dict -> + Dict.isEmpty dict + # Inserting a duplicate key has no effect on length. expect actual = @@ -261,6 +294,28 @@ walk : Set k, state, (state, k -> state) -> state | k has Hash & Eq walk = \@Set dict, state, step -> Dict.walk dict state (\s, k, _ -> step s k) +## Convert each value in the set to something new, by calling a conversion +## function on each of them which receives the old value. Then return a +## new set containing the converted values. +map : Set a, (a -> b) -> Set b | a has Hash & Eq, b has Hash & Eq +map = \set, transform -> + init = withCapacity (capacity set) + + walk set init \answer, k -> + insert answer (transform k) + +## Like [Set.map], except the transformation function wraps the return value +## in a set. At the end, all the sets get joined together +## (using [Set.union]) into one set. +## +## You may know a similar function named `concatMap` in other languages. +joinMap : Set a, (a -> Set b) -> Set b | a has Hash & Eq, b has Hash & Eq +joinMap = \set, transform -> + init = withCapacity (capacity set) # Might be a pessimization + + walk set init \answer, k -> + union answer (transform k) + ## Iterate through the values of a given `Set` and build a value, can stop ## iterating part way through the collection. ## ``` diff --git a/crates/compiler/builtins/roc/TotallyNotJson.roc b/crates/compiler/builtins/roc/TotallyNotJson.roc index 93c0feb9126..6df2a9cf7c0 100644 --- a/crates/compiler/builtins/roc/TotallyNotJson.roc +++ b/crates/compiler/builtins/roc/TotallyNotJson.roc @@ -44,49 +44,49 @@ interface TotallyNotJson ## An opaque type with the `EncoderFormatting` and ## `DecoderFormatting` abilities. Json := { fieldNameMapping : FieldNameMapping } - has [ - EncoderFormatting { - u8: encodeU8, - u16: encodeU16, - u32: encodeU32, - u64: encodeU64, - u128: encodeU128, - i8: encodeI8, - i16: encodeI16, - i32: encodeI32, - i64: encodeI64, - i128: encodeI128, - f32: encodeF32, - f64: encodeF64, - dec: encodeDec, - bool: encodeBool, - string: encodeString, - list: encodeList, - record: encodeRecord, - tuple: encodeTuple, - tag: encodeTag, - }, - DecoderFormatting { - u8: decodeU8, - u16: decodeU16, - u32: decodeU32, - u64: decodeU64, - u128: decodeU128, - i8: decodeI8, - i16: decodeI16, - i32: decodeI32, - i64: decodeI64, - i128: decodeI128, - f32: decodeF32, - f64: decodeF64, - dec: decodeDec, - bool: decodeBool, - string: decodeString, - list: decodeList, - record: decodeRecord, - tuple: decodeTuple, - }, - ] + has [ + EncoderFormatting { + u8: encodeU8, + u16: encodeU16, + u32: encodeU32, + u64: encodeU64, + u128: encodeU128, + i8: encodeI8, + i16: encodeI16, + i32: encodeI32, + i64: encodeI64, + i128: encodeI128, + f32: encodeF32, + f64: encodeF64, + dec: encodeDec, + bool: encodeBool, + string: encodeString, + list: encodeList, + record: encodeRecord, + tuple: encodeTuple, + tag: encodeTag, + }, + DecoderFormatting { + u8: decodeU8, + u16: decodeU16, + u32: decodeU32, + u64: decodeU64, + u128: decodeU128, + i8: decodeI8, + i16: decodeI16, + i32: decodeI32, + i64: decodeI64, + i128: decodeI128, + f32: decodeF32, + f64: decodeF64, + dec: decodeDec, + bool: decodeBool, + string: decodeString, + list: decodeList, + record: decodeRecord, + tuple: decodeTuple, + }, + ] ## Returns a JSON `Encoder` and `Decoder` json = @Json { fieldNameMapping: Default } diff --git a/crates/compiler/builtins/src/bitcode.rs b/crates/compiler/builtins/src/bitcode.rs index dfc6a667716..92869d9fc48 100644 --- a/crates/compiler/builtins/src/bitcode.rs +++ b/crates/compiler/builtins/src/bitcode.rs @@ -393,8 +393,10 @@ pub const UTILS_TEST_PANIC: &str = "roc_builtins.utils.test_panic"; pub const UTILS_ALLOCATE_WITH_REFCOUNT: &str = "roc_builtins.utils.allocate_with_refcount"; pub const UTILS_INCREF_RC_PTR: &str = "roc_builtins.utils.incref_rc_ptr"; pub const UTILS_DECREF_RC_PTR: &str = "roc_builtins.utils.decref_rc_ptr"; +pub const UTILS_FREE_RC_PTR: &str = "roc_builtins.utils.free_rc_ptr"; pub const UTILS_INCREF_DATA_PTR: &str = "roc_builtins.utils.incref_data_ptr"; pub const UTILS_DECREF_DATA_PTR: &str = "roc_builtins.utils.decref_data_ptr"; +pub const UTILS_FREE_DATA_PTR: &str = "roc_builtins.utils.free_data_ptr"; pub const UTILS_IS_UNIQUE: &str = "roc_builtins.utils.is_unique"; pub const UTILS_DECREF_CHECK_NULL: &str = "roc_builtins.utils.decref_check_null"; pub const UTILS_DICT_PSEUDO_SEED: &str = "roc_builtins.utils.dict_pseudo_seed"; diff --git a/crates/compiler/can/src/abilities.rs b/crates/compiler/can/src/abilities.rs index f5f056ebcb8..893702add13 100644 --- a/crates/compiler/can/src/abilities.rs +++ b/crates/compiler/can/src/abilities.rs @@ -469,8 +469,7 @@ impl IAbilitiesStore { debug_assert!( old_specialization.is_none(), - "Existing resolution: {:?}", - old_specialization + "Existing resolution: {old_specialization:?}" ); } diff --git a/crates/compiler/can/src/builtins.rs b/crates/compiler/can/src/builtins.rs index 9f05e61ddfe..55004df8b3c 100644 --- a/crates/compiler/can/src/builtins.rs +++ b/crates/compiler/can/src/builtins.rs @@ -85,7 +85,10 @@ macro_rules! map_symbol_to_lowlevel_and_arity { // these are used internally and not tied to a symbol LowLevel::Hash => unimplemented!(), LowLevel::PtrCast => unimplemented!(), - LowLevel::PtrWrite => unimplemented!(), + LowLevel::PtrStore => unimplemented!(), + LowLevel::PtrLoad => unimplemented!(), + LowLevel::PtrClearTagId => unimplemented!(), + LowLevel::Alloca => unimplemented!(), LowLevel::RefCountIncRcPtr => unimplemented!(), LowLevel::RefCountDecRcPtr=> unimplemented!(), LowLevel::RefCountIncDataPtr => unimplemented!(), diff --git a/crates/compiler/can/src/constraint.rs b/crates/compiler/can/src/constraint.rs index e22cb2fe453..26a3f8dbe36 100644 --- a/crates/compiler/can/src/constraint.rs +++ b/crates/compiler/can/src/constraint.rs @@ -64,6 +64,26 @@ pub type PExpectedTypeIndex = Index>; pub type TypeOrVar = EitherIndex; impl Constraints { + pub fn empty() -> Self { + Self { + constraints: Default::default(), + type_slices: Default::default(), + variables: Default::default(), + loc_symbols: Default::default(), + let_constraints: Default::default(), + categories: Default::default(), + pattern_categories: Default::default(), + expectations: Default::default(), + pattern_expectations: Default::default(), + includes_tags: Default::default(), + strings: Default::default(), + sketched_rows: Default::default(), + eq: Default::default(), + pattern_eq: Default::default(), + cycles: Default::default(), + } + } + pub fn new() -> Self { let constraints = Vec::new(); let type_slices = Vec::with_capacity(16); @@ -176,7 +196,7 @@ impl Constraints { let mut buf = String::new(); - writeln!(buf, "Constraints statistics for module {:?}:", module_id)?; + writeln!(buf, "Constraints statistics for module {module_id:?}:")?; writeln!(buf, " constraints length: {}:", self.constraints.len())?; writeln!( @@ -833,16 +853,16 @@ impl std::fmt::Debug for Constraint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Eq(Eq(arg0, arg1, arg2, arg3)) => { - write!(f, "Eq({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3) + write!(f, "Eq({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})") } Self::Store(arg0, arg1, arg2, arg3) => { - write!(f, "Store({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3) + write!(f, "Store({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})") } Self::Lookup(arg0, arg1, arg2) => { - write!(f, "Lookup({:?}, {:?}, {:?})", arg0, arg1, arg2) + write!(f, "Lookup({arg0:?}, {arg1:?}, {arg2:?})") } Self::Pattern(arg0, arg1, arg2, arg3) => { - write!(f, "Pattern({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3) + write!(f, "Pattern({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})") } Self::True => write!(f, "True"), Self::SaveTheEnvironment => write!(f, "SaveTheEnvironment"), @@ -851,27 +871,19 @@ impl std::fmt::Debug for Constraint { Self::IsOpenType(arg0) => f.debug_tuple("IsOpenType").field(arg0).finish(), Self::IncludesTag(arg0) => f.debug_tuple("IncludesTag").field(arg0).finish(), Self::PatternPresence(arg0, arg1, arg2, arg3) => { - write!( - f, - "PatternPresence({:?}, {:?}, {:?}, {:?})", - arg0, arg1, arg2, arg3 - ) + write!(f, "PatternPresence({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})") } Self::Exhaustive(arg0, arg1, arg2, arg3) => { - write!( - f, - "Exhaustive({:?}, {:?}, {:?}, {:?})", - arg0, arg1, arg2, arg3 - ) + write!(f, "Exhaustive({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})") } Self::Resolve(arg0) => { - write!(f, "Resolve({:?})", arg0) + write!(f, "Resolve({arg0:?})") } Self::CheckCycle(arg0, arg1) => { - write!(f, "CheckCycle({:?}, {:?})", arg0, arg1) + write!(f, "CheckCycle({arg0:?}, {arg1:?})") } Self::IngestedFile(arg0, arg1, arg2) => { - write!(f, "IngestedFile({:?}, {:?}, {:?})", arg0, arg1, arg2) + write!(f, "IngestedFile({arg0:?}, {arg1:?}, {arg2:?})") } } } diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index 94279debd07..847382fcc81 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -24,7 +24,7 @@ trait CopyEnv { if descriptor.copy.into_variable().is_some() { descriptor.copy = OptVariable::NONE; } else { - debug_assert!(false, "{:?} marked as copied but it wasn't", var); + debug_assert!(false, "{var:?} marked as copied but it wasn't"); } }) } @@ -1320,7 +1320,7 @@ mod test { FlexVar(Some(name)) => { assert_eq!(subs[*name].as_str(), "a"); } - it => panic!("{:?}", it), + it => panic!("{it:?}"), } assert_eq!(var, variant_var); assert!(matches!( @@ -1337,7 +1337,7 @@ mod test { FlexVar(Some(name)) => { assert_eq!(subs[*name].as_str(), "b"); } - it => panic!("{:?}", it), + it => panic!("{it:?}"), } match arg.value { @@ -1355,10 +1355,10 @@ mod test { assert_eq!(name.0.as_str(), "G"); assert_eq!(arguments.len(), 0); } - e => panic!("{:?}", e), + e => panic!("{e:?}"), } } - e => panic!("{:?}", e), + e => panic!("{e:?}"), } } @@ -1403,7 +1403,7 @@ mod test { FlexVar(Some(name)) => { assert_eq!(target[*name].as_str(), "a"); } - it => panic!("{:?}", it), + it => panic!("{it:?}"), } assert_eq!(var, variant_var); assert!(matches!( @@ -1418,7 +1418,7 @@ mod test { FlexVar(Some(name)) => { assert_eq!(target[*name].as_str(), "b"); } - it => panic!("{:?}", it), + it => panic!("{it:?}"), } match arg.value { @@ -1436,10 +1436,10 @@ mod test { assert_eq!(name.0.as_str(), "G"); assert_eq!(arguments.len(), 0); } - e => panic!("{:?}", e), + e => panic!("{e:?}"), } } - e => panic!("{:?}", e), + e => panic!("{e:?}"), } } diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 79c05a58716..7fecd6ccc21 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -1069,8 +1069,7 @@ fn canonicalize_value_defs<'a>( debug_assert_eq!(env.home, s.module_id()); debug_assert!( !symbol_to_index.iter().any(|(id, _)| *id == s.ident_id()), - "{:?}", - s + "{s:?}" ); symbol_to_index.push((s.ident_id(), def_index as u32)); @@ -1838,7 +1837,7 @@ pub(crate) fn sort_can_defs( ); let declaration = if def_ordering.references.get_row_col(index, index) { - debug_assert!(!is_specialization, "Self-recursive specializations can only be determined during solving - but it was determined for {:?} now, that's a bug!", def); + debug_assert!(!is_specialization, "Self-recursive specializations can only be determined during solving - but it was determined for {def:?} now, that's a bug!"); if is_initial && !def @@ -2272,12 +2271,18 @@ fn canonicalize_pending_body<'a>( opt_loc_annotation: Option>, ) -> DefOutput { + let mut loc_value = &loc_expr.value; + + while let ast::Expr::ParensAround(value) = loc_value { + loc_value = value; + } + // We treat closure definitions `foo = \a, b -> ...` differently from other body expressions, // because they need more bookkeeping (for tail calls, closure captures, etc.) // // Only defs of the form `foo = ...` can be closure declarations or self tail calls. let (loc_can_expr, def_references) = { - match (&loc_can_pattern.value, &loc_expr.value) { + match (&loc_can_pattern.value, &loc_value) { ( Pattern::Identifier(defined_symbol) | Pattern::AbilityMemberSpecialization { diff --git a/crates/compiler/can/src/derive.rs b/crates/compiler/can/src/derive.rs index a5e6e645f22..fd85aaa514b 100644 --- a/crates/compiler/can/src/derive.rs +++ b/crates/compiler/can/src/derive.rs @@ -222,16 +222,16 @@ pub(crate) fn synthesize_member_impl<'a>( ability_member: Symbol, ) -> (Symbol, Loc, &'a Loc>) { // @Opaq - let at_opaque = env.arena.alloc_str(&format!("@{}", opaque_name)); + let at_opaque = env.arena.alloc_str(&format!("@{opaque_name}")); let (impl_name, def_body): (String, ast::Expr<'a>) = match ability_member { Symbol::ENCODE_TO_ENCODER => ( - format!("#{}_toEncoder", opaque_name), + format!("#{opaque_name}_toEncoder"), to_encoder(env, at_opaque), ), - Symbol::DECODE_DECODER => (format!("#{}_decoder", opaque_name), decoder(env, at_opaque)), - Symbol::HASH_HASH => (format!("#{}_hash", opaque_name), hash(env, at_opaque)), - Symbol::BOOL_IS_EQ => (format!("#{}_isEq", opaque_name), is_eq(env, at_opaque)), + Symbol::DECODE_DECODER => (format!("#{opaque_name}_decoder"), decoder(env, at_opaque)), + Symbol::HASH_HASH => (format!("#{opaque_name}_hash"), hash(env, at_opaque)), + Symbol::BOOL_IS_EQ => (format!("#{opaque_name}_isEq"), is_eq(env, at_opaque)), other => internal_error!("{:?} is not a derivable ability member!", other), }; diff --git a/crates/compiler/can/src/effect_module.rs b/crates/compiler/can/src/effect_module.rs index 7c8629db24c..91fdb5dd728 100644 --- a/crates/compiler/can/src/effect_module.rs +++ b/crates/compiler/can/src/effect_module.rs @@ -1362,7 +1362,7 @@ pub fn build_host_exposed_def( match typ.shallow_structural_dealias() { Type::Function(args, _, _) => { for i in 0..args.len() { - let name = format!("closure_arg_{}_{}", ident, i); + let name = format!("closure_arg_{ident}_{i}"); let arg_symbol = { let ident = name.clone().into(); @@ -1381,7 +1381,7 @@ pub fn build_host_exposed_def( linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol, arg_var))); } - let foreign_symbol_name = format!("roc_fx_{}", ident); + let foreign_symbol_name = format!("roc_fx_{ident}"); let low_level_call = Expr::ForeignCall { foreign_symbol: foreign_symbol_name.into(), args: linked_symbol_arguments, @@ -1389,7 +1389,7 @@ pub fn build_host_exposed_def( }; let effect_closure_symbol = { - let name = format!("effect_closure_{}", ident); + let name = format!("effect_closure_{ident}"); let ident = name.into(); scope.introduce(ident, Region::zero()).unwrap() @@ -1435,7 +1435,7 @@ pub fn build_host_exposed_def( _ => { // not a function - let foreign_symbol_name = format!("roc_fx_{}", ident); + let foreign_symbol_name = format!("roc_fx_{ident}"); let low_level_call = Expr::ForeignCall { foreign_symbol: foreign_symbol_name.into(), args: linked_symbol_arguments, @@ -1443,7 +1443,7 @@ pub fn build_host_exposed_def( }; let effect_closure_symbol = { - let name = format!("effect_closure_{}", ident); + let name = format!("effect_closure_{ident}"); let ident = name.into(); scope.introduce(ident, Region::zero()).unwrap() diff --git a/crates/compiler/can/src/env.rs b/crates/compiler/can/src/env.rs index 5c052d2f071..38c26ec464b 100644 --- a/crates/compiler/can/src/env.rs +++ b/crates/compiler/can/src/env.rs @@ -67,8 +67,7 @@ impl<'a> Env<'a> { ) -> Result { debug_assert!( !module_name_str.is_empty(), - "Called env.qualified_lookup with an unqualified ident: {:?}", - ident + "Called env.qualified_lookup with an unqualified ident: {ident:?}" ); let module_name = ModuleName::from(module_name_str); diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 62ee13504ba..0beecfd7004 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -1403,14 +1403,13 @@ pub fn canonicalize_expr<'a>( (answer, Output::default()) } + &ast::Expr::ParensAround(sub_expr) => { + let (loc_expr, output) = canonicalize_expr(env, var_store, scope, region, sub_expr); + + (loc_expr.value, output) + } // Below this point, we shouln't see any of these nodes anymore because // operator desugaring should have removed them! - bad_expr @ ast::Expr::ParensAround(_) => { - internal_error!( - "A ParensAround did not get removed during operator desugaring somehow: {:#?}", - bad_expr - ); - } bad_expr @ ast::Expr::SpaceBefore(_, _) => { internal_error!( "A SpaceBefore did not get removed during operator desugaring somehow: {:#?}", diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index 8517d3c1df4..d0c36000741 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -347,9 +347,7 @@ pub fn canonicalize_module_defs<'a>( // the symbol should already be added to the scope when this module is canonicalized debug_assert!( scope.contains_alias(symbol) || scope.abilities_store.is_ability(symbol), - "The {:?} is not a type alias or ability known in {:?}", - symbol, - home + "The {symbol:?} is not a type alias or ability known in {home:?}" ); // but now we know this symbol by a different identifier, so we still need to add it to diff --git a/crates/compiler/can/src/num.rs b/crates/compiler/can/src/num.rs index 886de610278..6efa4d3805a 100644 --- a/crates/compiler/can/src/num.rs +++ b/crates/compiler/can/src/num.rs @@ -220,8 +220,7 @@ fn from_str_radix(src: &str, radix: u32) -> Result(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc break builder_arg.closure; } - SpaceBefore(expr, _) | SpaceAfter(expr, _) | ParensAround(expr) => { + SpaceBefore(expr, _) | SpaceAfter(expr, _) => { current = *expr; } _ => break loc_arg, @@ -382,7 +382,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc region: loc_expr.region, }) } - SpaceBefore(expr, _) | SpaceAfter(expr, _) | ParensAround(expr) => { + SpaceBefore(expr, _) | SpaceAfter(expr, _) => { // Since we've already begun canonicalization, spaces and parens // are no longer needed and should be dropped. desugar_expr( @@ -393,6 +393,20 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc>) -> &'a Loc }), ) } + ParensAround(expr) => { + let desugared = desugar_expr( + arena, + arena.alloc(Loc { + value: **expr, + region: loc_expr.region, + }), + ); + + arena.alloc(Loc { + value: ParensAround(&desugared.value), + region: loc_expr.region, + }) + } If(if_thens, final_else_branch) => { // If does not get desugared into `when` so we can give more targeted error messages during type checking. let desugared_final_else = &*arena.alloc(desugar_expr(arena, final_else_branch)); diff --git a/crates/compiler/can/src/pattern.rs b/crates/compiler/can/src/pattern.rs index 4e96e604b25..2f61468fa2e 100644 --- a/crates/compiler/can/src/pattern.rs +++ b/crates/compiler/can/src/pattern.rs @@ -531,7 +531,7 @@ pub fn canonicalize_pattern<'a>( use std::ops::Neg; let sign_str = if is_negative { "-" } else { "" }; - let int_str = format!("{}{}", sign_str, int).into_boxed_str(); + let int_str = format!("{sign_str}{int}").into_boxed_str(); let i = match int { // Safety: this is fine because I128::MAX = |I128::MIN| - 1 IntValue::I128(n) if is_negative => { diff --git a/crates/compiler/can/tests/helpers/mod.rs b/crates/compiler/can/tests/helpers/mod.rs index 5442135f926..d2f95267c68 100644 --- a/crates/compiler/can/tests/helpers/mod.rs +++ b/crates/compiler/can/tests/helpers/mod.rs @@ -37,8 +37,7 @@ pub struct CanExprOut { pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut { let loc_expr = roc_parse::test_helpers::parse_loc_with(arena, expr_str).unwrap_or_else(|e| { panic!( - "can_expr_with() got a parse error when attempting to canonicalize:\n\n{:?} {:?}", - expr_str, e + "can_expr_with() got a parse error when attempting to canonicalize:\n\n{expr_str:?} {e:?}" ) }); diff --git a/crates/compiler/can/tests/test_can.rs b/crates/compiler/can/tests/test_can.rs index 7086931de0e..2afe3eff297 100644 --- a/crates/compiler/can/tests/test_can.rs +++ b/crates/compiler/can/tests/test_can.rs @@ -422,7 +422,7 @@ mod test_can { let CanExprOut { problems, .. } = can_expr_with(&arena, test_home(), src); assert_eq!(problems.len(), 2); - println!("{:#?}", problems); + println!("{problems:#?}"); assert!(problems.iter().any(|problem| matches!( problem, Problem::RuntimeError(RuntimeError::Shadowing { .. }) diff --git a/crates/compiler/collections/src/all.rs b/crates/compiler/collections/src/all.rs index f0663930001..d42ec32f6c0 100644 --- a/crates/compiler/collections/src/all.rs +++ b/crates/compiler/collections/src/all.rs @@ -202,7 +202,7 @@ fn int_to_ordinal(number: usize) -> std::string::String { }, }; - format!("{}{}", number, ending) + format!("{number}{ending}") } #[macro_export] diff --git a/crates/compiler/collections/src/small_string_interner.rs b/crates/compiler/collections/src/small_string_interner.rs index db3124375a2..f9f009266c8 100644 --- a/crates/compiler/collections/src/small_string_interner.rs +++ b/crates/compiler/collections/src/small_string_interner.rs @@ -48,9 +48,9 @@ enum Kind { impl Debug for Kind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Generated(arg0) => write!(f, "Generated({})", arg0), + Self::Generated(arg0) => write!(f, "Generated({arg0})"), Self::Empty => write!(f, "Empty"), - Self::Interned(arg0) => write!(f, "Interned({})", arg0), + Self::Interned(arg0) => write!(f, "Interned({arg0})"), } } } @@ -155,7 +155,7 @@ impl SmallStringInterner { let index = self.lengths.len(); let offset = self.buffer.len(); - write!(self.buffer, "{}", index).unwrap(); + write!(self.buffer, "{index}").unwrap(); // this is a generated name, so store it as a negative length let length = Length(-((self.buffer.len() - offset) as i16)); diff --git a/crates/compiler/collections/src/vec_set.rs b/crates/compiler/collections/src/vec_set.rs index 52be9862497..2a19bf2b9c7 100644 --- a/crates/compiler/collections/src/vec_set.rs +++ b/crates/compiler/collections/src/vec_set.rs @@ -34,6 +34,12 @@ impl VecSet { self.elements.is_empty() } + pub fn singleton(value: T) -> Self { + Self { + elements: vec![value], + } + } + pub fn swap_remove(&mut self, index: usize) -> T { self.elements.swap_remove(index) } diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 338dc64e29b..972797a7f6e 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -1044,8 +1044,7 @@ pub fn constrain_expr( debug_assert!( intersection.is_empty(), - "Two patterns introduce the same symbols - that's a bug!\n{:?}", - intersection + "Two patterns introduce the same symbols - that's a bug!\n{intersection:?}" ); } diff --git a/crates/compiler/debug_flags/src/lib.rs b/crates/compiler/debug_flags/src/lib.rs index 3852100c710..ede77bd317a 100644 --- a/crates/compiler/debug_flags/src/lib.rs +++ b/crates/compiler/debug_flags/src/lib.rs @@ -135,6 +135,10 @@ flags! { /// instructions. ROC_PRINT_IR_AFTER_REFCOUNT + /// Writes a pretty-printed mono IR to stderr after the tail recursion (modulo cons) + /// has been applied. + ROC_PRINT_IR_AFTER_TRMC + /// Writes a pretty-printed mono IR to stderr after performing dropspecialization. /// Which inlines drop functions to remove pairs of alloc/dealloc instructions of its children. ROC_PRINT_IR_AFTER_DROP_SPECIALIZATION diff --git a/crates/compiler/derive/src/hash.rs b/crates/compiler/derive/src/hash.rs index f01a28eac21..a960fdf0c2d 100644 --- a/crates/compiler/derive/src/hash.rs +++ b/crates/compiler/derive/src/hash.rs @@ -298,7 +298,7 @@ fn hash_tag_union( Expr::Int( discr_num_var, discr_precision_var, - format!("{}", discr_n).into_boxed_str(), + format!("{discr_n}").into_boxed_str(), IntValue::I128((discr_n as i128).to_ne_bytes()), IntBound::Exact(discr_width), ), diff --git a/crates/compiler/derive/src/util.rs b/crates/compiler/derive/src/util.rs index 2319dd2e12f..4cc0f790f52 100644 --- a/crates/compiler/derive/src/util.rs +++ b/crates/compiler/derive/src/util.rs @@ -31,7 +31,7 @@ impl Env<'_> { let name = if i == 1 { hint.clone() } else { - format!("{}{}", hint, i) + format!("{hint}{i}") }; if self.derived_ident_ids.get_id(&name).is_none() { break name; @@ -151,7 +151,7 @@ impl Env<'_> { == self.subs.get_root_key_without_compacting(lambda_set) }); debug_assert!(belongs_to_specialized_lambda_sets, - "Did not expect derivers to need to specialize unspecialized lambda sets, but we got one: {:?} for {:?}", lambda_set, spec_var) + "Did not expect derivers to need to specialize unspecialized lambda sets, but we got one: {lambda_set:?} for {spec_var:?}") } } } diff --git a/crates/compiler/fmt/src/def.rs b/crates/compiler/fmt/src/def.rs index bb5268bcc28..997edb8a376 100644 --- a/crates/compiler/fmt/src/def.rs +++ b/crates/compiler/fmt/src/def.rs @@ -117,7 +117,7 @@ impl<'a> Formattable for TypeDef<'a> { buf, Parens::NotNeeded, Newlines::from_bool(make_multiline), - indent + 1 + INDENT, + indent + INDENT, ); } } diff --git a/crates/compiler/gen_dev/src/generic64/aarch64.rs b/crates/compiler/gen_dev/src/generic64/aarch64.rs index 6f06abf6611..fb777b1f4be 100644 --- a/crates/compiler/gen_dev/src/generic64/aarch64.rs +++ b/crates/compiler/gen_dev/src/generic64/aarch64.rs @@ -832,6 +832,10 @@ impl Assembler for AArch64Assembler { todo!("saving floating point reg to base offset for AArch64"); } #[inline(always)] + fn mov_base32_freg32(_buf: &mut Vec<'_, u8>, _offset: i32, _src: AArch64FloatReg) { + todo!("saving floating point reg to base offset for AArch64"); + } + #[inline(always)] fn movesd_mem64_offset32_freg64( _buf: &mut Vec<'_, u8>, _ptr: AArch64GeneralReg, @@ -3160,7 +3164,7 @@ mod tests { UsesZR => "xzr".to_owned(), UsesSP => "sp".to_owned(), }, - _ => format!("{}", self), + _ => format!("{self}"), } } } diff --git a/crates/compiler/gen_dev/src/generic64/mod.rs b/crates/compiler/gen_dev/src/generic64/mod.rs index 30a7078ee59..32261887609 100644 --- a/crates/compiler/gen_dev/src/generic64/mod.rs +++ b/crates/compiler/gen_dev/src/generic64/mod.rs @@ -321,6 +321,7 @@ pub trait Assembler: Sized + Copy { fn mov_reg8_base32(buf: &mut Vec<'_, u8>, dst: GeneralReg, offset: i32); fn mov_base32_freg64(buf: &mut Vec<'_, u8>, offset: i32, src: FloatReg); + fn mov_base32_freg32(buf: &mut Vec<'_, u8>, offset: i32, src: FloatReg); fn mov_base32_reg64(buf: &mut Vec<'_, u8>, offset: i32, src: GeneralReg); fn mov_base32_reg32(buf: &mut Vec<'_, u8>, offset: i32, src: GeneralReg); @@ -2791,14 +2792,7 @@ impl< .storage_manager .load_to_general_reg(&mut self.buf, structure); - let mask_symbol = self.debug_symbol("tag_id_mask"); - let mask_reg = self - .storage_manager - .claim_general_reg(&mut self.buf, &mask_symbol); - ASM::mov_reg64_imm64(&mut self.buf, mask_reg, (!0b111) as _); - - // mask out the tag id bits - ASM::and_reg64_reg64_reg64(&mut self.buf, ptr_reg, ptr_reg, mask_reg); + let (mask_symbol, mask_reg) = self.clear_tag_id(ptr_reg); let mut offset = 0; for field in &other_fields[..index as usize] { @@ -2809,11 +2803,13 @@ impl< &mut self.buf, &mut self.storage_manager, self.layout_interner, - ptr_reg, + mask_reg, offset as i32, element_layout, *sym, ); + + self.free_symbol(&mask_symbol) } UnionLayout::Recursive(tag_layouts) => { let other_fields = tag_layouts[tag_id as usize]; @@ -2824,15 +2820,13 @@ impl< .load_to_general_reg(&mut self.buf, structure); // mask out the tag id bits - if !union_layout.stores_tag_id_as_data(self.storage_manager.target_info) { - let mask_symbol = self.debug_symbol("tag_id_mask"); - let mask_reg = self - .storage_manager - .claim_general_reg(&mut self.buf, &mask_symbol); - ASM::mov_reg64_imm64(&mut self.buf, mask_reg, (!0b111) as _); - - ASM::and_reg64_reg64_reg64(&mut self.buf, ptr_reg, ptr_reg, mask_reg); - } + let (unmasked_symbol, unmasked_reg) = + if union_layout.stores_tag_id_as_data(self.storage_manager.target_info) { + (None, ptr_reg) + } else { + let (mask_symbol, mask_reg) = self.clear_tag_id(ptr_reg); + (Some(mask_symbol), mask_reg) + }; let mut offset = 0; for field in &other_fields[..index as usize] { @@ -2843,16 +2837,113 @@ impl< &mut self.buf, &mut self.storage_manager, self.layout_interner, - ptr_reg, + unmasked_reg, offset as i32, element_layout, *sym, ); + + if let Some(unmasked_symbol) = unmasked_symbol { + self.free_symbol(&unmasked_symbol); + } + } + } + } + + fn load_union_field_ptr_at_index( + &mut self, + sym: &Symbol, + structure: &Symbol, + tag_id: TagIdIntType, + index: u64, + union_layout: &UnionLayout<'a>, + ) { + let ptr_reg = self + .storage_manager + .load_to_general_reg(&mut self.buf, structure); + + let sym_reg = self.storage_manager.claim_general_reg(&mut self.buf, sym); + + match union_layout { + UnionLayout::NonRecursive(_) => { + unreachable!("operation not supported") + } + UnionLayout::NonNullableUnwrapped(field_layouts) => { + let mut offset = 0; + for field in &field_layouts[..index as usize] { + offset += self.layout_interner.stack_size(*field); + } + + ASM::add_reg64_reg64_imm32(&mut self.buf, sym_reg, ptr_reg, offset as i32); + } + UnionLayout::NullableUnwrapped { + nullable_id, + other_fields, + } => { + debug_assert_ne!(tag_id, *nullable_id as TagIdIntType); + + let mut offset = 0; + for field in &other_fields[..index as usize] { + offset += self.layout_interner.stack_size(*field); + } + + ASM::add_reg64_reg64_imm32(&mut self.buf, sym_reg, ptr_reg, offset as i32); + } + + UnionLayout::NullableWrapped { + nullable_id, + other_tags, + } => { + debug_assert_ne!(tag_id, *nullable_id as TagIdIntType); + + let other_fields = if tag_id < *nullable_id { + other_tags[tag_id as usize] + } else { + other_tags[tag_id as usize - 1] + }; + + let (mask_symbol, mask_reg) = self.clear_tag_id(ptr_reg); + + let mut offset = 0; + for field in &other_fields[..index as usize] { + offset += self.layout_interner.stack_size(*field); + } + + ASM::add_reg64_reg64_imm32(&mut self.buf, sym_reg, mask_reg, offset as i32); + + self.free_symbol(&mask_symbol); + } + UnionLayout::Recursive(tag_layouts) => { + let other_fields = tag_layouts[tag_id as usize]; + + let ptr_reg = self + .storage_manager + .load_to_general_reg(&mut self.buf, structure); + + // mask out the tag id bits + let (unmasked_symbol, unmasked_reg) = + if union_layout.stores_tag_id_as_data(self.storage_manager.target_info) { + (None, ptr_reg) + } else { + let (mask_symbol, mask_reg) = self.clear_tag_id(ptr_reg); + (Some(mask_symbol), mask_reg) + }; + + let mut offset = 0; + for field in &other_fields[..index as usize] { + offset += self.layout_interner.stack_size(*field); + } + + ASM::add_reg64_reg64_imm32(&mut self.buf, sym_reg, unmasked_reg, offset as i32); + + if let Some(unmasked_symbol) = unmasked_symbol { + self.free_symbol(&unmasked_symbol); + } } } } - fn build_ptr_write( + fn build_ptr_store( &mut self, sym: Symbol, ptr: Symbol, @@ -2888,6 +2979,51 @@ impl< ASM::mov_base32_reg64(&mut self.buf, base_offset, ptr_reg); } + fn build_ptr_load(&mut self, sym: Symbol, ptr: Symbol, element_layout: InLayout<'a>) { + let ptr_reg = self + .storage_manager + .load_to_general_reg(&mut self.buf, &ptr); + + let offset = 0; + + Self::ptr_read( + &mut self.buf, + &mut self.storage_manager, + self.layout_interner, + ptr_reg, + offset, + element_layout, + sym, + ); + } + + fn build_ptr_clear_tag_id(&mut self, sym: Symbol, ptr: Symbol) { + let buf = &mut self.buf; + + let ptr_reg = self.storage_manager.load_to_general_reg(buf, &ptr); + let sym_reg = self.storage_manager.claim_general_reg(buf, &sym); + + ASM::mov_reg64_imm64(buf, sym_reg, !0b111); + ASM::and_reg64_reg64_reg64(buf, sym_reg, sym_reg, ptr_reg); + } + + fn build_alloca(&mut self, sym: Symbol, value: Symbol, element_layout: InLayout<'a>) { + // 1. acquire some stack space + let element_width = self.interner().stack_size(element_layout); + let allocation = self.debug_symbol("stack_allocation"); + let ptr = self.debug_symbol("ptr"); + let base_offset = self + .storage_manager + .claim_stack_area(&allocation, element_width); + + let ptr_reg = self.storage_manager.claim_general_reg(&mut self.buf, &ptr); + + ASM::mov_reg64_reg64(&mut self.buf, ptr_reg, CC::BASE_PTR_REG); + ASM::add_reg64_reg64_imm32(&mut self.buf, ptr_reg, ptr_reg, base_offset); + + self.build_ptr_store(sym, ptr, value, element_layout); + } + fn expr_box( &mut self, sym: Symbol, @@ -2920,27 +3056,13 @@ impl< self.free_symbol(&element_width_symbol); self.free_symbol(&element_alignment_symbol); - self.build_ptr_write(sym, allocation, value, element_layout); + self.build_ptr_store(sym, allocation, value, element_layout); self.free_symbol(&allocation); } fn expr_unbox(&mut self, dst: Symbol, ptr: Symbol, element_layout: InLayout<'a>) { - let ptr_reg = self - .storage_manager - .load_to_general_reg(&mut self.buf, &ptr); - - let offset = 0; - - Self::ptr_read( - &mut self.buf, - &mut self.storage_manager, - self.layout_interner, - ptr_reg, - offset, - element_layout, - dst, - ); + self.build_ptr_load(dst, ptr, element_layout) } fn get_tag_id(&mut self, sym: &Symbol, structure: &Symbol, union_layout: &UnionLayout<'a>) { @@ -3850,6 +3972,19 @@ impl< CC: CallConv, > Backend64Bit<'a, 'r, GeneralReg, FloatReg, ASM, CC> { + fn clear_tag_id(&mut self, ptr_reg: GeneralReg) -> (Symbol, GeneralReg) { + let unmasked_symbol = self.debug_symbol("unmasked"); + let unmasked_reg = self + .storage_manager + .claim_general_reg(&mut self.buf, &unmasked_symbol); + + ASM::mov_reg64_imm64(&mut self.buf, unmasked_reg, (!0b111) as _); + + ASM::and_reg64_reg64_reg64(&mut self.buf, unmasked_reg, ptr_reg, unmasked_reg); + + (unmasked_symbol, unmasked_reg) + } + fn compare( &mut self, op: CompareOperation, @@ -4384,7 +4519,7 @@ macro_rules! single_register_layouts { #[macro_export] macro_rules! pointer_layouts { () => { - LayoutRepr::Boxed(_) + LayoutRepr::Ptr(_) | LayoutRepr::RecursivePointer(_) | LayoutRepr::Union( UnionLayout::Recursive(_) diff --git a/crates/compiler/gen_dev/src/generic64/storage.rs b/crates/compiler/gen_dev/src/generic64/storage.rs index a1581c7c037..6c053a1e33d 100644 --- a/crates/compiler/gen_dev/src/generic64/storage.rs +++ b/crates/compiler/gen_dev/src/generic64/storage.rs @@ -783,7 +783,11 @@ impl< let reg = self.load_to_float_reg(buf, sym); ASM::mov_base32_freg64(buf, to_offset, reg); } - FloatWidth::F32 => todo!(), + FloatWidth::F32 => { + debug_assert_eq!(to_offset % 4, 0); + let reg = self.load_to_float_reg(buf, sym); + ASM::mov_base32_freg64(buf, to_offset, reg); + } }, Builtin::Bool => { // same as 8-bit integer, but we special-case true/false because these symbols @@ -826,7 +830,7 @@ impl< self.copy_to_stack_offset(buf, size, from_offset, to_offset) } - LayoutRepr::RecursivePointer(_) | LayoutRepr::Boxed(_) | LayoutRepr::Union(_) => { + pointer_layouts!() => { // like a 64-bit integer debug_assert_eq!(to_offset % 8, 0); let reg = self.load_to_general_reg(buf, sym); diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index efeb6d8cce5..f67f8771943 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -1584,6 +1584,11 @@ impl Assembler for X86_64Assembler { movsd_base64_offset32_freg64(buf, X86_64GeneralReg::RBP, offset, src) } + #[inline(always)] + fn mov_base32_freg32(buf: &mut Vec<'_, u8>, offset: i32, src: X86_64FloatReg) { + movss_base32_offset32_freg32(buf, X86_64GeneralReg::RBP, offset, src) + } + #[inline(always)] fn movesd_mem64_offset32_freg64( buf: &mut Vec<'_, u8>, @@ -3180,6 +3185,31 @@ fn movsd_base64_offset32_freg64( buf.extend(offset.to_le_bytes()); } +// `MOVSS r/m64,xmm1` -> Move xmm1 to r/m64. where m64 references the base pointer. +#[inline(always)] +fn movss_base32_offset32_freg32( + buf: &mut Vec<'_, u8>, + base: X86_64GeneralReg, + offset: i32, + src: X86_64FloatReg, +) { + let rex = add_rm_extension(base, REX_W); + let rex = add_reg_extension(src, rex); + let src_mod = (src as u8 % 8) << 3; + let base_mod = base as u8 % 8; + buf.reserve(10); + buf.push(0xF3); + if src as u8 > 7 || base as u8 > 7 { + buf.push(rex); + } + buf.extend([0x0F, 0x11, 0x80 | src_mod | base_mod]); + // Using RSP or R12 requires a secondary index byte. + if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 { + buf.push(0x24); + } + buf.extend(offset.to_le_bytes()); +} + /// `MOVSD xmm1,r/m64` -> Move r/m64 to xmm1. where m64 references the base pointer. #[inline(always)] fn movsd_freg64_base64_offset32( @@ -3582,7 +3612,7 @@ mod tests { fn test_add_reg64_imm32() { disassembler_test!( add_reg64_imm32, - |reg, imm| format!("add {}, 0x{:x}", reg, imm), + |reg, imm| format!("add {reg}, 0x{imm:x}"), ALL_GENERAL_REGS, [TEST_I32] ); @@ -3592,7 +3622,7 @@ mod tests { fn test_add_reg64_reg64() { disassembler_test!( add_reg64_reg64, - |reg1, reg2| format!("add {}, {}", reg1, reg2), + |reg1, reg2| format!("add {reg1}, {reg2}"), ALL_GENERAL_REGS, ALL_GENERAL_REGS ); @@ -3602,7 +3632,7 @@ mod tests { fn test_sub_reg64_reg64() { disassembler_test!( sub_reg64_reg64, - |reg1, reg2| format!("sub {}, {}", reg1, reg2), + |reg1, reg2| format!("sub {reg1}, {reg2}"), ALL_GENERAL_REGS, ALL_GENERAL_REGS ); @@ -3612,7 +3642,7 @@ mod tests { fn test_addsd_freg64_freg64() { disassembler_test!( addsd_freg64_freg64, - |reg1, reg2| format!("addsd {}, {}", reg1, reg2), + |reg1, reg2| format!("addsd {reg1}, {reg2}"), ALL_FLOAT_REGS, ALL_FLOAT_REGS ); @@ -3622,7 +3652,7 @@ mod tests { fn test_addss_freg32_freg32() { disassembler_test!( addss_freg32_freg32, - |reg1, reg2| format!("addss {}, {}", reg1, reg2), + |reg1, reg2| format!("addss {reg1}, {reg2}"), ALL_FLOAT_REGS, ALL_FLOAT_REGS ); @@ -3632,7 +3662,7 @@ mod tests { fn test_andpd_freg64_freg64() { disassembler_test!( andpd_freg64_freg64, - |reg1, reg2| format!("andpd {}, {}", reg1, reg2), + |reg1, reg2| format!("andpd {reg1}, {reg2}"), ALL_FLOAT_REGS, ALL_FLOAT_REGS ); @@ -3699,7 +3729,7 @@ mod tests { fn test_cmovl_reg64_reg64() { disassembler_test!( cmovl_reg64_reg64, - |reg1, reg2| format!("cmovl {}, {}", reg1, reg2), + |reg1, reg2| format!("cmovl {reg1}, {reg2}"), ALL_GENERAL_REGS, ALL_GENERAL_REGS ); @@ -3709,7 +3739,7 @@ mod tests { fn test_cmp_reg64_imm32() { disassembler_test!( cmp_reg64_imm32, - |reg, imm| format!("cmp {}, 0x{:x}", reg, imm), + |reg, imm| format!("cmp {reg}, 0x{imm:x}"), ALL_GENERAL_REGS, [TEST_I32] ); @@ -3719,7 +3749,7 @@ mod tests { fn test_imul_reg64_reg64() { disassembler_test!( imul_reg64_reg64, - |reg1, reg2| format!("imul {}, {}", reg1, reg2), + |reg1, reg2| format!("imul {reg1}, {reg2}"), ALL_GENERAL_REGS, ALL_GENERAL_REGS ); @@ -3729,7 +3759,7 @@ mod tests { fn test_mul_reg64_reg64() { disassembler_test!( mul_reg64_reg64, - |reg| format!("mul {}", reg), + |reg| format!("mul {reg}"), ALL_GENERAL_REGS ); } @@ -3738,7 +3768,7 @@ mod tests { fn test_mulsd_freg64_freg64() { disassembler_test!( mulsd_freg64_freg64, - |reg1, reg2| format!("mulsd {}, {}", reg1, reg2), + |reg1, reg2| format!("mulsd {reg1}, {reg2}"), ALL_FLOAT_REGS, ALL_FLOAT_REGS ); @@ -3748,7 +3778,7 @@ mod tests { fn test_mulss_freg32_freg32() { disassembler_test!( mulss_freg32_freg32, - |reg1, reg2| format!("mulss {}, {}", reg1, reg2), + |reg1, reg2| format!("mulss {reg1}, {reg2}"), ALL_FLOAT_REGS, ALL_FLOAT_REGS ); @@ -3758,7 +3788,7 @@ mod tests { fn test_idiv_reg64_reg64() { disassembler_test!( idiv_reg64_reg64, - |reg| format!("cqo\nidiv {}", reg), + |reg| format!("cqo\nidiv {reg}"), ALL_GENERAL_REGS ); } @@ -3767,7 +3797,7 @@ mod tests { fn test_div_reg64_reg64() { disassembler_test!( udiv_reg64_reg64, - |reg| format!("cqo\ndiv {}", reg), + |reg| format!("cqo\ndiv {reg}"), ALL_GENERAL_REGS ); } @@ -3776,7 +3806,7 @@ mod tests { fn test_divsd_freg64_freg64() { disassembler_test!( divsd_freg64_freg64, - |reg1, reg2| format!("divsd {}, {}", reg1, reg2), + |reg1, reg2| format!("divsd {reg1}, {reg2}"), ALL_FLOAT_REGS, ALL_FLOAT_REGS ); @@ -3786,7 +3816,7 @@ mod tests { fn test_divss_freg32_freg32() { disassembler_test!( divss_freg32_freg32, - |reg1, reg2| format!("divss {}, {}", reg1, reg2), + |reg1, reg2| format!("divss {reg1}, {reg2}"), ALL_FLOAT_REGS, ALL_FLOAT_REGS ); @@ -3816,7 +3846,7 @@ mod tests { fn test_mov_reg64_imm32() { disassembler_test!( mov_reg64_imm32, - |reg, imm| format!("mov {}, 0x{:x}", reg, imm), + |reg, imm| format!("mov {reg}, 0x{imm:x}"), ALL_GENERAL_REGS, [TEST_I32] ); @@ -3826,13 +3856,13 @@ mod tests { fn test_mov_reg64_imm64() { disassembler_test!( mov_reg64_imm64, - |reg, imm| format!("movabs {}, 0x{:x}", reg, imm), + |reg, imm| format!("movabs {reg}, 0x{imm:x}"), ALL_GENERAL_REGS, [TEST_I64] ); disassembler_test!( mov_reg64_imm64, - |reg, imm| format!("mov {}, 0x{:x}", reg, imm), + |reg, imm| format!("mov {reg}, 0x{imm:x}"), ALL_GENERAL_REGS, [TEST_I32 as i64] ); @@ -3842,7 +3872,7 @@ mod tests { fn test_lea_reg64() { disassembler_test!( lea_reg64, - |reg| format!("lea {}, [rip]", reg), + |reg| format!("lea {reg}, [rip]"), ALL_GENERAL_REGS ); } @@ -3868,7 +3898,7 @@ mod tests { X86_64GeneralReg::low_32bits_string(®1), X86_64GeneralReg::low_32bits_string(®2) ), - RegisterWidth::W64 => format!("mov {}, {}", reg1, reg2), + RegisterWidth::W64 => format!("mov {reg1}, {reg2}"), } }, ALL_REGISTER_WIDTHS, @@ -3937,7 +3967,7 @@ mod tests { fn test_movsd_freg64_base64_offset32() { disassembler_test!( movsd_freg64_base64_offset32, - |reg1, reg2, imm| format!("movsd {}, qword ptr [{} + 0x{:x}]", reg1, reg2, imm), + |reg1, reg2, imm| format!("movsd {reg1}, qword ptr [{reg2} + 0x{imm:x}]"), ALL_FLOAT_REGS, ALL_GENERAL_REGS, [TEST_I32] @@ -3948,7 +3978,7 @@ mod tests { fn test_movss_freg32_base32_offset32() { disassembler_test!( movss_freg32_base32_offset32, - |reg1, reg2, imm| format!("movss {}, dword ptr [{} + 0x{:x}]", reg1, reg2, imm), + |reg1, reg2, imm| format!("movss {reg1}, dword ptr [{reg2} + 0x{imm:x}]"), ALL_FLOAT_REGS, ALL_GENERAL_REGS, [TEST_I32] @@ -3959,7 +3989,18 @@ mod tests { fn test_movsd_base64_offset32_freg64() { disassembler_test!( movsd_base64_offset32_freg64, - |reg1, imm, reg2| format!("movsd qword ptr [{} + 0x{:x}], {}", reg1, imm, reg2), + |reg1, imm, reg2| format!("movsd qword ptr [{reg1} + 0x{imm:x}], {reg2}"), + ALL_GENERAL_REGS, + [TEST_I32], + ALL_FLOAT_REGS + ); + } + + #[test] + fn test_movss_base64_offset32_freg64() { + disassembler_test!( + movss_base32_offset32_freg32, + |reg1, imm, reg2| format!("movss dword ptr [{} + 0x{:x}], {}", reg1, imm, reg2), ALL_GENERAL_REGS, [TEST_I32], ALL_FLOAT_REGS @@ -3970,7 +4011,7 @@ mod tests { fn test_mov_reg64_base64_offset32() { disassembler_test!( mov_reg64_base64_offset32, - |reg1, reg2, imm| format!("mov {}, qword ptr [{} + 0x{:x}]", reg1, reg2, imm), + |reg1, reg2, imm| format!("mov {reg1}, qword ptr [{reg2} + 0x{imm:x}]"), ALL_GENERAL_REGS, ALL_GENERAL_REGS, [TEST_I32] @@ -4029,7 +4070,7 @@ mod tests { fn test_mov_base64_offset32_reg64() { disassembler_test!( mov_base64_offset32_reg64, - |reg1, imm, reg2| format!("mov qword ptr [{} + 0x{:x}], {}", reg1, imm, reg2), + |reg1, imm, reg2| format!("mov qword ptr [{reg1} + 0x{imm:x}], {reg2}"), ALL_GENERAL_REGS, [TEST_I32], ALL_GENERAL_REGS @@ -4088,7 +4129,7 @@ mod tests { fn test_movsx_reg64_base32_offset32() { disassembler_test!( movsx_reg64_base32_offset32, - |reg1, reg2, imm| format!("movsxd {}, dword ptr [{} + 0x{:x}]", reg1, reg2, imm), + |reg1, reg2, imm| format!("movsxd {reg1}, dword ptr [{reg2} + 0x{imm:x}]"), ALL_GENERAL_REGS, ALL_GENERAL_REGS, [TEST_I32] @@ -4099,7 +4140,7 @@ mod tests { fn test_movsx_reg64_base16_offset32() { disassembler_test!( movsx_reg64_base16_offset32, - |reg1, reg2, imm| format!("movsx {}, word ptr [{} + 0x{:x}]", reg1, reg2, imm), + |reg1, reg2, imm| format!("movsx {reg1}, word ptr [{reg2} + 0x{imm:x}]"), ALL_GENERAL_REGS, ALL_GENERAL_REGS, [TEST_I32] @@ -4110,7 +4151,7 @@ mod tests { fn test_movsx_reg64_base8_offset32() { disassembler_test!( movsx_reg64_base8_offset32, - |reg1, reg2, imm| format!("movsx {}, byte ptr [{} + 0x{:x}]", reg1, reg2, imm), + |reg1, reg2, imm| format!("movsx {reg1}, byte ptr [{reg2} + 0x{imm:x}]"), ALL_GENERAL_REGS, ALL_GENERAL_REGS, [TEST_I32] @@ -4121,7 +4162,7 @@ mod tests { fn test_movzx_reg64_base16_offset32() { disassembler_test!( movzx_reg64_base16_offset32, - |reg1, reg2, imm| format!("movzx {}, word ptr [{} + 0x{:x}]", reg1, reg2, imm), + |reg1, reg2, imm| format!("movzx {reg1}, word ptr [{reg2} + 0x{imm:x}]"), ALL_GENERAL_REGS, ALL_GENERAL_REGS, [TEST_I32] @@ -4132,7 +4173,7 @@ mod tests { fn test_movzx_reg64_base8_offset32() { disassembler_test!( movzx_reg64_base8_offset32, - |reg1, reg2, imm| format!("movzx {}, byte ptr [{} + 0x{:x}]", reg1, reg2, imm), + |reg1, reg2, imm| format!("movzx {reg1}, byte ptr [{reg2} + 0x{imm:x}]"), ALL_GENERAL_REGS, ALL_GENERAL_REGS, [TEST_I32] @@ -4153,7 +4194,7 @@ mod tests { fn test_movq_reg64_freg64() { disassembler_test!( movq_reg64_freg64, - |dst, src| format!("movq {}, {}", dst, src), + |dst, src| format!("movq {dst}, {src}"), ALL_GENERAL_REGS, ALL_FLOAT_REGS ); @@ -4163,7 +4204,7 @@ mod tests { fn test_movsd_freg64_freg64() { disassembler_test!( raw_movsd_freg64_freg64, - |reg1, reg2| format!("movsd {}, {}", reg1, reg2), + |reg1, reg2| format!("movsd {reg1}, {reg2}"), ALL_FLOAT_REGS, ALL_FLOAT_REGS ); @@ -4173,7 +4214,7 @@ mod tests { fn test_movss_freg32_freg32() { disassembler_test!( raw_movss_freg32_freg32, - |reg1, reg2| format!("movss {}, {}", reg1, reg2), + |reg1, reg2| format!("movss {reg1}, {reg2}"), ALL_FLOAT_REGS, ALL_FLOAT_REGS ); @@ -4183,7 +4224,7 @@ mod tests { fn test_movss_freg32_rip_offset32() { disassembler_test!( movss_freg32_rip_offset32, - |reg, imm| format!("movss {}, dword ptr [rip + 0x{:x}]", reg, imm), + |reg, imm| format!("movss {reg}, dword ptr [rip + 0x{imm:x}]"), ALL_FLOAT_REGS, [TEST_I32 as u32] ); @@ -4193,7 +4234,7 @@ mod tests { fn test_movsd_freg64_rip_offset32() { disassembler_test!( movsd_freg64_rip_offset32, - |reg, imm| format!("movsd {}, qword ptr [rip + 0x{:x}]", reg, imm), + |reg, imm| format!("movsd {reg}, qword ptr [rip + 0x{imm:x}]"), ALL_FLOAT_REGS, [TEST_I32 as u32] ); @@ -4201,7 +4242,7 @@ mod tests { #[test] fn test_neg_reg64() { - disassembler_test!(neg_reg64, |reg| format!("neg {}", reg), ALL_GENERAL_REGS); + disassembler_test!(neg_reg64, |reg| format!("neg {reg}"), ALL_GENERAL_REGS); } #[test] @@ -4210,13 +4251,13 @@ mod tests { const CVTTSS2SI_CODE: u8 = 0x2C; disassembler_test!( |buf, r1, r2| cvtsi2_help(buf, 0xF3, CVTSI2SS_CODE, r1, r2), - |reg1, reg2| format!("cvtsi2ss {}, {}", reg1, reg2), + |reg1, reg2| format!("cvtsi2ss {reg1}, {reg2}"), ALL_FLOAT_REGS, ALL_GENERAL_REGS ); disassembler_test!( |buf, r1, r2| cvtsi2_help(buf, 0xF3, CVTTSS2SI_CODE, r1, r2), - |reg1, reg2| format!("cvttss2si {}, {}", reg1, reg2), + |reg1, reg2| format!("cvttss2si {reg1}, {reg2}"), ALL_GENERAL_REGS, ALL_FLOAT_REGS ); @@ -4227,7 +4268,7 @@ mod tests { const CVTSS2SD_CODE: u8 = 0x5A; disassembler_test!( |buf, r1, r2| cvtsi2_help(buf, 0xF3, CVTSS2SD_CODE, r1, r2), - |reg1, reg2| format!("cvtss2sd {}, {}", reg1, reg2), + |reg1, reg2| format!("cvtss2sd {reg1}, {reg2}"), ALL_FLOAT_REGS, ALL_FLOAT_REGS ); @@ -4251,7 +4292,7 @@ mod tests { fn test_sub_reg64_imm32() { disassembler_test!( sub_reg64_imm32, - |reg, imm| format!("sub {}, 0x{:x}", reg, imm), + |reg, imm| format!("sub {reg}, 0x{imm:x}"), ALL_GENERAL_REGS, [TEST_I32] ); @@ -4259,12 +4300,12 @@ mod tests { #[test] fn test_pop_reg64() { - disassembler_test!(pop_reg64, |reg| format!("pop {}", reg), ALL_GENERAL_REGS); + disassembler_test!(pop_reg64, |reg| format!("pop {reg}"), ALL_GENERAL_REGS); } #[test] fn test_push_reg64() { - disassembler_test!(push_reg64, |reg| format!("push {}", reg), ALL_GENERAL_REGS); + disassembler_test!(push_reg64, |reg| format!("push {reg}"), ALL_GENERAL_REGS); } #[test] diff --git a/crates/compiler/gen_dev/src/lib.rs b/crates/compiler/gen_dev/src/lib.rs index 648ab8247c3..be508b6669a 100644 --- a/crates/compiler/gen_dev/src/lib.rs +++ b/crates/compiler/gen_dev/src/lib.rs @@ -17,7 +17,7 @@ use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_mono::code_gen_help::{CallerProc, CodeGenHelp}; use roc_mono::ir::{ BranchInfo, CallType, CrashTag, Expr, HigherOrderLowLevel, JoinPointId, ListLiteralElement, - Literal, Param, Proc, ProcLayout, SelfRecursive, Stmt, + Literal, ModifyRc, Param, Proc, ProcLayout, SelfRecursive, Stmt, }; use roc_mono::layout::{ Builtin, InLayout, LambdaName, Layout, LayoutIds, LayoutInterner, LayoutRepr, STLayoutInterner, @@ -138,17 +138,17 @@ impl<'a> LastSeenMap<'a> { Expr::Call(call) => self.scan_ast_call(call, stmt), - Expr::Tag { arguments, .. } => { + Expr::Tag { + arguments, reuse, .. + } => { + if let Some(ru) = reuse { + self.set_last_seen(ru.symbol, stmt); + } + for sym in *arguments { self.set_last_seen(*sym, stmt); } } - Expr::ExprBox { symbol } => { - self.set_last_seen(*symbol, stmt); - } - Expr::ExprUnbox { symbol } => { - self.set_last_seen(*symbol, stmt); - } Expr::Struct(syms) => { for sym in *syms { self.set_last_seen(*sym, stmt); @@ -163,6 +163,9 @@ impl<'a> LastSeenMap<'a> { Expr::UnionAtIndex { structure, .. } => { self.set_last_seen(*structure, stmt); } + Expr::UnionFieldPtrAtIndex { structure, .. } => { + self.set_last_seen(*structure, stmt); + } Expr::Array { elems, .. } => { for elem in *elems { if let ListLiteralElement::Symbol(sym) = elem { @@ -170,14 +173,6 @@ impl<'a> LastSeenMap<'a> { } } } - Expr::Reuse { - symbol, arguments, .. - } => { - self.set_last_seen(*symbol, stmt); - for sym in *arguments { - self.set_last_seen(*sym, stmt); - } - } Expr::Reset { symbol, .. } | Expr::ResetRef { symbol, .. } => { self.set_last_seen(*symbol, stmt); } @@ -356,7 +351,7 @@ trait Backend<'a> { // the functions from the generates #help module (refcounting, equality) is always suffixed // with 1. That is fine, they are always unique anyway. if ident_string.contains("#help") { - format!("{}_{}_1", module_string, ident_string) + format!("{module_string}_{ident_string}_1") } else { format!("{}_{}_{}", module_string, ident_string, state.finish()) } @@ -393,7 +388,7 @@ trait Backend<'a> { fn increment_fn_pointer(&mut self, layout: InLayout<'a>) -> Symbol { let box_layout = self .interner_mut() - .insert_direct_no_semantic(LayoutRepr::Boxed(layout)); + .insert_direct_no_semantic(LayoutRepr::Ptr(layout)); let element_increment = self.debug_symbol("element_increment"); let element_increment_symbol = self.build_indirect_inc(layout); @@ -413,7 +408,7 @@ trait Backend<'a> { fn decrement_fn_pointer(&mut self, layout: InLayout<'a>) -> Symbol { let box_layout = self .interner_mut() - .insert_direct_no_semantic(LayoutRepr::Boxed(layout)); + .insert_direct_no_semantic(LayoutRepr::Ptr(layout)); let element_decrement = self.debug_symbol("element_decrement"); let element_decrement_symbol = self.build_indirect_dec(layout); @@ -522,6 +517,29 @@ trait Backend<'a> { self.return_symbol(sym, ret_layout); self.free_symbols(stmt); } + Stmt::Refcounting(ModifyRc::Free(symbol), following) => { + let dst = Symbol::DEV_TMP; + + let layout = *self.layout_map().get(symbol).unwrap(); + let alignment_bytes = self.interner().allocation_alignment_bytes(layout); + let alignment = self.debug_symbol("alignment"); + self.load_literal_i32(&alignment, alignment_bytes as i32); + + // NOTE: UTILS_FREE_DATA_PTR clears any tag id bits + + self.build_fn_call( + &dst, + bitcode::UTILS_FREE_DATA_PTR.to_string(), + &[*symbol, alignment], + &[Layout::I64, Layout::I32], + &Layout::UNIT, + ); + + self.free_symbol(&dst); + self.free_symbol(&alignment); + + self.build_stmt(layout_ids, following, ret_layout) + } Stmt::Refcounting(modify, following) => { let sym = modify.get_symbol(); let layout = *self.layout_map().get(&sym).unwrap(); @@ -794,6 +812,14 @@ trait Backend<'a> { } => { self.load_union_at_index(sym, structure, *tag_id, *index, union_layout); } + Expr::UnionFieldPtrAtIndex { + structure, + tag_id, + union_layout, + index, + } => { + self.load_union_field_ptr_at_index(sym, structure, *tag_id, *index, union_layout); + } Expr::GetTagId { structure, union_layout, @@ -804,39 +830,15 @@ trait Backend<'a> { tag_layout, tag_id, arguments, - .. + reuse, } => { self.load_literal_symbols(arguments); - self.tag(sym, arguments, tag_layout, *tag_id, None); - } - Expr::ExprBox { symbol: value } => { - let element_layout = match self.interner().get_repr(*layout) { - LayoutRepr::Boxed(boxed) => boxed, - _ => unreachable!("{:?}", self.interner().dbg(*layout)), - }; - - self.load_literal_symbols([*value].as_slice()); - self.expr_box(*sym, *value, element_layout, None) - } - Expr::ExprUnbox { symbol: ptr } => { - let element_layout = *layout; - - self.load_literal_symbols([*ptr].as_slice()); - self.expr_unbox(*sym, *ptr, element_layout) + let reuse = reuse.map(|ru| ru.symbol); + self.tag(sym, arguments, tag_layout, *tag_id, reuse); } Expr::NullPointer => { self.load_literal_i64(sym, 0); } - Expr::Reuse { - tag_layout, - tag_id, - symbol: reused, - arguments, - .. - } => { - self.load_literal_symbols(arguments); - self.tag(sym, arguments, tag_layout, *tag_id, Some(*reused)); - } Expr::Reset { symbol, .. } => { let layout = *self.layout_map().get(symbol).unwrap(); @@ -1581,14 +1583,26 @@ trait Backend<'a> { self.build_ptr_cast(sym, &args[0]) } - LowLevel::PtrWrite => { - let element_layout = match self.interner().get_repr(*ret_layout) { - LayoutRepr::Boxed(boxed) => boxed, + LowLevel::PtrStore => { + let element_layout = match self.interner().get_repr(arg_layouts[0]) { + LayoutRepr::Ptr(inner) => inner, _ => unreachable!("cannot write to {:?}", self.interner().dbg(*ret_layout)), }; - self.build_ptr_write(*sym, args[0], args[1], element_layout); + self.build_ptr_store(*sym, args[0], args[1], element_layout); + } + LowLevel::PtrLoad => { + self.build_ptr_load(*sym, args[0], *ret_layout); + } + + LowLevel::PtrClearTagId => { + self.build_ptr_clear_tag_id(*sym, args[0]); } + + LowLevel::Alloca => { + self.build_alloca(*sym, args[0], arg_layouts[0]); + } + LowLevel::RefCountDecRcPtr => self.build_fn_call( sym, bitcode::UTILS_DECREF_RC_PTR.to_string(), @@ -2217,7 +2231,7 @@ trait Backend<'a> { /// build_refcount_getptr loads the pointer to the reference count of src into dst. fn build_ptr_cast(&mut self, dst: &Symbol, src: &Symbol); - fn build_ptr_write( + fn build_ptr_store( &mut self, sym: Symbol, ptr: Symbol, @@ -2225,6 +2239,12 @@ trait Backend<'a> { element_layout: InLayout<'a>, ); + fn build_ptr_load(&mut self, sym: Symbol, ptr: Symbol, element_layout: InLayout<'a>); + + fn build_ptr_clear_tag_id(&mut self, sym: Symbol, ptr: Symbol); + + fn build_alloca(&mut self, sym: Symbol, value: Symbol, element_layout: InLayout<'a>); + /// literal_map gets the map from symbol to literal and layout, used for lazy loading and literal folding. fn literal_map(&mut self) -> &mut MutMap, *const InLayout<'a>)>; @@ -2302,6 +2322,16 @@ trait Backend<'a> { union_layout: &UnionLayout<'a>, ); + /// load_union_at_index loads into `sym` the value at `index` for `tag_id`. + fn load_union_field_ptr_at_index( + &mut self, + sym: &Symbol, + structure: &Symbol, + tag_id: TagIdIntType, + index: u64, + union_layout: &UnionLayout<'a>, + ); + /// get_tag_id loads the tag id from a the union. fn get_tag_id(&mut self, sym: &Symbol, structure: &Symbol, union_layout: &UnionLayout<'a>); diff --git a/crates/compiler/gen_dev/src/object_builder.rs b/crates/compiler/gen_dev/src/object_builder.rs index a7eba25d9ff..3cf0a0e2b96 100644 --- a/crates/compiler/gen_dev/src/object_builder.rs +++ b/crates/compiler/gen_dev/src/object_builder.rs @@ -464,7 +464,7 @@ fn build_exposed_generic_proc<'a, B: Backend<'a>>(backend: &mut B, proc: &Proc<' let box_layout = backend .interner_mut() - .insert_direct_no_semantic(roc_mono::layout::LayoutRepr::Boxed(proc.ret_layout)); + .insert_direct_no_semantic(roc_mono::layout::LayoutRepr::Ptr(proc.ret_layout)); let mut args = bumpalo::collections::Vec::new_in(arena); args.extend(proc.args); @@ -485,7 +485,7 @@ fn build_exposed_generic_proc<'a, B: Backend<'a>>(backend: &mut B, proc: &Proc<' let box_write = Call { call_type: roc_mono::ir::CallType::LowLevel { - op: roc_module::low_level::LowLevel::PtrWrite, + op: roc_module::low_level::LowLevel::PtrStore, update_mode: UpdateModeId::BACKEND_DUMMY, }, arguments: arena.alloc([arg_generic, s1]), @@ -605,7 +605,7 @@ fn build_proc<'a, B: Backend<'a>>( let elfreloc = match reloc { Relocation::LocalData { offset, data } => { let data_symbol = write::Symbol { - name: format!("{}.data{}", fn_name, local_data_index) + name: format!("{fn_name}.data{local_data_index}") .as_bytes() .to_vec(), value: 0, diff --git a/crates/compiler/gen_llvm/src/llvm/align.rs b/crates/compiler/gen_llvm/src/llvm/align.rs index d26b83ad5e0..68ed5fbf13d 100644 --- a/crates/compiler/gen_llvm/src/llvm/align.rs +++ b/crates/compiler/gen_llvm/src/llvm/align.rs @@ -120,7 +120,7 @@ impl<'a> LlvmAlignment<'a> for LayoutRepr<'a> { .llvm_alignment_bytes(interner), Builtin(builtin) => builtin.llvm_alignment_bytes(interner), RecursivePointer(_) => interner.target_info().ptr_width() as u32, - Boxed(_) => interner.target_info().ptr_width() as u32, + Ptr(_) => interner.target_info().ptr_width() as u32, } } } diff --git a/crates/compiler/gen_llvm/src/llvm/bitcode.rs b/crates/compiler/gen_llvm/src/llvm/bitcode.rs index 4762076da89..af4f7cd1d1e 100644 --- a/crates/compiler/gen_llvm/src/llvm/bitcode.rs +++ b/crates/compiler/gen_llvm/src/llvm/bitcode.rs @@ -34,10 +34,7 @@ pub fn call_bitcode_fn<'ctx>( .try_as_basic_value() .left() .unwrap_or_else(|| { - panic!( - "LLVM error: Did not get return value from bitcode function {:?}", - fn_name - ) + panic!("LLVM error: Did not get return value from bitcode function {fn_name:?}") }) } @@ -49,7 +46,7 @@ pub fn call_void_bitcode_fn<'ctx>( call_bitcode_fn_help(env, args, fn_name) .try_as_basic_value() .right() - .unwrap_or_else(|| panic!("LLVM error: Tried to call void bitcode function, but got return value from bitcode function, {:?}", fn_name)) + .unwrap_or_else(|| panic!("LLVM error: Tried to call void bitcode function, but got return value from bitcode function, {fn_name:?}")) } fn call_bitcode_fn_help<'ctx>( @@ -63,7 +60,7 @@ fn call_bitcode_fn_help<'ctx>( let fn_val = env .module .get_function(fn_name) - .unwrap_or_else(|| panic!("Unrecognized builtin function: {:?} - if you're working on the Roc compiler, do you need to rebuild the bitcode? See compiler/builtins/bitcode/README.md", fn_name)); + .unwrap_or_else(|| panic!("Unrecognized builtin function: {fn_name:?} - if you're working on the Roc compiler, do you need to rebuild the bitcode? See compiler/builtins/bitcode/README.md")); let call = env.builder.build_call(fn_val, &arguments, "call_builtin"); @@ -225,7 +222,7 @@ fn build_transform_caller_help<'a, 'ctx>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let attr = env.context.create_enum_attribute(kind_id, 1); + let attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, attr); let entry = env.context.append_basic_block(function_value, "entry"); @@ -378,9 +375,9 @@ fn build_rc_wrapper<'a, 'ctx>( .to_symbol_string(symbol, &env.interns); let fn_name = match rc_operation { - Mode::IncN => format!("{}_inc_n", fn_name), - Mode::Inc => format!("{}_inc", fn_name), - Mode::Dec => format!("{}_dec", fn_name), + Mode::IncN => format!("{fn_name}_inc_n"), + Mode::Inc => format!("{fn_name}_inc"), + Mode::Dec => format!("{fn_name}_dec"), }; let function_value = match env.module.get_function(fn_name.as_str()) { @@ -408,7 +405,7 @@ fn build_rc_wrapper<'a, 'ctx>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let attr = env.context.create_enum_attribute(kind_id, 1); + let attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, attr); let entry = env.context.append_basic_block(function_value, "entry"); @@ -497,7 +494,7 @@ pub fn build_eq_wrapper<'a, 'ctx>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let attr = env.context.create_enum_attribute(kind_id, 1); + let attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, attr); let entry = env.context.append_basic_block(function_value, "entry"); @@ -598,7 +595,7 @@ pub fn build_compare_wrapper<'a, 'ctx>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let attr = env.context.create_enum_attribute(kind_id, 1); + let attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, attr); let entry = env.context.append_basic_block(function_value, "entry"); diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 265a5ba504d..49547ddfa2c 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -23,7 +23,7 @@ use inkwell::passes::{PassManager, PassManagerBuilder}; use inkwell::types::{ AnyType, BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FunctionType, IntType, StructType, }; -use inkwell::values::BasicValueEnum::{self}; +use inkwell::values::BasicValueEnum; use inkwell::values::{ BasicMetadataValueEnum, CallSiteValue, FunctionValue, InstructionValue, IntValue, PointerValue, StructValue, @@ -263,7 +263,7 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { let fn_val = self .module .get_function(intrinsic_name) - .unwrap_or_else(|| panic!("Unrecognized intrinsic function: {}", intrinsic_name)); + .unwrap_or_else(|| panic!("Unrecognized intrinsic function: {intrinsic_name}")); let mut arg_vals: Vec = Vec::with_capacity_in(args.len(), self.arena); @@ -289,10 +289,7 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { let call = self.build_intrinsic_call(intrinsic_name, args); call.try_as_basic_value().left().unwrap_or_else(|| { - panic!( - "LLVM error: Invalid call by name for intrinsic {}", - intrinsic_name - ) + panic!("LLVM error: Invalid call by name for intrinsic {intrinsic_name}") }) } @@ -505,17 +502,14 @@ pub fn module_from_builtins<'ctx>( } => { include_bytes!("../../../builtins/bitcode/builtins-windows-x86_64.bc") } - _ => panic!( - "The zig builtins are not currently built for this target: {:?}", - target - ), + _ => panic!("The zig builtins are not currently built for this target: {target:?}"), } }; let memory_buffer = MemoryBuffer::create_from_memory_range(bitcode_bytes, module_name); let module = Module::parse_bitcode_from_buffer(&memory_buffer, ctx) - .unwrap_or_else(|err| panic!("Unable to import builtins bitcode. LLVM error: {:?}", err)); + .unwrap_or_else(|err| panic!("Unable to import builtins bitcode. LLVM error: {err:?}")); // Add LLVM intrinsics. add_intrinsics(ctx, &module); @@ -773,7 +767,7 @@ pub fn build_exp_literal<'a, 'ctx>( LayoutRepr::Builtin(Builtin::Int(int_width)) => { int_with_precision(env, i128::from_ne_bytes(*bytes), int_width).into() } - _ => panic!("Invalid layout for int literal = {:?}", layout), + _ => panic!("Invalid layout for int literal = {layout:?}"), }, U128(bytes) => const_u128(env, u128::from_ne_bytes(*bytes)).into(), @@ -782,7 +776,7 @@ pub fn build_exp_literal<'a, 'ctx>( LayoutRepr::Builtin(Builtin::Float(float_width)) => { float_with_precision(env, *float, float_width) } - _ => panic!("Invalid layout for float literal = {:?}", layout), + _ => panic!("Invalid layout for float literal = {layout:?}"), }, Decimal(bytes) => { @@ -1071,14 +1065,14 @@ pub(crate) fn build_exp_expr<'a, 'ctx>( ) .into(), - Reuse { + Tag { arguments, tag_layout: union_layout, tag_id, - symbol, - .. + reuse, } => { - let reset = scope.load_symbol(symbol).into_pointer_value(); + let reuse_ptr = reuse.map(|ru| scope.load_symbol(&ru.symbol).into_pointer_value()); + build_tag( env, layout_interner, @@ -1086,63 +1080,11 @@ pub(crate) fn build_exp_expr<'a, 'ctx>( union_layout, *tag_id, arguments, - Some(reset), + reuse_ptr, parent, ) } - Tag { - arguments, - tag_layout: union_layout, - tag_id, - .. - } => build_tag( - env, - layout_interner, - scope, - union_layout, - *tag_id, - arguments, - None, - parent, - ), - - ExprBox { symbol } => { - let (value, layout) = scope.load_symbol_and_layout(symbol); - let basic_type = - basic_type_from_layout(env, layout_interner, layout_interner.get_repr(layout)); - let allocation = reserve_with_refcount_help( - env, - basic_type, - layout_interner.stack_size(layout), - layout_interner.alignment_bytes(layout), - ); - - store_roc_value( - env, - layout_interner, - layout_interner.get_repr(layout), - allocation, - value, - ); - - allocation.into() - } - - ExprUnbox { symbol } => { - let value = scope.load_symbol(symbol); - - debug_assert!(value.is_pointer_value()); - - load_roc_value( - env, - layout_interner, - layout_interner.get_repr(layout), - value.into_pointer_value(), - "load_boxed_value", - ) - } - Reset { symbol, update_mode, @@ -1172,7 +1114,12 @@ pub(crate) fn build_exp_expr<'a, 'ctx>( env.builder.position_at_end(check_if_null); env.builder.build_conditional_branch( - env.builder.build_is_null(tag_ptr, "is_tag_null"), + // have llvm optimizations clean this up + if layout_interner.is_nullable(layout) { + env.builder.build_is_null(tag_ptr, "is_tag_null") + } else { + env.context.bool_type().const_int(false as _, false) + }, cont_block, check_if_unique, ); @@ -1182,8 +1129,14 @@ pub(crate) fn build_exp_expr<'a, 'ctx>( let then_block = ctx.append_basic_block(parent, "then_reset"); let else_block = ctx.append_basic_block(parent, "else_decref"); - let refcount_ptr = - PointerToRefcount::from_ptr_to_data(env, tag_pointer_clear_tag_id(env, tag_ptr)); + let refcount_ptr = PointerToRefcount::from_ptr_to_data( + env, + if union_layout.stores_tag_id_in_pointer(env.target_info) { + tag_pointer_clear_tag_id(env, tag_ptr) + } else { + tag_ptr + }, + ); let is_unique = match update_mode { UpdateMode::InPlace => env.context.bool_type().const_int(1, false), @@ -1253,7 +1206,12 @@ pub(crate) fn build_exp_expr<'a, 'ctx>( env.builder.position_at_end(check_if_null); env.builder.build_conditional_branch( - env.builder.build_is_null(tag_ptr, "is_tag_null"), + // have llvm optimizations clean this up + if layout_interner.is_nullable(layout) { + env.builder.build_is_null(tag_ptr, "is_tag_null") + } else { + env.context.bool_type().const_int(false as _, false) + }, cont_block, check_if_unique, ); @@ -1262,8 +1220,20 @@ pub(crate) fn build_exp_expr<'a, 'ctx>( let not_unique_block = ctx.append_basic_block(parent, "else_decref"); - let refcount_ptr = - PointerToRefcount::from_ptr_to_data(env, tag_pointer_clear_tag_id(env, tag_ptr)); + // reset is only generated for union values + let union_layout = match layout_interner.get_repr(layout) { + LayoutRepr::Union(ul) => ul, + _ => unreachable!(), + }; + + let refcount_ptr = PointerToRefcount::from_ptr_to_data( + env, + if union_layout.stores_tag_id_in_pointer(env.target_info) { + tag_pointer_clear_tag_id(env, tag_ptr) + } else { + tag_ptr + }, + ); let is_unique = match update_mode { UpdateMode::InPlace => env.context.bool_type().const_int(1, false), @@ -1386,12 +1356,13 @@ pub(crate) fn build_exp_expr<'a, 'ctx>( layout_interner.get_repr(layout), ); - lookup_at_index_ptr2( + lookup_at_index_ptr( env, layout_interner, field_layouts, *index as usize, ptr, + None, target_loaded_type, ) } @@ -1411,7 +1382,7 @@ pub(crate) fn build_exp_expr<'a, 'ctx>( field_layouts, *index as usize, argument.into_pointer_value(), - struct_type.into_struct_type(), + Some(struct_type.into_struct_type()), target_loaded_type, ) } @@ -1437,12 +1408,13 @@ pub(crate) fn build_exp_expr<'a, 'ctx>( layout_interner.get_repr(layout), ); - lookup_at_index_ptr2( + lookup_at_index_ptr( env, layout_interner, field_layouts, *index as usize, ptr, + None, target_loaded_type, ) } @@ -1470,13 +1442,116 @@ pub(crate) fn build_exp_expr<'a, 'ctx>( // the tag id is not stored *index as usize, argument.into_pointer_value(), - struct_type.into_struct_type(), + Some(struct_type.into_struct_type()), target_loaded_type, ) } } } + UnionFieldPtrAtIndex { + tag_id, + structure, + index, + union_layout, + } => { + // cast the argument bytes into the desired shape for this tag + let argument = scope.load_symbol(structure); + let ret_repr = layout_interner.get_repr(layout); + + let pointer_value = match union_layout { + UnionLayout::NonRecursive(_) => unreachable!(), + UnionLayout::Recursive(tag_layouts) => { + debug_assert!(argument.is_pointer_value()); + + let field_layouts = tag_layouts[*tag_id as usize]; + + let ptr = tag_pointer_clear_tag_id(env, argument.into_pointer_value()); + let target_loaded_type = basic_type_from_layout(env, layout_interner, ret_repr); + + union_field_ptr_at_index( + env, + layout_interner, + field_layouts, + None, + *index as usize, + ptr, + target_loaded_type, + ) + } + UnionLayout::NonNullableUnwrapped(field_layouts) => { + let struct_layout = LayoutRepr::struct_(field_layouts); + + let struct_type = basic_type_from_layout(env, layout_interner, struct_layout); + let target_loaded_type = basic_type_from_layout(env, layout_interner, ret_repr); + + union_field_ptr_at_index( + env, + layout_interner, + field_layouts, + Some(struct_type.into_struct_type()), + *index as usize, + argument.into_pointer_value(), + target_loaded_type, + ) + } + UnionLayout::NullableWrapped { + nullable_id, + other_tags, + } => { + debug_assert!(argument.is_pointer_value()); + debug_assert_ne!(*tag_id, *nullable_id); + + let tag_index = if *tag_id < *nullable_id { + *tag_id + } else { + tag_id - 1 + }; + + let field_layouts = other_tags[tag_index as usize]; + + let ptr = tag_pointer_clear_tag_id(env, argument.into_pointer_value()); + let target_loaded_type = basic_type_from_layout(env, layout_interner, ret_repr); + + union_field_ptr_at_index( + env, + layout_interner, + field_layouts, + None, + *index as usize, + ptr, + target_loaded_type, + ) + } + UnionLayout::NullableUnwrapped { + nullable_id, + other_fields, + } => { + debug_assert!(argument.is_pointer_value()); + debug_assert_ne!(*tag_id != 0, *nullable_id); + + let field_layouts = other_fields; + let struct_layout = LayoutRepr::struct_(field_layouts); + + let struct_type = basic_type_from_layout(env, layout_interner, struct_layout); + let target_loaded_type = basic_type_from_layout(env, layout_interner, ret_repr); + + union_field_ptr_at_index( + env, + layout_interner, + field_layouts, + Some(struct_type.into_struct_type()), + // the tag id is not stored + *index as usize, + argument.into_pointer_value(), + target_loaded_type, + ) + } + }; + + pointer_value.into() + } + GetTagId { structure, union_layout, @@ -1823,15 +1898,26 @@ fn tag_pointer_set_tag_id<'ctx>( // we only have 3 bits, so can encode only 0..7 (or on 32-bit targets, 2 bits to encode 0..3) debug_assert!((tag_id as u32) < env.target_info.ptr_width() as u32); - let ptr_int = env.ptr_int(); + let tag_id_intval = env.ptr_int().const_int(tag_id as u64, false); - let as_int = env.builder.build_ptr_to_int(pointer, ptr_int, "to_int"); + let cast_pointer = env.builder.build_pointer_cast( + pointer, + env.context.i8_type().ptr_type(AddressSpace::default()), + "cast_to_i8_ptr", + ); - let tag_id_intval = ptr_int.const_int(tag_id as u64, false); - let combined = env.builder.build_or(as_int, tag_id_intval, "store_tag_id"); + // NOTE: assumes the lower bits of `cast_pointer` are all 0 + let indexed_pointer = unsafe { + env.builder.new_build_in_bounds_gep( + env.context.i8_type(), + cast_pointer, + &[tag_id_intval], + "indexed_pointer", + ) + }; env.builder - .build_int_to_ptr(combined, pointer.get_type(), "to_ptr") + .build_pointer_cast(indexed_pointer, pointer.get_type(), "cast_from_i8_ptr") } pub fn tag_pointer_tag_id_bits_and_mask(target_info: TargetInfo) -> (u64, u64) { @@ -1861,22 +1947,35 @@ pub fn tag_pointer_clear_tag_id<'ctx>( env: &Env<'_, 'ctx, '_>, pointer: PointerValue<'ctx>, ) -> PointerValue<'ctx> { - let ptr_int = env.ptr_int(); + let (_, tag_id_bits_mask) = tag_pointer_tag_id_bits_and_mask(env.target_info); - let (tag_id_bits_mask, _) = tag_pointer_tag_id_bits_and_mask(env.target_info); + let as_int = env + .builder + .build_ptr_to_int(pointer, env.ptr_int(), "to_int"); - let as_int = env.builder.build_ptr_to_int(pointer, ptr_int, "to_int"); + let mask = env.ptr_int().const_int(tag_id_bits_mask, false); - let mask = { - let a = env.ptr_int().const_all_ones(); - let tag_id_bits = env.ptr_int().const_int(tag_id_bits_mask, false); - env.builder.build_left_shift(a, tag_id_bits, "make_mask") - }; + let current_tag_id = env.builder.build_and(as_int, mask, "masked"); - let masked = env.builder.build_and(as_int, mask, "masked"); + let index = env.builder.build_int_neg(current_tag_id, "index"); + + let cast_pointer = env.builder.build_pointer_cast( + pointer, + env.context.i8_type().ptr_type(AddressSpace::default()), + "cast_to_i8_ptr", + ); + + let indexed_pointer = unsafe { + env.builder.new_build_in_bounds_gep( + env.context.i8_type(), + cast_pointer, + &[index], + "new_ptr", + ) + }; env.builder - .build_int_to_ptr(masked, pointer.get_type(), "to_ptr") + .build_pointer_cast(indexed_pointer, pointer.get_type(), "cast_from_i8_ptr") } fn allocate_tag<'a, 'ctx>( @@ -1958,7 +2057,7 @@ pub fn get_tag_id<'a, 'ctx>( match union_layout { UnionLayout::NonRecursive(_) => { - debug_assert!(argument.is_pointer_value(), "{:?}", argument); + debug_assert!(argument.is_pointer_value(), "{argument:?}"); let argument_ptr = argument.into_pointer_value(); get_tag_id_wrapped(env, layout_interner, *union_layout, argument_ptr) @@ -2032,21 +2131,18 @@ fn lookup_at_index_ptr<'a, 'ctx>( field_layouts: &[InLayout<'a>], index: usize, value: PointerValue<'ctx>, - struct_type: StructType<'ctx>, + struct_type: Option>, target_loaded_type: BasicTypeEnum<'ctx>, ) -> BasicValueEnum<'ctx> { - let builder = env.builder; - - let ptr = env.builder.build_pointer_cast( + let elem_ptr = union_field_ptr_at_index_help( + env, + layout_interner, + field_layouts, + struct_type, + index, value, - struct_type.ptr_type(AddressSpace::default()), - "cast_lookup_at_index_ptr", ); - let elem_ptr = builder - .new_build_struct_gep(struct_type, ptr, index as u32, "at_index_struct_gep") - .unwrap(); - let field_layout = field_layouts[index]; let result = load_roc_value( env, @@ -2061,19 +2157,23 @@ fn lookup_at_index_ptr<'a, 'ctx>( cast_if_necessary_for_opaque_recursive_pointers(env.builder, result, target_loaded_type) } -fn lookup_at_index_ptr2<'a, 'ctx>( +fn union_field_ptr_at_index_help<'a, 'ctx>( env: &Env<'a, 'ctx, '_>, layout_interner: &STLayoutInterner<'a>, field_layouts: &'a [InLayout<'a>], + opt_struct_type: Option>, index: usize, value: PointerValue<'ctx>, - target_loaded_type: BasicTypeEnum<'ctx>, -) -> BasicValueEnum<'ctx> { +) -> PointerValue<'ctx> { let builder = env.builder; - let struct_layout = LayoutRepr::struct_(field_layouts); - let struct_type = - basic_type_from_layout(env, layout_interner, struct_layout).into_struct_type(); + let struct_type = match opt_struct_type { + Some(st) => st, + None => { + let struct_layout = LayoutRepr::struct_(field_layouts); + basic_type_from_layout(env, layout_interner, struct_layout).into_struct_type() + } + }; let data_ptr = env.builder.build_pointer_cast( value, @@ -2081,27 +2181,40 @@ fn lookup_at_index_ptr2<'a, 'ctx>( "cast_lookup_at_index_ptr", ); - let elem_ptr = builder + builder .new_build_struct_gep( struct_type, data_ptr, index as u32, "at_index_struct_gep_data", ) - .unwrap(); + .unwrap() +} - let field_layout = field_layouts[index]; - let result = load_roc_value( +fn union_field_ptr_at_index<'a, 'ctx>( + env: &Env<'a, 'ctx, '_>, + layout_interner: &STLayoutInterner<'a>, + field_layouts: &'a [InLayout<'a>], + opt_struct_type: Option>, + index: usize, + value: PointerValue<'ctx>, + target_loaded_type: BasicTypeEnum<'ctx>, +) -> PointerValue<'ctx> { + let result = union_field_ptr_at_index_help( env, layout_interner, - layout_interner.get_repr(field_layout), - elem_ptr, - "load_at_index_ptr", + field_layouts, + opt_struct_type, + index, + value, ); // A recursive pointer in the loaded structure is stored as a `i64*`, but the loaded layout // might want a more precise structure. As such, cast it to the refined type if needed. - cast_if_necessary_for_opaque_recursive_pointers(env.builder, result, target_loaded_type) + let from_value: BasicValueEnum = result.into(); + let to_type: BasicTypeEnum = target_loaded_type; + cast_if_necessary_for_opaque_recursive_pointers(env.builder, from_value, to_type) + .into_pointer_value() } pub fn reserve_with_refcount<'a, 'ctx>( @@ -2784,6 +2897,39 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>( cont, ) } + + Free(symbol) => { + // unconditionally deallocate the symbol + let (value, layout) = scope.load_symbol_and_layout(symbol); + let alignment = layout_interner.alignment_bytes(layout); + + debug_assert!(value.is_pointer_value()); + let value = value.into_pointer_value(); + + let clear_tag_id = match layout_interner.chase_recursive(layout) { + LayoutRepr::Union(union) => union.stores_tag_id_in_pointer(env.target_info), + _ => false, + }; + + let ptr = if clear_tag_id { + tag_pointer_clear_tag_id(env, value) + } else { + value + }; + + let rc_ptr = PointerToRefcount::from_ptr_to_data(env, ptr); + rc_ptr.deallocate(env, alignment); + + build_exp_stmt( + env, + layout_interner, + layout_ids, + func_spec_solutions, + scope, + parent, + cont, + ) + } } } @@ -3078,7 +3224,7 @@ pub fn cast_if_necessary_for_opaque_recursive_pointers<'ctx>( to_type: BasicTypeEnum<'ctx>, ) -> BasicValueEnum<'ctx> { if from_value.get_type() != to_type - // Only perform the cast if the target types are transumatble. + // Only perform the cast if the target types are transmutable. && equivalent_type_constructors(&from_value.get_type(), &to_type) { complex_bitcast( @@ -3369,10 +3515,7 @@ fn build_switch_ir<'a, 'ctx>( layout_interner, layout_interner.get_repr(stored_layout) ), - "This switch matches on {:?}, but the matched-on symbol {:?} has layout {:?}", - cond_layout, - cond_symbol, - stored_layout + "This switch matches on {cond_layout:?}, but the matched-on symbol {cond_symbol:?} has layout {stored_layout:?}" ); let cont_block = context.append_basic_block(parent, "cont"); @@ -3478,7 +3621,7 @@ fn build_switch_ir<'a, 'ctx>( condition_int_type.const_int(*int, false) }; - let block = context.append_basic_block(parent, format!("branch{}", int).as_str()); + let block = context.append_basic_block(parent, format!("branch{int}").as_str()); cases.push((int_val, block)); } @@ -3819,11 +3962,9 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx>( arguments_for_call.push(*arg); } else { match layout_interner.get_repr(*layout) { - LayoutRepr::Builtin(Builtin::List(_)) => { - let list_type = arg_type - .into_pointer_type() - .get_element_type() - .into_struct_type(); + repr @ LayoutRepr::Builtin(Builtin::List(_)) => { + let list_type = basic_type_from_layout(env, layout_interner, repr); + let loaded = env.builder.new_build_load( list_type, arg.into_pointer_value(), @@ -3889,7 +4030,7 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx>( &[], ); - let size_function_name: String = format!("roc__{}_size", ident_string); + let size_function_name: String = format!("roc__{ident_string}_size"); let size_function = add_func( env.context, @@ -4189,7 +4330,7 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx>( roc_function, arguments, return_layout, - &format!("{}_generic", c_function_name), + &format!("{c_function_name}_generic"), ); let c_function = expose_function_to_host_help_c_abi_v2( @@ -4208,7 +4349,7 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx>( Some(env.context.i64_type().as_basic_type_enum()), &[], ); - let size_function_name: String = format!("{}_size", c_function_name); + let size_function_name: String = format!("{c_function_name}_size"); let size_function = add_func( env.context, @@ -4855,7 +4996,7 @@ pub fn build_procedures_expose_expects<'a>( let mut it = func_solutions.specs(); let func_spec = match it.next() { Some(spec) => spec, - None => panic!("no specialization for expect {}", symbol), + None => panic!("no specialization for expect {symbol}"), }; debug_assert!( @@ -4869,7 +5010,7 @@ pub fn build_procedures_expose_expects<'a>( let name = roc_main_fn.get_name().to_str().unwrap(); - let expect_name = &format!("Expect_{}", name); + let expect_name = &format!("Expect_{name}"); let expect_name = env.arena.alloc_str(expect_name); expect_names.push(&*expect_name); @@ -4881,7 +5022,7 @@ pub fn build_procedures_expose_expects<'a>( roc_main_fn, top_level.arguments, top_level.result, - &format!("Expect_{}", name), + &format!("Expect_{name}"), ); } @@ -4908,7 +5049,7 @@ fn build_procedures_help<'a>( entry_point, it, ) { - Err(e) => panic!("Error in alias analysis: {}", e), + Err(e) => panic!("Error in alias analysis: {e}"), Ok(solutions) => solutions, }; @@ -5004,10 +5145,10 @@ fn func_spec_name<'a>( let ident_string = symbol.as_str(interns); let module_string = interns.module_ids.get_name(symbol.module_id()).unwrap(); - write!(buf, "{}_{}_", module_string, ident_string).unwrap(); + write!(buf, "{module_string}_{ident_string}_").unwrap(); for byte in func_spec.0.iter() { - write!(buf, "{:x?}", byte).unwrap(); + write!(buf, "{byte:x?}").unwrap(); } buf @@ -5072,14 +5213,14 @@ fn build_proc_header<'a, 'ctx>( if false { let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let enum_attr = env.context.create_enum_attribute(kind_id, 1); + let enum_attr = env.context.create_enum_attribute(kind_id, 0); fn_val.add_attribute(AttributeLoc::Function, enum_attr); } if false { let kind_id = Attribute::get_named_enum_kind_id("noinline"); debug_assert!(kind_id > 0); - let enum_attr = env.context.create_enum_attribute(kind_id, 1); + let enum_attr = env.context.create_enum_attribute(kind_id, 0); fn_val.add_attribute(AttributeLoc::Function, enum_attr); } @@ -5212,7 +5353,7 @@ fn build_closure_caller<'a, 'ctx>( // STEP 1: build function header // e.g. `roc__mainForHost_0_caller` (def_name is `mainForHost_0`) - let function_name = format!("roc__{}_caller", def_name); + let function_name = format!("roc__{def_name}_caller"); let function_spec = FunctionSpec::cconv(env, CCReturn::Void, None, &argument_types); @@ -5324,9 +5465,9 @@ fn build_host_exposed_alias_size_help<'a, 'ctx>( let i64 = env.context.i64_type().as_basic_type_enum(); let size_function_spec = FunctionSpec::cconv(env, CCReturn::Return, Some(i64), &[]); let size_function_name: String = if let Some(label) = opt_label { - format!("roc__{}_{}_size", def_name, label) + format!("roc__{def_name}_{label}_size") } else { - format!("roc__{}_size", def_name,) + format!("roc__{def_name}_size",) }; let size_function = add_func( @@ -5461,10 +5602,7 @@ fn function_value_by_name_help<'a, 'ctx>( ); eprintln!("Is the function defined? If so, maybe there is a problem with the layout"); - panic!( - "Unrecognized builtin function: {:?} (symbol: {:?})", - fn_name, symbol, - ) + panic!("Unrecognized builtin function: {fn_name:?} (symbol: {symbol:?})",) } else { // Unrecognized non-builtin function: eprintln!( @@ -5475,10 +5613,7 @@ fn function_value_by_name_help<'a, 'ctx>( ); eprintln!("Is the function defined? If so, maybe there is a problem with the layout"); - panic!( - "Unrecognized non-builtin function: {:?} (symbol: {:?})", - fn_name, symbol, - ) + panic!("Unrecognized non-builtin function: {fn_name:?} (symbol: {symbol:?})",) } }) } @@ -6139,7 +6274,7 @@ fn define_global_str_literal<'ctx>( message.hash(&mut hasher); let hash = hasher.finish(); - format!("_str_literal_{}", hash) + format!("_str_literal_{hash}") }; match module.get_global(&name) { @@ -6240,7 +6375,7 @@ pub fn add_func<'ctx>( ) -> FunctionValue<'ctx> { if cfg!(debug_assertions) { if let Some(func) = module.get_function(name) { - panic!("Attempting to redefine LLVM function {}, which was already defined in this module as:\n\n{:#?}", name, func); + panic!("Attempting to redefine LLVM function {name}, which was already defined in this module as:\n\n{func:#?}"); } } diff --git a/crates/compiler/gen_llvm/src/llvm/compare.rs b/crates/compiler/gen_llvm/src/llvm/compare.rs index 3f0cb0b4764..e8572d30cd1 100644 --- a/crates/compiler/gen_llvm/src/llvm/compare.rs +++ b/crates/compiler/gen_llvm/src/llvm/compare.rs @@ -144,14 +144,8 @@ fn build_eq<'a, 'ctx>( lhs_val: BasicValueEnum<'ctx>, rhs_val: BasicValueEnum<'ctx>, lhs_layout: LayoutRepr<'a>, - rhs_layout: LayoutRepr<'a>, + _rhs_layout: LayoutRepr<'a>, ) -> BasicValueEnum<'ctx> { - debug_assert_eq!( - lhs_layout, rhs_layout, - "Equality of different layouts; did you have a type mismatch?\n{:?} == {:?}", - lhs_layout, rhs_layout - ); - match lhs_layout { LayoutRepr::Builtin(builtin) => build_eq_builtin( env, @@ -185,7 +179,7 @@ fn build_eq<'a, 'ctx>( rhs_val, ), - LayoutRepr::Boxed(inner_layout) => build_box_eq( + LayoutRepr::Ptr(inner_layout) => build_box_eq( env, layout_interner, layout_ids, @@ -335,8 +329,7 @@ fn build_neq<'a, 'ctx>( ) -> BasicValueEnum<'ctx> { if lhs_layout != rhs_layout { panic!( - "Inequality of different layouts; did you have a type mismatch?\n{:?} != {:?}", - lhs_layout, rhs_layout + "Inequality of different layouts; did you have a type mismatch?\n{lhs_layout:?} != {rhs_layout:?}" ); } @@ -385,7 +378,7 @@ fn build_neq<'a, 'ctx>( result.into() } - LayoutRepr::Boxed(inner_layout) => { + LayoutRepr::Ptr(inner_layout) => { let is_equal = build_box_eq( env, layout_interner, @@ -795,7 +788,7 @@ fn build_struct_eq_help<'a, 'ctx>( .into_int_value() }; - current = ctx.append_basic_block(parent, &format!("eq_step_{}", index)); + current = ctx.append_basic_block(parent, &format!("eq_step_{index}")); env.builder .build_conditional_branch(are_equal, current, return_false); diff --git a/crates/compiler/gen_llvm/src/llvm/convert.rs b/crates/compiler/gen_llvm/src/llvm/convert.rs index 4cec102a7c1..1b0a3d12478 100644 --- a/crates/compiler/gen_llvm/src/llvm/convert.rs +++ b/crates/compiler/gen_llvm/src/llvm/convert.rs @@ -14,8 +14,8 @@ use roc_target::TargetInfo; use super::struct_::RocStruct; -pub fn basic_type_from_layout<'a, 'ctx, 'env>( - env: &Env<'a, 'ctx, 'env>, +pub fn basic_type_from_layout<'a, 'ctx>( + env: &Env<'a, 'ctx, '_>, layout_interner: &STLayoutInterner<'a>, layout: LayoutRepr<'_>, ) -> BasicTypeEnum<'ctx> { @@ -30,7 +30,8 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>( layout_interner, layout_interner.get_repr(lambda_set.runtime_representation()), ), - Boxed(inner_layout) => { + + Ptr(inner_layout) => { let inner_type = basic_type_from_layout( env, layout_interner, @@ -40,6 +41,7 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>( inner_type.ptr_type(AddressSpace::default()).into() } Union(union_layout) => basic_type_from_union_layout(env, layout_interner, &union_layout), + RecursivePointer(_) => env .context .i64_type() diff --git a/crates/compiler/gen_llvm/src/llvm/expect.rs b/crates/compiler/gen_llvm/src/llvm/expect.rs index 92057f21a19..b6a5eb73a62 100644 --- a/crates/compiler/gen_llvm/src/llvm/expect.rs +++ b/crates/compiler/gen_llvm/src/llvm/expect.rs @@ -18,7 +18,7 @@ use roc_mono::layout::{ use roc_region::all::Region; use super::build::BuilderExt; -use super::build::{add_func, load_roc_value, FunctionSpec, LlvmBackendMode}; +use super::build::{add_func, FunctionSpec, LlvmBackendMode}; use super::convert::struct_type_from_union_layout; use super::scope::Scope; use super::struct_::RocStruct; @@ -353,41 +353,8 @@ fn build_clone<'a, 'ctx>( } } - LayoutRepr::Boxed(inner_layout) => { - // write the offset - build_copy(env, ptr, cursors.offset, cursors.extra_offset.into()); - - let source = value.into_pointer_value(); - let value = load_roc_value( - env, - layout_interner, - layout_interner.get_repr(inner_layout), - source, - "inner", - ); - - let inner_width = env - .ptr_int() - .const_int(layout_interner.stack_size(inner_layout) as u64, false); - - let new_extra = env - .builder - .build_int_add(cursors.offset, inner_width, "new_extra"); - - let cursors = Cursors { - offset: cursors.extra_offset, - extra_offset: new_extra, - }; - - build_clone( - env, - layout_interner, - layout_ids, - ptr, - cursors, - value, - layout_interner.get_repr(inner_layout), - ) + LayoutRepr::Ptr(_) => { + unreachable!("for internal use only") } LayoutRepr::RecursivePointer(rec_layout) => { diff --git a/crates/compiler/gen_llvm/src/llvm/lowlevel.rs b/crates/compiler/gen_llvm/src/llvm/lowlevel.rs index ba2a60b81f4..22115d79cb3 100644 --- a/crates/compiler/gen_llvm/src/llvm/lowlevel.rs +++ b/crates/compiler/gen_llvm/src/llvm/lowlevel.rs @@ -1,4 +1,6 @@ use inkwell::{ + attributes::{Attribute, AttributeLoc}, + module::Linkage, types::{BasicType, IntType}, values::{ BasicValue, BasicValueEnum, FloatValue, FunctionValue, InstructionOpcode, IntValue, @@ -28,8 +30,8 @@ use crate::llvm::{ }, build::{ cast_basic_basic, complex_bitcast_check_size, create_entry_block_alloca, - function_value_by_func_spec, load_roc_value, roc_function_call, tag_pointer_clear_tag_id, - BuilderExt, RocReturn, + entry_block_alloca_zerofill, function_value_by_func_spec, load_roc_value, + roc_function_call, tag_pointer_clear_tag_id, BuilderExt, RocReturn, }, build_list::{ list_append_unsafe, list_concat, list_drop_at, list_get_unsafe, list_len, list_map, @@ -56,7 +58,7 @@ use crate::llvm::{ use super::{build::Env, convert::zig_dec_type}; use super::{ - build::{throw_internal_exception, use_roc_value}, + build::{throw_internal_exception, use_roc_value, FAST_CALL_CONV}, convert::zig_with_overflow_roc_dec, scope::Scope, }; @@ -1304,8 +1306,42 @@ pub(crate) fn run_low_level<'a, 'ctx>( .into() } - PtrWrite | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr - | RefCountDecDataPtr => { + PtrStore => { + arguments!(ptr, value); + + env.builder.build_store(ptr.into_pointer_value(), value); + + // ptr + env.context.struct_type(&[], false).const_zero().into() + } + + PtrLoad => { + arguments!(ptr); + + let ret_repr = layout_interner.get_repr(layout); + let element_type = basic_type_from_layout(env, layout_interner, ret_repr); + + env.builder + .new_build_load(element_type, ptr.into_pointer_value(), "ptr_load") + } + + PtrClearTagId => { + arguments!(ptr); + + tag_pointer_clear_tag_id(env, ptr.into_pointer_value()).into() + } + + Alloca => { + arguments!(initial_value); + + let ptr = entry_block_alloca_zerofill(env, initial_value.get_type(), "stack_value"); + + env.builder.build_store(ptr, initial_value); + + ptr.into() + } + + RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr | RefCountDecDataPtr => { unreachable!("Not used in LLVM backend: {:?}", op); } @@ -1788,7 +1824,7 @@ fn throw_on_overflow<'ctx>( bd.position_at_end(throw_block); - throw_internal_exception(env, parent, message); + throw_because_overflow(env, message); bd.position_at_end(then_block); @@ -1796,6 +1832,60 @@ fn throw_on_overflow<'ctx>( .unwrap() } +fn throw_because_overflow(env: &Env<'_, '_, '_>, message: &str) { + let block = env.builder.get_insert_block().expect("to be in a function"); + let di_location = env.builder.get_current_debug_location().unwrap(); + + let function_name = "throw_on_overflow"; + let function = match env.module.get_function(function_name) { + Some(function_value) => function_value, + None => { + let function_type = env.context.void_type().fn_type(&[], false); + let function_value = + env.module + .add_function(function_name, function_type, Some(Linkage::Internal)); + + function_value.set_call_conventions(FAST_CALL_CONV); + + // prevent inlining of this function + let kind_id = Attribute::get_named_enum_kind_id("noinline"); + debug_assert!(kind_id > 0); + let enum_attr = env.context.create_enum_attribute(kind_id, 0); + function_value.add_attribute(AttributeLoc::Function, enum_attr); + + // calling this function is unlikely + let kind_id = Attribute::get_named_enum_kind_id("cold"); + debug_assert!(kind_id > 0); + let enum_attr = env.context.create_enum_attribute(kind_id, 0); + function_value.add_attribute(AttributeLoc::Function, enum_attr); + + // this function never returns + let kind_id = Attribute::get_named_enum_kind_id("noreturn"); + debug_assert!(kind_id > 0); + let enum_attr = env.context.create_enum_attribute(kind_id, 0); + function_value.add_attribute(AttributeLoc::Function, enum_attr); + + // Add a basic block for the entry point + let entry = env.context.append_basic_block(function_value, "entry"); + + env.builder.position_at_end(entry); + + // ends in unreachable, so no return is needed + throw_internal_exception(env, function_value, message); + + function_value + } + }; + + env.builder.position_at_end(block); + env.builder.set_current_debug_location(di_location); + + let call = env.builder.build_call(function, &[], "overflow"); + call.set_call_convention(FAST_CALL_CONV); + + env.builder.build_unreachable(); +} + fn dec_split_into_words<'ctx>( env: &Env<'_, 'ctx, '_>, value: IntValue<'ctx>, @@ -2802,6 +2892,6 @@ fn load_symbol_and_lambda_set<'a, 'ctx>( let (ptr, layout) = scope.load_symbol_and_layout(symbol); match layout_interner.get_repr(layout) { LayoutRepr::LambdaSet(lambda_set) => (ptr, lambda_set), - other => panic!("Not a lambda set: {:?}, {:?}", other, ptr), + other => panic!("Not a lambda set: {other:?}, {ptr:?}"), } } diff --git a/crates/compiler/gen_llvm/src/llvm/refcounting.rs b/crates/compiler/gen_llvm/src/llvm/refcounting.rs index 5235bcf872b..64749f4a03e 100644 --- a/crates/compiler/gen_llvm/src/llvm/refcounting.rs +++ b/crates/compiler/gen_llvm/src/llvm/refcounting.rs @@ -14,7 +14,7 @@ use bumpalo::collections::Vec; use inkwell::basic_block::BasicBlock; use inkwell::module::Linkage; use inkwell::types::{AnyTypeEnum, BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; -use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue}; +use inkwell::values::{BasicValueEnum, FunctionValue, InstructionValue, IntValue, PointerValue}; use inkwell::{AddressSpace, IntPredicate}; use roc_module::symbol::Interns; use roc_module::symbol::Symbol; @@ -132,7 +132,7 @@ impl<'ctx> PointerToRefcount<'ctx> { let block = env.builder.get_insert_block().expect("to be in a function"); let di_location = env.builder.get_current_debug_location().unwrap(); - let fn_name = &format!("decrement_refcounted_ptr_{}", alignment); + let fn_name = &format!("decrement_refcounted_ptr_{alignment}"); let function = match env.module.get_function(fn_name) { Some(function_value) => function_value, @@ -193,6 +193,14 @@ impl<'ctx> PointerToRefcount<'ctx> { builder.build_return(None); } + + pub fn deallocate<'a, 'env>( + &self, + env: &Env<'a, 'ctx, 'env>, + alignment: u32, + ) -> InstructionValue<'ctx> { + free_pointer(env, self.value, alignment) + } } fn incref_pointer<'ctx>( @@ -216,6 +224,28 @@ fn incref_pointer<'ctx>( ); } +fn free_pointer<'ctx>( + env: &Env<'_, 'ctx, '_>, + pointer: PointerValue<'ctx>, + alignment: u32, +) -> InstructionValue<'ctx> { + let alignment = env.context.i32_type().const_int(alignment as _, false); + call_void_bitcode_fn( + env, + &[ + env.builder + .build_pointer_cast( + pointer, + env.ptr_int().ptr_type(AddressSpace::default()), + "to_isize_ptr", + ) + .into(), + alignment.into(), + ], + roc_builtins::bitcode::UTILS_FREE_RC_PTR, + ) +} + fn decref_pointer<'ctx>(env: &Env<'_, 'ctx, '_>, pointer: PointerValue<'ctx>, alignment: u32) { let alignment = env.context.i32_type().const_int(alignment as _, false); call_void_bitcode_fn( @@ -531,10 +561,10 @@ fn modify_refcount_layout_build_function<'a, 'ctx>( modify_refcount_builtin(env, layout_interner, layout_ids, mode, layout, &builtin) } - Boxed(inner) => { - let function = modify_refcount_boxed(env, layout_interner, layout_ids, mode, inner); + Ptr(_inner) => { + debug_assert_eq!(true, false); - Some(function) + None } Union(variant) => { @@ -861,130 +891,6 @@ fn modify_refcount_str_help<'a, 'ctx>( builder.build_return(None); } -fn modify_refcount_boxed<'a, 'ctx>( - env: &Env<'a, 'ctx, '_>, - layout_interner: &STLayoutInterner<'a>, - layout_ids: &mut LayoutIds<'a>, - mode: Mode, - inner_layout: InLayout<'a>, -) -> FunctionValue<'ctx> { - let block = env.builder.get_insert_block().expect("to be in a function"); - let di_location = env.builder.get_current_debug_location().unwrap(); - - let boxed_layout = LayoutRepr::Boxed(inner_layout); - - let (_, fn_name) = function_name_from_mode( - layout_ids, - &env.interns, - "increment_boxed", - "decrement_boxed", - boxed_layout, - mode, - ); - - let function = match env.module.get_function(fn_name.as_str()) { - Some(function_value) => function_value, - None => { - let basic_type = basic_type_from_layout(env, layout_interner, boxed_layout); - let function_value = build_header(env, basic_type, mode, &fn_name); - - modify_refcount_box_help( - env, - layout_interner, - layout_ids, - mode, - inner_layout, - function_value, - ); - - function_value - } - }; - - env.builder.position_at_end(block); - env.builder.set_current_debug_location(di_location); - - function -} - -fn modify_refcount_box_help<'a, 'ctx>( - env: &Env<'a, 'ctx, '_>, - layout_interner: &STLayoutInterner<'a>, - layout_ids: &mut LayoutIds<'a>, - mode: Mode, - inner_layout: InLayout<'a>, - fn_val: FunctionValue<'ctx>, -) { - let builder = env.builder; - let ctx = env.context; - - // Add a basic block for the entry point - let entry = ctx.append_basic_block(fn_val, "entry"); - - builder.position_at_end(entry); - - debug_info_init!(env, fn_val); - - // Add args to scope - let arg_symbol = Symbol::ARG_1; - let arg_val = fn_val.get_param_iter().next().unwrap(); - arg_val.set_name(arg_symbol.as_str(&env.interns)); - - let boxed = arg_val.into_pointer_value(); - let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, boxed); - let call_mode = mode_to_call_mode(fn_val, mode); - let boxed_layout = LayoutRepr::Boxed(inner_layout); - - match mode { - Mode::Inc => { - refcount_ptr.modify(call_mode, boxed_layout, env, layout_interner); - builder.build_return(None); - } - Mode::Dec => { - // if the box is unique, also decrement its inner value - let do_recurse_block = env.context.append_basic_block(fn_val, "do_recurse"); - let no_recurse_block = env.context.append_basic_block(fn_val, "no_recurse"); - - builder.build_conditional_branch( - refcount_ptr.is_1(env), - do_recurse_block, - no_recurse_block, - ); - - { - env.builder.position_at_end(do_recurse_block); - - let inner = load_roc_value( - env, - layout_interner, - layout_interner.get_repr(inner_layout), - boxed, - "inner", - ); - - modify_refcount_layout( - env, - layout_interner, - layout_ids, - call_mode, - inner, - inner_layout, - ); - - refcount_ptr.modify(call_mode, boxed_layout, env, layout_interner); - env.builder.build_return(None); - } - - { - env.builder.position_at_end(no_recurse_block); - - refcount_ptr.modify(call_mode, boxed_layout, env, layout_interner); - env.builder.build_return(None); - } - } - } -} - /// Build an increment or decrement function for a specific layout fn build_header<'ctx>( env: &Env<'_, 'ctx, '_>, @@ -1492,7 +1398,7 @@ pub fn build_reset<'a, 'ctx>( let union_layout_repr = LayoutRepr::Union(union_layout); let layout_id = layout_ids.get(Symbol::DEC, &union_layout_repr); let fn_name = layout_id.to_symbol_string(Symbol::DEC, &env.interns); - let fn_name = format!("{}_reset", fn_name); + let fn_name = format!("{fn_name}_reset"); let dec_function = build_rec_union(env, layout_interner, layout_ids, Mode::Dec, union_layout); diff --git a/crates/compiler/gen_llvm/src/llvm/scope.rs b/crates/compiler/gen_llvm/src/llvm/scope.rs index 3261977bc86..a771700e66d 100644 --- a/crates/compiler/gen_llvm/src/llvm/scope.rs +++ b/crates/compiler/gen_llvm/src/llvm/scope.rs @@ -28,17 +28,14 @@ impl<'a, 'ctx> Scope<'a, 'ctx> { match self.symbols.get(symbol) { Some((_, ptr)) => *ptr, - None => panic!( - "There was no entry for {:?} {} in scope {:?}", - symbol, symbol, self - ), + None => panic!("There was no entry for {symbol:?} {symbol} in scope {self:?}"), } } pub fn load_symbol_and_layout(&self, symbol: &Symbol) -> (BasicValueEnum<'ctx>, InLayout<'a>) { match self.symbols.get(symbol) { Some((layout, ptr)) => (*ptr, *layout), - None => panic!("There was no entry for {:?} in scope {:?}", symbol, self), + None => panic!("There was no entry for {symbol:?} in scope {self:?}"), } } diff --git a/crates/compiler/gen_llvm/src/llvm/struct_.rs b/crates/compiler/gen_llvm/src/llvm/struct_.rs index 30da7eb55e0..5f26b0ec0a9 100644 --- a/crates/compiler/gen_llvm/src/llvm/struct_.rs +++ b/crates/compiler/gen_llvm/src/llvm/struct_.rs @@ -92,7 +92,15 @@ impl<'ctx> RocStruct<'ctx> { index_struct_value(env, layout_interner, field_layouts, *argument, index) } (Self::ByReference(ptr), LayoutRepr::Struct(field_layouts)) => { - index_struct_ptr(env, layout_interner, field_layouts, *ptr, index) + let struct_type = basic_type_from_layout(env, layout_interner, struct_layout); + index_struct_ptr( + env, + layout_interner, + struct_type.into_struct_type(), + field_layouts, + *ptr, + index, + ) } (other, layout) => { unreachable!( @@ -118,7 +126,7 @@ fn index_struct_value<'a, 'ctx>( argument, index as _, env.arena - .alloc(format!("struct_field_access_record_{}", index)), + .alloc(format!("struct_field_access_record_{index}")), ); let field_layout = field_layouts[index as usize]; @@ -135,26 +143,26 @@ fn index_struct_value<'a, 'ctx>( fn index_struct_ptr<'a, 'ctx>( env: &Env<'a, 'ctx, '_>, layout_interner: &STLayoutInterner<'a>, + struct_type: StructType<'ctx>, field_layouts: &[InLayout<'a>], ptr: PointerValue<'ctx>, index: u64, ) -> BasicValueEnum<'ctx> { debug_assert!(!field_layouts.is_empty()); - let field_value = get_field_from_ptr( - env, - ptr, - index as _, - env.arena - .alloc(format!("struct_field_access_record_{}", index)), - ); - let field_layout = field_layouts[index as usize]; + let field_repr = layout_interner.get_repr(field_layout); + + let name = format!("struct_field_access_record_{index}"); + let field_value = env + .builder + .new_build_struct_gep(struct_type, ptr, index as u32, &name) + .unwrap(); load_roc_value( env, layout_interner, - layout_interner.get_repr(field_layout), + field_repr, field_value, "struct_field", ) @@ -171,15 +179,6 @@ fn get_field_from_value<'ctx>( .unwrap() } -fn get_field_from_ptr<'ctx>( - env: &Env<'_, 'ctx, '_>, - ptr: PointerValue<'ctx>, - index: u32, - name: &str, -) -> PointerValue<'ctx> { - env.builder.build_struct_gep(ptr, index, name).unwrap() -} - struct BuildStruct<'ctx> { struct_type: StructType<'ctx>, struct_val: StructValue<'ctx>, diff --git a/crates/compiler/gen_wasm/src/backend.rs b/crates/compiler/gen_wasm/src/backend.rs index 0476c9d19a4..0a87f5d098b 100644 --- a/crates/compiler/gen_wasm/src/backend.rs +++ b/crates/compiler/gen_wasm/src/backend.rs @@ -1,7 +1,7 @@ use bitvec::vec::BitVec; use bumpalo::collections::{String, Vec}; -use roc_builtins::bitcode::{FloatWidth, IntWidth}; +use roc_builtins::bitcode::{self, FloatWidth, IntWidth}; use roc_collections::all::MutMap; use roc_error_macros::internal_error; use roc_module::low_level::{LowLevel, LowLevelWrapperType}; @@ -313,7 +313,7 @@ impl<'a, 'r> WasmBackend<'a, 'r> { if let Ok(sym_index) = self.module.linking.find_internal_symbol(START) { let fn_index = match self.module.linking.symbol_table[sym_index] { SymInfo::Function(WasmObjectSymbol::ExplicitlyNamed { index, .. }) => index, - _ => panic!("linker symbol `{}` is not a function", START), + _ => panic!("linker symbol `{START}` is not a function"), }; self.module.export.append(Export { name: START, @@ -463,7 +463,7 @@ impl<'a, 'r> WasmBackend<'a, 'r> { if DEBUG_SETTINGS.storage_map { println!("\nStorage:"); for (sym, storage) in self.storage.symbol_storage_map.iter() { - println!("{:?} => {:?}", sym, storage); + println!("{sym:?} => {storage:?}"); } } } @@ -509,7 +509,7 @@ impl<'a, 'r> WasmBackend<'a, 'r> { .last() .map(|l| self.layout_interner.get_repr(*l)) { - Some(LayoutRepr::Boxed(inner)) => WasmLayout::new(self.layout_interner, inner), + Some(LayoutRepr::Ptr(inner)) => WasmLayout::new(self.layout_interner, inner), x => internal_error!("Higher-order wrapper: invalid return layout {:?}", x), }; @@ -540,8 +540,8 @@ impl<'a, 'r> WasmBackend<'a, 'r> { } let inner_layout = match self.layout_interner.get_repr(*wrapper_arg) { - LayoutRepr::Boxed(inner) => inner, - x => internal_error!("Expected a Boxed layout, got {:?}", x), + LayoutRepr::Ptr(inner) => inner, + x => internal_error!("Expected a Ptr layout, got {:?}", x), }; if self.layout_interner.stack_size(inner_layout) == 0 { continue; @@ -634,8 +634,8 @@ impl<'a, 'r> WasmBackend<'a, 'r> { } let inner_layout = match self.layout_interner.get_repr(value_layout) { - LayoutRepr::Boxed(inner) => inner, - x => internal_error!("Expected a Boxed layout, got {:?}", x), + LayoutRepr::Ptr(inner) => inner, + x => internal_error!("Expected a Ptr layout, got {:?}", x), }; self.code_builder.get_local(LocalId(1)); self.dereference_boxed_value(inner_layout); @@ -719,7 +719,10 @@ impl<'a, 'r> WasmBackend<'a, 'r> { Stmt::Jump(id, arguments) => self.stmt_jump(*id, arguments), - Stmt::Refcounting(modify, following) => self.stmt_refcounting(modify, following), + Stmt::Refcounting(modify, following) => match modify { + ModifyRc::Free(symbol) => self.stmt_refcounting_free(*symbol, following), + _ => self.stmt_refcounting(modify, following), + }, Stmt::Dbg { .. } => todo!("dbg is not implemented in the wasm backend"), Stmt::Expect { .. } => todo!("expect is not implemented in the wasm backend"), @@ -999,6 +1002,43 @@ impl<'a, 'r> WasmBackend<'a, 'r> { self.stmt(rc_stmt); } + fn stmt_refcounting_free(&mut self, value: Symbol, following: &'a Stmt<'a>) { + let layout = self.storage.symbol_layouts[&value]; + let alignment = self.layout_interner.allocation_alignment_bytes(layout); + + // Get pointer and offset + let value_storage = self.storage.get(&value).to_owned(); + let stored_with_local = + self.storage + .ensure_value_has_local(&mut self.code_builder, value, value_storage); + let (tag_local_id, tag_offset) = match stored_with_local { + StoredValue::StackMemory { location, .. } => { + location.local_and_offset(self.storage.stack_frame_pointer) + } + StoredValue::Local { local_id, .. } => (local_id, 0), + StoredValue::VirtualMachineStack { .. } => { + internal_error!("{:?} should have a local variable", value) + } + }; + + // load pointer, and add the offset to the pointer + self.code_builder.get_local(tag_local_id); + + if tag_offset > 0 { + self.code_builder.i32_const(tag_offset as i32); + self.code_builder.i32_add(); + } + + // NOTE: UTILS_FREE_DATA_PTR clears any tag id bits + + // push the allocation's alignment + self.code_builder.i32_const(alignment as i32); + + self.call_host_fn_after_loading_args(bitcode::UTILS_FREE_DATA_PTR, 2, false); + + self.stmt(following); + } + pub fn stmt_internal_error(&mut self, msg: &'a str) { let msg_sym = self.create_symbol("panic_str"); let msg_storage = self.storage.allocate_var( @@ -1064,8 +1104,11 @@ impl<'a, 'r> WasmBackend<'a, 'r> { tag_layout: union_layout, tag_id, arguments, - .. - } => self.expr_tag(union_layout, *tag_id, arguments, sym, storage, None), + reuse, + } => { + let reuse = reuse.map(|ru| ru.symbol); + self.expr_tag(union_layout, *tag_id, arguments, sym, storage, reuse) + } Expr::GetTagId { structure, @@ -1079,17 +1122,19 @@ impl<'a, 'r> WasmBackend<'a, 'r> { index, } => self.expr_union_at_index(*structure, *tag_id, union_layout, *index, sym), - Expr::ExprBox { symbol: arg_sym } => self.expr_box(sym, *arg_sym, layout, storage), - - Expr::ExprUnbox { symbol: arg_sym } => self.expr_unbox(sym, *arg_sym), - - Expr::Reuse { - tag_layout, + Expr::UnionFieldPtrAtIndex { + structure, tag_id, - arguments, - symbol: reused, - .. - } => self.expr_tag(tag_layout, *tag_id, arguments, sym, storage, Some(*reused)), + union_layout, + index, + } => self.expr_union_field_ptr_at_index( + *structure, + *tag_id, + union_layout, + *index, + sym, + storage, + ), Expr::Reset { symbol: arg, .. } => self.expr_reset(*arg, sym, storage), @@ -1379,7 +1424,7 @@ impl<'a, 'r> WasmBackend<'a, 'r> { .host_lookup .iter() .find(|(fn_name, _)| *fn_name == name) - .unwrap_or_else(|| panic!("The Roc app tries to call `{}` but I can't find it!", name)); + .unwrap_or_else(|| panic!("The Roc app tries to call `{name}` but I can't find it!")); self.called_fns.set(*fn_index as usize, true); @@ -1569,7 +1614,7 @@ impl<'a, 'r> WasmBackend<'a, 'r> { ListLiteralElement::Literal(lit) => { // This has no Symbol but our storage methods expect one. // Let's just pretend it was defined in a `Let`. - let debug_name = format!("{:?}_{}", sym, i); + let debug_name = format!("{sym:?}_{i}"); let elem_sym = self.create_symbol(&debug_name); let expr = Expr::Literal(*lit); @@ -1871,44 +1916,92 @@ impl<'a, 'r> WasmBackend<'a, 'r> { ); } - /******************************************************************* - * Box - *******************************************************************/ - - fn expr_box( + fn expr_union_field_ptr_at_index( &mut self, - ret_sym: Symbol, - arg_sym: Symbol, - layout: InLayout<'a>, + structure: Symbol, + tag_id: TagIdIntType, + union_layout: &UnionLayout<'a>, + index: u64, + symbol: Symbol, storage: &StoredValue, ) { - // create a local variable for the heap pointer - let ptr_local_id = match self.storage.ensure_value_has_local( + use UnionLayout::*; + + debug_assert!(!union_layout.tag_is_null(tag_id)); + + let tag_index = tag_id as usize; + let field_layouts = match union_layout { + NonRecursive(tags) => tags[tag_index], + Recursive(tags) => tags[tag_index], + NonNullableUnwrapped(layouts) => *layouts, + NullableWrapped { + other_tags, + nullable_id, + } => { + let index = if tag_index > *nullable_id as usize { + tag_index - 1 + } else { + tag_index + }; + other_tags[index] + } + NullableUnwrapped { other_fields, .. } => *other_fields, + }; + + let field_offset: u32 = field_layouts + .iter() + .take(index as usize) + .map(|field_layout| self.layout_interner.stack_size(*field_layout)) + .sum(); + + // Get pointer and offset to the tag's data + let structure_storage = self.storage.get(&structure).to_owned(); + let stored_with_local = self.storage.ensure_value_has_local( &mut self.code_builder, - ret_sym, + structure, + structure_storage, + ); + let (tag_local_id, tag_offset) = match stored_with_local { + StoredValue::StackMemory { location, .. } => { + location.local_and_offset(self.storage.stack_frame_pointer) + } + StoredValue::Local { local_id, .. } => (local_id, 0), + StoredValue::VirtualMachineStack { .. } => { + internal_error!("{:?} should have a local variable", structure) + } + }; + + let stores_tag_id_in_pointer = union_layout.stores_tag_id_in_pointer(TARGET_INFO); + + let from_offset = tag_offset + field_offset; + + self.code_builder.get_local(tag_local_id); + + if stores_tag_id_in_pointer { + self.code_builder.i32_const(-4); // 11111111...1100 + self.code_builder.i32_and(); + } + + self.code_builder.i32_const(from_offset as _); + self.code_builder.i32_add(); + + let symbol_local = match self.storage.ensure_value_has_local( + &mut self.code_builder, + symbol, storage.clone(), ) { StoredValue::Local { local_id, .. } => local_id, _ => internal_error!("A heap pointer will always be an i32"), }; - // allocate heap memory and load its data address onto the value stack - let arg_layout = match self.layout_interner.get_repr(layout) { - LayoutRepr::Boxed(arg) => arg, - _ => internal_error!("ExprBox should always produce a Boxed layout"), - }; - let (size, alignment) = self.layout_interner.stack_size_and_alignment(arg_layout); - self.allocate_with_refcount(Some(size), alignment, 1); - - // store the pointer value from the value stack into the local variable - self.code_builder.set_local(ptr_local_id); - - // copy the argument to the pointer address - self.storage - .copy_value_to_memory(&mut self.code_builder, ptr_local_id, 0, arg_sym); + self.code_builder.set_local(symbol_local); } - fn expr_unbox(&mut self, ret_sym: Symbol, arg_sym: Symbol) { + /******************************************************************* + * Box + *******************************************************************/ + + pub(crate) fn ptr_load(&mut self, ret_sym: Symbol, arg_sym: Symbol) { let (from_addr_val, from_offset) = match self.storage.get(&arg_sym) { StoredValue::VirtualMachineStack { .. } => { self.storage diff --git a/crates/compiler/gen_wasm/src/code_builder.rs b/crates/compiler/gen_wasm/src/code_builder.rs index 92912f0d012..ac20dc7dbed 100644 --- a/crates/compiler/gen_wasm/src/code_builder.rs +++ b/crates/compiler/gen_wasm/src/code_builder.rs @@ -322,10 +322,7 @@ impl<'a> CodeBuilder<'a> { self.add_insertion(pushed_at, SETLOCAL, local_id.0); } else { if DEBUG_SETTINGS.instructions { - println!( - "{:?} has been popped implicitly. Leaving it on the stack.", - symbol - ); + println!("{symbol:?} has been popped implicitly. Leaving it on the stack."); } self.add_insertion(pushed_at, TEELOCAL, local_id.0); } @@ -501,9 +498,7 @@ impl<'a> CodeBuilder<'a> { debug_assert!( stack_size >= pops, - "Wasm value stack underflow. Tried to pop {} but only {} available", - pops, - stack_size + "Wasm value stack underflow. Tried to pop {pops} but only {stack_size} available" ); let new_len = stack_size - pops; @@ -517,11 +512,7 @@ impl<'a> CodeBuilder<'a> { /// Plain instruction without any immediates fn inst(&mut self, opcode: OpCode, pops: usize, push: bool) { self.inst_base(opcode, pops, push); - log_instruction!( - "{:10}\t\t{:?}", - format!("{:?}", opcode), - self.vm_block_stack - ); + log_instruction!("{:10}\t\t{:?}", format!("{opcode:?}"), self.vm_block_stack); } /// Block instruction @@ -538,7 +529,7 @@ impl<'a> CodeBuilder<'a> { value_stack: Vec::with_capacity_in(8, self.arena), }); - log_instruction!("{:10}\t{:?}", format!("{:?}", opcode), &self.vm_block_stack); + log_instruction!("{:10}\t{:?}", format!("{opcode:?}"), &self.vm_block_stack); } fn inst_imm32(&mut self, opcode: OpCode, pops: usize, push: bool, immediate: u32) { @@ -546,7 +537,7 @@ impl<'a> CodeBuilder<'a> { self.code.encode_u32(immediate); log_instruction!( "{:10}\t{}\t{:?}", - format!("{:?}", opcode), + format!("{opcode:?}"), immediate, self.vm_block_stack ); @@ -558,7 +549,7 @@ impl<'a> CodeBuilder<'a> { self.code.encode_u32(offset); log_instruction!( "{:10} {:?} {}\t{:?}", - format!("{:?}", opcode), + format!("{opcode:?}"), align, offset, self.vm_block_stack @@ -654,7 +645,7 @@ impl<'a> CodeBuilder<'a> { log_instruction!( "{:10}\t{}\t{:?}", - format!("{:?}", CALL), + format!("{CALL:?}"), function_index, self.vm_block_stack ); @@ -725,7 +716,7 @@ impl<'a> CodeBuilder<'a> { { log_instruction!( "{:10}\t{}\t{:?}", - format!("{:?}", opcode), + format!("{opcode:?}"), x, self.vm_block_stack ); diff --git a/crates/compiler/gen_wasm/src/layout.rs b/crates/compiler/gen_wasm/src/layout.rs index 93e3b202b61..0745fc4bc9c 100644 --- a/crates/compiler/gen_wasm/src/layout.rs +++ b/crates/compiler/gen_wasm/src/layout.rs @@ -97,7 +97,7 @@ impl WasmLayout { | NullableWrapped { .. } | NullableUnwrapped { .. }, ) - | LayoutRepr::Boxed(_) + | LayoutRepr::Ptr(_) | LayoutRepr::RecursivePointer(_) => Self::Primitive(PTR_TYPE, PTR_SIZE), } } diff --git a/crates/compiler/gen_wasm/src/low_level.rs b/crates/compiler/gen_wasm/src/low_level.rs index d7dd1f3a322..3ab828f5bef 100644 --- a/crates/compiler/gen_wasm/src/low_level.rs +++ b/crates/compiler/gen_wasm/src/low_level.rs @@ -1960,7 +1960,74 @@ impl<'a> LowLevelCall<'a> { backend.storage.load_symbols(code_builder, self.arguments); } - PtrWrite => todo!("{:?}", self.lowlevel), + PtrStore => { + // PtrStore : Ptr a, a -> {} + let ptr = self.arguments[0]; + let value = self.arguments[1]; + + let (ptr_local_id, offset) = match backend.storage.get(&ptr) { + StoredValue::Local { local_id, .. } => (*local_id, 0), + _ => internal_error!("A pointer will always be an i32"), + }; + + // copy the argument to the pointer address + backend.storage.copy_value_to_memory( + &mut backend.code_builder, + ptr_local_id, + offset, + value, + ); + } + PtrLoad => backend.ptr_load(self.ret_symbol, self.arguments[0]), + PtrClearTagId => { + let ptr = self.arguments[0]; + + let ptr_local_id = match backend.storage.get(&ptr) { + StoredValue::Local { local_id, .. } => *local_id, + _ => internal_error!("A pointer will always be an i32"), + }; + + backend.code_builder.get_local(ptr_local_id); + + backend.code_builder.i32_const(-4); // 11111111...1100 + backend.code_builder.i32_and(); + } + Alloca => { + // Alloca : a -> Ptr a + let arg = self.arguments[0]; + let arg_layout = backend.storage.symbol_layouts.get(&arg).unwrap(); + + let (size, alignment_bytes) = backend + .layout_interner + .stack_size_and_alignment(*arg_layout); + + let (frame_ptr, offset) = backend + .storage + .allocate_anonymous_stack_memory(size, alignment_bytes); + + // write the default value into the stack memory + backend.storage.copy_value_to_memory( + &mut backend.code_builder, + frame_ptr, + offset, + arg, + ); + + // create a local variable for the pointer + let ptr_local_id = match backend.storage.ensure_value_has_local( + &mut backend.code_builder, + self.ret_symbol, + self.ret_storage.clone(), + ) { + StoredValue::Local { local_id, .. } => local_id, + _ => internal_error!("A pointer will always be an i32"), + }; + + backend.code_builder.get_local(frame_ptr); + backend.code_builder.i32_const(offset as i32); + backend.code_builder.i32_add(); + backend.code_builder.set_local(ptr_local_id); + } Hash => todo!("{:?}", self.lowlevel), @@ -1996,8 +2063,7 @@ impl<'a> LowLevelCall<'a> { .runtime_representation(backend.storage.symbol_layouts[&self.arguments[1]]); debug_assert_eq!( arg_layout_raw, other_arg_layout, - "Cannot do `==` comparison on different types: {:?} vs {:?}", - arg_layout, other_arg_layout + "Cannot do `==` comparison on different types: {arg_layout:?} vs {other_arg_layout:?}" ); let invert_result = matches!(self.lowlevel, LowLevel::NotEq); @@ -2030,7 +2096,7 @@ impl<'a> LowLevelCall<'a> { | LayoutRepr::Struct { .. } | LayoutRepr::Union(_) | LayoutRepr::LambdaSet(_) - | LayoutRepr::Boxed(_) => { + | LayoutRepr::Ptr(_) => { // Don't want Zig calling convention here, we're calling internal Roc functions backend .storage @@ -2435,7 +2501,7 @@ pub fn call_higher_order_lowlevel<'a>( } } }; - let wrapper_sym = backend.create_symbol(&format!("#wrap#{:?}", fn_name)); + let wrapper_sym = backend.create_symbol(&format!("#wrap#{fn_name:?}")); let wrapper_layout = { let mut wrapper_arg_layouts: Vec> = Vec::with_capacity_in(argument_layouts.len() + 1, backend.env.arena); @@ -2450,7 +2516,7 @@ pub fn call_higher_order_lowlevel<'a>( argument_layouts.iter().take(n_non_closure_args).map(|lay| { backend .layout_interner - .insert_direct_no_semantic(LayoutRepr::Boxed(*lay)) + .insert_direct_no_semantic(LayoutRepr::Ptr(*lay)) }); wrapper_arg_layouts.push(wrapped_captures_layout); @@ -2462,7 +2528,7 @@ pub fn call_higher_order_lowlevel<'a>( wrapper_arg_layouts.push( backend .layout_interner - .insert_direct_no_semantic(LayoutRepr::Boxed(*result_layout)), + .insert_direct_no_semantic(LayoutRepr::Ptr(*result_layout)), ); ProcLayout { arguments: wrapper_arg_layouts.into_bump_slice(), diff --git a/crates/compiler/gen_wasm/src/wasm32_result.rs b/crates/compiler/gen_wasm/src/wasm32_result.rs index b1410091975..db05c5ec123 100644 --- a/crates/compiler/gen_wasm/src/wasm32_result.rs +++ b/crates/compiler/gen_wasm/src/wasm32_result.rs @@ -78,7 +78,7 @@ pub fn insert_wrapper_for_layout<'a>( bool::insert_wrapper(arena, module, wrapper_name, main_fn_index); } LayoutRepr::Union(UnionLayout::NonRecursive(_)) => stack_data_structure(), - LayoutRepr::Union(_) | LayoutRepr::Boxed(_) => { + LayoutRepr::Union(_) => { i32::insert_wrapper(arena, module, wrapper_name, main_fn_index); } _ => stack_data_structure(), diff --git a/crates/compiler/late_solve/src/lib.rs b/crates/compiler/late_solve/src/lib.rs index 15098bda7aa..5e5a5ceb64a 100644 --- a/crates/compiler/late_solve/src/lib.rs +++ b/crates/compiler/late_solve/src/lib.rs @@ -5,6 +5,7 @@ use std::sync::{Arc, RwLock}; use bumpalo::Bump; use roc_can::abilities::AbilitiesStore; +use roc_can::constraint::Constraints; use roc_can::module::ExposedByModule; use roc_collections::MutMap; use roc_derive::SharedDerivedModule; @@ -12,13 +13,14 @@ use roc_error_macros::internal_error; use roc_module::symbol::ModuleId; use roc_module::symbol::Symbol; use roc_solve::ability::AbilityResolver; -use roc_solve::solve::Pools; -use roc_solve::specialize::{compact_lambda_sets_of_vars, DerivedEnv, Phase}; +use roc_solve::specialize::{compact_lambda_sets_of_vars, Phase}; +use roc_solve::Pools; +use roc_solve::{DerivedEnv, Env}; use roc_types::subs::{get_member_lambda_sets_at_region, Content, FlatType, LambdaSet}; use roc_types::subs::{ExposedTypesStorageSubs, Subs, Variable}; use roc_types::types::Polarity; use roc_unify::unify::MetaCollector; -use roc_unify::unify::{Env, Mode, Unified}; +use roc_unify::unify::{Env as UEnv, Mode, Unified}; pub use roc_solve::ability::{ResolveError, Resolved}; pub use roc_types::subs::instantiate_rigids; @@ -50,7 +52,7 @@ impl WorldAbilities { .unwrap() .insert(module, (store, exposed_types)); - debug_assert!(old_store.is_none(), "{:?} abilities not new", module); + debug_assert!(old_store.is_none(), "{module:?} abilities not new"); } #[inline(always)] @@ -340,6 +342,19 @@ impl MetaCollector for ChangedVariableCollector { } } +std::thread_local! { + static SCRATCHPAD_FOR_OCCURS: std::cell::RefCell> = std::cell::RefCell::new(Some(Constraints::empty())); +} + +fn with_empty_solve_constraints(f: impl FnOnce(&Constraints) -> T) -> T { + SCRATCHPAD_FOR_OCCURS.with(|cell| { + let constr = cell.take().unwrap(); + let result = f(&constr); + cell.replace(Some(constr)); + result + }) +} + /// Unifies two variables and performs lambda set compaction. /// Ranks and other ability demands are disregarded. #[allow(clippy::too_many_arguments)] @@ -359,7 +374,7 @@ pub fn unify( "derived module can only unify its subs in its own context!" ); let unified = roc_unify::unify::unify_with_collector::( - &mut Env::new(subs), + &mut UEnv::new(subs), left, right, Mode::EQ, @@ -381,14 +396,17 @@ pub fn unify( exposed_types: exposed_by_module, }; - let must_implement_constraints = compact_lambda_sets_of_vars( - subs, - &derived_env, - arena, - &mut pools, - lambda_sets_to_specialize, - &late_phase, - ); + let must_implement_constraints = with_empty_solve_constraints(|c| { + let mut env = Env { + constraints: c, + subs, + derived_env: &derived_env, + arena, + pools: &mut pools, + }; + + compact_lambda_sets_of_vars(&mut env, lambda_sets_to_specialize, &late_phase) + }); // 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. // After we land https://github.com/roc-lang/roc/issues/3207 this concern should totally diff --git a/crates/compiler/load/src/lib.rs b/crates/compiler/load/src/lib.rs index 3f66ff742a3..ff7e4ec5749 100644 --- a/crates/compiler/load/src/lib.rs +++ b/crates/compiler/load/src/lib.rs @@ -18,8 +18,11 @@ const SKIP_SUBS_CACHE: bool = { pub use roc_load_internal::docs; pub use roc_load_internal::file::{ - EntryPoint, ExecutionMode, ExpectMetadata, Expectations, ExposedToHost, LoadConfig, LoadResult, - LoadStart, LoadedModule, LoadingProblem, MonomorphizedModule, Phase, Threading, + ExecutionMode, ExpectMetadata, LoadConfig, LoadResult, LoadStart, LoadingProblem, Phase, + Threading, +}; +pub use roc_load_internal::module::{ + EntryPoint, Expectations, ExposedToHost, LoadedModule, MonomorphizedModule, }; #[allow(clippy::too_many_arguments)] diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 7c0045af3c6..8f1a4437c12 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -1,6 +1,12 @@ #![allow(clippy::too_many_arguments)] use crate::docs::ModuleDocumentation; +use crate::module::{ + ConstrainedModule, EntryPoint, Expectations, ExposedToHost, FoundSpecializationsModule, + LateSpecializationsModule, LoadedModule, ModuleHeader, ModuleTiming, MonomorphizedModule, + ParsedModule, ToplevelExpects, TypeCheckedModule, +}; +use crate::module_cache::ModuleCache; use bumpalo::{collections::CollectIn, Bump}; use crossbeam::channel::{bounded, Sender}; use crossbeam::deque::{Injector, Stealer, Worker}; @@ -20,7 +26,8 @@ use roc_debug_flags::dbg_do; #[cfg(debug_assertions)] use roc_debug_flags::{ ROC_CHECK_MONO_IR, ROC_PRINT_IR_AFTER_DROP_SPECIALIZATION, ROC_PRINT_IR_AFTER_REFCOUNT, - ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION, ROC_PRINT_LOAD_LOG, + ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION, ROC_PRINT_IR_AFTER_TRMC, + ROC_PRINT_LOAD_LOG, }; use roc_derive::SharedDerivedModule; use roc_error_macros::internal_error; @@ -31,8 +38,8 @@ use roc_module::symbol::{ PackageQualified, Symbol, }; use roc_mono::ir::{ - CapturedSymbols, ExternalSpecializations, GlueLayouts, LambdaSetId, PartialProc, Proc, - ProcLayout, Procs, ProcsBase, UpdateModeIds, UsageTrackingMap, + CapturedSymbols, ExternalSpecializations, GlueLayouts, PartialProc, Proc, ProcLayout, Procs, + ProcsBase, UpdateModeIds, UsageTrackingMap, }; use roc_mono::layout::LayoutInterner; use roc_mono::layout::{ @@ -42,13 +49,12 @@ use roc_mono::reset_reuse; use roc_mono::{drop_specialization, inc_dec}; use roc_packaging::cache::RocCacheDir; use roc_parse::ast::{ - self, CommentOrNewline, Defs, Expr, ExtractSpaces, Pattern, Spaced, StrLiteral, TypeAnnotation, - ValueDef, + self, CommentOrNewline, Expr, ExtractSpaces, Pattern, Spaced, StrLiteral, ValueDef, }; use roc_parse::header::{ - ExposedName, ImportsEntry, PackageEntry, PackageHeader, PlatformHeader, To, TypedIdent, + ExposedName, HeaderType, ImportsEntry, PackageEntry, PackageHeader, PlatformHeader, To, + TypedIdent, }; -use roc_parse::header::{HeaderType, PackageName}; use roc_parse::module::module_defs; use roc_parse::parser::{FileError, Parser, SourceError, SyntaxError}; use roc_problem::Severity; @@ -56,7 +62,7 @@ use roc_region::all::{LineInfo, Loc, Region}; #[cfg(not(target_family = "wasm"))] use roc_reporting::report::to_https_problem_report_string; use roc_reporting::report::{to_file_problem_report_string, Palette, RenderTarget}; -use roc_solve::module::{extract_module_owned_implementations, Solved, SolvedModule}; +use roc_solve::module::{extract_module_owned_implementations, SolveConfig, Solved, SolvedModule}; use roc_solve_problem::TypeError; use roc_target::TargetInfo; use roc_types::subs::{CopiedImport, ExposedTypesStorageSubs, Subs, VarStore, Variable}; @@ -135,101 +141,6 @@ impl ExecutionMode { } } -/// Struct storing various intermediate stages by their ModuleId -#[derive(Debug)] -struct ModuleCache<'a> { - module_names: MutMap>, - - /// Phases - headers: MutMap>, - parsed: MutMap>, - aliases: MutMap>, - pending_abilities: MutMap, - constrained: MutMap, - typechecked: MutMap>, - found_specializations: MutMap>, - late_specializations: MutMap>, - external_specializations_requested: MutMap>>, - - /// Various information - imports: MutMap>, - top_level_thunks: MutMap>, - documentation: VecMap, - can_problems: MutMap>, - type_problems: MutMap>, - - sources: MutMap, -} - -impl<'a> ModuleCache<'a> { - fn has_can_errors(&self) -> bool { - self.can_problems - .values() - .flatten() - .any(|problem| problem.severity() == Severity::RuntimeError) - } - - fn has_type_errors(&self) -> bool { - self.type_problems - .values() - .flatten() - .any(|problem| problem.severity() == Severity::RuntimeError) - } - - pub fn has_errors(&self) -> bool { - self.has_can_errors() || self.has_type_errors() - } -} - -impl Default for ModuleCache<'_> { - fn default() -> Self { - let mut module_names = MutMap::default(); - - macro_rules! insert_builtins { - ($($name:ident,)*) => {$( - module_names.insert( - ModuleId::$name, - PQModuleName::Unqualified(ModuleName::from(ModuleName::$name)), - ); - )*} - } - - insert_builtins! { - RESULT, - LIST, - STR, - DICT, - SET, - BOOL, - NUM, - BOX, - ENCODE, - DECODE, - HASH, - JSON, - } - - Self { - module_names, - headers: Default::default(), - parsed: Default::default(), - aliases: Default::default(), - pending_abilities: Default::default(), - constrained: Default::default(), - typechecked: Default::default(), - found_specializations: Default::default(), - late_specializations: Default::default(), - external_specializations_requested: Default::default(), - imports: Default::default(), - top_level_thunks: Default::default(), - documentation: Default::default(), - can_problems: Default::default(), - type_problems: Default::default(), - sources: Default::default(), - } - } -} - type SharedIdentIdsByModule = Arc>; fn start_phase<'a>( @@ -264,7 +175,7 @@ fn start_phase<'a>( match opt_dep_name { None => { - panic!("Module {:?} is not in module_cache.module_names", module_id) + panic!("Module {module_id:?} is not in module_cache.module_names") } Some(dep_name) => { let module_name = dep_name.clone(); @@ -624,157 +535,6 @@ fn start_phase<'a>( vec![task] } -#[derive(Debug)] -pub struct LoadedModule { - pub module_id: ModuleId, - pub interns: Interns, - pub solved: Solved, - pub can_problems: MutMap>, - pub type_problems: MutMap>, - pub declarations_by_id: MutMap, - pub exposed_to_host: MutMap, - pub dep_idents: IdentIdsByModule, - pub exposed_aliases: MutMap, - pub exposed_values: Vec, - pub exposed_types_storage: ExposedTypesStorageSubs, - pub resolved_implementations: ResolvedImplementations, - pub sources: MutMap)>, - pub timings: MutMap, - pub docs_by_module: VecMap, - pub abilities_store: AbilitiesStore, -} - -impl LoadedModule { - pub fn total_problems(&self) -> usize { - let mut total = 0; - - for problems in self.can_problems.values() { - total += problems.len(); - } - - for problems in self.type_problems.values() { - total += problems.len(); - } - - total - } - - pub fn exposed_values_str(&self) -> Vec<&str> { - self.exposed_values - .iter() - .map(|symbol| symbol.as_str(&self.interns)) - .collect() - } - - pub fn exposed_aliases_str(&self) -> Vec<&str> { - self.exposed_aliases - .keys() - .map(|symbol| symbol.as_str(&self.interns)) - .collect() - } -} - -#[derive(Debug)] -pub enum BuildProblem<'a> { - FileNotFound(&'a Path), -} - -#[derive(Debug)] -struct ModuleHeader<'a> { - module_id: ModuleId, - module_path: PathBuf, - is_root_module: bool, - exposed_ident_ids: IdentIds, - deps_by_name: MutMap, ModuleId>, - packages: MutMap<&'a str, PackageName<'a>>, - imported_modules: MutMap, - package_qualified_imported_modules: MutSet>, - exposes: Vec, - exposed_imports: MutMap, - parse_state: roc_parse::state::State<'a>, - header_type: HeaderType<'a>, - header_comments: &'a [CommentOrNewline<'a>], - symbols_from_requires: Vec<(Loc, Loc>)>, - module_timing: ModuleTiming, - defined_values: Vec>, -} - -#[derive(Debug)] -struct ConstrainedModule { - module: Module, - declarations: Declarations, - imported_modules: MutMap, - constraints: Constraints, - constraint: ConstraintSoa, - ident_ids: IdentIds, - var_store: VarStore, - dep_idents: IdentIdsByModule, - module_timing: ModuleTiming, - types: Types, - // Rather than adding pending derives as constraints, hand them directly to solve because they - // must be solved at the end of a module. - pending_derives: PendingDerives, -} - -#[derive(Debug)] -pub struct TypeCheckedModule<'a> { - pub module_id: ModuleId, - pub layout_cache: LayoutCache<'a>, - pub module_timing: ModuleTiming, - pub solved_subs: Solved, - pub decls: Declarations, - pub ident_ids: IdentIds, - pub abilities_store: AbilitiesStore, - pub expectations: Option, -} - -#[derive(Debug)] -struct FoundSpecializationsModule<'a> { - ident_ids: IdentIds, - layout_cache: LayoutCache<'a>, - procs_base: ProcsBase<'a>, - subs: Subs, - module_timing: ModuleTiming, - abilities_store: AbilitiesStore, - expectations: Option, -} - -#[derive(Debug)] -struct LateSpecializationsModule<'a> { - ident_ids: IdentIds, - subs: Subs, - module_timing: ModuleTiming, - layout_cache: LayoutCache<'a>, - procs_base: ProcsBase<'a>, - expectations: Option, -} - -#[derive(Debug, Default)] -pub struct ToplevelExpects { - pub pure: VecMap, - pub fx: VecMap, -} - -#[derive(Debug)] -pub struct MonomorphizedModule<'a> { - pub module_id: ModuleId, - pub interns: Interns, - pub subs: Subs, - pub layout_interner: STLayoutInterner<'a>, - pub output_path: Box, - pub can_problems: MutMap>, - pub type_problems: MutMap>, - pub procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>, - pub toplevel_expects: ToplevelExpects, - pub entry_point: EntryPoint<'a>, - pub exposed_to_host: ExposedToHost, - pub sources: MutMap)>, - pub timings: MutMap, - pub expectations: VecMap, - pub uses_prebuilt_platform: bool, - pub glue_layouts: GlueLayouts<'a>, -} - /// Values used to render expect output pub struct ExpectMetadata<'a> { pub interns: Interns, @@ -782,67 +542,6 @@ pub struct ExpectMetadata<'a> { pub expectations: VecMap, } -#[derive(Debug)] -pub enum EntryPoint<'a> { - Executable { - exposed_to_host: &'a [(Symbol, ProcLayout<'a>)], - platform_path: PathBuf, - }, - Test, -} - -#[derive(Debug)] -pub struct Expectations { - pub subs: roc_types::subs::Subs, - pub path: PathBuf, - pub expectations: VecMap>, - pub dbgs: VecMap, - pub ident_ids: IdentIds, -} - -#[derive(Clone, Debug, Default)] -pub struct ExposedToHost { - /// usually `mainForHost` - pub top_level_values: MutMap, - /// exposed closure types, typically `Fx` - pub closure_types: Vec, - /// lambda_sets - pub lambda_sets: Vec<(Symbol, LambdaSetId)>, - pub getters: Vec, -} - -impl<'a> MonomorphizedModule<'a> { - pub fn total_problems(&self) -> usize { - let mut total = 0; - - for problems in self.can_problems.values() { - total += problems.len(); - } - - for problems in self.type_problems.values() { - total += problems.len(); - } - - total - } -} - -#[derive(Debug)] -struct ParsedModule<'a> { - module_id: ModuleId, - module_path: PathBuf, - src: &'a str, - module_timing: ModuleTiming, - deps_by_name: MutMap, ModuleId>, - imported_modules: MutMap, - exposed_ident_ids: IdentIds, - exposed_imports: MutMap, - parsed_defs: Defs<'a>, - symbols_from_requires: Vec<(Loc, Loc>)>, - header_type: HeaderType<'a>, - header_comments: &'a [CommentOrNewline<'a>], -} - type LocExpects = VecMap>; type LocDbgs = VecMap; @@ -1090,76 +789,6 @@ impl<'a> State<'a> { } } -#[derive(Debug)] -pub struct ModuleTiming { - pub read_roc_file: Duration, - pub parse_header: Duration, - pub parse_body: Duration, - pub canonicalize: Duration, - pub constrain: Duration, - pub solve: Duration, - pub find_specializations: Duration, - // indexed by make specializations pass - pub make_specializations: Vec, - // TODO pub monomorphize: Duration, - /// Total duration will always be more than the sum of the other fields, due - /// to things like state lookups in between phases, waiting on other threads, etc. - start_time: Instant, - end_time: Instant, -} - -impl ModuleTiming { - pub fn new(start_time: Instant) -> Self { - ModuleTiming { - read_roc_file: Duration::default(), - parse_header: Duration::default(), - parse_body: Duration::default(), - canonicalize: Duration::default(), - constrain: Duration::default(), - solve: Duration::default(), - find_specializations: Duration::default(), - make_specializations: Vec::with_capacity(2), - start_time, - end_time: start_time, // just for now; we'll overwrite this at the end - } - } - - pub fn total(&self) -> Duration { - self.end_time.duration_since(self.start_time) - } - - /// Subtract all the other fields from total_start_to_finish - pub fn other(&self) -> Duration { - let Self { - read_roc_file, - parse_header, - parse_body, - canonicalize, - constrain, - solve, - find_specializations, - make_specializations, - start_time, - end_time, - } = self; - - let calculate = |d: Option| -> Option { - make_specializations - .iter() - .fold(d, |d, pass_time| d?.checked_sub(*pass_time))? - .checked_sub(*find_specializations)? - .checked_sub(*solve)? - .checked_sub(*constrain)? - .checked_sub(*canonicalize)? - .checked_sub(*parse_body)? - .checked_sub(*parse_header)? - .checked_sub(*read_roc_file) - }; - - calculate(Some(end_time.duration_since(*start_time))).unwrap_or_default() - } -} - fn report_timing( buf: &mut impl std::fmt::Write, label: &str, @@ -1191,11 +820,11 @@ impl std::fmt::Display for ModuleTiming { let multiple_make_specializations_passes = module_timing.make_specializations.len() > 1; for (i, pass_time) in module_timing.make_specializations.iter().enumerate() { let suffix = if multiple_make_specializations_passes { - format!(" (Pass {})", i) + format!(" (Pass {i})") } else { String::new() }; - report_timing(f, &format!("Make Specializations{}", suffix), *pass_time)?; + report_timing(f, &format!("Make Specializations{suffix}"), *pass_time)?; } report_timing(f, "Other", module_timing.other())?; f.write_str("\n")?; @@ -2237,7 +1866,7 @@ fn worker_task<'a>( ">>> {}", match &task { BuildTask::LoadModule { module_name, .. } => { - format!("BuildTask::LoadModule({:?})", module_name) + format!("BuildTask::LoadModule({module_name:?})") } BuildTask::Parse { header } => { format!("BuildTask::Parse({})", header.module_path.display()) @@ -2250,10 +1879,10 @@ fn worker_task<'a>( format!("BuildTask::Solve({:?})", module.module_id) } BuildTask::BuildPendingSpecializations { module_id, .. } => { - format!("BuildTask::BuildPendingSpecializations({:?})", module_id) + format!("BuildTask::BuildPendingSpecializations({module_id:?})") } BuildTask::MakeSpecializations { module_id, .. } => { - format!("BuildTask::MakeSpecializations({:?})", module_id) + format!("BuildTask::MakeSpecializations({module_id:?})") } } ); @@ -3104,6 +2733,16 @@ fn update<'a>( let ident_ids = state.constrained_ident_ids.get_mut(&module_id).unwrap(); + roc_mono::tail_recursion::apply_trmc( + arena, + &mut layout_interner, + module_id, + ident_ids, + &mut state.procedures, + ); + + debug_print_ir!(state, &layout_interner, ROC_PRINT_IR_AFTER_TRMC); + inc_dec::insert_inc_dec_operations( arena, &layout_interner, @@ -3130,6 +2769,7 @@ fn update<'a>( arena, &layout_interner, module_id, + state.target_info, ident_ids, &mut update_mode_ids, &mut state.procedures, @@ -3608,8 +3248,7 @@ fn load_package_from_disk<'a>( }, _parse_state, )) => Err(LoadingProblem::UnexpectedHeader(format!( - "expected platform/package module, got Interface with header\n{:?}", - header + "expected platform/package module, got Interface with header\n{header:?}" ))), Ok(( ast::Module { @@ -3618,8 +3257,7 @@ fn load_package_from_disk<'a>( }, _parse_state, )) => Err(LoadingProblem::UnexpectedHeader(format!( - "expected platform/package module, got Hosted module with header\n{:?}", - header + "expected platform/package module, got Hosted module with header\n{header:?}" ))), Ok(( ast::Module { @@ -3628,8 +3266,7 @@ fn load_package_from_disk<'a>( }, _parse_state, )) => Err(LoadingProblem::UnexpectedHeader(format!( - "expected platform/package module, got App with header\n{:?}", - header + "expected platform/package module, got App with header\n{header:?}" ))), Ok(( ast::Module { @@ -3762,10 +3399,7 @@ fn load_builtin_module_help<'a>( (info, parse_state) } Ok(_) => panic!("invalid header format for builtin module"), - Err(e) => panic!( - "Hit a parse error in the header of {:?}:\n{:?}", - filename, e - ), + Err(e) => panic!("Hit a parse error in the header of {filename:?}:\n{e:?}"), } } @@ -5061,6 +4695,14 @@ fn import_variable_for_symbol( } } +struct SolveResult { + solved: Solved, + solved_implementations: ResolvedImplementations, + exposed_vars_by_symbol: Vec<(Symbol, Variable)>, + problems: Vec, + abilities_store: AbilitiesStore, +} + #[allow(clippy::complexity)] fn run_solve_solve( exposed_for_module: ExposedForModule, @@ -5071,13 +4713,7 @@ fn run_solve_solve( var_store: VarStore, module: Module, derived_module: SharedDerivedModule, -) -> ( - Solved, - ResolvedImplementations, - Vec<(Symbol, Variable)>, - Vec, - AbilitiesStore, -) { +) -> SolveResult { let Module { exposed_symbols, aliases, @@ -5111,30 +4747,34 @@ fn run_solve_solve( &import_variables, ); - let mut solve_aliases = roc_solve::solve::Aliases::with_capacity(aliases.len()); + let mut solve_aliases = roc_solve::Aliases::with_capacity(aliases.len()); for (name, (_, alias)) in aliases.iter() { solve_aliases.insert(&mut types, *name, alias.clone()); } - let (solved_subs, solved_implementations, exposed_vars_by_symbol, problems, abilities_store) = { + let (solve_output, solved_implementations, exposed_vars_by_symbol) = { let module_id = module.module_id; - let (solved_subs, solved_env, problems, abilities_store) = roc_solve::module::run_solve( - module_id, + let solve_config = SolveConfig { + home: module_id, types, - &constraints, - actual_constraint, + constraints: &constraints, + root_constraint: actual_constraint, + pending_derives, + exposed_by_module: &exposed_for_module.exposed_by_module, + derived_module, + }; + + let solve_output = roc_solve::module::run_solve( + solve_config, rigid_variables, subs, solve_aliases, abilities_store, - pending_derives, - &exposed_for_module.exposed_by_module, - derived_module, ); let solved_implementations = - extract_module_owned_implementations(module_id, &abilities_store); + extract_module_owned_implementations(module_id, &solve_output.resolved_abilities_store); let is_specialization_symbol = |sym| { solved_implementations @@ -5147,7 +4787,8 @@ fn run_solve_solve( // Expose anything that is explicitly exposed by the header, or is a specialization of an // ability. - let exposed_vars_by_symbol: Vec<_> = solved_env + let exposed_vars_by_symbol: Vec<_> = solve_output + .scope .vars_by_symbol() .filter(|(k, _)| { exposed_symbols.contains(k) @@ -5156,22 +4797,23 @@ fn run_solve_solve( }) .collect(); - ( - solved_subs, - solved_implementations, - exposed_vars_by_symbol, - problems, - abilities_store, - ) + (solve_output, solved_implementations, exposed_vars_by_symbol) }; - ( - solved_subs, + let roc_solve::module::SolveOutput { + subs, + scope: _, + errors, + resolved_abilities_store, + } = solve_output; + + SolveResult { + solved: subs, solved_implementations, exposed_vars_by_symbol, - problems, - abilities_store, - ) + problems: errors, + abilities_store: resolved_abilities_store, + } } fn run_solve<'a>( @@ -5201,7 +4843,7 @@ fn run_solve<'a>( let loc_dbgs = std::mem::take(&mut module.loc_dbgs); let module = module; - let (solved_subs, solved_implementations, exposed_vars_by_symbol, problems, abilities_store) = { + let solve_result = { if module_id.is_builtin() { match cached_types.lock().remove(&module_id) { None => run_solve_solve( @@ -5219,13 +4861,13 @@ fn run_solve<'a>( exposed_vars_by_symbol, abilities, solved_implementations, - }) => ( - Solved(subs), + }) => SolveResult { + solved: Solved(subs), solved_implementations, exposed_vars_by_symbol, - vec![], - abilities, - ), + problems: vec![], + abilities_store: abilities, + }, } } else { run_solve_solve( @@ -5241,7 +4883,14 @@ fn run_solve<'a>( } }; - let mut solved_subs = solved_subs; + let SolveResult { + solved: mut solved_subs, + solved_implementations, + exposed_vars_by_symbol, + problems, + abilities_store, + } = solve_result; + let exposed_types = roc_solve::module::exposed_types_storage_subs( module_id, &mut solved_subs, @@ -5817,7 +5466,7 @@ fn make_specializations<'a>( ); let external_specializations_requested = procs.externals_we_need.clone(); - let (procedures, restored_procs_base) = procs.get_specialized_procs_without_rc(&mut mono_env); + let (procedures, restored_procs_base) = procs.get_specialized_procs_without_rc(); // Turn `Bytes.Decode.IdentId(238)` into `Bytes.Decode.238`, we rely on this in mono tests mono_env.home.register_debug_idents(mono_env.ident_ids); @@ -5928,8 +5577,7 @@ fn build_pending_specializations<'a>( } LayoutProblem::UnresolvedTypeVar(v) => { let message = format!( - "top level function has unresolved type variable {:?}", - v + "top level function has unresolved type variable {v:?}" ); procs_base .runtime_errors @@ -6009,8 +5657,7 @@ fn build_pending_specializations<'a>( } LayoutProblem::UnresolvedTypeVar(v) => { let message = format!( - "top level function has unresolved type variable {:?}", - v + "top level function has unresolved type variable {v:?}" ); procs_base .runtime_errors @@ -6047,14 +5694,13 @@ fn build_pending_specializations<'a>( use roc_can::pattern::Pattern; let symbol = match &loc_pattern.value { Pattern::Identifier(_) => { - debug_assert!(false, "identifier ended up in Destructure {:?}", symbol); + debug_assert!(false, "identifier ended up in Destructure {symbol:?}"); symbol } Pattern::AbilityMemberSpecialization { ident, specializes } => { debug_assert!( false, - "ability member ended up in Destructure {:?} specializes {:?}", - ident, specializes + "ability member ended up in Destructure {ident:?} specializes {specializes:?}" ); symbol } @@ -6086,8 +5732,7 @@ fn build_pending_specializations<'a>( } LayoutProblem::UnresolvedTypeVar(v) => { let message = format!( - "top level function has unresolved type variable {:?}", - v + "top level function has unresolved type variable {v:?}" ); procs_base .runtime_errors @@ -6153,8 +5798,7 @@ fn build_pending_specializations<'a>( } LayoutProblem::UnresolvedTypeVar(v) => { let message = format!( - "top level function has unresolved type variable {:?}", - v + "top level function has unresolved type variable {v:?}" ); procs_base .runtime_errors @@ -6226,8 +5870,7 @@ fn build_pending_specializations<'a>( } LayoutProblem::UnresolvedTypeVar(v) => { let message = format!( - "top level function has unresolved type variable {:?}", - v + "top level function has unresolved type variable {v:?}" ); procs_base .runtime_errors diff --git a/crates/compiler/load_internal/src/lib.rs b/crates/compiler/load_internal/src/lib.rs index 5aca83faa41..6bdb0cabbd7 100644 --- a/crates/compiler/load_internal/src/lib.rs +++ b/crates/compiler/load_internal/src/lib.rs @@ -6,6 +6,8 @@ use roc_module::symbol::ModuleId; pub mod docs; pub mod file; +pub mod module; +mod module_cache; mod work; #[cfg(target_family = "wasm")] diff --git a/crates/compiler/load_internal/src/module.rs b/crates/compiler/load_internal/src/module.rs new file mode 100644 index 00000000000..8eb1b29d4cb --- /dev/null +++ b/crates/compiler/load_internal/src/module.rs @@ -0,0 +1,306 @@ +use crate::docs::ModuleDocumentation; +use roc_can::constraint::{Constraint as ConstraintSoa, Constraints}; +use roc_can::expr::{DbgLookup, ExpectLookup}; +use roc_can::{ + abilities::AbilitiesStore, + expr::{Declarations, PendingDerives}, + module::{Module, ResolvedImplementations}, +}; +use roc_collections::{MutMap, MutSet, VecMap}; +use roc_module::ident::Ident; +use roc_module::symbol::{ + IdentIds, IdentIdsByModule, Interns, ModuleId, PQModuleName, PackageQualified, Symbol, +}; +use roc_mono::ir::{GlueLayouts, LambdaSetId, Proc, ProcLayout, ProcsBase}; +use roc_mono::layout::{LayoutCache, STLayoutInterner}; +use roc_parse::ast::{CommentOrNewline, Defs, TypeAnnotation, ValueDef}; +use roc_parse::header::{HeaderType, PackageName}; +use roc_region::all::{Loc, Region}; +use roc_solve::module::Solved; +use roc_solve_problem::TypeError; +use roc_types::subs::{ExposedTypesStorageSubs, Subs, VarStore, Variable}; +use roc_types::types::{Alias, Types}; +use std::path::Path; +use std::path::PathBuf; + +#[cfg(target_family = "wasm")] +use crate::wasm_instant::{Duration, Instant}; +#[cfg(not(target_family = "wasm"))] +use std::time::{Duration, Instant}; + +#[derive(Debug)] +pub struct LoadedModule { + pub module_id: ModuleId, + pub interns: Interns, + pub solved: Solved, + pub can_problems: MutMap>, + pub type_problems: MutMap>, + pub declarations_by_id: MutMap, + pub exposed_to_host: MutMap, + pub dep_idents: IdentIdsByModule, + pub exposed_aliases: MutMap, + pub exposed_values: Vec, + pub exposed_types_storage: ExposedTypesStorageSubs, + pub resolved_implementations: ResolvedImplementations, + pub sources: MutMap)>, + pub timings: MutMap, + pub docs_by_module: VecMap, + pub abilities_store: AbilitiesStore, +} + +impl LoadedModule { + pub fn total_problems(&self) -> usize { + let mut total = 0; + + for problems in self.can_problems.values() { + total += problems.len(); + } + + for problems in self.type_problems.values() { + total += problems.len(); + } + + total + } + + pub fn exposed_values_str(&self) -> Vec<&str> { + self.exposed_values + .iter() + .map(|symbol| symbol.as_str(&self.interns)) + .collect() + } + + pub fn exposed_aliases_str(&self) -> Vec<&str> { + self.exposed_aliases + .keys() + .map(|symbol| symbol.as_str(&self.interns)) + .collect() + } +} + +#[derive(Debug)] +pub(crate) struct ModuleHeader<'a> { + pub(crate) module_id: ModuleId, + pub(crate) module_path: PathBuf, + pub(crate) is_root_module: bool, + pub(crate) exposed_ident_ids: IdentIds, + pub(crate) deps_by_name: MutMap, ModuleId>, + pub(crate) packages: MutMap<&'a str, PackageName<'a>>, + pub(crate) imported_modules: MutMap, + pub(crate) package_qualified_imported_modules: MutSet>, + pub(crate) exposes: Vec, + pub(crate) exposed_imports: MutMap, + pub(crate) parse_state: roc_parse::state::State<'a>, + pub(crate) header_type: HeaderType<'a>, + pub(crate) header_comments: &'a [CommentOrNewline<'a>], + pub(crate) symbols_from_requires: Vec<(Loc, Loc>)>, + pub(crate) module_timing: ModuleTiming, + pub(crate) defined_values: Vec>, +} + +#[derive(Debug)] +pub(crate) struct ConstrainedModule { + pub(crate) module: Module, + pub(crate) declarations: Declarations, + pub(crate) imported_modules: MutMap, + pub(crate) constraints: Constraints, + pub(crate) constraint: ConstraintSoa, + pub(crate) ident_ids: IdentIds, + pub(crate) var_store: VarStore, + pub(crate) dep_idents: IdentIdsByModule, + pub(crate) module_timing: ModuleTiming, + pub(crate) types: Types, + // Rather than adding pending derives as constraints, hand them directly to solve because they + // must be solved at the end of a module. + pub(crate) pending_derives: PendingDerives, +} + +#[derive(Debug)] +pub struct TypeCheckedModule<'a> { + pub module_id: ModuleId, + pub layout_cache: LayoutCache<'a>, + pub module_timing: ModuleTiming, + pub solved_subs: Solved, + pub decls: Declarations, + pub ident_ids: IdentIds, + pub abilities_store: AbilitiesStore, + pub expectations: Option, +} + +#[derive(Debug)] +pub(crate) struct FoundSpecializationsModule<'a> { + pub(crate) ident_ids: IdentIds, + pub(crate) layout_cache: LayoutCache<'a>, + pub(crate) procs_base: ProcsBase<'a>, + pub(crate) subs: Subs, + pub(crate) module_timing: ModuleTiming, + pub(crate) abilities_store: AbilitiesStore, + pub(crate) expectations: Option, +} + +#[derive(Debug)] +pub(crate) struct LateSpecializationsModule<'a> { + pub(crate) ident_ids: IdentIds, + pub(crate) subs: Subs, + pub(crate) module_timing: ModuleTiming, + pub(crate) layout_cache: LayoutCache<'a>, + pub(crate) procs_base: ProcsBase<'a>, + pub(crate) expectations: Option, +} + +#[derive(Debug, Default)] +pub struct ToplevelExpects { + pub pure: VecMap, + pub fx: VecMap, +} + +#[derive(Debug)] +pub struct MonomorphizedModule<'a> { + pub module_id: ModuleId, + pub interns: Interns, + pub subs: Subs, + pub layout_interner: STLayoutInterner<'a>, + pub output_path: Box, + pub can_problems: MutMap>, + pub type_problems: MutMap>, + pub procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>, + pub toplevel_expects: ToplevelExpects, + pub entry_point: EntryPoint<'a>, + pub exposed_to_host: ExposedToHost, + pub sources: MutMap)>, + pub timings: MutMap, + pub expectations: VecMap, + pub uses_prebuilt_platform: bool, + pub glue_layouts: GlueLayouts<'a>, +} + +#[derive(Debug)] +pub struct ParsedModule<'a> { + pub module_id: ModuleId, + pub module_path: PathBuf, + pub src: &'a str, + pub module_timing: ModuleTiming, + pub deps_by_name: MutMap, ModuleId>, + pub imported_modules: MutMap, + pub exposed_ident_ids: IdentIds, + pub exposed_imports: MutMap, + pub parsed_defs: Defs<'a>, + pub symbols_from_requires: Vec<(Loc, Loc>)>, + pub header_type: HeaderType<'a>, + pub header_comments: &'a [CommentOrNewline<'a>], +} + +#[derive(Debug)] +pub enum EntryPoint<'a> { + Executable { + exposed_to_host: &'a [(Symbol, ProcLayout<'a>)], + platform_path: PathBuf, + }, + Test, +} + +#[derive(Debug)] +pub struct Expectations { + pub subs: roc_types::subs::Subs, + pub path: PathBuf, + pub expectations: VecMap>, + pub dbgs: VecMap, + pub ident_ids: IdentIds, +} + +#[derive(Clone, Debug, Default)] +pub struct ExposedToHost { + /// usually `mainForHost` + pub top_level_values: MutMap, + /// exposed closure types, typically `Fx` + pub closure_types: Vec, + /// lambda_sets + pub lambda_sets: Vec<(Symbol, LambdaSetId)>, + pub getters: Vec, +} + +impl<'a> MonomorphizedModule<'a> { + pub fn total_problems(&self) -> usize { + let mut total = 0; + + for problems in self.can_problems.values() { + total += problems.len(); + } + + for problems in self.type_problems.values() { + total += problems.len(); + } + + total + } +} + +#[derive(Debug)] +pub struct ModuleTiming { + pub read_roc_file: Duration, + pub parse_header: Duration, + pub parse_body: Duration, + pub canonicalize: Duration, + pub constrain: Duration, + pub solve: Duration, + pub find_specializations: Duration, + // indexed by make specializations pass + pub make_specializations: Vec, + // TODO pub monomorphize: Duration, + /// Total duration will always be more than the sum of the other fields, due + /// to things like state lookups in between phases, waiting on other threads, etc. + pub start_time: Instant, + pub end_time: Instant, +} + +impl ModuleTiming { + pub fn new(start_time: Instant) -> Self { + ModuleTiming { + read_roc_file: Duration::default(), + parse_header: Duration::default(), + parse_body: Duration::default(), + canonicalize: Duration::default(), + constrain: Duration::default(), + solve: Duration::default(), + find_specializations: Duration::default(), + make_specializations: Vec::with_capacity(2), + start_time, + end_time: start_time, // just for now; we'll overwrite this at the end + } + } + + pub fn total(&self) -> Duration { + self.end_time.duration_since(self.start_time) + } + + /// Subtract all the other fields from total_start_to_finish + pub fn other(&self) -> Duration { + let Self { + read_roc_file, + parse_header, + parse_body, + canonicalize, + constrain, + solve, + find_specializations, + make_specializations, + start_time, + end_time, + } = self; + + let calculate = |d: Option| -> Option { + make_specializations + .iter() + .fold(d, |d, pass_time| d?.checked_sub(*pass_time))? + .checked_sub(*find_specializations)? + .checked_sub(*solve)? + .checked_sub(*constrain)? + .checked_sub(*canonicalize)? + .checked_sub(*parse_body)? + .checked_sub(*parse_header)? + .checked_sub(*read_roc_file) + }; + + calculate(Some(end_time.duration_since(*start_time))).unwrap_or_default() + } +} diff --git a/crates/compiler/load_internal/src/module_cache.rs b/crates/compiler/load_internal/src/module_cache.rs new file mode 100644 index 00000000000..ec2bea79f79 --- /dev/null +++ b/crates/compiler/load_internal/src/module_cache.rs @@ -0,0 +1,110 @@ +use crate::docs::ModuleDocumentation; +use crate::module::{ + ConstrainedModule, FoundSpecializationsModule, LateSpecializationsModule, ModuleHeader, + ParsedModule, TypeCheckedModule, +}; +use roc_can::abilities::PendingAbilitiesStore; +use roc_collections::{MutMap, MutSet, VecMap}; +use roc_module::ident::ModuleName; +use roc_module::symbol::{ModuleId, PQModuleName, Symbol}; +use roc_mono::ir::ExternalSpecializations; +use roc_problem::Severity; +use roc_solve_problem::TypeError; +use roc_types::types::Alias; +use std::path::PathBuf; + +/// Struct storing various intermediate stages by their ModuleId +#[derive(Debug)] +pub(crate) struct ModuleCache<'a> { + pub(crate) module_names: MutMap>, + + /// Phases + pub(crate) headers: MutMap>, + pub(crate) parsed: MutMap>, + pub(crate) aliases: MutMap>, + pub(crate) pending_abilities: MutMap, + pub(crate) constrained: MutMap, + pub(crate) typechecked: MutMap>, + pub(crate) found_specializations: MutMap>, + pub(crate) late_specializations: MutMap>, + pub(crate) external_specializations_requested: + MutMap>>, + + /// Various information + pub(crate) imports: MutMap>, + pub(crate) top_level_thunks: MutMap>, + pub(crate) documentation: VecMap, + pub(crate) can_problems: MutMap>, + pub(crate) type_problems: MutMap>, + + pub(crate) sources: MutMap, +} + +impl<'a> ModuleCache<'a> { + pub(crate) fn has_can_errors(&self) -> bool { + self.can_problems + .values() + .flatten() + .any(|problem| problem.severity() == Severity::RuntimeError) + } + + pub(crate) fn has_type_errors(&self) -> bool { + self.type_problems + .values() + .flatten() + .any(|problem| problem.severity() == Severity::RuntimeError) + } + + pub fn has_errors(&self) -> bool { + self.has_can_errors() || self.has_type_errors() + } +} + +impl Default for ModuleCache<'_> { + fn default() -> Self { + let mut module_names = MutMap::default(); + + macro_rules! insert_builtins { + ($($name:ident,)*) => {$( + module_names.insert( + ModuleId::$name, + PQModuleName::Unqualified(ModuleName::from(ModuleName::$name)), + ); + )*} + } + + insert_builtins! { + RESULT, + LIST, + STR, + DICT, + SET, + BOOL, + NUM, + BOX, + ENCODE, + DECODE, + HASH, + JSON, + } + + Self { + module_names, + headers: Default::default(), + parsed: Default::default(), + aliases: Default::default(), + pending_abilities: Default::default(), + constrained: Default::default(), + typechecked: Default::default(), + found_specializations: Default::default(), + late_specializations: Default::default(), + external_specializations_requested: Default::default(), + imports: Default::default(), + top_level_thunks: Default::default(), + documentation: Default::default(), + can_problems: Default::default(), + type_problems: Default::default(), + sources: Default::default(), + } + } +} diff --git a/crates/compiler/load_internal/src/work.rs b/crates/compiler/load_internal/src/work.rs index 27712ebb983..fb443c7f035 100644 --- a/crates/compiler/load_internal/src/work.rs +++ b/crates/compiler/load_internal/src/work.rs @@ -67,8 +67,7 @@ impl MakeSpecializationsDependents { let entry = self.entry(module_id); debug_assert!( entry.succ.is_empty(), - "already added successors for module '{:?}'", - module_id + "already added successors for module '{module_id:?}'" ); entry.succ.extend(succ.into_iter()); @@ -516,8 +515,7 @@ impl<'a> Dependencies<'a> { debug_assert_eq!( make_specializations_dependents.0.len(), default_make_specializations_dependents_len, - "more modules were added to the graph: {:?}", - make_specializations_dependents + "more modules were added to the graph: {make_specializations_dependents:?}" ); output @@ -567,8 +565,7 @@ impl<'a> Dependencies<'a> { debug_assert_eq!( make_specializations_dependents.0.len(), default_make_specializations_dependents_len, - "more modules were added to the graph: {:?}", - make_specializations_dependents + "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 b16990c61a6..f788e6aed8a 100644 --- a/crates/compiler/load_internal/tests/test_load.rs +++ b/crates/compiler/load_internal/tests/test_load.rs @@ -17,8 +17,10 @@ mod helpers; use crate::helpers::fixtures_dir; use bumpalo::Bump; use roc_can::module::ExposedByModule; -use roc_load_internal::file::{ExecutionMode, LoadConfig, Threading}; -use roc_load_internal::file::{LoadResult, LoadStart, LoadedModule, LoadingProblem}; +use roc_load_internal::file::{ + ExecutionMode, LoadConfig, LoadResult, LoadStart, LoadingProblem, Threading, +}; +use roc_load_internal::module::LoadedModule; use roc_module::ident::ModuleName; use roc_module::symbol::{Interns, ModuleId}; use roc_packaging::cache::RocCacheDir; @@ -104,9 +106,9 @@ fn multiple_modules(subdir: &str, files: Vec<(&str, &str)>) -> Result panic!("IO trouble: {:?}", io_error), + Err(io_error) => panic!("IO trouble: {io_error:?}"), Ok(Err(LoadingProblem::FormattedReport(buf))) => Err(buf), - Ok(Err(loading_problem)) => Err(format!("{:?}", loading_problem)), + Ok(Err(loading_problem)) => Err(format!("{loading_problem:?}")), Ok(Ok(mut loaded_module)) => { let home = loaded_module.module_id; let (filepath, src) = loaded_module.sources.get(&home).unwrap(); @@ -146,7 +148,7 @@ fn multiple_modules_help<'a>( // Use a deterministic temporary directory. // We can't have all tests use "tmp" because tests run in parallel, // so append the test name to the tmp path. - let tmp = format!("tmp/{}", subdir); + let tmp = format!("tmp/{subdir}"); let dir = roc_test_utils::TmpDir::new(&tmp); let app_module = files.pop().unwrap(); @@ -160,7 +162,7 @@ fn multiple_modules_help<'a>( fs::create_dir_all(file_path.parent().unwrap())?; let mut file = File::create(file_path)?; - writeln!(file, "{}", source)?; + writeln!(file, "{source}")?; file_handles.push(file); } @@ -171,7 +173,7 @@ fn multiple_modules_help<'a>( let file_path = dir.path().join(filename); let full_file_path = file_path.clone(); let mut file = File::create(file_path)?; - writeln!(file, "{}", source)?; + writeln!(file, "{source}")?; file_handles.push(file); load_and_typecheck(arena, full_file_path, Default::default(), TARGET_INFO) @@ -186,16 +188,16 @@ fn load_fixture( subs_by_module: ExposedByModule, ) -> LoadedModule { let src_dir = fixtures_dir().join(dir_name); - let filename = src_dir.join(format!("{}.roc", module_name)); + let filename = src_dir.join(format!("{module_name}.roc")); let arena = Bump::new(); 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)) => { - println!("{}", report); + println!("{report}"); panic!("{}", report); } - Err(e) => panic!("{:?}", e), + Err(e) => panic!("{e:?}"), }; let home = loaded_module.module_id; @@ -256,7 +258,7 @@ fn expect_types(mut loaded_module: LoadedModule, mut expected_types: HashMap<&st let expected_type = expected_types .remove(fully_qualified.as_str()) .unwrap_or_else(|| { - panic!("Defs included an unexpected symbol: {:?}", fully_qualified) + panic!("Defs included an unexpected symbol: {fully_qualified:?}") }); assert_eq!((&symbol, expected_type), (&symbol, actual_str.as_str())); @@ -271,7 +273,7 @@ fn expect_types(mut loaded_module: LoadedModule, mut expected_types: HashMap<&st let expected_type = expected_types .remove(fully_qualified.as_str()) .unwrap_or_else(|| { - panic!("Defs included an unexpected symbol: {:?}", fully_qualified) + panic!("Defs included an unexpected symbol: {fully_qualified:?}") }); assert_eq!((&symbol, expected_type), (&symbol, actual_str.as_str())); @@ -703,8 +705,7 @@ fn platform_does_not_exist() { // assert!(report.contains("FILE NOT FOUND"), "report=({})", report); assert!( report.contains("zzz-does-not-exist/main.roc"), - "report=({})", - report + "report=({report})" ); } Ok(_) => unreachable!("we expect failure here"), diff --git a/crates/compiler/module/src/low_level.rs b/crates/compiler/module/src/low_level.rs index 32e8e162dc9..bca0692c50c 100644 --- a/crates/compiler/module/src/low_level.rs +++ b/crates/compiler/module/src/low_level.rs @@ -118,7 +118,10 @@ pub enum LowLevel { Not, Hash, PtrCast, - PtrWrite, + PtrStore, + PtrLoad, + PtrClearTagId, + Alloca, RefCountIncRcPtr, RefCountDecRcPtr, RefCountIncDataPtr, @@ -228,7 +231,10 @@ macro_rules! map_symbol_to_lowlevel { // these are used internally and not tied to a symbol LowLevel::Hash => unimplemented!(), LowLevel::PtrCast => unimplemented!(), - LowLevel::PtrWrite => unimplemented!(), + LowLevel::PtrStore => unimplemented!(), + LowLevel::PtrLoad => unimplemented!(), + LowLevel::PtrClearTagId => unimplemented!(), + LowLevel::Alloca => unimplemented!(), LowLevel::RefCountIncRcPtr => unimplemented!(), LowLevel::RefCountDecRcPtr=> unimplemented!(), LowLevel::RefCountIncDataPtr => unimplemented!(), diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index 474397654c8..0ef28252777 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -172,7 +172,7 @@ impl Symbol { #[cfg(debug_assertions)] pub fn contains(self, needle: &str) -> bool { - format!("{:?}", self).contains(needle) + format!("{self:?}").contains(needle) } } @@ -194,7 +194,7 @@ impl fmt::Debug for Symbol { match DEBUG_IDENT_IDS_BY_MODULE_ID.lock() { Ok(names) => match &names.get(&(module_id.to_zero_indexed() as u32)) { Some(ident_ids) => match ident_ids.get_name(ident_id) { - Some(ident_str) => write!(f, "`{:?}.{}`", module_id, ident_str), + Some(ident_str) => write!(f, "`{module_id:?}.{ident_str}`"), None => fallback_debug_fmt(*self, f), }, None => fallback_debug_fmt(*self, f), @@ -207,7 +207,7 @@ impl fmt::Debug for Symbol { use std::io::Write; let mut stderr = std::io::stderr(); - writeln!(stderr, "DEBUG INFO: Failed to acquire lock for Debug reading from DEBUG_IDENT_IDS_BY_MODULE_ID, presumably because a thread panicked: {:?}", err).unwrap(); + writeln!(stderr, "DEBUG INFO: Failed to acquire lock for Debug reading from DEBUG_IDENT_IDS_BY_MODULE_ID, presumably because a thread panicked: {err:?}").unwrap(); fallback_debug_fmt(*self, f) } @@ -229,7 +229,7 @@ impl fmt::Display for Symbol { let ident_id = self.ident_id(); match ident_id { - IdentId(value) => write!(f, "{:?}.{:?}", module_id, value), + IdentId(value) => write!(f, "{module_id:?}.{value:?}"), } } } @@ -244,7 +244,7 @@ fn fallback_debug_fmt(symbol: Symbol, f: &mut fmt::Formatter) -> fmt::Result { let module_id = symbol.module_id(); let ident_id = symbol.ident_id(); - write!(f, "`{:?}.{:?}`", module_id, ident_id) + write!(f, "`{module_id:?}.{ident_id:?}`") } /// This is used in Debug builds only, to let us have a Debug instance @@ -312,8 +312,8 @@ pub fn get_module_ident_ids<'a>( all_ident_ids .get(module_id) .with_context(|| ModuleIdNotFoundSnafu { - module_id: format!("{:?}", module_id), - all_ident_ids: format!("{:?}", all_ident_ids), + module_id: format!("{module_id:?}"), + all_ident_ids: format!("{all_ident_ids:?}"), }) } @@ -324,7 +324,7 @@ pub fn get_module_ident_ids_mut<'a>( all_ident_ids .get_mut(module_id) .with_context(|| ModuleIdNotFoundSnafu { - module_id: format!("{:?}", module_id), + module_id: format!("{module_id:?}"), all_ident_ids: "I could not return all_ident_ids here because of borrowing issues.", }) } @@ -400,7 +400,7 @@ impl fmt::Debug for ModuleId { if PRETTY_PRINT_DEBUG_SYMBOLS { match names.try_get(self.to_zero_indexed()) { - Some(str_ref) => write!(f, "{}", str_ref), + Some(str_ref) => write!(f, "{str_ref}"), None => { internal_error!( "Could not find a Debug name for module ID {} in {:?}", @@ -645,7 +645,7 @@ impl IdentIds { pub fn update_key(&mut self, old_name: &str, new_name: &str) -> Result { match self.interner.find_and_update(old_name, new_name) { Some(index) => Ok(IdentId(index as u32)), - None => Err(format!("The identifier {:?} is not in IdentIds", old_name)), + None => Err(format!("The identifier {old_name:?} is not in IdentIds")), } } @@ -682,7 +682,7 @@ impl IdentIds { self.get_name(ident_id) .with_context(|| IdentIdNotFoundSnafu { ident_id, - ident_ids_str: format!("{:?}", self), + ident_ids_str: format!("{self:?}"), }) } @@ -1471,6 +1471,9 @@ define_builtins! { 22 DICT_LIST_GET_UNSAFE: "listGetUnsafe" 23 DICT_PSEUDO_SEED: "pseudoSeed" + 24 DICT_IS_EMPTY: "isEmpty" + 25 DICT_MAP: "map" + 26 DICT_JOINMAP: "joinMap" } 9 SET: "Set" => { 0 SET_SET: "Set" exposed_type=true // the Set.Set type alias @@ -1490,6 +1493,9 @@ define_builtins! { 14 SET_CONTAINS: "contains" 15 SET_TO_DICT: "toDict" 16 SET_CAPACITY: "capacity" + 17 SET_IS_EMPTY: "isEmpty" + 18 SET_MAP: "map" + 19 SET_JOIN_MAP: "joinMap" } 10 BOX: "Box" => { 0 BOX_BOX_TYPE: "Box" exposed_apply_type=true // the Box.Box opaque type diff --git a/crates/compiler/mono/Cargo.toml b/crates/compiler/mono/Cargo.toml index 44887a5b8f6..2fd6db34d93 100644 --- a/crates/compiler/mono/Cargo.toml +++ b/crates/compiler/mono/Cargo.toml @@ -27,6 +27,7 @@ roc_types = { path = "../types" } ven_pretty = { path = "../../vendor/pretty" } bitvec.workspace = true +arrayvec.workspace = true bumpalo.workspace = true hashbrown.workspace = true parking_lot.workspace = true diff --git a/crates/compiler/mono/src/borrow.rs b/crates/compiler/mono/src/borrow.rs index b5e4dddfaab..4fd9ccc0da9 100644 --- a/crates/compiler/mono/src/borrow.rs +++ b/crates/compiler/mono/src/borrow.rs @@ -680,7 +680,8 @@ impl<'a> BorrowInfState<'a> { // the function must take it as an owned parameter self.own_args_if_param(&xs); } - Tag { arguments: xs, .. } | Struct(xs) => { + + Struct(xs) => { self.own_var(z); // if the used symbol is an argument to the current function, @@ -688,35 +689,25 @@ impl<'a> BorrowInfState<'a> { self.own_args_if_param(xs); } - ExprBox { symbol: x } => { + Tag { + arguments: xs, + reuse, + .. + } => { + debug_assert!(reuse.is_none()); + self.own_var(z); // if the used symbol is an argument to the current function, // the function must take it as an owned parameter - self.own_args_if_param(&[*x]); - } - - ExprUnbox { symbol: x } => { - // if the boxed value is owned, the box is - self.if_is_owned_then_own(*x, z); - - // if the extracted value is owned, the structure must be too - self.if_is_owned_then_own(z, *x); + self.own_args_if_param(xs); } Reset { symbol: x, .. } | ResetRef { symbol: x, .. } => { self.own_var(z); self.own_var(*x); } - Reuse { - symbol: x, - arguments: ys, - .. - } => { - self.own_var(z); - self.own_var(*x); - self.own_args_if_param(ys); - } + EmptyArray => { self.own_var(z); } @@ -741,6 +732,14 @@ impl<'a> BorrowInfState<'a> { self.if_is_owned_then_own(z, *x); } + UnionFieldPtrAtIndex { structure: x, .. } => { + // if the structure (record/tag/array) is owned, the extracted value is + self.if_is_owned_then_own(*x, z); + + // if the extracted value is owned, the structure must be too + self.if_is_owned_then_own(z, *x); + } + GetTagId { structure: x, .. } => { // if the structure (record/tag/array) is owned, the extracted value is self.if_is_owned_then_own(*x, z); @@ -1035,7 +1034,11 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[Ownership] { unreachable!("These lowlevel operations are turned into mono Expr's") } - PtrCast | PtrWrite | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr + PtrStore => arena.alloc_slice_copy(&[owned, owned]), + PtrLoad => arena.alloc_slice_copy(&[owned]), + Alloca => arena.alloc_slice_copy(&[owned]), + + PtrClearTagId | PtrCast | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr | RefCountDecDataPtr | RefCountIsUnique => { unreachable!("Only inserted *after* borrow checking: {:?}", op); } diff --git a/crates/compiler/mono/src/code_gen_help/equality.rs b/crates/compiler/mono/src/code_gen_help/equality.rs index d0e3b813af1..95ae8508636 100644 --- a/crates/compiler/mono/src/code_gen_help/equality.rs +++ b/crates/compiler/mono/src/code_gen_help/equality.rs @@ -37,7 +37,7 @@ pub fn eq_generic<'a>( Builtin(List(elem_layout)) => eq_list(root, ident_ids, ctx, layout_interner, elem_layout), Struct(field_layouts) => eq_struct(root, ident_ids, ctx, layout_interner, field_layouts), Union(union_layout) => eq_tag_union(root, ident_ids, ctx, layout_interner, union_layout), - Boxed(inner_layout) => eq_boxed(root, ident_ids, ctx, layout_interner, inner_layout), + Ptr(inner_layout) => eq_boxed(root, ident_ids, ctx, layout_interner, inner_layout), LambdaSet(_) => unreachable!("`==` is not defined on functions"), RecursivePointer(_) => { unreachable!( @@ -138,7 +138,7 @@ fn eq_struct<'a>( ) -> Stmt<'a> { let mut else_stmt = Stmt::Ret(Symbol::BOOL_TRUE); for (i, layout) in field_layouts.iter().enumerate().rev() { - let field1_sym = root.create_symbol(ident_ids, &format!("field_1_{}", i)); + let field1_sym = root.create_symbol(ident_ids, &format!("field_1_{i}")); let field1_expr = Expr::StructAtIndex { index: i as u64, field_layouts, @@ -146,7 +146,7 @@ fn eq_struct<'a>( }; let field1_stmt = |next| Stmt::Let(field1_sym, field1_expr, *layout, next); - let field2_sym = root.create_symbol(ident_ids, &format!("field_2_{}", i)); + let field2_sym = root.create_symbol(ident_ids, &format!("field_2_{i}")); let field2_expr = Expr::StructAtIndex { index: i as u64, field_layouts, @@ -164,7 +164,7 @@ fn eq_struct<'a>( ) .unwrap(); - let eq_call_name = format!("eq_call_{}", i); + let eq_call_name = format!("eq_call_{i}"); let eq_call_sym = root.create_symbol(ident_ids, &eq_call_name); let eq_call_stmt = |next| Stmt::Let(eq_call_sym, eq_call_expr, LAYOUT_BOOL, next); @@ -488,8 +488,8 @@ fn eq_tag_fields<'a>( Some(i) => { // Implement tail recursion on this RecursivePointer, // in the innermost `else` clause after all other fields have been checked - let field1_sym = root.create_symbol(ident_ids, &format!("field_1_{}_{}", tag_id, i)); - let field2_sym = root.create_symbol(ident_ids, &format!("field_2_{}_{}", tag_id, i)); + let field1_sym = root.create_symbol(ident_ids, &format!("field_1_{tag_id}_{i}")); + let field2_sym = root.create_symbol(ident_ids, &format!("field_2_{tag_id}_{i}")); let field1_expr = Expr::UnionAtIndex { union_layout, @@ -533,8 +533,8 @@ fn eq_tag_fields<'a>( continue; // the tail-recursive field is handled elsewhere } - let field1_sym = root.create_symbol(ident_ids, &format!("field_1_{}_{}", tag_id, i)); - let field2_sym = root.create_symbol(ident_ids, &format!("field_2_{}_{}", tag_id, i)); + let field1_sym = root.create_symbol(ident_ids, &format!("field_1_{tag_id}_{i}")); + let field2_sym = root.create_symbol(ident_ids, &format!("field_2_{tag_id}_{i}")); let field1_expr = Expr::UnionAtIndex { union_layout, @@ -560,7 +560,7 @@ fn eq_tag_fields<'a>( ) .unwrap(); - let eq_call_name = format!("eq_call_{}", i); + let eq_call_name = format!("eq_call_{i}"); let eq_call_sym = root.create_symbol(ident_ids, &eq_call_name); stmt = Stmt::Let( @@ -608,8 +608,8 @@ fn eq_boxed<'a>( let b = root.create_symbol(ident_ids, "b"); let result = root.create_symbol(ident_ids, "result"); - let a_expr = Expr::ExprUnbox { symbol: ARG_1 }; - let b_expr = Expr::ExprUnbox { symbol: ARG_2 }; + let a_expr = Expr::ptr_load(&ARG_1); + let b_expr = Expr::ptr_load(&ARG_2); let eq_call_expr = root .call_specialized_op( ident_ids, @@ -648,8 +648,8 @@ fn eq_boxed<'a>( /// TODO, ListGetUnsafe no longer increments the refcount, so we can use it here. /// We can't use `ListGetUnsafe` because it increments the refcount, and we don't want that. /// Another way to dereference a heap pointer is to use `Expr::UnionAtIndex`. -/// To achieve this we use `PtrCast` to cast the element pointer to a "Box" layout. -/// Then we can increment the Box pointer in a loop, dereferencing it each time. +/// To achieve this we use `PtrCast` to cast the element pointer to a "Ptr" layout. +/// Then we can increment the pointer in a loop, dereferencing it each time. /// (An alternative approach would be to create a new lowlevel like ListPeekUnsafe.) fn eq_list<'a>( root: &mut CodeGenHelp<'a>, @@ -662,8 +662,8 @@ fn eq_list<'a>( let layout_isize = root.layout_isize; let arena = root.arena; - // A "Box" layout (heap pointer to a single list element) - let box_layout = layout_interner.insert_direct_no_semantic(LayoutRepr::Boxed(elem_layout)); + // A pointer layout (heap pointer to a single list element) + let ptr_layout = layout_interner.insert_direct_no_semantic(LayoutRepr::Ptr(elem_layout)); // Compare lengths @@ -682,16 +682,16 @@ fn eq_list<'a>( let elements_2 = root.create_symbol(ident_ids, "elements_2"); let elements_1_expr = Expr::StructAtIndex { index: 0, - field_layouts: root.arena.alloc([box_layout, layout_isize]), + field_layouts: root.arena.alloc([ptr_layout, layout_isize]), structure: ARG_1, }; let elements_2_expr = Expr::StructAtIndex { index: 0, - field_layouts: root.arena.alloc([box_layout, layout_isize]), + field_layouts: root.arena.alloc([ptr_layout, layout_isize]), structure: ARG_2, }; - let elements_1_stmt = |next| Stmt::Let(elements_1, elements_1_expr, box_layout, next); - let elements_2_stmt = |next| Stmt::Let(elements_2, elements_2_expr, box_layout, next); + let elements_1_stmt = |next| Stmt::Let(elements_1, elements_1_expr, ptr_layout, next); + let elements_2_stmt = |next| Stmt::Let(elements_2, elements_2_expr, ptr_layout, next); // Cast to integers let start_1 = root.create_symbol(ident_ids, "start_1"); @@ -757,17 +757,17 @@ fn eq_list<'a>( // if we haven't reached the end yet... // - // Cast integers to box pointers - let box1 = root.create_symbol(ident_ids, "box1"); - let box2 = root.create_symbol(ident_ids, "box2"); - let box1_stmt = |next| let_lowlevel(arena, box_layout, box1, PtrCast, &[addr1], next); - let box2_stmt = |next| let_lowlevel(arena, box_layout, box2, PtrCast, &[addr2], next); + // Cast integers to pointers + let ptr1 = root.create_symbol(ident_ids, "ptr1"); + let ptr2 = root.create_symbol(ident_ids, "ptr2"); + let ptr1_stmt = |next| let_lowlevel(arena, ptr_layout, ptr1, PtrCast, &[addr1], next); + let ptr2_stmt = |next| let_lowlevel(arena, ptr_layout, ptr2, PtrCast, &[addr2], next); - // Dereference the box pointers to get the current elements + // Dereference the pointers to get the current elements let elem1 = root.create_symbol(ident_ids, "elem1"); let elem2 = root.create_symbol(ident_ids, "elem2"); - let elem1_expr = Expr::ExprUnbox { symbol: box1 }; - let elem2_expr = Expr::ExprUnbox { symbol: box2 }; + let elem1_expr = Expr::ptr_load(arena.alloc(ptr1)); + let elem2_expr = Expr::ptr_load(arena.alloc(ptr2)); let elem1_stmt = |next| Stmt::Let(elem1, elem1_expr, elem_layout, next); let elem2_stmt = |next| Stmt::Let(elem2, elem2_expr, elem_layout, next); @@ -818,9 +818,9 @@ fn eq_list<'a>( Stmt::Ret(Symbol::BOOL_TRUE), root.arena.alloc( // - box1_stmt(root.arena.alloc( + ptr1_stmt(root.arena.alloc( // - box2_stmt(root.arena.alloc( + ptr2_stmt(root.arena.alloc( // elem1_stmt(root.arena.alloc( // diff --git a/crates/compiler/mono/src/code_gen_help/mod.rs b/crates/compiler/mono/src/code_gen_help/mod.rs index 9f51d264942..30cc8ad5e60 100644 --- a/crates/compiler/mono/src/code_gen_help/mod.rs +++ b/crates/compiler/mono/src/code_gen_help/mod.rs @@ -141,6 +141,7 @@ impl<'a> CodeGenHelp<'a> { let jp_decref = JoinPointId(self.create_symbol(ident_ids, "jp_decref")); HelperOp::DecRef(jp_decref) } + ModifyRc::Free(_) => unreachable!("free should be handled by the backend directly"), }; let mut ctx = Context { @@ -301,14 +302,14 @@ impl<'a> CodeGenHelp<'a> { let (ret_layout, arg_layouts): (InLayout<'a>, &'a [InLayout<'a>]) = { let arg = self.replace_rec_ptr(ctx, layout_interner, layout); - let box_arg = layout_interner.insert_direct_no_semantic(LayoutRepr::Boxed(arg)); + let ptr_arg = layout_interner.insert_direct_no_semantic(LayoutRepr::Ptr(arg)); match ctx.op { Dec | DecRef(_) => (LAYOUT_UNIT, self.arena.alloc([arg])), Reset | ResetRef => (layout, self.arena.alloc([layout])), Inc => (LAYOUT_UNIT, self.arena.alloc([arg, self.layout_isize])), - IndirectDec => (LAYOUT_UNIT, arena.alloc([box_arg])), - IndirectInc => (LAYOUT_UNIT, arena.alloc([box_arg, self.layout_isize])), + IndirectDec => (LAYOUT_UNIT, arena.alloc([ptr_arg])), + IndirectInc => (LAYOUT_UNIT, arena.alloc([ptr_arg, self.layout_isize])), Eq => (LAYOUT_BOOL, self.arena.alloc([arg, arg])), } }; @@ -430,15 +431,15 @@ impl<'a> CodeGenHelp<'a> { } Dec | DecRef(_) | Reset | ResetRef => self.arena.alloc([roc_value]), IndirectInc => { - let box_layout = - layout_interner.insert_direct_no_semantic(LayoutRepr::Boxed(layout)); + let ptr_layout = + layout_interner.insert_direct_no_semantic(LayoutRepr::Ptr(layout)); let inc_amount = (self.layout_isize, ARG_2); - self.arena.alloc([(box_layout, ARG_1), inc_amount]) + self.arena.alloc([(ptr_layout, ARG_1), inc_amount]) } IndirectDec => { - let box_layout = - layout_interner.insert_direct_no_semantic(LayoutRepr::Boxed(layout)); - self.arena.alloc([(box_layout, ARG_1)]) + let ptr_layout = + layout_interner.insert_direct_no_semantic(LayoutRepr::Ptr(layout)); + self.arena.alloc([(ptr_layout, ARG_1)]) } Eq => self.arena.alloc([roc_value, (layout, ARG_2)]), } @@ -485,21 +486,19 @@ impl<'a> CodeGenHelp<'a> { niche: Niche::NONE, }, HelperOp::IndirectInc => { - let box_layout = - layout_interner.insert_direct_no_semantic(LayoutRepr::Boxed(layout)); + let ptr_layout = layout_interner.insert_direct_no_semantic(LayoutRepr::Ptr(layout)); ProcLayout { - arguments: self.arena.alloc([box_layout, self.layout_isize]), + arguments: self.arena.alloc([ptr_layout, self.layout_isize]), result: LAYOUT_UNIT, niche: Niche::NONE, } } HelperOp::IndirectDec => { - let box_layout = - layout_interner.insert_direct_no_semantic(LayoutRepr::Boxed(layout)); + let ptr_layout = layout_interner.insert_direct_no_semantic(LayoutRepr::Ptr(layout)); ProcLayout { - arguments: self.arena.alloc([box_layout]), + arguments: self.arena.alloc([ptr_layout]), result: LAYOUT_UNIT, niche: Niche::NONE, } @@ -572,9 +571,9 @@ impl<'a> CodeGenHelp<'a> { return layout; } - LayoutRepr::Boxed(inner) => { + LayoutRepr::Ptr(inner) => { let inner = self.replace_rec_ptr(ctx, layout_interner, inner); - LayoutRepr::Boxed(inner) + LayoutRepr::Ptr(inner) } LayoutRepr::LambdaSet(lambda_set) => { @@ -668,20 +667,20 @@ impl<'a> CallerProc<'a> { op: HelperOp::Eq, }; - let box_capture_layout = if let Some(capture_layout) = capture_layout { - layout_interner.insert_direct_no_semantic(LayoutRepr::Boxed(capture_layout)) + let ptr_capture_layout = if let Some(capture_layout) = capture_layout { + layout_interner.insert_direct_no_semantic(LayoutRepr::Ptr(capture_layout)) } else { - layout_interner.insert_direct_no_semantic(LayoutRepr::Boxed(Layout::UNIT)) + layout_interner.insert_direct_no_semantic(LayoutRepr::Ptr(Layout::UNIT)) }; - let box_argument_layout = layout_interner - .insert_direct_no_semantic(LayoutRepr::Boxed(passed_function.argument_layouts[0])); + let ptr_argument_layout = layout_interner + .insert_direct_no_semantic(LayoutRepr::Ptr(passed_function.argument_layouts[0])); - let box_return_layout = layout_interner - .insert_direct_no_semantic(LayoutRepr::Boxed(passed_function.return_layout)); + let ptr_return_layout = layout_interner + .insert_direct_no_semantic(LayoutRepr::Ptr(passed_function.return_layout)); let proc_layout = ProcLayout { - arguments: arena.alloc([box_capture_layout, box_argument_layout, box_return_layout]), + arguments: arena.alloc([ptr_capture_layout, ptr_argument_layout, ptr_return_layout]), result: Layout::UNIT, niche: Niche::NONE, }; @@ -691,16 +690,11 @@ impl<'a> CallerProc<'a> { ctx.new_linker_data.push((proc_symbol, proc_layout)); - let unbox_capture = Expr::ExprUnbox { - symbol: Symbol::ARG_1, - }; - - let unbox_argument = Expr::ExprUnbox { - symbol: Symbol::ARG_2, - }; + let load_capture = Expr::ptr_load(arena.alloc(Symbol::ARG_1)); + let load_argument = Expr::ptr_load(arena.alloc(Symbol::ARG_2)); - let unboxed_capture = Self::create_symbol(home, ident_ids, "unboxed_capture"); - let unboxed_argument = Self::create_symbol(home, ident_ids, "unboxed_argument"); + let loaded_capture = Self::create_symbol(home, ident_ids, "loaded_capture"); + let loaded_argument = Self::create_symbol(home, ident_ids, "loaded_argument"); let call_result = Self::create_symbol(home, ident_ids, "call_result"); let unit_symbol = Self::create_symbol(home, ident_ids, "unit_symbol"); let ignored = Self::create_symbol(home, ident_ids, "ignored"); @@ -713,23 +707,23 @@ impl<'a> CallerProc<'a> { specialization_id: passed_function.specialization_id, }, arguments: if capture_layout.is_some() { - arena.alloc([unboxed_argument, unboxed_capture]) + arena.alloc([loaded_argument, loaded_capture]) } else { - arena.alloc([unboxed_argument]) + arena.alloc([loaded_argument]) }, }); let ptr_write = Expr::Call(Call { call_type: CallType::LowLevel { - op: LowLevel::PtrWrite, + op: LowLevel::PtrStore, update_mode: UpdateModeId::BACKEND_DUMMY, }, arguments: arena.alloc([Symbol::ARG_3, call_result]), }); let mut body = Stmt::Let( - unboxed_argument, - unbox_argument, + loaded_argument, + load_argument, passed_function.argument_layouts[0], arena.alloc(Stmt::Let( call_result, @@ -738,7 +732,7 @@ impl<'a> CallerProc<'a> { arena.alloc(Stmt::Let( ignored, ptr_write, - box_return_layout, + ptr_return_layout, arena.alloc(Stmt::Let( unit_symbol, Expr::Struct(&[]), @@ -751,8 +745,8 @@ impl<'a> CallerProc<'a> { if let Some(capture_layout) = capture_layout { body = Stmt::Let( - unboxed_capture, - unbox_capture, + loaded_capture, + load_capture, capture_layout, arena.alloc(body), ); @@ -760,9 +754,9 @@ impl<'a> CallerProc<'a> { let args: &'a [(InLayout<'a>, Symbol)] = { arena.alloc([ - (box_capture_layout, ARG_1), - (box_argument_layout, ARG_2), - (box_return_layout, ARG_3), + (ptr_capture_layout, ARG_1), + (ptr_argument_layout, ARG_2), + (ptr_return_layout, ARG_3), ]) }; @@ -789,7 +783,7 @@ impl<'a> CallerProc<'a> { .pretty(80) .to_string(); - println!("{}", doc); + println!("{doc}"); } Self { @@ -843,6 +837,6 @@ fn layout_needs_helper_proc<'a>( LayoutRepr::Union(_) => true, LayoutRepr::LambdaSet(_) => true, LayoutRepr::RecursivePointer(_) => false, - LayoutRepr::Boxed(_) => true, + LayoutRepr::Ptr(_) => false, } } diff --git a/crates/compiler/mono/src/code_gen_help/refcount.rs b/crates/compiler/mono/src/code_gen_help/refcount.rs index 8ebaba87140..3f5b90c7fc9 100644 --- a/crates/compiler/mono/src/code_gen_help/refcount.rs +++ b/crates/compiler/mono/src/code_gen_help/refcount.rs @@ -118,6 +118,9 @@ pub fn refcount_stmt<'a>( }, } } + ModifyRc::Free(_) => { + unreachable!("free should be handled by the backend directly") + } } } @@ -132,7 +135,7 @@ pub fn refcount_indirect<'a>( let arena = root.arena; let unit = root.create_symbol(ident_ids, "unit"); - let unboxed = root.create_symbol(ident_ids, "unboxed"); + let loaded = root.create_symbol(ident_ids, "loaded"); let indirect_op = ctx.op; let direct_op = match ctx.op { @@ -144,7 +147,7 @@ pub fn refcount_indirect<'a>( // we've done the indirection, the inner value shoud be inc- or decremented directly ctx.op = direct_op; - let mod_args = refcount_args(root, ctx, unboxed); + let mod_args = refcount_args(root, ctx, loaded); let opt_mod_expr = root.call_specialized_op(ident_ids, ctx, layout_interner, element_layout, mod_args); @@ -153,8 +156,8 @@ pub fn refcount_indirect<'a>( if let Some(mod_expr) = opt_mod_expr { Stmt::Let( - unboxed, - Expr::ExprUnbox { symbol: structure }, + loaded, + Expr::ptr_load(arena.alloc(structure)), element_layout, arena.alloc( // @@ -230,63 +233,12 @@ pub fn refcount_generic<'a>( LayoutRepr::RecursivePointer(_) => unreachable!( "We should never call a refcounting helper on a RecursivePointer layout directly" ), - LayoutRepr::Boxed(inner_layout) => refcount_boxed( - root, - ident_ids, - ctx, - layout_interner, - layout, - inner_layout, - structure, - ), + LayoutRepr::Ptr(_) => { + unreachable!("We should never call a refcounting helper on a Ptr layout directly") + } } } -fn if_unique<'a>( - root: &mut CodeGenHelp<'a>, - ident_ids: &mut IdentIds, - value: Symbol, - when_unique: impl FnOnce(JoinPointId) -> Stmt<'a>, - when_done: Stmt<'a>, -) -> Stmt<'a> { - // joinpoint f = - // - // in - // if is_unique then - // (f) - // else - // jump f - - let joinpoint = root.create_symbol(ident_ids, "is_unique_joinpoint"); - let joinpoint = JoinPointId(joinpoint); - - let is_unique = root.create_symbol(ident_ids, "is_unique"); - - let mut stmt = Stmt::if_then_else( - root.arena, - is_unique, - Layout::UNIT, - when_unique(joinpoint), - root.arena.alloc(Stmt::Jump(joinpoint, &[])), - ); - - stmt = Stmt::Join { - id: joinpoint, - parameters: &[], - body: root.arena.alloc(when_done), - remainder: root.arena.alloc(stmt), - }; - - let_lowlevel( - root.arena, - root.layout_isize, - is_unique, - LowLevel::RefCountIsUnique, - &[value], - root.arena.alloc(stmt), - ) -} - pub fn refcount_reset_proc_body<'a>( root: &mut CodeGenHelp<'a>, ident_ids: &mut IdentIds, @@ -441,7 +393,7 @@ pub fn refcount_reset_proc_body<'a>( ); // Refcount value - let rc_expr = Expr::ExprUnbox { symbol: rc_ptr }; + let rc_expr = Expr::ptr_load(root.arena.alloc(rc_ptr)); let rc_stmt = Stmt::Let( rc, @@ -450,8 +402,10 @@ pub fn refcount_reset_proc_body<'a>( root.arena.alloc(refcount_1_stmt), ); - // a Box never masks bits - let mask_lower_bits = false; + let mask_lower_bits = match layout_interner.get_repr(layout) { + LayoutRepr::Union(ul) => ul.stores_tag_id_in_pointer(root.target_info), + _ => false, + }; // Refcount pointer let rc_ptr_stmt = { @@ -564,7 +518,7 @@ pub fn refcount_resetref_proc_body<'a>( ); // Refcount value - let rc_expr = Expr::ExprUnbox { symbol: rc_ptr }; + let rc_expr = Expr::ptr_load(root.arena.alloc(rc_ptr)); let rc_stmt = Stmt::Let( rc, @@ -573,8 +527,10 @@ pub fn refcount_resetref_proc_body<'a>( root.arena.alloc(refcount_1_stmt), ); - // a Box never masks bits - let mask_lower_bits = false; + let mask_lower_bits = match layout_interner.get_repr(layout) { + LayoutRepr::Union(ul) => ul.stores_tag_id_in_pointer(root.target_info), + _ => false, + }; // Refcount pointer let rc_ptr_stmt = { @@ -626,39 +582,33 @@ fn rc_ptr_from_data_ptr_help<'a>( addr_sym: Symbol, recursion_ptr: InLayout<'a>, ) -> Stmt<'a> { - use std::ops::Neg; - - // Typecast the structure pointer to an integer - // Backends expect a number Layout to choose the right "subtract" instruction - let as_int_sym = if mask_lower_bits { - root.create_symbol(ident_ids, "as_int") + // symbol of a pointer with any tag id bits cleared + let cleared_sym = if mask_lower_bits { + root.create_symbol(ident_ids, "cleared") } else { - addr_sym + structure }; - let as_int_expr = Expr::Call(Call { + + let clear_tag_id_expr = Expr::Call(Call { call_type: CallType::LowLevel { - op: LowLevel::PtrCast, + op: LowLevel::PtrClearTagId, update_mode: UpdateModeId::BACKEND_DUMMY, }, arguments: root.arena.alloc([structure]), }); - let as_int_stmt = |next| Stmt::Let(as_int_sym, as_int_expr, root.layout_isize, next); + let clear_tag_id_stmt = + |next| Stmt::Let(cleared_sym, clear_tag_id_expr, root.layout_isize, next); - // Mask for lower bits (for tag union id) - let mask_sym = root.create_symbol(ident_ids, "mask"); - let mask_expr = Expr::Literal(Literal::Int( - (root.target_info.ptr_width() as i128).neg().to_ne_bytes(), - )); - let mask_stmt = |next| Stmt::Let(mask_sym, mask_expr, root.layout_isize, next); - - let and_expr = Expr::Call(Call { + // Typecast the structure pointer to an integer + // Backends expect a number Layout to choose the right "subtract" instruction + let as_int_expr = Expr::Call(Call { call_type: CallType::LowLevel { - op: LowLevel::And, + op: LowLevel::PtrCast, update_mode: UpdateModeId::BACKEND_DUMMY, }, - arguments: root.arena.alloc([as_int_sym, mask_sym]), + arguments: root.arena.alloc([cleared_sym]), }); - let and_stmt = |next| Stmt::Let(addr_sym, and_expr, root.layout_isize, next); + let as_int_stmt = |next| Stmt::Let(addr_sym, as_int_expr, root.layout_isize, next); // Pointer size constant let ptr_size_sym = root.create_symbol(ident_ids, "ptr_size"); @@ -688,40 +638,24 @@ fn rc_ptr_from_data_ptr_help<'a>( }); let cast_stmt = |next| Stmt::Let(rc_ptr_sym, cast_expr, recursion_ptr, next); - if mask_lower_bits { - as_int_stmt(root.arena.alloc( + let body = as_int_stmt(root.arena.alloc( + // + ptr_size_stmt(root.arena.alloc( // - mask_stmt(root.arena.alloc( + sub_stmt(root.arena.alloc( // - and_stmt(root.arena.alloc( + cast_stmt(root.arena.alloc( // - ptr_size_stmt(root.arena.alloc( - // - sub_stmt(root.arena.alloc( - // - cast_stmt(root.arena.alloc( - // - following, - )), - )), - )), + following, )), )), - )) + )), + )); + + if mask_lower_bits { + clear_tag_id_stmt(root.arena.alloc(body)) } else { - as_int_stmt(root.arena.alloc( - // - ptr_size_stmt(root.arena.alloc( - // - sub_stmt(root.arena.alloc( - // - cast_stmt(root.arena.alloc( - // - following, - )), - )), - )), - )) + body } } @@ -980,8 +914,8 @@ fn refcount_list<'a>( let layout_isize = root.layout_isize; let arena = root.arena; - // A "Box" layout (heap pointer to a single list element) - let box_layout = layout_interner.insert_direct_no_semantic(LayoutRepr::Boxed(elem_layout)); + // A "Ptr" layout (heap pointer to a single list element) + let ptr_layout = layout_interner.insert_direct_no_semantic(LayoutRepr::Ptr(elem_layout)); // // Check if the list is empty @@ -1005,7 +939,7 @@ fn refcount_list<'a>( // let capacity = StructAtIndex 2 structure let capacity = root.create_symbol(ident_ids, "capacity"); - let list_field_layouts = arena.alloc([box_layout, layout_isize, layout_isize]); + let list_field_layouts = arena.alloc([ptr_layout, layout_isize, layout_isize]); let capacity_expr = Expr::StructAtIndex { index: 2, field_layouts: list_field_layouts, @@ -1029,7 +963,7 @@ fn refcount_list<'a>( field_layouts: list_field_layouts, structure, }; - let first_element_stmt = |next| Stmt::Let(first_element, first_element_expr, box_layout, next); + let first_element_stmt = |next| Stmt::Let(first_element, first_element_expr, ptr_layout, next); let jp_elements = JoinPointId(root.create_symbol(ident_ids, "jp_elements")); let data_pointer = root.create_symbol(ident_ids, "data_pointer"); @@ -1116,7 +1050,7 @@ fn refcount_list<'a>( layout_interner, elem_layout, LAYOUT_UNIT, - box_layout, + ptr_layout, len, first_element_pointer, modify_list, @@ -1178,7 +1112,7 @@ fn refcount_list_elems<'a>( layout_interner: &mut STLayoutInterner<'a>, elem_layout: InLayout<'a>, ret_layout: InLayout<'a>, - box_layout: InLayout<'a>, + ptr_layout: InLayout<'a>, length: Symbol, elements: Symbol, following: Stmt<'a>, @@ -1236,13 +1170,13 @@ fn refcount_list_elems<'a>( // if we haven't reached the end yet... // - // Cast integer to box pointer - let box_ptr = root.create_symbol(ident_ids, "box"); - let box_stmt = |next| let_lowlevel(arena, box_layout, box_ptr, PtrCast, &[addr], next); + // Cast integer to pointer + let ptr_symbol = root.create_symbol(ident_ids, "ptr"); + let ptr_stmt = |next| let_lowlevel(arena, ptr_layout, ptr_symbol, PtrCast, &[addr], next); - // Dereference the box pointer to get the current element + // Dereference the pointer to get the current element let elem = root.create_symbol(ident_ids, "elem"); - let elem_expr = Expr::ExprUnbox { symbol: box_ptr }; + let elem_expr = Expr::ptr_load(arena.alloc(ptr_symbol)); let elem_stmt = |next| Stmt::Let(elem, elem_expr, elem_layout, next); // @@ -1285,7 +1219,7 @@ fn refcount_list_elems<'a>( is_end, ret_layout, following, - arena.alloc(box_stmt(arena.alloc( + arena.alloc(ptr_stmt(arena.alloc( // elem_stmt(arena.alloc( // @@ -1342,7 +1276,7 @@ fn refcount_struct<'a>( for (i, field_layout) in field_layouts.iter().enumerate().rev() { if layout_interner.contains_refcounted(*field_layout) { - let field_val = root.create_symbol(ident_ids, &format!("field_val_{}", i)); + let field_val = root.create_symbol(ident_ids, &format!("field_val_{i}")); let field_val_expr = Expr::StructAtIndex { index: i as u64, field_layouts, @@ -1350,7 +1284,7 @@ fn refcount_struct<'a>( }; let field_val_stmt = |next| Stmt::Let(field_val, field_val_expr, *field_layout, next); - let mod_unit = root.create_symbol(ident_ids, &format!("mod_field_{}", i)); + let mod_unit = root.create_symbol(ident_ids, &format!("mod_field_{i}")); let mod_args = refcount_args(root, ctx, field_val); let mod_expr = root .call_specialized_op(ident_ids, ctx, layout_interner, *field_layout, mod_args) @@ -1824,7 +1758,7 @@ fn refcount_union_tailrec<'a>( filtered.push((i, *field)); } else { let field_val = - root.create_symbol(ident_ids, &format!("field_{}_{}", tag_id, i)); + root.create_symbol(ident_ids, &format!("field_{tag_id}_{i}")); let field_val_expr = Expr::UnionAtIndex { union_layout, tag_id, @@ -1962,7 +1896,7 @@ fn refcount_tag_fields<'a>( for (i, field_layout) in field_layouts.iter().rev() { if layout_interner.contains_refcounted(*field_layout) { - let field_val = root.create_symbol(ident_ids, &format!("field_{}_{}", tag_id, i)); + let field_val = root.create_symbol(ident_ids, &format!("field_{tag_id}_{i}")); let field_val_expr = Expr::UnionAtIndex { union_layout, tag_id, @@ -1971,7 +1905,7 @@ fn refcount_tag_fields<'a>( }; let field_val_stmt = |next| Stmt::Let(field_val, field_val_expr, *field_layout, next); - let mod_unit = root.create_symbol(ident_ids, &format!("mod_field_{}_{}", tag_id, i)); + let mod_unit = root.create_symbol(ident_ids, &format!("mod_field_{tag_id}_{i}")); let mod_args = refcount_args(root, ctx, field_val); let mod_expr = root .call_specialized_op(ident_ids, ctx, layout_interner, *field_layout, mod_args) @@ -1990,72 +1924,3 @@ fn refcount_tag_fields<'a>( stmt } - -fn refcount_boxed<'a>( - root: &mut CodeGenHelp<'a>, - ident_ids: &mut IdentIds, - ctx: &mut Context<'a>, - layout_interner: &mut STLayoutInterner<'a>, - layout: InLayout<'a>, - inner_layout: InLayout<'a>, - outer: Symbol, -) -> Stmt<'a> { - let arena = root.arena; - - // - // modify refcount of the inner and outer structures - // RC on inner first, to avoid use-after-free for Dec - // We're defining statements in reverse, so define outer first - // - - let alignment = layout_interner.allocation_alignment_bytes(layout); - let ret_stmt = rc_return_stmt(root, ident_ids, ctx); - let modify_outer = modify_refcount( - root, - ident_ids, - ctx, - Pointer::ToData(outer), - alignment, - arena.alloc(ret_stmt), - ); - - // decrement the inner value if the operation is a decrement and the box itself is unique - if layout_interner.is_refcounted(inner_layout) && ctx.op.is_dec() { - let inner = root.create_symbol(ident_ids, "inner"); - let inner_expr = Expr::ExprUnbox { symbol: outer }; - - let mod_inner_unit = root.create_symbol(ident_ids, "mod_inner_unit"); - let mod_inner_args = refcount_args(root, ctx, inner); - let mod_inner_expr = root - .call_specialized_op( - ident_ids, - ctx, - layout_interner, - inner_layout, - mod_inner_args, - ) - .unwrap(); - - if_unique( - root, - ident_ids, - outer, - |id| { - Stmt::Let( - inner, - inner_expr, - inner_layout, - arena.alloc(Stmt::Let( - mod_inner_unit, - mod_inner_expr, - LAYOUT_UNIT, - arena.alloc(Stmt::Jump(id, &[])), - )), - ) - }, - modify_outer, - ) - } else { - modify_outer - } -} diff --git a/crates/compiler/mono/src/debug/checker.rs b/crates/compiler/mono/src/debug/checker.rs index 93621212628..b74743c30d5 100644 --- a/crates/compiler/mono/src/debug/checker.rs +++ b/crates/compiler/mono/src/debug/checker.rs @@ -397,11 +397,18 @@ impl<'a, 'r> Ctx<'a, 'r> { tag_layout, tag_id, arguments, + reuse, } => { let interned_layout = self .interner .insert_direct_no_semantic(LayoutRepr::Union(tag_layout)); + + if let Some(reuse_token) = reuse { + self.check_sym_layout(reuse_token.symbol, interned_layout, UseKind::TagReuse); + } + self.check_tag_expr(interned_layout, tag_layout, tag_id, arguments); + Some(interned_layout) } Expr::Struct(syms) => { @@ -429,6 +436,14 @@ impl<'a, 'r> Ctx<'a, 'r> { } => self.with_sym_layout(structure, |ctx, _def_line, layout| { ctx.check_union_at_index(structure, layout, union_layout, tag_id, index) }), + &Expr::UnionFieldPtrAtIndex { + structure, + tag_id, + union_layout, + index, + } => self.with_sym_layout(structure, |ctx, _def_line, layout| { + ctx.check_union_field_ptr_at_index(structure, layout, union_layout, tag_id, index) + }), Expr::Array { elem_layout, elems } => { for elem in elems.iter() { match elem { @@ -449,38 +464,6 @@ impl<'a, 'r> Ctx<'a, 'r> { // TODO don't know what the element layout is None } - &Expr::ExprBox { symbol } => self.with_sym_layout(symbol, |ctx, _def_line, layout| { - let inner = layout; - Some( - ctx.interner - .insert_direct_no_semantic(LayoutRepr::Boxed(inner)), - ) - }), - &Expr::ExprUnbox { symbol } => self.with_sym_layout(symbol, |ctx, def_line, layout| { - let layout = ctx.resolve(layout); - match ctx.interner.get_repr(layout) { - LayoutRepr::Boxed(inner) => Some(inner), - _ => { - ctx.problem(ProblemKind::UnboxNotABox { symbol, def_line }); - None - } - } - }), - &Expr::Reuse { - symbol, - update_tag_id: _, - update_mode: _, - tag_layout, - tag_id: _, - arguments: _, - } => { - let union = self - .interner - .insert_direct_no_semantic(LayoutRepr::Union(tag_layout)); - self.check_sym_layout(symbol, union, UseKind::TagReuse); - // TODO also check update arguments - Some(union) - } &Expr::Reset { symbol, update_mode: _, @@ -566,6 +549,58 @@ impl<'a, 'r> Ctx<'a, 'r> { }) } + fn check_union_field_ptr_at_index( + &mut self, + structure: Symbol, + interned_union_layout: InLayout<'a>, + union_layout: UnionLayout<'a>, + tag_id: u16, + index: u64, + ) -> Option> { + let union = self + .interner + .insert_direct_no_semantic(LayoutRepr::Union(union_layout)); + + let field_ptr_layout = match get_tag_id_payloads(union_layout, tag_id) { + TagPayloads::IdNotInUnion => None, + TagPayloads::Payloads(payloads) => payloads.get(index as usize).map(|field_layout| { + self.interner + .insert_direct_no_semantic(LayoutRepr::Ptr(*field_layout)) + }), + }; + + self.with_sym_layout(structure, |ctx, def_line, _layout| { + ctx.check_sym_layout(structure, union, UseKind::TagExpr); + + match get_tag_id_payloads(union_layout, tag_id) { + TagPayloads::IdNotInUnion => { + ctx.problem(ProblemKind::IndexingTagIdNotInUnion { + structure, + def_line, + tag_id, + union_layout: interned_union_layout, + }); + None + } + TagPayloads::Payloads(payloads) => { + if field_ptr_layout.is_none() { + ctx.problem(ProblemKind::TagUnionStructIndexOOB { + structure, + def_line, + tag_id, + index, + size: payloads.len(), + }); + + None + } else { + field_ptr_layout + } + } + } + }) + } + fn check_call(&mut self, call: &Call<'a>) -> Option> { let Call { call_type, @@ -656,8 +691,10 @@ impl<'a, 'r> Ctx<'a, 'r> { } fn check_modify_rc(&mut self, rc: ModifyRc) { + use ModifyRc::*; + match rc { - ModifyRc::Inc(sym, _) | ModifyRc::Dec(sym) | ModifyRc::DecRef(sym) => { + Inc(sym, _) | Dec(sym) | DecRef(sym) | Free(sym) => { // TODO: also check that sym layout needs refcounting self.check_sym_exists(sym); } diff --git a/crates/compiler/mono/src/debug/report.rs b/crates/compiler/mono/src/debug/report.rs index 1f7dd578352..dfc5558a43f 100644 --- a/crates/compiler/mono/src/debug/report.rs +++ b/crates/compiler/mono/src/debug/report.rs @@ -62,7 +62,7 @@ where .pretty(80) .to_string(); - eprintln!("Full source: {}", src); + eprintln!("Full source: {src}"); let interpolated_docs = stack( f, diff --git a/crates/compiler/mono/src/drop_specialization.rs b/crates/compiler/mono/src/drop_specialization.rs index 82a4978bd50..19f8bbb10d9 100644 --- a/crates/compiler/mono/src/drop_specialization.rs +++ b/crates/compiler/mono/src/drop_specialization.rs @@ -169,11 +169,6 @@ fn specialize_drops_stmt<'a, 'i>( alloc_let_with_continuation!(environment) } - Expr::ExprBox { symbol: child } => { - environment.add_box_child(*binding, *child); - - alloc_let_with_continuation!(environment) - } Expr::Array { elems: children, .. } => { @@ -211,18 +206,22 @@ fn specialize_drops_stmt<'a, 'i>( // TODO perhaps we need the union_layout later as well? if so, create a new function/map to store it. environment.add_union_child(*structure, *binding, *tag_id, *index); // Generated code might know the tag of the union without switching on it. - // So if we unionAtIndex, we must know the tag and we can use it to specialize the drop. + // So if we UnionAtIndex, we must know the tag and we can use it to specialize the drop. environment.symbol_tag.insert(*structure, *tag_id); alloc_let_with_continuation!(environment) } - Expr::ExprUnbox { symbol } => { - environment.add_box_child(*symbol, *binding); + Expr::UnionFieldPtrAtIndex { + structure, + tag_id, + union_layout: _, + index: _, + } => { + // Generated code might know the tag of the union without switching on it. + // So if we UnionFieldPtrAtIndex, we must know the tag and we can use it to specialize the drop. + environment.symbol_tag.insert(*structure, *tag_id); alloc_let_with_continuation!(environment) } - Expr::Reuse { .. } => { - alloc_let_with_continuation!(environment) - } Expr::Reset { .. } => { // TODO allow to inline this to replace it with resetref alloc_let_with_continuation!(environment) @@ -527,15 +526,6 @@ fn specialize_drops_stmt<'a, 'i>( &mut incremented_children, continuation, ), - LayoutRepr::Boxed(_layout) => specialize_boxed( - arena, - layout_interner, - ident_ids, - environment, - &mut incremented_children, - symbol, - continuation, - ), LayoutRepr::Builtin(Builtin::List(layout)) => specialize_list( arena, layout_interner, @@ -571,8 +561,9 @@ fn specialize_drops_stmt<'a, 'i>( updated_stmt } } - ModifyRc::DecRef(_) => { - // Inlining has no point, since it doesn't decrement it's children + ModifyRc::DecRef(_) | ModifyRc::Free(_) => { + // These operations are not recursive (the children are not touched) + // so inlining is not useful arena.alloc(Stmt::Refcounting( *rc, specialize_drops_stmt( @@ -815,7 +806,7 @@ fn specialize_struct<'a, 'i>( // This value has not been index before, create a new symbol. None => { let field_symbol = - environment.create_symbol(ident_ids, &format!("field_val_{}", i)); + environment.create_symbol(ident_ids, &format!("field_val_{i}")); let field_val_expr = Expr::StructAtIndex { index: i as u64, @@ -954,7 +945,7 @@ fn specialize_union<'a, 'i>( Some(rc) => { let field_symbol = environment.create_symbol( ident_ids, - &format!("field_val_{}", i), + &format!("field_val_{i}"), ); let field_val_expr = Expr::UnionAtIndex { @@ -1020,8 +1011,10 @@ fn specialize_union<'a, 'i>( )) }), arena.alloc(Stmt::Refcounting( - // TODO this could be replaced by a free if ever added to the IR. - ModifyRc::DecRef(*symbol), + // we know for sure that the allocation is unique at + // this point. Therefore we can free (or maybe reuse) + // without checking the refcount again. + ModifyRc::Free(*symbol), continuation, )), ) @@ -1057,63 +1050,6 @@ fn specialize_union<'a, 'i>( } } -fn specialize_boxed<'a, 'i>( - arena: &'a Bump, - layout_interner: &'i mut STLayoutInterner<'a>, - ident_ids: &'i mut IdentIds, - environment: &mut DropSpecializationEnvironment<'a>, - incremented_children: &mut CountingMap, - symbol: &Symbol, - continuation: &'a Stmt<'a>, -) -> &'a Stmt<'a> { - let removed = match incremented_children.map.iter().next() { - Some((s, _)) => { - let s = *s; - incremented_children.pop(&s); - Some(s) - } - None => None, - }; - - let new_continuation = - specialize_drops_stmt(arena, layout_interner, ident_ids, environment, continuation); - - match removed { - Some(s) => { - branch_uniqueness( - arena, - ident_ids, - layout_interner, - environment, - *symbol, - // If the symbol is unique: - // - free the box - |_, _, continuation| { - arena.alloc(Stmt::Refcounting( - // TODO can be replaced by free if ever added to the IR. - ModifyRc::DecRef(*symbol), - continuation, - )) - }, - // If the symbol is not unique: - // - increment the child - // - decref the box - |_, _, continuation| { - arena.alloc(Stmt::Refcounting( - ModifyRc::Inc(s, 1), - arena.alloc(Stmt::Refcounting(ModifyRc::DecRef(*symbol), continuation)), - )) - }, - new_continuation, - ) - } - None => { - // No known children, keep decrementing the symbol. - arena.alloc(Stmt::Refcounting(ModifyRc::Dec(*symbol), new_continuation)) - } - } -} - fn specialize_list<'a, 'i>( arena: &'a Bump, layout_interner: &'i mut STLayoutInterner<'a>, @@ -1198,11 +1134,11 @@ fn specialize_list<'a, 'i>( // If the symbol is unknown, we have to get the value from the list. // Should only happen when list elements are discarded. None => { - let field_symbol = environment - .create_symbol(ident_ids, &format!("field_val_{}", i)); + let field_symbol = + environment.create_symbol(ident_ids, &format!("field_val_{i}")); - let index_symbol = environment - .create_symbol(ident_ids, &format!("index_val_{}", i)); + let index_symbol = + environment.create_symbol(ident_ids, &format!("index_val_{i}")); let dec = arena.alloc(Stmt::Refcounting( ModifyRc::Dec(field_symbol), @@ -1491,7 +1427,7 @@ impl<'a> DropSpecializationEnvironment<'a> { } } - fn create_symbol<'i>(&self, ident_ids: &'i mut IdentIds, debug_name: &str) -> Symbol { + fn create_symbol(&self, ident_ids: &mut IdentIds, debug_name: &str) -> Symbol { let ident_id = ident_ids.add_str(debug_name); Symbol::new(self.home, ident_id) } @@ -1520,13 +1456,6 @@ impl<'a> DropSpecializationEnvironment<'a> { .push((child, tag, index)); } - fn add_box_child(&mut self, parent: Parent, child: Child) { - self.box_children - .entry(parent) - .or_insert_with(|| Vec::new_in(self.arena)) - .push(child); - } - fn add_list_child(&mut self, parent: Parent, child: Child, index: u64) { self.list_children .entry(parent) @@ -1666,7 +1595,12 @@ fn low_level_no_rc(lowlevel: &LowLevel) -> RC { unreachable!("These lowlevel operations are turned into mono Expr's") } - PtrCast | PtrWrite | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr + // only inserted for internal purposes. RC should not touch it + PtrStore => RC::NoRc, + PtrLoad => RC::NoRc, + Alloca => RC::NoRc, + + PtrClearTagId | PtrCast | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr | RefCountDecDataPtr | RefCountIsUnique => { unreachable!("Only inserted *after* borrow checking: {:?}", lowlevel); } diff --git a/crates/compiler/mono/src/inc_dec.rs b/crates/compiler/mono/src/inc_dec.rs index fa6978fa1ee..a8c78c05ccb 100644 --- a/crates/compiler/mono/src/inc_dec.rs +++ b/crates/compiler/mono/src/inc_dec.rs @@ -10,6 +10,7 @@ use std::{collections::HashMap, hash::BuildHasherDefault}; use bumpalo::collections::{CollectIn, Vec}; use bumpalo::Bump; use roc_collections::{all::WyHash, MutMap, MutSet}; +use roc_error_macros::internal_error; use roc_module::low_level::LowLevel; use roc_module::{low_level::LowLevelWrapperType, symbol::Symbol}; @@ -271,9 +272,10 @@ impl<'v> RefcountEnvironment<'v> { */ fn consume_rc_symbol(&mut self, symbol: Symbol) -> Ownership { // Consume the symbol by setting it to borrowed (if it was owned before), and return the previous ownership. - self.symbols_ownership - .insert(symbol, Ownership::Borrowed) - .expect("Expected symbol to be in environment") + match self.symbols_ownership.insert(symbol, Ownership::Borrowed) { + Some(ownership) => ownership, + None => internal_error!("Expected symbol {symbol:?} to be in environment"), + } } /** @@ -344,17 +346,16 @@ impl<'v> RefcountEnvironment<'v> { // A groupby or something similar would be nice here. let mut symbol_usage = MutMap::default(); for symbol in symbols { - match { - self.symbols_rc_types - .get(&symbol) - .expect("Expected symbol to be in the map") - } { + match self.symbols_rc_types.get(&symbol) { // If the symbol is reference counted, we need to increment the usage count. - VarRcType::ReferenceCounted => { + Some(VarRcType::ReferenceCounted) => { *symbol_usage.entry(symbol).or_default() += 1; } // If the symbol is not reference counted, we don't need to do anything. - VarRcType::NotReferenceCounted => continue, + Some(VarRcType::NotReferenceCounted) => continue, + None => { + internal_error!("symbol {symbol:?} does not have an rc type") + } } } symbol_usage @@ -543,15 +544,16 @@ fn insert_refcount_operations_stmt<'v, 'a>( .iter() .filter(|(_, o)| o.is_owned()) { - let error = "All symbols defined in the current environment should be in the environment of the branches."; let consumed = branch_envs .iter() .any(|branch_env: &&RefcountEnvironment<'v>| { - matches!( - branch_env.get_symbol_ownership(symbol).expect(error), - Ownership::Borrowed - ) + match branch_env.get_symbol_ownership(symbol) { + None => internal_error!( + "symbol {symbol:?} in the current env should be in the branch's env" + ), + Some(ownership) => matches!(ownership, Ownership::Borrowed), + } }); if consumed { // If the symbol is currently owned, and not in a some branches, it must be consumed in all branches @@ -880,16 +882,11 @@ fn insert_refcount_operations_binding<'a>( inc_owned!(arguments.iter().copied(), new_let) } - Expr::ExprBox { symbol } => { - let new_let = new_let!(stmt); - - inc_owned!([*symbol], new_let) - } Expr::GetTagId { structure, .. } | Expr::StructAtIndex { structure, .. } | Expr::UnionAtIndex { structure, .. } - | Expr::ExprUnbox { symbol: structure } => { + | Expr::UnionFieldPtrAtIndex { structure, .. } => { // All structures are alive at this point and don't have to be copied in order to take an index out/get tag id/copy values to the stack. // But we do want to make sure to decrement this item if it is the last reference. let new_stmt = dec_borrowed!([*structure], stmt); @@ -902,7 +899,9 @@ fn insert_refcount_operations_binding<'a>( match expr { Expr::StructAtIndex { .. } | Expr::UnionAtIndex { .. } - | Expr::ExprUnbox { .. } => insert_inc_stmt(arena, *binding, 1, new_stmt), + | Expr::UnionFieldPtrAtIndex { .. } => { + insert_inc_stmt(arena, *binding, 1, new_stmt) + } // No usage of an element of a reference counted symbol. No need to increment. Expr::GetTagId { .. } => new_stmt, _ => unreachable!("Unexpected expression type"), @@ -1089,8 +1088,8 @@ fn insert_refcount_operations_binding<'a>( } } } - Expr::Reuse { .. } | Expr::Reset { .. } | Expr::ResetRef { .. } => { - unreachable!("Reset(ref) and reuse should not exist at this point") + Expr::Reset { .. } | Expr::ResetRef { .. } => { + unreachable!("Reset(ref) should not exist at this point") } } } diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index bd9a0975b79..4d8b4182b61 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -74,8 +74,8 @@ roc_error_macros::assert_sizeof_wasm!(Call, 44); roc_error_macros::assert_sizeof_wasm!(CallType, 36); roc_error_macros::assert_sizeof_non_wasm!(Literal, 3 * 8); -roc_error_macros::assert_sizeof_non_wasm!(Expr, 9 * 8); -roc_error_macros::assert_sizeof_non_wasm!(Stmt, 12 * 8); +roc_error_macros::assert_sizeof_non_wasm!(Expr, 10 * 8); +roc_error_macros::assert_sizeof_non_wasm!(Stmt, 13 * 8); roc_error_macros::assert_sizeof_non_wasm!(ProcLayout, 5 * 8); roc_error_macros::assert_sizeof_non_wasm!(Call, 9 * 8); roc_error_macros::assert_sizeof_non_wasm!(CallType, 7 * 8); @@ -181,8 +181,7 @@ impl<'a> PartialProcs<'a> { pub fn insert(&mut self, symbol: Symbol, partial_proc: PartialProc<'a>) -> PartialProcId { debug_assert!( !self.contains_key(symbol), - "The {:?} is inserted as a partial proc twice: that's a bug!", - symbol, + "The {symbol:?} is inserted as a partial proc twice: that's a bug!", ); let id = PartialProcId(self.symbols.len()); @@ -403,34 +402,6 @@ impl<'a> Proc<'a> { w.push(b'\n'); String::from_utf8(w).unwrap() } - - fn make_tail_recursive(&mut self, env: &mut Env<'a, '_>) { - let mut args = Vec::with_capacity_in(self.args.len(), env.arena); - let mut proc_args = Vec::with_capacity_in(self.args.len(), env.arena); - - for (layout, symbol) in self.args { - let new = env.unique_symbol(); - args.push((*layout, *symbol, new)); - proc_args.push((*layout, new)); - } - - use self::SelfRecursive::*; - if let SelfRecursive(id) = self.is_self_recursive { - let transformed = crate::tail_recursion::make_tail_recursive( - env.arena, - id, - self.name, - self.body.clone(), - args.into_bump_slice(), - self.ret_layout, - ); - - if let Some(with_tco) = transformed { - self.body = with_tco; - self.args = proc_args.into_bump_slice(); - } - } - } } /// A host-exposed function must be specialized; it's a seed for subsequent specializations @@ -703,7 +674,7 @@ impl<'a> Specialized<'a> { } else { match in_progress { InProgressProc::InProgress => { - panic!("Function {:?} ({:?}) is not done specializing", s, l) + panic!("Function {s:?} ({l:?}) is not done specializing") } InProgressProc::Done(proc) => Some((s, l, proc)), } @@ -866,11 +837,7 @@ impl<'a> SymbolSpecializations<'a> { /// Only those bound to number literals can be compiled polymorphically. fn mark_eligible(&mut self, symbol: Symbol) { let _old = self.0.insert(symbol, VecMap::with_capacity(1)); - debug_assert!( - _old.is_none(), - "overwriting specializations for {:?}", - symbol - ); + debug_assert!(_old.is_none(), "overwriting specializations for {symbol:?}"); } /// Removes all specializations for a symbol, returning the type and symbol of each specialization. @@ -973,8 +940,7 @@ impl<'a> Procs<'a> { .expect("specialization stack is empty"); debug_assert_eq!( popped, specialization, - "incorrect popped specialization: passed {:?}, but was {:?}", - specialization, popped + "incorrect popped specialization: passed {specialization:?}, but was {popped:?}" ); } @@ -1018,14 +984,11 @@ impl<'a> Procs<'a> { pub fn get_specialized_procs_without_rc( self, - env: &mut Env<'a, '_>, ) -> (MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>, ProcsBase<'a>) { let mut specialized_procs = MutMap::with_capacity_and_hasher(self.specialized.len(), default_hasher()); - for (symbol, layout, mut proc) in self.specialized.into_iter_assert_done() { - proc.make_tail_recursive(env); - + for (symbol, layout, proc) in self.specialized.into_iter_assert_done() { let key = (symbol, layout); specialized_procs.insert(key, proc); } @@ -1057,7 +1020,7 @@ impl<'a> Procs<'a> { ) -> Result, RuntimeError> { let raw_layout = layout_cache .raw_from_var(env.arena, annotation, env.subs) - .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); + .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {err:?}")); let top_level = ProcLayout::from_raw_named(env.arena, name, raw_layout); @@ -1085,7 +1048,7 @@ impl<'a> Procs<'a> { // if we've already specialized this one, no further work is needed. if !already_specialized { if self.is_module_thunk(name.name()) { - debug_assert!(layout.arguments.is_empty(), "{:?}", name); + debug_assert!(layout.arguments.is_empty(), "{name:?}"); } let needs_suspended_specialization = @@ -1176,7 +1139,7 @@ impl<'a> Procs<'a> { ); } Err(error) => { - panic!("TODO generate a RuntimeError message for {:?}", error); + panic!("TODO generate a RuntimeError message for {error:?}"); } } } @@ -1265,7 +1228,7 @@ impl<'a> Procs<'a> { .insert_specialized(proc_name.name(), proc_layout, proc); } Err(error) => { - panic!("TODO generate a RuntimeError message for {:?}", error); + panic!("TODO generate a RuntimeError message for {error:?}"); } } } @@ -1396,6 +1359,11 @@ impl<'a, 'i> Env<'a, 'i> { Symbol::new(self.home, ident_id) } + pub fn named_unique_symbol(&mut self, name: &str) -> Symbol { + let ident_id = self.ident_ids.add_str(name); + Symbol::new(self.home, ident_id) + } + pub fn next_update_mode_id(&mut self) -> UpdateModeId { self.update_mode_ids.next_id() } @@ -1638,6 +1606,9 @@ pub enum ModifyRc { /// sometimes we know we already dealt with the elements (e.g. by copying them all over /// to a new list) and so we can just do a DecRef, which is much cheaper in such a case. DecRef(Symbol), + /// Unconditionally deallocate the memory. For tag union that do pointer tagging (store the tag + /// id in the pointer) the backend has to clear the tag id! + Free(Symbol), } impl ModifyRc { @@ -1667,6 +1638,10 @@ impl ModifyRc { .text("decref ") .append(symbol_to_doc(alloc, symbol, pretty)) .append(";"), + Free(symbol) => alloc + .text("free ") + .append(symbol_to_doc(alloc, symbol, pretty)) + .append(";"), } } @@ -1677,6 +1652,7 @@ impl ModifyRc { Inc(symbol, _) => *symbol, Dec(symbol) => *symbol, DecRef(symbol) => *symbol, + Free(symbol) => *symbol, } } } @@ -1843,6 +1819,13 @@ pub struct HigherOrderLowLevel<'a> { pub passed_function: PassedFunction<'a>, } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ReuseToken { + pub symbol: Symbol, + pub update_tag_id: bool, + pub update_mode: UpdateModeId, +} + #[derive(Clone, Debug, PartialEq)] pub enum Expr<'a> { Literal(Literal<'a>), @@ -1854,6 +1837,7 @@ pub enum Expr<'a> { tag_layout: UnionLayout<'a>, tag_id: TagIdIntType, arguments: &'a [Symbol], + reuse: Option, }, Struct(&'a [Symbol]), NullPointer, @@ -1875,6 +1859,12 @@ pub enum Expr<'a> { union_layout: UnionLayout<'a>, index: u64, }, + UnionFieldPtrAtIndex { + structure: Symbol, + tag_id: TagIdIntType, + union_layout: UnionLayout<'a>, + index: u64, + }, Array { elem_layout: InLayout<'a>, @@ -1882,23 +1872,6 @@ pub enum Expr<'a> { }, EmptyArray, - ExprBox { - symbol: Symbol, - }, - - ExprUnbox { - symbol: Symbol, - }, - - Reuse { - symbol: Symbol, - update_tag_id: bool, - update_mode: UpdateModeId, - // normal Tag fields - tag_layout: UnionLayout<'a>, - tag_id: TagIdIntType, - arguments: &'a [Symbol], - }, Reset { symbol: Symbol, update_mode: UpdateModeId, @@ -1939,13 +1912,13 @@ pub(crate) fn symbol_to_doc_string(symbol: Symbol, force_pretty: bool) -> String use roc_module::ident::ModuleName; if pretty_print_ir_symbols() || force_pretty { - format!("{:?}", symbol) + format!("{symbol:?}") } else { - let text = format!("{}", symbol); + let text = format!("{symbol}"); if text.starts_with(ModuleName::APP) { let name: String = text.trim_start_matches(ModuleName::APP).into(); - format!("Test{}", name) + format!("Test{name}") } else { text } @@ -1989,7 +1962,10 @@ impl<'a> Expr<'a> { Call(call) => call.to_doc(alloc, pretty), Tag { - tag_id, arguments, .. + tag_id, + arguments, + reuse: None, + .. } => { let doc_tag = alloc .text("TagId(") @@ -2002,12 +1978,11 @@ impl<'a> Expr<'a> { .append(alloc.space()) .append(alloc.intersperse(it, " ")) } - NullPointer => alloc.text("NullPointer"), - Reuse { - symbol, + + Tag { tag_id, arguments, - update_mode, + reuse: Some(reuse_token), .. } => { let doc_tag = alloc @@ -2019,14 +1994,15 @@ impl<'a> Expr<'a> { alloc .text("Reuse ") - .append(symbol_to_doc(alloc, *symbol, pretty)) + .append(symbol_to_doc(alloc, reuse_token.symbol, pretty)) .append(alloc.space()) - .append(format!("{:?}", update_mode)) + .append(format!("{:?}", reuse_token.update_mode)) .append(alloc.space()) .append(doc_tag) .append(alloc.space()) .append(alloc.intersperse(it, " ")) } + NullPointer => alloc.text("NullPointer"), Reset { symbol, update_mode, @@ -2034,7 +2010,7 @@ impl<'a> Expr<'a> { .text("Reset { symbol: ") .append(symbol_to_doc(alloc, *symbol, pretty)) .append(", id: ") - .append(format!("{:?}", update_mode)) + .append(format!("{update_mode:?}")) .append(" }"), ResetRef { symbol, @@ -2043,7 +2019,7 @@ impl<'a> Expr<'a> { .text("ResetRef { symbol: ") .append(symbol_to_doc(alloc, *symbol, pretty)) .append(", id: ") - .append(format!("{:?}", update_mode)) + .append(format!("{update_mode:?}")) .append(" }"), Struct(args) => { let it = args.iter().map(|s| symbol_to_doc(alloc, *s, pretty)); @@ -2077,14 +2053,6 @@ impl<'a> Expr<'a> { .text("GetTagId ") .append(symbol_to_doc(alloc, *structure, pretty)), - ExprBox { symbol, .. } => alloc - .text("Box ") - .append(symbol_to_doc(alloc, *symbol, pretty)), - - ExprUnbox { symbol, .. } => alloc - .text("Unbox ") - .append(symbol_to_doc(alloc, *symbol, pretty)), - UnionAtIndex { tag_id, structure, @@ -2092,6 +2060,19 @@ impl<'a> Expr<'a> { .. } => text!(alloc, "UnionAtIndex (Id {}) (Index {}) ", tag_id, index) .append(symbol_to_doc(alloc, *structure, pretty)), + + UnionFieldPtrAtIndex { + tag_id, + structure, + index, + .. + } => text!( + alloc, + "UnionFieldPtrAtIndex (Id {}) (Index {}) ", + tag_id, + index + ) + .append(symbol_to_doc(alloc, *structure, pretty)), } } @@ -2105,6 +2086,34 @@ impl<'a> Expr<'a> { w.push(b'\n'); String::from_utf8(w).unwrap() } + + pub(crate) fn ptr_load(symbol: &'a Symbol) -> Expr<'a> { + Expr::Call(Call { + call_type: CallType::LowLevel { + op: LowLevel::PtrLoad, + update_mode: UpdateModeId::BACKEND_DUMMY, + }, + arguments: std::slice::from_ref(symbol), + }) + } + + pub(crate) fn expr_box(symbol: &'a Symbol, element_layout: &'a InLayout<'a>) -> Expr<'a> { + Expr::Tag { + tag_layout: UnionLayout::NonNullableUnwrapped(std::slice::from_ref(element_layout)), + tag_id: 0, + arguments: std::slice::from_ref(symbol), + reuse: None, + } + } + + pub(crate) fn expr_unbox(symbol: Symbol, element_layout: &'a InLayout<'a>) -> Expr<'a> { + Expr::UnionAtIndex { + structure: symbol, + tag_id: 0, + union_layout: UnionLayout::NonNullableUnwrapped(std::slice::from_ref(element_layout)), + index: 0, + } + } } impl<'a> Stmt<'a> { @@ -3021,7 +3030,7 @@ fn specialize_external_help<'a>( let partial_proc_id = match procs.partial_procs.symbol_to_id(name.name()) { Some(v) => v, None => { - panic!("Cannot find a partial proc for {:?}", name); + panic!("Cannot find a partial proc for {name:?}"); } }; @@ -3750,8 +3759,7 @@ fn build_specialized_proc<'a>( debug_assert_eq!( pattern_layouts_len + 1, pattern_symbols.len(), - "Tried to zip two vecs with different lengths in {:?}!", - proc_name, + "Tried to zip two vecs with different lengths in {proc_name:?}!", ); let proc_args = proc_args.into_bump_slice(); @@ -3791,14 +3799,12 @@ fn build_specialized_proc<'a>( // so far, the problem when hitting this branch was always somewhere else // I think this branch should not be reachable in a bugfree compiler panic!( - "more arguments (according to the layout) than argument symbols for {:?}", - proc_name + "more arguments (according to the layout) than argument symbols for {proc_name:?}" ) } } Ordering::Less => panic!( - "more argument symbols than arguments (according to the layout) for {:?}", - proc_name + "more argument symbols than arguments (according to the layout) for {proc_name:?}" ), } } @@ -3830,14 +3836,12 @@ fn build_specialized_proc<'a>( // so far, the problem when hitting this branch was always somewhere else // I think this branch should not be reachable in a bugfree compiler panic!( - "more arguments (according to the layout) than argument symbols for {:?}", - proc_name + "more arguments (according to the layout) than argument symbols for {proc_name:?}" ) } } Ordering::Less => panic!( - "more argument symbols than arguments (according to the layout) for {:?}", - proc_name + "more argument symbols than arguments (according to the layout) for {proc_name:?}" ), } } @@ -3866,7 +3870,7 @@ fn specialize_variable<'a>( // TODO: can we get rid of raw entirely? let raw = layout_cache .raw_from_var(env.arena, fn_var, env.subs) - .unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err)); + .unwrap_or_else(|err| panic!("TODO handle invalid function {err:?}")); let raw = if procs.is_module_thunk(proc_name.name()) { match raw { @@ -4028,7 +4032,7 @@ fn specialize_naked_symbol<'a>( return result; } else if env.is_imported_symbol(symbol) { match layout_cache.from_var(env.arena, variable, env.subs) { - Err(e) => panic!("invalid layout {:?}", e), + Err(e) => panic!("invalid layout {e:?}"), Ok(_) => { // this is a 0-arity thunk let result = call_by_name( @@ -4558,7 +4562,7 @@ pub fn with_hole<'a>( let layout = layout_cache .from_var(env.arena, branch_var, env.subs) .unwrap_or_else(|err| { - panic!("TODO turn fn_var into a RuntimeError {:?}", err) + panic!("TODO turn fn_var into a RuntimeError {err:?}") }); let param = Param { @@ -4624,7 +4628,7 @@ pub fn with_hole<'a>( let layout = layout_cache .from_var(env.arena, expr_var, env.subs) - .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); + .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {err:?}")); let param = Param { symbol: assigned, @@ -4677,7 +4681,7 @@ pub fn with_hole<'a>( let elem_layout = layout_cache .from_var(env.arena, elem_var, env.subs) - .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); + .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {err:?}")); for arg_expr in loc_elems.into_iter() { if let Some(literal) = @@ -5018,7 +5022,7 @@ pub fn with_hole<'a>( let record_layout = layout_cache .from_var(env.arena, record_var, env.subs) - .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); + .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {err:?}")); let field_layouts = match layout_cache.get_repr(record_layout) { LayoutRepr::Struct(field_layouts) => field_layouts, @@ -5166,7 +5170,7 @@ pub fn with_hole<'a>( if let Err(e) = inserted { return runtime_error( env, - env.arena.alloc(format!("RuntimeError: {:?}", e,)), + env.arena.alloc(format!("RuntimeError: {e:?}",)), ); } else { drop(inserted); @@ -5601,13 +5605,22 @@ pub fn with_hole<'a>( debug_assert_eq!(arg_symbols.len(), 1); let x = arg_symbols[0]; - Stmt::Let(assigned, Expr::ExprBox { symbol: x }, layout, hole) + let element_layout = match layout_cache.interner.get_repr(layout) { + LayoutRepr::Union(UnionLayout::NonNullableUnwrapped([l])) => l, + _ => unreachable!("invalid layout for a box expression"), + }; + + let expr = Expr::expr_box(arena.alloc(x), element_layout); + + Stmt::Let(assigned, expr, layout, hole) } UnboxExpr => { debug_assert_eq!(arg_symbols.len(), 1); let x = arg_symbols[0]; - Stmt::Let(assigned, Expr::ExprUnbox { symbol: x }, layout, hole) + let expr = Expr::expr_unbox(x, arena.alloc(layout)); + + Stmt::Let(assigned, expr, layout, hole) } _ => { let call = self::Call { @@ -5730,7 +5743,7 @@ fn compile_struct_like_access<'a>( let layout = layout_cache .from_var(env.arena, elem_var, env.subs) - .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); + .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {err:?}")); Stmt::Let(assigned, expr, layout, hole) } @@ -6004,6 +6017,7 @@ where tag_id, tag_layout: union_layout, arguments: symbols, + reuse: None, }; Stmt::Let(assigned, expr, lambda_set_layout, env.arena.alloc(hole)) @@ -6172,7 +6186,7 @@ fn convert_tag_union<'a>( // Layout will unpack this unwrapped tack if it only has one (non-zero-sized) field let layout = layout_cache .from_var(env.arena, variant_var, env.subs) - .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); + .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {err:?}")); // even though this was originally a Tag, we treat it as a Struct from now on let stmt = if let [only_field] = field_symbols { @@ -6206,7 +6220,7 @@ fn convert_tag_union<'a>( // Layout will unpack this unwrapped tack if it only has one (non-zero-sized) field let layout = layout_cache .from_var(env.arena, variant_var, env.subs) - .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err)); + .unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {err:?}")); // even though this was originally a Tag, we treat it as a Struct from now on let stmt = if let [only_field] = field_symbols { @@ -6273,6 +6287,7 @@ fn convert_tag_union<'a>( tag_layout: union_layout, tag_id: tag_id as _, arguments: field_symbols, + reuse: None, }; (tag, union_layout) @@ -6295,6 +6310,7 @@ fn convert_tag_union<'a>( tag_layout: union_layout, tag_id: tag_id as _, arguments: field_symbols, + reuse: None, }; (tag, union_layout) @@ -6319,6 +6335,7 @@ fn convert_tag_union<'a>( tag_layout: union_layout, tag_id: tag_id as _, arguments: field_symbols, + reuse: None, }; (tag, union_layout) @@ -6345,6 +6362,7 @@ fn convert_tag_union<'a>( tag_layout: union_layout, tag_id: tag_id as _, arguments: field_symbols, + reuse: None, }; (tag, union_layout) @@ -6362,6 +6380,7 @@ fn convert_tag_union<'a>( tag_layout: union_layout, tag_id: tag_id as _, arguments: field_symbols, + reuse: None, }; (tag, union_layout) @@ -6466,8 +6485,7 @@ fn tag_union_to_function<'a>( Err(e) => runtime_error( env, env.arena.alloc(format!( - "Could not produce tag function due to a runtime error: {:?}", - e, + "Could not produce tag function due to a runtime error: {e:?}", )), ), } @@ -7531,6 +7549,7 @@ fn substitute_in_expr<'a>( tag_layout, tag_id, arguments: args, + reuse, } => { let mut did_change = false; let new_args = Vec::from_iter_in( @@ -7544,6 +7563,18 @@ fn substitute_in_expr<'a>( arena, ); + let reuse = match *reuse { + Some(mut ru) => match substitute(subs, ru.symbol) { + Some(s) => { + did_change = true; + ru.symbol = s; + Some(ru) + } + None => Some(ru), + }, + None => None, + }; + if did_change { let arguments = new_args.into_bump_slice(); @@ -7551,6 +7582,7 @@ fn substitute_in_expr<'a>( tag_layout: *tag_layout, tag_id: *tag_id, arguments, + reuse, }) } else { None @@ -7559,8 +7591,8 @@ fn substitute_in_expr<'a>( NullPointer => None, - Reuse { .. } | Reset { .. } | ResetRef { .. } => { - unreachable!("reset/resetref/reuse have not been introduced yet") + Reset { .. } | ResetRef { .. } => { + unreachable!("reset(ref) has not been introduced yet") } Struct(args) => { @@ -7619,14 +7651,6 @@ fn substitute_in_expr<'a>( } } - ExprBox { symbol } => { - substitute(subs, *symbol).map(|new_symbol| ExprBox { symbol: new_symbol }) - } - - ExprUnbox { symbol } => { - substitute(subs, *symbol).map(|new_symbol| ExprUnbox { symbol: new_symbol }) - } - StructAtIndex { index, structure, @@ -7665,6 +7689,22 @@ fn substitute_in_expr<'a>( }), None => None, }, + + // currently only used for tail recursion modulo cons (TRMC) + UnionFieldPtrAtIndex { + structure, + tag_id, + index, + union_layout, + } => match substitute(subs, *structure) { + Some(structure) => Some(UnionFieldPtrAtIndex { + structure, + tag_id: *tag_id, + index: *index, + union_layout: *union_layout, + }), + None => None, + }, } } @@ -8232,17 +8272,13 @@ fn call_by_name<'a>( match layout_cache.raw_from_var(env.arena, fn_var, env.subs) { Err(LayoutProblem::UnresolvedTypeVar(var)) => { let msg = format!( - "Hit an unresolved type variable {:?} when creating a layout for {:?} (var {:?})", - var, proc_name, fn_var + "Hit an unresolved type variable {var:?} when creating a layout for {proc_name:?} (var {fn_var:?})" ); evaluate_arguments_then_runtime_error(env, procs, layout_cache, msg, loc_args) } Err(LayoutProblem::Erroneous) => { - let msg = format!( - "Hit an erroneous type when creating a layout for {:?}", - proc_name - ); + let msg = format!("Hit an erroneous type when creating a layout for {proc_name:?}"); evaluate_arguments_then_runtime_error(env, procs, layout_cache, msg, loc_args) } @@ -8394,9 +8430,7 @@ fn call_by_name_help<'a>( Some(name) => { debug_assert!( iter_lambda_names.next().is_none(), - "Somehow, call by name for {:?} has multiple capture niches: {:?}", - proc_name, - lambda_set + "Somehow, call by name for {proc_name:?} has multiple capture niches: {lambda_set:?}" ); name } @@ -8435,8 +8469,7 @@ fn call_by_name_help<'a>( debug_assert_eq!( argument_layouts.len(), field_symbols.len(), - "see call_by_name for background (scroll down a bit), function is {:?}", - proc_name, + "see call_by_name for background (scroll down a bit), function is {proc_name:?}", ); call_specialized_proc( env, @@ -8487,8 +8520,7 @@ fn call_by_name_help<'a>( debug_assert_eq!( argument_layouts.len(), field_symbols.len(), - "see call_by_name for background (scroll down a bit), function is {:?}", - proc_name, + "see call_by_name for background (scroll down a bit), function is {proc_name:?}", ); let field_symbols = field_symbols.into_bump_slice(); @@ -8540,8 +8572,7 @@ fn call_by_name_help<'a>( debug_assert_eq!( argument_layouts.len(), field_symbols.len(), - "see call_by_name for background (scroll down a bit), function is {:?}", - proc_name, + "see call_by_name for background (scroll down a bit), function is {proc_name:?}", ); let field_symbols = field_symbols.into_bump_slice(); @@ -8732,8 +8763,7 @@ fn call_by_name_module_thunk<'a>( Ok((proc, raw_layout)) => { debug_assert!( raw_layout.is_zero_argument_thunk(), - "but actually {:?}", - raw_layout + "but actually {raw_layout:?}" ); let was_present = procs @@ -8992,10 +9022,9 @@ where } None => { eprintln!( - "a function passed to `{:?}` LowLevel call has an empty lambda set! + "a function passed to `{op:?}` LowLevel call has an empty lambda set! The most likely reason is that some symbol you use is not in scope. - ", - op + " ); hole.clone() @@ -9863,8 +9892,8 @@ where stack.push(layout_interner.get(*in_layout)); } } - LayoutRepr::Boxed(boxed) => { - stack.push(layout_interner.get(boxed)); + LayoutRepr::Ptr(inner) => { + stack.push(layout_interner.get(inner)); } LayoutRepr::Union(union_layout) => match union_layout { UnionLayout::NonRecursive(tags) => { @@ -9933,8 +9962,9 @@ where I: LayoutInterner<'a>, { let interned_unboxed_struct_layout = layout_interner.insert(*unboxed_struct_layout); - let boxed_struct_layout = - Layout::no_semantic(LayoutRepr::Boxed(interned_unboxed_struct_layout).direct()); + let union_layout = + UnionLayout::NonNullableUnwrapped(arena.alloc([interned_unboxed_struct_layout])); + let boxed_struct_layout = Layout::no_semantic(LayoutRepr::Union(union_layout).direct()); let boxed_struct_layout = layout_interner.insert(boxed_struct_layout); let mut answer = bumpalo::collections::Vec::with_capacity_in(field_layouts.len(), arena); @@ -9974,7 +10004,7 @@ where let field_get_stmt = Stmt::Let(result, field_get_expr, *field, ret_stmt); - let unbox_expr = Expr::ExprUnbox { symbol: argument }; + let unbox_expr = Expr::expr_unbox(argument, arena.alloc(interned_unboxed_struct_layout)); let unbox_stmt = Stmt::Let( unboxed, @@ -10020,7 +10050,7 @@ fn unique_glue_symbol( use std::fmt::Write; let mut string = bumpalo::collections::String::with_capacity_in(32, arena); - let _result = write!(&mut string, "roc__getter_{}_{}", module_name, unique_id); + let _result = write!(&mut string, "roc__getter_{module_name}_{unique_id}"); debug_assert_eq!(_result, Ok(())); // This should never fail, but doesn't hurt to debug-check! let bump_string = string.into_bump_str(); @@ -10045,7 +10075,8 @@ where I: LayoutInterner<'a>, { let interned = layout_interner.insert(*unboxed_struct_layout); - let boxed_struct_layout = Layout::no_semantic(LayoutRepr::Boxed(interned).direct()); + let box_union_layout = UnionLayout::NonNullableUnwrapped(arena.alloc([interned])); + let boxed_struct_layout = Layout::no_semantic(LayoutRepr::Union(box_union_layout).direct()); let boxed_struct_layout = layout_interner.insert(boxed_struct_layout); let mut answer = bumpalo::collections::Vec::with_capacity_in(field_layouts.len(), arena); @@ -10074,8 +10105,7 @@ where let field_get_stmt = Stmt::Let(result, field_get_expr, *field, ret_stmt); - let unbox_expr = Expr::ExprUnbox { symbol: argument }; - + let unbox_expr = Expr::expr_unbox(argument, arena.alloc(interned)); let unbox_stmt = Stmt::Let(unboxed, unbox_expr, interned, arena.alloc(field_get_stmt)); let proc = Proc { diff --git a/crates/compiler/mono/src/ir/decision_tree.rs b/crates/compiler/mono/src/ir/decision_tree.rs index b34141e618f..76ba8e6a795 100644 --- a/crates/compiler/mono/src/ir/decision_tree.rs +++ b/crates/compiler/mono/src/ir/decision_tree.rs @@ -775,8 +775,7 @@ fn to_relevant_branch<'a>( // if there is no test, the pattern should not require any debug_assert!( matches!(pattern, Pattern::Identifier(_) | Pattern::Underscore,), - "{:?}", - pattern, + "{pattern:?}", ); Some(branch.clone()) diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 91cf9d02ba8..dbf9704f7d2 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -165,8 +165,7 @@ impl<'a> LayoutCache<'a> { let Cacheable(value, criteria) = Layout::from_var(&mut env, var); debug_assert!( criteria.is_cacheable(), - "{:?} not cacheable as top-level", - value + "{value:?} not cacheable as top-level" ); value } @@ -192,8 +191,7 @@ impl<'a> LayoutCache<'a> { let Cacheable(value, criteria) = RawFunctionLayout::from_var(&mut env, var); debug_assert!( criteria.is_cacheable(), - "{:?} not cacheable as top-level", - value + "{value:?} not cacheable as top-level" ); value } @@ -674,7 +672,9 @@ pub(crate) enum LayoutWrapper<'a> { pub enum LayoutRepr<'a> { Builtin(Builtin<'a>), Struct(&'a [InLayout<'a>]), - Boxed(InLayout<'a>), + // A pointer (heap or stack) without any reference counting + // Ptr is not user-facing. The compiler author must make sure that invariants are upheld + Ptr(InLayout<'a>), Union(UnionLayout<'a>), LambdaSet(LambdaSet<'a>), RecursivePointer(InLayout<'a>), @@ -1106,6 +1106,18 @@ impl<'a> UnionLayout<'a> { | UnionLayout::NullableUnwrapped { .. } => interner.target_info().ptr_width() as u32, } } + + pub fn is_recursive(&self) -> bool { + use UnionLayout::*; + + match self { + NonRecursive(_) => false, + Recursive(_) + | NonNullableUnwrapped(_) + | NullableWrapped { .. } + | NullableUnwrapped { .. } => true, + } + } } pub enum Discriminant { @@ -1471,9 +1483,7 @@ impl<'a> LambdaSet<'a> { { debug_assert!( self.contains(function_symbol), - "function symbol {:?} not in set {:?}", - function_symbol, - self + "function symbol {function_symbol:?} not in set {self:?}" ); let comparator = |other_name: Symbol, other_captures_layouts: &[InLayout<'a>]| { @@ -1898,6 +1908,11 @@ impl<'a> LambdaSet<'a> { ) -> Cacheable> { let union_labels = UnsortedUnionLabels { tags: set }; + // Even if a variant in the lambda set has uninhabitable captures (and is hence + // unreachable as a function), we want to keep it in the representation. Failing to do so + // risks dropping relevant specializations needed during monomorphization. + let drop_uninhabited_variants = DropUninhabitedVariants(false); + match opt_rec_var { Some(rec_var) => { let Cacheable(result, criteria) = @@ -1906,7 +1921,7 @@ impl<'a> LambdaSet<'a> { Cacheable(result, criteria) } - None => layout_from_non_recursive_union(env, &union_labels), + None => layout_from_non_recursive_union(env, &union_labels, drop_uninhabited_variants), } } @@ -2513,7 +2528,7 @@ impl<'a> LayoutRepr<'a> { pub const F64: Self = LayoutRepr::Builtin(Builtin::Float(FloatWidth::F64)); pub const DEC: Self = LayoutRepr::Builtin(Builtin::Decimal); pub const STR: Self = LayoutRepr::Builtin(Builtin::Str); - pub const OPAQUE_PTR: Self = LayoutRepr::Boxed(Layout::VOID); + pub const OPAQUE_PTR: Self = LayoutRepr::Ptr(Layout::VOID); pub const fn struct_(field_layouts: &'a [InLayout<'a>]) -> Self { Self::Struct(field_layouts) @@ -2555,7 +2570,7 @@ impl<'a> LayoutRepr<'a> { LambdaSet(lambda_set) => interner .get_repr(lambda_set.runtime_representation()) .safe_to_memcpy(interner), - Boxed(_) | RecursivePointer(_) => { + Ptr(_) | RecursivePointer(_) => { // We cannot memcpy pointers, because then we would have the same pointer in multiple places! false } @@ -2645,7 +2660,7 @@ impl<'a> LayoutRepr<'a> { .get_repr(lambda_set.runtime_representation()) .stack_size_without_alignment(interner), RecursivePointer(_) => interner.target_info().ptr_width() as u32, - Boxed(_) => interner.target_info().ptr_width() as u32, + Ptr(_) => interner.target_info().ptr_width() as u32, } } @@ -2698,7 +2713,7 @@ impl<'a> LayoutRepr<'a> { .alignment_bytes(interner), Builtin(builtin) => builtin.alignment_bytes(interner.target_info()), RecursivePointer(_) => interner.target_info().ptr_width() as u32, - Boxed(_) => interner.target_info().ptr_width() as u32, + Ptr(_) => interner.target_info().ptr_width() as u32, } } @@ -2719,10 +2734,7 @@ impl<'a> LayoutRepr<'a> { RecursivePointer(_) => { unreachable!("should be looked up to get an actual layout") } - Boxed(inner) => Ord::max( - ptr_width, - interner.get_repr(*inner).alignment_bytes(interner), - ), + Ptr(inner) => interner.get_repr(*inner).alignment_bytes(interner), } } @@ -2743,6 +2755,22 @@ impl<'a> LayoutRepr<'a> { } } + pub fn is_nullable(&self) -> bool { + use LayoutRepr::*; + + match self { + Union(union_layout) => match union_layout { + UnionLayout::NonRecursive(_) => false, + UnionLayout::Recursive(_) => false, + UnionLayout::NonNullableUnwrapped(_) => false, + UnionLayout::NullableWrapped { .. } => true, + UnionLayout::NullableUnwrapped { .. } => true, + }, + + _ => false, + } + } + /// Even if a value (say, a record) is not itself reference counted, /// it may contains values/fields that are. Therefore when this record /// goes out of scope, the refcount on those values/fields must be decremented. @@ -2775,7 +2803,11 @@ impl<'a> LayoutRepr<'a> { .get_repr(lambda_set.runtime_representation()) .contains_refcounted(interner), RecursivePointer(_) => true, - Boxed(_) => true, + Ptr(_) => { + // we never consider pointers for refcounting. Ptr is not user-facing. The compiler + // author must make sure that invariants are upheld + false + } } } @@ -2831,7 +2863,7 @@ impl<'a> LayoutRepr<'a> { } }, LambdaSet(_) => return true, - Boxed(_) => { + Ptr(_) => { // If there's any layer of indirection (behind a pointer), then it doesn't vary! } RecursivePointer(_) => { @@ -3161,18 +3193,20 @@ fn layout_from_flat_type<'a>( let inner_var = args[0]; let inner_layout = cached!(Layout::from_var(env, inner_var), criteria, env.subs); + + let repr = LayoutRepr::Union(UnionLayout::NonNullableUnwrapped( + arena.alloc([inner_layout]), + )); + let boxed_layout = env.cache.put_in(Layout { - repr: LayoutRepr::Boxed(inner_layout).direct(), + repr: repr.direct(), semantic: SemanticRepr::NONE, }); Cacheable(Ok(boxed_layout), criteria) } _ => { - panic!( - "TODO layout_from_flat_type for Apply({:?}, {:?})", - symbol, args - ); + panic!("TODO layout_from_flat_type for Apply({symbol:?}, {args:?})"); } } } @@ -3290,7 +3324,7 @@ fn layout_from_flat_type<'a>( debug_assert!(ext_var_is_empty_tag_union(subs, ext_var)); - layout_from_non_recursive_union(env, &tags).map(Ok) + layout_from_non_recursive_union(env, &tags, DropUninhabitedVariants(true)).map(Ok) } FunctionOrTagUnion(tag_names, _, ext_var) => { debug_assert!( @@ -3303,7 +3337,8 @@ fn layout_from_flat_type<'a>( tags: tag_names.iter().map(|t| (t, &[] as &[Variable])).collect(), }; - layout_from_non_recursive_union(env, &unsorted_tags).map(Ok) + layout_from_non_recursive_union(env, &unsorted_tags, DropUninhabitedVariants(true)) + .map(Ok) } RecursiveTagUnion(rec_var, tags, ext_var) => { let (tags, ext_var) = tags.unsorted_tags_and_ext(subs, ext_var); @@ -3581,11 +3616,14 @@ pub fn union_sorted_tags<'a>( var }; + let drop_uninhabited_variants = DropUninhabitedVariants(true); + let mut tags_vec = std::vec::Vec::new(); let result = match roc_types::pretty_print::chase_ext_tag_union(env.subs, var, &mut tags_vec) { ChasedExt::Empty => { let opt_rec_var = get_recursion_var(env.subs, var); - let Cacheable(result, _) = union_sorted_tags_help(env, tags_vec, opt_rec_var); + let Cacheable(result, _) = + union_sorted_tags_help(env, tags_vec, opt_rec_var, drop_uninhabited_variants); result } ChasedExt::NonEmpty { content, .. } => { @@ -3598,18 +3636,28 @@ pub fn union_sorted_tags<'a>( // x // In such cases it's fine to drop the variable. We may be proven wrong in the future... let opt_rec_var = get_recursion_var(env.subs, var); - let Cacheable(result, _) = union_sorted_tags_help(env, tags_vec, opt_rec_var); + let Cacheable(result, _) = union_sorted_tags_help( + env, + tags_vec, + opt_rec_var, + drop_uninhabited_variants, + ); result } RecursionVar { .. } => { let opt_rec_var = get_recursion_var(env.subs, var); - let Cacheable(result, _) = union_sorted_tags_help(env, tags_vec, opt_rec_var); + let Cacheable(result, _) = union_sorted_tags_help( + env, + tags_vec, + opt_rec_var, + drop_uninhabited_variants, + ); result } Error => return Err(LayoutProblem::Erroneous), - other => panic!("invalid content in tag union variable: {:?}", other), + other => panic!("invalid content in tag union variable: {other:?}"), } } }; @@ -3654,9 +3702,12 @@ impl Label for Symbol { } } +struct DropUninhabitedVariants(bool); + fn union_sorted_non_recursive_tags_help<'a, L>( env: &mut Env<'a, '_>, tags_list: &mut Vec<'_, &'_ (&'_ L, &[Variable])>, + drop_uninhabited_variants: DropUninhabitedVariants, ) -> Cacheable> where L: Label + Ord + Clone + Into, @@ -3776,7 +3827,7 @@ where answer.push((tag_name.clone().into(), arg_layouts.into_bump_slice())); } - if inhabited_tag_ids.count_ones() == 1 { + if inhabited_tag_ids.count_ones() == 1 && drop_uninhabited_variants.0 { let kept_tag_id = inhabited_tag_ids.first_one().unwrap(); let kept = answer.get(kept_tag_id).unwrap(); @@ -3829,13 +3880,14 @@ pub fn union_sorted_tags_pub<'a, L>( where L: Into + Ord + Clone, { - union_sorted_tags_help(env, tags_vec, opt_rec_var).value() + union_sorted_tags_help(env, tags_vec, opt_rec_var, DropUninhabitedVariants(true)).value() } fn union_sorted_tags_help<'a, L>( env: &mut Env<'a, '_>, mut tags_vec: std::vec::Vec<(L, std::vec::Vec)>, opt_rec_var: Option, + drop_uninhabited_variants: DropUninhabitedVariants, ) -> Cacheable> where L: Into + Ord + Clone, @@ -3988,7 +4040,7 @@ where answer.push((tag_name.into(), arg_layouts.into_bump_slice())); } - if inhabited_tag_ids.count_ones() == 1 && !is_recursive { + if inhabited_tag_ids.count_ones() == 1 && !is_recursive && drop_uninhabited_variants.0 { let kept_tag_id = inhabited_tag_ids.first_one().unwrap(); let kept = answer.get(kept_tag_id).unwrap(); @@ -4091,6 +4143,7 @@ fn layout_from_newtype<'a, L: Label>( fn layout_from_non_recursive_union<'a, L>( env: &mut Env<'a, '_>, tags: &UnsortedUnionLabels, + drop_uninhabited_variants: DropUninhabitedVariants, ) -> Cacheable> where L: Label + Ord + Into, @@ -4106,7 +4159,8 @@ where let mut criteria = CACHEABLE; let variant = - union_sorted_non_recursive_tags_help(env, &mut tags_vec).decompose(&mut criteria, env.subs); + union_sorted_non_recursive_tags_help(env, &mut tags_vec, drop_uninhabited_variants) + .decompose(&mut criteria, env.subs); let compute_semantic = || L::semantic_repr(env.arena, tags_vec.iter().map(|(l, _)| *l)); @@ -4326,7 +4380,7 @@ pub fn ext_var_is_empty_tag_union(subs: &Subs, tag_ext: TagExt) -> bool { } // So that we can continue compiling in the presence of errors Error => ext_fields.is_empty(), - _ => panic!("invalid content in ext_var: {:?}", content), + _ => panic!("invalid content in ext_var: {content:?}"), } } } @@ -4381,17 +4435,14 @@ fn layout_from_num_content<'a>( Symbol::NUM_DEC => Ok(Layout::DEC), _ => { - panic!( - "Invalid Num.Num type application: Apply({:?}, {:?})", - symbol, args - ); + panic!("Invalid Num.Num type application: Apply({symbol:?}, {args:?})"); } }, Alias(_, _, _, _) => { todo!("TODO recursively resolve type aliases in num_from_content"); } Structure(_) | RangedNumber(..) | LambdaSet(_) => { - panic!("Invalid Num.Num type application: {:?}", content); + panic!("Invalid Num.Num type application: {content:?}"); } Error => Err(LayoutProblem::Erroneous), }; diff --git a/crates/compiler/mono/src/layout/intern.rs b/crates/compiler/mono/src/layout/intern.rs index eb182b0b86a..321b4b2e355 100644 --- a/crates/compiler/mono/src/layout/intern.rs +++ b/crates/compiler/mono/src/layout/intern.rs @@ -76,7 +76,7 @@ cache_interned_layouts! { 16, STR, pub, nosema!(LayoutRepr::STR) 17, OPAQUE_PTR, pub, nosema!(LayoutRepr::OPAQUE_PTR) 18, NAKED_RECURSIVE_PTR, pub(super), nosema!(LayoutRepr::RecursivePointer(Layout::VOID)) - 19, STR_PTR, pub, nosema!(LayoutRepr::Boxed(Layout::STR)) + 19, STR_PTR, pub, nosema!(LayoutRepr::Ptr(Layout::STR)) 20, LIST_U8, pub, nosema!(LayoutRepr::Builtin(crate::layout::Builtin::List(Layout::U8))) ; 21 @@ -230,6 +230,10 @@ pub trait LayoutInterner<'a>: Sized { self.get_repr(layout).is_refcounted() } + fn is_nullable(&self, layout: InLayout<'a>) -> bool { + self.get_repr(layout).is_nullable() + } + fn is_passed_by_reference(&self, layout: InLayout<'a>) -> bool { self.get_repr(layout).is_passed_by_reference(self) } @@ -346,8 +350,8 @@ pub trait LayoutInterner<'a>: Sized { self.to_doc(rec_layout, alloc, seen_rec, parens) } } - Boxed(inner) => alloc - .text("Boxed(") + Ptr(inner) => alloc + .text("Ptr(") .append(self.to_doc(inner, alloc, seen_rec, parens)) .append(")"), } @@ -1107,7 +1111,7 @@ mod reify { LayoutRepr::Struct(field_layouts) => { LayoutRepr::Struct(reify_layout_slice(arena, interner, slot, field_layouts)) } - LayoutRepr::Boxed(lay) => LayoutRepr::Boxed(reify_layout(arena, interner, slot, lay)), + LayoutRepr::Ptr(lay) => LayoutRepr::Ptr(reify_layout(arena, interner, slot, lay)), LayoutRepr::Union(un) => LayoutRepr::Union(reify_union(arena, interner, slot, un)), LayoutRepr::LambdaSet(ls) => { LayoutRepr::LambdaSet(reify_lambda_set(arena, interner, slot, ls)) @@ -1311,7 +1315,7 @@ mod equiv { (Struct(fl1), Struct(fl2)) => { equiv_fields!(fl1, fl2) } - (Boxed(b1), Boxed(b2)) => stack.push((b1, b2)), + (Ptr(b1), Ptr(b2)) => stack.push((b1, b2)), (Union(u1), Union(u2)) => { use UnionLayout::*; match (u1, u2) { @@ -1431,7 +1435,7 @@ pub mod dbg_deep { .debug_struct("Struct") .field("fields", &DbgFields(self.0, field_layouts)) .finish(), - LayoutRepr::Boxed(b) => f.debug_tuple("Boxed").field(&Dbg(self.0, *b)).finish(), + LayoutRepr::Ptr(b) => f.debug_tuple("Ptr").field(&Dbg(self.0, *b)).finish(), LayoutRepr::Union(un) => f .debug_tuple("Union") .field(&DbgUnion(self.0, *un)) @@ -1604,7 +1608,7 @@ pub mod dbg_stable { .debug_struct("Struct") .field("fields", &DbgFields(self.0, field_layouts)) .finish(), - LayoutRepr::Boxed(b) => f.debug_tuple("Boxed").field(&Dbg(self.0, *b)).finish(), + LayoutRepr::Ptr(b) => f.debug_tuple("Ptr").field(&Dbg(self.0, *b)).finish(), LayoutRepr::Union(un) => f .debug_tuple("Union") .field(&DbgUnion(self.0, *un)) diff --git a/crates/compiler/mono/src/layout_soa.rs b/crates/compiler/mono/src/layout_soa.rs deleted file mode 100644 index 30dd83e1276..00000000000 --- a/crates/compiler/mono/src/layout_soa.rs +++ /dev/null @@ -1,907 +0,0 @@ -use crate::layout::{ext_var_is_empty_record, ext_var_is_empty_tag_union}; -use roc_builtins::bitcode::{FloatWidth, IntWidth}; -use roc_collections::all::MutMap; -use roc_module::symbol::Symbol; -use roc_target::TargetInfo; -use roc_types::subs::{self, Content, FlatType, Subs, Variable}; -use roc_types::types::RecordField; -use std::collections::hash_map::Entry; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Index { - index: u32, - _marker: std::marker::PhantomData, -} - -impl Index { - pub const fn new(index: u32) -> Self { - Self { - index, - _marker: std::marker::PhantomData, - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Slice { - start: u32, - length: u16, - _marker: std::marker::PhantomData, -} - -impl Slice { - pub const fn new(start: u32, length: u16) -> Self { - Self { - start, - length, - _marker: std::marker::PhantomData, - } - } - - pub const fn len(&self) -> usize { - self.length as _ - } - - pub const fn is_empty(&self) -> bool { - self.length == 0 - } - - pub const fn indices(&self) -> std::ops::Range { - self.start as usize..(self.start as usize + self.length as usize) - } - - pub fn into_iter(&self) -> impl Iterator> { - self.indices().map(|i| Index::new(i as _)) - } -} - -trait Reserve { - fn reserve(layouts: &mut Layouts, length: usize) -> Self; -} - -impl Reserve for Slice { - fn reserve(layouts: &mut Layouts, length: usize) -> Self { - let start = layouts.layouts.len() as u32; - - let it = std::iter::repeat(Layout::Reserved).take(length); - layouts.layouts.extend(it); - - Self { - start, - length: length as u16, - _marker: Default::default(), - } - } -} - -impl Reserve for Slice> { - fn reserve(layouts: &mut Layouts, length: usize) -> Self { - let start = layouts.layout_slices.len() as u32; - - let empty: Slice = Slice::new(0, 0); - let it = std::iter::repeat(empty).take(length); - layouts.layout_slices.extend(it); - - Self { - start, - length: length as u16, - _marker: Default::default(), - } - } -} - -static_assertions::assert_eq_size!([u8; 12], Layout); - -pub struct Layouts { - layouts: Vec, - layout_slices: Vec>, - // function_layouts: Vec<(Slice, Index)>, - lambda_sets: Vec, - symbols: Vec, - recursion_variable_to_structure_variable_map: MutMap>, - target_info: TargetInfo, -} - -pub struct FunctionLayout { - /// last element is the result, prior elements the arguments - arguments_and_result: Slice, - pub lambda_set: Index, -} - -impl FunctionLayout { - pub fn from_var( - layouts: &mut Layouts, - subs: &Subs, - var: Variable, - ) -> Result { - // so we can set some things/clean up - Self::from_var_help(layouts, subs, var) - } - - fn from_var_help( - layouts: &mut Layouts, - subs: &Subs, - var: Variable, - ) -> Result { - let content = &subs.get_content_without_compacting(var); - Self::from_content(layouts, subs, var, content) - } - - fn from_content( - layouts: &mut Layouts, - subs: &Subs, - var: Variable, - content: &Content, - ) -> Result { - use LayoutError::*; - - match content { - Content::FlexVar(_) - | Content::RigidVar(_) - | Content::FlexAbleVar(_, _) - | Content::RigidAbleVar(_, _) => Err(UnresolvedVariable(var)), - Content::RecursionVar { .. } => Err(TypeError(())), - Content::LambdaSet(lset) => Self::from_lambda_set(layouts, subs, *lset), - Content::Structure(flat_type) => Self::from_flat_type(layouts, subs, flat_type), - Content::Alias(_, _, actual, _) => Self::from_var_help(layouts, subs, *actual), - Content::RangedNumber(_) => todo!(), - Content::Error => Err(TypeError(())), - } - } - - fn from_lambda_set( - _layouts: &mut Layouts, - _subs: &Subs, - _lset: subs::LambdaSet, - ) -> Result { - todo!(); - } - - fn from_flat_type( - layouts: &mut Layouts, - subs: &Subs, - flat_type: &FlatType, - ) -> Result { - match flat_type { - FlatType::Func(arguments, lambda_set, result) => { - let slice = Slice::reserve(layouts, arguments.len() + 1); - - let variable_slice = &subs.variables[arguments.indices()]; - let it = slice.indices().zip(variable_slice); - for (target_index, var) in it { - let layout = Layout::from_var_help(layouts, subs, *var)?; - layouts.layouts[target_index] = layout; - } - - let result_layout = Layout::from_var_help(layouts, subs, *result)?; - let result_index: Index = Index::new(slice.start + slice.len() as u32 - 1); - layouts.layouts[result_index.index as usize] = result_layout; - - let lambda_set = LambdaSet::from_var(layouts, subs, *lambda_set)?; - let lambda_set_index = Index::new(layouts.lambda_sets.len() as u32); - layouts.lambda_sets.push(lambda_set); - - Ok(Self { - arguments_and_result: slice, - lambda_set: lambda_set_index, - }) - } - - _ => todo!(), - } - } - - pub fn argument_slice(&self) -> Slice { - let mut result = self.arguments_and_result; - result.length -= 1; - - result - } - pub fn result_index(&self) -> Index { - Index::new(self.arguments_and_result.start + self.arguments_and_result.length as u32 - 1) - } -} - -/// Idea: don't include the symbols for the first 3 cases in --optimize mode -pub enum LambdaSet { - Empty { - symbol: Index, - }, - Single { - symbol: Index, - layout: Index, - }, - Struct { - symbol: Index, - layouts: Slice, - }, - Union { - symbols: Slice, - layouts: Slice>, - }, -} - -impl LambdaSet { - pub fn from_var( - layouts: &mut Layouts, - subs: &Subs, - var: Variable, - ) -> Result { - // so we can set some things/clean up - Self::from_var_help(layouts, subs, var) - } - - fn from_var_help( - layouts: &mut Layouts, - subs: &Subs, - var: Variable, - ) -> Result { - let content = &subs.get_content_without_compacting(var); - Self::from_content(layouts, subs, var, content) - } - - fn from_content( - layouts: &mut Layouts, - subs: &Subs, - var: Variable, - content: &Content, - ) -> Result { - use LayoutError::*; - - match content { - Content::FlexVar(_) - | Content::RigidVar(_) - | Content::FlexAbleVar(_, _) - | Content::RigidAbleVar(_, _) => Err(UnresolvedVariable(var)), - Content::RecursionVar { .. } => { - unreachable!("lambda sets cannot currently be recursive") - } - Content::LambdaSet(lset) => Self::from_lambda_set(layouts, subs, *lset), - Content::Structure(_flat_type) => unreachable!(), - Content::Alias(_, _, actual, _) => Self::from_var_help(layouts, subs, *actual), - Content::RangedNumber(_) => todo!(), - Content::Error => Err(TypeError(())), - } - } - - fn from_lambda_set( - layouts: &mut Layouts, - subs: &Subs, - lset: subs::LambdaSet, - ) -> Result { - let subs::LambdaSet { - solved, - recursion_var: _, - unspecialized: _, - ambient_function: _, - } = lset; - - // TODO: handle unspecialized - - debug_assert!( - !solved.is_empty(), - "lambda set must contain atleast the function itself" - ); - - let lambda_names = solved.labels(); - let closure_names = Self::get_closure_names(layouts, subs, lambda_names); - - let variables = solved.variables(); - if variables.len() == 1 { - let symbol = subs.symbol_names[lambda_names.start as usize]; - let symbol_index = Index::new(layouts.symbols.len() as u32); - layouts.symbols.push(symbol); - let variable_slice = subs.variable_slices[variables.start as usize]; - - match variable_slice.len() { - 0 => Ok(LambdaSet::Empty { - symbol: symbol_index, - }), - 1 => { - let var = subs.variables[variable_slice.start as usize]; - let layout = Layout::from_var(layouts, subs, var)?; - - let index = Index::new(layouts.layouts.len() as u32); - layouts.layouts.push(layout); - - Ok(LambdaSet::Single { - symbol: symbol_index, - layout: index, - }) - } - _ => { - let slice = Layout::from_variable_slice(layouts, subs, variable_slice)?; - - Ok(LambdaSet::Struct { - symbol: symbol_index, - layouts: slice, - }) - } - } - } else { - let layouts = Layout::from_slice_variable_slice(layouts, subs, solved.variables())?; - - Ok(LambdaSet::Union { - symbols: closure_names, - layouts, - }) - } - } - - fn get_closure_names( - layouts: &mut Layouts, - subs: &Subs, - subs_slice: roc_types::subs::SubsSlice, - ) -> Slice { - let slice = Slice::new(layouts.symbols.len() as u32, subs_slice.len() as u16); - - let symbols = &subs.symbol_names[subs_slice.indices()]; - - for symbol in symbols { - layouts.symbols.push(*symbol); - } - - slice - } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Layout { - // theory: we can zero out memory to reserve space for many layouts - Reserved, - - // Question: where to store signedness information? - Int(IntWidth), - Float(FloatWidth), - Decimal, - - Str, - Dict(Index<(Layout, Layout)>), - Set(Index), - List(Index), - - Struct(Slice), - - UnionNonRecursive(Slice>), - - Boxed(Index), - UnionRecursive(Slice>), - // UnionNonNullableUnwrapped(Slice), - // UnionNullableWrapper { - // data: NullableUnionIndex, - // tag_id: u16, - // }, - // - // UnionNullableUnwrappedTrue(Slice), - // UnionNullableUnwrappedFalse(Slice), - - // RecursivePointer, -} - -fn round_up_to_alignment(unaligned: u16, alignment_bytes: u16) -> u16 { - let unaligned = unaligned as i32; - let alignment_bytes = alignment_bytes as i32; - if alignment_bytes <= 1 { - return unaligned as u16; - } - if alignment_bytes.count_ones() != 1 { - panic!( - "Cannot align to {} bytes. Not a power of 2.", - alignment_bytes - ); - } - let mut aligned = unaligned; - aligned += alignment_bytes - 1; // if lower bits are non-zero, push it over the next boundary - aligned &= -alignment_bytes; // mask with a flag that has upper bits 1, lower bits 0 - - aligned as u16 -} - -impl Layouts { - const VOID_INDEX: Index = Index::new(0); - const VOID_TUPLE: Index<(Layout, Layout)> = Index::new(0); - const UNIT_INDEX: Index = Index::new(2); - - pub fn new(target_info: TargetInfo) -> Self { - let mut layouts = Vec::with_capacity(64); - - layouts.push(Layout::VOID); - layouts.push(Layout::VOID); - layouts.push(Layout::UNIT); - - // sanity check - debug_assert_eq!(layouts[Self::VOID_INDEX.index as usize], Layout::VOID); - debug_assert_eq!(layouts[Self::VOID_TUPLE.index as usize + 1], Layout::VOID); - debug_assert_eq!(layouts[Self::UNIT_INDEX.index as usize], Layout::UNIT); - - Layouts { - layouts: Vec::default(), - layout_slices: Vec::default(), - lambda_sets: Vec::default(), - symbols: Vec::default(), - recursion_variable_to_structure_variable_map: MutMap::default(), - target_info, - } - } - - /// sort a slice according to elements' alignment - fn sort_slice_by_alignment(&mut self, layout_slice: Slice) { - let slice = &mut self.layouts[layout_slice.indices()]; - - // SAFETY: the align_of function does not mutate the layouts vector - // this unsafety is required to circumvent the borrow checker - let sneaky_slice = - unsafe { std::slice::from_raw_parts_mut(slice.as_mut_ptr(), slice.len()) }; - - sneaky_slice.sort_by(|layout1, layout2| { - let align1 = self.align_of_layout(*layout1); - let align2 = self.align_of_layout(*layout2); - - // we want the biggest alignment first - align2.cmp(&align1) - }); - } - - fn usize(&self) -> Layout { - let usize_int_width = match self.target_info.ptr_width() { - roc_target::PtrWidth::Bytes4 => IntWidth::U32, - roc_target::PtrWidth::Bytes8 => IntWidth::U64, - }; - - Layout::Int(usize_int_width) - } - - fn align_of_layout_index(&self, index: Index) -> u16 { - let layout = self.layouts[index.index as usize]; - - self.align_of_layout(layout) - } - - fn align_of_layout(&self, layout: Layout) -> u16 { - let usize_int_width = match self.target_info.ptr_width() { - roc_target::PtrWidth::Bytes4 => IntWidth::U32, - roc_target::PtrWidth::Bytes8 => IntWidth::U64, - }; - - let ptr_alignment = usize_int_width.alignment_bytes(self.target_info) as u16; - - match layout { - Layout::Reserved => unreachable!(), - Layout::Int(int_width) => int_width.alignment_bytes(self.target_info) as u16, - Layout::Float(float_width) => float_width.alignment_bytes(self.target_info) as u16, - Layout::Decimal => IntWidth::U128.alignment_bytes(self.target_info) as u16, - Layout::Str | Layout::Dict(_) | Layout::Set(_) | Layout::List(_) => ptr_alignment, - Layout::Struct(slice) => self.align_of_layout_slice(slice), - Layout::Boxed(_) | Layout::UnionRecursive(_) => ptr_alignment, - Layout::UnionNonRecursive(slices) => { - let tag_id_align = IntWidth::I64.alignment_bytes(self.target_info) as u16; - - self.align_of_layout_slices(slices).max(tag_id_align) - } -// Layout::UnionNonNullableUnwrapped(_) => todo!(), -// Layout::UnionNullableWrapper { data, tag_id } => todo!(), -// Layout::UnionNullableUnwrappedTrue(_) => todo!(), -// Layout::UnionNullableUnwrappedFalse(_) => todo!(), -// Layout::RecursivePointer => todo!(), - } - } - - /// Invariant: the layouts are sorted from biggest to smallest alignment - fn align_of_layout_slice(&self, slice: Slice) -> u16 { - match slice.into_iter().next() { - None => 0, - Some(first_index) => self.align_of_layout_index(first_index), - } - } - - fn align_of_layout_slices(&self, slice: Slice>) -> u16 { - slice - .into_iter() - .map(|index| self.layout_slices[index.index as usize]) - .map(|slice| self.align_of_layout_slice(slice)) - .max() - .unwrap_or_default() - } - - /// Invariant: the layouts are sorted from biggest to smallest alignment - fn size_of_layout_slice(&self, slice: Slice) -> u16 { - match slice.into_iter().next() { - None => 0, - Some(first_index) => { - let alignment = self.align_of_layout_index(first_index); - - let mut sum = 0; - - for index in slice.into_iter() { - sum += self.size_of_layout_index(index); - } - - round_up_to_alignment(sum, alignment) - } - } - } - - pub fn size_of_layout_index(&self, index: Index) -> u16 { - let layout = self.layouts[index.index as usize]; - - self.size_of_layout(layout) - } - - pub fn size_of_layout(&self, layout: Layout) -> u16 { - let usize_int_width = match self.target_info.ptr_width() { - roc_target::PtrWidth::Bytes4 => IntWidth::U32, - roc_target::PtrWidth::Bytes8 => IntWidth::U64, - }; - - let ptr_width = usize_int_width.stack_size() as u16; - - match layout { - Layout::Reserved => unreachable!(), - Layout::Int(int_width) => int_width.stack_size() as _, - Layout::Float(float_width) => float_width as _, - Layout::Decimal => (std::mem::size_of::()) as _, - Layout::Str | Layout::Dict(_) | Layout::Set(_) | Layout::List(_) => 2 * ptr_width, - Layout::Struct(slice) => self.size_of_layout_slice(slice), - Layout::Boxed(_) | Layout::UnionRecursive(_) => ptr_width, - Layout::UnionNonRecursive(slices) if slices.is_empty() => 0, - Layout::UnionNonRecursive(slices) => { - let tag_id = IntWidth::I64; - - let max_slice_size = slices - .into_iter() - .map(|index| self.layout_slices[index.index as usize]) - .map(|slice| self.align_of_layout_slice(slice)) - .max() - .unwrap_or_default(); - - tag_id.stack_size() as u16 + max_slice_size - } -// Layout::UnionNonNullableUnwrapped(_) => todo!(), -// Layout::UnionNullableWrapper { data, tag_id } => todo!(), -// Layout::UnionNullableUnwrappedTrue(_) => todo!(), -// Layout::UnionNullableUnwrappedFalse(_) => todo!(), -// Layout::RecursivePointer => todo!(), - } - } -} - -pub enum LayoutError { - UnresolvedVariable(Variable), - TypeError(()), -} - -impl Layout { - pub const UNIT: Self = Self::Struct(Slice::new(0, 0)); - pub const VOID: Self = Self::UnionNonRecursive(Slice::new(0, 0)); - - pub const EMPTY_LIST: Self = Self::List(Layouts::VOID_INDEX); - pub const EMPTY_DICT: Self = Self::Dict(Layouts::VOID_TUPLE); - pub const EMPTY_SET: Self = Self::Set(Layouts::VOID_INDEX); - - pub fn from_var( - layouts: &mut Layouts, - subs: &Subs, - var: Variable, - ) -> Result { - // so we can set some things/clean up - Self::from_var_help(layouts, subs, var) - } - - fn from_var_help( - layouts: &mut Layouts, - subs: &Subs, - var: Variable, - ) -> Result { - let content = &subs.get_content_without_compacting(var); - Self::from_content(layouts, subs, var, content) - } - - /// Used in situations where an unspecialized variable is not a problem, - /// and we can substitute with `[]`, the empty tag union. - /// e.g. an empty list literal has type `List *`. We can still generate code - /// in those cases by just picking any concrete type for the list element, - /// and we pick the empty tag union in practice. - fn from_var_help_or_void( - layouts: &mut Layouts, - subs: &Subs, - var: Variable, - ) -> Result { - let content = &subs.get_content_without_compacting(var); - - match content { - Content::FlexVar(_) | Content::RigidVar(_) => Ok(Layout::VOID), - - _ => Self::from_content(layouts, subs, var, content), - } - } - - fn from_content( - layouts: &mut Layouts, - subs: &Subs, - var: Variable, - content: &Content, - ) -> Result { - use LayoutError::*; - - match content { - Content::FlexVar(_) - | Content::RigidVar(_) - | Content::FlexAbleVar(_, _) - | Content::RigidAbleVar(_, _) => Err(UnresolvedVariable(var)), - Content::RecursionVar { - structure, - opt_name: _, - } => { - let structure = subs.get_root_key_without_compacting(*structure); - - let entry = layouts - .recursion_variable_to_structure_variable_map - .entry(structure); - - match entry { - Entry::Vacant(vacant) => { - let reserved = Index::new(layouts.layouts.len() as _); - layouts.layouts.push(Layout::Reserved); - - vacant.insert(reserved); - - let layout = Layout::from_var(layouts, subs, structure)?; - - layouts.layouts[reserved.index as usize] = layout; - - Ok(Layout::Boxed(reserved)) - } - Entry::Occupied(occupied) => { - let index = occupied.get(); - - Ok(Layout::Boxed(*index)) - } - } - } - // Lambda set layout is same as tag union - Content::LambdaSet(lset) => Self::from_lambda_set(layouts, subs, *lset), - Content::Structure(flat_type) => Self::from_flat_type(layouts, subs, flat_type), - Content::Alias(symbol, _, actual, _) => { - let symbol = *symbol; - - if let Some(int_width) = IntWidth::try_from_symbol(symbol) { - return Ok(Layout::Int(int_width)); - } - - if let Some(float_width) = FloatWidth::try_from_symbol(symbol) { - return Ok(Layout::Float(float_width)); - } - - match symbol { - Symbol::NUM_DECIMAL => Ok(Layout::Decimal), - - Symbol::NUM_NAT | Symbol::NUM_NATURAL => Ok(layouts.usize()), - - _ => { - // at this point we throw away alias information - Self::from_var_help(layouts, subs, *actual) - } - } - } - Content::RangedNumber(_) => todo!(), - Content::Error => Err(TypeError(())), - } - } - - fn from_lambda_set( - layouts: &mut Layouts, - subs: &Subs, - lset: subs::LambdaSet, - ) -> Result { - let subs::LambdaSet { - solved, - recursion_var, - unspecialized: _, - ambient_function: _, - } = lset; - - // TODO: handle unspecialized lambda set - - match recursion_var.into_variable() { - Some(rec_var) => { - let rec_var = subs.get_root_key_without_compacting(rec_var); - - let cached = layouts - .recursion_variable_to_structure_variable_map - .get(&rec_var); - - if let Some(layout_index) = cached { - match layouts.layouts[layout_index.index as usize] { - Layout::Reserved => { - // we have to do the work here to fill this reserved variable in - } - other => { - return Ok(other); - } - } - } - - let slices = Self::from_slice_variable_slice(layouts, subs, solved.variables())?; - - Ok(Layout::UnionRecursive(slices)) - } - None => { - let slices = Self::from_slice_variable_slice(layouts, subs, solved.variables())?; - - Ok(Layout::UnionNonRecursive(slices)) - } - } - } - - fn from_flat_type( - layouts: &mut Layouts, - subs: &Subs, - flat_type: &FlatType, - ) -> Result { - match flat_type { - FlatType::Apply(Symbol::LIST_LIST, arguments) => { - debug_assert_eq!(arguments.len(), 1); - - let element_var = subs.variables[arguments.start as usize]; - let element_layout = Self::from_var_help_or_void(layouts, subs, element_var)?; - - let element_index = Index::new(layouts.layouts.len() as _); - layouts.layouts.push(element_layout); - - Ok(Layout::List(element_index)) - } - - FlatType::Apply(Symbol::DICT_DICT, arguments) => { - debug_assert_eq!(arguments.len(), 2); - - let key_var = subs.variables[arguments.start as usize]; - let value_var = subs.variables[arguments.start as usize + 1]; - - let key_layout = Self::from_var_help_or_void(layouts, subs, key_var)?; - let value_layout = Self::from_var_help_or_void(layouts, subs, value_var)?; - - let index = Index::new(layouts.layouts.len() as _); - layouts.layouts.push(key_layout); - layouts.layouts.push(value_layout); - - Ok(Layout::Dict(index)) - } - - FlatType::Apply(Symbol::SET_SET, arguments) => { - debug_assert_eq!(arguments.len(), 1); - - let element_var = subs.variables[arguments.start as usize]; - let element_layout = Self::from_var_help_or_void(layouts, subs, element_var)?; - - let element_index = Index::new(layouts.layouts.len() as _); - layouts.layouts.push(element_layout); - - Ok(Layout::Set(element_index)) - } - - FlatType::Apply(symbol, _) => { - unreachable!("Symbol {:?} does not have a layout", symbol) - } - - FlatType::Func(_arguments, lambda_set, _result) => { - // in this case, a function (pointer) is represented by the environment it - // captures: the lambda set - - Self::from_var_help(layouts, subs, *lambda_set) - } - FlatType::Record(fields, ext) => { - debug_assert!(ext_var_is_empty_record(subs, *ext)); - - let mut slice = Slice::reserve(layouts, fields.len()); - - let mut non_optional_fields = 0; - let it = slice.indices().zip(fields.iter_all()); - for (target_index, (_, field_index, var_index)) in it { - match subs.record_fields[field_index.index as usize] { - RecordField::Optional(_) | RecordField::RigidOptional(_) => { - // do nothing - } - RecordField::Required(_) - | RecordField::Demanded(_) - | RecordField::RigidRequired(_) => { - let var = subs.variables[var_index.index as usize]; - let layout = Layout::from_var_help(layouts, subs, var)?; - - layouts.layouts[target_index] = layout; - - non_optional_fields += 1; - } - } - } - - // we have some wasted space in the case of optional fields; so be it - slice.length = non_optional_fields; - - layouts.sort_slice_by_alignment(slice); - - Ok(Layout::Struct(slice)) - } - FlatType::Tuple(_elems, _ext) => { - todo!(); - } - FlatType::TagUnion(union_tags, ext) => { - debug_assert!(ext_var_is_empty_tag_union(subs, *ext)); - - let slices = - Self::from_slice_variable_slice(layouts, subs, union_tags.variables())?; - - Ok(Layout::UnionNonRecursive(slices)) - } - - FlatType::FunctionOrTagUnion(_, _, ext) => { - debug_assert!(ext_var_is_empty_tag_union(subs, *ext)); - - // at this point we know this is a tag - Ok(Layout::UNIT) - } - FlatType::RecursiveTagUnion(rec_var, union_tags, ext) => { - debug_assert!(ext_var_is_empty_tag_union(subs, *ext)); - - let rec_var = subs.get_root_key_without_compacting(*rec_var); - - let cached = layouts - .recursion_variable_to_structure_variable_map - .get(&rec_var); - - if let Some(layout_index) = cached { - match layouts.layouts[layout_index.index as usize] { - Layout::Reserved => { - // we have to do the work here to fill this reserved variable in - } - other => { - return Ok(other); - } - } - } - - let slices = - Self::from_slice_variable_slice(layouts, subs, union_tags.variables())?; - - Ok(Layout::UnionRecursive(slices)) - } - FlatType::EmptyRecord | FlatType::EmptyTuple => Ok(Layout::UNIT), - FlatType::EmptyTagUnion => Ok(Layout::VOID), - } - } - - fn from_slice_variable_slice( - layouts: &mut Layouts, - subs: &Subs, - slice_variable_slice: roc_types::subs::SubsSlice, - ) -> Result>, LayoutError> { - let slice = Slice::reserve(layouts, slice_variable_slice.len()); - - let variable_slices = &subs.variable_slices[slice_variable_slice.indices()]; - let it = slice.indices().zip(variable_slices); - for (target_index, variable_slice) in it { - let layout_slice = Layout::from_variable_slice(layouts, subs, *variable_slice)?; - layouts.layout_slices[target_index] = layout_slice; - } - - Ok(slice) - } - - fn from_variable_slice( - layouts: &mut Layouts, - subs: &Subs, - variable_subs_slice: roc_types::subs::VariableSubsSlice, - ) -> Result, LayoutError> { - let slice = Slice::reserve(layouts, variable_subs_slice.len()); - - let variable_slice = &subs.variables[variable_subs_slice.indices()]; - let it = slice.indices().zip(variable_slice); - for (target_index, var) in it { - let layout = Layout::from_var_help(layouts, subs, *var)?; - layouts.layouts[target_index] = layout; - } - - layouts.sort_slice_by_alignment(slice); - - Ok(slice) - } -} diff --git a/crates/compiler/mono/src/lib.rs b/crates/compiler/mono/src/lib.rs index 1104473800f..0a0f96c22f1 100644 --- a/crates/compiler/mono/src/lib.rs +++ b/crates/compiler/mono/src/lib.rs @@ -15,7 +15,6 @@ pub mod drop_specialization; pub mod inc_dec; pub mod ir; pub mod layout; -pub mod layout_soa; pub mod low_level; pub mod reset_reuse; pub mod tail_recursion; diff --git a/crates/compiler/mono/src/reset_reuse.rs b/crates/compiler/mono/src/reset_reuse.rs index f6fe8264946..f68da9279b7 100644 --- a/crates/compiler/mono/src/reset_reuse.rs +++ b/crates/compiler/mono/src/reset_reuse.rs @@ -9,8 +9,8 @@ use std::hash::Hash; use crate::borrow::Ownership; use crate::ir::{ - BranchInfo, Expr, JoinPointId, ModifyRc, Param, Proc, ProcLayout, Stmt, UpdateModeId, - UpdateModeIds, + BranchInfo, Expr, JoinPointId, ModifyRc, Param, Proc, ProcLayout, ReuseToken, Stmt, + UpdateModeId, UpdateModeIds, }; use crate::layout::{InLayout, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout}; @@ -19,7 +19,9 @@ use bumpalo::Bump; use bumpalo::collections::vec::Vec; use bumpalo::collections::CollectIn; use roc_collections::{MutMap, MutSet}; +use roc_module::low_level::LowLevel; use roc_module::symbol::{IdentIds, ModuleId, Symbol}; +use roc_target::TargetInfo; /** Insert reset and reuse operations into the IR. @@ -29,6 +31,7 @@ pub fn insert_reset_reuse_operations<'a, 'i>( arena: &'a Bump, layout_interner: &'i STLayoutInterner<'a>, home: ModuleId, + target_info: TargetInfo, ident_ids: &'i mut IdentIds, update_mode_ids: &'i mut UpdateModeIds, procs: &mut MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>, @@ -42,6 +45,7 @@ pub fn insert_reset_reuse_operations<'a, 'i>( let new_proc = insert_reset_reuse_operations_proc( arena, layout_interner, + target_info, home, ident_ids, update_mode_ids, @@ -55,6 +59,7 @@ pub fn insert_reset_reuse_operations<'a, 'i>( fn insert_reset_reuse_operations_proc<'a, 'i>( arena: &'a Bump, layout_interner: &'i STLayoutInterner<'a>, + target_info: TargetInfo, home: ModuleId, ident_ids: &'i mut IdentIds, update_mode_ids: &'i mut UpdateModeIds, @@ -66,6 +71,7 @@ fn insert_reset_reuse_operations_proc<'a, 'i>( } let mut env = ReuseEnvironment { + target_info, symbol_tags: MutMap::default(), non_unique_symbols: MutSet::default(), reuse_tokens: MutMap::default(), @@ -128,7 +134,10 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>( tag_layout, tag_id, arguments, + reuse, } => { + debug_assert!(reuse.is_none()); + // The value of the tag is currently only used in the case of nullable recursive unions. // But for completeness we add every kind of union to the layout_tags. environment.add_symbol_tag(*binding, *tag_id); @@ -144,45 +153,44 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>( )) { // We have a reuse token for this layout, use it. Some(TokenWithInLayout { - token: reuse_token, + token: mut reuse_token, inlayout: layout_info, }) => { - // The reuse token layout is the same, we can use it without casting. if layout_info == layout { + // The reuse token layout is the same, we can use it without casting. ( None, - Expr::Reuse { - symbol: reuse_token.symbol, - update_mode: reuse_token.update_mode_id, - // for now, always overwrite the tag ID just to be sure - update_tag_id: true, + Expr::Tag { tag_layout: *tag_layout, tag_id: *tag_id, arguments, + reuse: Some(reuse_token), }, ) - } - // The reuse token has a different layout from the tag, we need to pointercast it before. - else { + } else { + // The reuse token has a different layout from the tag, we need to pointercast it before. let new_symbol = Symbol::new(home, ident_ids.gen_unique()); + + let ptr_cast = move |new_let| { + arena.alloc(Stmt::Let( + new_symbol, + create_ptr_cast(arena, reuse_token.symbol), + *layout, + new_let, + )) + }; + + // we now want to reuse the cast pointer + reuse_token.symbol = new_symbol; + ( - Some(move |new_let| { - arena.alloc(Stmt::Let( - new_symbol, - create_ptr_cast(arena, reuse_token.symbol), - *layout, - new_let, - )) - }), - Expr::Reuse { - symbol: new_symbol, - update_mode: reuse_token.update_mode_id, - // for now, always overwrite the tag ID just to be sure - update_tag_id: true, + Some(ptr_cast), + Expr::Tag { tag_layout: *tag_layout, tag_id: *tag_id, arguments, + reuse: Some(reuse_token), }, ) } @@ -215,12 +223,13 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>( new_triplets.into_iter().rev().fold( new_continuation, - |new_continuation, (binding, (reused, new_expr), layout)| { + |new_continuation, (binding, (opt_ptr_cast, new_expr), layout)| { let new_let = arena.alloc(Stmt::Let(*binding, new_expr, *layout, new_continuation)); - match reused { - // The layout for the reuse does not match that of the reset, use PtrCast to convert the layout. - Some(wrap) => wrap(new_let), + + // if the layout for the reuse does not match that of the reset, use PtrCast to convert the layout. + match opt_ptr_cast { + Some(ptr_cast) => ptr_cast(new_let), None => new_let, } }, @@ -398,41 +407,100 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>( }) } Stmt::Refcounting(rc, continuation) => { - let reuse_pair = match rc { - ModifyRc::Dec(symbol) | ModifyRc::DecRef(symbol) - if !environment.non_unique_symbols.contains(symbol) => - { + enum SymbolIsUnique { + Never, + Always(Symbol), + MustCheck(Symbol), + } + + let can_reuse = match rc { + ModifyRc::Dec(symbol) | ModifyRc::DecRef(symbol) => { + // can only reuse if the symbol is (potentially) unique + if environment.non_unique_symbols.contains(symbol) { + SymbolIsUnique::Never + } else { + SymbolIsUnique::MustCheck(*symbol) + } + } + ModifyRc::Free(symbol) => { + // a free'd symbol is guaranteed to be unique + SymbolIsUnique::Always(*symbol) + } + ModifyRc::Inc(_, _) => { + // an incremented symbol is never unique + SymbolIsUnique::Never + } + }; + + enum ResetOperation { + Reset, + ResetRef, + ClearTagId, + Nothing, + } + + let reuse_pair = match can_reuse { + SymbolIsUnique::MustCheck(symbol) | SymbolIsUnique::Always(symbol) => { // Get the layout of the symbol from where it is defined. - let layout_option = environment.get_symbol_layout(*symbol); + let layout_option = environment.get_symbol_layout(symbol); // If the symbol is defined in the current proc, we can use the layout from the environment. - match layout_option.clone() { + match layout_option { LayoutOption::Layout(layout) => { match symbol_layout_reusability( layout_interner, environment, - symbol, + &symbol, layout, ) { Reuse::Reusable(union_layout) => { - let reuse_token = ReuseToken { - symbol: Symbol::new(home, ident_ids.gen_unique()), - update_mode_id: update_mode_ids.next_id(), + let (reuse_symbol, reset_op) = match rc { + ModifyRc::Dec(_) => ( + Symbol::new(home, ident_ids.gen_unique()), + ResetOperation::Reset, + ), + ModifyRc::DecRef(_) => ( + Symbol::new(home, ident_ids.gen_unique()), + ResetOperation::ResetRef, + ), + ModifyRc::Free(_) => { + if union_layout + .stores_tag_id_in_pointer(environment.target_info) + { + ( + Symbol::new(home, ident_ids.gen_unique()), + ResetOperation::ClearTagId, + ) + } else { + (symbol, ResetOperation::Nothing) + } + } + _ => unreachable!(), }; - let dec_ref = match rc { - ModifyRc::Dec(_) => false, - ModifyRc::DecRef(_) => true, - _ => unreachable!(), + let reuse_token = ReuseToken { + symbol: reuse_symbol, + update_mode: update_mode_ids.next_id(), + // for now, always overwrite the tag ID just to be sure + update_tag_id: true, }; + let owned_layout = **layout; + environment.push_reuse_token( arena, get_reuse_layout_info(layout_interner, union_layout), reuse_token, layout, ); - Some((layout, union_layout, *symbol, reuse_token, dec_ref)) + + Some(( + owned_layout, + union_layout, + symbol, + reuse_token, + reset_op, + )) } Reuse::Nonreusable => None, } @@ -440,7 +508,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>( _ => None, } } - _ => { + SymbolIsUnique::Never => { // We don't need to do anything for an inc or symbols known to be non-unique. None } @@ -457,7 +525,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>( ); // If we inserted a reuse token, we need to insert a reset reuse operation if the reuse token is consumed. - if let Some((layout, union_layout, symbol, reuse_token, dec_ref)) = reuse_pair { + if let Some((layout, union_layout, symbol, reuse_token, reset_op)) = reuse_pair { let stack_reuse_token = environment .peek_reuse_token(&get_reuse_layout_info(layout_interner, union_layout)); @@ -471,29 +539,56 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>( _ => { // The token we inserted is no longer on the stack, it must have been consumed. // So we need to insert a reset operation. - let reset_expr = match dec_ref { - // A decref will be replaced by a resetref. - true => Expr::ResetRef { - symbol, - update_mode: reuse_token.update_mode_id, - }, - // And a dec will be replaced by a reset. - false => Expr::Reset { - symbol, - update_mode: reuse_token.update_mode_id, - }, - }; - - // If we generate a reuse token, we no longer want to use the drop statement anymore. So we just return the reset expression. - // TODO verify if this works for both dec and decref. - // TODO reset probably decrements it's children. So we probably need to create a resetref that only does the token. - return arena.alloc(Stmt::Let( - reuse_token.symbol, - reset_expr, - // TODO not sure what the layout should be for a reset token. Currently it is the layout of the symbol. - *layout, - new_continuation, - )); + match reset_op { + ResetOperation::Reset => { + // a dec will be replaced by a reset. + let reset_expr = Expr::Reset { + symbol, + update_mode: reuse_token.update_mode, + }; + + return arena.alloc(Stmt::Let( + reuse_token.symbol, + reset_expr, + layout, + new_continuation, + )); + } + ResetOperation::ResetRef => { + // a decref will be replaced by a resetref. + let reset_expr = Expr::ResetRef { + symbol, + update_mode: reuse_token.update_mode, + }; + + return arena.alloc(Stmt::Let( + reuse_token.symbol, + reset_expr, + layout, + new_continuation, + )); + } + ResetOperation::ClearTagId => { + let reset_expr = Expr::Call(crate::ir::Call { + call_type: crate::ir::CallType::LowLevel { + op: LowLevel::PtrClearTagId, + update_mode: update_mode_ids.next_id(), + }, + arguments: arena.alloc([symbol]), + }); + + return arena.alloc(Stmt::Let( + reuse_token.symbol, + reset_expr, + layout, + new_continuation, + )); + } + ResetOperation::Nothing => { + // the reuse token is already in a valid state + return new_continuation; + } + } } } } @@ -649,7 +744,9 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>( tokens.iter().map(|token| TokenWithInLayout { token: ReuseToken { symbol: Symbol::new(home, ident_ids.gen_unique()), - update_mode_id: update_mode_ids.next_id(), + update_mode: update_mode_ids.next_id(), + // for now, always overwrite the tag ID just to be sure + update_tag_id: true, }, inlayout: token.inlayout, }), @@ -661,6 +758,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>( // Create a new environment for the body. With everything but the jump reuse tokens. As those should be given by the jump. let mut first_pass_body_environment = ReuseEnvironment { + target_info: environment.target_info, symbol_tags: environment.symbol_tags.clone(), non_unique_symbols: environment.non_unique_symbols.clone(), reuse_tokens: max_reuse_token_symbols.clone(), @@ -824,6 +922,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>( let (second_pass_body_environment, second_pass_body) = { // Create a new environment for the body. With everything but the jump reuse tokens. As those should be given by the jump. let mut body_environment = ReuseEnvironment { + target_info: environment.target_info, symbol_tags: environment.symbol_tags.clone(), non_unique_symbols: environment.non_unique_symbols.clone(), reuse_tokens: used_reuse_tokens.clone(), @@ -889,7 +988,8 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>( let mut void_pointer_layout_symbols = Vec::new_in(arena); // See what tokens we can get from the env, if none are available, use a void pointer. - // We process the tokens in reverse order, so that when we consume the tokens we last added, we consume the tokens that are most likely not to be null. + // We process the tokens in reverse order, so that when we consume the tokens we last added, + // we consume the tokens that are most likely not to be null. let tokens = token_layouts_clone .iter() .rev() @@ -1002,7 +1102,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>( fn create_ptr_cast(arena: &Bump, symbol: Symbol) -> Expr { Expr::Call(crate::ir::Call { call_type: crate::ir::CallType::LowLevel { - op: roc_module::low_level::LowLevel::PtrCast, + op: LowLevel::PtrCast, update_mode: UpdateModeId::BACKEND_DUMMY, }, arguments: Vec::from_iter_in([symbol], arena).into_bump_slice(), @@ -1029,24 +1129,10 @@ Struct to to check whether two reuse layouts are interchangeable. */ #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] struct TokenLayout { - has_tag: bool, size: u32, alignment: u32, } -/** -A reuse token is a symbol that is used to reset a layout. -Matches symbols that are pointers. -*/ -#[derive(Clone, Copy, PartialEq, Eq)] -struct ReuseToken { - // The symbol of the reuse token. - symbol: Symbol, - - // Index that can be used later to determine if in place mutation is possible. - update_mode_id: UpdateModeId, -} - /** Combines a reuse token with it's possible layout info. */ @@ -1092,8 +1178,9 @@ enum JoinPointReuseTokens<'a> { RemainderSecond(Vec<'a, (&'a InLayout<'a>, TokenLayout)>), } -#[derive(Default, Clone)] +#[derive(Clone)] struct ReuseEnvironment<'a> { + target_info: TargetInfo, symbol_tags: MutMap, non_unique_symbols: MutSet, reuse_tokens: ReuseTokens<'a>, @@ -1317,21 +1404,11 @@ fn drop_unused_reuse_tokens<'a>( }) } -fn get_reuse_layout_info<'a, 'i>( - layout_interner: &'i STLayoutInterner<'a>, +fn get_reuse_layout_info<'a>( + layout_interner: &STLayoutInterner<'a>, union_layout: UnionLayout<'a>, ) -> TokenLayout { let (size, alignment) = union_layout.data_size_and_alignment(layout_interner); - let has_tag = match union_layout { - UnionLayout::NonRecursive(_) => unreachable!("Non recursive unions should not be reused."), - // The memory for union layouts that has a tag_id can be reused for new allocations with tag_id. - UnionLayout::Recursive(_) | UnionLayout::NullableWrapped { .. } => true, - // The memory for union layouts that have no tag_id can be reused for new allocations without tag_id - UnionLayout::NonNullableUnwrapped(_) | UnionLayout::NullableUnwrapped { .. } => false, - }; - TokenLayout { - has_tag, - size, - alignment, - } + + TokenLayout { size, alignment } } diff --git a/crates/compiler/mono/src/tail_recursion.rs b/crates/compiler/mono/src/tail_recursion.rs index 383363d3a2b..9517eb9ef85 100644 --- a/crates/compiler/mono/src/tail_recursion.rs +++ b/crates/compiler/mono/src/tail_recursion.rs @@ -1,11 +1,91 @@ #![allow(clippy::manual_map)] use crate::borrow::Ownership; -use crate::ir::{CallType, Expr, JoinPointId, Param, Stmt}; -use crate::layout::{InLayout, LambdaName}; +use crate::ir::{ + Call, CallType, Expr, JoinPointId, Param, Proc, ProcLayout, SelfRecursive, Stmt, UpdateModeId, +}; +use crate::layout::{ + InLayout, LambdaName, Layout, LayoutInterner, LayoutRepr, STLayoutInterner, TagIdIntType, + UnionLayout, +}; use bumpalo::collections::Vec; use bumpalo::Bump; -use roc_module::symbol::Symbol; +use roc_collections::{MutMap, VecMap}; +use roc_module::low_level::LowLevel; +use roc_module::symbol::{IdentIds, ModuleId, Symbol}; + +pub struct Env<'a, 'i> { + arena: &'a Bump, + home: ModuleId, + interner: &'i mut STLayoutInterner<'a>, + ident_ids: &'i mut IdentIds, +} + +impl<'a, 'i> Env<'a, 'i> { + fn unique_symbol(&mut self) -> Symbol { + let ident_id = self.ident_ids.gen_unique(); + + Symbol::new(self.home, ident_id) + } + + fn named_unique_symbol(&mut self, name: &str) -> Symbol { + let ident_id = self.ident_ids.add_str(name); + Symbol::new(self.home, ident_id) + } +} + +pub fn apply_trmc<'a, 'i>( + arena: &'a Bump, + interner: &'i mut STLayoutInterner<'a>, + home: ModuleId, + ident_ids: &'i mut IdentIds, + procs: &mut MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>, +) { + let mut env = Env { + arena, + interner, + home, + ident_ids, + }; + + let env = &mut env; + + for proc in procs.values_mut() { + use self::SelfRecursive::*; + if let SelfRecursive(id) = proc.is_self_recursive { + let trmc_candidate_symbols = trmc_candidates(env.interner, proc); + + if !trmc_candidate_symbols.is_empty() { + let new_proc = + crate::tail_recursion::TrmcEnv::init(env, proc, trmc_candidate_symbols); + *proc = new_proc; + } else { + let mut args = Vec::with_capacity_in(proc.args.len(), arena); + let mut proc_args = Vec::with_capacity_in(proc.args.len(), arena); + + for (layout, symbol) in proc.args { + let new = env.unique_symbol(); + args.push((*layout, *symbol, new)); + proc_args.push((*layout, new)); + } + + let transformed = crate::tail_recursion::make_tail_recursive( + arena, + id, + proc.name, + proc.body.clone(), + args.into_bump_slice(), + proc.ret_layout, + ); + + if let Some(with_tco) = transformed { + proc.body = with_tco; + proc.args = proc_args.into_bump_slice(); + } + } + } + } +} /// Make tail calls into loops (using join points) /// @@ -29,7 +109,8 @@ use roc_module::symbol::Symbol; /// /// This will effectively compile into a loop in llvm, and /// won't grow the call stack for each iteration -pub fn make_tail_recursive<'a>( + +fn make_tail_recursive<'a>( arena: &'a Bump, id: JoinPointId, needle: LambdaName, @@ -323,3 +404,732 @@ fn insert_jumps<'a>( Crash(..) => None, } } + +#[derive(Debug, Default)] +struct TrmcCandidateSet { + interner: arrayvec::ArrayVec, + confirmed: u64, + active: u64, + invalid: u64, +} + +impl TrmcCandidateSet { + fn confirmed(&self) -> impl Iterator + '_ { + self.interner + .iter() + .enumerate() + .filter_map(|(i, s)| (self.confirmed & (1 << i) != 0).then_some(*s)) + } + + fn active(&self) -> impl Iterator + '_ { + self.interner + .iter() + .enumerate() + .filter_map(|(i, s)| (self.active & (1 << i) != 0).then_some(*s)) + } + + fn position(&self, symbol: Symbol) -> Option { + self.interner.iter().position(|s| *s == symbol) + } + + fn insert(&mut self, symbol: Symbol) { + // there really is no way it could have been inserted already + debug_assert!(self.position(symbol).is_none()); + + let index = self.interner.len(); + self.interner.push(symbol); + + self.active |= 1 << index; + } + + fn retain(&mut self, keep: F) + where + F: Fn(&Symbol) -> bool, + { + for (i, s) in self.interner.iter().enumerate() { + if !keep(s) { + let mask = 1 << i; + + self.active &= !mask; + self.confirmed &= !mask; + + self.invalid |= mask; + } + } + } + + fn confirm(&mut self, symbol: Symbol) { + match self.position(symbol) { + None => debug_assert_eq!(0, 1, "confirm of invalid symbol"), + Some(index) => { + let mask = 1 << index; + + debug_assert_eq!(self.invalid & mask, 0); + debug_assert_ne!(self.active & mask, 0); + + self.active &= !mask; + self.confirmed |= mask; + } + } + } + + fn is_empty(&self) -> bool { + self.confirmed == 0 + } +} + +fn trmc_candidates<'a, I>(interner: &'_ I, proc: &'_ Proc<'a>) -> TrmcCandidateSet +where + I: LayoutInterner<'a>, +{ + // it must be a self-recursive function + if !matches!( + proc.is_self_recursive, + crate::ir::SelfRecursive::SelfRecursive(_) + ) { + return TrmcCandidateSet::default(); + } + + // and return a recursive tag union + if !matches!(interner.get_repr(proc.ret_layout), LayoutRepr::Union(union_layout) if union_layout.is_recursive()) + { + return TrmcCandidateSet::default(); + } + + let mut candidate_set = TrmcCandidateSet::default(); + trmc_candidates_help(proc.name, &proc.body, &mut candidate_set); + candidate_set +} + +fn trmc_candidates_help( + function_name: LambdaName, + stmt: &'_ Stmt<'_>, + candidates: &mut TrmcCandidateSet, +) { + // if this stmt is the literal tail tag application and return, then this is a TRMC opportunity + if let Some(cons_info) = TrmcEnv::is_terminal_constructor(stmt) { + // the tag application must directly use the result of the recursive call + let recursive_call = candidates + .active() + .find(|call| cons_info.arguments.contains(call)); + + // if we find a usage, this is a confirmed TRMC call + if let Some(recursive_call) = recursive_call { + candidates.confirm(recursive_call); + + return; + } + } + + // if the stmt uses the active recursive call, that invalidates the recursive call for this branch + candidates.retain(|recursive_call| !stmt_contains_symbol_nonrec(stmt, *recursive_call)); + + match stmt { + Stmt::Let(symbol, expr, _, next) => { + // find a new recursive call if we currently have none + // that means we generally pick the first recursive call we find + if TrmcEnv::is_recursive_expr(expr, function_name).is_some() { + candidates.insert(*symbol); + } + + trmc_candidates_help(function_name, next, candidates) + } + Stmt::Switch { + branches, + default_branch, + .. + } => { + let it = branches + .iter() + .map(|(_, _, stmt)| stmt) + .chain([default_branch.1]); + + for next in it { + trmc_candidates_help(function_name, next, candidates); + } + } + Stmt::Refcounting(_, next) => trmc_candidates_help(function_name, next, candidates), + Stmt::Expect { remainder, .. } + | Stmt::ExpectFx { remainder, .. } + | Stmt::Dbg { remainder, .. } => trmc_candidates_help(function_name, remainder, candidates), + Stmt::Join { + body, remainder, .. + } => { + trmc_candidates_help(function_name, body, candidates); + trmc_candidates_help(function_name, remainder, candidates); + } + Stmt::Ret(_) | Stmt::Jump(_, _) | Stmt::Crash(_, _) => { /* terminal */ } + } +} + +// TRMC (tail recursion modulo constructor) is an optimization for some recursive functions that return a recursive data type. The most basic example is a repeat function on linked lists: +// +// ```roc +// LinkedList a : [ Nil, Cons a (LinkedList a) ] +// +// repeat : a, Nat -> LinkedList a +// repeat = \element, n -> +// when n is +// 0 -> Nil +// _ -> Cons element (repeat element (n - 1)) +// ``` +// +// This function is recursive, but cannot use standard tail-call elimintation, because the recursive call is not in tail position (i.e. the last thing happening before a return). Rather the recursive call is an argument to a constructor of the recursive output type. This means that `repeat n` will creat `n` stack frames. For big inputs, a stack overflow is inevitable. +// +// But there is a trick: TRMC. Using TRMC and join points, we are able to convert this function into a loop, which uses only one stack frame for the whole process. +// +// ```pseudo-roc +// repeat : a, Nat -> LinkedList a +// repeat = \initialElement, initialN -> +// joinpoint trmc = \element, n, hole, head -> +// when n is +// 0 -> +// # write the value `Nil` into the hole +// *hole = Nil +// # dereference (load from) the pointer to the first element +// *head +// +// _ -> +// *hole = Cons element NULL +// newHole = &hole.Cons.1 +// jump trmc element (n - 1) newHole head +// in +// # creates a stack allocation, gives a pointer to that stack allocation +// initial : Ptr (LinkedList a) = #alloca NULL +// jump trmc initialElement initialN initial initial +// ``` +// +// The functionality here figures out whether this transformation can be applied in valid way, and then performs the transformation. + +#[derive(Clone)] +pub(crate) struct TrmcEnv<'a> { + lambda_name: LambdaName<'a>, + /// Current hole to fill + hole_symbol: Symbol, + /// Pointer to the first constructor ("the head of the list") + head_symbol: Symbol, + joinpoint_id: JoinPointId, + return_layout: InLayout<'a>, + ptr_return_layout: InLayout<'a>, + + trmc_calls: VecMap>>, +} + +#[derive(Debug)] +struct ConstructorInfo<'a> { + tag_layout: UnionLayout<'a>, + tag_id: TagIdIntType, + arguments: &'a [Symbol], +} + +impl<'a> TrmcEnv<'a> { + #[inline(always)] + fn is_terminal_constructor(stmt: &Stmt<'a>) -> Option> { + match stmt { + Stmt::Let(s1, expr, _layout, Stmt::Ret(s2)) if s1 == s2 => { + Self::get_contructor_info(expr) + } + + _ => None, + } + } + + fn get_contructor_info(expr: &Expr<'a>) -> Option> { + if let Expr::Tag { + tag_layout, + tag_id, + arguments, + reuse, + } = expr + { + debug_assert!(reuse.is_none()); + + let info = ConstructorInfo { + tag_layout: *tag_layout, + tag_id: *tag_id, + arguments, + }; + + Some(info) + } else { + None + } + } + + fn is_recursive_expr(expr: &Expr<'a>, lambda_name: LambdaName<'_>) -> Option> { + if let Expr::Call(call) = expr { + Self::is_recursive_call(call, lambda_name).then_some(call.clone()) + } else { + None + } + } + + fn is_recursive_call(call: &Call<'a>, lambda_name: LambdaName<'_>) -> bool { + match call.call_type { + CallType::ByName { name, .. } => { + // because we do not allow polymorphic recursion, this is the only constraint + name == lambda_name + } + CallType::Foreign { .. } | CallType::LowLevel { .. } | CallType::HigherOrder(_) => { + false + } + } + } + + fn is_tail_recursive_call( + lambda_name: LambdaName, + symbol: Symbol, + expr: &Expr<'a>, + next: &Stmt<'a>, + ) -> Option> { + match next { + Stmt::Ret(s) if *s == symbol => Self::is_recursive_expr(expr, lambda_name), + _ => None, + } + } + + fn ptr_write( + env: &mut Env<'a, '_>, + ptr: Symbol, + value: Symbol, + next: &'a Stmt<'a>, + ) -> Stmt<'a> { + let ptr_write = Call { + call_type: crate::ir::CallType::LowLevel { + op: LowLevel::PtrStore, + update_mode: UpdateModeId::BACKEND_DUMMY, + }, + arguments: env.arena.alloc([ptr, value]), + }; + + Stmt::Let( + env.named_unique_symbol("_ptr_write_unit"), + Expr::Call(ptr_write), + Layout::UNIT, + next, + ) + } + + fn init<'i>(env: &mut Env<'a, 'i>, proc: &Proc<'a>, trmc_calls: TrmcCandidateSet) -> Proc<'a> { + let arena = env.arena; + let return_layout = proc.ret_layout; + + let mut joinpoint_parameters = Vec::with_capacity_in(proc.args.len() + 2, env.arena); + let mut new_proc_arguments = Vec::with_capacity_in(proc.args.len(), env.arena); + let mut jump_arguments = Vec::with_capacity_in(proc.args.len() + 2, env.arena); + + for (i, (layout, old_symbol)) in proc.args.iter().enumerate() { + let symbol = env.named_unique_symbol(&format!("arg_{i}")); + new_proc_arguments.push((*layout, symbol)); + jump_arguments.push(symbol); + + let param = Param { + symbol: *old_symbol, + ownership: Ownership::Owned, + layout: *layout, + }; + joinpoint_parameters.push(param); + } + + // the root of the recursive structure that we'll be building + let initial_ptr_symbol = env.named_unique_symbol("initial"); + jump_arguments.push(initial_ptr_symbol); + jump_arguments.push(initial_ptr_symbol); + + let null_symbol = env.named_unique_symbol("null"); + let let_null = |next| Stmt::Let(null_symbol, Expr::NullPointer, return_layout, next); + + let ptr_return_layout = env + .interner + .insert_direct_no_semantic(LayoutRepr::Ptr(return_layout)); + + let call = Call { + call_type: CallType::LowLevel { + op: LowLevel::Alloca, + update_mode: UpdateModeId::BACKEND_DUMMY, + }, + arguments: arena.alloc([null_symbol]), + }; + + let ptr_null = Expr::Call(call); + let let_ptr = |next| Stmt::Let(initial_ptr_symbol, ptr_null, ptr_return_layout, next); + + let joinpoint_id = JoinPointId(env.named_unique_symbol("trmc")); + let hole_symbol = env.named_unique_symbol("hole"); + let head_symbol = env.named_unique_symbol("head"); + + let jump_stmt = Stmt::Jump(joinpoint_id, jump_arguments.into_bump_slice()); + + let trmc_calls = trmc_calls.confirmed().map(|s| (s, None)).collect(); + + let mut this = Self { + lambda_name: proc.name, + hole_symbol, + head_symbol, + joinpoint_id, + return_layout, + ptr_return_layout, + trmc_calls, + }; + + let param = Param { + symbol: hole_symbol, + ownership: Ownership::Owned, + layout: ptr_return_layout, + }; + joinpoint_parameters.push(param); + + let param = Param { + symbol: head_symbol, + ownership: Ownership::Owned, + layout: ptr_return_layout, + }; + joinpoint_parameters.push(param); + + let joinpoint = Stmt::Join { + id: joinpoint_id, + parameters: joinpoint_parameters.into_bump_slice(), + body: arena.alloc(this.walk_stmt(env, &proc.body)), + remainder: arena.alloc(jump_stmt), + }; + + let body = let_null(arena.alloc( + // + let_ptr(arena.alloc( + // + joinpoint, + )), + )); + + #[cfg(debug_assertions)] + env.home.register_debug_idents(env.ident_ids); + + Proc { + name: proc.name, + args: new_proc_arguments.into_bump_slice(), + body, + closure_data_layout: proc.closure_data_layout, + ret_layout: proc.ret_layout, + is_self_recursive: SelfRecursive::NotSelfRecursive, + host_exposed_layouts: proc.host_exposed_layouts.clone(), + } + } + + fn walk_stmt(&mut self, env: &mut Env<'a, '_>, stmt: &Stmt<'a>) -> Stmt<'a> { + let arena = env.arena; + + match stmt { + Stmt::Let(symbol, expr, layout, next) => { + // if this is a TRMC call, remember what the call looks like, so we can turn it + // into a jump later. The call is then removed from the Stmt + if let Some(opt_call) = self.trmc_calls.get_mut(symbol) { + debug_assert!( + opt_call.is_none(), + "didn't expect to visit call again since symbols are unique" + ); + + let call = match expr { + Expr::Call(call) => call, + _ => unreachable!(), + }; + + *opt_call = Some(call.clone()); + + return self.walk_stmt(env, next); + } + + if let Some(call) = + Self::is_tail_recursive_call(self.lambda_name, *symbol, expr, next) + { + // turn the call into a jump. Just re-use the existing hole + let mut arguments = Vec::new_in(arena); + arguments.extend(call.arguments); + arguments.push(self.hole_symbol); + arguments.push(self.head_symbol); + + let jump = Stmt::Jump(self.joinpoint_id, arguments.into_bump_slice()); + + return jump; + } + + if let Some(cons_info) = Self::is_terminal_constructor(stmt) { + // figure out which TRMC call to use here. We pick the first one that works + let opt_recursive_call = cons_info.arguments.iter().find_map(|arg| { + self.trmc_calls + .get(arg) + .and_then(|x| x.as_ref()) + .map(|x| (arg, x)) + }); + + match opt_recursive_call { + None => { + // this control flow path did not encounter a recursive call. Just + // write the end result into the hole and we're done. + + let define_tag = |next| Stmt::Let(*symbol, expr.clone(), *layout, next); + + let output = define_tag(arena.alloc( + // + self.non_trmc_return(env, *symbol), + )); + + return output; + } + Some((call_symbol, call)) => { + // we did encounter a recursive call, and can perform TRMC in this + // branch. + + let opt_recursive_field_index = + cons_info.arguments.iter().position(|s| *s == *call_symbol); + + let recursive_field_index = match opt_recursive_field_index { + None => { + let next = self.walk_stmt(env, next); + return Stmt::Let( + *symbol, + expr.clone(), + *layout, + arena.alloc(next), + ); + } + Some(v) => v, + }; + + let tag_arg_null_symbol = env.named_unique_symbol("tag_arg_null"); + let let_tag_arg_null = |next| { + Stmt::Let( + tag_arg_null_symbol, + Expr::NullPointer, + self.return_layout, + next, + ) + }; + + let mut arguments = + Vec::from_iter_in(cons_info.arguments.iter().copied(), env.arena); + arguments[recursive_field_index] = tag_arg_null_symbol; + + let tag_expr = Expr::Tag { + tag_layout: cons_info.tag_layout, + tag_id: cons_info.tag_id, + arguments: arguments.into_bump_slice(), + reuse: None, + }; + + let let_tag = |next| Stmt::Let(*symbol, tag_expr, *layout, next); + + let get_reference_expr = Expr::UnionFieldPtrAtIndex { + structure: *symbol, + tag_id: cons_info.tag_id, + union_layout: cons_info.tag_layout, + index: recursive_field_index as _, + }; + + let new_hole_symbol = env.named_unique_symbol("newHole"); + let let_new_hole = |next| { + Stmt::Let( + new_hole_symbol, + get_reference_expr, + self.ptr_return_layout, + next, + ) + }; + + let mut jump_arguments = + Vec::from_iter_in(call.arguments.iter().copied(), env.arena); + jump_arguments.push(new_hole_symbol); + jump_arguments.push(self.head_symbol); + + let jump = + Stmt::Jump(self.joinpoint_id, jump_arguments.into_bump_slice()); + + let output = let_tag_arg_null(arena.alloc( + // + let_tag(arena.alloc( + // + let_new_hole(arena.alloc( + // + Self::ptr_write( + env, + self.hole_symbol, + *symbol, + arena.alloc(jump), + ), + )), + )), + )); + + return output; + } + } + } + + let next = self.walk_stmt(env, next); + Stmt::Let(*symbol, expr.clone(), *layout, arena.alloc(next)) + } + Stmt::Switch { + cond_symbol, + cond_layout, + branches, + default_branch, + ret_layout, + } => { + let mut new_branches = Vec::with_capacity_in(branches.len(), arena); + + for (id, info, stmt) in branches.iter() { + let new_stmt = self.walk_stmt(env, stmt); + + new_branches.push((*id, info.clone(), new_stmt)); + } + + let new_default_branch = &*arena.alloc(self.walk_stmt(env, default_branch.1)); + + Stmt::Switch { + cond_symbol: *cond_symbol, + cond_layout: *cond_layout, + branches: arena.alloc(new_branches.into_bump_slice()), + default_branch: (default_branch.0.clone(), new_default_branch), + ret_layout: *ret_layout, + } + } + Stmt::Ret(symbol) => { + // write the symbol we're supposed to return into the hole + // then read initial_symbol and return its contents + self.non_trmc_return(env, *symbol) + } + Stmt::Refcounting(op, next) => { + let new_next = self.walk_stmt(env, next); + Stmt::Refcounting(*op, arena.alloc(new_next)) + } + Stmt::Expect { + condition, + region, + lookups, + variables, + remainder, + } => Stmt::Expect { + condition: *condition, + region: *region, + lookups, + variables, + remainder: arena.alloc(self.walk_stmt(env, remainder)), + }, + Stmt::ExpectFx { + condition, + region, + lookups, + variables, + remainder, + } => Stmt::Expect { + condition: *condition, + region: *region, + lookups, + variables, + remainder: arena.alloc(self.walk_stmt(env, remainder)), + }, + Stmt::Dbg { + symbol, + variable, + remainder, + } => Stmt::Dbg { + symbol: *symbol, + variable: *variable, + remainder: arena.alloc(self.walk_stmt(env, remainder)), + }, + Stmt::Join { + id, + parameters, + body, + remainder, + } => { + let new_body = self.walk_stmt(env, body); + let new_remainder = self.walk_stmt(env, remainder); + + Stmt::Join { + id: *id, + parameters, + body: arena.alloc(new_body), + remainder: arena.alloc(new_remainder), + } + } + Stmt::Jump(id, arguments) => Stmt::Jump(*id, arguments), + Stmt::Crash(symbol, crash_tag) => Stmt::Crash(*symbol, *crash_tag), + } + } + + fn non_trmc_return(&mut self, env: &mut Env<'a, '_>, value_symbol: Symbol) -> Stmt<'a> { + let arena = env.arena; + let layout = self.return_layout; + + let final_symbol = env.named_unique_symbol("final"); + + let call = Call { + call_type: CallType::LowLevel { + op: LowLevel::PtrLoad, + update_mode: UpdateModeId::BACKEND_DUMMY, + }, + arguments: &*arena.alloc([self.head_symbol]), + }; + + let ptr_load = |next| Stmt::Let(final_symbol, Expr::Call(call), layout, next); + + Self::ptr_write( + env, + self.hole_symbol, + value_symbol, + arena.alloc( + // + ptr_load(arena.alloc(Stmt::Ret(final_symbol))), + ), + ) + } +} + +fn expr_contains_symbol(expr: &Expr, needle: Symbol) -> bool { + match expr { + Expr::Literal(_) => false, + Expr::Call(call) => call.arguments.contains(&needle), + Expr::Tag { + arguments, reuse, .. + } => match reuse { + None => arguments.contains(&needle), + Some(ru) => ru.symbol == needle || arguments.contains(&needle), + }, + Expr::Struct(fields) => fields.contains(&needle), + Expr::NullPointer => false, + Expr::StructAtIndex { structure, .. } + | Expr::GetTagId { structure, .. } + | Expr::UnionAtIndex { structure, .. } + | Expr::UnionFieldPtrAtIndex { structure, .. } => needle == *structure, + Expr::Array { elems, .. } => elems.iter().any(|element| match element { + crate::ir::ListLiteralElement::Literal(_) => false, + crate::ir::ListLiteralElement::Symbol(symbol) => needle == *symbol, + }), + Expr::EmptyArray => false, + Expr::Reset { symbol, .. } | Expr::ResetRef { symbol, .. } => needle == *symbol, + Expr::RuntimeErrorFunction(_) => false, + } +} + +fn stmt_contains_symbol_nonrec(stmt: &Stmt, needle: Symbol) -> bool { + use crate::ir::ModifyRc::*; + + match stmt { + Stmt::Let(_, expr, _, _) => expr_contains_symbol(expr, needle), + Stmt::Switch { cond_symbol, .. } => needle == *cond_symbol, + Stmt::Ret(symbol) => needle == *symbol, + Stmt::Refcounting(modify, _) => { + matches!( modify, Inc(symbol, _) | Dec(symbol) | DecRef(symbol) if needle == *symbol ) + } + Stmt::Expect { + condition, lookups, .. + } + | Stmt::ExpectFx { + condition, lookups, .. + } => needle == *condition || lookups.contains(&needle), + Stmt::Dbg { symbol, .. } => needle == *symbol, + Stmt::Join { .. } => false, + Stmt::Jump(_, arguments) => arguments.contains(&needle), + Stmt::Crash(symbol, _) => needle == *symbol, + } +} diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index c0d35d67c0e..f1d6729c336 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -743,8 +743,8 @@ impl<'a> CommentOrNewline<'a> { use CommentOrNewline::*; match self { Newline => "\n".to_owned(), - LineComment(comment_str) => format!("#{}", comment_str), - DocComment(comment_str) => format!("##{}", comment_str), + LineComment(comment_str) => format!("#{comment_str}"), + DocComment(comment_str) => format!("##{comment_str}"), } } diff --git a/crates/compiler/parse/src/header.rs b/crates/compiler/parse/src/header.rs index 5c02bc580bb..3425f465738 100644 --- a/crates/compiler/parse/src/header.rs +++ b/crates/compiler/parse/src/header.rs @@ -2,6 +2,7 @@ use crate::ast::{ Collection, CommentOrNewline, Malformed, Spaced, Spaces, StrLiteral, TypeAnnotation, }; use crate::blankspace::space0_e; +use crate::expr::merge_spaces; use crate::ident::{lowercase_ident, UppercaseIdent}; use crate::parser::{optional, then}; use crate::parser::{specialize, word1, EPackageEntry, EPackageName, Parser}; @@ -300,7 +301,7 @@ pub struct PackageEntry<'a> { } pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPackageEntry<'a>> { - map!( + map_with_arena!( // You may optionally have a package shorthand, // e.g. "uc" in `uc: roc/unicode 1.0.0` // @@ -308,18 +309,25 @@ pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPac and!( optional(and!( skip_second!( - specialize(|_, pos| EPackageEntry::Shorthand(pos), lowercase_ident()), + and!( + specialize(|_, pos| EPackageEntry::Shorthand(pos), lowercase_ident()), + space0_e(EPackageEntry::IndentPackage) + ), word1(b':', EPackageEntry::Colon) ), space0_e(EPackageEntry::IndentPackage) )), loc!(specialize(EPackageEntry::BadPackage, package_name())) ), - move |(opt_shorthand, package_or_path)| { + move |arena, (opt_shorthand, package_or_path)| { let entry = match opt_shorthand { - Some((shorthand, spaces_after_shorthand)) => PackageEntry { + Some(((shorthand, spaces_before_colon), spaces_after_colon)) => PackageEntry { shorthand, - spaces_after_shorthand, + spaces_after_shorthand: merge_spaces( + arena, + spaces_before_colon, + spaces_after_colon, + ), package_name: package_or_path, }, None => PackageEntry { diff --git a/crates/compiler/parse/src/state.rs b/crates/compiler/parse/src/state.rs index 03e29519eb3..7e4207584f4 100644 --- a/crates/compiler/parse/src/state.rs +++ b/crates/compiler/parse/src/state.rs @@ -136,7 +136,7 @@ impl<'a> fmt::Debug for State<'a> { write!(f, "State {{")?; match std::str::from_utf8(self.bytes()) { - Ok(string) => write!(f, "\n\tbytes: [utf8] {:?}", string)?, + Ok(string) => write!(f, "\n\tbytes: [utf8] {string:?}")?, Err(_) => write!(f, "\n\tbytes: [invalid utf8] {:?}", self.bytes())?, } @@ -151,5 +151,5 @@ fn state_size() { // cache line. let state_size = std::mem::size_of::(); let maximum = std::mem::size_of::() * 8; - assert!(state_size <= maximum, "{:?} <= {:?}", state_size, maximum); + assert!(state_size <= maximum, "{state_size:?} <= {maximum:?}"); } diff --git a/crates/compiler/parse/tests/test_parse.rs b/crates/compiler/parse/tests/test_parse.rs index b22b7d7aa5b..b55e1ad269a 100644 --- a/crates/compiler/parse/tests/test_parse.rs +++ b/crates/compiler/parse/tests/test_parse.rs @@ -81,7 +81,7 @@ mod test_parse { #[test] fn string_with_escaped_char_at_end() { parses_with_escaped_char( - |esc| format!(r#""abcd{}""#, esc), + |esc| format!(r#""abcd{esc}""#), |esc, arena| bumpalo::vec![in arena; Plaintext("abcd"), EscapedChar(esc)], ); } @@ -89,7 +89,7 @@ mod test_parse { #[test] fn string_with_escaped_char_in_front() { parses_with_escaped_char( - |esc| format!(r#""{}abcd""#, esc), + |esc| format!(r#""{esc}abcd""#), |esc, arena| bumpalo::vec![in arena; EscapedChar(esc), Plaintext("abcd")], ); } @@ -97,7 +97,7 @@ mod test_parse { #[test] fn string_with_escaped_char_in_middle() { parses_with_escaped_char( - |esc| format!(r#""ab{}cd""#, esc), + |esc| format!(r#""ab{esc}cd""#), |esc, arena| bumpalo::vec![in arena; Plaintext("ab"), EscapedChar(esc), Plaintext("cd")], ); } @@ -105,7 +105,7 @@ mod test_parse { #[test] fn string_with_multiple_escaped_chars() { parses_with_escaped_char( - |esc| format!(r#""{}abc{}de{}fghi{}""#, esc, esc, esc, esc), + |esc| format!(r#""{esc}abc{esc}de{esc}fghi{esc}""#), |esc, arena| bumpalo::vec![in arena; EscapedChar(esc), Plaintext("abc"), EscapedChar(esc), Plaintext("de"), EscapedChar(esc), Plaintext("fghi"), EscapedChar(esc)], ); } @@ -247,7 +247,7 @@ mod test_parse { // These can potentially be whole numbers. `Display` omits the decimal point for those, // causing them to no longer be parsed as fractional numbers by Roc. // Using `Debug` instead of `Display` ensures they always have a decimal point. - let float_string = format!("{:?}", num); + let float_string = format!("{num:?}"); assert_parses_to(float_string.as_str(), Float(float_string.as_str())); } @@ -284,7 +284,7 @@ mod test_parse { // It should occur twice in the debug output - once for the pattern, // and then again for the lookup. - let occurrences = format!("{:?}", actual).split("isTest").count() - 1; + let occurrences = format!("{actual:?}").split("isTest").count() - 1; assert_eq!(occurrences, 2); } diff --git a/crates/compiler/problem/src/can.rs b/crates/compiler/problem/src/can.rs index fdeb251f338..2a9d97f31c8 100644 --- a/crates/compiler/problem/src/can.rs +++ b/crates/compiler/problem/src/can.rs @@ -613,11 +613,10 @@ impl RuntimeError { match self { DegenerateBranch(region) => { format!( - "Hit a branch pattern that does not bind all symbols its body needs, at {:?}", - region + "Hit a branch pattern that does not bind all symbols its body needs, at {region:?}" ) } - err => format!("{:?}", err), + err => format!("{err:?}"), } } } diff --git a/crates/compiler/region/src/all.rs b/crates/compiler/region/src/all.rs index 9b556b76b45..ce13c1131bc 100644 --- a/crates/compiler/region/src/all.rs +++ b/crates/compiler/region/src/all.rs @@ -434,10 +434,7 @@ fn test_line_info() { } else { "\n" // HACK! pretend there's an extra newline on the end, strictly so we can do the comparison }; - println!( - "checking {:?} {:?}, expecting {:?}", - input, offset, expected - ); + println!("checking {input:?} {offset:?}, expecting {expected:?}"); let line_column = info.convert_offset(offset as u32); assert!( Some(line_column) > last, diff --git a/crates/compiler/solve/src/ability.rs b/crates/compiler/solve/src/ability.rs index 47d0ba15a3d..fa93bf1f2ff 100644 --- a/crates/compiler/solve/src/ability.rs +++ b/crates/compiler/solve/src/ability.rs @@ -18,11 +18,11 @@ use roc_types::subs::{ TupleElems, Variable, }; use roc_types::types::{AliasKind, Category, MemberImpl, PatternCategory, Polarity, Types}; -use roc_unify::unify::{Env, MustImplementConstraints}; +use roc_unify::unify::{Env as UEnv, MustImplementConstraints}; use roc_unify::unify::{MustImplementAbility, Obligated}; -use crate::solve::type_to_var; -use crate::solve::{Aliases, Pools}; +use crate::env::Env; +use crate::{aliases::Aliases, to_var::type_to_var}; #[derive(Debug, Clone)] pub enum AbilityImplError { @@ -56,7 +56,7 @@ pub struct PendingDerivesTable( impl PendingDerivesTable { pub fn new( - subs: &mut Subs, + env: &mut Env, types: &mut Types, aliases: &mut Aliases, pending_derives: PendingDerives, @@ -81,17 +81,16 @@ impl PendingDerivesTable { // Neither rank nor pools should matter here. let typ = types.from_old_type(&typ); let opaque_var = type_to_var( - subs, + env, Rank::toplevel(), problems, abilities_store, obligation_cache, - &mut Pools::default(), types, aliases, typ, ); - let real_var = match subs.get_content_without_compacting(opaque_var) { + let real_var = match env.subs.get_content_without_compacting(opaque_var) { Content::Alias(_, _, real_var, AliasKind::Opaque) => real_var, _ => internal_error!("Non-opaque in derives table"), }; @@ -1284,7 +1283,7 @@ impl DerivableVisitor for DeriveEq { // Of the floating-point types, // only Dec implements Eq. - let mut env = Env::new(subs); + let mut env = UEnv::new(subs); let unified = unify( &mut env, content_var, @@ -1323,8 +1322,7 @@ pub fn type_implementing_specialization( .filter(|mia| mia.ability == ability) .count() } < 2, - "Multiple variables bound to an ability - this is ambiguous and should have been caught in canonicalization: {:?}", - specialization_must_implement_constraints + "Multiple variables bound to an ability - this is ambiguous and should have been caught in canonicalization: {specialization_must_implement_constraints:?}" ); specialization_must_implement_constraints @@ -1419,7 +1417,7 @@ pub fn resolve_ability_specialization( instantiate_rigids(subs, signature_var); let (_vars, must_implement_ability, _lambda_sets_to_specialize, _meta) = unify( - &mut Env::new(subs), + &mut UEnv::new(subs), specialization_var, signature_var, Mode::EQ, diff --git a/crates/compiler/solve/src/aliases.rs b/crates/compiler/solve/src/aliases.rs new file mode 100644 index 00000000000..ac8aca3b04e --- /dev/null +++ b/crates/compiler/solve/src/aliases.rs @@ -0,0 +1,329 @@ +use roc_can::abilities::AbilitiesStore; +use roc_collections::{soa::Index, MutMap}; +use roc_error_macros::internal_error; +use roc_module::symbol::Symbol; +use roc_solve_problem::TypeError; +use roc_types::{ + subs::{AliasVariables, Content, FlatType, Rank, Subs, SubsSlice, TagExt, UnionTags, Variable}, + types::{Alias, AliasKind, OptAbleVar, Type, TypeTag, Types}, +}; + +use crate::to_var::type_to_var_help; +use crate::{ability::ObligationCache, env::Env}; + +#[derive(Debug, Clone, Copy)] +struct DelayedAliasVariables { + start: u32, + type_variables_len: u8, + lambda_set_variables_len: u8, + recursion_variables_len: u8, + infer_ext_in_output_variables_len: u8, +} + +impl DelayedAliasVariables { + fn recursion_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] { + let start = self.start as usize + + (self.type_variables_len + self.lambda_set_variables_len) as usize; + let length = self.recursion_variables_len as usize; + + &mut variables[start..][..length] + } + + fn lambda_set_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] { + let start = self.start as usize + self.type_variables_len as usize; + let length = self.lambda_set_variables_len as usize; + + &mut variables[start..][..length] + } + + fn type_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] { + let start = self.start as usize; + let length = self.type_variables_len as usize; + + &mut variables[start..][..length] + } + + fn infer_ext_in_output_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] { + let start = self.start as usize + + (self.type_variables_len + + self.lambda_set_variables_len + + self.recursion_variables_len) as usize; + let length = self.infer_ext_in_output_variables_len as usize; + + &mut variables[start..][..length] + } +} + +#[derive(Debug, Default)] +pub struct Aliases { + aliases: Vec<(Symbol, Index, DelayedAliasVariables, AliasKind)>, + variables: Vec, +} + +impl Aliases { + pub fn with_capacity(cap: usize) -> Self { + Self { + aliases: Vec::with_capacity(cap), + variables: Vec::with_capacity(cap * 2), + } + } + + pub fn insert(&mut self, types: &mut Types, symbol: Symbol, alias: Alias) { + let alias_variables = + { + let start = self.variables.len() as _; + + self.variables.extend( + alias + .type_variables + .iter() + .map(|x| OptAbleVar::from(&x.value)), + ); + + self.variables.extend(alias.lambda_set_variables.iter().map( + |x| match x.as_inner() { + Type::Variable(v) => OptAbleVar::unbound(*v), + _ => unreachable!("lambda set type is not a variable"), + }, + )); + + let recursion_variables_len = alias.recursion_variables.len() as _; + self.variables.extend( + alias + .recursion_variables + .iter() + .copied() + .map(OptAbleVar::unbound), + ); + + self.variables.extend( + alias + .infer_ext_in_output_variables + .iter() + .map(|v| OptAbleVar::unbound(*v)), + ); + + DelayedAliasVariables { + start, + type_variables_len: alias.type_variables.len() as _, + lambda_set_variables_len: alias.lambda_set_variables.len() as _, + recursion_variables_len, + infer_ext_in_output_variables_len: alias.infer_ext_in_output_variables.len() + as _, + } + }; + + // TODO: can we construct Aliases from TypeTag directly? + let alias_typ = types.from_old_type(&alias.typ); + + self.aliases + .push((symbol, alias_typ, alias_variables, alias.kind)); + } + + fn instantiate_result_result( + env: &mut Env, + rank: Rank, + alias_variables: AliasVariables, + ) -> Variable { + let tag_names_slice = Subs::RESULT_TAG_NAMES; + + let err_slice = SubsSlice::new(alias_variables.variables_start + 1, 1); + let ok_slice = SubsSlice::new(alias_variables.variables_start, 1); + + let variable_slices = + SubsSlice::extend_new(&mut env.subs.variable_slices, [err_slice, ok_slice]); + + let union_tags = UnionTags::from_slices(tag_names_slice, variable_slices); + let ext_var = TagExt::Any(Variable::EMPTY_TAG_UNION); + let flat_type = FlatType::TagUnion(union_tags, ext_var); + let content = Content::Structure(flat_type); + + env.register(rank, content) + } + + /// Build an alias of the form `Num range := range` + fn build_num_opaque( + env: &mut Env, + rank: Rank, + symbol: Symbol, + range_var: Variable, + ) -> Variable { + let content = Content::Alias( + symbol, + AliasVariables::insert_into_subs(env.subs, [range_var], [], []), + range_var, + AliasKind::Opaque, + ); + + env.register(rank, content) + } + + fn instantiate_builtin_aliases_real_var( + &mut self, + env: &mut Env, + rank: Rank, + symbol: Symbol, + alias_variables: AliasVariables, + ) -> Option<(Variable, AliasKind)> { + match symbol { + Symbol::RESULT_RESULT => { + let var = Self::instantiate_result_result(env, rank, alias_variables); + + Some((var, AliasKind::Structural)) + } + Symbol::NUM_NUM | Symbol::NUM_INTEGER | Symbol::NUM_FLOATINGPOINT => { + // Num range := range | Integer range := range | FloatingPoint range := range + let range_var = env.subs.variables[alias_variables.variables_start as usize]; + Some((range_var, AliasKind::Opaque)) + } + Symbol::NUM_INT => { + // Int range : Num (Integer range) + // + // build `Integer range := range` + let integer_content_var = Self::build_num_opaque( + env, + rank, + Symbol::NUM_INTEGER, + env.subs.variables[alias_variables.variables_start as usize], + ); + + // build `Num (Integer range) := Integer range` + let num_content_var = + Self::build_num_opaque(env, rank, Symbol::NUM_NUM, integer_content_var); + + Some((num_content_var, AliasKind::Structural)) + } + Symbol::NUM_FRAC => { + // Frac range : Num (FloatingPoint range) + // + // build `FloatingPoint range := range` + let fpoint_content_var = Self::build_num_opaque( + env, + rank, + Symbol::NUM_FLOATINGPOINT, + env.subs.variables[alias_variables.variables_start as usize], + ); + + // build `Num (FloatingPoint range) := FloatingPoint range` + let num_content_var = + Self::build_num_opaque(env, rank, Symbol::NUM_NUM, fpoint_content_var); + + Some((num_content_var, AliasKind::Structural)) + } + Symbol::NUM_SIGNED8 => Some((Variable::SIGNED8, AliasKind::Opaque)), + Symbol::NUM_SIGNED16 => Some((Variable::SIGNED16, AliasKind::Opaque)), + Symbol::NUM_SIGNED32 => Some((Variable::SIGNED32, AliasKind::Opaque)), + Symbol::NUM_SIGNED64 => Some((Variable::SIGNED64, AliasKind::Opaque)), + Symbol::NUM_SIGNED128 => Some((Variable::SIGNED128, AliasKind::Opaque)), + Symbol::NUM_UNSIGNED8 => Some((Variable::UNSIGNED8, AliasKind::Opaque)), + Symbol::NUM_UNSIGNED16 => Some((Variable::UNSIGNED16, AliasKind::Opaque)), + Symbol::NUM_UNSIGNED32 => Some((Variable::UNSIGNED32, AliasKind::Opaque)), + Symbol::NUM_UNSIGNED64 => Some((Variable::UNSIGNED64, AliasKind::Opaque)), + Symbol::NUM_UNSIGNED128 => Some((Variable::UNSIGNED128, AliasKind::Opaque)), + Symbol::NUM_BINARY32 => Some((Variable::BINARY32, AliasKind::Opaque)), + Symbol::NUM_BINARY64 => Some((Variable::BINARY64, AliasKind::Opaque)), + _ => None, + } + } + + pub fn instantiate_real_var( + &mut self, + env: &mut Env, + rank: Rank, + problems: &mut Vec, + abilities_store: &AbilitiesStore, + obligation_cache: &mut ObligationCache, + arena: &bumpalo::Bump, + types: &mut Types, + symbol: Symbol, + alias_variables: AliasVariables, + ) -> (Variable, AliasKind) { + // hardcoded instantiations for builtin aliases + if let Some((var, kind)) = + self.instantiate_builtin_aliases_real_var(env, rank, symbol, alias_variables) + { + return (var, kind); + } + + let (typ, delayed_variables, kind) = + match self.aliases.iter().find(|(s, _, _, _)| *s == symbol) { + None => internal_error!( + "Alias {:?} not registered in delayed aliases! {:?}", + symbol, + &self.aliases + ), + Some(&(_, typ, delayed_variables, kind)) => (typ, delayed_variables, kind), + }; + + let mut substitutions: MutMap<_, _> = Default::default(); + + let old_type_variables = delayed_variables.type_variables(&mut self.variables); + let new_type_variables = &env.subs.variables[alias_variables.type_variables().indices()]; + + for (old, new) in old_type_variables.iter_mut().zip(new_type_variables) { + // if constraint gen duplicated a type these variables could be the same + // (happens very often in practice) + if old.var != *new { + substitutions.insert(old.var, *new); + } + } + + for OptAbleVar { + var: rec_var, + opt_abilities, + } in delayed_variables + .recursion_variables(&mut self.variables) + .iter_mut() + { + debug_assert!(opt_abilities.is_none()); + let new_var = env.subs.fresh_unnamed_flex_var(); + substitutions.insert(*rec_var, new_var); + } + + let old_lambda_set_variables = delayed_variables.lambda_set_variables(&mut self.variables); + let new_lambda_set_variables = + &env.subs.variables[alias_variables.lambda_set_variables().indices()]; + + for (old, new) in old_lambda_set_variables + .iter_mut() + .zip(new_lambda_set_variables) + { + debug_assert!(old.opt_abilities.is_none()); + if old.var != *new { + substitutions.insert(old.var, *new); + } + } + + let old_infer_ext_vars = + delayed_variables.infer_ext_in_output_variables(&mut self.variables); + let new_infer_ext_vars = + &env.subs.variables[alias_variables.infer_ext_in_output_variables().indices()]; + + for (old, new) in old_infer_ext_vars.iter_mut().zip(new_infer_ext_vars) { + debug_assert!(old.opt_abilities.is_none()); + if old.var != *new { + substitutions.insert(old.var, *new); + } + } + + let typ = if !substitutions.is_empty() { + types.clone_with_variable_substitutions(typ, &substitutions) + } else { + typ + }; + + let alias_variable = type_to_var_help( + env, + rank, + problems, + abilities_store, + obligation_cache, + arena, + self, + types, + typ, + false, + ); + (alias_variable, kind) + } +} diff --git a/crates/compiler/solve/src/deep_copy.rs b/crates/compiler/solve/src/deep_copy.rs new file mode 100644 index 00000000000..12eab6def5d --- /dev/null +++ b/crates/compiler/solve/src/deep_copy.rs @@ -0,0 +1,370 @@ +use std::ops::ControlFlow; + +use bumpalo::Bump; +use roc_error_macros::internal_error; +use roc_types::{ + subs::{ + self, AliasVariables, Content, Descriptor, FlatType, GetSubsSlice, Mark, OptVariable, Rank, + RecordFields, Subs, SubsSlice, TagExt, TupleElems, UnionLabels, Variable, + }, + types::{RecordField, Uls}, +}; + +use crate::env::Env; + +// TODO: eventually, we could possibly use the arena in Env instead. +pub(crate) fn deep_copy_var_in(env: &mut Env, rank: Rank, var: Variable, arena: &Bump) -> Variable { + let mut visited = bumpalo::collections::Vec::with_capacity_in(256, arena); + + let pool = env.pools.get_mut(rank); + + let var = env.subs.get_root_key(var); + match deep_copy_var_decision(env.subs, rank, var) { + ControlFlow::Break(copy) => copy, + ControlFlow::Continue(copy) => { + deep_copy_var_help(env.subs, rank, pool, &mut visited, var, copy); + + // we have tracked all visited variables, and can now traverse them + // in one go (without looking at the UnificationTable) and clear the copy field + for var in visited { + env.subs.set_copy_unchecked(var, OptVariable::NONE); + } + + copy + } + } +} + +#[inline] +fn has_trivial_copy(subs: &Subs, root_var: Variable) -> Option { + let existing_copy = subs.get_copy_unchecked(root_var); + + if let Some(copy) = existing_copy.into_variable() { + Some(copy) + } else if subs.get_rank_unchecked(root_var) != Rank::GENERALIZED { + Some(root_var) + } else { + None + } +} + +#[inline] +fn deep_copy_var_decision( + subs: &mut Subs, + max_rank: Rank, + var: Variable, +) -> ControlFlow { + let var = subs.get_root_key(var); + if let Some(copy) = has_trivial_copy(subs, var) { + ControlFlow::Break(copy) + } else { + let copy_descriptor = Descriptor { + content: Content::Structure(FlatType::EmptyTagUnion), + rank: max_rank, + mark: Mark::NONE, + copy: OptVariable::NONE, + }; + + let copy = subs.fresh(copy_descriptor); + + // Link the original variable to the new variable. This lets us + // avoid making multiple copies of the variable we are instantiating. + // + // Need to do this before recursively copying to avoid looping. + subs.set_mark_unchecked(var, Mark::NONE); + subs.set_copy_unchecked(var, copy.into()); + + ControlFlow::Continue(copy) + } +} + +fn deep_copy_var_help( + subs: &mut Subs, + max_rank: Rank, + pool: &mut Vec, + visited: &mut bumpalo::collections::Vec<'_, Variable>, + initial_source: Variable, + initial_copy: Variable, +) -> Variable { + use roc_types::subs::Content::*; + use roc_types::subs::FlatType::*; + + struct DeepCopyVarWork { + source: Variable, + copy: Variable, + } + + let initial = DeepCopyVarWork { + source: initial_source, + copy: initial_copy, + }; + let mut stack = vec![initial]; + + macro_rules! work { + ($variable:expr) => {{ + let var = subs.get_root_key($variable); + match deep_copy_var_decision(subs, max_rank, var) { + ControlFlow::Break(copy) => copy, + ControlFlow::Continue(copy) => { + stack.push(DeepCopyVarWork { source: var, copy }); + + copy + } + } + }}; + } + + macro_rules! copy_sequence { + ($length:expr, $variables:expr) => {{ + let new_variables = SubsSlice::reserve_into_subs(subs, $length as _); + for (target_index, var_index) in (new_variables.indices()).zip($variables) { + let var = subs[var_index]; + let copy_var = work!(var); + subs.variables[target_index] = copy_var; + } + + new_variables + }}; + } + + macro_rules! copy_union { + ($tags:expr) => {{ + let new_variable_slices = SubsSlice::reserve_variable_slices(subs, $tags.len()); + + let it = (new_variable_slices.indices()).zip($tags.variables()); + for (target_index, index) in it { + let slice = subs[index]; + + let new_variables = copy_sequence!(slice.len(), slice); + subs.variable_slices[target_index] = new_variables; + } + + UnionLabels::from_slices($tags.labels(), new_variable_slices) + }}; + } + + // When generalizing annotations with `Openness` extensions + // we want to promote them to `Any`, so that usages at + // specialized sites can grow unboundedly and are not bound to + // openness-polymorphism. + macro_rules! copy_tag_ext { + ($ext:expr) => { + TagExt::Any(work!($ext.var())) + }; + } + + while let Some(DeepCopyVarWork { source: var, copy }) = stack.pop() { + visited.push(var); + pool.push(copy); + + let content = *subs.get_content_unchecked(var); + + // Now we recursively copy the content of the variable. + // We have already marked the variable as copied, so we + // will not repeat this work or crawl this variable again. + match content { + Structure(flat_type) => { + let new_flat_type = match flat_type { + Apply(symbol, arguments) => { + let new_arguments = copy_sequence!(arguments.len(), arguments); + + Apply(symbol, new_arguments) + } + + Func(arguments, closure_var, ret_var) => { + let new_ret_var = work!(ret_var); + let new_closure_var = work!(closure_var); + + let new_arguments = copy_sequence!(arguments.len(), arguments); + + Func(new_arguments, new_closure_var, new_ret_var) + } + + same @ EmptyRecord | same @ EmptyTuple | same @ EmptyTagUnion => same, + + Record(fields, ext_var) => { + let record_fields = { + let new_variables = + copy_sequence!(fields.len(), fields.iter_variables()); + + // When copying a let-generalized record to a specialized region, rigid + // optionals just become optionals. + let field_types = subs.get_subs_slice(fields.record_fields()); + let has_rigid_optional_field = field_types + .iter() + .any(|f| matches!(f, RecordField::RigidOptional(..))); + + let new_field_types_start = if has_rigid_optional_field { + let field_types = field_types.to_vec(); + let slice = SubsSlice::extend_new( + &mut subs.record_fields, + field_types.into_iter().map(|f| match f { + RecordField::RigidOptional(()) + | RecordField::RigidRequired(()) => internal_error!("Rigid optional/required should be generalized to non-rigid by this point"), + + RecordField::Demanded(_) + | RecordField::Required(_) + | RecordField::Optional(_) => f, + }), + ); + slice.start + } else { + fields.field_types_start + }; + + RecordFields { + length: fields.length, + field_names_start: fields.field_names_start, + variables_start: new_variables.start, + field_types_start: new_field_types_start, + } + }; + + Record(record_fields, work!(ext_var)) + } + + Tuple(elems, ext_var) => { + let tuple_elems = { + let new_variables = copy_sequence!(elems.len(), elems.iter_variables()); + + TupleElems { + length: elems.length, + variables_start: new_variables.start, + elem_index_start: elems.elem_index_start, + } + }; + + Tuple(tuple_elems, work!(ext_var)) + } + + TagUnion(tags, ext_var) => { + let union_tags = copy_union!(tags); + + TagUnion(union_tags, copy_tag_ext!(ext_var)) + } + + FunctionOrTagUnion(tag_name, symbol, ext_var) => { + FunctionOrTagUnion(tag_name, symbol, copy_tag_ext!(ext_var)) + } + + RecursiveTagUnion(rec_var, tags, ext_var) => { + let union_tags = copy_union!(tags); + + RecursiveTagUnion(work!(rec_var), union_tags, copy_tag_ext!(ext_var)) + } + }; + + subs.set_content_unchecked(copy, Structure(new_flat_type)); + } + + FlexVar(_) | FlexAbleVar(_, _) | Error => { + subs.set_content_unchecked(copy, content); + } + + RecursionVar { + opt_name, + structure, + } => { + let content = RecursionVar { + opt_name, + structure: work!(structure), + }; + + subs.set_content_unchecked(copy, content); + } + + RigidVar(name) => { + subs.set_content_unchecked(copy, FlexVar(Some(name))); + } + + RigidAbleVar(name, ability) => { + subs.set_content_unchecked(copy, FlexAbleVar(Some(name), ability)); + } + + Alias(symbol, arguments, real_type_var, kind) => { + let new_variables = + copy_sequence!(arguments.all_variables_len, arguments.all_variables()); + + let new_arguments = AliasVariables { + variables_start: new_variables.start, + ..arguments + }; + + let new_real_type_var = work!(real_type_var); + let new_content = Alias(symbol, new_arguments, new_real_type_var, kind); + + subs.set_content_unchecked(copy, new_content); + } + + LambdaSet(subs::LambdaSet { + solved, + recursion_var, + unspecialized, + ambient_function: ambient_function_var, + }) => { + let lambda_set_var = copy; + + let new_solved = copy_union!(solved); + let new_rec_var = recursion_var.map(|v| work!(v)); + let new_unspecialized = SubsSlice::reserve_uls_slice(subs, unspecialized.len()); + + for (new_uls_index, uls_index) in + (new_unspecialized.into_iter()).zip(unspecialized.into_iter()) + { + let Uls(var, sym, region) = subs[uls_index]; + let new_var = work!(var); + + deep_copy_uls_precondition(subs, var, new_var); + + subs[new_uls_index] = Uls(new_var, sym, region); + + subs.uls_of_var.add(new_var, lambda_set_var); + } + + let new_ambient_function_var = work!(ambient_function_var); + debug_assert_ne!( + ambient_function_var, new_ambient_function_var, + "lambda set cloned but its ambient function wasn't?" + ); + + subs.set_content_unchecked( + lambda_set_var, + LambdaSet(subs::LambdaSet { + solved: new_solved, + recursion_var: new_rec_var, + unspecialized: new_unspecialized, + ambient_function: new_ambient_function_var, + }), + ); + } + + RangedNumber(range) => { + let new_content = RangedNumber(range); + + subs.set_content_unchecked(copy, new_content); + } + } + } + + initial_copy +} + +#[inline(always)] +fn deep_copy_uls_precondition(subs: &Subs, original_var: Variable, new_var: Variable) { + if cfg!(debug_assertions) { + let content = subs.get_content_without_compacting(original_var); + + debug_assert!( + matches!( + content, + Content::FlexAbleVar(..) | Content::RigidAbleVar(..) + ), + "var in unspecialized lamba set is not bound to an ability, it is {:?}", + roc_types::subs::SubsFmtContent(content, subs) + ); + debug_assert!( + original_var != new_var, + "unspecialized lamba set var was not instantiated" + ); + } +} diff --git a/crates/compiler/solve/src/env.rs b/crates/compiler/solve/src/env.rs new file mode 100644 index 00000000000..d55a571a904 --- /dev/null +++ b/crates/compiler/solve/src/env.rs @@ -0,0 +1,81 @@ +use bumpalo::Bump; +use roc_can::{constraint::Constraints, module::ExposedByModule}; +use roc_derive::SharedDerivedModule; +use roc_types::subs::{Content, Descriptor, Mark, OptVariable, Rank, Subs, Variable}; +use roc_unify::unify::Env as UEnv; + +use crate::Pools; + +pub struct DerivedEnv<'a> { + pub derived_module: &'a SharedDerivedModule, + /// Exposed types needed by the derived module. + pub exposed_types: &'a ExposedByModule, +} + +pub struct Env<'a> { + pub arena: &'a Bump, + pub constraints: &'a Constraints, + pub derived_env: &'a DerivedEnv<'a>, + pub subs: &'a mut Subs, + pub pools: &'a mut Pools, +} + +impl<'a> Env<'a> { + #[inline(always)] + pub fn register(&mut self, rank: Rank, content: Content) -> Variable { + let descriptor = Descriptor { + content, + rank, + mark: Mark::NONE, + copy: OptVariable::NONE, + }; + + let var = self.subs.fresh(descriptor); + + self.pools.get_mut(rank).push(var); + + var + } + + /// Introduce some variables to Pools at the given rank. + /// Also, set each of their ranks in Subs to be the given rank. + pub fn introduce(&mut self, rank: Rank, vars: &[Variable]) { + let pool: &mut Vec = self.pools.get_mut(rank); + + for &var in vars.iter() { + self.subs.set_rank(var, rank); + } + + pool.extend(vars); + } + + #[inline(always)] + pub fn register_existing_var(&mut self, var: Variable) { + self.pools.get_mut(self.subs.get_rank(var)).push(var); + } + + pub fn register_with_known_var( + &mut self, + var: Variable, + rank: Rank, + content: Content, + ) -> Variable { + let descriptor = Descriptor { + content, + rank, + mark: Mark::NONE, + copy: OptVariable::NONE, + }; + + self.subs.set(var, descriptor); + + self.pools.get_mut(rank).push(var); + + var + } + + /// Retrieves an environment for unification. + pub fn uenv(&mut self) -> UEnv { + UEnv::new(self.subs) + } +} diff --git a/crates/compiler/solve/src/lib.rs b/crates/compiler/solve/src/lib.rs index fa30f79aad4..d43fc7bff41 100644 --- a/crates/compiler/solve/src/lib.rs +++ b/crates/compiler/solve/src/lib.rs @@ -3,8 +3,20 @@ #![warn(clippy::dbg_macro)] // See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] +// TODO to be removed +#![allow(clippy::too_many_arguments)] pub mod ability; pub mod module; pub mod solve; pub mod specialize; + +mod aliases; +mod deep_copy; +mod env; +mod pools; +mod to_var; + +pub use aliases::Aliases; +pub use env::{DerivedEnv, Env}; +pub use pools::Pools; diff --git a/crates/compiler/solve/src/module.rs b/crates/compiler/solve/src/module.rs index 1920f46eefe..628fabd6d35 100644 --- a/crates/compiler/solve/src/module.rs +++ b/crates/compiler/solve/src/module.rs @@ -1,6 +1,6 @@ -use crate::solve::{self, Aliases}; +use crate::{aliases::Aliases, solve}; use roc_can::abilities::{AbilitiesStore, ResolvedImpl}; -use roc_can::constraint::{Constraint as ConstraintSoa, Constraints}; +use roc_can::constraint::{Constraint, Constraints}; use roc_can::expr::PendingDerives; use roc_can::module::{ExposedByModule, ResolvedImplementations, RigidVariables}; use roc_collections::all::MutMap; @@ -53,20 +53,41 @@ pub struct SolvedModule { pub exposed_types: ExposedTypesStorageSubs, } -#[allow(clippy::too_many_arguments)] // TODO: put params in a context/env var +pub struct SolveConfig<'a> { + /// The module we are solving. + pub home: ModuleId, + pub constraints: &'a Constraints, + pub root_constraint: Constraint, + /// All types introduced in the module. Canonicalized, but not necessarily yet associated with + /// a variable substitution. + pub types: Types, + /// Table of types introduced in this module that claim to derive an ability implementation. + /// Due for checking and instantiation after the solver runs over the module. + pub pending_derives: PendingDerives, + /// Types exposed by other modules. + /// Available for late instantiation of imports, lambda sets, or ability types. + pub exposed_by_module: &'a ExposedByModule, + /// The unique `#Derived` module, used to generate and retrieve derived ability + /// implementations. + /// Needed during solving to resolve lambda sets from derived implementations that escape into + /// the user module. + pub derived_module: SharedDerivedModule, +} + +pub struct SolveOutput { + pub subs: Solved, + pub scope: solve::Scope, + pub errors: Vec, + pub resolved_abilities_store: AbilitiesStore, +} + pub fn run_solve( - home: ModuleId, - types: Types, - constraints: &Constraints, - constraint: ConstraintSoa, + config: SolveConfig<'_>, rigid_variables: RigidVariables, mut subs: Subs, mut aliases: Aliases, mut abilities_store: AbilitiesStore, - pending_derives: PendingDerives, - exposed_by_module: &ExposedByModule, - derived_module: SharedDerivedModule, -) -> (Solved, solve::Env, Vec, AbilitiesStore) { +) -> SolveOutput { for (var, name) in rigid_variables.named { subs.rigid_var(var, name); } @@ -84,21 +105,20 @@ pub fn run_solve( let mut problems = Vec::new(); // Run the solver to populate Subs. - let (solved_subs, solved_env) = solve::run( - home, - types, - constraints, + let (solved_subs, solved_scope) = solve::run( + config, &mut problems, subs, &mut aliases, - &constraint, - pending_derives, &mut abilities_store, - exposed_by_module, - derived_module, ); - (solved_subs, solved_env, problems, abilities_store) + SolveOutput { + subs: solved_subs, + scope: solved_scope, + errors: problems, + resolved_abilities_store: abilities_store, + } } /// Copies exposed types and all ability specializations, which may be implicitly exposed. diff --git a/crates/compiler/solve/src/pools.rs b/crates/compiler/solve/src/pools.rs new file mode 100644 index 00000000000..877ebbec796 --- /dev/null +++ b/crates/compiler/solve/src/pools.rs @@ -0,0 +1,59 @@ +use roc_types::subs::{Rank, Variable}; + +const DEFAULT_POOLS: usize = 8; + +#[derive(Clone, Debug)] +pub struct Pools(Vec>); + +impl Default for Pools { + fn default() -> Self { + Pools::new(DEFAULT_POOLS) + } +} + +impl Pools { + pub fn new(num_pools: usize) -> Self { + Pools(vec![Vec::new(); num_pools]) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn get_mut(&mut self, rank: Rank) -> &mut Vec { + match self.0.get_mut(rank.into_usize()) { + Some(reference) => reference, + None => panic!("Compiler bug: could not find pool at rank {rank}"), + } + } + + pub fn get(&self, rank: Rank) -> &Vec { + match self.0.get(rank.into_usize()) { + Some(reference) => reference, + None => panic!("Compiler bug: could not find pool at rank {rank}"), + } + } + + pub fn iter(&self) -> std::slice::Iter<'_, Vec> { + self.0.iter() + } + + pub fn split_last(mut self) -> (Vec, Vec>) { + let last = self + .0 + .pop() + .unwrap_or_else(|| panic!("Attempted to split_last() on non-empty Pools")); + + (last, self.0) + } + + pub fn extend_to(&mut self, n: usize) { + for _ in self.len()..n { + self.0.push(Vec::new()); + } + } +} diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index ba319525376..ed55fa86155 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -1,47 +1,42 @@ -#![allow(clippy::too_many_arguments)] - use crate::ability::{ resolve_ability_specialization, type_implementing_specialization, AbilityImplError, CheckedDerives, ObligationCache, PendingDerivesTable, Resolved, }; -use crate::module::Solved; +use crate::deep_copy::deep_copy_var_in; +use crate::env::{DerivedEnv, Env}; +use crate::module::{SolveConfig, Solved}; +use crate::pools::Pools; use crate::specialize::{ - compact_lambda_sets_of_vars, AwaitingSpecializations, CompactionResult, DerivedEnv, SolvePhase, + compact_lambda_sets_of_vars, AwaitingSpecializations, CompactionResult, SolvePhase, }; +use crate::to_var::{either_type_index_to_var, type_to_var}; +use crate::Aliases; use bumpalo::Bump; use roc_can::abilities::{AbilitiesStore, MemberSpecializationInfo}; use roc_can::constraint::Constraint::{self, *}; -use roc_can::constraint::{Constraints, Cycle, LetConstraint, OpportunisticResolve, TypeOrVar}; +use roc_can::constraint::{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_collections::soa::{Index, Slice}; use roc_debug_flags::dbg_do; #[cfg(debug_assertions)] use roc_debug_flags::ROC_VERIFY_RIGID_LET_GENERALIZED; -use roc_derive::SharedDerivedModule; use roc_error_macros::internal_error; -use roc_module::ident::TagName; -use roc_module::symbol::{ModuleId, Symbol}; +use roc_module::symbol::Symbol; use roc_problem::can::CycleEntry; use roc_region::all::Loc; use roc_solve_problem::TypeError; use roc_types::subs::{ - self, AliasVariables, Content, Descriptor, FlatType, GetSubsSlice, LambdaSet, Mark, - OptVariable, Rank, RecordFields, Subs, SubsSlice, TagExt, TupleElems, UlsOfVar, UnionLabels, - UnionLambdas, UnionTags, Variable, VariableSubsSlice, -}; -use roc_types::types::{ - gather_fields_unsorted_iter, gather_tuple_elems_unsorted_iter, AliasKind, AliasShared, - Category, ExtImplicitOpenness, OptAbleVar, Polarity, Reason, RecordField, Type, TypeExtension, - TypeTag, Types, Uls, + self, Content, FlatType, GetSubsSlice, Mark, OptVariable, Rank, Subs, TagExt, UlsOfVar, + Variable, }; +use roc_types::types::{Category, Polarity, Reason, RecordField, Type, TypeExtension, Types, Uls}; use roc_unify::unify::{ - unify, unify_introduced_ability_specialization, Env as UEnv, Mode, Obligated, - SpecializationLsetCollector, Unified::*, + unify, unify_introduced_ability_specialization, Mode, Obligated, SpecializationLsetCollector, + Unified::*, }; +mod scope; +pub use scope::Scope; + // Type checking system adapted from Elm by Evan Czaplicki, BSD-3-Clause Licensed // https://github.com/elm/compiler // Thank you, Evan! @@ -92,465 +87,20 @@ use roc_unify::unify::{ // Ranks are used to limit the number of type variables considered for generalization. Only those inside // of the let (so those used in inferring the type of `\x -> x`) are considered. -use roc_types::types::Alias; - -#[derive(Debug, Clone, Copy)] -struct DelayedAliasVariables { - start: u32, - type_variables_len: u8, - lambda_set_variables_len: u8, - recursion_variables_len: u8, - infer_ext_in_output_variables_len: u8, -} - -impl DelayedAliasVariables { - fn recursion_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] { - let start = self.start as usize - + (self.type_variables_len + self.lambda_set_variables_len) as usize; - let length = self.recursion_variables_len as usize; - - &mut variables[start..][..length] - } - - fn lambda_set_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] { - let start = self.start as usize + self.type_variables_len as usize; - let length = self.lambda_set_variables_len as usize; - - &mut variables[start..][..length] - } - - fn type_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] { - let start = self.start as usize; - let length = self.type_variables_len as usize; - - &mut variables[start..][..length] - } - - fn infer_ext_in_output_variables(self, variables: &mut [OptAbleVar]) -> &mut [OptAbleVar] { - let start = self.start as usize - + (self.type_variables_len - + self.lambda_set_variables_len - + self.recursion_variables_len) as usize; - let length = self.infer_ext_in_output_variables_len as usize; - - &mut variables[start..][..length] - } -} - -#[derive(Debug, Default)] -pub struct Aliases { - aliases: Vec<(Symbol, Index, DelayedAliasVariables, AliasKind)>, - variables: Vec, -} - -impl Aliases { - pub fn with_capacity(cap: usize) -> Self { - Self { - aliases: Vec::with_capacity(cap), - variables: Vec::with_capacity(cap * 2), - } - } - - pub fn insert(&mut self, types: &mut Types, symbol: Symbol, alias: Alias) { - let alias_variables = - { - let start = self.variables.len() as _; - - self.variables.extend( - alias - .type_variables - .iter() - .map(|x| OptAbleVar::from(&x.value)), - ); - - self.variables.extend(alias.lambda_set_variables.iter().map( - |x| match x.as_inner() { - Type::Variable(v) => OptAbleVar::unbound(*v), - _ => unreachable!("lambda set type is not a variable"), - }, - )); - - let recursion_variables_len = alias.recursion_variables.len() as _; - self.variables.extend( - alias - .recursion_variables - .iter() - .copied() - .map(OptAbleVar::unbound), - ); - - self.variables.extend( - alias - .infer_ext_in_output_variables - .iter() - .map(|v| OptAbleVar::unbound(*v)), - ); - - DelayedAliasVariables { - start, - type_variables_len: alias.type_variables.len() as _, - lambda_set_variables_len: alias.lambda_set_variables.len() as _, - recursion_variables_len, - infer_ext_in_output_variables_len: alias.infer_ext_in_output_variables.len() - as _, - } - }; - - // TODO: can we construct Aliases from TypeTag directly? - let alias_typ = types.from_old_type(&alias.typ); - - self.aliases - .push((symbol, alias_typ, alias_variables, alias.kind)); - } - - fn instantiate_result_result( - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - alias_variables: AliasVariables, - ) -> Variable { - let tag_names_slice = Subs::RESULT_TAG_NAMES; - - let err_slice = SubsSlice::new(alias_variables.variables_start + 1, 1); - let ok_slice = SubsSlice::new(alias_variables.variables_start, 1); - - let variable_slices = - SubsSlice::extend_new(&mut subs.variable_slices, [err_slice, ok_slice]); - - let union_tags = UnionTags::from_slices(tag_names_slice, variable_slices); - let ext_var = TagExt::Any(Variable::EMPTY_TAG_UNION); - let flat_type = FlatType::TagUnion(union_tags, ext_var); - let content = Content::Structure(flat_type); - - register(subs, rank, pools, content) - } - - /// Build an alias of the form `Num range := range` - fn build_num_opaque( - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - symbol: Symbol, - range_var: Variable, - ) -> Variable { - let content = Content::Alias( - symbol, - AliasVariables::insert_into_subs(subs, [range_var], [], []), - range_var, - AliasKind::Opaque, - ); - - register(subs, rank, pools, content) - } - - fn instantiate_builtin_aliases_real_var( - &mut self, - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - symbol: Symbol, - alias_variables: AliasVariables, - ) -> Option<(Variable, AliasKind)> { - match symbol { - Symbol::RESULT_RESULT => { - let var = Self::instantiate_result_result(subs, rank, pools, alias_variables); - - Some((var, AliasKind::Structural)) - } - Symbol::NUM_NUM | Symbol::NUM_INTEGER | Symbol::NUM_FLOATINGPOINT => { - // Num range := range | Integer range := range | FloatingPoint range := range - let range_var = subs.variables[alias_variables.variables_start as usize]; - Some((range_var, AliasKind::Opaque)) - } - Symbol::NUM_INT => { - // Int range : Num (Integer range) - // - // build `Integer range := range` - let integer_content_var = Self::build_num_opaque( - subs, - rank, - pools, - Symbol::NUM_INTEGER, - subs.variables[alias_variables.variables_start as usize], - ); - - // build `Num (Integer range) := Integer range` - let num_content_var = - Self::build_num_opaque(subs, rank, pools, Symbol::NUM_NUM, integer_content_var); - - Some((num_content_var, AliasKind::Structural)) - } - Symbol::NUM_FRAC => { - // Frac range : Num (FloatingPoint range) - // - // build `FloatingPoint range := range` - let fpoint_content_var = Self::build_num_opaque( - subs, - rank, - pools, - Symbol::NUM_FLOATINGPOINT, - subs.variables[alias_variables.variables_start as usize], - ); - - // build `Num (FloatingPoint range) := FloatingPoint range` - let num_content_var = - Self::build_num_opaque(subs, rank, pools, Symbol::NUM_NUM, fpoint_content_var); - - Some((num_content_var, AliasKind::Structural)) - } - Symbol::NUM_SIGNED8 => Some((Variable::SIGNED8, AliasKind::Opaque)), - Symbol::NUM_SIGNED16 => Some((Variable::SIGNED16, AliasKind::Opaque)), - Symbol::NUM_SIGNED32 => Some((Variable::SIGNED32, AliasKind::Opaque)), - Symbol::NUM_SIGNED64 => Some((Variable::SIGNED64, AliasKind::Opaque)), - Symbol::NUM_SIGNED128 => Some((Variable::SIGNED128, AliasKind::Opaque)), - Symbol::NUM_UNSIGNED8 => Some((Variable::UNSIGNED8, AliasKind::Opaque)), - Symbol::NUM_UNSIGNED16 => Some((Variable::UNSIGNED16, AliasKind::Opaque)), - Symbol::NUM_UNSIGNED32 => Some((Variable::UNSIGNED32, AliasKind::Opaque)), - Symbol::NUM_UNSIGNED64 => Some((Variable::UNSIGNED64, AliasKind::Opaque)), - Symbol::NUM_UNSIGNED128 => Some((Variable::UNSIGNED128, AliasKind::Opaque)), - Symbol::NUM_BINARY32 => Some((Variable::BINARY32, AliasKind::Opaque)), - Symbol::NUM_BINARY64 => Some((Variable::BINARY64, AliasKind::Opaque)), - _ => None, - } - } - - fn instantiate_real_var( - &mut self, - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - problems: &mut Vec, - abilities_store: &AbilitiesStore, - obligation_cache: &mut ObligationCache, - arena: &bumpalo::Bump, - types: &mut Types, - symbol: Symbol, - alias_variables: AliasVariables, - ) -> (Variable, AliasKind) { - // hardcoded instantiations for builtin aliases - if let Some((var, kind)) = Self::instantiate_builtin_aliases_real_var( - self, - subs, - rank, - pools, - symbol, - alias_variables, - ) { - return (var, kind); - } - - let (typ, delayed_variables, kind) = - match self.aliases.iter().find(|(s, _, _, _)| *s == symbol) { - None => internal_error!( - "Alias {:?} not registered in delayed aliases! {:?}", - symbol, - &self.aliases - ), - Some(&(_, typ, delayed_variables, kind)) => (typ, delayed_variables, kind), - }; - - let mut substitutions: MutMap<_, _> = Default::default(); - - let old_type_variables = delayed_variables.type_variables(&mut self.variables); - let new_type_variables = &subs.variables[alias_variables.type_variables().indices()]; - - for (old, new) in old_type_variables.iter_mut().zip(new_type_variables) { - // if constraint gen duplicated a type these variables could be the same - // (happens very often in practice) - if old.var != *new { - substitutions.insert(old.var, *new); - } - } - - for OptAbleVar { - var: rec_var, - opt_abilities, - } in delayed_variables - .recursion_variables(&mut self.variables) - .iter_mut() - { - debug_assert!(opt_abilities.is_none()); - let new_var = subs.fresh_unnamed_flex_var(); - substitutions.insert(*rec_var, new_var); - } - - let old_lambda_set_variables = delayed_variables.lambda_set_variables(&mut self.variables); - let new_lambda_set_variables = - &subs.variables[alias_variables.lambda_set_variables().indices()]; - - for (old, new) in old_lambda_set_variables - .iter_mut() - .zip(new_lambda_set_variables) - { - debug_assert!(old.opt_abilities.is_none()); - if old.var != *new { - substitutions.insert(old.var, *new); - } - } - - let old_infer_ext_vars = - delayed_variables.infer_ext_in_output_variables(&mut self.variables); - let new_infer_ext_vars = - &subs.variables[alias_variables.infer_ext_in_output_variables().indices()]; - - for (old, new) in old_infer_ext_vars.iter_mut().zip(new_infer_ext_vars) { - debug_assert!(old.opt_abilities.is_none()); - if old.var != *new { - substitutions.insert(old.var, *new); - } - } - - let typ = if !substitutions.is_empty() { - types.clone_with_variable_substitutions(typ, &substitutions) - } else { - typ - }; - - let alias_variable = type_to_variable( - subs, - rank, - pools, - problems, - abilities_store, - obligation_cache, - arena, - self, - types, - typ, - false, - ); - (alias_variable, kind) - } -} - -#[derive(Clone, Debug, Default)] -pub struct Env { - symbols: Vec, - variables: Vec, -} - -impl Env { - pub fn vars_by_symbol(&self) -> impl Iterator + '_ { - let it1 = self.symbols.iter().copied(); - let it2 = self.variables.iter().copied(); - - it1.zip(it2) - } - - #[inline(always)] - fn get_var_by_symbol(&self, symbol: &Symbol) -> Option { - self.symbols - .iter() - .position(|s| s == symbol) - .map(|index| self.variables[index]) - } - - #[inline(always)] - fn insert_symbol_var_if_vacant(&mut self, symbol: Symbol, var: Variable) { - match self.symbols.iter().position(|s| *s == symbol) { - None => { - // symbol is not in vars_by_symbol yet; insert it - self.symbols.push(symbol); - self.variables.push(var); - } - Some(_) => { - // do nothing - } - } - } -} - -const DEFAULT_POOLS: usize = 8; - -#[derive(Clone, Debug)] -pub struct Pools(Vec>); - -impl Default for Pools { - fn default() -> Self { - Pools::new(DEFAULT_POOLS) - } -} - -impl Pools { - pub fn new(num_pools: usize) -> Self { - Pools(vec![Vec::new(); num_pools]) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - pub fn get_mut(&mut self, rank: Rank) -> &mut Vec { - match self.0.get_mut(rank.into_usize()) { - Some(reference) => reference, - None => panic!("Compiler bug: could not find pool at rank {}", rank), - } - } - - pub fn get(&self, rank: Rank) -> &Vec { - match self.0.get(rank.into_usize()) { - Some(reference) => reference, - None => panic!("Compiler bug: could not find pool at rank {}", rank), - } - } - - pub fn iter(&self) -> std::slice::Iter<'_, Vec> { - self.0.iter() - } - - pub fn split_last(mut self) -> (Vec, Vec>) { - let last = self - .0 - .pop() - .unwrap_or_else(|| panic!("Attempted to split_last() on non-empty Pools")); - - (last, self.0) - } - - pub fn extend_to(&mut self, n: usize) { - for _ in self.len()..n { - self.0.push(Vec::new()); - } - } -} - #[derive(Clone)] struct State { - env: Env, + scope: Scope, mark: Mark, } -#[allow(clippy::too_many_arguments)] // TODO: put params in a context/env var pub fn run( - home: ModuleId, - types: Types, - constraints: &Constraints, + config: SolveConfig, problems: &mut Vec, mut subs: Subs, aliases: &mut Aliases, - constraint: &Constraint, - pending_derives: PendingDerives, abilities_store: &mut AbilitiesStore, - exposed_by_module: &ExposedByModule, - derived_module: SharedDerivedModule, -) -> (Solved, Env) { - let env = run_in_place( - home, - types, - constraints, - problems, - &mut subs, - aliases, - constraint, - pending_derives, - abilities_store, - exposed_by_module, - derived_module, - ); +) -> (Solved, Scope) { + let env = run_in_place(config, problems, &mut subs, aliases, abilities_store); (Solved(subs), env) } @@ -558,22 +108,26 @@ 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? - mut types: Types, - constraints: &Constraints, + config: SolveConfig, problems: &mut Vec, subs: &mut Subs, aliases: &mut Aliases, - constraint: &Constraint, - pending_derives: PendingDerives, abilities_store: &mut AbilitiesStore, - exposed_by_module: &ExposedByModule, - derived_module: SharedDerivedModule, -) -> Env { +) -> Scope { + let SolveConfig { + home: _, + constraints, + root_constraint, + mut types, + pending_derives, + exposed_by_module, + derived_module, + } = config; + let mut pools = Pools::default(); let state = State { - env: Env::default(), + scope: Scope::default(), mark: Mark::NONE.next(), }; let rank = Rank::toplevel(); @@ -582,8 +136,21 @@ fn run_in_place( let mut obligation_cache = ObligationCache::default(); let mut awaiting_specializations = AwaitingSpecializations::default(); - let pending_derives = PendingDerivesTable::new( + let derived_env = DerivedEnv { + derived_module: &derived_module, + exposed_types: exposed_by_module, + }; + + let mut env = Env { + arena: &arena, + constraints, + derived_env: &derived_env, subs, + pools: &mut pools, + }; + + let pending_derives = PendingDerivesTable::new( + &mut env, &mut types, aliases, pending_derives, @@ -594,45 +161,36 @@ fn run_in_place( let CheckedDerives { legal_derives: _, problems: derives_problems, - } = obligation_cache.check_derives(subs, abilities_store, pending_derives); + } = obligation_cache.check_derives(env.subs, abilities_store, pending_derives); problems.extend(derives_problems); - let derived_env = DerivedEnv { - derived_module: &derived_module, - exposed_types: exposed_by_module, - }; - let state = solve( - &arena, + &mut env, types, - constraints, state, rank, - &mut pools, problems, aliases, - subs, - constraint, + &root_constraint, abilities_store, &mut obligation_cache, &mut awaiting_specializations, - &derived_env, ); - state.env + state.scope } #[derive(Debug)] enum Work<'a> { Constraint { - env: &'a Env, + scope: &'a Scope, rank: Rank, constraint: &'a Constraint, }, CheckForInfiniteTypes(LocalDefVarsVec<(Symbol, Loc)>), /// The ret_con part of a let constraint that does NOT introduces rigid and/or flex variables LetConNoVariables { - env: &'a Env, + scope: &'a Scope, rank: Rank, let_con: &'a LetConstraint, @@ -647,7 +205,7 @@ enum Work<'a> { /// These introduced variables must be generalized, hence this variant /// is more complex than `LetConNoVariables`. LetConIntroducesVariables { - env: &'a Env, + scope: &'a Scope, rank: Rank, let_con: &'a LetConstraint, @@ -659,25 +217,20 @@ enum Work<'a> { }, } -#[allow(clippy::too_many_arguments)] fn solve( - arena: &Bump, + env: &mut Env, mut can_types: Types, - constraints: &Constraints, mut state: State, rank: Rank, - pools: &mut Pools, problems: &mut Vec, aliases: &mut Aliases, - subs: &mut Subs, constraint: &Constraint, abilities_store: &mut AbilitiesStore, obligation_cache: &mut ObligationCache, awaiting_specializations: &mut AwaitingSpecializations, - derived_env: &DerivedEnv, ) -> State { let initial = Work::Constraint { - env: &Env::default(), + scope: &Scope::default(), rank, constraint, }; @@ -685,57 +238,52 @@ fn solve( let mut stack = vec![initial]; while let Some(work_item) = stack.pop() { - let (env, rank, constraint) = match work_item { + let (scope, rank, constraint) = match work_item { Work::Constraint { - env, + scope, rank, constraint, } => { // the default case; actually solve this constraint - (env, rank, constraint) + (scope, rank, constraint) } Work::CheckForInfiniteTypes(def_vars) => { // after a LetCon, we must check if any of the variables that we introduced // loop back to themselves after solving the ret_constraint for (symbol, loc_var) in def_vars.iter() { - check_for_infinite_type(subs, pools, problems, *symbol, *loc_var); + check_for_infinite_type(env, problems, *symbol, *loc_var); } continue; } Work::LetConNoVariables { - env, + scope, rank, let_con, pool_variables, } => { // NOTE be extremely careful with shadowing here let offset = let_con.defs_and_ret_constraint.index(); - let ret_constraint = &constraints.constraints[offset + 1]; + let ret_constraint = &env.constraints.constraints[offset + 1]; // Add a variable for each def to new_vars_by_env. let local_def_vars = LocalDefVarsVec::from_def_types( - constraints, + env, rank, - pools, problems, abilities_store, obligation_cache, &mut can_types, aliases, - subs, let_con.def_types, ); - pools.get_mut(rank).extend(pool_variables); + env.pools.get_mut(rank).extend(pool_variables); - let mut new_env = env.clone(); + let mut new_scope = scope.clone(); for (symbol, loc_var) in local_def_vars.iter() { check_ability_specialization( - arena, - subs, - derived_env, - pools, + env, rank, abilities_store, obligation_cache, @@ -745,11 +293,11 @@ fn solve( *loc_var, ); - new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value); + new_scope.insert_symbol_var_if_vacant(*symbol, loc_var.value); } stack.push(Work::Constraint { - env: arena.alloc(new_env), + scope: env.arena.alloc(new_scope), rank, constraint: ret_constraint, }); @@ -759,17 +307,17 @@ fn solve( continue; } Work::LetConIntroducesVariables { - env, + scope, rank, let_con, pool_variables, } => { // NOTE be extremely careful with shadowing here let offset = let_con.defs_and_ret_constraint.index(); - let ret_constraint = &constraints.constraints[offset + 1]; + let ret_constraint = &env.constraints.constraints[offset + 1]; let mark = state.mark; - let saved_env = state.env; + let saved_scope = state.scope; let young_mark = mark; let visit_mark = young_mark.next(); @@ -783,15 +331,13 @@ fn solve( // Add a variable for each def to local_def_vars. let local_def_vars = LocalDefVarsVec::from_def_types( - constraints, + env, intro_rank, - pools, problems, abilities_store, obligation_cache, &mut can_types, aliases, - subs, let_con.def_types, ); @@ -801,27 +347,28 @@ fn solve( // Otherwise, introduce all variables at the current rank; since none of them will // end up at the next rank, none will be generalized. if let_con.generalizable.0 { - pools.get_mut(rank.next()).extend(pool_variables); + env.pools.get_mut(rank.next()).extend(pool_variables); } else { - pools.get_mut(rank).extend(pool_variables); + env.pools.get_mut(rank).extend(pool_variables); } debug_assert_eq!( // Check that no variable ended up in a higher rank than the next rank.. that // would mean we generalized one level more than we need to! { - let offenders = pools + let offenders = env + .pools .get(rank.next()) .iter() .filter(|var| { - subs.get_rank(**var).into_usize() > rank.next().into_usize() + env.subs.get_rank(**var).into_usize() > rank.next().into_usize() }) .collect::>(); let result = offenders.len(); if result > 0 { - eprintln!("subs = {:?}", &subs); + eprintln!("subs = {:?}", &env.subs); eprintln!("offenders = {:?}", &offenders); eprintln!("let_con.def_types = {:?}", &let_con.def_types); } @@ -835,12 +382,12 @@ fn solve( // next rank. The variables introduced in the let-binding that are still at // that rank (intuitively, they did not "escape" into the lower level // before or after the let-binding) now get to be generalized. - generalize(subs, young_mark, visit_mark, rank.next(), pools); - debug_assert!(pools.get(rank.next()).is_empty(), "variables left over in let-binding scope, but they should all be in a lower scope or generalized now"); + generalize(env, young_mark, visit_mark, rank.next()); + debug_assert!(env.pools.get(rank.next()).is_empty(), "variables left over in let-binding scope, but they should all be in a lower scope or generalized now"); // check that things went well dbg_do!(ROC_VERIFY_RIGID_LET_GENERALIZED, { - let rigid_vars = &constraints.variables[let_con.rigid_vars.indices()]; + let rigid_vars = &env.constraints.variables[let_con.rigid_vars.indices()]; // NOTE the `subs.redundant` check does not come from elm. // It's unclear whether this is a bug with our implementation @@ -849,25 +396,23 @@ fn solve( let mut it = rigid_vars .iter() .filter(|&var| { - !subs.redundant(*var) && subs.get_rank(*var) != Rank::GENERALIZED + !env.subs.redundant(*var) + && env.subs.get_rank(*var) != Rank::GENERALIZED }) .peekable(); if it.peek().is_some() { let failing: Vec<_> = it.collect(); println!("Rigids {:?}", &rigid_vars); - println!("Failing {:?}", failing); + println!("Failing {failing:?}"); debug_assert!(false); } }); - let mut new_env = env.clone(); + let mut new_scope = scope.clone(); for (symbol, loc_var) in local_def_vars.iter() { check_ability_specialization( - arena, - subs, - derived_env, - pools, + env, rank, abilities_store, obligation_cache, @@ -877,20 +422,20 @@ fn solve( *loc_var, ); - new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value); + new_scope.insert_symbol_var_if_vacant(*symbol, loc_var.value); } // Note that this vars_by_symbol is the one returned by the // previous call to solve() let state_for_ret_con = State { - env: saved_env, + scope: saved_scope, mark: final_mark, }; // Now solve the body, using the new vars_by_symbol which includes // the assignments' name-to-variable mappings. stack.push(Work::Constraint { - env: arena.alloc(new_env), + scope: env.arena.alloc(new_scope), rank, constraint: ret_constraint, }); @@ -908,17 +453,16 @@ fn solve( SaveTheEnvironment => { let mut copy = state; - copy.env = env.clone(); + copy.scope = scope.clone(); copy } Eq(roc_can::constraint::Eq(type_index, expectation_index, category_index, region)) => { - let category = &constraints.categories[category_index.index()]; + let category = &env.constraints.categories[category_index.index()]; let actual = either_type_index_to_var( - subs, + env, rank, - pools, problems, abilities_store, obligation_cache, @@ -927,11 +471,10 @@ fn solve( *type_index, ); - let expectation = &constraints.expectations[expectation_index.index()]; + let expectation = &env.constraints.expectations[expectation_index.index()]; let expected = either_type_index_to_var( - subs, + env, rank, - pools, problems, abilities_store, obligation_cache, @@ -941,7 +484,7 @@ fn solve( ); match unify( - &mut UEnv::new(subs), + &mut env.uenv(), actual, expected, Mode::EQ, @@ -953,11 +496,11 @@ fn solve( lambda_sets_to_specialize, extra_metadata: _, } => { - introduce(subs, rank, pools, &vars); + env.introduce(rank, &vars); if !must_implement_ability.is_empty() { let new_problems = obligation_cache.check_obligations( - subs, + env.subs, abilities_store, must_implement_ability, AbilityImplError::BadExpr(*region, category.clone(), actual), @@ -965,21 +508,18 @@ fn solve( problems.extend(new_problems); } compact_lambdas_and_check_obligations( - arena, - pools, + env, problems, - subs, abilities_store, obligation_cache, awaiting_specializations, - derived_env, lambda_sets_to_specialize, ); state } Failure(vars, actual_type, expected_type, _bad_impls) => { - introduce(subs, rank, pools, &vars); + env.introduce(rank, &vars); let problem = TypeError::BadExpr( *region, @@ -998,9 +538,8 @@ fn solve( // a special version of Eq that is used to store types in the AST. // IT DOES NOT REPORT ERRORS! let actual = either_type_index_to_var( - subs, + env, rank, - pools, &mut vec![], // don't report any extra errors abilities_store, obligation_cache, @@ -1009,12 +548,12 @@ fn solve( *source_index, ); - let actual_desc = subs.get(actual); - subs.union(*target, actual, actual_desc); + let actual_desc = env.subs.get(actual); + env.subs.union(*target, actual, actual_desc); state } Lookup(symbol, expectation_index, region) => { - match env.get_var_by_symbol(symbol) { + match scope.get_var_by_symbol(symbol) { Some(var) => { // Deep copy the vars associated with this symbol before unifying them. // Otherwise, suppose we have this: @@ -1037,13 +576,12 @@ fn solve( // then we copy from that module's Subs into our own. If the value // is being looked up in this module, then we use our Subs as both // the source and destination. - let actual = deep_copy_var_in(subs, rank, pools, var, arena); - let expectation = &constraints.expectations[expectation_index.index()]; + let actual = deep_copy_var_in(env, rank, var, env.arena); + let expectation = &env.constraints.expectations[expectation_index.index()]; let expected = either_type_index_to_var( - subs, + env, rank, - pools, problems, abilities_store, obligation_cache, @@ -1053,7 +591,7 @@ fn solve( ); match unify( - &mut UEnv::new(subs), + &mut env.uenv(), actual, expected, Mode::EQ, @@ -1065,11 +603,11 @@ fn solve( lambda_sets_to_specialize, extra_metadata: _, } => { - introduce(subs, rank, pools, &vars); + env.introduce(rank, &vars); if !must_implement_ability.is_empty() { let new_problems = obligation_cache.check_obligations( - subs, + env.subs, abilities_store, must_implement_ability, AbilityImplError::BadExpr( @@ -1081,14 +619,11 @@ fn solve( problems.extend(new_problems); } compact_lambdas_and_check_obligations( - arena, - pools, + env, problems, - subs, abilities_store, obligation_cache, awaiting_specializations, - derived_env, lambda_sets_to_specialize, ); @@ -1096,7 +631,7 @@ fn solve( } Failure(vars, actual_type, expected_type, _bad_impls) => { - introduce(subs, rank, pools, &vars); + env.introduce(rank, &vars); let problem = TypeError::BadExpr( *region, @@ -1119,10 +654,10 @@ fn solve( } } And(slice) => { - let it = constraints.constraints[slice.indices()].iter().rev(); + let it = env.constraints.constraints[slice.indices()].iter().rev(); for sub_constraint in it { stack.push(Work::Constraint { - env, + scope, rank, constraint: sub_constraint, }) @@ -1132,12 +667,11 @@ fn solve( } Pattern(type_index, expectation_index, category_index, region) | PatternPresence(type_index, expectation_index, category_index, region) => { - let category = &constraints.pattern_categories[category_index.index()]; + let category = &env.constraints.pattern_categories[category_index.index()]; let actual = either_type_index_to_var( - subs, + env, rank, - pools, problems, abilities_store, obligation_cache, @@ -1146,11 +680,10 @@ fn solve( *type_index, ); - let expectation = &constraints.pattern_expectations[expectation_index.index()]; + let expectation = &env.constraints.pattern_expectations[expectation_index.index()]; let expected = either_type_index_to_var( - subs, + env, rank, - pools, problems, abilities_store, obligation_cache, @@ -1165,7 +698,7 @@ fn solve( }; match unify( - &mut UEnv::new(subs), + &mut env.uenv(), actual, expected, mode, @@ -1177,11 +710,11 @@ fn solve( lambda_sets_to_specialize, extra_metadata: _, } => { - introduce(subs, rank, pools, &vars); + env.introduce(rank, &vars); if !must_implement_ability.is_empty() { let new_problems = obligation_cache.check_obligations( - subs, + env.subs, abilities_store, must_implement_ability, AbilityImplError::BadPattern(*region, category.clone(), actual), @@ -1189,21 +722,18 @@ fn solve( problems.extend(new_problems); } compact_lambdas_and_check_obligations( - arena, - pools, + env, problems, - subs, abilities_store, obligation_cache, awaiting_specializations, - derived_env, lambda_sets_to_specialize, ); state } Failure(vars, actual_type, expected_type, _bad_impls) => { - introduce(subs, rank, pools, &vars); + env.introduce(rank, &vars); let problem = TypeError::BadPattern( *region, @@ -1219,26 +749,26 @@ fn solve( } } Let(index, pool_slice) => { - let let_con = &constraints.let_constraints[index.index()]; + let let_con = &env.constraints.let_constraints[index.index()]; let offset = let_con.defs_and_ret_constraint.index(); - let defs_constraint = &constraints.constraints[offset]; - let ret_constraint = &constraints.constraints[offset + 1]; + let defs_constraint = &env.constraints.constraints[offset]; + let ret_constraint = &env.constraints.constraints[offset + 1]; - let flex_vars = &constraints.variables[let_con.flex_vars.indices()]; - let rigid_vars = &constraints.variables[let_con.rigid_vars.indices()]; + let flex_vars = &env.constraints.variables[let_con.flex_vars.indices()]; + let rigid_vars = &env.constraints.variables[let_con.rigid_vars.indices()]; - let pool_variables = &constraints.variables[pool_slice.indices()]; + let pool_variables = &env.constraints.variables[pool_slice.indices()]; if matches!(&ret_constraint, True) && let_con.rigid_vars.is_empty() { debug_assert!(pool_variables.is_empty()); - introduce(subs, rank, pools, flex_vars); + env.introduce(rank, flex_vars); // If the return expression is guaranteed to solve, // solve the assignments themselves and move on. stack.push(Work::Constraint { - env, + scope, rank, constraint: defs_constraint, }); @@ -1251,13 +781,13 @@ fn solve( // Note that the LetConSimple gets the current env and rank, // and not the env/rank from after solving the defs_constraint stack.push(Work::LetConNoVariables { - env, + scope, rank, let_con, pool_variables, }); stack.push(Work::Constraint { - env, + scope, rank, constraint: defs_constraint, }); @@ -1274,21 +804,21 @@ fn solve( }; // determine the next pool - if binding_rank.into_usize() < pools.len() { + if binding_rank.into_usize() < env.pools.len() { // Nothing to do, we already accounted for the next rank, no need to // adjust the pools } else { // we should be off by one at this point - debug_assert_eq!(binding_rank.into_usize(), 1 + pools.len()); - pools.extend_to(binding_rank.into_usize()); + debug_assert_eq!(binding_rank.into_usize(), 1 + env.pools.len()); + env.pools.extend_to(binding_rank.into_usize()); } - let pool: &mut Vec = pools.get_mut(binding_rank); + let pool: &mut Vec = env.pools.get_mut(binding_rank); // Introduce the variables of this binding, and extend the pool at our binding // rank. for &var in rigid_vars.iter().chain(flex_vars.iter()) { - subs.set_rank(var, binding_rank); + env.subs.set_rank(var, binding_rank); } pool.reserve(rigid_vars.len() + flex_vars.len()); pool.extend(rigid_vars.iter()); @@ -1304,13 +834,13 @@ fn solve( // That's because the defs constraints will be solved in next_rank if it is eligible for generalization. // The LetCon will then generalize variables that are at a higher rank than the rank of the current scope. stack.push(Work::LetConIntroducesVariables { - env, + scope, rank, let_con, pool_variables, }); stack.push(Work::Constraint { - env, + scope, rank: binding_rank, constraint: defs_constraint, }); @@ -1320,9 +850,8 @@ fn solve( } IsOpenType(type_index) => { let actual = either_type_index_to_var( - subs, + env, rank, - pools, problems, abilities_store, obligation_cache, @@ -1331,12 +860,12 @@ fn solve( *type_index, ); - open_tag_union(subs, pools, actual); + open_tag_union(env, actual); state } IncludesTag(index) => { - let includes_tag = &constraints.includes_tags[index.index()]; + let includes_tag = &env.constraints.includes_tags[index.index()]; let roc_can::constraint::IncludesTag { type_index, @@ -1346,12 +875,12 @@ fn solve( region, } = includes_tag; - let pattern_category = &constraints.pattern_categories[pattern_category.index()]; + let pattern_category = + &env.constraints.pattern_categories[pattern_category.index()]; let actual = either_type_index_to_var( - subs, + env, rank, - pools, problems, abilities_store, obligation_cache, @@ -1360,7 +889,7 @@ fn solve( *type_index, ); - let payload_types = constraints.variables[types.indices()] + let payload_types = env.constraints.variables[types.indices()] .iter() .map(|v| Type::Variable(*v)) .collect(); @@ -1370,19 +899,18 @@ fn solve( TypeExtension::Closed, )); let includes = type_to_var( - subs, + env, rank, problems, abilities_store, obligation_cache, - pools, &mut can_types, aliases, tag_ty, ); match unify( - &mut UEnv::new(subs), + &mut env.uenv(), actual, includes, Mode::PRESENT, @@ -1394,11 +922,11 @@ fn solve( lambda_sets_to_specialize, extra_metadata: _, } => { - introduce(subs, rank, pools, &vars); + env.introduce(rank, &vars); if !must_implement_ability.is_empty() { let new_problems = obligation_cache.check_obligations( - subs, + env.subs, abilities_store, must_implement_ability, AbilityImplError::BadPattern( @@ -1410,21 +938,18 @@ fn solve( problems.extend(new_problems); } compact_lambdas_and_check_obligations( - arena, - pools, + env, problems, - subs, abilities_store, obligation_cache, awaiting_specializations, - derived_env, lambda_sets_to_specialize, ); state } Failure(vars, actual_type, expected_to_include_type, _bad_impls) => { - introduce(subs, rank, pools, &vars); + env.introduce(rank, &vars); let problem = TypeError::BadPattern( *region, @@ -1453,8 +978,8 @@ fn solve( let (real_var, real_region, branches_var, category_and_expected) = match eq { Ok(eq) => { let roc_can::constraint::Eq(real_var, expected, category, real_region) = - constraints.eq[eq.index()]; - let expected = &constraints.expectations[expected.index()]; + env.constraints.eq[eq.index()]; + let expected = &env.constraints.expectations[expected.index()]; ( real_var, @@ -1469,8 +994,8 @@ fn solve( expected, category, real_region, - ) = constraints.pattern_eq[peq.index()]; - let expected = &constraints.pattern_expectations[expected.index()]; + ) = env.constraints.pattern_eq[peq.index()]; + let expected = &env.constraints.pattern_expectations[expected.index()]; ( real_var, @@ -1482,9 +1007,8 @@ fn solve( }; let real_var = either_type_index_to_var( - subs, + env, rank, - pools, problems, abilities_store, obligation_cache, @@ -1494,9 +1018,8 @@ fn solve( ); let branches_var = either_type_index_to_var( - subs, + env, rank, - pools, problems, abilities_store, obligation_cache, @@ -1512,16 +1035,16 @@ fn solve( Polarity::OF_PATTERN }; - let real_content = subs.get_content_without_compacting(real_var); - let branches_content = subs.get_content_without_compacting(branches_var); + let real_content = env.subs.get_content_without_compacting(real_var); + let branches_content = env.subs.get_content_without_compacting(branches_var); let already_have_error = matches!( (real_content, branches_content), (Content::Error, _) | (_, Content::Error) ); - let snapshot = subs.snapshot(); + let snapshot = env.subs.snapshot(); let unify_cond_and_patterns_outcome = unify( - &mut UEnv::new(subs), + &mut env.uenv(), branches_var, real_var, Mode::EQ, @@ -1538,25 +1061,22 @@ fn solve( lambda_sets_to_specialize, extra_metadata: _, } => { - subs.commit_snapshot(snapshot); + env.subs.commit_snapshot(snapshot); - introduce(subs, rank, pools, &vars); + env.introduce(rank, &vars); problems.extend(obligation_cache.check_obligations( - subs, + env.subs, abilities_store, must_implement_ability, AbilityImplError::DoesNotImplement, )); compact_lambdas_and_check_obligations( - arena, - pools, + env, problems, - subs, abilities_store, obligation_cache, awaiting_specializations, - derived_env, lambda_sets_to_specialize, ); @@ -1566,15 +1086,15 @@ fn solve( } Failure(..) => { // Rollback and check for almost-equality. - subs.rollback_to(snapshot); + env.subs.rollback_to(snapshot); - let almost_eq_snapshot = subs.snapshot(); + let almost_eq_snapshot = env.subs.snapshot(); // TODO: turn this on for bidirectional exhaustiveness checking // open_tag_union(subs, real_var); - open_tag_union(subs, pools, branches_var); + open_tag_union(env, branches_var); let almost_eq = matches!( unify( - &mut UEnv::new(subs), + &mut env.uenv(), real_var, branches_var, Mode::EQ, @@ -1583,7 +1103,7 @@ fn solve( Success { .. } ); - subs.rollback_to(almost_eq_snapshot); + env.subs.rollback_to(almost_eq_snapshot); if almost_eq { // Case 3: almost equal, check exhaustiveness. @@ -1592,21 +1112,22 @@ fn solve( // Case 4: incompatible types, report type error. // Re-run first failed unification to get the type diff. match unify( - &mut UEnv::new(subs), + &mut env.uenv(), real_var, branches_var, Mode::EQ, cond_polarity, ) { Failure(vars, actual_type, expected_type, _bad_impls) => { - introduce(subs, rank, pools, &vars); + env.introduce(rank, &vars); // Figure out the problem - it might be pattern or value // related. let problem = match category_and_expected { Ok((category, expected)) => { - let real_category = - constraints.categories[category.index()].clone(); + let real_category = env.constraints.categories + [category.index()] + .clone(); TypeError::BadExpr( real_region, real_category, @@ -1616,7 +1137,7 @@ fn solve( } Err((category, expected)) => { - let real_category = constraints.pattern_categories + let real_category = env.constraints.pattern_categories [category.index()] .clone(); TypeError::BadPattern( @@ -1637,7 +1158,7 @@ fn solve( } } - let sketched_rows = constraints.sketched_rows[sketched_rows.index()].clone(); + let sketched_rows = env.constraints.sketched_rows[sketched_rows.index()].clone(); if should_check_exhaustiveness { use roc_can::exhaustive::{check, ExhaustiveSummary}; @@ -1684,23 +1205,23 @@ fn solve( // TODO: this can likely be removed after remodelling tag extension types // (#4440). if cond_source_is_likely_positive_value && has_unification_error { - close_pattern_matched_tag_unions(subs, real_var); + close_pattern_matched_tag_unions(env.subs, real_var); } if let Ok(ExhaustiveSummary { errors, exhaustive, redundancies, - }) = check(subs, real_var, sketched_rows, context) + }) = check(env.subs, real_var, sketched_rows, context) { // Store information about whether the "when" is exhaustive, and // which (if any) of its branches are redundant. Codegen may use // this for branch-fixing and redundant elimination. if !exhaustive { - exhaustive_mark.set_non_exhaustive(subs); + exhaustive_mark.set_non_exhaustive(env.subs); } for redundant_mark in redundancies { - redundant_mark.set_redundant(subs); + redundant_mark.set_redundant(env.subs); } // Store the errors. @@ -1719,7 +1240,7 @@ fn solve( specialization_id, }) => { if let Ok(Resolved::Specialization(specialization)) = resolve_ability_specialization( - subs, + env.subs, abilities_store, member, specialization_variable, @@ -1733,8 +1254,8 @@ fn solve( let Cycle { def_names, expr_regions, - } = &constraints.cycles[cycle.index()]; - let symbols = &constraints.loc_symbols[def_names.indices()]; + } = &env.constraints.cycles[cycle.index()]; + let symbols = &env.constraints.loc_symbols[def_names.indices()]; // If the type of a symbol is not a function, that's an error. // Roc is strict, so only functions can be mutually recursive. @@ -1742,8 +1263,8 @@ fn solve( use Content::*; symbols.iter().any(|(s, _)| { - let var = env.get_var_by_symbol(s).expect("Symbol not solved!"); - let (_, underlying_content) = chase_alias_content(subs, var); + let var = scope.get_var_by_symbol(s).expect("Symbol not solved!"); + let (_, underlying_content) = chase_alias_content(env.subs, var); !matches!(underlying_content, Error | Structure(FlatType::Func(..))) }) @@ -1752,7 +1273,7 @@ fn solve( if any_is_bad { // expr regions are stored in loc_symbols (that turned out to be convenient). // The symbol is just a dummy, and should not be used - let expr_regions = &constraints.loc_symbols[expr_regions.indices()]; + let expr_regions = &env.constraints.loc_symbols[expr_regions.indices()]; let cycle = symbols .iter() @@ -1766,16 +1287,15 @@ fn solve( problems.push(TypeError::CircularDef(cycle)); - cycle_mark.set_illegal(subs); + cycle_mark.set_illegal(env.subs); } state } IngestedFile(type_index, file_path, bytes) => { let actual = either_type_index_to_var( - subs, + env, rank, - pools, problems, abilities_store, obligation_cache, @@ -1784,21 +1304,21 @@ fn solve( *type_index, ); - let snapshot = subs.snapshot(); + let snapshot = env.subs.snapshot(); if let Success { vars, must_implement_ability, lambda_sets_to_specialize, extra_metadata: _, } = unify( - &mut UEnv::new(subs), + &mut env.uenv(), actual, Variable::LIST_U8, Mode::EQ, Polarity::OF_VALUE, ) { // List U8 always valid. - introduce(subs, rank, pools, &vars); + env.introduce(rank, &vars); debug_assert!( must_implement_ability.is_empty() && lambda_sets_to_specialize.is_empty(), @@ -1807,11 +1327,11 @@ fn solve( state } else { - subs.rollback_to(snapshot); + env.subs.rollback_to(snapshot); // We explicitly match on the last unify to get the type in the case it errors. match unify( - &mut UEnv::new(subs), + &mut env.uenv(), actual, Variable::STR, Mode::EQ, @@ -1823,7 +1343,7 @@ fn solve( lambda_sets_to_specialize, extra_metadata: _, } => { - introduce(subs, rank, pools, &vars); + env.introduce(rank, &vars); debug_assert!( must_implement_ability.is_empty() && lambda_sets_to_specialize.is_empty(), @@ -1840,7 +1360,7 @@ fn solve( state } Failure(vars, actual_type, _, _) => { - introduce(subs, rank, pools, &vars); + env.introduce(rank, &vars); let problem = TypeError::IngestedFileUnsupportedType( file_path.clone(), @@ -1869,31 +1389,24 @@ fn chase_alias_content(subs: &Subs, mut var: Variable) -> (Variable, &Content) { } } -#[allow(clippy::too_many_arguments)] fn compact_lambdas_and_check_obligations( - arena: &Bump, - pools: &mut Pools, + env: &mut Env, problems: &mut Vec, - subs: &mut Subs, abilities_store: &mut AbilitiesStore, obligation_cache: &mut ObligationCache, awaiting_specialization: &mut AwaitingSpecializations, - derived_env: &DerivedEnv, lambda_sets_to_specialize: UlsOfVar, ) { let CompactionResult { obligations, awaiting_specialization: new_awaiting, } = compact_lambda_sets_of_vars( - subs, - derived_env, - arena, - pools, + env, lambda_sets_to_specialize, &SolvePhase { abilities_store }, ); problems.extend(obligation_cache.check_obligations( - subs, + env.subs, abilities_store, obligations, AbilityImplError::DoesNotImplement, @@ -1901,39 +1414,44 @@ fn compact_lambdas_and_check_obligations( awaiting_specialization.union(new_awaiting); } -fn open_tag_union(subs: &mut Subs, pools: &mut Pools, var: Variable) { +fn open_tag_union(env: &mut Env, var: Variable) { let mut stack = vec![var]; while let Some(var) = stack.pop() { use {Content::*, FlatType::*}; - let desc = subs.get(var); + let desc = env.subs.get(var); match desc.content { Structure(TagUnion(tags, ext)) => { - if let Structure(EmptyTagUnion) = subs.get_content_without_compacting(ext.var()) { - let new_ext_var = register(subs, desc.rank, pools, Content::FlexVar(None)); + if let Structure(EmptyTagUnion) = env.subs.get_content_without_compacting(ext.var()) + { + let new_ext_var = env.register(desc.rank, Content::FlexVar(None)); let new_union = Structure(TagUnion(tags, TagExt::Any(new_ext_var))); - subs.set_content(var, new_union); + env.subs.set_content(var, new_union); } // Also open up all nested tag unions. let all_vars = tags.variables().into_iter(); - stack.extend(all_vars.flat_map(|slice| subs[slice]).map(|var| subs[var])); + stack.extend( + all_vars + .flat_map(|slice| env.subs[slice]) + .map(|var| env.subs[var]), + ); } Structure(Record(fields, _)) => { // Open up all nested tag unions. - stack.extend(subs.get_subs_slice(fields.variables())); + stack.extend(env.subs.get_subs_slice(fields.variables())); } Structure(Tuple(elems, _)) => { // Open up all nested tag unions. - stack.extend(subs.get_subs_slice(elems.variables())); + stack.extend(env.subs.get_subs_slice(elems.variables())); } Structure(Apply(Symbol::LIST_LIST, args)) => { // Open up nested tag unions. - stack.extend(subs.get_subs_slice(args)); + stack.extend(env.subs.get_subs_slice(args)); } _ => { @@ -2025,14 +1543,10 @@ fn close_pattern_matched_tag_unions(subs: &mut Subs, var: Variable) { /// If a symbol claims to specialize an ability member, check that its solved type in fact /// does specialize the ability, and record the specialization. -#[allow(clippy::too_many_arguments)] // Aggressive but necessary - there aren't many usages. #[inline(always)] fn check_ability_specialization( - arena: &Bump, - subs: &mut Subs, - derived_env: &DerivedEnv, - pools: &mut Pools, + env: &mut Env, rank: Rank, abilities_store: &mut AbilitiesStore, obligation_cache: &mut ObligationCache, @@ -2057,10 +1571,10 @@ fn check_ability_specialization( // We need to freshly instantiate the root signature so that all unifications are reflected // in the specialization type, but not the original signature type. let root_signature_var = - deep_copy_var_in(subs, Rank::toplevel(), pools, root_signature_var, arena); - let snapshot = subs.snapshot(); + deep_copy_var_in(env, Rank::toplevel(), root_signature_var, env.arena); + let snapshot = env.subs.snapshot(); let unified = unify_introduced_ability_specialization( - &mut UEnv::new(subs), + &mut env.uenv(), root_signature_var, symbol_loc_var.value, Mode::EQ, @@ -2083,8 +1597,8 @@ fn check_ability_specialization( if opaque == impl_key.opaque { // It was! All is good. - subs.commit_snapshot(snapshot); - introduce(subs, rank, pools, &vars); + env.subs.commit_snapshot(snapshot); + env.introduce(rank, &vars); let specialization_lambda_sets = specialization_lambda_sets .into_iter() @@ -2095,14 +1609,11 @@ fn check_ability_specialization( .collect(); compact_lambdas_and_check_obligations( - arena, - pools, + env, problems, - subs, abilities_store, obligation_cache, awaiting_specializations, - derived_env, lambda_sets_to_specialize, ); @@ -2115,10 +1626,11 @@ fn check_ability_specialization( // error. // Commit so that the bad signature and its error persists in subs. - subs.commit_snapshot(snapshot); + env.subs.commit_snapshot(snapshot); - let _typ = - subs.var_to_error_type(symbol_loc_var.value, Polarity::OF_VALUE); + let _typ = env + .subs + .var_to_error_type(symbol_loc_var.value, Polarity::OF_VALUE); let problem = TypeError::WrongSpecialization { region: symbol_loc_var.region, @@ -2136,9 +1648,9 @@ fn check_ability_specialization( // This is a specialization of a structural type - never allowed. // Commit so that `var` persists in subs. - subs.commit_snapshot(snapshot); + env.subs.commit_snapshot(snapshot); - let typ = subs.var_to_error_type(var, Polarity::OF_VALUE); + let typ = env.subs.var_to_error_type(var, Polarity::OF_VALUE); let problem = TypeError::StructuralSpecialization { region: symbol_loc_var.region, @@ -2158,12 +1670,14 @@ fn check_ability_specialization( // Rollback the snapshot so we unlink the root signature with the specialization, // so we can have two separate error types. - subs.rollback_to(snapshot); + env.subs.rollback_to(snapshot); - let expected_type = - subs.var_to_error_type(root_signature_var, Polarity::OF_VALUE); - let actual_type = - subs.var_to_error_type(symbol_loc_var.value, Polarity::OF_VALUE); + let expected_type = env + .subs + .var_to_error_type(root_signature_var, Polarity::OF_VALUE); + let actual_type = env + .subs + .var_to_error_type(symbol_loc_var.value, Polarity::OF_VALUE); let reason = Reason::GeneralizedAbilityMemberSpecialization { member_name: ability_member, @@ -2185,8 +1699,8 @@ fn check_ability_specialization( } Failure(vars, expected_type, actual_type, unimplemented_abilities) => { - subs.commit_snapshot(snapshot); - introduce(subs, rank, pools, &vars); + env.subs.commit_snapshot(snapshot); + env.introduce(rank, &vars); let reason = Reason::InvalidAbilityMemberSpecialization { member_name: ability_member, @@ -2214,22 +1728,18 @@ fn check_ability_specialization( // Get the lambda sets that are ready for specialization because this ability member // specialization was resolved, and compact them. let new_lambda_sets_to_specialize = - awaiting_specializations.remove_for_specialized(subs, impl_key); + awaiting_specializations.remove_for_specialized(env.subs, impl_key); compact_lambdas_and_check_obligations( - arena, - pools, + env, problems, - subs, abilities_store, obligation_cache, awaiting_specializations, - derived_env, new_lambda_sets_to_specialize, ); debug_assert!( !awaiting_specializations.waiting_for(impl_key), - "still have lambda sets waiting for {:?}, but it was just resolved", - impl_key + "still have lambda sets waiting for {impl_key:?}, but it was just resolved" ); } } @@ -2267,27 +1777,24 @@ impl LocalDefVarsVec { impl LocalDefVarsVec<(Symbol, Loc)> { fn from_def_types( - constraints: &Constraints, + env: &mut Env, rank: Rank, - pools: &mut Pools, problems: &mut Vec, abilities_store: &mut AbilitiesStore, obligation_cache: &mut ObligationCache, types: &mut Types, aliases: &mut Aliases, - subs: &mut Subs, def_types_slice: roc_can::constraint::DefTypes, ) -> Self { - let type_indices_slice = &constraints.type_slices[def_types_slice.types.indices()]; - let loc_symbols_slice = &constraints.loc_symbols[def_types_slice.loc_symbols.indices()]; + let type_indices_slice = &env.constraints.type_slices[def_types_slice.types.indices()]; + let loc_symbols_slice = &env.constraints.loc_symbols[def_types_slice.loc_symbols.indices()]; let mut local_def_vars = Self::with_length(type_indices_slice.len()); for (&(symbol, region), typ_index) in (loc_symbols_slice.iter()).zip(type_indices_slice) { let var = either_type_index_to_var( - subs, + env, rank, - pools, problems, abilities_store, obligation_cache, @@ -2303,1334 +1810,46 @@ impl LocalDefVarsVec<(Symbol, Loc)> { } } -use std::cell::RefCell; -use std::ops::ControlFlow; -std::thread_local! { - /// Scratchpad arena so we don't need to allocate a new one all the time - static SCRATCHPAD: RefCell> = RefCell::new(Some(bumpalo::Bump::with_capacity(4 * 1024))); -} - -fn take_scratchpad() -> bumpalo::Bump { - SCRATCHPAD.with(|f| f.take().unwrap()) -} +fn check_for_infinite_type( + env: &mut Env, + problems: &mut Vec, + symbol: Symbol, + loc_var: Loc, +) { + let var = loc_var.value; -fn put_scratchpad(scratchpad: bumpalo::Bump) { - SCRATCHPAD.with(|f| { - f.replace(Some(scratchpad)); - }); -} + 'next_occurs_check: while let Err((_, chain)) = env.subs.occurs(var) { + // walk the chain till we find a tag union or lambda set, starting from the variable that + // occurred recursively, which is always at the end of the chain. + for &var in chain.iter().rev() { + match *env.subs.get_content_without_compacting(var) { + Content::Structure(FlatType::TagUnion(tags, ext_var)) => { + let rec_var = env.subs.mark_tag_union_recursive(var, tags, ext_var); + env.register_existing_var(rec_var); -fn either_type_index_to_var( - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - problems: &mut Vec, - abilities_store: &mut AbilitiesStore, - obligation_cache: &mut ObligationCache, - types: &mut Types, - aliases: &mut Aliases, - either_type_index: TypeOrVar, -) -> Variable { - match either_type_index.split() { - Ok(type_index) => { - // Converts the celled type to a variable, emplacing the new variable for re-use. - let var = type_to_var( - subs, - rank, - problems, - abilities_store, - obligation_cache, - pools, - types, - aliases, - type_index, - ); + continue 'next_occurs_check; + } + Content::LambdaSet(subs::LambdaSet { + solved, + recursion_var: OptVariable::NONE, + unspecialized, + ambient_function: ambient_function_var, + }) => { + let rec_var = env.subs.mark_lambda_set_recursive( + var, + solved, + unspecialized, + ambient_function_var, + ); + env.register_existing_var(rec_var); - debug_assert!( - matches!(types[type_index], TypeTag::Variable(v) if v == var) - || matches!( - types[type_index], - TypeTag::EmptyRecord | TypeTag::EmptyTagUnion - ) - ); - var - } - Err(var_index) => { - // we cheat, and store the variable directly in the index - unsafe { Variable::from_index(var_index.index() as _) } + continue 'next_occurs_check; + } + _ => { /* fall through */ } + } } - } -} - -pub(crate) fn type_to_var( - subs: &mut Subs, - rank: Rank, - problems: &mut Vec, - abilities_store: &mut AbilitiesStore, - obligation_cache: &mut ObligationCache, - pools: &mut Pools, - types: &mut Types, - aliases: &mut Aliases, - typ: Index, -) -> Variable { - if let TypeTag::Variable(var) = types[typ] { - var - } else { - let mut arena = take_scratchpad(); - - let var = type_to_variable( - subs, - rank, - pools, - problems, - abilities_store, - obligation_cache, - &arena, - aliases, - types, - typ, - false, - ); - - arena.reset(); - put_scratchpad(arena); - - var - } -} - -enum RegisterVariable { - /// Based on the Type, we already know what variable this will be - Direct(Variable), - /// This Type needs more complicated Content. We reserve a Variable - /// for it, but put a placeholder Content in subs - Deferred, -} - -impl RegisterVariable { - fn from_type( - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - arena: &'_ bumpalo::Bump, - types: &mut Types, - typ: Index, - ) -> Self { - use RegisterVariable::*; - - match types[typ] { - TypeTag::Variable(var) => Direct(var), - TypeTag::EmptyRecord => Direct(Variable::EMPTY_RECORD), - TypeTag::EmptyTagUnion => Direct(Variable::EMPTY_TAG_UNION), - TypeTag::DelayedAlias { shared } - | TypeTag::StructuralAlias { shared, .. } - | TypeTag::OpaqueAlias { shared, .. } - | TypeTag::HostExposedAlias { shared, .. } => { - let AliasShared { symbol, .. } = types[shared]; - if let Some(reserved) = Variable::get_reserved(symbol) { - let direct_var = if rank.is_generalized() { - // reserved variables are stored with rank NONE - reserved - } else { - // for any other rank, we need to copy; it takes care of adjusting the rank - deep_copy_var_in(subs, rank, pools, reserved, arena) - }; - // Safety: the `destination` will become the source-of-truth for the type index, since it - // was not already transformed before (if it was, we'd be in the Variable branch!) - let _old_typ = unsafe { types.emplace_variable(typ, direct_var) }; - return Direct(direct_var); - } - - Deferred - } - _ => Deferred, - } - } - - #[inline(always)] - fn with_stack( - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - arena: &'_ bumpalo::Bump, - types: &mut Types, - typ_index: Index, - stack: &mut bumpalo::collections::Vec<'_, TypeToVar>, - ) -> Variable { - match Self::from_type(subs, rank, pools, arena, types, typ_index) { - Self::Direct(var) => var, - Self::Deferred => { - let var = subs.fresh_unnamed_flex_var(); - // Safety: the `destination` will become the source-of-truth for the type index, since it - // was not already transformed before (if it was, it wouldn't be deferred!) - let typ = unsafe { types.emplace_variable(typ_index, var) }; - stack.push(TypeToVar::Defer { - typ, - typ_index, - destination: var, - ambient_function: AmbientFunctionPolicy::NoFunction, - }); - var - } - } - } -} - -/// Instantiation of ambient functions in unspecialized lambda sets is somewhat tricky due to other -/// optimizations we have in place. This struct tells us how they should be instantiated. -#[derive(Debug)] -enum AmbientFunctionPolicy { - /// We're not in a function. This variant may never hold for unspecialized lambda sets. - NoFunction, - /// We're in a known function. - Function(Variable), -} - -impl AmbientFunctionPolicy { - fn link_to_alias_lambda_set_var(&self, subs: &mut Subs, var: Variable) { - let ambient_function = match self { - AmbientFunctionPolicy::Function(var) => *var, - _ => { - // Might be linked at a deeper point in time, ignore for now - return; - } - }; - let content = subs.get_content_without_compacting(var); - let new_content = match content { - Content::LambdaSet(LambdaSet { - solved, - recursion_var, - unspecialized, - ambient_function: _, - }) => Content::LambdaSet(LambdaSet { - solved: *solved, - recursion_var: *recursion_var, - unspecialized: *unspecialized, - ambient_function, - }), - Content::FlexVar(_) => { - // Something like - // Encoder fmt : List U8, fmt -a-> List U8 | fmt has EncoderFormatting - // THEORY: Replace these with empty lambda sets. They will unify the same as a flex - // var does, but allows us to record the ambient function properly. - Content::LambdaSet(LambdaSet { - solved: UnionLabels::default(), - recursion_var: OptVariable::NONE, - unspecialized: SubsSlice::default(), - ambient_function, - }) - } - content => internal_error!("{:?}({:?}) not a lambda set", content, var), - }; - subs.set_content_unchecked(var, new_content); - } -} - -#[derive(Debug)] -enum TypeToVar { - Defer { - typ: TypeTag, - typ_index: Index, - destination: Variable, - ambient_function: AmbientFunctionPolicy, - }, -} - -#[allow(clippy::too_many_arguments)] -fn type_to_variable( - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - problems: &mut Vec, - abilities_store: &AbilitiesStore, - obligation_cache: &mut ObligationCache, - arena: &bumpalo::Bump, - aliases: &mut Aliases, - types: &mut Types, - typ: Index, - // Helpers for instantiating ambient functions of lambda set variables from type aliases. - is_alias_lambda_set_arg: bool, -) -> Variable { - use bumpalo::collections::Vec; - - let mut stack = Vec::with_capacity_in(8, arena); - let mut bind_to_abilities = Vec::new_in(arena); - - macro_rules! helper { - ($typ:expr, $ambient_function_policy:expr) => {{ - match RegisterVariable::from_type(subs, rank, pools, arena, types, $typ) { - RegisterVariable::Direct(var) => { - // If the variable is just a type variable but we know we're in a lambda set - // context, try to link to the ambient function. - $ambient_function_policy.link_to_alias_lambda_set_var(subs, var); - - var - } - RegisterVariable::Deferred => { - let var = subs.fresh_unnamed_flex_var(); - - // Safety: the `destination` will become the source-of-truth for the type index, since it - // was not already transformed before (if it was, it wouldn't be deferred!) - let typ = unsafe { types.emplace_variable($typ, var) }; - - stack.push(TypeToVar::Defer { - typ, - typ_index: $typ, - destination: var, - ambient_function: $ambient_function_policy, - }); - - var - } - } - }}; - ($typ:expr) => {{ - helper!($typ, AmbientFunctionPolicy::NoFunction) - }}; - } - - let result = helper!(typ); - - while let Some(TypeToVar::Defer { - typ_index, - typ, - destination, - ambient_function, - }) = stack.pop() - { - use TypeTag::*; - match typ { - Variable(_) | EmptyRecord | EmptyTagUnion => { - unreachable!("This variant should never be deferred!",) - } - RangedNumber(range) => { - let content = Content::RangedNumber(range); - - register_with_known_var(subs, destination, rank, pools, content) - } - Apply { - symbol, - type_argument_regions: _, - region: _, - } => { - let arguments = types.get_type_arguments(typ_index); - let new_arguments = VariableSubsSlice::reserve_into_subs(subs, arguments.len()); - for (target_index, var_index) in - (new_arguments.indices()).zip(arguments.into_iter()) - { - let var = helper!(var_index); - subs.variables[target_index] = var; - } - - let flat_type = FlatType::Apply(symbol, new_arguments); - let content = Content::Structure(flat_type); - - register_with_known_var(subs, destination, rank, pools, content) - } - - ClosureTag { - name, - ambient_function, - } => { - let captures = types.get_type_arguments(typ_index); - let union_lambdas = create_union_lambda( - subs, rank, pools, arena, types, name, captures, &mut stack, - ); - - let content = Content::LambdaSet(subs::LambdaSet { - solved: union_lambdas, - // We may figure out the lambda set is recursive during solving, but it never - // is to begin with. - recursion_var: OptVariable::NONE, - unspecialized: SubsSlice::default(), - ambient_function, - }); - - register_with_known_var(subs, destination, rank, pools, content) - } - UnspecializedLambdaSet { unspecialized } => { - let unspecialized_slice = SubsSlice::extend_new( - &mut subs.unspecialized_lambda_sets, - std::iter::once(unspecialized), - ); - - // `ClosureTag` ambient functions are resolved during constraint generation. - // But `UnspecializedLambdaSet`s can only ever live in a type signature, and don't - // correspond to a expression, so they are never constrained. - // Instead, we resolve their ambient functions during type translation, observing - // the invariant that a lambda set can only ever appear under a function type. - let ambient_function = match ambient_function { - AmbientFunctionPolicy::NoFunction => { - debug_assert!(is_alias_lambda_set_arg); - // To be filled in during delayed type alias instantiation - roc_types::subs::Variable::NULL - } - AmbientFunctionPolicy::Function(var) => var, - }; - - let content = Content::LambdaSet(subs::LambdaSet { - unspecialized: unspecialized_slice, - solved: UnionLabels::default(), - recursion_var: OptVariable::NONE, - ambient_function, - }); - - register_with_known_var(subs, destination, rank, pools, content) - } - // This case is important for the rank of boolean variables - Function(closure_type, ret_type) => { - let arguments = types.get_type_arguments(typ_index); - let new_arguments = VariableSubsSlice::reserve_into_subs(subs, arguments.len()); - for (target_index, var_index) in - (new_arguments.indices()).zip(arguments.into_iter()) - { - let var = helper!(var_index); - subs.variables[target_index] = var; - } - - let ret_var = helper!(ret_type); - let closure_var = - helper!(closure_type, AmbientFunctionPolicy::Function(destination)); - let content = - Content::Structure(FlatType::Func(new_arguments, closure_var, ret_var)); - - register_with_known_var(subs, destination, rank, pools, content) - } - Record(fields) => { - let ext_slice = types.get_type_arguments(typ_index); - - // An empty fields is inefficient (but would be correct) - // If hit, try to turn the value into an EmptyRecord in canonicalization - debug_assert!(!fields.is_empty() || !ext_slice.is_empty()); - - let mut field_vars = Vec::with_capacity_in(fields.len(), arena); - - let (fields_names, field_kinds, field_tys) = types.record_fields_slices(fields); - - for ((field, field_kind), field_type) in (fields_names.into_iter()) - .zip(field_kinds.into_iter()) - .zip(field_tys.into_iter()) - { - let field_var = { - let t = helper!(field_type); - types[field_kind].replace(t) - }; - - field_vars.push((types[field].clone(), field_var)); - } - - debug_assert!(ext_slice.len() <= 1); - let temp_ext_var = match ext_slice.into_iter().next() { - None => roc_types::subs::Variable::EMPTY_RECORD, - Some(ext) => helper!(ext), - }; - - let (it, new_ext_var) = - gather_fields_unsorted_iter(subs, RecordFields::empty(), temp_ext_var) - .expect("Something ended up weird in this record type"); - - let it = it - .into_iter() - .map(|(field, field_type)| (field.clone(), field_type)); - - field_vars.extend(it); - insertion_sort_by(&mut field_vars, RecordFields::compare); - - let record_fields = RecordFields::insert_into_subs(subs, field_vars); - - let content = Content::Structure(FlatType::Record(record_fields, new_ext_var)); - - register_with_known_var(subs, destination, rank, pools, content) - } - - Tuple(elems) => { - let ext_slice = types.get_type_arguments(typ_index); - - // Elems should never be empty; we don't support empty tuples - debug_assert!(!elems.is_empty() || !ext_slice.is_empty()); - - let mut elem_vars = Vec::with_capacity_in(elems.len(), arena); - - let (indices, elem_tys) = types.tuple_elems_slices(elems); - - for (index, elem_type) in indices.into_iter().zip(elem_tys.into_iter()) { - let elem_var = helper!(elem_type); - elem_vars.push((types[index], elem_var)); - } - - debug_assert!(ext_slice.len() <= 1); - let temp_ext_var = match ext_slice.into_iter().next() { - None => roc_types::subs::Variable::EMPTY_TUPLE, - Some(ext) => helper!(ext), - }; - - let (it, new_ext_var) = - gather_tuple_elems_unsorted_iter(subs, TupleElems::empty(), temp_ext_var) - .expect("Something ended up weird in this tuple type"); - - elem_vars.extend(it); - let tuple_elems = TupleElems::insert_into_subs(subs, elem_vars); - - let content = Content::Structure(FlatType::Tuple(tuple_elems, new_ext_var)); - - register_with_known_var(subs, destination, rank, pools, content) - } - - TagUnion(tags, ext_openness) => { - let ext_slice = types.get_type_arguments(typ_index); - - // An empty tags is inefficient (but would be correct) - // If hit, try to turn the value into an EmptyTagUnion in canonicalization - debug_assert!(!tags.is_empty() || !ext_slice.is_empty()); - - let (union_tags, ext) = type_to_union_tags( - subs, - rank, - pools, - arena, - types, - tags, - ext_slice, - ext_openness, - &mut stack, - ); - let content = Content::Structure(FlatType::TagUnion(union_tags, ext)); - - register_with_known_var(subs, destination, rank, pools, content) - } - FunctionOrTagUnion(symbol, ext_openness) => { - let ext_slice = types.get_type_arguments(typ_index); - let tag_name = types.get_tag_name(&typ_index).clone(); - - debug_assert!(ext_slice.len() <= 1); - let temp_ext = match ext_slice.into_iter().next() { - Some(ext) => { - let var = helper!(ext); - TagExt::from_can(var, ext_openness) - } - None => TagExt::Any(roc_types::subs::Variable::EMPTY_TAG_UNION), - }; - - let (it, ext) = roc_types::types::gather_tags_unsorted_iter( - subs, - UnionTags::default(), - temp_ext, - ) - .expect("extension var could not be seen as a tag union"); - - for _ in it { - unreachable!("we assert that the ext var is empty; otherwise we'd already know it was a tag union!"); - } - - let tag_names = SubsSlice::extend_new(&mut subs.tag_names, [tag_name]); - let symbols = SubsSlice::extend_new(&mut subs.symbol_names, [symbol]); - - let content = - Content::Structure(FlatType::FunctionOrTagUnion(tag_names, symbols, ext)); - - register_with_known_var(subs, destination, rank, pools, content) - } - RecursiveTagUnion(rec_var, tags, ext_openness) => { - let ext_slice = types.get_type_arguments(typ_index); - - // An empty tags is inefficient (but would be correct) - // If hit, try to turn the value into an EmptyTagUnion in canonicalization - debug_assert!(!tags.is_empty() || !ext_slice.is_empty()); - - let (union_tags, ext) = type_to_union_tags( - subs, - rank, - pools, - arena, - types, - tags, - ext_slice, - ext_openness, - &mut stack, - ); - let content = - Content::Structure(FlatType::RecursiveTagUnion(rec_var, union_tags, ext)); - - let tag_union_var = destination; - register_with_known_var(subs, tag_union_var, rank, pools, content); - - register_with_known_var( - subs, - rec_var, - rank, - pools, - Content::RecursionVar { - opt_name: None, - structure: tag_union_var, - }, - ); - - tag_union_var - } - - DelayedAlias { shared } => { - let AliasShared { - symbol, - type_argument_abilities, - type_argument_regions, - lambda_set_variables, - infer_ext_in_output_variables, - } = types[shared]; - - let type_arguments = types.get_type_arguments(typ_index); - - let alias_variables = { - let all_vars_length = type_arguments.len() - + lambda_set_variables.len() - + infer_ext_in_output_variables.len(); - let new_variables = VariableSubsSlice::reserve_into_subs(subs, all_vars_length); - - let type_arguments_offset = 0; - let lambda_set_vars_offset = type_arguments_offset + type_arguments.len(); - let infer_ext_vars_offset = lambda_set_vars_offset + lambda_set_variables.len(); - - for (((target_index, arg_type), arg_region), abilities) in - (new_variables.indices().skip(type_arguments_offset)) - .zip(type_arguments.into_iter()) - .zip(type_argument_regions.into_iter()) - .zip(type_argument_abilities.into_iter()) - { - let copy_var = helper!(arg_type); - subs.variables[target_index] = copy_var; - if !types[abilities].is_empty() { - let arg_region = types[arg_region]; - bind_to_abilities.push((Loc::at(arg_region, copy_var), abilities)); - } - } - - let it = (new_variables.indices().skip(lambda_set_vars_offset)) - .zip(lambda_set_variables.into_iter()); - for (target_index, ls) in it { - // We MUST do this now, otherwise when linking the ambient function during - // instantiation of the real var, there will be nothing to link against. - let copy_var = type_to_variable( - subs, - rank, - pools, - problems, - abilities_store, - obligation_cache, - arena, - aliases, - types, - ls, - true, - ); - subs.variables[target_index] = copy_var; - } - - let it = (new_variables.indices().skip(infer_ext_vars_offset)) - .zip(infer_ext_in_output_variables.into_iter()); - for (target_index, ext_typ) in it { - let copy_var = helper!(ext_typ); - subs.variables[target_index] = copy_var; - } - - AliasVariables { - variables_start: new_variables.start, - type_variables_len: type_arguments.len() as _, - lambda_set_variables_len: lambda_set_variables.len() as _, - all_variables_len: all_vars_length as _, - } - }; - - let (alias_variable, kind) = aliases.instantiate_real_var( - subs, - rank, - pools, - problems, - abilities_store, - obligation_cache, - arena, - types, - symbol, - alias_variables, - ); - - let content = Content::Alias(symbol, alias_variables, alias_variable, kind); - - register_with_known_var(subs, destination, rank, pools, content) - } - - StructuralAlias { shared, actual } | OpaqueAlias { shared, actual } => { - let kind = match typ { - StructuralAlias { .. } => AliasKind::Structural, - OpaqueAlias { .. } => AliasKind::Opaque, - _ => internal_error!(), - }; - - let AliasShared { - symbol, - type_argument_abilities, - type_argument_regions, - lambda_set_variables, - infer_ext_in_output_variables, - } = types[shared]; - - debug_assert!(roc_types::subs::Variable::get_reserved(symbol).is_none()); - - let type_arguments = types.get_type_arguments(typ_index); - - let alias_variables = { - let all_vars_length = type_arguments.len() - + lambda_set_variables.len() - + infer_ext_in_output_variables.len(); - - let type_arguments_offset = 0; - let lambda_set_vars_offset = type_arguments_offset + type_arguments.len(); - let infer_ext_vars_offset = lambda_set_vars_offset + lambda_set_variables.len(); - - let new_variables = VariableSubsSlice::reserve_into_subs(subs, all_vars_length); - - for (((target_index, typ), region), abilities) in - (new_variables.indices().skip(type_arguments_offset)) - .zip(type_arguments.into_iter()) - .zip(type_argument_regions.into_iter()) - .zip(type_argument_abilities.into_iter()) - { - let copy_var = helper!(typ); - subs.variables[target_index] = copy_var; - if !types[abilities].is_empty() { - let region = types[region]; - bind_to_abilities.push((Loc::at(region, copy_var), abilities)); - } - } - - let it = (new_variables.indices().skip(lambda_set_vars_offset)) - .zip(lambda_set_variables.into_iter()); - for (target_index, ls) in it { - let copy_var = helper!(ls); - subs.variables[target_index] = copy_var; - } - - let it = (new_variables.indices().skip(infer_ext_vars_offset)) - .zip(infer_ext_in_output_variables.into_iter()); - for (target_index, ext_typ) in it { - let copy_var = helper!(ext_typ); - subs.variables[target_index] = copy_var; - } - - AliasVariables { - variables_start: new_variables.start, - type_variables_len: type_arguments.len() as _, - lambda_set_variables_len: lambda_set_variables.len() as _, - all_variables_len: all_vars_length as _, - } - }; - - let alias_variable = if let Symbol::RESULT_RESULT = symbol { - roc_result_to_var(subs, rank, pools, arena, types, actual, &mut stack) - } else { - helper!(actual) - }; - let content = Content::Alias(symbol, alias_variables, alias_variable, kind); - - register_with_known_var(subs, destination, rank, pools, content) - } - HostExposedAlias { - shared, - actual_type: alias_type, - actual_variable: actual_var, - } => { - let AliasShared { - symbol, - type_argument_abilities: _, - type_argument_regions: _, - lambda_set_variables, - infer_ext_in_output_variables: _, // TODO - } = types[shared]; - - let type_arguments = types.get_type_arguments(typ_index); - - let alias_variables = { - let length = type_arguments.len() + lambda_set_variables.len(); - let new_variables = VariableSubsSlice::reserve_into_subs(subs, length); - - for (target_index, arg_type) in - (new_variables.indices()).zip(type_arguments.into_iter()) - { - let copy_var = helper!(arg_type); - subs.variables[target_index] = copy_var; - } - let it = (new_variables.indices().skip(type_arguments.len())) - .zip(lambda_set_variables.into_iter()); - for (target_index, ls) in it { - // We MUST do this now, otherwise when linking the ambient function during - // instantiation of the real var, there will be nothing to link against. - let copy_var = type_to_variable( - subs, - rank, - pools, - problems, - abilities_store, - obligation_cache, - arena, - aliases, - types, - ls, - true, - ); - subs.variables[target_index] = copy_var; - } - - AliasVariables { - variables_start: new_variables.start, - type_variables_len: type_arguments.len() as _, - lambda_set_variables_len: lambda_set_variables.len() as _, - all_variables_len: length as _, - } - }; - - // cannot use helper! here because this variable may be involved in unification below - let alias_variable = type_to_variable( - subs, - rank, - pools, - problems, - abilities_store, - obligation_cache, - arena, - aliases, - types, - alias_type, - false, - ); - // TODO(opaques): I think host-exposed aliases should always be structural - // (when does it make sense to give a host an opaque type?) - let content = Content::Alias( - symbol, - alias_variables, - alias_variable, - AliasKind::Structural, - ); - let result = register_with_known_var(subs, destination, rank, pools, content); - - // We only want to unify the actual_var with the alias once - // if it's already redirected (and therefore, redundant) - // don't do it again - if !subs.redundant(actual_var) { - let descriptor = subs.get(result); - subs.union(result, actual_var, descriptor); - } - - result - } - Error => { - let content = Content::Error; - - register_with_known_var(subs, destination, rank, pools, content) - } - }; - } - - for (Loc { value: var, region }, abilities) in bind_to_abilities { - let abilities = &types[abilities]; - match *subs.get_content_unchecked(var) { - Content::RigidVar(a) => { - // TODO(multi-abilities): check run cache - let abilities_slice = - SubsSlice::extend_new(&mut subs.symbol_names, abilities.sorted_iter().copied()); - subs.set_content(var, Content::RigidAbleVar(a, abilities_slice)); - } - Content::RigidAbleVar(_, abs) - if (subs.get_subs_slice(abs).iter()).eq(abilities.sorted_iter()) => - { - // pass, already bound - } - _ => { - let abilities_slice = - SubsSlice::extend_new(&mut subs.symbol_names, abilities.sorted_iter().copied()); - - let flex_ability = register( - subs, - rank, - pools, - Content::FlexAbleVar(None, abilities_slice), - ); - - let category = Category::OpaqueArg; - match unify( - &mut UEnv::new(subs), - var, - flex_ability, - Mode::EQ, - Polarity::OF_VALUE, - ) { - Success { - vars: _, - must_implement_ability, - lambda_sets_to_specialize, - extra_metadata: _, - } => { - // No introduction needed - - if !must_implement_ability.is_empty() { - let new_problems = obligation_cache.check_obligations( - subs, - abilities_store, - must_implement_ability, - AbilityImplError::BadExpr(region, category, flex_ability), - ); - problems.extend(new_problems); - } - debug_assert!(lambda_sets_to_specialize - .drain() - .all(|(_, vals)| vals.is_empty())); - } - Failure(_vars, actual_type, expected_type, _bad_impls) => { - // No introduction needed - - let problem = TypeError::BadExpr( - region, - category, - actual_type, - Expected::NoExpectation(expected_type), - ); - - problems.push(problem); - } - } - } - } - } - - result -} - -#[inline(always)] -fn roc_result_to_var( - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - arena: &'_ bumpalo::Bump, - types: &mut Types, - result_type: Index, - stack: &mut bumpalo::collections::Vec<'_, TypeToVar>, -) -> Variable { - match types[result_type] { - TypeTag::TagUnion(tags, _ext_openness) => { - let ext_slice = types.get_type_arguments(result_type); - - debug_assert!(ext_slice.is_empty()); - debug_assert!(tags.len() == 2); - - let (tags_slice, payload_slices_slice) = types.union_tag_slices(tags); - - if let ([err, ok], [err_args, ok_args]) = - (&types[tags_slice], &types[payload_slices_slice]) - { - debug_assert_eq!(err, &subs.tag_names[0]); - debug_assert_eq!(ok, &subs.tag_names[1]); - - debug_assert_eq!(err_args.len(), 1); - debug_assert_eq!(ok_args.len(), 1); - - if let (Some(err_type), Some(ok_type)) = - (err_args.into_iter().next(), ok_args.into_iter().next()) - { - let err_var = RegisterVariable::with_stack( - subs, rank, pools, arena, types, err_type, stack, - ); - let ok_var = RegisterVariable::with_stack( - subs, rank, pools, arena, types, ok_type, stack, - ); - - let start = subs.variables.len() as u32; - let err_slice = SubsSlice::new(start, 1); - let ok_slice = SubsSlice::new(start + 1, 1); - - subs.variables.push(err_var); - subs.variables.push(ok_var); - - let variables = SubsSlice::new(subs.variable_slices.len() as _, 2); - subs.variable_slices.push(err_slice); - subs.variable_slices.push(ok_slice); - - let union_tags = UnionTags::from_slices(Subs::RESULT_TAG_NAMES, variables); - let ext = TagExt::Any(Variable::EMPTY_TAG_UNION); - - let content = Content::Structure(FlatType::TagUnion(union_tags, ext)); - - return register(subs, rank, pools, content); - } - } - - unreachable!("invalid arguments to Result.Result; canonicalization should catch this!") - } - _ => unreachable!("not a valid type inside a Result.Result alias"), - } -} - -fn insertion_sort_by(arr: &mut [T], mut compare: F) -where - F: FnMut(&T, &T) -> std::cmp::Ordering, -{ - for i in 1..arr.len() { - let val = &arr[i]; - let mut j = i; - let pos = arr[..i] - .binary_search_by(|x| compare(x, val)) - .unwrap_or_else(|pos| pos); - // Swap all elements until specific position. - while j > pos { - arr.swap(j - 1, j); - j -= 1; - } - } -} - -fn sorted_no_duplicate_tags(tag_slices: &[TagName]) -> bool { - match tag_slices.split_first() { - None => true, - Some((first, rest)) => { - let mut current = first; - - for next in rest { - if current >= next { - return false; - } else { - current = next; - } - } - - true - } - } -} - -fn sort_and_deduplicate(tag_vars: &mut bumpalo::collections::Vec<(TagName, T)>) { - insertion_sort_by(tag_vars, |(a, _), (b, _)| a.cmp(b)); - - // deduplicate, keeping the right-most occurrence of a tag name - let mut i = 0; - - while i < tag_vars.len() { - match (tag_vars.get(i), tag_vars.get(i + 1)) { - (Some((t1, _)), Some((t2, _))) => { - if t1 == t2 { - tag_vars.remove(i); - } else { - i += 1; - } - } - _ => break, - } - } -} - -/// Find whether the current run of tag names is in the subs.tag_names array already. If so, -/// we take a SubsSlice to the existing tag names, so we don't have to add/clone those tag names -/// and keep subs memory consumption low -fn find_tag_name_run(slice: &[TagName], subs: &mut Subs) -> Option> { - use std::cmp::Ordering; - - let tag_name = slice.get(0)?; - - let mut result = None; - - // the `SubsSlice` that inserting `slice` into subs would give - let bigger_slice = SubsSlice::new(subs.tag_names.len() as _, slice.len() as _); - - match subs.tag_name_cache.get_mut(tag_name) { - Some(occupied) => { - let subs_slice = *occupied; - - let prefix_slice = SubsSlice::new(subs_slice.start, slice.len() as _); - - if slice.len() == 1 { - return Some(prefix_slice); - } - - match slice.len().cmp(&subs_slice.len()) { - Ordering::Less => { - // we might have a prefix - let tag_names = &subs.tag_names[subs_slice.start as usize..]; - - for (from_subs, from_slice) in tag_names.iter().zip(slice.iter()) { - if from_subs != from_slice { - return None; - } - } - - result = Some(prefix_slice); - } - Ordering::Equal => { - let tag_names = &subs.tag_names[subs_slice.indices()]; - - for (from_subs, from_slice) in tag_names.iter().zip(slice.iter()) { - if from_subs != from_slice { - return None; - } - } - - result = Some(subs_slice); - } - Ordering::Greater => { - // switch to the bigger slice that is not inserted yet, but will be soon - *occupied = bigger_slice; - } - } - } - None => { - subs.tag_name_cache.push(tag_name, bigger_slice); - } - } - - result -} - -#[inline(always)] -fn register_tag_arguments( - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - arena: &'_ bumpalo::Bump, - types: &mut Types, - stack: &mut bumpalo::collections::Vec<'_, TypeToVar>, - arguments: Slice, -) -> VariableSubsSlice { - if arguments.is_empty() { - VariableSubsSlice::default() - } else { - let new_variables = VariableSubsSlice::reserve_into_subs(subs, arguments.len()); - let it = new_variables.indices().zip(arguments.into_iter()); - - for (target_index, argument) in it { - let var = - RegisterVariable::with_stack(subs, rank, pools, arena, types, argument, stack); - subs.variables[target_index] = var; - } - - new_variables - } -} - -/// Assumes that the tags are sorted and there are no duplicates! -fn insert_tags_fast_path( - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - arena: &'_ bumpalo::Bump, - types: &mut Types, - union_tags: UnionTags, - stack: &mut bumpalo::collections::Vec<'_, TypeToVar>, -) -> UnionTags { - let (tags, payload_slices) = types.union_tag_slices(union_tags); - - debug_assert_eq!(tags.len(), payload_slices.len()); - - if let [arguments_slice] = &types[payload_slices] { - let arguments_slice = *arguments_slice; - - let variable_slice = - register_tag_arguments(subs, rank, pools, arena, types, stack, arguments_slice); - - let new_variable_slices = - SubsSlice::extend_new(&mut subs.variable_slices, [variable_slice]); - - macro_rules! subs_tag_name { - ($tag_name_slice:expr) => { - return UnionTags::from_slices($tag_name_slice, new_variable_slices) - }; - } - - match types[tags][0].0.as_str() { - "Ok" => subs_tag_name!(Subs::TAG_NAME_OK.as_slice()), - "Err" => subs_tag_name!(Subs::TAG_NAME_ERR.as_slice()), - "InvalidNumStr" => subs_tag_name!(Subs::TAG_NAME_INVALID_NUM_STR.as_slice()), - "BadUtf8" => subs_tag_name!(Subs::TAG_NAME_BAD_UTF_8.as_slice()), - "OutOfBounds" => subs_tag_name!(Subs::TAG_NAME_OUT_OF_BOUNDS.as_slice()), - _other => {} - } - } - - let new_variable_slices = SubsSlice::reserve_variable_slices(subs, tags.len()); - match find_tag_name_run(&types[tags], subs) { - Some(new_tag_names) => { - let it = (new_variable_slices.indices()).zip(payload_slices.into_iter()); - - for (variable_slice_index, arguments_index) in it { - let arguments = types[arguments_index]; - subs.variable_slices[variable_slice_index] = - register_tag_arguments(subs, rank, pools, arena, types, stack, arguments); - } - - UnionTags::from_slices(new_tag_names, new_variable_slices) - } - None => { - let new_tag_names = SubsSlice::reserve_tag_names(subs, tags.len()); - - let it = (new_variable_slices.indices()) - .zip(new_tag_names.indices()) - .zip(tags.into_iter()) - .zip(payload_slices.into_iter()); - - for (((variable_slice_index, tag_name_index), tag_name), arguments_index) in it { - let arguments = types[arguments_index]; - subs.variable_slices[variable_slice_index] = - register_tag_arguments(subs, rank, pools, arena, types, stack, arguments); - - subs.tag_names[tag_name_index] = types[tag_name].clone(); - } - - UnionTags::from_slices(new_tag_names, new_variable_slices) - } - } -} - -fn insert_tags_slow_path( - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - arena: &'_ bumpalo::Bump, - types: &mut Types, - union_tags: UnionTags, - mut tag_vars: bumpalo::collections::Vec<(TagName, VariableSubsSlice)>, - stack: &mut bumpalo::collections::Vec<'_, TypeToVar>, -) -> UnionTags { - let (tags, payload_slices) = types.union_tag_slices(union_tags); - - for (tag_index, tag_argument_types_index) in (tags.into_iter()).zip(payload_slices.into_iter()) - { - let tag_argument_types = &types[tag_argument_types_index]; - - let new_slice = VariableSubsSlice::reserve_into_subs(subs, tag_argument_types.len()); - - for (i, arg) in (new_slice.indices()).zip(tag_argument_types.into_iter()) { - let var = RegisterVariable::with_stack(subs, rank, pools, arena, types, arg, stack); - subs.variables[i] = var; - } - - tag_vars.push((types[tag_index].clone(), new_slice)); - } - - sort_and_deduplicate(&mut tag_vars); - - UnionTags::insert_slices_into_subs(subs, tag_vars) -} - -fn type_to_union_tags( - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - arena: &'_ bumpalo::Bump, - types: &mut Types, - union_tags: UnionTags, - opt_ext_slice: Slice, - ext_openness: ExtImplicitOpenness, - stack: &mut bumpalo::collections::Vec<'_, TypeToVar>, -) -> (UnionTags, TagExt) { - use bumpalo::collections::Vec; - - let (tags, _) = types.union_tag_slices(union_tags); - - let sorted = tags.len() == 1 || sorted_no_duplicate_tags(&types[tags]); - - debug_assert!(opt_ext_slice.len() <= 1); - - match opt_ext_slice.into_iter().next() { - None => { - let ext = Variable::EMPTY_TAG_UNION; - - let union_tags = if sorted { - insert_tags_fast_path(subs, rank, pools, arena, types, union_tags, stack) - } else { - let tag_vars = Vec::with_capacity_in(tags.len(), arena); - insert_tags_slow_path(subs, rank, pools, arena, types, union_tags, tag_vars, stack) - }; - - (union_tags, TagExt::Any(ext)) - } - Some(ext) => { - let mut tag_vars = Vec::with_capacity_in(tags.len(), arena); - - let temp_ext = { - let temp_ext_var = - RegisterVariable::with_stack(subs, rank, pools, arena, types, ext, stack); - TagExt::from_can(temp_ext_var, ext_openness) - }; - let (it, ext) = - roc_types::types::gather_tags_unsorted_iter(subs, UnionTags::default(), temp_ext) - .expect("extension var could not be seen as tag union"); - - tag_vars.extend(it.map(|(n, v)| (n.clone(), v))); - - let union_tags = if tag_vars.is_empty() && sorted { - insert_tags_fast_path(subs, rank, pools, arena, types, union_tags, stack) - } else { - insert_tags_slow_path(subs, rank, pools, arena, types, union_tags, tag_vars, stack) - }; - - (union_tags, ext) - } - } -} - -fn create_union_lambda( - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - arena: &'_ bumpalo::Bump, - types: &mut Types, - closure: Symbol, - capture_types: Slice, - stack: &mut bumpalo::collections::Vec<'_, TypeToVar>, -) -> UnionLambdas { - let variable_slice = - register_tag_arguments(subs, rank, pools, arena, types, stack, capture_types); - let new_variable_slices = SubsSlice::extend_new(&mut subs.variable_slices, [variable_slice]); - - let lambda_name_slice = SubsSlice::extend_new(&mut subs.symbol_names, [closure]); - - UnionLambdas::from_slices(lambda_name_slice, new_variable_slices) -} - -fn check_for_infinite_type( - subs: &mut Subs, - pools: &mut Pools, - problems: &mut Vec, - symbol: Symbol, - loc_var: Loc, -) { - let var = loc_var.value; - - 'next_occurs_check: while let Err((_, chain)) = subs.occurs(var) { - // walk the chain till we find a tag union or lambda set, starting from the variable that - // occurred recursively, which is always at the end of the chain. - for &var in chain.iter().rev() { - match *subs.get_content_without_compacting(var) { - Content::Structure(FlatType::TagUnion(tags, ext_var)) => { - let rec_var = subs.mark_tag_union_recursive(var, tags, ext_var); - register_to_pools(subs, rec_var, pools); - - continue 'next_occurs_check; - } - Content::LambdaSet(subs::LambdaSet { - solved, - recursion_var: OptVariable::NONE, - unspecialized, - ambient_function: ambient_function_var, - }) => { - let rec_var = subs.mark_lambda_set_recursive( - var, - solved, - unspecialized, - ambient_function_var, - ); - register_to_pools(subs, rec_var, pools); - - continue 'next_occurs_check; - } - _ => { /* fall through */ } - } - } - - circular_error(subs, problems, symbol, &loc_var); + + circular_error(env.subs, problems, symbol, &loc_var); } } @@ -3655,13 +1874,10 @@ fn circular_error( /// Ensures that variables introduced at the `young_rank`, but that should be /// stuck at a lower level, are marked at that level and not generalized at the /// present `young_rank`. See [adjust_rank]. -fn generalize( - subs: &mut Subs, - young_mark: Mark, - visit_mark: Mark, - young_rank: Rank, - pools: &mut Pools, -) { +fn generalize(env: &mut Env, young_mark: Mark, visit_mark: Mark, young_rank: Rank) { + let subs = &mut env.subs; + let pools = &mut env.pools; + let young_vars = std::mem::take(pools.get_mut(young_rank)); let rank_table = pool_to_rank_table(subs, young_mark, young_rank, young_vars); @@ -4089,419 +2305,3 @@ fn adjust_rank_content( RangedNumber(_) => group_rank, } } - -/// Introduce some variables to Pools at the given rank. -/// Also, set each of their ranks in Subs to be the given rank. -pub(crate) fn introduce(subs: &mut Subs, rank: Rank, pools: &mut Pools, vars: &[Variable]) { - let pool: &mut Vec = pools.get_mut(rank); - - for &var in vars.iter() { - subs.set_rank(var, rank); - } - - pool.extend(vars); -} - -pub(crate) fn deep_copy_var_in( - subs: &mut Subs, - rank: Rank, - pools: &mut Pools, - var: Variable, - arena: &Bump, -) -> Variable { - let mut visited = bumpalo::collections::Vec::with_capacity_in(256, arena); - - let pool = pools.get_mut(rank); - - let var = subs.get_root_key(var); - match deep_copy_var_decision(subs, rank, var) { - ControlFlow::Break(copy) => copy, - ControlFlow::Continue(copy) => { - deep_copy_var_help(subs, rank, pool, &mut visited, var, copy); - - // we have tracked all visited variables, and can now traverse them - // in one go (without looking at the UnificationTable) and clear the copy field - for var in visited { - subs.set_copy_unchecked(var, OptVariable::NONE); - } - - copy - } - } -} - -#[inline] -fn has_trivial_copy(subs: &Subs, root_var: Variable) -> Option { - let existing_copy = subs.get_copy_unchecked(root_var); - - if let Some(copy) = existing_copy.into_variable() { - Some(copy) - } else if subs.get_rank_unchecked(root_var) != Rank::GENERALIZED { - Some(root_var) - } else { - None - } -} - -#[inline] -fn deep_copy_var_decision( - subs: &mut Subs, - max_rank: Rank, - var: Variable, -) -> ControlFlow { - let var = subs.get_root_key(var); - if let Some(copy) = has_trivial_copy(subs, var) { - ControlFlow::Break(copy) - } else { - let copy_descriptor = Descriptor { - content: Content::Structure(FlatType::EmptyTagUnion), - rank: max_rank, - mark: Mark::NONE, - copy: OptVariable::NONE, - }; - - let copy = subs.fresh(copy_descriptor); - - // Link the original variable to the new variable. This lets us - // avoid making multiple copies of the variable we are instantiating. - // - // Need to do this before recursively copying to avoid looping. - subs.set_mark_unchecked(var, Mark::NONE); - subs.set_copy_unchecked(var, copy.into()); - - ControlFlow::Continue(copy) - } -} - -fn deep_copy_var_help( - subs: &mut Subs, - max_rank: Rank, - pool: &mut Vec, - visited: &mut bumpalo::collections::Vec<'_, Variable>, - initial_source: Variable, - initial_copy: Variable, -) -> Variable { - use roc_types::subs::Content::*; - use roc_types::subs::FlatType::*; - - struct DeepCopyVarWork { - source: Variable, - copy: Variable, - } - - let initial = DeepCopyVarWork { - source: initial_source, - copy: initial_copy, - }; - let mut stack = vec![initial]; - - macro_rules! work { - ($variable:expr) => {{ - let var = subs.get_root_key($variable); - match deep_copy_var_decision(subs, max_rank, var) { - ControlFlow::Break(copy) => copy, - ControlFlow::Continue(copy) => { - stack.push(DeepCopyVarWork { source: var, copy }); - - copy - } - } - }}; - } - - macro_rules! copy_sequence { - ($length:expr, $variables:expr) => {{ - let new_variables = SubsSlice::reserve_into_subs(subs, $length as _); - for (target_index, var_index) in (new_variables.indices()).zip($variables) { - let var = subs[var_index]; - let copy_var = work!(var); - subs.variables[target_index] = copy_var; - } - - new_variables - }}; - } - - macro_rules! copy_union { - ($tags:expr) => {{ - let new_variable_slices = SubsSlice::reserve_variable_slices(subs, $tags.len()); - - let it = (new_variable_slices.indices()).zip($tags.variables()); - for (target_index, index) in it { - let slice = subs[index]; - - let new_variables = copy_sequence!(slice.len(), slice); - subs.variable_slices[target_index] = new_variables; - } - - UnionLabels::from_slices($tags.labels(), new_variable_slices) - }}; - } - - // When generalizing annotations with `Openness` extensions - // we want to promote them to `Any`, so that usages at - // specialized sites can grow unboundedly and are not bound to - // openness-polymorphism. - macro_rules! copy_tag_ext { - ($ext:expr) => { - TagExt::Any(work!($ext.var())) - }; - } - - while let Some(DeepCopyVarWork { source: var, copy }) = stack.pop() { - visited.push(var); - pool.push(copy); - - let content = *subs.get_content_unchecked(var); - - // Now we recursively copy the content of the variable. - // We have already marked the variable as copied, so we - // will not repeat this work or crawl this variable again. - match content { - Structure(flat_type) => { - let new_flat_type = match flat_type { - Apply(symbol, arguments) => { - let new_arguments = copy_sequence!(arguments.len(), arguments); - - Apply(symbol, new_arguments) - } - - Func(arguments, closure_var, ret_var) => { - let new_ret_var = work!(ret_var); - let new_closure_var = work!(closure_var); - - let new_arguments = copy_sequence!(arguments.len(), arguments); - - Func(new_arguments, new_closure_var, new_ret_var) - } - - same @ EmptyRecord | same @ EmptyTuple | same @ EmptyTagUnion => same, - - Record(fields, ext_var) => { - let record_fields = { - let new_variables = - copy_sequence!(fields.len(), fields.iter_variables()); - - // When copying a let-generalized record to a specialized region, rigid - // optionals just become optionals. - let field_types = subs.get_subs_slice(fields.record_fields()); - let has_rigid_optional_field = field_types - .iter() - .any(|f| matches!(f, RecordField::RigidOptional(..))); - - let new_field_types_start = if has_rigid_optional_field { - let field_types = field_types.to_vec(); - let slice = SubsSlice::extend_new( - &mut subs.record_fields, - field_types.into_iter().map(|f| match f { - RecordField::RigidOptional(()) - | RecordField::RigidRequired(()) => internal_error!("Rigid optional/required should be generalized to non-rigid by this point"), - - RecordField::Demanded(_) - | RecordField::Required(_) - | RecordField::Optional(_) => f, - }), - ); - slice.start - } else { - fields.field_types_start - }; - - RecordFields { - length: fields.length, - field_names_start: fields.field_names_start, - variables_start: new_variables.start, - field_types_start: new_field_types_start, - } - }; - - Record(record_fields, work!(ext_var)) - } - - Tuple(elems, ext_var) => { - let tuple_elems = { - let new_variables = copy_sequence!(elems.len(), elems.iter_variables()); - - TupleElems { - length: elems.length, - variables_start: new_variables.start, - elem_index_start: elems.elem_index_start, - } - }; - - Tuple(tuple_elems, work!(ext_var)) - } - - TagUnion(tags, ext_var) => { - let union_tags = copy_union!(tags); - - TagUnion(union_tags, copy_tag_ext!(ext_var)) - } - - FunctionOrTagUnion(tag_name, symbol, ext_var) => { - FunctionOrTagUnion(tag_name, symbol, copy_tag_ext!(ext_var)) - } - - RecursiveTagUnion(rec_var, tags, ext_var) => { - let union_tags = copy_union!(tags); - - RecursiveTagUnion(work!(rec_var), union_tags, copy_tag_ext!(ext_var)) - } - }; - - subs.set_content_unchecked(copy, Structure(new_flat_type)); - } - - FlexVar(_) | FlexAbleVar(_, _) | Error => { - subs.set_content_unchecked(copy, content); - } - - RecursionVar { - opt_name, - structure, - } => { - let content = RecursionVar { - opt_name, - structure: work!(structure), - }; - - subs.set_content_unchecked(copy, content); - } - - RigidVar(name) => { - subs.set_content_unchecked(copy, FlexVar(Some(name))); - } - - RigidAbleVar(name, ability) => { - subs.set_content_unchecked(copy, FlexAbleVar(Some(name), ability)); - } - - Alias(symbol, arguments, real_type_var, kind) => { - let new_variables = - copy_sequence!(arguments.all_variables_len, arguments.all_variables()); - - let new_arguments = AliasVariables { - variables_start: new_variables.start, - ..arguments - }; - - let new_real_type_var = work!(real_type_var); - let new_content = Alias(symbol, new_arguments, new_real_type_var, kind); - - subs.set_content_unchecked(copy, new_content); - } - - LambdaSet(subs::LambdaSet { - solved, - recursion_var, - unspecialized, - ambient_function: ambient_function_var, - }) => { - let lambda_set_var = copy; - - let new_solved = copy_union!(solved); - let new_rec_var = recursion_var.map(|v| work!(v)); - let new_unspecialized = SubsSlice::reserve_uls_slice(subs, unspecialized.len()); - - for (new_uls_index, uls_index) in - (new_unspecialized.into_iter()).zip(unspecialized.into_iter()) - { - let Uls(var, sym, region) = subs[uls_index]; - let new_var = work!(var); - - deep_copy_uls_precondition(subs, var, new_var); - - subs[new_uls_index] = Uls(new_var, sym, region); - - subs.uls_of_var.add(new_var, lambda_set_var); - } - - let new_ambient_function_var = work!(ambient_function_var); - debug_assert_ne!( - ambient_function_var, new_ambient_function_var, - "lambda set cloned but its ambient function wasn't?" - ); - - subs.set_content_unchecked( - lambda_set_var, - LambdaSet(subs::LambdaSet { - solved: new_solved, - recursion_var: new_rec_var, - unspecialized: new_unspecialized, - ambient_function: new_ambient_function_var, - }), - ); - } - - RangedNumber(range) => { - let new_content = RangedNumber(range); - - subs.set_content_unchecked(copy, new_content); - } - } - } - - initial_copy -} - -#[inline(always)] -fn deep_copy_uls_precondition(subs: &Subs, original_var: Variable, new_var: Variable) { - if cfg!(debug_assertions) { - let content = subs.get_content_without_compacting(original_var); - - debug_assert!( - matches!( - content, - Content::FlexAbleVar(..) | Content::RigidAbleVar(..) - ), - "var in unspecialized lamba set is not bound to an ability, it is {:?}", - roc_types::subs::SubsFmtContent(content, subs) - ); - debug_assert!( - original_var != new_var, - "unspecialized lamba set var was not instantiated" - ); - } -} - -#[inline(always)] -fn register(subs: &mut Subs, rank: Rank, pools: &mut Pools, content: Content) -> Variable { - let descriptor = Descriptor { - content, - rank, - mark: Mark::NONE, - copy: OptVariable::NONE, - }; - - let var = subs.fresh(descriptor); - - pools.get_mut(rank).push(var); - - var -} - -fn register_with_known_var( - subs: &mut Subs, - var: Variable, - rank: Rank, - pools: &mut Pools, - content: Content, -) -> Variable { - let descriptor = Descriptor { - content, - rank, - mark: Mark::NONE, - copy: OptVariable::NONE, - }; - - subs.set(var, descriptor); - - pools.get_mut(rank).push(var); - - var -} - -#[inline(always)] -fn register_to_pools(subs: &Subs, var: Variable, pools: &mut Pools) { - pools.get_mut(subs.get_rank(var)).push(var); -} diff --git a/crates/compiler/solve/src/solve/scope.rs b/crates/compiler/solve/src/solve/scope.rs new file mode 100644 index 00000000000..5acd041c968 --- /dev/null +++ b/crates/compiler/solve/src/solve/scope.rs @@ -0,0 +1,40 @@ +use roc_module::symbol::Symbol; +use roc_types::subs::Variable; + +/// The scope of the solver, as symbols are introduced. +#[derive(Clone, Debug, Default)] +pub struct Scope { + symbols: Vec, + variables: Vec, +} + +impl Scope { + pub fn vars_by_symbol(&self) -> impl Iterator + '_ { + let it1 = self.symbols.iter().copied(); + let it2 = self.variables.iter().copied(); + + it1.zip(it2) + } + + #[inline(always)] + pub fn get_var_by_symbol(&self, symbol: &Symbol) -> Option { + self.symbols + .iter() + .position(|s| s == symbol) + .map(|index| self.variables[index]) + } + + #[inline(always)] + pub fn insert_symbol_var_if_vacant(&mut self, symbol: Symbol, var: Variable) { + match self.symbols.iter().position(|s| *s == symbol) { + None => { + // symbol is not in vars_by_symbol yet; insert it + self.symbols.push(symbol); + self.variables.push(var); + } + Some(_) => { + // do nothing + } + } + } +} diff --git a/crates/compiler/solve/src/specialize.rs b/crates/compiler/solve/src/specialize.rs index d6344f70fc2..36c17ad2018 100644 --- a/crates/compiler/solve/src/specialize.rs +++ b/crates/compiler/solve/src/specialize.rs @@ -2,16 +2,11 @@ use std::collections::VecDeque; -use bumpalo::Bump; -use roc_can::{ - abilities::{AbilitiesStore, ImplKey}, - module::ExposedByModule, -}; +use roc_can::abilities::{AbilitiesStore, ImplKey}; use roc_collections::{VecMap, VecSet}; use roc_debug_flags::dbg_do; #[cfg(debug_assertions)] use roc_debug_flags::ROC_TRACE_COMPACTION; -use roc_derive::SharedDerivedModule; use roc_derive_key::{DeriveError, DeriveKey}; use roc_error_macros::{internal_error, todo_abilities}; use roc_module::symbol::{ModuleId, Symbol}; @@ -22,11 +17,12 @@ use roc_types::{ }, types::{AliasKind, MemberImpl, Polarity, Uls}, }; -use roc_unify::unify::{unify, Env as UEnv, Mode, MustImplementConstraints}; +use roc_unify::unify::{unify, Mode, MustImplementConstraints}; use crate::{ ability::builtin_module_with_unlisted_ability_impl, - solve::{deep_copy_var_in, introduce, Pools}, + deep_copy::deep_copy_var_in, + env::{DerivedEnv, Env}, }; /// What phase in the compiler is reaching out to specialize lambda sets? @@ -121,12 +117,6 @@ impl Phase for SolvePhase<'_> { } } -pub struct DerivedEnv<'a> { - pub derived_module: &'a SharedDerivedModule, - /// Exposed types needed by the derived module. - pub exposed_types: &'a ExposedByModule, -} - #[derive(Default)] pub struct AwaitingSpecializations { // What variables' specialized lambda sets in `uls_of_var` will be unlocked for specialization @@ -197,9 +187,9 @@ fn trace_compaction_step_1(subs: &Subs, c_a: Variable, uls_a: &[Variable]) { .collect::>() .join(","); eprintln!("===lambda set compaction==="); - eprintln!(" concrete type: {:?}", c_a); + eprintln!(" concrete type: {c_a:?}"); eprintln!(" step 1:"); - eprintln!(" uls_a = {{ {} }}", uls_a); + eprintln!(" uls_a = {{ {uls_a} }}"); } #[cfg(debug_assertions)] @@ -215,7 +205,7 @@ fn trace_compaction_step_2(subs: &Subs, uls_a: &[Variable]) { .collect::>() .join(","); eprintln!(" step 2:"); - eprintln!(" uls_a' = {{ {} }}", uls_a); + eprintln!(" uls_a' = {{ {uls_a} }}"); } #[cfg(debug_assertions)] @@ -236,9 +226,9 @@ fn trace_compaction_step_3iter_start( ); let t_f1 = roc_types::subs::SubsFmtContent(subs.get_content_without_compacting(t_f1), subs); let t_f2 = roc_types::subs::SubsFmtContent(subs.get_content_without_compacting(t_f2), subs); - eprintln!(" - iteration: {:?}", iteration_lambda_set); - eprintln!(" {:?}", t_f1); - eprintln!(" ~ {:?}", t_f2); + eprintln!(" - iteration: {iteration_lambda_set:?}"); + eprintln!(" {t_f1:?}"); + eprintln!(" ~ {t_f2:?}"); } #[cfg(debug_assertions)] @@ -249,7 +239,7 @@ fn trace_compaction_step_3iter_end(subs: &Subs, t_f_result: Variable, skipped: b if skipped { eprintln!(" SKIP"); } - eprintln!(" = {:?}\n", t_f_result); + eprintln!(" = {t_f_result:?}\n"); } macro_rules! trace_compact { @@ -305,10 +295,7 @@ 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_env: &DerivedEnv, - arena: &Bump, - pools: &mut Pools, + env: &mut Env, uls_of_var: UlsOfVar, phase: &P, ) -> CompactionResult { @@ -320,7 +307,7 @@ pub fn compact_lambda_sets_of_vars( // Suppose a type variable `a` with `uls_of_var` mapping `uls_a = {l1, ... ln}` has been instantiated to a concrete type `C_a`. while let Some((c_a, uls_a)) = uls_of_var_queue.pop_front() { - let c_a = subs.get_root_key_without_compacting(c_a); + let c_a = env.subs.get_root_key_without_compacting(c_a); // 1. Let each `l` in `uls_a` be of form `[solved_lambdas + ... + C:f:r + ...]`. // NB: There may be multiple unspecialized lambdas of form `C:f:r, C:f1:r1, ..., C:fn:rn` in `l`. // In this case, let `t1, ... tm` be the other unspecialized lambdas not of form `C:_:_`, @@ -332,13 +319,13 @@ pub fn compact_lambda_sets_of_vars( let mut uls = uls_a.into_vec(); // De-duplicate lambdas by root key. - uls.iter_mut().for_each(|v| *v = subs.get_root_key(*v)); + uls.iter_mut().for_each(|v| *v = env.subs.get_root_key(*v)); uls.sort(); uls.dedup(); uls }; - trace_compact!(1. subs, c_a, &uls_a); + trace_compact!(1. env.subs, c_a, &uls_a); // The flattening step - remove lambda sets that don't reference the concrete var, and for // flatten lambda sets that reference it more than once. @@ -350,15 +337,15 @@ pub fn compact_lambda_sets_of_vars( recursion_var, unspecialized, ambient_function, - } = subs.get_lambda_set(lambda_set); - let lambda_set_rank = subs.get_rank(lambda_set); - let unspecialized = subs.get_subs_slice(unspecialized); + } = env.subs.get_lambda_set(lambda_set); + let lambda_set_rank = env.subs.get_rank(lambda_set); + let unspecialized = env.subs.get_subs_slice(unspecialized); // TODO: is it faster to traverse once, see if we only have one concrete lambda, and // bail in that happy-path, rather than always splitting? let (concrete, mut not_concrete): (Vec<_>, Vec<_>) = unspecialized .iter() .copied() - .partition(|Uls(var, _, _)| subs.equivalent_without_compacting(*var, c_a)); + .partition(|Uls(var, _, _)| env.subs.equivalent_without_compacting(*var, c_a)); if concrete.len() == 1 { // No flattening needs to be done, just return the lambda set as-is return vec![lambda_set]; @@ -373,7 +360,7 @@ pub fn compact_lambda_sets_of_vars( // lambdas, plus all other unspecialized lambdas. // l' = [solved_lambdas + t1 + ... + tm + C:f:r] let unspecialized = SubsSlice::extend_new( - &mut subs.unspecialized_lambda_sets, + &mut env.subs.unspecialized_lambda_sets, not_concrete .drain(..) .chain(std::iter::once(concrete_lambda)), @@ -384,10 +371,10 @@ pub fn compact_lambda_sets_of_vars( // lambdas. // ln = [[] + C:fn:rn] let unspecialized = SubsSlice::extend_new( - &mut subs.unspecialized_lambda_sets, + &mut env.subs.unspecialized_lambda_sets, [concrete_lambda], ); - let var = subs.fresh(Descriptor { + let var = env.subs.fresh(Descriptor { content: Content::Error, rank: lambda_set_rank, mark: Mark::NONE, @@ -396,7 +383,7 @@ pub fn compact_lambda_sets_of_vars( (var, unspecialized) }; - subs.set_content( + env.subs.set_content( var, Content::LambdaSet(LambdaSet { solved, @@ -414,11 +401,15 @@ pub fn compact_lambda_sets_of_vars( // 2. Now, each `l` in `uls_a` has a unique unspecialized lambda of form `C:f:r`. // Sort `uls_a` primarily by `f` (arbitrary order), and secondarily by `r` in descending order. uls_a.sort_by(|v1, v2| { - let unspec_1 = subs.get_subs_slice(subs.get_lambda_set(*v1).unspecialized); - let unspec_2 = subs.get_subs_slice(subs.get_lambda_set(*v2).unspecialized); + let unspec_1 = env + .subs + .get_subs_slice(env.subs.get_lambda_set(*v1).unspecialized); + let unspec_2 = env + .subs + .get_subs_slice(env.subs.get_lambda_set(*v2).unspecialized); - let Uls(_, f1, r1) = unique_unspecialized_lambda(subs, c_a, unspec_1).unwrap(); - let Uls(_, f2, r2) = unique_unspecialized_lambda(subs, c_a, unspec_2).unwrap(); + let Uls(_, f1, r1) = unique_unspecialized_lambda(env.subs, c_a, unspec_1).unwrap(); + let Uls(_, f2, r2) = unique_unspecialized_lambda(env.subs, c_a, unspec_2).unwrap(); match f1.cmp(&f2) { std::cmp::Ordering::Equal => { @@ -429,7 +420,7 @@ pub fn compact_lambda_sets_of_vars( } }); - trace_compact!(2. subs, &uls_a); + trace_compact!(2. env.subs, &uls_a); // 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. @@ -439,8 +430,7 @@ pub fn compact_lambda_sets_of_vars( // 3. Unify `t_f1 ~ t_f2`. trace_compact!(3start.); for l in uls_a { - let compaction_result = - compact_lambda_set(subs, derived_env, arena, pools, c_a, l, phase); + let compaction_result = compact_lambda_set(env, c_a, l, phase); match compaction_result { OneCompactionResult::Compacted { @@ -474,10 +464,7 @@ enum OneCompactionResult { #[must_use] #[allow(clippy::too_many_arguments)] fn compact_lambda_set( - subs: &mut Subs, - derived_env: &DerivedEnv, - arena: &Bump, - pools: &mut Pools, + env: &mut Env, resolved_concrete: Variable, this_lambda_set: Variable, phase: &P, @@ -493,23 +480,24 @@ fn compact_lambda_set( recursion_var, unspecialized, ambient_function: t_f1, - } = subs.get_lambda_set(this_lambda_set); - let target_rank = subs.get_rank(this_lambda_set); + } = env.subs.get_lambda_set(this_lambda_set); + let target_rank = env.subs.get_rank(this_lambda_set); debug_assert!(!unspecialized.is_empty()); - let unspecialized = subs.get_subs_slice(unspecialized); + let unspecialized = env.subs.get_subs_slice(unspecialized); // 1. Let `t_f1` be the directly ambient function of the lambda set containing `C:f:r`. - let Uls(c, f, r) = unique_unspecialized_lambda(subs, resolved_concrete, unspecialized).unwrap(); + let Uls(c, f, r) = + unique_unspecialized_lambda(env.subs, resolved_concrete, unspecialized).unwrap(); - debug_assert!(subs.equivalent_without_compacting(c, resolved_concrete)); + debug_assert!(env.subs.equivalent_without_compacting(c, resolved_concrete)); // Now decide: do we // - proceed with specialization // - simply drop the specialization lambda set (due to an error) // - or do we need to wait, because we don't know enough information for the specialization yet? - let specialization_decision = make_specialization_decision(subs, phase, c, f); + let specialization_decision = make_specialization_decision(env.subs, phase, c, f); let specialization_key_or_drop = match specialization_decision { SpecializeDecision::Specialize(key) => Ok(key), SpecializeDecision::Drop => Err(()), @@ -522,7 +510,10 @@ fn compact_lambda_set( // 1b. Remove `C:f:r` from `t_f1`'s lambda set. let new_unspecialized: Vec<_> = unspecialized .iter() - .filter(|Uls(v, _, _)| !subs.equivalent_without_compacting(*v, resolved_concrete)) + .filter(|Uls(v, _, _)| { + !env.subs + .equivalent_without_compacting(*v, resolved_concrete) + }) .copied() .collect(); debug_assert_eq!(new_unspecialized.len(), unspecialized.len() - 1); @@ -530,12 +521,12 @@ fn compact_lambda_set( solved, recursion_var, unspecialized: SubsSlice::extend_new( - &mut subs.unspecialized_lambda_sets, + &mut env.subs.unspecialized_lambda_sets, new_unspecialized, ), ambient_function: t_f1, }; - subs.set_content( + env.subs.set_content( this_lambda_set, Content::LambdaSet(t_f1_lambda_set_without_concrete), ); @@ -545,7 +536,7 @@ fn compact_lambda_set( 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); + trace_compact!(3iter_end_skipped.env.subs, t_f1); return OneCompactionResult::Compacted { new_obligations: Default::default(), new_lambda_sets_to_specialize: Default::default(), @@ -554,8 +545,8 @@ fn compact_lambda_set( }; let specialization_ambient_function_var = get_specialization_lambda_set_ambient_function( - subs, - derived_env, + env.subs, + env.derived_env, phase, f, r, @@ -568,7 +559,7 @@ fn compact_lambda_set( 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); + trace_compact!(3iter_end_skipped.env.subs, t_f1); return OneCompactionResult::Compacted { new_obligations: Default::default(), new_lambda_sets_to_specialize: Default::default(), @@ -578,21 +569,21 @@ fn compact_lambda_set( // 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 t_f2 = deep_copy_var_in(env, target_rank, t_f2, env.arena); // 3. Unify `t_f1 ~ t_f2`. - trace_compact!(3iter_start.subs, this_lambda_set, t_f1, t_f2); + trace_compact!(3iter_start.env.subs, this_lambda_set, t_f1, t_f2); let (vars, new_obligations, new_lambda_sets_to_specialize, _meta) = unify( - &mut UEnv::new(subs), + &mut env.uenv(), t_f1, t_f2, Mode::LAMBDA_SET_SPECIALIZATION, Polarity::Pos, ) .expect_success("ambient functions don't unify"); - trace_compact!(3iter_end.subs, t_f1); + trace_compact!(3iter_end.env.subs, t_f1); - introduce(subs, target_rank, pools, &vars); + env.introduce(target_rank, &vars); OneCompactionResult::Compacted { new_obligations, diff --git a/crates/compiler/solve/src/to_var.rs b/crates/compiler/solve/src/to_var.rs new file mode 100644 index 00000000000..b49cddd1c1d --- /dev/null +++ b/crates/compiler/solve/src/to_var.rs @@ -0,0 +1,1292 @@ +use std::cell::RefCell; + +use roc_can::{abilities::AbilitiesStore, constraint::TypeOrVar, expected::Expected}; +use roc_collections::soa::{Index, Slice}; +use roc_error_macros::internal_error; +use roc_module::{ident::TagName, symbol::Symbol}; +use roc_region::all::Loc; +use roc_solve_problem::TypeError; +use roc_types::{ + subs::{ + self, AliasVariables, Content, FlatType, GetSubsSlice, LambdaSet, OptVariable, Rank, + RecordFields, Subs, SubsSlice, TagExt, TupleElems, UnionLabels, UnionLambdas, UnionTags, + Variable, VariableSubsSlice, + }, + types::{ + gather_fields_unsorted_iter, gather_tuple_elems_unsorted_iter, AliasKind, AliasShared, + Category, ExtImplicitOpenness, Polarity, TypeTag, Types, + }, +}; +use roc_unify::unify::{unify, Mode, Unified}; + +use crate::{ + ability::{AbilityImplError, ObligationCache}, + deep_copy::deep_copy_var_in, + env::Env, + Aliases, +}; + +std::thread_local! { + /// Scratchpad arena so we don't need to allocate a new one all the time + static SCRATCHPAD: RefCell> = RefCell::new(Some(bumpalo::Bump::with_capacity(4 * 1024))); +} + +fn take_scratchpad() -> bumpalo::Bump { + SCRATCHPAD.with(|f| f.take().unwrap()) +} + +fn put_scratchpad(scratchpad: bumpalo::Bump) { + SCRATCHPAD.with(|f| { + f.replace(Some(scratchpad)); + }); +} + +pub(crate) fn either_type_index_to_var( + env: &mut Env, + rank: Rank, + problems: &mut Vec, + abilities_store: &mut AbilitiesStore, + obligation_cache: &mut ObligationCache, + types: &mut Types, + aliases: &mut Aliases, + either_type_index: TypeOrVar, +) -> Variable { + match either_type_index.split() { + Ok(type_index) => { + // Converts the celled type to a variable, emplacing the new variable for re-use. + let var = type_to_var( + env, + rank, + problems, + abilities_store, + obligation_cache, + types, + aliases, + type_index, + ); + + debug_assert!( + matches!(types[type_index], TypeTag::Variable(v) if v == var) + || matches!( + types[type_index], + TypeTag::EmptyRecord | TypeTag::EmptyTagUnion + ) + ); + var + } + Err(var_index) => { + // we cheat, and store the variable directly in the index + unsafe { Variable::from_index(var_index.index() as _) } + } + } +} + +pub(crate) fn type_to_var( + env: &mut Env, + rank: Rank, + problems: &mut Vec, + abilities_store: &mut AbilitiesStore, + obligation_cache: &mut ObligationCache, + types: &mut Types, + aliases: &mut Aliases, + typ: Index, +) -> Variable { + if let TypeTag::Variable(var) = types[typ] { + var + } else { + let mut arena = take_scratchpad(); + + let var = type_to_var_help( + env, + rank, + problems, + abilities_store, + obligation_cache, + &arena, + aliases, + types, + typ, + false, + ); + + arena.reset(); + put_scratchpad(arena); + + var + } +} + +enum RegisterVariable { + /// Based on the Type, we already know what variable this will be + Direct(Variable), + /// This Type needs more complicated Content. We reserve a Variable + /// for it, but put a placeholder Content in subs + Deferred, +} + +impl RegisterVariable { + fn from_type( + env: &mut Env, + rank: Rank, + arena: &'_ bumpalo::Bump, + types: &mut Types, + typ: Index, + ) -> Self { + use RegisterVariable::*; + + match types[typ] { + TypeTag::Variable(var) => Direct(var), + TypeTag::EmptyRecord => Direct(Variable::EMPTY_RECORD), + TypeTag::EmptyTagUnion => Direct(Variable::EMPTY_TAG_UNION), + TypeTag::DelayedAlias { shared } + | TypeTag::StructuralAlias { shared, .. } + | TypeTag::OpaqueAlias { shared, .. } + | TypeTag::HostExposedAlias { shared, .. } => { + let AliasShared { symbol, .. } = types[shared]; + if let Some(reserved) = Variable::get_reserved(symbol) { + let direct_var = if rank.is_generalized() { + // reserved variables are stored with rank NONE + reserved + } else { + // for any other rank, we need to copy; it takes care of adjusting the rank + deep_copy_var_in(env, rank, reserved, arena) + }; + // Safety: the `destination` will become the source-of-truth for the type index, since it + // was not already transformed before (if it was, we'd be in the Variable branch!) + let _old_typ = unsafe { types.emplace_variable(typ, direct_var) }; + return Direct(direct_var); + } + + Deferred + } + _ => Deferred, + } + } + + #[inline(always)] + fn with_stack( + env: &mut Env, + rank: Rank, + arena: &'_ bumpalo::Bump, + types: &mut Types, + typ_index: Index, + stack: &mut bumpalo::collections::Vec<'_, TypeToVar>, + ) -> Variable { + match Self::from_type(env, rank, arena, types, typ_index) { + Self::Direct(var) => var, + Self::Deferred => { + let var = env.subs.fresh_unnamed_flex_var(); + // Safety: the `destination` will become the source-of-truth for the type index, since it + // was not already transformed before (if it was, it wouldn't be deferred!) + let typ = unsafe { types.emplace_variable(typ_index, var) }; + stack.push(TypeToVar::Defer { + typ, + typ_index, + destination: var, + ambient_function: AmbientFunctionPolicy::NoFunction, + }); + var + } + } + } +} + +/// Instantiation of ambient functions in unspecialized lambda sets is somewhat tricky due to other +/// optimizations we have in place. This struct tells us how they should be instantiated. +#[derive(Debug)] +enum AmbientFunctionPolicy { + /// We're not in a function. This variant may never hold for unspecialized lambda sets. + NoFunction, + /// We're in a known function. + Function(Variable), +} + +impl AmbientFunctionPolicy { + fn link_to_alias_lambda_set_var(&self, subs: &mut Subs, var: Variable) { + let ambient_function = match self { + AmbientFunctionPolicy::Function(var) => *var, + _ => { + // Might be linked at a deeper point in time, ignore for now + return; + } + }; + let content = subs.get_content_without_compacting(var); + let new_content = match content { + Content::LambdaSet(LambdaSet { + solved, + recursion_var, + unspecialized, + ambient_function: _, + }) => Content::LambdaSet(LambdaSet { + solved: *solved, + recursion_var: *recursion_var, + unspecialized: *unspecialized, + ambient_function, + }), + Content::FlexVar(_) => { + // Something like + // Encoder fmt : List U8, fmt -a-> List U8 | fmt has EncoderFormatting + // THEORY: Replace these with empty lambda sets. They will unify the same as a flex + // var does, but allows us to record the ambient function properly. + Content::LambdaSet(LambdaSet { + solved: UnionLabels::default(), + recursion_var: OptVariable::NONE, + unspecialized: SubsSlice::default(), + ambient_function, + }) + } + content => internal_error!("{:?}({:?}) not a lambda set", content, var), + }; + subs.set_content_unchecked(var, new_content); + } +} + +#[derive(Debug)] +enum TypeToVar { + Defer { + typ: TypeTag, + typ_index: Index, + destination: Variable, + ambient_function: AmbientFunctionPolicy, + }, +} + +#[allow(clippy::too_many_arguments)] +pub(crate) fn type_to_var_help( + env: &mut Env, + rank: Rank, + problems: &mut Vec, + abilities_store: &AbilitiesStore, + obligation_cache: &mut ObligationCache, + arena: &bumpalo::Bump, + aliases: &mut Aliases, + types: &mut Types, + typ: Index, + // Helpers for instantiating ambient functions of lambda set variables from type aliases. + is_alias_lambda_set_arg: bool, +) -> Variable { + use bumpalo::collections::Vec; + + let mut stack = Vec::with_capacity_in(8, arena); + let mut bind_to_abilities = Vec::new_in(arena); + + macro_rules! helper { + ($typ:expr, $ambient_function_policy:expr) => {{ + match RegisterVariable::from_type(env, rank, arena, types, $typ) { + RegisterVariable::Direct(var) => { + // If the variable is just a type variable but we know we're in a lambda set + // context, try to link to the ambient function. + $ambient_function_policy.link_to_alias_lambda_set_var(env.subs, var); + + var + } + RegisterVariable::Deferred => { + let var = env.subs.fresh_unnamed_flex_var(); + + // Safety: the `destination` will become the source-of-truth for the type index, since it + // was not already transformed before (if it was, it wouldn't be deferred!) + let typ = unsafe { types.emplace_variable($typ, var) }; + + stack.push(TypeToVar::Defer { + typ, + typ_index: $typ, + destination: var, + ambient_function: $ambient_function_policy, + }); + + var + } + } + }}; + ($typ:expr) => {{ + helper!($typ, AmbientFunctionPolicy::NoFunction) + }}; + } + + let result = helper!(typ); + + while let Some(TypeToVar::Defer { + typ_index, + typ, + destination, + ambient_function, + }) = stack.pop() + { + use TypeTag::*; + match typ { + Variable(_) | EmptyRecord | EmptyTagUnion => { + unreachable!("This variant should never be deferred!",) + } + RangedNumber(range) => { + let content = Content::RangedNumber(range); + + env.register_with_known_var(destination, rank, content) + } + Apply { + symbol, + type_argument_regions: _, + region: _, + } => { + let arguments = types.get_type_arguments(typ_index); + let new_arguments = VariableSubsSlice::reserve_into_subs(env.subs, arguments.len()); + for (target_index, var_index) in + (new_arguments.indices()).zip(arguments.into_iter()) + { + let var = helper!(var_index); + env.subs.variables[target_index] = var; + } + + let flat_type = FlatType::Apply(symbol, new_arguments); + let content = Content::Structure(flat_type); + + env.register_with_known_var(destination, rank, content) + } + + ClosureTag { + name, + ambient_function, + } => { + let captures = types.get_type_arguments(typ_index); + let union_lambdas = + create_union_lambda(env, rank, arena, types, name, captures, &mut stack); + + let content = Content::LambdaSet(subs::LambdaSet { + solved: union_lambdas, + // We may figure out the lambda set is recursive during solving, but it never + // is to begin with. + recursion_var: OptVariable::NONE, + unspecialized: SubsSlice::default(), + ambient_function, + }); + + env.register_with_known_var(destination, rank, content) + } + UnspecializedLambdaSet { unspecialized } => { + let unspecialized_slice = SubsSlice::extend_new( + &mut env.subs.unspecialized_lambda_sets, + std::iter::once(unspecialized), + ); + + // `ClosureTag` ambient functions are resolved during constraint generation. + // But `UnspecializedLambdaSet`s can only ever live in a type signature, and don't + // correspond to a expression, so they are never constrained. + // Instead, we resolve their ambient functions during type translation, observing + // the invariant that a lambda set can only ever appear under a function type. + let ambient_function = match ambient_function { + AmbientFunctionPolicy::NoFunction => { + debug_assert!(is_alias_lambda_set_arg); + // To be filled in during delayed type alias instantiation + roc_types::subs::Variable::NULL + } + AmbientFunctionPolicy::Function(var) => var, + }; + + let content = Content::LambdaSet(subs::LambdaSet { + unspecialized: unspecialized_slice, + solved: UnionLabels::default(), + recursion_var: OptVariable::NONE, + ambient_function, + }); + + env.register_with_known_var(destination, rank, content) + } + // This case is important for the rank of boolean variables + Function(closure_type, ret_type) => { + let arguments = types.get_type_arguments(typ_index); + let new_arguments = VariableSubsSlice::reserve_into_subs(env.subs, arguments.len()); + for (target_index, var_index) in + (new_arguments.indices()).zip(arguments.into_iter()) + { + let var = helper!(var_index); + env.subs.variables[target_index] = var; + } + + let ret_var = helper!(ret_type); + let closure_var = + helper!(closure_type, AmbientFunctionPolicy::Function(destination)); + let content = + Content::Structure(FlatType::Func(new_arguments, closure_var, ret_var)); + + env.register_with_known_var(destination, rank, content) + } + Record(fields) => { + let ext_slice = types.get_type_arguments(typ_index); + + // An empty fields is inefficient (but would be correct) + // If hit, try to turn the value into an EmptyRecord in canonicalization + debug_assert!(!fields.is_empty() || !ext_slice.is_empty()); + + let mut field_vars = Vec::with_capacity_in(fields.len(), arena); + + let (fields_names, field_kinds, field_tys) = types.record_fields_slices(fields); + + for ((field, field_kind), field_type) in (fields_names.into_iter()) + .zip(field_kinds.into_iter()) + .zip(field_tys.into_iter()) + { + let field_var = { + let t = helper!(field_type); + types[field_kind].replace(t) + }; + + field_vars.push((types[field].clone(), field_var)); + } + + debug_assert!(ext_slice.len() <= 1); + let temp_ext_var = match ext_slice.into_iter().next() { + None => roc_types::subs::Variable::EMPTY_RECORD, + Some(ext) => helper!(ext), + }; + + let (it, new_ext_var) = + gather_fields_unsorted_iter(env.subs, RecordFields::empty(), temp_ext_var) + .expect("Something ended up weird in this record type"); + + let it = it + .into_iter() + .map(|(field, field_type)| (field.clone(), field_type)); + + field_vars.extend(it); + insertion_sort_by(&mut field_vars, RecordFields::compare); + + let record_fields = RecordFields::insert_into_subs(env.subs, field_vars); + + let content = Content::Structure(FlatType::Record(record_fields, new_ext_var)); + + env.register_with_known_var(destination, rank, content) + } + + Tuple(elems) => { + let ext_slice = types.get_type_arguments(typ_index); + + // Elems should never be empty; we don't support empty tuples + debug_assert!(!elems.is_empty() || !ext_slice.is_empty()); + + let mut elem_vars = Vec::with_capacity_in(elems.len(), arena); + + let (indices, elem_tys) = types.tuple_elems_slices(elems); + + for (index, elem_type) in indices.into_iter().zip(elem_tys.into_iter()) { + let elem_var = helper!(elem_type); + elem_vars.push((types[index], elem_var)); + } + + debug_assert!(ext_slice.len() <= 1); + let temp_ext_var = match ext_slice.into_iter().next() { + None => roc_types::subs::Variable::EMPTY_TUPLE, + Some(ext) => helper!(ext), + }; + + let (it, new_ext_var) = + gather_tuple_elems_unsorted_iter(env.subs, TupleElems::empty(), temp_ext_var) + .expect("Something ended up weird in this tuple type"); + + elem_vars.extend(it); + let tuple_elems = TupleElems::insert_into_subs(env.subs, elem_vars); + + let content = Content::Structure(FlatType::Tuple(tuple_elems, new_ext_var)); + + env.register_with_known_var(destination, rank, content) + } + + TagUnion(tags, ext_openness) => { + let ext_slice = types.get_type_arguments(typ_index); + + // An empty tags is inefficient (but would be correct) + // If hit, try to turn the value into an EmptyTagUnion in canonicalization + debug_assert!(!tags.is_empty() || !ext_slice.is_empty()); + + let (union_tags, ext) = type_to_union_tags( + env, + rank, + arena, + types, + tags, + ext_slice, + ext_openness, + &mut stack, + ); + let content = Content::Structure(FlatType::TagUnion(union_tags, ext)); + + env.register_with_known_var(destination, rank, content) + } + FunctionOrTagUnion(symbol, ext_openness) => { + let ext_slice = types.get_type_arguments(typ_index); + let tag_name = types.get_tag_name(&typ_index).clone(); + + debug_assert!(ext_slice.len() <= 1); + let temp_ext = match ext_slice.into_iter().next() { + Some(ext) => { + let var = helper!(ext); + TagExt::from_can(var, ext_openness) + } + None => TagExt::Any(roc_types::subs::Variable::EMPTY_TAG_UNION), + }; + + let (it, ext) = roc_types::types::gather_tags_unsorted_iter( + env.subs, + UnionTags::default(), + temp_ext, + ) + .expect("extension var could not be seen as a tag union"); + + for _ in it { + unreachable!("we assert that the ext var is empty; otherwise we'd already know it was a tag union!"); + } + + let tag_names = SubsSlice::extend_new(&mut env.subs.tag_names, [tag_name]); + let symbols = SubsSlice::extend_new(&mut env.subs.symbol_names, [symbol]); + + let content = + Content::Structure(FlatType::FunctionOrTagUnion(tag_names, symbols, ext)); + + env.register_with_known_var(destination, rank, content) + } + RecursiveTagUnion(rec_var, tags, ext_openness) => { + let ext_slice = types.get_type_arguments(typ_index); + + // An empty tags is inefficient (but would be correct) + // If hit, try to turn the value into an EmptyTagUnion in canonicalization + debug_assert!(!tags.is_empty() || !ext_slice.is_empty()); + + let (union_tags, ext) = type_to_union_tags( + env, + rank, + arena, + types, + tags, + ext_slice, + ext_openness, + &mut stack, + ); + let content = + Content::Structure(FlatType::RecursiveTagUnion(rec_var, union_tags, ext)); + + let tag_union_var = destination; + env.register_with_known_var(tag_union_var, rank, content); + + env.register_with_known_var( + rec_var, + rank, + Content::RecursionVar { + opt_name: None, + structure: tag_union_var, + }, + ); + + tag_union_var + } + + DelayedAlias { shared } => { + let AliasShared { + symbol, + type_argument_abilities, + type_argument_regions, + lambda_set_variables, + infer_ext_in_output_variables, + } = types[shared]; + + let type_arguments = types.get_type_arguments(typ_index); + + let alias_variables = { + let all_vars_length = type_arguments.len() + + lambda_set_variables.len() + + infer_ext_in_output_variables.len(); + let new_variables = + VariableSubsSlice::reserve_into_subs(env.subs, all_vars_length); + + let type_arguments_offset = 0; + let lambda_set_vars_offset = type_arguments_offset + type_arguments.len(); + let infer_ext_vars_offset = lambda_set_vars_offset + lambda_set_variables.len(); + + for (((target_index, arg_type), arg_region), abilities) in + (new_variables.indices().skip(type_arguments_offset)) + .zip(type_arguments.into_iter()) + .zip(type_argument_regions.into_iter()) + .zip(type_argument_abilities.into_iter()) + { + let copy_var = helper!(arg_type); + env.subs.variables[target_index] = copy_var; + if !types[abilities].is_empty() { + let arg_region = types[arg_region]; + bind_to_abilities.push((Loc::at(arg_region, copy_var), abilities)); + } + } + + let it = (new_variables.indices().skip(lambda_set_vars_offset)) + .zip(lambda_set_variables.into_iter()); + for (target_index, ls) in it { + // We MUST do this now, otherwise when linking the ambient function during + // instantiation of the real var, there will be nothing to link against. + let copy_var = type_to_var_help( + env, + rank, + problems, + abilities_store, + obligation_cache, + arena, + aliases, + types, + ls, + true, + ); + env.subs.variables[target_index] = copy_var; + } + + let it = (new_variables.indices().skip(infer_ext_vars_offset)) + .zip(infer_ext_in_output_variables.into_iter()); + for (target_index, ext_typ) in it { + let copy_var = helper!(ext_typ); + env.subs.variables[target_index] = copy_var; + } + + AliasVariables { + variables_start: new_variables.start, + type_variables_len: type_arguments.len() as _, + lambda_set_variables_len: lambda_set_variables.len() as _, + all_variables_len: all_vars_length as _, + } + }; + + let (alias_variable, kind) = aliases.instantiate_real_var( + env, + rank, + problems, + abilities_store, + obligation_cache, + arena, + types, + symbol, + alias_variables, + ); + + let content = Content::Alias(symbol, alias_variables, alias_variable, kind); + + env.register_with_known_var(destination, rank, content) + } + + StructuralAlias { shared, actual } | OpaqueAlias { shared, actual } => { + let kind = match typ { + StructuralAlias { .. } => AliasKind::Structural, + OpaqueAlias { .. } => AliasKind::Opaque, + _ => internal_error!(), + }; + + let AliasShared { + symbol, + type_argument_abilities, + type_argument_regions, + lambda_set_variables, + infer_ext_in_output_variables, + } = types[shared]; + + debug_assert!(roc_types::subs::Variable::get_reserved(symbol).is_none()); + + let type_arguments = types.get_type_arguments(typ_index); + + let alias_variables = { + let all_vars_length = type_arguments.len() + + lambda_set_variables.len() + + infer_ext_in_output_variables.len(); + + let type_arguments_offset = 0; + let lambda_set_vars_offset = type_arguments_offset + type_arguments.len(); + let infer_ext_vars_offset = lambda_set_vars_offset + lambda_set_variables.len(); + + let new_variables = + VariableSubsSlice::reserve_into_subs(env.subs, all_vars_length); + + for (((target_index, typ), region), abilities) in + (new_variables.indices().skip(type_arguments_offset)) + .zip(type_arguments.into_iter()) + .zip(type_argument_regions.into_iter()) + .zip(type_argument_abilities.into_iter()) + { + let copy_var = helper!(typ); + env.subs.variables[target_index] = copy_var; + if !types[abilities].is_empty() { + let region = types[region]; + bind_to_abilities.push((Loc::at(region, copy_var), abilities)); + } + } + + let it = (new_variables.indices().skip(lambda_set_vars_offset)) + .zip(lambda_set_variables.into_iter()); + for (target_index, ls) in it { + let copy_var = helper!(ls); + env.subs.variables[target_index] = copy_var; + } + + let it = (new_variables.indices().skip(infer_ext_vars_offset)) + .zip(infer_ext_in_output_variables.into_iter()); + for (target_index, ext_typ) in it { + let copy_var = helper!(ext_typ); + env.subs.variables[target_index] = copy_var; + } + + AliasVariables { + variables_start: new_variables.start, + type_variables_len: type_arguments.len() as _, + lambda_set_variables_len: lambda_set_variables.len() as _, + all_variables_len: all_vars_length as _, + } + }; + + let alias_variable = if let Symbol::RESULT_RESULT = symbol { + roc_result_to_var(env, rank, arena, types, actual, &mut stack) + } else { + helper!(actual) + }; + let content = Content::Alias(symbol, alias_variables, alias_variable, kind); + + env.register_with_known_var(destination, rank, content) + } + HostExposedAlias { + shared, + actual_type: alias_type, + actual_variable: actual_var, + } => { + let AliasShared { + symbol, + type_argument_abilities: _, + type_argument_regions: _, + lambda_set_variables, + infer_ext_in_output_variables: _, // TODO + } = types[shared]; + + let type_arguments = types.get_type_arguments(typ_index); + + let alias_variables = { + let length = type_arguments.len() + lambda_set_variables.len(); + let new_variables = VariableSubsSlice::reserve_into_subs(env.subs, length); + + for (target_index, arg_type) in + (new_variables.indices()).zip(type_arguments.into_iter()) + { + let copy_var = helper!(arg_type); + env.subs.variables[target_index] = copy_var; + } + let it = (new_variables.indices().skip(type_arguments.len())) + .zip(lambda_set_variables.into_iter()); + for (target_index, ls) in it { + // We MUST do this now, otherwise when linking the ambient function during + // instantiation of the real var, there will be nothing to link against. + let copy_var = type_to_var_help( + env, + rank, + problems, + abilities_store, + obligation_cache, + arena, + aliases, + types, + ls, + true, + ); + env.subs.variables[target_index] = copy_var; + } + + AliasVariables { + variables_start: new_variables.start, + type_variables_len: type_arguments.len() as _, + lambda_set_variables_len: lambda_set_variables.len() as _, + all_variables_len: length as _, + } + }; + + // cannot use helper! here because this variable may be involved in unification below + let alias_variable = type_to_var_help( + env, + rank, + problems, + abilities_store, + obligation_cache, + arena, + aliases, + types, + alias_type, + false, + ); + // TODO(opaques): I think host-exposed aliases should always be structural + // (when does it make sense to give a host an opaque type?) + let content = Content::Alias( + symbol, + alias_variables, + alias_variable, + AliasKind::Structural, + ); + let result = env.register_with_known_var(destination, rank, content); + + // We only want to unify the actual_var with the alias once + // if it's already redirected (and therefore, redundant) + // don't do it again + if !env.subs.redundant(actual_var) { + let descriptor = env.subs.get(result); + env.subs.union(result, actual_var, descriptor); + } + + result + } + Error => { + let content = Content::Error; + + env.register_with_known_var(destination, rank, content) + } + }; + } + + for (Loc { value: var, region }, abilities) in bind_to_abilities { + let abilities = &types[abilities]; + match *env.subs.get_content_unchecked(var) { + Content::RigidVar(a) => { + // TODO(multi-abilities): check run cache + let abilities_slice = SubsSlice::extend_new( + &mut env.subs.symbol_names, + abilities.sorted_iter().copied(), + ); + env.subs + .set_content(var, Content::RigidAbleVar(a, abilities_slice)); + } + Content::RigidAbleVar(_, abs) + if (env.subs.get_subs_slice(abs).iter()).eq(abilities.sorted_iter()) => + { + // pass, already bound + } + _ => { + let abilities_slice = SubsSlice::extend_new( + &mut env.subs.symbol_names, + abilities.sorted_iter().copied(), + ); + + let flex_ability = env.register(rank, Content::FlexAbleVar(None, abilities_slice)); + + let category = Category::OpaqueArg; + match unify( + &mut env.uenv(), + var, + flex_ability, + Mode::EQ, + Polarity::OF_VALUE, + ) { + Unified::Success { + vars: _, + must_implement_ability, + lambda_sets_to_specialize, + extra_metadata: _, + } => { + // No introduction needed + + if !must_implement_ability.is_empty() { + let new_problems = obligation_cache.check_obligations( + env.subs, + abilities_store, + must_implement_ability, + AbilityImplError::BadExpr(region, category, flex_ability), + ); + problems.extend(new_problems); + } + debug_assert!(lambda_sets_to_specialize + .drain() + .all(|(_, vals)| vals.is_empty())); + } + Unified::Failure(_vars, actual_type, expected_type, _bad_impls) => { + // No introduction needed + + let problem = TypeError::BadExpr( + region, + category, + actual_type, + Expected::NoExpectation(expected_type), + ); + + problems.push(problem); + } + } + } + } + } + + result +} + +#[inline(always)] +fn roc_result_to_var( + env: &mut Env, + rank: Rank, + arena: &'_ bumpalo::Bump, + types: &mut Types, + result_type: Index, + stack: &mut bumpalo::collections::Vec<'_, TypeToVar>, +) -> Variable { + match types[result_type] { + TypeTag::TagUnion(tags, _ext_openness) => { + let ext_slice = types.get_type_arguments(result_type); + + debug_assert!(ext_slice.is_empty()); + debug_assert!(tags.len() == 2); + + let (tags_slice, payload_slices_slice) = types.union_tag_slices(tags); + + if let ([err, ok], [err_args, ok_args]) = + (&types[tags_slice], &types[payload_slices_slice]) + { + debug_assert_eq!(err, &env.subs.tag_names[0]); + debug_assert_eq!(ok, &env.subs.tag_names[1]); + + debug_assert_eq!(err_args.len(), 1); + debug_assert_eq!(ok_args.len(), 1); + + if let (Some(err_type), Some(ok_type)) = + (err_args.into_iter().next(), ok_args.into_iter().next()) + { + let err_var = + RegisterVariable::with_stack(env, rank, arena, types, err_type, stack); + let ok_var = + RegisterVariable::with_stack(env, rank, arena, types, ok_type, stack); + + let start = env.subs.variables.len() as u32; + let err_slice = SubsSlice::new(start, 1); + let ok_slice = SubsSlice::new(start + 1, 1); + + env.subs.variables.push(err_var); + env.subs.variables.push(ok_var); + + let variables = SubsSlice::new(env.subs.variable_slices.len() as _, 2); + env.subs.variable_slices.push(err_slice); + env.subs.variable_slices.push(ok_slice); + + let union_tags = UnionTags::from_slices(Subs::RESULT_TAG_NAMES, variables); + let ext = TagExt::Any(Variable::EMPTY_TAG_UNION); + + let content = Content::Structure(FlatType::TagUnion(union_tags, ext)); + + return env.register(rank, content); + } + } + + unreachable!("invalid arguments to Result.Result; canonicalization should catch this!") + } + _ => unreachable!("not a valid type inside a Result.Result alias"), + } +} + +fn insertion_sort_by(arr: &mut [T], mut compare: F) +where + F: FnMut(&T, &T) -> std::cmp::Ordering, +{ + for i in 1..arr.len() { + let val = &arr[i]; + let mut j = i; + let pos = arr[..i] + .binary_search_by(|x| compare(x, val)) + .unwrap_or_else(|pos| pos); + // Swap all elements until specific position. + while j > pos { + arr.swap(j - 1, j); + j -= 1; + } + } +} + +fn sorted_no_duplicate_tags(tag_slices: &[TagName]) -> bool { + match tag_slices.split_first() { + None => true, + Some((first, rest)) => { + let mut current = first; + + for next in rest { + if current >= next { + return false; + } else { + current = next; + } + } + + true + } + } +} + +fn sort_and_deduplicate(tag_vars: &mut bumpalo::collections::Vec<(TagName, T)>) { + insertion_sort_by(tag_vars, |(a, _), (b, _)| a.cmp(b)); + + // deduplicate, keeping the right-most occurrence of a tag name + let mut i = 0; + + while i < tag_vars.len() { + match (tag_vars.get(i), tag_vars.get(i + 1)) { + (Some((t1, _)), Some((t2, _))) => { + if t1 == t2 { + tag_vars.remove(i); + } else { + i += 1; + } + } + _ => break, + } + } +} + +/// Find whether the current run of tag names is in the subs.tag_names array already. If so, +/// we take a SubsSlice to the existing tag names, so we don't have to add/clone those tag names +/// and keep subs memory consumption low +fn find_tag_name_run(slice: &[TagName], subs: &mut Subs) -> Option> { + use std::cmp::Ordering; + + let tag_name = slice.get(0)?; + + let mut result = None; + + // the `SubsSlice` that inserting `slice` into subs would give + let bigger_slice = SubsSlice::new(subs.tag_names.len() as _, slice.len() as _); + + match subs.tag_name_cache.get_mut(tag_name) { + Some(occupied) => { + let subs_slice = *occupied; + + let prefix_slice = SubsSlice::new(subs_slice.start, slice.len() as _); + + if slice.len() == 1 { + return Some(prefix_slice); + } + + match slice.len().cmp(&subs_slice.len()) { + Ordering::Less => { + // we might have a prefix + let tag_names = &subs.tag_names[subs_slice.start as usize..]; + + for (from_subs, from_slice) in tag_names.iter().zip(slice.iter()) { + if from_subs != from_slice { + return None; + } + } + + result = Some(prefix_slice); + } + Ordering::Equal => { + let tag_names = &subs.tag_names[subs_slice.indices()]; + + for (from_subs, from_slice) in tag_names.iter().zip(slice.iter()) { + if from_subs != from_slice { + return None; + } + } + + result = Some(subs_slice); + } + Ordering::Greater => { + // switch to the bigger slice that is not inserted yet, but will be soon + *occupied = bigger_slice; + } + } + } + None => { + subs.tag_name_cache.push(tag_name, bigger_slice); + } + } + + result +} + +#[inline(always)] +fn register_tag_arguments( + env: &mut Env, + rank: Rank, + arena: &'_ bumpalo::Bump, + types: &mut Types, + stack: &mut bumpalo::collections::Vec<'_, TypeToVar>, + arguments: Slice, +) -> VariableSubsSlice { + if arguments.is_empty() { + VariableSubsSlice::default() + } else { + let new_variables = VariableSubsSlice::reserve_into_subs(env.subs, arguments.len()); + let it = new_variables.indices().zip(arguments.into_iter()); + + for (target_index, argument) in it { + let var = RegisterVariable::with_stack(env, rank, arena, types, argument, stack); + env.subs.variables[target_index] = var; + } + + new_variables + } +} + +/// Assumes that the tags are sorted and there are no duplicates! +fn insert_tags_fast_path( + env: &mut Env, + rank: Rank, + arena: &'_ bumpalo::Bump, + types: &mut Types, + union_tags: UnionTags, + stack: &mut bumpalo::collections::Vec<'_, TypeToVar>, +) -> UnionTags { + let (tags, payload_slices) = types.union_tag_slices(union_tags); + + debug_assert_eq!(tags.len(), payload_slices.len()); + + if let [arguments_slice] = &types[payload_slices] { + let arguments_slice = *arguments_slice; + + let variable_slice = + register_tag_arguments(env, rank, arena, types, stack, arguments_slice); + + let new_variable_slices = + SubsSlice::extend_new(&mut env.subs.variable_slices, [variable_slice]); + + macro_rules! subs_tag_name { + ($tag_name_slice:expr) => { + return UnionTags::from_slices($tag_name_slice, new_variable_slices) + }; + } + + match types[tags][0].0.as_str() { + "Ok" => subs_tag_name!(Subs::TAG_NAME_OK.as_slice()), + "Err" => subs_tag_name!(Subs::TAG_NAME_ERR.as_slice()), + "InvalidNumStr" => subs_tag_name!(Subs::TAG_NAME_INVALID_NUM_STR.as_slice()), + "BadUtf8" => subs_tag_name!(Subs::TAG_NAME_BAD_UTF_8.as_slice()), + "OutOfBounds" => subs_tag_name!(Subs::TAG_NAME_OUT_OF_BOUNDS.as_slice()), + _other => {} + } + } + + let new_variable_slices = SubsSlice::reserve_variable_slices(env.subs, tags.len()); + match find_tag_name_run(&types[tags], env.subs) { + Some(new_tag_names) => { + let it = (new_variable_slices.indices()).zip(payload_slices.into_iter()); + + for (variable_slice_index, arguments_index) in it { + let arguments = types[arguments_index]; + env.subs.variable_slices[variable_slice_index] = + register_tag_arguments(env, rank, arena, types, stack, arguments); + } + + UnionTags::from_slices(new_tag_names, new_variable_slices) + } + None => { + let new_tag_names = SubsSlice::reserve_tag_names(env.subs, tags.len()); + + let it = (new_variable_slices.indices()) + .zip(new_tag_names.indices()) + .zip(tags.into_iter()) + .zip(payload_slices.into_iter()); + + for (((variable_slice_index, tag_name_index), tag_name), arguments_index) in it { + let arguments = types[arguments_index]; + env.subs.variable_slices[variable_slice_index] = + register_tag_arguments(env, rank, arena, types, stack, arguments); + + env.subs.tag_names[tag_name_index] = types[tag_name].clone(); + } + + UnionTags::from_slices(new_tag_names, new_variable_slices) + } + } +} + +fn insert_tags_slow_path( + env: &mut Env, + rank: Rank, + arena: &'_ bumpalo::Bump, + types: &mut Types, + union_tags: UnionTags, + mut tag_vars: bumpalo::collections::Vec<(TagName, VariableSubsSlice)>, + stack: &mut bumpalo::collections::Vec<'_, TypeToVar>, +) -> UnionTags { + let (tags, payload_slices) = types.union_tag_slices(union_tags); + + for (tag_index, tag_argument_types_index) in (tags.into_iter()).zip(payload_slices.into_iter()) + { + let tag_argument_types = &types[tag_argument_types_index]; + + let new_slice = VariableSubsSlice::reserve_into_subs(env.subs, tag_argument_types.len()); + + for (i, arg) in (new_slice.indices()).zip(tag_argument_types.into_iter()) { + let var = RegisterVariable::with_stack(env, rank, arena, types, arg, stack); + env.subs.variables[i] = var; + } + + tag_vars.push((types[tag_index].clone(), new_slice)); + } + + sort_and_deduplicate(&mut tag_vars); + + UnionTags::insert_slices_into_subs(env.subs, tag_vars) +} + +fn type_to_union_tags( + env: &mut Env, + rank: Rank, + arena: &'_ bumpalo::Bump, + types: &mut Types, + union_tags: UnionTags, + opt_ext_slice: Slice, + ext_openness: ExtImplicitOpenness, + stack: &mut bumpalo::collections::Vec<'_, TypeToVar>, +) -> (UnionTags, TagExt) { + use bumpalo::collections::Vec; + + let (tags, _) = types.union_tag_slices(union_tags); + + let sorted = tags.len() == 1 || sorted_no_duplicate_tags(&types[tags]); + + debug_assert!(opt_ext_slice.len() <= 1); + + match opt_ext_slice.into_iter().next() { + None => { + let ext = Variable::EMPTY_TAG_UNION; + + let union_tags = if sorted { + insert_tags_fast_path(env, rank, arena, types, union_tags, stack) + } else { + let tag_vars = Vec::with_capacity_in(tags.len(), arena); + insert_tags_slow_path(env, rank, arena, types, union_tags, tag_vars, stack) + }; + + (union_tags, TagExt::Any(ext)) + } + Some(ext) => { + let mut tag_vars = Vec::with_capacity_in(tags.len(), arena); + + let temp_ext = { + let temp_ext_var = + RegisterVariable::with_stack(env, rank, arena, types, ext, stack); + TagExt::from_can(temp_ext_var, ext_openness) + }; + let (it, ext) = roc_types::types::gather_tags_unsorted_iter( + env.subs, + UnionTags::default(), + temp_ext, + ) + .expect("extension var could not be seen as tag union"); + + tag_vars.extend(it.map(|(n, v)| (n.clone(), v))); + + let union_tags = if tag_vars.is_empty() && sorted { + insert_tags_fast_path(env, rank, arena, types, union_tags, stack) + } else { + insert_tags_slow_path(env, rank, arena, types, union_tags, tag_vars, stack) + }; + + (union_tags, ext) + } + } +} + +fn create_union_lambda( + env: &mut Env, + rank: Rank, + arena: &'_ bumpalo::Bump, + types: &mut Types, + closure: Symbol, + capture_types: Slice, + stack: &mut bumpalo::collections::Vec<'_, TypeToVar>, +) -> UnionLambdas { + let variable_slice = register_tag_arguments(env, rank, arena, types, stack, capture_types); + let new_variable_slices = + SubsSlice::extend_new(&mut env.subs.variable_slices, [variable_slice]); + + let lambda_name_slice = SubsSlice::extend_new(&mut env.subs.symbol_names, [closure]); + + UnionLambdas::from_slices(lambda_name_slice, new_variable_slices) +} diff --git a/crates/compiler/solve/tests/solve_expr.rs b/crates/compiler/solve/tests/solve_expr.rs index 45dfa9f7b82..98392e80f42 100644 --- a/crates/compiler/solve/tests/solve_expr.rs +++ b/crates/compiler/solve/tests/solve_expr.rs @@ -49,7 +49,7 @@ mod solve_expr { exposed_to_host.retain(|s, _| !abilities_store.is_specialization_name(*s)); - debug_assert!(exposed_to_host.len() == 1, "{:?}", exposed_to_host); + debug_assert!(exposed_to_host.len() == 1, "{exposed_to_host:?}"); let (_symbol, variable) = exposed_to_host.into_iter().next().unwrap(); let actual_str = name_and_print_var(variable, subs, home, &interns, DebugPrint::NOTHING); @@ -61,8 +61,7 @@ mod solve_expr { assert!( can_problems.is_empty(), - "Canonicalization problems: {}", - can_problems + "Canonicalization problems: {can_problems}" ); assert_eq!(actual, expected.to_string()); @@ -73,17 +72,13 @@ mod solve_expr { assert!( can_problems.is_empty(), - "Canonicalization problems: {}", - can_problems + "Canonicalization problems: {can_problems}" ); if !type_problems.is_empty() { // fail with an assert, but print the problems normally so rust doesn't try to diff // an empty vec with the problems. - panic!( - "expected:\n{:?}\ninferred:\n{:?}\nproblems:\n{}", - expected, actual, type_problems, - ); + panic!("expected:\n{expected:?}\ninferred:\n{actual:?}\nproblems:\n{type_problems}",); } assert_eq!(actual, expected.to_string()); } diff --git a/crates/compiler/test_derive/src/util.rs b/crates/compiler/test_derive/src/util.rs index 566d433c9c0..d6e8d5fa10f 100644 --- a/crates/compiler/test_derive/src/util.rs +++ b/crates/compiler/test_derive/src/util.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; use bumpalo::Bump; use roc_packaging::cache::RocCacheDir; +use roc_solve::module::{SolveConfig, SolveOutput}; use ven_pretty::DocAllocator; use roc_can::{ @@ -21,7 +22,8 @@ use roc_constrain::expr::constrain_decls; use roc_debug_flags::dbg_do; use roc_derive::DerivedModule; use roc_derive_key::{DeriveBuiltin, DeriveError, DeriveKey, Derived}; -use roc_load_internal::file::{add_imports, LoadedModule, Threading}; +use roc_load_internal::file::{add_imports, Threading}; +use roc_load_internal::module::LoadedModule; use roc_module::symbol::{IdentIds, Interns, ModuleId, Symbol}; use roc_region::all::LineInfo; use roc_reporting::report::{type_problem, RocDocAllocator}; @@ -333,7 +335,7 @@ fn assemble_derived_golden( specialization_lsets.sort_by_key(|(region, _)| *region); for (region, var) in specialization_lsets { let pretty_lset = print_var(var, false); - let _ = writeln!(pretty_buf, "# @<{}>: {}", region, pretty_lset); + let _ = writeln!(pretty_buf, "# @<{region}>: {pretty_lset}"); } pretty_buf.push_str(derived_source); @@ -418,18 +420,27 @@ fn check_derived_typechecks_and_golden( roc_debug_flags::ROC_PRINT_UNIFICATIONS_DERIVED, std::env::set_var(roc_debug_flags::ROC_PRINT_UNIFICATIONS, "1") ); - let (mut solved_subs, _, problems, _) = roc_solve::module::run_solve( - test_module, + + let solve_config = SolveConfig { + home: test_module, + constraints: &constraints, + root_constraint: constr, types, - &constraints, - constr, + pending_derives: Default::default(), + exposed_by_module: &exposed_for_module.exposed_by_module, + derived_module: Default::default(), + }; + + let SolveOutput { + subs: mut solved_subs, + errors: problems, + .. + } = roc_solve::module::run_solve( + solve_config, RigidVariables::default(), test_subs, Default::default(), abilities_store, - Default::default(), - &exposed_for_module.exposed_by_module, - Default::default(), ); dbg_do!( roc_debug_flags::ROC_PRINT_UNIFICATIONS_DERIVED, @@ -465,10 +476,7 @@ fn check_derived_typechecks_and_golden( .render_raw(80, &mut roc_reporting::report::CiWrite::new(&mut buf)) .unwrap(); - panic!( - "Derived does not typecheck:\n{}\nDerived def:\n{}", - buf, derived_program - ); + panic!("Derived does not typecheck:\n{buf}\nDerived def:\n{derived_program}"); } let golden = assemble_derived_golden( diff --git a/crates/compiler/test_gen/benches/list_map.rs b/crates/compiler/test_gen/benches/list_map.rs index 665f6f3cb44..877957322b0 100644 --- a/crates/compiler/test_gen/benches/list_map.rs +++ b/crates/compiler/test_gen/benches/list_map.rs @@ -65,7 +65,7 @@ fn roc_function<'a, 'b>( let (main_fn_name, errors, lib) = helpers::llvm::helper(arena, config, source, arena.alloc(context)); - assert!(errors.is_empty(), "Encountered errors:\n{}", errors); + assert!(errors.is_empty(), "Encountered errors:\n{errors}"); run_roc_dylib!(arena.alloc(lib), main_fn_name, &Input, Output) } diff --git a/crates/compiler/test_gen/benches/quicksort.rs b/crates/compiler/test_gen/benches/quicksort.rs index 6c4dcf910f4..2c02646b492 100644 --- a/crates/compiler/test_gen/benches/quicksort.rs +++ b/crates/compiler/test_gen/benches/quicksort.rs @@ -94,7 +94,7 @@ fn roc_function<'a>( let (main_fn_name, errors, lib) = helpers::llvm::helper(arena, config, source, arena.alloc(context)); - assert!(errors.is_empty(), "Encountered errors:\n{}", errors); + assert!(errors.is_empty(), "Encountered errors:\n{errors}"); run_roc_dylib!(arena.alloc(lib), main_fn_name, *mut Input, Output) } diff --git a/crates/compiler/test_gen/build.rs b/crates/compiler/test_gen/build.rs index a9abb28f13d..2cd8f9cb45b 100644 --- a/crates/compiler/test_gen/build.rs +++ b/crates/compiler/test_gen/build.rs @@ -41,8 +41,8 @@ fn build_wasm_linking_test_host() { let host_wasm: &str = host_wasm_path.to_str().unwrap(); let host_native: &str = host_native_path.to_str().unwrap(); - println!("cargo:rerun-if-changed={}", host_source); - println!("cargo:rerun-if-changed={}", import_source); + println!("cargo:rerun-if-changed={host_source}"); + println!("cargo:rerun-if-changed={import_source}"); if !Path::new("build").exists() { fs::create_dir("build").unwrap(); @@ -57,7 +57,7 @@ fn build_wasm_linking_test_host() { "-target", "wasm32-freestanding-musl", host_source, - &format!("-femit-bin={}", host_wasm), + &format!("-femit-bin={host_wasm}"), ]); let mut import_obj_path = PathBuf::from("build").join("wasm_linking_host_imports"); @@ -73,7 +73,7 @@ fn build_wasm_linking_test_host() { "build-exe", host_source, import_obj, - &format!("-femit-bin={}", host_native), + &format!("-femit-bin={host_native}"), #[cfg(windows)] "--subsystem", #[cfg(windows)] @@ -148,7 +148,7 @@ fn run_zig(args: &[&str]) { let mut zig_cmd = zig(); let full_zig_cmd = zig_cmd.args(args); - println!("{:?}", full_zig_cmd); + println!("{full_zig_cmd:?}"); let zig_cmd_output = full_zig_cmd.output().unwrap(); @@ -164,6 +164,6 @@ fn run_zig(args: &[&str]) { panic!("zig call failed with status {:?}", zig_cmd_output.status); } - assert!(zig_cmd_output.stdout.is_empty(), "{:#?}", zig_cmd_output); - assert!(zig_cmd_output.stderr.is_empty(), "{:#?}", zig_cmd_output); + assert!(zig_cmd_output.stdout.is_empty(), "{zig_cmd_output:#?}"); + assert!(zig_cmd_output.stderr.is_empty(), "{zig_cmd_output:#?}"); } diff --git a/crates/compiler/test_gen/src/gen_definitions.rs b/crates/compiler/test_gen/src/gen_definitions.rs new file mode 100644 index 00000000000..ef575efc480 --- /dev/null +++ b/crates/compiler/test_gen/src/gen_definitions.rs @@ -0,0 +1,47 @@ +#[cfg(feature = "gen-llvm")] +use crate::helpers::llvm::assert_evals_to; + +#[cfg(feature = "gen-dev")] +use crate::helpers::dev::assert_evals_to; + +#[cfg(feature = "gen-wasm")] +use crate::helpers::wasm::assert_evals_to; + +// use crate::helpers::with_larger_debug_stack; +//use crate::assert_wasm_evals_to as assert_evals_to; +#[allow(unused_imports)] +use indoc::indoc; +#[allow(unused_imports)] +use roc_std::{RocList, RocResult, RocStr}; + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] +fn def_closure_in_parens() { + assert_evals_to!( + indoc!( + r#" + id = (\x -> x) + + id 42u32 + "# + ), + 42, + u32 + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] +fn def_closure_in_multiple_parens() { + assert_evals_to!( + indoc!( + r#" + id = (((\x -> x))) + + id 42u32 + "# + ), + 42, + u32 + ); +} diff --git a/crates/compiler/test_gen/src/gen_list.rs b/crates/compiler/test_gen/src/gen_list.rs index 5a29e47d2f1..af7ededf091 100644 --- a/crates/compiler/test_gen/src/gen_list.rs +++ b/crates/compiler/test_gen/src/gen_list.rs @@ -1776,14 +1776,14 @@ fn assert_concat_worked(num_elems1: i64, num_elems2: i64) { let vec2: Vec = (0..num_elems2) .map(|i| 54321 % (i + num_elems1 + num_elems2 + 1)) .collect(); - let slice_str1 = format!("{:?}", vec1); - let slice_str2 = format!("{:?}", vec2); + let slice_str1 = format!("{vec1:?}"); + let slice_str2 = format!("{vec2:?}"); let mut expected = vec1; expected.extend(vec2); assert_evals_to!( - &format!("List.concat {} {}", slice_str1, slice_str2), + &format!("List.concat {slice_str1} {slice_str2}"), RocList::from_slice(&expected), RocList ); diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index 587134965e7..7c51ad5c8f8 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -2211,9 +2211,10 @@ fn nullable_eval_cfold() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn nested_switch() { - // exposed bug with passing the right symbol/layout down into switch branch generation - // This is also the only test_gen test that exercises Reset/Reuse (as of Aug 2022) - assert_evals_to!( + crate::helpers::with_larger_debug_stack(|| + // exposed bug with passing the right symbol/layout down into switch branch generation + // This is also the only test_gen test that exercises Reset/Reuse (as of Aug 2022) + assert_evals_to!( indoc!( r#" app "test" provides [main] to "./platform" @@ -2249,7 +2250,7 @@ fn nested_switch() { ), 12, i64 - ); + )); } #[test] @@ -4499,3 +4500,36 @@ fn pass_lambda_set_to_function() { i64 ); } + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] +fn linked_list_trmc() { + assert_evals_to!( + indoc!( + r#" + app "test" provides [main] to "./platform" + + LinkedList a : [Nil, Cons a (LinkedList a)] + + repeat : a, Nat -> LinkedList a + repeat = \value, n -> + when n is + 0 -> Nil + _ -> Cons value (repeat value (n - 1)) + + length : LinkedList a -> I64 + length = \list -> + when list is + Nil -> 0 + Cons _ rest -> 1 + length rest + + main : I64 + main = + repeat "foo" 5 + |> length + "# + ), + 5, + i64 + ); +} diff --git a/crates/compiler/test_gen/src/helpers/llvm.rs b/crates/compiler/test_gen/src/helpers/llvm.rs index 2ec40d96245..ad1a7bcd0ad 100644 --- a/crates/compiler/test_gen/src/helpers/llvm.rs +++ b/crates/compiler/test_gen/src/helpers/llvm.rs @@ -89,10 +89,10 @@ fn create_llvm_module<'a>( Err(LoadMonomorphizedError::LoadingProblem(roc_load::LoadingProblem::FormattedReport( report, ))) => { - println!("{}", report); + println!("{report}"); panic!(); } - Err(e) => panic!("{:?}", e), + Err(e) => panic!("{e:?}"), }; use roc_load::MonomorphizedModule; @@ -192,7 +192,7 @@ fn create_llvm_module<'a>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let attr = context.create_enum_attribute(kind_id, 1); + let attr = context.create_enum_attribute(kind_id, 0); for function in module.get_functions() { let name = function.get_name().to_str().unwrap(); @@ -283,8 +283,7 @@ fn create_llvm_module<'a>( let path = std::env::temp_dir().join("test.ll"); env.module.print_to_file(&path).unwrap(); panic!( - "Errors defining module:\n\n{}\n\nI have written the full module to `{:?}`", - errors, path + "Errors defining module:\n\n{errors}\n\nI have written the full module to `{path:?}`" ); }; @@ -374,7 +373,7 @@ fn annotate_with_debug_info<'ctx>( ErrorKind::NotFound => panic!( r"I could not find the `debugir` tool on the PATH, install it from https://github.com/vaivaswatha/debugir" ), - _ => panic!("{:?}", error), + _ => panic!("{error:?}"), } } } @@ -495,17 +494,14 @@ fn llvm_module_to_wasm_file( let msg = String::from_utf8_lossy(&output.stderr); if msg.contains("wasm-ld: error: unknown file type") { - panic!( - "{}\nThis can happen if multiple tests have the same input string", - msg - ); + panic!("{msg}\nThis can happen if multiple tests have the same input string"); } else { panic!("{}", msg); } } - assert!(output.status.success(), "{:#?}", output); - assert!(output.stdout.is_empty(), "{:#?}", output); + assert!(output.status.success(), "{output:#?}"); + assert!(output.stdout.is_empty(), "{output:#?}"); test_wasm_path } @@ -570,7 +566,7 @@ pub fn try_run_lib_function( let main: libloading::Symbol)> = lib .get(main_fn_name.as_bytes()) .ok() - .ok_or(format!("Unable to JIT compile `{}`", main_fn_name)) + .ok_or(format!("Unable to JIT compile `{main_fn_name}`")) .expect("errored"); let mut main_result = MaybeUninit::uninit(); @@ -607,7 +603,7 @@ where match result { Ok(raw) => { // only if there are no exceptions thrown, check for errors - assert!(errors.is_empty(), "Encountered errors:\n{}", errors); + assert!(errors.is_empty(), "Encountered errors:\n{errors}"); #[allow(clippy::redundant_closure_call)] let given = transform(raw); @@ -618,8 +614,8 @@ where std::mem::forget(given); } Err((msg, tag)) => match tag { - CrashTag::Roc => panic!(r#"Roc failed with message: "{}""#, msg), - CrashTag::User => panic!(r#"User crash with message: "{}""#, msg), + CrashTag::Roc => panic!(r#"Roc failed with message: "{msg}""#), + CrashTag::User => panic!(r#"User crash with message: "{msg}""#), }, } } diff --git a/crates/compiler/test_gen/src/helpers/mod.rs b/crates/compiler/test_gen/src/helpers/mod.rs index ed49fa04ed5..2d530993013 100644 --- a/crates/compiler/test_gen/src/helpers/mod.rs +++ b/crates/compiler/test_gen/src/helpers/mod.rs @@ -24,7 +24,7 @@ pub(crate) fn src_hash(src: &str) -> u64 { pub(crate) fn save_wasm_file(app_module_bytes: &[u8], build_dir_hash: u64) { use std::path::Path; - let debug_dir_str = format!("/tmp/roc/gen_wasm/{:016x}", build_dir_hash); + let debug_dir_str = format!("/tmp/roc/gen_wasm/{build_dir_hash:016x}"); let debug_dir_path = Path::new(&debug_dir_str); let final_wasm_file = debug_dir_path.join("final.wasm"); diff --git a/crates/compiler/test_gen/src/tests.rs b/crates/compiler/test_gen/src/tests.rs index b7d87f148aa..5ca507478ba 100644 --- a/crates/compiler/test_gen/src/tests.rs +++ b/crates/compiler/test_gen/src/tests.rs @@ -6,6 +6,7 @@ pub mod gen_abilities; pub mod gen_compare; +pub mod gen_definitions; pub mod gen_dict; pub mod gen_list; pub mod gen_num; diff --git a/crates/compiler/test_mono/generated/anonymous_closure_in_polymorphic_expression_issue_4717.txt b/crates/compiler/test_mono/generated/anonymous_closure_in_polymorphic_expression_issue_4717.txt index 42f3862731e..f3eca0d0aa6 100644 --- a/crates/compiler/test_mono/generated/anonymous_closure_in_polymorphic_expression_issue_4717.txt +++ b/crates/compiler/test_mono/generated/anonymous_closure_in_polymorphic_expression_issue_4717.txt @@ -53,7 +53,7 @@ procedure List.72 (#Attr.2, #Attr.3, #Attr.4): let List.527 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; ret List.527; -procedure List.80 (List.571, List.572, List.573, List.574, List.575): +procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): joinpoint List.545 List.439 List.440 List.441 List.442 List.443: let List.547 : Int1 = CallByName Num.22 List.442 List.443; if List.547 then @@ -77,7 +77,7 @@ procedure List.80 (List.571, List.572, List.573, List.574, List.575): let List.546 : [C U64, C U64] = TagId(1) List.440; ret List.546; in - jump List.545 List.571 List.572 List.573 List.574 List.575; + jump List.545 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; procedure List.93 (List.436, List.437, List.438): let List.543 : U64 = 0i64; diff --git a/crates/compiler/test_mono/generated/binary_tree_fbip.txt b/crates/compiler/test_mono/generated/binary_tree_fbip.txt index 84f7f6907c3..d8b5457f1a9 100644 --- a/crates/compiler/test_mono/generated/binary_tree_fbip.txt +++ b/crates/compiler/test_mono/generated/binary_tree_fbip.txt @@ -8,7 +8,7 @@ procedure Test.4 (Test.27): let Test.38 : I64 = CallByName Test.5 Test.27 Test.39 Test.40; ret Test.38; -procedure Test.5 (Test.67, Test.68, Test.69): +procedure Test.5 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2): joinpoint Test.41 Test.29 Test.30 Test.31: let Test.51 : U8 = 0i64; let Test.52 : U8 = GetTagId Test.29; @@ -16,23 +16,22 @@ procedure Test.5 (Test.67, Test.68, Test.69): if Test.53 then let Test.32 : [, C *self *self] = UnionAtIndex (Id 0) (Index 0) Test.29; let Test.33 : [, C *self *self] = UnionAtIndex (Id 0) (Index 1) Test.29; - joinpoint #Derived_gen.0 #Derived_gen.4: - let #Derived_gen.5 : [C [, C *self *self] *self, ] = lowlevel PtrCast #Derived_gen.4; - let Test.43 : [C [, C *self *self] *self, ] = Reuse #Derived_gen.5 UpdateModeId { id: 1 } TagId(1) Test.33 Test.30; + joinpoint #Derived_gen.3 #Derived_gen.6: + let #Derived_gen.7 : [C [, C *self *self] *self, ] = lowlevel PtrCast #Derived_gen.6; + let Test.43 : [C [, C *self *self] *self, ] = Reuse #Derived_gen.7 UpdateModeId { id: 1 } TagId(1) Test.33 Test.30; let Test.45 : I64 = 1i64; let Test.44 : I64 = CallByName Num.19 Test.31 Test.45; jump Test.41 Test.32 Test.43 Test.44; in - let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.29; - if #Derived_gen.1 then - let #Derived_gen.6 : [, C *self *self] = ResetRef { symbol: Test.29, id: UpdateModeId { id: 2 } }; - jump #Derived_gen.0 #Derived_gen.6; + let #Derived_gen.4 : Int1 = lowlevel RefCountIsUnique Test.29; + if #Derived_gen.4 then + jump #Derived_gen.3 Test.29; else inc Test.32; inc Test.33; decref Test.29; - let #Derived_gen.7 : [, C *self *self] = NullPointer; - jump #Derived_gen.0 #Derived_gen.7; + let #Derived_gen.8 : [, C *self *self] = NullPointer; + jump #Derived_gen.3 #Derived_gen.8; else let Test.48 : U8 = 1i64; let Test.49 : U8 = GetTagId Test.30; @@ -40,9 +39,9 @@ procedure Test.5 (Test.67, Test.68, Test.69): if Test.50 then let Test.35 : [, C *self *self] = UnionAtIndex (Id 1) (Index 0) Test.30; let Test.36 : [C [, C *self *self] *self, ] = UnionAtIndex (Id 1) (Index 1) Test.30; - let #Derived_gen.2 : Int1 = lowlevel RefCountIsUnique Test.30; - if #Derived_gen.2 then - decref Test.30; + let #Derived_gen.5 : Int1 = lowlevel RefCountIsUnique Test.30; + if #Derived_gen.5 then + free Test.30; jump Test.41 Test.35 Test.36 Test.31; else inc Test.35; @@ -52,7 +51,7 @@ procedure Test.5 (Test.67, Test.68, Test.69): else ret Test.31; in - jump Test.41 Test.67 Test.68 Test.69; + jump Test.41 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2; procedure Test.0 (): let Test.64 : [, C *self *self] = TagId(1) ; diff --git a/crates/compiler/test_mono/generated/capture_void_layout_task.txt b/crates/compiler/test_mono/generated/capture_void_layout_task.txt new file mode 100644 index 00000000000..156638d67a3 --- /dev/null +++ b/crates/compiler/test_mono/generated/capture_void_layout_task.txt @@ -0,0 +1,174 @@ +procedure List.145 (List.146, List.147, List.144): + let List.540 : [C {}, C *self {{}, []}] = CallByName Test.29 List.146 List.147 List.144; + ret List.540; + +procedure List.18 (List.142, List.143, List.144): + let List.521 : [C {}, C *self {{}, []}] = CallByName List.93 List.142 List.143 List.144; + ret List.521; + +procedure List.6 (#Attr.2): + let List.538 : U64 = lowlevel ListLen #Attr.2; + ret List.538; + +procedure List.66 (#Attr.2, #Attr.3): + let List.537 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.537; + +procedure List.80 (#Derived_gen.6, #Derived_gen.7, #Derived_gen.8, #Derived_gen.9, #Derived_gen.10): + joinpoint List.527 List.439 List.440 List.441 List.442 List.443: + let List.529 : Int1 = CallByName Num.22 List.442 List.443; + if List.529 then + let List.536 : [] = CallByName List.66 List.439 List.442; + let List.530 : [C {}, C *self {{}, []}] = CallByName List.145 List.440 List.536 List.441; + let List.533 : U64 = 1i64; + let List.532 : U64 = CallByName Num.19 List.442 List.533; + jump List.527 List.439 List.530 List.441 List.532 List.443; + else + dec List.439; + ret List.440; + in + jump List.527 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10; + +procedure List.93 (List.436, List.437, List.438): + let List.525 : U64 = 0i64; + let List.526 : U64 = CallByName List.6 List.436; + let List.524 : [C {}, C *self {{}, []}] = CallByName List.80 List.436 List.437 List.438 List.525 List.526; + ret List.524; + +procedure Num.19 (#Attr.2, #Attr.3): + let Num.292 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.292; + +procedure Num.22 (#Attr.2, #Attr.3): + let Num.293 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.293; + +procedure Test.10 (Test.66, #Attr.12): + let Test.9 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12; + let #Derived_gen.20 : Int1 = lowlevel RefCountIsUnique #Attr.12; + if #Derived_gen.20 then + free #Attr.12; + ret Test.9; + else + decref #Attr.12; + ret Test.9; + +procedure Test.10 (Test.66, #Attr.12): + let Test.9 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12; + ret Test.9; + +procedure Test.14 (Test.45, #Attr.12): + let Test.13 : {{}, []} = UnionAtIndex (Id 1) (Index 1) #Attr.12; + let Test.12 : [C {}, C *self {{}, []}] = UnionAtIndex (Id 1) (Index 0) #Attr.12; + joinpoint #Derived_gen.18: + let Test.50 : {} = Struct {}; + let Test.51 : U8 = GetTagId Test.12; + joinpoint Test.52 Test.15: + let Test.16 : [C {}, C []] = CallByName Test.20 Test.15 Test.13; + let Test.48 : {} = Struct {}; + let Test.49 : U8 = GetTagId Test.16; + switch Test.49: + case 0: + let Test.47 : {} = CallByName Test.10 Test.48 Test.16; + ret Test.47; + + default: + let Test.47 : {} = CallByName Test.25 Test.48 Test.16; + ret Test.47; + + in + switch Test.51: + case 0: + let Test.53 : {} = CallByName Test.10 Test.50 Test.12; + jump Test.52 Test.53; + + default: + let Test.53 : {} = CallByName Test.14 Test.50 Test.12; + jump Test.52 Test.53; + + in + let #Derived_gen.19 : Int1 = lowlevel RefCountIsUnique #Attr.12; + if #Derived_gen.19 then + free #Attr.12; + jump #Derived_gen.18; + else + inc Test.12; + decref #Attr.12; + jump #Derived_gen.18; + +procedure Test.20 (Test.21, Test.18): + let Test.23 : [C {}, C []] = CallByName Test.32 Test.21 Test.18; + ret Test.23; + +procedure Test.25 (Test.57, #Attr.12): + let Test.24 : [] = UnionAtIndex (Id 1) (Index 0) #Attr.12; + let Test.60 : Str = "voided tag constructor is unreachable"; + Crash Test.60 + +procedure Test.29 (Test.30, Test.31, Test.28): + let Test.42 : {{}, []} = Struct {Test.28, Test.31}; + let Test.41 : [C {}, C *self {{}, []}] = CallByName Test.5 Test.30 Test.42; + ret Test.41; + +procedure Test.3 (Test.9): + let Test.65 : [C {}, C *self {{}, []}] = TagId(0) Test.9; + ret Test.65; + +procedure Test.3 (Test.9): + let Test.72 : [C {}, C []] = TagId(0) Test.9; + ret Test.72; + +procedure Test.32 (Test.61, #Attr.12): + let Test.31 : [] = StructAtIndex 1 #Attr.12; + let Test.28 : {} = StructAtIndex 0 #Attr.12; + let Test.63 : [C {}, C []] = CallByName Test.33 Test.31; + ret Test.63; + +procedure Test.33 (Test.69): + let Test.71 : {} = Struct {}; + let Test.70 : [C {}, C []] = CallByName Test.3 Test.71; + ret Test.70; + +procedure Test.4 (Test.12, Test.13): + let Test.46 : [C {}, C *self {{}, []}] = TagId(1) Test.12 Test.13; + ret Test.46; + +procedure Test.5 (Test.17, Test.18): + let Test.19 : [C {}, C *self {{}, []}] = CallByName Test.4 Test.17 Test.18; + ret Test.19; + +procedure Test.6 (Test.27, Test.28): + let Test.64 : {} = Struct {}; + let Test.38 : [C {}, C *self {{}, []}] = CallByName Test.3 Test.64; + let Test.37 : [C {}, C *self {{}, []}] = CallByName List.18 Test.27 Test.38 Test.28; + ret Test.37; + +procedure Test.76 (Test.77): + let Test.78 : {{}, []} = UnionAtIndex (Id 0) (Index 0) Test.77; + dec Test.77; + let Test.79 : {} = StructAtIndex 0 Test.78; + ret Test.79; + +procedure Test.80 (Test.81): + let Test.82 : {{}, []} = UnionAtIndex (Id 0) (Index 0) Test.81; + dec Test.81; + let Test.83 : [] = StructAtIndex 1 Test.82; + ret Test.83; + +procedure Test.84 (Test.86, #Attr.12): + let Test.87 : U8 = GetTagId #Attr.12; + switch Test.87: + case 0: + let Test.85 : {} = CallByName Test.10 Test.86 #Attr.12; + ret Test.85; + + default: + let Test.85 : {} = CallByName Test.14 Test.86 #Attr.12; + ret Test.85; + + +procedure Test.0 (): + let Test.35 : List [] = Array []; + let Test.36 : {} = Struct {}; + let Test.34 : [C {}, C *self {{}, []}] = CallByName Test.6 Test.35 Test.36; + ret Test.34; diff --git a/crates/compiler/test_mono/generated/choose_correct_recursion_var_under_record.txt b/crates/compiler/test_mono/generated/choose_correct_recursion_var_under_record.txt index 7a3b68e8d6e..b52e46a91d9 100644 --- a/crates/compiler/test_mono/generated/choose_correct_recursion_var_under_record.txt +++ b/crates/compiler/test_mono/generated/choose_correct_recursion_var_under_record.txt @@ -78,7 +78,7 @@ procedure Test.2 (Test.6): let Test.31 : Int1 = lowlevel Eq Test.29 Test.30; if Test.31 then let Test.7 : [C List *self, C *self] = UnionAtIndex (Id 1) (Index 0) Test.6; - joinpoint #Derived_gen.0: + joinpoint #Derived_gen.1: let Test.8 : Str = CallByName Test.2 Test.7; let Test.18 : Int1 = CallByName Bool.1; if Test.18 then @@ -88,17 +88,17 @@ procedure Test.2 (Test.6): let Test.17 : Str = "foo"; ret Test.17; in - let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.6; - if #Derived_gen.1 then - decref Test.6; - jump #Derived_gen.0; + let #Derived_gen.2 : Int1 = lowlevel RefCountIsUnique Test.6; + if #Derived_gen.2 then + free Test.6; + jump #Derived_gen.1; else inc Test.7; decref Test.6; - jump #Derived_gen.0; + jump #Derived_gen.1; else let Test.9 : List [C List [C List *self, C *self], C [C List *self, C *self]] = UnionAtIndex (Id 0) (Index 0) Test.6; - joinpoint #Derived_gen.2: + joinpoint #Derived_gen.3: let Test.24 : {} = Struct {}; let Test.23 : List Str = CallByName List.5 Test.9 Test.24; let Test.21 : [C {}, C Str] = CallByName List.9 Test.23; @@ -106,14 +106,14 @@ procedure Test.2 (Test.6): let Test.20 : Str = CallByName Result.5 Test.21 Test.22; ret Test.20; in - let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique Test.6; - if #Derived_gen.3 then - decref Test.6; - jump #Derived_gen.2; + let #Derived_gen.4 : Int1 = lowlevel RefCountIsUnique Test.6; + if #Derived_gen.4 then + free Test.6; + jump #Derived_gen.3; else inc Test.9; decref Test.6; - jump #Derived_gen.2; + jump #Derived_gen.3; procedure Test.0 (): let Test.32 : List [C List [C List *self, C *self], C [C List *self, C *self]] = Array []; diff --git a/crates/compiler/test_mono/generated/compose_recursive_lambda_set_productive_nullable_wrapped.txt b/crates/compiler/test_mono/generated/compose_recursive_lambda_set_productive_nullable_wrapped.txt index 39dc4fc550a..6d9e4c3688d 100644 --- a/crates/compiler/test_mono/generated/compose_recursive_lambda_set_productive_nullable_wrapped.txt +++ b/crates/compiler/test_mono/generated/compose_recursive_lambda_set_productive_nullable_wrapped.txt @@ -18,7 +18,7 @@ procedure List.66 (#Attr.2, #Attr.3): let List.537 : Int1 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; ret List.537; -procedure List.80 (List.544, List.545, List.546, List.547, List.548): +procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): joinpoint List.527 List.439 List.440 List.441 List.442 List.443: let List.529 : Int1 = CallByName Num.22 List.442 List.443; if List.529 then @@ -31,7 +31,7 @@ procedure List.80 (List.544, List.545, List.546, List.547, List.548): dec List.439; ret List.440; in - jump List.527 List.544 List.545 List.546 List.547 List.548; + jump List.527 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; procedure List.93 (List.436, List.437, List.438): let List.525 : U64 = 0i64; @@ -54,11 +54,11 @@ procedure Str.3 (#Attr.2, #Attr.3): procedure Test.1 (Test.5): ret Test.5; -procedure Test.11 (Test.53, Test.54): +procedure Test.11 (#Derived_gen.7, #Derived_gen.8): joinpoint Test.27 Test.12 #Attr.12: let Test.8 : Int1 = UnionAtIndex (Id 2) (Index 1) #Attr.12; let Test.7 : [, C *self Int1, C *self Int1] = UnionAtIndex (Id 2) (Index 0) #Attr.12; - joinpoint #Derived_gen.2: + joinpoint #Derived_gen.14: joinpoint Test.31 Test.29: let Test.30 : U8 = GetTagId Test.7; switch Test.30: @@ -85,16 +85,16 @@ procedure Test.11 (Test.53, Test.54): jump Test.31 Test.32; in - let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique #Attr.12; - if #Derived_gen.3 then - decref #Attr.12; - jump #Derived_gen.2; + let #Derived_gen.15 : Int1 = lowlevel RefCountIsUnique #Attr.12; + if #Derived_gen.15 then + free #Attr.12; + jump #Derived_gen.14; else inc Test.7; decref #Attr.12; - jump #Derived_gen.2; + jump #Derived_gen.14; in - jump Test.27 Test.53 Test.54; + jump Test.27 #Derived_gen.7 #Derived_gen.8; procedure Test.2 (Test.13): ret Test.13; @@ -125,7 +125,7 @@ procedure Test.6 (Test.7, Test.8, Test.5): procedure Test.9 (Test.10, #Attr.12): let Test.8 : Int1 = UnionAtIndex (Id 1) (Index 1) #Attr.12; let Test.7 : [, C *self Int1, C *self Int1] = UnionAtIndex (Id 1) (Index 0) #Attr.12; - joinpoint #Derived_gen.0: + joinpoint #Derived_gen.12: let Test.37 : U8 = GetTagId Test.7; joinpoint Test.38 Test.36: switch Test.8: @@ -153,14 +153,14 @@ procedure Test.9 (Test.10, #Attr.12): jump Test.38 Test.39; in - let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique #Attr.12; - if #Derived_gen.1 then - decref #Attr.12; - jump #Derived_gen.0; + let #Derived_gen.13 : Int1 = lowlevel RefCountIsUnique #Attr.12; + if #Derived_gen.13 then + free #Attr.12; + jump #Derived_gen.12; else inc Test.7; decref #Attr.12; - jump #Derived_gen.0; + jump #Derived_gen.12; procedure Test.0 (): let Test.41 : Int1 = false; diff --git a/crates/compiler/test_mono/generated/dict.txt b/crates/compiler/test_mono/generated/dict.txt index 74e203e84a9..1cb0f5bc2b1 100644 --- a/crates/compiler/test_mono/generated/dict.txt +++ b/crates/compiler/test_mono/generated/dict.txt @@ -1,28 +1,28 @@ -procedure Dict.1 (Dict.537): - let Dict.546 : List {[], []} = Array []; - let Dict.553 : U64 = 0i64; - let Dict.554 : U64 = 8i64; - let Dict.547 : List U64 = CallByName List.11 Dict.553 Dict.554; - let Dict.550 : I8 = CallByName Dict.36; - let Dict.551 : U64 = 8i64; - let Dict.548 : List I8 = CallByName List.11 Dict.550 Dict.551; - let Dict.549 : U64 = 0i64; - let Dict.545 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.546, Dict.547, Dict.548, Dict.549}; - ret Dict.545; +procedure Dict.1 (Dict.556): + let Dict.565 : List {[], []} = Array []; + let Dict.572 : U64 = 0i64; + let Dict.573 : U64 = 8i64; + let Dict.566 : List U64 = CallByName List.11 Dict.572 Dict.573; + let Dict.569 : I8 = CallByName Dict.39; + let Dict.570 : U64 = 8i64; + let Dict.567 : List I8 = CallByName List.11 Dict.569 Dict.570; + let Dict.568 : U64 = 0i64; + let Dict.564 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.565, Dict.566, Dict.567, Dict.568}; + ret Dict.564; -procedure Dict.36 (): - let Dict.552 : I8 = -128i64; - ret Dict.552; +procedure Dict.39 (): + let Dict.571 : I8 = -128i64; + ret Dict.571; -procedure Dict.4 (Dict.543): - let Dict.97 : U64 = StructAtIndex 3 Dict.543; - let #Derived_gen.2 : List {[], []} = StructAtIndex 0 Dict.543; - dec #Derived_gen.2; - let #Derived_gen.1 : List U64 = StructAtIndex 1 Dict.543; - dec #Derived_gen.1; - let #Derived_gen.0 : List I8 = StructAtIndex 2 Dict.543; - dec #Derived_gen.0; - ret Dict.97; +procedure Dict.4 (Dict.562): + let Dict.100 : U64 = StructAtIndex 3 Dict.562; + let #Derived_gen.8 : List {[], []} = StructAtIndex 0 Dict.562; + dec #Derived_gen.8; + let #Derived_gen.7 : List U64 = StructAtIndex 1 Dict.562; + dec #Derived_gen.7; + let #Derived_gen.6 : List I8 = StructAtIndex 2 Dict.562; + dec #Derived_gen.6; + ret Dict.100; procedure List.11 (List.121, List.122): let List.522 : List I8 = CallByName List.68 List.122; @@ -50,21 +50,7 @@ procedure List.71 (#Attr.2, #Attr.3): let List.541 : List U64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; ret List.541; -procedure List.83 (List.545, List.546, List.547): - joinpoint List.523 List.123 List.124 List.125: - let List.531 : U64 = 0i64; - let List.525 : Int1 = CallByName Num.24 List.124 List.531; - if List.525 then - let List.530 : U64 = 1i64; - let List.527 : U64 = CallByName Num.20 List.124 List.530; - let List.528 : List I8 = CallByName List.71 List.125 List.123; - jump List.523 List.123 List.527 List.528; - else - ret List.125; - in - jump List.523 List.545 List.546 List.547; - -procedure List.83 (List.553, List.554, List.555): +procedure List.83 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2): joinpoint List.535 List.123 List.124 List.125: let List.543 : U64 = 0i64; let List.537 : Int1 = CallByName Num.24 List.124 List.543; @@ -76,7 +62,21 @@ procedure List.83 (List.553, List.554, List.555): else ret List.125; in - jump List.535 List.553 List.554 List.555; + jump List.535 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2; + +procedure List.83 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5): + joinpoint List.523 List.123 List.124 List.125: + let List.531 : U64 = 0i64; + let List.525 : Int1 = CallByName Num.24 List.124 List.531; + if List.525 then + let List.530 : U64 = 1i64; + let List.527 : U64 = CallByName Num.20 List.124 List.530; + let List.528 : List I8 = CallByName List.71 List.125 List.123; + jump List.523 List.123 List.527 List.528; + else + ret List.125; + in + jump List.523 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5; procedure Num.20 (#Attr.2, #Attr.3): let Num.293 : U64 = lowlevel NumSub #Attr.2 #Attr.3; 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 index 9f28113a0c3..9aa6441298a 100644 --- a/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt @@ -12,17 +12,17 @@ procedure #Derived.2 (#Derived.3, #Derived.4, #Derived.1): ret #Derived_gen.3; procedure #Derived.5 (#Derived.6): - let #Derived_gen.14 : Str = CallByName Encode.23 #Derived.6; - ret #Derived_gen.14; + let #Derived_gen.10 : Str = CallByName Encode.23 #Derived.6; + ret #Derived_gen.10; procedure #Derived.7 (#Derived.8, #Derived.9, #Derived.6): - let #Derived_gen.21 : Str = "b"; - let #Derived_gen.22 : Str = CallByName TotallyNotJson.25 #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 TotallyNotJson.29 #Derived_gen.19; - let #Derived_gen.17 : List U8 = CallByName Encode.24 #Derived.8 #Derived_gen.18 #Derived.9; - ret #Derived_gen.17; + let #Derived_gen.17 : Str = "b"; + let #Derived_gen.18 : Str = CallByName TotallyNotJson.25 #Derived.6; + let #Derived_gen.16 : {Str, Str} = Struct {#Derived_gen.17, #Derived_gen.18}; + let #Derived_gen.15 : List {Str, Str} = Array [#Derived_gen.16]; + let #Derived_gen.14 : List {Str, Str} = CallByName TotallyNotJson.29 #Derived_gen.15; + let #Derived_gen.13 : List U8 = CallByName Encode.24 #Derived.8 #Derived_gen.14 #Derived.9; + ret #Derived_gen.13; procedure Bool.1 (): let Bool.76 : Int1 = false; @@ -56,20 +56,20 @@ procedure Encode.24 (Encode.99, Encode.107, Encode.101): ret Encode.111; procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.118 : List U8 = CallByName TotallyNotJson.234 Encode.99 Encode.101 Encode.107; - ret Encode.118; + let Encode.113 : List U8 = CallByName TotallyNotJson.234 Encode.99 Encode.101 Encode.107; + ret Encode.113; procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.120 : List U8 = CallByName #Derived.7 Encode.99 Encode.101 Encode.107; - ret Encode.120; + let Encode.115 : List U8 = CallByName #Derived.7 Encode.99 Encode.101 Encode.107; + ret Encode.115; procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.130 : List U8 = CallByName TotallyNotJson.234 Encode.99 Encode.101 Encode.107; - ret Encode.130; + let Encode.117 : List U8 = CallByName TotallyNotJson.234 Encode.99 Encode.101 Encode.107; + ret Encode.117; procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.133 : List U8 = CallByName TotallyNotJson.182 Encode.99 Encode.101 Encode.107; - ret Encode.133; + let Encode.120 : List U8 = CallByName TotallyNotJson.182 Encode.99 Encode.101 Encode.107; + ret Encode.120; procedure Encode.26 (Encode.105, Encode.106): let Encode.109 : List U8 = Array []; @@ -78,303 +78,303 @@ procedure Encode.26 (Encode.105, Encode.106): ret Encode.108; procedure List.13 (#Attr.2, #Attr.3): - let List.730 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; - ret List.730; + let List.689 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; + ret List.689; procedure List.145 (List.146, List.147, List.144): - let List.570 : {List U8, U64} = CallByName TotallyNotJson.237 List.146 List.147 List.144; - ret List.570; + let List.569 : {List U8, U64} = CallByName TotallyNotJson.237 List.146 List.147 List.144; + ret List.569; procedure List.145 (List.146, List.147, List.144): - let List.678 : {List U8, U64} = CallByName TotallyNotJson.237 List.146 List.147 List.144; - ret List.678; + let List.637 : {List U8, U64} = CallByName TotallyNotJson.237 List.146 List.147 List.144; + ret List.637; procedure List.145 (List.146, List.147, List.144): - let List.698 : List U8 = CallByName TotallyNotJson.215 List.146 List.147; - ret List.698; + let List.657 : List U8 = CallByName TotallyNotJson.215 List.146 List.147; + ret List.657; procedure List.18 (List.142, List.143, List.144): - let List.551 : {List U8, U64} = CallByName List.93 List.142 List.143 List.144; - ret List.551; + let List.550 : {List U8, U64} = CallByName List.93 List.142 List.143 List.144; + ret List.550; procedure List.18 (List.142, List.143, List.144): - let List.659 : {List U8, U64} = CallByName List.93 List.142 List.143 List.144; - ret List.659; + let List.618 : {List U8, U64} = CallByName List.93 List.142 List.143 List.144; + ret List.618; procedure List.18 (List.142, List.143, List.144): - let List.679 : List U8 = CallByName List.93 List.142 List.143 List.144; - ret List.679; + let List.638 : List U8 = CallByName List.93 List.142 List.143 List.144; + ret List.638; procedure List.26 (List.159, List.160, List.161): - let List.747 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.93 List.159 List.160 List.161; - let List.750 : U8 = 1i64; - let List.751 : U8 = GetTagId List.747; - let List.752 : Int1 = lowlevel Eq List.750 List.751; - if List.752 then - let List.162 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.747; + let List.706 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.93 List.159 List.160 List.161; + let List.709 : U8 = 1i64; + let List.710 : U8 = GetTagId List.706; + let List.711 : Int1 = lowlevel Eq List.709 List.710; + if List.711 then + let List.162 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.706; ret List.162; else - let List.163 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.747; + let List.163 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.706; ret List.163; procedure List.31 (#Attr.2, #Attr.3): - let List.712 : List Str = lowlevel ListDropAt #Attr.2 #Attr.3; - ret List.712; + let List.671 : List Str = lowlevel ListDropAt #Attr.2 #Attr.3; + ret List.671; procedure List.38 (List.298): - let List.720 : U64 = 0i64; - let List.719 : List Str = CallByName List.31 List.298 List.720; - ret List.719; + let List.679 : U64 = 0i64; + let List.678 : List Str = CallByName List.31 List.298 List.679; + ret List.678; procedure List.4 (List.113, List.114): - let List.655 : U64 = 1i64; - let List.654 : List Str = CallByName List.70 List.113 List.655; - let List.653 : List Str = CallByName List.71 List.654 List.114; - ret List.653; + let List.614 : U64 = 1i64; + let List.613 : List Str = CallByName List.70 List.113 List.614; + let List.612 : List Str = CallByName List.71 List.613 List.114; + ret List.612; procedure List.4 (List.113, List.114): - let List.658 : U64 = 1i64; - let List.657 : List U8 = CallByName List.70 List.113 List.658; - let List.656 : List U8 = CallByName List.71 List.657 List.114; - ret List.656; + let List.617 : U64 = 1i64; + let List.616 : List U8 = CallByName List.70 List.113 List.617; + let List.615 : List U8 = CallByName List.71 List.616 List.114; + ret List.615; procedure List.49 (List.376, List.377): - let List.739 : U64 = StructAtIndex 0 List.377; - let List.740 : U64 = 0i64; - let List.737 : Int1 = CallByName Bool.11 List.739 List.740; - if List.737 then + let List.698 : U64 = StructAtIndex 0 List.377; + let List.699 : U64 = 0i64; + let List.696 : Int1 = CallByName Bool.11 List.698 List.699; + if List.696 then dec List.376; - let List.738 : List U8 = Array []; - ret List.738; + let List.697 : List U8 = Array []; + ret List.697; else - let List.734 : U64 = StructAtIndex 1 List.377; - let List.735 : U64 = StructAtIndex 0 List.377; - let List.733 : List U8 = CallByName List.72 List.376 List.734 List.735; - ret List.733; + let List.693 : U64 = StructAtIndex 1 List.377; + let List.694 : U64 = StructAtIndex 0 List.377; + let List.692 : List U8 = CallByName List.72 List.376 List.693 List.694; + ret List.692; procedure List.52 (List.391, List.392): let List.393 : U64 = CallByName List.6 List.391; - joinpoint List.745 List.394: - let List.743 : U64 = 0i64; - let List.742 : {U64, U64} = Struct {List.394, List.743}; + joinpoint List.704 List.394: + let List.702 : U64 = 0i64; + let List.701 : {U64, U64} = Struct {List.394, List.702}; inc List.391; - let List.395 : List U8 = CallByName List.49 List.391 List.742; - let List.741 : U64 = CallByName Num.20 List.393 List.394; - let List.732 : {U64, U64} = Struct {List.741, List.394}; - let List.396 : List U8 = CallByName List.49 List.391 List.732; - let List.731 : {List U8, List U8} = Struct {List.395, List.396}; - ret List.731; + let List.395 : List U8 = CallByName List.49 List.391 List.701; + let List.700 : U64 = CallByName Num.20 List.393 List.394; + let List.691 : {U64, U64} = Struct {List.700, List.394}; + let List.396 : List U8 = CallByName List.49 List.391 List.691; + let List.690 : {List U8, List U8} = Struct {List.395, List.396}; + ret List.690; in - let List.746 : Int1 = CallByName Num.24 List.393 List.392; - if List.746 then - jump List.745 List.392; + let List.705 : Int1 = CallByName Num.24 List.393 List.392; + if List.705 then + jump List.704 List.392; else - jump List.745 List.393; + jump List.704 List.393; procedure List.6 (#Attr.2): - let List.589 : U64 = lowlevel ListLen #Attr.2; - ret List.589; + let List.588 : U64 = lowlevel ListLen #Attr.2; + ret List.588; procedure List.6 (#Attr.2): - let List.726 : U64 = lowlevel ListLen #Attr.2; - ret List.726; + let List.685 : U64 = lowlevel ListLen #Attr.2; + ret List.685; procedure List.6 (#Attr.2): - let List.727 : U64 = lowlevel ListLen #Attr.2; - ret List.727; + let List.686 : U64 = lowlevel ListLen #Attr.2; + ret List.686; procedure List.6 (#Attr.2): - let List.729 : U64 = lowlevel ListLen #Attr.2; - ret List.729; + let List.688 : U64 = lowlevel ListLen #Attr.2; + ret List.688; procedure List.66 (#Attr.2, #Attr.3): - let List.567 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.567; + let List.566 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.566; procedure List.66 (#Attr.2, #Attr.3): - let List.675 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.675; + let List.634 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.634; procedure List.66 (#Attr.2, #Attr.3): - let List.695 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.695; + let List.654 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.654; procedure List.68 (#Attr.2): - let List.722 : List Str = lowlevel ListWithCapacity #Attr.2; - ret List.722; + let List.681 : List Str = lowlevel ListWithCapacity #Attr.2; + ret List.681; procedure List.68 (#Attr.2): - let List.724 : List U8 = lowlevel ListWithCapacity #Attr.2; - ret List.724; + let List.683 : List U8 = lowlevel ListWithCapacity #Attr.2; + ret List.683; procedure List.70 (#Attr.2, #Attr.3): - let List.635 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.635; + let List.594 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.594; procedure List.70 (#Attr.2, #Attr.3): - let List.652 : List Str = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.652; + let List.611 : List Str = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.611; procedure List.71 (#Attr.2, #Attr.3): - let List.633 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.633; + let List.592 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.592; procedure List.71 (#Attr.2, #Attr.3): - let List.650 : List Str = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.650; + let List.609 : List Str = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.609; procedure List.72 (#Attr.2, #Attr.3, #Attr.4): - let List.736 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; - ret List.736; + let List.695 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; + ret List.695; procedure List.8 (#Attr.2, #Attr.3): - let List.701 : List Str = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.701; + let List.660 : List Str = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.660; procedure List.8 (#Attr.2, #Attr.3): - let List.709 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.709; - -procedure List.80 (List.606, List.607, List.608, List.609, List.610): - joinpoint List.557 List.439 List.440 List.441 List.442 List.443: - let List.559 : Int1 = CallByName Num.22 List.442 List.443; - if List.559 then - let List.566 : {Str, Str} = CallByName List.66 List.439 List.442; - inc List.566; - let List.560 : {List U8, U64} = CallByName List.145 List.440 List.566 List.441; - let List.563 : U64 = 1i64; - let List.562 : U64 = CallByName Num.19 List.442 List.563; - jump List.557 List.439 List.560 List.441 List.562 List.443; + let List.668 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.668; + +procedure List.80 (#Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27): + joinpoint List.715 List.439 List.440 List.441 List.442 List.443: + let List.717 : Int1 = CallByName Num.22 List.442 List.443; + if List.717 then + let List.726 : U8 = CallByName List.66 List.439 List.442; + let List.718 : [C {U64, Int1}, C {U64, Int1}] = CallByName TotallyNotJson.189 List.440 List.726; + let List.723 : U8 = 1i64; + let List.724 : U8 = GetTagId List.718; + let List.725 : Int1 = lowlevel Eq List.723 List.724; + if List.725 then + let List.444 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.718; + let List.721 : U64 = 1i64; + let List.720 : U64 = CallByName Num.19 List.442 List.721; + jump List.715 List.439 List.444 List.441 List.720 List.443; + else + dec List.439; + let List.445 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.718; + let List.722 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) List.445; + ret List.722; else dec List.439; - ret List.440; + let List.716 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440; + ret List.716; in - jump List.557 List.606 List.607 List.608 List.609 List.610; - -procedure List.80 (List.783, List.784, List.785, List.786, List.787): - joinpoint List.665 List.439 List.440 List.441 List.442 List.443: - let List.667 : Int1 = CallByName Num.22 List.442 List.443; - if List.667 then - let List.674 : {Str, Str} = CallByName List.66 List.439 List.442; - inc List.674; - let List.668 : {List U8, U64} = CallByName List.145 List.440 List.674 List.441; - let List.671 : U64 = 1i64; - let List.670 : U64 = CallByName Num.19 List.442 List.671; - jump List.665 List.439 List.668 List.441 List.670 List.443; + jump List.715 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27; + +procedure List.80 (#Derived_gen.31, #Derived_gen.32, #Derived_gen.33, #Derived_gen.34, #Derived_gen.35): + joinpoint List.644 List.439 List.440 List.441 List.442 List.443: + let List.646 : Int1 = CallByName Num.22 List.442 List.443; + if List.646 then + let List.653 : U8 = CallByName List.66 List.439 List.442; + let List.647 : List U8 = CallByName List.145 List.440 List.653 List.441; + let List.650 : U64 = 1i64; + let List.649 : U64 = CallByName Num.19 List.442 List.650; + jump List.644 List.439 List.647 List.441 List.649 List.443; else dec List.439; ret List.440; in - jump List.665 List.783 List.784 List.785 List.786 List.787; - -procedure List.80 (List.800, List.801, List.802, List.803, List.804): - joinpoint List.685 List.439 List.440 List.441 List.442 List.443: - let List.687 : Int1 = CallByName Num.22 List.442 List.443; - if List.687 then - let List.694 : U8 = CallByName List.66 List.439 List.442; - let List.688 : List U8 = CallByName List.145 List.440 List.694 List.441; - let List.691 : U64 = 1i64; - let List.690 : U64 = CallByName Num.19 List.442 List.691; - jump List.685 List.439 List.688 List.441 List.690 List.443; + jump List.644 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33 #Derived_gen.34 #Derived_gen.35; + +procedure List.80 (#Derived_gen.36, #Derived_gen.37, #Derived_gen.38, #Derived_gen.39, #Derived_gen.40): + joinpoint List.624 List.439 List.440 List.441 List.442 List.443: + let List.626 : Int1 = CallByName Num.22 List.442 List.443; + if List.626 then + let List.633 : {Str, Str} = CallByName List.66 List.439 List.442; + inc List.633; + let List.627 : {List U8, U64} = CallByName List.145 List.440 List.633 List.441; + let List.630 : U64 = 1i64; + let List.629 : U64 = CallByName Num.19 List.442 List.630; + jump List.624 List.439 List.627 List.441 List.629 List.443; else dec List.439; ret List.440; in - jump List.685 List.800 List.801 List.802 List.803 List.804; - -procedure List.80 (List.836, List.837, List.838, List.839, List.840): - joinpoint List.756 List.439 List.440 List.441 List.442 List.443: - let List.758 : Int1 = CallByName Num.22 List.442 List.443; - if List.758 then - let List.767 : U8 = CallByName List.66 List.439 List.442; - let List.759 : [C {U64, Int1}, C {U64, Int1}] = CallByName TotallyNotJson.189 List.440 List.767; - let List.764 : U8 = 1i64; - let List.765 : U8 = GetTagId List.759; - let List.766 : Int1 = lowlevel Eq List.764 List.765; - if List.766 then - let List.444 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.759; - let List.762 : U64 = 1i64; - let List.761 : U64 = CallByName Num.19 List.442 List.762; - jump List.756 List.439 List.444 List.441 List.761 List.443; - else - dec List.439; - let List.445 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.759; - let List.763 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) List.445; - ret List.763; + jump List.624 #Derived_gen.36 #Derived_gen.37 #Derived_gen.38 #Derived_gen.39 #Derived_gen.40; + +procedure List.80 (#Derived_gen.42, #Derived_gen.43, #Derived_gen.44, #Derived_gen.45, #Derived_gen.46): + joinpoint List.556 List.439 List.440 List.441 List.442 List.443: + let List.558 : Int1 = CallByName Num.22 List.442 List.443; + if List.558 then + let List.565 : {Str, Str} = CallByName List.66 List.439 List.442; + inc List.565; + let List.559 : {List U8, U64} = CallByName List.145 List.440 List.565 List.441; + let List.562 : U64 = 1i64; + let List.561 : U64 = CallByName Num.19 List.442 List.562; + jump List.556 List.439 List.559 List.441 List.561 List.443; else dec List.439; - let List.757 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440; - ret List.757; + ret List.440; in - jump List.756 List.836 List.837 List.838 List.839 List.840; + jump List.556 #Derived_gen.42 #Derived_gen.43 #Derived_gen.44 #Derived_gen.45 #Derived_gen.46; procedure List.93 (List.436, List.437, List.438): - let List.555 : U64 = 0i64; - let List.556 : U64 = CallByName List.6 List.436; - let List.554 : {List U8, U64} = CallByName List.80 List.436 List.437 List.438 List.555 List.556; - ret List.554; + let List.554 : U64 = 0i64; + let List.555 : U64 = CallByName List.6 List.436; + let List.553 : {List U8, U64} = CallByName List.80 List.436 List.437 List.438 List.554 List.555; + ret List.553; procedure List.93 (List.436, List.437, List.438): - let List.663 : U64 = 0i64; - let List.664 : U64 = CallByName List.6 List.436; - let List.662 : {List U8, U64} = CallByName List.80 List.436 List.437 List.438 List.663 List.664; - ret List.662; + let List.622 : U64 = 0i64; + let List.623 : U64 = CallByName List.6 List.436; + let List.621 : {List U8, U64} = CallByName List.80 List.436 List.437 List.438 List.622 List.623; + ret List.621; procedure List.93 (List.436, List.437, List.438): - let List.683 : U64 = 0i64; - let List.684 : U64 = CallByName List.6 List.436; - let List.682 : List U8 = CallByName List.80 List.436 List.437 List.438 List.683 List.684; - ret List.682; + let List.642 : U64 = 0i64; + let List.643 : U64 = CallByName List.6 List.436; + let List.641 : List U8 = CallByName List.80 List.436 List.437 List.438 List.642 List.643; + ret List.641; procedure List.93 (List.436, List.437, List.438): - let List.754 : U64 = 0i64; - let List.755 : U64 = CallByName List.6 List.436; - let List.753 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.80 List.436 List.437 List.438 List.754 List.755; - ret List.753; + let List.713 : U64 = 0i64; + let List.714 : U64 = CallByName List.6 List.436; + let List.712 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.80 List.436 List.437 List.438 List.713 List.714; + ret List.712; procedure Num.127 (#Attr.2): - let Num.316 : U8 = lowlevel NumIntCast #Attr.2; - ret Num.316; + let Num.307 : U8 = lowlevel NumIntCast #Attr.2; + ret Num.307; procedure Num.19 (#Attr.2, #Attr.3): - let Num.325 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.325; + let Num.316 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.316; procedure Num.20 (#Attr.2, #Attr.3): - let Num.329 : U64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.329; + let Num.320 : U64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.320; procedure Num.21 (#Attr.2, #Attr.3): - let Num.322 : U64 = lowlevel NumMul #Attr.2 #Attr.3; - ret Num.322; + let Num.313 : U64 = lowlevel NumMul #Attr.2 #Attr.3; + ret Num.313; procedure Num.22 (#Attr.2, #Attr.3): - let Num.328 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.328; + let Num.319 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.319; procedure Num.24 (#Attr.2, #Attr.3): - let Num.330 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; - ret Num.330; + let Num.321 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; + ret Num.321; procedure Num.94 (#Attr.2, #Attr.3): - let Num.321 : U64 = lowlevel NumDivCeilUnchecked #Attr.2 #Attr.3; - ret Num.321; + let Num.312 : U64 = lowlevel NumDivCeilUnchecked #Attr.2 #Attr.3; + ret Num.312; procedure Str.12 (#Attr.2): - let Str.324 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.324; + let Str.316 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.316; procedure Str.4 (#Attr.2, #Attr.3): - let Str.327 : Str = lowlevel StrJoinWith #Attr.2 #Attr.3; - ret Str.327; + let Str.319 : Str = lowlevel StrJoinWith #Attr.2 #Attr.3; + ret Str.319; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): let Str.307 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; ret Str.307; procedure Str.55 (#Attr.2): - let Str.330 : List Str = lowlevel StrGraphemes #Attr.2; - ret Str.330; + let Str.322 : List Str = lowlevel StrGraphemes #Attr.2; + ret Str.322; procedure Str.9 (Str.79): let Str.305 : U64 = 0i64; @@ -388,709 +388,709 @@ procedure Str.9 (Str.79): else let Str.300 : U8 = StructAtIndex 3 Str.80; let Str.301 : U64 = StructAtIndex 0 Str.80; - let #Derived_gen.29 : Str = StructAtIndex 1 Str.80; - dec #Derived_gen.29; + let #Derived_gen.58 : Str = StructAtIndex 1 Str.80; + dec #Derived_gen.58; let Str.299 : {U64, U8} = Struct {Str.301, Str.300}; let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299; ret Str.298; procedure TotallyNotJson.100 (TotallyNotJson.850): - let TotallyNotJson.1853 : Str = "a"; - let TotallyNotJson.1854 : Int1 = lowlevel Eq TotallyNotJson.1853 TotallyNotJson.850; - dec TotallyNotJson.1853; - if TotallyNotJson.1854 then + let TotallyNotJson.1830 : Str = "a"; + let TotallyNotJson.1831 : Int1 = lowlevel Eq TotallyNotJson.1830 TotallyNotJson.850; + dec TotallyNotJson.1830; + if TotallyNotJson.1831 then dec TotallyNotJson.850; - let TotallyNotJson.1776 : Str = "A"; - ret TotallyNotJson.1776; + let TotallyNotJson.1753 : Str = "A"; + ret TotallyNotJson.1753; else - let TotallyNotJson.1851 : Str = "b"; - let TotallyNotJson.1852 : Int1 = lowlevel Eq TotallyNotJson.1851 TotallyNotJson.850; - dec TotallyNotJson.1851; - if TotallyNotJson.1852 then + let TotallyNotJson.1828 : Str = "b"; + let TotallyNotJson.1829 : Int1 = lowlevel Eq TotallyNotJson.1828 TotallyNotJson.850; + dec TotallyNotJson.1828; + if TotallyNotJson.1829 then dec TotallyNotJson.850; - let TotallyNotJson.1777 : Str = "B"; - ret TotallyNotJson.1777; + let TotallyNotJson.1754 : Str = "B"; + ret TotallyNotJson.1754; else - let TotallyNotJson.1849 : Str = "c"; - let TotallyNotJson.1850 : Int1 = lowlevel Eq TotallyNotJson.1849 TotallyNotJson.850; - dec TotallyNotJson.1849; - if TotallyNotJson.1850 then + let TotallyNotJson.1826 : Str = "c"; + let TotallyNotJson.1827 : Int1 = lowlevel Eq TotallyNotJson.1826 TotallyNotJson.850; + dec TotallyNotJson.1826; + if TotallyNotJson.1827 then dec TotallyNotJson.850; - let TotallyNotJson.1778 : Str = "C"; - ret TotallyNotJson.1778; + let TotallyNotJson.1755 : Str = "C"; + ret TotallyNotJson.1755; else - let TotallyNotJson.1847 : Str = "d"; - let TotallyNotJson.1848 : Int1 = lowlevel Eq TotallyNotJson.1847 TotallyNotJson.850; - dec TotallyNotJson.1847; - if TotallyNotJson.1848 then + let TotallyNotJson.1824 : Str = "d"; + let TotallyNotJson.1825 : Int1 = lowlevel Eq TotallyNotJson.1824 TotallyNotJson.850; + dec TotallyNotJson.1824; + if TotallyNotJson.1825 then dec TotallyNotJson.850; - let TotallyNotJson.1779 : Str = "D"; - ret TotallyNotJson.1779; + let TotallyNotJson.1756 : Str = "D"; + ret TotallyNotJson.1756; else - let TotallyNotJson.1845 : Str = "e"; - let TotallyNotJson.1846 : Int1 = lowlevel Eq TotallyNotJson.1845 TotallyNotJson.850; - dec TotallyNotJson.1845; - if TotallyNotJson.1846 then + let TotallyNotJson.1822 : Str = "e"; + let TotallyNotJson.1823 : Int1 = lowlevel Eq TotallyNotJson.1822 TotallyNotJson.850; + dec TotallyNotJson.1822; + if TotallyNotJson.1823 then dec TotallyNotJson.850; - let TotallyNotJson.1780 : Str = "E"; - ret TotallyNotJson.1780; + let TotallyNotJson.1757 : Str = "E"; + ret TotallyNotJson.1757; else - let TotallyNotJson.1843 : Str = "f"; - let TotallyNotJson.1844 : Int1 = lowlevel Eq TotallyNotJson.1843 TotallyNotJson.850; - dec TotallyNotJson.1843; - if TotallyNotJson.1844 then + let TotallyNotJson.1820 : Str = "f"; + let TotallyNotJson.1821 : Int1 = lowlevel Eq TotallyNotJson.1820 TotallyNotJson.850; + dec TotallyNotJson.1820; + if TotallyNotJson.1821 then dec TotallyNotJson.850; - let TotallyNotJson.1781 : Str = "F"; - ret TotallyNotJson.1781; + let TotallyNotJson.1758 : Str = "F"; + ret TotallyNotJson.1758; else - let TotallyNotJson.1841 : Str = "g"; - let TotallyNotJson.1842 : Int1 = lowlevel Eq TotallyNotJson.1841 TotallyNotJson.850; - dec TotallyNotJson.1841; - if TotallyNotJson.1842 then + let TotallyNotJson.1818 : Str = "g"; + let TotallyNotJson.1819 : Int1 = lowlevel Eq TotallyNotJson.1818 TotallyNotJson.850; + dec TotallyNotJson.1818; + if TotallyNotJson.1819 then dec TotallyNotJson.850; - let TotallyNotJson.1782 : Str = "G"; - ret TotallyNotJson.1782; + let TotallyNotJson.1759 : Str = "G"; + ret TotallyNotJson.1759; else - let TotallyNotJson.1839 : Str = "h"; - let TotallyNotJson.1840 : Int1 = lowlevel Eq TotallyNotJson.1839 TotallyNotJson.850; - dec TotallyNotJson.1839; - if TotallyNotJson.1840 then + let TotallyNotJson.1816 : Str = "h"; + let TotallyNotJson.1817 : Int1 = lowlevel Eq TotallyNotJson.1816 TotallyNotJson.850; + dec TotallyNotJson.1816; + if TotallyNotJson.1817 then dec TotallyNotJson.850; - let TotallyNotJson.1783 : Str = "H"; - ret TotallyNotJson.1783; + let TotallyNotJson.1760 : Str = "H"; + ret TotallyNotJson.1760; else - let TotallyNotJson.1837 : Str = "i"; - let TotallyNotJson.1838 : Int1 = lowlevel Eq TotallyNotJson.1837 TotallyNotJson.850; - dec TotallyNotJson.1837; - if TotallyNotJson.1838 then + let TotallyNotJson.1814 : Str = "i"; + let TotallyNotJson.1815 : Int1 = lowlevel Eq TotallyNotJson.1814 TotallyNotJson.850; + dec TotallyNotJson.1814; + if TotallyNotJson.1815 then dec TotallyNotJson.850; - let TotallyNotJson.1784 : Str = "I"; - ret TotallyNotJson.1784; + let TotallyNotJson.1761 : Str = "I"; + ret TotallyNotJson.1761; else - let TotallyNotJson.1835 : Str = "j"; - let TotallyNotJson.1836 : Int1 = lowlevel Eq TotallyNotJson.1835 TotallyNotJson.850; - dec TotallyNotJson.1835; - if TotallyNotJson.1836 then + let TotallyNotJson.1812 : Str = "j"; + let TotallyNotJson.1813 : Int1 = lowlevel Eq TotallyNotJson.1812 TotallyNotJson.850; + dec TotallyNotJson.1812; + if TotallyNotJson.1813 then dec TotallyNotJson.850; - let TotallyNotJson.1785 : Str = "J"; - ret TotallyNotJson.1785; + let TotallyNotJson.1762 : Str = "J"; + ret TotallyNotJson.1762; else - let TotallyNotJson.1833 : Str = "k"; - let TotallyNotJson.1834 : Int1 = lowlevel Eq TotallyNotJson.1833 TotallyNotJson.850; - dec TotallyNotJson.1833; - if TotallyNotJson.1834 then + let TotallyNotJson.1810 : Str = "k"; + let TotallyNotJson.1811 : Int1 = lowlevel Eq TotallyNotJson.1810 TotallyNotJson.850; + dec TotallyNotJson.1810; + if TotallyNotJson.1811 then dec TotallyNotJson.850; - let TotallyNotJson.1786 : Str = "K"; - ret TotallyNotJson.1786; + let TotallyNotJson.1763 : Str = "K"; + ret TotallyNotJson.1763; else - let TotallyNotJson.1831 : Str = "l"; - let TotallyNotJson.1832 : Int1 = lowlevel Eq TotallyNotJson.1831 TotallyNotJson.850; - dec TotallyNotJson.1831; - if TotallyNotJson.1832 then + let TotallyNotJson.1808 : Str = "l"; + let TotallyNotJson.1809 : Int1 = lowlevel Eq TotallyNotJson.1808 TotallyNotJson.850; + dec TotallyNotJson.1808; + if TotallyNotJson.1809 then dec TotallyNotJson.850; - let TotallyNotJson.1787 : Str = "L"; - ret TotallyNotJson.1787; + let TotallyNotJson.1764 : Str = "L"; + ret TotallyNotJson.1764; else - let TotallyNotJson.1829 : Str = "m"; - let TotallyNotJson.1830 : Int1 = lowlevel Eq TotallyNotJson.1829 TotallyNotJson.850; - dec TotallyNotJson.1829; - if TotallyNotJson.1830 then + let TotallyNotJson.1806 : Str = "m"; + let TotallyNotJson.1807 : Int1 = lowlevel Eq TotallyNotJson.1806 TotallyNotJson.850; + dec TotallyNotJson.1806; + if TotallyNotJson.1807 then dec TotallyNotJson.850; - let TotallyNotJson.1788 : Str = "M"; - ret TotallyNotJson.1788; + let TotallyNotJson.1765 : Str = "M"; + ret TotallyNotJson.1765; else - let TotallyNotJson.1827 : Str = "n"; - let TotallyNotJson.1828 : Int1 = lowlevel Eq TotallyNotJson.1827 TotallyNotJson.850; - dec TotallyNotJson.1827; - if TotallyNotJson.1828 then + let TotallyNotJson.1804 : Str = "n"; + let TotallyNotJson.1805 : Int1 = lowlevel Eq TotallyNotJson.1804 TotallyNotJson.850; + dec TotallyNotJson.1804; + if TotallyNotJson.1805 then dec TotallyNotJson.850; - let TotallyNotJson.1789 : Str = "N"; - ret TotallyNotJson.1789; + let TotallyNotJson.1766 : Str = "N"; + ret TotallyNotJson.1766; else - let TotallyNotJson.1825 : Str = "o"; - let TotallyNotJson.1826 : Int1 = lowlevel Eq TotallyNotJson.1825 TotallyNotJson.850; - dec TotallyNotJson.1825; - if TotallyNotJson.1826 then + let TotallyNotJson.1802 : Str = "o"; + let TotallyNotJson.1803 : Int1 = lowlevel Eq TotallyNotJson.1802 TotallyNotJson.850; + dec TotallyNotJson.1802; + if TotallyNotJson.1803 then dec TotallyNotJson.850; - let TotallyNotJson.1790 : Str = "O"; - ret TotallyNotJson.1790; + let TotallyNotJson.1767 : Str = "O"; + ret TotallyNotJson.1767; else - let TotallyNotJson.1823 : Str = "p"; - let TotallyNotJson.1824 : Int1 = lowlevel Eq TotallyNotJson.1823 TotallyNotJson.850; - dec TotallyNotJson.1823; - if TotallyNotJson.1824 then + let TotallyNotJson.1800 : Str = "p"; + let TotallyNotJson.1801 : Int1 = lowlevel Eq TotallyNotJson.1800 TotallyNotJson.850; + dec TotallyNotJson.1800; + if TotallyNotJson.1801 then dec TotallyNotJson.850; - let TotallyNotJson.1791 : Str = "P"; - ret TotallyNotJson.1791; + let TotallyNotJson.1768 : Str = "P"; + ret TotallyNotJson.1768; else - let TotallyNotJson.1821 : Str = "q"; - let TotallyNotJson.1822 : Int1 = lowlevel Eq TotallyNotJson.1821 TotallyNotJson.850; - dec TotallyNotJson.1821; - if TotallyNotJson.1822 then + let TotallyNotJson.1798 : Str = "q"; + let TotallyNotJson.1799 : Int1 = lowlevel Eq TotallyNotJson.1798 TotallyNotJson.850; + dec TotallyNotJson.1798; + if TotallyNotJson.1799 then dec TotallyNotJson.850; - let TotallyNotJson.1792 : Str = "Q"; - ret TotallyNotJson.1792; + let TotallyNotJson.1769 : Str = "Q"; + ret TotallyNotJson.1769; else - let TotallyNotJson.1819 : Str = "r"; - let TotallyNotJson.1820 : Int1 = lowlevel Eq TotallyNotJson.1819 TotallyNotJson.850; - dec TotallyNotJson.1819; - if TotallyNotJson.1820 then + let TotallyNotJson.1796 : Str = "r"; + let TotallyNotJson.1797 : Int1 = lowlevel Eq TotallyNotJson.1796 TotallyNotJson.850; + dec TotallyNotJson.1796; + if TotallyNotJson.1797 then dec TotallyNotJson.850; - let TotallyNotJson.1793 : Str = "R"; - ret TotallyNotJson.1793; + let TotallyNotJson.1770 : Str = "R"; + ret TotallyNotJson.1770; else - let TotallyNotJson.1817 : Str = "s"; - let TotallyNotJson.1818 : Int1 = lowlevel Eq TotallyNotJson.1817 TotallyNotJson.850; - dec TotallyNotJson.1817; - if TotallyNotJson.1818 then + let TotallyNotJson.1794 : Str = "s"; + let TotallyNotJson.1795 : Int1 = lowlevel Eq TotallyNotJson.1794 TotallyNotJson.850; + dec TotallyNotJson.1794; + if TotallyNotJson.1795 then dec TotallyNotJson.850; - let TotallyNotJson.1794 : Str = "S"; - ret TotallyNotJson.1794; + let TotallyNotJson.1771 : Str = "S"; + ret TotallyNotJson.1771; else - let TotallyNotJson.1815 : Str = "t"; - let TotallyNotJson.1816 : Int1 = lowlevel Eq TotallyNotJson.1815 TotallyNotJson.850; - dec TotallyNotJson.1815; - if TotallyNotJson.1816 then + let TotallyNotJson.1792 : Str = "t"; + let TotallyNotJson.1793 : Int1 = lowlevel Eq TotallyNotJson.1792 TotallyNotJson.850; + dec TotallyNotJson.1792; + if TotallyNotJson.1793 then dec TotallyNotJson.850; - let TotallyNotJson.1795 : Str = "T"; - ret TotallyNotJson.1795; + let TotallyNotJson.1772 : Str = "T"; + ret TotallyNotJson.1772; else - let TotallyNotJson.1813 : Str = "u"; - let TotallyNotJson.1814 : Int1 = lowlevel Eq TotallyNotJson.1813 TotallyNotJson.850; - dec TotallyNotJson.1813; - if TotallyNotJson.1814 then + let TotallyNotJson.1790 : Str = "u"; + let TotallyNotJson.1791 : Int1 = lowlevel Eq TotallyNotJson.1790 TotallyNotJson.850; + dec TotallyNotJson.1790; + if TotallyNotJson.1791 then dec TotallyNotJson.850; - let TotallyNotJson.1796 : Str = "U"; - ret TotallyNotJson.1796; + let TotallyNotJson.1773 : Str = "U"; + ret TotallyNotJson.1773; else - let TotallyNotJson.1811 : Str = "v"; - let TotallyNotJson.1812 : Int1 = lowlevel Eq TotallyNotJson.1811 TotallyNotJson.850; - dec TotallyNotJson.1811; - if TotallyNotJson.1812 then + let TotallyNotJson.1788 : Str = "v"; + let TotallyNotJson.1789 : Int1 = lowlevel Eq TotallyNotJson.1788 TotallyNotJson.850; + dec TotallyNotJson.1788; + if TotallyNotJson.1789 then dec TotallyNotJson.850; - let TotallyNotJson.1797 : Str = "V"; - ret TotallyNotJson.1797; + let TotallyNotJson.1774 : Str = "V"; + ret TotallyNotJson.1774; else - let TotallyNotJson.1809 : Str = "w"; - let TotallyNotJson.1810 : Int1 = lowlevel Eq TotallyNotJson.1809 TotallyNotJson.850; - dec TotallyNotJson.1809; - if TotallyNotJson.1810 then + let TotallyNotJson.1786 : Str = "w"; + let TotallyNotJson.1787 : Int1 = lowlevel Eq TotallyNotJson.1786 TotallyNotJson.850; + dec TotallyNotJson.1786; + if TotallyNotJson.1787 then dec TotallyNotJson.850; - let TotallyNotJson.1798 : Str = "W"; - ret TotallyNotJson.1798; + let TotallyNotJson.1775 : Str = "W"; + ret TotallyNotJson.1775; else - let TotallyNotJson.1807 : Str = "x"; - let TotallyNotJson.1808 : Int1 = lowlevel Eq TotallyNotJson.1807 TotallyNotJson.850; - dec TotallyNotJson.1807; - if TotallyNotJson.1808 then + let TotallyNotJson.1784 : Str = "x"; + let TotallyNotJson.1785 : Int1 = lowlevel Eq TotallyNotJson.1784 TotallyNotJson.850; + dec TotallyNotJson.1784; + if TotallyNotJson.1785 then dec TotallyNotJson.850; - let TotallyNotJson.1799 : Str = "X"; - ret TotallyNotJson.1799; + let TotallyNotJson.1776 : Str = "X"; + ret TotallyNotJson.1776; else - let TotallyNotJson.1805 : Str = "y"; - let TotallyNotJson.1806 : Int1 = lowlevel Eq TotallyNotJson.1805 TotallyNotJson.850; - dec TotallyNotJson.1805; - if TotallyNotJson.1806 then + let TotallyNotJson.1782 : Str = "y"; + let TotallyNotJson.1783 : Int1 = lowlevel Eq TotallyNotJson.1782 TotallyNotJson.850; + dec TotallyNotJson.1782; + if TotallyNotJson.1783 then dec TotallyNotJson.850; - let TotallyNotJson.1800 : Str = "Y"; - ret TotallyNotJson.1800; + let TotallyNotJson.1777 : Str = "Y"; + ret TotallyNotJson.1777; else - let TotallyNotJson.1803 : Str = "z"; - let TotallyNotJson.1804 : Int1 = lowlevel Eq TotallyNotJson.1803 TotallyNotJson.850; - dec TotallyNotJson.1803; - if TotallyNotJson.1804 then + let TotallyNotJson.1780 : Str = "z"; + let TotallyNotJson.1781 : Int1 = lowlevel Eq TotallyNotJson.1780 TotallyNotJson.850; + dec TotallyNotJson.1780; + if TotallyNotJson.1781 then dec TotallyNotJson.850; - let TotallyNotJson.1801 : Str = "Z"; - ret TotallyNotJson.1801; + let TotallyNotJson.1778 : Str = "Z"; + ret TotallyNotJson.1778; else ret TotallyNotJson.850; procedure TotallyNotJson.101 (TotallyNotJson.851): - let TotallyNotJson.1677 : Str = "A"; - let TotallyNotJson.1678 : Int1 = lowlevel Eq TotallyNotJson.1677 TotallyNotJson.851; - dec TotallyNotJson.1677; - if TotallyNotJson.1678 then + let TotallyNotJson.1654 : Str = "A"; + let TotallyNotJson.1655 : Int1 = lowlevel Eq TotallyNotJson.1654 TotallyNotJson.851; + dec TotallyNotJson.1654; + if TotallyNotJson.1655 then dec TotallyNotJson.851; - let TotallyNotJson.1600 : Str = "a"; - ret TotallyNotJson.1600; + let TotallyNotJson.1577 : Str = "a"; + ret TotallyNotJson.1577; else - let TotallyNotJson.1675 : Str = "B"; - let TotallyNotJson.1676 : Int1 = lowlevel Eq TotallyNotJson.1675 TotallyNotJson.851; - dec TotallyNotJson.1675; - if TotallyNotJson.1676 then + let TotallyNotJson.1652 : Str = "B"; + let TotallyNotJson.1653 : Int1 = lowlevel Eq TotallyNotJson.1652 TotallyNotJson.851; + dec TotallyNotJson.1652; + if TotallyNotJson.1653 then dec TotallyNotJson.851; - let TotallyNotJson.1601 : Str = "b"; - ret TotallyNotJson.1601; + let TotallyNotJson.1578 : Str = "b"; + ret TotallyNotJson.1578; else - let TotallyNotJson.1673 : Str = "C"; - let TotallyNotJson.1674 : Int1 = lowlevel Eq TotallyNotJson.1673 TotallyNotJson.851; - dec TotallyNotJson.1673; - if TotallyNotJson.1674 then + let TotallyNotJson.1650 : Str = "C"; + let TotallyNotJson.1651 : Int1 = lowlevel Eq TotallyNotJson.1650 TotallyNotJson.851; + dec TotallyNotJson.1650; + if TotallyNotJson.1651 then dec TotallyNotJson.851; - let TotallyNotJson.1602 : Str = "c"; - ret TotallyNotJson.1602; + let TotallyNotJson.1579 : Str = "c"; + ret TotallyNotJson.1579; else - let TotallyNotJson.1671 : Str = "D"; - let TotallyNotJson.1672 : Int1 = lowlevel Eq TotallyNotJson.1671 TotallyNotJson.851; - dec TotallyNotJson.1671; - if TotallyNotJson.1672 then + let TotallyNotJson.1648 : Str = "D"; + let TotallyNotJson.1649 : Int1 = lowlevel Eq TotallyNotJson.1648 TotallyNotJson.851; + dec TotallyNotJson.1648; + if TotallyNotJson.1649 then dec TotallyNotJson.851; - let TotallyNotJson.1603 : Str = "d"; - ret TotallyNotJson.1603; + let TotallyNotJson.1580 : Str = "d"; + ret TotallyNotJson.1580; else - let TotallyNotJson.1669 : Str = "E"; - let TotallyNotJson.1670 : Int1 = lowlevel Eq TotallyNotJson.1669 TotallyNotJson.851; - dec TotallyNotJson.1669; - if TotallyNotJson.1670 then + let TotallyNotJson.1646 : Str = "E"; + let TotallyNotJson.1647 : Int1 = lowlevel Eq TotallyNotJson.1646 TotallyNotJson.851; + dec TotallyNotJson.1646; + if TotallyNotJson.1647 then dec TotallyNotJson.851; - let TotallyNotJson.1604 : Str = "e"; - ret TotallyNotJson.1604; + let TotallyNotJson.1581 : Str = "e"; + ret TotallyNotJson.1581; else - let TotallyNotJson.1667 : Str = "F"; - let TotallyNotJson.1668 : Int1 = lowlevel Eq TotallyNotJson.1667 TotallyNotJson.851; - dec TotallyNotJson.1667; - if TotallyNotJson.1668 then + let TotallyNotJson.1644 : Str = "F"; + let TotallyNotJson.1645 : Int1 = lowlevel Eq TotallyNotJson.1644 TotallyNotJson.851; + dec TotallyNotJson.1644; + if TotallyNotJson.1645 then dec TotallyNotJson.851; - let TotallyNotJson.1605 : Str = "f"; - ret TotallyNotJson.1605; + let TotallyNotJson.1582 : Str = "f"; + ret TotallyNotJson.1582; else - let TotallyNotJson.1665 : Str = "G"; - let TotallyNotJson.1666 : Int1 = lowlevel Eq TotallyNotJson.1665 TotallyNotJson.851; - dec TotallyNotJson.1665; - if TotallyNotJson.1666 then + let TotallyNotJson.1642 : Str = "G"; + let TotallyNotJson.1643 : Int1 = lowlevel Eq TotallyNotJson.1642 TotallyNotJson.851; + dec TotallyNotJson.1642; + if TotallyNotJson.1643 then dec TotallyNotJson.851; - let TotallyNotJson.1606 : Str = "g"; - ret TotallyNotJson.1606; + let TotallyNotJson.1583 : Str = "g"; + ret TotallyNotJson.1583; else - let TotallyNotJson.1663 : Str = "H"; - let TotallyNotJson.1664 : Int1 = lowlevel Eq TotallyNotJson.1663 TotallyNotJson.851; - dec TotallyNotJson.1663; - if TotallyNotJson.1664 then + let TotallyNotJson.1640 : Str = "H"; + let TotallyNotJson.1641 : Int1 = lowlevel Eq TotallyNotJson.1640 TotallyNotJson.851; + dec TotallyNotJson.1640; + if TotallyNotJson.1641 then dec TotallyNotJson.851; - let TotallyNotJson.1607 : Str = "h"; - ret TotallyNotJson.1607; + let TotallyNotJson.1584 : Str = "h"; + ret TotallyNotJson.1584; else - let TotallyNotJson.1661 : Str = "I"; - let TotallyNotJson.1662 : Int1 = lowlevel Eq TotallyNotJson.1661 TotallyNotJson.851; - dec TotallyNotJson.1661; - if TotallyNotJson.1662 then + let TotallyNotJson.1638 : Str = "I"; + let TotallyNotJson.1639 : Int1 = lowlevel Eq TotallyNotJson.1638 TotallyNotJson.851; + dec TotallyNotJson.1638; + if TotallyNotJson.1639 then dec TotallyNotJson.851; - let TotallyNotJson.1608 : Str = "i"; - ret TotallyNotJson.1608; + let TotallyNotJson.1585 : Str = "i"; + ret TotallyNotJson.1585; else - let TotallyNotJson.1659 : Str = "J"; - let TotallyNotJson.1660 : Int1 = lowlevel Eq TotallyNotJson.1659 TotallyNotJson.851; - dec TotallyNotJson.1659; - if TotallyNotJson.1660 then + let TotallyNotJson.1636 : Str = "J"; + let TotallyNotJson.1637 : Int1 = lowlevel Eq TotallyNotJson.1636 TotallyNotJson.851; + dec TotallyNotJson.1636; + if TotallyNotJson.1637 then dec TotallyNotJson.851; - let TotallyNotJson.1609 : Str = "j"; - ret TotallyNotJson.1609; + let TotallyNotJson.1586 : Str = "j"; + ret TotallyNotJson.1586; else - let TotallyNotJson.1657 : Str = "K"; - let TotallyNotJson.1658 : Int1 = lowlevel Eq TotallyNotJson.1657 TotallyNotJson.851; - dec TotallyNotJson.1657; - if TotallyNotJson.1658 then + let TotallyNotJson.1634 : Str = "K"; + let TotallyNotJson.1635 : Int1 = lowlevel Eq TotallyNotJson.1634 TotallyNotJson.851; + dec TotallyNotJson.1634; + if TotallyNotJson.1635 then dec TotallyNotJson.851; - let TotallyNotJson.1610 : Str = "k"; - ret TotallyNotJson.1610; + let TotallyNotJson.1587 : Str = "k"; + ret TotallyNotJson.1587; else - let TotallyNotJson.1655 : Str = "L"; - let TotallyNotJson.1656 : Int1 = lowlevel Eq TotallyNotJson.1655 TotallyNotJson.851; - dec TotallyNotJson.1655; - if TotallyNotJson.1656 then + let TotallyNotJson.1632 : Str = "L"; + let TotallyNotJson.1633 : Int1 = lowlevel Eq TotallyNotJson.1632 TotallyNotJson.851; + dec TotallyNotJson.1632; + if TotallyNotJson.1633 then dec TotallyNotJson.851; - let TotallyNotJson.1611 : Str = "l"; - ret TotallyNotJson.1611; + let TotallyNotJson.1588 : Str = "l"; + ret TotallyNotJson.1588; else - let TotallyNotJson.1653 : Str = "M"; - let TotallyNotJson.1654 : Int1 = lowlevel Eq TotallyNotJson.1653 TotallyNotJson.851; - dec TotallyNotJson.1653; - if TotallyNotJson.1654 then + let TotallyNotJson.1630 : Str = "M"; + let TotallyNotJson.1631 : Int1 = lowlevel Eq TotallyNotJson.1630 TotallyNotJson.851; + dec TotallyNotJson.1630; + if TotallyNotJson.1631 then dec TotallyNotJson.851; - let TotallyNotJson.1612 : Str = "m"; - ret TotallyNotJson.1612; + let TotallyNotJson.1589 : Str = "m"; + ret TotallyNotJson.1589; else - let TotallyNotJson.1651 : Str = "N"; - let TotallyNotJson.1652 : Int1 = lowlevel Eq TotallyNotJson.1651 TotallyNotJson.851; - dec TotallyNotJson.1651; - if TotallyNotJson.1652 then + let TotallyNotJson.1628 : Str = "N"; + let TotallyNotJson.1629 : Int1 = lowlevel Eq TotallyNotJson.1628 TotallyNotJson.851; + dec TotallyNotJson.1628; + if TotallyNotJson.1629 then dec TotallyNotJson.851; - let TotallyNotJson.1613 : Str = "n"; - ret TotallyNotJson.1613; + let TotallyNotJson.1590 : Str = "n"; + ret TotallyNotJson.1590; else - let TotallyNotJson.1649 : Str = "O"; - let TotallyNotJson.1650 : Int1 = lowlevel Eq TotallyNotJson.1649 TotallyNotJson.851; - dec TotallyNotJson.1649; - if TotallyNotJson.1650 then + let TotallyNotJson.1626 : Str = "O"; + let TotallyNotJson.1627 : Int1 = lowlevel Eq TotallyNotJson.1626 TotallyNotJson.851; + dec TotallyNotJson.1626; + if TotallyNotJson.1627 then dec TotallyNotJson.851; - let TotallyNotJson.1614 : Str = "o"; - ret TotallyNotJson.1614; + let TotallyNotJson.1591 : Str = "o"; + ret TotallyNotJson.1591; else - let TotallyNotJson.1647 : Str = "P"; - let TotallyNotJson.1648 : Int1 = lowlevel Eq TotallyNotJson.1647 TotallyNotJson.851; - dec TotallyNotJson.1647; - if TotallyNotJson.1648 then + let TotallyNotJson.1624 : Str = "P"; + let TotallyNotJson.1625 : Int1 = lowlevel Eq TotallyNotJson.1624 TotallyNotJson.851; + dec TotallyNotJson.1624; + if TotallyNotJson.1625 then dec TotallyNotJson.851; - let TotallyNotJson.1615 : Str = "p"; - ret TotallyNotJson.1615; + let TotallyNotJson.1592 : Str = "p"; + ret TotallyNotJson.1592; else - let TotallyNotJson.1645 : Str = "Q"; - let TotallyNotJson.1646 : Int1 = lowlevel Eq TotallyNotJson.1645 TotallyNotJson.851; - dec TotallyNotJson.1645; - if TotallyNotJson.1646 then + let TotallyNotJson.1622 : Str = "Q"; + let TotallyNotJson.1623 : Int1 = lowlevel Eq TotallyNotJson.1622 TotallyNotJson.851; + dec TotallyNotJson.1622; + if TotallyNotJson.1623 then dec TotallyNotJson.851; - let TotallyNotJson.1616 : Str = "q"; - ret TotallyNotJson.1616; + let TotallyNotJson.1593 : Str = "q"; + ret TotallyNotJson.1593; else - let TotallyNotJson.1643 : Str = "R"; - let TotallyNotJson.1644 : Int1 = lowlevel Eq TotallyNotJson.1643 TotallyNotJson.851; - dec TotallyNotJson.1643; - if TotallyNotJson.1644 then + let TotallyNotJson.1620 : Str = "R"; + let TotallyNotJson.1621 : Int1 = lowlevel Eq TotallyNotJson.1620 TotallyNotJson.851; + dec TotallyNotJson.1620; + if TotallyNotJson.1621 then dec TotallyNotJson.851; - let TotallyNotJson.1617 : Str = "r"; - ret TotallyNotJson.1617; + let TotallyNotJson.1594 : Str = "r"; + ret TotallyNotJson.1594; else - let TotallyNotJson.1641 : Str = "S"; - let TotallyNotJson.1642 : Int1 = lowlevel Eq TotallyNotJson.1641 TotallyNotJson.851; - dec TotallyNotJson.1641; - if TotallyNotJson.1642 then + let TotallyNotJson.1618 : Str = "S"; + let TotallyNotJson.1619 : Int1 = lowlevel Eq TotallyNotJson.1618 TotallyNotJson.851; + dec TotallyNotJson.1618; + if TotallyNotJson.1619 then dec TotallyNotJson.851; - let TotallyNotJson.1618 : Str = "s"; - ret TotallyNotJson.1618; + let TotallyNotJson.1595 : Str = "s"; + ret TotallyNotJson.1595; else - let TotallyNotJson.1639 : Str = "T"; - let TotallyNotJson.1640 : Int1 = lowlevel Eq TotallyNotJson.1639 TotallyNotJson.851; - dec TotallyNotJson.1639; - if TotallyNotJson.1640 then + let TotallyNotJson.1616 : Str = "T"; + let TotallyNotJson.1617 : Int1 = lowlevel Eq TotallyNotJson.1616 TotallyNotJson.851; + dec TotallyNotJson.1616; + if TotallyNotJson.1617 then dec TotallyNotJson.851; - let TotallyNotJson.1619 : Str = "t"; - ret TotallyNotJson.1619; + let TotallyNotJson.1596 : Str = "t"; + ret TotallyNotJson.1596; else - let TotallyNotJson.1637 : Str = "U"; - let TotallyNotJson.1638 : Int1 = lowlevel Eq TotallyNotJson.1637 TotallyNotJson.851; - dec TotallyNotJson.1637; - if TotallyNotJson.1638 then + let TotallyNotJson.1614 : Str = "U"; + let TotallyNotJson.1615 : Int1 = lowlevel Eq TotallyNotJson.1614 TotallyNotJson.851; + dec TotallyNotJson.1614; + if TotallyNotJson.1615 then dec TotallyNotJson.851; - let TotallyNotJson.1620 : Str = "u"; - ret TotallyNotJson.1620; + let TotallyNotJson.1597 : Str = "u"; + ret TotallyNotJson.1597; else - let TotallyNotJson.1635 : Str = "V"; - let TotallyNotJson.1636 : Int1 = lowlevel Eq TotallyNotJson.1635 TotallyNotJson.851; - dec TotallyNotJson.1635; - if TotallyNotJson.1636 then + let TotallyNotJson.1612 : Str = "V"; + let TotallyNotJson.1613 : Int1 = lowlevel Eq TotallyNotJson.1612 TotallyNotJson.851; + dec TotallyNotJson.1612; + if TotallyNotJson.1613 then dec TotallyNotJson.851; - let TotallyNotJson.1621 : Str = "v"; - ret TotallyNotJson.1621; + let TotallyNotJson.1598 : Str = "v"; + ret TotallyNotJson.1598; else - let TotallyNotJson.1633 : Str = "W"; - let TotallyNotJson.1634 : Int1 = lowlevel Eq TotallyNotJson.1633 TotallyNotJson.851; - dec TotallyNotJson.1633; - if TotallyNotJson.1634 then + let TotallyNotJson.1610 : Str = "W"; + let TotallyNotJson.1611 : Int1 = lowlevel Eq TotallyNotJson.1610 TotallyNotJson.851; + dec TotallyNotJson.1610; + if TotallyNotJson.1611 then dec TotallyNotJson.851; - let TotallyNotJson.1622 : Str = "w"; - ret TotallyNotJson.1622; + let TotallyNotJson.1599 : Str = "w"; + ret TotallyNotJson.1599; else - let TotallyNotJson.1631 : Str = "X"; - let TotallyNotJson.1632 : Int1 = lowlevel Eq TotallyNotJson.1631 TotallyNotJson.851; - dec TotallyNotJson.1631; - if TotallyNotJson.1632 then + let TotallyNotJson.1608 : Str = "X"; + let TotallyNotJson.1609 : Int1 = lowlevel Eq TotallyNotJson.1608 TotallyNotJson.851; + dec TotallyNotJson.1608; + if TotallyNotJson.1609 then dec TotallyNotJson.851; - let TotallyNotJson.1623 : Str = "x"; - ret TotallyNotJson.1623; + let TotallyNotJson.1600 : Str = "x"; + ret TotallyNotJson.1600; else - let TotallyNotJson.1629 : Str = "Y"; - let TotallyNotJson.1630 : Int1 = lowlevel Eq TotallyNotJson.1629 TotallyNotJson.851; - dec TotallyNotJson.1629; - if TotallyNotJson.1630 then + let TotallyNotJson.1606 : Str = "Y"; + let TotallyNotJson.1607 : Int1 = lowlevel Eq TotallyNotJson.1606 TotallyNotJson.851; + dec TotallyNotJson.1606; + if TotallyNotJson.1607 then dec TotallyNotJson.851; - let TotallyNotJson.1624 : Str = "y"; - ret TotallyNotJson.1624; + let TotallyNotJson.1601 : Str = "y"; + ret TotallyNotJson.1601; else - let TotallyNotJson.1627 : Str = "Z"; - let TotallyNotJson.1628 : Int1 = lowlevel Eq TotallyNotJson.1627 TotallyNotJson.851; - dec TotallyNotJson.1627; - if TotallyNotJson.1628 then + let TotallyNotJson.1604 : Str = "Z"; + let TotallyNotJson.1605 : Int1 = lowlevel Eq TotallyNotJson.1604 TotallyNotJson.851; + dec TotallyNotJson.1604; + if TotallyNotJson.1605 then dec TotallyNotJson.851; - let TotallyNotJson.1625 : Str = "z"; - ret TotallyNotJson.1625; + let TotallyNotJson.1602 : Str = "z"; + ret TotallyNotJson.1602; else ret TotallyNotJson.851; procedure TotallyNotJson.102 (TotallyNotJson.852): - let TotallyNotJson.1764 : Str = "A"; - let TotallyNotJson.1765 : Int1 = lowlevel Eq TotallyNotJson.1764 TotallyNotJson.852; - dec TotallyNotJson.1764; - if TotallyNotJson.1765 then + let TotallyNotJson.1741 : Str = "A"; + let TotallyNotJson.1742 : Int1 = lowlevel Eq TotallyNotJson.1741 TotallyNotJson.852; + dec TotallyNotJson.1741; + if TotallyNotJson.1742 then dec TotallyNotJson.852; - let TotallyNotJson.1687 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1687; + let TotallyNotJson.1664 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1664; else - let TotallyNotJson.1762 : Str = "B"; - let TotallyNotJson.1763 : Int1 = lowlevel Eq TotallyNotJson.1762 TotallyNotJson.852; - dec TotallyNotJson.1762; - if TotallyNotJson.1763 then + let TotallyNotJson.1739 : Str = "B"; + let TotallyNotJson.1740 : Int1 = lowlevel Eq TotallyNotJson.1739 TotallyNotJson.852; + dec TotallyNotJson.1739; + if TotallyNotJson.1740 then dec TotallyNotJson.852; - let TotallyNotJson.1688 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1688; + let TotallyNotJson.1665 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1665; else - let TotallyNotJson.1760 : Str = "C"; - let TotallyNotJson.1761 : Int1 = lowlevel Eq TotallyNotJson.1760 TotallyNotJson.852; - dec TotallyNotJson.1760; - if TotallyNotJson.1761 then + let TotallyNotJson.1737 : Str = "C"; + let TotallyNotJson.1738 : Int1 = lowlevel Eq TotallyNotJson.1737 TotallyNotJson.852; + dec TotallyNotJson.1737; + if TotallyNotJson.1738 then dec TotallyNotJson.852; - let TotallyNotJson.1689 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1689; + let TotallyNotJson.1666 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1666; else - let TotallyNotJson.1758 : Str = "D"; - let TotallyNotJson.1759 : Int1 = lowlevel Eq TotallyNotJson.1758 TotallyNotJson.852; - dec TotallyNotJson.1758; - if TotallyNotJson.1759 then + let TotallyNotJson.1735 : Str = "D"; + let TotallyNotJson.1736 : Int1 = lowlevel Eq TotallyNotJson.1735 TotallyNotJson.852; + dec TotallyNotJson.1735; + if TotallyNotJson.1736 then dec TotallyNotJson.852; - let TotallyNotJson.1690 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1690; + let TotallyNotJson.1667 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1667; else - let TotallyNotJson.1756 : Str = "E"; - let TotallyNotJson.1757 : Int1 = lowlevel Eq TotallyNotJson.1756 TotallyNotJson.852; - dec TotallyNotJson.1756; - if TotallyNotJson.1757 then + let TotallyNotJson.1733 : Str = "E"; + let TotallyNotJson.1734 : Int1 = lowlevel Eq TotallyNotJson.1733 TotallyNotJson.852; + dec TotallyNotJson.1733; + if TotallyNotJson.1734 then dec TotallyNotJson.852; - let TotallyNotJson.1691 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1691; + let TotallyNotJson.1668 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1668; else - let TotallyNotJson.1754 : Str = "F"; - let TotallyNotJson.1755 : Int1 = lowlevel Eq TotallyNotJson.1754 TotallyNotJson.852; - dec TotallyNotJson.1754; - if TotallyNotJson.1755 then + let TotallyNotJson.1731 : Str = "F"; + let TotallyNotJson.1732 : Int1 = lowlevel Eq TotallyNotJson.1731 TotallyNotJson.852; + dec TotallyNotJson.1731; + if TotallyNotJson.1732 then dec TotallyNotJson.852; - let TotallyNotJson.1692 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1692; + let TotallyNotJson.1669 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1669; else - let TotallyNotJson.1752 : Str = "G"; - let TotallyNotJson.1753 : Int1 = lowlevel Eq TotallyNotJson.1752 TotallyNotJson.852; - dec TotallyNotJson.1752; - if TotallyNotJson.1753 then + let TotallyNotJson.1729 : Str = "G"; + let TotallyNotJson.1730 : Int1 = lowlevel Eq TotallyNotJson.1729 TotallyNotJson.852; + dec TotallyNotJson.1729; + if TotallyNotJson.1730 then dec TotallyNotJson.852; - let TotallyNotJson.1693 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1693; + let TotallyNotJson.1670 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1670; else - let TotallyNotJson.1750 : Str = "H"; - let TotallyNotJson.1751 : Int1 = lowlevel Eq TotallyNotJson.1750 TotallyNotJson.852; - dec TotallyNotJson.1750; - if TotallyNotJson.1751 then + let TotallyNotJson.1727 : Str = "H"; + let TotallyNotJson.1728 : Int1 = lowlevel Eq TotallyNotJson.1727 TotallyNotJson.852; + dec TotallyNotJson.1727; + if TotallyNotJson.1728 then dec TotallyNotJson.852; - let TotallyNotJson.1694 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1694; + let TotallyNotJson.1671 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1671; else - let TotallyNotJson.1748 : Str = "I"; - let TotallyNotJson.1749 : Int1 = lowlevel Eq TotallyNotJson.1748 TotallyNotJson.852; - dec TotallyNotJson.1748; - if TotallyNotJson.1749 then + let TotallyNotJson.1725 : Str = "I"; + let TotallyNotJson.1726 : Int1 = lowlevel Eq TotallyNotJson.1725 TotallyNotJson.852; + dec TotallyNotJson.1725; + if TotallyNotJson.1726 then dec TotallyNotJson.852; - let TotallyNotJson.1695 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1695; + let TotallyNotJson.1672 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1672; else - let TotallyNotJson.1746 : Str = "J"; - let TotallyNotJson.1747 : Int1 = lowlevel Eq TotallyNotJson.1746 TotallyNotJson.852; - dec TotallyNotJson.1746; - if TotallyNotJson.1747 then + let TotallyNotJson.1723 : Str = "J"; + let TotallyNotJson.1724 : Int1 = lowlevel Eq TotallyNotJson.1723 TotallyNotJson.852; + dec TotallyNotJson.1723; + if TotallyNotJson.1724 then dec TotallyNotJson.852; - let TotallyNotJson.1696 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1696; + let TotallyNotJson.1673 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1673; else - let TotallyNotJson.1744 : Str = "K"; - let TotallyNotJson.1745 : Int1 = lowlevel Eq TotallyNotJson.1744 TotallyNotJson.852; - dec TotallyNotJson.1744; - if TotallyNotJson.1745 then + let TotallyNotJson.1721 : Str = "K"; + let TotallyNotJson.1722 : Int1 = lowlevel Eq TotallyNotJson.1721 TotallyNotJson.852; + dec TotallyNotJson.1721; + if TotallyNotJson.1722 then dec TotallyNotJson.852; - let TotallyNotJson.1697 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1697; + let TotallyNotJson.1674 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1674; else - let TotallyNotJson.1742 : Str = "L"; - let TotallyNotJson.1743 : Int1 = lowlevel Eq TotallyNotJson.1742 TotallyNotJson.852; - dec TotallyNotJson.1742; - if TotallyNotJson.1743 then + let TotallyNotJson.1719 : Str = "L"; + let TotallyNotJson.1720 : Int1 = lowlevel Eq TotallyNotJson.1719 TotallyNotJson.852; + dec TotallyNotJson.1719; + if TotallyNotJson.1720 then dec TotallyNotJson.852; - let TotallyNotJson.1698 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1698; + let TotallyNotJson.1675 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1675; else - let TotallyNotJson.1740 : Str = "M"; - let TotallyNotJson.1741 : Int1 = lowlevel Eq TotallyNotJson.1740 TotallyNotJson.852; - dec TotallyNotJson.1740; - if TotallyNotJson.1741 then + let TotallyNotJson.1717 : Str = "M"; + let TotallyNotJson.1718 : Int1 = lowlevel Eq TotallyNotJson.1717 TotallyNotJson.852; + dec TotallyNotJson.1717; + if TotallyNotJson.1718 then dec TotallyNotJson.852; - let TotallyNotJson.1699 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1699; + let TotallyNotJson.1676 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1676; else - let TotallyNotJson.1738 : Str = "N"; - let TotallyNotJson.1739 : Int1 = lowlevel Eq TotallyNotJson.1738 TotallyNotJson.852; - dec TotallyNotJson.1738; - if TotallyNotJson.1739 then + let TotallyNotJson.1715 : Str = "N"; + let TotallyNotJson.1716 : Int1 = lowlevel Eq TotallyNotJson.1715 TotallyNotJson.852; + dec TotallyNotJson.1715; + if TotallyNotJson.1716 then dec TotallyNotJson.852; - let TotallyNotJson.1700 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1700; + let TotallyNotJson.1677 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1677; else - let TotallyNotJson.1736 : Str = "O"; - let TotallyNotJson.1737 : Int1 = lowlevel Eq TotallyNotJson.1736 TotallyNotJson.852; - dec TotallyNotJson.1736; - if TotallyNotJson.1737 then + let TotallyNotJson.1713 : Str = "O"; + let TotallyNotJson.1714 : Int1 = lowlevel Eq TotallyNotJson.1713 TotallyNotJson.852; + dec TotallyNotJson.1713; + if TotallyNotJson.1714 then dec TotallyNotJson.852; - let TotallyNotJson.1701 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1701; + let TotallyNotJson.1678 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1678; else - let TotallyNotJson.1734 : Str = "P"; - let TotallyNotJson.1735 : Int1 = lowlevel Eq TotallyNotJson.1734 TotallyNotJson.852; - dec TotallyNotJson.1734; - if TotallyNotJson.1735 then + let TotallyNotJson.1711 : Str = "P"; + let TotallyNotJson.1712 : Int1 = lowlevel Eq TotallyNotJson.1711 TotallyNotJson.852; + dec TotallyNotJson.1711; + if TotallyNotJson.1712 then dec TotallyNotJson.852; - let TotallyNotJson.1702 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1702; + let TotallyNotJson.1679 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1679; else - let TotallyNotJson.1732 : Str = "Q"; - let TotallyNotJson.1733 : Int1 = lowlevel Eq TotallyNotJson.1732 TotallyNotJson.852; - dec TotallyNotJson.1732; - if TotallyNotJson.1733 then + let TotallyNotJson.1709 : Str = "Q"; + let TotallyNotJson.1710 : Int1 = lowlevel Eq TotallyNotJson.1709 TotallyNotJson.852; + dec TotallyNotJson.1709; + if TotallyNotJson.1710 then dec TotallyNotJson.852; - let TotallyNotJson.1703 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1703; + let TotallyNotJson.1680 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1680; else - let TotallyNotJson.1730 : Str = "R"; - let TotallyNotJson.1731 : Int1 = lowlevel Eq TotallyNotJson.1730 TotallyNotJson.852; - dec TotallyNotJson.1730; - if TotallyNotJson.1731 then + let TotallyNotJson.1707 : Str = "R"; + let TotallyNotJson.1708 : Int1 = lowlevel Eq TotallyNotJson.1707 TotallyNotJson.852; + dec TotallyNotJson.1707; + if TotallyNotJson.1708 then dec TotallyNotJson.852; - let TotallyNotJson.1704 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1704; + let TotallyNotJson.1681 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1681; else - let TotallyNotJson.1728 : Str = "S"; - let TotallyNotJson.1729 : Int1 = lowlevel Eq TotallyNotJson.1728 TotallyNotJson.852; - dec TotallyNotJson.1728; - if TotallyNotJson.1729 then + let TotallyNotJson.1705 : Str = "S"; + let TotallyNotJson.1706 : Int1 = lowlevel Eq TotallyNotJson.1705 TotallyNotJson.852; + dec TotallyNotJson.1705; + if TotallyNotJson.1706 then dec TotallyNotJson.852; - let TotallyNotJson.1705 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1705; + let TotallyNotJson.1682 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1682; else - let TotallyNotJson.1726 : Str = "T"; - let TotallyNotJson.1727 : Int1 = lowlevel Eq TotallyNotJson.1726 TotallyNotJson.852; - dec TotallyNotJson.1726; - if TotallyNotJson.1727 then + let TotallyNotJson.1703 : Str = "T"; + let TotallyNotJson.1704 : Int1 = lowlevel Eq TotallyNotJson.1703 TotallyNotJson.852; + dec TotallyNotJson.1703; + if TotallyNotJson.1704 then dec TotallyNotJson.852; - let TotallyNotJson.1706 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1706; + let TotallyNotJson.1683 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1683; else - let TotallyNotJson.1724 : Str = "U"; - let TotallyNotJson.1725 : Int1 = lowlevel Eq TotallyNotJson.1724 TotallyNotJson.852; - dec TotallyNotJson.1724; - if TotallyNotJson.1725 then + let TotallyNotJson.1701 : Str = "U"; + let TotallyNotJson.1702 : Int1 = lowlevel Eq TotallyNotJson.1701 TotallyNotJson.852; + dec TotallyNotJson.1701; + if TotallyNotJson.1702 then dec TotallyNotJson.852; - let TotallyNotJson.1707 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1707; + let TotallyNotJson.1684 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1684; else - let TotallyNotJson.1722 : Str = "V"; - let TotallyNotJson.1723 : Int1 = lowlevel Eq TotallyNotJson.1722 TotallyNotJson.852; - dec TotallyNotJson.1722; - if TotallyNotJson.1723 then + let TotallyNotJson.1699 : Str = "V"; + let TotallyNotJson.1700 : Int1 = lowlevel Eq TotallyNotJson.1699 TotallyNotJson.852; + dec TotallyNotJson.1699; + if TotallyNotJson.1700 then dec TotallyNotJson.852; - let TotallyNotJson.1708 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1708; + let TotallyNotJson.1685 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1685; else - let TotallyNotJson.1720 : Str = "W"; - let TotallyNotJson.1721 : Int1 = lowlevel Eq TotallyNotJson.1720 TotallyNotJson.852; - dec TotallyNotJson.1720; - if TotallyNotJson.1721 then + let TotallyNotJson.1697 : Str = "W"; + let TotallyNotJson.1698 : Int1 = lowlevel Eq TotallyNotJson.1697 TotallyNotJson.852; + dec TotallyNotJson.1697; + if TotallyNotJson.1698 then dec TotallyNotJson.852; - let TotallyNotJson.1709 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1709; + let TotallyNotJson.1686 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1686; else - let TotallyNotJson.1718 : Str = "X"; - let TotallyNotJson.1719 : Int1 = lowlevel Eq TotallyNotJson.1718 TotallyNotJson.852; - dec TotallyNotJson.1718; - if TotallyNotJson.1719 then + let TotallyNotJson.1695 : Str = "X"; + let TotallyNotJson.1696 : Int1 = lowlevel Eq TotallyNotJson.1695 TotallyNotJson.852; + dec TotallyNotJson.1695; + if TotallyNotJson.1696 then dec TotallyNotJson.852; - let TotallyNotJson.1710 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1710; + let TotallyNotJson.1687 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1687; else - let TotallyNotJson.1716 : Str = "Y"; - let TotallyNotJson.1717 : Int1 = lowlevel Eq TotallyNotJson.1716 TotallyNotJson.852; - dec TotallyNotJson.1716; - if TotallyNotJson.1717 then + let TotallyNotJson.1693 : Str = "Y"; + let TotallyNotJson.1694 : Int1 = lowlevel Eq TotallyNotJson.1693 TotallyNotJson.852; + dec TotallyNotJson.1693; + if TotallyNotJson.1694 then dec TotallyNotJson.852; - let TotallyNotJson.1711 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1711; + let TotallyNotJson.1688 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1688; else - let TotallyNotJson.1714 : Str = "Z"; - let TotallyNotJson.1715 : Int1 = lowlevel Eq TotallyNotJson.1714 TotallyNotJson.852; + let TotallyNotJson.1691 : Str = "Z"; + let TotallyNotJson.1692 : Int1 = lowlevel Eq TotallyNotJson.1691 TotallyNotJson.852; dec TotallyNotJson.852; - dec TotallyNotJson.1714; - if TotallyNotJson.1715 then - let TotallyNotJson.1712 : Int1 = CallByName Bool.2; - ret TotallyNotJson.1712; + dec TotallyNotJson.1691; + if TotallyNotJson.1692 then + let TotallyNotJson.1689 : Int1 = CallByName Bool.2; + ret TotallyNotJson.1689; else - let TotallyNotJson.1713 : Int1 = CallByName Bool.1; - ret TotallyNotJson.1713; + let TotallyNotJson.1690 : Int1 = CallByName Bool.1; + ret TotallyNotJson.1690; -procedure TotallyNotJson.182 (TotallyNotJson.183, TotallyNotJson.1902, TotallyNotJson.181): - let TotallyNotJson.1905 : List U8 = CallByName TotallyNotJson.26 TotallyNotJson.181; - let TotallyNotJson.1904 : List U8 = CallByName List.8 TotallyNotJson.183 TotallyNotJson.1905; - ret TotallyNotJson.1904; +procedure TotallyNotJson.182 (TotallyNotJson.183, TotallyNotJson.1879, TotallyNotJson.181): + let TotallyNotJson.1882 : List U8 = CallByName TotallyNotJson.26 TotallyNotJson.181; + let TotallyNotJson.1881 : List U8 = CallByName List.8 TotallyNotJson.183 TotallyNotJson.1882; + ret TotallyNotJson.1881; -procedure TotallyNotJson.189 (TotallyNotJson.1953, TotallyNotJson.192): - let TotallyNotJson.190 : U64 = StructAtIndex 0 TotallyNotJson.1953; - let TotallyNotJson.191 : Int1 = StructAtIndex 1 TotallyNotJson.1953; +procedure TotallyNotJson.189 (TotallyNotJson.1930, TotallyNotJson.192): + let TotallyNotJson.190 : U64 = StructAtIndex 0 TotallyNotJson.1930; + let TotallyNotJson.191 : Int1 = StructAtIndex 1 TotallyNotJson.1930; switch TotallyNotJson.192: case 34: - let TotallyNotJson.1956 : Int1 = false; - let TotallyNotJson.1955 : {U64, Int1} = Struct {TotallyNotJson.190, TotallyNotJson.1956}; - let TotallyNotJson.1954 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) TotallyNotJson.1955; - ret TotallyNotJson.1954; + let TotallyNotJson.1933 : Int1 = false; + let TotallyNotJson.1932 : {U64, Int1} = Struct {TotallyNotJson.190, TotallyNotJson.1933}; + let TotallyNotJson.1931 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) TotallyNotJson.1932; + ret TotallyNotJson.1931; case 92: - let TotallyNotJson.1959 : Int1 = false; - let TotallyNotJson.1958 : {U64, Int1} = Struct {TotallyNotJson.190, TotallyNotJson.1959}; - let TotallyNotJson.1957 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) TotallyNotJson.1958; - ret TotallyNotJson.1957; + let TotallyNotJson.1936 : Int1 = false; + let TotallyNotJson.1935 : {U64, Int1} = Struct {TotallyNotJson.190, TotallyNotJson.1936}; + let TotallyNotJson.1934 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) TotallyNotJson.1935; + ret TotallyNotJson.1934; case 47: - let TotallyNotJson.1962 : Int1 = false; - let TotallyNotJson.1961 : {U64, Int1} = Struct {TotallyNotJson.190, TotallyNotJson.1962}; - let TotallyNotJson.1960 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) TotallyNotJson.1961; - ret TotallyNotJson.1960; + let TotallyNotJson.1939 : Int1 = false; + let TotallyNotJson.1938 : {U64, Int1} = Struct {TotallyNotJson.190, TotallyNotJson.1939}; + let TotallyNotJson.1937 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) TotallyNotJson.1938; + ret TotallyNotJson.1937; case 8: - let TotallyNotJson.1965 : Int1 = false; - let TotallyNotJson.1964 : {U64, Int1} = Struct {TotallyNotJson.190, TotallyNotJson.1965}; - let TotallyNotJson.1963 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) TotallyNotJson.1964; - ret TotallyNotJson.1963; + let TotallyNotJson.1942 : Int1 = false; + let TotallyNotJson.1941 : {U64, Int1} = Struct {TotallyNotJson.190, TotallyNotJson.1942}; + let TotallyNotJson.1940 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) TotallyNotJson.1941; + ret TotallyNotJson.1940; case 12: - let TotallyNotJson.1968 : Int1 = false; - let TotallyNotJson.1967 : {U64, Int1} = Struct {TotallyNotJson.190, TotallyNotJson.1968}; - let TotallyNotJson.1966 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) TotallyNotJson.1967; - ret TotallyNotJson.1966; + let TotallyNotJson.1945 : Int1 = false; + let TotallyNotJson.1944 : {U64, Int1} = Struct {TotallyNotJson.190, TotallyNotJson.1945}; + let TotallyNotJson.1943 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) TotallyNotJson.1944; + ret TotallyNotJson.1943; case 10: - let TotallyNotJson.1971 : Int1 = false; - let TotallyNotJson.1970 : {U64, Int1} = Struct {TotallyNotJson.190, TotallyNotJson.1971}; - let TotallyNotJson.1969 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) TotallyNotJson.1970; - ret TotallyNotJson.1969; + let TotallyNotJson.1948 : Int1 = false; + let TotallyNotJson.1947 : {U64, Int1} = Struct {TotallyNotJson.190, TotallyNotJson.1948}; + let TotallyNotJson.1946 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) TotallyNotJson.1947; + ret TotallyNotJson.1946; case 13: - let TotallyNotJson.1974 : Int1 = false; - let TotallyNotJson.1973 : {U64, Int1} = Struct {TotallyNotJson.190, TotallyNotJson.1974}; - let TotallyNotJson.1972 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) TotallyNotJson.1973; - ret TotallyNotJson.1972; + let TotallyNotJson.1951 : Int1 = false; + let TotallyNotJson.1950 : {U64, Int1} = Struct {TotallyNotJson.190, TotallyNotJson.1951}; + let TotallyNotJson.1949 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) TotallyNotJson.1950; + ret TotallyNotJson.1949; case 9: - let TotallyNotJson.1977 : Int1 = false; - let TotallyNotJson.1976 : {U64, Int1} = Struct {TotallyNotJson.190, TotallyNotJson.1977}; - let TotallyNotJson.1975 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) TotallyNotJson.1976; - ret TotallyNotJson.1975; + let TotallyNotJson.1954 : Int1 = false; + let TotallyNotJson.1953 : {U64, Int1} = Struct {TotallyNotJson.190, TotallyNotJson.1954}; + let TotallyNotJson.1952 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) TotallyNotJson.1953; + ret TotallyNotJson.1952; default: - let TotallyNotJson.1981 : U64 = 1i64; - let TotallyNotJson.1980 : U64 = CallByName Num.19 TotallyNotJson.190 TotallyNotJson.1981; - let TotallyNotJson.1979 : {U64, Int1} = Struct {TotallyNotJson.1980, TotallyNotJson.191}; - let TotallyNotJson.1978 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) TotallyNotJson.1979; - ret TotallyNotJson.1978; + let TotallyNotJson.1958 : U64 = 1i64; + let TotallyNotJson.1957 : U64 = CallByName Num.19 TotallyNotJson.190 TotallyNotJson.1958; + let TotallyNotJson.1956 : {U64, Int1} = Struct {TotallyNotJson.1957, TotallyNotJson.191}; + let TotallyNotJson.1955 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) TotallyNotJson.1956; + ret TotallyNotJson.1955; procedure TotallyNotJson.2 (): @@ -1098,9 +1098,9 @@ procedure TotallyNotJson.2 (): ret TotallyNotJson.1172; procedure TotallyNotJson.215 (TotallyNotJson.216, TotallyNotJson.217): - let TotallyNotJson.1924 : List U8 = CallByName TotallyNotJson.27 TotallyNotJson.217; - let TotallyNotJson.1923 : List U8 = CallByName List.8 TotallyNotJson.216 TotallyNotJson.1924; - ret TotallyNotJson.1923; + let TotallyNotJson.1901 : List U8 = CallByName TotallyNotJson.27 TotallyNotJson.217; + let TotallyNotJson.1900 : List U8 = CallByName List.8 TotallyNotJson.216 TotallyNotJson.1901; + ret TotallyNotJson.1900; procedure TotallyNotJson.234 (TotallyNotJson.235, TotallyNotJson.1175, TotallyNotJson.233): let TotallyNotJson.1525 : I64 = 123i64; @@ -1116,17 +1116,17 @@ procedure TotallyNotJson.234 (TotallyNotJson.235, TotallyNotJson.1175, TotallyNo ret TotallyNotJson.1179; procedure TotallyNotJson.234 (TotallyNotJson.235, TotallyNotJson.1175, TotallyNotJson.233): - let TotallyNotJson.1899 : I64 = 123i64; - let TotallyNotJson.1898 : U8 = CallByName Num.127 TotallyNotJson.1899; - let TotallyNotJson.238 : List U8 = CallByName List.4 TotallyNotJson.235 TotallyNotJson.1898; - let TotallyNotJson.1897 : U64 = CallByName List.6 TotallyNotJson.233; - let TotallyNotJson.1557 : {List U8, U64} = Struct {TotallyNotJson.238, TotallyNotJson.1897}; - let TotallyNotJson.1556 : {List U8, U64} = CallByName List.18 TotallyNotJson.233 TotallyNotJson.1557 TotallyNotJson.1175; - let TotallyNotJson.240 : List U8 = StructAtIndex 0 TotallyNotJson.1556; - let TotallyNotJson.1555 : I64 = 125i64; - let TotallyNotJson.1554 : U8 = CallByName Num.127 TotallyNotJson.1555; - let TotallyNotJson.1553 : List U8 = CallByName List.4 TotallyNotJson.240 TotallyNotJson.1554; - ret TotallyNotJson.1553; + let TotallyNotJson.1876 : I64 = 123i64; + let TotallyNotJson.1875 : U8 = CallByName Num.127 TotallyNotJson.1876; + let TotallyNotJson.238 : List U8 = CallByName List.4 TotallyNotJson.235 TotallyNotJson.1875; + let TotallyNotJson.1874 : U64 = CallByName List.6 TotallyNotJson.233; + let TotallyNotJson.1534 : {List U8, U64} = Struct {TotallyNotJson.238, TotallyNotJson.1874}; + let TotallyNotJson.1533 : {List U8, U64} = CallByName List.18 TotallyNotJson.233 TotallyNotJson.1534 TotallyNotJson.1175; + let TotallyNotJson.240 : List U8 = StructAtIndex 0 TotallyNotJson.1533; + let TotallyNotJson.1532 : I64 = 125i64; + let TotallyNotJson.1531 : U8 = CallByName Num.127 TotallyNotJson.1532; + let TotallyNotJson.1530 : List U8 = CallByName List.4 TotallyNotJson.240 TotallyNotJson.1531; + ret TotallyNotJson.1530; procedure TotallyNotJson.237 (TotallyNotJson.1177, TotallyNotJson.1178, TotallyNotJson.236): let TotallyNotJson.243 : Str = StructAtIndex 0 TotallyNotJson.1178; @@ -1168,128 +1168,128 @@ procedure TotallyNotJson.237 (TotallyNotJson.1177, TotallyNotJson.1178, TotallyN let TotallyNotJson.241 : List U8 = StructAtIndex 0 TotallyNotJson.1177; let TotallyNotJson.242 : U64 = StructAtIndex 1 TotallyNotJson.1177; let TotallyNotJson.245 : Str = CallByName TotallyNotJson.82 TotallyNotJson.243 TotallyNotJson.236; - let TotallyNotJson.1579 : I64 = 34i64; - let TotallyNotJson.1578 : U8 = CallByName Num.127 TotallyNotJson.1579; - let TotallyNotJson.1576 : List U8 = CallByName List.4 TotallyNotJson.241 TotallyNotJson.1578; - let TotallyNotJson.1577 : List U8 = CallByName Str.12 TotallyNotJson.245; - let TotallyNotJson.1573 : List U8 = CallByName List.8 TotallyNotJson.1576 TotallyNotJson.1577; - let TotallyNotJson.1575 : I64 = 34i64; - let TotallyNotJson.1574 : U8 = CallByName Num.127 TotallyNotJson.1575; - let TotallyNotJson.1570 : List U8 = CallByName List.4 TotallyNotJson.1573 TotallyNotJson.1574; - let TotallyNotJson.1572 : I64 = 58i64; - let TotallyNotJson.1571 : U8 = CallByName Num.127 TotallyNotJson.1572; - let TotallyNotJson.1568 : List U8 = CallByName List.4 TotallyNotJson.1570 TotallyNotJson.1571; - let TotallyNotJson.246 : List U8 = CallByName Encode.24 TotallyNotJson.1568 TotallyNotJson.244 TotallyNotJson.236; - joinpoint TotallyNotJson.1563 TotallyNotJson.247: - let TotallyNotJson.1561 : U64 = 1i64; - let TotallyNotJson.1560 : U64 = CallByName Num.20 TotallyNotJson.242 TotallyNotJson.1561; - let TotallyNotJson.1559 : {List U8, U64} = Struct {TotallyNotJson.247, TotallyNotJson.1560}; - ret TotallyNotJson.1559; + let TotallyNotJson.1556 : I64 = 34i64; + let TotallyNotJson.1555 : U8 = CallByName Num.127 TotallyNotJson.1556; + let TotallyNotJson.1553 : List U8 = CallByName List.4 TotallyNotJson.241 TotallyNotJson.1555; + let TotallyNotJson.1554 : List U8 = CallByName Str.12 TotallyNotJson.245; + let TotallyNotJson.1550 : List U8 = CallByName List.8 TotallyNotJson.1553 TotallyNotJson.1554; + let TotallyNotJson.1552 : I64 = 34i64; + let TotallyNotJson.1551 : U8 = CallByName Num.127 TotallyNotJson.1552; + let TotallyNotJson.1547 : List U8 = CallByName List.4 TotallyNotJson.1550 TotallyNotJson.1551; + let TotallyNotJson.1549 : I64 = 58i64; + let TotallyNotJson.1548 : U8 = CallByName Num.127 TotallyNotJson.1549; + let TotallyNotJson.1545 : List U8 = CallByName List.4 TotallyNotJson.1547 TotallyNotJson.1548; + let TotallyNotJson.246 : List U8 = CallByName Encode.24 TotallyNotJson.1545 TotallyNotJson.244 TotallyNotJson.236; + joinpoint TotallyNotJson.1540 TotallyNotJson.247: + let TotallyNotJson.1538 : U64 = 1i64; + let TotallyNotJson.1537 : U64 = CallByName Num.20 TotallyNotJson.242 TotallyNotJson.1538; + let TotallyNotJson.1536 : {List U8, U64} = Struct {TotallyNotJson.247, TotallyNotJson.1537}; + ret TotallyNotJson.1536; in - let TotallyNotJson.1567 : U64 = 1i64; - let TotallyNotJson.1564 : Int1 = CallByName Num.24 TotallyNotJson.242 TotallyNotJson.1567; - if TotallyNotJson.1564 then - let TotallyNotJson.1566 : I64 = 44i64; - let TotallyNotJson.1565 : U8 = CallByName Num.127 TotallyNotJson.1566; - let TotallyNotJson.1562 : List U8 = CallByName List.4 TotallyNotJson.246 TotallyNotJson.1565; - jump TotallyNotJson.1563 TotallyNotJson.1562; + let TotallyNotJson.1544 : U64 = 1i64; + let TotallyNotJson.1541 : Int1 = CallByName Num.24 TotallyNotJson.242 TotallyNotJson.1544; + if TotallyNotJson.1541 then + let TotallyNotJson.1543 : I64 = 44i64; + let TotallyNotJson.1542 : U8 = CallByName Num.127 TotallyNotJson.1543; + let TotallyNotJson.1539 : List U8 = CallByName List.4 TotallyNotJson.246 TotallyNotJson.1542; + jump TotallyNotJson.1540 TotallyNotJson.1539; else - jump TotallyNotJson.1563 TotallyNotJson.246; + jump TotallyNotJson.1540 TotallyNotJson.246; procedure TotallyNotJson.25 (TotallyNotJson.181): - let TotallyNotJson.1900 : Str = CallByName Encode.23 TotallyNotJson.181; - ret TotallyNotJson.1900; + let TotallyNotJson.1877 : Str = CallByName Encode.23 TotallyNotJson.181; + ret TotallyNotJson.1877; procedure TotallyNotJson.26 (TotallyNotJson.184): let TotallyNotJson.185 : List U8 = CallByName Str.12 TotallyNotJson.184; - let TotallyNotJson.1982 : U64 = 0i64; - let TotallyNotJson.1983 : Int1 = true; - let TotallyNotJson.186 : {U64, Int1} = Struct {TotallyNotJson.1982, TotallyNotJson.1983}; - let TotallyNotJson.1952 : {} = Struct {}; + let TotallyNotJson.1959 : U64 = 0i64; + let TotallyNotJson.1960 : Int1 = true; + let TotallyNotJson.186 : {U64, Int1} = Struct {TotallyNotJson.1959, TotallyNotJson.1960}; + let TotallyNotJson.1929 : {} = Struct {}; inc TotallyNotJson.185; - let TotallyNotJson.187 : {U64, Int1} = CallByName List.26 TotallyNotJson.185 TotallyNotJson.186 TotallyNotJson.1952; - let TotallyNotJson.1906 : Int1 = StructAtIndex 1 TotallyNotJson.187; - let TotallyNotJson.1950 : Int1 = true; - let TotallyNotJson.1951 : Int1 = lowlevel Eq TotallyNotJson.1950 TotallyNotJson.1906; - if TotallyNotJson.1951 then - let TotallyNotJson.1916 : U64 = CallByName List.6 TotallyNotJson.185; - let TotallyNotJson.1917 : U64 = 2i64; - let TotallyNotJson.1915 : U64 = CallByName Num.19 TotallyNotJson.1916 TotallyNotJson.1917; - let TotallyNotJson.1912 : List U8 = CallByName List.68 TotallyNotJson.1915; - let TotallyNotJson.1914 : U8 = 34i64; - let TotallyNotJson.1913 : List U8 = Array [TotallyNotJson.1914]; - let TotallyNotJson.1911 : List U8 = CallByName List.8 TotallyNotJson.1912 TotallyNotJson.1913; - let TotallyNotJson.1908 : List U8 = CallByName List.8 TotallyNotJson.1911 TotallyNotJson.185; - let TotallyNotJson.1910 : U8 = 34i64; - let TotallyNotJson.1909 : List U8 = Array [TotallyNotJson.1910]; - let TotallyNotJson.1907 : List U8 = CallByName List.8 TotallyNotJson.1908 TotallyNotJson.1909; - ret TotallyNotJson.1907; + let TotallyNotJson.187 : {U64, Int1} = CallByName List.26 TotallyNotJson.185 TotallyNotJson.186 TotallyNotJson.1929; + let TotallyNotJson.1883 : Int1 = StructAtIndex 1 TotallyNotJson.187; + let TotallyNotJson.1927 : Int1 = true; + let TotallyNotJson.1928 : Int1 = lowlevel Eq TotallyNotJson.1927 TotallyNotJson.1883; + if TotallyNotJson.1928 then + let TotallyNotJson.1893 : U64 = CallByName List.6 TotallyNotJson.185; + let TotallyNotJson.1894 : U64 = 2i64; + let TotallyNotJson.1892 : U64 = CallByName Num.19 TotallyNotJson.1893 TotallyNotJson.1894; + let TotallyNotJson.1889 : List U8 = CallByName List.68 TotallyNotJson.1892; + let TotallyNotJson.1891 : U8 = 34i64; + let TotallyNotJson.1890 : List U8 = Array [TotallyNotJson.1891]; + let TotallyNotJson.1888 : List U8 = CallByName List.8 TotallyNotJson.1889 TotallyNotJson.1890; + let TotallyNotJson.1885 : List U8 = CallByName List.8 TotallyNotJson.1888 TotallyNotJson.185; + let TotallyNotJson.1887 : U8 = 34i64; + let TotallyNotJson.1886 : List U8 = Array [TotallyNotJson.1887]; + let TotallyNotJson.1884 : List U8 = CallByName List.8 TotallyNotJson.1885 TotallyNotJson.1886; + ret TotallyNotJson.1884; else inc TotallyNotJson.185; - let TotallyNotJson.1949 : U64 = StructAtIndex 0 TotallyNotJson.187; - let TotallyNotJson.1948 : {List U8, List U8} = CallByName List.52 TotallyNotJson.185 TotallyNotJson.1949; - let TotallyNotJson.211 : List U8 = StructAtIndex 0 TotallyNotJson.1948; - let TotallyNotJson.213 : List U8 = StructAtIndex 1 TotallyNotJson.1948; - let TotallyNotJson.1946 : U64 = CallByName List.6 TotallyNotJson.185; + let TotallyNotJson.1926 : U64 = StructAtIndex 0 TotallyNotJson.187; + let TotallyNotJson.1925 : {List U8, List U8} = CallByName List.52 TotallyNotJson.185 TotallyNotJson.1926; + let TotallyNotJson.211 : List U8 = StructAtIndex 0 TotallyNotJson.1925; + let TotallyNotJson.213 : List U8 = StructAtIndex 1 TotallyNotJson.1925; + let TotallyNotJson.1923 : U64 = CallByName List.6 TotallyNotJson.185; dec TotallyNotJson.185; - let TotallyNotJson.1947 : U64 = 120i64; - let TotallyNotJson.1944 : U64 = CallByName Num.21 TotallyNotJson.1946 TotallyNotJson.1947; - let TotallyNotJson.1945 : U64 = 100i64; - let TotallyNotJson.1943 : U64 = CallByName Num.94 TotallyNotJson.1944 TotallyNotJson.1945; - let TotallyNotJson.1940 : List U8 = CallByName List.68 TotallyNotJson.1943; - let TotallyNotJson.1942 : U8 = 34i64; - let TotallyNotJson.1941 : List U8 = Array [TotallyNotJson.1942]; - let TotallyNotJson.1939 : List U8 = CallByName List.8 TotallyNotJson.1940 TotallyNotJson.1941; - let TotallyNotJson.214 : List U8 = CallByName List.8 TotallyNotJson.1939 TotallyNotJson.211; - let TotallyNotJson.1922 : {} = Struct {}; - let TotallyNotJson.1919 : List U8 = CallByName List.18 TotallyNotJson.213 TotallyNotJson.214 TotallyNotJson.1922; - let TotallyNotJson.1921 : U8 = 34i64; - let TotallyNotJson.1920 : List U8 = Array [TotallyNotJson.1921]; - let TotallyNotJson.1918 : List U8 = CallByName List.8 TotallyNotJson.1919 TotallyNotJson.1920; - ret TotallyNotJson.1918; + let TotallyNotJson.1924 : U64 = 120i64; + let TotallyNotJson.1921 : U64 = CallByName Num.21 TotallyNotJson.1923 TotallyNotJson.1924; + let TotallyNotJson.1922 : U64 = 100i64; + let TotallyNotJson.1920 : U64 = CallByName Num.94 TotallyNotJson.1921 TotallyNotJson.1922; + let TotallyNotJson.1917 : List U8 = CallByName List.68 TotallyNotJson.1920; + let TotallyNotJson.1919 : U8 = 34i64; + let TotallyNotJson.1918 : List U8 = Array [TotallyNotJson.1919]; + let TotallyNotJson.1916 : List U8 = CallByName List.8 TotallyNotJson.1917 TotallyNotJson.1918; + let TotallyNotJson.214 : List U8 = CallByName List.8 TotallyNotJson.1916 TotallyNotJson.211; + let TotallyNotJson.1899 : {} = Struct {}; + let TotallyNotJson.1896 : List U8 = CallByName List.18 TotallyNotJson.213 TotallyNotJson.214 TotallyNotJson.1899; + let TotallyNotJson.1898 : U8 = 34i64; + let TotallyNotJson.1897 : List U8 = Array [TotallyNotJson.1898]; + let TotallyNotJson.1895 : List U8 = CallByName List.8 TotallyNotJson.1896 TotallyNotJson.1897; + ret TotallyNotJson.1895; procedure TotallyNotJson.27 (TotallyNotJson.218): switch TotallyNotJson.218: case 34: - let TotallyNotJson.1925 : List U8 = Array [92i64, 34i64]; - ret TotallyNotJson.1925; + let TotallyNotJson.1902 : List U8 = Array [92i64, 34i64]; + ret TotallyNotJson.1902; case 92: - let TotallyNotJson.1926 : List U8 = Array [92i64, 92i64]; - ret TotallyNotJson.1926; + let TotallyNotJson.1903 : List U8 = Array [92i64, 92i64]; + ret TotallyNotJson.1903; case 47: - let TotallyNotJson.1927 : List U8 = Array [92i64, 47i64]; - ret TotallyNotJson.1927; + let TotallyNotJson.1904 : List U8 = Array [92i64, 47i64]; + ret TotallyNotJson.1904; case 8: - let TotallyNotJson.1929 : U8 = 98i64; - let TotallyNotJson.1928 : List U8 = Array [92i64, TotallyNotJson.1929]; - ret TotallyNotJson.1928; + let TotallyNotJson.1906 : U8 = 98i64; + let TotallyNotJson.1905 : List U8 = Array [92i64, TotallyNotJson.1906]; + ret TotallyNotJson.1905; case 12: - let TotallyNotJson.1931 : U8 = 102i64; - let TotallyNotJson.1930 : List U8 = Array [92i64, TotallyNotJson.1931]; - ret TotallyNotJson.1930; + let TotallyNotJson.1908 : U8 = 102i64; + let TotallyNotJson.1907 : List U8 = Array [92i64, TotallyNotJson.1908]; + ret TotallyNotJson.1907; case 10: - let TotallyNotJson.1933 : U8 = 110i64; - let TotallyNotJson.1932 : List U8 = Array [92i64, TotallyNotJson.1933]; - ret TotallyNotJson.1932; + let TotallyNotJson.1910 : U8 = 110i64; + let TotallyNotJson.1909 : List U8 = Array [92i64, TotallyNotJson.1910]; + ret TotallyNotJson.1909; case 13: - let TotallyNotJson.1935 : U8 = 114i64; - let TotallyNotJson.1934 : List U8 = Array [92i64, TotallyNotJson.1935]; - ret TotallyNotJson.1934; + let TotallyNotJson.1912 : U8 = 114i64; + let TotallyNotJson.1911 : List U8 = Array [92i64, TotallyNotJson.1912]; + ret TotallyNotJson.1911; case 9: - let TotallyNotJson.1937 : U8 = 114i64; - let TotallyNotJson.1936 : List U8 = Array [92i64, TotallyNotJson.1937]; - ret TotallyNotJson.1936; + let TotallyNotJson.1914 : U8 = 114i64; + let TotallyNotJson.1913 : List U8 = Array [92i64, TotallyNotJson.1914]; + ret TotallyNotJson.1913; default: - let TotallyNotJson.1938 : List U8 = Array [TotallyNotJson.218]; - ret TotallyNotJson.1938; + let TotallyNotJson.1915 : List U8 = Array [TotallyNotJson.218]; + ret TotallyNotJson.1915; procedure TotallyNotJson.29 (TotallyNotJson.233): @@ -1297,83 +1297,83 @@ procedure TotallyNotJson.29 (TotallyNotJson.233): ret TotallyNotJson.1173; procedure TotallyNotJson.29 (TotallyNotJson.233): - let TotallyNotJson.1549 : List {Str, Str} = CallByName Encode.23 TotallyNotJson.233; - ret TotallyNotJson.1549; + let TotallyNotJson.1526 : List {Str, Str} = CallByName Encode.23 TotallyNotJson.233; + ret TotallyNotJson.1526; procedure TotallyNotJson.82 (TotallyNotJson.802, TotallyNotJson.803): - let TotallyNotJson.1896 : U8 = GetTagId TotallyNotJson.803; - switch TotallyNotJson.1896: + let TotallyNotJson.1873 : U8 = GetTagId TotallyNotJson.803; + switch TotallyNotJson.1873: case 2: ret TotallyNotJson.802; case 5: - let TotallyNotJson.1581 : Str = CallByName TotallyNotJson.87 TotallyNotJson.802; - ret TotallyNotJson.1581; + let TotallyNotJson.1558 : Str = CallByName TotallyNotJson.87 TotallyNotJson.802; + ret TotallyNotJson.1558; case 4: - let TotallyNotJson.1771 : Str = CallByName TotallyNotJson.88 TotallyNotJson.802; - ret TotallyNotJson.1771; + let TotallyNotJson.1748 : Str = CallByName TotallyNotJson.88 TotallyNotJson.802; + ret TotallyNotJson.1748; case 3: - let TotallyNotJson.1860 : Str = CallByName TotallyNotJson.89 TotallyNotJson.802; - ret TotallyNotJson.1860; + let TotallyNotJson.1837 : Str = CallByName TotallyNotJson.89 TotallyNotJson.802; + ret TotallyNotJson.1837; case 0: - let TotallyNotJson.1892 : Str = CallByName TotallyNotJson.90 TotallyNotJson.802; - ret TotallyNotJson.1892; + let TotallyNotJson.1869 : Str = CallByName TotallyNotJson.90 TotallyNotJson.802; + ret TotallyNotJson.1869; default: dec TotallyNotJson.802; let TotallyNotJson.804 : [] = UnionAtIndex (Id 1) (Index 0) TotallyNotJson.803; - let TotallyNotJson.1895 : Str = "a Lambda Set is empty. Most likely there is a type error in your program."; - Crash TotallyNotJson.1895 + let TotallyNotJson.1872 : Str = "a Lambda Set is empty. Most likely there is a type error in your program."; + Crash TotallyNotJson.1872 procedure TotallyNotJson.832 (TotallyNotJson.1493): - let TotallyNotJson.1868 : List Str = StructAtIndex 1 TotallyNotJson.1493; - let #Derived_gen.30 : List Str = StructAtIndex 0 TotallyNotJson.1493; - dec #Derived_gen.30; - ret TotallyNotJson.1868; + let TotallyNotJson.1845 : List Str = StructAtIndex 1 TotallyNotJson.1493; + let #Derived_gen.59 : List Str = StructAtIndex 0 TotallyNotJson.1493; + dec #Derived_gen.59; + ret TotallyNotJson.1845; procedure TotallyNotJson.840 (TotallyNotJson.1214): - let TotallyNotJson.1589 : List Str = StructAtIndex 1 TotallyNotJson.1214; - let #Derived_gen.28 : List Str = StructAtIndex 0 TotallyNotJson.1214; - dec #Derived_gen.28; - ret TotallyNotJson.1589; + let TotallyNotJson.1566 : List Str = StructAtIndex 1 TotallyNotJson.1214; + let #Derived_gen.57 : List Str = StructAtIndex 0 TotallyNotJson.1214; + dec #Derived_gen.57; + ret TotallyNotJson.1566; procedure TotallyNotJson.87 (TotallyNotJson.809): - let TotallyNotJson.1582 : Str = CallByName TotallyNotJson.97 TotallyNotJson.809; - ret TotallyNotJson.1582; + let TotallyNotJson.1559 : Str = CallByName TotallyNotJson.97 TotallyNotJson.809; + ret TotallyNotJson.1559; procedure TotallyNotJson.88 (TotallyNotJson.810): - let TotallyNotJson.1772 : Str = CallByName TotallyNotJson.94 TotallyNotJson.810; - ret TotallyNotJson.1772; + let TotallyNotJson.1749 : Str = CallByName TotallyNotJson.94 TotallyNotJson.810; + ret TotallyNotJson.1749; procedure TotallyNotJson.89 (TotallyNotJson.811): - let TotallyNotJson.1861 : Str = CallByName TotallyNotJson.95 TotallyNotJson.811; - ret TotallyNotJson.1861; + let TotallyNotJson.1838 : Str = CallByName TotallyNotJson.95 TotallyNotJson.811; + ret TotallyNotJson.1838; procedure TotallyNotJson.90 (TotallyNotJson.812): ret TotallyNotJson.812; procedure TotallyNotJson.94 (TotallyNotJson.824): let TotallyNotJson.825 : List Str = CallByName Str.55 TotallyNotJson.824; - let TotallyNotJson.1857 : U64 = lowlevel ListLen TotallyNotJson.825; - let TotallyNotJson.1858 : U64 = 1i64; - let TotallyNotJson.1859 : Int1 = lowlevel NumGte TotallyNotJson.1857 TotallyNotJson.1858; - if TotallyNotJson.1859 then + let TotallyNotJson.1834 : U64 = lowlevel ListLen TotallyNotJson.825; + let TotallyNotJson.1835 : U64 = 1i64; + let TotallyNotJson.1836 : Int1 = lowlevel NumGte TotallyNotJson.1834 TotallyNotJson.1835; + if TotallyNotJson.1836 then dec TotallyNotJson.824; - let TotallyNotJson.1856 : U64 = 0i64; - let TotallyNotJson.826 : Str = lowlevel ListGetUnsafe TotallyNotJson.825 TotallyNotJson.1856; + let TotallyNotJson.1833 : U64 = 0i64; + let TotallyNotJson.826 : Str = lowlevel ListGetUnsafe TotallyNotJson.825 TotallyNotJson.1833; inc TotallyNotJson.826; let TotallyNotJson.827 : Str = CallByName TotallyNotJson.100 TotallyNotJson.826; let TotallyNotJson.828 : List Str = CallByName List.38 TotallyNotJson.825; - let TotallyNotJson.1774 : List Str = CallByName List.13 TotallyNotJson.828 TotallyNotJson.827; - let TotallyNotJson.1775 : Str = ""; - let TotallyNotJson.1773 : Str = CallByName Str.4 TotallyNotJson.1774 TotallyNotJson.1775; - dec TotallyNotJson.1774; - dec TotallyNotJson.1775; - ret TotallyNotJson.1773; + let TotallyNotJson.1751 : List Str = CallByName List.13 TotallyNotJson.828 TotallyNotJson.827; + let TotallyNotJson.1752 : Str = ""; + let TotallyNotJson.1750 : Str = CallByName Str.4 TotallyNotJson.1751 TotallyNotJson.1752; + dec TotallyNotJson.1752; + dec TotallyNotJson.1751; + ret TotallyNotJson.1750; else dec TotallyNotJson.825; ret TotallyNotJson.824; @@ -1381,108 +1381,108 @@ procedure TotallyNotJson.94 (TotallyNotJson.824): procedure TotallyNotJson.95 (TotallyNotJson.829): let TotallyNotJson.830 : List Str = CallByName Str.55 TotallyNotJson.829; dec TotallyNotJson.829; - let TotallyNotJson.1891 : U64 = CallByName List.6 TotallyNotJson.830; - let TotallyNotJson.831 : List Str = CallByName List.68 TotallyNotJson.1891; - let TotallyNotJson.1869 : {List Str, List Str} = Struct {TotallyNotJson.830, TotallyNotJson.831}; - let TotallyNotJson.1865 : {List Str, List Str} = CallByName TotallyNotJson.96 TotallyNotJson.1869; - let TotallyNotJson.1866 : {} = Struct {}; - let TotallyNotJson.1863 : List Str = CallByName TotallyNotJson.832 TotallyNotJson.1865; - let TotallyNotJson.1864 : Str = ""; - let TotallyNotJson.1862 : Str = CallByName Str.4 TotallyNotJson.1863 TotallyNotJson.1864; - dec TotallyNotJson.1864; - dec TotallyNotJson.1863; - ret TotallyNotJson.1862; - -procedure TotallyNotJson.96 (TotallyNotJson.2004): - joinpoint TotallyNotJson.1870 TotallyNotJson.1168: + let TotallyNotJson.1868 : U64 = CallByName List.6 TotallyNotJson.830; + let TotallyNotJson.831 : List Str = CallByName List.68 TotallyNotJson.1868; + let TotallyNotJson.1846 : {List Str, List Str} = Struct {TotallyNotJson.830, TotallyNotJson.831}; + let TotallyNotJson.1842 : {List Str, List Str} = CallByName TotallyNotJson.96 TotallyNotJson.1846; + let TotallyNotJson.1843 : {} = Struct {}; + let TotallyNotJson.1840 : List Str = CallByName TotallyNotJson.832 TotallyNotJson.1842; + let TotallyNotJson.1841 : Str = ""; + let TotallyNotJson.1839 : Str = CallByName Str.4 TotallyNotJson.1840 TotallyNotJson.1841; + dec TotallyNotJson.1840; + dec TotallyNotJson.1841; + ret TotallyNotJson.1839; + +procedure TotallyNotJson.96 (#Derived_gen.41): + joinpoint TotallyNotJson.1847 TotallyNotJson.1168: let TotallyNotJson.834 : List Str = StructAtIndex 0 TotallyNotJson.1168; let TotallyNotJson.833 : List Str = StructAtIndex 1 TotallyNotJson.1168; - let TotallyNotJson.1888 : U64 = lowlevel ListLen TotallyNotJson.834; - let TotallyNotJson.1889 : U64 = 1i64; - let TotallyNotJson.1890 : Int1 = lowlevel NumGte TotallyNotJson.1888 TotallyNotJson.1889; - if TotallyNotJson.1890 then - let TotallyNotJson.1887 : U64 = 0i64; - let TotallyNotJson.835 : Str = lowlevel ListGetUnsafe TotallyNotJson.834 TotallyNotJson.1887; + let TotallyNotJson.1865 : U64 = lowlevel ListLen TotallyNotJson.834; + let TotallyNotJson.1866 : U64 = 1i64; + let TotallyNotJson.1867 : Int1 = lowlevel NumGte TotallyNotJson.1865 TotallyNotJson.1866; + if TotallyNotJson.1867 then + let TotallyNotJson.1864 : U64 = 0i64; + let TotallyNotJson.835 : Str = lowlevel ListGetUnsafe TotallyNotJson.834 TotallyNotJson.1864; inc 2 TotallyNotJson.835; - joinpoint TotallyNotJson.1885 TotallyNotJson.1884: - if TotallyNotJson.1884 then - let TotallyNotJson.1874 : List Str = CallByName List.38 TotallyNotJson.834; - let TotallyNotJson.1877 : Str = "-"; - let TotallyNotJson.1878 : Str = CallByName TotallyNotJson.101 TotallyNotJson.835; - let TotallyNotJson.1876 : List Str = Array [TotallyNotJson.1877, TotallyNotJson.1878]; - let TotallyNotJson.1875 : List Str = CallByName List.8 TotallyNotJson.833 TotallyNotJson.1876; - let TotallyNotJson.1873 : {List Str, List Str} = Struct {TotallyNotJson.1874, TotallyNotJson.1875}; - jump TotallyNotJson.1870 TotallyNotJson.1873; + joinpoint TotallyNotJson.1862 TotallyNotJson.1861: + if TotallyNotJson.1861 then + let TotallyNotJson.1851 : List Str = CallByName List.38 TotallyNotJson.834; + let TotallyNotJson.1854 : Str = "-"; + let TotallyNotJson.1855 : Str = CallByName TotallyNotJson.101 TotallyNotJson.835; + let TotallyNotJson.1853 : List Str = Array [TotallyNotJson.1854, TotallyNotJson.1855]; + let TotallyNotJson.1852 : List Str = CallByName List.8 TotallyNotJson.833 TotallyNotJson.1853; + let TotallyNotJson.1850 : {List Str, List Str} = Struct {TotallyNotJson.1851, TotallyNotJson.1852}; + jump TotallyNotJson.1847 TotallyNotJson.1850; else dec TotallyNotJson.835; - let TotallyNotJson.1883 : U64 = 0i64; - let TotallyNotJson.836 : Str = lowlevel ListGetUnsafe TotallyNotJson.834 TotallyNotJson.1883; + let TotallyNotJson.1860 : U64 = 0i64; + let TotallyNotJson.836 : Str = lowlevel ListGetUnsafe TotallyNotJson.834 TotallyNotJson.1860; inc TotallyNotJson.836; - let TotallyNotJson.1881 : List Str = CallByName List.38 TotallyNotJson.834; - let TotallyNotJson.1882 : List Str = CallByName List.4 TotallyNotJson.833 TotallyNotJson.836; - let TotallyNotJson.1880 : {List Str, List Str} = Struct {TotallyNotJson.1881, TotallyNotJson.1882}; - jump TotallyNotJson.1870 TotallyNotJson.1880; + let TotallyNotJson.1858 : List Str = CallByName List.38 TotallyNotJson.834; + let TotallyNotJson.1859 : List Str = CallByName List.4 TotallyNotJson.833 TotallyNotJson.836; + let TotallyNotJson.1857 : {List Str, List Str} = Struct {TotallyNotJson.1858, TotallyNotJson.1859}; + jump TotallyNotJson.1847 TotallyNotJson.1857; in - let TotallyNotJson.1886 : Int1 = CallByName TotallyNotJson.102 TotallyNotJson.835; - jump TotallyNotJson.1885 TotallyNotJson.1886; + let TotallyNotJson.1863 : Int1 = CallByName TotallyNotJson.102 TotallyNotJson.835; + jump TotallyNotJson.1862 TotallyNotJson.1863; else - let TotallyNotJson.1871 : {List Str, List Str} = Struct {TotallyNotJson.834, TotallyNotJson.833}; - ret TotallyNotJson.1871; + let TotallyNotJson.1848 : {List Str, List Str} = Struct {TotallyNotJson.834, TotallyNotJson.833}; + ret TotallyNotJson.1848; in - jump TotallyNotJson.1870 TotallyNotJson.2004; + jump TotallyNotJson.1847 #Derived_gen.41; procedure TotallyNotJson.97 (TotallyNotJson.837): let TotallyNotJson.838 : List Str = CallByName Str.55 TotallyNotJson.837; dec TotallyNotJson.837; - let TotallyNotJson.1770 : U64 = CallByName List.6 TotallyNotJson.838; - let TotallyNotJson.839 : List Str = CallByName List.68 TotallyNotJson.1770; - let TotallyNotJson.1590 : {List Str, List Str} = Struct {TotallyNotJson.838, TotallyNotJson.839}; - let TotallyNotJson.1586 : {List Str, List Str} = CallByName TotallyNotJson.98 TotallyNotJson.1590; - let TotallyNotJson.1587 : {} = Struct {}; - let TotallyNotJson.1584 : List Str = CallByName TotallyNotJson.840 TotallyNotJson.1586; - let TotallyNotJson.1585 : Str = ""; - let TotallyNotJson.1583 : Str = CallByName Str.4 TotallyNotJson.1584 TotallyNotJson.1585; - dec TotallyNotJson.1585; - dec TotallyNotJson.1584; - ret TotallyNotJson.1583; - -procedure TotallyNotJson.98 (TotallyNotJson.1995): - joinpoint TotallyNotJson.1591 TotallyNotJson.1169: + let TotallyNotJson.1747 : U64 = CallByName List.6 TotallyNotJson.838; + let TotallyNotJson.839 : List Str = CallByName List.68 TotallyNotJson.1747; + let TotallyNotJson.1567 : {List Str, List Str} = Struct {TotallyNotJson.838, TotallyNotJson.839}; + let TotallyNotJson.1563 : {List Str, List Str} = CallByName TotallyNotJson.98 TotallyNotJson.1567; + let TotallyNotJson.1564 : {} = Struct {}; + let TotallyNotJson.1561 : List Str = CallByName TotallyNotJson.840 TotallyNotJson.1563; + let TotallyNotJson.1562 : Str = ""; + let TotallyNotJson.1560 : Str = CallByName Str.4 TotallyNotJson.1561 TotallyNotJson.1562; + dec TotallyNotJson.1561; + dec TotallyNotJson.1562; + ret TotallyNotJson.1560; + +procedure TotallyNotJson.98 (#Derived_gen.47): + joinpoint TotallyNotJson.1568 TotallyNotJson.1169: let TotallyNotJson.842 : List Str = StructAtIndex 0 TotallyNotJson.1169; let TotallyNotJson.841 : List Str = StructAtIndex 1 TotallyNotJson.1169; - let TotallyNotJson.1767 : U64 = lowlevel ListLen TotallyNotJson.842; - let TotallyNotJson.1768 : U64 = 1i64; - let TotallyNotJson.1769 : Int1 = lowlevel NumGte TotallyNotJson.1767 TotallyNotJson.1768; - if TotallyNotJson.1769 then - let TotallyNotJson.1766 : U64 = 0i64; - let TotallyNotJson.843 : Str = lowlevel ListGetUnsafe TotallyNotJson.842 TotallyNotJson.1766; + let TotallyNotJson.1744 : U64 = lowlevel ListLen TotallyNotJson.842; + let TotallyNotJson.1745 : U64 = 1i64; + let TotallyNotJson.1746 : Int1 = lowlevel NumGte TotallyNotJson.1744 TotallyNotJson.1745; + if TotallyNotJson.1746 then + let TotallyNotJson.1743 : U64 = 0i64; + let TotallyNotJson.843 : Str = lowlevel ListGetUnsafe TotallyNotJson.842 TotallyNotJson.1743; inc 2 TotallyNotJson.843; - joinpoint TotallyNotJson.1685 TotallyNotJson.1684: - if TotallyNotJson.1684 then - let TotallyNotJson.1595 : List Str = CallByName List.38 TotallyNotJson.842; - let TotallyNotJson.1598 : Str = "_"; - let TotallyNotJson.1599 : Str = CallByName TotallyNotJson.101 TotallyNotJson.843; - let TotallyNotJson.1597 : List Str = Array [TotallyNotJson.1598, TotallyNotJson.1599]; - let TotallyNotJson.1596 : List Str = CallByName List.8 TotallyNotJson.841 TotallyNotJson.1597; - let TotallyNotJson.1594 : {List Str, List Str} = Struct {TotallyNotJson.1595, TotallyNotJson.1596}; - jump TotallyNotJson.1591 TotallyNotJson.1594; + joinpoint TotallyNotJson.1662 TotallyNotJson.1661: + if TotallyNotJson.1661 then + let TotallyNotJson.1572 : List Str = CallByName List.38 TotallyNotJson.842; + let TotallyNotJson.1575 : Str = "_"; + let TotallyNotJson.1576 : Str = CallByName TotallyNotJson.101 TotallyNotJson.843; + let TotallyNotJson.1574 : List Str = Array [TotallyNotJson.1575, TotallyNotJson.1576]; + let TotallyNotJson.1573 : List Str = CallByName List.8 TotallyNotJson.841 TotallyNotJson.1574; + let TotallyNotJson.1571 : {List Str, List Str} = Struct {TotallyNotJson.1572, TotallyNotJson.1573}; + jump TotallyNotJson.1568 TotallyNotJson.1571; else dec TotallyNotJson.843; - let TotallyNotJson.1683 : U64 = 0i64; - let TotallyNotJson.844 : Str = lowlevel ListGetUnsafe TotallyNotJson.842 TotallyNotJson.1683; + let TotallyNotJson.1660 : U64 = 0i64; + let TotallyNotJson.844 : Str = lowlevel ListGetUnsafe TotallyNotJson.842 TotallyNotJson.1660; inc TotallyNotJson.844; - let TotallyNotJson.1681 : List Str = CallByName List.38 TotallyNotJson.842; - let TotallyNotJson.1682 : List Str = CallByName List.4 TotallyNotJson.841 TotallyNotJson.844; - let TotallyNotJson.1680 : {List Str, List Str} = Struct {TotallyNotJson.1681, TotallyNotJson.1682}; - jump TotallyNotJson.1591 TotallyNotJson.1680; + let TotallyNotJson.1658 : List Str = CallByName List.38 TotallyNotJson.842; + let TotallyNotJson.1659 : List Str = CallByName List.4 TotallyNotJson.841 TotallyNotJson.844; + let TotallyNotJson.1657 : {List Str, List Str} = Struct {TotallyNotJson.1658, TotallyNotJson.1659}; + jump TotallyNotJson.1568 TotallyNotJson.1657; in - let TotallyNotJson.1686 : Int1 = CallByName TotallyNotJson.102 TotallyNotJson.843; - jump TotallyNotJson.1685 TotallyNotJson.1686; + let TotallyNotJson.1663 : Int1 = CallByName TotallyNotJson.102 TotallyNotJson.843; + jump TotallyNotJson.1662 TotallyNotJson.1663; else - let TotallyNotJson.1592 : {List Str, List Str} = Struct {TotallyNotJson.842, TotallyNotJson.841}; - ret TotallyNotJson.1592; + let TotallyNotJson.1569 : {List Str, List Str} = Struct {TotallyNotJson.842, TotallyNotJson.841}; + ret TotallyNotJson.1569; in - jump TotallyNotJson.1591 TotallyNotJson.1995; + jump TotallyNotJson.1568 #Derived_gen.47; procedure Test.0 (): let Test.12 : Str = "bar"; 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 index 5aff466792c..a8e9541cdfa 100644 --- 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 @@ -37,12 +37,12 @@ procedure Encode.24 (Encode.99, Encode.107, Encode.101): ret Encode.111; procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.118 : List U8 = CallByName TotallyNotJson.234 Encode.99 Encode.101 Encode.107; - ret Encode.118; + let Encode.113 : List U8 = CallByName TotallyNotJson.234 Encode.99 Encode.101 Encode.107; + ret Encode.113; procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.121 : List U8 = CallByName TotallyNotJson.182 Encode.99 Encode.101 Encode.107; - ret Encode.121; + let Encode.116 : List U8 = CallByName TotallyNotJson.182 Encode.99 Encode.101 Encode.107; + ret Encode.116; procedure Encode.26 (Encode.105, Encode.106): let Encode.109 : List U8 = Array []; @@ -51,221 +51,221 @@ procedure Encode.26 (Encode.105, Encode.106): ret Encode.108; procedure List.13 (#Attr.2, #Attr.3): - let List.622 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; - ret List.622; + let List.621 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; + ret List.621; procedure List.145 (List.146, List.147, List.144): - let List.570 : {List U8, U64} = CallByName TotallyNotJson.237 List.146 List.147 List.144; - ret List.570; + let List.569 : {List U8, U64} = CallByName TotallyNotJson.237 List.146 List.147 List.144; + ret List.569; procedure List.145 (List.146, List.147, List.144): - let List.590 : List U8 = CallByName TotallyNotJson.215 List.146 List.147; - ret List.590; + let List.589 : List U8 = CallByName TotallyNotJson.215 List.146 List.147; + ret List.589; procedure List.18 (List.142, List.143, List.144): - let List.551 : {List U8, U64} = CallByName List.93 List.142 List.143 List.144; - ret List.551; + let List.550 : {List U8, U64} = CallByName List.93 List.142 List.143 List.144; + ret List.550; procedure List.18 (List.142, List.143, List.144): - let List.571 : List U8 = CallByName List.93 List.142 List.143 List.144; - ret List.571; + let List.570 : List U8 = CallByName List.93 List.142 List.143 List.144; + ret List.570; procedure List.26 (List.159, List.160, List.161): - let List.639 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.93 List.159 List.160 List.161; - let List.642 : U8 = 1i64; - let List.643 : U8 = GetTagId List.639; - let List.644 : Int1 = lowlevel Eq List.642 List.643; - if List.644 then - let List.162 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.639; + let List.638 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.93 List.159 List.160 List.161; + let List.641 : U8 = 1i64; + let List.642 : U8 = GetTagId List.638; + let List.643 : Int1 = lowlevel Eq List.641 List.642; + if List.643 then + let List.162 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.638; ret List.162; else - let List.163 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.639; + let List.163 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.638; ret List.163; procedure List.31 (#Attr.2, #Attr.3): - let List.604 : List Str = lowlevel ListDropAt #Attr.2 #Attr.3; - ret List.604; + let List.603 : List Str = lowlevel ListDropAt #Attr.2 #Attr.3; + ret List.603; procedure List.38 (List.298): - let List.612 : U64 = 0i64; - let List.611 : List Str = CallByName List.31 List.298 List.612; - ret List.611; + let List.611 : U64 = 0i64; + let List.610 : List Str = CallByName List.31 List.298 List.611; + ret List.610; procedure List.4 (List.113, List.114): - let List.547 : U64 = 1i64; - let List.546 : List Str = CallByName List.70 List.113 List.547; - let List.545 : List Str = CallByName List.71 List.546 List.114; - ret List.545; + let List.546 : U64 = 1i64; + let List.545 : List Str = CallByName List.70 List.113 List.546; + let List.544 : List Str = CallByName List.71 List.545 List.114; + ret List.544; procedure List.4 (List.113, List.114): - let List.550 : U64 = 1i64; - let List.549 : List U8 = CallByName List.70 List.113 List.550; - let List.548 : List U8 = CallByName List.71 List.549 List.114; - ret List.548; + let List.549 : U64 = 1i64; + let List.548 : List U8 = CallByName List.70 List.113 List.549; + let List.547 : List U8 = CallByName List.71 List.548 List.114; + ret List.547; procedure List.49 (List.376, List.377): - let List.631 : U64 = StructAtIndex 0 List.377; - let List.632 : U64 = 0i64; - let List.629 : Int1 = CallByName Bool.11 List.631 List.632; - if List.629 then + let List.630 : U64 = StructAtIndex 0 List.377; + let List.631 : U64 = 0i64; + let List.628 : Int1 = CallByName Bool.11 List.630 List.631; + if List.628 then dec List.376; - let List.630 : List U8 = Array []; - ret List.630; + let List.629 : List U8 = Array []; + ret List.629; else - let List.626 : U64 = StructAtIndex 1 List.377; - let List.627 : U64 = StructAtIndex 0 List.377; - let List.625 : List U8 = CallByName List.72 List.376 List.626 List.627; - ret List.625; + let List.625 : U64 = StructAtIndex 1 List.377; + let List.626 : U64 = StructAtIndex 0 List.377; + let List.624 : List U8 = CallByName List.72 List.376 List.625 List.626; + ret List.624; procedure List.52 (List.391, List.392): let List.393 : U64 = CallByName List.6 List.391; - joinpoint List.637 List.394: - let List.635 : U64 = 0i64; - let List.634 : {U64, U64} = Struct {List.394, List.635}; + joinpoint List.636 List.394: + let List.634 : U64 = 0i64; + let List.633 : {U64, U64} = Struct {List.394, List.634}; inc List.391; - let List.395 : List U8 = CallByName List.49 List.391 List.634; - let List.633 : U64 = CallByName Num.20 List.393 List.394; - let List.624 : {U64, U64} = Struct {List.633, List.394}; - let List.396 : List U8 = CallByName List.49 List.391 List.624; - let List.623 : {List U8, List U8} = Struct {List.395, List.396}; - ret List.623; + let List.395 : List U8 = CallByName List.49 List.391 List.633; + let List.632 : U64 = CallByName Num.20 List.393 List.394; + let List.623 : {U64, U64} = Struct {List.632, List.394}; + let List.396 : List U8 = CallByName List.49 List.391 List.623; + let List.622 : {List U8, List U8} = Struct {List.395, List.396}; + ret List.622; in - let List.638 : Int1 = CallByName Num.24 List.393 List.392; - if List.638 then - jump List.637 List.392; + let List.637 : Int1 = CallByName Num.24 List.393 List.392; + if List.637 then + jump List.636 List.392; else - jump List.637 List.393; + jump List.636 List.393; procedure List.6 (#Attr.2): - let List.618 : U64 = lowlevel ListLen #Attr.2; - ret List.618; + let List.617 : U64 = lowlevel ListLen #Attr.2; + ret List.617; procedure List.6 (#Attr.2): - let List.619 : U64 = lowlevel ListLen #Attr.2; - ret List.619; + let List.618 : U64 = lowlevel ListLen #Attr.2; + ret List.618; procedure List.6 (#Attr.2): - let List.621 : U64 = lowlevel ListLen #Attr.2; - ret List.621; + let List.620 : U64 = lowlevel ListLen #Attr.2; + ret List.620; procedure List.66 (#Attr.2, #Attr.3): - let List.567 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.567; + let List.566 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.566; procedure List.66 (#Attr.2, #Attr.3): - let List.587 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.587; + let List.586 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.586; procedure List.68 (#Attr.2): - let List.614 : List Str = lowlevel ListWithCapacity #Attr.2; - ret List.614; + let List.613 : List Str = lowlevel ListWithCapacity #Attr.2; + ret List.613; procedure List.68 (#Attr.2): - let List.616 : List U8 = lowlevel ListWithCapacity #Attr.2; - ret List.616; + let List.615 : List U8 = lowlevel ListWithCapacity #Attr.2; + ret List.615; procedure List.70 (#Attr.2, #Attr.3): - let List.527 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.527; + let List.526 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.526; procedure List.70 (#Attr.2, #Attr.3): - let List.544 : List Str = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.544; + let List.543 : List Str = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.543; procedure List.71 (#Attr.2, #Attr.3): - let List.525 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.525; + let List.524 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.524; procedure List.71 (#Attr.2, #Attr.3): - let List.542 : List Str = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.542; + let List.541 : List Str = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.541; procedure List.72 (#Attr.2, #Attr.3, #Attr.4): - let List.628 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; - ret List.628; + let List.627 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; + ret List.627; procedure List.8 (#Attr.2, #Attr.3): - let List.593 : List Str = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.593; + let List.592 : List Str = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.592; procedure List.8 (#Attr.2, #Attr.3): - let List.601 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.601; - -procedure List.80 (List.675, List.676, List.677, List.678, List.679): - joinpoint List.557 List.439 List.440 List.441 List.442 List.443: - let List.559 : Int1 = CallByName Num.22 List.442 List.443; - if List.559 then - let List.566 : {Str, Str} = CallByName List.66 List.439 List.442; - inc List.566; - let List.560 : {List U8, U64} = CallByName List.145 List.440 List.566 List.441; - let List.563 : U64 = 1i64; - let List.562 : U64 = CallByName Num.19 List.442 List.563; - jump List.557 List.439 List.560 List.441 List.562 List.443; + let List.600 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.600; + +procedure List.80 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15): + joinpoint List.556 List.439 List.440 List.441 List.442 List.443: + let List.558 : Int1 = CallByName Num.22 List.442 List.443; + if List.558 then + let List.565 : {Str, Str} = CallByName List.66 List.439 List.442; + inc List.565; + let List.559 : {List U8, U64} = CallByName List.145 List.440 List.565 List.441; + let List.562 : U64 = 1i64; + let List.561 : U64 = CallByName Num.19 List.442 List.562; + jump List.556 List.439 List.559 List.441 List.561 List.443; else dec List.439; ret List.440; in - jump List.557 List.675 List.676 List.677 List.678 List.679; - -procedure List.80 (List.692, List.693, List.694, List.695, List.696): - joinpoint List.577 List.439 List.440 List.441 List.442 List.443: - let List.579 : Int1 = CallByName Num.22 List.442 List.443; - if List.579 then - let List.586 : U8 = CallByName List.66 List.439 List.442; - let List.580 : List U8 = CallByName List.145 List.440 List.586 List.441; - let List.583 : U64 = 1i64; - let List.582 : U64 = CallByName Num.19 List.442 List.583; - jump List.577 List.439 List.580 List.441 List.582 List.443; + jump List.556 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15; + +procedure List.80 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_gen.19, #Derived_gen.20): + joinpoint List.647 List.439 List.440 List.441 List.442 List.443: + let List.649 : Int1 = CallByName Num.22 List.442 List.443; + if List.649 then + let List.658 : U8 = CallByName List.66 List.439 List.442; + let List.650 : [C {U64, Int1}, C {U64, Int1}] = CallByName TotallyNotJson.189 List.440 List.658; + let List.655 : U8 = 1i64; + let List.656 : U8 = GetTagId List.650; + let List.657 : Int1 = lowlevel Eq List.655 List.656; + if List.657 then + let List.444 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.650; + let List.653 : U64 = 1i64; + let List.652 : U64 = CallByName Num.19 List.442 List.653; + jump List.647 List.439 List.444 List.441 List.652 List.443; + else + dec List.439; + let List.445 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.650; + let List.654 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) List.445; + ret List.654; else dec List.439; - ret List.440; + let List.648 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440; + ret List.648; in - jump List.577 List.692 List.693 List.694 List.695 List.696; - -procedure List.80 (List.728, List.729, List.730, List.731, List.732): - joinpoint List.648 List.439 List.440 List.441 List.442 List.443: - let List.650 : Int1 = CallByName Num.22 List.442 List.443; - if List.650 then - let List.659 : U8 = CallByName List.66 List.439 List.442; - let List.651 : [C {U64, Int1}, C {U64, Int1}] = CallByName TotallyNotJson.189 List.440 List.659; - let List.656 : U8 = 1i64; - let List.657 : U8 = GetTagId List.651; - let List.658 : Int1 = lowlevel Eq List.656 List.657; - if List.658 then - let List.444 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.651; - let List.654 : U64 = 1i64; - let List.653 : U64 = CallByName Num.19 List.442 List.654; - jump List.648 List.439 List.444 List.441 List.653 List.443; - else - dec List.439; - let List.445 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.651; - let List.655 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) List.445; - ret List.655; + jump List.647 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20; + +procedure List.80 (#Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24, #Derived_gen.25): + joinpoint List.576 List.439 List.440 List.441 List.442 List.443: + let List.578 : Int1 = CallByName Num.22 List.442 List.443; + if List.578 then + let List.585 : U8 = CallByName List.66 List.439 List.442; + let List.579 : List U8 = CallByName List.145 List.440 List.585 List.441; + let List.582 : U64 = 1i64; + let List.581 : U64 = CallByName Num.19 List.442 List.582; + jump List.576 List.439 List.579 List.441 List.581 List.443; else dec List.439; - let List.649 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440; - ret List.649; + ret List.440; in - jump List.648 List.728 List.729 List.730 List.731 List.732; + jump List.576 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25; procedure List.93 (List.436, List.437, List.438): - let List.555 : U64 = 0i64; - let List.556 : U64 = CallByName List.6 List.436; - let List.554 : {List U8, U64} = CallByName List.80 List.436 List.437 List.438 List.555 List.556; - ret List.554; + let List.554 : U64 = 0i64; + let List.555 : U64 = CallByName List.6 List.436; + let List.553 : {List U8, U64} = CallByName List.80 List.436 List.437 List.438 List.554 List.555; + ret List.553; procedure List.93 (List.436, List.437, List.438): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.436; - let List.574 : List U8 = CallByName List.80 List.436 List.437 List.438 List.575 List.576; - ret List.574; + let List.574 : U64 = 0i64; + let List.575 : U64 = CallByName List.6 List.436; + let List.573 : List U8 = CallByName List.80 List.436 List.437 List.438 List.574 List.575; + ret List.573; procedure List.93 (List.436, List.437, List.438): - let List.646 : U64 = 0i64; - let List.647 : U64 = CallByName List.6 List.436; - let List.645 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.80 List.436 List.437 List.438 List.646 List.647; - ret List.645; + let List.645 : U64 = 0i64; + let List.646 : U64 = CallByName List.6 List.436; + let List.644 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.80 List.436 List.437 List.438 List.645 List.646; + ret List.644; procedure Num.127 (#Attr.2): let Num.297 : U8 = lowlevel NumIntCast #Attr.2; @@ -296,20 +296,20 @@ procedure Num.94 (#Attr.2, #Attr.3): ret Num.302; procedure Str.12 (#Attr.2): - let Str.313 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.313; + let Str.309 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.309; procedure Str.4 (#Attr.2, #Attr.3): - let Str.316 : Str = lowlevel StrJoinWith #Attr.2 #Attr.3; - ret Str.316; + let Str.312 : Str = lowlevel StrJoinWith #Attr.2 #Attr.3; + ret Str.312; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): let Str.307 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; ret Str.307; procedure Str.55 (#Attr.2): - let Str.319 : List Str = lowlevel StrGraphemes #Attr.2; - ret Str.319; + let Str.315 : List Str = lowlevel StrGraphemes #Attr.2; + ret Str.315; procedure Str.9 (Str.79): let Str.305 : U64 = 0i64; @@ -323,8 +323,8 @@ procedure Str.9 (Str.79): else let Str.300 : U8 = StructAtIndex 3 Str.80; let Str.301 : U64 = StructAtIndex 0 Str.80; - let #Derived_gen.16 : Str = StructAtIndex 1 Str.80; - dec #Derived_gen.16; + let #Derived_gen.38 : Str = StructAtIndex 1 Str.80; + dec #Derived_gen.38; let Str.299 : {U64, U8} = Struct {Str.301, Str.300}; let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299; ret Str.298; @@ -1215,14 +1215,14 @@ procedure TotallyNotJson.82 (TotallyNotJson.802, TotallyNotJson.803): procedure TotallyNotJson.832 (TotallyNotJson.1493): let TotallyNotJson.1494 : List Str = StructAtIndex 1 TotallyNotJson.1493; - let #Derived_gen.14 : List Str = StructAtIndex 0 TotallyNotJson.1493; - dec #Derived_gen.14; + let #Derived_gen.36 : List Str = StructAtIndex 0 TotallyNotJson.1493; + dec #Derived_gen.36; ret TotallyNotJson.1494; procedure TotallyNotJson.840 (TotallyNotJson.1214): let TotallyNotJson.1215 : List Str = StructAtIndex 1 TotallyNotJson.1214; - let #Derived_gen.15 : List Str = StructAtIndex 0 TotallyNotJson.1214; - dec #Derived_gen.15; + let #Derived_gen.37 : List Str = StructAtIndex 0 TotallyNotJson.1214; + dec #Derived_gen.37; ret TotallyNotJson.1215; procedure TotallyNotJson.87 (TotallyNotJson.809): @@ -1277,7 +1277,7 @@ procedure TotallyNotJson.95 (TotallyNotJson.829): dec TotallyNotJson.1489; ret TotallyNotJson.1488; -procedure TotallyNotJson.96 (TotallyNotJson.1630): +procedure TotallyNotJson.96 (#Derived_gen.32): joinpoint TotallyNotJson.1496 TotallyNotJson.1168: let TotallyNotJson.834 : List Str = StructAtIndex 0 TotallyNotJson.1168; let TotallyNotJson.833 : List Str = StructAtIndex 1 TotallyNotJson.1168; @@ -1313,7 +1313,7 @@ procedure TotallyNotJson.96 (TotallyNotJson.1630): let TotallyNotJson.1497 : {List Str, List Str} = Struct {TotallyNotJson.834, TotallyNotJson.833}; ret TotallyNotJson.1497; in - jump TotallyNotJson.1496 TotallyNotJson.1630; + jump TotallyNotJson.1496 #Derived_gen.32; procedure TotallyNotJson.97 (TotallyNotJson.837): let TotallyNotJson.838 : List Str = CallByName Str.55 TotallyNotJson.837; @@ -1330,7 +1330,7 @@ procedure TotallyNotJson.97 (TotallyNotJson.837): dec TotallyNotJson.1210; ret TotallyNotJson.1209; -procedure TotallyNotJson.98 (TotallyNotJson.1621): +procedure TotallyNotJson.98 (#Derived_gen.10): joinpoint TotallyNotJson.1217 TotallyNotJson.1169: let TotallyNotJson.842 : List Str = StructAtIndex 0 TotallyNotJson.1169; let TotallyNotJson.841 : List Str = StructAtIndex 1 TotallyNotJson.1169; @@ -1366,7 +1366,7 @@ procedure TotallyNotJson.98 (TotallyNotJson.1621): let TotallyNotJson.1218 : {List Str, List Str} = Struct {TotallyNotJson.842, TotallyNotJson.841}; ret TotallyNotJson.1218; in - jump TotallyNotJson.1217 TotallyNotJson.1621; + jump TotallyNotJson.1217 #Derived_gen.10; procedure Test.0 (): let Test.11 : Str = "foo"; 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 index 6ee53cde33a..5069fc23e17 100644 --- 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 @@ -44,12 +44,12 @@ procedure Encode.24 (Encode.99, Encode.107, Encode.101): ret Encode.111; procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.118 : List U8 = CallByName TotallyNotJson.234 Encode.99 Encode.101 Encode.107; - ret Encode.118; + let Encode.113 : List U8 = CallByName TotallyNotJson.234 Encode.99 Encode.101 Encode.107; + ret Encode.113; procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.122 : List U8 = CallByName TotallyNotJson.182 Encode.99 Encode.101 Encode.107; - ret Encode.122; + let Encode.117 : List U8 = CallByName TotallyNotJson.182 Encode.99 Encode.101 Encode.107; + ret Encode.117; procedure Encode.26 (Encode.105, Encode.106): let Encode.109 : List U8 = Array []; @@ -58,221 +58,221 @@ procedure Encode.26 (Encode.105, Encode.106): ret Encode.108; procedure List.13 (#Attr.2, #Attr.3): - let List.622 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; - ret List.622; + let List.621 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; + ret List.621; procedure List.145 (List.146, List.147, List.144): - let List.570 : {List U8, U64} = CallByName TotallyNotJson.237 List.146 List.147 List.144; - ret List.570; + let List.569 : {List U8, U64} = CallByName TotallyNotJson.237 List.146 List.147 List.144; + ret List.569; procedure List.145 (List.146, List.147, List.144): - let List.590 : List U8 = CallByName TotallyNotJson.215 List.146 List.147; - ret List.590; + let List.589 : List U8 = CallByName TotallyNotJson.215 List.146 List.147; + ret List.589; procedure List.18 (List.142, List.143, List.144): - let List.551 : {List U8, U64} = CallByName List.93 List.142 List.143 List.144; - ret List.551; + let List.550 : {List U8, U64} = CallByName List.93 List.142 List.143 List.144; + ret List.550; procedure List.18 (List.142, List.143, List.144): - let List.571 : List U8 = CallByName List.93 List.142 List.143 List.144; - ret List.571; + let List.570 : List U8 = CallByName List.93 List.142 List.143 List.144; + ret List.570; procedure List.26 (List.159, List.160, List.161): - let List.639 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.93 List.159 List.160 List.161; - let List.642 : U8 = 1i64; - let List.643 : U8 = GetTagId List.639; - let List.644 : Int1 = lowlevel Eq List.642 List.643; - if List.644 then - let List.162 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.639; + let List.638 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.93 List.159 List.160 List.161; + let List.641 : U8 = 1i64; + let List.642 : U8 = GetTagId List.638; + let List.643 : Int1 = lowlevel Eq List.641 List.642; + if List.643 then + let List.162 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.638; ret List.162; else - let List.163 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.639; + let List.163 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.638; ret List.163; procedure List.31 (#Attr.2, #Attr.3): - let List.604 : List Str = lowlevel ListDropAt #Attr.2 #Attr.3; - ret List.604; + let List.603 : List Str = lowlevel ListDropAt #Attr.2 #Attr.3; + ret List.603; procedure List.38 (List.298): - let List.612 : U64 = 0i64; - let List.611 : List Str = CallByName List.31 List.298 List.612; - ret List.611; + let List.611 : U64 = 0i64; + let List.610 : List Str = CallByName List.31 List.298 List.611; + ret List.610; procedure List.4 (List.113, List.114): - let List.547 : U64 = 1i64; - let List.546 : List Str = CallByName List.70 List.113 List.547; - let List.545 : List Str = CallByName List.71 List.546 List.114; - ret List.545; + let List.546 : U64 = 1i64; + let List.545 : List Str = CallByName List.70 List.113 List.546; + let List.544 : List Str = CallByName List.71 List.545 List.114; + ret List.544; procedure List.4 (List.113, List.114): - let List.550 : U64 = 1i64; - let List.549 : List U8 = CallByName List.70 List.113 List.550; - let List.548 : List U8 = CallByName List.71 List.549 List.114; - ret List.548; + let List.549 : U64 = 1i64; + let List.548 : List U8 = CallByName List.70 List.113 List.549; + let List.547 : List U8 = CallByName List.71 List.548 List.114; + ret List.547; procedure List.49 (List.376, List.377): - let List.631 : U64 = StructAtIndex 0 List.377; - let List.632 : U64 = 0i64; - let List.629 : Int1 = CallByName Bool.11 List.631 List.632; - if List.629 then + let List.630 : U64 = StructAtIndex 0 List.377; + let List.631 : U64 = 0i64; + let List.628 : Int1 = CallByName Bool.11 List.630 List.631; + if List.628 then dec List.376; - let List.630 : List U8 = Array []; - ret List.630; + let List.629 : List U8 = Array []; + ret List.629; else - let List.626 : U64 = StructAtIndex 1 List.377; - let List.627 : U64 = StructAtIndex 0 List.377; - let List.625 : List U8 = CallByName List.72 List.376 List.626 List.627; - ret List.625; + let List.625 : U64 = StructAtIndex 1 List.377; + let List.626 : U64 = StructAtIndex 0 List.377; + let List.624 : List U8 = CallByName List.72 List.376 List.625 List.626; + ret List.624; procedure List.52 (List.391, List.392): let List.393 : U64 = CallByName List.6 List.391; - joinpoint List.637 List.394: - let List.635 : U64 = 0i64; - let List.634 : {U64, U64} = Struct {List.394, List.635}; + joinpoint List.636 List.394: + let List.634 : U64 = 0i64; + let List.633 : {U64, U64} = Struct {List.394, List.634}; inc List.391; - let List.395 : List U8 = CallByName List.49 List.391 List.634; - let List.633 : U64 = CallByName Num.20 List.393 List.394; - let List.624 : {U64, U64} = Struct {List.633, List.394}; - let List.396 : List U8 = CallByName List.49 List.391 List.624; - let List.623 : {List U8, List U8} = Struct {List.395, List.396}; - ret List.623; + let List.395 : List U8 = CallByName List.49 List.391 List.633; + let List.632 : U64 = CallByName Num.20 List.393 List.394; + let List.623 : {U64, U64} = Struct {List.632, List.394}; + let List.396 : List U8 = CallByName List.49 List.391 List.623; + let List.622 : {List U8, List U8} = Struct {List.395, List.396}; + ret List.622; in - let List.638 : Int1 = CallByName Num.24 List.393 List.392; - if List.638 then - jump List.637 List.392; + let List.637 : Int1 = CallByName Num.24 List.393 List.392; + if List.637 then + jump List.636 List.392; else - jump List.637 List.393; + jump List.636 List.393; procedure List.6 (#Attr.2): - let List.618 : U64 = lowlevel ListLen #Attr.2; - ret List.618; + let List.617 : U64 = lowlevel ListLen #Attr.2; + ret List.617; procedure List.6 (#Attr.2): - let List.619 : U64 = lowlevel ListLen #Attr.2; - ret List.619; + let List.618 : U64 = lowlevel ListLen #Attr.2; + ret List.618; procedure List.6 (#Attr.2): - let List.621 : U64 = lowlevel ListLen #Attr.2; - ret List.621; + let List.620 : U64 = lowlevel ListLen #Attr.2; + ret List.620; procedure List.66 (#Attr.2, #Attr.3): - let List.567 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.567; + let List.566 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.566; procedure List.66 (#Attr.2, #Attr.3): - let List.587 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.587; + let List.586 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.586; procedure List.68 (#Attr.2): - let List.614 : List Str = lowlevel ListWithCapacity #Attr.2; - ret List.614; + let List.613 : List Str = lowlevel ListWithCapacity #Attr.2; + ret List.613; procedure List.68 (#Attr.2): - let List.616 : List U8 = lowlevel ListWithCapacity #Attr.2; - ret List.616; + let List.615 : List U8 = lowlevel ListWithCapacity #Attr.2; + ret List.615; procedure List.70 (#Attr.2, #Attr.3): - let List.527 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.527; + let List.526 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.526; procedure List.70 (#Attr.2, #Attr.3): - let List.544 : List Str = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.544; + let List.543 : List Str = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.543; procedure List.71 (#Attr.2, #Attr.3): - let List.525 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.525; + let List.524 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.524; procedure List.71 (#Attr.2, #Attr.3): - let List.542 : List Str = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.542; + let List.541 : List Str = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.541; procedure List.72 (#Attr.2, #Attr.3, #Attr.4): - let List.628 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; - ret List.628; + let List.627 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; + ret List.627; procedure List.8 (#Attr.2, #Attr.3): - let List.593 : List Str = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.593; + let List.592 : List Str = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.592; procedure List.8 (#Attr.2, #Attr.3): - let List.601 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.601; - -procedure List.80 (List.675, List.676, List.677, List.678, List.679): - joinpoint List.557 List.439 List.440 List.441 List.442 List.443: - let List.559 : Int1 = CallByName Num.22 List.442 List.443; - if List.559 then - let List.566 : {Str, Str} = CallByName List.66 List.439 List.442; - inc List.566; - let List.560 : {List U8, U64} = CallByName List.145 List.440 List.566 List.441; - let List.563 : U64 = 1i64; - let List.562 : U64 = CallByName Num.19 List.442 List.563; - jump List.557 List.439 List.560 List.441 List.562 List.443; + let List.600 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.600; + +procedure List.80 (#Derived_gen.15, #Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_gen.19): + joinpoint List.556 List.439 List.440 List.441 List.442 List.443: + let List.558 : Int1 = CallByName Num.22 List.442 List.443; + if List.558 then + let List.565 : {Str, Str} = CallByName List.66 List.439 List.442; + inc List.565; + let List.559 : {List U8, U64} = CallByName List.145 List.440 List.565 List.441; + let List.562 : U64 = 1i64; + let List.561 : U64 = CallByName Num.19 List.442 List.562; + jump List.556 List.439 List.559 List.441 List.561 List.443; else dec List.439; ret List.440; in - jump List.557 List.675 List.676 List.677 List.678 List.679; - -procedure List.80 (List.692, List.693, List.694, List.695, List.696): - joinpoint List.577 List.439 List.440 List.441 List.442 List.443: - let List.579 : Int1 = CallByName Num.22 List.442 List.443; - if List.579 then - let List.586 : U8 = CallByName List.66 List.439 List.442; - let List.580 : List U8 = CallByName List.145 List.440 List.586 List.441; - let List.583 : U64 = 1i64; - let List.582 : U64 = CallByName Num.19 List.442 List.583; - jump List.577 List.439 List.580 List.441 List.582 List.443; + jump List.556 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19; + +procedure List.80 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24): + joinpoint List.647 List.439 List.440 List.441 List.442 List.443: + let List.649 : Int1 = CallByName Num.22 List.442 List.443; + if List.649 then + let List.658 : U8 = CallByName List.66 List.439 List.442; + let List.650 : [C {U64, Int1}, C {U64, Int1}] = CallByName TotallyNotJson.189 List.440 List.658; + let List.655 : U8 = 1i64; + let List.656 : U8 = GetTagId List.650; + let List.657 : Int1 = lowlevel Eq List.655 List.656; + if List.657 then + let List.444 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.650; + let List.653 : U64 = 1i64; + let List.652 : U64 = CallByName Num.19 List.442 List.653; + jump List.647 List.439 List.444 List.441 List.652 List.443; + else + dec List.439; + let List.445 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.650; + let List.654 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) List.445; + ret List.654; else dec List.439; - ret List.440; + let List.648 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440; + ret List.648; in - jump List.577 List.692 List.693 List.694 List.695 List.696; - -procedure List.80 (List.728, List.729, List.730, List.731, List.732): - joinpoint List.648 List.439 List.440 List.441 List.442 List.443: - let List.650 : Int1 = CallByName Num.22 List.442 List.443; - if List.650 then - let List.659 : U8 = CallByName List.66 List.439 List.442; - let List.651 : [C {U64, Int1}, C {U64, Int1}] = CallByName TotallyNotJson.189 List.440 List.659; - let List.656 : U8 = 1i64; - let List.657 : U8 = GetTagId List.651; - let List.658 : Int1 = lowlevel Eq List.656 List.657; - if List.658 then - let List.444 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.651; - let List.654 : U64 = 1i64; - let List.653 : U64 = CallByName Num.19 List.442 List.654; - jump List.648 List.439 List.444 List.441 List.653 List.443; - else - dec List.439; - let List.445 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.651; - let List.655 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) List.445; - ret List.655; + jump List.647 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24; + +procedure List.80 (#Derived_gen.25, #Derived_gen.26, #Derived_gen.27, #Derived_gen.28, #Derived_gen.29): + joinpoint List.576 List.439 List.440 List.441 List.442 List.443: + let List.578 : Int1 = CallByName Num.22 List.442 List.443; + if List.578 then + let List.585 : U8 = CallByName List.66 List.439 List.442; + let List.579 : List U8 = CallByName List.145 List.440 List.585 List.441; + let List.582 : U64 = 1i64; + let List.581 : U64 = CallByName Num.19 List.442 List.582; + jump List.576 List.439 List.579 List.441 List.581 List.443; else dec List.439; - let List.649 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440; - ret List.649; + ret List.440; in - jump List.648 List.728 List.729 List.730 List.731 List.732; + jump List.576 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28 #Derived_gen.29; procedure List.93 (List.436, List.437, List.438): - let List.555 : U64 = 0i64; - let List.556 : U64 = CallByName List.6 List.436; - let List.554 : {List U8, U64} = CallByName List.80 List.436 List.437 List.438 List.555 List.556; - ret List.554; + let List.554 : U64 = 0i64; + let List.555 : U64 = CallByName List.6 List.436; + let List.553 : {List U8, U64} = CallByName List.80 List.436 List.437 List.438 List.554 List.555; + ret List.553; procedure List.93 (List.436, List.437, List.438): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.436; - let List.574 : List U8 = CallByName List.80 List.436 List.437 List.438 List.575 List.576; - ret List.574; + let List.574 : U64 = 0i64; + let List.575 : U64 = CallByName List.6 List.436; + let List.573 : List U8 = CallByName List.80 List.436 List.437 List.438 List.574 List.575; + ret List.573; procedure List.93 (List.436, List.437, List.438): - let List.646 : U64 = 0i64; - let List.647 : U64 = CallByName List.6 List.436; - let List.645 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.80 List.436 List.437 List.438 List.646 List.647; - ret List.645; + let List.645 : U64 = 0i64; + let List.646 : U64 = CallByName List.6 List.436; + let List.644 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.80 List.436 List.437 List.438 List.645 List.646; + ret List.644; procedure Num.127 (#Attr.2): let Num.297 : U8 = lowlevel NumIntCast #Attr.2; @@ -303,20 +303,20 @@ procedure Num.94 (#Attr.2, #Attr.3): ret Num.302; procedure Str.12 (#Attr.2): - let Str.313 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.313; + let Str.309 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.309; procedure Str.4 (#Attr.2, #Attr.3): - let Str.316 : Str = lowlevel StrJoinWith #Attr.2 #Attr.3; - ret Str.316; + let Str.312 : Str = lowlevel StrJoinWith #Attr.2 #Attr.3; + ret Str.312; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): let Str.307 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; ret Str.307; procedure Str.55 (#Attr.2): - let Str.319 : List Str = lowlevel StrGraphemes #Attr.2; - ret Str.319; + let Str.315 : List Str = lowlevel StrGraphemes #Attr.2; + ret Str.315; procedure Str.9 (Str.79): let Str.305 : U64 = 0i64; @@ -330,8 +330,8 @@ procedure Str.9 (Str.79): else let Str.300 : U8 = StructAtIndex 3 Str.80; let Str.301 : U64 = StructAtIndex 0 Str.80; - let #Derived_gen.20 : Str = StructAtIndex 1 Str.80; - dec #Derived_gen.20; + let #Derived_gen.42 : Str = StructAtIndex 1 Str.80; + dec #Derived_gen.42; let Str.299 : {U64, U8} = Struct {Str.301, Str.300}; let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299; ret Str.298; @@ -1222,14 +1222,14 @@ procedure TotallyNotJson.82 (TotallyNotJson.802, TotallyNotJson.803): procedure TotallyNotJson.832 (TotallyNotJson.1493): let TotallyNotJson.1494 : List Str = StructAtIndex 1 TotallyNotJson.1493; - let #Derived_gen.18 : List Str = StructAtIndex 0 TotallyNotJson.1493; - dec #Derived_gen.18; + let #Derived_gen.40 : List Str = StructAtIndex 0 TotallyNotJson.1493; + dec #Derived_gen.40; ret TotallyNotJson.1494; procedure TotallyNotJson.840 (TotallyNotJson.1214): let TotallyNotJson.1215 : List Str = StructAtIndex 1 TotallyNotJson.1214; - let #Derived_gen.19 : List Str = StructAtIndex 0 TotallyNotJson.1214; - dec #Derived_gen.19; + let #Derived_gen.41 : List Str = StructAtIndex 0 TotallyNotJson.1214; + dec #Derived_gen.41; ret TotallyNotJson.1215; procedure TotallyNotJson.87 (TotallyNotJson.809): @@ -1284,7 +1284,7 @@ procedure TotallyNotJson.95 (TotallyNotJson.829): dec TotallyNotJson.1489; ret TotallyNotJson.1488; -procedure TotallyNotJson.96 (TotallyNotJson.1633): +procedure TotallyNotJson.96 (#Derived_gen.36): joinpoint TotallyNotJson.1496 TotallyNotJson.1168: let TotallyNotJson.834 : List Str = StructAtIndex 0 TotallyNotJson.1168; let TotallyNotJson.833 : List Str = StructAtIndex 1 TotallyNotJson.1168; @@ -1320,7 +1320,7 @@ procedure TotallyNotJson.96 (TotallyNotJson.1633): let TotallyNotJson.1497 : {List Str, List Str} = Struct {TotallyNotJson.834, TotallyNotJson.833}; ret TotallyNotJson.1497; in - jump TotallyNotJson.1496 TotallyNotJson.1633; + jump TotallyNotJson.1496 #Derived_gen.36; procedure TotallyNotJson.97 (TotallyNotJson.837): let TotallyNotJson.838 : List Str = CallByName Str.55 TotallyNotJson.837; @@ -1337,7 +1337,7 @@ procedure TotallyNotJson.97 (TotallyNotJson.837): dec TotallyNotJson.1210; ret TotallyNotJson.1209; -procedure TotallyNotJson.98 (TotallyNotJson.1624): +procedure TotallyNotJson.98 (#Derived_gen.14): joinpoint TotallyNotJson.1217 TotallyNotJson.1169: let TotallyNotJson.842 : List Str = StructAtIndex 0 TotallyNotJson.1169; let TotallyNotJson.841 : List Str = StructAtIndex 1 TotallyNotJson.1169; @@ -1373,7 +1373,7 @@ procedure TotallyNotJson.98 (TotallyNotJson.1624): let TotallyNotJson.1218 : {List Str, List Str} = Struct {TotallyNotJson.842, TotallyNotJson.841}; ret TotallyNotJson.1218; in - jump TotallyNotJson.1217 TotallyNotJson.1624; + jump TotallyNotJson.1217 #Derived_gen.14; procedure Test.0 (): let Test.11 : Str = "foo"; diff --git a/crates/compiler/test_mono/generated/encode_derived_string.txt b/crates/compiler/test_mono/generated/encode_derived_string.txt index 5685ff13aed..57d54ca67a8 100644 --- a/crates/compiler/test_mono/generated/encode_derived_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_string.txt @@ -16,130 +16,130 @@ procedure Encode.26 (Encode.105, Encode.106): ret Encode.108; procedure List.145 (List.146, List.147, List.144): - let List.553 : List U8 = CallByName TotallyNotJson.215 List.146 List.147; - ret List.553; + let List.552 : List U8 = CallByName TotallyNotJson.215 List.146 List.147; + ret List.552; procedure List.18 (List.142, List.143, List.144): - let List.535 : List U8 = CallByName List.93 List.142 List.143 List.144; - ret List.535; + let List.534 : List U8 = CallByName List.93 List.142 List.143 List.144; + ret List.534; procedure List.26 (List.159, List.160, List.161): - let List.570 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.93 List.159 List.160 List.161; - let List.573 : U8 = 1i64; - let List.574 : U8 = GetTagId List.570; - let List.575 : Int1 = lowlevel Eq List.573 List.574; - if List.575 then - let List.162 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.570; + let List.569 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.93 List.159 List.160 List.161; + let List.572 : U8 = 1i64; + let List.573 : U8 = GetTagId List.569; + let List.574 : Int1 = lowlevel Eq List.572 List.573; + if List.574 then + let List.162 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.569; ret List.162; else - let List.163 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.570; + let List.163 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.569; ret List.163; procedure List.49 (List.376, List.377): - let List.562 : U64 = StructAtIndex 0 List.377; - let List.563 : U64 = 0i64; - let List.560 : Int1 = CallByName Bool.11 List.562 List.563; - if List.560 then + let List.561 : U64 = StructAtIndex 0 List.377; + let List.562 : U64 = 0i64; + let List.559 : Int1 = CallByName Bool.11 List.561 List.562; + if List.559 then dec List.376; - let List.561 : List U8 = Array []; - ret List.561; + let List.560 : List U8 = Array []; + ret List.560; else - let List.557 : U64 = StructAtIndex 1 List.377; - let List.558 : U64 = StructAtIndex 0 List.377; - let List.556 : List U8 = CallByName List.72 List.376 List.557 List.558; - ret List.556; + let List.556 : U64 = StructAtIndex 1 List.377; + let List.557 : U64 = StructAtIndex 0 List.377; + let List.555 : List U8 = CallByName List.72 List.376 List.556 List.557; + ret List.555; procedure List.52 (List.391, List.392): let List.393 : U64 = CallByName List.6 List.391; - joinpoint List.568 List.394: - let List.566 : U64 = 0i64; - let List.565 : {U64, U64} = Struct {List.394, List.566}; + joinpoint List.567 List.394: + let List.565 : U64 = 0i64; + let List.564 : {U64, U64} = Struct {List.394, List.565}; inc List.391; - let List.395 : List U8 = CallByName List.49 List.391 List.565; - let List.564 : U64 = CallByName Num.20 List.393 List.394; - let List.555 : {U64, U64} = Struct {List.564, List.394}; - let List.396 : List U8 = CallByName List.49 List.391 List.555; - let List.554 : {List U8, List U8} = Struct {List.395, List.396}; - ret List.554; + let List.395 : List U8 = CallByName List.49 List.391 List.564; + let List.563 : U64 = CallByName Num.20 List.393 List.394; + let List.554 : {U64, U64} = Struct {List.563, List.394}; + let List.396 : List U8 = CallByName List.49 List.391 List.554; + let List.553 : {List U8, List U8} = Struct {List.395, List.396}; + ret List.553; in - let List.569 : Int1 = CallByName Num.24 List.393 List.392; - if List.569 then - jump List.568 List.392; + let List.568 : Int1 = CallByName Num.24 List.393 List.392; + if List.568 then + jump List.567 List.392; else - jump List.568 List.393; + jump List.567 List.393; procedure List.6 (#Attr.2): - let List.534 : U64 = lowlevel ListLen #Attr.2; - ret List.534; + let List.533 : U64 = lowlevel ListLen #Attr.2; + ret List.533; procedure List.66 (#Attr.2, #Attr.3): - let List.551 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.551; + let List.550 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.550; procedure List.68 (#Attr.2): - let List.532 : List U8 = lowlevel ListWithCapacity #Attr.2; - ret List.532; + let List.531 : List U8 = lowlevel ListWithCapacity #Attr.2; + ret List.531; procedure List.72 (#Attr.2, #Attr.3, #Attr.4): - let List.559 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; - ret List.559; + let List.558 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; + ret List.558; procedure List.8 (#Attr.2, #Attr.3): - let List.530 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.530; - -procedure List.80 (List.598, List.599, List.600, List.601, List.602): - joinpoint List.541 List.439 List.440 List.441 List.442 List.443: - let List.543 : Int1 = CallByName Num.22 List.442 List.443; - if List.543 then - let List.550 : U8 = CallByName List.66 List.439 List.442; - let List.544 : List U8 = CallByName List.145 List.440 List.550 List.441; - let List.547 : U64 = 1i64; - let List.546 : U64 = CallByName Num.19 List.442 List.547; - jump List.541 List.439 List.544 List.441 List.546 List.443; + let List.529 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.529; + +procedure List.80 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7): + joinpoint List.578 List.439 List.440 List.441 List.442 List.443: + let List.580 : Int1 = CallByName Num.22 List.442 List.443; + if List.580 then + let List.589 : U8 = CallByName List.66 List.439 List.442; + let List.581 : [C {U64, Int1}, C {U64, Int1}] = CallByName TotallyNotJson.189 List.440 List.589; + let List.586 : U8 = 1i64; + let List.587 : U8 = GetTagId List.581; + let List.588 : Int1 = lowlevel Eq List.586 List.587; + if List.588 then + let List.444 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.581; + let List.584 : U64 = 1i64; + let List.583 : U64 = CallByName Num.19 List.442 List.584; + jump List.578 List.439 List.444 List.441 List.583 List.443; + else + dec List.439; + let List.445 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.581; + let List.585 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) List.445; + ret List.585; else dec List.439; - ret List.440; + let List.579 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440; + ret List.579; in - jump List.541 List.598 List.599 List.600 List.601 List.602; - -procedure List.80 (List.621, List.622, List.623, List.624, List.625): - joinpoint List.579 List.439 List.440 List.441 List.442 List.443: - let List.581 : Int1 = CallByName Num.22 List.442 List.443; - if List.581 then - let List.590 : U8 = CallByName List.66 List.439 List.442; - let List.582 : [C {U64, Int1}, C {U64, Int1}] = CallByName TotallyNotJson.189 List.440 List.590; - let List.587 : U8 = 1i64; - let List.588 : U8 = GetTagId List.582; - let List.589 : Int1 = lowlevel Eq List.587 List.588; - if List.589 then - let List.444 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.582; - let List.585 : U64 = 1i64; - let List.584 : U64 = CallByName Num.19 List.442 List.585; - jump List.579 List.439 List.444 List.441 List.584 List.443; - else - dec List.439; - let List.445 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.582; - let List.586 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) List.445; - ret List.586; + jump List.578 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7; + +procedure List.80 (#Derived_gen.8, #Derived_gen.9, #Derived_gen.10, #Derived_gen.11, #Derived_gen.12): + joinpoint List.540 List.439 List.440 List.441 List.442 List.443: + let List.542 : Int1 = CallByName Num.22 List.442 List.443; + if List.542 then + let List.549 : U8 = CallByName List.66 List.439 List.442; + let List.543 : List U8 = CallByName List.145 List.440 List.549 List.441; + let List.546 : U64 = 1i64; + let List.545 : U64 = CallByName Num.19 List.442 List.546; + jump List.540 List.439 List.543 List.441 List.545 List.443; else dec List.439; - let List.580 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440; - ret List.580; + ret List.440; in - jump List.579 List.621 List.622 List.623 List.624 List.625; + jump List.540 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12; procedure List.93 (List.436, List.437, List.438): - let List.539 : U64 = 0i64; - let List.540 : U64 = CallByName List.6 List.436; - let List.538 : List U8 = CallByName List.80 List.436 List.437 List.438 List.539 List.540; - ret List.538; + let List.538 : U64 = 0i64; + let List.539 : U64 = CallByName List.6 List.436; + let List.537 : List U8 = CallByName List.80 List.436 List.437 List.438 List.538 List.539; + ret List.537; procedure List.93 (List.436, List.437, List.438): - let List.577 : U64 = 0i64; - let List.578 : U64 = CallByName List.6 List.436; - let List.576 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.80 List.436 List.437 List.438 List.577 List.578; - ret List.576; + let List.576 : U64 = 0i64; + let List.577 : U64 = CallByName List.6 List.436; + let List.575 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.80 List.436 List.437 List.438 List.576 List.577; + ret List.575; procedure Num.19 (#Attr.2, #Attr.3): let Num.297 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; @@ -166,8 +166,8 @@ procedure Num.94 (#Attr.2, #Attr.3): ret Num.294; procedure Str.12 (#Attr.2): - let Str.312 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.312; + let Str.308 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.308; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): let Str.307 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; @@ -185,8 +185,8 @@ procedure Str.9 (Str.79): else let Str.300 : U8 = StructAtIndex 3 Str.80; let Str.301 : U64 = StructAtIndex 0 Str.80; - let #Derived_gen.0 : Str = StructAtIndex 1 Str.80; - dec #Derived_gen.0; + let #Derived_gen.13 : Str = StructAtIndex 1 Str.80; + dec #Derived_gen.13; let Str.299 : {U64, U8} = Struct {Str.301, Str.300}; let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299; ret Str.298; diff --git a/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt b/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt index 1908d7dc0a7..8ec699d6a79 100644 --- a/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt @@ -31,12 +31,12 @@ procedure Encode.24 (Encode.99, Encode.107, Encode.101): ret Encode.111; procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.118 : List U8 = CallByName TotallyNotJson.264 Encode.99 Encode.101 Encode.107; - ret Encode.118; + let Encode.113 : List U8 = CallByName TotallyNotJson.264 Encode.99 Encode.101 Encode.107; + ret Encode.113; procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.121 : List U8 = CallByName TotallyNotJson.182 Encode.99 Encode.101 Encode.107; - ret Encode.121; + let Encode.116 : List U8 = CallByName TotallyNotJson.182 Encode.99 Encode.101 Encode.107; + ret Encode.116; procedure Encode.26 (Encode.105, Encode.106): let Encode.109 : List U8 = Array []; @@ -45,182 +45,182 @@ procedure Encode.26 (Encode.105, Encode.106): ret Encode.108; procedure List.145 (List.146, List.147, List.144): - let List.568 : {List U8, U64} = CallByName TotallyNotJson.267 List.146 List.147 List.144; - ret List.568; + let List.567 : {List U8, U64} = CallByName TotallyNotJson.267 List.146 List.147 List.144; + ret List.567; procedure List.145 (List.146, List.147, List.144): - let List.588 : List U8 = CallByName TotallyNotJson.215 List.146 List.147; - ret List.588; + let List.587 : List U8 = CallByName TotallyNotJson.215 List.146 List.147; + ret List.587; procedure List.18 (List.142, List.143, List.144): - let List.549 : {List U8, U64} = CallByName List.93 List.142 List.143 List.144; - ret List.549; + let List.548 : {List U8, U64} = CallByName List.93 List.142 List.143 List.144; + ret List.548; procedure List.18 (List.142, List.143, List.144): - let List.569 : List U8 = CallByName List.93 List.142 List.143 List.144; - ret List.569; + let List.568 : List U8 = CallByName List.93 List.142 List.143 List.144; + ret List.568; procedure List.26 (List.159, List.160, List.161): - let List.619 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.93 List.159 List.160 List.161; - let List.622 : U8 = 1i64; - let List.623 : U8 = GetTagId List.619; - let List.624 : Int1 = lowlevel Eq List.622 List.623; - if List.624 then - let List.162 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.619; + let List.618 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.93 List.159 List.160 List.161; + let List.621 : U8 = 1i64; + let List.622 : U8 = GetTagId List.618; + let List.623 : Int1 = lowlevel Eq List.621 List.622; + if List.623 then + let List.162 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.618; ret List.162; else - let List.163 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.619; + let List.163 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.618; ret List.163; procedure List.4 (List.113, List.114): - let List.548 : U64 = 1i64; - let List.547 : List U8 = CallByName List.70 List.113 List.548; - let List.546 : List U8 = CallByName List.71 List.547 List.114; - ret List.546; + let List.547 : U64 = 1i64; + let List.546 : List U8 = CallByName List.70 List.113 List.547; + let List.545 : List U8 = CallByName List.71 List.546 List.114; + ret List.545; procedure List.49 (List.376, List.377): - let List.611 : U64 = StructAtIndex 0 List.377; - let List.612 : U64 = 0i64; - let List.609 : Int1 = CallByName Bool.11 List.611 List.612; - if List.609 then + let List.610 : U64 = StructAtIndex 0 List.377; + let List.611 : U64 = 0i64; + let List.608 : Int1 = CallByName Bool.11 List.610 List.611; + if List.608 then dec List.376; - let List.610 : List U8 = Array []; - ret List.610; + let List.609 : List U8 = Array []; + ret List.609; else - let List.606 : U64 = StructAtIndex 1 List.377; - let List.607 : U64 = StructAtIndex 0 List.377; - let List.605 : List U8 = CallByName List.72 List.376 List.606 List.607; - ret List.605; + let List.605 : U64 = StructAtIndex 1 List.377; + let List.606 : U64 = StructAtIndex 0 List.377; + let List.604 : List U8 = CallByName List.72 List.376 List.605 List.606; + ret List.604; procedure List.52 (List.391, List.392): let List.393 : U64 = CallByName List.6 List.391; - joinpoint List.617 List.394: - let List.615 : U64 = 0i64; - let List.614 : {U64, U64} = Struct {List.394, List.615}; + joinpoint List.616 List.394: + let List.614 : U64 = 0i64; + let List.613 : {U64, U64} = Struct {List.394, List.614}; inc List.391; - let List.395 : List U8 = CallByName List.49 List.391 List.614; - let List.613 : U64 = CallByName Num.20 List.393 List.394; - let List.604 : {U64, U64} = Struct {List.613, List.394}; - let List.396 : List U8 = CallByName List.49 List.391 List.604; - let List.603 : {List U8, List U8} = Struct {List.395, List.396}; - ret List.603; + let List.395 : List U8 = CallByName List.49 List.391 List.613; + let List.612 : U64 = CallByName Num.20 List.393 List.394; + let List.603 : {U64, U64} = Struct {List.612, List.394}; + let List.396 : List U8 = CallByName List.49 List.391 List.603; + let List.602 : {List U8, List U8} = Struct {List.395, List.396}; + ret List.602; in - let List.618 : Int1 = CallByName Num.24 List.393 List.392; - if List.618 then - jump List.617 List.392; + let List.617 : Int1 = CallByName Num.24 List.393 List.392; + if List.617 then + jump List.616 List.392; else - jump List.617 List.393; + jump List.616 List.393; procedure List.6 (#Attr.2): - let List.589 : U64 = lowlevel ListLen #Attr.2; - ret List.589; + let List.588 : U64 = lowlevel ListLen #Attr.2; + ret List.588; procedure List.6 (#Attr.2): - let List.591 : U64 = lowlevel ListLen #Attr.2; - ret List.591; + let List.590 : U64 = lowlevel ListLen #Attr.2; + ret List.590; procedure List.66 (#Attr.2, #Attr.3): - let List.565 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.565; + let List.564 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.564; procedure List.66 (#Attr.2, #Attr.3): - let List.585 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.585; + let List.584 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.584; procedure List.68 (#Attr.2): - let List.602 : List U8 = lowlevel ListWithCapacity #Attr.2; - ret List.602; + let List.601 : List U8 = lowlevel ListWithCapacity #Attr.2; + ret List.601; procedure List.70 (#Attr.2, #Attr.3): - let List.527 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.527; + let List.526 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.526; procedure List.71 (#Attr.2, #Attr.3): - let List.525 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.525; + let List.524 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.524; procedure List.72 (#Attr.2, #Attr.3, #Attr.4): - let List.608 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; - ret List.608; + let List.607 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; + ret List.607; procedure List.8 (#Attr.2, #Attr.3): - let List.600 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.600; - -procedure List.80 (List.649, List.650, List.651, List.652, List.653): - joinpoint List.555 List.439 List.440 List.441 List.442 List.443: - let List.557 : Int1 = CallByName Num.22 List.442 List.443; - if List.557 then - let List.564 : Str = CallByName List.66 List.439 List.442; - inc List.564; - let List.558 : {List U8, U64} = CallByName List.145 List.440 List.564 List.441; - let List.561 : U64 = 1i64; - let List.560 : U64 = CallByName Num.19 List.442 List.561; - jump List.555 List.439 List.558 List.441 List.560 List.443; + let List.599 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.599; + +procedure List.80 (#Derived_gen.10, #Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14): + joinpoint List.554 List.439 List.440 List.441 List.442 List.443: + let List.556 : Int1 = CallByName Num.22 List.442 List.443; + if List.556 then + let List.563 : Str = CallByName List.66 List.439 List.442; + inc List.563; + let List.557 : {List U8, U64} = CallByName List.145 List.440 List.563 List.441; + let List.560 : U64 = 1i64; + let List.559 : U64 = CallByName Num.19 List.442 List.560; + jump List.554 List.439 List.557 List.441 List.559 List.443; else dec List.439; ret List.440; in - jump List.555 List.649 List.650 List.651 List.652 List.653; - -procedure List.80 (List.666, List.667, List.668, List.669, List.670): - joinpoint List.575 List.439 List.440 List.441 List.442 List.443: - let List.577 : Int1 = CallByName Num.22 List.442 List.443; - if List.577 then - let List.584 : U8 = CallByName List.66 List.439 List.442; - let List.578 : List U8 = CallByName List.145 List.440 List.584 List.441; - let List.581 : U64 = 1i64; - let List.580 : U64 = CallByName Num.19 List.442 List.581; - jump List.575 List.439 List.578 List.441 List.580 List.443; + jump List.554 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14; + +procedure List.80 (#Derived_gen.18, #Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22): + joinpoint List.574 List.439 List.440 List.441 List.442 List.443: + let List.576 : Int1 = CallByName Num.22 List.442 List.443; + if List.576 then + let List.583 : U8 = CallByName List.66 List.439 List.442; + let List.577 : List U8 = CallByName List.145 List.440 List.583 List.441; + let List.580 : U64 = 1i64; + let List.579 : U64 = CallByName Num.19 List.442 List.580; + jump List.574 List.439 List.577 List.441 List.579 List.443; else dec List.439; ret List.440; in - jump List.575 List.666 List.667 List.668 List.669 List.670; - -procedure List.80 (List.693, List.694, List.695, List.696, List.697): - joinpoint List.628 List.439 List.440 List.441 List.442 List.443: - let List.630 : Int1 = CallByName Num.22 List.442 List.443; - if List.630 then - let List.639 : U8 = CallByName List.66 List.439 List.442; - let List.631 : [C {U64, Int1}, C {U64, Int1}] = CallByName TotallyNotJson.189 List.440 List.639; - let List.636 : U8 = 1i64; - let List.637 : U8 = GetTagId List.631; - let List.638 : Int1 = lowlevel Eq List.636 List.637; - if List.638 then - let List.444 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.631; - let List.634 : U64 = 1i64; - let List.633 : U64 = CallByName Num.19 List.442 List.634; - jump List.628 List.439 List.444 List.441 List.633 List.443; + jump List.574 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22; + +procedure List.80 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33): + joinpoint List.627 List.439 List.440 List.441 List.442 List.443: + let List.629 : Int1 = CallByName Num.22 List.442 List.443; + if List.629 then + let List.638 : U8 = CallByName List.66 List.439 List.442; + let List.630 : [C {U64, Int1}, C {U64, Int1}] = CallByName TotallyNotJson.189 List.440 List.638; + let List.635 : U8 = 1i64; + let List.636 : U8 = GetTagId List.630; + let List.637 : Int1 = lowlevel Eq List.635 List.636; + if List.637 then + let List.444 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.630; + let List.633 : U64 = 1i64; + let List.632 : U64 = CallByName Num.19 List.442 List.633; + jump List.627 List.439 List.444 List.441 List.632 List.443; else dec List.439; - let List.445 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.631; - let List.635 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) List.445; - ret List.635; + let List.445 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.630; + let List.634 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) List.445; + ret List.634; else dec List.439; - let List.629 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440; - ret List.629; + let List.628 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440; + ret List.628; in - jump List.628 List.693 List.694 List.695 List.696 List.697; + jump List.627 #Derived_gen.29 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33; procedure List.93 (List.436, List.437, List.438): - let List.553 : U64 = 0i64; - let List.554 : U64 = CallByName List.6 List.436; - let List.552 : {List U8, U64} = CallByName List.80 List.436 List.437 List.438 List.553 List.554; - ret List.552; + let List.552 : U64 = 0i64; + let List.553 : U64 = CallByName List.6 List.436; + let List.551 : {List U8, U64} = CallByName List.80 List.436 List.437 List.438 List.552 List.553; + ret List.551; procedure List.93 (List.436, List.437, List.438): - let List.573 : U64 = 0i64; - let List.574 : U64 = CallByName List.6 List.436; - let List.572 : List U8 = CallByName List.80 List.436 List.437 List.438 List.573 List.574; - ret List.572; + let List.572 : U64 = 0i64; + let List.573 : U64 = CallByName List.6 List.436; + let List.571 : List U8 = CallByName List.80 List.436 List.437 List.438 List.572 List.573; + ret List.571; procedure List.93 (List.436, List.437, List.438): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.436; - let List.625 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.80 List.436 List.437 List.438 List.626 List.627; - ret List.625; + let List.625 : U64 = 0i64; + let List.626 : U64 = CallByName List.6 List.436; + let List.624 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.80 List.436 List.437 List.438 List.625 List.626; + ret List.624; procedure Num.127 (#Attr.2): let Num.299 : U8 = lowlevel NumIntCast #Attr.2; @@ -251,8 +251,8 @@ procedure Num.94 (#Attr.2, #Attr.3): ret Num.304; procedure Str.12 (#Attr.2): - let Str.313 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.313; + let Str.309 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.309; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): let Str.307 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; @@ -270,8 +270,8 @@ procedure Str.9 (Str.79): else let Str.300 : U8 = StructAtIndex 3 Str.80; let Str.301 : U64 = StructAtIndex 0 Str.80; - let #Derived_gen.14 : Str = StructAtIndex 1 Str.80; - dec #Derived_gen.14; + let #Derived_gen.34 : Str = StructAtIndex 1 Str.80; + dec #Derived_gen.34; let Str.299 : {U64, U8} = Struct {Str.301, Str.300}; let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299; ret Str.298; diff --git a/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt b/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt index 147632f0945..c0ccba518fc 100644 --- a/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt @@ -34,12 +34,12 @@ procedure Encode.24 (Encode.99, Encode.107, Encode.101): ret Encode.111; procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.118 : List U8 = CallByName TotallyNotJson.264 Encode.99 Encode.101 Encode.107; - ret Encode.118; + let Encode.113 : List U8 = CallByName TotallyNotJson.264 Encode.99 Encode.101 Encode.107; + ret Encode.113; procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.122 : List U8 = CallByName TotallyNotJson.182 Encode.99 Encode.101 Encode.107; - ret Encode.122; + let Encode.117 : List U8 = CallByName TotallyNotJson.182 Encode.99 Encode.101 Encode.107; + ret Encode.117; procedure Encode.26 (Encode.105, Encode.106): let Encode.109 : List U8 = Array []; @@ -48,182 +48,182 @@ procedure Encode.26 (Encode.105, Encode.106): ret Encode.108; procedure List.145 (List.146, List.147, List.144): - let List.568 : {List U8, U64} = CallByName TotallyNotJson.267 List.146 List.147 List.144; - ret List.568; + let List.567 : {List U8, U64} = CallByName TotallyNotJson.267 List.146 List.147 List.144; + ret List.567; procedure List.145 (List.146, List.147, List.144): - let List.588 : List U8 = CallByName TotallyNotJson.215 List.146 List.147; - ret List.588; + let List.587 : List U8 = CallByName TotallyNotJson.215 List.146 List.147; + ret List.587; procedure List.18 (List.142, List.143, List.144): - let List.549 : {List U8, U64} = CallByName List.93 List.142 List.143 List.144; - ret List.549; + let List.548 : {List U8, U64} = CallByName List.93 List.142 List.143 List.144; + ret List.548; procedure List.18 (List.142, List.143, List.144): - let List.569 : List U8 = CallByName List.93 List.142 List.143 List.144; - ret List.569; + let List.568 : List U8 = CallByName List.93 List.142 List.143 List.144; + ret List.568; procedure List.26 (List.159, List.160, List.161): - let List.619 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.93 List.159 List.160 List.161; - let List.622 : U8 = 1i64; - let List.623 : U8 = GetTagId List.619; - let List.624 : Int1 = lowlevel Eq List.622 List.623; - if List.624 then - let List.162 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.619; + let List.618 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.93 List.159 List.160 List.161; + let List.621 : U8 = 1i64; + let List.622 : U8 = GetTagId List.618; + let List.623 : Int1 = lowlevel Eq List.621 List.622; + if List.623 then + let List.162 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.618; ret List.162; else - let List.163 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.619; + let List.163 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.618; ret List.163; procedure List.4 (List.113, List.114): - let List.548 : U64 = 1i64; - let List.547 : List U8 = CallByName List.70 List.113 List.548; - let List.546 : List U8 = CallByName List.71 List.547 List.114; - ret List.546; + let List.547 : U64 = 1i64; + let List.546 : List U8 = CallByName List.70 List.113 List.547; + let List.545 : List U8 = CallByName List.71 List.546 List.114; + ret List.545; procedure List.49 (List.376, List.377): - let List.611 : U64 = StructAtIndex 0 List.377; - let List.612 : U64 = 0i64; - let List.609 : Int1 = CallByName Bool.11 List.611 List.612; - if List.609 then + let List.610 : U64 = StructAtIndex 0 List.377; + let List.611 : U64 = 0i64; + let List.608 : Int1 = CallByName Bool.11 List.610 List.611; + if List.608 then dec List.376; - let List.610 : List U8 = Array []; - ret List.610; + let List.609 : List U8 = Array []; + ret List.609; else - let List.606 : U64 = StructAtIndex 1 List.377; - let List.607 : U64 = StructAtIndex 0 List.377; - let List.605 : List U8 = CallByName List.72 List.376 List.606 List.607; - ret List.605; + let List.605 : U64 = StructAtIndex 1 List.377; + let List.606 : U64 = StructAtIndex 0 List.377; + let List.604 : List U8 = CallByName List.72 List.376 List.605 List.606; + ret List.604; procedure List.52 (List.391, List.392): let List.393 : U64 = CallByName List.6 List.391; - joinpoint List.617 List.394: - let List.615 : U64 = 0i64; - let List.614 : {U64, U64} = Struct {List.394, List.615}; + joinpoint List.616 List.394: + let List.614 : U64 = 0i64; + let List.613 : {U64, U64} = Struct {List.394, List.614}; inc List.391; - let List.395 : List U8 = CallByName List.49 List.391 List.614; - let List.613 : U64 = CallByName Num.20 List.393 List.394; - let List.604 : {U64, U64} = Struct {List.613, List.394}; - let List.396 : List U8 = CallByName List.49 List.391 List.604; - let List.603 : {List U8, List U8} = Struct {List.395, List.396}; - ret List.603; + let List.395 : List U8 = CallByName List.49 List.391 List.613; + let List.612 : U64 = CallByName Num.20 List.393 List.394; + let List.603 : {U64, U64} = Struct {List.612, List.394}; + let List.396 : List U8 = CallByName List.49 List.391 List.603; + let List.602 : {List U8, List U8} = Struct {List.395, List.396}; + ret List.602; in - let List.618 : Int1 = CallByName Num.24 List.393 List.392; - if List.618 then - jump List.617 List.392; + let List.617 : Int1 = CallByName Num.24 List.393 List.392; + if List.617 then + jump List.616 List.392; else - jump List.617 List.393; + jump List.616 List.393; procedure List.6 (#Attr.2): - let List.589 : U64 = lowlevel ListLen #Attr.2; - ret List.589; + let List.588 : U64 = lowlevel ListLen #Attr.2; + ret List.588; procedure List.6 (#Attr.2): - let List.591 : U64 = lowlevel ListLen #Attr.2; - ret List.591; + let List.590 : U64 = lowlevel ListLen #Attr.2; + ret List.590; procedure List.66 (#Attr.2, #Attr.3): - let List.565 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.565; + let List.564 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.564; procedure List.66 (#Attr.2, #Attr.3): - let List.585 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.585; + let List.584 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.584; procedure List.68 (#Attr.2): - let List.602 : List U8 = lowlevel ListWithCapacity #Attr.2; - ret List.602; + let List.601 : List U8 = lowlevel ListWithCapacity #Attr.2; + ret List.601; procedure List.70 (#Attr.2, #Attr.3): - let List.527 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.527; + let List.526 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.526; procedure List.71 (#Attr.2, #Attr.3): - let List.525 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.525; + let List.524 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.524; procedure List.72 (#Attr.2, #Attr.3, #Attr.4): - let List.608 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; - ret List.608; + let List.607 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; + ret List.607; procedure List.8 (#Attr.2, #Attr.3): - let List.600 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.600; - -procedure List.80 (List.649, List.650, List.651, List.652, List.653): - joinpoint List.555 List.439 List.440 List.441 List.442 List.443: - let List.557 : Int1 = CallByName Num.22 List.442 List.443; - if List.557 then - let List.564 : Str = CallByName List.66 List.439 List.442; - inc List.564; - let List.558 : {List U8, U64} = CallByName List.145 List.440 List.564 List.441; - let List.561 : U64 = 1i64; - let List.560 : U64 = CallByName Num.19 List.442 List.561; - jump List.555 List.439 List.558 List.441 List.560 List.443; + let List.599 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.599; + +procedure List.80 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24): + joinpoint List.574 List.439 List.440 List.441 List.442 List.443: + let List.576 : Int1 = CallByName Num.22 List.442 List.443; + if List.576 then + let List.583 : U8 = CallByName List.66 List.439 List.442; + let List.577 : List U8 = CallByName List.145 List.440 List.583 List.441; + let List.580 : U64 = 1i64; + let List.579 : U64 = CallByName Num.19 List.442 List.580; + jump List.574 List.439 List.577 List.441 List.579 List.443; else dec List.439; ret List.440; in - jump List.555 List.649 List.650 List.651 List.652 List.653; - -procedure List.80 (List.666, List.667, List.668, List.669, List.670): - joinpoint List.575 List.439 List.440 List.441 List.442 List.443: - let List.577 : Int1 = CallByName Num.22 List.442 List.443; - if List.577 then - let List.584 : U8 = CallByName List.66 List.439 List.442; - let List.578 : List U8 = CallByName List.145 List.440 List.584 List.441; - let List.581 : U64 = 1i64; - let List.580 : U64 = CallByName Num.19 List.442 List.581; - jump List.575 List.439 List.578 List.441 List.580 List.443; + jump List.574 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24; + +procedure List.80 (#Derived_gen.25, #Derived_gen.26, #Derived_gen.27, #Derived_gen.28, #Derived_gen.29): + joinpoint List.627 List.439 List.440 List.441 List.442 List.443: + let List.629 : Int1 = CallByName Num.22 List.442 List.443; + if List.629 then + let List.638 : U8 = CallByName List.66 List.439 List.442; + let List.630 : [C {U64, Int1}, C {U64, Int1}] = CallByName TotallyNotJson.189 List.440 List.638; + let List.635 : U8 = 1i64; + let List.636 : U8 = GetTagId List.630; + let List.637 : Int1 = lowlevel Eq List.635 List.636; + if List.637 then + let List.444 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.630; + let List.633 : U64 = 1i64; + let List.632 : U64 = CallByName Num.19 List.442 List.633; + jump List.627 List.439 List.444 List.441 List.632 List.443; + else + dec List.439; + let List.445 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.630; + let List.634 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) List.445; + ret List.634; else dec List.439; - ret List.440; + let List.628 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440; + ret List.628; in - jump List.575 List.666 List.667 List.668 List.669 List.670; - -procedure List.80 (List.693, List.694, List.695, List.696, List.697): - joinpoint List.628 List.439 List.440 List.441 List.442 List.443: - let List.630 : Int1 = CallByName Num.22 List.442 List.443; - if List.630 then - let List.639 : U8 = CallByName List.66 List.439 List.442; - let List.631 : [C {U64, Int1}, C {U64, Int1}] = CallByName TotallyNotJson.189 List.440 List.639; - let List.636 : U8 = 1i64; - let List.637 : U8 = GetTagId List.631; - let List.638 : Int1 = lowlevel Eq List.636 List.637; - if List.638 then - let List.444 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.631; - let List.634 : U64 = 1i64; - let List.633 : U64 = CallByName Num.19 List.442 List.634; - jump List.628 List.439 List.444 List.441 List.633 List.443; - else - dec List.439; - let List.445 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.631; - let List.635 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) List.445; - ret List.635; + jump List.627 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28 #Derived_gen.29; + +procedure List.80 (#Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33, #Derived_gen.34): + joinpoint List.554 List.439 List.440 List.441 List.442 List.443: + let List.556 : Int1 = CallByName Num.22 List.442 List.443; + if List.556 then + let List.563 : Str = CallByName List.66 List.439 List.442; + inc List.563; + let List.557 : {List U8, U64} = CallByName List.145 List.440 List.563 List.441; + let List.560 : U64 = 1i64; + let List.559 : U64 = CallByName Num.19 List.442 List.560; + jump List.554 List.439 List.557 List.441 List.559 List.443; else dec List.439; - let List.629 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440; - ret List.629; + ret List.440; in - jump List.628 List.693 List.694 List.695 List.696 List.697; + jump List.554 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33 #Derived_gen.34; procedure List.93 (List.436, List.437, List.438): - let List.553 : U64 = 0i64; - let List.554 : U64 = CallByName List.6 List.436; - let List.552 : {List U8, U64} = CallByName List.80 List.436 List.437 List.438 List.553 List.554; - ret List.552; + let List.552 : U64 = 0i64; + let List.553 : U64 = CallByName List.6 List.436; + let List.551 : {List U8, U64} = CallByName List.80 List.436 List.437 List.438 List.552 List.553; + ret List.551; procedure List.93 (List.436, List.437, List.438): - let List.573 : U64 = 0i64; - let List.574 : U64 = CallByName List.6 List.436; - let List.572 : List U8 = CallByName List.80 List.436 List.437 List.438 List.573 List.574; - ret List.572; + let List.572 : U64 = 0i64; + let List.573 : U64 = CallByName List.6 List.436; + let List.571 : List U8 = CallByName List.80 List.436 List.437 List.438 List.572 List.573; + ret List.571; procedure List.93 (List.436, List.437, List.438): - let List.626 : U64 = 0i64; - let List.627 : U64 = CallByName List.6 List.436; - let List.625 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.80 List.436 List.437 List.438 List.626 List.627; - ret List.625; + let List.625 : U64 = 0i64; + let List.626 : U64 = CallByName List.6 List.436; + let List.624 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.80 List.436 List.437 List.438 List.625 List.626; + ret List.624; procedure Num.127 (#Attr.2): let Num.299 : U8 = lowlevel NumIntCast #Attr.2; @@ -254,8 +254,8 @@ procedure Num.94 (#Attr.2, #Attr.3): ret Num.304; procedure Str.12 (#Attr.2): - let Str.313 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.313; + let Str.309 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.309; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): let Str.307 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; @@ -273,8 +273,8 @@ procedure Str.9 (Str.79): else let Str.300 : U8 = StructAtIndex 3 Str.80; let Str.301 : U64 = StructAtIndex 0 Str.80; - let #Derived_gen.15 : Str = StructAtIndex 1 Str.80; - dec #Derived_gen.15; + let #Derived_gen.35 : Str = StructAtIndex 1 Str.80; + dec #Derived_gen.35; let Str.299 : {U64, U8} = Struct {Str.301, Str.300}; let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299; ret Str.298; diff --git a/crates/compiler/test_mono/generated/factorial.txt b/crates/compiler/test_mono/generated/factorial.txt index bc43696252d..5b844fd3d9d 100644 --- a/crates/compiler/test_mono/generated/factorial.txt +++ b/crates/compiler/test_mono/generated/factorial.txt @@ -6,7 +6,7 @@ procedure Num.21 (#Attr.2, #Attr.3): let Num.292 : I64 = lowlevel NumMul #Attr.2 #Attr.3; ret Num.292; -procedure Test.1 (Test.15, Test.16): +procedure Test.1 (#Derived_gen.0, #Derived_gen.1): joinpoint Test.7 Test.2 Test.3: let Test.13 : I64 = 0i64; let Test.14 : Int1 = lowlevel Eq Test.13 Test.2; @@ -18,7 +18,7 @@ procedure Test.1 (Test.15, Test.16): let Test.11 : I64 = CallByName Num.21 Test.2 Test.3; jump Test.7 Test.10 Test.11; in - jump Test.7 Test.15 Test.16; + jump Test.7 #Derived_gen.0 #Derived_gen.1; procedure Test.0 (): let Test.5 : I64 = 10i64; diff --git a/crates/compiler/test_mono/generated/inline_return_joinpoints_in_bool_lambda_set.txt b/crates/compiler/test_mono/generated/inline_return_joinpoints_in_bool_lambda_set.txt index 16466af3715..6f8a56f0cbe 100644 --- a/crates/compiler/test_mono/generated/inline_return_joinpoints_in_bool_lambda_set.txt +++ b/crates/compiler/test_mono/generated/inline_return_joinpoints_in_bool_lambda_set.txt @@ -9,7 +9,7 @@ procedure Num.19 (#Attr.2, #Attr.3): procedure Test.3 (Test.4): ret Test.4; -procedure Test.0 (Test.14): +procedure Test.0 (#Derived_gen.0): joinpoint Test.5 Test.1: joinpoint Test.10 Test.2: let Test.8 : I64 = 1i64; @@ -31,4 +31,4 @@ procedure Test.0 (Test.14): let Test.9 : Int1 = true; jump Test.10 Test.9; in - jump Test.5 Test.14; + jump Test.5 #Derived_gen.0; diff --git a/crates/compiler/test_mono/generated/inline_return_joinpoints_in_enum_lambda_set.txt b/crates/compiler/test_mono/generated/inline_return_joinpoints_in_enum_lambda_set.txt index bf4dcfca155..5265cdc174d 100644 --- a/crates/compiler/test_mono/generated/inline_return_joinpoints_in_enum_lambda_set.txt +++ b/crates/compiler/test_mono/generated/inline_return_joinpoints_in_enum_lambda_set.txt @@ -34,7 +34,7 @@ procedure Test.8 (Test.9): let Test.23 : I64 = CallByName Num.19 Test.9 Test.24; ret Test.23; -procedure Test.0 (Test.30): +procedure Test.0 (#Derived_gen.0): joinpoint Test.11 Test.1: let Test.25 : I64 = 1i64; let Test.13 : I64 = CallByName Num.19 Test.1 Test.25; @@ -57,4 +57,4 @@ procedure Test.0 (Test.30): ret Test.12; in - jump Test.11 Test.30; + jump Test.11 #Derived_gen.0; diff --git a/crates/compiler/test_mono/generated/inline_return_joinpoints_in_union_lambda_set.txt b/crates/compiler/test_mono/generated/inline_return_joinpoints_in_union_lambda_set.txt index cc5f96b3e6c..6ab49274c59 100644 --- a/crates/compiler/test_mono/generated/inline_return_joinpoints_in_union_lambda_set.txt +++ b/crates/compiler/test_mono/generated/inline_return_joinpoints_in_union_lambda_set.txt @@ -17,7 +17,7 @@ procedure Test.4 (Test.5, #Attr.12): let Test.16 : I64 = CallByName Num.19 Test.5 Test.1; ret Test.16; -procedure Test.0 (Test.25): +procedure Test.0 (#Derived_gen.0): joinpoint Test.7 Test.1: let Test.20 : I64 = 1i64; let Test.9 : I64 = CallByName Num.19 Test.1 Test.20; @@ -33,4 +33,4 @@ procedure Test.0 (Test.25): ret Test.8; in - jump Test.7 Test.25; + jump Test.7 #Derived_gen.0; diff --git a/crates/compiler/test_mono/generated/issue_3669.txt b/crates/compiler/test_mono/generated/issue_3669.txt index fd2126b6c9e..1b670be63b8 100644 --- a/crates/compiler/test_mono/generated/issue_3669.txt +++ b/crates/compiler/test_mono/generated/issue_3669.txt @@ -2,7 +2,7 @@ procedure Bool.11 (#Attr.2, #Attr.3): let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3; ret Bool.23; -procedure Test.2 (Test.19): +procedure Test.2 (#Derived_gen.0): joinpoint Test.13 Test.7: let Test.16 : U8 = 1i64; let Test.17 : U8 = GetTagId Test.7; @@ -13,16 +13,16 @@ procedure Test.2 (Test.19): ret Test.14; else let Test.5 : [, C *self] = UnionAtIndex (Id 0) (Index 0) Test.7; - let #Derived_gen.0 : Int1 = lowlevel RefCountIsUnique Test.7; - if #Derived_gen.0 then - decref Test.7; + let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.7; + if #Derived_gen.1 then + free Test.7; jump Test.13 Test.5; else inc Test.5; decref Test.7; jump Test.13 Test.5; in - jump Test.13 Test.19; + jump Test.13 #Derived_gen.0; procedure Test.0 (): let Test.12 : [, C *self] = TagId(1) ; diff --git a/crates/compiler/test_mono/generated/issue_4749.txt b/crates/compiler/test_mono/generated/issue_4749.txt index 82602e8a385..f43c0ae8dc4 100644 --- a/crates/compiler/test_mono/generated/issue_4749.txt +++ b/crates/compiler/test_mono/generated/issue_4749.txt @@ -1,30 +1,30 @@ procedure Bool.1 (): - let Bool.55 : Int1 = false; - ret Bool.55; + let Bool.51 : Int1 = false; + ret Bool.51; procedure Bool.11 (#Attr.2, #Attr.3): let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3; ret Bool.23; procedure Bool.11 (#Attr.2, #Attr.3): - let Bool.46 : Int1 = lowlevel Eq #Attr.2 #Attr.3; - ret Bool.46; + let Bool.42 : Int1 = lowlevel Eq #Attr.2 #Attr.3; + ret Bool.42; procedure Bool.11 (#Attr.2, #Attr.3): - let Bool.67 : Int1 = lowlevel Eq #Attr.2 #Attr.3; - ret Bool.67; + let Bool.57 : Int1 = lowlevel Eq #Attr.2 #Attr.3; + ret Bool.57; procedure Bool.2 (): - let Bool.54 : Int1 = true; - ret Bool.54; + let Bool.50 : Int1 = true; + ret Bool.50; procedure Bool.3 (#Attr.2, #Attr.3): - let Bool.37 : Int1 = lowlevel And #Attr.2 #Attr.3; - ret Bool.37; + let Bool.33 : Int1 = lowlevel And #Attr.2 #Attr.3; + ret Bool.33; procedure Bool.4 (#Attr.2, #Attr.3): - let Bool.57 : Int1 = lowlevel Or #Attr.2 #Attr.3; - ret Bool.57; + let Bool.53 : Int1 = lowlevel Or #Attr.2 #Attr.3; + ret Bool.53; procedure Decode.24 (Decode.101): ret Decode.101; @@ -65,151 +65,151 @@ procedure Decode.27 (Decode.107, Decode.108): ret Decode.123; procedure List.1 (List.96): - let List.590 : U64 = CallByName List.6 List.96; + let List.588 : U64 = CallByName List.6 List.96; dec List.96; - let List.591 : U64 = 0i64; - let List.589 : Int1 = CallByName Bool.11 List.590 List.591; - ret List.589; + let List.589 : U64 = 0i64; + let List.587 : Int1 = CallByName Bool.11 List.588 List.589; + ret List.587; procedure List.2 (List.97, List.98): - let List.573 : U64 = CallByName List.6 List.97; - let List.570 : Int1 = CallByName Num.22 List.98 List.573; - if List.570 then - let List.572 : U8 = CallByName List.66 List.97 List.98; + let List.571 : U64 = CallByName List.6 List.97; + let List.568 : Int1 = CallByName Num.22 List.98 List.571; + if List.568 then + let List.570 : U8 = CallByName List.66 List.97 List.98; dec List.97; - let List.571 : [C {}, C U8] = TagId(1) List.572; - ret List.571; + let List.569 : [C {}, C U8] = TagId(1) List.570; + ret List.569; else dec List.97; - let List.569 : {} = Struct {}; - let List.568 : [C {}, C U8] = TagId(0) List.569; - ret List.568; + let List.567 : {} = Struct {}; + let List.566 : [C {}, C U8] = TagId(0) List.567; + ret List.566; procedure List.26 (List.159, List.160, List.161): - let List.592 : [C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64], C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64]] = CallByName List.93 List.159 List.160 List.161; - let List.595 : U8 = 1i64; - let List.596 : U8 = GetTagId List.592; - let List.597 : Int1 = lowlevel Eq List.595 List.596; - if List.597 then - let List.162 : [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64] = UnionAtIndex (Id 1) (Index 0) List.592; + let List.590 : [C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64], C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64]] = CallByName List.93 List.159 List.160 List.161; + let List.593 : U8 = 1i64; + let List.594 : U8 = GetTagId List.590; + let List.595 : Int1 = lowlevel Eq List.593 List.594; + if List.595 then + let List.162 : [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64] = UnionAtIndex (Id 1) (Index 0) List.590; ret List.162; else - let List.163 : [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64] = UnionAtIndex (Id 0) (Index 0) List.592; + let List.163 : [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64] = UnionAtIndex (Id 0) (Index 0) List.590; ret List.163; procedure List.29 (List.304, List.305): - let List.547 : U64 = CallByName List.6 List.304; - let List.306 : U64 = CallByName Num.77 List.547 List.305; - let List.546 : List U8 = CallByName List.43 List.304 List.306; - ret List.546; + let List.545 : U64 = CallByName List.6 List.304; + let List.306 : U64 = CallByName Num.77 List.545 List.305; + let List.544 : List U8 = CallByName List.43 List.304 List.306; + ret List.544; procedure List.31 (#Attr.2, #Attr.3): - let List.560 : List U8 = lowlevel ListDropAt #Attr.2 #Attr.3; - ret List.560; + let List.558 : List U8 = lowlevel ListDropAt #Attr.2 #Attr.3; + ret List.558; procedure List.38 (List.298): - let List.559 : U64 = 0i64; - let List.558 : List U8 = CallByName List.31 List.298 List.559; - ret List.558; + let List.557 : U64 = 0i64; + let List.556 : List U8 = CallByName List.31 List.298 List.557; + ret List.556; procedure List.4 (List.113, List.114): - let List.557 : U64 = 1i64; - let List.556 : List U8 = CallByName List.70 List.113 List.557; - let List.555 : List U8 = CallByName List.71 List.556 List.114; - ret List.555; + let List.555 : U64 = 1i64; + let List.554 : List U8 = CallByName List.70 List.113 List.555; + let List.553 : List U8 = CallByName List.71 List.554 List.114; + ret List.553; procedure List.43 (List.302, List.303): - let List.539 : U64 = CallByName List.6 List.302; - let List.538 : U64 = CallByName Num.77 List.539 List.303; - let List.529 : {U64, U64} = Struct {List.303, List.538}; - let List.528 : List U8 = CallByName List.49 List.302 List.529; - ret List.528; + let List.537 : U64 = CallByName List.6 List.302; + let List.536 : U64 = CallByName Num.77 List.537 List.303; + let List.527 : {U64, U64} = Struct {List.303, List.536}; + let List.526 : List U8 = CallByName List.49 List.302 List.527; + ret List.526; procedure List.49 (List.376, List.377): - let List.586 : U64 = StructAtIndex 0 List.377; - let List.587 : U64 = 0i64; - let List.584 : Int1 = CallByName Bool.11 List.586 List.587; - if List.584 then + let List.584 : U64 = StructAtIndex 0 List.377; + let List.585 : U64 = 0i64; + let List.582 : Int1 = CallByName Bool.11 List.584 List.585; + if List.582 then dec List.376; - let List.585 : List U8 = Array []; - ret List.585; + let List.583 : List U8 = Array []; + ret List.583; else - let List.582 : U64 = StructAtIndex 1 List.377; - let List.583 : U64 = StructAtIndex 0 List.377; - let List.581 : List U8 = CallByName List.72 List.376 List.582 List.583; - ret List.581; + let List.580 : U64 = StructAtIndex 1 List.377; + let List.581 : U64 = StructAtIndex 0 List.377; + let List.579 : List U8 = CallByName List.72 List.376 List.580 List.581; + ret List.579; procedure List.6 (#Attr.2): - let List.650 : U64 = lowlevel ListLen #Attr.2; - ret List.650; + let List.611 : U64 = lowlevel ListLen #Attr.2; + ret List.611; procedure List.66 (#Attr.2, #Attr.3): - let List.566 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.566; + let List.564 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.564; procedure List.70 (#Attr.2, #Attr.3): - let List.554 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.554; + let List.552 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.552; procedure List.71 (#Attr.2, #Attr.3): - let List.552 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.552; + let List.550 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.550; procedure List.72 (#Attr.2, #Attr.3, #Attr.4): - let List.533 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; - ret List.533; + let List.531 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; + ret List.531; procedure List.8 (#Attr.2, #Attr.3): - let List.549 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.549; - -procedure List.80 (List.642, List.643, List.644, List.645, List.646): - joinpoint List.601 List.439 List.440 List.441 List.442 List.443: - let List.603 : Int1 = CallByName Num.22 List.442 List.443; - if List.603 then - let List.612 : U8 = CallByName List.66 List.439 List.442; - let List.604 : [C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64], C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64]] = CallByName TotallyNotJson.62 List.440 List.612; - let List.609 : U8 = 1i64; - let List.610 : U8 = GetTagId List.604; - let List.611 : Int1 = lowlevel Eq List.609 List.610; - if List.611 then - let List.444 : [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64] = UnionAtIndex (Id 1) (Index 0) List.604; - let List.607 : U64 = 1i64; - let List.606 : U64 = CallByName Num.19 List.442 List.607; - jump List.601 List.439 List.444 List.441 List.606 List.443; + let List.547 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.547; + +procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): + joinpoint List.599 List.439 List.440 List.441 List.442 List.443: + let List.601 : Int1 = CallByName Num.22 List.442 List.443; + if List.601 then + let List.610 : U8 = CallByName List.66 List.439 List.442; + let List.602 : [C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64], C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64]] = CallByName TotallyNotJson.62 List.440 List.610; + let List.607 : U8 = 1i64; + let List.608 : U8 = GetTagId List.602; + let List.609 : Int1 = lowlevel Eq List.607 List.608; + if List.609 then + let List.444 : [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64] = UnionAtIndex (Id 1) (Index 0) List.602; + let List.605 : U64 = 1i64; + let List.604 : U64 = CallByName Num.19 List.442 List.605; + jump List.599 List.439 List.444 List.441 List.604 List.443; else dec List.439; - let List.445 : [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64] = UnionAtIndex (Id 0) (Index 0) List.604; - let List.608 : [C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64], C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64]] = TagId(0) List.445; - ret List.608; + let List.445 : [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64] = UnionAtIndex (Id 0) (Index 0) List.602; + let List.606 : [C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64], C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64]] = TagId(0) List.445; + ret List.606; else dec List.439; - let List.602 : [C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64], C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64]] = TagId(1) List.440; - ret List.602; + let List.600 : [C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64], C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64]] = TagId(1) List.440; + ret List.600; in - jump List.601 List.642 List.643 List.644 List.645 List.646; + jump List.599 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; procedure List.93 (List.436, List.437, List.438): - let List.599 : U64 = 0i64; - let List.600 : U64 = CallByName List.6 List.436; - let List.598 : [C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64], C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64]] = CallByName List.80 List.436 List.437 List.438 List.599 List.600; - ret List.598; + let List.597 : U64 = 0i64; + let List.598 : U64 = CallByName List.6 List.436; + let List.596 : [C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64], C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64]] = CallByName List.80 List.436 List.437 List.438 List.597 List.598; + ret List.596; procedure Num.19 (#Attr.2, #Attr.3): let Num.295 : U8 = lowlevel NumAdd #Attr.2 #Attr.3; ret Num.295; procedure Num.19 (#Attr.2, #Attr.3): - let Num.345 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.345; + let Num.329 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.329; procedure Num.20 (#Attr.2, #Attr.3): let Num.307 : U8 = lowlevel NumSub #Attr.2 #Attr.3; ret Num.307; procedure Num.22 (#Attr.2, #Attr.3): - let Num.344 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.344; + let Num.328 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.328; procedure Num.23 (#Attr.2, #Attr.3): let Num.313 : Int1 = lowlevel NumLte #Attr.2 #Attr.3; @@ -228,8 +228,8 @@ procedure Num.72 (#Attr.2, #Attr.3): ret Num.293; procedure Num.77 (#Attr.2, #Attr.3): - let Num.341 : U64 = lowlevel NumSubSaturated #Attr.2 #Attr.3; - ret Num.341; + let Num.325 : U64 = lowlevel NumSubSaturated #Attr.2 #Attr.3; + ret Num.325; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): let Str.307 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; @@ -247,8 +247,8 @@ procedure Str.9 (Str.79): else let Str.300 : U8 = StructAtIndex 3 Str.80; let Str.301 : U64 = StructAtIndex 0 Str.80; - let #Derived_gen.1 : Str = StructAtIndex 1 Str.80; - dec #Derived_gen.1; + let #Derived_gen.7 : Str = StructAtIndex 1 Str.80; + dec #Derived_gen.7; let Str.299 : {U64, U8} = Struct {Str.301, Str.300}; let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299; ret Str.298; @@ -365,8 +365,8 @@ procedure TotallyNotJson.534 (TotallyNotJson.535): procedure TotallyNotJson.536 (TotallyNotJson.1192): let TotallyNotJson.1193 : List U8 = StructAtIndex 1 TotallyNotJson.1192; - let #Derived_gen.0 : List U8 = StructAtIndex 0 TotallyNotJson.1192; - dec #Derived_gen.0; + let #Derived_gen.6 : List U8 = StructAtIndex 0 TotallyNotJson.1192; + dec #Derived_gen.6; ret TotallyNotJson.1193; procedure TotallyNotJson.60 (): @@ -733,7 +733,7 @@ procedure TotallyNotJson.69 (): let TotallyNotJson.1247 : List U8 = CallByName TotallyNotJson.68 TotallyNotJson.1248 TotallyNotJson.1249 TotallyNotJson.1250 TotallyNotJson.1251; ret TotallyNotJson.1247; -procedure TotallyNotJson.70 (TotallyNotJson.1468): +procedure TotallyNotJson.70 (#Derived_gen.5): joinpoint TotallyNotJson.1198 TotallyNotJson.1166: let TotallyNotJson.600 : List U8 = StructAtIndex 0 TotallyNotJson.1166; inc 4 TotallyNotJson.600; @@ -834,4 +834,4 @@ procedure TotallyNotJson.70 (TotallyNotJson.1468): let TotallyNotJson.1276 : {List U8, List U8} = Struct {TotallyNotJson.600, TotallyNotJson.601}; ret TotallyNotJson.1276; in - jump TotallyNotJson.1198 TotallyNotJson.1468; + jump TotallyNotJson.1198 #Derived_gen.5; diff --git a/crates/compiler/test_mono/generated/issue_4770.txt b/crates/compiler/test_mono/generated/issue_4770.txt index 7c43ab26ff1..7c010f63343 100644 --- a/crates/compiler/test_mono/generated/issue_4770.txt +++ b/crates/compiler/test_mono/generated/issue_4770.txt @@ -48,7 +48,7 @@ procedure List.66 (#Attr.2, #Attr.3): let List.549 : {[C I64, C List *self], [C I64, C List *self]} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; ret List.549; -procedure List.80 (List.561, List.562, List.563, List.564, List.565): +procedure List.80 (#Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4, #Derived_gen.5): joinpoint List.537 List.439 List.440 List.441 List.442 List.443: let List.539 : Int1 = CallByName Num.22 List.442 List.443; if List.539 then @@ -73,7 +73,7 @@ procedure List.80 (List.561, List.562, List.563, List.564, List.565): let List.538 : [C {}, C {}] = TagId(1) List.440; ret List.538; in - jump List.537 List.561 List.562 List.563 List.564 List.565; + jump List.537 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5; procedure List.93 (List.436, List.437, List.438): let List.535 : U64 = 0i64; @@ -93,7 +93,7 @@ procedure Num.22 (#Attr.2, #Attr.3): let Num.295 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; ret Num.295; -procedure Test.1 (Test.77): +procedure Test.1 (#Derived_gen.0): joinpoint Test.26 Test.6: let Test.65 : [C I64, C List *self] = StructAtIndex 1 Test.6; let Test.66 : U8 = 0i64; @@ -110,17 +110,17 @@ procedure Test.1 (Test.77): let Test.49 : [C I64, C List *self] = StructAtIndex 1 Test.6; dec Test.50; let Test.10 : I64 = UnionAtIndex (Id 0) (Index 0) Test.49; - joinpoint #Derived_gen.0: + joinpoint #Derived_gen.6: let Test.27 : Int1 = CallByName Num.22 Test.8 Test.10; ret Test.27; in - let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.49; - if #Derived_gen.1 then - decref Test.49; - jump #Derived_gen.0; + let #Derived_gen.7 : Int1 = lowlevel RefCountIsUnique Test.49; + if #Derived_gen.7 then + free Test.49; + jump #Derived_gen.6; else decref Test.49; - jump #Derived_gen.0; + jump #Derived_gen.6; else let Test.39 : [C I64, C List *self] = StructAtIndex 0 Test.6; let Test.42 : [C I64, C List *self] = StructAtIndex 1 Test.6; @@ -141,7 +141,7 @@ procedure Test.1 (Test.77): dec Test.52; let Test.14 : List [C I64, C List *self] = UnionAtIndex (Id 1) (Index 0) Test.51; inc Test.14; - joinpoint #Derived_gen.2: + joinpoint #Derived_gen.8: let Test.35 : {} = Struct {}; let Test.33 : List {[C I64, C List *self], [C I64, C List *self]} = CallByName List.23 Test.12 Test.14 Test.35; let Test.34 : {} = Struct {}; @@ -159,14 +159,14 @@ procedure Test.1 (Test.77): let Test.28 : Int1 = CallByName Bool.1; ret Test.28; in - let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique Test.51; - if #Derived_gen.3 then - decref Test.51; - jump #Derived_gen.2; + let #Derived_gen.9 : Int1 = lowlevel RefCountIsUnique Test.51; + if #Derived_gen.9 then + free Test.51; + jump #Derived_gen.8; else inc Test.14; decref Test.51; - jump #Derived_gen.2; + jump #Derived_gen.8; else let Test.48 : [C I64, C List *self] = StructAtIndex 0 Test.6; let Test.47 : List [C I64, C List *self] = Array [Test.48]; @@ -175,7 +175,7 @@ procedure Test.1 (Test.77): let Test.44 : {[C I64, C List *self], [C I64, C List *self]} = Struct {Test.45, Test.46}; jump Test.26 Test.44; in - jump Test.26 Test.77; + jump Test.26 #Derived_gen.0; procedure Test.15 (Test.16, Test.17): let Test.36 : {[C I64, C List *self], [C I64, C List *self]} = Struct {Test.16, Test.17}; diff --git a/crates/compiler/test_mono/generated/issue_4772_weakened_monomorphic_destructure.txt b/crates/compiler/test_mono/generated/issue_4772_weakened_monomorphic_destructure.txt index d94e756d715..9e4b221fed2 100644 --- a/crates/compiler/test_mono/generated/issue_4772_weakened_monomorphic_destructure.txt +++ b/crates/compiler/test_mono/generated/issue_4772_weakened_monomorphic_destructure.txt @@ -1,30 +1,30 @@ procedure Bool.1 (): - let Bool.55 : Int1 = false; - ret Bool.55; + let Bool.51 : Int1 = false; + ret Bool.51; procedure Bool.11 (#Attr.2, #Attr.3): let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3; ret Bool.23; procedure Bool.11 (#Attr.2, #Attr.3): - let Bool.46 : Int1 = lowlevel Eq #Attr.2 #Attr.3; - ret Bool.46; + let Bool.42 : Int1 = lowlevel Eq #Attr.2 #Attr.3; + ret Bool.42; procedure Bool.11 (#Attr.2, #Attr.3): - let Bool.67 : Int1 = lowlevel Eq #Attr.2 #Attr.3; - ret Bool.67; + let Bool.57 : Int1 = lowlevel Eq #Attr.2 #Attr.3; + ret Bool.57; procedure Bool.2 (): - let Bool.54 : Int1 = true; - ret Bool.54; + let Bool.50 : Int1 = true; + ret Bool.50; procedure Bool.3 (#Attr.2, #Attr.3): - let Bool.37 : Int1 = lowlevel And #Attr.2 #Attr.3; - ret Bool.37; + let Bool.33 : Int1 = lowlevel And #Attr.2 #Attr.3; + ret Bool.33; procedure Bool.4 (#Attr.2, #Attr.3): - let Bool.57 : Int1 = lowlevel Or #Attr.2 #Attr.3; - ret Bool.57; + let Bool.53 : Int1 = lowlevel Or #Attr.2 #Attr.3; + ret Bool.53; procedure Decode.24 (Decode.101): ret Decode.101; @@ -114,8 +114,8 @@ procedure List.49 (List.376, List.377): ret List.575; procedure List.6 (#Attr.2): - let List.644 : U64 = lowlevel ListLen #Attr.2; - ret List.644; + let List.607 : U64 = lowlevel ListLen #Attr.2; + ret List.607; procedure List.66 (#Attr.2, #Attr.3): let List.560 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; @@ -137,7 +137,7 @@ procedure List.8 (#Attr.2, #Attr.3): let List.543 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; ret List.543; -procedure List.80 (List.636, List.637, List.638, List.639, List.640): +procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): joinpoint List.595 List.439 List.440 List.441 List.442 List.443: let List.597 : Int1 = CallByName Num.22 List.442 List.443; if List.597 then @@ -161,7 +161,7 @@ procedure List.80 (List.636, List.637, List.638, List.639, List.640): let List.596 : [C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64], C [C U64, C U64, C U64, C , C , C U64, C U64, C U64, C U64]] = TagId(1) List.440; ret List.596; in - jump List.595 List.636 List.637 List.638 List.639 List.640; + jump List.595 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; procedure List.93 (List.436, List.437, List.438): let List.593 : U64 = 0i64; @@ -174,16 +174,16 @@ procedure Num.19 (#Attr.2, #Attr.3): ret Num.295; procedure Num.19 (#Attr.2, #Attr.3): - let Num.345 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.345; + let Num.329 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.329; procedure Num.20 (#Attr.2, #Attr.3): let Num.307 : U8 = lowlevel NumSub #Attr.2 #Attr.3; ret Num.307; procedure Num.22 (#Attr.2, #Attr.3): - let Num.344 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.344; + let Num.328 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.328; procedure Num.23 (#Attr.2, #Attr.3): let Num.313 : Int1 = lowlevel NumLte #Attr.2 #Attr.3; @@ -202,8 +202,8 @@ procedure Num.72 (#Attr.2, #Attr.3): ret Num.293; procedure Num.77 (#Attr.2, #Attr.3): - let Num.341 : U64 = lowlevel NumSubSaturated #Attr.2 #Attr.3; - ret Num.341; + let Num.325 : U64 = lowlevel NumSubSaturated #Attr.2 #Attr.3; + ret Num.325; procedure Str.12 (#Attr.2): let Str.307 : List U8 = lowlevel StrToUtf8 #Attr.2; @@ -218,8 +218,8 @@ procedure Str.47 (#Attr.2): ret Str.306; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): - let Str.321 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; - ret Str.321; + let Str.317 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; + ret Str.317; procedure Str.72 (Str.244): let Str.245 : {I64, U8} = CallByName Str.47 Str.244; @@ -237,22 +237,22 @@ procedure Str.72 (Str.244): ret Str.299; procedure Str.9 (Str.79): - let Str.319 : U64 = 0i64; - let Str.320 : U64 = CallByName List.6 Str.79; - let Str.80 : {U64, Str, Int1, U8} = CallByName Str.48 Str.79 Str.319 Str.320; - let Str.316 : Int1 = StructAtIndex 2 Str.80; - if Str.316 then - let Str.318 : Str = StructAtIndex 1 Str.80; - let Str.317 : [C {U64, U8}, C Str] = TagId(1) Str.318; - ret Str.317; + let Str.315 : U64 = 0i64; + let Str.316 : U64 = CallByName List.6 Str.79; + let Str.80 : {U64, Str, Int1, U8} = CallByName Str.48 Str.79 Str.315 Str.316; + let Str.312 : Int1 = StructAtIndex 2 Str.80; + if Str.312 then + let Str.314 : Str = StructAtIndex 1 Str.80; + let Str.313 : [C {U64, U8}, C Str] = TagId(1) Str.314; + ret Str.313; else - let Str.314 : U8 = StructAtIndex 3 Str.80; - let Str.315 : U64 = StructAtIndex 0 Str.80; - let #Derived_gen.0 : Str = StructAtIndex 1 Str.80; - dec #Derived_gen.0; - let Str.313 : {U64, U8} = Struct {Str.315, Str.314}; - let Str.312 : [C {U64, U8}, C Str] = TagId(0) Str.313; - ret Str.312; + let Str.310 : U8 = StructAtIndex 3 Str.80; + let Str.311 : U64 = StructAtIndex 0 Str.80; + let #Derived_gen.6 : Str = StructAtIndex 1 Str.80; + dec #Derived_gen.6; + let Str.309 : {U64, U8} = Struct {Str.311, Str.310}; + let Str.308 : [C {U64, U8}, C Str] = TagId(0) Str.309; + ret Str.308; procedure Test.0 (): let Test.37 : Str = "-1234"; @@ -397,8 +397,8 @@ procedure TotallyNotJson.534 (TotallyNotJson.535): procedure TotallyNotJson.536 (TotallyNotJson.1192): let TotallyNotJson.1193 : List U8 = StructAtIndex 1 TotallyNotJson.1192; - let #Derived_gen.1 : List U8 = StructAtIndex 0 TotallyNotJson.1192; - dec #Derived_gen.1; + let #Derived_gen.7 : List U8 = StructAtIndex 0 TotallyNotJson.1192; + dec #Derived_gen.7; ret TotallyNotJson.1193; procedure TotallyNotJson.60 (): @@ -765,7 +765,7 @@ procedure TotallyNotJson.69 (): let TotallyNotJson.1247 : List U8 = CallByName TotallyNotJson.68 TotallyNotJson.1248 TotallyNotJson.1249 TotallyNotJson.1250 TotallyNotJson.1251; ret TotallyNotJson.1247; -procedure TotallyNotJson.70 (TotallyNotJson.1468): +procedure TotallyNotJson.70 (#Derived_gen.5): joinpoint TotallyNotJson.1198 TotallyNotJson.1166: let TotallyNotJson.600 : List U8 = StructAtIndex 0 TotallyNotJson.1166; inc 4 TotallyNotJson.600; @@ -866,4 +866,4 @@ procedure TotallyNotJson.70 (TotallyNotJson.1468): let TotallyNotJson.1276 : {List U8, List U8} = Struct {TotallyNotJson.600, TotallyNotJson.601}; ret TotallyNotJson.1276; in - jump TotallyNotJson.1198 TotallyNotJson.1468; + jump TotallyNotJson.1198 #Derived_gen.5; diff --git a/crates/compiler/test_mono/generated/layout_cache_structure_with_multiple_recursive_structures.txt b/crates/compiler/test_mono/generated/layout_cache_structure_with_multiple_recursive_structures.txt index 96ce04fefab..d94a1b57be2 100644 --- a/crates/compiler/test_mono/generated/layout_cache_structure_with_multiple_recursive_structures.txt +++ b/crates/compiler/test_mono/generated/layout_cache_structure_with_multiple_recursive_structures.txt @@ -14,7 +14,7 @@ procedure List.66 (#Attr.2, #Attr.3): let List.537 : [C *self, ] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; ret List.537; -procedure List.80 (List.544, List.545, List.546, List.547, List.548): +procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): joinpoint List.527 List.439 List.440 List.441 List.442 List.443: let List.529 : Int1 = CallByName Num.22 List.442 List.443; if List.529 then @@ -28,7 +28,7 @@ procedure List.80 (List.544, List.545, List.546, List.547, List.548): dec List.439; ret List.440; in - jump List.527 List.544 List.545 List.546 List.547 List.548; + jump List.527 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; procedure List.93 (List.436, List.437, List.438): let List.525 : U64 = 0i64; diff --git a/crates/compiler/test_mono/generated/linked_list_filter.txt b/crates/compiler/test_mono/generated/linked_list_filter.txt new file mode 100644 index 00000000000..988e96f2fa0 --- /dev/null +++ b/crates/compiler/test_mono/generated/linked_list_filter.txt @@ -0,0 +1,56 @@ +procedure Num.31 (Num.234): + let Num.293 : I64 = 2i64; + let Num.292 : Int1 = CallByName Num.86 Num.234 Num.293; + ret Num.292; + +procedure Num.86 (#Attr.2, #Attr.3): + let Num.294 : Int1 = lowlevel NumIsMultipleOf #Attr.2 #Attr.3; + ret Num.294; + +procedure Test.2 (#Derived_gen.0, #Derived_gen.1): + let #Derived_gen.3 : [, C I64 *self] = NullPointer; + let #Derived_gen.2 : Ptr([, C I64 *self]) = lowlevel Alloca #Derived_gen.3; + joinpoint #Derived_gen.4 Test.4 Test.5 #Derived_gen.5 #Derived_gen.6: + let Test.22 : U8 = 1i64; + let Test.23 : U8 = GetTagId Test.4; + let Test.24 : Int1 = lowlevel Eq Test.22 Test.23; + if Test.24 then + let Test.17 : [, C I64 *self] = TagId(1) ; + let #Derived_gen.8 : {} = lowlevel PtrStore #Derived_gen.5 Test.17; + let #Derived_gen.7 : [, C I64 *self] = lowlevel PtrLoad #Derived_gen.6; + ret #Derived_gen.7; + else + let Test.7 : I64 = UnionAtIndex (Id 0) (Index 0) Test.4; + let Test.8 : [, C I64 *self] = UnionAtIndex (Id 0) (Index 1) Test.4; + joinpoint #Derived_gen.12 #Derived_gen.14: + let Test.19 : Int1 = CallByName Num.31 Test.7; + if Test.19 then + let #Derived_gen.9 : [, C I64 *self] = NullPointer; + let Test.20 : [, C I64 *self] = Reuse #Derived_gen.14 UpdateModeId { id: 1 } TagId(0) Test.7 #Derived_gen.9; + let #Derived_gen.10 : Ptr([, C I64 *self]) = UnionFieldPtrAtIndex (Id 0) (Index 1) Test.20; + let #Derived_gen.11 : {} = lowlevel PtrStore #Derived_gen.5 Test.20; + jump #Derived_gen.4 Test.8 Test.5 #Derived_gen.10 #Derived_gen.6; + else + decref #Derived_gen.14; + jump #Derived_gen.4 Test.8 Test.5 #Derived_gen.5 #Derived_gen.6; + in + let #Derived_gen.13 : Int1 = lowlevel RefCountIsUnique Test.4; + if #Derived_gen.13 then + jump #Derived_gen.12 Test.4; + else + inc Test.8; + decref Test.4; + let #Derived_gen.15 : [, C I64 *self] = NullPointer; + jump #Derived_gen.12 #Derived_gen.15; + in + jump #Derived_gen.4 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.2; + +procedure Test.0 (): + let Test.25 : I64 = 1i64; + let Test.27 : I64 = 2i64; + let Test.28 : [, C I64 *self] = TagId(1) ; + let Test.26 : [, C I64 *self] = TagId(0) Test.27 Test.28; + let Test.14 : [, C I64 *self] = TagId(0) Test.25 Test.26; + let Test.15 : {} = Struct {}; + let Test.13 : [, C I64 *self] = CallByName Test.2 Test.14 Test.15; + ret Test.13; diff --git a/crates/compiler/test_mono/generated/linked_list_map.txt b/crates/compiler/test_mono/generated/linked_list_map.txt new file mode 100644 index 00000000000..8e57cb49a4e --- /dev/null +++ b/crates/compiler/test_mono/generated/linked_list_map.txt @@ -0,0 +1,50 @@ +procedure Num.19 (#Attr.2, #Attr.3): + let Num.292 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.292; + +procedure Test.10 (Test.11): + let Test.28 : I64 = 1i64; + let Test.27 : I64 = CallByName Num.19 Test.11 Test.28; + ret Test.27; + +procedure Test.2 (#Derived_gen.0, #Derived_gen.1): + let #Derived_gen.3 : [, C I64 *self] = NullPointer; + let #Derived_gen.2 : Ptr([, C I64 *self]) = lowlevel Alloca #Derived_gen.3; + joinpoint #Derived_gen.4 Test.4 Test.5 #Derived_gen.5 #Derived_gen.6: + let Test.22 : U8 = 1i64; + let Test.23 : U8 = GetTagId Test.5; + let Test.24 : Int1 = lowlevel Eq Test.22 Test.23; + if Test.24 then + let Test.18 : [, C I64 *self] = TagId(1) ; + let #Derived_gen.8 : {} = lowlevel PtrStore #Derived_gen.5 Test.18; + let #Derived_gen.7 : [, C I64 *self] = lowlevel PtrLoad #Derived_gen.6; + ret #Derived_gen.7; + else + let Test.7 : I64 = UnionAtIndex (Id 0) (Index 0) Test.5; + let Test.8 : [, C I64 *self] = UnionAtIndex (Id 0) (Index 1) Test.5; + joinpoint #Derived_gen.12 #Derived_gen.14: + let Test.20 : I64 = CallByName Test.10 Test.7; + let #Derived_gen.9 : [, C I64 *self] = NullPointer; + let Test.19 : [, C I64 *self] = Reuse #Derived_gen.14 UpdateModeId { id: 1 } TagId(0) Test.20 #Derived_gen.9; + let #Derived_gen.10 : Ptr([, C I64 *self]) = UnionFieldPtrAtIndex (Id 0) (Index 1) Test.19; + let #Derived_gen.11 : {} = lowlevel PtrStore #Derived_gen.5 Test.19; + jump #Derived_gen.4 Test.4 Test.8 #Derived_gen.10 #Derived_gen.6; + in + let #Derived_gen.13 : Int1 = lowlevel RefCountIsUnique Test.5; + if #Derived_gen.13 then + jump #Derived_gen.12 Test.5; + else + inc Test.8; + decref Test.5; + let #Derived_gen.15 : [, C I64 *self] = NullPointer; + jump #Derived_gen.12 #Derived_gen.15; + in + jump #Derived_gen.4 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.2; + +procedure Test.0 (): + let Test.15 : {} = Struct {}; + let Test.25 : I64 = 42i64; + let Test.26 : [, C I64 *self] = TagId(1) ; + let Test.16 : [, C I64 *self] = TagId(0) Test.25 Test.26; + let Test.14 : [, C I64 *self] = CallByName Test.2 Test.15 Test.16; + ret Test.14; diff --git a/crates/compiler/test_mono/generated/linked_list_reverse.txt b/crates/compiler/test_mono/generated/linked_list_reverse.txt new file mode 100644 index 00000000000..892df1a7132 --- /dev/null +++ b/crates/compiler/test_mono/generated/linked_list_reverse.txt @@ -0,0 +1,36 @@ +procedure Test.2 (Test.5): + let Test.17 : [, C I64 *self] = TagId(1) ; + let Test.16 : [, C I64 *self] = CallByName Test.3 Test.17 Test.5; + ret Test.16; + +procedure Test.3 (#Derived_gen.0, #Derived_gen.1): + joinpoint Test.18 Test.7 Test.8: + let Test.22 : U8 = 1i64; + let Test.23 : U8 = GetTagId Test.8; + let Test.24 : Int1 = lowlevel Eq Test.22 Test.23; + if Test.24 then + ret Test.7; + else + let Test.9 : I64 = UnionAtIndex (Id 0) (Index 0) Test.8; + let Test.10 : [, C I64 *self] = UnionAtIndex (Id 0) (Index 1) Test.8; + joinpoint #Derived_gen.2 #Derived_gen.4: + let Test.21 : [, C I64 *self] = Reuse #Derived_gen.4 UpdateModeId { id: 1 } TagId(0) Test.9 Test.7; + jump Test.18 Test.21 Test.10; + in + let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique Test.8; + if #Derived_gen.3 then + jump #Derived_gen.2 Test.8; + else + inc Test.10; + decref Test.8; + let #Derived_gen.5 : [, C I64 *self] = NullPointer; + jump #Derived_gen.2 #Derived_gen.5; + in + jump Test.18 #Derived_gen.0 #Derived_gen.1; + +procedure Test.0 (): + let Test.25 : I64 = 42i64; + let Test.26 : [, C I64 *self] = TagId(1) ; + let Test.15 : [, C I64 *self] = TagId(0) Test.25 Test.26; + let Test.14 : [, C I64 *self] = CallByName Test.2 Test.15; + ret Test.14; diff --git a/crates/compiler/test_mono/generated/peano1.txt b/crates/compiler/test_mono/generated/peano1.txt index 1d291bf98f9..7ec0b3c2f5f 100644 --- a/crates/compiler/test_mono/generated/peano1.txt +++ b/crates/compiler/test_mono/generated/peano1.txt @@ -17,7 +17,7 @@ procedure Test.0 (): let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.2; if #Derived_gen.1 then dec Test.13; - decref Test.2; + free Test.2; jump #Derived_gen.0; else decref Test.2; diff --git a/crates/compiler/test_mono/generated/peano2.txt b/crates/compiler/test_mono/generated/peano2.txt index e4d53cc3be9..f090c272ddc 100644 --- a/crates/compiler/test_mono/generated/peano2.txt +++ b/crates/compiler/test_mono/generated/peano2.txt @@ -22,7 +22,7 @@ procedure Test.0 (): in let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.2; if #Derived_gen.1 then - decref Test.2; + free Test.2; jump #Derived_gen.0; else inc Test.12; diff --git a/crates/compiler/test_mono/generated/polymorphic_expression_unification.txt b/crates/compiler/test_mono/generated/polymorphic_expression_unification.txt index 8984da0b47e..de6d1acdc53 100644 --- a/crates/compiler/test_mono/generated/polymorphic_expression_unification.txt +++ b/crates/compiler/test_mono/generated/polymorphic_expression_unification.txt @@ -37,7 +37,7 @@ procedure Test.0 (): let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.15; if #Derived_gen.1 then dec Test.16; - decref Test.15; + free Test.15; jump #Derived_gen.0; else decref Test.15; diff --git a/crates/compiler/test_mono/generated/quicksort_help.txt b/crates/compiler/test_mono/generated/quicksort_help.txt index 9afb51dda4f..8f8eb676946 100644 --- a/crates/compiler/test_mono/generated/quicksort_help.txt +++ b/crates/compiler/test_mono/generated/quicksort_help.txt @@ -10,7 +10,7 @@ procedure Num.22 (#Attr.2, #Attr.3): let Num.294 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; ret Num.294; -procedure Test.1 (Test.24, Test.25, Test.26): +procedure Test.1 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2): joinpoint Test.12 Test.2 Test.3 Test.4: let Test.14 : Int1 = CallByName Num.22 Test.3 Test.4; if Test.14 then @@ -29,7 +29,7 @@ procedure Test.1 (Test.24, Test.25, Test.26): else ret Test.2; in - jump Test.12 Test.24 Test.25 Test.26; + jump Test.12 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2; procedure Test.0 (): let Test.9 : List I64 = Array []; diff --git a/crates/compiler/test_mono/generated/rb_tree_fbip.txt b/crates/compiler/test_mono/generated/rb_tree_fbip.txt index 5a6b8aed152..28f4d749260 100644 --- a/crates/compiler/test_mono/generated/rb_tree_fbip.txt +++ b/crates/compiler/test_mono/generated/rb_tree_fbip.txt @@ -6,415 +6,442 @@ procedure Num.24 (#Attr.2, #Attr.3): let Num.293 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; ret Num.293; -procedure Test.3 (Test.9, Test.10, Test.11): - let Test.254 : U8 = 0i64; - let Test.255 : U8 = GetTagId Test.9; - let Test.256 : Int1 = lowlevel Eq Test.254 Test.255; - if Test.256 then - let Test.113 : [C *self I64 *self I32 Int1, ] = TagId(0) ; - let Test.114 : [C *self I64 *self I32 Int1, ] = TagId(0) ; - let Test.112 : Int1 = true; - let Test.111 : [C *self I64 *self I32 Int1, ] = TagId(1) Test.113 Test.11 Test.114 Test.10 Test.112; - ret Test.111; - else - let Test.251 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.9; - let Test.252 : Int1 = false; - let Test.253 : Int1 = lowlevel Eq Test.252 Test.251; - if Test.253 then - let Test.16 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.9; - let Test.18 : I64 = UnionAtIndex (Id 1) (Index 1) Test.9; - let Test.19 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.9; - let Test.17 : I32 = UnionAtIndex (Id 1) (Index 3) Test.9; - joinpoint #Derived_gen.72 #Derived_gen.77: - let Test.179 : Int1 = CallByName Num.22 Test.10 Test.17; - if Test.179 then - joinpoint Test.238 #Derived_gen.139: - let Test.233 : [C *self I64 *self I32 Int1, ] = CallByName Test.3 Test.16 Test.10 Test.11; - let Test.232 : Int1 = false; - let Test.231 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.139 UpdateModeId { id: 56 } TagId(1) Test.233 Test.18 Test.19 Test.17 Test.232; - ret Test.231; - in - let Test.236 : U8 = 1i64; - let Test.237 : U8 = GetTagId Test.16; - let Test.240 : Int1 = lowlevel Eq Test.236 Test.237; - if Test.240 then - let Test.234 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.16; - let Test.235 : Int1 = true; - let Test.239 : Int1 = lowlevel Eq Test.235 Test.234; - if Test.239 then - let Test.180 : [C *self I64 *self I32 Int1, ] = CallByName Test.3 Test.16 Test.10 Test.11; - joinpoint Test.199 #Derived_gen.166: - let Test.198 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.180; - let Test.20 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.198; - inc Test.20; - let Test.22 : I64 = UnionAtIndex (Id 1) (Index 1) Test.198; - let Test.23 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.198; - inc Test.23; - let Test.21 : I32 = UnionAtIndex (Id 1) (Index 3) Test.198; - let Test.25 : I64 = UnionAtIndex (Id 1) (Index 1) Test.180; - let Test.26 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.180; - let Test.24 : I32 = UnionAtIndex (Id 1) (Index 3) Test.180; - joinpoint #Derived_gen.30 #Derived_gen.169 #Derived_gen.170 #Derived_gen.171: - let Test.186 : Int1 = false; - let Test.183 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.171 UpdateModeId { id: 85 } TagId(1) Test.20 Test.22 Test.23 Test.21 Test.186; - let Test.185 : Int1 = false; - let Test.184 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.170 UpdateModeId { id: 84 } TagId(1) Test.26 Test.18 Test.19 Test.17 Test.185; - let Test.182 : Int1 = true; - let Test.181 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.169 UpdateModeId { id: 83 } TagId(1) Test.183 Test.25 Test.184 Test.24 Test.182; - ret Test.181; - in - let #Derived_gen.31 : Int1 = lowlevel RefCountIsUnique Test.180; - if #Derived_gen.31 then - let #Derived_gen.172 : [C *self I64 *self I32 Int1, ] = Reset { symbol: Test.198, id: UpdateModeId { id: 86 } }; - let #Derived_gen.173 : [C *self I64 *self I32 Int1, ] = ResetRef { symbol: Test.180, id: UpdateModeId { id: 87 } }; - jump #Derived_gen.30 #Derived_gen.166 #Derived_gen.172 #Derived_gen.173; - else - inc Test.26; - decref Test.180; - let #Derived_gen.174 : [C *self I64 *self I32 Int1, ] = NullPointer; - jump #Derived_gen.30 #Derived_gen.174 #Derived_gen.174 #Derived_gen.166; - in - let Test.228 : U8 = 1i64; - let Test.229 : U8 = GetTagId Test.180; - let Test.230 : Int1 = lowlevel Eq Test.228 Test.229; - if Test.230 then - joinpoint Test.225 #Derived_gen.184: - joinpoint Test.216 #Derived_gen.185: - let Test.46 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.180; - let Test.48 : I64 = UnionAtIndex (Id 1) (Index 1) Test.180; - let Test.49 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.180; - let Test.47 : I32 = UnionAtIndex (Id 1) (Index 3) Test.180; - joinpoint #Derived_gen.24 #Derived_gen.187 #Derived_gen.188: - let Test.196 : Int1 = true; - let Test.195 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.188 UpdateModeId { id: 100 } TagId(1) Test.46 Test.48 Test.49 Test.47 Test.196; - let Test.194 : Int1 = false; - let Test.193 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.187 UpdateModeId { id: 99 } TagId(1) Test.195 Test.18 Test.19 Test.17 Test.194; - ret Test.193; - in - let #Derived_gen.25 : Int1 = lowlevel RefCountIsUnique Test.180; - if #Derived_gen.25 then - let #Derived_gen.189 : [C *self I64 *self I32 Int1, ] = ResetRef { symbol: Test.180, id: UpdateModeId { id: 101 } }; - jump #Derived_gen.24 #Derived_gen.185 #Derived_gen.189; - else - inc Test.46; - inc Test.49; - decref Test.180; - let #Derived_gen.190 : [C *self I64 *self I32 Int1, ] = NullPointer; - jump #Derived_gen.24 #Derived_gen.190 #Derived_gen.185; +procedure Test.3 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2): + let #Derived_gen.4 : [C *self I64 *self I32 Int1, ] = NullPointer; + let #Derived_gen.3 : Ptr([C *self I64 *self I32 Int1, ]) = lowlevel Alloca #Derived_gen.4; + joinpoint #Derived_gen.5 Test.9 Test.10 Test.11 #Derived_gen.6 #Derived_gen.7: + let Test.254 : U8 = 0i64; + let Test.255 : U8 = GetTagId Test.9; + let Test.256 : Int1 = lowlevel Eq Test.254 Test.255; + if Test.256 then + let Test.113 : [C *self I64 *self I32 Int1, ] = TagId(0) ; + let Test.114 : [C *self I64 *self I32 Int1, ] = TagId(0) ; + let Test.112 : Int1 = true; + let Test.111 : [C *self I64 *self I32 Int1, ] = TagId(1) Test.113 Test.11 Test.114 Test.10 Test.112; + let #Derived_gen.9 : {} = lowlevel PtrStore #Derived_gen.6 Test.111; + let #Derived_gen.8 : [C *self I64 *self I32 Int1, ] = lowlevel PtrLoad #Derived_gen.7; + ret #Derived_gen.8; + else + let Test.251 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.9; + let Test.252 : Int1 = false; + let Test.253 : Int1 = lowlevel Eq Test.252 Test.251; + if Test.253 then + let Test.16 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.9; + let Test.18 : I64 = UnionAtIndex (Id 1) (Index 1) Test.9; + let Test.19 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.9; + let Test.17 : I32 = UnionAtIndex (Id 1) (Index 3) Test.9; + joinpoint #Derived_gen.114 #Derived_gen.118: + let Test.179 : Int1 = CallByName Num.22 Test.10 Test.17; + if Test.179 then + joinpoint Test.238 #Derived_gen.166: + let Test.232 : Int1 = false; + let #Derived_gen.10 : [C *self I64 *self I32 Int1, ] = NullPointer; + let Test.231 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.166 UpdateModeId { id: 56 } TagId(1) #Derived_gen.10 Test.18 Test.19 Test.17 Test.232; + let #Derived_gen.11 : Ptr([C *self I64 *self I32 Int1, ]) = UnionFieldPtrAtIndex (Id 1) (Index 0) Test.231; + let #Derived_gen.12 : {} = lowlevel PtrStore #Derived_gen.6 Test.231; + jump #Derived_gen.5 Test.16 Test.10 Test.11 #Derived_gen.11 #Derived_gen.7; + in + let Test.236 : U8 = 1i64; + let Test.237 : U8 = GetTagId Test.16; + let Test.240 : Int1 = lowlevel Eq Test.236 Test.237; + if Test.240 then + let Test.234 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.16; + let Test.235 : Int1 = true; + let Test.239 : Int1 = lowlevel Eq Test.235 Test.234; + if Test.239 then + let Test.180 : [C *self I64 *self I32 Int1, ] = CallByName Test.3 Test.16 Test.10 Test.11; + joinpoint Test.199 #Derived_gen.187: + let Test.198 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.180; + let Test.20 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.198; + inc Test.20; + let Test.22 : I64 = UnionAtIndex (Id 1) (Index 1) Test.198; + let Test.23 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.198; + inc Test.23; + let Test.21 : I32 = UnionAtIndex (Id 1) (Index 3) Test.198; + let Test.25 : I64 = UnionAtIndex (Id 1) (Index 1) Test.180; + let Test.26 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.180; + let Test.24 : I32 = UnionAtIndex (Id 1) (Index 3) Test.180; + joinpoint #Derived_gen.72 #Derived_gen.189 #Derived_gen.190 #Derived_gen.191: + let Test.186 : Int1 = false; + let Test.183 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.191 UpdateModeId { id: 85 } TagId(1) Test.20 Test.22 Test.23 Test.21 Test.186; + let Test.185 : Int1 = false; + let Test.184 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.190 UpdateModeId { id: 84 } TagId(1) Test.26 Test.18 Test.19 Test.17 Test.185; + let Test.182 : Int1 = true; + let Test.181 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.189 UpdateModeId { id: 83 } TagId(1) Test.183 Test.25 Test.184 Test.24 Test.182; + let #Derived_gen.14 : {} = lowlevel PtrStore #Derived_gen.6 Test.181; + let #Derived_gen.13 : [C *self I64 *self I32 Int1, ] = lowlevel PtrLoad #Derived_gen.7; + ret #Derived_gen.13; in - let Test.213 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.180; - let Test.214 : U8 = 1i64; - let Test.215 : U8 = GetTagId Test.213; - let Test.218 : Int1 = lowlevel Eq Test.214 Test.215; - if Test.218 then - let Test.210 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.180; - let Test.211 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.210; - let Test.212 : Int1 = true; - let Test.217 : Int1 = lowlevel Eq Test.212 Test.211; - if Test.217 then - jump Test.199 #Derived_gen.184; - else - jump Test.216 #Derived_gen.184; + let #Derived_gen.73 : Int1 = lowlevel RefCountIsUnique Test.180; + if #Derived_gen.73 then + let #Derived_gen.192 : [C *self I64 *self I32 Int1, ] = Reset { symbol: Test.198, id: UpdateModeId { id: 86 } }; + jump #Derived_gen.72 #Derived_gen.187 #Derived_gen.192 Test.180; else - jump Test.216 #Derived_gen.184; + inc Test.26; + decref Test.180; + let #Derived_gen.193 : [C *self I64 *self I32 Int1, ] = NullPointer; + jump #Derived_gen.72 #Derived_gen.193 #Derived_gen.193 #Derived_gen.187; in - let Test.222 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.180; - let Test.223 : U8 = 1i64; - let Test.224 : U8 = GetTagId Test.222; - let Test.227 : Int1 = lowlevel Eq Test.223 Test.224; - if Test.227 then - let Test.219 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.180; - let Test.220 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.219; - let Test.221 : Int1 = true; - let Test.226 : Int1 = lowlevel Eq Test.221 Test.220; - if Test.226 then - joinpoint Test.207 #Derived_gen.191: - let Test.33 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.180; - let Test.35 : I64 = UnionAtIndex (Id 1) (Index 1) Test.180; - let Test.200 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.180; - let Test.36 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.200; - inc Test.36; - let Test.38 : I64 = UnionAtIndex (Id 1) (Index 1) Test.200; - let Test.39 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.200; - inc Test.39; - let Test.37 : I32 = UnionAtIndex (Id 1) (Index 3) Test.200; - let Test.34 : I32 = UnionAtIndex (Id 1) (Index 3) Test.180; - joinpoint #Derived_gen.28 #Derived_gen.194 #Derived_gen.195 #Derived_gen.196: - let Test.192 : Int1 = false; - let Test.189 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.196 UpdateModeId { id: 107 } TagId(1) Test.33 Test.35 Test.36 Test.34 Test.192; - let Test.191 : Int1 = false; - let Test.190 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.195 UpdateModeId { id: 106 } TagId(1) Test.39 Test.18 Test.19 Test.17 Test.191; - let Test.188 : Int1 = true; - let Test.187 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.194 UpdateModeId { id: 105 } TagId(1) Test.189 Test.38 Test.190 Test.37 Test.188; - ret Test.187; + let Test.228 : U8 = 1i64; + let Test.229 : U8 = GetTagId Test.180; + let Test.230 : Int1 = lowlevel Eq Test.228 Test.229; + if Test.230 then + joinpoint Test.225 #Derived_gen.201: + joinpoint Test.216 #Derived_gen.202: + let Test.46 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.180; + let Test.48 : I64 = UnionAtIndex (Id 1) (Index 1) Test.180; + let Test.49 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.180; + let Test.47 : I32 = UnionAtIndex (Id 1) (Index 3) Test.180; + joinpoint #Derived_gen.66 #Derived_gen.203 #Derived_gen.204: + let Test.196 : Int1 = true; + let Test.195 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.204 UpdateModeId { id: 100 } TagId(1) Test.46 Test.48 Test.49 Test.47 Test.196; + let Test.194 : Int1 = false; + let Test.193 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.203 UpdateModeId { id: 99 } TagId(1) Test.195 Test.18 Test.19 Test.17 Test.194; + let #Derived_gen.16 : {} = lowlevel PtrStore #Derived_gen.6 Test.193; + let #Derived_gen.15 : [C *self I64 *self I32 Int1, ] = lowlevel PtrLoad #Derived_gen.7; + ret #Derived_gen.15; in - let #Derived_gen.29 : Int1 = lowlevel RefCountIsUnique Test.180; - if #Derived_gen.29 then - let #Derived_gen.197 : [C *self I64 *self I32 Int1, ] = Reset { symbol: Test.200, id: UpdateModeId { id: 108 } }; - let #Derived_gen.198 : [C *self I64 *self I32 Int1, ] = ResetRef { symbol: Test.180, id: UpdateModeId { id: 109 } }; - jump #Derived_gen.28 #Derived_gen.191 #Derived_gen.197 #Derived_gen.198; + let #Derived_gen.67 : Int1 = lowlevel RefCountIsUnique Test.180; + if #Derived_gen.67 then + jump #Derived_gen.66 #Derived_gen.202 Test.180; else - inc Test.33; + inc Test.46; + inc Test.49; decref Test.180; - let #Derived_gen.199 : [C *self I64 *self I32 Int1, ] = NullPointer; - jump #Derived_gen.28 #Derived_gen.199 #Derived_gen.199 #Derived_gen.191; + let #Derived_gen.205 : [C *self I64 *self I32 Int1, ] = NullPointer; + jump #Derived_gen.66 #Derived_gen.205 #Derived_gen.202; in - let Test.204 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.180; - let Test.205 : U8 = 1i64; - let Test.206 : U8 = GetTagId Test.204; - let Test.209 : Int1 = lowlevel Eq Test.205 Test.206; - if Test.209 then - let Test.201 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.180; - let Test.202 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.201; - let Test.203 : Int1 = true; - let Test.208 : Int1 = lowlevel Eq Test.203 Test.202; - if Test.208 then - jump Test.199 #Derived_gen.77; + let Test.213 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.180; + let Test.214 : U8 = 1i64; + let Test.215 : U8 = GetTagId Test.213; + let Test.218 : Int1 = lowlevel Eq Test.214 Test.215; + if Test.218 then + let Test.210 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.180; + let Test.211 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.210; + let Test.212 : Int1 = true; + let Test.217 : Int1 = lowlevel Eq Test.212 Test.211; + if Test.217 then + jump Test.199 #Derived_gen.201; + else + jump Test.216 #Derived_gen.201; + else + jump Test.216 #Derived_gen.201; + in + let Test.222 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.180; + let Test.223 : U8 = 1i64; + let Test.224 : U8 = GetTagId Test.222; + let Test.227 : Int1 = lowlevel Eq Test.223 Test.224; + if Test.227 then + let Test.219 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.180; + let Test.220 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.219; + let Test.221 : Int1 = true; + let Test.226 : Int1 = lowlevel Eq Test.221 Test.220; + if Test.226 then + joinpoint Test.207 #Derived_gen.206: + let Test.33 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.180; + let Test.35 : I64 = UnionAtIndex (Id 1) (Index 1) Test.180; + let Test.200 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.180; + let Test.36 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.200; + inc Test.36; + let Test.38 : I64 = UnionAtIndex (Id 1) (Index 1) Test.200; + let Test.39 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.200; + inc Test.39; + let Test.37 : I32 = UnionAtIndex (Id 1) (Index 3) Test.200; + let Test.34 : I32 = UnionAtIndex (Id 1) (Index 3) Test.180; + joinpoint #Derived_gen.70 #Derived_gen.208 #Derived_gen.209 #Derived_gen.210: + let Test.192 : Int1 = false; + let Test.189 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.210 UpdateModeId { id: 107 } TagId(1) Test.33 Test.35 Test.36 Test.34 Test.192; + let Test.191 : Int1 = false; + let Test.190 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.209 UpdateModeId { id: 106 } TagId(1) Test.39 Test.18 Test.19 Test.17 Test.191; + let Test.188 : Int1 = true; + let Test.187 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.208 UpdateModeId { id: 105 } TagId(1) Test.189 Test.38 Test.190 Test.37 Test.188; + let #Derived_gen.18 : {} = lowlevel PtrStore #Derived_gen.6 Test.187; + let #Derived_gen.17 : [C *self I64 *self I32 Int1, ] = lowlevel PtrLoad #Derived_gen.7; + ret #Derived_gen.17; + in + let #Derived_gen.71 : Int1 = lowlevel RefCountIsUnique Test.180; + if #Derived_gen.71 then + let #Derived_gen.211 : [C *self I64 *self I32 Int1, ] = Reset { symbol: Test.200, id: UpdateModeId { id: 108 } }; + jump #Derived_gen.70 #Derived_gen.206 #Derived_gen.211 Test.180; + else + inc Test.33; + decref Test.180; + let #Derived_gen.212 : [C *self I64 *self I32 Int1, ] = NullPointer; + jump #Derived_gen.70 #Derived_gen.212 #Derived_gen.212 #Derived_gen.206; + in + let Test.204 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.180; + let Test.205 : U8 = 1i64; + let Test.206 : U8 = GetTagId Test.204; + let Test.209 : Int1 = lowlevel Eq Test.205 Test.206; + if Test.209 then + let Test.201 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.180; + let Test.202 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.201; + let Test.203 : Int1 = true; + let Test.208 : Int1 = lowlevel Eq Test.203 Test.202; + if Test.208 then + jump Test.199 #Derived_gen.118; + else + jump Test.207 #Derived_gen.118; else - jump Test.207 #Derived_gen.77; + jump Test.207 #Derived_gen.118; else - jump Test.207 #Derived_gen.77; + jump Test.225 #Derived_gen.118; else - jump Test.225 #Derived_gen.77; + jump Test.225 #Derived_gen.118; else - jump Test.225 #Derived_gen.77; + decref #Derived_gen.118; + dec Test.19; + let Test.197 : [C *self I64 *self I32 Int1, ] = TagId(0) ; + let #Derived_gen.20 : {} = lowlevel PtrStore #Derived_gen.6 Test.197; + let #Derived_gen.19 : [C *self I64 *self I32 Int1, ] = lowlevel PtrLoad #Derived_gen.7; + ret #Derived_gen.19; else - decref #Derived_gen.77; - dec Test.19; - let Test.197 : [C *self I64 *self I32 Int1, ] = TagId(0) ; - ret Test.197; + jump Test.238 #Derived_gen.118; else - jump Test.238 #Derived_gen.77; + jump Test.238 #Derived_gen.118; else - jump Test.238 #Derived_gen.77; - else - let Test.117 : Int1 = CallByName Num.24 Test.10 Test.17; - if Test.117 then - joinpoint Test.176 #Derived_gen.291: - let Test.171 : [C *self I64 *self I32 Int1, ] = CallByName Test.3 Test.19 Test.10 Test.11; - let Test.170 : Int1 = false; - let Test.169 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.291 UpdateModeId { id: 196 } TagId(1) Test.16 Test.18 Test.171 Test.17 Test.170; - ret Test.169; - in - let Test.174 : U8 = 1i64; - let Test.175 : U8 = GetTagId Test.19; - let Test.178 : Int1 = lowlevel Eq Test.174 Test.175; - if Test.178 then - let Test.172 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.19; - let Test.173 : Int1 = true; - let Test.177 : Int1 = lowlevel Eq Test.173 Test.172; - if Test.177 then - inc Test.19; - let #Derived_gen.292 : [C *self I64 *self I32 Int1, ] = Reset { symbol: Test.16, id: UpdateModeId { id: 197 } }; - let Test.118 : [C *self I64 *self I32 Int1, ] = CallByName Test.3 Test.19 Test.10 Test.11; - joinpoint Test.137 #Derived_gen.332 #Derived_gen.333: - let Test.136 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.118; - let Test.57 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.136; - inc Test.57; - let Test.59 : I64 = UnionAtIndex (Id 1) (Index 1) Test.136; - let Test.60 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.136; - inc Test.60; - let Test.58 : I32 = UnionAtIndex (Id 1) (Index 3) Test.136; - let Test.62 : I64 = UnionAtIndex (Id 1) (Index 1) Test.118; - let Test.63 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.118; - let Test.61 : I32 = UnionAtIndex (Id 1) (Index 3) Test.118; - joinpoint #Derived_gen.70 #Derived_gen.337 #Derived_gen.338 #Derived_gen.339: - let Test.124 : Int1 = false; - let Test.121 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.339 UpdateModeId { id: 242 } TagId(1) Test.57 Test.59 Test.60 Test.58 Test.124; - let Test.123 : Int1 = false; - let Test.122 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.338 UpdateModeId { id: 241 } TagId(1) Test.63 Test.18 Test.19 Test.17 Test.123; - let Test.120 : Int1 = true; - let Test.119 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.337 UpdateModeId { id: 240 } TagId(1) Test.121 Test.62 Test.122 Test.61 Test.120; - ret Test.119; - in - let #Derived_gen.71 : Int1 = lowlevel RefCountIsUnique Test.118; - if #Derived_gen.71 then - decref #Derived_gen.332; - let #Derived_gen.340 : [C *self I64 *self I32 Int1, ] = Reset { symbol: Test.136, id: UpdateModeId { id: 243 } }; - let #Derived_gen.341 : [C *self I64 *self I32 Int1, ] = ResetRef { symbol: Test.118, id: UpdateModeId { id: 244 } }; - jump #Derived_gen.70 #Derived_gen.333 #Derived_gen.340 #Derived_gen.341; - else - inc Test.63; - decref Test.118; - let #Derived_gen.342 : [C *self I64 *self I32 Int1, ] = NullPointer; - jump #Derived_gen.70 #Derived_gen.342 #Derived_gen.332 #Derived_gen.333; - in - let Test.166 : U8 = 1i64; - let Test.167 : U8 = GetTagId Test.118; - let Test.168 : Int1 = lowlevel Eq Test.166 Test.167; - if Test.168 then - joinpoint Test.163 #Derived_gen.354 #Derived_gen.355: - joinpoint Test.154 #Derived_gen.356 #Derived_gen.357: - let Test.83 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.118; - let Test.85 : I64 = UnionAtIndex (Id 1) (Index 1) Test.118; - let Test.86 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.118; - let Test.84 : I32 = UnionAtIndex (Id 1) (Index 3) Test.118; - joinpoint #Derived_gen.60 #Derived_gen.360 #Derived_gen.361: - let Test.134 : Int1 = true; - let Test.133 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.361 UpdateModeId { id: 262 } TagId(1) Test.83 Test.85 Test.86 Test.84 Test.134; - let Test.132 : Int1 = false; - let Test.131 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.360 UpdateModeId { id: 261 } TagId(1) Test.133 Test.18 Test.19 Test.17 Test.132; - ret Test.131; - in - let #Derived_gen.61 : Int1 = lowlevel RefCountIsUnique Test.118; - if #Derived_gen.61 then - decref #Derived_gen.356; - let #Derived_gen.362 : [C *self I64 *self I32 Int1, ] = ResetRef { symbol: Test.118, id: UpdateModeId { id: 263 } }; - jump #Derived_gen.60 #Derived_gen.357 #Derived_gen.362; - else - inc Test.83; - inc Test.86; - decref Test.118; - jump #Derived_gen.60 #Derived_gen.356 #Derived_gen.357; + let Test.117 : Int1 = CallByName Num.24 Test.10 Test.17; + if Test.117 then + joinpoint Test.176 #Derived_gen.288: + let Test.170 : Int1 = false; + let #Derived_gen.21 : [C *self I64 *self I32 Int1, ] = NullPointer; + let Test.169 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.288 UpdateModeId { id: 196 } TagId(1) Test.16 Test.18 #Derived_gen.21 Test.17 Test.170; + let #Derived_gen.22 : Ptr([C *self I64 *self I32 Int1, ]) = UnionFieldPtrAtIndex (Id 1) (Index 2) Test.169; + let #Derived_gen.23 : {} = lowlevel PtrStore #Derived_gen.6 Test.169; + jump #Derived_gen.5 Test.19 Test.10 Test.11 #Derived_gen.22 #Derived_gen.7; + in + let Test.174 : U8 = 1i64; + let Test.175 : U8 = GetTagId Test.19; + let Test.178 : Int1 = lowlevel Eq Test.174 Test.175; + if Test.178 then + let Test.172 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.19; + let Test.173 : Int1 = true; + let Test.177 : Int1 = lowlevel Eq Test.173 Test.172; + if Test.177 then + inc Test.19; + let #Derived_gen.289 : [C *self I64 *self I32 Int1, ] = Reset { symbol: Test.16, id: UpdateModeId { id: 197 } }; + let Test.118 : [C *self I64 *self I32 Int1, ] = CallByName Test.3 Test.19 Test.10 Test.11; + joinpoint Test.137 #Derived_gen.322 #Derived_gen.323: + let Test.136 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.118; + let Test.57 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.136; + inc Test.57; + let Test.59 : I64 = UnionAtIndex (Id 1) (Index 1) Test.136; + let Test.60 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.136; + inc Test.60; + let Test.58 : I32 = UnionAtIndex (Id 1) (Index 3) Test.136; + let Test.62 : I64 = UnionAtIndex (Id 1) (Index 1) Test.118; + let Test.63 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.118; + let Test.61 : I32 = UnionAtIndex (Id 1) (Index 3) Test.118; + joinpoint #Derived_gen.112 #Derived_gen.326 #Derived_gen.327 #Derived_gen.328: + let Test.124 : Int1 = false; + let Test.121 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.328 UpdateModeId { id: 242 } TagId(1) Test.57 Test.59 Test.60 Test.58 Test.124; + let Test.123 : Int1 = false; + let Test.122 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.327 UpdateModeId { id: 241 } TagId(1) Test.63 Test.18 Test.19 Test.17 Test.123; + let Test.120 : Int1 = true; + let Test.119 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.326 UpdateModeId { id: 240 } TagId(1) Test.121 Test.62 Test.122 Test.61 Test.120; + let #Derived_gen.25 : {} = lowlevel PtrStore #Derived_gen.6 Test.119; + let #Derived_gen.24 : [C *self I64 *self I32 Int1, ] = lowlevel PtrLoad #Derived_gen.7; + ret #Derived_gen.24; in - let Test.151 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.118; - let Test.152 : U8 = 1i64; - let Test.153 : U8 = GetTagId Test.151; - let Test.156 : Int1 = lowlevel Eq Test.152 Test.153; - if Test.156 then - let Test.148 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.118; - let Test.149 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.148; - let Test.150 : Int1 = true; - let Test.155 : Int1 = lowlevel Eq Test.150 Test.149; - if Test.155 then - jump Test.137 #Derived_gen.354 #Derived_gen.355; - else - jump Test.154 #Derived_gen.354 #Derived_gen.355; + let #Derived_gen.113 : Int1 = lowlevel RefCountIsUnique Test.118; + if #Derived_gen.113 then + decref #Derived_gen.322; + let #Derived_gen.329 : [C *self I64 *self I32 Int1, ] = Reset { symbol: Test.136, id: UpdateModeId { id: 243 } }; + jump #Derived_gen.112 #Derived_gen.323 #Derived_gen.329 Test.118; else - jump Test.154 #Derived_gen.354 #Derived_gen.355; + inc Test.63; + decref Test.118; + let #Derived_gen.330 : [C *self I64 *self I32 Int1, ] = NullPointer; + jump #Derived_gen.112 #Derived_gen.330 #Derived_gen.322 #Derived_gen.323; in - let Test.160 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.118; - let Test.161 : U8 = 1i64; - let Test.162 : U8 = GetTagId Test.160; - let Test.165 : Int1 = lowlevel Eq Test.161 Test.162; - if Test.165 then - let Test.157 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.118; - let Test.158 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.157; - let Test.159 : Int1 = true; - let Test.164 : Int1 = lowlevel Eq Test.159 Test.158; - if Test.164 then - joinpoint Test.145 #Derived_gen.363 #Derived_gen.364: - let Test.70 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.118; - let Test.72 : I64 = UnionAtIndex (Id 1) (Index 1) Test.118; - let Test.138 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.118; - let Test.73 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.138; - inc Test.73; - let Test.75 : I64 = UnionAtIndex (Id 1) (Index 1) Test.138; - let Test.76 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.138; - inc Test.76; - let Test.74 : I32 = UnionAtIndex (Id 1) (Index 3) Test.138; - let Test.71 : I32 = UnionAtIndex (Id 1) (Index 3) Test.118; - joinpoint #Derived_gen.64 #Derived_gen.368 #Derived_gen.369 #Derived_gen.370: - let Test.130 : Int1 = false; - let Test.127 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.370 UpdateModeId { id: 271 } TagId(1) Test.70 Test.72 Test.73 Test.71 Test.130; - let Test.129 : Int1 = false; - let Test.128 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.369 UpdateModeId { id: 270 } TagId(1) Test.76 Test.18 Test.19 Test.17 Test.129; - let Test.126 : Int1 = true; - let Test.125 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.368 UpdateModeId { id: 269 } TagId(1) Test.127 Test.75 Test.128 Test.74 Test.126; - ret Test.125; + let Test.166 : U8 = 1i64; + let Test.167 : U8 = GetTagId Test.118; + let Test.168 : Int1 = lowlevel Eq Test.166 Test.167; + if Test.168 then + joinpoint Test.163 #Derived_gen.340 #Derived_gen.341: + joinpoint Test.154 #Derived_gen.342 #Derived_gen.343: + let Test.83 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.118; + let Test.85 : I64 = UnionAtIndex (Id 1) (Index 1) Test.118; + let Test.86 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.118; + let Test.84 : I32 = UnionAtIndex (Id 1) (Index 3) Test.118; + joinpoint #Derived_gen.102 #Derived_gen.345 #Derived_gen.346: + let Test.134 : Int1 = true; + let Test.133 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.346 UpdateModeId { id: 262 } TagId(1) Test.83 Test.85 Test.86 Test.84 Test.134; + let Test.132 : Int1 = false; + let Test.131 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.345 UpdateModeId { id: 261 } TagId(1) Test.133 Test.18 Test.19 Test.17 Test.132; + let #Derived_gen.27 : {} = lowlevel PtrStore #Derived_gen.6 Test.131; + let #Derived_gen.26 : [C *self I64 *self I32 Int1, ] = lowlevel PtrLoad #Derived_gen.7; + ret #Derived_gen.26; in - let #Derived_gen.65 : Int1 = lowlevel RefCountIsUnique Test.118; - if #Derived_gen.65 then - decref #Derived_gen.363; - let #Derived_gen.371 : [C *self I64 *self I32 Int1, ] = Reset { symbol: Test.138, id: UpdateModeId { id: 272 } }; - let #Derived_gen.372 : [C *self I64 *self I32 Int1, ] = ResetRef { symbol: Test.118, id: UpdateModeId { id: 273 } }; - jump #Derived_gen.64 #Derived_gen.364 #Derived_gen.371 #Derived_gen.372; + let #Derived_gen.103 : Int1 = lowlevel RefCountIsUnique Test.118; + if #Derived_gen.103 then + decref #Derived_gen.342; + jump #Derived_gen.102 #Derived_gen.343 Test.118; else - inc Test.70; + inc Test.83; + inc Test.86; decref Test.118; - let #Derived_gen.373 : [C *self I64 *self I32 Int1, ] = NullPointer; - jump #Derived_gen.64 #Derived_gen.373 #Derived_gen.363 #Derived_gen.364; + jump #Derived_gen.102 #Derived_gen.342 #Derived_gen.343; in - let Test.142 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.118; - let Test.143 : U8 = 1i64; - let Test.144 : U8 = GetTagId Test.142; - let Test.147 : Int1 = lowlevel Eq Test.143 Test.144; - if Test.147 then - let Test.139 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.118; - let Test.140 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.139; - let Test.141 : Int1 = true; - let Test.146 : Int1 = lowlevel Eq Test.141 Test.140; - if Test.146 then - jump Test.137 #Derived_gen.77 #Derived_gen.292; + let Test.151 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.118; + let Test.152 : U8 = 1i64; + let Test.153 : U8 = GetTagId Test.151; + let Test.156 : Int1 = lowlevel Eq Test.152 Test.153; + if Test.156 then + let Test.148 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.118; + let Test.149 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.148; + let Test.150 : Int1 = true; + let Test.155 : Int1 = lowlevel Eq Test.150 Test.149; + if Test.155 then + jump Test.137 #Derived_gen.340 #Derived_gen.341; + else + jump Test.154 #Derived_gen.340 #Derived_gen.341; + else + jump Test.154 #Derived_gen.340 #Derived_gen.341; + in + let Test.160 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.118; + let Test.161 : U8 = 1i64; + let Test.162 : U8 = GetTagId Test.160; + let Test.165 : Int1 = lowlevel Eq Test.161 Test.162; + if Test.165 then + let Test.157 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.118; + let Test.158 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.157; + let Test.159 : Int1 = true; + let Test.164 : Int1 = lowlevel Eq Test.159 Test.158; + if Test.164 then + joinpoint Test.145 #Derived_gen.347 #Derived_gen.348: + let Test.70 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.118; + let Test.72 : I64 = UnionAtIndex (Id 1) (Index 1) Test.118; + let Test.138 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.118; + let Test.73 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.138; + inc Test.73; + let Test.75 : I64 = UnionAtIndex (Id 1) (Index 1) Test.138; + let Test.76 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.138; + inc Test.76; + let Test.74 : I32 = UnionAtIndex (Id 1) (Index 3) Test.138; + let Test.71 : I32 = UnionAtIndex (Id 1) (Index 3) Test.118; + joinpoint #Derived_gen.106 #Derived_gen.351 #Derived_gen.352 #Derived_gen.353: + let Test.130 : Int1 = false; + let Test.127 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.353 UpdateModeId { id: 271 } TagId(1) Test.70 Test.72 Test.73 Test.71 Test.130; + let Test.129 : Int1 = false; + let Test.128 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.352 UpdateModeId { id: 270 } TagId(1) Test.76 Test.18 Test.19 Test.17 Test.129; + let Test.126 : Int1 = true; + let Test.125 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.351 UpdateModeId { id: 269 } TagId(1) Test.127 Test.75 Test.128 Test.74 Test.126; + let #Derived_gen.29 : {} = lowlevel PtrStore #Derived_gen.6 Test.125; + let #Derived_gen.28 : [C *self I64 *self I32 Int1, ] = lowlevel PtrLoad #Derived_gen.7; + ret #Derived_gen.28; + in + let #Derived_gen.107 : Int1 = lowlevel RefCountIsUnique Test.118; + if #Derived_gen.107 then + decref #Derived_gen.347; + let #Derived_gen.354 : [C *self I64 *self I32 Int1, ] = Reset { symbol: Test.138, id: UpdateModeId { id: 272 } }; + jump #Derived_gen.106 #Derived_gen.348 #Derived_gen.354 Test.118; + else + inc Test.70; + decref Test.118; + let #Derived_gen.355 : [C *self I64 *self I32 Int1, ] = NullPointer; + jump #Derived_gen.106 #Derived_gen.355 #Derived_gen.347 #Derived_gen.348; + in + let Test.142 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.118; + let Test.143 : U8 = 1i64; + let Test.144 : U8 = GetTagId Test.142; + let Test.147 : Int1 = lowlevel Eq Test.143 Test.144; + if Test.147 then + let Test.139 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.118; + let Test.140 : Int1 = UnionAtIndex (Id 1) (Index 4) Test.139; + let Test.141 : Int1 = true; + let Test.146 : Int1 = lowlevel Eq Test.141 Test.140; + if Test.146 then + jump Test.137 #Derived_gen.118 #Derived_gen.289; + else + jump Test.145 #Derived_gen.118 #Derived_gen.289; else - jump Test.145 #Derived_gen.77 #Derived_gen.292; + jump Test.145 #Derived_gen.118 #Derived_gen.289; else - jump Test.145 #Derived_gen.77 #Derived_gen.292; + jump Test.163 #Derived_gen.118 #Derived_gen.289; else - jump Test.163 #Derived_gen.77 #Derived_gen.292; + jump Test.163 #Derived_gen.118 #Derived_gen.289; else - jump Test.163 #Derived_gen.77 #Derived_gen.292; + decref #Derived_gen.289; + decref #Derived_gen.118; + joinpoint #Derived_gen.108: + let Test.135 : [C *self I64 *self I32 Int1, ] = TagId(0) ; + let #Derived_gen.31 : {} = lowlevel PtrStore #Derived_gen.6 Test.135; + let #Derived_gen.30 : [C *self I64 *self I32 Int1, ] = lowlevel PtrLoad #Derived_gen.7; + ret #Derived_gen.30; + in + let #Derived_gen.111 : Int1 = lowlevel RefCountIsUnique Test.19; + if #Derived_gen.111 then + let #Derived_gen.110 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.19; + dec #Derived_gen.110; + let #Derived_gen.109 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.19; + dec #Derived_gen.109; + free Test.19; + jump #Derived_gen.108; + else + decref Test.19; + jump #Derived_gen.108; else - decref #Derived_gen.292; - decref #Derived_gen.77; - joinpoint #Derived_gen.66: - let Test.135 : [C *self I64 *self I32 Int1, ] = TagId(0) ; - ret Test.135; - in - let #Derived_gen.69 : Int1 = lowlevel RefCountIsUnique Test.19; - if #Derived_gen.69 then - let #Derived_gen.68 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.19; - dec #Derived_gen.68; - let #Derived_gen.67 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.19; - dec #Derived_gen.67; - decref Test.19; - jump #Derived_gen.66; - else - decref Test.19; - jump #Derived_gen.66; + jump Test.176 #Derived_gen.118; else - jump Test.176 #Derived_gen.77; + jump Test.176 #Derived_gen.118; else - jump Test.176 #Derived_gen.77; - else - let Test.116 : Int1 = false; - let Test.115 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.77 UpdateModeId { id: 1 } TagId(1) Test.16 Test.11 Test.19 Test.10 Test.116; - ret Test.115; - in - let #Derived_gen.73 : Int1 = lowlevel RefCountIsUnique Test.9; - if #Derived_gen.73 then - let #Derived_gen.382 : [C *self I64 *self I32 Int1, ] = ResetRef { symbol: Test.9, id: UpdateModeId { id: 282 } }; - jump #Derived_gen.72 #Derived_gen.382; - else - inc Test.16; - inc Test.19; - decref Test.9; - let #Derived_gen.383 : [C *self I64 *self I32 Int1, ] = NullPointer; - jump #Derived_gen.72 #Derived_gen.383; - else - let Test.96 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.9; - let Test.98 : I64 = UnionAtIndex (Id 1) (Index 1) Test.9; - let Test.99 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.9; - let Test.97 : I32 = UnionAtIndex (Id 1) (Index 3) Test.9; - joinpoint #Derived_gen.74 #Derived_gen.385: - let Test.247 : Int1 = CallByName Num.22 Test.10 Test.97; - if Test.247 then - let Test.250 : [C *self I64 *self I32 Int1, ] = CallByName Test.3 Test.96 Test.10 Test.11; - let Test.249 : Int1 = true; - let Test.248 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.385 UpdateModeId { id: 284 } TagId(1) Test.250 Test.98 Test.99 Test.97 Test.249; - ret Test.248; + let Test.116 : Int1 = false; + let Test.115 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.118 UpdateModeId { id: 1 } TagId(1) Test.16 Test.11 Test.19 Test.10 Test.116; + let #Derived_gen.33 : {} = lowlevel PtrStore #Derived_gen.6 Test.115; + let #Derived_gen.32 : [C *self I64 *self I32 Int1, ] = lowlevel PtrLoad #Derived_gen.7; + ret #Derived_gen.32; + in + let #Derived_gen.115 : Int1 = lowlevel RefCountIsUnique Test.9; + if #Derived_gen.115 then + jump #Derived_gen.114 Test.9; else - let Test.243 : Int1 = CallByName Num.24 Test.10 Test.97; - if Test.243 then - let Test.246 : [C *self I64 *self I32 Int1, ] = CallByName Test.3 Test.99 Test.10 Test.11; - let Test.245 : Int1 = true; - let Test.244 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.385 UpdateModeId { id: 284 } TagId(1) Test.96 Test.98 Test.246 Test.97 Test.245; - ret Test.244; - else - let Test.242 : Int1 = true; - let Test.241 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.385 UpdateModeId { id: 284 } TagId(1) Test.96 Test.11 Test.99 Test.10 Test.242; - ret Test.241; - in - let #Derived_gen.75 : Int1 = lowlevel RefCountIsUnique Test.9; - if #Derived_gen.75 then - let #Derived_gen.386 : [C *self I64 *self I32 Int1, ] = ResetRef { symbol: Test.9, id: UpdateModeId { id: 285 } }; - jump #Derived_gen.74 #Derived_gen.386; + inc Test.16; + inc Test.19; + decref Test.9; + let #Derived_gen.363 : [C *self I64 *self I32 Int1, ] = NullPointer; + jump #Derived_gen.114 #Derived_gen.363; else - inc Test.96; - inc Test.99; - decref Test.9; - let #Derived_gen.387 : [C *self I64 *self I32 Int1, ] = NullPointer; - jump #Derived_gen.74 #Derived_gen.387; + let Test.96 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 0) Test.9; + let Test.98 : I64 = UnionAtIndex (Id 1) (Index 1) Test.9; + let Test.99 : [C *self I64 *self I32 Int1, ] = UnionAtIndex (Id 1) (Index 2) Test.9; + let Test.97 : I32 = UnionAtIndex (Id 1) (Index 3) Test.9; + joinpoint #Derived_gen.116 #Derived_gen.364: + let Test.247 : Int1 = CallByName Num.22 Test.10 Test.97; + if Test.247 then + let Test.249 : Int1 = true; + let #Derived_gen.34 : [C *self I64 *self I32 Int1, ] = NullPointer; + let Test.248 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.364 UpdateModeId { id: 284 } TagId(1) #Derived_gen.34 Test.98 Test.99 Test.97 Test.249; + let #Derived_gen.35 : Ptr([C *self I64 *self I32 Int1, ]) = UnionFieldPtrAtIndex (Id 1) (Index 0) Test.248; + let #Derived_gen.36 : {} = lowlevel PtrStore #Derived_gen.6 Test.248; + jump #Derived_gen.5 Test.96 Test.10 Test.11 #Derived_gen.35 #Derived_gen.7; + else + let Test.243 : Int1 = CallByName Num.24 Test.10 Test.97; + if Test.243 then + let Test.245 : Int1 = true; + let #Derived_gen.37 : [C *self I64 *self I32 Int1, ] = NullPointer; + let Test.244 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.364 UpdateModeId { id: 284 } TagId(1) Test.96 Test.98 #Derived_gen.37 Test.97 Test.245; + let #Derived_gen.38 : Ptr([C *self I64 *self I32 Int1, ]) = UnionFieldPtrAtIndex (Id 1) (Index 2) Test.244; + let #Derived_gen.39 : {} = lowlevel PtrStore #Derived_gen.6 Test.244; + jump #Derived_gen.5 Test.99 Test.10 Test.11 #Derived_gen.38 #Derived_gen.7; + else + let Test.242 : Int1 = true; + let Test.241 : [C *self I64 *self I32 Int1, ] = Reuse #Derived_gen.364 UpdateModeId { id: 284 } TagId(1) Test.96 Test.11 Test.99 Test.10 Test.242; + let #Derived_gen.41 : {} = lowlevel PtrStore #Derived_gen.6 Test.241; + let #Derived_gen.40 : [C *self I64 *self I32 Int1, ] = lowlevel PtrLoad #Derived_gen.7; + ret #Derived_gen.40; + in + let #Derived_gen.117 : Int1 = lowlevel RefCountIsUnique Test.9; + if #Derived_gen.117 then + jump #Derived_gen.116 Test.9; + else + inc Test.96; + inc Test.99; + decref Test.9; + let #Derived_gen.365 : [C *self I64 *self I32 Int1, ] = NullPointer; + jump #Derived_gen.116 #Derived_gen.365; + in + jump #Derived_gen.5 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.3; procedure Test.0 (): let Test.281 : [C *self I64 *self I32 Int1, ] = TagId(0) ; diff --git a/crates/compiler/test_mono/generated/recursive_call_capturing_function.txt b/crates/compiler/test_mono/generated/recursive_call_capturing_function.txt index 333be0ae754..44abe4f94b7 100644 --- a/crates/compiler/test_mono/generated/recursive_call_capturing_function.txt +++ b/crates/compiler/test_mono/generated/recursive_call_capturing_function.txt @@ -11,7 +11,7 @@ procedure Test.1 (Test.2): let Test.7 : U32 = CallByName Test.3 Test.8 Test.2; ret Test.7; -procedure Test.3 (Test.17, Test.18): +procedure Test.3 (#Derived_gen.0, #Derived_gen.1): joinpoint Test.9 Test.4 Test.2: let Test.13 : Int1 = CallByName Bool.2; if Test.13 then @@ -20,7 +20,7 @@ procedure Test.3 (Test.17, Test.18): let Test.11 : U32 = CallByName Num.19 Test.4 Test.2; jump Test.9 Test.11 Test.2; in - jump Test.9 Test.17 Test.18; + jump Test.9 #Derived_gen.0 #Derived_gen.1; procedure Test.0 (): let Test.6 : U32 = 6i64; diff --git a/crates/compiler/test_mono/generated/recursive_function_and_union_with_inference_hole.txt b/crates/compiler/test_mono/generated/recursive_function_and_union_with_inference_hole.txt index 341384e835f..32b22743616 100644 --- a/crates/compiler/test_mono/generated/recursive_function_and_union_with_inference_hole.txt +++ b/crates/compiler/test_mono/generated/recursive_function_and_union_with_inference_hole.txt @@ -6,10 +6,10 @@ procedure List.5 (#Attr.2, #Attr.3): procedure Test.2 (Test.5): let Test.6 : List [C List *self] = UnionAtIndex (Id 0) (Index 0) Test.5; inc Test.6; - let #Derived_gen.0 : [C List *self] = Reset { symbol: Test.5, id: UpdateModeId { id: 0 } }; + let #Derived_gen.1 : [C List *self] = Reset { symbol: Test.5, id: UpdateModeId { id: 0 } }; let Test.15 : {} = Struct {}; let Test.7 : List [C List *self] = CallByName List.5 Test.6 Test.15; - let Test.14 : [C List *self] = Reuse #Derived_gen.0 UpdateModeId { id: 0 } TagId(0) Test.7; + let Test.14 : [C List *self] = Reuse #Derived_gen.1 UpdateModeId { id: 0 } TagId(0) Test.7; ret Test.14; procedure Test.0 (): diff --git a/crates/compiler/test_mono/generated/recursive_lambda_set_has_nested_non_recursive_lambda_sets_issue_5026.txt b/crates/compiler/test_mono/generated/recursive_lambda_set_has_nested_non_recursive_lambda_sets_issue_5026.txt index 858df4d18a7..c6a00595d7d 100644 --- a/crates/compiler/test_mono/generated/recursive_lambda_set_has_nested_non_recursive_lambda_sets_issue_5026.txt +++ b/crates/compiler/test_mono/generated/recursive_lambda_set_has_nested_non_recursive_lambda_sets_issue_5026.txt @@ -26,7 +26,7 @@ procedure Test.3 (Test.7): procedure Test.6 (Test.16, #Attr.12): let Test.5 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12; - joinpoint #Derived_gen.0: + joinpoint #Derived_gen.2: let Test.19 : {} = Struct {}; let Test.22 : Str = "foobar"; let Test.20 : [, C {}] = CallByName Test.8 Test.22 Test.5; @@ -42,13 +42,13 @@ procedure Test.6 (Test.16, #Attr.12): ret Test.18; in - let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique #Attr.12; - if #Derived_gen.1 then - decref #Attr.12; - jump #Derived_gen.0; + let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique #Attr.12; + if #Derived_gen.3 then + free #Attr.12; + jump #Derived_gen.2; else decref #Attr.12; - jump #Derived_gen.0; + jump #Derived_gen.2; procedure Test.8 (Test.9, Test.7): let Test.24 : [, C {}] = CallByName Test.10 Test.9; diff --git a/crates/compiler/test_mono/generated/recursive_lambda_set_resolved_only_upon_specialization.txt b/crates/compiler/test_mono/generated/recursive_lambda_set_resolved_only_upon_specialization.txt index e46d5fb2253..a6ad8acc154 100644 --- a/crates/compiler/test_mono/generated/recursive_lambda_set_resolved_only_upon_specialization.txt +++ b/crates/compiler/test_mono/generated/recursive_lambda_set_resolved_only_upon_specialization.txt @@ -10,7 +10,7 @@ procedure Num.21 (#Attr.2, #Attr.3): let Num.292 : U8 = lowlevel NumMul #Attr.2 #Attr.3; ret Num.292; -procedure Test.1 (Test.26, Test.27): +procedure Test.1 (#Derived_gen.0, #Derived_gen.1): joinpoint Test.11 Test.2 Test.3: let Test.24 : U8 = 0i64; let Test.20 : Int1 = CallByName Bool.11 Test.2 Test.24; @@ -33,13 +33,13 @@ procedure Test.1 (Test.26, Test.27): let Test.14 : [, C *self U8] = TagId(0) Test.3 Test.2; jump Test.11 Test.13 Test.14; in - jump Test.11 Test.26 Test.27; + jump Test.11 #Derived_gen.0 #Derived_gen.1; -procedure Test.4 (Test.28, Test.29): +procedure Test.4 (#Derived_gen.2, #Derived_gen.3): joinpoint Test.15 Test.5 #Attr.12: let Test.2 : U8 = UnionAtIndex (Id 0) (Index 1) #Attr.12; let Test.3 : [, C *self U8] = UnionAtIndex (Id 0) (Index 0) #Attr.12; - joinpoint #Derived_gen.0: + joinpoint #Derived_gen.4: let Test.17 : U8 = CallByName Num.21 Test.2 Test.5; let Test.18 : U8 = GetTagId Test.3; switch Test.18: @@ -52,16 +52,16 @@ procedure Test.4 (Test.28, Test.29): ret Test.16; in - let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique #Attr.12; - if #Derived_gen.1 then - decref #Attr.12; - jump #Derived_gen.0; + let #Derived_gen.5 : Int1 = lowlevel RefCountIsUnique #Attr.12; + if #Derived_gen.5 then + free #Attr.12; + jump #Derived_gen.4; else inc Test.3; decref #Attr.12; - jump #Derived_gen.0; + jump #Derived_gen.4; in - jump Test.15 Test.28 Test.29; + jump Test.15 #Derived_gen.2 #Derived_gen.3; procedure Test.6 (Test.7): ret Test.7; diff --git a/crates/compiler/test_mono/generated/recursively_build_effect.txt b/crates/compiler/test_mono/generated/recursively_build_effect.txt index 5e4853903c6..d1f4bd9809b 100644 --- a/crates/compiler/test_mono/generated/recursively_build_effect.txt +++ b/crates/compiler/test_mono/generated/recursively_build_effect.txt @@ -8,9 +8,9 @@ procedure Str.3 (#Attr.2, #Attr.3): procedure Test.11 (Test.29, #Attr.12): let Test.10 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12; - let #Derived_gen.0 : Int1 = lowlevel RefCountIsUnique #Attr.12; - if #Derived_gen.0 then - decref #Attr.12; + let #Derived_gen.9 : Int1 = lowlevel RefCountIsUnique #Attr.12; + if #Derived_gen.9 then + free #Attr.12; ret Test.10; else decref #Attr.12; @@ -19,11 +19,11 @@ procedure Test.11 (Test.29, #Attr.12): procedure Test.11 (Test.29, Test.10): ret Test.10; -procedure Test.14 (Test.62, Test.63): +procedure Test.14 (#Derived_gen.2, #Derived_gen.3): joinpoint Test.37 Test.36 #Attr.12: let Test.12 : {} = UnionAtIndex (Id 1) (Index 1) #Attr.12; let Test.13 : I64 = UnionAtIndex (Id 1) (Index 0) #Attr.12; - joinpoint #Derived_gen.1: + joinpoint #Derived_gen.10: let Test.43 : {} = Struct {}; let Test.42 : {} = CallByName Test.11 Test.43 Test.12; let Test.38 : [C {}, C I64 {}] = CallByName Test.9 Test.42 Test.13; @@ -38,15 +38,15 @@ procedure Test.14 (Test.62, Test.63): jump Test.37 Test.40 Test.38; in - let #Derived_gen.2 : Int1 = lowlevel RefCountIsUnique #Attr.12; - if #Derived_gen.2 then - decref #Attr.12; - jump #Derived_gen.1; + let #Derived_gen.11 : Int1 = lowlevel RefCountIsUnique #Attr.12; + if #Derived_gen.11 then + free #Attr.12; + jump #Derived_gen.10; else decref #Attr.12; - jump #Derived_gen.1; + jump #Derived_gen.10; in - jump Test.37 Test.62 Test.63; + jump Test.37 #Derived_gen.2 #Derived_gen.3; procedure Test.2 (): let Test.6 : Str = "Hello"; diff --git a/crates/compiler/test_mono/generated/specialize_after_match.txt b/crates/compiler/test_mono/generated/specialize_after_match.txt index d64d3bbf985..dfb63f7e7da 100644 --- a/crates/compiler/test_mono/generated/specialize_after_match.txt +++ b/crates/compiler/test_mono/generated/specialize_after_match.txt @@ -23,7 +23,7 @@ procedure Test.2 (Test.9, Test.10): let Test.29 : U64 = CallByName Test.3 Test.9; ret Test.29; else - joinpoint #Derived_gen.3: + joinpoint #Derived_gen.4: let Test.13 : Str = UnionAtIndex (Id 0) (Index 0) Test.10; let Test.14 : [, C Str *self] = UnionAtIndex (Id 0) (Index 1) Test.10; let Test.33 : U64 = CallByName Test.3 Test.12; @@ -36,15 +36,15 @@ procedure Test.2 (Test.9, Test.10): else ret Test.16; in - let #Derived_gen.4 : Int1 = lowlevel RefCountIsUnique Test.9; - if #Derived_gen.4 then + let #Derived_gen.5 : Int1 = lowlevel RefCountIsUnique Test.9; + if #Derived_gen.5 then dec Test.11; - decref Test.9; - jump #Derived_gen.3; + free Test.9; + jump #Derived_gen.4; else inc Test.12; decref Test.9; - jump #Derived_gen.3; + jump #Derived_gen.4; procedure Test.3 (Test.17): let Test.26 : U8 = 1i64; @@ -55,22 +55,22 @@ procedure Test.3 (Test.17): ret Test.22; else let Test.18 : [, C Str *self] = UnionAtIndex (Id 0) (Index 1) Test.17; - joinpoint #Derived_gen.0: + joinpoint #Derived_gen.1: let Test.24 : U64 = 1i64; let Test.25 : U64 = CallByName Test.3 Test.18; let Test.23 : U64 = CallByName Num.19 Test.24 Test.25; ret Test.23; in - let #Derived_gen.2 : Int1 = lowlevel RefCountIsUnique Test.17; - if #Derived_gen.2 then - let #Derived_gen.1 : Str = UnionAtIndex (Id 0) (Index 0) Test.17; - dec #Derived_gen.1; - decref Test.17; - jump #Derived_gen.0; + let #Derived_gen.3 : Int1 = lowlevel RefCountIsUnique Test.17; + if #Derived_gen.3 then + let #Derived_gen.2 : Str = UnionAtIndex (Id 0) (Index 0) Test.17; + dec #Derived_gen.2; + free Test.17; + jump #Derived_gen.1; else inc Test.18; decref Test.17; - jump #Derived_gen.0; + jump #Derived_gen.1; procedure Test.0 (): let Test.5 : [, C Str *self] = TagId(1) ; diff --git a/crates/compiler/test_mono/generated/tail_call_elimination.txt b/crates/compiler/test_mono/generated/tail_call_elimination.txt index ede33bf2bcf..798de03e174 100644 --- a/crates/compiler/test_mono/generated/tail_call_elimination.txt +++ b/crates/compiler/test_mono/generated/tail_call_elimination.txt @@ -6,7 +6,7 @@ procedure Num.20 (#Attr.2, #Attr.3): let Num.293 : I64 = lowlevel NumSub #Attr.2 #Attr.3; ret Num.293; -procedure Test.1 (Test.15, Test.16): +procedure Test.1 (#Derived_gen.0, #Derived_gen.1): joinpoint Test.7 Test.2 Test.3: let Test.13 : I64 = 0i64; let Test.14 : Int1 = lowlevel Eq Test.13 Test.2; @@ -18,7 +18,7 @@ procedure Test.1 (Test.15, Test.16): let Test.11 : I64 = CallByName Num.19 Test.2 Test.3; jump Test.7 Test.10 Test.11; in - jump Test.7 Test.15 Test.16; + jump Test.7 #Derived_gen.0 #Derived_gen.1; procedure Test.0 (): let Test.5 : I64 = 1000000i64; diff --git a/crates/compiler/test_mono/generated/tail_call_with_different_layout.txt b/crates/compiler/test_mono/generated/tail_call_with_different_layout.txt index f1a49ed0713..07005ec6d69 100644 --- a/crates/compiler/test_mono/generated/tail_call_with_different_layout.txt +++ b/crates/compiler/test_mono/generated/tail_call_with_different_layout.txt @@ -7,13 +7,13 @@ procedure Test.1 (Test.2, Test.3): ret Test.21; procedure Test.30 (Test.31): - let Test.32 : {U8, {}} = Unbox Test.31; + let Test.32 : {U8, {}} = UnionAtIndex (Id 0) (Index 0) Test.31; dec Test.31; let Test.33 : U8 = StructAtIndex 0 Test.32; ret Test.33; procedure Test.34 (Test.35): - let Test.36 : {U8, {}} = Unbox Test.35; + let Test.36 : {U8, {}} = UnionAtIndex (Id 0) (Index 0) Test.35; dec Test.35; let Test.37 : {} = StructAtIndex 1 Test.36; ret Test.37; diff --git a/crates/compiler/test_mono/generated/tail_call_with_same_layout_different_lambda_sets.txt b/crates/compiler/test_mono/generated/tail_call_with_same_layout_different_lambda_sets.txt index af7ef46c476..1c87417bc35 100644 --- a/crates/compiler/test_mono/generated/tail_call_with_same_layout_different_lambda_sets.txt +++ b/crates/compiler/test_mono/generated/tail_call_with_same_layout_different_lambda_sets.txt @@ -7,13 +7,13 @@ procedure Test.1 (Test.2, Test.3): ret Test.21; procedure Test.30 (Test.31): - let Test.32 : {U8, {}} = Unbox Test.31; + let Test.32 : {U8, {}} = UnionAtIndex (Id 0) (Index 0) Test.31; dec Test.31; let Test.33 : U8 = StructAtIndex 0 Test.32; ret Test.33; procedure Test.34 (Test.35): - let Test.36 : {U8, {}} = Unbox Test.35; + let Test.36 : {U8, {}} = UnionAtIndex (Id 0) (Index 0) Test.35; dec Test.35; let Test.37 : {} = StructAtIndex 1 Test.36; ret Test.37; diff --git a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt index 14474338b6e..1bc8990f43f 100644 --- a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt +++ b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt @@ -20,12 +20,12 @@ procedure Encode.24 (Encode.99, Encode.107, Encode.101): ret Encode.111; procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.118 : List U8 = CallByName TotallyNotJson.264 Encode.99 Encode.101 Encode.107; - ret Encode.118; + let Encode.113 : List U8 = CallByName TotallyNotJson.264 Encode.99 Encode.101 Encode.107; + ret Encode.113; procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.123 : List U8 = CallByName TotallyNotJson.182 Encode.99 Encode.101 Encode.107; - ret Encode.123; + let Encode.118 : List U8 = CallByName TotallyNotJson.182 Encode.99 Encode.101 Encode.107; + ret Encode.118; procedure Encode.26 (Encode.105, Encode.106): let Encode.109 : List U8 = Array []; @@ -136,23 +136,7 @@ procedure List.8 (#Attr.2, #Attr.3): let List.598 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; ret List.598; -procedure List.80 (List.647, List.648, List.649, List.650, List.651): - joinpoint List.553 List.439 List.440 List.441 List.442 List.443: - let List.555 : Int1 = CallByName Num.22 List.442 List.443; - if List.555 then - let List.562 : Str = CallByName List.66 List.439 List.442; - inc List.562; - let List.556 : {List U8, U64} = CallByName List.145 List.440 List.562 List.441; - let List.559 : U64 = 1i64; - let List.558 : U64 = CallByName Num.19 List.442 List.559; - jump List.553 List.439 List.556 List.441 List.558 List.443; - else - dec List.439; - ret List.440; - in - jump List.553 List.647 List.648 List.649 List.650 List.651; - -procedure List.80 (List.664, List.665, List.666, List.667, List.668): +procedure List.80 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15): joinpoint List.573 List.439 List.440 List.441 List.442 List.443: let List.575 : Int1 = CallByName Num.22 List.442 List.443; if List.575 then @@ -165,9 +149,9 @@ procedure List.80 (List.664, List.665, List.666, List.667, List.668): dec List.439; ret List.440; in - jump List.573 List.664 List.665 List.666 List.667 List.668; + jump List.573 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15; -procedure List.80 (List.691, List.692, List.693, List.694, List.695): +procedure List.80 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_gen.19, #Derived_gen.20): joinpoint List.626 List.439 List.440 List.441 List.442 List.443: let List.628 : Int1 = CallByName Num.22 List.442 List.443; if List.628 then @@ -191,7 +175,23 @@ procedure List.80 (List.691, List.692, List.693, List.694, List.695): let List.627 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440; ret List.627; in - jump List.626 List.691 List.692 List.693 List.694 List.695; + jump List.626 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20; + +procedure List.80 (#Derived_gen.6, #Derived_gen.7, #Derived_gen.8, #Derived_gen.9, #Derived_gen.10): + joinpoint List.553 List.439 List.440 List.441 List.442 List.443: + let List.555 : Int1 = CallByName Num.22 List.442 List.443; + if List.555 then + let List.562 : Str = CallByName List.66 List.439 List.442; + inc List.562; + let List.556 : {List U8, U64} = CallByName List.145 List.440 List.562 List.441; + let List.559 : U64 = 1i64; + let List.558 : U64 = CallByName Num.19 List.442 List.559; + jump List.553 List.439 List.556 List.441 List.558 List.443; + else + dec List.439; + ret List.440; + in + jump List.553 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10; procedure List.93 (List.436, List.437, List.438): let List.551 : U64 = 0i64; @@ -262,8 +262,8 @@ procedure Test.5 (Test.6, Test.7, Test.4): if Test.25 then let Test.26 : Str = "A"; let Test.29 : Str = StructAtIndex 0 Test.4; - let #Derived_gen.0 : Str = StructAtIndex 1 Test.4; - dec #Derived_gen.0; + let #Derived_gen.24 : Str = StructAtIndex 1 Test.4; + dec #Derived_gen.24; let Test.28 : Str = CallByName TotallyNotJson.25 Test.29; let Test.27 : List Str = Array [Test.28]; let Test.19 : {Str, List Str} = CallByName TotallyNotJson.32 Test.26 Test.27; @@ -271,8 +271,8 @@ procedure Test.5 (Test.6, Test.7, Test.4): else let Test.21 : Str = "B"; let Test.24 : Str = StructAtIndex 1 Test.4; - let #Derived_gen.1 : Str = StructAtIndex 0 Test.4; - dec #Derived_gen.1; + let #Derived_gen.25 : Str = StructAtIndex 0 Test.4; + dec #Derived_gen.25; let Test.23 : Str = CallByName TotallyNotJson.25 Test.24; let Test.22 : List Str = Array [Test.23]; let Test.19 : {Str, List Str} = CallByName TotallyNotJson.32 Test.21 Test.22; diff --git a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification.txt b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification.txt index a71c0661a45..f81285c492c 100644 --- a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification.txt +++ b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification.txt @@ -59,8 +59,8 @@ procedure Test.43 (Test.44, Test.42): jump Test.62 Test.61; else let Test.69 : U8 = StructAtIndex 1 Test.42; - let #Derived_gen.0 : Str = StructAtIndex 0 Test.42; - dec #Derived_gen.0; + let #Derived_gen.5 : Str = StructAtIndex 0 Test.42; + dec #Derived_gen.5; let Test.63 : Int1 = CallByName Test.15 Test.69; let Test.61 : Int1 = CallByName Test.14 Test.63; jump Test.62 Test.61; diff --git a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt index 8b07ca09815..f61f2ffa113 100644 --- a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt +++ b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt @@ -48,32 +48,32 @@ procedure Encode.23 (Encode.98): procedure Encode.24 (Encode.99, Encode.107, Encode.101): dec Encode.99; - let Encode.138 : Str = "a Lambda Set is empty. Most likely there is a type error in your program."; - Crash Encode.138 + let Encode.125 : Str = "a Lambda Set is empty. Most likely there is a type error in your program."; + Crash Encode.125 procedure Encode.24 (Encode.99, Encode.107, Encode.101): let Encode.111 : List U8 = CallByName Test.5 Encode.99 Encode.101 Encode.107; ret Encode.111; procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.118 : List U8 = CallByName TotallyNotJson.264 Encode.99 Encode.101 Encode.107; - ret Encode.118; + let Encode.113 : List U8 = CallByName TotallyNotJson.264 Encode.99 Encode.101 Encode.107; + ret Encode.113; procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.122 : U8 = GetTagId Encode.107; - switch Encode.122: + let Encode.117 : U8 = GetTagId Encode.107; + switch Encode.117: case 0: - let Encode.121 : List U8 = CallByName #Derived.2 Encode.99 Encode.101 Encode.107; - ret Encode.121; + let Encode.116 : List U8 = CallByName #Derived.2 Encode.99 Encode.101 Encode.107; + ret Encode.116; default: - let Encode.121 : List U8 = CallByName #Derived.7 Encode.99 Encode.101 Encode.107; - ret Encode.121; + let Encode.116 : List U8 = CallByName #Derived.7 Encode.99 Encode.101 Encode.107; + ret Encode.116; procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.134 : List U8 = CallByName TotallyNotJson.264 Encode.99 Encode.101 Encode.107; - ret Encode.134; + let Encode.121 : List U8 = CallByName TotallyNotJson.264 Encode.99 Encode.101 Encode.107; + ret Encode.121; procedure Encode.26 (Encode.105, Encode.106): let Encode.109 : List U8 = Array []; @@ -86,52 +86,67 @@ procedure List.145 (List.146, List.147, List.144): ret List.566; procedure List.145 (List.146, List.147, List.144): - let List.639 : {List U8, U64} = CallByName TotallyNotJson.267 List.146 List.147 List.144; - ret List.639; + let List.614 : {List U8, U64} = CallByName TotallyNotJson.267 List.146 List.147 List.144; + ret List.614; procedure List.18 (List.142, List.143, List.144): let List.547 : {List U8, U64} = CallByName List.93 List.142 List.143 List.144; ret List.547; procedure List.18 (List.142, List.143, List.144): - let List.620 : {List U8, U64} = CallByName List.93 List.142 List.143 List.144; - ret List.620; + let List.595 : {List U8, U64} = CallByName List.93 List.142 List.143 List.144; + ret List.595; procedure List.4 (List.113, List.114): - let List.619 : U64 = 1i64; - let List.618 : List U8 = CallByName List.70 List.113 List.619; - let List.617 : List U8 = CallByName List.71 List.618 List.114; - ret List.617; + let List.594 : U64 = 1i64; + let List.593 : List U8 = CallByName List.70 List.113 List.594; + let List.592 : List U8 = CallByName List.71 List.593 List.114; + ret List.592; procedure List.6 (#Attr.2): let List.567 : U64 = lowlevel ListLen #Attr.2; ret List.567; procedure List.6 (#Attr.2): - let List.640 : U64 = lowlevel ListLen #Attr.2; - ret List.640; + let List.615 : U64 = lowlevel ListLen #Attr.2; + ret List.615; procedure List.66 (#Attr.2, #Attr.3): let List.563 : [C {}, C {}] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; ret List.563; procedure List.66 (#Attr.2, #Attr.3): - let List.636 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.636; + let List.611 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.611; procedure List.70 (#Attr.2, #Attr.3): - let List.598 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.598; + let List.573 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.573; procedure List.71 (#Attr.2, #Attr.3): - let List.596 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.596; + let List.571 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.571; procedure List.8 (#Attr.2, #Attr.3): - let List.641 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.641; + let List.616 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.616; + +procedure List.80 (#Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24, #Derived_gen.25): + joinpoint List.601 List.439 List.440 List.441 List.442 List.443: + let List.603 : Int1 = CallByName Num.22 List.442 List.443; + if List.603 then + let List.610 : [] = CallByName List.66 List.439 List.442; + let List.604 : {List U8, U64} = CallByName List.145 List.440 List.610 List.441; + let List.607 : U64 = 1i64; + let List.606 : U64 = CallByName Num.19 List.442 List.607; + jump List.601 List.439 List.604 List.441 List.606 List.443; + else + dec List.439; + ret List.440; + in + jump List.601 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25; -procedure List.80 (List.578, List.579, List.580, List.581, List.582): +procedure List.80 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33): joinpoint List.553 List.439 List.440 List.441 List.442 List.443: let List.555 : Int1 = CallByName Num.22 List.442 List.443; if List.555 then @@ -144,22 +159,7 @@ procedure List.80 (List.578, List.579, List.580, List.581, List.582): dec List.439; ret List.440; in - jump List.553 List.578 List.579 List.580 List.581 List.582; - -procedure List.80 (List.651, List.652, List.653, List.654, List.655): - joinpoint List.626 List.439 List.440 List.441 List.442 List.443: - let List.628 : Int1 = CallByName Num.22 List.442 List.443; - if List.628 then - let List.635 : [] = CallByName List.66 List.439 List.442; - let List.629 : {List U8, U64} = CallByName List.145 List.440 List.635 List.441; - let List.632 : U64 = 1i64; - let List.631 : U64 = CallByName Num.19 List.442 List.632; - jump List.626 List.439 List.629 List.441 List.631 List.443; - else - dec List.439; - ret List.440; - in - jump List.626 List.651 List.652 List.653 List.654 List.655; + jump List.553 #Derived_gen.29 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33; procedure List.93 (List.436, List.437, List.438): let List.551 : U64 = 0i64; @@ -168,34 +168,34 @@ procedure List.93 (List.436, List.437, List.438): ret List.550; procedure List.93 (List.436, List.437, List.438): - let List.624 : U64 = 0i64; - let List.625 : U64 = CallByName List.6 List.436; - let List.623 : {List U8, U64} = CallByName List.80 List.436 List.437 List.438 List.624 List.625; - ret List.623; + let List.599 : U64 = 0i64; + let List.600 : U64 = CallByName List.6 List.436; + let List.598 : {List U8, U64} = CallByName List.80 List.436 List.437 List.438 List.599 List.600; + ret List.598; procedure Num.127 (#Attr.2): - let Num.320 : U8 = lowlevel NumIntCast #Attr.2; - ret Num.320; + let Num.311 : U8 = lowlevel NumIntCast #Attr.2; + ret Num.311; procedure Num.19 (#Attr.2, #Attr.3): - let Num.323 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.323; + let Num.314 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.314; procedure Num.20 (#Attr.2, #Attr.3): - let Num.321 : U64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.321; + let Num.312 : U64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.312; procedure Num.22 (#Attr.2, #Attr.3): - let Num.324 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.324; + let Num.315 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.315; procedure Num.24 (#Attr.2, #Attr.3): - let Num.322 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; - ret Num.322; + let Num.313 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; + ret Num.313; procedure Str.12 (#Attr.2): - let Str.300 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.300; + let Str.299 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.299; procedure Test.2 (Test.11): let Test.18 : {{}, {}} = CallByName Encode.23 Test.11; @@ -267,34 +267,34 @@ procedure TotallyNotJson.264 (TotallyNotJson.265, TotallyNotJson.1175, #Attr.12) procedure TotallyNotJson.264 (TotallyNotJson.265, TotallyNotJson.1175, #Attr.12): let TotallyNotJson.263 : List [] = StructAtIndex 1 #Attr.12; let TotallyNotJson.262 : Str = StructAtIndex 0 #Attr.12; - let TotallyNotJson.1264 : I64 = 123i64; - let TotallyNotJson.1263 : U8 = CallByName Num.127 TotallyNotJson.1264; - let TotallyNotJson.1260 : List U8 = CallByName List.4 TotallyNotJson.265 TotallyNotJson.1263; - let TotallyNotJson.1262 : I64 = 34i64; - let TotallyNotJson.1261 : U8 = CallByName Num.127 TotallyNotJson.1262; - let TotallyNotJson.1258 : List U8 = CallByName List.4 TotallyNotJson.1260 TotallyNotJson.1261; - let TotallyNotJson.1259 : List U8 = CallByName Str.12 TotallyNotJson.262; - let TotallyNotJson.1255 : List U8 = CallByName List.8 TotallyNotJson.1258 TotallyNotJson.1259; - let TotallyNotJson.1257 : I64 = 34i64; - let TotallyNotJson.1256 : U8 = CallByName Num.127 TotallyNotJson.1257; - let TotallyNotJson.1252 : List U8 = CallByName List.4 TotallyNotJson.1255 TotallyNotJson.1256; - let TotallyNotJson.1254 : I64 = 58i64; + let TotallyNotJson.1256 : I64 = 123i64; + let TotallyNotJson.1255 : U8 = CallByName Num.127 TotallyNotJson.1256; + let TotallyNotJson.1252 : List U8 = CallByName List.4 TotallyNotJson.265 TotallyNotJson.1255; + let TotallyNotJson.1254 : I64 = 34i64; let TotallyNotJson.1253 : U8 = CallByName Num.127 TotallyNotJson.1254; - let TotallyNotJson.1249 : List U8 = CallByName List.4 TotallyNotJson.1252 TotallyNotJson.1253; - let TotallyNotJson.1251 : I64 = 91i64; - let TotallyNotJson.1250 : U8 = CallByName Num.127 TotallyNotJson.1251; - let TotallyNotJson.268 : List U8 = CallByName List.4 TotallyNotJson.1249 TotallyNotJson.1250; - let TotallyNotJson.1248 : U64 = CallByName List.6 TotallyNotJson.263; - let TotallyNotJson.1236 : {List U8, U64} = Struct {TotallyNotJson.268, TotallyNotJson.1248}; - let TotallyNotJson.1235 : {List U8, U64} = CallByName List.18 TotallyNotJson.263 TotallyNotJson.1236 TotallyNotJson.1175; - let TotallyNotJson.270 : List U8 = StructAtIndex 0 TotallyNotJson.1235; - let TotallyNotJson.1234 : I64 = 93i64; - let TotallyNotJson.1233 : U8 = CallByName Num.127 TotallyNotJson.1234; - let TotallyNotJson.1230 : List U8 = CallByName List.4 TotallyNotJson.270 TotallyNotJson.1233; - let TotallyNotJson.1232 : I64 = 125i64; - let TotallyNotJson.1231 : U8 = CallByName Num.127 TotallyNotJson.1232; - let TotallyNotJson.1229 : List U8 = CallByName List.4 TotallyNotJson.1230 TotallyNotJson.1231; - ret TotallyNotJson.1229; + let TotallyNotJson.1250 : List U8 = CallByName List.4 TotallyNotJson.1252 TotallyNotJson.1253; + let TotallyNotJson.1251 : List U8 = CallByName Str.12 TotallyNotJson.262; + let TotallyNotJson.1247 : List U8 = CallByName List.8 TotallyNotJson.1250 TotallyNotJson.1251; + let TotallyNotJson.1249 : I64 = 34i64; + let TotallyNotJson.1248 : U8 = CallByName Num.127 TotallyNotJson.1249; + let TotallyNotJson.1244 : List U8 = CallByName List.4 TotallyNotJson.1247 TotallyNotJson.1248; + let TotallyNotJson.1246 : I64 = 58i64; + let TotallyNotJson.1245 : U8 = CallByName Num.127 TotallyNotJson.1246; + let TotallyNotJson.1241 : List U8 = CallByName List.4 TotallyNotJson.1244 TotallyNotJson.1245; + let TotallyNotJson.1243 : I64 = 91i64; + let TotallyNotJson.1242 : U8 = CallByName Num.127 TotallyNotJson.1243; + let TotallyNotJson.268 : List U8 = CallByName List.4 TotallyNotJson.1241 TotallyNotJson.1242; + let TotallyNotJson.1240 : U64 = CallByName List.6 TotallyNotJson.263; + let TotallyNotJson.1228 : {List U8, U64} = Struct {TotallyNotJson.268, TotallyNotJson.1240}; + let TotallyNotJson.1227 : {List U8, U64} = CallByName List.18 TotallyNotJson.263 TotallyNotJson.1228 TotallyNotJson.1175; + let TotallyNotJson.270 : List U8 = StructAtIndex 0 TotallyNotJson.1227; + let TotallyNotJson.1226 : I64 = 93i64; + let TotallyNotJson.1225 : U8 = CallByName Num.127 TotallyNotJson.1226; + let TotallyNotJson.1222 : List U8 = CallByName List.4 TotallyNotJson.270 TotallyNotJson.1225; + let TotallyNotJson.1224 : I64 = 125i64; + let TotallyNotJson.1223 : U8 = CallByName Num.127 TotallyNotJson.1224; + let TotallyNotJson.1221 : List U8 = CallByName List.4 TotallyNotJson.1222 TotallyNotJson.1223; + ret TotallyNotJson.1221; procedure TotallyNotJson.267 (TotallyNotJson.1177, TotallyNotJson.273, TotallyNotJson.266): let TotallyNotJson.271 : List U8 = StructAtIndex 0 TotallyNotJson.1177; @@ -320,21 +320,21 @@ procedure TotallyNotJson.267 (TotallyNotJson.1177, TotallyNotJson.273, TotallyNo let TotallyNotJson.271 : List U8 = StructAtIndex 0 TotallyNotJson.1177; let TotallyNotJson.272 : U64 = StructAtIndex 1 TotallyNotJson.1177; let TotallyNotJson.274 : List U8 = CallByName Encode.24 TotallyNotJson.271 TotallyNotJson.273 TotallyNotJson.266; - joinpoint TotallyNotJson.1242 TotallyNotJson.275: - let TotallyNotJson.1240 : U64 = 1i64; - let TotallyNotJson.1239 : U64 = CallByName Num.20 TotallyNotJson.272 TotallyNotJson.1240; - let TotallyNotJson.1238 : {List U8, U64} = Struct {TotallyNotJson.275, TotallyNotJson.1239}; - ret TotallyNotJson.1238; + joinpoint TotallyNotJson.1234 TotallyNotJson.275: + let TotallyNotJson.1232 : U64 = 1i64; + let TotallyNotJson.1231 : U64 = CallByName Num.20 TotallyNotJson.272 TotallyNotJson.1232; + let TotallyNotJson.1230 : {List U8, U64} = Struct {TotallyNotJson.275, TotallyNotJson.1231}; + ret TotallyNotJson.1230; in - let TotallyNotJson.1246 : U64 = 1i64; - let TotallyNotJson.1243 : Int1 = CallByName Num.24 TotallyNotJson.272 TotallyNotJson.1246; - if TotallyNotJson.1243 then - let TotallyNotJson.1245 : I64 = 44i64; - let TotallyNotJson.1244 : U8 = CallByName Num.127 TotallyNotJson.1245; - let TotallyNotJson.1241 : List U8 = CallByName List.4 TotallyNotJson.274 TotallyNotJson.1244; - jump TotallyNotJson.1242 TotallyNotJson.1241; + let TotallyNotJson.1238 : U64 = 1i64; + let TotallyNotJson.1235 : Int1 = CallByName Num.24 TotallyNotJson.272 TotallyNotJson.1238; + if TotallyNotJson.1235 then + let TotallyNotJson.1237 : I64 = 44i64; + let TotallyNotJson.1236 : U8 = CallByName Num.127 TotallyNotJson.1237; + let TotallyNotJson.1233 : List U8 = CallByName List.4 TotallyNotJson.274 TotallyNotJson.1236; + jump TotallyNotJson.1234 TotallyNotJson.1233; else - jump TotallyNotJson.1242 TotallyNotJson.274; + jump TotallyNotJson.1234 TotallyNotJson.274; procedure TotallyNotJson.32 (TotallyNotJson.262, TotallyNotJson.263): let TotallyNotJson.1215 : {Str, List [C {}, C {}]} = Struct {TotallyNotJson.262, TotallyNotJson.263}; @@ -342,9 +342,9 @@ procedure TotallyNotJson.32 (TotallyNotJson.262, TotallyNotJson.263): ret TotallyNotJson.1214; procedure TotallyNotJson.32 (TotallyNotJson.262, TotallyNotJson.263): - let TotallyNotJson.1266 : {Str, List []} = Struct {TotallyNotJson.262, TotallyNotJson.263}; - let TotallyNotJson.1265 : {Str, List []} = CallByName Encode.23 TotallyNotJson.1266; - ret TotallyNotJson.1265; + let TotallyNotJson.1258 : {Str, List []} = Struct {TotallyNotJson.262, TotallyNotJson.263}; + let TotallyNotJson.1257 : {Str, List []} = CallByName Encode.23 TotallyNotJson.1258; + ret TotallyNotJson.1257; procedure Test.0 (): let Test.13 : {{}, {}} = CallByName Test.3; diff --git a/crates/compiler/test_mono/generated/weakening_avoids_overspecialization.txt b/crates/compiler/test_mono/generated/weakening_avoids_overspecialization.txt index 151d18ae6dd..1396a444e32 100644 --- a/crates/compiler/test_mono/generated/weakening_avoids_overspecialization.txt +++ b/crates/compiler/test_mono/generated/weakening_avoids_overspecialization.txt @@ -53,7 +53,7 @@ procedure List.72 (#Attr.2, #Attr.3, #Attr.4): let List.527 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; ret List.527; -procedure List.80 (List.571, List.572, List.573, List.574, List.575): +procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): joinpoint List.545 List.439 List.440 List.441 List.442 List.443: let List.547 : Int1 = CallByName Num.22 List.442 List.443; if List.547 then @@ -77,7 +77,7 @@ procedure List.80 (List.571, List.572, List.573, List.574, List.575): let List.546 : [C U64, C U64] = TagId(1) List.440; ret List.546; in - jump List.545 List.571 List.572 List.573 List.574 List.575; + jump List.545 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; procedure List.93 (List.436, List.437, List.438): let List.543 : U64 = 0i64; diff --git a/crates/compiler/test_mono/src/tests.rs b/crates/compiler/test_mono/src/tests.rs index 1ea7bb17461..80c8ad48c65 100644 --- a/crates/compiler/test_mono/src/tests.rs +++ b/crates/compiler/test_mono/src/tests.rs @@ -123,10 +123,10 @@ fn compiles_to_ir(test_name: &str, src: &str, mode: &str, allow_type_errors: boo Err(LoadMonomorphizedError::LoadingProblem(roc_load::LoadingProblem::FormattedReport( report, ))) => { - println!("{}", report); + println!("{report}"); panic!(); } - Err(e) => panic!("{:?}", e), + Err(e) => panic!("{e:?}"), }; use roc_load::MonomorphizedModule; @@ -146,7 +146,9 @@ fn compiles_to_ir(test_name: &str, src: &str, mode: &str, allow_type_errors: boo println!("Ignoring {} canonicalization problems", can_problems.len()); } - assert!(allow_type_errors || type_problems.is_empty()); + if !(allow_type_errors || type_problems.is_empty()) { + panic!("mono test has type problems:\n\n{:#?}", type_problems); + } let main_fn_symbol = exposed_to_host.top_level_values.keys().copied().next(); @@ -199,7 +201,7 @@ fn verify_procedures<'a>( let result = procs_string.join("\n"); - let path = format!("generated/{}.txt", test_name); + let path = format!("generated/{test_name}.txt"); std::fs::create_dir_all("generated").unwrap(); std::fs::write(&path, result).unwrap(); @@ -2873,11 +2875,11 @@ fn layout_cache_structure_with_multiple_recursive_structures() { LinkedList : [Nil, Cons { first : Chain, rest : LinkedList }] main = - base : LinkedList + base : LinkedList base = Nil walker : LinkedList, Chain -> LinkedList - walker = \rest, first -> Cons { first, rest } + walker = \rest, first -> Cons { first, rest } list : List Chain list = [] @@ -3004,7 +3006,7 @@ fn rb_tree_fbip() { if k < kx then Node Red (ins l k v) kx vx r else - if k > kx + if k > kx then Node Red l kx vx (ins r k v) else Node Red l k v r "# @@ -3017,10 +3019,10 @@ fn specialize_after_match() { r#" app "test" provides [main] to "./platform" - main = + main = listA : LinkedList Str listA = Nil - + listB : LinkedList Str listB = Nil @@ -3033,13 +3035,13 @@ fn specialize_after_match() { Nil -> linkedListLength listB Cons a aa -> when listB is Nil -> linkedListLength listA - Cons b bb -> + Cons b bb -> lengthA = (linkedListLength aa) + 1 lengthB = linkedListLength listB if lengthA > lengthB then lengthA else lengthB - + linkedListLength : LinkedList a -> Nat linkedListLength = \list -> when list is Nil -> 0 @@ -3088,7 +3090,7 @@ fn drop_specialize_after_jump() { v = "value" t = { left: { left: v, right: v }, right: v } tupleItem t - + tupleItem = \t -> true = Bool.true l = t.left @@ -3146,3 +3148,115 @@ fn dbg_str_followed_by_number() { "# ) } + +#[mono_test] +fn linked_list_reverse() { + indoc!( + r#" + app "test" provides [main] to "./platform" + + LinkedList a : [Nil, Cons a (LinkedList a)] + + reverse : LinkedList a -> LinkedList a + reverse = \list -> reverseHelp Nil list + + reverseHelp : LinkedList a, LinkedList a -> LinkedList a + reverseHelp = \accum, list -> + when list is + Nil -> accum + Cons first rest -> reverseHelp (Cons first accum) rest + + main : LinkedList I64 + main = reverse (Cons 42 Nil) + "# + ) +} + +#[mono_test] +fn linked_list_map() { + indoc!( + r#" + app "test" provides [main] to "./platform" + + LinkedList a : [Nil, Cons a (LinkedList a)] + + map : (a -> b), LinkedList a -> LinkedList b + map = \f, list -> + when list is + Nil -> Nil + Cons x xs -> Cons (f x) (map f xs) + + main : LinkedList I64 + main = map (\x -> x + 1i64) (Cons 42 Nil) + "# + ) +} + +#[mono_test] +fn linked_list_filter() { + indoc!( + r#" + app "test" provides [main] to "./platform" + + LinkedList a : [Nil, Cons a (LinkedList a)] + + filter : LinkedList a, (a -> Bool) -> LinkedList a + filter = \list, predicate -> + when list is + Nil -> Nil + Cons x xs -> + if predicate x then + Cons x (filter xs predicate) + else + filter xs predicate + + + main : LinkedList I64 + main = filter (Cons 1 (Cons 2 Nil)) Num.isEven + "# + ) +} + +#[mono_test] +fn capture_void_layout_task() { + indoc!( + r#" + app "test" provides [main] to "./platform" + + Fx a : {} -> a + + Task ok err : Fx (Result ok err) + + succeed : ok -> Task ok * + succeed = \ok -> \{} -> Ok ok + + after : Fx a, (a -> Fx b) -> Fx b + after = \fx, toNext -> + afterInner = \{} -> + fxOut = fx {} + next = toNext fxOut + next {} + + afterInner + + await : Task a err, (a -> Task b err) -> Task b err + await = \fx, toNext -> + inner = after fx \result -> + when result is + Ok a -> + bFx = toNext a + bFx + Err e -> (\{} -> Err e) + inner + + forEach : List a, (a -> Task {} err) -> Task {} err + forEach = \list, fromElem -> + List.walk list (succeed {}) \task, elem -> + await task \{} -> fromElem elem + + main : Task {} [] + main = + forEach [] \_ -> succeed {} + "# + ) +} diff --git a/crates/compiler/test_syntax/src/test_helpers.rs b/crates/compiler/test_syntax/src/test_helpers.rs index e0038c57055..e5bfe854d6a 100644 --- a/crates/compiler/test_syntax/src/test_helpers.rs +++ b/crates/compiler/test_syntax/src/test_helpers.rs @@ -96,10 +96,10 @@ impl<'a> Output<'a> { pub fn debug_format_inner(&self) -> String { match self { - Output::Header(header) => format!("{:#?}\n", header), - Output::ModuleDefs(defs) => format!("{:#?}\n", defs), - Output::Expr(expr) => format!("{:#?}\n", expr), - Output::Full { .. } => format!("{:#?}\n", self), + Output::Header(header) => format!("{header:#?}\n"), + Output::ModuleDefs(defs) => format!("{defs:#?}\n"), + Output::Expr(expr) => format!("{expr:#?}\n"), + Output::Full { .. } => format!("{self:#?}\n"), } } } @@ -224,7 +224,7 @@ impl<'a> Input<'a> { // the PartialEq implementation is returning `false` even when the Debug-formatted impl is exactly the same. // I don't have the patience to debug this right now, so let's leave it for another day... // TODO: fix PartialEq impl on ast types - if format!("{:?}", ast_normalized) != format!("{:?}", reparsed_ast_normalized) { + if format!("{ast_normalized:?}") != format!("{reparsed_ast_normalized:?}") { panic!( "Formatting bug; formatting didn't reparse to the same AST (after removing spaces)\n\n\ * * * Source code before formatting:\n{}\n\n\ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_has_abilities.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_has_abilities.expr.formatted.roc index 783b9d8c175..768b1dab9d5 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_has_abilities.expr.formatted.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_has_abilities.expr.formatted.roc @@ -1,10 +1,10 @@ A := U8 has [Eq, Hash] A := a | a has Other - has [Eq, Hash] + has [Eq, Hash] A := a | a has Other - has [Eq, Hash] + has [Eq, Hash] A := U8 has [Eq { eq }, Hash { hash }] @@ -17,7 +17,7 @@ A := U8 has [Hash, Eq { eq, eq1 }] A := U8 has [] A := a | a has Other - has [Eq { eq }, Hash { hash }] + has [Eq { eq }, Hash { hash }] A := U8 has [Eq {}] diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.formatted.roc new file mode 100644 index 00000000000..48ccd21f646 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.formatted.roc @@ -0,0 +1,6 @@ +app "example" + packages { pf: "path" } + imports [pf.Stdout] + provides [main] to pf + +main = Stdout.line "Hello" diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast new file mode 100644 index 00000000000..dd4a45a9f21 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast @@ -0,0 +1,116 @@ +Full { + header: Module { + comments: [], + header: App( + AppHeader { + before_name: [], + name: @4-13 PlainLine( + "example", + ), + packages: Some( + KeywordItem { + keyword: Spaces { + before: [ + Newline, + ], + item: PackagesKeyword, + after: [], + }, + item: [ + @29-40 PackageEntry { + shorthand: "pf", + spaces_after_shorthand: [], + package_name: @34-40 PackageName( + "path", + ), + }, + ], + }, + ), + imports: Some( + KeywordItem { + keyword: Spaces { + before: [ + Newline, + ], + item: ImportsKeyword, + after: [], + }, + item: [ + @57-66 Package( + "pf", + ModuleName( + "Stdout", + ), + [], + ), + ], + }, + ), + provides: ProvidesTo { + provides_keyword: Spaces { + before: [ + Newline, + ], + item: ProvidesKeyword, + after: [], + }, + entries: [ + @84-88 ExposedName( + "main", + ), + ], + types: None, + to_keyword: Spaces { + before: [], + item: ToKeyword, + after: [], + }, + to: @94-96 ExistingPackage( + "pf", + ), + }, + }, + ), + }, + module_defs: Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @98-124, + ], + space_before: [ + Slice(start = 0, length = 2), + ], + space_after: [ + Slice(start = 2, length = 0), + ], + spaces: [ + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @98-102 Identifier( + "main", + ), + @105-124 Apply( + @105-116 Var { + module_name: "Stdout", + ident: "line", + }, + [ + @117-124 Str( + PlainLine( + "Hello", + ), + ), + ], + Space, + ), + ), + ], + }, +} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.roc b/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.roc new file mode 100644 index 00000000000..e6d070fc184 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.roc @@ -0,0 +1,6 @@ +app "example" + packages { pf : "path" } + imports [ pf.Stdout ] + provides [ main ] to pf + +main = Stdout.line "Hello" \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/test_fmt.rs b/crates/compiler/test_syntax/tests/test_fmt.rs index 751b3f080b4..847832782f0 100644 --- a/crates/compiler/test_syntax/tests/test_fmt.rs +++ b/crates/compiler/test_syntax/tests/test_fmt.rs @@ -43,8 +43,7 @@ mod test_fmt { fmt_defs(buf, &loc_defs, 0); } Err(error) => panic!( - r"Unexpected parse failure when parsing this for defs formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", - src, error + r"Unexpected parse failure when parsing this for defs formatting:\n\n{src:?}\n\nParse error was:\n\n{error:?}\n\n" ), } } @@ -67,8 +66,7 @@ mod test_fmt { let (reparsed_ast, state) = module::parse_header(&arena, State::new(output.as_bytes())).unwrap_or_else(|err| { panic!( - "After formatting, the source code no longer parsed!\n\nParse error was: {:?}\n\nThe code that failed to parse:\n\n{}\n\n", - err, output + "After formatting, the source code no longer parsed!\n\nParse error was: {err:?}\n\nThe code that failed to parse:\n\n{output}\n\n" ); }); @@ -80,13 +78,11 @@ mod test_fmt { // the PartialEq implementation is returning `false` even when the Debug-formatted impl is exactly the same. // I don't have the patience to debug this right now, so let's leave it for another day... // TODO: fix PartialEq impl on ast types - if format!("{:?}", ast_normalized) != format!("{:?}", reparsed_ast_normalized) { + if format!("{ast_normalized:?}") != format!("{reparsed_ast_normalized:?}") { panic!( "Formatting bug; formatting didn't reparse to the same AST (after removing spaces)\n\n\ - * * * Source code before formatting:\n{}\n\n\ - * * * Source code after formatting:\n{}\n\n", - src, - output + * * * Source code before formatting:\n{src}\n\n\ + * * * Source code after formatting:\n{output}\n\n" ); } @@ -111,7 +107,7 @@ mod test_fmt { // those more than we want to know that the expectation failed! assert_multiline_str_eq!(expected, output); } - Err(error) => panic!("Unexpected parse failure when parsing this for module header formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", src, error) + Err(error) => panic!("Unexpected parse failure when parsing this for module header formatting:\n\n{src:?}\n\nParse error was:\n\n{error:?}\n\n") }; } @@ -5447,7 +5443,7 @@ mod test_fmt { indoc!( r#" A := U8 - has [Eq, Hash] + has [Eq, Hash] 0 "# @@ -5465,7 +5461,7 @@ mod test_fmt { indoc!( r#" A := a | a has Hash - has [Eq, Hash] + has [Eq, Hash] 0 "# @@ -5555,11 +5551,11 @@ mod test_fmt { r#" A := U8 has [Eq { eq, eq1 }] A := U8 has [ - Eq { - eq, - eq1, - }, - ] + Eq { + eq, + eq1, + }, + ] 0 "# @@ -5569,7 +5565,7 @@ mod test_fmt { expr_formats_same(indoc!( r#" A := a | a has Other - has [Eq { eq }, Hash { hash }] + has [Eq { eq }, Hash { hash }] 0 "# diff --git a/crates/compiler/test_syntax/tests/test_snapshots.rs b/crates/compiler/test_syntax/tests/test_snapshots.rs index eb12ac3da7d..e51d67cf165 100644 --- a/crates/compiler/test_syntax/tests/test_snapshots.rs +++ b/crates/compiler/test_syntax/tests/test_snapshots.rs @@ -430,6 +430,7 @@ mod test_snapshots { pass/requires_type.header, pass/single_arg_closure.expr, pass/single_underscore_closure.expr, + pass/space_before_colon.full, pass/space_only_after_minus.expr, pass/spaced_singleton_list.expr, pass/spaces_inside_empty_list.expr, @@ -525,14 +526,13 @@ mod test_snapshots { // Expect file to be missing assert!( !result_path.exists(), - "Expected {:?} to be missing. \ + "Expected {result_path:?} to be missing. \ This is how we represent a 'default' result (i.e. a test that \ formats to the same thing as the input). \ Consider running the tests with:\n\ `env ROC_SNAPSHOT_TEST_OVERWRITE=1 cargo test ...`\n\ (which will delete the file for you),\n\ - and commiting the delete.", - result_path + and commiting the delete." ); } } @@ -544,15 +544,12 @@ mod test_snapshots { let mut parent = std::path::PathBuf::from("tests"); parent.push("snapshots"); parent.push(expect.to_dir_name()); - let input_path = parent.join(format!("{}.{}.roc", name, ty)); - let result_path = parent.join(format!("{}.{}.result-ast", name, ty)); - let formatted_path = parent.join(format!("{}.{}.formatted.roc", name, ty)); + let input_path = parent.join(format!("{name}.{ty}.roc")); + let result_path = parent.join(format!("{name}.{ty}.result-ast")); + let formatted_path = parent.join(format!("{name}.{ty}.formatted.roc")); let source = std::fs::read_to_string(&input_path).unwrap_or_else(|err| { - panic!( - "Could not find a snapshot test result at {:?} - {:?}", - input_path, err - ) + panic!("Could not find a snapshot test result at {input_path:?} - {err:?}") }); let input = func(&source); @@ -565,14 +562,14 @@ mod test_snapshots { } Ok(ast.debug_format_inner()) } - Err(err) => Err(format!("{:?}", err)), + Err(err) => Err(format!("{err:?}")), }; if expect == TestExpectation::Pass { let tokens = roc_parse::highlight::highlight(&source); for token in tokens { if token.value == roc_parse::highlight::Token::Error { - panic!("Found an error highlight token in the input: {:?}", token); + panic!("Found an error highlight token in the input: {token:?}"); } } } @@ -652,7 +649,7 @@ mod test_snapshots { #[test] fn string_with_escaped_char_at_end() { parses_with_escaped_char( - |esc| format!(r#""abcd{}""#, esc), + |esc| format!(r#""abcd{esc}""#), |esc, arena| bumpalo::vec![in arena; Plaintext("abcd"), EscapedChar(esc)], ); } @@ -660,7 +657,7 @@ mod test_snapshots { #[test] fn string_with_escaped_char_in_front() { parses_with_escaped_char( - |esc| format!(r#""{}abcd""#, esc), + |esc| format!(r#""{esc}abcd""#), |esc, arena| bumpalo::vec![in arena; EscapedChar(esc), Plaintext("abcd")], ); } @@ -668,7 +665,7 @@ mod test_snapshots { #[test] fn string_with_escaped_char_in_middle() { parses_with_escaped_char( - |esc| format!(r#""ab{}cd""#, esc), + |esc| format!(r#""ab{esc}cd""#), |esc, arena| bumpalo::vec![in arena; Plaintext("ab"), EscapedChar(esc), Plaintext("cd")], ); } @@ -676,7 +673,7 @@ mod test_snapshots { #[test] fn string_with_multiple_escaped_chars() { parses_with_escaped_char( - |esc| format!(r#""{}abc{}de{}fghi{}""#, esc, esc, esc, esc), + |esc| format!(r#""{esc}abc{esc}de{esc}fghi{esc}""#), |esc, arena| bumpalo::vec![in arena; EscapedChar(esc), Plaintext("abc"), EscapedChar(esc), Plaintext("de"), EscapedChar(esc), Plaintext("fghi"), EscapedChar(esc)], ); } diff --git a/crates/compiler/types/src/pretty_print.rs b/crates/compiler/types/src/pretty_print.rs index 77bbbb4da18..2b235b43aa8 100644 --- a/crates/compiler/types/src/pretty_print.rs +++ b/crates/compiler/types/src/pretty_print.rs @@ -820,7 +820,7 @@ fn write_content<'a>( "".to_string() }; if env.home == symbol.module_id() { - format!("{}{}", ident_str, disambiguation,) + format!("{ident_str}{disambiguation}",) } else { format!( "{}.{}{}", diff --git a/crates/compiler/types/src/subs.rs b/crates/compiler/types/src/subs.rs index 15b895de732..c1126f84434 100644 --- a/crates/compiler/types/src/subs.rs +++ b/crates/compiler/types/src/subs.rs @@ -774,11 +774,11 @@ impl fmt::Debug for Subs { let root = self.get_root_key_without_compacting(var); if var == root { - write!(f, "{} => ", i)?; + write!(f, "{i} => ")?; subs_fmt_desc(&desc, self, f)?; } else { - write!(f, "{} => <{:?}>", i, root)?; + write!(f, "{i} => <{root:?}>")?; } writeln!(f)?; @@ -811,7 +811,7 @@ fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt: Some(index) => subs[*index].as_str(), None => "_", }; - write!(f, "Flex({})", name) + write!(f, "Flex({name})") } Content::FlexAbleVar(name, symbols) => { let name = match name { @@ -827,7 +827,7 @@ fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt: Content::RecursionVar { structure, opt_name, - } => write!(f, "Recursion({:?}, {:?})", structure, opt_name), + } => write!(f, "Recursion({structure:?}, {opt_name:?})"), Content::Structure(flat_type) => subs_fmt_flat_type(flat_type, subs, f), Content::Alias(name, arguments, actual, kind) => { let slice = subs.get_subs_slice(arguments.all_variables()); @@ -855,7 +855,7 @@ fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt: write!(f, "LambdaSet([")?; for (name, slice) in solved.iter_from_subs(subs) { - write!(f, "{:?} ", name)?; + write!(f, "{name:?} ")?; for var in slice { write!( f, @@ -869,7 +869,7 @@ fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt: write!(f, "]")?; if let Some(rec_var) = recursion_var.into_variable() { - write!(f, " as <{:?}>", rec_var)?; + write!(f, " as <{rec_var:?}>")?; } for Uls(var, member, region) in subs.get_subs_slice(*unspecialized) { write!( @@ -881,10 +881,10 @@ fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt: region )?; } - write!(f, ", ^<{:?}>)", ambient_function_var) + write!(f, ", ^<{ambient_function_var:?}>)") } Content::RangedNumber(range) => { - write!(f, "RangedNumber( {:?})", range) + write!(f, "RangedNumber( {range:?})") } Content::Error => write!(f, "Error"), } @@ -903,7 +903,7 @@ fn subs_fmt_flat_type(this: &FlatType, subs: &Subs, f: &mut fmt::Formatter) -> f FlatType::Apply(name, arguments) => { let slice = subs.get_subs_slice(*arguments); - write!(f, "Apply({:?}, {:?})", name, slice) + write!(f, "Apply({name:?}, {slice:?})") } FlatType::Func(arguments, lambda_set, result) => { let slice = subs.get_subs_slice(*arguments); @@ -948,7 +948,7 @@ fn subs_fmt_flat_type(this: &FlatType, subs: &Subs, f: &mut fmt::Formatter) -> f )?; } - write!(f, "}}<{:?}>", new_ext) + write!(f, "}}<{new_ext:?}>") } FlatType::Tuple(elems, ext) => { write!(f, "( ")?; @@ -962,14 +962,14 @@ fn subs_fmt_flat_type(this: &FlatType, subs: &Subs, f: &mut fmt::Formatter) -> f )?; } - write!(f, ")<{:?}>", new_ext) + write!(f, ")<{new_ext:?}>") } FlatType::TagUnion(tags, ext) => { write!(f, "[")?; let (it, new_ext) = tags.sorted_iterator_and_ext(subs, *ext); for (name, slice) in it { - write!(f, "{:?} ", name)?; + write!(f, "{name:?} ")?; for var in slice { write!( f, @@ -981,26 +981,22 @@ fn subs_fmt_flat_type(this: &FlatType, subs: &Subs, f: &mut fmt::Formatter) -> f write!(f, ", ")?; } - write!(f, "]<{:?}>", new_ext) + write!(f, "]<{new_ext:?}>") } FlatType::FunctionOrTagUnion(tagnames, symbol, ext) => { let tagnames: &[TagName] = subs.get_subs_slice(*tagnames); - write!( - f, - "FunctionOrTagUnion({:?}, {:?}, {:?})", - tagnames, symbol, ext - ) + write!(f, "FunctionOrTagUnion({tagnames:?}, {symbol:?}, {ext:?})") } FlatType::RecursiveTagUnion(rec, tags, ext) => { write!(f, "[")?; let (it, new_ext) = tags.sorted_iterator_and_ext(subs, *ext); for (name, slice) in it { - write!(f, "{:?} {:?}, ", name, slice)?; + write!(f, "{name:?} {slice:?}, ")?; } - write!(f, "]<{:?}> as <{:?}>", new_ext, rec) + write!(f, "]<{new_ext:?}> as <{rec:?}>") } FlatType::EmptyRecord => write!(f, "EmptyRecord"), FlatType::EmptyTuple => write!(f, "EmptyTuple"), @@ -1016,7 +1012,7 @@ impl std::fmt::Debug for DebugUtable<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("UnificationTable {\n")?; for v in 0..self.0.utable.len() { - f.write_fmt(format_args!(" {} => ", v))?; + f.write_fmt(format_args!(" {v} => "))?; let var = unsafe { Variable::from_index(v as u32) }; let root = self.0.utable.root_key_without_compacting(var); if root == var { @@ -2163,10 +2159,15 @@ impl Subs { self.utable.vars_since_snapshot(&snapshot.utable_snapshot) } - pub fn get_lambda_set(&self, lambda_set: Variable) -> LambdaSet { - match self.get_content_without_compacting(lambda_set) { - Content::LambdaSet(lambda_set) => *lambda_set, - _ => internal_error!("not a lambda set"), + pub fn get_lambda_set(&self, mut lambda_set: Variable) -> LambdaSet { + loop { + match self.get_content_without_compacting(lambda_set) { + Content::LambdaSet(lambda_set) => return *lambda_set, + Content::RecursionVar { structure, .. } => { + lambda_set = *structure; + } + _ => internal_error!("not a lambda set"), + } } } @@ -2815,9 +2816,7 @@ where debug_assert_eq!( labels.len(), variables.len(), - "tag name len != variables len: {:?} {:?}", - labels, - variables, + "tag name len != variables len: {labels:?} {variables:?}", ); Self { @@ -4020,7 +4019,7 @@ where } else { // TODO is this the proper use of index here, or should we be // doing something else like turning it into an ASCII letter? - Lowercase::from(format!("{}{}", given_name, index)) + Lowercase::from(format!("{given_name}{index}")) }; match taken_names.get(&indexed_name) { @@ -4330,7 +4329,7 @@ fn flat_type_to_err_type( ErrorType::Error => ErrorType::Record(err_fields, TypeExt::Closed), other => - panic!("Tried to convert a record extension to an error, but the record extension had the ErrorType of {:?}", other) + panic!("Tried to convert a record extension to an error, but the record extension had the ErrorType of {other:?}") } } @@ -4362,7 +4361,7 @@ fn flat_type_to_err_type( ErrorType::Error => ErrorType::Tuple(err_elems, TypeExt::Closed), other => - panic!("Tried to convert a record extension to an error, but the record extension had the ErrorType of {:?}", other) + panic!("Tried to convert a record extension to an error, but the record extension had the ErrorType of {other:?}") } } @@ -4388,7 +4387,7 @@ fn flat_type_to_err_type( ErrorType::Error => ErrorType::TagUnion(err_tags, TypeExt::Closed, pol), other => - panic!("Tried to convert a tag union extension to an error, but the tag union extension had the ErrorType of {:?}", other) + panic!("Tried to convert a tag union extension to an error, but the tag union extension had the ErrorType of {other:?}") } } @@ -4418,7 +4417,7 @@ fn flat_type_to_err_type( ErrorType::Error => ErrorType::TagUnion(err_tags, TypeExt::Closed, pol), other => - panic!("Tried to convert a tag union extension to an error, but the tag union extension had the ErrorType of {:?}", other) + panic!("Tried to convert a tag union extension to an error, but the tag union extension had the ErrorType of {other:?}") } } @@ -4450,7 +4449,7 @@ fn flat_type_to_err_type( ErrorType::Error => ErrorType::RecursiveTagUnion(rec_error_type, err_tags, TypeExt::Closed, pol), other => - panic!("Tried to convert a recursive tag union extension to an error, but the tag union extension had the ErrorType of {:?}", other) + panic!("Tried to convert a recursive tag union extension to an error, but the tag union extension had the ErrorType of {other:?}") } } } diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index 00eb2b0098d..a8d69888402 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -55,11 +55,11 @@ impl fmt::Debug for RecordField { use RecordField::*; match self { - Optional(typ) => write!(f, "Optional({:?})", typ), - Required(typ) => write!(f, "Required({:?})", typ), - Demanded(typ) => write!(f, "Demanded({:?})", typ), - RigidRequired(typ) => write!(f, "RigidRequired({:?})", typ), - RigidOptional(typ) => write!(f, "RigidOptional({:?})", typ), + Optional(typ) => write!(f, "Optional({typ:?})"), + Required(typ) => write!(f, "Required({typ:?})"), + Demanded(typ) => write!(f, "Demanded({typ:?})"), + RigidRequired(typ) => write!(f, "RigidRequired({typ:?})"), + RigidOptional(typ) => write!(f, "RigidOptional({typ:?})"), } } } @@ -1949,10 +1949,10 @@ fn write_tags<'a>( let mut it = tags.peekable(); while let Some((label, arguments)) = it.next() { - write!(f, "{:?}", label)?; + write!(f, "{label:?}")?; for argument in arguments { - write!(f, " {:?}", argument)?; + write!(f, " {argument:?}")?; } if it.peek().is_some() { @@ -1976,23 +1976,23 @@ impl fmt::Debug for Type { write!(f, ", ")?; } - write!(f, "{:?}", arg)?; + write!(f, "{arg:?}")?; } - write!(f, " |{:?}|", closure)?; + write!(f, " |{closure:?}|")?; write!(f, " -> ")?; ret.fmt(f)?; write!(f, ")") } - Type::Variable(var) => write!(f, "<{:?}>", var), + Type::Variable(var) => write!(f, "<{var:?}>"), Type::Apply(symbol, args, _) => { - write!(f, "({:?}", symbol)?; + write!(f, "({symbol:?}")?; for arg in args { - write!(f, " {:?}", arg)?; + write!(f, " {arg:?}")?; } write!(f, ")") @@ -2004,10 +2004,10 @@ impl fmt::Debug for Type { lambda_set_variables, infer_ext_in_output_types, }) => { - write!(f, "(DelayedAlias {:?}", symbol)?; + write!(f, "(DelayedAlias {symbol:?}")?; for arg in type_arguments { - write!(f, " {:?}", arg)?; + write!(f, " {arg:?}")?; } for (lambda_set, greek_letter) in @@ -2017,7 +2017,7 @@ impl fmt::Debug for Type { } for (i, infer_ext) in infer_ext_in_output_types.iter().enumerate() { - write!(f, " `{}@{:?}", i, infer_ext)?; + write!(f, " `{i}@{infer_ext:?}")?; } write!(f, ")")?; @@ -2032,12 +2032,12 @@ impl fmt::Debug for Type { actual: _actual, .. } => { - write!(f, "(Alias {:?}", symbol)?; + write!(f, "(Alias {symbol:?}")?; for arg in type_arguments { write!(f, " {:?}", &arg.typ)?; if let Some(abs) = &arg.opt_abilities { - write!(f, ":{:?}", abs)?; + write!(f, ":{abs:?}")?; } } @@ -2048,7 +2048,7 @@ impl fmt::Debug for Type { } // Sometimes it's useful to see the expansion of the alias - write!(f, "[ but actually {:?} ]", _actual)?; + write!(f, "[ but actually {_actual:?} ]")?; write!(f, ")")?; @@ -2059,10 +2059,10 @@ impl fmt::Debug for Type { type_arguments: arguments, .. } => { - write!(f, "HostExposedAlias {:?}", name)?; + write!(f, "HostExposedAlias {name:?}")?; for arg in arguments { - write!(f, " {:?}", arg)?; + write!(f, " {arg:?}")?; } // Sometimes it's useful to see the expansion of the alias @@ -2082,13 +2082,11 @@ impl fmt::Debug for Type { for (label, field_type) in fields { match field_type { RecordField::Optional(_) | RecordField::RigidOptional(_) => { - write!(f, "{:?} ? {:?}", label, field_type)? + write!(f, "{label:?} ? {field_type:?}")? } RecordField::Required(_) | RecordField::Demanded(_) - | RecordField::RigidRequired(_) => { - write!(f, "{:?} : {:?}", label, field_type)? - } + | RecordField::RigidRequired(_) => write!(f, "{label:?} : {field_type:?}")?, } if any_written_yet { @@ -2129,7 +2127,7 @@ impl fmt::Debug for Type { let mut any_written_yet = false; for (_, field_type) in elems.iter() { - write!(f, "{:?}", field_type)?; + write!(f, "{field_type:?}")?; if any_written_yet { write!(f, ", ")?; @@ -2179,7 +2177,7 @@ impl fmt::Debug for Type { } Type::FunctionOrTagUnion(tag_name, _, ext) => { write!(f, "[")?; - write!(f, "{:?}", tag_name)?; + write!(f, "{tag_name:?}")?; write!(f, "]")?; match ext { @@ -2204,9 +2202,9 @@ impl fmt::Debug for Type { } => { write!(f, "ClosureTag(")?; - write!(f, "{:?}, ", name)?; + write!(f, "{name:?}, ")?; for capture in captures { - write!(f, "{:?}, ", capture)?; + write!(f, "{capture:?}, ")?; } write!(f, ")") @@ -2229,13 +2227,13 @@ impl fmt::Debug for Type { } }?; - write!(f, " as <{:?}>", rec) + write!(f, " as <{rec:?}>") } Type::RangedNumber(range_vars) => { - write!(f, "Ranged({:?})", range_vars) + write!(f, "Ranged({range_vars:?})") } Type::UnspecializedLambdaSet { unspecialized } => { - write!(f, "{:?}", unspecialized) + write!(f, "{unspecialized:?}") } } } @@ -4005,7 +4003,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens: buf.push('('); } buf.push_str(name.as_str()); - write!(buf, "has {:?}", symbol).unwrap(); + write!(buf, "has {symbol:?}").unwrap(); if write_parens { buf.push(')'); } @@ -4016,7 +4014,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens: if write_parens { buf.push('('); } - write!(buf, "{:?}", symbol).unwrap(); + write!(buf, "{symbol:?}").unwrap(); for arg in arguments { buf.push(' '); @@ -4061,7 +4059,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens: if write_parens { buf.push('('); } - write!(buf, "{:?}", symbol).unwrap(); + write!(buf, "{symbol:?}").unwrap(); for arg in arguments { buf.push(' '); @@ -4146,7 +4144,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens: let mut it = tags.into_iter().peekable(); while let Some((tag, args)) = it.next() { - write!(buf, "{:?}", tag).unwrap(); + write!(buf, "{tag:?}").unwrap(); for arg in args { buf.push(' '); write_debug_error_type_help(arg, buf, Parens::InTypeParam); @@ -4165,7 +4163,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens: let mut it = tags.into_iter().peekable(); while let Some((tag, args)) = it.next() { - write!(buf, "{:?}", tag).unwrap(); + write!(buf, "{tag:?}").unwrap(); for arg in args { buf.push(' '); write_debug_error_type_help(arg, buf, Parens::Unnecessary); diff --git a/crates/compiler/uitest/src/mono.rs b/crates/compiler/uitest/src/mono.rs index 423197c0384..06f4f421965 100644 --- a/crates/compiler/uitest/src/mono.rs +++ b/crates/compiler/uitest/src/mono.rs @@ -60,10 +60,10 @@ pub fn write_compiled_ir<'a>( Err(LoadMonomorphizedError::LoadingProblem(roc_load::LoadingProblem::FormattedReport( report, ))) => { - println!("{}", report); + println!("{report}"); panic!(); } - Err(e) => panic!("{:?}", e), + Err(e) => panic!("{e:?}"), }; use roc_load::MonomorphizedModule; @@ -146,9 +146,9 @@ fn write_procedures<'a>( let mut procs = procs_strings.iter().peekable(); while let Some(proc) = procs.next() { if procs.peek().is_some() { - writeln!(writer, "{}", proc)?; + writeln!(writer, "{proc}")?; } else { - write!(writer, "{}", proc)?; + write!(writer, "{proc}")?; } } diff --git a/crates/compiler/uitest/src/uitest.rs b/crates/compiler/uitest/src/uitest.rs index b9fb9c8dca6..3ed55f6bd25 100644 --- a/crates/compiler/uitest/src/uitest.rs +++ b/crates/compiler/uitest/src/uitest.rs @@ -337,7 +337,7 @@ fn assemble_query_output( for (module, source) in other_modules.iter() { writeln!(writer, "## module {module}")?; - writeln!(writer, "{}\n", source)?; + writeln!(writer, "{source}\n")?; } if !other_modules.is_empty() { diff --git a/crates/compiler/uitest/tests/ability/specialize/set_eq_issue_4761.txt b/crates/compiler/uitest/tests/ability/specialize/set_eq_issue_4761.txt index 1387893fab3..b8fa24db29a 100644 --- a/crates/compiler/uitest/tests/ability/specialize/set_eq_issue_4761.txt +++ b/crates/compiler/uitest/tests/ability/specialize/set_eq_issue_4761.txt @@ -8,5 +8,5 @@ main = s2 = Set.empty {} Bool.isEq s1 s1 && Bool.isEq s2 s2 -# ^^^^^^^^^ Set#Bool.isEq(17): Set Str, Set Str -[[Set.isEq(17)]]-> Bool -# ^^^^^^^^^ Set#Bool.isEq(17): Set U8, Set U8 -[[Set.isEq(17)]]-> Bool +# ^^^^^^^^^ Set#Bool.isEq(20): Set Str, Set Str -[[Set.isEq(20)]]-> Bool +# ^^^^^^^^^ Set#Bool.isEq(20): Set U8, Set U8 -[[Set.isEq(20)]]-> Bool diff --git a/crates/compiler/unify/src/unify.rs b/crates/compiler/unify/src/unify.rs index 383197a8056..37b991cb7b1 100644 --- a/crates/compiler/unify/src/unify.rs +++ b/crates/compiler/unify/src/unify.rs @@ -1853,8 +1853,7 @@ fn unify_unspecialized_lambdas( debug_assert!( is_sorted_unspecialized_lamba_set_list(env.subs, &merged_uls), - "merging of unspecialized lambda sets does not preserve sort! {:?}", - merged_uls + "merging of unspecialized lambda sets does not preserve sort! {merged_uls:?}" ); Ok(( diff --git a/crates/docs/src/lib.rs b/crates/docs/src/lib.rs index 756c23a7afa..ff911aa92aa 100644 --- a/crates/docs/src/lib.rs +++ b/crates/docs/src/lib.rs @@ -151,10 +151,7 @@ pub fn generate_docs_html(root_file: PathBuf) { ); fs::write(build_dir.join("index.html"), rendered_package).unwrap_or_else(|error| { - panic!( - "Attempted to write index.html but failed with this error: {}", - error - ) + panic!("Attempted to write index.html but failed with this error: {error}") }); } @@ -470,10 +467,10 @@ pub fn load_module_for_docs(filename: PathBuf) -> LoadedModule { ) { Ok(loaded) => loaded, Err(LoadingProblem::FormattedReport(report)) => { - eprintln!("{}", report); + eprintln!("{report}"); std::process::exit(1); } - Err(e) => panic!("{:?}", e), + Err(e) => panic!("{e:?}"), } } @@ -796,8 +793,7 @@ fn doc_url<'a>( Err(_) => { // TODO return Err here panic!( - "Tried to generate an automatic link in docs for symbol `{}`, but that symbol was not in scope in this module.", - ident + "Tried to generate an automatic link in docs for symbol `{ident}`, but that symbol was not in scope in this module." ); } } @@ -819,8 +815,7 @@ fn doc_url<'a>( else if !all_exposed_symbols.contains(&symbol) { // TODO return Err here panic!( - "Tried to generate an automatic link in docs for `{}.{}`, but `{}` does not expose `{}`.", - module_name, ident, module_name, ident); + "Tried to generate an automatic link in docs for `{module_name}.{ident}`, but `{module_name}` does not expose `{ident}`."); } // This is a valid symbol for this dependency, @@ -831,7 +826,7 @@ fn doc_url<'a>( } None => { // TODO return Err here - panic!("Tried to generate a doc link for `{}.{}` but the `{}` module was not imported!", module_name, ident, module_name); + panic!("Tried to generate a doc link for `{module_name}.{ident}` but the `{module_name}` module was not imported!"); } } } @@ -847,7 +842,7 @@ fn doc_url<'a>( DocUrl { url, - title: format!("Docs for {}.{}", module_name, ident), + title: format!("Docs for {module_name}.{ident}"), } } @@ -935,8 +930,7 @@ fn markdown_to_html( for event in parser { match event { Event::Code(code_str) => { - let inline_code = - pulldown_cmark::CowStr::from(format!("{}", code_str)); + let inline_code = pulldown_cmark::CowStr::from(format!("{code_str}")); docs_parser.push(pulldown_cmark::Event::Html(inline_code)); } Event::End(Link(LinkType::ShortcutUnknown, ref _url, ref _title)) => { diff --git a/crates/editor/src/editor/code_lines.rs b/crates/editor/src/editor/code_lines.rs index 521686e6e07..38e6aac66d3 100644 --- a/crates/editor/src/editor/code_lines.rs +++ b/crates/editor/src/editor/code_lines.rs @@ -66,13 +66,13 @@ impl fmt::Display for CodeLines { for row in &self.lines { let row_str = row .chars() - .map(|code_char| format!("{}", code_char)) + .map(|code_char| format!("{code_char}")) .collect::>() .join(" "); let escaped_row_str = row_str.replace('\n', "\\n"); - write!(f, "\n{}", escaped_row_str)?; + write!(f, "\n{escaped_row_str}")?; } writeln!(f, " (code_lines, {:?} lines)", self.lines.len())?; diff --git a/crates/editor/src/editor/ed_error.rs b/crates/editor/src/editor/ed_error.rs index e1409896694..732b7873fc8 100644 --- a/crates/editor/src/editor/ed_error.rs +++ b/crates/editor/src/editor/ed_error.rs @@ -278,7 +278,7 @@ pub enum EdError { pub type EdResult = std::result::Result; pub fn print_err(err: &EdError) { - eprintln!("{}", format!("{}", err).truecolor(255, 0, 0)); + eprintln!("{}", format!("{err}").truecolor(255, 0, 0)); if let Some(backtrace) = ErrorCompat::backtrace(err) { eprintln!("{}", color_backtrace(backtrace)); @@ -286,7 +286,7 @@ pub fn print_err(err: &EdError) { } fn color_backtrace(backtrace: &snafu::Backtrace) -> String { - let backtrace_str = format!("{}", backtrace); + let backtrace_str = format!("{backtrace}"); let backtrace_split = backtrace_str.split('\n'); let irrelevant_src = vec![".cargo", "registry", ".rustup", "rustc"]; @@ -301,10 +301,10 @@ fn color_backtrace(backtrace: &snafu::Backtrace) -> String { } format!("{}\n", line.truecolor(255, 100, 100)) } else { - format!("{}\n", line) + format!("{line}\n") } } else { - format!("{}\n", line) + format!("{line}\n") }; if let Some(prev_line) = prev_line_opt { @@ -328,7 +328,7 @@ fn contains_one_of(main_str: &str, contain_slice: &[&str]) -> bool { impl From for String { fn from(ed_error: EdError) -> Self { - format!("{}", ed_error) + format!("{ed_error}") } } diff --git a/crates/editor/src/editor/grid_node_map.rs b/crates/editor/src/editor/grid_node_map.rs index 3d3c1f239ae..87907276dcc 100644 --- a/crates/editor/src/editor/grid_node_map.rs +++ b/crates/editor/src/editor/grid_node_map.rs @@ -429,11 +429,11 @@ impl fmt::Display for GridNodeMap { for row in &self.lines { let row_str = row .iter() - .map(|mark_node_id| format!(" {} ", mark_node_id)) + .map(|mark_node_id| format!(" {mark_node_id} ")) .collect::>() .join(", "); - writeln!(f, "{}", row_str)?; + writeln!(f, "{row_str}")?; } writeln!(f, "(grid_node_map, {:?} lines)", self.lines.len())?; diff --git a/crates/editor/src/editor/main.rs b/crates/editor/src/editor/main.rs index 3bb28ef4079..9ac83cd0f29 100644 --- a/crates/editor/src/editor/main.rs +++ b/crates/editor/src/editor/main.rs @@ -92,7 +92,7 @@ fn run_event_loop(project_path_opt: Option<&Path>) -> Result<(), Box> ) }) .unwrap_or_else(|err| { - panic!("Failed to request device: `{}`", err); + panic!("Failed to request device: `{err}`"); }) .await }); @@ -125,7 +125,7 @@ fn run_event_loop(project_path_opt: Option<&Path>) -> Result<(), Box> let code_arena = Bump::new(); let (file_path_buf, code_str) = read_main_roc_file(project_path_opt); - println!("Loading file {:?}...", file_path_buf); + println!("Loading file {file_path_buf:?}..."); let file_path = Path::new(&file_path_buf); let loaded_module = load_module( @@ -237,7 +237,7 @@ fn run_event_loop(project_path_opt: Option<&Path>) -> Result<(), Box> if let Err(e) = input_outcome_res { print_err(&e) } else if let Ok(InputOutcome::Ignored) = input_outcome_res { - println!("\nInput '{}' ignored!", ch); + println!("\nInput '{ch}' ignored!"); } else { window.request_redraw() } @@ -507,10 +507,7 @@ fn read_main_roc_file(project_path_opt: Option<&Path>) -> (PathBuf, String) { let dir_items = ls(project_path, &ls_config) .unwrap_or_else(|err| { - panic!( - "Failed to list items in project directory; error: {:?}", - err - ) + panic!("Failed to list items in project directory; error: {err:?}") }) .items; @@ -574,15 +571,13 @@ fn init_new_roc_project(project_dir_path: &Path) -> (PathBuf, String) { fn create_roc_file_if_not_exists(project_dir_path: &Path, roc_file_path: &Path) -> String { if !roc_file_path.exists() { let mut roc_file = File::create(roc_file_path).unwrap_or_else(|err| { - panic!("No roc file path was passed to the editor, so I wanted to create a new roc project with the file {:?}, but it failed: {}", roc_file_path, err) + panic!("No roc file path was passed to the editor, so I wanted to create a new roc project with the file {roc_file_path:?}, but it failed: {err}") }); - write!(roc_file, "{}", HELLO_WORLD).unwrap_or_else(|err| { + write!(roc_file, "{HELLO_WORLD}").unwrap_or_else(|err| { panic!( - r#"No roc file path was passed to the editor, so I created a new roc project with the file {:?} - I wanted to write roc hello world to that file, but it failed: {:?}"#, - roc_file_path, - err + r#"No roc file path was passed to the editor, so I created a new roc project with the file {roc_file_path:?} + I wanted to write roc hello world to that file, but it failed: {err:?}"# ) }); @@ -590,8 +585,7 @@ fn create_roc_file_if_not_exists(project_dir_path: &Path, roc_file_path: &Path) } else { std::fs::read_to_string(roc_file_path).unwrap_or_else(|err| { panic!( - "I detected an existing {:?} inside {:?}, but I failed to read from it: {}", - roc_file_path, project_dir_path, err + "I detected an existing {roc_file_path:?} inside {project_dir_path:?}, but I failed to read from it: {err}" ) }) } @@ -613,10 +607,7 @@ fn copy_roc_platform_if_not_exists( } else if !project_platform_path.exists() { copy(orig_platform_path, project_dir_path, &CopyOptions::new()).unwrap_or_else(|err|{ panic!(r#"No roc file path was passed to the editor, so I wanted to create a new roc project and roc projects require a platform, - I tried to copy the platform at {:?} to {:?} but it failed: {}"#, - orig_platform_path, - project_platform_path, - err + I tried to copy the platform at {orig_platform_path:?} to {project_platform_path:?} but it failed: {err}"# ) }); } diff --git a/crates/editor/src/editor/mvc/ed_model.rs b/crates/editor/src/editor/mvc/ed_model.rs index 549ce688e2b..ab7f131aeaa 100644 --- a/crates/editor/src/editor/mvc/ed_model.rs +++ b/crates/editor/src/editor/mvc/ed_model.rs @@ -206,7 +206,7 @@ impl<'a> EdModule<'a> { match parse_res { Ok(ast) => Ok(EdModule { env, ast }), Err(err) => SrcParseSnafu { - syntax_err: format!("{:?}", err), + syntax_err: format!("{err:?}"), } .fail(), } @@ -315,20 +315,17 @@ pub mod test_ed_model { let platform_module_path = platform_dir.join("main.roc"); let mut platform_module_file = File::create(platform_module_path).expect("Failed to create main.roc"); - writeln!(platform_module_file, "{}", PLATFORM_STR).expect("Failed to write to main.roc"); + writeln!(platform_module_file, "{PLATFORM_STR}").expect("Failed to write to main.roc"); let temp_file_path_buf = PathBuf::from([Uuid::new_v4().to_string(), ".roc".to_string()].join("")); let temp_file_full_path = temp_dir.path().join(temp_file_path_buf); let mut file = File::create(temp_file_full_path.clone()).unwrap_or_else(|_| { - panic!( - "Failed to create temporary file for path {:?}", - temp_file_full_path - ) + panic!("Failed to create temporary file for path {temp_file_full_path:?}") }); - writeln!(file, "{}", clean_code_str) - .unwrap_or_else(|_| panic!("Failed to write {:?} to file: {:?}", clean_code_str, file)); + writeln!(file, "{clean_code_str}") + .unwrap_or_else(|_| panic!("Failed to write {clean_code_str:?} to file: {file:?}")); let loaded_module = load_module( &temp_file_full_path, diff --git a/crates/editor/src/editor/mvc/int_update.rs b/crates/editor/src/editor/mvc/int_update.rs index 39cfb119955..7796d237e58 100644 --- a/crates/editor/src/editor/mvc/int_update.rs +++ b/crates/editor/src/editor/mvc/int_update.rs @@ -126,7 +126,7 @@ fn check_parse_res(parse_res: Result) -> EdResult Ok(some_type), Err(parse_err) => StringParseSnafu { - msg: format!("{:?}", parse_err), + msg: format!("{parse_err:?}"), } .fail(), } diff --git a/crates/editor/src/editor/util.rs b/crates/editor/src/editor/util.rs index bea115b11a2..b5266c0ced7 100644 --- a/crates/editor/src/editor/util.rs +++ b/crates/editor/src/editor/util.rs @@ -9,7 +9,7 @@ pub fn map_get<'a, K: ::std::fmt::Debug + std::hash::Hash + std::cmp::Eq, V>( key: &K, ) -> EdResult<&'a V> { let value = hash_map.get(key).context(KeyNotFoundSnafu { - key_str: format!("{:?}", key), + key_str: format!("{key:?}"), })?; Ok(value) @@ -20,8 +20,8 @@ pub fn index_of(elt: T, slice: &[T]) -> EdR .iter() .position(|slice_elt| *slice_elt == elt) .with_context(|| { - let elt_str = format!("{:?}", elt); - let collection_str = format!("{:?}", slice); + let elt_str = format!("{elt:?}"); + let collection_str = format!("{slice:?}"); IndexOfFailedSnafu { elt_str, @@ -56,8 +56,8 @@ pub fn first_last_index_of( if let (Some(first_index), Some(last_index)) = (first_index_opt, last_index_opt) { Ok((first_index, last_index)) } else { - let elt_str = format!("{:?}", elt); - let collection_str = format!("{:?}", slice); + let elt_str = format!("{elt:?}"); + let collection_str = format!("{slice:?}"); IndexOfFailedSnafu { elt_str, diff --git a/crates/editor/src/ui/text/big_text_area.rs b/crates/editor/src/ui/text/big_text_area.rs index fee3119d6e4..178882376ab 100644 --- a/crates/editor/src/ui/text/big_text_area.rs +++ b/crates/editor/src/ui/text/big_text_area.rs @@ -596,7 +596,7 @@ pub mod test_big_sel_text { assert_eq!(expected_post_lines, post_lines); Ok(()) } - Err(e) => Err(format!("{:?}", e)), + Err(e) => Err(format!("{e:?}")), } } diff --git a/crates/editor/src/ui/text/text_buffer.rs b/crates/editor/src/ui/text/text_buffer.rs index cbd6a424912..faa455e1b68 100644 --- a/crates/editor/src/ui/text/text_buffer.rs +++ b/crates/editor/src/ui/text/text_buffer.rs @@ -91,7 +91,7 @@ impl TextBuffer { txt_pos.column <= line_len, OutOfBoundsSnafu { index: txt_pos.column, - collection_name: format!("Line in TextBuffer: {}", line_ref), + collection_name: format!("Line in TextBuffer: {line_ref}"), len: line_len, } ); diff --git a/crates/editor/src/ui/ui_error.rs b/crates/editor/src/ui/ui_error.rs index 9f4d1a96b08..af4f34fd747 100644 --- a/crates/editor/src/ui/ui_error.rs +++ b/crates/editor/src/ui/ui_error.rs @@ -64,6 +64,6 @@ pub type UIResult = std::result::Result; impl From for String { fn from(ui_error: UIError) -> Self { - format!("{}", ui_error) + format!("{ui_error}") } } diff --git a/crates/glue/src/RustGlue.roc b/crates/glue/src/RustGlue.roc index 5e3fcc71089..66ef96d1db4 100644 --- a/crates/glue/src/RustGlue.roc +++ b/crates/glue/src/RustGlue.roc @@ -1,6 +1,17 @@ app "rust-glue" packages { pf: "../platform/main.roc" } - imports [pf.Types.{ Types }, pf.Shape.{ Shape, RocFn }, pf.File.{ File }, pf.TypeId.{ TypeId }] + imports [ + pf.Types.{ Types }, pf.Shape.{ Shape, RocFn }, pf.File.{ File }, pf.TypeId.{ TypeId }, + "../static/Cargo.toml" as rocAppCargoToml : Str, + "../../roc_std/Cargo.toml" as rocStdCargoToml : Str, + "../../roc_std/src/lib.rs" as rocStdLib : Str, + "../../roc_std/src/roc_box.rs" as rocStdBox : Str, + "../../roc_std/src/roc_list.rs" as rocStdList : Str, + "../../roc_std/src/roc_dict.rs" as rocStdDict : Str, + "../../roc_std/src/roc_set.rs" as rocStdSet : Str, + "../../roc_std/src/roc_str.rs" as rocStdStr : Str, + "../../roc_std/src/storage.rs" as rocStdStorage : Str, + ] provides [makeGlue] to pf makeGlue : List Types -> Result (List File) Str @@ -22,9 +33,25 @@ makeGlue = \typesByArch -> typesByArch |> List.map convertTypesToFile - |> List.append { name: "mod.rs", content: modFileContent } + |> List.append { name: "roc_app/src/lib.rs", content: modFileContent } + |> List.concat staticFiles |> Ok +## These are always included, and don't depend on the specifics of the app. +staticFiles : List File +staticFiles = + [ + { name: "roc_app/Cargo.toml", content: rocAppCargoToml }, + { name: "roc_std/Cargo.toml", content: rocStdCargoToml }, + { name: "roc_std/src/lib.rs", content: rocStdLib }, + { name: "roc_std/src/roc_box.rs", content: rocStdBox }, + { name: "roc_std/src/roc_list.rs", content: rocStdList }, + { name: "roc_std/src/roc_dict.rs", content: rocStdDict }, + { name: "roc_std/src/roc_set.rs", content: rocStdSet }, + { name: "roc_std/src/roc_str.rs", content: rocStdStr }, + { name: "roc_std/src/storage.rs", content: rocStdStorage }, + ] + convertTypesToFile : Types -> File convertTypesToFile = \types -> content = @@ -94,7 +121,7 @@ convertTypesToFile = \types -> archStr = archName arch { - name: "\(archStr).rs", + name: "roc_app/src/\(archStr).rs", content: content |> generateEntryPoints types, } @@ -1288,7 +1315,7 @@ generateNullableUnwrapped = \buf, types, tagUnionid, name, nullTag, nonNullTag, FirstTagIsNull -> """ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - enum discriminant_\(name) { + pub enum discriminant_\(name) { \(nullTag) = 0, \(nonNullTag) = 1, } @@ -1297,7 +1324,7 @@ generateNullableUnwrapped = \buf, types, tagUnionid, name, nullTag, nonNullTag, SecondTagIsNull -> """ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - enum discriminant_\(name) { + pub enum discriminant_\(name) { \(nonNullTag) = 0, \(nullTag) = 1, } diff --git a/crates/glue/src/load.rs b/crates/glue/src/load.rs index 466ca17eb88..aa58bf5edd2 100644 --- a/crates/glue/src/load.rs +++ b/crates/glue/src/load.rs @@ -138,7 +138,7 @@ pub fn generate( let files: Result, roc_std::RocStr> = files.into(); let files = files.unwrap_or_else(|err| { - eprintln!("Glue generation failed: {}", err); + eprintln!("Glue generation failed: {err}"); process::exit(1); }); @@ -358,7 +358,7 @@ pub fn load_types( ) .unwrap_or_else(|problem| match problem { LoadingProblem::FormattedReport(report) => { - eprintln!("{}", report); + eprintln!("{report}"); process::exit(1); } diff --git a/crates/glue/src/rust_glue.rs b/crates/glue/src/rust_glue.rs index 5a27609cac0..5d13bb0ff05 100644 --- a/crates/glue/src/rust_glue.rs +++ b/crates/glue/src/rust_glue.rs @@ -999,7 +999,7 @@ pub struct {name} {{ | RocType::RocResult(_, _) | RocType::RecursivePointer { .. } => { owned_ret_type = type_name(*payload_id, types); - borrowed_ret_type = format!("&{}", owned_ret_type); + borrowed_ret_type = format!("&{owned_ret_type}"); owned_ret = "payload".to_string(); borrowed_ret = format!("&{owned_ret}"); payload_args = format!("arg: {owned_ret_type}"); @@ -1119,7 +1119,7 @@ pub struct {name} {{ // TODO revise these - they're all copy/pasted from somewhere else owned_ret_type = type_name(*payload_id, types); - borrowed_ret_type = format!("&{}", owned_ret_type); + borrowed_ret_type = format!("&{owned_ret_type}"); owned_ret = "payload".to_string(); borrowed_ret = format!("&{owned_ret}"); payload_args = format!("arg: {owned_ret_type}"); @@ -1658,7 +1658,7 @@ pub struct {name} {{ }},"# ) } else { - format!("{},", hash_tag) + format!("{hash_tag},") } }, ); @@ -2255,7 +2255,7 @@ pub struct {name} {{ | RocType::TagUnion(_) | RocType::RecursivePointer { .. } => { owned_ret_type = type_name(non_null_payload, types); - borrowed_ret_type = format!("&{}", owned_ret_type); + borrowed_ret_type = format!("&{owned_ret_type}"); payload_args = format!("arg: {owned_ret_type}"); args_to_payload = "arg".to_string(); owned_ret = "payload".to_string(); @@ -2631,7 +2631,7 @@ fn tag_union_struct_help( let label = if is_tag_union_payload { // Tag union payload fields need "f" prefix // because they're numbers - format!("f{}", label) + format!("f{label}") } else { escape_kw(label.to_string()) }; @@ -2671,12 +2671,9 @@ fn tag_union_struct_help( if cannot_derive_copy(types.get_type(payload_id), types) { format!( - "core::mem::ManuallyDrop::new({payload_type_name} {{\n{}\n{INDENT}{INDENT}{INDENT}{INDENT}}})",prefixed_fields) + "core::mem::ManuallyDrop::new({payload_type_name} {{\n{prefixed_fields}\n{INDENT}{INDENT}{INDENT}{INDENT}}})") } else { - format!( - "{payload_type_name} {{\n{}\n{INDENT}{INDENT}{INDENT}{INDENT}}}", - prefixed_fields - ) + format!("{payload_type_name} {{\n{prefixed_fields}\n{INDENT}{INDENT}{INDENT}{INDENT}}}") } } else { "core::mem::ManuallyDrop::new(arg0)".to_string() diff --git a/crates/glue/src/types.rs b/crates/glue/src/types.rs index 7b66d5ed7dd..2933a0d35af 100644 --- a/crates/glue/src/types.rs +++ b/crates/glue/src/types.rs @@ -521,8 +521,7 @@ impl Types { } else { // TODO report this gracefully! panic!( - "Duplicate name detected - {:?} could refer to either {:?} or {:?}", - name, existing_type, typ + "Duplicate name detected - {name:?} could refer to either {existing_type:?} or {typ:?}" ); } } else { @@ -1213,8 +1212,7 @@ impl<'a> Env<'a> { debug_assert!( matches!(types.get_type(type_id), RocType::RecursivePointer(TypeId::PENDING)), - "The TypeId {:?} was registered as a pending recursive pointer, but was not stored in Types as one.", - type_id + "The TypeId {type_id:?} was registered as a pending recursive pointer, but was not stored in Types as one." ); // size and alignment shouldn't change; this is still @@ -1267,7 +1265,7 @@ fn add_function_type<'a>( let args = env.subs.get_subs_slice(*args); let mut arg_type_ids = Vec::with_capacity(args.len()); - let name = format!("RocFunction_{:?}", closure_var); + let name = format!("RocFunction_{closure_var:?}"); let id = env.lambda_set_ids.get(&closure_var).unwrap(); let extern_name = format!("roc__mainForHost_{}_caller", id.0); @@ -1851,7 +1849,7 @@ where getter: getter.clone(), }; - (format!("{}", label), type_id, accessors) + (format!("{label}"), type_id, accessors) }) .collect(); @@ -1865,7 +1863,7 @@ where .map(|(label, field_var, field_layout)| { let type_id = add_type_help(env, field_layout, field_var, None, types); - (format!("{}", label), type_id) + (format!("{label}"), type_id) }) .collect(); @@ -2085,16 +2083,7 @@ fn tag_union_type_from_layout<'a>( }, } } - LayoutRepr::Boxed(elem_layout) => { - let (tag_name, payload_fields) = - single_tag_payload_fields(env, union_tags, subs, layout, &[elem_layout], types); - - RocTagUnion::SingleTagStruct { - name: name.clone(), - tag_name, - payload: payload_fields, - } - } + LayoutRepr::Ptr(_) => unreachable!("Ptr values are never publicly exposed"), LayoutRepr::LambdaSet(lambda_set) => tag_union_type_from_layout( env, opt_name, @@ -2228,7 +2217,13 @@ fn single_tag_payload_fields<'a, 'b>( env.glue_procs_by_layout.get(&layout).is_some(), env.layout_cache .interner - .has_varying_stack_size(in_layout, env.arena) + .has_varying_stack_size(in_layout, env.arena), + "glue_procs_by_layout for {:?} was {:?}, but the layout cache said its has_varying_stack_size was {}", + &layout, + env.glue_procs_by_layout.get(&layout), + env.layout_cache + .interner + .has_varying_stack_size(in_layout, env.arena) ); let (tag_name, payload_vars) = single_tag_payload(union_tags, subs); diff --git a/crates/glue/static/Cargo.toml b/crates/glue/static/Cargo.toml new file mode 100644 index 00000000000..ff900959690 --- /dev/null +++ b/crates/glue/static/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "roc_app" +description = "This was generated by `roc glue`. It provides glue between a specific Roc platform and a Rust host." +version = "1.0.0" +edition = "2021" + +[dependencies] +roc_std = { path = "../roc_std" } diff --git a/crates/glue/tests/fixture-templates/rust/Cargo.toml b/crates/glue/tests/fixture-templates/rust/Cargo.toml index 9697079aa5a..56f73f42ff6 100644 --- a/crates/glue/tests/fixture-templates/rust/Cargo.toml +++ b/crates/glue/tests/fixture-templates/rust/Cargo.toml @@ -27,7 +27,8 @@ name = "host" path = "src/main.rs" [dependencies] -roc_std = { path = "../../../../roc_std" } +roc_std = { path = "test_glue/roc_std" } +roc_app = { path = "test_glue/roc_app" } libc = "0.2" indoc = "1.0.6" diff --git a/crates/glue/tests/fixture-templates/rust/build.rs b/crates/glue/tests/fixture-templates/rust/build.rs index 7dcf8da4e95..6c70d142f01 100644 --- a/crates/glue/tests/fixture-templates/rust/build.rs +++ b/crates/glue/tests/fixture-templates/rust/build.rs @@ -10,6 +10,11 @@ // file with the same name in the fixture-templates/ directory. fn main() { + #[cfg(not(windows))] println!("cargo:rustc-link-lib=dylib=app"); + + #[cfg(windows)] + println!("cargo:rustc-link-lib=dylib=libapp"); + println!("cargo:rustc-link-search=."); } diff --git a/crates/glue/tests/fixtures/advanced-recursive-union/src/lib.rs b/crates/glue/tests/fixtures/advanced-recursive-union/src/lib.rs index e5c6e0c71f8..85af141dcbd 100644 --- a/crates/glue/tests/fixtures/advanced-recursive-union/src/lib.rs +++ b/crates/glue/tests/fixtures/advanced-recursive-union/src/lib.rs @@ -1,14 +1,13 @@ -mod test_glue; +use roc_app; use indoc::indoc; -// use test_glue::Rbt; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(()); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/arguments/src/lib.rs b/crates/glue/tests/fixtures/arguments/src/lib.rs index e20cb959598..9b5d52ac9b3 100644 --- a/crates/glue/tests/fixtures/arguments/src/lib.rs +++ b/crates/glue/tests/fixtures/arguments/src/lib.rs @@ -1,8 +1,8 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { - let answer = test_glue::mainForHost(42i64); + let answer = roc_app::mainForHost(42i64); println!("Answer was: {:?}", answer); // Debug diff --git a/crates/glue/tests/fixtures/basic-record/src/lib.rs b/crates/glue/tests/fixtures/basic-record/src/lib.rs index 4adf2de4c6f..d5cc2ea1e0b 100644 --- a/crates/glue/tests/fixtures/basic-record/src/lib.rs +++ b/crates/glue/tests/fixtures/basic-record/src/lib.rs @@ -1,11 +1,11 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let record = test_glue::mainForHost(); + let record = roc_app::mainForHost(); // Verify that the record has all the expected traits. diff --git a/crates/glue/tests/fixtures/basic-recursive-union/src/lib.rs b/crates/glue/tests/fixtures/basic-recursive-union/src/lib.rs index 299f9c802c6..ff0293bf68d 100644 --- a/crates/glue/tests/fixtures/basic-recursive-union/src/lib.rs +++ b/crates/glue/tests/fixtures/basic-recursive-union/src/lib.rs @@ -1,19 +1,12 @@ -mod test_glue; - use indoc::indoc; -use test_glue::Expr; - -extern "C" { - #[link_name = "roc__mainForHost_1_exposed_generic"] - fn roc_main(_: *mut Expr); -} +use roc_app::{self, Expr}; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(()); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/enumeration/src/lib.rs b/crates/glue/tests/fixtures/enumeration/src/lib.rs index 395b1296433..59704b6be9c 100644 --- a/crates/glue/tests/fixtures/enumeration/src/lib.rs +++ b/crates/glue/tests/fixtures/enumeration/src/lib.rs @@ -1,11 +1,11 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(); + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. @@ -28,8 +28,8 @@ pub extern "C" fn rust_main() -> i32 { println!( "tag_union was: {:?}, Bar is: {:?}, Baz is: {:?}", tag_union, - test_glue::MyEnum::Bar, - test_glue::MyEnum::Baz, + roc_app::MyEnum::Bar, + roc_app::MyEnum::Baz, ); // Debug // Exit code diff --git a/crates/glue/tests/fixtures/list-recursive-union/src/lib.rs b/crates/glue/tests/fixtures/list-recursive-union/src/lib.rs index e494b94e25b..0b598c19d93 100644 --- a/crates/glue/tests/fixtures/list-recursive-union/src/lib.rs +++ b/crates/glue/tests/fixtures/list-recursive-union/src/lib.rs @@ -1,25 +1,14 @@ -mod test_glue; +use roc_app; use indoc::indoc; -use test_glue::Rbt; - -extern "C" { - #[link_name = "roc__mainForHost_1_exposed_generic"] - fn roc_main(_: *mut Rbt); -} +use roc_app::Rbt; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = unsafe { - let mut ret: core::mem::MaybeUninit = core::mem::MaybeUninit::uninit(); - - roc_main(ret.as_mut_ptr()); - - ret.assume_init() - }; + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/multiple-modules/src/lib.rs b/crates/glue/tests/fixtures/multiple-modules/src/lib.rs index 74c44d6f528..e26415e7101 100644 --- a/crates/glue/tests/fixtures/multiple-modules/src/lib.rs +++ b/crates/glue/tests/fixtures/multiple-modules/src/lib.rs @@ -1,13 +1,12 @@ -mod test_glue; - use indoc::indoc; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(); + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/nested-record/src/lib.rs b/crates/glue/tests/fixtures/nested-record/src/lib.rs index 161b103780b..92deb035b25 100644 --- a/crates/glue/tests/fixtures/nested-record/src/lib.rs +++ b/crates/glue/tests/fixtures/nested-record/src/lib.rs @@ -1,10 +1,10 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; - let outer = test_glue::mainForHost(); + let outer = roc_app::mainForHost(); // Verify that `inner` has all the expected traits. { diff --git a/crates/glue/tests/fixtures/nonnullable-unwrapped/src/lib.rs b/crates/glue/tests/fixtures/nonnullable-unwrapped/src/lib.rs index ed7286e47ae..fcae9742c22 100644 --- a/crates/glue/tests/fixtures/nonnullable-unwrapped/src/lib.rs +++ b/crates/glue/tests/fixtures/nonnullable-unwrapped/src/lib.rs @@ -1,8 +1,8 @@ -mod test_glue; +use roc_app; use indoc::indoc; +use roc_app::StrRoseTree; use roc_std::{RocList, RocStr}; -use test_glue::StrRoseTree; extern "C" { #[link_name = "roc__mainForHost_1_exposed_generic"] diff --git a/crates/glue/tests/fixtures/nullable-unwrapped/platform.roc b/crates/glue/tests/fixtures/nullable-unwrapped/platform.roc index c5e54d6f594..13992876507 100644 --- a/crates/glue/tests/fixtures/nullable-unwrapped/platform.roc +++ b/crates/glue/tests/fixtures/nullable-unwrapped/platform.roc @@ -7,5 +7,5 @@ platform "test-platform" StrConsList : [Nil, Cons Str StrConsList] -mainForHost : {} -> StrConsList -mainForHost = \{} -> main +mainForHost : StrConsList +mainForHost = main diff --git a/crates/glue/tests/fixtures/nullable-unwrapped/src/lib.rs b/crates/glue/tests/fixtures/nullable-unwrapped/src/lib.rs index 22ece28b599..f2bf716c7fd 100644 --- a/crates/glue/tests/fixtures/nullable-unwrapped/src/lib.rs +++ b/crates/glue/tests/fixtures/nullable-unwrapped/src/lib.rs @@ -1,19 +1,14 @@ -mod test_glue; +use roc_app; use indoc::indoc; -use test_glue::StrConsList; - -extern "C" { - #[link_name = "roc__mainForHost_1_exposed_generic"] - fn roc_main(_: *mut StrConsList); -} +use roc_app::StrConsList; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/nullable-wrapped/src/lib.rs b/crates/glue/tests/fixtures/nullable-wrapped/src/lib.rs index 8a288f4d304..7cb25db13be 100644 --- a/crates/glue/tests/fixtures/nullable-wrapped/src/lib.rs +++ b/crates/glue/tests/fixtures/nullable-wrapped/src/lib.rs @@ -1,8 +1,8 @@ -mod test_glue; +use roc_app; use indoc::indoc; +use roc_app::StrFingerTree; use roc_std::RocStr; -use test_glue::StrFingerTree; extern "C" { #[link_name = "roc__mainForHost_1_exposed_generic"] @@ -14,7 +14,7 @@ pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(()); // Eq assert!(StrFingerTree::Empty() == StrFingerTree::Empty()); diff --git a/crates/glue/tests/fixtures/option/src/lib.rs b/crates/glue/tests/fixtures/option/src/lib.rs index 697013032c6..3f7ee45b88e 100644 --- a/crates/glue/tests/fixtures/option/src/lib.rs +++ b/crates/glue/tests/fixtures/option/src/lib.rs @@ -1,11 +1,11 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { - let string = test_glue::mainForHost(true); + let string = roc_app::mainForHost(true); println!("Answer was: {:?}", string.unwrap_Some()); // Debug // - let integer = test_glue::mainForHost(false); + let integer = roc_app::mainForHost(false); println!("Answer was: {:?}", integer.discriminant()); // Debug // Exit code diff --git a/crates/glue/tests/fixtures/return-function/src/lib.rs b/crates/glue/tests/fixtures/return-function/src/lib.rs index fad8f088059..396bed19b2e 100644 --- a/crates/glue/tests/fixtures/return-function/src/lib.rs +++ b/crates/glue/tests/fixtures/return-function/src/lib.rs @@ -1,8 +1,8 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { - let record = test_glue::mainForHost(); + let record = roc_app::mainForHost(); let answer1 = record.f.force_thunk(42i64, 1); let answer2 = record.g.force_thunk(42i64, 1); diff --git a/crates/glue/tests/fixtures/rocresult/src/lib.rs b/crates/glue/tests/fixtures/rocresult/src/lib.rs index dad518783b5..e07fbee768e 100644 --- a/crates/glue/tests/fixtures/rocresult/src/lib.rs +++ b/crates/glue/tests/fixtures/rocresult/src/lib.rs @@ -1,11 +1,11 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { - let string = test_glue::mainForHost(true); + let string = roc_app::mainForHost(true); println!("Answer was: {:?}", string); // Debug // - let integer = test_glue::mainForHost(false); + let integer = roc_app::mainForHost(false); println!("Answer was: {:?}", integer); // Debug // Exit code diff --git a/crates/glue/tests/fixtures/single-tag-union/src/lib.rs b/crates/glue/tests/fixtures/single-tag-union/src/lib.rs index a5b8aa1e3e9..cc2e1ae01da 100644 --- a/crates/glue/tests/fixtures/single-tag-union/src/lib.rs +++ b/crates/glue/tests/fixtures/single-tag-union/src/lib.rs @@ -1,14 +1,14 @@ -mod test_glue; +use roc_app; use indoc::indoc; -use test_glue::SingleTagUnion; +use roc_app::SingleTagUnion; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(); + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/union-with-padding/src/lib.rs b/crates/glue/tests/fixtures/union-with-padding/src/lib.rs index 31f209efa99..0c22b185fbe 100644 --- a/crates/glue/tests/fixtures/union-with-padding/src/lib.rs +++ b/crates/glue/tests/fixtures/union-with-padding/src/lib.rs @@ -1,6 +1,6 @@ -mod test_glue; +use roc_app; -use test_glue::NonRecursive; +use roc_app::NonRecursive; extern "C" { #[link_name = "roc__mainForHost_1_exposed_generic"] @@ -12,7 +12,7 @@ pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(()); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/union-without-padding/src/lib.rs b/crates/glue/tests/fixtures/union-without-padding/src/lib.rs index 6e5bbb2a2f1..e4fb9a53047 100644 --- a/crates/glue/tests/fixtures/union-without-padding/src/lib.rs +++ b/crates/glue/tests/fixtures/union-without-padding/src/lib.rs @@ -1,8 +1,8 @@ -mod test_glue; +use roc_app; extern "C" { #[link_name = "roc__mainForHost_1_exposed_generic"] - fn roc_main(_: *mut test_glue::NonRecursive); + fn roc_main(_: *mut roc_app::NonRecursive); } #[no_mangle] @@ -10,7 +10,7 @@ pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(()); // Verify that it has all the expected traits. @@ -23,10 +23,10 @@ pub extern "C" fn rust_main() -> i32 { println!( "tag_union was: {:?}\n`Foo \"small str\"` is: {:?}\n`Bar 123` is: {:?}\n`Baz` is: {:?}\n`Blah 456` is: {:?}", tag_union, - test_glue::NonRecursive::Foo("small str".into()), - test_glue::NonRecursive::Bar(123), - test_glue::NonRecursive::Baz(), - test_glue::NonRecursive::Blah(456), + roc_app::NonRecursive::Foo("small str".into()), + roc_app::NonRecursive::Bar(123), + roc_app::NonRecursive::Baz(), + roc_app::NonRecursive::Blah(456), ); // Debug let mut set = HashSet::new(); diff --git a/crates/glue/tests/test_glue_cli.rs b/crates/glue/tests/test_glue_cli.rs index f61189f033e..6397c28369c 100644 --- a/crates/glue/tests/test_glue_cli.rs +++ b/crates/glue/tests/test_glue_cli.rs @@ -173,7 +173,7 @@ mod glue_cli_run { args: I, ) -> Out { let platform_module_path = platform_dir.join("platform.roc"); - let glue_dir = platform_dir.join("src").join("test_glue"); + let glue_dir = platform_dir.join("test_glue"); let fixture_templates_dir = platform_dir .parent() .unwrap() @@ -220,7 +220,7 @@ mod glue_cli_run { ); } - assert!(glue_out.status.success(), "bad status {:?}", glue_out); + assert!(glue_out.status.success(), "bad status {glue_out:?}"); glue_out } @@ -243,7 +243,7 @@ mod glue_cli_run { ); } - assert!(compile_out.status.success(), "bad status {:?}", compile_out); + assert!(compile_out.status.success(), "bad status {compile_out:?}"); compile_out } diff --git a/crates/highlight/src/lib.rs b/crates/highlight/src/lib.rs index 8929824f4cb..2002b4c8fc6 100644 --- a/crates/highlight/src/lib.rs +++ b/crates/highlight/src/lib.rs @@ -102,7 +102,7 @@ fn push_html_span(mut buf: Vec, curr: &str, class: &str) -> Vec // html escape strings from source code let escaped = html_escape::encode_text(curr); - buf.push(format!("{}", class, escaped)); + buf.push(format!("{escaped}")); buf } @@ -111,7 +111,7 @@ fn push_html(mut buf: Vec, curr: &str) -> Vec { // html escape strings from source code let escaped = html_escape::encode_text(curr); - buf.push(format!("{}", escaped)); + buf.push(format!("{escaped}")); buf } diff --git a/crates/linker/src/elf.rs b/crates/linker/src/elf.rs index de5920b8849..7ddf0dafd41 100644 --- a/crates/linker/src/elf.rs +++ b/crates/linker/src/elf.rs @@ -202,7 +202,7 @@ impl<'a> Surgeries<'a> { println!(); println!("Text Sections"); for sec in text_sections.iter() { - println!("{:+x?}", sec); + println!("{sec:+x?}"); } } @@ -285,8 +285,7 @@ impl<'a> Surgeries<'a> { let offset = inst.next_ip() - op_size as u64 - sec.address() + file_offset; if verbose { println!( - "\tNeed to surgically replace {} bytes at file offset {:+x}", - op_size, offset, + "\tNeed to surgically replace {op_size} bytes at file offset {offset:+x}", ); println!( "\tIts current value is {:+x?}", @@ -373,13 +372,13 @@ pub(crate) fn preprocess_elf( other.sort_by_key(|t| t.1); for (name, vaddr) in other.iter() { - println!("\t{:#08x}: {}", vaddr, name); + println!("\t{vaddr:#08x}: {name}"); } println!("Of which {} are builtins", builtins.len(),); for (name, vaddr) in builtins.iter() { - println!("\t{:#08x}: {}", vaddr, name); + println!("\t{vaddr:#08x}: {name}"); } } @@ -410,8 +409,8 @@ pub(crate) fn preprocess_elf( } }; if verbose { - println!("PLT Address: {:+x}", plt_address); - println!("PLT File Offset: {:+x}", plt_offset); + println!("PLT Address: {plt_address:+x}"); + println!("PLT File Offset: {plt_offset:+x}"); } let app_syms: Vec<_> = exec_obj @@ -467,7 +466,7 @@ pub(crate) fn preprocess_elf( } println!(); - println!("App Function Address Map: {:+x?}", app_func_addresses); + println!("App Function Address Map: {app_func_addresses:+x?}"); } let symbol_and_plt_processing_duration = symbol_and_plt_processing_start.elapsed(); @@ -526,7 +525,7 @@ pub(crate) fn preprocess_elf( if verbose { println!(); - println!("{:+x?}", md); + println!("{md:+x?}"); } let saving_metadata_start = Instant::now(); @@ -593,12 +592,12 @@ fn gen_elf_le( if verbose { println!(); - println!("PH Offset: {:+x}", ph_offset); - println!("PH Entry Size: {}", ph_ent_size); - println!("PH Entry Count: {}", ph_num); - println!("SH Offset: {:+x}", sh_offset); - println!("SH Entry Size: {}", sh_ent_size); - println!("SH Entry Count: {}", sh_num); + println!("PH Offset: {ph_offset:+x}"); + println!("PH Entry Size: {ph_ent_size}"); + println!("PH Entry Count: {ph_num}"); + println!("SH Offset: {sh_offset:+x}"); + println!("SH Entry Size: {sh_ent_size}"); + println!("SH Entry Count: {sh_num}"); } // Copy header and shift everything to enable more program sections. @@ -633,10 +632,7 @@ fn gen_elf_le( user_error!("Executable does not load any data at 0x00000000\nProbably input the wrong file as the executable"); } if verbose { - println!( - "Shifting all data after: {:+x}({:+x})", - physical_shift_start, virtual_shift_start - ); + println!("Shifting all data after: {physical_shift_start:+x}({virtual_shift_start:+x})"); } // Shift all of the program headers. @@ -998,7 +994,7 @@ fn scan_elf_dynamic_deps( let dynstr_data = match dynstr_sec.uncompressed_data() { Ok(data) => data, Err(err) => { - panic!("Failed to load dynstr section: {}", err); + panic!("Failed to load dynstr section: {err}"); } }; @@ -1023,15 +1019,12 @@ fn scan_elf_dynamic_deps( ) .unwrap(), ) as usize; - let c_buf: *const c_char = dynstr_data[dynstr_off..].as_ptr() as *const i8; + let c_buf = dynstr_data[dynstr_off..].as_ptr() as *const c_char; let c_str = unsafe { CStr::from_ptr(c_buf) }.to_str().unwrap(); if Path::new(c_str).file_name() == shared_lib_filename { shared_lib_index = Some(dyn_lib_index); if verbose { - println!( - "Found shared lib in dynamic table at index: {}", - dyn_lib_index - ); + println!("Found shared lib in dynamic table at index: {dyn_lib_index}"); } } } @@ -1260,14 +1253,14 @@ fn surgery_elf_help( if verbose { println!(); - println!("Is Elf64: {}", elf64); - println!("Is Little Endian: {}", litte_endian); - println!("PH Offset: {:+x}", ph_offset); - println!("PH Entry Size: {}", ph_ent_size); - println!("PH Entry Count: {}", ph_num); - println!("SH Offset: {:+x}", sh_offset); - println!("SH Entry Size: {}", sh_ent_size); - println!("SH Entry Count: {}", sh_num); + println!("Is Elf64: {elf64}"); + println!("Is Little Endian: {litte_endian}"); + println!("PH Offset: {ph_offset:+x}"); + println!("PH Entry Size: {ph_ent_size}"); + println!("PH Entry Count: {ph_num}"); + println!("SH Offset: {sh_offset:+x}"); + println!("SH Entry Size: {sh_ent_size}"); + println!("SH Entry Count: {sh_num}"); } // Backup section header table. @@ -1360,8 +1353,8 @@ fn surgery_elf_help( } } if verbose { - println!("Data Relocation Offsets: {:+x?}", symbol_vaddr_map); - println!("Found App Function Symbols: {:+x?}", app_func_vaddr_map); + println!("Data Relocation Offsets: {symbol_vaddr_map:+x?}"); + println!("Found App Function Symbols: {app_func_vaddr_map:+x?}"); } let (new_text_section_offset, new_text_section_vaddr) = text_sections @@ -1427,22 +1420,18 @@ fn surgery_elf_help( if verbose { println!(); println!( - "Processing Relocations for Section: 0x{:+x?} @ {:+x} (virt: {:+x})", - sec, section_offset, section_virtual_offset + "Processing Relocations for Section: 0x{sec:+x?} @ {section_offset:+x} (virt: {section_virtual_offset:+x})" ); } for rel in sec.relocations() { if verbose { - println!("\tFound Relocation: {:+x?}", rel); + println!("\tFound Relocation: {rel:+x?}"); } match rel.1.target() { RelocationTarget::Symbol(index) => { let target_offset = if let Some(target_offset) = symbol_vaddr_map.get(&index) { if verbose { - println!( - "\t\tRelocation targets symbol in app at: {:+x}", - target_offset - ); + println!("\t\tRelocation targets symbol in app at: {target_offset:+x}"); } Some(*target_offset as i64) } else { @@ -1455,8 +1444,7 @@ fn surgery_elf_help( let vaddr = (*address + md.added_byte_count) as i64; if verbose { println!( - "\t\tRelocation targets symbol in host: {} @ {:+x}", - name, vaddr + "\t\tRelocation targets symbol in host: {name} @ {vaddr:+x}" ); } vaddr @@ -1660,7 +1648,7 @@ fn surgery_elf_help( for s in md.surgeries.get(func_name).unwrap_or(&vec![]) { if verbose { - println!("\tPerforming surgery: {:+x?}", s); + println!("\tPerforming surgery: {s:+x?}"); } let surgery_virt_offset = match s.virtual_offset { VirtualOffset::Relative(vs) => (vs + md.added_byte_count) as i64, @@ -1670,7 +1658,7 @@ fn surgery_elf_help( 4 => { let target = (func_virt_offset as i64 - surgery_virt_offset) as i32; if verbose { - println!("\tTarget Jump: {:+x}", target); + println!("\tTarget Jump: {target:+x}"); } let data = target.to_le_bytes(); exec_mmap[(s.file_offset + md.added_byte_count) as usize..][..4] @@ -1679,7 +1667,7 @@ fn surgery_elf_help( 8 => { let target = func_virt_offset as i64 - surgery_virt_offset; if verbose { - println!("\tTarget Jump: {:+x}", target); + println!("\tTarget Jump: {target:+x}"); } let data = target.to_le_bytes(); exec_mmap[(s.file_offset + md.added_byte_count) as usize..][..8] @@ -1700,8 +1688,8 @@ fn surgery_elf_help( let target = (func_virt_offset as i64 - (plt_vaddr as i64 + jmp_inst_len as i64)) as i32; if verbose { - println!("\tPLT: {:+x}, {:+x}", plt_off, plt_vaddr); - println!("\tTarget Jump: {:+x}", target); + println!("\tPLT: {plt_off:+x}, {plt_vaddr:+x}"); + println!("\tTarget Jump: {target:+x}"); } let data = target.to_le_bytes(); exec_mmap[plt_off] = 0xE9; diff --git a/crates/linker/src/generate_dylib/macho.rs b/crates/linker/src/generate_dylib/macho.rs index 697b9187d04..6c5d2bc04d5 100644 --- a/crates/linker/src/generate_dylib/macho.rs +++ b/crates/linker/src/generate_dylib/macho.rs @@ -82,12 +82,10 @@ pub fn create_dylib_macho( if !output.status.success() { match std::str::from_utf8(&output.stderr) { Ok(stderr) => panic!( - "Failed to link dummy shared library - stderr of the `ld` command was:\n{}", - stderr + "Failed to link dummy shared library - stderr of the `ld` command was:\n{stderr}" ), Err(utf8_err) => panic!( - "Failed to link dummy shared library - stderr of the `ld` command was invalid utf8 ({:?})", - utf8_err + "Failed to link dummy shared library - stderr of the `ld` command was invalid utf8 ({utf8_err:?})" ), } } diff --git a/crates/linker/src/lib.rs b/crates/linker/src/lib.rs index b92ae67965c..9f53ac42b41 100644 --- a/crates/linker/src/lib.rs +++ b/crates/linker/src/lib.rs @@ -64,7 +64,7 @@ pub fn supported(link_type: LinkType, target: &Triple) -> bool { pub const PRECOMPILED_HOST_EXT: &str = "rh"; // Short for "roc host" pub fn preprocessed_host_filename(target: &Triple) -> Option { - roc_target::get_target_triple_str(target).map(|x| format!("{}.{}", x, PRECOMPILED_HOST_EXT)) + roc_target::get_target_triple_str(target).map(|x| format!("{x}.{PRECOMPILED_HOST_EXT}")) } fn metadata_file_name(target: &Triple) -> String { @@ -181,9 +181,9 @@ impl ExposedSymbols { let sym = x.as_str(interns); custom_names.extend([ - format!("roc__{}_1_exposed", sym), - format!("roc__{}_1_exposed_generic", sym), - format!("roc__{}_1_exposed_size", sym), + format!("roc__{sym}_1_exposed"), + format!("roc__{sym}_1_exposed_generic"), + format!("roc__{sym}_1_exposed_size"), ]); let exported_closure_types = exposed_to_host @@ -193,9 +193,9 @@ impl ExposedSymbols { for (i, _) in exported_closure_types.enumerate() { custom_names.extend([ - format!("roc__{}_{i}_caller", sym), - format!("roc__{}_{i}_size", sym), - format!("roc__{}_{i}_result_size", sym), + format!("roc__{sym}_{i}_caller"), + format!("roc__{sym}_{i}_size"), + format!("roc__{sym}_{i}_result_size"), ]); } } @@ -227,16 +227,16 @@ impl ExposedSymbols { for sym in &self.top_level_values { custom_names.extend([ - format!("roc__{}_1_exposed", sym), - format!("roc__{}_1_exposed_generic", sym), - format!("roc__{}_size", sym), + format!("roc__{sym}_1_exposed"), + format!("roc__{sym}_1_exposed_generic"), + format!("roc__{sym}_size"), ]); for closure_type in &self.exported_closure_types { custom_names.extend([ - format!("roc__{}_1_{}_caller", sym, closure_type), - format!("roc__{}_1_{}_size", sym, closure_type), - format!("roc__{}_1_{}_result_size", sym, closure_type), + format!("roc__{sym}_1_{closure_type}_caller"), + format!("roc__{sym}_1_{closure_type}_size"), + format!("roc__{sym}_1_{closure_type}_result_size"), ]); } } @@ -421,7 +421,7 @@ fn preprocess( time: bool, ) { if verbose { - println!("Targeting: {}", target); + println!("Targeting: {target}"); } let endianness = target diff --git a/crates/linker/src/macho.rs b/crates/linker/src/macho.rs index 1d481b6de71..ce4e8a24d11 100644 --- a/crates/linker/src/macho.rs +++ b/crates/linker/src/macho.rs @@ -11,7 +11,7 @@ use roc_collections::all::MutMap; use roc_error_macros::internal_error; use serde::{Deserialize, Serialize}; use std::{ - ffi::CStr, + ffi::{c_char, CStr}, io::{BufReader, BufWriter}, mem, path::Path, @@ -192,7 +192,7 @@ impl<'a> Surgeries<'a> { println!(); println!("Text Sections"); for sec in text_sections.iter() { - println!("{:+x?}", sec); + println!("{sec:+x?}"); } } @@ -275,8 +275,7 @@ impl<'a> Surgeries<'a> { let offset = inst.next_ip() - op_size as u64 - sec.address() + file_offset; if verbose { println!( - "\tNeed to surgically replace {} bytes at file offset {:+x}", - op_size, offset, + "\tNeed to surgically replace {op_size} bytes at file offset {offset:+x}", ); println!( "\tIts current value is {:+x?}", @@ -382,8 +381,8 @@ pub(crate) fn preprocess_macho( } }; if verbose { - println!("PLT Address: {:+x}", plt_address); - println!("PLT File Offset: {:+x}", plt_offset); + println!("PLT Address: {plt_address:+x}"); + println!("PLT File Offset: {plt_offset:+x}"); } let app_syms: Vec<_> = exec_obj.symbols().filter(is_roc_undefined).collect(); @@ -499,7 +498,7 @@ pub(crate) fn preprocess_macho( // std::ffi::CStr is actually not a char* under // the hood (!) but rather an array, so to strip // the trailing null bytes we have to use from_ptr. - let c_str = unsafe { CStr::from_ptr(str_bytes.as_ptr() as *const i8) }; + let c_str = unsafe { CStr::from_ptr(str_bytes.as_ptr() as *const c_char) }; Path::new(c_str.to_str().unwrap()) } else { @@ -532,7 +531,7 @@ pub(crate) fn preprocess_macho( } println!(); - println!("App Function Address Map: {:+x?}", app_func_addresses); + println!("App Function Address Map: {app_func_addresses:+x?}"); } let symbol_and_plt_processing_duration = symbol_and_plt_processing_start.elapsed(); @@ -603,7 +602,7 @@ pub(crate) fn preprocess_macho( if verbose { println!(); - println!("{:+x?}", md); + println!("{md:+x?}"); } let saving_metadata_start = Instant::now(); @@ -1101,8 +1100,7 @@ fn gen_macho_le( } cmd => { eprintln!( - "- - - Unrecognized Mach-O command during linker preprocessing: 0x{:x?}", - cmd + "- - - Unrecognized Mach-O command during linker preprocessing: 0x{cmd:x?}" ); // panic!( // "Unrecognized Mach-O command during linker preprocessing: 0x{:x?}", @@ -1237,10 +1235,7 @@ fn surgery_macho_help( let new_rodata_section_vaddr = virt_offset; if verbose { println!(); - println!( - "New Virtual Rodata Section Address: {:+x?}", - new_rodata_section_vaddr - ); + println!("New Virtual Rodata Section Address: {new_rodata_section_vaddr:+x?}"); } // First decide on sections locations and then recode every exact symbol locations. @@ -1320,8 +1315,8 @@ fn surgery_macho_help( } } if verbose { - println!("Data Relocation Offsets: {:+x?}", symbol_vaddr_map); - println!("Found App Function Symbols: {:+x?}", app_func_vaddr_map); + println!("Data Relocation Offsets: {symbol_vaddr_map:+x?}"); + println!("Found App Function Symbols: {app_func_vaddr_map:+x?}"); } // let (new_text_section_offset, new_text_section_vaddr) = text_sections @@ -1356,22 +1351,18 @@ fn surgery_macho_help( if verbose { println!(); println!( - "Processing Relocations for Section: 0x{:+x?} @ {:+x} (virt: {:+x})", - sec, section_offset, section_virtual_offset + "Processing Relocations for Section: 0x{sec:+x?} @ {section_offset:+x} (virt: {section_virtual_offset:+x})" ); } for rel in sec.relocations() { if verbose { - println!("\tFound Relocation: {:+x?}", rel); + println!("\tFound Relocation: {rel:+x?}"); } match rel.1.target() { RelocationTarget::Symbol(index) => { let target_offset = if let Some(target_offset) = symbol_vaddr_map.get(&index) { if verbose { - println!( - "\t\tRelocation targets symbol in app at: {:+x}", - target_offset - ); + println!("\t\tRelocation targets symbol in app at: {target_offset:+x}"); } Some(*target_offset as i64) } else { @@ -1384,8 +1375,7 @@ fn surgery_macho_help( let vaddr = (*address + md.added_byte_count) as i64; if verbose { println!( - "\t\tRelocation targets symbol in host: {} @ {:+x}", - name, vaddr + "\t\tRelocation targets symbol in host: {name} @ {vaddr:+x}" ); } vaddr @@ -1406,10 +1396,9 @@ fn surgery_macho_help( }; if verbose { println!( - "\t\tRelocation base location: {:+x} (virt: {:+x})", - base, virt_base + "\t\tRelocation base location: {base:+x} (virt: {virt_base:+x})" ); - println!("\t\tFinal relocation target offset: {:+x}", target); + println!("\t\tFinal relocation target offset: {target:+x}"); } match rel.1.size() { 32 => { @@ -1560,7 +1549,7 @@ fn surgery_macho_help( for s in md.surgeries.get(func_name).unwrap_or(&vec![]) { if verbose { - println!("\tPerforming surgery: {:+x?}", s); + println!("\tPerforming surgery: {s:+x?}"); } let surgery_virt_offset = match s.virtual_offset { VirtualOffset::Relative(vs) => (vs + md.added_byte_count) as i64, @@ -1570,7 +1559,7 @@ fn surgery_macho_help( 4 => { let target = (func_virt_offset as i64 - surgery_virt_offset) as i32; if verbose { - println!("\tTarget Jump: {:+x}", target); + println!("\tTarget Jump: {target:+x}"); } let data = target.to_le_bytes(); exec_mmap[(s.file_offset + md.added_byte_count) as usize @@ -1580,7 +1569,7 @@ fn surgery_macho_help( 8 => { let target = func_virt_offset as i64 - surgery_virt_offset; if verbose { - println!("\tTarget Jump: {:+x}", target); + println!("\tTarget Jump: {target:+x}"); } let data = target.to_le_bytes(); exec_mmap[(s.file_offset + md.added_byte_count) as usize @@ -1602,8 +1591,8 @@ fn surgery_macho_help( let target = (func_virt_offset as i64 - (plt_vaddr as i64 + jmp_inst_len as i64)) as i32; if verbose { - println!("\tPLT: {:+x}, {:+x}", plt_off, plt_vaddr); - println!("\tTarget Jump: {:+x}", target); + println!("\tPLT: {plt_off:+x}, {plt_vaddr:+x}"); + println!("\tTarget Jump: {target:+x}"); } let data = target.to_le_bytes(); exec_mmap[plt_off] = 0xE9; diff --git a/crates/linker/src/pe.rs b/crates/linker/src/pe.rs index 0332aaf7144..34a9f295147 100644 --- a/crates/linker/src/pe.rs +++ b/crates/linker/src/pe.rs @@ -444,13 +444,13 @@ pub(crate) fn surgery_pe(executable_path: &Path, metadata_path: &Path, roc_app_b "__fixsfti", "__fixunsdfti", "__fixunssfti", + "__lshrti3", "memcpy_decision", ] .contains(&name.as_str()); if *address == 0 && !name.starts_with("roc") && !is_ingested_compiler_rt { eprintln!( - "I don't know the address of the {} function! this may cause segfaults", - name + "I don't know the address of the {name} function! this may cause segfaults" ); } @@ -1717,7 +1717,7 @@ mod test { std::io::stdout().write_all(&output.stdout).unwrap(); std::io::stderr().write_all(&output.stderr).unwrap(); - panic!("zig build-exe failed: {}", command_str); + panic!("zig build-exe failed: {command_str}"); } let preprocessed_host_filename = @@ -1938,7 +1938,7 @@ mod test { std::io::stdout().write_all(&output.stdout).unwrap(); std::io::stderr().write_all(&output.stderr).unwrap(); - panic!("zig build-exe failed: {}", command_str); + panic!("zig build-exe failed: {command_str}"); } let host_bytes = std::fs::read(dir.join("host.exe")).unwrap(); diff --git a/crates/repl_cli/Cargo.toml b/crates/repl_cli/Cargo.toml index 34db9dc2238..147b887b705 100644 --- a/crates/repl_cli/Cargo.toml +++ b/crates/repl_cli/Cargo.toml @@ -30,6 +30,7 @@ roc_reporting = { path = "../reporting" } roc_std = { path = "../roc_std" } roc_target = { path = "../compiler/roc_target" } roc_types = { path = "../compiler/types" } +roc_error_macros = { path = "../error_macros" } bumpalo.workspace = true const_format.workspace = true diff --git a/crates/repl_cli/src/cli_gen.rs b/crates/repl_cli/src/cli_gen.rs index 4edf4ca92df..95236209aba 100644 --- a/crates/repl_cli/src/cli_gen.rs +++ b/crates/repl_cli/src/cli_gen.rs @@ -3,6 +3,7 @@ use inkwell::context::Context; use libloading::Library; use roc_build::link::llvm_module_to_dylib; use roc_collections::all::MutSet; +use roc_error_macros::internal_error; use roc_gen_llvm::llvm::build::LlvmBackendMode; use roc_gen_llvm::llvm::externs::add_default_roc_externs; use roc_gen_llvm::{run_jit_function, run_jit_function_dynamic_type}; @@ -263,7 +264,7 @@ fn mono_module_to_dylib<'a>( if main_fn.verify(true) { function_pass.run_on(&main_fn); } else { - panic!("Main function {} failed LLVM verification in build. Uncomment things nearby to see more details.", main_fn_name); + internal_error!("Main function {main_fn_name} failed LLVM verification in build. Uncomment things nearby to see more details.", ); } module_pass.run_on(env.module); @@ -273,10 +274,7 @@ fn mono_module_to_dylib<'a>( // Verify the module if let Err(errors) = env.module.verify() { - panic!( - "Errors defining module:\n{}\n\nUncomment things nearby to see more details.", - errors.to_string() - ); + internal_error!("Errors defining module:\n{}", errors.to_string()); } llvm_module_to_dylib(env.module, &target, opt_level) diff --git a/crates/repl_cli/src/lib.rs b/crates/repl_cli/src/lib.rs index 336bb2b53ac..2b700750710 100644 --- a/crates/repl_cli/src/lib.rs +++ b/crates/repl_cli/src/lib.rs @@ -32,7 +32,7 @@ pub fn main() -> i32 { // To debug rustyline: // env_logger::init(); // RUST_LOG=rustyline=debug cargo run repl 2> debug.log - print!("{}{}", WELCOME_MESSAGE, SHORT_INSTRUCTIONS); + print!("{WELCOME_MESSAGE}{SHORT_INSTRUCTIONS}"); let mut editor = Editor::::new(); let repl_helper = ReplState::new(); @@ -51,7 +51,7 @@ pub fn main() -> i32 { // If there was no output, don't print a blank line! // (This happens for something like a type annotation.) if !output.is_empty() { - println!("{}", output); + println!("{output}"); } } Err(exit_code) => return exit_code, @@ -70,7 +70,7 @@ pub fn main() -> i32 { return 1; } Err(err) => { - eprintln!("REPL error: {:?}", err); + eprintln!("REPL error: {err:?}"); return 1; } } diff --git a/crates/repl_eval/src/eval.rs b/crates/repl_eval/src/eval.rs index 9b053198cd2..c5372b8ffe7 100644 --- a/crates/repl_eval/src/eval.rs +++ b/crates/repl_eval/src/eval.rs @@ -534,21 +534,10 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>( LayoutRepr::RecursivePointer(_) => { unreachable!("RecursivePointers can only be inside structures") } - LayoutRepr::LambdaSet(_) => OPAQUE_FUNCTION, - LayoutRepr::Boxed(_) => { - let size = env.layout_cache.interner.stack_size(layout); - - app.call_function_dynamic_size(main_fn_name, size as usize, |mem: &A::Memory, addr| { - addr_to_ast( - env, - mem, - addr, - env.layout_cache.get_repr(layout), - WhenRecursive::Unreachable, - env.subs.get_root_key_without_compacting(raw_var), - ) - }) + LayoutRepr::Ptr(_) => { + unreachable!("Ptr will never be visible to users") } + LayoutRepr::LambdaSet(_) => OPAQUE_FUNCTION, }; apply_newtypes(env, newtype_containers.into_bump_slice(), expr) @@ -773,6 +762,34 @@ fn addr_to_ast<'a, M: ReplAppMemory>( when_recursive, ) } + ( + Content::Structure(FlatType::Apply(Symbol::BOX_BOX_TYPE, args)), + LayoutRepr::Union(UnionLayout::NonNullableUnwrapped([inner_layout])), + ) => { + debug_assert_eq!(args.len(), 1); + + let inner_var_index = args.into_iter().next().unwrap(); + let inner_var = env.subs[inner_var_index]; + + let addr_of_inner = mem.deref_usize(addr); + let inner_expr = addr_to_ast( + env, + mem, + addr_of_inner, + env.layout_cache.get_repr(*inner_layout), + WhenRecursive::Unreachable, + inner_var, + ); + + let box_box = env.arena.alloc(Loc::at_zero(Expr::Var { + module_name: "Box", + ident: "box", + })); + let box_box_arg = &*env.arena.alloc(Loc::at_zero(inner_expr)); + let box_box_args = env.arena.alloc([box_box_arg]); + + Expr::Apply(box_box, box_box_args, CalledVia::Space) + } (_, LayoutRepr::Union(UnionLayout::NonNullableUnwrapped(_))) => { let (rec_var, tags) = match unroll_recursion_var(env, raw_content) { Content::Structure(FlatType::RecursiveTagUnion(rec_var, tags, _)) => { @@ -887,36 +904,8 @@ fn addr_to_ast<'a, M: ReplAppMemory>( ) } } - ( - Content::Structure(FlatType::Apply(Symbol::BOX_BOX_TYPE, args)), - LayoutRepr::Boxed(inner_layout), - ) => { - debug_assert_eq!(args.len(), 1); - - let inner_var_index = args.into_iter().next().unwrap(); - let inner_var = env.subs[inner_var_index]; - - let addr_of_inner = mem.deref_usize(addr); - let inner_expr = addr_to_ast( - env, - mem, - addr_of_inner, - env.layout_cache.get_repr(inner_layout), - WhenRecursive::Unreachable, - inner_var, - ); - - let box_box = env.arena.alloc(Loc::at_zero(Expr::Var { - module_name: "Box", - ident: "box", - })); - let box_box_arg = &*env.arena.alloc(Loc::at_zero(inner_expr)); - let box_box_args = env.arena.alloc([box_box_arg]); - - Expr::Apply(box_box, box_box_args, CalledVia::Space) - } - (_, LayoutRepr::Boxed(_)) => { - unreachable!("Box layouts can only be behind a `Box.Box` application") + (_, LayoutRepr::Ptr(_)) => { + unreachable!("Ptr layouts are never available in user code") } }; apply_newtypes(env, newtype_containers.into_bump_slice(), expr) @@ -1468,6 +1457,6 @@ fn number_literal_to_ast(arena: &Bump, num: T) -> Expr<'_> use std::fmt::Write; let mut string = bumpalo::collections::String::with_capacity_in(64, arena); - write!(string, "{}", num).unwrap(); + write!(string, "{num}").unwrap(); Expr::Num(string.into_bump_str()) } diff --git a/crates/repl_expect/src/lib.rs b/crates/repl_expect/src/lib.rs index 1d399b7efec..e08a23d7ad2 100644 --- a/crates/repl_expect/src/lib.rs +++ b/crates/repl_expect/src/lib.rs @@ -93,6 +93,7 @@ pub fn get_values<'a>( mod test { use indoc::indoc; use pretty_assertions::assert_eq; + use roc_error_macros::internal_error; use roc_gen_llvm::{llvm::build::LlvmBackendMode, run_roc::RocCallResult, run_roc_dylib}; use roc_load::{ExecutionMode, LoadConfig, LoadMonomorphizedError, Threading}; use roc_packaging::cache::RocCacheDir; @@ -136,9 +137,9 @@ mod test { ) { Ok(m) => m, Err(LoadMonomorphizedError::ErrorModule(m)) => { - panic!("{:?}", (m.can_problems, m.type_problems)) + internal_error!("{:?}", (m.can_problems, m.type_problems)) } - Err(e) => panic!("{e:?}"), + Err(e) => internal_error!("{e:?}"), }; let mut loaded = loaded; @@ -199,7 +200,7 @@ mod test { let expected = expected.trim_end(); if x != expected { - println!("{}", x); + println!("{x}"); } assert_eq!(expected, x); diff --git a/crates/repl_expect/src/run.rs b/crates/repl_expect/src/run.rs index b200f3a2ecc..413a6e7a161 100644 --- a/crates/repl_expect/src/run.rs +++ b/crates/repl_expect/src/run.rs @@ -327,7 +327,7 @@ fn run_expect_fx<'a, W: std::io::Write>( try_run_jit_function!(lib, expect.name, (), |v: ()| v); if let Err((msg, _)) = result { - panic!("roc panic {}", msg); + internal_error!("roc panic {msg}"); } if sequence.count_failures() > 0 { @@ -386,7 +386,7 @@ fn run_expect_fx<'a, W: std::io::Write>( ExpectSequence::START_OFFSET, )?; } - _ => println!("received signal {}", sig), + _ => println!("received signal {sig}"), } } @@ -518,7 +518,7 @@ fn render_dbg_failure<'a>( let data = expectations.get_mut(&module_id).unwrap(); let current = match data.dbgs.get(&dbg_symbol) { - None => panic!("region {failure_region:?} not in list of dbgs"), + None => internal_error!("region {failure_region:?} not in list of dbgs"), Some(current) => current, }; let failure_region = current.region; @@ -565,7 +565,7 @@ fn render_expect_failure<'a>( let data = expectations.get_mut(&module_id).unwrap(); let current = match data.expectations.get(&failure_region) { - None => panic!("region {failure_region:?} not in list of expects"), + None => internal_error!("region {failure_region:?} not in list of expects"), Some(current) => current, }; @@ -638,7 +638,7 @@ impl ExpectSequence { 0 => std::hint::spin_loop(), 1 => break ChildProcessMsg::Expect, 2 => break ChildProcessMsg::Dbg, - n => panic!("invalid atomic value set by the child: {:#x}", n), + n => internal_error!("invalid atomic value set by the child: {n:#x}"), } } } @@ -815,7 +815,7 @@ pub fn expect_mono_module_to_dylib<'a>( if let Err(errors) = env.module.verify() { let path = std::env::temp_dir().join("test.ll"); env.module.print_to_file(&path).unwrap(); - panic!( + internal_error!( "Errors defining module:\n{}\n\nUncomment things nearby to see more details. IR written to `{:?}`", errors.to_string(), path, ); diff --git a/crates/repl_test/src/cli.rs b/crates/repl_test/src/cli.rs index 8aa71cd9418..314d31ad868 100644 --- a/crates/repl_test/src/cli.rs +++ b/crates/repl_test/src/cli.rs @@ -75,13 +75,12 @@ pub fn repl_eval(input: &str) -> Out { // Remove the initial instructions from the output. - let expected_instructions = format!("{}{}", WELCOME_MESSAGE, SHORT_INSTRUCTIONS); + let expected_instructions = format!("{WELCOME_MESSAGE}{SHORT_INSTRUCTIONS}"); let stdout = String::from_utf8(output.stdout).unwrap(); assert!( stdout.starts_with(&expected_instructions), - "Unexpected repl output: {}", - stdout + "Unexpected repl output: {stdout}" ); let (_, answer) = stdout.split_at(expected_instructions.len()); @@ -101,8 +100,7 @@ pub fn repl_eval(input: &str) -> Out { assert!( answer.ends_with(&expected_after_answer), - "Unexpected repl output after answer: {}", - answer + "Unexpected repl output after answer: {answer}" ); // Use [1..] to trim the leading '\n' diff --git a/crates/repl_wasm/build.rs b/crates/repl_wasm/build.rs index 7cb3d021498..150eb81c4e2 100644 --- a/crates/repl_wasm/build.rs +++ b/crates/repl_wasm/build.rs @@ -14,8 +14,8 @@ const OBJECT_EXTENSION: &str = "obj"; fn main() { println!("cargo:rerun-if-changed=build.rs"); - let source_path = format!("src/{}.c", PLATFORM_FILENAME); - println!("cargo:rerun-if-changed={}", source_path); + let source_path = format!("src/{PLATFORM_FILENAME}.c"); + println!("cargo:rerun-if-changed={source_path}"); // Zig can produce *either* an object containing relocations OR an object containing libc code // But we want both, so we have to compile twice with different flags, then link them @@ -51,9 +51,9 @@ fn main() { // (and thus deleted) before the Zig process is done using it! let _ = builtins_host_tempfile; - assert!(output.status.success(), "{:#?}", output); - assert!(output.stdout.is_empty(), "{:#?}", output); - assert!(output.stderr.is_empty(), "{:#?}", output); + assert!(output.status.success(), "{output:#?}"); + assert!(output.stdout.is_empty(), "{output:#?}"); + assert!(output.stderr.is_empty(), "{output:#?}"); } fn zig_executable() -> String { diff --git a/crates/repl_wasm/src/repl.rs b/crates/repl_wasm/src/repl.rs index 8108e4a9e82..b1703985d6b 100644 --- a/crates/repl_wasm/src/repl.rs +++ b/crates/repl_wasm/src/repl.rs @@ -233,7 +233,7 @@ pub async fn entrypoint_from_js(src: String) -> Result { let (_, main_fn_layout) = match procedures.keys().find(|(s, _)| *s == main_fn_symbol) { Some(layout) => *layout, - None => return Ok(format!(" : {}", expr_type_str)), + None => return Ok(format!(" : {expr_type_str}")), }; let app_module_bytes = { @@ -280,7 +280,7 @@ pub async fn entrypoint_from_js(src: String) -> Result { // Send the compiled binary out to JS and create an executable instance from it js_create_app(&app_module_bytes) .await - .map_err(|js| format!("{:?}", js))?; + .map_err(|js| format!("{js:?}"))?; let mut app = WasmReplApp { arena }; diff --git a/crates/reporting/src/cli.rs b/crates/reporting/src/cli.rs index 22bd527da0e..a31533f35fe 100644 --- a/crates/reporting/src/cli.rs +++ b/crates/reporting/src/cli.rs @@ -133,13 +133,13 @@ pub fn report_problems( problems_reported = warnings.len(); for warning in warnings.iter() { - println!("\n{}\n", warning); + println!("\n{warning}\n"); } } else { problems_reported = errors.len(); for error in errors.iter() { - println!("\n{}\n", error); + println!("\n{error}\n"); } } diff --git a/crates/reporting/src/error/canonicalize.rs b/crates/reporting/src/error/canonicalize.rs index c0be61e751f..faa2556af82 100644 --- a/crates/reporting/src/error/canonicalize.rs +++ b/crates/reporting/src/error/canonicalize.rs @@ -843,7 +843,7 @@ pub fn can_problem<'b>( alloc.reflow("An implementation of "), alloc.symbol_unqualified(member), alloc.reflow(" could not be found in this scope:"), ]), alloc.region(lines.convert_region(region)), - alloc.tip().append(alloc.concat([alloc.reflow("consider adding a value of name "), alloc.symbol_unqualified(member), alloc.reflow(" in this scope, or using another variable that implements this ability member, like "), alloc.type_str(&format!("{{ {}: my{} }}", member_str, member_str))])) + alloc.tip().append(alloc.concat([alloc.reflow("consider adding a value of name "), alloc.symbol_unqualified(member), alloc.reflow(" in this scope, or using another variable that implements this ability member, like "), alloc.type_str(&format!("{{ {member_str}: my{member_str} }}"))])) ]); title = IMPLEMENTATION_NOT_FOUND.to_string(); } diff --git a/crates/reporting/src/error/expect.rs b/crates/reporting/src/error/expect.rs index 4da494eece3..8b5eaa1b20a 100644 --- a/crates/reporting/src/error/expect.rs +++ b/crates/reporting/src/error/expect.rs @@ -167,7 +167,7 @@ impl<'a> Renderer<'a> { &crate::report::DEFAULT_PALETTE, ); - write!(writer, "{}", buf) + write!(writer, "{buf}") } #[allow(clippy::too_many_arguments)] @@ -238,6 +238,6 @@ impl<'a> Renderer<'a> { &crate::report::DEFAULT_PALETTE, ); - write!(writer, "{}", buf) + write!(writer, "{buf}") } } diff --git a/crates/reporting/src/error/parse.rs b/crates/reporting/src/error/parse.rs index abf66192240..3d1cba1403a 100644 --- a/crates/reporting/src/error/parse.rs +++ b/crates/reporting/src/error/parse.rs @@ -3469,7 +3469,7 @@ fn to_provides_report<'a>( } } - EProvides::Provides(pos) => { + EProvides::Provides(pos) | EProvides::IndentProvides(pos) => { let surroundings = Region::new(start, pos); let region = LineColumnRegion::from_pos(lines.convert_pos(pos)); diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index ea2b56d9d82..44df8197316 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -1047,7 +1047,7 @@ fn to_expr_report<'b>( region, Some(expr_region), alloc.reflow("This list contains elements with different types:"), - alloc.string(format!("Its {} element is", ith)), + alloc.string(format!("Its {ith} element is")), alloc.reflow(prev_elems_msg), Some(alloc.reflow("Every element in a list must have the same type!")), ) @@ -1180,7 +1180,7 @@ fn to_expr_report<'b>( if arity == 1 { "1 argument".into() } else { - format!("{} arguments", arity) + format!("{arity} arguments") } )), ]), @@ -1227,7 +1227,7 @@ fn to_expr_report<'b>( if n == 1 { "1 argument".into() } else { - format!("{} arguments", n) + format!("{n} arguments") }, arity )), @@ -1251,7 +1251,7 @@ fn to_expr_report<'b>( if n == 1 { "1 argument".into() } else { - format!("{} arguments", n) + format!("{n} arguments") }, arity )), @@ -1865,10 +1865,7 @@ fn format_category<'b>( ), CallResult(None, _) => (this_is, alloc.text(":")), LowLevelOpResult(op) => { - panic!( - "Compiler bug: invalid return type from low-level op {:?}", - op - ); + panic!("Compiler bug: invalid return type from low-level op {op:?}"); } ForeignCall => { panic!("Compiler bug: invalid return type from foreign call",); @@ -4372,8 +4369,8 @@ fn type_problem_to_pretty<'b>( match suggestions.get(0) { None => alloc.nil(), Some(nearest) => { - let typo_str = format!("{}", typo); - let nearest_str = format!("{}", nearest); + let typo_str = format!("{typo}"); + let nearest_str = format!("{nearest}"); let found = alloc.text(typo_str).annotate(Annotation::Typo); let suggestion = alloc.text(nearest_str).annotate(Annotation::TypoSuggestion); @@ -4424,7 +4421,7 @@ fn type_problem_to_pretty<'b>( match suggestions.get(0) { None => alloc.nil(), Some(nearest) => { - let nearest_str = format!("{}", nearest); + let nearest_str = format!("{nearest}"); let found = alloc.text(typo_str).annotate(Annotation::Typo); let suggestion = alloc.text(nearest_str).annotate(Annotation::TypoSuggestion); diff --git a/crates/reporting/src/report.rs b/crates/reporting/src/report.rs index 81fcb990165..504c563635f 100644 --- a/crates/reporting/src/report.rs +++ b/crates/reporting/src/report.rs @@ -438,7 +438,7 @@ impl<'a> RocDocAllocator<'a> { } pub fn wrapped_opaque_name(&'a self, opaque: Symbol) -> DocBuilder<'a, Self, Annotation> { - debug_assert_eq!(opaque.module_id(), self.home, "Opaque wrappings can only be defined in the same module they're defined in, but this one is defined elsewhere: {:?}", opaque); + debug_assert_eq!(opaque.module_id(), self.home, "Opaque wrappings can only be defined in the same module they're defined in, but this one is defined elsewhere: {opaque:?}"); text!(self, "@{}", opaque.as_str(self.interns)).annotate(Annotation::Opaque) } @@ -1612,7 +1612,7 @@ pub fn to_file_problem_report<'b>( } _ => { let error = std::io::Error::from(error); - let formatted = format!("{}", error); + let formatted = format!("{error}"); let doc = alloc.stack([ alloc.reflow(r"I tried to read this file:"), alloc.string(filename).annotate(Annotation::Error).indent(4), diff --git a/crates/reporting/tests/helpers/mod.rs b/crates/reporting/tests/helpers/mod.rs index 187102105ea..a8bea3eeacb 100644 --- a/crates/reporting/tests/helpers/mod.rs +++ b/crates/reporting/tests/helpers/mod.rs @@ -15,7 +15,8 @@ use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds}; use roc_parse::parser::{SourceError, SyntaxError}; use roc_problem::can::Problem; use roc_region::all::Loc; -use roc_solve::solve::{self, Aliases}; +use roc_solve::module::SolveConfig; +use roc_solve::{solve, Aliases}; use roc_solve_problem::TypeError; use roc_types::subs::{Content, Subs, VarStore, Variable}; use roc_types::types::Types; @@ -33,26 +34,24 @@ pub fn infer_expr( problems: &mut Vec, types: Types, constraints: &Constraints, - constraint: &Constraint, + constraint: Constraint, pending_derives: PendingDerives, aliases: &mut Aliases, abilities_store: &mut AbilitiesStore, derived_module: SharedDerivedModule, expr_var: Variable, ) -> (Content, Subs) { - let (solved, _) = solve::run( - ModuleId::ATTR, + let config = SolveConfig { types, constraints, - problems, - subs, - aliases, - constraint, + root_constraint: constraint, + home: ModuleId::ATTR, pending_derives, - abilities_store, - &Default::default(), + exposed_by_module: &Default::default(), derived_module, - ); + }; + + let (solved, _) = solve::run(config, problems, subs, aliases, abilities_store); 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 4d51d7c1907..46702abcace 100644 --- a/crates/reporting/tests/test_reporting.rs +++ b/crates/reporting/tests/test_reporting.rs @@ -86,7 +86,7 @@ mod test_reporting { path.push("snapshots"); path.push("fail"); let kind = if is_expr { "expr" } else { "header" }; - path.push(format!("{}.{}.roc", test_name, kind)); + path.push(format!("{test_name}.{kind}.roc")); std::fs::write(path, src).unwrap(); } @@ -113,14 +113,14 @@ mod test_reporting { // Use a deterministic temporary directory. // We can't have all tests use "tmp" because tests run in parallel, // so append the test name to the tmp path. - let tmp = format!("tmp/{}", subdir); + let tmp = format!("tmp/{subdir}"); let dir = roc_test_utils::TmpDir::new(&tmp); let filename = PathBuf::from("Test.roc"); let file_path = dir.path().join(filename); let full_file_path = file_path.clone(); let mut file = File::create(file_path).unwrap(); - writeln!(file, "{}", module_src).unwrap(); + writeln!(file, "{module_src}").unwrap(); let load_config = LoadConfig { target_info: roc_target::TargetInfo::default_x86_64(), render: RenderTarget::Generic, @@ -218,7 +218,7 @@ mod test_reporting { buf } Err(other) => { - panic!("failed to load: {:?}", other); + panic!("failed to load: {other:?}"); } } } @@ -258,7 +258,7 @@ mod test_reporting { subs.rigid_var(var.value, "*".into()); } - let mut solve_aliases = roc_solve::solve::Aliases::default(); + let mut solve_aliases = roc_solve::Aliases::default(); for (name, alias) in output.aliases { solve_aliases.insert(&mut types, name, alias); @@ -271,7 +271,7 @@ mod test_reporting { &mut unify_problems, types, &constraints, - &constraint, + constraint, // Use `new_report_problem_as` in order to get proper derives. // TODO: remove the non-new reporting test infra. PendingDerives::default(), @@ -389,7 +389,7 @@ mod test_reporting { // convenient to copy-paste the generated message if buf != expected_rendering { for line in buf.split('\n') { - println!(" {}", line); + println!(" {line}"); } } @@ -5938,6 +5938,40 @@ In roc, functions are always written as a lambda, like{} ) } + #[test] + fn missing_provides_in_app_header() { + report_header_problem_as( + indoc!( + r#" + app "broken" + packages { + pf: "https://github.com/roc-lang/basic-cli/releases/download/0.3.2/tE4xS_zLdmmxmHwHih9kHWQ7fsXtJr7W7h3425-eZFk.tar.br", + } + imports [ + pf.Stdout, + ] + + main = + Stdout.line "answer" + "# + ), + indoc!( + r#" + ── WEIRD PROVIDES ──────────────────────────────────────── /code/proj/Main.roc ─ + + I am partway through parsing a header, but I got stuck here: + + 7│ ] + ^ + + I am expecting the `provides` keyword next, like + + provides [Animal, default, tame] + "# + ), + ) + } + #[test] fn platform_requires_rigids() { report_header_problem_as( @@ -13841,4 +13875,44 @@ In roc, functions are always written as a lambda, like{} Tip: It looks like it takes too many arguments. I'm seeing 1 extra. "### ); + + test_report!( + pizza_parens_right, + indoc!( + r#" + 2 |> (Num.sub 3) + "# + ), + @r###" + ── TOO FEW ARGS ────────────────────────────────────────── /code/proj/Main.roc ─ + + The `sub` function expects 2 arguments, but it got only 1: + + 4│ 2 |> (Num.sub 3) + ^^^^^^^ + + Roc does not allow functions to be partially applied. Use a closure to + make partial application explicit. + "### + ); + + test_report!( + pizza_parens_middle, + indoc!( + r#" + 2 |> (Num.sub 3) |> Num.sub 3 + "# + ), + @r###" + ── TOO FEW ARGS ────────────────────────────────────────── /code/proj/Main.roc ─ + + The `sub` function expects 2 arguments, but it got only 1: + + 4│ 2 |> (Num.sub 3) |> Num.sub 3 + ^^^^^^^ + + Roc does not allow functions to be partially applied. Use a closure to + make partial application explicit. + "### + ); } diff --git a/crates/roc_std/Cargo.toml b/crates/roc_std/Cargo.toml index 8c7b41cb9df..8318bdad415 100644 --- a/crates/roc_std/Cargo.toml +++ b/crates/roc_std/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "roc_std" description = "Rust representations of Roc data structures" -readme = "README.md" authors = ["The Roc Contributors"] edition = "2021" @@ -26,4 +25,4 @@ serde = ["dep:serde"] std = [] [package.metadata.cargo-udeps.ignore] -development = ["quickcheck_macros", "serde_json"] \ No newline at end of file +development = ["quickcheck_macros", "serde_json"] diff --git a/crates/roc_std/tests/test_roc_std.rs b/crates/roc_std/tests/test_roc_std.rs index c868acdfb72..6a41552c698 100644 --- a/crates/roc_std/tests/test_roc_std.rs +++ b/crates/roc_std/tests/test_roc_std.rs @@ -39,7 +39,7 @@ pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) { 0 => { let c_str = CStr::from_ptr(c_ptr as *const c_char); let string = c_str.to_str().unwrap(); - panic!("roc_panic during test: {}", string); + panic!("roc_panic during test: {string}"); } _ => todo!(), } @@ -282,16 +282,16 @@ mod test_roc_std { ); let half = RocDec::from_str("0.5").unwrap(); - assert_eq!(format!("{}", half), "0.5"); + assert_eq!(format!("{half}"), "0.5"); let ten = RocDec::from_str("10").unwrap(); - assert_eq!(format!("{}", ten), "10"); + assert_eq!(format!("{ten}"), "10"); let example = RocDec::from_str("1234.5678").unwrap(); - assert_eq!(format!("{}", example), "1234.5678"); + assert_eq!(format!("{example}"), "1234.5678"); let example = RocDec::from_str("1_000.5678").unwrap(); - assert_eq!(format!("{}", example), "1000.5678"); + assert_eq!(format!("{example}"), "1000.5678"); } #[test] diff --git a/crates/utils/command/src/lib.rs b/crates/utils/command/src/lib.rs index 4bdb87fb23d..80b43baf498 100644 --- a/crates/utils/command/src/lib.rs +++ b/crates/utils/command/src/lib.rs @@ -112,12 +112,11 @@ fn check_command_available(command_name: &str) -> bool { cmd.args([command_name]); - let cmd_str = format!("{:?}", cmd); + let cmd_str = format!("{cmd:?}"); let cmd_out = cmd.output().unwrap_or_else(|err| { panic!( - "Failed to execute `{}` to check if {} is available:\n {}", - cmd_str, command_name, err + "Failed to execute `{cmd_str}` to check if {command_name} is available:\n {err}" ) }); diff --git a/crates/utils/error/src/lib.rs b/crates/utils/error/src/lib.rs index 9b63f1307ca..d6b3584512e 100644 --- a/crates/utils/error/src/lib.rs +++ b/crates/utils/error/src/lib.rs @@ -42,7 +42,7 @@ pub fn map_get<'a, K: ::std::fmt::Debug + std::hash::Hash + std::cmp::Eq, V>( key: &K, ) -> UtilResult<&'a V> { let value = hash_map.get(key).context(KeyNotFoundSnafu { - key_str: format!("{:?}", key), + key_str: format!("{key:?}"), })?; Ok(value) @@ -53,8 +53,8 @@ pub fn index_of(elt: T, slice: &[T]) -> Uti .iter() .position(|slice_elt| *slice_elt == elt) .with_context(|| { - let elt_str = format!("{:?}", elt); - let collection_str = format!("{:?}", slice); + let elt_str = format!("{elt:?}"); + let collection_str = format!("{slice:?}"); IndexOfFailedSnafu { elt_str, @@ -115,8 +115,8 @@ pub fn first_last_index_of( if let (Some(first_index), Some(last_index)) = (first_index_opt, last_index_opt) { Ok((first_index, last_index)) } else { - let elt_str = format!("{:?}", elt); - let collection_str = format!("{:?}", slice); + let elt_str = format!("{elt:?}"); + let collection_str = format!("{slice:?}"); IndexOfFailedSnafu { elt_str, diff --git a/crates/valgrind/src/lib.rs b/crates/valgrind/src/lib.rs index a7ca1ee2b33..8633f53176a 100644 --- a/crates/valgrind/src/lib.rs +++ b/crates/valgrind/src/lib.rs @@ -122,10 +122,10 @@ fn valgrind_test_linux(source: &str) { Err(roc_build::program::BuildFileError::LoadingProblem( roc_load::LoadingProblem::FormattedReport(report), )) => { - eprintln!("{}", report); + eprintln!("{report}"); panic!(""); } - Err(e) => panic!("{:?}", e), + Err(e) => panic!("{e:?}"), } drop(temp_dir) @@ -180,7 +180,7 @@ fn run_with_valgrind(binary_path: &std::path::Path) { what: _, xwhat, } = error; - println!("Valgrind Error: {}\n", kind); + println!("Valgrind Error: {kind}\n"); if let Some(ValgrindErrorXWhat { text, @@ -188,14 +188,14 @@ fn run_with_valgrind(binary_path: &std::path::Path) { leakedblocks: _, }) = xwhat { - println!(" {}", text); + println!(" {text}"); } } panic!("Valgrind found memory errors"); } } else { let exit_code = match valgrind_out.status.code() { - Some(code) => format!("exit code {}", code), + Some(code) => format!("exit code {code}"), None => "no exit code".to_string(), }; diff --git a/crates/valgrind/zig-platform/host.zig b/crates/valgrind/zig-platform/host.zig index 6025d07d157..8ba1179e4ff 100644 --- a/crates/valgrind/zig-platform/host.zig +++ b/crates/valgrind/zig-platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const Align = 2 * @alignOf(usize); extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque; extern fn realloc(c_ptr: [*]align(Align) u8, size: usize) callconv(.C) ?*anyopaque; diff --git a/crates/vendor/morphic_lib/src/preprocess.rs b/crates/vendor/morphic_lib/src/preprocess.rs index e4eee2c6f01..bd1f9e21f94 100644 --- a/crates/vendor/morphic_lib/src/preprocess.rs +++ b/crates/vendor/morphic_lib/src/preprocess.rs @@ -160,23 +160,23 @@ impl std::fmt::Display for Error { if let Some(mod_) = &self.mod_ { loc_prefix(f)?; - write!(f, "module {:?}", mod_)?; + write!(f, "module {mod_:?}")?; } if let Some(def) = &self.def { loc_prefix(f)?; match def { DefName::Type(name) => { - write!(f, "named type definition {:?}", name)?; + write!(f, "named type definition {name:?}")?; } DefName::Func(name) => { - write!(f, "function definition {:?}", name)?; + write!(f, "function definition {name:?}")?; } DefName::Const(name) => { - write!(f, "constant definition {:?}", name)?; + write!(f, "constant definition {name:?}")?; } DefName::EntryPoint(name) => { - write!(f, "entry point definition {:?}", name)?; + write!(f, "entry point definition {name:?}")?; } } } @@ -185,13 +185,13 @@ impl std::fmt::Display for Error { loc_prefix(f)?; match binding { BindingLocation::Type(id) => { - write!(f, "definition of type binding {:?}", id)?; + write!(f, "definition of type binding {id:?}")?; } BindingLocation::Value(id) => { - write!(f, "definition of value binding {:?}", id)?; + write!(f, "definition of value binding {id:?}")?; } BindingLocation::Continuation(id) => { - write!(f, "definition of continuation binding {:?}", id)?; + write!(f, "definition of continuation binding {id:?}")?; } } } diff --git a/crates/vendor/morphic_lib/src/render_api_ir.rs b/crates/vendor/morphic_lib/src/render_api_ir.rs index dadbec892d0..f7db922b898 100644 --- a/crates/vendor/morphic_lib/src/render_api_ir.rs +++ b/crates/vendor/morphic_lib/src/render_api_ir.rs @@ -39,7 +39,7 @@ impl RenderContext { .extend((0..self.indent_level * self.spaces_per_level).map(|_| ' ')); self.pending_indent = false; } - write!(&mut self.content, "{}", to_write).expect("writing to string failed"); + write!(&mut self.content, "{to_write}").expect("writing to string failed"); } fn writeln(&mut self, to_write: impl std::fmt::Display) { @@ -146,8 +146,7 @@ fn render_op(builder: &ExprBuilder, ctx: &mut RenderContext, op: &Op) { match op { Op::Arg | Op::ContinuationArg | Op::DeclareContinuation { .. } => { ctx.write(format_args!( - "/* internal error: {:?} should not be rendered as a value */", - op + "/* internal error: {op:?} should not be rendered as a value */" )); } @@ -252,7 +251,7 @@ fn render_op(builder: &ExprBuilder, ctx: &mut RenderContext, op: &Op) { } Op::GetTupleField { field_idx } => { - ctx.write(format_args!("get_tuple_field {}", field_idx)); + ctx.write(format_args!("get_tuple_field {field_idx}")); } Op::MakeUnion { @@ -270,11 +269,11 @@ fn render_op(builder: &ExprBuilder, ctx: &mut RenderContext, op: &Op) { ctx.write(type_ident(*variant_type)); }, ); - ctx.write(format_args!("> {}", variant_idx)); + ctx.write(format_args!("> {variant_idx}")); } Op::UnwrapUnion { variant_idx } => { - ctx.write(format_args!("unwrap_union {}", variant_idx)); + ctx.write(format_args!("unwrap_union {variant_idx}")); } Op::MakeNamed { named_mod, named } => { diff --git a/crates/wasi-libc-sys/build.rs b/crates/wasi-libc-sys/build.rs index 74a2cd6a370..3f3031df098 100644 --- a/crates/wasi-libc-sys/build.rs +++ b/crates/wasi-libc-sys/build.rs @@ -24,7 +24,7 @@ fn main() { "--global-cache-dir", zig_cache_dir.to_str().unwrap(), "src/dummy.c", - &format!("-femit-bin={}/dummy.wasm", out_dir), + &format!("-femit-bin={out_dir}/dummy.wasm"), ]) .output() .unwrap(); diff --git a/crates/wasm_interp/src/instance.rs b/crates/wasm_interp/src/instance.rs index 7040b8757e1..2278a2d2552 100644 --- a/crates/wasm_interp/src/instance.rs +++ b/crates/wasm_interp/src/instance.rs @@ -2,7 +2,7 @@ use bumpalo::{collections::Vec, Bump}; use std::fmt::{self, Write}; use std::iter::{self, once, Iterator}; -use roc_wasm_module::opcodes::OpCode; +use roc_wasm_module::opcodes::{MemoryInstruction, OpCode}; use roc_wasm_module::parse::{Parse, SkipBytes}; use roc_wasm_module::sections::{ImportDesc, MemorySection, SignatureParamsIter}; use roc_wasm_module::{ExportType, WasmModule}; @@ -105,7 +105,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { is_debug_mode: bool, ) -> Result { let module = - WasmModule::preload(arena, module_bytes, false).map_err(|e| format!("{:?}", e))?; + WasmModule::preload(arena, module_bytes, false).map_err(|e| format!("{e:?}"))?; Self::for_module(arena, arena.alloc(module), import_dispatcher, is_debug_mode) } @@ -178,8 +178,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { let actual_type = ValueType::from(value); if actual_type != expected_type { return Err(format!( - "Type mismatch on argument {} of {}. Expected {:?} but got {:?}", - i, fn_name, expected_type, value + "Type mismatch on argument {i} of {fn_name}. Expected {expected_type:?} but got {value:?}" )); } self.value_store.push(value); @@ -261,10 +260,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { ) }) .ok_or_else(|| { - format!( - "I couldn't find a function '{}' in this WebAssembly module", - fn_name - ) + format!("I couldn't find a function '{fn_name}' in this WebAssembly module") })? as usize }; @@ -348,7 +344,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { fn fetch_immediate_u32(&mut self, module: &WasmModule<'a>) -> u32 { let x = u32::parse((), &module.code.bytes, &mut self.program_counter).unwrap(); if let Some(debug_string) = self.debug_string.as_mut() { - write!(debug_string, "{} ", x).unwrap(); + write!(debug_string, "{x} ").unwrap(); } x } @@ -426,7 +422,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { fn write_debug(&mut self, value: T) { if let Some(debug_string) = self.debug_string.as_mut() { - std::write!(debug_string, "{:?} ", value).unwrap(); + std::write!(debug_string, "{value:?} ").unwrap(); } } @@ -512,8 +508,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { if let Some(expected) = expected_signature { assert_eq!( expected, signature_index, - "Indirect function call failed. Expected signature {} but found {}", - expected, signature_index, + "Indirect function call failed. Expected signature {expected} but found {signature_index}", ); } @@ -599,9 +594,9 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { } else { write!(debug_string, ", ").unwrap(); } - write!(debug_string, "{:x?}", arg).unwrap(); + write!(debug_string, "{arg:x?}").unwrap(); } - writeln!(debug_string, "] return_type={:?}", return_type).unwrap(); + writeln!(debug_string, "] return_type={return_type:?}").unwrap(); } } @@ -747,15 +742,13 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { // So far, all compilers seem to be emitting MVP-compatible code. (Rust, Zig, Roc...) assert_eq!( table_index, 0, - "Table index {} not supported at file offset {:#x}. This interpreter only supports Wasm MVP.", - table_index, file_offset + "Table index {table_index} not supported at file offset {file_offset:#x}. This interpreter only supports Wasm MVP." ); // Dereference the function pointer (look up the element index in the function table) let fn_index = module.element.lookup(element_index).unwrap_or_else(|| { panic!( - "Indirect function call failed. There is no function with element index {}", - element_index + "Indirect function call failed. There is no function with element index {element_index}" ) }); @@ -976,6 +969,38 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { self.value_store.push(Value::I32(-1)); } } + MEMORY => { + // the first argument determines exactly which memory operation we have + match MemoryInstruction::try_from(module.code.bytes[self.program_counter]) { + Ok(op) => match op { + MemoryInstruction::MemoryInit => todo!("WASM instruction: memory.init"), + MemoryInstruction::DataDrop => todo!("WASM instruction: data.drop"), + MemoryInstruction::MemoryCopy => { + let size = self.value_store.pop_u32()? as usize; + let source = self.value_store.pop_u32()? as usize; + let destination = self.value_store.pop_u32()? as usize; + + // skip the op byte and an extra two zero bytes. + // in future versions of WebAssembly this byte may be used to index additional memories + self.program_counter += 1 + 2; + + self.memory.copy_within(source..source + size, destination) + } + MemoryInstruction::MemoryFill => { + let size = self.value_store.pop_u32()? as usize; + let byte_value = self.value_store.pop_u32()? as u8; + let destination = self.value_store.pop_u32()? as usize; + + // skip the op byte and an extra zero byte. + // in future versions of WebAssembly this byte may be used to index additional memories + self.program_counter += 1 + 1; + + self.memory[destination..][..size].fill(byte_value); + } + }, + Err(other) => unreachable!("invalid memory instruction {other:?}"), + }; + } I32CONST => { let value = i32::parse((), &module.code.bytes, &mut self.program_counter).unwrap(); self.write_debug(value); @@ -1581,28 +1606,28 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { I32TRUNCSF32 => { let arg = self.value_store.pop_f32()?; if arg < i32::MIN as f32 || arg > i32::MAX as f32 { - panic!("Cannot truncate {} from F32 to I32", arg); + panic!("Cannot truncate {arg} from F32 to I32"); } self.value_store.push(Value::I32(arg as i32)); } I32TRUNCUF32 => { let arg = self.value_store.pop_f32()?; if arg < u32::MIN as f32 || arg > u32::MAX as f32 { - panic!("Cannot truncate {} from F32 to unsigned I32", arg); + panic!("Cannot truncate {arg} from F32 to unsigned I32"); } self.value_store.push(Value::from(arg as u32)); } I32TRUNCSF64 => { let arg = self.value_store.pop_f64()?; if arg < i32::MIN as f64 || arg > i32::MAX as f64 { - panic!("Cannot truncate {} from F64 to I32", arg); + panic!("Cannot truncate {arg} from F64 to I32"); } self.value_store.push(Value::I32(arg as i32)); } I32TRUNCUF64 => { let arg = self.value_store.pop_f64()?; if arg < u32::MIN as f64 || arg > u32::MAX as f64 { - panic!("Cannot truncate {} from F64 to unsigned I32", arg); + panic!("Cannot truncate {arg} from F64 to unsigned I32"); } self.value_store.push(Value::from(arg as u32)); } @@ -1617,28 +1642,28 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { I64TRUNCSF32 => { let arg = self.value_store.pop_f32()?; if arg < i64::MIN as f32 || arg > i64::MAX as f32 { - panic!("Cannot truncate {} from F32 to I64", arg); + panic!("Cannot truncate {arg} from F32 to I64"); } self.value_store.push(Value::I64(arg as i64)); } I64TRUNCUF32 => { let arg = self.value_store.pop_f32()?; if arg < u64::MIN as f32 || arg > u64::MAX as f32 { - panic!("Cannot truncate {} from F32 to unsigned I64", arg); + panic!("Cannot truncate {arg} from F32 to unsigned I64"); } self.value_store.push(Value::from(arg as u64)); } I64TRUNCSF64 => { let arg = self.value_store.pop_f64()?; if arg < i64::MIN as f64 || arg > i64::MAX as f64 { - panic!("Cannot truncate {} from F64 to I64", arg); + panic!("Cannot truncate {arg} from F64 to I64"); } self.value_store.push(Value::I64(arg as i64)); } I64TRUNCUF64 => { let arg = self.value_store.pop_f64()?; if arg < u64::MIN as f64 || arg > u64::MAX as f64 { - panic!("Cannot truncate {} from F64 to unsigned I64", arg); + panic!("Cannot truncate {arg} from F64 to unsigned I64"); } self.value_store.push(Value::from(arg as u64)); } @@ -1707,12 +1732,12 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { if let Some(debug_string) = &self.debug_string { if matches!(op_code, CALL | CALLINDIRECT) { - eprintln!("\n{:06x} {}", file_offset, debug_string); + eprintln!("\n{file_offset:06x} {debug_string}"); } else { // For calls, we print special debug stuff in do_call let base = self.current_frame.locals_start + self.current_frame.locals_count; let slice = self.value_store.get_slice(base); - eprintln!("{:06x} {:17} {:x?}", file_offset, debug_string, slice); + eprintln!("{file_offset:06x} {debug_string:17} {slice:x?}"); } let is_return = op_code == RETURN || (op_code == END && implicit_return); let is_program_end = self.program_counter == 0; @@ -1730,7 +1755,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { #[allow(dead_code)] fn debug_values_and_blocks(&self, label: &str) { - eprintln!("\n========== {} ==========", label); + eprintln!("\n========== {label} =========="); let mut block_str = String::new(); let mut block_iter = self.blocks.iter().enumerate(); @@ -1742,17 +1767,17 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { if *vstack > i { break; } - write!(block_str, "{}:{:?} ", b, ty).unwrap(); + write!(block_str, "{b}:{ty:?} ").unwrap(); block = block_iter.next(); } if !block_str.is_empty() { - eprintln!("--------------- {}", block_str); + eprintln!("--------------- {block_str}"); } }; for (i, v) in self.value_store.iter().enumerate() { print_blocks(i); - eprintln!("{:3} {:x?}", i, v); + eprintln!("{i:3} {v:x?}"); } print_blocks(self.value_store.depth()); @@ -1769,7 +1794,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { /// -------------- fn debug_stack_trace(&self, buffer: &mut String) -> fmt::Result { let divider = "-------------------"; - writeln!(buffer, "{}", divider)?; + writeln!(buffer, "{divider}")?; let frames = self.previous_frames.iter().chain(once(&self.current_frame)); let next_frames = frames.clone().skip(1); @@ -1818,7 +1843,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { .unwrap_or(""); // Function and address match wasm-objdump formatting, for easy copy & find - writeln!(buffer, "func[{}] {}", fn_index, fn_name)?; + writeln!(buffer, "func[{fn_index}] {fn_name}")?; writeln!(buffer, " address {:06x}", execution_addrs.next().unwrap())?; write!(buffer, " args ")?; @@ -1829,7 +1854,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { } else if local_index != 0 { write!(buffer, ", ")?; } - write!(buffer, "{}: {:?}", local_index, value)?; + write!(buffer, "{local_index}: {value:?}")?; } write!(buffer, "\n stack [")?; @@ -1842,10 +1867,10 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> { if i != stack_start { write!(buffer, ", ")?; } - write!(buffer, "{:?}", value)?; + write!(buffer, "{value:?}")?; } writeln!(buffer, "]")?; - writeln!(buffer, "{}", divider)?; + writeln!(buffer, "{divider}")?; } Ok(()) diff --git a/crates/wasm_interp/src/lib.rs b/crates/wasm_interp/src/lib.rs index 09b81f95269..8cc98d3e399 100644 --- a/crates/wasm_interp/src/lib.rs +++ b/crates/wasm_interp/src/lib.rs @@ -53,10 +53,7 @@ impl<'a> ImportDispatcher for DefaultImportDispatcher<'a> { if module_name == wasi::MODULE_NAME { self.wasi.dispatch(function_name, arguments, memory) } else { - panic!( - "DefaultImportDispatcher does not implement {}.{}", - module_name, function_name - ); + panic!("DefaultImportDispatcher does not implement {module_name}.{function_name}"); } } } @@ -76,14 +73,12 @@ impl Error { match self { Error::Type(expected, actual) => { format!( - "ERROR: I found a type mismatch at file offset {:#x}. Expected {:?}, but found {:?}.\n", - file_offset, expected, actual + "ERROR: I found a type mismatch at file offset {file_offset:#x}. Expected {expected:?}, but found {actual:?}.\n" ) } Error::StackEmpty => { format!( - "ERROR: I tried to pop a value from the stack at file offset {:#x}, but it was empty.\n", - file_offset + "ERROR: I tried to pop a value from the stack at file offset {file_offset:#x}, but it was empty.\n" ) } Error::MemoryAccessOutOfBounds(addr, memory_size) => { @@ -93,10 +88,7 @@ impl Error { ) } Error::UnreachableOp => { - format!( - "WebAssembly `unreachable` instruction at file offset {:#x}.\n", - file_offset - ) + format!("WebAssembly `unreachable` instruction at file offset {file_offset:#x}.\n") } } } diff --git a/crates/wasm_interp/src/main.rs b/crates/wasm_interp/src/main.rs index b7b3a5dea1f..86bce0dc903 100644 --- a/crates/wasm_interp/src/main.rs +++ b/crates/wasm_interp/src/main.rs @@ -90,7 +90,7 @@ fn main() -> io::Result<()> { let dispatcher = DefaultImportDispatcher::new(&wasi_argv); let mut inst = Instance::for_module(&arena, &module, dispatcher, is_debug_mode).unwrap_or_else(|e| { - eprintln!("{}", e); + eprintln!("{e}"); process::exit(2); }); @@ -103,14 +103,14 @@ fn main() -> io::Result<()> { match result { Ok(Some(val)) => { if is_hex_format { - println!("{:#x?}", val) + println!("{val:#x?}") } else { - println!("{:?}", val) + println!("{val:?}") } } Ok(None) => {} Err(e) => { - eprintln!("{}", e); + eprintln!("{e}"); process::exit(3); } } diff --git a/crates/wasm_interp/src/tests/mod.rs b/crates/wasm_interp/src/tests/mod.rs index a78e4031ccf..9b83a81b98a 100644 --- a/crates/wasm_interp/src/tests/mod.rs +++ b/crates/wasm_interp/src/tests/mod.rs @@ -85,7 +85,7 @@ where // Dump the generated module to a file (this is mainly for debugging the test itself) if std::env::var("DEBUG_WASM_INTERP_TEST").is_ok() { - let filename = format!("/tmp/{:?}.wasm", op); + let filename = format!("/tmp/{op:?}.wasm"); println!("\nDumping test module to {}\n", &filename); let mut outfile_buf = Vec::new_in(&arena); module.serialize(&mut outfile_buf); diff --git a/crates/wasm_interp/src/tests/test_basics.rs b/crates/wasm_interp/src/tests/test_basics.rs index c0b56cec858..7ce3a11996e 100644 --- a/crates/wasm_interp/src/tests/test_basics.rs +++ b/crates/wasm_interp/src/tests/test_basics.rs @@ -563,7 +563,7 @@ fn test_call_import() { module.serialize(&mut buf); let filename = "/tmp/roc/call-return.wasm"; std::fs::write(filename, buf).unwrap(); - println!("Wrote to {}", filename); + println!("Wrote to {filename}"); } let mut inst = Instance::for_module(&arena, &module, import_dispatcher, true).unwrap(); @@ -631,7 +631,7 @@ fn test_call_return_no_args() { module.serialize(&mut buf); let filename = "/tmp/roc/call-return.wasm"; std::fs::write(filename, buf).unwrap(); - println!("Wrote to {}", filename); + println!("Wrote to {filename}"); } let mut inst = @@ -771,9 +771,9 @@ fn test_call_indirect_help(table_index: u32, elem_index: u32) -> Value { if false { let mut outfile_buf = Vec::new_in(&arena); module.serialize(&mut outfile_buf); - let filename = format!("/tmp/roc/call_indirect_{}_{}.wasm", table_index, elem_index); + let filename = format!("/tmp/roc/call_indirect_{table_index}_{elem_index}.wasm"); std::fs::write(&filename, outfile_buf).unwrap(); - println!("\nWrote to {}\n", filename); + println!("\nWrote to {filename}\n"); } let mut inst = Instance::for_module( diff --git a/crates/wasm_interp/src/tests/test_mem.rs b/crates/wasm_interp/src/tests/test_mem.rs index 731b2b12b97..9d24e0df0d1 100644 --- a/crates/wasm_interp/src/tests/test_mem.rs +++ b/crates/wasm_interp/src/tests/test_mem.rs @@ -49,6 +49,76 @@ fn test_growmemory() { assert_eq!(state.memory.len(), 5 * MemorySection::PAGE_SIZE as usize); } +#[test] +fn test_memory_fill() { + let arena = Bump::new(); + let mut module = WasmModule::new(&arena); + + let pages = 3; + let pc = 0; + module.memory = MemorySection::new(&arena, pages * MemorySection::PAGE_SIZE); + + const SIZE: i32 = 16; + let byte_value = 0xAA; + let destination = 0x4; + + let bytes = [OpCode::MEMORY as u8, 11, 0x0]; + module.code.bytes.extend(bytes); + + let mut state = Instance::new(&arena, pages, pc, [], DefaultImportDispatcher::default()); + + state.value_store.push(Value::I32(destination)); + state.value_store.push(Value::I32(byte_value)); + state.value_store.push(Value::I32(SIZE)); + + // before the instruction, the memory is all zeros + let memory_before = &state.memory[destination as usize..][..SIZE as usize]; + assert_eq!(memory_before, &[0; SIZE as usize]); + + state.execute_next_instruction(&module).unwrap(); + + // after the fill, the same memory range is now all 0xAA bytes + let memory_after = &state.memory[destination as usize..][..SIZE as usize]; + assert_eq!(memory_after, &[byte_value as u8; SIZE as usize]) +} + +#[test] +fn test_memory_copy() { + let arena = Bump::new(); + let mut module = WasmModule::new(&arena); + + let pages = 3; + let pc = 0; + module.memory = MemorySection::new(&arena, pages * MemorySection::PAGE_SIZE); + + const SIZE: i32 = 4; + let source = 0x4; + let destination = 0x8; + + let bytes = [OpCode::MEMORY as u8, 10, 0x0, 0x0]; + module.code.bytes.extend(bytes); + + let mut state = Instance::new(&arena, pages, pc, [], DefaultImportDispatcher::default()); + + state.value_store.push(Value::I32(destination)); + state.value_store.push(Value::I32(source)); + state.value_store.push(Value::I32(SIZE)); + + // fill the source slice with 0xAA bytes + let source_slice = &mut state.memory[source as usize..][..SIZE as usize]; + source_slice.fill(0xAA); + + // before the copy, the destination slice is all 0x00 bytes + let dest_slice = &state.memory[destination as usize..][..SIZE as usize]; + assert_eq!(dest_slice, &[0x00; SIZE as usize]); + + state.execute_next_instruction(&module).unwrap(); + + // after the copy, the destination slice is all 0xAA bytes + let dest_slice = &state.memory[destination as usize..][..SIZE as usize]; + assert_eq!(dest_slice, &[0xAA; SIZE as usize]) +} + fn test_load(load_op: OpCode, ty: ValueType, data: &[u8], addr: u32, offset: u32) -> Value { let arena = Bump::new(); let mut module = WasmModule::new(&arena); diff --git a/crates/wasm_interp/src/value_store.rs b/crates/wasm_interp/src/value_store.rs index 5636a5d0be8..50287827c5e 100644 --- a/crates/wasm_interp/src/value_store.rs +++ b/crates/wasm_interp/src/value_store.rs @@ -158,6 +158,6 @@ mod tests { stack.push(val); } - assert_eq!(format!("{:?}", VALUES), format!("{:?}", stack)); + assert_eq!(format!("{VALUES:?}"), format!("{stack:?}")); } } diff --git a/crates/wasm_interp/src/wasi.rs b/crates/wasm_interp/src/wasi.rs index a8a288de247..5b3d8f285a8 100644 --- a/crates/wasm_interp/src/wasi.rs +++ b/crates/wasm_interp/src/wasi.rs @@ -88,8 +88,25 @@ impl<'a> WasiDispatcher<'a> { success_code } - "environ_get" => todo!("WASI {}({:?})", function_name, arguments), - "environ_sizes_get" => todo!("WASI {}({:?})", function_name, arguments), + "environ_get" => { + // `environ_sizes_get` always reports 0 environment variables + // so we don't have to do anything here. + + success_code + } + "environ_sizes_get" => { + let num_env_ptr = arguments[0].expect_i32().unwrap() as usize; + let size_env_ptr = arguments[1].expect_i32().unwrap() as usize; + + // Calculate the total size required for environment variables + let total_size = 0; + let count = 0; + + write_u32(memory, num_env_ptr, count); + write_u32(memory, size_env_ptr, total_size as u32); + + success_code + } "clock_res_get" => success_code, // this dummy implementation seems to be good enough for some functions "clock_time_get" => success_code, "fd_advise" => todo!("WASI {}({:?})", function_name, arguments), @@ -144,7 +161,7 @@ impl<'a> WasiDispatcher<'a> { if fd < self.files.len() { success_code } else { - println!("WASI warning: file descriptor {} does not exist", fd); + println!("WASI warning: file descriptor {fd} does not exist"); Some(Value::I32(Errno::Badf as i32)) } } @@ -274,8 +291,7 @@ impl<'a> WasiDispatcher<'a> { if negative_length_count > 0 { // Let's see if we ever get this message. If not, we can remove this negative-length stuff. eprintln!( - "WASI DEV INFO: found {} negative-length iovecs.", - negative_length_count + "WASI DEV INFO: found {negative_length_count} negative-length iovecs." ); } @@ -314,7 +330,7 @@ impl<'a> WasiDispatcher<'a> { "sock_recv" => todo!("WASI {}({:?})", function_name, arguments), "sock_send" => todo!("WASI {}({:?})", function_name, arguments), "sock_shutdown" => todo!("WASI {}({:?})", function_name, arguments), - _ => panic!("Unknown WASI function {}({:?})", function_name, arguments), + _ => panic!("Unknown WASI function {function_name}({arguments:?})"), } } } diff --git a/crates/wasm_module/src/lib.rs b/crates/wasm_module/src/lib.rs index 87b1183d6d6..73cbb92f7fd 100644 --- a/crates/wasm_module/src/lib.rs +++ b/crates/wasm_module/src/lib.rs @@ -183,10 +183,7 @@ impl<'a> WasmModule<'a> { module_errors, ) } else { - format!( - "I wasn't able to understand this WebAssembly file.\n{}", - module_errors, - ) + format!("I wasn't able to understand this WebAssembly file.\n{module_errors}",) }; return Err(ParseError { offset: 0, message }); } diff --git a/crates/wasm_module/src/linking.rs b/crates/wasm_module/src/linking.rs index 89235951808..d8432ebd65d 100644 --- a/crates/wasm_module/src/linking.rs +++ b/crates/wasm_module/src/linking.rs @@ -144,7 +144,7 @@ impl Parse<()> for RelocationEntry { Err(ParseError { offset: *cursor, - message: format!("Unknown relocation type 0x{:2x}", type_id_byte), + message: format!("Unknown relocation type 0x{type_id_byte:2x}"), }) } } @@ -491,7 +491,7 @@ impl Parse<()> for SymType { 5 => Ok(Self::Table), x => Err(ParseError { offset, - message: format!("Invalid symbol info type in linking section: {}", x), + message: format!("Invalid symbol info type in linking section: {x}"), }), } } @@ -536,7 +536,7 @@ impl Parse<()> for SubSectionId { 8 => Ok(Self::SymbolTable), x => Err(ParseError { offset, - message: format!("Invalid linking subsection ID {}", x), + message: format!("Invalid linking subsection ID {x}"), }), } } @@ -578,10 +578,7 @@ impl<'a> LinkingSection<'a> { .iter() .position(|sym| sym.name() == Some(target_name)) .ok_or_else(|| { - format!( - "Linking failed! Can't find `{}` in host symbol table", - target_name - ) + format!("Linking failed! Can't find `{target_name}` in host symbol table") }) } @@ -596,7 +593,7 @@ impl<'a> LinkingSection<'a> { _ => false, }) .map(|sym_index| sym_index as u32) - .ok_or_else(|| format!("Can't find fn #{} in host symbol table", fn_index)) + .ok_or_else(|| format!("Can't find fn #{fn_index} in host symbol table")) } pub fn find_and_reindex_imported_fn( @@ -619,10 +616,7 @@ impl<'a> LinkingSection<'a> { }) .map(|sym_index| sym_index as u32) .ok_or_else(|| { - format!( - "Linking failed! Can't find fn #{} in host symbol table", - old_fn_index - ) + format!("Linking failed! Can't find fn #{old_fn_index} in host symbol table") }) } } @@ -650,8 +644,7 @@ impl<'a> Parse<&'a Bump> for LinkingSection<'a> { return Err(ParseError { offset: *cursor, message: format!( - "This file uses version {} of Wasm linking data, but only version {} is supported.", - linking_version, LINKING_VERSION + "This file uses version {linking_version} of Wasm linking data, but only version {LINKING_VERSION} is supported." ), }); } diff --git a/crates/wasm_module/src/opcodes.rs b/crates/wasm_module/src/opcodes.rs index a6058f01bf4..c5245dde831 100644 --- a/crates/wasm_module/src/opcodes.rs +++ b/crates/wasm_module/src/opcodes.rs @@ -50,6 +50,7 @@ pub enum OpCode { I64STORE32 = 0x3e, CURRENTMEMORY = 0x3f, GROWMEMORY = 0x40, + MEMORY = 0xFC, I32CONST = 0x41, I64CONST = 0x42, F32CONST = 0x43, @@ -191,6 +192,29 @@ impl From for OpCode { } } +#[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum MemoryInstruction { + MemoryInit = 8, + DataDrop = 9, + MemoryCopy = 10, + MemoryFill = 11, +} + +impl TryFrom for MemoryInstruction { + type Error = u8; + + fn try_from(value: u8) -> Result { + match value { + 8 => Ok(Self::MemoryInit), + 9 => Ok(Self::DataDrop), + 10 => Ok(Self::MemoryCopy), + 11 => Ok(Self::MemoryFill), + _ => Err(value), + } + } +} + /// The format of the *immediate* operands of an operator /// Immediates appear directly in the byte stream after the opcode, /// rather than being popped off the value stack. These are the possible forms. @@ -204,6 +228,7 @@ enum OpImmediates { Leb64x1, Leb32x2, BrTable, + Memory, } fn immediates_for(op: OpCode) -> Result { @@ -232,6 +257,7 @@ fn immediates_for(op: OpCode) -> Result { | I64STORE32 => Leb32x2, CURRENTMEMORY | GROWMEMORY => Byte1, + MEMORY => Memory, I32CONST => Leb32x1, I64CONST => Leb64x1, @@ -312,6 +338,29 @@ impl SkipBytes for OpCode { u32::skip_bytes(bytes, cursor)?; } } + Memory => { + match MemoryInstruction::try_from(bytes[*cursor + 1]) { + Ok(op) => match op { + MemoryInstruction::MemoryInit => { + // memory.init + todo!("WASM instruction: memory.init") + } + MemoryInstruction::DataDrop => { + // data.drop x + todo!("WASM instruction: data.drop") + } + MemoryInstruction::MemoryCopy => { + // memory.copy + *cursor += 1 + 1 + 2; + } + MemoryInstruction::MemoryFill => { + // memory.fill + *cursor += 1 + 1 + 1; + } + }, + Err(other) => unreachable!("invalid memory instruction {other:?}"), + } + } } Ok(()) } diff --git a/crates/wasm_module/src/sections.rs b/crates/wasm_module/src/sections.rs index 6b6723f8363..99ef4db60ce 100644 --- a/crates/wasm_module/src/sections.rs +++ b/crates/wasm_module/src/sections.rs @@ -628,7 +628,7 @@ impl Parse<()> for RefType { 0x6f => Ok(Self::Extern), _ => Err(ParseError { offset: *cursor - 1, - message: format!("Invalid RefType 0x{:2x}", byte), + message: format!("Invalid RefType 0x{byte:2x}"), }), } } @@ -973,7 +973,7 @@ impl Parse<()> for ConstExpr { } _ => Err(ParseError { offset: *cursor, - message: format!("Unsupported opcode {:?} in constant expression.", opcode), + message: format!("Unsupported opcode {opcode:?} in constant expression."), }), }; @@ -1522,7 +1522,7 @@ impl Parse<()> for DataMode { } else { Err(ParseError { offset: *cursor - 1, - message: format!("Data section: invalid DataMode variant 0x{:x}", variant_id), + message: format!("Data section: invalid DataMode variant 0x{variant_id:x}"), }) } } @@ -1574,7 +1574,7 @@ impl<'a> DataSection<'a> { let mut cursor = 0; for _ in 0..self.count { let mode = - DataMode::parse((), &self.bytes, &mut cursor).map_err(|e| format!("{:?}", e))?; + DataMode::parse((), &self.bytes, &mut cursor).map_err(|e| format!("{e:?}"))?; let start = match mode { DataMode::Active { offset: ConstExpr::I32(addr), @@ -1583,12 +1583,12 @@ impl<'a> DataSection<'a> { continue; } }; - let len32 = u32::parse((), &self.bytes, &mut cursor).map_err(|e| format!("{:?}", e))?; + let len32 = u32::parse((), &self.bytes, &mut cursor).map_err(|e| format!("{e:?}"))?; let len = len32 as usize; let mut target_slice = &mut memory[start..][..len]; target_slice .write(&self.bytes[cursor..][..len]) - .map_err(|e| format!("{:?}", e))?; + .map_err(|e| format!("{e:?}"))?; cursor += len; } Ok(()) @@ -1859,7 +1859,7 @@ impl<'a> Debug for NameSection<'a> { writeln!(f, "NameSection")?; for (index, name) in self.function_names.iter() { - writeln!(f, " {:4}: {}", index, name)?; + writeln!(f, " {index:4}: {name}")?; } Ok(()) diff --git a/default.nix b/default.nix index 5c8e8cbeea9..c4839e99d51 100644 --- a/default.nix +++ b/default.nix @@ -10,91 +10,107 @@ let rustPlatform = pkgs.rustPlatform; + desiredRustVersion = (builtins.fromTOML (builtins.readFile (./rust-toolchain.toml))).toolchain.channel; + actualRustVersion = rustPlatform.rust.rustc; + rustVersionsMatch = pkgs.lib.strings.hasSuffix desiredRustVersion actualRustVersion; + llvmPkgs = pkgs.llvmPackages_13; # nix does not store libs in /usr/lib or /lib nixGlibcPath = if pkgs.stdenv.isLinux then "${pkgs.glibc.out}/lib" else ""; in -rustPlatform.buildRustPackage { - pname = "roc"; - version = "0.0.1"; - src = pkgs.nix-gitignore.gitignoreSource [ ] ./.; + assert pkgs.lib.assertMsg rustVersionsMatch '' + The rust version changed in rust-toolchain.toml but the rev(commit) in nixpkgs.url in flake.nix was not updated. + 1. clone the nixpkgs repo: `git clone --depth 60000 git@github.com:NixOS/nixpkgs.git` + 2. `cd nixpkgs` + 3. `git log --oneline | rg -A 1 "rustc: "` + 4. Copy the short SHA from the line **after** the commit with the message of for example `rustc: 1.67.1 -> 1.68.0` + 5. Find the long SHA by executing `git rev-parse ` + 6. Copy the long SHA + 7. Paste it in place of the old SHA(rev) in flake.nix:inputs:nixpkgs.url + ''; - cargoLock = { - lockFile = ./Cargo.lock; - outputHashes = { - "criterion-0.3.5" = "sha256-+FibPQGiR45g28xCHcM0pMN+C+Q8gO8206Wb5fiTy+k="; - "inkwell-0.1.0" = "sha256-1kpvY3naS33B99nuu5ZYhb7mdddAyG+DkbUl/RG1Ptg="; - "plotters-0.3.1" = "sha256-noy/RSjoEPZZbOJTZw1yxGcX5S+2q/7mxnUrzDyxOFw="; - "rustyline-9.1.1" = "sha256-aqQqz6nSp+Qn44gm3jXmmQUO6/fYTx7iLph2tbA24Bs="; - }; - }; + rustPlatform.buildRustPackage { + pname = "roc"; + version = "0.0.1"; + + src = pkgs.nix-gitignore.gitignoreSource [ ] ./.; - LLVM_SYS_130_PREFIX = "${llvmPkgs.llvm.dev}"; + cargoLock = { + lockFile = ./Cargo.lock; + outputHashes = { + "criterion-0.3.5" = "sha256-+FibPQGiR45g28xCHcM0pMN+C+Q8gO8206Wb5fiTy+k="; + "inkwell-0.1.0" = "sha256-1kpvY3naS33B99nuu5ZYhb7mdddAyG+DkbUl/RG1Ptg="; + "plotters-0.3.1" = "sha256-noy/RSjoEPZZbOJTZw1yxGcX5S+2q/7mxnUrzDyxOFw="; + "rustyline-9.1.1" = "sha256-aqQqz6nSp+Qn44gm3jXmmQUO6/fYTx7iLph2tbA24Bs="; + }; + }; - # required for zig - XDG_CACHE_HOME = - "xdg_cache"; # prevents zig AccessDenied error github.com/ziglang/zig/issues/6810 - # want to see backtrace in case of failure - RUST_BACKTRACE = 1; + LLVM_SYS_130_PREFIX = "${llvmPkgs.llvm.dev}"; - # skip running rust tests, problems: - # building of example platforms requires network: Could not resolve host - # zig AccessDenied error github.com/ziglang/zig/issues/6810 - # Once instance has previously been poisoned ?? - doCheck = false; + # required for zig + XDG_CACHE_HOME = + "xdg_cache"; # prevents zig AccessDenied error github.com/ziglang/zig/issues/6810 + # want to see backtrace in case of failure + RUST_BACKTRACE = 1; - nativeBuildInputs = (with pkgs; [ - cmake - git - pkg-config - python3 - llvmPkgs.clang - llvmPkgs.llvm.dev - zig - ]); + # skip running rust tests, problems: + # building of example platforms requires network: Could not resolve host + # zig AccessDenied error github.com/ziglang/zig/issues/6810 + # Once instance has previously been poisoned ?? + doCheck = false; - buildInputs = (with pkgs; - [ - libffi - libiconv - libxkbcommon - libxml2 - ncurses - zlib - cargo - makeWrapper # necessary for postBuild wrapProgram - ] ++ lib.optionals pkgs.stdenv.isLinux [ - valgrind - vulkan-headers - vulkan-loader - vulkan-tools - vulkan-validation-layers - xorg.libX11 - xorg.libXcursor - xorg.libXi - xorg.libXrandr - xorg.libxcb - ] ++ lib.optionals pkgs.stdenv.isDarwin [ - pkgs.darwin.apple_sdk.frameworks.AppKit - pkgs.darwin.apple_sdk.frameworks.CoreFoundation - pkgs.darwin.apple_sdk.frameworks.CoreServices - pkgs.darwin.apple_sdk.frameworks.CoreVideo - pkgs.darwin.apple_sdk.frameworks.Foundation - pkgs.darwin.apple_sdk.frameworks.Metal - pkgs.darwin.apple_sdk.frameworks.Security + nativeBuildInputs = (with pkgs; [ + cmake + git + pkg-config + python3 + llvmPkgs.clang + llvmPkgs.llvm.dev + zig_0_9 ]); - # cp: to copy str.zig,list.zig... - # wrapProgram pkgs.stdenv.cc: to make ld available for compiler/build/src/link.rs - postInstall = - if pkgs.stdenv.isLinux then '' - wrapProgram $out/bin/roc --set NIX_GLIBC_PATH ${nixGlibcPath} --prefix PATH : ${ - pkgs.lib.makeBinPath [ pkgs.stdenv.cc ] - } - '' else '' - wrapProgram $out/bin/roc --prefix PATH : ${ - pkgs.lib.makeBinPath [ pkgs.stdenv.cc ] - } - ''; -} + buildInputs = (with pkgs; + [ + libffi + libiconv + libxkbcommon + libxml2 + ncurses + zlib + cargo + makeWrapper # necessary for postBuild wrapProgram + ] ++ lib.optionals pkgs.stdenv.isLinux [ + valgrind + vulkan-headers + vulkan-loader + vulkan-tools + vulkan-validation-layers + xorg.libX11 + xorg.libXcursor + xorg.libXi + xorg.libXrandr + xorg.libxcb + ] ++ lib.optionals pkgs.stdenv.isDarwin [ + pkgs.darwin.apple_sdk.frameworks.AppKit + pkgs.darwin.apple_sdk.frameworks.CoreFoundation + pkgs.darwin.apple_sdk.frameworks.CoreServices + pkgs.darwin.apple_sdk.frameworks.CoreVideo + pkgs.darwin.apple_sdk.frameworks.Foundation + pkgs.darwin.apple_sdk.frameworks.Metal + pkgs.darwin.apple_sdk.frameworks.Security + ]); + + # cp: to copy str.zig,list.zig... + # wrapProgram pkgs.stdenv.cc: to make ld available for compiler/build/src/link.rs + postInstall = + if pkgs.stdenv.isLinux then '' + wrapProgram $out/bin/roc --set NIX_GLIBC_PATH ${nixGlibcPath} --prefix PATH : ${ + pkgs.lib.makeBinPath [ pkgs.stdenv.cc ] + } + '' else '' + wrapProgram $out/bin/roc --prefix PATH : ${ + pkgs.lib.makeBinPath [ pkgs.stdenv.cc ] + } + ''; + } diff --git a/devtools/flake.lock b/devtools/flake.lock index dd655bc0b49..beb73d08082 100644 --- a/devtools/flake.lock +++ b/devtools/flake.lock @@ -115,8 +115,8 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1682761608, - "narHash": "sha256-+6tHbEK8GYDSCLasgawufSLegyJ0cqHV2nfmJICwzrk=", + "lastModified": 1682784625, + "narHash": "sha256-QUncKiwgpmHajo601NNHOjeUG2/vrGp1oMgxnPhq900=", "path": "/home/username/gitrepos/roc9/roc", "type": "path" }, diff --git a/examples/cli/cli-platform/EnvDecoding.roc b/examples/cli/cli-platform/EnvDecoding.roc index ccc4a9f2bb6..d6be7f2b2cb 100644 --- a/examples/cli/cli-platform/EnvDecoding.roc +++ b/examples/cli/cli-platform/EnvDecoding.roc @@ -1,27 +1,27 @@ interface EnvDecoding exposes [EnvFormat, format] imports [] EnvFormat := {} has [ - DecoderFormatting { - u8: envU8, - u16: envU16, - u32: envU32, - u64: envU64, - u128: envU128, - i8: envI8, - i16: envI16, - i32: envI32, - i64: envI64, - i128: envI128, - f32: envF32, - f64: envF64, - dec: envDec, - bool: envBool, - string: envString, - list: envList, - record: envRecord, - tuple: envTuple, - }, - ] + DecoderFormatting { + u8: envU8, + u16: envU16, + u32: envU32, + u64: envU64, + u128: envU128, + i8: envI8, + i16: envI16, + i32: envI32, + i64: envI64, + i128: envI128, + f32: envF32, + f64: envF64, + dec: envDec, + bool: envBool, + string: envString, + list: envList, + record: envRecord, + tuple: envTuple, + }, + ] format : {} -> EnvFormat format = \{} -> @EnvFormat {} diff --git a/examples/cli/effects-platform/host.zig b/examples/cli/effects-platform/host.zig index a0780797b31..e0e100278ff 100644 --- a/examples/cli/effects-platform/host.zig +++ b/examples/cli/effects-platform/host.zig @@ -7,20 +7,6 @@ const expectEqual = testing.expectEqual; const expect = testing.expect; const maxInt = std.math.maxInt; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/examples/cli/tui-platform/host.zig b/examples/cli/tui-platform/host.zig index 0eb4717d8c7..6e1c1869e93 100644 --- a/examples/cli/tui-platform/host.zig +++ b/examples/cli/tui-platform/host.zig @@ -7,20 +7,6 @@ const expectEqual = testing.expectEqual; const expect = testing.expect; const maxInt = std.math.maxInt; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/examples/glue/rust-platform/src/lib.rs b/examples/glue/rust-platform/src/lib.rs index a91550458b1..fe5398b6475 100644 --- a/examples/glue/rust-platform/src/lib.rs +++ b/examples/glue/rust-platform/src/lib.rs @@ -87,7 +87,7 @@ pub extern "C" fn rust_main() -> i32 { let mut op: Op = roc_main(); loop { - match dbg!(op.discriminant()) { + match op.discriminant() { StdoutWrite => { let stdout_write = op.get_StdoutWrite(); let output: RocStr = stdout_write.f0; diff --git a/examples/parser/Parser/Str.roc b/examples/parser/Parser/Str.roc index 0d7cdbf02c8..2c5bb33e206 100644 --- a/examples/parser/Parser/Str.roc +++ b/examples/parser/Parser/Str.roc @@ -15,7 +15,7 @@ interface Parser.Str scalar, oneOf, digit, - digits, + positiveInt, strFromRaw, ] imports [Parser.Core.{ Parser, ParseResult, map, oneOrMore, parse, parsePartial, buildPrimitiveParser }] @@ -186,12 +186,12 @@ digit = oneOf digitParsers # NOTE: Currently happily accepts leading zeroes -digits : Parser RawStr (Int *) -digits = +positiveInt : Parser RawStr (Int *) +positiveInt = oneOrMore digit |> map \digitsList -> digitsList - |> List.map Num.intCast + |> List.map \char -> Num.intCast char - '0' |> List.walk 0 \sum, digitVal -> 10 * sum + digitVal ## Try a bunch of different parsers. diff --git a/examples/parser/package/ParserStr.roc b/examples/parser/package/ParserStr.roc index 8b62b318881..f433c030236 100644 --- a/examples/parser/package/ParserStr.roc +++ b/examples/parser/package/ParserStr.roc @@ -15,7 +15,7 @@ interface ParserStr scalar, oneOf, digit, - digits, + positiveInt, strFromRaw, ] imports [ @@ -196,12 +196,12 @@ digit = oneOf digitParsers # NOTE: Currently happily accepts leading zeroes -digits : Parser RawStr (Int *) -digits = +positiveInt : Parser RawStr (Int *) +positiveInt = oneOrMore digit |> map \digitsList -> digitsList - |> List.map Num.intCast + |> List.map \char -> Num.intCast char - '0' |> List.walk 0 \sum, digitVal -> 10 * sum + digitVal ## Try a bunch of different parsers. diff --git a/examples/platform-switching/rust-platform/rust-toolchain.toml b/examples/platform-switching/rust-platform/rust-toolchain.toml index bfaab275a2a..9e80c038294 100644 --- a/examples/platform-switching/rust-platform/rust-toolchain.toml +++ b/examples/platform-switching/rust-platform/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.66.1" +channel = "1.67.1" profile = "default" diff --git a/examples/platform-switching/zig-platform/host.zig b/examples/platform-switching/zig-platform/host.zig index 6025d07d157..8ba1179e4ff 100644 --- a/examples/platform-switching/zig-platform/host.zig +++ b/examples/platform-switching/zig-platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const Align = 2 * @alignOf(usize); extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque; extern fn realloc(c_ptr: [*]align(Align) u8, size: usize) callconv(.C) ?*anyopaque; diff --git a/examples/webserver/.gitignore b/examples/webserver/.gitignore new file mode 100644 index 00000000000..d0e2c82835f --- /dev/null +++ b/examples/webserver/.gitignore @@ -0,0 +1,2 @@ +platform/glue +app diff --git a/examples/webserver/Http.roc b/examples/webserver/Http.roc new file mode 100644 index 00000000000..bdb8a14eba3 --- /dev/null +++ b/examples/webserver/Http.roc @@ -0,0 +1,4 @@ +interface Http exposes [request] imports [HttpInternal] + +request : Str -> Str +request = \req -> HttpInternal.request req diff --git a/examples/webserver/HttpInternal.roc b/examples/webserver/HttpInternal.roc new file mode 100644 index 00000000000..0201a2d2723 --- /dev/null +++ b/examples/webserver/HttpInternal.roc @@ -0,0 +1,4 @@ +interface HttpInternal exposes [request] imports [] + +request : Str -> Str +request = \req -> req diff --git a/examples/webserver/build.sh b/examples/webserver/build.sh new file mode 100755 index 00000000000..46790466f83 --- /dev/null +++ b/examples/webserver/build.sh @@ -0,0 +1,2 @@ +roc glue ../../crates/glue/src/RustGlue.roc platform/glue platform/main.roc +roc build diff --git a/examples/webserver/main.roc b/examples/webserver/main.roc new file mode 100644 index 00000000000..d5660386553 --- /dev/null +++ b/examples/webserver/main.roc @@ -0,0 +1,6 @@ +app "app" + packages { pf: "platform/main.roc" } + imports [] + provides [main] to pf + +main = \str -> "hi, \(str)!!" diff --git a/examples/webserver/platform/Cargo.toml b/examples/webserver/platform/Cargo.toml new file mode 100644 index 00000000000..867603fafc2 --- /dev/null +++ b/examples/webserver/platform/Cargo.toml @@ -0,0 +1,38 @@ +# ⚠️ READ THIS BEFORE MODIFYING THIS FILE! ⚠️ +# +# This file is a fixture template. If the file you're looking at is +# in the fixture-templates/ directory, then you're all set - go ahead +# and modify it, and it will modify all the fixture tests. +# +# If this file is in the fixtures/ directory, on the other hand, then +# it is gitignored and will be overwritten the next time tests run. +# So you probably don't want to modify it by hand! Instead, modify the +# file with the same name in the fixture-templates/ directory. + +[package] +name = "host" +version = "0.0.1" +authors = ["The Roc Contributors"] +license = "UPL-1.0" +edition = "2018" +links = "app" + +[lib] +name = "host" +path = "src/lib.rs" +crate-type = ["staticlib", "rlib"] + +[[bin]] +name = "host" +path = "src/main.rs" + +[dependencies] +roc_std = { path = "glue/roc_std", features = ["std"] } +roc_app = { path = "glue/roc_app" } +libc = "0.2" +hyper = { version = "0.14", features = ["http1", "http2", "client", "server", "runtime", "backports", "deprecated"] } +tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } +futures = "0.3" +bytes = "1.0" + +[workspace] diff --git a/examples/webserver/platform/build.rs b/examples/webserver/platform/build.rs new file mode 100644 index 00000000000..47763b34c39 --- /dev/null +++ b/examples/webserver/platform/build.rs @@ -0,0 +1,9 @@ +fn main() { + #[cfg(not(windows))] + println!("cargo:rustc-link-lib=dylib=app"); + + #[cfg(windows)] + println!("cargo:rustc-link-lib=dylib=libapp"); + + println!("cargo:rustc-link-search=."); +} diff --git a/examples/webserver/platform/host.c b/examples/webserver/platform/host.c new file mode 100644 index 00000000000..3914d3f6eed --- /dev/null +++ b/examples/webserver/platform/host.c @@ -0,0 +1,14 @@ +// ⚠️ READ THIS BEFORE MODIFYING THIS FILE! ⚠️ +// +// This file is a fixture template. If the file you're looking at is +// in the fixture-templates/ directory, then you're all set - go ahead +// and modify it, and it will modify all the fixture tests. +// +// If this file is in the fixtures/ directory, on the other hand, then +// it is gitignored and will be overwritten the next time tests run. +// So you probably don't want to modify it by hand! Instead, modify the +// file with the same name in the fixture-templates/ directory. + +extern int rust_main(); + +int main() { return rust_main(); } diff --git a/examples/webserver/platform/main.roc b/examples/webserver/platform/main.roc new file mode 100644 index 00000000000..c01047b7575 --- /dev/null +++ b/examples/webserver/platform/main.roc @@ -0,0 +1,9 @@ +platform "webserver-platform" + requires {} { main : _ } + exposes [] + packages {} + imports [] + provides [mainForHost] + +mainForHost : Str -> Str +mainForHost = \str -> main str diff --git a/examples/webserver/platform/src/lib.rs b/examples/webserver/platform/src/lib.rs new file mode 100644 index 00000000000..ad54ace3592 --- /dev/null +++ b/examples/webserver/platform/src/lib.rs @@ -0,0 +1,210 @@ +use futures::{Future, FutureExt}; +use hyper::{Body, Request, Response, Server, StatusCode}; +use roc_app; +use roc_std::RocStr; +use std::cell::RefCell; +use std::convert::Infallible; +use std::net::SocketAddr; +use std::os::raw::{c_int, c_long, c_void}; +use std::panic::AssertUnwindSafe; +use tokio::task::spawn_blocking; +use libc::{sigaction, siginfo_t, sigemptyset, SIGBUS, SIGFPE, SIGILL, SIGSEGV, sighandler_t, sigset_t, SA_SIGINFO, SIG_DFL}; + +const DEFAULT_PORT: u16 = 8000; + +// If we have a roc_panic or a segfault, these will be used to record where to jump back to +// (a point at which we can return a different response). +thread_local! { + // 64 is the biggest jmp_buf in setjmp.h + static SETJMP_ENV: RefCell<[c_long; 64]> = RefCell::new([0 as c_long; 64]); + static ROC_CRASH_MSG: RefCell = RefCell::new(RocStr::empty()); + static SIGNAL_CAUGHT: RefCell = RefCell::new(0); +} + +extern "C" { + #[link_name = "setjmp"] + pub fn setjmp(env: *mut c_void) -> c_int; + + #[link_name = "longjmp"] + pub fn longjmp(env: *mut c_void, val: c_int); +} + +unsafe extern "C" fn signal_handler(sig: c_int, _: *mut siginfo_t, _: *mut libc::c_void) { + SIGNAL_CAUGHT.with(|val| { + *val.borrow_mut() = sig; + }); + + SETJMP_ENV.with(|env| { + longjmp(env.borrow_mut().as_mut_ptr().cast(), 1); + }); +} + +fn setup_signal(sig: c_int) { + let sa = libc::sigaction { + sa_sigaction: signal_handler as sighandler_t, + sa_mask: sigset_t::default(), + sa_flags: SA_SIGINFO, + }; + + let mut old_sa = libc::sigaction { + sa_sigaction: SIG_DFL, + sa_mask: sigset_t::default(), + sa_flags: 0, + }; + + unsafe { + sigemptyset(&mut old_sa.sa_mask as *mut sigset_t); + sigaction(sig, &sa, &mut old_sa); + } +} + +fn call_roc(req_bytes: &[u8]) -> Response { + let mut setjmp_result = 0; + + SETJMP_ENV.with(|env| { + setjmp_result = unsafe { setjmp(env.borrow_mut().as_mut_ptr().cast()) }; + }); + + if setjmp_result == 0 { + setup_signal(SIGSEGV); + setup_signal(SIGILL); + setup_signal(SIGFPE); + setup_signal(SIGBUS); + + let req_str: &str = std::str::from_utf8(req_bytes).unwrap(); // TODO don't unwrap + let resp: String = roc_app::mainForHost(req_str.into()).as_str().into(); + + Response::builder() + .status(StatusCode::OK) // TODO get status code from Roc too + .body(Body::from(resp)) + .unwrap() // TODO don't unwrap() here + } else { + let mut crash_msg: String = String::new(); + let mut sig: c_int = 0; + + SIGNAL_CAUGHT.with(|val| { + sig = *val.borrow(); + }); + + if sig == 0 { + ROC_CRASH_MSG.with(|env| { + crash_msg = env.borrow().as_str().into(); + }); + } else { + crash_msg = "Roc crashed with signal {sig}".into(); // TODO print the name of the signal + } + + Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Body::from(crash_msg)) + .unwrap() // TODO don't unwrap() here + } +} + +async fn handle_req(req: Request) -> Response { + match hyper::body::to_bytes(req.into_body()).await { + Ok(req_body) => { + spawn_blocking(move || call_roc(&req_body)) + .then(|resp| async { + resp.unwrap() // TODO don't unwrap here + }) + .await + } + Err(_) => todo!(), // TODO + } +} + +/// Translate Rust panics in the given Future into 500 errors +async fn handle_panics( + fut: impl Future>, +) -> Result, Infallible> { + match AssertUnwindSafe(fut).catch_unwind().await { + Ok(response) => Ok(response), + Err(_panic) => { + let error = Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body("Panic detected!".into()) + .unwrap(); // TODO don't unwrap here + + Ok(error) + } + } +} + +const LOCALHOST: [u8; 4] = [127, 0, 0, 1]; + +async fn run_server(port: u16) -> i32 { + let addr = SocketAddr::from((LOCALHOST, port)); + let server = Server::bind(&addr).serve(hyper::service::make_service_fn(|_conn| async { + Ok::<_, Infallible>(hyper::service::service_fn(|req| handle_panics(handle_req(req)))) + })); + + println!("Listening on "); + + match server.await { + Ok(_) => 0, + Err(err) => { + eprintln!("Error initializing Rust `hyper` server: {}", err); // TODO improve this + + 1 + } + } +} + +#[no_mangle] +pub extern "C" fn rust_main() -> i32 { + match tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + { + Ok(runtime) => runtime.block_on(async { run_server(DEFAULT_PORT).await }), + Err(err) => { + eprintln!("Error initializing tokio multithreaded runtime: {}", err); // TODO improve this + + 1 + } + } +} + +// Externs required by roc_std and by the Roc app + +#[no_mangle] +pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void { + return libc::malloc(size); +} + +#[no_mangle] +pub unsafe extern "C" fn roc_realloc( + c_ptr: *mut c_void, + new_size: usize, + _old_size: usize, + _alignment: u32, +) -> *mut c_void { + return libc::realloc(c_ptr, new_size); +} + +#[no_mangle] +pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) { + return libc::free(c_ptr); +} + +#[no_mangle] +pub unsafe extern "C" fn roc_panic(msg: RocStr) { + // Set the last caught signal to 0, so we don't mistake this for a signal. + SIGNAL_CAUGHT.with(|val| { + *val.borrow_mut() = 0; + }); + + ROC_CRASH_MSG.with(|val| { + *val.borrow_mut() = msg; + }); + + SETJMP_ENV.with(|env| { + longjmp(env.borrow_mut().as_mut_ptr().cast(), 1); + }); +} + +#[no_mangle] +pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { + libc::memset(dst, c, n) +} diff --git a/examples/webserver/platform/src/main.rs b/examples/webserver/platform/src/main.rs new file mode 100644 index 00000000000..0765384f29f --- /dev/null +++ b/examples/webserver/platform/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + std::process::exit(host::rust_main() as _); +} diff --git a/flake.lock b/flake.lock index ff6fc7a2072..e8b6934018e 100644 --- a/flake.lock +++ b/flake.lock @@ -74,17 +74,17 @@ }, "nixpkgs": { "locked": { - "lastModified": 1674860021, - "narHash": "sha256-ES4XUf/AlPp8RetKR6WlWgYEZ7bLWI7k6reHp2q9rqY=", + "lastModified": 1679868513, + "narHash": "sha256-uRGLALUmieyMVqVt3mXEv0a+g9ouFOCmsv7V/01efEc=", "owner": "nixos", "repo": "nixpkgs", - "rev": "9f4346eac544cc0db5eb7d889e71eac0f9c8b9eb", + "rev": "d7887373fe0731719365831cd254c1e5948307d3", "type": "github" }, "original": { "owner": "nixos", "repo": "nixpkgs", - "rev": "9f4346eac544cc0db5eb7d889e71eac0f9c8b9eb", + "rev": "d7887373fe0731719365831cd254c1e5948307d3", "type": "github" } }, diff --git a/flake.nix b/flake.nix index d5ead805997..52fa26c8346 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ description = "Roc flake"; inputs = { - nixpkgs.url = "github:nixos/nixpkgs?rev=9f4346eac544cc0db5eb7d889e71eac0f9c8b9eb"; + nixpkgs.url = "github:nixos/nixpkgs?rev=d7887373fe0731719365831cd254c1e5948307d3"; # rust from nixpkgs has some libc problems, this is patched in the rust-overlay rust-overlay = { @@ -90,7 +90,7 @@ llvmPkgs.clang libxkbcommon pkg-config - zig # roc builtins are implemented in zig, see compiler/builtins/bitcode/ + zig_0_9 # roc builtins are implemented in zig, see compiler/builtins/bitcode/ # lib deps libffi diff --git a/mlc_config.json b/mlc_config.json index 38b8efc73fc..c8341a6907a 100644 --- a/mlc_config.json +++ b/mlc_config.json @@ -41,6 +41,9 @@ }, { "pattern": "https://www.roc-lang.org/packages/basic-cli/Stdout#line" + }, + { + "pattern": "https://liberapay.com/" } ] } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index fd2c2a81d47..8a17faa5c3a 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -9,9 +9,9 @@ # - update nightly-OLD_DATE in .github/workflows/windows_release_build.yml # - update nightly-OLD_DATE in crates/compiler/build/src/link.rs -channel = "1.66.1" # check ^^^ when changing this +channel = "1.67.1" # check ^^^ when changing this # -# channel = "nightly-2022-10-30" # 1.66.0 nightly to be able to use unstable features +# channel = "nightly-2022-12-09" # 1.67.0 nightly to be able to use unstable features profile = "default" components = [ # for usages of rust-analyzer or similar tools inside `nix develop` diff --git a/www/README.md b/www/README.md index b4828a8d4b8..3ffdf87a216 100644 --- a/www/README.md +++ b/www/README.md @@ -4,7 +4,7 @@ To view the website after you've made a change, execute: ```bash ./www/build.sh cd www/build -simple-http-server --nocache # If you're using the nix flake simple-http-server will already be installed. Without nix you can install it with `cargo install simple-http-server`. +simple-http-server --nocache --index # already installed if you're using `nix develop`, otherwise use `cargo install simple-http-server` ``` Open http://0.0.0.0:8000 in your browser. diff --git a/www/build.sh b/www/build.sh index e2df683f4b8..499ee39f2c4 100755 --- a/www/build.sh +++ b/www/build.sh @@ -69,7 +69,8 @@ if ! [ -v GITHUB_TOKEN_READ_ONLY ]; then echo 'Building tutorial.html from tutorial.md...' mkdir www/build/tutorial - cargo run --release --bin roc run www/generate_tutorial/src/tutorial.roc -- www/generate_tutorial/src/input/ www/build/tutorial/ + cargo build --release --bin roc + roc=target/release/roc else echo 'Fetching latest roc nightly...' @@ -81,19 +82,21 @@ else ls | grep "roc_nightly.*tar.gz" | xargs rm # simplify dir name mv roc_nightly* roc_nightly + roc='./roc_nightly/roc' echo 'Building tutorial.html from tutorial.md...' mkdir www/build/tutorial - - ./roc_nightly/roc version - ./roc_nightly/roc run www/generate_tutorial/src/tutorial.roc -- www/generate_tutorial/src/input/ www/build/tutorial/ - - # cleanup - rm -rf roc_nightly roc_releases.json fi +$roc version +$roc run www/generate_tutorial/src/tutorial.roc -- www/generate_tutorial/src/input/ www/build/tutorial/ mv www/build/tutorial/tutorial.html www/build/tutorial/index.html +# for new wip site +mkdir www/build/wip +$roc run www/wip_new_website/main.roc -- www/wip_new_website/content/ www/build/wip +cp -r www/wip_new_website/static/site.css www/build/wip + # cleanup rm -rf roc_nightly roc_releases.json @@ -112,7 +115,17 @@ rm -rf ./downloaded-basic-cli BASIC_CLI_PACKAGE_DIR="www/build/packages/basic-cli" mkdir -p $BASIC_CLI_PACKAGE_DIR -rm generated-docs/*.* # we already copied over the *.js and *.css files earlier, so just drop these. +mv generated-docs/index.html $BASIC_CLI_PACKAGE_DIR +rm generated-docs/*.* # we already copied over the *.js and *.css files earlier for the builtins, so just drop these. mv generated-docs/* $BASIC_CLI_PACKAGE_DIR # move all the folders to build/packages/basic-cli +# set up docs for basic-cli 0.3.2 +BASIC_CLI_DIR_0_3_2=$BASIC_CLI_PACKAGE_DIR/0.3.2 +mkdir -p $BASIC_CLI_DIR_0_3_2 +curl -fL --output $BASIC_CLI_DIR_0_3_2/docs.tar.gz https://github.com/roc-lang/basic-cli/releases/download/0.3.2/docs.tar.gz +tar -xf $BASIC_CLI_DIR_0_3_2/docs.tar.gz -C $BASIC_CLI_DIR_0_3_2/ +rm $BASIC_CLI_DIR_0_3_2/docs.tar.gz +mv $BASIC_CLI_DIR_0_3_2/generated-docs/* $BASIC_CLI_DIR_0_3_2 +rm -rf $BASIC_CLI_DIR_0_3_2/generated-docs + popd diff --git a/www/generate_tutorial/src/input/tutorial.md b/www/generate_tutorial/src/input/tutorial.md index 2ee3c608aec..3a7abc0215d 100644 --- a/www/generate_tutorial/src/input/tutorial.md +++ b/www/generate_tutorial/src/input/tutorial.md @@ -1373,7 +1373,7 @@ Let's take a closer look at the part of `main.roc` above the `main` def: app "hello" packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.4.0/DI4lqn7LIZs8ZrCDUgLK-tHHpQmxGF1ZrlevRKq5LXk.tar.br" } imports [pf.Stdout] - provides main to pf + provides [main] to pf ``` This is known as a _module header_. Every `.roc` file is a _module_, and there are different types of modules. We know this particular one is an _application module_ because it begins with the `app` keyword. diff --git a/www/public/repl/index.html b/www/public/repl/index.html index b71fc2c87b7..fdb8c971a65 100644 --- a/www/public/repl/index.html +++ b/www/public/repl/index.html @@ -33,6 +33,7 @@

The rockin' Roc REPL

> +

⚠️ This web REPL misses some features that are available in the CLI (roc repl) like defining variables without a final expression, which will result in the "Missing Final Expression" error.

diff --git a/www/wip_new_website/.gitignore b/www/wip_new_website/.gitignore new file mode 100644 index 00000000000..ff2a61edb64 --- /dev/null +++ b/www/wip_new_website/.gitignore @@ -0,0 +1,3 @@ +roc-website +dist/ +website-builder \ No newline at end of file diff --git a/www/wip_new_website/build.roc b/www/wip_new_website/build.roc new file mode 100755 index 00000000000..79f6c878f59 --- /dev/null +++ b/www/wip_new_website/build.roc @@ -0,0 +1,46 @@ +#!/usr/bin/env roc +app "website-builder" + packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.4.0-rc1/hbIodFf7kULTYZJkzgsvgsnFAvQexm5hVeBaOMZk84I.tar.br" } + imports [ + pf.Task.{ Task }, + pf.Command, + pf.Arg + ] + provides [main] to pf + +main = + # TODO take dist folder name and main.roc path as args once https://github.com/roc-lang/basic-cli/issues/82 is fixed + # TODO add function to remove boilerplate + # Remove dist folder + {} <- + Command.new "rm" + |> Command.args ["-rf", "dist/"] + |> Command.status + |> Task.onErr \_ -> crash "Failed to remove dist folder" + |> Task.await + + # Build site + {} <- + Command.new "roc" + |> Command.args ["run", "main.roc", "--", "content/", "dist/"] + |> Command.status + |> Task.onErr \_ -> crash "Failed to build site" + |> Task.await + + # Copy static files + {} <- + Command.new "cp" + |> Command.args ["-r", "static/site.css", "dist/"] + |> Command.status + |> Task.onErr \_ -> crash "Failed to copy static files" + |> Task.await + + # Start file server + {} <- + Command.new "simple-http-server" + |> Command.args ["-p", "8080", "--", "dist/"] + |> Command.status + |> Task.onErr \_ -> crash "Failed to run file server; consider intalling with `cargo install simple-http-server`" + |> Task.await + + Task.ok {} diff --git a/www/wip_new_website/content/community.md b/www/wip_new_website/content/community.md new file mode 100644 index 00000000000..e78151d6fce --- /dev/null +++ b/www/wip_new_website/content/community.md @@ -0,0 +1,39 @@ +# Contribute to Roc + +## Community Values + + + +## Meet the Community + +- Group Chat [roc.zulipchat.com](https://roc.zulipchat.com/) +- Github Project [roc-lang/roc](https://github.com/roc-lang/roc) +- Meetups & Events + +## Ideas & Proposals + +- [Good First Issues](https://github.com/roc-lang/roc/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) + + + +Roc doesn't have a formal process for managing design proposals. The guiding principal for now is that as a community we should all be friendly, supportive, and openly discuss and sharing ideas. + +There are three loose stages that a design proposal can go through in Roc: Idea, Proposal, and Implementation. + +In the Idea stage, people are encouraged to describe their idea and explore the problem, potential solutions, and trade-offs. It is helpful to share the idea in the group chat using the #ideas channel. + +If there is a general consensus that an idea is promising and worth developing, people are encouraged to develop a design Proposal. Some ideas are simple and should progress straight to implementation. However, for more complex Proposals a it write the design into an article or blog post and share it online. This documentation helps in clarifying the proposal and making it more accessible to the community. + +Implementation using PR... + +## Code of Conduct + + diff --git a/www/wip_new_website/content/design_goals.md b/www/wip_new_website/content/design_goals.md new file mode 100644 index 00000000000..045bc71b785 --- /dev/null +++ b/www/wip_new_website/content/design_goals.md @@ -0,0 +1,146 @@ + +# Design Goals + + + +Roc's goal is to be a fast, friendly, functional language. It's very much a work in progress; below, you can see the current progress towards this goal. This website is intentionally unstyled as a way to emphasize the language's current level of incompleteness. The website will become more polished after the language itself becomes more polished! + +Roc compiles to machine code or to [WebAssembly](https://webassembly.org). Eventually you'll be able to use Roc to build high-quality servers, command-line applications, graphical native desktop user interfaces, among other classes of applications. Today, only command-line interfaces have support beyond the proof-of-concept stage; the other use cases will mature over time. + +Like [Lua](https://www.lua.org/), Roc's automatic memory management doesn't require a virtual machine, and it's possible to call Roc functions directly from any language that can call [C](https://en.wikipedia.org/wiki/C_(programming_language)) functions. This makes Roc additionally useful as a language for implementing plugins, and gives you a way to incrementally transition a legacy code base from another language to Roc. + +So far, the Roc compiler has progressed past the "proof of concept" stage, but there are currently lots of known bugs and unimplemented features, and the documentation for both the language and the standard library is incomplete. The overall ecosystem is in its infancy, and the compiler is neither battle-tested nor fuzz-tested yet, so we don't recommend relying on Roc for critical projects until its development is further along. + +# Fast{#fast} + + + +## Goal + +We want Roc to run faster than any non-systems language (like C, C++, Rust, or Zig) that sees mainstream use in industry. The goal is that nobody should find themselves thinking "I should rewrite my Roc program in \[some mainstream garbage-collected language\] because that will make it run significantly faster." + +When benchmarking Roc code against similarly-optimized programs written in [Go](https://go.dev), [Swift](https://www.swift.org/), [Java](https://www.oracle.com/java/), [C#](https://learn.microsoft.com/en-us/dotnet/csharp), or [JavaScript](https://www.ecma-international.org/publications-and-standards/standards/ecma-262), we generally aim for Roc to outperform all of those languages. Outperforming systems languages like Rust, Zig, C, D, and C++ is a non-goal, as is outperforming research languages that see little or no use in industry. (Realistically, there will always be certain specific benchmarks where some popular non-systems-level languages outperform Roc, but the goal is to usually be at the front of that pack.) + +## Current progress + +Progress towards this performance goal is already quite far along. + +Roc already uses unboxed data structures and unboxed closures, monomorphizes polymorphic code, and uses LLVM as a compiler backend. These optimizations, especially unboxed closures and monomorphization, can be found in several systems-level languages (like C++ and Rust), but not in any mainstream garbage-collected languages. Roc closures in particular have the distinction of being as ergonomic as the closures found in garbage-collected languages (where they are typically boxed), but have the performance of systems language closures (which are typically unboxed, but have more complicated types). + +Because of these optimizations, in many cases Roc code already compiles to the same machine instructions that the equivalent code written in one of these systems languages would. Something we do regularly is to compare the LLVM instructions generated by Roc's compiler and by these systems languages' compilers, to check whether we're generating equivalent instructions. + +That said, there are also cases where Roc has strictly more runtime overhead than languages like C, C++, Zig, and Rust do. The most costly is automatic memory management, which Roc implements using automatic reference counting. Static reference count optimizations like elision and reuse (thanks to Morphic and [Perceus](https://www.microsoft.com/en-us/research/publication/perceus-garbage-free-reference-counting-with-reuse/)) improve things, but significant runtime overhead remains. + +Eliminating this overhead altogether would require sacrificing other design goals (e.g. it would require introducing memory-unsafe operations, or compile-time lifetime errors), and there isn't much overhead left to remove outside of automatic memory management. For example, smaller sources of overhead include mandatory array bounds checks, disallowing cyclic references (which rules out a certain niche of efficient graph data structures), and automatic opportunistic in-place mutation instead of direct mutation. Even if all of these sources of overhead were completely eliminated, it seems unlikely that typical Roc programs would see a particularly big performance boost. + +Overall, we expect Roc's performance in the use cases mentioned above (servers, CLIs, GUIs, etc.) to be about the same as the equivalent C++ code would be, if all that C++ code (including its dependencies) were written in a restricted subset of C++ which always did array bounds checks and used shared pointers for all heap allocations. The Roc code might even run somewhat faster, because its reference counts are non-atomic by default, and can be statically optimized away in some cases—but then again, Roc also has a bit of overhead to perform opportunistic in-place mutation instead of direct mutation. + +To be clear, we don't expect this because we've benchmarked a bunch of programs written in Roc and in this restricted C++ subset, and found that the numbers were about the same (although if you know C++ well enough and want to do such experiments, we'd happy to help and would be interested to see the results!) but rather because Roc's compiler and [clang](https://clang.llvm.org/) should both be generating essentially the same LLVM instructions when the C++ is restricted to that subset. + +Of course, _unrestricted_ C++ code can certainly run faster than unrestricted Roc code. The same is true when comparing other such minimal-overhead systems languages to Roc, including Rust, Zig, C, and D. The point of the comparison is to give you a general idea of what Roc compiles to, since it is quite different from the VMs and JITted bytecode interpreters found in today's most popular garbage-collected languages! + +The talk [Outperforming Imperative with Pure Functional Languages](https://youtu.be/vzfy4EKwG_Y) discusses some early results from Roc's optimizations, and [Roc at Handmade Seattle](https://media.handmade-seattle.com/roc-lang) gets into low-level details of how Roc's compiler generates programs similarly to how clang does. + +# Friendly{#friendly} + + + +## Goals + +Roc aims to be a user-friendly language with a friendly community of users. + +A programming language can be much more than a tool for writing software, it can also be a way for people to come together through shared experiences, to teach and to learn from one another, and to make new friends. + +No community is perfect, but a community where people show kindness to each another by default can be a true joy to participate in. That all starts with friendliness, especially towards beginners, and including towards people who prefer other programming languages. After all, languages are tools people use to create software, and there's no need for us to create artificial divisions between ourselves based on the tools we use! + +On a technical level, Roc aims to ship a toolset where user-friendliness is a major priority. This includes everything from helpful error messages (aiming to meet the bar set by [Elm](https://elm-lang.org)) to quality-of-life improvements inspired by dynamic languages (always being able to run your program even if there are compile errors, automatic serialization and deserialization using schemas determined by type inference, reliable hot code loading that's always enabled and requires no configuration to set up, etc.) to accessibility features in the included editor. + +Roc also aims to ship a single binary that includes not only a compiler, but also a [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop), package manager, test runner, debugger, static analyzer, code formatter, and a full-featured editor, all of which are designed to work seamlessly together. + +## Current Progress + +Work has not yet started on the package manager, static analyzer, debugger, or hot code loading system, and although work has started on the editor, it's not yet far enough along to be usable for practical purposes. The standard library is perhaps 80 percent complete in terms of functionality, but a lot of operations do not yet have documentation. + +The REPL fully supports entering arbitrary expressions, and will evaluate them and print the results. It remembers recent expressions entered in the current session (if you press the up arrow), but it can't yet execute effects. You can try out the REPL in a browser at [roc-lang.org/repl](https://roc-lang.org/repl) - it uses a WebAssembly build of Roc's compiler, and compiles the code you write to WebAssembly on the fly, which it then executes in the browser to display the answer. + +The compiler works well enough on a basic level to build things with it, but some error messages could use significant improvement, and it has a lot of known bugs and missing features. You can currently use it on macOS (either Intel or Apple Silicon), Linux (only x86-64 machines at the moment), and Windows (only recently supported; debugging and testing features don't work on it yet, and there are likely bugs we haven't encountered yet due to lack of battle testing). Support for other operating systems has not yet been discussed. + +The compiler doesn't yet support incremental compilation or hot code loading, and build times vary based on what machine you're building for. + +For example, suppose you run \`roc check\`, which reports errors it finds (type mismatches, naming errors, and so on) but doesn't actually build an executable, on a code base that's under a thousand lines of code. On an M1 MacBook Pro, this typically takes about 10 milliseconds. + +In contrast, if you do \`roc build\` (or \`roc run\`) on that same machine, it will take closer to 500 milliseconds instead. Almost all that extra time is spent waiting for LLVM to generate (unoptimized) machine code, and then for the system linker to assemble an executable from it. + +Fortunately, we can eliminate almost all of those extra 490 millisconds of build time by using Roc's (work in progress) development backend instead of LLVM. This compiles directly from Roc's internal representation to machine code, like most compilers did before LLVM. (LLVM can optimize code into running very fast, but even when it performs no optimization at all, LLVM itself takes a lot longer to run than generating unoptimized machine code directly.) + +The LLVM backend is currently the most feature-complete, followed closely by the WebAssembly backend (which the online REPL uses exclusively, instead of LLVM). The x86 and ARM backends still have a ways to go, but improving them can be done by anyone with the patience to read some documentation; we have issues split up for them, and are happy to help new contributors get up and running! + +Builds on Linux and Windows also use Roc's surgical linker instead of the system linker, which runs so fast that linking essentially disappears from the performance profile altogether. The surgical linker currently only works on Linux and Windows, and it currently supports building executables but not (yet) dynamic libraries, which is relevant if you're using Roc to create plugins or want to call Roc functions from existing code bases in other languages. Work has started on macOS surgical linking, but it isn't usable yet. If you're interested in working on that, please get in touch on [Roc Zulip](https://roc.zulipchat.com/)! + +The test runner currently has first-class support for running standard non-effectful tests. It does not yet have first-class support for effectful tests, property-based tests, snapshot tests, or "simulation tests" (where effects are replaced by hardcoded values during the test - similar to "mocking" in other languages), although these are all planned for the future. + +The code formatter is nearly feature-complete, although occasionally it will report an error - usually due to a comment being placed somewhere it doesn't yet know how to handle. Unlike most of the rest of the compiler, the formatter is one place where the number of known bugs is so small that fuzzing would be very helpful as a way to surface bugs we don't yet know about. (If you're interested in working on setting up fuzzing for the formatter, please let us know in the [`#contributing` channel](https://roc.zulipchat.com/#narrow/stream/316715-contributing) on Zulip! Separately, we're also very interested in fuzzing the compiler, even though we already have a sizable list of known bugs there.) + +On the community side, so far the community is a friendly bunch, and we want to keep it that way as it grows! We hope to do that by encouraging a culture of kindness and helping one another out, especially by being welcoming towards beginners. + +If you'd like to join in, the best place to do that is in our Zulip chat. Feel free to drop by the [`introductions` topic](https://roc.zulipchat.com/#narrow/stream/231634-beginners/topic/introductions) and introduce yourself! + +# Functional{#functional} + + + +## Goals + +Roc aims to be a purely functional programming language. This means all Roc functions are [pure functions](https://en.wikipedia.org/wiki/Pure_function), and all effects are [managed effects](https://medium.com/@kaw2k/managed-effects-and-elm-36b7fcd246a9) instead of side effects. + +A major motivating reason for this is to facilitate tooling. For example, in the future the goal is that Roc's test runner won't bother re-running tests whose outcomes could not possibly have changed (because they were pure functions whose inputs did not change). Tests that contain only pure functions can be trivially run in parallel, and they will never [flake](https://www.smashingmagazine.com/2021/04/flaky-tests-living-nightmare/). Additionally, having the guarantee that the application contains only pure functions can also make certain debugging tools more reliable, such as time travel and retroactive tracing. + +Roc also takes a novel approach to managed effects. In most programming languages, the standard library contains both data structures and I/O primitives (e.g. for using the file system or the network), and then you might decide to use a [framework](https://en.wikipedia.org/wiki/Application_framework) on top of that standard library. + +In Roc, every application is built on a _platform_. A platform is like a framework except that it also provides I/O primitives and behind-the-scenes memory management. (Roc's standard library only contains data structures.) In practice, this means that using Roc feels similar to using any other programming language where you've chosen to use a framework, except that the documentation for your I/O primitives comes from the framework instead of the standard library. + +This might sound like a minor distinction, but it turns out there are a lot of surprising benefits to organizing things this way, which would be impossible to achieve without having platforms as a first-class language concept. [The Edges of Cutting-Edge Languages](https://youtu.be/cpQwtwVKAfU) goes into more detail about some of these benefits. + +## Current Progress + +Today, platforms as a concept already exist, and there are a few different ones implemented. You can find them in the [`examples/`](https://github.com/roc-lang/roc/tree/main/examples) directory in the source code repository. The platform for building command-line interfaces is the most fully featured; the others are mostly in the proof-of-concept stage. + +Roc's built-in tooling is not yet far enough along to take advantage of pure functions. For example, there is a built-in test runner, but it does not yet run tests in parallel or skip running tests whose outcomes could not possibly have changed. + +Roc is already a purely functional programming language, though, so all of these benefits are ready to be unlocked as the tooling implementations progress! diff --git a/www/wip_new_website/content/docs.md b/www/wip_new_website/content/docs.md new file mode 100644 index 00000000000..764b1b5de7c --- /dev/null +++ b/www/wip_new_website/content/docs.md @@ -0,0 +1,11 @@ +# Documentation + +- Builtin Package +- Basic-CLI Platform + + + +## Guides + +- [Frequently Asked Questions](https://github.com/roc-lang/roc/blob/main/FAQ.md) +- [Roc for Elm Programmers](https://github.com/roc-lang/roc/blob/main/roc-for-elm-programmers.md) \ No newline at end of file diff --git a/www/wip_new_website/content/index.md b/www/wip_new_website/content/index.md new file mode 100644 index 00000000000..60f7c5e4b92 --- /dev/null +++ b/www/wip_new_website/content/index.md @@ -0,0 +1,63 @@ + + +# Roc lang + +A systems programming language that is fast, friendly, and functional. + +
+ +
+

Friendly

+

Roc aims to be a user-friendly language with a friendly community of users. Learn more

+
+
+

Functional

+

Roc aims to be a purely functional programming language. Learn more

+
+ +## Try Roc + + + +```roc +app "hello-world" + packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.3.2/tE4xS_zLdmmxmHwHih9kHWQ7fsXtJr7W7h3425-eZFk.tar.br" } + imports [pf.Stdout] + provides [main] to pf + +main = Stdout.line "Hello, World!" +``` + +## Use cases + +- Tools & Scripts +- Web (coming soon) +- Networking & Servers (coming soon) +- Graphical (coming soon) +- Scientific (coming soon) +- Embedded (coming soon) + +## Roc Platforms/Applications (vs libraries) + + +TODO + +## Talks and Publications + +If you'd like to learn more about Roc check out one of these videos: + +* [Roc at Handmade Seattle](https://media.handmade-seattle.com/roc-lang) - November 12, 2021 (very low-level explanation of how Roc's compiler makes programs run fast) +* [Outperforming Imperative with Pure Functional Languages](https://youtu.be/vzfy4EKwG_Y) - October 1, 2021 (about Roc's runtime performance and optimizer) +* [A taste of Roc](https://youtu.be/6qzWm_eoUXM) - September 23, 2021 (syntax, application examples) +* [Roc at the Philly ETE conference](https://youtu.be/cpQwtwVKAfU?t=75) - May 6, 2021 (platforms and applications) +* [Roc on Zig Showtime](https://youtu.be/FMyyYdFSOHA) - April 24, 2021 (making a platform) +* [Roc at the Berlin FP Meetup](https://youtu.be/ZnYa99QoznE?t=4790) - September 1, 2020 (overall vision for the language) diff --git a/www/wip_new_website/content/install.md b/www/wip_new_website/content/install.md new file mode 100644 index 00000000000..3ede7d1a857 --- /dev/null +++ b/www/wip_new_website/content/install.md @@ -0,0 +1,50 @@ +# Installing Roc + +## Installation + +- [Linux x86-64 Getting Started Guide](https://github.com/roc-lang/roc/blob/main/getting_started/linux_x86_64.md) +- [MacOS Apple Silicon Getting Started Guide](https://github.com/roc-lang/roc/blob/main/getting_started/macos_apple_silicon.md) +- [MacOS x86-64 Getting Started Guide](https://github.com/roc-lang/roc/blob/main/getting_started/macos_x86_64.md) +- [Windows Getting Started Guide](https://github.com/roc-lang/roc/blob/main/getting_started/windows.md) +- [Other Systems Getting Started Guide](https://github.com/roc-lang/roc/blob/main/getting_started/other.md) + +## Nightly Builds + + + +[nightly builds](https://github.com/roc-lang/roc/releases) + +## Roc CLI + + +- Script `roc myApp.roc` +- Develop `roc dev` +- Test `roc test` +- Run `roc run` +- Build `roc build` +- Format `roc format` +- Documentation `roc docs` + +## Package Management + + + +## Editor Support + + + +- Language Server +- Neo(Vim) diff --git a/www/wip_new_website/content/sponsor.md b/www/wip_new_website/content/sponsor.md new file mode 100644 index 00000000000..71a00cf1050 --- /dev/null +++ b/www/wip_new_website/content/sponsor.md @@ -0,0 +1,8 @@ +# Sponsor + +- [Github Sponsorship](https://github.com/sponsors/roc-lang) + + \ No newline at end of file diff --git a/www/wip_new_website/content/tutorial.md b/www/wip_new_website/content/tutorial.md new file mode 100644 index 00000000000..2d8346554d9 --- /dev/null +++ b/www/wip_new_website/content/tutorial.md @@ -0,0 +1,5 @@ +# Tutorial + +This is a placeholder page for the Roc tutorial. + +TODO move the tutorial here from `../generate_tutorial/src/input/tutorial.roc` \ No newline at end of file diff --git a/www/wip_new_website/main.roc b/www/wip_new_website/main.roc new file mode 100644 index 00000000000..bf23c0a8f4b --- /dev/null +++ b/www/wip_new_website/main.roc @@ -0,0 +1,100 @@ +app "roc-website" + packages { pf: "../../examples/static-site-gen/platform/main.roc" } + imports [ + pf.Html.{ html, head, body, footer, p, div, main, text, nav, a, link, meta }, + pf.Html.Attributes.{ content, name, id, href, rel, lang, class, title, charset }, + ] + provides [transformFileContent] to pf + +pageData = + Dict.empty {} + |> Dict.insert "community.html" { title: "Community", description: "The Roc community" } + |> Dict.insert "design_goals.html" { title: "Design Goals", description: "Roc's design goals" } + |> Dict.insert "docs.html" { title: "Documentation", description: "Learn the Roc programming language" } + |> Dict.insert "home.html" { title: "Roc Lang", description: "The Roc programming language" } + |> Dict.insert "install.html" { title: "Install", description: "Getting started with the Roc programming language" } + |> Dict.insert "sponsor.html" { title: "Sponsor", description: "Sponsor Roc" } + |> Dict.insert "tutorial.html" { title: "Tutorial", description: "The Roc tutorial" } + +getPage : Str -> {title : Str, description : Str} +getPage = \current -> + Dict.get pageData current + |> Result.withDefault { title: "", description: ""} + +getTitle : Str -> Str +getTitle = \current -> + getPage current |> .title + +getDescription : Str -> Str +getDescription = \current -> + getPage current |> .description + +transformFileContent : Str, Str -> Str +transformFileContent = \page, htmlContent -> + Html.render (view page htmlContent) + +view : Str, Str -> Html.Node +view = \page, htmlContent -> + html [lang "en"] [ + head [] [ + meta [charset "utf-8"] [], + Html.title [] [text (getTitle page)], + meta [name "description", content (getDescription page)] [], + meta [name "viewport", content "width=device-width"] [], + link [rel "stylesheet", href "./site.css"] [], + link [rel "icon", href "/favicon.svg"] [], + ], + body [] [ + viewNavbar, + main [] [ + text htmlContent, + ], + footer [ + id "footer" + ] [ + p [] [ + a [href "https://github.com/roc-lang/roc"] [text "source code repository"], + ], + # + text "This site is powered by ", + a [href "https://www.netlify.com"] [ text "Netlify"], + text ". Made by people who like to make nice things. © Roc 2023", + ] + ], + # TODO - add site.js if needed + # script [src "/site.js"] [], + ] + +viewNavbar : Html.Node +viewNavbar = + div [id "top-bar"] [ + nav [] [ + a [href "./home.html", title "The Roc Programming Language"] [ + rocLogo + ], + div [id "top-bar-links"] [ + a [href "./tutorial.html"] [text "tutorial"], + a [href "./install.html"] [text "install"], + a [href "#todo-link-to-examples-site"] [text "examples"], + a [href "./community.html"] [text "community"], + a [href "./sponsor.html"] [text "sponsor"], + a [href "./docs.html"] [text "docs"], + ], + ], + ] + +rocLogo = + (Html.element "svg") [ + (Html.attribute "viewBox") "0 -6 51 58", + (Html.attribute "fill") "#7c38f5", + (Html.attribute "xmlns") "http://www.w3.org/2000/svg", + (Html.attribute "aria-labelledby") "logo-link", + (Html.attribute "role") "img", + class "roc-logo" + ] [ + (Html.element "title") [id "logo-link"] [text "Return to Roc Home"], + (Html.element "polygon") [ + (Html.attribute "role") "presentation", + (Html.attribute "points") "0,0 23.8834,3.21052 37.2438,19.0101 45.9665,16.6324 50.5,22 45,22 44.0315,26.3689 26.4673,39.3424 27.4527,45.2132 17.655,53 23.6751,22.7086", + ] [], + ] diff --git a/www/wip_new_website/static/site.css b/www/wip_new_website/static/site.css new file mode 100644 index 00000000000..66781081d07 --- /dev/null +++ b/www/wip_new_website/static/site.css @@ -0,0 +1,587 @@ +:root { + /* WCAG AAA Compliant colors */ + --code-bg: #f4f8f9; + --gray: #717171; + --orange: #bf5000; + --green: #0b8400; + --cyan: #067c94; + --blue: #05006d; + --violet: #7c38f5; + --violet-bg: #ece2fd; + --magenta: #a20031; + + --link-color: var(--violet); + --code-link-color: var(--violet); + --text-color: #000; + --text-hover-color: var(--violet); + --body-bg-color: #ffffff; + --border-color: #717171; + --faded-color: #4c4c4c; + --font-sans: -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, + sans-serif; + --font-mono: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, + monospace; + --top-header-height: 67px; + --sidebar-width: 280px; +} + +html { + line-height: 1.5rem; + background: var(--body-bg-color); + color: var(--text-color); + font-family: "Lato", sans-serif; +} + +html, +body { + margin: 0; + padding: 0; +} + +footer { + font-size: 0.9rem; + max-width: 1024px; + margin: 0 auto; + font-size: 14px; +} + +section p:last-child { + margin-bottom: 0; +} + +aside { + margin-left: 4rem; +} + +a { + text-decoration: none; + color: var(--link-color); +} + +a:hover { + text-decoration: underline; +} + +li { + margin-bottom: 0.5rem; +} + +#top-bar { + background-color: var(--violet-bg); + width: 100%; + height: 50px; +} + +#top-bar nav { + max-width: 1024px; + margin: 0 auto; + display: flex; + justify-content: space-between; +} + +#top-bar .home-link { + display: flex; + color: var(--top-bar-fg); + font-size: 1.8rem; + padding: 10px; +} + +#top-bar-links { + display: flex; +} + +#top-bar-links a, +#top-bar-links label { + box-sizing: border-box; + color: var(--top-bar-fg); + font-size: 1.1rem; + display: block; + height: 40px; + padding: 12px 16px; + margin: 0 2px; +} + +main { + max-width: var(--body-max-width); + margin: 36px auto; + padding: 0 12px; +} + +code, +samp { + font-family: var(--font-mono); + color: var(--code-color); + background-color: var(--code-bg); + display: inline-block; +} + +p code, +td code, +li code, +th code, +samp { + padding: 0 8px; +} + +code a, +a code { + text-decoration: none; + color: var(--code-link-color); + background: none; + padding: 0; +} + +code a:visited, +a:visited code { + color: var(--code-link-color); +} + +pre { + position: relative; + margin-bottom: 16px; + padding: 8px 16px; + box-sizing: border-box; + background-color: var(--code-bg); + overflow-x: hidden; + word-wrap: normal; + font-size: 1.2rem; + line-height: 1.76em; + white-space: pre; +} + +pre > samp { + overflow-x: auto; + display: block; +} + +.repl-prompt:before { + /* Add this using CSS so it isn't selectable, which would be annoying when trying to copy/paste! */ + color: var(--repl-prompt); + content: "» "; +} + +.repl-err { + color: var(--magenta); +} + +/* Tables */ + +table { + border-collapse: collapse; + overflow-x: auto; + border: 2px solid #f0f0f0; +} + +thead { + border: none; +} + +tbody { + border: none; +} + +tr { + border: none; + border-top: 2px solid #f0f0f0; +} + +th, +td { + border: none; + border-right: 2px solid #f0f0f0; + padding: 12px; +} + +th:last-child, +td:last-child { + border-right: none; +} + +p, +aside, +li, +footer { + font-size: 1.2rem; + line-height: 1.85rem; +} + +/* Mobile-friendly screen width */ +@media only screen and (max-device-width: 480px) and (orientation: portrait) { + p, + aside, + li, + footer, + code, + samp, + .code-snippet { + font-size: 16px; + } + + h1 code, + h2 code, + h3 code, + h4 code, + h5 code { + font-size: inherit; + } + + code { + white-space: normal; + } + + #tutorial-toc-toggle-label, + #close-tutorial-toc { + display: block; + } + + #tutorial-toc-toggle:checked + #tutorial-toc { + display: block; + } + + #tutorial-toc { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow-y: auto; + margin: 0; + padding-right: 120px; + border: 0; + } + + h1, + h2, + h3, + h4, + h5, + h6, + p, + code { + word-break: break-word !important; + } + + h1, + h2, + h3, + h4, + h5 { + line-height: 1.2em !important; + font-size: 2rem !important; + } + + #top-bar-links a, + #top-bar-links label { + padding: 12px 8px; + margin: 0; + } +} + +@font-face { + font-family: "Permanent Marker"; + font-style: normal; + font-weight: 400; + src: url("/fonts/permanent-marker-v16-latin/permanent-marker-v16-latin-regular.woff2") + format("woff2"), + url("/fonts/permanent-marker-v16-latin/permanent-marker-v16-latin-regular.woff") + format("woff"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, + U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, + U+2215, U+FEFF, U+FFFD; +} + +/* latin-ext */ +@font-face { + font-family: "Merriweather"; + font-style: normal; + font-weight: 400; + src: url("/fonts/merriweather-v30-latin-ext_latin/merriweather-v30-latin-ext_latin-regular.woff2") + format("woff2"), + url("/fonts/merriweather-v30-latin-ext_latin/merriweather-v30-latin-ext_latin-regular.woff") + format("woff"); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, + U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +/* latin */ +@font-face { + font-family: "Merriweather"; + font-style: normal; + font-weight: 400; + src: url("/fonts/merriweather-v30-latin/merriweather-v30-latin-regular.woff2") + format("woff2"), + url("/fonts/merriweather-v30-latin/merriweather-v30-latin-regular.woff") + format("woff"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, + U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, + U+2215, U+FEFF, U+FFFD; +} + +/* latin-ext */ +@font-face { + font-family: "Merriweather Sans"; + font-style: normal; + font-weight: 400; + src: url("/fonts/merriweather-sans-v22-latin-ext_latin/merriweather-sans-v22-latin-ext_latin-regular.woff2") + format("woff2"), + url("/fonts/merriweather-sans-v22-latin-ext_latin/merriweather-sans-v22-latin-ext_latin-regular.woff") + format("woff"); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, + U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +/* latin */ +@font-face { + font-family: "Merriweather Sans"; + font-style: normal; + font-weight: 400; + src: url("/fonts/merriweather-sans-v22-latin/merriweather-sans-v22-latin-regular.woff2") + format("woff2"), + url("/fonts/merriweather-sans-v22-latin/merriweather-sans-v22-latin-regular.woff") + format("woff"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, + U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, + U+2215, U+FEFF, U+FFFD; +} + +/* latin-ext */ +@font-face { + font-family: "Lato"; + font-style: normal; + font-weight: 400; + src: url("/fonts/lato-v23-latin-ext_latin/lato-v23-latin-ext_latin-regular.woff2") + format("woff2"), + url("/fonts/lato-v23-latin-ext_latin/lato-v23-latin-ext_latin-regular.woff") + format("woff"); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, + U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +/* latin */ +@font-face { + font-family: "Lato"; + font-style: normal; + font-weight: 400; + src: url("/fonts/lato-v23-latin/lato-v23-latin-regular.woff2") + format("woff2"), + url("/fonts/lato-v23-latin/lato-v23-latin-regular.woff") format("woff"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, + U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, + U+2215, U+FEFF, U+FFFD; +} + +/* latin-ext */ +@font-face { + font-family: "Source Code Pro"; + font-style: normal; + font-weight: 400; + src: url("/fonts/source-code-pro-v22-latin-ext_latin/source-code-pro-v22-latin-ext_latin-regular.woff2") + format("woff2"), + url("/fonts/source-code-pro-v22-latin-ext_latin/source-code-pro-v22-latin-ext_latin-regular.woff") + format("woff"); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, + U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +/* latin */ +@font-face { + font-family: "Source Code Pro"; + font-style: normal; + font-weight: 400; + src: url("/fonts/source-code-pro-v22-latin/source-code-pro-v22-latin-regular.woff2") + format("woff2"), + url("/fonts/source-code-pro-v22-latin/source-code-pro-v22-latin-regular.woff") + format("woff"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, + U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, + U+2215, U+FEFF, U+FFFD; +} + +@media (prefers-color-scheme: dark) { + :root { + /* WCAG AAA Compliant colors */ + /* WCAG AAA Compliant colors */ + --code-bg: #202746; + --gray: #b6b6b6; + --orange: #fd6e08; + --green: #8ecc88; + --cyan: #12c9be; + --blue: #b1afdf; + --violet: #caadfb; + --violet-bg: #332944; + --magenta: #f39bac; + + --link-color: var(--violet); + --code-link-color: var(--violet); + --text-color: #eaeaea; + --body-bg-color: #0e0e0f; + --border-color: var(--gray); + --code-color: #eeeeee; + --logo-solid: #8f8f8f; + --faded-color: #bbbbbb; + --gray: #6e6e6e; + } + + h1, + h2, + h3, + h4, + h5 { + text-shadow: none; + } + + html { + scrollbar-color: #444444 #2f2f2f; + } + + table, + tr, + th, + td { + border-color: var(--gray); + } +} + +/* Comments `#` and Documentation comments `##` */ +samp .comment, +code .comment { + color: var(--green); +} + +/* Number, String, Tag literals */ +samp .storage.type, +code .storage.type, +samp .string, +code .string, +samp .string.begin, +code .string.begin, +samp .string.end, +code .string.end, +samp .constant, +code .constant, +samp .literal, +code .literal { + color: var(--cyan); +} + +/* Keywords and punctuation */ +samp .keyword, +code .keyword, +samp .punctuation.section, +code .punctuation.section, +samp .punctuation.separator, +code .punctuation.separator, +samp .punctuation.terminator, +code .punctuation.terminator, +samp .kw, +code .kw { + color: var(--magenta); +} + +/* Operators */ +samp .op, +code .op, +samp .keyword.operator, +code .keyword.operator { + color: var(--orange); +} + +/* Delimieters */ +samp .delimeter, +code .delimeter { + color: var(--gray); +} + +/* Variables modules and field names */ +samp .function, +code .function, +samp .meta.group, +code .meta.group, +samp .meta.block, +code .meta.block, +samp .lowerident, +code .lowerident { + color: var(--blue); +} + +/* Types, Tags, and Modules */ +samp .type, +code .type, +samp .meta.path, +code .meta.path, +samp .upperident, +code .upperident { + color: var(--green); +} + +samp .dim, +code .dim { + opacity: 0.55; +} + +.button-container { + position: absolute; + top: 0; + right: 0; +} + +.copy-button { + background: var(--code-bg); + border: 1px solid var(--magenta); + color: var(--magenta); + display: inline-block; + cursor: pointer; + margin: 8px; +} + +.copy-button:hover { + border-color: var(--green); + color: var(--green); +} + +.roc-logo { + width: 40px; + height: 40px; + margin: 0 auto; +} + +.home-goals-container { + display: flex; + justify-content: space-between; +} + +.home-goals-column { + flex-basis: 30%; + padding: 20px; + background-color: var(--violet-bg); + margin-right: 20px; +} + +.home-goals-column:last-child { + margin-right: 0; +} + +.home-goals-title { + font-size: 18px; + font-weight: bold; + margin-bottom: 10px; +} + +.home-goals-description { + font-size: 14px; + line-height: 1.5; + margin-bottom: 10px; +} + +.home-goals-learn-more { + color: var(--violet); + text-decoration: none; +} + +#footer { + background-color: var(--violet-bg); + color: var(--text-color); + padding: 20px; + text-align: center; + } \ No newline at end of file