aboutsummaryrefslogtreecommitdiff
path: root/src/checker
diff options
context:
space:
mode:
Diffstat (limited to 'src/checker')
-rw-r--r--src/checker/checker.c93
-rw-r--r--src/checker/expr.c83
-rw-r--r--src/checker/stmt.c84
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;