diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 349cac6f80..590764224c 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -285,7 +285,11 @@ fn lowlevel_4(symbol: Symbol, op: LowLevel, var_store: &mut VarStore) -> Def { fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def { let int_var = var_store.fresh(); let int_precision_var = var_store.fresh(); - let body = Int(int_var, int_precision_var, i64::MAX.into()); + let body = Int( + int_var, + int_precision_var, + i64::MAX.to_string().into_boxed_str(), + ); Def { annotation: None, @@ -300,7 +304,11 @@ fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_min_int(symbol: Symbol, var_store: &mut VarStore) -> Def { let int_var = var_store.fresh(); let int_precision_var = var_store.fresh(); - let body = Int(int_var, int_precision_var, i64::MIN.into()); + let body = Int( + int_var, + int_precision_var, + i64::MIN.to_string().into_boxed_str(), + ); Def { annotation: None, @@ -685,7 +693,10 @@ fn num_is_zero(symbol: Symbol, var_store: &mut VarStore) -> Def { op: LowLevel::Eq, args: vec![ (arg_var, Var(Symbol::ARG_1)), - (arg_var, Num(unbound_zero_var, 0)), + ( + arg_var, + Num(unbound_zero_var, "0".to_string().into_boxed_str()), + ), ], ret_var: bool_var, }; @@ -708,7 +719,10 @@ fn num_is_negative(symbol: Symbol, var_store: &mut VarStore) -> Def { let body = RunLowLevel { op: LowLevel::NumGt, args: vec![ - (arg_var, Num(unbound_zero_var, 0)), + ( + arg_var, + Num(unbound_zero_var, "0".to_string().into_boxed_str()), + ), (arg_var, Var(Symbol::ARG_1)), ], ret_var: bool_var, @@ -733,7 +747,10 @@ fn num_is_positive(symbol: Symbol, var_store: &mut VarStore) -> Def { op: LowLevel::NumGt, args: vec![ (arg_var, Var(Symbol::ARG_1)), - (arg_var, Num(unbound_zero_var, 0)), + ( + arg_var, + Num(unbound_zero_var, "0".to_string().into_boxed_str()), + ), ], ret_var: bool_var, }; @@ -756,14 +773,24 @@ fn num_is_odd(symbol: Symbol, var_store: &mut VarStore) -> Def { let body = RunLowLevel { op: LowLevel::Eq, args: vec![ - (arg_var, Int(var_store.fresh(), var_store.fresh(), 1)), + ( + arg_var, + Int( + var_store.fresh(), + var_store.fresh(), + 1.to_string().into_boxed_str(), + ), + ), ( arg_var, RunLowLevel { op: LowLevel::NumRemUnchecked, args: vec![ (arg_var, Var(Symbol::ARG_1)), - (arg_var, Num(unbound_two_var, 2)), + ( + arg_var, + Num(unbound_two_var, "2".to_string().into_boxed_str()), + ), ], ret_var: arg_var, }, @@ -790,14 +817,14 @@ fn num_is_even(symbol: Symbol, var_store: &mut VarStore) -> Def { let body = RunLowLevel { op: LowLevel::Eq, args: vec![ - (arg_var, Num(arg_num_var, 0)), + (arg_var, Num(arg_num_var, "0".to_string().into_boxed_str())), ( arg_var, RunLowLevel { op: LowLevel::NumRemUnchecked, args: vec![ (arg_var, Var(Symbol::ARG_1)), - (arg_var, Num(arg_num_var, 2)), + (arg_var, Num(arg_num_var, "2".to_string().into_boxed_str())), ], ret_var: arg_var, }, @@ -851,7 +878,14 @@ fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def { op: LowLevel::NumGte, args: vec![ (float_var, Var(Symbol::ARG_1)), - (float_var, Float(unbound_zero_var, precision_var, 0.0)), + ( + float_var, + Float( + unbound_zero_var, + precision_var, + "0.0".to_string().into_boxed_str(), + ), + ), ], ret_var: bool_var, }), @@ -897,7 +931,14 @@ fn num_log(symbol: Symbol, var_store: &mut VarStore) -> Def { op: LowLevel::NumGt, args: vec![ (float_var, Var(Symbol::ARG_1)), - (float_var, Float(unbound_zero_var, precision_var, 0.0)), + ( + float_var, + Float( + unbound_zero_var, + precision_var, + "0.0".to_string().into_boxed_str(), + ), + ), ], ret_var: bool_var, }), @@ -1127,7 +1168,11 @@ fn num_int_cast(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_max_i128(symbol: Symbol, var_store: &mut VarStore) -> Def { let int_var = var_store.fresh(); let int_precision_var = var_store.fresh(); - let body = Int(int_var, int_precision_var, i128::MAX); + let body = Int( + int_var, + int_precision_var, + i128::MAX.to_string().into_boxed_str(), + ); let std = roc_builtins::std::types(); let solved = std.get(&symbol).unwrap(); @@ -1160,7 +1205,10 @@ fn list_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def { let body = RunLowLevel { op: LowLevel::Eq, args: vec![ - (len_var, Num(unbound_zero_var, 0)), + ( + len_var, + Num(unbound_zero_var, "0".to_string().into_boxed_str()), + ), ( len_var, RunLowLevel { @@ -2039,7 +2087,10 @@ fn list_sum(symbol: Symbol, var_store: &mut VarStore) -> Def { args: vec![ (list_var, Var(Symbol::ARG_1)), (closure_var, list_sum_add(num_var, var_store)), - (num_var, Num(var_store.fresh(), 0)), + ( + num_var, + Num(var_store.fresh(), "0".to_string().into_boxed_str()), + ), ], ret_var, }; @@ -2081,7 +2132,10 @@ fn list_product(symbol: Symbol, var_store: &mut VarStore) -> Def { args: vec![ (list_var, Var(Symbol::ARG_1)), (closure_var, list_product_mul(num_var, var_store)), - (num_var, Num(var_store.fresh(), 1)), + ( + num_var, + Num(var_store.fresh(), "1".to_string().into_boxed_str()), + ), ], ret_var, }; @@ -2559,7 +2613,10 @@ fn num_rem(symbol: Symbol, var_store: &mut VarStore) -> Def { op: LowLevel::NotEq, args: vec![ (num_var, Var(Symbol::ARG_2)), - (num_var, Num(unbound_zero_var, 0)), + ( + num_var, + Num(unbound_zero_var, "0".to_string().into_boxed_str()), + ), ], ret_var: bool_var, }, @@ -2662,7 +2719,14 @@ fn num_div_float(symbol: Symbol, var_store: &mut VarStore) -> Def { op: LowLevel::NotEq, args: vec![ (num_var, Var(Symbol::ARG_2)), - (num_var, Float(unbound_zero_var, precision_var, 0.0)), + ( + num_var, + Float( + unbound_zero_var, + precision_var, + "0.0".to_string().into_boxed_str(), + ), + ), ], ret_var: bool_var, }, @@ -2727,7 +2791,11 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def { (num_var, Var(Symbol::ARG_2)), ( num_var, - Int(unbound_zero_var, unbound_zero_precision_var, 0), + Int( + unbound_zero_var, + unbound_zero_precision_var, + 0.to_string().into_boxed_str(), + ), ), ], ret_var: bool_var, @@ -2797,7 +2865,10 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def { RunLowLevel { op: LowLevel::NotEq, args: vec![ - (len_var, Int(zero_var, zero_precision_var, 0)), + ( + len_var, + Int(zero_var, zero_precision_var, 0.to_string().into_boxed_str()), + ), ( len_var, RunLowLevel { @@ -2821,7 +2892,14 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def { op: LowLevel::ListGetUnsafe, args: vec![ (list_var, Var(Symbol::ARG_1)), - (len_var, Int(zero_var, zero_precision_var, 0)), + ( + len_var, + Int( + zero_var, + zero_precision_var, + 0.to_string().into_boxed_str(), + ), + ), ], ret_var: list_elem_var, }, @@ -2878,7 +2956,10 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def { RunLowLevel { op: LowLevel::NotEq, args: vec![ - (len_var, Int(num_var, num_precision_var, 0)), + ( + len_var, + Int(num_var, num_precision_var, 0.to_string().into_boxed_str()), + ), ( len_var, RunLowLevel { @@ -2917,7 +2998,14 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def { ret_var: len_var, }, ), - (arg_var, Int(num_var, num_precision_var, 1)), + ( + arg_var, + Int( + num_var, + num_precision_var, + 1.to_string().into_boxed_str(), + ), + ), ], ret_var: len_var, }, diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index 264e6fefe6..c5430bd95e 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -3,8 +3,8 @@ use crate::builtins::builtin_defs_map; use crate::def::{can_defs_with_return, Def}; use crate::env::Env; use crate::num::{ - finish_parsing_base, finish_parsing_float, finish_parsing_int, float_expr_from_result, - int_expr_from_result, num_expr_from_result, + finish_parsing_base, finish_parsing_int, float_expr_from_result, int_expr_from_result, + num_expr_from_result, validate_float_str, }; use crate::pattern::{canonicalize_pattern, Pattern}; use crate::procedure::References; @@ -21,7 +21,7 @@ use roc_region::all::{Located, Region}; use roc_types::subs::{VarStore, Variable}; use roc_types::types::Alias; use std::fmt::Debug; -use std::{char, i64, u32}; +use std::{char, u32}; #[derive(Clone, Default, Debug, PartialEq)] pub struct Output { @@ -52,11 +52,11 @@ pub enum Expr { // Num stores the `a` variable in `Num a`. Not the same as the variable // stored in Int and Float below, which is strictly for better error messages - Num(Variable, i64), + Num(Variable, Box), // Int and Float store a variable to generate better error messages - Int(Variable, Variable, i128), - Float(Variable, Variable, f64), + Int(Variable, Variable, Box), + Float(Variable, Variable, Box), Str(Box), List { elem_var: Variable, @@ -207,13 +207,22 @@ pub fn canonicalize_expr<'a>( let (expr, output) = match expr { ast::Expr::Num(string) => { - let answer = num_expr_from_result(var_store, finish_parsing_int(*string), region, env); + let answer = num_expr_from_result( + var_store, + finish_parsing_int(*string).map(|_| *string), + region, + env, + ); (answer, Output::default()) } - ast::Expr::Float(string) => { - let answer = - float_expr_from_result(var_store, finish_parsing_float(string), region, env); + ast::Expr::Float(str) => { + let answer = float_expr_from_result( + var_store, + validate_float_str(str).map(|()| *str), + region, + env, + ); (answer, Output::default()) } @@ -795,8 +804,16 @@ pub fn canonicalize_expr<'a>( is_negative, } => { // the minus sign is added before parsing, to get correct overflow/underflow behavior - let result = finish_parsing_base(string, *base, *is_negative); - let answer = int_expr_from_result(var_store, result, region, *base, env); + let answer = match finish_parsing_base(string, *base, *is_negative) { + Ok(int) => { + // Done in this kinda round about way with intermediate variables + // to keep borrowed values around and make this compile + let int_string = int.to_string(); + let int_str = int_string.as_str(); + int_expr_from_result(var_store, Ok(int_str), region, *base, env) + } + Err(e) => int_expr_from_result(var_store, Err(e), region, *base, env), + }; (answer, Output::default()) } diff --git a/compiler/can/src/num.rs b/compiler/can/src/num.rs index f7d977be9f..168bcee52c 100644 --- a/compiler/can/src/num.rs +++ b/compiler/can/src/num.rs @@ -16,12 +16,12 @@ use std::i64; #[inline(always)] pub fn num_expr_from_result( var_store: &mut VarStore, - result: Result, + result: Result<&str, (&str, IntErrorKind)>, region: Region, env: &mut Env, ) -> Expr { match result { - Ok(int) => Expr::Num(var_store.fresh(), int), + Ok(str) => Expr::Num(var_store.fresh(), (*str).into()), Err((raw, error)) => { // (Num *) compiles to Int if it doesn't // get specialized to something else first, @@ -38,14 +38,14 @@ pub fn num_expr_from_result( #[inline(always)] pub fn int_expr_from_result( var_store: &mut VarStore, - result: Result, + result: Result<&str, (&str, IntErrorKind)>, region: Region, base: Base, env: &mut Env, ) -> Expr { // Int stores a variable to generate better error messages match result { - Ok(int) => Expr::Int(var_store.fresh(), var_store.fresh(), int.into()), + Ok(str) => Expr::Int(var_store.fresh(), var_store.fresh(), (*str).into()), Err((raw, error)) => { let runtime_error = InvalidInt(error, base, region, raw.into()); @@ -59,13 +59,13 @@ pub fn int_expr_from_result( #[inline(always)] pub fn float_expr_from_result( var_store: &mut VarStore, - result: Result, + result: Result<&str, (&str, FloatErrorKind)>, region: Region, env: &mut Env, ) -> Expr { // Float stores a variable to generate better error messages match result { - Ok(float) => Expr::Float(var_store.fresh(), var_store.fresh(), float), + Ok(str) => Expr::Float(var_store.fresh(), var_store.fresh(), (*str).into()), Err((raw, error)) => { let runtime_error = InvalidFloat(error, region, raw.into()); @@ -77,10 +77,12 @@ pub fn float_expr_from_result( } #[inline(always)] -pub fn finish_parsing_int(raw: &str) -> Result { +pub fn finish_parsing_int(raw: &str) -> Result<&str, (&str, IntErrorKind)> { // Ignore underscores. let radix = 10; - from_str_radix::(raw.replace("_", "").as_str(), radix).map_err(|e| (raw, e.kind)) + from_str_radix::(raw.replace("_", "").as_str(), radix) + .map(|_| raw) + .map_err(|e| (raw, e.kind)) } #[inline(always)] @@ -106,10 +108,10 @@ pub fn finish_parsing_base( } #[inline(always)] -pub fn finish_parsing_float(raw: &str) -> Result { +pub fn validate_float_str(raw: &str) -> Result<(), (&str, FloatErrorKind)> { // Ignore underscores. match raw.replace("_", "").parse::() { - Ok(float) if float.is_finite() => Ok(float), + Ok(float) if float.is_finite() => Ok(()), Ok(float) => { if float.is_sign_positive() { Err((raw, FloatErrorKind::PositiveInfinity)) diff --git a/compiler/can/src/pattern.rs b/compiler/can/src/pattern.rs index 92b1ca611b..1e464a38e4 100644 --- a/compiler/can/src/pattern.rs +++ b/compiler/can/src/pattern.rs @@ -1,6 +1,6 @@ use crate::env::Env; use crate::expr::{canonicalize_expr, unescape_char, Expr, Output}; -use crate::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int}; +use crate::num::{finish_parsing_base, finish_parsing_int, validate_float_str}; use crate::scope::Scope; use roc_module::ident::{Ident, Lowercase, TagName}; use roc_module::symbol::Symbol; @@ -25,9 +25,9 @@ pub enum Pattern { ext_var: Variable, destructs: Vec>, }, - IntLiteral(Variable, i64), - NumLiteral(Variable, i64), - FloatLiteral(Variable, f64), + IntLiteral(Variable, Box), + NumLiteral(Variable, Box), + FloatLiteral(Variable, Box), StrLiteral(Box), Underscore, @@ -185,13 +185,13 @@ pub fn canonicalize_pattern<'a>( } } - FloatLiteral(string) => match pattern_type { - WhenBranch => match finish_parsing_float(string) { + FloatLiteral(str) => match pattern_type { + WhenBranch => match validate_float_str(str) { Err(_error) => { let problem = MalformedPatternProblem::MalformedFloat; malformed_pattern(env, problem, region) } - Ok(float) => Pattern::FloatLiteral(var_store.fresh(), float), + Ok(_float) => Pattern::FloatLiteral(var_store.fresh(), (*str).into()), }, ptype => unsupported_pattern(env, ptype, region), }, @@ -201,13 +201,13 @@ pub fn canonicalize_pattern<'a>( TopLevelDef | DefExpr => bad_underscore(env, region), }, - NumLiteral(string) => match pattern_type { - WhenBranch => match finish_parsing_int(string) { + NumLiteral(str) => match pattern_type { + WhenBranch => match finish_parsing_int(str) { Err(_error) => { let problem = MalformedPatternProblem::MalformedInt; malformed_pattern(env, problem, region) } - Ok(int) => Pattern::NumLiteral(var_store.fresh(), int), + Ok(_int) => Pattern::NumLiteral(var_store.fresh(), (*str).into()), }, ptype => unsupported_pattern(env, ptype, region), }, @@ -223,11 +223,9 @@ pub fn canonicalize_pattern<'a>( malformed_pattern(env, problem, region) } Ok(int) => { - if *is_negative { - Pattern::IntLiteral(var_store.fresh(), -int) - } else { - Pattern::IntLiteral(var_store.fresh(), int) - } + let sign_str = if *is_negative { "-" } else { "" }; + let int_str = format!("{}{}", sign_str, int.to_string()).into_boxed_str(); + Pattern::IntLiteral(var_store.fresh(), int_str) } }, ptype => unsupported_pattern(env, ptype, region),