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):
[](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