From 34f829c01820e40344eaacb1b6720e7a48a0387e Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Tue, 31 Dec 2024 17:29:06 -0600 Subject: [PATCH 01/17] trying out expanding for-loops using macros The main problem occuring at the moment is that the body of a for-loop expansion has already been through symbol resolution, so it's symbols are already completely known. When the macro is expanded however, the cloned symbols are reinserted and cause duplicate symbol definitions. --- compiler/include/astnodes.h | 9 ++++++ compiler/src/astnodes.c | 56 +++++++++++++++++++++++++++++++++++++ compiler/src/builtins.c | 6 ++++ compiler/src/checker.c | 56 +++++++++++++++++++++++-------------- core/builtin.onyx | 9 ++++++ 5 files changed, 115 insertions(+), 21 deletions(-) diff --git a/compiler/include/astnodes.h b/compiler/include/astnodes.h index d2b01a3e..74fc7dd2 100644 --- a/compiler/include/astnodes.h +++ b/compiler/include/astnodes.h @@ -879,6 +879,12 @@ struct AstFor { AstBlock *stmt; + // NOTE: This is set when a for-loop isn't over a primitive type + // and instead is over a custom type, such as `Iterator` or `Map`. + // To properly invoke `__for_expansion`, we need to store the prepared + // call, as there could be overloads we have to wait for by yielding. + AstCall *intermediate_macro_expansion; + // ROBUSTNESS: This should be able to be set by a compile time variable at some point. // But for now, this will do. b32 by_pointer : 1; @@ -2105,6 +2111,7 @@ struct CompilerBuiltins { bh_arr(AstFunction *) init_procedures; AstOverloadedFunction *implicit_bool_cast; AstOverloadedFunction *dispose_used_local; + AstOverloadedFunction *for_expansion; }; typedef struct TypeStore TypeStore; @@ -2357,6 +2364,8 @@ AstPolyCallType* convert_call_to_polycall(Context *context, AstCall* call); void insert_auto_dispose_call(Context *context, AstLocal *local); +AstCall * create_implicit_for_expansion_call(Context *context, AstFor *fornode); + typedef struct OverloadReturnTypeCheck { Type *expected_type; AstTyped *node; diff --git a/compiler/src/astnodes.c b/compiler/src/astnodes.c index 52008776..7dc8af1c 100644 --- a/compiler/src/astnodes.c +++ b/compiler/src/astnodes.c @@ -1952,6 +1952,62 @@ void insert_auto_dispose_call(Context *context, AstLocal *local) { } +AstCall * create_implicit_for_expansion_call(Context *context, AstFor *fornode) { + AstCall *call = onyx_ast_node_new(context->ast_alloc, sizeof(AstCall), Ast_Kind_Call); + call->token = fornode->token; + call->callee = (AstTyped *) context->builtins.for_expansion; + + arguments_initialize(context, &call->args); + + // + // Create the code block that will represent the body of the for-loop. + // This code block is given up to 2 inputs, depending on if the index variable + // was set in the for-loop. The implementer of a __for_expansion overload must + // always provide 2 values when `#unquote`-ing, as they cannot currently know if + // the index variable was asked for. + // + // Maybe we could pass `void` as the `index_type` if the index variable is not necessary? + // I would like to keep the implementations of __for_expansion simple and easy + // to read, but sometimes there are extra complications you cannot avoid... + // + AstCodeBlock *body_code_block = onyx_ast_node_new(context->ast_alloc, sizeof(AstCodeBlock), Ast_Kind_Code_Block); + body_code_block->token = fornode->token; + body_code_block->type_node = context->builtins.code_type; + body_code_block->code = fornode->stmt; + + bh_arr_new(context->ast_alloc, body_code_block->binding_symbols, 2); + bh_arr_push(body_code_block->binding_symbols, fornode->var->token); + if (fornode->index_var) bh_arr_push(body_code_block->binding_symbols, fornode->index_var->token); + + i32 flags = 0; + if (fornode->by_pointer) flags |= 1; // BY_POINTER + if (fornode->no_close) flags |= 2; // NO_CLOSE + + AstNumLit *flag_node = make_int_literal(context, flags); + + AstTypeRawAlias *index_type = onyx_ast_node_new(context->ast_alloc, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias); + index_type->type = context->types.basic[Basic_Kind_Type_Index]; + index_type->to = context->types.basic[Basic_Kind_I32]; + if (fornode->index_var) { + assert(fornode->index_var->type); + index_type->to = fornode->index_var->type; + } + + // Arguments are: + // Iterator + // Code block with 2 inputs (value, index) + // Flags + // Index variable type + bh_arr_push(call->args.values, (AstTyped *) make_argument(context, (AstTyped *) fornode->iter)); + bh_arr_push(call->args.values, (AstTyped *) make_argument(context, (AstTyped *) body_code_block)); + bh_arr_push(call->args.values, (AstTyped *) make_argument(context, (AstTyped *) flag_node)); + bh_arr_push(call->args.values, (AstTyped *) make_argument(context, (AstTyped *) index_type)); + + return call; +} + + + b32 resolve_intrinsic_interface_constraint(Context *context, AstConstraint *constraint) { AstInterface *interface = constraint->interface; Type* type = type_build_from_ast(context, (AstType *) constraint->args[0]); diff --git a/compiler/src/builtins.c b/compiler/src/builtins.c index 9b09684a..203d4095 100644 --- a/compiler/src/builtins.c +++ b/compiler/src/builtins.c @@ -511,6 +511,12 @@ void initialize_builtins(Context *context) { return; } + context->builtins.for_expansion = (AstOverloadedFunction *) symbol_raw_resolve(context, p->scope, "__for_expansion"); + if (context->builtins.for_expansion == NULL || context->builtins.for_expansion->kind != Ast_Kind_Overloaded_Function) { + ONYX_ERROR((OnyxFilePos) { 0 }, Error_Critical, "'__for_expansion' #match procedure not found."); + return; + } + context->builtins.closure_block_allocate = (AstFunction *) symbol_raw_resolve(context, p->scope, "__closure_block_allocate"); if (context->builtins.closure_block_allocate == NULL || context->builtins.closure_block_allocate->kind != Ast_Kind_Function) { ONYX_ERROR((OnyxFilePos) { 0 }, Error_Critical, "'__closure_block_allocate' procedure not found."); diff --git a/compiler/src/checker.c b/compiler/src/checker.c index 1a4668eb..f168f83f 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -91,7 +91,7 @@ CHECK_FUNC(statement, AstNode** pstmt); CHECK_FUNC(return, AstReturn* retnode); CHECK_FUNC(if, AstIfWhile* ifnode); CHECK_FUNC(while, AstIfWhile* whilenode); -CHECK_FUNC(for, AstFor* fornode); +CHECK_FUNC(for, AstFor** pfornode); CHECK_FUNC(switch, AstSwitch* switchnode); CHECK_FUNC(call, AstCall** pcall); CHECK_FUNC(binaryop, AstBinaryOp** pbinop); @@ -296,10 +296,21 @@ CHECK_FUNC(while, AstIfWhile* whilenode) { return Check_Success; } -CHECK_FUNC(for, AstFor* fornode) { +CHECK_FUNC(for, AstFor** pfornode) { + AstFor *fornode = *pfornode; + b32 old_inside_for_iterator; if (fornode->flags & Ast_Flag_Has_Been_Checked) goto fornode_expr_checked; + if (fornode->index_var) { + fornode->index_var->flags |= Ast_Flag_Cannot_Take_Addr; + CHECK(expression, (AstTyped **) &fornode->index_var); + + if (!type_is_integer(fornode->index_var->type)) { + ERROR_(fornode->index_var->token->pos, "Index for a for loop must be an integer type, but it is a '%s'.", type_get_name(context, fornode->index_var->type)); + } + } + CHECK(expression, &fornode->iter); resolve_expression_type(context, fornode->iter); @@ -399,15 +410,27 @@ CHECK_FUNC(for, AstFor* fornode) { fornode->loop_type = For_Loop_DynArr; } - else if (type_constructed_from_poly(iter_type, context->builtins.iterator_type)) { - if (fornode->by_pointer) { - ERROR(error_loc, "Cannot iterate by pointer over an iterator."); + //else if (type_constructed_from_poly(iter_type, context->builtins.iterator_type)) { + // if (fornode->by_pointer) { + // ERROR(error_loc, "Cannot iterate by pointer over an iterator."); + // } + + // // HACK: This assumes the Iterator type only has a single type argument. + // given_type = iter_type->Struct.poly_sln[0].type; + // fornode->loop_type = For_Loop_Iterator; + // fornode->var->flags |= Ast_Flag_Address_Taken; + //} + else { + if (!fornode->intermediate_macro_expansion) { + fornode->intermediate_macro_expansion = create_implicit_for_expansion_call(context, fornode); + assert(fornode->intermediate_macro_expansion); } - // HACK: This assumes the Iterator type only has a single type argument. - given_type = iter_type->Struct.poly_sln[0].type; - fornode->loop_type = For_Loop_Iterator; - fornode->var->flags |= Ast_Flag_Address_Taken; + *pfornode = (AstFor *) fornode->intermediate_macro_expansion; + CHECK(call, (AstCall **) pfornode); + + // This will likely never happen, because __for_expansion should be a macro which should cause a return to symres. + return Check_Return_To_Symres; } if (given_type == NULL) @@ -423,15 +446,6 @@ CHECK_FUNC(for, AstFor* fornode) { fornode->var->type = given_type; } - if (fornode->index_var) { - fornode->index_var->flags |= Ast_Flag_Cannot_Take_Addr; - CHECK(expression, (AstTyped **) &fornode->index_var); - - if (!type_is_integer(fornode->index_var->type)) { - ERROR_(fornode->index_var->token->pos, "Index for a for loop must be an integer type, but it is a '%s'.", type_get_name(context, fornode->index_var->type)); - } - } - if (fornode->by_pointer) fornode->var->flags |= Ast_Flag_Cannot_Take_Addr; @@ -1041,7 +1055,7 @@ CHECK_FUNC(call, AstCall** pcall) { if (tm == TYPE_MATCH_YIELD) YIELD(call->token->pos, "Waiting on argument type checking."); - call->flags |= Ast_Flag_Has_Been_Checked; + call->flags |= Ast_Flag_Has_Been_Checked; if (call->kind == Ast_Kind_Call && call->callee->kind == Ast_Kind_Macro) { expand_macro(context, pcall, callee); @@ -3029,7 +3043,7 @@ CHECK_FUNC(statement, AstNode** pstmt) { case Ast_Kind_If: return check_if(context, (AstIfWhile *) stmt); case Ast_Kind_Static_If: return check_if(context, (AstIfWhile *) stmt); case Ast_Kind_While: return check_while(context, (AstIfWhile *) stmt); - case Ast_Kind_For: return check_for(context, (AstFor *) stmt); + case Ast_Kind_For: return check_for(context, (AstFor **) pstmt); case Ast_Kind_Switch: return check_switch(context, (AstSwitch *) stmt); case Ast_Kind_Block: return check_block(context, (AstBlock *) stmt); case Ast_Kind_Defer: return check_statement(context, &((AstDefer *) stmt)->stmt); @@ -3067,7 +3081,7 @@ CHECK_FUNC(statement, AstNode** pstmt) { return Check_Error; } else { - ERROR(stmt->token->pos, "The type of this local is not a type."); + ERROR_(stmt->token->pos, "The type of this local is not a type. It is a %s.", onyx_ast_node_kind_string(typed_stmt->type_node->kind)); } } diff --git a/core/builtin.onyx b/core/builtin.onyx index a69b0a99..fdb29657 100644 --- a/core/builtin.onyx +++ b/core/builtin.onyx @@ -527,6 +527,15 @@ __dispose_used_local :: #match -> void { } +__For_Expansion_Flags :: enum #flags { + BY_POINTER + NO_CLOSE +} + +/// TODO: comment this +__for_expansion :: #match -> void {} + + /// Defines all options for changing the memory layout, imports and exports, /// and more of an Onyx binary. Link_Options :: struct { From 47d3561dcbb2fa1bd84b674118e929f305d0cbd0 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Sun, 5 Jan 2025 17:50:21 -0600 Subject: [PATCH 02/17] fixed: working for-loop macro expansion concept --- compiler/src/astnodes.c | 2 ++ compiler/src/checker.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/astnodes.c b/compiler/src/astnodes.c index e69652d4..9fb7249e 100644 --- a/compiler/src/astnodes.c +++ b/compiler/src/astnodes.c @@ -1952,6 +1952,7 @@ AstCall * create_implicit_for_expansion_call(Context *context, AstFor *fornode) AstCall *call = onyx_ast_node_new(context->ast_alloc, sizeof(AstCall), Ast_Kind_Call); call->token = fornode->token; call->callee = (AstTyped *) context->builtins.for_expansion; + call->next = fornode->next; arguments_initialize(context, &call->args); @@ -1970,6 +1971,7 @@ AstCall * create_implicit_for_expansion_call(Context *context, AstFor *fornode) body_code_block->token = fornode->token; body_code_block->type_node = context->builtins.code_type; body_code_block->code = fornode->stmt; + ((AstBlock *) body_code_block->code)->rules = Block_Rule_Code_Block; bh_arr_new(context->ast_alloc, body_code_block->binding_symbols, 2); bh_arr_push(body_code_block->binding_symbols, fornode->var->token); diff --git a/compiler/src/checker.c b/compiler/src/checker.c index 53a05d2e..468592ac 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -581,7 +581,7 @@ CHECK_FUNC(for, AstFor** pfornode) { CHECK(call, (AstCall **) pfornode); // This will likely never happen, because __for_expansion should be a macro which should cause a return to symres. - return Check_Return_To_Symres; + return Check_Yield; } if (given_type == NULL) From 169ca0f11855be1a374daac6e71d9548bd9a4a47 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Sun, 5 Jan 2025 19:50:31 -0600 Subject: [PATCH 03/17] added: typed code block captures and `#skip_scope` --- compiler/include/astnodes.h | 10 +++++++++- compiler/src/astnodes.c | 6 +++--- compiler/src/checker.c | 37 +++++++++++++++++++++++++++++++++++-- compiler/src/parser.c | 15 +++++++++++++-- 4 files changed, 60 insertions(+), 8 deletions(-) diff --git a/compiler/include/astnodes.h b/compiler/include/astnodes.h index bf6284bb..2d5a0f39 100644 --- a/compiler/include/astnodes.h +++ b/compiler/include/astnodes.h @@ -1613,12 +1613,17 @@ struct AstCallSite { b32 collapsed : 1; }; +typedef struct CodeBlockBindingSymbol { + OnyxToken *symbol; + AstType *type_node; // This can be NULL if no type was given. +} CodeBlockBindingSymbol; + // Represents a "pastable" block of code. struct AstCodeBlock { AstTyped_base; AstNode *code; - bh_arr(OnyxToken *) binding_symbols; + bh_arr(CodeBlockBindingSymbol) binding_symbols; b32 is_expression: 1; }; @@ -1628,6 +1633,9 @@ struct AstDirectiveInsert { AstTyped *code_expr; bh_arr(AstTyped *) binding_exprs; + + // Set when using #skip_scope + AstTyped *skip_scope_index; }; struct AstDirectiveInit { diff --git a/compiler/src/astnodes.c b/compiler/src/astnodes.c index 9fb7249e..a3c9f7ca 100644 --- a/compiler/src/astnodes.c +++ b/compiler/src/astnodes.c @@ -1970,12 +1970,12 @@ AstCall * create_implicit_for_expansion_call(Context *context, AstFor *fornode) AstCodeBlock *body_code_block = onyx_ast_node_new(context->ast_alloc, sizeof(AstCodeBlock), Ast_Kind_Code_Block); body_code_block->token = fornode->token; body_code_block->type_node = context->builtins.code_type; - body_code_block->code = fornode->stmt; + body_code_block->code = (AstNode *) fornode->stmt; ((AstBlock *) body_code_block->code)->rules = Block_Rule_Code_Block; bh_arr_new(context->ast_alloc, body_code_block->binding_symbols, 2); - bh_arr_push(body_code_block->binding_symbols, fornode->var->token); - if (fornode->index_var) bh_arr_push(body_code_block->binding_symbols, fornode->index_var->token); + bh_arr_push(body_code_block->binding_symbols, ((CodeBlockBindingSymbol) { .symbol = fornode->var->token, .type_node = NULL })); + if (fornode->index_var) bh_arr_push(body_code_block->binding_symbols, ((CodeBlockBindingSymbol) { .symbol = fornode->index_var->token, .type_node = NULL })); i32 flags = 0; if (fornode->by_pointer) flags |= 1; // BY_POINTER diff --git a/compiler/src/checker.c b/compiler/src/checker.c index 468592ac..c8413c2a 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -3256,6 +3256,11 @@ CHECK_FUNC(expression, AstTyped** pexpr) { case Ast_Kind_Code_Block: expr->flags |= Ast_Flag_Comptime; fill_in_type(context, expr); + bh_arr_each(CodeBlockBindingSymbol, sym, ((AstCodeBlock *) expr)->binding_symbols) { + if (sym->type_node) { + CHECK(type, &sym->type_node); + } + } break; case Ast_Kind_Do_Block: { @@ -3381,8 +3386,11 @@ CHECK_FUNC(insert_directive, AstDirectiveInsert** pinsert, b32 expected_expressi CHECK(expression, pexpr); } - Type* code_type = type_build_from_ast(context, context->builtins.code_type); + if (insert->skip_scope_index) { + CHECK(expression, &insert->skip_scope_index); + } + Type* code_type = type_build_from_ast(context, context->builtins.code_type); TYPE_CHECK(&insert->code_expr, code_type) { ERROR_(insert->token->pos, "#unquote expected a value of type 'Code', got '%s'.", type_get_name(context, insert->code_expr->type)); @@ -3413,10 +3421,23 @@ CHECK_FUNC(insert_directive, AstDirectiveInsert** pinsert, b32 expected_expressi AstNode* cloned_block = ast_clone(context, code_block->code); cloned_block->next = insert->next; + i32 skip_scope_index = get_expression_integer_value(context, insert->skip_scope_index, NULL); + Scope *scope_for_cloned_block = NULL; + if (skip_scope_index > 0) { + Scope *skip_scope = context->checker.current_scope; + fori (i, 0, skip_scope_index) { + if (!skip_scope->parent) break; + skip_scope = skip_scope->parent; + } + + scope_for_cloned_block = scope_create(context, skip_scope, cloned_block->token->pos); + } + if (bound_expr_count > 0) { Scope **scope = NULL; if (cloned_block->kind == Ast_Kind_Block) { + ((AstBlock *) cloned_block)->scope = scope_for_cloned_block; scope = &((AstBlock *) cloned_block)->quoted_block_capture_scope; } else if (bound_symbol_count > 0) { @@ -3428,6 +3449,7 @@ CHECK_FUNC(insert_directive, AstDirectiveInsert** pinsert, b32 expected_expressi body_block->token = cloned_block->token; body_block->body = (AstNode *) return_node; body_block->rules = Block_Rule_Code_Block; + ((AstBlock *) body_block)->scope = scope_for_cloned_block; scope = &((AstBlock *) body_block)->quoted_block_capture_scope; AstDoBlock* doblock = (AstDoBlock *) onyx_ast_node_new(context->ast_alloc, sizeof(AstDoBlock), Ast_Kind_Do_Block); @@ -3444,7 +3466,18 @@ CHECK_FUNC(insert_directive, AstDirectiveInsert** pinsert, b32 expected_expressi *scope = scope_create(context, NULL, code_block->token->pos); fori (i, 0, bound_symbol_count) { - symbol_introduce(context, *scope, code_block->binding_symbols[i], (AstNode *) insert->binding_exprs[i]); + CodeBlockBindingSymbol sym = code_block->binding_symbols[i]; + if (sym.type_node) { + Type *type = type_build_from_ast(context, sym.type_node); + + TYPE_CHECK(&insert->binding_exprs[i], type) { + ERROR_(insert->token->pos, "FIX ME!!! Expected type '%s' but got type '%s'.", + type_get_name(context, type), type_get_name(context, insert->binding_exprs[i]->type)); + } + } + + AstNode *value = (void *) insert->binding_exprs[i]; + symbol_introduce(context, *scope, sym.symbol, value); } } } diff --git a/compiler/src/parser.c b/compiler/src/parser.c index eca4c6f1..440e4dab 100644 --- a/compiler/src/parser.c +++ b/compiler/src/parser.c @@ -862,8 +862,15 @@ static AstTyped* parse_factor(OnyxParser* parser) { while (!consume_token_if_next(parser, ']')) { if (parser->hit_unexpected_token) return (AstTyped *) code_block; - OnyxToken *symbol = expect_token(parser, Token_Type_Symbol); - bh_arr_push(code_block->binding_symbols, symbol); + CodeBlockBindingSymbol sym; + sym.symbol = expect_token(parser, Token_Type_Symbol); + sym.type_node = NULL; + + if (consume_token_if_next(parser, ':')) { + sym.type_node = parse_type(parser); + } + + bh_arr_push(code_block->binding_symbols, sym); if (parser->curr->type != ']') expect_token(parser, ','); @@ -1066,6 +1073,10 @@ static AstTyped* parse_factor(OnyxParser* parser) { } } + if (parse_possible_directive(parser, "skip_scope")) { + insert->skip_scope_index = parse_factor(parser); + } + retval = (AstTyped *) insert; break; } From 7e1cbe96e4fffc5b378c720ea54006ab181877de Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Sun, 5 Jan 2025 22:14:00 -0600 Subject: [PATCH 04/17] added: more powerful code block features and for-macros --- compiler/include/astnodes.h | 6 ++--- compiler/src/astnodes.c | 20 ++++++---------- compiler/src/checker.c | 46 ++++++++++++++++++------------------- compiler/src/clone.c | 15 ++++++++++-- compiler/src/parser.c | 35 ++++++++++++++-------------- compiler/src/utils.c | 18 +++++++++++++++ 6 files changed, 82 insertions(+), 58 deletions(-) diff --git a/compiler/include/astnodes.h b/compiler/include/astnodes.h index 2d5a0f39..8cbc1a88 100644 --- a/compiler/include/astnodes.h +++ b/compiler/include/astnodes.h @@ -866,9 +866,9 @@ struct AstFor { // NOTE: Stores the iteration variable Scope *scope; - // NOTE: Local defining the iteration variable - AstLocal* var; - AstLocal* index_var; + AstLocal *var; + AstLocal *index_var; + bh_arr(AstLocal *) indexing_variables; // NOTE: This can be any expression, but it is checked that // it is of a type that we know how to iterate over. diff --git a/compiler/src/astnodes.c b/compiler/src/astnodes.c index a3c9f7ca..b9172770 100644 --- a/compiler/src/astnodes.c +++ b/compiler/src/astnodes.c @@ -1973,9 +1973,13 @@ AstCall * create_implicit_for_expansion_call(Context *context, AstFor *fornode) body_code_block->code = (AstNode *) fornode->stmt; ((AstBlock *) body_code_block->code)->rules = Block_Rule_Code_Block; - bh_arr_new(context->ast_alloc, body_code_block->binding_symbols, 2); - bh_arr_push(body_code_block->binding_symbols, ((CodeBlockBindingSymbol) { .symbol = fornode->var->token, .type_node = NULL })); - if (fornode->index_var) bh_arr_push(body_code_block->binding_symbols, ((CodeBlockBindingSymbol) { .symbol = fornode->index_var->token, .type_node = NULL })); + bh_arr_new(context->ast_alloc, body_code_block->binding_symbols, bh_arr_length(fornode->indexing_variables)); + bh_arr_each(AstLocal *, indexing_variable, fornode->indexing_variables) { + CodeBlockBindingSymbol sym; + sym.symbol = (*indexing_variable)->token; + sym.type_node = (*indexing_variable)->type_node; + bh_arr_push(body_code_block->binding_symbols, sym); + } i32 flags = 0; if (fornode->by_pointer) flags |= 1; // BY_POINTER @@ -1983,23 +1987,13 @@ AstCall * create_implicit_for_expansion_call(Context *context, AstFor *fornode) AstNumLit *flag_node = make_int_literal(context, flags); - AstTypeRawAlias *index_type = onyx_ast_node_new(context->ast_alloc, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias); - index_type->type = context->types.basic[Basic_Kind_Type_Index]; - index_type->to = context->types.basic[Basic_Kind_I32]; - if (fornode->index_var) { - assert(fornode->index_var->type); - index_type->to = fornode->index_var->type; - } - // Arguments are: // Iterator // Code block with 2 inputs (value, index) // Flags - // Index variable type bh_arr_push(call->args.values, (AstTyped *) make_argument(context, (AstTyped *) fornode->iter)); bh_arr_push(call->args.values, (AstTyped *) make_argument(context, (AstTyped *) body_code_block)); bh_arr_push(call->args.values, (AstTyped *) make_argument(context, (AstTyped *) flag_node)); - bh_arr_push(call->args.values, (AstTyped *) make_argument(context, (AstTyped *) index_type)); return call; } diff --git a/compiler/src/checker.c b/compiler/src/checker.c index c8413c2a..38877fa9 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -336,15 +336,15 @@ CHECK_FUNC(if, AstIfWhile* ifnode) { if (static_if_resolution(context, ifnode)) { if (ifnode->true_stmt != NULL) { + ifnode->true_stmt->rules = Block_Rule_Macro & ~Block_Rule_New_Scope; CHECK(statement, (AstNode **) &ifnode->true_stmt); - ifnode->true_stmt->rules = Block_Rule_Macro; ifnode->flags |= ifnode->true_stmt->flags & Ast_Flag_Block_Returns; } } else { if (ifnode->false_stmt != NULL) { + ifnode->false_stmt->rules = Block_Rule_Macro & ~Block_Rule_New_Scope; CHECK(statement, (AstNode **) &ifnode->false_stmt); - ifnode->false_stmt->rules = Block_Rule_Macro; ifnode->flags |= ifnode->false_stmt->flags & Ast_Flag_Block_Returns; } } @@ -433,15 +433,6 @@ CHECK_FUNC(for, AstFor** pfornode) { b32 old_inside_for_iterator; if (fornode->flags & Ast_Flag_Has_Been_Checked) goto fornode_expr_checked; - if (fornode->index_var) { - fornode->index_var->flags |= Ast_Flag_Cannot_Take_Addr; - CHECK(expression, (AstTyped **) &fornode->index_var); - - if (!type_is_integer(fornode->index_var->type)) { - ERROR_(fornode->index_var->token->pos, "Index for a for loop must be an integer type, but it is a '%s'.", type_get_name(context, fornode->index_var->type)); - } - } - CHECK(expression, &fornode->iter); resolve_expression_type(context, fornode->iter); @@ -454,17 +445,12 @@ CHECK_FUNC(for, AstFor** pfornode) { // } // } // - CHECK(local, &fornode->var); - if (fornode->index_var) { - fornode->index_var->flags |= Ast_Flag_Cannot_Take_Addr; - CHECK(local, &fornode->index_var); - fill_in_type(context, (AstTyped *) fornode->index_var); - - if (!type_is_integer(fornode->index_var->type)) { - ERROR_(fornode->index_var->token->pos, "Index for a for loop must be an integer type, but it is a '%s'.", type_get_name(context, fornode->index_var->type)); - } + bh_arr_each(AstLocal *, index_variable, fornode->indexing_variables) { + CHECK(local, index_variable); } + assert(fornode->var == fornode->indexing_variables[0]); + Type* iter_type = fornode->iter->type; if (iter_type == NULL) YIELD(fornode->token->pos, "Waiting for iteration expression type to be known."); @@ -607,6 +593,19 @@ CHECK_FUNC(for, AstFor** pfornode) { ONYX_WARNING(error_loc, "Warning: #no_close here is meaningless as the iterable is not an iterator."); } + if (fornode->index_var) { + fornode->index_var->flags |= Ast_Flag_Cannot_Take_Addr; + if (fornode->index_var->type_node == NULL) { + fornode->index_var->type_node = (AstType *) &context->basic_types.type_u32; + } + fill_in_type(context, (AstTyped *) fornode->index_var); + + // if (!type_is_integer(fornode->index_var->type)) { + // ERROR_(fornode->index_var->token->pos, "Index for a for loop must be an integer type, but it is a '%s'.", type_get_name(context, fornode->index_var->type)); + // } + } + + fornode->flags |= Ast_Flag_Has_Been_Checked; @@ -2572,7 +2571,7 @@ CHECK_FUNC(dereference, AstDereference* deref) { CHECK(expression, &deref->expr); if (!type_is_pointer(deref->expr->type)) - ERROR(deref->token->pos, "Cannot dereference non-pointer value."); + ERROR_(deref->token->pos, "Cannot dereference non-pointer value, '%s'.", type_get_name(context, deref->expr->type)); if (deref->expr->type == context->types.basic[Basic_Kind_Rawptr]) ERROR(deref->token->pos, "Cannot dereference 'rawptr'. Cast to another pointer type first."); @@ -2699,7 +2698,8 @@ CHECK_FUNC(field_access, AstFieldAccess** pfield) { expr->kind == Ast_Kind_Distinct_Type || expr->kind == Ast_Kind_Interface || expr->kind == Ast_Kind_Compiler_Extension || - expr->kind == Ast_Kind_Package) { + expr->kind == Ast_Kind_Package || + expr->kind == Ast_Kind_Code_Block) { goto try_resolve_from_node; } } @@ -3258,7 +3258,7 @@ CHECK_FUNC(expression, AstTyped** pexpr) { fill_in_type(context, expr); bh_arr_each(CodeBlockBindingSymbol, sym, ((AstCodeBlock *) expr)->binding_symbols) { if (sym->type_node) { - CHECK(type, &sym->type_node); + CHECK(expression, (AstTyped **) &sym->type_node); } } break; diff --git a/compiler/src/clone.c b/compiler/src/clone.c index 17b29552..1ad2ff28 100644 --- a/compiler/src/clone.c +++ b/compiler/src/clone.c @@ -277,11 +277,22 @@ AstNode* ast_clone(Context *context, void* n) { C(AstDefer, stmt); break; - case Ast_Kind_For: - C(AstFor, var); + case Ast_Kind_For: { + AstFor* sf = (AstFor *) node; + AstFor* df = (AstFor *) nn; + + df->indexing_variables = NULL; + bh_arr_new(context->gp_alloc, df->indexing_variables, bh_arr_length(sf->indexing_variables)); + bh_arr_each(AstLocal *, iv, sf->indexing_variables) + bh_arr_push(df->indexing_variables, (AstLocal *) ast_clone(context, (AstTyped *) *iv)); + + df->var = df->indexing_variables[0]; + if (bh_arr_length(df->indexing_variables) > 1) df->index_var = df->indexing_variables[1]; + C(AstFor, iter); C(AstFor, stmt); break; + } case Ast_Kind_If: case Ast_Kind_While: diff --git a/compiler/src/parser.c b/compiler/src/parser.c index 440e4dab..bf1065bc 100644 --- a/compiler/src/parser.c +++ b/compiler/src/parser.c @@ -1630,49 +1630,50 @@ static AstFor* parse_for_stmt(OnyxParser* parser) { } // - // For loops can take on a lot of shapes. + // For loops can take on a lot of shapes. Look for "sym (:|,|in)". // for value in iter // for value: i64 in iter // for value, index in iter // for value: i64, index in iter // for value: i64, index: i32 in iter // + bh_arr_new(parser->context->gp_alloc, for_node->indexing_variables, 1); + if (next_tokens_are(parser, 2, Token_Type_Symbol, Token_Type_Keyword_In) || next_tokens_are(parser, 2, Token_Type_Symbol, ':') || next_tokens_are(parser, 2, Token_Type_Symbol, ',') ) { - for_node->var = make_local( - parser->context, - expect_token(parser, Token_Type_Symbol), - NULL - ); + while (!consume_token_if_next(parser, Token_Type_Keyword_In)) { + if (parser->hit_unexpected_token) return for_node; - if (consume_token_if_next(parser, ':')) { - for_node->var->type_node = parse_type(parser); - } - - if (consume_token_if_next(parser, ',')) { - for_node->index_var = make_local( + AstLocal *var = make_local( parser->context, expect_token(parser, Token_Type_Symbol), - (AstType *) &parser->context->basic_types.type_u32 + NULL ); if (consume_token_if_next(parser, ':')) { - for_node->index_var->type_node = parse_type(parser); + var->type_node = parse_type(parser); } - } - expect_token(parser, Token_Type_Keyword_In); + bh_arr_push(for_node->indexing_variables, var); + + if (peek_token(0)->type != Token_Type_Keyword_In) { + expect_token(parser, ','); + } + } } else { // HACK static char it_name[] = "it "; static OnyxToken it_token = { Token_Type_Symbol, 2, it_name, { 0 } }; AstLocal* var_node = make_local(parser->context, &it_token, NULL); - for_node->var = var_node; + bh_arr_push(for_node->indexing_variables, var_node); } + for_node->var = for_node->indexing_variables[0]; + if (bh_arr_length(for_node->indexing_variables) > 1) for_node->index_var = for_node->indexing_variables[1]; + for_node->iter = parse_expression(parser, 1); for_node->stmt = parse_block(parser, 1, NULL); diff --git a/compiler/src/utils.c b/compiler/src/utils.c index 7f04ee80..aa1cea47 100644 --- a/compiler/src/utils.c +++ b/compiler/src/utils.c @@ -379,6 +379,24 @@ AstNode* try_symbol_raw_resolve_from_node(Context *context, AstNode* node, char* return NULL; } + case Ast_Kind_Code_Block: { + AstCodeBlock *block = (AstCodeBlock *) node; + + if (!strcmp(symbol, "capture_count")) { + return (AstNode *) make_int_literal(context, bh_arr_length(block->binding_symbols)); + } + + if (bh_str_starts_with(symbol, "capture_type_")) { + char *num = symbol + 13; // go past the "capture_type_" + int idx = atoi(num); // TODO: Remove use of atoi here... + if (idx != 0 && idx <= bh_arr_length(block->binding_symbols)) { + return (AstNode *) block->binding_symbols[idx - 1].type_node; + } + } + + return NULL; + } + default: break; } From 65e22a89346d659e4ec2a83be3a1db2a50fb3ea2 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Mon, 6 Jan 2025 07:34:41 -0600 Subject: [PATCH 05/17] fixed: error message on unmatched types in `#unquote` --- compiler/src/checker.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/src/checker.c b/compiler/src/checker.c index 38877fa9..8509f1b5 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -3471,8 +3471,9 @@ CHECK_FUNC(insert_directive, AstDirectiveInsert** pinsert, b32 expected_expressi Type *type = type_build_from_ast(context, sym.type_node); TYPE_CHECK(&insert->binding_exprs[i], type) { - ERROR_(insert->token->pos, "FIX ME!!! Expected type '%s' but got type '%s'.", - type_get_name(context, type), type_get_name(context, insert->binding_exprs[i]->type)); + ERROR_(insert->token->pos, "Expected type '%s' but got type '%s' for the '%d%s' argument to the code block.", + type_get_name(context, type), type_get_name(context, insert->binding_exprs[i]->type), + i + 1, bh_num_suffix(i + 1)); } } From 44823488510d30e9e094e34236a18a56a0496e06 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Mon, 6 Jan 2025 21:14:16 -0600 Subject: [PATCH 06/17] WIP: trying to remove most of the `for` logic --- compiler/include/astnodes.h | 4 +- compiler/src/astnodes.c | 5 +- compiler/src/builtins.c | 6 + compiler/src/checker.c | 411 ++++++++++++++++++----------------- compiler/src/clone.c | 4 - compiler/src/parser.c | 3 - compiler/src/wasm_emit.c | 423 ------------------------------------ core/operations.onyx | 90 ++++++++ 8 files changed, 319 insertions(+), 627 deletions(-) diff --git a/compiler/include/astnodes.h b/compiler/include/astnodes.h index 8cbc1a88..594c70ff 100644 --- a/compiler/include/astnodes.h +++ b/compiler/include/astnodes.h @@ -866,8 +866,6 @@ struct AstFor { // NOTE: Stores the iteration variable Scope *scope; - AstLocal *var; - AstLocal *index_var; bh_arr(AstLocal *) indexing_variables; // NOTE: This can be any expression, but it is checked that @@ -2123,6 +2121,8 @@ struct CompilerBuiltins { bh_arr(AstFunction *) init_procedures; AstOverloadedFunction *implicit_bool_cast; AstOverloadedFunction *dispose_used_local; + + AstType *for_expansion_flag_type; AstOverloadedFunction *for_expansion; }; diff --git a/compiler/src/astnodes.c b/compiler/src/astnodes.c index b9172770..0827b575 100644 --- a/compiler/src/astnodes.c +++ b/compiler/src/astnodes.c @@ -1986,14 +1986,17 @@ AstCall * create_implicit_for_expansion_call(Context *context, AstFor *fornode) if (fornode->no_close) flags |= 2; // NO_CLOSE AstNumLit *flag_node = make_int_literal(context, flags); + // flag_node->type_node = context->builtins.for_expansion_flag_type; + // flag_node->type = type_build_from_ast(context, context->builtins.for_expansion_flag_type); + // assert(flag_node->type); // Arguments are: // Iterator // Code block with 2 inputs (value, index) // Flags bh_arr_push(call->args.values, (AstTyped *) make_argument(context, (AstTyped *) fornode->iter)); - bh_arr_push(call->args.values, (AstTyped *) make_argument(context, (AstTyped *) body_code_block)); bh_arr_push(call->args.values, (AstTyped *) make_argument(context, (AstTyped *) flag_node)); + bh_arr_push(call->args.values, (AstTyped *) make_argument(context, (AstTyped *) body_code_block)); return call; } diff --git a/compiler/src/builtins.c b/compiler/src/builtins.c index 203d4095..4f8736ae 100644 --- a/compiler/src/builtins.c +++ b/compiler/src/builtins.c @@ -517,6 +517,12 @@ void initialize_builtins(Context *context) { return; } + context->builtins.for_expansion_flag_type = (AstType *) symbol_raw_resolve(context, p->scope, "__For_Expansion_Flags"); + if (context->builtins.for_expansion_flag_type == NULL || context->builtins.for_expansion_flag_type->kind != Ast_Kind_Enum_Type) { + ONYX_ERROR((OnyxFilePos) { 0 }, Error_Critical, "'__For_Expansion_Flags' enum procedure not found."); + return; + } + context->builtins.closure_block_allocate = (AstFunction *) symbol_raw_resolve(context, p->scope, "__closure_block_allocate"); if (context->builtins.closure_block_allocate == NULL || context->builtins.closure_block_allocate->kind != Ast_Kind_Function) { ONYX_ERROR((OnyxFilePos) { 0 }, Error_Critical, "'__closure_block_allocate' procedure not found."); diff --git a/compiler/src/checker.c b/compiler/src/checker.c index 8509f1b5..29571694 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -424,210 +424,233 @@ CHECK_FUNC(while, AstIfWhile* whilenode) { CHECK_FUNC(for, AstFor** pfornode) { AstFor *fornode = *pfornode; - if (!fornode->scope) { - fornode->scope = scope_create(context, context->checker.current_scope, fornode->token->pos); - } - - scope_enter(context, fornode->scope); - - b32 old_inside_for_iterator; - if (fornode->flags & Ast_Flag_Has_Been_Checked) goto fornode_expr_checked; + // HACK + CHECK(expression, (AstTyped **) &context->builtins.for_expansion_flag_type); CHECK(expression, &fornode->iter); resolve_expression_type(context, fornode->iter); - // - // These locals have to be checked after the iterator value to avoid incorrect - // symbol resolutions. - // - // for a in x { - // for a in a { // <- - // } - // } - // bh_arr_each(AstLocal *, index_variable, fornode->indexing_variables) { - CHECK(local, index_variable); - } - - assert(fornode->var == fornode->indexing_variables[0]); - - Type* iter_type = fornode->iter->type; - if (iter_type == NULL) YIELD(fornode->token->pos, "Waiting for iteration expression type to be known."); - - OnyxFilePos error_loc = fornode->var->token->pos; - if (error_loc.filename == NULL) { - error_loc = fornode->token->pos; - } - - // @HACK This should be built elsewhere... - context->builtins.range_type_type = type_build_from_ast(context, context->builtins.range_type); - if (context->builtins.range_type_type == NULL) YIELD(fornode->token->pos, "Waiting for 'range' structure to be built."); - - Type* given_type = NULL; - - fornode->loop_type = For_Loop_Invalid; - if (types_are_compatible(context, iter_type, context->types.basic[Basic_Kind_I32])) { - if (fornode->by_pointer) { - ERROR(error_loc, "Cannot iterate by pointer over a range."); - } - - AstNumLit* low_0 = make_int_literal(context, 0); - AstRangeLiteral* rl = make_range_literal(context, (AstTyped *) low_0, fornode->iter); - CHECK(range_literal, &rl); - fornode->iter = (AstTyped *) rl; - - given_type = context->builtins.range_type_type->Struct.memarr[0]->type; - fornode->var->flags |= Ast_Flag_Cannot_Take_Addr; - fornode->loop_type = For_Loop_Range; - } - else if (types_are_compatible(context, iter_type, context->types.basic[Basic_Kind_I64])) { - if (fornode->by_pointer) { - ERROR(error_loc, "Cannot iterate by pointer over a range."); - } - - AstNumLit* low_0 = make_int_literal(context, 0); - low_0->type = context->types.basic[Basic_Kind_I64]; - - AstRangeLiteral* rl = make_range_literal(context, (AstTyped *) low_0, fornode->iter); - CHECK(range_literal, &rl); - fornode->iter = (AstTyped *) rl; - - given_type = context->builtins.range64_type_type->Struct.memarr[0]->type; - fornode->var->flags |= Ast_Flag_Cannot_Take_Addr; - fornode->loop_type = For_Loop_Range; - } - else if (types_are_compatible(context, iter_type, context->builtins.range_type_type)) { - if (fornode->by_pointer) { - ERROR(error_loc, "Cannot iterate by pointer over a range."); - } - - // NOTE: Blindly copy the first range member's type which will - // be the low value. - brendanfh 2020/09/04 - given_type = iter_type->Struct.memarr[0]->type; - fornode->var->flags |= Ast_Flag_Cannot_Take_Addr; - fornode->loop_type = For_Loop_Range; - } - else if (types_are_compatible(context, iter_type, context->builtins.range64_type_type)) { - if (fornode->by_pointer) { - ERROR(error_loc, "Cannot iterate by pointer over a range."); - } - - // NOTE: Blindly copy the first range member's type which will - // be the low value. - brendanfh 2020/09/04 - given_type = iter_type->Struct.memarr[0]->type; - fornode->var->flags |= Ast_Flag_Cannot_Take_Addr; - fornode->loop_type = For_Loop_Range; - } - else if (iter_type->kind == Type_Kind_Array) { - if (fornode->by_pointer) given_type = type_make_pointer(context, iter_type->Array.elem); - else given_type = iter_type->Array.elem; - - fornode->loop_type = For_Loop_Array; - } - else if (iter_type->kind == Type_Kind_Slice) { - if (fornode->by_pointer) given_type = type_make_pointer(context, iter_type->Slice.elem); - else given_type = iter_type->Slice.elem; - - fornode->loop_type = For_Loop_Slice; - - } - else if (iter_type->kind == Type_Kind_VarArgs) { - if (fornode->by_pointer) { - ERROR_(error_loc, "Cannot iterate by pointer over '%s'.", type_get_name(context, iter_type)); + if ((*index_variable)->type_node) { + CHECK(type, &(*index_variable)->type_node); } - - given_type = iter_type->VarArgs.elem; - - // NOTE: Slices are VarArgs are being treated the same here. - fornode->loop_type = For_Loop_Slice; - } - else if (iter_type->kind == Type_Kind_DynArray) { - if (fornode->by_pointer) given_type = type_make_pointer(context, iter_type->DynArray.elem); - else given_type = iter_type->DynArray.elem; - - fornode->loop_type = For_Loop_DynArr; - } - //else if (type_constructed_from_poly(iter_type, context->builtins.iterator_type)) { - // if (fornode->by_pointer) { - // ERROR(error_loc, "Cannot iterate by pointer over an iterator."); - // } - - // // HACK: This assumes the Iterator type only has a single type argument. - // given_type = iter_type->Struct.poly_sln[0].type; - // fornode->loop_type = For_Loop_Iterator; - // fornode->var->flags |= Ast_Flag_Address_Taken; - //} - else { - if (!fornode->intermediate_macro_expansion) { - fornode->intermediate_macro_expansion = create_implicit_for_expansion_call(context, fornode); - assert(fornode->intermediate_macro_expansion); - } - - *pfornode = (AstFor *) fornode->intermediate_macro_expansion; - CHECK(call, (AstCall **) pfornode); - - // This will likely never happen, because __for_expansion should be a macro which should cause a return to symres. - return Check_Yield; } - if (given_type == NULL) - ERROR_(error_loc, "Cannot iterate over a '%s'.", type_get_name(context, iter_type)); - - if (fornode->var->type_node) { - fill_in_type(context, (AstTyped *) fornode->var); - TYPE_CHECK((AstTyped **) &fornode->var, given_type) { - ERROR_(error_loc, "Mismatched type for loop variable. You specified '%s', but it should be '%s'.", type_get_name(context, fornode->var->type), type_get_name(context, given_type)); - } - - } else { - fornode->var->type = given_type; + if (!fornode->intermediate_macro_expansion) { + fornode->intermediate_macro_expansion = create_implicit_for_expansion_call(context, fornode); + assert(fornode->intermediate_macro_expansion); } + + *pfornode = (AstFor *) fornode->intermediate_macro_expansion; + CHECK(call, (AstCall **) pfornode); + + // This will likely never happen, because __for_expansion should be a macro which should cause a return to symres. + return Check_Yield; - if (fornode->by_pointer) - fornode->var->flags |= Ast_Flag_Cannot_Take_Addr; - - if (fornode->loop_type == For_Loop_Invalid) - ERROR_(error_loc, "Cannot iterate over a '%s'.", type_get_name(context, iter_type)); - - if (fornode->no_close && fornode->loop_type != For_Loop_Iterator) { - ONYX_WARNING(error_loc, "Warning: #no_close here is meaningless as the iterable is not an iterator."); - } - - if (fornode->index_var) { - fornode->index_var->flags |= Ast_Flag_Cannot_Take_Addr; - if (fornode->index_var->type_node == NULL) { - fornode->index_var->type_node = (AstType *) &context->basic_types.type_u32; - } - fill_in_type(context, (AstTyped *) fornode->index_var); - - // if (!type_is_integer(fornode->index_var->type)) { - // ERROR_(fornode->index_var->token->pos, "Index for a for loop must be an integer type, but it is a '%s'.", type_get_name(context, fornode->index_var->type)); - // } - } - - - fornode->flags |= Ast_Flag_Has_Been_Checked; - - -fornode_expr_checked: - bh_arr_push(context->checker.for_node_stack, fornode); - - old_inside_for_iterator = context->checker.inside_for_iterator; - context->checker.inside_for_iterator = 0; - iter_type = fornode->iter->type; - if (type_constructed_from_poly(iter_type, context->builtins.iterator_type)) { - context->checker.inside_for_iterator = 1; - } - - do { - CheckStatus cs = check_block(context, fornode->stmt); - context->checker.inside_for_iterator = old_inside_for_iterator; - if (cs > Check_Errors_Start) return cs; - } while(0); - - bh_arr_pop(context->checker.for_node_stack); - scope_leave(context); - return Check_Success; +// if (!fornode->scope) { +// fornode->scope = scope_create(context, context->checker.current_scope, fornode->token->pos); +// } +// +// scope_enter(context, fornode->scope); +// +// b32 old_inside_for_iterator; +// if (fornode->flags & Ast_Flag_Has_Been_Checked) goto fornode_expr_checked; +// +// CHECK(expression, &fornode->iter); +// resolve_expression_type(context, fornode->iter); +// +// // +// // These locals have to be checked after the iterator value to avoid incorrect +// // symbol resolutions. +// // +// // for a in x { +// // for a in a { // <- +// // } +// // } +// // +// bh_arr_each(AstLocal *, index_variable, fornode->indexing_variables) { +// CHECK(local, index_variable); +// } +// +// assert(fornode->var == fornode->indexing_variables[0]); +// +// Type* iter_type = fornode->iter->type; +// if (iter_type == NULL) YIELD(fornode->token->pos, "Waiting for iteration expression type to be known."); +// +// OnyxFilePos error_loc = fornode->var->token->pos; +// if (error_loc.filename == NULL) { +// error_loc = fornode->token->pos; +// } +// +// // @HACK This should be built elsewhere... +// context->builtins.range_type_type = type_build_from_ast(context, context->builtins.range_type); +// if (context->builtins.range_type_type == NULL) YIELD(fornode->token->pos, "Waiting for 'range' structure to be built."); +// +// Type* given_type = NULL; +// +// fornode->loop_type = For_Loop_Invalid; +// if (types_are_compatible(context, iter_type, context->types.basic[Basic_Kind_I32])) { +// if (fornode->by_pointer) { +// ERROR(error_loc, "Cannot iterate by pointer over a range."); +// } +// +// AstNumLit* low_0 = make_int_literal(context, 0); +// AstRangeLiteral* rl = make_range_literal(context, (AstTyped *) low_0, fornode->iter); +// CHECK(range_literal, &rl); +// fornode->iter = (AstTyped *) rl; +// +// given_type = context->builtins.range_type_type->Struct.memarr[0]->type; +// fornode->var->flags |= Ast_Flag_Cannot_Take_Addr; +// fornode->loop_type = For_Loop_Range; +// } +// else if (types_are_compatible(context, iter_type, context->types.basic[Basic_Kind_I64])) { +// if (fornode->by_pointer) { +// ERROR(error_loc, "Cannot iterate by pointer over a range."); +// } +// +// AstNumLit* low_0 = make_int_literal(context, 0); +// low_0->type = context->types.basic[Basic_Kind_I64]; +// +// AstRangeLiteral* rl = make_range_literal(context, (AstTyped *) low_0, fornode->iter); +// CHECK(range_literal, &rl); +// fornode->iter = (AstTyped *) rl; +// +// given_type = context->builtins.range64_type_type->Struct.memarr[0]->type; +// fornode->var->flags |= Ast_Flag_Cannot_Take_Addr; +// fornode->loop_type = For_Loop_Range; +// } +// else if (types_are_compatible(context, iter_type, context->builtins.range_type_type)) { +// if (fornode->by_pointer) { +// ERROR(error_loc, "Cannot iterate by pointer over a range."); +// } +// +// // NOTE: Blindly copy the first range member's type which will +// // be the low value. - brendanfh 2020/09/04 +// given_type = iter_type->Struct.memarr[0]->type; +// fornode->var->flags |= Ast_Flag_Cannot_Take_Addr; +// fornode->loop_type = For_Loop_Range; +// } +// else if (types_are_compatible(context, iter_type, context->builtins.range64_type_type)) { +// if (fornode->by_pointer) { +// ERROR(error_loc, "Cannot iterate by pointer over a range."); +// } +// +// // NOTE: Blindly copy the first range member's type which will +// // be the low value. - brendanfh 2020/09/04 +// given_type = iter_type->Struct.memarr[0]->type; +// fornode->var->flags |= Ast_Flag_Cannot_Take_Addr; +// fornode->loop_type = For_Loop_Range; +// } +// else if (iter_type->kind == Type_Kind_Array) { +// if (fornode->by_pointer) given_type = type_make_pointer(context, iter_type->Array.elem); +// else given_type = iter_type->Array.elem; +// +// fornode->loop_type = For_Loop_Array; +// } +// else if (iter_type->kind == Type_Kind_Slice) { +// if (fornode->by_pointer) given_type = type_make_pointer(context, iter_type->Slice.elem); +// else given_type = iter_type->Slice.elem; +// +// fornode->loop_type = For_Loop_Slice; +// +// } +// else if (iter_type->kind == Type_Kind_VarArgs) { +// if (fornode->by_pointer) { +// ERROR_(error_loc, "Cannot iterate by pointer over '%s'.", type_get_name(context, iter_type)); +// } +// +// given_type = iter_type->VarArgs.elem; +// +// // NOTE: Slices are VarArgs are being treated the same here. +// fornode->loop_type = For_Loop_Slice; +// } +// else if (iter_type->kind == Type_Kind_DynArray) { +// if (fornode->by_pointer) given_type = type_make_pointer(context, iter_type->DynArray.elem); +// else given_type = iter_type->DynArray.elem; +// +// fornode->loop_type = For_Loop_DynArr; +// } +// //else if (type_constructed_from_poly(iter_type, context->builtins.iterator_type)) { +// // if (fornode->by_pointer) { +// // ERROR(error_loc, "Cannot iterate by pointer over an iterator."); +// // } +// +// // // HACK: This assumes the Iterator type only has a single type argument. +// // given_type = iter_type->Struct.poly_sln[0].type; +// // fornode->loop_type = For_Loop_Iterator; +// // fornode->var->flags |= Ast_Flag_Address_Taken; +// //} +// else { +// if (!fornode->intermediate_macro_expansion) { +// fornode->intermediate_macro_expansion = create_implicit_for_expansion_call(context, fornode); +// assert(fornode->intermediate_macro_expansion); +// } +// +// *pfornode = (AstFor *) fornode->intermediate_macro_expansion; +// CHECK(call, (AstCall **) pfornode); +// +// // This will likely never happen, because __for_expansion should be a macro which should cause a return to symres. +// return Check_Yield; +// } +// +// if (given_type == NULL) +// ERROR_(error_loc, "Cannot iterate over a '%s'.", type_get_name(context, iter_type)); +// +// if (fornode->var->type_node) { +// fill_in_type(context, (AstTyped *) fornode->var); +// TYPE_CHECK((AstTyped **) &fornode->var, given_type) { +// ERROR_(error_loc, "Mismatched type for loop variable. You specified '%s', but it should be '%s'.", type_get_name(context, fornode->var->type), type_get_name(context, given_type)); +// } +// +// } else { +// fornode->var->type = given_type; +// } +// +// if (fornode->by_pointer) +// fornode->var->flags |= Ast_Flag_Cannot_Take_Addr; +// +// if (fornode->loop_type == For_Loop_Invalid) +// ERROR_(error_loc, "Cannot iterate over a '%s'.", type_get_name(context, iter_type)); +// +// if (fornode->no_close && fornode->loop_type != For_Loop_Iterator) { +// ONYX_WARNING(error_loc, "Warning: #no_close here is meaningless as the iterable is not an iterator."); +// } +// +// if (fornode->index_var) { +// fornode->index_var->flags |= Ast_Flag_Cannot_Take_Addr; +// if (fornode->index_var->type_node == NULL) { +// fornode->index_var->type_node = (AstType *) &context->basic_types.type_u32; +// } +// fill_in_type(context, (AstTyped *) fornode->index_var); +// +// // if (!type_is_integer(fornode->index_var->type)) { +// // ERROR_(fornode->index_var->token->pos, "Index for a for loop must be an integer type, but it is a '%s'.", type_get_name(context, fornode->index_var->type)); +// // } +// } +// +// +// fornode->flags |= Ast_Flag_Has_Been_Checked; +// +// +// fornode_expr_checked: +// bh_arr_push(context->checker.for_node_stack, fornode); +// +// old_inside_for_iterator = context->checker.inside_for_iterator; +// context->checker.inside_for_iterator = 0; +// iter_type = fornode->iter->type; +// if (type_constructed_from_poly(iter_type, context->builtins.iterator_type)) { +// context->checker.inside_for_iterator = 1; +// } +// +// do { +// CheckStatus cs = check_block(context, fornode->stmt); +// context->checker.inside_for_iterator = old_inside_for_iterator; +// if (cs > Check_Errors_Start) return cs; +// } while(0); +// +// bh_arr_pop(context->checker.for_node_stack); +// scope_leave(context); +// return Check_Success; } static b32 add_case_to_switch_statement(Context *context, AstSwitch* switchnode, u64 case_value, AstSwitchCase* casestmt, OnyxFilePos pos) { diff --git a/compiler/src/clone.c b/compiler/src/clone.c index 1ad2ff28..4d829b78 100644 --- a/compiler/src/clone.c +++ b/compiler/src/clone.c @@ -286,11 +286,7 @@ AstNode* ast_clone(Context *context, void* n) { bh_arr_each(AstLocal *, iv, sf->indexing_variables) bh_arr_push(df->indexing_variables, (AstLocal *) ast_clone(context, (AstTyped *) *iv)); - df->var = df->indexing_variables[0]; - if (bh_arr_length(df->indexing_variables) > 1) df->index_var = df->indexing_variables[1]; - C(AstFor, iter); - C(AstFor, stmt); break; } diff --git a/compiler/src/parser.c b/compiler/src/parser.c index bf1065bc..0cdd89a3 100644 --- a/compiler/src/parser.c +++ b/compiler/src/parser.c @@ -1671,9 +1671,6 @@ static AstFor* parse_for_stmt(OnyxParser* parser) { bh_arr_push(for_node->indexing_variables, var_node); } - for_node->var = for_node->indexing_variables[0]; - if (bh_arr_length(for_node->indexing_variables) > 1) for_node->index_var = for_node->indexing_variables[1]; - for_node->iter = parse_expression(parser, 1); for_node->stmt = parse_block(parser, 1, NULL); diff --git a/compiler/src/wasm_emit.c b/compiler/src/wasm_emit.c index 87f917ce..cfd88309 100644 --- a/compiler/src/wasm_emit.c +++ b/compiler/src/wasm_emit.c @@ -505,7 +505,6 @@ EMIT_FUNC(load_instruction, Type* type, u32 offset); EMIT_FUNC(load_with_ignored_instruction, Type* type, u32 offset, i32 ignored_value_count); EMIT_FUNC(if, AstIfWhile* if_node); EMIT_FUNC(while, AstIfWhile* while_node); -EMIT_FUNC(for, AstFor* for_node); EMIT_FUNC(switch, AstSwitch* switch_node); EMIT_FUNC(defer, AstDefer* defer); EMIT_FUNC(defer_code, WasmInstruction* deferred_code, u32 code_count); @@ -754,7 +753,6 @@ EMIT_FUNC(statement, AstNode* stmt) { case Ast_Kind_If: emit_if(mod, &code, (AstIfWhile *) stmt); break; case Ast_Kind_Static_If: emit_if(mod, &code, (AstIfWhile *) stmt); break; case Ast_Kind_While: emit_while(mod, &code, (AstIfWhile *) stmt); break; - case Ast_Kind_For: emit_for(mod, &code, (AstFor *) stmt); break; case Ast_Kind_Switch: emit_switch(mod, &code, (AstSwitch *) stmt); break; case Ast_Kind_Jump: emit_structured_jump(mod, &code, (AstJump *) stmt); break; case Ast_Kind_Block: emit_block(mod, &code, (AstBlock *) stmt, 1); break; @@ -1330,427 +1328,6 @@ EMIT_FUNC(while, AstIfWhile* while_node) { *pcode = code; } -EMIT_FUNC(for__prologue, AstFor* for_node, u64 iter_local, i64 index_local) { - bh_arr(WasmInstruction) code = *pcode; - - if (for_node->has_first) { - for_node->first_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32); - WIL(for_node->token, WI_I32_CONST, 1); - WIL(for_node->token, WI_LOCAL_SET, for_node->first_local); - } - - if (index_local != -1) { - if (type_is_small_integer(for_node->index_var->type)) { - WIL(for_node->token, WI_I32_CONST, 0); - } else { - WIL(for_node->token, WI_I64_CONST, 0); - } - - WIL(for_node->token, WI_LOCAL_SET, index_local); - - WasmInstruction* increment_instructions = bh_alloc_array(mod->allocator, WasmInstruction, 4); - increment_instructions[0] = (WasmInstruction) { WI_LOCAL_GET, { .l = index_local } }; - if (type_is_small_integer(for_node->index_var->type)) { - increment_instructions[1] = (WasmInstruction) { WI_I32_CONST, { .l = 1 } }; - increment_instructions[2] = (WasmInstruction) { WI_I32_ADD, { .l = 0x00 } }; - } else { - increment_instructions[1] = (WasmInstruction) { WI_I64_CONST, { .l = 1 } }; - increment_instructions[2] = (WasmInstruction) { WI_I64_ADD, { .l = 0x00 } }; - } - increment_instructions[3] = (WasmInstruction) { WI_LOCAL_SET, { .l = index_local } }; - - emit_defer_code(mod, &code, increment_instructions, 4); - } - - *pcode = code; -} - -EMIT_FUNC(for__epilogue, AstFor* for_node, u64 iter_local, i64 index_local) { - bh_arr(WasmInstruction) code = *pcode; - - if (for_node->has_first) { - WIL(NULL, WI_I32_CONST, 0); - WIL(NULL, WI_LOCAL_SET, for_node->first_local); - } - - if (index_local != -1) { - WIL(for_node->token, WI_LOCAL_GET, index_local); - - if (type_is_small_integer(for_node->index_var->type)) { - WIL(for_node->token, WI_I32_CONST, 1); - WI(for_node->token, WI_I32_ADD); - } else { - WIL(for_node->token, WI_I64_CONST, 1); - WI(for_node->token, WI_I64_ADD); - } - - WIL(for_node->token, WI_LOCAL_SET, index_local); - } - - *pcode = code; -} - -EMIT_FUNC(for_range, AstFor* for_node, u64 iter_local, i64 index_local) { - bh_arr(WasmInstruction) code = *pcode; - - // NOTE: There are some aspects of the code below that rely on the - // low, high, and step members to be i32's. This restriction can be lifted, - // but it is important to change the code here. - // -brendanfh 2020/09/04 - - // NOTE: This might not be a range literal - AstStructLiteral *range = (AstStructLiteral *) for_node->iter; - u64 offset = 0; - - assert(for_node->iter->type); - - StructMember high_mem, step_mem; - type_lookup_member(mod->context, for_node->iter->type, "high", &high_mem); - type_lookup_member(mod->context, for_node->iter->type, "step", &step_mem); - u64 high_local = local_raw_allocate(mod->local_alloc, onyx_type_to_wasm_type(high_mem.type)); - u64 step_local = local_raw_allocate(mod->local_alloc, onyx_type_to_wasm_type(step_mem.type)); - - emit_struct_as_separate_values(mod, &code, for_node->iter->type, 0); - - WIL(for_node->token, WI_LOCAL_SET, step_local); - WIL(for_node->token, WI_LOCAL_SET, high_local); - WIL(for_node->token, WI_LOCAL_SET, iter_local); - - u64 INT_GE = WI_I32_GE_S; - u64 INT_LT = WI_I32_LT_S; - u64 INT_CONST = WI_I32_CONST; - u64 INT_ADD = WI_I32_ADD; - - if (high_mem.type == mod->context->types.basic[Basic_Kind_I64]) { - INT_GE = WI_I64_GE_S; - INT_LT = WI_I64_LT_S; - INT_CONST = WI_I64_CONST; - INT_ADD = WI_I64_ADD; - } - - emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token); - emit_for__prologue(mod, &code, for_node, iter_local, index_local); - emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token); - emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token); - - if (range->kind == Ast_Kind_Struct_Literal && (range->args.values[2]->flags & Ast_Flag_Comptime) != 0) { - AstNumLit *step_value = (AstNumLit *) range->args.values[2]; - assert(step_value->kind == Ast_Kind_NumLit); - - if (step_value->value.l >= 0) { - WIL(for_node->token, WI_LOCAL_GET, iter_local); - WIL(for_node->token, WI_LOCAL_GET, high_local); - WI(for_node->token, INT_GE); - WID(for_node->token, WI_COND_JUMP, 0x02); - } else { - WIL(for_node->token, WI_LOCAL_GET, iter_local); - WIL(for_node->token, WI_LOCAL_GET, high_local); - WI(for_node->token, INT_LT); - WID(for_node->token, WI_COND_JUMP, 0x02); - } - - } else { - WIL(for_node->token, WI_LOCAL_GET, step_local); - WID(for_node->token, INT_CONST, 0); - WI(for_node->token, INT_GE); - WID(for_node->token, WI_IF_START, 0x40); - WIL(for_node->token, WI_LOCAL_GET, iter_local); - WIL(for_node->token, WI_LOCAL_GET, high_local); - WI(for_node->token, INT_GE); - WID(for_node->token, WI_COND_JUMP, 0x03); - WI(for_node->token, WI_ELSE); - WIL(for_node->token, WI_LOCAL_GET, iter_local); - WIL(for_node->token, WI_LOCAL_GET, high_local); - WI(for_node->token, INT_LT); - WID(for_node->token, WI_COND_JUMP, 0x03); - WI(for_node->token, WI_IF_END); - } - - - emit_block(mod, &code, for_node->stmt, 0); - - emit_leave_structured_block(mod, &code); - - WIL(for_node->token, WI_LOCAL_GET, iter_local); - WIL(for_node->token, WI_LOCAL_GET, step_local); - WI(for_node->token, INT_ADD); - WIL(for_node->token, WI_LOCAL_SET, iter_local); - - emit_for__epilogue(mod, &code, for_node, iter_local, index_local); - - if (bh_arr_last(code).type != WI_JUMP) - WID(for_node->token, WI_JUMP, 0x00); - - emit_leave_structured_block(mod, &code); - emit_leave_structured_block(mod, &code); - - if (for_node->has_first) local_raw_free(mod->local_alloc, WASM_TYPE_INT32); - local_raw_free(mod->local_alloc, onyx_type_to_wasm_type(high_mem.type)); - local_raw_free(mod->local_alloc, onyx_type_to_wasm_type(step_mem.type)); - - *pcode = code; -} - -EMIT_FUNC(for_slice, AstFor* for_node, u64 iter_local, i64 index_local) { - bh_arr(WasmInstruction) code = *pcode; - - u64 end_ptr_local, ptr_local; - end_ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR); - - if (for_node->by_pointer) { - ptr_local = iter_local; - } else { - ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR); - } - - AstLocal* var = for_node->var; - b32 it_is_local = (b32) ((iter_local & LOCAL_IS_WASM) != 0); - u64 offset = 0; - - u64 elem_size; - if (for_node->by_pointer) elem_size = type_size_of(var->type->Pointer.elem); - else elem_size = type_size_of(var->type); - - WIL(for_node->token, WI_LOCAL_SET, end_ptr_local); - WIL(for_node->token, WI_LOCAL_TEE, ptr_local); - WIL(for_node->token, WI_LOCAL_GET, end_ptr_local); - if (elem_size != 1) { - WID(for_node->token, WI_PTR_CONST, elem_size); - WI(for_node->token, WI_PTR_MUL); - } - WI(for_node->token, WI_PTR_ADD); - WIL(for_node->token, WI_LOCAL_SET, end_ptr_local); - - emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token); - emit_for__prologue(mod, &code, for_node, iter_local, index_local); - emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token); - emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token); - - WIL(for_node->token, WI_LOCAL_GET, ptr_local); - WIL(for_node->token, WI_LOCAL_GET, end_ptr_local); - WI(for_node->token, WI_PTR_GE); - WID(for_node->token, WI_COND_JUMP, 0x02); - - if (!for_node->by_pointer) { - if (!it_is_local) emit_local_location(mod, &code, var, &offset); - - WIL(for_node->token, WI_LOCAL_GET, ptr_local); - emit_load_instruction(mod, &code, var->type, 0); - - if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset); - else WIL(for_node->token, WI_LOCAL_SET, iter_local); - } - - emit_block(mod, &code, for_node->stmt, 0); - - emit_leave_structured_block(mod, &code); - - WIL(for_node->token, WI_LOCAL_GET, ptr_local); - WIL(for_node->token, WI_PTR_CONST, elem_size); - WI(for_node->token, WI_PTR_ADD); - WIL(for_node->token, WI_LOCAL_SET, ptr_local); - - emit_for__epilogue(mod, &code, for_node, iter_local, index_local); - - if (bh_arr_last(code).type != WI_JUMP) - WID(for_node->token, WI_JUMP, 0x00); - - emit_leave_structured_block(mod, &code); - emit_leave_structured_block(mod, &code); - - if (for_node->has_first) local_raw_free(mod->local_alloc, WASM_TYPE_INT32); - local_raw_free(mod->local_alloc, WASM_TYPE_PTR); - if (!for_node->by_pointer) local_raw_free(mod->local_alloc, WASM_TYPE_PTR); - - *pcode = code; -} - -EMIT_FUNC(for_iterator, AstFor* for_node, u64 iter_local, i64 index_local) { - bh_arr(WasmInstruction) code = *pcode; - - // Allocate temporaries for iterator contents - emit_struct_as_separate_values(mod, &code, for_node->iter->type, 0); - - u64 iterator_data_ptr = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR); - u64 iterator_next_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC); - u64 iterator_close_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC); - u64 iterator_remove_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC); - u64 iterator_done_res = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR); - WI(for_node->token, WI_DROP); // TODO: These need to not be dropped but actually used because they are the closure pointers! - WIL(for_node->token, WI_LOCAL_SET, iterator_remove_func); - WI(for_node->token, WI_DROP); - WIL(for_node->token, WI_LOCAL_SET, iterator_close_func); - WI(for_node->token, WI_DROP); - WIL(for_node->token, WI_LOCAL_SET, iterator_next_func); - WIL(for_node->token, WI_LOCAL_SET, iterator_data_ptr); - - - { - // - // This pushes an entry onto the stack of for loops that have - // are iterator that can have a '#remove' directive in them. - ForRemoveInfo remove_info; - remove_info.iterator_data_ptr = iterator_data_ptr; - remove_info.iterator_remove_func = iterator_remove_func; - - StructMember remove_func_type; - type_lookup_member_by_idx(mod->context, for_node->iter->type, 3, &remove_func_type); - remove_info.remove_func_type_idx = generate_type_idx(mod, remove_func_type.type); - - bh_arr_push(mod->for_remove_info, remove_info); - } - - AstLocal* var = for_node->var; - assert((iter_local & LOCAL_IS_WASM) == 0); - - // Enter a deferred statement for the auto-close - emit_enter_structured_block(mod, &code, SBT_Basic_Block, for_node->token); - - emit_for__prologue(mod, &code, for_node, iter_local, index_local); - - if (!for_node->no_close) { - StructMember close_func_type; - type_lookup_member_by_idx(mod->context, for_node->iter->type, 2, &close_func_type); - i32 close_type_idx = generate_type_idx(mod, close_func_type.type); - - WasmInstruction* close_instructions = bh_alloc_array(mod->context->gp_alloc, WasmInstruction, 8); - close_instructions[0] = (WasmInstruction) { WI_LOCAL_GET, { .l = iterator_close_func } }; - close_instructions[1] = (WasmInstruction) { WI_I32_CONST, { .l = mod->null_proc_func_idx } }; - close_instructions[2] = (WasmInstruction) { WI_I32_NE, { .l = 0x00 } }; - close_instructions[3] = (WasmInstruction) { WI_IF_START, { .l = 0x40 } }; - close_instructions[4] = (WasmInstruction) { WI_LOCAL_GET, { .l = iterator_data_ptr } }; - close_instructions[5] = (WasmInstruction) { WI_LOCAL_GET, { .l = iterator_close_func } }; - close_instructions[6] = (WasmInstruction) { WI_CALL_INDIRECT, { .l = close_type_idx } }; - close_instructions[7] = (WasmInstruction) { WI_IF_END, { .l = 0x00 } }; - - emit_defer_code(mod, &code, close_instructions, 8); - } - - emit_enter_structured_block(mod, &code, SBT_Breakable_Block, for_node->token); - emit_enter_structured_block(mod, &code, SBT_Basic_Loop, for_node->token); - emit_enter_structured_block(mod, &code, SBT_Continue_Block, for_node->token); - - // CLEANUP: Calling a function is way too f-ing complicated. FACTOR IT!! - u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &mod->context->builtins.stack_top); - - StructMember next_func_type; - type_lookup_member_by_idx(mod->context, for_node->iter->type, 1, &next_func_type); - Type* return_type = next_func_type.type->Function.return_type; - - u32 return_size = type_size_of(return_type); - u32 return_align = type_alignment_of(return_type); - bh_align(return_size, return_align); - - u64 reserve_size = return_size; - bh_align(reserve_size, 16); - - WID(for_node->token, WI_GLOBAL_GET, stack_top_idx); - WID(for_node->token, WI_PTR_CONST, reserve_size); - WI(for_node->token, WI_PTR_SUB); - WID(for_node->token, WI_GLOBAL_SET, stack_top_idx); - - WIL(for_node->token, WI_LOCAL_GET, iterator_data_ptr); - WIL(for_node->token, WI_LOCAL_GET, iterator_next_func); - i32 type_idx = generate_type_idx(mod, next_func_type.type); - WID(for_node->token, WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 })); - - WID(for_node->token, WI_GLOBAL_GET, stack_top_idx); - WID(for_node->token, WI_PTR_CONST, reserve_size); - WI(for_node->token, WI_PTR_ADD); - WID(for_node->token, WI_GLOBAL_SET, stack_top_idx); - - WIL(for_node->token, WI_LOCAL_TEE, iterator_done_res); - - emit_load_instruction(mod, &code, mod->context->types.basic[Basic_Kind_U8], 0); - WI(for_node->token, WI_I32_EQZ); - WID(for_node->token, WI_COND_JUMP, 0x02); - - u64 offset = 0; - emit_local_location(mod, &code, var, &offset); - WIL(for_node->token, WI_LOCAL_GET, iterator_done_res); - emit_load_instruction(mod, &code, var->type, type_alignment_of(return_type)); - emit_store_instruction(mod, &code, var->type, offset); - - emit_block(mod, &code, for_node->stmt, 0); - - emit_leave_structured_block(mod, &code); // CONTINUE_BLOCK - - emit_for__epilogue(mod, &code, for_node, iter_local, index_local); - - WID(for_node->token, WI_JUMP, 0x00); - - emit_leave_structured_block(mod, &code); // BASIC_LOOP - emit_leave_structured_block(mod, &code); // BREAKABLE_BLOCK - - emit_deferred_stmts(mod, &code); - emit_leave_structured_block(mod, &code); // BASIC_BLOCK - - bh_arr_pop(mod->for_remove_info); - - if (for_node->has_first) local_raw_free(mod->local_alloc, WASM_TYPE_INT32); - local_raw_free(mod->local_alloc, WASM_TYPE_PTR); - local_raw_free(mod->local_alloc, WASM_TYPE_FUNC); - local_raw_free(mod->local_alloc, WASM_TYPE_FUNC); - local_raw_free(mod->local_alloc, WASM_TYPE_FUNC); - local_raw_free(mod->local_alloc, WASM_TYPE_PTR); - *pcode = code; -} - -EMIT_FUNC(for, AstFor* for_node) { - bh_arr(WasmInstruction) code = *pcode; - - AstLocal* var = for_node->var; - u64 iter_local = local_allocate(mod->local_alloc, (AstTyped *) var); - bh_imap_put(&mod->local_map, (u64) var, iter_local); - - i64 index_local = -1; - if (for_node->index_var) { - index_local = local_allocate(mod->local_alloc, (AstTyped *) for_node->index_var); - bh_imap_put(&mod->local_map, (u64) for_node->index_var, index_local); - } - - debug_enter_symbol_frame(mod); - debug_introduce_symbol(mod, var->token, - local_is_wasm_local((AstTyped *) var) ? DSL_REGISTER : DSL_STACK, - iter_local, var->type); - - if (for_node->index_var) { - // index variables must be register allocated. - debug_introduce_symbol(mod, for_node->index_var->token, DSL_REGISTER, index_local, for_node->index_var->type); - } - - emit_expression(mod, &code, for_node->iter); - - switch (for_node->loop_type) { - case For_Loop_Range: emit_for_range(mod, &code, for_node, iter_local, index_local); break; - - // NOTE: For static arrays, simply outputing the size - // of the array right after the pointer to the start - // of the array essentially makes it a slice. - case For_Loop_Array: - WIL(NULL, WI_I32_CONST, for_node->iter->type->Array.count); - emit_for_slice(mod, &code, for_node, iter_local, index_local); - break; - - // NOTE: A dynamic array is just a slice with a capacity and allocator on the end. - // Just dropping the extra fields will mean we can just use the slice implementation. - // - brendanfh 2020/09/04 - // - brendanfh 2021/04/13 - case For_Loop_DynArr: - emit_load_slice(mod, &code); - // fallthrough - - case For_Loop_Slice: emit_for_slice(mod, &code, for_node, iter_local, index_local); break; - case For_Loop_Iterator: emit_for_iterator(mod, &code, for_node, iter_local, index_local); break; - default: ONYX_ERROR(for_node->token->pos, Error_Critical, "Invalid for loop type. You should probably not be seeing this..."); - } - - local_free(mod->local_alloc, (AstTyped *) var); - debug_leave_symbol_frame(mod); - - *pcode = code; -} - EMIT_FUNC(switch, AstSwitch* switch_node) { bh_arr(WasmInstruction) code = *pcode; diff --git a/core/operations.onyx b/core/operations.onyx index 9809adfc..eaf65844 100644 --- a/core/operations.onyx +++ b/core/operations.onyx @@ -42,3 +42,93 @@ __array_op_scalar :: macro (l: [$N]$T, r: T, $body: Code) -> [N]T { for 0..N do res[it] = #unquote body(l[it], r); return res; } + + + +// +// Builtin for expansions +// + +#overload +__for_expansion :: macro (r: range, $flags: i32, body: Code) { + i := r.low + end := r.high + step := r.step + + while true { + if step < 0 && i >= end do break + if step >= 0 && i < end do break + + defer i += step + + #unquote body(i) #skip_scope(2) + } +} + +#overload +__for_expansion :: macro (r: range64, $flags: i32, body: Code) { + i := r.low + end := r.high + step := r.step + + while true { + if step < 0 && i >= end do break + if step >= 0 && i < end do break + + defer i += step + + #unquote body(i) #skip_scope(2) + } +} + +#overload +__for_expansion :: macro (n: i32, flags: i32, body: Code) { + i := 0 + end := n + + while i < end { + defer i += step + + #unquote body(i) #skip_scope(2) + } +} + +#overload +__for_expansion :: macro (n: i64, flags: i32, body: Code) { + i : i64 = 0 + end : i64 = n + + while i < end { + defer i += step + + #unquote body(i) #skip_scope(2) + } +} + +#overload +__for_expansion :: macro (s: [] $T, $flags: i32, body: Code) where (flags & 1 == 0) { + i := 0 + data := s.data + count := s.count + + while i < count { + defer i += 1 + + v := data[i] + #unquote body(v, i) #skip_scope(2) + } +} + +#overload +__for_expansion :: macro (s: [] $T, $flags: i32, body: Code) where (flags & 1 != 0) { + i := 0 + data := s.data + count := s.count + + while i < count { + defer i += 1 + + v := &data[i] + #unquote body(v, i) #skip_scope(2) + } +} From dd083ec1a7a9b3b251a1354685962e6f57bb463a Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Mon, 6 Jan 2025 22:10:13 -0600 Subject: [PATCH 07/17] WIP: trying to resolve weird overload failing issues --- compiler/src/checker.c | 210 +---------------------------------------- core/operations.onyx | 54 +++++++++++ 2 files changed, 56 insertions(+), 208 deletions(-) diff --git a/compiler/src/checker.c b/compiler/src/checker.c index 29571694..1988e580 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -444,213 +444,7 @@ CHECK_FUNC(for, AstFor** pfornode) { *pfornode = (AstFor *) fornode->intermediate_macro_expansion; CHECK(call, (AstCall **) pfornode); - // This will likely never happen, because __for_expansion should be a macro which should cause a return to symres. - return Check_Yield; - -// if (!fornode->scope) { -// fornode->scope = scope_create(context, context->checker.current_scope, fornode->token->pos); -// } -// -// scope_enter(context, fornode->scope); -// -// b32 old_inside_for_iterator; -// if (fornode->flags & Ast_Flag_Has_Been_Checked) goto fornode_expr_checked; -// -// CHECK(expression, &fornode->iter); -// resolve_expression_type(context, fornode->iter); -// -// // -// // These locals have to be checked after the iterator value to avoid incorrect -// // symbol resolutions. -// // -// // for a in x { -// // for a in a { // <- -// // } -// // } -// // -// bh_arr_each(AstLocal *, index_variable, fornode->indexing_variables) { -// CHECK(local, index_variable); -// } -// -// assert(fornode->var == fornode->indexing_variables[0]); -// -// Type* iter_type = fornode->iter->type; -// if (iter_type == NULL) YIELD(fornode->token->pos, "Waiting for iteration expression type to be known."); -// -// OnyxFilePos error_loc = fornode->var->token->pos; -// if (error_loc.filename == NULL) { -// error_loc = fornode->token->pos; -// } -// -// // @HACK This should be built elsewhere... -// context->builtins.range_type_type = type_build_from_ast(context, context->builtins.range_type); -// if (context->builtins.range_type_type == NULL) YIELD(fornode->token->pos, "Waiting for 'range' structure to be built."); -// -// Type* given_type = NULL; -// -// fornode->loop_type = For_Loop_Invalid; -// if (types_are_compatible(context, iter_type, context->types.basic[Basic_Kind_I32])) { -// if (fornode->by_pointer) { -// ERROR(error_loc, "Cannot iterate by pointer over a range."); -// } -// -// AstNumLit* low_0 = make_int_literal(context, 0); -// AstRangeLiteral* rl = make_range_literal(context, (AstTyped *) low_0, fornode->iter); -// CHECK(range_literal, &rl); -// fornode->iter = (AstTyped *) rl; -// -// given_type = context->builtins.range_type_type->Struct.memarr[0]->type; -// fornode->var->flags |= Ast_Flag_Cannot_Take_Addr; -// fornode->loop_type = For_Loop_Range; -// } -// else if (types_are_compatible(context, iter_type, context->types.basic[Basic_Kind_I64])) { -// if (fornode->by_pointer) { -// ERROR(error_loc, "Cannot iterate by pointer over a range."); -// } -// -// AstNumLit* low_0 = make_int_literal(context, 0); -// low_0->type = context->types.basic[Basic_Kind_I64]; -// -// AstRangeLiteral* rl = make_range_literal(context, (AstTyped *) low_0, fornode->iter); -// CHECK(range_literal, &rl); -// fornode->iter = (AstTyped *) rl; -// -// given_type = context->builtins.range64_type_type->Struct.memarr[0]->type; -// fornode->var->flags |= Ast_Flag_Cannot_Take_Addr; -// fornode->loop_type = For_Loop_Range; -// } -// else if (types_are_compatible(context, iter_type, context->builtins.range_type_type)) { -// if (fornode->by_pointer) { -// ERROR(error_loc, "Cannot iterate by pointer over a range."); -// } -// -// // NOTE: Blindly copy the first range member's type which will -// // be the low value. - brendanfh 2020/09/04 -// given_type = iter_type->Struct.memarr[0]->type; -// fornode->var->flags |= Ast_Flag_Cannot_Take_Addr; -// fornode->loop_type = For_Loop_Range; -// } -// else if (types_are_compatible(context, iter_type, context->builtins.range64_type_type)) { -// if (fornode->by_pointer) { -// ERROR(error_loc, "Cannot iterate by pointer over a range."); -// } -// -// // NOTE: Blindly copy the first range member's type which will -// // be the low value. - brendanfh 2020/09/04 -// given_type = iter_type->Struct.memarr[0]->type; -// fornode->var->flags |= Ast_Flag_Cannot_Take_Addr; -// fornode->loop_type = For_Loop_Range; -// } -// else if (iter_type->kind == Type_Kind_Array) { -// if (fornode->by_pointer) given_type = type_make_pointer(context, iter_type->Array.elem); -// else given_type = iter_type->Array.elem; -// -// fornode->loop_type = For_Loop_Array; -// } -// else if (iter_type->kind == Type_Kind_Slice) { -// if (fornode->by_pointer) given_type = type_make_pointer(context, iter_type->Slice.elem); -// else given_type = iter_type->Slice.elem; -// -// fornode->loop_type = For_Loop_Slice; -// -// } -// else if (iter_type->kind == Type_Kind_VarArgs) { -// if (fornode->by_pointer) { -// ERROR_(error_loc, "Cannot iterate by pointer over '%s'.", type_get_name(context, iter_type)); -// } -// -// given_type = iter_type->VarArgs.elem; -// -// // NOTE: Slices are VarArgs are being treated the same here. -// fornode->loop_type = For_Loop_Slice; -// } -// else if (iter_type->kind == Type_Kind_DynArray) { -// if (fornode->by_pointer) given_type = type_make_pointer(context, iter_type->DynArray.elem); -// else given_type = iter_type->DynArray.elem; -// -// fornode->loop_type = For_Loop_DynArr; -// } -// //else if (type_constructed_from_poly(iter_type, context->builtins.iterator_type)) { -// // if (fornode->by_pointer) { -// // ERROR(error_loc, "Cannot iterate by pointer over an iterator."); -// // } -// -// // // HACK: This assumes the Iterator type only has a single type argument. -// // given_type = iter_type->Struct.poly_sln[0].type; -// // fornode->loop_type = For_Loop_Iterator; -// // fornode->var->flags |= Ast_Flag_Address_Taken; -// //} -// else { -// if (!fornode->intermediate_macro_expansion) { -// fornode->intermediate_macro_expansion = create_implicit_for_expansion_call(context, fornode); -// assert(fornode->intermediate_macro_expansion); -// } -// -// *pfornode = (AstFor *) fornode->intermediate_macro_expansion; -// CHECK(call, (AstCall **) pfornode); -// -// // This will likely never happen, because __for_expansion should be a macro which should cause a return to symres. -// return Check_Yield; -// } -// -// if (given_type == NULL) -// ERROR_(error_loc, "Cannot iterate over a '%s'.", type_get_name(context, iter_type)); -// -// if (fornode->var->type_node) { -// fill_in_type(context, (AstTyped *) fornode->var); -// TYPE_CHECK((AstTyped **) &fornode->var, given_type) { -// ERROR_(error_loc, "Mismatched type for loop variable. You specified '%s', but it should be '%s'.", type_get_name(context, fornode->var->type), type_get_name(context, given_type)); -// } -// -// } else { -// fornode->var->type = given_type; -// } -// -// if (fornode->by_pointer) -// fornode->var->flags |= Ast_Flag_Cannot_Take_Addr; -// -// if (fornode->loop_type == For_Loop_Invalid) -// ERROR_(error_loc, "Cannot iterate over a '%s'.", type_get_name(context, iter_type)); -// -// if (fornode->no_close && fornode->loop_type != For_Loop_Iterator) { -// ONYX_WARNING(error_loc, "Warning: #no_close here is meaningless as the iterable is not an iterator."); -// } -// -// if (fornode->index_var) { -// fornode->index_var->flags |= Ast_Flag_Cannot_Take_Addr; -// if (fornode->index_var->type_node == NULL) { -// fornode->index_var->type_node = (AstType *) &context->basic_types.type_u32; -// } -// fill_in_type(context, (AstTyped *) fornode->index_var); -// -// // if (!type_is_integer(fornode->index_var->type)) { -// // ERROR_(fornode->index_var->token->pos, "Index for a for loop must be an integer type, but it is a '%s'.", type_get_name(context, fornode->index_var->type)); -// // } -// } -// -// -// fornode->flags |= Ast_Flag_Has_Been_Checked; -// -// -// fornode_expr_checked: -// bh_arr_push(context->checker.for_node_stack, fornode); -// -// old_inside_for_iterator = context->checker.inside_for_iterator; -// context->checker.inside_for_iterator = 0; -// iter_type = fornode->iter->type; -// if (type_constructed_from_poly(iter_type, context->builtins.iterator_type)) { -// context->checker.inside_for_iterator = 1; -// } -// -// do { -// CheckStatus cs = check_block(context, fornode->stmt); -// context->checker.inside_for_iterator = old_inside_for_iterator; -// if (cs > Check_Errors_Start) return cs; -// } while(0); -// -// bh_arr_pop(context->checker.for_node_stack); -// scope_leave(context); -// return Check_Success; + return Check_Success; } static b32 add_case_to_switch_statement(Context *context, AstSwitch* switchnode, u64 case_value, AstSwitchCase* casestmt, OnyxFilePos pos) { @@ -1081,7 +875,7 @@ CHECK_FUNC(resolve_callee, AstCall* call, AstTyped** effective_callee) { &call->args); if (new_callee == NULL) { - if (context->cycle_almost_detected < 2) { + if (!context->cycle_detected) { YIELD(call->token->pos, "Waiting to know all options for overloaded function"); } diff --git a/core/operations.onyx b/core/operations.onyx index eaf65844..c7c9c4c3 100644 --- a/core/operations.onyx +++ b/core/operations.onyx @@ -132,3 +132,57 @@ __for_expansion :: macro (s: [] $T, $flags: i32, body: Code) where (flags & 1 != #unquote body(v, i) #skip_scope(2) } } + + +// +// Move these to iter.onyx +#overload +__for_expansion :: macro (iterator: Iterator($T), $flags: i32, $body: Code) where + (flags & 1 == 0) && (body.capture_count == 1) +{ + _iterator := iterator + + #if flags & 2 != 0 { + defer _iterator.close(_iterator.data) + } + + #if #defined(body.capture_type_2) { + _i: body.capture_type_2 + } else { + _i: i32 + } + + while true { + defer _i += 1 + + value := _iterator.next(_iterator.data) + if value.tag == .None do break + + use core.memory + _it := *cast(&T) memory.ptr_add(&value, alignof ? T) + + #unquote body(_it, _i) #skip_scope(2) + } +} + +#overload +__for_expansion :: macro (iterator: Iterator($T), $flags: i32, $body: Code) where + (flags & 1 != 0) && (body.capture_count == 1) +{ + _iterator := iterator + + #if (flags & 2 != 0) { + defer _iterator.close(_iterator.data) + } + + while true { + value := _iterator.next(_iterator.data) + if value.tag == .None do break + + use core.memory + _it := *cast(&T) memory.ptr_add(&value, alignof ? T) + + #unquote body(_it) #skip_scope(2) + } +} + From 616c193f92364e2b9b0cdb7711d5b4c4d40e7e1b Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Mon, 6 Jan 2025 22:27:52 -0600 Subject: [PATCH 08/17] fixed: successfully compiling most things again --- compiler/src/checker.c | 2 +- core/container/map.onyx | 3 +- core/operations.onyx | 70 ++++++++++++++++++++++++++++++++--------- core/string/string.onyx | 9 +++--- 4 files changed, 64 insertions(+), 20 deletions(-) diff --git a/compiler/src/checker.c b/compiler/src/checker.c index 1988e580..ff2136e6 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -875,7 +875,7 @@ CHECK_FUNC(resolve_callee, AstCall* call, AstTyped** effective_callee) { &call->args); if (new_callee == NULL) { - if (!context->cycle_detected) { + if (context->cycle_almost_detected < 2) { YIELD(call->token->pos, "Waiting to know all options for overloaded function"); } diff --git a/core/container/map.onyx b/core/container/map.onyx index d5a05d1a..f905337f 100644 --- a/core/container/map.onyx +++ b/core/container/map.onyx @@ -231,7 +231,8 @@ Map.format_map :: (output: &conv.Format_Output, format: &conv.Format, x: &Map($K } else { output->write("{ "); for& x.entries { - if !#first do output->write(", "); + // :RestoreThingsThatBrokeWithForLoopChanges + // if !#first do output->write(", "); conv.format(output, "{\"p} => {\"p}", it.key, it.value); } output->write(" }"); diff --git a/core/operations.onyx b/core/operations.onyx index c7c9c4c3..9024472f 100644 --- a/core/operations.onyx +++ b/core/operations.onyx @@ -50,7 +50,7 @@ __array_op_scalar :: macro (l: [$N]$T, r: T, $body: Code) -> [N]T { // #overload -__for_expansion :: macro (r: range, $flags: i32, body: Code) { +__for_expansion :: macro (r: range, $flags: i32, $body: Code) where (body.capture_count == 1) { i := r.low end := r.high step := r.step @@ -66,7 +66,30 @@ __for_expansion :: macro (r: range, $flags: i32, body: Code) { } #overload -__for_expansion :: macro (r: range64, $flags: i32, body: Code) { +__for_expansion :: macro (r: range, $flags: i32, $body: Code) where (body.capture_count == 2) { + i := r.low + end := r.high + step := r.step + + #if #defined(body.capture_type_2) { + j: body.capture_type_2 + } else { + j: i32 + } + + while true { + if step < 0 && i >= end do break + if step >= 0 && i < end do break + + defer i += step + defer j += 1 + + #unquote body(i, j) #skip_scope(2) + } +} + +#overload +__for_expansion :: macro (r: range64, $flags: i32, $body: Code) { i := r.low end := r.high step := r.step @@ -82,31 +105,54 @@ __for_expansion :: macro (r: range64, $flags: i32, body: Code) { } #overload -__for_expansion :: macro (n: i32, flags: i32, body: Code) { +__for_expansion :: macro (r: range64, $flags: i32, $body: Code) where (body.capture_count == 2) { + i := r.low + end := r.high + step := r.step + + #if #defined(body.capture_type_2) { + j: body.capture_type_2 + } else { + j: i32 + } + + while true { + if step < 0 && i >= end do break + if step >= 0 && i < end do break + + defer i += step + defer j += 1 + + #unquote body(i, j) #skip_scope(2) + } +} + +#overload +__for_expansion :: macro (n: i32, $flags: i32, $body: Code) { i := 0 end := n while i < end { - defer i += step + defer i += 1 #unquote body(i) #skip_scope(2) } } #overload -__for_expansion :: macro (n: i64, flags: i32, body: Code) { +__for_expansion :: macro (n: i64, $flags: i32, $body: Code) { i : i64 = 0 end : i64 = n while i < end { - defer i += step + defer i += 1 #unquote body(i) #skip_scope(2) } } #overload -__for_expansion :: macro (s: [] $T, $flags: i32, body: Code) where (flags & 1 == 0) { +__for_expansion :: macro (s: [] $T, $flags: i32, $body: Code) where (flags & 1 == 0) { i := 0 data := s.data count := s.count @@ -120,7 +166,7 @@ __for_expansion :: macro (s: [] $T, $flags: i32, body: Code) where (flags & 1 == } #overload -__for_expansion :: macro (s: [] $T, $flags: i32, body: Code) where (flags & 1 != 0) { +__for_expansion :: macro (s: [] $T, $flags: i32, $body: Code) where (flags & 1 != 0) { i := 0 data := s.data count := s.count @@ -158,9 +204,7 @@ __for_expansion :: macro (iterator: Iterator($T), $flags: i32, $body: Code) wher value := _iterator.next(_iterator.data) if value.tag == .None do break - use core.memory - _it := *cast(&T) memory.ptr_add(&value, alignof ? T) - + _it := *cast(&T) (cast([&] u8, &value) + alignof ? T) #unquote body(_it, _i) #skip_scope(2) } } @@ -179,9 +223,7 @@ __for_expansion :: macro (iterator: Iterator($T), $flags: i32, $body: Code) wher value := _iterator.next(_iterator.data) if value.tag == .None do break - use core.memory - _it := *cast(&T) memory.ptr_add(&value, alignof ? T) - + _it := *cast(&T) (cast([&] u8, &value) + alignof ? T) #unquote body(_it) #skip_scope(2) } } diff --git a/core/string/string.onyx b/core/string/string.onyx index d583f085..b213cfa4 100644 --- a/core/string/string.onyx +++ b/core/string/string.onyx @@ -197,10 +197,11 @@ str.join :: (strs: [] str, sep: str, allocator := context.allocator) -> str { i := 0; for strs { - if !#first { - core.memory.copy(&out.data[i], sep.data, sep.count); - i += sep.count; - } + // :RestoreThingsThatBrokeWithForLoopChanges + // if !#first { + // core.memory.copy(&out.data[i], sep.data, sep.count); + // i += sep.count; + // } core.memory.copy(&out.data[i], it.data, it.count); i += it.count; From cd35aaa1de9c42376374191a28a0afe72edd932b Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Tue, 7 Jan 2025 16:24:29 -0600 Subject: [PATCH 09/17] fixed: bugs in for loop implementations --- core/operations.onyx | 46 +++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/core/operations.onyx b/core/operations.onyx index 9024472f..1cf8c11d 100644 --- a/core/operations.onyx +++ b/core/operations.onyx @@ -50,14 +50,15 @@ __array_op_scalar :: macro (l: [$N]$T, r: T, $body: Code) -> [N]T { // #overload -__for_expansion :: macro (r: range, $flags: i32, $body: Code) where (body.capture_count == 1) { +__for_expansion :: macro (_r: range, $flags: i32, $body: Code) where (body.capture_count == 1) { + r := _r i := r.low end := r.high step := r.step while true { - if step < 0 && i >= end do break - if step >= 0 && i < end do break + if step >= 0 && i >= end do break + if step < 0 && i < end do break defer i += step @@ -66,7 +67,8 @@ __for_expansion :: macro (r: range, $flags: i32, $body: Code) where (body.captur } #overload -__for_expansion :: macro (r: range, $flags: i32, $body: Code) where (body.capture_count == 2) { +__for_expansion :: macro (_r: range, $flags: i32, $body: Code) where (body.capture_count == 2) { + r := _r i := r.low end := r.high step := r.step @@ -78,8 +80,8 @@ __for_expansion :: macro (r: range, $flags: i32, $body: Code) where (body.captur } while true { - if step < 0 && i >= end do break - if step >= 0 && i < end do break + if step >= 0 && i >= end do break + if step < 0 && i < end do break defer i += step defer j += 1 @@ -89,14 +91,15 @@ __for_expansion :: macro (r: range, $flags: i32, $body: Code) where (body.captur } #overload -__for_expansion :: macro (r: range64, $flags: i32, $body: Code) { +__for_expansion :: macro (_r: range64, $flags: i32, $body: Code) { + r := _r i := r.low end := r.high step := r.step while true { - if step < 0 && i >= end do break - if step >= 0 && i < end do break + if step >= 0 && i >= end do break + if step < 0 && i < end do break defer i += step @@ -105,7 +108,8 @@ __for_expansion :: macro (r: range64, $flags: i32, $body: Code) { } #overload -__for_expansion :: macro (r: range64, $flags: i32, $body: Code) where (body.capture_count == 2) { +__for_expansion :: macro (_r: range64, $flags: i32, $body: Code) where (body.capture_count == 2) { + r := _r i := r.low end := r.high step := r.step @@ -117,8 +121,8 @@ __for_expansion :: macro (r: range64, $flags: i32, $body: Code) where (body.capt } while true { - if step < 0 && i >= end do break - if step >= 0 && i < end do break + if step >= 0 && i >= end do break + if step < 0 && i < end do break defer i += step defer j += 1 @@ -152,11 +156,12 @@ __for_expansion :: macro (n: i64, $flags: i32, $body: Code) { } #overload -__for_expansion :: macro (s: [] $T, $flags: i32, $body: Code) where (flags & 1 == 0) { - i := 0 +__for_expansion :: macro (_s: [] $T, $flags: i32, $body: Code) where (flags & 1 == 0) { + s := _s data := s.data count := s.count + i := 0 while i < count { defer i += 1 @@ -166,11 +171,12 @@ __for_expansion :: macro (s: [] $T, $flags: i32, $body: Code) where (flags & 1 = } #overload -__for_expansion :: macro (s: [] $T, $flags: i32, $body: Code) where (flags & 1 != 0) { - i := 0 +__for_expansion :: macro (_s: [] $T, $flags: i32, $body: Code) where (flags & 1 != 0) { + s := _s data := s.data count := s.count + i := 0 while i < count { defer i += 1 @@ -184,11 +190,11 @@ __for_expansion :: macro (s: [] $T, $flags: i32, $body: Code) where (flags & 1 ! // Move these to iter.onyx #overload __for_expansion :: macro (iterator: Iterator($T), $flags: i32, $body: Code) where - (flags & 1 == 0) && (body.capture_count == 1) + (flags & 1 == 0) && (body.capture_count == 2) { _iterator := iterator - #if flags & 2 != 0 { + #if flags & 2 == 0 { defer _iterator.close(_iterator.data) } @@ -211,11 +217,11 @@ __for_expansion :: macro (iterator: Iterator($T), $flags: i32, $body: Code) wher #overload __for_expansion :: macro (iterator: Iterator($T), $flags: i32, $body: Code) where - (flags & 1 != 0) && (body.capture_count == 1) + (flags & 1 == 0) && (body.capture_count == 1) { _iterator := iterator - #if (flags & 2 != 0) { + #if (flags & 2 == 0) { defer _iterator.close(_iterator.data) } From 02a73f866b9a0d91013d5fcccc99b06061c1b68f Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Tue, 7 Jan 2025 18:25:39 -0600 Subject: [PATCH 10/17] fixed: re-added `#first` for while loops; removed: `#remove` directive --- compiler/include/astnodes.h | 21 ++++++++++++--------- compiler/src/checker.c | 33 ++++++++++++++++----------------- compiler/src/parser.c | 8 -------- compiler/src/wasm_emit.c | 29 +++++++++++++++++------------ core/container/map.onyx | 37 +++++++++++++++++++++++++++++++++++-- core/container/set.onyx | 33 +++++++++++++++++++++++++++++++++ core/string/string.onyx | 9 ++++----- tests/aoc-2021/day11.onyx | 8 +++++--- 8 files changed, 122 insertions(+), 56 deletions(-) diff --git a/compiler/include/astnodes.h b/compiler/include/astnodes.h index 594c70ff..5d74625e 100644 --- a/compiler/include/astnodes.h +++ b/compiler/include/astnodes.h @@ -885,12 +885,8 @@ struct AstFor { // But for now, this will do. b32 by_pointer : 1; b32 no_close : 1; - b32 has_first : 1; - - // NOTE: This is used by the AstDirectiveFirst node for this - // for node to know which local variable to use. - u64 first_local; }; + struct AstIfWhile { AstNode_base; @@ -911,7 +907,14 @@ struct AstIfWhile { }; // Used by While - b32 bottom_test; + struct { + // NOTE: This is used by the AstDirectiveFirst node for this + // for node to know which local variable to use. + u64 first_local; + b32 has_first; + + b32 bottom_test; + }; }; }; typedef struct AstIfWhile AstIf; @@ -1576,7 +1579,8 @@ struct AstDirectiveRemove { struct AstDirectiveFirst { AstTyped_base; - AstFor *for_node; + + AstIfWhile *while_node; }; struct AstDirectiveExportName { @@ -1933,8 +1937,7 @@ typedef enum CheckerMode { typedef struct CheckerData { b32 expression_types_must_be_known; b32 all_checks_are_final; - b32 inside_for_iterator; - bh_arr(AstFor *) for_node_stack; + bh_arr(AstIfWhile *) while_node_stack; bh_imap __binop_impossible_cache[Binary_Op_Count]; AstCall __op_maybe_overloaded; Entity *current_entity; diff --git a/compiler/src/checker.c b/compiler/src/checker.c index ff2136e6..a9a1d180 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -405,7 +405,12 @@ CHECK_FUNC(while, AstIfWhile* whilenode) { } } - if (whilenode->true_stmt) CHECK(statement, (AstNode **) &whilenode->true_stmt); + bh_arr_push(context->checker.while_node_stack, whilenode); + + if (whilenode->true_stmt) { + CHECK(statement, (AstNode **) &whilenode->true_stmt); + } + if (whilenode->false_stmt) { if (whilenode->bottom_test) { ERROR(whilenode->token->pos, "while-loops with an 'else' clause cannot be bottom tested."); @@ -414,6 +419,8 @@ CHECK_FUNC(while, AstIfWhile* whilenode) { CHECK(statement, (AstNode **) &whilenode->false_stmt); } + bh_arr_pop(context->checker.while_node_stack); + if (whilenode->initialization != NULL) { scope_leave(context); } @@ -3377,23 +3384,15 @@ CHECK_FUNC(directive_solidify, AstDirectiveSolidify** psolid) { return Check_Success; } -CHECK_FUNC(remove_directive, AstDirectiveRemove *remove) { - if (!context->checker.inside_for_iterator) { - ERROR(remove->token->pos, "#remove is only allowed in the body of a for-loop over an iterator."); - } - - return Check_Success; -} - CHECK_FUNC(directive_first, AstDirectiveFirst *first) { - if (bh_arr_length(context->checker.for_node_stack) == 0) { - ERROR(first->token->pos, "#first is only allowed in the body of a for-loop."); + if (bh_arr_length(context->checker.while_node_stack) == 0) { + ERROR(first->token->pos, "#first is only allowed in the body of a while-loop or for-loop."); } - first->for_node = bh_arr_last(context->checker.for_node_stack); - assert(first->for_node); + first->while_node = bh_arr_last(context->checker.while_node_stack); + assert(first->while_node); - first->for_node->has_first = 1; + first->while_node->has_first = 1; return Check_Success; } @@ -3512,7 +3511,6 @@ CHECK_FUNC(statement, AstNode** pstmt) { case Ast_Kind_Defer: return check_statement(context, &((AstDefer *) stmt)->stmt); case Ast_Kind_Argument: return check_expression(context, (AstTyped **) &((AstArgument *) stmt)->value); - case Ast_Kind_Directive_Remove: return check_remove_directive(context, (AstDirectiveRemove *) stmt); case Ast_Kind_Directive_Insert: return check_insert_directive(context, (AstDirectiveInsert **) pstmt, 0); case Ast_Kind_Procedural_Expansion: return check_proc_expansion(context, (AstProceduralExpansion **) pstmt, PMEK_Statement); @@ -3710,8 +3708,9 @@ CHECK_FUNC(function, AstFunction* func) { bh_arr_push(context->checker.expected_return_type_stack, &func->type->Function.return_type); bh_arr_push(context->checker.named_return_values_stack, func->named_return_locals); - context->checker.inside_for_iterator = 0; - if (context->checker.for_node_stack) bh_arr_clear(context->checker.for_node_stack); + if (context->checker.while_node_stack) { + bh_arr_clear(context->checker.while_node_stack); + } assert(func->scope); diff --git a/compiler/src/parser.c b/compiler/src/parser.c index 0cdd89a3..120ad60b 100644 --- a/compiler/src/parser.c +++ b/compiler/src/parser.c @@ -2143,14 +2143,6 @@ static AstNode* parse_statement(OnyxParser* parser) { break; } - if (parse_possible_directive(parser, "remove")) { - // :LinearTokenDependent - AstDirectiveRemove *remove = make_node(AstDirectiveRemove, Ast_Kind_Directive_Remove); - remove->token = parser->curr - 2; - retval = (AstNode *) remove; - break; - } - if (parse_possible_directive(parser, "error")) { AstDirectiveError *error = make_node(AstDirectiveError, Ast_Kind_Directive_Error); error->token = parser->curr - 2; diff --git a/compiler/src/wasm_emit.c b/compiler/src/wasm_emit.c index cfd88309..47bd772f 100644 --- a/compiler/src/wasm_emit.c +++ b/compiler/src/wasm_emit.c @@ -507,7 +507,6 @@ EMIT_FUNC(if, AstIfWhile* if_node); EMIT_FUNC(while, AstIfWhile* while_node); EMIT_FUNC(switch, AstSwitch* switch_node); EMIT_FUNC(defer, AstDefer* defer); -EMIT_FUNC(defer_code, WasmInstruction* deferred_code, u32 code_count); EMIT_FUNC(deferred_stmt, DeferredStmt deferred_stmt); EMIT_FUNC_NO_ARGS(deferred_stmts); EMIT_FUNC(remove_directive, AstDirectiveRemove* remove); @@ -1282,6 +1281,12 @@ EMIT_FUNC(while, AstIfWhile* while_node) { } } + if (while_node->has_first) { + while_node->first_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32); + WIL(while_node->token, WI_I32_CONST, 1); + WIL(while_node->token, WI_LOCAL_SET, while_node->first_local); + } + if (while_node->false_stmt == NULL) { emit_enter_structured_block(mod, &code, SBT_Breakable_Block, while_node->token); emit_enter_structured_block(mod, &code, SBT_Continue_Loop, while_node->token); @@ -1294,6 +1299,11 @@ EMIT_FUNC(while, AstIfWhile* while_node) { emit_block(mod, &code, while_node->true_stmt, 0); + if (while_node->has_first) { + WIL(while_node->token, WI_I32_CONST, 0); + WIL(while_node->token, WI_LOCAL_SET, while_node->first_local); + } + if (while_node->bottom_test) { emit_expression(mod, &code, while_node->cond); WID(while_node->cond->token, WI_COND_JUMP, 0x00); @@ -1314,6 +1324,11 @@ EMIT_FUNC(while, AstIfWhile* while_node) { emit_block(mod, &code, while_node->true_stmt, 0); + if (while_node->has_first) { + WIL(while_node->token, WI_I32_CONST, 0); + WIL(while_node->token, WI_LOCAL_SET, while_node->first_local); + } + emit_expression(mod, &code, while_node->cond); WID(while_node->cond->token, WI_COND_JUMP, 0x00); @@ -1530,16 +1545,6 @@ EMIT_FUNC(defer, AstDefer* defer) { })); } -EMIT_FUNC(defer_code, WasmInstruction* deferred_code, u32 code_count) { - bh_arr_push(mod->deferred_stmts, ((DeferredStmt) { - .type = Deferred_Stmt_Code, - .depth = bh_arr_length(mod->structured_jump_target), - .defer_node= NULL, - .instructions = deferred_code, - .instruction_count = code_count, - })); -} - EMIT_FUNC(deferred_stmt, DeferredStmt deferred_stmt) { bh_arr(WasmInstruction) code = *pcode; if (deferred_stmt.defer_node) { @@ -3617,7 +3622,7 @@ EMIT_FUNC(expression, AstTyped* expr) { case Ast_Kind_Directive_First: { AstDirectiveFirst *first = (AstDirectiveFirst *) expr; - WIL(first->token, WI_LOCAL_GET, first->for_node->first_local); + WIL(first->token, WI_LOCAL_GET, first->while_node->first_local); break; } diff --git a/core/container/map.onyx b/core/container/map.onyx index f905337f..431b1f24 100644 --- a/core/container/map.onyx +++ b/core/container/map.onyx @@ -231,8 +231,7 @@ Map.format_map :: (output: &conv.Format_Output, format: &conv.Format, x: &Map($K } else { output->write("{ "); for& x.entries { - // :RestoreThingsThatBrokeWithForLoopChanges - // if !#first do output->write(", "); + if !#first do output->write(", "); conv.format(output, "{\"p} => {\"p}", it.key, it.value); } output->write(" }"); @@ -276,6 +275,40 @@ Map.as_iter :: (m: &Map) => }); +/// Allows for looping over a map with a for-loop +#overload +__for_expansion :: macro (map: Map($K, $V), $flags: i32, $body: Code) where (body.capture_count == 2) { + m := map + m_data := m.entries.data + m_length := m.entries.length + i := 0 + while i < m_length { + defer i += 1 + + #if flags & 1 != 0 { + #unquote body(m_data[i].key, &m_data[i].value) #skip_scope(2) + } else { + #unquote body(m_data[i].key, m_data[i].value) #skip_scope(2) + } + } +} + +#overload +__for_expansion :: macro (map: &Map($K, $V), $flags: i32, $body: Code) where (body.capture_count == 2) { + m := map + i := 0 + while i < m.entries.length { + defer i += 1 + + #if flags & 1 != 0 { + #unquote body(m.entries[i].key, &m.entries[i].value) #skip_scope(2) + } else { + #unquote body(m.entries[i].key, m.entries[i].value) #skip_scope(2) + } + } +} + + // // Helper operator overloads for accessing values, accessing // values by pointer, and setting values. diff --git a/core/container/set.onyx b/core/container/set.onyx index 5bc42f82..066399cd 100644 --- a/core/container/set.onyx +++ b/core/container/set.onyx @@ -146,6 +146,39 @@ Set.as_iter :: (s: &Set) => return .None; }); +/// Allows for looping over a Set with a for-loop +#overload +__for_expansion :: macro (set: Set($T), $flags: i32, $body: Code) where (body.capture_count == 1) { + s := set + s_data := s.entries.data + s_len := s.entries.length + i := 0 + while i < s_len { + defer i += 1 + + #if flags & 1 != 0 { + #unquote body(&s_data[i].value) #skip_scope(2) + } else { + #unquote body(s_data[i].value) #skip_scope(2) + } + } +} + +#overload +__for_expansion :: macro (set: &Set($T), $flags: i32, $body: Code) where (body.capture_count == 1) { + s := set + i := 0 + while i < s.entries.length { + defer i += 1 + + #if flags & 1 != 0 { + #unquote body(&s.entries[i].value) #skip_scope(2) + } else { + #unquote body(s.entries[i].value) #skip_scope(2) + } + } +} + // // Private symbols // diff --git a/core/string/string.onyx b/core/string/string.onyx index b213cfa4..d583f085 100644 --- a/core/string/string.onyx +++ b/core/string/string.onyx @@ -197,11 +197,10 @@ str.join :: (strs: [] str, sep: str, allocator := context.allocator) -> str { i := 0; for strs { - // :RestoreThingsThatBrokeWithForLoopChanges - // if !#first { - // core.memory.copy(&out.data[i], sep.data, sep.count); - // i += sep.count; - // } + if !#first { + core.memory.copy(&out.data[i], sep.data, sep.count); + i += sep.count; + } core.memory.copy(&out.data[i], it.data, it.count); i += it.count; diff --git a/tests/aoc-2021/day11.onyx b/tests/aoc-2021/day11.onyx index 16c00429..61055a88 100644 --- a/tests/aoc-2021/day11.onyx +++ b/tests/aoc-2021/day11.onyx @@ -41,7 +41,9 @@ main :: (args) => { sync_step := 0; while true { step += 1; - for &o in octopuses do *o += 1; + for &o in octopuses { + *o += 1 + } #persist to_flash: Set(Pos); for y in 10 do for x in 10 { @@ -50,7 +52,7 @@ main :: (args) => { } } - for flash in iter.as_iter(&to_flash) { + for flash in &to_flash { for y in -1 .. 2 do for x in -1 .. 2 { if y == 0 && x == 0 do continue; @@ -60,7 +62,7 @@ main :: (args) => { } } - for flash in iter.as_iter(&to_flash) { + for flash in &to_flash { set_octopus(flash.x, flash.y, 0); if step <= 100 do flash_count += 1; } From a7a4c8a2d1033df79ed1871744400838639c745b Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Wed, 8 Jan 2025 09:23:39 -0600 Subject: [PATCH 11/17] fixed: bug checking `iterator.close`; added: names to trap printing --- compiler/src/wasm_runtime.c | 44 +++++++++++++++++++++++++++++-------- core/operations.onyx | 8 +++++-- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/compiler/src/wasm_runtime.c b/compiler/src/wasm_runtime.c index c6d7c73d..3f80ff9c 100644 --- a/compiler/src/wasm_runtime.c +++ b/compiler/src/wasm_runtime.c @@ -385,12 +385,39 @@ static void (* wasm_func_from_idx(wasm_table_t *func_table, unsigned int index, #endif // USE_DYNCALL +static char *lookup_func_name_in_name_section(i32 funcidx, i32 name_section, i32 *out_len) { + if (name_section == 0) return NULL; + + i32 cursor = name_section; + + // This is not the most robust checking, since there are other name + // subsections that could come before the func section. For the + // moment, Onyx only produces the func subsection in output_name_section. + u32 name_kind = uleb128_to_uint(wasm_raw_bytes.data, &cursor); + if (name_kind == 1) { + u32 func_count = uleb128_to_uint(wasm_raw_bytes.data, &cursor); + fori (i, 0, func_count) { + u32 idx = uleb128_to_uint(wasm_raw_bytes.data, &cursor); + u32 len = uleb128_to_uint(wasm_raw_bytes.data, &cursor); + + if (idx == funcidx) { + *out_len = len; + return &wasm_raw_bytes.data[cursor]; + } + + cursor += len; + } + } + + return NULL; +} + static void onyx_print_trap(wasm_trap_t* trap) { wasm_message_t msg; wasm_trap_message(trap, &msg); bh_printf("TRAP: %b\n", msg.data, msg.size); - i32 func_name_section = 0; + i32 name_section = 0; i32 cursor = 8; // skip the magic number and version while (cursor < wasm_raw_bytes.length) { @@ -400,9 +427,9 @@ static void onyx_print_trap(wasm_trap_t* trap) { i32 section_start = cursor; if (section_number == 0) { u64 name_len = uleb128_to_uint(wasm_raw_bytes.data, &cursor); - if (!strncmp((const char *) wasm_raw_bytes.data + cursor, "_onyx_func_offsets", name_len)) { + if (!strncmp((const char *) wasm_raw_bytes.data + cursor, "name", name_len)) { cursor += name_len; - func_name_section = cursor; + name_section = cursor; break; } } @@ -417,14 +444,13 @@ static void onyx_print_trap(wasm_trap_t* trap) { i32 func_idx = wasm_frame_func_index(frames.data[i]); i32 mod_offset = wasm_frame_module_offset(frames.data[i]); - if (func_name_section > 0) { - i32 cursor = func_name_section + 4 * func_idx; - i32 func_offset = *(i32 *) (wasm_raw_bytes.data + cursor); - char* func_name = (char *) wasm_raw_bytes.data + func_name_section + func_offset; + i32 func_name_length; + char* func_name = lookup_func_name_in_name_section(func_idx, name_section, &func_name_length); - bh_printf(" func[%d]:%p at %s\n", func_idx, mod_offset, func_name); + if (func_name) { + bh_printf(" func[%d]:%p at %b\n", func_idx, mod_offset, func_name, func_name_length); } else { - bh_printf(" func[%d]\n", func_idx); + bh_printf(" func[%d]:%p\n", func_idx, mod_offset); } } } diff --git a/core/operations.onyx b/core/operations.onyx index 1cf8c11d..db551810 100644 --- a/core/operations.onyx +++ b/core/operations.onyx @@ -195,7 +195,9 @@ __for_expansion :: macro (iterator: Iterator($T), $flags: i32, $body: Code) wher _iterator := iterator #if flags & 2 == 0 { - defer _iterator.close(_iterator.data) + defer if _iterator.close != null_proc { + _iterator.close(_iterator.data) + } } #if #defined(body.capture_type_2) { @@ -222,7 +224,9 @@ __for_expansion :: macro (iterator: Iterator($T), $flags: i32, $body: Code) wher _iterator := iterator #if (flags & 2 == 0) { - defer _iterator.close(_iterator.data) + defer if _iterator.close != null_proc { + _iterator.close(_iterator.data) + } } while true { From 9d582e6a95e88a16c282dad3312e29d018f37ca1 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Thu, 9 Jan 2025 14:35:52 -0600 Subject: [PATCH 12/17] fixed: warning when compiling --- compiler/src/wasm_runtime.c | 4 +-- core/operations.onyx | 62 ++++++++++++++++++------------------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/compiler/src/wasm_runtime.c b/compiler/src/wasm_runtime.c index 3f80ff9c..356f197b 100644 --- a/compiler/src/wasm_runtime.c +++ b/compiler/src/wasm_runtime.c @@ -385,7 +385,7 @@ static void (* wasm_func_from_idx(wasm_table_t *func_table, unsigned int index, #endif // USE_DYNCALL -static char *lookup_func_name_in_name_section(i32 funcidx, i32 name_section, i32 *out_len) { +static char *lookup_func_name_in_name_section(u32 funcidx, i32 name_section, i32 *out_len) { if (name_section == 0) return NULL; i32 cursor = name_section; @@ -441,7 +441,7 @@ static void onyx_print_trap(wasm_trap_t* trap) { wasm_frame_vec_t frames; wasm_trap_trace(trap, &frames); fori (i, 0, (i32) frames.size) { - i32 func_idx = wasm_frame_func_index(frames.data[i]); + u32 func_idx = wasm_frame_func_index(frames.data[i]); i32 mod_offset = wasm_frame_module_offset(frames.data[i]); i32 func_name_length; diff --git a/core/operations.onyx b/core/operations.onyx index db551810..ecc74aca 100644 --- a/core/operations.onyx +++ b/core/operations.onyx @@ -47,6 +47,36 @@ __array_op_scalar :: macro (l: [$N]$T, r: T, $body: Code) -> [N]T { // // Builtin for expansions + +#overload +__for_expansion :: macro (_s: [] $T, $flags: i32, $body: Code) where (flags & 1 == 0) { + s := _s + data := s.data + count := s.count + + i := 0 + while i < count { + defer i += 1 + + v := data[i] + #unquote body(v, i) #skip_scope(2) + } +} + +#overload +__for_expansion :: macro (_s: [] $T, $flags: i32, $body: Code) where (flags & 1 != 0) { + s := _s + data := s.data + count := s.count + + i := 0 + while i < count { + defer i += 1 + + v := &data[i] + #unquote body(v, i) #skip_scope(2) + } +} // #overload @@ -91,7 +121,7 @@ __for_expansion :: macro (_r: range, $flags: i32, $body: Code) where (body.captu } #overload -__for_expansion :: macro (_r: range64, $flags: i32, $body: Code) { +__for_expansion :: macro (_r: range64, $flags: i32, $body: Code) where (body.capture_count == 1) { r := _r i := r.low end := r.high @@ -155,36 +185,6 @@ __for_expansion :: macro (n: i64, $flags: i32, $body: Code) { } } -#overload -__for_expansion :: macro (_s: [] $T, $flags: i32, $body: Code) where (flags & 1 == 0) { - s := _s - data := s.data - count := s.count - - i := 0 - while i < count { - defer i += 1 - - v := data[i] - #unquote body(v, i) #skip_scope(2) - } -} - -#overload -__for_expansion :: macro (_s: [] $T, $flags: i32, $body: Code) where (flags & 1 != 0) { - s := _s - data := s.data - count := s.count - - i := 0 - while i < count { - defer i += 1 - - v := &data[i] - #unquote body(v, i) #skip_scope(2) - } -} - // // Move these to iter.onyx From e8cd02731416c4d1863102188a149059f5a8a47f Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Thu, 9 Jan 2025 14:36:31 -0600 Subject: [PATCH 13/17] removed: test case for `#remove` --- tests/remove_test | 2 -- tests/remove_test.onyx | 18 ------------------ 2 files changed, 20 deletions(-) delete mode 100644 tests/remove_test delete mode 100644 tests/remove_test.onyx diff --git a/tests/remove_test b/tests/remove_test deleted file mode 100644 index 71682c4d..00000000 --- a/tests/remove_test +++ /dev/null @@ -1,2 +0,0 @@ -[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 ] -[ 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23 ] diff --git a/tests/remove_test.onyx b/tests/remove_test.onyx deleted file mode 100644 index 14f70c0c..00000000 --- a/tests/remove_test.onyx +++ /dev/null @@ -1,18 +0,0 @@ - - -use core {*} - -main :: (args) => { - x: [..] i32; - for 25 do x << it; - - println(x); - - for iter.as_iter(&x) { - if it % 2 == 0 { - #remove; - } - } - - println(x); -} From 983dfb858ec62f77c273c838d3c6c9514afc4f20 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Fri, 10 Jan 2025 18:03:25 -0600 Subject: [PATCH 14/17] fixed: error reporting for `for` overloads --- compiler/src/checker.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/compiler/src/checker.c b/compiler/src/checker.c index a9a1d180..879fd09e 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -448,9 +448,25 @@ CHECK_FUNC(for, AstFor** pfornode) { assert(fornode->intermediate_macro_expansion); } + CheckStatus cs = check_call(context, (AstCall **) &fornode->intermediate_macro_expansion); + if (cs == Check_Yield) { + if (fornode->intermediate_macro_expansion->kind == Ast_Kind_Call) { + return Check_Yield; + } + } + + if (cs == Check_Error) { + i32 vars = bh_arr_length(fornode->indexing_variables); + + ERROR_(fornode->token->pos, "Unable to loop over a '%s' with %d captured argument%s.", + type_get_name(context, fornode->iter->type), + vars, + bh_num_plural(vars)); + } + *pfornode = (AstFor *) fornode->intermediate_macro_expansion; - CHECK(call, (AstCall **) pfornode); - + CHECK(expression, (AstTyped **) pfornode); + return Check_Success; } @@ -886,6 +902,14 @@ CHECK_FUNC(resolve_callee, AstCall* call, AstTyped** effective_callee) { YIELD(call->token->pos, "Waiting to know all options for overloaded function"); } + // + // When reporting an error about a for-loop expansion overload, don't report all the overloads + // as there are likely very many of them and the error message won't be very useful. Instead, + // a better error message will be printed in check_for. + if ((AstOverloadedFunction *) callee == context->builtins.for_expansion) { + return Check_Error; + } + report_unable_to_match_overload(context, call, ((AstOverloadedFunction *) callee)->overloads); return Check_Error; } From 02459dcdc11763a9ea56ac838b69d35b54987e76 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Fri, 10 Jan 2025 21:44:57 -0600 Subject: [PATCH 15/17] added: for expansion test cases --- tests/for_expansion_misc | 21 +++++++++ tests/for_expansion_misc.onyx | 83 +++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 tests/for_expansion_misc create mode 100644 tests/for_expansion_misc.onyx diff --git a/tests/for_expansion_misc b/tests/for_expansion_misc new file mode 100644 index 00000000..6b452f5a --- /dev/null +++ b/tests/for_expansion_misc @@ -0,0 +1,21 @@ +0: Test +1: Foo +1 +2 +3 +4 +5 +c: 0 (i32) v: 1 +c: 1 (i32) v: 2 +c: 2 (i32) v: 3 +c: 3 (i32) v: 4 +c: 4 (i32) v: 5 +1, a +1, b +1, c +2, a +2, b +2, c +3, a +3, b +3, c diff --git a/tests/for_expansion_misc.onyx b/tests/for_expansion_misc.onyx new file mode 100644 index 00000000..0865bec3 --- /dev/null +++ b/tests/for_expansion_misc.onyx @@ -0,0 +1,83 @@ +use core {*} + +ArrPair :: struct (A: type_expr, B: type_expr) { + a: [] A + b: [] B +} + +ArrPair.make :: (a: [] $A, b: [] $B) -> ArrPair(A, B) { + return .{ a, b } +} + +#overload +__for_expansion :: macro (x: ArrPair($A, $B), $flags: i32, $body: Code) where body.capture_count == 2 { + #if flags & 1 != 0 { + #error "Cannot iterate by pointer." + } + + for _a in x.a { + for _b in x.b { + #unquote body(_a, _b) #skip_scope(3) + } + } +} + + + +MyRange :: struct { low, high: i32; step: i32 = 1 } + +#overload +__for_expansion :: macro (r: MyRange, $flags: i32, $body: Code) where (body.capture_count == 1) { + _r := r + _i := _r.low + _high := _r.high + _step := _r.step + while _i < _high { + defer { + _i += _step + } + + #unquote body(_i) #skip_scope(2) + } +} + +range_old :: () { + for v in 0 .. 10 { + println(v) + } +} + +range_new :: () { + for v in MyRange.{0, 10} { + println(v) + } +} + +main :: () { + m := Map.literal(u32, str, .[ + .{0, "Test"} + .{1, "Foo"} + .{2, "Cowabunga"} + ]) + for k, v in m { + printf("{}: {}\n", k, v) + if k == 1 do break + } + + + s := Set.from(.[1, 2, 3, 4, 5]) + for v in s { + println(v) + } + + + i := Iterator.from(s) + for v, c in i { + printf("c: {} ({}) v: {}\n", c, typeof c, *v) + } + + ap := ArrPair.make(.[1, 2, 3], .["a", "b", "c"]) + for v1, v2 in ap { + printf("{}, {}\n", v1, v2) + } +} From 1ed14080631348eb733f08bf56fce37591f4358f Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Sat, 11 Jan 2025 11:22:27 -0600 Subject: [PATCH 16/17] changed: language used when reporting error about a for-loop by pointer --- compiler/src/checker.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/src/checker.c b/compiler/src/checker.c index 879fd09e..c6fb9b57 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -458,8 +458,9 @@ CHECK_FUNC(for, AstFor** pfornode) { if (cs == Check_Error) { i32 vars = bh_arr_length(fornode->indexing_variables); - ERROR_(fornode->token->pos, "Unable to loop over a '%s' with %d captured argument%s.", + ERROR_(fornode->token->pos, "Unable to loop over a '%s'%s with %d captured argument%s.", type_get_name(context, fornode->iter->type), + fornode->by_pointer ? " by pointer," : "", vars, bh_num_plural(vars)); } From c51efc5d5cafdf8149cf8495650ea6fbb391d749 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Sun, 19 Jan 2025 13:54:12 -0600 Subject: [PATCH 17/17] changed: using `__For_Expansion_Flags` again --- compiler/src/astnodes.c | 2 +- compiler/src/library_main.c | 2 + compiler/src/polymorph.h | 7 ++++ core/builtin.onyx | 9 +++-- core/container/iter.onyx | 51 ++++++++++++++++++++++++++ core/container/map.onyx | 8 ++-- core/container/set.onyx | 8 ++-- core/operations.onyx | 69 ++++------------------------------- tests/for_expansion_misc.onyx | 6 +-- 9 files changed, 86 insertions(+), 76 deletions(-) diff --git a/compiler/src/astnodes.c b/compiler/src/astnodes.c index 0827b575..0975635e 100644 --- a/compiler/src/astnodes.c +++ b/compiler/src/astnodes.c @@ -1986,7 +1986,7 @@ AstCall * create_implicit_for_expansion_call(Context *context, AstFor *fornode) if (fornode->no_close) flags |= 2; // NO_CLOSE AstNumLit *flag_node = make_int_literal(context, flags); - // flag_node->type_node = context->builtins.for_expansion_flag_type; + flag_node->type_node = context->builtins.for_expansion_flag_type; // flag_node->type = type_build_from_ast(context, context->builtins.for_expansion_flag_type); // assert(flag_node->type); diff --git a/compiler/src/library_main.c b/compiler/src/library_main.c index cf10ce5e..ce2cdb5d 100644 --- a/compiler/src/library_main.c +++ b/compiler/src/library_main.c @@ -133,6 +133,8 @@ void onyx_context_free(onyx_context_t *ctx) { bh_arr_free(context->scopes); bh_scratch_free(&context->scratch); bh_managed_heap_free(&context->heap); + + free(ctx); } void onyx_options_ready(onyx_context_t *ctx) { diff --git a/compiler/src/polymorph.h b/compiler/src/polymorph.h index de47331d..05194acb 100644 --- a/compiler/src/polymorph.h +++ b/compiler/src/polymorph.h @@ -170,6 +170,13 @@ static AstSolidifiedFunction generate_solidified_function( bh_arr_new(context->gp_alloc, constraint->args, 1); bh_arr_push(constraint->args, (AstTyped *) ast_clone(context, param->poly_sym)); + // + // Sometimes this array is uninitialized, and that would cause a memory leak + // because the memory wouldn't be tracked in the gp_alloc. + if (!solidified_func.func->constraints.constraints) { + bh_arr_new(context->gp_alloc, solidified_func.func->constraints.constraints, 1); + } + bh_arr_push(solidified_func.func->constraints.constraints, constraint); } diff --git a/core/builtin.onyx b/core/builtin.onyx index fdb29657..117a3d77 100644 --- a/core/builtin.onyx +++ b/core/builtin.onyx @@ -528,11 +528,14 @@ __dispose_used_local :: #match -> void { __For_Expansion_Flags :: enum #flags { - BY_POINTER - NO_CLOSE + BY_POINTER :: 1 + NO_CLOSE :: 2 } -/// TODO: comment this +/// All for-loops in Onyx are actually defined as an overload to this procedure. +/// All overloads are macros that accept 3 arguments: the iteratable, flags, and the body +/// of the loop. Examples of how to use this overloaded procedure can be found in `operations.onyx` +/// next to this file. __for_expansion :: #match -> void {} diff --git a/core/container/iter.onyx b/core/container/iter.onyx index 9557440d..cc62cc17 100644 --- a/core/container/iter.onyx +++ b/core/container/iter.onyx @@ -14,6 +14,57 @@ use core.intrinsics.types {type_is_struct} // Iterator is a builtin type known by the compiler, as Iterators // can be used in for-loops natively without any translation. +#overload +__for_expansion :: macro (iterator: Iterator($T), $flags: __For_Expansion_Flags, $body: Code) where + !(flags & .BY_POINTER) && (body.capture_count == 2) +{ + _iterator := iterator + + #if !(flags & .NO_CLOSE) { + defer if _iterator.close != null_proc { + _iterator.close(_iterator.data) + } + } + + #if #defined(body.capture_type_2) { + _i: body.capture_type_2 + } else { + _i: i32 + } + + while true { + defer _i += 1 + + value := _iterator.next(_iterator.data) + if value.tag == .None do break + + _it := *cast(&T) (cast([&] u8, &value) + alignof ? T) + #unquote body(_it, _i) #skip_scope(2) + } +} + +#overload +__for_expansion :: macro (iterator: Iterator($T), $flags: __For_Expansion_Flags, $body: Code) where + !(flags & .BY_POINTER) && (body.capture_count == 1) +{ + _iterator := iterator + + #if !(flags & .NO_CLOSE) { + defer if _iterator.close != null_proc { + _iterator.close(_iterator.data) + } + } + + while true { + value := _iterator.next(_iterator.data) + if value.tag == .None do break + + _it := *cast(&T) (cast([&] u8, &value) + alignof ? T) + #unquote body(_it) #skip_scope(2) + } +} + + Iterator.from :: as_iter Iterator.next :: next Iterator.close :: close diff --git a/core/container/map.onyx b/core/container/map.onyx index 431b1f24..0b588dfc 100644 --- a/core/container/map.onyx +++ b/core/container/map.onyx @@ -277,7 +277,7 @@ Map.as_iter :: (m: &Map) => /// Allows for looping over a map with a for-loop #overload -__for_expansion :: macro (map: Map($K, $V), $flags: i32, $body: Code) where (body.capture_count == 2) { +__for_expansion :: macro (map: Map($K, $V), $flags: __For_Expansion_Flags, $body: Code) where (body.capture_count == 2) { m := map m_data := m.entries.data m_length := m.entries.length @@ -285,7 +285,7 @@ __for_expansion :: macro (map: Map($K, $V), $flags: i32, $body: Code) where (bod while i < m_length { defer i += 1 - #if flags & 1 != 0 { + #if flags & .BY_POINTER { #unquote body(m_data[i].key, &m_data[i].value) #skip_scope(2) } else { #unquote body(m_data[i].key, m_data[i].value) #skip_scope(2) @@ -294,13 +294,13 @@ __for_expansion :: macro (map: Map($K, $V), $flags: i32, $body: Code) where (bod } #overload -__for_expansion :: macro (map: &Map($K, $V), $flags: i32, $body: Code) where (body.capture_count == 2) { +__for_expansion :: macro (map: &Map($K, $V), $flags: __For_Expansion_Flags, $body: Code) where (body.capture_count == 2) { m := map i := 0 while i < m.entries.length { defer i += 1 - #if flags & 1 != 0 { + #if flags & .BY_POINTER { #unquote body(m.entries[i].key, &m.entries[i].value) #skip_scope(2) } else { #unquote body(m.entries[i].key, m.entries[i].value) #skip_scope(2) diff --git a/core/container/set.onyx b/core/container/set.onyx index 066399cd..dcf6a6df 100644 --- a/core/container/set.onyx +++ b/core/container/set.onyx @@ -148,7 +148,7 @@ Set.as_iter :: (s: &Set) => /// Allows for looping over a Set with a for-loop #overload -__for_expansion :: macro (set: Set($T), $flags: i32, $body: Code) where (body.capture_count == 1) { +__for_expansion :: macro (set: Set($T), $flags: __For_Expansion_Flags, $body: Code) where (body.capture_count == 1) { s := set s_data := s.entries.data s_len := s.entries.length @@ -156,7 +156,7 @@ __for_expansion :: macro (set: Set($T), $flags: i32, $body: Code) where (body.ca while i < s_len { defer i += 1 - #if flags & 1 != 0 { + #if flags & .BY_POINTER { #unquote body(&s_data[i].value) #skip_scope(2) } else { #unquote body(s_data[i].value) #skip_scope(2) @@ -165,13 +165,13 @@ __for_expansion :: macro (set: Set($T), $flags: i32, $body: Code) where (body.ca } #overload -__for_expansion :: macro (set: &Set($T), $flags: i32, $body: Code) where (body.capture_count == 1) { +__for_expansion :: macro (set: &Set($T), $flags: __For_Expansion_Flags, $body: Code) where (body.capture_count == 1) { s := set i := 0 while i < s.entries.length { defer i += 1 - #if flags & 1 != 0 { + #if flags & .BY_POINTER { #unquote body(&s.entries[i].value) #skip_scope(2) } else { #unquote body(s.entries[i].value) #skip_scope(2) diff --git a/core/operations.onyx b/core/operations.onyx index ecc74aca..9810e7a1 100644 --- a/core/operations.onyx +++ b/core/operations.onyx @@ -49,7 +49,7 @@ __array_op_scalar :: macro (l: [$N]$T, r: T, $body: Code) -> [N]T { // Builtin for expansions #overload -__for_expansion :: macro (_s: [] $T, $flags: i32, $body: Code) where (flags & 1 == 0) { +__for_expansion :: macro (_s: [] $T, $flags: __For_Expansion_Flags, $body: Code) where !(flags & .BY_POINTER) { s := _s data := s.data count := s.count @@ -64,7 +64,7 @@ __for_expansion :: macro (_s: [] $T, $flags: i32, $body: Code) where (flags & 1 } #overload -__for_expansion :: macro (_s: [] $T, $flags: i32, $body: Code) where (flags & 1 != 0) { +__for_expansion :: macro (_s: [] $T, $flags: __For_Expansion_Flags, $body: Code) where (flags & .BY_POINTER) { s := _s data := s.data count := s.count @@ -80,7 +80,7 @@ __for_expansion :: macro (_s: [] $T, $flags: i32, $body: Code) where (flags & 1 // #overload -__for_expansion :: macro (_r: range, $flags: i32, $body: Code) where (body.capture_count == 1) { +__for_expansion :: macro (_r: range, $flags: __For_Expansion_Flags, $body: Code) where (body.capture_count == 1) { r := _r i := r.low end := r.high @@ -97,7 +97,7 @@ __for_expansion :: macro (_r: range, $flags: i32, $body: Code) where (body.captu } #overload -__for_expansion :: macro (_r: range, $flags: i32, $body: Code) where (body.capture_count == 2) { +__for_expansion :: macro (_r: range, $flags: __For_Expansion_Flags, $body: Code) where (body.capture_count == 2) { r := _r i := r.low end := r.high @@ -121,7 +121,7 @@ __for_expansion :: macro (_r: range, $flags: i32, $body: Code) where (body.captu } #overload -__for_expansion :: macro (_r: range64, $flags: i32, $body: Code) where (body.capture_count == 1) { +__for_expansion :: macro (_r: range64, $flags: __For_Expansion_Flags, $body: Code) where (body.capture_count == 1) { r := _r i := r.low end := r.high @@ -138,7 +138,7 @@ __for_expansion :: macro (_r: range64, $flags: i32, $body: Code) where (body.cap } #overload -__for_expansion :: macro (_r: range64, $flags: i32, $body: Code) where (body.capture_count == 2) { +__for_expansion :: macro (_r: range64, $flags: __For_Expansion_Flags, $body: Code) where (body.capture_count == 2) { r := _r i := r.low end := r.high @@ -162,7 +162,7 @@ __for_expansion :: macro (_r: range64, $flags: i32, $body: Code) where (body.cap } #overload -__for_expansion :: macro (n: i32, $flags: i32, $body: Code) { +__for_expansion :: macro (n: i32, $flags: __For_Expansion_Flags, $body: Code) { i := 0 end := n @@ -174,7 +174,7 @@ __for_expansion :: macro (n: i32, $flags: i32, $body: Code) { } #overload -__for_expansion :: macro (n: i64, $flags: i32, $body: Code) { +__for_expansion :: macro (n: i64, $flags: __For_Expansion_Flags, $body: Code) { i : i64 = 0 end : i64 = n @@ -185,56 +185,3 @@ __for_expansion :: macro (n: i64, $flags: i32, $body: Code) { } } - -// -// Move these to iter.onyx -#overload -__for_expansion :: macro (iterator: Iterator($T), $flags: i32, $body: Code) where - (flags & 1 == 0) && (body.capture_count == 2) -{ - _iterator := iterator - - #if flags & 2 == 0 { - defer if _iterator.close != null_proc { - _iterator.close(_iterator.data) - } - } - - #if #defined(body.capture_type_2) { - _i: body.capture_type_2 - } else { - _i: i32 - } - - while true { - defer _i += 1 - - value := _iterator.next(_iterator.data) - if value.tag == .None do break - - _it := *cast(&T) (cast([&] u8, &value) + alignof ? T) - #unquote body(_it, _i) #skip_scope(2) - } -} - -#overload -__for_expansion :: macro (iterator: Iterator($T), $flags: i32, $body: Code) where - (flags & 1 == 0) && (body.capture_count == 1) -{ - _iterator := iterator - - #if (flags & 2 == 0) { - defer if _iterator.close != null_proc { - _iterator.close(_iterator.data) - } - } - - while true { - value := _iterator.next(_iterator.data) - if value.tag == .None do break - - _it := *cast(&T) (cast([&] u8, &value) + alignof ? T) - #unquote body(_it) #skip_scope(2) - } -} - diff --git a/tests/for_expansion_misc.onyx b/tests/for_expansion_misc.onyx index 0865bec3..212c25ff 100644 --- a/tests/for_expansion_misc.onyx +++ b/tests/for_expansion_misc.onyx @@ -10,8 +10,8 @@ ArrPair.make :: (a: [] $A, b: [] $B) -> ArrPair(A, B) { } #overload -__for_expansion :: macro (x: ArrPair($A, $B), $flags: i32, $body: Code) where body.capture_count == 2 { - #if flags & 1 != 0 { +__for_expansion :: macro (x: ArrPair($A, $B), $flags: __For_Expansion_Flags, $body: Code) where body.capture_count == 2 { + #if flags & .BY_POINTER { #error "Cannot iterate by pointer." } @@ -27,7 +27,7 @@ __for_expansion :: macro (x: ArrPair($A, $B), $flags: i32, $body: Code) where bo MyRange :: struct { low, high: i32; step: i32 = 1 } #overload -__for_expansion :: macro (r: MyRange, $flags: i32, $body: Code) where (body.capture_count == 1) { +__for_expansion :: macro (r: MyRange, $flags: __For_Expansion_Flags, $body: Code) where (body.capture_count == 1) { _r := r _i := _r.low _high := _r.high