mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
Merge pull request #2442 from rtfeldman/i/2332
Check lower bounds for numeric literals, and permit 128-bit literals
This commit is contained in:
commit
860c5eb0ce
17 changed files with 931 additions and 223 deletions
|
@ -1,5 +1,5 @@
|
|||
use bumpalo::Bump;
|
||||
use roc_can::expr::Recursive;
|
||||
use roc_can::expr::{IntValue, Recursive};
|
||||
use roc_can::num::{
|
||||
finish_parsing_base, finish_parsing_float, finish_parsing_num, ParsedNumResult,
|
||||
};
|
||||
|
@ -78,7 +78,10 @@ pub fn expr_to_expr2<'a>(
|
|||
match finish_parsing_num(string) {
|
||||
Ok(ParsedNumResult::UnknownNum(int) | ParsedNumResult::Int(int, _)) => {
|
||||
let expr = Expr2::SmallInt {
|
||||
number: IntVal::I64(int),
|
||||
number: IntVal::I64(match int {
|
||||
IntValue::U128(_) => todo!(),
|
||||
IntValue::I128(n) => n as i64, // FIXME
|
||||
}),
|
||||
var: env.var_store.fresh(),
|
||||
// TODO non-hardcode
|
||||
style: IntStyle::Decimal,
|
||||
|
@ -120,7 +123,10 @@ pub fn expr_to_expr2<'a>(
|
|||
match finish_parsing_base(string, *base, *is_negative) {
|
||||
Ok((int, _bound)) => {
|
||||
let expr = Expr2::SmallInt {
|
||||
number: IntVal::I64(int),
|
||||
number: IntVal::I64(match int {
|
||||
IntValue::U128(_) => todo!(),
|
||||
IntValue::I128(n) => n as i64, // FIXME
|
||||
}),
|
||||
var: env.var_store.fresh(),
|
||||
// TODO non-hardcode
|
||||
style: IntStyle::from_base(*base),
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#![allow(unused_imports)]
|
||||
|
||||
use bumpalo::collections::Vec as BumpVec;
|
||||
use roc_can::expr::unescape_char;
|
||||
use roc_can::expr::{unescape_char, IntValue};
|
||||
use roc_can::num::{
|
||||
finish_parsing_base, finish_parsing_float, finish_parsing_num, ParsedNumResult,
|
||||
};
|
||||
|
@ -197,9 +197,20 @@ pub fn to_pattern2<'a>(
|
|||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
Ok(ParsedNumResult::UnknownNum(int)) => {
|
||||
Pattern2::NumLiteral(env.var_store.fresh(), int)
|
||||
Pattern2::NumLiteral(
|
||||
env.var_store.fresh(),
|
||||
match int {
|
||||
IntValue::U128(_) => todo!(),
|
||||
IntValue::I128(n) => n as i64, // FIXME
|
||||
},
|
||||
)
|
||||
}
|
||||
Ok(ParsedNumResult::Int(int, _bound)) => {
|
||||
Pattern2::IntLiteral(IntVal::I64(match int {
|
||||
IntValue::U128(_) => todo!(),
|
||||
IntValue::I128(n) => n as i64, // FIXME
|
||||
}))
|
||||
}
|
||||
Ok(ParsedNumResult::Int(int, _bound)) => Pattern2::IntLiteral(IntVal::I64(int)),
|
||||
Ok(ParsedNumResult::Float(int, _bound)) => {
|
||||
Pattern2::FloatLiteral(FloatVal::F64(int))
|
||||
}
|
||||
|
@ -218,6 +229,10 @@ pub fn to_pattern2<'a>(
|
|||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
Ok((int, _bound)) => {
|
||||
let int = match int {
|
||||
IntValue::U128(_) => todo!(),
|
||||
IntValue::I128(n) => n as i64, // FIXME
|
||||
};
|
||||
if *is_negative {
|
||||
Pattern2::IntLiteral(IntVal::I64(-int))
|
||||
} else {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::def::Def;
|
||||
use crate::expr::{self, ClosureData, Expr::*};
|
||||
use crate::expr::{self, ClosureData, Expr::*, IntValue};
|
||||
use crate::expr::{Expr, Field, Recursive};
|
||||
use crate::num::{FloatWidth, IntWidth, NumWidth, NumericBound};
|
||||
use crate::pattern::Pattern;
|
||||
|
@ -5197,7 +5197,7 @@ where
|
|||
num_var,
|
||||
precision_var,
|
||||
ii.to_string().into_boxed_str(),
|
||||
ii,
|
||||
IntValue::I128(ii),
|
||||
bound,
|
||||
)
|
||||
}
|
||||
|
@ -5219,6 +5219,12 @@ fn float(
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn num(num_var: Variable, i: i64, bound: NumericBound<NumWidth>) -> Expr {
|
||||
Num(num_var, i.to_string().into_boxed_str(), i, bound)
|
||||
fn num<I: Into<i128>>(num_var: Variable, i: I, bound: NumericBound<NumWidth>) -> Expr {
|
||||
let i = i.into();
|
||||
Num(
|
||||
num_var,
|
||||
i.to_string().into_boxed_str(),
|
||||
IntValue::I128(i),
|
||||
bound,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError};
|
|||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::Alias;
|
||||
use std::fmt::Debug;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::{char, u32};
|
||||
|
||||
#[derive(Clone, Default, Debug, PartialEq)]
|
||||
|
@ -46,16 +46,37 @@ impl Output {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum IntValue {
|
||||
I128(i128),
|
||||
U128(u128),
|
||||
}
|
||||
|
||||
impl Display for IntValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
IntValue::I128(n) => Display::fmt(&n, f),
|
||||
IntValue::U128(n) => Display::fmt(&n, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Expr {
|
||||
// Literals
|
||||
|
||||
// Num stores the `a` variable in `Num a`. Not the same as the variable
|
||||
// stored in Int and Float below, which is strictly for better error messages
|
||||
Num(Variable, Box<str>, i64, NumericBound<NumWidth>),
|
||||
Num(Variable, Box<str>, IntValue, NumericBound<NumWidth>),
|
||||
|
||||
// Int and Float store a variable to generate better error messages
|
||||
Int(Variable, Variable, Box<str>, i128, NumericBound<IntWidth>),
|
||||
Int(
|
||||
Variable,
|
||||
Variable,
|
||||
Box<str>,
|
||||
IntValue,
|
||||
NumericBound<IntWidth>,
|
||||
),
|
||||
Float(Variable, Variable, Box<str>, f64, NumericBound<FloatWidth>),
|
||||
Str(Box<str>),
|
||||
List {
|
||||
|
@ -802,13 +823,7 @@ pub fn canonicalize_expr<'a>(
|
|||
// to keep borrowed values around and make this compile
|
||||
let int_string = int.to_string();
|
||||
let int_str = int_string.as_str();
|
||||
int_expr_from_result(
|
||||
var_store,
|
||||
Ok((int_str, int as i128, bound)),
|
||||
region,
|
||||
base,
|
||||
env,
|
||||
)
|
||||
int_expr_from_result(var_store, Ok((int_str, int, bound)), region, base, env)
|
||||
}
|
||||
Err(e) => int_expr_from_result(var_store, Err(e), region, base, env),
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::env::Env;
|
||||
use crate::expr::Expr;
|
||||
use crate::expr::{Expr, IntValue};
|
||||
use roc_parse::ast::Base;
|
||||
use roc_problem::can::Problem;
|
||||
use roc_problem::can::RuntimeError::*;
|
||||
|
@ -9,11 +9,6 @@ use roc_types::subs::VarStore;
|
|||
use std::i64;
|
||||
use std::str;
|
||||
|
||||
// TODO use rust's integer parsing again
|
||||
//
|
||||
// We're waiting for libcore here, see https://github.com/rust-lang/rust/issues/22639
|
||||
// There is a nightly API for exposing the parse error.
|
||||
|
||||
#[inline(always)]
|
||||
pub fn num_expr_from_result(
|
||||
var_store: &mut VarStore,
|
||||
|
@ -29,7 +24,7 @@ pub fn num_expr_from_result(
|
|||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
(*str).into(),
|
||||
num.into(),
|
||||
num,
|
||||
bound,
|
||||
),
|
||||
Ok((str, ParsedNumResult::Float(num, bound))) => Expr::Float(
|
||||
|
@ -55,7 +50,7 @@ pub fn num_expr_from_result(
|
|||
#[inline(always)]
|
||||
pub fn int_expr_from_result(
|
||||
var_store: &mut VarStore,
|
||||
result: Result<(&str, i128, NumericBound<IntWidth>), (&str, IntErrorKind)>,
|
||||
result: Result<(&str, IntValue, NumericBound<IntWidth>), (&str, IntErrorKind)>,
|
||||
region: Region,
|
||||
base: Base,
|
||||
env: &mut Env,
|
||||
|
@ -106,9 +101,9 @@ pub fn float_expr_from_result(
|
|||
}
|
||||
|
||||
pub enum ParsedNumResult {
|
||||
Int(i64, NumericBound<IntWidth>),
|
||||
Int(IntValue, NumericBound<IntWidth>),
|
||||
Float(f64, NumericBound<FloatWidth>),
|
||||
UnknownNum(i64),
|
||||
UnknownNum(IntValue),
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -116,7 +111,7 @@ pub fn finish_parsing_num(raw: &str) -> Result<ParsedNumResult, (&str, IntErrorK
|
|||
// Ignore underscores.
|
||||
let radix = 10;
|
||||
let (num, bound) =
|
||||
from_str_radix::<i64>(raw.replace("_", "").as_str(), radix).map_err(|e| (raw, e.kind))?;
|
||||
from_str_radix(raw.replace("_", "").as_str(), radix).map_err(|e| (raw, e))?;
|
||||
// Let's try to specialize the number
|
||||
Ok(match bound {
|
||||
NumericBound::None => ParsedNumResult::UnknownNum(num),
|
||||
|
@ -124,7 +119,11 @@ pub fn finish_parsing_num(raw: &str) -> Result<ParsedNumResult, (&str, IntErrorK
|
|||
ParsedNumResult::Int(num, NumericBound::Exact(iw))
|
||||
}
|
||||
NumericBound::Exact(NumWidth::Float(fw)) => {
|
||||
ParsedNumResult::Float(num as f64, NumericBound::Exact(fw))
|
||||
let num = match num {
|
||||
IntValue::I128(n) => n as f64,
|
||||
IntValue::U128(n) => n as f64,
|
||||
};
|
||||
ParsedNumResult::Float(num, NumericBound::Exact(fw))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -134,7 +133,7 @@ pub fn finish_parsing_base(
|
|||
raw: &str,
|
||||
base: Base,
|
||||
is_negative: bool,
|
||||
) -> Result<(i64, NumericBound<IntWidth>), (&str, IntErrorKind)> {
|
||||
) -> Result<(IntValue, NumericBound<IntWidth>), (&str, IntErrorKind)> {
|
||||
let radix = match base {
|
||||
Base::Hex => 16,
|
||||
Base::Decimal => 10,
|
||||
|
@ -144,11 +143,10 @@ pub fn finish_parsing_base(
|
|||
|
||||
// Ignore underscores, insert - when negative to get correct underflow/overflow behavior
|
||||
(if is_negative {
|
||||
from_str_radix::<i64>(format!("-{}", raw.replace("_", "")).as_str(), radix)
|
||||
from_str_radix(format!("-{}", raw.replace("_", "")).as_str(), radix)
|
||||
} else {
|
||||
from_str_radix::<i64>(raw.replace("_", "").as_str(), radix)
|
||||
from_str_radix(raw.replace("_", "").as_str(), radix)
|
||||
})
|
||||
.map_err(|e| e.kind)
|
||||
.and_then(|(n, bound)| {
|
||||
let bound = match bound {
|
||||
NumericBound::None => NumericBound::None,
|
||||
|
@ -164,15 +162,12 @@ pub fn finish_parsing_base(
|
|||
pub fn finish_parsing_float(
|
||||
raw: &str,
|
||||
) -> Result<(f64, NumericBound<FloatWidth>), (&str, FloatErrorKind)> {
|
||||
let (bound, raw_without_suffix) = parse_literal_suffix(raw.as_bytes());
|
||||
// Safety: `raw` is valid UTF8, and `parse_literal_suffix` will only chop UTF8
|
||||
// characters off the end, if it chops off anything at all.
|
||||
let raw_without_suffix = unsafe { str::from_utf8_unchecked(raw_without_suffix) };
|
||||
let (opt_bound, raw_without_suffix) = parse_literal_suffix(raw);
|
||||
|
||||
let bound = match bound {
|
||||
NumericBound::None => NumericBound::None,
|
||||
NumericBound::Exact(NumWidth::Float(fw)) => NumericBound::Exact(fw),
|
||||
NumericBound::Exact(NumWidth::Int(_)) => return Err((raw, FloatErrorKind::IntSuffix)),
|
||||
let bound = match opt_bound {
|
||||
None => NumericBound::None,
|
||||
Some(NumWidth::Float(fw)) => NumericBound::Exact(fw),
|
||||
Some(NumWidth::Int(_)) => return Err((raw, FloatErrorKind::IntSuffix)),
|
||||
};
|
||||
|
||||
// Ignore underscores.
|
||||
|
@ -189,33 +184,33 @@ pub fn finish_parsing_float(
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_literal_suffix(num_str: &[u8]) -> (NumericBound<NumWidth>, &[u8]) {
|
||||
fn parse_literal_suffix(num_str: &str) -> (Option<NumWidth>, &str) {
|
||||
macro_rules! parse_num_suffix {
|
||||
($($suffix:expr, $width:expr)*) => {$(
|
||||
if num_str.ends_with($suffix) {
|
||||
return (NumericBound::Exact($width), num_str.get(0..num_str.len() - $suffix.len()).unwrap());
|
||||
return (Some($width), num_str.get(0..num_str.len() - $suffix.len()).unwrap());
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
parse_num_suffix! {
|
||||
b"u8", NumWidth::Int(IntWidth::U8)
|
||||
b"u16", NumWidth::Int(IntWidth::U16)
|
||||
b"u32", NumWidth::Int(IntWidth::U32)
|
||||
b"u64", NumWidth::Int(IntWidth::U64)
|
||||
b"u128", NumWidth::Int(IntWidth::U128)
|
||||
b"i8", NumWidth::Int(IntWidth::I8)
|
||||
b"i16", NumWidth::Int(IntWidth::I16)
|
||||
b"i32", NumWidth::Int(IntWidth::I32)
|
||||
b"i64", NumWidth::Int(IntWidth::I64)
|
||||
b"i128", NumWidth::Int(IntWidth::I128)
|
||||
b"nat", NumWidth::Int(IntWidth::Nat)
|
||||
b"dec", NumWidth::Float(FloatWidth::Dec)
|
||||
b"f32", NumWidth::Float(FloatWidth::F32)
|
||||
b"f64", NumWidth::Float(FloatWidth::F64)
|
||||
"u8", NumWidth::Int(IntWidth::U8)
|
||||
"u16", NumWidth::Int(IntWidth::U16)
|
||||
"u32", NumWidth::Int(IntWidth::U32)
|
||||
"u64", NumWidth::Int(IntWidth::U64)
|
||||
"u128", NumWidth::Int(IntWidth::U128)
|
||||
"i8", NumWidth::Int(IntWidth::I8)
|
||||
"i16", NumWidth::Int(IntWidth::I16)
|
||||
"i32", NumWidth::Int(IntWidth::I32)
|
||||
"i64", NumWidth::Int(IntWidth::I64)
|
||||
"i128", NumWidth::Int(IntWidth::I128)
|
||||
"nat", NumWidth::Int(IntWidth::Nat)
|
||||
"dec", NumWidth::Float(FloatWidth::Dec)
|
||||
"f32", NumWidth::Float(FloatWidth::F32)
|
||||
"f64", NumWidth::Float(FloatWidth::F64)
|
||||
}
|
||||
|
||||
(NumericBound::None, num_str)
|
||||
(None, num_str)
|
||||
}
|
||||
|
||||
/// Integer parsing code taken from the rust libcore,
|
||||
|
@ -226,47 +221,11 @@ fn parse_literal_suffix(num_str: &[u8]) -> (NumericBound<NumWidth>, &[u8]) {
|
|||
/// the LEGAL_DETAILS file in the root directory of this distribution.
|
||||
///
|
||||
/// Thanks to the Rust project and its contributors!
|
||||
trait FromStrRadixHelper: PartialOrd + Copy {
|
||||
fn min_value() -> Self;
|
||||
fn max_value() -> Self;
|
||||
fn from_u32(u: u32) -> Self;
|
||||
fn checked_mul(&self, other: u32) -> Option<Self>;
|
||||
fn checked_sub(&self, other: u32) -> Option<Self>;
|
||||
fn checked_add(&self, other: u32) -> Option<Self>;
|
||||
}
|
||||
|
||||
macro_rules! doit {
|
||||
($($t:ty)*) => ($(impl FromStrRadixHelper for $t {
|
||||
#[inline]
|
||||
fn min_value() -> Self { Self::min_value() }
|
||||
#[inline]
|
||||
fn max_value() -> Self { Self::max_value() }
|
||||
#[inline]
|
||||
fn from_u32(u: u32) -> Self { u as Self }
|
||||
#[inline]
|
||||
fn checked_mul(&self, other: u32) -> Option<Self> {
|
||||
Self::checked_mul(*self, other as Self)
|
||||
}
|
||||
#[inline]
|
||||
fn checked_sub(&self, other: u32) -> Option<Self> {
|
||||
Self::checked_sub(*self, other as Self)
|
||||
}
|
||||
#[inline]
|
||||
fn checked_add(&self, other: u32) -> Option<Self> {
|
||||
Self::checked_add(*self, other as Self)
|
||||
}
|
||||
})*)
|
||||
}
|
||||
// We only need the i64 implementation, but libcore defines
|
||||
// doit! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize }
|
||||
doit! { i64 }
|
||||
|
||||
fn from_str_radix<T: FromStrRadixHelper>(
|
||||
fn from_str_radix(
|
||||
src: &str,
|
||||
radix: u32,
|
||||
) -> Result<(T, NumericBound<NumWidth>), ParseIntError> {
|
||||
) -> Result<(IntValue, NumericBound<NumWidth>), IntErrorKind> {
|
||||
use self::IntErrorKind::*;
|
||||
use self::ParseIntError as PIE;
|
||||
|
||||
assert!(
|
||||
(2..=36).contains(&radix),
|
||||
|
@ -274,65 +233,119 @@ fn from_str_radix<T: FromStrRadixHelper>(
|
|||
radix
|
||||
);
|
||||
|
||||
if src.is_empty() {
|
||||
return Err(PIE { kind: Empty });
|
||||
}
|
||||
let (opt_exact_bound, src) = parse_literal_suffix(src);
|
||||
|
||||
let is_signed_ty = T::from_u32(0) > T::min_value();
|
||||
|
||||
// all valid digits are ascii, so we will just iterate over the utf8 bytes
|
||||
// and cast them to chars. .to_digit() will safely return None for anything
|
||||
// other than a valid ascii digit for the given radix, including the first-byte
|
||||
// of multi-byte sequences
|
||||
let src = src.as_bytes();
|
||||
|
||||
let (is_positive, digits) = match src[0] {
|
||||
b'+' => (true, &src[1..]),
|
||||
b'-' if is_signed_ty => (false, &src[1..]),
|
||||
_ => (true, src),
|
||||
use std::num::IntErrorKind as StdIEK;
|
||||
let result = match i128::from_str_radix(src, radix) {
|
||||
Ok(result) => IntValue::I128(result),
|
||||
Err(pie) => match pie.kind() {
|
||||
StdIEK::Empty => return Err(IntErrorKind::Empty),
|
||||
StdIEK::InvalidDigit => return Err(IntErrorKind::InvalidDigit),
|
||||
StdIEK::NegOverflow => return Err(IntErrorKind::Underflow),
|
||||
StdIEK::PosOverflow => {
|
||||
// try a u128
|
||||
match u128::from_str_radix(src, radix) {
|
||||
Ok(result) => IntValue::U128(result),
|
||||
Err(pie) => match pie.kind() {
|
||||
StdIEK::InvalidDigit => return Err(IntErrorKind::InvalidDigit),
|
||||
StdIEK::PosOverflow => return Err(IntErrorKind::Overflow),
|
||||
StdIEK::Empty | StdIEK::Zero | StdIEK::NegOverflow => unreachable!(),
|
||||
_ => unreachable!("I thought all possibilities were exhausted, but std::num added a new one")
|
||||
},
|
||||
}
|
||||
}
|
||||
StdIEK::Zero => unreachable!("Parsed a i128"),
|
||||
_ => unreachable!(
|
||||
"I thought all possibilities were exhausted, but std::num added a new one"
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
let (bound, digits) = parse_literal_suffix(digits);
|
||||
let (lower_bound, is_negative) = match result {
|
||||
IntValue::I128(num) => (lower_bound_of_int(num), num <= 0),
|
||||
IntValue::U128(_) => (IntWidth::U128, false),
|
||||
};
|
||||
|
||||
if digits.is_empty() {
|
||||
return Err(PIE { kind: Empty });
|
||||
match opt_exact_bound {
|
||||
None => {
|
||||
// TODO: use the lower bound
|
||||
Ok((result, NumericBound::None))
|
||||
}
|
||||
Some(bound @ NumWidth::Float(_)) => {
|
||||
// For now, assume floats can represent all integers
|
||||
// TODO: this is somewhat incorrect, revisit
|
||||
Ok((result, NumericBound::Exact(bound)))
|
||||
}
|
||||
Some(NumWidth::Int(exact_width)) => {
|
||||
// We need to check if the exact bound >= lower bound.
|
||||
if exact_width.is_superset(&lower_bound, is_negative) {
|
||||
// Great! Use the exact bound.
|
||||
Ok((result, NumericBound::Exact(NumWidth::Int(exact_width))))
|
||||
} else {
|
||||
// This is something like 200i8; the lower bound is u8, which holds strictly more
|
||||
// ints on the positive side than i8 does. Report an error depending on which side
|
||||
// of the integers we checked.
|
||||
let err = if is_negative {
|
||||
UnderflowsSuffix {
|
||||
suffix_type: exact_width.type_str(),
|
||||
min_value: exact_width.min_value(),
|
||||
}
|
||||
} else {
|
||||
OverflowsSuffix {
|
||||
suffix_type: exact_width.type_str(),
|
||||
max_value: exact_width.max_value(),
|
||||
}
|
||||
};
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut result = T::from_u32(0);
|
||||
if is_positive {
|
||||
// The number is positive
|
||||
for &c in digits {
|
||||
let x = match (c as char).to_digit(radix) {
|
||||
Some(x) => x,
|
||||
None => return Err(PIE { kind: InvalidDigit }),
|
||||
};
|
||||
result = match result.checked_mul(radix) {
|
||||
Some(result) => result,
|
||||
None => return Err(PIE { kind: Overflow }),
|
||||
};
|
||||
result = match result.checked_add(x) {
|
||||
Some(result) => result,
|
||||
None => return Err(PIE { kind: Overflow }),
|
||||
};
|
||||
fn lower_bound_of_int(result: i128) -> IntWidth {
|
||||
use IntWidth::*;
|
||||
if result >= 0 {
|
||||
// Positive
|
||||
let result = result as u128;
|
||||
if result > U64.max_value() {
|
||||
I128
|
||||
} else if result > I64.max_value() {
|
||||
U64
|
||||
} else if result > U32.max_value() {
|
||||
I64
|
||||
} else if result > I32.max_value() {
|
||||
U32
|
||||
} else if result > U16.max_value() {
|
||||
I32
|
||||
} else if result > I16.max_value() {
|
||||
U16
|
||||
} else if result > U8.max_value() {
|
||||
I16
|
||||
} else if result > I8.max_value() {
|
||||
U8
|
||||
} else {
|
||||
I8
|
||||
}
|
||||
} else {
|
||||
// The number is negative
|
||||
for &c in digits {
|
||||
let x = match (c as char).to_digit(radix) {
|
||||
Some(x) => x,
|
||||
None => return Err(PIE { kind: InvalidDigit }),
|
||||
};
|
||||
result = match result.checked_mul(radix) {
|
||||
Some(result) => result,
|
||||
None => return Err(PIE { kind: Underflow }),
|
||||
};
|
||||
result = match result.checked_sub(x) {
|
||||
Some(result) => result,
|
||||
None => return Err(PIE { kind: Underflow }),
|
||||
};
|
||||
// Negative
|
||||
if result < I64.min_value() {
|
||||
I128
|
||||
} else if result < I32.min_value() {
|
||||
I64
|
||||
} else if result < I16.min_value() {
|
||||
I32
|
||||
} else if result < I8.min_value() {
|
||||
I16
|
||||
} else {
|
||||
I8
|
||||
}
|
||||
}
|
||||
Ok((result, bound))
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
enum IntSign {
|
||||
Unsigned,
|
||||
Signed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
|
@ -350,6 +363,109 @@ pub enum IntWidth {
|
|||
Nat,
|
||||
}
|
||||
|
||||
impl IntWidth {
|
||||
/// Returns the `IntSign` and bit width of a variant.
|
||||
fn sign_and_width(&self) -> (IntSign, u32) {
|
||||
use IntSign::*;
|
||||
use IntWidth::*;
|
||||
match self {
|
||||
U8 => (Unsigned, 8),
|
||||
U16 => (Unsigned, 16),
|
||||
U32 => (Unsigned, 32),
|
||||
U64 => (Unsigned, 64),
|
||||
U128 => (Unsigned, 128),
|
||||
I8 => (Signed, 8),
|
||||
I16 => (Signed, 16),
|
||||
I32 => (Signed, 32),
|
||||
I64 => (Signed, 64),
|
||||
I128 => (Signed, 128),
|
||||
// TODO: this is platform specific!
|
||||
Nat => (Unsigned, 64),
|
||||
}
|
||||
}
|
||||
|
||||
fn type_str(&self) -> &'static str {
|
||||
use IntWidth::*;
|
||||
match self {
|
||||
U8 => "U8",
|
||||
U16 => "U16",
|
||||
U32 => "U32",
|
||||
U64 => "U64",
|
||||
U128 => "U128",
|
||||
I8 => "I8",
|
||||
I16 => "I16",
|
||||
I32 => "I32",
|
||||
I64 => "I64",
|
||||
I128 => "I128",
|
||||
Nat => "Nat",
|
||||
}
|
||||
}
|
||||
|
||||
fn max_value(&self) -> u128 {
|
||||
use IntWidth::*;
|
||||
match self {
|
||||
U8 => u8::MAX as u128,
|
||||
U16 => u16::MAX as u128,
|
||||
U32 => u32::MAX as u128,
|
||||
U64 => u64::MAX as u128,
|
||||
U128 => u128::MAX,
|
||||
I8 => i8::MAX as u128,
|
||||
I16 => i16::MAX as u128,
|
||||
I32 => i32::MAX as u128,
|
||||
I64 => i64::MAX as u128,
|
||||
I128 => i128::MAX as u128,
|
||||
// TODO: this is platform specific!
|
||||
Nat => u64::MAX as u128,
|
||||
}
|
||||
}
|
||||
|
||||
fn min_value(&self) -> i128 {
|
||||
use IntWidth::*;
|
||||
match self {
|
||||
U8 | U16 | U32 | U64 | U128 | Nat => 0,
|
||||
I8 => i8::MIN as i128,
|
||||
I16 => i16::MIN as i128,
|
||||
I32 => i32::MIN as i128,
|
||||
I64 => i64::MIN as i128,
|
||||
I128 => i128::MIN,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if `self` represents superset of integers that `lower_bound` represents, on a particular
|
||||
/// side of the integers relative to 0.
|
||||
///
|
||||
/// If `is_negative` is true, the negative side is checked; otherwise the positive side is checked.
|
||||
pub fn is_superset(&self, lower_bound: &Self, is_negative: bool) -> bool {
|
||||
use IntSign::*;
|
||||
|
||||
if is_negative {
|
||||
match (self.sign_and_width(), lower_bound.sign_and_width()) {
|
||||
((Signed, us), (Signed, lower_bound)) => us >= lower_bound,
|
||||
// Unsigned ints can never represent negative numbers; signed (non-zero width)
|
||||
// ints always can.
|
||||
((Unsigned, _), (Signed, _)) => false,
|
||||
((Signed, _), (Unsigned, _)) => true,
|
||||
// Trivially true; both can only express 0.
|
||||
((Unsigned, _), (Unsigned, _)) => true,
|
||||
}
|
||||
} else {
|
||||
match (self.sign_and_width(), lower_bound.sign_and_width()) {
|
||||
((Signed, us), (Signed, lower_bound))
|
||||
| ((Unsigned, us), (Unsigned, lower_bound)) => us >= lower_bound,
|
||||
|
||||
// Unsigned ints with the same bit width as their unsigned counterparts can always
|
||||
// express 2x more integers on the positive side as unsigned ints.
|
||||
((Unsigned, us), (Signed, lower_bound)) => us >= lower_bound,
|
||||
|
||||
// ...but that means signed int widths can represent less than their unsigned
|
||||
// counterparts, so the below is true iff the bit width is strictly greater. E.g.
|
||||
// i16 is a superset of u8, but i16 is not a superset of u16.
|
||||
((Signed, us), (Unsigned, lower_bound)) => us > lower_bound,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum FloatWidth {
|
||||
Dec,
|
||||
|
@ -374,28 +490,3 @@ where
|
|||
/// Must have exactly the width `W`.
|
||||
Exact(W),
|
||||
}
|
||||
|
||||
/// An error which can be returned when parsing an integer.
|
||||
///
|
||||
/// This error is used as the error type for the `from_str_radix()` functions
|
||||
/// on the primitive integer types, such as [`i8::from_str_radix`].
|
||||
///
|
||||
/// # Potential causes
|
||||
///
|
||||
/// Among other causes, `ParseIntError` can be thrown because of leading or trailing whitespace
|
||||
/// in the string e.g., when it is obtained from the standard input.
|
||||
/// Using the [`str.trim()`] method ensures that no whitespace remains before parsing.
|
||||
///
|
||||
/// [`str.trim()`]: ../../std/primitive.str.html#method.trim
|
||||
/// [`i8::from_str_radix`]: ../../std/primitive.i8.html#method.from_str_radix
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ParseIntError {
|
||||
kind: IntErrorKind,
|
||||
}
|
||||
|
||||
impl ParseIntError {
|
||||
/// Outputs the detailed cause of parsing an integer failing.
|
||||
pub fn kind(&self) -> &IntErrorKind {
|
||||
&self.kind
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::env::Env;
|
||||
use crate::expr::{canonicalize_expr, unescape_char, Expr, Output};
|
||||
use crate::expr::{canonicalize_expr, unescape_char, Expr, IntValue, Output};
|
||||
use crate::num::{
|
||||
finish_parsing_base, finish_parsing_float, finish_parsing_num, FloatWidth, IntWidth, NumWidth,
|
||||
NumericBound, ParsedNumResult,
|
||||
|
@ -29,8 +29,14 @@ pub enum Pattern {
|
|||
ext_var: Variable,
|
||||
destructs: Vec<Loc<RecordDestruct>>,
|
||||
},
|
||||
NumLiteral(Variable, Box<str>, i64, NumericBound<NumWidth>),
|
||||
IntLiteral(Variable, Variable, Box<str>, i64, NumericBound<IntWidth>),
|
||||
NumLiteral(Variable, Box<str>, IntValue, NumericBound<NumWidth>),
|
||||
IntLiteral(
|
||||
Variable,
|
||||
Variable,
|
||||
Box<str>,
|
||||
IntValue,
|
||||
NumericBound<IntWidth>,
|
||||
),
|
||||
FloatLiteral(Variable, Variable, Box<str>, f64, NumericBound<FloatWidth>),
|
||||
StrLiteral(Box<str>),
|
||||
Underscore,
|
||||
|
@ -247,10 +253,20 @@ pub fn canonicalize_pattern<'a>(
|
|||
let problem = MalformedPatternProblem::MalformedBase(base);
|
||||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
Ok((IntValue::U128(_), _)) if is_negative => {
|
||||
// Can't negate a u128; that doesn't fit in any integer literal type we support.
|
||||
let problem = MalformedPatternProblem::MalformedInt;
|
||||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
Ok((int, bound)) => {
|
||||
let sign_str = if is_negative { "-" } else { "" };
|
||||
let int_str = format!("{}{}", sign_str, int.to_string()).into_boxed_str();
|
||||
let i = if is_negative { -int } else { int };
|
||||
let i = match int {
|
||||
// Safety: this is fine because I128::MAX = |I128::MIN| - 1
|
||||
IntValue::I128(n) if is_negative => IntValue::I128(-n),
|
||||
IntValue::I128(n) => IntValue::I128(n),
|
||||
IntValue::U128(_) => unreachable!(),
|
||||
};
|
||||
Pattern::IntLiteral(var_store.fresh(), var_store.fresh(), int_str, i, bound)
|
||||
}
|
||||
},
|
||||
|
|
|
@ -15,7 +15,7 @@ mod test_can {
|
|||
use crate::helpers::{can_expr_with, test_home, CanExprOut};
|
||||
use bumpalo::Bump;
|
||||
use roc_can::expr::Expr::{self, *};
|
||||
use roc_can::expr::{ClosureData, Recursive};
|
||||
use roc_can::expr::{ClosureData, IntValue, Recursive};
|
||||
use roc_problem::can::{CycleEntry, FloatErrorKind, IntErrorKind, Problem, RuntimeError};
|
||||
use roc_region::all::{Position, Region};
|
||||
use std::{f64, i64};
|
||||
|
@ -47,7 +47,7 @@ mod test_can {
|
|||
|
||||
match actual_out.loc_expr.value {
|
||||
Expr::Int(_, _, _, actual, _) => {
|
||||
assert_eq!(expected, actual);
|
||||
assert_eq!(IntValue::I128(expected), actual);
|
||||
}
|
||||
actual => {
|
||||
panic!("Expected an Int *, but got: {:?}", actual);
|
||||
|
@ -55,13 +55,13 @@ mod test_can {
|
|||
}
|
||||
}
|
||||
|
||||
fn assert_can_num(input: &str, expected: i64) {
|
||||
fn assert_can_num(input: &str, expected: i128) {
|
||||
let arena = Bump::new();
|
||||
let actual_out = can_expr_with(&arena, test_home(), input);
|
||||
|
||||
match actual_out.loc_expr.value {
|
||||
Expr::Num(_, _, actual, _) => {
|
||||
assert_eq!(expected, actual);
|
||||
assert_eq!(IntValue::I128(expected), actual);
|
||||
}
|
||||
actual => {
|
||||
panic!("Expected a Num, but got: {:?}", actual);
|
||||
|
@ -79,7 +79,7 @@ mod test_can {
|
|||
fn int_too_large() {
|
||||
use roc_parse::ast::Base;
|
||||
|
||||
let string = (i64::MAX as i128 + 1).to_string();
|
||||
let string = "340_282_366_920_938_463_463_374_607_431_768_211_456".to_string();
|
||||
|
||||
assert_can(
|
||||
&string.clone(),
|
||||
|
@ -96,7 +96,7 @@ mod test_can {
|
|||
fn int_too_small() {
|
||||
use roc_parse::ast::Base;
|
||||
|
||||
let string = (i64::MIN as i128 - 1).to_string();
|
||||
let string = "-170_141_183_460_469_231_731_687_303_715_884_105_729".to_string();
|
||||
|
||||
assert_can(
|
||||
&string.clone(),
|
||||
|
@ -186,12 +186,12 @@ mod test_can {
|
|||
|
||||
#[test]
|
||||
fn num_max() {
|
||||
assert_can_num(&(i64::MAX.to_string()), i64::MAX);
|
||||
assert_can_num(&(i64::MAX.to_string()), i64::MAX.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn num_min() {
|
||||
assert_can_num(&(i64::MIN.to_string()), i64::MIN);
|
||||
assert_can_num(&(i64::MIN.to_string()), i64::MIN.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -771,11 +771,13 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
|
|||
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()
|
||||
int_with_precision(env, *int, *int_width).into()
|
||||
}
|
||||
_ => panic!("Invalid layout for int literal = {:?}", layout),
|
||||
},
|
||||
|
||||
U128(int) => const_u128(env, *int).into(),
|
||||
|
||||
Float(float) => match layout {
|
||||
Layout::Builtin(Builtin::Float(float_width)) => {
|
||||
float_with_precision(env, *float, *float_width)
|
||||
|
@ -3073,6 +3075,19 @@ fn const_i128<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, value: i128) -> IntValu
|
|||
.const_int_arbitrary_precision(&[a, b])
|
||||
}
|
||||
|
||||
fn const_u128<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, value: u128) -> IntValue<'ctx> {
|
||||
// truncate the lower 64 bits
|
||||
let value = value as u128;
|
||||
let a = value as u64;
|
||||
|
||||
// get the upper 64 bits
|
||||
let b = (value >> 64) as u64;
|
||||
|
||||
env.context
|
||||
.i128_type()
|
||||
.const_int_arbitrary_precision(&[a, b])
|
||||
}
|
||||
|
||||
fn build_switch_ir<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
|
|
|
@ -1638,7 +1638,9 @@ fn literal_spec(
|
|||
|
||||
match literal {
|
||||
Str(_) => new_static_string(builder, block),
|
||||
Int(_) | Float(_) | Decimal(_) | Bool(_) | Byte(_) => builder.add_make_tuple(block, &[]),
|
||||
Int(_) | U128(_) | Float(_) | Decimal(_) | Bool(_) | Byte(_) => {
|
||||
builder.add_make_tuple(block, &[])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -87,6 +87,7 @@ enum Test<'a> {
|
|||
arguments: Vec<(Pattern<'a>, Layout<'a>)>,
|
||||
},
|
||||
IsInt(i128, IntWidth),
|
||||
IsU128(u128),
|
||||
IsFloat(u64, FloatWidth),
|
||||
IsDecimal(RocDec),
|
||||
IsStr(Box<str>),
|
||||
|
@ -136,6 +137,10 @@ impl<'a> Hash for Test<'a> {
|
|||
state.write_u8(6);
|
||||
v.0.hash(state);
|
||||
}
|
||||
IsU128(v) => {
|
||||
state.write_u8(7);
|
||||
v.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -311,6 +316,7 @@ fn tests_are_complete_help(last_test: &Test, number_of_tests: usize) -> bool {
|
|||
Test::IsByte { num_alts, .. } => number_of_tests == *num_alts,
|
||||
Test::IsBit(_) => number_of_tests == 2,
|
||||
Test::IsInt(_, _) => false,
|
||||
Test::IsU128(_) => false,
|
||||
Test::IsFloat(_, _) => false,
|
||||
Test::IsDecimal(_) => false,
|
||||
Test::IsStr(_) => false,
|
||||
|
@ -565,6 +571,7 @@ fn test_at_path<'a>(
|
|||
num_alts: union.alternatives.len(),
|
||||
},
|
||||
IntLiteral(v, precision) => IsInt(*v, *precision),
|
||||
U128Literal(v) => IsU128(*v),
|
||||
FloatLiteral(v, precision) => IsFloat(*v, *precision),
|
||||
DecimalLiteral(v) => IsDecimal(*v),
|
||||
StrLiteral(v) => IsStr(v.clone()),
|
||||
|
@ -823,6 +830,18 @@ fn to_relevant_branch_help<'a>(
|
|||
_ => None,
|
||||
},
|
||||
|
||||
U128Literal(int) => match test {
|
||||
IsU128(is_int) if int == *is_int => {
|
||||
start.extend(end);
|
||||
Some(Branch {
|
||||
goal: branch.goal,
|
||||
guard: branch.guard.clone(),
|
||||
patterns: start,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
|
||||
FloatLiteral(float, p1) => match test {
|
||||
IsFloat(test_float, p2) if float == *test_float => {
|
||||
debug_assert_eq!(p1, *p2);
|
||||
|
@ -934,6 +953,7 @@ fn needs_tests(pattern: &Pattern) -> bool {
|
|||
| BitLiteral { .. }
|
||||
| EnumLiteral { .. }
|
||||
| IntLiteral(_, _)
|
||||
| U128Literal(_)
|
||||
| FloatLiteral(_, _)
|
||||
| DecimalLiteral(_)
|
||||
| StrLiteral(_) => true,
|
||||
|
@ -1305,6 +1325,14 @@ fn test_to_equality<'a>(
|
|||
(stores, lhs_symbol, rhs_symbol, None)
|
||||
}
|
||||
|
||||
Test::IsU128(test_int) => {
|
||||
let lhs = Expr::Literal(Literal::U128(test_int));
|
||||
let lhs_symbol = env.unique_symbol();
|
||||
stores.push((lhs_symbol, Layout::int_width(IntWidth::U128), lhs));
|
||||
|
||||
(stores, lhs_symbol, rhs_symbol, None)
|
||||
}
|
||||
|
||||
Test::IsFloat(test_int, precision) => {
|
||||
// TODO maybe we can actually use i64 comparison here?
|
||||
let test_float = f64::from_bits(test_int as u64);
|
||||
|
|
|
@ -54,6 +54,7 @@ pub enum Pattern {
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Literal {
|
||||
Int(i128),
|
||||
U128(u128),
|
||||
Bit(bool),
|
||||
Byte(u8),
|
||||
Float(u64),
|
||||
|
@ -66,6 +67,7 @@ fn simplify(pattern: &crate::ir::Pattern) -> Pattern {
|
|||
|
||||
match pattern {
|
||||
IntLiteral(v, _) => Literal(Literal::Int(*v)),
|
||||
U128Literal(v) => Literal(Literal::U128(*v)),
|
||||
FloatLiteral(v, _) => Literal(Literal::Float(*v)),
|
||||
DecimalLiteral(v) => Literal(Literal::Decimal(*v)),
|
||||
StrLiteral(v) => Literal(Literal::Str(v.clone())),
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::layout::{
|
|||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
use roc_can::expr::ClosureData;
|
||||
use roc_can::expr::{ClosureData, IntValue};
|
||||
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
|
||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||
use roc_module::low_level::LowLevel;
|
||||
|
@ -1278,6 +1278,7 @@ impl ModifyRc {
|
|||
pub enum Literal<'a> {
|
||||
// Literals
|
||||
Int(i128),
|
||||
U128(u128),
|
||||
Float(f64),
|
||||
Decimal(RocDec),
|
||||
Str(&'a str),
|
||||
|
@ -1524,6 +1525,7 @@ impl<'a> Literal<'a> {
|
|||
|
||||
match self {
|
||||
Int(lit) => alloc.text(format!("{}i64", lit)),
|
||||
U128(lit) => alloc.text(format!("{}u128", lit)),
|
||||
Float(lit) => alloc.text(format!("{}f64", lit)),
|
||||
// TODO: Add proper Dec.to_str
|
||||
Decimal(lit) => alloc.text(format!("{}Dec", lit.0)),
|
||||
|
@ -3011,7 +3013,10 @@ fn try_make_literal<'a>(
|
|||
match can_expr {
|
||||
Int(_, precision, _, int, _bound) => {
|
||||
match num_argument_to_int_or_float(env.subs, env.target_info, *precision, false) {
|
||||
IntOrFloat::Int(_) => Some(Literal::Int(*int)),
|
||||
IntOrFloat::Int(_) => Some(match *int {
|
||||
IntValue::I128(n) => Literal::Int(n),
|
||||
IntValue::U128(n) => Literal::U128(n),
|
||||
}),
|
||||
_ => unreachable!("unexpected float precision for integer"),
|
||||
}
|
||||
}
|
||||
|
@ -3039,8 +3044,14 @@ fn try_make_literal<'a>(
|
|||
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(Literal::Int((*num).into())),
|
||||
IntOrFloat::Float(_) => Some(Literal::Float(*num as f64)),
|
||||
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(n as f64),
|
||||
IntValue::U128(n) => Literal::Float(n as f64),
|
||||
}),
|
||||
IntOrFloat::DecimalFloatType => {
|
||||
let dec = match RocDec::from_str(num_str) {
|
||||
Some(d) => d,
|
||||
|
@ -3076,7 +3087,10 @@ pub fn with_hole<'a>(
|
|||
match num_argument_to_int_or_float(env.subs, env.target_info, precision, false) {
|
||||
IntOrFloat::Int(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Int(int)),
|
||||
Expr::Literal(match int {
|
||||
IntValue::I128(n) => Literal::Int(n),
|
||||
IntValue::U128(n) => Literal::U128(n),
|
||||
}),
|
||||
Layout::Builtin(Builtin::Int(precision)),
|
||||
hole,
|
||||
),
|
||||
|
@ -3120,13 +3134,19 @@ pub fn with_hole<'a>(
|
|||
match num_argument_to_int_or_float(env.subs, env.target_info, var, false) {
|
||||
IntOrFloat::Int(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Int(num.into())),
|
||||
Expr::Literal(match num {
|
||||
IntValue::I128(n) => Literal::Int(n),
|
||||
IntValue::U128(n) => Literal::U128(n),
|
||||
}),
|
||||
Layout::int_width(precision),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::Float(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Float(num as f64)),
|
||||
Expr::Literal(match num {
|
||||
IntValue::I128(n) => Literal::Float(n as f64),
|
||||
IntValue::U128(n) => Literal::Float(n as f64),
|
||||
}),
|
||||
Layout::float_width(precision),
|
||||
hole,
|
||||
),
|
||||
|
@ -6211,6 +6231,7 @@ fn store_pattern_help<'a>(
|
|||
return StorePattern::NotProductive(stmt);
|
||||
}
|
||||
IntLiteral(_, _)
|
||||
| U128Literal(_)
|
||||
| FloatLiteral(_, _)
|
||||
| DecimalLiteral(_)
|
||||
| EnumLiteral { .. }
|
||||
|
@ -7583,6 +7604,7 @@ fn call_specialized_proc<'a>(
|
|||
pub enum Pattern<'a> {
|
||||
Identifier(Symbol),
|
||||
Underscore,
|
||||
U128Literal(u128),
|
||||
IntLiteral(i128, IntWidth),
|
||||
FloatLiteral(u64, FloatWidth),
|
||||
DecimalLiteral(RocDec),
|
||||
|
@ -7664,7 +7686,13 @@ fn from_can_pattern_help<'a>(
|
|||
Identifier(symbol) => Ok(Pattern::Identifier(*symbol)),
|
||||
IntLiteral(_, precision_var, _, int, _bound) => {
|
||||
match num_argument_to_int_or_float(env.subs, env.target_info, *precision_var, false) {
|
||||
IntOrFloat::Int(precision) => Ok(Pattern::IntLiteral(*int as i128, precision)),
|
||||
IntOrFloat::Int(precision) => {
|
||||
let int = match *int {
|
||||
IntValue::I128(n) => Pattern::IntLiteral(n, precision),
|
||||
IntValue::U128(n) => Pattern::U128Literal(n),
|
||||
};
|
||||
Ok(int)
|
||||
}
|
||||
other => {
|
||||
panic!(
|
||||
"Invalid precision for int pattern: {:?} has {:?}",
|
||||
|
@ -7706,8 +7734,18 @@ fn from_can_pattern_help<'a>(
|
|||
}
|
||||
NumLiteral(var, num_str, num, _bound) => {
|
||||
match num_argument_to_int_or_float(env.subs, env.target_info, *var, false) {
|
||||
IntOrFloat::Int(precision) => Ok(Pattern::IntLiteral(*num as i128, precision)),
|
||||
IntOrFloat::Float(precision) => Ok(Pattern::FloatLiteral(*num as u64, precision)),
|
||||
IntOrFloat::Int(precision) => Ok(match num {
|
||||
IntValue::I128(num) => Pattern::IntLiteral(*num, precision),
|
||||
IntValue::U128(num) => Pattern::U128Literal(*num),
|
||||
}),
|
||||
IntOrFloat::Float(precision) => {
|
||||
// TODO: this may be lossy
|
||||
let num = match *num {
|
||||
IntValue::I128(n) => f64::to_bits(n as f64),
|
||||
IntValue::U128(n) => f64::to_bits(n as f64),
|
||||
};
|
||||
Ok(Pattern::FloatLiteral(num, precision))
|
||||
}
|
||||
IntOrFloat::DecimalFloatType => {
|
||||
let dec = match RocDec::from_str(num_str) {
|
||||
Some(d) => d,
|
||||
|
|
|
@ -109,6 +109,16 @@ pub enum IntErrorKind {
|
|||
Underflow,
|
||||
/// This is an integer, but it has a float numeric suffix.
|
||||
FloatSuffix,
|
||||
/// The integer literal overflows the width of the suffix associated with it.
|
||||
OverflowsSuffix {
|
||||
suffix_type: &'static str,
|
||||
max_value: u128,
|
||||
},
|
||||
/// The integer literal underflows the width of the suffix associated with it.
|
||||
UnderflowsSuffix {
|
||||
suffix_type: &'static str,
|
||||
min_value: i128,
|
||||
},
|
||||
}
|
||||
|
||||
/// Enum to store the various types of errors that can cause parsing a float to fail.
|
||||
|
|
|
@ -26,6 +26,8 @@ const VALUE_NOT_EXPOSED: &str = "NOT EXPOSED";
|
|||
const MODULE_NOT_IMPORTED: &str = "MODULE NOT IMPORTED";
|
||||
const NESTED_DATATYPE: &str = "NESTED DATATYPE";
|
||||
const CONFLICTING_NUMBER_SUFFIX: &str = "CONFLICTING NUMBER SUFFIX";
|
||||
const NUMBER_OVERFLOWS_SUFFIX: &str = "NUMBER OVERFLOWS SUFFIX";
|
||||
const NUMBER_UNDERFLOWS_SUFFIX: &str = "NUMBER UNDERFLOWS SUFFIX";
|
||||
|
||||
pub fn can_problem<'b>(
|
||||
alloc: &'b RocDocAllocator<'b>,
|
||||
|
@ -1136,10 +1138,28 @@ fn pretty_runtime_error<'b>(
|
|||
}
|
||||
RuntimeError::InvalidInt(error_kind @ IntErrorKind::Underflow, _base, region, _raw_str)
|
||||
| RuntimeError::InvalidInt(error_kind @ IntErrorKind::Overflow, _base, region, _raw_str) => {
|
||||
let big_or_small = if let IntErrorKind::Underflow = error_kind {
|
||||
"small"
|
||||
let (big_or_small, info) = if let IntErrorKind::Underflow = error_kind {
|
||||
(
|
||||
"small",
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(
|
||||
"The smallest number representable in Roc is the minimum I128 value, ",
|
||||
),
|
||||
alloc.int_literal(i128::MIN),
|
||||
alloc.text("."),
|
||||
]),
|
||||
)
|
||||
} else {
|
||||
"big"
|
||||
(
|
||||
"big",
|
||||
alloc.concat(vec![
|
||||
alloc.reflow(
|
||||
"The largest number representable in Roc is the maximum U128 value, ",
|
||||
),
|
||||
alloc.int_literal(u128::MAX),
|
||||
alloc.text("."),
|
||||
]),
|
||||
)
|
||||
};
|
||||
|
||||
let tip = alloc
|
||||
|
@ -1153,7 +1173,7 @@ fn pretty_runtime_error<'b>(
|
|||
alloc.reflow(":"),
|
||||
]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.reflow("Roc uses signed 64-bit integers, allowing values between −9_223_372_036_854_775_808 and 9_223_372_036_854_775_807."),
|
||||
info,
|
||||
tip,
|
||||
]);
|
||||
|
||||
|
@ -1169,6 +1189,56 @@ fn pretty_runtime_error<'b>(
|
|||
|
||||
title = CONFLICTING_NUMBER_SUFFIX;
|
||||
}
|
||||
RuntimeError::InvalidInt(
|
||||
IntErrorKind::OverflowsSuffix {
|
||||
suffix_type,
|
||||
max_value,
|
||||
},
|
||||
_base,
|
||||
region,
|
||||
_raw_str,
|
||||
) => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![alloc.reflow(
|
||||
"This integer literal overflows the type indicated by its suffix:",
|
||||
)]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.tip().append(alloc.concat(vec![
|
||||
alloc.reflow("The suffix indicates this integer is a "),
|
||||
alloc.type_str(suffix_type),
|
||||
alloc.reflow(", whose maximum value is "),
|
||||
alloc.int_literal(max_value),
|
||||
alloc.reflow("."),
|
||||
])),
|
||||
]);
|
||||
|
||||
title = NUMBER_OVERFLOWS_SUFFIX;
|
||||
}
|
||||
RuntimeError::InvalidInt(
|
||||
IntErrorKind::UnderflowsSuffix {
|
||||
suffix_type,
|
||||
min_value,
|
||||
},
|
||||
_base,
|
||||
region,
|
||||
_raw_str,
|
||||
) => {
|
||||
doc = alloc.stack(vec![
|
||||
alloc.concat(vec![alloc.reflow(
|
||||
"This integer literal underflows the type indicated by its suffix:",
|
||||
)]),
|
||||
alloc.region(lines.convert_region(region)),
|
||||
alloc.tip().append(alloc.concat(vec![
|
||||
alloc.reflow("The suffix indicates this integer is a "),
|
||||
alloc.type_str(suffix_type),
|
||||
alloc.reflow(", whose minimum value is "),
|
||||
alloc.int_literal(min_value),
|
||||
alloc.reflow("."),
|
||||
])),
|
||||
]);
|
||||
|
||||
title = NUMBER_UNDERFLOWS_SUFFIX;
|
||||
}
|
||||
RuntimeError::InvalidOptionalValue {
|
||||
field_name,
|
||||
field_region,
|
||||
|
|
|
@ -148,6 +148,7 @@ fn pattern_to_doc_help<'b>(
|
|||
Anything => alloc.text("_"),
|
||||
Literal(l) => match l {
|
||||
Int(i) => alloc.text(i.to_string()),
|
||||
U128(i) => alloc.text(i.to_string()),
|
||||
Bit(true) => alloc.text("True"),
|
||||
Bit(false) => alloc.text("False"),
|
||||
Byte(b) => alloc.text(b.to_string()),
|
||||
|
|
|
@ -632,6 +632,39 @@ impl<'a> RocDocAllocator<'a> {
|
|||
self.text(format!("{}", ident.as_inline_str()))
|
||||
.annotate(Annotation::Symbol)
|
||||
}
|
||||
|
||||
pub fn int_literal<I>(&'a self, int: I) -> DocBuilder<'a, Self, Annotation>
|
||||
where
|
||||
I: ToString,
|
||||
{
|
||||
let s = int.to_string();
|
||||
|
||||
let is_negative = s.starts_with('-');
|
||||
|
||||
if s.len() < 7 + (is_negative as usize) {
|
||||
// If the number is not at least in the millions, return it as-is.
|
||||
return self.text(s);
|
||||
}
|
||||
|
||||
// Otherwise, let's add numeric separators to make it easier to read.
|
||||
let mut result = String::with_capacity(s.len() + s.len() / 3);
|
||||
for (idx, c) in s
|
||||
.get((is_negative as usize)..)
|
||||
.unwrap()
|
||||
.chars()
|
||||
.rev()
|
||||
.enumerate()
|
||||
{
|
||||
if idx != 0 && idx % 3 == 0 {
|
||||
result.push('_');
|
||||
}
|
||||
result.push(c);
|
||||
}
|
||||
if is_negative {
|
||||
result.push('-');
|
||||
}
|
||||
self.text(result.chars().rev().collect::<String>())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
|
|
@ -3409,15 +3409,15 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
x = 9_223_372_036_854_775_807_000
|
||||
x = 170_141_183_460_469_231_731_687_303_715_884_105_728_000
|
||||
|
||||
y = -9_223_372_036_854_775_807_000
|
||||
y = -170_141_183_460_469_231_731_687_303_715_884_105_728_000
|
||||
|
||||
h = 0x8FFF_FFFF_FFFF_FFFF
|
||||
l = -0x8FFF_FFFF_FFFF_FFFF
|
||||
h = 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
|
||||
l = -0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
|
||||
|
||||
minlit = -9_223_372_036_854_775_808
|
||||
maxlit = 9_223_372_036_854_775_807
|
||||
minlit = -170_141_183_460_469_231_731_687_303_715_884_105_728
|
||||
maxlit = 340_282_366_920_938_463_463_374_607_431_768_211_455
|
||||
|
||||
x + y + h + l + minlit + maxlit
|
||||
"#
|
||||
|
@ -3428,11 +3428,11 @@ mod test_reporting {
|
|||
|
||||
This integer literal is too big:
|
||||
|
||||
1│ x = 9_223_372_036_854_775_807_000
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
1│ x = 170_141_183_460_469_231_731_687_303_715_884_105_728_000
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Roc uses signed 64-bit integers, allowing values between
|
||||
−9_223_372_036_854_775_808 and 9_223_372_036_854_775_807.
|
||||
The largest number representable in Roc is the maximum U128 value,
|
||||
340_282_366_920_938_463_463_374_607_431_768_211_455.
|
||||
|
||||
Tip: Learn more about number literals at TODO
|
||||
|
||||
|
@ -3440,11 +3440,11 @@ mod test_reporting {
|
|||
|
||||
This integer literal is too small:
|
||||
|
||||
3│ y = -9_223_372_036_854_775_807_000
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
3│ y = -170_141_183_460_469_231_731_687_303_715_884_105_728_000
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Roc uses signed 64-bit integers, allowing values between
|
||||
−9_223_372_036_854_775_808 and 9_223_372_036_854_775_807.
|
||||
The smallest number representable in Roc is the minimum I128 value,
|
||||
-170_141_183_460_469_231_731_687_303_715_884_105_728.
|
||||
|
||||
Tip: Learn more about number literals at TODO
|
||||
|
||||
|
@ -3452,11 +3452,11 @@ mod test_reporting {
|
|||
|
||||
This integer literal is too big:
|
||||
|
||||
5│ h = 0x8FFF_FFFF_FFFF_FFFF
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
5│ h = 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Roc uses signed 64-bit integers, allowing values between
|
||||
−9_223_372_036_854_775_808 and 9_223_372_036_854_775_807.
|
||||
The largest number representable in Roc is the maximum U128 value,
|
||||
340_282_366_920_938_463_463_374_607_431_768_211_455.
|
||||
|
||||
Tip: Learn more about number literals at TODO
|
||||
|
||||
|
@ -3464,11 +3464,11 @@ mod test_reporting {
|
|||
|
||||
This integer literal is too small:
|
||||
|
||||
6│ l = -0x8FFF_FFFF_FFFF_FFFF
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
6│ l = -0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Roc uses signed 64-bit integers, allowing values between
|
||||
−9_223_372_036_854_775_808 and 9_223_372_036_854_775_807.
|
||||
The smallest number representable in Roc is the minimum I128 value,
|
||||
-170_141_183_460_469_231_731_687_303_715_884_105_728.
|
||||
|
||||
Tip: Learn more about number literals at TODO
|
||||
"#
|
||||
|
@ -7527,4 +7527,364 @@ I need all branches in an `if` to have the same type!
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn u8_overflow() {
|
||||
report_problem_as(
|
||||
"256u8",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────────────────────────────
|
||||
|
||||
This integer literal overflows the type indicated by its suffix:
|
||||
|
||||
1│ 256u8
|
||||
^^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a U8, whose maximum value is
|
||||
255.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negative_u8() {
|
||||
report_problem_as(
|
||||
"-1u8",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────────────────────────────
|
||||
|
||||
This integer literal underflows the type indicated by its suffix:
|
||||
|
||||
1│ -1u8
|
||||
^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a U8, whose minimum value is
|
||||
0.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn u16_overflow() {
|
||||
report_problem_as(
|
||||
"65536u16",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────────────────────────────
|
||||
|
||||
This integer literal overflows the type indicated by its suffix:
|
||||
|
||||
1│ 65536u16
|
||||
^^^^^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a U16, whose maximum value
|
||||
is 65535.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negative_u16() {
|
||||
report_problem_as(
|
||||
"-1u16",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────────────────────────────
|
||||
|
||||
This integer literal underflows the type indicated by its suffix:
|
||||
|
||||
1│ -1u16
|
||||
^^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a U16, whose minimum value
|
||||
is 0.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn u32_overflow() {
|
||||
report_problem_as(
|
||||
"4_294_967_296u32",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────────────────────────────
|
||||
|
||||
This integer literal overflows the type indicated by its suffix:
|
||||
|
||||
1│ 4_294_967_296u32
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a U32, whose maximum value
|
||||
is 4_294_967_295.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negative_u32() {
|
||||
report_problem_as(
|
||||
"-1u32",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────────────────────────────
|
||||
|
||||
This integer literal underflows the type indicated by its suffix:
|
||||
|
||||
1│ -1u32
|
||||
^^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a U32, whose minimum value
|
||||
is 0.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn u64_overflow() {
|
||||
report_problem_as(
|
||||
"18_446_744_073_709_551_616u64",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────────────────────────────
|
||||
|
||||
This integer literal overflows the type indicated by its suffix:
|
||||
|
||||
1│ 18_446_744_073_709_551_616u64
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a U64, whose maximum value
|
||||
is 18_446_744_073_709_551_615.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negative_u64() {
|
||||
report_problem_as(
|
||||
"-1u64",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────────────────────────────
|
||||
|
||||
This integer literal underflows the type indicated by its suffix:
|
||||
|
||||
1│ -1u64
|
||||
^^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a U64, whose minimum value
|
||||
is 0.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negative_u128() {
|
||||
report_problem_as(
|
||||
"-1u128",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────────────────────────────
|
||||
|
||||
This integer literal underflows the type indicated by its suffix:
|
||||
|
||||
1│ -1u128
|
||||
^^^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a U128, whose minimum value
|
||||
is 0.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i8_overflow() {
|
||||
report_problem_as(
|
||||
"128i8",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────────────────────────────
|
||||
|
||||
This integer literal overflows the type indicated by its suffix:
|
||||
|
||||
1│ 128i8
|
||||
^^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a I8, whose maximum value is
|
||||
127.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i8_underflow() {
|
||||
report_problem_as(
|
||||
"-129i8",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────────────────────────────
|
||||
|
||||
This integer literal underflows the type indicated by its suffix:
|
||||
|
||||
1│ -129i8
|
||||
^^^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a I8, whose minimum value is
|
||||
-128.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i16_overflow() {
|
||||
report_problem_as(
|
||||
"32768i16",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────────────────────────────
|
||||
|
||||
This integer literal overflows the type indicated by its suffix:
|
||||
|
||||
1│ 32768i16
|
||||
^^^^^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a I16, whose maximum value
|
||||
is 32767.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i16_underflow() {
|
||||
report_problem_as(
|
||||
"-32769i16",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────────────────────────────
|
||||
|
||||
This integer literal underflows the type indicated by its suffix:
|
||||
|
||||
1│ -32769i16
|
||||
^^^^^^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a I16, whose minimum value
|
||||
is -32768.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i32_overflow() {
|
||||
report_problem_as(
|
||||
"2_147_483_648i32",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────────────────────────────
|
||||
|
||||
This integer literal overflows the type indicated by its suffix:
|
||||
|
||||
1│ 2_147_483_648i32
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a I32, whose maximum value
|
||||
is 2_147_483_647.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i32_underflow() {
|
||||
report_problem_as(
|
||||
"-2_147_483_649i32",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────────────────────────────
|
||||
|
||||
This integer literal underflows the type indicated by its suffix:
|
||||
|
||||
1│ -2_147_483_649i32
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a I32, whose minimum value
|
||||
is -2_147_483_648.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i64_overflow() {
|
||||
report_problem_as(
|
||||
"9_223_372_036_854_775_808i64",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────────────────────────────
|
||||
|
||||
This integer literal overflows the type indicated by its suffix:
|
||||
|
||||
1│ 9_223_372_036_854_775_808i64
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a I64, whose maximum value
|
||||
is 9_223_372_036_854_775_807.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i64_underflow() {
|
||||
report_problem_as(
|
||||
"-9_223_372_036_854_775_809i64",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────────────────────────────
|
||||
|
||||
This integer literal underflows the type indicated by its suffix:
|
||||
|
||||
1│ -9_223_372_036_854_775_809i64
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a I64, whose minimum value
|
||||
is -9_223_372_036_854_775_808.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i128_overflow() {
|
||||
report_problem_as(
|
||||
"170_141_183_460_469_231_731_687_303_715_884_105_728i128",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────────────────────────────
|
||||
|
||||
This integer literal overflows the type indicated by its suffix:
|
||||
|
||||
1│ 170_141_183_460_469_231_731_687_303_715_884_105_728i128
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Tip: The suffix indicates this integer is a I128, whose maximum value
|
||||
is 170_141_183_460_469_231_731_687_303_715_884_105_727.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue