mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +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)]
|
||||
pub enum Expr {
|
||||
// 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),
|
||||
|
||||
// Int and Float store a variable to generate better error messages
|
||||
Int(Variable, i64),
|
||||
Float(Variable, f64),
|
||||
Str(Box<str>),
|
||||
|
|
|
@ -33,6 +33,7 @@ pub fn int_expr_from_result(
|
|||
result: Result<i64, &str>,
|
||||
env: &mut Env,
|
||||
) -> Expr {
|
||||
// Int stores a variable to generate better error messages
|
||||
match result {
|
||||
Ok(int) => Expr::Int(var_store.fresh(), int),
|
||||
Err(raw) => {
|
||||
|
@ -51,6 +52,7 @@ pub fn float_expr_from_result(
|
|||
result: Result<f64, &str>,
|
||||
env: &mut Env,
|
||||
) -> Expr {
|
||||
// Float stores a variable to generate better error messages
|
||||
match result {
|
||||
Ok(float) => Expr::Float(var_store.fresh(), float),
|
||||
Err(raw) => {
|
||||
|
|
|
@ -10,7 +10,7 @@ use roc_can::expr::Expr::{self, *};
|
|||
use roc_can::expr::Field;
|
||||
use roc_can::pattern::Pattern;
|
||||
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_region::all::{Located, Region};
|
||||
use roc_types::subs::Variable;
|
||||
|
@ -326,14 +326,7 @@ pub fn constrain_expr(
|
|||
branches,
|
||||
final_else,
|
||||
} => {
|
||||
// TODO use Bool alias here, so we don't allocate this type every time
|
||||
let bool_type = Type::TagUnion(
|
||||
vec![
|
||||
(TagName::Global("True".into()), vec![]),
|
||||
(TagName::Global("False".into()), vec![]),
|
||||
],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
);
|
||||
let bool_type = Type::Variable(Variable::BOOL);
|
||||
let expect_bool = Expected::ForReason(Reason::IfCondition, bool_type, region);
|
||||
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::pattern::{Pattern, RecordDestruct};
|
||||
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_region::all::{Located, Region};
|
||||
use roc_types::boolean_algebra::{Atom, Bool};
|
||||
|
@ -802,14 +802,7 @@ pub fn constrain_expr(
|
|||
final_else,
|
||||
} => {
|
||||
// TODO use Bool alias here, so we don't allocate this type every time
|
||||
let bool_type = Type::TagUnion(
|
||||
vec![
|
||||
(TagName::Global("True".into()), vec![]),
|
||||
(TagName::Global("False".into()), vec![]),
|
||||
],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
);
|
||||
|
||||
let bool_type = Type::Variable(Variable::BOOL);
|
||||
let mut branch_cons = Vec::with_capacity(2 * 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 {
|
||||
Int64 => types::I64,
|
||||
Float64 => types::F64,
|
||||
Bool(_, _) => types::B1,
|
||||
Byte(_) => types::I8,
|
||||
Str | Map(_, _) | Set(_) | List(_) => cfg.pointer_type(),
|
||||
},
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@ pub fn basic_type_from_layout<'ctx>(
|
|||
Builtin(builtin) => match builtin {
|
||||
Int64 => context.i64_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
|
||||
.i8_type()
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
|
|
|
@ -37,6 +37,9 @@ mod test_gen {
|
|||
use std::mem;
|
||||
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 {
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
let arena = Bump::new();
|
||||
|
@ -57,7 +60,7 @@ mod test_gen {
|
|||
let main_fn_name = "$Test.main";
|
||||
|
||||
// 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));
|
||||
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();
|
||||
|
||||
// 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
|
||||
env.interns.all_ident_ids.insert(home, ident_ids);
|
||||
|
@ -195,7 +198,7 @@ mod test_gen {
|
|||
fpm.initialize();
|
||||
|
||||
// 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));
|
||||
let main_fn_type = basic_type_from_layout(&context, &layout)
|
||||
.fn_type(&[], false);
|
||||
|
@ -221,7 +224,7 @@ mod test_gen {
|
|||
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
||||
|
||||
// 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.
|
||||
env.interns.all_ident_ids.insert(home, ident_ids);
|
||||
|
@ -330,7 +333,7 @@ mod test_gen {
|
|||
fpm.initialize();
|
||||
|
||||
// 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));
|
||||
let main_fn_type = basic_type_from_layout(&context, &layout)
|
||||
.fn_type(&[], false);
|
||||
|
@ -356,7 +359,7 @@ mod test_gen {
|
|||
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
|
||||
|
||||
// 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.
|
||||
env.interns.all_ident_ids.insert(home, ident_ids);
|
||||
|
|
|
@ -24,6 +24,7 @@ struct Env<'a, 'i> {
|
|||
pub subs: &'a Subs,
|
||||
pub home: ModuleId,
|
||||
pub ident_ids: &'i mut IdentIds,
|
||||
pub pointer_size: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -89,7 +90,6 @@ pub enum Expr<'a> {
|
|||
},
|
||||
Tag {
|
||||
tag_layout: Layout<'a>,
|
||||
ext_layout: Layout<'a>,
|
||||
name: TagName,
|
||||
arguments: &'a [Expr<'a>],
|
||||
},
|
||||
|
@ -119,12 +119,14 @@ impl<'a> Expr<'a> {
|
|||
procs: &mut Procs<'a>,
|
||||
home: ModuleId,
|
||||
ident_ids: &mut IdentIds,
|
||||
pointer_size: u32,
|
||||
) -> Self {
|
||||
let mut env = Env {
|
||||
arena,
|
||||
subs,
|
||||
home,
|
||||
ident_ids,
|
||||
pointer_size,
|
||||
};
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
let layout =
|
||||
Layout::from_var(env.arena, fn_var, env.subs).unwrap_or_else(|err| {
|
||||
let layout = Layout::from_var(env.arena, fn_var, env.subs, env.pointer_size)
|
||||
.unwrap_or_else(|err| {
|
||||
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
|
||||
});
|
||||
Expr::CallByPointer(&*env.arena.alloc(ptr), args.into_bump_slice(), layout)
|
||||
|
@ -344,7 +346,7 @@ fn from_can<'a>(
|
|||
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,
|
||||
Err(()) => {
|
||||
// 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 {
|
||||
ext_var,
|
||||
field_var,
|
||||
|
@ -367,7 +403,7 @@ fn from_can<'a>(
|
|||
let subs = env.subs;
|
||||
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,
|
||||
Err(()) => {
|
||||
// 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,
|
||||
Err(()) => {
|
||||
// Invalid field!
|
||||
|
@ -396,7 +432,7 @@ fn from_can<'a>(
|
|||
} => {
|
||||
let subs = env.subs;
|
||||
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,
|
||||
Err(()) => {
|
||||
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);
|
||||
|
||||
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,
|
||||
Err(()) => {
|
||||
// Invalid closure!
|
||||
|
@ -451,7 +487,7 @@ fn add_closure<'a>(
|
|||
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));
|
||||
|
||||
let proc = Proc {
|
||||
|
@ -476,7 +512,7 @@ fn store_pattern<'a>(
|
|||
) {
|
||||
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,
|
||||
Err(()) => {
|
||||
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 pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
||||
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
||||
let ret_layout =
|
||||
Layout::from_var(arena, expr_var, env.subs).unwrap_or_else(|err| {
|
||||
let ret_layout = Layout::from_var(arena, expr_var, env.subs, env.pointer_size)
|
||||
.unwrap_or_else(|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 pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
||||
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
||||
let ret_layout =
|
||||
Layout::from_var(arena, expr_var, env.subs).unwrap_or_else(|err| {
|
||||
let ret_layout = Layout::from_var(arena, expr_var, env.subs, env.pointer_size)
|
||||
.unwrap_or_else(|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 pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
||||
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
||||
let ret_layout =
|
||||
Layout::from_var(arena, expr_var, env.subs).unwrap_or_else(|err| {
|
||||
let ret_layout = Layout::from_var(arena, expr_var, env.subs, env.pointer_size)
|
||||
.unwrap_or_else(|err| {
|
||||
panic!("TODO turn this into a RuntimeError {:?}", err)
|
||||
});
|
||||
|
||||
|
@ -637,7 +673,7 @@ fn from_can_when<'a>(
|
|||
let arena = env.arena;
|
||||
let cond = from_can(env, loc_cond.value, procs, None);
|
||||
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!"));
|
||||
|
||||
// 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());
|
||||
let default_branch = opt_default_branch.unwrap();
|
||||
|
||||
let cond_layout =
|
||||
Layout::from_var(arena, cond_var, env.subs).unwrap_or_else(|err| {
|
||||
let cond_layout = Layout::from_var(arena, cond_var, env.subs, env.pointer_size)
|
||||
.unwrap_or_else(|err| {
|
||||
panic!("TODO turn cond_layout into a RuntimeError {:?}", err)
|
||||
});
|
||||
let ret_layout =
|
||||
Layout::from_var(arena, expr_var, env.subs).unwrap_or_else(|err| {
|
||||
let ret_layout = Layout::from_var(arena, expr_var, env.subs, env.pointer_size)
|
||||
.unwrap_or_else(|err| {
|
||||
panic!("TODO turn ret_layout into a RuntimeError {:?}", err)
|
||||
});
|
||||
|
||||
|
@ -779,7 +815,7 @@ fn call_by_name<'a>(
|
|||
let arena = env.arena;
|
||||
|
||||
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));
|
||||
|
||||
args.push((from_can(env, loc_arg.value, procs, None), layout));
|
||||
|
|
|
@ -19,6 +19,8 @@ pub enum Layout<'a> {
|
|||
pub enum Builtin<'a> {
|
||||
Int64,
|
||||
Float64,
|
||||
Bool(TagName, TagName),
|
||||
Byte(MutMap<TagName, u8>),
|
||||
Str,
|
||||
Map(&'a Layout<'a>, &'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.
|
||||
/// Panics if given a FlexVar or RigidVar, since those should have been
|
||||
/// 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;
|
||||
|
||||
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::*;
|
||||
|
||||
match content {
|
||||
var @ FlexVar(_) | var @ RigidVar(_) => {
|
||||
panic!("Layout::from_content encountered an unresolved {:?}", var);
|
||||
}
|
||||
Structure(flat_type) => layout_from_flat_type(arena, flat_type, subs),
|
||||
Structure(flat_type) => layout_from_flat_type(arena, flat_type, subs, pointer_size),
|
||||
|
||||
Alias(Symbol::INT_INT, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
|
@ -52,9 +64,12 @@ impl<'a> Layout<'a> {
|
|||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Float64))
|
||||
}
|
||||
Alias(_, _, var) => {
|
||||
Self::from_content(arena, subs.get_without_compacting(var).content, subs)
|
||||
}
|
||||
Alias(_, _, var) => Self::from_content(
|
||||
arena,
|
||||
subs.get_without_compacting(var).content,
|
||||
subs,
|
||||
pointer_size,
|
||||
),
|
||||
Error => Err(()),
|
||||
}
|
||||
}
|
||||
|
@ -81,6 +96,8 @@ impl<'a> Layout<'a> {
|
|||
impl<'a> Builtin<'a> {
|
||||
const I64_SIZE: u32 = std::mem::size_of::<i64>() 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
|
||||
const STR_WORDS: u32 = 3;
|
||||
|
@ -94,6 +111,8 @@ impl<'a> Builtin<'a> {
|
|||
match self {
|
||||
Int64 => Builtin::I64_SIZE,
|
||||
Float64 => Builtin::F64_SIZE,
|
||||
Bool(_, _) => Builtin::BOOL_SIZE,
|
||||
Byte(_) => Builtin::BYTE_SIZE,
|
||||
Str => Builtin::STR_WORDS * pointer_size,
|
||||
Map(_, _) => Builtin::MAP_WORDS * pointer_size,
|
||||
Set(_) => Builtin::SET_WORDS * pointer_size,
|
||||
|
@ -106,6 +125,7 @@ fn layout_from_flat_type<'a>(
|
|||
arena: &'a Bump,
|
||||
flat_type: FlatType,
|
||||
subs: &Subs,
|
||||
pointer_size: u32,
|
||||
) -> Result<Layout<'a>, ()> {
|
||||
use roc_types::subs::FlatType::*;
|
||||
|
||||
|
@ -131,7 +151,7 @@ fn layout_from_flat_type<'a>(
|
|||
}
|
||||
Symbol::STR_STR => Ok(Layout::Builtin(Builtin::Str)),
|
||||
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))))
|
||||
}
|
||||
|
@ -145,7 +165,7 @@ fn layout_from_flat_type<'a>(
|
|||
// For now, layout is unaffected by uniqueness.
|
||||
// (Incorporating refcounting may change this.)
|
||||
// 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));
|
||||
|
@ -158,11 +178,16 @@ fn layout_from_flat_type<'a>(
|
|||
for arg_var in args {
|
||||
let arg_content = subs.get_without_compacting(arg_var).content;
|
||||
|
||||
fn_args.push(Layout::from_content(arena, arg_content, subs)?);
|
||||
fn_args.push(Layout::from_content(
|
||||
arena,
|
||||
arg_content,
|
||||
subs,
|
||||
pointer_size,
|
||||
)?);
|
||||
}
|
||||
|
||||
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(
|
||||
fn_args.into_bump_slice(),
|
||||
|
@ -172,7 +197,7 @@ fn layout_from_flat_type<'a>(
|
|||
Record(mut fields, ext_var) => {
|
||||
flatten_record(&mut fields, ext_var, subs);
|
||||
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,
|
||||
Err(()) => {
|
||||
// Invalid record!
|
||||
|
@ -200,7 +225,8 @@ fn layout_from_flat_type<'a>(
|
|||
|
||||
for (label, field_var) in fields {
|
||||
let field_content = subs.get_without_compacting(field_var).content;
|
||||
let field_layout = match Layout::from_content(arena, field_content, subs) {
|
||||
let field_layout =
|
||||
match Layout::from_content(arena, field_content, subs, pointer_size) {
|
||||
Ok(layout) => layout,
|
||||
Err(()) => {
|
||||
// Invalid field!
|
||||
|
@ -243,10 +269,53 @@ fn layout_from_flat_type<'a>(
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RecursiveTagUnion(_, _, _) => {
|
||||
panic!("TODO make Layout for non-empty Tag Union");
|
||||
}
|
||||
|
@ -301,13 +370,15 @@ fn flatten_union(
|
|||
|
||||
match subs.get_without_compacting(ext_var).content {
|
||||
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 {
|
||||
tags.insert(tag_name, vars);
|
||||
}
|
||||
|
||||
flatten_union(tags, new_ext_var, subs)
|
||||
}
|
||||
Alias(_, _, actual) => flatten_union(tags, actual, subs),
|
||||
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)
|
||||
}
|
||||
Alias(_, _, actual) => flatten_record(fields, actual, subs),
|
||||
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
|
||||
#[cfg(test)]
|
||||
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 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::layout::{Builtin, Layout};
|
||||
use roc_types::subs::Subs;
|
||||
|
@ -22,6 +23,13 @@ mod test_mono {
|
|||
// HELPERS
|
||||
|
||||
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 CanExprOut {
|
||||
loc_expr,
|
||||
|
@ -40,6 +48,9 @@ mod test_mono {
|
|||
let mut procs = MutMap::default();
|
||||
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
|
||||
let mono_expr = Expr::new(
|
||||
&arena,
|
||||
|
@ -48,9 +59,13 @@ mod test_mono {
|
|||
&mut procs,
|
||||
home,
|
||||
&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]
|
||||
|
@ -63,10 +78,95 @@ mod test_mono {
|
|||
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]
|
||||
fn set_unique_int_list() {
|
||||
compiles_to(
|
||||
"List.getUnsafe (List.set [ 12, 9, 7, 3 ] 1 42) 1",
|
||||
compiles_to("List.getUnsafe (List.set [ 12, 9, 7, 3 ] 1 42) 1", {
|
||||
CallByName(
|
||||
Symbol::LIST_GET_UNSAFE,
|
||||
&vec![
|
||||
|
@ -91,7 +191,7 @@ mod test_mono {
|
|||
),
|
||||
(Int(1), Layout::Builtin(Builtin::Int64)),
|
||||
],
|
||||
),
|
||||
);
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,9 @@ mod test_opt {
|
|||
let mut procs = MutMap::default();
|
||||
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
|
||||
let mono_expr = Expr::new(
|
||||
&arena,
|
||||
|
@ -39,6 +42,7 @@ mod test_opt {
|
|||
&mut procs,
|
||||
home,
|
||||
&mut ident_ids,
|
||||
pointer_size,
|
||||
);
|
||||
|
||||
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::operator;
|
||||
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::module::{constrain_imported_values, load_builtin_aliases, Import};
|
||||
use roc_module::ident::Ident;
|
||||
|
@ -350,108 +350,3 @@ pub fn fixtures_dir<'a>() -> PathBuf {
|
|||
pub fn builtins_dir<'a>() -> PathBuf {
|
||||
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 std::path::PathBuf;
|
||||
// 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_types::subs::Content::{FlexVar, RigidVar, Structure};
|
||||
use roc_types::subs::FlatType::EmptyRecord;
|
||||
|
@ -53,8 +53,6 @@ mod test_report {
|
|||
} = can_expr(expr_src);
|
||||
let mut subs = Subs::new(var_store.into());
|
||||
|
||||
assert_correct_variable_usage(&constraint);
|
||||
|
||||
for (var, name) in output.introduced_variables.name_by_var {
|
||||
subs.rigid_var(var, name);
|
||||
}
|
||||
|
|
|
@ -546,6 +546,7 @@ fn type_to_variable(
|
|||
|
||||
register(subs, rank, pools, content)
|
||||
}
|
||||
Alias(Symbol::BOOL_BOOL, _, _) => Variable::BOOL,
|
||||
Alias(symbol, args, alias_type) => {
|
||||
// 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
|
||||
|
@ -560,6 +561,7 @@ fn type_to_variable(
|
|||
//
|
||||
// This `u` variable can be different between lists, so giving just one variable to
|
||||
// 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 no_args = args.is_empty();
|
||||
if no_args && !is_recursive {
|
||||
|
|
|
@ -400,9 +400,10 @@ pub fn variable_usage(con: &Constraint) -> (SeenVariables, Vec<Variable>) {
|
|||
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) });
|
||||
// ..= because there is an extra undeclared variable that contains the type of the full expression
|
||||
for i in 0..=Variable::RESERVED {
|
||||
used.remove(unsafe { &Variable::unsafe_test_debug_variable(i as u32) });
|
||||
}
|
||||
|
||||
let mut used_vec: Vec<Variable> = used.into_iter().collect();
|
||||
used_vec.sort();
|
||||
|
|
|
@ -2113,8 +2113,8 @@ mod test_uniq_solve {
|
|||
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_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(3);
|
||||
const FIRST_USER_SPACE_VAR: Variable = Variable(Self::RESERVED as u32);
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
|
@ -228,8 +230,26 @@ impl Subs {
|
|||
subs.utable.new_key(flex_var_descriptor());
|
||||
}
|
||||
|
||||
subs.set_content(Variable(1), Content::Structure(FlatType::EmptyRecord));
|
||||
subs.set_content(Variable(2), Content::Structure(FlatType::EmptyTagUnion));
|
||||
subs.set_content(
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue