mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
Merge remote-tracking branch 'origin/trunk' into num
This commit is contained in:
commit
fb8e8570bc
17 changed files with 320 additions and 192 deletions
|
@ -33,7 +33,12 @@ pub struct Output {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
// Literals
|
// Literals
|
||||||
|
|
||||||
|
// Num stores the `a` variable in `Num a`. Not the same as the variable
|
||||||
|
// stored in Int and Float below, which is strictly for better error messages
|
||||||
Num(Variable, i64),
|
Num(Variable, i64),
|
||||||
|
|
||||||
|
// Int and Float store a variable to generate better error messages
|
||||||
Int(Variable, i64),
|
Int(Variable, i64),
|
||||||
Float(Variable, f64),
|
Float(Variable, f64),
|
||||||
Str(Box<str>),
|
Str(Box<str>),
|
||||||
|
|
|
@ -33,6 +33,7 @@ pub fn int_expr_from_result(
|
||||||
result: Result<i64, &str>,
|
result: Result<i64, &str>,
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
) -> Expr {
|
) -> Expr {
|
||||||
|
// Int stores a variable to generate better error messages
|
||||||
match result {
|
match result {
|
||||||
Ok(int) => Expr::Int(var_store.fresh(), int),
|
Ok(int) => Expr::Int(var_store.fresh(), int),
|
||||||
Err(raw) => {
|
Err(raw) => {
|
||||||
|
@ -51,6 +52,7 @@ pub fn float_expr_from_result(
|
||||||
result: Result<f64, &str>,
|
result: Result<f64, &str>,
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
) -> Expr {
|
) -> Expr {
|
||||||
|
// Float stores a variable to generate better error messages
|
||||||
match result {
|
match result {
|
||||||
Ok(float) => Expr::Float(var_store.fresh(), float),
|
Ok(float) => Expr::Float(var_store.fresh(), float),
|
||||||
Err(raw) => {
|
Err(raw) => {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use roc_can::expr::Expr::{self, *};
|
||||||
use roc_can::expr::Field;
|
use roc_can::expr::Field;
|
||||||
use roc_can::pattern::Pattern;
|
use roc_can::pattern::Pattern;
|
||||||
use roc_collections::all::{ImMap, SendMap};
|
use roc_collections::all::{ImMap, SendMap};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::Lowercase;
|
||||||
use roc_module::symbol::{ModuleId, Symbol};
|
use roc_module::symbol::{ModuleId, Symbol};
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
|
@ -326,14 +326,7 @@ pub fn constrain_expr(
|
||||||
branches,
|
branches,
|
||||||
final_else,
|
final_else,
|
||||||
} => {
|
} => {
|
||||||
// TODO use Bool alias here, so we don't allocate this type every time
|
let bool_type = Type::Variable(Variable::BOOL);
|
||||||
let bool_type = Type::TagUnion(
|
|
||||||
vec![
|
|
||||||
(TagName::Global("True".into()), vec![]),
|
|
||||||
(TagName::Global("False".into()), vec![]),
|
|
||||||
],
|
|
||||||
Box::new(Type::EmptyTagUnion),
|
|
||||||
);
|
|
||||||
let expect_bool = Expected::ForReason(Reason::IfCondition, bool_type, region);
|
let expect_bool = Expected::ForReason(Reason::IfCondition, bool_type, region);
|
||||||
let mut branch_cons = Vec::with_capacity(2 * branches.len() + 2);
|
let mut branch_cons = Vec::with_capacity(2 * branches.len() + 2);
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use roc_can::expected::{Expected, PExpected};
|
||||||
use roc_can::expr::{Expr, Field};
|
use roc_can::expr::{Expr, Field};
|
||||||
use roc_can::pattern::{Pattern, RecordDestruct};
|
use roc_can::pattern::{Pattern, RecordDestruct};
|
||||||
use roc_collections::all::{ImMap, ImSet, SendMap};
|
use roc_collections::all::{ImMap, ImSet, SendMap};
|
||||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
use roc_module::ident::{Ident, Lowercase};
|
||||||
use roc_module::symbol::{ModuleId, Symbol};
|
use roc_module::symbol::{ModuleId, Symbol};
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use roc_types::boolean_algebra::{Atom, Bool};
|
use roc_types::boolean_algebra::{Atom, Bool};
|
||||||
|
@ -802,14 +802,7 @@ pub fn constrain_expr(
|
||||||
final_else,
|
final_else,
|
||||||
} => {
|
} => {
|
||||||
// TODO use Bool alias here, so we don't allocate this type every time
|
// TODO use Bool alias here, so we don't allocate this type every time
|
||||||
let bool_type = Type::TagUnion(
|
let bool_type = Type::Variable(Variable::BOOL);
|
||||||
vec![
|
|
||||||
(TagName::Global("True".into()), vec![]),
|
|
||||||
(TagName::Global("False".into()), vec![]),
|
|
||||||
],
|
|
||||||
Box::new(Type::EmptyTagUnion),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut branch_cons = Vec::with_capacity(2 * branches.len() + 2);
|
let mut branch_cons = Vec::with_capacity(2 * branches.len() + 2);
|
||||||
let mut cond_uniq_vars = Vec::with_capacity(branches.len() + 2);
|
let mut cond_uniq_vars = Vec::with_capacity(branches.len() + 2);
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,8 @@ pub fn type_from_layout(cfg: TargetFrontendConfig, layout: &Layout<'_>) -> Type
|
||||||
Builtin(builtin) => match builtin {
|
Builtin(builtin) => match builtin {
|
||||||
Int64 => types::I64,
|
Int64 => types::I64,
|
||||||
Float64 => types::F64,
|
Float64 => types::F64,
|
||||||
|
Bool(_, _) => types::B1,
|
||||||
|
Byte(_) => types::I8,
|
||||||
Str | Map(_, _) | Set(_) | List(_) => cfg.pointer_type(),
|
Str | Map(_, _) | Set(_) | List(_) => cfg.pointer_type(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,8 @@ pub fn basic_type_from_layout<'ctx>(
|
||||||
Builtin(builtin) => match builtin {
|
Builtin(builtin) => match builtin {
|
||||||
Int64 => context.i64_type().as_basic_type_enum(),
|
Int64 => context.i64_type().as_basic_type_enum(),
|
||||||
Float64 => context.f64_type().as_basic_type_enum(),
|
Float64 => context.f64_type().as_basic_type_enum(),
|
||||||
|
Bool(_, _) => context.bool_type().as_basic_type_enum(),
|
||||||
|
Byte(_) => context.i8_type().as_basic_type_enum(),
|
||||||
Str => context
|
Str => context
|
||||||
.i8_type()
|
.i8_type()
|
||||||
.ptr_type(AddressSpace::Generic)
|
.ptr_type(AddressSpace::Generic)
|
||||||
|
|
|
@ -37,6 +37,9 @@ mod test_gen {
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
|
// Pointer size on 64-bit platforms
|
||||||
|
const POINTER_SIZE: u32 = std::mem::size_of::<u64>() as u32;
|
||||||
|
|
||||||
macro_rules! assert_crane_evals_to {
|
macro_rules! assert_crane_evals_to {
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
|
@ -57,7 +60,7 @@ mod test_gen {
|
||||||
let main_fn_name = "$Test.main";
|
let main_fn_name = "$Test.main";
|
||||||
|
|
||||||
// Compute main_fn_ret_type before moving subs to Env
|
// Compute main_fn_ret_type before moving subs to Env
|
||||||
let layout = Layout::from_content(&arena, content, &subs)
|
let layout = Layout::from_content(&arena, content, &subs, POINTER_SIZE)
|
||||||
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert content to layout. Err was {:?} and Subs were {:?}", err, subs));
|
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert content to layout. Err was {:?} and Subs were {:?}", err, subs));
|
||||||
let main_ret_type = type_from_layout(cfg, &layout);
|
let main_ret_type = type_from_layout(cfg, &layout);
|
||||||
|
|
||||||
|
@ -72,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);
|
let mono_expr = Expr::new(&arena, &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);
|
||||||
|
@ -195,7 +198,7 @@ mod test_gen {
|
||||||
fpm.initialize();
|
fpm.initialize();
|
||||||
|
|
||||||
// Compute main_fn_type before moving subs to Env
|
// Compute main_fn_type before moving subs to Env
|
||||||
let layout = Layout::from_content(&arena, content, &subs)
|
let layout = Layout::from_content(&arena, content, &subs, POINTER_SIZE)
|
||||||
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs));
|
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs));
|
||||||
let main_fn_type = basic_type_from_layout(&context, &layout)
|
let main_fn_type = basic_type_from_layout(&context, &layout)
|
||||||
.fn_type(&[], false);
|
.fn_type(&[], false);
|
||||||
|
@ -221,7 +224,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 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);
|
let main_body = Expr::new(&arena, &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);
|
||||||
|
@ -330,7 +333,7 @@ mod test_gen {
|
||||||
fpm.initialize();
|
fpm.initialize();
|
||||||
|
|
||||||
// Compute main_fn_type before moving subs to Env
|
// Compute main_fn_type before moving subs to Env
|
||||||
let layout = Layout::from_content(&arena, content, &subs)
|
let layout = Layout::from_content(&arena, content, &subs, POINTER_SIZE)
|
||||||
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs));
|
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs));
|
||||||
let main_fn_type = basic_type_from_layout(&context, &layout)
|
let main_fn_type = basic_type_from_layout(&context, &layout)
|
||||||
.fn_type(&[], false);
|
.fn_type(&[], false);
|
||||||
|
@ -356,7 +359,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 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);
|
let main_body = Expr::new(&arena, &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);
|
||||||
|
|
|
@ -24,6 +24,7 @@ struct Env<'a, 'i> {
|
||||||
pub subs: &'a Subs,
|
pub subs: &'a Subs,
|
||||||
pub home: ModuleId,
|
pub home: ModuleId,
|
||||||
pub ident_ids: &'i mut IdentIds,
|
pub ident_ids: &'i mut IdentIds,
|
||||||
|
pub pointer_size: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -89,7 +90,6 @@ pub enum Expr<'a> {
|
||||||
},
|
},
|
||||||
Tag {
|
Tag {
|
||||||
tag_layout: Layout<'a>,
|
tag_layout: Layout<'a>,
|
||||||
ext_layout: Layout<'a>,
|
|
||||||
name: TagName,
|
name: TagName,
|
||||||
arguments: &'a [Expr<'a>],
|
arguments: &'a [Expr<'a>],
|
||||||
},
|
},
|
||||||
|
@ -119,12 +119,14 @@ impl<'a> Expr<'a> {
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
home: ModuleId,
|
home: ModuleId,
|
||||||
ident_ids: &mut IdentIds,
|
ident_ids: &mut IdentIds,
|
||||||
|
pointer_size: u32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut env = Env {
|
let mut env = Env {
|
||||||
arena,
|
arena,
|
||||||
subs,
|
subs,
|
||||||
home,
|
home,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
pointer_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
from_can(&mut env, can_expr, procs, None)
|
from_can(&mut env, can_expr, procs, None)
|
||||||
|
@ -317,8 +319,8 @@ fn from_can<'a>(
|
||||||
args.push(from_can(env, loc_arg.value, procs, None));
|
args.push(from_can(env, loc_arg.value, procs, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
let layout =
|
let layout = Layout::from_var(env.arena, fn_var, env.subs, env.pointer_size)
|
||||||
Layout::from_var(env.arena, fn_var, env.subs).unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
|
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
|
||||||
});
|
});
|
||||||
Expr::CallByPointer(&*env.arena.alloc(ptr), args.into_bump_slice(), layout)
|
Expr::CallByPointer(&*env.arena.alloc(ptr), args.into_bump_slice(), layout)
|
||||||
|
@ -344,7 +346,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) {
|
let struct_layout = match Layout::from_var(arena, ext_var, subs, env.pointer_size) {
|
||||||
Ok(layout) => layout,
|
Ok(layout) => layout,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
// Invalid field!
|
// Invalid field!
|
||||||
|
@ -358,6 +360,40 @@ fn from_can<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Tag {
|
||||||
|
variant_var,
|
||||||
|
name,
|
||||||
|
arguments: args,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let arena = env.arena;
|
||||||
|
|
||||||
|
match Layout::from_var(arena, variant_var, &env.subs, env.pointer_size) {
|
||||||
|
Ok(Layout::Builtin(Builtin::Bool(_smaller, larger))) => Expr::Bool(name == larger),
|
||||||
|
Ok(Layout::Builtin(Builtin::Byte(tags))) => match tags.get(&name) {
|
||||||
|
Some(v) => Expr::Byte(*v),
|
||||||
|
None => panic!("Tag name is not part of the type"),
|
||||||
|
},
|
||||||
|
Ok(layout) => {
|
||||||
|
let mut arguments = Vec::with_capacity_in(args.len(), arena);
|
||||||
|
|
||||||
|
for (_, arg) in args {
|
||||||
|
arguments.push(from_can(env, arg.value, procs, None));
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Tag {
|
||||||
|
tag_layout: layout,
|
||||||
|
name,
|
||||||
|
arguments: arguments.into_bump_slice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(()) => {
|
||||||
|
// Invalid field!
|
||||||
|
panic!("TODO gracefully handle Access with invalid struct_layout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Access {
|
Access {
|
||||||
ext_var,
|
ext_var,
|
||||||
field_var,
|
field_var,
|
||||||
|
@ -367,7 +403,7 @@ fn from_can<'a>(
|
||||||
let subs = env.subs;
|
let subs = env.subs;
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
|
|
||||||
let struct_layout = match Layout::from_var(arena, ext_var, subs) {
|
let struct_layout = match Layout::from_var(arena, ext_var, subs, env.pointer_size) {
|
||||||
Ok(layout) => layout,
|
Ok(layout) => layout,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
// Invalid field!
|
// Invalid field!
|
||||||
|
@ -375,7 +411,7 @@ fn from_can<'a>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let field_layout = match Layout::from_var(arena, field_var, subs) {
|
let field_layout = match Layout::from_var(arena, field_var, subs, env.pointer_size) {
|
||||||
Ok(layout) => layout,
|
Ok(layout) => layout,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
// Invalid field!
|
// Invalid field!
|
||||||
|
@ -396,7 +432,7 @@ fn from_can<'a>(
|
||||||
} => {
|
} => {
|
||||||
let subs = env.subs;
|
let subs = env.subs;
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
let elem_layout = match Layout::from_var(arena, elem_var, subs) {
|
let elem_layout = match Layout::from_var(arena, elem_var, 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");
|
||||||
|
@ -431,7 +467,7 @@ fn add_closure<'a>(
|
||||||
let mut proc_args = Vec::with_capacity_in(loc_args.len(), arena);
|
let mut proc_args = Vec::with_capacity_in(loc_args.len(), arena);
|
||||||
|
|
||||||
for (arg_var, loc_arg) in loc_args.iter() {
|
for (arg_var, loc_arg) in loc_args.iter() {
|
||||||
let layout = match Layout::from_var(arena, *arg_var, subs) {
|
let layout = match Layout::from_var(arena, *arg_var, subs, env.pointer_size) {
|
||||||
Ok(layout) => layout,
|
Ok(layout) => layout,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
// Invalid closure!
|
// Invalid closure!
|
||||||
|
@ -451,7 +487,7 @@ fn add_closure<'a>(
|
||||||
proc_args.push((layout, arg_name));
|
proc_args.push((layout, arg_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret_layout = Layout::from_var(arena, ret_var, subs)
|
let ret_layout = Layout::from_var(arena, ret_var, subs, env.pointer_size)
|
||||||
.unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err));
|
.unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err));
|
||||||
|
|
||||||
let proc = Proc {
|
let proc = Proc {
|
||||||
|
@ -476,7 +512,7 @@ fn store_pattern<'a>(
|
||||||
) {
|
) {
|
||||||
use roc_can::pattern::Pattern::*;
|
use roc_can::pattern::Pattern::*;
|
||||||
|
|
||||||
let layout = match Layout::from_var(env.arena, var, env.subs) {
|
let layout = match Layout::from_var(env.arena, var, env.subs, env.pointer_size) {
|
||||||
Ok(layout) => layout,
|
Ok(layout) => layout,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
panic!("TODO gen a runtime error here");
|
panic!("TODO gen a runtime error here");
|
||||||
|
@ -575,8 +611,8 @@ fn from_can_when<'a>(
|
||||||
let cond_rhs = arena.alloc(cond_rhs_expr);
|
let cond_rhs = arena.alloc(cond_rhs_expr);
|
||||||
let pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
let pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
||||||
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
||||||
let ret_layout =
|
let ret_layout = Layout::from_var(arena, expr_var, env.subs, env.pointer_size)
|
||||||
Layout::from_var(arena, expr_var, env.subs).unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
panic!("TODO turn this into a RuntimeError {:?}", err)
|
panic!("TODO turn this into a RuntimeError {:?}", err)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -594,8 +630,8 @@ fn from_can_when<'a>(
|
||||||
let cond_rhs = arena.alloc(Expr::Int(*int));
|
let cond_rhs = arena.alloc(Expr::Int(*int));
|
||||||
let pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
let pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
||||||
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
||||||
let ret_layout =
|
let ret_layout = Layout::from_var(arena, expr_var, env.subs, env.pointer_size)
|
||||||
Layout::from_var(arena, expr_var, env.subs).unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
panic!("TODO turn this into a RuntimeError {:?}", err)
|
panic!("TODO turn this into a RuntimeError {:?}", err)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -613,8 +649,8 @@ fn from_can_when<'a>(
|
||||||
let cond_rhs = arena.alloc(Expr::Float(*float));
|
let cond_rhs = arena.alloc(Expr::Float(*float));
|
||||||
let pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
let pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
||||||
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
||||||
let ret_layout =
|
let ret_layout = Layout::from_var(arena, expr_var, env.subs, env.pointer_size)
|
||||||
Layout::from_var(arena, expr_var, env.subs).unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
panic!("TODO turn this into a RuntimeError {:?}", err)
|
panic!("TODO turn this into a RuntimeError {:?}", err)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -637,7 +673,7 @@ fn from_can_when<'a>(
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
let cond = from_can(env, loc_cond.value, procs, None);
|
let cond = from_can(env, loc_cond.value, procs, None);
|
||||||
let subs = &env.subs;
|
let subs = &env.subs;
|
||||||
let layout = Layout::from_var(arena, cond_var, subs)
|
let layout = Layout::from_var(arena, cond_var, subs, env.pointer_size)
|
||||||
.unwrap_or_else(|_| panic!("TODO generate a runtime error in from_can_when here!"));
|
.unwrap_or_else(|_| panic!("TODO generate a runtime error in from_can_when here!"));
|
||||||
|
|
||||||
// We can Switch on integers and tags, because they both have
|
// We can Switch on integers and tags, because they both have
|
||||||
|
@ -734,12 +770,12 @@ fn from_can_when<'a>(
|
||||||
debug_assert!(opt_default_branch.is_some());
|
debug_assert!(opt_default_branch.is_some());
|
||||||
let default_branch = opt_default_branch.unwrap();
|
let default_branch = opt_default_branch.unwrap();
|
||||||
|
|
||||||
let cond_layout =
|
let cond_layout = Layout::from_var(arena, cond_var, env.subs, env.pointer_size)
|
||||||
Layout::from_var(arena, cond_var, env.subs).unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
panic!("TODO turn cond_layout into a RuntimeError {:?}", err)
|
panic!("TODO turn cond_layout into a RuntimeError {:?}", err)
|
||||||
});
|
});
|
||||||
let ret_layout =
|
let ret_layout = Layout::from_var(arena, expr_var, env.subs, env.pointer_size)
|
||||||
Layout::from_var(arena, expr_var, env.subs).unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
panic!("TODO turn ret_layout into a RuntimeError {:?}", err)
|
panic!("TODO turn ret_layout into a RuntimeError {:?}", err)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -779,7 +815,7 @@ fn call_by_name<'a>(
|
||||||
let arena = env.arena;
|
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)
|
let layout = Layout::from_var(arena, var, 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));
|
||||||
|
|
|
@ -19,6 +19,8 @@ pub enum Layout<'a> {
|
||||||
pub enum Builtin<'a> {
|
pub enum Builtin<'a> {
|
||||||
Int64,
|
Int64,
|
||||||
Float64,
|
Float64,
|
||||||
|
Bool(TagName, TagName),
|
||||||
|
Byte(MutMap<TagName, u8>),
|
||||||
Str,
|
Str,
|
||||||
Map(&'a Layout<'a>, &'a Layout<'a>),
|
Map(&'a Layout<'a>, &'a Layout<'a>),
|
||||||
Set(&'a Layout<'a>),
|
Set(&'a Layout<'a>),
|
||||||
|
@ -29,20 +31,30 @@ impl<'a> Layout<'a> {
|
||||||
/// Returns Err(()) if given an error, or Ok(Layout) if given a non-erroneous Structure.
|
/// Returns Err(()) if given an error, or Ok(Layout) if given a non-erroneous Structure.
|
||||||
/// Panics if given a FlexVar or RigidVar, since those should have been
|
/// Panics if given a FlexVar or RigidVar, since those should have been
|
||||||
/// monomorphized away already!
|
/// monomorphized away already!
|
||||||
pub fn from_var(arena: &'a Bump, var: Variable, subs: &Subs) -> Result<Self, ()> {
|
pub fn from_var(
|
||||||
|
arena: &'a Bump,
|
||||||
|
var: Variable,
|
||||||
|
subs: &Subs,
|
||||||
|
pointer_size: u32,
|
||||||
|
) -> Result<Self, ()> {
|
||||||
let content = subs.get_without_compacting(var).content;
|
let content = subs.get_without_compacting(var).content;
|
||||||
|
|
||||||
Self::from_content(arena, content, subs)
|
Self::from_content(arena, content, subs, pointer_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_content(arena: &'a Bump, content: Content, subs: &Subs) -> Result<Self, ()> {
|
pub fn from_content(
|
||||||
|
arena: &'a Bump,
|
||||||
|
content: Content,
|
||||||
|
subs: &Subs,
|
||||||
|
pointer_size: u32,
|
||||||
|
) -> Result<Self, ()> {
|
||||||
use roc_types::subs::Content::*;
|
use roc_types::subs::Content::*;
|
||||||
|
|
||||||
match content {
|
match content {
|
||||||
var @ FlexVar(_) | var @ RigidVar(_) => {
|
var @ FlexVar(_) | var @ RigidVar(_) => {
|
||||||
panic!("Layout::from_content encountered an unresolved {:?}", var);
|
panic!("Layout::from_content encountered an unresolved {:?}", var);
|
||||||
}
|
}
|
||||||
Structure(flat_type) => layout_from_flat_type(arena, flat_type, subs),
|
Structure(flat_type) => layout_from_flat_type(arena, flat_type, subs, pointer_size),
|
||||||
|
|
||||||
Alias(Symbol::INT_INT, args, _) => {
|
Alias(Symbol::INT_INT, args, _) => {
|
||||||
debug_assert!(args.is_empty());
|
debug_assert!(args.is_empty());
|
||||||
|
@ -52,9 +64,12 @@ impl<'a> Layout<'a> {
|
||||||
debug_assert!(args.is_empty());
|
debug_assert!(args.is_empty());
|
||||||
Ok(Layout::Builtin(Builtin::Float64))
|
Ok(Layout::Builtin(Builtin::Float64))
|
||||||
}
|
}
|
||||||
Alias(_, _, var) => {
|
Alias(_, _, var) => Self::from_content(
|
||||||
Self::from_content(arena, subs.get_without_compacting(var).content, subs)
|
arena,
|
||||||
}
|
subs.get_without_compacting(var).content,
|
||||||
|
subs,
|
||||||
|
pointer_size,
|
||||||
|
),
|
||||||
Error => Err(()),
|
Error => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,6 +96,8 @@ impl<'a> Layout<'a> {
|
||||||
impl<'a> Builtin<'a> {
|
impl<'a> Builtin<'a> {
|
||||||
const I64_SIZE: u32 = std::mem::size_of::<i64>() as u32;
|
const I64_SIZE: u32 = std::mem::size_of::<i64>() as u32;
|
||||||
const F64_SIZE: u32 = std::mem::size_of::<f64>() as u32;
|
const F64_SIZE: u32 = std::mem::size_of::<f64>() as u32;
|
||||||
|
const BOOL_SIZE: u32 = std::mem::size_of::<bool>() as u32;
|
||||||
|
const BYTE_SIZE: u32 = std::mem::size_of::<u8>() as u32;
|
||||||
|
|
||||||
/// Number of machine words in an empty one of these
|
/// Number of machine words in an empty one of these
|
||||||
const STR_WORDS: u32 = 3;
|
const STR_WORDS: u32 = 3;
|
||||||
|
@ -94,6 +111,8 @@ impl<'a> Builtin<'a> {
|
||||||
match self {
|
match self {
|
||||||
Int64 => Builtin::I64_SIZE,
|
Int64 => Builtin::I64_SIZE,
|
||||||
Float64 => Builtin::F64_SIZE,
|
Float64 => Builtin::F64_SIZE,
|
||||||
|
Bool(_, _) => Builtin::BOOL_SIZE,
|
||||||
|
Byte(_) => Builtin::BYTE_SIZE,
|
||||||
Str => Builtin::STR_WORDS * pointer_size,
|
Str => Builtin::STR_WORDS * pointer_size,
|
||||||
Map(_, _) => Builtin::MAP_WORDS * pointer_size,
|
Map(_, _) => Builtin::MAP_WORDS * pointer_size,
|
||||||
Set(_) => Builtin::SET_WORDS * pointer_size,
|
Set(_) => Builtin::SET_WORDS * pointer_size,
|
||||||
|
@ -106,6 +125,7 @@ fn layout_from_flat_type<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
flat_type: FlatType,
|
flat_type: FlatType,
|
||||||
subs: &Subs,
|
subs: &Subs,
|
||||||
|
pointer_size: u32,
|
||||||
) -> Result<Layout<'a>, ()> {
|
) -> Result<Layout<'a>, ()> {
|
||||||
use roc_types::subs::FlatType::*;
|
use roc_types::subs::FlatType::*;
|
||||||
|
|
||||||
|
@ -131,7 +151,7 @@ fn layout_from_flat_type<'a>(
|
||||||
}
|
}
|
||||||
Symbol::STR_STR => Ok(Layout::Builtin(Builtin::Str)),
|
Symbol::STR_STR => Ok(Layout::Builtin(Builtin::Str)),
|
||||||
Symbol::LIST_LIST => {
|
Symbol::LIST_LIST => {
|
||||||
let elem_layout = Layout::from_var(arena, args[0], subs)?;
|
let elem_layout = Layout::from_var(arena, args[0], subs, pointer_size)?;
|
||||||
|
|
||||||
Ok(Layout::Builtin(Builtin::List(arena.alloc(elem_layout))))
|
Ok(Layout::Builtin(Builtin::List(arena.alloc(elem_layout))))
|
||||||
}
|
}
|
||||||
|
@ -145,7 +165,7 @@ fn layout_from_flat_type<'a>(
|
||||||
// For now, layout is unaffected by uniqueness.
|
// For now, layout is unaffected by uniqueness.
|
||||||
// (Incorporating refcounting may change this.)
|
// (Incorporating refcounting may change this.)
|
||||||
// Unwrap and continue
|
// Unwrap and continue
|
||||||
Layout::from_var(arena, wrapped_var, subs)
|
Layout::from_var(arena, wrapped_var, subs, pointer_size)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
panic!("TODO layout_from_flat_type for {:?}", Apply(symbol, args));
|
panic!("TODO layout_from_flat_type for {:?}", Apply(symbol, args));
|
||||||
|
@ -158,11 +178,16 @@ fn layout_from_flat_type<'a>(
|
||||||
for arg_var in args {
|
for arg_var in args {
|
||||||
let arg_content = subs.get_without_compacting(arg_var).content;
|
let arg_content = subs.get_without_compacting(arg_var).content;
|
||||||
|
|
||||||
fn_args.push(Layout::from_content(arena, arg_content, subs)?);
|
fn_args.push(Layout::from_content(
|
||||||
|
arena,
|
||||||
|
arg_content,
|
||||||
|
subs,
|
||||||
|
pointer_size,
|
||||||
|
)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret_content = subs.get_without_compacting(ret_var).content;
|
let ret_content = subs.get_without_compacting(ret_var).content;
|
||||||
let ret = Layout::from_content(arena, ret_content, subs)?;
|
let ret = Layout::from_content(arena, ret_content, subs, pointer_size)?;
|
||||||
|
|
||||||
Ok(Layout::FunctionPointer(
|
Ok(Layout::FunctionPointer(
|
||||||
fn_args.into_bump_slice(),
|
fn_args.into_bump_slice(),
|
||||||
|
@ -172,7 +197,7 @@ fn layout_from_flat_type<'a>(
|
||||||
Record(mut fields, ext_var) => {
|
Record(mut fields, ext_var) => {
|
||||||
flatten_record(&mut fields, ext_var, subs);
|
flatten_record(&mut fields, ext_var, subs);
|
||||||
let ext_content = subs.get_without_compacting(ext_var).content;
|
let ext_content = subs.get_without_compacting(ext_var).content;
|
||||||
let ext_layout = match Layout::from_content(arena, ext_content, subs) {
|
let ext_layout = match Layout::from_content(arena, ext_content, subs, pointer_size) {
|
||||||
Ok(layout) => layout,
|
Ok(layout) => layout,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
// Invalid record!
|
// Invalid record!
|
||||||
|
@ -200,13 +225,14 @@ fn layout_from_flat_type<'a>(
|
||||||
|
|
||||||
for (label, field_var) in fields {
|
for (label, field_var) in fields {
|
||||||
let field_content = subs.get_without_compacting(field_var).content;
|
let field_content = subs.get_without_compacting(field_var).content;
|
||||||
let field_layout = match Layout::from_content(arena, field_content, subs) {
|
let field_layout =
|
||||||
Ok(layout) => layout,
|
match Layout::from_content(arena, field_content, subs, pointer_size) {
|
||||||
Err(()) => {
|
Ok(layout) => layout,
|
||||||
// Invalid field!
|
Err(()) => {
|
||||||
panic!("TODO gracefully handle record with invalid field.var");
|
// Invalid field!
|
||||||
}
|
panic!("TODO gracefully handle record with invalid field.var");
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
field_layouts.push((label.clone(), field_layout));
|
field_layouts.push((label.clone(), field_layout));
|
||||||
}
|
}
|
||||||
|
@ -243,7 +269,50 @@ fn layout_from_flat_type<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
panic!("TODO handle a tag union with mutliple tags: {:?}", tags);
|
// Check if we can turn this tag union into an enum
|
||||||
|
// The arguments of all tags must have size 0.
|
||||||
|
// That is trivially the case when there are no arguments
|
||||||
|
//
|
||||||
|
// [ Orange, Apple, Banana ]
|
||||||
|
//
|
||||||
|
// But when one-tag tag unions are optimized away, we can also use an enum for
|
||||||
|
//
|
||||||
|
// [ Foo [ Unit ], Bar [ Unit ] ]
|
||||||
|
let arguments_have_size_0 = || {
|
||||||
|
tags.iter().all(|(_, args)| {
|
||||||
|
args.iter().all(|var| {
|
||||||
|
Layout::from_var(arena, *var, subs, pointer_size)
|
||||||
|
.map(|v| v.stack_size(pointer_size))
|
||||||
|
== Ok(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
// up to 256 enum keys can be stored in a byte
|
||||||
|
if tags.len() <= std::u8::MAX as usize + 1 && arguments_have_size_0() {
|
||||||
|
if tags.len() <= 2 {
|
||||||
|
// Up to 2 enum tags can be stored (in theory) in one bit
|
||||||
|
let mut it = tags.keys();
|
||||||
|
let a: TagName = it.next().unwrap().clone();
|
||||||
|
let b: TagName = it.next().unwrap().clone();
|
||||||
|
|
||||||
|
if a < b {
|
||||||
|
Ok(Layout::Builtin(Builtin::Bool(a, b)))
|
||||||
|
} else {
|
||||||
|
Ok(Layout::Builtin(Builtin::Bool(b, a)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// up to 256 enum tags can be stored in a byte
|
||||||
|
let mut tag_to_u8 = MutMap::default();
|
||||||
|
|
||||||
|
for (counter, (name, _)) in tags.into_iter().enumerate() {
|
||||||
|
tag_to_u8.insert(name, counter as u8);
|
||||||
|
}
|
||||||
|
Ok(Layout::Builtin(Builtin::Byte(tag_to_u8)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("TODO handle a tag union with mutliple tags: {:?}", tags);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,13 +370,15 @@ fn flatten_union(
|
||||||
|
|
||||||
match subs.get_without_compacting(ext_var).content {
|
match subs.get_without_compacting(ext_var).content {
|
||||||
Structure(EmptyTagUnion) => (),
|
Structure(EmptyTagUnion) => (),
|
||||||
Structure(TagUnion(new_tags, new_ext_var)) => {
|
Structure(TagUnion(new_tags, new_ext_var))
|
||||||
|
| Structure(RecursiveTagUnion(_, new_tags, new_ext_var)) => {
|
||||||
for (tag_name, vars) in new_tags {
|
for (tag_name, vars) in new_tags {
|
||||||
tags.insert(tag_name, vars);
|
tags.insert(tag_name, vars);
|
||||||
}
|
}
|
||||||
|
|
||||||
flatten_union(tags, new_ext_var, subs)
|
flatten_union(tags, new_ext_var, subs)
|
||||||
}
|
}
|
||||||
|
Alias(_, _, actual) => flatten_union(tags, actual, subs),
|
||||||
invalid => {
|
invalid => {
|
||||||
panic!("Compiler error: flatten_union got an ext_var in a tag union that wasn't itself a tag union; instead, it was: {:?}", invalid);
|
panic!("Compiler error: flatten_union got an ext_var in a tag union that wasn't itself a tag union; instead, it was: {:?}", invalid);
|
||||||
}
|
}
|
||||||
|
@ -329,6 +400,7 @@ fn flatten_record(fields: &mut MutMap<Lowercase, Variable>, ext_var: Variable, s
|
||||||
|
|
||||||
flatten_record(fields, new_ext_var, subs)
|
flatten_record(fields, new_ext_var, subs)
|
||||||
}
|
}
|
||||||
|
Alias(_, _, actual) => flatten_record(fields, actual, subs),
|
||||||
invalid => {
|
invalid => {
|
||||||
panic!("Compiler error: flatten_record encountered an ext_var in a record that wasn't itself a record; instead, it was: {:?}", invalid);
|
panic!("Compiler error: flatten_record encountered an ext_var in a record that wasn't itself a record; instead, it was: {:?}", invalid);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,11 @@ mod helpers;
|
||||||
// Test monomorphization
|
// Test monomorphization
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_mono {
|
mod test_mono {
|
||||||
use crate::helpers::{can_expr, infer_expr, CanExprOut};
|
use crate::helpers::{can_expr, infer_expr, test_home, CanExprOut};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::ident::TagName::*;
|
||||||
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
use roc_mono::expr::Expr::{self, *};
|
use roc_mono::expr::Expr::{self, *};
|
||||||
use roc_mono::layout::{Builtin, Layout};
|
use roc_mono::layout::{Builtin, Layout};
|
||||||
use roc_types::subs::Subs;
|
use roc_types::subs::Subs;
|
||||||
|
@ -22,6 +23,13 @@ mod test_mono {
|
||||||
// HELPERS
|
// HELPERS
|
||||||
|
|
||||||
fn compiles_to(src: &str, expected: Expr<'_>) {
|
fn compiles_to(src: &str, expected: Expr<'_>) {
|
||||||
|
compiles_to_with_interns(src, |_| expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compiles_to_with_interns<'a, F>(src: &str, get_expected: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(Interns) -> Expr<'a>,
|
||||||
|
{
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let CanExprOut {
|
let CanExprOut {
|
||||||
loc_expr,
|
loc_expr,
|
||||||
|
@ -40,6 +48,9 @@ mod test_mono {
|
||||||
let mut procs = MutMap::default();
|
let mut procs = MutMap::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
|
||||||
|
let pointer_size = std::mem::size_of::<u64>() as u32;
|
||||||
|
|
||||||
// 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,
|
||||||
|
@ -48,9 +59,13 @@ mod test_mono {
|
||||||
&mut procs,
|
&mut procs,
|
||||||
home,
|
home,
|
||||||
&mut ident_ids,
|
&mut ident_ids,
|
||||||
|
pointer_size,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(mono_expr, expected);
|
// Put this module's ident_ids back in the interns
|
||||||
|
interns.all_ident_ids.insert(home, ident_ids);
|
||||||
|
|
||||||
|
assert_eq!(mono_expr, get_expected(interns));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -63,10 +78,95 @@ mod test_mono {
|
||||||
compiles_to("0.5", Float(0.5));
|
compiles_to("0.5", Float(0.5));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bool_literal() {
|
||||||
|
let arena = Bump::new();
|
||||||
|
|
||||||
|
compiles_to_with_interns(
|
||||||
|
r#"
|
||||||
|
x : Bool
|
||||||
|
x = True
|
||||||
|
|
||||||
|
x
|
||||||
|
"#,
|
||||||
|
|interns| {
|
||||||
|
let home = test_home();
|
||||||
|
let var_x = interns.symbol(home, "x".into());
|
||||||
|
|
||||||
|
let stores = [(
|
||||||
|
var_x,
|
||||||
|
Layout::Builtin(Builtin::Bool(Global("False".into()), Global("True".into()))),
|
||||||
|
Bool(true),
|
||||||
|
)];
|
||||||
|
|
||||||
|
let load = Load(var_x);
|
||||||
|
|
||||||
|
Store(arena.alloc(stores), arena.alloc(load))
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_element_enum() {
|
||||||
|
let arena = Bump::new();
|
||||||
|
|
||||||
|
compiles_to_with_interns(
|
||||||
|
r#"
|
||||||
|
x : [ Yes, No ]
|
||||||
|
x = No
|
||||||
|
|
||||||
|
x
|
||||||
|
"#,
|
||||||
|
|interns| {
|
||||||
|
let home = test_home();
|
||||||
|
let var_x = interns.symbol(home, "x".into());
|
||||||
|
|
||||||
|
let stores = [(
|
||||||
|
var_x,
|
||||||
|
Layout::Builtin(Builtin::Bool(Global("No".into()), Global("Yes".into()))),
|
||||||
|
Bool(false),
|
||||||
|
)];
|
||||||
|
|
||||||
|
let load = Load(var_x);
|
||||||
|
|
||||||
|
Store(arena.alloc(stores), arena.alloc(load))
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn three_element_enum() {
|
||||||
|
let arena = Bump::new();
|
||||||
|
|
||||||
|
compiles_to_with_interns(
|
||||||
|
r#"
|
||||||
|
# this test is brought to you by fruits.com!
|
||||||
|
x : [ Apple, Orange, Banana ]
|
||||||
|
x = Orange
|
||||||
|
|
||||||
|
x
|
||||||
|
"#,
|
||||||
|
|interns| {
|
||||||
|
let home = test_home();
|
||||||
|
let var_x = interns.symbol(home, "x".into());
|
||||||
|
|
||||||
|
let mut fruits = MutMap::default();
|
||||||
|
|
||||||
|
fruits.insert(Global("Banana".into()), 0);
|
||||||
|
fruits.insert(Global("Orange".into()), 1);
|
||||||
|
fruits.insert(Global("Apple".into()), 2);
|
||||||
|
|
||||||
|
let stores = [(var_x, Layout::Builtin(Builtin::Byte(fruits)), Byte(1))];
|
||||||
|
|
||||||
|
let load = Load(var_x);
|
||||||
|
|
||||||
|
Store(arena.alloc(stores), arena.alloc(load))
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_unique_int_list() {
|
fn set_unique_int_list() {
|
||||||
compiles_to(
|
compiles_to("List.getUnsafe (List.set [ 12, 9, 7, 3 ] 1 42) 1", {
|
||||||
"List.getUnsafe (List.set [ 12, 9, 7, 3 ] 1 42) 1",
|
|
||||||
CallByName(
|
CallByName(
|
||||||
Symbol::LIST_GET_UNSAFE,
|
Symbol::LIST_GET_UNSAFE,
|
||||||
&vec![
|
&vec![
|
||||||
|
@ -91,7 +191,7 @@ mod test_mono {
|
||||||
),
|
),
|
||||||
(Int(1), Layout::Builtin(Builtin::Int64)),
|
(Int(1), Layout::Builtin(Builtin::Int64)),
|
||||||
],
|
],
|
||||||
),
|
)
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,9 @@ mod test_opt {
|
||||||
let mut procs = MutMap::default();
|
let mut procs = MutMap::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
|
||||||
|
let pointer_size = std::mem::size_of::<u64>() as u32;
|
||||||
|
|
||||||
// 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,
|
||||||
|
@ -39,6 +42,7 @@ mod test_opt {
|
||||||
&mut procs,
|
&mut procs,
|
||||||
home,
|
home,
|
||||||
&mut ident_ids,
|
&mut ident_ids,
|
||||||
|
pointer_size,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(mono_expr, expected);
|
assert_eq!(mono_expr, expected);
|
||||||
|
|
|
@ -8,7 +8,7 @@ use roc_can::expected::Expected;
|
||||||
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
||||||
use roc_can::operator;
|
use roc_can::operator;
|
||||||
use roc_can::scope::Scope;
|
use roc_can::scope::Scope;
|
||||||
use roc_collections::all::{ImMap, ImSet, MutMap, SendMap, SendSet};
|
use roc_collections::all::{ImMap, MutMap, SendMap, SendSet};
|
||||||
use roc_constrain::expr::constrain_expr;
|
use roc_constrain::expr::constrain_expr;
|
||||||
use roc_constrain::module::{constrain_imported_values, load_builtin_aliases, Import};
|
use roc_constrain::module::{constrain_imported_values, load_builtin_aliases, Import};
|
||||||
use roc_module::ident::Ident;
|
use roc_module::ident::Ident;
|
||||||
|
@ -350,108 +350,3 @@ pub fn fixtures_dir<'a>() -> PathBuf {
|
||||||
pub fn builtins_dir<'a>() -> PathBuf {
|
pub fn builtins_dir<'a>() -> PathBuf {
|
||||||
PathBuf::new().join("builtins")
|
PathBuf::new().join("builtins")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check constraints
|
|
||||||
//
|
|
||||||
// Keep track of the used (in types or expectations) variables, and the declared variables (in
|
|
||||||
// flex_vars or rigid_vars fields of LetConstraint. These roc_collections should match: no duplicates
|
|
||||||
// and no variables that are used but not declared are allowed.
|
|
||||||
//
|
|
||||||
// There is one exception: the initial variable (that stores the type of the whole expression) is
|
|
||||||
// never declared, but is used.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn assert_correct_variable_usage(constraint: &Constraint) {
|
|
||||||
// variables declared in constraint (flex_vars or rigid_vars)
|
|
||||||
// and variables actually used in constraints
|
|
||||||
let (declared, used) = variable_usage(constraint);
|
|
||||||
|
|
||||||
let used: ImSet<Variable> = used.clone().into();
|
|
||||||
let mut decl: ImSet<Variable> = declared.rigid_vars.clone().into();
|
|
||||||
|
|
||||||
for var in declared.flex_vars.clone() {
|
|
||||||
decl.insert(var);
|
|
||||||
}
|
|
||||||
|
|
||||||
let diff = used.clone().relative_complement(decl);
|
|
||||||
|
|
||||||
// NOTE: this checks whether we're using variables that are not declared. For recursive type
|
|
||||||
// definitions, their rigid types are declared twice, which is correct!
|
|
||||||
if !diff.is_empty() {
|
|
||||||
println!("VARIABLE USAGE PROBLEM");
|
|
||||||
|
|
||||||
println!("used: {:?}", &used);
|
|
||||||
println!("rigids: {:?}", &declared.rigid_vars);
|
|
||||||
println!("flexs: {:?}", &declared.flex_vars);
|
|
||||||
|
|
||||||
println!("difference: {:?}", &diff);
|
|
||||||
|
|
||||||
panic!("variable usage problem (see stdout for details)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct SeenVariables {
|
|
||||||
pub rigid_vars: Vec<Variable>,
|
|
||||||
pub flex_vars: Vec<Variable>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn variable_usage(con: &Constraint) -> (SeenVariables, Vec<Variable>) {
|
|
||||||
let mut declared = SeenVariables::default();
|
|
||||||
let mut used = ImSet::default();
|
|
||||||
variable_usage_help(con, &mut declared, &mut used);
|
|
||||||
|
|
||||||
used.remove(unsafe { &Variable::unsafe_test_debug_variable(1) });
|
|
||||||
used.remove(unsafe { &Variable::unsafe_test_debug_variable(2) });
|
|
||||||
used.remove(unsafe { &Variable::unsafe_test_debug_variable(3) });
|
|
||||||
|
|
||||||
let mut used_vec: Vec<Variable> = used.into_iter().collect();
|
|
||||||
used_vec.sort();
|
|
||||||
|
|
||||||
declared.rigid_vars.sort();
|
|
||||||
declared.flex_vars.sort();
|
|
||||||
|
|
||||||
(declared, used_vec)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn variable_usage_help(con: &Constraint, declared: &mut SeenVariables, used: &mut ImSet<Variable>) {
|
|
||||||
use Constraint::*;
|
|
||||||
|
|
||||||
match con {
|
|
||||||
True | SaveTheEnvironment => (),
|
|
||||||
Eq(tipe, expectation, _) => {
|
|
||||||
for v in tipe.variables() {
|
|
||||||
used.insert(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
for v in expectation.get_type_ref().variables() {
|
|
||||||
used.insert(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Lookup(_, expectation, _) => {
|
|
||||||
for v in expectation.get_type_ref().variables() {
|
|
||||||
used.insert(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Pattern(_, _, tipe, pexpectation) => {
|
|
||||||
for v in tipe.variables() {
|
|
||||||
used.insert(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
for v in pexpectation.get_type_ref().variables() {
|
|
||||||
used.insert(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Let(letcon) => {
|
|
||||||
declared.rigid_vars.extend(letcon.rigid_vars.clone());
|
|
||||||
declared.flex_vars.extend(letcon.flex_vars.clone());
|
|
||||||
|
|
||||||
variable_usage_help(&letcon.defs_constraint, declared, used);
|
|
||||||
variable_usage_help(&letcon.ret_constraint, declared, used);
|
|
||||||
}
|
|
||||||
And(constraints) => {
|
|
||||||
for sub in constraints {
|
|
||||||
variable_usage_help(sub, declared, used);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ mod test_report {
|
||||||
use roc_types::types;
|
use roc_types::types;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
// use roc_region::all;
|
// use roc_region::all;
|
||||||
use crate::helpers::{assert_correct_variable_usage, can_expr, infer_expr, CanExprOut};
|
use crate::helpers::{can_expr, infer_expr, CanExprOut};
|
||||||
use roc_reporting::report::ReportText::{EmText, Plain, Region, Type, Url, Value};
|
use roc_reporting::report::ReportText::{EmText, Plain, Region, Type, Url, Value};
|
||||||
use roc_types::subs::Content::{FlexVar, RigidVar, Structure};
|
use roc_types::subs::Content::{FlexVar, RigidVar, Structure};
|
||||||
use roc_types::subs::FlatType::EmptyRecord;
|
use roc_types::subs::FlatType::EmptyRecord;
|
||||||
|
@ -53,8 +53,6 @@ mod test_report {
|
||||||
} = can_expr(expr_src);
|
} = can_expr(expr_src);
|
||||||
let mut subs = Subs::new(var_store.into());
|
let mut subs = Subs::new(var_store.into());
|
||||||
|
|
||||||
assert_correct_variable_usage(&constraint);
|
|
||||||
|
|
||||||
for (var, name) in output.introduced_variables.name_by_var {
|
for (var, name) in output.introduced_variables.name_by_var {
|
||||||
subs.rigid_var(var, name);
|
subs.rigid_var(var, name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -546,6 +546,7 @@ fn type_to_variable(
|
||||||
|
|
||||||
register(subs, rank, pools, content)
|
register(subs, rank, pools, content)
|
||||||
}
|
}
|
||||||
|
Alias(Symbol::BOOL_BOOL, _, _) => Variable::BOOL,
|
||||||
Alias(symbol, args, alias_type) => {
|
Alias(symbol, args, alias_type) => {
|
||||||
// Cache aliases without type arguments. Commonly used aliases like `Int` would otherwise get O(n)
|
// Cache aliases without type arguments. Commonly used aliases like `Int` would otherwise get O(n)
|
||||||
// different variables (once for each occurence). The recursion restriction is required
|
// different variables (once for each occurence). The recursion restriction is required
|
||||||
|
@ -560,6 +561,7 @@ fn type_to_variable(
|
||||||
//
|
//
|
||||||
// This `u` variable can be different between lists, so giving just one variable to
|
// This `u` variable can be different between lists, so giving just one variable to
|
||||||
// this type is incorrect.
|
// this type is incorrect.
|
||||||
|
// TODO does caching work at all with uniqueness types? even Int then hides a uniqueness variable
|
||||||
let is_recursive = alias_type.is_recursive();
|
let is_recursive = alias_type.is_recursive();
|
||||||
let no_args = args.is_empty();
|
let no_args = args.is_empty();
|
||||||
if no_args && !is_recursive {
|
if no_args && !is_recursive {
|
||||||
|
|
|
@ -400,9 +400,10 @@ pub fn variable_usage(con: &Constraint) -> (SeenVariables, Vec<Variable>) {
|
||||||
let mut used = ImSet::default();
|
let mut used = ImSet::default();
|
||||||
variable_usage_help(con, &mut declared, &mut used);
|
variable_usage_help(con, &mut declared, &mut used);
|
||||||
|
|
||||||
used.remove(unsafe { &Variable::unsafe_test_debug_variable(1) });
|
// ..= because there is an extra undeclared variable that contains the type of the full expression
|
||||||
used.remove(unsafe { &Variable::unsafe_test_debug_variable(2) });
|
for i in 0..=Variable::RESERVED {
|
||||||
used.remove(unsafe { &Variable::unsafe_test_debug_variable(3) });
|
used.remove(unsafe { &Variable::unsafe_test_debug_variable(i as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
let mut used_vec: Vec<Variable> = used.into_iter().collect();
|
let mut used_vec: Vec<Variable> = used.into_iter().collect();
|
||||||
used_vec.sort();
|
used_vec.sort();
|
||||||
|
|
|
@ -2113,8 +2113,8 @@ mod test_uniq_solve {
|
||||||
f
|
f
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (Attr (* | a | b) { p : (Attr b *), q : (Attr a *) }* -> Attr * (Num (Attr * *)))"
|
"Attr * (Attr (* | a | b) { p : (Attr a *), q : (Attr b *) }* -> Attr * (Num (Attr * *)))",
|
||||||
//"Attr * (Attr (* | a | b) { p : (Attr a *), q : (Attr b *) }* -> Attr * (Num (Attr * *)))",
|
//"Attr * (Attr (* | a | b) { p : (Attr b *), q : (Attr a *) }* -> Attr * (Num (Attr * *)))"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,9 +151,11 @@ impl Variable {
|
||||||
|
|
||||||
pub const EMPTY_RECORD: Variable = Variable(1);
|
pub const EMPTY_RECORD: Variable = Variable(1);
|
||||||
pub const EMPTY_TAG_UNION: Variable = Variable(2);
|
pub const EMPTY_TAG_UNION: Variable = Variable(2);
|
||||||
|
const BOOL_ENUM: Variable = Variable(3);
|
||||||
|
pub const BOOL: Variable = Variable(4);
|
||||||
|
pub const RESERVED: usize = 5;
|
||||||
|
|
||||||
// variables 1 and 2 are reserved for EmptyRecord and EmptyTagUnion
|
const FIRST_USER_SPACE_VAR: Variable = Variable(Self::RESERVED as u32);
|
||||||
const FIRST_USER_SPACE_VAR: Variable = Variable(3);
|
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
|
@ -228,8 +230,26 @@ impl Subs {
|
||||||
subs.utable.new_key(flex_var_descriptor());
|
subs.utable.new_key(flex_var_descriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
subs.set_content(Variable(1), Content::Structure(FlatType::EmptyRecord));
|
subs.set_content(
|
||||||
subs.set_content(Variable(2), Content::Structure(FlatType::EmptyTagUnion));
|
Variable::EMPTY_RECORD,
|
||||||
|
Content::Structure(FlatType::EmptyRecord),
|
||||||
|
);
|
||||||
|
subs.set_content(
|
||||||
|
Variable::EMPTY_TAG_UNION,
|
||||||
|
Content::Structure(FlatType::EmptyTagUnion),
|
||||||
|
);
|
||||||
|
|
||||||
|
subs.set_content(Variable::BOOL_ENUM, {
|
||||||
|
let mut tags = MutMap::default();
|
||||||
|
tags.insert(TagName::Global("False".into()), vec![]);
|
||||||
|
tags.insert(TagName::Global("True".into()), vec![]);
|
||||||
|
|
||||||
|
Content::Structure(FlatType::TagUnion(tags, Variable::EMPTY_TAG_UNION))
|
||||||
|
});
|
||||||
|
|
||||||
|
subs.set_content(Variable::BOOL, {
|
||||||
|
Content::Alias(Symbol::BOOL_BOOL, vec![], Variable::BOOL_ENUM)
|
||||||
|
});
|
||||||
|
|
||||||
subs
|
subs
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue