From 393e4a9db6d649387dd5c831134e75d98b780156 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 27 Apr 2024 09:53:02 +0100 Subject: Generalize Odin call-based "iterators" to work with more than 2-values: `for x, y, z, w in iterate(&it)` It has an artificial limitation of 100 values because if you need for than that, you're doing something wrong. --- src/check_stmt.cpp | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 971841165..c31056b33 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1669,12 +1669,20 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) case Type_Tuple: { isize count = t->Tuple.variables.count; - if (count < 1 || count > 3) { + if (count < 1) { ERROR_BLOCK(); check_not_tuple(ctx, &operand); - error_line("\tMultiple return valued parameters in a range statement are limited to a maximum of 2 usable values with a trailing boolean for the conditional\n"); + error_line("\tMultiple return valued parameters in a range statement are limited to a minimum of 1 usable values with a trailing boolean for the conditional, got %td\n", count); break; } + enum : isize {MAXIMUM_COUNT = 100}; + if (count > MAXIMUM_COUNT) { + ERROR_BLOCK(); + check_not_tuple(ctx, &operand); + error_line("\tMultiple return valued parameters in a range statement are limited to a maximum of %td usable values with a trailing boolean for the conditional, got %td\n", MAXIMUM_COUNT, count); + break; + } + Type *cond_type = t->Tuple.variables[count-1]->type; if (!is_type_boolean(cond_type)) { gbString s = type_to_string(cond_type); @@ -1683,24 +1691,23 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) break; } + max_val_count = count; + for (Entity *e : t->Tuple.variables) { array_add(&vals, e->type); } is_possibly_addressable = false; - if (rs->vals.count > 1 && rs->vals[1] != nullptr && count < 3) { - gbString s = type_to_string(t); - error(operand.expr, "Expected a 3-valued expression on the rhs, got (%s)", s); - gb_string_free(s); - break; - } - - if (rs->vals.count > 0 && rs->vals[0] != nullptr && count < 2) { - gbString s = type_to_string(t); - error(operand.expr, "Expected at least a 2-valued expression on the rhs, got (%s)", s); - gb_string_free(s); - break; + bool do_break = false; + for (isize i = rs->vals.count-1; i >= 0; i--) { + if (rs->vals[i] != nullptr && count < i+2) { + gbString s = type_to_string(t); + error(operand.expr, "Expected a %td-valued expression on the rhs, got (%s)", i+2, s); + gb_string_free(s); + do_break = true; + break; + } } if (is_reverse) { -- cgit v1.2.3 From 5e1b376e22854ee4dd1d455d9db372d24f445ae4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 27 Apr 2024 10:34:17 +0100 Subject: Disallow `for x in bitset_or_map` if `x` is a variable that matches the "key" --- src/check_stmt.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index c31056b33..cccbab4f6 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1629,6 +1629,17 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) if (build_context.no_rtti && is_type_enum(t->BitSet.elem)) { error(node, "Iteration over a bit_set of an enum is not allowed runtime type information (RTTI) has been disallowed"); } + if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) { + String name = rs->vals[0]->Ident.token.string; + Entity *found = scope_lookup(ctx->scope, name); + if (found && are_types_identical(found->type, t->BitSet.elem)) { + ERROR_BLOCK(); + gbString s = expr_to_string(expr); + error(rs->vals[0], "'%.*s' shadows a previous declaration which might be ambiguous with 'for (%.*s in %s)'", LIT(name), LIT(name), s); + error_line("\tSuggestion: Use a different identifier if iteration is wanted, or surround in parentheses if a normal for loop is wanted\n"); + gb_string_free(s); + } + } break; case Type_EnumeratedArray: @@ -1664,6 +1675,17 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) if (is_reverse) { error(node, "#reverse for is not supported for map types, as maps are unordered"); } + if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) { + String name = rs->vals[0]->Ident.token.string; + Entity *found = scope_lookup(ctx->scope, name); + if (found && are_types_identical(found->type, t->Map.key)) { + ERROR_BLOCK(); + gbString s = expr_to_string(expr); + error(rs->vals[0], "'%.*s' shadows a previous declaration which might be ambiguous with 'for (%.*s in %s)'", LIT(name), LIT(name), s); + error_line("\tSuggestion: Use a different identifier if iteration is wanted, or surround in parentheses if a normal for loop is wanted\n"); + gb_string_free(s); + } + } break; case Type_Tuple: -- cgit v1.2.3 From ad5c9469d82e85b0d1fe4dbeb5cce98420e5a0ab Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 10 May 2024 14:22:43 +0100 Subject: Fix #3522 --- src/check_stmt.cpp | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index cccbab4f6..c018077f9 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -161,8 +161,7 @@ gb_internal bool check_is_terminating_list(Slice const &stmts, String con } gb_internal bool check_has_break_list(Slice const &stmts, String const &label, bool implicit) { - for_array(i, stmts) { - Ast *stmt = stmts[i]; + for (Ast *stmt : stmts) { if (check_has_break(stmt, label, implicit)) { return true; } @@ -170,6 +169,14 @@ gb_internal bool check_has_break_list(Slice const &stmts, String const &l return false; } +gb_internal bool check_has_break_expr_list(Slice const &exprs, String const &label) { + for (Ast *expr : exprs) { + if (expr && expr->viral_state_flags & ViralStateFlag_ContainsOrBreak) { + return true; + } + } + return false; +} gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) { switch (stmt->kind) { @@ -227,6 +234,20 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) return true; } break; + + case Ast_ValueDecl: + if (stmt->ValueDecl.is_mutable && check_has_break_expr_list(stmt->ValueDecl.values, label)) { + return true; + } + break; + case Ast_AssignStmt: + if (check_has_break_expr_list(stmt->AssignStmt.lhs, label)) { + return true; + } + if (check_has_break_expr_list(stmt->AssignStmt.rhs, label)) { + return true; + } + break; } return false; @@ -250,6 +271,15 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { return check_is_terminating(unparen_expr(es->expr), label); case_end; + case_ast_node(vd, ValueDecl, node); + return check_has_break_expr_list(vd->values, label); + case_end; + + case_ast_node(as, AssignStmt, node); + return check_has_break_expr_list(as->lhs, label) || + check_has_break_expr_list(as->rhs, label); + case_end; + case_ast_node(bs, BranchStmt, node); return bs->token.kind == Token_fallthrough; case_end; -- cgit v1.2.3 From 4eab735b137b5bdf7e5c5805ab19177af97d0423 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 12:24:50 +0100 Subject: Minor clean up of `is_terminating` code This does not fix all known issues with it --- src/check_stmt.cpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index c018077f9..3b836aa3c 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -254,6 +254,13 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) } +String label_from_node(Ast *node, String default_label) { + if (node != nullptr && node->kind == Ast_Ident) { + return node->Ident.token.string; + } + return default_label; +} + // NOTE(bill): The last expression has to be a 'return' statement // TODO(bill): This is a mild hack and should be probably handled properly @@ -264,7 +271,7 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(bs, BlockStmt, node); - return check_is_terminating_list(bs->stmts, label); + return check_is_terminating_list(bs->stmts, label_from_node(bs->label, label)); case_end; case_ast_node(es, ExprStmt, node); @@ -285,9 +292,10 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(is, IfStmt, node); + String new_label = label_from_node(is->label, label); if (is->else_stmt != nullptr) { - if (check_is_terminating(is->body, label) && - check_is_terminating(is->else_stmt, label)) { + if (check_is_terminating(is->body, new_label) && + check_is_terminating(is->else_stmt, new_label)) { return true; } } @@ -320,7 +328,8 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(fs, ForStmt, node); - if (fs->cond == nullptr && !check_has_break(fs->body, label, true)) { + String new_label = label_from_node(fs->label, label); + if (fs->cond == nullptr && !check_has_break(fs->body, new_label, true)) { return true; } case_end; @@ -335,14 +344,15 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_ast_node(ss, SwitchStmt, node); bool has_default = false; + String new_label = label_from_node(ss->label, label); for_array(i, ss->body->BlockStmt.stmts) { Ast *clause = ss->body->BlockStmt.stmts[i]; ast_node(cc, CaseClause, clause); if (cc->list.count == 0) { has_default = true; } - if (!check_is_terminating_list(cc->stmts, label) || - check_has_break_list(cc->stmts, label, true)) { + if (!check_is_terminating_list(cc->stmts, new_label) || + check_has_break_list(cc->stmts, new_label, true)) { return false; } } @@ -351,14 +361,15 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_ast_node(ss, TypeSwitchStmt, node); bool has_default = false; + String new_label = label_from_node(ss->label, label); for_array(i, ss->body->BlockStmt.stmts) { Ast *clause = ss->body->BlockStmt.stmts[i]; ast_node(cc, CaseClause, clause); if (cc->list.count == 0) { has_default = true; } - if (!check_is_terminating_list(cc->stmts, label) || - check_has_break_list(cc->stmts, label, true)) { + if (!check_is_terminating_list(cc->stmts, new_label) || + check_has_break_list(cc->stmts, new_label, true)) { return false; } } -- cgit v1.2.3 From 34c8739b69e0778fe19f0593fecb7e7080363862 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 12:42:06 +0100 Subject: Fix #3578 --- src/check_stmt.cpp | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 3b836aa3c..ad91838b9 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -253,15 +253,17 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) return false; } - -String label_from_node(Ast *node, String default_label) { - if (node != nullptr && node->kind == Ast_Ident) { +String label_string(Ast *node) { + GB_ASSERT(node != nullptr); + if (node->kind == Ast_Ident) { return node->Ident.token.string; + } else if (node->kind == Ast_Label) { + return label_string(node->Label.name); } - return default_label; + GB_ASSERT("INVALID LABEL"); + return {}; } - // NOTE(bill): The last expression has to be a 'return' statement // TODO(bill): This is a mild hack and should be probably handled properly gb_internal bool check_is_terminating(Ast *node, String const &label) { @@ -271,7 +273,12 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(bs, BlockStmt, node); - return check_is_terminating_list(bs->stmts, label_from_node(bs->label, label)); + if (check_is_terminating_list(bs->stmts, label)) { + if (bs->label != nullptr) { + return check_is_terminating_list(bs->stmts, label_string(bs->label)); + } + return true; + } case_end; case_ast_node(es, ExprStmt, node); @@ -292,10 +299,9 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(is, IfStmt, node); - String new_label = label_from_node(is->label, label); if (is->else_stmt != nullptr) { - if (check_is_terminating(is->body, new_label) && - check_is_terminating(is->else_stmt, new_label)) { + if (check_is_terminating(is->body, label) && + check_is_terminating(is->else_stmt, label)) { return true; } } @@ -328,8 +334,10 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(fs, ForStmt, node); - String new_label = label_from_node(fs->label, label); - if (fs->cond == nullptr && !check_has_break(fs->body, new_label, true)) { + if (fs->cond == nullptr && !check_has_break(fs->body, label, true)) { + if (fs->label) { + return !check_has_break(fs->body, label_string(fs->label), false); + } return true; } case_end; @@ -344,15 +352,14 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_ast_node(ss, SwitchStmt, node); bool has_default = false; - String new_label = label_from_node(ss->label, label); for_array(i, ss->body->BlockStmt.stmts) { Ast *clause = ss->body->BlockStmt.stmts[i]; ast_node(cc, CaseClause, clause); if (cc->list.count == 0) { has_default = true; } - if (!check_is_terminating_list(cc->stmts, new_label) || - check_has_break_list(cc->stmts, new_label, true)) { + if (!check_is_terminating_list(cc->stmts, label) || + check_has_break_list(cc->stmts, label, true)) { return false; } } @@ -361,15 +368,14 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_ast_node(ss, TypeSwitchStmt, node); bool has_default = false; - String new_label = label_from_node(ss->label, label); for_array(i, ss->body->BlockStmt.stmts) { Ast *clause = ss->body->BlockStmt.stmts[i]; ast_node(cc, CaseClause, clause); if (cc->list.count == 0) { has_default = true; } - if (!check_is_terminating_list(cc->stmts, new_label) || - check_has_break_list(cc->stmts, new_label, true)) { + if (!check_is_terminating_list(cc->stmts, label) || + check_has_break_list(cc->stmts, label, true)) { return false; } } -- cgit v1.2.3 From 8b4a8e4d806a9d8328c3aedf8175c681b42cb324 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 12:49:12 +0100 Subject: Fix #3569 --- src/check_stmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index ad91838b9..719a0da15 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -751,7 +751,7 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, for (auto const &entry : scope->elements) { String name = entry.key; Entity *decl = entry.value; - if (!is_entity_exported(decl)) continue; + if (!is_entity_exported(decl, true)) continue; Entity *found = scope_insert_with_name(ctx->scope, name, decl); if (found != nullptr) { -- cgit v1.2.3 From 20f8f9012d71fa9d95ff65a0badf844c9cbe4ddb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 00:11:57 +0100 Subject: Attempt at fixing #3588 --- src/check_stmt.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 719a0da15..ee55ff0d7 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -776,6 +776,8 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, bool is_ptr = is_type_pointer(e->type); Type *t = base_type(type_deref(e->type)); if (t->kind == Type_Struct) { + wait_signal_until_available(&t->Struct.fields_wait_signal); + Scope *found = t->Struct.scope; GB_ASSERT(found != nullptr); for (auto const &entry : found->elements) { -- cgit v1.2.3 From 7734b12f9a494142c59487265e840bc1c8e605d3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 00:16:32 +0100 Subject: Fix #3587 --- src/check_stmt.cpp | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index ee55ff0d7..875503874 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -169,9 +169,16 @@ gb_internal bool check_has_break_list(Slice const &stmts, String const &l return false; } +gb_internal bool check_has_break_expr(Ast * expr, String const &label) { + if (expr && expr->viral_state_flags & ViralStateFlag_ContainsOrBreak) { + return true; + } + return false; +} + gb_internal bool check_has_break_expr_list(Slice const &exprs, String const &label) { for (Ast *expr : exprs) { - if (expr && expr->viral_state_flags & ViralStateFlag_ContainsOrBreak) { + if (check_has_break_expr(expr, label)) { return true; } } @@ -196,6 +203,13 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) return check_has_break_list(stmt->BlockStmt.stmts, label, implicit); case Ast_IfStmt: + if (stmt->IfStmt.init && check_has_break(stmt->IfStmt.init, label, implicit)) { + return true; + } + if (stmt->IfStmt.cond && check_has_break_expr(stmt->IfStmt.cond, label)) { + return true; + } + if (check_has_break(stmt->IfStmt.body, label, implicit) || (stmt->IfStmt.else_stmt != nullptr && check_has_break(stmt->IfStmt.else_stmt, label, implicit))) { return true; @@ -206,6 +220,9 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) return check_has_break_list(stmt->CaseClause.stmts, label, implicit); case Ast_SwitchStmt: + if (stmt->SwitchStmt.init && check_has_break_expr(stmt->SwitchStmt.init, label)) { + return true; + } if (label != "" && check_has_break(stmt->SwitchStmt.body, label, false)) { return true; } @@ -218,6 +235,16 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) break; case Ast_ForStmt: + if (stmt->ForStmt.init && check_has_break(stmt->ForStmt.init, label, implicit)) { + return true; + } + if (stmt->ForStmt.cond && check_has_break_expr(stmt->ForStmt.cond, label)) { + return true; + } + if (stmt->ForStmt.post && check_has_break(stmt->ForStmt.post, label, implicit)) { + return true; + } + if (label != "" && check_has_break(stmt->ForStmt.body, label, false)) { return true; } -- cgit v1.2.3 From a344bc4c0e672d9740f5777dae862723fe269973 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 16 May 2024 14:39:16 +0100 Subject: Remove the old switch/for semantics entirely and enforce `switch &x in y` --- src/check_expr.cpp | 46 +++++++++++++--------------------------------- src/check_stmt.cpp | 30 ++---------------------------- src/entity.cpp | 2 -- 3 files changed, 15 insertions(+), 63 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6a293a97e..c44232dab 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2440,32 +2440,6 @@ gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) { return o->mode != Addressing_Variable && o->mode != Addressing_SoaVariable; } -gb_internal void check_old_for_or_switch_value_usage(Ast *expr) { - Entity *e = entity_of_node(expr); - if (e != nullptr && (e->flags & EntityFlag_OldForOrSwitchValue) != 0) { - GB_ASSERT(e->kind == Entity_Variable); - - ERROR_BLOCK(); - - if ((e->flags & EntityFlag_ForValue) != 0) { - Type *parent_type = type_deref(e->Variable.for_loop_parent_type); - - error(expr, "Assuming a for-in defined value is addressable as the iterable is passed by value has been disallowed."); - - if (is_type_map(parent_type)) { - error_line("\tSuggestion: Prefer doing 'for key, &%.*s in ...'\n", LIT(e->token.string)); - } else { - error_line("\tSuggestion: Prefer doing 'for &%.*s in ...'\n", LIT(e->token.string)); - } - } else { - GB_ASSERT((e->flags & EntityFlag_SwitchValue) != 0); - - error(expr, "Assuming a switch-in defined value is addressable as the iterable is passed by value has been disallowed."); - error_line("\tSuggestion: Prefer doing 'switch &%.*s in ...'\n", LIT(e->token.string)); - } - } -} - gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) { switch (op.kind) { case Token_And: { // Pointer address @@ -2493,7 +2467,10 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * { ERROR_BLOCK(); error(op, "Cannot take the pointer address of '%s'", str); - if (e != nullptr && (e->flags & EntityFlag_ForValue) != 0) { + if (e == nullptr) { + break; + } + if ((e->flags & EntityFlag_ForValue) != 0) { Type *parent_type = type_deref(e->Variable.for_loop_parent_type); if (parent_type != nullptr && is_type_string(parent_type)) { @@ -2503,9 +2480,17 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * } else { error_line("\tSuggestion: Did you want to pass the iterable value to the for statement by pointer to get addressable semantics?\n"); } + + if (is_type_map(parent_type)) { + error_line("\t Prefer doing 'for key, &%.*s in ...'\n", LIT(e->token.string)); + } else { + error_line("\t Prefer doing 'for &%.*s in ...'\n", LIT(e->token.string)); + } } - if (e != nullptr && (e->flags & EntityFlag_SwitchValue) != 0) { + if ((e->flags & EntityFlag_SwitchValue) != 0) { error_line("\tSuggestion: Did you want to pass the value to the switch statement by pointer to get addressable semantics?\n"); + + error_line("\t Prefer doing 'switch &%.*s in ...'\n", LIT(e->token.string)); } } break; @@ -2527,11 +2512,6 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * o->type = alloc_type_pointer(o->type); } } else { - if (ast_node_expect(node, Ast_UnaryExpr)) { - ast_node(ue, UnaryExpr, node); - check_old_for_or_switch_value_usage(ue->expr); - } - o->type = alloc_type_pointer(o->type); } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 875503874..0267bdf80 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -501,7 +501,6 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O return nullptr; case Addressing_Variable: - check_old_for_or_switch_value_usage(lhs->expr); break; case Addressing_MapIndex: { @@ -523,9 +522,8 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O break; } - case Addressing_Context: { + case Addressing_Context: break; - } case Addressing_SoaVariable: break; @@ -1328,7 +1326,6 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ } } - bool is_ptr = is_type_pointer(x.type); // NOTE(bill): Check for multiple defaults Ast *first_default = nullptr; @@ -1447,15 +1444,6 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ } bool is_reference = is_addressed; - bool old_style = false; - - if (!is_reference && - is_ptr && - cc->list.count == 1 && - case_type != nullptr) { - is_reference = true; - old_style = true; - } if (cc->list.count > 1 || saw_nil) { case_type = nullptr; @@ -1477,9 +1465,6 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ if (!is_reference) { tag_var->flags |= EntityFlag_Value; } - if (old_style) { - tag_var->flags |= EntityFlag_OldForOrSwitchValue; - } add_entity(ctx, ctx->scope, lhs, tag_var); add_entity_use(ctx, lhs, tag_var); add_implicit_entity(ctx, stmt, tag_var); @@ -1618,7 +1603,6 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) auto entities = array_make(temporary_allocator(), 0, 2); bool is_map = false; bool is_bit_set = false; - bool use_by_reference_for_value = false; bool is_soa = false; bool is_reverse = rs->reverse; @@ -1679,7 +1663,6 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } } } - bool is_ptr = is_type_pointer(operand.type); Type *t = base_type(type_deref(operand.type)); switch (t->kind) { @@ -1719,32 +1702,27 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) break; case Type_EnumeratedArray: - if (is_ptr) use_by_reference_for_value = true; array_add(&vals, t->EnumeratedArray.elem); array_add(&vals, t->EnumeratedArray.index); break; case Type_Array: - if (is_ptr) use_by_reference_for_value = true; - if (!is_ptr) is_possibly_addressable = operand.mode == Addressing_Variable; + is_possibly_addressable = operand.mode == Addressing_Variable; array_add(&vals, t->Array.elem); array_add(&vals, t_int); break; case Type_DynamicArray: - if (is_ptr) use_by_reference_for_value = true; array_add(&vals, t->DynamicArray.elem); array_add(&vals, t_int); break; case Type_Slice: - if (is_ptr) use_by_reference_for_value = true; array_add(&vals, t->Slice.elem); array_add(&vals, t_int); break; case Type_Map: - if (is_ptr) use_by_reference_for_value = true; is_map = true; array_add(&vals, t->Map.key); array_add(&vals, t->Map.value); @@ -1817,7 +1795,6 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) case Type_Struct: if (t->Struct.soa_kind != StructSoa_None) { is_soa = true; - if (is_ptr) use_by_reference_for_value = true; array_add(&vals, t->Struct.soa_elem); array_add(&vals, t_int); } @@ -1894,9 +1871,6 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) char const *idx_name = is_map ? "key" : is_bit_set ? "element" : "index"; error(token, "The %s variable '%.*s' cannot be made addressable", idx_name, LIT(str)); } - } else if (i == addressable_index && use_by_reference_for_value) { - entity->flags |= EntityFlag_OldForOrSwitchValue; - entity->flags &= ~EntityFlag_Value; } if (is_soa) { if (i == 0) { diff --git a/src/entity.cpp b/src/entity.cpp index 60ca208ec..8a7417006 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -85,8 +85,6 @@ enum EntityFlag : u64 { EntityFlag_Require = 1ull<<50, EntityFlag_ByPtr = 1ull<<51, // enforce parameter is passed by pointer - EntityFlag_OldForOrSwitchValue = 1ull<<52, - EntityFlag_Overridden = 1ull<<63, }; -- cgit v1.2.3 From b2dc5cc81254b4ab194dac900ab143609ba07bac Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 16 May 2024 15:32:15 +0100 Subject: Fix error reporting for enforce new switch/for syntax --- src/check_stmt.cpp | 4 ++-- src/error.cpp | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 0267bdf80..23a97696d 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -572,7 +572,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O error(lhs->expr, "Cannot assign to '%s'", str); if (e && e->flags & EntityFlag_ForValue) { - isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token), "Suggestion:"); + isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token)); if (offset < 0) { if (is_type_map(e->type)) { error_line("\tSuggestion: Did you mean? 'for key, &%.*s in ...'\n", LIT(e->token.string)); @@ -588,7 +588,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O } } else if (e && e->flags & EntityFlag_SwitchValue) { - isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token), "Suggestion:"); + isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token)); if (offset < 0) { error_line("\tSuggestion: Did you mean? 'switch &%.*s in ...'\n", LIT(e->token.string)); } else { diff --git a/src/error.cpp b/src/error.cpp index f5123b969..da444e998 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -266,7 +266,7 @@ gb_internal void terminal_reset_colours(void) { } -gb_internal isize show_error_on_line(TokenPos const &pos, TokenPos end, char const *prefix=nullptr) { +gb_internal isize show_error_on_line(TokenPos const &pos, TokenPos end) { get_error_value()->end = end; if (!show_error_line()) { return -1; @@ -289,17 +289,13 @@ gb_internal isize show_error_on_line(TokenPos const &pos, TokenPos end, char con MAX_LINE_LENGTH_PADDED = MAX_LINE_LENGTH-MAX_TAB_WIDTH-ELLIPSIS_PADDING, }; - if (prefix) { - error_out("\t%s\n\n", prefix); - } + i32 error_length = gb_max(end.offset - pos.offset, 1); error_out("\t"); terminal_set_colours(TerminalStyle_Bold, TerminalColour_White); - i32 error_length = gb_max(end.offset - pos.offset, 1); - isize squiggle_extra = 0; if (line_len > MAX_LINE_LENGTH_PADDED) { -- cgit v1.2.3 From 46b3e7b6fa9477b7feb4389a353f92c7cc4c3d20 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 20 May 2024 13:36:32 +0100 Subject: Fix `for &v in &fixed_array` --- src/check_stmt.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 23a97696d..2083dbf11 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1663,6 +1663,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } } } + bool is_ptr = type_deref(operand.type); Type *t = base_type(type_deref(operand.type)); switch (t->kind) { @@ -1707,7 +1708,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) break; case Type_Array: - is_possibly_addressable = operand.mode == Addressing_Variable; + is_possibly_addressable = operand.mode == Addressing_Variable || is_ptr; array_add(&vals, t->Array.elem); array_add(&vals, t_int); break; -- cgit v1.2.3 From 7dc1f114b96b9e25dfc2537ab153fe655e65affc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 22 May 2024 22:22:41 +0100 Subject: Add shadow suggestion --- src/check_stmt.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 2083dbf11..866cdb5a1 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -565,7 +565,11 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O } else { error(lhs->expr, "Cannot assign to '%s' which is a procedure parameter", str); } - error_line("\tSuggestion: Did you mean to pass '%.*s' by pointer?\n", LIT(e->token.string)); + if (is_type_pointer(e->type)) { + error_line("\tSuggestion: Did you mean to shadow it? '%.*s := %.*s'?\n", LIT(e->token.string), LIT(e->token.string)); + } else { + error_line("\tSuggestion: Did you mean to pass '%.*s' by pointer?\n", LIT(e->token.string)); + } show_error_on_line(e->token.pos, token_pos_end(e->token)); } else { ERROR_BLOCK(); -- cgit v1.2.3 From 66acbb7fed88ed9132dfc8107865e0ac27ed3ac8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 30 May 2024 21:48:23 +0100 Subject: Add `@(link_suffix=)` --- src/check_decl.cpp | 13 +++++++------ src/check_stmt.cpp | 4 ++-- src/checker.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/checker.hpp | 5 ++++- src/entity.cpp | 2 ++ 5 files changed, 61 insertions(+), 9 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 1ec366ae7..44b06d712 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -724,15 +724,16 @@ gb_internal Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e) return nullptr; } -gb_internal String handle_link_name(CheckerContext *ctx, Token token, String link_name, String link_prefix) { +gb_internal String handle_link_name(CheckerContext *ctx, Token token, String link_name, String link_prefix, String link_suffix) { if (link_prefix.len > 0) { if (link_name.len > 0) { error(token, "'link_name' and 'link_prefix' cannot be used together"); } else { - isize len = link_prefix.len + token.string.len; + isize len = link_prefix.len + token.string.len + link_suffix.len; u8 *name = gb_alloc_array(permanent_allocator(), u8, len+1); gb_memmove(name, &link_prefix[0], link_prefix.len); gb_memmove(name+link_prefix.len, &token.string[0], token.string.len); + gb_memmove(name+link_prefix.len+token.string.len, link_suffix.text, link_suffix.len); name[len] = 0; link_name = make_string(name, len); @@ -862,7 +863,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } TypeProc *pt = &proc_type->Proc; - AttributeContext ac = make_attribute_context(e->Procedure.link_prefix); + AttributeContext ac = make_attribute_context(e->Procedure.link_prefix, e->Procedure.link_suffix); if (d != nullptr) { check_decl_attributes(ctx, d->attributes, proc_decl_attribute, &ac); @@ -1015,7 +1016,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->deprecated_message = ac.deprecated_message; e->warning_message = ac.warning_message; - ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); + ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix,ac.link_suffix); if (ac.has_disabled_proc) { if (ac.disabled_proc) { e->flags |= EntityFlag_Disabled; @@ -1223,7 +1224,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast } e->flags |= EntityFlag_Visited; - AttributeContext ac = make_attribute_context(e->Variable.link_prefix); + AttributeContext ac = make_attribute_context(e->Variable.link_prefix, e->Variable.link_suffix); ac.init_expr_list_count = init_expr != nullptr ? 1 : 0; DeclInfo *decl = decl_info_of_entity(e); @@ -1244,7 +1245,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast if (ac.is_static) { error(e->token, "@(static) is not supported for global variables, nor required"); } - ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); + ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix, ac.link_suffix); if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) { e->Variable.thread_local_model.len = 0; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 866cdb5a1..2c37bced0 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2020,7 +2020,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f // TODO NOTE(bill): This technically checks things multple times - AttributeContext ac = make_attribute_context(ctx->foreign_context.link_prefix); + AttributeContext ac = make_attribute_context(ctx->foreign_context.link_prefix, ctx->foreign_context.link_suffix); check_decl_attributes(ctx, vd->attributes, var_decl_attribute, &ac); for (isize i = 0; i < entity_count; i++) { @@ -2037,7 +2037,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f e->type = init_type; e->state = EntityState_Resolved; } - ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); + ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix, ac.link_suffix); if (ac.link_name.len > 0) { e->Variable.link_name = ac.link_name; diff --git a/src/checker.cpp b/src/checker.cpp index 1ded6ea6e..ec58b9d8e 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3127,6 +3127,18 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "link_suffix") { + if (ev.kind == ExactValue_String) { + String link_suffix = ev.value_string; + if (!is_foreign_name_valid(link_suffix)) { + error(elem, "Invalid link suffix: '%.*s'", LIT(link_suffix)); + } else { + c->foreign_context.link_suffix = link_suffix; + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } else if (name == "private") { EntityVisiblityKind kind = EntityVisiblity_PrivateToPackage; if (ev.kind == ExactValue_Invalid) { @@ -3421,6 +3433,18 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "link_suffix") { + ExactValue ev = check_decl_attribute_value(c, value); + + if (ev.kind == ExactValue_String) { + ac->link_suffix = ev.value_string; + if (!is_foreign_name_valid(ac->link_suffix)) { + error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix)); + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } else if (name == "deprecated") { ExactValue ev = check_decl_attribute_value(c, value); @@ -3702,6 +3726,17 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "link_suffix") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_String) { + ac->link_suffix = ev.value_string; + if (!is_foreign_name_valid(ac->link_suffix)) { + error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix)); + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } else if (name == "link_section") { ExactValue ev = check_decl_attribute_value(c, value); if (ev.kind == ExactValue_String) { @@ -3733,6 +3768,7 @@ gb_internal DECL_ATTRIBUTE_PROC(const_decl_attribute) { name == "linkage" || name == "link_name" || name == "link_prefix" || + name == "link_suffix" || false) { error(elem, "@(%.*s) is not supported for compile time constant value declarations", LIT(name)); return true; @@ -3775,8 +3811,10 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array const &at if (attributes.count == 0) return; String original_link_prefix = {}; + String original_link_suffix = {}; if (ac) { original_link_prefix = ac->link_prefix; + original_link_suffix = ac->link_suffix; } StringSet set = {}; @@ -3851,6 +3889,12 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array const &at ac->link_prefix.len = 0; } } + if (ac->link_suffix.text == original_link_suffix.text) { + if (ac->link_name.len > 0) { + ac->link_suffix.text = nullptr; + ac->link_suffix.len = 0; + } + } } } @@ -4145,6 +4189,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { e->Variable.foreign_library_ident = fl; e->Variable.link_prefix = c->foreign_context.link_prefix; + e->Variable.link_suffix = c->foreign_context.link_suffix; } Ast *init_expr = value; @@ -4219,6 +4264,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { } } e->Procedure.link_prefix = c->foreign_context.link_prefix; + e->Procedure.link_suffix = c->foreign_context.link_suffix; GB_ASSERT(cc != ProcCC_Invalid); pl->type->ProcType.calling_convention = cc; diff --git a/src/checker.hpp b/src/checker.hpp index 6ae7b90e2..539b72b2d 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -112,6 +112,7 @@ enum InstrumentationFlag : i32 { struct AttributeContext { String link_name; String link_prefix; + String link_suffix; String link_section; String linkage; isize init_expr_list_count; @@ -146,9 +147,10 @@ struct AttributeContext { String enable_target_feature; // will be enabled for the procedure only }; -gb_internal gb_inline AttributeContext make_attribute_context(String link_prefix) { +gb_internal gb_inline AttributeContext make_attribute_context(String link_prefix, String link_suffix) { AttributeContext ac = {}; ac.link_prefix = link_prefix; + ac.link_suffix = link_suffix; return ac; } @@ -302,6 +304,7 @@ struct ForeignContext { Ast * curr_library; ProcCallingConvention default_cc; String link_prefix; + String link_suffix; EntityVisiblityKind visibility_kind; }; diff --git a/src/entity.cpp b/src/entity.cpp index 1461b96d7..e4fc66dac 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -223,6 +223,7 @@ struct Entity { Ast * foreign_library_ident; String link_name; String link_prefix; + String link_suffix; String link_section; CommentGroup *docs; CommentGroup *comment; @@ -243,6 +244,7 @@ struct Entity { Ast * foreign_library_ident; String link_name; String link_prefix; + String link_suffix; DeferredProcedure deferred_procedure; struct GenProcsData *gen_procs; -- cgit v1.2.3 From 9ef43fc782159893b7af139f9d9be3aec3108ecd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 Jun 2024 15:16:34 +0100 Subject: Add `@(rodata)` --- src/check_decl.cpp | 6 ++++++ src/check_stmt.cpp | 6 ++++++ src/checker.cpp | 6 ++++++ src/checker.hpp | 1 + src/entity.cpp | 1 + src/llvm_backend.cpp | 20 ++++++++++++++++++-- 6 files changed, 38 insertions(+), 2 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index f2afce59c..43947836b 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1264,6 +1264,9 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast if (ac.is_static) { error(e->token, "@(static) is not supported for global variables, nor required"); } + if (ac.rodata) { + e->Variable.is_rodata = true; + } ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix, ac.link_suffix); if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) { @@ -1350,6 +1353,9 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast Operand o = {}; check_expr_with_type_hint(ctx, &o, init_expr, e->type); check_init_variable(ctx, e, &o, str_lit("variable declaration")); + if (e->Variable.is_rodata && o.mode != Addressing_Constant) { + error(o.expr, "Variables declared with @(rodata) must have constant initialization"); + } check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed"); } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 2c37bced0..fc443a7b5 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -501,6 +501,9 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O return nullptr; case Addressing_Variable: + if (e && e->kind == Entity_Variable && e->Variable.is_rodata) { + error(lhs->expr, "Assignment to variable '%.*s' marked as @(rodata) is not allowed", LIT(e->token.string)); + } break; case Addressing_MapIndex: { @@ -2055,6 +2058,9 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f } } } + if (ac.rodata) { + error(e->token, "Only global variables can have @(rodata) applied"); + } if (ac.thread_local_model != "") { String name = e->token.string; if (name == "_") { diff --git a/src/checker.cpp b/src/checker.cpp index 97e685d33..8a58bb425 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3628,6 +3628,12 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { } ac->is_static = true; return true; + } else if (name == "rodata") { + if (value != nullptr) { + error(elem, "'rodata' does not have any parameters"); + } + ac->rodata = true; + return true; } else if (name == "thread_local") { ExactValue ev = check_decl_attribute_value(c, value); if (ac->init_expr_list_count > 0) { diff --git a/src/checker.hpp b/src/checker.hpp index e793540e3..2ac4c8e7a 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -133,6 +133,7 @@ struct AttributeContext { bool entry_point_only : 1; bool instrumentation_enter : 1; bool instrumentation_exit : 1; + bool rodata : 1; u32 optimization_mode; // ProcedureOptimizationMode i64 foreign_import_priority_index; String extra_linker_flags; diff --git a/src/entity.cpp b/src/entity.cpp index e4fc66dac..7f484e308 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -230,6 +230,7 @@ struct Entity { bool is_foreign; bool is_export; bool is_global; + bool is_rodata; } Variable; struct { Type * type_parameter_specialization; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 03c17a8bb..5dc6d94d5 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1160,6 +1160,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc if (is_type_untyped_nil(init.type)) { LLVMSetInitializer(var.var.value, LLVMConstNull(global_type)); var.is_initialized = true; + + if (e->Variable.is_rodata) { + LLVMSetGlobalConstant(var.var.value, true); + } continue; } GB_PANIC("Invalid init value, got %s", expr_to_string(init_expr)); @@ -1174,6 +1178,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc } LLVMSetInitializer(var.var.value, init.value); var.is_initialized = true; + + if (e->Variable.is_rodata) { + LLVMSetGlobalConstant(var.var.value, true); + } continue; } } else { @@ -1206,8 +1214,9 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc var.is_initialized = true; } + + } - CheckerInfo *info = main_module->gen->info; for (Entity *e : info->init_procedures) { @@ -3210,14 +3219,21 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { lbValue init = lb_const_value(m, tav.type, v); LLVMSetInitializer(g.value, init.value); var.is_initialized = true; + if (e->kind == Entity_Variable && e->Variable.is_rodata) { + LLVMSetGlobalConstant(g.value, true); + } } } } if (!var.is_initialized && is_type_untyped_nil(tav.type)) { var.is_initialized = true; + if (e->kind == Entity_Variable && e->Variable.is_rodata) { + LLVMSetGlobalConstant(g.value, true); + } } + } else if (e->kind == Entity_Variable && e->Variable.is_rodata) { + LLVMSetGlobalConstant(g.value, true); } - array_add(&global_variables, var); lb_add_entity(m, e, g); -- cgit v1.2.3 From bea47db4953559dbbcdce1da5dbaf38d0bb8d943 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 Jun 2024 15:20:47 +0100 Subject: Allow `@(rodata)` on `@(static)` variables --- src/check_stmt.cpp | 6 +++++- src/llvm_backend_stmt.cpp | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index fc443a7b5..a1698bbfe 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2059,7 +2059,11 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f } } if (ac.rodata) { - error(e->token, "Only global variables can have @(rodata) applied"); + if (ac.is_static) { + e->Variable.is_rodata = true; + } else { + error(e->token, "Only global or @(static) variables can have @(rodata) applied"); + } } if (ac.thread_local_model != "") { String name = e->token.string; diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index b18db4e45..9f28e45e0 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -1850,7 +1850,9 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) { LLVMSetInitializer(global, LLVMConstNull(lb_type(p->module, e->type))); if (value.value != nullptr) { LLVMSetInitializer(global, value.value); - } else { + } + if (e->Variable.is_rodata) { + LLVMSetGlobalConstant(global, true); } if (e->Variable.thread_local_model != "") { LLVMSetThreadLocal(global, true); -- cgit v1.2.3 From 7e994b6d216b091b90c47eba1b834bc7c690535c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 8 Jun 2024 15:42:19 +0100 Subject: Remove empty line preventing a suggestion from happening --- src/check_stmt.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a1698bbfe..65bac36be 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1255,8 +1255,6 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags error_line("\t%.*s\n", LIT(f->token.string)); } } - error_line("\n"); - error_line("\tSuggestion: Was '#partial switch' wanted?\n"); } } -- cgit v1.2.3 From be0774acc8a67ac2f9e003764284bbda21d4ebcd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 8 Jun 2024 16:07:28 +0100 Subject: Add error message on return a constant slice value from a procedure --- src/check_stmt.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 65bac36be..c37c58cd6 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2501,6 +2501,10 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { unsafe_return_error(o, "the address of an indexed variable", f->type); } } + } else if (o.mode == Addressing_Constant && is_type_slice(o.type)) { + ERROR_BLOCK(); + unsafe_return_error(o, "a compound literal of a slice"); + error_line("\tNote: A constant slice value will use the memory of the current stack frame\n"); } } -- cgit v1.2.3 From fa3cae2bb04db76f52f1b2288a9c858f20332b8a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 10 Jun 2024 15:02:34 +0100 Subject: Add `intrinsics.procedure_of` ```odin foo :: proc(x: $T) { fmt.println(x) } bar :: intrinsics.procedure_of(foo(int(123))) // parameters are never ran at compile time, similar to `size_of` bar(333) // prints 333 ``` --- base/intrinsics/intrinsics.odin | 4 ++++ src/check_builtin.cpp | 46 +++++++++++++++++++++++++++++++++++++++++ src/check_decl.cpp | 17 ++++++++++----- src/check_expr.cpp | 1 + src/check_stmt.cpp | 10 ++++++++- src/checker.cpp | 4 ++++ src/checker.hpp | 6 ++++++ src/checker_builtin_procs.hpp | 4 ++++ src/parser.hpp | 1 + 9 files changed, 87 insertions(+), 6 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index 8873f3bbc..4f6fa2713 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -295,6 +295,10 @@ simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T --- // if all listed features are supported. has_target_feature :: proc($test: $T) -> bool where type_is_string(T) || type_is_proc(T) --- + +// Returns the value of the procedure where `x` must be a call expression +procedure_of :: proc(x: $T) -> T where type_is_proc(T) --- + // WASM targets only wasm_memory_grow :: proc(index, delta: uintptr) -> int --- wasm_memory_size :: proc(index: uintptr) -> int --- diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 98c695a2c..3aee804df 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1843,6 +1843,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_objc_register_class: case BuiltinProc_atomic_type_is_lock_free: case BuiltinProc_has_target_feature: + case BuiltinProc_procedure_of: // NOTE(bill): The first arg may be a Type, this will be checked case by case break; @@ -6157,6 +6158,51 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; } + case BuiltinProc_procedure_of: + { + Ast *call_expr = unparen_expr(ce->args[0]); + Operand op = {}; + check_expr_base(c, &op, ce->args[0], nullptr); + if (op.mode != Addressing_Value && !(call_expr && call_expr->kind == Ast_CallExpr)) { + error(ce->args[0], "Expected a call expression for '%.*s'", LIT(builtin_name)); + return false; + } + + Ast *proc = call_expr->CallExpr.proc; + Entity *e = entity_of_node(proc); + + if (e == nullptr) { + error(ce->args[0], "Invalid procedure value, expected a regular/specialized procedure"); + return false; + } + + TypeAndValue tav = proc->tav; + + + operand->type = e->type; + operand->mode = Addressing_Value; + operand->value = tav.value; + operand->builtin_id = BuiltinProc_Invalid; + operand->proc_group = nullptr; + + if (tav.mode == Addressing_Builtin) { + operand->mode = tav.mode; + operand->builtin_id = cast(BuiltinProcId)e->Builtin.id; + break; + } + + if (!is_type_proc(e->type)) { + gbString s = type_to_string(e->type); + error(ce->args[0], "Expected a procedure value, got '%s'", s); + gb_string_free(s); + return false; + } + + + ce->entity_procedure_of = e; + break; + } + case BuiltinProc_constant_utf16_cstring: { String value = {}; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 43947836b..13b14149a 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -88,11 +88,14 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o e->type = t_invalid; return nullptr; } else if (is_type_polymorphic(t)) { - gbString str = type_to_string(t); - defer (gb_string_free(str)); - error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name)); - e->type = t_invalid; - return nullptr; + Entity *e = entity_of_node(operand->expr); + if (e->state.load() != EntityState_Resolved) { + gbString str = type_to_string(t); + defer (gb_string_free(str)); + error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name)); + e->type = t_invalid; + return nullptr; + } } else if (is_type_empty_union(t)) { gbString str = type_to_string(t); defer (gb_string_free(str)); @@ -479,6 +482,9 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr entity = check_selector(ctx, &operand, init, e->type); } else { check_expr_or_type(ctx, &operand, init, e->type); + if (init->kind == Ast_CallExpr) { + entity = init->CallExpr.entity_procedure_of; + } } switch (operand.mode) { @@ -526,6 +532,7 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr return; } + if (entity != nullptr) { if (e->type != nullptr) { Operand x = {}; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 641f70566..01cba881e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -578,6 +578,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E d->defer_use_checked = false; Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags); + entity->state.store(EntityState_Resolved); entity->identifier = ident; add_entity_and_decl_info(&nctx, ident, entity, d); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index c37c58cd6..f2e3b0242 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2224,8 +2224,16 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) { } if (do_require) { gbString expr_str = expr_to_string(ce->proc); + defer (gb_string_free(expr_str)); + if (builtin_id) { + String real_name = builtin_procs[builtin_id].name; + if (real_name != make_string(cast(u8 const *)expr_str, gb_string_length(expr_str))) { + error(node, "'%s' ('%.*s.%.*s') requires that its results must be handled", expr_str, + LIT(builtin_proc_pkg_name[builtin_procs[builtin_id].pkg]), LIT(real_name)); + return; + } + } error(node, "'%s' requires that its results must be handled", expr_str); - gb_string_free(expr_str); } return; } else if (expr && expr->kind == Ast_SelectorCallExpr) { diff --git a/src/checker.cpp b/src/checker.cpp index 8f0cc1cd1..852fb89bb 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1479,6 +1479,10 @@ gb_internal Entity *entity_of_node(Ast *expr) { case_ast_node(cc, CaseClause, expr); return cc->implicit_entity; case_end; + + case_ast_node(ce, CallExpr, expr); + return ce->entity_procedure_of; + case_end; } return nullptr; } diff --git a/src/checker.hpp b/src/checker.hpp index 2ac4c8e7a..492a64fb6 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -51,6 +51,12 @@ enum StmtFlag { enum BuiltinProcPkg { BuiltinProcPkg_builtin, BuiltinProcPkg_intrinsics, + BuiltinProcPkg_COUNT +}; + +String builtin_proc_pkg_name[BuiltinProcPkg_COUNT] = { + str_lit("builtin"), + str_lit("intrinsics"), }; struct BuiltinProc { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 5f98bb7b3..35acad42f 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -299,6 +299,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc__type_end, + BuiltinProc_procedure_of, + BuiltinProc___entry_point, BuiltinProc_objc_send, @@ -614,6 +616,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("procedure_of"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, diff --git a/src/parser.hpp b/src/parser.hpp index 0e411d9ac..02f2af28d 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -458,6 +458,7 @@ AST_KIND(_ExprBegin, "", bool) \ bool optional_ok_one; \ bool was_selector; \ AstSplitArgs *split_args; \ + Entity *entity_procedure_of; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(EnumFieldValue, "enum field value", struct { \ -- cgit v1.2.3 From e8517e2694524a94ee88cf925a4a667fb6575f90 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 29 Jun 2024 18:56:45 +0100 Subject: `-strict-style`: enforce `case` to be in the same column as `switch` --- src/check_stmt.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index f2e3b0242..edf2fae39 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1258,6 +1258,20 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags error_line("\tSuggestion: Was '#partial switch' wanted?\n"); } } + + if (build_context.strict_style) { + Token stok = ss->token; + for_array(i, bs->stmts) { + Ast *stmt = bs->stmts[i]; + if (stmt->kind != Ast_CaseClause) { + continue; + } + Token ctok = stmt->CaseClause.token; + if (ctok.pos.column > stok.pos.column) { + error(ctok, "With '-strict-style', 'case' statements must share the same column as the 'switch' token"); + } + } + } } -- cgit v1.2.3 From c54e3d3c4f3d32761c17e3528714598717ecebbd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Jul 2024 13:45:21 +0100 Subject: Improve warning handling for possible `default:` typo --- src/check_stmt.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index edf2fae39..f4d3bd6b8 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2031,6 +2031,12 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f gb_string_free(str); init_type = t_invalid; } + if (init_type == t_invalid && entity_count == 1 && (mod_flags & (Stmt_BreakAllowed|Stmt_FallthroughAllowed))) { + Entity *e = entities[0]; + if (e != nullptr && e->token.string == "default") { + warning(e->token, "Did you mean 'case:'?"); + } + } } -- cgit v1.2.3 From b38237e8f0872fd8979a729553338e01d04dc3e8 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 14 Jul 2024 14:59:00 -0400 Subject: Fix compiler crash when switching on no value --- src/check_stmt.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index f4d3bd6b8..74397828d 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1060,6 +1060,9 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags if (ss->tag != nullptr) { check_expr(ctx, &x, ss->tag); check_assignment(ctx, &x, nullptr, str_lit("switch expression")); + if (x.type == nullptr) { + return; + } } else { x.mode = Addressing_Constant; x.type = t_bool; -- cgit v1.2.3