aboutsummaryrefslogtreecommitdiff
path: root/src/checker/stmt.cpp
diff options
context:
space:
mode:
authorGinger Bill <bill@gingerbill.org>2016-11-23 12:29:50 +0000
committerGinger Bill <bill@gingerbill.org>2016-11-23 12:29:50 +0000
commit4d30ef7eda0021f0cb827c7a218ef3afc6ce8b55 (patch)
tree9f31e2b07cf610300c1db3c511b84d9d2cc72d6b /src/checker/stmt.cpp
parenta77c6b3e55c5857c9c0ba36baae2dbdcd7564cd4 (diff)
Change extensions .cpp to .c
Diffstat (limited to 'src/checker/stmt.cpp')
-rw-r--r--src/checker/stmt.cpp1130
1 files changed, 0 insertions, 1130 deletions
diff --git a/src/checker/stmt.cpp b/src/checker/stmt.cpp
deleted file mode 100644
index 129f13607..000000000
--- a/src/checker/stmt.cpp
+++ /dev/null
@@ -1,1130 +0,0 @@
-bool check_is_terminating(AstNode *node);
-bool check_has_break (AstNode *stmt, bool implicit);
-void check_stmt (Checker *c, AstNode *node, u32 flags);
-
-
-// Statements and Declarations
-typedef enum StmtFlag {
- Stmt_BreakAllowed = GB_BIT(0),
- Stmt_ContinueAllowed = GB_BIT(1),
- Stmt_FallthroughAllowed = GB_BIT(2), // TODO(bill): fallthrough
-} StmtFlag;
-
-
-
-void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
- if (stmts.count == 0) {
- return;
- }
-
- gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
-
- typedef struct {
- Entity *e;
- DeclInfo *d;
- } Delay;
- Array(Delay) delayed_const; array_init_reserve(&delayed_const, c->tmp_allocator, stmts.count);
- Array(Delay) delayed_type; array_init_reserve(&delayed_type, c->tmp_allocator, stmts.count);
-
- for_array(i, stmts) {
- AstNode *node = stmts.e[i];
- switch (node->kind) {
- case_ast_node(cd, ConstDecl, node);
- for_array(i, cd->values) {
- AstNode *name = cd->names.e[i];
- AstNode *value = cd->values.e[i];
- ExactValue v = {ExactValue_Invalid};
-
- Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v);
- e->identifier = name;
-
- DeclInfo *d = make_declaration_info(c->allocator, e->scope);
- d->type_expr = cd->type;
- d->init_expr = value;
-
- add_entity_and_decl_info(c, name, e, d);
-
- Delay delay = {e, d};
- array_add(&delayed_const, delay);
- }
-
- isize lhs_count = cd->names.count;
- isize rhs_count = cd->values.count;
-
- if (rhs_count == 0 && cd->type == NULL) {
- error(ast_node_token(node), "Missing type or initial expression");
- } else if (lhs_count < rhs_count) {
- error(ast_node_token(node), "Extra initial expression");
- }
- case_end;
-
- case_ast_node(td, TypeDecl, node);
- Entity *e = make_entity_type_name(c->allocator, c->context.scope, td->name->Ident, NULL);
- e->identifier = td->name;
-
- DeclInfo *d = make_declaration_info(c->allocator, e->scope);
- d->type_expr = td->type;
-
- add_entity_and_decl_info(c, td->name, e, d);
-
- Delay delay = {e, d};
- array_add(&delayed_type, delay);
- case_end;
- }
- }
-
- for_array(i, delayed_type) {
- check_entity_decl(c, delayed_type.e[i].e, delayed_type.e[i].d, NULL, NULL);
- }
- for_array(i, delayed_const) {
- check_entity_decl(c, delayed_const.e[i].e, delayed_const.e[i].d, NULL, NULL);
- }
-
- bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
- u32 f = flags & (~Stmt_FallthroughAllowed);
-
- for_array(i, stmts) {
- AstNode *n = stmts.e[i];
- if (n->kind == AstNode_EmptyStmt) {
- continue;
- }
- u32 new_flags = f;
- if (ft_ok && i+1 == stmts.count) {
- new_flags |= Stmt_FallthroughAllowed;
- }
- check_stmt(c, n, new_flags);
- }
-
- gb_temp_arena_memory_end(tmp);
-}
-
-bool check_is_terminating_list(AstNodeArray stmts) {
-
- // Iterate backwards
- for (isize n = stmts.count-1; n >= 0; n--) {
- AstNode *stmt = stmts.e[n];
- if (stmt->kind != AstNode_EmptyStmt) {
- return check_is_terminating(stmt);
- }
- }
-
- return false;
-}
-
-bool check_has_break_list(AstNodeArray stmts, bool implicit) {
- for_array(i, stmts) {
- AstNode *stmt = stmts.e[i];
- if (check_has_break(stmt, implicit)) {
- return true;
- }
- }
- return false;
-}
-
-
-bool check_has_break(AstNode *stmt, bool implicit) {
- switch (stmt->kind) {
- case AstNode_BranchStmt:
- if (stmt->BranchStmt.token.kind == Token_break) {
- return implicit;
- }
- break;
- case AstNode_BlockStmt:
- return check_has_break_list(stmt->BlockStmt.stmts, implicit);
-
- case AstNode_IfStmt:
- if (check_has_break(stmt->IfStmt.body, implicit) ||
- (stmt->IfStmt.else_stmt != NULL && check_has_break(stmt->IfStmt.else_stmt, implicit))) {
- return true;
- }
- break;
-
- case AstNode_CaseClause:
- return check_has_break_list(stmt->CaseClause.stmts, implicit);
- }
-
- return false;
-}
-
-
-
-// NOTE(bill): The last expression has to be a `return` statement
-// TODO(bill): This is a mild hack and should be probably handled properly
-// TODO(bill): Warn/err against code after `return` that it won't be executed
-bool check_is_terminating(AstNode *node) {
- switch (node->kind) {
- case_ast_node(rs, ReturnStmt, node);
- return true;
- case_end;
-
- case_ast_node(bs, BlockStmt, node);
- return check_is_terminating_list(bs->stmts);
- case_end;
-
- case_ast_node(es, ExprStmt, node);
- return check_is_terminating(es->expr);
- case_end;
-
- case_ast_node(is, IfStmt, node);
- if (is->else_stmt != NULL) {
- if (check_is_terminating(is->body) &&
- check_is_terminating(is->else_stmt)) {
- return true;
- }
- }
- case_end;
-
- case_ast_node(fs, ForStmt, node);
- if (fs->cond == NULL && !check_has_break(fs->body, true)) {
- return true;
- }
- case_end;
-
- case_ast_node(ms, MatchStmt, node);
- bool has_default = false;
- for_array(i, ms->body->BlockStmt.stmts) {
- AstNode *clause = ms->body->BlockStmt.stmts.e[i];
- ast_node(cc, CaseClause, clause);
- if (cc->list.count == 0) {
- has_default = true;
- }
- if (!check_is_terminating_list(cc->stmts) ||
- check_has_break_list(cc->stmts, true)) {
- return false;
- }
- }
- return has_default;
- case_end;
-
- case_ast_node(ms, TypeMatchStmt, node);
- bool has_default = false;
- for_array(i, ms->body->BlockStmt.stmts) {
- AstNode *clause = ms->body->BlockStmt.stmts.e[i];
- ast_node(cc, CaseClause, clause);
- if (cc->list.count == 0) {
- has_default = true;
- }
- if (!check_is_terminating_list(cc->stmts) ||
- check_has_break_list(cc->stmts, true)) {
- return false;
- }
- }
- return has_default;
- case_end;
-
- case_ast_node(pa, PushAllocator, node);
- return check_is_terminating(pa->body);
- case_end;
- case_ast_node(pc, PushContext, node);
- return check_is_terminating(pc->body);
- case_end;
- }
-
- return false;
-}
-
-Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
- if (op_a->mode == Addressing_Invalid ||
- op_a->type == t_invalid) {
- return NULL;
- }
-
- AstNode *node = unparen_expr(lhs);
-
- // NOTE(bill): Ignore assignments to `_`
- if (node->kind == AstNode_Ident &&
- str_eq(node->Ident.string, str_lit("_"))) {
- add_entity_definition(&c->info, node, NULL);
- check_assignment(c, op_a, NULL, str_lit("assignment to `_` identifier"));
- if (op_a->mode == Addressing_Invalid)
- return NULL;
- return op_a->type;
- }
-
- Entity *e = NULL;
- bool used = false;
- if (node->kind == AstNode_Ident) {
- ast_node(i, Ident, node);
- e = scope_lookup_entity(c->context.scope, i->string);
- if (e != NULL && e->kind == Entity_Variable) {
- used = (e->flags & EntityFlag_Used) != 0; // TODO(bill): Make backup just in case
- }
- }
-
-
- Operand op_b = {Addressing_Invalid};
- check_expr(c, &op_b, lhs);
- if (e) {
- e->flags |= EntityFlag_Used*used;
- }
-
- if (op_b.mode == Addressing_Invalid ||
- op_b.type == t_invalid) {
- return NULL;
- }
-
- switch (op_b.mode) {
- case Addressing_Invalid:
- return NULL;
- case Addressing_Variable:
- break;
- default: {
- if (op_b.expr->kind == AstNode_SelectorExpr) {
- // NOTE(bill): Extra error checks
- Operand op_c = {Addressing_Invalid};
- ast_node(se, SelectorExpr, op_b.expr);
- check_expr(c, &op_c, se->expr);
- }
-
- gbString str = expr_to_string(op_b.expr);
- switch (op_b.mode) {
- case Addressing_Value:
- error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str);
- break;
- default:
- error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str);
- break;
- }
- gb_string_free(str);
- } break;
- }
-
- check_assignment(c, op_a, op_b.type, str_lit("assignment"));
- if (op_a->mode == Addressing_Invalid) {
- return NULL;
- }
-
- return op_a->type;
-}
-
-bool check_valid_type_match_type(Type *type, bool *is_union_ptr, bool *is_any) {
- if (is_type_pointer(type)) {
- *is_union_ptr = is_type_union(type_deref(type));
- return *is_union_ptr;
- }
- if (is_type_any(type)) {
- *is_any = true;
- return *is_any;
- }
- return false;
-}
-
-void check_stmt_internal(Checker *c, AstNode *node, u32 flags);
-void check_stmt(Checker *c, AstNode *node, u32 flags) {
- u32 prev_stmt_state_flags = c->context.stmt_state_flags;
-
- if (node->stmt_state_flags != 0) {
- u32 in = node->stmt_state_flags;
- u32 out = c->context.stmt_state_flags;
-
- if (in & StmtStateFlag_bounds_check) {
- out |= StmtStateFlag_bounds_check;
- out &= ~StmtStateFlag_no_bounds_check;
- } else if (in & StmtStateFlag_no_bounds_check) {
- out |= StmtStateFlag_no_bounds_check;
- out &= ~StmtStateFlag_bounds_check;
- }
-
- c->context.stmt_state_flags = out;
- }
-
- check_stmt_internal(c, node, flags);
-
- c->context.stmt_state_flags = prev_stmt_state_flags;
-}
-
-typedef struct TypeAndToken {
- Type *type;
- Token token;
-} TypeAndToken;
-
-#define MAP_TYPE TypeAndToken
-#define MAP_FUNC map_type_and_token_
-#define MAP_NAME MapTypeAndToken
-#include "../map.c"
-
-void check_stmt_internal(Checker *c, AstNode *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)
- Operand operand = {Addressing_Invalid};
- ExprKind kind = check_expr_base(c, &operand, es->expr, NULL);
- switch (operand.mode) {
- case Addressing_Type:
- error(ast_node_token(node), "Is not an expression");
- break;
- case Addressing_NoValue:
- return;
- default: {
- if (kind == Expr_Stmt) {
- return;
- }
- if (operand.expr->kind == AstNode_CallExpr) {
- return;
- }
- gbString expr_str = expr_to_string(operand.expr);
- error(ast_node_token(node), "Expression is not used: `%s`", expr_str);
- gb_string_free(expr_str);
- } break;
- }
- case_end;
-
- case_ast_node(ts, TagStmt, node);
- // TODO(bill): Tag Statements
- error(ast_node_token(node), "Tag statements are not supported yet");
- check_stmt(c, ts->stmt, flags);
- case_end;
-
- case_ast_node(ids, IncDecStmt, node);
- Token op = ids->op;
- switch (ids->op.kind) {
- case Token_Increment:
- op.kind = Token_Add;
- op.string.len = 1;
- break;
- case Token_Decrement:
- op.kind = Token_Sub;
- op.string.len = 1;
- break;
- default:
- error(ids->op, "Unknown inc/dec operation %.*s", LIT(ids->op.string));
- return;
- }
-
- Operand operand = {Addressing_Invalid};
- check_expr(c, &operand, ids->expr);
- if (operand.mode == Addressing_Invalid)
- return;
- if (!is_type_numeric(operand.type)) {
- error(ids->op, "Non numeric type");
- return;
- }
-
- AstNode basic_lit = {AstNode_BasicLit};
- ast_node(bl, BasicLit, &basic_lit);
- *bl = ids->op;
- bl->kind = Token_Integer;
- bl->string = str_lit("1");
-
- AstNode binary_expr = {AstNode_BinaryExpr};
- ast_node(be, BinaryExpr, &binary_expr);
- be->op = op;
- be->left = ids->expr;
- be->right = &basic_lit;
- check_binary_expr(c, &operand, &binary_expr);
- case_end;
-
- case_ast_node(as, AssignStmt, node);
- switch (as->op.kind) {
- case Token_Eq: {
- // a, b, c = 1, 2, 3; // Multisided
- if (as->lhs.count == 0) {
- error(as->op, "Missing lhs in assignment statement");
- return;
- }
-
- gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
-
- // NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
- // an extra allocation
- Array(Operand) operands;
- array_init_reserve(&operands, c->tmp_allocator, 2 * as->lhs.count);
-
- for_array(i, as->rhs) {
- AstNode *rhs = as->rhs.e[i];
- Operand o = {0};
- check_multi_expr(c, &o, rhs);
- if (o.type->kind != Type_Tuple) {
- array_add(&operands, o);
- } else {
- TypeTuple *tuple = &o.type->Tuple;
- for (isize j = 0; j < tuple->variable_count; j++) {
- o.type = tuple->variables[j]->type;
- array_add(&operands, o);
- }
- }
- }
-
- isize lhs_count = as->lhs.count;
- isize rhs_count = operands.count;
-
- isize operand_count = gb_min(as->lhs.count, operands.count);
- for (isize i = 0; i < operand_count; i++) {
- AstNode *lhs = as->lhs.e[i];
- check_assignment_variable(c, &operands.e[i], lhs);
- }
- if (lhs_count != rhs_count) {
- error(ast_node_token(as->lhs.e[0]), "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
- }
-
- gb_temp_arena_memory_end(tmp);
- } break;
-
- 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;
- }
- // TODO(bill): Check if valid assignment operator
- Operand operand = {Addressing_Invalid};
- AstNode binary_expr = {AstNode_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.e[0];
- be->right = as->rhs.e[0];
-
- check_binary_expr(c, &operand, &binary_expr);
- if (operand.mode == Addressing_Invalid) {
- return;
- }
- // NOTE(bill): Only use the first one will be used
- check_assignment_variable(c, &operand, as->lhs.e[0]);
- } break;
- }
- case_end;
-
- case_ast_node(bs, BlockStmt, node);
- check_open_scope(c, node);
- check_stmt_list(c, bs->stmts, mod_flags);
- check_close_scope(c);
- case_end;
-
- case_ast_node(is, IfStmt, node);
- check_open_scope(c, node);
-
- if (is->init != NULL) {
- check_stmt(c, is->init, 0);
- }
-
- Operand operand = {Addressing_Invalid};
- check_expr(c, &operand, is->cond);
- if (operand.mode != Addressing_Invalid &&
- !is_type_boolean(operand.type)) {
- error(ast_node_token(is->cond),
- "Non-boolean condition in `if` statement");
- }
-
- check_stmt(c, is->body, mod_flags);
-
- if (is->else_stmt) {
- switch (is->else_stmt->kind) {
- case AstNode_IfStmt:
- case AstNode_BlockStmt:
- check_stmt(c, is->else_stmt, mod_flags);
- break;
- default:
- error(ast_node_token(is->else_stmt),
- "Invalid `else` statement in `if` statement");
- break;
- }
- }
-
- check_close_scope(c);
- case_end;
-
- case_ast_node(rs, ReturnStmt, node);
- GB_ASSERT(c->proc_stack.count > 0);
-
- if (c->in_defer) {
- error(rs->token, "You cannot `return` within a defer statement");
- // TODO(bill): Should I break here?
- break;
- }
-
-
- Type *proc_type = c->proc_stack.e[c->proc_stack.count-1];
- isize result_count = 0;
- if (proc_type->Proc.results) {
- result_count = proc_type->Proc.results->Tuple.variable_count;
- }
-
- if (result_count > 0) {
- Entity **variables = NULL;
- if (proc_type->Proc.results != NULL) {
- TypeTuple *tuple = &proc_type->Proc.results->Tuple;
- variables = tuple->variables;
- }
- if (rs->results.count == 0) {
- error(ast_node_token(node), "Expected %td return values, got 0", result_count);
- } else {
- check_init_variables(c, variables, result_count,
- rs->results, str_lit("return statement"));
- }
- } else if (rs->results.count > 0) {
- error(ast_node_token(rs->results.e[0]), "No return values expected");
- }
- case_end;
-
- case_ast_node(fs, ForStmt, node);
- u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
- check_open_scope(c, node);
-
- if (fs->init != NULL) {
- check_stmt(c, fs->init, 0);
- }
- if (fs->cond) {
- Operand operand = {Addressing_Invalid};
- check_expr(c, &operand, fs->cond);
- if (operand.mode != Addressing_Invalid &&
- !is_type_boolean(operand.type)) {
- error(ast_node_token(fs->cond),
- "Non-boolean condition in `for` statement");
- }
- }
- if (fs->post != NULL) {
- check_stmt(c, fs->post, 0);
- }
- check_stmt(c, fs->body, new_flags);
-
- check_close_scope(c);
- case_end;
-
- case_ast_node(ms, MatchStmt, node);
- Operand x = {0};
-
- mod_flags |= Stmt_BreakAllowed;
- check_open_scope(c, node);
-
- if (ms->init != NULL) {
- check_stmt(c, ms->init, 0);
- }
- if (ms->tag != NULL) {
- check_expr(c, &x, ms->tag);
- check_assignment(c, &x, NULL, str_lit("match expression"));
- } else {
- x.mode = Addressing_Constant;
- x.type = t_bool;
- x.value = make_exact_value_bool(true);
-
- Token token = {0};
- token.pos = ast_node_token(ms->body).pos;
- token.string = str_lit("true");
- x.expr = make_ident(c->curr_ast_file, token);
- }
-
- // NOTE(bill): Check for multiple defaults
- AstNode *first_default = NULL;
- ast_node(bs, BlockStmt, ms->body);
- for_array(i, bs->stmts) {
- AstNode *stmt = bs->stmts.e[i];
- AstNode *default_stmt = NULL;
- if (stmt->kind == AstNode_CaseClause) {
- ast_node(cc, CaseClause, stmt);
- if (cc->list.count == 0) {
- default_stmt = stmt;
- }
- } else {
- error(ast_node_token(stmt), "Invalid AST - expected case clause");
- }
-
- if (default_stmt != NULL) {
- if (first_default != NULL) {
- TokenPos pos = ast_node_token(first_default).pos;
- error(ast_node_token(stmt),
- "multiple `default` clauses\n"
- "\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
- } else {
- first_default = default_stmt;
- }
- }
- }
-;
-
- MapTypeAndToken seen = {0}; // NOTE(bill): Multimap
- map_type_and_token_init(&seen, heap_allocator());
-
- for_array(i, bs->stmts) {
- AstNode *stmt = bs->stmts.e[i];
- if (stmt->kind != AstNode_CaseClause) {
- // NOTE(bill): error handled by above multiple default checker
- continue;
- }
- ast_node(cc, CaseClause, stmt);
-
-
- for_array(j, cc->list) {
- AstNode *expr = cc->list.e[j];
- Operand y = {0};
- Operand z = {0};
- Token eq = {Token_CmpEq};
-
- check_expr(c, &y, expr);
- if (x.mode == Addressing_Invalid ||
- y.mode == Addressing_Invalid) {
- continue;
- }
- convert_to_typed(c, &y, x.type, 0);
- if (y.mode == Addressing_Invalid) {
- continue;
- }
-
- z = y;
- check_comparison(c, &z, &x, eq);
- if (z.mode == Addressing_Invalid) {
- continue;
- }
- if (y.mode != Addressing_Constant) {
- continue;
- }
-
- if (y.value.kind != ExactValue_Invalid) {
- HashKey key = hash_exact_value(y.value);
- TypeAndToken *found = map_type_and_token_get(&seen, key);
- if (found != NULL) {
- gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
- isize count = map_type_and_token_multi_count(&seen, key);
- TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count);
-
- map_type_and_token_multi_get_all(&seen, key, taps);
- bool continue_outer = false;
-
- for (isize i = 0; i < count; i++) {
- TypeAndToken tap = taps[i];
- if (are_types_identical(y.type, tap.type)) {
- TokenPos pos = tap.token.pos;
- gbString expr_str = expr_to_string(y.expr);
- error(ast_node_token(y.expr),
- "Duplicate case `%s`\n"
- "\tprevious case at %.*s(%td:%td)",
- expr_str,
- LIT(pos.file), pos.line, pos.column);
- gb_string_free(expr_str);
- continue_outer = true;
- break;
- }
- }
-
- gb_temp_arena_memory_end(tmp);
-
- if (continue_outer) {
- continue;
- }
- }
- TypeAndToken tap = {y.type, ast_node_token(y.expr)};
- map_type_and_token_multi_insert(&seen, key, tap);
- }
- }
-
- check_open_scope(c, stmt);
- u32 ft_flags = mod_flags;
- if (i+1 < bs->stmts.count) {
- ft_flags |= Stmt_FallthroughAllowed;
- }
- check_stmt_list(c, cc->stmts, ft_flags);
- check_close_scope(c);
- }
-
- map_type_and_token_destroy(&seen);
-
- check_close_scope(c);
- case_end;
-
- case_ast_node(ms, TypeMatchStmt, node);
- Operand x = {0};
-
- mod_flags |= Stmt_BreakAllowed;
- check_open_scope(c, node);
-
- bool is_union_ptr = false;
- bool is_any = false;
-
- check_expr(c, &x, ms->tag);
- check_assignment(c, &x, NULL, str_lit("type match expression"));
- if (!check_valid_type_match_type(x.type, &is_union_ptr, &is_any)) {
- gbString str = type_to_string(x.type);
- error(ast_node_token(x.expr),
- "Invalid type for this type match expression, got `%s`", str);
- gb_string_free(str);
- break;
- }
-
-
- // NOTE(bill): Check for multiple defaults
- AstNode *first_default = NULL;
- ast_node(bs, BlockStmt, ms->body);
- for_array(i, bs->stmts) {
- AstNode *stmt = bs->stmts.e[i];
- AstNode *default_stmt = NULL;
- if (stmt->kind == AstNode_CaseClause) {
- ast_node(cc, CaseClause, stmt);
- if (cc->list.count == 0) {
- default_stmt = stmt;
- }
- } else {
- error(ast_node_token(stmt), "Invalid AST - expected case clause");
- }
-
- if (default_stmt != NULL) {
- if (first_default != NULL) {
- TokenPos pos = ast_node_token(first_default).pos;
- error(ast_node_token(stmt),
- "multiple `default` clauses\n"
- "\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
- } else {
- first_default = default_stmt;
- }
- }
- }
-
- if (ms->var->kind != AstNode_Ident) {
- break;
- }
-
-
- MapBool seen = {0};
- map_bool_init(&seen, heap_allocator());
-
- for_array(i, bs->stmts) {
- AstNode *stmt = bs->stmts.e[i];
- if (stmt->kind != AstNode_CaseClause) {
- // NOTE(bill): error handled by above multiple default checker
- continue;
- }
- ast_node(cc, CaseClause, stmt);
-
- // TODO(bill): Make robust
- Type *bt = base_type(type_deref(x.type));
-
-
- AstNode *type_expr = cc->list.count > 0 ? cc->list.e[0] : NULL;
- Type *case_type = NULL;
- if (type_expr != NULL) { // Otherwise it's a default expression
- Operand y = {0};
- check_expr_or_type(c, &y, type_expr);
-
- if (is_union_ptr) {
- GB_ASSERT(is_type_union(bt));
- bool tag_type_found = false;
- for (isize i = 0; i < bt->Record.field_count; i++) {
- Entity *f = bt->Record.fields[i];
- if (are_types_identical(f->type, y.type)) {
- tag_type_found = true;
- break;
- }
- }
- if (!tag_type_found) {
- gbString type_str = type_to_string(y.type);
- error(ast_node_token(y.expr),
- "Unknown tag type, got `%s`", type_str);
- gb_string_free(type_str);
- continue;
- }
- case_type = y.type;
- } else if (is_any) {
- case_type = y.type;
- } else {
- GB_PANIC("Unknown type to type match statement");
- }
-
- HashKey key = hash_pointer(y.type);
- bool *found = map_bool_get(&seen, key);
- if (found) {
- TokenPos pos = cc->token.pos;
- gbString expr_str = expr_to_string(y.expr);
- error(ast_node_token(y.expr),
- "Duplicate type case `%s`\n"
- "\tprevious type case at %.*s(%td:%td)",
- expr_str,
- LIT(pos.file), pos.line, pos.column);
- gb_string_free(expr_str);
- break;
- }
- map_bool_set(&seen, key, cast(bool)true);
- }
-
- check_open_scope(c, stmt);
- if (case_type != NULL) {
- add_type_info_type(c, case_type);
-
- // NOTE(bill): Dummy type
- Type *tt = case_type;
- if (is_union_ptr) {
- tt = make_type_pointer(c->allocator, case_type);
- add_type_info_type(c, tt);
- }
- Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, ms->var->Ident, tt);
- tag_var->flags |= EntityFlag_Used;
- add_entity(c, c->context.scope, ms->var, tag_var);
- add_entity_use(c, ms->var, tag_var);
- }
- check_stmt_list(c, cc->stmts, mod_flags);
- check_close_scope(c);
- }
- map_bool_destroy(&seen);
-
- check_close_scope(c);
- case_end;
-
-
- case_ast_node(ds, DeferStmt, node);
- if (is_ast_node_decl(ds->stmt)) {
- error(ds->token, "You cannot defer a declaration");
- } else {
- bool out_in_defer = c->in_defer;
- c->in_defer = true;
- check_stmt(c, ds->stmt, 0);
- c->in_defer = out_in_defer;
- }
- case_end;
-
- case_ast_node(bs, BranchStmt, node);
- Token token = bs->token;
- switch (token.kind) {
- case Token_break:
- if ((flags & Stmt_BreakAllowed) == 0) {
- error(token, "`break` only allowed in `for` or `match` statements");
- }
- break;
- case Token_continue:
- if ((flags & Stmt_ContinueAllowed) == 0) {
- error(token, "`continue` only allowed in `for` statements");
- }
- break;
- case Token_fallthrough:
- if ((flags & Stmt_FallthroughAllowed) == 0) {
- error(token, "`fallthrough` statement in illegal position");
- }
- break;
- default:
- error(token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
- break;
- }
- case_end;
-
- case_ast_node(us, UsingStmt, node);
- switch (us->node->kind) {
- case_ast_node(es, ExprStmt, us->node);
- // TODO(bill): Allow for just a LHS expression list rather than this silly code
- Entity *e = NULL;
-
- bool is_selector = false;
- AstNode *expr = unparen_expr(es->expr);
- if (expr->kind == AstNode_Ident) {
- String name = expr->Ident.string;
- e = scope_lookup_entity(c->context.scope, name);
- } else if (expr->kind == AstNode_SelectorExpr) {
- Operand o = {0};
- e = check_selector(c, &o, expr);
- is_selector = true;
- }
-
- if (e == NULL) {
- error(us->token, "`using` applied to an unknown entity");
- return;
- }
-
- switch (e->kind) {
- case Entity_TypeName: {
- Type *t = base_type(e->type);
- if (is_type_struct(t) || is_type_enum(t)) {
- for (isize i = 0; i < t->Record.other_field_count; i++) {
- Entity *f = t->Record.other_fields[i];
- Entity *found = scope_insert_entity(c->context.scope, f);
- if (found != NULL) {
- gbString expr_str = expr_to_string(expr);
- error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
- gb_string_free(expr_str);
- return;
- }
- f->using_parent = e;
- }
- } else if (is_type_union(t)) {
- for (isize i = 0; i < t->Record.field_count; i++) {
- Entity *f = t->Record.fields[i];
- Entity *found = scope_insert_entity(c->context.scope, f);
- if (found != NULL) {
- gbString expr_str = expr_to_string(expr);
- error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
- gb_string_free(expr_str);
- return;
- }
- f->using_parent = e;
- }
- for (isize i = 0; i < t->Record.other_field_count; i++) {
- Entity *f = t->Record.other_fields[i];
- Entity *found = scope_insert_entity(c->context.scope, f);
- if (found != NULL) {
- gbString expr_str = expr_to_string(expr);
- error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
- gb_string_free(expr_str);
- return;
- }
- f->using_parent = e;
- }
- }
- } break;
-
- case Entity_ImportName: {
- Scope *scope = e->ImportName.scope;
- for_array(i, scope->elements.entries) {
- Entity *decl = scope->elements.entries.e[i].value;
- Entity *found = scope_insert_entity(c->context.scope, decl);
- if (found != NULL) {
- gbString expr_str = expr_to_string(expr);
- error(us->token,
- "Namespace collision while `using` `%s` of: %.*s\n"
- "\tat %.*s(%td:%td)\n"
- "\tat %.*s(%td:%td)",
- expr_str, LIT(found->token.string),
- LIT(found->token.pos.file), found->token.pos.line, found->token.pos.column,
- LIT(decl->token.pos.file), decl->token.pos.line, decl->token.pos.column
- );
- gb_string_free(expr_str);
- return;
- }
- }
- } break;
-
- case Entity_Variable: {
- Type *t = base_type(type_deref(e->type));
- if (is_type_struct(t) || is_type_raw_union(t)) {
- Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
- GB_ASSERT(found != NULL);
- for_array(i, (*found)->elements.entries) {
- Entity *f = (*found)->elements.entries.e[i].value;
- if (f->kind == Entity_Variable) {
- Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
- if (is_selector) {
- uvar->using_expr = expr;
- }
- Entity *prev = scope_insert_entity(c->context.scope, uvar);
- if (prev != NULL) {
- gbString expr_str = expr_to_string(expr);
- error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(prev->token.string));
- gb_string_free(expr_str);
- return;
- }
- }
- }
- } else {
- error(us->token, "`using` can only be applied to variables of type struct or raw_union");
- return;
- }
- } break;
-
- case Entity_Constant:
- error(us->token, "`using` cannot be applied to a constant");
- break;
-
- case Entity_Procedure:
- case Entity_Builtin:
- error(us->token, "`using` cannot be applied to a procedure");
- break;
-
- case Entity_ImplicitValue:
- error(us->token, "`using` cannot be applied to an implicit value");
- break;
-
- case Entity_Nil:
- error(us->token, "`using` cannot be applied to `nil`");
- break;
-
- case Entity_Invalid:
- error(us->token, "`using` cannot be applied to an invalid entity");
- break;
-
- default:
- GB_PANIC("TODO(bill): `using` other expressions?");
- }
- case_end;
-
- case_ast_node(vd, VarDecl, us->node);
- if (vd->names.count > 1 && vd->type != NULL) {
- error(us->token, "`using` can only be applied to one variable of the same type");
- }
- check_var_decl_node(c, us->node);
-
- for_array(name_index, vd->names) {
- AstNode *item = vd->names.e[name_index];
- ast_node(i, Ident, item);
- String name = i->string;
- Entity *e = scope_lookup_entity(c->context.scope, name);
- Type *t = base_type(type_deref(e->type));
- if (is_type_struct(t) || is_type_raw_union(t)) {
- Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
- GB_ASSERT(found != NULL);
- for_array(i, (*found)->elements.entries) {
- Entity *f = (*found)->elements.entries.e[i].value;
- if (f->kind == Entity_Variable) {
- Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
- Entity *prev = scope_insert_entity(c->context.scope, uvar);
- if (prev != NULL) {
- error(us->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
- return;
- }
- }
- }
- } else {
- error(us->token, "`using` can only be applied to variables of type struct or raw_union");
- return;
- }
- }
- case_end;
-
-
- default:
- error(us->token, "Invalid AST: Using Statement");
- break;
- }
- case_end;
-
-
-
- case_ast_node(pa, PushAllocator, node);
- Operand op = {0};
- check_expr(c, &op, pa->expr);
- check_assignment(c, &op, t_allocator, str_lit("argument to push_allocator"));
- check_stmt(c, pa->body, mod_flags);
- case_end;
-
-
- case_ast_node(pa, PushContext, node);
- Operand op = {0};
- check_expr(c, &op, pa->expr);
- check_assignment(c, &op, t_context, str_lit("argument to push_context"));
- check_stmt(c, pa->body, mod_flags);
- case_end;
-
-
-
-
-
-
- case_ast_node(vd, VarDecl, node);
- check_var_decl_node(c, node);
- case_end;
-
- case_ast_node(cd, ConstDecl, node);
- // NOTE(bill): Handled elsewhere
- case_end;
-
- case_ast_node(td, TypeDecl, node);
- // NOTE(bill): Handled elsewhere
- case_end;
-
- case_ast_node(pd, ProcDecl, node);
- // NOTE(bill): This must be handled here so it has access to the parent scope stuff
- // e.g. using
- Entity *e = make_entity_procedure(c->allocator, c->context.scope, pd->name->Ident, NULL);
- e->identifier = pd->name;
-
- DeclInfo *d = make_declaration_info(c->allocator, e->scope);
- d->proc_decl = node;
-
- add_entity_and_decl_info(c, pd->name, e, d);
- check_entity_decl(c, e, d, NULL, NULL);
- case_end;
- }
-}