mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-24 06:55:15 +00:00
Choose i128/u128 num layouts when necessary
This commit is contained in:
parent
f31f78fde1
commit
ba450367ca
5 changed files with 170 additions and 252 deletions
|
@ -63,7 +63,7 @@ impl Output {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Copy)]
|
||||
pub enum IntValue {
|
||||
I128([u8; 16]),
|
||||
U128([u8; 16]),
|
||||
|
|
|
@ -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> = (
|
||||
|
|
13
crates/compiler/test_mono/generated/choose_i128_layout.txt
Normal file
13
crates/compiler/test_mono/generated/choose_i128_layout.txt
Normal 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;
|
|
@ -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;
|
|
@ -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
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue