Merge branch 'trunk' into dev-backend-num-is-zero

This commit is contained in:
satotake 2021-11-22 15:25:25 +00:00 committed by GitHub
commit 0085272cb8
40 changed files with 1382 additions and 892 deletions

1
Cargo.lock generated
View file

@ -3458,6 +3458,7 @@ dependencies = [
"bumpalo", "bumpalo",
"hashbrown 0.11.2", "hashbrown 0.11.2",
"morphic_lib", "morphic_lib",
"roc_builtins",
"roc_can", "roc_can",
"roc_collections", "roc_collections",
"roc_module", "roc_module",

View file

@ -2492,4 +2492,121 @@ pub mod test_constrain {
"custom", "custom",
); );
} }
#[test]
fn inference_var_inside_arrow() {
infer_eq(
indoc!(
r#"
id : _ -> _
id = \x -> x
id
"#
),
"a -> a",
)
}
#[test]
#[ignore = "TODO: Type2::substitute"]
fn inference_var_inside_ctor() {
infer_eq(
indoc!(
r#"
canIGo : _ -> Result _ _
canIGo = \color ->
when color is
"green" -> Ok "go!"
"yellow" -> Err (SlowIt "whoa, let's slow down!")
"red" -> Err (StopIt "absolutely not")
_ -> Err (UnknownColor "this is a weird stoplight")
canIGo
"#
),
"Str -> Result Str [ SlowIt Str, StopIt Str, UnknownColor Str ]*",
)
}
#[test]
#[ignore = "TODO: Gives { x : *, y : * } -> { x : *, y : * }. This is a bug in typechecking defs with annotations."]
fn inference_var_inside_ctor_linked() {
infer_eq(
indoc!(
r#"
swapRcd: {x: _, y: _} -> {x: _, y: _}
swapRcd = \{x, y} -> {x: y, y: x}
swapRcd
"#
),
"{ x : a, y : b } -> { x : b, y : a }",
)
}
#[test]
fn inference_var_link_with_rigid() {
infer_eq(
indoc!(
r#"
swapRcd: {x: tx, y: ty} -> {x: _, y: _}
swapRcd = \{x, y} -> {x: y, y: x}
swapRcd
"#
),
"{ x : tx, y : ty } -> { x : ty, y : tx }",
)
}
#[test]
#[ignore = "TODO: Type2::substitute"]
fn inference_var_inside_tag_ctor() {
infer_eq(
indoc!(
r#"
badComics: Bool -> [ CowTools _, Thagomizer _ ]
badComics = \c ->
when c is
True -> CowTools "The Far Side"
False -> Thagomizer "The Far Side"
badComics
"#
),
"Bool -> [ CowTools Str, Thagomizer Str ]",
)
}
#[test]
fn inference_var_tag_union_ext() {
// TODO: we should really be inferring [ Blue, Orange ]a -> [ Lavender, Peach ]a here.
// See https://github.com/rtfeldman/roc/issues/2053
infer_eq(
indoc!(
r#"
pastelize: _ -> [ Lavender, Peach ]_
pastelize = \color ->
when color is
Blue -> Lavender
Orange -> Peach
col -> col
pastelize
"#
),
"[ Blue, Lavender, Orange, Peach ]a -> [ Blue, Lavender, Orange, Peach ]a",
)
}
#[test]
#[ignore = "TODO: gives { email : a, name : b }c -> { email : a, name : b }c. This is a bug in typechecking defs with annotations."]
fn inference_var_rcd_union_ext() {
infer_eq(
indoc!(
r#"
setRocEmail : _ -> { name: Str, email: Str }_
setRocEmail = \person ->
{ person & email: "\(person.name)@roclang.com" }
setRocEmail
"#
),
"{ email : Str, name : Str }a -> { email : Str, name : Str }a",
)
}
} }

View file

@ -390,7 +390,9 @@ pub fn to_type2<'a>(
} }
} }
Inferred => { Inferred => {
unimplemented!(); let var = env.var_store.fresh();
Type2::Variable(var)
} }
Wildcard | Malformed(_) => { Wildcard | Malformed(_) => {
let var = env.var_store.fresh(); let var = env.var_store.fresh();

View file

@ -1,6 +1,7 @@
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use libloading::Library; use libloading::Library;
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_gen_llvm::{run_jit_function, run_jit_function_dynamic_type}; use roc_gen_llvm::{run_jit_function, run_jit_function_dynamic_type};
use roc_module::called_via::CalledVia; use roc_module::called_via::CalledVia;
use roc_module::ident::TagName; use roc_module::ident::TagName;
@ -71,60 +72,59 @@ fn jit_to_ast_help<'a>(
content: &Content, content: &Content,
) -> Result<Expr<'a>, ToAstProblem> { ) -> Result<Expr<'a>, ToAstProblem> {
match layout { match layout {
Layout::Builtin(Builtin::Int1) => Ok(run_jit_function!(lib, main_fn_name, bool, |num| { Layout::Builtin(Builtin::Bool) => Ok(run_jit_function!(lib, main_fn_name, bool, |num| {
bool_to_ast(env, num, content) bool_to_ast(env, num, content)
})), })),
Layout::Builtin(Builtin::Int8) => { Layout::Builtin(Builtin::Int(int_width)) => {
Ok( use IntWidth::*;
// NOTE: this is does not handle 8-bit numbers yet
run_jit_function!(lib, main_fn_name, u8, |num| byte_to_ast(env, num, content)), macro_rules! helper {
) ($ty:ty) => {
} run_jit_function!(lib, main_fn_name, $ty, |num| num_to_ast(
Layout::Builtin(Builtin::Usize) => Ok(run_jit_function!(lib, main_fn_name, usize, |num| {
num_to_ast(env, number_literal_to_ast(env.arena, num), content)
})),
Layout::Builtin(Builtin::Int16) => {
Ok(run_jit_function!(lib, main_fn_name, i16, |num| num_to_ast(
env, env,
number_literal_to_ast(env.arena, num), number_literal_to_ast(env.arena, num),
content content
)))
}
Layout::Builtin(Builtin::Int32) => {
Ok(run_jit_function!(lib, main_fn_name, i32, |num| num_to_ast(
env,
number_literal_to_ast(env.arena, num),
content
)))
}
Layout::Builtin(Builtin::Int64) => {
Ok(run_jit_function!(lib, main_fn_name, i64, |num| num_to_ast(
env,
number_literal_to_ast(env.arena, num),
content
)))
}
Layout::Builtin(Builtin::Int128) => {
Ok(run_jit_function!(
lib,
main_fn_name,
i128,
|num| num_to_ast(env, number_literal_to_ast(env.arena, num), content)
)) ))
};
} }
Layout::Builtin(Builtin::Float32) => {
Ok(run_jit_function!(lib, main_fn_name, f32, |num| num_to_ast( let result = match int_width {
U8 | I8 => {
// NOTE: this is does not handle 8-bit numbers yet
run_jit_function!(lib, main_fn_name, u8, |num| byte_to_ast(env, num, content))
}
U16 => helper!(u16),
U32 => helper!(u32),
U64 => helper!(u64),
U128 => helper!(u128),
I16 => helper!(i16),
I32 => helper!(i32),
I64 => helper!(i64),
I128 => helper!(i128),
};
Ok(result)
}
Layout::Builtin(Builtin::Float(float_width)) => {
use FloatWidth::*;
macro_rules! helper {
($ty:ty) => {
run_jit_function!(lib, main_fn_name, $ty, |num| num_to_ast(
env, env,
number_literal_to_ast(env.arena, num), number_literal_to_ast(env.arena, num),
content content
))) ))
};
} }
Layout::Builtin(Builtin::Float64) => {
Ok(run_jit_function!(lib, main_fn_name, f64, |num| num_to_ast( let result = match float_width {
env, F32 => helper!(f32),
number_literal_to_ast(env.arena, num), F64 => helper!(f64),
content F128 => todo!("F128 not implemented"),
))) };
Ok(result)
} }
Layout::Builtin(Builtin::Str) | Layout::Builtin(Builtin::EmptyStr) => Ok( Layout::Builtin(Builtin::Str) | Layout::Builtin(Builtin::EmptyStr) => Ok(
run_jit_function!(lib, main_fn_name, &'static str, |string: &'static str| { run_jit_function!(lib, main_fn_name, &'static str, |string: &'static str| {
@ -192,7 +192,7 @@ fn jit_to_ast_help<'a>(
} }
}; };
let fields = [Layout::Builtin(Builtin::Int64), *layout]; let fields = [Layout::u64(), *layout];
let layout = Layout::Struct(&fields); let layout = Layout::Struct(&fields);
let result_stack_size = layout.stack_size(env.ptr_bytes); let result_stack_size = layout.stack_size(env.ptr_bytes);
@ -248,16 +248,16 @@ fn jit_to_ast_help<'a>(
.unwrap_or(0); .unwrap_or(0);
let tag_id = match union_layout.tag_id_builtin() { let tag_id = match union_layout.tag_id_builtin() {
Builtin::Int1 => { Builtin::Bool => {
*(ptr.add(offset as usize) as *const i8) as i64 *(ptr.add(offset as usize) as *const i8) as i64
} }
Builtin::Int8 => { Builtin::Int(IntWidth::U8) => {
*(ptr.add(offset as usize) as *const i8) as i64 *(ptr.add(offset as usize) as *const i8) as i64
} }
Builtin::Int16 => { Builtin::Int(IntWidth::U16) => {
*(ptr.add(offset as usize) as *const i16) as i64 *(ptr.add(offset as usize) as *const i16) as i64
} }
Builtin::Int64 => { Builtin::Int(IntWidth::U64) => {
// used by non-recursive unions at the // used by non-recursive unions at the
// moment, remove if that is no longer the case // moment, remove if that is no longer the case
*(ptr.add(offset as usize) as *const i64) as i64 *(ptr.add(offset as usize) as *const i64) as i64
@ -380,53 +380,46 @@ fn ptr_to_ast<'a>(
layout: &Layout<'a>, layout: &Layout<'a>,
content: &Content, content: &Content,
) -> Expr<'a> { ) -> Expr<'a> {
macro_rules! helper {
($ty:ty) => {{
let num = unsafe { *(ptr as *const $ty) };
num_to_ast(env, number_literal_to_ast(env.arena, num), content)
}};
}
match layout { match layout {
Layout::Builtin(Builtin::Int128) => { Layout::Builtin(Builtin::Bool) => {
let num = unsafe { *(ptr as *const i128) };
num_to_ast(env, number_literal_to_ast(env.arena, num), content)
}
Layout::Builtin(Builtin::Int64) => {
let num = unsafe { *(ptr as *const i64) };
num_to_ast(env, number_literal_to_ast(env.arena, num), content)
}
Layout::Builtin(Builtin::Int32) => {
let num = unsafe { *(ptr as *const i32) };
num_to_ast(env, number_literal_to_ast(env.arena, num), content)
}
Layout::Builtin(Builtin::Int16) => {
let num = unsafe { *(ptr as *const i16) };
num_to_ast(env, number_literal_to_ast(env.arena, num), content)
}
Layout::Builtin(Builtin::Int8) => {
let num = unsafe { *(ptr as *const i8) };
num_to_ast(env, number_literal_to_ast(env.arena, num), content)
}
Layout::Builtin(Builtin::Int1) => {
// TODO: bits are not as expected here. // TODO: bits are not as expected here.
// num is always false at the moment. // num is always false at the moment.
let num = unsafe { *(ptr as *const bool) }; let num = unsafe { *(ptr as *const bool) };
bool_to_ast(env, num, content) bool_to_ast(env, num, content)
} }
Layout::Builtin(Builtin::Usize) => { Layout::Builtin(Builtin::Int(int_width)) => {
let num = unsafe { *(ptr as *const usize) }; use IntWidth::*;
num_to_ast(env, number_literal_to_ast(env.arena, num), content) match int_width {
U8 => helper!(u8),
U16 => helper!(u16),
U32 => helper!(u32),
U64 => helper!(u64),
U128 => helper!(u128),
I8 => helper!(i8),
I16 => helper!(i16),
I32 => helper!(i32),
I64 => helper!(i64),
I128 => helper!(i128),
} }
Layout::Builtin(Builtin::Float64) => {
let num = unsafe { *(ptr as *const f64) };
num_to_ast(env, number_literal_to_ast(env.arena, num), content)
} }
Layout::Builtin(Builtin::Float32) => { Layout::Builtin(Builtin::Float(float_width)) => {
let num = unsafe { *(ptr as *const f32) }; use FloatWidth::*;
num_to_ast(env, number_literal_to_ast(env.arena, num), content) match float_width {
F32 => helper!(f32),
F64 => helper!(f64),
F128 => todo!("F128 not implemented"),
}
} }
Layout::Builtin(Builtin::EmptyList) => Expr::List(Collection::empty()), Layout::Builtin(Builtin::EmptyList) => Expr::List(Collection::empty()),
Layout::Builtin(Builtin::List(elem_layout)) => { Layout::Builtin(Builtin::List(elem_layout)) => {

1
cli_utils/Cargo.lock generated
View file

@ -2709,6 +2709,7 @@ dependencies = [
"bumpalo", "bumpalo",
"hashbrown 0.11.2", "hashbrown 0.11.2",
"morphic_lib", "morphic_lib",
"roc_builtins",
"roc_can", "roc_can",
"roc_collections", "roc_collections",
"roc_module", "roc_module",

View file

@ -975,7 +975,6 @@ pub fn listRange(width: utils.IntWidth, low: Opaque, high: Opaque) callconv(.C)
.I32 => helper1(i32, low, high), .I32 => helper1(i32, low, high),
.I64 => helper1(i64, low, high), .I64 => helper1(i64, low, high),
.I128 => helper1(i128, low, high), .I128 => helper1(i128, low, high),
.Usize => helper1(usize, low, high),
}; };
} }

View file

@ -92,17 +92,16 @@ pub const REFCOUNT_ONE_ISIZE: isize = std.math.minInt(isize);
pub const REFCOUNT_ONE: usize = @bitCast(usize, REFCOUNT_ONE_ISIZE); pub const REFCOUNT_ONE: usize = @bitCast(usize, REFCOUNT_ONE_ISIZE);
pub const IntWidth = enum(u8) { pub const IntWidth = enum(u8) {
U8, U8 = 0,
U16, U16 = 1,
U32, U32 = 2,
U64, U64 = 3,
U128, U128 = 4,
I8, I8 = 5,
I16, I16 = 6,
I32, I32 = 7,
I64, I64 = 8,
I128, I128 = 9,
Usize,
}; };
pub fn decrefC( pub fn decrefC(

View file

@ -1,3 +1,4 @@
use roc_module::symbol::Symbol;
use std::ops::Index; use std::ops::Index;
pub const BUILTINS_HOST_OBJ_PATH: &str = env!( pub const BUILTINS_HOST_OBJ_PATH: &str = env!(
@ -27,24 +28,123 @@ pub enum DecWidth {
} }
#[repr(u8)] #[repr(u8)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum FloatWidth { pub enum FloatWidth {
F32, F32,
F64, F64,
F128, F128,
} }
impl FloatWidth {
pub const fn stack_size(&self) -> u32 {
use FloatWidth::*;
match self {
F32 => 4,
F64 => 8,
F128 => 16,
}
}
pub const fn alignment_bytes(&self) -> u32 {
use std::mem::align_of;
use FloatWidth::*;
// TODO actually alignment is architecture-specific
match self {
F32 => align_of::<f32>() as u32,
F64 => align_of::<f64>() as u32,
F128 => align_of::<i128>() as u32,
}
}
pub const fn try_from_symbol(symbol: Symbol) -> Option<Self> {
match symbol {
Symbol::NUM_F64 | Symbol::NUM_BINARY64 | Symbol::NUM_AT_BINARY64 => {
Some(FloatWidth::F64)
}
Symbol::NUM_F32 | Symbol::NUM_BINARY32 | Symbol::NUM_AT_BINARY32 => {
Some(FloatWidth::F32)
}
_ => None,
}
}
}
#[repr(u8)] #[repr(u8)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum IntWidth { pub enum IntWidth {
U8, U8 = 0,
U16, U16 = 1,
U32, U32 = 2,
U64, U64 = 3,
U128, U128 = 4,
I8, I8 = 5,
I16, I16 = 6,
I32, I32 = 7,
I64, I64 = 8,
I128, I128 = 9,
}
impl IntWidth {
pub const fn is_signed(&self) -> bool {
use IntWidth::*;
matches!(self, I8 | I16 | I32 | I64 | I128)
}
pub const fn stack_size(&self) -> u32 {
use IntWidth::*;
match self {
U8 | I8 => 1,
U16 | I16 => 2,
U32 | I32 => 4,
U64 | I64 => 8,
U128 | I128 => 16,
}
}
pub const fn alignment_bytes(&self) -> u32 {
use std::mem::align_of;
use IntWidth::*;
// TODO actually alignment is architecture-specific
match self {
U8 | I8 => align_of::<i8>() as u32,
U16 | I16 => align_of::<i16>() as u32,
U32 | I32 => align_of::<i32>() as u32,
U64 | I64 => align_of::<i64>() as u32,
U128 | I128 => align_of::<i128>() as u32,
}
}
pub const fn try_from_symbol(symbol: Symbol) -> Option<Self> {
match symbol {
Symbol::NUM_I128 | Symbol::NUM_SIGNED128 | Symbol::NUM_AT_SIGNED128 => {
Some(IntWidth::I128)
}
Symbol::NUM_I64 | Symbol::NUM_SIGNED64 | Symbol::NUM_AT_SIGNED64 => Some(IntWidth::I64),
Symbol::NUM_I32 | Symbol::NUM_SIGNED32 | Symbol::NUM_AT_SIGNED32 => Some(IntWidth::I32),
Symbol::NUM_I16 | Symbol::NUM_SIGNED16 | Symbol::NUM_AT_SIGNED16 => Some(IntWidth::I16),
Symbol::NUM_I8 | Symbol::NUM_SIGNED8 | Symbol::NUM_AT_SIGNED8 => Some(IntWidth::I8),
Symbol::NUM_U128 | Symbol::NUM_UNSIGNED128 | Symbol::NUM_AT_UNSIGNED128 => {
Some(IntWidth::U128)
}
Symbol::NUM_U64 | Symbol::NUM_UNSIGNED64 | Symbol::NUM_AT_UNSIGNED64 => {
Some(IntWidth::U64)
}
Symbol::NUM_U32 | Symbol::NUM_UNSIGNED32 | Symbol::NUM_AT_UNSIGNED32 => {
Some(IntWidth::U32)
}
Symbol::NUM_U16 | Symbol::NUM_UNSIGNED16 | Symbol::NUM_AT_UNSIGNED16 => {
Some(IntWidth::U16)
}
Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8 | Symbol::NUM_AT_UNSIGNED8 => Some(IntWidth::U8),
_ => None,
}
}
} }
impl Index<DecWidth> for IntrinsicName { impl Index<DecWidth> for IntrinsicName {

View file

@ -387,17 +387,6 @@ fn can_annotation_help(
}, },
Record { fields, ext, .. } => { Record { fields, ext, .. } => {
let field_types = can_assigned_fields(
env,
&fields.items,
region,
scope,
var_store,
introduced_variables,
local_aliases,
references,
);
let ext_type = match ext { let ext_type = match ext {
Some(loc_ann) => can_annotation_help( Some(loc_ann) => can_annotation_help(
env, env,
@ -412,12 +401,21 @@ fn can_annotation_help(
None => Type::EmptyRec, None => Type::EmptyRec,
}; };
Type::Record(field_types, Box::new(ext_type)) if fields.is_empty() {
match ext {
Some(_) => {
// just `a` does not mean the same as `{}a`, so even
// if there are no fields, still make this a `Record`,
// not an EmptyRec
Type::Record(Default::default(), Box::new(ext_type))
} }
TagUnion { tags, ext, .. } => {
let tag_types = can_tags( None => Type::EmptyRec,
}
} else {
let field_types = can_assigned_fields(
env, env,
tags.items, &fields.items,
region, region,
scope, scope,
var_store, var_store,
@ -426,6 +424,10 @@ fn can_annotation_help(
references, references,
); );
Type::Record(field_types, Box::new(ext_type))
}
}
TagUnion { tags, ext, .. } => {
let ext_type = match ext { let ext_type = match ext {
Some(loc_ann) => can_annotation_help( Some(loc_ann) => can_annotation_help(
env, env,
@ -440,8 +442,32 @@ fn can_annotation_help(
None => Type::EmptyTagUnion, None => Type::EmptyTagUnion,
}; };
if tags.is_empty() {
match ext {
Some(_) => {
// just `a` does not mean the same as `{}a`, so even
// if there are no fields, still make this a `Record`,
// not an EmptyRec
Type::TagUnion(Default::default(), Box::new(ext_type))
}
None => Type::EmptyTagUnion,
}
} else {
let tag_types = can_tags(
env,
tags.items,
region,
scope,
var_store,
introduced_variables,
local_aliases,
references,
);
Type::TagUnion(tag_types, Box::new(ext_type)) Type::TagUnion(tag_types, Box::new(ext_type))
} }
}
SpaceBefore(nested, _) | SpaceAfter(nested, _) => can_annotation_help( SpaceBefore(nested, _) | SpaceAfter(nested, _) => can_annotation_help(
env, env,
nested, nested,
@ -460,7 +486,10 @@ fn can_annotation_help(
Type::Variable(var) Type::Variable(var)
} }
Inferred => { Inferred => {
unimplemented!(); // Inference variables aren't bound to a rigid or a wildcard, so all we have to do is
// make a fresh unconstrained variable, and let the type solver fill it in for us 🤠
let var = var_store.fresh();
Type::Variable(var)
} }
Malformed(string) => { Malformed(string) => {
malformed(env, region, string); malformed(env, region, string);

View file

@ -22,17 +22,7 @@ pub struct ConstrainedModule {
pub constraint: Constraint, pub constraint: Constraint,
} }
pub fn constrain_module( pub fn constrain_module(declarations: &[Declaration], home: ModuleId) -> Constraint {
aliases: &MutMap<Symbol, Alias>,
declarations: &[Declaration],
home: ModuleId,
) -> Constraint {
let mut send_aliases = SendMap::default();
for (symbol, alias) in aliases.iter() {
send_aliases.insert(*symbol, alias.clone());
}
constrain_decls(home, declarations) constrain_decls(home, declarations)
} }

View file

@ -1,5 +1,6 @@
use crate::{Backend, Env, Relocation}; use crate::{Backend, Env, Relocation};
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_collections::all::{MutMap, MutSet}; use roc_collections::all::{MutMap, MutSet};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::ir::{BranchInfo, JoinPointId, Literal, Param, SelfRecursive, Stmt}; use roc_mono::ir::{BranchInfo, JoinPointId, Literal, Param, SelfRecursive, Stmt};
@ -502,12 +503,12 @@ impl<
// move return value to dst. // move return value to dst.
match ret_layout { match ret_layout {
Layout::Builtin(Builtin::Int64 | Builtin::Int1) => { Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64) | Builtin::Bool) => {
let dst_reg = self.claim_general_reg(dst)?; let dst_reg = self.claim_general_reg(dst)?;
ASM::mov_reg64_reg64(&mut self.buf, dst_reg, CC::GENERAL_RETURN_REGS[0]); ASM::mov_reg64_reg64(&mut self.buf, dst_reg, CC::GENERAL_RETURN_REGS[0]);
Ok(()) Ok(())
} }
Layout::Builtin(Builtin::Float64) => { Layout::Builtin(Builtin::Float(FloatWidth::F64)) => {
let dst_reg = self.claim_float_reg(dst)?; let dst_reg = self.claim_float_reg(dst)?;
ASM::mov_freg64_freg64(&mut self.buf, dst_reg, CC::FLOAT_RETURN_REGS[0]); ASM::mov_freg64_freg64(&mut self.buf, dst_reg, CC::FLOAT_RETURN_REGS[0]);
Ok(()) Ok(())
@ -742,13 +743,13 @@ impl<
layout: &Layout<'a>, layout: &Layout<'a>,
) -> Result<(), String> { ) -> Result<(), String> {
match layout { match layout {
Layout::Builtin(Builtin::Int64) => { Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
let dst_reg = self.claim_general_reg(dst)?; let dst_reg = self.claim_general_reg(dst)?;
let src_reg = self.load_to_general_reg(src)?; let src_reg = self.load_to_general_reg(src)?;
ASM::abs_reg64_reg64(&mut self.buf, dst_reg, src_reg); ASM::abs_reg64_reg64(&mut self.buf, dst_reg, src_reg);
Ok(()) Ok(())
} }
Layout::Builtin(Builtin::Float64) => { Layout::Builtin(Builtin::Float(FloatWidth::F64)) => {
let dst_reg = self.claim_float_reg(dst)?; let dst_reg = self.claim_float_reg(dst)?;
let src_reg = self.load_to_float_reg(src)?; let src_reg = self.load_to_float_reg(src)?;
ASM::abs_freg64_freg64(&mut self.buf, &mut self.relocs, dst_reg, src_reg); ASM::abs_freg64_freg64(&mut self.buf, &mut self.relocs, dst_reg, src_reg);
@ -766,14 +767,14 @@ impl<
layout: &Layout<'a>, layout: &Layout<'a>,
) -> Result<(), String> { ) -> Result<(), String> {
match layout { match layout {
Layout::Builtin(Builtin::Int64) => { Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
let dst_reg = self.claim_general_reg(dst)?; let dst_reg = self.claim_general_reg(dst)?;
let src1_reg = self.load_to_general_reg(src1)?; let src1_reg = self.load_to_general_reg(src1)?;
let src2_reg = self.load_to_general_reg(src2)?; let src2_reg = self.load_to_general_reg(src2)?;
ASM::add_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg); ASM::add_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
Ok(()) Ok(())
} }
Layout::Builtin(Builtin::Float64) => { Layout::Builtin(Builtin::Float(FloatWidth::F64)) => {
let dst_reg = self.claim_float_reg(dst)?; let dst_reg = self.claim_float_reg(dst)?;
let src1_reg = self.load_to_float_reg(src1)?; let src1_reg = self.load_to_float_reg(src1)?;
let src2_reg = self.load_to_float_reg(src2)?; let src2_reg = self.load_to_float_reg(src2)?;
@ -792,7 +793,7 @@ impl<
layout: &Layout<'a>, layout: &Layout<'a>,
) -> Result<(), String> { ) -> Result<(), String> {
match layout { match layout {
Layout::Builtin(Builtin::Int64) => { Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
let dst_reg = self.claim_general_reg(dst)?; let dst_reg = self.claim_general_reg(dst)?;
let src1_reg = self.load_to_general_reg(src1)?; let src1_reg = self.load_to_general_reg(src1)?;
let src2_reg = self.load_to_general_reg(src2)?; let src2_reg = self.load_to_general_reg(src2)?;
@ -810,7 +811,7 @@ impl<
layout: &Layout<'a>, layout: &Layout<'a>,
) -> Result<(), String> { ) -> Result<(), String> {
match layout { match layout {
Layout::Builtin(Builtin::Int64) => { Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
let dst_reg = self.claim_general_reg(dst)?; let dst_reg = self.claim_general_reg(dst)?;
let src_reg = self.load_to_general_reg(src)?; let src_reg = self.load_to_general_reg(src)?;
ASM::neg_reg64_reg64(&mut self.buf, dst_reg, src_reg); ASM::neg_reg64_reg64(&mut self.buf, dst_reg, src_reg);
@ -828,7 +829,7 @@ impl<
layout: &Layout<'a>, layout: &Layout<'a>,
) -> Result<(), String> { ) -> Result<(), String> {
match layout { match layout {
Layout::Builtin(Builtin::Int64) => { Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
let dst_reg = self.claim_general_reg(dst)?; let dst_reg = self.claim_general_reg(dst)?;
let src1_reg = self.load_to_general_reg(src1)?; let src1_reg = self.load_to_general_reg(src1)?;
let src2_reg = self.load_to_general_reg(src2)?; let src2_reg = self.load_to_general_reg(src2)?;
@ -847,7 +848,7 @@ impl<
arg_layout: &Layout<'a>, arg_layout: &Layout<'a>,
) -> Result<(), String> { ) -> Result<(), String> {
match arg_layout { match arg_layout {
Layout::Builtin(Builtin::Int64) => { Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
let dst_reg = self.claim_general_reg(dst)?; let dst_reg = self.claim_general_reg(dst)?;
let src1_reg = self.load_to_general_reg(src1)?; let src1_reg = self.load_to_general_reg(src1)?;
let src2_reg = self.load_to_general_reg(src2)?; let src2_reg = self.load_to_general_reg(src2)?;
@ -866,7 +867,7 @@ impl<
arg_layout: &Layout<'a>, arg_layout: &Layout<'a>,
) -> Result<(), String> { ) -> Result<(), String> {
match arg_layout { match arg_layout {
Layout::Builtin(Builtin::Int64) => { Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
let dst_reg = self.claim_general_reg(dst)?; let dst_reg = self.claim_general_reg(dst)?;
let src1_reg = self.load_to_general_reg(src1)?; let src1_reg = self.load_to_general_reg(src1)?;
let src2_reg = self.load_to_general_reg(src2)?; let src2_reg = self.load_to_general_reg(src2)?;
@ -885,7 +886,7 @@ impl<
arg_layout: &Layout<'a>, arg_layout: &Layout<'a>,
) -> Result<(), String> { ) -> Result<(), String> {
match arg_layout { match arg_layout {
Layout::Builtin(Builtin::Int64) => { Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
let dst_reg = self.claim_general_reg(dst)?; let dst_reg = self.claim_general_reg(dst)?;
let src1_reg = self.load_to_general_reg(src1)?; let src1_reg = self.load_to_general_reg(src1)?;
let src2_reg = self.load_to_general_reg(src2)?; let src2_reg = self.load_to_general_reg(src2)?;
@ -903,7 +904,7 @@ impl<
arg_layout: &Layout<'a>, arg_layout: &Layout<'a>,
) -> Result<(), String> { ) -> Result<(), String> {
match arg_layout { match arg_layout {
Layout::Builtin(Builtin::Int64) => { Layout::Builtin(Builtin::Int(IntWidth::I64)) => {
let dst_reg = self.claim_general_reg(dst)?; let dst_reg = self.claim_general_reg(dst)?;
let src_reg = self.load_to_general_reg(src)?; let src_reg = self.load_to_general_reg(src)?;
ASM::is_zero_reg64_reg64(&mut self.buf, dst_reg, src_reg); ASM::is_zero_reg64_reg64(&mut self.buf, dst_reg, src_reg);
@ -1149,10 +1150,10 @@ impl<
ASM::mov_freg64_freg64(&mut self.buf, CC::FLOAT_RETURN_REGS[0], *reg); ASM::mov_freg64_freg64(&mut self.buf, CC::FLOAT_RETURN_REGS[0], *reg);
} }
Some(SymbolStorage::Base { offset, size, .. }) => match layout { Some(SymbolStorage::Base { offset, size, .. }) => match layout {
Layout::Builtin(Builtin::Int64) => { Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
ASM::mov_reg64_base32(&mut self.buf, CC::GENERAL_RETURN_REGS[0], *offset); ASM::mov_reg64_base32(&mut self.buf, CC::GENERAL_RETURN_REGS[0], *offset);
} }
Layout::Builtin(Builtin::Float64) => { Layout::Builtin(Builtin::Float(FloatWidth::F64)) => {
ASM::mov_freg64_base32(&mut self.buf, CC::FLOAT_RETURN_REGS[0], *offset); ASM::mov_freg64_base32(&mut self.buf, CC::FLOAT_RETURN_REGS[0], *offset);
} }
Layout::Builtin(Builtin::Str) => { Layout::Builtin(Builtin::Str) => {
@ -1505,12 +1506,12 @@ impl<
layout: &Layout<'a>, layout: &Layout<'a>,
) -> Result<(), String> { ) -> Result<(), String> {
match layout { match layout {
Layout::Builtin(Builtin::Int64) => { Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
let reg = self.load_to_general_reg(sym)?; let reg = self.load_to_general_reg(sym)?;
ASM::mov_base32_reg64(&mut self.buf, to_offset, reg); ASM::mov_base32_reg64(&mut self.buf, to_offset, reg);
Ok(()) Ok(())
} }
Layout::Builtin(Builtin::Float64) => { Layout::Builtin(Builtin::Float(FloatWidth::F64)) => {
let reg = self.load_to_float_reg(sym)?; let reg = self.load_to_float_reg(sym)?;
ASM::mov_base32_freg64(&mut self.buf, to_offset, reg); ASM::mov_base32_freg64(&mut self.buf, to_offset, reg);
Ok(()) Ok(())
@ -1590,19 +1591,24 @@ impl<
#[macro_export] #[macro_export]
macro_rules! single_register_integers { macro_rules! single_register_integers {
() => { () => {
Builtin::Int1 Builtin::Bool
| Builtin::Int8 | Builtin::Int(
| Builtin::Int16 IntWidth::I8
| Builtin::Int32 | IntWidth::I16
| Builtin::Int64 | IntWidth::I32
| Builtin::Usize | IntWidth::I64
| IntWidth::U8
| IntWidth::U16
| IntWidth::U32
| IntWidth::U64,
)
}; };
} }
#[macro_export] #[macro_export]
macro_rules! single_register_floats { macro_rules! single_register_floats {
() => { () => {
Builtin::Float32 | Builtin::Float64 Builtin::Float(FloatWidth::F32 | FloatWidth::F64)
}; };
} }

View file

@ -3,6 +3,7 @@ use crate::{
single_register_builtins, single_register_floats, single_register_integers, Relocation, single_register_builtins, single_register_floats, single_register_integers, Relocation,
}; };
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout}; use roc_mono::layout::{Builtin, Layout};

View file

@ -413,9 +413,9 @@ where
"Eq: expected all arguments of to have the same layout" "Eq: expected all arguments of to have the same layout"
); );
debug_assert_eq!( debug_assert_eq!(
Layout::Builtin(Builtin::Int1), Layout::Builtin(Builtin::Bool),
*ret_layout, *ret_layout,
"Eq: expected to have return layout of type I1" "Eq: expected to have return layout of type Bool"
); );
self.build_eq(sym, &args[0], &args[1], &arg_layouts[0]) self.build_eq(sym, &args[0], &args[1], &arg_layouts[0])
} }
@ -430,9 +430,9 @@ where
"NotEq: expected all arguments of to have the same layout" "NotEq: expected all arguments of to have the same layout"
); );
debug_assert_eq!( debug_assert_eq!(
Layout::Builtin(Builtin::Int1), Layout::Builtin(Builtin::Bool),
*ret_layout, *ret_layout,
"NotEq: expected to have return layout of type I1" "NotEq: expected to have return layout of type Bool"
); );
self.build_neq(sym, &args[0], &args[1], &arg_layouts[0]) self.build_neq(sym, &args[0], &args[1], &arg_layouts[0])
} }
@ -447,9 +447,9 @@ where
"NumLt: expected all arguments of to have the same layout" "NumLt: expected all arguments of to have the same layout"
); );
debug_assert_eq!( debug_assert_eq!(
Layout::Builtin(Builtin::Int1), Layout::Builtin(Builtin::Bool),
*ret_layout, *ret_layout,
"NumLt: expected to have return layout of type I1" "NumLt: expected to have return layout of type Bool"
); );
self.build_num_lt(sym, &args[0], &args[1], &arg_layouts[0]) self.build_num_lt(sym, &args[0], &args[1], &arg_layouts[0])
} }
@ -489,7 +489,7 @@ where
"NumIsZero: expected to have exactly one argument" "NumIsZero: expected to have exactly one argument"
); );
debug_assert_eq!( debug_assert_eq!(
Layout::Builtin(Builtin::Int1), Layout::Builtin(Builtin::Bool),
*ret_layout, *ret_layout,
"NumIsZero: expected to have return layout of type I1" "NumIsZero: expected to have return layout of type I1"
); );

View file

@ -22,8 +22,8 @@ use crate::llvm::build_str::{
}; };
use crate::llvm::compare::{generic_eq, generic_neq}; use crate::llvm::compare::{generic_eq, generic_neq};
use crate::llvm::convert::{ use crate::llvm::convert::{
basic_type_from_builtin, basic_type_from_layout, basic_type_from_layout_1, self, basic_type_from_builtin, basic_type_from_layout, basic_type_from_layout_1,
block_of_memory_slices, ptr_int, block_of_memory_slices,
}; };
use crate::llvm::refcounting::{ use crate::llvm::refcounting::{
build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount, build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
@ -190,7 +190,18 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
/// on 64-bit systems, this is i64 /// on 64-bit systems, this is i64
/// on 32-bit systems, this is i32 /// on 32-bit systems, this is i32
pub fn ptr_int(&self) -> IntType<'ctx> { pub fn ptr_int(&self) -> IntType<'ctx> {
ptr_int(self.context, self.ptr_bytes) let ctx = self.context;
match self.ptr_bytes {
1 => ctx.i8_type(),
2 => ctx.i16_type(),
4 => ctx.i32_type(),
8 => ctx.i64_type(),
_ => panic!(
"Invalid target: Roc does't support compiling to {}-bit systems.",
self.ptr_bytes * 8
),
}
} }
/// The integer type representing a RocList or RocStr when following the C ABI /// The integer type representing a RocList or RocStr when following the C ABI
@ -703,32 +714,31 @@ fn promote_to_main_function<'a, 'ctx, 'env>(
(main_fn_name, main_fn) (main_fn_name, main_fn)
} }
pub fn int_with_precision<'a, 'ctx, 'env>( fn int_with_precision<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
value: i128, value: i128,
precision: &Builtin, int_width: IntWidth,
) -> IntValue<'ctx> { ) -> IntValue<'ctx> {
match precision { use IntWidth::*;
Builtin::Usize => ptr_int(env.context, env.ptr_bytes).const_int(value as u64, false),
Builtin::Int128 => const_i128(env, value), match int_width {
Builtin::Int64 => env.context.i64_type().const_int(value as u64, false), U128 | I128 => const_i128(env, value),
Builtin::Int32 => env.context.i32_type().const_int(value as u64, false), U64 | I64 => env.context.i64_type().const_int(value as u64, false),
Builtin::Int16 => env.context.i16_type().const_int(value as u64, false), U32 | I32 => env.context.i32_type().const_int(value as u64, false),
Builtin::Int8 => env.context.i8_type().const_int(value as u64, false), U16 | I16 => env.context.i16_type().const_int(value as u64, false),
Builtin::Int1 => env.context.bool_type().const_int(value as u64, false), U8 | I8 => env.context.i8_type().const_int(value as u64, false),
_ => panic!("Invalid layout for int literal = {:?}", precision),
} }
} }
pub fn float_with_precision<'a, 'ctx, 'env>( fn float_with_precision<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
value: f64, value: f64,
precision: &Builtin, float_width: FloatWidth,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
match precision { match float_width {
Builtin::Float64 => env.context.f64_type().const_float(value).into(), FloatWidth::F64 => env.context.f64_type().const_float(value).into(),
Builtin::Float32 => env.context.f32_type().const_float(value).into(), FloatWidth::F32 => env.context.f32_type().const_float(value).into(),
_ => panic!("Invalid layout for float literal = {:?}", precision), FloatWidth::F128 => todo!("F128 is not implemented"),
} }
} }
@ -741,12 +751,19 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
match literal { match literal {
Int(int) => match layout { Int(int) => match layout {
Layout::Builtin(builtin) => int_with_precision(env, *int as i128, builtin).into(), Layout::Builtin(Builtin::Bool) => {
env.context.bool_type().const_int(*int as u64, false).into()
}
Layout::Builtin(Builtin::Int(int_width)) => {
int_with_precision(env, *int as i128, *int_width).into()
}
_ => panic!("Invalid layout for int literal = {:?}", layout), _ => panic!("Invalid layout for int literal = {:?}", layout),
}, },
Float(float) => match layout { Float(float) => match layout {
Layout::Builtin(builtin) => float_with_precision(env, *float, builtin), Layout::Builtin(Builtin::Float(float_width)) => {
float_with_precision(env, *float, *float_width)
}
_ => panic!("Invalid layout for float literal = {:?}", layout), _ => panic!("Invalid layout for float literal = {:?}", layout),
}, },
@ -2131,7 +2148,6 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>(
initial_refcount: IntValue<'ctx>, initial_refcount: IntValue<'ctx>,
) -> PointerValue<'ctx> { ) -> PointerValue<'ctx> {
let builder = env.builder; let builder = env.builder;
let ctx = env.context;
let len_type = env.ptr_int(); let len_type = env.ptr_int();
@ -2150,7 +2166,7 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>(
// We must return a pointer to the first element: // We must return a pointer to the first element:
let data_ptr = { let data_ptr = {
let int_type = ptr_int(ctx, env.ptr_bytes); let int_type = env.ptr_int();
let as_usize_ptr = builder let as_usize_ptr = builder
.build_bitcast( .build_bitcast(
ptr, ptr,
@ -3044,20 +3060,18 @@ fn build_switch_ir<'a, 'ctx, 'env>(
// Build the condition // Build the condition
let cond = match cond_layout { let cond = match cond_layout {
Layout::Builtin(Builtin::Float64) => { Layout::Builtin(Builtin::Float(float_width)) => {
// float matches are done on the bit pattern // float matches are done on the bit pattern
cond_layout = Layout::Builtin(Builtin::Int64); cond_layout = Layout::float_width(float_width);
let int_type = match float_width {
FloatWidth::F32 => env.context.i32_type(),
FloatWidth::F64 => env.context.i64_type(),
FloatWidth::F128 => env.context.i128_type(),
};
builder builder
.build_bitcast(cond_value, env.context.i64_type(), "") .build_bitcast(cond_value, int_type, "")
.into_int_value()
}
Layout::Builtin(Builtin::Float32) => {
// float matches are done on the bit pattern
cond_layout = Layout::Builtin(Builtin::Int32);
builder
.build_bitcast(cond_value, env.context.i32_type(), "")
.into_int_value() .into_int_value()
} }
Layout::Union(variant) => { Layout::Union(variant) => {
@ -3072,7 +3086,7 @@ fn build_switch_ir<'a, 'ctx, 'env>(
// Build the cases // Build the cases
let mut incoming = Vec::with_capacity_in(branches.len(), arena); let mut incoming = Vec::with_capacity_in(branches.len(), arena);
if let Layout::Builtin(Builtin::Int1) = cond_layout { if let Layout::Builtin(Builtin::Bool) = cond_layout {
match (branches, default_branch) { match (branches, default_branch) {
([(0, _, false_branch)], true_branch) | ([(1, _, true_branch)], false_branch) => { ([(0, _, false_branch)], true_branch) | ([(1, _, true_branch)], false_branch) => {
let then_block = context.append_basic_block(parent, "then_block"); let then_block = context.append_basic_block(parent, "then_block");
@ -3333,7 +3347,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx, 'env>(
builder.position_at_end(entry); builder.position_at_end(entry);
let wrapped_layout = roc_result_layout(env.arena, return_layout); let wrapped_layout = roc_result_layout(env.arena, return_layout, env.ptr_bytes);
call_roc_function(env, roc_function, &wrapped_layout, arguments_for_call) call_roc_function(env, roc_function, &wrapped_layout, arguments_for_call)
} else { } else {
call_roc_function(env, roc_function, &return_layout, arguments_for_call) call_roc_function(env, roc_function, &return_layout, arguments_for_call)
@ -3433,7 +3447,7 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx, 'env>(
call_roc_function( call_roc_function(
env, env,
roc_wrapper_function, roc_wrapper_function,
&Layout::Struct(&[Layout::Builtin(Builtin::Int64), return_layout]), &Layout::Struct(&[Layout::u64(), return_layout]),
arguments_for_call, arguments_for_call,
) )
}; };
@ -3810,12 +3824,8 @@ fn make_exception_catcher<'a, 'ctx, 'env>(
function_value function_value
} }
fn roc_result_layout<'a>(arena: &'a Bump, return_layout: Layout<'a>) -> Layout<'a> { fn roc_result_layout<'a>(arena: &'a Bump, return_layout: Layout<'a>, ptr_bytes: u32) -> Layout<'a> {
let elements = [ let elements = [Layout::u64(), Layout::usize(ptr_bytes), return_layout];
Layout::Builtin(Builtin::Int64),
Layout::Builtin(Builtin::Usize),
return_layout,
];
Layout::Struct(arena.alloc(elements)) Layout::Struct(arena.alloc(elements))
} }
@ -4940,7 +4950,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
Layout::Builtin(Builtin::List(element_layout)), Layout::Builtin(Builtin::List(element_layout)),
Layout::Builtin(Builtin::List(result_layout)), Layout::Builtin(Builtin::List(result_layout)),
) => { ) => {
let argument_layouts = &[Layout::Builtin(Builtin::Usize), **element_layout]; let argument_layouts = &[Layout::usize(env.ptr_bytes), **element_layout];
let roc_function_call = roc_function_call( let roc_function_call = roc_function_call(
env, env,
@ -5133,7 +5143,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
closure_layout, closure_layout,
function_owns_closure_data, function_owns_closure_data,
argument_layouts, argument_layouts,
Layout::Builtin(Builtin::Int1), Layout::Builtin(Builtin::Bool),
); );
list_any(env, roc_function_call, list, element_layout) list_any(env, roc_function_call, list, element_layout)
@ -5160,7 +5170,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
closure_layout, closure_layout,
function_owns_closure_data, function_owns_closure_data,
argument_layouts, argument_layouts,
Layout::Builtin(Builtin::Int1), Layout::Builtin(Builtin::Bool),
); );
list_all(env, roc_function_call, list, element_layout) list_all(env, roc_function_call, list, element_layout)
@ -5193,7 +5203,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
closure_layout, closure_layout,
function_owns_closure_data, function_owns_closure_data,
argument_layouts, argument_layouts,
Layout::Builtin(Builtin::Int1), Layout::Builtin(Builtin::Bool),
); );
list_find_unsafe(env, layout_ids, roc_function_call, list, element_layout) list_find_unsafe(env, layout_ids, roc_function_call, list, element_layout)
} }
@ -5297,7 +5307,15 @@ fn run_low_level<'a, 'ctx, 'env>(
// Str.fromInt : Int -> Str // Str.fromInt : Int -> Str
debug_assert_eq!(args.len(), 1); debug_assert_eq!(args.len(), 1);
str_from_int(env, scope, args[0]) let (int, int_layout) = load_symbol_and_layout(scope, &args[0]);
let int = int.into_int_value();
let int_width = match int_layout {
Layout::Builtin(Builtin::Int(int_width)) => *int_width,
_ => unreachable!(),
};
str_from_int(env, int, int_width)
} }
StrFromFloat => { StrFromFloat => {
// Str.fromFloat : Float * -> Str // Str.fromFloat : Float * -> Str
@ -5439,12 +5457,12 @@ fn run_low_level<'a, 'ctx, 'env>(
let (low, low_layout) = load_symbol_and_layout(scope, &args[0]); let (low, low_layout) = load_symbol_and_layout(scope, &args[0]);
let high = load_symbol(scope, &args[1]); let high = load_symbol(scope, &args[1]);
let builtin = match low_layout { let int_width = match low_layout {
Layout::Builtin(builtin) => builtin, Layout::Builtin(Builtin::Int(int_width)) => *int_width,
_ => unreachable!(), _ => unreachable!(),
}; };
list_range(env, *builtin, low.into_int_value(), high.into_int_value()) list_range(env, int_width, low.into_int_value(), high.into_int_value())
} }
ListAppend => { ListAppend => {
// List.append : List elem, elem -> List elem // List.append : List elem, elem -> List elem
@ -5553,17 +5571,12 @@ fn run_low_level<'a, 'ctx, 'env>(
use roc_mono::layout::Builtin::*; use roc_mono::layout::Builtin::*;
match arg_builtin { match arg_builtin {
Usize | Int128 | Int64 | Int32 | Int16 | Int8 => { Int(int_width) => {
build_int_unary_op(env, arg.into_int_value(), arg_builtin, op) let int_type = convert::int_type_from_int_width(env, *int_width);
build_int_unary_op(env, arg.into_int_value(), int_type, op)
} }
Float32 => { Float(float_width) => {
build_float_unary_op(env, arg.into_float_value(), op, FloatWidth::F32) build_float_unary_op(env, arg.into_float_value(), op, *float_width)
}
Float64 => {
build_float_unary_op(env, arg.into_float_value(), op, FloatWidth::F64)
}
Float128 => {
build_float_unary_op(env, arg.into_float_value(), op, FloatWidth::F128)
} }
_ => { _ => {
unreachable!("Compiler bug: tried to run numeric operation {:?} on invalid builtin layout: ({:?})", op, arg_layout); unreachable!("Compiler bug: tried to run numeric operation {:?} on invalid builtin layout: ({:?})", op, arg_layout);
@ -5633,15 +5646,22 @@ fn run_low_level<'a, 'ctx, 'env>(
let tag_lt = env.context.i8_type().const_int(2_u64, false); let tag_lt = env.context.i8_type().const_int(2_u64, false);
match lhs_builtin { match lhs_builtin {
Usize | Int128 | Int64 | Int32 | Int16 | Int8 => { Int(int_width) => {
let are_equal = env.builder.build_int_compare( let are_equal = env.builder.build_int_compare(
IntPredicate::EQ, IntPredicate::EQ,
lhs_arg.into_int_value(), lhs_arg.into_int_value(),
rhs_arg.into_int_value(), rhs_arg.into_int_value(),
"int_eq", "int_eq",
); );
let predicate = if int_width.is_signed() {
IntPredicate::SLT
} else {
IntPredicate::ULT
};
let is_less_than = env.builder.build_int_compare( let is_less_than = env.builder.build_int_compare(
IntPredicate::SLT, predicate,
lhs_arg.into_int_value(), lhs_arg.into_int_value(),
rhs_arg.into_int_value(), rhs_arg.into_int_value(),
"int_compare", "int_compare",
@ -5658,7 +5678,7 @@ fn run_low_level<'a, 'ctx, 'env>(
"lt_or_gt", "lt_or_gt",
) )
} }
Float128 | Float64 | Float32 => { Float(_) => {
let are_equal = env.builder.build_float_compare( let are_equal = env.builder.build_float_compare(
FloatPredicate::OEQ, FloatPredicate::OEQ,
lhs_arg.into_float_value(), lhs_arg.into_float_value(),
@ -5711,13 +5731,15 @@ fn run_low_level<'a, 'ctx, 'env>(
let (lhs_arg, lhs_layout) = load_symbol_and_layout(scope, &args[0]); let (lhs_arg, lhs_layout) = load_symbol_and_layout(scope, &args[0]);
let (rhs_arg, rhs_layout) = load_symbol_and_layout(scope, &args[1]); let (rhs_arg, rhs_layout) = load_symbol_and_layout(scope, &args[1]);
debug_assert_eq!(lhs_layout, rhs_layout);
let int_width = intwidth_from_layout(*lhs_layout);
build_int_binop( build_int_binop(
env, env,
parent, parent,
int_width,
lhs_arg.into_int_value(), lhs_arg.into_int_value(),
lhs_layout,
rhs_arg.into_int_value(), rhs_arg.into_int_value(),
rhs_layout,
op, op,
) )
} }
@ -5727,13 +5749,15 @@ fn run_low_level<'a, 'ctx, 'env>(
let (lhs_arg, lhs_layout) = load_symbol_and_layout(scope, &args[0]); let (lhs_arg, lhs_layout) = load_symbol_and_layout(scope, &args[0]);
let (rhs_arg, rhs_layout) = load_symbol_and_layout(scope, &args[1]); let (rhs_arg, rhs_layout) = load_symbol_and_layout(scope, &args[1]);
debug_assert_eq!(lhs_layout, rhs_layout);
let int_width = intwidth_from_layout(*lhs_layout);
build_int_binop( build_int_binop(
env, env,
parent, parent,
int_width,
lhs_arg.into_int_value(), lhs_arg.into_int_value(),
lhs_layout,
rhs_arg.into_int_value(), rhs_arg.into_int_value(),
rhs_layout,
op, op,
) )
} }
@ -6096,17 +6120,9 @@ fn to_cc_type_builtin<'a, 'ctx, 'env>(
builtin: &Builtin<'a>, builtin: &Builtin<'a>,
) -> BasicTypeEnum<'ctx> { ) -> BasicTypeEnum<'ctx> {
match builtin { match builtin {
Builtin::Int128 Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal => {
| Builtin::Int64 basic_type_from_builtin(env, builtin)
| Builtin::Int32 }
| Builtin::Int16
| Builtin::Int8
| Builtin::Int1
| Builtin::Usize
| Builtin::Decimal
| Builtin::Float128
| Builtin::Float64
| Builtin::Float32 => basic_type_from_builtin(env, builtin),
Builtin::Str | Builtin::EmptyStr | Builtin::List(_) | Builtin::EmptyList => { Builtin::Str | Builtin::EmptyStr | Builtin::List(_) | Builtin::EmptyList => {
env.str_list_c_abi().into() env.str_list_c_abi().into()
} }
@ -6325,27 +6341,10 @@ fn throw_on_overflow<'a, 'ctx, 'env>(
.unwrap() .unwrap()
} }
pub fn intwidth_from_builtin(builtin: Builtin<'_>, ptr_bytes: u32) -> IntWidth { fn intwidth_from_layout(layout: Layout<'_>) -> IntWidth {
use IntWidth::*;
match builtin {
Builtin::Int128 => I128,
Builtin::Int64 => I64,
Builtin::Int32 => I32,
Builtin::Int16 => I16,
Builtin::Int8 => I8,
Builtin::Usize => match ptr_bytes {
4 => I32,
8 => I64,
_ => unreachable!(),
},
_ => unreachable!(),
}
}
fn intwidth_from_layout(layout: Layout<'_>, ptr_bytes: u32) -> IntWidth {
match layout { match layout {
Layout::Builtin(builtin) => intwidth_from_builtin(builtin, ptr_bytes), Layout::Builtin(Builtin::Int(int_width)) => int_width,
_ => unreachable!(), _ => unreachable!(),
} }
} }
@ -6353,10 +6352,9 @@ fn intwidth_from_layout(layout: Layout<'_>, ptr_bytes: u32) -> IntWidth {
fn build_int_binop<'a, 'ctx, 'env>( fn build_int_binop<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>, parent: FunctionValue<'ctx>,
int_width: IntWidth,
lhs: IntValue<'ctx>, lhs: IntValue<'ctx>,
lhs_layout: &Layout<'a>,
rhs: IntValue<'ctx>, rhs: IntValue<'ctx>,
_rhs_layout: &Layout<'a>,
op: LowLevel, op: LowLevel,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
use inkwell::IntPredicate::*; use inkwell::IntPredicate::*;
@ -6364,8 +6362,6 @@ fn build_int_binop<'a, 'ctx, 'env>(
let bd = env.builder; let bd = env.builder;
let int_width = intwidth_from_layout(*lhs_layout, env.ptr_bytes);
match op { match op {
NumAdd => { NumAdd => {
let result = env let result = env
@ -6541,27 +6537,24 @@ pub fn build_num_binop<'a, 'ctx, 'env>(
build_float_binop( build_float_binop(
env, env,
parent, parent,
float_width,
lhs_arg.into_float_value(), lhs_arg.into_float_value(),
rhs_arg.into_float_value(), rhs_arg.into_float_value(),
float_width,
op, op,
) )
}; };
match lhs_builtin { match lhs_builtin {
Usize | Int128 | Int64 | Int32 | Int16 | Int8 => build_int_binop( Int(int_width) => build_int_binop(
env, env,
parent, parent,
*int_width,
lhs_arg.into_int_value(), lhs_arg.into_int_value(),
lhs_layout,
rhs_arg.into_int_value(), rhs_arg.into_int_value(),
rhs_layout,
op, op,
), ),
Float32 => float_binop(FloatWidth::F32), Float(float_width) => float_binop(*float_width),
Float64 => float_binop(FloatWidth::F64),
Float128 => float_binop(FloatWidth::F128),
Decimal => { Decimal => {
build_dec_binop(env, parent, lhs_arg, lhs_layout, rhs_arg, rhs_layout, op) build_dec_binop(env, parent, lhs_arg, lhs_layout, rhs_arg, rhs_layout, op)
@ -6580,9 +6573,9 @@ pub fn build_num_binop<'a, 'ctx, 'env>(
fn build_float_binop<'a, 'ctx, 'env>( fn build_float_binop<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>, parent: FunctionValue<'ctx>,
float_width: FloatWidth,
lhs: FloatValue<'ctx>, lhs: FloatValue<'ctx>,
rhs: FloatValue<'ctx>, rhs: FloatValue<'ctx>,
float_width: FloatWidth,
op: LowLevel, op: LowLevel,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
use inkwell::FloatPredicate::*; use inkwell::FloatPredicate::*;
@ -6843,20 +6836,10 @@ fn int_type_signed_min(int_type: IntType) -> IntValue {
} }
} }
fn builtin_to_int_type<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
builtin: &Builtin<'a>,
) -> IntType<'ctx> {
let result = basic_type_from_builtin(env, builtin);
debug_assert!(result.is_int_type());
result.into_int_type()
}
fn build_int_unary_op<'a, 'ctx, 'env>( fn build_int_unary_op<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
arg: IntValue<'ctx>, arg: IntValue<'ctx>,
arg_layout: &Builtin<'a>, int_type: IntType<'ctx>,
op: LowLevel, op: LowLevel,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
use roc_module::low_level::LowLevel::*; use roc_module::low_level::LowLevel::*;
@ -6866,11 +6849,11 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
match op { match op {
NumNeg => { NumNeg => {
// integer abs overflows when applied to the minimum value of a signed type // integer abs overflows when applied to the minimum value of a signed type
int_neg_raise_on_overflow(env, arg, arg_layout) int_neg_raise_on_overflow(env, arg, int_type)
} }
NumAbs => { NumAbs => {
// integer abs overflows when applied to the minimum value of a signed type // integer abs overflows when applied to the minimum value of a signed type
int_abs_raise_on_overflow(env, arg, arg_layout) int_abs_raise_on_overflow(env, arg, int_type)
} }
NumToFloat => { NumToFloat => {
// TODO: Handle different sized numbers // TODO: Handle different sized numbers
@ -6891,11 +6874,11 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
fn int_neg_raise_on_overflow<'a, 'ctx, 'env>( fn int_neg_raise_on_overflow<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
arg: IntValue<'ctx>, arg: IntValue<'ctx>,
builtin: &Builtin<'a>, int_type: IntType<'ctx>,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
let builder = env.builder; let builder = env.builder;
let min_val = int_type_signed_min(builtin_to_int_type(env, builtin)); let min_val = int_type_signed_min(int_type);
let condition = builder.build_int_compare(IntPredicate::EQ, arg, min_val, "is_min_val"); let condition = builder.build_int_compare(IntPredicate::EQ, arg, min_val, "is_min_val");
let block = env.builder.get_insert_block().expect("to be in a function"); let block = env.builder.get_insert_block().expect("to be in a function");
@ -6921,11 +6904,11 @@ fn int_neg_raise_on_overflow<'a, 'ctx, 'env>(
fn int_abs_raise_on_overflow<'a, 'ctx, 'env>( fn int_abs_raise_on_overflow<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
arg: IntValue<'ctx>, arg: IntValue<'ctx>,
builtin: &Builtin<'a>, int_type: IntType<'ctx>,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
let builder = env.builder; let builder = env.builder;
let min_val = int_type_signed_min(builtin_to_int_type(env, builtin)); let min_val = int_type_signed_min(int_type);
let condition = builder.build_int_compare(IntPredicate::EQ, arg, min_val, "is_min_val"); let condition = builder.build_int_compare(IntPredicate::EQ, arg, min_val, "is_min_val");
let block = env.builder.get_insert_block().expect("to be in a function"); let block = env.builder.get_insert_block().expect("to be in a function");
@ -6945,13 +6928,13 @@ fn int_abs_raise_on_overflow<'a, 'ctx, 'env>(
builder.position_at_end(else_block); builder.position_at_end(else_block);
int_abs_with_overflow(env, arg, builtin) int_abs_with_overflow(env, arg, int_type)
} }
fn int_abs_with_overflow<'a, 'ctx, 'env>( fn int_abs_with_overflow<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
arg: IntValue<'ctx>, arg: IntValue<'ctx>,
arg_layout: &Builtin<'a>, int_type: IntType<'ctx>,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
// This is how libc's abs() is implemented - it uses no branching! // This is how libc's abs() is implemented - it uses no branching!
// //
@ -6964,10 +6947,10 @@ fn int_abs_with_overflow<'a, 'ctx, 'env>(
let ctx = env.context; let ctx = env.context;
let shifted_name = "abs_shift_right"; let shifted_name = "abs_shift_right";
let shifted_alloca = { let shifted_alloca = {
let bits_to_shift = ((arg_layout.stack_size(env.ptr_bytes) as u64) * 8) - 1; let bits_to_shift = int_type.get_bit_width() as u64 - 1;
let shift_val = ctx.i64_type().const_int(bits_to_shift, false); let shift_val = ctx.i64_type().const_int(bits_to_shift, false);
let shifted = bd.build_right_shift(arg, shift_val, true, shifted_name); let shifted = bd.build_right_shift(arg, shift_val, true, shifted_name);
let alloca = bd.build_alloca(basic_type_from_builtin(env, arg_layout), "#int_abs_help"); let alloca = bd.build_alloca(int_type, "#int_abs_help");
// shifted = arg >>> 63 // shifted = arg >>> 63
bd.build_store(alloca, shifted); bd.build_store(alloca, shifted);

View file

@ -123,17 +123,7 @@ fn hash_builtin<'a, 'ctx, 'env>(
let ptr_bytes = env.ptr_bytes; let ptr_bytes = env.ptr_bytes;
match builtin { match builtin {
Builtin::Int128 Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal => {
| Builtin::Int64
| Builtin::Int32
| Builtin::Int16
| Builtin::Int8
| Builtin::Int1
| Builtin::Float64
| Builtin::Float32
| Builtin::Float128
| Builtin::Decimal
| Builtin::Usize => {
let hash_bytes = store_and_use_as_u8_ptr(env, val, layout); let hash_bytes = store_and_use_as_u8_ptr(env, val, layout);
hash_bitcode_fn(env, seed, hash_bytes, layout.stack_size(ptr_bytes)) hash_bitcode_fn(env, seed, hash_bytes, layout.stack_size(ptr_bytes))
} }

View file

@ -14,7 +14,7 @@ use inkwell::types::{BasicType, BasicTypeEnum, PointerType};
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue}; use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
use inkwell::{AddressSpace, IntPredicate}; use inkwell::{AddressSpace, IntPredicate};
use morphic_lib::UpdateMode; use morphic_lib::UpdateMode;
use roc_builtins::bitcode; use roc_builtins::bitcode::{self, IntWidth};
use roc_mono::layout::{Builtin, Layout, LayoutIds}; use roc_mono::layout::{Builtin, Layout, LayoutIds};
use super::build::{load_roc_value, store_roc_value}; use super::build::{load_roc_value, store_roc_value};
@ -183,7 +183,7 @@ pub fn list_reverse<'a, 'ctx, 'env>(
let element_layout = match *list_layout { let element_layout = match *list_layout {
Layout::Builtin(Builtin::EmptyList) => { Layout::Builtin(Builtin::EmptyList) => {
// this pointer will never actually be dereferenced // this pointer will never actually be dereferenced
Layout::Builtin(Builtin::Int64) Layout::i64()
} }
Layout::Builtin(Builtin::List(elem_layout)) => *elem_layout, Layout::Builtin(Builtin::List(elem_layout)) => *elem_layout,
@ -514,7 +514,7 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
/// List.range : Int a, Int a -> List (Int a) /// List.range : Int a, Int a -> List (Int a)
pub fn list_range<'a, 'ctx, 'env>( pub fn list_range<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
builtin: Builtin<'a>, int_width: IntWidth,
low: IntValue<'ctx>, low: IntValue<'ctx>,
high: IntValue<'ctx>, high: IntValue<'ctx>,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
@ -529,10 +529,7 @@ pub fn list_range<'a, 'ctx, 'env>(
let int_width = env let int_width = env
.context .context
.i8_type() .i8_type()
.const_int( .const_int(int_width as u64, false)
crate::llvm::build::intwidth_from_builtin(builtin, env.ptr_bytes) as u64,
false,
)
.into(); .into();
call_bitcode_fn( call_bitcode_fn(

View file

@ -7,13 +7,13 @@ use inkwell::builder::Builder;
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue}; use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
use inkwell::AddressSpace; use inkwell::AddressSpace;
use morphic_lib::UpdateMode; use morphic_lib::UpdateMode;
use roc_builtins::bitcode; use roc_builtins::bitcode::{self, IntWidth};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout}; use roc_mono::layout::{Builtin, Layout};
use super::build::{intwidth_from_builtin, load_symbol, load_symbol_and_layout}; use super::build::load_symbol;
pub static CHAR_LAYOUT: Layout = Layout::Builtin(Builtin::Int8); pub static CHAR_LAYOUT: Layout = Layout::u8();
/// Str.repeat : Str, Nat -> Str /// Str.repeat : Str, Nat -> Str
pub fn str_repeat<'a, 'ctx, 'env>( pub fn str_repeat<'a, 'ctx, 'env>(
@ -282,36 +282,10 @@ pub fn str_trim_right<'a, 'ctx, 'env>(
/// Str.fromInt : Int -> Str /// Str.fromInt : Int -> Str
pub fn str_from_int<'a, 'ctx, 'env>( pub fn str_from_int<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>, value: IntValue<'ctx>,
int_symbol: Symbol, int_width: IntWidth,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
let (int, int_layout) = load_symbol_and_layout(scope, &int_symbol); call_bitcode_fn(env, &[value.into()], &bitcode::STR_FROM_INT[int_width])
match int_layout {
Layout::Builtin(builtin) => match builtin {
Builtin::Usize
| Builtin::Int128
| Builtin::Int64
| Builtin::Int32
| Builtin::Int16
| Builtin::Int8 => {
let intwidth = intwidth_from_builtin(*builtin, env.ptr_bytes);
call_bitcode_fn(env, &[int], &bitcode::STR_FROM_INT[intwidth])
}
_ => {
unreachable!(
"Compiler bug: tried to convert numeric on invalid builtin layout: ({:?})",
int_layout
);
}
},
_ => {
unreachable!(
"Compiler bug: tried to convert numeric on invalid layout: {:?}",
int_layout
);
}
}
} }
/// Str.toUtf8 : Str -> List U8 /// Str.toUtf8 : Str -> List U8

View file

@ -10,6 +10,7 @@ use inkwell::values::{
}; };
use inkwell::{AddressSpace, FloatPredicate, IntPredicate}; use inkwell::{AddressSpace, FloatPredicate, IntPredicate};
use roc_builtins::bitcode; use roc_builtins::bitcode;
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout}; use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
@ -88,19 +89,39 @@ fn build_eq_builtin<'a, 'ctx, 'env>(
}; };
match builtin { match builtin {
Builtin::Int128 => int_cmp(IntPredicate::EQ, "eq_i128"), Builtin::Int(int_width) => {
Builtin::Int64 => int_cmp(IntPredicate::EQ, "eq_i64"), use IntWidth::*;
Builtin::Int32 => int_cmp(IntPredicate::EQ, "eq_i32"),
Builtin::Int16 => int_cmp(IntPredicate::EQ, "eq_i16"),
Builtin::Int8 => int_cmp(IntPredicate::EQ, "eq_i8"),
Builtin::Int1 => int_cmp(IntPredicate::EQ, "eq_i1"),
Builtin::Usize => int_cmp(IntPredicate::EQ, "eq_usize"), let name = match int_width {
I128 => "eq_i128",
I64 => "eq_i64",
I32 => "eq_i32",
I16 => "eq_i16",
I8 => "eq_i8",
U128 => "eq_u128",
U64 => "eq_u64",
U32 => "eq_u32",
U16 => "eq_u16",
U8 => "eq_u8",
};
int_cmp(IntPredicate::EQ, name)
}
Builtin::Float(float_width) => {
use FloatWidth::*;
let name = match float_width {
F128 => "eq_f128",
F64 => "eq_f64",
F32 => "eq_f32",
};
float_cmp(FloatPredicate::OEQ, name)
}
Builtin::Bool => int_cmp(IntPredicate::EQ, "eq_i1"),
Builtin::Decimal => call_bitcode_fn(env, &[lhs_val, rhs_val], bitcode::DEC_EQ), Builtin::Decimal => call_bitcode_fn(env, &[lhs_val, rhs_val], bitcode::DEC_EQ),
Builtin::Float128 => float_cmp(FloatPredicate::OEQ, "eq_f128"),
Builtin::Float64 => float_cmp(FloatPredicate::OEQ, "eq_f64"),
Builtin::Float32 => float_cmp(FloatPredicate::OEQ, "eq_f32"),
Builtin::Str => str_equal(env, lhs_val, rhs_val), Builtin::Str => str_equal(env, lhs_val, rhs_val),
Builtin::List(elem) => build_list_eq( Builtin::List(elem) => build_list_eq(
@ -231,19 +252,39 @@ fn build_neq_builtin<'a, 'ctx, 'env>(
}; };
match builtin { match builtin {
Builtin::Int128 => int_cmp(IntPredicate::NE, "neq_i128"), Builtin::Int(int_width) => {
Builtin::Int64 => int_cmp(IntPredicate::NE, "neq_i64"), use IntWidth::*;
Builtin::Int32 => int_cmp(IntPredicate::NE, "neq_i32"),
Builtin::Int16 => int_cmp(IntPredicate::NE, "neq_i16"),
Builtin::Int8 => int_cmp(IntPredicate::NE, "neq_i8"),
Builtin::Int1 => int_cmp(IntPredicate::NE, "neq_i1"),
Builtin::Usize => int_cmp(IntPredicate::NE, "neq_usize"), let name = match int_width {
I128 => "neq_i128",
I64 => "neq_i64",
I32 => "neq_i32",
I16 => "neq_i16",
I8 => "neq_i8",
U128 => "neq_u128",
U64 => "neq_u64",
U32 => "neq_u32",
U16 => "neq_u16",
U8 => "neq_u8",
};
int_cmp(IntPredicate::NE, name)
}
Builtin::Float(float_width) => {
use FloatWidth::*;
let name = match float_width {
F128 => "neq_f128",
F64 => "neq_f64",
F32 => "neq_f32",
};
float_cmp(FloatPredicate::ONE, name)
}
Builtin::Bool => int_cmp(IntPredicate::NE, "neq_i1"),
Builtin::Decimal => call_bitcode_fn(env, &[lhs_val, rhs_val], bitcode::DEC_NEQ), Builtin::Decimal => call_bitcode_fn(env, &[lhs_val, rhs_val], bitcode::DEC_NEQ),
Builtin::Float128 => float_cmp(FloatPredicate::ONE, "neq_f128"),
Builtin::Float64 => float_cmp(FloatPredicate::ONE, "neq_f64"),
Builtin::Float32 => float_cmp(FloatPredicate::ONE, "neq_f32"),
Builtin::Str => { Builtin::Str => {
let is_equal = str_equal(env, lhs_val, rhs_val).into_int_value(); let is_equal = str_equal(env, lhs_val, rhs_val).into_int_value();

View file

@ -1,7 +1,8 @@
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use inkwell::context::Context; use inkwell::context::Context;
use inkwell::types::{BasicType, BasicTypeEnum, IntType, StructType}; use inkwell::types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType};
use inkwell::AddressSpace; use inkwell::AddressSpace;
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_mono::layout::{Builtin, Layout, UnionLayout}; use roc_mono::layout::{Builtin, Layout, UnionLayout};
fn basic_type_from_record<'a, 'ctx, 'env>( fn basic_type_from_record<'a, 'ctx, 'env>(
@ -143,20 +144,12 @@ pub fn basic_type_from_builtin<'a, 'ctx, 'env>(
use Builtin::*; use Builtin::*;
let context = env.context; let context = env.context;
let ptr_bytes = env.ptr_bytes;
match builtin { match builtin {
Int128 => context.i128_type().as_basic_type_enum(), Int(int_width) => int_type_from_int_width(env, *int_width).as_basic_type_enum(),
Int64 => context.i64_type().as_basic_type_enum(), Float(float_width) => float_type_from_float_width(env, *float_width).as_basic_type_enum(),
Int32 => context.i32_type().as_basic_type_enum(), Bool => context.bool_type().as_basic_type_enum(),
Int16 => context.i16_type().as_basic_type_enum(),
Int8 => context.i8_type().as_basic_type_enum(),
Int1 => context.bool_type().as_basic_type_enum(),
Usize => ptr_int(context, ptr_bytes).as_basic_type_enum(),
Decimal => context.i128_type().as_basic_type_enum(), Decimal => context.i128_type().as_basic_type_enum(),
Float128 => context.f128_type().as_basic_type_enum(),
Float64 => context.f64_type().as_basic_type_enum(),
Float32 => context.f32_type().as_basic_type_enum(),
Dict(_, _) | EmptyDict => zig_dict_type(env).into(), Dict(_, _) | EmptyDict => zig_dict_type(env).into(),
Set(_) | EmptySet => zig_dict_type(env).into(), Set(_) | EmptySet => zig_dict_type(env).into(),
List(_) | EmptyList => zig_list_type(env).into(), List(_) | EmptyList => zig_list_type(env).into(),
@ -164,6 +157,34 @@ pub fn basic_type_from_builtin<'a, 'ctx, 'env>(
} }
} }
pub fn int_type_from_int_width<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
int_width: IntWidth,
) -> IntType<'ctx> {
use IntWidth::*;
match int_width {
U128 | I128 => env.context.i128_type(),
U64 | I64 => env.context.i64_type(),
U32 | I32 => env.context.i32_type(),
U16 | I16 => env.context.i16_type(),
U8 | I8 => env.context.i8_type(),
}
}
pub fn float_type_from_float_width<'a, 'ctx, 'env>(
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
float_width: FloatWidth,
) -> FloatType<'ctx> {
use FloatWidth::*;
match float_width {
F128 => todo!("F128 is not implemented"),
F64 => env.context.f64_type(),
F32 => env.context.f32_type(),
}
}
pub fn block_of_memory_slices<'ctx>( pub fn block_of_memory_slices<'ctx>(
context: &'ctx Context, context: &'ctx Context,
layouts: &[&[Layout<'_>]], layouts: &[&[Layout<'_>]],
@ -241,19 +262,6 @@ fn block_of_memory_help(context: &Context, union_size: u32) -> BasicTypeEnum<'_>
} }
} }
pub fn ptr_int(ctx: &Context, ptr_bytes: u32) -> IntType<'_> {
match ptr_bytes {
1 => ctx.i8_type(),
2 => ctx.i16_type(),
4 => ctx.i32_type(),
8 => ctx.i64_type(),
_ => panic!(
"Invalid target: Roc does't support compiling to {}-bit systems.",
ptr_bytes * 8
),
}
}
/// The int type that the C ABI turns our RocList/RocStr into /// The int type that the C ABI turns our RocList/RocStr into
pub fn str_list_int(ctx: &Context, ptr_bytes: u32) -> IntType<'_> { pub fn str_list_int(ctx: &Context, ptr_bytes: u32) -> IntType<'_> {
match ptr_bytes { match ptr_bytes {

View file

@ -1,6 +1,5 @@
use crate::llvm::build::Env; use crate::llvm::build::Env;
use crate::llvm::build::{add_func, C_CALL_CONV}; use crate::llvm::build::{add_func, C_CALL_CONV};
use crate::llvm::convert::ptr_int;
use inkwell::module::Linkage; use inkwell::module::Linkage;
use inkwell::values::BasicValue; use inkwell::values::BasicValue;
use inkwell::AddressSpace; use inkwell::AddressSpace;
@ -11,9 +10,8 @@ pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
let ctx = env.context; let ctx = env.context;
let module = env.module; let module = env.module;
let builder = env.builder; let builder = env.builder;
let ptr_bytes = env.ptr_bytes;
let usize_type = ptr_int(ctx, ptr_bytes); let usize_type = env.ptr_int();
let i8_ptr_type = ctx.i8_type().ptr_type(AddressSpace::Generic); let i8_ptr_type = ctx.i8_type().ptr_type(AddressSpace::Generic);
// roc_alloc // roc_alloc

View file

@ -5,7 +5,7 @@ use crate::llvm::build::{
FAST_CALL_CONV, TAG_DATA_INDEX, TAG_ID_INDEX, FAST_CALL_CONV, TAG_DATA_INDEX, TAG_ID_INDEX,
}; };
use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list}; use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list};
use crate::llvm::convert::{basic_type_from_layout, basic_type_from_layout_1, ptr_int}; use crate::llvm::convert::{basic_type_from_layout, basic_type_from_layout_1};
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use inkwell::basic_block::BasicBlock; use inkwell::basic_block::BasicBlock;
use inkwell::context::Context; use inkwell::context::Context;
@ -46,7 +46,7 @@ impl<'ctx> PointerToRefcount<'ctx> {
/// alignment works out that way. /// alignment works out that way.
pub unsafe fn from_ptr<'a, 'env>(env: &Env<'a, 'ctx, 'env>, ptr: PointerValue<'ctx>) -> Self { pub unsafe fn from_ptr<'a, 'env>(env: &Env<'a, 'ctx, 'env>, ptr: PointerValue<'ctx>) -> Self {
// must make sure it's a pointer to usize // must make sure it's a pointer to usize
let refcount_type = ptr_int(env.context, env.ptr_bytes); let refcount_type = env.ptr_int();
let value = env let value = env
.builder .builder
@ -66,7 +66,7 @@ impl<'ctx> PointerToRefcount<'ctx> {
) -> Self { ) -> Self {
let builder = env.builder; let builder = env.builder;
// pointer to usize // pointer to usize
let refcount_type = ptr_int(env.context, env.ptr_bytes); let refcount_type = env.ptr_int();
let refcount_ptr_type = refcount_type.ptr_type(AddressSpace::Generic); let refcount_ptr_type = refcount_type.ptr_type(AddressSpace::Generic);
let ptr_as_usize_ptr = builder let ptr_as_usize_ptr = builder
@ -127,7 +127,7 @@ impl<'ctx> PointerToRefcount<'ctx> {
fn increment<'a, 'env>(&self, amount: IntValue<'ctx>, env: &Env<'a, 'ctx, 'env>) { fn increment<'a, 'env>(&self, amount: IntValue<'ctx>, env: &Env<'a, 'ctx, 'env>) {
let refcount = self.get_refcount(env); let refcount = self.get_refcount(env);
let builder = env.builder; let builder = env.builder;
let refcount_type = ptr_int(env.context, env.ptr_bytes); let refcount_type = env.ptr_int();
let is_static_allocation = builder.build_int_compare( let is_static_allocation = builder.build_int_compare(
IntPredicate::EQ, IntPredicate::EQ,
@ -857,7 +857,7 @@ fn modify_refcount_str_help<'a, 'ctx, 'env>(
let is_big_and_non_empty = builder.build_int_compare( let is_big_and_non_empty = builder.build_int_compare(
IntPredicate::SGT, IntPredicate::SGT,
len, len,
ptr_int(ctx, env.ptr_bytes).const_zero(), env.ptr_int().const_zero(),
"is_big_str", "is_big_str",
); );
@ -979,7 +979,7 @@ fn modify_refcount_dict_help<'a, 'ctx, 'env>(
let is_non_empty = builder.build_int_compare( let is_non_empty = builder.build_int_compare(
IntPredicate::SGT, IntPredicate::SGT,
len, len,
ptr_int(ctx, env.ptr_bytes).const_zero(), env.ptr_int().const_zero(),
"is_non_empty", "is_non_empty",
); );
@ -1027,7 +1027,7 @@ fn build_header<'a, 'ctx, 'env>(
env, env,
fn_name, fn_name,
env.context.void_type().into(), env.context.void_type().into(),
&[arg_type, ptr_int(env.context, env.ptr_bytes).into()], &[arg_type, env.ptr_int().into()],
), ),
Mode::Dec => build_header_help(env, fn_name, env.context.void_type().into(), &[arg_type]), Mode::Dec => build_header_help(env, fn_name, env.context.void_type().into(), &[arg_type]),
} }

View file

@ -277,6 +277,7 @@ impl<'a> WasmBackend<'a> {
location, location,
size, size,
alignment_bytes, alignment_bytes,
..
} => { } => {
let (from_ptr, from_offset) = let (from_ptr, from_offset) =
location.local_and_offset(self.storage.stack_frame_pointer); location.local_and_offset(self.storage.stack_frame_pointer);
@ -328,7 +329,7 @@ impl<'a> WasmBackend<'a> {
self.start_block(BlockType::NoResult) self.start_block(BlockType::NoResult)
} }
let is_bool = matches!(cond_layout, Layout::Builtin(Builtin::Int1)); let is_bool = matches!(cond_layout, Layout::Builtin(Builtin::Bool));
let cond_type = WasmLayout::new(cond_layout).value_type(); let cond_type = WasmLayout::new(cond_layout).value_type();
// then, we jump whenever the value under scrutiny is equal to the value of a branch // then, we jump whenever the value under scrutiny is equal to the value of a branch
@ -623,6 +624,21 @@ impl<'a> WasmBackend<'a> {
} }
StoredValue::StackMemory { location, .. } => match lit { StoredValue::StackMemory { location, .. } => match lit {
Literal::Decimal(decimal) => {
let (local_id, offset) =
location.local_and_offset(self.storage.stack_frame_pointer);
let lower_bits = decimal.0 as i64;
let upper_bits = (decimal.0 >> 64) as i64;
self.code_builder.get_local(local_id);
self.code_builder.i64_const(lower_bits);
self.code_builder.i64_store(Align::Bytes8, offset);
self.code_builder.get_local(local_id);
self.code_builder.i64_const(upper_bits);
self.code_builder.i64_store(Align::Bytes8, offset + 8);
}
Literal::Str(string) => { Literal::Str(string) => {
let (local_id, offset) = let (local_id, offset) =
location.local_and_offset(self.storage.stack_frame_pointer); location.local_and_offset(self.storage.stack_frame_pointer);

View file

@ -1,7 +1,17 @@
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_mono::layout::{Layout, UnionLayout}; use roc_mono::layout::{Layout, UnionLayout};
use crate::{wasm_module::ValueType, PTR_SIZE, PTR_TYPE}; use crate::{wasm_module::ValueType, PTR_SIZE, PTR_TYPE};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StackMemoryFormat {
/// Record, Str, List, Dict, etc.
Aggregate,
Int128,
Float128,
Decimal,
}
// See README for background information on Wasm locals, memory and function calls // See README for background information on Wasm locals, memory and function calls
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum WasmLayout { pub enum WasmLayout {
@ -10,7 +20,11 @@ pub enum WasmLayout {
Primitive(ValueType, u32), Primitive(ValueType, u32),
// Local pointer to stack memory // Local pointer to stack memory
StackMemory { size: u32, alignment_bytes: u32 }, StackMemory {
size: u32,
alignment_bytes: u32,
format: StackMemoryFormat,
},
// Local pointer to heap memory // Local pointer to heap memory
HeapMemory, HeapMemory,
@ -26,32 +40,51 @@ impl WasmLayout {
let alignment_bytes = layout.alignment_bytes(PTR_SIZE); let alignment_bytes = layout.alignment_bytes(PTR_SIZE);
match layout { match layout {
Layout::Builtin(Int32 | Int16 | Int8 | Int1 | Usize) => Self::Primitive(I32, size), Layout::Builtin(Int(int_width)) => {
use IntWidth::*;
Layout::Builtin(Int64) => Self::Primitive(I64, size), match int_width {
I32 | U32 | I16 | U16 | I8 | U8 => Self::Primitive(ValueType::I32, size),
I64 | U64 => Self::Primitive(ValueType::I64, size),
I128 | U128 => Self::StackMemory {
size,
alignment_bytes,
format: StackMemoryFormat::Int128,
},
}
}
Layout::Builtin(Float32) => Self::Primitive(F32, size), Layout::Builtin(Bool) => Self::Primitive(I32, size),
Layout::Builtin(Float64) => Self::Primitive(F64, size), Layout::Builtin(Float(float_width)) => {
use FloatWidth::*;
match float_width {
F32 => Self::Primitive(ValueType::F32, size),
F64 => Self::Primitive(ValueType::F64, size),
F128 => Self::StackMemory {
size,
alignment_bytes,
format: StackMemoryFormat::Float128,
},
}
}
Layout::Builtin(Decimal) => Self::StackMemory {
size,
alignment_bytes,
format: StackMemoryFormat::Decimal,
},
Layout::Builtin( Layout::Builtin(
Int128 Str | Dict(_, _) | Set(_) | List(_) | EmptyStr | EmptyList | EmptyDict | EmptySet,
| Decimal
| Float128
| Str
| Dict(_, _)
| Set(_)
| List(_)
| EmptyStr
| EmptyList
| EmptyDict
| EmptySet,
) )
| Layout::Struct(_) | Layout::Struct(_)
| Layout::LambdaSet(_) | Layout::LambdaSet(_)
| Layout::Union(NonRecursive(_)) => Self::StackMemory { | Layout::Union(NonRecursive(_)) => Self::StackMemory {
size, size,
alignment_bytes, alignment_bytes,
format: StackMemoryFormat::Aggregate,
}, },
Layout::Union( Layout::Union(

View file

@ -4,7 +4,7 @@ use bumpalo::Bump;
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use crate::layout::WasmLayout; use crate::layout::{StackMemoryFormat, WasmLayout};
use crate::wasm_module::{Align, CodeBuilder, LocalId, ValueType, VmSymbolState}; use crate::wasm_module::{Align, CodeBuilder, LocalId, ValueType, VmSymbolState};
use crate::{copy_memory, round_up_to_alignment, CopyMemoryConfig, PTR_SIZE, PTR_TYPE}; use crate::{copy_memory, round_up_to_alignment, CopyMemoryConfig, PTR_SIZE, PTR_TYPE};
@ -50,6 +50,7 @@ pub enum StoredValue {
location: StackMemoryLocation, location: StackMemoryLocation,
size: u32, size: u32,
alignment_bytes: u32, alignment_bytes: u32,
format: StackMemoryFormat,
}, },
} }
@ -147,6 +148,7 @@ impl<'a> Storage<'a> {
WasmLayout::StackMemory { WasmLayout::StackMemory {
size, size,
alignment_bytes, alignment_bytes,
format,
} => { } => {
let location = match kind { let location = match kind {
StoredValueKind::Parameter => { StoredValueKind::Parameter => {
@ -175,6 +177,7 @@ impl<'a> Storage<'a> {
location, location,
size: *size, size: *size,
alignment_bytes: *alignment_bytes, alignment_bytes: *alignment_bytes,
format: *format,
} }
} }
}; };
@ -239,13 +242,26 @@ impl<'a> Storage<'a> {
code_builder.set_top_symbol(sym); code_builder.set_top_symbol(sym);
} }
StoredValue::StackMemory { location, .. } => { StoredValue::StackMemory {
location, format, ..
} => {
let (local_id, offset) = location.local_and_offset(self.stack_frame_pointer); let (local_id, offset) = location.local_and_offset(self.stack_frame_pointer);
// Load the address of the value
code_builder.get_local(local_id); code_builder.get_local(local_id);
if offset != 0 { if offset != 0 {
code_builder.i32_const(offset as i32); code_builder.i32_const(offset as i32);
code_builder.i32_add(); code_builder.i32_add();
} }
if format != StackMemoryFormat::Aggregate {
// It's one of the 128-bit numbers, all of which we load as two i64's
// Mark the same Symbol twice in the VM value stack! Shouldn't matter except debug.
code_builder.i64_load(Align::Bytes8, offset);
code_builder.set_top_symbol(sym);
code_builder.i64_load(Align::Bytes8, offset + 8);
}
code_builder.set_top_symbol(sym); code_builder.set_top_symbol(sym);
} }
} }
@ -292,6 +308,7 @@ impl<'a> Storage<'a> {
location, location,
size, size,
alignment_bytes, alignment_bytes,
format: StackMemoryFormat::Aggregate,
} = self.get(sym) } = self.get(sym)
{ {
if *size == 0 { if *size == 0 {
@ -334,6 +351,7 @@ impl<'a> Storage<'a> {
location, location,
size, size,
alignment_bytes, alignment_bytes,
..
} => { } => {
let (from_ptr, from_offset) = location.local_and_offset(self.stack_frame_pointer); let (from_ptr, from_offset) = location.local_and_offset(self.stack_frame_pointer);
copy_memory( copy_memory(
@ -390,6 +408,7 @@ impl<'a> Storage<'a> {
location, location,
size, size,
alignment_bytes, alignment_bytes,
..
} => { } => {
let (to_ptr, to_offset) = location.local_and_offset(self.stack_frame_pointer); let (to_ptr, to_offset) = location.local_and_offset(self.stack_frame_pointer);
copy_memory( copy_memory(
@ -490,11 +509,13 @@ impl<'a> Storage<'a> {
location: to_location, location: to_location,
size: to_size, size: to_size,
alignment_bytes: to_alignment_bytes, alignment_bytes: to_alignment_bytes,
..
}, },
StackMemory { StackMemory {
location: from_location, location: from_location,
size: from_size, size: from_size,
alignment_bytes: from_alignment_bytes, alignment_bytes: from_alignment_bytes,
..
}, },
) => { ) => {
let (from_ptr, from_offset) = let (from_ptr, from_offset) =

View file

@ -3627,11 +3627,7 @@ fn fabricate_effects_module<'a>(
scope, scope,
}; };
let constraint = constrain_module( let constraint = constrain_module(&module_output.declarations, module_id);
&module_output.aliases,
&module_output.declarations,
module_id,
);
let module = Module { let module = Module {
module_id, module_id,
@ -3764,11 +3760,7 @@ where
)), )),
}; };
let constraint = constrain_module( let constraint = constrain_module(&module_output.declarations, module_id);
&module_output.aliases,
&module_output.declarations,
module_id,
);
let module = Module { let module = Module {
module_id, module_id,

View file

@ -15,6 +15,7 @@ roc_unify = { path = "../unify" }
roc_solve = { path = "../solve" } roc_solve = { path = "../solve" }
roc_std = { path = "../../roc_std" } roc_std = { path = "../../roc_std" }
roc_problem = { path = "../problem" } roc_problem = { path = "../problem" }
roc_builtins = { path = "../builtins" }
ven_pretty = { path = "../../vendor/pretty" } ven_pretty = { path = "../../vendor/pretty" }
morphic_lib = { path = "../../vendor/morphic_lib" } morphic_lib = { path = "../../vendor/morphic_lib" }
bumpalo = { version = "3.8.0", features = ["collections"] } bumpalo = { version = "3.8.0", features = ["collections"] }

View file

@ -590,7 +590,7 @@ impl<'a> ResultRepr<'a> {
err: tags[ERR_TAG_ID as usize][0], err: tags[ERR_TAG_ID as usize][0],
ok: tags[OK_TAG_ID as usize][0], ok: tags[OK_TAG_ID as usize][0],
}, },
Layout::Builtin(Builtin::Int1) => ResultRepr::Int1, Layout::Builtin(Builtin::Bool) => ResultRepr::Int1,
other => unreachable!("unexpected layout: {:?}", other), other => unreachable!("unexpected layout: {:?}", other),
} }
} }
@ -1086,7 +1086,7 @@ fn call_spec(
Ok(new_state) Ok(new_state)
}; };
let state_layout = Layout::Builtin(Builtin::Int1); let state_layout = Layout::Builtin(Builtin::Bool);
let state_type = layout_spec(builder, &state_layout)?; let state_type = layout_spec(builder, &state_layout)?;
let init_state = new_num(builder, block)?; let init_state = new_num(builder, block)?;
@ -1105,7 +1105,7 @@ fn call_spec(
Ok(new_state) Ok(new_state)
}; };
let state_layout = Layout::Builtin(Builtin::Int1); let state_layout = Layout::Builtin(Builtin::Bool);
let state_type = layout_spec(builder, &state_layout)?; let state_type = layout_spec(builder, &state_layout)?;
let init_state = new_num(builder, block)?; let init_state = new_num(builder, block)?;
@ -1116,7 +1116,7 @@ fn call_spec(
let list = env.symbols[xs]; let list = env.symbols[xs];
// ListFindUnsafe returns { value: v, found: Bool=Int1 } // ListFindUnsafe returns { value: v, found: Bool=Int1 }
let output_layouts = vec![arg_layouts[0], Layout::Builtin(Builtin::Int1)]; let output_layouts = vec![arg_layouts[0], Layout::Builtin(Builtin::Bool)];
let output_layout = Layout::Struct(&output_layouts); let output_layout = Layout::Struct(&output_layouts);
let output_type = layout_spec(builder, &output_layout)?; let output_type = layout_spec(builder, &output_layout)?;
@ -1718,8 +1718,8 @@ fn builtin_spec(
use Builtin::*; use Builtin::*;
match builtin { match builtin {
Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Usize => builder.add_tuple_type(&[]), Int(_) | Bool => builder.add_tuple_type(&[]),
Decimal | Float128 | Float64 | Float32 => builder.add_tuple_type(&[]), Decimal | Float(_) => builder.add_tuple_type(&[]),
Str | EmptyStr => str_type(builder), Str | EmptyStr => str_type(builder),
Dict(key_layout, value_layout) => { Dict(key_layout, value_layout) => {
let value_type = layout_spec_help(builder, value_layout, when_recursive)?; let value_type = layout_spec_help(builder, value_layout, when_recursive)?;

View file

@ -1,9 +1,9 @@
use crate::exhaustive::{Ctor, RenderAs, TagId, Union}; use crate::exhaustive::{Ctor, RenderAs, TagId, Union};
use crate::ir::{ use crate::ir::{
BranchInfo, DestructType, Env, Expr, FloatPrecision, IntPrecision, JoinPointId, Literal, Param, BranchInfo, DestructType, Env, Expr, JoinPointId, Literal, Param, Pattern, Procs, Stmt,
Pattern, Procs, Stmt,
}; };
use crate::layout::{Builtin, Layout, LayoutCache, TagIdIntType, UnionLayout}; use crate::layout::{Builtin, Layout, LayoutCache, TagIdIntType, UnionLayout};
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_collections::all::{MutMap, MutSet}; use roc_collections::all::{MutMap, MutSet};
use roc_module::ident::TagName; use roc_module::ident::TagName;
use roc_module::low_level::LowLevel; use roc_module::low_level::LowLevel;
@ -86,8 +86,8 @@ enum Test<'a> {
union: crate::exhaustive::Union, union: crate::exhaustive::Union,
arguments: Vec<(Pattern<'a>, Layout<'a>)>, arguments: Vec<(Pattern<'a>, Layout<'a>)>,
}, },
IsInt(i128, IntPrecision), IsInt(i128, IntWidth),
IsFloat(u64, FloatPrecision), IsFloat(u64, FloatWidth),
IsDecimal(RocDec), IsDecimal(RocDec),
IsStr(Box<str>), IsStr(Box<str>),
IsBit(bool), IsBit(bool),
@ -1300,7 +1300,7 @@ fn test_to_equality<'a>(
debug_assert!(test_int <= i64::MAX as i128); debug_assert!(test_int <= i64::MAX as i128);
let lhs = Expr::Literal(Literal::Int(test_int as i128)); let lhs = Expr::Literal(Literal::Int(test_int as i128));
let lhs_symbol = env.unique_symbol(); let lhs_symbol = env.unique_symbol();
stores.push((lhs_symbol, precision.as_layout(), lhs)); stores.push((lhs_symbol, Layout::int_width(precision), lhs));
(stores, lhs_symbol, rhs_symbol, None) (stores, lhs_symbol, rhs_symbol, None)
} }
@ -1310,7 +1310,7 @@ fn test_to_equality<'a>(
let test_float = f64::from_bits(test_int as u64); let test_float = f64::from_bits(test_int as u64);
let lhs = Expr::Literal(Literal::Float(test_float)); let lhs = Expr::Literal(Literal::Float(test_float));
let lhs_symbol = env.unique_symbol(); let lhs_symbol = env.unique_symbol();
stores.push((lhs_symbol, precision.as_layout(), lhs)); stores.push((lhs_symbol, Layout::float_width(precision), lhs));
(stores, lhs_symbol, rhs_symbol, None) (stores, lhs_symbol, rhs_symbol, None)
} }
@ -1330,7 +1330,7 @@ fn test_to_equality<'a>(
let lhs = Expr::Literal(Literal::Byte(test_byte as u8)); let lhs = Expr::Literal(Literal::Byte(test_byte as u8));
let lhs_symbol = env.unique_symbol(); let lhs_symbol = env.unique_symbol();
stores.push((lhs_symbol, Layout::Builtin(Builtin::Int8), lhs)); stores.push((lhs_symbol, Layout::u8(), lhs));
(stores, lhs_symbol, rhs_symbol, None) (stores, lhs_symbol, rhs_symbol, None)
} }
@ -1338,7 +1338,7 @@ fn test_to_equality<'a>(
Test::IsBit(test_bit) => { Test::IsBit(test_bit) => {
let lhs = Expr::Literal(Literal::Bool(test_bit)); let lhs = Expr::Literal(Literal::Bool(test_bit));
let lhs_symbol = env.unique_symbol(); let lhs_symbol = env.unique_symbol();
stores.push((lhs_symbol, Layout::Builtin(Builtin::Int1), lhs)); stores.push((lhs_symbol, Layout::Builtin(Builtin::Bool), lhs));
(stores, lhs_symbol, rhs_symbol, None) (stores, lhs_symbol, rhs_symbol, None)
} }
@ -1459,7 +1459,7 @@ fn compile_test_help<'a>(
cond = Stmt::Switch { cond = Stmt::Switch {
cond_symbol: test_symbol, cond_symbol: test_symbol,
cond_layout: Layout::Builtin(Builtin::Int1), cond_layout: Layout::Builtin(Builtin::Bool),
ret_layout, ret_layout,
branches, branches,
default_branch, default_branch,
@ -1478,7 +1478,7 @@ fn compile_test_help<'a>(
cond = Stmt::Let( cond = Stmt::Let(
test_symbol, test_symbol,
test, test,
Layout::Builtin(Builtin::Int1), Layout::Builtin(Builtin::Bool),
arena.alloc(cond), arena.alloc(cond),
); );
@ -1622,7 +1622,7 @@ fn decide_to_branching<'a>(
let decide = crate::ir::cond( let decide = crate::ir::cond(
env, env,
test_symbol, test_symbol,
Layout::Builtin(Builtin::Int1), Layout::Builtin(Builtin::Bool),
pass_expr, pass_expr,
fail_expr, fail_expr,
ret_layout, ret_layout,
@ -1631,7 +1631,7 @@ fn decide_to_branching<'a>(
// calculate the guard value // calculate the guard value
let param = Param { let param = Param {
symbol: test_symbol, symbol: test_symbol,
layout: Layout::Builtin(Builtin::Int1), layout: Layout::Builtin(Builtin::Bool),
borrow: false, borrow: false,
}; };

View file

@ -7,6 +7,7 @@ use crate::layout::{
}; };
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_can::expr::ClosureData; use roc_can::expr::ClosureData;
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap}; use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
use roc_module::ident::{ForeignSymbol, Lowercase, TagName}; use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
@ -1139,8 +1140,17 @@ impl<'a> BranchInfo<'a> {
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum ModifyRc { pub enum ModifyRc {
/// Increment a reference count
Inc(Symbol, u64), Inc(Symbol, u64),
/// Decrement a reference count
Dec(Symbol), Dec(Symbol),
/// A DecRef is a non-recursive reference count decrement
/// e.g. If we Dec a list of lists, then if the reference count of the outer list is one,
/// a Dec will recursively decrement all elements, then free the memory of the outer list.
/// A DecRef would just free the outer list.
/// That is dangerous because you may not free the elements, but in our Zig builtins,
/// sometimes we know we already dealt with the elements (e.g. by copying them all over
/// to a new list) and so we can just do a DecRef, which is much cheaper in such a case.
DecRef(Symbol), DecRef(Symbol),
} }
@ -2431,11 +2441,11 @@ fn specialize_external<'a>(
} }
ClosureRepresentation::Other(layout) => match layout { ClosureRepresentation::Other(layout) => match layout {
Layout::Builtin(Builtin::Int1) => { Layout::Builtin(Builtin::Bool) => {
// just ignore this value // just ignore this value
// IDEA don't pass this value in the future // IDEA don't pass this value in the future
} }
Layout::Builtin(Builtin::Int8) => { Layout::Builtin(Builtin::Int(IntWidth::U8)) => {
// just ignore this value // just ignore this value
// IDEA don't pass this value in the future // IDEA don't pass this value in the future
} }
@ -2888,20 +2898,21 @@ fn try_make_literal<'a>(
match can_expr { match can_expr {
Int(_, precision, _, int) => { Int(_, precision, _, int) => {
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *precision, false) { match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *precision, false) {
IntOrFloat::SignedIntType(_) | IntOrFloat::UnsignedIntType(_) => { IntOrFloat::Int(_) => Some(Literal::Int(*int)),
Some(Literal::Int(*int))
}
_ => unreachable!("unexpected float precision for integer"), _ => unreachable!("unexpected float precision for integer"),
} }
} }
Float(_, precision, float_str, float) => { Float(_, precision, float_str, float) => {
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *precision, true) { match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *precision, true) {
IntOrFloat::BinaryFloatType(_) => Some(Literal::Float(*float)), IntOrFloat::Float(_) => Some(Literal::Float(*float)),
IntOrFloat::DecimalFloatType => { IntOrFloat::DecimalFloatType => {
let dec = match RocDec::from_str(float_str) { let dec = match RocDec::from_str(float_str) {
Some(d) => d, Some(d) => d,
None => panic!("Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message", float_str), None => panic!(
r"Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message",
float_str
),
}; };
Some(Literal::Decimal(dec)) Some(Literal::Decimal(dec))
@ -2915,10 +2926,8 @@ fn try_make_literal<'a>(
Num(var, num_str, num) => { Num(var, num_str, num) => {
// first figure out what kind of number this is // first figure out what kind of number this is
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, false) { match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, false) {
IntOrFloat::SignedIntType(_) | IntOrFloat::UnsignedIntType(_) => { IntOrFloat::Int(_) => Some(Literal::Int((*num).into())),
Some(Literal::Int((*num).into())) IntOrFloat::Float(_) => Some(Literal::Float(*num as f64)),
}
IntOrFloat::BinaryFloatType(_) => Some(Literal::Float(*num as f64)),
IntOrFloat::DecimalFloatType => { IntOrFloat::DecimalFloatType => {
let dec = match RocDec::from_str(num_str) { let dec = match RocDec::from_str(num_str) {
Some(d) => d, Some(d) => d,
@ -2952,16 +2961,10 @@ pub fn with_hole<'a>(
match can_expr { match can_expr {
Int(_, precision, _, int) => { Int(_, precision, _, int) => {
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, precision, false) { match num_argument_to_int_or_float(env.subs, env.ptr_bytes, precision, false) {
IntOrFloat::SignedIntType(precision) => Stmt::Let( IntOrFloat::Int(precision) => Stmt::Let(
assigned, assigned,
Expr::Literal(Literal::Int(int)), Expr::Literal(Literal::Int(int)),
precision.as_layout(), Layout::Builtin(Builtin::Int(precision)),
hole,
),
IntOrFloat::UnsignedIntType(precision) => Stmt::Let(
assigned,
Expr::Literal(Literal::Int(int)),
precision.as_layout(),
hole, hole,
), ),
_ => unreachable!("unexpected float precision for integer"), _ => unreachable!("unexpected float precision for integer"),
@ -2970,10 +2973,10 @@ pub fn with_hole<'a>(
Float(_, precision, float_str, float) => { Float(_, precision, float_str, float) => {
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, precision, true) { match num_argument_to_int_or_float(env.subs, env.ptr_bytes, precision, true) {
IntOrFloat::BinaryFloatType(precision) => Stmt::Let( IntOrFloat::Float(precision) => Stmt::Let(
assigned, assigned,
Expr::Literal(Literal::Float(float)), Expr::Literal(Literal::Float(float)),
precision.as_layout(), Layout::Builtin(Builtin::Float(precision)),
hole, hole,
), ),
IntOrFloat::DecimalFloatType => { IntOrFloat::DecimalFloatType => {
@ -3002,22 +3005,16 @@ pub fn with_hole<'a>(
Num(var, num_str, num) => { Num(var, num_str, num) => {
// first figure out what kind of number this is // first figure out what kind of number this is
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, var, false) { match num_argument_to_int_or_float(env.subs, env.ptr_bytes, var, false) {
IntOrFloat::SignedIntType(precision) => Stmt::Let( IntOrFloat::Int(precision) => Stmt::Let(
assigned, assigned,
Expr::Literal(Literal::Int(num.into())), Expr::Literal(Literal::Int(num.into())),
precision.as_layout(), Layout::int_width(precision),
hole, hole,
), ),
IntOrFloat::UnsignedIntType(precision) => Stmt::Let( IntOrFloat::Float(precision) => Stmt::Let(
assigned,
Expr::Literal(Literal::Int(num.into())),
precision.as_layout(),
hole,
),
IntOrFloat::BinaryFloatType(precision) => Stmt::Let(
assigned, assigned,
Expr::Literal(Literal::Float(num as f64)), Expr::Literal(Literal::Float(num as f64)),
precision.as_layout(), Layout::float_width(precision),
hole, hole,
), ),
IntOrFloat::DecimalFloatType => { IntOrFloat::DecimalFloatType => {
@ -4402,7 +4399,7 @@ fn construct_closure_data<'a>(
Stmt::Let(assigned, expr, lambda_set_layout, hole) Stmt::Let(assigned, expr, lambda_set_layout, hole)
} }
ClosureRepresentation::Other(Layout::Builtin(Builtin::Int1)) => { ClosureRepresentation::Other(Layout::Builtin(Builtin::Bool)) => {
debug_assert_eq!(symbols.len(), 0); debug_assert_eq!(symbols.len(), 0);
debug_assert_eq!(lambda_set.set.len(), 2); debug_assert_eq!(lambda_set.set.len(), 2);
@ -4411,7 +4408,7 @@ fn construct_closure_data<'a>(
Stmt::Let(assigned, expr, lambda_set_layout, hole) Stmt::Let(assigned, expr, lambda_set_layout, hole)
} }
ClosureRepresentation::Other(Layout::Builtin(Builtin::Int8)) => { ClosureRepresentation::Other(Layout::Builtin(Builtin::Int(IntWidth::U8))) => {
debug_assert_eq!(symbols.len(), 0); debug_assert_eq!(symbols.len(), 0);
debug_assert!(lambda_set.set.len() > 2); debug_assert!(lambda_set.set.len() > 2);
@ -4468,7 +4465,7 @@ fn convert_tag_union<'a>(
BoolUnion { ttrue, .. } => Stmt::Let( BoolUnion { ttrue, .. } => Stmt::Let(
assigned, assigned,
Expr::Literal(Literal::Bool(tag_name == ttrue)), Expr::Literal(Literal::Bool(tag_name == ttrue)),
Layout::Builtin(Builtin::Int1), Layout::Builtin(Builtin::Bool),
hole, hole,
), ),
ByteUnion(tag_names) => { ByteUnion(tag_names) => {
@ -4478,7 +4475,7 @@ fn convert_tag_union<'a>(
Some(tag_id) => Stmt::Let( Some(tag_id) => Stmt::Let(
assigned, assigned,
Expr::Literal(Literal::Byte(tag_id as u8)), Expr::Literal(Literal::Byte(tag_id as u8)),
Layout::Builtin(Builtin::Int8), Layout::Builtin(Builtin::Int(IntWidth::U8)),
hole, hole,
), ),
None => Stmt::RuntimeError("tag must be in its own type"), None => Stmt::RuntimeError("tag must be in its own type"),
@ -4989,7 +4986,7 @@ pub fn from_can<'a>(
Expect(condition, rest) => { Expect(condition, rest) => {
let rest = from_can(env, variable, rest.value, procs, layout_cache); let rest = from_can(env, variable, rest.value, procs, layout_cache);
let bool_layout = Layout::Builtin(Builtin::Int1); let bool_layout = Layout::Builtin(Builtin::Bool);
let cond_symbol = env.unique_symbol(); let cond_symbol = env.unique_symbol();
let op = LowLevel::ExpectTrue; let op = LowLevel::ExpectTrue;
@ -7172,8 +7169,8 @@ fn call_specialized_proc<'a>(
pub enum Pattern<'a> { pub enum Pattern<'a> {
Identifier(Symbol), Identifier(Symbol),
Underscore, Underscore,
IntLiteral(i128, IntPrecision), IntLiteral(i128, IntWidth),
FloatLiteral(u64, FloatPrecision), FloatLiteral(u64, FloatWidth),
DecimalLiteral(RocDec), DecimalLiteral(RocDec),
BitLiteral { BitLiteral {
value: bool, value: bool,
@ -7253,9 +7250,7 @@ fn from_can_pattern_help<'a>(
Identifier(symbol) => Ok(Pattern::Identifier(*symbol)), Identifier(symbol) => Ok(Pattern::Identifier(*symbol)),
IntLiteral(var, _, int) => { IntLiteral(var, _, int) => {
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, false) { match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, false) {
IntOrFloat::SignedIntType(precision) | IntOrFloat::UnsignedIntType(precision) => { IntOrFloat::Int(precision) => Ok(Pattern::IntLiteral(*int as i128, precision)),
Ok(Pattern::IntLiteral(*int as i128, precision))
}
other => { other => {
panic!( panic!(
"Invalid precision for int pattern: {:?} has {:?}", "Invalid precision for int pattern: {:?} has {:?}",
@ -7267,10 +7262,10 @@ fn from_can_pattern_help<'a>(
FloatLiteral(var, float_str, float) => { FloatLiteral(var, float_str, float) => {
// TODO: Can I reuse num_argument_to_int_or_float here if I pass in true? // TODO: Can I reuse num_argument_to_int_or_float here if I pass in true?
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, true) { match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, true) {
IntOrFloat::SignedIntType(_) | IntOrFloat::UnsignedIntType(_) => { IntOrFloat::Int(_) => {
panic!("Invalid precision for float pattern {:?}", var) panic!("Invalid precision for float pattern {:?}", var)
} }
IntOrFloat::BinaryFloatType(precision) => { IntOrFloat::Float(precision) => {
Ok(Pattern::FloatLiteral(f64::to_bits(*float), precision)) Ok(Pattern::FloatLiteral(f64::to_bits(*float), precision))
} }
IntOrFloat::DecimalFloatType => { IntOrFloat::DecimalFloatType => {
@ -7297,15 +7292,8 @@ fn from_can_pattern_help<'a>(
} }
NumLiteral(var, num_str, num) => { NumLiteral(var, num_str, num) => {
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, false) { match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, false) {
IntOrFloat::SignedIntType(precision) => { IntOrFloat::Int(precision) => Ok(Pattern::IntLiteral(*num as i128, precision)),
Ok(Pattern::IntLiteral(*num as i128, precision)) IntOrFloat::Float(precision) => Ok(Pattern::FloatLiteral(*num as u64, precision)),
}
IntOrFloat::UnsignedIntType(precision) => {
Ok(Pattern::IntLiteral(*num as i128, precision))
}
IntOrFloat::BinaryFloatType(precision) => {
Ok(Pattern::FloatLiteral(*num as u64, precision))
}
IntOrFloat::DecimalFloatType => { IntOrFloat::DecimalFloatType => {
let dec = match RocDec::from_str(num_str) { let dec = match RocDec::from_str(num_str) {
Some(d) => d, Some(d) => d,
@ -7887,59 +7875,10 @@ fn from_can_record_destruct<'a>(
}) })
} }
#[derive(Debug, Clone, Copy, PartialEq, Hash)]
pub enum IntPrecision {
Usize,
I128,
I64,
I32,
I16,
I8,
}
impl IntPrecision {
pub fn as_layout(&self) -> Layout<'static> {
Layout::Builtin(self.as_builtin())
}
pub fn as_builtin(&self) -> Builtin<'static> {
use IntPrecision::*;
match self {
I128 => Builtin::Int128,
I64 => Builtin::Int64,
I32 => Builtin::Int32,
I16 => Builtin::Int16,
I8 => Builtin::Int8,
Usize => Builtin::Usize,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Hash)]
pub enum FloatPrecision {
F64,
F32,
}
impl FloatPrecision {
pub fn as_layout(&self) -> Layout<'static> {
Layout::Builtin(self.as_builtin())
}
pub fn as_builtin(&self) -> Builtin<'static> {
use FloatPrecision::*;
match self {
F64 => Builtin::Float64,
F32 => Builtin::Float32,
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub enum IntOrFloat { pub enum IntOrFloat {
SignedIntType(IntPrecision), Int(IntWidth),
UnsignedIntType(IntPrecision), Float(FloatWidth),
BinaryFloatType(FloatPrecision),
DecimalFloatType, DecimalFloatType,
} }
@ -7951,8 +7890,10 @@ pub fn num_argument_to_int_or_float(
known_to_be_float: bool, known_to_be_float: bool,
) -> IntOrFloat { ) -> IntOrFloat {
match subs.get_content_without_compacting(var) { match subs.get_content_without_compacting(var) {
Content::FlexVar(_) | Content::RigidVar(_) if known_to_be_float => IntOrFloat::BinaryFloatType(FloatPrecision::F64), Content::FlexVar(_) | Content::RigidVar(_) if known_to_be_float => {
Content::FlexVar(_) | Content::RigidVar(_) => IntOrFloat::SignedIntType(IntPrecision::I64), // We default (Num *) to I64 IntOrFloat::Float(FloatWidth::F64)
}
Content::FlexVar(_) | Content::RigidVar(_) => IntOrFloat::Int(IntWidth::I64), // We default (Num *) to I64
Content::Alias(Symbol::NUM_INTEGER, args, _) => { Content::Alias(Symbol::NUM_INTEGER, args, _) => {
debug_assert!(args.len() == 1); debug_assert!(args.len() == 1);
@ -7962,85 +7903,43 @@ pub fn num_argument_to_int_or_float(
num_argument_to_int_or_float(subs, ptr_bytes, var, false) num_argument_to_int_or_float(subs, ptr_bytes, var, false)
} }
Content::Alias(Symbol::NUM_I128, _, _) other @ Content::Alias(symbol, args, _) => {
| Content::Alias(Symbol::NUM_SIGNED128, _, _) if let Some(int_width) = IntWidth::try_from_symbol(*symbol) {
| Content::Alias(Symbol::NUM_AT_SIGNED128, _, _) => { return IntOrFloat::Int(int_width);
IntOrFloat::SignedIntType(IntPrecision::I128)
} }
Content::Alias(Symbol::NUM_INT, _, _)// We default Integer to I64
| Content::Alias(Symbol::NUM_I64, _, _) if let Some(float_width) = FloatWidth::try_from_symbol(*symbol) {
| Content::Alias(Symbol::NUM_SIGNED64, _, _) return IntOrFloat::Float(float_width);
| Content::Alias(Symbol::NUM_AT_SIGNED64, _, _) => {
IntOrFloat::SignedIntType(IntPrecision::I64)
} }
Content::Alias(Symbol::NUM_I32, _, _)
| Content::Alias(Symbol::NUM_SIGNED32, _, _) match *symbol {
| Content::Alias(Symbol::NUM_AT_SIGNED32, _, _) => { Symbol::NUM_FLOATINGPOINT => {
IntOrFloat::SignedIntType(IntPrecision::I32)
}
Content::Alias(Symbol::NUM_I16, _, _)
| Content::Alias(Symbol::NUM_SIGNED16, _, _)
| Content::Alias(Symbol::NUM_AT_SIGNED16, _, _) => {
IntOrFloat::SignedIntType(IntPrecision::I16)
}
Content::Alias(Symbol::NUM_I8, _, _)
| Content::Alias(Symbol::NUM_SIGNED8, _, _)
| Content::Alias(Symbol::NUM_AT_SIGNED8, _, _) => {
IntOrFloat::SignedIntType(IntPrecision::I8)
}
Content::Alias(Symbol::NUM_U128, _, _)
| Content::Alias(Symbol::NUM_UNSIGNED128, _, _)
| Content::Alias(Symbol::NUM_AT_UNSIGNED128, _, _) => {
IntOrFloat::UnsignedIntType(IntPrecision::I128)
}
Content::Alias(Symbol::NUM_U64, _, _)
| Content::Alias(Symbol::NUM_UNSIGNED64, _, _)
| Content::Alias(Symbol::NUM_AT_UNSIGNED64, _, _) => {
IntOrFloat::UnsignedIntType(IntPrecision::I64)
}
Content::Alias(Symbol::NUM_U32, _, _)
| Content::Alias(Symbol::NUM_UNSIGNED32, _, _)
| Content::Alias(Symbol::NUM_AT_UNSIGNED32, _, _) => {
IntOrFloat::UnsignedIntType(IntPrecision::I32)
}
Content::Alias(Symbol::NUM_U16, _, _)
| Content::Alias(Symbol::NUM_UNSIGNED16, _, _)
| Content::Alias(Symbol::NUM_AT_UNSIGNED16, _, _) => {
IntOrFloat::UnsignedIntType(IntPrecision::I16)
}
Content::Alias(Symbol::NUM_U8, _, _)
| Content::Alias(Symbol::NUM_UNSIGNED8, _, _)
| Content::Alias(Symbol::NUM_AT_UNSIGNED8, _, _) => {
IntOrFloat::UnsignedIntType(IntPrecision::I8)
}
Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _) => {
debug_assert!(args.len() == 1); debug_assert!(args.len() == 1);
// Recurse on the second argument // Recurse on the second argument
let var = subs[args.variables().into_iter().next().unwrap()]; let var = subs[args.variables().into_iter().next().unwrap()];
num_argument_to_int_or_float(subs, ptr_bytes, var, true) num_argument_to_int_or_float(subs, ptr_bytes, var, true)
} }
Content::Alias(Symbol::NUM_FLOAT, _, _) // We default FloatingPoint to F64
| Content::Alias(Symbol::NUM_F64, _, _)
| Content::Alias(Symbol::NUM_BINARY64, _, _)
| Content::Alias(Symbol::NUM_AT_BINARY64, _, _) => {
IntOrFloat::BinaryFloatType(FloatPrecision::F64)
}
Content::Alias(Symbol::NUM_DECIMAL, _, _)
| Content::Alias(Symbol::NUM_AT_DECIMAL, _, _) => {
IntOrFloat::DecimalFloatType
}
Content::Alias(Symbol::NUM_F32, _, _)
| Content::Alias(Symbol::NUM_BINARY32, _, _)
| Content::Alias(Symbol::NUM_AT_BINARY32, _, _) => {
IntOrFloat::BinaryFloatType(FloatPrecision::F32)
}
Content::Alias(Symbol::NUM_NAT, _, _)
| Content::Alias(Symbol::NUM_NATURAL, _, _)
| Content::Alias(Symbol::NUM_AT_NATURAL, _, _) => {
IntOrFloat::UnsignedIntType(IntPrecision::Usize)
Symbol::NUM_DECIMAL | Symbol::NUM_AT_DECIMAL => IntOrFloat::DecimalFloatType,
Symbol::NUM_NAT | Symbol::NUM_NATURAL | Symbol::NUM_AT_NATURAL => {
let int_width = match ptr_bytes {
4 => IntWidth::U32,
8 => IntWidth::U64,
_ => panic!("unsupported word size"),
};
IntOrFloat::Int(int_width)
} }
_ => panic!(
"Unrecognized Num type argument for var {:?} with Content: {:?}",
var, other
),
}
}
other => { other => {
panic!( panic!(
"Unrecognized Num type argument for var {:?} with Content: {:?}", "Unrecognized Num type argument for var {:?} with Content: {:?}",
@ -8122,14 +8021,14 @@ where
hole.clone() hole.clone()
} }
}, },
Layout::Builtin(Builtin::Int1) => { Layout::Builtin(Builtin::Bool) => {
let closure_tag_id_symbol = closure_data_symbol; let closure_tag_id_symbol = closure_data_symbol;
lowlevel_enum_lambda_set_to_switch( lowlevel_enum_lambda_set_to_switch(
env, env,
lambda_set.set, lambda_set.set,
closure_tag_id_symbol, closure_tag_id_symbol,
Layout::Builtin(Builtin::Int1), Layout::Builtin(Builtin::Bool),
closure_data_symbol, closure_data_symbol,
lambda_set.is_represented(), lambda_set.is_represented(),
to_lowlevel_call, to_lowlevel_call,
@ -8138,14 +8037,14 @@ where
hole, hole,
) )
} }
Layout::Builtin(Builtin::Int8) => { Layout::Builtin(Builtin::Int(IntWidth::U8)) => {
let closure_tag_id_symbol = closure_data_symbol; let closure_tag_id_symbol = closure_data_symbol;
lowlevel_enum_lambda_set_to_switch( lowlevel_enum_lambda_set_to_switch(
env, env,
lambda_set.set, lambda_set.set,
closure_tag_id_symbol, closure_tag_id_symbol,
Layout::Builtin(Builtin::Int8), Layout::Builtin(Builtin::Int(IntWidth::U8)),
closure_data_symbol, closure_data_symbol,
lambda_set.is_represented(), lambda_set.is_represented(),
to_lowlevel_call, to_lowlevel_call,
@ -8286,14 +8185,14 @@ fn match_on_lambda_set<'a>(
hole, hole,
) )
} }
Layout::Builtin(Builtin::Int1) => { Layout::Builtin(Builtin::Bool) => {
let closure_tag_id_symbol = closure_data_symbol; let closure_tag_id_symbol = closure_data_symbol;
enum_lambda_set_to_switch( enum_lambda_set_to_switch(
env, env,
lambda_set.set, lambda_set.set,
closure_tag_id_symbol, closure_tag_id_symbol,
Layout::Builtin(Builtin::Int1), Layout::Builtin(Builtin::Bool),
closure_data_symbol, closure_data_symbol,
argument_symbols, argument_symbols,
argument_layouts, argument_layouts,
@ -8302,14 +8201,14 @@ fn match_on_lambda_set<'a>(
hole, hole,
) )
} }
Layout::Builtin(Builtin::Int8) => { Layout::Builtin(Builtin::Int(IntWidth::U8)) => {
let closure_tag_id_symbol = closure_data_symbol; let closure_tag_id_symbol = closure_data_symbol;
enum_lambda_set_to_switch( enum_lambda_set_to_switch(
env, env,
lambda_set.set, lambda_set.set,
closure_tag_id_symbol, closure_tag_id_symbol,
Layout::Builtin(Builtin::Int8), Layout::Builtin(Builtin::Int(IntWidth::U8)),
closure_data_symbol, closure_data_symbol,
argument_symbols, argument_symbols,
argument_layouts, argument_layouts,
@ -8437,7 +8336,9 @@ fn union_lambda_set_branch_help<'a>(
hole: &'a Stmt<'a>, hole: &'a Stmt<'a>,
) -> Stmt<'a> { ) -> Stmt<'a> {
let (argument_layouts, argument_symbols) = match closure_data_layout { let (argument_layouts, argument_symbols) = match closure_data_layout {
Layout::Struct(&[]) | Layout::Builtin(Builtin::Int1) | Layout::Builtin(Builtin::Int8) => { Layout::Struct(&[])
| Layout::Builtin(Builtin::Bool)
| Layout::Builtin(Builtin::Int(IntWidth::U8)) => {
(argument_layouts_slice, argument_symbols_slice) (argument_layouts_slice, argument_symbols_slice)
} }
_ if lambda_set.member_does_not_need_closure_argument(function_symbol) => { _ if lambda_set.member_does_not_need_closure_argument(function_symbol) => {
@ -8562,7 +8463,9 @@ fn enum_lambda_set_branch<'a>(
let assigned = result_symbol; let assigned = result_symbol;
let (argument_layouts, argument_symbols) = match closure_data_layout { let (argument_layouts, argument_symbols) = match closure_data_layout {
Layout::Struct(&[]) | Layout::Builtin(Builtin::Int1) | Layout::Builtin(Builtin::Int8) => { Layout::Struct(&[])
| Layout::Builtin(Builtin::Bool)
| Layout::Builtin(Builtin::Int(IntWidth::U8)) => {
(argument_layouts_slice, argument_symbols_slice) (argument_layouts_slice, argument_symbols_slice)
} }
_ => { _ => {

View file

@ -1,6 +1,7 @@
use crate::ir::Parens; use crate::ir::Parens;
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_collections::all::{default_hasher, MutMap}; use roc_collections::all::{default_hasher, MutMap};
use roc_module::ident::{Lowercase, TagName}; use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::{Interns, Symbol}; use roc_module::symbol::{Interns, Symbol};
@ -24,9 +25,6 @@ pub type TagIdIntType = u16;
pub const MAX_ENUM_SIZE: usize = (std::mem::size_of::<TagIdIntType>() * 8) as usize; pub const MAX_ENUM_SIZE: usize = (std::mem::size_of::<TagIdIntType>() * 8) as usize;
const GENERATE_NULLABLE: bool = true; const GENERATE_NULLABLE: bool = true;
/// If a (Num *) gets translated to a Layout, this is the numeric type it defaults to.
const DEFAULT_NUM_BUILTIN: Builtin<'_> = Builtin::Int64;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum LayoutProblem { pub enum LayoutProblem {
UnresolvedTypeVar(Variable), UnresolvedTypeVar(Variable),
@ -61,60 +59,61 @@ impl<'a> RawFunctionLayout<'a> {
// Ints // Ints
Alias(Symbol::NUM_I128, args, _) => { Alias(Symbol::NUM_I128, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.is_empty());
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int128))) Ok(Self::ZeroArgumentThunk(Layout::i128()))
} }
Alias(Symbol::NUM_I64, args, _) => { Alias(Symbol::NUM_I64, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.is_empty());
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int64))) Ok(Self::ZeroArgumentThunk(Layout::i64()))
} }
Alias(Symbol::NUM_I32, args, _) => { Alias(Symbol::NUM_I32, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.is_empty());
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int32))) Ok(Self::ZeroArgumentThunk(Layout::i32()))
} }
Alias(Symbol::NUM_I16, args, _) => { Alias(Symbol::NUM_I16, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.is_empty());
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int16))) Ok(Self::ZeroArgumentThunk(Layout::i16()))
} }
Alias(Symbol::NUM_I8, args, _) => { Alias(Symbol::NUM_I8, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.is_empty());
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int8))) Ok(Self::ZeroArgumentThunk(Layout::i8()))
} }
// I think unsigned and signed use the same layout // I think unsigned and signed use the same layout
Alias(Symbol::NUM_U128, args, _) => { Alias(Symbol::NUM_U128, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.is_empty());
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int128))) Ok(Self::ZeroArgumentThunk(Layout::u128()))
} }
Alias(Symbol::NUM_U64, args, _) => { Alias(Symbol::NUM_U64, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.is_empty());
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int64))) Ok(Self::ZeroArgumentThunk(Layout::u64()))
} }
Alias(Symbol::NUM_U32, args, _) => { Alias(Symbol::NUM_U32, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.is_empty());
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int32))) Ok(Self::ZeroArgumentThunk(Layout::u32()))
} }
Alias(Symbol::NUM_U16, args, _) => { Alias(Symbol::NUM_U16, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.is_empty());
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int16))) Ok(Self::ZeroArgumentThunk(Layout::u16()))
} }
Alias(Symbol::NUM_U8, args, _) => { Alias(Symbol::NUM_U8, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.is_empty());
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Int8))) Ok(Self::ZeroArgumentThunk(Layout::u8()))
}
Alias(Symbol::NUM_NAT, args, _) => {
debug_assert!(args.is_empty());
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Usize)))
} }
// Floats // Floats
Alias(Symbol::NUM_F64, args, _) => { Alias(Symbol::NUM_F64, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.is_empty());
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Float64))) Ok(Self::ZeroArgumentThunk(Layout::f64()))
} }
Alias(Symbol::NUM_F32, args, _) => { Alias(Symbol::NUM_F32, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.is_empty());
Ok(Self::ZeroArgumentThunk(Layout::Builtin(Builtin::Float32))) Ok(Self::ZeroArgumentThunk(Layout::f32()))
}
// Nat
Alias(Symbol::NUM_NAT, args, _) => {
debug_assert!(args.is_empty());
Ok(Self::ZeroArgumentThunk(Layout::usize(env.ptr_bytes)))
} }
Alias(symbol, _, _) if symbol.is_builtin() => Ok(Self::ZeroArgumentThunk( Alias(symbol, _, _) if symbol.is_builtin() => Ok(Self::ZeroArgumentThunk(
@ -315,9 +314,9 @@ impl<'a> UnionLayout<'a> {
fn tag_id_builtin_help(union_size: usize) -> Builtin<'a> { fn tag_id_builtin_help(union_size: usize) -> Builtin<'a> {
if union_size <= u8::MAX as usize { if union_size <= u8::MAX as usize {
Builtin::Int8 Builtin::Int(IntWidth::U8)
} else if union_size <= u16::MAX as usize { } else if union_size <= u16::MAX as usize {
Builtin::Int16 Builtin::Int(IntWidth::U16)
} else { } else {
panic!("tag union is too big") panic!("tag union is too big")
} }
@ -332,7 +331,7 @@ impl<'a> UnionLayout<'a> {
// The quicksort-benchmarks version of Quicksort.roc segfaults when // The quicksort-benchmarks version of Quicksort.roc segfaults when
// this number is not I64. There must be some dependence on that fact // this number is not I64. There must be some dependence on that fact
// somewhere in the code, I have not found where that is yet... // somewhere in the code, I have not found where that is yet...
Builtin::Int64 Builtin::Int(IntWidth::U64)
} }
UnionLayout::Recursive(tags) => { UnionLayout::Recursive(tags) => {
let union_size = tags.len(); let union_size = tags.len();
@ -343,8 +342,8 @@ impl<'a> UnionLayout<'a> {
UnionLayout::NullableWrapped { other_tags, .. } => { UnionLayout::NullableWrapped { other_tags, .. } => {
Self::tag_id_builtin_help(other_tags.len() + 1) Self::tag_id_builtin_help(other_tags.len() + 1)
} }
UnionLayout::NonNullableUnwrapped(_) => Builtin::Int1, UnionLayout::NonNullableUnwrapped(_) => Builtin::Bool,
UnionLayout::NullableUnwrapped { .. } => Builtin::Int1, UnionLayout::NullableUnwrapped { .. } => Builtin::Bool,
} }
} }
@ -546,7 +545,8 @@ impl<'a> LambdaSet<'a> {
// singleton, so we pass no extra argument // singleton, so we pass no extra argument
argument_layouts argument_layouts
} }
Layout::Builtin(Builtin::Int1) | Layout::Builtin(Builtin::Int8) => { Layout::Builtin(Builtin::Bool)
| Layout::Builtin(Builtin::Int(IntWidth::I8 | IntWidth::U8)) => {
// we don't pass this along either // we don't pass this along either
argument_layouts argument_layouts
} }
@ -628,8 +628,8 @@ impl<'a> LambdaSet<'a> {
use UnionVariant::*; use UnionVariant::*;
match variant { match variant {
Never => Layout::Union(UnionLayout::NonRecursive(&[])), Never => Layout::Union(UnionLayout::NonRecursive(&[])),
BoolUnion { .. } => Layout::Builtin(Builtin::Int1), BoolUnion { .. } => Layout::bool(),
ByteUnion { .. } => Layout::Builtin(Builtin::Int8), ByteUnion { .. } => Layout::u8(),
Unit | UnitWithArguments => { Unit | UnitWithArguments => {
// no useful information to store // no useful information to store
Layout::Struct(&[]) Layout::Struct(&[])
@ -680,17 +680,10 @@ impl<'a> LambdaSet<'a> {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Builtin<'a> { pub enum Builtin<'a> {
Int128, Int(IntWidth),
Int64, Float(FloatWidth),
Int32, Bool,
Int16,
Int8,
Int1,
Usize,
Decimal, Decimal,
Float128,
Float64,
Float32,
Str, Str,
Dict(&'a Layout<'a>, &'a Layout<'a>), Dict(&'a Layout<'a>, &'a Layout<'a>),
Set(&'a Layout<'a>), Set(&'a Layout<'a>),
@ -756,67 +749,28 @@ impl<'a> Layout<'a> {
} }
Structure(flat_type) => layout_from_flat_type(env, flat_type), Structure(flat_type) => layout_from_flat_type(env, flat_type),
// Ints Alias(symbol, _args, actual_var) => {
Alias(Symbol::NUM_I128, args, _) => { if let Some(int_width) = IntWidth::try_from_symbol(symbol) {
debug_assert!(args.is_empty()); return Ok(Layout::Builtin(Builtin::Int(int_width)));
Ok(Layout::Builtin(Builtin::Int128))
}
Alias(Symbol::NUM_I64, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int64))
}
Alias(Symbol::NUM_I32, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int32))
}
Alias(Symbol::NUM_I16, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int16))
}
Alias(Symbol::NUM_I8, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int8))
} }
// I think unsigned and signed use the same layout if let Some(float_width) = FloatWidth::try_from_symbol(symbol) {
Alias(Symbol::NUM_U128, args, _) => { return Ok(Layout::Builtin(Builtin::Float(float_width)));
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int128))
}
Alias(Symbol::NUM_U64, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int64))
}
Alias(Symbol::NUM_U32, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int32))
}
Alias(Symbol::NUM_U16, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int16))
}
Alias(Symbol::NUM_U8, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int8))
} }
// Floats match symbol {
Alias(Symbol::NUM_F64, args, _) => { Symbol::NUM_DECIMAL | Symbol::NUM_AT_DECIMAL => {
debug_assert!(args.is_empty()); return Ok(Layout::Builtin(Builtin::Decimal))
Ok(Layout::Builtin(Builtin::Float64))
}
Alias(Symbol::NUM_F32, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Float32))
} }
// Nat Symbol::NUM_NAT | Symbol::NUM_NATURAL | Symbol::NUM_AT_NATURAL => {
Alias(Symbol::NUM_NAT, args, _) => { return Ok(Layout::usize(env.ptr_bytes))
debug_assert!(args.is_empty()); }
Ok(Layout::Builtin(Builtin::Usize))
_ => Self::from_var(env, actual_var),
}
} }
Alias(_, _, var) => Self::from_var(env, var),
Error => Err(LayoutProblem::Erroneous), Error => Err(LayoutProblem::Erroneous),
} }
} }
@ -1148,17 +1102,87 @@ impl<'a> LayoutCache<'a> {
// placeholder for the type ven_ena::unify::Snapshot<ven_ena::unify::InPlace<CachedVariable<'a>>> // placeholder for the type ven_ena::unify::Snapshot<ven_ena::unify::InPlace<CachedVariable<'a>>>
pub struct SnapshotKeyPlaceholder; pub struct SnapshotKeyPlaceholder;
impl<'a> Layout<'a> {
pub fn int_width(width: IntWidth) -> Layout<'a> {
Layout::Builtin(Builtin::Int(width))
}
pub fn float_width(width: FloatWidth) -> Layout<'a> {
Layout::Builtin(Builtin::Float(width))
}
pub fn f64() -> Layout<'a> {
Layout::Builtin(Builtin::Float(FloatWidth::F64))
}
pub fn f32() -> Layout<'a> {
Layout::Builtin(Builtin::Float(FloatWidth::F32))
}
pub fn usize(ptr_bytes: u32) -> Layout<'a> {
match ptr_bytes {
4 => Self::u32(),
8 => Self::u64(),
_ => panic!("width of usize {} not supported", ptr_bytes),
}
}
pub fn bool() -> Layout<'a> {
Layout::Builtin(Builtin::Bool)
}
pub const fn u8() -> Layout<'a> {
Layout::Builtin(Builtin::Int(IntWidth::U8))
}
pub fn u16() -> Layout<'a> {
Layout::Builtin(Builtin::Int(IntWidth::U16))
}
pub fn u32() -> Layout<'a> {
Layout::Builtin(Builtin::Int(IntWidth::U32))
}
pub fn u64() -> Layout<'a> {
Layout::Builtin(Builtin::Int(IntWidth::U64))
}
pub fn u128() -> Layout<'a> {
Layout::Builtin(Builtin::Int(IntWidth::U128))
}
pub fn i8() -> Layout<'a> {
Layout::Builtin(Builtin::Int(IntWidth::I8))
}
pub fn i16() -> Layout<'a> {
Layout::Builtin(Builtin::Int(IntWidth::I16))
}
pub fn i32() -> Layout<'a> {
Layout::Builtin(Builtin::Int(IntWidth::I32))
}
pub fn i64() -> Layout<'a> {
Layout::Builtin(Builtin::Int(IntWidth::I64))
}
pub fn i128() -> Layout<'a> {
Layout::Builtin(Builtin::Int(IntWidth::I128))
}
pub fn default_integer() -> Layout<'a> {
Layout::i64()
}
pub fn default_float() -> Layout<'a> {
Layout::f64()
}
}
impl<'a> Builtin<'a> { impl<'a> Builtin<'a> {
const I128_SIZE: u32 = std::mem::size_of::<i128>() as u32;
const I64_SIZE: u32 = std::mem::size_of::<i64>() as u32;
const I32_SIZE: u32 = std::mem::size_of::<i32>() as u32;
const I16_SIZE: u32 = std::mem::size_of::<i16>() as u32;
const I8_SIZE: u32 = std::mem::size_of::<i8>() as u32;
const I1_SIZE: u32 = std::mem::size_of::<bool>() as u32; const I1_SIZE: u32 = std::mem::size_of::<bool>() as u32;
const DECIMAL_SIZE: u32 = std::mem::size_of::<i128>() as u32; const DECIMAL_SIZE: u32 = std::mem::size_of::<i128>() as u32;
const F128_SIZE: u32 = 16;
const F64_SIZE: u32 = std::mem::size_of::<f64>() as u32;
const F32_SIZE: u32 = std::mem::size_of::<f32>() as u32;
/// Number of machine words in an empty one of these /// Number of machine words in an empty one of these
pub const STR_WORDS: u32 = 2; pub const STR_WORDS: u32 = 2;
@ -1177,17 +1201,10 @@ impl<'a> Builtin<'a> {
use Builtin::*; use Builtin::*;
match self { match self {
Int128 => Builtin::I128_SIZE, Int(int) => int.stack_size(),
Int64 => Builtin::I64_SIZE, Float(float) => float.stack_size(),
Int32 => Builtin::I32_SIZE, Bool => Builtin::I1_SIZE,
Int16 => Builtin::I16_SIZE,
Int8 => Builtin::I8_SIZE,
Int1 => Builtin::I1_SIZE,
Usize => pointer_size,
Decimal => Builtin::DECIMAL_SIZE, Decimal => Builtin::DECIMAL_SIZE,
Float128 => Builtin::F128_SIZE,
Float64 => Builtin::F64_SIZE,
Float32 => Builtin::F32_SIZE,
Str | EmptyStr => Builtin::STR_WORDS * pointer_size, Str | EmptyStr => Builtin::STR_WORDS * pointer_size,
Dict(_, _) | EmptyDict => Builtin::DICT_WORDS * pointer_size, Dict(_, _) | EmptyDict => Builtin::DICT_WORDS * pointer_size,
Set(_) | EmptySet => Builtin::SET_WORDS * pointer_size, Set(_) | EmptySet => Builtin::SET_WORDS * pointer_size,
@ -1203,17 +1220,10 @@ impl<'a> Builtin<'a> {
// since both of those are one pointer size, the alignment of that structure is a pointer // since both of those are one pointer size, the alignment of that structure is a pointer
// size // size
match self { match self {
Int128 => align_of::<i128>() as u32, Int(int_width) => int_width.alignment_bytes(),
Int64 => align_of::<i64>() as u32, Float(float_width) => float_width.alignment_bytes(),
Int32 => align_of::<i32>() as u32, Bool => align_of::<bool>() as u32,
Int16 => align_of::<i16>() as u32,
Int8 => align_of::<i8>() as u32,
Int1 => align_of::<bool>() as u32,
Usize => pointer_size,
Decimal => align_of::<i128>() as u32, Decimal => align_of::<i128>() as u32,
Float128 => align_of::<i128>() as u32,
Float64 => align_of::<f64>() as u32,
Float32 => align_of::<f32>() as u32,
Dict(_, _) | EmptyDict => pointer_size, Dict(_, _) | EmptyDict => pointer_size,
Set(_) | EmptySet => pointer_size, Set(_) | EmptySet => pointer_size,
// we often treat these as i128 (64-bit systems) // we often treat these as i128 (64-bit systems)
@ -1230,8 +1240,10 @@ impl<'a> Builtin<'a> {
use Builtin::*; use Builtin::*;
match self { match self {
Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Usize | Decimal | Float128 | Float64 Int(_) | Float(_) | Bool | Decimal | EmptyStr | EmptyDict | EmptyList | EmptySet => {
| Float32 | EmptyStr | EmptyDict | EmptyList | EmptySet => true, true
}
Str | Dict(_, _) | Set(_) | List(_) => false, Str | Dict(_, _) | Set(_) | List(_) => false,
} }
} }
@ -1241,8 +1253,9 @@ impl<'a> Builtin<'a> {
use Builtin::*; use Builtin::*;
match self { match self {
Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Usize | Decimal | Float128 | Float64 Int(_) | Float(_) | Bool | Decimal | EmptyStr | EmptyDict | EmptyList | EmptySet => {
| Float32 | EmptyStr | EmptyDict | EmptyList | EmptySet => false, false
}
List(_) => true, List(_) => true,
Str | Dict(_, _) | Set(_) => true, Str | Dict(_, _) | Set(_) => true,
@ -1258,17 +1271,35 @@ impl<'a> Builtin<'a> {
use Builtin::*; use Builtin::*;
match self { match self {
Int128 => alloc.text("Int128"), Int(int_width) => {
Int64 => alloc.text("Int64"), use IntWidth::*;
Int32 => alloc.text("Int32"),
Int16 => alloc.text("Int16"), match int_width {
Int8 => alloc.text("Int8"), I128 => alloc.text("I128"),
Int1 => alloc.text("Int1"), I64 => alloc.text("I64"),
Usize => alloc.text("Usize"), I32 => alloc.text("I32"),
I16 => alloc.text("I16"),
I8 => alloc.text("I8"),
U128 => alloc.text("U128"),
U64 => alloc.text("U64"),
U32 => alloc.text("U32"),
U16 => alloc.text("U16"),
U8 => alloc.text("U8"),
}
}
Float(float_width) => {
use FloatWidth::*;
match float_width {
F128 => alloc.text("Float128"),
F64 => alloc.text("Float64"),
F32 => alloc.text("Float32"),
}
}
Bool => alloc.text("Int1"),
Decimal => alloc.text("Decimal"), Decimal => alloc.text("Decimal"),
Float128 => alloc.text("Float128"),
Float64 => alloc.text("Float64"),
Float32 => alloc.text("Float32"),
EmptyStr => alloc.text("EmptyStr"), EmptyStr => alloc.text("EmptyStr"),
EmptyList => alloc.text("EmptyList"), EmptyList => alloc.text("EmptyList"),
@ -1292,17 +1323,9 @@ impl<'a> Builtin<'a> {
pub fn allocation_alignment_bytes(&self, pointer_size: u32) -> u32 { pub fn allocation_alignment_bytes(&self, pointer_size: u32) -> u32 {
let allocation = match self { let allocation = match self {
Builtin::Int128 Builtin::Int(_) | Builtin::Float(_) | Builtin::Bool | Builtin::Decimal => {
| Builtin::Int64 unreachable!("not heap-allocated")
| Builtin::Int32 }
| Builtin::Int16
| Builtin::Int8
| Builtin::Int1
| Builtin::Usize
| Builtin::Decimal
| Builtin::Float128
| Builtin::Float64
| Builtin::Float32 => unreachable!("not heap-allocated"),
Builtin::Str => pointer_size, Builtin::Str => pointer_size,
Builtin::Dict(k, v) => k Builtin::Dict(k, v) => k
.alignment_bytes(pointer_size) .alignment_bytes(pointer_size)
@ -1337,49 +1360,49 @@ fn layout_from_flat_type<'a>(
// Ints // Ints
Symbol::NUM_NAT => { Symbol::NUM_NAT => {
debug_assert_eq!(args.len(), 0); debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Usize)) Ok(Layout::usize(env.ptr_bytes))
} }
Symbol::NUM_I128 => { Symbol::NUM_I128 => {
debug_assert_eq!(args.len(), 0); debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int128)) Ok(Layout::i128())
} }
Symbol::NUM_I64 => { Symbol::NUM_I64 => {
debug_assert_eq!(args.len(), 0); debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int64)) Ok(Layout::i64())
} }
Symbol::NUM_I32 => { Symbol::NUM_I32 => {
debug_assert_eq!(args.len(), 0); debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int32)) Ok(Layout::i32())
} }
Symbol::NUM_I16 => { Symbol::NUM_I16 => {
debug_assert_eq!(args.len(), 0); debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int16)) Ok(Layout::i16())
} }
Symbol::NUM_I8 => { Symbol::NUM_I8 => {
debug_assert_eq!(args.len(), 0); debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int8)) Ok(Layout::i8())
} }
Symbol::NUM_U128 => { Symbol::NUM_U128 => {
debug_assert_eq!(args.len(), 0); debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int128)) Ok(Layout::u128())
} }
Symbol::NUM_U64 => { Symbol::NUM_U64 => {
debug_assert_eq!(args.len(), 0); debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int64)) Ok(Layout::u64())
} }
Symbol::NUM_U32 => { Symbol::NUM_U32 => {
debug_assert_eq!(args.len(), 0); debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int32)) Ok(Layout::u32())
} }
Symbol::NUM_U16 => { Symbol::NUM_U16 => {
debug_assert_eq!(args.len(), 0); debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int16)) Ok(Layout::u16())
} }
Symbol::NUM_U8 => { Symbol::NUM_U8 => {
debug_assert_eq!(args.len(), 0); debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int8)) Ok(Layout::u8())
} }
// Floats // Floats
@ -1389,11 +1412,11 @@ fn layout_from_flat_type<'a>(
} }
Symbol::NUM_F64 => { Symbol::NUM_F64 => {
debug_assert_eq!(args.len(), 0); debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Float64)) Ok(Layout::f64())
} }
Symbol::NUM_F32 => { Symbol::NUM_F32 => {
debug_assert_eq!(args.len(), 0); debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Float32)) Ok(Layout::f32())
} }
Symbol::NUM_NUM | Symbol::NUM_AT_NUM => { Symbol::NUM_NUM | Symbol::NUM_AT_NUM => {
@ -1555,9 +1578,7 @@ fn layout_from_flat_type<'a>(
Ok(Layout::Union(union_layout)) Ok(Layout::Union(union_layout))
} }
EmptyTagUnion => { EmptyTagUnion => Ok(Layout::Union(UnionLayout::NonRecursive(&[]))),
panic!("TODO make Layout for empty Tag Union");
}
Erroneous(_) => Err(LayoutProblem::Erroneous), Erroneous(_) => Err(LayoutProblem::Erroneous),
EmptyRecord => Ok(Layout::Struct(&[])), EmptyRecord => Ok(Layout::Struct(&[])),
} }
@ -1824,7 +1845,7 @@ fn union_sorted_tags_help_new<'a>(
match tag_name { match tag_name {
TagName::Private(Symbol::NUM_AT_NUM) => { TagName::Private(Symbol::NUM_AT_NUM) => {
let var = subs[arguments.into_iter().next().unwrap()]; let var = subs[arguments.into_iter().next().unwrap()];
layouts.push(unwrap_num_tag(subs, var).expect("invalid num layout")); layouts.push(unwrap_num_tag(subs, var, ptr_bytes).expect("invalid num layout"));
} }
_ => { _ => {
for var_index in arguments { for var_index in arguments {
@ -2027,7 +2048,9 @@ pub fn union_sorted_tags_help<'a>(
// special-case NUM_AT_NUM: if its argument is a FlexVar, make it Int // special-case NUM_AT_NUM: if its argument is a FlexVar, make it Int
match tag_name { match tag_name {
TagName::Private(Symbol::NUM_AT_NUM) => { TagName::Private(Symbol::NUM_AT_NUM) => {
layouts.push(unwrap_num_tag(subs, arguments[0]).expect("invalid num layout")); layouts.push(
unwrap_num_tag(subs, arguments[0], ptr_bytes).expect("invalid num layout"),
);
} }
_ => { _ => {
for var in arguments { for var in arguments {
@ -2239,7 +2262,7 @@ fn layout_from_newtype<'a>(
let tag_name = &subs[tag_name_index]; let tag_name = &subs[tag_name_index];
if tag_name == &TagName::Private(Symbol::NUM_AT_NUM) { if tag_name == &TagName::Private(Symbol::NUM_AT_NUM) {
unwrap_num_tag(subs, var).expect("invalid Num argument") unwrap_num_tag(subs, var, ptr_bytes).expect("invalid Num argument")
} else { } else {
let mut env = Env { let mut env = Env {
arena, arena,
@ -2286,7 +2309,7 @@ fn layout_from_tag_union<'a>(
let var_index = arguments.into_iter().next().unwrap(); let var_index = arguments.into_iter().next().unwrap();
let var = subs[var_index]; let var = subs[var_index];
unwrap_num_tag(subs, var).expect("invalid Num argument") unwrap_num_tag(subs, var, ptr_bytes).expect("invalid Num argument")
} }
_ => { _ => {
let opt_rec_var = None; let opt_rec_var = None;
@ -2295,8 +2318,8 @@ fn layout_from_tag_union<'a>(
match variant { match variant {
Never => Layout::Union(UnionLayout::NonRecursive(&[])), Never => Layout::Union(UnionLayout::NonRecursive(&[])),
Unit | UnitWithArguments => Layout::Struct(&[]), Unit | UnitWithArguments => Layout::Struct(&[]),
BoolUnion { .. } => Layout::Builtin(Builtin::Int1), BoolUnion { .. } => Layout::bool(),
ByteUnion(_) => Layout::Builtin(Builtin::Int8), ByteUnion(_) => Layout::u8(),
Newtype { Newtype {
arguments: field_layouts, arguments: field_layouts,
.. ..
@ -2389,6 +2412,8 @@ fn layout_from_num_content<'a>(content: &Content) -> Result<Layout<'a>, LayoutPr
use roc_types::subs::Content::*; use roc_types::subs::Content::*;
use roc_types::subs::FlatType::*; use roc_types::subs::FlatType::*;
let ptr_bytes = 8;
match content { match content {
RecursionVar { .. } => panic!("recursion var in num"), RecursionVar { .. } => panic!("recursion var in num"),
FlexVar(_) | RigidVar(_) => { FlexVar(_) | RigidVar(_) => {
@ -2396,30 +2421,32 @@ fn layout_from_num_content<'a>(content: &Content) -> Result<Layout<'a>, LayoutPr
// type variable, then assume it's a 64-bit integer. // type variable, then assume it's a 64-bit integer.
// //
// (e.g. for (5 + 5) assume both 5s are 64-bit integers.) // (e.g. for (5 + 5) assume both 5s are 64-bit integers.)
Ok(Layout::Builtin(DEFAULT_NUM_BUILTIN)) Ok(Layout::default_integer())
} }
Structure(Apply(symbol, args)) => match *symbol { Structure(Apply(symbol, args)) => match *symbol {
// Ints // Ints
Symbol::NUM_NAT => Ok(Layout::Builtin(Builtin::Usize)), Symbol::NUM_NAT => Ok(Layout::usize(ptr_bytes)),
Symbol::NUM_INTEGER => Ok(Layout::Builtin(Builtin::Int64)), Symbol::NUM_INTEGER => Ok(Layout::i64()),
Symbol::NUM_I128 => Ok(Layout::Builtin(Builtin::Int128)), Symbol::NUM_I128 => Ok(Layout::i128()),
Symbol::NUM_I64 => Ok(Layout::Builtin(Builtin::Int64)), Symbol::NUM_I64 => Ok(Layout::i64()),
Symbol::NUM_I32 => Ok(Layout::Builtin(Builtin::Int32)), Symbol::NUM_I32 => Ok(Layout::i32()),
Symbol::NUM_I16 => Ok(Layout::Builtin(Builtin::Int16)), Symbol::NUM_I16 => Ok(Layout::i16()),
Symbol::NUM_I8 => Ok(Layout::Builtin(Builtin::Int8)), Symbol::NUM_I8 => Ok(Layout::i8()),
Symbol::NUM_U128 => Ok(Layout::Builtin(Builtin::Int128)), Symbol::NUM_U128 => Ok(Layout::u128()),
Symbol::NUM_U64 => Ok(Layout::Builtin(Builtin::Int64)), Symbol::NUM_U64 => Ok(Layout::u64()),
Symbol::NUM_U32 => Ok(Layout::Builtin(Builtin::Int32)), Symbol::NUM_U32 => Ok(Layout::u32()),
Symbol::NUM_U16 => Ok(Layout::Builtin(Builtin::Int16)), Symbol::NUM_U16 => Ok(Layout::u16()),
Symbol::NUM_U8 => Ok(Layout::Builtin(Builtin::Int8)), Symbol::NUM_U8 => Ok(Layout::u8()),
// Floats // Floats
Symbol::NUM_FLOATINGPOINT => Ok(Layout::Builtin(Builtin::Float64)), Symbol::NUM_FLOATINGPOINT => Ok(Layout::f64()),
Symbol::NUM_F64 => Ok(Layout::f64()),
Symbol::NUM_F32 => Ok(Layout::f32()),
// Dec
Symbol::NUM_DEC => Ok(Layout::Builtin(Builtin::Decimal)), Symbol::NUM_DEC => Ok(Layout::Builtin(Builtin::Decimal)),
Symbol::NUM_F64 => Ok(Layout::Builtin(Builtin::Float64)),
Symbol::NUM_F32 => Ok(Layout::Builtin(Builtin::Float32)),
_ => { _ => {
panic!( panic!(
@ -2438,7 +2465,11 @@ fn layout_from_num_content<'a>(content: &Content) -> Result<Layout<'a>, LayoutPr
} }
} }
fn unwrap_num_tag<'a>(subs: &Subs, var: Variable) -> Result<Layout<'a>, LayoutProblem> { fn unwrap_num_tag<'a>(
subs: &Subs,
var: Variable,
ptr_bytes: u32,
) -> Result<Layout<'a>, LayoutProblem> {
match subs.get_content_without_compacting(var) { match subs.get_content_without_compacting(var) {
Content::Alias(Symbol::NUM_INTEGER, args, _) => { Content::Alias(Symbol::NUM_INTEGER, args, _) => {
debug_assert!(args.len() == 1); debug_assert!(args.len() == 1);
@ -2451,26 +2482,27 @@ fn unwrap_num_tag<'a>(subs: &Subs, var: Variable) -> Result<Layout<'a>, LayoutPr
Content::Alias(symbol, args, _) => { Content::Alias(symbol, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.is_empty());
let builtin = match *symbol { let layout = match *symbol {
Symbol::NUM_SIGNED128 => Builtin::Int128, Symbol::NUM_SIGNED128 => Layout::i128(),
Symbol::NUM_SIGNED64 => Builtin::Int64, Symbol::NUM_SIGNED64 => Layout::i64(),
Symbol::NUM_SIGNED32 => Builtin::Int32, Symbol::NUM_SIGNED32 => Layout::i32(),
Symbol::NUM_SIGNED16 => Builtin::Int16, Symbol::NUM_SIGNED16 => Layout::i16(),
Symbol::NUM_SIGNED8 => Builtin::Int8, Symbol::NUM_SIGNED8 => Layout::i8(),
Symbol::NUM_UNSIGNED128 => Builtin::Int128, Symbol::NUM_UNSIGNED128 => Layout::u128(),
Symbol::NUM_UNSIGNED64 => Builtin::Int64, Symbol::NUM_UNSIGNED64 => Layout::u64(),
Symbol::NUM_UNSIGNED32 => Builtin::Int32, Symbol::NUM_UNSIGNED32 => Layout::u32(),
Symbol::NUM_UNSIGNED16 => Builtin::Int16, Symbol::NUM_UNSIGNED16 => Layout::u16(),
Symbol::NUM_UNSIGNED8 => Builtin::Int8, Symbol::NUM_UNSIGNED8 => Layout::u8(),
Symbol::NUM_NATURAL => Builtin::Usize, Symbol::NUM_NATURAL => Layout::usize(ptr_bytes),
_ => unreachable!("not a valid int variant: {:?} {:?}", symbol, args), _ => unreachable!("not a valid int variant: {:?} {:?}", symbol, args),
}; };
Ok(Layout::Builtin(builtin)) Ok(layout)
} }
Content::FlexVar(_) | Content::RigidVar(_) => { Content::FlexVar(_) | Content::RigidVar(_) => {
// default to i64 // default to i64
Ok(Layout::Builtin(Builtin::Int64)) Ok(Layout::i64())
} }
_ => unreachable!("not a valid int variant: {:?}", precision), _ => unreachable!("not a valid int variant: {:?}", precision),
} }
@ -2486,12 +2518,12 @@ fn unwrap_num_tag<'a>(subs: &Subs, var: Variable) -> Result<Layout<'a>, LayoutPr
Content::Alias(Symbol::NUM_BINARY32, args, _) => { Content::Alias(Symbol::NUM_BINARY32, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Float32)) Ok(Layout::f32())
} }
Content::Alias(Symbol::NUM_BINARY64, args, _) => { Content::Alias(Symbol::NUM_BINARY64, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Float64)) Ok(Layout::f64())
} }
Content::Alias(Symbol::NUM_DECIMAL, args, _) => { Content::Alias(Symbol::NUM_DECIMAL, args, _) => {
debug_assert!(args.is_empty()); debug_assert!(args.is_empty());
@ -2500,14 +2532,14 @@ fn unwrap_num_tag<'a>(subs: &Subs, var: Variable) -> Result<Layout<'a>, LayoutPr
} }
Content::FlexVar(_) | Content::RigidVar(_) => { Content::FlexVar(_) | Content::RigidVar(_) => {
// default to f64 // default to f64
Ok(Layout::Builtin(Builtin::Float64)) Ok(Layout::f64())
} }
_ => unreachable!("not a valid float variant: {:?}", precision), _ => unreachable!("not a valid float variant: {:?}", precision),
} }
} }
Content::FlexVar(_) | Content::RigidVar(_) => { Content::FlexVar(_) | Content::RigidVar(_) => {
// If this was still a (Num *) then default to compiling it to i64 // If this was still a (Num *) then default to compiling it to i64
Ok(Layout::Builtin(DEFAULT_NUM_BUILTIN)) Ok(Layout::default_integer())
} }
other => { other => {
todo!("TODO non structure Num.@Num flat_type {:?}", other); todo!("TODO non structure Num.@Num flat_type {:?}", other);

View file

@ -686,6 +686,10 @@ fn type_to_variable<'a>(
register(subs, rank, pools, content) register(subs, rank, pools, content)
} }
Record(fields, ext) => { Record(fields, ext) => {
// An empty fields is inefficient (but would be correct)
// If hit, try to turn the value into an EmptyRecord in canonicalization
debug_assert!(!fields.is_empty() || !ext.is_empty_record());
let mut field_vars = Vec::with_capacity_in(fields.len(), arena); let mut field_vars = Vec::with_capacity_in(fields.len(), arena);
for (field, field_type) in fields { for (field, field_type) in fields {
@ -714,6 +718,10 @@ fn type_to_variable<'a>(
register(subs, rank, pools, content) register(subs, rank, pools, content)
} }
TagUnion(tags, ext) => { TagUnion(tags, ext) => {
// An empty tags is inefficient (but would be correct)
// If hit, try to turn the value into an EmptyTagUnion in canonicalization
debug_assert!(!tags.is_empty() || !ext.is_empty_tag_union());
let (union_tags, ext) = type_to_union_tags(subs, rank, pools, arena, tags, ext); let (union_tags, ext) = type_to_union_tags(subs, rank, pools, arena, tags, ext);
let content = Content::Structure(FlatType::TagUnion(union_tags, ext)); let content = Content::Structure(FlatType::TagUnion(union_tags, ext));
@ -742,6 +750,10 @@ fn type_to_variable<'a>(
register(subs, rank, pools, content) register(subs, rank, pools, content)
} }
RecursiveTagUnion(rec_var, tags, ext) => { RecursiveTagUnion(rec_var, tags, ext) => {
// An empty tags is inefficient (but would be correct)
// If hit, try to turn the value into an EmptyTagUnion in canonicalization
debug_assert!(!tags.is_empty() || !ext.is_empty_tag_union());
let (union_tags, ext) = type_to_union_tags(subs, rank, pools, arena, tags, ext); let (union_tags, ext) = type_to_union_tags(subs, rank, pools, arena, tags, ext);
let content = let content =
Content::Structure(FlatType::RecursiveTagUnion(*rec_var, union_tags, ext)); Content::Structure(FlatType::RecursiveTagUnion(*rec_var, union_tags, ext));

View file

@ -4604,4 +4604,117 @@ mod solve_expr {
"RBTree {}", "RBTree {}",
); );
} }
#[test]
fn inference_var_inside_arrow() {
infer_eq_without_problem(
indoc!(
r#"
id : _ -> _
id = \x -> x
id
"#
),
"a -> a",
)
}
#[test]
fn inference_var_inside_ctor() {
infer_eq_without_problem(
indoc!(
r#"
canIGo : _ -> Result _ _
canIGo = \color ->
when color is
"green" -> Ok "go!"
"yellow" -> Err (SlowIt "whoa, let's slow down!")
"red" -> Err (StopIt "absolutely not")
_ -> Err (UnknownColor "this is a weird stoplight")
canIGo
"#
),
"Str -> Result Str [ SlowIt Str, StopIt Str, UnknownColor Str ]*",
)
}
#[test]
fn inference_var_inside_ctor_linked() {
infer_eq_without_problem(
indoc!(
r#"
swapRcd: {x: _, y: _} -> {x: _, y: _}
swapRcd = \{x, y} -> {x: y, y: x}
swapRcd
"#
),
"{ x : a, y : b } -> { x : b, y : a }",
)
}
#[test]
fn inference_var_link_with_rigid() {
infer_eq_without_problem(
indoc!(
r#"
swapRcd: {x: tx, y: ty} -> {x: _, y: _}
swapRcd = \{x, y} -> {x: y, y: x}
swapRcd
"#
),
"{ x : tx, y : ty } -> { x : ty, y : tx }",
)
}
#[test]
fn inference_var_inside_tag_ctor() {
infer_eq_without_problem(
indoc!(
r#"
badComics: Bool -> [ CowTools _, Thagomizer _ ]
badComics = \c ->
when c is
True -> CowTools "The Far Side"
False -> Thagomizer "The Far Side"
badComics
"#
),
"Bool -> [ CowTools Str, Thagomizer Str ]",
)
}
#[test]
fn inference_var_tag_union_ext() {
// TODO: we should really be inferring [ Blue, Orange ]a -> [ Lavender, Peach ]a here.
// See https://github.com/rtfeldman/roc/issues/2053
infer_eq_without_problem(
indoc!(
r#"
pastelize: _ -> [ Lavender, Peach ]_
pastelize = \color ->
when color is
Blue -> Lavender
Orange -> Peach
col -> col
pastelize
"#
),
"[ Blue, Lavender, Orange, Peach ]a -> [ Blue, Lavender, Orange, Peach ]a",
)
}
#[test]
fn inference_var_rcd_union_ext() {
infer_eq_without_problem(
indoc!(
r#"
setRocEmail : _ -> { name: Str, email: Str }_
setRocEmail = \person ->
{ person & email: "\(person.name)@roclang.com" }
setRocEmail
"#
),
"{ email : Str, name : Str }a -> { email : Str, name : Str }a",
)
}
} }

View file

@ -358,7 +358,7 @@ fn u8_hex_int_alias() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn dec_float_alias() { fn dec_float_alias() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(

View file

@ -16,11 +16,10 @@ use roc_std::{RocList, RocStr};
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm"))]
fn width_and_alignment_u8_u8() { fn width_and_alignment_u8_u8() {
use roc_mono::layout::Builtin;
use roc_mono::layout::Layout; use roc_mono::layout::Layout;
use roc_mono::layout::UnionLayout; use roc_mono::layout::UnionLayout;
let t = &[Layout::Builtin(Builtin::Int8)] as &[_]; let t = &[Layout::u8()] as &[_];
let tt = [t, t]; let tt = [t, t];
let layout = Layout::Union(UnionLayout::NonRecursive(&tt)); let layout = Layout::Union(UnionLayout::NonRecursive(&tt));

View file

@ -5,6 +5,9 @@ use roc_can::builtins::builtin_defs_map;
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use tempfile::tempdir; use tempfile::tempdir;
#[allow(unused_imports)]
use roc_mono::ir::PRETTY_PRINT_IR_SYMBOLS;
#[allow(dead_code)] #[allow(dead_code)]
fn promote_expr_to_module(src: &str) -> String { fn promote_expr_to_module(src: &str) -> String {
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n"); let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
@ -77,7 +80,14 @@ pub fn helper(
// while you're working on the dev backend! // while you're working on the dev backend!
{ {
// println!("=========== Procedures =========="); // println!("=========== Procedures ==========");
// println!("{:?}", procedures); // if PRETTY_PRINT_IR_SYMBOLS {
// println!("");
// for proc in procedures.values() {
// println!("{}", proc.to_pretty(200));
// }
// } else {
// println!("{:?}", procedures.values());
// }
// println!("=================================\n"); // println!("=================================\n");
// println!("=========== Interns =========="); // println!("=========== Interns ==========");

View file

@ -17,6 +17,9 @@ use roc_gen_wasm::wasm_module::{
}; };
use roc_gen_wasm::MEMORY_NAME; use roc_gen_wasm::MEMORY_NAME;
#[allow(unused_imports)]
use roc_mono::ir::PRETTY_PRINT_IR_SYMBOLS;
const TEST_WRAPPER_NAME: &str = "test_wrapper"; const TEST_WRAPPER_NAME: &str = "test_wrapper";
std::thread_local! { std::thread_local! {
@ -60,6 +63,7 @@ pub fn helper_wasm<'a, T: Wasm32TestResult>(
} }
let exposed_types = MutMap::default(); let exposed_types = MutMap::default();
let ptr_bytes = 4;
let loaded = roc_load::file::load_and_monomorphize_from_str( let loaded = roc_load::file::load_and_monomorphize_from_str(
arena, arena,
filename, filename,
@ -67,7 +71,7 @@ pub fn helper_wasm<'a, T: Wasm32TestResult>(
stdlib, stdlib,
src_dir, src_dir,
exposed_types, exposed_types,
8, ptr_bytes,
builtin_defs_map, builtin_defs_map,
); );
@ -83,9 +87,16 @@ pub fn helper_wasm<'a, T: Wasm32TestResult>(
// You can comment and uncomment this block out to get more useful information // You can comment and uncomment this block out to get more useful information
// while you're working on the wasm backend! // while you're working on the wasm backend!
// { {
// println!("=========== Procedures =========="); // println!("=========== Procedures ==========");
// println!("{:?}", procedures); // if PRETTY_PRINT_IR_SYMBOLS {
// println!("");
// for proc in procedures.values() {
// println!("{}", proc.to_pretty(200));
// }
// } else {
// println!("{:?}", procedures.values());
// }
// println!("=================================\n"); // println!("=================================\n");
// println!("=========== Interns =========="); // println!("=========== Interns ==========");
@ -95,7 +106,7 @@ pub fn helper_wasm<'a, T: Wasm32TestResult>(
// println!("=========== Exposed =========="); // println!("=========== Exposed ==========");
// println!("{:?}", exposed_to_host); // println!("{:?}", exposed_to_host);
// println!("=================================\n"); // println!("=================================\n");
// } }
debug_assert_eq!(exposed_to_host.len(), 1); debug_assert_eq!(exposed_to_host.len(), 1);

View file

@ -1057,12 +1057,14 @@ impl Subs {
} }
} }
#[inline(always)]
pub fn fresh(&mut self, value: Descriptor) -> Variable { pub fn fresh(&mut self, value: Descriptor) -> Variable {
self.utable.new_key(value) self.utable.new_key(value)
} }
#[inline(always)]
pub fn fresh_unnamed_flex_var(&mut self) -> Variable { pub fn fresh_unnamed_flex_var(&mut self) -> Variable {
self.fresh(unnamed_flex_var().into()) self.fresh(Descriptor::from(unnamed_flex_var()))
} }
pub fn rigid_var(&mut self, var: Variable, name: Lowercase) { pub fn rigid_var(&mut self, var: Variable, name: Lowercase) {
@ -1091,6 +1093,7 @@ impl Subs {
&self.utable.probe_value_ref(key).value &self.utable.probe_value_ref(key).value
} }
#[inline(always)]
pub fn get_ref_mut(&mut self, key: Variable) -> &mut Descriptor { pub fn get_ref_mut(&mut self, key: Variable) -> &mut Descriptor {
&mut self.utable.probe_value_ref_mut(key).value &mut self.utable.probe_value_ref_mut(key).value
} }
@ -1109,6 +1112,7 @@ impl Subs {
(desc.rank, desc.mark) (desc.rank, desc.mark)
} }
#[inline(always)]
pub fn get_without_compacting(&self, key: Variable) -> Descriptor { pub fn get_without_compacting(&self, key: Variable) -> Descriptor {
self.utable.probe_value_without_compacting(key) self.utable.probe_value_without_compacting(key)
} }
@ -1125,6 +1129,7 @@ impl Subs {
self.utable.get_root_key_without_compacting(key) self.utable.get_root_key_without_compacting(key)
} }
#[inline(always)]
pub fn set(&mut self, key: Variable, r_value: Descriptor) { pub fn set(&mut self, key: Variable, r_value: Descriptor) {
let l_key = self.utable.get_root_key(key); let l_key = self.utable.get_root_key(key);
@ -1261,7 +1266,7 @@ fn flex_var_descriptor() -> Descriptor {
} }
#[inline(always)] #[inline(always)]
fn unnamed_flex_var() -> Content { const fn unnamed_flex_var() -> Content {
Content::FlexVar(None) Content::FlexVar(None)
} }

View file

@ -436,6 +436,14 @@ impl Type {
matches!(self, Type::RecursiveTagUnion(_, _, _)) matches!(self, Type::RecursiveTagUnion(_, _, _))
} }
pub fn is_empty_tag_union(&self) -> bool {
matches!(self, Type::EmptyTagUnion)
}
pub fn is_empty_record(&self) -> bool {
matches!(self, Type::EmptyRec)
}
pub fn variables(&self) -> ImSet<Variable> { pub fn variables(&self) -> ImSet<Variable> {
let mut result = ImSet::default(); let mut result = ImSet::default();
variables_help(self, &mut result); variables_help(self, &mut result);

View file

@ -6667,4 +6667,102 @@ I need all branches in an `if` to have the same type!
), ),
) )
} }
#[test]
fn inference_var_not_enough_in_alias() {
report_problem_as(
indoc!(
r#"
canIGo : _ -> Result _
canIGo = \color ->
when color is
"green" -> Ok "go!"
"yellow" -> Err (SlowIt "whoa, let's slow down!")
"red" -> Err (StopIt "absolutely not")
_ -> Err (UnknownColor "this is a weird stoplight")
canIGo
"#
),
indoc!(
r#"
TOO FEW TYPE ARGUMENTS
The `Result` alias expects 2 type arguments, but it got 1 instead:
1 canIGo : _ -> Result _
^^^^^^^^
Are there missing parentheses?
"#
),
)
}
#[test]
fn inference_var_too_many_in_alias() {
report_problem_as(
indoc!(
r#"
canIGo : _ -> Result _ _ _
canIGo = \color ->
when color is
"green" -> Ok "go!"
"yellow" -> Err (SlowIt "whoa, let's slow down!")
"red" -> Err (StopIt "absolutely not")
_ -> Err (UnknownColor "this is a weird stoplight")
canIGo
"#
),
indoc!(
r#"
TOO MANY TYPE ARGUMENTS
The `Result` alias expects 2 type arguments, but it got 3 instead:
1 canIGo : _ -> Result _ _ _
^^^^^^^^^^^^
Are there missing parentheses?
"#
),
)
}
#[test]
fn inference_var_conflict_in_rigid_links() {
report_problem_as(
indoc!(
r#"
f : a -> (_ -> b)
f = \x -> \y -> if x == y then x else y
f
"#
),
// TODO: We should tell the user that we inferred `_` as `a`
indoc!(
r#"
TYPE MISMATCH
Something is off with the body of the `f` definition:
1 f : a -> (_ -> b)
2 f = \x -> \y -> if x == y then x else y
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The body is an anonymous function of type:
a -> a
But the type annotation on `f` says it should be:
a -> b
Tip: Your type annotation uses `a` and `b` as separate type variables.
Your code seems to be saying they are the same though. Maybe they
should be the same your type annotation? Maybe your code uses them in
a weird way?
"#
),
)
}
} }

View file

@ -186,6 +186,7 @@ impl<K: UnifyKey> VarValue<K> {
self.value = value; self.value = value;
} }
#[inline(always)]
fn parent(&self, self_key: K) -> Option<K> { fn parent(&self, self_key: K) -> Option<K> {
self.if_not_self(self.parent, self_key) self.if_not_self(self.parent, self_key)
} }
@ -194,6 +195,7 @@ impl<K: UnifyKey> VarValue<K> {
self.parent self.parent
} }
#[inline(always)]
fn if_not_self(&self, key: K, self_key: K) -> Option<K> { fn if_not_self(&self, key: K, self_key: K) -> Option<K> {
if key == self_key { if key == self_key {
None None
@ -311,11 +313,12 @@ impl<S: UnificationStore> UnificationTable<S> {
} }
} }
pub fn get_root_key_without_compacting(&self, vid: S::Key) -> S::Key { pub fn get_root_key_without_compacting(&self, mut vid: S::Key) -> S::Key {
match self.value(vid).parent(vid) { while let Some(redirect) = self.value(vid).parent(vid) {
None => vid, vid = redirect;
Some(redirect) => self.get_root_key_without_compacting(redirect),
} }
vid
} }
pub fn is_redirect(&self, vid: S::Key) -> bool { pub fn is_redirect(&self, vid: S::Key) -> bool {
@ -437,6 +440,7 @@ where
/// Returns the current value for the given key. If the key has /// Returns the current value for the given key. If the key has
/// been union'd, this will give the value from the current root. /// been union'd, this will give the value from the current root.
#[inline(always)]
pub fn probe_value<K1>(&mut self, id: K1) -> V pub fn probe_value<K1>(&mut self, id: K1) -> V
where where
K1: Into<K>, K1: Into<K>,
@ -448,6 +452,7 @@ where
/// Returns the current value for the given key. If the key has /// Returns the current value for the given key. If the key has
/// been union'd, this will give the value from the current root. /// been union'd, this will give the value from the current root.
#[inline(always)]
pub fn probe_value_ref<K1>(&self, id: K1) -> &VarValue<K> pub fn probe_value_ref<K1>(&self, id: K1) -> &VarValue<K>
where where
K1: Into<K>, K1: Into<K>,
@ -459,6 +464,7 @@ where
/// Returns the current value for the given key. If the key has /// Returns the current value for the given key. If the key has
/// been union'd, this will give the value from the current root. /// been union'd, this will give the value from the current root.
#[inline(always)]
pub fn probe_value_ref_mut<K1>(&mut self, id: K1) -> &mut VarValue<K> pub fn probe_value_ref_mut<K1>(&mut self, id: K1) -> &mut VarValue<K>
where where
K1: Into<K>, K1: Into<K>,
@ -469,6 +475,7 @@ where
} }
/// This is for a debug_assert! in solve() only. Do not use it elsewhere! /// This is for a debug_assert! in solve() only. Do not use it elsewhere!
#[inline(always)]
pub fn probe_value_without_compacting<K1>(&self, id: K1) -> V pub fn probe_value_without_compacting<K1>(&self, id: K1) -> V
where where
K1: Into<K>, K1: Into<K>,