Introduce ll module

This commit is contained in:
Richard Feldman 2020-01-03 20:18:07 -05:00
parent 297c9f567a
commit 55e4ce1134
13 changed files with 1141 additions and 513 deletions

1
Cargo.lock generated
View file

@ -596,6 +596,7 @@ dependencies = [
"indoc 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"inkwell 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm8-0)",
"inlinable_string 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -21,6 +21,7 @@ inlinable_string = "0.1.0"
# but after several hours of trying unsuccessfully to fix it, `branch` is it.)
inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm8-0" }
futures = "0.3"
lazy_static = "1.4"
[dev-dependencies]
pretty_assertions = "0.5.1"

View file

@ -20,6 +20,18 @@ impl fmt::Debug for Symbol {
}
}
impl Into<InlinableString> for Symbol {
fn into(self) -> InlinableString {
self.0
}
}
impl From<InlinableString> for Symbol {
fn from(string: InlinableString) -> Self {
Symbol(string)
}
}
impl From<&str> for Symbol {
fn from(string: &str) -> Self {
Symbol(string.into())

471
src/gen/build.rs Normal file
View file

@ -0,0 +1,471 @@
use bumpalo::Bump;
use inkwell::module::Linkage;
use inkwell::types::BasicType;
use inkwell::types::BasicTypeEnum;
use inkwell::values::BasicValueEnum::{self, *};
use inkwell::values::{FunctionValue, IntValue, PointerValue};
use inkwell::{FloatPredicate, IntPredicate};
use crate::can;
use crate::collections::{ImMap, MutMap};
use crate::gen::convert::content_to_basic_type;
use crate::gen::env::Env;
use crate::ll::expr::{Expr, Procs};
use crate::subs::Variable;
use inlinable_string::InlinableString;
/// This is for Inkwell's FunctionValue::verify - we want to know the verification
/// output in debug builds, but we don't want it to print to stdout in release builds!
#[cfg(debug_assertions)]
const PRINT_FN_VERIFICATION_OUTPUT: bool = true;
#[cfg(not(debug_assertions))]
const PRINT_FN_VERIFICATION_OUTPUT: bool = false;
type Scope<'ctx> = ImMap<InlinableString, (Variable, PointerValue<'ctx>)>;
pub fn build_can_expr<'ctx, 'env>(
env: &Env<'ctx, 'env>,
parent: FunctionValue<'ctx>,
can_expr: can::expr::Expr,
) -> BasicValueEnum<'ctx> {
let arena = Bump::new();
let mut procs = MutMap::default();
let expr = Expr::new(
&arena,
&env.subs,
env.module,
env.context,
can_expr,
&mut procs,
);
build_expr(
env,
&ImMap::default(),
parent,
&expr,
&mut MutMap::default(),
)
}
fn build_expr<'a, 'ctx, 'env>(
env: &Env<'ctx, 'env>,
scope: &Scope<'ctx>,
parent: FunctionValue<'ctx>,
expr: &Expr<'a>,
procs: &mut Procs<'a, 'ctx>,
) -> BasicValueEnum<'ctx> {
use crate::ll::expr::Expr::*;
match expr {
Int(num) => env.context.i64_type().const_int(*num as u64, false).into(),
Float(num) => env.context.f64_type().const_float(*num).into(),
Cond {
cond_lhs,
cond_rhs,
pass,
fail,
ret_var,
} => {
let cond = Cond2 {
cond_lhs,
cond_rhs,
pass,
fail,
ret_var: *ret_var,
};
build_cond(env, scope, parent, cond, procs)
}
Branches { .. } => {
panic!("TODO build_branches(env, scope, parent, cond_lhs, branches, procs)");
}
Store(ref stores, ref ret) => {
let mut scope = im_rc::HashMap::clone(scope);
let subs = &env.subs;
let context = &env.context;
for (name, var, expr) in stores.iter() {
let content = subs.get_without_compacting(*var).content;
let val = build_expr(env, &scope, parent, &expr, procs);
let expr_bt =
content_to_basic_type(&content, subs, context).unwrap_or_else(|err| {
panic!(
"Error converting symbol {:?} to basic type: {:?} - scope was: {:?}",
name, err, scope
)
});
let alloca = create_entry_block_alloca(env, parent, expr_bt, &name);
env.builder.build_store(alloca, val);
// Make a new scope which includes the binding we just encountered.
// This should be done *after* compiling the bound expr, since any
// recursive (in the LetRec sense) bindings should already have
// been extracted as procedures. Nothing in here should need to
// access itself!
scope = im_rc::HashMap::clone(&scope);
scope.insert(name.clone(), (*var, alloca));
}
build_expr(env, &scope, parent, ret, procs)
}
CallByName(ref name, ref args) => {
// TODO try one of these alternative strategies:
//
// 1. use SIMD string comparison to compare these strings faster
// 2. pre-register Bool.or using module.add_function, and see if LLVM inlines it
if name == "Bool.or" {
panic!("TODO create a phi node for ||");
} else if name == "Bool.and" {
panic!("TODO create a phi node for &&");
} else {
let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity(args.len());
for arg in args.iter() {
arg_vals.push(build_expr(env, scope, parent, arg, procs).into());
}
let fn_val = env
.module
.get_function(name)
.unwrap_or_else(|| panic!("Unrecognized function: {:?}", name));
let call = env.builder.build_call(fn_val, arg_vals.as_slice(), "tmp");
call.try_as_basic_value()
.left()
.unwrap_or_else(|| panic!("LLVM error: Invalid call by name."))
}
}
CallByPointer(ref _ptr, ref args) => {
let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity(args.len());
for arg in args.iter() {
arg_vals.push(build_expr(env, scope, parent, arg, procs).into());
}
panic!("TODO do a load(ptr) to get back the pointer, then pass *that* in here!");
// let call = match build_expr(env, scope, parent, expr, procs) {
// BasicValueEnum::PointerValue(ptr) => {
// env.builder.build_call(ptr, arg_vals.as_slice(), "tmp")
// }
// non_ptr => {
// panic!(
// "Tried to call by pointer, but encountered a non-pointer: {:?}",
// non_ptr
// );
// }
// };
// call.try_as_basic_value()
// .left()
// .unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."))
}
Load(name) => match scope.get(name) {
Some((_, ptr)) => env.builder.build_load(*ptr, name),
None => panic!("Could not find a var for {:?} in scope {:?}", name, scope),
},
_ => {
panic!("I don't yet know how to build {:?}", expr);
}
}
}
struct Cond2<'a> {
cond_lhs: &'a Expr<'a>,
cond_rhs: &'a Expr<'a>,
pass: &'a Expr<'a>,
fail: &'a Expr<'a>,
ret_var: Variable,
}
fn build_cond<'a, 'ctx, 'env>(
env: &Env<'ctx, 'env>,
scope: &Scope<'ctx>,
parent: FunctionValue<'ctx>,
cond: Cond2<'a>,
procs: &mut Procs<'a, 'ctx>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let context = env.context;
let subs = &env.subs;
let content = subs.get_without_compacting(cond.ret_var).content;
let ret_type = content_to_basic_type(&content, subs, context).unwrap_or_else(|err| {
panic!(
"Error converting cond branch ret_type content {:?} to basic type: {:?}",
cond.pass, err
)
});
let lhs = build_expr(env, scope, parent, cond.cond_lhs, procs);
let rhs = build_expr(env, scope, parent, cond.cond_rhs, procs);
match (lhs, rhs) {
(FloatValue(lhs_float), FloatValue(rhs_float)) => {
let comparison =
builder.build_float_compare(FloatPredicate::OEQ, lhs_float, rhs_float, "cond");
build_phi2(
env, scope, parent, comparison, cond.pass, cond.fail, ret_type, procs,
)
}
(IntValue(lhs_int), IntValue(rhs_int)) => {
let comparison = builder.build_int_compare(IntPredicate::EQ, lhs_int, rhs_int, "cond");
build_phi2(
env, scope, parent, comparison, cond.pass, cond.fail, ret_type, procs,
)
}
_ => panic!(
"Tried to make a branch out of incompatible conditions: lhs = {:?} and rhs = {:?}",
cond.cond_lhs, cond.cond_rhs
),
}
}
// fn build_branches<'a, 'ctx, 'env>(
// env: &Env<'ctx, 'env>,
// scope: &Scope<'ctx>,
// parent: FunctionValue<'ctx>,
// cond_lhs: &'a Expr<'a>,
// branches: &'a [(Expr<'a>, Expr<'a>, Expr<'a>)],
// ret_type: BasicValueEnum<'ctx>,
// procs: &mut Procs<'a, 'ctx>,
// ) -> BasicValueEnum<'ctx> {
// let builder = env.builder;
// let context = env.context;
// let lhs = build_expr(env, scope, parent, cond_lhs, procs);
// let mut branch_iter = branches.into_iter();
// let content = subs.get_without_compacting(cond.ret_var).content;
// let ret_type = content_to_basic_type(&content, subs, context).unwrap_or_else(|err| {
// panic!(
// "Error converting cond branch ret_type content {:?} to basic type: {:?}",
// cond.pass, err
// )
// });
// for (cond_rhs, cond_pass, cond_else) in branches {
// let rhs = build_expr(env, scope, parent, cond_rhs, procs);
// let pass = build_expr(env, scope, parent, cond_pass, procs);
// let fail = build_expr(env, scope, parent, cond_else, procs);
// let cond = Cond {
// lhs,
// rhs,
// pass,
// fail,
// ret_type,
// };
// build_cond(env, scope, parent, cond, procs)
// }
// }
fn build_phi2<'a, 'ctx, 'env>(
env: &Env<'ctx, 'env>,
scope: &Scope<'ctx>,
parent: FunctionValue<'ctx>,
comparison: IntValue<'ctx>,
pass: &'a Expr<'a>,
fail: &'a Expr<'a>,
ret_type: BasicTypeEnum<'ctx>,
procs: &mut Procs<'a, 'ctx>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let context = env.context;
// build branch
let then_bb = context.append_basic_block(parent, "then");
let else_bb = context.append_basic_block(parent, "else");
let cont_bb = context.append_basic_block(parent, "branchcont");
builder.build_conditional_branch(comparison, &then_bb, &else_bb);
// build then block
builder.position_at_end(&then_bb);
let then_val = build_expr(env, scope, parent, pass, procs);
builder.build_unconditional_branch(&cont_bb);
let then_bb = builder.get_insert_block().unwrap();
// build else block
builder.position_at_end(&else_bb);
let else_val = build_expr(env, scope, parent, fail, procs);
builder.build_unconditional_branch(&cont_bb);
let else_bb = builder.get_insert_block().unwrap();
// emit merge block
builder.position_at_end(&cont_bb);
let phi = builder.build_phi(ret_type, "branch");
phi.add_incoming(&[
(&Into::<BasicValueEnum>::into(then_val), &then_bb),
(&Into::<BasicValueEnum>::into(else_val), &else_bb),
]);
phi.as_basic_value()
}
/// TODO could this be added to Inkwell itself as a method on BasicValueEnum?
fn set_name(bv_enum: BasicValueEnum<'_>, name: &str) {
match bv_enum {
ArrayValue(val) => val.set_name(name),
IntValue(val) => val.set_name(name),
FloatValue(val) => val.set_name(name),
PointerValue(val) => val.set_name(name),
StructValue(val) => val.set_name(name),
VectorValue(val) => val.set_name(name),
}
}
pub fn build_closure<'a, 'ctx, BT>(
env: &Env<'ctx, '_>,
name: InlinableString,
arg_vars: Vec<Variable>,
arg_names: &[InlinableString],
ret_type: BT,
body_expr: &Expr<'a>,
scope: &Scope<'ctx>,
procs: &mut Procs<'a, 'ctx>,
linkage: Option<Linkage>,
) -> FunctionValue<'ctx>
where
BT: BasicType<'ctx>,
{
// We need these to be separate, but they must have the same length!
debug_assert!(arg_vars.len() == arg_names.len());
let subs = &env.subs;
// Register the function value in the module
let mut arg_basic_types = Vec::with_capacity(arg_vars.len());
for var in arg_vars.iter() {
let content = subs.get_without_compacting(*var).content;
arg_basic_types.push(
content_to_basic_type(&content, &env.subs, env.context).unwrap_or_else(|err| {
panic!(
"Error converting function arg content to basic type: {:?}",
err
)
}),
);
}
let fn_type = ret_type.fn_type(arg_basic_types.as_slice(), false);
let fn_val = env.module.add_function(&name, fn_type, linkage);
let entry = env.context.append_basic_block(fn_val, "entry");
let builder = env.builder;
builder.position_at_end(&entry);
let mut scope = scope.clone();
// Add args to scope
for (((arg_val, arg_name), arg_type), var) in fn_val
.get_param_iter()
.zip(arg_names)
.zip(arg_basic_types)
.zip(arg_vars.into_iter())
{
set_name(arg_val, arg_name);
let alloca = create_entry_block_alloca(env, fn_val, arg_type, arg_name);
builder.build_store(alloca, arg_val);
scope.insert(arg_name.clone(), (var, alloca));
}
let body = build_expr(env, &scope, fn_val, body_expr, procs);
builder.build_return(Some(&body));
fn_val
}
/// Creates a new stack allocation instruction in the entry block of the function.
pub fn create_entry_block_alloca<'ctx>(
env: &Env<'ctx, '_>,
parent: FunctionValue<'_>,
basic_type: BasicTypeEnum<'ctx>,
name: &str,
) -> PointerValue<'ctx> {
let builder = env.context.create_builder();
let entry = parent.get_first_basic_block().unwrap();
match entry.get_first_instruction() {
Some(first_instr) => builder.position_before(&first_instr),
None => builder.position_at_end(&entry),
}
builder.build_alloca(basic_type, name)
}
// fn build_proc() {
// let (ref loc_body_expr, ret_var) = **body;
// let subs = &env.subs;
// let mut arg_types = Vec::new();
// let mut arg_names = Vec::new();
// let ret_content = subs.get_without_compacting(ret_var).content;
// let ret_type =
// content_to_basic_type(&ret_content, &env.subs, env.context).unwrap_or_else(|err| {
// panic!(
// "Error converting symbol {:?} to basic type: {:?} - scope was: {:?}",
// symbol, err, scope
// )
// });
// for (var, loc_pat) in args {
// let content = subs.get_without_compacting(*var).content;
// let name = match &loc_pat.value {
// Pattern::Identifier(ident) => ident.as_str().into(),
// pat => {
// panic!("TODO code gen function arg for pattern {:?}", pat);
// }
// };
// arg_types.push(content);
// arg_names.push(name);
// }
// let fn_val = build_closure(
// env,
// symbol.as_str().into(),
// arg_types,
// arg_names.as_slice(),
// ret_type,
// &loc_body_expr.value,
// scope,
// procs,
// None,
// );
// if fn_val.verify(PRINT_FN_VERIFICATION_OUTPUT) {
// // TODO call pass_manager.run_on(&fn_val) to optimize it!
// // The closure evaluates to a pointer to the function.
// fn_val
// .as_global_value()
// .as_pointer_value()
// .as_basic_value_enum()
// } else {
// unsafe {
// fn_val.delete();
// }
// panic!("Invalid generated fn_val.")
// }
// }

View file

@ -1,478 +0,0 @@
use inkwell::basic_block::BasicBlock;
use inkwell::module::Linkage;
use inkwell::types::BasicType;
use inkwell::types::BasicTypeEnum;
use inkwell::values::BasicValueEnum::{self, *};
use inkwell::values::{BasicValue, FunctionValue, IntValue, PointerValue};
use inkwell::{FloatPredicate, IntPredicate};
use crate::can::expr::Expr;
use crate::can::ident::Lowercase;
use crate::can::pattern::Pattern::{self, *};
use crate::can::symbol::Symbol;
use crate::collections::ImMap;
use crate::collections::MutMap;
use crate::gen::convert::content_to_basic_type;
use crate::gen::env::Env;
use crate::subs::{Content, FlatType, Subs};
/// This is for Inkwell's FunctionValue::verify - we want to know the verification
/// output in debug builds, but we don't want it to print to stdout in release builds!
#[cfg(debug_assertions)]
const PRINT_FN_VERIFICATION_OUTPUT: bool = true;
#[cfg(not(debug_assertions))]
const PRINT_FN_VERIFICATION_OUTPUT: bool = false;
type Procs<'ctx> = MutMap<Symbol, (Content, FunctionValue<'ctx>)>;
type Scope<'ctx> = ImMap<Symbol, (Content, PointerValue<'ctx>)>;
pub fn compile_standalone_expr<'ctx, 'env>(
env: &Env<'ctx, 'env>,
parent: FunctionValue<'ctx>,
expr: &Expr,
) -> BasicValueEnum<'ctx> {
compile_expr(env, &ImMap::default(), parent, expr, &mut MutMap::default())
}
fn compile_expr<'ctx, 'env>(
env: &Env<'ctx, 'env>,
scope: &Scope<'ctx>,
parent: FunctionValue<'ctx>,
expr: &Expr,
procs: &mut Procs,
) -> BasicValueEnum<'ctx> {
use crate::can::expr::Expr::*;
match *expr {
Int(_, num) => env.context.i64_type().const_int(num as u64, false).into(),
Float(_, num) => env.context.f64_type().const_float(num).into(),
When {
ref loc_cond,
ref branches,
..
} => {
if branches.len() < 2 {
panic!("TODO support when-expressions of fewer than 2 branches.");
}
if branches.len() == 2 {
let mut iter = branches.iter();
let (pattern, branch_expr) = iter.next().unwrap();
let (_, else_expr) = iter.next().unwrap();
compile_when_branch(
env,
scope,
parent,
&loc_cond.value,
pattern.value.clone(),
&branch_expr.value,
&else_expr.value,
procs,
)
} else {
panic!("TODO support when-expressions of more than 2 branches.");
}
}
LetNonRec(ref def, ref loc_ret, _) => {
match &def.loc_pattern.value {
Pattern::Identifier(symbol) => {
let expr = &def.loc_expr.value;
let subs = &env.subs;
let context = &env.context;
let content = content_from_expr(scope, subs, expr);
let val = compile_expr(env, &scope, parent, &expr, procs);
let expr_bt = content_to_basic_type(&content, subs, context).unwrap_or_else(|err| panic!("Error converting symbol {:?} to basic type: {:?} - scope was: {:?}", symbol, err, scope));
let alloca = create_entry_block_alloca(env, parent, expr_bt, symbol.as_str());
env.builder.build_store(alloca, val);
// Make a new scope which includes the binding we just encountered.
// This should be done *after* compiling the bound expr, since this is a
// LetNonRec rather than a LetRec. It shouldn't need to access itself!
let mut scope = scope.clone();
scope.insert(symbol.clone(), (content.clone(), alloca));
compile_expr(env, &scope, parent, &loc_ret.value, procs)
}
pat => {
panic!("TODO code gen Def pattern {:?}", pat);
}
}
}
Closure(_, ref symbol, ref _recursive, ref args, ref body) => {
let (ref loc_body_expr, ret_var) = **body;
let subs = &env.subs;
let mut arg_types = Vec::new();
let mut arg_names = Vec::new();
let ret_content = subs.get_without_compacting(ret_var).content;
let ret_type = content_to_basic_type(&ret_content, &env.subs, env.context)
.unwrap_or_else(|err| {
panic!(
"Error converting symbol {:?} to basic type: {:?} - scope was: {:?}",
symbol, err, scope
)
});
for (var, loc_pat) in args {
let content = subs.get_without_compacting(*var).content;
let name = match &loc_pat.value {
Pattern::Identifier(ident) => ident.as_str().into(),
pat => {
panic!("TODO code gen function arg for pattern {:?}", pat);
}
};
arg_types.push(content);
arg_names.push(name);
}
let fn_val = compile_closure(
env,
symbol.as_str().into(),
arg_types,
arg_names.as_slice(),
ret_type,
&loc_body_expr.value,
scope,
procs,
None,
);
if fn_val.verify(PRINT_FN_VERIFICATION_OUTPUT) {
// TODO call pass_manager.run_on(&fn_val) to optimize it!
// The closure evaluates to a pointer to the function.
fn_val
.as_global_value()
.as_pointer_value()
.as_basic_value_enum()
} else {
unsafe {
fn_val.delete();
}
panic!("Invalid generated fn_val.")
}
}
Call(ref boxed, ref loc_args, _) => {
let (_, ref loc_expr, _) = **boxed;
let mut arg_vars: Vec<BasicValueEnum> = Vec::with_capacity(loc_args.len());
for (_var, loc_arg) in loc_args.iter() {
let arg = compile_expr(env, scope, parent, &loc_arg.value, procs);
arg_vars.push(arg.into());
}
let call = match &loc_expr.value {
// TODO fix and then re-enable this. The problem is that it fails on
// symbol lookup, because the closure's stored Symbol is always the
// auto-generated one like "Test.blah$1" whereas the lookup is always
// something like "Test.blah$identity" - so if we want to call by
// name directly, we need to detect the closure's name *during code gen*
// (while processing defs) and insert it into scope accordingly.
// Afterwards, we should remove that Symbol from Expr.
//
// Var {
// resolved_symbol, ..
// } => {
// // Call by name
// let fn_val = env
// .module
// .get_function(resolved_symbol.as_str())
// .unwrap_or_else(|| panic!("Unrecognized function: {:?}", resolved_symbol));
// env.builder.build_call(fn_val, arg_vars.as_slice(), "tmp")
// }
expr => {
// Call by pointer - the closure was anonymous, e.g. ((\a -> a) 5)
match compile_expr(env, scope, parent, expr, procs) {
BasicValueEnum::PointerValue(ptr) => {
env.builder.build_call(ptr, arg_vars.as_slice(), "tmp")
}
non_ptr => {
panic!(
"Tried to call by pointer, but encountered a non-pointer: {:?}",
non_ptr
);
}
}
}
};
call.try_as_basic_value()
.left()
.unwrap_or_else(|| panic!("Invalid call produced."))
}
Var {
ref resolved_symbol,
..
} => match scope.get(resolved_symbol) {
Some((_, ptr)) => env.builder.build_load(*ptr, resolved_symbol.as_str()),
None => panic!(
"Could not find a var for {:?} in scope {:?}",
resolved_symbol, scope
),
},
_ => {
panic!("I don't yet know how to compile {:?}", expr);
}
}
}
fn content_from_expr(scope: &Scope<'_>, subs: &Subs, expr: &Expr) -> Content {
use crate::can::expr::Expr::*;
match expr {
Int(var, _)
| Float(var, _)
| When { expr_var: var, .. }
| LetNonRec(_, _, var)
| LetRec(_, _, var)
| Closure(var, _, _, _, _) => subs.get_without_compacting(*var).content,
Str(_) | BlockStr(_) => Content::Structure(FlatType::Apply {
module_name: "Str".into(),
name: "Str".into(),
args: Vec::new(),
}),
Var {
ref resolved_symbol,
..
} => {
let (content, _) = scope.get(resolved_symbol).unwrap_or_else(|| {
panic!(
"Code gen problem: Couldn't find {:?} in scope {:?}",
resolved_symbol, scope
)
});
content.clone()
}
other => panic!("TODO handle content_from_expr for {:?}", other),
}
}
fn compile_when_branch<'ctx, 'env>(
env: &Env<'ctx, 'env>,
scope: &Scope<'ctx>,
parent: FunctionValue<'ctx>,
cond_expr: &Expr,
pattern: Pattern,
branch_expr: &Expr,
else_expr: &Expr,
procs: &mut Procs,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let context = env.context;
match compile_expr(env, scope, parent, cond_expr, procs) {
FloatValue(float_val) => match pattern {
FloatLiteral(target_val) => {
let comparison = builder.build_float_compare(
FloatPredicate::OEQ,
float_val,
context.f64_type().const_float(target_val),
"whencond",
);
let (then_bb, else_bb, then_val, else_val) = two_way_branch(
env,
scope,
parent,
comparison,
branch_expr,
else_expr,
procs,
);
let phi = builder.build_phi(context.f64_type(), "whenbranch");
phi.add_incoming(&[
(&Into::<BasicValueEnum>::into(then_val), &then_bb),
(&Into::<BasicValueEnum>::into(else_val), &else_bb),
]);
phi.as_basic_value().into_float_value().into()
}
_ => panic!("TODO support pattern matching on floats other than literals."),
},
IntValue(int_val) => match pattern {
IntLiteral(target_val) => {
let comparison = builder.build_int_compare(
IntPredicate::EQ,
int_val,
context.i64_type().const_int(target_val as u64, false),
"whencond",
);
let (then_bb, else_bb, then_val, else_val) = two_way_branch(
env,
scope,
parent,
comparison,
branch_expr,
else_expr,
procs,
);
let phi = builder.build_phi(context.i64_type(), "whenbranch");
phi.add_incoming(&[
(&Into::<BasicValueEnum>::into(then_val), &then_bb),
(&Into::<BasicValueEnum>::into(else_val), &else_bb),
]);
phi.as_basic_value().into_int_value().into()
}
_ => panic!("TODO support pattern matching on ints other than literals."),
},
_ => panic!(
"TODO handle pattern matching on conditionals other than int and float literals."
),
}
}
fn two_way_branch<'ctx, 'env>(
env: &Env<'ctx, 'env>,
scope: &Scope<'ctx>,
parent: FunctionValue<'ctx>,
comparison: IntValue<'ctx>,
branch_expr: &Expr,
else_expr: &Expr,
procs: &mut Procs,
) -> (
BasicBlock,
BasicBlock,
BasicValueEnum<'ctx>,
BasicValueEnum<'ctx>,
) {
let builder = env.builder;
let context = env.context;
// build branch
let then_bb = context.append_basic_block(parent, "then");
let else_bb = context.append_basic_block(parent, "else");
let cont_bb = context.append_basic_block(parent, "casecont");
builder.build_conditional_branch(comparison, &then_bb, &else_bb);
// build then block
builder.position_at_end(&then_bb);
let then_val = compile_expr(env, scope, parent, branch_expr, procs);
builder.build_unconditional_branch(&cont_bb);
let then_bb = builder.get_insert_block().unwrap();
// build else block
builder.position_at_end(&else_bb);
let else_val = compile_expr(env, scope, parent, else_expr, procs);
builder.build_unconditional_branch(&cont_bb);
let else_bb = builder.get_insert_block().unwrap();
// emit merge block
builder.position_at_end(&cont_bb);
(then_bb, else_bb, then_val, else_val)
}
/// TODO could this be added to Inkwell itself as a method on BasicValueEnum?
fn set_name(bv_enum: BasicValueEnum<'_>, name: &str) {
match bv_enum {
ArrayValue(val) => val.set_name(name),
IntValue(val) => val.set_name(name),
FloatValue(val) => val.set_name(name),
PointerValue(val) => val.set_name(name),
StructValue(val) => val.set_name(name),
VectorValue(val) => val.set_name(name),
}
}
pub fn compile_closure<'ctx, BT>(
env: &Env<'ctx, '_>,
name: Lowercase,
arg_types: Vec<Content>,
arg_names: &[Lowercase],
ret_type: BT,
body_expr: &Expr,
scope: &Scope<'ctx>,
procs: &mut Procs,
linkage: Option<Linkage>,
) -> FunctionValue<'ctx>
where
BT: BasicType<'ctx>,
{
// We need these to be separate, but they must have the same length!
debug_assert!(arg_types.len() == arg_names.len());
// Register the function value in the module
let mut arg_basic_types = Vec::with_capacity(arg_types.len());
for content in arg_types.iter() {
arg_basic_types.push(
content_to_basic_type(content, &env.subs, env.context).unwrap_or_else(|err| {
panic!(
"Error converting function arg content to basic type: {:?}",
err
)
}),
);
}
let fn_type = ret_type.fn_type(arg_basic_types.as_slice(), false);
let fn_val = env.module.add_function(name.as_str(), fn_type, linkage);
let entry = env.context.append_basic_block(fn_val, "entry");
let builder = env.builder;
builder.position_at_end(&entry);
let mut scope = scope.clone();
// Add args to scope
for (((arg_val, arg_name), arg_type), content) in fn_val
.get_param_iter()
.zip(arg_names)
.zip(arg_basic_types)
.zip(arg_types.into_iter())
{
let arg_name = arg_name.as_str();
set_name(arg_val, arg_name);
let alloca = create_entry_block_alloca(env, fn_val, arg_type, arg_name);
builder.build_store(alloca, arg_val);
scope.insert(arg_name.into(), (content, alloca));
}
let body = compile_expr(env, &scope, fn_val, body_expr, procs);
builder.build_return(Some(&body));
fn_val
}
/// Creates a new stack allocation instruction in the entry block of the function.
pub fn create_entry_block_alloca<'ctx>(
env: &Env<'ctx, '_>,
parent: FunctionValue<'_>,
basic_type: BasicTypeEnum<'ctx>,
name: &str,
) -> PointerValue<'ctx> {
let builder = env.context.create_builder();
let entry = parent.get_first_basic_block().unwrap();
match entry.get_first_instruction() {
Some(first_instr) => builder.position_before(&first_instr),
None => builder.position_at_end(&entry),
}
builder.build_alloca(basic_type, name)
}

View file

@ -6,6 +6,6 @@ use inkwell::module::Module;
pub struct Env<'ctx, 'env> {
pub context: &'ctx Context,
pub builder: &'env Builder<'ctx>,
pub module: &'env Module<'ctx>,
pub module: &'ctx Module<'ctx>,
pub subs: Subs,
}

View file

@ -1,3 +1,3 @@
pub mod compile;
pub mod build;
pub mod convert;
pub mod env;

89
src/gen/proc.rs Normal file
View file

@ -0,0 +1,89 @@
use inkwell::basic_block::BasicBlock;
use inkwell::module::Linkage;
use inkwell::types::BasicType;
use inkwell::types::BasicTypeEnum;
use inkwell::values::BasicValueEnum::{self, *};
use inkwell::values::{BasicValue, FunctionValue, IntValue, PointerValue};
use inkwell::{FloatPredicate, IntPredicate};
use crate::can::expr::Expr;
use crate::can::ident::Lowercase;
use crate::can::pattern::Pattern::{self, *};
use crate::can::symbol::Symbol;
use crate::collections::ImMap;
use crate::collections::MutMap;
use crate::gen::convert::content_to_basic_type;
use crate::gen::env::Env;
use crate::subs::{Content, FlatType, Subs};
type Procs<'ctx> = MutMap<Symbol, (Content, FunctionValue<'ctx>)>;
fn extract_procs(loc_expr: Located<Expr>, module: &Module<'ctx>, name: Option<Lowercase>, procs, &mut Procs<'ctx>) -> Located<Expr> {
let mut procs = Vec::new();
match expr {
LetNonRec(def, ret_expr, var) => {
let loc_pattern = def.loc_pattern;
let loc_expr = def.loc_expr;
// If we're defining a named closure, insert it into Procs and then
// remove the Let. When code later goes to look it up, it'll be in Procs!
//
// Before:
//
// identity = \a -> a
//
// identity 5
//
// After: (`identity` is now in Procs)
//
// identity 5
//
let pattern = match loc_pattern.value {
Identifier(name) => {
match &loc_expr.value {
Closure(_, _, _, _, _) => {
// Extract Procs, but discard the resulting Expr::Var.
// That Var looks up the pointer, which we won't use here!
extract_procs(loc_expr, Some(name), procs);
// Discard this LetNonRec by replacing it with its ret_expr.
return ret_expr;
}
_ => {
// If this isn't a Closure, proceed as normal.
Identifier(name)
}
}
}
pat => pat
}
// At this point, it's safe to assume we aren't assigning a Closure to a def.
// Extract Procs from the def body and the ret expression, and return the result!
let ret_expr = extract_procs(ret_expr, None, procs);
let loc_expr = extract_procs(def.loc_expr, None, procs);
let loc_pattern = Located { region: def.loc_pattern.region, value: pattern };
let def = Def { loc_pattern, loc_expr, ..def };
LetNonRec(def, ret_expr, var)
}
Closure(var, symbol, recursive, loc_args, boxed_ret) => {
let (loc_ret, var) = boxed_ret;
let name = match name {
Some(name) => name.as_str(),
None => {
// Give the closure a name like "_0" or "_1".
// We know procs.len() will be unique!
format!("_{}", procs.len()).as_str();
}
};
let fn_val = module.add_function(name, fn_type, linkage);
panic!("push to procs");
}
};
}

View file

@ -27,6 +27,7 @@ pub mod ena;
pub mod fmt;
pub mod gen;
pub mod infer;
pub mod ll;
pub mod load;
pub mod module;
pub mod pretty_print_types;
@ -37,3 +38,6 @@ pub mod unify;
#[macro_use]
extern crate log;
// #[macro_use]
// extern crate lazy_static;

400
src/ll/expr.rs Normal file
View file

@ -0,0 +1,400 @@
use crate::can;
use crate::can::pattern::Pattern;
use crate::collections::MutMap;
use crate::gen::convert::content_to_basic_type;
use crate::ll::layout::Layout;
use crate::region::Located;
use crate::subs::{Subs, Variable};
use bumpalo::collections::Vec;
use bumpalo::Bump;
use inkwell::context::Context;
use inkwell::module::Module;
use inkwell::types::{BasicType, BasicTypeEnum};
use inkwell::values::FunctionValue;
use inlinable_string::InlinableString;
pub type Procs<'a, 'ctx> = MutMap<InlinableString, (Option<Proc<'a>>, FunctionValue<'ctx>)>;
#[derive(Clone, Debug, PartialEq)]
pub struct Proc<'a> {
args: &'a [(Layout<'a>, InlinableString)],
body: Expr<'a>,
closes_over: Layout<'a>,
}
struct Env<'a, 'ctx> {
arena: &'a Bump,
subs: &'a Subs,
module: &'ctx Module<'ctx>,
context: &'ctx Context,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Expr<'a> {
// Literals
Int(i64),
Float(f64),
Str(&'a str),
// Load/Store
Load(InlinableString),
Store(&'a [(InlinableString, Variable, Expr<'a>)], &'a Expr<'a>),
// Functions
FunctionPointer(InlinableString),
CallByPointer(InlinableString, &'a [Expr<'a>]),
CallByName(InlinableString, &'a [Expr<'a>]),
// Exactly two conditional branches, e.g. if/else
Cond {
// The left-hand side of the conditional comparison and the right-hand side.
// These are stored separately because there are different machine instructions
// for e.g. "compare float and jump" vs. "compare integer and jump"
cond_lhs: &'a Expr<'a>,
cond_rhs: &'a Expr<'a>,
// What to do if the condition either passes or fails
pass: &'a Expr<'a>,
fail: &'a Expr<'a>,
ret_var: Variable,
},
/// More than two conditional branches, e.g. a 3-way when-expression
Branches {
/// The left-hand side of the conditional. We compile this to LLVM once,
/// then reuse it to test against each different compiled cond_rhs value.
cond_lhs: &'a Expr<'a>,
/// ( cond_rhs, pass, fail )
branches: &'a [(Expr<'a>, Expr<'a>, Expr<'a>)],
ret_var: Variable,
},
Struct(&'a [(InlinableString, Expr<'a>)]),
RuntimeError(&'a str),
}
impl<'a> Expr<'a> {
pub fn new<'ctx>(
arena: &'a Bump,
subs: &'a Subs,
module: &'ctx Module<'ctx>,
context: &'ctx Context,
can_expr: can::expr::Expr,
procs: &mut Procs<'a, 'ctx>,
) -> Self {
let env = Env {
arena,
subs,
module,
context,
};
from_can(&env, can_expr, procs, None)
}
}
fn from_can<'a, 'ctx>(
env: &Env<'a, 'ctx>,
can_expr: can::expr::Expr,
procs: &mut Procs<'a, 'ctx>,
name: Option<InlinableString>,
) -> Expr<'a> {
use crate::can::expr::Expr::*;
use crate::can::pattern::Pattern::*;
match can_expr {
Int(_, val) => Expr::Int(val),
Float(_, val) => Expr::Float(val),
Str(string) | BlockStr(string) => Expr::Str(env.arena.alloc(string)),
Var {
resolved_symbol, ..
} => Expr::Load(resolved_symbol.into()),
LetNonRec(def, ret_expr, _) => {
let arena = env.arena;
let loc_pattern = def.loc_pattern;
let loc_expr = def.loc_expr;
let mut stored = Vec::with_capacity_in(1, arena);
// If we're defining a named closure, insert it into Procs and then
// remove the Let. When code gen later goes to look it up, it'll be in Procs!
//
// Before:
//
// identity = \a -> a
//
// identity 5
//
// After: (`identity` is now in Procs)
//
// identity 5
//
match &loc_pattern.value {
Identifier(name) => {
match &loc_expr.value {
Closure(_, _, _, _, _) => {
// Extract Procs, but discard the resulting Expr::Load.
// That Load looks up the pointer, which we won't use here!
from_can(env, loc_expr.value, procs, Some(name.clone().into()));
// Discard this LetNonRec by replacing it with its ret_expr.
return from_can(env, ret_expr.value, procs, None);
}
_ => (),
}
}
_ => (),
}
// If it wasn't specifically an Identifier & Closure, proceed as normal.
store_pattern(
env,
loc_pattern.value,
loc_expr.value,
def.expr_var,
procs,
&mut stored,
);
// At this point, it's safe to assume we aren't assigning a Closure to a def.
// Extract Procs from the def body and the ret expression, and return the result!
let ret = from_can(env, ret_expr.value, procs, None);
Expr::Store(stored.into_bump_slice(), arena.alloc(ret))
}
Closure(_, _symbol, _, loc_args, boxed_body) => {
let (loc_body, ret_var) = *boxed_body;
let name = name.unwrap_or_else(||
// Give the closure a name like "_0" or "_1".
// We know procs.len() will be unique!
format!("_{}", procs.len()).into());
add_closure(env, name, loc_body.value, ret_var, &loc_args, procs)
}
Call(boxed, loc_args, _) => {
let (_, loc_expr, _) = *boxed;
let mut args = Vec::with_capacity_in(loc_args.len(), env.arena);
for (_, loc_arg) in loc_args {
args.push(from_can(env, loc_arg.value, procs, None));
}
match from_can(env, loc_expr.value, procs, None) {
Expr::Load(proc_name) => Expr::CallByName(proc_name, args.into_bump_slice()),
Expr::FunctionPointer(proc_name) => {
// Call by pointer - the closure was anonymous, e.g.
//
// ((\a -> a) 5)
//
// It might even be the anonymous result of a conditional:
//
// ((if x > 0 then \a -> a else \_ -> 0) 5)
Expr::CallByPointer(proc_name, args.into_bump_slice())
}
non_ptr => {
panic!(
"Tried to call by pointer, but encountered a non-pointer: {:?}",
non_ptr
);
}
}
}
When {
expr_var,
loc_cond,
branches,
..
} => {
debug_assert!(!branches.is_empty());
if branches.len() == 2 {
let arena = env.arena;
let mut iter = branches.into_iter();
let (loc_pat1, loc_then) = iter.next().unwrap();
let (loc_pat2, loc_else) = iter.next().unwrap();
match (&loc_pat1.value, &loc_pat2.value) {
(IntLiteral(int), IntLiteral(_)) | (IntLiteral(int), Underscore) => {
let cond_lhs = arena.alloc(from_can(env, loc_cond.value, procs, None));
let cond_rhs = arena.alloc(Expr::Int(*int));
let pass = arena.alloc(from_can(env, loc_then.value, procs, None));
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
Expr::Cond {
cond_lhs,
cond_rhs,
pass,
fail,
ret_var: expr_var,
}
}
(FloatLiteral(float), FloatLiteral(_)) | (FloatLiteral(float), Underscore) => {
let cond_lhs = arena.alloc(from_can(env, loc_cond.value, procs, None));
let cond_rhs = arena.alloc(Expr::Float(*float));
let pass = arena.alloc(from_can(env, loc_then.value, procs, None));
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
Expr::Cond {
cond_lhs,
cond_rhs,
pass,
fail,
ret_var: expr_var,
}
}
_ => {
panic!("TODO handle more conds");
}
}
} else if branches.len() == 1 {
// A when-expression with exactly 1 branch is essentially a LetNonRec.
// As such, we can compile it direcly to a Store.
let arena = env.arena;
let mut stored = Vec::with_capacity_in(1, arena);
let (loc_pattern, loc_branch) = branches.into_iter().next().unwrap();
store_pattern(
env,
loc_pattern.value,
loc_branch.value,
expr_var,
procs,
&mut stored,
);
let ret = from_can(env, loc_cond.value, procs, None);
Expr::Store(stored.into_bump_slice(), arena.alloc(ret))
} else {
// /// More than two conditional branches, e.g. a 3-way when-expression
// Expr::Branches {
// /// The left-hand side of the conditional. We compile this to LLVM once,
// /// then reuse it to test against each different compiled cond_rhs value.
// cond_lhs: &'a Expr<'a>,
// /// ( cond_rhs, pass, fail )
// branches: &'a [(Expr<'a>, Expr<'a>, Expr<'a>)],
// ret_var: Variable,
// },
panic!("TODO support when-expressions of more than 2 branches.");
}
}
other => panic!("TODO convert canonicalized {:?} to ll::Expr", other),
}
}
fn add_closure<'a, 'ctx>(
env: &Env<'a, 'ctx>,
name: InlinableString,
can_body: can::expr::Expr,
ret_var: Variable,
loc_args: &[(Variable, Located<Pattern>)],
procs: &mut Procs<'a, 'ctx>,
) -> Expr<'a> {
let subs = &env.subs;
let context = env.context;
let arena = env.arena;
let ret_content = subs.get_without_compacting(ret_var).content;
let ret_type = content_to_basic_type(&ret_content, subs, context).unwrap_or_else(|err| {
panic!(
"Error converting function return value content to basic type: {:?}",
err
)
});
let mut arg_names = Vec::with_capacity_in(loc_args.len(), arena);
let mut arg_basic_types = Vec::with_capacity_in(loc_args.len(), arena);
let mut proc_args = Vec::with_capacity_in(loc_args.len(), arena);
for (arg_var, loc_arg) in loc_args.iter() {
let content = subs.get_without_compacting(*arg_var).content;
arg_basic_types.push(
content_to_basic_type(&content, subs, context).unwrap_or_else(|err| {
panic!(
"Error converting function arg content to basic type: {:?}",
err
)
}),
);
let layout = match Layout::from_content(arena, content, subs) {
Ok(layout) => layout,
Err(()) => {
return invalid_closure(env, name, ret_type, procs);
}
};
let arg_name: InlinableString = match &loc_arg.value {
Pattern::Identifier(name) => name.as_str().into(),
_ => {
panic!("TODO determine arg_name for pattern {:?}", loc_arg.value);
}
};
arg_names.push(arg_name.clone());
proc_args.push((layout, arg_name));
}
let fn_type = ret_type.fn_type(arg_basic_types.into_bump_slice(), false);
let fn_val = env.module.add_function(&name, fn_type, None);
let proc = Proc {
args: proc_args.into_bump_slice(),
body: from_can(env, can_body, procs, None),
closes_over: Layout::ZeroSized,
};
procs.insert(name.clone(), (Some(proc), fn_val));
Expr::FunctionPointer(name)
}
fn invalid_closure<'a, 'ctx>(
env: &Env<'a, 'ctx>,
name: InlinableString,
ret_type: BasicTypeEnum<'ctx>,
procs: &mut Procs<'a, 'ctx>,
) -> Expr<'a> {
let fn_type = ret_type.fn_type(&[], false);
let fn_val = env.module.add_function(&name, fn_type, None);
procs.insert(name.clone(), (None, fn_val));
Expr::FunctionPointer(name)
}
fn store_pattern<'a, 'ctx>(
env: &Env<'a, 'ctx>,
can_pat: Pattern,
can_expr: can::expr::Expr,
var: Variable,
procs: &mut Procs<'a, 'ctx>,
stored: &mut Vec<'a, (InlinableString, Variable, Expr<'a>)>,
) {
use crate::can::pattern::Pattern::*;
// If we're defining a named closure, insert it into Procs and then
// remove the Let. When code gen later goes to look it up, it'll be in Procs!
//
// Before:
//
// identity = \a -> a
//
// identity 5
//
// After: (`identity` is now in Procs)
//
// identity 5
//
match can_pat {
Identifier(name) => stored.push((name.into(), var, from_can(env, can_expr, procs, None))),
Underscore => {
// Since _ is never read, it's safe to reassign it.
stored.push(("_".into(), var, from_can(env, can_expr, procs, None)))
}
_ => {
panic!("TODO store_pattern for {:?}", can_pat);
}
}
}

95
src/ll/layout.rs Normal file
View file

@ -0,0 +1,95 @@
use crate::subs::{Content, FlatType, Subs};
use bumpalo::collections::Vec;
use bumpalo::Bump;
use inlinable_string::InlinableString;
/// Types for code gen must be monomorphic. No type variables allowed!
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Layout<'a> {
/// A unary type like {} or [ NoOtherTags ] - this does not need to be a value at runtime.
ZeroSized,
/// A function. The types of its arguments, then the type of its return value.
FunctionPointer(&'a [Layout<'a>], &'a Layout<'a>),
Struct(&'a [(InlinableString, Layout<'a>)]),
/// Applying a type to some arguments (e.g. Map.Map String Int)
Apply {
module_name: InlinableString,
name: InlinableString,
args: &'a [Layout<'a>],
},
Pointer(&'a Layout<'a>),
}
impl<'a> Layout<'a> {
/// Returns Err(()) if given an error, or Ok(Layout) if given a non-erroneous Structure.
/// Panics if given a FlexVar or RigidVar, since those should have been
/// monomorphized away already!
pub fn from_content(arena: &'a Bump, content: Content, subs: &Subs) -> Result<Self, ()> {
use crate::subs::Content::*;
match content {
var @ FlexVar(_) | var @ RigidVar(_) => {
panic!("Layout::from_content encountered an unresolved {:?}", var);
}
Structure(flat_type) => layout_from_flat_type(arena, flat_type, subs),
Alias(_, _, _, _) => {
panic!("TODO recursively resolve type aliases in Layout::from_content");
}
Error => Err(()),
}
}
}
fn layout_from_flat_type<'a>(
arena: &'a Bump,
flat_type: FlatType,
subs: &Subs,
) -> Result<Layout<'a>, ()> {
use crate::subs::FlatType::*;
match flat_type {
Apply {
module_name,
name,
args,
} => {
let mut layout_args = Vec::with_capacity_in(args.len(), arena);
for arg_var in args {
let arg_content = subs.get_without_compacting(arg_var).content;
layout_args.push(Layout::from_content(arena, arg_content, subs)?);
}
Ok(Layout::Apply {
module_name: module_name.as_str().into(),
name: name.as_str().into(),
args: layout_args.into_bump_slice(),
})
}
Func(args, ret_var) => {
let mut fn_args = Vec::with_capacity_in(args.len(), arena);
for arg_var in args {
let arg_content = subs.get_without_compacting(arg_var).content;
fn_args.push(Layout::from_content(arena, arg_content, subs)?);
}
let ret_content = subs.get_without_compacting(ret_var).content;
let ret = Layout::from_content(arena, ret_content, subs)?;
Ok(Layout::FunctionPointer(
fn_args.into_bump_slice(),
arena.alloc(ret),
))
}
Record(_, _) => {
panic!("TODO make Layout for non-empty Record");
}
Erroneous(_) => Err(()),
EmptyRecord => Ok(Layout::Struct(&[])),
}
}

2
src/ll/mod.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod expr;
pub mod layout;

View file

@ -12,11 +12,12 @@ mod helpers;
#[cfg(test)]
mod test_gen {
use crate::helpers::can_expr;
use bumpalo::Bump;
use inkwell::context::Context;
use inkwell::execution_engine::JitFunction;
use inkwell::types::BasicType;
use inkwell::OptimizationLevel;
use roc::gen::compile::compile_standalone_expr;
use roc::gen::build::build_can_expr;
use roc::gen::convert::content_to_basic_type;
use roc::gen::env::Env;
use roc::infer::infer_expr;
@ -24,6 +25,7 @@ mod test_gen {
macro_rules! assert_evals_to {
($src:expr, $expected:expr, $ty:ty) => {
let arena = Bump::new();
let (expr, _output, _problems, var_store, variable, constraint) = can_expr($src);
let mut subs = Subs::new(var_store.into());
let mut unify_problems = Vec::new();
@ -49,9 +51,9 @@ mod test_gen {
subs,
builder: &builder,
context: &context,
module: &module,
module: arena.alloc(module),
};
let ret = compile_standalone_expr(&env, function, &expr);
let ret = build_can_expr(&env, function, expr);
builder.build_return(Some(&ret));
@ -229,37 +231,66 @@ mod test_gen {
);
}
#[test]
fn gen_basic_fn() {
assert_evals_to!(
indoc!(
r#"
always42 : Num.Num Int.Integer -> Num.Num Int.Integer
always42 = \num -> 42
// #[test]
// fn gen_basic_fn() {
// assert_evals_to!(
// indoc!(
// r#"
// always42 : Num.Num Int.Integer -> Num.Num Int.Integer
// always42 = \num -> 42
always42 5
"#
),
42,
i64
);
}
// always42 5
// "#
// ),
// 42,
// i64
// );
// }
#[test]
fn gen_when_fn() {
assert_evals_to!(
indoc!(
r#"
limitedNegate = \num ->
when num is
1 -> -1
0 -> 0
// #[test]
// fn gen_when_fn() {
// assert_evals_to!(
// indoc!(
// r#"
// limitedNegate = \num ->
// when num is
// 1 -> -1
// 0 -> 0
limitedNegate 1
"#
),
42,
i64
);
}
// limitedNegate 1
// "#
// ),
// 42,
// i64
// );
// }
// #[test]
// fn apply_unnamed_fn() {
// assert_evals_to!(
// // We could improve the perf of this scenario by
// indoc!(
// r#"
// (\a -> a) 5
// "#
// ),
// 5,
// i64
// );
// }
// #[test]
// fn return_unnamed_fn() {
// assert_evals_to!(
// indoc!(
// r#"
// alwaysIdentity = \_ -> (\a -> a)
// (alwaysIdentity 1) 3.14
// "#
// ),
// 3.14,
// f64
// );
// }
}