mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 07:49:17 +03:00
Merge pull request #3115 from rtfeldman/improve-range-checking
Optimize range checking
This commit is contained in:
commit
d8eaa135b2
@ -1849,18 +1849,8 @@ fn deep_copy_var_help(
|
||||
}
|
||||
|
||||
RangedNumber(typ, vars) => {
|
||||
let mut new_vars = Vec::with_capacity(vars.len());
|
||||
|
||||
for var_index in vars {
|
||||
let var = subs[var_index];
|
||||
let new_var = deep_copy_var_help(subs, max_rank, pools, var);
|
||||
new_vars.push(new_var);
|
||||
}
|
||||
|
||||
let new_slice = VariableSubsSlice::insert_into_subs(subs, new_vars.drain(..));
|
||||
|
||||
let new_real_type = deep_copy_var_help(subs, max_rank, pools, typ);
|
||||
let new_content = RangedNumber(new_real_type, new_slice);
|
||||
let new_content = RangedNumber(new_real_type, vars);
|
||||
|
||||
subs.set(copy, make_descriptor(new_content));
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::def::Def;
|
||||
use crate::expr::{self, AnnotatedMark, ClosureData, Expr::*, IntValue};
|
||||
use crate::expr::{Expr, Field, Recursive};
|
||||
use crate::num::{FloatBound, IntBound, IntWidth, NumericBound};
|
||||
use crate::num::{FloatBound, IntBound, IntWidth, NumBound};
|
||||
use crate::pattern::Pattern;
|
||||
use roc_collections::all::SendMap;
|
||||
use roc_module::called_via::CalledVia;
|
||||
@ -5414,8 +5414,8 @@ fn defn_help(
|
||||
})
|
||||
}
|
||||
|
||||
fn num_no_bound() -> NumericBound {
|
||||
NumericBound::None
|
||||
fn num_no_bound() -> NumBound {
|
||||
NumBound::None
|
||||
}
|
||||
|
||||
fn int_no_bound() -> IntBound {
|
||||
@ -5453,7 +5453,7 @@ fn frac(num_var: Variable, precision_var: Variable, f: f64, bound: FloatBound) -
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn num<I: Into<i128>>(num_var: Variable, i: I, bound: NumericBound) -> Expr {
|
||||
fn num<I: Into<i128>>(num_var: Variable, i: I, bound: NumBound) -> Expr {
|
||||
let i = i.into();
|
||||
Num(
|
||||
num_var,
|
||||
|
@ -5,7 +5,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, FloatBound, IntBound, NumericBound,
|
||||
int_expr_from_result, num_expr_from_result, FloatBound, IntBound, NumBound,
|
||||
};
|
||||
use crate::pattern::{canonicalize_pattern, BindingsFromPattern, Pattern};
|
||||
use crate::procedure::References;
|
||||
@ -82,7 +82,7 @@ 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),
|
||||
Num(Variable, Box<str>, IntValue, NumBound),
|
||||
|
||||
// Int and Float store a variable to generate better error messages
|
||||
Int(Variable, Variable, Box<str>, IntValue, IntBound),
|
||||
|
@ -5,8 +5,9 @@ use roc_problem::can::Problem;
|
||||
use roc_problem::can::RuntimeError::*;
|
||||
use roc_problem::can::{FloatErrorKind, IntErrorKind};
|
||||
use roc_region::all::Region;
|
||||
pub use roc_types::num::{FloatBound, FloatWidth, IntBound, IntWidth, NumBound, SignDemand};
|
||||
use roc_types::subs::VarStore;
|
||||
use std::i64;
|
||||
|
||||
use std::str;
|
||||
|
||||
#[inline(always)]
|
||||
@ -103,7 +104,7 @@ pub fn float_expr_from_result(
|
||||
pub enum ParsedNumResult {
|
||||
Int(IntValue, IntBound),
|
||||
Float(f64, FloatBound),
|
||||
UnknownNum(IntValue, NumericBound),
|
||||
UnknownNum(IntValue, NumBound),
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -139,8 +140,8 @@ pub fn finish_parsing_base(
|
||||
.and_then(|parsed| match parsed {
|
||||
ParsedNumResult::Float(..) => Err(IntErrorKind::FloatSuffix),
|
||||
ParsedNumResult::Int(val, bound) => Ok((val, bound)),
|
||||
ParsedNumResult::UnknownNum(val, NumericBound::None) => Ok((val, IntBound::None)),
|
||||
ParsedNumResult::UnknownNum(val, NumericBound::AtLeastIntOrFloat { sign, width }) => {
|
||||
ParsedNumResult::UnknownNum(val, NumBound::None) => Ok((val, IntBound::None)),
|
||||
ParsedNumResult::UnknownNum(val, NumBound::AtLeastIntOrFloat { sign, width }) => {
|
||||
Ok((val, IntBound::AtLeast { sign, width }))
|
||||
}
|
||||
})
|
||||
@ -270,7 +271,7 @@ fn from_str_radix(src: &str, radix: u32) -> Result<ParsedNumResult, IntErrorKind
|
||||
};
|
||||
Ok(ParsedNumResult::UnknownNum(
|
||||
result,
|
||||
NumericBound::AtLeastIntOrFloat {
|
||||
NumBound::AtLeastIntOrFloat {
|
||||
sign: sign_demand,
|
||||
width: lower_bound,
|
||||
},
|
||||
@ -352,169 +353,3 @@ fn lower_bound_of_int(result: i128) -> IntWidth {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
enum IntSign {
|
||||
Unsigned,
|
||||
Signed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum IntWidth {
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
U128,
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
I64,
|
||||
I128,
|
||||
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,
|
||||
F32,
|
||||
F64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum SignDemand {
|
||||
/// Can be signed or unsigned.
|
||||
NoDemand,
|
||||
/// Must be signed.
|
||||
Signed,
|
||||
}
|
||||
|
||||
/// Describes a bound on the width of an integer.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum IntBound {
|
||||
/// There is no bound on the width.
|
||||
None,
|
||||
/// 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,
|
||||
/// Must be an integer of a certain size, or any float.
|
||||
AtLeastIntOrFloat {
|
||||
sign: SignDemand,
|
||||
width: IntWidth,
|
||||
},
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ use crate::annotation::freshen_opaque_def;
|
||||
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, FloatBound, IntBound,
|
||||
NumericBound, ParsedNumResult,
|
||||
finish_parsing_base, finish_parsing_float, finish_parsing_num, FloatBound, IntBound, NumBound,
|
||||
ParsedNumResult,
|
||||
};
|
||||
use crate::scope::Scope;
|
||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||
@ -55,7 +55,7 @@ pub enum Pattern {
|
||||
ext_var: Variable,
|
||||
destructs: Vec<Loc<RecordDestruct>>,
|
||||
},
|
||||
NumLiteral(Variable, Box<str>, IntValue, NumericBound),
|
||||
NumLiteral(Variable, Box<str>, IntValue, NumBound),
|
||||
IntLiteral(Variable, Variable, Box<str>, IntValue, IntBound),
|
||||
FloatLiteral(Variable, Variable, Box<str>, f64, FloatBound),
|
||||
StrLiteral(Box<str>),
|
||||
|
@ -1,9 +1,10 @@
|
||||
use arrayvec::ArrayVec;
|
||||
use roc_can::constraint::{Constraint, Constraints};
|
||||
use roc_can::expected::Expected::{self, *};
|
||||
use roc_can::num::{FloatBound, FloatWidth, IntBound, IntWidth, NumericBound, SignDemand};
|
||||
use roc_can::num::{FloatBound, FloatWidth, IntBound, IntWidth, NumBound, SignDemand};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::Region;
|
||||
use roc_types::num::NumericRange;
|
||||
use roc_types::subs::Variable;
|
||||
use roc_types::types::Type::{self, *};
|
||||
use roc_types::types::{AliasKind, Category};
|
||||
@ -19,14 +20,18 @@ pub fn add_numeric_bound_constr(
|
||||
region: Region,
|
||||
category: Category,
|
||||
) -> Type {
|
||||
let range = bound.bounded_range();
|
||||
|
||||
let range = bound.numeric_bound();
|
||||
let total_num_type = num_type;
|
||||
|
||||
match range.len() {
|
||||
0 => total_num_type,
|
||||
1 => {
|
||||
let actual_type = Variable(range[0]);
|
||||
use roc_types::num::{float_width_to_variable, int_width_to_variable};
|
||||
|
||||
match range {
|
||||
NumericBound::None => {
|
||||
// no additional constraints
|
||||
total_num_type
|
||||
}
|
||||
NumericBound::FloatExact(width) => {
|
||||
let actual_type = Variable(float_width_to_variable(width));
|
||||
let expected = Expected::ForReason(Reason::NumericLiteralSuffix, actual_type, region);
|
||||
let because_suffix =
|
||||
constraints.equal_types(total_num_type.clone(), expected, category, region);
|
||||
@ -35,7 +40,17 @@ pub fn add_numeric_bound_constr(
|
||||
|
||||
total_num_type
|
||||
}
|
||||
_ => RangedNumber(Box::new(total_num_type), range),
|
||||
NumericBound::IntExact(width) => {
|
||||
let actual_type = Variable(int_width_to_variable(width));
|
||||
let expected = Expected::ForReason(Reason::NumericLiteralSuffix, actual_type, region);
|
||||
let because_suffix =
|
||||
constraints.equal_types(total_num_type.clone(), expected, category, region);
|
||||
|
||||
num_constraints.extend([because_suffix]);
|
||||
|
||||
total_num_type
|
||||
}
|
||||
NumericBound::Range(range) => RangedNumber(Box::new(total_num_type), range),
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,7 +132,7 @@ pub fn num_literal(
|
||||
num_var: Variable,
|
||||
expected: Expected<Type>,
|
||||
region: Region,
|
||||
bound: NumericBound,
|
||||
bound: NumBound,
|
||||
) -> Constraint {
|
||||
let open_number_type = crate::builtins::num_num(Type::Variable(num_var));
|
||||
|
||||
@ -264,83 +279,57 @@ pub fn num_num(typ: Type) -> Type {
|
||||
}
|
||||
|
||||
pub trait TypedNumericBound {
|
||||
fn bounded_range(&self) -> Vec<Variable>;
|
||||
fn numeric_bound(&self) -> NumericBound;
|
||||
}
|
||||
|
||||
impl TypedNumericBound for IntBound {
|
||||
fn bounded_range(&self) -> Vec<Variable> {
|
||||
fn numeric_bound(&self) -> NumericBound {
|
||||
match self {
|
||||
IntBound::None => vec![],
|
||||
IntBound::Exact(w) => vec![match w {
|
||||
IntWidth::U8 => Variable::U8,
|
||||
IntWidth::U16 => Variable::U16,
|
||||
IntWidth::U32 => Variable::U32,
|
||||
IntWidth::U64 => Variable::U64,
|
||||
IntWidth::U128 => Variable::U128,
|
||||
IntWidth::I8 => Variable::I8,
|
||||
IntWidth::I16 => Variable::I16,
|
||||
IntWidth::I32 => Variable::I32,
|
||||
IntWidth::I64 => Variable::I64,
|
||||
IntWidth::I128 => Variable::I128,
|
||||
IntWidth::Nat => Variable::NAT,
|
||||
}],
|
||||
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)| *var)
|
||||
.collect()
|
||||
}
|
||||
IntBound::None => NumericBound::None,
|
||||
IntBound::Exact(w) => NumericBound::IntExact(*w),
|
||||
IntBound::AtLeast {
|
||||
sign: SignDemand::NoDemand,
|
||||
width,
|
||||
} => NumericBound::Range(NumericRange::IntAtLeastEitherSign(*width)),
|
||||
IntBound::AtLeast {
|
||||
sign: SignDemand::Signed,
|
||||
width,
|
||||
} => NumericBound::Range(NumericRange::IntAtLeastSigned(*width)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypedNumericBound for FloatBound {
|
||||
fn bounded_range(&self) -> Vec<Variable> {
|
||||
fn numeric_bound(&self) -> NumericBound {
|
||||
match self {
|
||||
FloatBound::None => vec![],
|
||||
FloatBound::Exact(w) => vec![match w {
|
||||
FloatWidth::Dec => Variable::DEC,
|
||||
FloatWidth::F32 => Variable::F32,
|
||||
FloatWidth::F64 => Variable::F64,
|
||||
}],
|
||||
FloatBound::None => NumericBound::None,
|
||||
FloatBound::Exact(w) => NumericBound::FloatExact(*w),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypedNumericBound for NumericBound {
|
||||
fn bounded_range(&self) -> Vec<Variable> {
|
||||
impl TypedNumericBound for NumBound {
|
||||
fn numeric_bound(&self) -> NumericBound {
|
||||
match self {
|
||||
NumericBound::None => vec![],
|
||||
&NumericBound::AtLeastIntOrFloat { sign, width } => {
|
||||
let mut range = IntBound::AtLeast { sign, width }.bounded_range();
|
||||
range.extend_from_slice(&[Variable::F32, Variable::F64, Variable::DEC]);
|
||||
range
|
||||
}
|
||||
NumBound::None => NumericBound::None,
|
||||
&NumBound::AtLeastIntOrFloat {
|
||||
sign: SignDemand::NoDemand,
|
||||
width,
|
||||
} => NumericBound::Range(NumericRange::NumAtLeastEitherSign(width)),
|
||||
&NumBound::AtLeastIntOrFloat {
|
||||
sign: SignDemand::Signed,
|
||||
width,
|
||||
} => NumericBound::Range(NumericRange::NumAtLeastSigned(width)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A bound placed on a number because of its literal value.
|
||||
/// e.g. `-5` cannot be unsigned, and 300 does not fit in a U8
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum NumericBound {
|
||||
None,
|
||||
FloatExact(FloatWidth),
|
||||
IntExact(IntWidth),
|
||||
Range(NumericRange),
|
||||
}
|
||||
|
@ -611,15 +611,10 @@ fn deep_copy_type_vars<'a>(
|
||||
})
|
||||
}
|
||||
|
||||
RangedNumber(typ, range_vars) => {
|
||||
RangedNumber(typ, range) => {
|
||||
let new_typ = descend_var!(typ);
|
||||
descend_slice!(range_vars);
|
||||
|
||||
perform_clone!({
|
||||
let new_range_vars = clone_var_slice!(range_vars);
|
||||
|
||||
RangedNumber(new_typ, new_range_vars)
|
||||
})
|
||||
perform_clone!(RangedNumber(new_typ, range))
|
||||
}
|
||||
Error => Error,
|
||||
};
|
||||
|
@ -1854,10 +1854,9 @@ fn type_to_variable<'a>(
|
||||
Variable(_) | EmptyRec | EmptyTagUnion => {
|
||||
unreachable!("This variant should never be deferred!")
|
||||
}
|
||||
RangedNumber(typ, vars) => {
|
||||
RangedNumber(typ, range) => {
|
||||
let ty_var = helper!(typ);
|
||||
let vars = VariableSubsSlice::insert_into_subs(subs, vars.iter().copied());
|
||||
let content = Content::RangedNumber(ty_var, vars);
|
||||
let content = Content::RangedNumber(ty_var, *range);
|
||||
|
||||
register_with_known_var(subs, destination, rank, pools, content)
|
||||
}
|
||||
@ -3015,10 +3014,8 @@ fn instantiate_rigids_help(subs: &mut Subs, max_rank: Rank, initial: Variable) {
|
||||
|
||||
stack.push(var);
|
||||
}
|
||||
&RangedNumber(typ, vars) => {
|
||||
&RangedNumber(typ, _) => {
|
||||
stack.push(typ);
|
||||
|
||||
stack.extend(var_slice!(vars));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3267,12 +3264,10 @@ fn deep_copy_var_help(
|
||||
copy
|
||||
}
|
||||
|
||||
RangedNumber(typ, range_vars) => {
|
||||
RangedNumber(typ, range) => {
|
||||
let new_type_var = deep_copy_var_help(subs, max_rank, pool, visited, typ);
|
||||
|
||||
let new_variables = copy_sequence!(range_vars.len(), range_vars);
|
||||
|
||||
let new_content = RangedNumber(new_type_var, new_variables);
|
||||
let new_content = RangedNumber(new_type_var, range);
|
||||
|
||||
subs.set_content_unchecked(copy, new_content);
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
pub mod builtin_aliases;
|
||||
pub mod num;
|
||||
pub mod pretty_print;
|
||||
pub mod solved_types;
|
||||
pub mod subs;
|
||||
|
327
compiler/types/src/num.rs
Normal file
327
compiler/types/src/num.rs
Normal file
@ -0,0 +1,327 @@
|
||||
use crate::subs::Variable;
|
||||
use roc_module::symbol::Symbol;
|
||||
|
||||
/// A bound placed on a number because of its literal value.
|
||||
/// e.g. `-5` cannot be unsigned, and 300 does not fit in a U8
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum NumericRange {
|
||||
IntAtLeastSigned(IntWidth),
|
||||
IntAtLeastEitherSign(IntWidth),
|
||||
NumAtLeastSigned(IntWidth),
|
||||
NumAtLeastEitherSign(IntWidth),
|
||||
}
|
||||
|
||||
impl NumericRange {
|
||||
pub fn contains_symbol(&self, symbol: Symbol) -> Option<bool> {
|
||||
let contains = match symbol {
|
||||
Symbol::NUM_I8 => self.contains_int_width(IntWidth::I8),
|
||||
Symbol::NUM_U8 => self.contains_int_width(IntWidth::U8),
|
||||
Symbol::NUM_I16 => self.contains_int_width(IntWidth::I16),
|
||||
Symbol::NUM_U16 => self.contains_int_width(IntWidth::U16),
|
||||
Symbol::NUM_I32 => self.contains_int_width(IntWidth::I32),
|
||||
Symbol::NUM_U32 => self.contains_int_width(IntWidth::U32),
|
||||
Symbol::NUM_I64 => self.contains_int_width(IntWidth::I64),
|
||||
Symbol::NUM_NAT => self.contains_int_width(IntWidth::Nat),
|
||||
Symbol::NUM_U64 => self.contains_int_width(IntWidth::U64),
|
||||
Symbol::NUM_I128 => self.contains_int_width(IntWidth::I128),
|
||||
Symbol::NUM_U128 => self.contains_int_width(IntWidth::U128),
|
||||
|
||||
Symbol::NUM_DEC => self.contains_float_width(FloatWidth::Dec),
|
||||
Symbol::NUM_F32 => self.contains_float_width(FloatWidth::F32),
|
||||
Symbol::NUM_F64 => self.contains_float_width(FloatWidth::F64),
|
||||
|
||||
Symbol::NUM_NUM | Symbol::NUM_INT | Symbol::NUM_FRAC => {
|
||||
// these satisfy any range that they are given
|
||||
true
|
||||
}
|
||||
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some(contains)
|
||||
}
|
||||
|
||||
fn contains_float_width(&self, _width: FloatWidth) -> bool {
|
||||
// we don't currently check the float width
|
||||
true
|
||||
}
|
||||
|
||||
fn contains_int_width(&self, width: IntWidth) -> bool {
|
||||
use NumericRange::*;
|
||||
|
||||
let (range_signedness, at_least_width) = match self {
|
||||
IntAtLeastSigned(width) => (SignDemand::Signed, width),
|
||||
IntAtLeastEitherSign(width) => (SignDemand::NoDemand, width),
|
||||
NumAtLeastSigned(width) => (SignDemand::Signed, width),
|
||||
NumAtLeastEitherSign(width) => (SignDemand::NoDemand, width),
|
||||
};
|
||||
|
||||
let (actual_signedness, _) = width.signedness_and_width();
|
||||
|
||||
if let (IntSignedness::Unsigned, SignDemand::Signed) = (actual_signedness, range_signedness)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
width.signedness_and_width().1 >= at_least_width.signedness_and_width().1
|
||||
}
|
||||
|
||||
pub fn variable_slice(&self) -> &'static [Variable] {
|
||||
use NumericRange::*;
|
||||
|
||||
match self {
|
||||
IntAtLeastSigned(width) => {
|
||||
let target = int_width_to_variable(*width);
|
||||
let start = SIGNED_VARIABLES.iter().position(|v| *v == target).unwrap();
|
||||
let end = SIGNED_VARIABLES.len() - 3;
|
||||
|
||||
&SIGNED_VARIABLES[start..end]
|
||||
}
|
||||
IntAtLeastEitherSign(width) => {
|
||||
let target = int_width_to_variable(*width);
|
||||
let start = ALL_VARIABLES.iter().position(|v| *v == target).unwrap();
|
||||
let end = ALL_VARIABLES.len() - 3;
|
||||
|
||||
&ALL_VARIABLES[start..end]
|
||||
}
|
||||
NumAtLeastSigned(width) => {
|
||||
let target = int_width_to_variable(*width);
|
||||
let start = SIGNED_VARIABLES.iter().position(|v| *v == target).unwrap();
|
||||
|
||||
&SIGNED_VARIABLES[start..]
|
||||
}
|
||||
NumAtLeastEitherSign(width) => {
|
||||
let target = int_width_to_variable(*width);
|
||||
let start = ALL_VARIABLES.iter().position(|v| *v == target).unwrap();
|
||||
|
||||
&ALL_VARIABLES[start..]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
enum IntSignedness {
|
||||
Unsigned,
|
||||
Signed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum IntWidth {
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
U128,
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
I64,
|
||||
I128,
|
||||
Nat,
|
||||
}
|
||||
|
||||
impl IntWidth {
|
||||
/// Returns the `IntSignedness` and bit width of a variant.
|
||||
fn signedness_and_width(&self) -> (IntSignedness, u32) {
|
||||
use IntSignedness::*;
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
pub 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",
|
||||
}
|
||||
}
|
||||
|
||||
pub 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,
|
||||
}
|
||||
}
|
||||
|
||||
pub 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 IntSignedness::*;
|
||||
|
||||
if is_negative {
|
||||
match (
|
||||
self.signedness_and_width(),
|
||||
lower_bound.signedness_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.signedness_and_width(),
|
||||
lower_bound.signedness_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,
|
||||
F32,
|
||||
F64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum SignDemand {
|
||||
/// Can be signed or unsigned.
|
||||
NoDemand,
|
||||
/// Must be signed.
|
||||
Signed,
|
||||
}
|
||||
|
||||
/// Describes a bound on the width of an integer.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum IntBound {
|
||||
/// There is no bound on the width.
|
||||
None,
|
||||
/// 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 NumBound {
|
||||
None,
|
||||
/// Must be an integer of a certain size, or any float.
|
||||
AtLeastIntOrFloat {
|
||||
sign: SignDemand,
|
||||
width: IntWidth,
|
||||
},
|
||||
}
|
||||
|
||||
pub const fn int_width_to_variable(w: IntWidth) -> Variable {
|
||||
match w {
|
||||
IntWidth::U8 => Variable::U8,
|
||||
IntWidth::U16 => Variable::U16,
|
||||
IntWidth::U32 => Variable::U32,
|
||||
IntWidth::U64 => Variable::U64,
|
||||
IntWidth::U128 => Variable::U128,
|
||||
IntWidth::I8 => Variable::I8,
|
||||
IntWidth::I16 => Variable::I16,
|
||||
IntWidth::I32 => Variable::I32,
|
||||
IntWidth::I64 => Variable::I64,
|
||||
IntWidth::I128 => Variable::I128,
|
||||
IntWidth::Nat => Variable::NAT,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn float_width_to_variable(w: FloatWidth) -> Variable {
|
||||
match w {
|
||||
FloatWidth::Dec => Variable::DEC,
|
||||
FloatWidth::F32 => Variable::F32,
|
||||
FloatWidth::F64 => Variable::F64,
|
||||
}
|
||||
}
|
||||
|
||||
const ALL_VARIABLES: &[Variable] = &[
|
||||
Variable::I8,
|
||||
Variable::U8,
|
||||
Variable::I16,
|
||||
Variable::U16,
|
||||
Variable::I32,
|
||||
Variable::U32,
|
||||
Variable::I64,
|
||||
Variable::NAT, // FIXME: Nat's order here depends on the platfor,
|
||||
Variable::U64,
|
||||
Variable::I128,
|
||||
Variable::U128,
|
||||
Variable::F32,
|
||||
Variable::F64,
|
||||
Variable::DEC,
|
||||
];
|
||||
|
||||
const SIGNED_VARIABLES: &[Variable] = &[
|
||||
Variable::I8,
|
||||
Variable::I16,
|
||||
Variable::I32,
|
||||
Variable::I64,
|
||||
Variable::I128,
|
||||
Variable::F32,
|
||||
Variable::F64,
|
||||
Variable::DEC,
|
||||
];
|
@ -220,12 +220,8 @@ fn find_names_needed(
|
||||
// TODO should we also look in the actual variable?
|
||||
// find_names_needed(_actual, subs, roots, root_appearances, names_taken);
|
||||
}
|
||||
&RangedNumber(typ, vars) => {
|
||||
&RangedNumber(typ, _) => {
|
||||
find_names_needed(typ, subs, roots, root_appearances, names_taken);
|
||||
for var_index in vars {
|
||||
let var = subs[var_index];
|
||||
find_names_needed(var, subs, roots, root_appearances, names_taken);
|
||||
}
|
||||
}
|
||||
Error | Structure(Erroneous(_)) | Structure(EmptyRecord) | Structure(EmptyTagUnion) => {
|
||||
// Errors and empty records don't need names.
|
||||
|
@ -713,8 +713,7 @@ fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt:
|
||||
)
|
||||
}
|
||||
Content::RangedNumber(typ, range) => {
|
||||
let slice = subs.get_subs_slice(*range);
|
||||
write!(f, "RangedNumber({:?}, {:?})", typ, slice)
|
||||
write!(f, "RangedNumber({:?}, {:?})", typ, range)
|
||||
}
|
||||
Content::Error => write!(f, "Error"),
|
||||
}
|
||||
@ -2009,7 +2008,7 @@ pub enum Content {
|
||||
},
|
||||
Structure(FlatType),
|
||||
Alias(Symbol, AliasVariables, Variable, AliasKind),
|
||||
RangedNumber(Variable, VariableSubsSlice),
|
||||
RangedNumber(Variable, crate::num::NumericRange),
|
||||
Error,
|
||||
}
|
||||
|
||||
@ -3025,16 +3024,10 @@ fn explicit_substitute(
|
||||
|
||||
in_var
|
||||
}
|
||||
RangedNumber(typ, vars) => {
|
||||
for index in vars.into_iter() {
|
||||
let var = subs[index];
|
||||
let new_var = explicit_substitute(subs, from, to, var, seen);
|
||||
subs[index] = new_var;
|
||||
}
|
||||
|
||||
RangedNumber(typ, range) => {
|
||||
let new_typ = explicit_substitute(subs, from, to, typ, seen);
|
||||
|
||||
subs.set_content(in_var, RangedNumber(new_typ, vars));
|
||||
subs.set_content(in_var, RangedNumber(new_typ, range));
|
||||
|
||||
in_var
|
||||
}
|
||||
@ -3094,12 +3087,7 @@ fn get_var_names(
|
||||
get_var_names(subs, subs[arg_var], answer)
|
||||
}),
|
||||
|
||||
RangedNumber(typ, vars) => {
|
||||
let taken_names = get_var_names(subs, typ, taken_names);
|
||||
vars.into_iter().fold(taken_names, |answer, var| {
|
||||
get_var_names(subs, subs[var], answer)
|
||||
})
|
||||
}
|
||||
RangedNumber(typ, _) => get_var_names(subs, typ, taken_names),
|
||||
|
||||
Structure(flat_type) => match flat_type {
|
||||
FlatType::Apply(_, args) => {
|
||||
@ -3340,12 +3328,12 @@ fn content_to_err_type(
|
||||
RangedNumber(typ, range) => {
|
||||
let err_type = var_to_err_type(subs, state, typ);
|
||||
|
||||
if state.context == ErrorTypeContext::ExpandRanges {
|
||||
let mut types = Vec::with_capacity(range.len());
|
||||
for var_index in range {
|
||||
let var = subs[var_index];
|
||||
dbg!(range);
|
||||
|
||||
types.push(var_to_err_type(subs, state, var));
|
||||
if state.context == ErrorTypeContext::ExpandRanges {
|
||||
let mut types = Vec::new();
|
||||
for var in range.variable_slice() {
|
||||
types.push(var_to_err_type(subs, state, *var));
|
||||
}
|
||||
ErrorType::Range(Box::new(err_type), types)
|
||||
} else {
|
||||
@ -3645,9 +3633,8 @@ fn restore_help(subs: &mut Subs, initial: Variable) {
|
||||
stack.push(*var);
|
||||
}
|
||||
|
||||
RangedNumber(typ, vars) => {
|
||||
RangedNumber(typ, _vars) => {
|
||||
stack.push(*typ);
|
||||
stack.extend(var_slice(*vars));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3833,10 +3820,7 @@ impl StorageSubs {
|
||||
Self::offset_variable(offsets, *actual),
|
||||
*kind,
|
||||
),
|
||||
RangedNumber(typ, vars) => RangedNumber(
|
||||
Self::offset_variable(offsets, *typ),
|
||||
Self::offset_variable_slice(offsets, *vars),
|
||||
),
|
||||
RangedNumber(typ, range) => RangedNumber(Self::offset_variable(offsets, *typ), *range),
|
||||
Error => Content::Error,
|
||||
}
|
||||
}
|
||||
@ -4262,18 +4246,10 @@ fn deep_copy_var_to_help(env: &mut DeepCopyVarToEnv<'_>, var: Variable) -> Varia
|
||||
copy
|
||||
}
|
||||
|
||||
RangedNumber(typ, vars) => {
|
||||
RangedNumber(typ, range) => {
|
||||
let new_typ = deep_copy_var_to_help(env, typ);
|
||||
|
||||
let new_vars = SubsSlice::reserve_into_subs(env.target, vars.len());
|
||||
|
||||
for (target_index, var_index) in (new_vars.indices()).zip(vars) {
|
||||
let var = env.source[var_index];
|
||||
let copy_var = deep_copy_var_to_help(env, var);
|
||||
env.target.variables[target_index] = copy_var;
|
||||
}
|
||||
|
||||
let new_content = RangedNumber(new_typ, new_vars);
|
||||
let new_content = RangedNumber(new_typ, range);
|
||||
|
||||
env.target.set(copy, make_descriptor(new_content));
|
||||
copy
|
||||
@ -4731,18 +4707,10 @@ fn copy_import_to_help(env: &mut CopyImportEnv<'_>, max_rank: Rank, var: Variabl
|
||||
copy
|
||||
}
|
||||
|
||||
RangedNumber(typ, vars) => {
|
||||
RangedNumber(typ, range) => {
|
||||
let new_typ = copy_import_to_help(env, max_rank, typ);
|
||||
|
||||
let new_vars = SubsSlice::reserve_into_subs(env.target, vars.len());
|
||||
|
||||
for (target_index, var_index) in (new_vars.indices()).zip(vars) {
|
||||
let var = env.source[var_index];
|
||||
let copy_var = copy_import_to_help(env, max_rank, var);
|
||||
env.target.variables[target_index] = copy_var;
|
||||
}
|
||||
|
||||
let new_content = RangedNumber(new_typ, new_vars);
|
||||
let new_content = RangedNumber(new_typ, range);
|
||||
|
||||
env.target.set(copy, make_descriptor(new_content));
|
||||
copy
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::num::NumericRange;
|
||||
use crate::pretty_print::Parens;
|
||||
use crate::subs::{
|
||||
GetSubsSlice, RecordFields, Subs, UnionTags, VarStore, Variable, VariableSubsSlice,
|
||||
@ -254,7 +255,7 @@ pub enum Type {
|
||||
/// Applying a type to some arguments (e.g. Dict.Dict String Int)
|
||||
Apply(Symbol, Vec<Type>, Region),
|
||||
Variable(Variable),
|
||||
RangedNumber(Box<Type>, Vec<Variable>),
|
||||
RangedNumber(Box<Type>, NumericRange),
|
||||
/// A type error, which will code gen to a runtime error
|
||||
Erroneous(Problem),
|
||||
}
|
||||
@ -324,7 +325,7 @@ impl Clone for Type {
|
||||
}
|
||||
Self::Apply(arg0, arg1, arg2) => Self::Apply(*arg0, arg1.clone(), *arg2),
|
||||
Self::Variable(arg0) => Self::Variable(*arg0),
|
||||
Self::RangedNumber(arg0, arg1) => Self::RangedNumber(arg0.clone(), arg1.clone()),
|
||||
Self::RangedNumber(arg0, arg1) => Self::RangedNumber(arg0.clone(), *arg1),
|
||||
Self::Erroneous(arg0) => Self::Erroneous(arg0.clone()),
|
||||
}
|
||||
}
|
||||
@ -1089,9 +1090,7 @@ impl Type {
|
||||
} => actual_type.contains_variable(rep_variable),
|
||||
HostExposedAlias { actual, .. } => actual.contains_variable(rep_variable),
|
||||
Apply(_, args, _) => args.iter().any(|arg| arg.contains_variable(rep_variable)),
|
||||
RangedNumber(typ, vars) => {
|
||||
typ.contains_variable(rep_variable) || vars.iter().any(|&v| v == rep_variable)
|
||||
}
|
||||
RangedNumber(typ, _) => typ.contains_variable(rep_variable),
|
||||
EmptyRec | EmptyTagUnion | Erroneous(_) => false,
|
||||
}
|
||||
}
|
||||
@ -1594,9 +1593,8 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
||||
}
|
||||
variables_help(actual, accum);
|
||||
}
|
||||
RangedNumber(typ, vars) => {
|
||||
RangedNumber(typ, _) => {
|
||||
variables_help(typ, accum);
|
||||
accum.extend(vars.iter().copied());
|
||||
}
|
||||
Apply(_, args, _) => {
|
||||
for x in args {
|
||||
@ -1730,9 +1728,8 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
|
||||
}
|
||||
variables_help_detailed(actual, accum);
|
||||
}
|
||||
RangedNumber(typ, vars) => {
|
||||
RangedNumber(typ, _) => {
|
||||
variables_help_detailed(typ, accum);
|
||||
accum.type_variables.extend(vars);
|
||||
}
|
||||
Apply(_, args, _) => {
|
||||
for x in args {
|
||||
|
@ -5,6 +5,7 @@ use roc_debug_flags::{ROC_PRINT_MISMATCHES, ROC_PRINT_UNIFICATIONS};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_types::num::NumericRange;
|
||||
use roc_types::subs::Content::{self, *};
|
||||
use roc_types::subs::{
|
||||
AliasVariables, Descriptor, ErrorTypeContext, FlatType, GetSubsSlice, Mark, OptVariable,
|
||||
@ -413,7 +414,7 @@ fn unify_ranged_number(
|
||||
pool: &mut Pool,
|
||||
ctx: &Context,
|
||||
real_var: Variable,
|
||||
range_vars: VariableSubsSlice,
|
||||
range_vars: NumericRange,
|
||||
) -> Outcome {
|
||||
let other_content = &ctx.second_desc.content;
|
||||
|
||||
@ -431,7 +432,7 @@ fn unify_ranged_number(
|
||||
&RangedNumber(other_real_var, other_range_vars) => {
|
||||
let outcome = unify_pool(subs, pool, real_var, other_real_var, ctx.mode);
|
||||
if outcome.mismatches.is_empty() {
|
||||
check_valid_range(subs, pool, ctx.first, other_range_vars, ctx.mode)
|
||||
check_valid_range(subs, ctx.first, other_range_vars)
|
||||
} else {
|
||||
outcome
|
||||
}
|
||||
@ -444,41 +445,41 @@ fn unify_ranged_number(
|
||||
return outcome;
|
||||
}
|
||||
|
||||
check_valid_range(subs, pool, ctx.second, range_vars, ctx.mode)
|
||||
check_valid_range(subs, ctx.second, range_vars)
|
||||
}
|
||||
|
||||
fn check_valid_range(
|
||||
subs: &mut Subs,
|
||||
pool: &mut Pool,
|
||||
var: Variable,
|
||||
range: VariableSubsSlice,
|
||||
mode: Mode,
|
||||
) -> Outcome {
|
||||
let slice = subs.get_subs_slice(range).to_vec();
|
||||
fn check_valid_range(subs: &mut Subs, var: Variable, range: NumericRange) -> Outcome {
|
||||
let content = subs.get_content_without_compacting(var);
|
||||
|
||||
let mut it = slice.iter().peekable();
|
||||
while let Some(&possible_var) = it.next() {
|
||||
let snapshot = subs.snapshot();
|
||||
let old_pool = pool.clone();
|
||||
let outcome = unify_pool(subs, pool, var, possible_var, mode | Mode::RIGID_AS_FLEX);
|
||||
if outcome.mismatches.is_empty() {
|
||||
// Okay, we matched some type in the range.
|
||||
subs.rollback_to(snapshot);
|
||||
*pool = old_pool;
|
||||
return Outcome::default();
|
||||
} else if it.peek().is_some() {
|
||||
// We failed to match something in the range, but there are still things we can try.
|
||||
subs.rollback_to(snapshot);
|
||||
*pool = old_pool;
|
||||
} else {
|
||||
subs.commit_snapshot(snapshot);
|
||||
match content {
|
||||
&Content::Alias(symbol, _, actual, _) => {
|
||||
match range.contains_symbol(symbol) {
|
||||
None => {
|
||||
// symbol not recognized; go into the alias
|
||||
return check_valid_range(subs, actual, range);
|
||||
}
|
||||
Some(false) => {
|
||||
let outcome = Outcome {
|
||||
mismatches: vec![Mismatch::TypeNotInRange],
|
||||
must_implement_ability: Default::default(),
|
||||
};
|
||||
|
||||
return outcome;
|
||||
}
|
||||
Some(true) => { /* fall through */ }
|
||||
}
|
||||
}
|
||||
|
||||
Content::RangedNumber(_, _) => {
|
||||
// these ranges always intersect, we need more information before we can say more
|
||||
}
|
||||
|
||||
_ => {
|
||||
// anything else is definitely a type error, and will be reported elsewhere
|
||||
}
|
||||
}
|
||||
|
||||
Outcome {
|
||||
mismatches: vec![Mismatch::TypeNotInRange],
|
||||
..Outcome::default()
|
||||
}
|
||||
Outcome::default()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -576,7 +577,7 @@ fn unify_alias(
|
||||
RangedNumber(other_real_var, other_range_vars) => {
|
||||
let outcome = unify_pool(subs, pool, real_var, *other_real_var, ctx.mode);
|
||||
if outcome.mismatches.is_empty() {
|
||||
check_valid_range(subs, pool, real_var, *other_range_vars, ctx.mode)
|
||||
check_valid_range(subs, real_var, *other_range_vars)
|
||||
} else {
|
||||
outcome
|
||||
}
|
||||
@ -637,7 +638,7 @@ fn unify_opaque(
|
||||
// This opaque might be a number, check if it unifies with the target ranged number var.
|
||||
let outcome = unify_pool(subs, pool, ctx.first, *other_real_var, ctx.mode);
|
||||
if outcome.mismatches.is_empty() {
|
||||
check_valid_range(subs, pool, ctx.first, *other_range_vars, ctx.mode)
|
||||
check_valid_range(subs, ctx.first, *other_range_vars)
|
||||
} else {
|
||||
outcome
|
||||
}
|
||||
@ -768,7 +769,7 @@ fn unify_structure(
|
||||
RangedNumber(other_real_var, other_range_vars) => {
|
||||
let outcome = unify_pool(subs, pool, ctx.first, *other_real_var, ctx.mode);
|
||||
if outcome.mismatches.is_empty() {
|
||||
check_valid_range(subs, pool, ctx.first, *other_range_vars, ctx.mode)
|
||||
check_valid_range(subs, ctx.first, *other_range_vars)
|
||||
} else {
|
||||
outcome
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user