First pass

This commit is contained in:
ayazhafiz 2022-02-04 08:46:27 -05:00
parent d25e891fb0
commit 5e0d90ac53
8 changed files with 391 additions and 263 deletions

View file

@ -1,7 +1,7 @@
use crate::def::Def; use crate::def::Def;
use crate::expr::{self, ClosureData, Expr::*, IntValue}; use crate::expr::{self, ClosureData, Expr::*, IntValue};
use crate::expr::{Expr, Field, Recursive}; use crate::expr::{Expr, Field, Recursive};
use crate::num::{FloatWidth, IntWidth, NumWidth, NumericBound}; use crate::num::{FloatBound, IntBound, IntWidth, NumericBound};
use crate::pattern::Pattern; use crate::pattern::Pattern;
use roc_collections::all::SendMap; use roc_collections::all::SendMap;
use roc_module::called_via::CalledVia; use roc_module::called_via::CalledVia;
@ -867,7 +867,7 @@ fn num_is_odd(symbol: Symbol, var_store: &mut VarStore) -> Def {
args: vec![ args: vec![
( (
arg_var, arg_var,
int::<i128>(var_store.fresh(), var_store.fresh(), 1, num_no_bound()), int::<i128>(var_store.fresh(), var_store.fresh(), 1, int_no_bound()),
), ),
( (
arg_var, arg_var,
@ -965,7 +965,7 @@ fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def {
(float_var, Var(Symbol::ARG_1)), (float_var, Var(Symbol::ARG_1)),
( (
float_var, float_var,
float(unbound_zero_var, precision_var, 0.0, num_no_bound()), float(unbound_zero_var, precision_var, 0.0, float_no_bound()),
), ),
], ],
ret_var: bool_var, ret_var: bool_var,
@ -1014,7 +1014,7 @@ fn num_log(symbol: Symbol, var_store: &mut VarStore) -> Def {
(float_var, Var(Symbol::ARG_1)), (float_var, Var(Symbol::ARG_1)),
( (
float_var, float_var,
float(unbound_zero_var, precision_var, 0.0, num_no_bound()), float(unbound_zero_var, precision_var, 0.0, float_no_bound()),
), ),
], ],
ret_var: bool_var, ret_var: bool_var,
@ -1253,162 +1253,82 @@ fn num_int_cast(symbol: Symbol, var_store: &mut VarStore) -> Def {
/// Num.minI8: I8 /// Num.minI8: I8
fn num_min_i8(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_min_i8(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<i8>( int_min_or_max::<i8>(symbol, var_store, i8::MIN, IntBound::Exact(IntWidth::I8))
symbol,
var_store,
i8::MIN,
NumericBound::Exact(IntWidth::I8),
)
} }
/// Num.maxI8: I8 /// Num.maxI8: I8
fn num_max_i8(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_max_i8(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<i8>( int_min_or_max::<i8>(symbol, var_store, i8::MAX, IntBound::Exact(IntWidth::I8))
symbol,
var_store,
i8::MAX,
NumericBound::Exact(IntWidth::I8),
)
} }
/// Num.minU8: U8 /// Num.minU8: U8
fn num_min_u8(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_min_u8(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<u8>( int_min_or_max::<u8>(symbol, var_store, u8::MIN, IntBound::Exact(IntWidth::U8))
symbol,
var_store,
u8::MIN,
NumericBound::Exact(IntWidth::U8),
)
} }
/// Num.maxU8: U8 /// Num.maxU8: U8
fn num_max_u8(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_max_u8(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<u8>( int_min_or_max::<u8>(symbol, var_store, u8::MAX, IntBound::Exact(IntWidth::U8))
symbol,
var_store,
u8::MAX,
NumericBound::Exact(IntWidth::U8),
)
} }
/// Num.minI16: I16 /// Num.minI16: I16
fn num_min_i16(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_min_i16(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<i16>( int_min_or_max::<i16>(symbol, var_store, i16::MIN, IntBound::Exact(IntWidth::I16))
symbol,
var_store,
i16::MIN,
NumericBound::Exact(IntWidth::I16),
)
} }
/// Num.maxI16: I16 /// Num.maxI16: I16
fn num_max_i16(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_max_i16(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<i16>( int_min_or_max::<i16>(symbol, var_store, i16::MAX, IntBound::Exact(IntWidth::I16))
symbol,
var_store,
i16::MAX,
NumericBound::Exact(IntWidth::I16),
)
} }
/// Num.minU16: U16 /// Num.minU16: U16
fn num_min_u16(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_min_u16(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<u16>( int_min_or_max::<u16>(symbol, var_store, u16::MIN, IntBound::Exact(IntWidth::U16))
symbol,
var_store,
u16::MIN,
NumericBound::Exact(IntWidth::U16),
)
} }
/// Num.maxU16: U16 /// Num.maxU16: U16
fn num_max_u16(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_max_u16(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<u16>( int_min_or_max::<u16>(symbol, var_store, u16::MAX, IntBound::Exact(IntWidth::U16))
symbol,
var_store,
u16::MAX,
NumericBound::Exact(IntWidth::U16),
)
} }
/// Num.minI32: I32 /// Num.minI32: I32
fn num_min_i32(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_min_i32(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<i32>( int_min_or_max::<i32>(symbol, var_store, i32::MIN, IntBound::Exact(IntWidth::I32))
symbol,
var_store,
i32::MIN,
NumericBound::Exact(IntWidth::I32),
)
} }
/// Num.maxI32: I32 /// Num.maxI32: I32
fn num_max_i32(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_max_i32(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<i32>( int_min_or_max::<i32>(symbol, var_store, i32::MAX, IntBound::Exact(IntWidth::I32))
symbol,
var_store,
i32::MAX,
NumericBound::Exact(IntWidth::I32),
)
} }
/// Num.minU32: U32 /// Num.minU32: U32
fn num_min_u32(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_min_u32(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<u32>( int_min_or_max::<u32>(symbol, var_store, u32::MIN, IntBound::Exact(IntWidth::U32))
symbol,
var_store,
u32::MIN,
NumericBound::Exact(IntWidth::U32),
)
} }
/// Num.maxU32: U32 /// Num.maxU32: U32
fn num_max_u32(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_max_u32(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<u32>( int_min_or_max::<u32>(symbol, var_store, u32::MAX, IntBound::Exact(IntWidth::U32))
symbol,
var_store,
u32::MAX,
NumericBound::Exact(IntWidth::U32),
)
} }
/// Num.minI64: I64 /// Num.minI64: I64
fn num_min_i64(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_min_i64(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<i64>( int_min_or_max::<i64>(symbol, var_store, i64::MIN, IntBound::Exact(IntWidth::I64))
symbol,
var_store,
i64::MIN,
NumericBound::Exact(IntWidth::I64),
)
} }
/// Num.maxI64: I64 /// Num.maxI64: I64
fn num_max_i64(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_max_i64(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<i64>( int_min_or_max::<i64>(symbol, var_store, i64::MAX, IntBound::Exact(IntWidth::I64))
symbol,
var_store,
i64::MAX,
NumericBound::Exact(IntWidth::I64),
)
} }
/// Num.minU64: U64 /// Num.minU64: U64
fn num_min_u64(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_min_u64(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<u64>( int_min_or_max::<u64>(symbol, var_store, u64::MIN, IntBound::Exact(IntWidth::U64))
symbol,
var_store,
u64::MIN,
NumericBound::Exact(IntWidth::U64),
)
} }
/// Num.maxU64: U64 /// Num.maxU64: U64
fn num_max_u64(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_max_u64(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<u64>( int_min_or_max::<u64>(symbol, var_store, u64::MAX, IntBound::Exact(IntWidth::U64))
symbol,
var_store,
u64::MAX,
NumericBound::Exact(IntWidth::U64),
)
} }
/// Num.minI128: I128 /// Num.minI128: I128
@ -1417,7 +1337,7 @@ fn num_min_i128(symbol: Symbol, var_store: &mut VarStore) -> Def {
symbol, symbol,
var_store, var_store,
i128::MIN, i128::MIN,
NumericBound::Exact(IntWidth::I128), IntBound::Exact(IntWidth::I128),
) )
} }
@ -1427,7 +1347,7 @@ fn num_max_i128(symbol: Symbol, var_store: &mut VarStore) -> Def {
symbol, symbol,
var_store, var_store,
i128::MAX, i128::MAX,
NumericBound::Exact(IntWidth::I128), IntBound::Exact(IntWidth::I128),
) )
} }
@ -1559,7 +1479,7 @@ fn str_to_num(symbol: Symbol, var_store: &mut VarStore) -> Def {
errorcode_var, errorcode_var,
Variable::UNSIGNED8, Variable::UNSIGNED8,
0, 0,
NumericBound::Exact(IntWidth::U8), IntBound::Exact(IntWidth::U8),
), ),
), ),
], ],
@ -2307,7 +2227,7 @@ fn list_take_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
len_var, len_var,
Variable::NATURAL, Variable::NATURAL,
0, 0,
NumericBound::Exact(IntWidth::Nat), IntBound::Exact(IntWidth::Nat),
); );
let body = RunLowLevel { let body = RunLowLevel {
@ -2338,7 +2258,7 @@ fn list_take_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
len_var, len_var,
Variable::NATURAL, Variable::NATURAL,
0, 0,
NumericBound::Exact(IntWidth::Nat), IntBound::Exact(IntWidth::Nat),
); );
let bool_var = var_store.fresh(); let bool_var = var_store.fresh();
@ -2453,7 +2373,7 @@ fn list_intersperse(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_var, int_var,
Variable::NATURAL, Variable::NATURAL,
0, 0,
NumericBound::Exact(IntWidth::Nat), IntBound::Exact(IntWidth::Nat),
); );
// \acc, elem -> acc |> List.append sep |> List.append elem // \acc, elem -> acc |> List.append sep |> List.append elem
@ -2538,7 +2458,7 @@ fn list_split(symbol: Symbol, var_store: &mut VarStore) -> Def {
index_var, index_var,
Variable::NATURAL, Variable::NATURAL,
0, 0,
NumericBound::Exact(IntWidth::Nat), IntBound::Exact(IntWidth::Nat),
); );
let clos = Closure(ClosureData { let clos = Closure(ClosureData {
@ -2703,7 +2623,7 @@ fn list_drop_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
(list_var, Var(Symbol::ARG_1)), (list_var, Var(Symbol::ARG_1)),
( (
index_var, index_var,
int::<i128>(num_var, num_precision_var, 0, num_no_bound()), int::<i128>(num_var, num_precision_var, 0, int_no_bound()),
), ),
], ],
ret_var: list_var, ret_var: list_var,
@ -2803,7 +2723,7 @@ fn list_drop_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
), ),
( (
arg_var, arg_var,
int::<i128>(num_var, num_precision_var, 1, num_no_bound()), int::<i128>(num_var, num_precision_var, 1, int_no_bound()),
), ),
], ],
ret_var: len_var, ret_var: len_var,
@ -3004,7 +2924,7 @@ fn list_min(symbol: Symbol, var_store: &mut VarStore) -> Def {
args: vec![ args: vec![
( (
len_var, len_var,
int::<i128>(num_var, num_precision_var, 0, num_no_bound()), int::<i128>(num_var, num_precision_var, 0, int_no_bound()),
), ),
( (
len_var, len_var,
@ -3042,7 +2962,7 @@ fn list_min(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_var, num_var,
num_precision_var, num_precision_var,
0, 0,
num_no_bound(), int_no_bound(),
), ),
), ),
], ],
@ -3145,7 +3065,7 @@ fn list_max(symbol: Symbol, var_store: &mut VarStore) -> Def {
args: vec![ args: vec![
( (
len_var, len_var,
int::<i128>(num_var, num_precision_var, 0, num_no_bound()), int::<i128>(num_var, num_precision_var, 0, int_no_bound()),
), ),
( (
len_var, len_var,
@ -3183,7 +3103,7 @@ fn list_max(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_var, num_var,
num_precision_var, num_precision_var,
0, 0,
num_no_bound(), int_no_bound(),
), ),
), ),
], ],
@ -4076,7 +3996,7 @@ fn num_div_float(symbol: Symbol, var_store: &mut VarStore) -> Def {
(num_var, Var(Symbol::ARG_2)), (num_var, Var(Symbol::ARG_2)),
( (
num_var, num_var,
float(unbound_zero_var, precision_var, 0.0, num_no_bound()), float(unbound_zero_var, precision_var, 0.0, float_no_bound()),
), ),
], ],
ret_var: bool_var, ret_var: bool_var,
@ -4146,7 +4066,7 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
unbound_zero_var, unbound_zero_var,
unbound_zero_precision_var, unbound_zero_precision_var,
0, 0,
num_no_bound(), int_no_bound(),
), ),
), ),
], ],
@ -4217,7 +4137,7 @@ fn num_div_ceil(symbol: Symbol, var_store: &mut VarStore) -> Def {
unbound_zero_var, unbound_zero_var,
unbound_zero_precision_var, unbound_zero_precision_var,
0, 0,
num_no_bound(), int_no_bound(),
), ),
), ),
], ],
@ -4290,7 +4210,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
args: vec![ args: vec![
( (
len_var, len_var,
int::<i128>(zero_var, zero_precision_var, 0, num_no_bound()), int::<i128>(zero_var, zero_precision_var, 0, int_no_bound()),
), ),
( (
len_var, len_var,
@ -4317,7 +4237,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
(list_var, Var(Symbol::ARG_1)), (list_var, Var(Symbol::ARG_1)),
( (
len_var, len_var,
int::<i128>(zero_var, zero_precision_var, 0, num_no_bound()), int::<i128>(zero_var, zero_precision_var, 0, int_no_bound()),
), ),
], ],
ret_var: list_elem_var, ret_var: list_elem_var,
@ -4377,7 +4297,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
args: vec![ args: vec![
( (
len_var, len_var,
int::<i128>(num_var, num_precision_var, 0, num_no_bound()), int::<i128>(num_var, num_precision_var, 0, int_no_bound()),
), ),
( (
len_var, len_var,
@ -4423,7 +4343,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_var, num_var,
num_precision_var, num_precision_var,
1, 1,
num_no_bound(), int_no_bound(),
), ),
), ),
], ],
@ -5144,12 +5064,7 @@ fn defn_help(
} }
#[inline(always)] #[inline(always)]
fn int_min_or_max<I128>( fn int_min_or_max<I128>(symbol: Symbol, var_store: &mut VarStore, i: I128, bound: IntBound) -> Def
symbol: Symbol,
var_store: &mut VarStore,
i: I128,
bound: NumericBound<IntWidth>,
) -> Def
where where
I128: Into<i128>, I128: Into<i128>,
{ {
@ -5178,17 +5093,20 @@ where
} }
} }
fn num_no_bound<W: Copy>() -> NumericBound<W> { fn num_no_bound() -> NumericBound {
NumericBound::None NumericBound::None
} }
fn int_no_bound() -> IntBound {
IntBound::None
}
fn float_no_bound() -> FloatBound {
FloatBound::None
}
#[inline(always)] #[inline(always)]
fn int<I128>( fn int<I128>(num_var: Variable, precision_var: Variable, i: I128, bound: IntBound) -> Expr
num_var: Variable,
precision_var: Variable,
i: I128,
bound: NumericBound<IntWidth>,
) -> Expr
where where
I128: Into<i128>, I128: Into<i128>,
{ {
@ -5203,12 +5121,7 @@ where
} }
#[inline(always)] #[inline(always)]
fn float( fn float(num_var: Variable, precision_var: Variable, f: f64, bound: FloatBound) -> Expr {
num_var: Variable,
precision_var: Variable,
f: f64,
bound: NumericBound<FloatWidth>,
) -> Expr {
Float( Float(
num_var, num_var,
precision_var, precision_var,
@ -5219,7 +5132,7 @@ fn float(
} }
#[inline(always)] #[inline(always)]
fn num<I: Into<i128>>(num_var: Variable, i: I, bound: NumericBound<NumWidth>) -> Expr { fn num<I: Into<i128>>(num_var: Variable, i: I, bound: NumericBound) -> Expr {
let i = i.into(); let i = i.into();
Num( Num(
num_var, num_var,

View file

@ -27,6 +27,14 @@ pub enum Constraint {
Let(Box<LetConstraint>), Let(Box<LetConstraint>),
And(Vec<Constraint>), And(Vec<Constraint>),
Present(Type, PresenceConstraint), Present(Type, PresenceConstraint),
/// `EqBoundedRange(Ts, U, ...)` means there must be at least one `T` in the *ordered* range `Ts`
/// that unifies (via `Eq`) with `U`.
///
/// This is only used for integers, where we may see e.g. the number literal `-1` and know it
/// has the bounded range `[I8, I16, I32, I64, I128]`, at least one of which must unify with
/// the type the number literal is used as.
EqBoundedRange(Type, Expected<Vec<Type>>, Category, Region),
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -87,6 +95,7 @@ impl Constraint {
} }
Constraint::And(cs) => cs.iter().any(|c| c.contains_save_the_environment()), Constraint::And(cs) => cs.iter().any(|c| c.contains_save_the_environment()),
Constraint::Present(_, _) => false, Constraint::Present(_, _) => false,
Constraint::EqBoundedRange(_, _, _, _) => false,
} }
} }
} }
@ -171,5 +180,11 @@ fn validate_help(constraint: &Constraint, declared: &Declared, accum: &mut Varia
} }
} }
} }
Constraint::EqBoundedRange(typ, one_of, _, _) => {
subtract(declared, &typ.variables_detail(), accum);
for typ in one_of.get_type_ref() {
subtract(declared, &typ.variables_detail(), accum);
}
}
} }
} }

View file

@ -4,7 +4,7 @@ use crate::def::{can_defs_with_return, Def};
use crate::env::Env; use crate::env::Env;
use crate::num::{ use crate::num::{
finish_parsing_base, finish_parsing_float, finish_parsing_num, float_expr_from_result, finish_parsing_base, finish_parsing_float, finish_parsing_num, float_expr_from_result,
int_expr_from_result, num_expr_from_result, FloatWidth, IntWidth, NumWidth, NumericBound, int_expr_from_result, num_expr_from_result, FloatBound, IntBound, NumericBound,
}; };
use crate::pattern::{canonicalize_pattern, Pattern}; use crate::pattern::{canonicalize_pattern, Pattern};
use crate::procedure::References; use crate::procedure::References;
@ -67,17 +67,11 @@ pub enum Expr {
// Num stores the `a` variable in `Num a`. Not the same as the variable // 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 // stored in Int and Float below, which is strictly for better error messages
Num(Variable, Box<str>, IntValue, NumericBound<NumWidth>), Num(Variable, Box<str>, IntValue, NumericBound),
// Int and Float store a variable to generate better error messages // Int and Float store a variable to generate better error messages
Int( Int(Variable, Variable, Box<str>, IntValue, IntBound),
Variable, Float(Variable, Variable, Box<str>, f64, FloatBound),
Variable,
Box<str>,
IntValue,
NumericBound<IntWidth>,
),
Float(Variable, Variable, Box<str>, f64, NumericBound<FloatWidth>),
Str(Box<str>), Str(Box<str>),
List { List {
elem_var: Variable, elem_var: Variable,

View file

@ -50,7 +50,7 @@ pub fn num_expr_from_result(
#[inline(always)] #[inline(always)]
pub fn int_expr_from_result( pub fn int_expr_from_result(
var_store: &mut VarStore, var_store: &mut VarStore,
result: Result<(&str, IntValue, NumericBound<IntWidth>), (&str, IntErrorKind)>, result: Result<(&str, IntValue, IntBound), (&str, IntErrorKind)>,
region: Region, region: Region,
base: Base, base: Base,
env: &mut Env, env: &mut Env,
@ -77,7 +77,7 @@ pub fn int_expr_from_result(
#[inline(always)] #[inline(always)]
pub fn float_expr_from_result( pub fn float_expr_from_result(
var_store: &mut VarStore, var_store: &mut VarStore,
result: Result<(&str, f64, NumericBound<FloatWidth>), (&str, FloatErrorKind)>, result: Result<(&str, f64, FloatBound), (&str, FloatErrorKind)>,
region: Region, region: Region,
env: &mut Env, env: &mut Env,
) -> Expr { ) -> Expr {
@ -101,8 +101,8 @@ pub fn float_expr_from_result(
} }
pub enum ParsedNumResult { pub enum ParsedNumResult {
Int(IntValue, NumericBound<IntWidth>), Int(IntValue, IntBound),
Float(f64, NumericBound<FloatWidth>), Float(f64, FloatBound),
UnknownNum(IntValue), UnknownNum(IntValue),
} }
@ -115,15 +115,13 @@ pub fn finish_parsing_num(raw: &str) -> Result<ParsedNumResult, (&str, IntErrorK
// Let's try to specialize the number // Let's try to specialize the number
Ok(match bound { Ok(match bound {
NumericBound::None => ParsedNumResult::UnknownNum(num), NumericBound::None => ParsedNumResult::UnknownNum(num),
NumericBound::Exact(NumWidth::Int(iw)) => { NumericBound::Int(ib) => ParsedNumResult::Int(num, ib),
ParsedNumResult::Int(num, NumericBound::Exact(iw)) NumericBound::Float(fb) => {
}
NumericBound::Exact(NumWidth::Float(fw)) => {
let num = match num { let num = match num {
IntValue::I128(n) => n as f64, IntValue::I128(n) => n as f64,
IntValue::U128(n) => n as f64, IntValue::U128(n) => n as f64,
}; };
ParsedNumResult::Float(num, NumericBound::Exact(fw)) ParsedNumResult::Float(num, fb)
} }
}) })
} }
@ -133,7 +131,7 @@ pub fn finish_parsing_base(
raw: &str, raw: &str,
base: Base, base: Base,
is_negative: bool, is_negative: bool,
) -> Result<(IntValue, NumericBound<IntWidth>), (&str, IntErrorKind)> { ) -> Result<(IntValue, IntBound), (&str, IntErrorKind)> {
let radix = match base { let radix = match base {
Base::Hex => 16, Base::Hex => 16,
Base::Decimal => 10, Base::Decimal => 10,
@ -149,9 +147,9 @@ pub fn finish_parsing_base(
}) })
.and_then(|(n, bound)| { .and_then(|(n, bound)| {
let bound = match bound { let bound = match bound {
NumericBound::None => NumericBound::None, NumericBound::None => IntBound::None,
NumericBound::Exact(NumWidth::Int(iw)) => NumericBound::Exact(iw), NumericBound::Int(ib) => ib,
NumericBound::Exact(NumWidth::Float(_)) => return Err(IntErrorKind::FloatSuffix), NumericBound::Float(_) => return Err(IntErrorKind::FloatSuffix),
}; };
Ok((n, bound)) Ok((n, bound))
}) })
@ -159,15 +157,13 @@ pub fn finish_parsing_base(
} }
#[inline(always)] #[inline(always)]
pub fn finish_parsing_float( pub fn finish_parsing_float(raw: &str) -> Result<(f64, FloatBound), (&str, FloatErrorKind)> {
raw: &str,
) -> Result<(f64, NumericBound<FloatWidth>), (&str, FloatErrorKind)> {
let (opt_bound, raw_without_suffix) = parse_literal_suffix(raw); let (opt_bound, raw_without_suffix) = parse_literal_suffix(raw);
let bound = match opt_bound { let bound = match opt_bound {
None => NumericBound::None, None => FloatBound::None,
Some(NumWidth::Float(fw)) => NumericBound::Exact(fw), Some(ParsedWidth::Float(fw)) => FloatBound::Exact(fw),
Some(NumWidth::Int(_)) => return Err((raw, FloatErrorKind::IntSuffix)), Some(ParsedWidth::Int(_)) => return Err((raw, FloatErrorKind::IntSuffix)),
}; };
// Ignore underscores. // Ignore underscores.
@ -184,7 +180,13 @@ pub fn finish_parsing_float(
} }
} }
fn parse_literal_suffix(num_str: &str) -> (Option<NumWidth>, &str) { #[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum ParsedWidth {
Int(IntWidth),
Float(FloatWidth),
}
fn parse_literal_suffix(num_str: &str) -> (Option<ParsedWidth>, &str) {
macro_rules! parse_num_suffix { macro_rules! parse_num_suffix {
($($suffix:expr, $width:expr)*) => {$( ($($suffix:expr, $width:expr)*) => {$(
if num_str.ends_with($suffix) { if num_str.ends_with($suffix) {
@ -194,20 +196,20 @@ fn parse_literal_suffix(num_str: &str) -> (Option<NumWidth>, &str) {
} }
parse_num_suffix! { parse_num_suffix! {
"u8", NumWidth::Int(IntWidth::U8) "u8", ParsedWidth::Int(IntWidth::U8)
"u16", NumWidth::Int(IntWidth::U16) "u16", ParsedWidth::Int(IntWidth::U16)
"u32", NumWidth::Int(IntWidth::U32) "u32", ParsedWidth::Int(IntWidth::U32)
"u64", NumWidth::Int(IntWidth::U64) "u64", ParsedWidth::Int(IntWidth::U64)
"u128", NumWidth::Int(IntWidth::U128) "u128", ParsedWidth::Int(IntWidth::U128)
"i8", NumWidth::Int(IntWidth::I8) "i8", ParsedWidth::Int(IntWidth::I8)
"i16", NumWidth::Int(IntWidth::I16) "i16", ParsedWidth::Int(IntWidth::I16)
"i32", NumWidth::Int(IntWidth::I32) "i32", ParsedWidth::Int(IntWidth::I32)
"i64", NumWidth::Int(IntWidth::I64) "i64", ParsedWidth::Int(IntWidth::I64)
"i128", NumWidth::Int(IntWidth::I128) "i128", ParsedWidth::Int(IntWidth::I128)
"nat", NumWidth::Int(IntWidth::Nat) "nat", ParsedWidth::Int(IntWidth::Nat)
"dec", NumWidth::Float(FloatWidth::Dec) "dec", ParsedWidth::Float(FloatWidth::Dec)
"f32", NumWidth::Float(FloatWidth::F32) "f32", ParsedWidth::Float(FloatWidth::F32)
"f64", NumWidth::Float(FloatWidth::F64) "f64", ParsedWidth::Float(FloatWidth::F64)
} }
(None, num_str) (None, num_str)
@ -221,10 +223,7 @@ fn parse_literal_suffix(num_str: &str) -> (Option<NumWidth>, &str) {
/// the LEGAL_DETAILS file in the root directory of this distribution. /// the LEGAL_DETAILS file in the root directory of this distribution.
/// ///
/// Thanks to the Rust project and its contributors! /// Thanks to the Rust project and its contributors!
fn from_str_radix( fn from_str_radix(src: &str, radix: u32) -> Result<(IntValue, NumericBound), IntErrorKind> {
src: &str,
radix: u32,
) -> Result<(IntValue, NumericBound<NumWidth>), IntErrorKind> {
use self::IntErrorKind::*; use self::IntErrorKind::*;
assert!( assert!(
@ -268,19 +267,31 @@ fn from_str_radix(
match opt_exact_bound { match opt_exact_bound {
None => { None => {
// TODO: use the lower bound // There's no exact bound, but we do have a lower bound.
Ok((result, NumericBound::None)) let sign_demand = if is_negative {
SignDemand::Signed
} else {
SignDemand::NoDemand
};
Ok((
result,
IntBound::AtLeast {
sign: sign_demand,
width: lower_bound,
} }
Some(bound @ NumWidth::Float(_)) => { .into(),
))
}
Some(ParsedWidth::Float(fw)) => {
// For now, assume floats can represent all integers // For now, assume floats can represent all integers
// TODO: this is somewhat incorrect, revisit // TODO: this is somewhat incorrect, revisit
Ok((result, NumericBound::Exact(bound))) Ok((result, FloatBound::Exact(fw).into()))
} }
Some(NumWidth::Int(exact_width)) => { Some(ParsedWidth::Int(exact_width)) => {
// We need to check if the exact bound >= lower bound. // We need to check if the exact bound >= lower bound.
if exact_width.is_superset(&lower_bound, is_negative) { if exact_width.is_superset(&lower_bound, is_negative) {
// Great! Use the exact bound. // Great! Use the exact bound.
Ok((result, NumericBound::Exact(NumWidth::Int(exact_width)))) Ok((result, IntBound::Exact(exact_width).into()))
} else { } else {
// This is something like 200i8; the lower bound is u8, which holds strictly more // 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 // ints on the positive side than i8 does. Report an error depending on which side
@ -474,19 +485,47 @@ pub enum FloatWidth {
} }
#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum NumWidth { pub enum SignDemand {
Int(IntWidth), /// Can be signed or unsigned.
Float(FloatWidth), NoDemand,
/// Must be signed.
Signed,
} }
/// Describes a bound on the width of a numeric literal. /// Describes a bound on the width of an integer.
#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum NumericBound<W> pub enum IntBound {
where
W: Copy,
{
/// There is no bound on the width. /// There is no bound on the width.
None, None,
/// Must have exactly the width `W`. /// Must have an exact width.
Exact(W), Exact(IntWidth),
/// Must have a certain sign and a minimum width.
AtLeast { sign: SignDemand, width: IntWidth },
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum FloatBound {
None,
Exact(FloatWidth),
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum NumericBound {
None,
Int(IntBound),
Float(FloatBound),
}
impl From<IntBound> for NumericBound {
#[inline(always)]
fn from(ib: IntBound) -> Self {
Self::Int(ib)
}
}
impl From<FloatBound> for NumericBound {
#[inline(always)]
fn from(fb: FloatBound) -> Self {
Self::Float(fb)
}
} }

View file

@ -1,7 +1,7 @@
use crate::env::Env; use crate::env::Env;
use crate::expr::{canonicalize_expr, unescape_char, Expr, IntValue, Output}; use crate::expr::{canonicalize_expr, unescape_char, Expr, IntValue, Output};
use crate::num::{ use crate::num::{
finish_parsing_base, finish_parsing_float, finish_parsing_num, FloatWidth, IntWidth, NumWidth, finish_parsing_base, finish_parsing_float, finish_parsing_num, FloatBound, IntBound,
NumericBound, ParsedNumResult, NumericBound, ParsedNumResult,
}; };
use crate::scope::Scope; use crate::scope::Scope;
@ -29,15 +29,9 @@ pub enum Pattern {
ext_var: Variable, ext_var: Variable,
destructs: Vec<Loc<RecordDestruct>>, destructs: Vec<Loc<RecordDestruct>>,
}, },
NumLiteral(Variable, Box<str>, IntValue, NumericBound<NumWidth>), NumLiteral(Variable, Box<str>, IntValue, NumericBound),
IntLiteral( IntLiteral(Variable, Variable, Box<str>, IntValue, IntBound),
Variable, FloatLiteral(Variable, Variable, Box<str>, f64, FloatBound),
Variable,
Box<str>,
IntValue,
NumericBound<IntWidth>,
),
FloatLiteral(Variable, Variable, Box<str>, f64, NumericBound<FloatWidth>),
StrLiteral(Box<str>), StrLiteral(Box<str>),
Underscore, Underscore,

View file

@ -1,7 +1,7 @@
use roc_can::constraint::Constraint::{self, *}; use roc_can::constraint::Constraint::{self, *};
use roc_can::constraint::LetConstraint; use roc_can::constraint::LetConstraint;
use roc_can::expected::Expected::{self, *}; use roc_can::expected::Expected::{self, *};
use roc_can::num::{FloatWidth, IntWidth, NumWidth, NumericBound}; use roc_can::num::{FloatBound, FloatWidth, IntBound, IntWidth, NumericBound, SignDemand};
use roc_collections::all::SendMap; use roc_collections::all::SendMap;
use roc_module::ident::{Lowercase, TagName}; use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
@ -28,13 +28,31 @@ pub fn add_numeric_bound_constr(
} }
} }
pub fn add_numeric_range_constr(
constrs: &mut Vec<Constraint>,
num_type: Type,
bound: impl TypedNumericBound,
region: Region,
category: Category,
) {
let range = bound.bounded_range();
if !range.is_empty() {
constrs.push(EqBoundedRange(
num_type,
Expected::ForReason(Reason::NumericLiteralSuffix, range, region),
category,
region,
));
}
}
#[inline(always)] #[inline(always)]
pub fn int_literal( pub fn int_literal(
num_var: Variable, num_var: Variable,
precision_var: Variable, precision_var: Variable,
expected: Expected<Type>, expected: Expected<Type>,
region: Region, region: Region,
bound: NumericBound<IntWidth>, bound: IntBound,
) -> Constraint { ) -> Constraint {
let num_type = Variable(num_var); let num_type = Variable(num_var);
let reason = Reason::IntLiteral; let reason = Reason::IntLiteral;
@ -50,8 +68,15 @@ pub fn int_literal(
Category::Int, Category::Int,
region, region,
), ),
Eq(num_type, expected, Category::Int, region), Eq(num_type, expected.clone(), Category::Int, region),
]); ]);
add_numeric_range_constr(
&mut constrs,
expected.get_type(),
bound,
region,
Category::Int,
);
exists(vec![num_var], And(constrs)) exists(vec![num_var], And(constrs))
} }
@ -62,7 +87,7 @@ pub fn float_literal(
precision_var: Variable, precision_var: Variable,
expected: Expected<Type>, expected: Expected<Type>,
region: Region, region: Region,
bound: NumericBound<FloatWidth>, bound: FloatBound,
) -> Constraint { ) -> Constraint {
let num_type = Variable(num_var); let num_type = Variable(num_var);
let reason = Reason::FloatLiteral; let reason = Reason::FloatLiteral;
@ -93,7 +118,7 @@ pub fn num_literal(
num_var: Variable, num_var: Variable,
expected: Expected<Type>, expected: Expected<Type>,
region: Region, region: Region,
bound: NumericBound<NumWidth>, bound: NumericBound,
) -> Constraint { ) -> Constraint {
let num_type = crate::builtins::num_num(Type::Variable(num_var)); let num_type = crate::builtins::num_num(Type::Variable(num_var));
@ -290,13 +315,15 @@ pub trait TypedNumericBound {
/// Get a concrete type for this number, if one exists. /// Get a concrete type for this number, if one exists.
/// Returns `None` e.g. if the bound is open, like `Int *`. /// Returns `None` e.g. if the bound is open, like `Int *`.
fn concrete_num_type(&self) -> Option<Type>; fn concrete_num_type(&self) -> Option<Type>;
fn bounded_range(&self) -> Vec<Type>;
} }
impl TypedNumericBound for NumericBound<IntWidth> { impl TypedNumericBound for IntBound {
fn concrete_num_type(&self) -> Option<Type> { fn concrete_num_type(&self) -> Option<Type> {
match self { match self {
NumericBound::None => None, IntBound::None | IntBound::AtLeast { .. } => None,
NumericBound::Exact(w) => Some(match w { IntBound::Exact(w) => Some(match w {
IntWidth::U8 => num_u8(), IntWidth::U8 => num_u8(),
IntWidth::U16 => num_u16(), IntWidth::U16 => num_u16(),
IntWidth::U32 => num_u32(), IntWidth::U32 => num_u32(),
@ -311,29 +338,80 @@ impl TypedNumericBound for NumericBound<IntWidth> {
}), }),
} }
} }
fn bounded_range(&self) -> Vec<Type> {
match self {
IntBound::None => vec![],
IntBound::Exact(_) => vec![],
IntBound::AtLeast { sign, width } => {
let whole_range: &[(IntWidth, Variable)] = match sign {
SignDemand::NoDemand => {
&[
(IntWidth::I8, Variable::I8),
(IntWidth::U8, Variable::U8),
(IntWidth::I16, Variable::I16),
(IntWidth::U16, Variable::U16),
(IntWidth::I32, Variable::I32),
(IntWidth::U32, Variable::U32),
(IntWidth::I64, Variable::I64),
(IntWidth::Nat, Variable::NAT), // FIXME: Nat's order here depends on the platform!
(IntWidth::U64, Variable::U64),
(IntWidth::I128, Variable::I128),
(IntWidth::U128, Variable::U128),
]
}
SignDemand::Signed => &[
(IntWidth::I8, Variable::I8),
(IntWidth::I16, Variable::I16),
(IntWidth::I32, Variable::I32),
(IntWidth::I64, Variable::I64),
(IntWidth::I128, Variable::I128),
],
};
whole_range
.iter()
.skip_while(|(lower_bound, _)| *lower_bound != *width)
.map(|(_, var)| Type::Variable(*var))
.collect()
}
}
}
} }
impl TypedNumericBound for NumericBound<FloatWidth> { impl TypedNumericBound for FloatBound {
fn concrete_num_type(&self) -> Option<Type> { fn concrete_num_type(&self) -> Option<Type> {
match self { match self {
NumericBound::None => None, FloatBound::None => None,
NumericBound::Exact(w) => Some(match w { FloatBound::Exact(w) => Some(match w {
FloatWidth::Dec => num_dec(), FloatWidth::Dec => num_dec(),
FloatWidth::F32 => num_f32(), FloatWidth::F32 => num_f32(),
FloatWidth::F64 => num_f64(), FloatWidth::F64 => num_f64(),
}), }),
} }
} }
fn bounded_range(&self) -> Vec<Type> {
match self {
FloatBound::None => vec![],
FloatBound::Exact(_) => vec![],
}
}
} }
impl TypedNumericBound for NumericBound<NumWidth> { impl TypedNumericBound for NumericBound {
fn concrete_num_type(&self) -> Option<Type> { fn concrete_num_type(&self) -> Option<Type> {
match self { match self {
NumericBound::None => None, NumericBound::None => None,
NumericBound::Exact(NumWidth::Int(iw)) => NumericBound::Exact(*iw).concrete_num_type(), NumericBound::Int(ib) => ib.concrete_num_type(),
NumericBound::Exact(NumWidth::Float(fw)) => { NumericBound::Float(fb) => fb.concrete_num_type(),
NumericBound::Exact(*fw).concrete_num_type() }
} }
fn bounded_range(&self) -> Vec<Type> {
match self {
NumericBound::None => vec![],
NumericBound::Int(ib) => ib.bounded_range(),
NumericBound::Float(fb) => fb.bounded_range(),
} }
} }
} }

View file

@ -689,6 +689,51 @@ fn solve(
} }
} }
} }
EqBoundedRange(typ, expect_one_of, category, region) => {
let actual = type_to_var(subs, rank, pools, cached_aliases, typ);
let mut it = expect_one_of.get_type_ref().iter().peekable();
while let Some(expected) = it.next() {
let expected = type_to_var(subs, rank, pools, cached_aliases, expected);
let snapshot = subs.snapshot();
match unify(subs, actual, expected, Mode::Eq) {
Success(vars) => {
introduce(subs, rank, pools, &vars);
return state;
}
Failure(..) if it.peek().is_some() => {
subs.rollback_to(snapshot);
continue;
}
Failure(vars, actual_type, expected_type) => {
// This is the last type we could have tried and failed; record the error.
introduce(subs, rank, pools, &vars);
let problem = TypeError::BadExpr(
*region,
category.clone(),
actual_type,
expect_one_of.clone().replace(expected_type),
);
problems.push(problem);
return state;
}
BadType(vars, problem) => {
introduce(subs, rank, pools, &vars);
problems.push(TypeError::BadType(problem));
return state;
}
}
}
unreachable!()
}
} }
} }

View file

@ -862,9 +862,9 @@ mod test_reporting {
2> 2 if 1 -> 0x0 2> 2 if 1 -> 0x0
3 _ -> 0x1 3 _ -> 0x1
Right now its a number of type: Right now its an integer of type:
Num a Int a
But I need every `if` guard condition to evaluate to a Booleither But I need every `if` guard condition to evaluate to a Booleither
`True` or `False`. `True` or `False`.
@ -896,7 +896,7 @@ mod test_reporting {
but the `then` branch has the type: but the `then` branch has the type:
Num a Int a
I need all branches in an `if` to have the same type! I need all branches in an `if` to have the same type!
"# "#
@ -927,7 +927,7 @@ mod test_reporting {
But all the previous branches have type: But all the previous branches have type:
Num a Int a
I need all branches in an `if` to have the same type! I need all branches in an `if` to have the same type!
"# "#
@ -993,7 +993,7 @@ mod test_reporting {
However, the preceding elements in the list all have the type: However, the preceding elements in the list all have the type:
Num a Int a
I need every element in a list to have the same type! I need every element in a list to have the same type!
"# "#
@ -1424,7 +1424,7 @@ mod test_reporting {
But the expression between `when` and `is` has the type: But the expression between `when` and `is` has the type:
Num a Int a
"# "#
), ),
) )
@ -1455,7 +1455,7 @@ mod test_reporting {
But all the previous branches match: But all the previous branches match:
Num a Int a
"# "#
), ),
) )
@ -1485,7 +1485,7 @@ mod test_reporting {
But the expression between `when` and `is` has the type: But the expression between `when` and `is` has the type:
{ foo : Num a } { foo : Int a }
"# "#
), ),
) )
@ -1605,13 +1605,13 @@ mod test_reporting {
2 {} | 1 -> 3 2 {} | 1 -> 3
^^^^^^ ^^^^^^
The first pattern is trying to match numbers: The first pattern is trying to match integers:
Num a Int a
But the expression between `when` and `is` has the type: But the expression between `when` and `is` has the type:
{ foo : Num a } { foo : Int a }
"# "#
), ),
) )
@ -1637,9 +1637,9 @@ mod test_reporting {
1 (Foo x) = 42 1 (Foo x) = 42
^^ ^^
It is a number of type: It is an integer of type:
Num a Int a
But you are trying to use it as: But you are trying to use it as:
@ -2162,8 +2162,8 @@ mod test_reporting {
This is usually a typo. Here are the `x` fields that are most similar: This is usually a typo. Here are the `x` fields that are most similar:
{ fo : Num b { fo : Int b
, bar : Num a , bar : Int a
} }
So maybe `.foo` should be `.fo`? So maybe `.foo` should be `.fo`?
@ -2229,10 +2229,10 @@ mod test_reporting {
This is usually a typo. Here are the `x` fields that are most similar: This is usually a typo. Here are the `x` fields that are most similar:
{ fo : Num c { fo : Int c
, foobar : Num d , foobar : Int d
, bar : Num a , bar : Int a
, baz : Num b , baz : Int b
, ... , ...
} }
@ -2327,7 +2327,7 @@ mod test_reporting {
But `add` needs the 2nd argument to be: But `add` needs the 2nd argument to be:
Num a Num (Integer a)
"# "#
), ),
) )
@ -3360,8 +3360,8 @@ mod test_reporting {
This `ACons` global tag application has the type: This `ACons` global tag application has the type:
[ ACons Num (Integer Signed64) [ BCons (Num a) [ ACons Str [ BNil [ ACons Int Signed64 [ BCons (Int a) [ ACons Str [ BNil ]b ]c ]d,
]b ]c ]d, ANil ] ANil ]
But the type annotation on `x` says it should be: But the type annotation on `x` says it should be:
@ -4973,7 +4973,7 @@ mod test_reporting {
This `insert` call produces: This `insert` call produces:
Dict Str (Num a) Dict Str (Int a)
But the type annotation on `myDict` says it should be: But the type annotation on `myDict` says it should be:
@ -5635,7 +5635,7 @@ mod test_reporting {
but the `then` branch has the type: but the `then` branch has the type:
Num a Int a
I need all branches in an `if` to have the same type! I need all branches in an `if` to have the same type!
"# "#
@ -6276,7 +6276,7 @@ I need all branches in an `if` to have the same type!
This `map` call produces: This `map` call produces:
List [ Foo Num a ] List [ Foo Int a ]
But the type annotation on `x` says it should be: But the type annotation on `x` says it should be:
@ -6526,11 +6526,11 @@ I need all branches in an `if` to have the same type!
This argument is an anonymous function of type: This argument is an anonymous function of type:
Num a -> Num a Num (Integer a) -> Num (Integer a)
But `map` needs the 2nd argument to be: But `map` needs the 2nd argument to be:
Str -> Num a Str -> Num (Integer a)
"# "#
), ),
) )
@ -6632,6 +6632,21 @@ I need all branches in an `if` to have the same type!
But the type annotation on `mult` says it should be: But the type annotation on `mult` says it should be:
F64 F64
TYPE MISMATCH
The 2nd argument to `mult` is not what I expect:
4 mult 0 0
^
This argument is an integer of type:
Int a
But `mult` needs the 2nd argument to be:
F64
"# "#
), ),
) )
@ -6680,6 +6695,21 @@ I need all branches in an `if` to have the same type!
But the type annotation on `mult` says it should be: But the type annotation on `mult` says it should be:
F64 F64
TYPE MISMATCH
The 2nd argument to `mult` is not what I expect:
4 mult 0 0
^
This argument is an integer of type:
Int a
But `mult` needs the 2nd argument to be:
F64
"# "#
), ),
) )
@ -7031,9 +7061,9 @@ I need all branches in an `if` to have the same type!
5 f = \c -> c 6 5 f = \c -> c 6
^ ^
This argument is a number of type: This argument is an integer of type:
Num a Int a
But `c` needs the 1st argument to be: But `c` needs the 1st argument to be:
@ -7041,7 +7071,7 @@ I need all branches in an `if` to have the same type!
Tip: The type annotation uses the type variable `a` to say that this Tip: The type annotation uses the type variable `a` to say that this
definition can produce any type of value. But in the body I see that definition can produce any type of value. But in the body I see that
it will only produce a `Num` value of a single specific type. Maybe it will only produce a `Int` value of a single specific type. Maybe
change the type annotation to be more specific? Maybe change the code change the type annotation to be more specific? Maybe change the code
to be more general? to be more general?
"# "#
@ -7848,4 +7878,24 @@ I need all branches in an `if` to have the same type!
), ),
) )
} }
#[test]
fn list_get_negative_number() {
report_problem_as(
"List.get [1, 2, 3] -1",
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.
"#
),
)
}
} }