Fix function pointers for Cranelift and LLVM

This commit is contained in:
Richard Feldman 2020-01-07 22:52:46 -05:00
parent 68a46b4433
commit d74aff960a
8 changed files with 168 additions and 481 deletions

View file

@ -11,7 +11,7 @@ use cranelift_module::{Backend, FuncId, Linkage, Module};
use inlinable_string::InlinableString;
use crate::collections::ImMap;
use crate::crane::convert::{content_to_crane_type, type_from_layout};
use crate::crane::convert::{content_to_crane_type, sig_from_layout, type_from_layout};
use crate::mono::expr::{Expr, Proc, Procs};
use crate::mono::layout::Layout;
use crate::subs::Subs;
@ -23,7 +23,7 @@ pub enum ScopeEntry {
Stack { expr_type: Type, slot: StackSlot },
Heap { expr_type: Type, ptr: Value },
Arg { expr_type: Type, param: Value },
FuncId(FuncId),
Func { sig: Signature, func_id: FuncId },
}
pub struct Env<'a> {
@ -78,7 +78,7 @@ pub fn build_expr<'a, B: Backend>(
let content = subs.get_without_compacting(*var).content;
let layout = Layout::from_content(arena, content, subs)
.unwrap_or_else(|()| panic!("TODO generate a runtime error here!"));
let expr_type = type_from_layout(&layout, subs);
let expr_type = type_from_layout(cfg, &layout, subs);
let slot = builder.create_stack_slot(StackSlotData::new(
StackSlotKind::ExplicitSlot,
@ -117,7 +117,7 @@ pub fn build_expr<'a, B: Backend>(
}
let fn_id = match scope.get(name) {
Some(ScopeEntry::FuncId(id)) => *id,
Some(ScopeEntry::Func{ func_id, .. }) => *func_id,
other => panic!(
"CallByName could not find function named {:?} in scope; instead, found {:?} in scope {:?}",
name, other, scope
@ -132,43 +132,40 @@ pub fn build_expr<'a, B: Backend>(
results[0]
}
}
// FunctionPointer(ref fn_name) => {
// let ptr = env
// .module
// .get_function(fn_name)
// .unwrap_or_else(|| {
// panic!("Could not get pointer to unknown function {:?}", fn_name)
// })
// .as_global_value()
// .as_pointer_value();
FunctionPointer(ref name) => {
let fn_id = match scope.get(name) {
Some(ScopeEntry::Func{ func_id, .. }) => *func_id,
other => panic!(
"FunctionPointer could not find function named {:?} in scope; instead, found {:?} in scope {:?}",
name, other, scope
),
};
// BasicValueEnum::PointerValue(ptr)
// }
// CallByPointer(ref _ptr, ref args) => {
// let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity(args.len());
let func_ref = module.declare_func_in_func(fn_id, &mut builder.func);
// for arg in args.iter() {
// arg_vals.push(build_expr(env, scope, arg, procs));
// }
builder.ins().func_addr(env.cfg.pointer_type(), func_ref)
}
CallByPointer(ref sub_expr, ref args, ref fn_var) => {
let subs = &env.subs;
let mut arg_vals = Vec::with_capacity(args.len());
// panic!("TODO do a load(ptr) to get back the pointer, then pass *that* in here!");
for arg in args.iter() {
arg_vals.push(build_expr(env, scope, module, builder, arg, procs));
}
// // let call = match build_expr(env, scope, 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
// // );
// // }
// // };
let content = subs.get_without_compacting(*fn_var).content;
let layout = Layout::from_content(env.arena, content, &subs)
.unwrap_or_else(|()| panic!("TODO generate a runtime error here!"));
let sig = sig_from_layout(env.cfg, module, layout, &subs);
let callee = build_expr(env, scope, module, builder, sub_expr, procs);
let sig_ref = builder.import_signature(sig);
let call = builder.ins().call_indirect(sig_ref, callee, &arg_vals);
let results = builder.inst_results(call);
// // call.try_as_basic_value()
// // .left()
// // .unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."))
// }
debug_assert!(results.len() == 1);
results[0]
}
Load(name) => match scope.get(name) {
Some(ScopeEntry::Stack { expr_type, slot }) => {
builder
@ -181,7 +178,7 @@ pub fn build_expr<'a, B: Backend>(
.ins()
.load(*expr_type, MemFlags::new(), *ptr, Offset32::new(0))
}
Some(ScopeEntry::FuncId(_fn_id)) => {
Some(ScopeEntry::Func { .. }) => {
panic!("TODO I don't yet know how to return fn pointers")
}
None => panic!("Could not find a var for {:?} in scope {:?}", name, scope),
@ -341,6 +338,7 @@ pub fn declare_proc<'a, B: Backend>(
) -> (FuncId, Signature) {
let args = proc.args;
let subs = &env.subs;
let cfg = env.cfg;
let ret_content = subs.get_without_compacting(proc.ret_var).content;
// TODO this content_to_crane_type is duplicated when building this Proc
let ret_type = content_to_crane_type(&ret_content, subs, env.cfg).unwrap_or_else(|err| {
@ -358,7 +356,7 @@ pub fn declare_proc<'a, B: Backend>(
// Add params to the signature
for (layout, _name, _var) in args.iter() {
let arg_type = type_from_layout(&layout, subs);
let arg_type = type_from_layout(cfg, &layout, subs);
sig.params.push(AbiParam::new(arg_type));
}
@ -385,6 +383,7 @@ pub fn define_proc_body<'a, B: Backend>(
) {
let args = proc.args;
let subs = &env.subs;
let cfg = env.cfg;
// Build the body of the function
{
@ -409,7 +408,7 @@ pub fn define_proc_body<'a, B: Backend>(
//
let layout = Layout::from_content(arena, content, subs)
.unwrap_or_else(|()| panic!("TODO generate a runtime error here!"));
let expr_type = type_from_layout(&layout, subs);
let expr_type = type_from_layout(cfg, &layout, subs);
scope.insert(arg_name.clone(), ScopeEntry::Arg { expr_type, param });
}

View file

@ -1,9 +1,11 @@
use cranelift_codegen::ir::{types, Type};
use cranelift::prelude::AbiParam;
use cranelift_codegen::ir::{types, Signature, Type};
use cranelift_codegen::isa::TargetFrontendConfig;
use crate::mono::layout::Layout;
use crate::subs::FlatType::*;
use crate::subs::{Content, Subs};
use cranelift_module::{Backend, Module};
pub fn content_to_crane_type(
content: &Content,
@ -82,20 +84,15 @@ fn num_to_crane_type(content: Content) -> Result<Type, String> {
}
}
pub fn type_from_layout(layout: &Layout<'_>, _subs: &Subs) -> Type {
pub fn type_from_layout(cfg: TargetFrontendConfig, layout: &Layout<'_>, _subs: &Subs) -> Type {
use crate::mono::layout::Builtin::*;
use crate::mono::layout::Layout::*;
match layout {
FunctionPointer(_arg_layouts, _ret_layout) => {
panic!("TODO function poitner");
}
Pointer(_) | FunctionPointer(_, _) => cfg.pointer_type(),
Struct(_fields) => {
panic!("TODO layout_to_crane_type for Struct");
}
Pointer(_layout) => {
panic!("TODO layout_to_crane_type for Pointer");
}
Builtin(builtin) => match builtin {
Int64 => types::I64,
Float64 => types::F64,
@ -105,3 +102,32 @@ pub fn type_from_layout(layout: &Layout<'_>, _subs: &Subs) -> Type {
},
}
}
pub fn sig_from_layout<B: Backend>(
cfg: TargetFrontendConfig,
module: &mut Module<B>,
layout: Layout,
subs: &Subs,
) -> Signature {
match layout {
Layout::FunctionPointer(args, ret) => {
let ret_type = type_from_layout(cfg, &ret, subs);
let mut sig = module.make_signature();
// Add return type to the signature
sig.returns.push(AbiParam::new(ret_type));
// Add params to the signature
for layout in args.iter() {
let arg_type = type_from_layout(cfg, &layout, subs);
sig.params.push(AbiParam::new(arg_type));
}
sig
}
_ => {
panic!("Could not make Signature from Layout {:?}", layout);
}
}
}

View file

@ -116,9 +116,9 @@ pub fn build_expr<'a, 'ctx, 'env>(
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."))
call.try_as_basic_value().left().unwrap_or_else(|| {
panic!("LLVM error: Invalid call by name for name {:?}", name)
})
}
}
FunctionPointer(ref fn_name) => {
@ -133,30 +133,28 @@ pub fn build_expr<'a, 'ctx, 'env>(
BasicValueEnum::PointerValue(ptr)
}
CallByPointer(ref _ptr, ref args) => {
CallByPointer(ref sub_expr, ref args, _var) => {
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));
}
panic!("TODO do a load(ptr) to get back the pointer, then pass *that* in here!");
let call = match build_expr(env, scope, parent, sub_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
);
}
};
// 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."))
call.try_as_basic_value()
.left()
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."))
}
Load(name) => match scope.get(name) {
@ -368,6 +366,7 @@ pub fn build_proc<'a, 'ctx, 'env>(
}
let fn_type = get_fn_type(&ret_type, &arg_basic_types);
let fn_val = env
.module
.add_function(&name, fn_type, Some(Linkage::Private));
@ -401,9 +400,7 @@ pub fn build_proc<'a, 'ctx, 'env>(
}
pub fn verify_fn(fn_val: FunctionValue<'_>) {
if fn_val.verify(PRINT_FN_VERIFICATION_OUTPUT) {
// TODO call pass_manager.run_on(&fn_val) to optimize it!
} else {
if !fn_val.verify(PRINT_FN_VERIFICATION_OUTPUT) {
unsafe {
fn_val.delete();
}

View file

@ -46,8 +46,9 @@ pub fn content_to_basic_type<'ctx>(
arg_basic_types.push(content_to_basic_type(&arg_content, subs, context)?);
}
let fn_type = get_fn_type(&ret_type, arg_basic_types.as_slice());
let ptr_type = fn_type.ptr_type(AddressSpace::Generic);
Ok(fn_type.ptr_type(AddressSpace::Global).as_basic_type_enum())
Ok(ptr_type.as_basic_type_enum())
}
other => panic!("TODO handle content_to_basic_type for {:?}", other),
},

View file

@ -1,342 +0,0 @@
use crate::can;
use crate::can::pattern::Pattern;
use crate::collections::MutMap;
use crate::mono::layout::Layout;
use crate::region::Located;
use crate::subs::{Subs, Variable};
use bumpalo::collections::Vec;
use bumpalo::Bump;
use inlinable_string::InlinableString;
pub type Procs<'a> = MutMap<InlinableString, Option<Proc<'a>>>;
#[derive(Clone, Debug, PartialEq)]
pub struct Proc<'a> {
pub args: &'a [(Layout<'a>, InlinableString, Variable)],
pub body: Expr<'a>,
pub closes_over: Layout<'a>,
pub ret_var: Variable,
}
struct Env<'a> {
pub arena: &'a Bump,
pub subs: &'a Subs,
}
#[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(&'a Expr<'a>, &'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(
arena: &'a Bump,
subs: &'a Subs,
can_expr: can::expr::Expr,
procs: &mut Procs<'a>,
) -> Self {
let env = Env { arena, subs };
from_can(&env, can_expr, procs, None)
}
}
fn from_can<'a>(
env: &Env<'a>,
can_expr: can::expr::Expr,
procs: &mut Procs<'a>,
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
//
if let Identifier(name) = &loc_pattern.value {
if let Closure(_, _, _, _, _) = &loc_expr.value {
// 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()),
ptr => {
// 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(&*env.arena.alloc(ptr), args.into_bump_slice())
}
}
}
When {
cond_var,
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_cond.value,
cond_var,
procs,
&mut stored,
);
let ret = from_can(env, loc_branch.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>(
env: &Env<'a>,
name: InlinableString,
can_body: can::expr::Expr,
ret_var: Variable,
loc_args: &[(Variable, Located<Pattern>)],
procs: &mut Procs<'a>,
) -> Expr<'a> {
let subs = &env.subs;
let arena = env.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;
let layout = match Layout::from_content(arena, content, subs) {
Ok(layout) => layout,
Err(()) => {
// Invalid closure!
procs.insert(name.clone(), None);
return Expr::FunctionPointer(name);
}
};
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);
}
};
proc_args.push((layout, arg_name, *arg_var));
}
let proc = Proc {
args: proc_args.into_bump_slice(),
body: from_can(env, can_body, procs, None),
closes_over: Layout::Struct(&[]),
ret_var,
};
procs.insert(name.clone(), Some(proc));
Expr::FunctionPointer(name)
}
fn store_pattern<'a>(
env: &Env<'a>,
can_pat: Pattern,
can_expr: can::expr::Expr,
var: Variable,
procs: &mut Procs<'a>,
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);
}
}
}

View file

@ -45,8 +45,8 @@ pub enum Expr<'a> {
// Functions
FunctionPointer(InlinableString),
CallByPointer(&'a Expr<'a>, &'a [Expr<'a>]),
CallByName(InlinableString, &'a [Expr<'a>]),
CallByPointer(&'a Expr<'a>, &'a [Expr<'a>], Variable),
// Exactly two conditional branches, e.g. if/else
Cond {
@ -159,16 +159,13 @@ fn from_can<'a>(
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());
let name = name.unwrap_or_else(|| gen_closure_name(procs));
add_closure(env, name, loc_body.value, ret_var, &loc_args, procs)
}
Call(boxed, loc_args, _) => {
let (_, loc_expr, _) = *boxed;
let (fn_var, loc_expr, _) = *boxed;
let mut args = Vec::with_capacity_in(loc_args.len(), env.arena);
for (_, loc_arg) in loc_args {
@ -185,7 +182,7 @@ fn from_can<'a>(
// It might even be the anonymous result of a conditional:
//
// ((if x > 0 then \a -> a else \_ -> 0) 5)
Expr::CallByPointer(&*env.arena.alloc(ptr), args.into_bump_slice())
Expr::CallByPointer(&*env.arena.alloc(ptr), args.into_bump_slice(), fn_var)
}
}
}
@ -355,3 +352,9 @@ fn store_pattern<'a>(
}
}
}
fn gen_closure_name(procs: &Procs<'_>) -> InlinableString {
// Give the closure a name like "_0" or "_1".
// We know procs.len() will be unique!
format!("_{}", procs.len()).into()
}

View file

@ -75,14 +75,14 @@ mod test_crane {
let mut declared = Vec::with_capacity(procs.len());
// Declare all the Procs, then insert them into scope so their bodies
// can look up their FuncIds later when calling each other by value.
// can look up their Funcs in scope later when calling each other by value.
for (name, opt_proc) in procs.iter() {
if let Some(proc) = opt_proc {
let (fn_id, sig) = declare_proc(&env, &mut module, name.clone(), proc);
let (func_id, sig) = declare_proc(&env, &mut module, name.clone(), proc);
scope.insert(name.clone(), ScopeEntry::FuncId(fn_id));
declared.push((proc.clone(), sig.clone(), func_id));
declared.push((proc.clone(), sig, fn_id));
scope.insert(name.clone(), ScopeEntry::Func { func_id, sig });
}
}
@ -346,34 +346,33 @@ mod test_crane {
// );
// }
// #[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 apply_unnamed_fn() {
assert_evals_to!(
indoc!(
r#"
(\a -> a) 5
"#
),
5,
i64
);
}
// #[test]
// fn return_unnamed_fn() {
// assert_evals_to!(
// indoc!(
// r#"
// alwaysIdentity : Num.Num Int.Integer -> (Num.Num Float.FloatingPoint -> Num.Num Float.FloatingPoint)
// alwaysIdentity = \num ->
// (\a -> a)
#[test]
fn return_unnamed_fn() {
assert_evals_to!(
indoc!(
r#"
alwaysIdentity : Num.Num Int.Integer -> (Num.Num Float.FloatingPoint -> Num.Num Float.FloatingPoint)
alwaysIdentity = \num ->
(\a -> a)
// (alwaysIdentity 2) 3.14
// "#
// ),
// 3.14,
// f64
// );
// }
(alwaysIdentity 2) 3.14
"#
),
3.14,
f64
);
}
}

View file

@ -75,12 +75,17 @@ mod test_llvm {
// Add all the Procs to the module
for (name, opt_proc) in procs.clone() {
if let Some(proc) = opt_proc {
// NOTE: This is here to be uncommented in case verification fails.
// (This approach means we don't have to defensively clone name here.)
//
// println!("\n\nBuilding and then verifying function {}\n\n", name);
let fn_val = build_proc(&env, name, proc, &procs);
if fn_val.verify(true) {
fpm.run_on(&fn_val);
} else {
panic!("Function {} failed LLVM verification.", main_fn_name);
// NOTE: If this fails, uncomment the above println to debug
panic!("Non-main function failed LLVM verification.");
}
}
}
@ -332,34 +337,33 @@ mod test_llvm {
);
}
// #[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 apply_unnamed_fn() {
assert_evals_to!(
indoc!(
r#"
(\a -> a) 5
"#
),
5,
i64
);
}
// #[test]
// fn return_unnamed_fn() {
// assert_evals_to!(
// indoc!(
// r#"
// alwaysIdentity : Num.Num Int.Integer -> (Num.Num Float.FloatingPoint -> Num.Num Float.FloatingPoint)
// alwaysIdentity = \num ->
// (\a -> a)
#[test]
fn return_unnamed_fn() {
assert_evals_to!(
indoc!(
r#"
alwaysIdentity : Num.Num Int.Integer -> (Num.Num Float.FloatingPoint -> Num.Num Float.FloatingPoint)
alwaysIdentity = \num ->
(\a -> a)
// (alwaysIdentity 2) 3.14
// "#
// ),
// 3.14,
// f64
// );
// }
(alwaysIdentity 2) 3.14
"#
),
3.14,
f64
);
}
}