diff --git a/Cargo.lock b/Cargo.lock index 2990e9eca8..22a6dcae25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3017,6 +3017,7 @@ dependencies = [ "roc_region", "roc_reporting", "roc_solve", + "roc_std", "roc_types", "roc_unify", "serde_json", @@ -3364,6 +3365,7 @@ dependencies = [ "roc_problem", "roc_region", "roc_solve", + "roc_std", "roc_types", "roc_unify", "ven_ena", diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index 0fb00a9e48..c201bc009e 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -223,13 +223,13 @@ mod cli_run { // expected_ending: "", // use_valgrind: true, // }, - // cli:"cli" => Example { - // filename: "Echo.roc", - // executable_filename: "echo", - // stdin: &["Giovanni\n", "Giorgio\n"], - // expected_ending: "Giovanni Giorgio!\n", - // use_valgrind: true, - // }, + cli:"cli" => Example { + filename: "Echo.roc", + executable_filename: "echo", + stdin: &["Giovanni\n", "Giorgio\n"], + expected_ending: "Hi, Giovanni Giorgio!\n", + use_valgrind: true, + }, // custom_malloc:"custom-malloc" => Example { // filename: "Main.roc", // executable_filename: "custom-malloc-example", diff --git a/compiler/build/Cargo.toml b/compiler/build/Cargo.toml index dc1cce7b4e..c49608fd97 100644 --- a/compiler/build/Cargo.toml +++ b/compiler/build/Cargo.toml @@ -21,6 +21,7 @@ roc_mono = { path = "../mono" } roc_load = { path = "../load" } roc_gen_llvm = { path = "../gen_llvm", optional = true } roc_reporting = { path = "../reporting" } +roc_std = { path = "../../roc_std" } im = "14" # im and im-rc should always have the same version! im-rc = "14" # im and im-rc should always have the same version! bumpalo = { version = "3.6.1", features = ["collections"] } diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index ddcf76db78..9c2f014586 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -287,7 +287,7 @@ 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.into()); Def { annotation: None, @@ -302,7 +302,7 @@ 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.into()); Def { annotation: None, @@ -687,7 +687,7 @@ 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)), ], ret_var: bool_var, }; @@ -710,7 +710,7 @@ 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)), (arg_var, Var(Symbol::ARG_1)), ], ret_var: bool_var, @@ -735,7 +735,7 @@ 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)), ], ret_var: bool_var, }; @@ -758,14 +758,14 @@ 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)), ( 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)), ], ret_var: arg_var, }, @@ -792,14 +792,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)), ( 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)), ], ret_var: arg_var, }, @@ -853,7 +853,7 @@ 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)), ], ret_var: bool_var, }), @@ -899,7 +899,7 @@ 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)), ], ret_var: bool_var, }), @@ -1139,7 +1139,7 @@ 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); let std = roc_builtins::std::types(); let solved = std.get(&symbol).unwrap(); @@ -1172,7 +1172,7 @@ 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)), ( len_var, RunLowLevel { @@ -2051,7 +2051,7 @@ 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)), ], ret_var, }; @@ -2093,7 +2093,7 @@ 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)), ], ret_var, }; @@ -2571,7 +2571,7 @@ 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)), ], ret_var: bool_var, }, @@ -2674,7 +2674,7 @@ 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)), ], ret_var: bool_var, }, @@ -2739,7 +2739,7 @@ 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), ), ], ret_var: bool_var, @@ -2809,7 +2809,7 @@ 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)), ( len_var, RunLowLevel { @@ -2833,7 +2833,7 @@ 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)), ], ret_var: list_elem_var, }, @@ -2890,7 +2890,7 @@ 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)), ( len_var, RunLowLevel { @@ -2929,7 +2929,7 @@ 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)), ], ret_var: len_var, }, @@ -3403,7 +3403,7 @@ fn num_bytes_to(symbol: Symbol, var_store: &mut VarStore, offset: i64, low_level add_var, RunLowLevel { ret_var: cast_var, - args: vec![(cast_var, Num(var_store.fresh(), offset))], + args: vec![(cast_var, num(var_store.fresh(), offset))], op: LowLevel::NumIntCast, }, ), @@ -3489,3 +3489,18 @@ fn defn_help( loc_body: Box::new(no_region(body)), } } + +#[inline(always)] +fn int(num_var: Variable, precision_var: Variable, i: i128) -> Expr { + Int(num_var, precision_var, i.to_string().into_boxed_str(), i) +} + +#[inline(always)] +fn float(num_var: Variable, precision_var: Variable, f: f64) -> Expr { + Float(num_var, precision_var, f.to_string().into_boxed_str(), f) +} + +#[inline(always)] +fn num(num_var: Variable, i: i64) -> Expr { + Num(num_var, i.to_string().into_boxed_str(), i) +} diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index 35f967e83a..e5e3414048 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -742,9 +742,9 @@ fn pattern_to_vars_by_symbol( } } - NumLiteral(_, _) - | IntLiteral(_, _) - | FloatLiteral(_, _) + NumLiteral(_, _, _) + | IntLiteral(_, _, _) + | FloatLiteral(_, _, _) | StrLiteral(_) | Underscore | MalformedPattern(_, _) diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index 264e6fefe6..5dd8903993 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -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, i64), // Int and Float store a variable to generate better error messages - Int(Variable, Variable, i128), - Float(Variable, Variable, f64), + Int(Variable, Variable, Box, i128), + Float(Variable, Variable, Box, f64), Str(Box), List { elem_var: Variable, @@ -206,14 +206,23 @@ pub fn canonicalize_expr<'a>( use Expr::*; let (expr, output) = match expr { - ast::Expr::Num(string) => { - let answer = num_expr_from_result(var_store, finish_parsing_int(*string), region, env); + ast::Expr::Num(str) => { + let answer = num_expr_from_result( + var_store, + finish_parsing_int(*str).map(|int| (*str, int)), + 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, + finish_parsing_float(str).map(|f| (*str, f)), + 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, int as i128)), region, *base, env) + } + Err(e) => int_expr_from_result(var_store, Err(e), region, *base, env), + }; (answer, Output::default()) } @@ -1217,9 +1234,9 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> match 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 - other @ Num(_, _) - | other @ Int(_, _, _) - | other @ Float(_, _, _) + other @ Num(_, _, _) + | other @ Int(_, _, _, _) + | other @ Float(_, _, _, _) | other @ Str { .. } | other @ RuntimeError(_) | other @ EmptyRecord diff --git a/compiler/can/src/module.rs b/compiler/can/src/module.rs index fb5d2e8b3b..771f73611d 100644 --- a/compiler/can/src/module.rs +++ b/compiler/can/src/module.rs @@ -382,9 +382,9 @@ fn fix_values_captured_in_closure_pattern( } } Identifier(_) - | NumLiteral(_, _) - | IntLiteral(_, _) - | FloatLiteral(_, _) + | NumLiteral(_, _, _) + | IntLiteral(_, _, _) + | FloatLiteral(_, _, _) | StrLiteral(_) | Underscore | Shadowed(_, _) @@ -438,9 +438,9 @@ fn fix_values_captured_in_closure_expr( fix_values_captured_in_closure_expr(&mut loc_body.value, no_capture_symbols); } - Num(_, _) - | Int(_, _, _) - | Float(_, _, _) + Num(_, _, _) + | Int(_, _, _, _) + | Float(_, _, _, _) | Str(_) | Var(_) | EmptyRecord diff --git a/compiler/can/src/num.rs b/compiler/can/src/num.rs index f7d977be9f..aff1231def 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, i64), (&str, IntErrorKind)>, region: Region, env: &mut Env, ) -> Expr { match result { - Ok(int) => Expr::Num(var_store.fresh(), int), + Ok((str, num)) => Expr::Num(var_store.fresh(), (*str).into(), num), 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, i128), (&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, int)) => Expr::Int(var_store.fresh(), var_store.fresh(), (*str).into(), int), 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, f64), (&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, float)) => Expr::Float(var_store.fresh(), var_store.fresh(), (*str).into(), float), Err((raw, error)) => { let runtime_error = InvalidFloat(error, region, raw.into()); diff --git a/compiler/can/src/pattern.rs b/compiler/can/src/pattern.rs index 92b1ca611b..adc853bd69 100644 --- a/compiler/can/src/pattern.rs +++ b/compiler/can/src/pattern.rs @@ -25,9 +25,9 @@ pub enum Pattern { ext_var: Variable, destructs: Vec>, }, - IntLiteral(Variable, i64), - NumLiteral(Variable, i64), - FloatLiteral(Variable, f64), + IntLiteral(Variable, Box, i64), + NumLiteral(Variable, Box, i64), + FloatLiteral(Variable, Box, f64), StrLiteral(Box), Underscore, @@ -85,9 +85,9 @@ pub fn symbols_from_pattern_help(pattern: &Pattern, symbols: &mut Vec) { } } - NumLiteral(_, _) - | IntLiteral(_, _) - | FloatLiteral(_, _) + NumLiteral(_, _, _) + | IntLiteral(_, _, _) + | FloatLiteral(_, _, _) | StrLiteral(_) | Underscore | MalformedPattern(_, _) @@ -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 finish_parsing_float(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(), float), }, 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(), int), }, ptype => unsupported_pattern(env, ptype, region), }, @@ -223,11 +223,10 @@ 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(); + let i = if *is_negative { -int } else { int }; + Pattern::IntLiteral(var_store.fresh(), int_str, i) } }, ptype => unsupported_pattern(env, ptype, region), @@ -473,9 +472,9 @@ fn add_bindings_from_patterns( answer.push((*symbol, *region)); } } - NumLiteral(_, _) - | IntLiteral(_, _) - | FloatLiteral(_, _) + NumLiteral(_, _, _) + | IntLiteral(_, _, _) + | FloatLiteral(_, _, _) | StrLiteral(_) | Underscore | Shadowed(_, _) diff --git a/compiler/can/tests/test_can.rs b/compiler/can/tests/test_can.rs index a937af1c45..51dd68b8ac 100644 --- a/compiler/can/tests/test_can.rs +++ b/compiler/can/tests/test_can.rs @@ -32,7 +32,7 @@ mod test_can { let actual_out = can_expr_with(&arena, test_home(), input); match actual_out.loc_expr.value { - Expr::Float(_, _, actual) => { + Expr::Float(_, _, _, actual) => { assert_eq!(expected, actual); } actual => { @@ -46,7 +46,7 @@ mod test_can { let actual_out = can_expr_with(&arena, test_home(), input); match actual_out.loc_expr.value { - Expr::Int(_, _, actual) => { + Expr::Int(_, _, _, actual) => { assert_eq!(expected, actual); } actual => { @@ -60,7 +60,7 @@ mod test_can { let actual_out = can_expr_with(&arena, test_home(), input); match actual_out.loc_expr.value { - Expr::Num(_, actual) => { + Expr::Num(_, _, actual) => { assert_eq!(expected, actual); } actual => { diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index 24bf892102..77b8a3c82c 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -96,8 +96,8 @@ pub fn constrain_expr( expected: Expected, ) -> Constraint { match expr { - Int(var, precision, _) => int_literal(*var, *precision, expected, region), - Num(var, _) => exists( + Int(var, precision, _, _) => int_literal(*var, *precision, expected, region), + Num(var, _, _) => exists( vec![*var], Eq( crate::builtins::num_num(Type::Variable(*var)), @@ -106,7 +106,7 @@ pub fn constrain_expr( region, ), ), - Float(var, precision, _) => float_literal(*var, *precision, expected, region), + Float(var, precision, _, _) => float_literal(*var, *precision, expected, region), EmptyRecord => constrain_empty_record(region, expected), Expr::Record { record_var, fields } => { if fields.is_empty() { diff --git a/compiler/constrain/src/pattern.rs b/compiler/constrain/src/pattern.rs index 310a7a251c..f4adf7a803 100644 --- a/compiler/constrain/src/pattern.rs +++ b/compiler/constrain/src/pattern.rs @@ -56,9 +56,9 @@ fn headers_from_annotation_help( | Shadowed(_, _) | MalformedPattern(_, _) | UnsupportedPattern(_) - | NumLiteral(_, _) - | IntLiteral(_, _) - | FloatLiteral(_, _) + | NumLiteral(_, _, _) + | IntLiteral(_, _, _) + | FloatLiteral(_, _, _) | StrLiteral(_) => true, RecordDestructure { destructs, .. } => match annotation.value.shallow_dealias() { @@ -143,7 +143,7 @@ pub fn constrain_pattern( ); } - NumLiteral(var, _) => { + NumLiteral(var, _, _) => { state.vars.push(*var); state.constraints.push(Constraint::Pattern( @@ -154,7 +154,7 @@ pub fn constrain_pattern( )); } - IntLiteral(precision_var, _) => { + IntLiteral(precision_var, _, _) => { state.constraints.push(Constraint::Pattern( region, PatternCategory::Int, @@ -163,7 +163,7 @@ pub fn constrain_pattern( )); } - FloatLiteral(precision_var, _) => { + FloatLiteral(precision_var, _, _) => { state.constraints.push(Constraint::Pattern( region, PatternCategory::Float, diff --git a/compiler/gen_llvm/Cargo.toml b/compiler/gen_llvm/Cargo.toml index af5107b096..eaec8982bd 100644 --- a/compiler/gen_llvm/Cargo.toml +++ b/compiler/gen_llvm/Cargo.toml @@ -16,6 +16,7 @@ roc_builtins = { path = "../builtins" } roc_unify = { path = "../unify" } roc_solve = { path = "../solve" } roc_mono = { path = "../mono" } +roc_std = { path = "../../roc_std" } morphic_lib = { path = "../../vendor/morphic_lib" } im = "14" # im and im-rc should always have the same version! im-rc = "14" # im and im-rc should always have the same version! @@ -29,7 +30,6 @@ roc_parse = { path = "../parse" } roc_load = { path = "../load" } roc_reporting = { path = "../reporting" } roc_build = { path = "../build" } -roc_std = { path = "../../roc_std" } pretty_assertions = "0.5.1" maplit = "1.0.1" indoc = "0.3.3" diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 8627189b38..2e01908dcc 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -709,11 +709,6 @@ pub fn float_with_precision<'a, 'ctx, 'env>( precision: &Builtin, ) -> BasicValueEnum<'ctx> { match precision { - Builtin::Decimal => call_bitcode_fn( - env, - &[env.context.f64_type().const_float(value).into()], - bitcode::DEC_FROM_F64, - ), Builtin::Float64 => env.context.f64_type().const_float(value).into(), Builtin::Float32 => env.context.f32_type().const_float(value).into(), _ => panic!("Invalid layout for float literal = {:?}", precision), @@ -738,6 +733,11 @@ pub fn build_exp_literal<'a, 'ctx, 'env>( _ => panic!("Invalid layout for float literal = {:?}", layout), }, + Decimal(int) => env + .context + .i128_type() + .const_int(int.0 as u64, false) + .into(), Bool(b) => env.context.bool_type().const_int(*b as u64, false).into(), Byte(b) => env.context.i8_type().const_int(*b as u64, false).into(), Str(str_literal) => { diff --git a/compiler/mono/Cargo.toml b/compiler/mono/Cargo.toml index 9bd2127016..c227760d86 100644 --- a/compiler/mono/Cargo.toml +++ b/compiler/mono/Cargo.toml @@ -13,6 +13,7 @@ roc_types = { path = "../types" } roc_can = { path = "../can" } roc_unify = { path = "../unify" } roc_solve = { path = "../solve" } +roc_std = { path = "../../roc_std" } roc_problem = { path = "../problem" } ven_pretty = { path = "../../vendor/pretty" } morphic_lib = { path = "../../vendor/morphic_lib" } diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index 8d62b2b4ab..467169b9bb 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -1169,7 +1169,7 @@ fn literal_spec( match literal { Str(_) => new_static_string(builder, block), - Int(_) | Float(_) | Bool(_) | Byte(_) => builder.add_make_tuple(block, &[]), + Int(_) | Float(_) | Decimal(_) | Bool(_) | Byte(_) => builder.add_make_tuple(block, &[]), } } diff --git a/compiler/mono/src/decision_tree.rs b/compiler/mono/src/decision_tree.rs index 67a9ab8bca..436eb43446 100644 --- a/compiler/mono/src/decision_tree.rs +++ b/compiler/mono/src/decision_tree.rs @@ -7,6 +7,7 @@ use roc_collections::all::{MutMap, MutSet}; use roc_module::ident::TagName; use roc_module::low_level::LowLevel; use roc_module::symbol::Symbol; +use roc_std::RocDec; /// COMPILE CASES @@ -85,8 +86,8 @@ enum Test<'a> { arguments: Vec<(Pattern<'a>, Layout<'a>)>, }, IsInt(i128), - // float patterns are stored as u64 so they are comparable/hashable IsFloat(u64), + IsDecimal(RocDec), IsStr(Box), IsBit(bool), IsByte { @@ -126,6 +127,11 @@ impl<'a> Hash for Test<'a> { tag_id.hash(state); num_alts.hash(state); } + IsDecimal(v) => { + // TODO: Is this okay? + state.write_u8(6); + v.0.hash(state); + } } } } @@ -302,6 +308,7 @@ fn tests_are_complete_help(last_test: &Test, number_of_tests: usize) -> bool { Test::IsBit(_) => number_of_tests == 2, Test::IsInt(_) => false, Test::IsFloat(_) => false, + Test::IsDecimal(_) => false, Test::IsStr(_) => false, } } @@ -556,6 +563,7 @@ fn test_at_path<'a>( }, IntLiteral(v) => IsInt(*v), FloatLiteral(v) => IsFloat(*v), + DecimalLiteral(v) => IsDecimal(*v), StrLiteral(v) => IsStr(v.clone()), }; @@ -823,6 +831,18 @@ fn to_relevant_branch_help<'a>( _ => None, }, + DecimalLiteral(dec) => match test { + IsDecimal(test_dec) if dec.0 == test_dec.0 => { + start.extend(end); + Some(Branch { + goal: branch.goal, + guard: branch.guard.clone(), + patterns: start, + }) + } + _ => None, + }, + BitLiteral { value: bit, .. } => match test { IsBit(test_bit) if bit == *test_bit => { start.extend(end); @@ -910,6 +930,7 @@ fn needs_tests(pattern: &Pattern) -> bool { | EnumLiteral { .. } | IntLiteral(_) | FloatLiteral(_) + | DecimalLiteral(_) | StrLiteral(_) => true, } } @@ -1279,6 +1300,14 @@ fn test_to_equality<'a>( (stores, lhs_symbol, rhs_symbol, None) } + Test::IsDecimal(test_dec) => { + let lhs = Expr::Literal(Literal::Int(test_dec.0)); + let lhs_symbol = env.unique_symbol(); + stores.push((lhs_symbol, Layout::Builtin(Builtin::Int128), lhs)); + + (stores, lhs_symbol, rhs_symbol, None) + } + Test::IsByte { tag_id: test_byte, .. } => { diff --git a/compiler/mono/src/exhaustive.rs b/compiler/mono/src/exhaustive.rs index 14d70270bf..51b20d32aa 100644 --- a/compiler/mono/src/exhaustive.rs +++ b/compiler/mono/src/exhaustive.rs @@ -2,6 +2,7 @@ use crate::ir::DestructType; use roc_collections::all::{Index, MutMap}; use roc_module::ident::{Lowercase, TagName}; use roc_region::all::{Located, Region}; +use roc_std::RocDec; use self::Pattern::*; @@ -56,6 +57,7 @@ pub enum Literal { Bit(bool), Byte(u8), Float(u64), + Decimal(RocDec), Str(Box), } @@ -65,6 +67,7 @@ fn simplify(pattern: &crate::ir::Pattern) -> Pattern { match pattern { IntLiteral(v) => Literal(Literal::Int(*v)), FloatLiteral(v) => Literal(Literal::Float(*v)), + DecimalLiteral(v) => Literal(Literal::Decimal(*v)), StrLiteral(v) => Literal(Literal::Str(v.clone())), // To make sure these are exhaustive, we have to "fake" a union here diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index b6bffeda60..daade748e6 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -14,6 +14,7 @@ use roc_module::low_level::LowLevel; use roc_module::symbol::{IdentIds, ModuleId, Symbol}; use roc_problem::can::RuntimeError; use roc_region::all::{Located, Region}; +use roc_std::RocDec; use roc_types::solved_types::SolvedType; use roc_types::subs::{Content, FlatType, Subs, Variable, VariableSubsSlice}; use std::collections::HashMap; @@ -1024,6 +1025,7 @@ pub enum Literal<'a> { // Literals Int(i128), Float(f64), + Decimal(RocDec), Str(&'a str), /// Closed tag unions containing exactly two (0-arity) tags compile to Expr::Bool, /// so they can (at least potentially) be emitted as 1-bit machine bools. @@ -1205,6 +1207,8 @@ impl<'a> Literal<'a> { match self { Int(lit) => alloc.text(format!("{}i64", lit)), Float(lit) => alloc.text(format!("{}f64", lit)), + // TODO: Add proper Dec.to_str + Decimal(lit) => alloc.text(format!("{}Dec", lit.0)), Bool(lit) => alloc.text(format!("{}", lit)), Byte(lit) => alloc.text(format!("{}u8", lit)), Str(lit) => alloc.text(format!("{:?}", lit)), @@ -1702,7 +1706,7 @@ fn pattern_to_when<'a>( (symbol, Located::at_zero(wrapped_body)) } - IntLiteral(_, _) | NumLiteral(_, _) | FloatLiteral(_, _) | StrLiteral(_) => { + IntLiteral(_, _, _) | NumLiteral(_, _, _) | FloatLiteral(_, _, _) | StrLiteral(_) => { // These patters are refutable, and thus should never occur outside a `when` expression // They should have been replaced with `UnsupportedPattern` during canonicalization unreachable!("refutable pattern {:?} where irrefutable pattern is expected. This should never happen!", pattern.value) @@ -2738,17 +2742,17 @@ pub fn with_hole<'a>( let arena = env.arena; match can_expr { - Int(_, precision, num) => { + Int(_, precision, _, int) => { match num_argument_to_int_or_float(env.subs, env.ptr_bytes, precision, false) { IntOrFloat::SignedIntType(precision) => Stmt::Let( assigned, - Expr::Literal(Literal::Int(num)), + Expr::Literal(Literal::Int(int)), Layout::Builtin(int_precision_to_builtin(precision)), hole, ), IntOrFloat::UnsignedIntType(precision) => Stmt::Let( assigned, - Expr::Literal(Literal::Int(num)), + Expr::Literal(Literal::Int(int)), Layout::Builtin(int_precision_to_builtin(precision)), hole, ), @@ -2756,20 +2760,26 @@ pub fn with_hole<'a>( } } - Float(_, precision, num) => { + Float(_, precision, float_str, float) => { match num_argument_to_int_or_float(env.subs, env.ptr_bytes, precision, true) { IntOrFloat::BinaryFloatType(precision) => Stmt::Let( assigned, - Expr::Literal(Literal::Float(num as f64)), + Expr::Literal(Literal::Float(float)), Layout::Builtin(float_precision_to_builtin(precision)), hole, ), - IntOrFloat::DecimalFloatType => Stmt::Let( - assigned, - Expr::Literal(Literal::Float(num as f64)), - Layout::Builtin(Builtin::Decimal), - hole, - ), + IntOrFloat::DecimalFloatType => { + let dec = match RocDec::from_str(&float_str) { + Some(d) => d, + None => panic!("Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message", float_str), + }; + Stmt::Let( + assigned, + Expr::Literal(Literal::Decimal(dec)), + Layout::Builtin(Builtin::Decimal), + hole, + ) + } _ => unreachable!("unexpected float precision for integer"), } } @@ -2781,9 +2791,8 @@ pub fn with_hole<'a>( hole, ), - Num(var, num) => { + Num(var, num_str, num) => { // first figure out what kind of number this is - match num_argument_to_int_or_float(env.subs, env.ptr_bytes, var, false) { IntOrFloat::SignedIntType(precision) => Stmt::Let( assigned, @@ -2803,12 +2812,18 @@ pub fn with_hole<'a>( Layout::Builtin(float_precision_to_builtin(precision)), hole, ), - IntOrFloat::DecimalFloatType => Stmt::Let( - assigned, - Expr::Literal(Literal::Float(num as f64)), - Layout::Builtin(Builtin::Decimal), - hole, - ), + IntOrFloat::DecimalFloatType => { + let dec = match RocDec::from_str(&num_str) { + Some(d) => d, + None => panic!("Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message", num_str), + }; + Stmt::Let( + assigned, + Expr::Literal(Literal::Decimal(dec)), + Layout::Builtin(Builtin::Decimal), + hole, + ) + } } } LetNonRec(def, cont, _) => { @@ -5577,6 +5592,7 @@ fn store_pattern_help<'a>( } IntLiteral(_) | FloatLiteral(_) + | DecimalLiteral(_) | EnumLiteral { .. } | BitLiteral { .. } | StrLiteral(_) => { @@ -5711,6 +5727,7 @@ fn store_tag_pattern<'a>( } IntLiteral(_) | FloatLiteral(_) + | DecimalLiteral(_) | EnumLiteral { .. } | BitLiteral { .. } | StrLiteral(_) => {} @@ -5786,6 +5803,7 @@ fn store_newtype_pattern<'a>( } IntLiteral(_) | FloatLiteral(_) + | DecimalLiteral(_) | EnumLiteral { .. } | BitLiteral { .. } | StrLiteral(_) => {} @@ -5861,6 +5879,7 @@ fn store_record_destruct<'a>( } IntLiteral(_) | FloatLiteral(_) + | DecimalLiteral(_) | EnumLiteral { .. } | BitLiteral { .. } | StrLiteral(_) => { @@ -6832,6 +6851,7 @@ pub enum Pattern<'a> { Underscore, IntLiteral(i128), FloatLiteral(u64), + DecimalLiteral(RocDec), BitLiteral { value: bool, tag_name: TagName, @@ -6908,8 +6928,26 @@ fn from_can_pattern_help<'a>( match can_pattern { Underscore => Ok(Pattern::Underscore), Identifier(symbol) => Ok(Pattern::Identifier(*symbol)), - IntLiteral(_, int) => Ok(Pattern::IntLiteral(*int as i128)), - FloatLiteral(_, float) => Ok(Pattern::FloatLiteral(f64::to_bits(*float))), + IntLiteral(_, _, int) => Ok(Pattern::IntLiteral(*int as i128)), + FloatLiteral(var, float_str, float) => { + // TODO: Can I reuse num_argument_to_int_or_float here if I pass in true? + match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, true) { + IntOrFloat::SignedIntType(_) => { + panic!("Invalid percision for float literal = {:?}", var) + } + IntOrFloat::UnsignedIntType(_) => { + panic!("Invalid percision for float literal = {:?}", var) + } + IntOrFloat::BinaryFloatType(_) => Ok(Pattern::FloatLiteral(f64::to_bits(*float))), + IntOrFloat::DecimalFloatType => { + let dec = match RocDec::from_str(float_str) { + Some(d) => d, + None => panic!("Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message", float_str), + }; + Ok(Pattern::DecimalLiteral(dec)) + } + } + } StrLiteral(v) => Ok(Pattern::StrLiteral(v.clone())), Shadowed(region, ident) => Err(RuntimeError::Shadowing { original_region: *region, @@ -6920,12 +6958,18 @@ fn from_can_pattern_help<'a>( // TODO preserve malformed problem information here? Err(RuntimeError::UnsupportedPattern(*region)) } - NumLiteral(var, num) => { + NumLiteral(var, num_str, num) => { match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, false) { IntOrFloat::SignedIntType(_) => Ok(Pattern::IntLiteral(*num as i128)), IntOrFloat::UnsignedIntType(_) => Ok(Pattern::IntLiteral(*num as i128)), IntOrFloat::BinaryFloatType(_) => Ok(Pattern::FloatLiteral(*num as u64)), - IntOrFloat::DecimalFloatType => Ok(Pattern::FloatLiteral(*num as u64)), + IntOrFloat::DecimalFloatType => { + let dec = match RocDec::from_str(num_str) { + Some(d) => d, + None => panic!("Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message", num_str), + }; + Ok(Pattern::DecimalLiteral(dec)) + } } } diff --git a/compiler/reporting/src/error/mono.rs b/compiler/reporting/src/error/mono.rs index 2f69644598..9f365c55ae 100644 --- a/compiler/reporting/src/error/mono.rs +++ b/compiler/reporting/src/error/mono.rs @@ -143,6 +143,8 @@ fn pattern_to_doc_help<'b>( Bit(false) => alloc.text("False"), Byte(b) => alloc.text(b.to_string()), Float(f) => alloc.text(f.to_string()), + // TODO: Proper Dec.to_str + Decimal(d) => alloc.text(d.0.to_string()), Str(s) => alloc.string(s.into()), }, Ctor(union, tag_id, args) => { diff --git a/examples/.gitignore b/examples/.gitignore index a7b3b64e43..874bbd86e5 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -4,4 +4,3 @@ app libhost.a roc_app.ll roc_app.bc -effect-example diff --git a/examples/benchmarks/platform/host.zig b/examples/benchmarks/platform/host.zig index 731863b198..7d6bba7632 100644 --- a/examples/benchmarks/platform/host.zig +++ b/examples/benchmarks/platform/host.zig @@ -29,8 +29,8 @@ extern fn roc__mainForHost_1_Fx_caller(*const u8, [*]u8, [*]u8) void; extern fn roc__mainForHost_1_Fx_size() i64; extern fn roc__mainForHost_1_Fx_result_size() i64; -const Align = usize; -extern fn malloc(size: usize) callconv(.C) ?*c_void; +const Align = extern struct { a: usize, b: usize }; +extern fn malloc(size: usize) callconv(.C) ?*align(@alignOf(Align)) c_void; extern fn realloc(c_ptr: [*]align(@alignOf(Align)) u8, size: usize) callconv(.C) ?*c_void; extern fn free(c_ptr: [*]align(@alignOf(Align)) u8) callconv(.C) void; diff --git a/examples/cli/.gitignore b/examples/cli/.gitignore new file mode 100644 index 0000000000..fa11a6a9c5 --- /dev/null +++ b/examples/cli/.gitignore @@ -0,0 +1 @@ +echo diff --git a/examples/cli/Echo.roc b/examples/cli/Echo.roc new file mode 100644 index 0000000000..1dbb742a07 --- /dev/null +++ b/examples/cli/Echo.roc @@ -0,0 +1,18 @@ +#!/usr/bin/env roc + +app "echo" + packages { base: "platform" } + imports [ base.Task.{ Task, await }, base.Stdout, base.Stdin ] + provides [ main ] to base + +main : Task {} * +main = + {} <- await (Stdout.line "What's your first name?") + + firstName <- await Stdin.line + + {} <- await (Stdout.line "What's your last name?") + + lastName <- await Stdin.line + + Stdout.line "Hi, \(firstName) \(lastName)!" diff --git a/examples/cli/cli-example b/examples/cli/cli-example new file mode 100755 index 0000000000..28e07a40f7 Binary files /dev/null and b/examples/cli/cli-example differ diff --git a/examples/cli/hello-world b/examples/cli/hello-world new file mode 100755 index 0000000000..c3248ebe2c Binary files /dev/null and b/examples/cli/hello-world differ diff --git a/examples/cli/platform/Cargo.lock b/examples/cli/platform/Cargo.lock new file mode 100644 index 0000000000..cfd1e1e09d --- /dev/null +++ b/examples/cli/platform/Cargo.lock @@ -0,0 +1,21 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "host" +version = "0.1.0" +dependencies = [ + "libc", + "roc_std", +] + +[[package]] +name = "libc" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5" + +[[package]] +name = "roc_std" +version = "0.1.0" diff --git a/examples/cli/platform/Cargo.toml b/examples/cli/platform/Cargo.toml new file mode 100644 index 0000000000..ad2bc7c449 --- /dev/null +++ b/examples/cli/platform/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "host" +version = "0.1.0" +authors = ["The Roc Contributors"] +license = "UPL-1.0" +edition = "2018" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +roc_std = { path = "../../../roc_std" } +libc = "0.2" + +[workspace] diff --git a/examples/cli/platform/Package-Config.roc b/examples/cli/platform/Package-Config.roc new file mode 100644 index 0000000000..1efb7e95fa --- /dev/null +++ b/examples/cli/platform/Package-Config.roc @@ -0,0 +1,14 @@ +platform examples/cli + requires {}{ main : Task {} [] } # TODO FIXME + exposes [] + packages {} + imports [ Task.{ Task } ] + provides [ mainForHost ] + effects fx.Effect + { + putLine : Str -> Effect {}, + getLine : Effect Str + } + +mainForHost : Task {} [] as Fx +mainForHost = main diff --git a/examples/cli/platform/Stdin.roc b/examples/cli/platform/Stdin.roc new file mode 100644 index 0000000000..b46406c6de --- /dev/null +++ b/examples/cli/platform/Stdin.roc @@ -0,0 +1,6 @@ +interface Stdin + exposes [ line ] + imports [ fx.Effect, Task ] + +line : Task.Task Str * +line = Effect.after Effect.getLine Task.succeed # TODO FIXME Effect.getLine should suffice diff --git a/examples/cli/platform/Stdout.roc b/examples/cli/platform/Stdout.roc new file mode 100644 index 0000000000..dbde579ffc --- /dev/null +++ b/examples/cli/platform/Stdout.roc @@ -0,0 +1,9 @@ +interface Stdout + exposes [ line ] + imports [ fx.Effect, Task.{ Task } ] + +# line : Str -> Task.Task {} * +# line = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {}) + +line : Str -> Task {} * +line = \str -> Effect.map (Effect.putLine str) (\_ -> Ok {}) diff --git a/examples/cli/platform/Task.roc b/examples/cli/platform/Task.roc new file mode 100644 index 0000000000..d3f996bc8a --- /dev/null +++ b/examples/cli/platform/Task.roc @@ -0,0 +1,44 @@ +interface Task + exposes [ Task, succeed, fail, await, map, onFail, attempt ] + imports [ fx.Effect ] + + +Task ok err : Effect.Effect (Result ok err) + + +succeed : val -> Task val * +succeed = \val -> + Effect.always (Ok val) + + +fail : err -> Task * err +fail = \val -> + Effect.always (Err val) + +attempt : Task a b, (Result a b -> Task c d) -> Task c d +attempt = \effect, transform -> + Effect.after effect \result -> + when result is + Ok ok -> transform (Ok ok) + Err err -> transform (Err err) + +await : Task a err, (a -> Task b err) -> Task b err +await = \effect, transform -> + Effect.after effect \result -> + when result is + Ok a -> transform a + Err err -> Task.fail err + +onFail : Task ok a, (a -> Task ok b) -> Task ok b +onFail = \effect, transform -> + Effect.after effect \result -> + when result is + Ok a -> Task.succeed a + Err err -> transform err + +map : Task a err, (a -> b) -> Task b err +map = \effect, transform -> + Effect.after effect \result -> + when result is + Ok a -> Task.succeed (transform a) + Err err -> Task.fail err diff --git a/examples/cli/platform/host.c b/examples/cli/platform/host.c new file mode 100644 index 0000000000..0378c69589 --- /dev/null +++ b/examples/cli/platform/host.c @@ -0,0 +1,7 @@ +#include + +extern int rust_main(); + +int main() { + return rust_main(); +} diff --git a/examples/cli/platform/src/lib.rs b/examples/cli/platform/src/lib.rs new file mode 100644 index 0000000000..2a98179f8e --- /dev/null +++ b/examples/cli/platform/src/lib.rs @@ -0,0 +1,139 @@ +#![allow(non_snake_case)] + +use core::alloc::Layout; +use core::ffi::c_void; +use core::mem::MaybeUninit; +use libc; +use roc_std::{RocCallResult, RocStr}; +use std::ffi::CStr; +use std::os::raw::c_char; + +extern "C" { + #[link_name = "roc__mainForHost_1_exposed"] + fn roc_main(output: *mut u8) -> (); + + #[link_name = "roc__mainForHost_size"] + fn roc_main_size() -> i64; + + #[link_name = "roc__mainForHost_1_Fx_caller"] + fn call_Fx(flags: *const u8, closure_data: *const u8, output: *mut u8) -> (); + + #[allow(dead_code)] + #[link_name = "roc__mainForHost_1_Fx_size"] + fn size_Fx() -> i64; + + #[link_name = "roc__mainForHost_1_Fx_result_size"] + fn size_Fx_result() -> i64; +} + +#[no_mangle] +pub unsafe fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void { + libc::malloc(size) +} + +#[no_mangle] +pub unsafe fn roc_realloc( + c_ptr: *mut c_void, + new_size: usize, + _old_size: usize, + _alignment: u32, +) -> *mut c_void { + libc::realloc(c_ptr, new_size) +} + +#[no_mangle] +pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) { + libc::free(c_ptr) +} + +#[no_mangle] +pub unsafe fn roc_panic(c_ptr: *mut c_void, tag_id: u32) { + match tag_id { + 0 => { + let slice = CStr::from_ptr(c_ptr as *const c_char); + let string = slice.to_str().unwrap(); + eprintln!("Roc hit a panic: {}", string); + std::process::exit(1); + } + _ => todo!(), + } +} + +#[no_mangle] +pub fn rust_main() -> isize { + let size = unsafe { roc_main_size() } as usize; + let layout = Layout::array::(size).unwrap(); + + unsafe { + // TODO allocate on the stack if it's under a certain size + let buffer = std::alloc::alloc(layout); + + roc_main(buffer); + + let output = buffer as *mut RocCallResult<()>; + + match (&*output).into() { + Ok(()) => { + let closure_data_ptr = buffer.offset(8); + let result = call_the_closure(closure_data_ptr as *const u8); + + std::alloc::dealloc(buffer, layout); + + result + } + Err(msg) => { + std::alloc::dealloc(buffer, layout); + + panic!("Roc failed with message: {}", msg); + } + } + }; + + // Exit code + 0 +} + +unsafe fn call_the_closure(closure_data_ptr: *const u8) -> i64 { + let size = size_Fx_result() as usize; + let layout = Layout::array::(size).unwrap(); + let buffer = std::alloc::alloc(layout) as *mut u8; + + call_Fx( + // This flags pointer will never get dereferenced + MaybeUninit::uninit().as_ptr(), + closure_data_ptr as *const u8, + buffer as *mut u8, + ); + + let output = &*(buffer as *mut RocCallResult<()>); + + match output.into() { + Ok(_) => { + std::alloc::dealloc(buffer, layout); + 0 + } + Err(e) => panic!("failed with {}", e), + } +} + +#[no_mangle] +pub fn roc_fx_getLine() -> RocStr { + use std::io::{self, BufRead}; + + let stdin = io::stdin(); + let line1 = stdin.lock().lines().next().unwrap().unwrap(); + + RocStr::from_slice(line1.as_bytes()) +} + +#[no_mangle] +pub fn roc_fx_putLine(line: RocStr) -> () { + let bytes = line.as_slice(); + let string = unsafe { std::str::from_utf8_unchecked(bytes) }; + println!("{}", string); + + // don't mess with the refcount! + core::mem::forget(line); + + () +} diff --git a/examples/hello-zig/platform/host.zig b/examples/hello-zig/platform/host.zig index 5c2db73c2d..8384c996d3 100644 --- a/examples/hello-zig/platform/host.zig +++ b/examples/hello-zig/platform/host.zig @@ -19,9 +19,10 @@ comptime { } } -extern fn malloc(size: usize) callconv(.C) ?*c_void; -extern fn realloc(c_ptr: [*]align(@alignOf(usize)) u8, size: usize) callconv(.C) ?*c_void; -extern fn free(c_ptr: [*]align(@alignOf(usize)) u8) callconv(.C) void; +const Align = extern struct { a: usize, b: usize }; +extern fn malloc(size: usize) callconv(.C) ?*align(@alignOf(Align)) c_void; +extern fn realloc(c_ptr: [*]align(@alignOf(Align)) u8, size: usize) callconv(.C) ?*c_void; +extern fn free(c_ptr: [*]align(@alignOf(Align)) u8) callconv(.C) void; export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void { _ = alignment; @@ -33,14 +34,13 @@ export fn roc_realloc(c_ptr: *c_void, old_size: usize, new_size: usize, alignmen _ = old_size; _ = alignment; - return realloc(@alignCast(@alignOf(usize), @ptrCast([*]u8, c_ptr)), new_size); + return realloc(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)), new_size); } export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void { _ = alignment; - const ptr = @alignCast(@alignOf(usize), @ptrCast([*]u8, c_ptr)); - free(ptr); + free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr))); } export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void { diff --git a/roc_std/src/lib.rs b/roc_std/src/lib.rs index 16d1eb4660..c5b1f3e87a 100644 --- a/roc_std/src/lib.rs +++ b/roc_std/src/lib.rs @@ -584,6 +584,10 @@ impl RocStr { let raw_ptr = Self::get_element_ptr(raw_ptr as *mut u8); + // write the refcount + let refcount_ptr = raw_ptr as *mut isize; + *(refcount_ptr.offset(-1)) = isize::MIN; + { // NOTE: using a memcpy here causes weird issues let target_ptr = raw_ptr as *mut u8; @@ -832,11 +836,9 @@ impl RocDec { } }; - let after_point = match parts.next() { - Some(answer) if answer.len() <= Self::DECIMAL_PLACES as usize => answer, - _ => { - return None; - } + let opt_after_point = match parts.next() { + Some(answer) if answer.len() <= Self::DECIMAL_PLACES as usize => Some(answer), + _ => None, }; // There should have only been one "." in the string! @@ -845,22 +847,27 @@ impl RocDec { } // Calculate the low digits - the ones after the decimal point. - let lo = match after_point.parse::() { - Ok(answer) => { - // Translate e.g. the 1 from 0.1 into 10000000000000000000 - // by "restoring" the elided trailing zeroes to the number! - let trailing_zeroes = Self::DECIMAL_PLACES as usize - after_point.len(); - let lo = answer * 10i128.pow(trailing_zeroes as u32); + let lo = match opt_after_point { + Some(after_point) => { + match after_point.parse::() { + Ok(answer) => { + // Translate e.g. the 1 from 0.1 into 10000000000000000000 + // by "restoring" the elided trailing zeroes to the number! + let trailing_zeroes = Self::DECIMAL_PLACES as usize - after_point.len(); + let lo = answer * 10i128.pow(trailing_zeroes as u32); - if !before_point.starts_with('-') { - lo - } else { - -lo + if !before_point.starts_with('-') { + lo + } else { + -lo + } + } + Err(_) => { + return None; + } } } - Err(_) => { - return None; - } + None => 0, }; // Calculate the high digits - the ones before the decimal point.