Browse Source

Begin "Everything's a namespace"

Ginger Bill 9 years ago
parent
commit
0eaf7bd830

+ 1 - 1
examples/basic.odin

@@ -3,7 +3,7 @@
 #load "file.odin"
 #load "file.odin"
 
 
 print_string :: proc(s: string) {
 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) {
 byte_reverse :: proc(b: []byte) {

+ 29 - 5
examples/demo.odin

@@ -32,6 +32,30 @@ main :: proc() {
 	d := ptr_sub(y, ptr_offset(x, 1));
 	d := ptr_sub(y, ptr_offset(x, 1));
 	print_int(d); nl();
 	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();
 	// run_game();
@@ -301,7 +325,7 @@ types :: proc() {
 	Array3Int :: type [3]int;
 	Array3Int :: type [3]int;
 
 
 	Vec3 :: type struct {
 	Vec3 :: type struct {
-		x, y, z: f32
+		x, y, z: f32;
 	}
 	}
 
 
 	BinaryNode :: type struct {
 	BinaryNode :: type struct {
@@ -430,8 +454,8 @@ types :: proc() {
 				variable: struct{
 				variable: struct{
 					visited, is_field, used, anonymous: bool;
 					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();
 		context.allocator = __default_allocator();
 		defer context.allocator = prev_allocator;
 		defer context.allocator = prev_allocator;
 
 
-		File :: type struct { filename: string };
+		File :: type struct { filename: string; };
 		FileError :: type int;
 		FileError :: type int;
 		open_file  :: proc(filename: string) -> (File, FileError) {
 		open_file  :: proc(filename: string) -> (File, FileError) {
 			return File{}, 0;
 			return File{}, 0;
@@ -717,7 +741,7 @@ using_fields :: proc() {
 
 
 	{ // Crazy Shit
 	{ // Crazy Shit
 		Vec2 :: type union {
 		Vec2 :: type union {
-			using _xy: struct {x, y: f32};
+			using _xy: struct { x, y: f32; };
 			e: [2]f32;
 			e: [2]f32;
 			v: {2}f32;
 			v: {2}f32;
 		}
 		}

+ 13 - 13
examples/file.odin

@@ -1,18 +1,16 @@
 #load "win32.odin"
 #load "win32.odin"
 
 
-FileHandle :: type HANDLE;
-
 File :: type struct {
 File :: type struct {
-	handle: FileHandle;
+	Handle :: type HANDLE;
+	handle: Handle;
 }
 }
 
 
 file_open :: proc(name: string) -> (File, bool) {
 file_open :: proc(name: string) -> (File, bool) {
 	buf: [300]byte;
 	buf: [300]byte;
 	_ = copy(buf[:], name as []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;
 	return f, success;
 }
 }
 
 
@@ -22,7 +20,7 @@ file_create :: proc(name: string) -> (File, bool) {
 	f := File{
 	f := File{
 		handle = CreateFileA(^buf[0], FILE_GENERIC_WRITE, FILE_SHARE_READ, null, CREATE_ALWAYS, 0, null),
 		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;
 	return f, success;
 }
 }
 
 
@@ -31,9 +29,9 @@ file_close :: proc(f: ^File) {
 	CloseHandle(f.handle);
 	CloseHandle(f.handle);
 }
 }
 
 
-file_write :: proc(f: ^File, buf: rawptr, len: int) -> bool {
+file_write :: proc(f: ^File, buf: []byte) -> bool {
 	bytes_written: i32;
 	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 {
 FileStandard :: type enum {
@@ -47,10 +45,12 @@ __std_file_set := false;
 __std_files: [FileStandard.COUNT as int]File;
 __std_files: [FileStandard.COUNT as int]File;
 
 
 file_get_standard :: proc(std: FileStandard) -> ^File {
 file_get_standard :: proc(std: FileStandard) -> ^File {
+	// using FileStandard;
 	if (!__std_file_set) {
 	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;
 		__std_file_set = true;
 	}
 	}
 	return ^__std_files[std];
 	return ^__std_files[std];

+ 1 - 1
examples/runtime.odin

@@ -98,7 +98,7 @@ memory_copy :: proc(dst, src: rawptr, n: int) #inline {
 		w = (s as ^u32)^;
 		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);
 		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 {
 		for n > 17 {
 			d32 := d as ^u32;
 			d32 := d as ^u32;

+ 1 - 1
examples/win32.odin

@@ -33,7 +33,7 @@ LPARAM    :: type int;
 LRESULT   :: type int;
 LRESULT   :: type int;
 ATOM      :: type i16;
 ATOM      :: type i16;
 BOOL      :: type i32;
 BOOL      :: type i32;
-POINT     :: type struct { x, y: i32 };
+POINT     :: type struct { x, y: i32; };
 
 
 INVALID_HANDLE_VALUE :: (-1 as int) as HANDLE;
 INVALID_HANDLE_VALUE :: (-1 as int) as HANDLE;
 
 

+ 34 - 15
src/checker/checker.cpp

@@ -2,19 +2,29 @@
 #include "entity.cpp"
 #include "entity.cpp"
 #include "type.cpp"
 #include "type.cpp"
 
 
-enum AddressingMode {
-	Addressing_Invalid,
+#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), \
 
 
-	Addressing_NoValue,
-	Addressing_Value,
-	Addressing_Variable,
-	Addressing_Constant,
-	Addressing_Type,
-	Addressing_Builtin,
+enum AddressingMode {
+#define ADDRESSING_MODE(x) GB_JOIN2(Addressing_, x)
+	ADDRESSING_KINDS
+#undef ADDRESSING_MODE
+};
 
 
-	Addressing_Count,
+String const addressing_mode_strings[] = {
+#define ADDRESSING_MODE(x) {cast(u8 *)#x, gb_size_of(#x)-1}
+	ADDRESSING_KINDS
+#undef ADDRESSING_MODE
 };
 };
 
 
+
 struct Operand {
 struct Operand {
 	AddressingMode mode;
 	AddressingMode mode;
 	Type *type;
 	Type *type;
@@ -261,7 +271,7 @@ void add_scope(Checker *c, AstNode *node, Scope *scope) {
 
 
 
 
 void check_open_scope(Checker *c, AstNode *stmt) {
 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);
 	Scope *scope = make_scope(c->context.scope, c->allocator);
 	add_scope(c, stmt, scope);
 	add_scope(c, stmt, scope);
 	if (stmt->kind == AstNode_ProcType) {
 	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("_"))) {
 	if (!are_strings_equal(entity->token.string, make_string("_"))) {
 		Entity *insert_entity = scope_insert_entity(scope, entity);
 		Entity *insert_entity = scope_insert_entity(scope, entity);
 		if (insert_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;
 			return;
 		}
 		}
 	}
 	}

+ 1 - 0
src/checker/entity.cpp

@@ -34,6 +34,7 @@ struct Entity {
 	Scope *scope;
 	Scope *scope;
 	Token token;
 	Token token;
 	Type *type;
 	Type *type;
+	Entity *using_parent;
 
 
 	union {
 	union {
 		struct { ExactValue value; } Constant;
 		struct { ExactValue value; } Constant;

+ 223 - 108
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);
 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);
 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);
 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_selector            (Checker *c, Operand *operand, AstNode *node);
 void     check_not_tuple           (Checker *c, Operand *operand);
 void     check_not_tuple           (Checker *c, Operand *operand);
 b32      check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value);
 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) {
 void check_struct_type(Checker *c, Type *struct_type, AstNode *node, CycleChecker *cycle_checker) {
 	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);
 
 
 	isize field_count = 0;
 	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 *, field_count);
+
+	Map<Entity *> 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);
 		}
 		}
+
 	}
 	}
 
 
-	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;
+	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) {
 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"));
 	check_fields(c, node, ut->field_list, fields, field_count, cycle_checker, make_string("union"));
 	union_type->Union.fields = fields;
 	union_type->Union.fields = fields;
 	union_type->Union.field_count = field_count;
 	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);
 	case_ast_node(se, SelectorExpr, e);
 		Operand o = {};
 		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);
 		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) {
 		if (o.mode == Addressing_Type) {
 			set_base_type(type, o.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);
 	case_ast_node(st, StructType, e);
 		type = make_type_struct(c->allocator);
 		type = make_type_struct(c->allocator);
 		set_base_type(named_type, type);
 		set_base_type(named_type, type);
+		check_open_scope(c, e);
 		check_struct_type(c, type, e, cycle_checker);
 		check_struct_type(c, type, e, cycle_checker);
+		check_close_scope(c);
 		goto end;
 		goto end;
 	case_end;
 	case_end;
 
 
 	case_ast_node(st, UnionType, e);
 	case_ast_node(st, UnionType, e);
 		type = make_type_union(c->allocator);
 		type = make_type_union(c->allocator);
 		set_base_type(named_type, type);
 		set_base_type(named_type, type);
+		check_open_scope(c, e);
 		check_union_type(c, type, e, cycle_checker);
 		check_union_type(c, type, e, cycle_checker);
+		check_close_scope(c);
 		goto end;
 		goto end;
 	case_end;
 	case_end;
 
 
@@ -1493,7 +1646,7 @@ void selection_add_index(Selection *s, isize index) {
 	gb_array_append(s->index, 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);
 	GB_ASSERT(type_ != NULL);
 
 
 	if (are_strings_equal(field_name, make_string("_"))) {
 	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) {
 	switch (type->kind) {
 	case Type_Struct:
 	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 (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;
+				}
 			}
 			}
-
-			if (f->Variable.anonymous) {
-				isize prev_count = 0;
-				if (sel.index != NULL) {
-					prev_count = gb_array_count(sel.index);
+		} 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;
 				}
 				}
-				selection_add_index(&sel, i); // HACK(bill): Leaky memory
 
 
-				sel = lookup_field(f->type, field_name, 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
 
 
-				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;
+					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;
 				}
 				}
-				gb_array_count(sel.index) = prev_count;
 			}
 			}
 		}
 		}
 		break;
 		break;
@@ -1553,7 +1719,7 @@ Selection lookup_field(Type *type_, String field_name, Selection sel = empty_sel
 			String str = f->token.string;
 			String str = f->token.string;
 			if (f->Variable.anonymous) {
 			if (f->Variable.anonymous) {
 				selection_add_index(&sel, i); // HACK(bill): Leaky memory
 				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)) {
 				if (sel.entity != NULL && is_type_pointer(f->type)) {
 					sel.indirect = true;
 					sel.indirect = true;
 				}
 				}
@@ -1563,67 +1729,21 @@ Selection lookup_field(Type *type_, String field_name, Selection sel = empty_sel
 		break;
 		break;
 
 
 	case Type_Enum:
 	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;
 		break;
 	}
 	}
 
 
 	return sel;
 	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) {
 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 *op_expr  = se->expr;
 	AstNode *selector = se->selector;
 	AstNode *selector = se->selector;
 	if (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) {
 		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(op_str));
+			defer (gb_string_free(type_str));
 			defer (gb_string_free(sel_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->mode = Addressing_Invalid;
 			operand->expr = node;
 			operand->expr = node;
 			return;
 			return;
 		}
 		}
 		add_entity_use(&c->info, selector, entity);
 		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->mode = Addressing_Constant;
 			operand->value = entity->Constant.value;
 			operand->value = entity->Constant.value;
+		} else if (entity->kind == Entity_TypeName) {
+			operand->mode = Addressing_Type;
 		} else {
 		} else {
-			operand->type = entity->type;
-			operand->expr = node;
 			if (operand->mode != Addressing_Variable)
 			if (operand->mode != Addressing_Variable)
 				operand->mode = Addressing_Value;
 				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);
 		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) {
 		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),
@@ -1878,7 +1993,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 
 
 
 
 		ast_node(i, Ident, s->selector);
 		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) {
 		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),
@@ -2504,7 +2619,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 						}
 						}
 						String name = kv->field->Ident.token.string;
 						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) {
 						if (sel.entity == 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));
@@ -3121,7 +3236,7 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 
 
 	case_ast_node(st, StructType, node);
 	case_ast_node(st, StructType, node);
 		str = gb_string_appendc(str, "struct{");
 		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, "}");
 		str = gb_string_appendc(str, "}");
 	case_end;
 	case_end;
 
 

+ 69 - 0
src/checker/stmt.cpp

@@ -685,6 +685,75 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		}
 		}
 	case_end;
 	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);
 	case_ast_node(vd, VarDecl, node);
 		isize entity_count = vd->name_count;
 		isize entity_count = vd->name_count;
 		isize entity_index = 0;
 		isize entity_index = 0;

+ 3 - 0
src/checker/type.cpp

@@ -110,6 +110,9 @@ struct Type {
 			i64 *    offsets;
 			i64 *    offsets;
 			b32      are_offsets_set;
 			b32      are_offsets_set;
 			b32      is_packed;
 			b32      is_packed;
+
+			Entity **other_fields; // Entity_Constant or Entity_TypeName
+			isize    other_field_count;
 		} Struct;
 		} Struct;
 		struct {
 		struct {
 			// IMPORTANT HACK(bill): The positions of fields and field_count
 			// IMPORTANT HACK(bill): The positions of fields and field_count

+ 3 - 5
src/codegen/codegen.cpp

@@ -70,11 +70,9 @@ void ssa_gen_code(ssaGen *s) {
 		String name = e->token.string;
 		String name = e->token.string;
 
 
 		switch (e->kind) {
 		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: {
 		case Entity_Variable: {
 			ssaValue *g = ssa_make_value_global(a, e, NULL);
 			ssaValue *g = ssa_make_value_global(a, e, NULL);

+ 11 - 14
src/codegen/print_llvm.cpp

@@ -169,7 +169,11 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) {
 		ssa_fprintf(f, "*");
 		ssa_fprintf(f, "*");
 		break;
 		break;
 	case Type_Named:
 	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;
 		break;
 	case Type_Tuple:
 	case Type_Tuple:
 		if (t->Tuple.variable_count == 1) {
 		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) {
 void ssa_print_type_name(gbFile *f, ssaModule *m, ssaValue *v) {
 	GB_ASSERT(v->kind == ssaValue_TypeName);
 	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_print_encoded_local(f, v->TypeName.name);
 	ssa_fprintf(f, " = type ");
 	ssa_fprintf(f, " = type ");
 	ssa_print_type(f, m->sizes, get_base_type(v->TypeName.type));
 	ssa_print_type(f, m->sizes, get_base_type(v->TypeName.type));
 	ssa_fprintf(f, "\n");
 	ssa_fprintf(f, "\n");
 }
 }
 
 
-
 void ssa_print_llvm_ir(gbFile *f, ssaModule *m) {
 void ssa_print_llvm_ir(gbFile *f, ssaModule *m) {
 	if (m->layout.len > 0) {
 	if (m->layout.len > 0) {
 		ssa_fprintf(f, "target datalayout = \"%.*s\"\n", LIT(m->layout));
 		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];
 		auto *entry = &m->members.entries[member_index];
 		ssaValue *v = entry->value;
 		ssaValue *v = entry->value;
 		switch (v->kind) {
 		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) {
 	gb_for_array(member_index, m->members.entries) {
 		auto *entry = &m->members.entries[member_index];
 		auto *entry = &m->members.entries[member_index];
 		ssaValue *v = entry->value;
 		ssaValue *v = entry->value;

+ 34 - 8
src/codegen/ssa.cpp

@@ -14,7 +14,6 @@ struct ssaModule {
 
 
 	Map<ssaValue *> values;  // Key: Entity *
 	Map<ssaValue *> values;  // Key: Entity *
 	Map<ssaValue *> members; // Key: String
 	Map<ssaValue *> members; // Key: String
-	gbArray(ssaValue *) nested_type_names; // ssaValue_TypeName
 	i32 global_string_index;
 	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->values,  m->allocator);
 	map_init(&m->members, m->allocator);
 	map_init(&m->members, m->allocator);
-	gb_array_init(m->nested_type_names, m->allocator);
 }
 }
 
 
 void ssa_module_destroy(ssaModule *m) {
 void ssa_module_destroy(ssaModule *m) {
 	map_destroy(&m->values);
 	map_destroy(&m->values);
 	map_destroy(&m->members);
 	map_destroy(&m->members);
-	gb_array_free(m->nested_type_names);
 	gb_arena_free(&m->arena);
 	gb_arena_free(&m->arena);
 }
 }
 
 
@@ -1606,7 +1603,7 @@ 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);
-						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_index = sel.index[0];
 						field_expr = ssa_build_expr(proc, kv->value);
 						field_expr = ssa_build_expr(proc, kv->value);
 					} else {
 					} else {
@@ -2063,7 +2060,7 @@ 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));
 
 
-		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);
 		GB_ASSERT(sel.entity != NULL);
 
 
 		ssaValue *e = ssa_build_addr(proc, se->expr).addr;
 		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) {
 void ssa_build_stmt_list(ssaProcedure *proc, AstNode *list) {
 	for (AstNode *stmt = list ; stmt != NULL; stmt = stmt->next)
 	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;
 		String td_name = td->name->Ident.token.string;
 		isize name_len = proc->name.len + 1 + td_name.len + 1 + 10 + 1;
 		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);
 		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);
 		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);
 		String name = make_string(name_text, name_len-1);
 
 
@@ -2400,8 +2427,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 		                                           name, e->type);
 		                                           name, e->type);
 		// HACK(bill): Override name of type so printer prints it correctly
 		// HACK(bill): Override name of type so printer prints it correctly
 		e->type->Named.name = name;
 		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_end;
 
 
 	case_ast_node(ids, IncDecStmt, node);
 	case_ast_node(ids, IncDecStmt, node);

+ 151 - 28
src/parser.cpp

@@ -169,6 +169,7 @@ AST_NODE_KIND(_ComplexStmtBegin, struct{}) \
 	}) \
 	}) \
 	AST_NODE_KIND(DeferStmt,  struct { Token token; AstNode *stmt; }) \
 	AST_NODE_KIND(DeferStmt,  struct { Token token; AstNode *stmt; }) \
 	AST_NODE_KIND(BranchStmt, struct { Token token; }) \
 	AST_NODE_KIND(BranchStmt, struct { Token token; }) \
+	AST_NODE_KIND(UsingStmt,  struct { Token token; AstNode *node; }) \
 \
 \
 AST_NODE_KIND(_ComplexStmtEnd, struct{}) \
 AST_NODE_KIND(_ComplexStmtEnd, struct{}) \
 AST_NODE_KIND(_StmtEnd,        struct{}) \
 AST_NODE_KIND(_StmtEnd,        struct{}) \
@@ -177,6 +178,7 @@ AST_NODE_KIND(_DeclBegin,      struct{}) \
 	AST_NODE_KIND(VarDecl, struct { \
 	AST_NODE_KIND(VarDecl, struct { \
 			DeclKind kind; \
 			DeclKind kind; \
 			u32      tags; \
 			u32      tags; \
+			b32      is_using; \
 			AstNode *name_list; \
 			AstNode *name_list; \
 			AstNode *type; \
 			AstNode *type; \
 			AstNode *value_list; \
 			AstNode *value_list; \
@@ -222,8 +224,8 @@ AST_NODE_KIND(_TypeBegin, struct{}) \
 	}) \
 	}) \
 	AST_NODE_KIND(StructType, struct { \
 	AST_NODE_KIND(StructType, struct { \
 		Token token; \
 		Token token; \
-		AstNode *field_list; \
-		isize field_count; \
+		AstNode *decl_list; \
+		isize decl_count; \
 		b32 is_packed; \
 		b32 is_packed; \
 	}) \
 	}) \
 	AST_NODE_KIND(UnionType, struct { \
 	AST_NODE_KIND(UnionType, struct { \
@@ -354,6 +356,8 @@ Token ast_node_token(AstNode *node) {
 		return node->DeferStmt.token;
 		return node->DeferStmt.token;
 	case AstNode_BranchStmt:
 	case AstNode_BranchStmt:
 		return node->BranchStmt.token;
 		return node->BranchStmt.token;
+	case AstNode_UsingStmt:
+		return node->UsingStmt.token;
 	case AstNode_BadDecl:
 	case AstNode_BadDecl:
 		return node->BadDecl.begin;
 		return node->BadDecl.begin;
 	case AstNode_VarDecl:
 	case AstNode_VarDecl:
@@ -707,6 +711,13 @@ gb_inline AstNode *make_branch_stmt(AstFile *f, Token token) {
 	return result;
 	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) {
 gb_inline AstNode *make_bad_decl(AstFile *f, Token begin, Token end) {
 	AstNode *result = make_node(f, AstNode_BadDecl);
 	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;
 	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);
 	AstNode *result = make_node(f, AstNode_StructType);
 	result->StructType.token = token;
 	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;
 	result->StructType.is_packed = is_packed;
 	return result;
 	return result;
 }
 }
@@ -938,6 +949,32 @@ void fix_advance_to_next_stmt(AstFile *f) {
 #endif
 #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);
 AstNode *parse_expr(AstFile *f, b32 lhs);
@@ -1211,6 +1248,7 @@ b32 is_literal_type(AstNode *node) {
 	switch (node->kind) {
 	switch (node->kind) {
 	case AstNode_BadExpr:
 	case AstNode_BadExpr:
 	case AstNode_Ident:
 	case AstNode_Ident:
+	case AstNode_SelectorExpr:
 	case AstNode_ArrayType:
 	case AstNode_ArrayType:
 	case AstNode_VectorType:
 	case AstNode_VectorType:
 	case AstNode_StructType:
 	case AstNode_StructType:
@@ -1652,10 +1690,19 @@ AstNode *parse_parameter_list(AstFile *f, AstScope *scope, isize *param_count_,
 	return param_list;
 	return param_list;
 }
 }
 
 
+
 AstNode *parse_identifier_or_type(AstFile *f) {
 AstNode *parse_identifier_or_type(AstFile *f) {
 	switch (f->cursor[0].kind) {
 	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:
 	case Token_Pointer:
 		return make_pointer_type(f, expect_token(f, Token_Pointer), parse_type(f));
 		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: {
 	case Token_struct: {
 		Token token = expect_token(f, 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;
 		b32 is_packed = false;
 		if (allow_token(f, Token_Hash)) {
 		if (allow_token(f, Token_Hash)) {
 			Token tag = expect_token(f, Token_Identifier);
 			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, &param_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, &param_count, Token_Semicolon, true);
+
+		return make_struct_type(f, token, decls, decl_count, is_packed);
+	} break;
 
 
 	case Token_union: {
 	case Token_union: {
 		Token token = expect_token(f, 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);
 	return make_defer_stmt(f, token, statement);
 }
 }
 
 
+
 AstNode *parse_stmt(AstFile *f) {
 AstNode *parse_stmt(AstFile *f) {
 	AstNode *s = NULL;
 	AstNode *s = NULL;
 	Token token = f->cursor[0];
 	Token token = f->cursor[0];
@@ -2117,18 +2221,7 @@ AstNode *parse_stmt(AstFile *f) {
 	case Token_Xor:
 	case Token_Xor:
 	case Token_Not:
 	case Token_Not:
 		s = parse_simple_stmt(f);
 		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;
 		return s;
 
 
 	// TODO(bill): other keywords
 	// TODO(bill): other keywords
@@ -2146,6 +2239,36 @@ AstNode *parse_stmt(AstFile *f) {
 		expect_token(f, Token_Semicolon);
 		expect_token(f, Token_Semicolon);
 		return make_branch_stmt(f, token);
 		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: {
 	case Token_Hash: {
 		s = parse_tag_stmt(f, NULL);
 		s = parse_tag_stmt(f, NULL);
 
 

+ 1 - 1
src/printer.cpp

@@ -180,7 +180,7 @@ void print_ast(AstNode *node, isize indent) {
 	case AstNode_StructType:
 	case AstNode_StructType:
 		print_indent(indent);
 		print_indent(indent);
 		gb_printf("(struct)\n");
 		gb_printf("(struct)\n");
-		print_ast(node->StructType.field_list, indent+1);
+		print_ast(node->StructType.decl_list, indent+1);
 		break;
 		break;
 	}
 	}