Choose i128/u128 num layouts when necessary

This commit is contained in:
Ayaz Hafiz 2022-07-04 18:32:47 -04:00 committed by ayazhafiz
parent f31f78fde1
commit ba450367ca
No known key found for this signature in database
GPG key ID: B443F7A3030C9AED
5 changed files with 170 additions and 252 deletions

View file

@ -63,7 +63,7 @@ impl Output {
}
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Copy)]
pub enum IntValue {
I128([u8; 16]),
U128([u8; 16]),

View file

@ -3610,66 +3610,22 @@ fn specialize_naked_symbol<'a>(
)
}
fn try_make_literal<'a>(
env: &mut Env<'a, '_>,
can_expr: &roc_can::expr::Expr,
) -> Option<Literal<'a>> {
fn try_make_literal<'a>(can_expr: &roc_can::expr::Expr, layout: Layout<'a>) -> Option<Literal<'a>> {
use roc_can::expr::Expr::*;
match can_expr {
Int(_, precision, _, int, _bound) => {
match num_argument_to_int_or_float(env.subs, env.target_info, *precision, false) {
IntOrFloat::Int(_) => Some(match *int {
IntValue::I128(n) => Literal::Int(n),
IntValue::U128(n) => Literal::U128(n),
}),
_ => unreachable!("unexpected float precision for integer"),
}
Int(_, _, int_str, int, _bound) => {
Some(make_num_literal(layout, int_str, IntOrFloatValue::Int(*int)).to_expr_literal())
}
Float(_, precision, float_str, float, _bound) => {
match num_argument_to_int_or_float(env.subs, env.target_info, *precision, true) {
IntOrFloat::Float(_) => Some(Literal::Float(*float)),
IntOrFloat::DecimalFloatType => {
let dec = match RocDec::from_str(float_str) {
Some(d) => d,
None => panic!(
r"Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message",
float_str
),
};
Some(Literal::Decimal(dec.to_ne_bytes()))
}
_ => unreachable!("unexpected float precision for integer"),
}
}
Float(_, _, float_str, float, _bound) => Some(
make_num_literal(layout, float_str, IntOrFloatValue::Float(*float)).to_expr_literal(),
),
// TODO investigate lifetime trouble
// Str(string) => Some(Literal::Str(env.arena.alloc(string))),
Num(var, num_str, num, _bound) => {
// first figure out what kind of number this is
match num_argument_to_int_or_float(env.subs, env.target_info, *var, false) {
IntOrFloat::Int(_) => Some(match *num {
IntValue::I128(n) => Literal::Int(n),
IntValue::U128(n) => Literal::U128(n),
}),
IntOrFloat::Float(_) => Some(match *num {
IntValue::I128(n) => Literal::Float(i128::from_ne_bytes(n) as f64),
IntValue::U128(n) => Literal::Float(u128::from_ne_bytes(n) as f64),
}),
IntOrFloat::DecimalFloatType => {
let dec = match RocDec::from_str(num_str) {
Some(d) => d,
None => panic!(
r"Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message",
num_str
),
};
Some(Literal::Decimal(dec.to_ne_bytes()))
}
}
Num(_, num_str, num, _bound) => {
Some(make_num_literal(layout, num_str, IntOrFloatValue::Int(*num)).to_expr_literal())
}
_ => None,
}
@ -3689,44 +3645,35 @@ pub fn with_hole<'a>(
let arena = env.arena;
match can_expr {
Int(_, precision, _, int, _bound) => {
match num_argument_to_int_or_float(env.subs, env.target_info, precision, false) {
IntOrFloat::Int(precision) => Stmt::Let(
assigned,
Expr::Literal(match int {
IntValue::I128(n) => Literal::Int(n),
IntValue::U128(n) => Literal::U128(n),
}),
Layout::Builtin(Builtin::Int(precision)),
hole,
),
_ => unreachable!("unexpected float precision for integer"),
}
}
Int(_, _, int_str, int, _bound) => assign_num_literal_expr(
env,
layout_cache,
assigned,
variable,
&int_str,
IntOrFloatValue::Int(int),
hole,
),
Float(_, precision, float_str, float, _bound) => {
match num_argument_to_int_or_float(env.subs, env.target_info, precision, true) {
IntOrFloat::Float(precision) => Stmt::Let(
assigned,
Expr::Literal(Literal::Float(float)),
Layout::Builtin(Builtin::Float(precision)),
hole,
),
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,
Expr::Literal(Literal::Decimal(dec.to_ne_bytes())),
Layout::Builtin(Builtin::Decimal),
hole,
)
}
_ => unreachable!("unexpected float precision for integer"),
}
}
Float(_, _, float_str, float, _bound) => assign_num_literal_expr(
env,
layout_cache,
assigned,
variable,
&float_str,
IntOrFloatValue::Float(float),
hole,
),
Num(_, num_str, num, _bound) => assign_num_literal_expr(
env,
layout_cache,
assigned,
variable,
&num_str,
IntOrFloatValue::Int(num),
hole,
),
Str(string) => Stmt::Let(
assigned,
@ -3741,16 +3688,6 @@ pub fn with_hole<'a>(
Layout::int_width(IntWidth::I32),
hole,
),
Num(_, num_str, num, _bound) => assign_num_literal(
env,
layout_cache,
assigned,
variable,
&num_str,
IntOrFloatValue::Int(num),
hole,
),
LetNonRec(def, cont) => from_can_let(
env,
procs,
@ -4255,8 +4192,12 @@ pub fn with_hole<'a>(
let mut symbol_exprs = Vec::with_capacity_in(loc_elems.len(), env.arena);
let elem_layout = layout_cache
.from_var(env.arena, elem_var, env.subs)
.unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err));
for arg_expr in loc_elems.into_iter() {
if let Some(literal) = try_make_literal(env, &arg_expr.value) {
if let Some(literal) = try_make_literal(&arg_expr.value, elem_layout) {
elements.push(ListLiteralElement::Literal(literal));
} else {
let symbol = possible_reuse_symbol_or_specialize(
@ -4274,10 +4215,6 @@ pub fn with_hole<'a>(
}
let arg_symbols = arg_symbols.into_bump_slice();
let elem_layout = layout_cache
.from_var(env.arena, elem_var, env.subs)
.unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err));
let expr = Expr::Array {
elem_layout,
elems: elements.into_bump_slice(),
@ -8249,40 +8186,20 @@ fn from_can_pattern_help<'a>(
Underscore => Ok(Pattern::Underscore),
Identifier(symbol) => Ok(Pattern::Identifier(*symbol)),
AbilityMemberSpecialization { ident, .. } => Ok(Pattern::Identifier(*ident)),
IntLiteral(_, precision_var, _, int, _bound) => {
match num_argument_to_int_or_float(env.subs, env.target_info, *precision_var, false) {
IntOrFloat::Int(precision) => match *int {
IntValue::I128(n) | IntValue::U128(n) => Ok(Pattern::IntLiteral(n, precision)),
},
other => {
panic!(
"Invalid precision for int pattern: {:?} has {:?}",
can_pattern, other
)
}
}
}
FloatLiteral(_, precision_var, float_str, float, _bound) => {
// 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.target_info, *precision_var, true) {
IntOrFloat::Int(_) => {
panic!("Invalid precision for float pattern {:?}", precision_var)
}
IntOrFloat::Float(precision) => {
Ok(Pattern::FloatLiteral(f64::to_bits(*float), precision))
}
IntOrFloat::DecimalFloatType => {
let dec = match RocDec::from_str(float_str) {
Some(d) => d,
None => panic!(
r"Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message",
float_str
),
};
Ok(Pattern::DecimalLiteral(dec.to_ne_bytes()))
}
}
}
IntLiteral(_, precision_var, int_str, int, _bound) => Ok(make_num_literal_pattern(
env,
layout_cache,
*precision_var,
int_str,
IntOrFloatValue::Int(*int),
)),
FloatLiteral(_, precision_var, float_str, float, _bound) => Ok(make_num_literal_pattern(
env,
layout_cache,
*precision_var,
float_str,
IntOrFloatValue::Float(*float),
)),
StrLiteral(v) => Ok(Pattern::StrLiteral(v.clone())),
SingleQuote(c) => Ok(Pattern::IntLiteral(
(*c as i128).to_ne_bytes(),
@ -8302,30 +8219,13 @@ fn from_can_pattern_help<'a>(
// TODO(opaques) should be `RuntimeError::OpaqueNotDefined`
Err(RuntimeError::UnsupportedPattern(loc_ident.region))
}
NumLiteral(var, num_str, num, _bound) => {
match num_argument_to_int_or_float(env.subs, env.target_info, *var, false) {
IntOrFloat::Int(precision) => Ok(match num {
IntValue::I128(num) | IntValue::U128(num) => {
Pattern::IntLiteral(*num, precision)
}
}),
IntOrFloat::Float(precision) => {
// TODO: this may be lossy
let num = match *num {
IntValue::I128(n) => f64::to_bits(i128::from_ne_bytes(n) as f64),
IntValue::U128(n) => f64::to_bits(u128::from_ne_bytes(n) as f64),
};
Ok(Pattern::FloatLiteral(num, precision))
}
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),
};
Ok(Pattern::DecimalLiteral(dec.to_ne_bytes()))
}
}
}
NumLiteral(var, num_str, num, _bound) => Ok(make_num_literal_pattern(
env,
layout_cache,
*var,
num_str,
IntOrFloatValue::Int(*num),
)),
AppliedTag {
whole_var,
@ -8936,40 +8836,52 @@ fn from_can_record_destruct<'a>(
})
}
#[derive(Debug)]
pub enum IntOrFloat {
Int(IntWidth),
Float(FloatWidth),
DecimalFloatType,
}
enum IntOrFloatValue {
Int(IntValue),
Float(f64),
}
fn assign_num_literal<'a>(
env: &mut Env<'a, '_>,
layout_cache: &mut LayoutCache<'a>,
assigned: Symbol,
variable: Variable,
enum NumLiteral {
Int([u8; 16], IntWidth),
U128([u8; 16]),
Float(f64, FloatWidth),
Decimal([u8; 16]),
}
impl NumLiteral {
fn to_expr_literal(self) -> Literal<'static> {
match self {
NumLiteral::Int(n, _) => Literal::Int(n),
NumLiteral::U128(n) => Literal::U128(n),
NumLiteral::Float(n, _) => Literal::Float(n),
NumLiteral::Decimal(n) => Literal::Decimal(n),
}
}
fn to_pattern(self) -> Pattern<'static> {
match self {
NumLiteral::Int(n, w) => Pattern::IntLiteral(n, w),
NumLiteral::U128(_) => todo!(),
NumLiteral::Float(n, w) => Pattern::FloatLiteral(f64::to_bits(n), w),
NumLiteral::Decimal(n) => Pattern::DecimalLiteral(n),
}
}
}
fn make_num_literal<'a>(
layout: Layout<'a>,
num_str: &str,
num_value: IntOrFloatValue,
hole: &'a Stmt<'a>,
) -> Stmt<'a> {
let layout = layout_cache
.from_var(env.arena, variable, &env.subs)
.unwrap();
let literal = match layout {
Layout::Builtin(Builtin::Int(_)) => match num_value {
IntOrFloatValue::Int(IntValue::I128(n)) => Literal::Int(n),
IntOrFloatValue::Int(IntValue::U128(n)) => Literal::U128(n),
) -> NumLiteral {
match layout {
Layout::Builtin(Builtin::Int(width)) => match num_value {
IntOrFloatValue::Int(IntValue::I128(n)) => NumLiteral::Int(n, width),
IntOrFloatValue::Int(IntValue::U128(n)) => NumLiteral::U128(n),
IntOrFloatValue::Float(..) => {
internal_error!("Float value where int was expected, should have been a type error")
}
},
Layout::Builtin(Builtin::Float(_)) => match num_value {
IntOrFloatValue::Float(n) => Literal::Float(n),
Layout::Builtin(Builtin::Float(width)) => match num_value {
IntOrFloatValue::Float(n) => NumLiteral::Float(n, width),
IntOrFloatValue::Int(..) => {
internal_error!("Int value where float was expected, should have been a type error")
}
@ -8982,81 +8894,44 @@ fn assign_num_literal<'a>(
num_str
),
};
Literal::Decimal(dec.to_ne_bytes())
NumLiteral::Decimal(dec.to_ne_bytes())
}
layout => internal_error!(
"Found a non-num layout where a number was expected: {:?}",
layout
),
};
}
}
fn assign_num_literal_expr<'a>(
env: &mut Env<'a, '_>,
layout_cache: &mut LayoutCache<'a>,
assigned: Symbol,
variable: Variable,
num_str: &str,
num_value: IntOrFloatValue,
hole: &'a Stmt<'a>,
) -> Stmt<'a> {
let layout = layout_cache
.from_var(env.arena, variable, &env.subs)
.unwrap();
let literal = make_num_literal(layout, num_str, num_value).to_expr_literal();
Stmt::Let(assigned, Expr::Literal(literal), layout, hole)
}
/// Given the `a` in `Num a`, determines whether it's an int or a float
pub fn num_argument_to_int_or_float(
subs: &Subs,
target_info: TargetInfo,
var: Variable,
known_to_be_float: bool,
) -> IntOrFloat {
match subs.get_content_without_compacting(var) {
Content::FlexVar(_) | Content::RigidVar(_) if known_to_be_float => {
IntOrFloat::Float(FloatWidth::F64)
}
Content::FlexVar(_) | Content::RigidVar(_) => IntOrFloat::Int(IntWidth::I64), // We default (Num *) to I64
Content::Alias(Symbol::NUM_INTEGER, args, _, _) => {
debug_assert!(args.len() == 1);
// Recurse on the second argument
let var = subs[args.all_variables().into_iter().next().unwrap()];
num_argument_to_int_or_float(subs, target_info, var, false)
}
other @ Content::Alias(symbol, args, _, _) => {
if let Some(int_width) = IntWidth::try_from_symbol(*symbol) {
return IntOrFloat::Int(int_width);
}
if let Some(float_width) = FloatWidth::try_from_symbol(*symbol) {
return IntOrFloat::Float(float_width);
}
match *symbol {
Symbol::NUM_FLOATINGPOINT => {
debug_assert!(args.len() == 1);
// Recurse on the second argument
let var = subs[args.all_variables().into_iter().next().unwrap()];
num_argument_to_int_or_float(subs, target_info, var, true)
}
Symbol::NUM_DECIMAL => IntOrFloat::DecimalFloatType,
Symbol::NUM_NAT | Symbol::NUM_NATURAL => {
let int_width = match target_info.ptr_width() {
roc_target::PtrWidth::Bytes4 => IntWidth::U32,
roc_target::PtrWidth::Bytes8 => IntWidth::U64,
};
IntOrFloat::Int(int_width)
}
_ => panic!(
"Unrecognized Num type argument for var {:?} with Content: {:?}",
var, other
),
}
}
other => {
panic!(
"Unrecognized Num type argument for var {:?} with Content: {:?}",
var, other
);
}
}
fn make_num_literal_pattern<'a>(
env: &mut Env<'a, '_>,
layout_cache: &mut LayoutCache<'a>,
variable: Variable,
num_str: &str,
num_value: IntOrFloatValue,
) -> Pattern<'a> {
let layout = layout_cache
.from_var(env.arena, variable, &env.subs)
.unwrap();
let literal = make_num_literal(layout, num_str, num_value);
literal.to_pattern()
}
type ToLowLevelCallArguments<'a> = (

View file

@ -0,0 +1,13 @@
procedure Num.19 (#Attr.2, #Attr.3):
let Num.189 : I128 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.189;
procedure Test.0 ():
let Test.6 : I128 = 18446744073709551616i64;
let Test.7 : I128 = 1i64;
let Test.2 : I128 = CallByName Num.19 Test.6 Test.7;
let Test.4 : I128 = -9223372036854775809i64;
let Test.5 : I128 = 1i64;
let Test.3 : I128 = CallByName Num.19 Test.4 Test.5;
let Test.1 : {I128, I128} = Struct {Test.2, Test.3};
ret Test.1;

View file

@ -0,0 +1,9 @@
procedure Num.19 (#Attr.2, #Attr.3):
let Num.188 : U128 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.188;
procedure Test.0 ():
let Test.2 : U128 = 170141183460469231731687303715884105728u128;
let Test.3 : U128 = 1i64;
let Test.1 : U128 = CallByName Num.19 Test.2 Test.3;
ret Test.1;

View file

@ -1661,3 +1661,24 @@ fn choose_u64_layout() {
"#
)
}
#[mono_test]
fn choose_i128_layout() {
indoc!(
r#"
{
a: 18446744073709551616 + 1,
b: -9223372036854775809 + 1,
}
"#
)
}
#[mono_test]
fn choose_u128_layout() {
indoc!(
r#"
170141183460469231731687303715884105728 + 1
"#
)
}