added __implicit_bool_cast
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 18 Jan 2023 19:20:33 +0000 (13:20 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 18 Jan 2023 19:20:33 +0000 (13:20 -0600)
Allows users to customize how user-defined values become bools

compiler/include/astnodes.h
compiler/src/astnodes.c
compiler/src/builtins.c
compiler/src/checker.c
compiler/src/utils.c
core/builtin.onyx

index cb0a4bdf78abcbc158a4fe73c38d1a217c3be014..f70ae309d2426a174feb7435a505a5d7d0666192 100644 (file)
@@ -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);
 
index aa2f757f1e8bfc4bd304a90d80667fc4649459d4..2f1e7ddea863fe7947de7cf49b9f933ad01cc2ba 100644 (file)
@@ -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) {
index 86d6a441a711d230d43a6e33291c256837f0f079..6992124f0357c827760ab1d025e769ecf2e6e346 100644 (file)
@@ -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.");
index 1b6888a85871c8e73c5f21ae17b9e886d6c1448f..c0f58c246b8cbe037c8d1ef0d6c01abb99057beb 100644 (file)
@@ -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));
         }
index e5b42d5b93f54673b8dc8f6cc56105babd872bb7..608a63f2d614d046fec36e352c660a5bdfd16bfa 100644 (file)
@@ -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.
 
index 509867a807dbf24ba4a8c3ead05a5fb84a5204b6..d5a9d86e2fdc69c082a1f32a475c9b5b2dc4a076 100644 (file)
@@ -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 {