Browse Source

`using` on struct/union fields

Ginger Bill 9 years ago
parent
commit
d2c64be85c
11 changed files with 454 additions and 182 deletions
  1. 117 25
      examples/demo.odin
  2. 1 1
      examples/file.odin
  3. 8 8
      examples/game.odin
  4. 6 6
      examples/runtime.odin
  5. 18 18
      examples/win32.odin
  6. 6 4
      src/checker/entity.cpp
  7. 230 84
      src/checker/expr.cpp
  8. 2 0
      src/checker/type.cpp
  9. 36 24
      src/codegen/ssa.cpp
  10. 2 2
      src/common.cpp
  11. 28 10
      src/parser.cpp

+ 117 - 25
examples/demo.odin

@@ -1,26 +1,18 @@
 // Demo 001
 #load "basic.odin"
-#load "math.odin"
 #load "game.odin"
 
 main :: proc() {
-	// _ = hellope();
-	// procedures();
-	// variables();
-	// constants();
-	// types();
-	// data_control();
+	_ = hellope();
+	procedures();
+	variables();
+	constants();
+	types();
+	data_control();
+	using_fields();
 
-	// run_game();
 
-	Thing :: type union {
-		i: i32,
-		f: f32,
-	}
-	t: Thing;
-	t.f = 123;
-	print_int_base(t.i as int, 16);
-	print_nl();
+	// run_game();
 }
 
 hellope :: proc() -> int {
@@ -43,7 +35,7 @@ hellope :: proc() -> int {
 
 apple, banana, carrot: bool;
 box, carboard: bool = true, false;
-hellope_value: int = hellope();
+hellope_value: int = hellope(); // The procedure is ran just before `main`
 
 variables :: proc() {
 	i: int; // initialized with zero value
@@ -293,16 +285,16 @@ types :: proc() {
 	}
 
 	BinaryNode :: type struct {
-		left, right: ^BinaryNode, // same format as procedure argument
-		data: rawptr,
+		left, right: ^BinaryNode; // same format as procedure argument
+		data: rawptr;
 	}
 
 	AddProc :: type proc(a, b: int) -> int
 
 	Packed :: type struct #packed {
-		a: u8,
-		b: u16,
-		c: u32,
+		a: u8;
+		b: u16;
+		c: u32;
 	}
 	static_assert(size_of(Packed) == 7); // builtin procedure
 
@@ -383,6 +375,50 @@ types :: proc() {
 		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
@@ -478,9 +514,9 @@ void main() {
 
 	{ // size, align, offset
 		Thing :: type struct {
-			a: u8,
-			b: u16,
-			c, d, e: u32,
+			a: u8;
+			b: u16;
+			c, d, e: u32;
 		}
 
 		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
examples/file.odin

@@ -3,7 +3,7 @@
 FileHandle :: type HANDLE;
 
 File :: type struct {
-	handle: FileHandle,
+	handle: FileHandle;
 }
 
 file_open :: proc(name: string) -> (File, bool) {

+ 8 - 8
examples/game.odin

@@ -34,12 +34,12 @@ to_c_string :: proc(s: string) -> ^u8 {
 
 
 Window :: type struct {
-	width, height:      int,
-	wc:                 WNDCLASSEXA,
-	dc:                 HDC,
-	hwnd:               HWND,
-	opengl_context, rc: HGLRC,
-	c_title:            ^u8,
+	width, height:      int;
+	wc:                 WNDCLASSEXA;
+	dc:                 HDC;
+	hwnd:               HWND;
+	opengl_context, rc: HGLRC;
+	c_title:            ^u8;
 }
 
 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 {
-	pos: Vec2,
-	dim: Vec2,
+	pos: Vec2;
+	dim: Vec2;
 }
 
 

+ 6 - 6
examples/runtime.odin

@@ -258,18 +258,18 @@ AllocatorProc :: type proc(allocator_data: rawptr, mode: AllocationMode,
                            old_memory: rawptr, old_size: int, flags: u64) -> rawptr;
 
 Allocator :: type struct {
-	procedure: AllocatorProc,
-	data:      rawptr,
+	procedure: AllocatorProc;
+	data:      rawptr;
 }
 
 
 Context :: type struct {
-	thread_id: i32,
+	thread_id: i32;
 
-	user_index: i32,
-	user_data:  rawptr,
+	user_index: i32;
+	user_data:  rawptr;
 
-	allocator: Allocator,
+	allocator: Allocator;
 }
 
 #thread_local context: Context;

+ 18 - 18
examples/win32.odin

@@ -40,24 +40,24 @@ INVALID_HANDLE_VALUE :: (-1 as int) as HANDLE;
 WNDPROC :: type proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT
 
 WNDCLASSEXA :: type struct {
-	size, style:           u32,
-	wnd_proc:              WNDPROC,
-	cls_extra, wnd_extra:  i32,
-	instance:              HINSTANCE,
-	icon:                  HICON,
-	cursor:                HCURSOR,
-	background:            HBRUSH,
-	menu_name, class_name: ^u8,
-	sm:                    HICON,
+	size, style:           u32;
+	wnd_proc:              WNDPROC;
+	cls_extra, wnd_extra:  i32;
+	instance:              HINSTANCE;
+	icon:                  HICON;
+	cursor:                HCURSOR;
+	background:            HBRUSH;
+	menu_name, class_name: ^u8;
+	sm:                    HICON;
 }
 
 MSG :: type struct {
-	hwnd:    HWND,
-	message: u32,
-	wparam:  WPARAM,
-	lparam:  LPARAM,
-	time:    u32,
-	pt:      POINT,
+	hwnd:    HWND;
+	message: u32;
+	wparam:  WPARAM;
+	lparam:  LPARAM;
+	time:    u32;
+	pt:      POINT;
 }
 
 
@@ -189,7 +189,7 @@ wglCreateContextAttribsARBType :: type proc(hdc: HDC, hshareContext: rawptr, att
 PIXELFORMATDESCRIPTOR :: type struct  {
 	size,
 	version,
-	flags: u32,
+	flags: u32;
 
 	pixel_type,
 	color_bits,
@@ -210,11 +210,11 @@ PIXELFORMATDESCRIPTOR :: type struct  {
 	stencil_bits,
 	aux_buffers,
 	layer_type,
-	reserved: byte,
+	reserved: byte;
 
 	layer_mask,
 	visible_mask,
-	damage_mask: u32,
+	damage_mask: u32;
 }
 
 GetDC             :: proc(h: HANDLE) -> HDC #foreign

+ 6 - 4
src/checker/entity.cpp

@@ -38,9 +38,10 @@ struct Entity {
 	union {
 		struct { ExactValue value; } Constant;
 		struct {
-			b8 visited;
-			b8 is_field;
-			b8 used;
+			b8 visited;   // Cycle detection
+			b8 is_field;  // Is struct field
+			b8 used;      // Variable is used
+			b8 anonymous; // Variable is an anonymous struct field
 		} Variable;
 		struct { b8 used; } Procedure;
 		struct { BuiltinProcId id; } Builtin;
@@ -85,9 +86,10 @@ Entity *make_entity_param(gbAllocator a, Scope *parent, Token token, Type *type)
 	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->Variable.is_field  = true;
+	entity->Variable.anonymous = cast(b8)is_anonymous;
 	return entity;
 }
 

+ 230 - 84
src/checker/expr.cpp

@@ -13,45 +13,101 @@ 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           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));
 
-void check_struct_type(Checker *c, Type *struct_type, AstNode *node) {
-	GB_ASSERT(node->kind == AstNode_StructType);
-	GB_ASSERT(struct_type->kind == Type_Struct);
-	ast_node(st, StructType, node);
+	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_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++;
-		}
-	}
-
-	Entity **fields = gb_alloc_array(c->allocator, Entity *, st->field_count);
 	isize field_index = 0;
-	for (AstNode *field = st->field_list; field != NULL; field = field->next) {
+	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;
-			// TODO(bill): is the curr_scope correct?
-			Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type);
+
+			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)) {
+			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));
+				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) {
+	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++;
+		}
 	}
+
+	Entity **fields = gb_alloc_array(c->allocator, Entity *, st->field_count);
+	check_fields(c, node, st->field_list, fields, field_count, make_string("structure"));
 	struct_type->Struct.fields = fields;
 	struct_type->Struct.field_count = field_count;
 	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);
 	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;
 	for (AstNode *field = ut->field_list; field != NULL; field = field->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);
-	isize field_index = 0;
-	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);
-		}
-	}
+	check_fields(c, node, ut->field_list, fields, field_count, make_string("union"));
 	union_type->Union.fields = fields;
 	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;
 }
 
-Entity *lookup_field(Type *type, AstNode *field_node, isize *index = NULL) {
-	GB_ASSERT(type != NULL);
-	GB_ASSERT(field_node->kind == AstNode_Ident);
+struct Selection {
+	Entity *entity;
+	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);
-	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) {
 	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_str, str)) {
-				if (index) *index = i;
-				return f;
+			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;
+					return sel;
+				}
+				gb_array_count(sel.index) = prev_count;
 			}
 		}
 		break;
@@ -1431,7 +1504,66 @@ Entity *lookup_field(Type *type, AstNode *field_node, isize *index = NULL) {
 			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_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;
 				return f;
 			}
@@ -1443,7 +1575,7 @@ Entity *lookup_field(Type *type, AstNode *field_node, isize *index = NULL) {
 			Entity *f = type->Enum.fields[i];
 			GB_ASSERT(f->kind == Entity_Constant);
 			String str = f->token.string;
-			if (are_strings_equal(field_str, str)) {
+			if (are_strings_equal(field_name, str)) {
 				if (index) *index = i;
 				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?
 	}
 
-	return NULL;
+*/
 }
 
 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;
 		if (is_type_enum(operand->type)) {
 			if (operand->mode == Addressing_Type) {
-				entity = lookup_field(operand->type, selector);
+				entity = lookup_field(operand->type, selector->Ident.token.string).entity;
 			}
 		} else {
-			entity = lookup_field(operand->type, selector);
+			entity = lookup_field(operand->type, selector->Ident.token.string).entity;
 		}
 		if (entity == NULL) {
 			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);
-		if (entity == NULL) {
-			ast_node(arg, Ident, field_arg);
+
+		ast_node(arg, Ident, field_arg);
+		Selection sel = lookup_field(type, arg->token.string);
+		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));
@@ -1607,7 +1739,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		}
 
 		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;
 	} break;
 
@@ -1632,10 +1765,10 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 				type = p->Pointer.elem;
 		}
 
-		isize index = 0;
-		Entity *entity = lookup_field(type, s->selector, &index);
-		if (entity == NULL) {
-			ast_node(i, Ident, s->selector);
+
+		ast_node(i, Ident, s->selector);
+		Selection sel = lookup_field(type, i->token.string);
+		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));
@@ -1643,7 +1776,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		}
 
 		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;
 	} break;
 
@@ -2097,23 +2231,29 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ
 						}
 						String name = kv->field->Ident.token.string;
 
-						isize index = 0;
-						Entity *e = lookup_field(type, kv->field, &index);
-						if (e == NULL) {
+						Selection sel = lookup_field(type, kv->field->Ident.token.string);
+						if (sel.entity == NULL) {
 							error(&c->error_collector, ast_node_token(elem),
 							      "Unknown field `%.*s` in structure literal", LIT(name));
 							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);
 
-						if (fields_visited[index]) {
+						if (fields_visited[sel.index[0]]) {
 							error(&c->error_collector, ast_node_token(elem),
 							      "Duplicate field `%.*s` in structure literal", LIT(name));
 							continue;
 						}
 
-						fields_visited[index] = true;
+						fields_visited[sel.index[0]] = true;
 						check_expr(c, o, kv->value);
 						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)
 			str = gb_string_appendc(str, sep);
 
-		isize j = 0;
-		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);
-
+		str = write_expr_to_string(str, field);
 		i++;
 	}
 	return str;
@@ -2674,6 +2804,22 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 		str = write_expr_to_string(str, vt->elem);
 	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);
 		str = write_expr_to_string(str, ce->proc);
 		str = gb_string_appendc(str, "(");

+ 2 - 0
src/checker/type.cpp

@@ -112,6 +112,8 @@ struct Type {
 			b32      is_packed;
 		} Struct;
 		struct {
+			// IMPORTANT HACK(bill): The positions of fields and field_count
+			// must be same for Struct and Union
 			Entity **fields; // Entity_Variable
 			isize    field_count;
 		} Union;

+ 36 - 24
src/codegen/ssa.cpp

@@ -1606,7 +1606,8 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 
 					if (elem->kind == AstNode_FieldValue) {
 						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);
 					} else {
 						field_expr = ssa_build_expr(proc, elem);
@@ -1874,6 +1875,36 @@ ssaValue *ssa_build_expr(ssaProcedure *proc, AstNode *expr) {
 	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) {
 	switch (expr->kind) {
@@ -1901,31 +1932,12 @@ 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));
 
-		isize field_index = 0;
-		Entity *entity = lookup_field(type, unparen_expr(se->selector), &field_index);
-		GB_ASSERT(entity != NULL);
+		Selection sel = lookup_field(type, unparen_expr(se->selector)->Ident.token.string);
+		GB_ASSERT(sel.entity != NULL);
 
 		ssaValue *e = ssa_build_addr(proc, se->expr).addr;
-
-		if (is_type_pointer(type)) {
-			// 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);
-		}
+		e = ssa_emit_deep_field_gep(proc, type, e, sel);
+		return ssa_make_addr(e, expr);
 	case_end;
 
 	case_ast_node(ue, UnaryExpr, expr);

+ 2 - 2
src/common.cpp

@@ -14,8 +14,8 @@ struct HashKey {
 
 gb_inline HashKey hashing_proc(void const *data, isize len) {
 	HashKey h = {};
-	h.key = gb_murmur64(data, len);
-	// h.key = gb_fnv64a(data, len);
+	// h.key = gb_murmur64(data, len);
+	h.key = gb_fnv64a(data, len);
 	return h;
 }
 

+ 28 - 10
src/parser.cpp

@@ -197,6 +197,7 @@ AST_NODE_KIND(_TypeBegin, struct{}) \
 		AstNode *name_list; \
 		isize name_count; \
 		AstNode *type; \
+		b32 is_using; \
 	}) \
 	AST_NODE_KIND(ProcType, struct { \
 		Token token;          \
@@ -725,11 +726,12 @@ gb_inline AstNode *make_var_decl(AstFile *f, DeclKind kind, AstNode *name_list,
 	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);
 	result->Field.name_list = name_list;
 	result->Field.name_count = name_count;
 	result->Field.type = type;
+	result->Field.is_using = is_using;
 	return result;
 }
 
@@ -1566,20 +1568,32 @@ AstNode *parse_type(AstFile *f) {
 	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;
 	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);
 	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");
+	}
+
+
 	expect_token(f, Token_Colon);
 
 	AstNode *type = parse_type_attempt(f);
 	if (type == NULL)
 		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);
 	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_curr = NULL;
 	isize param_count = 0;
-	while (f->cursor[0].kind == Token_Identifier) {
-		AstNode *field = parse_field_decl(f, scope);
+	while (f->cursor[0].kind == Token_Identifier ||
+	       (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);
 		param_count += field->Field.name_count;
-		if (f->cursor[0].kind != Token_Comma)
+		if (f->cursor[0].kind != separator)
 			break;
 		next_token(f);
 	}
@@ -1669,7 +1687,7 @@ AstNode *parse_identifier_or_type(AstFile *f) {
 		}
 
 		open   = expect_token(f, Token_OpenBrace);
-		params = parse_parameter_list(f, scope, &param_count);
+		params = parse_parameter_list(f, scope, &param_count, Token_Semicolon, true);
 		close  = expect_token(f, Token_CloseBrace);
 
 		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
 
 		open   = expect_token(f, Token_OpenBrace);
-		params = parse_parameter_list(f, scope, &param_count);
+		params = parse_parameter_list(f, scope, &param_count, Token_Semicolon, true);
 		close  = expect_token(f, Token_CloseBrace);
 
 		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) {
 	Token proc_token = expect_token(f, Token_proc);
 	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);
 	*result_list = parse_results(f, scope, result_count);
 	return proc_token;