diff options
Diffstat (limited to 'src/checker')
| -rw-r--r-- | src/checker/checker.c | 93 | ||||
| -rw-r--r-- | src/checker/expr.c | 83 | ||||
| -rw-r--r-- | src/checker/stmt.c | 84 |
3 files changed, 259 insertions, 1 deletions
diff --git a/src/checker/checker.c b/src/checker/checker.c index 3e0606d6a..3af0c2f8b 100644 --- a/src/checker/checker.c +++ b/src/checker/checker.c @@ -1106,6 +1106,94 @@ void check_global_collect_entities_from_file(Checker *c, Scope *parent_scope, As switch (decl->kind) { case_ast_node(bd, BadDecl, decl); case_end; + case_ast_node(gd, GenericDecl, decl); + if (!parent_scope->is_file) { + // NOTE(bill): Within a procedure, variables must be in order + continue; + } + + for_array(spec_index, gd->specs) { + AstNode *spec = gd->specs.e[spec_index]; + switch (spec->kind) { + case_ast_node(vs, ValueSpec, spec); + switch (vs->keyword) { + case Token_var: { + // NOTE(bill): You need to store the entity information here unline a constant declaration + isize entity_count = vs->names.count; + isize entity_index = 0; + Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count); + DeclInfo *di = NULL; + if (vs->values.count > 0) { + di = make_declaration_info(heap_allocator(), parent_scope); + di->entities = entities; + di->entity_count = entity_count; + di->type_expr = vs->type; + di->init_expr = vs->values.e[0]; + } + + for_array(i, vs->names) { + AstNode *name = vs->names.e[i]; + AstNode *value = NULL; + if (i < vs->values.count) { + value = vs->values.e[i]; + } + if (name->kind != AstNode_Ident) { + error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind])); + continue; + } + Entity *e = make_entity_variable(c->allocator, parent_scope, name->Ident, NULL); + e->identifier = name; + entities[entity_index++] = e; + + DeclInfo *d = di; + if (d == NULL) { + AstNode *init_expr = value; + d = make_declaration_info(heap_allocator(), e->scope); + d->type_expr = vs->type; + d->init_expr = init_expr; + d->var_decl_tags = gd->tags; + } + + add_entity_and_decl_info(c, name, e, d); + } + } break; + + case Token_const: { + for_array(i, vs->values) { + AstNode *name = vs->names.e[i]; + AstNode *value = unparen_expr(vs->values.e[i]); + if (name->kind != AstNode_Ident) { + error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind])); + continue; + } + + ExactValue v = {ExactValue_Invalid}; + Entity *e = make_entity_constant(c->allocator, parent_scope, name->Ident, NULL, v); + e->identifier = name; + DeclInfo *di = make_declaration_info(c->allocator, e->scope); + di->type_expr = vs->type; + di->init_expr = value; + add_entity_and_decl_info(c, name, e, di); + } + + isize lhs_count = vs->names.count; + isize rhs_count = vs->values.count; + + if (rhs_count == 0 && vs->type == NULL) { + error_node(decl, "Missing type or initial expression"); + } else if (lhs_count < rhs_count) { + error_node(decl, "Extra initial expression"); + } + } break; + } + case_end; + + default: + error(ast_node_token(spec), "Invalid specification in declaration: `%.*s`", LIT(ast_node_strings[spec->kind])); + break; + } + } + case_end; case_ast_node(id, ImportDecl, decl); if (!parent_scope->is_file) { // NOTE(bill): _Should_ be caught by the parser @@ -1428,7 +1516,10 @@ void check_parsed_files(Checker *c) { ImplicitValueInfo *ivi = &implicit_value_infos[i]; Entity *backing = scope_lookup_entity(e->scope, ivi->backing_name); - GB_ASSERT(backing != NULL); + // GB_ASSERT(backing != NULL); + if (backing == NULL) { + gb_exit(1); + } e->ImplicitValue.backing = backing; } diff --git a/src/checker/expr.c b/src/checker/expr.c index 8900cb4a7..3f8215f55 100644 --- a/src/checker/expr.c +++ b/src/checker/expr.c @@ -80,6 +80,89 @@ void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntitie case_ast_node(ws, WhenStmt, node); // Will be handled later case_end; + case_ast_node(gd, GenericDecl, node); + for_array(spec_index, gd->specs) { + AstNode *spec = gd->specs.e[spec_index]; + switch (spec->kind) { + case_ast_node(vs, ValueSpec, spec); + switch (vs->keyword) { + case Token_var: + break; + + case Token_const: { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + + isize entity_count = vs->names.count; + isize entity_index = 0; + Entity **entities = gb_alloc_array(c->tmp_allocator, Entity *, entity_count); + + for_array(i, vs->values) { + AstNode *name = vs->names.e[i]; + AstNode *value = unparen_expr(vs->values.e[i]); + if (name->kind != AstNode_Ident) { + error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind])); + entities[entity_index++] = NULL; + continue; + } + + ExactValue v = {ExactValue_Invalid}; + Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v); + e->identifier = name; + entities[entity_index++] = e; + DeclInfo *d = make_declaration_info(c->allocator, e->scope); + d->type_expr = vs->type; + d->init_expr = value; + add_entity_and_decl_info(c, name, e, d); + + DelayedEntity delay = {name, e, d}; + array_add(delayed_entities, delay); + } + + isize lhs_count = vs->names.count; + isize rhs_count = vs->values.count; + + // TODO(bill): Better error messages or is this good enough? + if (rhs_count == 0 && vs->type == NULL) { + error_node(node, "Missing type or initial expression"); + } else if (lhs_count < rhs_count) { + error_node(node, "Extra initial expression"); + } + + if (dof != NULL) { + // NOTE(bill): Within a record + for_array(i, vs->names) { + Entity *e = entities[i]; + if (e == NULL) { + continue; + } + AstNode *name = vs->names.e[i]; + if (name->kind != AstNode_Ident) { + continue; + } + Token name_token = name->Ident; + if (str_eq(name_token.string, str_lit("_"))) { + dof->other_fields[dof->other_field_index++] = e; + } else { + HashKey key = hash_string(name_token.string); + if (map_entity_get(dof->entity_map, key) != NULL) { + // TODO(bill): Scope checking already checks the declaration + error(name_token, "`%.*s` is already declared in this record", LIT(name_token.string)); + } else { + map_entity_set(dof->entity_map, key, e); + dof->other_fields[dof->other_field_index++] = e; + } + add_entity(c, c->context.scope, name, e); + } + } + } + + gb_temp_arena_memory_end(tmp); + } break; + } + case_end; + } + } + case_end; case_ast_node(cd, ConstDecl, node); gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); diff --git a/src/checker/stmt.c b/src/checker/stmt.c index f1074e2e6..7e1578987 100644 --- a/src/checker/stmt.c +++ b/src/checker/stmt.c @@ -1074,6 +1074,90 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { check_var_decl_node(c, node); case_end; + case_ast_node(gd, GenericDecl, node); + for_array(spec_index, gd->specs) { + AstNode *spec = gd->specs.e[spec_index]; + switch (spec->kind) { + case_ast_node(vs, ValueSpec, spec); + switch (vs->keyword) { + case Token_var: { + isize entity_count = vs->names.count; + isize entity_index = 0; + Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count); + + for_array(i, vs->names) { + AstNode *name = vs->names.e[i]; + Entity *entity = NULL; + if (name->kind == AstNode_Ident) { + Token token = name->Ident; + String str = token.string; + Entity *found = NULL; + // NOTE(bill): Ignore assignments to `_` + if (str_ne(str, str_lit("_"))) { + found = current_scope_lookup_entity(c->context.scope, str); + } + if (found == NULL) { + entity = make_entity_variable(c->allocator, c->context.scope, token, NULL); + add_entity_definition(&c->info, name, entity); + } else { + TokenPos pos = found->token.pos; + error(token, + "Redeclaration of `%.*s` in this scope\n" + "\tat %.*s(%td:%td)", + LIT(str), LIT(pos.file), pos.line, pos.column); + entity = found; + } + } else { + error_node(name, "A variable declaration must be an identifier"); + } + if (entity == NULL) { + entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name)); + } + entities[entity_index++] = entity; + } + + Type *init_type = NULL; + if (vs->type) { + init_type = check_type_extra(c, vs->type, NULL); + if (init_type == NULL) { + init_type = t_invalid; + } + } + + for (isize i = 0; i < entity_count; i++) { + Entity *e = entities[i]; + GB_ASSERT(e != NULL); + if (e->flags & EntityFlag_Visited) { + e->type = t_invalid; + continue; + } + e->flags |= EntityFlag_Visited; + + if (e->type == NULL) + e->type = init_type; + } + + check_init_variables(c, entities, entity_count, vs->values, str_lit("variable declaration")); + + for_array(i, vs->names) { + if (entities[i] != NULL) { + add_entity(c, c->context.scope, vs->names.e[i], entities[i]); + } + } + } break; + + case Token_const: + break; + } + case_end; + + default: + error(ast_node_token(spec), "Invalid specification in declaration: `%.*s`", LIT(ast_node_strings[spec->kind])); + break; + } + } + case_end; + case_ast_node(cd, ConstDecl, node); // NOTE(bill): Handled elsewhere case_end; |