Browse Source

Nested record declarations

Ginger Bill 8 years ago
parent
commit
ce4b7b8b7d
7 changed files with 221 additions and 91 deletions
  1. 3 0
      core/_preload.odin
  2. 84 64
      src/check_expr.cpp
  3. 34 2
      src/check_stmt.cpp
  4. 42 6
      src/checker.cpp
  5. 17 0
      src/ir.cpp
  6. 26 19
      src/parser.cpp
  7. 15 0
      src/types.cpp

+ 3 - 0
core/_preload.odin

@@ -34,6 +34,8 @@ CallingConvention :: enum {
 }
 // IMPORTANT NOTE(bill): Do not change the order of any of this data
 // The compiler relies upon this _exact_ order
+
+
 TypeInfoEnumValue :: raw_union {
 	f: f64;
 	i: i128;
@@ -49,6 +51,7 @@ TypeInfoRecord :: struct #ordered {
 	custom_align: bool;
 }
 
+
 TypeInfo :: union {
 	size:  int;
 	align: int;

+ 84 - 64
src/check_expr.cpp

@@ -692,14 +692,16 @@ void populate_using_entity_map(Checker *c, AstNode *node, Type *t, Map<Entity *>
 
 
 // Returns filled field_count
-isize check_fields(Checker *c, AstNode *node, Array<AstNode *> decls,
-                   Entity **fields, isize field_count,
-                   String context) {
+Array<Entity *> check_fields(Checker *c, AstNode *node, Array<AstNode *> decls,
+                             isize init_field_capacity, String context) {
 	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
 	defer (gb_temp_arena_memory_end(tmp));
 
+	Array<Entity *> fields = {};
+	array_init(&fields, heap_allocator(), init_field_capacity);
+
 	Map<Entity *> entity_map = {};
-	map_init_with_reserve(&entity_map, c->tmp_allocator, 2*field_count);
+	map_init_with_reserve(&entity_map, c->tmp_allocator, 2*init_field_capacity);
 
 	Entity *using_index_expr = nullptr;
 
@@ -707,14 +709,33 @@ isize check_fields(Checker *c, AstNode *node, Array<AstNode *> decls,
 		GB_ASSERT(node->kind != AstNode_UnionType);
 	}
 
-	isize field_index = 0;
+	check_collect_entities(c, decls, false);
+	for_array(i, c->context.scope->elements.entries) {
+		Entity *e = c->context.scope->elements.entries[i].value;
+		if (e->token.string == "EnumValue") {
+			// gb_printf_err("EnumValue\n");
+		}
+		DeclInfo *d = nullptr;
+		switch (e->kind) {
+		default: continue;
+		case Entity_Constant:
+		case Entity_TypeName:
+			d = decl_info_of_entity(&c->info, e);
+			if (d != nullptr) {
+				check_entity_decl(c, e, d, nullptr);
+			}
+			break;
+		}
+	}
+
 	for_array(decl_index, decls) {
 		AstNode *decl = decls[decl_index];
-		if (decl->kind != AstNode_ValueDecl) {
-			continue;
-		}
+		if (decl->kind != AstNode_ValueDecl) continue;
+
 		ast_node(vd, ValueDecl, decl);
 
+		if (!vd->is_mutable) continue;
+
 		Type *type = nullptr;
 		if (vd->type != nullptr) {
 			type = check_type(c, vd->type);
@@ -746,10 +767,10 @@ isize check_fields(Checker *c, AstNode *node, Array<AstNode *> decls,
 
 			Token name_token = name->Ident.token;
 
-			Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, is_using, cast(i32)field_index);
+			Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, is_using, cast(i32)fields.count);
 			e->identifier = name;
 			if (name_token.string == "_") {
-				fields[field_index++] = e;
+				array_add(&fields, e);
 			} else if (name_token.string == "__tag") {
 				error(name, "`__tag` is a reserved identifier for fields");
 			} else {
@@ -763,7 +784,7 @@ isize check_fields(Checker *c, AstNode *node, Array<AstNode *> decls,
 					error(e->token,   "\tpreviously declared");
 				} else {
 					map_set(&entity_map, key, e);
-					fields[field_index++] = e;
+					array_add(&fields, e);
 					add_entity(c, c->context.scope, name, e);
 				}
 				add_entity_use(c, name, e);
@@ -792,9 +813,9 @@ isize check_fields(Checker *c, AstNode *node, Array<AstNode *> decls,
 						}
 					}
 					if (ok) {
-						using_index_expr = fields[field_index-1];
+						using_index_expr = fields[fields.count-1];
 					} else {
-						fields[field_index-1]->flags &= ~EntityFlag_Using;
+						fields[fields.count-1]->flags &= ~EntityFlag_Using;
 						error(name_token, "Previous `using` for an index expression `%.*s`", LIT(name_token.string));
 					}
 				} else {
@@ -810,7 +831,7 @@ isize check_fields(Checker *c, AstNode *node, Array<AstNode *> decls,
 	}
 
 
-	return field_index;
+	return fields;
 }
 
 
@@ -863,26 +884,26 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) {
 	GB_ASSERT(is_type_struct(struct_type));
 	ast_node(st, StructType, node);
 
-	isize field_count = 0;
+	isize min_field_count = 0;
 	for_array(field_index, st->fields) {
 	AstNode *field = st->fields[field_index];
 		switch (field->kind) {
 		case_ast_node(f, ValueDecl, field);
-			field_count += f->names.count;
+			min_field_count += f->names.count;
 		case_end;
 		}
-	}
 
-	Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count);
+	}
+	struct_type->Record.names = make_names_field_for_record(c, c->context.scope);
 
-	field_count = check_fields(c, node, st->fields, fields, field_count, str_lit("struct"));
+	auto fields = check_fields(c, node, st->fields, min_field_count, str_lit("struct"));
 
+	struct_type->Record.scope               = c->context.scope;
 	struct_type->Record.is_packed           = st->is_packed;
 	struct_type->Record.is_ordered          = st->is_ordered;
-	struct_type->Record.fields              = fields;
-	struct_type->Record.fields_in_src_order = fields;
-	struct_type->Record.field_count         = field_count;
-	struct_type->Record.names = make_names_field_for_record(c, c->context.scope);
+	struct_type->Record.fields              = fields.data;
+	struct_type->Record.fields_in_src_order = fields.data;
+	struct_type->Record.field_count         = fields.count;
 
 	type_set_offsets(c->allocator, struct_type);
 
@@ -893,8 +914,8 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) {
 		struct_type->Record.offsets = nullptr;
 		// NOTE(bill): Reorder fields for reduced size/performance
 
-		Entity **reordered_fields = gb_alloc_array(c->allocator, Entity *, field_count);
-		for (isize i = 0; i < field_count; i++) {
+		Entity **reordered_fields = gb_alloc_array(c->allocator, Entity *, fields.count);
+		for (isize i = 0; i < fields.count; i++) {
 			reordered_fields[i] = struct_type->Record.fields_in_src_order[i];
 		}
 
@@ -902,9 +923,9 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) {
 		// TODO(bill): Probably make an inline sorting procedure rather than use global variables
 		__checker_allocator = c->allocator;
 		// NOTE(bill): compound literal order must match source not layout
-		gb_sort_array(reordered_fields, field_count, cmp_reorder_struct_fields);
+		gb_sort_array(reordered_fields, fields.count, cmp_reorder_struct_fields);
 
-		for (isize i = 0; i < field_count; i++) {
+		for (isize i = 0; i < fields.count; i++) {
 			reordered_fields[i]->Variable.field_index = i;
 		}
 
@@ -959,12 +980,12 @@ void check_union_type(Checker *c, Type *named_type, Type *union_type, AstNode *n
 	ast_node(ut, UnionType, node);
 
 	isize variant_count = ut->variants.count+1;
-	isize field_count = 0;
+	isize min_field_count = 0;
 	for_array(i, ut->fields) {
 		AstNode *field = ut->fields[i];
 		switch (field->kind) {
 		case_ast_node(f, ValueDecl, field);
-			field_count += f->names.count;
+			min_field_count += f->names.count;
 		case_end;
 		}
 	}
@@ -977,23 +998,23 @@ void check_union_type(Checker *c, Type *named_type, Type *union_type, AstNode *n
 
 	Entity *using_index_expr = nullptr;
 
-	Entity **variants = gb_alloc_array(c->allocator, Entity *, variant_count);
-	Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count);
+	Array<Entity *> variants = {};
+	array_init(&variants, heap_allocator(), variant_count);
 
-	isize variant_index = 0;
-	variants[variant_index++] = make_entity_type_name(c->allocator, c->context.scope, empty_token, nullptr);
+	array_add(&variants, make_entity_type_name(c->allocator, c->context.scope, empty_token, nullptr));
 
-	field_count = check_fields(c, nullptr, ut->fields, fields, field_count, str_lit("union"));
+	auto fields = check_fields(c, nullptr, ut->fields, min_field_count, str_lit("union"));
 
-	for (isize i = 0; i < field_count; i++) {
+	for (isize i = 0; i < fields.count; i++) {
 		Entity *f = fields[i];
 		String name = f->token.string;
 		map_set(&entity_map, hash_string(name), f);
 	}
 
-	union_type->Record.fields              = fields;
-	union_type->Record.fields_in_src_order = fields;
-	union_type->Record.field_count         = field_count;
+	union_type->Record.scope               = c->context.scope;
+	union_type->Record.fields              = fields.data;
+	union_type->Record.fields_in_src_order = fields.data;
+	union_type->Record.field_count         = fields.count;
 	union_type->Record.are_offsets_set     = false;
 	union_type->Record.is_ordered          = true;
 	{
@@ -1035,17 +1056,16 @@ void check_union_type(Checker *c, Type *named_type, Type *union_type, AstNode *n
 			AstNode *dummy_struct = ast_struct_type(c->curr_ast_file, token, list, list_count, false, true, nullptr);
 
 			check_open_scope(c, dummy_struct);
-			Entity **fields = gb_alloc_array(c->allocator, Entity *, list_count);
-			isize field_count = check_fields(c, dummy_struct, list, fields, list_count, str_lit("variant"));
+			base_type->Record.names = make_names_field_for_record(c, c->context.scope);
+			auto fields = check_fields(c, dummy_struct, list, list_count, str_lit("variant"));
 			base_type->Record.is_packed           = false;
 			base_type->Record.is_ordered          = true;
-			base_type->Record.fields              = fields;
-			base_type->Record.fields_in_src_order = fields;
-			base_type->Record.field_count         = field_count;
-			base_type->Record.names = make_names_field_for_record(c, c->context.scope);
-			base_type->Record.node = dummy_struct;
-			base_type->Record.variant_parent = named_type != nullptr ? named_type : union_type;
-			base_type->Record.variant_index = variant_index;
+			base_type->Record.fields              = fields.data;
+			base_type->Record.fields_in_src_order = fields.data;
+			base_type->Record.field_count         = fields.count;
+			base_type->Record.node                = dummy_struct;
+			base_type->Record.variant_parent      = named_type != nullptr ? named_type : union_type;
+			base_type->Record.variant_index       = variants.count;
 
 
 			type_set_offsets(c->allocator, base_type);
@@ -1069,7 +1089,7 @@ void check_union_type(Checker *c, Type *named_type, Type *union_type, AstNode *n
 			error(name_token, "`%.*s` is already declared in this union", LIT(name_token.string));
 		} else {
 			map_set(&entity_map, key, e);
-			variants[variant_index++] = e;
+			array_add(&variants, e);
 		}
 		add_entity_use(c, f->name, e);
 	}
@@ -1077,8 +1097,8 @@ void check_union_type(Checker *c, Type *named_type, Type *union_type, AstNode *n
 	type_set_offsets(c->allocator, union_type);
 
 
-	union_type->Record.variants      = variants;
-	union_type->Record.variant_count = variant_index;
+	union_type->Record.variants      = variants.data;
+	union_type->Record.variant_count = variants.count;
 }
 
 void check_raw_union_type(Checker *c, Type *union_type, AstNode *node) {
@@ -1086,23 +1106,23 @@ void check_raw_union_type(Checker *c, Type *union_type, AstNode *node) {
 	GB_ASSERT(is_type_raw_union(union_type));
 	ast_node(ut, RawUnionType, node);
 
-	isize field_count = 0;
+	isize min_field_count = 0;
 	for_array(i, ut->fields) {
 		AstNode *field = ut->fields[i];
 		switch (field->kind) {
 		case_ast_node(f, ValueDecl, field);
-			field_count += f->names.count;
+			min_field_count += f->names.count;
 		case_end;
 		}
 	}
 
-	Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count);
+	union_type->Record.names = make_names_field_for_record(c, c->context.scope);
 
-	field_count = check_fields(c, node, ut->fields, fields, field_count, str_lit("raw_union"));
+	auto fields = check_fields(c, node, ut->fields, min_field_count, str_lit("raw_union"));
 
-	union_type->Record.fields = fields;
-	union_type->Record.field_count = field_count;
-	union_type->Record.names = make_names_field_for_record(c, c->context.scope);
+	union_type->Record.scope       = c->context.scope;
+	union_type->Record.fields      = fields.data;
+	union_type->Record.field_count = fields.count;
 }
 
 
@@ -1133,8 +1153,8 @@ void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *nod
 	Map<Entity *> entity_map = {}; // Key: String
 	map_init_with_reserve(&entity_map, c->tmp_allocator, 2*(et->fields.count));
 
-	Entity **fields = gb_alloc_array(c->allocator, Entity *, et->fields.count);
-	isize field_count = 0;
+	Array<Entity *> fields = {};
+	array_init(&fields, c->allocator, et->fields.count);
 
 	Type *constant_type = enum_type;
 	if (named_type != nullptr) {
@@ -1222,18 +1242,18 @@ void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *nod
 		} else {
 			map_set(&entity_map, key, e);
 			add_entity(c, c->context.scope, nullptr, e);
-			fields[field_count++] = e;
+			array_add(&fields, e);
 			add_entity_use(c, field, e);
 		}
 	}
-	GB_ASSERT(field_count <= et->fields.count);
+	GB_ASSERT(fields.count <= et->fields.count);
 
 
-	enum_type->Record.fields = fields;
-	enum_type->Record.field_count = field_count;
+	enum_type->Record.fields      = fields.data;
+	enum_type->Record.field_count = fields.count;
 
 	enum_type->Record.enum_count = make_entity_constant(c->allocator, c->context.scope,
-		make_token_ident(str_lit("count")), t_int, exact_value_i64(field_count));
+		make_token_ident(str_lit("count")), t_int, exact_value_i64(fields.count));
 	enum_type->Record.enum_min_value = make_entity_constant(c->allocator, c->context.scope,
 		make_token_ident(str_lit("min_value")), constant_type, min_value);
 	enum_type->Record.enum_max_value = make_entity_constant(c->allocator, c->context.scope,

+ 34 - 2
src/check_stmt.cpp

@@ -473,7 +473,39 @@ bool check_using_stmt_entity(Checker *c, AstNodeUsingStmt *us, AstNode *expr, bo
 	switch (e->kind) {
 	case Entity_TypeName: {
 		Type *t = base_type(e->type);
-		if (is_type_union(t)) {
+		if (t->kind == Type_Record) {
+			Scope *s = t->Record.scope;
+			if (s != nullptr) {
+				for_array(i, s->elements.entries) {
+					Entity *f = s->elements.entries[i].value;
+					if (f->kind != Entity_Variable) {
+						Entity *found = scope_insert_entity(c->context.scope, f);
+						if (found != nullptr) {
+							gbString expr_str = expr_to_string(expr);
+							error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
+							gb_string_free(expr_str);
+							return false;
+						}
+						f->using_parent = e;
+					}
+				}
+			} else if (is_type_enum(t)) {
+				for (isize i = 0; i < t->Record.field_count; i++) {
+					Entity *f = t->Record.fields[i];
+					Entity *found = scope_insert_entity(c->context.scope, f);
+					if (found != nullptr) {
+						gbString expr_str = expr_to_string(expr);
+						error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
+						gb_string_free(expr_str);
+						return false;
+					}
+					f->using_parent = e;
+				}
+			}
+		} else {
+			error(us->token, "`using` can be only applied to record type entities");
+		}
+		/* if (is_type_union(t)) {
 			TokenPos pos = ast_node_token(expr).pos;
 			for (isize i = 1; i < t->Record.variant_count; i++) {
 				Entity *f = t->Record.variants[i];
@@ -502,7 +534,7 @@ bool check_using_stmt_entity(Checker *c, AstNodeUsingStmt *us, AstNode *expr, bo
 
 		} else {
 			error(us->token, "`using` can be only applied to `union` or `enum` type entities");
-		}
+		} */
 	} break;
 
 	case Entity_ImportName: {

+ 42 - 6
src/checker.cpp

@@ -233,6 +233,7 @@ struct Scope {
 	bool             is_global;
 	bool             is_file;
 	bool             is_init;
+	bool             is_record;
 	bool             has_been_imported; // This is only applicable to file scopes
 	AstFile *        file;
 };
@@ -447,8 +448,16 @@ void check_open_scope(Checker *c, AstNode *node) {
 	          is_ast_node_type(node));
 	Scope *scope = make_scope(c->context.scope, c->allocator);
 	add_scope(c, node, scope);
-	if (node->kind == AstNode_ProcType) {
+	switch (node->kind) {
+	case AstNode_ProcType:
 		scope->is_proc = true;
+		break;
+	case AstNode_StructType:
+	case AstNode_EnumType:
+	case AstNode_UnionType:
+	case AstNode_RawUnionType:
+		scope->is_record = true;
+		break;
 	}
 	c->context.scope = scope;
 	c->context.stmt_state_flags |= StmtStateFlag_bounds_check;
@@ -1307,9 +1316,26 @@ void init_preload(Checker *c) {
 		GB_ASSERT(is_type_union(type_info_entity->type));
 		TypeRecord *record = &base_type(type_info_entity->type)->Record;
 
-		t_type_info_record = find_core_entity(c, str_lit("TypeInfoRecord"))->type;
+		// Entity *type_info_record = current_scope_lookup_entity(record->scope, str_lit("Record"));
+		// if (type_info_record == nullptr) {
+		// 	compiler_error("Could not find type declaration for TypeInfo.Record\n"
+		// 	               "Is `_preload.odin` missing from the `core` directory relative to the odin executable?");
+		// }
+		// Entity *type_info_enum_value = current_scope_lookup_entity(record->scope, str_lit("EnumValue"));
+		// if (type_info_record == nullptr) {
+		// 	compiler_error("Could not find type declaration for TypeInfo.EnumValue\n"
+		// 	               "Is `_preload.odin` missing from the `core` directory relative to the odin executable?");
+		// }
+
+		// GB_ASSERT(type_info_record->type != nullptr);
+		// GB_ASSERT(type_info_enum_value->type != nullptr);
+		Entity *type_info_record     = find_core_entity(c, str_lit("TypeInfoRecord"));
+		Entity *type_info_enum_value = find_core_entity(c, str_lit("TypeInfoEnumValue"));
+
+
+		t_type_info_record = type_info_record->type;
 		t_type_info_record_ptr = make_type_pointer(c->allocator, t_type_info_record);
-		t_type_info_enum_value = find_core_entity(c, str_lit("TypeInfoEnumValue"))->type;
+		t_type_info_enum_value = type_info_enum_value->type;
 		t_type_info_enum_value_ptr = make_type_pointer(c->allocator, t_type_info_enum_value);
 
 
@@ -1692,20 +1718,29 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 						continue;
 					}
 
+					Token token = name->Ident.token;
+					if (token.string == "EnumValue") {
+						gb_printf_err("EnumValue %p\n", name);
+					}
+
 					AstNode *fl = c->context.curr_foreign_library;
 					DeclInfo *d = make_declaration_info(c->allocator, c->context.scope, c->context.decl);
 					Entity *e = nullptr;
 
 					if (is_ast_node_type(init)) {
-						e = make_entity_type_name(c->allocator, d->scope, name->Ident.token, nullptr);
+						e = make_entity_type_name(c->allocator, d->scope, token, nullptr);
 						if (vd->type != nullptr) {
 							error(name, "A type declaration cannot have an type parameter");
 						}
 						d->type_expr = init;
 						d->init_expr = init;
 					} else if (init->kind == AstNode_ProcLit) {
+						if (c->context.scope->is_record) {
+							error(name, "Procedure declarations are not allowed within a record");
+							continue;
+						}
 						ast_node(pl, ProcLit, init);
-						e = make_entity_procedure(c->allocator, d->scope, name->Ident.token, nullptr, pl->tags);
+						e = make_entity_procedure(c->allocator, d->scope, token, nullptr, pl->tags);
 						if (fl != nullptr) {
 							GB_ASSERT(fl->kind == AstNode_Ident);
 							e->Procedure.foreign_library_ident = fl;
@@ -1714,7 +1749,7 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 						d->proc_lit = init;
 						d->type_expr = pl->type;
 					} else {
-						e = make_entity_constant(c->allocator, d->scope, name->Ident.token, nullptr, empty_exact_value);
+						e = make_entity_constant(c->allocator, d->scope, token, nullptr, empty_exact_value);
 						d->type_expr = vd->type;
 						d->init_expr = init;
 					}
@@ -1730,6 +1765,7 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 					}
 
 
+
 					add_entity_and_decl_info(c, name, e, d);
 				}
 

+ 17 - 0
src/ir.cpp

@@ -3632,10 +3632,13 @@ void ir_pop_target_list(irProcedure *proc) {
 
 
 void ir_gen_global_type_name(irModule *m, Entity *e, String name) {
+	if (e->type == nullptr) return;
+
 	irValue *t = ir_value_type_name(m->allocator, name, e->type);
 	ir_module_add_value(m, e, t);
 	map_set(&m->members, hash_string(name), t);
 
+	#if 0
 	if (is_type_union(e->type)) {
 		Type *bt = base_type(e->type);
 		// NOTE(bill): Zeroth entry is null (for `match type` stmts)
@@ -3643,6 +3646,20 @@ void ir_gen_global_type_name(irModule *m, Entity *e, String name) {
 			ir_mangle_add_sub_type_name(m, bt->Record.variants[j], name);
 		}
 	}
+	#endif
+
+	Type *bt = base_type(e->type);
+	if (bt->kind == Type_Record) {
+		Scope *s = bt->Record.scope;
+		if (s != nullptr) {
+			for_array(i, s->elements.entries) {
+				Entity *e = s->elements.entries[i].value;
+				if (e->kind == Entity_TypeName) {
+					ir_mangle_add_sub_type_name(m, e, name);
+				}
+			}
+		}
+	}
 }
 
 

+ 26 - 19
src/parser.cpp

@@ -1366,6 +1366,7 @@ AstNode *ast_field_list(AstFile *f, Token token, Array<AstNode *> list) {
 	result->FieldList.list  = list;
 	return result;
 }
+
 AstNode *ast_union_field(AstFile *f, AstNode *name, AstNode *list) {
 	AstNode *result = make_ast_node(f, AstNode_UnionField);
 	result->UnionField.name = name;
@@ -3390,20 +3391,28 @@ AstNode *parse_record_field_list(AstFile *f, isize *name_count_) {
 	while (f->curr_token.kind != Token_CloseBrace &&
 	       f->curr_token.kind != Token_EOF) {
 		AstNode *decl = parse_stmt(f);
-		if (decl->kind != AstNode_ValueDecl) {
-			error(decl, "Expected a field list, got %.*s", LIT(ast_node_strings[decl->kind]));
-		} else {
-			ast_node(vd, ValueDecl, decl);
-			if (vd->is_mutable) {
-				if (vd->flags&VarDeclFlag_thread_local) {
-					vd->flags &= ~VarDeclFlag_thread_local;
-					error(decl, "Field values cannot be #thread_local");
-				}
-				array_add(&decls, decl);
-				total_name_count += vd->names.count;
-			} else {
-				error(decl, "Only variable declarations are allowed at the moment");
+		switch (decl->kind) {
+		case AstNode_EmptyStmt:
+		case AstNode_BadStmt:
+		case AstNode_BadDecl:
+			break;
+
+		case_ast_node(vd, ValueDecl, decl);
+			if (vd->flags&VarDeclFlag_thread_local) {
+				vd->flags &= ~VarDeclFlag_thread_local;
+				error(decl, "Field values cannot be #thread_local");
 			}
+			array_add(&decls, decl);
+			total_name_count += vd->names.count;
+		case_end;
+
+		case AstNode_WhenStmt:
+			array_add(&decls, decl);
+			break;
+
+		default:
+			error(decl, "Expected a value declaration, got %.*s", LIT(ast_node_strings[decl->kind]));
+			break;
 		}
 	}
 
@@ -3683,8 +3692,8 @@ AstNode *parse_type_or_ident(AstFile *f) {
 		Token open = expect_token_after(f, Token_OpenBrace, "struct");
 
 		isize    name_count = 0;
-		AstNode *fields     = parse_record_field_list(f, &name_count);
-		Token    close      = expect_token(f, Token_CloseBrace);
+		AstNode *fields = parse_record_field_list(f, &name_count);
+		Token    close  = expect_token(f, Token_CloseBrace);
 
 		Array<AstNode *> decls = {};
 		if (fields != nullptr) {
@@ -3742,11 +3751,9 @@ AstNode *parse_type_or_ident(AstFile *f) {
 							vd->flags &= ~VarDeclFlag_thread_local;
 							error(decl, "Field values cannot be #thread_local");
 						}
-						array_add(&decls, decl);
-						total_decl_name_count += vd->names.count;
-					} else {
-						error(decl, "Only variable declarations are allowed at the moment");
 					}
+					array_add(&decls, decl);
+					total_decl_name_count += vd->names.count;
 				}
 			}
 		}

+ 15 - 0
src/types.cpp

@@ -90,6 +90,7 @@ struct TypeRecord {
 	i32      field_count; // == struct_offsets count
 	Entity **fields_in_src_order; // Entity_Variable
 	AstNode *node;
+	Scope *  scope;
 
 	// Entity_TypeName - union
 	Entity **variants;
@@ -1477,6 +1478,8 @@ Selection lookup_field_from_index(gbAllocator a, Type *type, i64 index) {
 gb_global Entity *entity__any_data       = nullptr;
 gb_global Entity *entity__any_type_info  = nullptr;
 
+Entity *current_scope_lookup_entity(Scope *s, String name);
+
 Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_name, bool is_type, Selection sel) {
 	GB_ASSERT(type_ != nullptr);
 
@@ -1593,6 +1596,18 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
 				}
 			}
 		}
+
+		if (type->kind == Type_Record) {
+			Scope *s = type->Record.scope;
+			if (s != nullptr) {
+				Entity *found = current_scope_lookup_entity(s, field_name);
+				if (found != nullptr && found->kind != Entity_Variable) {
+					sel.entity = found;
+					return sel;
+				}
+			}
+		}
+
 	} else if (type->kind == Type_Record) {
 		for (isize i = 0; i < type->Record.field_count; i++) {
 			Entity *f = type->Record.fields[i];