Browse Source

Bring back `enum` but using iota

Ginger Bill 8 years ago
parent
commit
6fef74317c
13 changed files with 314 additions and 46 deletions
  1. 22 6
      code/demo.odin
  2. 23 18
      core/_preload.odin
  3. 10 0
      core/fmt.odin
  4. 5 4
      core/mem.odin
  5. 1 1
      core/os_windows.odin
  6. 2 1
      src/checker/checker.c
  7. 129 11
      src/checker/expr.c
  8. 14 1
      src/checker/stmt.c
  9. 47 2
      src/checker/types.c
  10. 13 1
      src/parser.c
  11. 43 0
      src/ssa.c
  12. 4 1
      src/ssa_print.c
  13. 1 0
      src/tokenizer.c

+ 22 - 6
code/demo.odin

@@ -11,11 +11,27 @@ import {
 	win32 "sys/windows.odin";
 }
 
+type Thing enum f64 {
+	_, // Ignore first value
+	A = 1<<(10*iota),
+	B,
+	C,
+	D,
+}
+
 proc main() {
-	var x = if false {
-		give 123;
-	} else {
-		give 321;
-	};
-	fmt.println(x);
+	var ti = type_info(Thing);
+	match type info : type_info_base(ti) {
+	case Type_Info.Enum:
+		for var i = 0; i < info.names.count; i++ {
+			if i > 0 {
+				fmt.print(", ");
+			}
+			fmt.print(info.names[i]);
+		}
+		fmt.println();
+	}
+
+	fmt.println(Thing.A, Thing.B, Thing.C, Thing.D);
+
 }

+ 23 - 18
core/_preload.odin

@@ -73,6 +73,10 @@ type {
 		Struct    Type_Info_Record;
 		Union     Type_Info_Record;
 		Raw_Union Type_Info_Record;
+		Enum struct #ordered {
+			base  ^Type_Info;
+			names []string;
+		};
 	}
 }
 
@@ -112,12 +116,11 @@ proc fmuladd64(a, b, c f64) -> f64 #foreign "llvm.fmuladd.f64"
 
 
 
-type Allocator_Mode u8;
-const {
-	ALLOCATOR_ALLOC Allocator_Mode = iota;
-	ALLOCATOR_FREE;
-	ALLOCATOR_FREE_ALL;
-	ALLOCATOR_RESIZE;
+type Allocator_Mode enum u8 {
+	ALLOC = iota,
+	FREE,
+	FREE_ALL,
+	RESIZE,
 }
 type {
 	Allocator_Proc proc(allocator_data rawptr, mode Allocator_Mode,
@@ -160,20 +163,20 @@ proc alloc(size int) -> rawptr #inline { return alloc_align(size, DEFAULT_ALIGNM
 proc alloc_align(size, alignment int) -> rawptr #inline {
 	__check_context();
 	var a = context.allocator;
-	return a.procedure(a.data, ALLOCATOR_ALLOC, size, alignment, nil, 0, 0);
+	return a.procedure(a.data, Allocator_Mode.ALLOC, size, alignment, nil, 0, 0);
 }
 
 proc free(ptr rawptr) #inline {
 	__check_context();
 	var a = context.allocator;
 	if ptr != nil {
-		a.procedure(a.data, ALLOCATOR_FREE, 0, 0, ptr, 0, 0);
+		a.procedure(a.data, Allocator_Mode.FREE, 0, 0, ptr, 0, 0);
 	}
 }
 proc free_all() #inline {
 	__check_context();
 	var a = context.allocator;
-	a.procedure(a.data, ALLOCATOR_FREE_ALL, 0, 0, nil, 0, 0);
+	a.procedure(a.data, Allocator_Mode.FREE_ALL, 0, 0, nil, 0, 0);
 }
 
 
@@ -181,7 +184,7 @@ proc resize      (ptr rawptr, old_size, new_size int) -> rawptr #inline { return
 proc resize_align(ptr rawptr, old_size, new_size, alignment int) -> rawptr #inline {
 	__check_context();
 	var a = context.allocator;
-	return a.procedure(a.data, ALLOCATOR_RESIZE, new_size, alignment, ptr, old_size, 0);
+	return a.procedure(a.data, Allocator_Mode.RESIZE, new_size, alignment, ptr, old_size, 0);
 }
 
 
@@ -214,9 +217,11 @@ proc default_resize_align(old_memory rawptr, old_size, new_size, alignment int)
 proc default_allocator_proc(allocator_data rawptr, mode Allocator_Mode,
                             size, alignment int,
                             old_memory rawptr, old_size int, flags u64) -> rawptr {
+	using Allocator_Mode;
+
 	when false {
 		match mode {
-		case ALLOCATOR_ALLOC:
+		case ALLOC:
 			var total_size = size + alignment + size_of(mem.AllocationHeader);
 			var ptr = os.heap_alloc(total_size);
 			var header = ptr as ^mem.AllocationHeader;
@@ -224,14 +229,14 @@ proc default_allocator_proc(allocator_data rawptr, mode Allocator_Mode,
 			mem.allocation_header_fill(header, ptr, size);
 			return mem.zero(ptr, size);
 
-		case ALLOCATOR_FREE:
+		case FREE:
 			os.heap_free(mem.allocation_header(old_memory));
 			return nil;
 
-		case ALLOCATOR_FREE_ALL:
+		case FREE_ALL:
 			// NOTE(bill): Does nothing
 
-		case ALLOCATOR_RESIZE:
+		case RESIZE:
 			var total_size = size + alignment + size_of(mem.AllocationHeader);
 			var ptr = os.heap_resize(mem.allocation_header(old_memory), total_size);
 			var header = ptr as ^mem.AllocationHeader;
@@ -241,17 +246,17 @@ proc default_allocator_proc(allocator_data rawptr, mode Allocator_Mode,
 		}
 	} else {
 		match mode {
-		case ALLOCATOR_ALLOC:
+		case ALLOC:
 			return os.heap_alloc(size);
 
-		case ALLOCATOR_FREE:
+		case FREE:
 			os.heap_free(old_memory);
 			return nil;
 
-		case ALLOCATOR_FREE_ALL:
+		case FREE_ALL:
 			// NOTE(bill): Does nothing
 
-		case ALLOCATOR_RESIZE:
+		case RESIZE:
 			return os.heap_resize(old_memory, size);
 		}
 	}

+ 10 - 0
core/fmt.odin

@@ -292,6 +292,12 @@ proc bprint_type(buf ^[]byte, ti ^Type_Info) {
 			bprint_type(buf, info.fields[i].type_info);
 		}
 		bprint_string(buf, "}");
+
+	case Enum:
+		bprint_string(buf, "enum ");
+		bprint_type(buf, info.base);
+		bprint_string(buf, " {}");
+
 	}
 }
 
@@ -463,6 +469,10 @@ proc bprint_any(buf ^[]byte, arg any) {
 		bprint_string(buf, "(union)");
 	case Raw_Union:
 		bprint_string(buf, "(raw_union)");
+
+	case Enum:
+		bprint_any(buf, make_any(info.base, arg.data));
+
 	case Procedure:
 		bprint_type(buf, arg.type_info);
 		bprint_string(buf, " @ ");

+ 5 - 4
core/mem.odin

@@ -163,10 +163,11 @@ proc arena_allocator(arena ^Arena) -> Allocator {
 proc arena_allocator_proc(allocator_data rawptr, mode Allocator_Mode,
                           size, alignment int,
                           old_memory rawptr, old_size int, flags u64) -> rawptr {
+	using Allocator_Mode;
 	var arena = allocator_data as ^Arena;
 
 	match mode {
-	case ALLOCATOR_ALLOC:
+	case ALLOC:
 		var total_size = size + alignment;
 
 		if arena.memory.count + total_size > arena.memory.capacity {
@@ -180,14 +181,14 @@ proc arena_allocator_proc(allocator_data rawptr, mode Allocator_Mode,
 		arena.memory.count += total_size;
 		return zero(ptr, size);
 
-	case ALLOCATOR_FREE:
+	case FREE:
 		// NOTE(bill): Free all at once
 		// Use Arena_Temp_Memory if you want to free a block
 
-	case ALLOCATOR_FREE_ALL:
+	case FREE_ALL:
 		arena.memory.count = 0;
 
-	case ALLOCATOR_RESIZE:
+	case RESIZE:
 		return default_resize_align(old_memory, old_size, size, alignment);
 	}
 

+ 1 - 1
core/os_windows.odin

@@ -54,7 +54,7 @@ const {
 }
 
 const { // Windows reserves errors >= 1<<29 for application use
-	ERROR_FILE_IS_PIPE Error = 1<<29 + iota;
+	ERROR_FILE_IS_PIPE Error = 1<<29 + 0;
 }
 
 

+ 2 - 1
src/checker/checker.c

@@ -1027,7 +1027,7 @@ void init_preload(Checker *c) {
 		t_type_info_member = type_info_member_entity->type;
 		t_type_info_member_ptr = make_type_pointer(c->allocator, t_type_info_member);
 
-		if (record->field_count != 17) {
+		if (record->field_count != 18) {
 			compiler_error("Invalid `Type_Info` layout");
 		}
 		t_type_info_named     = record->fields[ 1]->type;
@@ -1046,6 +1046,7 @@ void init_preload(Checker *c) {
 		t_type_info_struct    = record->fields[14]->type;
 		t_type_info_union     = record->fields[15]->type;
 		t_type_info_raw_union = record->fields[16]->type;
+		t_type_info_enum      = record->fields[17]->type;
 	}
 
 	if (t_allocator == NULL) {

+ 129 - 11
src/checker/expr.c

@@ -18,6 +18,7 @@ bool     check_is_terminating      (AstNode *node);
 bool     check_has_break           (AstNode *stmt, bool implicit);
 void     check_stmt                (Checker *c, AstNode *node, u32 flags);
 void     check_stmt_list           (Checker *c, AstNodeArray stmts, u32 flags);
+void     check_init_constant       (Checker *c, Entity *e, Operand *operand);
 
 
 gb_inline Type *check_type(Checker *c, AstNode *expression) {
@@ -724,6 +725,106 @@ void check_raw_union_type(Checker *c, Type *union_type, AstNode *node) {
 // 	return i < j ? -1 : i > j;
 // }
 
+void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *node) {
+	ast_node(et, EnumType, node);
+	GB_ASSERT(is_type_enum(enum_type));
+
+	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
+
+	Type *base_type = t_int;
+	if (et->base_type != NULL) {
+		base_type = check_type(c, et->base_type);
+	}
+
+	if (base_type == NULL || !(is_type_integer(base_type) || is_type_float(base_type))) {
+		error_node(node, "Base type for enumeration must be numeric");
+		return;
+	}
+
+	// NOTE(bill): Must be up here for the `check_init_constant` system
+	enum_type->Record.enum_base_type = base_type;
+
+	MapEntity entity_map = {0}; // Key: String
+	map_entity_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_index = 0;
+
+	Type *constant_type = enum_type;
+	if (named_type != NULL) {
+		constant_type = named_type;
+	}
+
+	AstNode *prev_expr = NULL;
+
+	i64 iota = 0;
+
+	for_array(i, et->fields) {
+		AstNode *field = et->fields.e[i];
+		AstNode *ident = NULL;
+		if (field->kind == AstNode_FieldValue) {
+			ast_node(fv, FieldValue, field);
+			if (fv->field == NULL || fv->field->kind != AstNode_Ident) {
+				error_node(field, "An enum field's name must be an identifier");
+				continue;
+			}
+			ident = fv->field;
+			prev_expr = fv->value;
+		} else if (field->kind == AstNode_Ident) {
+			ident = field;
+		} else {
+			error_node(field, "An enum field's name must be an identifier");
+			continue;
+		}
+		String name = ident->Ident.string;
+
+		if (str_ne(name, str_lit("_"))) {
+			ExactValue v = make_exact_value_integer(iota);
+			Entity *e = make_entity_constant(c->allocator, c->context.scope, ident->Ident, constant_type, v);
+			e->identifier = ident;
+			e->flags |= EntityFlag_Visited;
+
+
+			AstNode *init = prev_expr;
+			if (init == NULL) {
+				error_node(field, "Missing initial expression for enumeration, e.g. iota");
+				continue;
+			}
+
+			GB_ASSERT(c->context.iota.kind == ExactValue_Invalid);
+			c->context.iota = e->Constant.value;
+			e->Constant.value = (ExactValue){0};
+
+			Operand operand = {0};
+			check_expr(c, &operand, init);
+
+			check_init_constant(c, e, &operand);
+			c->context.iota = (ExactValue){0};
+
+			if (operand.mode == Addressing_Constant) {
+				HashKey key = hash_string(name);
+				if (map_entity_get(&entity_map, key) != NULL) {
+					error_node(ident, "`%.*s` is already declared in this enumeration", LIT(name));
+				} else {
+					map_entity_set(&entity_map, key, e);
+					add_entity(c, c->context.scope, NULL, e);
+					fields[field_index++] = e;
+					add_entity_use(c, field, e);
+				}
+			}
+		}
+		iota++;
+	}
+
+	GB_ASSERT(field_index <= et->fields.count);
+
+	enum_type->Record.fields = fields;
+	enum_type->Record.field_count = field_index;
+
+	gb_temp_arena_memory_end(tmp);
+}
+
+
 Type *check_get_params(Checker *c, Scope *scope, AstNodeArray params, bool *is_variadic_) {
 	if (params.count == 0) {
 		return NULL;
@@ -1105,6 +1206,16 @@ Type *check_type_extra(Checker *c, AstNode *e, Type *named_type) {
 		goto end;
 	case_end;
 
+	case_ast_node(et, EnumType, e);
+		type = make_type_enum(c->allocator);
+		set_base_type(named_type, type);
+		check_open_scope(c, e);
+		check_enum_type(c, type, named_type, e);
+		check_close_scope(c);
+		type->Record.node = e;
+		goto end;
+	case_end;
+
 	case_ast_node(pt, ProcType, e);
 		type = alloc_type(c->allocator, Type_Proc);
 		set_base_type(named_type, type);
@@ -1114,6 +1225,7 @@ Type *check_type_extra(Checker *c, AstNode *e, Type *named_type) {
 		goto end;
 	case_end;
 
+
 	case_ast_node(ce, CallExpr, e);
 		Operand o = {0};
 		check_expr_or_type(c, &o, e);
@@ -1161,7 +1273,7 @@ end:
 
 bool check_unary_op(Checker *c, Operand *o, Token op) {
 	// TODO(bill): Handle errors correctly
-	Type *type = base_type(base_vector_type(o->type));
+	Type *type = base_type(base_enum_type(base_vector_type(o->type)));
 	gbString str = NULL;
 	switch (op.kind) {
 	case Token_Add:
@@ -1197,7 +1309,7 @@ bool check_unary_op(Checker *c, Operand *o, Token op) {
 
 bool check_binary_op(Checker *c, Operand *o, Token op) {
 	// TODO(bill): Handle errors correctly
-	Type *type = base_type(base_vector_type(o->type));
+	Type *type = base_type(base_enum_type(base_vector_type(o->type)));
 	switch (op.kind) {
 	case Token_Sub:
 	case Token_SubEq:
@@ -1274,6 +1386,8 @@ bool check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, Exa
 		return true;
 	}
 
+	type = base_type(base_enum_type(type));
+
 	if (is_type_boolean(type)) {
 		return in_value.kind == ExactValue_Bool;
 	} else if (is_type_string(type)) {
@@ -1363,7 +1477,7 @@ void check_is_expressible(Checker *c, Operand *o, Type *type) {
 				error_node(o->expr, "`%s = %lld` overflows `%s`", a, o->value.value_integer, b);
 			}
 		} else {
-			error_node(o->expr, "Cannot convert `%s`  to `%s`", a, b);
+			error_node(o->expr, "Cannot convert `%s` to `%s`", a, b);
 		}
 
 		gb_string_free(b);
@@ -2036,10 +2150,10 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 	}
 
 	if (op.kind == Token_Add || op.kind == Token_Sub) {
-		if (is_type_pointer(x->type) && is_type_integer(y->type)) {
+		if (is_type_pointer(x->type) && is_type_integer(base_enum_type(y->type))) {
 			*x = check_ptr_addition(c, op.kind, x, y, node);
 			return;
-		} else if (is_type_integer(x->type) && is_type_pointer(y->type)) {
+		} else if (is_type_integer(base_enum_type(x->type)) && is_type_pointer(y->type)) {
 			if (op.kind == Token_Sub) {
 				gbString lhs = expr_to_string(x->expr);
 				gbString rhs = expr_to_string(y->expr);
@@ -2247,20 +2361,22 @@ void convert_to_typed(Checker *c, Operand *operand, Type *target_type, i32 level
 	}
 
 	if (is_type_untyped(target_type)) {
-		Type *x = operand->type;
-		Type *y = target_type;
-		if (is_type_numeric(x) && is_type_numeric(y)) {
-			if (x < y) {
+		GB_ASSERT(operand->type->kind == Type_Basic);
+		GB_ASSERT(target_type->kind == Type_Basic);
+		BasicKind x_kind = operand->type->Basic.kind;
+		BasicKind y_kind = target_type->Basic.kind;
+		if (is_type_numeric(operand->type) && is_type_numeric(target_type)) {
+			if (x_kind < y_kind) {
 				operand->type = target_type;
 				update_expr_type(c, operand->expr, target_type, false);
 			}
-		} else if (x != y) {
+		} else if (x_kind != y_kind) {
 			convert_untyped_error(c, operand, target_type);
 		}
 		return;
 	}
 
-	Type *t = base_type(target_type);
+	Type *t = base_type(base_enum_type(target_type));
 	switch (t->kind) {
 	case Type_Basic:
 		if (operand->mode == Addressing_Constant) {
@@ -4366,7 +4482,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 	case AstNode_ArrayType:
 	case AstNode_VectorType:
 	case AstNode_StructType:
+	case AstNode_UnionType:
 	case AstNode_RawUnionType:
+	case AstNode_EnumType:
 		o->mode = Addressing_Type;
 		o->type = check_type(c, node);
 		break;

+ 14 - 1
src/checker/stmt.c

@@ -934,8 +934,21 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 						}
 						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 != NULL) {
+							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;
+						}
+						f->using_parent = e;
+					}
+
 				} else {
-					error(us->token, "`using` can be only applied to `union` type entities");
+					error(us->token, "`using` can be only applied to `union` or `enum` type entities");
 				}
 			} break;
 

+ 47 - 2
src/checker/types.c

@@ -64,6 +64,7 @@ typedef enum TypeRecordKind {
 	TypeRecord_Struct,
 	TypeRecord_RawUnion,
 	TypeRecord_Union, // Tagged
+	TypeRecord_Enum,
 
 	TypeRecord_Count,
 } TypeRecordKind;
@@ -82,6 +83,8 @@ typedef struct TypeRecord {
 	bool     struct_is_packed;
 	bool     struct_is_ordered;
 	Entity **fields_in_src_order; // Entity_Variable
+
+	Type *   enum_base_type;
 } TypeRecord;
 
 #define TYPE_KINDS \
@@ -270,6 +273,7 @@ gb_global Type *t_type_info_tuple      = NULL;
 gb_global Type *t_type_info_struct     = NULL;
 gb_global Type *t_type_info_union      = NULL;
 gb_global Type *t_type_info_raw_union  = NULL;
+gb_global Type *t_type_info_enum       = NULL;
 
 gb_global Type *t_allocator            = NULL;
 gb_global Type *t_allocator_ptr        = NULL;
@@ -285,7 +289,10 @@ gbString type_to_string(Type *type);
 
 Type *base_type(Type *t) {
 	for (;;) {
-		if (t == NULL || t->kind != Type_Named) {
+		if (t == NULL) {
+			break;
+		}
+		if (t->kind != Type_Named) {
 			break;
 		}
 		if (t == t->Named.base) {
@@ -296,6 +303,16 @@ Type *base_type(Type *t) {
 	return t;
 }
 
+Type *base_enum_type(Type *t) {
+	Type *bt = base_type(t);
+	if (bt != NULL &&
+	    bt->kind == Type_Record &&
+	    bt->Record.kind == TypeRecord_Enum) {
+		return bt->Record.enum_base_type;
+	}
+	return t;
+}
+
 void set_base_type(Type *t, Type *base) {
 	if (t && t->kind == Type_Named) {
 		t->Named.base = base;
@@ -367,6 +384,13 @@ Type *make_type_raw_union(gbAllocator a) {
 	return t;
 }
 
+Type *make_type_enum(gbAllocator a) {
+	Type *t = alloc_type(a, Type_Record);
+	t->Record.kind = TypeRecord_Enum;
+	return t;
+}
+
+
 
 
 
@@ -492,7 +516,7 @@ bool is_type_ordered(Type *t) {
 	return false;
 }
 bool is_type_constant_type(Type *t) {
-	t = base_type(t);
+	t = base_type(base_enum_type(t));
 	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_ConstantType) != 0;
 	}
@@ -598,6 +622,11 @@ bool is_type_raw_union(Type *t) {
 	t = base_type(t);
 	return (t->kind == Type_Record && t->Record.kind == TypeRecord_RawUnion);
 }
+bool is_type_enum(Type *t) {
+	t = base_type(t);
+	return (t->kind == Type_Record && t->Record.kind == TypeRecord_Enum);
+}
+
 
 bool is_type_any(Type *t) {
 	t = base_type(t);
@@ -642,6 +671,8 @@ bool is_type_comparable(Type *t) {
 				if (!is_type_comparable(t->Record.fields[i]->type))
 					return false;
 			}
+		} else if (is_type_enum(t)) {
+			return is_type_comparable(base_enum_type(t));
 		}
 		return false;
 	} break;
@@ -712,6 +743,8 @@ bool are_types_identical(Type *x, Type *y) {
 						return true;
 					}
 					break;
+				case TypeRecord_Enum:
+					return x == y; // NOTE(bill): All enums are unique
 				}
 			}
 		}
@@ -992,6 +1025,18 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
 				GB_ASSERT(f->kind == Entity_TypeName);
 				String str = f->token.string;
 
+				if (str_eq(field_name, str)) {
+					sel.entity = f;
+					selection_add_index(&sel, i);
+					return sel;
+				}
+			}
+		} else if (is_type_enum(type)) {
+			for (isize i = 0; i < type->Record.field_count; i++) {
+				Entity *f = type->Record.fields[i];
+				GB_ASSERT(f->kind == Entity_Constant);
+				String str = f->token.string;
+
 				if (str_eq(field_name, str)) {
 					sel.entity = f;
 					selection_add_index(&sel, i);

+ 13 - 1
src/parser.c

@@ -347,7 +347,7 @@ AST_NODE_KIND(_TypeBegin, "", i32) \
 	AST_NODE_KIND(EnumType, "enum type", struct { \
 		Token token; \
 		AstNode *base_type; \
-		AstNodeArray fields; \
+		AstNodeArray fields; /* FieldValue */ \
 	}) \
 AST_NODE_KIND(_TypeEnd,  "", i32)
 
@@ -2548,6 +2548,18 @@ AstNode *parse_identifier_or_type(AstFile *f) {
 		return make_raw_union_type(f, token, decls, decl_count);
 	}
 
+	case Token_enum: {
+		Token token = expect_token(f, Token_enum);
+		AstNode *base_type = NULL;
+		if (f->curr_token.kind != Token_OpenBrace) {
+			base_type = parse_type(f);
+		}
+		Token open = expect_token(f, Token_OpenBrace);
+		AstNodeArray fields = parse_element_list(f);
+		Token close = expect_token(f, Token_CloseBrace);
+		return make_enum_type(f, token, base_type, fields);
+	}
+
 	case Token_proc: {
 		Token token = f->curr_token;
 		AstNode *pt = parse_proc_type(f, NULL, NULL);

+ 43 - 0
src/ssa.c

@@ -5484,6 +5484,49 @@ void ssa_gen_tree(ssaGen *s) {
 						ssa_emit_store(proc, len, field_count);
 						ssa_emit_store(proc, cap, field_count);
 					} break;
+					case TypeRecord_Enum:
+						tag = ssa_add_local_generated(proc, t_type_info_enum);
+						{
+							GB_ASSERT(t->Record.enum_base_type != NULL);
+							ssaValue *base = ssa_type_info(proc, t->Record.enum_base_type);
+							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), base);
+
+							if (t->Record.field_count > 0) {
+								Entity **fields = t->Record.fields;
+								isize count = t->Record.field_count;
+								ssaValue *name_array = NULL;
+
+								{
+									Token token = {Token_Ident};
+									i32 id = cast(i32)entry_index;
+									char name_base[] = "__$enum_names";
+									isize name_len = gb_size_of(name_base) + 10;
+									token.string.text = gb_alloc_array(a, u8, name_len);
+									token.string.len = gb_snprintf(cast(char *)token.string.text, name_len,
+									                               "%s-%d", name_base, id)-1;
+									Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_string, count), false);
+									name_array = ssa_make_value_global(a, e, NULL);
+									name_array->Global.is_private = true;
+									ssa_module_add_value(m, e, name_array);
+									map_ssa_value_set(&m->members, hash_string(token.string), name_array);
+								}
+
+								for (isize i = 0; i < count; i++) {
+									ssaValue *name_ep = ssa_emit_array_epi(proc, name_array, i);
+									ssa_emit_store(proc, name_ep, ssa_make_const_string(a, fields[i]->token.string));
+								}
+
+								ssaValue *v_count = ssa_make_const_int(a, count);
+
+								ssaValue *names = ssa_emit_struct_ep(proc, tag, 1);
+								ssaValue *name_array_elem = ssa_array_elem(proc, name_array);
+
+								ssa_emit_store(proc, ssa_emit_struct_ep(proc, names, 0), name_array_elem);
+								ssa_emit_store(proc, ssa_emit_struct_ep(proc, names, 1), v_count);
+								ssa_emit_store(proc, ssa_emit_struct_ep(proc, names, 2), v_count);
+							}
+						}
+						break;
 					}
 				} break;
 

+ 4 - 1
src/ssa_print.c

@@ -225,6 +225,9 @@ void ssa_print_type(ssaFileBuffer *f, ssaModule *m, Type *t) {
 			i64 align_of_union = type_align_of(s, heap_allocator(), t);
 			ssa_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8]}", align_of_union, size_of_union);
 		} break;
+		case TypeRecord_Enum:
+			ssa_print_type(f, m, base_enum_type(t));
+			break;
 		}
 	} break;
 
@@ -299,7 +302,7 @@ void ssa_print_compound_element(ssaFileBuffer *f, ssaModule *m, ExactValue v, Ty
 }
 
 void ssa_print_exact_value(ssaFileBuffer *f, ssaModule *m, ExactValue value, Type *type) {
-	type = base_type(type);
+	type = base_type(base_enum_type(type));
 	if (is_type_float(type)) {
 		value = exact_value_to_float(value);
 	} else if (is_type_integer(type)) {

+ 1 - 0
src/tokenizer.c

@@ -108,6 +108,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
 	TOKEN_KIND(Token_struct,         "struct"), \
 	TOKEN_KIND(Token_union,          "union"), \
 	TOKEN_KIND(Token_raw_union,      "raw_union"), \
+	TOKEN_KIND(Token_enum,           "enum"), \
 	TOKEN_KIND(Token_vector,         "vector"), \
 	TOKEN_KIND(Token_using,          "using"), \
 	TOKEN_KIND(Token_asm,            "asm"), \