diff options
| author | gingerBill <gingerBill@users.noreply.github.com> | 2023-06-23 14:33:01 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-06-23 14:33:01 +0100 |
| commit | 26a5614572afd39fc35fc32b47d5f7e5e9771e56 (patch) | |
| tree | ed94ed8589d3d0e32a5a8e494319bfcf292d27cd /src/check_stmt.cpp | |
| parent | f36e19e86fe88198fc1d17426afea577920efbf8 (diff) | |
| parent | 19ea0906332e6185cd0eefe873179b9058ccd725 (diff) | |
Merge branch 'master' into skytrias-vendor-additions
Diffstat (limited to 'src/check_stmt.cpp')
| -rw-r--r-- | src/check_stmt.cpp | 1648 |
1 files changed, 858 insertions, 790 deletions
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 502eed57e..cf6f998e5 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1,4 +1,4 @@ -bool is_diverging_expr(Ast *expr) { +gb_internal bool is_diverging_expr(Ast *expr) { expr = unparen_expr(expr); if (expr->kind != Ast_CallExpr) { return false; @@ -23,14 +23,14 @@ bool is_diverging_expr(Ast *expr) { t = base_type(t); return t != nullptr && t->kind == Type_Proc && t->Proc.diverging; } -bool is_diverging_stmt(Ast *stmt) { +gb_internal bool is_diverging_stmt(Ast *stmt) { if (stmt->kind != Ast_ExprStmt) { return false; } return is_diverging_expr(stmt->ExprStmt.expr); } -bool contains_deferred_call(Ast *node) { +gb_internal bool contains_deferred_call(Ast *node) { if (node->viral_state_flags & ViralStateFlag_ContainsDeferredProcedure) { return true; } @@ -61,7 +61,7 @@ bool contains_deferred_call(Ast *node) { return false; } -void check_stmt_list(CheckerContext *ctx, Slice<Ast *> const &stmts, u32 flags) { +gb_internal void check_stmt_list(CheckerContext *ctx, Slice<Ast *> const &stmts, u32 flags) { if (stmts.count == 0) { return; } @@ -137,7 +137,7 @@ void check_stmt_list(CheckerContext *ctx, Slice<Ast *> const &stmts, u32 flags) } } -bool check_is_terminating_list(Slice<Ast *> const &stmts, String const &label) { +gb_internal bool check_is_terminating_list(Slice<Ast *> const &stmts, String const &label) { // Iterate backwards for (isize n = stmts.count-1; n >= 0; n--) { Ast *stmt = stmts[n]; @@ -155,7 +155,7 @@ bool check_is_terminating_list(Slice<Ast *> const &stmts, String const &label) { return false; } -bool check_has_break_list(Slice<Ast *> const &stmts, String const &label, bool implicit) { +gb_internal bool check_has_break_list(Slice<Ast *> const &stmts, String const &label, bool implicit) { for_array(i, stmts) { Ast *stmt = stmts[i]; if (check_has_break(stmt, label, implicit)) { @@ -166,7 +166,7 @@ bool check_has_break_list(Slice<Ast *> const &stmts, String const &label, bool i } -bool check_has_break(Ast *stmt, String const &label, bool implicit) { +gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) { switch (stmt->kind) { case Ast_BranchStmt: if (stmt->BranchStmt.token.kind == Token_break) { @@ -225,7 +225,7 @@ bool check_has_break(Ast *stmt, String const &label, bool implicit) { // NOTE(bill): The last expression has to be a 'return' statement // TODO(bill): This is a mild hack and should be probably handled properly -bool check_is_terminating(Ast *node, String const &label) { +gb_internal bool check_is_terminating(Ast *node, String const &label) { switch (node->kind) { case_ast_node(rs, ReturnStmt, node); return true; @@ -239,6 +239,10 @@ bool check_is_terminating(Ast *node, String const &label) { return check_is_terminating(unparen_expr(es->expr), label); case_end; + case_ast_node(bs, BranchStmt, node); + return bs->token.kind == Token_fallthrough; + case_end; + case_ast_node(is, IfStmt, node); if (is->else_stmt != nullptr) { if (check_is_terminating(is->body, label) && @@ -326,8 +330,7 @@ bool check_is_terminating(Ast *node, String const &label) { - -Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs) { +gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs) { if (rhs->mode == Addressing_Invalid) { return nullptr; } @@ -339,6 +342,8 @@ Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs) Ast *node = unparen_expr(lhs->expr); + check_no_copy_assignment(*rhs, str_lit("assignment")); + // NOTE(bill): Ignore assignments to '_' if (is_blank_ident(node)) { check_assignment(ctx, rhs, nullptr, str_lit("assignment to '_' identifier")); @@ -400,6 +405,13 @@ Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs) } Type *assignment_type = lhs->type; + + if (rhs->mode == Addressing_Type && is_type_polymorphic(rhs->type)) { + gbString t = type_to_string(rhs->type); + error(rhs->expr, "Invalid use of a non-specialized polymorphic type '%s'", t); + gb_string_free(t); + } + switch (lhs->mode) { case Addressing_Invalid: return nullptr; @@ -477,8 +489,8 @@ Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs) } -void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags); -void check_stmt(CheckerContext *ctx, Ast *node, u32 flags) { +gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags); +gb_internal void check_stmt(CheckerContext *ctx, Ast *node, u32 flags) { u32 prev_state_flags = ctx->state_flags; if (node->state_flags != 0) { @@ -510,7 +522,7 @@ void check_stmt(CheckerContext *ctx, Ast *node, u32 flags) { } -void check_when_stmt(CheckerContext *ctx, AstWhenStmt *ws, u32 flags) { +gb_internal void check_when_stmt(CheckerContext *ctx, AstWhenStmt *ws, u32 flags) { Operand operand = {Addressing_Invalid}; check_expr(ctx, &operand, ws->cond); if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) { @@ -539,7 +551,7 @@ void check_when_stmt(CheckerContext *ctx, AstWhenStmt *ws, u32 flags) { } } -void check_label(CheckerContext *ctx, Ast *label, Ast *parent) { +gb_internal void check_label(CheckerContext *ctx, Ast *label, Ast *parent) { if (label == nullptr) { return; } @@ -582,7 +594,7 @@ void check_label(CheckerContext *ctx, Ast *label, Ast *parent) { } // Returns 'true' for 'continue', 'false' for 'return' -bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, bool is_selector, Entity *e) { +gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, bool is_selector, Entity *e) { if (e == nullptr) { if (is_blank_ident(expr)) { error(us->token, "'using' in a statement is not allowed with the blank identifier '_'"); @@ -622,9 +634,12 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b case Entity_ImportName: { Scope *scope = e->ImportName.scope; - MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) { - String name = scope->elements.entries[i].key.string; - Entity *decl = scope->elements.entries[i].value; + rw_mutex_lock(&scope->mutex); + defer (rw_mutex_unlock(&scope->mutex)); + + for (auto const &entry : scope->elements) { + String name = entry.key; + Entity *decl = entry.value; if (!is_entity_exported(decl)) continue; Entity *found = scope_insert_with_name(ctx->scope, name, decl); @@ -652,8 +667,8 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b if (t->kind == Type_Struct) { Scope *found = t->Struct.scope; GB_ASSERT(found != nullptr); - for_array(i, found->elements.entries) { - Entity *f = found->elements.entries[i].value; + for (auto const &entry : found->elements) { + Entity *f = entry.value; if (f->kind == Entity_Variable) { Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, expr); if (!is_ptr && e->flags & EntityFlag_Value) uvar->flags |= EntityFlag_Value; @@ -704,7 +719,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b return true; } -void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { +gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { ast_node(irs, UnrollRangeStmt, node); check_open_scope(ctx, node); @@ -722,7 +737,7 @@ void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { Operand x = {}; Operand y = {}; - bool ok = check_range(ctx, expr, &x, &y, &inline_for_depth); + bool ok = check_range(ctx, expr, true, &x, &y, &inline_for_depth); if (!ok) { goto skip_expr; } @@ -863,7 +878,7 @@ void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { check_close_scope(ctx); } -void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { +gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { ast_node(ss, SwitchStmt, node); Operand x = {}; @@ -929,19 +944,17 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { } SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue - map_init(&seen, heap_allocator()); defer (map_destroy(&seen)); - for_array(stmt_index, bs->stmts) { - Ast *stmt = bs->stmts[stmt_index]; + for (Ast *stmt : bs->stmts) { if (stmt->kind != Ast_CaseClause) { // NOTE(bill): error handled by above multiple default checker continue; } ast_node(cc, CaseClause, stmt); - for_array(j, cc->list) { - Ast *expr = unparen_expr(cc->list[j]); + for (Ast *expr : cc->list) { + expr = unparen_expr(expr); if (is_ast_range(expr)) { ast_node(be, BinaryExpr, expr); @@ -977,19 +990,19 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { Operand a = lhs; Operand b = rhs; - check_comparison(ctx, &a, &x, Token_LtEq); + check_comparison(ctx, expr, &a, &x, Token_LtEq); if (a.mode == Addressing_Invalid) { continue; } - check_comparison(ctx, &b, &x, upper_op); + check_comparison(ctx, expr, &b, &x, upper_op); if (b.mode == Addressing_Invalid) { continue; } Operand a1 = lhs; Operand b1 = rhs; - check_comparison(ctx, &a1, &b1, Token_LtEq); + check_comparison(ctx, expr, &a1, &b1, Token_LtEq); add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs); @@ -1028,7 +1041,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { // NOTE(bill): the ordering here matters Operand z = y; - check_comparison(ctx, &z, &x, Token_CmpEq); + check_comparison(ctx, expr, &z, &x, Token_CmpEq); if (z.mode == Addressing_Invalid) { continue; } @@ -1047,14 +1060,15 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { } if (!is_partial && is_type_enum(x.type)) { + TEMPORARY_ALLOCATOR_GUARD(); + Type *et = base_type(x.type); GB_ASSERT(is_type_enum(et)); auto fields = et->Enum.fields; auto unhandled = array_make<Entity *>(temporary_allocator(), 0, fields.count); - for_array(i, fields) { - Entity *f = fields[i]; + for (Entity *f : fields) { if (f->kind != Entity_Constant) { continue; } @@ -1073,8 +1087,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { error_no_newline(node, "Unhandled switch case: %.*s", LIT(unhandled[0]->token.string)); } else { error(node, "Unhandled switch cases:"); - for_array(i, unhandled) { - Entity *f = unhandled[i]; + for (Entity *f : unhandled) { error_line("\t%.*s\n", LIT(f->token.string)); } } @@ -1092,7 +1105,7 @@ enum TypeSwitchKind { TypeSwitch_Any, }; -TypeSwitchKind check_valid_type_switch_type(Type *type) { +gb_internal TypeSwitchKind check_valid_type_switch_type(Type *type) { type = type_deref(type); if (is_type_union(type)) { return TypeSwitch_Union; @@ -1103,7 +1116,7 @@ TypeSwitchKind check_valid_type_switch_type(Type *type) { return TypeSwitch_Invalid; } -void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { +gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { ast_node(ss, TypeSwitchStmt, node); Operand x = {}; @@ -1155,8 +1168,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { // NOTE(bill): Check for multiple defaults Ast *first_default = nullptr; ast_node(bs, BlockStmt, ss->body); - for_array(i, bs->stmts) { - Ast *stmt = bs->stmts[i]; + for (Ast *stmt : bs->stmts) { Ast *default_stmt = nullptr; if (stmt->kind == Ast_CaseClause) { ast_node(cc, CaseClause, stmt); @@ -1184,27 +1196,46 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { return; } + + Ast *nil_seen = nullptr; PtrSet<Type *> seen = {}; - ptr_set_init(&seen, heap_allocator()); defer (ptr_set_destroy(&seen)); - for_array(i, bs->stmts) { - Ast *stmt = bs->stmts[i]; + for (Ast *stmt : bs->stmts) { if (stmt->kind != Ast_CaseClause) { // NOTE(bill): error handled by above multiple default checker continue; } ast_node(cc, CaseClause, stmt); + bool saw_nil = false; // TODO(bill): Make robust Type *bt = base_type(type_deref(x.type)); Type *case_type = nullptr; - for_array(type_index, cc->list) { - Ast *type_expr = cc->list[type_index]; + for (Ast *type_expr : cc->list) { if (type_expr != nullptr) { // Otherwise it's a default expression Operand y = {}; check_expr_or_type(ctx, &y, type_expr); + + if (is_operand_nil(y)) { + if (!type_has_nil(type_deref(x.type))) { + error(type_expr, "'nil' case is not allowed for the type '%s'", type_to_string(type_deref(x.type))); + continue; + } + saw_nil = true; + + if (nil_seen) { + ERROR_BLOCK(); + error(type_expr, "'nil' case has already been handled previously"); + error_line("\t 'nil' was already previously seen at %s", token_pos_to_string(ast_token(nil_seen).pos)); + } else { + nil_seen = type_expr; + } + case_type = y.type; + continue; + } + if (y.mode != Addressing_Type) { gbString str = expr_to_string(type_expr); error(type_expr, "Expected a type as a case, got %s", str); @@ -1215,8 +1246,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { if (switch_kind == TypeSwitch_Union) { GB_ASSERT(is_type_union(bt)); bool tag_type_found = false; - for_array(j, bt->Union.variants) { - Type *vt = bt->Union.variants[j]; + for (Type *vt : bt->Union.variants) { if (are_types_identical(vt, y.type)) { tag_type_found = true; break; @@ -1237,7 +1267,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { GB_PANIC("Unknown type to type switch statement"); } - if (type_ptr_set_exists(&seen, y.type)) { + if (type_ptr_set_update(&seen, y.type)) { TokenPos pos = cc->token.pos; gbString expr_str = expr_to_string(y.expr); error(y.expr, @@ -1248,7 +1278,6 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { gb_string_free(expr_str); break; } - ptr_set_add(&seen, y.type); } } @@ -1260,13 +1289,17 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { is_reference = true; } - if (cc->list.count > 1) { + if (cc->list.count > 1 || saw_nil) { case_type = nullptr; } if (case_type == nullptr) { case_type = x.type; } - add_type_info_type(ctx, case_type); + if (switch_kind == TypeSwitch_Any) { + if (!is_type_untyped(case_type)) { + add_type_info_type(ctx, case_type); + } + } check_open_scope(ctx, stmt); { @@ -1285,14 +1318,15 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { } if (!is_partial && is_type_union(type_deref(x.type))) { + TEMPORARY_ALLOCATOR_GUARD(); + Type *ut = base_type(type_deref(x.type)); GB_ASSERT(is_type_union(ut)); auto variants = ut->Union.variants; auto unhandled = array_make<Type *>(temporary_allocator(), 0, variants.count); - for_array(i, variants) { - Type *t = variants[i]; + for (Type *t : variants) { if (!type_ptr_set_exists(&seen, t)) { array_add(&unhandled, t); } @@ -1305,8 +1339,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { gb_string_free(s); } else { error_no_newline(node, "Unhandled switch cases:\n"); - for_array(i, unhandled) { - Type *t = unhandled[i]; + for (Type *t : unhandled) { gbString s = type_to_string(t); error_line("\t%s\n", s); gb_string_free(s); @@ -1318,14 +1351,14 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { } } -void check_block_stmt_for_errors(CheckerContext *ctx, Ast *body) { +gb_internal void check_block_stmt_for_errors(CheckerContext *ctx, Ast *body) { if (body->kind != Ast_BlockStmt) { return; } ast_node(bs, BlockStmt, body); // NOTE(bill, 2020-09-23): This logic is prevent common erros with block statements // e.g. if cond { x := 123; } // this is an error - if (bs->scope != nullptr && bs->scope->elements.entries.count > 0) { + if (bs->scope != nullptr && bs->scope->elements.count > 0) { if (bs->scope->parent->node != nullptr) { switch (bs->scope->parent->node->kind) { case Ast_IfStmt: @@ -1343,8 +1376,7 @@ void check_block_stmt_for_errors(CheckerContext *ctx, Ast *body) { isize stmt_count = 0; Ast *the_stmt = nullptr; - for_array(i, bs->stmts) { - Ast *stmt = bs->stmts[i]; + for (Ast *stmt : bs->stmts) { GB_ASSERT(stmt != nullptr); switch (stmt->kind) { case_ast_node(es, EmptyStmt, stmt); @@ -1362,8 +1394,7 @@ void check_block_stmt_for_errors(CheckerContext *ctx, Ast *body) { if (stmt_count == 1) { if (the_stmt->kind == Ast_ValueDecl) { - for_array(i, the_stmt->ValueDecl.names) { - Ast *name = the_stmt->ValueDecl.names[i]; + for (Ast *name : the_stmt->ValueDecl.names) { if (name->kind != Ast_Ident) { continue; } @@ -1377,10 +1408,10 @@ void check_block_stmt_for_errors(CheckerContext *ctx, Ast *body) { } } -bool all_operands_valid(Array<Operand> const &operands) { +gb_internal bool all_operands_valid(Array<Operand> const &operands) { if (any_errors()) { - for_array(i, operands) { - if (operands[i].type == t_invalid) { + for (Operand const &o : operands) { + if (o.type == t_invalid) { return false; } } @@ -1388,7 +1419,7 @@ bool all_operands_valid(Array<Operand> const &operands) { return true; } -bool check_stmt_internal_builtin_proc_id(Ast *expr, BuiltinProcId *id_) { +gb_internal bool check_stmt_internal_builtin_proc_id(Ast *expr, BuiltinProcId *id_) { BuiltinProcId id = BuiltinProc_Invalid; Entity *e = entity_of_node(expr); if (e != nullptr && e->kind == Entity_Builtin) { @@ -1400,7 +1431,7 @@ bool check_stmt_internal_builtin_proc_id(Ast *expr, BuiltinProcId *id_) { return id != BuiltinProc_Invalid; } -bool check_expr_is_stack_variable(Ast *expr) { +gb_internal bool check_expr_is_stack_variable(Ast *expr) { /* expr = unparen_expr(expr); Entity *e = entity_of_node(expr); @@ -1419,584 +1450,880 @@ bool check_expr_is_stack_variable(Ast *expr) { return false; } -void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { - u32 mod_flags = flags & (~Stmt_FallthroughAllowed); - switch (node->kind) { - case_ast_node(_, EmptyStmt, node); case_end; - case_ast_node(_, BadStmt, node); case_end; - case_ast_node(_, BadDecl, node); case_end; +gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { + ast_node(rs, RangeStmt, node); - case_ast_node(es, ExprStmt, node) - Operand operand = {Addressing_Invalid}; - ExprKind kind = check_expr_base(ctx, &operand, es->expr, nullptr); - switch (operand.mode) { - case Addressing_Type: { - gbString str = type_to_string(operand.type); - error(node, "'%s' is not an expression", str); - gb_string_free(str); + TEMPORARY_ALLOCATOR_GUARD(); - break; + u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; + + check_open_scope(ctx, node); + check_label(ctx, rs->label, node); + + auto vals = array_make<Type *>(temporary_allocator(), 0, 2); + auto entities = array_make<Entity *>(temporary_allocator(), 0, 2); + bool is_map = false; + bool use_by_reference_for_value = false; + bool is_soa = false; + bool is_reverse = rs->reverse; + + Ast *expr = unparen_expr(rs->expr); + + isize max_val_count = 2; + if (is_ast_range(expr)) { + ast_node(ie, BinaryExpr, expr); + Operand x = {}; + Operand y = {}; + + bool ok = check_range(ctx, expr, true, &x, &y, nullptr); + if (!ok) { + goto skip_expr_range_stmt; } - case Addressing_NoValue: - return; - default: { - if (kind == Expr_Stmt) { - return; - } + array_add(&vals, x.type); + array_add(&vals, t_int); - Ast *expr = strip_or_return_expr(operand.expr); - if (expr->kind == Ast_CallExpr) { - BuiltinProcId builtin_id = BuiltinProc_Invalid; - bool do_require = false; + if (is_reverse) { + error(node, "#reverse for is not supported with ranges, prefer an explicit for loop with init, condition, and post arguments"); + } + } else { + Operand operand = {Addressing_Invalid}; + check_expr_base(ctx, &operand, expr, nullptr); + error_operand_no_value(&operand); - AstCallExpr *ce = &expr->CallExpr; - Type *t = base_type(type_of_expr(ce->proc)); - if (t->kind == Type_Proc) { - do_require = t->Proc.require_results; - } else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) { - auto const &bp = builtin_procs[builtin_id]; - do_require = bp.kind == Expr_Expr && !bp.ignore_results; + if (operand.mode == Addressing_Type) { + if (!is_type_enum(operand.type)) { + gbString t = type_to_string(operand.type); + error(operand.expr, "Cannot iterate over the type '%s'", t); + gb_string_free(t); + goto skip_expr_range_stmt; + } else { + if (is_reverse) { + error(node, "#reverse for is not supported for enum types"); } - if (do_require) { - gbString expr_str = expr_to_string(ce->proc); - error(node, "'%s' requires that its results must be handled", expr_str); - gb_string_free(expr_str); + array_add(&vals, operand.type); + array_add(&vals, t_int); + add_type_info_type(ctx, operand.type); + goto skip_expr_range_stmt; + } + } else if (operand.mode != Addressing_Invalid) { + bool is_ptr = is_type_pointer(operand.type); + Type *t = base_type(type_deref(operand.type)); + switch (t->kind) { + case Type_Basic: + if (is_type_string(t) && t->Basic.kind != Basic_cstring) { + array_add(&vals, t_rune); + array_add(&vals, t_int); + if (is_reverse) { + add_package_dependency(ctx, "runtime", "string_decode_last_rune"); + } else { + add_package_dependency(ctx, "runtime", "string_decode_rune"); + } } - return; - } else if (expr->kind == Ast_SelectorCallExpr) { - BuiltinProcId builtin_id = BuiltinProc_Invalid; - bool do_require = false; - - AstSelectorCallExpr *se = &expr->SelectorCallExpr; - ast_node(ce, CallExpr, se->call); - Type *t = base_type(type_of_expr(ce->proc)); - if (t == nullptr) { - gbString expr_str = expr_to_string(ce->proc); - error(node, "'%s' is not a value field nor procedure", expr_str); - gb_string_free(expr_str); - return; + 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; + 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); + if (is_reverse) { + error(node, "#reverse for is not supported for map types, as maps are unordered"); } - if (t->kind == Type_Proc) { - do_require = t->Proc.require_results; - } else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) { - auto const &bp = builtin_procs[builtin_id]; - do_require = bp.kind == Expr_Expr && !bp.ignore_results; + break; + + case Type_Tuple: + { + isize count = t->Tuple.variables.count; + if (count < 1 || count > 3) { + 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"); + break; + } + Type *cond_type = t->Tuple.variables[count-1]->type; + if (!is_type_boolean(cond_type)) { + gbString s = type_to_string(cond_type); + error(operand.expr, "The final type of %td-valued expression must be a boolean, got %s", count, s); + gb_string_free(s); + break; + } + + for (Entity *e : t->Tuple.variables) { + array_add(&vals, e->type); + } + + 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; + } + + if (is_reverse) { + error(node, "#reverse for is not supported for multiple return valued parameters"); + } } - if (do_require) { - gbString expr_str = expr_to_string(ce->proc); - error(node, "'%s' requires that its results must be handled", expr_str); - gb_string_free(expr_str); + break; + + 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); } - return; + break; } - gbString expr_str = expr_to_string(operand.expr); - error(node, "Expression is not used: '%s'", expr_str); - gb_string_free(expr_str); - if (operand.expr->kind == Ast_BinaryExpr) { - ast_node(be, BinaryExpr, operand.expr); - if (be->op.kind != Token_CmpEq) { - break; - } + } - switch (be->left->tav.mode) { - case Addressing_Context: - case Addressing_Variable: - case Addressing_MapIndex: - case Addressing_SoaVariable: - { - gbString lhs = expr_to_string(be->left); - gbString rhs = expr_to_string(be->right); - error_line("\tSuggestion: Did you mean to do an assignment?\n", lhs, rhs); - error_line("\t '%s = %s;'\n", lhs, rhs); - gb_string_free(rhs); - gb_string_free(lhs); - } - break; + if (vals.count == 0 || vals[0] == nullptr) { + gbString s = expr_to_string(operand.expr); + gbString t = type_to_string(operand.type); + defer (gb_string_free(s)); + defer (gb_string_free(t)); + + error(operand.expr, "Cannot iterate over '%s' of type '%s'", s, t); + + if (rs->vals.count == 1) { + Type *t = type_deref(operand.type); + if (is_type_map(t) || is_type_bit_set(t)) { + gbString v = expr_to_string(rs->vals[0]); + defer (gb_string_free(v)); + error_line("\tSuggestion: place parentheses around the expression\n"); + error_line("\t for (%s in %s) {\n", v, s); } } - - break; - } } - case_end; + } - case_ast_node(ts, TagStmt, node); - // TODO(bill): Tag Statements - error(node, "Tag statements are not supported yet"); - check_stmt(ctx, ts->stmt, flags); - case_end; + skip_expr_range_stmt:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird. - case_ast_node(as, AssignStmt, node); - switch (as->op.kind) { - case Token_Eq: { - // a, b, c = 1, 2, 3; // Multisided + if (rs->vals.count > max_val_count) { + error(rs->vals[max_val_count], "Expected a maximum of %td identifier%s, got %td", max_val_count, max_val_count == 1 ? "" : "s", rs->vals.count); + } - isize lhs_count = as->lhs.count; - if (lhs_count == 0) { - error(as->op, "Missing lhs in assignment statement"); - return; - } + auto rhs = slice_from_array(vals); + auto lhs = slice_make<Ast *>(temporary_allocator(), rhs.count); + slice_copy(&lhs, rs->vals); + isize addressable_index = cast(isize)is_map; - // NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be - // an extra allocation - auto lhs_operands = array_make<Operand>(temporary_allocator(), lhs_count); - auto rhs_operands = array_make<Operand>(temporary_allocator(), 0, 2*lhs_count); + for_array(i, rhs) { + if (lhs[i] == nullptr) { + continue; + } + Ast * name = lhs[i]; + Type *type = rhs[i]; - for_array(i, as->lhs) { - if (is_blank_ident(as->lhs[i])) { - Operand *o = &lhs_operands[i]; - o->expr = as->lhs[i]; - o->mode = Addressing_Value; - } else { - ctx->assignment_lhs_hint = unparen_expr(as->lhs[i]); - check_expr(ctx, &lhs_operands[i], as->lhs[i]); + Entity *entity = nullptr; + if (name->kind == Ast_Ident) { + Token token = name->Ident.token; + String str = token.string; + Entity *found = nullptr; + + if (!is_blank_ident(str)) { + found = scope_lookup_current(ctx->scope, str); + } + if (found == nullptr) { + entity = alloc_entity_variable(ctx->scope, token, type, EntityState_Resolved); + entity->flags |= EntityFlag_ForValue; + entity->flags |= EntityFlag_Value; + entity->identifier = name; + if (i == addressable_index && use_by_reference_for_value) { + entity->flags &= ~EntityFlag_Value; + } + if (is_soa) { + if (i == 0) { + entity->flags |= EntityFlag_SoaPtrField; + } } + + add_entity_definition(&ctx->checker->info, name, entity); + } else { + TokenPos pos = found->token.pos; + error(token, + "Redeclaration of '%.*s' in this scope\n" + "\tat %s", + LIT(str), token_pos_to_string(pos)); + entity = found; } - ctx->assignment_lhs_hint = nullptr; // Reset the assignment_lhs_hint + } else { + error(name, "A variable declaration must be an identifier"); + } - check_assignment_arguments(ctx, lhs_operands, &rhs_operands, as->rhs); + if (entity == nullptr) { + entity = alloc_entity_dummy_variable(builtin_pkg->scope, ast_token(name)); + entity->identifier = name; // might not be an identifier + } - isize rhs_count = rhs_operands.count; - for_array(i, rhs_operands) { - if (rhs_operands[i].mode == Addressing_Invalid) { - // TODO(bill): Should I ignore invalid parameters? - // rhs_count--; - } - } + array_add(&entities, entity); - auto lhs_to_ignore = array_make<bool>(temporary_allocator(), lhs_count); + if (type == nullptr) { + entity->type = t_invalid; + entity->flags |= EntityFlag_Used; + } + } - isize max = gb_min(lhs_count, rhs_count); - for (isize i = 0; i < max; i++) { - if (lhs_to_ignore[i]) { - continue; + for (Entity *e : entities) { + DeclInfo *d = decl_info_of_entity(e); + GB_ASSERT(d == nullptr); + add_entity(ctx, ctx->scope, e->identifier, e); + d = make_decl_info(ctx->scope, ctx->decl); + add_entity_and_decl_info(ctx, e->identifier, e, d); + } + + check_stmt(ctx, rs->body, new_flags); + + check_close_scope(ctx); +} + +gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { + ast_node(vd, ValueDecl, node); + if (!vd->is_mutable) { + // constant value declaration + // NOTE(bill): Check `_` declarations + for (Ast *name : vd->names) { + if (is_blank_ident(name)) { + Entity *e = name->Ident.entity; + DeclInfo *d = decl_info_of_entity(e); + if (d != nullptr) { + check_entity_decl(ctx, e, d, nullptr); } - check_assignment_variable(ctx, &lhs_operands[i], &rhs_operands[i]); } - if (lhs_count != rhs_count) { - error(as->lhs[0], "Assignment count mismatch '%td' = '%td'", lhs_count, rhs_count); - } - break; } + return; + } + Entity **entities = gb_alloc_array(permanent_allocator(), Entity *, vd->names.count); + isize entity_count = 0; - default: { - // a += 1; // Single-sided - Token op = as->op; - if (as->lhs.count != 1 || as->rhs.count != 1) { - error(op, "Assignment operation '%.*s' requires single-valued expressions", LIT(op.string)); - return; - } - if (!gb_is_between(op.kind, Token__AssignOpBegin+1, Token__AssignOpEnd-1)) { - error(op, "Unknown Assignment operation '%.*s'", LIT(op.string)); - return; + isize new_name_count = 0; + for (Ast *name : vd->names) { + Entity *entity = nullptr; + if (name->kind != Ast_Ident) { + error(name, "A variable declaration must be an identifier"); + } else { + Token token = name->Ident.token; + String str = token.string; + Entity *found = nullptr; + // NOTE(bill): Ignore assignments to '_' + if (!is_blank_ident(str)) { + found = scope_lookup_current(ctx->scope, str); + new_name_count += 1; } - Operand lhs = {Addressing_Invalid}; - Operand rhs = {Addressing_Invalid}; - Ast *binary_expr = alloc_ast_node(node->file(), Ast_BinaryExpr); - ast_node(be, BinaryExpr, binary_expr); - be->op = op; - be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add)); - // NOTE(bill): Only use the first one will be used - be->left = as->lhs[0]; - be->right = as->rhs[0]; - - check_expr(ctx, &lhs, as->lhs[0]); - check_binary_expr(ctx, &rhs, binary_expr, nullptr, true); - if (rhs.mode == Addressing_Invalid) { - return; + if (found == nullptr) { + entity = alloc_entity_variable(ctx->scope, token, nullptr); + entity->identifier = name; + + Ast *fl = ctx->foreign_context.curr_library; + if (fl != nullptr) { + GB_ASSERT(fl->kind == Ast_Ident); + entity->Variable.is_foreign = true; + entity->Variable.foreign_library_ident = fl; + } + } else { + TokenPos pos = found->token.pos; + error(token, + "Redeclaration of '%.*s' in this scope\n" + "\tat %s", + LIT(str), token_pos_to_string(pos)); + entity = found; } - // NOTE(bill): Only use the first one will be used - check_assignment_variable(ctx, &lhs, &rhs); + } + if (entity == nullptr) { + entity = alloc_entity_dummy_variable(builtin_pkg->scope, ast_token(name)); + } + entity->parent_proc_decl = ctx->curr_proc_decl; + entities[entity_count++] = entity; + if (name->kind == Ast_Ident) { + name->Ident.entity = entity; + } + } - break; + if (new_name_count == 0) { + begin_error_block(); + error(node, "No new declarations on the left hand side"); + bool all_underscore = true; + for (Ast *name : vd->names) { + if (name->kind == Ast_Ident) { + if (!is_blank_ident(name)) { + all_underscore = false; + break; + } + } else { + all_underscore = false; + break; + } } + if (all_underscore) { + error_line("\tSuggestion: Try changing the declaration (:=) to an assignment (=)\n"); } - case_end; - case_ast_node(bs, BlockStmt, node); - check_open_scope(ctx, node); - check_label(ctx, bs->label, node); + end_error_block(); + } - check_stmt_list(ctx, bs->stmts, flags); - check_block_stmt_for_errors(ctx, node); - check_close_scope(ctx); - case_end; + Type *init_type = nullptr; + if (vd->type != nullptr) { + init_type = check_type(ctx, vd->type); + if (init_type == nullptr) { + init_type = t_invalid; + } else if (is_type_polymorphic(base_type(init_type))) { + gbString str = type_to_string(init_type); + error(vd->type, "Invalid use of a polymorphic type '%s' in variable declaration", str); + gb_string_free(str); + init_type = t_invalid; + } + } - case_ast_node(is, IfStmt, node); - check_open_scope(ctx, node); - check_label(ctx, is->label, node); + // TODO NOTE(bill): This technically checks things multple times + AttributeContext ac = make_attribute_context(ctx->foreign_context.link_prefix); + check_decl_attributes(ctx, vd->attributes, var_decl_attribute, &ac); - if (is->init != nullptr) { - check_stmt(ctx, is->init, 0); + for (isize i = 0; i < entity_count; i++) { + Entity *e = entities[i]; + GB_ASSERT(e != nullptr); + if (e->flags & EntityFlag_Visited) { + e->type = t_invalid; + continue; } + e->flags |= EntityFlag_Visited; - Operand operand = {Addressing_Invalid}; - check_expr(ctx, &operand, is->cond); - if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) { - error(is->cond, "Non-boolean condition in 'if' statement"); + e->state = EntityState_InProgress; + if (e->type == nullptr) { + e->type = init_type; + e->state = EntityState_Resolved; } + ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); - check_stmt(ctx, is->body, mod_flags); + if (ac.link_name.len > 0) { + e->Variable.link_name = ac.link_name; + } - if (is->else_stmt != nullptr) { - switch (is->else_stmt->kind) { - case Ast_IfStmt: - case Ast_BlockStmt: - check_stmt(ctx, is->else_stmt, mod_flags); - break; - default: - error(is->else_stmt, "Invalid 'else' statement in 'if' statement"); - break; + e->flags &= ~EntityFlag_Static; + if (ac.is_static) { + String name = e->token.string; + if (name == "_") { + error(e->token, "The 'static' attribute is not allowed to be applied to '_'"); + } else { + e->flags |= EntityFlag_Static; + if (ctx->in_defer) { + error(e->token, "'static' variables cannot be declared within a defer statement"); + } } } + if (ac.thread_local_model != "") { + String name = e->token.string; + if (name == "_") { + error(e->token, "The 'thread_local' attribute is not allowed to be applied to '_'"); + } else { + e->flags |= EntityFlag_Static; + if (ctx->in_defer) { + error(e->token, "'thread_local' variables cannot be declared within a defer statement"); + } + } + e->Variable.thread_local_model = ac.thread_local_model; + } - check_close_scope(ctx); - case_end; - - case_ast_node(ws, WhenStmt, node); - check_when_stmt(ctx, ws, flags); - case_end; + if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) { + error(e->token, "@(thread_local) is not supported for this target platform"); + } - case_ast_node(rs, ReturnStmt, node); - GB_ASSERT(ctx->curr_proc_sig != nullptr); - if (ctx->in_defer) { - error(rs->token, "'return' cannot be used within a defer statement"); - break; + if (ac.is_static && ac.thread_local_model != "") { + error(e->token, "The 'static' attribute is not needed if 'thread_local' is applied"); } + } - Type *proc_type = ctx->curr_proc_sig; - GB_ASSERT(proc_type != nullptr); - GB_ASSERT(proc_type->kind == Type_Proc); + check_init_variables(ctx, entities, entity_count, vd->values, str_lit("variable declaration")); + check_arity_match(ctx, vd, false); - TypeProc *pt = &proc_type->Proc; - if (pt->diverging) { - error(rs->token, "Diverging procedures may not return"); - break; - } + for (isize i = 0; i < entity_count; i++) { + Entity *e = entities[i]; - Entity **result_entities = nullptr; - isize result_count = 0; - bool has_named_results = pt->has_named_results; - if (pt->results) { - result_entities = proc_type->Proc.results->Tuple.variables.data; - result_count = proc_type->Proc.results->Tuple.variables.count; - } + if (e->Variable.is_foreign) { + if (vd->values.count > 0) { + error(e->token, "A foreign variable declaration cannot have a default value"); + } - auto operands = array_make<Operand>(heap_allocator(), 0, 2*rs->results.count); - defer (array_free(&operands)); + String name = e->token.string; + if (e->Variable.link_name.len > 0) { + name = e->Variable.link_name; + } - check_unpack_arguments(ctx, result_entities, result_count, &operands, rs->results, true, false); + if (vd->values.count > 0) { + error(e->token, "A foreign variable declaration cannot have a default value"); + } + init_entity_foreign_library(ctx, e); - if (result_count == 0 && rs->results.count > 0) { - error(rs->results[0], "No return values expected"); - } else if (has_named_results && operands.count == 0) { - // Okay - } else if (operands.count != result_count) { - // Ignore error message as it has most likely already been reported - if (all_operands_valid(operands)) { - error(node, "Expected %td return values, got %td", result_count, operands.count); + auto *fp = &ctx->checker->info.foreigns; + StringHashKey key = string_hash_string(name); + Entity **found = string_map_get(fp, key); + if (found) { + Entity *f = *found; + TokenPos pos = f->token.pos; + Type *this_type = base_type(e->type); + Type *other_type = base_type(f->type); + if (!are_types_identical(this_type, other_type)) { + error(e->token, + "Foreign entity '%.*s' previously declared elsewhere with a different type\n" + "\tat %s", + LIT(name), token_pos_to_string(pos)); + } + } else { + string_map_set(fp, key, e); } - } else { - for (isize i = 0; i < result_count; i++) { - Entity *e = pt->results->Tuple.variables[i]; - Operand *o = &operands[i]; - check_assignment(ctx, o, e->type, str_lit("return statement")); - if (is_type_untyped(o->type)) { - update_untyped_expr_type(ctx, o->expr, e->type, true); + } else if (e->flags & EntityFlag_Static) { + if (vd->values.count > 0) { + if (entity_count != vd->values.count) { + error(e->token, "A static variable declaration with a default value must be constant"); + } else { + Ast *value = vd->values[i]; + if (value->tav.mode != Addressing_Constant) { + error(e->token, "A static variable declaration with a default value must be constant"); + } } + } + } + add_entity(ctx, ctx->scope, e->identifier, e); + } + if (vd->is_using != 0) { + Token token = ast_token(node); + if (vd->type != nullptr && entity_count > 1) { + error(token, "'using' can only be applied to one variable of the same type"); + // TODO(bill): Should a 'continue' happen here? + } - // NOTE(bill): This is very basic escape analysis - // This needs to be improved tremendously, and a lot of it done during the - // middle-end (or LLVM side) to improve checks and error messages - Ast *expr = unparen_expr(o->expr); - if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) { - Ast *x = unparen_expr(expr->UnaryExpr.expr); - if (x->kind == Ast_CompoundLit) { - error(expr, "Cannot return the address to a stack value from a procedure"); - } else if (x->kind == Ast_IndexExpr) { - Ast *array = x->IndexExpr.expr; - if (is_type_array_like(type_of_expr(array)) && check_expr_is_stack_variable(array)) { - gbString t = type_to_string(type_of_expr(array)); - error(expr, "Cannot return the address to an element of stack variable from a procedure, of type %s", t); - gb_string_free(t); - } - } else { - if (check_expr_is_stack_variable(x)) { - error(expr, "Cannot return the address to a stack variable from a procedure"); + for (isize entity_index = 0; entity_index < 1; entity_index++) { + Entity *e = entities[entity_index]; + if (e == nullptr) { + continue; + } + if (e->kind != Entity_Variable) { + continue; + } + String name = e->token.string; + Type *t = base_type(type_deref(e->type)); + + if (is_blank_ident(name)) { + error(token, "'using' cannot be applied variable declared as '_'"); + } else if (is_type_struct(t) || is_type_raw_union(t)) { + ERROR_BLOCK(); + + Scope *scope = t->Struct.scope; + GB_ASSERT(scope != nullptr); + for (auto const &entry : scope->elements) { + Entity *f = entry.value; + if (f->kind == Entity_Variable) { + Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, nullptr); + uvar->flags |= (e->flags & EntityFlag_Value); + Entity *prev = scope_insert(ctx->scope, uvar); + if (prev != nullptr) { + error(token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(name), LIT(prev->token.string)); + return; } } } + + add_entity_use(ctx, nullptr, e); + } else { + // NOTE(bill): skip the rest to remove extra errors + error(token, "'using' can only be applied to variables of type struct or raw_union"); + return; } } - case_end; + } +} - case_ast_node(fs, ForStmt, node); - u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; +gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) { + ast_node(es, ExprStmt, node); - check_open_scope(ctx, node); - check_label(ctx, fs->label, node); // TODO(bill): What should the label's "scope" be? + Operand operand = {Addressing_Invalid}; + ExprKind kind = check_expr_base(ctx, &operand, es->expr, nullptr); + switch (operand.mode) { + case Addressing_Type: + { + gbString str = type_to_string(operand.type); + error(node, "'%s' is not an expression but a type and cannot be used as a statement", str); + gb_string_free(str); + break; + } + case Addressing_NoValue: + return; + } + if (kind == Expr_Stmt) { + return; + } - if (fs->init != nullptr) { - check_stmt(ctx, fs->init, 0); + Ast *expr = strip_or_return_expr(operand.expr); + if (expr->kind == Ast_CallExpr) { + BuiltinProcId builtin_id = BuiltinProc_Invalid; + bool do_require = false; + + AstCallExpr *ce = &expr->CallExpr; + Type *t = base_type(type_of_expr(ce->proc)); + if (t->kind == Type_Proc) { + do_require = t->Proc.require_results; + } else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) { + auto const &bp = builtin_procs[builtin_id]; + do_require = bp.kind == Expr_Expr && !bp.ignore_results; } - if (fs->cond != nullptr) { - Operand o = {Addressing_Invalid}; - check_expr(ctx, &o, fs->cond); - if (o.mode != Addressing_Invalid && !is_type_boolean(o.type)) { - error(fs->cond, "Non-boolean condition in 'for' statement"); - } + if (do_require) { + gbString expr_str = expr_to_string(ce->proc); + error(node, "'%s' requires that its results must be handled", expr_str); + gb_string_free(expr_str); + } + return; + } else if (expr->kind == Ast_SelectorCallExpr) { + BuiltinProcId builtin_id = BuiltinProc_Invalid; + bool do_require = false; + + AstSelectorCallExpr *se = &expr->SelectorCallExpr; + ast_node(ce, CallExpr, se->call); + Type *t = base_type(type_of_expr(ce->proc)); + if (t == nullptr) { + gbString expr_str = expr_to_string(ce->proc); + error(node, "'%s' is not a value field nor procedure", expr_str); + gb_string_free(expr_str); + return; + } + if (t->kind == Type_Proc) { + do_require = t->Proc.require_results; + } else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) { + auto const &bp = builtin_procs[builtin_id]; + do_require = bp.kind == Expr_Expr && !bp.ignore_results; + } + if (do_require) { + gbString expr_str = expr_to_string(ce->proc); + error(node, "'%s' requires that its results must be handled", expr_str); + gb_string_free(expr_str); + } + return; + } + gbString expr_str = expr_to_string(operand.expr); + error(node, "Expression is not used: '%s'", expr_str); + gb_string_free(expr_str); + if (operand.expr->kind == Ast_BinaryExpr) { + ast_node(be, BinaryExpr, operand.expr); + if (be->op.kind != Token_CmpEq) { + return; } - if (fs->post != nullptr) { - check_stmt(ctx, fs->post, 0); - if (fs->post->kind != Ast_AssignStmt) { - error(fs->post, "'for' statement post statement must be a simple statement"); + switch (be->left->tav.mode) { + case Addressing_Context: + case Addressing_Variable: + case Addressing_MapIndex: + case Addressing_SoaVariable: + { + gbString lhs = expr_to_string(be->left); + gbString rhs = expr_to_string(be->right); + error_line("\tSuggestion: Did you mean to do an assignment?\n", lhs, rhs); + error_line("\t '%s = %s;'\n", lhs, rhs); + gb_string_free(rhs); + gb_string_free(lhs); } + break; } - check_stmt(ctx, fs->body, new_flags); + } +} - check_close_scope(ctx); - case_end; +gb_internal void check_assign_stmt(CheckerContext *ctx, Ast *node) { + ast_node(as, AssignStmt, node); + if (as->op.kind == Token_Eq) { + // a, b, c = 1, 2, 3; // Multisided - case_ast_node(rs, RangeStmt, node); - u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; + isize lhs_count = as->lhs.count; + if (lhs_count == 0) { + error(as->op, "Missing lhs in assignment statement"); + return; + } - check_open_scope(ctx, node); - check_label(ctx, rs->label, node); + TEMPORARY_ALLOCATOR_GUARD(); - auto vals = array_make<Type *>(temporary_allocator(), 0, 2); - auto entities = array_make<Entity *>(temporary_allocator(), 0, 2); - bool is_map = false; - bool use_by_reference_for_value = false; - bool is_soa = false; + // NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be + // an extra allocation + auto lhs_operands = array_make<Operand>(temporary_allocator(), lhs_count); + auto rhs_operands = array_make<Operand>(temporary_allocator(), 0, 2*lhs_count); - Ast *expr = unparen_expr(rs->expr); + for_array(i, as->lhs) { + if (is_blank_ident(as->lhs[i])) { + Operand *o = &lhs_operands[i]; + o->expr = as->lhs[i]; + o->mode = Addressing_Value; + } else { + ctx->assignment_lhs_hint = unparen_expr(as->lhs[i]); + check_expr(ctx, &lhs_operands[i], as->lhs[i]); + } + } + ctx->assignment_lhs_hint = nullptr; // Reset the assignment_lhs_hint - isize max_val_count = 2; - if (is_ast_range(expr)) { - ast_node(ie, BinaryExpr, expr); - Operand x = {}; - Operand y = {}; + check_assignment_arguments(ctx, lhs_operands, &rhs_operands, as->rhs); - bool ok = check_range(ctx, expr, &x, &y, nullptr); - if (!ok) { - goto skip_expr_range_stmt; + auto lhs_to_ignore = array_make<bool>(temporary_allocator(), lhs_count); + + isize rhs_count = rhs_operands.count; + isize max = gb_min(lhs_count, rhs_count); + for (isize i = 0; i < max; i++) { + if (lhs_to_ignore[i]) { + continue; } - array_add(&vals, x.type); - array_add(&vals, t_int); - } else { - Operand operand = {Addressing_Invalid}; - check_expr_base(ctx, &operand, expr, nullptr); - error_operand_no_value(&operand); - - if (operand.mode == Addressing_Type) { - if (!is_type_enum(operand.type)) { - gbString t = type_to_string(operand.type); - error(operand.expr, "Cannot iterate over the type '%s'", t); - gb_string_free(t); - goto skip_expr_range_stmt; - } else { - array_add(&vals, operand.type); - array_add(&vals, t_int); - add_type_info_type(ctx, operand.type); - goto skip_expr_range_stmt; - } - } else if (operand.mode != Addressing_Invalid) { - bool is_ptr = is_type_pointer(operand.type); - Type *t = base_type(type_deref(operand.type)); - switch (t->kind) { - case Type_Basic: - if (is_type_string(t) && t->Basic.kind != Basic_cstring) { - array_add(&vals, t_rune); - array_add(&vals, t_int); - add_package_dependency(ctx, "runtime", "string_decode_rune"); - } - break; + check_assignment_variable(ctx, &lhs_operands[i], &rhs_operands[i]); + } + if (lhs_count != rhs_count) { + error(as->lhs[0], "Assignment count mismatch '%td' = '%td'", lhs_count, rhs_count); + } - 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; + } else { + // a += 1; // Single-sided + Token op = as->op; + if (as->lhs.count != 1 || as->rhs.count != 1) { + error(op, "Assignment operation '%.*s' requires single-valued expressions", LIT(op.string)); + return; + } + if (!gb_is_between(op.kind, Token__AssignOpBegin+1, Token__AssignOpEnd-1)) { + error(op, "Unknown Assignment operation '%.*s'", LIT(op.string)); + return; + } + Operand lhs = {Addressing_Invalid}; + Operand rhs = {Addressing_Invalid}; + Ast *binary_expr = alloc_ast_node(node->file(), Ast_BinaryExpr); + ast_node(be, BinaryExpr, binary_expr); + be->op = op; + be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add)); + // NOTE(bill): Only use the first one will be used + be->left = as->lhs[0]; + be->right = as->rhs[0]; + + check_expr(ctx, &lhs, as->lhs[0]); + check_binary_expr(ctx, &rhs, binary_expr, nullptr, true); + if (rhs.mode != Addressing_Invalid) { + // NOTE(bill): Only use the first one will be used + check_assignment_variable(ctx, &lhs, &rhs); + } + } +} - case Type_Array: - if (is_ptr) use_by_reference_for_value = true; - array_add(&vals, t->Array.elem); - array_add(&vals, t_int); - break; +gb_internal void check_if_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { + ast_node(is, IfStmt, node); + check_open_scope(ctx, node); - case Type_DynamicArray: - if (is_ptr) use_by_reference_for_value = true; - array_add(&vals, t->DynamicArray.elem); - array_add(&vals, t_int); - break; + check_label(ctx, is->label, node); - case Type_Slice: - if (is_ptr) use_by_reference_for_value = true; - array_add(&vals, t->Slice.elem); - array_add(&vals, t_int); - break; + if (is->init != nullptr) { + check_stmt(ctx, is->init, 0); + } - 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); - break; + Operand operand = {Addressing_Invalid}; + check_expr(ctx, &operand, is->cond); + if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) { + error(is->cond, "Non-boolean condition in 'if' statement"); + } - case Type_Tuple: - { - isize count = t->Tuple.variables.count; - if (count < 1 || count > 3) { - 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"); - break; - } - Type *cond_type = t->Tuple.variables[count-1]->type; - if (!is_type_boolean(cond_type)) { - gbString s = type_to_string(cond_type); - error(operand.expr, "The final type of %td-valued expression must be a boolean, got %s", count, s); - gb_string_free(s); - break; - } + check_stmt(ctx, is->body, mod_flags); - for_array(ti, t->Tuple.variables) { - array_add(&vals, t->Tuple.variables[ti]->type); - } + if (is->else_stmt != nullptr) { + switch (is->else_stmt->kind) { + case Ast_IfStmt: + case Ast_BlockStmt: + check_stmt(ctx, is->else_stmt, mod_flags); + break; + default: + error(is->else_stmt, "Invalid 'else' statement in 'if' statement"); + break; + } + } - 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; - } + check_close_scope(ctx); +} - 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; - } +gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { + ast_node(rs, ReturnStmt, node); - } - break; + GB_ASSERT(ctx->curr_proc_sig != nullptr); - 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); - } - break; - } - } + if (ctx->in_defer) { + error(rs->token, "'return' cannot be used within a defer statement"); + return; + } - if (vals.count == 0 || vals[0] == nullptr) { - gbString s = expr_to_string(operand.expr); - gbString t = type_to_string(operand.type); - defer (gb_string_free(s)); - defer (gb_string_free(t)); - - error(operand.expr, "Cannot iterate over '%s' of type '%s'", s, t); - - if (rs->vals.count == 1) { - Type *t = type_deref(operand.type); - if (is_type_map(t) || is_type_bit_set(t)) { - gbString v = expr_to_string(rs->vals[0]); - defer (gb_string_free(v)); - error_line("\tSuggestion: place parentheses around the expression\n"); - error_line("\t for (%s in %s) {\n", v, s); - } - } - } - } + Type *proc_type = ctx->curr_proc_sig; + GB_ASSERT(proc_type != nullptr); + GB_ASSERT(proc_type->kind == Type_Proc); - skip_expr_range_stmt:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird. + TypeProc *pt = &proc_type->Proc; + if (pt->diverging) { + error(rs->token, "Diverging procedures may not return"); + return; + } - if (rs->vals.count > max_val_count) { - error(rs->vals[max_val_count], "Expected a maximum of %td identifier%s, got %td", max_val_count, max_val_count == 1 ? "" : "s", rs->vals.count); - } + Entity **result_entities = nullptr; + isize result_count = 0; + bool has_named_results = pt->has_named_results; + if (pt->results) { + result_entities = proc_type->Proc.results->Tuple.variables.data; + result_count = proc_type->Proc.results->Tuple.variables.count; + } - auto rhs = slice_from_array(vals); - auto lhs = slice_make<Ast *>(temporary_allocator(), rhs.count); - slice_copy(&lhs, rs->vals); + auto operands = array_make<Operand>(heap_allocator(), 0, 2*rs->results.count); + defer (array_free(&operands)); - isize addressable_index = cast(isize)is_map; + check_unpack_arguments(ctx, result_entities, result_count, &operands, rs->results, UnpackFlag_AllowOk); - for_array(i, rhs) { - if (lhs[i] == nullptr) { - continue; + if (result_count == 0 && rs->results.count > 0) { + error(rs->results[0], "No return values expected"); + } else if (has_named_results && operands.count == 0) { + // Okay + } else if (operands.count != result_count) { + // Ignore error message as it has most likely already been reported + if (all_operands_valid(operands)) { + if (operands.count == 1) { + gbString t = type_to_string(operands[0].type); + error(node, "Expected %td return values, got %td (%s)", result_count, operands.count, t); + gb_string_free(t); + } else { + error(node, "Expected %td return values, got %td", result_count, operands.count); } - Ast * name = lhs[i]; - Type *type = rhs[i]; - - Entity *entity = nullptr; - if (name->kind == Ast_Ident) { - Token token = name->Ident.token; - String str = token.string; - Entity *found = nullptr; - - if (!is_blank_ident(str)) { - found = scope_lookup_current(ctx->scope, str); - } - if (found == nullptr) { - entity = alloc_entity_variable(ctx->scope, token, type, EntityState_Resolved); - entity->flags |= EntityFlag_ForValue; - entity->flags |= EntityFlag_Value; - if (i == addressable_index && use_by_reference_for_value) { - entity->flags &= ~EntityFlag_Value; - } - if (is_soa) { - if (i == 0) { - entity->flags |= EntityFlag_SoaPtrField; - } + } + } else { + for (isize i = 0; i < result_count; i++) { + Entity *e = pt->results->Tuple.variables[i]; + Operand *o = &operands[i]; + check_assignment(ctx, o, e->type, str_lit("return statement")); + if (is_type_untyped(o->type)) { + update_untyped_expr_type(ctx, o->expr, e->type, true); + } + + + // NOTE(bill): This is very basic escape analysis + // This needs to be improved tremendously, and a lot of it done during the + // middle-end (or LLVM side) to improve checks and error messages + Ast *expr = unparen_expr(o->expr); + if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) { + Ast *x = unparen_expr(expr->UnaryExpr.expr); + if (x->kind == Ast_CompoundLit) { + error(expr, "Cannot return the address to a stack value from a procedure"); + } else if (x->kind == Ast_IndexExpr) { + Ast *array = x->IndexExpr.expr; + if (is_type_array_like(type_of_expr(array)) && check_expr_is_stack_variable(array)) { + gbString t = type_to_string(type_of_expr(array)); + error(expr, "Cannot return the address to an element of stack variable from a procedure, of type %s", t); + gb_string_free(t); } - - add_entity_definition(&ctx->checker->info, name, entity); } else { - TokenPos pos = found->token.pos; - error(token, - "Redeclaration of '%.*s' in this scope\n" - "\tat %s", - LIT(str), token_pos_to_string(pos)); - entity = found; + if (check_expr_is_stack_variable(x)) { + error(expr, "Cannot return the address to a stack variable from a procedure"); + } } - } else { - error(name, "A variable declaration must be an identifier"); } + } + } +} - if (entity == nullptr) { - entity = alloc_entity_dummy_variable(builtin_pkg->scope, ast_token(name)); - } +gb_internal void check_for_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { + ast_node(fs, ForStmt, node); + mod_flags |= Stmt_BreakAllowed | Stmt_ContinueAllowed; - array_add(&entities, entity); + check_open_scope(ctx, node); + check_label(ctx, fs->label, node); // TODO(bill): What should the label's "scope" be? - if (type == nullptr) { - entity->type = t_invalid; - entity->flags |= EntityFlag_Used; - } + if (fs->init != nullptr) { + check_stmt(ctx, fs->init, 0); + } + if (fs->cond != nullptr) { + Operand o = {Addressing_Invalid}; + check_expr(ctx, &o, fs->cond); + if (o.mode != Addressing_Invalid && !is_type_boolean(o.type)) { + error(fs->cond, "Non-boolean condition in 'for' statement"); } + } + if (fs->post != nullptr) { + check_stmt(ctx, fs->post, 0); - for_array(i, entities) { - Entity *e = entities[i]; - DeclInfo *d = decl_info_of_entity(e); - GB_ASSERT(d == nullptr); - add_entity(ctx, ctx->scope, e->identifier, e); - d = make_decl_info(ctx->scope, ctx->decl); - add_entity_and_decl_info(ctx, e->identifier, e, d); + if (fs->post->kind != Ast_AssignStmt) { + error(fs->post, "'for' statement post statement must be a simple statement"); } + } + check_stmt(ctx, fs->body, mod_flags); + + check_close_scope(ctx); +} - check_stmt(ctx, rs->body, new_flags); +gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { + u32 mod_flags = flags & (~Stmt_FallthroughAllowed); + switch (node->kind) { + case_ast_node(_, EmptyStmt, node); case_end; + case_ast_node(_, BadStmt, node); case_end; + case_ast_node(_, BadDecl, node); case_end; + + case_ast_node(es, ExprStmt, node) + check_expr_stmt(ctx, node); + case_end; + + case_ast_node(as, AssignStmt, node); + check_assign_stmt(ctx, node); + case_end; + + case_ast_node(bs, BlockStmt, node); + check_open_scope(ctx, node); + check_label(ctx, bs->label, node); + + check_stmt_list(ctx, bs->stmts, flags); + check_block_stmt_for_errors(ctx, node); check_close_scope(ctx); case_end; + case_ast_node(is, IfStmt, node); + check_if_stmt(ctx, node, mod_flags); + case_end; + + case_ast_node(ws, WhenStmt, node); + check_when_stmt(ctx, ws, flags); + case_end; + + case_ast_node(rs, ReturnStmt, node); + check_return_stmt(ctx, node); + case_end; + + case_ast_node(fs, ForStmt, node); + check_for_stmt(ctx, node, mod_flags); + case_end; + + case_ast_node(rs, RangeStmt, node); + check_range_stmt(ctx, node, mod_flags); + case_end; + case_ast_node(irs, UnrollRangeStmt, node); check_inline_range_stmt(ctx, node, mod_flags); case_end; @@ -2098,8 +2425,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { error(us->token, "Empty 'using' list"); return; } - for_array(i, us->list) { - Ast *expr = unparen_expr(us->list[i]); + for (Ast *expr : us->list) { + expr = unparen_expr(expr); Entity *e = nullptr; bool is_selector = false; @@ -2139,8 +2466,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { check_decl_attributes(&c, fb->attributes, foreign_block_decl_attribute, nullptr); ast_node(block, BlockStmt, fb->body); - for_array(i, block->stmts) { - Ast *decl = block->stmts[i]; + for (Ast *decl : block->stmts) { if (decl->kind == Ast_ValueDecl && decl->ValueDecl.is_mutable) { check_stmt(&c, decl, flags); } @@ -2148,265 +2474,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { case_end; case_ast_node(vd, ValueDecl, node); - if (vd->is_mutable) { - Entity **entities = gb_alloc_array(permanent_allocator(), Entity *, vd->names.count); - isize entity_count = 0; - - isize new_name_count = 0; - for_array(i, vd->names) { - Ast *name = vd->names[i]; - Entity *entity = nullptr; - if (name->kind != Ast_Ident) { - error(name, "A variable declaration must be an identifier"); - } else { - Token token = name->Ident.token; - String str = token.string; - Entity *found = nullptr; - // NOTE(bill): Ignore assignments to '_' - if (!is_blank_ident(str)) { - found = scope_lookup_current(ctx->scope, str); - new_name_count += 1; - } - if (found == nullptr) { - entity = alloc_entity_variable(ctx->scope, token, nullptr); - entity->identifier = name; - - Ast *fl = ctx->foreign_context.curr_library; - if (fl != nullptr) { - GB_ASSERT(fl->kind == Ast_Ident); - entity->Variable.is_foreign = true; - entity->Variable.foreign_library_ident = fl; - } - } else { - TokenPos pos = found->token.pos; - error(token, - "Redeclaration of '%.*s' in this scope\n" - "\tat %s", - LIT(str), token_pos_to_string(pos)); - entity = found; - } - } - if (entity == nullptr) { - entity = alloc_entity_dummy_variable(builtin_pkg->scope, ast_token(name)); - } - entity->parent_proc_decl = ctx->curr_proc_decl; - entities[entity_count++] = entity; - if (name->kind == Ast_Ident) { - name->Ident.entity = entity; - } - } - - if (new_name_count == 0) { - begin_error_block(); - error(node, "No new declarations on the left hand side"); - bool all_underscore = true; - for_array(i, vd->names) { - Ast *name = vd->names[i]; - if (name->kind == Ast_Ident) { - if (!is_blank_ident(name)) { - all_underscore = false; - break; - } - } else { - all_underscore = false; - break; - } - } - if (all_underscore) { - error_line("\tSuggestion: Try changing the declaration (:=) to an assignment (=)\n"); - } - - end_error_block(); - } - - Type *init_type = nullptr; - if (vd->type != nullptr) { - init_type = check_type(ctx, vd->type); - if (init_type == nullptr) { - init_type = t_invalid; - } else if (is_type_polymorphic(base_type(init_type))) { - gbString str = type_to_string(init_type); - error(vd->type, "Invalid use of a polymorphic type '%s' in variable declaration", str); - gb_string_free(str); - init_type = t_invalid; - } - } - - - // TODO NOTE(bill): This technically checks things multple times - AttributeContext ac = make_attribute_context(ctx->foreign_context.link_prefix); - check_decl_attributes(ctx, vd->attributes, var_decl_attribute, &ac); - - for (isize i = 0; i < entity_count; i++) { - Entity *e = entities[i]; - GB_ASSERT(e != nullptr); - if (e->flags & EntityFlag_Visited) { - e->type = t_invalid; - continue; - } - e->flags |= EntityFlag_Visited; - - e->state = EntityState_InProgress; - if (e->type == nullptr) { - e->type = init_type; - e->state = EntityState_Resolved; - } - ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); - - if (ac.link_name.len > 0) { - e->Variable.link_name = ac.link_name; - } - - e->flags &= ~EntityFlag_Static; - if (ac.is_static) { - String name = e->token.string; - if (name == "_") { - error(e->token, "The 'static' attribute is not allowed to be applied to '_'"); - } else { - e->flags |= EntityFlag_Static; - if (ctx->in_defer) { - error(e->token, "'static' variables cannot be declared within a defer statement"); - } - } - } - if (ac.thread_local_model != "") { - String name = e->token.string; - if (name == "_") { - error(e->token, "The 'thread_local' attribute is not allowed to be applied to '_'"); - } else { - e->flags |= EntityFlag_Static; - if (ctx->in_defer) { - error(e->token, "'thread_local' variables cannot be declared within a defer statement"); - } - } - e->Variable.thread_local_model = ac.thread_local_model; - } - - if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) { - error(e->token, "@(thread_local) is not supported for this target platform"); - } - - - if (ac.is_static && ac.thread_local_model != "") { - error(e->token, "The 'static' attribute is not needed if 'thread_local' is applied"); - } - } - - check_init_variables(ctx, entities, entity_count, vd->values, str_lit("variable declaration")); - check_arity_match(ctx, vd, false); - - for (isize i = 0; i < entity_count; i++) { - Entity *e = entities[i]; - - if (e->Variable.is_foreign) { - if (vd->values.count > 0) { - error(e->token, "A foreign variable declaration cannot have a default value"); - } - - String name = e->token.string; - if (e->Variable.link_name.len > 0) { - name = e->Variable.link_name; - } - - if (vd->values.count > 0) { - error(e->token, "A foreign variable declaration cannot have a default value"); - } - init_entity_foreign_library(ctx, e); - - auto *fp = &ctx->checker->info.foreigns; - StringHashKey key = string_hash_string(name); - Entity **found = string_map_get(fp, key); - if (found) { - Entity *f = *found; - TokenPos pos = f->token.pos; - Type *this_type = base_type(e->type); - Type *other_type = base_type(f->type); - if (!are_types_identical(this_type, other_type)) { - error(e->token, - "Foreign entity '%.*s' previously declared elsewhere with a different type\n" - "\tat %s", - LIT(name), token_pos_to_string(pos)); - } - } else { - string_map_set(fp, key, e); - } - } else if (e->flags & EntityFlag_Static) { - if (vd->values.count > 0) { - if (entity_count != vd->values.count) { - error(e->token, "A static variable declaration with a default value must be constant"); - } else { - Ast *value = vd->values[i]; - if (value->tav.mode != Addressing_Constant) { - error(e->token, "A static variable declaration with a default value must be constant"); - } - } - } - } - add_entity(ctx, ctx->scope, e->identifier, e); - } - - if (vd->is_using != 0) { - Token token = ast_token(node); - if (vd->type != nullptr && entity_count > 1) { - error(token, "'using' can only be applied to one variable of the same type"); - // TODO(bill): Should a 'continue' happen here? - } - - for (isize entity_index = 0; entity_index < 1; entity_index++) { - Entity *e = entities[entity_index]; - if (e == nullptr) { - continue; - } - if (e->kind != Entity_Variable) { - continue; - } - String name = e->token.string; - Type *t = base_type(type_deref(e->type)); - - if (is_blank_ident(name)) { - error(token, "'using' cannot be applied variable declared as '_'"); - } else if (is_type_struct(t) || is_type_raw_union(t)) { - ERROR_BLOCK(); - - Scope *scope = t->Struct.scope; - GB_ASSERT(scope != nullptr); - for_array(i, scope->elements.entries) { - Entity *f = scope->elements.entries[i].value; - if (f->kind == Entity_Variable) { - Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, nullptr); - uvar->flags |= (e->flags & EntityFlag_Value); - Entity *prev = scope_insert(ctx->scope, uvar); - if (prev != nullptr) { - error(token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(name), LIT(prev->token.string)); - return; - } - } - } - - add_entity_use(ctx, nullptr, e); - } else { - // NOTE(bill): skip the rest to remove extra errors - error(token, "'using' can only be applied to variables of type struct or raw_union"); - return; - } - } - } - - } else { - // constant value declaration - // NOTE(bill): Check `_` declarations - for_array(i, vd->names) { - Ast *name = vd->names[i]; - if (is_blank_ident(name)) { - Entity *e = name->Ident.entity; - DeclInfo *d = decl_info_of_entity(e); - if (d != nullptr) { - check_entity_decl(ctx, e, d, nullptr); - } - } - } - - } + check_value_decl_stmt(ctx, node, mod_flags); case_end; } } |