From 0eaf7bd830dcda6e00f80eefed36bdf7beb02d5d Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Tue, 30 Aug 2016 00:04:14 +0100 Subject: [PATCH] Begin "Everything's a namespace" --- examples/basic.odin | 2 +- examples/demo.odin | 34 +++- examples/file.odin | 26 +-- examples/runtime.odin | 2 +- examples/win32.odin | 2 +- src/checker/checker.cpp | 51 ++++-- src/checker/entity.cpp | 1 + src/checker/expr.cpp | 337 +++++++++++++++++++++++++------------ src/checker/stmt.cpp | 69 ++++++++ src/checker/type.cpp | 3 + src/codegen/codegen.cpp | 8 +- src/codegen/print_llvm.cpp | 25 ++- src/codegen/ssa.cpp | 42 ++++- src/parser.cpp | 179 +++++++++++++++++--- src/printer.cpp | 2 +- 15 files changed, 579 insertions(+), 204 deletions(-) diff --git a/examples/basic.odin b/examples/basic.odin index 42d7404c2..c9a54d15a 100644 --- a/examples/basic.odin +++ b/examples/basic.odin @@ -3,7 +3,7 @@ #load "file.odin" print_string :: proc(s: string) { - file_write(file_get_standard(FileStandard.OUTPUT), ^s[0], len(s)); + file_write(file_get_standard(FileStandard.OUTPUT), s as []byte); } byte_reverse :: proc(b: []byte) { diff --git a/examples/demo.odin b/examples/demo.odin index 698e570cd..07fa7b1d5 100644 --- a/examples/demo.odin +++ b/examples/demo.odin @@ -32,6 +32,30 @@ main :: proc() { d := ptr_sub(y, ptr_offset(x, 1)); print_int(d); nl(); + Thing :: type struct { + CONSTANT :: 123; + Thing :: type struct { + y: f32; + + z: int; + w: int; + } + + x: Thing; + } + + test :: proc() -> int { + t_outer: Thing; + t_outer.x = Thing.Thing{}; + using Thing; + t_inner: Thing; + t_inner.y = 1; + print_int(CONSTANT); nl(); + return CONSTANT; + } + + test__ := test(); + // run_game(); @@ -301,7 +325,7 @@ types :: proc() { Array3Int :: type [3]int; Vec3 :: type struct { - x, y, z: f32 + x, y, z: f32; } BinaryNode :: type struct { @@ -430,8 +454,8 @@ types :: proc() { variable: struct{ visited, is_field, used, anonymous: bool; }; - procedure: struct { used: bool }; - buitlin: struct { id: i32 }; + procedure: struct { used: bool; }; + buitlin: struct { id: i32; }; }; } @@ -632,7 +656,7 @@ data_control :: proc() { context.allocator = __default_allocator(); defer context.allocator = prev_allocator; - File :: type struct { filename: string }; + File :: type struct { filename: string; }; FileError :: type int; open_file :: proc(filename: string) -> (File, FileError) { return File{}, 0; @@ -717,7 +741,7 @@ using_fields :: proc() { { // Crazy Shit Vec2 :: type union { - using _xy: struct {x, y: f32}; + using _xy: struct { x, y: f32; }; e: [2]f32; v: {2}f32; } diff --git a/examples/file.odin b/examples/file.odin index 3e827b3dd..a4011520a 100644 --- a/examples/file.odin +++ b/examples/file.odin @@ -1,18 +1,16 @@ #load "win32.odin" -FileHandle :: type HANDLE; - File :: type struct { - handle: FileHandle; + Handle :: type HANDLE; + handle: Handle; } file_open :: proc(name: string) -> (File, bool) { buf: [300]byte; _ = copy(buf[:], name as []byte); - f := File{ - handle = CreateFileA(^buf[0], FILE_GENERIC_READ, FILE_SHARE_READ, null, OPEN_EXISTING, 0, null), - }; - success := f.handle != INVALID_HANDLE_VALUE as FileHandle; + f := File{CreateFileA(^buf[0], FILE_GENERIC_READ, FILE_SHARE_READ, null, OPEN_EXISTING, 0, null)}; + success := f.handle != INVALID_HANDLE_VALUE as File.Handle; + return f, success; } @@ -22,7 +20,7 @@ file_create :: proc(name: string) -> (File, bool) { f := File{ handle = CreateFileA(^buf[0], FILE_GENERIC_WRITE, FILE_SHARE_READ, null, CREATE_ALWAYS, 0, null), }; - success := f.handle != INVALID_HANDLE_VALUE as FileHandle; + success := f.handle != INVALID_HANDLE_VALUE as File.Handle; return f, success; } @@ -31,9 +29,9 @@ file_close :: proc(f: ^File) { CloseHandle(f.handle); } -file_write :: proc(f: ^File, buf: rawptr, len: int) -> bool { +file_write :: proc(f: ^File, buf: []byte) -> bool { bytes_written: i32; - return WriteFile(f.handle, buf, len as i32, ^bytes_written, null) != 0; + return WriteFile(f.handle, ^buf[0], len(buf) as i32, ^bytes_written, null) != 0; } FileStandard :: type enum { @@ -47,10 +45,12 @@ __std_file_set := false; __std_files: [FileStandard.COUNT as int]File; file_get_standard :: proc(std: FileStandard) -> ^File { + // using FileStandard; if (!__std_file_set) { - __std_files[FileStandard.INPUT] .handle = GetStdHandle(STD_INPUT_HANDLE); - __std_files[FileStandard.OUTPUT].handle = GetStdHandle(STD_OUTPUT_HANDLE); - __std_files[FileStandard.ERROR] .handle = GetStdHandle(STD_ERROR_HANDLE); + using FileStandard; + __std_files[INPUT] .handle = GetStdHandle(STD_INPUT_HANDLE); + __std_files[OUTPUT].handle = GetStdHandle(STD_OUTPUT_HANDLE); + __std_files[ERROR] .handle = GetStdHandle(STD_ERROR_HANDLE); __std_file_set = true; } return ^__std_files[std]; diff --git a/examples/runtime.odin b/examples/runtime.odin index d6a6e354f..5f39bc306 100644 --- a/examples/runtime.odin +++ b/examples/runtime.odin @@ -98,7 +98,7 @@ memory_copy :: proc(dst, src: rawptr, n: int) #inline { w = (s as ^u32)^; d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1); d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1); - n -= 2 + n -= 2; for n > 17 { d32 := d as ^u32; diff --git a/examples/win32.odin b/examples/win32.odin index b262cbca3..4807cf2a2 100644 --- a/examples/win32.odin +++ b/examples/win32.odin @@ -33,7 +33,7 @@ LPARAM :: type int; LRESULT :: type int; ATOM :: type i16; BOOL :: type i32; -POINT :: type struct { x, y: i32 }; +POINT :: type struct { x, y: i32; }; INVALID_HANDLE_VALUE :: (-1 as int) as HANDLE; diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index 5bb2e53bb..717c70b57 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -2,19 +2,29 @@ #include "entity.cpp" #include "type.cpp" +#define ADDRESSING_KINDS \ + ADDRESSING_MODE(Invalid), \ + ADDRESSING_MODE(NoValue), \ + ADDRESSING_MODE(Value), \ + ADDRESSING_MODE(Variable), \ + ADDRESSING_MODE(Constant), \ + ADDRESSING_MODE(Type), \ + ADDRESSING_MODE(Builtin), \ + ADDRESSING_MODE(Count), \ + enum AddressingMode { - Addressing_Invalid, - - Addressing_NoValue, - Addressing_Value, - Addressing_Variable, - Addressing_Constant, - Addressing_Type, - Addressing_Builtin, - - Addressing_Count, +#define ADDRESSING_MODE(x) GB_JOIN2(Addressing_, x) + ADDRESSING_KINDS +#undef ADDRESSING_MODE }; +String const addressing_mode_strings[] = { +#define ADDRESSING_MODE(x) {cast(u8 *)#x, gb_size_of(#x)-1} + ADDRESSING_KINDS +#undef ADDRESSING_MODE +}; + + struct Operand { AddressingMode mode; Type *type; @@ -261,7 +271,7 @@ void add_scope(Checker *c, AstNode *node, Scope *scope) { void check_open_scope(Checker *c, AstNode *stmt) { - GB_ASSERT(is_ast_node_stmt(stmt) || stmt->kind == AstNode_ProcType); + GB_ASSERT(is_ast_node_stmt(stmt) || is_ast_node_type(stmt)); Scope *scope = make_scope(c->context.scope, c->allocator); add_scope(c, stmt, scope); if (stmt->kind == AstNode_ProcType) { @@ -524,11 +534,20 @@ void add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) { if (!are_strings_equal(entity->token.string, make_string("_"))) { Entity *insert_entity = scope_insert_entity(scope, entity); if (insert_entity) { - error(&c->error_collector, entity->token, - "Redeclararation of `%.*s` in this scope\n" - "\tat %.*s(%td:%td)", - LIT(entity->token.string), - LIT(entity->token.pos.file), entity->token.pos.line, entity->token.pos.column); + Entity *up = insert_entity->using_parent; + if (up != NULL) { + error(&c->error_collector, entity->token, + "Redeclararation of `%.*s` in this scope through `using`\n" + "\tat %.*s(%td:%td)", + LIT(entity->token.string), + LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column); + } else { + error(&c->error_collector, entity->token, + "Redeclararation of `%.*s` in this scope\n" + "\tat %.*s(%td:%td)", + LIT(entity->token.string), + LIT(entity->token.pos.file), entity->token.pos.line, entity->token.pos.column); + } return; } } diff --git a/src/checker/entity.cpp b/src/checker/entity.cpp index 339eda248..de4095766 100644 --- a/src/checker/entity.cpp +++ b/src/checker/entity.cpp @@ -34,6 +34,7 @@ struct Entity { Scope *scope; Token token; Type *type; + Entity *using_parent; union { struct { ExactValue value; } Constant; diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp index 2319131bc..c252871a8 100644 --- a/src/checker/expr.cpp +++ b/src/checker/expr.cpp @@ -3,6 +3,7 @@ void check_multi_expr (Checker *c, Operand *operand, AstNode *expre void check_expr_or_type (Checker *c, Operand *operand, AstNode *expression); ExprKind check_expr_base (Checker *c, Operand *operand, AstNode *expression, Type *type_hint = NULL); Type * check_type (Checker *c, AstNode *expression, Type *named_type = NULL, CycleChecker *cycle_checker = NULL); +void check_type_decl (Checker *c, Entity *e, AstNode *type_expr, Type *def, CycleChecker *cycle_checker); void check_selector (Checker *c, Operand *operand, AstNode *node); void check_not_tuple (Checker *c, Operand *operand); b32 check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value); @@ -202,24 +203,166 @@ void check_fields(Checker *c, AstNode *node, } } +void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr); + 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); ast_node(st, StructType, node); isize field_count = 0; - for (AstNode *field = st->field_list; field != NULL; field = field->next) { - for (AstNode *name = field->Field.name_list; name != NULL; name = name->next) { - GB_ASSERT(name->kind == AstNode_Ident); - field_count++; + isize other_field_count = 0; + for (AstNode *decl = st->decl_list; decl != NULL; decl = decl->next) { + switch (decl->kind) { + case_ast_node(vd, VarDecl, decl); + if (vd->kind == Declaration_Mutable) { + field_count += vd->name_count; + } else { + other_field_count += vd->name_count; + } + case_end; + + case_ast_node(td, TypeDecl, decl); + other_field_count += 1; + case_end; } } - Entity **fields = gb_alloc_array(c->allocator, Entity *, st->field_count); - check_fields(c, node, st->field_list, fields, field_count, cycle_checker, make_string("structure")); - struct_type->Struct.fields = fields; - struct_type->Struct.field_count = field_count; - struct_type->Struct.is_packed = st->is_packed; + Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); + + Map entity_map = {}; + map_init(&entity_map, gb_heap_allocator()); + defer (map_destroy(&entity_map)); + + Entity **other_fields = gb_alloc_array(c->allocator, Entity *, other_field_count); + isize other_field_index = 0; + // TODO(bill): Random declarations with DeclInfo +#if 0 + Entity *e; + DeclInfo *d;d + check_entity_decl(c, e, d, NULL); +#endif + for (AstNode *decl = st->decl_list; decl != NULL; decl = decl->next) { + if (decl->kind == AstNode_VarDecl) { + ast_node(vd, VarDecl, decl); + if (vd->kind != Declaration_Immutable) + continue; + + isize entity_count = vd->name_count; + isize entity_index = 0; + Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count); + + for (AstNode *name = vd->name_list, *value = vd->value_list; + name != NULL && value != NULL; + 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; + 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); + } + + isize lhs_count = vd->name_count; + isize rhs_count = vd->value_count; + + // TODO(bill): Better error messages or is this good enough? + if (rhs_count == 0 && vd->type == NULL) { + error(&c->error_collector, ast_node_token(node), "Missing type or initial expression"); + } else if (lhs_count < rhs_count) { + error(&c->error_collector, ast_node_token(node), "Extra initial expression"); + } + + 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; + HashKey key = hash_string(name_token.string); + if (map_get(&entity_map, key) != NULL) { + // TODO(bill): Scope checking already checks the declaration + error(&c->error_collector, name_token, "`%.*s` is already declared in this structure", LIT(name_token.string)); + } else { + map_set(&entity_map, key, e); + other_fields[other_field_index++] = e; + } + add_entity(c, c->context.scope, name, e); + } + } else if (decl->kind == AstNode_TypeDecl) { + ast_node(td, TypeDecl, decl); + ast_node(name, Ident, td->name); + Token name_token = name->token; + + 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); + + HashKey key = hash_string(name_token.string); + if (map_get(&entity_map, key) != NULL) { + // TODO(bill): Scope checking already checks the declaration + error(&c->error_collector, name_token, "`%.*s` is already declared in this structure", LIT(name_token.string)); + } else { + map_set(&entity_map, key, e); + other_fields[other_field_index++] = e; + } + add_entity_use(&c->info, td->name, e); + } + + } + + isize field_index = 0; + for (AstNode *decl = st->decl_list; decl != NULL; decl = decl->next) { + if (decl->kind != AstNode_VarDecl) + continue; + ast_node(vd, VarDecl, decl); + if (vd->kind != Declaration_Mutable) + continue; + Type *type = check_type(c, vd->type, NULL, cycle_checker); + + if (vd->is_using) { + if (vd->name_count > 1) { + error(&c->error_collector, ast_node_token(vd->name_list), + "Cannot apply `using` to more than one of the same type"); + } + } + + for (AstNode *name = vd->name_list; name != NULL; name = name->next) { + ast_node(i, Ident, name); + Token name_token = i->token; + + Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, vd->is_using); + HashKey key = hash_string(name_token.string); + if (map_get(&entity_map, key) != NULL) { + // TODO(bill): Scope checking already checks the declaration + error(&c->error_collector, name_token, "`%.*s` is already declared in this structure", LIT(name_token.string)); + } else { + map_set(&entity_map, key, e); + fields[field_index++] = e; + } + add_entity_use(&c->info, name, e); + } + + + 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; + error(&c->error_collector, name_token, "`using` on a field `%.*s` must be a structure or union", LIT(name_token.string)); + continue; + } + + populate_using_entity_map(c, node, type, &entity_map); + } + } + + + + 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; } void check_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker *cycle_checker) { @@ -235,7 +378,7 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker } } - Entity **fields = gb_alloc_array(c->allocator, Entity *, ut->field_count); + Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count); check_fields(c, node, ut->field_list, fields, field_count, cycle_checker, make_string("union")); union_type->Union.fields = fields; union_type->Union.field_count = field_count; @@ -533,7 +676,13 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c case_ast_node(se, SelectorExpr, e); Operand o = {}; + o.mode = Addressing_Type; + o.type = check_type(c, se->expr, named_type, cycle_checker); + // gb_printf_err("mode: %.*s\n", LIT(addressing_mode_strings[o.mode])); check_selector(c, &o, e); + // gb_printf_err("%s.%s\n", expr_to_string(se->expr), expr_to_string(se->selector)); + // gb_printf_err("%s\n", type_to_string(o.type)); + // gb_printf_err("mode: %.*s\n", LIT(addressing_mode_strings[o.mode])); if (o.mode == Addressing_Type) { set_base_type(type, o.type); @@ -575,14 +724,18 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c case_ast_node(st, StructType, e); type = make_type_struct(c->allocator); set_base_type(named_type, type); + check_open_scope(c, e); check_struct_type(c, type, e, cycle_checker); + check_close_scope(c); goto end; case_end; case_ast_node(st, UnionType, e); type = make_type_union(c->allocator); set_base_type(named_type, type); + check_open_scope(c, e); check_union_type(c, type, e, cycle_checker); + check_close_scope(c); goto end; case_end; @@ -1493,7 +1646,7 @@ void selection_add_index(Selection *s, isize index) { gb_array_append(s->index, index); } -Selection lookup_field(Type *type_, String field_name, Selection sel = empty_selection) { +Selection lookup_field(Type *type_, String field_name, AddressingMode mode, Selection sel = empty_selection) { GB_ASSERT(type_ != NULL); if (are_strings_equal(field_name, make_string("_"))) { @@ -1506,32 +1659,45 @@ Selection lookup_field(Type *type_, String field_name, Selection sel = empty_sel switch (type->kind) { case Type_Struct: - 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 (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, sel); - - if (sel.entity != NULL) { - // gb_printf("%.*s, %.*s, %.*s\n", LIT(field_name), LIT(str), LIT(sel.entity->token.string)); - if (is_type_pointer(f->type)) - sel.indirect = true; + if (mode == Addressing_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; } - gb_array_count(sel.index) = prev_count; + } + } 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 (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, mode, sel); + + if (sel.entity != NULL) { + // gb_printf("%.*s, %.*s, %.*s\n", LIT(field_name), LIT(str), LIT(sel.entity->token.string)); + if (is_type_pointer(f->type)) + sel.indirect = true; + return sel; + } + gb_array_count(sel.index) = prev_count; + } } } break; @@ -1553,7 +1719,7 @@ Selection lookup_field(Type *type_, String field_name, Selection sel = empty_sel String str = f->token.string; if (f->Variable.anonymous) { selection_add_index(&sel, i); // HACK(bill): Leaky memory - sel = lookup_field(f->type, field_name, sel); + sel = lookup_field(f->type, field_name, mode, sel); if (sel.entity != NULL && is_type_pointer(f->type)) { sel.indirect = true; } @@ -1563,67 +1729,21 @@ Selection lookup_field(Type *type_, String field_name, Selection sel = empty_sel break; case Type_Enum: - 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 - return make_selection(f, NULL, i); + if (mode == Addressing_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 + return make_selection(f, NULL, i); + } } } break; } return sel; - -/* - switch (type->kind) { - case Type_Struct: - 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)) { - make_selection() - FoundField ff = {f, i, offset+field_offset}; - return ff; - } - if (f->Variable.anonymous) { - return lookup_field(f->type, field_node, offset+field_offset); - } - } - break; - - case Type_Union: - for (isize i = 0; i < type->Union.field_count; i++) { - Entity *f = type->Union.fields[i]; - GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field); - String str = f->token.string; - if (are_strings_equal(field_name, str)) { - if (index) *index = i; - return f; - } - } - break; - - case Type_Enum: - 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)) { - if (index) *index = i; - return f; - } - } - break; - // TODO(bill): Other types and extra "hidden" fields (e.g. introspection stuff) - // TODO(bill): Allow for access of field through index? e.g. `x.3` will get member of index 3 - // Or is this only suitable if tuples are first-class? - } - -*/ } void check_selector(Checker *c, Operand *operand, AstNode *node) { @@ -1633,34 +1753,29 @@ void check_selector(Checker *c, Operand *operand, AstNode *node) { AstNode *op_expr = se->expr; AstNode *selector = se->selector; if (selector) { - Entity *entity = NULL; - if (is_type_enum(operand->type)) { - if (operand->mode == Addressing_Type) { - entity = lookup_field(operand->type, selector->Ident.token.string).entity; - } - } else { - entity = lookup_field(operand->type, selector->Ident.token.string).entity; - } + Entity *entity = lookup_field(operand->type, selector->Ident.token.string, operand->mode).entity; if (entity == NULL) { - gbString op_str = expr_to_string(op_expr); - gbString sel_str = expr_to_string(selector); + gbString op_str = expr_to_string(op_expr); + gbString type_str = type_to_string(operand->type); + gbString sel_str = expr_to_string(selector); defer (gb_string_free(op_str)); + defer (gb_string_free(type_str)); defer (gb_string_free(sel_str)); - error(&c->error_collector, ast_node_token(op_expr), "`%s` has no field `%s`", op_str, sel_str); + error(&c->error_collector, ast_node_token(op_expr), "`%s` (`%s`) has no field `%s`", op_str, type_str, sel_str); operand->mode = Addressing_Invalid; operand->expr = node; return; } add_entity_use(&c->info, selector, entity); - if (is_type_enum(operand->type)) { - operand->type = entity->type; - operand->expr = node; + operand->type = entity->type; + operand->expr = node; + if (entity->kind == Entity_Constant) { operand->mode = Addressing_Constant; operand->value = entity->Constant.value; + } else if (entity->kind == Entity_TypeName) { + operand->mode = Addressing_Type; } else { - operand->type = entity->type; - operand->expr = node; if (operand->mode != Addressing_Variable) operand->mode = Addressing_Value; } @@ -1841,7 +1956,7 @@ 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); + Selection sel = lookup_field(type, arg->token.string, operand->mode); if (sel.entity == NULL) { gbString type_str = type_to_string(type); error(&c->error_collector, ast_node_token(ce->arg_list), @@ -1878,7 +1993,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) ast_node(i, Ident, s->selector); - Selection sel = lookup_field(type, i->token.string); + Selection sel = lookup_field(type, i->token.string, operand->mode); if (sel.entity == NULL) { gbString type_str = type_to_string(type); error(&c->error_collector, ast_node_token(arg), @@ -2504,7 +2619,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint } String name = kv->field->Ident.token.string; - Selection sel = lookup_field(type, kv->field->Ident.token.string); + Selection sel = lookup_field(type, kv->field->Ident.token.string, o->mode); if (sel.entity == NULL) { error(&c->error_collector, ast_node_token(elem), "Unknown field `%.*s` in structure literal", LIT(name)); @@ -3121,7 +3236,7 @@ gbString write_expr_to_string(gbString str, AstNode *node) { case_ast_node(st, StructType, node); str = gb_string_appendc(str, "struct{"); - str = write_field_list_to_string(str, st->field_list, ", "); + // str = write_field_list_to_string(str, st->decl_list, ", "); str = gb_string_appendc(str, "}"); case_end; diff --git a/src/checker/stmt.cpp b/src/checker/stmt.cpp index 9e54a73e3..a0d1838a8 100644 --- a/src/checker/stmt.cpp +++ b/src/checker/stmt.cpp @@ -685,6 +685,75 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { } case_end; + case_ast_node(us, UsingStmt, node); + switch (us->node->kind) { + case_ast_node(es, ExprStmt, us->node); + AstNode *ident = es->expr; + GB_ASSERT(ident->kind == AstNode_Ident); + String name = ident->Ident.token.string; + + Entity *e = scope_lookup_entity(c, c->context.scope, name); + if (e == NULL) { + error(&c->error_collector, us->token, "`using` applied to an unknown entity"); + return; + } + + 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]; + 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", LIT(name), LIT(found->token.string)); + return; + } + f->using_parent = e; + } + } else if (t->kind == Type_Struct) { + for (isize i = 0; i < t->Struct.other_field_count; i++) { + Entity *f = t->Struct.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", LIT(name), LIT(found->token.string)); + return; + } + f->using_parent = e; + } + } + } break; + + case Entity_Constant: + error(&c->error_collector, us->token, "`using` cannot be applied to a constant"); + break; + + case Entity_Procedure: + case Entity_Builtin: + error(&c->error_collector, us->token, "`using` cannot be applied to a procedure"); + break; + + default: + GB_PANIC("TODO(bill): using Ident"); + } + case_end; + + case_ast_node(vd, VarDecl, us->node); + GB_PANIC("TODO(bill): using VarDecl"); + case_end; + + + default: + error(&c->error_collector, us->token, "Invalid AST: Using Statement"); + break; + } + case_end; + + + + + + case_ast_node(vd, VarDecl, node); isize entity_count = vd->name_count; isize entity_index = 0; diff --git a/src/checker/type.cpp b/src/checker/type.cpp index 26bb2dc40..bd1ff223e 100644 --- a/src/checker/type.cpp +++ b/src/checker/type.cpp @@ -110,6 +110,9 @@ struct Type { i64 * offsets; b32 are_offsets_set; b32 is_packed; + + Entity **other_fields; // Entity_Constant or Entity_TypeName + isize other_field_count; } Struct; struct { // IMPORTANT HACK(bill): The positions of fields and field_count diff --git a/src/codegen/codegen.cpp b/src/codegen/codegen.cpp index 795d43a07..88260be8c 100644 --- a/src/codegen/codegen.cpp +++ b/src/codegen/codegen.cpp @@ -70,11 +70,9 @@ void ssa_gen_code(ssaGen *s) { String name = e->token.string; switch (e->kind) { - case Entity_TypeName: { - ssaValue *t = ssa_make_value_type_name(a, e->token.string, e->type); - map_set(&m->values, hash_pointer(e), t); - map_set(&m->members, hash_string(name), t); - } break; + case Entity_TypeName: + ssa_gen_global_type_name(m, e, name); + break; case Entity_Variable: { ssaValue *g = ssa_make_value_global(a, e, NULL); diff --git a/src/codegen/print_llvm.cpp b/src/codegen/print_llvm.cpp index c5571712c..689b19ac8 100644 --- a/src/codegen/print_llvm.cpp +++ b/src/codegen/print_llvm.cpp @@ -169,7 +169,11 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) { ssa_fprintf(f, "*"); break; case Type_Named: - ssa_print_encoded_local(f, t->Named.name); + if (get_base_type(t)->kind == Type_Struct) { + ssa_print_encoded_local(f, t->Named.name); + } else { + ssa_print_type(f, s, get_base_type(t)); + } break; case Type_Tuple: if (t->Tuple.variable_count == 1) { @@ -748,13 +752,15 @@ void ssa_print_proc(gbFile *f, ssaModule *m, ssaProcedure *proc) { void ssa_print_type_name(gbFile *f, ssaModule *m, ssaValue *v) { GB_ASSERT(v->kind == ssaValue_TypeName); + Type *base_type = get_base_type(ssa_type(v)); + if (base_type->kind != Type_Struct) + return; ssa_print_encoded_local(f, v->TypeName.name); ssa_fprintf(f, " = type "); ssa_print_type(f, m->sizes, get_base_type(v->TypeName.type)); ssa_fprintf(f, "\n"); } - void ssa_print_llvm_ir(gbFile *f, ssaModule *m) { if (m->layout.len > 0) { ssa_fprintf(f, "target datalayout = \"%.*s\"\n", LIT(m->layout)); @@ -772,21 +778,12 @@ void ssa_print_llvm_ir(gbFile *f, ssaModule *m) { auto *entry = &m->members.entries[member_index]; ssaValue *v = entry->value; switch (v->kind) { - case ssaValue_TypeName: { - ssa_print_encoded_local(f, v->TypeName.name); - ssa_fprintf(f, " = type "); - ssa_print_type(f, m->sizes, get_base_type(v->TypeName.type)); - ssa_fprintf(f, "\n"); - } break; + case ssaValue_TypeName: + ssa_print_type_name(f, m, v); + break; } } - gb_for_array(i, m->nested_type_names) { - ssaValue *v = m->nested_type_names[i]; - ssa_print_type_name(f, m, v); - } - - gb_for_array(member_index, m->members.entries) { auto *entry = &m->members.entries[member_index]; ssaValue *v = entry->value; diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index d52aff90d..bf2470a28 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -14,7 +14,6 @@ struct ssaModule { Map values; // Key: Entity * Map members; // Key: String - gbArray(ssaValue *) nested_type_names; // ssaValue_TypeName i32 global_string_index; }; @@ -308,13 +307,11 @@ void ssa_module_init(ssaModule *m, Checker *c) { map_init(&m->values, m->allocator); map_init(&m->members, m->allocator); - gb_array_init(m->nested_type_names, m->allocator); } void ssa_module_destroy(ssaModule *m) { map_destroy(&m->values); map_destroy(&m->members); - gb_array_free(m->nested_type_names); gb_arena_free(&m->arena); } @@ -1606,7 +1603,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue if (elem->kind == AstNode_FieldValue) { ast_node(kv, FieldValue, elem); - Selection sel = lookup_field(base_type, kv->field->Ident.token.string); + Selection sel = lookup_field(base_type, kv->field->Ident.token.string, Addressing_Value); field_index = sel.index[0]; field_expr = ssa_build_expr(proc, kv->value); } else { @@ -2063,7 +2060,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) { case_ast_node(se, SelectorExpr, expr); Type *type = get_base_type(type_of_expr(proc->module->info, se->expr)); - Selection sel = lookup_field(type, unparen_expr(se->selector)->Ident.token.string); + Selection sel = lookup_field(type, unparen_expr(se->selector)->Ident.token.string, Addressing_Value); GB_ASSERT(sel.entity != NULL); ssaValue *e = ssa_build_addr(proc, se->expr).addr; @@ -2248,6 +2245,36 @@ void ssa_build_cond(ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssa } +void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name) { + ssaValue *t = ssa_make_value_type_name(m->allocator, name, e->type); + map_set(&m->values, hash_pointer(e), t); + map_set(&m->members, hash_string(name), t); + + Type *bt = get_base_type(e->type); + if (bt->kind == Type_Struct) { + auto *s = &bt->Struct; + for (isize j = 0; j < s->other_field_count; j++) { + Entity *field = s->other_fields[j]; + if (field->kind == Entity_TypeName) { + // HACK(bill): Override name of type so printer prints it correctly + auto *tn = &field->type->Named; + String cn = field->token.string; + isize len = name.len + 1 + cn.len; + String child = {NULL, len}; + child.text = gb_alloc_array(m->allocator, u8, len); + isize i = 0; + gb_memcopy(child.text+i, name.text, name.len); + i += name.len; + child.text[i++] = '.'; + gb_memcopy(child.text+i, cn.text, cn.len); + tn->name = child; + ssa_gen_global_type_name(m, field, tn->name); + } + } + } +} + + void ssa_build_stmt_list(ssaProcedure *proc, AstNode *list) { for (AstNode *stmt = list ; stmt != NULL; stmt = stmt->next) @@ -2389,7 +2416,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { String td_name = td->name->Ident.token.string; isize name_len = proc->name.len + 1 + td_name.len + 1 + 10 + 1; u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len); - i32 guid = cast(i32)gb_array_count(proc->module->nested_type_names); + i32 guid = cast(i32)gb_array_count(proc->module->members.entries); name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(td_name), guid); String name = make_string(name_text, name_len-1); @@ -2400,8 +2427,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { name, e->type); // HACK(bill): Override name of type so printer prints it correctly e->type->Named.name = name; - ssa_module_add_value(proc->module, e, value); - gb_array_append(proc->module->nested_type_names, value); + ssa_gen_global_type_name(proc->module, e, name); case_end; case_ast_node(ids, IncDecStmt, node); diff --git a/src/parser.cpp b/src/parser.cpp index a79cae37e..e3be11940 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -169,6 +169,7 @@ AST_NODE_KIND(_ComplexStmtBegin, struct{}) \ }) \ AST_NODE_KIND(DeferStmt, struct { Token token; AstNode *stmt; }) \ AST_NODE_KIND(BranchStmt, struct { Token token; }) \ + AST_NODE_KIND(UsingStmt, struct { Token token; AstNode *node; }) \ \ AST_NODE_KIND(_ComplexStmtEnd, struct{}) \ AST_NODE_KIND(_StmtEnd, struct{}) \ @@ -177,6 +178,7 @@ AST_NODE_KIND(_DeclBegin, struct{}) \ AST_NODE_KIND(VarDecl, struct { \ DeclKind kind; \ u32 tags; \ + b32 is_using; \ AstNode *name_list; \ AstNode *type; \ AstNode *value_list; \ @@ -222,8 +224,8 @@ AST_NODE_KIND(_TypeBegin, struct{}) \ }) \ AST_NODE_KIND(StructType, struct { \ Token token; \ - AstNode *field_list; \ - isize field_count; \ + AstNode *decl_list; \ + isize decl_count; \ b32 is_packed; \ }) \ AST_NODE_KIND(UnionType, struct { \ @@ -354,6 +356,8 @@ Token ast_node_token(AstNode *node) { return node->DeferStmt.token; case AstNode_BranchStmt: return node->BranchStmt.token; + case AstNode_UsingStmt: + return node->UsingStmt.token; case AstNode_BadDecl: return node->BadDecl.begin; case AstNode_VarDecl: @@ -707,6 +711,13 @@ gb_inline AstNode *make_branch_stmt(AstFile *f, Token token) { return result; } +gb_inline AstNode *make_using_stmt(AstFile *f, Token token, AstNode *node) { + AstNode *result = make_node(f, AstNode_UsingStmt); + result->UsingStmt.token = token; + result->UsingStmt.node = node; + return result; +} + gb_inline AstNode *make_bad_decl(AstFile *f, Token begin, Token end) { AstNode *result = make_node(f, AstNode_BadDecl); @@ -778,11 +789,11 @@ gb_inline AstNode *make_vector_type(AstFile *f, Token token, AstNode *count, Ast return result; } -gb_inline AstNode *make_struct_type(AstFile *f, Token token, AstNode *field_list, isize field_count, b32 is_packed) { +gb_inline AstNode *make_struct_type(AstFile *f, Token token, AstNode *decl_list, isize decl_count, b32 is_packed) { AstNode *result = make_node(f, AstNode_StructType); result->StructType.token = token; - result->StructType.field_list = field_list; - result->StructType.field_count = field_count; + result->StructType.decl_list = decl_list; + result->StructType.decl_count = decl_count; result->StructType.is_packed = is_packed; return result; } @@ -938,6 +949,32 @@ void fix_advance_to_next_stmt(AstFile *f) { #endif } +b32 expect_semicolon_after_stmt(AstFile *f, AstNode *s) { + if (s != NULL) { + switch (s->kind) { + case AstNode_ProcDecl: + return true; + case AstNode_TypeDecl: { + switch (s->TypeDecl.type->kind) { + case AstNode_StructType: + case AstNode_UnionType: + case AstNode_EnumType: + case AstNode_ProcType: + return true; + } + } break; + } + } + + if (!allow_token(f, Token_Semicolon)) { + // CLEANUP(bill): Semicolon handling in parser + ast_file_err(f, f->cursor[0], + "Expected `;` after %.*s, got `%.*s`", + LIT(ast_node_strings[s->kind]), LIT(token_strings[f->cursor[0].kind])); + return false; + } + return true; +} AstNode *parse_expr(AstFile *f, b32 lhs); @@ -1211,6 +1248,7 @@ b32 is_literal_type(AstNode *node) { switch (node->kind) { case AstNode_BadExpr: case AstNode_Ident: + case AstNode_SelectorExpr: case AstNode_ArrayType: case AstNode_VectorType: case AstNode_StructType: @@ -1652,10 +1690,19 @@ AstNode *parse_parameter_list(AstFile *f, AstScope *scope, isize *param_count_, return param_list; } + AstNode *parse_identifier_or_type(AstFile *f) { switch (f->cursor[0].kind) { - case Token_Identifier: - return parse_identifier(f); + case Token_Identifier: { + AstNode *ident = parse_identifier(f); + while (f->cursor[0].kind == Token_Period) { + Token token = f->cursor[0]; + next_token(f); + AstNode *sel = parse_identifier(f); + ident = make_selector_expr(f, token, ident, sel); + } + return ident; + } case Token_Pointer: return make_pointer_type(f, expect_token(f, Token_Pointer), parse_type(f)); @@ -1687,10 +1734,6 @@ AstNode *parse_identifier_or_type(AstFile *f) { case Token_struct: { Token token = expect_token(f, Token_struct); - Token open, close; - AstNode *params = NULL; - isize param_count = 0; - AstScope *scope = make_ast_scope(f, NULL); // NOTE(bill): The struct needs its own scope with NO parent b32 is_packed = false; if (allow_token(f, Token_Hash)) { Token tag = expect_token(f, Token_Identifier); @@ -1701,12 +1744,72 @@ AstNode *parse_identifier_or_type(AstFile *f) { } } - open = expect_token(f, Token_OpenBrace); - params = parse_parameter_list(f, scope, ¶m_count, Token_Semicolon, true); - close = expect_token(f, Token_CloseBrace); + AstScope *scope = make_ast_scope(f, NULL); // NOTE(bill): The struct needs its own scope with NO parent + AstScope *curr_scope = f->curr_scope; + f->curr_scope = scope; + defer (f->curr_scope = curr_scope); - return make_struct_type(f, token, params, param_count, is_packed); - } + + Token open = expect_token(f, Token_OpenBrace); + AstNode *decls = NULL; + AstNode *decls_curr = NULL; + isize decl_count = 0; + + while (f->cursor[0].kind == Token_Identifier || + f->cursor[0].kind == Token_using) { + b32 is_using = false; + if (allow_token(f, Token_using)) { + is_using = true; + } + isize name_count = 0; + AstNode *name_list = parse_lhs_expr_list(f, &name_count); + if (name_count == 0) { + ast_file_err(f, f->cursor[0], "Empty field declaration"); + } + + if (name_count > 1 && is_using) { + ast_file_err(f, f->cursor[0], "Cannot apply `using` to more than one of the same type"); + } + + AstNode *decl = NULL; + + if (f->cursor[0].kind == Token_Colon) { + decl = parse_decl(f, name_list, name_count); + + if (decl->kind == AstNode_ProcDecl) { + ast_file_err(f, f->cursor[0], "Procedure declarations are not allowed within a structure"); + decl = make_bad_decl(f, ast_node_token(name_list), f->cursor[0]); + } + } else { + ast_file_err(f, f->cursor[0], "Illegal structure field"); + decl = make_bad_decl(f, ast_node_token(name_list), f->cursor[0]); + } + + expect_semicolon_after_stmt(f, decl); + + if (decl != NULL && is_ast_node_decl(decl)) { + DLIST_APPEND(decls, decls_curr, decl); + if (decl->kind == AstNode_VarDecl) { + decl_count += decl->VarDecl.name_count; + decl->VarDecl.is_using = is_using; + + if (decl->VarDecl.kind == Declaration_Mutable) { + if (decl->VarDecl.value_count > 0) { + ast_file_err(f, f->cursor[0], "Default variable assignments within a structure will be ignored (at the moment)"); + } + } + + } else { + decl_count += 1; + } + } + } + Token close = expect_token(f, Token_CloseBrace); + + // params = parse_parameter_list(f, scope, ¶m_count, Token_Semicolon, true); + + return make_struct_type(f, token, decls, decl_count, is_packed); + } break; case Token_union: { Token token = expect_token(f, Token_union); @@ -2100,6 +2203,7 @@ AstNode *parse_defer_stmt(AstFile *f) { return make_defer_stmt(f, token, statement); } + AstNode *parse_stmt(AstFile *f) { AstNode *s = NULL; Token token = f->cursor[0]; @@ -2117,18 +2221,7 @@ AstNode *parse_stmt(AstFile *f) { case Token_Xor: case Token_Not: s = parse_simple_stmt(f); - if (s->kind != AstNode_ProcDecl && - (s->kind == AstNode_TypeDecl && - s->TypeDecl.type->kind != AstNode_StructType && - s->TypeDecl.type->kind != AstNode_UnionType && - s->TypeDecl.type->kind != AstNode_EnumType && - s->TypeDecl.type->kind != AstNode_ProcType) && - !allow_token(f, Token_Semicolon)) { - // CLEANUP(bill): Semicolon handling in parser - ast_file_err(f, f->cursor[0], - "Expected `;` after statement, got `%.*s`", - LIT(token_strings[f->cursor[0].kind])); - } + expect_semicolon_after_stmt(f, s); return s; // TODO(bill): other keywords @@ -2146,6 +2239,36 @@ AstNode *parse_stmt(AstFile *f) { expect_token(f, Token_Semicolon); return make_branch_stmt(f, token); + case Token_using: { + AstNode *node = NULL; + + next_token(f); + node = parse_stmt(f); + + b32 valid = false; + + switch (node->kind) { + case AstNode_ExprStmt: + if (node->ExprStmt.expr->kind == AstNode_Ident) { + valid = true; + } + break; + case AstNode_VarDecl: + if (node->VarDecl.kind == Declaration_Mutable) { + valid = true; + } + break; + } + + if (!valid) { + ast_file_err(f, token, "Illegal use of `using` statement."); + return make_bad_stmt(f, token, f->cursor[0]); + } + + + return make_using_stmt(f, token, node); + } break; + case Token_Hash: { s = parse_tag_stmt(f, NULL); diff --git a/src/printer.cpp b/src/printer.cpp index f2e215fb3..df93a92db 100644 --- a/src/printer.cpp +++ b/src/printer.cpp @@ -180,7 +180,7 @@ void print_ast(AstNode *node, isize indent) { case AstNode_StructType: print_indent(indent); gb_printf("(struct)\n"); - print_ast(node->StructType.field_list, indent+1); + print_ast(node->StructType.decl_list, indent+1); break; }