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
|
@ -28,24 +28,7 @@ inlinable_string = "0.1.0"
|
|||
libloading = "0.6"
|
||||
tempfile = "3.1.0"
|
||||
serde_json = "1.0"
|
||||
# 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" ] }
|
||||
inkwell = { path = "../../vendor/inkwell" }
|
||||
target-lexicon = "0.10"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -100,20 +100,22 @@ pub fn gen_from_mono_module(
|
|||
|
||||
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
|
||||
debug_assert!(kind_id > 0);
|
||||
let attr = context.create_enum_attribute(kind_id, 1);
|
||||
let enum_attr = context.create_enum_attribute(kind_id, 1);
|
||||
|
||||
for function in FunctionIterator::from_module(module) {
|
||||
let name = function.get_name().to_str().unwrap();
|
||||
|
||||
// mark our zig-defined builtins as internal
|
||||
if name.starts_with("roc_builtins") {
|
||||
function.set_linkage(Linkage::Internal);
|
||||
}
|
||||
|
||||
if name.starts_with("roc_builtins.dict") || name.starts_with("dict.RocDict") {
|
||||
function.add_attribute(AttributeLoc::Function, attr);
|
||||
}
|
||||
|
||||
if name.starts_with("roc_builtins.list") || name.starts_with("list.RocList") {
|
||||
function.add_attribute(AttributeLoc::Function, attr);
|
||||
if name.starts_with("roc_builtins.dict")
|
||||
|| name.starts_with("dict.RocDict")
|
||||
|| name.starts_with("roc_builtins.list")
|
||||
|| name.starts_with("list.RocList")
|
||||
{
|
||||
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,15 +10,29 @@ use std::str;
|
|||
fn main() {
|
||||
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
|
||||
let build_script_dir_path = fs::canonicalize(Path::new(".")).unwrap();
|
||||
let bitcode_path = build_script_dir_path.join("bitcode");
|
||||
|
||||
let src_obj_path = bitcode_path.join("builtins.o");
|
||||
let src_obj = src_obj_path.to_str().expect("Invalid src object path");
|
||||
println!("Compiling zig object to: {}", src_obj);
|
||||
|
||||
run_command(&bitcode_path, "zig", &["build", "object", "-Drelease=true"]);
|
||||
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"]);
|
||||
|
||||
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 = dest_obj_path.to_str().expect("Invalid dest object path");
|
||||
|
@ -26,12 +40,6 @@ fn main() {
|
|||
|
||||
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 = dest_bc_path.to_str().expect("Invalid dest bc path");
|
||||
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<()> {
|
||||
if dir.is_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"] }
|
||||
inlinable_string = "0.1"
|
||||
either = "1.6.1"
|
||||
# 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" ] }
|
||||
inkwell = { path = "../../vendor/inkwell" }
|
||||
target-lexicon = "0.10"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -5791,7 +5791,7 @@ fn get_gxx_personality_v0<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> Function
|
|||
None => {
|
||||
let personality_func = add_func(
|
||||
module,
|
||||
"__gxx_personality_v0",
|
||||
name,
|
||||
context.i64_type().fn_type(&[], false),
|
||||
Linkage::External,
|
||||
C_CALL_CONV,
|
||||
|
@ -5843,7 +5843,7 @@ fn cxa_begin_catch<'a, 'ctx, 'env>(
|
|||
|
||||
let cxa_begin_catch = add_func(
|
||||
module,
|
||||
"__cxa_begin_catch",
|
||||
name,
|
||||
u8_ptr.fn_type(&[u8_ptr.into()], false),
|
||||
Linkage::External,
|
||||
C_CALL_CONV,
|
||||
|
|
|
@ -5928,35 +5928,48 @@ fn reuse_function_symbol<'a>(
|
|||
) -> Stmt<'a> {
|
||||
match procs.partial_procs.get(&original) {
|
||||
None => {
|
||||
let is_imported = env.is_imported_symbol(original);
|
||||
|
||||
match arg_var {
|
||||
Some(arg_var) if is_imported => {
|
||||
Some(arg_var) if env.is_imported_symbol(original) => {
|
||||
let layout = layout_cache
|
||||
.from_var(env.arena, arg_var, env.subs)
|
||||
.expect("creating layout does not fail");
|
||||
|
||||
let top_level = TopLevelFunctionLayout::from_layout(env.arena, layout);
|
||||
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,
|
||||
);
|
||||
|
||||
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);
|
||||
procs.insert_passed_by_name(
|
||||
env,
|
||||
arg_var,
|
||||
original,
|
||||
top_level,
|
||||
layout_cache,
|
||||
);
|
||||
|
||||
// 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));
|
||||
let_empty_struct(symbol, env.arena.alloc(result))
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
// danger: a foreign symbol may not be specialized!
|
||||
debug_assert!(
|
||||
!is_imported,
|
||||
!env.is_imported_symbol(original),
|
||||
"symbol {:?} while processing module {:?}",
|
||||
original,
|
||||
(env.home, &arg_var),
|
||||
);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
Some(partial_proc) => {
|
||||
|
|
|
@ -276,7 +276,8 @@ impl<'a> LambdaSet<'a> {
|
|||
|
||||
use UnionVariant::*;
|
||||
match variant {
|
||||
Never | Unit | UnitWithArguments => Layout::Struct(&[]),
|
||||
Never => Layout::Union(UnionLayout::NonRecursive(&[])),
|
||||
Unit | UnitWithArguments => Layout::Struct(&[]),
|
||||
BoolUnion { .. } => Layout::Builtin(Builtin::Int1),
|
||||
ByteUnion(_) => Layout::Builtin(Builtin::Int8),
|
||||
Unwrapped(_tag_name, layouts) => Layout::Struct(layouts.into_bump_slice()),
|
||||
|
|
|
@ -270,11 +270,12 @@ mod test_mono {
|
|||
indoc!(
|
||||
r#"
|
||||
procedure Test.0 ():
|
||||
let Test.11 = 1i64;
|
||||
let Test.9 = 1i64;
|
||||
let Test.10 = 2i64;
|
||||
let Test.5 = These Test.11 Test.9 Test.10;
|
||||
switch Test.5:
|
||||
let Test.12 = 1i64;
|
||||
let Test.10 = 1i64;
|
||||
let Test.11 = 2i64;
|
||||
let Test.5 = These Test.12 Test.10 Test.11;
|
||||
let Test.9 = Index 0 Test.5;
|
||||
switch Test.9:
|
||||
case 2:
|
||||
let Test.2 = Index 1 Test.5;
|
||||
ret Test.2;
|
||||
|
|
|
@ -30,24 +30,7 @@ inlinable_string = "0.1"
|
|||
either = "1.6.1"
|
||||
indoc = "0.3.3"
|
||||
libc = "0.2"
|
||||
# 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" ] }
|
||||
inkwell = { path = "../../vendor/inkwell" }
|
||||
target-lexicon = "0.10"
|
||||
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()
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue