mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-04 22:58:46 +00:00
Re-allow when statements at the file scope
This commit is contained in:
@@ -16,9 +16,11 @@ import "core:unicode/utf16"
|
||||
import "core:unicode/utf8"
|
||||
import "core:c"
|
||||
|
||||
import "core:atomics"
|
||||
import "core:thread"
|
||||
import "core:sys/win32"
|
||||
when os.OS == "windows" {
|
||||
import "core:atomics"
|
||||
import "core:thread"
|
||||
import "core:sys/win32"
|
||||
}
|
||||
|
||||
@(link_name="general_stuff")
|
||||
general_stuff :: proc() {
|
||||
|
||||
+238
-20
@@ -669,6 +669,7 @@ void init_checker(Checker *c, Parser *parser) {
|
||||
init_checker_info(&c->info);
|
||||
|
||||
array_init(&c->procs_to_check, a);
|
||||
ptr_set_init(&c->checked_packages, a);
|
||||
|
||||
// NOTE(bill): Is this big enough or too small?
|
||||
isize item_size = gb_max3(gb_size_of(Entity), gb_size_of(Type), gb_size_of(Scope));
|
||||
@@ -684,8 +685,7 @@ void destroy_checker(Checker *c) {
|
||||
destroy_checker_info(&c->info);
|
||||
|
||||
array_free(&c->procs_to_check);
|
||||
|
||||
// gb_arena_free(&c->tmp_arena);
|
||||
ptr_set_destroy(&c->checked_packages);
|
||||
|
||||
destroy_checker_context(&c->init_ctx);
|
||||
}
|
||||
@@ -1800,7 +1800,7 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
|
||||
|
||||
|
||||
|
||||
void check_decl_attributes(CheckerContext *c, Array<AstNode *> attributes, DeclAttributeProc *proc, AttributeContext *ac) {
|
||||
void check_decl_attributes(CheckerContext *c, Array<AstNode *> const &attributes, DeclAttributeProc *proc, AttributeContext *ac) {
|
||||
if (attributes.count == 0) return;
|
||||
|
||||
String original_link_prefix = {};
|
||||
@@ -2129,19 +2129,21 @@ void check_add_foreign_block_decl(CheckerContext *ctx, AstNode *decl) {
|
||||
|
||||
check_decl_attributes(&c, fb->attributes, foreign_block_decl_attribute, nullptr);
|
||||
|
||||
c.collect_delayed_decls = true;
|
||||
check_collect_entities(&c, fb->decls);
|
||||
}
|
||||
|
||||
// NOTE(bill): If file_scopes == nullptr, this will act like a local scope
|
||||
void check_collect_entities(CheckerContext *c, Array<AstNode *> nodes) {
|
||||
void check_collect_entities(CheckerContext *c, Array<AstNode *> const &nodes) {
|
||||
for_array(decl_index, nodes) {
|
||||
AstNode *decl = nodes[decl_index];
|
||||
if (!is_ast_node_decl(decl) && !is_ast_node_when_stmt(decl)) {
|
||||
if (c->scope->is_file && decl->kind == AstNode_ExprStmt) {
|
||||
AstNode *expr = decl->ExprStmt.expr;
|
||||
if (expr->kind == AstNode_CallExpr &&
|
||||
expr->CallExpr.proc->kind == AstNode_BasicDirective) {
|
||||
array_add(&c->scope->delayed_directives, expr);
|
||||
if (expr->kind == AstNode_CallExpr && expr->CallExpr.proc->kind == AstNode_BasicDirective) {
|
||||
if (c->collect_delayed_decls) {
|
||||
array_add(&c->scope->delayed_directives, expr);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -2167,7 +2169,9 @@ void check_collect_entities(CheckerContext *c, Array<AstNode *> nodes) {
|
||||
// TODO(bill): Better error handling if it isn't
|
||||
continue;
|
||||
}
|
||||
array_add(&c->scope->delayed_imports, decl);
|
||||
if (c->collect_delayed_decls) {
|
||||
array_add(&c->scope->delayed_imports, decl);
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(fl, ForeignImportDecl, decl);
|
||||
@@ -2194,7 +2198,7 @@ void check_collect_entities(CheckerContext *c, Array<AstNode *> nodes) {
|
||||
|
||||
// NOTE(bill): 'when' stmts need to be handled after the other as the condition may refer to something
|
||||
// declared after this stmt in source
|
||||
if (!c->scope->is_file) {
|
||||
if (!c->scope->is_file || c->collect_delayed_decls) {
|
||||
for_array(i, nodes) {
|
||||
AstNode *node = nodes[i];
|
||||
switch (node->kind) {
|
||||
@@ -2591,6 +2595,7 @@ void check_add_import_decl(CheckerContext *ctx, AstNodeImportDecl *id) {
|
||||
}
|
||||
}
|
||||
|
||||
ptr_set_add(&ctx->checker->checked_packages, pkg);
|
||||
scope->has_been_imported = true;
|
||||
}
|
||||
|
||||
@@ -2638,7 +2643,163 @@ void check_add_foreign_import_decl(CheckerContext *ctx, AstNode *decl) {
|
||||
add_entity(ctx->checker, parent_scope, nullptr, e);
|
||||
}
|
||||
|
||||
bool collect_checked_packages_from_decl_list(Checker *c, Array<AstNode *> const &decls) {
|
||||
bool new_files = false;
|
||||
for_array(i, decls) {
|
||||
AstNode *decl = decls[i];
|
||||
switch (decl->kind) {
|
||||
case_ast_node(id, ImportDecl, decl);
|
||||
HashKey key = hash_string(id->fullpath);
|
||||
AstPackage **found = map_get(&c->info.packages, key);
|
||||
if (found == nullptr) {
|
||||
continue;
|
||||
}
|
||||
AstPackage *pkg = *found;
|
||||
if (!ptr_set_exists(&c->checked_packages, pkg)) {
|
||||
new_files = true;
|
||||
ptr_set_add(&c->checked_packages, pkg);
|
||||
}
|
||||
case_end;
|
||||
}
|
||||
}
|
||||
return new_files;
|
||||
}
|
||||
|
||||
// Returns true if a new package is present
|
||||
bool collect_file_decls(CheckerContext *ctx, Array<AstNode *> const &decls);
|
||||
bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstNodeWhenStmt *ws);
|
||||
|
||||
bool collect_when_stmt_from_file(CheckerContext *ctx, AstNodeWhenStmt *ws) {
|
||||
Operand operand = {Addressing_Invalid};
|
||||
if (!ws->is_cond_determined) {
|
||||
check_expr(ctx, &operand, ws->cond);
|
||||
if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
|
||||
error(ws->cond, "Non-boolean condition in 'when' statement");
|
||||
}
|
||||
if (operand.mode != Addressing_Constant) {
|
||||
error(ws->cond, "Non-constant condition in 'when' statement");
|
||||
}
|
||||
|
||||
ws->is_cond_determined = true;
|
||||
ws->determined_cond = operand.value.kind == ExactValue_Bool && operand.value.value_bool;
|
||||
}
|
||||
|
||||
if (ws->body == nullptr || ws->body->kind != AstNode_BlockStmt) {
|
||||
error(ws->cond, "Invalid body for 'when' statement");
|
||||
} else {
|
||||
if (ws->determined_cond) {
|
||||
return collect_checked_packages_from_decl_list(ctx->checker, ws->body->BlockStmt.stmts);
|
||||
} else if (ws->else_stmt) {
|
||||
switch (ws->else_stmt->kind) {
|
||||
case AstNode_BlockStmt:
|
||||
return collect_checked_packages_from_decl_list(ctx->checker, ws->else_stmt->BlockStmt.stmts);
|
||||
case AstNode_WhenStmt:
|
||||
return collect_when_stmt_from_file(ctx, &ws->else_stmt->WhenStmt);
|
||||
default:
|
||||
error(ws->else_stmt, "Invalid 'else' statement in 'when' statement");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstNodeWhenStmt *ws) {
|
||||
Operand operand = {Addressing_Invalid};
|
||||
if (!ws->is_cond_determined) {
|
||||
check_expr(ctx, &operand, ws->cond);
|
||||
if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
|
||||
error(ws->cond, "Non-boolean condition in 'when' statement");
|
||||
}
|
||||
if (operand.mode != Addressing_Constant) {
|
||||
error(ws->cond, "Non-constant condition in 'when' statement");
|
||||
}
|
||||
|
||||
ws->is_cond_determined = true;
|
||||
ws->determined_cond = operand.value.kind == ExactValue_Bool && operand.value.value_bool;
|
||||
}
|
||||
|
||||
if (ws->body == nullptr || ws->body->kind != AstNode_BlockStmt) {
|
||||
error(ws->cond, "Invalid body for 'when' statement");
|
||||
} else {
|
||||
if (ws->determined_cond) {
|
||||
return collect_file_decls(ctx, ws->body->BlockStmt.stmts);
|
||||
} else if (ws->else_stmt) {
|
||||
switch (ws->else_stmt->kind) {
|
||||
case AstNode_BlockStmt:
|
||||
return collect_file_decls(ctx, ws->else_stmt->BlockStmt.stmts);
|
||||
case AstNode_WhenStmt:
|
||||
return collect_file_decls_from_when_stmt(ctx, &ws->else_stmt->WhenStmt);
|
||||
default:
|
||||
error(ws->else_stmt, "Invalid 'else' statement in 'when' statement");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool collect_file_decls(CheckerContext *ctx, Array<AstNode *> const &decls) {
|
||||
GB_ASSERT(ctx->scope->is_file);
|
||||
|
||||
if (collect_checked_packages_from_decl_list(ctx->checker, decls)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for_array(i, decls) {
|
||||
AstNode *decl = decls[i];
|
||||
switch (decl->kind) {
|
||||
case_ast_node(vd, ValueDecl, decl);
|
||||
check_collect_value_decl(ctx, decl);
|
||||
case_end;
|
||||
|
||||
case_ast_node(id, ImportDecl, decl);
|
||||
check_add_import_decl(ctx, id);
|
||||
case_end;
|
||||
|
||||
case_ast_node(fl, ForeignImportDecl, decl);
|
||||
check_add_foreign_import_decl(ctx, decl);
|
||||
case_end;
|
||||
|
||||
case_ast_node(fb, ForeignBlockDecl, decl);
|
||||
check_add_foreign_block_decl(ctx, decl);
|
||||
case_end;
|
||||
|
||||
case_ast_node(ws, WhenStmt, decl);
|
||||
if (!ws->is_cond_determined) {
|
||||
if (collect_when_stmt_from_file(ctx, ws)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
CheckerContext nctx = *ctx;
|
||||
nctx.collect_delayed_decls = true;
|
||||
|
||||
if (collect_file_decls_from_when_stmt(&nctx, ws)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
CheckerContext nctx = *ctx;
|
||||
nctx.collect_delayed_decls = true;
|
||||
|
||||
if (collect_file_decls_from_when_stmt(&nctx, ws)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(ce, CallExpr, decl);
|
||||
if (ce->proc->kind == AstNode_BasicDirective) {
|
||||
Operand o = {};
|
||||
check_expr(ctx, &o, decl);
|
||||
}
|
||||
case_end;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void check_import_entities(Checker *c) {
|
||||
Array<ImportGraphNode *> dep_graph = generate_import_dependency_graph(c);
|
||||
@@ -2687,14 +2848,14 @@ void check_import_entities(Checker *c) {
|
||||
#endif
|
||||
} else if (path.count > 0) {
|
||||
ImportPathItem item = path[path.count-1];
|
||||
String filename = fn(item);
|
||||
error(item.decl, "Cyclic importation of '%.*s'", LIT(filename));
|
||||
String pkg_name = item.pkg->name;
|
||||
error(item.decl, "Cyclic importation of '%.*s'", LIT(pkg_name));
|
||||
for (isize i = 0; i < path.count; i++) {
|
||||
error(item.decl, "'%.*s' refers to", LIT(filename));
|
||||
error(item.decl, "'%.*s' refers to", LIT(pkg_name));
|
||||
item = path[i];
|
||||
filename = fn(item);
|
||||
pkg_name = item.pkg->name;
|
||||
}
|
||||
error(item.decl, "'%.*s'", LIT(filename));
|
||||
error(item.decl, "'%.*s'", LIT(pkg_name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2716,6 +2877,68 @@ void check_import_entities(Checker *c) {
|
||||
array_add(&package_order, n);
|
||||
}
|
||||
|
||||
for_array(i, c->parser->packages) {
|
||||
AstPackage *pkg = c->parser->packages[i];
|
||||
switch (pkg->kind) {
|
||||
case Package_Init:
|
||||
case Package_Runtime:
|
||||
ptr_set_add(&c->checked_packages, pkg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (isize loop_count = 0; ; loop_count++) {
|
||||
bool new_files = false;
|
||||
for_array(i, package_order) {
|
||||
ImportGraphNode *node = package_order[i];
|
||||
GB_ASSERT(node->scope->is_package);
|
||||
AstPackage *pkg = node->scope->package;
|
||||
if (!ptr_set_exists(&c->checked_packages, pkg)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for_array(i, pkg->files) {
|
||||
AstFile *f = pkg->files[i];
|
||||
CheckerContext ctx = c->init_ctx;
|
||||
add_curr_ast_file(&ctx, f);
|
||||
new_files |= collect_checked_packages_from_decl_list(c, f->decls);
|
||||
}
|
||||
}
|
||||
|
||||
if (!new_files) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (isize pkg_index = 0; pkg_index < package_order.count; pkg_index++) {
|
||||
ImportGraphNode *node = package_order[pkg_index];
|
||||
AstPackage *pkg = node->pkg;
|
||||
|
||||
if (!ptr_set_exists(&c->checked_packages, pkg)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool new_packages = false;
|
||||
|
||||
for_array(i, pkg->files) {
|
||||
AstFile *f = pkg->files[i];
|
||||
|
||||
CheckerContext ctx = c->init_ctx;
|
||||
ctx.collect_delayed_decls = true;
|
||||
add_curr_ast_file(&ctx, f);
|
||||
|
||||
if (collect_file_decls(&ctx, f->decls)) {
|
||||
new_packages = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_packages) {
|
||||
pkg_index = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for_array(i, package_order) {
|
||||
ImportGraphNode *node = package_order[i];
|
||||
GB_ASSERT(node->scope->is_package);
|
||||
@@ -2731,12 +2954,6 @@ void check_import_entities(Checker *c) {
|
||||
ast_node(id, ImportDecl, decl);
|
||||
check_add_import_decl(&ctx, id);
|
||||
}
|
||||
}
|
||||
|
||||
for_array(i, pkg->files) {
|
||||
AstFile *f = pkg->files[i];
|
||||
CheckerContext ctx = c->init_ctx;
|
||||
add_curr_ast_file(&ctx, f);
|
||||
for_array(j, f->scope->delayed_directives) {
|
||||
AstNode *expr = f->scope->delayed_directives[j];
|
||||
Operand o = {};
|
||||
@@ -2963,6 +3180,7 @@ void check_parsed_files(Checker *c) {
|
||||
CheckerContext ctx = make_checker_context(c);
|
||||
defer (destroy_checker_context(&ctx));
|
||||
ctx.pkg = p;
|
||||
ctx.collect_delayed_decls = false;
|
||||
|
||||
for_array(j, p->files) {
|
||||
AstFile *f = p->files[j];
|
||||
|
||||
+5
-2
@@ -322,6 +322,7 @@ struct CheckerContext {
|
||||
CheckerTypePath *type_path;
|
||||
isize type_level; // TODO(bill): Actually handle correctly
|
||||
|
||||
bool collect_delayed_decls;
|
||||
bool allow_polymorphic_types;
|
||||
bool no_polymorphic_errors;
|
||||
bool in_polymorphic_specialization;
|
||||
@@ -333,6 +334,8 @@ struct Checker {
|
||||
CheckerInfo info;
|
||||
|
||||
Array<ProcedureInfo> procs_to_check;
|
||||
PtrSet<AstPackage *> checked_packages;
|
||||
|
||||
gbAllocator allocator;
|
||||
CheckerContext init_ctx;
|
||||
bool done_preload;
|
||||
@@ -386,7 +389,7 @@ void check_add_foreign_import_decl(CheckerContext *c, AstNode *decl);
|
||||
|
||||
|
||||
bool check_arity_match(CheckerContext *c, AstNodeValueDecl *vd, bool is_global = false);
|
||||
void check_collect_entities(CheckerContext *c, Array<AstNode *> nodes);
|
||||
void check_collect_entities(CheckerContext *c, Array<AstNode *> const &nodes);
|
||||
void check_collect_entities_from_when_stmt(CheckerContext *c, AstNodeWhenStmt *ws);
|
||||
void check_delayed_file_import_entity(CheckerContext *c, AstNode *decl);
|
||||
|
||||
@@ -407,7 +410,7 @@ AttributeContext make_attribute_context(String link_prefix) {
|
||||
#define DECL_ATTRIBUTE_PROC(_name) bool _name(CheckerContext *c, AstNode *elem, String name, ExactValue value, AttributeContext *ac)
|
||||
typedef DECL_ATTRIBUTE_PROC(DeclAttributeProc);
|
||||
|
||||
void check_decl_attributes(CheckerContext *c, Array<AstNode *> attributes, DeclAttributeProc *proc, AttributeContext *ac);
|
||||
void check_decl_attributes(CheckerContext *c, Array<AstNode *> const &attributes, DeclAttributeProc *proc, AttributeContext *ac);
|
||||
|
||||
CheckerTypePath *new_checker_type_path();
|
||||
void destroy_checker_type_path(CheckerTypePath *tp);
|
||||
|
||||
@@ -603,6 +603,17 @@ void show_timings(Checker *c, Timings *t) {
|
||||
gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
|
||||
gb_printf("\n");
|
||||
}
|
||||
{
|
||||
TimeStamp ts = t->sections[1];
|
||||
GB_ASSERT(ts.label == "type check");
|
||||
f64 parse_time = time_stamp_as_s(ts, t->freq);
|
||||
gb_printf("Checker 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);
|
||||
gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
|
||||
gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
|
||||
gb_printf("\n");
|
||||
}
|
||||
{
|
||||
f64 total_time = t->total_time_seconds;
|
||||
gb_printf("Total pass\n");
|
||||
|
||||
+27
-2
@@ -4162,10 +4162,33 @@ bool determine_path_from_string(Parser *p, AstNode *node, String base_dir, Strin
|
||||
}
|
||||
|
||||
|
||||
void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<AstNode *> decls) {
|
||||
|
||||
void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<AstNode *> &decls);
|
||||
|
||||
void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstNodeWhenStmt *ws) {
|
||||
if (ws->body != nullptr) {
|
||||
auto stmts = ws->body->BlockStmt.stmts;
|
||||
parse_setup_file_decls(p, f, base_dir, stmts);
|
||||
}
|
||||
|
||||
if (ws->else_stmt != nullptr) {
|
||||
switch (ws->else_stmt->kind) {
|
||||
case AstNode_BlockStmt: {
|
||||
auto stmts = ws->else_stmt->BlockStmt.stmts;
|
||||
parse_setup_file_decls(p, f, base_dir, stmts);
|
||||
} break;
|
||||
case AstNode_WhenStmt:
|
||||
parse_setup_file_when_stmt(p, f, base_dir, &ws->else_stmt->WhenStmt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<AstNode *> &decls) {
|
||||
for_array(i, decls) {
|
||||
AstNode *node = decls[i];
|
||||
if (!is_ast_node_decl(node) &&
|
||||
node->kind != AstNode_WhenStmt &&
|
||||
node->kind != AstNode_BadStmt &&
|
||||
node->kind != AstNode_EmptyStmt) {
|
||||
// NOTE(bill): Sanity check
|
||||
@@ -4212,7 +4235,9 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<AstNod
|
||||
}
|
||||
fl->fullpath = foreign_path;
|
||||
}
|
||||
|
||||
} else if (node->kind == AstNode_WhenStmt) {
|
||||
ast_node(ws, WhenStmt, node);
|
||||
parse_setup_file_when_stmt(p, f, base_dir, ws);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user