mirror of
https://github.com/odin-lang/Odin.git
synced 2026-03-01 22:28:20 +00:00
Remove using in arrays; Remove _ non-exported struct fields
Start determining slow parts of the compiler
This commit is contained in:
@@ -2541,27 +2541,6 @@ isize entity_overload_count(Scope *s, String name) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool check_is_field_exported(Checker *c, Entity *field) {
|
||||
if (field == nullptr) {
|
||||
// NOTE(bill): Just incase
|
||||
return true;
|
||||
}
|
||||
if (field->kind != Entity_Variable) {
|
||||
return true;
|
||||
}
|
||||
Scope *file_scope = field->scope;
|
||||
if (file_scope == nullptr) {
|
||||
return true;
|
||||
}
|
||||
while (file_scope->file == nullptr) {
|
||||
file_scope = file_scope->parent;
|
||||
}
|
||||
if (!is_entity_exported(field) && file_scope != c->context.file_scope) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_hint) {
|
||||
ast_node(se, SelectorExpr, node);
|
||||
|
||||
@@ -2730,13 +2709,6 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
|
||||
if (entity == nullptr && selector->kind == AstNode_Ident) {
|
||||
String field_name = selector->Ident.token.string;
|
||||
sel = lookup_field(c->allocator, operand->type, field_name, operand->mode == Addressing_Type);
|
||||
|
||||
if (operand->mode != Addressing_Type && !check_is_field_exported(c, sel.entity)) {
|
||||
error(op_expr, "'%.*s' is an unexported field", LIT(field_name));
|
||||
operand->mode = Addressing_Invalid;
|
||||
operand->expr = node;
|
||||
return nullptr;
|
||||
}
|
||||
entity = sel.entity;
|
||||
|
||||
// NOTE(bill): Add type info needed for fields like 'names'
|
||||
@@ -4718,29 +4690,6 @@ CallArgumentData check_call_arguments(Checker *c, Operand *operand, Type *proc_t
|
||||
}
|
||||
|
||||
|
||||
Entity *find_using_index_expr(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t->kind != Type_Struct) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for_array(i, t->Struct.fields) {
|
||||
Entity *f = t->Struct.fields[i];
|
||||
if (f->kind == Entity_Variable &&
|
||||
(f->flags & EntityFlag_Field) != 0 &&
|
||||
(f->flags & EntityFlag_Using) != 0) {
|
||||
if (is_type_indexable(f->type)) {
|
||||
return f;
|
||||
}
|
||||
Entity *res = find_using_index_expr(f->type);
|
||||
if (res != nullptr) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
isize lookup_polymorphic_struct_parameter(TypeStruct *st, String parameter_name) {
|
||||
if (!st->is_polymorphic) return -1;
|
||||
|
||||
@@ -5531,11 +5480,6 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
|
||||
error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
|
||||
continue;
|
||||
}
|
||||
if (!is_unknown && !check_is_field_exported(c, sel.entity)) {
|
||||
error(elem, "Cannot assign to an unexported field '%.*s' in structure literal", LIT(name));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (sel.index.count > 1) {
|
||||
error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
|
||||
@@ -5564,15 +5508,6 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
|
||||
check_assignment(c, o, field->type, str_lit("structure literal"));
|
||||
}
|
||||
} else {
|
||||
bool all_fields_are_blank = true;
|
||||
for_array(i, t->Struct.fields_in_src_order) {
|
||||
Entity *field = t->Struct.fields_in_src_order[i];
|
||||
if (!is_blank_ident(field->token)) {
|
||||
all_fields_are_blank = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool seen_field_value = false;
|
||||
|
||||
for_array(index, cl->elems) {
|
||||
@@ -5594,21 +5529,9 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
|
||||
if (field == nullptr) {
|
||||
field = t->Struct.fields_in_src_order[index];
|
||||
}
|
||||
if (!all_fields_are_blank && is_blank_ident(field->token)) {
|
||||
// NOTE(bill): Ignore blank identifiers
|
||||
continue;
|
||||
}
|
||||
|
||||
check_expr_with_type_hint(c, o, elem, field->type);
|
||||
|
||||
if (!check_is_field_exported(c, field)) {
|
||||
gbString t = type_to_string(type);
|
||||
error(o->expr, "Implicit assignment to an unexported field '%.*s' in '%s' literal",
|
||||
LIT(field->token.string), t);
|
||||
gb_string_free(t);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type)) {
|
||||
is_constant = false;
|
||||
}
|
||||
@@ -6032,13 +5955,6 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (!valid && t->kind == Type_Struct) {
|
||||
Entity *found = find_using_index_expr(t);
|
||||
if (found != nullptr) {
|
||||
valid = check_set_index_data(o, found->type, is_type_pointer(found->type), &max_count);
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
gbString str = expr_to_string(o->expr);
|
||||
gbString type_str = type_to_string(o->type);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -208,7 +208,6 @@ void check_struct_field_decl(Checker *c, AstNode *decl, Array<Entity *> *fields,
|
||||
|
||||
}
|
||||
|
||||
Entity *using_index_expr = nullptr;
|
||||
|
||||
if (is_using && fields->count > 0) {
|
||||
Type *first_type = (*fields)[fields->count-1]->type;
|
||||
@@ -217,32 +216,10 @@ void check_struct_field_decl(Checker *c, AstNode *decl, Array<Entity *> *fields,
|
||||
vd->names.count >= 1 &&
|
||||
vd->names[0]->kind == AstNode_Ident) {
|
||||
Token name_token = vd->names[0]->Ident.token;
|
||||
if (is_type_indexable(t)) {
|
||||
bool ok = true;
|
||||
for_array(emi, entity_map->entries) {
|
||||
Entity *e = entity_map->entries[emi].value;
|
||||
if (e->kind == Entity_Variable && e->flags & EntityFlag_Using) {
|
||||
if (is_type_indexable(e->type)) {
|
||||
if (e->identifier != vd->names[0]) {
|
||||
ok = false;
|
||||
using_index_expr = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
using_index_expr = (*fields)[fields->count-1];
|
||||
} else {
|
||||
(*fields)[fields->count-1]->flags &= ~EntityFlag_Using;
|
||||
error(name_token, "Previous 'using' for an index expression '%.*s'", LIT(name_token.string));
|
||||
}
|
||||
} else {
|
||||
gbString type_str = type_to_string(first_type);
|
||||
error(name_token, "'using' cannot be applied to the field '%.*s' of type '%s'", LIT(name_token.string), type_str);
|
||||
gb_string_free(type_str);
|
||||
return;
|
||||
}
|
||||
gbString type_str = type_to_string(first_type);
|
||||
error(name_token, "'using' cannot be applied to the field '%.*s' of type '%s'", LIT(name_token.string), type_str);
|
||||
gb_string_free(type_str);
|
||||
return;
|
||||
}
|
||||
|
||||
populate_using_entity_map(c, struct_node, type, entity_map);
|
||||
@@ -408,7 +385,6 @@ Array<Entity *> check_struct_fields(Checker *c, AstNode *node, Array<AstNode *>
|
||||
field_src_index += 1;
|
||||
}
|
||||
|
||||
Entity *using_index_expr = nullptr;
|
||||
|
||||
if (is_using && p->names.count > 0) {
|
||||
Type *first_type = fields[fields.count-1]->type;
|
||||
@@ -418,32 +394,10 @@ Array<Entity *> check_struct_fields(Checker *c, AstNode *node, Array<AstNode *>
|
||||
p->names.count >= 1 &&
|
||||
p->names[0]->kind == AstNode_Ident) {
|
||||
Token name_token = p->names[0]->Ident.token;
|
||||
if (is_type_indexable(t)) {
|
||||
bool ok = true;
|
||||
for_array(emi, entity_map.entries) {
|
||||
Entity *e = entity_map.entries[emi].value;
|
||||
if (e->kind == Entity_Variable && e->flags & EntityFlag_Using) {
|
||||
if (is_type_indexable(e->type)) {
|
||||
if (e->identifier != p->names[0]) {
|
||||
ok = false;
|
||||
using_index_expr = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
using_index_expr = fields[fields.count-1];
|
||||
} else {
|
||||
fields[fields.count-1]->flags &= ~EntityFlag_Using;
|
||||
error(name_token, "Previous 'using' for an index expression '%.*s'", LIT(name_token.string));
|
||||
}
|
||||
} else {
|
||||
gbString type_str = type_to_string(first_type);
|
||||
error(name_token, "'using' cannot be applied to the field '%.*s' of type '%s'", LIT(name_token.string), type_str);
|
||||
gb_string_free(type_str);
|
||||
continue;
|
||||
}
|
||||
gbString type_str = type_to_string(first_type);
|
||||
error(name_token, "'using' cannot be applied to the field '%.*s' of type '%s'", LIT(name_token.string), type_str);
|
||||
gb_string_free(type_str);
|
||||
continue;
|
||||
}
|
||||
|
||||
populate_using_entity_map(c, node, type, &entity_map);
|
||||
|
||||
@@ -1551,18 +1551,18 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) {
|
||||
}
|
||||
|
||||
Array<EntityGraphNode *> G = {};
|
||||
array_init(&G, a);
|
||||
array_init(&G, a, 2*M.entries.count);
|
||||
|
||||
for_array(i, M.entries) {
|
||||
auto *entry = &M.entries[i];
|
||||
Entity * e = cast(Entity *)entry->key.ptr;
|
||||
auto *e = cast(Entity *)entry->key.ptr;
|
||||
EntityGraphNode *n = entry->value;
|
||||
|
||||
if (e->kind == Entity_Procedure) {
|
||||
// Connect each pred 'p' of 'n' with each succ 's' and from
|
||||
// the procedure node
|
||||
for_array(j, n->pred.entries) {
|
||||
EntityGraphNode *p = cast(EntityGraphNode *)n->pred.entries[j].ptr;
|
||||
EntityGraphNode *p = n->pred.entries[j].ptr;
|
||||
|
||||
// Ignore self-cycles
|
||||
if (p != n) {
|
||||
@@ -1594,6 +1594,7 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) {
|
||||
GB_ASSERT(n->dep_count >= 0);
|
||||
}
|
||||
|
||||
|
||||
return G;
|
||||
}
|
||||
|
||||
@@ -3335,6 +3336,20 @@ void calculate_global_init_order(Checker *c) {
|
||||
|
||||
|
||||
void check_parsed_files(Checker *c) {
|
||||
#if 0
|
||||
Timings timings = {};
|
||||
timings_init(&timings, str_lit("check_parsed_files"), 16);
|
||||
defer ({
|
||||
timings_print_all(&timings);
|
||||
timings_destroy(&timings);
|
||||
});
|
||||
#define TIME_SECTION(str) timings_start_section(&timings, str_lit(str))
|
||||
#else
|
||||
#define TIME_SECTION(str)
|
||||
#endif
|
||||
|
||||
TIME_SECTION("map full filepaths to scope");
|
||||
|
||||
add_type_info_type(c, t_invalid);
|
||||
|
||||
// Map full filepaths to Scopes
|
||||
@@ -3351,6 +3366,7 @@ void check_parsed_files(Checker *c) {
|
||||
}
|
||||
}
|
||||
|
||||
TIME_SECTION("collect entities");
|
||||
// Collect Entities
|
||||
for_array(i, c->parser->files) {
|
||||
AstFile *f = c->parser->files[i];
|
||||
@@ -3360,10 +3376,16 @@ void check_parsed_files(Checker *c) {
|
||||
c->context = prev_context;
|
||||
}
|
||||
|
||||
TIME_SECTION("import entities");
|
||||
check_import_entities(c);
|
||||
|
||||
TIME_SECTION("check all global entities");
|
||||
check_all_global_entities(c);
|
||||
|
||||
TIME_SECTION("init preload");
|
||||
init_preload(c); // NOTE(bill): This could be setup previously through the use of 'type_info_of'
|
||||
|
||||
TIME_SECTION("check procedure bodies");
|
||||
// Check procedure bodies
|
||||
// NOTE(bill): Nested procedures bodies will be added to this "queue"
|
||||
for_array(i, c->procs.entries) {
|
||||
@@ -3397,12 +3419,16 @@ void check_parsed_files(Checker *c) {
|
||||
check_proc_body(c, pi->token, pi->decl, pi->type, pi->body);
|
||||
}
|
||||
|
||||
TIME_SECTION("generate minimum dependency set");
|
||||
c->info.minimum_dependency_set = generate_minimum_dependency_set(&c->info, c->info.entry_point);
|
||||
|
||||
|
||||
TIME_SECTION("calculate global init order");
|
||||
// Calculate initialization order of global variables
|
||||
calculate_global_init_order(c);
|
||||
|
||||
|
||||
TIME_SECTION("add untyped expression values");
|
||||
// Add untyped expression values
|
||||
for_array(i, c->info.untyped.entries) {
|
||||
auto *entry = &c->info.untyped.entries[i];
|
||||
@@ -3420,6 +3446,8 @@ void check_parsed_files(Checker *c) {
|
||||
// TODO(bill): Check for unused imports (and remove) or even warn/err
|
||||
// TODO(bill): Any other checks?
|
||||
|
||||
|
||||
TIME_SECTION("add type information");
|
||||
// Add "Basic" type information
|
||||
for (isize i = 0; i < gb_count_of(basic_types)-1; i++) {
|
||||
Type *t = &basic_types[i];
|
||||
@@ -3440,6 +3468,7 @@ void check_parsed_files(Checker *c) {
|
||||
}
|
||||
}
|
||||
|
||||
TIME_SECTION("check entry poiny");
|
||||
if (!build_context.is_dll) {
|
||||
Scope *s = c->info.init_scope;
|
||||
GB_ASSERT(s != nullptr);
|
||||
@@ -3458,4 +3487,6 @@ void check_parsed_files(Checker *c) {
|
||||
error(token, "Undefined entry point procedure 'main'");
|
||||
}
|
||||
}
|
||||
|
||||
#undef TIME_SECTION
|
||||
}
|
||||
|
||||
15
src/ir.cpp
15
src/ir.cpp
@@ -5335,7 +5335,7 @@ bool ir_is_elem_const(irModule *m, AstNode *elem, Type *elem_type) {
|
||||
elem = elem->FieldValue.value;
|
||||
}
|
||||
TypeAndValue tav = type_and_value_of_expr(m->info, elem);
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s", expr_to_string(elem), type_to_string(tav.type));
|
||||
return tav.value.kind != ExactValue_Invalid;
|
||||
}
|
||||
|
||||
@@ -5517,6 +5517,7 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
|
||||
|
||||
bool deref = is_type_pointer(t);
|
||||
t = base_type(type_deref(t));
|
||||
GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr));
|
||||
|
||||
if (is_type_map(t)) {
|
||||
irValue *map_val = ir_build_addr_ptr(proc, ie->expr);
|
||||
@@ -5532,18 +5533,6 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
|
||||
}
|
||||
|
||||
irValue *using_addr = nullptr;
|
||||
if (!is_type_indexable(t)) {
|
||||
// Using index expression
|
||||
Entity *using_field = find_using_index_expr(t);
|
||||
if (using_field != nullptr) {
|
||||
Selection sel = lookup_field(a, t, using_field->token.string, false);
|
||||
irValue *e = ir_build_addr_ptr(proc, ie->expr);
|
||||
using_addr = ir_emit_deep_field_gep(proc, e, sel);
|
||||
|
||||
t = using_field->type;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
switch (t->kind) {
|
||||
case Type_Vector: {
|
||||
|
||||
@@ -880,8 +880,6 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin
|
||||
ir_print_type(f, m, t_int);
|
||||
ir_write_string(f, " 0, i32 0), ");
|
||||
ir_print_type(f, m, t_int);
|
||||
ir_fprintf(f, " %lld, ", cs->count);
|
||||
ir_print_type(f, m, t_int);
|
||||
ir_fprintf(f, " %lld}", cs->count);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -558,7 +558,7 @@ void show_timings(Checker *c, Timings *t) {
|
||||
{
|
||||
TimeStamp ts = t->sections[0];
|
||||
GB_ASSERT(ts.label == "parse files");
|
||||
f64 parse_time = time_stamp_as_second(ts, t->freq);
|
||||
f64 parse_time = time_stamp_as_s(ts, t->freq);
|
||||
gb_printf("Parse pass\n");
|
||||
gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time);
|
||||
gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
|
||||
@@ -610,6 +610,7 @@ int main(int arg_count, char **arg_ptr) {
|
||||
Timings timings = {0};
|
||||
timings_init(&timings, str_lit("Total Time"), 128);
|
||||
defer (timings_destroy(&timings));
|
||||
|
||||
init_string_buffer_memory();
|
||||
init_scratch_memory(gb_megabytes(10));
|
||||
init_global_error_collector();
|
||||
@@ -853,7 +854,6 @@ int main(int arg_count, char **arg_ptr) {
|
||||
if (run_output) {
|
||||
system_exec_command_line_app("odin run", false, "%.*s.exe", LIT(output_base));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// NOTE(zangent): Linux / Unix is unfinished and not tested very well.
|
||||
@@ -993,7 +993,6 @@ int main(int arg_count, char **arg_ptr) {
|
||||
if (run_output) {
|
||||
system_exec_command_line_app("odin run", false, "%.*s", LIT(output_base));
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -104,16 +104,40 @@ void timings_start_section(Timings *t, String label) {
|
||||
array_add(&t->sections, make_time_stamp(label));
|
||||
}
|
||||
|
||||
f64 time_stamp_as_second(TimeStamp ts, u64 freq) {
|
||||
f64 time_stamp_as_s(TimeStamp const &ts, u64 freq) {
|
||||
GB_ASSERT_MSG(ts.finish >= ts.start, "time_stamp_as_ms - %.*s", LIT(ts.label));
|
||||
return cast(f64)(ts.finish - ts.start) / cast(f64)freq;
|
||||
}
|
||||
|
||||
f64 time_stamp_as_ms(TimeStamp ts, u64 freq) {
|
||||
return 1000.0*time_stamp_as_second(ts, freq);
|
||||
f64 time_stamp_as_ms(TimeStamp const &ts, u64 freq) {
|
||||
return 1000.0*time_stamp_as_s(ts, freq);
|
||||
}
|
||||
|
||||
void timings_print_all(Timings *t) {
|
||||
f64 time_stamp_as_us(TimeStamp const &ts, u64 freq) {
|
||||
return 1000000.0*time_stamp_as_s(ts, freq);
|
||||
}
|
||||
|
||||
enum TimingUnit {
|
||||
TimingUnit_Second,
|
||||
TimingUnit_Millisecond,
|
||||
TimingUnit_Microsecond,
|
||||
|
||||
TimingUnit_COUNT,
|
||||
};
|
||||
|
||||
char const *timing_unit_strings[TimingUnit_COUNT] = {"s", "ms", "us"};
|
||||
|
||||
f64 time_stamp(TimeStamp const &ts, u64 freq, TimingUnit unit) {
|
||||
f64 total_time = 0;
|
||||
switch (unit) {
|
||||
case TimingUnit_Millisecond: return time_stamp_as_ms(ts, freq);
|
||||
case TimingUnit_Microsecond: return time_stamp_as_us(ts, freq);
|
||||
default: /*fallthrough*/
|
||||
case TimingUnit_Second: return time_stamp_as_s (ts, freq);
|
||||
}
|
||||
}
|
||||
|
||||
void timings_print_all(Timings *t, TimingUnit unit = TimingUnit_Millisecond) {
|
||||
char const SPACES[] = " ";
|
||||
isize max_len;
|
||||
|
||||
@@ -121,6 +145,7 @@ void timings_print_all(Timings *t) {
|
||||
t->total.finish = time_stamp_time_now();
|
||||
|
||||
max_len = t->total.label.len;
|
||||
max_len = 36;
|
||||
for_array(i, t->sections) {
|
||||
TimeStamp ts = t->sections[i];
|
||||
max_len = gb_max(max_len, ts.label.len);
|
||||
@@ -128,22 +153,25 @@ void timings_print_all(Timings *t) {
|
||||
|
||||
GB_ASSERT(max_len <= gb_size_of(SPACES)-1);
|
||||
|
||||
t->total_time_seconds = time_stamp_as_second(t->total, t->freq);
|
||||
t->total_time_seconds = time_stamp_as_s(t->total, t->freq);
|
||||
|
||||
f64 total_ms = time_stamp_as_ms(t->total, t->freq);
|
||||
f64 total_time = time_stamp(t->total, t->freq, unit);
|
||||
|
||||
gb_printf("%.*s%.*s - % 9.3f ms - %6.2f%%\n",
|
||||
gb_printf("%.*s%.*s - % 9.3f %s - %6.2f%%\n",
|
||||
LIT(t->total.label),
|
||||
cast(int)(max_len-t->total.label.len), SPACES,
|
||||
total_ms,
|
||||
total_time,
|
||||
timing_unit_strings[unit],
|
||||
cast(f64)100.0);
|
||||
|
||||
for_array(i, t->sections) {
|
||||
TimeStamp ts = t->sections[i];
|
||||
f64 section_ms = time_stamp_as_ms(ts, t->freq);
|
||||
gb_printf("%.*s%.*s - % 9.3f ms - %6.2f%%\n",
|
||||
f64 section_time = time_stamp(ts, t->freq, unit);
|
||||
gb_printf("%.*s%.*s - % 9.3f %s - %6.2f%%\n",
|
||||
LIT(ts.label),
|
||||
cast(int)(max_len-ts.label.len), SPACES,
|
||||
section_ms, 100*section_ms/total_ms);
|
||||
section_time,
|
||||
timing_unit_strings[unit],
|
||||
100.0*section_time/total_time);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -950,7 +950,18 @@ bool is_type_valid_for_keys(Type *t) {
|
||||
|
||||
|
||||
bool is_type_indexable(Type *t) {
|
||||
return is_type_array(t) || is_type_slice(t) || is_type_vector(t) || is_type_string(t);
|
||||
Type *bt = base_type(t);
|
||||
switch (bt->kind) {
|
||||
case Type_Basic:
|
||||
return is_type_string(bt);
|
||||
case Type_Array:
|
||||
case Type_Slice:
|
||||
case Type_Vector:
|
||||
case Type_DynamicArray:
|
||||
case Type_Map:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_type_polymorphic_struct(Type *t) {
|
||||
|
||||
Reference in New Issue
Block a user