diff --git a/ast/src/lang/core/expr/expr_to_expr2.rs b/ast/src/lang/core/expr/expr_to_expr2.rs index 5bcd4edd19..6295763f22 100644 --- a/ast/src/lang/core/expr/expr_to_expr2.rs +++ b/ast/src/lang/core/expr/expr_to_expr2.rs @@ -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!(), diff --git a/ast/src/lang/core/pattern.rs b/ast/src/lang/core/pattern.rs index aabb06254f..3bbefb7d83 100644 --- a/ast/src/lang/core/pattern.rs +++ b/ast/src/lang/core/pattern.rs @@ -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 { diff --git a/compiler/can/src/num.rs b/compiler/can/src/num.rs index c1d8511eb4..d568a4f221 100644 --- a/compiler/can/src/num.rs +++ b/compiler/can/src/num.rs @@ -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 { // 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, &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 { 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 for NumericBound { - #[inline(always)] - fn from(ib: IntBound) -> Self { - Self::Int(ib) - } -} - -impl From 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, + }, } diff --git a/compiler/can/src/pattern.rs b/compiler/can/src/pattern.rs index 2e574796c2..41aa49ca9a 100644 --- a/compiler/can/src/pattern.rs +++ b/compiler/can/src/pattern.rs @@ -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(), diff --git a/compiler/constrain/src/builtins.rs b/compiler/constrain/src/builtins.rs index 1081d0c1fa..b5ac1cf14f 100644 --- a/compiler/constrain/src/builtins.rs +++ b/compiler/constrain/src/builtins.rs @@ -324,8 +324,11 @@ impl TypedNumericBound for NumericBound { fn bounded_range(&self) -> Vec { 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 + } } } } diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index fee93d0253..d8c5a38d6e 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -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", ) } diff --git a/reporting/src/error/type.rs b/reporting/src/error/type.rs index 68dfa687e0..2092640e39 100644 --- a/reporting/src/error/type.rs +++ b/reporting/src/error/type.rs @@ -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)); diff --git a/reporting/tests/test_reporting.rs b/reporting/tests/test_reporting.rs index 95f31c5cad..7da7f48487 100644 --- a/reporting/tests/test_reporting.rs +++ b/reporting/tests/test_reporting.rs @@ -862,9 +862,9 @@ mod test_reporting { 2│> 2 if 1 -> 0x0 3│ _ -> 0x1 - Right now it’s an integer of type: + Right now it’s a number of type: - Int a + Num a But I need every `if` guard condition to evaluate to a Bool—either `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: