Change how decimals are stored in mono

This commit is contained in:
Jared Ramirez 2021-08-26 11:18:26 -07:00
parent 4c72aba4a7
commit 8594f2efbe
8 changed files with 126 additions and 54 deletions

1
Cargo.lock generated
View file

@ -3365,6 +3365,7 @@ dependencies = [
"roc_problem", "roc_problem",
"roc_region", "roc_region",
"roc_solve", "roc_solve",
"roc_std",
"roc_types", "roc_types",
"roc_unify", "roc_unify",
"ven_ena", "ven_ena",

View file

@ -52,7 +52,6 @@ use roc_module::low_level::LowLevel;
use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_mono::ir::{BranchInfo, CallType, EntryPoint, JoinPointId, ModifyRc, OptLevel, ProcLayout}; use roc_mono::ir::{BranchInfo, CallType, EntryPoint, JoinPointId, ModifyRc, OptLevel, ProcLayout};
use roc_mono::layout::{Builtin, LambdaSet, Layout, LayoutIds, UnionLayout}; use roc_mono::layout::{Builtin, LambdaSet, Layout, LayoutIds, UnionLayout};
use roc_std::RocDec;
/// This is for Inkwell's FunctionValue::verify - we want to know the verification /// This is for Inkwell's FunctionValue::verify - we want to know the verification
/// output in debug builds, but we don't want it to print to stdout in release builds! /// output in debug builds, but we don't want it to print to stdout in release builds!
@ -686,18 +685,10 @@ pub fn int_with_precision<'a, 'ctx, 'env>(
pub fn float_with_precision<'a, 'ctx, 'env>( pub fn float_with_precision<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
value_str: &str,
value: f64, value: f64,
precision: &Builtin, precision: &Builtin,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
match precision { match precision {
Builtin::Decimal => {
let dec = match RocDec::from_str(value_str) {
Some(d) => d.0,
None => panic!("Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message", value),
};
env.context.i128_type().const_int(dec as u64, false).into()
}
Builtin::Float64 => env.context.f64_type().const_float(value).into(), Builtin::Float64 => env.context.f64_type().const_float(value).into(),
Builtin::Float32 => env.context.f32_type().const_float(value).into(), Builtin::Float32 => env.context.f32_type().const_float(value).into(),
_ => panic!("Invalid layout for float literal = {:?}", precision), _ => panic!("Invalid layout for float literal = {:?}", precision),
@ -717,11 +708,16 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
_ => panic!("Invalid layout for int literal = {:?}", layout), _ => panic!("Invalid layout for int literal = {:?}", layout),
}, },
Float(float_str, float) => match layout { Float(float) => match layout {
Layout::Builtin(builtin) => float_with_precision(env, *float_str, *float, builtin), Layout::Builtin(builtin) => float_with_precision(env, *float, builtin),
_ => panic!("Invalid layout for float literal = {:?}", layout), _ => panic!("Invalid layout for float literal = {:?}", layout),
}, },
Decimal(int) => env
.context
.i128_type()
.const_int(int.0 as u64, false)
.into(),
Bool(b) => env.context.bool_type().const_int(*b as u64, false).into(), Bool(b) => env.context.bool_type().const_int(*b as u64, false).into(),
Byte(b) => env.context.i8_type().const_int(*b as u64, false).into(), Byte(b) => env.context.i8_type().const_int(*b as u64, false).into(),
Str(str_literal) => { Str(str_literal) => {

View file

@ -13,6 +13,7 @@ roc_types = { path = "../types" }
roc_can = { path = "../can" } roc_can = { path = "../can" }
roc_unify = { path = "../unify" } roc_unify = { path = "../unify" }
roc_solve = { path = "../solve" } roc_solve = { path = "../solve" }
roc_std = { path = "../../roc_std" }
roc_problem = { path = "../problem" } roc_problem = { path = "../problem" }
ven_pretty = { path = "../../vendor/pretty" } ven_pretty = { path = "../../vendor/pretty" }
morphic_lib = { path = "../../vendor/morphic_lib" } morphic_lib = { path = "../../vendor/morphic_lib" }

View file

@ -1193,7 +1193,7 @@ fn literal_spec(
match literal { match literal {
Str(_) => new_static_string(builder, block), Str(_) => new_static_string(builder, block),
Int(_) | Float(_, _) | Bool(_) | Byte(_) => builder.add_make_tuple(block, &[]), Int(_) | Float(_) | Decimal(_) | Bool(_) | Byte(_) => builder.add_make_tuple(block, &[]),
} }
} }

View file

@ -7,6 +7,7 @@ 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;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_std::RocDec;
/// COMPILE CASES /// COMPILE CASES
@ -85,7 +86,8 @@ enum Test<'a> {
arguments: Vec<(Pattern<'a>, Layout<'a>)>, arguments: Vec<(Pattern<'a>, Layout<'a>)>,
}, },
IsInt(i128), IsInt(i128),
IsFloat(Box<str>, u64), IsFloat(u64),
IsDecimal(RocDec),
IsStr(Box<str>), IsStr(Box<str>),
IsBit(bool), IsBit(bool),
IsByte { IsByte {
@ -108,7 +110,7 @@ impl<'a> Hash for Test<'a> {
state.write_u8(1); state.write_u8(1);
v.hash(state); v.hash(state);
} }
IsFloat(_, v) => { IsFloat(v) => {
state.write_u8(2); state.write_u8(2);
v.hash(state); v.hash(state);
} }
@ -125,6 +127,11 @@ impl<'a> Hash for Test<'a> {
tag_id.hash(state); tag_id.hash(state);
num_alts.hash(state); num_alts.hash(state);
} }
IsDecimal(v) => {
// TODO: Is this okay?
state.write_u8(6);
v.0.hash(state);
}
} }
} }
} }
@ -300,7 +307,8 @@ fn tests_are_complete_help(last_test: &Test, number_of_tests: usize) -> bool {
Test::IsByte { num_alts, .. } => number_of_tests == *num_alts, Test::IsByte { num_alts, .. } => number_of_tests == *num_alts,
Test::IsBit(_) => number_of_tests == 2, Test::IsBit(_) => number_of_tests == 2,
Test::IsInt(_) => false, Test::IsInt(_) => false,
Test::IsFloat(_, _) => false, Test::IsFloat(_) => false,
Test::IsDecimal(_) => false,
Test::IsStr(_) => false, Test::IsStr(_) => false,
} }
} }
@ -554,7 +562,8 @@ fn test_at_path<'a>(
num_alts: union.alternatives.len(), num_alts: union.alternatives.len(),
}, },
IntLiteral(v) => IsInt(*v), IntLiteral(v) => IsInt(*v),
FloatLiteral(s, v) => IsFloat(s.clone(), *v), FloatLiteral(v) => IsFloat(*v),
DecimalLiteral(v) => IsDecimal(*v),
StrLiteral(v) => IsStr(v.clone()), StrLiteral(v) => IsStr(v.clone()),
}; };
@ -810,8 +819,20 @@ fn to_relevant_branch_help<'a>(
_ => None, _ => None,
}, },
FloatLiteral(_, float) => match test { FloatLiteral(float) => match test {
IsFloat(_, test_float) if float == *test_float => { IsFloat(test_float) if float == *test_float => {
start.extend(end);
Some(Branch {
goal: branch.goal,
guard: branch.guard.clone(),
patterns: start,
})
}
_ => None,
},
DecimalLiteral(dec) => match test {
IsDecimal(test_dec) if dec.0 == test_dec.0 => {
start.extend(end); start.extend(end);
Some(Branch { Some(Branch {
goal: branch.goal, goal: branch.goal,
@ -908,7 +929,8 @@ fn needs_tests(pattern: &Pattern) -> bool {
| BitLiteral { .. } | BitLiteral { .. }
| EnumLiteral { .. } | EnumLiteral { .. }
| IntLiteral(_) | IntLiteral(_)
| FloatLiteral(_, _) | FloatLiteral(_)
| DecimalLiteral(_)
| StrLiteral(_) => true, | StrLiteral(_) => true,
} }
} }
@ -1268,16 +1290,25 @@ fn test_to_equality<'a>(
(stores, lhs_symbol, rhs_symbol, None) (stores, lhs_symbol, rhs_symbol, None)
} }
Test::IsFloat(test_str, test_int) => { Test::IsFloat(test_int) => {
// TODO maybe we can actually use i64 comparison here? // TODO maybe we can actually use i64 comparison here?
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(env.arena.alloc(test_str), 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, Layout::Builtin(Builtin::Float64), lhs)); stores.push((lhs_symbol, Layout::Builtin(Builtin::Float64), lhs));
(stores, lhs_symbol, rhs_symbol, None) (stores, lhs_symbol, rhs_symbol, None)
} }
Test::IsDecimal(test_dec) => {
// Is comparing as an i128 here fine?
let lhs = Expr::Literal(Literal::Int(test_dec.0));
let lhs_symbol = env.unique_symbol();
stores.push((lhs_symbol, Layout::Builtin(Builtin::Int128), lhs));
(stores, lhs_symbol, rhs_symbol, None)
}
Test::IsByte { Test::IsByte {
tag_id: test_byte, .. tag_id: test_byte, ..
} => { } => {
@ -1708,7 +1739,7 @@ fn decide_to_branching<'a>(
let tag = match test { let tag = match test {
Test::IsInt(v) => v as u64, Test::IsInt(v) => v as u64,
Test::IsFloat(_, v) => v as u64, Test::IsFloat(v) => v as u64,
Test::IsBit(v) => v as u64, Test::IsBit(v) => v as u64,
Test::IsByte { tag_id, .. } => tag_id as u64, Test::IsByte { tag_id, .. } => tag_id as u64,
Test::IsCtor { tag_id, .. } => tag_id as u64, Test::IsCtor { tag_id, .. } => tag_id as u64,

View file

@ -2,6 +2,7 @@ use crate::ir::DestructType;
use roc_collections::all::{Index, MutMap}; use roc_collections::all::{Index, MutMap};
use roc_module::ident::{Lowercase, TagName}; use roc_module::ident::{Lowercase, TagName};
use roc_region::all::{Located, Region}; use roc_region::all::{Located, Region};
use roc_std::RocDec;
use self::Pattern::*; use self::Pattern::*;
@ -55,7 +56,8 @@ pub enum Literal {
Int(i128), Int(i128),
Bit(bool), Bit(bool),
Byte(u8), Byte(u8),
Float(Box<str>, u64), Float(u64),
Decimal(RocDec),
Str(Box<str>), Str(Box<str>),
} }
@ -64,7 +66,8 @@ fn simplify(pattern: &crate::ir::Pattern) -> Pattern {
match pattern { match pattern {
IntLiteral(v) => Literal(Literal::Int(*v)), IntLiteral(v) => Literal(Literal::Int(*v)),
FloatLiteral(s, v) => Literal(Literal::Float(s.clone(), *v)), FloatLiteral(v) => Literal(Literal::Float(*v)),
DecimalLiteral(v) => Literal(Literal::Decimal(*v)),
StrLiteral(v) => Literal(Literal::Str(v.clone())), StrLiteral(v) => Literal(Literal::Str(v.clone())),
// To make sure these are exhaustive, we have to "fake" a union here // To make sure these are exhaustive, we have to "fake" a union here

View file

@ -14,6 +14,7 @@ use roc_module::low_level::LowLevel;
use roc_module::symbol::{IdentIds, ModuleId, Symbol}; use roc_module::symbol::{IdentIds, ModuleId, Symbol};
use roc_problem::can::RuntimeError; use roc_problem::can::RuntimeError;
use roc_region::all::{Located, Region}; use roc_region::all::{Located, Region};
use roc_std::RocDec;
use roc_types::solved_types::SolvedType; use roc_types::solved_types::SolvedType;
use roc_types::subs::{Content, FlatType, Subs, Variable, VariableSubsSlice}; use roc_types::subs::{Content, FlatType, Subs, Variable, VariableSubsSlice};
use std::collections::HashMap; use std::collections::HashMap;
@ -1020,7 +1021,8 @@ impl ModifyRc {
pub enum Literal<'a> { pub enum Literal<'a> {
// Literals // Literals
Int(i128), Int(i128),
Float(&'a str, f64), Float(f64),
Decimal(RocDec),
Str(&'a str), Str(&'a str),
/// Closed tag unions containing exactly two (0-arity) tags compile to Expr::Bool, /// Closed tag unions containing exactly two (0-arity) tags compile to Expr::Bool,
/// so they can (at least potentially) be emitted as 1-bit machine bools. /// so they can (at least potentially) be emitted as 1-bit machine bools.
@ -1201,7 +1203,9 @@ impl<'a> Literal<'a> {
match self { match self {
Int(lit) => alloc.text(format!("{}i64", lit)), Int(lit) => alloc.text(format!("{}i64", lit)),
Float(_, lit) => alloc.text(format!("{}f64", lit)), Float(lit) => alloc.text(format!("{}f64", lit)),
// TODO: Add proper Dec.to_str
Decimal(lit) => alloc.text(format!("{}Dec", lit.0)),
Bool(lit) => alloc.text(format!("{}", lit)), Bool(lit) => alloc.text(format!("{}", lit)),
Byte(lit) => alloc.text(format!("{}u8", lit)), Byte(lit) => alloc.text(format!("{}u8", lit)),
Str(lit) => alloc.text(format!("{:?}", lit)), Str(lit) => alloc.text(format!("{:?}", lit)),
@ -2747,16 +2751,22 @@ pub fn with_hole<'a>(
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::BinaryFloatType(precision) => Stmt::Let(
assigned, assigned,
Expr::Literal(Literal::Float(arena.alloc(float_str), float)), Expr::Literal(Literal::Float(float)),
Layout::Builtin(float_precision_to_builtin(precision)), Layout::Builtin(float_precision_to_builtin(precision)),
hole, hole,
), ),
IntOrFloat::DecimalFloatType => Stmt::Let( IntOrFloat::DecimalFloatType => {
let dec = match RocDec::from_str(&float_str) {
Some(d) => d,
None => panic!("Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message", float_str),
};
Stmt::Let(
assigned, assigned,
Expr::Literal(Literal::Float(arena.alloc(float_str), float)), Expr::Literal(Literal::Decimal(dec)),
Layout::Builtin(Builtin::Decimal), Layout::Builtin(Builtin::Decimal),
hole, hole,
), )
}
_ => unreachable!("unexpected float precision for integer"), _ => unreachable!("unexpected float precision for integer"),
} }
} }
@ -2784,16 +2794,22 @@ pub fn with_hole<'a>(
), ),
IntOrFloat::BinaryFloatType(precision) => Stmt::Let( IntOrFloat::BinaryFloatType(precision) => Stmt::Let(
assigned, assigned,
Expr::Literal(Literal::Float(arena.alloc(num_str), num as f64)), Expr::Literal(Literal::Float(num as f64)),
Layout::Builtin(float_precision_to_builtin(precision)), Layout::Builtin(float_precision_to_builtin(precision)),
hole, hole,
), ),
IntOrFloat::DecimalFloatType => Stmt::Let( IntOrFloat::DecimalFloatType => {
let dec = match RocDec::from_str(&num_str) {
Some(d) => d,
None => panic!("Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message", num_str),
};
Stmt::Let(
assigned, assigned,
Expr::Literal(Literal::Float(arena.alloc(num_str), num as f64)), Expr::Literal(Literal::Decimal(dec)),
Layout::Builtin(Builtin::Decimal), Layout::Builtin(Builtin::Decimal),
hole, hole,
), )
}
} }
} }
LetNonRec(def, cont, _) => { LetNonRec(def, cont, _) => {
@ -5589,7 +5605,8 @@ fn store_pattern_help<'a>(
return StorePattern::NotProductive(stmt); return StorePattern::NotProductive(stmt);
} }
IntLiteral(_) IntLiteral(_)
| FloatLiteral(_, _) | FloatLiteral(_)
| DecimalLiteral(_)
| EnumLiteral { .. } | EnumLiteral { .. }
| BitLiteral { .. } | BitLiteral { .. }
| StrLiteral(_) => { | StrLiteral(_) => {
@ -5723,7 +5740,8 @@ fn store_tag_pattern<'a>(
// ignore // ignore
} }
IntLiteral(_) IntLiteral(_)
| FloatLiteral(_, _) | FloatLiteral(_)
| DecimalLiteral(_)
| EnumLiteral { .. } | EnumLiteral { .. }
| BitLiteral { .. } | BitLiteral { .. }
| StrLiteral(_) => {} | StrLiteral(_) => {}
@ -5798,7 +5816,8 @@ fn store_newtype_pattern<'a>(
// ignore // ignore
} }
IntLiteral(_) IntLiteral(_)
| FloatLiteral(_, _) | FloatLiteral(_)
| DecimalLiteral(_)
| EnumLiteral { .. } | EnumLiteral { .. }
| BitLiteral { .. } | BitLiteral { .. }
| StrLiteral(_) => {} | StrLiteral(_) => {}
@ -5873,7 +5892,8 @@ fn store_record_destruct<'a>(
return StorePattern::NotProductive(stmt); return StorePattern::NotProductive(stmt);
} }
IntLiteral(_) IntLiteral(_)
| FloatLiteral(_, _) | FloatLiteral(_)
| DecimalLiteral(_)
| EnumLiteral { .. } | EnumLiteral { .. }
| BitLiteral { .. } | BitLiteral { .. }
| StrLiteral(_) => { | StrLiteral(_) => {
@ -6822,7 +6842,8 @@ pub enum Pattern<'a> {
Identifier(Symbol), Identifier(Symbol),
Underscore, Underscore,
IntLiteral(i128), IntLiteral(i128),
FloatLiteral(Box<str>, u64), FloatLiteral(u64),
DecimalLiteral(RocDec),
BitLiteral { BitLiteral {
value: bool, value: bool,
tag_name: TagName, tag_name: TagName,
@ -6900,10 +6921,25 @@ fn from_can_pattern_help<'a>(
Underscore => Ok(Pattern::Underscore), Underscore => Ok(Pattern::Underscore),
Identifier(symbol) => Ok(Pattern::Identifier(*symbol)), Identifier(symbol) => Ok(Pattern::Identifier(*symbol)),
IntLiteral(_, _, int) => Ok(Pattern::IntLiteral(*int as i128)), IntLiteral(_, _, int) => Ok(Pattern::IntLiteral(*int as i128)),
FloatLiteral(_, float_str, float) => Ok(Pattern::FloatLiteral( FloatLiteral(var, float_str, float) => {
float_str.clone(), // TODO: Can I reuse num_argument_to_int_or_float here if I pass in true?
f64::to_bits(*float), match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, true) {
)), IntOrFloat::SignedIntType(_) => {
panic!("Invalid percision for float literal = {:?}", var)
}
IntOrFloat::UnsignedIntType(_) => {
panic!("Invalid percision for float literal = {:?}", var)
}
IntOrFloat::BinaryFloatType(_) => Ok(Pattern::FloatLiteral(f64::to_bits(*float))),
IntOrFloat::DecimalFloatType => {
let dec = match RocDec::from_str(float_str) {
Some(d) => d,
None => panic!("Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message", float_str),
};
Ok(Pattern::DecimalLiteral(dec))
}
}
}
StrLiteral(v) => Ok(Pattern::StrLiteral(v.clone())), StrLiteral(v) => Ok(Pattern::StrLiteral(v.clone())),
Shadowed(region, ident) => Err(RuntimeError::Shadowing { Shadowed(region, ident) => Err(RuntimeError::Shadowing {
original_region: *region, original_region: *region,
@ -6918,11 +6954,13 @@ fn from_can_pattern_help<'a>(
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(_) => Ok(Pattern::IntLiteral(*num as i128)), IntOrFloat::SignedIntType(_) => Ok(Pattern::IntLiteral(*num as i128)),
IntOrFloat::UnsignedIntType(_) => Ok(Pattern::IntLiteral(*num as i128)), IntOrFloat::UnsignedIntType(_) => Ok(Pattern::IntLiteral(*num as i128)),
IntOrFloat::BinaryFloatType(_) => { IntOrFloat::BinaryFloatType(_) => Ok(Pattern::FloatLiteral(*num as u64)),
Ok(Pattern::FloatLiteral(num_str.clone(), *num as u64))
}
IntOrFloat::DecimalFloatType => { IntOrFloat::DecimalFloatType => {
Ok(Pattern::FloatLiteral(num_str.clone(), *num as u64)) let dec = match RocDec::from_str(num_str) {
Some(d) => d,
None => panic!("Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message", num_str),
};
Ok(Pattern::DecimalLiteral(dec))
} }
} }
} }

View file

@ -142,7 +142,9 @@ fn pattern_to_doc_help<'b>(
Bit(true) => alloc.text("True"), Bit(true) => alloc.text("True"),
Bit(false) => alloc.text("False"), Bit(false) => alloc.text("False"),
Byte(b) => alloc.text(b.to_string()), Byte(b) => alloc.text(b.to_string()),
Float(_, f) => alloc.text(f.to_string()), Float(f) => alloc.text(f.to_string()),
// TODO: Proper Dec.to_str
Decimal(d) => alloc.text(d.0.to_string()),
Str(s) => alloc.string(s.into()), Str(s) => alloc.string(s.into()),
}, },
Ctor(union, tag_id, args) => { Ctor(union, tag_id, args) => {