diff --git a/lib/std/math/math.c3 b/lib/std/math/math.c3 index 67d182a74..7b16d71ea 100644 --- a/lib/std/math/math.c3 +++ b/lib/std/math/math.c3 @@ -234,6 +234,13 @@ macro asinh(x) *> macro ceil(x) => $$ceil(x); +<* + Ceil for compile time evaluation. + + @require @typeis($input, double) || @typeis($input, float) : "Only float and double may be used" +*> +macro @ceil($input) @const => $$ceil($input); + <* Constrain the value to lie within the given interval. diff --git a/releasenotes.md b/releasenotes.md index 1867a9b2b..e3502eb37 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -9,6 +9,7 @@ - Add experimental LL / ULL suffixes for int128 and uint128 literals. - Allow the right hand side of `|||` and `&&&` be runtime values. - Added `@rnd()` compile time random function (using the `$$rnd()` builtin). #2078 +- Add `math::@ceil()` compile time ceil function. #2134 ### Fixes - Assert triggered when casting from `int[2]` to `uint[2]` #2115 diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 32f3997b8..de7bb6e2b 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2245,6 +2245,7 @@ INLINE bool expr_is_mult(Expr *expr); INLINE bool expr_is_deref(Expr *expr); INLINE bool expr_is_const(Expr *expr); INLINE bool expr_is_const_int(Expr *expr); +INLINE bool expr_is_const_float(Expr *expr); INLINE bool expr_is_const_string(Expr *expr); INLINE bool expr_is_const_initializer(Expr *expr); INLINE bool expr_is_const_untyped_list(Expr *expr); @@ -4071,6 +4072,12 @@ INLINE bool expr_is_const_int(Expr *expr) return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_INTEGER; } +INLINE bool expr_is_const_float(Expr *expr) +{ + ASSERT(expr->resolve_status == RESOLVE_DONE); + return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_FLOAT; +} + INLINE bool expr_is_const_member(Expr *expr) { ASSERT(expr->resolve_status == RESOLVE_DONE); diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index e7f55f029..d3638a511 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -2231,7 +2231,7 @@ static inline Decl *parse_attrdef(ParseContext *c) CONSUME_OR_RET(TOKEN_EQ, poisoned_decl); if (tok_is(c, TOKEN_EOS)) { - PRINT_ERROR_LAST("Expected a list of attributes after '='."); + PRINT_ERROR_HERE("Expected a list of attributes."); return poisoned_decl; } diff --git a/src/compiler/sema_builtins.c b/src/compiler/sema_builtins.c index a81c328f1..1bfa7e1b0 100644 --- a/src/compiler/sema_builtins.c +++ b/src/compiler/sema_builtins.c @@ -1,6 +1,8 @@ // Copyright (c) 2022-2025 Christoffer Lerno. All rights reserved. // Use of this source code is governed by a LGPLv3.0 // a copy of which can be found in the LICENSE file. +#include + #include "sema_internal.h" @@ -761,6 +763,11 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) if (!sema_check_builtin_args(context, args, (BuiltinArg[]) {BA_FLOATLIKE, BA_FLOATLIKE, BA_FLOATLIKE}, arg_count)) return false; rtype = args[0]->type; + if (func == BUILTIN_CEIL && expr_is_const_float(args[0])) + { + expr_rewrite_const_float(expr, rtype, ceil(args[0]->const_expr.fxx.f)); + return true; + } break; case BUILTIN_FRAMEADDRESS: case BUILTIN_RETURNADDRESS: diff --git a/test/test_suite/attributes/attrdef_err.c3 b/test/test_suite/attributes/attrdef_err.c3 index 376aa247c..0af3c43b2 100644 --- a/test/test_suite/attributes/attrdef_err.c3 +++ b/test/test_suite/attributes/attrdef_err.c3 @@ -1,5 +1,5 @@ -attrdef @Foo = ; // #error: Expected a list of attributes after +attrdef @Foo = ; // #error: Expected a list of attributes fn void main() { diff --git a/test/unit/stdlib/math/math.c3 b/test/unit/stdlib/math/math.c3 index 1478cf2b4..0dfd56528 100644 --- a/test/unit/stdlib/math/math.c3 +++ b/test/unit/stdlib/math/math.c3 @@ -322,6 +322,18 @@ fn void test_ceil() @test assert(math::ceil(vec) == (double[<5>]) { -123, 124, 1, 0, 0 }); } +fn void test_ct_ceil() @test +{ + $assert(math::@ceil(-123.1) == -123.0); + $assert(math::@ceil(123.1) == 124.0); + $assert(math::@ceil(0.1) == 1.0); + $assert(math::@ceil(-0.9) == 0.0); + $assert(math::@ceil(-123.1f) == -123.0f); + $assert(math::@ceil(123.1f) == 124.0f); + $assert(math::@ceil(0.1f) == 1.0f); + $assert(math::@ceil(-0.9f) == 0.0f); +} + fn void test_cos() @test { int [<5>] in = { 231, 1, 0, -1, -231 };