mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
Merge branch 'trunk' into morphic-static-strings
This commit is contained in:
commit
d263016a84
35 changed files with 917 additions and 589 deletions
2
.envrc
2
.envrc
|
@ -93,7 +93,7 @@ use_nix() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log_status "use nix: updating cache"
|
log_status "use nix: updating cache"
|
||||||
nix-shell --pure "${drv}" --show-trace --run "$(join_args "$direnv" dump bash)" > "${dump}"
|
nix-shell "${drv}" --show-trace --run "$(join_args "$direnv" dump bash)" > "${dump}"
|
||||||
if [[ "${?}" -ne 0 ]] || [[ ! -f "${dump}" ]] || ! grep -q IN_NIX_SHELL "${dump}"; then
|
if [[ "${?}" -ne 0 ]] || [[ ! -f "${dump}" ]] || ! grep -q IN_NIX_SHELL "${dump}"; then
|
||||||
rm -rf "${wd}"
|
rm -rf "${wd}"
|
||||||
fail "use nix: was not able to update the cache of the environment. Please run 'direnv reload' to try again."
|
fail "use nix: was not able to update the cache of the environment. Please run 'direnv reload' to try again."
|
||||||
|
|
|
@ -20,7 +20,7 @@ For debugging LLVM IR, we use [DebugIR](https://github.com/vaivaswatha/debugir).
|
||||||
|
|
||||||
### libunwind & libc++-dev
|
### libunwind & libc++-dev
|
||||||
|
|
||||||
MacOS systems should already have `libunwind`, but other systems will need to install it (On Ubuntu, this can be donw with `sudo apt-get install libunwind-dev`).
|
MacOS systems should already have `libunwind`, but other systems will need to install it (On Ubuntu, this can be done with `sudo apt-get install libunwind-dev`).
|
||||||
Some systems may already have `libc++-dev` on them, but if not, you may need to install it. (On Ubuntu, this can be done with `sudo apt-get install libc++-dev`.)
|
Some systems may already have `libc++-dev` on them, but if not, you may need to install it. (On Ubuntu, this can be done with `sudo apt-get install libc++-dev`.)
|
||||||
|
|
||||||
### libcxb libraries
|
### libcxb libraries
|
||||||
|
|
391
Cargo.lock
generated
391
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -14,6 +14,8 @@ members = [
|
||||||
"compiler/reporting",
|
"compiler/reporting",
|
||||||
"compiler/fmt",
|
"compiler/fmt",
|
||||||
"compiler/mono",
|
"compiler/mono",
|
||||||
|
"compiler/test_mono_macros",
|
||||||
|
"compiler/test_mono",
|
||||||
"compiler/load",
|
"compiler/load",
|
||||||
"compiler/gen",
|
"compiler/gen",
|
||||||
"compiler/gen_dev",
|
"compiler/gen_dev",
|
||||||
|
@ -21,6 +23,7 @@ members = [
|
||||||
"compiler/arena_pool",
|
"compiler/arena_pool",
|
||||||
"compiler/test_gen",
|
"compiler/test_gen",
|
||||||
"vendor/ena",
|
"vendor/ena",
|
||||||
|
"vendor/inkwell",
|
||||||
"vendor/pathfinding",
|
"vendor/pathfinding",
|
||||||
"vendor/pretty",
|
"vendor/pretty",
|
||||||
"editor",
|
"editor",
|
||||||
|
|
|
@ -62,24 +62,7 @@ inlinable_string = "0.1"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
libloading = "0.6"
|
libloading = "0.6"
|
||||||
|
|
||||||
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
|
inkwell = { path = "../vendor/inkwell" }
|
||||||
#
|
|
||||||
# The reason for this fork is that the way Inkwell is designed, you have to use
|
|
||||||
# a particular branch (e.g. "llvm8-0") in Cargo.toml. That would be fine, except that
|
|
||||||
# breaking changes get pushed directly to that branch, which breaks our build
|
|
||||||
# without warning.
|
|
||||||
#
|
|
||||||
# We tried referencing a specific rev on TheDan64/inkwell directly (instead of branch),
|
|
||||||
# but although that worked locally, it did not work on GitHub Actions. (After a few
|
|
||||||
# hours of investigation, gave up trying to figure out why.) So this is the workaround:
|
|
||||||
# having an immutable tag on the rtfeldman/inkwell fork which points to
|
|
||||||
# a particular "release" of Inkwell.
|
|
||||||
#
|
|
||||||
# When we want to update Inkwell, we can sync up rtfeldman/inkwell to the latest
|
|
||||||
# commit of TheDan64/inkwell, push a new tag which points to the latest commit,
|
|
||||||
# change the tag value in this Cargo.toml to point to that tag, and `cargo update`.
|
|
||||||
# This way, GitHub Actions works and nobody's builds get broken.
|
|
||||||
inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm10-0.release4", features = [ "llvm10-0" ] }
|
|
||||||
target-lexicon = "0.10"
|
target-lexicon = "0.10"
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
|
|
||||||
|
|
|
@ -28,24 +28,7 @@ inlinable_string = "0.1.0"
|
||||||
libloading = "0.6"
|
libloading = "0.6"
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
|
inkwell = { path = "../../vendor/inkwell" }
|
||||||
#
|
|
||||||
# The reason for this fork is that the way Inkwell is designed, you have to use
|
|
||||||
# a particular branch (e.g. "llvm8-0") in Cargo.toml. That would be fine, except that
|
|
||||||
# breaking changes get pushed directly to that branch, which breaks our build
|
|
||||||
# without warning.
|
|
||||||
#
|
|
||||||
# We tried referencing a specific rev on TheDan64/inkwell directly (instead of branch),
|
|
||||||
# but although that worked locally, it did not work on GitHub Actions. (After a few
|
|
||||||
# hours of investigation, gave up trying to figure out why.) So this is the workaround:
|
|
||||||
# having an immutable tag on the rtfeldman/inkwell fork which points to
|
|
||||||
# a particular "release" of Inkwell.
|
|
||||||
#
|
|
||||||
# When we want to update Inkwell, we can sync up rtfeldman/inkwell to the latest
|
|
||||||
# commit of TheDan64/inkwell, push a new tag which points to the latest commit,
|
|
||||||
# change the tag value in this Cargo.toml to point to that tag, and `cargo update`.
|
|
||||||
# This way, GitHub Actions works and nobody's builds get broken.
|
|
||||||
inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm10-0.release4", features = [ "llvm10-0" ] }
|
|
||||||
target-lexicon = "0.10"
|
target-lexicon = "0.10"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -100,20 +100,22 @@ pub fn gen_from_mono_module(
|
||||||
|
|
||||||
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
|
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
|
||||||
debug_assert!(kind_id > 0);
|
debug_assert!(kind_id > 0);
|
||||||
let attr = context.create_enum_attribute(kind_id, 1);
|
let enum_attr = context.create_enum_attribute(kind_id, 1);
|
||||||
|
|
||||||
for function in FunctionIterator::from_module(module) {
|
for function in FunctionIterator::from_module(module) {
|
||||||
let name = function.get_name().to_str().unwrap();
|
let name = function.get_name().to_str().unwrap();
|
||||||
|
|
||||||
|
// mark our zig-defined builtins as internal
|
||||||
if name.starts_with("roc_builtins") {
|
if name.starts_with("roc_builtins") {
|
||||||
function.set_linkage(Linkage::Internal);
|
function.set_linkage(Linkage::Internal);
|
||||||
}
|
}
|
||||||
|
|
||||||
if name.starts_with("roc_builtins.dict") || name.starts_with("dict.RocDict") {
|
if name.starts_with("roc_builtins.dict")
|
||||||
function.add_attribute(AttributeLoc::Function, attr);
|
|| name.starts_with("dict.RocDict")
|
||||||
}
|
|| name.starts_with("roc_builtins.list")
|
||||||
|
|| name.starts_with("list.RocList")
|
||||||
if name.starts_with("roc_builtins.list") || name.starts_with("list.RocList") {
|
{
|
||||||
function.add_attribute(AttributeLoc::Function, attr);
|
function.add_attribute(AttributeLoc::Function, enum_attr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
5
compiler/builtins/bitcode/build.sh
Executable file
5
compiler/builtins/bitcode/build.sh
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
zig build-obj src/main.zig -O ReleaseFast -femit-llvm-ir=builtins.ll -femit-bin=builtins.o --strip
|
|
@ -10,28 +10,36 @@ use std::str;
|
||||||
fn main() {
|
fn main() {
|
||||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
|
||||||
|
let big_sur_path = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib";
|
||||||
|
let use_build_script = Path::new(big_sur_path).exists();
|
||||||
|
|
||||||
// "." is relative to where "build.rs" is
|
// "." is relative to where "build.rs" is
|
||||||
let build_script_dir_path = fs::canonicalize(Path::new(".")).unwrap();
|
let build_script_dir_path = fs::canonicalize(Path::new(".")).unwrap();
|
||||||
let bitcode_path = build_script_dir_path.join("bitcode");
|
let bitcode_path = build_script_dir_path.join("bitcode");
|
||||||
|
|
||||||
let src_obj_path = bitcode_path.join("builtins.o");
|
let src_obj_path = bitcode_path.join("builtins.o");
|
||||||
let src_obj = src_obj_path.to_str().expect("Invalid src object path");
|
let src_obj = src_obj_path.to_str().expect("Invalid src object path");
|
||||||
println!("Compiling zig object to: {}", src_obj);
|
|
||||||
|
|
||||||
|
let dest_ir_path = bitcode_path.join("builtins.ll");
|
||||||
|
let dest_ir = dest_ir_path.to_str().expect("Invalid dest ir path");
|
||||||
|
|
||||||
|
if use_build_script {
|
||||||
|
println!("Compiling zig object & ir to: {} and {}", src_obj, dest_ir);
|
||||||
|
run_command_with_no_args(&bitcode_path, "./build.sh");
|
||||||
|
} else {
|
||||||
|
println!("Compiling zig object to: {}", src_obj);
|
||||||
run_command(&bitcode_path, "zig", &["build", "object", "-Drelease=true"]);
|
run_command(&bitcode_path, "zig", &["build", "object", "-Drelease=true"]);
|
||||||
|
|
||||||
|
println!("Compiling ir to: {}", dest_ir);
|
||||||
|
run_command(&bitcode_path, "zig", &["build", "ir", "-Drelease=true"]);
|
||||||
|
}
|
||||||
|
|
||||||
let dest_obj_path = Path::new(&out_dir).join("builtins.o");
|
let dest_obj_path = Path::new(&out_dir).join("builtins.o");
|
||||||
let dest_obj = dest_obj_path.to_str().expect("Invalid dest object path");
|
let dest_obj = dest_obj_path.to_str().expect("Invalid dest object path");
|
||||||
println!("Moving zig object to: {}", dest_obj);
|
println!("Moving zig object to: {}", dest_obj);
|
||||||
|
|
||||||
run_command(&bitcode_path, "mv", &[src_obj, dest_obj]);
|
run_command(&bitcode_path, "mv", &[src_obj, dest_obj]);
|
||||||
|
|
||||||
let dest_ir_path = bitcode_path.join("builtins.ll");
|
|
||||||
let dest_ir = dest_ir_path.to_str().expect("Invalid dest ir path");
|
|
||||||
println!("Compiling ir to: {}", dest_ir);
|
|
||||||
|
|
||||||
run_command(&bitcode_path, "zig", &["build", "ir", "-Drelease=true"]);
|
|
||||||
|
|
||||||
let dest_bc_path = bitcode_path.join("builtins.bc");
|
let dest_bc_path = bitcode_path.join("builtins.bc");
|
||||||
let dest_bc = dest_bc_path.to_str().expect("Invalid dest bc path");
|
let dest_bc = dest_bc_path.to_str().expect("Invalid dest bc path");
|
||||||
println!("Compiling bitcode to: {}", dest_bc);
|
println!("Compiling bitcode to: {}", dest_bc);
|
||||||
|
@ -78,6 +86,25 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_command_with_no_args<P: AsRef<Path>>(path: P, command_str: &str) {
|
||||||
|
let output_result = Command::new(OsStr::new(&command_str))
|
||||||
|
.current_dir(path)
|
||||||
|
.output();
|
||||||
|
match output_result {
|
||||||
|
Ok(output) => match output.status.success() {
|
||||||
|
true => (),
|
||||||
|
false => {
|
||||||
|
let error_str = match str::from_utf8(&output.stderr) {
|
||||||
|
Ok(stderr) => stderr.to_string(),
|
||||||
|
Err(_) => format!("Failed to run \"{}\"", command_str),
|
||||||
|
};
|
||||||
|
panic!("{} failed: {}", command_str, error_str);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(reason) => panic!("{} failed: {}", command_str, reason),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_zig_files(dir: &Path, cb: &dyn Fn(&Path)) -> io::Result<()> {
|
fn get_zig_files(dir: &Path, cb: &dyn Fn(&Path)) -> io::Result<()> {
|
||||||
if dir.is_dir() {
|
if dir.is_dir() {
|
||||||
for entry in fs::read_dir(dir)? {
|
for entry in fs::read_dir(dir)? {
|
||||||
|
|
|
@ -20,24 +20,7 @@ im-rc = "14" # im and im-rc should always have the same version!
|
||||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
inlinable_string = "0.1"
|
inlinable_string = "0.1"
|
||||||
either = "1.6.1"
|
either = "1.6.1"
|
||||||
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
|
inkwell = { path = "../../vendor/inkwell" }
|
||||||
#
|
|
||||||
# The reason for this fork is that the way Inkwell is designed, you have to use
|
|
||||||
# a particular branch (e.g. "llvm8-0") in Cargo.toml. That would be fine, except that
|
|
||||||
# breaking changes get pushed directly to that branch, which breaks our build
|
|
||||||
# without warning.
|
|
||||||
#
|
|
||||||
# We tried referencing a specific rev on TheDan64/inkwell directly (instead of branch),
|
|
||||||
# but although that worked locally, it did not work on GitHub Actions. (After a few
|
|
||||||
# hours of investigation, gave up trying to figure out why.) So this is the workaround:
|
|
||||||
# having an immutable tag on the rtfeldman/inkwell fork which points to
|
|
||||||
# a particular "release" of Inkwell.
|
|
||||||
#
|
|
||||||
# When we want to update Inkwell, we can sync up rtfeldman/inkwell to the latest
|
|
||||||
# commit of TheDan64/inkwell, push a new tag which points to the latest commit,
|
|
||||||
# change the tag value in this Cargo.toml to point to that tag, and `cargo update`.
|
|
||||||
# This way, GitHub Actions works and nobody's builds get broken.
|
|
||||||
inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm10-0.release4", features = [ "llvm10-0" ] }
|
|
||||||
target-lexicon = "0.10"
|
target-lexicon = "0.10"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -5791,7 +5791,7 @@ fn get_gxx_personality_v0<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> Function
|
||||||
None => {
|
None => {
|
||||||
let personality_func = add_func(
|
let personality_func = add_func(
|
||||||
module,
|
module,
|
||||||
"__gxx_personality_v0",
|
name,
|
||||||
context.i64_type().fn_type(&[], false),
|
context.i64_type().fn_type(&[], false),
|
||||||
Linkage::External,
|
Linkage::External,
|
||||||
C_CALL_CONV,
|
C_CALL_CONV,
|
||||||
|
@ -5843,7 +5843,7 @@ fn cxa_begin_catch<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let cxa_begin_catch = add_func(
|
let cxa_begin_catch = add_func(
|
||||||
module,
|
module,
|
||||||
"__cxa_begin_catch",
|
name,
|
||||||
u8_ptr.fn_type(&[u8_ptr.into()], false),
|
u8_ptr.fn_type(&[u8_ptr.into()], false),
|
||||||
Linkage::External,
|
Linkage::External,
|
||||||
C_CALL_CONV,
|
C_CALL_CONV,
|
||||||
|
|
|
@ -5928,36 +5928,49 @@ fn reuse_function_symbol<'a>(
|
||||||
) -> Stmt<'a> {
|
) -> Stmt<'a> {
|
||||||
match procs.partial_procs.get(&original) {
|
match procs.partial_procs.get(&original) {
|
||||||
None => {
|
None => {
|
||||||
let is_imported = env.is_imported_symbol(original);
|
|
||||||
|
|
||||||
match arg_var {
|
match arg_var {
|
||||||
Some(arg_var) if is_imported => {
|
Some(arg_var) if env.is_imported_symbol(original) => {
|
||||||
let layout = layout_cache
|
let layout = layout_cache
|
||||||
.from_var(env.arena, arg_var, env.subs)
|
.from_var(env.arena, arg_var, env.subs)
|
||||||
.expect("creating layout does not fail");
|
.expect("creating layout does not fail");
|
||||||
|
|
||||||
|
if procs.imported_module_thunks.contains(&original) {
|
||||||
|
let top_level = TopLevelFunctionLayout::new(env.arena, &[], layout);
|
||||||
|
procs.insert_passed_by_name(
|
||||||
|
env,
|
||||||
|
arg_var,
|
||||||
|
original,
|
||||||
|
top_level,
|
||||||
|
layout_cache,
|
||||||
|
);
|
||||||
|
|
||||||
|
force_thunk(env, original, layout, symbol, env.arena.alloc(result))
|
||||||
|
} else {
|
||||||
let top_level = TopLevelFunctionLayout::from_layout(env.arena, layout);
|
let top_level = TopLevelFunctionLayout::from_layout(env.arena, layout);
|
||||||
|
procs.insert_passed_by_name(
|
||||||
|
env,
|
||||||
|
arg_var,
|
||||||
|
original,
|
||||||
|
top_level,
|
||||||
|
layout_cache,
|
||||||
|
);
|
||||||
|
|
||||||
procs.insert_passed_by_name(env, arg_var, original, top_level, layout_cache);
|
let_empty_struct(symbol, env.arena.alloc(result))
|
||||||
|
}
|
||||||
// an imported symbol is either a function, or a top-level 0-argument thunk
|
|
||||||
// it never has closure data, so we use the empty struct
|
|
||||||
return let_empty_struct(symbol, env.arena.alloc(result));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
// danger: a foreign symbol may not be specialized!
|
// danger: a foreign symbol may not be specialized!
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
!is_imported,
|
!env.is_imported_symbol(original),
|
||||||
"symbol {:?} while processing module {:?}",
|
"symbol {:?} while processing module {:?}",
|
||||||
original,
|
original,
|
||||||
(env.home, &arg_var),
|
(env.home, &arg_var),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Some(partial_proc) => {
|
Some(partial_proc) => {
|
||||||
let arg_var = arg_var.unwrap_or(partial_proc.annotation);
|
let arg_var = arg_var.unwrap_or(partial_proc.annotation);
|
||||||
|
|
|
@ -276,7 +276,8 @@ impl<'a> LambdaSet<'a> {
|
||||||
|
|
||||||
use UnionVariant::*;
|
use UnionVariant::*;
|
||||||
match variant {
|
match variant {
|
||||||
Never | Unit | UnitWithArguments => Layout::Struct(&[]),
|
Never => Layout::Union(UnionLayout::NonRecursive(&[])),
|
||||||
|
Unit | UnitWithArguments => Layout::Struct(&[]),
|
||||||
BoolUnion { .. } => Layout::Builtin(Builtin::Int1),
|
BoolUnion { .. } => Layout::Builtin(Builtin::Int1),
|
||||||
ByteUnion(_) => Layout::Builtin(Builtin::Int8),
|
ByteUnion(_) => Layout::Builtin(Builtin::Int8),
|
||||||
Unwrapped(_tag_name, layouts) => Layout::Struct(layouts.into_bump_slice()),
|
Unwrapped(_tag_name, layouts) => Layout::Struct(layouts.into_bump_slice()),
|
||||||
|
|
|
@ -270,11 +270,12 @@ mod test_mono {
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.11 = 1i64;
|
let Test.12 = 1i64;
|
||||||
let Test.9 = 1i64;
|
let Test.10 = 1i64;
|
||||||
let Test.10 = 2i64;
|
let Test.11 = 2i64;
|
||||||
let Test.5 = These Test.11 Test.9 Test.10;
|
let Test.5 = These Test.12 Test.10 Test.11;
|
||||||
switch Test.5:
|
let Test.9 = Index 0 Test.5;
|
||||||
|
switch Test.9:
|
||||||
case 2:
|
case 2:
|
||||||
let Test.2 = Index 1 Test.5;
|
let Test.2 = Index 1 Test.5;
|
||||||
ret Test.2;
|
ret Test.2;
|
||||||
|
|
|
@ -30,24 +30,7 @@ inlinable_string = "0.1"
|
||||||
either = "1.6.1"
|
either = "1.6.1"
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
|
inkwell = { path = "../../vendor/inkwell" }
|
||||||
#
|
|
||||||
# The reason for this fork is that the way Inkwell is designed, you have to use
|
|
||||||
# a particular branch (e.g. "llvm8-0") in Cargo.toml. That would be fine, except that
|
|
||||||
# breaking changes get pushed directly to that branch, which breaks our build
|
|
||||||
# without warning.
|
|
||||||
#
|
|
||||||
# We tried referencing a specific rev on TheDan64/inkwell directly (instead of branch),
|
|
||||||
# but although that worked locally, it did not work on GitHub Actions. (After a few
|
|
||||||
# hours of investigation, gave up trying to figure out why.) So this is the workaround:
|
|
||||||
# having an immutable tag on the rtfeldman/inkwell fork which points to
|
|
||||||
# a particular "release" of Inkwell.
|
|
||||||
#
|
|
||||||
# When we want to update Inkwell, we can sync up rtfeldman/inkwell to the latest
|
|
||||||
# commit of TheDan64/inkwell, push a new tag which points to the latest commit,
|
|
||||||
# change the tag value in this Cargo.toml to point to that tag, and `cargo update`.
|
|
||||||
# This way, GitHub Actions works and nobody's builds get broken.
|
|
||||||
inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm10-0.release4", features = [ "llvm10-0" ] }
|
|
||||||
target-lexicon = "0.10"
|
target-lexicon = "0.10"
|
||||||
libloading = "0.6"
|
libloading = "0.6"
|
||||||
|
|
||||||
|
|
40
compiler/test_mono/Cargo.toml
Normal file
40
compiler/test_mono/Cargo.toml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
[package]
|
||||||
|
name = "test_mono"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["The Roc Contributors"]
|
||||||
|
license = "UPL-1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
roc_collections = { path = "../collections" }
|
||||||
|
roc_region = { path = "../region" }
|
||||||
|
roc_module = { path = "../module" }
|
||||||
|
roc_problem = { path = "../problem" }
|
||||||
|
roc_types = { path = "../types" }
|
||||||
|
roc_builtins = { path = "../builtins" }
|
||||||
|
roc_constrain = { path = "../constrain" }
|
||||||
|
roc_unify = { path = "../unify" }
|
||||||
|
roc_solve = { path = "../solve" }
|
||||||
|
roc_reporting = { path = "../reporting" }
|
||||||
|
roc_load = { path = "../load" }
|
||||||
|
roc_can = { path = "../can" }
|
||||||
|
roc_parse = { path = "../parse" }
|
||||||
|
roc_build = { path = "../build" }
|
||||||
|
roc_mono = { path = "../mono" }
|
||||||
|
test_mono_macros = { path = "../test_mono_macros" }
|
||||||
|
im = "14" # im and im-rc should always have the same version!
|
||||||
|
im-rc = "14" # im and im-rc should always have the same version!
|
||||||
|
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
|
inlinable_string = "0.1"
|
||||||
|
either = "1.6.1"
|
||||||
|
indoc = "0.3.3"
|
||||||
|
libc = "0.2"
|
||||||
|
target-lexicon = "0.10"
|
||||||
|
libloading = "0.6"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
pretty_assertions = "0.5.1"
|
||||||
|
indoc = "0.3.3"
|
||||||
|
quickcheck = "0.8"
|
||||||
|
quickcheck_macros = "0.8"
|
||||||
|
bumpalo = { version = "3.6.1", features = ["collections"] }
|
25
compiler/test_mono/generated/ir_int_add.txt
Normal file
25
compiler/test_mono/generated/ir_int_add.txt
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
procedure List.7 (#Attr.2):
|
||||||
|
let Test.6 = lowlevel ListLen #Attr.2;
|
||||||
|
ret Test.6;
|
||||||
|
|
||||||
|
procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
|
let Test.5 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
|
ret Test.5;
|
||||||
|
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.11 = 1i64;
|
||||||
|
let Test.12 = 2i64;
|
||||||
|
let Test.1 = Array [Test.11, Test.12];
|
||||||
|
let Test.9 = 5i64;
|
||||||
|
let Test.10 = 4i64;
|
||||||
|
invoke Test.7 = CallByName Num.24 Test.9 Test.10 catch
|
||||||
|
dec Test.1;
|
||||||
|
unreachable;
|
||||||
|
let Test.8 = 3i64;
|
||||||
|
invoke Test.3 = CallByName Num.24 Test.7 Test.8 catch
|
||||||
|
dec Test.1;
|
||||||
|
unreachable;
|
||||||
|
let Test.4 = CallByName List.7 Test.1;
|
||||||
|
dec Test.1;
|
||||||
|
let Test.2 = CallByName Num.24 Test.3 Test.4;
|
||||||
|
ret Test.2;
|
3
compiler/test_mono/generated/ir_int_literal.txt
Normal file
3
compiler/test_mono/generated/ir_int_literal.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.1 = 5i64;
|
||||||
|
ret Test.1;
|
184
compiler/test_mono/src/lib.rs
Normal file
184
compiler/test_mono/src/lib.rs
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
#![cfg(test)]
|
||||||
|
#![warn(clippy::dbg_macro)]
|
||||||
|
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||||
|
#![allow(clippy::large_enum_variant)]
|
||||||
|
// we actually want to compare against the literal float bits
|
||||||
|
#![allow(clippy::clippy::float_cmp)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate pretty_assertions;
|
||||||
|
|
||||||
|
use test_mono_macros::*;
|
||||||
|
|
||||||
|
use roc_can::builtins::builtin_defs_map;
|
||||||
|
use roc_collections::all::MutMap;
|
||||||
|
use roc_module::symbol::Symbol;
|
||||||
|
use roc_mono::ir::Proc;
|
||||||
|
|
||||||
|
use roc_mono::ir::TopLevelFunctionLayout;
|
||||||
|
|
||||||
|
fn promote_expr_to_module(src: &str) -> String {
|
||||||
|
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
|
||||||
|
|
||||||
|
for line in src.lines() {
|
||||||
|
// indent the body!
|
||||||
|
buffer.push_str(" ");
|
||||||
|
buffer.push_str(line);
|
||||||
|
buffer.push('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compiles_to_ir(test_name: &str, src: &str) {
|
||||||
|
use bumpalo::Bump;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
let arena = &Bump::new();
|
||||||
|
|
||||||
|
// let stdlib = roc_builtins::unique::uniq_stdlib();
|
||||||
|
let stdlib = roc_builtins::std::standard_stdlib();
|
||||||
|
let filename = PathBuf::from("Test.roc");
|
||||||
|
let src_dir = Path::new("fake/test/path");
|
||||||
|
|
||||||
|
let module_src;
|
||||||
|
let temp;
|
||||||
|
if src.starts_with("app") {
|
||||||
|
// this is already a module
|
||||||
|
module_src = src;
|
||||||
|
} else {
|
||||||
|
// this is an expression, promote it to a module
|
||||||
|
temp = promote_expr_to_module(src);
|
||||||
|
module_src = &temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
let exposed_types = MutMap::default();
|
||||||
|
|
||||||
|
let loaded = roc_load::file::load_and_monomorphize_from_str(
|
||||||
|
arena,
|
||||||
|
filename,
|
||||||
|
&module_src,
|
||||||
|
&stdlib,
|
||||||
|
src_dir,
|
||||||
|
exposed_types,
|
||||||
|
8,
|
||||||
|
builtin_defs_map,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut loaded = match loaded {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(roc_load::file::LoadingProblem::FormattedReport(report)) => {
|
||||||
|
println!("{}", report);
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
Err(e) => panic!("{:?}", e),
|
||||||
|
};
|
||||||
|
|
||||||
|
use roc_load::file::MonomorphizedModule;
|
||||||
|
let MonomorphizedModule {
|
||||||
|
module_id: home,
|
||||||
|
procedures,
|
||||||
|
exposed_to_host,
|
||||||
|
..
|
||||||
|
} = loaded;
|
||||||
|
|
||||||
|
let can_problems = loaded.can_problems.remove(&home).unwrap_or_default();
|
||||||
|
let type_problems = loaded.type_problems.remove(&home).unwrap_or_default();
|
||||||
|
let mono_problems = loaded.mono_problems.remove(&home).unwrap_or_default();
|
||||||
|
|
||||||
|
if !can_problems.is_empty() {
|
||||||
|
println!("Ignoring {} canonicalization problems", can_problems.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(type_problems, Vec::new());
|
||||||
|
assert_eq!(mono_problems, Vec::new());
|
||||||
|
|
||||||
|
debug_assert_eq!(exposed_to_host.len(), 1);
|
||||||
|
|
||||||
|
let main_fn_symbol = exposed_to_host.keys().copied().next().unwrap();
|
||||||
|
|
||||||
|
verify_procedures(test_name, procedures, main_fn_symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
fn verify_procedures(
|
||||||
|
test_name: &str,
|
||||||
|
procedures: MutMap<(Symbol, TopLevelFunctionLayout<'_>), Proc<'_>>,
|
||||||
|
main_fn_symbol: Symbol,
|
||||||
|
) {
|
||||||
|
let index = procedures
|
||||||
|
.keys()
|
||||||
|
.position(|(s, _)| *s == main_fn_symbol)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut procs_string = procedures
|
||||||
|
.values()
|
||||||
|
.map(|proc| proc.to_pretty(200))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let main_fn = procs_string.swap_remove(index);
|
||||||
|
|
||||||
|
procs_string.sort();
|
||||||
|
procs_string.push(main_fn);
|
||||||
|
|
||||||
|
let result = procs_string.join("\n");
|
||||||
|
|
||||||
|
let path = format!("generated/{}.txt", test_name);
|
||||||
|
std::fs::create_dir_all("generated").unwrap();
|
||||||
|
std::fs::write(&path, result).unwrap();
|
||||||
|
|
||||||
|
use std::process::Command;
|
||||||
|
let is_tracked = Command::new("git")
|
||||||
|
.args(&["ls-files", "--error-unmatch", &path])
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if !is_tracked.status.success() {
|
||||||
|
panic!(
|
||||||
|
"The file {:?} is not tracked by git. Try using `git add` on it",
|
||||||
|
&path
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let has_changes = Command::new("git")
|
||||||
|
.args(&["diff", "--color=always", &path])
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if !has_changes.status.success() {
|
||||||
|
eprintln!("`git diff {:?}` failed", &path);
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
|
||||||
|
if !has_changes.stdout.is_empty() {
|
||||||
|
println!("{}", std::str::from_utf8(&has_changes.stdout).unwrap());
|
||||||
|
panic!("Output changed: resolve conflicts and `git add` the file.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE because the Show instance of module names is different in --release mode,
|
||||||
|
// these tests would all fail. In the future, when we do interesting optimizations,
|
||||||
|
// we'll likely want some tests for --release too.
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
fn verify_procedures(
|
||||||
|
_expected: &str,
|
||||||
|
_procedures: MutMap<(Symbol, TopLevelFunctionLayout<'_>), Proc<'_>>,
|
||||||
|
_main_fn_symbol: Symbol,
|
||||||
|
) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn ir_int_literal() {
|
||||||
|
r#"
|
||||||
|
5
|
||||||
|
"#
|
||||||
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn ir_int_add() {
|
||||||
|
r#"
|
||||||
|
x = [ 1,2 ]
|
||||||
|
5 + 4 + 3 + List.len x
|
||||||
|
"#
|
||||||
|
}
|
15
compiler/test_mono_macros/Cargo.toml
Normal file
15
compiler/test_mono_macros/Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
[package]
|
||||||
|
name = "test_mono_macros"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["The Roc Contributors"]
|
||||||
|
license = "UPL-1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
syn = { version = "1.0.39", features = ["full", "extra-traits"] }
|
||||||
|
quote = "1.0.7"
|
||||||
|
darling = "0.10.2"
|
||||||
|
proc-macro2 = "1.0.24"
|
26
compiler/test_mono_macros/src/lib.rs
Normal file
26
compiler/test_mono_macros/src/lib.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn mono_test(_args: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
|
let task_fn = syn::parse_macro_input!(item as syn::ItemFn);
|
||||||
|
|
||||||
|
let args = task_fn.sig.inputs.clone();
|
||||||
|
|
||||||
|
let name = task_fn.sig.ident.clone();
|
||||||
|
let name_str = name.to_string();
|
||||||
|
let body = task_fn.block.clone();
|
||||||
|
|
||||||
|
let visibility = &task_fn.vis;
|
||||||
|
|
||||||
|
let result = quote! {
|
||||||
|
#[test]
|
||||||
|
#visibility fn #name(#args) -> () {
|
||||||
|
compiles_to_ir(#name_str, #body);
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
result.into()
|
||||||
|
}
|
|
@ -121,7 +121,7 @@ pub enum Expr2 {
|
||||||
If {
|
If {
|
||||||
cond_var: Variable, // 4B
|
cond_var: Variable, // 4B
|
||||||
expr_var: Variable, // 4B
|
expr_var: Variable, // 4B
|
||||||
branches: PoolVec<(Expr2, Expr2)>, // 8B
|
branches: PoolVec<(NodeId<Expr2>, NodeId<Expr2>)>, // 8B
|
||||||
final_else: NodeId<Expr2>, // 4B
|
final_else: NodeId<Expr2>, // 4B
|
||||||
},
|
},
|
||||||
When {
|
When {
|
||||||
|
|
|
@ -13,7 +13,7 @@ use roc_module::{ident::TagName, symbol::Symbol};
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use roc_types::{
|
use roc_types::{
|
||||||
subs::Variable,
|
subs::Variable,
|
||||||
types,
|
types::{self, AnnotationSource},
|
||||||
types::{Category, Reason},
|
types::{Category, Reason},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -479,6 +479,157 @@ pub fn constrain_expr<'a>(
|
||||||
|
|
||||||
exists(arena, flex_vars, And(and_constraints))
|
exists(arena, flex_vars, And(and_constraints))
|
||||||
}
|
}
|
||||||
|
Expr2::If {
|
||||||
|
cond_var,
|
||||||
|
expr_var,
|
||||||
|
branches,
|
||||||
|
final_else,
|
||||||
|
} => {
|
||||||
|
let expect_bool = |region| {
|
||||||
|
let bool_type = Type2::Variable(Variable::BOOL);
|
||||||
|
Expected::ForReason(Reason::IfCondition, bool_type, region)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut branch_cons = BumpVec::with_capacity_in(2 * branches.len() + 3, arena);
|
||||||
|
|
||||||
|
// TODO why does this cond var exist? is it for error messages?
|
||||||
|
// let first_cond_region = branches[0].0.region;
|
||||||
|
let cond_var_is_bool_con = Eq(
|
||||||
|
Type2::Variable(*cond_var),
|
||||||
|
expect_bool(region),
|
||||||
|
Category::If,
|
||||||
|
region,
|
||||||
|
);
|
||||||
|
|
||||||
|
branch_cons.push(cond_var_is_bool_con);
|
||||||
|
|
||||||
|
let final_else_expr = env.pool.get(*final_else);
|
||||||
|
|
||||||
|
let mut flex_vars = BumpVec::with_capacity_in(2, arena);
|
||||||
|
|
||||||
|
flex_vars.push(*cond_var);
|
||||||
|
flex_vars.push(*expr_var);
|
||||||
|
|
||||||
|
match expected {
|
||||||
|
Expected::FromAnnotation(name, arity, _, tipe) => {
|
||||||
|
let num_branches = branches.len() + 1;
|
||||||
|
|
||||||
|
for (index, branch_id) in branches.iter_node_ids().enumerate() {
|
||||||
|
let (cond_id, body_id) = env.pool.get(branch_id);
|
||||||
|
|
||||||
|
let cond = env.pool.get(*cond_id);
|
||||||
|
let body = env.pool.get(*body_id);
|
||||||
|
|
||||||
|
let cond_con =
|
||||||
|
constrain_expr(arena, env, cond, expect_bool(region), region);
|
||||||
|
|
||||||
|
let then_con = constrain_expr(
|
||||||
|
arena,
|
||||||
|
env,
|
||||||
|
body,
|
||||||
|
Expected::FromAnnotation(
|
||||||
|
name.clone(),
|
||||||
|
arity,
|
||||||
|
AnnotationSource::TypedIfBranch {
|
||||||
|
index: Index::zero_based(index),
|
||||||
|
num_branches,
|
||||||
|
},
|
||||||
|
tipe.shallow_clone(),
|
||||||
|
),
|
||||||
|
region,
|
||||||
|
);
|
||||||
|
|
||||||
|
branch_cons.push(cond_con);
|
||||||
|
branch_cons.push(then_con);
|
||||||
|
}
|
||||||
|
|
||||||
|
let else_con = constrain_expr(
|
||||||
|
arena,
|
||||||
|
env,
|
||||||
|
final_else_expr,
|
||||||
|
Expected::FromAnnotation(
|
||||||
|
name,
|
||||||
|
arity,
|
||||||
|
AnnotationSource::TypedIfBranch {
|
||||||
|
index: Index::zero_based(branches.len()),
|
||||||
|
num_branches,
|
||||||
|
},
|
||||||
|
tipe.shallow_clone(),
|
||||||
|
),
|
||||||
|
region,
|
||||||
|
);
|
||||||
|
|
||||||
|
let ast_con = Eq(
|
||||||
|
Type2::Variable(*expr_var),
|
||||||
|
Expected::NoExpectation(tipe),
|
||||||
|
Category::Storage(std::file!(), std::line!()),
|
||||||
|
region,
|
||||||
|
);
|
||||||
|
|
||||||
|
branch_cons.push(ast_con);
|
||||||
|
branch_cons.push(else_con);
|
||||||
|
|
||||||
|
exists(arena, flex_vars, And(branch_cons))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
for (index, branch_id) in branches.iter_node_ids().enumerate() {
|
||||||
|
let (cond_id, body_id) = env.pool.get(branch_id);
|
||||||
|
|
||||||
|
let cond = env.pool.get(*cond_id);
|
||||||
|
let body = env.pool.get(*body_id);
|
||||||
|
|
||||||
|
let cond_con =
|
||||||
|
constrain_expr(arena, env, cond, expect_bool(region), region);
|
||||||
|
|
||||||
|
let then_con = constrain_expr(
|
||||||
|
arena,
|
||||||
|
env,
|
||||||
|
body,
|
||||||
|
Expected::ForReason(
|
||||||
|
Reason::IfBranch {
|
||||||
|
index: Index::zero_based(index),
|
||||||
|
total_branches: branches.len(),
|
||||||
|
},
|
||||||
|
Type2::Variable(*expr_var),
|
||||||
|
// should be from body
|
||||||
|
region,
|
||||||
|
),
|
||||||
|
region,
|
||||||
|
);
|
||||||
|
|
||||||
|
branch_cons.push(cond_con);
|
||||||
|
branch_cons.push(then_con);
|
||||||
|
}
|
||||||
|
|
||||||
|
let else_con = constrain_expr(
|
||||||
|
arena,
|
||||||
|
env,
|
||||||
|
final_else_expr,
|
||||||
|
Expected::ForReason(
|
||||||
|
Reason::IfBranch {
|
||||||
|
index: Index::zero_based(branches.len()),
|
||||||
|
total_branches: branches.len() + 1,
|
||||||
|
},
|
||||||
|
Type2::Variable(*expr_var),
|
||||||
|
// should come from final_else
|
||||||
|
region,
|
||||||
|
),
|
||||||
|
region,
|
||||||
|
);
|
||||||
|
|
||||||
|
branch_cons.push(Eq(
|
||||||
|
Type2::Variable(*expr_var),
|
||||||
|
expected,
|
||||||
|
Category::Storage(std::file!(), std::line!()),
|
||||||
|
region,
|
||||||
|
));
|
||||||
|
|
||||||
|
branch_cons.push(else_con);
|
||||||
|
|
||||||
|
exists(arena, flex_vars, And(branch_cons))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => todo!("implement constaints for {:?}", expr),
|
_ => todo!("implement constaints for {:?}", expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -516,7 +516,7 @@ pub fn to_expr2<'a>(
|
||||||
output.references.union_mut(cond_output.references);
|
output.references.union_mut(cond_output.references);
|
||||||
output.references.union_mut(then_output.references);
|
output.references.union_mut(then_output.references);
|
||||||
|
|
||||||
new_branches.push((cond, then_expr));
|
new_branches.push((env.pool.add(cond), env.pool.add(then_expr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (else_expr, else_output) =
|
let (else_expr, else_output) =
|
||||||
|
|
|
@ -274,3 +274,15 @@ fn constrain_access() {
|
||||||
"Str",
|
"Str",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn constrain_if() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
if True then Green else Red
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"[ Green, Red ]*",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
1
examples/balance/.gitignore
vendored
1
examples/balance/.gitignore
vendored
|
@ -1 +0,0 @@
|
||||||
main
|
|
|
@ -1,7 +0,0 @@
|
||||||
app "main" imports [ Effect ] provides [ rocMain ] to "./platform"
|
|
||||||
|
|
||||||
|
|
||||||
rocMain : Effect.Effect {} as Fx
|
|
||||||
rocMain =
|
|
||||||
when List.len (Str.split "hello" "JJJJ there") is
|
|
||||||
_ -> Effect.putLine "Yay"
|
|
23
examples/balance/platform/Cargo.lock
generated
23
examples/balance/platform/Cargo.lock
generated
|
@ -1,23 +0,0 @@
|
||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
[[package]]
|
|
||||||
name = "host"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"roc_std 0.1.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libc"
|
|
||||||
version = "0.2.79"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "roc_std"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[metadata]
|
|
||||||
"checksum libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)" = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
|
|
|
@ -1,14 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "host"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["The Roc Contributors"]
|
|
||||||
license = "UPL-1.0"
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["staticlib"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
roc_std = { path = "../../../roc_std" }
|
|
||||||
|
|
||||||
[workspace]
|
|
|
@ -1,15 +0,0 @@
|
||||||
platform folkertdev/foo
|
|
||||||
requires { rocMain : Effect {} }
|
|
||||||
exposes []
|
|
||||||
packages {}
|
|
||||||
imports []
|
|
||||||
provides [ mainForHost ]
|
|
||||||
effects Effect
|
|
||||||
{
|
|
||||||
putChar : Int -> Effect {},
|
|
||||||
putLine : Str -> Effect {},
|
|
||||||
getLine : Effect Str
|
|
||||||
}
|
|
||||||
|
|
||||||
mainForHost : Effect {} as Fx
|
|
||||||
mainForHost = rocMain
|
|
|
@ -1,7 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
extern int rust_main();
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
return rust_main();
|
|
||||||
}
|
|
|
@ -1,147 +0,0 @@
|
||||||
#![allow(non_snake_case)]
|
|
||||||
|
|
||||||
use roc_std::alloca;
|
|
||||||
use roc_std::RocCallResult;
|
|
||||||
use roc_std::RocStr;
|
|
||||||
use std::alloc::Layout;
|
|
||||||
use std::ffi::c_void;
|
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#[link_name = "roc__rocMain_1_exposed"]
|
|
||||||
fn roc_main(output: *mut u8) -> ();
|
|
||||||
|
|
||||||
#[link_name = "roc__rocMain_1_size"]
|
|
||||||
fn roc_main_size() -> i64;
|
|
||||||
|
|
||||||
#[link_name = "roc__rocMain_1_Fx_caller"]
|
|
||||||
fn call_Fx(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> ();
|
|
||||||
|
|
||||||
#[link_name = "roc__rocMain_1_Fx_size"]
|
|
||||||
fn size_Fx() -> i64;
|
|
||||||
|
|
||||||
fn malloc(size: usize) -> *mut c_void;
|
|
||||||
fn realloc(c_ptr: *mut c_void, size: usize) -> *mut c_void;
|
|
||||||
fn free(c_ptr: *mut c_void);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
|
||||||
return malloc(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe fn roc_realloc(
|
|
||||||
c_ptr: *mut c_void,
|
|
||||||
new_size: usize,
|
|
||||||
_old_size: usize,
|
|
||||||
_alignment: u32,
|
|
||||||
) -> *mut c_void {
|
|
||||||
return realloc(c_ptr, new_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
|
||||||
return free(c_ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub fn roc_fx_putChar(foo: i64) -> () {
|
|
||||||
let character = foo as u8 as char;
|
|
||||||
print!("{}", character);
|
|
||||||
|
|
||||||
()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub fn roc_fx_putLine(line: RocStr) -> () {
|
|
||||||
let bytes = line.as_slice();
|
|
||||||
let string = unsafe { std::str::from_utf8_unchecked(bytes) };
|
|
||||||
println!("{}", string);
|
|
||||||
|
|
||||||
()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub fn roc_fx_getLine() -> RocStr {
|
|
||||||
use std::io::{self, BufRead};
|
|
||||||
|
|
||||||
let stdin = io::stdin();
|
|
||||||
let line1 = stdin.lock().lines().next().unwrap().unwrap();
|
|
||||||
|
|
||||||
RocStr::from_slice_with_capacity(line1.as_bytes(), line1.len())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn call_the_closure(function_pointer: *const u8, closure_data_ptr: *const u8) -> i64 {
|
|
||||||
let size = size_Fx() as usize;
|
|
||||||
|
|
||||||
alloca::with_stack_bytes(size, |buffer| {
|
|
||||||
let buffer: *mut std::ffi::c_void = buffer;
|
|
||||||
let buffer: *mut u8 = buffer as *mut u8;
|
|
||||||
|
|
||||||
call_Fx(
|
|
||||||
function_pointer,
|
|
||||||
closure_data_ptr as *const u8,
|
|
||||||
buffer as *mut u8,
|
|
||||||
);
|
|
||||||
|
|
||||||
let output = &*(buffer as *mut RocCallResult<i64>);
|
|
||||||
|
|
||||||
match output.into() {
|
|
||||||
Ok(_) => 0,
|
|
||||||
Err(e) => panic!("failed with {}", e),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub fn rust_main() -> isize {
|
|
||||||
println!("Running Roc closure");
|
|
||||||
let start_time = SystemTime::now();
|
|
||||||
|
|
||||||
let size = unsafe { roc_main_size() } as usize;
|
|
||||||
let layout = Layout::array::<u8>(size).unwrap();
|
|
||||||
let answer = unsafe {
|
|
||||||
let buffer = std::alloc::alloc(layout);
|
|
||||||
|
|
||||||
roc_main(buffer);
|
|
||||||
|
|
||||||
let output = &*(buffer as *mut RocCallResult<()>);
|
|
||||||
|
|
||||||
match output.into() {
|
|
||||||
Ok(()) => {
|
|
||||||
let function_pointer = {
|
|
||||||
// this is a pointer to the location where the function pointer is stored
|
|
||||||
// we pass just the function pointer
|
|
||||||
let temp = buffer.offset(8) as *const i64;
|
|
||||||
|
|
||||||
(*temp) as *const u8
|
|
||||||
};
|
|
||||||
|
|
||||||
let closure_data_ptr = buffer.offset(16);
|
|
||||||
|
|
||||||
let result =
|
|
||||||
call_the_closure(function_pointer as *const u8, closure_data_ptr as *const u8);
|
|
||||||
|
|
||||||
std::alloc::dealloc(buffer, layout);
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
Err(msg) => {
|
|
||||||
std::alloc::dealloc(buffer, layout);
|
|
||||||
|
|
||||||
panic!("Roc failed with message: {}", msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let end_time = SystemTime::now();
|
|
||||||
let duration = end_time.duration_since(start_time).unwrap();
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"Roc execution took {:.4} ms",
|
|
||||||
duration.as_secs_f64() * 1000.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Exit code
|
|
||||||
0
|
|
||||||
}
|
|
32
shell.nix
32
shell.nix
|
@ -4,15 +4,18 @@ let
|
||||||
splitSystem = builtins.split "-" builtins.currentSystem;
|
splitSystem = builtins.split "-" builtins.currentSystem;
|
||||||
currentArch = builtins.elemAt splitSystem 0;
|
currentArch = builtins.elemAt splitSystem 0;
|
||||||
currentOS = builtins.elemAt splitSystem 2;
|
currentOS = builtins.elemAt splitSystem 2;
|
||||||
in with {
|
in
|
||||||
|
with {
|
||||||
# Look here for information about how pin version of nixpkgs
|
# Look here for information about how pin version of nixpkgs
|
||||||
# → https://nixos.wiki/wiki/FAQ/Pinning_Nixpkgs
|
# → https://nixos.wiki/wiki/FAQ/Pinning_Nixpkgs
|
||||||
pkgs = import (builtins.fetchGit {
|
pkgs = import (
|
||||||
name = "nixpkgs-2021-04-23";
|
builtins.fetchGit {
|
||||||
|
# name = "nixpkgs-2021-04-23";
|
||||||
url = "https://github.com/nixos/nixpkgs/";
|
url = "https://github.com/nixos/nixpkgs/";
|
||||||
ref = "refs/heads/nixpkgs-unstable";
|
ref = "refs/heads/nixpkgs-unstable";
|
||||||
rev = "8d0340aee5caac3807c58ad7fa4ebdbbdd9134d6";
|
rev = "8d0340aee5caac3807c58ad7fa4ebdbbdd9134d6";
|
||||||
}) { };
|
}
|
||||||
|
) {};
|
||||||
|
|
||||||
isMacOS = currentOS == "darwin";
|
isMacOS = currentOS == "darwin";
|
||||||
isLinux = currentOS == "linux";
|
isLinux = currentOS == "linux";
|
||||||
|
@ -53,13 +56,6 @@ let
|
||||||
else
|
else
|
||||||
[];
|
[];
|
||||||
|
|
||||||
nixos-env =
|
|
||||||
if isLinux && builtins.pathExists /etc/nixos/configuration.nix then
|
|
||||||
{ XDG_DATA_DIRS = "/run/opengl-driver/share:$XDG_DATA_DIRS";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{ };
|
|
||||||
|
|
||||||
llvmPkgs = pkgs.llvmPackages_10;
|
llvmPkgs = pkgs.llvmPackages_10;
|
||||||
zig = import ./nix/zig.nix { inherit pkgs isMacOS isAarch64; };
|
zig = import ./nix/zig.nix { inherit pkgs isMacOS isAarch64; };
|
||||||
inputs = [
|
inputs = [
|
||||||
|
@ -91,13 +87,16 @@ let
|
||||||
# (import ./nix/zls.nix { inherit pkgs zig; })
|
# (import ./nix/zls.nix { inherit pkgs zig; })
|
||||||
];
|
];
|
||||||
|
|
||||||
in mkShell (nixos-env // {
|
in
|
||||||
|
mkShell (
|
||||||
|
{
|
||||||
buildInputs = inputs ++ darwin-inputs ++ linux-inputs;
|
buildInputs = inputs ++ darwin-inputs ++ linux-inputs;
|
||||||
|
|
||||||
# Additional Env vars
|
# Additional Env vars
|
||||||
LLVM_SYS_100_PREFIX = "${llvmPkgs.llvm}";
|
LLVM_SYS_100_PREFIX = "${llvmPkgs.llvm}";
|
||||||
LD_LIBRARY_PATH = stdenv.lib.makeLibraryPath
|
LD_LIBRARY_PATH = stdenv.lib.makeLibraryPath
|
||||||
([
|
(
|
||||||
|
[
|
||||||
pkg-config
|
pkg-config
|
||||||
stdenv.cc.cc.lib
|
stdenv.cc.cc.lib
|
||||||
llvmPkgs.libcxx
|
llvmPkgs.libcxx
|
||||||
|
@ -106,11 +105,12 @@ in mkShell (nixos-env // {
|
||||||
libffi
|
libffi
|
||||||
ncurses
|
ncurses
|
||||||
zlib
|
zlib
|
||||||
] ++ linux-inputs);
|
] ++ linux-inputs
|
||||||
|
);
|
||||||
|
|
||||||
# Aliases don't work cross shell, so we do this
|
# Aliases don't work cross shell, so we do this
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
export PATH="$PATH:$PWD/nix/bin"
|
export PATH="$PATH:$PWD/nix/bin"
|
||||||
'';
|
'';
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
31
vendor/inkwell/Cargo.toml
vendored
Normal file
31
vendor/inkwell/Cargo.toml
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
[package]
|
||||||
|
name = "inkwell"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["The Roc Contributors"]
|
||||||
|
license = "UPL-1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
|
||||||
|
#
|
||||||
|
# The reason for this fork is that the way Inkwell is designed, you have to use
|
||||||
|
# a particular branch (e.g. "llvm8-0") in Cargo.toml. That would be fine, except that
|
||||||
|
# breaking changes get pushed directly to that branch, which breaks our build
|
||||||
|
# without warning.
|
||||||
|
#
|
||||||
|
# We tried referencing a specific rev on TheDan64/inkwell directly (instead of branch),
|
||||||
|
# but although that worked locally, it did not work on GitHub Actions. (After a few
|
||||||
|
# hours of investigation, gave up trying to figure out why.) So this is the workaround:
|
||||||
|
# having an immutable tag on the rtfeldman/inkwell fork which points to
|
||||||
|
# a particular "release" of Inkwell.
|
||||||
|
#
|
||||||
|
# When we want to update Inkwell, we can sync up rtfeldman/inkwell to the latest
|
||||||
|
# commit of TheDan64/inkwell, push a new tag which points to the latest commit,
|
||||||
|
# change the tag value in this Cargo.toml to point to that tag, and `cargo update`.
|
||||||
|
# This way, GitHub Actions works and nobody's builds get broken.
|
||||||
|
inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm10-0.release4", features = [ "llvm10-0" ] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
target-arm = []
|
||||||
|
target-aarch64 = []
|
||||||
|
target-webassembly = []
|
4
vendor/inkwell/src/lib.rs
vendored
Normal file
4
vendor/inkwell/src/lib.rs
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#![cfg(not(doctest))]
|
||||||
|
// re-export all inkwell members. This way we can switch
|
||||||
|
// inkwell versions by making changes in just one place.
|
||||||
|
pub use inkwell::*;
|
Loading…
Add table
Add a link
Reference in a new issue