Match statements; Type System change (Type_Record for all sum and product types)

This commit is contained in:
Ginger Bill
2016-09-01 20:38:44 +01:00
parent ff6e21cb87
commit fa09d805e2
16 changed files with 1172 additions and 622 deletions
+93 -16
View File
@@ -339,6 +339,7 @@ Entity *scope_insert_entity(Scope *s, Entity *entity) {
return NULL;
}
void add_dependency(DeclInfo *d, Entity *e) {
map_set(&d->deps, hash_pointer(e), cast(b32)true);
}
@@ -543,6 +544,7 @@ b32 add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column);
return false;
} else {
gb_printf_err("!!Here\n");
error(&c->error_collector, entity->token,
"Redeclararation of `%.*s` in this scope\n"
"\tat %.*s(%td:%td)",
@@ -557,6 +559,78 @@ b32 add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
return true;
}
/*
b32 add_proc_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
GB_ASSERT(entity->kind == Entity_Procedure);
auto error_proc_redecl = [](Checker *c, Token token, Entity *other_entity, char *extra_msg) {
error(&c->error_collector, token,
"Redeclararation of `%.*s` in this scope %s\n"
"\tat %.*s(%td:%td)",
LIT(other_entity->token.string),
extra_msg,
LIT(other_entity->token.pos.file), other_entity->token.pos.line, other_entity->token.pos.column);
};
String name = entity->token.string;
HashKey key = hash_string(name);
b32 insert_overload = false;
if (!are_strings_equal(name, make_string("_"))) {
Entity *insert_entity = scope_insert_entity(scope, entity);
if (insert_entity != NULL) {
if (insert_entity != entity) {
isize count = multi_map_count(&scope->elements, key);
GB_ASSERT(count > 0);
Entity **entities = gb_alloc_array(gb_heap_allocator(), Entity *, count);
defer (gb_free(gb_heap_allocator(), entities));
multi_map_get_all(&scope->elements, key, entities);
for (isize i = 0; i < count; i++) {
Entity *e = entities[i];
if (e == entity) {
continue;
}
if (e->kind == Entity_Procedure) {
Type *proc_type = entity->type;
Type *other_proc_type = e->type;
// gb_printf_err("%s == %s\n", type_to_string(proc_type), type_to_string(other_proc_type));
if (are_types_identical(proc_type, other_proc_type)) {
error_proc_redecl(c, entity->token, e, "with identical types");
return false;
}
if (proc_type != NULL && other_proc_type != NULL) {
Type *params = proc_type->Proc.params;
Type *other_params = other_proc_type->Proc.params;
if (are_types_identical(params, other_params)) {
error_proc_redecl(c, entity->token, e, "with 2identical parameters");
return false;
}
}
} else {
error_proc_redecl(c, entity->token, e, "");
return false;
}
}
insert_overload = true;
}
}
}
if (insert_overload) {
multi_map_insert(&scope->elements, key, entity);
}
if (identifier != NULL)
add_entity_definition(&c->info, identifier, entity);
return true;
}
*/
void add_entity_use(CheckerInfo *i, AstNode *identifier, Entity *entity) {
GB_ASSERT(identifier != NULL);
GB_ASSERT(identifier->kind == AstNode_Ident);
@@ -566,7 +640,7 @@ void add_entity_use(CheckerInfo *i, AstNode *identifier, Entity *entity) {
void add_file_entity(Checker *c, AstNode *identifier, Entity *e, DeclInfo *d) {
GB_ASSERT(are_strings_equal(identifier->Ident.token.string, e->token.string));
GB_ASSERT(are_strings_equal(identifier->Ident.string, e->token.string));
add_entity(c, c->global_scope, identifier, e);
map_set(&c->info.entities, hash_pointer(e), d);
@@ -608,7 +682,6 @@ void add_curr_ast_file(Checker *c, AstFile *file) {
#include "expr.cpp"
#include "stmt.cpp"
@@ -658,9 +731,8 @@ void check_parsed_files(Checker *c) {
for (AstNode *name = vd->name_list, *value = vd->value_list;
name != NULL && value != NULL;
name = name->next, value = value->next) {
ast_node(n, Ident, name);
ExactValue v = {ExactValue_Invalid};
Entity *e = make_entity_constant(c->allocator, c->context.scope, n->token, NULL, v);
Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v);
DeclInfo *di = make_declaration_info(c->allocator, c->global_scope);
di->type_expr = vd->type;
di->init_expr = value;
@@ -692,8 +764,7 @@ void check_parsed_files(Checker *c) {
AstNode *value = vd->value_list;
for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
ast_node(n, Ident, name);
Entity *e = make_entity_variable(c->allocator, c->global_scope, n->token, NULL);
Entity *e = make_entity_variable(c->allocator, c->global_scope, name->Ident, NULL);
entities[entity_index++] = e;
DeclInfo *d = di;
@@ -716,7 +787,7 @@ void check_parsed_files(Checker *c) {
case_ast_node(td, TypeDecl, decl);
ast_node(n, Ident, td->name);
Entity *e = make_entity_type_name(c->allocator, c->global_scope, n->token, NULL);
Entity *e = make_entity_type_name(c->allocator, c->global_scope, *n, NULL);
DeclInfo *d = make_declaration_info(c->allocator, e->scope);
d->type_expr = td->type;
add_file_entity(c, td->name, e, d);
@@ -724,9 +795,8 @@ void check_parsed_files(Checker *c) {
case_ast_node(pd, ProcDecl, decl);
ast_node(n, Ident, pd->name);
Token token = n->token;
Token token = *n;
Entity *e = make_entity_procedure(c->allocator, c->global_scope, token, NULL);
add_entity(c, c->global_scope, pd->name, e);
DeclInfo *d = make_declaration_info(c->allocator, e->scope);
d->proc_decl = decl;
map_set(&c->info.entities, hash_pointer(e), d);
@@ -747,14 +817,21 @@ void check_parsed_files(Checker *c) {
}
}
auto check_global_entity = [](Checker *c, EntityKind kind) {
gb_for_array(i, c->info.entities.entries) {
auto *entry = &c->info.entities.entries[i];
Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
if (e->kind == kind) {
DeclInfo *d = entry->value;
check_entity_decl(c, e, d, NULL);
}
}
};
gb_for_array(i, c->info.entities.entries) {
auto *entry = &c->info.entities.entries[i];
Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
DeclInfo *d = entry->value;
check_entity_decl(c, e, d, NULL);
}
check_global_entity(c, Entity_TypeName);
check_global_entity(c, Entity_Constant);
check_global_entity(c, Entity_Procedure);
check_global_entity(c, Entity_Variable);
// Check procedure bodies
gb_for_array(i, c->procs) {
+1
View File
@@ -27,6 +27,7 @@ String const entity_strings[] = {
typedef i64 EntityGuid;
typedef struct Type Type;
struct Entity {
EntityKind kind;
+78 -86
View File
@@ -23,9 +23,9 @@ b32 check_is_assignable_to_using_subtype(Type *dst, Type *src) {
b32 src_is_ptr = src != prev_src;
// b32 dst_is_ptr = dst != prev_dst;
if (src->kind == Type_Struct) {
for (isize i = 0; i < src->Struct.field_count; i++) {
Entity *f = src->Struct.fields[i];
if (is_type_struct(src)) {
for (isize i = 0; i < src->Record.field_count; i++) {
Entity *f = src->Record.fields[i];
if (f->kind == Entity_Variable && f->Variable.anonymous) {
if (are_types_identical(dst, f->type)) {
return true;
@@ -152,13 +152,9 @@ void populate_using_entity_map(Checker *c, AstNode *node, Type *t, Map<Entity *>
gbString str = expr_to_string(node);
defer (gb_string_free(str));
switch (t->kind) {
// IMPORTANT HACK(bill): The positions of fields and field_count
// must be same for Struct and Union
case Type_Struct:
case Type_Union:
for (isize i = 0; i < t->Struct.field_count; i++) {
Entity *f = t->Struct.fields[i];
if (t->kind == Type_Record) {
for (isize i = 0; i < t->Record.field_count; i++) {
Entity *f = t->Record.fields[i];
GB_ASSERT(f->kind == Entity_Variable);
String name = f->token.string;
HashKey key = hash_string(name);
@@ -175,7 +171,6 @@ void populate_using_entity_map(Checker *c, AstNode *node, Type *t, Map<Entity *>
}
}
}
break;
}
}
@@ -215,8 +210,7 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
name = name->next, value = value->next) {
GB_ASSERT(name->kind == AstNode_Ident);
ExactValue v = {ExactValue_Invalid};
ast_node(i, Ident, name);
Token name_token = i->token;
Token name_token = name->Ident;
Entity *e = make_entity_constant(c->allocator, c->context.scope, name_token, NULL, v);
entities[entity_index++] = e;
check_const_decl(c, e, vd->type, value);
@@ -235,7 +229,7 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
AstNode *name = vd->name_list;
for (isize i = 0; i < entity_count; i++, name = name->next) {
Entity *e = entities[i];
Token name_token = name->Ident.token;
Token name_token = name->Ident;
HashKey key = hash_string(name_token.string);
if (map_get(&entity_map, key) != NULL) {
// TODO(bill): Scope checking already checks the declaration
@@ -248,10 +242,9 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
}
} else if (decl->kind == AstNode_TypeDecl) {
ast_node(td, TypeDecl, decl);
ast_node(name, Ident, td->name);
Token name_token = name->token;
Token name_token = td->name->Ident;
Entity *e = make_entity_type_name(c->allocator, c->context.scope, name->token, NULL);
Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, NULL);
check_type_decl(c, e, td->type, NULL, NULL);
add_entity(c, c->context.scope, td->name, e);
@@ -285,8 +278,7 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
}
for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
ast_node(i, Ident, name);
Token name_token = i->token;
Token name_token = name->Ident;
Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, vd->is_using);
HashKey key = hash_string(name_token.string);
@@ -304,9 +296,8 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
if (vd->is_using) {
Type *t = get_base_type(type_deref(type));
if (t->kind != Type_Struct &&
t->kind != Type_Union) {
Token name_token = vd->name_list->Ident.token;
if (!is_type_struct(t) && !is_type_raw_union(t)) {
Token name_token = vd->name_list->Ident;
error(&c->error_collector, name_token, "`using` on a field `%.*s` must be a structure or union", LIT(name_token.string));
continue;
}
@@ -322,12 +313,12 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
void check_struct_type(Checker *c, Type *struct_type, AstNode *node, CycleChecker *cycle_checker) {
GB_ASSERT(node->kind == AstNode_StructType);
GB_ASSERT(struct_type->kind == Type_Struct);
GB_ASSERT(is_type_struct(struct_type));
ast_node(st, StructType, node);
// TODO(bill): check_struct_type and check_union_type are very similar so why not and try to merge them better
isize field_count = 0;
isize field_count = 0;
isize other_field_count = 0;
for (AstNode *decl = st->decl_list; decl != NULL; decl = decl->next) {
switch (decl->kind) {
@@ -350,17 +341,17 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node, CycleChecke
check_fields(c, node, st->decl_list, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("struct"));
struct_type->Struct.is_packed = st->is_packed;
struct_type->Struct.fields = fields;
struct_type->Struct.field_count = field_count;
struct_type->Struct.other_fields = other_fields;
struct_type->Struct.other_field_count = other_field_count;
struct_type->Record.struct_is_packed = st->is_packed;
struct_type->Record.fields = fields;
struct_type->Record.field_count = field_count;
struct_type->Record.other_fields = other_fields;
struct_type->Record.other_field_count = other_field_count;
}
void check_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker *cycle_checker) {
GB_ASSERT(node->kind == AstNode_UnionType);
GB_ASSERT(union_type->kind == Type_Union);
ast_node(ut, UnionType, node);
void check_raw_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker *cycle_checker) {
GB_ASSERT(node->kind == AstNode_RawUnionType);
GB_ASSERT(is_type_raw_union(union_type));
ast_node(ut, RawUnionType, node);
isize field_count = 0;
isize other_field_count = 0;
@@ -385,16 +376,16 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker
check_fields(c, node, ut->decl_list, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("union"));
union_type->Union.fields = fields;
union_type->Union.field_count = field_count;
union_type->Union.other_fields = other_fields;
union_type->Union.other_field_count = other_field_count;
union_type->Record.fields = fields;
union_type->Record.field_count = field_count;
union_type->Record.other_fields = other_fields;
union_type->Record.other_field_count = other_field_count;
}
void check_enum_type(Checker *c, Type *enum_type, AstNode *node) {
GB_ASSERT(node->kind == AstNode_EnumType);
GB_ASSERT(enum_type->kind == Type_Enum);
GB_ASSERT(is_type_enum(enum_type));
ast_node(et, EnumType, node);
Map<Entity *> entity_map = {};
@@ -413,14 +404,14 @@ void check_enum_type(Checker *c, Type *enum_type, AstNode *node) {
if (base_type == NULL) {
base_type = t_int;
}
enum_type->Enum.base = base_type;
enum_type->Record.enum_base = base_type;
Entity **fields = gb_alloc_array(c->allocator, Entity *, et->field_count);
isize field_index = 0;
ExactValue iota = make_exact_value_integer(-1);
for (AstNode *field = et->field_list; field != NULL; field = field->next) {
ast_node(f, FieldValue, field);
Token name_token = f->field->Ident.token;
Token name_token = f->field->Ident;
Operand o = {};
if (f->value != NULL) {
@@ -455,8 +446,8 @@ void check_enum_type(Checker *c, Type *enum_type, AstNode *node) {
}
add_entity_use(&c->info, f->field, e);
}
enum_type->Enum.fields = fields;
enum_type->Enum.field_count = et->field_count;
enum_type->Record.other_fields = fields;
enum_type->Record.other_field_count = et->field_count;
}
Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize field_count) {
@@ -474,8 +465,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize fiel
Type *type = check_type(c, type_expr);
for (AstNode *name = f->name_list; name != NULL; name = name->next) {
if (name->kind == AstNode_Ident) {
ast_node(i, Ident, name);
Entity *param = make_entity_param(c->allocator, scope, i->token, type, f->is_using);
Entity *param = make_entity_param(c->allocator, scope, name->Ident, type, f->is_using);
add_entity(c, scope, name, param);
variables[variable_index++] = param;
} else {
@@ -536,11 +526,10 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Cycl
GB_ASSERT(n->kind == AstNode_Ident);
o->mode = Addressing_Invalid;
o->expr = n;
ast_node(i, Ident, n);
Entity *e = scope_lookup_entity(c, c->context.scope, i->token.string);
Entity *e = scope_lookup_entity(c, c->context.scope, n->Ident.string);
if (e == NULL) {
error(&c->error_collector, i->token,
"Undeclared type or identifier `%.*s`", LIT(i->token.string));
error(&c->error_collector, n->Ident,
"Undeclared type or identifier `%.*s`", LIT(n->Ident.string));
return;
}
add_entity_use(&c->info, n, e);
@@ -563,7 +552,7 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Cycl
}
if (e->type == NULL) {
GB_PANIC("Compiler error: How did this happen? type: %s; identifier: %.*s\n", type_to_string(e->type), LIT(i->token.string));
GB_PANIC("Compiler error: How did this happen? type: %s; identifier: %.*s\n", type_to_string(e->type), LIT(n->Ident.string));
return;
}
@@ -739,17 +728,17 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c
check_open_scope(c, e);
check_struct_type(c, type, e, cycle_checker);
check_close_scope(c);
type->Struct.node = e;
type->Record.node = e;
goto end;
case_end;
case_ast_node(st, UnionType, e);
type = make_type_union(c->allocator);
case_ast_node(st, RawUnionType, e);
type = make_type_raw_union(c->allocator);
set_base_type(named_type, type);
check_open_scope(c, e);
check_union_type(c, type, e, cycle_checker);
check_raw_union_type(c, type, e, cycle_checker);
check_close_scope(c);
type->Union.node = e;
type->Record.node = e;
goto end;
case_end;
@@ -757,6 +746,7 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c
type = make_type_enum(c->allocator);
set_base_type(named_type, type);
check_enum_type(c, type, e);
type->Record.node = e;
goto end;
case_end;
@@ -1249,10 +1239,9 @@ String check_down_cast_name(Type *dst_, Type *src_) {
Type *dst = type_deref(dst_);
Type *src = type_deref(src_);
Type *dst_s = get_base_type(dst);
GB_ASSERT(dst_s->kind == Type_Struct || dst_s->kind == Type_Union);
// HACK(bill): struct/union variable overlay from unsafe tagged union
for (isize i = 0; i < dst_s->Struct.field_count; i++) {
Entity *f = dst_s->Struct.fields[i];
GB_ASSERT(is_type_struct(dst_s) || is_type_raw_union(dst_s));
for (isize i = 0; i < dst_s->Record.field_count; i++) {
Entity *f = dst_s->Record.fields[i];
GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field);
if (f->Variable.anonymous) {
if (are_types_identical(f->type, src_)) {
@@ -1397,7 +1386,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
Type *bsrc = get_base_type(src);
Type *bdst = get_base_type(dst);
if (!(bsrc->kind == Type_Struct || bsrc->kind == Type_Union)) {
if (!(is_type_struct(bsrc) || is_type_raw_union(bsrc))) {
gbString expr_str = expr_to_string(node);
defer (gb_string_free(expr_str));
error(&c->error_collector, ast_node_token(node), "Can only `down_cast` pointer from structs or unions: `%s`", expr_str);
@@ -1405,7 +1394,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
return;
}
if (!(bdst->kind == Type_Struct || bdst->kind == Type_Union)) {
if (!(is_type_struct(bdst) || is_type_struct(bdst))) {
gbString expr_str = expr_to_string(node);
defer (gb_string_free(expr_str));
error(&c->error_collector, ast_node_token(node), "Can only `down_cast` pointer to structs or unions: `%s`", expr_str);
@@ -1740,7 +1729,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
AstNode *op_expr = se->expr;
AstNode *selector = se->selector;
if (selector) {
Entity *entity = lookup_field(operand->type, selector->Ident.token.string, operand->mode == Addressing_Type).entity;
Entity *entity = lookup_field(operand->type, selector->Ident.string, operand->mode == Addressing_Type).entity;
if (entity == NULL) {
gbString op_str = expr_to_string(op_expr);
gbString type_str = type_to_string(operand->type);
@@ -1787,7 +1776,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
if (err) {
ast_node(proc, Ident, ce->proc);
error(&c->error_collector, ce->close, "`%s` arguments for `%.*s`, expected %td, got %td",
err, LIT(proc->token.string),
err, LIT(proc->string),
bp->arg_count, ce->arg_list_count);
return false;
}
@@ -1931,7 +1920,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
Type *type = get_base_type(check_type(c, ce->arg_list));
AstNode *field_arg = unparen_expr(ce->arg_list->next);
if (type) {
if (type->kind != Type_Struct) {
if (!is_type_struct(type)) {
error(&c->error_collector, ast_node_token(ce->arg_list), "Expected a structure type for `offset_of`");
return false;
}
@@ -1944,11 +1933,11 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
ast_node(arg, Ident, field_arg);
Selection sel = lookup_field(type, arg->token.string, operand->mode == Addressing_Type);
Selection sel = lookup_field(type, arg->string, operand->mode == Addressing_Type);
if (sel.entity == NULL) {
gbString type_str = type_to_string(type);
error(&c->error_collector, ast_node_token(ce->arg_list),
"`%s` has no field named `%.*s`", type_str, LIT(arg->token.string));
"`%s` has no field named `%.*s`", type_str, LIT(arg->string));
return false;
}
@@ -1975,17 +1964,18 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
Type *type = operand->type;
if (get_base_type(type)->kind == Type_Pointer) {
Type *p = get_base_type(type);
if (get_base_type(p)->kind == Type_Struct)
if (is_type_struct(p)) {
type = p->Pointer.elem;
}
}
ast_node(i, Ident, s->selector);
Selection sel = lookup_field(type, i->token.string, operand->mode == Addressing_Type);
Selection sel = lookup_field(type, i->string, operand->mode == Addressing_Type);
if (sel.entity == NULL) {
gbString type_str = type_to_string(type);
error(&c->error_collector, ast_node_token(arg),
"`%s` has no field named `%.*s`", type_str, LIT(i->token.string));
"`%s` has no field named `%.*s`", type_str, LIT(i->string));
return false;
}
@@ -2357,7 +2347,6 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
return true;
}
void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode *call) {
GB_ASSERT(call->kind == AstNode_CallExpr);
GB_ASSERT(proc_type->kind == Type_Proc);
@@ -2439,8 +2428,9 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
check_expr_or_type(c, operand, ce->proc);
if (operand->mode == Addressing_Invalid) {
for (AstNode *arg = ce->arg_list; arg != NULL; arg = arg->next)
for (AstNode *arg = ce->arg_list; arg != NULL; arg = arg->next) {
check_expr_base(c, operand, arg);
}
operand->mode = Addressing_Invalid;
operand->expr = call;
return Expr_Stmt;
@@ -2449,8 +2439,9 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
if (operand->mode == Addressing_Builtin) {
i32 id = operand->builtin_id;
if (!check_builtin_procedure(c, operand, call, id))
if (!check_builtin_procedure(c, operand, call, id)) {
operand->mode = Addressing_Invalid;
}
operand->expr = call;
return builtin_procs[id].kind;
}
@@ -2470,15 +2461,14 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
check_call_arguments(c, operand, proc_type, call);
auto *proc = &proc_type->Proc;
if (proc->result_count == 0) {
if (proc_type->Proc.result_count == 0) {
operand->mode = Addressing_NoValue;
} else if (proc->result_count == 1) {
} else if (proc_type->Proc.result_count == 1) {
operand->mode = Addressing_Value;
operand->type = proc->results->Tuple.variables[0]->type;
operand->type = proc_type->Proc.results->Tuple.variables[0]->type;
} else {
operand->mode = Addressing_Value;
operand->type = proc->results;
operand->type = proc_type->Proc.results;
}
operand->expr = call;
@@ -2580,12 +2570,14 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
Type *t = get_base_type(type);
switch (t->kind) {
case Type_Struct: {
case Type_Record: {
if (!is_type_struct(t))
break;
if (cl->elem_count == 0)
break; // NOTE(bill): No need to init
{ // Checker values
AstNode *elem = cl->elem_list;
isize field_count = t->Struct.field_count;
isize field_count = t->Record.field_count;
if (elem->kind == AstNode_FieldValue) {
b32 *fields_visited = gb_alloc_array(c->allocator, b32, field_count);
@@ -2605,9 +2597,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
"Invalid field name `%s` in structure literal", expr_str);
continue;
}
String name = kv->field->Ident.token.string;
String name = kv->field->Ident.string;
Selection sel = lookup_field(type, kv->field->Ident.token.string, o->mode == Addressing_Type);
Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
if (sel.entity == NULL) {
error(&c->error_collector, ast_node_token(elem),
"Unknown field `%.*s` in structure literal", LIT(name));
@@ -2620,7 +2612,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
continue;
}
Entity *field = t->Struct.fields[sel.index[0]];
Entity *field = t->Record.fields[sel.index[0]];
add_entity_use(&c->info, kv->field, field);
if (fields_visited[sel.index[0]]) {
@@ -2643,7 +2635,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
"Mixture of `field = value` and value elements in a structure literal is not allowed");
continue;
}
Entity *field = t->Struct.fields[index];
Entity *field = t->Record.fields[index];
check_expr(c, o, elem);
if (index >= field_count) {
@@ -2955,7 +2947,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
case AstNode_ArrayType:
case AstNode_VectorType:
case AstNode_StructType:
case AstNode_UnionType:
case AstNode_RawUnionType:
o->mode = Addressing_Type;
o->type = check_type(c, node);
break;
@@ -3090,7 +3082,7 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
break;
case_ast_node(i, Ident, node);
str = string_append_token(str, i->token);
str = string_append_token(str, *i);
case_end;
case_ast_node(bl, BasicLit, node);
@@ -3229,8 +3221,8 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
str = gb_string_appendc(str, "}");
case_end;
case_ast_node(st, UnionType, node);
str = gb_string_appendc(str, "union{");
case_ast_node(st, RawUnionType, node);
str = gb_string_appendc(str, "raw_union{");
// str = write_field_list_to_string(str, st->decl_list, ", ");
str = gb_string_appendc(str, "}");
case_end;
+283 -66
View File
@@ -1,24 +1,35 @@
// Statements and Declarations
enum StatementFlag : u32 {
Statement_BreakAllowed = GB_BIT(0),
Statement_ContinueAllowed = GB_BIT(1),
// Statement_FallthroughAllowed = GB_BIT(2), // TODO(bill): fallthrough
enum StmtFlag : u32 {
Stmt_BreakAllowed = GB_BIT(0),
Stmt_ContinueAllowed = GB_BIT(1),
Stmt_FallthroughAllowed = GB_BIT(2), // TODO(bill): fallthrough
};
void check_stmt(Checker *c, AstNode *node, u32 flags);
void check_stmt_list(Checker *c, AstNode *node, u32 flags) {
for (; node != NULL; node = node->next) {
if (node->kind != AstNode_EmptyStmt) {
check_stmt(c, node, flags);
void check_stmt_list(Checker *c, AstNode *list, isize list_count, u32 flags) {
b32 ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
u32 f = flags & (~Stmt_FallthroughAllowed);
isize i = 0;
for (AstNode *n = list; n != NULL; n = n->next, i++) {
if (n->kind == AstNode_EmptyStmt) {
continue;
}
u32 new_flags = f;
if (ft_ok && i+1 == list_count) {
new_flags |= Stmt_FallthroughAllowed;
}
check_stmt(c, n, new_flags);
}
}
b32 check_is_terminating(Checker *c, AstNode *node);
b32 check_is_terminating(AstNode *node);
b32 check_has_break(AstNode *stmt, b32 implicit);
b32 check_is_terminating_list(Checker *c, AstNode *list) {
b32 check_is_terminating_list(AstNode *list) {
// Get to end of list
for (; list != NULL; list = list->next) {
if (list->next == NULL)
@@ -28,43 +39,94 @@ b32 check_is_terminating_list(Checker *c, AstNode *list) {
// Iterate backwards
for (AstNode *n = list; n != NULL; n = n->prev) {
if (n->kind != AstNode_EmptyStmt)
return check_is_terminating(c, n);
return check_is_terminating(n);
}
return false;
}
b32 check_has_break_list(AstNode *list, b32 implicit) {
for (AstNode *stmt = list; stmt != NULL; stmt = stmt->next) {
if (check_has_break(stmt, implicit)) {
return true;
}
}
return false;
}
b32 check_has_break(AstNode *stmt, b32 implicit) {
switch (stmt->kind) {
case AstNode_BranchStmt:
if (stmt->BranchStmt.token.kind == Token_break) {
return implicit;
}
break;
case AstNode_BlockStmt:
return check_has_break_list(stmt->BlockStmt.list, implicit);
case AstNode_IfStmt:
if (check_has_break(stmt->IfStmt.body, implicit) ||
(stmt->IfStmt.else_stmt != NULL && check_has_break(stmt->IfStmt.else_stmt, implicit))) {
return true;
}
break;
case AstNode_CaseClause:
return check_has_break_list(stmt->CaseClause.stmts, implicit);
}
return false;
}
// NOTE(bill): The last expression has to be a `return` statement
// TODO(bill): This is a mild hack and should be probably handled properly
// TODO(bill): Warn/err against code after `return` that it won't be executed
b32 check_is_terminating(Checker *c, AstNode *node) {
b32 check_is_terminating(AstNode *node) {
switch (node->kind) {
case_ast_node(rs, ReturnStmt, node);
return true;
case_end;
case_ast_node(bs, BlockStmt, node);
return check_is_terminating_list(c, bs->list);
return check_is_terminating_list(bs->list);
case_end;
case_ast_node(es, ExprStmt, node);
return check_is_terminating(c, es->expr);
return check_is_terminating(es->expr);
case_end;
case_ast_node(is, IfStmt, node);
if (is->else_stmt != NULL) {
if (check_is_terminating(c, is->body) &&
check_is_terminating(c, is->else_stmt)) {
if (check_is_terminating(is->body) &&
check_is_terminating(is->else_stmt)) {
return true;
}
}
case_end;
case_ast_node(fs, ForStmt, node);
if (fs->cond == NULL) {
if (fs->cond == NULL && !check_has_break(fs->body, true)) {
return true;
}
case_end;
case_ast_node(ms, MatchStmt, node);
b32 has_default = false;
for (AstNode *clause = ms->body->BlockStmt.list; clause != NULL; clause = clause->next) {
ast_node(cc, CaseClause, clause);
if (cc->list == NULL) {
has_default = true;
}
if (!check_is_terminating_list(cc->stmts) ||
check_has_break_list(cc->stmts, true)) {
return false;
}
}
return has_default;
case_end;
}
return false;
@@ -81,7 +143,7 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
// NOTE(bill): Ignore assignments to `_`
if (node->kind == AstNode_Ident) {
ast_node(i, Ident, node);
if (are_strings_equal(i->token.string, make_string("_"))) {
if (are_strings_equal(i->string, make_string("_"))) {
add_entity_definition(&c->info, node, NULL);
check_assignment(c, op_a, NULL, make_string("assignment to `_` identifier"));
if (op_a->mode == Addressing_Invalid)
@@ -94,7 +156,7 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
b32 used = false;
if (node->kind == AstNode_Ident) {
ast_node(i, Ident, node);
e = scope_lookup_entity(c, c->context.scope, i->token.string);
e = scope_lookup_entity(c, c->context.scope, i->string);
if (e != NULL && e->kind == Entity_Variable) {
used = e->Variable.used; // TODO(bill): Make backup just in case
}
@@ -306,10 +368,8 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
continue;
String name = e->token.string;
Type *t = get_base_type(type_deref(e->type));
if (t->kind == Type_Struct || t->kind == Type_Union) {
// IMPORTANT HACK(bill): Entity_(Struct|Union) overlap in some memory allowing
// for some variables to accessed to same
Scope **found = map_get(&c->info.scopes, hash_pointer(t->Struct.node));
if (is_type_struct(t) || is_type_raw_union(t)) {
Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node));
GB_ASSERT(found != NULL);
gb_for_array(i, (*found)->elements.entries) {
Entity *f = (*found)->elements.entries[i].value;
@@ -323,7 +383,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
}
}
} else {
error(&c->error_collector, e->token, "`using` can only be applied to variables of type struct or union");
error(&c->error_collector, e->token, "`using` can only be applied to variables of type struct or raw_union");
break;
}
}
@@ -332,9 +392,9 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
push_procedure(c, type);
ast_node(bs, BlockStmt, body);
// TODO(bill): Check declarations first (except mutable variable declarations)
check_stmt_list(c, bs->list, 0);
check_stmt_list(c, bs->list, bs->list_count, 0);
if (type->Proc.result_count > 0) {
if (!check_is_terminating(c, body)) {
if (!check_is_terminating(body)) {
error(&c->error_collector, bs->close, "Missing return statement at the end of the procedure");
}
}
@@ -349,14 +409,15 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) {
Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0);
e->type = proc_type;
ast_node(pd, ProcDecl, d->proc_decl);
check_open_scope(c, pd->type);
defer ({
check_close_scope(c);
});
defer (check_close_scope(c));
check_procedure_type(c, proc_type, pd->type);
// add_proc_entity(c, d->scope, pd->name, e);
add_entity(c, d->scope, pd->name, e);
b32 is_foreign = (pd->tags & ProcTag_foreign) != 0;
b32 is_inline = (pd->tags & ProcTag_inline) != 0;
b32 is_no_inline = (pd->tags & ProcTag_no_inline) != 0;
@@ -481,7 +542,7 @@ void check_var_decl(Checker *c, AstNode *node) {
for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
Entity *entity = NULL;
Token token = name->Ident.token;
Token token = name->Ident;
if (name->kind == AstNode_Ident) {
String str = token.string;
Entity *found = NULL;
@@ -497,6 +558,11 @@ void check_var_decl(Checker *c, AstNode *node) {
}
add_entity_definition(&c->info, name, entity);
} else {
TokenPos pos = found->token.pos;
error(&c->error_collector, token,
"Redeclaration of `%.*s` in this scope\n"
"\tat %.*s(%td:%td)",
LIT(str), LIT(pos.file), pos.line, pos.column);
entity = found;
}
} else {
@@ -543,9 +609,15 @@ void check_var_decl(Checker *c, AstNode *node) {
GB_ASSERT(name->kind == AstNode_Ident);
ExactValue v = {ExactValue_Invalid};
ast_node(i, Ident, name);
Entity *e = make_entity_constant(c->allocator, c->context.scope, i->token, NULL, v);
entities[entity_index++] = e;
check_const_decl(c, e, vd->type, value);
String str = i->string;
Entity *found = current_scope_lookup_entity(c->context.scope, str);
if (found == NULL) {
Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v);
entities[entity_index++] = e;
check_const_decl(c, e, vd->type, value);
} else {
entities[entity_index++] = found;
}
}
isize lhs_count = vd->name_count;
@@ -572,6 +644,7 @@ void check_var_decl(Checker *c, AstNode *node) {
void check_stmt(Checker *c, AstNode *node, u32 flags) {
u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
switch (node->kind) {
case_ast_node(_, EmptyStmt, node); case_end;
case_ast_node(_, BadStmt, node); case_end;
@@ -711,7 +784,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
case_ast_node(bs, BlockStmt, node);
check_open_scope(c, node);
check_stmt_list(c, bs->list, flags);
check_stmt_list(c, bs->list, bs->list_count, mod_flags);
check_close_scope(c);
case_end;
@@ -730,13 +803,13 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
"Non-boolean condition in `if` statement");
}
check_stmt(c, is->body, flags);
check_stmt(c, is->body, mod_flags);
if (is->else_stmt) {
switch (is->else_stmt->kind) {
case AstNode_IfStmt:
case AstNode_BlockStmt:
check_stmt(c, is->else_stmt, flags);
check_stmt(c, is->else_stmt, mod_flags);
break;
default:
error(&c->error_collector, ast_node_token(is->else_stmt),
@@ -772,6 +845,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
case_end;
case_ast_node(fs, ForStmt, node);
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
check_open_scope(c, node);
defer (check_close_scope(c));
@@ -788,9 +862,152 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
}
if (fs->post != NULL)
check_stmt(c, fs->post, 0);
check_stmt(c, fs->body, flags | Statement_BreakAllowed | Statement_ContinueAllowed);
check_stmt(c, fs->body, new_flags);
case_end;
case_ast_node(ms, MatchStmt, node);
Operand x = {};
mod_flags |= Stmt_BreakAllowed;
check_open_scope(c, node);
defer (check_close_scope(c));
if (ms->init != NULL) {
check_stmt(c, ms->init, 0);
}
if (ms->tag != NULL) {
check_expr(c, &x, ms->tag);
check_assignment(c, &x, NULL, make_string("match expression"));
} else {
x.mode = Addressing_Constant;
x.type = t_bool;
x.value = make_exact_value_bool(true);
Token token = {};
token.pos = ast_node_token(ms->body).pos;
token.string = make_string("true");
x.expr = make_ident(c->curr_ast_file, token);
}
// NOTE(bill): Check for multiple defaults
AstNode *first_default = NULL;
ast_node(bs, BlockStmt, ms->body);
for (AstNode *stmt = bs->list; stmt != NULL; stmt = stmt->next) {
AstNode *default_stmt = NULL;
if (stmt->kind == AstNode_CaseClause) {
ast_node(c, CaseClause, stmt);
if (c->list_count == 0) {
default_stmt = stmt;
}
} else {
error(&c->error_collector, ast_node_token(stmt), "Invalid AST - expected case clause");
}
if (default_stmt != NULL) {
if (first_default != NULL) {
TokenPos pos = ast_node_token(first_default).pos;
error(&c->error_collector, ast_node_token(stmt),
"multiple `default` clauses\n"
"\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
} else {
first_default = default_stmt;
}
}
}
struct TypeAndToken {
Type *type;
Token token;
};
Map<TypeAndToken> seen = {}; // Multimap
map_init(&seen, gb_heap_allocator());
defer (map_destroy(&seen));
isize i = 0;
for (AstNode *stmt = bs->list; stmt != NULL; stmt = stmt->next) {
if (stmt->kind != AstNode_CaseClause) {
// NOTE(bill): error handled by above multiple default checker
continue;
}
ast_node(cc, CaseClause, stmt);
for (AstNode *expr = cc->list; expr != NULL; expr = expr->next) {
Operand y = {};
Operand z = {};
Token eq = {Token_CmpEq};
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;
}
z = y;
check_comparison(c, &z, &x, eq);
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);
auto *found = map_get(&seen, key);
if (found != NULL) {
isize count = multi_map_count(&seen, key);
TypeAndToken *taps = gb_alloc_array(gb_heap_allocator(), TypeAndToken, count);
defer (gb_free(gb_heap_allocator(), taps));
multi_map_get_all(&seen, key, taps);
b32 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(&c->error_collector,
ast_node_token(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);
u32 ft_flags = mod_flags;
if (i+1 < bs->list_count) {
ft_flags |= Stmt_FallthroughAllowed;
}
check_stmt_list(c, cc->stmts, cc->stmt_count, ft_flags);
check_close_scope(c);
i++;
}
case_end;
case_ast_node(ds, DeferStmt, node);
if (is_ast_node_decl(ds->stmt)) {
error(&c->error_collector, ds->token, "You cannot defer a declaration");
@@ -806,12 +1023,16 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
Token token = bs->token;
switch (token.kind) {
case Token_break:
if ((flags & Statement_BreakAllowed) == 0)
error(&c->error_collector, token, "`break` only allowed in `for` statement");
if ((flags & Stmt_BreakAllowed) == 0)
error(&c->error_collector, token, "`break` only allowed in `for` or `match` statements");
break;
case Token_continue:
if ((flags & Statement_ContinueAllowed) == 0)
error(&c->error_collector, token, "`continue` only allowed in `for` statement");
if ((flags & Stmt_ContinueAllowed) == 0)
error(&c->error_collector, token, "`continue` only allowed in `for` statements");
break;
case Token_fallthrough:
if ((flags & Stmt_FallthroughAllowed) == 0)
error(&c->error_collector, token, "`fallthrough` statement in illegal position");
break;
default:
error(&c->error_collector, token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
@@ -828,7 +1049,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
b32 is_selector = false;
AstNode *expr = unparen_expr(es->expr);
if (expr->kind == AstNode_Ident) {
String name = expr->Ident.token.string;
String name = expr->Ident.string;
e = scope_lookup_entity(c, c->context.scope, name);
} else if (expr->kind == AstNode_SelectorExpr) {
Operand o = {};
@@ -848,9 +1069,9 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
switch (e->kind) {
case Entity_TypeName: {
Type *t = get_base_type(e->type);
if (t->kind == Type_Enum) {
for (isize i = 0; i < t->Enum.field_count; i++) {
Entity *f = t->Enum.fields[i];
if (is_type_enum(t)) {
for (isize i = 0; i < t->Record.other_field_count; i++) {
Entity *f = t->Record.other_fields[i];
Entity *found = scope_insert_entity(c->context.scope, f);
if (found != NULL) {
error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of the constant: %.*s", expr_str, LIT(found->token.string));
@@ -858,8 +1079,8 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
}
f->using_parent = e;
}
} else if (t->kind == Type_Struct) {
Scope **found = map_get(&c->info.scopes, hash_pointer(t->Struct.node));
} else if (is_type_struct(t)) {
Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node));
if (found != NULL) {
gb_for_array(i, (*found)->elements.entries) {
Entity *f = (*found)->elements.entries[i].value;
@@ -871,9 +1092,9 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
f->using_parent = e;
}
} else {
for (isize i = 0; i < t->Struct.other_field_count; i++) {
for (isize i = 0; i < t->Record.other_field_count; i++) {
// TODO(bill): using field types too
Entity *f = t->Struct.other_fields[i];
Entity *f = t->Record.other_fields[i];
Entity *found = scope_insert_entity(c->context.scope, f);
if (found != NULL) {
error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
@@ -897,10 +1118,8 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
case Entity_Variable:
case Entity_UsingVariable: {
Type *t = get_base_type(type_deref(e->type));
if (t->kind == Type_Struct || t->kind == Type_Union) {
// IMPORTANT HACK(bill): Entity_(Struct|Union) overlap in some memory allowing
// for some variables to accessed to same
Scope **found = map_get(&c->info.scopes, hash_pointer(t->Struct.node));
if (is_type_struct(t) || is_type_raw_union(t)) {
Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node));
GB_ASSERT(found != NULL);
gb_for_array(i, (*found)->elements.entries) {
Entity *f = (*found)->elements.entries[i].value;
@@ -917,7 +1136,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
}
}
} else {
error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or union");
error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or raw_union");
return;
}
} break;
@@ -935,13 +1154,11 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
for (AstNode *item = vd->name_list; item != NULL; item = item->next) {
ast_node(i, Ident, item);
String name = i->token.string;
String name = i->string;
Entity *e = scope_lookup_entity(c, c->context.scope, name);
Type *t = get_base_type(type_deref(e->type));
if (t->kind == Type_Struct || t->kind == Type_Union) {
// IMPORTANT HACK(bill): Entity_(Struct|Union) overlap in some memory allowing
// for some variables to accessed to same
Scope **found = map_get(&c->info.scopes, hash_pointer(t->Struct.node));
if (is_type_struct(t) || is_type_raw_union(t)) {
Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node));
GB_ASSERT(found != NULL);
gb_for_array(i, (*found)->elements.entries) {
Entity *f = (*found)->elements.entries[i].value;
@@ -955,7 +1172,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
}
}
} else {
error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or union");
error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or raw_union");
return;
}
}
@@ -979,8 +1196,8 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
case_ast_node(pd, ProcDecl, node);
ast_node(name, Ident, pd->name);
Entity *e = make_entity_procedure(c->allocator, c->context.scope, name->token, NULL);
add_entity(c, c->context.scope, pd->name, e);
Entity *e = make_entity_procedure(c->allocator, c->context.scope, *name, NULL);
// add_proc_entity(c, c->context.scope, pd->name, e);
DeclInfo decl = {};
init_declaration_info(&decl, e->scope);
@@ -991,7 +1208,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
case_ast_node(td, TypeDecl, node);
ast_node(name, Ident, td->name);
Entity *e = make_entity_type_name(c->allocator, c->context.scope, name->token, NULL);
Entity *e = make_entity_type_name(c->allocator, c->context.scope, *name, NULL);
add_entity(c, c->context.scope, td->name, e);
check_type_decl(c, e, td->type, NULL, NULL);
case_end;
+200 -172
View File
@@ -61,9 +61,7 @@ struct BasicType {
TYPE_KIND(Array), \
TYPE_KIND(Vector), \
TYPE_KIND(Slice), \
TYPE_KIND(Struct), \
TYPE_KIND(Union), \
TYPE_KIND(Enum), \
TYPE_KIND(Record), \
TYPE_KIND(Pointer), \
TYPE_KIND(Named), \
TYPE_KIND(Tuple), \
@@ -87,6 +85,17 @@ enum TypeFlag {
TypeFlag_volatile = GB_BIT(1),
};
enum TypeRecordKind {
TypeRecord_Invalid,
TypeRecord_Struct,
TypeRecord_Enum,
TypeRecord_RawUnion,
TypeRecord_Union, // Tagged
TypeRecord_Count,
};
struct Type {
u32 flags;
TypeKind kind;
@@ -104,26 +113,26 @@ struct Type {
Type *elem;
} Slice;
struct {
TypeRecordKind kind;
// All record types
// Theses are arrays
Entity **fields; // Entity_Variable
isize field_count; // == offset_count
Entity **other_fields; // Entity_Constant or Entity_TypeName
isize other_field_count;
AstNode *node;
i64 * offsets;
b32 are_offsets_set;
b32 is_packed;
} Struct;
struct {
// IMPORTANT HACK(bill): The positions of fields, field_count, and node
// must be same for Struct and Union
Entity **fields; // Entity_Variable
isize field_count;
Entity **other_fields; // Entity_Constant or Entity_TypeName
// enum only
Type * enum_base; // Default is `int`
// struct only
i64 * struct_offsets;
b32 struct_are_offsets_set;
b32 struct_is_packed;
// Entity_Constant or Entity_TypeName
Entity **other_fields;
isize other_field_count;
AstNode *node;
} Union;
} Record;
struct { Type *elem; } Pointer;
struct {
String name;
@@ -141,11 +150,6 @@ struct Type {
isize param_count;
isize result_count;
} Proc;
struct {
Type * base; // Default is `int`
Entity **fields; // Entity_Constant
isize field_count;
} Enum;
};
};
@@ -200,17 +204,20 @@ Type *make_type_slice(gbAllocator a, Type *elem) {
}
Type *make_type_struct(gbAllocator a) {
Type *t = alloc_type(a, Type_Struct);
Type *t = alloc_type(a, Type_Record);
t->Record.kind = TypeRecord_Struct;
return t;
}
Type *make_type_union(gbAllocator a) {
Type *t = alloc_type(a, Type_Union);
Type *make_type_raw_union(gbAllocator a) {
Type *t = alloc_type(a, Type_Record);
t->Record.kind = TypeRecord_RawUnion;
return t;
}
Type *make_type_enum(gbAllocator a) {
Type *t = alloc_type(a, Type_Enum);
Type *t = alloc_type(a, Type_Record);
t->Record.kind = TypeRecord_Enum;
return t;
}
@@ -433,12 +440,21 @@ Type *base_vector_type(Type *t) {
}
b32 is_type_enum(Type *t) {
t = get_base_type(t);
return t->kind == Type_Enum;
return (t->kind == Type_Record && t->Record.kind == TypeRecord_Enum);
}
b32 is_type_struct(Type *t) {
t = get_base_type(t);
return (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct);
}
b32 is_type_raw_union(Type *t) {
t = get_base_type(t);
return (t->kind == Type_Record && t->Record.kind == TypeRecord_RawUnion);
}
Type *get_enum_base_type(Type *t) {
Type *bt = get_base_type(t);
if (is_type_enum(bt)) {
return bt->Enum.base;
return bt->Record.enum_base;
}
return t;
}
@@ -452,10 +468,14 @@ b32 is_type_comparable(Type *t) {
return true;
case Type_Pointer:
return true;
case Type_Struct: {
for (isize i = 0; i < t->Struct.field_count; i++) {
if (!is_type_comparable(t->Struct.fields[i]->type))
return false;
case Type_Record: {
if (is_type_struct(t)) {
for (isize i = 0; i < t->Record.field_count; i++) {
if (!is_type_comparable(t->Record.fields[i]->type))
return false;
}
} else if (is_type_enum(t)) {
return is_type_comparable(t->Record.enum_base);
}
return true;
} break;
@@ -463,8 +483,6 @@ b32 is_type_comparable(Type *t) {
return is_type_comparable(t->Array.elem);
case Type_Vector:
return is_type_comparable(t->Vector.elem);
case Type_Enum:
return is_type_comparable(t->Enum.base);
case Type_Proc:
return true;
}
@@ -501,16 +519,25 @@ b32 are_types_identical(Type *x, Type *y) {
return are_types_identical(x->Slice.elem, y->Slice.elem);
break;
case Type_Struct:
if (y->kind == Type_Struct) {
if (x->Struct.field_count == y->Struct.field_count) {
for (isize i = 0; i < x->Struct.field_count; i++) {
if (!are_types_identical(x->Struct.fields[i]->type, y->Struct.fields[i]->type)) {
return false;
}
}
case Type_Record:
if (y->kind == Type_Record) {
if (x->Record.kind == y->Record.kind) {
switch (x->Record.kind) {
case TypeRecord_Struct:
case TypeRecord_RawUnion:
case TypeRecord_Union:
if (x->Record.field_count == y->Record.field_count) {
for (isize i = 0; i < x->Record.field_count; i++) {
if (!are_types_identical(x->Record.fields[i]->type, y->Record.fields[i]->type)) {
return false;
}
}
return true;
return true;
}
case TypeRecord_Enum:
return are_types_identical(x->Record.enum_base, y->Record.enum_base);
}
}
}
break;
@@ -627,65 +654,55 @@ Selection lookup_field(Type *type_, String field_name, b32 is_type, Selection se
b32 is_ptr = type != type_;
type = get_base_type(type);
switch (type->kind) {
// HACK(bill): struct/union variable overlay from unsafe tagged union
case Type_Struct:
case Type_Union:
if (is_type) {
for (isize i = 0; i < type->Struct.other_field_count; i++) {
Entity *f = type->Struct.other_fields[i];
GB_ASSERT(f->kind != Entity_Variable);
String str = f->token.string;
if (are_strings_equal(field_name, str)) {
selection_add_index(&sel, i);
sel.entity = f;
return sel;
}
}
} else {
for (isize i = 0; i < type->Struct.field_count; i++) {
Entity *f = type->Struct.fields[i];
GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field);
String str = f->token.string;
if (are_strings_equal(field_name, str)) {
selection_add_index(&sel, i);
sel.entity = f;
return sel;
}
if (type->kind != Type_Record) {
return sel;
}
if (is_type) {
for (isize i = 0; i < type->Record.other_field_count; i++) {
Entity *f = type->Record.other_fields[i];
GB_ASSERT(f->kind != Entity_Variable);
String str = f->token.string;
if (f->Variable.anonymous) {
isize prev_count = 0;
if (sel.index != NULL) {
prev_count = gb_array_count(sel.index);
}
selection_add_index(&sel, i); // HACK(bill): Leaky memory
sel = lookup_field(f->type, field_name, is_type, sel);
if (sel.entity != NULL) {
if (is_type_pointer(f->type))
sel.indirect = true;
return sel;
}
gb_array_count(sel.index) = prev_count;
}
}
}
break;
case Type_Enum:
if (is_type) {
for (isize i = 0; i < type->Enum.field_count; i++) {
Entity *f = type->Enum.fields[i];
GB_ASSERT(f->kind == Entity_Constant);
String str = f->token.string;
if (are_strings_equal(field_name, str)) {
// Enums are constant expression
if (are_strings_equal(field_name, str)) {
if (is_type_enum(type)) {
GB_ASSERT(f->kind == Entity_Constant);
// NOTE(bill): Enums are constant expression
return make_selection(f, NULL, i);
} else {
selection_add_index(&sel, i);
sel.entity = f;
return sel;
}
}
}
break;
} else if (!is_type_enum(type)) {
for (isize i = 0; i < type->Record.field_count; i++) {
Entity *f = type->Record.fields[i];
GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field);
String str = f->token.string;
if (are_strings_equal(field_name, str)) {
selection_add_index(&sel, i);
sel.entity = f;
return sel;
}
if (f->Variable.anonymous) {
isize prev_count = 0;
if (sel.index != NULL) {
prev_count = gb_array_count(sel.index);
}
selection_add_index(&sel, i); // HACK(bill): Leaky memory
sel = lookup_field(f->type, field_name, is_type, sel);
if (sel.entity != NULL) {
if (is_type_pointer(f->type))
sel.indirect = true;
return sel;
}
gb_array_count(sel.index) = prev_count;
}
}
}
return sel;
@@ -716,30 +733,32 @@ i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
return gb_clamp(size, s.max_align, 4*s.max_align);
} break;
case Type_Struct: {
if (!t->Struct.is_packed) {
case Type_Record: {
switch (t->Record.kind) {
case TypeRecord_Struct:
if (!t->Record.struct_is_packed) {
i64 max = 1;
for (isize i = 0; i < t->Record.field_count; i++) {
i64 align = type_align_of(s, allocator, t->Record.fields[i]->type);
if (max < align)
max = align;
}
return max;
}
break;
case TypeRecord_RawUnion: {
i64 max = 1;
for (isize i = 0; i < t->Struct.field_count; i++) {
i64 align = type_align_of(s, allocator, t->Struct.fields[i]->type);
for (isize i = 0; i < t->Record.field_count; i++) {
i64 align = type_align_of(s, allocator, t->Record.fields[i]->type);
if (max < align)
max = align;
}
return max;
} break;
case TypeRecord_Enum:
return type_align_of(s, allocator, t->Record.enum_base);
}
} break;
case Type_Union: {
i64 max = 1;
for (isize i = 0; i < t->Union.field_count; i++) {
i64 align = type_align_of(s, allocator, t->Union.fields[i]->type);
if (max < align)
max = align;
}
return max;
} break;
case Type_Enum:
return type_align_of(s, allocator, t->Enum.base);
}
return gb_clamp(next_pow2(type_size_of(s, allocator, t)), 1, s.max_align);
@@ -767,10 +786,10 @@ i64 *type_set_offsets_of(BaseTypeSizes s, gbAllocator allocator, Entity **fields
}
b32 type_set_offsets(BaseTypeSizes s, gbAllocator allocator, Type *t) {
GB_ASSERT(t->kind == Type_Struct);
if (!t->Struct.are_offsets_set) {
t->Struct.offsets = type_set_offsets_of(s, allocator, t->Struct.fields, t->Struct.field_count, t->Struct.is_packed);
t->Struct.are_offsets_set = true;
GB_ASSERT(is_type_struct(t));
if (!t->Record.struct_are_offsets_set) {
t->Record.struct_offsets = type_set_offsets_of(s, allocator, t->Record.fields, t->Record.field_count, t->Record.struct_is_packed);
t->Record.struct_are_offsets_set = true;
return true;
}
return false;
@@ -824,27 +843,32 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
case Type_Slice: // ptr + len + cap
return 3 * s.word_size;
case Type_Struct: {
i64 count = t->Struct.field_count;
if (count == 0)
return 0;
type_set_offsets(s, allocator, t);
return t->Struct.offsets[count-1] + type_size_of(s, allocator, t->Struct.fields[count-1]->type);
} break;
case Type_Record: {
switch (t->Record.kind) {
case TypeRecord_Struct: {
i64 count = t->Record.field_count;
if (count == 0)
return 0;
type_set_offsets(s, allocator, t);
return t->Record.struct_offsets[count-1] + type_size_of(s, allocator, t->Record.fields[count-1]->type);
} break;
case Type_Union: {
i64 count = t->Union.field_count;
i64 max = 0;
for (isize i = 0; i < count; i++) {
i64 size = type_size_of(s, allocator, t->Struct.fields[i]->type);
if (max < size)
max = size;
case TypeRecord_RawUnion: {
i64 count = t->Record.field_count;
i64 max = 0;
for (isize i = 0; i < count; i++) {
i64 size = type_size_of(s, allocator, t->Record.fields[i]->type);
if (max < size)
max = size;
}
return max;
} break;
case TypeRecord_Enum: {
return type_size_of(s, allocator, t->Record.enum_base);
} break;
}
return max;
} break;
case Type_Enum:
return type_size_of(s, allocator, t->Enum.base);
}
// Catch all
@@ -852,10 +876,10 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
}
i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, isize index) {
if (t->kind == Type_Struct) {
if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) {
type_set_offsets(s, allocator, t);
if (gb_is_between(index, 0, t->Struct.field_count-1)) {
return t->Struct.offsets[index];
if (gb_is_between(index, 0, t->Record.field_count-1)) {
return t->Record.struct_offsets[index];
}
}
return 0;
@@ -868,11 +892,11 @@ i64 type_offset_of_from_selection(BaseTypeSizes s, gbAllocator allocator, Type *
for (isize i = 0; i < gb_array_count(sel.index); i++) {
isize index = sel.index[i];
t = get_base_type(t);
if (t->kind == Type_Struct) {
if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) {
type_set_offsets(s, allocator, t);
GB_ASSERT(gb_is_between(index, 0, t->Struct.field_count-1));
offset += t->Struct.offsets[index];
t = t->Struct.fields[index]->type;
GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
offset += t->Record.struct_offsets[index];
t = t->Record.fields[index]->type;
}
}
return offset;
@@ -905,37 +929,41 @@ gbString write_type_to_string(gbString str, Type *type) {
str = write_type_to_string(str, type->Array.elem);
break;
case Type_Struct: {
str = gb_string_appendc(str, "struct{");
for (isize i = 0; i < type->Struct.field_count; i++) {
Entity *f = type->Struct.fields[i];
GB_ASSERT(f->kind == Entity_Variable);
if (i > 0)
str = gb_string_appendc(str, ", ");
str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
str = gb_string_appendc(str, ": ");
str = write_type_to_string(str, f->type);
}
str = gb_string_appendc(str, "}");
} break;
case Type_Record: {
switch (type->Record.kind) {
case TypeRecord_Struct:
str = gb_string_appendc(str, "struct{");
for (isize i = 0; i < type->Record.field_count; i++) {
Entity *f = type->Record.fields[i];
GB_ASSERT(f->kind == Entity_Variable);
if (i > 0)
str = gb_string_appendc(str, ", ");
str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
str = gb_string_appendc(str, ": ");
str = write_type_to_string(str, f->type);
}
str = gb_string_appendc(str, "}");
break;
case Type_Union: {
str = gb_string_appendc(str, "union{");
for (isize i = 0; i < type->Union.field_count; i++) {
Entity *f = type->Union.fields[i];
GB_ASSERT(f->kind == Entity_Variable);
if (i > 0)
str = gb_string_appendc(str, ", ");
str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
str = gb_string_appendc(str, ": ");
str = write_type_to_string(str, f->type);
}
str = gb_string_appendc(str, "}");
} break;
case TypeRecord_RawUnion:
str = gb_string_appendc(str, "raw_union{");
for (isize i = 0; i < type->Record.field_count; i++) {
Entity *f = type->Record.fields[i];
GB_ASSERT(f->kind == Entity_Variable);
if (i > 0)
str = gb_string_appendc(str, ", ");
str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
str = gb_string_appendc(str, ": ");
str = write_type_to_string(str, f->type);
}
str = gb_string_appendc(str, "}");
break;
case Type_Enum: {
str = gb_string_appendc(str, "enum ");
str = write_type_to_string(str, type->Enum.base);
case TypeRecord_Enum:
str = gb_string_appendc(str, "enum ");
str = write_type_to_string(str, type->Record.enum_base);
break;
}
} break;
case Type_Pointer: