From: Brendan Hansen Date: Wed, 18 Jan 2023 19:20:33 +0000 (-0600) Subject: added __implicit_bool_cast X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=beebf12c087f1b0afe54b4306ff3b15c89c47874;p=onyx.git added __implicit_bool_cast Allows users to customize how user-defined values become bools --- diff --git a/compiler/include/astnodes.h b/compiler/include/astnodes.h index cb0a4bdf..f70ae309 100644 --- a/compiler/include/astnodes.h +++ b/compiler/include/astnodes.h @@ -1701,6 +1701,8 @@ extern AstTyped *tagged_procedures_node; extern AstFunction *builtin_initialize_data_segments; extern AstFunction *builtin_run_init_procedures; extern bh_arr(AstFunction *) init_procedures; +extern AstOverloadedFunction *builtin_implicit_bool_cast; + typedef struct BuiltinSymbol { char* package; @@ -1758,7 +1760,7 @@ char *get_expression_string_value(AstTyped* node, b32 *out_is_valid); b32 cast_is_legal(Type* from_, Type* to_, char** err_msg); char* get_function_name(AstFunction* func); -b32 implicit_cast_to_bool(AstTyped **pnode); +TypeMatch implicit_cast_to_bool(AstTyped **pnode); AstNode* strip_aliases(AstNode* node); diff --git a/compiler/src/astnodes.c b/compiler/src/astnodes.c index aa2f757f..2f1e7dde 100644 --- a/compiler/src/astnodes.c +++ b/compiler/src/astnodes.c @@ -1320,11 +1320,14 @@ b32 cast_is_legal(Type* from_, Type* to_, char** err_msg) { return 1; } -b32 implicit_cast_to_bool(AstTyped **pnode) { + + +static bh_imap implicit_cast_to_bool_cache; + +TypeMatch implicit_cast_to_bool(AstTyped **pnode) { AstTyped *node = *pnode; - if ((node->type->kind == Type_Kind_Basic && - node->type->Basic.kind == Basic_Kind_Rawptr) + if ((node->type->kind == Type_Kind_Basic && node->type->Basic.kind == Basic_Kind_Rawptr) || (node->type->kind == Type_Kind_Pointer)) { AstNumLit *zero = make_int_literal(context.ast_alloc, 0); zero->type = &basic_types[Basic_Kind_Rawptr]; @@ -1334,7 +1337,7 @@ b32 implicit_cast_to_bool(AstTyped **pnode) { cmp->type = &basic_types[Basic_Kind_Bool]; *pnode = (AstTyped *) cmp; - return 1; + return TYPE_MATCH_SUCCESS; } if (node->type->kind == Type_Kind_Slice || @@ -1358,10 +1361,39 @@ b32 implicit_cast_to_bool(AstTyped **pnode) { cmp->type = &basic_types[Basic_Kind_Bool]; *pnode = (AstTyped *) cmp; - return 1; + return TYPE_MATCH_SUCCESS; + } + + if (implicit_cast_to_bool_cache.entries == NULL) { + bh_imap_init(&implicit_cast_to_bool_cache, global_heap_allocator, 8); } - return 0; + if (!bh_imap_has(&implicit_cast_to_bool_cache, (u64) node)) { + AstArgument *implicit_arg = make_argument(context.ast_alloc, node); + + Arguments *args = bh_alloc_item(context.ast_alloc, Arguments); + bh_arr_new(context.ast_alloc, args->values, 1); + bh_arr_push(args->values, (AstTyped *) implicit_arg); + + bh_imap_put(&implicit_cast_to_bool_cache, (u64) node, (u64) args); + } + + Arguments *args = (Arguments *) bh_imap_get(&implicit_cast_to_bool_cache, (u64) node); + AstFunction *overload = (AstFunction *) find_matching_overload_by_arguments(builtin_implicit_bool_cast->overloads, args); + + if (overload == NULL) return TYPE_MATCH_FAILED; + if (overload == (AstFunction *) &node_that_signals_a_yield) return TYPE_MATCH_YIELD; + + AstCall *implicit_call = onyx_ast_node_new(context.ast_alloc, sizeof(AstCall), Ast_Kind_Call); + implicit_call->token = node->token; + implicit_call->callee = (AstTyped *) overload; + implicit_call->va_kind = VA_Kind_Not_VA; + implicit_call->args.values = args->values; + + *(AstCall **) pnode = implicit_call; + bh_imap_delete(&implicit_cast_to_bool_cache, (u64) node); + + return TYPE_MATCH_YIELD; } char* get_function_name(AstFunction* func) { diff --git a/compiler/src/builtins.c b/compiler/src/builtins.c index 86d6a441..6992124f 100644 --- a/compiler/src/builtins.c +++ b/compiler/src/builtins.c @@ -69,6 +69,7 @@ AstTyped *tagged_procedures_node = NULL; AstFunction *builtin_initialize_data_segments = NULL; AstFunction *builtin_run_init_procedures = NULL; bh_arr(AstFunction *) init_procedures = NULL; +AstOverloadedFunction *builtin_implicit_bool_cast; const BuiltinSymbol builtin_symbols[] = { { NULL, "void", (AstNode *) &basic_type_void }, @@ -459,6 +460,12 @@ void initialize_builtins(bh_allocator a) { return; } + builtin_implicit_bool_cast = (AstOverloadedFunction *) symbol_raw_resolve(p->scope, "__implicit_bool_cast"); + if (builtin_implicit_bool_cast == NULL || builtin_implicit_bool_cast->kind != Ast_Kind_Overloaded_Function) { + onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'__implicit_bool_cast' #match procedure not found."); + return; + } + builtin_link_options_type = (AstType *) symbol_raw_resolve(p->scope, "Link_Options"); if (builtin_link_options_type == NULL) { onyx_report_error((OnyxFilePos) { 0 }, Error_Critical, "'Link_Options' type not found."); diff --git a/compiler/src/checker.c b/compiler/src/checker.c index 1b6888a8..c0f58c24 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -206,7 +206,9 @@ CheckStatus check_if(AstIfWhile* ifnode) { CHECK(expression, &ifnode->cond); if (!type_is_bool(ifnode->cond->type)) { - if (!implicit_cast_to_bool(&ifnode->cond)) { + TypeMatch implicit_cast = implicit_cast_to_bool(&ifnode->cond); + if (implicit_cast == TYPE_MATCH_YIELD) YIELD(ifnode->token->pos, "Waiting for implicit cast to bool to check."); + if (implicit_cast == TYPE_MATCH_FAILED) { ERROR_(ifnode->token->pos, "Expected expression of type 'bool' for condition, got '%s'", type_get_name(ifnode->cond->type)); } } @@ -224,7 +226,9 @@ CheckStatus check_while(AstIfWhile* whilenode) { CHECK(expression, &whilenode->cond); if (!type_is_bool(whilenode->cond->type)) { - if (!implicit_cast_to_bool(&whilenode->cond)) { + TypeMatch implicit_cast = implicit_cast_to_bool(&whilenode->cond); + if (implicit_cast == TYPE_MATCH_YIELD) YIELD(whilenode->token->pos, "Waiting for implicit cast to bool to check."); + if (implicit_cast == TYPE_MATCH_FAILED) { ERROR_(whilenode->token->pos, "Expected expression of type 'bool' for condition, got '%s'", type_get_name(whilenode->cond->type)); } } @@ -1090,14 +1094,22 @@ CheckStatus check_binaryop_bool(AstBinaryOp** pbinop) { if (type_is_bool(binop->left->type)) { left_is_bool = 1; - } else if (implicit_cast_to_bool(&binop->left)) { - left_is_bool = 1; + } else { + TypeMatch implicit_cast = implicit_cast_to_bool(&binop->left); + if (implicit_cast == TYPE_MATCH_YIELD) YIELD(binop->token->pos, "Waiting for implicit cast to bool to check."); + if (implicit_cast == TYPE_MATCH_SUCCESS) { + left_is_bool = 1; + } } if (type_is_bool(binop->right->type)) { right_is_bool = 1; - } else if (implicit_cast_to_bool(&binop->right)) { - right_is_bool = 1; + } else { + TypeMatch implicit_cast = implicit_cast_to_bool(&binop->right); + if (implicit_cast == TYPE_MATCH_YIELD) YIELD(binop->token->pos, "Waiting for implicit cast to bool to check."); + if (implicit_cast == TYPE_MATCH_SUCCESS) { + right_is_bool = 1; + } } if (!left_is_bool || !right_is_bool) { @@ -1324,7 +1336,9 @@ CheckStatus check_unaryop(AstUnaryOp** punop) { if (unaryop->operation == Unary_Op_Not) { if (!type_is_bool(unaryop->expr->type)) { - if (!implicit_cast_to_bool(&unaryop->expr)) { + TypeMatch implicit_cast = implicit_cast_to_bool(&unaryop->expr); + if (implicit_cast == TYPE_MATCH_YIELD) YIELD(unaryop->token->pos, "Waiting for implicit cast to bool to check."); + if (implicit_cast == TYPE_MATCH_FAILED) { ERROR_(unaryop->token->pos, "Bool negation operator expected bool type, got '%s'.", node_get_type_name(unaryop->expr)); @@ -1590,7 +1604,9 @@ CheckStatus check_if_expression(AstIfExpression* if_expr) { CHECK(expression, &if_expr->false_expr); TYPE_CHECK(&if_expr->cond, &basic_types[Basic_Kind_Bool]) { - if (!implicit_cast_to_bool(&if_expr->cond)) { + TypeMatch implicit_cast = implicit_cast_to_bool(&if_expr->cond); + if (implicit_cast == TYPE_MATCH_YIELD) YIELD(if_expr->token->pos, "Waiting for implicit cast to bool to check."); + if (implicit_cast == TYPE_MATCH_FAILED) { ERROR_(if_expr->token->pos, "If-expression expected boolean for condition, got '%s'.", type_get_name(if_expr->cond->type)); } diff --git a/compiler/src/utils.c b/compiler/src/utils.c index e5b42d5b..608a63f2 100644 --- a/compiler/src/utils.c +++ b/compiler/src/utils.c @@ -465,9 +465,8 @@ AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads, // NOTE: Overload is not something that is known to be overloadable. if (overload == NULL) continue; - if (overload == (AstFunction *) &node_that_signals_a_yield) return (AstTyped *) overload; if (overload->kind != Ast_Kind_Function) continue; - if (overload->type == NULL) { + if (overload == (AstFunction *) &node_that_signals_a_yield || overload->type == NULL) { // If it was not possible to create the type for this procedure, tell the // caller that this should yield and try again later. diff --git a/core/builtin.onyx b/core/builtin.onyx index 509867a8..d5a9d86e 100644 --- a/core/builtin.onyx +++ b/core/builtin.onyx @@ -340,6 +340,11 @@ __initialize_data_segments :: () -> void --- // #init, in the specified order. It should theoritically only be called once on the main thread. __run_init_procedures :: () -> void --- +// This overloaded procedure allow you to define an implicit rule for how to convert any value +// into a boolean. A default is provided for ALL pointer types and array types, but this can +// be used for structures or distinct types. +__implicit_bool_cast :: #match -> bool {} + #local { #if runtime.runtime == .Onyx {