aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGinger Bill <bill@gingerbill.org>2017-01-01 16:18:50 +0000
committerGinger Bill <bill@gingerbill.org>2017-01-01 16:18:50 +0000
commit6fef74317cdd0e403fb913ebe965dc08b3dfb22d (patch)
tree9f25deab9276dc60847c0548d1024cfdbc3679d4
parent0c6775ca14ced37ac58a03ccad4028e225bda7d6 (diff)
Bring back `enum` but using iota
-rw-r--r--code/demo.odin28
-rw-r--r--core/_preload.odin41
-rw-r--r--core/fmt.odin10
-rw-r--r--core/mem.odin9
-rw-r--r--core/os_windows.odin2
-rw-r--r--src/checker/checker.c3
-rw-r--r--src/checker/expr.c140
-rw-r--r--src/checker/stmt.c15
-rw-r--r--src/checker/types.c49
-rw-r--r--src/parser.c14
-rw-r--r--src/ssa.c43
-rw-r--r--src/ssa_print.c5
-rw-r--r--src/tokenizer.c1
13 files changed, 314 insertions, 46 deletions
diff --git a/code/demo.odin b/code/demo.odin
index 422b68c77..bd4ab5dcd 100644
--- a/code/demo.odin
+++ b/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);
+
}
diff --git a/core/_preload.odin b/core/_preload.odin
index eae72ede1..2fafc408a 100644
--- a/core/_preload.odin
+++ b/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);
}
}
diff --git a/core/fmt.odin b/core/fmt.odin
index cfd442d2c..2b044131a 100644
--- a/core/fmt.odin
+++ b/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, " @ ");
diff --git a/core/mem.odin b/core/mem.odin
index f504df84e..a12135764 100644
--- a/core/mem.odin
+++ b/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);
}
diff --git a/core/os_windows.odin b/core/os_windows.odin
index 6dfeef42c..c45ac134f 100644
--- a/core/os_windows.odin
+++ b/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;
}
diff --git a/src/checker/checker.c b/src/checker/checker.c
index c125049ac..79ca9eef6 100644
--- a/src/checker/checker.c
+++ b/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) {
diff --git a/src/checker/expr.c b/src/checker/expr.c
index 77e9bd58e..7b9b0db52 100644
--- a/src/checker/expr.c
+++ b/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;
diff --git a/src/checker/stmt.c b/src/checker/stmt.c
index 45906b94e..5b7d7887f 100644
--- a/src/checker/stmt.c
+++ b/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;
diff --git a/src/checker/types.c b/src/checker/types.c
index 4e81c5a38..df11e1097 100644
--- a/src/checker/types.c
+++ b/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
}
}
}
@@ -998,6 +1031,18 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
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);
+ return sel;
+ }
+ }
}
} else if (!is_type_union(type)) {
for (isize i = 0; i < type->Record.field_count; i++) {
diff --git a/src/parser.c b/src/parser.c
index bfed77b0b..a7d9b4282 100644
--- a/src/parser.c
+++ b/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);
diff --git a/src/ssa.c b/src/ssa.c
index 1d0ef093c..d53b5fcf6 100644
--- a/src/ssa.c
+++ b/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;
diff --git a/src/ssa_print.c b/src/ssa_print.c
index bbca6b9db..81842e72a 100644
--- a/src/ssa_print.c
+++ b/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)) {
diff --git a/src/tokenizer.c b/src/tokenizer.c
index 2c42df1c5..a35ed070e 100644
--- a/src/tokenizer.c
+++ b/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"), \