added interfaces and type constraints
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 12 Nov 2021 05:44:13 +0000 (23:44 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 12 Nov 2021 05:44:13 +0000 (23:44 -0600)
14 files changed:
core/container/map.onyx
include/astnodes.h
include/lex.h
include/parser.h
misc/onyx.sublime-syntax
src/astnodes.c
src/checker.c
src/clone.c
src/entities.c
src/lex.c
src/onyx.c
src/parser.c
src/polymorph.c
src/symres.c

index 819460b82f3b3b843d4aa8591472cfb9860a2635..02ad27992c4feb40729a711e6d74c51ad6476911 100644 (file)
@@ -9,7 +9,14 @@ package core.map
     use package core.intrinsics.onyx { __zero_value, __initialize }
 }
 
-Map :: struct (K: type_expr, V: type_expr) {
+#private_file {
+    ValidKey :: interface (T: type_expr) {
+        hash.to_u32(T);
+        T == T;
+    }
+}
+
+Map :: struct (K: type_expr, V: type_expr) where ValidKey(K) {
     allocator : Allocator;
 
     hashes  : [] i32;
index b8ce6bba5b72a11d2d0517ffcbcac1c8a0264451..4545ce9c57fb40354c7bbb60982b82f6641ee739 100644 (file)
@@ -80,6 +80,8 @@
     NODE(Param)                \
     NODE(Function)             \
     NODE(OverloadedFunction)   \
+    NODE(Interface)            \
+    NODE(Constraint)           \
                                \
     NODE(PolyParam)            \
     NODE(PolySolution)         \
@@ -122,6 +124,9 @@ typedef enum AstKind {
     Ast_Kind_Function,
     Ast_Kind_Overloaded_Function,
     Ast_Kind_Polymorphic_Proc,
+    Ast_Kind_Interface,
+    Ast_Kind_Constraint,
+    Ast_Kind_Constraint_Sentinel,
     Ast_Kind_Block,
     Ast_Kind_Local,
     Ast_Kind_Global,
@@ -507,6 +512,19 @@ typedef enum BlockRule {
     Block_Rule_Do_Block   = Block_Rule_New_Scope | Block_Rule_Clear_Defer | Block_Rule_Override_Return | Block_Rule_Emit_Instructions,
 } BlockRule;
 
+typedef enum ConstraintCheckStatus {
+    Constraint_Check_Status_Queued,
+    Constraint_Check_Status_Failed,
+    Constraint_Check_Status_Success,
+} ConstraintCheckStatus;
+
+typedef struct ConstraintContext {
+    bh_arr(AstConstraint *) constraints;
+    ConstraintCheckStatus *constraint_checks;
+    b32 constraints_met : 1;
+} ConstraintContext;
+
+
 typedef struct Arguments Arguments;
 struct Arguments {
     bh_arr(AstTyped *) values;
@@ -790,6 +808,8 @@ struct AstStructType {
     struct Entity* entity_type;
     struct Entity* entity_defaults;
 
+    ConstraintContext constraints;
+
     b32 stcache_is_valid : 1;
     b32 is_union         : 1;
 };
@@ -919,6 +939,8 @@ struct AstFunction {
     struct Entity* entity_header;
     struct Entity* entity_body;
 
+    ConstraintContext constraints;
+
     b32 is_exported  : 1;
     b32 is_foreign   : 1;
     b32 is_intrinsic : 1;
@@ -945,6 +967,44 @@ struct AstOverloadedFunction {
     bh_imap            all_overloads;
 };
 
+// @CLEANUP: Is this really necessary?
+typedef struct InterfaceParam {
+    OnyxToken *token;
+    AstType   *type_node;
+    Type      *type;
+} InterfaceParam;
+
+struct AstInterface {
+    AstTyped_base;
+    char *name;
+
+    bh_arr(InterfaceParam) params;
+    bh_arr(AstTyped *)  exprs;
+};
+
+typedef enum ConstraintPhase {
+    Constraint_Phase_Waiting_To_Be_Queued,
+    Constraint_Phase_Cloning_Expressions,
+    Constraint_Phase_Checking_Expressions,
+    Constraint_Phase_Finished,
+} ConstraintPhase;
+
+struct AstConstraint {
+    AstNode_base;
+
+    ConstraintPhase phase;
+
+    AstInterface *interface;
+    bh_arr(AstType *) type_args;
+
+    ConstraintCheckStatus *report_status;
+
+    Scope* scope;
+    bh_arr(AstTyped *) exprs;
+    u32 expr_idx;
+};
+
+
 struct AstPackage {
     AstTyped_base;
 
@@ -1117,6 +1177,7 @@ typedef enum EntityState {
     Entity_State_Check_Types,
     Entity_State_Code_Gen,
     Entity_State_Finalized,
+    Entity_State_Failed,
 
     Entity_State_Count,
 } EntityState;
@@ -1141,6 +1202,8 @@ typedef enum EntityType {
     Entity_Type_Type_Alias,
     Entity_Type_Memory_Reservation_Type,
     Entity_Type_Use,
+    Entity_Type_Interface,
+    Entity_Type_Constraint_Check,
     Entity_Type_Polymorphic_Proc,
     Entity_Type_Macro,
     Entity_Type_Foreign_Function_Header,
@@ -1195,6 +1258,8 @@ typedef struct Entity {
         AstPolyProc           *poly_proc;
         AstMacro              *macro;
         AstUse                *use;
+        AstInterface          *interface;
+        AstConstraint         *constraint;
     };
 } Entity;
 
index 43fed6705474bc40a39230fc18ce4883fb2c3d10..fb9a9493a01706ea84663bfa054699ddc7bdf3b0 100644 (file)
@@ -39,6 +39,8 @@ typedef enum TokenType {
     Token_Type_Keyword_Switch,
     Token_Type_Keyword_Fallthrough,
     Token_Type_Keyword_Macro,
+    Token_Type_Keyword_Interface,
+    Token_Type_Keyword_Where,
 
     Token_Type_Right_Arrow,
     Token_Type_Left_Arrow,
index f837a01afd610a72208d44aa29f728bc0bd02294..2f48c4833330e4f28b1abec33da910fd258b1874 100644 (file)
@@ -33,10 +33,7 @@ typedef struct OnyxParser {
 
     b32 hit_unexpected_token : 1;
 
-    // If this is 1, then things that look like calls with immediately be parsed as calls.
-    // However, if this is 0 then things that look like procedure definitions after expressions,
-    // will be parsed as procedure definitions.
-    b32 greedily_consume_calls : 1;
+    b32 parse_calls : 1;
 } OnyxParser;
 
 const char* onyx_ast_node_kind_string(AstKind kind);
index 1dd0907205fab88372aa1aa71ce2cd5211a2c4cd..9f0233b29acb3e9d132d5a604a6b06ebd98ac457 100644 (file)
@@ -29,7 +29,7 @@ contexts:
     # strings in YAML. When using single quoted strings, only single quotes
     # need to be escaped: this is done by using two single quotes next to each
     # other.
-    - match: '\b(package|struct|use|global|enum|if|elseif|else|for|while|do|break|continue|fallthrough|return|as|cast|sizeof|alignof|typeof|defer|switch|case|macro)\b'
+    - match: '\b(package|struct|interface|use|where|global|enum|if|elseif|else|for|while|do|break|continue|fallthrough|return|as|cast|sizeof|alignof|typeof|defer|switch|case|macro)\b'
       scope: keyword.control.onyx
 
     - match: '\b(bool|void|i8|u8|i16|u16|i32|u32|i64|u64|f32|f64|rawptr|str|cstr|range|type_expr|any)\b'
index 5a5d6dc6a48253223d9feae3171a6a18ae04f338..98a84e49902c81dd9be2dd8f1832d8570d975bea 100644 (file)
@@ -14,6 +14,9 @@ static const char* ast_node_names[] = {
     "FUNCTION",
     "OVERLOADED_FUNCTION",
     "POLYMORPHIC PROC",
+    "INTERFACE",
+    "CONSTRAINT",
+    "CONSTRAINT SENTITEL",
     "BLOCK",
     "LOCAL",
     "GLOBAL",
@@ -127,6 +130,7 @@ const char* entity_state_strings[Entity_State_Count] = {
     "Resolve Symbols",
     "Check Types",
     "Code Gen",
+    "Failed",
     "Finalized",
 };
 
@@ -145,9 +149,12 @@ const char* entity_type_strings[Entity_Type_Count] = {
     "Type Alias",
     "Memory Reservation Type",
     "Use",
+    "Interface",
+    "Constraint Check",
     "Polymorphic Proc",
     "Macro",
     "Foreign_Function Header",
+    "Temporary Function Header",
     "Function Header",
     "Global Header",
     "Process Directive",
index 7500b5e8e789ff9bc8cae1f5e9cf219d020d9841..70395c3525e0947fcff34326be2154e2760b3988 100644 (file)
@@ -56,6 +56,7 @@ typedef enum CheckStatus {
     Check_Errors_Start,
     Check_Return_To_Symres, // Return this node for further symres processing
     Check_Yield_Macro,
+    Check_Failed,           // The node is done processing and should be put in the state of Failed.
     Check_Error,    // There was an error when checking the node
 } CheckStatus;
 
@@ -94,6 +95,8 @@ CheckStatus check_memres(AstMemRes* memres);
 CheckStatus check_type(AstType* type);
 CheckStatus check_insert_directive(AstDirectiveInsert** pinsert);
 CheckStatus check_do_block(AstDoBlock** pdoblock);
+CheckStatus check_constraint(AstConstraint *constraint);
+CheckStatus check_constraint_context(ConstraintContext *cc, OnyxFilePos pos);
 
 // HACK HACK HACK
 b32 expression_types_must_be_known = 0;
@@ -497,6 +500,12 @@ CheckStatus check_call(AstCall** pcall) {
     AstFunction* callee=NULL;
     CHECK(resolve_callee, call, (AstTyped **) &callee);
 
+    if (callee->kind == Ast_Kind_Function) {
+        if (callee->constraints.constraints != NULL && callee->constraints.constraints_met == 0) {
+            YIELD(call->token->pos, "Waiting for constraints to be checked on callee.");
+        }
+    }
+
     i32 arg_count = get_argument_buffer_size(&callee->type->Function, &call->args);
     arguments_ensure_length(&call->args, arg_count);
 
@@ -1714,6 +1723,7 @@ CheckStatus check_expression(AstTyped** pexpr) {
         case Ast_Kind_Package: break;
         case Ast_Kind_Error: break;
         case Ast_Kind_Unary_Field_Access: break;
+        case Ast_Kind_Constraint_Sentinel: break;
 
         // NOTE: The only way to have an Intrinsic_Call node is to have gone through the
         // checking of a call node at least once.
@@ -1930,6 +1940,10 @@ CheckStatus check_struct(AstStructType* s_node) {
     if (s_node->entity_defaults && s_node->entity_defaults->state < Entity_State_Check_Types)
         YIELD(s_node->token->pos, "Waiting for struct member defaults to pass symbol resolution.");
 
+    if (s_node->constraints.constraints) {
+        CHECK(constraint_context, &s_node->constraints, s_node->token->pos);
+    }
+
     bh_arr_each(AstStructMember *, smem, s_node->members) {
         if ((*smem)->type_node != NULL) {
             CHECK(type, (*smem)->type_node);
@@ -2012,7 +2026,14 @@ CheckStatus check_struct_defaults(AstStructType* s_node) {
 }
 
 CheckStatus check_temp_function_header(AstFunction* func) {
-    CHECK(function_header, func);
+    CheckStatus cs = check_function_header(func);
+    if (cs == Check_Error) {
+        onyx_clear_errors();
+        return Check_Failed;
+    }
+
+    if (cs != Check_Success) return cs;
+
     return Check_Complete;
 }
 
@@ -2106,6 +2127,10 @@ CheckStatus check_function_header(AstFunction* func) {
 
     if (func->return_type != NULL) CHECK(type, func->return_type);
 
+    if (func->constraints.constraints != NULL) {
+        CHECK(constraint_context, &func->constraints, func->token->pos);
+    }
+
     func->type = type_build_function_type(context.ast_alloc, func);
     if (func->type == NULL) YIELD(func->token->pos, "Waiting for function type to be constructed");
 
@@ -2350,6 +2375,120 @@ CheckStatus check_macro(AstMacro* macro) {
     return Check_Success;
 }
 
+CheckStatus check_constraint(AstConstraint *constraint) {
+    switch (constraint->phase) {
+        case Constraint_Phase_Cloning_Expressions: {
+            if (constraint->interface->kind != Ast_Kind_Interface) {
+                // CLEANUP: This error message might not look totally right in some cases.
+                ERROR_(constraint->token->pos, "'%b' is not an interface.", constraint->token->text, constraint->token->length);
+            }
+
+            bh_arr_new(global_heap_allocator, constraint->exprs, bh_arr_length(constraint->interface->exprs));
+            bh_arr_each(AstTyped *, expr, constraint->interface->exprs) {
+                bh_arr_push(constraint->exprs, (AstTyped *) ast_clone(context.ast_alloc, (AstNode *) *expr));
+            }
+
+            assert(constraint->interface->entity && constraint->interface->entity->scope);
+
+            constraint->scope = scope_create(context.ast_alloc, constraint->interface->entity->scope, constraint->token->pos);
+
+            fori (i, 0, bh_arr_length(constraint->interface->params)) {
+                InterfaceParam *ip = &constraint->interface->params[i];
+
+                AstTyped *sentinel = onyx_ast_node_new(context.ast_alloc, sizeof(AstTyped), Ast_Kind_Constraint_Sentinel);
+                sentinel->token = ip->token;
+                sentinel->type_node = constraint->type_args[i];
+
+                symbol_introduce(constraint->scope, ip->token, (AstNode *) sentinel);
+            }
+
+            assert(constraint->entity);
+            constraint->entity->scope = constraint->scope;
+
+            constraint->phase = Constraint_Phase_Checking_Expressions;
+            return Check_Return_To_Symres;
+        }
+
+        case Constraint_Phase_Checking_Expressions: {
+            fori (i, constraint->expr_idx, bh_arr_length(constraint->exprs)) {
+                CheckStatus cs = check_expression(&constraint->exprs[i]);
+                if (cs == Check_Return_To_Symres || cs == Check_Yield_Macro) {
+                    return cs;
+                }
+
+                if (cs == Check_Error) {
+                    // HACK HACK HACK
+                    onyx_clear_errors();
+
+                    // onyx_report_error(constraint->interface->exprs[i]->token->pos, "This constraint was not satisfied.");
+                    // onyx_report_error(constraint->token->pos, "Here was where the interface was used.");
+                    *constraint->report_status = Constraint_Check_Status_Failed;
+                    return Check_Failed;
+                }
+
+                constraint->expr_idx++;
+            }
+
+            *constraint->report_status = Constraint_Check_Status_Success;
+            return Check_Complete;
+        }
+    }
+
+    return Check_Success;
+}
+
+CheckStatus check_constraint_context(ConstraintContext *cc, OnyxFilePos pos) {
+    if (cc->constraint_checks) {
+        if (cc->constraints_met == 1) return Check_Success;
+
+        fori (i, 0, bh_arr_length(cc->constraints)) {
+            if (cc->constraint_checks[i] == Constraint_Check_Status_Failed) {
+                AstConstraint *constraint = cc->constraints[i];
+                char constraint_map[512] = {0};
+                fori (i, 0, bh_arr_length(constraint->type_args)) {
+                    if (i != 0) strncat(constraint_map, ", ", 511);
+
+                    OnyxToken* symbol = constraint->interface->params[i].token;
+                    token_toggle_end(symbol);
+                    strncat(constraint_map, symbol->text, 511);
+                    token_toggle_end(symbol);
+
+                    strncat(constraint_map, " is of type '", 511);
+                    strncat(constraint_map, type_get_name(type_build_from_ast(context.ast_alloc, constraint->type_args[i])), 511);
+                    strncat(constraint_map, "'", 511);
+                }
+
+                onyx_report_error(constraint->exprs[constraint->expr_idx]->token->pos, "Failed to satisfy constraint where %s.", constraint_map);
+                onyx_report_error(constraint->token->pos, "Here is where the interface was used.");
+                return Check_Error;
+            }
+
+            if (cc->constraint_checks[i] == Constraint_Check_Status_Queued) {
+                YIELD(pos, "Waiting for constraints to be checked.");
+            }
+        }
+
+        cc->constraints_met = 1;
+        return Check_Success;
+
+    } else {
+        u32 count = bh_arr_length(cc->constraints);
+        ConstraintCheckStatus *ccs = bh_alloc_array(context.ast_alloc, ConstraintCheckStatus, count);
+
+        cc->constraint_checks = ccs;
+
+        fori (i, 0, count) {
+            ccs[i] = Constraint_Check_Status_Queued;
+            cc->constraints[i]->report_status = &ccs[i];
+            cc->constraints[i]->phase = Constraint_Phase_Cloning_Expressions;
+
+            add_entities_for_node(NULL, (AstNode *) cc->constraints[i], NULL, NULL);
+        }
+
+        return Check_Yield_Macro;
+    }
+}
+
 CheckStatus check_node(AstNode* node) {
     switch (node->kind) {
         case Ast_Kind_Function:             return check_function((AstFunction *) node);
@@ -2379,7 +2518,8 @@ void check_entity(Entity* ent) {
         case Entity_Type_Memory_Reservation_Type:  cs = check_memres_type(ent->mem_res); break;
         case Entity_Type_Memory_Reservation:       cs = check_memres(ent->mem_res); break;
         case Entity_Type_Static_If:                cs = check_static_if(ent->static_if); break;
-        case Entity_Type_Macro:                    cs = check_macro(ent->macro);
+        case Entity_Type_Macro:                    cs = check_macro(ent->macro); break;
+        case Entity_Type_Constraint_Check:         cs = check_constraint(ent->constraint); break;
 
         case Entity_Type_Expression:
             cs = check_expression(&ent->expr);
@@ -2407,6 +2547,7 @@ void check_entity(Entity* ent) {
     if (cs == Check_Success)          ent->state = Entity_State_Code_Gen;
     if (cs == Check_Complete)         ent->state = Entity_State_Finalized;
     if (cs == Check_Return_To_Symres) ent->state = Entity_State_Resolve_Symbols;
+    if (cs == Check_Failed)           ent->state = Entity_State_Failed;
     if (cs == Check_Yield_Macro)      ent->macro_attempts++;
     else {
         ent->macro_attempts = 0;
index 9c5b693cf6d533cdcfb840f1cbf0e2b4c4754098..b7a5c981e6ae68aae082d3cef58e03c8d57a6f75 100644 (file)
@@ -99,6 +99,7 @@ static inline i32 ast_kind_to_size(AstNode* node) {
         case Ast_Kind_If_Expression: return sizeof(AstIfExpression);
         case Ast_Kind_Directive_Insert: return sizeof(AstDirectiveInsert);
         case Ast_Kind_Do_Block: return sizeof(AstDoBlock);
+        case Ast_Kind_Constraint: return sizeof(AstConstraint);
         case Ast_Kind_Count: return 0;
     }
 
@@ -320,6 +321,15 @@ AstNode* ast_clone(bh_allocator a, void* n) {
             bh_arr_each(AstTyped *, tag, ss->meta_tags) {
                 bh_arr_push(ds->meta_tags, (AstTyped *) ast_clone(a, *tag));
             }
+            
+            if (ss->constraints.constraints) {
+                memset(&ds->constraints, 0, sizeof(ConstraintContext));
+                bh_arr_new(global_heap_allocator, ds->constraints.constraints, bh_arr_length(ss->constraints.constraints));
+
+                bh_arr_each(AstConstraint *, constraint, ss->constraints.constraints) {
+                    bh_arr_push(ds->constraints.constraints, (AstConstraint *) ast_clone(a, (AstNode *) *constraint));
+                }
+            }
 
             ds->stcache = NULL;
             break;
@@ -409,6 +419,31 @@ AstNode* ast_clone(bh_allocator a, void* n) {
                 bh_arr_push(df->params, new_param);
             }
 
+            if (sf->constraints.constraints) {
+                memset(&df->constraints, 0, sizeof(ConstraintContext));
+                bh_arr_new(global_heap_allocator, df->constraints.constraints, bh_arr_length(sf->constraints.constraints));
+
+                bh_arr_each(AstConstraint *, constraint, sf->constraints.constraints) {
+                    bh_arr_push(df->constraints.constraints, (AstConstraint *) ast_clone(a, (AstNode *) *constraint));
+                }
+            }
+
+            break;
+        }
+
+        case Ast_Kind_Constraint: {
+            C(AstConstraint, interface);
+
+            AstConstraint* dc = (AstConstraint *) nn;
+            AstConstraint* sc = (AstConstraint *) node;
+
+            dc->type_args = NULL;
+            bh_arr_new(global_heap_allocator, dc->type_args, bh_arr_length(sc->type_args));
+
+            bh_arr_each(AstType *, type_arg, sc->type_args) {
+                bh_arr_push(dc->type_args, (AstType *) ast_clone(a, (AstNode *) *type_arg));
+            }
+
             break;
         }
 
@@ -502,6 +537,15 @@ AstFunction* clone_function_header(bh_allocator a, AstFunction* func) {
         bh_arr_push(new_func->params, new_param);
     }
 
+    if (func->constraints.constraints) {
+        memset(&new_func->constraints, 0, sizeof(ConstraintContext));
+        bh_arr_new(global_heap_allocator, new_func->constraints.constraints, bh_arr_length(func->constraints.constraints));
+
+        bh_arr_each(AstConstraint *, constraint, func->constraints.constraints) {
+            bh_arr_push(new_func->constraints.constraints, (AstConstraint *) ast_clone(a, (AstNode *) *constraint));
+        }
+    }
+
     return new_func;
 }
 
index 9b65e84bc8976b70e50aa846f1d03ba3bec1e03a..6300629a695da1b2d4908014146a06fa99d6cbcf 100644 (file)
@@ -337,6 +337,22 @@ void add_entities_for_node(bh_arr(Entity *) *target_arr, AstNode* node, Scope* s
             break;
         }
 
+        case Ast_Kind_Interface: {
+            ent.type = Entity_Type_Interface;
+            ent.interface = (AstInterface *) node;
+            ent.state = Entity_State_Resolve_Symbols;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
+        case Ast_Kind_Constraint: {
+            ent.type = Entity_Type_Constraint_Check;
+            ent.constraint = (AstConstraint *) node;
+            ent.state = Entity_State_Resolve_Symbols;
+            ENTITY_INSERT(ent);
+            break;
+        }
+
         default: {
             ent.type = Entity_Type_Expression;
             ent.expr = (AstTyped *) node;
index b6bdfc399b5d8d2195b3900577e45ef5dd576649..a19b6759ed8ffe2e31973b7f088cf30d49b777cd 100644 (file)
--- a/src/lex.c
+++ b/src/lex.c
@@ -37,6 +37,8 @@ static const char* token_type_names[] = {
     "case",
     "fallthrough",
     "macro",
+    "interface",
+    "where",
 
     "->",
     "<-",
@@ -357,6 +359,7 @@ whitespace_skipped:
         break;
     case 'i':
         LITERAL_TOKEN("if",          1, Token_Type_Keyword_If);
+        LITERAL_TOKEN("interface",   1, Token_Type_Keyword_Interface);
         break;
     case 'm':
         LITERAL_TOKEN("macro",       1, Token_Type_Keyword_Macro);
@@ -382,6 +385,7 @@ whitespace_skipped:
         break;
     case 'w':
         LITERAL_TOKEN("while",       1, Token_Type_Keyword_While);
+        LITERAL_TOKEN("where",       1, Token_Type_Keyword_Where);
         break;
 
     case '-':
index 5fc1e87842a22fb3b5964fa2f6ec9d7b0eed3d6c..522e664723f205cb8ea3f357f801a5ab55e55d15 100644 (file)
@@ -401,7 +401,7 @@ static void output_dummy_progress_bar() {
 
     static const char* state_colors[] = {
         "\e[91m", "\e[93m", "\e[94m", "\e[93m", 
-        "\e[97m", "\e[95m", "\e[96m", "\e[92m",
+        "\e[97m", "\e[95m", "\e[96m", "\e[92m", "\e[91m",
     };
 
     printf("\e[2;1H");
@@ -514,7 +514,7 @@ static i32 onyx_compile() {
 
         if (onyx_has_errors()) return ONYX_COMPILER_PROGRESS_ERROR;
 
-        if (ent->state != Entity_State_Finalized)
+        if (ent->state != Entity_State_Finalized && ent->state != Entity_State_Failed)
             entity_heap_insert_existing(&context.entities, ent);
     }
 
index 093d20a280e96cf593fa0f5c1eebdd3fd8db7a0a..50b57a7bba9ada8873ae9305511ec339e23cd9ad 100644 (file)
@@ -57,6 +57,8 @@ static AstType*       parse_type(OnyxParser* parser);
 static AstTypeOf*     parse_typeof(OnyxParser* parser);
 static AstStructType* parse_struct(OnyxParser* parser);
 static AstInterface*  parse_interface(OnyxParser* parser);
+static AstConstraint* parse_constraint(OnyxParser* parser);
+static void           parse_constraints(OnyxParser* parser, ConstraintContext *constraints);
 static void           parse_function_params(OnyxParser* parser, AstFunction* func);
 static b32            parse_possible_directive(OnyxParser* parser, const char* dir);
 static b32            parse_possible_function_definition_no_consume(OnyxParser* parser);
@@ -728,10 +730,7 @@ static AstTyped* parse_factor(OnyxParser* parser) {
             }
 
             case '(': {
-                if (!parser->greedily_consume_calls) {
-                    if (parse_possible_function_definition_no_consume(parser)) goto factor_parsed;
-                    if (parse_possible_quick_function_definition_no_consume(parser)) goto factor_parsed;
-                }
+                if (!parser->parse_calls) goto factor_parsed;
 
                 AstCall* call_node = make_node(AstCall, Ast_Kind_Call);
                 call_node->token = expect_token(parser, '(');
@@ -1739,7 +1738,7 @@ static AstType* parse_type(OnyxParser* parser) {
                     *next_insertion = (AstType *) field;
                 }
 
-                if (parser->curr->type == '(') {
+                if (parser->curr->type == '(' && parser->parse_calls) {
                     OnyxToken* paren_token = expect_token(parser, '(');
 
                     bh_arr(AstNode *) params = NULL;
@@ -1876,6 +1875,10 @@ static AstStructType* parse_struct(OnyxParser* parser) {
         poly_struct->base_struct = s_node;
     }
 
+    if (parser->curr->type == Token_Type_Keyword_Where) {
+        parse_constraints(parser, &s_node->constraints);
+    }
+
     bh_arr_new(global_heap_allocator, s_node->members, 4);
 
     while (parser->curr->type == '#') {
@@ -2033,6 +2036,80 @@ static AstStructType* parse_struct(OnyxParser* parser) {
     }
 }
 
+static AstInterface* parse_interface(OnyxParser* parser) {
+    AstInterface *interface = make_node(AstInterface, Ast_Kind_Interface);
+    interface->token = expect_token(parser, Token_Type_Keyword_Interface);
+
+    bh_arr_new(global_heap_allocator, interface->params, 2);
+
+    expect_token(parser, '(');
+    while (!consume_token_if_next(parser, ')')) {
+        if (parser->hit_unexpected_token) return interface;
+
+        InterfaceParam ip;
+        ip.token = expect_token(parser, Token_Type_Symbol);
+        expect_token(parser, ':');
+        ip.type_node = parse_type(parser);
+
+        bh_arr_push(interface->params, ip);
+
+        if (parser->curr->type != ')')
+            expect_token(parser, ',');
+    }
+
+    bh_arr_new(global_heap_allocator, interface->exprs, 2);
+
+    expect_token(parser, '{');
+    while (!consume_token_if_next(parser, '}')) {
+        if (parser->hit_unexpected_token) return interface;
+
+        AstTyped *expr = parse_expression(parser, 0);
+        bh_arr_push(interface->exprs, expr);
+
+        expect_token(parser, ';');
+    }
+
+    return interface;
+}
+
+static AstConstraint* parse_constraint(OnyxParser* parser) {
+    AstConstraint* constraint = make_node(AstConstraint, Ast_Kind_Constraint);
+
+    parser->parse_calls = 0;
+    constraint->interface = (AstInterface *) parse_factor(parser);
+    parser->parse_calls = 1;
+
+    constraint->token = constraint->interface->token;
+
+    bh_arr_new(global_heap_allocator, constraint->type_args, 2);
+
+    expect_token(parser, '(');
+    while (!consume_token_if_next(parser, ')')) {
+        if (parser->hit_unexpected_token) return constraint;
+
+        AstType* type_node = parse_type(parser);
+        bh_arr_push(constraint->type_args, type_node);
+
+        if (parser->curr->type != ')')
+            expect_token(parser, ',');
+    }
+
+    return constraint;
+}
+
+static void parse_constraints(OnyxParser* parser, ConstraintContext *out_constraints) {
+    bh_arr_new(global_heap_allocator, out_constraints->constraints, 2);
+
+    expect_token(parser, Token_Type_Keyword_Where);
+
+    do {
+        AstConstraint *constraint = parse_constraint(parser);
+        if (parser->hit_unexpected_token) return;
+
+        bh_arr_push(out_constraints->constraints, constraint);
+    } while (consume_token_if_next(parser, ','));
+}
+
 static void parse_function_params(OnyxParser* parser, AstFunction* func) {
     expect_token(parser, '(');
 
@@ -2228,6 +2305,10 @@ static AstFunction* parse_function_definition(OnyxParser* parser, OnyxToken* tok
         }
     }
 
+    if (parser->curr->type == Token_Type_Keyword_Where) {
+        parse_constraints(parser, &func_def->constraints);
+    }
+
     while (parser->curr->type == '#') {
         if (parse_possible_directive(parser, "intrinsic")) {
             func_def->is_intrinsic = 1;
@@ -2608,10 +2689,11 @@ static AstMacro* parse_macro(OnyxParser* parser) {
 }
 
 static AstTyped* parse_top_level_expression(OnyxParser* parser) {
-    if (parser->curr->type == Token_Type_Keyword_Global) return parse_global_declaration(parser);
-    if (parser->curr->type == Token_Type_Keyword_Struct) return (AstTyped *) parse_struct(parser);
-    if (parser->curr->type == Token_Type_Keyword_Enum)   return (AstTyped *) parse_enum_declaration(parser);
-    if (parser->curr->type == Token_Type_Keyword_Macro)  return (AstTyped *) parse_macro(parser);
+    if (parser->curr->type == Token_Type_Keyword_Global)    return parse_global_declaration(parser);
+    if (parser->curr->type == Token_Type_Keyword_Struct)    return (AstTyped *) parse_struct(parser);
+    if (parser->curr->type == Token_Type_Keyword_Interface) return (AstTyped *) parse_interface(parser);
+    if (parser->curr->type == Token_Type_Keyword_Enum)      return (AstTyped *) parse_enum_declaration(parser);
+    if (parser->curr->type == Token_Type_Keyword_Macro)     return (AstTyped *) parse_macro(parser);
 
     if (parse_possible_directive(parser, "type")) {
         AstTypeAlias* alias = make_node(AstTypeAlias, Ast_Kind_Type_Alias);
@@ -2696,6 +2778,7 @@ static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol
         case Ast_Kind_StrLit:
             break;
 
+        case Ast_Kind_Interface:
         case Ast_Kind_Struct_Type:
         case Ast_Kind_Poly_Struct_Type:
         case Ast_Kind_Enum_Type:
@@ -2881,9 +2964,9 @@ static void parse_top_level_statement(OnyxParser* parser) {
                 AstDirectiveAddOverload *add_overload = make_node(AstDirectiveAddOverload, Ast_Kind_Directive_Add_Overload);
                 add_overload->token = dir_token;
 
-                parser->greedily_consume_calls = 0;
+                parser->parse_calls = 0;
                 add_overload->overloaded_function = (AstNode *) parse_expression(parser, 0);
-                parser->greedily_consume_calls = 1;
+                parser->parse_calls = 1;
 
                 if (parse_possible_directive(parser, "precedence")) {
                     AstNumLit* pre = parse_int_literal(parser);
@@ -3072,7 +3155,7 @@ OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer) {
     parser.alternate_entity_placement_stack = NULL;
     parser.current_symbol_stack = NULL;
     parser.scope_flags = NULL;
-    parser.greedily_consume_calls = 1;
+    parser.parse_calls = 1;
 
     parser.polymorph_context = (PolymorphicContext) {
         .root_node = NULL,
index f98c45be6d2afcccba29e33cbfb4aecf9345ca46..7af1daaf158f74e731af23d3eb846268d3709e48 100644 (file)
@@ -91,15 +91,12 @@ static char* build_poly_slns_unique_key(bh_arr(AstPolySolution) slns) {
 // NOTE: This function adds a solidified function to the entity heap for it to be processed
 // later. It optionally can start the function header entity at the code generation state if
 // the header has already been processed.
-static b32 add_solidified_function_entities(AstSolidifiedFunction solidified_func, b32 header_already_processed) {
+static b32 add_solidified_function_entities(AstSolidifiedFunction solidified_func) {
     solidified_func.func->flags |= Ast_Flag_Function_Used;
     solidified_func.func->flags |= Ast_Flag_From_Polymorphism;
 
-    EntityState header_start_state = Entity_State_Resolve_Symbols;
-    if (header_already_processed) header_start_state = Entity_State_Code_Gen;
-
     Entity func_header_entity = {
-        .state = header_start_state,
+        .state = Entity_State_Resolve_Symbols,
         .type = Entity_Type_Function_Header,
         .function = solidified_func.func,
         .package = NULL,
@@ -180,7 +177,7 @@ static void ensure_solidified_function_has_body(AstPolyProc* pp, AstSolidifiedFu
         // header. This should never be the case in this situation, since the header would
         // have to have successfully passed type checking before it would become a solidified
         // procedure.
-        assert(add_solidified_function_entities(solidified_func, 1));
+        assert(add_solidified_function_entities(solidified_func));
 
         solidified_func.func->flags &= ~Ast_Flag_Incomplete_Body;
     }
@@ -649,7 +646,7 @@ AstFunction* polymorphic_proc_solidify(AstPolyProc* pp, bh_arr(AstPolySolution)
     // NOTE: Cache the function for later use, reducing duplicate functions.
     bh_table_put(AstSolidifiedFunction, pp->concrete_funcs, unique_key, solidified_func);
 
-    add_solidified_function_entities(solidified_func, 0);
+    add_solidified_function_entities(solidified_func);
 
     return (AstFunction *) &node_that_signals_a_yield;
 }
@@ -728,7 +725,12 @@ AstFunction* polymorphic_proc_build_only_header_with_slns(AstPolyProc* pp, bh_ar
         solidified_func = generate_solidified_function(pp, slns, NULL, 1);
     }
 
-    if (solidified_func.func_header_entity && solidified_func.func_header_entity->state == Entity_State_Finalized) return solidified_func.func;
+    if (solidified_func.func_header_entity) {
+        if (solidified_func.func_header_entity->state == Entity_State_Finalized) return solidified_func.func;
+        if (solidified_func.func_header_entity->state == Entity_State_Failed)    return NULL;
+
+        return (AstFunction *) &node_that_signals_a_yield;
+    }
 
     Entity func_header_entity = {
         .state = Entity_State_Resolve_Symbols,
index 5d8af4a8dec7fadff57398a5d31b362420bf5f10..9757e6e853e67507127f634103ba214086884d10 100644 (file)
@@ -57,6 +57,7 @@ static SymresStatus symres_memres(AstMemRes** memres);
 static SymresStatus symres_struct_defaults(AstType* st);
 static SymresStatus symres_static_if(AstIf* static_if);
 static SymresStatus symres_macro(AstMacro* macro);
+static SymresStatus symres_constraint(AstConstraint* constraint);
 
 static void scope_enter(Scope* new_scope) {
     curr_scope = new_scope;
@@ -102,6 +103,12 @@ static SymresStatus symres_struct_type(AstStructType* s_node) {
         scope_enter(s_node->scope);
     }
 
+    if (s_node->constraints.constraints) {
+        bh_arr_each(AstConstraint *, constraint, s_node->constraints.constraints) {
+            SYMRES(constraint, *constraint);
+        }
+    }
+
     fori (i, 0, bh_arr_length(s_node->members)) {
         AstStructMember *member = s_node->members[i];
 
@@ -893,6 +900,12 @@ SymresStatus symres_function_header(AstFunction* func) {
         onyx_report_error(func->token->pos, "Return type is not a type.");
     }
 
+    if (func->constraints.constraints != NULL) {
+        bh_arr_each(AstConstraint *, constraint, func->constraints.constraints) {
+            SYMRES(constraint, *constraint);
+        }
+    }
+
     scope_leave();
 
     return Symres_Success;
@@ -1219,6 +1232,30 @@ static SymresStatus symres_macro(AstMacro* macro) {
     return Symres_Success;
 }
 
+static SymresStatus symres_constraint(AstConstraint* constraint) {
+    switch (constraint->phase) {
+        case Constraint_Phase_Waiting_To_Be_Queued: {
+            SYMRES(expression, (AstTyped **) &constraint->interface);
+
+            bh_arr_each(AstType *, type_arg, constraint->type_args) {
+                SYMRES(type, type_arg);
+            }
+
+            return Symres_Success;
+        }
+
+        case Constraint_Phase_Checking_Expressions: {
+            fori (i, constraint->expr_idx, bh_arr_length(constraint->exprs)) {
+                SYMRES(expression, &constraint->exprs[i]);
+            }
+
+            return Symres_Success;
+        }
+    }
+
+    return Symres_Success;
+}
+
 void symres_entity(Entity* ent) {
     Scope* old_scope = NULL;
     if (ent->scope) {
@@ -1267,6 +1304,7 @@ void symres_entity(Entity* ent) {
         case Entity_Type_Struct_Member_Default:   ss = symres_struct_defaults((AstType *) ent->type_alias); break;
         case Entity_Type_Process_Directive:       ss = symres_process_directive((AstNode *) ent->expr); break;
         case Entity_Type_Macro:                   ss = symres_macro(ent->macro); break;
+        case Entity_Type_Constraint_Check:        ss = symres_constraint(ent->constraint); break;
 
         default: break;
     }