mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 23:28:48 +00:00
using on struct/union fields
This commit is contained in:
+117
-25
@@ -1,26 +1,18 @@
|
|||||||
// Demo 001
|
// Demo 001
|
||||||
#load "basic.odin"
|
#load "basic.odin"
|
||||||
#load "math.odin"
|
|
||||||
#load "game.odin"
|
#load "game.odin"
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
// _ = hellope();
|
_ = hellope();
|
||||||
// procedures();
|
procedures();
|
||||||
// variables();
|
variables();
|
||||||
// constants();
|
constants();
|
||||||
// types();
|
types();
|
||||||
// data_control();
|
data_control();
|
||||||
|
using_fields();
|
||||||
|
|
||||||
|
|
||||||
// run_game();
|
// run_game();
|
||||||
|
|
||||||
Thing :: type union {
|
|
||||||
i: i32,
|
|
||||||
f: f32,
|
|
||||||
}
|
|
||||||
t: Thing;
|
|
||||||
t.f = 123;
|
|
||||||
print_int_base(t.i as int, 16);
|
|
||||||
print_nl();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hellope :: proc() -> int {
|
hellope :: proc() -> int {
|
||||||
@@ -43,7 +35,7 @@ hellope :: proc() -> int {
|
|||||||
|
|
||||||
apple, banana, carrot: bool;
|
apple, banana, carrot: bool;
|
||||||
box, carboard: bool = true, false;
|
box, carboard: bool = true, false;
|
||||||
hellope_value: int = hellope();
|
hellope_value: int = hellope(); // The procedure is ran just before `main`
|
||||||
|
|
||||||
variables :: proc() {
|
variables :: proc() {
|
||||||
i: int; // initialized with zero value
|
i: int; // initialized with zero value
|
||||||
@@ -293,16 +285,16 @@ types :: proc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BinaryNode :: type struct {
|
BinaryNode :: type struct {
|
||||||
left, right: ^BinaryNode, // same format as procedure argument
|
left, right: ^BinaryNode; // same format as procedure argument
|
||||||
data: rawptr,
|
data: rawptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
AddProc :: type proc(a, b: int) -> int
|
AddProc :: type proc(a, b: int) -> int
|
||||||
|
|
||||||
Packed :: type struct #packed {
|
Packed :: type struct #packed {
|
||||||
a: u8,
|
a: u8;
|
||||||
b: u16,
|
b: u16;
|
||||||
c: u32,
|
c: u32;
|
||||||
}
|
}
|
||||||
static_assert(size_of(Packed) == 7); // builtin procedure
|
static_assert(size_of(Packed) == 7); // builtin procedure
|
||||||
|
|
||||||
@@ -383,6 +375,50 @@ types :: proc() {
|
|||||||
static_assert(Certain.TOMB == 8);
|
static_assert(Certain.TOMB == 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ // Untagged union
|
||||||
|
BitHack :: type union {
|
||||||
|
i: i32;
|
||||||
|
f: f32;
|
||||||
|
}
|
||||||
|
b: BitHack;
|
||||||
|
b.f = 123;
|
||||||
|
print_int(b.i as int); print_nl();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Manually tagged union
|
||||||
|
|
||||||
|
EntityKind :: type enum {
|
||||||
|
Invalid,
|
||||||
|
Constant,
|
||||||
|
Variable,
|
||||||
|
TypeName,
|
||||||
|
Procedure,
|
||||||
|
Builtin,
|
||||||
|
Count,
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity :: type struct {
|
||||||
|
kind: EntityKind;
|
||||||
|
guid: u64;
|
||||||
|
|
||||||
|
// Other data
|
||||||
|
|
||||||
|
/*using*/
|
||||||
|
data: union {
|
||||||
|
constant: struct{};
|
||||||
|
variable: struct{
|
||||||
|
visited, is_field, used, anonymous: bool;
|
||||||
|
};
|
||||||
|
procedure: struct { used: bool };
|
||||||
|
buitlin: struct { id: i32 };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NOTE(bill): Tagged unions are not added yet but are planned
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{ // Compound Literals
|
{ // Compound Literals
|
||||||
@@ -478,9 +514,9 @@ void main() {
|
|||||||
|
|
||||||
{ // size, align, offset
|
{ // size, align, offset
|
||||||
Thing :: type struct {
|
Thing :: type struct {
|
||||||
a: u8,
|
a: u8;
|
||||||
b: u16,
|
b: u16;
|
||||||
c, d, e: u32,
|
c, d, e: u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
s := size_of(Thing);
|
s := size_of(Thing);
|
||||||
@@ -619,3 +655,59 @@ data_control :: proc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
using_fields :: proc() {
|
||||||
|
{ // Everyday stuff
|
||||||
|
Vec3 :: type struct { x, y, z: f32; }
|
||||||
|
|
||||||
|
Entity :: type struct {
|
||||||
|
name: string;
|
||||||
|
using pos: Vec3;
|
||||||
|
vel: Vec3;
|
||||||
|
}
|
||||||
|
t: Entity;
|
||||||
|
t.y = 456;
|
||||||
|
print_f32(t.y); print_nl();
|
||||||
|
print_f32(t.pos.y); print_nl();
|
||||||
|
print_f32(t.vel.y); print_nl();
|
||||||
|
|
||||||
|
|
||||||
|
Frog :: type struct { // Subtype (kind of)
|
||||||
|
using entity: Entity;
|
||||||
|
colour: u32;
|
||||||
|
jump_height: f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
f: Frog;
|
||||||
|
f.y = 1337;
|
||||||
|
print_f32(f.y); print_nl();
|
||||||
|
print_f32(f.pos.y); print_nl();
|
||||||
|
print_f32(f.vel.y); print_nl();
|
||||||
|
|
||||||
|
|
||||||
|
Buffalo :: type struct {
|
||||||
|
using entity: Entity;
|
||||||
|
speed: f32;
|
||||||
|
noise_level: f32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{ // Crazy Shit
|
||||||
|
Vec2 :: type union {
|
||||||
|
using _xy: struct {x, y: f32};
|
||||||
|
e: [2]f32;
|
||||||
|
v: {2}f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity :: type struct {
|
||||||
|
using pos: ^Vec2;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
t: Entity;
|
||||||
|
t.pos = alloc(size_of(Vec2)) as ^Vec2; // TODO(bill): make an alloc type? i.e. new(Type)?
|
||||||
|
t.x = 123;
|
||||||
|
print_f32(t._xy.x); print_nl();
|
||||||
|
print_f32(t.pos.x); print_nl();
|
||||||
|
print_f32(t.pos._xy.x); print_nl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
FileHandle :: type HANDLE;
|
FileHandle :: type HANDLE;
|
||||||
|
|
||||||
File :: type struct {
|
File :: type struct {
|
||||||
handle: FileHandle,
|
handle: FileHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
file_open :: proc(name: string) -> (File, bool) {
|
file_open :: proc(name: string) -> (File, bool) {
|
||||||
|
|||||||
+8
-8
@@ -34,12 +34,12 @@ to_c_string :: proc(s: string) -> ^u8 {
|
|||||||
|
|
||||||
|
|
||||||
Window :: type struct {
|
Window :: type struct {
|
||||||
width, height: int,
|
width, height: int;
|
||||||
wc: WNDCLASSEXA,
|
wc: WNDCLASSEXA;
|
||||||
dc: HDC,
|
dc: HDC;
|
||||||
hwnd: HWND,
|
hwnd: HWND;
|
||||||
opengl_context, rc: HGLRC,
|
opengl_context, rc: HGLRC;
|
||||||
c_title: ^u8,
|
c_title: ^u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
make_window :: proc(title: string, msg, height: int, window_proc: WNDPROC) -> (Window, bool) {
|
make_window :: proc(title: string, msg, height: int, window_proc: WNDPROC) -> (Window, bool) {
|
||||||
@@ -123,8 +123,8 @@ display_window :: proc(w: ^Window) {
|
|||||||
|
|
||||||
|
|
||||||
Entity :: type struct {
|
Entity :: type struct {
|
||||||
pos: Vec2,
|
pos: Vec2;
|
||||||
dim: Vec2,
|
dim: Vec2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -258,18 +258,18 @@ AllocatorProc :: type proc(allocator_data: rawptr, mode: AllocationMode,
|
|||||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr;
|
old_memory: rawptr, old_size: int, flags: u64) -> rawptr;
|
||||||
|
|
||||||
Allocator :: type struct {
|
Allocator :: type struct {
|
||||||
procedure: AllocatorProc,
|
procedure: AllocatorProc;
|
||||||
data: rawptr,
|
data: rawptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Context :: type struct {
|
Context :: type struct {
|
||||||
thread_id: i32,
|
thread_id: i32;
|
||||||
|
|
||||||
user_index: i32,
|
user_index: i32;
|
||||||
user_data: rawptr,
|
user_data: rawptr;
|
||||||
|
|
||||||
allocator: Allocator,
|
allocator: Allocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
#thread_local context: Context;
|
#thread_local context: Context;
|
||||||
|
|||||||
+18
-18
@@ -40,24 +40,24 @@ INVALID_HANDLE_VALUE :: (-1 as int) as HANDLE;
|
|||||||
WNDPROC :: type proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT
|
WNDPROC :: type proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT
|
||||||
|
|
||||||
WNDCLASSEXA :: type struct {
|
WNDCLASSEXA :: type struct {
|
||||||
size, style: u32,
|
size, style: u32;
|
||||||
wnd_proc: WNDPROC,
|
wnd_proc: WNDPROC;
|
||||||
cls_extra, wnd_extra: i32,
|
cls_extra, wnd_extra: i32;
|
||||||
instance: HINSTANCE,
|
instance: HINSTANCE;
|
||||||
icon: HICON,
|
icon: HICON;
|
||||||
cursor: HCURSOR,
|
cursor: HCURSOR;
|
||||||
background: HBRUSH,
|
background: HBRUSH;
|
||||||
menu_name, class_name: ^u8,
|
menu_name, class_name: ^u8;
|
||||||
sm: HICON,
|
sm: HICON;
|
||||||
}
|
}
|
||||||
|
|
||||||
MSG :: type struct {
|
MSG :: type struct {
|
||||||
hwnd: HWND,
|
hwnd: HWND;
|
||||||
message: u32,
|
message: u32;
|
||||||
wparam: WPARAM,
|
wparam: WPARAM;
|
||||||
lparam: LPARAM,
|
lparam: LPARAM;
|
||||||
time: u32,
|
time: u32;
|
||||||
pt: POINT,
|
pt: POINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -189,7 +189,7 @@ wglCreateContextAttribsARBType :: type proc(hdc: HDC, hshareContext: rawptr, att
|
|||||||
PIXELFORMATDESCRIPTOR :: type struct {
|
PIXELFORMATDESCRIPTOR :: type struct {
|
||||||
size,
|
size,
|
||||||
version,
|
version,
|
||||||
flags: u32,
|
flags: u32;
|
||||||
|
|
||||||
pixel_type,
|
pixel_type,
|
||||||
color_bits,
|
color_bits,
|
||||||
@@ -210,11 +210,11 @@ PIXELFORMATDESCRIPTOR :: type struct {
|
|||||||
stencil_bits,
|
stencil_bits,
|
||||||
aux_buffers,
|
aux_buffers,
|
||||||
layer_type,
|
layer_type,
|
||||||
reserved: byte,
|
reserved: byte;
|
||||||
|
|
||||||
layer_mask,
|
layer_mask,
|
||||||
visible_mask,
|
visible_mask,
|
||||||
damage_mask: u32,
|
damage_mask: u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
GetDC :: proc(h: HANDLE) -> HDC #foreign
|
GetDC :: proc(h: HANDLE) -> HDC #foreign
|
||||||
|
|||||||
@@ -38,9 +38,10 @@ struct Entity {
|
|||||||
union {
|
union {
|
||||||
struct { ExactValue value; } Constant;
|
struct { ExactValue value; } Constant;
|
||||||
struct {
|
struct {
|
||||||
b8 visited;
|
b8 visited; // Cycle detection
|
||||||
b8 is_field;
|
b8 is_field; // Is struct field
|
||||||
b8 used;
|
b8 used; // Variable is used
|
||||||
|
b8 anonymous; // Variable is an anonymous struct field
|
||||||
} Variable;
|
} Variable;
|
||||||
struct { b8 used; } Procedure;
|
struct { b8 used; } Procedure;
|
||||||
struct { BuiltinProcId id; } Builtin;
|
struct { BuiltinProcId id; } Builtin;
|
||||||
@@ -85,9 +86,10 @@ Entity *make_entity_param(gbAllocator a, Scope *parent, Token token, Type *type)
|
|||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
Entity *make_entity_field(gbAllocator a, Scope *parent, Token token, Type *type) {
|
Entity *make_entity_field(gbAllocator a, Scope *parent, Token token, Type *type, b32 is_anonymous) {
|
||||||
Entity *entity = make_entity_variable(a, parent, token, type);
|
Entity *entity = make_entity_variable(a, parent, token, type);
|
||||||
entity->Variable.is_field = true;
|
entity->Variable.is_field = true;
|
||||||
|
entity->Variable.anonymous = cast(b8)is_anonymous;
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+236
-90
@@ -13,16 +13,91 @@ void check_entity_decl (Checker *c, Entity *e, DeclInfo *decl, T
|
|||||||
void check_proc_body (Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body);
|
void check_proc_body (Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body);
|
||||||
void update_expr_type (Checker *c, AstNode *e, Type *type, b32 final);
|
void update_expr_type (Checker *c, AstNode *e, Type *type, b32 final);
|
||||||
|
|
||||||
|
void populate_using_entity_map(Checker *c, AstNode *node, Type *t, Map<Entity *> *entity_map) {
|
||||||
|
t = get_base_type(type_deref(t));
|
||||||
|
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];
|
||||||
|
GB_ASSERT(f->kind == Entity_Variable);
|
||||||
|
String name = f->token.string;
|
||||||
|
HashKey key = hash_string(name);
|
||||||
|
Entity **found = map_get(entity_map, key);
|
||||||
|
if (found != NULL) {
|
||||||
|
Entity *e = *found;
|
||||||
|
// TODO(bill): Better type error
|
||||||
|
error(&c->error_collector, e->token, "`%.*s` is already declared in `%s`", LIT(name), str);
|
||||||
|
} else {
|
||||||
|
map_set(entity_map, key, f);
|
||||||
|
if (f->Variable.anonymous) {
|
||||||
|
populate_using_entity_map(c, node, f->type, entity_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_fields(Checker *c, AstNode *node, AstNode *field_list, Entity **fields, isize field_count, String context) {
|
||||||
|
Map<Entity *> entity_map = {};
|
||||||
|
map_init(&entity_map, gb_heap_allocator());
|
||||||
|
defer (map_destroy(&entity_map));
|
||||||
|
|
||||||
|
isize field_index = 0;
|
||||||
|
for (AstNode *field = field_list; field != NULL; field = field->next) {
|
||||||
|
ast_node(f, Field, field);
|
||||||
|
Type *type = check_type(c, f->type);
|
||||||
|
|
||||||
|
if (f->is_using) {
|
||||||
|
if (f->name_count > 1) {
|
||||||
|
error(&c->error_collector, ast_node_token(f->name_list),
|
||||||
|
"Cannot apply `using` to more than one of the same type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (AstNode *name = f->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, f->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 %.*s", LIT(name_token.string), LIT(context));
|
||||||
|
} else {
|
||||||
|
map_set(&entity_map, key, e);
|
||||||
|
fields[field_index++] = e;
|
||||||
|
}
|
||||||
|
add_entity_use(&c->info, name, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (f->is_using) {
|
||||||
|
Type *t = get_base_type(type_deref(type));
|
||||||
|
if (t->kind != Type_Struct &&
|
||||||
|
t->kind != Type_Union) {
|
||||||
|
Token name_token = f->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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void check_struct_type(Checker *c, Type *struct_type, AstNode *node) {
|
void check_struct_type(Checker *c, Type *struct_type, AstNode *node) {
|
||||||
GB_ASSERT(node->kind == AstNode_StructType);
|
GB_ASSERT(node->kind == AstNode_StructType);
|
||||||
GB_ASSERT(struct_type->kind == Type_Struct);
|
GB_ASSERT(struct_type->kind == Type_Struct);
|
||||||
ast_node(st, StructType, node);
|
ast_node(st, StructType, node);
|
||||||
|
|
||||||
Map<Entity *> entity_map = {};
|
|
||||||
map_init(&entity_map, gb_heap_allocator());
|
|
||||||
defer (map_destroy(&entity_map));
|
|
||||||
|
|
||||||
isize field_count = 0;
|
isize field_count = 0;
|
||||||
for (AstNode *field = st->field_list; field != NULL; field = field->next) {
|
for (AstNode *field = st->field_list; field != NULL; field = field->next) {
|
||||||
for (AstNode *name = field->Field.name_list; name != NULL; name = name->next) {
|
for (AstNode *name = field->Field.name_list; name != NULL; name = name->next) {
|
||||||
@@ -32,26 +107,7 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Entity **fields = gb_alloc_array(c->allocator, Entity *, st->field_count);
|
Entity **fields = gb_alloc_array(c->allocator, Entity *, st->field_count);
|
||||||
isize field_index = 0;
|
check_fields(c, node, st->field_list, fields, field_count, make_string("structure"));
|
||||||
for (AstNode *field = st->field_list; field != NULL; field = field->next) {
|
|
||||||
ast_node(f, Field, field);
|
|
||||||
Type *type = check_type(c, f->type);
|
|
||||||
for (AstNode *name = f->name_list; name != NULL; name = name->next) {
|
|
||||||
ast_node(i, Ident, name);
|
|
||||||
Token name_token = i->token;
|
|
||||||
// TODO(bill): is the curr_scope correct?
|
|
||||||
Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type);
|
|
||||||
HashKey key = hash_string(name_token.string);
|
|
||||||
if (map_get(&entity_map, key)) {
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
struct_type->Struct.fields = fields;
|
struct_type->Struct.fields = fields;
|
||||||
struct_type->Struct.field_count = field_count;
|
struct_type->Struct.field_count = field_count;
|
||||||
struct_type->Struct.is_packed = st->is_packed;
|
struct_type->Struct.is_packed = st->is_packed;
|
||||||
@@ -62,10 +118,6 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) {
|
|||||||
GB_ASSERT(union_type->kind == Type_Union);
|
GB_ASSERT(union_type->kind == Type_Union);
|
||||||
ast_node(ut, UnionType, node);
|
ast_node(ut, UnionType, node);
|
||||||
|
|
||||||
Map<Entity *> entity_map = {};
|
|
||||||
map_init(&entity_map, gb_heap_allocator());
|
|
||||||
defer (map_destroy(&entity_map));
|
|
||||||
|
|
||||||
isize field_count = 0;
|
isize field_count = 0;
|
||||||
for (AstNode *field = ut->field_list; field != NULL; field = field->next) {
|
for (AstNode *field = ut->field_list; field != NULL; field = field->next) {
|
||||||
for (AstNode *name = field->Field.name_list; name != NULL; name = name->next) {
|
for (AstNode *name = field->Field.name_list; name != NULL; name = name->next) {
|
||||||
@@ -75,26 +127,7 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Entity **fields = gb_alloc_array(c->allocator, Entity *, ut->field_count);
|
Entity **fields = gb_alloc_array(c->allocator, Entity *, ut->field_count);
|
||||||
isize field_index = 0;
|
check_fields(c, node, ut->field_list, fields, field_count, make_string("union"));
|
||||||
for (AstNode *field = ut->field_list; field != NULL; field = field->next) {
|
|
||||||
ast_node(f, Field, field);
|
|
||||||
Type *type = check_type(c, f->type);
|
|
||||||
for (AstNode *name = f->name_list; name != NULL; name = name->next) {
|
|
||||||
ast_node(i, Ident, name);
|
|
||||||
Token name_token = i->token;
|
|
||||||
// TODO(bill): is the curr_scope correct?
|
|
||||||
Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type);
|
|
||||||
HashKey key = hash_string(name_token.string);
|
|
||||||
if (map_get(&entity_map, key)) {
|
|
||||||
// TODO(bill): Scope checking already checks the declaration
|
|
||||||
error(&c->error_collector, name_token, "`%.*s` is already declared in this union", LIT(name_token.string));
|
|
||||||
} else {
|
|
||||||
map_set(&entity_map, key, e);
|
|
||||||
fields[field_index++] = e;
|
|
||||||
}
|
|
||||||
add_entity_use(&c->info, name, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
union_type->Union.fields = fields;
|
union_type->Union.fields = fields;
|
||||||
union_type->Union.field_count = field_count;
|
union_type->Union.field_count = field_count;
|
||||||
}
|
}
|
||||||
@@ -1404,24 +1437,64 @@ b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *valu
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Entity *lookup_field(Type *type, AstNode *field_node, isize *index = NULL) {
|
struct Selection {
|
||||||
GB_ASSERT(type != NULL);
|
Entity *entity;
|
||||||
GB_ASSERT(field_node->kind == AstNode_Ident);
|
gbArray(isize) index;
|
||||||
|
b32 indirect; // Set if there was a pointer deref anywhere down the line
|
||||||
|
};
|
||||||
|
Selection empty_selection = {};
|
||||||
|
|
||||||
|
Selection make_selection(Entity *entity, gbArray(isize) index, b32 indirect) {
|
||||||
|
Selection s = {entity, index, indirect};
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void selection_add_index(Selection *s, isize index) {
|
||||||
|
if (s->index == NULL) {
|
||||||
|
gb_array_init(s->index, gb_heap_allocator());
|
||||||
|
}
|
||||||
|
gb_array_append(s->index, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Selection lookup_field(Type *type_, String field_name, Selection sel = empty_selection) {
|
||||||
|
GB_ASSERT(type_ != NULL);
|
||||||
|
|
||||||
|
if (are_strings_equal(field_name, make_string("_"))) {
|
||||||
|
return empty_selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type *type = type_deref(type_);
|
||||||
|
b32 is_ptr = type != type_;
|
||||||
type = get_base_type(type);
|
type = get_base_type(type);
|
||||||
if (type->kind == Type_Pointer)
|
|
||||||
type = get_base_type(type->Pointer.elem);
|
|
||||||
|
|
||||||
ast_node(i, Ident, field_node);
|
|
||||||
String field_str = i->token.string;
|
|
||||||
switch (type->kind) {
|
switch (type->kind) {
|
||||||
case Type_Struct:
|
case Type_Struct:
|
||||||
for (isize i = 0; i < type->Struct.field_count; i++) {
|
for (isize i = 0; i < type->Struct.field_count; i++) {
|
||||||
Entity *f = type->Struct.fields[i];
|
Entity *f = type->Struct.fields[i];
|
||||||
GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field);
|
GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field);
|
||||||
String str = f->token.string;
|
String str = f->token.string;
|
||||||
if (are_strings_equal(field_str, str)) {
|
if (are_strings_equal(field_name, str)) {
|
||||||
if (index) *index = i;
|
selection_add_index(&sel, i);
|
||||||
return f;
|
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;
|
||||||
|
return sel;
|
||||||
|
}
|
||||||
|
gb_array_count(sel.index) = prev_count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1431,7 +1504,66 @@ Entity *lookup_field(Type *type, AstNode *field_node, isize *index = NULL) {
|
|||||||
Entity *f = type->Union.fields[i];
|
Entity *f = type->Union.fields[i];
|
||||||
GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field);
|
GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field);
|
||||||
String str = f->token.string;
|
String str = f->token.string;
|
||||||
if (are_strings_equal(field_str, str)) {
|
if (are_strings_equal(field_name, str)) {
|
||||||
|
selection_add_index(&sel, i);
|
||||||
|
sel.entity = f;
|
||||||
|
return sel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 (f->Variable.anonymous) {
|
||||||
|
selection_add_index(&sel, i); // HACK(bill): Leaky memory
|
||||||
|
sel = lookup_field(f->type, field_name, sel);
|
||||||
|
if (sel.entity != NULL && is_type_pointer(f->type)) {
|
||||||
|
sel.indirect = true;
|
||||||
|
}
|
||||||
|
return 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
if (index) *index = i;
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
@@ -1443,7 +1575,7 @@ Entity *lookup_field(Type *type, AstNode *field_node, isize *index = NULL) {
|
|||||||
Entity *f = type->Enum.fields[i];
|
Entity *f = type->Enum.fields[i];
|
||||||
GB_ASSERT(f->kind == Entity_Constant);
|
GB_ASSERT(f->kind == Entity_Constant);
|
||||||
String str = f->token.string;
|
String str = f->token.string;
|
||||||
if (are_strings_equal(field_str, str)) {
|
if (are_strings_equal(field_name, str)) {
|
||||||
if (index) *index = i;
|
if (index) *index = i;
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
@@ -1454,7 +1586,7 @@ Entity *lookup_field(Type *type, AstNode *field_node, isize *index = NULL) {
|
|||||||
// Or is this only suitable if tuples are first-class?
|
// Or is this only suitable if tuples are first-class?
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_selector(Checker *c, Operand *operand, AstNode *node) {
|
void check_selector(Checker *c, Operand *operand, AstNode *node) {
|
||||||
@@ -1467,10 +1599,10 @@ void check_selector(Checker *c, Operand *operand, AstNode *node) {
|
|||||||
Entity *entity = NULL;
|
Entity *entity = NULL;
|
||||||
if (is_type_enum(operand->type)) {
|
if (is_type_enum(operand->type)) {
|
||||||
if (operand->mode == Addressing_Type) {
|
if (operand->mode == Addressing_Type) {
|
||||||
entity = lookup_field(operand->type, selector);
|
entity = lookup_field(operand->type, selector->Ident.token.string).entity;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
entity = lookup_field(operand->type, selector);
|
entity = lookup_field(operand->type, selector->Ident.token.string).entity;
|
||||||
}
|
}
|
||||||
if (entity == NULL) {
|
if (entity == NULL) {
|
||||||
gbString op_str = expr_to_string(op_expr);
|
gbString op_str = expr_to_string(op_expr);
|
||||||
@@ -1596,10 +1728,10 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isize index = 0;
|
|
||||||
Entity *entity = lookup_field(type, field_arg, &index);
|
ast_node(arg, Ident, field_arg);
|
||||||
if (entity == NULL) {
|
Selection sel = lookup_field(type, arg->token.string);
|
||||||
ast_node(arg, Ident, field_arg);
|
if (sel.entity == NULL) {
|
||||||
gbString type_str = type_to_string(type);
|
gbString type_str = type_to_string(type);
|
||||||
error(&c->error_collector, ast_node_token(ce->arg_list),
|
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->token.string));
|
||||||
@@ -1607,7 +1739,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
operand->mode = Addressing_Constant;
|
operand->mode = Addressing_Constant;
|
||||||
operand->value = make_exact_value_integer(type_offset_of(c->sizes, c->allocator, type, index));
|
// IMPORTANT TODO(bill): Fix for anonymous fields
|
||||||
|
operand->value = make_exact_value_integer(type_offset_of(c->sizes, c->allocator, type, sel.index[0]));
|
||||||
operand->type = t_int;
|
operand->type = t_int;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@@ -1632,10 +1765,10 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
|||||||
type = p->Pointer.elem;
|
type = p->Pointer.elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
isize index = 0;
|
|
||||||
Entity *entity = lookup_field(type, s->selector, &index);
|
ast_node(i, Ident, s->selector);
|
||||||
if (entity == NULL) {
|
Selection sel = lookup_field(type, i->token.string);
|
||||||
ast_node(i, Ident, s->selector);
|
if (sel.entity == NULL) {
|
||||||
gbString type_str = type_to_string(type);
|
gbString type_str = type_to_string(type);
|
||||||
error(&c->error_collector, ast_node_token(arg),
|
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->token.string));
|
||||||
@@ -1643,7 +1776,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
operand->mode = Addressing_Constant;
|
operand->mode = Addressing_Constant;
|
||||||
operand->value = make_exact_value_integer(type_offset_of(c->sizes, c->allocator, type, index));
|
// IMPORTANT TODO(bill): Fix for anonymous fields
|
||||||
|
operand->value = make_exact_value_integer(type_offset_of(c->sizes, c->allocator, type, sel.index[0]));
|
||||||
operand->type = t_int;
|
operand->type = t_int;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@@ -2097,23 +2231,29 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ
|
|||||||
}
|
}
|
||||||
String name = kv->field->Ident.token.string;
|
String name = kv->field->Ident.token.string;
|
||||||
|
|
||||||
isize index = 0;
|
Selection sel = lookup_field(type, kv->field->Ident.token.string);
|
||||||
Entity *e = lookup_field(type, kv->field, &index);
|
if (sel.entity == NULL) {
|
||||||
if (e == NULL) {
|
|
||||||
error(&c->error_collector, ast_node_token(elem),
|
error(&c->error_collector, ast_node_token(elem),
|
||||||
"Unknown field `%.*s` in structure literal", LIT(name));
|
"Unknown field `%.*s` in structure literal", LIT(name));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Entity *field = t->Struct.fields[index];
|
|
||||||
|
if (gb_array_count(sel.index) > 1) {
|
||||||
|
error(&c->error_collector, ast_node_token(elem),
|
||||||
|
"You cannot assign to an anonymous field `%.*s` in a structure literal (at the moment)", LIT(name));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity *field = t->Struct.fields[sel.index[0]];
|
||||||
add_entity_use(&c->info, kv->field, field);
|
add_entity_use(&c->info, kv->field, field);
|
||||||
|
|
||||||
if (fields_visited[index]) {
|
if (fields_visited[sel.index[0]]) {
|
||||||
error(&c->error_collector, ast_node_token(elem),
|
error(&c->error_collector, ast_node_token(elem),
|
||||||
"Duplicate field `%.*s` in structure literal", LIT(name));
|
"Duplicate field `%.*s` in structure literal", LIT(name));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
fields_visited[index] = true;
|
fields_visited[sel.index[0]] = true;
|
||||||
check_expr(c, o, kv->value);
|
check_expr(c, o, kv->value);
|
||||||
check_assignment(c, o, field->type, make_string("structure literal"));
|
check_assignment(c, o, field->type, make_string("structure literal"));
|
||||||
}
|
}
|
||||||
@@ -2540,17 +2680,7 @@ gbString write_field_list_to_string(gbString str, AstNode *field_list, char *sep
|
|||||||
if (i > 0)
|
if (i > 0)
|
||||||
str = gb_string_appendc(str, sep);
|
str = gb_string_appendc(str, sep);
|
||||||
|
|
||||||
isize j = 0;
|
str = write_expr_to_string(str, field);
|
||||||
for (AstNode *name = f->name_list; name != NULL; name = name->next) {
|
|
||||||
if (j > 0)
|
|
||||||
str = gb_string_appendc(str, ", ");
|
|
||||||
str = write_expr_to_string(str, name);
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
|
|
||||||
str = gb_string_appendc(str, ": ");
|
|
||||||
str = write_expr_to_string(str, f->type);
|
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
@@ -2674,6 +2804,22 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
|
|||||||
str = write_expr_to_string(str, vt->elem);
|
str = write_expr_to_string(str, vt->elem);
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
|
case_ast_node(f, Field, node);
|
||||||
|
if (f->is_using) {
|
||||||
|
str = gb_string_appendc(str, "using ");
|
||||||
|
}
|
||||||
|
isize i = 0;
|
||||||
|
for (AstNode *name = f->name_list; name != NULL; name = name->next) {
|
||||||
|
if (i > 0)
|
||||||
|
str = gb_string_appendc(str, ", ");
|
||||||
|
str = write_expr_to_string(str, name);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
str = gb_string_appendc(str, ": ");
|
||||||
|
str = write_expr_to_string(str, f->type);
|
||||||
|
case_end;
|
||||||
|
|
||||||
case_ast_node(ce, CallExpr, node);
|
case_ast_node(ce, CallExpr, node);
|
||||||
str = write_expr_to_string(str, ce->proc);
|
str = write_expr_to_string(str, ce->proc);
|
||||||
str = gb_string_appendc(str, "(");
|
str = gb_string_appendc(str, "(");
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ struct Type {
|
|||||||
b32 is_packed;
|
b32 is_packed;
|
||||||
} Struct;
|
} Struct;
|
||||||
struct {
|
struct {
|
||||||
|
// IMPORTANT HACK(bill): The positions of fields and field_count
|
||||||
|
// must be same for Struct and Union
|
||||||
Entity **fields; // Entity_Variable
|
Entity **fields; // Entity_Variable
|
||||||
isize field_count;
|
isize field_count;
|
||||||
} Union;
|
} Union;
|
||||||
|
|||||||
+36
-24
@@ -1606,7 +1606,8 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
|||||||
|
|
||||||
if (elem->kind == AstNode_FieldValue) {
|
if (elem->kind == AstNode_FieldValue) {
|
||||||
ast_node(kv, FieldValue, elem);
|
ast_node(kv, FieldValue, elem);
|
||||||
Entity *e = lookup_field(base_type, kv->field, &field_index);
|
Selection sel = lookup_field(base_type, kv->field->Ident.token.string);
|
||||||
|
field_index = sel.index[0];
|
||||||
field_expr = ssa_build_expr(proc, kv->value);
|
field_expr = ssa_build_expr(proc, kv->value);
|
||||||
} else {
|
} else {
|
||||||
field_expr = ssa_build_expr(proc, elem);
|
field_expr = ssa_build_expr(proc, elem);
|
||||||
@@ -1874,6 +1875,36 @@ ssaValue *ssa_build_expr(ssaProcedure *proc, AstNode *expr) {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ssaValue *ssa_emit_deep_field_gep(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) {
|
||||||
|
GB_ASSERT(gb_array_count(sel.index) > 0);
|
||||||
|
|
||||||
|
gb_for_array(i, sel.index) {
|
||||||
|
isize index = sel.index[i];
|
||||||
|
if (is_type_pointer(type)) {
|
||||||
|
type = type_deref(type);
|
||||||
|
e = ssa_emit_load(proc, e);
|
||||||
|
e = ssa_emit_ptr_offset(proc, e, v_zero);
|
||||||
|
ssa_set_type(e, type);
|
||||||
|
}
|
||||||
|
type = get_base_type(type);
|
||||||
|
|
||||||
|
|
||||||
|
if (type->kind == Type_Union) {
|
||||||
|
ssaValue *v = ssa_emit_ptr_offset(proc, e, v_zero);
|
||||||
|
ssa_set_type(v, make_type_pointer(proc->module->allocator, type));
|
||||||
|
type = type->Union.fields[index]->type;
|
||||||
|
e = ssa_emit_conv(proc, v, make_type_pointer(proc->module->allocator, type));
|
||||||
|
e = ssa_emit_ptr_offset(proc, e, v_zero);
|
||||||
|
ssa_set_type(e, type);
|
||||||
|
} else {
|
||||||
|
type = type->Union.fields[index]->type;
|
||||||
|
e = ssa_emit_struct_gep(proc, e, index, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
|
ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
|
||||||
switch (expr->kind) {
|
switch (expr->kind) {
|
||||||
@@ -1901,31 +1932,12 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
|
|||||||
case_ast_node(se, SelectorExpr, expr);
|
case_ast_node(se, SelectorExpr, expr);
|
||||||
Type *type = get_base_type(type_of_expr(proc->module->info, se->expr));
|
Type *type = get_base_type(type_of_expr(proc->module->info, se->expr));
|
||||||
|
|
||||||
isize field_index = 0;
|
Selection sel = lookup_field(type, unparen_expr(se->selector)->Ident.token.string);
|
||||||
Entity *entity = lookup_field(type, unparen_expr(se->selector), &field_index);
|
GB_ASSERT(sel.entity != NULL);
|
||||||
GB_ASSERT(entity != NULL);
|
|
||||||
|
|
||||||
ssaValue *e = ssa_build_addr(proc, se->expr).addr;
|
ssaValue *e = ssa_build_addr(proc, se->expr).addr;
|
||||||
|
e = ssa_emit_deep_field_gep(proc, type, e, sel);
|
||||||
if (is_type_pointer(type)) {
|
return ssa_make_addr(e, expr);
|
||||||
// NOTE(bill): Allow x^.y and x.y to be the same
|
|
||||||
e = ssa_emit_load(proc, e);
|
|
||||||
e = ssa_emit_ptr_offset(proc, e, v_zero);
|
|
||||||
ssa_set_type(e, type_deref(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type->kind == Type_Union) {
|
|
||||||
ssaValue *v = ssa_emit_ptr_offset(proc, e, v_zero);
|
|
||||||
ssa_set_type(v, make_type_pointer(proc->module->allocator, type));
|
|
||||||
ssaValue *f = ssa_emit_conv(proc, v, make_type_pointer(proc->module->allocator, entity->type));
|
|
||||||
f = ssa_emit_ptr_offset(proc, f, v_zero);
|
|
||||||
ssa_set_type(f, entity->type);
|
|
||||||
return ssa_make_addr(f, expr);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
ssaValue *v = ssa_emit_struct_gep(proc, e, field_index, entity->type);
|
|
||||||
return ssa_make_addr(v, expr);
|
|
||||||
}
|
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(ue, UnaryExpr, expr);
|
case_ast_node(ue, UnaryExpr, expr);
|
||||||
|
|||||||
+2
-2
@@ -14,8 +14,8 @@ struct HashKey {
|
|||||||
|
|
||||||
gb_inline HashKey hashing_proc(void const *data, isize len) {
|
gb_inline HashKey hashing_proc(void const *data, isize len) {
|
||||||
HashKey h = {};
|
HashKey h = {};
|
||||||
h.key = gb_murmur64(data, len);
|
// h.key = gb_murmur64(data, len);
|
||||||
// h.key = gb_fnv64a(data, len);
|
h.key = gb_fnv64a(data, len);
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+28
-10
@@ -197,6 +197,7 @@ AST_NODE_KIND(_TypeBegin, struct{}) \
|
|||||||
AstNode *name_list; \
|
AstNode *name_list; \
|
||||||
isize name_count; \
|
isize name_count; \
|
||||||
AstNode *type; \
|
AstNode *type; \
|
||||||
|
b32 is_using; \
|
||||||
}) \
|
}) \
|
||||||
AST_NODE_KIND(ProcType, struct { \
|
AST_NODE_KIND(ProcType, struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
@@ -725,11 +726,12 @@ gb_inline AstNode *make_var_decl(AstFile *f, DeclKind kind, AstNode *name_list,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
gb_inline AstNode *make_field(AstFile *f, AstNode *name_list, isize name_count, AstNode *type) {
|
gb_inline AstNode *make_field(AstFile *f, AstNode *name_list, isize name_count, AstNode *type, b32 is_using) {
|
||||||
AstNode *result = make_node(f, AstNode_Field);
|
AstNode *result = make_node(f, AstNode_Field);
|
||||||
result->Field.name_list = name_list;
|
result->Field.name_list = name_list;
|
||||||
result->Field.name_count = name_count;
|
result->Field.name_count = name_count;
|
||||||
result->Field.type = type;
|
result->Field.type = type;
|
||||||
|
result->Field.is_using = is_using;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1566,20 +1568,32 @@ AstNode *parse_type(AstFile *f) {
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNode *parse_field_decl(AstFile *f, AstScope *scope) {
|
AstNode *parse_field_decl(AstFile *f, AstScope *scope, b32 allow_using) {
|
||||||
|
b32 is_using = false;
|
||||||
AstNode *name_list = NULL;
|
AstNode *name_list = NULL;
|
||||||
isize name_count = 0;
|
isize name_count = 0;
|
||||||
|
if (allow_using) {
|
||||||
|
if (allow_token(f, Token_using)) {
|
||||||
|
is_using = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
name_list = parse_lhs_expr_list(f, &name_count);
|
name_list = parse_lhs_expr_list(f, &name_count);
|
||||||
if (name_count == 0)
|
if (name_count == 0)
|
||||||
ast_file_err(f, f->cursor[0], "Empty field declaration");
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
expect_token(f, Token_Colon);
|
expect_token(f, Token_Colon);
|
||||||
|
|
||||||
AstNode *type = parse_type_attempt(f);
|
AstNode *type = parse_type_attempt(f);
|
||||||
if (type == NULL)
|
if (type == NULL)
|
||||||
ast_file_err(f, f->cursor[0], "Expected a type for this field declaration");
|
ast_file_err(f, f->cursor[0], "Expected a type for this field declaration");
|
||||||
|
|
||||||
AstNode *field = make_field(f, name_list, name_count, type);
|
AstNode *field = make_field(f, name_list, name_count, type, is_using);
|
||||||
add_ast_entity(f, scope, field, name_list);
|
add_ast_entity(f, scope, field, name_list);
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
@@ -1602,15 +1616,19 @@ AstNode *parse_proc_type(AstFile *f, AstScope **scope_) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AstNode *parse_parameter_list(AstFile *f, AstScope *scope, isize *param_count_) {
|
AstNode *parse_parameter_list(AstFile *f, AstScope *scope, isize *param_count_, TokenKind separator, b32 allow_using) {
|
||||||
AstNode *param_list = NULL;
|
AstNode *param_list = NULL;
|
||||||
AstNode *param_list_curr = NULL;
|
AstNode *param_list_curr = NULL;
|
||||||
isize param_count = 0;
|
isize param_count = 0;
|
||||||
while (f->cursor[0].kind == Token_Identifier) {
|
while (f->cursor[0].kind == Token_Identifier ||
|
||||||
AstNode *field = parse_field_decl(f, scope);
|
(allow_using && f->cursor[0].kind == Token_using)) {
|
||||||
|
if (!allow_using && allow_token(f, Token_using)) {
|
||||||
|
ast_file_err(f, f->cursor[-1], "`using` is only allowed within structures (at the moment)");
|
||||||
|
}
|
||||||
|
AstNode *field = parse_field_decl(f, scope, allow_using);
|
||||||
DLIST_APPEND(param_list, param_list_curr, field);
|
DLIST_APPEND(param_list, param_list_curr, field);
|
||||||
param_count += field->Field.name_count;
|
param_count += field->Field.name_count;
|
||||||
if (f->cursor[0].kind != Token_Comma)
|
if (f->cursor[0].kind != separator)
|
||||||
break;
|
break;
|
||||||
next_token(f);
|
next_token(f);
|
||||||
}
|
}
|
||||||
@@ -1669,7 +1687,7 @@ AstNode *parse_identifier_or_type(AstFile *f) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open = expect_token(f, Token_OpenBrace);
|
open = expect_token(f, Token_OpenBrace);
|
||||||
params = parse_parameter_list(f, scope, ¶m_count);
|
params = parse_parameter_list(f, scope, ¶m_count, Token_Semicolon, true);
|
||||||
close = expect_token(f, Token_CloseBrace);
|
close = expect_token(f, Token_CloseBrace);
|
||||||
|
|
||||||
return make_struct_type(f, token, params, param_count, is_packed);
|
return make_struct_type(f, token, params, param_count, is_packed);
|
||||||
@@ -1683,7 +1701,7 @@ AstNode *parse_identifier_or_type(AstFile *f) {
|
|||||||
AstScope *scope = make_ast_scope(f, NULL); // NOTE(bill): The union needs its own scope with NO parent
|
AstScope *scope = make_ast_scope(f, NULL); // NOTE(bill): The union needs its own scope with NO parent
|
||||||
|
|
||||||
open = expect_token(f, Token_OpenBrace);
|
open = expect_token(f, Token_OpenBrace);
|
||||||
params = parse_parameter_list(f, scope, ¶m_count);
|
params = parse_parameter_list(f, scope, ¶m_count, Token_Semicolon, true);
|
||||||
close = expect_token(f, Token_CloseBrace);
|
close = expect_token(f, Token_CloseBrace);
|
||||||
|
|
||||||
return make_union_type(f, token, params, param_count);
|
return make_union_type(f, token, params, param_count);
|
||||||
@@ -1792,7 +1810,7 @@ Token parse_procedure_signature(AstFile *f, AstScope *scope,
|
|||||||
AstNode **result_list, isize *result_count) {
|
AstNode **result_list, isize *result_count) {
|
||||||
Token proc_token = expect_token(f, Token_proc);
|
Token proc_token = expect_token(f, Token_proc);
|
||||||
expect_token(f, Token_OpenParen);
|
expect_token(f, Token_OpenParen);
|
||||||
*param_list = parse_parameter_list(f, scope, param_count);
|
*param_list = parse_parameter_list(f, scope, param_count, Token_Comma, false);
|
||||||
expect_token(f, Token_CloseParen);
|
expect_token(f, Token_CloseParen);
|
||||||
*result_list = parse_results(f, scope, result_count);
|
*result_list = parse_results(f, scope, result_count);
|
||||||
return proc_token;
|
return proc_token;
|
||||||
|
|||||||
Reference in New Issue
Block a user