Remove using in arrays; Remove _ non-exported struct fields

Start determining slow parts of the compiler
This commit is contained in:
gingerBill
2017-11-28 22:12:33 +00:00
parent 91b534d128
commit cfabc0e61f
9 changed files with 299 additions and 369 deletions
+202 -198
View File
@@ -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);