Bring back enum but using iota

This commit is contained in:
Ginger Bill
2017-01-01 16:18:50 +00:00
parent 0c6775ca14
commit 6fef74317c
13 changed files with 315 additions and 47 deletions

View File

@@ -11,11 +11,27 @@ import {
win32 "sys/windows.odin";
}
proc main() {
var x = if false {
give 123;
} else {
give 321;
};
fmt.println(x);
type Thing enum f64 {
_, // Ignore first value
A = 1<<(10*iota),
B,
C,
D,
}
proc main() {
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);
}

View File

@@ -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);
}
}

View File

@@ -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, " @ ");

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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)) {

View File

@@ -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"), \