diff --git a/Cargo.lock b/Cargo.lock index 541d1c97c3..d09ec931df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1631,9 +1631,9 @@ checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.19", + "proc-macro2 1.0.21", "quote 1.0.7", - "syn 1.0.35", + "syn 1.0.40", ] [[package]] diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index 62db18a590..fe0b0460ff 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -418,6 +418,12 @@ pub fn types() -> MutMap { SolvedType::Func(vec![float_type(), float_type()], Box::new(float_type())), ); + // ceiling : Float -> Int + add_type( + Symbol::NUM_CEILING, + SolvedType::Func(vec![float_type()], Box::new(int_type())), + ); + // Bool module // and : Bool, Bool -> Bool diff --git a/compiler/builtins/src/unique.rs b/compiler/builtins/src/unique.rs index 5eea0a40fd..c9cfd5dade 100644 --- a/compiler/builtins/src/unique.rs +++ b/compiler/builtins/src/unique.rs @@ -457,6 +457,12 @@ pub fn types() -> MutMap { ) }); + // ceiling : Float -> Int + add_type(Symbol::NUM_CEILING, { + let_tvars! { star1, star2 }; + unique_function(vec![float_type(star1)], int_type(star2)) + }); + // Bool module // isEq or (==) : Attr * a, Attr * a -> Attr * Bool diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 9c4068a593..8b0bd3098d 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -91,6 +91,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap { Symbol::NUM_IS_NEGATIVE => num_is_negative, Symbol::NUM_TO_FLOAT => num_to_float, Symbol::NUM_POW => num_pow, + Symbol::NUM_CEILING => num_ceiling, } } @@ -601,6 +602,25 @@ fn num_pow(symbol: Symbol, var_store: &mut VarStore) -> Def { ) } +/// Num.ceiling : Float -> Int +fn num_ceiling(symbol: Symbol, var_store: &mut VarStore) -> Def { + let float_var = var_store.fresh(); + let int_var = var_store.fresh(); + + let body = RunLowLevel { + op: LowLevel::NumCeiling, + args: vec![(float_var, Var(Symbol::ARG_1))], + ret_var: int_var, + }; + + defn( + symbol, + vec![(float_var, Symbol::ARG_1)], + var_store, + body, + int_var, + ) +} /// List.isEmpty : List * -> Bool fn list_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def { let list_var = var_store.fresh(); diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 4c02ea9878..f9dc5efeae 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -23,6 +23,7 @@ use inkwell::module::{Linkage, Module}; use inkwell::passes::{PassManager, PassManagerBuilder}; use inkwell::types::{BasicTypeEnum, FunctionType, IntType, StructType}; use inkwell::values::BasicValueEnum::{self, *}; +use inkwell::values::InstructionOpcode; use inkwell::values::{BasicValue, FloatValue, FunctionValue, IntValue, PointerValue, StructValue}; use inkwell::AddressSpace; use inkwell::OptimizationLevel; @@ -151,6 +152,12 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) { LLVM_POW_F64, f64_type.fn_type(&[f64_type.into(), f64_type.into()], false), ); + + add_intrinsic( + module, + LLVM_CEILING_F64, + f64_type.fn_type(&[f64_type.into()], false), + ); } static LLVM_SQRT_F64: &str = "llvm.sqrt.f64"; @@ -159,6 +166,7 @@ static LLVM_FABS_F64: &str = "llvm.fabs.f64"; static LLVM_SIN_F64: &str = "llvm.sin.f64"; static LLVM_COS_F64: &str = "llvm.cos.f64"; static LLVM_POW_F64: &str = "llvm.pow.f64"; +static LLVM_CEILING_F64: &str = "llvm.ceil.f64"; fn add_intrinsic<'ctx>( module: &Module<'ctx>, @@ -1783,7 +1791,8 @@ fn run_low_level<'a, 'ctx, 'env>( list_join(env, inplace, parent, list, outer_list_layout) } - NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumToFloat => { + NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumCeiling + | NumToFloat => { debug_assert_eq!(args.len(), 1); let (arg, arg_layout) = load_symbol_and_layout(env, scope, &args[0]); @@ -2415,6 +2424,12 @@ fn build_float_unary_op<'a, 'ctx, 'env>( NumSin => call_intrinsic(LLVM_SIN_F64, env, &[(arg.into(), arg_layout)]), NumCos => call_intrinsic(LLVM_COS_F64, env, &[(arg.into(), arg_layout)]), NumToFloat => arg.into(), /* Converting from Float to Float is a no-op */ + NumCeiling => env.builder.build_cast( + InstructionOpcode::FPToSI, + call_intrinsic(LLVM_CEILING_F64, env, &[(arg.into(), arg_layout)]), + env.context.i64_type(), + "num_ceiling", + ), _ => { unreachable!("Unrecognized int unary operation: {:?}", op); } diff --git a/compiler/gen/tests/gen_num.rs b/compiler/gen/tests/gen_num.rs index 09968a5bc6..ec0d1a40fa 100644 --- a/compiler/gen/tests/gen_num.rs +++ b/compiler/gen/tests/gen_num.rs @@ -670,4 +670,9 @@ mod gen_num { fn pow() { assert_evals_to!("Num.pow 2.0 2.0", 4.0, f64); } + + #[test] + fn ceiling() { + assert_evals_to!("Num.ceiling 1.5", 2, i64); + } } diff --git a/compiler/module/src/low_level.rs b/compiler/module/src/low_level.rs index 489f05f143..f5023566a1 100644 --- a/compiler/module/src/low_level.rs +++ b/compiler/module/src/low_level.rs @@ -36,6 +36,7 @@ pub enum LowLevel { NumRound, NumToFloat, NumPow, + NumCeiling, Eq, NotEq, And, diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 1a02c3ed2c..dd02f9614f 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -641,6 +641,7 @@ define_builtins! { 36 NUM_ROUND: "round" 37 NUM_COMPARE: "compare" 38 NUM_POW: "pow" + 39 NUM_CEILING: "ceiling" } 2 BOOL: "Bool" => { 0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index ffebd21c5d..6e2634767d 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -526,8 +526,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] { arena.alloc_slice_copy(&[irrelevant, irrelevant]) } - NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumToFloat | Not => { - arena.alloc_slice_copy(&[irrelevant]) - } + NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumToFloat + | NumCeiling | Not => arena.alloc_slice_copy(&[irrelevant]), } } diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index 7033c5f7b6..96e3fd1e08 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -2382,6 +2382,18 @@ mod solve_expr { ); } + #[test] + fn ceiling() { + infer_eq_without_problem( + indoc!( + r#" + Num.ceiling + "# + ), + "Float -> Int", + ); + } + #[test] fn pow() { infer_eq_without_problem(