From 6f7ffbeb3cc353d8364f07b7690dc73794070eed Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 25 Sep 2024 00:18:11 +0200 Subject: [PATCH] Add `rand_in_range` random function. Fix methodsof to apply to more types. Prevent methodsof in the wrong stage. --- lib/std/math/math_random.c3 | 45 ++++++++++++++++++++++----- releasenotes.md | 2 +- src/compiler/compiler_internal.h | 6 ++-- src/compiler/copying.c | 2 +- src/compiler/enums.h | 4 +-- src/compiler/expr.c | 4 +-- src/compiler/sema_expr.c | 52 ++++++++++++++++++-------------- src/compiler/sema_stmts.c | 2 +- 8 files changed, 77 insertions(+), 40 deletions(-) diff --git a/lib/std/math/math_random.c3 b/lib/std/math/math_random.c3 index 90d1952a6..c4579e983 100644 --- a/lib/std/math/math_random.c3 +++ b/lib/std/math/math_random.c3 @@ -29,15 +29,35 @@ macro void seed_entropy(random) } /** - * Get the next value between 0 and max (not including max). + * Get the next value between 0 and range (not including range). * * @require is_random(random) - * @require max > 0 + * @require range > 0 **/ -macro int next(random, int max) +macro int next(random, uint range) { - if (max == 1) return 0; - return (int)(random.next_long() % (uint)max); + if (range == 1) return 0; + uint mask = ~0U; + range--; + mask >>= range.clz(); + uint x @noinit; + do + { + x = random.next_int() & mask; + } + while (x > range); + return x; +} + +/** + * Get a random in the range [min, max], both included. + * + * @require is_random(random) + * @require max >= min + **/ +macro int next_in_range(random, int min, int max) +{ + return next(random, max - min + 1) + min; } def DefaultRandom = Sfc64Random; @@ -55,14 +75,23 @@ fn void srand(ulong seed) @builtin } /** - * Get a default random value between 0 and max (not including max) + * Get a default random value between 0 and range (not including range) **/ -fn int rand(int max) @builtin +fn int rand(int range) @builtin { init_default_random(); - return next(&default_random, max); + return next(&default_random, range); } +/** + * Get a random in the range, both included. + * @require max >= min + **/ +fn int rand_in_range(int min, int max) @builtin +{ + init_default_random(); + return next_in_range(&default_random, min, max); +} fn double rnd() @builtin { diff --git a/releasenotes.md b/releasenotes.md index f1386f2d3..57ff42192 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -65,7 +65,7 @@ - Add support for the QOI format. - Add `io::read_new_fully` for reading to the end of a stream. - Add `io::wrap_bytes` for reading bytes with `io` functions. -- Add `rnd` default random function. +- Add `rnd` and `rand_in_range` default random functions. ## 0.6.2 Change list diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index e2f507b11..fd1c5bd4c 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -853,7 +853,7 @@ typedef struct { Decl *type; TypeProperty property; -} ExprTagOf; +} ExprTypeCall; typedef struct { @@ -1162,7 +1162,7 @@ struct Expr_ ExprSwizzle swizzle_expr; ExprTernary ternary_expr; // 16 BuiltinDefine benchmark_hook_expr; - ExprTagOf tag_of_expr; + ExprTypeCall type_call_expr; BuiltinDefine test_hook_expr; Expr** try_unwrap_chain_expr; // 8 ExprTryUnwrap try_unwrap_expr; // 24 @@ -3350,7 +3350,7 @@ static inline void expr_set_span(Expr *expr, SourceSpan loc) case EXPR_VASPLAT: case EXPR_MACRO_BODY: case EXPR_DEFAULT_ARG: - case EXPR_TAGOF: + case EXPR_TYPECALL: case EXPR_MEMBER_GET: break; } diff --git a/src/compiler/copying.c b/src/compiler/copying.c index d8170b08d..a8c7a2435 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -290,7 +290,7 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) Expr *expr = expr_copy(source_expr); switch (source_expr->expr_kind) { - case EXPR_TAGOF: + case EXPR_TYPECALL: case EXPR_ANYSWITCH: UNREACHABLE case EXPR_OTHER_CONTEXT: diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 4d1f7e1af..2b1d2e96b 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -759,7 +759,7 @@ typedef enum EXPR_COMPOUND_LITERAL, EXPR_COND, EXPR_CONST, - EXPR_TAGOF, + EXPR_TYPECALL, EXPR_CT_AND_OR, EXPR_CT_ARG, EXPR_CT_APPEND, @@ -1601,7 +1601,7 @@ typedef enum case EXPR_CT_CASTABLE: case EXPR_CT_IS_CONST: \ case EXPR_CT_ARG: case EXPR_TYPEINFO: case EXPR_CT_IDENT: case EXPR_HASH_IDENT: \ case EXPR_COMPILER_CONST: case EXPR_CT_CALL: \ - case EXPR_SPLAT: case EXPR_ANYSWITCH: case EXPR_STRINGIFY: case EXPR_TAGOF: \ + case EXPR_SPLAT: case EXPR_ANYSWITCH: case EXPR_STRINGIFY: case EXPR_TYPECALL: \ case EXPR_CT_EVAL: case EXPR_CT_CONCAT: case EXPR_CT_APPEND static_assert(EXPR_LAST < 128, "Too many expression types"); diff --git a/src/compiler/expr.c b/src/compiler/expr.c index 3f8f80648..14a2376c7 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -156,7 +156,7 @@ bool expr_is_runtime_const(Expr *expr) case EXPR_TEST_HOOK: case EXPR_ANYSWITCH: case EXPR_BITASSIGN: - case EXPR_TAGOF: + case EXPR_TYPECALL: case EXPR_BINARY: case EXPR_OPERATOR_CHARS: case EXPR_STRINGIFY: @@ -614,7 +614,7 @@ bool expr_is_pure(Expr *expr) return exprid_is_pure(expr->pointer_offset_expr.ptr) && exprid_is_pure(expr->pointer_offset_expr.offset); case EXPR_COMPILER_CONST: case EXPR_CONST: - case EXPR_TAGOF: + case EXPR_TYPECALL: case EXPR_CT_AND_OR: case EXPR_CT_CONCAT: case EXPR_CT_APPEND: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index cbb1dac11..6f87d0494 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -548,7 +548,7 @@ static bool sema_binary_is_expr_lvalue(SemaContext *context, Expr *top_expr, Exp case EXPR_SLICE_COPY: case EXPR_SPLAT: case EXPR_STRINGIFY: - case EXPR_TAGOF: + case EXPR_TYPECALL: case EXPR_TERNARY: case EXPR_TRY_UNWRAP: case EXPR_TRY_UNWRAP_CHAIN: @@ -588,7 +588,7 @@ static bool expr_may_ref(Expr *expr) case EXPR_CT_IDENT: case EXPR_EMBED: case EXPR_DEFAULT_ARG: - case EXPR_TAGOF: + case EXPR_TYPECALL: case EXPR_MEMBER_GET: return false; case EXPR_OTHER_CONTEXT: @@ -2492,14 +2492,14 @@ bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl } } -static inline bool sema_expr_analyse_tagof(SemaContext *context, Expr *expr) +static inline bool sema_expr_analyse_typecall(SemaContext *context, Expr *expr) { expr->call_expr.arguments = sema_expand_vasplat_exprs(context, expr->call_expr.arguments); Expr **args = expr->call_expr.arguments; unsigned arg_count = vec_size(args); Expr *tag = exprptr(expr->call_expr.function); - Decl *decl = tag->tag_of_expr.type; - bool is_has = tag->tag_of_expr.property == TYPE_PROPERTY_HAS_TAGOF; + Decl *decl = tag->type_call_expr.type; + bool is_has = tag->type_call_expr.property == TYPE_PROPERTY_HAS_TAGOF; const char *name = is_has ? "has_tagof" : "tagof"; if (arg_count != 1) RETURN_SEMA_ERROR(expr, "Expected a single string argument to '%s'.", name); Expr *key = args[0]; @@ -2603,8 +2603,8 @@ static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr, bool Expr *struct_var = NULL; switch (func_expr->expr_kind) { - case EXPR_TAGOF: - return sema_expr_analyse_tagof(context, expr); + case EXPR_TYPECALL: + return sema_expr_analyse_typecall(context, expr); case EXPR_BUILTIN: return sema_expr_analyse_builtin_call(context, expr); case EXPR_IDENTIFIER: @@ -3623,8 +3623,8 @@ static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *e { case TYPE_PROPERTY_TAGOF: case TYPE_PROPERTY_HAS_TAGOF: - expr->expr_kind = EXPR_TAGOF; - expr->tag_of_expr = (ExprTagOf) { .type = decl, .property = type_property }; + expr->expr_kind = EXPR_TYPECALL; + expr->type_call_expr = (ExprTypeCall) { .type = decl, .property = type_property }; return true; case TYPE_PROPERTY_GET: expr->expr_kind = EXPR_MEMBER_GET; @@ -3647,7 +3647,6 @@ static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *e sema_create_const_membersof(context, expr, decl->type->canonical, parent->const_expr.member.align, parent->const_expr.member.offset); return true; case TYPE_PROPERTY_METHODSOF: - sema_create_const_methodsof(context, expr, decl->type->canonical); case TYPE_PROPERTY_KINDOF: case TYPE_PROPERTY_SIZEOF: return sema_expr_rewrite_to_type_property(context, expr, decl->type->canonical, type_property, decl->type->canonical); @@ -4414,12 +4413,19 @@ static bool sema_type_property_is_valid_for_type(Type *original_type, TypeProper case TYPE_PROPERTY_METHODSOF: switch (type->type_kind) { - case TYPE_STRUCT: - case TYPE_UNION: - case TYPE_BITSTRUCT: - return true; - default: + case TYPE_POISONED: + case TYPE_VOID: + case TYPE_FUNC_RAW: + case TYPE_TYPEDEF: + case TYPE_UNTYPED_LIST: + case TYPE_FLEXIBLE_ARRAY: + case TYPE_OPTIONAL: + case TYPE_WILDCARD: + case TYPE_TYPEINFO: + case TYPE_MEMBER: return false; + default: + return true; } case TYPE_PROPERTY_PARAMSOF: case TYPE_PROPERTY_PARAMS: @@ -4501,10 +4507,12 @@ static bool sema_expr_rewrite_to_type_property(SemaContext *context, Expr *expr, return true; } case TYPE_PROPERTY_METHODSOF: - { + if (context->compilation_unit->module->stage <= ANALYSIS_DECLS) + { + RETURN_SEMA_ERROR(expr, "'methodsof' is not available until after declaration analysis is complete."); + } sema_create_const_methodsof(context, expr, flat); return true; - } case TYPE_PROPERTY_PARAMSOF: return sema_create_const_paramsof(context, expr, flat); case TYPE_PROPERTY_PARAMS: @@ -4538,8 +4546,8 @@ static bool sema_expr_rewrite_to_type_property(SemaContext *context, Expr *expr, return true; case TYPE_PROPERTY_TAGOF: case TYPE_PROPERTY_HAS_TAGOF: - expr->expr_kind = EXPR_TAGOF; - expr->tag_of_expr = (ExprTagOf) { + expr->expr_kind = EXPR_TYPECALL; + expr->type_call_expr = (ExprTypeCall) { .type = type->type_kind == TYPE_FUNC_PTR ? type->pointer->function.decl : type->decl, @@ -8624,7 +8632,7 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr case EXPR_POST_UNARY: case EXPR_TYPEID: case EXPR_TYPEID_INFO: - case EXPR_TAGOF: + case EXPR_TYPECALL: case EXPR_MEMBER_GET: if (!sema_analyse_expr(active_context, main_expr)) return false; break; @@ -9007,7 +9015,7 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr, case EXPR_TRY_UNWRAP_CHAIN: case EXPR_TYPEID_INFO: UNREACHABLE - case EXPR_TAGOF: + case EXPR_TYPECALL: RETURN_SEMA_ERROR(expr, "Expected '()' after this."); case EXPR_OTHER_CONTEXT: context = expr->expr_other_context.context; @@ -9248,7 +9256,7 @@ static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr) return false; } break; - case EXPR_TAGOF: + case EXPR_TYPECALL: RETURN_SEMA_ERROR(expr, "A tag name must be given."); case EXPR_BUILTIN: RETURN_SEMA_ERROR(expr, "A builtin must be followed by ()."); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 367c1e2c9..e2f794757 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -664,7 +664,7 @@ static inline bool sema_expr_valid_try_expression(Expr *expr) case EXPR_POST_UNARY: case EXPR_TERNARY: case EXPR_LAST_FAULT: - case EXPR_TAGOF: + case EXPR_TYPECALL: case EXPR_MEMBER_GET: return false; case EXPR_BITACCESS: