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>),
|
||||
And(Vec<Constraint>),
|
||||
Present(Type, PresenceConstraint),
|
||||
|
||||
/// `EqBoundedRange(Ts, U, ...)` means there must be at least one `T` in the *ordered* range `Ts`
|
||||
/// that unifies (via `Eq`) with `U`.
|
||||
///
|
||||
/// This is only used for integers, where we may see e.g. the number literal `-1` and know it
|
||||
/// has the bounded range `[I8, I16, I32, I64, I128]`, at least one of which must unify with
|
||||
/// the type the number literal is used as.
|
||||
EqBoundedRange(Type, Expected<Vec<Type>>, Category, Region),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
@ -95,7 +87,6 @@ impl Constraint {
|
|||
}
|
||||
Constraint::And(cs) => cs.iter().any(|c| c.contains_save_the_environment()),
|
||||
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 {
|
||||
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),
|
||||
};
|
||||
|
||||
|
|
|
@ -11,38 +11,31 @@ use roc_types::types::Category;
|
|||
use roc_types::types::Reason;
|
||||
use roc_types::types::Type::{self, *};
|
||||
|
||||
#[must_use]
|
||||
pub fn add_numeric_bound_constr(
|
||||
constrs: &mut Vec<Constraint>,
|
||||
num_type: Type,
|
||||
bound: impl TypedNumericBound,
|
||||
region: Region,
|
||||
category: Category,
|
||||
) {
|
||||
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,
|
||||
) {
|
||||
) -> Type {
|
||||
let range = bound.bounded_range();
|
||||
if !range.is_empty() {
|
||||
constrs.push(EqBoundedRange(
|
||||
num_type,
|
||||
Expected::ForReason(Reason::NumericLiteralSuffix, range, region),
|
||||
|
||||
let total_num_type = num_type;
|
||||
|
||||
match range.len() {
|
||||
0 => total_num_type,
|
||||
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,
|
||||
bound: IntBound,
|
||||
) -> Constraint {
|
||||
let num_type = Variable(num_var);
|
||||
let reason = Reason::IntLiteral;
|
||||
|
||||
let mut constrs = Vec::with_capacity(3);
|
||||
// Always add the bound first; this improves the resolved type quality in case it's an alias
|
||||
// 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![
|
||||
Eq(
|
||||
num_type.clone(),
|
||||
|
@ -70,13 +68,6 @@ pub fn int_literal(
|
|||
),
|
||||
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))
|
||||
}
|
||||
|
@ -89,13 +80,12 @@ pub fn float_literal(
|
|||
region: Region,
|
||||
bound: FloatBound,
|
||||
) -> Constraint {
|
||||
let num_type = Variable(num_var);
|
||||
let reason = Reason::FloatLiteral;
|
||||
|
||||
let mut constrs = Vec::with_capacity(3);
|
||||
add_numeric_bound_constr(
|
||||
let num_type = add_numeric_bound_constr(
|
||||
&mut constrs,
|
||||
num_type.clone(),
|
||||
Variable(num_var),
|
||||
bound,
|
||||
region,
|
||||
Category::Float,
|
||||
|
@ -120,10 +110,11 @@ pub fn num_literal(
|
|||
region: Region,
|
||||
bound: NumericBound,
|
||||
) -> 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);
|
||||
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)]);
|
||||
|
||||
exists(vec![num_var], And(constrs))
|
||||
|
@ -219,56 +210,56 @@ pub fn num_int(range: Type) -> Type {
|
|||
)
|
||||
}
|
||||
|
||||
macro_rules! num_types {
|
||||
// Represent
|
||||
// num_u8 ~ U8 : Num Integer Unsigned8 = @Num (@Integer (@Unsigned8))
|
||||
// int_u8 ~ Integer Unsigned8 = @Integer (@Unsigned8)
|
||||
// macro_rules! num_types {
|
||||
// // Represent
|
||||
// // num_u8 ~ U8 : Num Integer Unsigned8 = @Num (@Integer (@Unsigned8))
|
||||
// // int_u8 ~ Integer Unsigned8 = @Integer (@Unsigned8)
|
||||
// //
|
||||
// // num_f32 ~ F32 : Num FloaingPoint Binary32 = @Num (@FloaingPoint (@Binary32))
|
||||
// // float_f32 ~ FloatingPoint Binary32 = @FloatingPoint (@Binary32)
|
||||
// // 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)*) => {
|
||||
// $(
|
||||
// #[inline(always)]
|
||||
// fn $sub_fn() -> Type {
|
||||
// builtin_alias(
|
||||
// $inner_alias,
|
||||
// vec![],
|
||||
// Box::new(Type::TagUnion(
|
||||
// vec![(TagName::Private($inner_private_tag), vec![])],
|
||||
// Box::new(Type::EmptyTagUnion)
|
||||
// )),
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// num_f32 ~ F32 : Num FloaingPoint Binary32 = @Num (@FloaingPoint (@Binary32))
|
||||
// float_f32 ~ FloatingPoint Binary32 = @FloatingPoint (@Binary32)
|
||||
// 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)*) => {
|
||||
$(
|
||||
#[inline(always)]
|
||||
fn $sub_fn() -> Type {
|
||||
builtin_alias(
|
||||
$inner_alias,
|
||||
vec![],
|
||||
Box::new(Type::TagUnion(
|
||||
vec![(TagName::Private($inner_private_tag), vec![])],
|
||||
Box::new(Type::EmptyTagUnion)
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn $num_fn() -> Type {
|
||||
builtin_alias(
|
||||
$alias,
|
||||
vec![],
|
||||
Box::new($num_type($sub_fn()))
|
||||
)
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
num_types! {
|
||||
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_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_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_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_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_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_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
|
||||
}
|
||||
// #[inline(always)]
|
||||
// fn $num_fn() -> Type {
|
||||
// builtin_alias(
|
||||
// $alias,
|
||||
// vec![],
|
||||
// Box::new($num_type($sub_fn()))
|
||||
// )
|
||||
// }
|
||||
// )*
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// num_types! {
|
||||
// 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_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_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_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_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_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_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
|
||||
// }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn num_signed64() -> Type {
|
||||
|
@ -312,37 +303,26 @@ pub fn num_num(typ: Type) -> Type {
|
|||
}
|
||||
|
||||
pub trait TypedNumericBound {
|
||||
/// Get a concrete type for this number, if one exists.
|
||||
/// Returns `None` e.g. if the bound is open, like `Int *`.
|
||||
fn concrete_num_type(&self) -> Option<Type>;
|
||||
|
||||
fn bounded_range(&self) -> Vec<Type>;
|
||||
fn bounded_range(&self) -> Vec<Variable>;
|
||||
}
|
||||
|
||||
impl TypedNumericBound for IntBound {
|
||||
fn concrete_num_type(&self) -> Option<Type> {
|
||||
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> {
|
||||
fn bounded_range(&self) -> Vec<Variable> {
|
||||
match self {
|
||||
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 } => {
|
||||
let whole_range: &[(IntWidth, Variable)] = match sign {
|
||||
SignDemand::NoDemand => {
|
||||
|
@ -371,7 +351,7 @@ impl TypedNumericBound for IntBound {
|
|||
whole_range
|
||||
.iter()
|
||||
.skip_while(|(lower_bound, _)| *lower_bound != *width)
|
||||
.map(|(_, var)| Type::Variable(*var))
|
||||
.map(|(_, var)| *var)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
@ -379,35 +359,20 @@ impl TypedNumericBound for IntBound {
|
|||
}
|
||||
|
||||
impl TypedNumericBound for FloatBound {
|
||||
fn concrete_num_type(&self) -> Option<Type> {
|
||||
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> {
|
||||
fn bounded_range(&self) -> Vec<Variable> {
|
||||
match self {
|
||||
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 {
|
||||
fn concrete_num_type(&self) -> Option<Type> {
|
||||
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> {
|
||||
fn bounded_range(&self) -> Vec<Variable> {
|
||||
match self {
|
||||
NumericBound::None => vec![],
|
||||
NumericBound::Int(ib) => ib.bounded_range(),
|
||||
|
|
|
@ -183,7 +183,7 @@ pub fn constrain_pattern(
|
|||
|
||||
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,
|
||||
num_type.clone(),
|
||||
bound,
|
||||
|
@ -202,7 +202,7 @@ pub fn constrain_pattern(
|
|||
&IntLiteral(num_var, precision_var, _, _, bound) => {
|
||||
// First constraint on the free num var; this improves the resolved type quality in
|
||||
// case the bound is an alias.
|
||||
builtins::add_numeric_bound_constr(
|
||||
let num_type = builtins::add_numeric_bound_constr(
|
||||
&mut state.constraints,
|
||||
Type::Variable(num_var),
|
||||
bound,
|
||||
|
@ -214,7 +214,7 @@ pub fn constrain_pattern(
|
|||
let int_type = builtins::num_int(Type::Variable(precision_var));
|
||||
|
||||
state.constraints.push(Constraint::Eq(
|
||||
Type::Variable(num_var),
|
||||
num_type, // TODO check me if something breaks!
|
||||
Expected::NoExpectation(int_type),
|
||||
Category::Int,
|
||||
region,
|
||||
|
@ -232,7 +232,7 @@ pub fn constrain_pattern(
|
|||
&FloatLiteral(num_var, precision_var, _, _, bound) => {
|
||||
// First constraint on the free num var; this improves the resolved type quality in
|
||||
// case the bound is an alias.
|
||||
builtins::add_numeric_bound_constr(
|
||||
let num_type = builtins::add_numeric_bound_constr(
|
||||
&mut state.constraints,
|
||||
Type::Variable(num_var),
|
||||
bound,
|
||||
|
@ -244,7 +244,7 @@ pub fn constrain_pattern(
|
|||
let float_type = builtins::num_float(Type::Variable(precision_var));
|
||||
|
||||
state.constraints.push(Constraint::Eq(
|
||||
Type::Variable(num_var),
|
||||
num_type.clone(), // TODO check me if something breaks!
|
||||
Expected::NoExpectation(float_type),
|
||||
Category::Float,
|
||||
region,
|
||||
|
@ -254,7 +254,7 @@ pub fn constrain_pattern(
|
|||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Float,
|
||||
Type::Variable(num_var),
|
||||
num_type, // TODO check me if something breaks!
|
||||
expected,
|
||||
));
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ impl<'a> RawFunctionLayout<'a> {
|
|||
Self::new_help(env, structure, structure_content.clone())
|
||||
}
|
||||
Structure(flat_type) => Self::layout_from_flat_type(env, flat_type),
|
||||
RangedNumber(typ, _) => Self::from_var(env, typ),
|
||||
|
||||
// Ints
|
||||
Alias(Symbol::NUM_I128, args, _) => {
|
||||
|
@ -902,6 +903,8 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
RangedNumber(typ, _) => Self::from_var(env, typ),
|
||||
|
||||
Error => Err(LayoutProblem::Erroneous),
|
||||
}
|
||||
}
|
||||
|
@ -2562,7 +2565,7 @@ fn layout_from_num_content<'a>(
|
|||
Alias(_, _, _) => {
|
||||
todo!("TODO recursively resolve type aliases in num_from_content");
|
||||
}
|
||||
Structure(_) => {
|
||||
Structure(_) | RangedNumber(..) => {
|
||||
panic!("Invalid Num.Num type application: {:?}", content);
|
||||
}
|
||||
Error => Err(LayoutProblem::Erroneous),
|
||||
|
|
|
@ -142,6 +142,7 @@ impl FunctionLayout {
|
|||
Content::RecursionVar { .. } => Err(TypeError(())),
|
||||
Content::Structure(flat_type) => Self::from_flat_type(layouts, subs, flat_type),
|
||||
Content::Alias(_, _, actual) => Self::from_var_help(layouts, subs, *actual),
|
||||
Content::RangedNumber(actual, _) => Self::from_var_help(layouts, subs, *actual),
|
||||
Content::Error => Err(TypeError(())),
|
||||
}
|
||||
}
|
||||
|
@ -249,6 +250,7 @@ impl LambdaSet {
|
|||
}
|
||||
Content::Structure(flat_type) => Self::from_flat_type(layouts, subs, flat_type),
|
||||
Content::Alias(_, _, actual) => Self::from_var_help(layouts, subs, *actual),
|
||||
Content::RangedNumber(actual, _) => Self::from_var_help(layouts, subs, *actual),
|
||||
Content::Error => Err(TypeError(())),
|
||||
}
|
||||
}
|
||||
|
@ -682,6 +684,7 @@ impl Layout {
|
|||
}
|
||||
}
|
||||
}
|
||||
Content::RangedNumber(typ, _) => Self::from_var_help(layouts, subs, *typ),
|
||||
Content::Error => Err(TypeError(())),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,6 +72,7 @@ pub enum TypeError {
|
|||
CircularType(Region, Symbol, ErrorType),
|
||||
BadType(roc_types::types::Problem),
|
||||
UnexposedLookup(Symbol),
|
||||
NotInRange(Region, ErrorType, Expected<Vec<ErrorType>>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
@ -231,6 +232,17 @@ fn solve(
|
|||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -254,7 +266,7 @@ fn solve(
|
|||
|
||||
state
|
||||
}
|
||||
BadType(vars, _problem) => {
|
||||
BadType(vars, _) | NotInRange(vars, _, _) => {
|
||||
introduce(subs, rank, pools, &vars);
|
||||
|
||||
// ERROR NOT REPORTED
|
||||
|
@ -321,6 +333,17 @@ fn solve(
|
|||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -391,6 +414,18 @@ fn solve(
|
|||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -687,53 +722,19 @@ fn solve(
|
|||
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
EqBoundedRange(typ, expect_one_of, category, region) => {
|
||||
let actual = type_to_var(subs, rank, pools, cached_aliases, typ);
|
||||
|
||||
let mut it = expect_one_of.get_type_ref().iter().peekable();
|
||||
|
||||
while let Some(expected) = it.next() {
|
||||
let expected = type_to_var(subs, rank, pools, cached_aliases, expected);
|
||||
let snapshot = subs.snapshot();
|
||||
match unify(subs, actual, expected, Mode::Eq) {
|
||||
Success(vars) => {
|
||||
NotInRange(vars, typ, range) => {
|
||||
introduce(subs, rank, pools, &vars);
|
||||
|
||||
return state;
|
||||
}
|
||||
Failure(..) if it.peek().is_some() => {
|
||||
subs.rollback_to(snapshot);
|
||||
problems.push(TypeError::NotInRange(
|
||||
Region::zero(),
|
||||
typ,
|
||||
Expected::NoExpectation(range),
|
||||
));
|
||||
|
||||
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;
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -816,6 +817,13 @@ fn type_to_variable<'a>(
|
|||
|
||||
match typ {
|
||||
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, _) => {
|
||||
let new_arguments = VariableSubsSlice::reserve_into_subs(subs, arguments.len());
|
||||
for (target_index, var_index) in (new_arguments.indices()).zip(arguments) {
|
||||
|
@ -1636,6 +1644,8 @@ fn adjust_rank_content(
|
|||
|
||||
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);
|
||||
}
|
||||
&RangedNumber(typ, vars) => {
|
||||
stack.push(typ);
|
||||
|
||||
stack.extend(var_slice!(vars));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2027,6 +2042,23 @@ fn deep_copy_var_help(
|
|||
|
||||
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]
|
||||
fn int_literal() {
|
||||
infer_eq("5", "Num *");
|
||||
infer_eq("5", "Int *");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -334,7 +334,7 @@ mod solve_expr {
|
|||
[42]
|
||||
"#
|
||||
),
|
||||
"List (Num *)",
|
||||
"List (Int *)",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -346,7 +346,7 @@ mod solve_expr {
|
|||
[[[ 5 ]]]
|
||||
"#
|
||||
),
|
||||
"List (List (List (Num *)))",
|
||||
"List (List (List (Int *)))",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -358,7 +358,7 @@ mod solve_expr {
|
|||
[ 1, 2, 3 ]
|
||||
"#
|
||||
),
|
||||
"List (Num *)",
|
||||
"List (Int *)",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -370,7 +370,7 @@ mod solve_expr {
|
|||
[ [ 1 ], [ 2, 3 ] ]
|
||||
"#
|
||||
),
|
||||
"List (List (Num *))",
|
||||
"List (List (Int *))",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -518,7 +518,7 @@ mod solve_expr {
|
|||
\_, _ -> 42
|
||||
"#
|
||||
),
|
||||
"*, * -> Num *",
|
||||
"*, * -> Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -689,7 +689,7 @@ mod solve_expr {
|
|||
func
|
||||
"#
|
||||
),
|
||||
"*, * -> Num *",
|
||||
"*, * -> Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -753,7 +753,7 @@ mod solve_expr {
|
|||
c
|
||||
"#
|
||||
),
|
||||
"Num *",
|
||||
"Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -788,7 +788,7 @@ mod solve_expr {
|
|||
alwaysFive "stuff"
|
||||
"#
|
||||
),
|
||||
"Num *",
|
||||
"Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -835,7 +835,7 @@ mod solve_expr {
|
|||
x
|
||||
"#
|
||||
),
|
||||
"Num *",
|
||||
"Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -849,7 +849,7 @@ mod solve_expr {
|
|||
enlist 5
|
||||
"#
|
||||
),
|
||||
"List (Num *)",
|
||||
"List (Int *)",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -876,7 +876,7 @@ mod solve_expr {
|
|||
1 |> (\a -> a)
|
||||
"#
|
||||
),
|
||||
"Num *",
|
||||
"Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -890,7 +890,7 @@ mod solve_expr {
|
|||
1 |> always2 "foo"
|
||||
"#
|
||||
),
|
||||
"Num *",
|
||||
"Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -955,7 +955,7 @@ mod solve_expr {
|
|||
apply identity 5
|
||||
"#
|
||||
),
|
||||
"Num *",
|
||||
"Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -984,7 +984,7 @@ mod solve_expr {
|
|||
// flip neverendingInt
|
||||
// "#
|
||||
// ),
|
||||
// "(Num *, (a -> a)) -> Num *",
|
||||
// "(Int *, (a -> a)) -> Int *",
|
||||
// );
|
||||
// }
|
||||
|
||||
|
@ -1058,7 +1058,7 @@ mod solve_expr {
|
|||
// 1 // 2
|
||||
// "#
|
||||
// ),
|
||||
// "Num *",
|
||||
// "Int *",
|
||||
// );
|
||||
// }
|
||||
|
||||
|
@ -1070,7 +1070,7 @@ mod solve_expr {
|
|||
// 1 + 2
|
||||
// "#
|
||||
// ),
|
||||
// "Num *",
|
||||
// "Int *",
|
||||
// );
|
||||
// }
|
||||
|
||||
|
@ -1119,7 +1119,7 @@ mod solve_expr {
|
|||
[ alwaysFive "foo", alwaysFive [] ]
|
||||
"#
|
||||
),
|
||||
"List (Num *)",
|
||||
"List (Int *)",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1134,7 +1134,7 @@ mod solve_expr {
|
|||
24
|
||||
"#
|
||||
),
|
||||
"Num *",
|
||||
"Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1148,7 +1148,7 @@ mod solve_expr {
|
|||
3 -> 4
|
||||
"#
|
||||
),
|
||||
"Num *",
|
||||
"Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1161,17 +1161,17 @@ mod solve_expr {
|
|||
|
||||
#[test]
|
||||
fn one_field_record() {
|
||||
infer_eq("{ x: 5 }", "{ x : Num * }");
|
||||
infer_eq("{ x: 5 }", "{ x : Int * }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
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]
|
||||
fn record_literal_accessor() {
|
||||
infer_eq("{ x: 5, y : 3.14 }.x", "Num *");
|
||||
infer_eq("{ x: 5, y : 3.14 }.x", "Int *");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1230,7 +1230,7 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
foo : Num * -> custom
|
||||
foo : Int * -> custom
|
||||
|
||||
foo 2
|
||||
"#
|
||||
|
@ -1327,7 +1327,7 @@ mod solve_expr {
|
|||
\Foo -> 42
|
||||
"#
|
||||
),
|
||||
"[ Foo ] -> Num *",
|
||||
"[ Foo ] -> Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1339,7 +1339,7 @@ mod solve_expr {
|
|||
\@Foo -> 42
|
||||
"#
|
||||
),
|
||||
"[ @Foo ] -> Num *",
|
||||
"[ @Foo ] -> Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1354,7 +1354,7 @@ mod solve_expr {
|
|||
False -> 0
|
||||
"#
|
||||
),
|
||||
"[ False, True ] -> Num *",
|
||||
"[ False, True ] -> Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1366,7 +1366,7 @@ mod solve_expr {
|
|||
Foo "happy" 2020
|
||||
"#
|
||||
),
|
||||
"[ Foo Str (Num *) ]*",
|
||||
"[ Foo Str (Int *) ]*",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1378,7 +1378,7 @@ mod solve_expr {
|
|||
@Foo "happy" 2020
|
||||
"#
|
||||
),
|
||||
"[ @Foo Str (Num *) ]*",
|
||||
"[ @Foo Str (Int *) ]*",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1407,7 +1407,7 @@ mod solve_expr {
|
|||
{ x: 4 } -> 4
|
||||
"#
|
||||
),
|
||||
"Num *",
|
||||
"Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2347,7 +2347,7 @@ mod solve_expr {
|
|||
{ 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
|
||||
"#
|
||||
),
|
||||
"Num * -> Num *",
|
||||
"Int * -> Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2416,7 +2416,7 @@ mod solve_expr {
|
|||
toBit
|
||||
"#
|
||||
),
|
||||
"[ False, True ] -> Num *",
|
||||
"[ False, True ] -> Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2453,7 +2453,7 @@ mod solve_expr {
|
|||
fromBit
|
||||
"#
|
||||
),
|
||||
"Num * -> [ False, True ]*",
|
||||
"Int * -> [ False, True ]*",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2505,7 +2505,7 @@ mod solve_expr {
|
|||
foo { x: 5 }
|
||||
"#
|
||||
),
|
||||
"Num *",
|
||||
"Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2774,7 +2774,7 @@ mod solve_expr {
|
|||
// infer_eq_without_problem(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// s : Num *
|
||||
// s : Int *
|
||||
// s = 3.1
|
||||
|
||||
// s
|
||||
|
@ -3214,7 +3214,7 @@ mod solve_expr {
|
|||
List.get [ 10, 9, 8, 7 ] 1
|
||||
"#
|
||||
),
|
||||
"Result (Num *) [ OutOfBounds ]*",
|
||||
"Result (Int *) [ OutOfBounds ]*",
|
||||
);
|
||||
|
||||
infer_eq_without_problem(
|
||||
|
@ -3497,7 +3497,7 @@ mod solve_expr {
|
|||
f
|
||||
"#
|
||||
),
|
||||
"{ p : *, q : * }* -> Num *",
|
||||
"{ p : *, q : * }* -> Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3552,7 +3552,7 @@ mod solve_expr {
|
|||
_ -> 3
|
||||
"#
|
||||
),
|
||||
"Num * -> Num *",
|
||||
"Int * -> Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3724,7 +3724,8 @@ mod solve_expr {
|
|||
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 : { 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 : Num a, y ? Num a }* -> Num a",
|
||||
"{ x : Int a, y ? Int a }* -> Int a",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3767,7 +3769,7 @@ mod solve_expr {
|
|||
x + y
|
||||
"#
|
||||
),
|
||||
"Num *",
|
||||
"Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3781,7 +3783,7 @@ mod solve_expr {
|
|||
{ 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
|
||||
"#
|
||||
),
|
||||
"Num a -> Num a",
|
||||
"Int a -> Int a",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3985,10 +3987,10 @@ mod solve_expr {
|
|||
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]
|
||||
|
@ -4680,7 +4682,7 @@ mod solve_expr {
|
|||
x
|
||||
"#
|
||||
),
|
||||
"Num *",
|
||||
"Int *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -4981,7 +4983,7 @@ mod solve_expr {
|
|||
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 : [ 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 (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?
|
||||
// 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) => {
|
||||
// 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>"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -231,6 +231,7 @@ impl SolvedType {
|
|||
}
|
||||
}
|
||||
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))
|
||||
}
|
||||
RangedNumber(typ, _range_vars) => Self::from_var_help(subs, recursion_vars, *typ),
|
||||
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)
|
||||
}
|
||||
Content::RangedNumber(typ, range) => {
|
||||
let slice = subs.get_subs_slice(*range);
|
||||
write!(f, "RangedNumber({:?}, {:?})", typ, slice)
|
||||
}
|
||||
Content::Error => write!(f, "Error"),
|
||||
}
|
||||
}
|
||||
|
@ -588,11 +592,6 @@ define_const_var! {
|
|||
|
||||
AT_NATURAL,
|
||||
|
||||
AT_BINARY32,
|
||||
AT_BINARY64,
|
||||
|
||||
AT_DECIMAL,
|
||||
|
||||
// Signed8 : [ @Signed8 ]
|
||||
:pub SIGNED8,
|
||||
:pub SIGNED16,
|
||||
|
@ -608,11 +607,6 @@ define_const_var! {
|
|||
|
||||
:pub NATURAL,
|
||||
|
||||
:pub BINARY32,
|
||||
:pub BINARY64,
|
||||
|
||||
:pub DECIMAL,
|
||||
|
||||
// [ @Integer Signed8 ]
|
||||
AT_INTEGER_SIGNED8,
|
||||
AT_INTEGER_SIGNED16,
|
||||
|
@ -688,6 +682,36 @@ define_const_var! {
|
|||
|
||||
: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 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 {
|
||||
pub const RESULT_TAG_NAMES: SubsSlice<TagName> = SubsSlice::new(0, 2);
|
||||
|
||||
|
@ -1072,6 +1208,7 @@ impl Subs {
|
|||
}
|
||||
|
||||
define_integer_types(&mut subs);
|
||||
define_float_types(&mut subs);
|
||||
|
||||
subs.set_content(
|
||||
Variable::EMPTY_RECORD,
|
||||
|
@ -1492,6 +1629,7 @@ pub enum Content {
|
|||
},
|
||||
Structure(FlatType),
|
||||
Alias(Symbol, AliasVariables, Variable),
|
||||
RangedNumber(Variable, VariableSubsSlice),
|
||||
Error,
|
||||
}
|
||||
|
||||
|
@ -2244,6 +2382,15 @@ fn occurs(
|
|||
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(())
|
||||
}
|
||||
}
|
||||
|
@ -2433,6 +2580,19 @@ fn explicit_substitute(
|
|||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -2484,6 +2644,13 @@ fn get_var_names(
|
|||
get_var_names(subs, subs[arg_var], answer)
|
||||
}),
|
||||
|
||||
RangedNumber(typ, vars) => {
|
||||
let taken_names = get_var_names(subs, typ, taken_names);
|
||||
vars.into_iter().fold(taken_names, |answer, var| {
|
||||
get_var_names(subs, subs[var], answer)
|
||||
})
|
||||
}
|
||||
|
||||
Structure(flat_type) => match flat_type {
|
||||
FlatType::Apply(_, args) => {
|
||||
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))
|
||||
}
|
||||
|
||||
RangedNumber(typ, _) => var_to_err_type(subs, state, typ),
|
||||
|
||||
Error => ErrorType::Error,
|
||||
}
|
||||
}
|
||||
|
@ -2974,6 +3143,11 @@ fn restore_help(subs: &mut Subs, initial: Variable) {
|
|||
|
||||
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_variable(offsets, *actual),
|
||||
),
|
||||
RangedNumber(typ, vars) => RangedNumber(
|
||||
Self::offset_variable(offsets, *typ),
|
||||
Self::offset_variable_slice(offsets, *vars),
|
||||
),
|
||||
Error => Content::Error,
|
||||
}
|
||||
}
|
||||
|
@ -3525,6 +3703,23 @@ fn deep_copy_var_to_help<'a>(
|
|||
|
||||
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());
|
||||
stack.push(*real_type_var);
|
||||
}
|
||||
RangedNumber(typ, vars) => {
|
||||
stack.push(*typ);
|
||||
push_var_slice!(*vars);
|
||||
}
|
||||
Error => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -196,6 +196,7 @@ pub enum Type {
|
|||
/// Applying a type to some arguments (e.g. Dict.Dict String Int)
|
||||
Apply(Symbol, Vec<Type>, Region),
|
||||
Variable(Variable),
|
||||
RangedNumber(Box<Type>, Vec<Variable>),
|
||||
/// A type error, which will code gen to a runtime error
|
||||
Erroneous(Problem),
|
||||
}
|
||||
|
@ -439,6 +440,9 @@ impl fmt::Debug for Type {
|
|||
|
||||
write!(f, " as <{:?}>", rec)
|
||||
}
|
||||
Type::RangedNumber(typ, range_vars) => {
|
||||
write!(f, "Ranged({:?}, {:?})", typ, range_vars)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -549,6 +553,9 @@ impl Type {
|
|||
arg.substitute(substitutions);
|
||||
}
|
||||
}
|
||||
RangedNumber(typ, _) => {
|
||||
typ.substitute(substitutions);
|
||||
}
|
||||
|
||||
EmptyRec | EmptyTagUnion | Erroneous(_) => {}
|
||||
}
|
||||
|
@ -616,6 +623,7 @@ impl Type {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
RangedNumber(typ, _) => typ.substitute_alias(rep_symbol, rep_args, actual),
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
@ -653,6 +661,7 @@ impl Type {
|
|||
}
|
||||
Apply(symbol, _, _) if *symbol == rep_symbol => true,
|
||||
Apply(_, args, _) => args.iter().any(|arg| arg.contains_symbol(rep_symbol)),
|
||||
RangedNumber(typ, _) => typ.contains_symbol(rep_symbol),
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => false,
|
||||
}
|
||||
}
|
||||
|
@ -689,6 +698,9 @@ impl Type {
|
|||
} => actual_type.contains_variable(rep_variable),
|
||||
HostExposedAlias { actual, .. } => actual.contains_variable(rep_variable),
|
||||
Apply(_, args, _) => args.iter().any(|arg| arg.contains_variable(rep_variable)),
|
||||
RangedNumber(typ, vars) => {
|
||||
typ.contains_variable(rep_variable) || vars.iter().any(|&v| v == rep_variable)
|
||||
}
|
||||
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(_) => {}
|
||||
}
|
||||
}
|
||||
|
@ -901,6 +916,9 @@ fn symbols_help(tipe: &Type, accum: &mut ImSet<Symbol>) {
|
|||
Erroneous(Problem::CyclicAlias(alias, _, _)) => {
|
||||
accum.insert(*alias);
|
||||
}
|
||||
RangedNumber(typ, _) => {
|
||||
symbols_help(typ, accum);
|
||||
}
|
||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {}
|
||||
}
|
||||
}
|
||||
|
@ -979,6 +997,10 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
|||
}
|
||||
variables_help(actual, accum);
|
||||
}
|
||||
RangedNumber(typ, vars) => {
|
||||
variables_help(typ, accum);
|
||||
accum.extend(vars.iter().copied());
|
||||
}
|
||||
Apply(_, args, _) => {
|
||||
for x in args {
|
||||
variables_help(x, accum);
|
||||
|
@ -1083,6 +1105,10 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
|
|||
}
|
||||
variables_help_detailed(actual, accum);
|
||||
}
|
||||
RangedNumber(typ, vars) => {
|
||||
variables_help_detailed(typ, accum);
|
||||
accum.type_variables.extend(vars);
|
||||
}
|
||||
Apply(_, args, _) => {
|
||||
for x in args {
|
||||
variables_help_detailed(x, accum);
|
||||
|
@ -1288,6 +1314,7 @@ pub enum Mismatch {
|
|||
InconsistentIfElse,
|
||||
InconsistentWhenBranches,
|
||||
CanonicalizationProblem,
|
||||
TypeNotInRange(Variable, Vec<Variable>),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Hash)]
|
||||
|
|
|
@ -83,6 +83,7 @@ pub enum Unified {
|
|||
Success(Pool),
|
||||
Failure(Pool, ErrorType, ErrorType),
|
||||
BadType(Pool, roc_types::types::Problem),
|
||||
NotInRange(Pool, ErrorType, Vec<ErrorType>),
|
||||
}
|
||||
|
||||
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() {
|
||||
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 {
|
||||
let (type1, mut problems) = subs.var_to_error_type(var1);
|
||||
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)
|
||||
}
|
||||
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 propagates. Whatever we're comparing it to doesn't matter!
|
||||
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)]
|
||||
fn unify_alias(
|
||||
subs: &mut Subs,
|
||||
|
@ -231,6 +307,9 @@ fn unify_alias(
|
|||
}
|
||||
}
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
@ -302,6 +381,7 @@ fn unify_structure(
|
|||
// 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)
|
||||
}
|
||||
RangedNumber(real_var, _) => unify_pool(subs, pool, ctx.first, *real_var, ctx.mode),
|
||||
Error => merge(subs, ctx, Error),
|
||||
}
|
||||
}
|
||||
|
@ -829,6 +909,7 @@ fn unify_tag_union_new(
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum OtherTags2 {
|
||||
Empty,
|
||||
Union(
|
||||
|
@ -1222,7 +1303,7 @@ fn unify_rigid(subs: &mut Subs, ctx: &Context, name: &Lowercase, other: &Content
|
|||
// If the other is flex, rigid wins!
|
||||
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
|
||||
// rigid names are the same.
|
||||
mismatch!("Rigid {:?} with {:?}", ctx.first, &other)
|
||||
|
@ -1247,7 +1328,12 @@ fn unify_flex(
|
|||
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
|
||||
// In all other cases, if left is flex, defer to right.
|
||||
// (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)
|
||||
}
|
||||
|
||||
RangedNumber(..) => mismatch!(
|
||||
"RecursionVar {:?} with ranged number {:?}",
|
||||
ctx.first,
|
||||
&other
|
||||
),
|
||||
|
||||
Error => merge(subs, ctx, Error),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ use ven_pretty::DocAllocator;
|
|||
|
||||
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 TYPE_NOT_IN_RANGE: &str = r#"TYPE NOT IN RANGE"#;
|
||||
|
||||
pub fn type_problem<'b>(
|
||||
alloc: &'b RocDocAllocator<'b>,
|
||||
|
@ -59,6 +60,31 @@ pub fn type_problem<'b>(
|
|||
|
||||
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) => {
|
||||
use roc_types::types::Problem::*;
|
||||
match type_problem {
|
||||
|
|
|
@ -1221,6 +1221,7 @@ mod test_reporting {
|
|||
x
|
||||
"#
|
||||
),
|
||||
// TODO FIXME the second error message is incomplete, should be removed
|
||||
indoc!(
|
||||
r#"
|
||||
── TYPE MISMATCH ───────────────────────────────────────────────────────────────
|
||||
|
@ -1241,6 +1242,20 @@ mod test_reporting {
|
|||
|
||||
Tip: You can convert between Int and Float using functions like
|
||||
`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
|
||||
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!(
|
||||
|
@ -7882,18 +7899,26 @@ I need all branches in an `if` to have the same type!
|
|||
#[test]
|
||||
fn list_get_negative_number() {
|
||||
report_problem_as(
|
||||
"List.get [1, 2, 3] -1",
|
||||
indoc!(
|
||||
r#"
|
||||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────────────────────────────
|
||||
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
|
||||
is 170_141_183_460_469_231_731_687_303_715_884_105_727.
|
||||
It can only be used as a `I64` or `I128`
|
||||
|
||||
But it is being used as:
|
||||
|
||||
`Nat`
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue