From f7d3ffa2459ee6cd564242494b16ebfe925bc39f Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Thu, 11 Nov 2021 23:44:13 -0600 Subject: [PATCH] added interfaces and type constraints --- core/container/map.onyx | 9 ++- include/astnodes.h | 65 ++++++++++++++++++ include/lex.h | 2 + include/parser.h | 5 +- misc/onyx.sublime-syntax | 2 +- src/astnodes.c | 7 ++ src/checker.c | 145 ++++++++++++++++++++++++++++++++++++++- src/clone.c | 44 ++++++++++++ src/entities.c | 16 +++++ src/lex.c | 4 ++ src/onyx.c | 4 +- src/parser.c | 107 +++++++++++++++++++++++++---- src/polymorph.c | 18 ++--- src/symres.c | 38 ++++++++++ 14 files changed, 436 insertions(+), 30 deletions(-) diff --git a/core/container/map.onyx b/core/container/map.onyx index 819460b8..02ad2799 100644 --- a/core/container/map.onyx +++ b/core/container/map.onyx @@ -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; diff --git a/include/astnodes.h b/include/astnodes.h index b8ce6bba..4545ce9c 100644 --- a/include/astnodes.h +++ b/include/astnodes.h @@ -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; diff --git a/include/lex.h b/include/lex.h index 43fed670..fb9a9493 100644 --- a/include/lex.h +++ b/include/lex.h @@ -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, diff --git a/include/parser.h b/include/parser.h index f837a01a..2f48c483 100644 --- a/include/parser.h +++ b/include/parser.h @@ -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); diff --git a/misc/onyx.sublime-syntax b/misc/onyx.sublime-syntax index 1dd09072..9f0233b2 100644 --- a/misc/onyx.sublime-syntax +++ b/misc/onyx.sublime-syntax @@ -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' diff --git a/src/astnodes.c b/src/astnodes.c index 5a5d6dc6..98a84e49 100644 --- a/src/astnodes.c +++ b/src/astnodes.c @@ -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", diff --git a/src/checker.c b/src/checker.c index 7500b5e8..70395c35 100644 --- a/src/checker.c +++ b/src/checker.c @@ -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; diff --git a/src/clone.c b/src/clone.c index 9c5b693c..b7a5c981 100644 --- a/src/clone.c +++ b/src/clone.c @@ -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; } diff --git a/src/entities.c b/src/entities.c index 9b65e84b..6300629a 100644 --- a/src/entities.c +++ b/src/entities.c @@ -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; diff --git a/src/lex.c b/src/lex.c index b6bdfc39..a19b6759 100644 --- 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 '-': diff --git a/src/onyx.c b/src/onyx.c index 5fc1e878..522e6647 100644 --- a/src/onyx.c +++ b/src/onyx.c @@ -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); } diff --git a/src/parser.c b/src/parser.c index 093d20a2..50b57a7b 100644 --- a/src/parser.c +++ b/src/parser.c @@ -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, diff --git a/src/polymorph.c b/src/polymorph.c index f98c45be..7af1daaf 100644 --- a/src/polymorph.c +++ b/src/polymorph.c @@ -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, diff --git a/src/symres.c b/src/symres.c index 5d8af4a8..9757e6e8 100644 --- a/src/symres.c +++ b/src/symres.c @@ -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; } -- 2.25.1