diff --git a/src/crane/build.rs b/src/crane/build.rs index 9aeae518d9..4ca6f83ac1 100644 --- a/src/crane/build.rs +++ b/src/crane/build.rs @@ -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 = 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 }); } diff --git a/src/crane/convert.rs b/src/crane/convert.rs index 744451ff9a..35c8a9df5c 100644 --- a/src/crane/convert.rs +++ b/src/crane/convert.rs @@ -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 { } } -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( + cfg: TargetFrontendConfig, + module: &mut Module, + 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); + } + } +} diff --git a/src/llvm/build.rs b/src/llvm/build.rs index ce202afbf1..21360ab3e1 100644 --- a/src/llvm/build.rs +++ b/src/llvm/build.rs @@ -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 = 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(); } diff --git a/src/llvm/convert.rs b/src/llvm/convert.rs index a1dcf2411a..9161740ff1 100644 --- a/src/llvm/convert.rs +++ b/src/llvm/convert.rs @@ -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), }, diff --git a/src/llvm/expr.rs b/src/llvm/expr.rs deleted file mode 100644 index 4497b3c68e..0000000000 --- a/src/llvm/expr.rs +++ /dev/null @@ -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>>; - -#[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, -) -> 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)], - 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); - } - } -} diff --git a/src/mono/expr.rs b/src/mono/expr.rs index 648190ad8f..765cfd0068 100644 --- a/src/mono/expr.rs +++ b/src/mono/expr.rs @@ -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() +} diff --git a/tests/test_crane.rs b/tests/test_crane.rs index 09057bc31a..6d15bd7dc2 100644 --- a/tests/test_crane.rs +++ b/tests/test_crane.rs @@ -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 + ); + } } diff --git a/tests/test_llvm.rs b/tests/test_llvm.rs index 525535bde3..2ffb28e883 100644 --- a/tests/test_llvm.rs +++ b/tests/test_llvm.rs @@ -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 + ); + } }