mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-15 18:32:22 -07:00
Remove using in arrays; Remove _ non-exported struct fields
Start determining slow parts of the compiler
This commit is contained in:
+202
-198
@@ -41,7 +41,6 @@ void check_stmt_list(Checker *c, Array<AstNode *> stmts, u32 flags) {
|
||||
|
||||
check_stmt(c, n, new_flags);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool check_is_terminating_list(Array<AstNode *> stmts) {
|
||||
@@ -576,6 +575,207 @@ bool check_using_stmt_entity(Checker *c, AstNodeUsingStmt *us, AstNode *expr, bo
|
||||
return true;
|
||||
}
|
||||
|
||||
void check_switch_stmt(Checker *c, AstNode *node, u32 mod_flags) {
|
||||
ast_node(ss, SwitchStmt, node);
|
||||
|
||||
Operand x = {};
|
||||
|
||||
mod_flags |= Stmt_BreakAllowed | Stmt_FallthroughAllowed;
|
||||
check_open_scope(c, node);
|
||||
defer (check_close_scope(c));
|
||||
|
||||
check_label(c, ss->label); // TODO(bill): What should the label's "scope" be?
|
||||
|
||||
if (ss->init != nullptr) {
|
||||
check_stmt(c, ss->init, 0);
|
||||
}
|
||||
if (ss->tag != nullptr) {
|
||||
check_expr(c, &x, ss->tag);
|
||||
check_assignment(c, &x, nullptr, str_lit("switch expression"));
|
||||
} else {
|
||||
x.mode = Addressing_Constant;
|
||||
x.type = t_bool;
|
||||
x.value = exact_value_bool(true);
|
||||
|
||||
Token token = {};
|
||||
token.pos = ast_node_token(ss->body).pos;
|
||||
token.string = str_lit("true");
|
||||
x.expr = ast_ident(c->curr_ast_file, token);
|
||||
}
|
||||
if (is_type_vector(x.type)) {
|
||||
gbString str = type_to_string(x.type);
|
||||
error(x.expr, "Invalid switch expression type: %s", str);
|
||||
gb_string_free(str);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): Check for multiple defaults
|
||||
AstNode *first_default = nullptr;
|
||||
ast_node(bs, BlockStmt, ss->body);
|
||||
for_array(i, bs->stmts) {
|
||||
AstNode *stmt = bs->stmts[i];
|
||||
AstNode *default_stmt = nullptr;
|
||||
if (stmt->kind == AstNode_CaseClause) {
|
||||
ast_node(cc, CaseClause, stmt);
|
||||
if (cc->list.count == 0) {
|
||||
default_stmt = stmt;
|
||||
}
|
||||
} else {
|
||||
error(stmt, "Invalid AST - expected case clause");
|
||||
}
|
||||
|
||||
if (default_stmt != nullptr) {
|
||||
if (first_default != nullptr) {
|
||||
TokenPos pos = ast_node_token(first_default).pos;
|
||||
error(stmt,
|
||||
"multiple default clauses\n"
|
||||
"\tfirst at %.*s(%td:%td)",
|
||||
LIT(pos.file), pos.line, pos.column);
|
||||
} else {
|
||||
first_default = default_stmt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<TypeAndToken> seen = {}; // NOTE(bill): Multimap
|
||||
map_init(&seen, heap_allocator());
|
||||
defer (map_destroy(&seen));
|
||||
|
||||
for_array(stmt_index, bs->stmts) {
|
||||
AstNode *stmt = bs->stmts[stmt_index];
|
||||
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 = unparen_expr(cc->list[j]);
|
||||
|
||||
if (is_ast_node_a_range(expr)) {
|
||||
ast_node(ie, BinaryExpr, expr);
|
||||
Operand lhs = {};
|
||||
Operand rhs = {};
|
||||
check_expr(c, &lhs, ie->left);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
if (lhs.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
check_expr(c, &rhs, ie->right);
|
||||
if (rhs.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_type_ordered(x.type)) {
|
||||
gbString str = type_to_string(x.type);
|
||||
error(expr, "Unordered type '%s', is invalid for an interval expression", str);
|
||||
gb_string_free(str);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
TokenKind op = Token_Invalid;
|
||||
|
||||
Operand a = lhs;
|
||||
Operand b = rhs;
|
||||
check_comparison(c, &a, &x, Token_LtEq);
|
||||
if (a.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
switch (ie->op.kind) {
|
||||
case Token_Ellipsis: op = Token_GtEq; break;
|
||||
case Token_HalfClosed: op = Token_Gt; break;
|
||||
default: error(ie->op, "Invalid interval operator"); continue;
|
||||
}
|
||||
|
||||
check_comparison(c, &b, &x, op);
|
||||
if (b.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (ie->op.kind) {
|
||||
case Token_Ellipsis: op = Token_LtEq; break;
|
||||
case Token_HalfClosed: op = Token_Lt; break;
|
||||
default: error(ie->op, "Invalid interval operator"); continue;
|
||||
}
|
||||
|
||||
Operand a1 = lhs;
|
||||
Operand b1 = rhs;
|
||||
check_comparison(c, &a1, &b1, op);
|
||||
} else {
|
||||
Operand y = {};
|
||||
check_expr(c, &y, expr);
|
||||
|
||||
if (x.mode == Addressing_Invalid ||
|
||||
y.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
convert_to_typed(c, &y, x.type);
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE(bill): the ordering here matters
|
||||
Operand z = y;
|
||||
check_comparison(c, &z, &x, Token_CmpEq);
|
||||
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_get(&seen, key);
|
||||
if (found != nullptr) {
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||
defer (gb_temp_arena_memory_end(tmp));
|
||||
|
||||
isize count = multi_map_count(&seen, key);
|
||||
TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count);
|
||||
|
||||
multi_map_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(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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (continue_outer) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
TypeAndToken tap = {y.type, ast_node_token(y.expr)};
|
||||
multi_map_insert(&seen, key, tap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check_open_scope(c, stmt);
|
||||
check_stmt_list(c, cc->stmts, mod_flags);
|
||||
check_close_scope(c);
|
||||
}
|
||||
}
|
||||
|
||||
void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
|
||||
switch (node->kind) {
|
||||
@@ -940,7 +1140,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
check_assignment(c, &operands[i], e->type, str_lit("return statement"));
|
||||
}
|
||||
}
|
||||
|
||||
case_end;
|
||||
|
||||
case_ast_node(fs, ForStmt, node);
|
||||
@@ -1193,202 +1392,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(ss, SwitchStmt, node);
|
||||
Operand x = {};
|
||||
|
||||
mod_flags |= Stmt_BreakAllowed | Stmt_FallthroughAllowed;
|
||||
check_open_scope(c, node);
|
||||
defer (check_close_scope(c));
|
||||
|
||||
check_label(c, ss->label); // TODO(bill): What should the label's "scope" be?
|
||||
|
||||
if (ss->init != nullptr) {
|
||||
check_stmt(c, ss->init, 0);
|
||||
}
|
||||
if (ss->tag != nullptr) {
|
||||
check_expr(c, &x, ss->tag);
|
||||
check_assignment(c, &x, nullptr, str_lit("switch expression"));
|
||||
} else {
|
||||
x.mode = Addressing_Constant;
|
||||
x.type = t_bool;
|
||||
x.value = exact_value_bool(true);
|
||||
|
||||
Token token = {};
|
||||
token.pos = ast_node_token(ss->body).pos;
|
||||
token.string = str_lit("true");
|
||||
x.expr = ast_ident(c->curr_ast_file, token);
|
||||
}
|
||||
if (is_type_vector(x.type)) {
|
||||
gbString str = type_to_string(x.type);
|
||||
error(x.expr, "Invalid switch expression type: %s", str);
|
||||
gb_string_free(str);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): Check for multiple defaults
|
||||
AstNode *first_default = nullptr;
|
||||
ast_node(bs, BlockStmt, ss->body);
|
||||
for_array(i, bs->stmts) {
|
||||
AstNode *stmt = bs->stmts[i];
|
||||
AstNode *default_stmt = nullptr;
|
||||
if (stmt->kind == AstNode_CaseClause) {
|
||||
ast_node(cc, CaseClause, stmt);
|
||||
if (cc->list.count == 0) {
|
||||
default_stmt = stmt;
|
||||
}
|
||||
} else {
|
||||
error(stmt, "Invalid AST - expected case clause");
|
||||
}
|
||||
|
||||
if (default_stmt != nullptr) {
|
||||
if (first_default != nullptr) {
|
||||
TokenPos pos = ast_node_token(first_default).pos;
|
||||
error(stmt,
|
||||
"multiple default clauses\n"
|
||||
"\tfirst at %.*s(%td:%td)",
|
||||
LIT(pos.file), pos.line, pos.column);
|
||||
} else {
|
||||
first_default = default_stmt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<TypeAndToken> seen = {}; // NOTE(bill): Multimap
|
||||
map_init(&seen, heap_allocator());
|
||||
defer (map_destroy(&seen));
|
||||
|
||||
for_array(stmt_index, bs->stmts) {
|
||||
AstNode *stmt = bs->stmts[stmt_index];
|
||||
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 = unparen_expr(cc->list[j]);
|
||||
|
||||
if (is_ast_node_a_range(expr)) {
|
||||
ast_node(ie, BinaryExpr, expr);
|
||||
Operand lhs = {};
|
||||
Operand rhs = {};
|
||||
check_expr(c, &lhs, ie->left);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
if (lhs.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
check_expr(c, &rhs, ie->right);
|
||||
if (rhs.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_type_ordered(x.type)) {
|
||||
gbString str = type_to_string(x.type);
|
||||
error(expr, "Unordered type '%s', is invalid for an interval expression", str);
|
||||
gb_string_free(str);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
TokenKind op = Token_Invalid;
|
||||
|
||||
Operand a = lhs;
|
||||
Operand b = rhs;
|
||||
check_comparison(c, &a, &x, Token_LtEq);
|
||||
if (a.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
switch (ie->op.kind) {
|
||||
case Token_Ellipsis: op = Token_GtEq; break;
|
||||
case Token_HalfClosed: op = Token_Gt; break;
|
||||
default: error(ie->op, "Invalid interval operator"); continue;
|
||||
}
|
||||
|
||||
check_comparison(c, &b, &x, op);
|
||||
if (b.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (ie->op.kind) {
|
||||
case Token_Ellipsis: op = Token_LtEq; break;
|
||||
case Token_HalfClosed: op = Token_Lt; break;
|
||||
default: error(ie->op, "Invalid interval operator"); continue;
|
||||
}
|
||||
|
||||
Operand a1 = lhs;
|
||||
Operand b1 = rhs;
|
||||
check_comparison(c, &a1, &b1, op);
|
||||
} else {
|
||||
Operand y = {};
|
||||
check_expr(c, &y, expr);
|
||||
|
||||
if (x.mode == Addressing_Invalid ||
|
||||
y.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
convert_to_typed(c, &y, x.type);
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE(bill): the ordering here matters
|
||||
Operand z = y;
|
||||
check_comparison(c, &z, &x, Token_CmpEq);
|
||||
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_get(&seen, key);
|
||||
if (found != nullptr) {
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||
defer (gb_temp_arena_memory_end(tmp));
|
||||
|
||||
isize count = multi_map_count(&seen, key);
|
||||
TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count);
|
||||
|
||||
multi_map_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(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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (continue_outer) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
TypeAndToken tap = {y.type, ast_node_token(y.expr)};
|
||||
multi_map_insert(&seen, key, tap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check_open_scope(c, stmt);
|
||||
check_stmt_list(c, cc->stmts, mod_flags);
|
||||
check_close_scope(c);
|
||||
}
|
||||
check_switch_stmt(c, node, mod_flags);
|
||||
case_end;
|
||||
|
||||
case_ast_node(ss, TypeSwitchStmt, node);
|
||||
|
||||
Reference in New Issue
Block a user