diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 8d9a0ae2f1..007c48bee8 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -212,7 +212,8 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap { /// Num.maxInt : Int fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def { let int_var = var_store.fresh(); - let body = Int(int_var, i64::MAX); + let int_percision_var = var_store.fresh(); + let body = Int(int_var, int_percision_var, i64::MAX); Def { annotation: None, @@ -226,7 +227,8 @@ fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def { /// Num.minInt : Int fn num_min_int(symbol: Symbol, var_store: &mut VarStore) -> Def { let int_var = var_store.fresh(); - let body = Int(int_var, i64::MIN); + let int_percision_var = var_store.fresh(); + let body = Int(int_var, int_percision_var, i64::MIN); Def { annotation: None, @@ -846,7 +848,7 @@ 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(), 1)), + (arg_var, Int(var_store.fresh(), var_store.fresh(), 1)), ( arg_var, RunLowLevel { @@ -930,6 +932,7 @@ fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def { let bool_var = var_store.fresh(); let float_var = var_store.fresh(); let unbound_zero_var = var_store.fresh(); + let percision_var = var_store.fresh(); let ret_var = var_store.fresh(); let body = If { @@ -943,7 +946,7 @@ fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def { op: LowLevel::NotEq, args: vec![ (float_var, Var(Symbol::ARG_1)), - (float_var, Float(unbound_zero_var, 0.0)), + (float_var, Float(unbound_zero_var, percision_var, 0.0)), ], ret_var: bool_var, }, @@ -1896,6 +1899,7 @@ fn num_div_float(symbol: Symbol, var_store: &mut VarStore) -> Def { let bool_var = var_store.fresh(); let num_var = var_store.fresh(); let unbound_zero_var = var_store.fresh(); + let percision_var = var_store.fresh(); let ret_var = var_store.fresh(); let body = If { @@ -1909,7 +1913,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, 0.0)), + (num_var, Float(unbound_zero_var, percision_var, 0.0)), ], ret_var: bool_var, }, @@ -1958,6 +1962,7 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def { let bool_var = var_store.fresh(); let num_var = var_store.fresh(); let unbound_zero_var = var_store.fresh(); + let unbound_zero_percision_var = var_store.fresh(); let ret_var = var_store.fresh(); let body = If { @@ -1971,7 +1976,10 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def { op: LowLevel::NotEq, args: vec![ (num_var, Var(Symbol::ARG_2)), - (num_var, Int(unbound_zero_var, 0)), + ( + num_var, + Int(unbound_zero_var, unbound_zero_percision_var, 0), + ), ], ret_var: bool_var, }, @@ -2025,6 +2033,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def { let list_var = var_store.fresh(); let len_var = var_store.fresh(); let zero_var = var_store.fresh(); + let zero_percision_var = var_store.fresh(); let list_elem_var = var_store.fresh(); let ret_var = var_store.fresh(); @@ -2039,7 +2048,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def { RunLowLevel { op: LowLevel::NotEq, args: vec![ - (len_var, Int(zero_var, 0)), + (len_var, Int(zero_var, zero_percision_var, 0)), ( len_var, RunLowLevel { @@ -2061,7 +2070,10 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def { // List.#getUnsafe list 0 RunLowLevel { op: LowLevel::ListGetUnsafe, - args: vec![(list_var, Var(Symbol::ARG_1)), (len_var, Int(zero_var, 0))], + args: vec![ + (list_var, Var(Symbol::ARG_1)), + (len_var, Int(zero_var, zero_percision_var, 0)), + ], ret_var: list_elem_var, }, ], @@ -2102,6 +2114,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def { let list_var = var_store.fresh(); let len_var = var_store.fresh(); let num_var = var_store.fresh(); + let num_percision_var = var_store.fresh(); let list_elem_var = var_store.fresh(); let ret_var = var_store.fresh(); @@ -2116,7 +2129,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def { RunLowLevel { op: LowLevel::NotEq, args: vec![ - (len_var, Int(num_var, 0)), + (len_var, Int(num_var, num_percision_var, 0)), ( len_var, RunLowLevel { @@ -2155,7 +2168,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def { ret_var: len_var, }, ), - (arg_var, Int(num_var, 1)), + (arg_var, Int(num_var, num_percision_var, 1)), ], ret_var: len_var, }, diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index cf1cc2d02b..bea498d237 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -56,8 +56,8 @@ pub enum Expr { Num(Variable, i64), // Int and Float store a variable to generate better error messages - Int(Variable, i64), - Float(Variable, f64), + Int(Variable, Variable, i64), + Float(Variable, Variable, f64), Str(InlinableString), List { list_var: Variable, // required for uniqueness of the list @@ -1170,8 +1170,8 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: 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 @ Int(_, _, _) + | other @ Float(_, _, _) | other @ Str { .. } | other @ RuntimeError(_) | other @ EmptyRecord diff --git a/compiler/can/src/module.rs b/compiler/can/src/module.rs index 4f9c2f4906..3c59d13f7b 100644 --- a/compiler/can/src/module.rs +++ b/compiler/can/src/module.rs @@ -414,8 +414,8 @@ fn fix_values_captured_in_closure_expr( } Num(_, _) - | Int(_, _) - | Float(_, _) + | Int(_, _, _) + | Float(_, _, _) | Str(_) | Var(_) | EmptyRecord diff --git a/compiler/can/src/num.rs b/compiler/can/src/num.rs index 9a0b810a6f..8d42b9775a 100644 --- a/compiler/can/src/num.rs +++ b/compiler/can/src/num.rs @@ -45,7 +45,7 @@ pub fn int_expr_from_result( ) -> Expr { // Int stores a variable to generate better error messages match result { - Ok(int) => Expr::Int(var_store.fresh(), int), + Ok(int) => Expr::Int(var_store.fresh(), var_store.fresh(), int), Err((raw, error)) => { let runtime_error = InvalidInt(error, base, region, raw.into()); @@ -65,7 +65,7 @@ pub fn float_expr_from_result( ) -> Expr { // Float stores a variable to generate better error messages match result { - Ok(float) => Expr::Float(var_store.fresh(), float), + Ok(float) => Expr::Float(var_store.fresh(), var_store.fresh(), float), Err((raw, error)) => { let runtime_error = InvalidFloat(error, region, raw.into()); diff --git a/compiler/constrain/src/builtins.rs b/compiler/constrain/src/builtins.rs index 7e84af8864..2987bfa174 100644 --- a/compiler/constrain/src/builtins.rs +++ b/compiler/constrain/src/builtins.rs @@ -11,30 +11,50 @@ use roc_types::types::Reason; use roc_types::types::Type::{self, *}; #[inline(always)] -pub fn int_literal(num_var: Variable, expected: Expected, region: Region) -> Constraint { +pub fn int_literal( + num_var: Variable, + percision_var: Variable, + expected: Expected, + region: Region, +) -> Constraint { let num_type = Variable(num_var); let reason = Reason::IntLiteral; - let expected_literal = ForReason(reason, num_int(Type::Variable(num_var)), region); exists( vec![num_var], And(vec![ - Eq(num_type.clone(), expected_literal, Category::Int, region), + Eq( + num_type.clone(), + ForReason(reason, num_int(Type::Variable(percision_var)), region), + Category::Int, + region, + ), Eq(num_type, expected, Category::Int, region), ]), ) } #[inline(always)] -pub fn float_literal(num_var: Variable, expected: Expected, region: Region) -> Constraint { +pub fn float_literal( + num_var: Variable, + percision_var: Variable, + expected: Expected, + region: Region, +) -> Constraint { let num_type = Variable(num_var); let reason = Reason::FloatLiteral; - let expected_literal = ForReason(reason, num_float(Type::Variable(num_var)), region); + + dbg!(&expected); exists( vec![num_var], And(vec![ - Eq(num_type.clone(), expected_literal, Category::Float, region), + Eq( + num_type.clone(), + ForReason(reason, num_float(Type::Variable(percision_var)), region), + Category::Float, + region, + ), Eq(num_type, expected, Category::Float, region), ]), ) diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index 000bbde915..2bde1b52da 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -96,7 +96,7 @@ pub fn constrain_expr( expected: Expected, ) -> Constraint { match expr { - Int(var, _) => int_literal(*var, expected, region), + Int(var, percision, _) => int_literal(*var, *percision, expected, region), Num(var, _) => exists( vec![*var], Eq( @@ -106,7 +106,7 @@ pub fn constrain_expr( region, ), ), - Float(var, _) => float_literal(*var, expected, region), + Float(var, percision, _) => float_literal(*var, *percision, expected, region), EmptyRecord => constrain_empty_record(region, expected), Expr::Record { record_var, fields } => { if fields.is_empty() { diff --git a/compiler/constrain/src/uniq.rs b/compiler/constrain/src/uniq.rs index 0dd8bc67a3..916c0e7150 100644 --- a/compiler/constrain/src/uniq.rs +++ b/compiler/constrain/src/uniq.rs @@ -426,6 +426,7 @@ fn unique_int( let num_uvar1 = var_store.fresh(); let num_uvar2 = var_store.fresh(); let num_uvar3 = var_store.fresh(); + let num_uvar4 = var_store.fresh(); let inner_type = Type::Variable(inner_var); let attr_inner_type = attr_type(Bool::variable(num_uvar1), inner_type); @@ -444,6 +445,7 @@ fn unique_float( let num_uvar1 = var_store.fresh(); let num_uvar2 = var_store.fresh(); let num_uvar3 = var_store.fresh(); + let num_uvar4 = var_store.fresh(); let inner_type = Type::Variable(inner_var); let attr_inner_type = attr_type(Bool::variable(num_uvar1), inner_type); @@ -484,7 +486,7 @@ pub fn constrain_expr( ]), ) } - Int(var, _) => { + Int(var, _, _) => { let (a, b, c, num_type) = unique_int(*var, var_store); exists( @@ -500,7 +502,7 @@ pub fn constrain_expr( ]), ) } - Float(var, _) => { + Float(var, _, _) => { let (a, b, c, num_type) = unique_float(*var, var_store); exists( diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index a14c7fae26..4888657b40 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -477,13 +477,27 @@ fn get_inplace_from_layout(layout: &Layout<'_>) -> InPlace { pub fn build_exp_literal<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + layout: &Layout<'_>, literal: &roc_mono::ir::Literal<'a>, ) -> BasicValueEnum<'ctx> { use roc_mono::ir::Literal::*; match literal { - Int(num) => env.context.i64_type().const_int(*num as u64, true).into(), - Float(num) => env.context.f64_type().const_float(*num).into(), + Int(int) => + (match layout { + Layout::Builtin(Builtin::Int128) => env.context.i128_type(), /* TODO file an issue: you can't currently have an int literal bigger than 64 bits long, and also (as we see here), you can't currently have (at least in Inkwell) a when-branch with an i128 literal in its pattren */ + Layout::Builtin(Builtin::Int64) => env.context.i64_type(), + Layout::Builtin(Builtin::Int32) => env.context.i32_type(), + Layout::Builtin(Builtin::Int16) => env.context.i16_type(), + Layout::Builtin(Builtin::Int8) => env.context.i8_type(), + _ => panic!("Invalid layout for int literal = {:?}", layout), + }).const_int(*int as u64, false).into(), + Float(num) => + (match layout { + Layout::Builtin(Builtin::Float64) => env.context.f64_type(), + Layout::Builtin(Builtin::Float32) => env.context.f32_type(), + _ => panic!("Invalid layout for float literal = {:?}", layout), + }).const_float(*num).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) => { @@ -622,7 +636,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( use roc_mono::ir::Expr::*; match expr { - Literal(literal) => build_exp_literal(env, literal), + Literal(literal) => build_exp_literal(env, layout, literal), RunLowLevel(op, symbols) => { run_low_level(env, layout_ids, scope, parent, layout, *op, symbols) } @@ -1298,6 +1312,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( Let(first_symbol, first_expr, first_layout, mut cont) => { let mut queue = Vec::new_in(env.arena); + queue.push((first_symbol, first_expr, first_layout)); while let Let(symbol, expr, layout, new_cont) = cont { @@ -1691,6 +1706,15 @@ fn build_switch_ir<'a, 'ctx, 'env>( .build_bitcast(full_cond, env.context.i64_type(), "") .into_int_value() } + Layout::Builtin(Builtin::Float32) => { + // float matches are done on the bit pattern + cond_layout = Layout::Builtin(Builtin::Int32); + let full_cond = load_symbol(env, scope, cond_symbol); + + builder + .build_bitcast(full_cond, env.context.i32_type(), "") + .into_int_value() + } Layout::Union(_) => { // we match on the discriminant, not the whole Tag cond_layout = Layout::Builtin(Builtin::Int64); @@ -3622,6 +3646,7 @@ fn build_int_unary_op<'a, 'ctx, 'env>( int_abs_raise_on_overflow(env, arg, arg_layout) } NumToFloat => { + // TODO: Handle differnt sized numbers // This is an Int, so we need to convert it. bd.build_cast( InstructionOpcode::SIToFP, @@ -3748,6 +3773,7 @@ fn build_float_unary_op<'a, 'ctx, 'env>( let bd = env.builder; + // TODO: Handle differnt sized floats match op { NumNeg => bd.build_float_neg(arg, "negate_float").into(), NumAbs => env.call_intrinsic(LLVM_FABS_F64, &[arg.into()]), diff --git a/compiler/gen/tests/gen_num.rs b/compiler/gen/tests/gen_num.rs index 2dc79e07f8..b713072af5 100644 --- a/compiler/gen/tests/gen_num.rs +++ b/compiler/gen/tests/gen_num.rs @@ -15,6 +15,113 @@ mod helpers; mod gen_num { use roc_std::RocOrder; + #[test] + fn i32_to_hex() { + assert_evals_to!( + indoc!( + r#" + f : I32 + f = 0x123 + + f + "# + ), + 0x123, + i32 + ); + } + + #[test] + fn i128_signed_int_alias() { + assert_evals_to!( + indoc!( + r#" + i : I128 + i = 15 + + i + "# + ), + 15, + i128 + ); + } + // TODO: Use indoc + #[test] + fn i64_signed_int_alias() { + assert_evals_to!("15 : I64", 15, i64); + } + #[test] + fn i32_signed_int_alias() { + assert_evals_to!("15 : I32", 15, i32); + } + #[test] + fn i16_signed_int_alias() { + assert_evals_to!("15 : I16", 15, i16); + } + #[test] + fn i8_signed_int_alias() { + assert_evals_to!("15 : I8", 15, i8); + } + + #[test] + fn i8_signed_int_alias_overflow() { + // TODO: How/where do we handle overflow + assert_evals_to!("(127 + 1): I8", -128, i8); + } + + #[test] + fn u128_unsigned_int_alias() { + assert_evals_to!("15 : U128", 15, u128); + } + #[test] + fn u64_unsigned_int_alias() { + assert_evals_to!("15 : U64", 15, u64); + } + #[test] + fn u32_unsigned_int_alias() { + assert_evals_to!("15 : U32", 15, u32); + } + #[test] + fn u16_unsigned_int_alias() { + assert_evals_to!("15 : U16", 15, u16); + } + #[test] + fn u8_unsigned_int_alias() { + assert_evals_to!("15 : u8", 15, u8); + } + + #[test] + fn f64_float_alias() { + assert_evals_to!( + indoc!( + r#" + f : F64 + f = 3.6 + + f + "# + ), + 3.6, + f64 + ); + } + #[test] + fn f32_float_alias() { + assert_evals_to!( + indoc!( + r#" + f : F32 + f = 3.6 + + f + "# + ), + 3.6, + f32 + ); + } + #[test] fn f64_sqrt() { // FIXME this works with normal types, but fails when checking uniqueness types diff --git a/compiler/mono/src/decision_tree.rs b/compiler/mono/src/decision_tree.rs index ec9fff2c5f..2e3701faa2 100644 --- a/compiler/mono/src/decision_tree.rs +++ b/compiler/mono/src/decision_tree.rs @@ -451,7 +451,7 @@ fn test_at_path<'a>(selected_path: &Path, branch: &Branch<'a>, all_tests: &mut V IntLiteral(v) => { all_tests.push(guarded(IsInt(*v))); } - FloatLiteral(v) => { + FloatLiteral(_, v) => { all_tests.push(IsFloat(*v)); } StrLiteral(v) => { @@ -655,7 +655,7 @@ fn to_relevant_branch_help<'a>( _ => None, }, - FloatLiteral(float) => match test { + FloatLiteral(_, float) => match test { IsFloat(test_float) if float == *test_float => { start.extend(end); Some(Branch { @@ -749,7 +749,7 @@ fn needs_tests<'a>(pattern: &Pattern<'a>) -> bool { | BitLiteral { .. } | EnumLiteral { .. } | IntLiteral(_) - | FloatLiteral(_) + | FloatLiteral(_, _) | StrLiteral(_) => true, } } diff --git a/compiler/mono/src/exhaustive.rs b/compiler/mono/src/exhaustive.rs index 70077c2199..64847454d2 100644 --- a/compiler/mono/src/exhaustive.rs +++ b/compiler/mono/src/exhaustive.rs @@ -49,7 +49,7 @@ fn simplify<'a>(pattern: &crate::ir::Pattern<'a>) -> Pattern { match pattern { IntLiteral(v) => Literal(Literal::Int(*v)), - FloatLiteral(v) => Literal(Literal::Float(*v)), + FloatLiteral(_, v) => Literal(Literal::Float(*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 689f907855..32a413786e 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -14,6 +14,26 @@ use roc_types::subs::{Content, FlatType, Subs, Variable}; use std::collections::HashMap; use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder}; +// Function return type does not match operand type of return inst! +// ret i64 %f1, !dbg !521 +// i32define private fastcc i32 @"#UserApp_main_1"() !dbg !517 { +// entry: +// %f = alloca i64 +// store i64 291, i64* %f, !dbg !521 +// %f1 = load i64, i64* %f, !dbg !521 +// ret i64 %f1, !dbg !521 +// } + +// Function return type does not match operand type of return inst! +// ret i32 %f1, !dbg !521 +// i64define private fastcc i64 @"#UserApp_main_1"() !dbg !517 { +// entry: +// %f = alloca i32 +// store i32 291, i32* %f, !dbg !521 +// %f1 = load i32, i32* %f, !dbg !521 +// ret i32 %f1, !dbg !521 +// } + pub const PRETTY_PRINT_IR_SYMBOLS: bool = false; #[derive(Clone, Debug, PartialEq)] @@ -2245,19 +2265,37 @@ pub fn with_hole<'a>( let arena = env.arena; match can_expr { - Int(_, num) => Stmt::Let( - assigned, - Expr::Literal(Literal::Int(num)), - Layout::Builtin(Builtin::Int64), - hole, - ), + Int(_, precision, num) => match num_argument_to_int_or_float(env.subs, precision) { + IntOrFloat::SignedIntType(precision) => Stmt::Let( + assigned, + Expr::Literal(Literal::Int(num)), + Layout::Builtin(int_precision_to_builtin(precision)), + hole, + ), + IntOrFloat::UnsignedIntType(precision) => Stmt::Let( + assigned, + Expr::Literal(Literal::Int(num)), + Layout::Builtin(int_precision_to_builtin(precision)), + hole, + ), + _ => unreachable!("unexpected float precision for integer"), + }, - Float(_, num) => Stmt::Let( - assigned, - Expr::Literal(Literal::Float(num)), - Layout::Builtin(Builtin::Float64), - hole, - ), + Float(_, precision, num) => match num_argument_to_int_or_float(env.subs, precision) { + IntOrFloat::BinaryFloatType(precision) => Stmt::Let( + assigned, + Expr::Literal(Literal::Float(num as f64)), + Layout::Builtin(float_precision_to_builtin(precision)), + hole, + ), + IntOrFloat::DecimalFloatType(precision) => Stmt::Let( + assigned, + Expr::Literal(Literal::Float(num as f64)), + Layout::Builtin(float_precision_to_builtin(precision)), + hole, + ), + _ => unreachable!("unexpected float precision for integer"), + }, Str(string) => Stmt::Let( assigned, @@ -2267,16 +2305,28 @@ pub fn with_hole<'a>( ), Num(var, num) => match num_argument_to_int_or_float(env.subs, var) { - IntOrFloat::IntType => Stmt::Let( + IntOrFloat::SignedIntType(precision) => Stmt::Let( assigned, Expr::Literal(Literal::Int(num)), - Layout::Builtin(Builtin::Int64), + Layout::Builtin(int_precision_to_builtin(precision)), hole, ), - IntOrFloat::FloatType => Stmt::Let( + IntOrFloat::UnsignedIntType(precision) => Stmt::Let( + assigned, + Expr::Literal(Literal::Int(num)), + Layout::Builtin(int_precision_to_builtin(precision)), + hole, + ), + IntOrFloat::BinaryFloatType(precision) => Stmt::Let( assigned, Expr::Literal(Literal::Float(num as f64)), - Layout::Builtin(Builtin::Float64), + Layout::Builtin(float_precision_to_builtin(precision)), + hole, + ), + IntOrFloat::DecimalFloatType(precision) => Stmt::Let( + assigned, + Expr::Literal(Literal::Float(num as f64)), + Layout::Builtin(float_precision_to_builtin(precision)), hole, ), }, @@ -4583,7 +4633,7 @@ fn store_pattern<'a>( // do nothing } IntLiteral(_) - | FloatLiteral(_) + | FloatLiteral(_, _) | EnumLiteral { .. } | BitLiteral { .. } | StrLiteral(_) => {} @@ -4623,7 +4673,7 @@ fn store_pattern<'a>( // ignore } IntLiteral(_) - | FloatLiteral(_) + | FloatLiteral(_, _) | EnumLiteral { .. } | BitLiteral { .. } | StrLiteral(_) => {} @@ -4737,7 +4787,7 @@ fn store_record_destruct<'a>( // internally. But `y` is never used, so we must make sure it't not stored/loaded. } IntLiteral(_) - | FloatLiteral(_) + | FloatLiteral(_, _) | EnumLiteral { .. } | BitLiteral { .. } | StrLiteral(_) => {} @@ -5408,7 +5458,7 @@ pub enum Pattern<'a> { Underscore, IntLiteral(i64), - FloatLiteral(u64), + FloatLiteral(Variable, u64), BitLiteral { value: bool, tag_name: TagName, @@ -5469,7 +5519,7 @@ pub fn from_can_pattern<'a>( Underscore => Pattern::Underscore, Identifier(symbol) => Pattern::Identifier(*symbol), IntLiteral(_, v) => Pattern::IntLiteral(*v), - FloatLiteral(_, v) => Pattern::FloatLiteral(f64::to_bits(*v)), + FloatLiteral(var, float) => Pattern::FloatLiteral(*var, f64::to_bits(*float)), StrLiteral(v) => Pattern::StrLiteral(v.clone()), Shadowed(region, ident) => Pattern::Shadowed(*region, ident.clone()), UnsupportedPattern(region) => Pattern::UnsupportedPattern(*region), @@ -5478,8 +5528,10 @@ pub fn from_can_pattern<'a>( Pattern::UnsupportedPattern(*region) } NumLiteral(var, num) => match num_argument_to_int_or_float(env.subs, *var) { - IntOrFloat::IntType => Pattern::IntLiteral(*num), - IntOrFloat::FloatType => Pattern::FloatLiteral(*num as u64), + IntOrFloat::SignedIntType(_) => Pattern::IntLiteral(*num), + IntOrFloat::UnsignedIntType(_) => Pattern::IntLiteral(*num), + IntOrFloat::BinaryFloatType(_) => Pattern::FloatLiteral(*var, *num as u64), + IntOrFloat::DecimalFloatType(_) => Pattern::FloatLiteral(*var, *num as u64), }, AppliedTag { @@ -5854,29 +5906,100 @@ fn optimize_low_level( } } +pub enum IntPrecision { + I128, + I64, + I32, + I16, + I8, +} + +pub enum FloatPrecision { + F64, + F32, +} + pub enum IntOrFloat { - IntType, - FloatType, + SignedIntType(IntPrecision), + UnsignedIntType(IntPrecision), + BinaryFloatType(FloatPrecision), + DecimalFloatType(FloatPrecision), +} + +fn float_precision_to_builtin(precision: FloatPrecision) -> Builtin<'static> { + use FloatPrecision::*; + match precision { + F64 => Builtin::Float64, + F32 => Builtin::Float32, + } +} + +fn int_precision_to_builtin(precision: IntPrecision) -> Builtin<'static> { + use IntPrecision::*; + match precision { + I128 => Builtin::Int128, + I64 => Builtin::Int64, + I32 => Builtin::Int32, + I16 => Builtin::Int16, + I8 => Builtin::Int8, + } } /// Given the `a` in `Num a`, determines whether it's an int or a float pub fn num_argument_to_int_or_float(subs: &Subs, var: Variable) -> IntOrFloat { match subs.get_without_compacting(var).content { - Content::Alias(Symbol::NUM_INTEGER, args, _) => { - debug_assert!(args.len() == 1); - - // TODO: we probably need to match on the type of the arg - IntOrFloat::IntType + Content::FlexVar(_) => IntOrFloat::SignedIntType(IntPrecision::I64), // We default (Num *) to I64 + Content::Alias(Symbol::NUM_I128, _, _) + | Content::Alias(Symbol::NUM_SIGNED128, _, _) + | Content::Alias(Symbol::NUM_AT_SIGNED128, _, _) => { + IntOrFloat::SignedIntType(IntPrecision::I128) } - Content::FlexVar(_) => { - // If this was still a (Num *), assume compiling it to an Int - IntOrFloat::IntType + Content::Alias(Symbol::NUM_INTEGER, _, _) // We default Integer to I64 + | Content::Alias(Symbol::NUM_I64, _, _) + | Content::Alias(Symbol::NUM_SIGNED64, _, _) + | Content::Alias(Symbol::NUM_AT_SIGNED64, _, _) => { + IntOrFloat::SignedIntType(IntPrecision::I64) } - Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _) => { - debug_assert!(args.len() == 1); - - // TODO: we probably need to match on the type of the arg - IntOrFloat::FloatType + // TODO: Add ors for aall NUM_SIGNED, NUM_BINARY (maybe NUM_AT_*) + Content::Alias(Symbol::NUM_I32, _, _) + | Content::Alias(Symbol::NUM_SIGNED32, _, _) + | Content::Alias(Symbol::NUM_AT_SIGNED32, _, _) => { + IntOrFloat::SignedIntType(IntPrecision::I32) + } + Content::Alias(Symbol::NUM_I16, _, _) + | Content::Alias(Symbol::NUM_SIGNED16, _, _) + | Content::Alias(Symbol::NUM_AT_SIGNED16, _, _) => { + IntOrFloat::SignedIntType(IntPrecision::I16) + } + Content::Alias(Symbol::NUM_I8, _, _) + | Content::Alias(Symbol::NUM_SIGNED8, _, _) + | Content::Alias(Symbol::NUM_AT_SIGNED8, _, _) => { + IntOrFloat::SignedIntType(IntPrecision::I8) + } + Content::Alias(Symbol::NUM_U128, _, _) + | Content::Alias(Symbol::NUM_UNSIGNED128, _, _) + | Content::Alias(Symbol::NUM_AT_UNSIGNED128, _, _) => { + IntOrFloat::UnsignedIntType(IntPrecision::I128) + } + Content::Alias(Symbol::NUM_U64, _, _) + | Content::Alias(Symbol::NUM_UNSIGNED64, _, _) + | Content::Alias(Symbol::NUM_AT_UNSIGNED64, _, _) => { + IntOrFloat::UnsignedIntType(IntPrecision::I64) + } + Content::Alias(Symbol::NUM_U32, _, _) + | Content::Alias(Symbol::NUM_UNSIGNED32, _, _) + | Content::Alias(Symbol::NUM_AT_UNSIGNED32, _, _) => { + IntOrFloat::UnsignedIntType(IntPrecision::I32) + } + Content::Alias(Symbol::NUM_U16, _, _) + | Content::Alias(Symbol::NUM_UNSIGNED16, _, _) + | Content::Alias(Symbol::NUM_AT_UNSIGNED16, _, _) => { + IntOrFloat::UnsignedIntType(IntPrecision::I16) + } + Content::Alias(Symbol::NUM_U8, _, _) + | Content::Alias(Symbol::NUM_UNSIGNED8, _, _) + | Content::Alias(Symbol::NUM_AT_UNSIGNED8, _, _) => { + IntOrFloat::UnsignedIntType(IntPrecision::I8) } Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, attr_args)) => { debug_assert!(attr_args.len() == 2); @@ -5884,10 +6007,16 @@ pub fn num_argument_to_int_or_float(subs: &Subs, var: Variable) -> IntOrFloat { // Recurse on the second argument num_argument_to_int_or_float(subs, attr_args[1]) } - Content::Alias(Symbol::NUM_F64, args, _) | Content::Alias(Symbol::NUM_F32, args, _) => { - debug_assert!(args.is_empty()); - - IntOrFloat::FloatType + Content::Alias(Symbol::NUM_FLOATINGPOINT, _, _) // We default FloatingPoint to F64 + | Content::Alias(Symbol::NUM_F64, _, _) + | Content::Alias(Symbol::NUM_BINARY64, _, _) + | Content::Alias(Symbol::NUM_AT_BINARY64, _, _) => { + IntOrFloat::BinaryFloatType(FloatPrecision::F64) + } + Content::Alias(Symbol::NUM_F32, _, _) + | Content::Alias(Symbol::NUM_BINARY32, _, _) + | Content::Alias(Symbol::NUM_AT_BINARY32, _, _) => { + IntOrFloat::BinaryFloatType(FloatPrecision::F32) } other => { panic!( diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 529a4d0ca3..3cfd1c9c29 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -362,14 +362,60 @@ impl<'a> Layout<'a> { } Structure(flat_type) => layout_from_flat_type(env, flat_type), - Alias(Symbol::NUM_INT, args, _) => { - debug_assert!(args.len() == 1); + // Ints + Alias(Symbol::NUM_I128, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int128)) + } + Alias(Symbol::NUM_I64, args, _) => { + debug_assert!(args.is_empty()); Ok(Layout::Builtin(Builtin::Int64)) } - Alias(Symbol::NUM_FLOAT, args, _) => { - debug_assert!(args.len() == 1); + Alias(Symbol::NUM_I32, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int32)) + } + Alias(Symbol::NUM_I16, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int16)) + } + Alias(Symbol::NUM_I8, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int8)) + } + + // I think unsigned and signed use the same layout + Alias(Symbol::NUM_U128, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int128)) + } + Alias(Symbol::NUM_U64, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int64)) + } + Alias(Symbol::NUM_U32, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int32)) + } + Alias(Symbol::NUM_U16, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int16)) + } + Alias(Symbol::NUM_U8, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Int8)) + } + + // Floats + Alias(Symbol::NUM_F64, args, _) => { + debug_assert!(args.is_empty()); Ok(Layout::Builtin(Builtin::Float64)) } + Alias(Symbol::NUM_F32, args, _) => { + debug_assert!(args.is_empty()); + Ok(Layout::Builtin(Builtin::Float32)) + } + Alias(_, _, var) => Self::from_var(env, var), Error => Err(LayoutProblem::Erroneous), } @@ -763,14 +809,60 @@ fn layout_from_flat_type<'a>( match flat_type { Apply(symbol, args) => { match symbol { - Symbol::NUM_INT => { + // Ints + Symbol::NUM_I128 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int128)) + } + Symbol::NUM_I64 => { debug_assert_eq!(args.len(), 0); Ok(Layout::Builtin(Builtin::Int64)) } - Symbol::NUM_FLOAT => { + Symbol::NUM_I32 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int64)) + } + Symbol::NUM_I16 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int64)) + } + Symbol::NUM_I8 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int64)) + } + + // I think unsigned and signed use the same layout + Symbol::NUM_U128 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int128)) + } + Symbol::NUM_U64 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int64)) + } + Symbol::NUM_U32 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int64)) + } + Symbol::NUM_U16 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int64)) + } + Symbol::NUM_U8 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Int64)) + } + + // Floats + Symbol::NUM_F64 => { debug_assert_eq!(args.len(), 0); Ok(Layout::Builtin(Builtin::Float64)) } + Symbol::NUM_F32 => { + debug_assert_eq!(args.len(), 0); + Ok(Layout::Builtin(Builtin::Float32)) + } + Symbol::NUM_NUM | Symbol::NUM_AT_NUM => { // Num.Num should only ever have 1 argument, e.g. Num.Num Int.Integer debug_assert_eq!(args.len(), 1); @@ -780,6 +872,7 @@ fn layout_from_flat_type<'a>( layout_from_num_content(content) } + Symbol::STR_STR => Ok(Layout::Builtin(Builtin::Str)), Symbol::LIST_LIST => list_layout_from_elem(env, args[0]), Symbol::ATTR_ATTR => { @@ -1263,8 +1356,26 @@ fn layout_from_num_content<'a>(content: Content) -> Result, LayoutPro Ok(Layout::Builtin(DEFAULT_NUM_BUILTIN)) } Structure(Apply(symbol, args)) => match symbol { + // Ints Symbol::NUM_INTEGER => Ok(Layout::Builtin(Builtin::Int64)), + Symbol::NUM_I128 => Ok(Layout::Builtin(Builtin::Int128)), + Symbol::NUM_I64 => Ok(Layout::Builtin(Builtin::Int64)), + Symbol::NUM_I32 => Ok(Layout::Builtin(Builtin::Int32)), + Symbol::NUM_I16 => Ok(Layout::Builtin(Builtin::Int16)), + Symbol::NUM_I8 => Ok(Layout::Builtin(Builtin::Int8)), + + // I think unsigned and signed use the same layout + Symbol::NUM_U128 => Ok(Layout::Builtin(Builtin::Int128)), + Symbol::NUM_U64 => Ok(Layout::Builtin(Builtin::Int64)), + Symbol::NUM_U32 => Ok(Layout::Builtin(Builtin::Int32)), + Symbol::NUM_U16 => Ok(Layout::Builtin(Builtin::Int16)), + Symbol::NUM_U8 => Ok(Layout::Builtin(Builtin::Int8)), + + // Floats Symbol::NUM_FLOATINGPOINT => Ok(Layout::Builtin(Builtin::Float64)), + Symbol::NUM_F64 => Ok(Layout::Builtin(Builtin::Float64)), + Symbol::NUM_F32 => Ok(Layout::Builtin(Builtin::Float32)), + _ => { panic!( "Invalid Num.Num type application: {:?}", diff --git a/compiler/uniq/src/sharing.rs b/compiler/uniq/src/sharing.rs index 9a0fe7b7ab..f991617599 100644 --- a/compiler/uniq/src/sharing.rs +++ b/compiler/uniq/src/sharing.rs @@ -785,8 +785,8 @@ pub fn annotate_usage(expr: &Expr, usage: &mut VarUsage) { match expr { RuntimeError(_) | Num(_, _) - | Int(_, _) - | Float(_, _) + | Int(_, _, _) + | Float(_, _, _) | Str { .. } | EmptyRecord | Accessor { .. }