mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 22:34:45 +00:00
Second pass
This commit is contained in:
parent
5e0d90ac53
commit
8dc92ccd97
15 changed files with 651 additions and 275 deletions
|
@ -27,14 +27,6 @@ pub enum Constraint {
|
||||||
Let(Box<LetConstraint>),
|
Let(Box<LetConstraint>),
|
||||||
And(Vec<Constraint>),
|
And(Vec<Constraint>),
|
||||||
Present(Type, PresenceConstraint),
|
Present(Type, PresenceConstraint),
|
||||||
|
|
||||||
/// `EqBoundedRange(Ts, U, ...)` means there must be at least one `T` in the *ordered* range `Ts`
|
|
||||||
/// that unifies (via `Eq`) with `U`.
|
|
||||||
///
|
|
||||||
/// This is only used for integers, where we may see e.g. the number literal `-1` and know it
|
|
||||||
/// has the bounded range `[I8, I16, I32, I64, I128]`, at least one of which must unify with
|
|
||||||
/// the type the number literal is used as.
|
|
||||||
EqBoundedRange(Type, Expected<Vec<Type>>, Category, Region),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -95,7 +87,6 @@ impl Constraint {
|
||||||
}
|
}
|
||||||
Constraint::And(cs) => cs.iter().any(|c| c.contains_save_the_environment()),
|
Constraint::And(cs) => cs.iter().any(|c| c.contains_save_the_environment()),
|
||||||
Constraint::Present(_, _) => false,
|
Constraint::Present(_, _) => false,
|
||||||
Constraint::EqBoundedRange(_, _, _, _) => false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,11 +171,5 @@ fn validate_help(constraint: &Constraint, declared: &Declared, accum: &mut Varia
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Constraint::EqBoundedRange(typ, one_of, _, _) => {
|
|
||||||
subtract(declared, &typ.variables_detail(), accum);
|
|
||||||
for typ in one_of.get_type_ref() {
|
|
||||||
subtract(declared, &typ.variables_detail(), accum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -261,7 +261,7 @@ fn from_str_radix(src: &str, radix: u32) -> Result<(IntValue, NumericBound), Int
|
||||||
};
|
};
|
||||||
|
|
||||||
let (lower_bound, is_negative) = match result {
|
let (lower_bound, is_negative) = match result {
|
||||||
IntValue::I128(num) => (lower_bound_of_int(num), num <= 0),
|
IntValue::I128(num) => (lower_bound_of_int(num), num < 0),
|
||||||
IntValue::U128(_) => (IntWidth::U128, false),
|
IntValue::U128(_) => (IntWidth::U128, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,38 +11,31 @@ use roc_types::types::Category;
|
||||||
use roc_types::types::Reason;
|
use roc_types::types::Reason;
|
||||||
use roc_types::types::Type::{self, *};
|
use roc_types::types::Type::{self, *};
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn add_numeric_bound_constr(
|
pub fn add_numeric_bound_constr(
|
||||||
constrs: &mut Vec<Constraint>,
|
constrs: &mut Vec<Constraint>,
|
||||||
num_type: Type,
|
num_type: Type,
|
||||||
bound: impl TypedNumericBound,
|
bound: impl TypedNumericBound,
|
||||||
region: Region,
|
region: Region,
|
||||||
category: Category,
|
category: Category,
|
||||||
) {
|
) -> Type {
|
||||||
if let Some(typ) = bound.concrete_num_type() {
|
|
||||||
constrs.push(Eq(
|
|
||||||
num_type,
|
|
||||||
Expected::ForReason(Reason::NumericLiteralSuffix, typ, region),
|
|
||||||
category,
|
|
||||||
region,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_numeric_range_constr(
|
|
||||||
constrs: &mut Vec<Constraint>,
|
|
||||||
num_type: Type,
|
|
||||||
bound: impl TypedNumericBound,
|
|
||||||
region: Region,
|
|
||||||
category: Category,
|
|
||||||
) {
|
|
||||||
let range = bound.bounded_range();
|
let range = bound.bounded_range();
|
||||||
if !range.is_empty() {
|
|
||||||
constrs.push(EqBoundedRange(
|
let total_num_type = num_type;
|
||||||
num_type,
|
|
||||||
Expected::ForReason(Reason::NumericLiteralSuffix, range, region),
|
match range.len() {
|
||||||
category,
|
0 => total_num_type,
|
||||||
region,
|
1 => {
|
||||||
));
|
let actual_type = Variable(range[0]);
|
||||||
|
constrs.push(Eq(
|
||||||
|
total_num_type.clone(),
|
||||||
|
Expected::ForReason(Reason::NumericLiteralSuffix, actual_type, region),
|
||||||
|
category,
|
||||||
|
region,
|
||||||
|
));
|
||||||
|
total_num_type
|
||||||
|
}
|
||||||
|
_ => RangedNumber(Box::new(total_num_type.clone()), range),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,13 +47,18 @@ pub fn int_literal(
|
||||||
region: Region,
|
region: Region,
|
||||||
bound: IntBound,
|
bound: IntBound,
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
let num_type = Variable(num_var);
|
|
||||||
let reason = Reason::IntLiteral;
|
let reason = Reason::IntLiteral;
|
||||||
|
|
||||||
let mut constrs = Vec::with_capacity(3);
|
let mut constrs = Vec::with_capacity(3);
|
||||||
// Always add the bound first; this improves the resolved type quality in case it's an alias
|
// Always add the bound first; this improves the resolved type quality in case it's an alias
|
||||||
// like "U8".
|
// like "U8".
|
||||||
add_numeric_bound_constr(&mut constrs, num_type.clone(), bound, region, Category::Num);
|
let num_type = add_numeric_bound_constr(
|
||||||
|
&mut constrs,
|
||||||
|
Variable(num_var),
|
||||||
|
bound,
|
||||||
|
region,
|
||||||
|
Category::Num,
|
||||||
|
);
|
||||||
constrs.extend(vec![
|
constrs.extend(vec![
|
||||||
Eq(
|
Eq(
|
||||||
num_type.clone(),
|
num_type.clone(),
|
||||||
|
@ -70,13 +68,6 @@ pub fn int_literal(
|
||||||
),
|
),
|
||||||
Eq(num_type, expected.clone(), Category::Int, region),
|
Eq(num_type, expected.clone(), Category::Int, region),
|
||||||
]);
|
]);
|
||||||
add_numeric_range_constr(
|
|
||||||
&mut constrs,
|
|
||||||
expected.get_type(),
|
|
||||||
bound,
|
|
||||||
region,
|
|
||||||
Category::Int,
|
|
||||||
);
|
|
||||||
|
|
||||||
exists(vec![num_var], And(constrs))
|
exists(vec![num_var], And(constrs))
|
||||||
}
|
}
|
||||||
|
@ -89,13 +80,12 @@ pub fn float_literal(
|
||||||
region: Region,
|
region: Region,
|
||||||
bound: FloatBound,
|
bound: FloatBound,
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
let num_type = Variable(num_var);
|
|
||||||
let reason = Reason::FloatLiteral;
|
let reason = Reason::FloatLiteral;
|
||||||
|
|
||||||
let mut constrs = Vec::with_capacity(3);
|
let mut constrs = Vec::with_capacity(3);
|
||||||
add_numeric_bound_constr(
|
let num_type = add_numeric_bound_constr(
|
||||||
&mut constrs,
|
&mut constrs,
|
||||||
num_type.clone(),
|
Variable(num_var),
|
||||||
bound,
|
bound,
|
||||||
region,
|
region,
|
||||||
Category::Float,
|
Category::Float,
|
||||||
|
@ -120,10 +110,11 @@ pub fn num_literal(
|
||||||
region: Region,
|
region: Region,
|
||||||
bound: NumericBound,
|
bound: NumericBound,
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
let num_type = crate::builtins::num_num(Type::Variable(num_var));
|
let open_number_type = crate::builtins::num_num(Type::Variable(num_var));
|
||||||
|
|
||||||
let mut constrs = Vec::with_capacity(3);
|
let mut constrs = Vec::with_capacity(3);
|
||||||
add_numeric_bound_constr(&mut constrs, num_type.clone(), bound, region, Category::Num);
|
let num_type =
|
||||||
|
add_numeric_bound_constr(&mut constrs, open_number_type, bound, region, Category::Num);
|
||||||
constrs.extend(vec![Eq(num_type, expected, Category::Num, region)]);
|
constrs.extend(vec![Eq(num_type, expected, Category::Num, region)]);
|
||||||
|
|
||||||
exists(vec![num_var], And(constrs))
|
exists(vec![num_var], And(constrs))
|
||||||
|
@ -219,56 +210,56 @@ pub fn num_int(range: Type) -> Type {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! num_types {
|
// macro_rules! num_types {
|
||||||
// Represent
|
// // Represent
|
||||||
// num_u8 ~ U8 : Num Integer Unsigned8 = @Num (@Integer (@Unsigned8))
|
// // num_u8 ~ U8 : Num Integer Unsigned8 = @Num (@Integer (@Unsigned8))
|
||||||
// int_u8 ~ Integer Unsigned8 = @Integer (@Unsigned8)
|
// // int_u8 ~ Integer Unsigned8 = @Integer (@Unsigned8)
|
||||||
//
|
// //
|
||||||
// num_f32 ~ F32 : Num FloaingPoint Binary32 = @Num (@FloaingPoint (@Binary32))
|
// // num_f32 ~ F32 : Num FloaingPoint Binary32 = @Num (@FloaingPoint (@Binary32))
|
||||||
// float_f32 ~ FloatingPoint Binary32 = @FloatingPoint (@Binary32)
|
// // float_f32 ~ FloatingPoint Binary32 = @FloatingPoint (@Binary32)
|
||||||
// and so on, for all numeric types.
|
// // and so on, for all numeric types.
|
||||||
($($num_fn:ident, $sub_fn:ident, $num_type:ident, $alias:path, $inner_alias:path, $inner_private_tag:path)*) => {
|
// ($($num_fn:ident, $sub_fn:ident, $num_type:ident, $alias:path, $inner_alias:path, $inner_private_tag:path)*) => {
|
||||||
$(
|
// $(
|
||||||
#[inline(always)]
|
// #[inline(always)]
|
||||||
fn $sub_fn() -> Type {
|
// fn $sub_fn() -> Type {
|
||||||
builtin_alias(
|
// builtin_alias(
|
||||||
$inner_alias,
|
// $inner_alias,
|
||||||
vec![],
|
// vec![],
|
||||||
Box::new(Type::TagUnion(
|
// Box::new(Type::TagUnion(
|
||||||
vec![(TagName::Private($inner_private_tag), vec![])],
|
// vec![(TagName::Private($inner_private_tag), vec![])],
|
||||||
Box::new(Type::EmptyTagUnion)
|
// Box::new(Type::EmptyTagUnion)
|
||||||
)),
|
// )),
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
#[inline(always)]
|
// #[inline(always)]
|
||||||
fn $num_fn() -> Type {
|
// fn $num_fn() -> Type {
|
||||||
builtin_alias(
|
// builtin_alias(
|
||||||
$alias,
|
// $alias,
|
||||||
vec![],
|
// vec![],
|
||||||
Box::new($num_type($sub_fn()))
|
// Box::new($num_type($sub_fn()))
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
)*
|
// )*
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
num_types! {
|
// num_types! {
|
||||||
num_u8, int_u8, num_int, Symbol::NUM_U8, Symbol::NUM_UNSIGNED8, Symbol::NUM_AT_UNSIGNED8
|
// num_u8, int_u8, num_int, Symbol::NUM_U8, Symbol::NUM_UNSIGNED8, Symbol::NUM_AT_UNSIGNED8
|
||||||
num_u16, int_u16, num_int, Symbol::NUM_U16, Symbol::NUM_UNSIGNED16, Symbol::NUM_AT_UNSIGNED16
|
// num_u16, int_u16, num_int, Symbol::NUM_U16, Symbol::NUM_UNSIGNED16, Symbol::NUM_AT_UNSIGNED16
|
||||||
num_u32, int_u32, num_int, Symbol::NUM_U32, Symbol::NUM_UNSIGNED32, Symbol::NUM_AT_UNSIGNED32
|
// num_u32, int_u32, num_int, Symbol::NUM_U32, Symbol::NUM_UNSIGNED32, Symbol::NUM_AT_UNSIGNED32
|
||||||
num_u64, int_u64, num_int, Symbol::NUM_U64, Symbol::NUM_UNSIGNED64, Symbol::NUM_AT_UNSIGNED64
|
// num_u64, int_u64, num_int, Symbol::NUM_U64, Symbol::NUM_UNSIGNED64, Symbol::NUM_AT_UNSIGNED64
|
||||||
num_u128, int_u128, num_int, Symbol::NUM_U128, Symbol::NUM_UNSIGNED128, Symbol::NUM_AT_UNSIGNED128
|
// num_u128, int_u128, num_int, Symbol::NUM_U128, Symbol::NUM_UNSIGNED128, Symbol::NUM_AT_UNSIGNED128
|
||||||
num_i8, int_i8, num_int, Symbol::NUM_I8, Symbol::NUM_SIGNED8, Symbol::NUM_AT_SIGNED8
|
// num_i8, int_i8, num_int, Symbol::NUM_I8, Symbol::NUM_SIGNED8, Symbol::NUM_AT_SIGNED8
|
||||||
num_i16, int_i16, num_int, Symbol::NUM_I16, Symbol::NUM_SIGNED16, Symbol::NUM_AT_SIGNED16
|
// num_i16, int_i16, num_int, Symbol::NUM_I16, Symbol::NUM_SIGNED16, Symbol::NUM_AT_SIGNED16
|
||||||
num_i32, int_i32, num_int, Symbol::NUM_I32, Symbol::NUM_SIGNED32, Symbol::NUM_AT_SIGNED32
|
// num_i32, int_i32, num_int, Symbol::NUM_I32, Symbol::NUM_SIGNED32, Symbol::NUM_AT_SIGNED32
|
||||||
num_i64, int_i64, num_int, Symbol::NUM_I64, Symbol::NUM_SIGNED64, Symbol::NUM_AT_SIGNED64
|
// num_i64, int_i64, num_int, Symbol::NUM_I64, Symbol::NUM_SIGNED64, Symbol::NUM_AT_SIGNED64
|
||||||
num_i128, int_i128, num_int, Symbol::NUM_I128, Symbol::NUM_SIGNED128, Symbol::NUM_AT_SIGNED128
|
// num_i128, int_i128, num_int, Symbol::NUM_I128, Symbol::NUM_SIGNED128, Symbol::NUM_AT_SIGNED128
|
||||||
num_nat, int_nat, num_int, Symbol::NUM_NAT, Symbol::NUM_NATURAL, Symbol::NUM_AT_NATURAL
|
// num_nat, int_nat, num_int, Symbol::NUM_NAT, Symbol::NUM_NATURAL, Symbol::NUM_AT_NATURAL
|
||||||
num_dec, float_dec, num_float, Symbol::NUM_DEC, Symbol::NUM_DECIMAL, Symbol::NUM_AT_DECIMAL
|
// num_dec, float_dec, num_float, Symbol::NUM_DEC, Symbol::NUM_DECIMAL, Symbol::NUM_AT_DECIMAL
|
||||||
num_f32, float_f32, num_float, Symbol::NUM_F32, Symbol::NUM_BINARY32, Symbol::NUM_AT_BINARY32
|
// num_f32, float_f32, num_float, Symbol::NUM_F32, Symbol::NUM_BINARY32, Symbol::NUM_AT_BINARY32
|
||||||
num_f64, float_f64, num_float, Symbol::NUM_F64, Symbol::NUM_BINARY64, Symbol::NUM_AT_BINARY64
|
// num_f64, float_f64, num_float, Symbol::NUM_F64, Symbol::NUM_BINARY64, Symbol::NUM_AT_BINARY64
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn num_signed64() -> Type {
|
pub fn num_signed64() -> Type {
|
||||||
|
@ -312,37 +303,26 @@ pub fn num_num(typ: Type) -> Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TypedNumericBound {
|
pub trait TypedNumericBound {
|
||||||
/// Get a concrete type for this number, if one exists.
|
fn bounded_range(&self) -> Vec<Variable>;
|
||||||
/// Returns `None` e.g. if the bound is open, like `Int *`.
|
|
||||||
fn concrete_num_type(&self) -> Option<Type>;
|
|
||||||
|
|
||||||
fn bounded_range(&self) -> Vec<Type>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypedNumericBound for IntBound {
|
impl TypedNumericBound for IntBound {
|
||||||
fn concrete_num_type(&self) -> Option<Type> {
|
fn bounded_range(&self) -> Vec<Variable> {
|
||||||
match self {
|
|
||||||
IntBound::None | IntBound::AtLeast { .. } => None,
|
|
||||||
IntBound::Exact(w) => Some(match w {
|
|
||||||
IntWidth::U8 => num_u8(),
|
|
||||||
IntWidth::U16 => num_u16(),
|
|
||||||
IntWidth::U32 => num_u32(),
|
|
||||||
IntWidth::U64 => num_u64(),
|
|
||||||
IntWidth::U128 => num_u128(),
|
|
||||||
IntWidth::I8 => num_i8(),
|
|
||||||
IntWidth::I16 => num_i16(),
|
|
||||||
IntWidth::I32 => num_i32(),
|
|
||||||
IntWidth::I64 => num_i64(),
|
|
||||||
IntWidth::I128 => num_i128(),
|
|
||||||
IntWidth::Nat => num_nat(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bounded_range(&self) -> Vec<Type> {
|
|
||||||
match self {
|
match self {
|
||||||
IntBound::None => vec![],
|
IntBound::None => vec![],
|
||||||
IntBound::Exact(_) => 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 } => {
|
IntBound::AtLeast { sign, width } => {
|
||||||
let whole_range: &[(IntWidth, Variable)] = match sign {
|
let whole_range: &[(IntWidth, Variable)] = match sign {
|
||||||
SignDemand::NoDemand => {
|
SignDemand::NoDemand => {
|
||||||
|
@ -371,7 +351,7 @@ impl TypedNumericBound for IntBound {
|
||||||
whole_range
|
whole_range
|
||||||
.iter()
|
.iter()
|
||||||
.skip_while(|(lower_bound, _)| *lower_bound != *width)
|
.skip_while(|(lower_bound, _)| *lower_bound != *width)
|
||||||
.map(|(_, var)| Type::Variable(*var))
|
.map(|(_, var)| *var)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -379,35 +359,20 @@ impl TypedNumericBound for IntBound {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypedNumericBound for FloatBound {
|
impl TypedNumericBound for FloatBound {
|
||||||
fn concrete_num_type(&self) -> Option<Type> {
|
fn bounded_range(&self) -> Vec<Variable> {
|
||||||
match self {
|
|
||||||
FloatBound::None => None,
|
|
||||||
FloatBound::Exact(w) => Some(match w {
|
|
||||||
FloatWidth::Dec => num_dec(),
|
|
||||||
FloatWidth::F32 => num_f32(),
|
|
||||||
FloatWidth::F64 => num_f64(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bounded_range(&self) -> Vec<Type> {
|
|
||||||
match self {
|
match self {
|
||||||
FloatBound::None => vec![],
|
FloatBound::None => vec![],
|
||||||
FloatBound::Exact(_) => vec![],
|
FloatBound::Exact(w) => vec![match w {
|
||||||
|
FloatWidth::Dec => Variable::DEC,
|
||||||
|
FloatWidth::F32 => Variable::F32,
|
||||||
|
FloatWidth::F64 => Variable::F64,
|
||||||
|
}],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypedNumericBound for NumericBound {
|
impl TypedNumericBound for NumericBound {
|
||||||
fn concrete_num_type(&self) -> Option<Type> {
|
fn bounded_range(&self) -> Vec<Variable> {
|
||||||
match self {
|
|
||||||
NumericBound::None => None,
|
|
||||||
NumericBound::Int(ib) => ib.concrete_num_type(),
|
|
||||||
NumericBound::Float(fb) => fb.concrete_num_type(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bounded_range(&self) -> Vec<Type> {
|
|
||||||
match self {
|
match self {
|
||||||
NumericBound::None => vec![],
|
NumericBound::None => vec![],
|
||||||
NumericBound::Int(ib) => ib.bounded_range(),
|
NumericBound::Int(ib) => ib.bounded_range(),
|
||||||
|
|
|
@ -183,7 +183,7 @@ pub fn constrain_pattern(
|
||||||
|
|
||||||
let num_type = builtins::num_num(Type::Variable(var));
|
let num_type = builtins::num_num(Type::Variable(var));
|
||||||
|
|
||||||
builtins::add_numeric_bound_constr(
|
let num_type = builtins::add_numeric_bound_constr(
|
||||||
&mut state.constraints,
|
&mut state.constraints,
|
||||||
num_type.clone(),
|
num_type.clone(),
|
||||||
bound,
|
bound,
|
||||||
|
@ -202,7 +202,7 @@ pub fn constrain_pattern(
|
||||||
&IntLiteral(num_var, precision_var, _, _, bound) => {
|
&IntLiteral(num_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.
|
||||||
builtins::add_numeric_bound_constr(
|
let num_type = builtins::add_numeric_bound_constr(
|
||||||
&mut state.constraints,
|
&mut state.constraints,
|
||||||
Type::Variable(num_var),
|
Type::Variable(num_var),
|
||||||
bound,
|
bound,
|
||||||
|
@ -214,7 +214,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(Constraint::Eq(
|
state.constraints.push(Constraint::Eq(
|
||||||
Type::Variable(num_var),
|
num_type, // TODO check me if something breaks!
|
||||||
Expected::NoExpectation(int_type),
|
Expected::NoExpectation(int_type),
|
||||||
Category::Int,
|
Category::Int,
|
||||||
region,
|
region,
|
||||||
|
@ -232,7 +232,7 @@ pub fn constrain_pattern(
|
||||||
&FloatLiteral(num_var, precision_var, _, _, bound) => {
|
&FloatLiteral(num_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.
|
||||||
builtins::add_numeric_bound_constr(
|
let num_type = builtins::add_numeric_bound_constr(
|
||||||
&mut state.constraints,
|
&mut state.constraints,
|
||||||
Type::Variable(num_var),
|
Type::Variable(num_var),
|
||||||
bound,
|
bound,
|
||||||
|
@ -244,7 +244,7 @@ pub fn constrain_pattern(
|
||||||
let float_type = builtins::num_float(Type::Variable(precision_var));
|
let float_type = builtins::num_float(Type::Variable(precision_var));
|
||||||
|
|
||||||
state.constraints.push(Constraint::Eq(
|
state.constraints.push(Constraint::Eq(
|
||||||
Type::Variable(num_var),
|
num_type.clone(), // TODO check me if something breaks!
|
||||||
Expected::NoExpectation(float_type),
|
Expected::NoExpectation(float_type),
|
||||||
Category::Float,
|
Category::Float,
|
||||||
region,
|
region,
|
||||||
|
@ -254,7 +254,7 @@ pub fn constrain_pattern(
|
||||||
state.constraints.push(Constraint::Pattern(
|
state.constraints.push(Constraint::Pattern(
|
||||||
region,
|
region,
|
||||||
PatternCategory::Float,
|
PatternCategory::Float,
|
||||||
Type::Variable(num_var),
|
num_type, // TODO check me if something breaks!
|
||||||
expected,
|
expected,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ impl<'a> RawFunctionLayout<'a> {
|
||||||
Self::new_help(env, structure, structure_content.clone())
|
Self::new_help(env, structure, structure_content.clone())
|
||||||
}
|
}
|
||||||
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),
|
||||||
|
|
||||||
// Ints
|
// Ints
|
||||||
Alias(Symbol::NUM_I128, args, _) => {
|
Alias(Symbol::NUM_I128, args, _) => {
|
||||||
|
@ -902,6 +903,8 @@ impl<'a> Layout<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RangedNumber(typ, _) => Self::from_var(env, typ),
|
||||||
|
|
||||||
Error => Err(LayoutProblem::Erroneous),
|
Error => Err(LayoutProblem::Erroneous),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2562,7 +2565,7 @@ fn layout_from_num_content<'a>(
|
||||||
Alias(_, _, _) => {
|
Alias(_, _, _) => {
|
||||||
todo!("TODO recursively resolve type aliases in num_from_content");
|
todo!("TODO recursively resolve type aliases in num_from_content");
|
||||||
}
|
}
|
||||||
Structure(_) => {
|
Structure(_) | RangedNumber(..) => {
|
||||||
panic!("Invalid Num.Num type application: {:?}", content);
|
panic!("Invalid Num.Num type application: {:?}", content);
|
||||||
}
|
}
|
||||||
Error => Err(LayoutProblem::Erroneous),
|
Error => Err(LayoutProblem::Erroneous),
|
||||||
|
|
|
@ -142,6 +142,7 @@ impl FunctionLayout {
|
||||||
Content::RecursionVar { .. } => Err(TypeError(())),
|
Content::RecursionVar { .. } => Err(TypeError(())),
|
||||||
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::Error => Err(TypeError(())),
|
Content::Error => Err(TypeError(())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -249,6 +250,7 @@ impl LambdaSet {
|
||||||
}
|
}
|
||||||
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::Error => Err(TypeError(())),
|
Content::Error => Err(TypeError(())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -682,6 +684,7 @@ impl Layout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Content::RangedNumber(typ, _) => Self::from_var_help(layouts, subs, *typ),
|
||||||
Content::Error => Err(TypeError(())),
|
Content::Error => Err(TypeError(())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,7 @@ pub enum TypeError {
|
||||||
CircularType(Region, Symbol, ErrorType),
|
CircularType(Region, Symbol, ErrorType),
|
||||||
BadType(roc_types::types::Problem),
|
BadType(roc_types::types::Problem),
|
||||||
UnexposedLookup(Symbol),
|
UnexposedLookup(Symbol),
|
||||||
|
NotInRange(Region, ErrorType, Expected<Vec<ErrorType>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
|
@ -231,6 +232,17 @@ fn solve(
|
||||||
|
|
||||||
problems.push(TypeError::BadType(problem));
|
problems.push(TypeError::BadType(problem));
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
NotInRange(vars, typ, range) => {
|
||||||
|
introduce(subs, rank, pools, &vars);
|
||||||
|
|
||||||
|
problems.push(TypeError::NotInRange(
|
||||||
|
*region,
|
||||||
|
typ,
|
||||||
|
expectation.clone().replace(range),
|
||||||
|
));
|
||||||
|
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -254,7 +266,7 @@ fn solve(
|
||||||
|
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
BadType(vars, _problem) => {
|
BadType(vars, _) | NotInRange(vars, _, _) => {
|
||||||
introduce(subs, rank, pools, &vars);
|
introduce(subs, rank, pools, &vars);
|
||||||
|
|
||||||
// ERROR NOT REPORTED
|
// ERROR NOT REPORTED
|
||||||
|
@ -321,6 +333,17 @@ fn solve(
|
||||||
|
|
||||||
problems.push(TypeError::BadType(problem));
|
problems.push(TypeError::BadType(problem));
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
NotInRange(vars, typ, range) => {
|
||||||
|
introduce(subs, rank, pools, &vars);
|
||||||
|
|
||||||
|
problems.push(TypeError::NotInRange(
|
||||||
|
*region,
|
||||||
|
typ,
|
||||||
|
expectation.clone().replace(range),
|
||||||
|
));
|
||||||
|
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -391,6 +414,18 @@ fn solve(
|
||||||
|
|
||||||
problems.push(TypeError::BadType(problem));
|
problems.push(TypeError::BadType(problem));
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
NotInRange(vars, typ, range) => {
|
||||||
|
introduce(subs, rank, pools, &vars);
|
||||||
|
|
||||||
|
problems.push(TypeError::NotInRange(
|
||||||
|
*region,
|
||||||
|
typ,
|
||||||
|
// TODO expectation.clone().replace(range),
|
||||||
|
Expected::NoExpectation(range),
|
||||||
|
));
|
||||||
|
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -687,52 +722,18 @@ fn solve(
|
||||||
|
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
}
|
NotInRange(vars, typ, range) => {
|
||||||
}
|
introduce(subs, rank, pools, &vars);
|
||||||
EqBoundedRange(typ, expect_one_of, category, region) => {
|
|
||||||
let actual = type_to_var(subs, rank, pools, cached_aliases, typ);
|
|
||||||
|
|
||||||
let mut it = expect_one_of.get_type_ref().iter().peekable();
|
problems.push(TypeError::NotInRange(
|
||||||
|
Region::zero(),
|
||||||
|
typ,
|
||||||
|
Expected::NoExpectation(range),
|
||||||
|
));
|
||||||
|
|
||||||
while let Some(expected) = it.next() {
|
state
|
||||||
let expected = type_to_var(subs, rank, pools, cached_aliases, expected);
|
|
||||||
let snapshot = subs.snapshot();
|
|
||||||
match unify(subs, actual, expected, Mode::Eq) {
|
|
||||||
Success(vars) => {
|
|
||||||
introduce(subs, rank, pools, &vars);
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
Failure(..) if it.peek().is_some() => {
|
|
||||||
subs.rollback_to(snapshot);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Failure(vars, actual_type, expected_type) => {
|
|
||||||
// This is the last type we could have tried and failed; record the error.
|
|
||||||
introduce(subs, rank, pools, &vars);
|
|
||||||
|
|
||||||
let problem = TypeError::BadExpr(
|
|
||||||
*region,
|
|
||||||
category.clone(),
|
|
||||||
actual_type,
|
|
||||||
expect_one_of.clone().replace(expected_type),
|
|
||||||
);
|
|
||||||
|
|
||||||
problems.push(problem);
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
BadType(vars, problem) => {
|
|
||||||
introduce(subs, rank, pools, &vars);
|
|
||||||
|
|
||||||
problems.push(TypeError::BadType(problem));
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unreachable!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -816,6 +817,13 @@ fn type_to_variable<'a>(
|
||||||
|
|
||||||
match typ {
|
match typ {
|
||||||
Variable(var) => *var,
|
Variable(var) => *var,
|
||||||
|
RangedNumber(typ, vars) => {
|
||||||
|
let ty_var = type_to_variable(subs, rank, pools, arena, typ);
|
||||||
|
let vars = VariableSubsSlice::insert_into_subs(subs, vars.iter().copied());
|
||||||
|
let content = Content::RangedNumber(ty_var, vars);
|
||||||
|
|
||||||
|
register(subs, rank, pools, content)
|
||||||
|
}
|
||||||
Apply(symbol, arguments, _) => {
|
Apply(symbol, arguments, _) => {
|
||||||
let new_arguments = VariableSubsSlice::reserve_into_subs(subs, arguments.len());
|
let new_arguments = VariableSubsSlice::reserve_into_subs(subs, arguments.len());
|
||||||
for (target_index, var_index) in (new_arguments.indices()).zip(arguments) {
|
for (target_index, var_index) in (new_arguments.indices()).zip(arguments) {
|
||||||
|
@ -1636,6 +1644,8 @@ fn adjust_rank_content(
|
||||||
|
|
||||||
rank
|
rank
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RangedNumber(typ, _) => adjust_rank(subs, young_mark, visit_mark, group_rank, *typ),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1771,6 +1781,11 @@ fn instantiate_rigids_help(subs: &mut Subs, max_rank: Rank, initial: Variable) {
|
||||||
|
|
||||||
stack.push(var);
|
stack.push(var);
|
||||||
}
|
}
|
||||||
|
&RangedNumber(typ, vars) => {
|
||||||
|
stack.push(typ);
|
||||||
|
|
||||||
|
stack.extend(var_slice!(vars));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2027,6 +2042,23 @@ fn deep_copy_var_help(
|
||||||
|
|
||||||
copy
|
copy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RangedNumber(typ, range_vars) => {
|
||||||
|
let new_type_var = deep_copy_var_help(subs, max_rank, pools, visited, typ);
|
||||||
|
|
||||||
|
let new_vars = SubsSlice::reserve_into_subs(subs, range_vars.len());
|
||||||
|
for (target_index, var_index) in (new_vars.indices()).zip(range_vars) {
|
||||||
|
let var = subs[var_index];
|
||||||
|
let copy_var = deep_copy_var_help(subs, max_rank, pools, visited, var);
|
||||||
|
subs.variables[target_index] = copy_var;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_content = RangedNumber(new_type_var, new_vars);
|
||||||
|
|
||||||
|
subs.set(copy, make_descriptor(new_content));
|
||||||
|
|
||||||
|
copy
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,7 @@ mod solve_expr {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn int_literal() {
|
fn int_literal() {
|
||||||
infer_eq("5", "Num *");
|
infer_eq("5", "Int *");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -334,7 +334,7 @@ mod solve_expr {
|
||||||
[42]
|
[42]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"List (Num *)",
|
"List (Int *)",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,7 +346,7 @@ mod solve_expr {
|
||||||
[[[ 5 ]]]
|
[[[ 5 ]]]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"List (List (List (Num *)))",
|
"List (List (List (Int *)))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,7 +358,7 @@ mod solve_expr {
|
||||||
[ 1, 2, 3 ]
|
[ 1, 2, 3 ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"List (Num *)",
|
"List (Int *)",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,7 +370,7 @@ mod solve_expr {
|
||||||
[ [ 1 ], [ 2, 3 ] ]
|
[ [ 1 ], [ 2, 3 ] ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"List (List (Num *))",
|
"List (List (Int *))",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -518,7 +518,7 @@ mod solve_expr {
|
||||||
\_, _ -> 42
|
\_, _ -> 42
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"*, * -> Num *",
|
"*, * -> Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -689,7 +689,7 @@ mod solve_expr {
|
||||||
func
|
func
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"*, * -> Num *",
|
"*, * -> Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -753,7 +753,7 @@ mod solve_expr {
|
||||||
c
|
c
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Num *",
|
"Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -788,7 +788,7 @@ mod solve_expr {
|
||||||
alwaysFive "stuff"
|
alwaysFive "stuff"
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Num *",
|
"Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -835,7 +835,7 @@ mod solve_expr {
|
||||||
x
|
x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Num *",
|
"Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -849,7 +849,7 @@ mod solve_expr {
|
||||||
enlist 5
|
enlist 5
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"List (Num *)",
|
"List (Int *)",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -876,7 +876,7 @@ mod solve_expr {
|
||||||
1 |> (\a -> a)
|
1 |> (\a -> a)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Num *",
|
"Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -890,7 +890,7 @@ mod solve_expr {
|
||||||
1 |> always2 "foo"
|
1 |> always2 "foo"
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Num *",
|
"Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -955,7 +955,7 @@ mod solve_expr {
|
||||||
apply identity 5
|
apply identity 5
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Num *",
|
"Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -984,7 +984,7 @@ mod solve_expr {
|
||||||
// flip neverendingInt
|
// flip neverendingInt
|
||||||
// "#
|
// "#
|
||||||
// ),
|
// ),
|
||||||
// "(Num *, (a -> a)) -> Num *",
|
// "(Int *, (a -> a)) -> Int *",
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -1058,7 +1058,7 @@ mod solve_expr {
|
||||||
// 1 // 2
|
// 1 // 2
|
||||||
// "#
|
// "#
|
||||||
// ),
|
// ),
|
||||||
// "Num *",
|
// "Int *",
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -1070,7 +1070,7 @@ mod solve_expr {
|
||||||
// 1 + 2
|
// 1 + 2
|
||||||
// "#
|
// "#
|
||||||
// ),
|
// ),
|
||||||
// "Num *",
|
// "Int *",
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -1119,7 +1119,7 @@ mod solve_expr {
|
||||||
[ alwaysFive "foo", alwaysFive [] ]
|
[ alwaysFive "foo", alwaysFive [] ]
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"List (Num *)",
|
"List (Int *)",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1134,7 +1134,7 @@ mod solve_expr {
|
||||||
24
|
24
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Num *",
|
"Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1148,7 +1148,7 @@ mod solve_expr {
|
||||||
3 -> 4
|
3 -> 4
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Num *",
|
"Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1161,17 +1161,17 @@ mod solve_expr {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn one_field_record() {
|
fn one_field_record() {
|
||||||
infer_eq("{ x: 5 }", "{ x : Num * }");
|
infer_eq("{ x: 5 }", "{ x : Int * }");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn two_field_record() {
|
fn two_field_record() {
|
||||||
infer_eq("{ x: 5, y : 3.14 }", "{ x : Num *, y : Float * }");
|
infer_eq("{ x: 5, y : 3.14 }", "{ x : Int *, y : Float * }");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn record_literal_accessor() {
|
fn record_literal_accessor() {
|
||||||
infer_eq("{ x: 5, y : 3.14 }.x", "Num *");
|
infer_eq("{ x: 5, y : 3.14 }.x", "Int *");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1230,7 +1230,7 @@ mod solve_expr {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
foo : Num * -> custom
|
foo : Int * -> custom
|
||||||
|
|
||||||
foo 2
|
foo 2
|
||||||
"#
|
"#
|
||||||
|
@ -1327,7 +1327,7 @@ mod solve_expr {
|
||||||
\Foo -> 42
|
\Foo -> 42
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ Foo ] -> Num *",
|
"[ Foo ] -> Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1339,7 +1339,7 @@ mod solve_expr {
|
||||||
\@Foo -> 42
|
\@Foo -> 42
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ @Foo ] -> Num *",
|
"[ @Foo ] -> Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1354,7 +1354,7 @@ mod solve_expr {
|
||||||
False -> 0
|
False -> 0
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ False, True ] -> Num *",
|
"[ False, True ] -> Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1366,7 +1366,7 @@ mod solve_expr {
|
||||||
Foo "happy" 2020
|
Foo "happy" 2020
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ Foo Str (Num *) ]*",
|
"[ Foo Str (Int *) ]*",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1378,7 +1378,7 @@ mod solve_expr {
|
||||||
@Foo "happy" 2020
|
@Foo "happy" 2020
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ @Foo Str (Num *) ]*",
|
"[ @Foo Str (Int *) ]*",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1407,7 +1407,7 @@ mod solve_expr {
|
||||||
{ x: 4 } -> 4
|
{ x: 4 } -> 4
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Num *",
|
"Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2347,7 +2347,7 @@ mod solve_expr {
|
||||||
{ numIdentity, x : numIdentity 42, y }
|
{ numIdentity, x : numIdentity 42, y }
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"{ numIdentity : Num a -> Num a, x : Num a, y : F64 }",
|
"{ numIdentity : Num a -> Num a, x : Int *, y : F64 }",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2383,7 +2383,7 @@ mod solve_expr {
|
||||||
f
|
f
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Num * -> Num *",
|
"Int * -> Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2416,7 +2416,7 @@ mod solve_expr {
|
||||||
toBit
|
toBit
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ False, True ] -> Num *",
|
"[ False, True ] -> Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2453,7 +2453,7 @@ mod solve_expr {
|
||||||
fromBit
|
fromBit
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Num * -> [ False, True ]*",
|
"Int * -> [ False, True ]*",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2505,7 +2505,7 @@ mod solve_expr {
|
||||||
foo { x: 5 }
|
foo { x: 5 }
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Num *",
|
"Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2774,7 +2774,7 @@ mod solve_expr {
|
||||||
// infer_eq_without_problem(
|
// infer_eq_without_problem(
|
||||||
// indoc!(
|
// indoc!(
|
||||||
// r#"
|
// r#"
|
||||||
// s : Num *
|
// s : Int *
|
||||||
// s = 3.1
|
// s = 3.1
|
||||||
|
|
||||||
// s
|
// s
|
||||||
|
@ -3214,7 +3214,7 @@ mod solve_expr {
|
||||||
List.get [ 10, 9, 8, 7 ] 1
|
List.get [ 10, 9, 8, 7 ] 1
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Result (Num *) [ OutOfBounds ]*",
|
"Result (Int *) [ OutOfBounds ]*",
|
||||||
);
|
);
|
||||||
|
|
||||||
infer_eq_without_problem(
|
infer_eq_without_problem(
|
||||||
|
@ -3497,7 +3497,7 @@ mod solve_expr {
|
||||||
f
|
f
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"{ p : *, q : * }* -> Num *",
|
"{ p : *, q : * }* -> Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3552,7 +3552,7 @@ mod solve_expr {
|
||||||
_ -> 3
|
_ -> 3
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Num * -> Num *",
|
"Int * -> Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3724,7 +3724,8 @@ mod solve_expr {
|
||||||
negatePoint { x: 1, y: 2.1, z: 0x3 }
|
negatePoint { x: 1, y: 2.1, z: 0x3 }
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"{ x : Num a, y : F64, z : Int * }",
|
// TODO this should be "Int a", FIXME
|
||||||
|
"{ x : Int *, y : F64, z : Int * }",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3741,7 +3742,8 @@ mod solve_expr {
|
||||||
{ a, b }
|
{ a, b }
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"{ a : { x : Num a, y : F64, z : c }, b : { blah : Str, x : Num a, y : F64, z : c } }",
|
// TODO this should be "Int a", FIXME
|
||||||
|
"{ a : { x : Int *, y : F64, z : c }, b : { blah : Str, x : Int *, y : F64, z : c } }",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3753,7 +3755,7 @@ mod solve_expr {
|
||||||
\{ x, y ? 0 } -> x + y
|
\{ x, y ? 0 } -> x + y
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"{ x : Num a, y ? Num a }* -> Num a",
|
"{ x : Int a, y ? Int a }* -> Int a",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3767,7 +3769,7 @@ mod solve_expr {
|
||||||
x + y
|
x + y
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Num *",
|
"Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3781,7 +3783,7 @@ mod solve_expr {
|
||||||
{ x, y ? 0 } -> x + y
|
{ x, y ? 0 } -> x + y
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"{ x : Num a, y ? Num a }* -> Num a",
|
"{ x : Int a, y ? Int a }* -> Int a",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3946,7 +3948,7 @@ mod solve_expr {
|
||||||
g
|
g
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Num a -> Num a",
|
"Int a -> Int a",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3985,10 +3987,10 @@ mod solve_expr {
|
||||||
Foo Bar 1
|
Foo Bar 1
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ Foo [ Bar ]* (Num *) ]*",
|
"[ Foo [ Bar ]* (Int *) ]*",
|
||||||
);
|
);
|
||||||
|
|
||||||
infer_eq_without_problem("Foo Bar 1", "[ Foo [ Bar ]* (Num *) ]*");
|
infer_eq_without_problem("Foo Bar 1", "[ Foo [ Bar ]* (Int *) ]*");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -4680,7 +4682,7 @@ mod solve_expr {
|
||||||
x
|
x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Num *",
|
"Int *",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4981,7 +4983,7 @@ mod solve_expr {
|
||||||
None -> 0
|
None -> 0
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ None, Some { tag : [ A, B ] }* ] -> Num *",
|
"[ None, Some { tag : [ A, B ] }* ] -> Int *",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5014,7 +5016,7 @@ mod solve_expr {
|
||||||
{ x: Red, y ? 5 } -> y
|
{ x: Red, y ? 5 } -> y
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"{ x : [ Blue, Red ], y ? Num a }* -> Num a",
|
"{ x : [ Blue, Red ], y ? Int a }* -> Int a",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5027,7 +5029,8 @@ mod solve_expr {
|
||||||
\UserId id -> id + 1
|
\UserId id -> id + 1
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"[ UserId (Num a) ] -> Num a",
|
// TODO needs parantheses
|
||||||
|
"[ UserId Int a ] -> Int a",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -206,6 +206,13 @@ fn find_names_needed(
|
||||||
// TODO should we also look in the actual variable?
|
// TODO should we also look in the actual variable?
|
||||||
// find_names_needed(_actual, subs, roots, root_appearances, names_taken);
|
// find_names_needed(_actual, subs, roots, root_appearances, names_taken);
|
||||||
}
|
}
|
||||||
|
&RangedNumber(typ, vars) => {
|
||||||
|
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) => {
|
Error | Structure(Erroneous(_)) | Structure(EmptyRecord) | Structure(EmptyTagUnion) => {
|
||||||
// Errors and empty records don't need names.
|
// Errors and empty records don't need names.
|
||||||
}
|
}
|
||||||
|
@ -397,6 +404,13 @@ fn write_content(env: &Env, content: &Content, subs: &Subs, buf: &mut String, pa
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RangedNumber(typ, _range_vars) => write_content(
|
||||||
|
env,
|
||||||
|
subs.get_content_without_compacting(*typ),
|
||||||
|
subs,
|
||||||
|
buf,
|
||||||
|
parens,
|
||||||
|
),
|
||||||
Error => buf.push_str("<type mismatch>"),
|
Error => buf.push_str("<type mismatch>"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,6 +231,7 @@ impl SolvedType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Variable(var) => Self::from_var(solved_subs.inner(), *var),
|
Variable(var) => Self::from_var(solved_subs.inner(), *var),
|
||||||
|
RangedNumber(typ, _) => Self::from_type(solved_subs, &typ),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,6 +285,7 @@ impl SolvedType {
|
||||||
|
|
||||||
SolvedType::Alias(*symbol, new_args, solved_lambda_sets, Box::new(aliased_to))
|
SolvedType::Alias(*symbol, new_args, solved_lambda_sets, Box::new(aliased_to))
|
||||||
}
|
}
|
||||||
|
RangedNumber(typ, _range_vars) => Self::from_var_help(subs, recursion_vars, *typ),
|
||||||
Error => SolvedType::Error,
|
Error => SolvedType::Error,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -371,6 +371,10 @@ fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt:
|
||||||
|
|
||||||
write!(f, "Alias({:?}, {:?}, {:?})", name, slice, actual)
|
write!(f, "Alias({:?}, {:?}, {:?})", name, slice, actual)
|
||||||
}
|
}
|
||||||
|
Content::RangedNumber(typ, range) => {
|
||||||
|
let slice = subs.get_subs_slice(*range);
|
||||||
|
write!(f, "RangedNumber({:?}, {:?})", typ, slice)
|
||||||
|
}
|
||||||
Content::Error => write!(f, "Error"),
|
Content::Error => write!(f, "Error"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -588,11 +592,6 @@ define_const_var! {
|
||||||
|
|
||||||
AT_NATURAL,
|
AT_NATURAL,
|
||||||
|
|
||||||
AT_BINARY32,
|
|
||||||
AT_BINARY64,
|
|
||||||
|
|
||||||
AT_DECIMAL,
|
|
||||||
|
|
||||||
// Signed8 : [ @Signed8 ]
|
// Signed8 : [ @Signed8 ]
|
||||||
:pub SIGNED8,
|
:pub SIGNED8,
|
||||||
:pub SIGNED16,
|
:pub SIGNED16,
|
||||||
|
@ -608,11 +607,6 @@ define_const_var! {
|
||||||
|
|
||||||
:pub NATURAL,
|
:pub NATURAL,
|
||||||
|
|
||||||
:pub BINARY32,
|
|
||||||
:pub BINARY64,
|
|
||||||
|
|
||||||
:pub DECIMAL,
|
|
||||||
|
|
||||||
// [ @Integer Signed8 ]
|
// [ @Integer Signed8 ]
|
||||||
AT_INTEGER_SIGNED8,
|
AT_INTEGER_SIGNED8,
|
||||||
AT_INTEGER_SIGNED16,
|
AT_INTEGER_SIGNED16,
|
||||||
|
@ -688,6 +682,36 @@ define_const_var! {
|
||||||
|
|
||||||
:pub NAT,
|
:pub NAT,
|
||||||
|
|
||||||
|
// [ @Binary32 ]
|
||||||
|
AT_BINARY32,
|
||||||
|
AT_BINARY64,
|
||||||
|
AT_DECIMAL,
|
||||||
|
|
||||||
|
// Binary32 : [ @Binary32 ]
|
||||||
|
BINARY32,
|
||||||
|
BINARY64,
|
||||||
|
DECIMAL,
|
||||||
|
|
||||||
|
// [ @Float Binary32 ]
|
||||||
|
AT_FLOAT_BINARY32,
|
||||||
|
AT_FLOAT_BINARY64,
|
||||||
|
AT_FLOAT_DECIMAL,
|
||||||
|
|
||||||
|
// Float Binary32 : [ @Float Binary32 ]
|
||||||
|
FLOAT_BINARY32,
|
||||||
|
FLOAT_BINARY64,
|
||||||
|
FLOAT_DECIMAL,
|
||||||
|
|
||||||
|
// [ @Num (Float Binary32) ]
|
||||||
|
AT_NUM_FLOAT_BINARY32,
|
||||||
|
AT_NUM_FLOAT_BINARY64,
|
||||||
|
AT_NUM_FLOAT_DECIMAL,
|
||||||
|
|
||||||
|
// Num (Float Binary32)
|
||||||
|
NUM_FLOAT_BINARY32,
|
||||||
|
NUM_FLOAT_BINARY64,
|
||||||
|
NUM_FLOAT_DECIMAL,
|
||||||
|
|
||||||
:pub F32,
|
:pub F32,
|
||||||
:pub F64,
|
:pub F64,
|
||||||
|
|
||||||
|
@ -1034,6 +1058,118 @@ fn define_integer_types(subs: &mut Subs) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn float_type(
|
||||||
|
subs: &mut Subs,
|
||||||
|
|
||||||
|
num_at_binary64: Symbol,
|
||||||
|
num_binary64: Symbol,
|
||||||
|
num_f64: Symbol,
|
||||||
|
|
||||||
|
at_binary64: Variable,
|
||||||
|
binary64: Variable,
|
||||||
|
|
||||||
|
at_float_binary64: Variable,
|
||||||
|
float_binary64: Variable,
|
||||||
|
|
||||||
|
at_num_float_binary64: Variable,
|
||||||
|
num_float_binary64: Variable,
|
||||||
|
|
||||||
|
var_f64: Variable,
|
||||||
|
) {
|
||||||
|
// define the type Binary64 (which is an alias for [ @Binary64 ])
|
||||||
|
{
|
||||||
|
let tags = UnionTags::insert_into_subs(subs, [(TagName::Private(num_at_binary64), [])]);
|
||||||
|
|
||||||
|
subs.set_content(at_binary64, {
|
||||||
|
Content::Structure(FlatType::TagUnion(tags, Variable::EMPTY_TAG_UNION))
|
||||||
|
});
|
||||||
|
|
||||||
|
subs.set_content(binary64, {
|
||||||
|
Content::Alias(num_binary64, AliasVariables::default(), at_binary64)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// define the type `Num.Float Num.Binary64`
|
||||||
|
{
|
||||||
|
let tags = UnionTags::insert_into_subs(
|
||||||
|
subs,
|
||||||
|
[(TagName::Private(Symbol::NUM_AT_FLOATINGPOINT), [binary64])],
|
||||||
|
);
|
||||||
|
subs.set_content(at_float_binary64, {
|
||||||
|
Content::Structure(FlatType::TagUnion(tags, Variable::EMPTY_TAG_UNION))
|
||||||
|
});
|
||||||
|
|
||||||
|
let vars = AliasVariables::insert_into_subs(subs, [binary64], []);
|
||||||
|
subs.set_content(float_binary64, {
|
||||||
|
Content::Alias(Symbol::NUM_FLOATINGPOINT, vars, at_binary64)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// define the type `F64: Num.Num (Num.Float Num.Binary64)`
|
||||||
|
{
|
||||||
|
let tags = UnionTags::insert_into_subs(
|
||||||
|
subs,
|
||||||
|
[(TagName::Private(Symbol::NUM_AT_NUM), [float_binary64])],
|
||||||
|
);
|
||||||
|
subs.set_content(at_num_float_binary64, {
|
||||||
|
Content::Structure(FlatType::TagUnion(tags, Variable::EMPTY_TAG_UNION))
|
||||||
|
});
|
||||||
|
|
||||||
|
let vars = AliasVariables::insert_into_subs(subs, [float_binary64], []);
|
||||||
|
subs.set_content(num_float_binary64, {
|
||||||
|
Content::Alias(Symbol::NUM_NUM, vars, at_num_float_binary64)
|
||||||
|
});
|
||||||
|
|
||||||
|
subs.set_content(var_f64, {
|
||||||
|
Content::Alias(num_f64, AliasVariables::default(), num_float_binary64)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn define_float_types(subs: &mut Subs) {
|
||||||
|
float_type(
|
||||||
|
subs,
|
||||||
|
Symbol::NUM_AT_BINARY32,
|
||||||
|
Symbol::NUM_BINARY32,
|
||||||
|
Symbol::NUM_F32,
|
||||||
|
Variable::AT_BINARY32,
|
||||||
|
Variable::BINARY32,
|
||||||
|
Variable::AT_FLOAT_BINARY32,
|
||||||
|
Variable::FLOAT_BINARY32,
|
||||||
|
Variable::AT_NUM_FLOAT_BINARY32,
|
||||||
|
Variable::NUM_FLOAT_BINARY32,
|
||||||
|
Variable::F32,
|
||||||
|
);
|
||||||
|
|
||||||
|
float_type(
|
||||||
|
subs,
|
||||||
|
Symbol::NUM_AT_BINARY64,
|
||||||
|
Symbol::NUM_BINARY64,
|
||||||
|
Symbol::NUM_F64,
|
||||||
|
Variable::AT_BINARY64,
|
||||||
|
Variable::BINARY64,
|
||||||
|
Variable::AT_FLOAT_BINARY64,
|
||||||
|
Variable::FLOAT_BINARY64,
|
||||||
|
Variable::AT_NUM_FLOAT_BINARY64,
|
||||||
|
Variable::NUM_FLOAT_BINARY64,
|
||||||
|
Variable::F64,
|
||||||
|
);
|
||||||
|
|
||||||
|
float_type(
|
||||||
|
subs,
|
||||||
|
Symbol::NUM_AT_DECIMAL,
|
||||||
|
Symbol::NUM_DECIMAL,
|
||||||
|
Symbol::NUM_DEC,
|
||||||
|
Variable::AT_DECIMAL,
|
||||||
|
Variable::DECIMAL,
|
||||||
|
Variable::AT_FLOAT_DECIMAL,
|
||||||
|
Variable::FLOAT_DECIMAL,
|
||||||
|
Variable::AT_NUM_FLOAT_DECIMAL,
|
||||||
|
Variable::NUM_FLOAT_DECIMAL,
|
||||||
|
Variable::DEC,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
impl Subs {
|
impl Subs {
|
||||||
pub const RESULT_TAG_NAMES: SubsSlice<TagName> = SubsSlice::new(0, 2);
|
pub const RESULT_TAG_NAMES: SubsSlice<TagName> = SubsSlice::new(0, 2);
|
||||||
|
|
||||||
|
@ -1072,6 +1208,7 @@ impl Subs {
|
||||||
}
|
}
|
||||||
|
|
||||||
define_integer_types(&mut subs);
|
define_integer_types(&mut subs);
|
||||||
|
define_float_types(&mut subs);
|
||||||
|
|
||||||
subs.set_content(
|
subs.set_content(
|
||||||
Variable::EMPTY_RECORD,
|
Variable::EMPTY_RECORD,
|
||||||
|
@ -1492,6 +1629,7 @@ pub enum Content {
|
||||||
},
|
},
|
||||||
Structure(FlatType),
|
Structure(FlatType),
|
||||||
Alias(Symbol, AliasVariables, Variable),
|
Alias(Symbol, AliasVariables, Variable),
|
||||||
|
RangedNumber(Variable, VariableSubsSlice),
|
||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2244,6 +2382,15 @@ fn occurs(
|
||||||
short_circuit_help(subs, root_var, &new_seen, var)?;
|
short_circuit_help(subs, root_var, &new_seen, var)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
RangedNumber(typ, _range_vars) => {
|
||||||
|
let mut new_seen = seen.clone();
|
||||||
|
new_seen.insert(root_var);
|
||||||
|
|
||||||
|
short_circuit_help(subs, root_var, &new_seen, *typ)?;
|
||||||
|
// _range_vars excluded because they are not explicitly part of the type.
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2433,6 +2580,19 @@ fn explicit_substitute(
|
||||||
|
|
||||||
subs.set_content(in_var, Alias(symbol, args, new_actual));
|
subs.set_content(in_var, Alias(symbol, args, new_actual));
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_typ = explicit_substitute(subs, from, to, typ, seen);
|
||||||
|
|
||||||
|
subs.set_content(in_var, RangedNumber(new_typ, vars));
|
||||||
|
|
||||||
in_var
|
in_var
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2484,6 +2644,13 @@ fn get_var_names(
|
||||||
get_var_names(subs, subs[arg_var], answer)
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
Structure(flat_type) => match flat_type {
|
Structure(flat_type) => match flat_type {
|
||||||
FlatType::Apply(_, args) => {
|
FlatType::Apply(_, args) => {
|
||||||
args.into_iter().fold(taken_names, |answer, arg_var| {
|
args.into_iter().fold(taken_names, |answer, arg_var| {
|
||||||
|
@ -2696,6 +2863,8 @@ fn content_to_err_type(
|
||||||
ErrorType::Alias(symbol, err_args, Box::new(err_type))
|
ErrorType::Alias(symbol, err_args, Box::new(err_type))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RangedNumber(typ, _) => var_to_err_type(subs, state, typ),
|
||||||
|
|
||||||
Error => ErrorType::Error,
|
Error => ErrorType::Error,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2974,6 +3143,11 @@ fn restore_help(subs: &mut Subs, initial: Variable) {
|
||||||
|
|
||||||
stack.push(*var);
|
stack.push(*var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RangedNumber(typ, vars) => {
|
||||||
|
stack.push(*typ);
|
||||||
|
stack.extend(var_slice(*vars));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3133,6 +3307,10 @@ impl StorageSubs {
|
||||||
Self::offset_alias_variables(offsets, *alias_variables),
|
Self::offset_alias_variables(offsets, *alias_variables),
|
||||||
Self::offset_variable(offsets, *actual),
|
Self::offset_variable(offsets, *actual),
|
||||||
),
|
),
|
||||||
|
RangedNumber(typ, vars) => RangedNumber(
|
||||||
|
Self::offset_variable(offsets, *typ),
|
||||||
|
Self::offset_variable_slice(offsets, *vars),
|
||||||
|
),
|
||||||
Error => Content::Error,
|
Error => Content::Error,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3525,6 +3703,23 @@ fn deep_copy_var_to_help<'a>(
|
||||||
|
|
||||||
copy
|
copy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RangedNumber(typ, vars) => {
|
||||||
|
let new_typ = deep_copy_var_to_help(arena, visited, source, target, max_rank, typ);
|
||||||
|
|
||||||
|
let new_vars = SubsSlice::reserve_into_subs(target, vars.len());
|
||||||
|
|
||||||
|
for (target_index, var_index) in (new_vars.indices()).zip(vars) {
|
||||||
|
let var = source[var_index];
|
||||||
|
let copy_var = deep_copy_var_to_help(arena, visited, source, target, max_rank, var);
|
||||||
|
target.variables[target_index] = copy_var;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_content = RangedNumber(new_typ, new_vars);
|
||||||
|
|
||||||
|
target.set(copy, make_descriptor(new_content));
|
||||||
|
copy
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3598,6 +3793,10 @@ where
|
||||||
push_var_slice!(arguments.variables());
|
push_var_slice!(arguments.variables());
|
||||||
stack.push(*real_type_var);
|
stack.push(*real_type_var);
|
||||||
}
|
}
|
||||||
|
RangedNumber(typ, vars) => {
|
||||||
|
stack.push(*typ);
|
||||||
|
push_var_slice!(*vars);
|
||||||
|
}
|
||||||
Error => {}
|
Error => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,6 +196,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>, Vec<Variable>),
|
||||||
/// 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),
|
||||||
}
|
}
|
||||||
|
@ -439,6 +440,9 @@ impl fmt::Debug for Type {
|
||||||
|
|
||||||
write!(f, " as <{:?}>", rec)
|
write!(f, " as <{:?}>", rec)
|
||||||
}
|
}
|
||||||
|
Type::RangedNumber(typ, range_vars) => {
|
||||||
|
write!(f, "Ranged({:?}, {:?})", typ, range_vars)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -549,6 +553,9 @@ impl Type {
|
||||||
arg.substitute(substitutions);
|
arg.substitute(substitutions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RangedNumber(typ, _) => {
|
||||||
|
typ.substitute(substitutions);
|
||||||
|
}
|
||||||
|
|
||||||
EmptyRec | EmptyTagUnion | Erroneous(_) => {}
|
EmptyRec | EmptyTagUnion | Erroneous(_) => {}
|
||||||
}
|
}
|
||||||
|
@ -616,6 +623,7 @@ impl Type {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
RangedNumber(typ, _) => typ.substitute_alias(rep_symbol, rep_args, actual),
|
||||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => Ok(()),
|
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -653,6 +661,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),
|
||||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => false,
|
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -689,6 +698,9 @@ 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, vars) => {
|
||||||
|
typ.contains_variable(rep_variable) || vars.iter().any(|&v| v == rep_variable)
|
||||||
|
}
|
||||||
EmptyRec | EmptyTagUnion | Erroneous(_) => false,
|
EmptyRec | EmptyTagUnion | Erroneous(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -845,6 +857,9 @@ impl Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RangedNumber(typ, _) => {
|
||||||
|
typ.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
|
}
|
||||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {}
|
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -901,6 +916,9 @@ fn symbols_help(tipe: &Type, accum: &mut ImSet<Symbol>) {
|
||||||
Erroneous(Problem::CyclicAlias(alias, _, _)) => {
|
Erroneous(Problem::CyclicAlias(alias, _, _)) => {
|
||||||
accum.insert(*alias);
|
accum.insert(*alias);
|
||||||
}
|
}
|
||||||
|
RangedNumber(typ, _) => {
|
||||||
|
symbols_help(typ, accum);
|
||||||
|
}
|
||||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {}
|
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -979,6 +997,10 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
||||||
}
|
}
|
||||||
variables_help(actual, accum);
|
variables_help(actual, accum);
|
||||||
}
|
}
|
||||||
|
RangedNumber(typ, vars) => {
|
||||||
|
variables_help(typ, accum);
|
||||||
|
accum.extend(vars.iter().copied());
|
||||||
|
}
|
||||||
Apply(_, args, _) => {
|
Apply(_, args, _) => {
|
||||||
for x in args {
|
for x in args {
|
||||||
variables_help(x, accum);
|
variables_help(x, accum);
|
||||||
|
@ -1083,6 +1105,10 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
|
||||||
}
|
}
|
||||||
variables_help_detailed(actual, accum);
|
variables_help_detailed(actual, accum);
|
||||||
}
|
}
|
||||||
|
RangedNumber(typ, vars) => {
|
||||||
|
variables_help_detailed(typ, accum);
|
||||||
|
accum.type_variables.extend(vars);
|
||||||
|
}
|
||||||
Apply(_, args, _) => {
|
Apply(_, args, _) => {
|
||||||
for x in args {
|
for x in args {
|
||||||
variables_help_detailed(x, accum);
|
variables_help_detailed(x, accum);
|
||||||
|
@ -1288,6 +1314,7 @@ pub enum Mismatch {
|
||||||
InconsistentIfElse,
|
InconsistentIfElse,
|
||||||
InconsistentWhenBranches,
|
InconsistentWhenBranches,
|
||||||
CanonicalizationProblem,
|
CanonicalizationProblem,
|
||||||
|
TypeNotInRange(Variable, Vec<Variable>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Hash)]
|
#[derive(PartialEq, Eq, Clone, Hash)]
|
||||||
|
|
|
@ -83,6 +83,7 @@ pub enum Unified {
|
||||||
Success(Pool),
|
Success(Pool),
|
||||||
Failure(Pool, ErrorType, ErrorType),
|
Failure(Pool, ErrorType, ErrorType),
|
||||||
BadType(Pool, roc_types::types::Problem),
|
BadType(Pool, roc_types::types::Problem),
|
||||||
|
NotInRange(Pool, ErrorType, Vec<ErrorType>),
|
||||||
}
|
}
|
||||||
|
|
||||||
type Outcome = Vec<Mismatch>;
|
type Outcome = Vec<Mismatch>;
|
||||||
|
@ -94,6 +95,13 @@ pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable, mode: Mode) -> Uni
|
||||||
|
|
||||||
if mismatches.is_empty() {
|
if mismatches.is_empty() {
|
||||||
Unified::Success(vars)
|
Unified::Success(vars)
|
||||||
|
} else if let Some((typ, range)) = mismatches.iter().find_map(|mis| match mis {
|
||||||
|
Mismatch::TypeNotInRange(typ, range) => Some((typ, range)),
|
||||||
|
_ => None,
|
||||||
|
}) {
|
||||||
|
let (target_type, _) = subs.var_to_error_type(*typ);
|
||||||
|
let range_types = range.iter().map(|&v| subs.var_to_error_type(v).0).collect();
|
||||||
|
Unified::NotInRange(vars, target_type, range_types)
|
||||||
} else {
|
} else {
|
||||||
let (type1, mut problems) = subs.var_to_error_type(var1);
|
let (type1, mut problems) = subs.var_to_error_type(var1);
|
||||||
let (type2, problems2) = subs.var_to_error_type(var2);
|
let (type2, problems2) = subs.var_to_error_type(var2);
|
||||||
|
@ -175,6 +183,7 @@ fn unify_context(subs: &mut Subs, pool: &mut Pool, ctx: Context) -> Outcome {
|
||||||
unify_structure(subs, pool, &ctx, flat_type, &ctx.second_desc.content)
|
unify_structure(subs, pool, &ctx, flat_type, &ctx.second_desc.content)
|
||||||
}
|
}
|
||||||
Alias(symbol, args, real_var) => unify_alias(subs, pool, &ctx, *symbol, *args, *real_var),
|
Alias(symbol, args, real_var) => unify_alias(subs, pool, &ctx, *symbol, *args, *real_var),
|
||||||
|
&RangedNumber(typ, range_vars) => unify_ranged_number(subs, pool, &ctx, typ, 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)
|
||||||
|
@ -182,6 +191,73 @@ fn unify_context(subs: &mut Subs, pool: &mut Pool, ctx: Context) -> Outcome {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn unify_ranged_number(
|
||||||
|
subs: &mut Subs,
|
||||||
|
pool: &mut Pool,
|
||||||
|
ctx: &Context,
|
||||||
|
real_var: Variable,
|
||||||
|
range_vars: VariableSubsSlice,
|
||||||
|
) -> Outcome {
|
||||||
|
let other_content = &ctx.second_desc.content;
|
||||||
|
|
||||||
|
let outcome = match other_content {
|
||||||
|
FlexVar(_) => {
|
||||||
|
// Ranged number wins
|
||||||
|
merge(subs, ctx, RangedNumber(real_var, range_vars))
|
||||||
|
}
|
||||||
|
RecursionVar { .. } | RigidVar(..) | Alias(..) | Structure(..) => {
|
||||||
|
unify_pool(subs, pool, real_var, ctx.second, ctx.mode)
|
||||||
|
}
|
||||||
|
&RangedNumber(other_real_var, _other_range_vars) => {
|
||||||
|
unify_pool(subs, pool, real_var, other_real_var, ctx.mode)
|
||||||
|
// TODO: check and intersect "other_range_vars"
|
||||||
|
}
|
||||||
|
Error => merge(subs, ctx, Error),
|
||||||
|
};
|
||||||
|
|
||||||
|
if !outcome.is_empty() {
|
||||||
|
return outcome;
|
||||||
|
}
|
||||||
|
|
||||||
|
check_valid_range(subs, pool, ctx.second, range_vars, ctx.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_valid_range(
|
||||||
|
subs: &mut Subs,
|
||||||
|
pool: &mut Pool,
|
||||||
|
var: Variable,
|
||||||
|
range: VariableSubsSlice,
|
||||||
|
mode: Mode,
|
||||||
|
) -> Outcome {
|
||||||
|
let slice = subs
|
||||||
|
.get_subs_slice(range)
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
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);
|
||||||
|
if outcome.is_empty() {
|
||||||
|
// Okay, we matched some type in the range.
|
||||||
|
subs.rollback_to(snapshot);
|
||||||
|
*pool = old_pool;
|
||||||
|
return vec![];
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vec![Mismatch::TypeNotInRange(var, slice)];
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unify_alias(
|
fn unify_alias(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
|
@ -231,6 +307,9 @@ fn unify_alias(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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, _) => {
|
||||||
|
unify_pool(subs, pool, real_var, *other_real_var, ctx.mode)
|
||||||
|
}
|
||||||
Error => merge(subs, ctx, Error),
|
Error => merge(subs, ctx, Error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -302,6 +381,7 @@ fn unify_structure(
|
||||||
// can't quite figure out why, but it doesn't seem to impact other types.
|
// can't quite figure out why, but it doesn't seem to impact other types.
|
||||||
unify_pool(subs, pool, ctx.first, *real_var, Mode::Eq)
|
unify_pool(subs, pool, ctx.first, *real_var, Mode::Eq)
|
||||||
}
|
}
|
||||||
|
RangedNumber(real_var, _) => unify_pool(subs, pool, ctx.first, *real_var, ctx.mode),
|
||||||
Error => merge(subs, ctx, Error),
|
Error => merge(subs, ctx, Error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -829,6 +909,7 @@ fn unify_tag_union_new(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
enum OtherTags2 {
|
enum OtherTags2 {
|
||||||
Empty,
|
Empty,
|
||||||
Union(
|
Union(
|
||||||
|
@ -1222,7 +1303,7 @@ fn unify_rigid(subs: &mut Subs, ctx: &Context, name: &Lowercase, other: &Content
|
||||||
// If the other is flex, rigid wins!
|
// If the other is flex, rigid wins!
|
||||||
merge(subs, ctx, RigidVar(name.clone()))
|
merge(subs, ctx, RigidVar(name.clone()))
|
||||||
}
|
}
|
||||||
RigidVar(_) | RecursionVar { .. } | Structure(_) | Alias(_, _, _) => {
|
RigidVar(_) | RecursionVar { .. } | Structure(_) | Alias(_, _, _) | RangedNumber(..) => {
|
||||||
// Type mismatch! Rigid can only unify with flex, even if the
|
// Type mismatch! Rigid can only unify with flex, even if the
|
||||||
// rigid names are the same.
|
// rigid names are the same.
|
||||||
mismatch!("Rigid {:?} with {:?}", ctx.first, &other)
|
mismatch!("Rigid {:?} with {:?}", ctx.first, &other)
|
||||||
|
@ -1247,7 +1328,12 @@ fn unify_flex(
|
||||||
merge(subs, ctx, FlexVar(opt_name.clone()))
|
merge(subs, ctx, FlexVar(opt_name.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
FlexVar(Some(_)) | RigidVar(_) | RecursionVar { .. } | Structure(_) | Alias(_, _, _) => {
|
FlexVar(Some(_))
|
||||||
|
| RigidVar(_)
|
||||||
|
| RecursionVar { .. }
|
||||||
|
| Structure(_)
|
||||||
|
| Alias(_, _, _)
|
||||||
|
| RangedNumber(..) => {
|
||||||
// TODO special-case boolean here
|
// TODO special-case boolean here
|
||||||
// In all other cases, if left is flex, defer to right.
|
// In all other cases, if left is flex, defer to right.
|
||||||
// (This includes using right's name if both are flex and named.)
|
// (This includes using right's name if both are flex and named.)
|
||||||
|
@ -1306,6 +1392,12 @@ fn unify_recursion(
|
||||||
unify_pool(subs, pool, ctx.first, *actual, ctx.mode)
|
unify_pool(subs, pool, ctx.first, *actual, ctx.mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RangedNumber(..) => mismatch!(
|
||||||
|
"RecursionVar {:?} with ranged number {:?}",
|
||||||
|
ctx.first,
|
||||||
|
&other
|
||||||
|
),
|
||||||
|
|
||||||
Error => merge(subs, ctx, Error),
|
Error => merge(subs, ctx, Error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ use ven_pretty::DocAllocator;
|
||||||
|
|
||||||
const DUPLICATE_NAME: &str = "DUPLICATE NAME";
|
const DUPLICATE_NAME: &str = "DUPLICATE NAME";
|
||||||
const ADD_ANNOTATIONS: &str = r#"Can more type annotations be added? Type annotations always help me give more specific messages, and I think they could help a lot in this case"#;
|
const ADD_ANNOTATIONS: &str = r#"Can more type annotations be added? Type annotations always help me give more specific messages, and I think they could help a lot in this case"#;
|
||||||
|
const TYPE_NOT_IN_RANGE: &str = r#"TYPE NOT IN RANGE"#;
|
||||||
|
|
||||||
pub fn type_problem<'b>(
|
pub fn type_problem<'b>(
|
||||||
alloc: &'b RocDocAllocator<'b>,
|
alloc: &'b RocDocAllocator<'b>,
|
||||||
|
@ -59,6 +60,31 @@ pub fn type_problem<'b>(
|
||||||
|
|
||||||
report(title, doc, filename)
|
report(title, doc, filename)
|
||||||
}
|
}
|
||||||
|
NotInRange(region, found, expected_range) => {
|
||||||
|
let mut range_choices = vec![alloc.reflow("It can only be used as a ")];
|
||||||
|
let range = expected_range.get_type();
|
||||||
|
let last = range.len() - 1;
|
||||||
|
for (i, choice) in range.into_iter().enumerate() {
|
||||||
|
if i == last && i == 1 {
|
||||||
|
range_choices.push(alloc.text(" or "));
|
||||||
|
} else if i == last && i > 1 {
|
||||||
|
range_choices.push(alloc.text(", or "));
|
||||||
|
} else if i > 1 {
|
||||||
|
range_choices.push(alloc.text(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
range_choices.push(to_doc(alloc, Parens::Unnecessary, choice));
|
||||||
|
}
|
||||||
|
let doc = alloc.stack(vec![
|
||||||
|
alloc.reflow("This expression is used in an unexpected way:"),
|
||||||
|
alloc.region(lines.convert_region(region)),
|
||||||
|
alloc.concat(range_choices),
|
||||||
|
alloc.text("But it is being used as:"),
|
||||||
|
to_doc(alloc, Parens::Unnecessary, found),
|
||||||
|
]);
|
||||||
|
|
||||||
|
report(TYPE_NOT_IN_RANGE.into(), doc, filename)
|
||||||
|
}
|
||||||
BadType(type_problem) => {
|
BadType(type_problem) => {
|
||||||
use roc_types::types::Problem::*;
|
use roc_types::types::Problem::*;
|
||||||
match type_problem {
|
match type_problem {
|
||||||
|
|
|
@ -1221,6 +1221,7 @@ mod test_reporting {
|
||||||
x
|
x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
// TODO FIXME the second error message is incomplete, should be removed
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
── TYPE MISMATCH ───────────────────────────────────────────────────────────────
|
── TYPE MISMATCH ───────────────────────────────────────────────────────────────
|
||||||
|
@ -1241,6 +1242,20 @@ mod test_reporting {
|
||||||
|
|
||||||
Tip: You can convert between Int and Float using functions like
|
Tip: You can convert between Int and Float using functions like
|
||||||
`Num.toFloat` and `Num.round`.
|
`Num.toFloat` and `Num.round`.
|
||||||
|
|
||||||
|
── TYPE NOT IN RANGE ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
This expression is used in an unexpected way:
|
||||||
|
|
||||||
|
2│ x = if True then 3.14 else 4
|
||||||
|
^
|
||||||
|
|
||||||
|
It can only be used as a
|
||||||
|
`I8``U8`, `I16`, `U16`, `I32`, `U32`, `I64`, `Nat`, `U64`, `I128`, or `U128`
|
||||||
|
|
||||||
|
But it is being used as:
|
||||||
|
|
||||||
|
`Int` `*`
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -3386,7 +3401,9 @@ mod test_reporting {
|
||||||
minlit = -170_141_183_460_469_231_731_687_303_715_884_105_728
|
minlit = -170_141_183_460_469_231_731_687_303_715_884_105_728
|
||||||
maxlit = 340_282_366_920_938_463_463_374_607_431_768_211_455
|
maxlit = 340_282_366_920_938_463_463_374_607_431_768_211_455
|
||||||
|
|
||||||
x + y + h + l + minlit + maxlit
|
getI128 = \_ -> 1i128
|
||||||
|
|
||||||
|
x + y + h + l + minlit + (getI128 maxlit)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -7882,18 +7899,26 @@ I need all branches in an `if` to have the same type!
|
||||||
#[test]
|
#[test]
|
||||||
fn list_get_negative_number() {
|
fn list_get_negative_number() {
|
||||||
report_problem_as(
|
report_problem_as(
|
||||||
"List.get [1, 2, 3] -1",
|
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────────────────────────────
|
a = -9_223_372_036_854
|
||||||
|
List.get [1,2,3] a
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── TYPE NOT IN RANGE ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
This integer literal overflows the type indicated by its suffix:
|
This expression is used in an unexpected way:
|
||||||
|
|
||||||
1│ 170_141_183_460_469_231_731_687_303_715_884_105_728i128
|
2│ List.get [1,2,3] a
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^
|
||||||
|
|
||||||
Tip: The suffix indicates this integer is a I128, whose maximum value
|
It can only be used as a `I64` or `I128`
|
||||||
is 170_141_183_460_469_231_731_687_303_715_884_105_727.
|
|
||||||
|
But it is being used as:
|
||||||
|
|
||||||
|
`Nat`
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue