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::expr::{self, ClosureData, Expr::*, IntValue};
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 roc_collections::all::SendMap;
use roc_module::called_via::CalledVia;
@ -867,7 +867,7 @@ fn num_is_odd(symbol: Symbol, var_store: &mut VarStore) -> Def {
args: vec![
(
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,
@ -965,7 +965,7 @@ fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def {
(float_var, Var(Symbol::ARG_1)),
(
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,
@ -1014,7 +1014,7 @@ fn num_log(symbol: Symbol, var_store: &mut VarStore) -> Def {
(float_var, Var(Symbol::ARG_1)),
(
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,
@ -1253,162 +1253,82 @@ fn num_int_cast(symbol: Symbol, var_store: &mut VarStore) -> Def {
/// Num.minI8: I8
fn num_min_i8(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<i8>(
symbol,
var_store,
i8::MIN,
NumericBound::Exact(IntWidth::I8),
)
int_min_or_max::<i8>(symbol, var_store, i8::MIN, IntBound::Exact(IntWidth::I8))
}
/// Num.maxI8: I8
fn num_max_i8(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<i8>(
symbol,
var_store,
i8::MAX,
NumericBound::Exact(IntWidth::I8),
)
int_min_or_max::<i8>(symbol, var_store, i8::MAX, IntBound::Exact(IntWidth::I8))
}
/// Num.minU8: U8
fn num_min_u8(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<u8>(
symbol,
var_store,
u8::MIN,
NumericBound::Exact(IntWidth::U8),
)
int_min_or_max::<u8>(symbol, var_store, u8::MIN, IntBound::Exact(IntWidth::U8))
}
/// Num.maxU8: U8
fn num_max_u8(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<u8>(
symbol,
var_store,
u8::MAX,
NumericBound::Exact(IntWidth::U8),
)
int_min_or_max::<u8>(symbol, var_store, u8::MAX, IntBound::Exact(IntWidth::U8))
}
/// Num.minI16: I16
fn num_min_i16(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<i16>(
symbol,
var_store,
i16::MIN,
NumericBound::Exact(IntWidth::I16),
)
int_min_or_max::<i16>(symbol, var_store, i16::MIN, IntBound::Exact(IntWidth::I16))
}
/// Num.maxI16: I16
fn num_max_i16(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<i16>(
symbol,
var_store,
i16::MAX,
NumericBound::Exact(IntWidth::I16),
)
int_min_or_max::<i16>(symbol, var_store, i16::MAX, IntBound::Exact(IntWidth::I16))
}
/// Num.minU16: U16
fn num_min_u16(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<u16>(
symbol,
var_store,
u16::MIN,
NumericBound::Exact(IntWidth::U16),
)
int_min_or_max::<u16>(symbol, var_store, u16::MIN, IntBound::Exact(IntWidth::U16))
}
/// Num.maxU16: U16
fn num_max_u16(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<u16>(
symbol,
var_store,
u16::MAX,
NumericBound::Exact(IntWidth::U16),
)
int_min_or_max::<u16>(symbol, var_store, u16::MAX, IntBound::Exact(IntWidth::U16))
}
/// Num.minI32: I32
fn num_min_i32(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<i32>(
symbol,
var_store,
i32::MIN,
NumericBound::Exact(IntWidth::I32),
)
int_min_or_max::<i32>(symbol, var_store, i32::MIN, IntBound::Exact(IntWidth::I32))
}
/// Num.maxI32: I32
fn num_max_i32(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<i32>(
symbol,
var_store,
i32::MAX,
NumericBound::Exact(IntWidth::I32),
)
int_min_or_max::<i32>(symbol, var_store, i32::MAX, IntBound::Exact(IntWidth::I32))
}
/// Num.minU32: U32
fn num_min_u32(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<u32>(
symbol,
var_store,
u32::MIN,
NumericBound::Exact(IntWidth::U32),
)
int_min_or_max::<u32>(symbol, var_store, u32::MIN, IntBound::Exact(IntWidth::U32))
}
/// Num.maxU32: U32
fn num_max_u32(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<u32>(
symbol,
var_store,
u32::MAX,
NumericBound::Exact(IntWidth::U32),
)
int_min_or_max::<u32>(symbol, var_store, u32::MAX, IntBound::Exact(IntWidth::U32))
}
/// Num.minI64: I64
fn num_min_i64(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<i64>(
symbol,
var_store,
i64::MIN,
NumericBound::Exact(IntWidth::I64),
)
int_min_or_max::<i64>(symbol, var_store, i64::MIN, IntBound::Exact(IntWidth::I64))
}
/// Num.maxI64: I64
fn num_max_i64(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<i64>(
symbol,
var_store,
i64::MAX,
NumericBound::Exact(IntWidth::I64),
)
int_min_or_max::<i64>(symbol, var_store, i64::MAX, IntBound::Exact(IntWidth::I64))
}
/// Num.minU64: U64
fn num_min_u64(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<u64>(
symbol,
var_store,
u64::MIN,
NumericBound::Exact(IntWidth::U64),
)
int_min_or_max::<u64>(symbol, var_store, u64::MIN, IntBound::Exact(IntWidth::U64))
}
/// Num.maxU64: U64
fn num_max_u64(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_min_or_max::<u64>(
symbol,
var_store,
u64::MAX,
NumericBound::Exact(IntWidth::U64),
)
int_min_or_max::<u64>(symbol, var_store, u64::MAX, IntBound::Exact(IntWidth::U64))
}
/// Num.minI128: I128
@ -1417,7 +1337,7 @@ fn num_min_i128(symbol: Symbol, var_store: &mut VarStore) -> Def {
symbol,
var_store,
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,
var_store,
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,
Variable::UNSIGNED8,
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,
Variable::NATURAL,
0,
NumericBound::Exact(IntWidth::Nat),
IntBound::Exact(IntWidth::Nat),
);
let body = RunLowLevel {
@ -2338,7 +2258,7 @@ fn list_take_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
len_var,
Variable::NATURAL,
0,
NumericBound::Exact(IntWidth::Nat),
IntBound::Exact(IntWidth::Nat),
);
let bool_var = var_store.fresh();
@ -2453,7 +2373,7 @@ fn list_intersperse(symbol: Symbol, var_store: &mut VarStore) -> Def {
int_var,
Variable::NATURAL,
0,
NumericBound::Exact(IntWidth::Nat),
IntBound::Exact(IntWidth::Nat),
);
// \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,
Variable::NATURAL,
0,
NumericBound::Exact(IntWidth::Nat),
IntBound::Exact(IntWidth::Nat),
);
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)),
(
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,
@ -2803,7 +2723,7 @@ fn list_drop_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
),
(
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,
@ -3004,7 +2924,7 @@ fn list_min(symbol: Symbol, var_store: &mut VarStore) -> Def {
args: vec![
(
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,
@ -3042,7 +2962,7 @@ fn list_min(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_var,
num_precision_var,
0,
num_no_bound(),
int_no_bound(),
),
),
],
@ -3145,7 +3065,7 @@ fn list_max(symbol: Symbol, var_store: &mut VarStore) -> Def {
args: vec![
(
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,
@ -3183,7 +3103,7 @@ fn list_max(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_var,
num_precision_var,
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,
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,
@ -4146,7 +4066,7 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
unbound_zero_var,
unbound_zero_precision_var,
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_precision_var,
0,
num_no_bound(),
int_no_bound(),
),
),
],
@ -4290,7 +4210,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
args: vec![
(
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,
@ -4317,7 +4237,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
(list_var, Var(Symbol::ARG_1)),
(
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,
@ -4377,7 +4297,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
args: vec![
(
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,
@ -4423,7 +4343,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_var,
num_precision_var,
1,
num_no_bound(),
int_no_bound(),
),
),
],
@ -5144,12 +5064,7 @@ fn defn_help(
}
#[inline(always)]
fn int_min_or_max<I128>(
symbol: Symbol,
var_store: &mut VarStore,
i: I128,
bound: NumericBound<IntWidth>,
) -> Def
fn int_min_or_max<I128>(symbol: Symbol, var_store: &mut VarStore, i: I128, bound: IntBound) -> Def
where
I128: Into<i128>,
{
@ -5178,17 +5093,20 @@ where
}
}
fn num_no_bound<W: Copy>() -> NumericBound<W> {
fn num_no_bound() -> NumericBound {
NumericBound::None
}
fn int_no_bound() -> IntBound {
IntBound::None
}
fn float_no_bound() -> FloatBound {
FloatBound::None
}
#[inline(always)]
fn int<I128>(
num_var: Variable,
precision_var: Variable,
i: I128,
bound: NumericBound<IntWidth>,
) -> Expr
fn int<I128>(num_var: Variable, precision_var: Variable, i: I128, bound: IntBound) -> Expr
where
I128: Into<i128>,
{
@ -5203,12 +5121,7 @@ where
}
#[inline(always)]
fn float(
num_var: Variable,
precision_var: Variable,
f: f64,
bound: NumericBound<FloatWidth>,
) -> Expr {
fn float(num_var: Variable, precision_var: Variable, f: f64, bound: FloatBound) -> Expr {
Float(
num_var,
precision_var,
@ -5219,7 +5132,7 @@ fn float(
}
#[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();
Num(
num_var,

View file

@ -27,6 +27,14 @@ pub enum Constraint {
Let(Box<LetConstraint>),
And(Vec<Constraint>),
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)]
@ -87,6 +95,7 @@ impl Constraint {
}
Constraint::And(cs) => cs.iter().any(|c| c.contains_save_the_environment()),
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::num::{
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::procedure::References;
@ -67,17 +67,11 @@ pub enum Expr {
// 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>, IntValue, NumericBound<NumWidth>),
Num(Variable, Box<str>, IntValue, NumericBound),
// Int and Float store a variable to generate better error messages
Int(
Variable,
Variable,
Box<str>,
IntValue,
NumericBound<IntWidth>,
),
Float(Variable, Variable, Box<str>, f64, NumericBound<FloatWidth>),
Int(Variable, Variable, Box<str>, IntValue, IntBound),
Float(Variable, Variable, Box<str>, f64, FloatBound),
Str(Box<str>),
List {
elem_var: Variable,

View file

@ -50,7 +50,7 @@ pub fn num_expr_from_result(
#[inline(always)]
pub fn int_expr_from_result(
var_store: &mut VarStore,
result: Result<(&str, IntValue, NumericBound<IntWidth>), (&str, IntErrorKind)>,
result: Result<(&str, IntValue, IntBound), (&str, IntErrorKind)>,
region: Region,
base: Base,
env: &mut Env,
@ -77,7 +77,7 @@ pub fn int_expr_from_result(
#[inline(always)]
pub fn float_expr_from_result(
var_store: &mut VarStore,
result: Result<(&str, f64, NumericBound<FloatWidth>), (&str, FloatErrorKind)>,
result: Result<(&str, f64, FloatBound), (&str, FloatErrorKind)>,
region: Region,
env: &mut Env,
) -> Expr {
@ -101,8 +101,8 @@ pub fn float_expr_from_result(
}
pub enum ParsedNumResult {
Int(IntValue, NumericBound<IntWidth>),
Float(f64, NumericBound<FloatWidth>),
Int(IntValue, IntBound),
Float(f64, FloatBound),
UnknownNum(IntValue),
}
@ -115,15 +115,13 @@ pub fn finish_parsing_num(raw: &str) -> Result<ParsedNumResult, (&str, IntErrorK
// Let's try to specialize the number
Ok(match bound {
NumericBound::None => ParsedNumResult::UnknownNum(num),
NumericBound::Exact(NumWidth::Int(iw)) => {
ParsedNumResult::Int(num, NumericBound::Exact(iw))
}
NumericBound::Exact(NumWidth::Float(fw)) => {
NumericBound::Int(ib) => ParsedNumResult::Int(num, ib),
NumericBound::Float(fb) => {
let num = match num {
IntValue::I128(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,
base: Base,
is_negative: bool,
) -> Result<(IntValue, NumericBound<IntWidth>), (&str, IntErrorKind)> {
) -> Result<(IntValue, IntBound), (&str, IntErrorKind)> {
let radix = match base {
Base::Hex => 16,
Base::Decimal => 10,
@ -149,9 +147,9 @@ pub fn finish_parsing_base(
})
.and_then(|(n, bound)| {
let bound = match bound {
NumericBound::None => NumericBound::None,
NumericBound::Exact(NumWidth::Int(iw)) => NumericBound::Exact(iw),
NumericBound::Exact(NumWidth::Float(_)) => return Err(IntErrorKind::FloatSuffix),
NumericBound::None => IntBound::None,
NumericBound::Int(ib) => ib,
NumericBound::Float(_) => return Err(IntErrorKind::FloatSuffix),
};
Ok((n, bound))
})
@ -159,15 +157,13 @@ pub fn finish_parsing_base(
}
#[inline(always)]
pub fn finish_parsing_float(
raw: &str,
) -> Result<(f64, NumericBound<FloatWidth>), (&str, FloatErrorKind)> {
pub fn finish_parsing_float(raw: &str) -> Result<(f64, FloatBound), (&str, FloatErrorKind)> {
let (opt_bound, raw_without_suffix) = parse_literal_suffix(raw);
let bound = match opt_bound {
None => NumericBound::None,
Some(NumWidth::Float(fw)) => NumericBound::Exact(fw),
Some(NumWidth::Int(_)) => return Err((raw, FloatErrorKind::IntSuffix)),
None => FloatBound::None,
Some(ParsedWidth::Float(fw)) => FloatBound::Exact(fw),
Some(ParsedWidth::Int(_)) => return Err((raw, FloatErrorKind::IntSuffix)),
};
// 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 {
($($suffix:expr, $width:expr)*) => {$(
if num_str.ends_with($suffix) {
@ -194,20 +196,20 @@ fn parse_literal_suffix(num_str: &str) -> (Option<NumWidth>, &str) {
}
parse_num_suffix! {
"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)
"u8", ParsedWidth::Int(IntWidth::U8)
"u16", ParsedWidth::Int(IntWidth::U16)
"u32", ParsedWidth::Int(IntWidth::U32)
"u64", ParsedWidth::Int(IntWidth::U64)
"u128", ParsedWidth::Int(IntWidth::U128)
"i8", ParsedWidth::Int(IntWidth::I8)
"i16", ParsedWidth::Int(IntWidth::I16)
"i32", ParsedWidth::Int(IntWidth::I32)
"i64", ParsedWidth::Int(IntWidth::I64)
"i128", ParsedWidth::Int(IntWidth::I128)
"nat", ParsedWidth::Int(IntWidth::Nat)
"dec", ParsedWidth::Float(FloatWidth::Dec)
"f32", ParsedWidth::Float(FloatWidth::F32)
"f64", ParsedWidth::Float(FloatWidth::F64)
}
(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.
///
/// Thanks to the Rust project and its contributors!
fn from_str_radix(
src: &str,
radix: u32,
) -> Result<(IntValue, NumericBound<NumWidth>), IntErrorKind> {
fn from_str_radix(src: &str, radix: u32) -> Result<(IntValue, NumericBound), IntErrorKind> {
use self::IntErrorKind::*;
assert!(
@ -268,19 +267,31 @@ fn from_str_radix(
match opt_exact_bound {
None => {
// TODO: use the lower bound
Ok((result, NumericBound::None))
// There's no exact bound, but we do have a lower bound.
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
// 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.
if exact_width.is_superset(&lower_bound, is_negative) {
// Great! Use the exact bound.
Ok((result, NumericBound::Exact(NumWidth::Int(exact_width))))
Ok((result, IntBound::Exact(exact_width).into()))
} 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
@ -474,19 +485,47 @@ pub enum FloatWidth {
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum NumWidth {
Int(IntWidth),
Float(FloatWidth),
pub enum SignDemand {
/// Can be signed or unsigned.
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)]
pub enum NumericBound<W>
where
W: Copy,
{
pub enum IntBound {
/// There is no bound on the width.
None,
/// Must have exactly the width `W`.
Exact(W),
/// Must have an exact width.
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::expr::{canonicalize_expr, unescape_char, Expr, IntValue, Output};
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,
};
use crate::scope::Scope;
@ -29,15 +29,9 @@ pub enum Pattern {
ext_var: Variable,
destructs: Vec<Loc<RecordDestruct>>,
},
NumLiteral(Variable, Box<str>, IntValue, NumericBound<NumWidth>),
IntLiteral(
Variable,
Variable,
Box<str>,
IntValue,
NumericBound<IntWidth>,
),
FloatLiteral(Variable, Variable, Box<str>, f64, NumericBound<FloatWidth>),
NumLiteral(Variable, Box<str>, IntValue, NumericBound),
IntLiteral(Variable, Variable, Box<str>, IntValue, IntBound),
FloatLiteral(Variable, Variable, Box<str>, f64, FloatBound),
StrLiteral(Box<str>),
Underscore,

View file

@ -1,7 +1,7 @@
use roc_can::constraint::Constraint::{self, *};
use roc_can::constraint::LetConstraint;
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_module::ident::{Lowercase, TagName};
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)]
pub fn int_literal(
num_var: Variable,
precision_var: Variable,
expected: Expected<Type>,
region: Region,
bound: NumericBound<IntWidth>,
bound: IntBound,
) -> Constraint {
let num_type = Variable(num_var);
let reason = Reason::IntLiteral;
@ -50,8 +68,15 @@ pub fn int_literal(
Category::Int,
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))
}
@ -62,7 +87,7 @@ pub fn float_literal(
precision_var: Variable,
expected: Expected<Type>,
region: Region,
bound: NumericBound<FloatWidth>,
bound: FloatBound,
) -> Constraint {
let num_type = Variable(num_var);
let reason = Reason::FloatLiteral;
@ -93,7 +118,7 @@ pub fn num_literal(
num_var: Variable,
expected: Expected<Type>,
region: Region,
bound: NumericBound<NumWidth>,
bound: NumericBound,
) -> Constraint {
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.
/// Returns `None` e.g. if the bound is open, like `Int *`.
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> {
match self {
NumericBound::None => None,
NumericBound::Exact(w) => Some(match w {
IntBound::None | IntBound::AtLeast { .. } => None,
IntBound::Exact(w) => Some(match w {
IntWidth::U8 => num_u8(),
IntWidth::U16 => num_u16(),
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> {
match self {
NumericBound::None => None,
NumericBound::Exact(w) => Some(match w {
FloatBound::None => None,
FloatBound::Exact(w) => Some(match w {
FloatWidth::Dec => num_dec(),
FloatWidth::F32 => num_f32(),
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> {
match self {
NumericBound::None => None,
NumericBound::Exact(NumWidth::Int(iw)) => NumericBound::Exact(*iw).concrete_num_type(),
NumericBound::Exact(NumWidth::Float(fw)) => {
NumericBound::Exact(*fw).concrete_num_type()
}
NumericBound::Int(ib) => ib.concrete_num_type(),
NumericBound::Float(fb) => fb.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
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
`True` or `False`.
@ -896,7 +896,7 @@ mod test_reporting {
but the `then` branch has the type:
Num a
Int a
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:
Num a
Int a
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:
Num a
Int a
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:
Num a
Int a
"#
),
)
@ -1455,7 +1455,7 @@ mod test_reporting {
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:
{ foo : Num a }
{ foo : Int a }
"#
),
)
@ -1605,13 +1605,13 @@ mod test_reporting {
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:
{ foo : Num a }
{ foo : Int a }
"#
),
)
@ -1637,9 +1637,9 @@ mod test_reporting {
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:
@ -2162,8 +2162,8 @@ mod test_reporting {
This is usually a typo. Here are the `x` fields that are most similar:
{ fo : Num b
, bar : Num a
{ fo : Int b
, bar : Int a
}
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:
{ fo : Num c
, foobar : Num d
, bar : Num a
, baz : Num b
{ fo : Int c
, foobar : Int d
, bar : Int a
, baz : Int b
, ...
}
@ -2327,7 +2327,7 @@ mod test_reporting {
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:
[ ACons Num (Integer Signed64) [ BCons (Num a) [ ACons Str [ BNil
]b ]c ]d, ANil ]
[ ACons Int Signed64 [ BCons (Int a) [ ACons Str [ BNil ]b ]c ]d,
ANil ]
But the type annotation on `x` says it should be:
@ -4973,7 +4973,7 @@ mod test_reporting {
This `insert` call produces:
Dict Str (Num a)
Dict Str (Int a)
But the type annotation on `myDict` says it should be:
@ -5635,7 +5635,7 @@ mod test_reporting {
but the `then` branch has the type:
Num a
Int a
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:
List [ Foo Num a ]
List [ Foo Int a ]
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:
Num a -> Num a
Num (Integer a) -> Num (Integer a)
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:
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:
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
^
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:
@ -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
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
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.
"#
),
)
}
}