mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 03:42:17 +00:00
Get deep range numbers working
This commit is contained in:
parent
5a18490050
commit
c154a337a9
17 changed files with 503 additions and 295 deletions
|
@ -10,47 +10,54 @@ use roc_types::types::Type::{self, *};
|
||||||
use roc_types::types::{AliasKind, Category};
|
use roc_types::types::{AliasKind, Category};
|
||||||
use roc_types::types::{OptAbleType, Reason};
|
use roc_types::types::{OptAbleType, Reason};
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn add_numeric_bound_constr(
|
pub fn add_numeric_bound_constr(
|
||||||
constraints: &mut Constraints,
|
constraints: &mut Constraints,
|
||||||
num_constraints: &mut impl Extend<Constraint>,
|
num_constraints: &mut impl Extend<Constraint>,
|
||||||
num_type: Type,
|
num_var: Variable,
|
||||||
|
precision_var: Variable,
|
||||||
bound: impl TypedNumericBound,
|
bound: impl TypedNumericBound,
|
||||||
region: Region,
|
region: Region,
|
||||||
category: Category,
|
category: Category,
|
||||||
) -> Type {
|
) -> Type {
|
||||||
let range = bound.numeric_bound();
|
let range = bound.numeric_bound();
|
||||||
let total_num_type = num_type;
|
|
||||||
|
|
||||||
use roc_types::num::{float_width_to_variable, int_lit_width_to_variable};
|
use roc_types::num::{float_width_to_variable, int_lit_width_to_variable};
|
||||||
|
|
||||||
match range {
|
match range {
|
||||||
NumericBound::None => {
|
NumericBound::None => {
|
||||||
// no additional constraints
|
// no additional constraints, just a Num *
|
||||||
total_num_type
|
num_num(Variable(num_var))
|
||||||
}
|
}
|
||||||
NumericBound::FloatExact(width) => {
|
NumericBound::FloatExact(width) => {
|
||||||
let actual_type = Variable(float_width_to_variable(width));
|
let actual_type = Variable(float_width_to_variable(width));
|
||||||
let expected = Expected::ForReason(Reason::NumericLiteralSuffix, actual_type, region);
|
let expected = Expected::ForReason(Reason::NumericLiteralSuffix, actual_type, region);
|
||||||
let because_suffix =
|
let because_suffix =
|
||||||
constraints.equal_types(total_num_type.clone(), expected, category, region);
|
constraints.equal_types(Variable(num_var), expected, category, region);
|
||||||
|
|
||||||
num_constraints.extend([because_suffix]);
|
num_constraints.extend([because_suffix]);
|
||||||
|
|
||||||
total_num_type
|
Variable(num_var)
|
||||||
}
|
}
|
||||||
NumericBound::IntExact(width) => {
|
NumericBound::IntExact(width) => {
|
||||||
let actual_type = Variable(int_lit_width_to_variable(width));
|
let actual_type = Variable(int_lit_width_to_variable(width));
|
||||||
let expected = Expected::ForReason(Reason::NumericLiteralSuffix, actual_type, region);
|
let expected = Expected::ForReason(Reason::NumericLiteralSuffix, actual_type, region);
|
||||||
let because_suffix =
|
let because_suffix =
|
||||||
constraints.equal_types(total_num_type.clone(), expected, category, region);
|
constraints.equal_types(Variable(num_var), expected, category, region);
|
||||||
|
|
||||||
num_constraints.extend([because_suffix]);
|
num_constraints.extend([because_suffix]);
|
||||||
|
|
||||||
total_num_type
|
Variable(num_var)
|
||||||
|
}
|
||||||
|
NumericBound::Range(range) => {
|
||||||
|
let actual_type = Variable(precision_var);
|
||||||
|
let expected = Expected::NoExpectation(RangedNumber(range));
|
||||||
|
let constr = constraints.equal_types(actual_type, expected, category, region);
|
||||||
|
|
||||||
|
num_constraints.extend([constr]);
|
||||||
|
|
||||||
|
num_num(Variable(num_var))
|
||||||
}
|
}
|
||||||
NumericBound::Range(range) => RangedNumber(Box::new(total_num_type), range),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +77,8 @@ pub fn int_literal(
|
||||||
let num_type = add_numeric_bound_constr(
|
let num_type = add_numeric_bound_constr(
|
||||||
constraints,
|
constraints,
|
||||||
&mut constrs,
|
&mut constrs,
|
||||||
Variable(num_var),
|
num_var,
|
||||||
|
precision_var,
|
||||||
bound,
|
bound,
|
||||||
region,
|
region,
|
||||||
Category::Num,
|
Category::Num,
|
||||||
|
@ -106,7 +114,8 @@ pub fn float_literal(
|
||||||
let num_type = add_numeric_bound_constr(
|
let num_type = add_numeric_bound_constr(
|
||||||
constraints,
|
constraints,
|
||||||
&mut constrs,
|
&mut constrs,
|
||||||
Variable(num_var),
|
num_var,
|
||||||
|
precision_var,
|
||||||
bound,
|
bound,
|
||||||
region,
|
region,
|
||||||
Category::Float,
|
Category::Float,
|
||||||
|
@ -134,13 +143,12 @@ pub fn num_literal(
|
||||||
region: Region,
|
region: Region,
|
||||||
bound: NumBound,
|
bound: NumBound,
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
let open_number_type = crate::builtins::num_num(Type::Variable(num_var));
|
|
||||||
|
|
||||||
let mut constrs = ArrayVec::<_, 2>::new();
|
let mut constrs = ArrayVec::<_, 2>::new();
|
||||||
let num_type = add_numeric_bound_constr(
|
let num_type = add_numeric_bound_constr(
|
||||||
constraints,
|
constraints,
|
||||||
&mut constrs,
|
&mut constrs,
|
||||||
open_number_type,
|
num_var,
|
||||||
|
num_var,
|
||||||
bound,
|
bound,
|
||||||
region,
|
region,
|
||||||
Category::Num,
|
Category::Num,
|
||||||
|
|
|
@ -226,15 +226,14 @@ pub fn constrain_pattern(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
&NumLiteral(var, _, _, bound) => {
|
&NumLiteral(precision_var, _, _, bound) => {
|
||||||
state.vars.push(var);
|
state.vars.push(precision_var);
|
||||||
|
|
||||||
let num_type = builtins::num_num(Type::Variable(var));
|
|
||||||
|
|
||||||
let num_type = builtins::add_numeric_bound_constr(
|
let num_type = builtins::add_numeric_bound_constr(
|
||||||
constraints,
|
constraints,
|
||||||
&mut state.constraints,
|
&mut state.constraints,
|
||||||
num_type,
|
precision_var,
|
||||||
|
precision_var,
|
||||||
bound,
|
bound,
|
||||||
region,
|
region,
|
||||||
Category::Num,
|
Category::Num,
|
||||||
|
@ -248,13 +247,14 @@ pub fn constrain_pattern(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
&IntLiteral(num_var, precision_var, _, _, bound) => {
|
&IntLiteral(num_precision_var, precision_var, _, _, bound) => {
|
||||||
// First constraint on the free num var; this improves the resolved type quality in
|
// First constraint on the free num var; this improves the resolved type quality in
|
||||||
// case the bound is an alias.
|
// case the bound is an alias.
|
||||||
let num_type = builtins::add_numeric_bound_constr(
|
let num_type = builtins::add_numeric_bound_constr(
|
||||||
constraints,
|
constraints,
|
||||||
&mut state.constraints,
|
&mut state.constraints,
|
||||||
Type::Variable(num_var),
|
num_precision_var,
|
||||||
|
num_precision_var,
|
||||||
bound,
|
bound,
|
||||||
region,
|
region,
|
||||||
Category::Int,
|
Category::Int,
|
||||||
|
@ -264,7 +264,7 @@ pub fn constrain_pattern(
|
||||||
let int_type = builtins::num_int(Type::Variable(precision_var));
|
let int_type = builtins::num_int(Type::Variable(precision_var));
|
||||||
|
|
||||||
state.constraints.push(constraints.equal_types(
|
state.constraints.push(constraints.equal_types(
|
||||||
num_type, // TODO check me if something breaks!
|
num_type.clone(), // TODO check me if something breaks!
|
||||||
Expected::NoExpectation(int_type),
|
Expected::NoExpectation(int_type),
|
||||||
Category::Int,
|
Category::Int,
|
||||||
region,
|
region,
|
||||||
|
@ -272,20 +272,21 @@ pub fn constrain_pattern(
|
||||||
|
|
||||||
// Also constrain the pattern against the num var, again to reuse aliases if they're present.
|
// Also constrain the pattern against the num var, again to reuse aliases if they're present.
|
||||||
state.constraints.push(constraints.equal_pattern_types(
|
state.constraints.push(constraints.equal_pattern_types(
|
||||||
Type::Variable(num_var),
|
num_type,
|
||||||
expected,
|
expected,
|
||||||
PatternCategory::Int,
|
PatternCategory::Int,
|
||||||
region,
|
region,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
&FloatLiteral(num_var, precision_var, _, _, bound) => {
|
&FloatLiteral(num_precision_var, precision_var, _, _, bound) => {
|
||||||
// First constraint on the free num var; this improves the resolved type quality in
|
// First constraint on the free num var; this improves the resolved type quality in
|
||||||
// case the bound is an alias.
|
// case the bound is an alias.
|
||||||
let num_type = builtins::add_numeric_bound_constr(
|
let num_type = builtins::add_numeric_bound_constr(
|
||||||
constraints,
|
constraints,
|
||||||
&mut state.constraints,
|
&mut state.constraints,
|
||||||
Type::Variable(num_var),
|
num_precision_var,
|
||||||
|
num_precision_var,
|
||||||
bound,
|
bound,
|
||||||
region,
|
region,
|
||||||
Category::Float,
|
Category::Float,
|
||||||
|
|
|
@ -151,7 +151,7 @@ impl FlatEncodable {
|
||||||
// by the backend, and the backend treats opaques like structural aliases.
|
// by the backend, and the backend treats opaques like structural aliases.
|
||||||
_ => Self::from_var(subs, real_var),
|
_ => Self::from_var(subs, real_var),
|
||||||
},
|
},
|
||||||
Content::RangedNumber(real_var, _) => Self::from_var(subs, real_var),
|
Content::RangedNumber(_) => Err(Underivable),
|
||||||
//
|
//
|
||||||
Content::RecursionVar { .. } => Err(Underivable),
|
Content::RecursionVar { .. } => Err(Underivable),
|
||||||
Content::Error => Err(Underivable),
|
Content::Error => Err(Underivable),
|
||||||
|
|
|
@ -663,10 +663,8 @@ fn deep_copy_type_vars<'a>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
RangedNumber(typ, range) => {
|
RangedNumber(range) => {
|
||||||
let new_typ = descend_var!(typ);
|
perform_clone!(RangedNumber(range))
|
||||||
|
|
||||||
perform_clone!(RangedNumber(new_typ, range))
|
|
||||||
}
|
}
|
||||||
Error => Error,
|
Error => Error,
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,7 +17,7 @@ use roc_debug_flags::{
|
||||||
ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION,
|
ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION,
|
||||||
};
|
};
|
||||||
use roc_derive_key::GlobalDerivedSymbols;
|
use roc_derive_key::GlobalDerivedSymbols;
|
||||||
use roc_error_macros::todo_abilities;
|
use roc_error_macros::{internal_error, todo_abilities};
|
||||||
use roc_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId};
|
use roc_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId};
|
||||||
use roc_late_solve::{resolve_ability_specialization, AbilitiesView, Resolved, UnificationFailed};
|
use roc_late_solve::{resolve_ability_specialization, AbilitiesView, Resolved, UnificationFailed};
|
||||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||||
|
@ -3742,41 +3742,15 @@ pub fn with_hole<'a>(
|
||||||
hole,
|
hole,
|
||||||
),
|
),
|
||||||
|
|
||||||
Num(var, num_str, num, _bound) => {
|
Num(_, num_str, num, _bound) => assign_num_literal(
|
||||||
// first figure out what kind of number this is
|
env,
|
||||||
match num_argument_to_int_or_float(env.subs, env.target_info, var, false) {
|
layout_cache,
|
||||||
IntOrFloat::Int(precision) => Stmt::Let(
|
assigned,
|
||||||
assigned,
|
variable,
|
||||||
Expr::Literal(match num {
|
&num_str,
|
||||||
IntValue::I128(n) => Literal::Int(n),
|
IntOrFloatValue::Int(num),
|
||||||
IntValue::U128(n) => Literal::U128(n),
|
hole,
|
||||||
}),
|
),
|
||||||
Layout::int_width(precision),
|
|
||||||
hole,
|
|
||||||
),
|
|
||||||
IntOrFloat::Float(precision) => Stmt::Let(
|
|
||||||
assigned,
|
|
||||||
Expr::Literal(match num {
|
|
||||||
IntValue::I128(n) => Literal::Float(i128::from_ne_bytes(n) as f64),
|
|
||||||
IntValue::U128(n) => Literal::Float(u128::from_ne_bytes(n) as f64),
|
|
||||||
}),
|
|
||||||
Layout::float_width(precision),
|
|
||||||
hole,
|
|
||||||
),
|
|
||||||
IntOrFloat::DecimalFloatType => {
|
|
||||||
let dec = match RocDec::from_str(&num_str) {
|
|
||||||
Some(d) => d,
|
|
||||||
None => panic!("Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message", num_str),
|
|
||||||
};
|
|
||||||
Stmt::Let(
|
|
||||||
assigned,
|
|
||||||
Expr::Literal(Literal::Decimal(dec.to_ne_bytes())),
|
|
||||||
Layout::Builtin(Builtin::Decimal),
|
|
||||||
hole,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LetNonRec(def, cont) => from_can_let(
|
LetNonRec(def, cont) => from_can_let(
|
||||||
env,
|
env,
|
||||||
procs,
|
procs,
|
||||||
|
@ -8969,6 +8943,56 @@ pub enum IntOrFloat {
|
||||||
DecimalFloatType,
|
DecimalFloatType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum IntOrFloatValue {
|
||||||
|
Int(IntValue),
|
||||||
|
Float(f64),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assign_num_literal<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
|
assigned: Symbol,
|
||||||
|
variable: Variable,
|
||||||
|
num_str: &str,
|
||||||
|
num_value: IntOrFloatValue,
|
||||||
|
hole: &'a Stmt<'a>,
|
||||||
|
) -> Stmt<'a> {
|
||||||
|
let layout = layout_cache
|
||||||
|
.from_var(env.arena, variable, &env.subs)
|
||||||
|
.unwrap();
|
||||||
|
let literal = match layout {
|
||||||
|
Layout::Builtin(Builtin::Int(_)) => match num_value {
|
||||||
|
IntOrFloatValue::Int(IntValue::I128(n)) => Literal::Int(n),
|
||||||
|
IntOrFloatValue::Int(IntValue::U128(n)) => Literal::U128(n),
|
||||||
|
IntOrFloatValue::Float(..) => {
|
||||||
|
internal_error!("Float value where int was expected, should have been a type error")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Layout::Builtin(Builtin::Float(_)) => match num_value {
|
||||||
|
IntOrFloatValue::Float(n) => Literal::Float(n),
|
||||||
|
IntOrFloatValue::Int(..) => {
|
||||||
|
internal_error!("Int value where float was expected, should have been a type error")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Layout::Builtin(Builtin::Decimal) => {
|
||||||
|
let dec = match RocDec::from_str(&num_str) {
|
||||||
|
Some(d) => d,
|
||||||
|
None => internal_error!(
|
||||||
|
"Invalid decimal for float literal = {}. This should be a type error!",
|
||||||
|
num_str
|
||||||
|
),
|
||||||
|
};
|
||||||
|
Literal::Decimal(dec.to_ne_bytes())
|
||||||
|
}
|
||||||
|
layout => internal_error!(
|
||||||
|
"Found a non-num layout where a number was expected: {:?}",
|
||||||
|
layout
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
Stmt::Let(assigned, Expr::Literal(literal), layout, hole)
|
||||||
|
}
|
||||||
|
|
||||||
/// Given the `a` in `Num a`, determines whether it's an int or a float
|
/// Given the `a` in `Num a`, determines whether it's an int or a float
|
||||||
pub fn num_argument_to_int_or_float(
|
pub fn num_argument_to_int_or_float(
|
||||||
subs: &Subs,
|
subs: &Subs,
|
||||||
|
|
|
@ -8,6 +8,7 @@ use roc_module::ident::{Lowercase, TagName};
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
use roc_problem::can::RuntimeError;
|
use roc_problem::can::RuntimeError;
|
||||||
use roc_target::{PtrWidth, TargetInfo};
|
use roc_target::{PtrWidth, TargetInfo};
|
||||||
|
use roc_types::num::NumericRange;
|
||||||
use roc_types::subs::{
|
use roc_types::subs::{
|
||||||
self, Content, FlatType, Label, RecordFields, Subs, UnionTags, UnsortedUnionLabels, Variable,
|
self, Content, FlatType, Label, RecordFields, Subs, UnionTags, UnsortedUnionLabels, Variable,
|
||||||
};
|
};
|
||||||
|
@ -81,7 +82,9 @@ impl<'a> RawFunctionLayout<'a> {
|
||||||
}
|
}
|
||||||
LambdaSet(lset) => Self::layout_from_lambda_set(env, lset),
|
LambdaSet(lset) => Self::layout_from_lambda_set(env, lset),
|
||||||
Structure(flat_type) => Self::layout_from_flat_type(env, flat_type),
|
Structure(flat_type) => Self::layout_from_flat_type(env, flat_type),
|
||||||
RangedNumber(typ, _) => Self::from_var(env, typ),
|
RangedNumber(..) => Ok(Self::ZeroArgumentThunk(Layout::new_help(
|
||||||
|
env, var, content,
|
||||||
|
)?)),
|
||||||
|
|
||||||
// Ints
|
// Ints
|
||||||
Alias(Symbol::NUM_I128, args, _, _) => {
|
Alias(Symbol::NUM_I128, args, _, _) => {
|
||||||
|
@ -1262,12 +1265,47 @@ impl<'a> Layout<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RangedNumber(typ, _) => Self::from_var(env, typ),
|
RangedNumber(range) => Self::layout_from_ranged_number(env, range),
|
||||||
|
|
||||||
Error => Err(LayoutProblem::Erroneous),
|
Error => Err(LayoutProblem::Erroneous),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn layout_from_ranged_number(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
range: NumericRange,
|
||||||
|
) -> Result<Self, LayoutProblem> {
|
||||||
|
use roc_types::num::IntLitWidth;
|
||||||
|
|
||||||
|
// If we chose the default int layout then the real var might have been `Num *`, or
|
||||||
|
// similar. In this case fix-up width if we need to. Choose I64 if the range says
|
||||||
|
// that the number will fit, otherwise choose the next-largest number layout.
|
||||||
|
//
|
||||||
|
// We don't pass the range down because `RangedNumber`s are somewhat rare, they only
|
||||||
|
// appear due to number literals, so no need to increase parameter list sizes.
|
||||||
|
let num_layout = match range {
|
||||||
|
NumericRange::IntAtLeastSigned(w) | NumericRange::NumAtLeastSigned(w) => {
|
||||||
|
[IntLitWidth::I64, IntLitWidth::I128]
|
||||||
|
.iter()
|
||||||
|
.find(|candidate| candidate.is_superset(&w, true))
|
||||||
|
.expect("if number doesn't fit, should have been a type error")
|
||||||
|
}
|
||||||
|
NumericRange::IntAtLeastEitherSign(w) | NumericRange::NumAtLeastEitherSign(w) => [
|
||||||
|
IntLitWidth::I64,
|
||||||
|
IntLitWidth::U64,
|
||||||
|
IntLitWidth::I128,
|
||||||
|
IntLitWidth::U128,
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.find(|candidate| candidate.is_superset(&w, false))
|
||||||
|
.expect("if number doesn't fit, should have been a type error"),
|
||||||
|
};
|
||||||
|
Ok(Layout::int_literal_width_to_int(
|
||||||
|
*num_layout,
|
||||||
|
env.target_info,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns Err(()) if given an error, or Ok(Layout) if given a non-erroneous Structure.
|
/// Returns Err(()) if given an error, or Ok(Layout) if given a non-erroneous Structure.
|
||||||
/// Panics if given a FlexVar or RigidVar, since those should have been
|
/// Panics if given a FlexVar or RigidVar, since those should have been
|
||||||
/// monomorphized away already!
|
/// monomorphized away already!
|
||||||
|
@ -1724,6 +1762,32 @@ impl<'a> Layout<'a> {
|
||||||
pub fn default_float() -> Layout<'a> {
|
pub fn default_float() -> Layout<'a> {
|
||||||
Layout::f64()
|
Layout::f64()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn int_literal_width_to_int(
|
||||||
|
width: roc_types::num::IntLitWidth,
|
||||||
|
target_info: TargetInfo,
|
||||||
|
) -> Layout<'a> {
|
||||||
|
use roc_types::num::IntLitWidth::*;
|
||||||
|
match width {
|
||||||
|
U8 => Layout::u8(),
|
||||||
|
U16 => Layout::u16(),
|
||||||
|
U32 => Layout::u32(),
|
||||||
|
U64 => Layout::u64(),
|
||||||
|
U128 => Layout::u128(),
|
||||||
|
I8 => Layout::i8(),
|
||||||
|
I16 => Layout::i16(),
|
||||||
|
I32 => Layout::i32(),
|
||||||
|
I64 => Layout::i64(),
|
||||||
|
I128 => Layout::i128(),
|
||||||
|
Nat => Layout::usize(target_info),
|
||||||
|
// f32 int literal bounded by +/- 2^24, so fit it into an i32
|
||||||
|
F32 => Layout::i32(),
|
||||||
|
// f64 int literal bounded by +/- 2^53, so fit it into an i32
|
||||||
|
F64 => Layout::i64(),
|
||||||
|
// dec int literal bounded by i128, so fit it into an i128
|
||||||
|
Dec => Layout::i128(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Builtin<'a> {
|
impl<'a> Builtin<'a> {
|
||||||
|
|
|
@ -144,7 +144,7 @@ impl FunctionLayout {
|
||||||
Content::LambdaSet(lset) => Self::from_lambda_set(layouts, subs, *lset),
|
Content::LambdaSet(lset) => Self::from_lambda_set(layouts, subs, *lset),
|
||||||
Content::Structure(flat_type) => Self::from_flat_type(layouts, subs, flat_type),
|
Content::Structure(flat_type) => Self::from_flat_type(layouts, subs, flat_type),
|
||||||
Content::Alias(_, _, actual, _) => Self::from_var_help(layouts, subs, *actual),
|
Content::Alias(_, _, actual, _) => Self::from_var_help(layouts, subs, *actual),
|
||||||
Content::RangedNumber(actual, _) => Self::from_var_help(layouts, subs, *actual),
|
Content::RangedNumber(_) => todo!(),
|
||||||
Content::Error => Err(TypeError(())),
|
Content::Error => Err(TypeError(())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,7 +263,7 @@ impl LambdaSet {
|
||||||
Content::LambdaSet(lset) => Self::from_lambda_set(layouts, subs, *lset),
|
Content::LambdaSet(lset) => Self::from_lambda_set(layouts, subs, *lset),
|
||||||
Content::Structure(_flat_type) => unreachable!(),
|
Content::Structure(_flat_type) => unreachable!(),
|
||||||
Content::Alias(_, _, actual, _) => Self::from_var_help(layouts, subs, *actual),
|
Content::Alias(_, _, actual, _) => Self::from_var_help(layouts, subs, *actual),
|
||||||
Content::RangedNumber(actual, _) => Self::from_var_help(layouts, subs, *actual),
|
Content::RangedNumber(_) => todo!(),
|
||||||
Content::Error => Err(TypeError(())),
|
Content::Error => Err(TypeError(())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -686,7 +686,7 @@ impl Layout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Content::RangedNumber(typ, _) => Self::from_var_help(layouts, subs, *typ),
|
Content::RangedNumber(_) => todo!(),
|
||||||
Content::Error => Err(TypeError(())),
|
Content::Error => Err(TypeError(())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1855,7 +1855,7 @@ fn compact_lambda_set<P: Phase>(
|
||||||
| FlexVar(..)
|
| FlexVar(..)
|
||||||
| RecursionVar { .. }
|
| RecursionVar { .. }
|
||||||
| LambdaSet(..)
|
| LambdaSet(..)
|
||||||
| RangedNumber(_, _) => {
|
| RangedNumber(_) => {
|
||||||
internal_error!("unexpected")
|
internal_error!("unexpected")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -2174,9 +2174,8 @@ fn type_to_variable<'a>(
|
||||||
Variable(_) | EmptyRec | EmptyTagUnion => {
|
Variable(_) | EmptyRec | EmptyTagUnion => {
|
||||||
unreachable!("This variant should never be deferred!")
|
unreachable!("This variant should never be deferred!")
|
||||||
}
|
}
|
||||||
RangedNumber(typ, range) => {
|
RangedNumber(range) => {
|
||||||
let ty_var = helper!(typ);
|
let content = Content::RangedNumber(*range);
|
||||||
let content = Content::RangedNumber(ty_var, *range);
|
|
||||||
|
|
||||||
register_with_known_var(subs, destination, rank, pools, content)
|
register_with_known_var(subs, destination, rank, pools, content)
|
||||||
}
|
}
|
||||||
|
@ -3266,7 +3265,7 @@ fn adjust_rank_content(
|
||||||
rank
|
rank
|
||||||
}
|
}
|
||||||
|
|
||||||
RangedNumber(typ, _) => adjust_rank(subs, young_mark, visit_mark, group_rank, *typ),
|
RangedNumber(_) => group_rank,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3556,8 +3555,8 @@ fn deep_copy_var_help(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
RangedNumber(typ, range) => {
|
RangedNumber(range) => {
|
||||||
let new_content = RangedNumber(work!(typ), range);
|
let new_content = RangedNumber(range);
|
||||||
|
|
||||||
subs.set_content_unchecked(copy, new_content);
|
subs.set_content_unchecked(copy, new_content);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1572,7 +1572,7 @@ mod solve_expr {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
Foo "happy" 2020
|
Foo "happy" 12
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[Foo Str (Num *)]*",
|
"[Foo Str (Num *)]*",
|
||||||
|
@ -2531,7 +2531,7 @@ mod solve_expr {
|
||||||
{ numIdentity, x : numIdentity 42, y }
|
{ numIdentity, x : numIdentity 42, y }
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"{ numIdentity : Num a -> Num a, x : Num b, y : Float * }",
|
"{ numIdentity : Num a -> Num a, x : Num *, y : Float * }",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3951,7 +3951,7 @@ mod solve_expr {
|
||||||
negatePoint { x: 1, y: 2.1, z: 0x3 }
|
negatePoint { x: 1, y: 2.1, z: 0x3 }
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"{ x : Num a, y : Float *, z : Int * }",
|
"{ x : Num *, y : Float *, z : Int * }",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3968,7 +3968,7 @@ mod solve_expr {
|
||||||
{ a, b }
|
{ a, b }
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"{ a : { x : Num a, y : Float *, z : c }, b : { blah : Str, x : Num b, y : Float *, z : d } }",
|
"{ a : { x : Num *, y : Float *, z : c }, b : { blah : Str, x : Num *, y : Float *, z : a } }",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1652,3 +1652,12 @@ fn lambda_set_niche_same_layout_different_constructor() {
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn choose_u64_layout() {
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
9999999999999999999 + 1
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::subs::Variable;
|
use crate::subs::{Content, GetSubsSlice, Subs, Variable};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
|
||||||
/// A bound placed on a number because of its literal value.
|
/// A bound placed on a number because of its literal value.
|
||||||
|
@ -11,36 +11,120 @@ pub enum NumericRange {
|
||||||
NumAtLeastEitherSign(IntLitWidth),
|
NumAtLeastEitherSign(IntLitWidth),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum MatchResult {
|
||||||
|
/// When the range < content, for example <U8, I8> < Int *
|
||||||
|
RangeInContent,
|
||||||
|
/// When the content < range, for example I8 < <U8, I8>
|
||||||
|
ContentInRange,
|
||||||
|
/// Ranges don't intersect
|
||||||
|
NoIntersection,
|
||||||
|
/// The content is not comparable
|
||||||
|
DifferentContent,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_content_in_range(result: bool) -> MatchResult {
|
||||||
|
if result {
|
||||||
|
MatchResult::ContentInRange
|
||||||
|
} else {
|
||||||
|
MatchResult::NoIntersection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl NumericRange {
|
impl NumericRange {
|
||||||
pub fn contains_symbol(&self, symbol: Symbol) -> Option<bool> {
|
pub fn match_content(&self, subs: &Subs, content: &Content) -> MatchResult {
|
||||||
let contains = match symbol {
|
use Content::*;
|
||||||
Symbol::NUM_I8 => self.contains_int_width(IntLitWidth::I8),
|
match content {
|
||||||
Symbol::NUM_U8 => self.contains_int_width(IntLitWidth::U8),
|
RangedNumber(other_range) => match self.intersection(other_range) {
|
||||||
Symbol::NUM_I16 => self.contains_int_width(IntLitWidth::I16),
|
Some(r) => {
|
||||||
Symbol::NUM_U16 => self.contains_int_width(IntLitWidth::U16),
|
if r == *other_range {
|
||||||
Symbol::NUM_I32 => self.contains_int_width(IntLitWidth::I32),
|
MatchResult::ContentInRange
|
||||||
Symbol::NUM_U32 => self.contains_int_width(IntLitWidth::U32),
|
} else {
|
||||||
Symbol::NUM_I64 => self.contains_int_width(IntLitWidth::I64),
|
MatchResult::RangeInContent
|
||||||
Symbol::NUM_NAT => self.contains_int_width(IntLitWidth::Nat),
|
}
|
||||||
Symbol::NUM_U64 => self.contains_int_width(IntLitWidth::U64),
|
}
|
||||||
Symbol::NUM_I128 => self.contains_int_width(IntLitWidth::I128),
|
None => MatchResult::NoIntersection,
|
||||||
Symbol::NUM_U128 => self.contains_int_width(IntLitWidth::U128),
|
},
|
||||||
|
Alias(symbol, args, real_var, _) => match *symbol {
|
||||||
|
Symbol::NUM_I8 | Symbol::NUM_SIGNED8 => {
|
||||||
|
from_content_in_range(self.contains_int_width(IntLitWidth::I8))
|
||||||
|
}
|
||||||
|
Symbol::NUM_U8 | Symbol::NUM_UNSIGNED8 => {
|
||||||
|
from_content_in_range(self.contains_int_width(IntLitWidth::U8))
|
||||||
|
}
|
||||||
|
Symbol::NUM_I16 | Symbol::NUM_SIGNED16 => {
|
||||||
|
from_content_in_range(self.contains_int_width(IntLitWidth::I16))
|
||||||
|
}
|
||||||
|
Symbol::NUM_U16 | Symbol::NUM_UNSIGNED16 => {
|
||||||
|
from_content_in_range(self.contains_int_width(IntLitWidth::U16))
|
||||||
|
}
|
||||||
|
Symbol::NUM_I32 | Symbol::NUM_SIGNED32 => {
|
||||||
|
from_content_in_range(self.contains_int_width(IntLitWidth::I32))
|
||||||
|
}
|
||||||
|
Symbol::NUM_U32 | Symbol::NUM_UNSIGNED32 => {
|
||||||
|
from_content_in_range(self.contains_int_width(IntLitWidth::U32))
|
||||||
|
}
|
||||||
|
Symbol::NUM_I64 | Symbol::NUM_SIGNED64 => {
|
||||||
|
from_content_in_range(self.contains_int_width(IntLitWidth::I64))
|
||||||
|
}
|
||||||
|
Symbol::NUM_NAT | Symbol::NUM_NATURAL => {
|
||||||
|
from_content_in_range(self.contains_int_width(IntLitWidth::Nat))
|
||||||
|
}
|
||||||
|
Symbol::NUM_U64 | Symbol::NUM_UNSIGNED64 => {
|
||||||
|
from_content_in_range(self.contains_int_width(IntLitWidth::U64))
|
||||||
|
}
|
||||||
|
Symbol::NUM_I128 | Symbol::NUM_SIGNED128 => {
|
||||||
|
from_content_in_range(self.contains_int_width(IntLitWidth::I128))
|
||||||
|
}
|
||||||
|
Symbol::NUM_U128 | Symbol::NUM_UNSIGNED128 => {
|
||||||
|
from_content_in_range(self.contains_int_width(IntLitWidth::U128))
|
||||||
|
}
|
||||||
|
|
||||||
Symbol::NUM_DEC => self.contains_float_width(FloatWidth::Dec),
|
Symbol::NUM_DEC => {
|
||||||
Symbol::NUM_F32 => self.contains_float_width(FloatWidth::F32),
|
from_content_in_range(self.contains_float_width(FloatWidth::Dec))
|
||||||
Symbol::NUM_F64 => self.contains_float_width(FloatWidth::F64),
|
}
|
||||||
|
Symbol::NUM_F32 => {
|
||||||
|
from_content_in_range(self.contains_float_width(FloatWidth::F32))
|
||||||
|
}
|
||||||
|
Symbol::NUM_F64 => {
|
||||||
|
from_content_in_range(self.contains_float_width(FloatWidth::F64))
|
||||||
|
}
|
||||||
|
Symbol::NUM_FRAC | Symbol::NUM_FLOATINGPOINT => {
|
||||||
|
match self {
|
||||||
|
NumericRange::IntAtLeastSigned(_)
|
||||||
|
| NumericRange::IntAtLeastEitherSign(_) => MatchResult::DifferentContent,
|
||||||
|
NumericRange::NumAtLeastSigned(_)
|
||||||
|
| NumericRange::NumAtLeastEitherSign(_) => MatchResult::ContentInRange,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Symbol::NUM_NUM => {
|
||||||
|
debug_assert_eq!(args.len(), 1);
|
||||||
|
match subs.get_content_without_compacting(
|
||||||
|
subs.get_subs_slice(args.all_variables())[0],
|
||||||
|
) {
|
||||||
|
FlexVar(_) | RigidVar(_) => MatchResult::RangeInContent,
|
||||||
|
_ => {
|
||||||
|
self.match_content(subs, subs.get_content_without_compacting(*real_var))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Symbol::NUM_INT | Symbol::NUM_INTEGER => {
|
||||||
|
debug_assert_eq!(args.len(), 1);
|
||||||
|
match subs.get_content_without_compacting(
|
||||||
|
subs.get_subs_slice(args.all_variables())[0],
|
||||||
|
) {
|
||||||
|
FlexVar(_) | RigidVar(_) => MatchResult::RangeInContent,
|
||||||
|
_ => {
|
||||||
|
self.match_content(subs, subs.get_content_without_compacting(*real_var))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Symbol::NUM_NUM | Symbol::NUM_INT | Symbol::NUM_FRAC => {
|
_ => MatchResult::DifferentContent,
|
||||||
// these satisfy any range that they are given
|
},
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {
|
_ => MatchResult::DifferentContent,
|
||||||
return None;
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(contains)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contains_float_width(&self, _width: FloatWidth) -> bool {
|
fn contains_float_width(&self, _width: FloatWidth) -> bool {
|
||||||
|
@ -83,26 +167,31 @@ impl NumericRange {
|
||||||
pub fn intersection(&self, other: &Self) -> Option<Self> {
|
pub fn intersection(&self, other: &Self) -> Option<Self> {
|
||||||
use NumericRange::*;
|
use NumericRange::*;
|
||||||
let (left, right) = (self.width(), other.width());
|
let (left, right) = (self.width(), other.width());
|
||||||
let constructor: fn(IntLitWidth) -> NumericRange = match (self, other) {
|
let (constructor, is_negative): (fn(IntLitWidth) -> NumericRange, _) = match (self, other) {
|
||||||
// Matching against a signed int, the intersection must also be a signed int
|
// Matching against a signed int, the intersection must also be a signed int
|
||||||
(IntAtLeastSigned(_), _) | (_, IntAtLeastSigned(_)) => IntAtLeastSigned,
|
(IntAtLeastSigned(_), _) | (_, IntAtLeastSigned(_)) => (IntAtLeastSigned, true),
|
||||||
// It's a signed number, but also an int, so the intersection must be a signed int
|
// It's a signed number, but also an int, so the intersection must be a signed int
|
||||||
(NumAtLeastSigned(_), IntAtLeastEitherSign(_))
|
(NumAtLeastSigned(_), IntAtLeastEitherSign(_))
|
||||||
| (IntAtLeastEitherSign(_), NumAtLeastSigned(_)) => IntAtLeastSigned,
|
| (IntAtLeastEitherSign(_), NumAtLeastSigned(_)) => (IntAtLeastSigned, true),
|
||||||
// It's a signed number
|
// It's a signed number
|
||||||
(NumAtLeastSigned(_), NumAtLeastSigned(_) | NumAtLeastEitherSign(_))
|
(NumAtLeastSigned(_), NumAtLeastSigned(_) | NumAtLeastEitherSign(_))
|
||||||
| (NumAtLeastEitherSign(_), NumAtLeastSigned(_)) => NumAtLeastSigned,
|
| (NumAtLeastEitherSign(_), NumAtLeastSigned(_)) => (NumAtLeastSigned, true),
|
||||||
// Otherwise we must be an int, signed or unsigned
|
// Otherwise we must be an int, signed or unsigned
|
||||||
(IntAtLeastEitherSign(_), IntAtLeastEitherSign(_) | NumAtLeastEitherSign(_))
|
(IntAtLeastEitherSign(_), IntAtLeastEitherSign(_) | NumAtLeastEitherSign(_))
|
||||||
| (NumAtLeastEitherSign(_), IntAtLeastEitherSign(_)) => IntAtLeastEitherSign,
|
| (NumAtLeastEitherSign(_), IntAtLeastEitherSign(_)) => (IntAtLeastEitherSign, false),
|
||||||
// Otherwise we must be a num, signed or unsigned
|
// Otherwise we must be a num, signed or unsigned
|
||||||
(NumAtLeastEitherSign(_), NumAtLeastEitherSign(_)) => NumAtLeastEitherSign,
|
(NumAtLeastEitherSign(_), NumAtLeastEitherSign(_)) => (NumAtLeastEitherSign, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
// One is a superset of the other if it's a superset on both sides
|
// If the intersection must be signed but one of the lower bounds isn't signed, then there
|
||||||
if left.is_superset(&right, true) && left.is_superset(&right, false) {
|
// is no intersection.
|
||||||
|
if is_negative && (!left.is_signed() || !right.is_signed()) {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
// Otherwise, find the greatest lower bound depending on the signed-ness.
|
||||||
|
else if left.is_superset(&right, is_negative) {
|
||||||
Some(constructor(left))
|
Some(constructor(left))
|
||||||
} else if right.is_superset(&left, true) && right.is_superset(&left, false) {
|
} else if right.is_superset(&left, is_negative) {
|
||||||
Some(constructor(right))
|
Some(constructor(right))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -115,29 +204,36 @@ impl NumericRange {
|
||||||
match self {
|
match self {
|
||||||
IntAtLeastSigned(width) => {
|
IntAtLeastSigned(width) => {
|
||||||
let target = int_lit_width_to_variable(*width);
|
let target = int_lit_width_to_variable(*width);
|
||||||
let start = SIGNED_VARIABLES.iter().position(|v| *v == target).unwrap();
|
let start = SIGNED_INT_VARIABLES
|
||||||
let end = SIGNED_VARIABLES.len() - 3;
|
.iter()
|
||||||
|
.position(|v| *v == target)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
&SIGNED_VARIABLES[start..end]
|
&SIGNED_INT_VARIABLES[start..]
|
||||||
}
|
}
|
||||||
IntAtLeastEitherSign(width) => {
|
IntAtLeastEitherSign(width) => {
|
||||||
let target = int_lit_width_to_variable(*width);
|
let target = int_lit_width_to_variable(*width);
|
||||||
let start = ALL_VARIABLES.iter().position(|v| *v == target).unwrap();
|
let start = ALL_INT_VARIABLES.iter().position(|v| *v == target).unwrap();
|
||||||
let end = ALL_VARIABLES.len() - 3;
|
|
||||||
|
|
||||||
&ALL_VARIABLES[start..end]
|
&ALL_INT_VARIABLES[start..]
|
||||||
}
|
}
|
||||||
NumAtLeastSigned(width) => {
|
NumAtLeastSigned(width) => {
|
||||||
let target = int_lit_width_to_variable(*width);
|
let target = int_lit_width_to_variable(*width);
|
||||||
let start = SIGNED_VARIABLES.iter().position(|v| *v == target).unwrap();
|
let start = SIGNED_INT_OR_FLOAT_VARIABLES
|
||||||
|
.iter()
|
||||||
|
.position(|v| *v == target)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
&SIGNED_VARIABLES[start..]
|
&SIGNED_INT_OR_FLOAT_VARIABLES[start..]
|
||||||
}
|
}
|
||||||
NumAtLeastEitherSign(width) => {
|
NumAtLeastEitherSign(width) => {
|
||||||
let target = int_lit_width_to_variable(*width);
|
let target = int_lit_width_to_variable(*width);
|
||||||
let start = ALL_VARIABLES.iter().position(|v| *v == target).unwrap();
|
let start = ALL_INT_OR_FLOAT_VARIABLES
|
||||||
|
.iter()
|
||||||
|
.position(|v| *v == target)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
&ALL_VARIABLES[start..]
|
&ALL_INT_OR_FLOAT_VARIABLES[start..]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,6 +292,10 @@ impl IntLitWidth {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_signed(&self) -> bool {
|
||||||
|
return self.signedness_and_width().0 == IntSignedness::Signed;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn type_str(&self) -> &'static str {
|
pub fn type_str(&self) -> &'static str {
|
||||||
use IntLitWidth::*;
|
use IntLitWidth::*;
|
||||||
match self {
|
match self {
|
||||||
|
@ -371,7 +471,7 @@ pub const fn float_width_to_variable(w: FloatWidth) -> Variable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ALL_VARIABLES: &[Variable] = &[
|
const ALL_INT_OR_FLOAT_VARIABLES: &[Variable] = &[
|
||||||
Variable::I8,
|
Variable::I8,
|
||||||
Variable::U8,
|
Variable::U8,
|
||||||
Variable::I16,
|
Variable::I16,
|
||||||
|
@ -388,7 +488,7 @@ const ALL_VARIABLES: &[Variable] = &[
|
||||||
Variable::U128,
|
Variable::U128,
|
||||||
];
|
];
|
||||||
|
|
||||||
const SIGNED_VARIABLES: &[Variable] = &[
|
const SIGNED_INT_OR_FLOAT_VARIABLES: &[Variable] = &[
|
||||||
Variable::I8,
|
Variable::I8,
|
||||||
Variable::I16,
|
Variable::I16,
|
||||||
Variable::F32,
|
Variable::F32,
|
||||||
|
@ -398,3 +498,25 @@ const SIGNED_VARIABLES: &[Variable] = &[
|
||||||
Variable::I128,
|
Variable::I128,
|
||||||
Variable::DEC,
|
Variable::DEC,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const ALL_INT_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 platform
|
||||||
|
Variable::U64,
|
||||||
|
Variable::I128,
|
||||||
|
Variable::U128,
|
||||||
|
];
|
||||||
|
|
||||||
|
const SIGNED_INT_VARIABLES: &[Variable] = &[
|
||||||
|
Variable::I8,
|
||||||
|
Variable::I16,
|
||||||
|
Variable::I32,
|
||||||
|
Variable::I64,
|
||||||
|
Variable::I128,
|
||||||
|
];
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::num::IntLitWidth;
|
||||||
use crate::subs::{
|
use crate::subs::{
|
||||||
self, AliasVariables, Content, FlatType, GetSubsSlice, Label, Subs, SubsIndex, UnionLabels,
|
self, AliasVariables, Content, FlatType, GetSubsSlice, Label, Subs, SubsIndex, UnionLabels,
|
||||||
UnionTags, UnsortedUnionLabels, Variable,
|
UnionTags, UnsortedUnionLabels, Variable,
|
||||||
|
@ -381,16 +382,21 @@ fn find_names_needed(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&RangedNumber(typ, _) => {
|
RangedNumber(range) => match range {
|
||||||
find_names_needed(
|
crate::num::NumericRange::NumAtLeastEitherSign(IntLitWidth::I8)
|
||||||
typ,
|
| crate::num::NumericRange::IntAtLeastEitherSign(IntLitWidth::I8) => {
|
||||||
subs,
|
subs.set_content(variable, FlexVar(None));
|
||||||
roots,
|
find_names_needed(
|
||||||
root_appearances,
|
variable,
|
||||||
names_taken,
|
subs,
|
||||||
find_under_alias,
|
roots,
|
||||||
);
|
root_appearances,
|
||||||
}
|
names_taken,
|
||||||
|
find_under_alias,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
Error | Structure(Erroneous(_)) | Structure(EmptyRecord) | Structure(EmptyTagUnion) => {
|
Error | Structure(Erroneous(_)) | Structure(EmptyRecord) | Structure(EmptyTagUnion) => {
|
||||||
// Errors and empty records don't need names.
|
// Errors and empty records don't need names.
|
||||||
}
|
}
|
||||||
|
@ -783,14 +789,23 @@ fn write_content<'a>(
|
||||||
|
|
||||||
buf.push(']');
|
buf.push(']');
|
||||||
}
|
}
|
||||||
RangedNumber(typ, _range_vars) => write_content(
|
RangedNumber(range) => {
|
||||||
env,
|
buf.push_str("Range(");
|
||||||
ctx,
|
for (i, &var) in range.variable_slice().iter().enumerate() {
|
||||||
subs.get_content_without_compacting(*typ),
|
if i > 0 {
|
||||||
subs,
|
buf.push_str(", ");
|
||||||
buf,
|
}
|
||||||
parens,
|
write_content(
|
||||||
),
|
env,
|
||||||
|
ctx,
|
||||||
|
subs.get_content_without_compacting(var),
|
||||||
|
subs,
|
||||||
|
buf,
|
||||||
|
Parens::Unnecessary,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
buf.push_str(")");
|
||||||
|
}
|
||||||
Error => buf.push_str("<type mismatch>"),
|
Error => buf.push_str("<type mismatch>"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -841,8 +841,8 @@ fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt:
|
||||||
}
|
}
|
||||||
write!(f, ")")
|
write!(f, ")")
|
||||||
}
|
}
|
||||||
Content::RangedNumber(typ, range) => {
|
Content::RangedNumber(range) => {
|
||||||
write!(f, "RangedNumber({:?}, {:?})", typ, range)
|
write!(f, "RangedNumber( {:?})", range)
|
||||||
}
|
}
|
||||||
Content::Error => write!(f, "Error"),
|
Content::Error => write!(f, "Error"),
|
||||||
}
|
}
|
||||||
|
@ -2202,7 +2202,7 @@ pub enum Content {
|
||||||
LambdaSet(LambdaSet),
|
LambdaSet(LambdaSet),
|
||||||
Structure(FlatType),
|
Structure(FlatType),
|
||||||
Alias(Symbol, AliasVariables, Variable, AliasKind),
|
Alias(Symbol, AliasVariables, Variable, AliasKind),
|
||||||
RangedNumber(Variable, crate::num::NumericRange),
|
RangedNumber(crate::num::NumericRange),
|
||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3150,15 +3150,7 @@ fn occurs(
|
||||||
|
|
||||||
occurs_union(subs, root_var, &new_seen, include_recursion_var, solved)
|
occurs_union(subs, root_var, &new_seen, include_recursion_var, solved)
|
||||||
}
|
}
|
||||||
RangedNumber(typ, _range_vars) => {
|
RangedNumber(_range_vars) => Ok(()),
|
||||||
let mut new_seen = seen.to_owned();
|
|
||||||
new_seen.push(root_var);
|
|
||||||
|
|
||||||
short_circuit_help(subs, root_var, &new_seen, *typ, include_recursion_var)?;
|
|
||||||
// _range_vars excluded because they are not explicitly part of the type.
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3345,10 +3337,8 @@ fn explicit_substitute(
|
||||||
|
|
||||||
in_var
|
in_var
|
||||||
}
|
}
|
||||||
RangedNumber(typ, range) => {
|
RangedNumber(range) => {
|
||||||
let new_typ = explicit_substitute(subs, from, to, typ, seen);
|
subs.set_content(in_var, RangedNumber(range));
|
||||||
|
|
||||||
subs.set_content(in_var, RangedNumber(new_typ, range));
|
|
||||||
|
|
||||||
in_var
|
in_var
|
||||||
}
|
}
|
||||||
|
@ -3462,7 +3452,7 @@ fn get_var_names(
|
||||||
taken_names
|
taken_names
|
||||||
}
|
}
|
||||||
|
|
||||||
RangedNumber(typ, _) => get_var_names(subs, typ, taken_names),
|
RangedNumber(_) => taken_names,
|
||||||
|
|
||||||
Structure(flat_type) => match flat_type {
|
Structure(flat_type) => match flat_type {
|
||||||
FlatType::Apply(_, args) => {
|
FlatType::Apply(_, args) => {
|
||||||
|
@ -3685,6 +3675,14 @@ fn content_to_err_type(
|
||||||
Alias(symbol, args, aliased_to, kind) => {
|
Alias(symbol, args, aliased_to, kind) => {
|
||||||
let err_type = var_to_err_type(subs, state, aliased_to);
|
let err_type = var_to_err_type(subs, state, aliased_to);
|
||||||
|
|
||||||
|
// Lift RangedNumber up if needed.
|
||||||
|
match (symbol, &err_type) {
|
||||||
|
(Symbol::NUM_INT | Symbol::NUM_NUM | Symbol::NUM_INTEGER, ErrorType::Range(_)) => {
|
||||||
|
return err_type;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
let mut err_args = Vec::with_capacity(args.len());
|
let mut err_args = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
for var_index in args.into_iter() {
|
for var_index in args.into_iter() {
|
||||||
|
@ -3703,17 +3701,18 @@ fn content_to_err_type(
|
||||||
ErrorType::Error
|
ErrorType::Error
|
||||||
}
|
}
|
||||||
|
|
||||||
RangedNumber(typ, range) => {
|
RangedNumber(range) => {
|
||||||
let err_type = var_to_err_type(subs, state, typ);
|
|
||||||
|
|
||||||
if state.context == ErrorTypeContext::ExpandRanges {
|
if state.context == ErrorTypeContext::ExpandRanges {
|
||||||
let mut types = Vec::new();
|
let mut types = Vec::new();
|
||||||
for var in range.variable_slice() {
|
for var in range.variable_slice() {
|
||||||
types.push(var_to_err_type(subs, state, *var));
|
types.push(var_to_err_type(subs, state, *var));
|
||||||
}
|
}
|
||||||
ErrorType::Range(Box::new(err_type), types)
|
ErrorType::Range(types)
|
||||||
} else {
|
} else {
|
||||||
err_type
|
let content = FlexVar(None);
|
||||||
|
subs.set_content(var, content);
|
||||||
|
subs.set_mark(var, Mark::NONE);
|
||||||
|
var_to_err_type(subs, state, var)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4160,7 +4159,7 @@ impl StorageSubs {
|
||||||
recursion_var: recursion_var.map(|v| Self::offset_variable(offsets, v)),
|
recursion_var: recursion_var.map(|v| Self::offset_variable(offsets, v)),
|
||||||
unspecialized: Self::offset_uls_slice(offsets, *unspecialized),
|
unspecialized: Self::offset_uls_slice(offsets, *unspecialized),
|
||||||
}),
|
}),
|
||||||
RangedNumber(typ, range) => RangedNumber(Self::offset_variable(offsets, *typ), *range),
|
RangedNumber(range) => RangedNumber(*range),
|
||||||
Error => Content::Error,
|
Error => Content::Error,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4601,10 +4600,8 @@ fn storage_copy_var_to_help(env: &mut StorageCopyVarToEnv<'_>, var: Variable) ->
|
||||||
copy
|
copy
|
||||||
}
|
}
|
||||||
|
|
||||||
RangedNumber(typ, range) => {
|
RangedNumber(range) => {
|
||||||
let new_typ = storage_copy_var_to_help(env, typ);
|
let new_content = RangedNumber(range);
|
||||||
|
|
||||||
let new_content = RangedNumber(new_typ, range);
|
|
||||||
|
|
||||||
env.target.set(copy, make_descriptor(new_content));
|
env.target.set(copy, make_descriptor(new_content));
|
||||||
copy
|
copy
|
||||||
|
@ -4729,7 +4726,7 @@ fn is_registered(content: &Content) -> bool {
|
||||||
Content::Structure(_)
|
Content::Structure(_)
|
||||||
| Content::RecursionVar { .. }
|
| Content::RecursionVar { .. }
|
||||||
| Content::Alias(_, _, _, _)
|
| Content::Alias(_, _, _, _)
|
||||||
| Content::RangedNumber(_, _)
|
| Content::RangedNumber(_)
|
||||||
| Content::Error
|
| Content::Error
|
||||||
| Content::LambdaSet(_) => true,
|
| Content::LambdaSet(_) => true,
|
||||||
}
|
}
|
||||||
|
@ -5067,10 +5064,8 @@ fn copy_import_to_help(env: &mut CopyImportEnv<'_>, max_rank: Rank, var: Variabl
|
||||||
copy
|
copy
|
||||||
}
|
}
|
||||||
|
|
||||||
RangedNumber(typ, range) => {
|
RangedNumber(range) => {
|
||||||
let new_typ = copy_import_to_help(env, max_rank, typ);
|
let new_content = RangedNumber(range);
|
||||||
|
|
||||||
let new_content = RangedNumber(new_typ, range);
|
|
||||||
|
|
||||||
env.target.set(copy, make_descriptor(new_content));
|
env.target.set(copy, make_descriptor(new_content));
|
||||||
copy
|
copy
|
||||||
|
@ -5232,9 +5227,7 @@ fn instantiate_rigids_help(subs: &mut Subs, max_rank: Rank, initial: Variable) {
|
||||||
stack.push(*var);
|
stack.push(*var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&RangedNumber(typ, _) => {
|
&RangedNumber(_) => {}
|
||||||
stack.push(typ);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -261,7 +261,7 @@ pub enum Type {
|
||||||
/// Applying a type to some arguments (e.g. Dict.Dict String Int)
|
/// Applying a type to some arguments (e.g. Dict.Dict String Int)
|
||||||
Apply(Symbol, Vec<Type>, Region),
|
Apply(Symbol, Vec<Type>, Region),
|
||||||
Variable(Variable),
|
Variable(Variable),
|
||||||
RangedNumber(Box<Type>, NumericRange),
|
RangedNumber(NumericRange),
|
||||||
/// A type error, which will code gen to a runtime error
|
/// A type error, which will code gen to a runtime error
|
||||||
Erroneous(Problem),
|
Erroneous(Problem),
|
||||||
}
|
}
|
||||||
|
@ -349,7 +349,7 @@ impl Clone for Type {
|
||||||
}
|
}
|
||||||
Self::Apply(arg0, arg1, arg2) => Self::Apply(*arg0, arg1.clone(), *arg2),
|
Self::Apply(arg0, arg1, arg2) => Self::Apply(*arg0, arg1.clone(), *arg2),
|
||||||
Self::Variable(arg0) => Self::Variable(*arg0),
|
Self::Variable(arg0) => Self::Variable(*arg0),
|
||||||
Self::RangedNumber(arg0, arg1) => Self::RangedNumber(arg0.clone(), *arg1),
|
Self::RangedNumber(arg1) => Self::RangedNumber(*arg1),
|
||||||
Self::Erroneous(arg0) => Self::Erroneous(arg0.clone()),
|
Self::Erroneous(arg0) => Self::Erroneous(arg0.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -652,8 +652,8 @@ impl fmt::Debug for Type {
|
||||||
|
|
||||||
write!(f, " as <{:?}>", rec)
|
write!(f, " as <{:?}>", rec)
|
||||||
}
|
}
|
||||||
Type::RangedNumber(typ, range_vars) => {
|
Type::RangedNumber(range_vars) => {
|
||||||
write!(f, "Ranged({:?}, {:?})", typ, range_vars)
|
write!(f, "Ranged({:?})", range_vars)
|
||||||
}
|
}
|
||||||
Type::UnspecializedLambdaSet(uls) => {
|
Type::UnspecializedLambdaSet(uls) => {
|
||||||
write!(f, "{:?}", uls)
|
write!(f, "{:?}", uls)
|
||||||
|
@ -794,9 +794,7 @@ impl Type {
|
||||||
Apply(_, args, _) => {
|
Apply(_, args, _) => {
|
||||||
stack.extend(args);
|
stack.extend(args);
|
||||||
}
|
}
|
||||||
RangedNumber(typ, _) => {
|
RangedNumber(_) => {}
|
||||||
stack.push(typ);
|
|
||||||
}
|
|
||||||
UnspecializedLambdaSet(Uls(v, _, _)) => {
|
UnspecializedLambdaSet(Uls(v, _, _)) => {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
substitutions.get(v).is_none(),
|
substitutions.get(v).is_none(),
|
||||||
|
@ -911,9 +909,7 @@ impl Type {
|
||||||
Apply(_, args, _) => {
|
Apply(_, args, _) => {
|
||||||
stack.extend(args);
|
stack.extend(args);
|
||||||
}
|
}
|
||||||
RangedNumber(typ, _) => {
|
RangedNumber(_) => {}
|
||||||
stack.push(typ);
|
|
||||||
}
|
|
||||||
UnspecializedLambdaSet(Uls(v, _, _)) => {
|
UnspecializedLambdaSet(Uls(v, _, _)) => {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
substitutions.get(v).is_none(),
|
substitutions.get(v).is_none(),
|
||||||
|
@ -1016,7 +1012,7 @@ impl Type {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
RangedNumber(typ, _) => typ.substitute_alias(rep_symbol, rep_args, actual),
|
RangedNumber(_) => Ok(()),
|
||||||
UnspecializedLambdaSet(..) => Ok(()),
|
UnspecializedLambdaSet(..) => Ok(()),
|
||||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => Ok(()),
|
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => Ok(()),
|
||||||
}
|
}
|
||||||
|
@ -1073,7 +1069,7 @@ impl Type {
|
||||||
}
|
}
|
||||||
Apply(symbol, _, _) if *symbol == rep_symbol => true,
|
Apply(symbol, _, _) if *symbol == rep_symbol => true,
|
||||||
Apply(_, args, _) => args.iter().any(|arg| arg.contains_symbol(rep_symbol)),
|
Apply(_, args, _) => args.iter().any(|arg| arg.contains_symbol(rep_symbol)),
|
||||||
RangedNumber(typ, _) => typ.contains_symbol(rep_symbol),
|
RangedNumber(_) => false,
|
||||||
UnspecializedLambdaSet(Uls(_, sym, _)) => *sym == rep_symbol,
|
UnspecializedLambdaSet(Uls(_, sym, _)) => *sym == rep_symbol,
|
||||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => false,
|
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => false,
|
||||||
}
|
}
|
||||||
|
@ -1124,7 +1120,7 @@ impl Type {
|
||||||
} => actual_type.contains_variable(rep_variable),
|
} => actual_type.contains_variable(rep_variable),
|
||||||
HostExposedAlias { actual, .. } => actual.contains_variable(rep_variable),
|
HostExposedAlias { actual, .. } => actual.contains_variable(rep_variable),
|
||||||
Apply(_, args, _) => args.iter().any(|arg| arg.contains_variable(rep_variable)),
|
Apply(_, args, _) => args.iter().any(|arg| arg.contains_variable(rep_variable)),
|
||||||
RangedNumber(typ, _) => typ.contains_variable(rep_variable),
|
RangedNumber(_) => false,
|
||||||
EmptyRec | EmptyTagUnion | Erroneous(_) => false,
|
EmptyRec | EmptyTagUnion | Erroneous(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1387,9 +1383,7 @@ impl Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RangedNumber(typ, _) => {
|
RangedNumber(_) => {}
|
||||||
typ.instantiate_aliases(region, aliases, var_store, new_lambda_set_variables);
|
|
||||||
}
|
|
||||||
UnspecializedLambdaSet(..) => {}
|
UnspecializedLambdaSet(..) => {}
|
||||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {}
|
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {}
|
||||||
}
|
}
|
||||||
|
@ -1525,9 +1519,7 @@ fn symbols_help(initial: &Type) -> Vec<Symbol> {
|
||||||
Erroneous(Problem::CyclicAlias(alias, _, _)) => {
|
Erroneous(Problem::CyclicAlias(alias, _, _)) => {
|
||||||
output.push(*alias);
|
output.push(*alias);
|
||||||
}
|
}
|
||||||
RangedNumber(typ, _) => {
|
RangedNumber(_) => {}
|
||||||
stack.push(typ);
|
|
||||||
}
|
|
||||||
UnspecializedLambdaSet(Uls(_, _sym, _)) => {
|
UnspecializedLambdaSet(Uls(_, _sym, _)) => {
|
||||||
// ignore the member symbol because unspecialized lambda sets are internal-only
|
// ignore the member symbol because unspecialized lambda sets are internal-only
|
||||||
}
|
}
|
||||||
|
@ -1647,9 +1639,7 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
||||||
}
|
}
|
||||||
variables_help(actual, accum);
|
variables_help(actual, accum);
|
||||||
}
|
}
|
||||||
RangedNumber(typ, _) => {
|
RangedNumber(_) => {}
|
||||||
variables_help(typ, accum);
|
|
||||||
}
|
|
||||||
Apply(_, args, _) => {
|
Apply(_, args, _) => {
|
||||||
for x in args {
|
for x in args {
|
||||||
variables_help(x, accum);
|
variables_help(x, accum);
|
||||||
|
@ -1790,9 +1780,7 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
|
||||||
}
|
}
|
||||||
variables_help_detailed(actual, accum);
|
variables_help_detailed(actual, accum);
|
||||||
}
|
}
|
||||||
RangedNumber(typ, _) => {
|
RangedNumber(_) => {}
|
||||||
variables_help_detailed(typ, accum);
|
|
||||||
}
|
|
||||||
Apply(_, args, _) => {
|
Apply(_, args, _) => {
|
||||||
for x in args {
|
for x in args {
|
||||||
variables_help_detailed(x, accum);
|
variables_help_detailed(x, accum);
|
||||||
|
@ -2089,7 +2077,7 @@ pub enum ErrorType {
|
||||||
RecursiveTagUnion(Box<ErrorType>, SendMap<TagName, Vec<ErrorType>>, TypeExt),
|
RecursiveTagUnion(Box<ErrorType>, SendMap<TagName, Vec<ErrorType>>, TypeExt),
|
||||||
Function(Vec<ErrorType>, Box<ErrorType>, Box<ErrorType>),
|
Function(Vec<ErrorType>, Box<ErrorType>, Box<ErrorType>),
|
||||||
Alias(Symbol, Vec<ErrorType>, Box<ErrorType>, AliasKind),
|
Alias(Symbol, Vec<ErrorType>, Box<ErrorType>, AliasKind),
|
||||||
Range(Box<ErrorType>, Vec<ErrorType>),
|
Range(Vec<ErrorType>),
|
||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2145,8 +2133,7 @@ impl ErrorType {
|
||||||
});
|
});
|
||||||
t.add_names(taken);
|
t.add_names(taken);
|
||||||
}
|
}
|
||||||
Range(typ, ts) => {
|
Range(ts) => {
|
||||||
typ.add_names(taken);
|
|
||||||
ts.iter().for_each(|t| {
|
ts.iter().for_each(|t| {
|
||||||
t.add_names(taken);
|
t.add_names(taken);
|
||||||
});
|
});
|
||||||
|
@ -2472,8 +2459,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
|
||||||
|
|
||||||
write_debug_error_type_help(*rec, buf, Parens::Unnecessary);
|
write_debug_error_type_help(*rec, buf, Parens::Unnecessary);
|
||||||
}
|
}
|
||||||
Range(typ, types) => {
|
Range(types) => {
|
||||||
write_debug_error_type_help(*typ, buf, parens);
|
|
||||||
buf.push('<');
|
buf.push('<');
|
||||||
|
|
||||||
let mut it = types.into_iter().peekable();
|
let mut it = types.into_iter().peekable();
|
||||||
|
@ -2825,9 +2811,7 @@ fn instantiate_lambda_sets_as_unspecialized(
|
||||||
stack.extend(args.iter_mut().rev());
|
stack.extend(args.iter_mut().rev());
|
||||||
}
|
}
|
||||||
Type::Variable(_) => {}
|
Type::Variable(_) => {}
|
||||||
Type::RangedNumber(t, _) => {
|
Type::RangedNumber(_) => {}
|
||||||
stack.push(t);
|
|
||||||
}
|
|
||||||
Type::Erroneous(_) => {}
|
Type::Erroneous(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -475,7 +475,7 @@ fn unify_context<M: MetaCollector>(subs: &mut Subs, pool: &mut Pool, ctx: Contex
|
||||||
unify_opaque(subs, pool, &ctx, *symbol, *args, *real_var)
|
unify_opaque(subs, pool, &ctx, *symbol, *args, *real_var)
|
||||||
}
|
}
|
||||||
LambdaSet(lset) => unify_lambda_set(subs, pool, &ctx, *lset, &ctx.second_desc.content),
|
LambdaSet(lset) => unify_lambda_set(subs, pool, &ctx, *lset, &ctx.second_desc.content),
|
||||||
&RangedNumber(typ, range_vars) => unify_ranged_number(subs, pool, &ctx, typ, range_vars),
|
&RangedNumber(range_vars) => unify_ranged_number(subs, &ctx, range_vars),
|
||||||
Error => {
|
Error => {
|
||||||
// Error propagates. Whatever we're comparing it to doesn't matter!
|
// Error propagates. Whatever we're comparing it to doesn't matter!
|
||||||
merge(subs, &ctx, Error)
|
merge(subs, &ctx, Error)
|
||||||
|
@ -500,9 +500,7 @@ fn not_in_range_mismatch<M: MetaCollector>() -> Outcome<M> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unify_ranged_number<M: MetaCollector>(
|
fn unify_ranged_number<M: MetaCollector>(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
pool: &mut Pool,
|
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
real_var: Variable,
|
|
||||||
range_vars: NumericRange,
|
range_vars: NumericRange,
|
||||||
) -> Outcome<M> {
|
) -> Outcome<M> {
|
||||||
let other_content = &ctx.second_desc.content;
|
let other_content = &ctx.second_desc.content;
|
||||||
|
@ -510,71 +508,38 @@ fn unify_ranged_number<M: MetaCollector>(
|
||||||
match other_content {
|
match other_content {
|
||||||
FlexVar(_) => {
|
FlexVar(_) => {
|
||||||
// Ranged number wins
|
// Ranged number wins
|
||||||
merge(subs, ctx, RangedNumber(real_var, range_vars))
|
merge(subs, ctx, RangedNumber(range_vars))
|
||||||
}
|
}
|
||||||
RecursionVar { .. }
|
RecursionVar { .. }
|
||||||
| RigidVar(..)
|
| RigidVar(..)
|
||||||
| Alias(..)
|
| Alias(..)
|
||||||
| Structure(..)
|
| Structure(..)
|
||||||
| RigidAbleVar(..)
|
| RigidAbleVar(..)
|
||||||
| FlexAbleVar(..) => {
|
| FlexAbleVar(..) => check_and_merge_valid_range(subs, ctx, ctx.second, range_vars),
|
||||||
let outcome = unify_pool(subs, pool, real_var, ctx.second, ctx.mode);
|
&RangedNumber(other_range_vars) => match range_vars.intersection(&other_range_vars) {
|
||||||
if !outcome.mismatches.is_empty() {
|
Some(range) => merge(subs, ctx, RangedNumber(range)),
|
||||||
return outcome;
|
None => not_in_range_mismatch(),
|
||||||
}
|
},
|
||||||
let outcome = check_valid_range(subs, ctx.second, range_vars);
|
|
||||||
if !outcome.mismatches.is_empty() {
|
|
||||||
return outcome;
|
|
||||||
}
|
|
||||||
let real_var = subs.fresh(subs.get_without_compacting(real_var));
|
|
||||||
merge(subs, ctx, RangedNumber(real_var, range_vars))
|
|
||||||
}
|
|
||||||
&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() {
|
|
||||||
return outcome;
|
|
||||||
}
|
|
||||||
match range_vars.intersection(&other_range_vars) {
|
|
||||||
Some(range) => merge(subs, ctx, RangedNumber(real_var, range)),
|
|
||||||
None => not_in_range_mismatch(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LambdaSet(..) => mismatch!(),
|
LambdaSet(..) => mismatch!(),
|
||||||
Error => merge(subs, ctx, Error),
|
Error => merge(subs, ctx, Error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_valid_range<M: MetaCollector>(
|
fn check_and_merge_valid_range<M: MetaCollector>(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
|
ctx: &Context,
|
||||||
var: Variable,
|
var: Variable,
|
||||||
range: NumericRange,
|
range: NumericRange,
|
||||||
) -> Outcome<M> {
|
) -> Outcome<M> {
|
||||||
let content = subs.get_content_without_compacting(var);
|
use roc_types::num::MatchResult;
|
||||||
|
let content = *subs.get_content_without_compacting(var);
|
||||||
|
|
||||||
match content {
|
match range.match_content(subs, &content) {
|
||||||
&Content::Alias(symbol, _, actual, _) => {
|
MatchResult::RangeInContent => merge(subs, ctx, RangedNumber(range)),
|
||||||
match range.contains_symbol(symbol) {
|
MatchResult::ContentInRange => merge(subs, ctx, content),
|
||||||
None => {
|
MatchResult::NoIntersection => not_in_range_mismatch(),
|
||||||
// symbol not recognized; go into the alias
|
MatchResult::DifferentContent => mismatch!(),
|
||||||
return check_valid_range(subs, actual, range);
|
|
||||||
}
|
|
||||||
Some(false) => {
|
|
||||||
return not_in_range_mismatch();
|
|
||||||
}
|
|
||||||
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::default()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -668,13 +633,8 @@ fn unify_alias<M: MetaCollector>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Structure(_) => unify_pool(subs, pool, real_var, ctx.second, ctx.mode),
|
Structure(_) => unify_pool(subs, pool, real_var, ctx.second, ctx.mode),
|
||||||
RangedNumber(other_real_var, other_range_vars) => {
|
RangedNumber(other_range_vars) => {
|
||||||
let outcome = unify_pool(subs, pool, real_var, *other_real_var, ctx.mode);
|
check_and_merge_valid_range(subs, ctx, ctx.first, *other_range_vars)
|
||||||
if outcome.mismatches.is_empty() {
|
|
||||||
check_valid_range(subs, real_var, *other_range_vars)
|
|
||||||
} else {
|
|
||||||
outcome
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
LambdaSet(..) => mismatch!("cannot unify alias {:?} with lambda set {:?}: lambda sets should never be directly behind an alias!", ctx.first, other_content),
|
LambdaSet(..) => mismatch!("cannot unify alias {:?} with lambda set {:?}: lambda sets should never be directly behind an alias!", ctx.first, other_content),
|
||||||
Error => merge(subs, ctx, Error),
|
Error => merge(subs, ctx, Error),
|
||||||
|
@ -731,15 +691,11 @@ fn unify_opaque<M: MetaCollector>(
|
||||||
mismatch!("{:?}", symbol)
|
mismatch!("{:?}", symbol)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RangedNumber(other_real_var, other_range_vars) => {
|
RangedNumber(other_range_vars) => {
|
||||||
// This opaque might be a number, check if it unifies with the target ranged number var.
|
// 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);
|
check_and_merge_valid_range(subs, ctx, ctx.first, *other_range_vars)
|
||||||
if outcome.mismatches.is_empty() {
|
|
||||||
check_valid_range(subs, ctx.first, *other_range_vars)
|
|
||||||
} else {
|
|
||||||
outcome
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Error => merge(subs, ctx, Error),
|
||||||
// _other has an underscore because it's unused in --release builds
|
// _other has an underscore because it's unused in --release builds
|
||||||
_other => {
|
_other => {
|
||||||
// The type on the left is an opaque, but the one on the right is not!
|
// The type on the left is an opaque, but the one on the right is not!
|
||||||
|
@ -874,13 +830,8 @@ fn unify_structure<M: MetaCollector>(
|
||||||
// other
|
// other
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
RangedNumber(other_real_var, other_range_vars) => {
|
RangedNumber(other_range_vars) => {
|
||||||
let outcome = unify_pool(subs, pool, ctx.first, *other_real_var, ctx.mode);
|
check_and_merge_valid_range(subs, ctx, ctx.first, *other_range_vars)
|
||||||
if outcome.mismatches.is_empty() {
|
|
||||||
check_valid_range(subs, ctx.first, *other_range_vars)
|
|
||||||
} else {
|
|
||||||
outcome
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Error => merge(subs, ctx, Error),
|
Error => merge(subs, ctx, Error),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1520,6 +1520,7 @@ fn type_comparison<'b>(
|
||||||
lines.push(alloc.concat(context_hints));
|
lines.push(alloc.concat(context_hints));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dbg!(&comparison.problems);
|
||||||
lines.extend(problems_to_tip(
|
lines.extend(problems_to_tip(
|
||||||
alloc,
|
alloc,
|
||||||
comparison.problems,
|
comparison.problems,
|
||||||
|
@ -2347,13 +2348,12 @@ fn to_doc_help<'b>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Range(typ, range_types) => {
|
Range(range_types) => {
|
||||||
let typ = to_doc_help(ctx, alloc, parens, *typ);
|
|
||||||
let range_types = range_types
|
let range_types = range_types
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|arg| to_doc_help(ctx, alloc, Parens::Unnecessary, arg))
|
.map(|arg| to_doc_help(ctx, alloc, Parens::Unnecessary, arg))
|
||||||
.collect();
|
.collect();
|
||||||
report_text::range(alloc, typ, range_types)
|
report_text::range(alloc, range_types)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2407,6 +2407,27 @@ fn error_type_to_doc<'b>(
|
||||||
type_with_able_vars(alloc, typ, able_vars)
|
type_with_able_vars(alloc, typ, able_vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compact_builtin_aliases(typ: ErrorType) -> ErrorType {
|
||||||
|
use ErrorType::*;
|
||||||
|
match typ {
|
||||||
|
Alias(Symbol::NUM_NUM, mut args, real, kind) => {
|
||||||
|
debug_assert!(args.len() == 1);
|
||||||
|
let type_arg = args.remove(0);
|
||||||
|
|
||||||
|
match type_arg {
|
||||||
|
Alias(Symbol::NUM_FLOATINGPOINT, inner_args, real, _) => {
|
||||||
|
Alias(Symbol::NUM_FRAC, inner_args, real, AliasKind::Structural)
|
||||||
|
}
|
||||||
|
Alias(Symbol::NUM_INTEGER, inner_args, real, _) => {
|
||||||
|
Alias(Symbol::NUM_INT, inner_args, real, AliasKind::Structural)
|
||||||
|
}
|
||||||
|
_ => Alias(Symbol::NUM_NUM, vec![type_arg], real, kind),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
typ => typ,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn to_diff<'b>(
|
fn to_diff<'b>(
|
||||||
alloc: &'b RocDocAllocator<'b>,
|
alloc: &'b RocDocAllocator<'b>,
|
||||||
parens: Parens,
|
parens: Parens,
|
||||||
|
@ -2415,6 +2436,11 @@ fn to_diff<'b>(
|
||||||
) -> Diff<RocDocBuilder<'b>> {
|
) -> Diff<RocDocBuilder<'b>> {
|
||||||
use ErrorType::*;
|
use ErrorType::*;
|
||||||
|
|
||||||
|
let (type1, type2) = (
|
||||||
|
compact_builtin_aliases(type1),
|
||||||
|
compact_builtin_aliases(type2),
|
||||||
|
);
|
||||||
|
|
||||||
// TODO remove clone
|
// TODO remove clone
|
||||||
match (type1.clone(), type2.clone()) {
|
match (type1.clone(), type2.clone()) {
|
||||||
(Error, Error) | (Infinite, Infinite) => same(alloc, parens, type1),
|
(Error, Error) | (Infinite, Infinite) => same(alloc, parens, type1),
|
||||||
|
@ -3318,7 +3344,6 @@ mod report_text {
|
||||||
|
|
||||||
pub fn range<'b>(
|
pub fn range<'b>(
|
||||||
alloc: &'b RocDocAllocator<'b>,
|
alloc: &'b RocDocAllocator<'b>,
|
||||||
_encompassing_type: RocDocBuilder<'b>,
|
|
||||||
ranged_types: Vec<RocDocBuilder<'b>>,
|
ranged_types: Vec<RocDocBuilder<'b>>,
|
||||||
) -> RocDocBuilder<'b> {
|
) -> RocDocBuilder<'b> {
|
||||||
let mut doc = Vec::with_capacity(ranged_types.len() * 2);
|
let mut doc = Vec::with_capacity(ranged_types.len() * 2);
|
||||||
|
|
|
@ -3301,12 +3301,12 @@ mod test_reporting {
|
||||||
|
|
||||||
This `ACons` tag application has the type:
|
This `ACons` tag application has the type:
|
||||||
|
|
||||||
[ACons (Int Signed64) [BCons (Int Signed64) [ACons Str [BCons (Int Signed64) [ACons (Int Signed64) (BList (Int Signed64) I64),
|
[ACons (Int Signed64) [BCons (Int Signed64) [ACons Str [BCons I64 [ACons I64 (BList I64 I64),
|
||||||
ANil] as ∞, BNil], ANil], BNil], ANil]
|
ANil] as ∞, BNil], ANil], BNil], ANil]
|
||||||
|
|
||||||
But the type annotation on `x` says it should be:
|
But the type annotation on `x` says it should be:
|
||||||
|
|
||||||
[ACons (Int Signed64) (BList (Int Signed64) I64), ANil] as a
|
[ACons I64 (BList I64 I64), ANil] as a
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3375,6 +3375,21 @@ mod test_reporting {
|
||||||
-170_141_183_460_469_231_731_687_303_715_884_105_728.
|
-170_141_183_460_469_231_731_687_303_715_884_105_728.
|
||||||
|
|
||||||
Tip: Learn more about number literals at TODO
|
Tip: Learn more about number literals at TODO
|
||||||
|
|
||||||
|
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
|
The 2nd argument to `add` is not what I expect:
|
||||||
|
|
||||||
|
14│ x + y + h + l + minlit + maxlit
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
This `maxlit` value is a:
|
||||||
|
|
||||||
|
U128
|
||||||
|
|
||||||
|
But `add` needs the 2nd argument to be:
|
||||||
|
|
||||||
|
I128 or Dec
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue