mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
Merge branch 'trunk' into access-record-fields
This commit is contained in:
commit
099145e4b4
14 changed files with 1022 additions and 158 deletions
|
@ -1326,7 +1326,7 @@ fn to_pending_def<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(_err) => panic!("TODO gracefully handle shadowing of type alias"),
|
Err(err) => panic!("TODO gracefully handle shadowing of type alias {:?}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,9 +120,7 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
let fn_id = match scope.get(name) {
|
let fn_id = match scope.get(name) {
|
||||||
Some(ScopeEntry::Func{ func_id, .. }) => *func_id,
|
Some(ScopeEntry::Func{ func_id, .. }) => *func_id,
|
||||||
other => panic!(
|
other => panic!(
|
||||||
"FunctionPointer could not find function named {:?} in scope; instead, found {:?} in scope {:?}",
|
"FunctionPointer could not find function named {:?} declared in scope (and it was not special-cased in crane::build as a builtin); instead, found {:?} in scope {:?}", name, other, scope),
|
||||||
name, other, scope
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let func_ref = module.declare_func_in_func(fn_id, &mut builder.func);
|
let func_ref = module.declare_func_in_func(fn_id, &mut builder.func);
|
||||||
|
@ -161,7 +159,10 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
Some(ScopeEntry::Func { .. }) => {
|
Some(ScopeEntry::Func { .. }) => {
|
||||||
panic!("TODO I don't yet know how to return fn pointers")
|
panic!("TODO I don't yet know how to return fn pointers")
|
||||||
}
|
}
|
||||||
None => panic!("Could not find a var for {:?} in scope {:?}", name, scope),
|
None => panic!(
|
||||||
|
"Could not resolve lookup for {:?} because no ScopeEntry was found for {:?} in scope {:?}",
|
||||||
|
name, name, scope
|
||||||
|
),
|
||||||
},
|
},
|
||||||
Struct { layout, fields } => {
|
Struct { layout, fields } => {
|
||||||
let cfg = env.cfg;
|
let cfg = env.cfg;
|
||||||
|
@ -590,20 +591,34 @@ fn call_by_name<'a, B: Backend>(
|
||||||
procs: &Procs<'a>,
|
procs: &Procs<'a>,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
match symbol {
|
match symbol {
|
||||||
Symbol::NUM_ADD => {
|
Symbol::INT_ADD | Symbol::NUM_ADD => {
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
let a = build_arg(&args[0], env, scope, module, builder, procs);
|
let a = build_arg(&args[0], env, scope, module, builder, procs);
|
||||||
let b = build_arg(&args[1], env, scope, module, builder, procs);
|
let b = build_arg(&args[1], env, scope, module, builder, procs);
|
||||||
|
|
||||||
builder.ins().iadd(a, b)
|
builder.ins().iadd(a, b)
|
||||||
}
|
}
|
||||||
Symbol::NUM_SUB => {
|
Symbol::FLOAT_ADD => {
|
||||||
|
debug_assert!(args.len() == 2);
|
||||||
|
let a = build_arg(&args[0], env, scope, module, builder, procs);
|
||||||
|
let b = build_arg(&args[1], env, scope, module, builder, procs);
|
||||||
|
|
||||||
|
builder.ins().fadd(a, b)
|
||||||
|
}
|
||||||
|
Symbol::INT_SUB | Symbol::NUM_SUB => {
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
let a = build_arg(&args[0], env, scope, module, builder, procs);
|
let a = build_arg(&args[0], env, scope, module, builder, procs);
|
||||||
let b = build_arg(&args[1], env, scope, module, builder, procs);
|
let b = build_arg(&args[1], env, scope, module, builder, procs);
|
||||||
|
|
||||||
builder.ins().isub(a, b)
|
builder.ins().isub(a, b)
|
||||||
}
|
}
|
||||||
|
Symbol::FLOAT_SUB => {
|
||||||
|
debug_assert!(args.len() == 2);
|
||||||
|
let a = build_arg(&args[0], env, scope, module, builder, procs);
|
||||||
|
let b = build_arg(&args[1], env, scope, module, builder, procs);
|
||||||
|
|
||||||
|
builder.ins().fsub(a, b)
|
||||||
|
}
|
||||||
Symbol::NUM_MUL => {
|
Symbol::NUM_MUL => {
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
let a = build_arg(&args[0], env, scope, module, builder, procs);
|
let a = build_arg(&args[0], env, scope, module, builder, procs);
|
||||||
|
@ -708,11 +723,8 @@ fn call_by_name<'a, B: Backend>(
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let fn_id = match scope.get(&symbol) {
|
let fn_id = match scope.get(&symbol) {
|
||||||
Some(ScopeEntry::Func{ func_id, .. }) => *func_id,
|
Some(ScopeEntry::Func { func_id, .. }) => *func_id,
|
||||||
other => panic!(
|
other => panic!("CallByName could not find function named {:?} declared in scope (and it was not special-cased in crane::build as a builtin); instead, found {:?} in scope {:?}", symbol, other, scope),
|
||||||
"CallByName could not find function named {:?} in scope; instead, found {:?} in scope {:?}",
|
|
||||||
symbol, other, scope
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
let local_func = module.declare_func_in_func(fn_id, &mut builder.func);
|
let local_func = module.declare_func_in_func(fn_id, &mut builder.func);
|
||||||
let mut arg_vals = Vec::with_capacity_in(args.len(), env.arena);
|
let mut arg_vals = Vec::with_capacity_in(args.len(), env.arena);
|
||||||
|
|
|
@ -536,35 +536,57 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
match symbol {
|
match symbol {
|
||||||
Symbol::NUM_ADD => {
|
Symbol::INT_ADD | Symbol::NUM_ADD => {
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
|
|
||||||
let int_val = env.builder.build_int_add(
|
let int_val = env.builder.build_int_add(
|
||||||
args[0].into_int_value(),
|
args[0].into_int_value(),
|
||||||
args[1].into_int_value(),
|
args[1].into_int_value(),
|
||||||
"ADD_I64",
|
"add_i64",
|
||||||
);
|
);
|
||||||
|
|
||||||
BasicValueEnum::IntValue(int_val)
|
BasicValueEnum::IntValue(int_val)
|
||||||
}
|
}
|
||||||
Symbol::NUM_SUB => {
|
Symbol::FLOAT_ADD => {
|
||||||
|
debug_assert!(args.len() == 2);
|
||||||
|
|
||||||
|
let float_val = env.builder.build_float_add(
|
||||||
|
args[0].into_float_value(),
|
||||||
|
args[1].into_float_value(),
|
||||||
|
"add_f64",
|
||||||
|
);
|
||||||
|
|
||||||
|
BasicValueEnum::FloatValue(float_val)
|
||||||
|
}
|
||||||
|
Symbol::INT_SUB | Symbol::NUM_SUB => {
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
|
|
||||||
let int_val = env.builder.build_int_sub(
|
let int_val = env.builder.build_int_sub(
|
||||||
args[0].into_int_value(),
|
args[0].into_int_value(),
|
||||||
args[1].into_int_value(),
|
args[1].into_int_value(),
|
||||||
"SUB_I64",
|
"sub_I64",
|
||||||
);
|
);
|
||||||
|
|
||||||
BasicValueEnum::IntValue(int_val)
|
BasicValueEnum::IntValue(int_val)
|
||||||
}
|
}
|
||||||
|
Symbol::FLOAT_SUB => {
|
||||||
|
debug_assert!(args.len() == 2);
|
||||||
|
|
||||||
|
let float_val = env.builder.build_float_sub(
|
||||||
|
args[0].into_float_value(),
|
||||||
|
args[1].into_float_value(),
|
||||||
|
"sub_f64",
|
||||||
|
);
|
||||||
|
|
||||||
|
BasicValueEnum::FloatValue(float_val)
|
||||||
|
}
|
||||||
Symbol::NUM_MUL => {
|
Symbol::NUM_MUL => {
|
||||||
debug_assert!(args.len() == 2);
|
debug_assert!(args.len() == 2);
|
||||||
|
|
||||||
let int_val = env.builder.build_int_mul(
|
let int_val = env.builder.build_int_mul(
|
||||||
args[0].into_int_value(),
|
args[0].into_int_value(),
|
||||||
args[1].into_int_value(),
|
args[1].into_int_value(),
|
||||||
"MUL_I64",
|
"mul_i64",
|
||||||
);
|
);
|
||||||
|
|
||||||
BasicValueEnum::IntValue(int_val)
|
BasicValueEnum::IntValue(int_val)
|
||||||
|
@ -574,7 +596,7 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let int_val = env
|
let int_val = env
|
||||||
.builder
|
.builder
|
||||||
.build_int_neg(args[0].into_int_value(), "NEGATE_I64");
|
.build_int_neg(args[0].into_int_value(), "negate_i64");
|
||||||
|
|
||||||
BasicValueEnum::IntValue(int_val)
|
BasicValueEnum::IntValue(int_val)
|
||||||
}
|
}
|
||||||
|
@ -587,7 +609,7 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
let elem_bytes = 8; // TODO Look this up instead of hardcoding it!
|
let elem_bytes = 8; // TODO Look this up instead of hardcoding it!
|
||||||
let elem_size = env.context.i64_type().const_int(elem_bytes, false);
|
let elem_size = env.context.i64_type().const_int(elem_bytes, false);
|
||||||
let offset = builder.build_int_mul(elem_index, elem_size, "MUL_OFFSET");
|
let offset = builder.build_int_mul(elem_index, elem_size, "mul_offset");
|
||||||
|
|
||||||
let elem_ptr = unsafe { builder.build_gep(list_ptr, &[offset], "elem") };
|
let elem_ptr = unsafe { builder.build_gep(list_ptr, &[offset], "elem") };
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,13 @@ mod test_gen {
|
||||||
use inkwell::passes::PassManager;
|
use inkwell::passes::PassManager;
|
||||||
use inkwell::types::BasicType;
|
use inkwell::types::BasicType;
|
||||||
use inkwell::OptimizationLevel;
|
use inkwell::OptimizationLevel;
|
||||||
use roc_collections::all::{ImMap, MutMap};
|
use roc_collections::all::ImMap;
|
||||||
use roc_gen::crane::build::{declare_proc, define_proc_body, ScopeEntry};
|
use roc_gen::crane::build::{declare_proc, define_proc_body, ScopeEntry};
|
||||||
use roc_gen::crane::convert::type_from_layout;
|
use roc_gen::crane::convert::type_from_layout;
|
||||||
use roc_gen::crane::imports::define_malloc;
|
use roc_gen::crane::imports::define_malloc;
|
||||||
use roc_gen::llvm::build::{build_proc, build_proc_header};
|
use roc_gen::llvm::build::{build_proc, build_proc_header};
|
||||||
use roc_gen::llvm::convert::basic_type_from_layout;
|
use roc_gen::llvm::convert::basic_type_from_layout;
|
||||||
use roc_mono::expr::Expr;
|
use roc_mono::expr::{Expr, Procs};
|
||||||
use roc_mono::layout::Layout;
|
use roc_mono::layout::Layout;
|
||||||
use roc_types::subs::Subs;
|
use roc_types::subs::Subs;
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
|
@ -46,7 +46,7 @@ mod test_gen {
|
||||||
let CanExprOut { loc_expr, var_store, var, constraint, home, interns, .. } = can_expr($src);
|
let CanExprOut { loc_expr, var_store, var, constraint, home, interns, .. } = can_expr($src);
|
||||||
let subs = Subs::new(var_store.into());
|
let subs = Subs::new(var_store.into());
|
||||||
let mut unify_problems = Vec::new();
|
let mut unify_problems = Vec::new();
|
||||||
let (content, subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
||||||
let shared_builder = settings::builder();
|
let shared_builder = settings::builder();
|
||||||
let shared_flags = settings::Flags::new(shared_builder);
|
let shared_flags = settings::Flags::new(shared_builder);
|
||||||
let mut module: Module<SimpleJITBackend> =
|
let mut module: Module<SimpleJITBackend> =
|
||||||
|
@ -65,7 +65,7 @@ mod test_gen {
|
||||||
let main_ret_type = type_from_layout(cfg, &layout);
|
let main_ret_type = type_from_layout(cfg, &layout);
|
||||||
|
|
||||||
// Compile and add all the Procs before adding main
|
// Compile and add all the Procs before adding main
|
||||||
let mut procs = MutMap::default();
|
let mut procs = Procs::default();
|
||||||
let mut env = roc_gen::crane::build::Env {
|
let mut env = roc_gen::crane::build::Env {
|
||||||
arena: &arena,
|
arena: &arena,
|
||||||
interns,
|
interns,
|
||||||
|
@ -75,7 +75,7 @@ mod test_gen {
|
||||||
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
||||||
|
|
||||||
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
||||||
let mono_expr = Expr::new(&arena, &subs, loc_expr.value, &mut procs, home, &mut ident_ids, POINTER_SIZE);
|
let mono_expr = Expr::new(&arena, &mut subs, loc_expr.value, &mut procs, home, &mut ident_ids, POINTER_SIZE);
|
||||||
|
|
||||||
// Put this module's ident_ids back in the interns
|
// Put this module's ident_ids back in the interns
|
||||||
env.interns.all_ident_ids.insert(home, ident_ids);
|
env.interns.all_ident_ids.insert(home, ident_ids);
|
||||||
|
@ -85,9 +85,9 @@ mod test_gen {
|
||||||
|
|
||||||
// Declare all the Procs, then insert them into scope so their bodies
|
// Declare all the Procs, then insert them into scope so their bodies
|
||||||
// can look up their Funcs in scope 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() {
|
for (name, opt_proc) in procs.as_map().into_iter() {
|
||||||
if let Some(proc) = opt_proc {
|
if let Some(proc) = opt_proc {
|
||||||
let (func_id, sig) = declare_proc(&env, &mut module, name.clone(), proc);
|
let (func_id, sig) = declare_proc(&env, &mut module, name, &proc);
|
||||||
|
|
||||||
declared.push((proc.clone(), sig.clone(), func_id));
|
declared.push((proc.clone(), sig.clone(), func_id));
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ mod test_gen {
|
||||||
let CanExprOut { loc_expr, var_store, var, constraint, home, interns, .. } = can_expr($src);
|
let CanExprOut { loc_expr, var_store, var, constraint, home, interns, .. } = can_expr($src);
|
||||||
let subs = Subs::new(var_store.into());
|
let subs = Subs::new(var_store.into());
|
||||||
let mut unify_problems = Vec::new();
|
let mut unify_problems = Vec::new();
|
||||||
let (content, subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
||||||
|
|
||||||
let context = Context::create();
|
let context = Context::create();
|
||||||
let module = context.create_module("app");
|
let module = context.create_module("app");
|
||||||
|
@ -220,11 +220,11 @@ mod test_gen {
|
||||||
module: arena.alloc(module),
|
module: arena.alloc(module),
|
||||||
pointer_bytes
|
pointer_bytes
|
||||||
};
|
};
|
||||||
let mut procs = MutMap::default();
|
let mut procs = Procs::default();
|
||||||
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
||||||
|
|
||||||
// Populate Procs and get the low-level Expr from the canonical Expr
|
// Populate Procs and get the low-level Expr from the canonical Expr
|
||||||
let main_body = Expr::new(&arena, &subs, loc_expr.value, &mut procs, home, &mut ident_ids, POINTER_SIZE);
|
let main_body = Expr::new(&arena, &mut subs, loc_expr.value, &mut procs, home, &mut ident_ids, POINTER_SIZE);
|
||||||
|
|
||||||
// Put this module's ident_ids back in the interns, so we can use them in Env.
|
// Put this module's ident_ids back in the interns, so we can use them in Env.
|
||||||
env.interns.all_ident_ids.insert(home, ident_ids);
|
env.interns.all_ident_ids.insert(home, ident_ids);
|
||||||
|
@ -234,7 +234,7 @@ mod test_gen {
|
||||||
// Add all the Proc headers to the module.
|
// Add all the Proc headers to the module.
|
||||||
// We have to do this in a separate pass first,
|
// We have to do this in a separate pass first,
|
||||||
// because their bodies may reference each other.
|
// because their bodies may reference each other.
|
||||||
for (symbol, opt_proc) in procs.clone().into_iter() {
|
for (symbol, opt_proc) in procs.as_map().into_iter() {
|
||||||
if let Some(proc) = opt_proc {
|
if let Some(proc) = opt_proc {
|
||||||
let (fn_val, arg_basic_types) = build_proc_header(&env, symbol, &proc);
|
let (fn_val, arg_basic_types) = build_proc_header(&env, symbol, &proc);
|
||||||
|
|
||||||
|
@ -271,7 +271,7 @@ mod test_gen {
|
||||||
&ImMap::default(),
|
&ImMap::default(),
|
||||||
main_fn,
|
main_fn,
|
||||||
&main_body,
|
&main_body,
|
||||||
&mut MutMap::default(),
|
&mut Procs::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
builder.build_return(Some(&ret));
|
builder.build_return(Some(&ret));
|
||||||
|
@ -309,7 +309,7 @@ mod test_gen {
|
||||||
let (loc_expr, _output, _problems, subs, var, constraint, home, interns) = uniq_expr($src);
|
let (loc_expr, _output, _problems, subs, var, constraint, home, interns) = uniq_expr($src);
|
||||||
|
|
||||||
let mut unify_problems = Vec::new();
|
let mut unify_problems = Vec::new();
|
||||||
let (content, subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
||||||
|
|
||||||
let context = Context::create();
|
let context = Context::create();
|
||||||
let module = context.create_module("app");
|
let module = context.create_module("app");
|
||||||
|
@ -355,11 +355,11 @@ mod test_gen {
|
||||||
module: arena.alloc(module),
|
module: arena.alloc(module),
|
||||||
pointer_bytes
|
pointer_bytes
|
||||||
};
|
};
|
||||||
let mut procs = MutMap::default();
|
let mut procs = Procs::default();
|
||||||
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
||||||
|
|
||||||
// Populate Procs and get the low-level Expr from the canonical Expr
|
// Populate Procs and get the low-level Expr from the canonical Expr
|
||||||
let main_body = Expr::new(&arena, &subs, loc_expr.value, &mut procs, home, &mut ident_ids, POINTER_SIZE);
|
let main_body = Expr::new(&arena, &mut subs, loc_expr.value, &mut procs, home, &mut ident_ids, POINTER_SIZE);
|
||||||
|
|
||||||
// Put this module's ident_ids back in the interns, so we can use them in Env.
|
// Put this module's ident_ids back in the interns, so we can use them in Env.
|
||||||
env.interns.all_ident_ids.insert(home, ident_ids);
|
env.interns.all_ident_ids.insert(home, ident_ids);
|
||||||
|
@ -369,12 +369,13 @@ mod test_gen {
|
||||||
// Add all the Proc headers to the module.
|
// Add all the Proc headers to the module.
|
||||||
// We have to do this in a separate pass first,
|
// We have to do this in a separate pass first,
|
||||||
// because their bodies may reference each other.
|
// because their bodies may reference each other.
|
||||||
for (symbol, opt_proc) in procs.clone().into_iter() {
|
for (symbol, opt_proc) in procs.as_map().into_iter() {
|
||||||
if let Some(proc) = opt_proc {
|
if let Some(proc) = opt_proc {
|
||||||
let (fn_val, arg_basic_types) = build_proc_header(&env, symbol, &proc);
|
let (fn_val, arg_basic_types) = build_proc_header(&env, symbol, &proc);
|
||||||
|
|
||||||
headers.push((proc, fn_val, arg_basic_types));
|
headers.push((proc, fn_val, arg_basic_types));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build each proc using its header info.
|
// Build each proc using its header info.
|
||||||
|
@ -406,7 +407,7 @@ mod test_gen {
|
||||||
&ImMap::default(),
|
&ImMap::default(),
|
||||||
main_fn,
|
main_fn,
|
||||||
&main_body,
|
&main_body,
|
||||||
&mut MutMap::default(),
|
&mut Procs::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
builder.build_return(Some(&ret));
|
builder.build_return(Some(&ret));
|
||||||
|
@ -786,6 +787,21 @@ mod test_gen {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn apply_identity_() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
identity = \a -> a
|
||||||
|
|
||||||
|
identity 5
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
5,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn apply_unnamed_fn() {
|
fn apply_unnamed_fn() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
@ -799,6 +815,19 @@ mod test_gen {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gen_add_f64() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
1.1 + 2.4 + 3
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
6.5,
|
||||||
|
f64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gen_add_i64() {
|
fn gen_add_i64() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
@ -812,6 +841,19 @@ mod test_gen {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gen_sub_f64() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
1.5 - 2.4 - 3
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
-3.9,
|
||||||
|
f64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gen_sub_i64() {
|
fn gen_sub_i64() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
|
|
@ -1048,8 +1048,11 @@ fn parse_and_constrain(
|
||||||
|
|
||||||
(module, ident_ids, constraint, problems)
|
(module, ident_ids, constraint, problems)
|
||||||
}
|
}
|
||||||
Err(_runtime_error) => {
|
Err(runtime_error) => {
|
||||||
panic!("TODO gracefully handle module canonicalization error");
|
panic!(
|
||||||
|
"TODO gracefully handle module canonicalization error {:?}",
|
||||||
|
runtime_error
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,14 @@ use std::{fmt, u32};
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub struct Symbol(u64);
|
pub struct Symbol(u64);
|
||||||
|
|
||||||
|
// When this is `true` (which it normally should be), Symbol's Debug::fmt implementation
|
||||||
|
// attempts to pretty print debug symbols using interns recorded using
|
||||||
|
// register_debug_idents calls (which should be made in debug mode).
|
||||||
|
// Set it to false if you want to see the raw ModuleId and IdentId ints,
|
||||||
|
// but please set it back to true before checking in the result!
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
const PRETTY_PRINT_DEBUG_SYMBOLS: bool = true;
|
||||||
|
|
||||||
/// In Debug builds only, Symbol has a name() method that lets
|
/// In Debug builds only, Symbol has a name() method that lets
|
||||||
/// you look up its name in a global intern table. This table is
|
/// you look up its name in a global intern table. This table is
|
||||||
/// behind a mutex, so it is neither populated nor available in release builds.
|
/// behind a mutex, so it is neither populated nor available in release builds.
|
||||||
|
@ -101,6 +109,7 @@ impl Symbol {
|
||||||
impl fmt::Debug for Symbol {
|
impl fmt::Debug for Symbol {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
if PRETTY_PRINT_DEBUG_SYMBOLS {
|
||||||
let module_id = self.module_id();
|
let module_id = self.module_id();
|
||||||
let ident_id = self.ident_id();
|
let ident_id = self.ident_id();
|
||||||
|
|
||||||
|
@ -122,6 +131,9 @@ impl fmt::Debug for Symbol {
|
||||||
fallback_debug_fmt(*self, f)
|
fallback_debug_fmt(*self, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
fallback_debug_fmt(*self, f)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
|
@ -547,11 +559,13 @@ macro_rules! define_builtins {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: Some of these builtins have a # at the beginning of their names.
|
||||||
|
// This is because they are for compiler use only, and should not cause
|
||||||
|
// namespace conflicts with userspace!
|
||||||
define_builtins! {
|
define_builtins! {
|
||||||
0 ATTR: "Attr" => {
|
0 ATTR: "#Attr" => {
|
||||||
0 UNDERSCORE: "_" // the _ used in pattern matches. This is Symbol 0.
|
0 UNDERSCORE: "_" // the _ used in pattern matches. This is Symbol 0.
|
||||||
1 ATTR_ATTR: "Attr" // the Attr.Attr type alias, used in uniqueness types
|
1 ATTR_ATTR: "Attr" // the #Attr.Attr type alias, used in uniqueness types.
|
||||||
2 ATTR_AT_ATTR: "@Attr" // the Attr.@Attr private tag
|
|
||||||
}
|
}
|
||||||
1 NUM: "Num" => {
|
1 NUM: "Num" => {
|
||||||
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
||||||
|
@ -575,6 +589,8 @@ define_builtins! {
|
||||||
4 INT_MOD: "mod"
|
4 INT_MOD: "mod"
|
||||||
5 INT_HIGHEST: "highest"
|
5 INT_HIGHEST: "highest"
|
||||||
6 INT_LOWEST: "lowest"
|
6 INT_LOWEST: "lowest"
|
||||||
|
7 INT_ADD: "#add"
|
||||||
|
8 INT_SUB: "#sub"
|
||||||
}
|
}
|
||||||
3 FLOAT: "Float" => {
|
3 FLOAT: "Float" => {
|
||||||
0 FLOAT_FLOAT: "Float" imported // the Float.Float type alias
|
0 FLOAT_FLOAT: "Float" imported // the Float.Float type alias
|
||||||
|
@ -585,6 +601,8 @@ define_builtins! {
|
||||||
5 FLOAT_SQRT: "sqrt"
|
5 FLOAT_SQRT: "sqrt"
|
||||||
6 FLOAT_HIGHEST: "highest"
|
6 FLOAT_HIGHEST: "highest"
|
||||||
7 FLOAT_LOWEST: "lowest"
|
7 FLOAT_LOWEST: "lowest"
|
||||||
|
8 FLOAT_ADD: "#add"
|
||||||
|
9 FLOAT_SUB: "#sub"
|
||||||
}
|
}
|
||||||
4 BOOL: "Bool" => {
|
4 BOOL: "Bool" => {
|
||||||
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
||||||
|
@ -606,7 +624,7 @@ define_builtins! {
|
||||||
2 LIST_ISEMPTY: "isEmpty"
|
2 LIST_ISEMPTY: "isEmpty"
|
||||||
3 LIST_GET: "get"
|
3 LIST_GET: "get"
|
||||||
4 LIST_SET: "set"
|
4 LIST_SET: "set"
|
||||||
5 LIST_SET_IN_PLACE: "set_in_place"
|
5 LIST_SET_IN_PLACE: "#setInPlace"
|
||||||
6 LIST_PUSH: "push"
|
6 LIST_PUSH: "push"
|
||||||
7 LIST_MAP: "map"
|
7 LIST_MAP: "map"
|
||||||
8 LIST_LENGTH: "length"
|
8 LIST_LENGTH: "length"
|
||||||
|
|
|
@ -3,16 +3,95 @@ use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_can;
|
use roc_can;
|
||||||
use roc_can::pattern::Pattern;
|
use roc_can::pattern::Pattern;
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||||
use roc_region::all::Located;
|
use roc_region::all::Located;
|
||||||
use roc_types::subs::{Content, FlatType, Subs, Variable};
|
use roc_types::subs::{Content, ContentHash, FlatType, Subs, Variable};
|
||||||
|
|
||||||
pub type Procs<'a> = MutMap<Symbol, Option<Proc<'a>>>;
|
#[derive(Clone, Debug, PartialEq, Default)]
|
||||||
|
pub struct Procs<'a> {
|
||||||
|
user_defined: MutMap<Symbol, PartialProc<'a>>,
|
||||||
|
anonymous: MutMap<Symbol, Option<Proc<'a>>>,
|
||||||
|
builtin: MutSet<Symbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Procs<'a> {
|
||||||
|
fn insert_user_defined(&mut self, symbol: Symbol, partial_proc: PartialProc<'a>) {
|
||||||
|
self.user_defined.insert(symbol, partial_proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_anonymous(&mut self, symbol: Symbol, proc: Option<Proc<'a>>) {
|
||||||
|
self.anonymous.insert(symbol, proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_specialization(
|
||||||
|
&mut self,
|
||||||
|
symbol: Symbol,
|
||||||
|
hash: ContentHash,
|
||||||
|
spec_name: Symbol,
|
||||||
|
proc: Option<Proc<'a>>,
|
||||||
|
) {
|
||||||
|
self.user_defined
|
||||||
|
.get_mut(&symbol)
|
||||||
|
.map(|partial_proc| partial_proc.specializations.insert(hash, (spec_name, proc)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_user_defined(&self, symbol: Symbol) -> Option<&PartialProc<'a>> {
|
||||||
|
self.user_defined.get(&symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
let anonymous: usize = self.anonymous.len();
|
||||||
|
let user_defined: usize = self
|
||||||
|
.user_defined
|
||||||
|
.values()
|
||||||
|
.map(|v| v.specializations.len())
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
anonymous + user_defined
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.len() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_builtin(&mut self, symbol: Symbol) {
|
||||||
|
self.builtin.insert(symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_map(&self) -> MutMap<Symbol, Option<Proc<'a>>> {
|
||||||
|
let mut result = MutMap::default();
|
||||||
|
|
||||||
|
for partial_proc in self.user_defined.values() {
|
||||||
|
for (_, (symbol, opt_proc)) in partial_proc.specializations.clone().into_iter() {
|
||||||
|
result.insert(symbol, opt_proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (symbol, proc) in self.anonymous.clone().into_iter() {
|
||||||
|
result.insert(symbol, proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
for symbol in self.builtin.iter() {
|
||||||
|
result.insert(*symbol, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct PartialProc<'a> {
|
||||||
|
pub annotation: Variable,
|
||||||
|
pub patterns: Vec<'a, Symbol>,
|
||||||
|
pub body: roc_can::expr::Expr,
|
||||||
|
pub specializations: MutMap<ContentHash, (Symbol, Option<Proc<'a>>)>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Proc<'a> {
|
pub struct Proc<'a> {
|
||||||
|
pub name: Symbol,
|
||||||
pub args: &'a [(Layout<'a>, Symbol)],
|
pub args: &'a [(Layout<'a>, Symbol)],
|
||||||
pub body: Expr<'a>,
|
pub body: Expr<'a>,
|
||||||
pub closes_over: Layout<'a>,
|
pub closes_over: Layout<'a>,
|
||||||
|
@ -21,10 +100,24 @@ pub struct Proc<'a> {
|
||||||
|
|
||||||
struct Env<'a, 'i> {
|
struct Env<'a, 'i> {
|
||||||
pub arena: &'a Bump,
|
pub arena: &'a Bump,
|
||||||
pub subs: &'a Subs,
|
pub subs: &'a mut Subs,
|
||||||
pub home: ModuleId,
|
pub home: ModuleId,
|
||||||
pub ident_ids: &'i mut IdentIds,
|
pub ident_ids: &'i mut IdentIds,
|
||||||
pub pointer_size: u32,
|
pub pointer_size: u32,
|
||||||
|
symbol_counter: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'i> Env<'a, 'i> {
|
||||||
|
pub fn fresh_symbol(&mut self) -> Symbol {
|
||||||
|
let ident_id = self
|
||||||
|
.ident_ids
|
||||||
|
.add(format!("_{}", self.symbol_counter).into());
|
||||||
|
self.symbol_counter += 1;
|
||||||
|
|
||||||
|
self.home.register_debug_idents(&self.ident_ids);
|
||||||
|
|
||||||
|
Symbol::new(self.home, ident_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -115,7 +208,7 @@ pub enum Expr<'a> {
|
||||||
impl<'a> Expr<'a> {
|
impl<'a> Expr<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
subs: &'a Subs,
|
subs: &'a mut Subs,
|
||||||
can_expr: roc_can::expr::Expr,
|
can_expr: roc_can::expr::Expr,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
home: ModuleId,
|
home: ModuleId,
|
||||||
|
@ -128,6 +221,7 @@ impl<'a> Expr<'a> {
|
||||||
home,
|
home,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
pointer_size,
|
pointer_size,
|
||||||
|
symbol_counter: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
from_can(&mut env, can_expr, procs, None)
|
from_can(&mut env, can_expr, procs, None)
|
||||||
|
@ -191,6 +285,79 @@ fn to_int_or_float(subs: &Subs, var: Variable) -> IntOrFloat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn patterns_to_when<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
patterns: std::vec::Vec<(Variable, Located<roc_can::pattern::Pattern>)>,
|
||||||
|
body_var: Variable,
|
||||||
|
mut body: Located<roc_can::expr::Expr>,
|
||||||
|
) -> (
|
||||||
|
Vec<'a, Variable>,
|
||||||
|
Vec<'a, Symbol>,
|
||||||
|
Located<roc_can::expr::Expr>,
|
||||||
|
) {
|
||||||
|
let mut arg_vars = Vec::with_capacity_in(patterns.len(), env.arena);
|
||||||
|
let mut symbols = Vec::with_capacity_in(patterns.len(), env.arena);
|
||||||
|
|
||||||
|
for (pattern_var, pattern) in patterns.into_iter().rev() {
|
||||||
|
let (new_symbol, new_body) = pattern_to_when(env, pattern_var, pattern, body_var, body);
|
||||||
|
body = new_body;
|
||||||
|
symbols.push(new_symbol);
|
||||||
|
arg_vars.push(pattern_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
(arg_vars, symbols, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// turn irrefutable patterns into when. For example
|
||||||
|
///
|
||||||
|
/// foo = \{ x } -> body
|
||||||
|
///
|
||||||
|
/// Assuming the above program typechecks, the pattern match cannot fail
|
||||||
|
/// (it is irrefutable). It becomes
|
||||||
|
///
|
||||||
|
/// foo = \r ->
|
||||||
|
/// when r is
|
||||||
|
/// { x } -> body
|
||||||
|
///
|
||||||
|
/// conversion of one-pattern when expressions will do the most optimal thing
|
||||||
|
fn pattern_to_when<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
pattern_var: Variable,
|
||||||
|
pattern: Located<roc_can::pattern::Pattern>,
|
||||||
|
body_var: Variable,
|
||||||
|
body: Located<roc_can::expr::Expr>,
|
||||||
|
) -> (Symbol, Located<roc_can::expr::Expr>) {
|
||||||
|
use roc_can::expr::Expr::*;
|
||||||
|
use roc_can::pattern::Pattern::*;
|
||||||
|
|
||||||
|
match &pattern.value {
|
||||||
|
Identifier(symbol) => (*symbol, body),
|
||||||
|
Underscore => {
|
||||||
|
// for underscore we generate a dummy Symbol
|
||||||
|
(env.fresh_symbol(), body)
|
||||||
|
}
|
||||||
|
|
||||||
|
AppliedTag(_, _, _) | RecordDestructure(_, _) | Shadowed(_, _) | UnsupportedPattern(_) => {
|
||||||
|
let symbol = env.fresh_symbol();
|
||||||
|
|
||||||
|
let wrapped_body = When {
|
||||||
|
cond_var: pattern_var,
|
||||||
|
expr_var: body_var,
|
||||||
|
loc_cond: Box::new(Located::at_zero(Var(symbol))),
|
||||||
|
branches: vec![(pattern, body)],
|
||||||
|
};
|
||||||
|
|
||||||
|
(symbol, Located::at_zero(wrapped_body))
|
||||||
|
}
|
||||||
|
|
||||||
|
// These patters are refutable, and thus should never occur outside a `when` expression
|
||||||
|
IntLiteral(_) | NumLiteral(_,_) | FloatLiteral(_) | StrLiteral(_) => {
|
||||||
|
unreachable!("refutable pattern {:?} where irrefutable pattern is expected. This should never happen!", pattern.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn from_can<'a>(
|
fn from_can<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
can_expr: roc_can::expr::Expr,
|
can_expr: roc_can::expr::Expr,
|
||||||
|
@ -256,22 +423,88 @@ fn from_can<'a>(
|
||||||
Expr::Store(stored.into_bump_slice(), arena.alloc(ret))
|
Expr::Store(stored.into_bump_slice(), arena.alloc(ret))
|
||||||
}
|
}
|
||||||
|
|
||||||
Closure(_, _, _, loc_args, boxed_body) => {
|
Closure(annotation, _, _, loc_args, boxed_body) => {
|
||||||
let (loc_body, ret_var) = *boxed_body;
|
let (loc_body, ret_var) = *boxed_body;
|
||||||
let symbol =
|
|
||||||
name.unwrap_or_else(|| gen_closure_name(procs, &mut env.ident_ids, env.home));
|
|
||||||
|
|
||||||
add_closure(env, symbol, loc_body.value, ret_var, &loc_args, procs)
|
// turn record/tag patterns into a when expression, e.g.
|
||||||
|
//
|
||||||
|
// foo = \{ x } -> body
|
||||||
|
//
|
||||||
|
// becomes
|
||||||
|
//
|
||||||
|
// foo = \r -> when r is { x } -> body
|
||||||
|
//
|
||||||
|
// conversion of one-pattern when expressions will do the most optimal thing
|
||||||
|
let (arg_vars, arg_symbols, body) = patterns_to_when(env, loc_args, ret_var, loc_body);
|
||||||
|
|
||||||
|
let symbol = match name {
|
||||||
|
Some(symbol) => {
|
||||||
|
// a named closure
|
||||||
|
procs.insert_user_defined(
|
||||||
|
symbol,
|
||||||
|
PartialProc {
|
||||||
|
annotation,
|
||||||
|
patterns: arg_symbols,
|
||||||
|
body: body.value,
|
||||||
|
specializations: MutMap::default(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// an anonymous closure. These will always be specialized already
|
||||||
|
// by the surrounding context
|
||||||
|
let symbol = env.fresh_symbol();
|
||||||
|
|
||||||
|
// Has the side-effect of monomorphizing record types
|
||||||
|
// turning the ext_var into EmptyRecord or EmptyTagUnion
|
||||||
|
let _ = ContentHash::from_var(annotation, env.subs);
|
||||||
|
|
||||||
|
let opt_proc = specialize_proc_body(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
annotation,
|
||||||
|
ret_var,
|
||||||
|
symbol,
|
||||||
|
&arg_vars,
|
||||||
|
&arg_symbols,
|
||||||
|
annotation,
|
||||||
|
body.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
procs.insert_anonymous(symbol, opt_proc);
|
||||||
|
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Expr::FunctionPointer(symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
Call(boxed, loc_args, _) => {
|
Call(boxed, loc_args, _) => {
|
||||||
let (fn_var, loc_expr, _) = *boxed;
|
use IntOrFloat::*;
|
||||||
|
|
||||||
|
let (fn_var, loc_expr, ret_var) = *boxed;
|
||||||
|
|
||||||
|
let specialize_builtin_functions = {
|
||||||
|
|symbol, subs: &Subs| match symbol {
|
||||||
|
Symbol::NUM_ADD => match to_int_or_float(subs, ret_var) {
|
||||||
|
FloatType => Symbol::FLOAT_ADD,
|
||||||
|
IntType => Symbol::INT_ADD,
|
||||||
|
},
|
||||||
|
Symbol::NUM_SUB => match to_int_or_float(subs, ret_var) {
|
||||||
|
FloatType => Symbol::FLOAT_SUB,
|
||||||
|
IntType => Symbol::INT_SUB,
|
||||||
|
},
|
||||||
|
_ => symbol,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
match from_can(env, loc_expr.value, procs, None) {
|
match from_can(env, loc_expr.value, procs, None) {
|
||||||
Expr::Load(proc_name) => {
|
Expr::Load(proc_name) => {
|
||||||
// Some functions can potentially mutate in-place.
|
// Some functions can potentially mutate in-place.
|
||||||
// If we have one of those, switch to the in-place version if appropriate.
|
// If we have one of those, switch to the in-place version if appropriate.
|
||||||
match proc_name {
|
match specialize_builtin_functions(proc_name, &env.subs) {
|
||||||
Symbol::LIST_SET => {
|
Symbol::LIST_SET => {
|
||||||
let subs = &env.subs;
|
let subs = &env.subs;
|
||||||
// The first arg is the one with the List in it.
|
// The first arg is the one with the List in it.
|
||||||
|
@ -298,12 +531,19 @@ fn from_can<'a>(
|
||||||
Symbol::LIST_SET
|
Symbol::LIST_SET
|
||||||
};
|
};
|
||||||
|
|
||||||
call_by_name(env, procs, new_name, loc_args)
|
call_by_name(env, procs, fn_var, ret_var, new_name, loc_args)
|
||||||
}
|
}
|
||||||
_ => call_by_name(env, procs, proc_name, loc_args),
|
_ => call_by_name(env, procs, fn_var, ret_var, proc_name, loc_args),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => call_by_name(env, procs, proc_name, loc_args),
|
specialized_proc_symbol => call_by_name(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
fn_var,
|
||||||
|
ret_var,
|
||||||
|
specialized_proc_symbol,
|
||||||
|
loc_args,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ptr => {
|
ptr => {
|
||||||
|
@ -337,7 +577,6 @@ fn from_can<'a>(
|
||||||
} => from_can_when(env, cond_var, expr_var, *loc_cond, branches, procs),
|
} => from_can_when(env, cond_var, expr_var, *loc_cond, branches, procs),
|
||||||
|
|
||||||
Record(ext_var, fields) => {
|
Record(ext_var, fields) => {
|
||||||
let subs = env.subs;
|
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
let mut field_bodies = Vec::with_capacity_in(fields.len(), arena);
|
let mut field_bodies = Vec::with_capacity_in(fields.len(), arena);
|
||||||
|
|
||||||
|
@ -347,7 +586,7 @@ fn from_can<'a>(
|
||||||
field_bodies.push((label, expr));
|
field_bodies.push((label, expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
let struct_layout = match Layout::from_var(arena, ext_var, subs, env.pointer_size) {
|
let struct_layout = match Layout::from_var(arena, ext_var, env.subs, env.pointer_size) {
|
||||||
Ok(layout) => layout,
|
Ok(layout) => layout,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
// Invalid field!
|
// Invalid field!
|
||||||
|
@ -401,10 +640,9 @@ fn from_can<'a>(
|
||||||
field,
|
field,
|
||||||
loc_expr,
|
loc_expr,
|
||||||
} => {
|
} => {
|
||||||
let subs = env.subs;
|
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
|
|
||||||
let struct_layout = match Layout::from_var(arena, ext_var, subs, env.pointer_size) {
|
let struct_layout = match Layout::from_var(arena, ext_var, env.subs, env.pointer_size) {
|
||||||
Ok(layout) => layout,
|
Ok(layout) => layout,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
// Invalid field!
|
// Invalid field!
|
||||||
|
@ -412,7 +650,8 @@ fn from_can<'a>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let field_layout = match Layout::from_var(arena, field_var, subs, env.pointer_size) {
|
let field_layout = match Layout::from_var(arena, field_var, env.subs, env.pointer_size)
|
||||||
|
{
|
||||||
Ok(layout) => layout,
|
Ok(layout) => layout,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
// Invalid field!
|
// Invalid field!
|
||||||
|
@ -434,9 +673,8 @@ fn from_can<'a>(
|
||||||
elem_var,
|
elem_var,
|
||||||
loc_elems,
|
loc_elems,
|
||||||
} => {
|
} => {
|
||||||
let subs = env.subs;
|
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
let elem_layout = match Layout::from_var(arena, elem_var, subs, env.pointer_size) {
|
let elem_layout = match Layout::from_var(arena, elem_var, env.subs, env.pointer_size) {
|
||||||
Ok(layout) => layout,
|
Ok(layout) => layout,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
panic!("TODO gracefully handle List with invalid element layout");
|
panic!("TODO gracefully handle List with invalid element layout");
|
||||||
|
@ -458,54 +696,6 @@ fn from_can<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_closure<'a>(
|
|
||||||
env: &mut Env<'a, '_>,
|
|
||||||
symbol: Symbol,
|
|
||||||
can_body: roc_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 layout = match Layout::from_var(arena, *arg_var, subs, env.pointer_size) {
|
|
||||||
Ok(layout) => layout,
|
|
||||||
Err(()) => {
|
|
||||||
// Invalid closure!
|
|
||||||
procs.insert(symbol, None);
|
|
||||||
|
|
||||||
return Expr::FunctionPointer(symbol);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let arg_name: Symbol = match &loc_arg.value {
|
|
||||||
Pattern::Identifier(symbol) => *symbol,
|
|
||||||
_ => {
|
|
||||||
panic!("TODO determine arg_name for pattern {:?}", loc_arg.value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
proc_args.push((layout, arg_name));
|
|
||||||
}
|
|
||||||
|
|
||||||
let ret_layout = Layout::from_var(arena, ret_var, subs, env.pointer_size)
|
|
||||||
.unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err));
|
|
||||||
|
|
||||||
let proc = Proc {
|
|
||||||
args: proc_args.into_bump_slice(),
|
|
||||||
body: from_can(env, can_body, procs, None),
|
|
||||||
closes_over: Layout::Struct(&[]),
|
|
||||||
ret_layout,
|
|
||||||
};
|
|
||||||
|
|
||||||
procs.insert(symbol, Some(proc));
|
|
||||||
|
|
||||||
Expr::FunctionPointer(symbol)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn store_pattern<'a>(
|
fn store_pattern<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
can_pat: Pattern,
|
can_pat: Pattern,
|
||||||
|
@ -552,12 +742,6 @@ fn store_pattern<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_closure_name(procs: &Procs<'_>, ident_ids: &mut IdentIds, home: ModuleId) -> Symbol {
|
|
||||||
let ident_id = ident_ids.add(format!("_{}", procs.len()).into());
|
|
||||||
|
|
||||||
Symbol::new(home, ident_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_can_when<'a>(
|
fn from_can_when<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
cond_var: Variable,
|
cond_var: Variable,
|
||||||
|
@ -811,19 +995,131 @@ fn from_can_when<'a>(
|
||||||
fn call_by_name<'a>(
|
fn call_by_name<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
|
fn_var: Variable,
|
||||||
|
ret_var: Variable,
|
||||||
proc_name: Symbol,
|
proc_name: Symbol,
|
||||||
loc_args: std::vec::Vec<(Variable, Located<roc_can::expr::Expr>)>,
|
loc_args: std::vec::Vec<(Variable, Located<roc_can::expr::Expr>)>,
|
||||||
) -> Expr<'a> {
|
) -> Expr<'a> {
|
||||||
|
// create specialized procedure to call
|
||||||
|
|
||||||
|
// If we need to specialize the body, this will get populated with the info
|
||||||
|
// we need to do that. This is defined outside the procs.get_user_defined(...) call
|
||||||
|
// because if we tried to specialize the body inside that match, we would
|
||||||
|
// get a borrow checker error about trying to borrow `procs` as mutable
|
||||||
|
// while there is still an active immutable borrow.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
let opt_specialize_body: Option<(
|
||||||
|
ContentHash,
|
||||||
|
Variable,
|
||||||
|
roc_can::expr::Expr,
|
||||||
|
Vec<'a, Symbol>,
|
||||||
|
)>;
|
||||||
|
|
||||||
|
let specialized_proc_name = if let Some(partial_proc) = procs.get_user_defined(proc_name) {
|
||||||
|
let content_hash = ContentHash::from_var(fn_var, env.subs);
|
||||||
|
|
||||||
|
if let Some(specialization) = partial_proc.specializations.get(&content_hash) {
|
||||||
|
opt_specialize_body = None;
|
||||||
|
|
||||||
|
// a specialization with this type hash already exists, use its symbol
|
||||||
|
specialization.0
|
||||||
|
} else {
|
||||||
|
opt_specialize_body = Some((
|
||||||
|
content_hash,
|
||||||
|
partial_proc.annotation,
|
||||||
|
partial_proc.body.clone(),
|
||||||
|
partial_proc.patterns.clone(),
|
||||||
|
));
|
||||||
|
|
||||||
|
// generate a symbol for this specialization
|
||||||
|
env.fresh_symbol()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
opt_specialize_body = None;
|
||||||
|
|
||||||
|
// This happens for built-in symbols (they are never defined as a Closure)
|
||||||
|
procs.insert_builtin(proc_name);
|
||||||
|
proc_name
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some((content_hash, annotation, body, loc_patterns)) = opt_specialize_body {
|
||||||
|
// register proc, so specialization doesn't loop infinitely
|
||||||
|
procs.insert_specialization(proc_name, content_hash, specialized_proc_name, None);
|
||||||
|
|
||||||
|
let arg_vars = loc_args.iter().map(|v| v.0).collect::<std::vec::Vec<_>>();
|
||||||
|
|
||||||
|
let proc = specialize_proc_body(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
fn_var,
|
||||||
|
ret_var,
|
||||||
|
specialized_proc_name,
|
||||||
|
&arg_vars,
|
||||||
|
&loc_patterns,
|
||||||
|
annotation,
|
||||||
|
body,
|
||||||
|
);
|
||||||
|
|
||||||
|
procs.insert_specialization(proc_name, content_hash, specialized_proc_name, proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate actual call
|
||||||
let mut args = Vec::with_capacity_in(loc_args.len(), env.arena);
|
let mut args = Vec::with_capacity_in(loc_args.len(), env.arena);
|
||||||
let subs = env.subs;
|
|
||||||
let arena = env.arena;
|
|
||||||
|
|
||||||
for (var, loc_arg) in loc_args {
|
for (var, loc_arg) in loc_args {
|
||||||
let layout = Layout::from_var(arena, var, subs, env.pointer_size)
|
let layout = Layout::from_var(&env.arena, var, &env.subs, env.pointer_size)
|
||||||
.unwrap_or_else(|err| panic!("TODO gracefully handle bad layout: {:?}", err));
|
.unwrap_or_else(|err| panic!("TODO gracefully handle bad layout: {:?}", err));
|
||||||
|
|
||||||
args.push((from_can(env, loc_arg.value, procs, None), layout));
|
args.push((from_can(env, loc_arg.value, procs, None), layout));
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::CallByName(proc_name, args.into_bump_slice())
|
Expr::CallByName(specialized_proc_name, args.into_bump_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn specialize_proc_body<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
procs: &mut Procs<'a>,
|
||||||
|
fn_var: Variable,
|
||||||
|
ret_var: Variable,
|
||||||
|
proc_name: Symbol,
|
||||||
|
loc_args: &[Variable],
|
||||||
|
pattern_symbols: &[Symbol],
|
||||||
|
annotation: Variable,
|
||||||
|
body: roc_can::expr::Expr,
|
||||||
|
) -> Option<Proc<'a>> {
|
||||||
|
// unify the called function with the specialized signature, then specialize the function body
|
||||||
|
let snapshot = env.subs.snapshot();
|
||||||
|
let unified = roc_unify::unify::unify(env.subs, annotation, fn_var);
|
||||||
|
debug_assert!(unified.mismatches.is_empty());
|
||||||
|
let specialized_body = from_can(env, body, procs, None);
|
||||||
|
// reset subs, so we don't get type errors when specializing for a different signature
|
||||||
|
env.subs.rollback_to(snapshot);
|
||||||
|
|
||||||
|
let mut proc_args = Vec::with_capacity_in(loc_args.len(), &env.arena);
|
||||||
|
|
||||||
|
for (arg_var, arg_name) in loc_args.iter().zip(pattern_symbols.iter()) {
|
||||||
|
let layout = match Layout::from_var(&env.arena, *arg_var, env.subs, env.pointer_size) {
|
||||||
|
Ok(layout) => layout,
|
||||||
|
Err(()) => {
|
||||||
|
// Invalid closure!
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
proc_args.push((layout, *arg_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret_layout = Layout::from_var(&env.arena, ret_var, env.subs, env.pointer_size)
|
||||||
|
.unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err));
|
||||||
|
|
||||||
|
let proc = Proc {
|
||||||
|
name: proc_name,
|
||||||
|
args: proc_args.into_bump_slice(),
|
||||||
|
body: specialized_body,
|
||||||
|
closes_over: Layout::Struct(&[]),
|
||||||
|
ret_layout,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(proc)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ mod test_mono {
|
||||||
use roc_module::ident::TagName::*;
|
use roc_module::ident::TagName::*;
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
use roc_mono::expr::Expr::{self, *};
|
use roc_mono::expr::Expr::{self, *};
|
||||||
|
use roc_mono::expr::Procs;
|
||||||
use roc_mono::layout::{Builtin, Layout};
|
use roc_mono::layout::{Builtin, Layout};
|
||||||
use roc_types::subs::Subs;
|
use roc_types::subs::Subs;
|
||||||
|
|
||||||
|
@ -42,10 +43,10 @@ mod test_mono {
|
||||||
} = can_expr(src);
|
} = can_expr(src);
|
||||||
let subs = Subs::new(var_store.into());
|
let subs = Subs::new(var_store.into());
|
||||||
let mut unify_problems = Vec::new();
|
let mut unify_problems = Vec::new();
|
||||||
let (_content, subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
let (_content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
||||||
|
|
||||||
// Compile and add all the Procs before adding main
|
// Compile and add all the Procs before adding main
|
||||||
let mut procs = MutMap::default();
|
let mut procs = Procs::default();
|
||||||
let mut ident_ids = interns.all_ident_ids.remove(&home).unwrap();
|
let mut ident_ids = interns.all_ident_ids.remove(&home).unwrap();
|
||||||
|
|
||||||
// assume 64-bit pointers
|
// assume 64-bit pointers
|
||||||
|
@ -54,7 +55,7 @@ mod test_mono {
|
||||||
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
||||||
let mono_expr = Expr::new(
|
let mono_expr = Expr::new(
|
||||||
&arena,
|
&arena,
|
||||||
&subs,
|
&mut subs,
|
||||||
loc_expr.value,
|
loc_expr.value,
|
||||||
&mut procs,
|
&mut procs,
|
||||||
home,
|
home,
|
||||||
|
@ -62,6 +63,8 @@ mod test_mono {
|
||||||
pointer_size,
|
pointer_size,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
dbg!(&procs);
|
||||||
|
|
||||||
// Put this module's ident_ids back in the interns
|
// Put this module's ident_ids back in the interns
|
||||||
interns.all_ident_ids.insert(home, ident_ids);
|
interns.all_ident_ids.insert(home, ident_ids);
|
||||||
|
|
||||||
|
@ -78,6 +81,191 @@ mod test_mono {
|
||||||
compiles_to("0.5", Float(0.5));
|
compiles_to("0.5", Float(0.5));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn float_addition() {
|
||||||
|
compiles_to(
|
||||||
|
"3.0 + 4",
|
||||||
|
CallByName(
|
||||||
|
Symbol::FLOAT_ADD,
|
||||||
|
&[
|
||||||
|
(Float(3.0), Layout::Builtin(Builtin::Float64)),
|
||||||
|
(Float(4.0), Layout::Builtin(Builtin::Float64)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn int_addition() {
|
||||||
|
compiles_to(
|
||||||
|
"0xDEADBEEF + 4",
|
||||||
|
CallByName(
|
||||||
|
Symbol::INT_ADD,
|
||||||
|
&[
|
||||||
|
(Int(3735928559), Layout::Builtin(Builtin::Int64)),
|
||||||
|
(Int(4), Layout::Builtin(Builtin::Int64)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn num_addition() {
|
||||||
|
// Default to Int for `Num *`
|
||||||
|
compiles_to(
|
||||||
|
"3 + 5",
|
||||||
|
CallByName(
|
||||||
|
Symbol::INT_ADD,
|
||||||
|
&[
|
||||||
|
(Int(3), Layout::Builtin(Builtin::Int64)),
|
||||||
|
(Int(5), Layout::Builtin(Builtin::Int64)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn specialize_closure() {
|
||||||
|
compiles_to(
|
||||||
|
r#"
|
||||||
|
f = \x -> x + 5
|
||||||
|
|
||||||
|
{ x: f 0x4, y: f 3.14 }
|
||||||
|
"#,
|
||||||
|
{
|
||||||
|
use self::Builtin::*;
|
||||||
|
use Layout::Builtin;
|
||||||
|
let home = test_home();
|
||||||
|
|
||||||
|
let gen_symbol_3 = Interns::from_index(home, 3);
|
||||||
|
let gen_symbol_4 = Interns::from_index(home, 4);
|
||||||
|
|
||||||
|
Struct {
|
||||||
|
fields: &[
|
||||||
|
(
|
||||||
|
"x".into(),
|
||||||
|
CallByName(gen_symbol_3, &[(Int(4), Builtin(Int64))]),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"y".into(),
|
||||||
|
CallByName(gen_symbol_4, &[(Float(3.14), Builtin(Float64))]),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
layout: Layout::Struct(&[
|
||||||
|
("x".into(), Builtin(Int64)),
|
||||||
|
("y".into(), Builtin(Float64)),
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn record_pattern() {
|
||||||
|
// compiles_to(
|
||||||
|
// r#"
|
||||||
|
// \{ x } -> x + 0x5
|
||||||
|
// "#,
|
||||||
|
// { Float(3.45) },
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[test]
|
||||||
|
// fn tag_pattern() {
|
||||||
|
// compiles_to(
|
||||||
|
// r#"
|
||||||
|
// \Foo x -> x + 0x5
|
||||||
|
// "#,
|
||||||
|
// { Float(3.45) },
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn polymorphic_identity() {
|
||||||
|
compiles_to(
|
||||||
|
r#"
|
||||||
|
id = \x -> x
|
||||||
|
|
||||||
|
id { x: id 0x4 }
|
||||||
|
"#,
|
||||||
|
{
|
||||||
|
use self::Builtin::*;
|
||||||
|
use Layout::Builtin;
|
||||||
|
let home = test_home();
|
||||||
|
|
||||||
|
let gen_symbol_3 = Interns::from_index(home, 3);
|
||||||
|
let gen_symbol_4 = Interns::from_index(home, 4);
|
||||||
|
|
||||||
|
CallByName(
|
||||||
|
gen_symbol_3,
|
||||||
|
&[(
|
||||||
|
Struct {
|
||||||
|
fields: &[(
|
||||||
|
"x".into(),
|
||||||
|
CallByName(gen_symbol_4, &[(Int(4), Builtin(Int64))]),
|
||||||
|
)],
|
||||||
|
layout: Layout::Struct(&[("x".into(), Builtin(Int64))]),
|
||||||
|
},
|
||||||
|
Layout::Struct(&[("x".into(), Builtin(Int64))]),
|
||||||
|
)],
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// needs LetRec to be converted to mono
|
||||||
|
// #[test]
|
||||||
|
// fn polymorphic_recursive() {
|
||||||
|
// compiles_to(
|
||||||
|
// r#"
|
||||||
|
// f = \x ->
|
||||||
|
// when x < 10 is
|
||||||
|
// True -> f (x + 1)
|
||||||
|
// False -> x
|
||||||
|
//
|
||||||
|
// { x: f 0x4, y: f 3.14 }
|
||||||
|
// "#,
|
||||||
|
// {
|
||||||
|
// use self::Builtin::*;
|
||||||
|
// use Layout::Builtin;
|
||||||
|
// let home = test_home();
|
||||||
|
//
|
||||||
|
// let gen_symbol_3 = Interns::from_index(home, 3);
|
||||||
|
// let gen_symbol_4 = Interns::from_index(home, 4);
|
||||||
|
//
|
||||||
|
// Float(3.4)
|
||||||
|
//
|
||||||
|
// },
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// needs layout for non-empty tag union
|
||||||
|
// #[test]
|
||||||
|
// fn is_nil() {
|
||||||
|
// let arena = Bump::new();
|
||||||
|
//
|
||||||
|
// compiles_to_with_interns(
|
||||||
|
// r#"
|
||||||
|
// LinkedList a : [ Cons a (LinkedList a), Nil ]
|
||||||
|
//
|
||||||
|
// isNil : LinkedList a -> Bool
|
||||||
|
// isNil = \list ->
|
||||||
|
// when list is
|
||||||
|
// Nil -> True
|
||||||
|
// Cons _ _ -> False
|
||||||
|
//
|
||||||
|
// listInt : LinkedList Int
|
||||||
|
// listInt = Nil
|
||||||
|
//
|
||||||
|
// isNil listInt
|
||||||
|
// "#,
|
||||||
|
// |interns| {
|
||||||
|
// let home = test_home();
|
||||||
|
// let var_is_nil = interns.symbol(home, "isNil".into());
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bool_literal() {
|
fn bool_literal() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
|
|
|
@ -13,9 +13,9 @@ mod helpers;
|
||||||
mod test_opt {
|
mod test_opt {
|
||||||
use crate::helpers::{infer_expr, uniq_expr};
|
use crate::helpers::{infer_expr, uniq_expr};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_collections::all::MutMap;
|
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::expr::Expr::{self, *};
|
use roc_mono::expr::Expr::{self, *};
|
||||||
|
use roc_mono::expr::Procs;
|
||||||
use roc_mono::layout::{Builtin, Layout};
|
use roc_mono::layout::{Builtin, Layout};
|
||||||
|
|
||||||
// HELPERS
|
// HELPERS
|
||||||
|
@ -25,10 +25,10 @@ mod test_opt {
|
||||||
let (loc_expr, _, _problems, subs, var, constraint, home, mut interns) = uniq_expr(src);
|
let (loc_expr, _, _problems, subs, var, constraint, home, mut interns) = uniq_expr(src);
|
||||||
|
|
||||||
let mut unify_problems = Vec::new();
|
let mut unify_problems = Vec::new();
|
||||||
let (_content, subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
let (_content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
||||||
|
|
||||||
// Compile and add all the Procs before adding main
|
// Compile and add all the Procs before adding main
|
||||||
let mut procs = MutMap::default();
|
let mut procs = Procs::default();
|
||||||
let mut ident_ids = interns.all_ident_ids.remove(&home).unwrap();
|
let mut ident_ids = interns.all_ident_ids.remove(&home).unwrap();
|
||||||
|
|
||||||
// assume 64-bit pointers
|
// assume 64-bit pointers
|
||||||
|
@ -37,7 +37,7 @@ mod test_opt {
|
||||||
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
||||||
let mono_expr = Expr::new(
|
let mono_expr = Expr::new(
|
||||||
&arena,
|
&arena,
|
||||||
&subs,
|
&mut subs,
|
||||||
loc_expr.value,
|
loc_expr.value,
|
||||||
&mut procs,
|
&mut procs,
|
||||||
home,
|
home,
|
||||||
|
|
|
@ -1310,8 +1310,11 @@ pub fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
region: loc_arg.region,
|
region: loc_arg.region,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Err(_malformed) => {
|
Err(malformed) => {
|
||||||
panic!("TODO early return malformed pattern");
|
panic!(
|
||||||
|
"TODO early return malformed pattern {:?}",
|
||||||
|
malformed
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,11 @@ impl<T> Located<T> {
|
||||||
pub fn at(region: Region, value: T) -> Located<T> {
|
pub fn at(region: Region, value: T) -> Located<T> {
|
||||||
Located { value, region }
|
Located { value, region }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn at_zero(value: T) -> Located<T> {
|
||||||
|
let region = Region::zero();
|
||||||
|
Located { value, region }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Located<T> {
|
impl<T> Located<T> {
|
||||||
|
|
|
@ -436,7 +436,7 @@ fn write_flat_type(
|
||||||
|
|
||||||
buf.push_str(" ]");
|
buf.push_str(" ]");
|
||||||
|
|
||||||
if let Some(content) = ext_content {
|
if let Err(content) = ext_content {
|
||||||
// This is an open tag union, so print the variable
|
// This is an open tag union, so print the variable
|
||||||
// right after the ']'
|
// right after the ']'
|
||||||
//
|
//
|
||||||
|
@ -483,7 +483,7 @@ fn write_flat_type(
|
||||||
|
|
||||||
buf.push_str(" ]");
|
buf.push_str(" ]");
|
||||||
|
|
||||||
if let Some(content) = ext_content {
|
if let Err(content) = ext_content {
|
||||||
// This is an open tag union, so print the variable
|
// This is an open tag union, so print the variable
|
||||||
// right after the ']'
|
// right after the ']'
|
||||||
//
|
//
|
||||||
|
@ -504,14 +504,14 @@ fn write_flat_type(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chase_ext_tag_union(
|
pub fn chase_ext_tag_union(
|
||||||
subs: &mut Subs,
|
subs: &Subs,
|
||||||
var: Variable,
|
var: Variable,
|
||||||
fields: &mut Vec<(TagName, Vec<Variable>)>,
|
fields: &mut Vec<(TagName, Vec<Variable>)>,
|
||||||
) -> Option<Content> {
|
) -> Result<(), Content> {
|
||||||
use FlatType::*;
|
use FlatType::*;
|
||||||
match subs.get(var).content {
|
match subs.get_without_compacting(var).content {
|
||||||
Content::Structure(EmptyTagUnion) => None,
|
Content::Structure(EmptyTagUnion) => Ok(()),
|
||||||
Content::Structure(TagUnion(tags, ext_var))
|
Content::Structure(TagUnion(tags, ext_var))
|
||||||
| Content::Structure(RecursiveTagUnion(_, tags, ext_var)) => {
|
| Content::Structure(RecursiveTagUnion(_, tags, ext_var)) => {
|
||||||
for (label, vars) in tags {
|
for (label, vars) in tags {
|
||||||
|
@ -521,7 +521,30 @@ fn chase_ext_tag_union(
|
||||||
chase_ext_tag_union(subs, ext_var, fields)
|
chase_ext_tag_union(subs, ext_var, fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
content => Some(content),
|
content => Err(content),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chase_ext_record(
|
||||||
|
subs: &Subs,
|
||||||
|
var: Variable,
|
||||||
|
fields: &mut MutMap<Lowercase, Variable>,
|
||||||
|
) -> Result<(), Content> {
|
||||||
|
use crate::subs::Content::*;
|
||||||
|
use crate::subs::FlatType::*;
|
||||||
|
|
||||||
|
match subs.get_without_compacting(var).content {
|
||||||
|
Structure(Record(sub_fields, sub_ext)) => {
|
||||||
|
fields.extend(sub_fields.into_iter());
|
||||||
|
|
||||||
|
chase_ext_record(subs, sub_ext, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
Structure(EmptyRecord) => Ok(()),
|
||||||
|
|
||||||
|
Alias(_, _, var) => chase_ext_record(subs, var, fields),
|
||||||
|
|
||||||
|
content => Err(content),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use roc_module::symbol::Symbol;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::iter::{once, Iterator};
|
use std::iter::{once, Iterator};
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
use std::sync::atomic::{AtomicU32, Ordering};
|
||||||
use ven_ena::unify::{InPlace, UnificationTable, UnifyKey};
|
use ven_ena::unify::{InPlace, Snapshot, UnificationTable, UnifyKey};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
pub struct Mark(i32);
|
pub struct Mark(i32);
|
||||||
|
@ -42,7 +42,7 @@ struct NameState {
|
||||||
normals: u32,
|
normals: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Clone)]
|
||||||
pub struct Subs {
|
pub struct Subs {
|
||||||
utable: UnificationTable<InPlace<Variable>>,
|
utable: UnificationTable<InPlace<Variable>>,
|
||||||
}
|
}
|
||||||
|
@ -402,6 +402,14 @@ impl Subs {
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.utable.is_empty()
|
self.utable.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn snapshot(&mut self) -> Snapshot<InPlace<Variable>> {
|
||||||
|
self.utable.snapshot()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rollback_to(&mut self, snapshot: Snapshot<InPlace<Variable>>) {
|
||||||
|
self.utable.rollback_to(snapshot)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -542,6 +550,250 @@ pub enum FlatType {
|
||||||
Boolean(boolean_algebra::Bool),
|
Boolean(boolean_algebra::Bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Copy)]
|
||||||
|
pub struct ContentHash(u64);
|
||||||
|
|
||||||
|
impl ContentHash {
|
||||||
|
pub fn from_var(var: Variable, subs: &mut Subs) -> Self {
|
||||||
|
use std::hash::Hasher;
|
||||||
|
|
||||||
|
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||||
|
Self::from_var_help(var, subs, &mut hasher);
|
||||||
|
|
||||||
|
ContentHash(hasher.finish())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_var_help<T>(var: Variable, subs: &mut Subs, hasher: &mut T)
|
||||||
|
where
|
||||||
|
T: std::hash::Hasher,
|
||||||
|
{
|
||||||
|
Self::from_content_help(var, &subs.get_without_compacting(var).content, subs, hasher)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_content_help<T>(var: Variable, content: &Content, subs: &mut Subs, hasher: &mut T)
|
||||||
|
where
|
||||||
|
T: std::hash::Hasher,
|
||||||
|
{
|
||||||
|
match content {
|
||||||
|
Content::Alias(_, _, actual) => {
|
||||||
|
// ensure an alias has the same hash as just the body of the alias
|
||||||
|
Self::from_var_help(*actual, subs, hasher)
|
||||||
|
}
|
||||||
|
Content::Structure(flat_type) => {
|
||||||
|
hasher.write_u8(0x10);
|
||||||
|
Self::from_flat_type_help(var, flat_type, subs, hasher)
|
||||||
|
}
|
||||||
|
Content::FlexVar(_) | Content::RigidVar(_) => {
|
||||||
|
hasher.write_u8(0x11);
|
||||||
|
}
|
||||||
|
Content::Error => {
|
||||||
|
hasher.write_u8(0x12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_flat_type_help<T>(
|
||||||
|
flat_type_var: Variable,
|
||||||
|
flat_type: &FlatType,
|
||||||
|
subs: &mut Subs,
|
||||||
|
hasher: &mut T,
|
||||||
|
) where
|
||||||
|
T: std::hash::Hasher,
|
||||||
|
{
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
match flat_type {
|
||||||
|
FlatType::Func(arguments, ret) => {
|
||||||
|
hasher.write_u8(0);
|
||||||
|
|
||||||
|
for var in arguments {
|
||||||
|
Self::from_var_help(*var, subs, hasher);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::from_var_help(*ret, subs, hasher);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatType::Apply(symbol, arguments) => {
|
||||||
|
hasher.write_u8(1);
|
||||||
|
|
||||||
|
symbol.hash(hasher);
|
||||||
|
|
||||||
|
for var in arguments {
|
||||||
|
Self::from_var_help(*var, subs, hasher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatType::EmptyRecord => {
|
||||||
|
hasher.write_u8(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatType::Record(record_fields, ext) => {
|
||||||
|
hasher.write_u8(3);
|
||||||
|
|
||||||
|
// NOTE: This function will modify the subs, putting all fields from the ext_var
|
||||||
|
// into the record itself, then setting the ext_var to EMPTY_RECORD
|
||||||
|
|
||||||
|
let mut fields = Vec::with_capacity(record_fields.len());
|
||||||
|
|
||||||
|
let mut extracted_fields_from_ext = false;
|
||||||
|
if *ext != Variable::EMPTY_RECORD {
|
||||||
|
let mut fields_map = MutMap::default();
|
||||||
|
match crate::pretty_print::chase_ext_record(subs, *ext, &mut fields_map) {
|
||||||
|
Err(Content::FlexVar(_)) | Ok(()) => {
|
||||||
|
if !fields_map.is_empty() {
|
||||||
|
extracted_fields_from_ext = true;
|
||||||
|
fields.extend(fields_map.into_iter());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(content) => panic!("Record with unexpected ext_var: {:?}", content),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fields.extend(record_fields.clone().into_iter());
|
||||||
|
fields.sort();
|
||||||
|
|
||||||
|
for (name, argument) in &fields {
|
||||||
|
name.hash(hasher);
|
||||||
|
Self::from_var_help(*argument, subs, hasher);
|
||||||
|
}
|
||||||
|
|
||||||
|
if *ext != Variable::EMPTY_RECORD {
|
||||||
|
// unify ext with empty record
|
||||||
|
let desc = subs.get(Variable::EMPTY_RECORD);
|
||||||
|
subs.union(Variable::EMPTY_RECORD, *ext, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if extracted_fields_from_ext {
|
||||||
|
let fields_map = fields.into_iter().collect();
|
||||||
|
|
||||||
|
subs.set_content(
|
||||||
|
flat_type_var,
|
||||||
|
Content::Structure(FlatType::Record(fields_map, Variable::EMPTY_RECORD)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatType::EmptyTagUnion => {
|
||||||
|
hasher.write_u8(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatType::TagUnion(tags, ext) => {
|
||||||
|
hasher.write_u8(5);
|
||||||
|
|
||||||
|
// NOTE: This function will modify the subs, putting all tags from the ext_var
|
||||||
|
// into the tag union itself, then setting the ext_var to EMPTY_TAG_UNION
|
||||||
|
|
||||||
|
let mut tag_vec = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
|
let mut extracted_fields_from_ext = false;
|
||||||
|
if *ext != Variable::EMPTY_TAG_UNION {
|
||||||
|
match crate::pretty_print::chase_ext_tag_union(subs, *ext, &mut tag_vec) {
|
||||||
|
Err(Content::FlexVar(_)) | Ok(()) => {
|
||||||
|
extracted_fields_from_ext = !tag_vec.is_empty();
|
||||||
|
}
|
||||||
|
Err(content) => panic!("TagUnion with unexpected ext_var: {:?}", content),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tag_vec.extend(tags.clone().into_iter());
|
||||||
|
tag_vec.sort();
|
||||||
|
for (name, arguments) in &tag_vec {
|
||||||
|
name.hash(hasher);
|
||||||
|
|
||||||
|
for var in arguments {
|
||||||
|
Self::from_var_help(*var, subs, hasher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *ext != Variable::EMPTY_TAG_UNION {
|
||||||
|
// unify ext with empty record
|
||||||
|
let desc = subs.get(Variable::EMPTY_TAG_UNION);
|
||||||
|
subs.union(Variable::EMPTY_TAG_UNION, *ext, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if extracted_fields_from_ext {
|
||||||
|
let fields_map = tag_vec.into_iter().collect();
|
||||||
|
|
||||||
|
subs.set_content(
|
||||||
|
flat_type_var,
|
||||||
|
Content::Structure(FlatType::TagUnion(
|
||||||
|
fields_map,
|
||||||
|
Variable::EMPTY_TAG_UNION,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatType::RecursiveTagUnion(rec, tags, ext) => {
|
||||||
|
// NOTE: rec is not hashed in. If all the tags and their arguments are the same,
|
||||||
|
// then the recursive tag unions are the same
|
||||||
|
hasher.write_u8(6);
|
||||||
|
|
||||||
|
// NOTE: This function will modify the subs, putting all tags from the ext_var
|
||||||
|
// into the tag union itself, then setting the ext_var to EMPTY_TAG_UNION
|
||||||
|
|
||||||
|
let mut tag_vec = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
|
let mut extracted_fields_from_ext = false;
|
||||||
|
if *ext != Variable::EMPTY_TAG_UNION {
|
||||||
|
match crate::pretty_print::chase_ext_tag_union(subs, *ext, &mut tag_vec) {
|
||||||
|
Err(Content::FlexVar(_)) | Ok(()) => {
|
||||||
|
extracted_fields_from_ext = !tag_vec.is_empty();
|
||||||
|
}
|
||||||
|
Err(content) => {
|
||||||
|
panic!("RecursiveTagUnion with unexpected ext_var: {:?}", content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tag_vec.extend(tags.clone().into_iter());
|
||||||
|
tag_vec.sort();
|
||||||
|
for (name, arguments) in &tag_vec {
|
||||||
|
name.hash(hasher);
|
||||||
|
|
||||||
|
for var in arguments {
|
||||||
|
Self::from_var_help(*var, subs, hasher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *ext != Variable::EMPTY_TAG_UNION {
|
||||||
|
// unify ext with empty record
|
||||||
|
let desc = subs.get(Variable::EMPTY_TAG_UNION);
|
||||||
|
subs.union(Variable::EMPTY_TAG_UNION, *ext, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if extracted_fields_from_ext {
|
||||||
|
let fields_map = tag_vec.into_iter().collect();
|
||||||
|
|
||||||
|
subs.set_content(
|
||||||
|
flat_type_var,
|
||||||
|
Content::Structure(FlatType::RecursiveTagUnion(
|
||||||
|
*rec,
|
||||||
|
fields_map,
|
||||||
|
Variable::EMPTY_TAG_UNION,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatType::Boolean(boolean) => {
|
||||||
|
hasher.write_u8(7);
|
||||||
|
match boolean.simplify(subs) {
|
||||||
|
Ok(_variables) => hasher.write_u8(1),
|
||||||
|
Err(crate::boolean_algebra::Atom::One) => hasher.write_u8(1),
|
||||||
|
Err(crate::boolean_algebra::Atom::Zero) => hasher.write_u8(0),
|
||||||
|
Err(crate::boolean_algebra::Atom::Variable(_)) => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatType::Erroneous(_problem) => {
|
||||||
|
hasher.write_u8(8);
|
||||||
|
//TODO hash the problem?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||||
pub enum Builtin {
|
pub enum Builtin {
|
||||||
Str,
|
Str,
|
||||||
|
|
|
@ -728,14 +728,14 @@ pub fn name_type_var(letters_used: u32, taken: &mut MutSet<Lowercase>) -> (Lower
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gather_fields(
|
pub fn gather_fields(
|
||||||
subs: &mut Subs,
|
subs: &Subs,
|
||||||
fields: MutMap<Lowercase, Variable>,
|
fields: MutMap<Lowercase, Variable>,
|
||||||
var: Variable,
|
var: Variable,
|
||||||
) -> RecordStructure {
|
) -> RecordStructure {
|
||||||
use crate::subs::Content::*;
|
use crate::subs::Content::*;
|
||||||
use crate::subs::FlatType::*;
|
use crate::subs::FlatType::*;
|
||||||
|
|
||||||
match subs.get(var).content {
|
match subs.get_without_compacting(var).content {
|
||||||
Structure(Record(sub_fields, sub_ext)) => {
|
Structure(Record(sub_fields, sub_ext)) => {
|
||||||
gather_fields(subs, union(fields, &sub_fields), sub_ext)
|
gather_fields(subs, union(fields, &sub_fields), sub_ext)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue