Include floats in bounds for unspecified numbers

This commit is contained in:
ayazhafiz 2022-02-06 12:31:37 -05:00
parent 3fffca48bb
commit c5d918e68c
8 changed files with 129 additions and 180 deletions

View File

@ -76,7 +76,7 @@ pub fn expr_to_expr2<'a>(
}
Num(string) => {
match finish_parsing_num(string) {
Ok(ParsedNumResult::UnknownNum(int) | ParsedNumResult::Int(int, _)) => {
Ok(ParsedNumResult::UnknownNum(int, _) | ParsedNumResult::Int(int, _)) => {
let expr = Expr2::SmallInt {
number: IntVal::I64(match int {
IntValue::U128(_) => todo!(),

View File

@ -196,7 +196,7 @@ pub fn to_pattern2<'a>(
let problem = MalformedPatternProblem::MalformedInt;
malformed_pattern(env, problem, region)
}
Ok(ParsedNumResult::UnknownNum(int)) => {
Ok(ParsedNumResult::UnknownNum(int, _bound)) => {
Pattern2::NumLiteral(
env.var_store.fresh(),
match int {

View File

@ -17,8 +17,8 @@ pub fn num_expr_from_result(
env: &mut Env,
) -> Expr {
match result {
Ok((str, ParsedNumResult::UnknownNum(num))) => {
Expr::Num(var_store.fresh(), (*str).into(), num, NumericBound::None)
Ok((str, ParsedNumResult::UnknownNum(num, bound))) => {
Expr::Num(var_store.fresh(), (*str).into(), num, bound)
}
Ok((str, ParsedNumResult::Int(num, bound))) => Expr::Int(
var_store.fresh(),
@ -103,27 +103,14 @@ pub fn float_expr_from_result(
pub enum ParsedNumResult {
Int(IntValue, IntBound),
Float(f64, FloatBound),
UnknownNum(IntValue),
UnknownNum(IntValue, NumericBound),
}
#[inline(always)]
pub fn finish_parsing_num(raw: &str) -> Result<ParsedNumResult, (&str, IntErrorKind)> {
// Ignore underscores.
let radix = 10;
let (num, bound) =
from_str_radix(raw.replace("_", "").as_str(), radix).map_err(|e| (raw, e))?;
// Let's try to specialize the number
Ok(match bound {
NumericBound::None => ParsedNumResult::UnknownNum(num),
NumericBound::Int(ib) => ParsedNumResult::Int(num, ib),
NumericBound::Float(fb) => {
let num = match num {
IntValue::I128(n) => n as f64,
IntValue::U128(n) => n as f64,
};
ParsedNumResult::Float(num, fb)
}
})
from_str_radix(raw.replace("_", "").as_str(), radix).map_err(|e| (raw, e))
}
#[inline(always)]
@ -145,13 +132,13 @@ pub fn finish_parsing_base(
} else {
from_str_radix(raw.replace("_", "").as_str(), radix)
})
.and_then(|(n, bound)| {
let bound = match bound {
NumericBound::None => IntBound::None,
NumericBound::Int(ib) => ib,
NumericBound::Float(_) => return Err(IntErrorKind::FloatSuffix),
};
Ok((n, bound))
.and_then(|parsed| match parsed {
ParsedNumResult::Float(..) => return Err(IntErrorKind::FloatSuffix),
ParsedNumResult::Int(val, bound) => Ok((val, bound)),
ParsedNumResult::UnknownNum(val, NumericBound::None) => Ok((val, IntBound::None)),
ParsedNumResult::UnknownNum(val, NumericBound::AtLeastIntOrFloat { sign, width }) => {
Ok((val, IntBound::AtLeast { sign, width }))
}
})
.map_err(|e| (raw, e))
}
@ -223,7 +210,7 @@ fn parse_literal_suffix(num_str: &str) -> (Option<ParsedWidth>, &str) {
/// the LEGAL_DETAILS file in the root directory of this distribution.
///
/// Thanks to the Rust project and its contributors!
fn from_str_radix(src: &str, radix: u32) -> Result<(IntValue, NumericBound), IntErrorKind> {
fn from_str_radix(src: &str, radix: u32) -> Result<ParsedNumResult, IntErrorKind> {
use self::IntErrorKind::*;
assert!(
@ -273,25 +260,30 @@ fn from_str_radix(src: &str, radix: u32) -> Result<(IntValue, NumericBound), Int
} else {
SignDemand::NoDemand
};
Ok((
Ok(ParsedNumResult::UnknownNum(
result,
IntBound::AtLeast {
NumericBound::AtLeastIntOrFloat {
sign: sign_demand,
width: lower_bound,
}
.into(),
},
))
}
Some(ParsedWidth::Float(fw)) => {
// For now, assume floats can represent all integers
// TODO: this is somewhat incorrect, revisit
Ok((result, FloatBound::Exact(fw).into()))
Ok(ParsedNumResult::Float(
match result {
IntValue::I128(n) => n as f64,
IntValue::U128(n) => n as f64,
},
FloatBound::Exact(fw),
))
}
Some(ParsedWidth::Int(exact_width)) => {
// We need to check if the exact bound >= lower bound.
if exact_width.is_superset(&lower_bound, is_negative) {
// Great! Use the exact bound.
Ok((result, IntBound::Exact(exact_width).into()))
Ok(ParsedNumResult::Int(result, IntBound::Exact(exact_width)))
} else {
// This is something like 200i8; the lower bound is u8, which holds strictly more
// ints on the positive side than i8 does. Report an error depending on which side
@ -512,20 +504,9 @@ pub enum FloatBound {
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum NumericBound {
None,
Int(IntBound),
Float(FloatBound),
}
impl From<IntBound> for NumericBound {
#[inline(always)]
fn from(ib: IntBound) -> Self {
Self::Int(ib)
}
}
impl From<FloatBound> for NumericBound {
#[inline(always)]
fn from(fb: FloatBound) -> Self {
Self::Float(fb)
}
/// Must be an integer of a certain size, or any float.
AtLeastIntOrFloat {
sign: SignDemand,
width: IntWidth,
},
}

View File

@ -216,8 +216,8 @@ pub fn canonicalize_pattern<'a>(
let problem = MalformedPatternProblem::MalformedInt;
malformed_pattern(env, problem, region)
}
Ok(ParsedNumResult::UnknownNum(int)) => {
Pattern::NumLiteral(var_store.fresh(), (str).into(), int, NumericBound::None)
Ok(ParsedNumResult::UnknownNum(int, bound)) => {
Pattern::NumLiteral(var_store.fresh(), (str).into(), int, bound)
}
Ok(ParsedNumResult::Int(int, bound)) => Pattern::IntLiteral(
var_store.fresh(),

View File

@ -324,8 +324,11 @@ impl TypedNumericBound for NumericBound {
fn bounded_range(&self) -> Vec<Variable> {
match self {
NumericBound::None => vec![],
NumericBound::Int(ib) => ib.bounded_range(),
NumericBound::Float(fb) => fb.bounded_range(),
&NumericBound::AtLeastIntOrFloat { sign, width } => {
let mut range = IntBound::AtLeast { sign, width }.bounded_range();
range.extend_from_slice(&[Variable::F32, Variable::F64, Variable::DEC]);
range
}
}
}
}

View File

@ -164,7 +164,7 @@ mod solve_expr {
#[test]
fn int_literal() {
infer_eq("5", "Int *");
infer_eq("5", "Num *");
}
#[test]
@ -334,7 +334,7 @@ mod solve_expr {
[42]
"#
),
"List (Int *)",
"List (Num *)",
);
}
@ -346,7 +346,7 @@ mod solve_expr {
[[[ 5 ]]]
"#
),
"List (List (List (Int *)))",
"List (List (List (Num *)))",
);
}
@ -358,7 +358,7 @@ mod solve_expr {
[ 1, 2, 3 ]
"#
),
"List (Int *)",
"List (Num *)",
);
}
@ -370,7 +370,7 @@ mod solve_expr {
[ [ 1 ], [ 2, 3 ] ]
"#
),
"List (List (Int *))",
"List (List (Num *))",
);
}
@ -518,7 +518,7 @@ mod solve_expr {
\_, _ -> 42
"#
),
"*, * -> Int *",
"*, * -> Num *",
);
}
@ -689,7 +689,7 @@ mod solve_expr {
func
"#
),
"*, * -> Int *",
"*, * -> Num *",
);
}
@ -753,7 +753,7 @@ mod solve_expr {
c
"#
),
"Int *",
"Num *",
);
}
@ -788,7 +788,7 @@ mod solve_expr {
alwaysFive "stuff"
"#
),
"Int *",
"Num *",
);
}
@ -835,7 +835,7 @@ mod solve_expr {
x
"#
),
"Int *",
"Num *",
);
}
@ -849,7 +849,7 @@ mod solve_expr {
enlist 5
"#
),
"List (Int *)",
"List (Num *)",
);
}
@ -876,7 +876,7 @@ mod solve_expr {
1 |> (\a -> a)
"#
),
"Int *",
"Num *",
);
}
@ -890,7 +890,7 @@ mod solve_expr {
1 |> always2 "foo"
"#
),
"Int *",
"Num *",
);
}
@ -955,7 +955,7 @@ mod solve_expr {
apply identity 5
"#
),
"Int *",
"Num *",
);
}
@ -984,7 +984,7 @@ mod solve_expr {
// flip neverendingInt
// "#
// ),
// "(Int *, (a -> a)) -> Int *",
// "(Num *, (a -> a)) -> Num *",
// );
// }
@ -1058,7 +1058,7 @@ mod solve_expr {
// 1 // 2
// "#
// ),
// "Int *",
// "Num *",
// );
// }
@ -1070,7 +1070,7 @@ mod solve_expr {
// 1 + 2
// "#
// ),
// "Int *",
// "Num *",
// );
// }
@ -1119,7 +1119,7 @@ mod solve_expr {
[ alwaysFive "foo", alwaysFive [] ]
"#
),
"List (Int *)",
"List (Num *)",
);
}
@ -1134,7 +1134,7 @@ mod solve_expr {
24
"#
),
"Int *",
"Num *",
);
}
@ -1148,7 +1148,7 @@ mod solve_expr {
3 -> 4
"#
),
"Int *",
"Num *",
);
}
@ -1161,17 +1161,17 @@ mod solve_expr {
#[test]
fn one_field_record() {
infer_eq("{ x: 5 }", "{ x : Int * }");
infer_eq("{ x: 5 }", "{ x : Num * }");
}
#[test]
fn two_field_record() {
infer_eq("{ x: 5, y : 3.14 }", "{ x : Int *, y : Float * }");
infer_eq("{ x: 5, y : 3.14 }", "{ x : Num *, y : Float * }");
}
#[test]
fn record_literal_accessor() {
infer_eq("{ x: 5, y : 3.14 }.x", "Int *");
infer_eq("{ x: 5, y : 3.14 }.x", "Num *");
}
#[test]
@ -1230,7 +1230,7 @@ mod solve_expr {
infer_eq(
indoc!(
r#"
foo : Int * -> custom
foo : Num * -> custom
foo 2
"#
@ -1327,7 +1327,7 @@ mod solve_expr {
\Foo -> 42
"#
),
"[ Foo ] -> Int *",
"[ Foo ] -> Num *",
);
}
@ -1339,7 +1339,7 @@ mod solve_expr {
\@Foo -> 42
"#
),
"[ @Foo ] -> Int *",
"[ @Foo ] -> Num *",
);
}
@ -1354,7 +1354,7 @@ mod solve_expr {
False -> 0
"#
),
"[ False, True ] -> Int *",
"[ False, True ] -> Num *",
);
}
@ -1366,7 +1366,7 @@ mod solve_expr {
Foo "happy" 2020
"#
),
"[ Foo Str (Int *) ]*",
"[ Foo Str (Num *) ]*",
);
}
@ -1378,7 +1378,7 @@ mod solve_expr {
@Foo "happy" 2020
"#
),
"[ @Foo Str (Int *) ]*",
"[ @Foo Str (Num *) ]*",
);
}
@ -1407,7 +1407,7 @@ mod solve_expr {
{ x: 4 } -> 4
"#
),
"Int *",
"Num *",
);
}
@ -2347,7 +2347,7 @@ mod solve_expr {
{ numIdentity, x : numIdentity 42, y }
"#
),
"{ numIdentity : Num a -> Num a, x : Int *, y : F64 }",
"{ numIdentity : Num a -> Num a, x : Num a, y : F64 }",
);
}
@ -2383,7 +2383,7 @@ mod solve_expr {
f
"#
),
"Int * -> Int *",
"Num * -> Num *",
);
}
@ -2416,7 +2416,7 @@ mod solve_expr {
toBit
"#
),
"[ False, True ] -> Int *",
"[ False, True ] -> Num *",
);
}
@ -2453,7 +2453,7 @@ mod solve_expr {
fromBit
"#
),
"Int * -> [ False, True ]*",
"Num * -> [ False, True ]*",
);
}
@ -2505,7 +2505,7 @@ mod solve_expr {
foo { x: 5 }
"#
),
"Int *",
"Num *",
);
}
@ -2774,7 +2774,7 @@ mod solve_expr {
// infer_eq_without_problem(
// indoc!(
// r#"
// s : Int *
// s : Num *
// s = 3.1
// s
@ -3214,7 +3214,7 @@ mod solve_expr {
List.get [ 10, 9, 8, 7 ] 1
"#
),
"Result (Int *) [ OutOfBounds ]*",
"Result (Num *) [ OutOfBounds ]*",
);
infer_eq_without_problem(
@ -3497,7 +3497,7 @@ mod solve_expr {
f
"#
),
"{ p : *, q : * }* -> Int *",
"{ p : *, q : * }* -> Num *",
);
}
@ -3552,7 +3552,7 @@ mod solve_expr {
_ -> 3
"#
),
"Int * -> Int *",
"Num * -> Num *",
);
}
@ -3724,8 +3724,7 @@ mod solve_expr {
negatePoint { x: 1, y: 2.1, z: 0x3 }
"#
),
// TODO this should be "Int a", FIXME
"{ x : Int *, y : F64, z : Int * }",
"{ x : Num a, y : F64, z : Int * }",
);
}
@ -3742,8 +3741,7 @@ mod solve_expr {
{ a, b }
"#
),
// TODO this should be "Int a", FIXME
"{ a : { x : Int *, y : F64, z : c }, b : { blah : Str, x : Int *, y : F64, z : c } }",
"{ a : { x : Num a, y : F64, z : c }, b : { blah : Str, x : Num a, y : F64, z : c } }",
);
}
@ -3755,7 +3753,7 @@ mod solve_expr {
\{ x, y ? 0 } -> x + y
"#
),
"{ x : Int a, y ? Int a }* -> Int a",
"{ x : Num a, y ? Num a }* -> Num a",
);
}
@ -3769,7 +3767,7 @@ mod solve_expr {
x + y
"#
),
"Int *",
"Num *",
);
}
@ -3783,7 +3781,7 @@ mod solve_expr {
{ x, y ? 0 } -> x + y
"#
),
"{ x : Int a, y ? Int a }* -> Int a",
"{ x : Num a, y ? Num a }* -> Num a",
);
}
@ -3948,7 +3946,7 @@ mod solve_expr {
g
"#
),
"Int a -> Int a",
"Num a -> Num a",
);
}
@ -3987,10 +3985,10 @@ mod solve_expr {
Foo Bar 1
"#
),
"[ Foo [ Bar ]* (Int *) ]*",
"[ Foo [ Bar ]* (Num *) ]*",
);
infer_eq_without_problem("Foo Bar 1", "[ Foo [ Bar ]* (Int *) ]*");
infer_eq_without_problem("Foo Bar 1", "[ Foo [ Bar ]* (Num *) ]*");
}
#[test]
@ -4682,7 +4680,7 @@ mod solve_expr {
x
"#
),
"Int *",
"Num *",
);
}
@ -4983,7 +4981,7 @@ mod solve_expr {
None -> 0
"#
),
"[ None, Some { tag : [ A, B ] }* ] -> Int *",
"[ None, Some { tag : [ A, B ] }* ] -> Num *",
)
}
@ -5016,7 +5014,7 @@ mod solve_expr {
{ x: Red, y ? 5 } -> y
"#
),
"{ x : [ Blue, Red ], y ? Int a }* -> Int a",
"{ x : [ Blue, Red ], y ? Num a }* -> Num a",
)
}
@ -5029,8 +5027,7 @@ mod solve_expr {
\UserId id -> id + 1
"#
),
// TODO needs parantheses
"[ UserId Int a ] -> Int a",
"[ UserId (Num a) ] -> Num a",
)
}

View File

@ -66,11 +66,11 @@ pub fn type_problem<'b>(
let last = range.len() - 1;
for (i, choice) in range.into_iter().enumerate() {
if i == last && i == 1 {
range_choices.push(alloc.text(" or "));
range_choices.push(alloc.reflow(" 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(alloc.reflow(", or "));
} else if i > 0 {
range_choices.push(alloc.reflow(", "));
}
range_choices.push(to_doc(alloc, Parens::Unnecessary, choice));

View File

@ -862,9 +862,9 @@ mod test_reporting {
2> 2 if 1 -> 0x0
3 _ -> 0x1
Right now its an integer of type:
Right now its a number of type:
Int a
Num a
But I need every `if` guard condition to evaluate to a Booleither
`True` or `False`.
@ -896,7 +896,7 @@ mod test_reporting {
but the `then` branch has the type:
Int a
Num a
I need all branches in an `if` to have the same type!
"#
@ -927,7 +927,7 @@ mod test_reporting {
But all the previous branches have type:
Int a
Num a
I need all branches in an `if` to have the same type!
"#
@ -993,7 +993,7 @@ mod test_reporting {
However, the preceding elements in the list all have the type:
Int a
Num a
I need every element in a list to have the same type!
"#
@ -1250,8 +1250,8 @@ mod test_reporting {
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`
It can only be used as a `I8`, `U8`, `I16`, `U16`, `I32`, `U32`, `I64`, `Nat`, `U64`,
`I128`, `U128`, `F32`, `F64`, or `Dec`
But it is being used as:
@ -1439,7 +1439,7 @@ mod test_reporting {
But the expression between `when` and `is` has the type:
Int a
Num a
"#
),
)
@ -1470,7 +1470,7 @@ mod test_reporting {
But all the previous branches match:
Int a
Num a
"#
),
)
@ -1500,7 +1500,7 @@ mod test_reporting {
But the expression between `when` and `is` has the type:
{ foo : Int a }
{ foo : Num a }
"#
),
)
@ -1620,13 +1620,13 @@ mod test_reporting {
2 {} | 1 -> 3
^^^^^^
The first pattern is trying to match integers:
The first pattern is trying to match numbers:
Int a
Num a
But the expression between `when` and `is` has the type:
{ foo : Int a }
{ foo : Num a }
"#
),
)
@ -1652,9 +1652,9 @@ mod test_reporting {
1 (Foo x) = 42
^^
It is an integer of type:
It is a number of type:
Int a
Num a
But you are trying to use it as:
@ -2177,8 +2177,8 @@ mod test_reporting {
This is usually a typo. Here are the `x` fields that are most similar:
{ fo : Int b
, bar : Int a
{ fo : Num b
, bar : Num a
}
So maybe `.foo` should be `.fo`?
@ -2244,10 +2244,10 @@ mod test_reporting {
This is usually a typo. Here are the `x` fields that are most similar:
{ fo : Int c
, foobar : Int d
, bar : Int a
, baz : Int b
{ fo : Num c
, foobar : Num d
, bar : Num a
, baz : Num b
, ...
}
@ -2342,7 +2342,7 @@ mod test_reporting {
But `add` needs the 2nd argument to be:
Num (Integer a)
Num a
"#
),
)
@ -3375,8 +3375,8 @@ mod test_reporting {
This `ACons` global tag application has the type:
[ ACons Int Signed64 [ BCons (Int a) [ ACons Str [ BNil ]b ]c ]d,
ANil ]
[ ACons Num (Integer Signed64) [ BCons (Num a) [ ACons Str [ BNil
]b ]c ]d, ANil ]
But the type annotation on `x` says it should be:
@ -3401,9 +3401,7 @@ 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
getI128 = \_ -> 1i128
x + y + h + l + minlit + (getI128 maxlit)
x + y + h + l + minlit + maxlit
"#
),
indoc!(
@ -4990,7 +4988,7 @@ mod test_reporting {
This `insert` call produces:
Dict Str (Int a)
Dict Str (Num a)
But the type annotation on `myDict` says it should be:
@ -5652,7 +5650,7 @@ mod test_reporting {
but the `then` branch has the type:
Int a
Num a
I need all branches in an `if` to have the same type!
"#
@ -6293,7 +6291,7 @@ I need all branches in an `if` to have the same type!
This `map` call produces:
List [ Foo Int a ]
List [ Foo Num a ]
But the type annotation on `x` says it should be:
@ -6543,11 +6541,11 @@ I need all branches in an `if` to have the same type!
This argument is an anonymous function of type:
Num (Integer a) -> Num (Integer a)
Num a -> Num a
But `map` needs the 2nd argument to be:
Str -> Num (Integer a)
Str -> Num a
"#
),
)
@ -6649,21 +6647,6 @@ I need all branches in an `if` to have the same type!
But the type annotation on `mult` says it should be:
F64
TYPE MISMATCH
The 2nd argument to `mult` is not what I expect:
4 mult 0 0
^
This argument is an integer of type:
Int a
But `mult` needs the 2nd argument to be:
F64
"#
),
)
@ -6712,21 +6695,6 @@ I need all branches in an `if` to have the same type!
But the type annotation on `mult` says it should be:
F64
TYPE MISMATCH
The 2nd argument to `mult` is not what I expect:
4 mult 0 0
^
This argument is an integer of type:
Int a
But `mult` needs the 2nd argument to be:
F64
"#
),
)
@ -7078,9 +7046,9 @@ I need all branches in an `if` to have the same type!
5 f = \c -> c 6
^
This argument is an integer of type:
This argument is a number of type:
Int a
Num a
But `c` needs the 1st argument to be:
@ -7088,7 +7056,7 @@ I need all branches in an `if` to have the same type!
Tip: The type annotation uses the type variable `a` to say that this
definition can produce any type of value. But in the body I see that
it will only produce a `Int` value of a single specific type. Maybe
it will only produce a `Num` value of a single specific type. Maybe
change the type annotation to be more specific? Maybe change the code
to be more general?
"#
@ -7901,9 +7869,9 @@ I need all branches in an `if` to have the same type!
report_problem_as(
indoc!(
r#"
a = -9_223_372_036_854
List.get [1,2,3] a
"#
a = -9_223_372_036_854
List.get [1,2,3] a
"#
),
indoc!(
r#"
@ -7914,7 +7882,7 @@ I need all branches in an `if` to have the same type!
2 List.get [1,2,3] a
^
It can only be used as a `I64` or `I128`
It can only be used as a `I64`, `I128`, `F32`, `F64`, or `Dec`
But it is being used as: