diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 1215e337d..52133bf32 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -864,13 +864,21 @@ void check_proc_group_decl(Checker *c, Entity *pg_entity, DeclInfo *d) { } void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) { - if (e->type != nullptr) { + if (e->state == EntityState_Resolved) { + return; + } + String name = e->token.string; + + if (e->type != nullptr || e->state != EntityState_Unresolved) { + error(e->token, "Illegal declaration cycle of `%.*s`", LIT(name)); return; } + GB_ASSERT(e->state == EntityState_Unresolved); + #if 0 char buf[256] = {}; - isize n = gb_snprintf(buf, 256, "%.*s %d", LIT(e->token.string), e->kind); + isize n = gb_snprintf(buf, 256, "%.*s %d", LIT(name), e->kind); Timings timings = {}; timings_init(&timings, make_string(cast(u8 *)buf, n-1), 16); defer ({ @@ -887,17 +895,21 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) { if (d == nullptr) { // TODO(bill): Err here? e->type = t_invalid; + e->state = EntityState_Resolved; set_base_type(named_type, t_invalid); return; - // GB_PANIC("'%.*s' should been declared!", LIT(e->token.string)); + // GB_PANIC("'%.*s' should been declared!", LIT(name)); } } CheckerContext prev = c->context; c->context.scope = d->scope; c->context.decl = d; + c->context.type_level = 0; e->parent_proc_decl = c->context.curr_proc_decl; + e->state = EntityState_InProgress; + switch (e->kind) { case Entity_Variable: @@ -918,6 +930,8 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) { break; } + e->state = EntityState_Resolved; + c->context = prev; #undef TIME_SECTION diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 925a69c43..fc040fd2f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -984,7 +984,9 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type * } add_entity_use(c, n, e); - check_entity_decl(c, e, nullptr, named_type); + if (e->state == EntityState_Unresolved) { + check_entity_decl(c, e, nullptr, named_type); + } if (e->type == nullptr) { diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index affdad8a5..be37b937c 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1000,7 +1000,7 @@ void check_type_switch_stmt(Checker *c, AstNode *node, u32 mod_flags) { check_open_scope(c, stmt); { - Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, lhs->Ident.token, case_type, false); + Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, lhs->Ident.token, case_type, false, EntityState_Resolved); tag_var->flags |= EntityFlag_Used; tag_var->flags |= EntityFlag_Value; add_entity(c, c->context.scope, lhs, tag_var); @@ -1467,7 +1467,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { } if (found == nullptr) { bool is_immutable = true; - entity = make_entity_variable(c->allocator, c->context.scope, token, type, is_immutable); + entity = make_entity_variable(c->allocator, c->context.scope, token, type, is_immutable, EntityState_Resolved); add_entity_definition(&c->info, name, entity); } else { TokenPos pos = found->token.pos; @@ -1858,8 +1858,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { } e->flags |= EntityFlag_Visited; + e->state = EntityState_InProgress; if (e->type == nullptr) { e->type = init_type; + e->state = EntityState_Resolved; } ac.link_name = handle_link_name(c, e->token, ac.link_name, ac.link_prefix); e->Variable.thread_local_model = ac.thread_local_model; diff --git a/src/check_type.cpp b/src/check_type.cpp index ae6be323f..a999b9807 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -312,6 +312,7 @@ void add_polymorphic_struct_entity(Checker *c, AstNode *node, Type *named_type, node->Ident.token = token; e = make_entity_type_name(a, s, token, named_type); + e->state = EntityState_Resolved; add_entity_use(c, node, e); } @@ -468,6 +469,7 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node, Arraystate = EntityState_Resolved; add_entity(c, scope, name, e); array_add(&entities, e); } @@ -698,6 +700,7 @@ void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *nod Entity *e = make_entity_constant(c->allocator, c->context.scope, ident->Ident.token, constant_type, iota); e->identifier = ident; e->flags |= EntityFlag_Visited; + e->state = EntityState_Resolved; HashKey key = hash_string(name); if (map_get(&entity_map, key) != nullptr) { @@ -1181,7 +1184,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari type = t_invalid; } } - param = make_entity_type_name(c->allocator, scope, name->Ident.token, type); + param = make_entity_type_name(c->allocator, scope, name->Ident.token, type, EntityState_Resolved); param->TypeName.is_type_alias = true; } else { if (operands != nullptr && variables.count < operands->count) { @@ -1212,6 +1215,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari if (p->flags&FieldFlag_no_alias) { param->flags |= EntityFlag_NoAlias; } + param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it add_entity(c, scope, name, param); array_add(&variables, param); @@ -1754,9 +1758,9 @@ void generate_map_entry_type(gbAllocator a, Type *type) { Scope *s = create_scope(universal_scope, a); auto fields = array_make(a, 0, 3); - array_add(&fields, make_entity_field(a, s, make_token_ident(str_lit("key")), t_map_key, false, 0)); - array_add(&fields, make_entity_field(a, s, make_token_ident(str_lit("next")), t_int, false, 1)); - array_add(&fields, make_entity_field(a, s, make_token_ident(str_lit("value")), type->Map.value, false, 2)); + array_add(&fields, make_entity_field(a, s, make_token_ident(str_lit("key")), t_map_key, false, 0, EntityState_Resolved)); + array_add(&fields, make_entity_field(a, s, make_token_ident(str_lit("next")), t_int, false, 1, EntityState_Resolved)); + array_add(&fields, make_entity_field(a, s, make_token_ident(str_lit("value")), type->Map.value, false, 2, EntityState_Resolved)); entry_type->Struct.fields = fields; @@ -1793,8 +1797,8 @@ void generate_map_internal_types(gbAllocator a, Type *type) { auto fields = array_make(a, 0, 2); - array_add(&fields, make_entity_field(a, s, make_token_ident(str_lit("hashes")), hashes_type, false, 0)); - array_add(&fields, make_entity_field(a, s, make_token_ident(str_lit("entries")), entries_type, false, 1)); + array_add(&fields, make_entity_field(a, s, make_token_ident(str_lit("hashes")), hashes_type, false, 0, EntityState_Resolved)); + array_add(&fields, make_entity_field(a, s, make_token_ident(str_lit("entries")), entries_type, false, 1, EntityState_Resolved)); generated_struct_type->Struct.fields = fields; @@ -1842,7 +1846,7 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type) case_ast_node(i, Ident, e); Operand o = {}; - check_ident(c, &o, e, named_type, nullptr, false); + Entity *entity = check_ident(c, &o, e, named_type, nullptr, false); gbString err_str = nullptr; defer (gb_string_free(err_str)); @@ -1857,8 +1861,13 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type) if (t != nullptr && is_type_polymorphic_struct_unspecialized(t)) { err_str = expr_to_string(e); error(e, "Invalid use of a non-specialized polymorphic type '%s'", err_str); + return true; } } + + if (c->context.type_level == 0 && entity->state == EntityState_InProgress) { + error(e, "Illegal declaration cycle of `%.*s`", LIT(entity->token.string)); + } return true; } @@ -1895,8 +1904,7 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type) Token token = ident->Ident.token; Type *specific = nullptr; if (pt->specialization != nullptr) { - auto prev_ips = c->context.in_polymorphic_specialization; - defer (c->context.in_polymorphic_specialization = prev_ips); + CheckerContext prev = c->context; defer (c->context = prev); c->context.in_polymorphic_specialization = true; AstNode *s = pt->specialization; @@ -1919,6 +1927,7 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type) } Entity *e = make_entity_type_name(c->allocator, entity_scope, token, t); e->TypeName.is_type_alias = true; + e->state = EntityState_Resolved; add_entity(c, ps, ident, e); add_entity(c, s, ident, e); } else { @@ -2007,9 +2016,9 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type) case_end; case_ast_node(st, StructType, e); - bool ips = c->context.in_polymorphic_specialization; - defer (c->context.in_polymorphic_specialization = ips); + CheckerContext prev = c->context; defer (c->context = prev); c->context.in_polymorphic_specialization = false; + c->context.type_level += 1; *type = make_type_struct(c->allocator); set_base_type(named_type, *type); @@ -2021,9 +2030,9 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type) case_end; case_ast_node(ut, UnionType, e); - bool ips = c->context.in_polymorphic_specialization; - defer (c->context.in_polymorphic_specialization = ips); + CheckerContext prev = c->context; defer (c->context = prev); c->context.in_polymorphic_specialization = false; + c->context.type_level += 1; *type = make_type_union(c->allocator); set_base_type(named_type, *type); diff --git a/src/checker.cpp b/src/checker.cpp index 9d99b3a77..2083c7446 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -503,6 +503,7 @@ Entity *add_global_entity(Entity *entity) { if (scope_insert_entity(universal_scope, entity)) { compiler_error("double declaration"); } + entity->state = EntityState_Resolved; return entity; } diff --git a/src/checker.hpp b/src/checker.hpp index df3364f89..172768835 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -268,6 +268,7 @@ struct CheckerContext { DeclInfo * decl; u32 stmt_state_flags; bool in_defer; // TODO(bill): Actually handle correctly + isize type_level; // TODO(bill): Actually handle correctly String proc_name; Type * type_hint; DeclInfo * curr_proc_decl; diff --git a/src/entity.cpp b/src/entity.cpp index 5c7157490..5dcb3ff7b 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -57,25 +57,32 @@ enum OverloadKind { Overload_Yes = 2, }; +enum EntityState { + EntityState_Unresolved = 0, + EntityState_InProgress = 1, + EntityState_Resolved = 2, +}; + // An Entity is a named "thing" in the language struct Entity { - EntityKind kind; - u64 id; - u32 flags; - Token token; - Scope * scope; - Type * type; - AstNode * identifier; // Can be nullptr - DeclInfo * decl_info; - DeclInfo * parent_proc_decl; // nullptr if in file/global scope + EntityKind kind; + u64 id; + u32 flags; + EntityState state; + Token token; + Scope * scope; + Type * type; + AstNode * identifier; // Can be nullptr + DeclInfo * decl_info; + DeclInfo * parent_proc_decl; // nullptr if in file/global scope // TODO(bill): Cleanup how `using` works for entities - Entity * using_parent; - AstNode * using_expr; + Entity * using_parent; + AstNode * using_expr; - isize order_in_src; - String deprecated_message; + isize order_in_src; + String deprecated_message; union { struct { @@ -173,6 +180,7 @@ gb_global u64 global_entity_id = 0; Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) { Entity *entity = gb_alloc_item(a, Entity); entity->kind = kind; + entity->state = EntityState_Unresolved; entity->scope = scope; entity->token = token; entity->type = type; @@ -180,9 +188,10 @@ Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, return entity; } -Entity *make_entity_variable(gbAllocator a, Scope *scope, Token token, Type *type, bool is_immutable) { +Entity *make_entity_variable(gbAllocator a, Scope *scope, Token token, Type *type, bool is_immutable, EntityState state = EntityState_Unresolved) { Entity *entity = alloc_entity(a, Entity_Variable, scope, token, type); entity->Variable.is_immutable = is_immutable; + entity->state = state; return entity; } @@ -194,6 +203,7 @@ Entity *make_entity_using_variable(gbAllocator a, Entity *parent, Token token, T entity->parent_proc_decl = parent->parent_proc_decl; entity->flags |= EntityFlag_Using; entity->flags |= EntityFlag_Used; + entity->state = EntityState_Resolved; return entity; } @@ -204,8 +214,9 @@ Entity *make_entity_constant(gbAllocator a, Scope *scope, Token token, Type *typ return entity; } -Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *type) { +Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *type, EntityState state = EntityState_Unresolved) { Entity *entity = alloc_entity(a, Entity_TypeName, scope, token, type); + entity->state = state; return entity; } @@ -214,6 +225,7 @@ Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, Entity *entity = make_entity_variable(a, scope, token, type, is_immutable); entity->flags |= EntityFlag_Used; entity->flags |= EntityFlag_Param; + entity->state = EntityState_Resolved; if (is_using) entity->flags |= EntityFlag_Using; if (is_value) entity->flags |= EntityFlag_Value; return entity; @@ -229,12 +241,13 @@ Entity *make_entity_const_param(gbAllocator a, Scope *scope, Token token, Type * } -Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool is_using, i32 field_src_index) { +Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool is_using, i32 field_src_index, EntityState state = EntityState_Unresolved) { Entity *entity = make_entity_variable(a, scope, token, type, false); entity->Variable.field_src_index = field_src_index; entity->Variable.field_index = field_src_index; if (is_using) entity->flags |= EntityFlag_Using; entity->flags |= EntityFlag_Field; + entity->state = state; return entity; } @@ -244,6 +257,7 @@ Entity *make_entity_array_elem(gbAllocator a, Scope *scope, Token token, Type *t entity->Variable.field_index = field_src_index; entity->flags |= EntityFlag_Field; entity->flags |= EntityFlag_ArrayElem; + entity->state = EntityState_Resolved; return entity; } @@ -262,6 +276,7 @@ Entity *make_entity_proc_group(gbAllocator a, Scope *scope, Token token, Type *t Entity *make_entity_builtin(gbAllocator a, Scope *scope, Token token, Type *type, i32 id) { Entity *entity = alloc_entity(a, Entity_Builtin, scope, token, type); entity->Builtin.id = id; + entity->state = EntityState_Resolved; return entity; } @@ -277,6 +292,7 @@ Entity *make_entity_import_name(gbAllocator a, Scope *scope, Token token, Type * entity->ImportName.path = path; entity->ImportName.name = name; entity->ImportName.scope = import_scope; + entity->state = EntityState_Resolved; // TODO(bill): Is this correct? return entity; } @@ -285,6 +301,7 @@ Entity *make_entity_library_name(gbAllocator a, Scope *scope, Token token, Type Entity *entity = alloc_entity(a, Entity_LibraryName, scope, token, type); entity->LibraryName.path = path; entity->LibraryName.name = name; + entity->state = EntityState_Resolved; // TODO(bill): Is this correct? return entity; } @@ -301,6 +318,7 @@ Entity *make_entity_label(gbAllocator a, Scope *scope, Token token, Type *type, AstNode *node) { Entity *entity = alloc_entity(a, Entity_Label, scope, token, type); entity->Label.node = node; + entity->state = EntityState_Resolved; return entity; } diff --git a/src/types.cpp b/src/types.cpp index 3e8d024e4..727dd4a57 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -217,6 +217,7 @@ struct Type { }; + // TODO(bill): Should I add extra information here specifying the kind of selection? // e.g. field, constant, array field, type field, etc. struct Selection { @@ -481,12 +482,6 @@ Type *alloc_type(gbAllocator a, TypeKind kind) { } -Type *make_type_basic(gbAllocator a, BasicType basic) { - Type *t = alloc_type(a, Type_Basic); - t->Basic = basic; - return t; -} - Type *make_type_generic(gbAllocator a, Scope *scope, i64 id, String name, Type *specialized) { Type *t = alloc_type(a, Type_Generic); t->Generic.id = id;