commit ba94026d37ce868f230a3f8bba4b9cfce442851b
parent 0776ad2e4eed263119947c3db9a092bb88b71b6c
Author: Bor Grošelj Simić <bgs@turminal.net>
Date: Sat, 21 May 2022 03:03:04 +0200
make enum type id algorithm rely on enum name
Before this change enum type id was calculated based on names and values
of enum field. The enum type wasn't really useful since the only way to
access it's values was through a type alias. The old method also
rendered some complex combinations of enum declarations and constant
declarations impossible to resolve.
This is now solved by special-casing enum types in the grammar. Unlike
other types they are not standalone and can only be used in type
declarations. The name given to an enum in an enum declaration is not
just an alias, but is a property of the enum type. The type id algorithm
for enums now relies on that name.
Signed-off-by: Bor Grošelj Simić <bgs@turminal.net>
Diffstat:
13 files changed, 160 insertions(+), 173 deletions(-)
diff --git a/include/ast.h b/include/ast.h
@@ -83,7 +83,6 @@ struct ast_type {
unsigned int flags;
union {
struct ast_list_type array;
- struct ast_enum_type _enum;
struct ast_function_type func;
struct ast_pointer_type pointer;
struct ast_list_type slice;
@@ -92,7 +91,10 @@ struct ast_type {
struct ast_tuple_type tuple;
struct {
struct identifier alias;
- bool unwrap;
+ union {
+ struct ast_enum_type _enum;
+ bool unwrap;
+ };
};
};
};
diff --git a/include/check.h b/include/check.h
@@ -109,7 +109,8 @@ struct unit {
enum idecl_type {
IDECL_DECL,
- IDECL_ENUM,
+ IDECL_ENUM_TYPE,
+ IDECL_ENUM_FLD,
};
// Keeps track of enum specific context required for enum field resolution
@@ -127,11 +128,17 @@ struct incomplete_declaration {
enum idecl_type type;
bool in_progress;
union {
- struct ast_decl decl;
+ struct {
+ struct ast_decl decl;
+ struct scope *enum_values;
+ };
struct incomplete_enum_field *field;
};
};
+void mkident(struct context *ctx, struct identifier *out,
+ const struct identifier *in);
+
const struct scope_object *scan_decl_finish(struct context *ctx,
const struct scope_object *obj, struct dimensions *dim);
diff --git a/include/types.h b/include/types.h
@@ -72,7 +72,6 @@ struct type_enum_value {
};
struct type_enum {
- enum type_storage storage;
struct type_enum_value *values;
};
@@ -155,9 +154,11 @@ struct type {
unsigned int flags;
size_t size, align;
union {
- struct type_alias alias;
+ struct {
+ struct type_alias alias;
+ struct scope *enum_values;
+ };
struct type_array array;
- struct type_enum _enum;
struct type_func func;
struct type_const _const;
struct type_pointer pointer;
diff --git a/src/check.c b/src/check.c
@@ -15,7 +15,7 @@
#include "types.h"
#include "util.h"
-static void
+void
mkident(struct context *ctx, struct identifier *out, const struct identifier *in)
{
identifier_dup(out, in);
@@ -740,7 +740,7 @@ type_promote(struct type_store *store,
return promote_const(a, b);
}
- if (db->storage == STORAGE_ENUM && da->storage == db->_enum.storage) {
+ if (db->storage == STORAGE_ENUM && da->storage == db->alias.type->storage) {
return b;
}
switch (da->storage) {
@@ -753,7 +753,7 @@ type_promote(struct type_store *store,
}
return NULL;
case STORAGE_ENUM:
- if (da->_enum.storage == db->storage) {
+ if (da->alias.type->storage == db->storage) {
return a;
}
return NULL;
@@ -3102,21 +3102,24 @@ check_type(struct context *ctx,
decl->type = DECL_TYPE;
const struct type *type =
type_store_lookup_atype(ctx->store, adecl->type);
- struct type _alias = {
- .storage = STORAGE_ALIAS,
- .alias = {
- .ident = decl->ident,
- .name = adecl->ident,
- .type = type,
- .exported = exported,
- },
- .size = type->size,
- .align = type->align,
- .flags = type->flags,
- };
- const struct type *alias =
- type_store_lookup_alias(ctx->store, &_alias);
- decl->_type = alias;
+ if (type->storage == STORAGE_ENUM) {
+ decl->_type = type;
+ } else {
+ struct type _alias = {
+ .storage = type->storage == STORAGE_ENUM ?
+ STORAGE_ENUM : STORAGE_ALIAS,
+ .alias = {
+ .ident = decl->ident,
+ .name = adecl->ident,
+ .type = type,
+ .exported = exported,
+ },
+ .size = type->size,
+ .align = type->align,
+ .flags = type->flags,
+ };
+ decl->_type = type_store_lookup_alias(ctx->store, &_alias);
+ }
return decl;
}
@@ -3218,7 +3221,7 @@ incomplete_enum_field_create(struct context *ctx, struct scope *imports,
incomplete_enum_field_create(ctx, imports, enum_scope,
etype, f->next);
}
- assert(etype->storage == STORAGE_ALIAS);
+ assert(etype->storage == STORAGE_ENUM);
struct incomplete_enum_field *field = xcalloc(1, sizeof(struct ast_type));
*field = (struct incomplete_enum_field){
.field = f,
@@ -3240,7 +3243,7 @@ incomplete_enum_field_create(struct context *ctx, struct scope *imports,
struct incomplete_declaration *fld =
incomplete_declaration_create(ctx, etype->loc, enum_scope,
&name, &localname);
- fld->type = IDECL_ENUM;
+ fld->type = IDECL_ENUM_FLD;
fld->imports = imports;
fld->field = field;
@@ -3252,7 +3255,7 @@ incomplete_enum_field_create(struct context *ctx, struct scope *imports,
};
fld = incomplete_declaration_create(ctx, etype->loc, ctx->scope,
&ident, &name);
- fld->type = IDECL_ENUM,
+ fld->type = IDECL_ENUM_FLD,
fld->imports = imports,
fld->field = field;
free(name.name);
@@ -3267,7 +3270,6 @@ incomplete_types_create(struct context *ctx, struct scope *imp, struct ast_decl
struct incomplete_declaration *idecl =
incomplete_declaration_create(ctx, decl->loc, ctx->scope,
&with_ns, &t->ident);
- idecl->type = IDECL_DECL;
idecl->decl = (struct ast_decl){
.decl_type = AST_DECL_TYPE,
.loc = decl->loc,
@@ -3276,18 +3278,12 @@ incomplete_types_create(struct context *ctx, struct scope *imp, struct ast_decl
};
idecl->imports = imp;
if (t->type->storage == STORAGE_ENUM) {
- struct ast_type *etype = xcalloc(1, sizeof(struct ast_type));
- *etype = (struct ast_type){
- .loc = t->type->loc,
- .storage = STORAGE_ALIAS,
- .flags = t->type->flags,
- .unwrap = false,
- };
- identifier_dup(&etype->alias, &t->ident);
- struct scope *enum_scope = NULL;
- scope_push(&enum_scope, SCOPE_ENUM);
- incomplete_enum_field_create(ctx, imp, enum_scope,
- etype, t->type->_enum.values);
+ scope_push(&idecl->enum_values, SCOPE_ENUM);
+ incomplete_enum_field_create(ctx, imp, idecl->enum_values,
+ t->type, t->type->_enum.values);
+ idecl->type = IDECL_ENUM_TYPE;
+ } else {
+ idecl->type = IDECL_DECL;
}
}
}
@@ -3399,7 +3395,7 @@ const struct scope_object *
scan_enum_field(struct context *ctx, struct incomplete_declaration *idecl)
{
assert(ctx->resolving_enum == NULL);
- assert(idecl->type == IDECL_ENUM);
+ assert(idecl->type == IDECL_ENUM_FLD);
struct identifier localname = {
.name = idecl->obj.ident.name
@@ -3423,17 +3419,15 @@ scan_enum_field(struct context *ctx, struct incomplete_declaration *idecl)
if (idecl->field->field->value) { // explicit value
// TODO: negative values in unsigned enums, too big values in
// signed enums
- const struct type *builtin = builtin_type_for_storage(
- type->alias.type->_enum.storage, false);
struct expression *initializer =
xcalloc(1, sizeof(struct expression));
check_expression(ctx, idecl->field->field->value,
- initializer, builtin);
+ initializer, type->alias.type);
handle_errors(ctx->errors);
expect(&idecl->field->field->value->loc,
- type_is_assignable(builtin, initializer->result),
+ type_is_assignable(type->alias.type, initializer->result),
"Enum value type is not assignable from initializer type");
initializer = lower_implicit_cast(type, initializer);
@@ -3473,6 +3467,19 @@ scan_enum_field(struct context *ctx, struct incomplete_declaration *idecl)
}
static const struct scope_object *
+scan_enum_type(struct context *ctx, struct incomplete_declaration *idecl)
+{
+ assert(idecl->type == IDECL_ENUM_TYPE);
+ struct type *type = (struct type *)type_store_lookup_atype(ctx->store,
+ idecl->decl.type.type);
+ type->enum_values = idecl->enum_values;
+ type->alias.exported = idecl->decl.exported;
+ type_store_lookup_alias(ctx->store, type);
+ return scope_insert(ctx->scope, O_TYPE,
+ &idecl->obj.ident, &idecl->obj.name, type, NULL);
+}
+
+static const struct scope_object *
scan_type(struct context *ctx, struct ast_type_decl *decl, bool exported,
struct dimensions dim)
{
@@ -3498,6 +3505,7 @@ scan_type(struct context *ctx, struct ast_type_decl *decl, bool exported,
scope_insert(ctx->scope, O_TYPE, &ident, &decl->ident, alias, NULL);
((struct type *)ret->type)->alias.type =
type_store_lookup_atype(ctx->store, decl->type);
+ assert(alias->alias.type->storage != STORAGE_ENUM);
return ret;
}
@@ -3585,10 +3593,16 @@ scan_decl_finish(struct context *ctx, const struct scope_object *obj,
// load this declaration's subunit context
ctx->unit->parent = idecl->imports;
- // TODO handle circular enum dependencies
- if (idecl->type == IDECL_ENUM) {
+ switch (idecl->type) {
+ case IDECL_ENUM_FLD:
+ // TODO handle circular enum dependencies
obj = scan_enum_field(ctx, idecl);
goto exit;
+ case IDECL_ENUM_TYPE:
+ obj = scan_enum_type(ctx, idecl);
+ goto exit;
+ case IDECL_DECL:
+ break;
}
// resolving a declaration that is already in progress -> cycle
diff --git a/src/eval.c b/src/eval.c
@@ -115,7 +115,7 @@ itrunc(const struct type *type, uintmax_t val)
case STORAGE_ALIAS:
return itrunc(type_dealias(type), val);
case STORAGE_ENUM:
- return itrunc(builtin_type_for_storage(type->_enum.storage, false), val);
+ return itrunc(type->alias.type, val);
case STORAGE_F32:
case STORAGE_F64:
case STORAGE_FCONST:
@@ -349,7 +349,7 @@ eval_const(struct context *ctx, struct expression *in, struct expression *out)
out->result = in->result;
enum type_storage storage = type_dealias(out->result)->storage;
if (storage == STORAGE_ENUM) {
- storage = type_dealias(out->result)->_enum.storage;
+ storage = type_dealias(out->result)->alias.type->storage;
}
struct array_constant **next;
switch (storage) {
diff --git a/src/gen.c b/src/gen.c
@@ -151,8 +151,7 @@ gen_store(struct gen_context *ctx,
gen_copy_memcpy(ctx, object, value);
return;
case STORAGE_ENUM:
- object.type = builtin_type_for_storage(ty->_enum.storage,
- (ty->flags & TYPE_CONST) != 0);
+ object.type = ty->alias.type;
break;
default:
break; // no-op
@@ -180,8 +179,7 @@ gen_load(struct gen_context *ctx, struct gen_value object)
case STORAGE_VALIST:
return object;
case STORAGE_ENUM:
- object.type = builtin_type_for_storage(ty->_enum.storage,
- (ty->flags & TYPE_CONST) != 0);
+ object.type = ty->alias.type;
break;
default:
break; // no-op
@@ -3437,7 +3435,7 @@ gen_data_item(struct gen_context *ctx, struct expression *expr,
}
break;
case STORAGE_ENUM:
- switch (type->_enum.storage) {
+ switch (type->alias.type->storage) {
case STORAGE_I8:
case STORAGE_U8:
item->type = QD_VALUE;
diff --git a/src/parse.c b/src/parse.c
@@ -410,11 +410,12 @@ static struct ast_expression *parse_binding_list(
static struct ast_expression *parse_object_selector(struct lexer *lexer);
static struct ast_type *
-parse_enum_type(struct lexer *lexer)
+parse_enum_type(struct identifier *ident, struct lexer *lexer)
{
struct token tok = {0};
struct ast_type *type = mktype(&lexer->loc);
type->storage = STORAGE_ENUM;
+ identifier_dup(&type->alias, ident);
struct ast_enum_field **next = &type->_enum.values;
switch (lex(lexer, &tok)) {
case T_LBRACE:
@@ -2438,7 +2439,7 @@ parse_type_decl(struct lexer *lexer, struct ast_type_decl *decl)
want(lexer, T_EQUAL, NULL);
switch (lex(lexer, &tok)) {
case T_ENUM:
- i->type = parse_enum_type(lexer);
+ i->type = parse_enum_type(&i->ident, lexer);
break;
default:
unlex(lexer, &tok);
diff --git a/src/qinstr.c b/src/qinstr.c
@@ -66,8 +66,6 @@ store_for_type(struct gen_context *ctx, const struct type *type)
}
break;
case STORAGE_ENUM:
- return store_for_type(ctx, builtin_type_for_storage(
- type->_enum.storage, false));
case STORAGE_ALIAS:
return store_for_type(ctx, type->alias.type);
case STORAGE_ARRAY:
@@ -135,8 +133,6 @@ load_for_type(struct gen_context *ctx, const struct type *type)
}
break;
case STORAGE_ENUM:
- return load_for_type(ctx, builtin_type_for_storage(
- type->_enum.storage, false));
case STORAGE_ALIAS:
return load_for_type(ctx, type->alias.type);
case STORAGE_ARRAY:
diff --git a/src/qtype.c b/src/qtype.c
@@ -224,9 +224,6 @@ qtype_lookup(struct gen_context *ctx,
case STORAGE_F64:
return &qbe_double;
case STORAGE_ENUM:
- return qtype_lookup(ctx,
- builtin_type_for_storage(type->_enum.storage, false),
- xtype);
case STORAGE_ALIAS:
return qtype_lookup(ctx, type->alias.type, xtype);
case STORAGE_ARRAY:
diff --git a/src/type_store.c b/src/type_store.c
@@ -643,11 +643,12 @@ type_init_from_atype(struct type_store *store,
return (struct dimensions){0};
}
-
if (obj->otype == O_SCAN) {
// an incomplete declaration was encountered
struct dimensions dim = {0};
- if (size_only) {
+ struct incomplete_declaration *idecl =
+ (struct incomplete_declaration *)obj;
+ if (size_only && idecl->type == IDECL_DECL) {
scan_decl_finish(store->check_context, obj, &dim);
type->size = dim.size;
type->align = dim.align;
@@ -665,7 +666,11 @@ type_init_from_atype(struct type_store *store,
return (struct dimensions){0};
}
- if (atype->unwrap) {
+ type->storage = obj->type->storage;
+ if (obj->type->storage == STORAGE_ENUM) {
+ assert(obj->type->enum_values);
+ type->enum_values = obj->type->enum_values;
+ } else if (atype->unwrap) {
*type = *type_dealias(obj->type);
break;
}
@@ -700,82 +705,19 @@ type_init_from_atype(struct type_store *store,
}
break;
case STORAGE_ENUM:
- type->_enum.storage = atype->_enum.storage;
- const struct type *storage =
- builtin_type_for_storage(type->_enum.storage, true);
- if (!type_is_integer(storage)
- && type->_enum.storage != STORAGE_RUNE) {
+ mkident(store->check_context, &type->alias.ident, &atype->alias);
+ identifier_dup(&type->alias.name, &atype->alias);
+ type->alias.type =
+ builtin_type_for_storage(atype->_enum.storage, false);
+ if (!type_is_integer(type->alias.type)
+ && type->alias.type->storage != STORAGE_RUNE) {
error(store->check_context, atype->loc,
"Enum storage must be an integer or rune");
*type = builtin_type_void;
return (struct dimensions){0};
}
- type->size = storage->size;
- type->align = storage->size;
- if (size_only) {
- break;
- }
-
- struct scope *scope = scope_push(
- &store->check_context->scope, SCOPE_ENUM);
- // TODO: Check for duplicates
- struct ast_enum_field *avalue = atype->_enum.values;
- struct type_enum_value **values = &type->_enum.values;
- intmax_t iimplicit = 0;
- uintmax_t uimplicit = 0;
- while (avalue) {
- struct type_enum_value *value = *values =
- xcalloc(sizeof(struct type_enum_value), 1);
- value->name = strdup(avalue->name);
- if (avalue->value != NULL) {
- struct expression in, out;
- check_expression(store->check_context,
- avalue->value, &in, storage);
- enum eval_result r =
- eval_expr(store->check_context, &in, &out);
- if (r != EVAL_OK) {
- error(store->check_context, atype->loc,
- "Cannot evaluate enum value at compile time");
- values = &value->next;
- avalue = avalue->next;
- continue;
- }
- if (!type_is_assignable(storage, out.result)) {
- error(store->check_context, atype->loc,
- "Cannot assign enum value to enum type");
- values = &value->next;
- avalue = avalue->next;
- continue;
- }
- if (type_is_signed(storage)) {
- iimplicit = out.constant.ival;
- } else {
- uimplicit = out.constant.uval;
- }
- }
- // TODO: Test that the value fits into this precision
- if (type_is_signed(storage)) {
- value->ival = iimplicit++;
- } else {
- value->uval = uimplicit++;
- }
-
- struct identifier name = {
- .name = value->name,
- .ns = NULL,
- };
- // TODO: This leaks:
- struct expression *vexpr = xcalloc(1, sizeof(struct expression));
- vexpr->type = EXPR_CONSTANT;
- vexpr->result = storage;
- vexpr->constant.uval = value->uval;
- scope_insert(scope, O_CONST, &name, &name, storage, vexpr);
-
- values = &value->next;
- avalue = avalue->next;
- }
- scope_pop(&store->check_context->scope);
- scope_free(scope);
+ type->size = type->alias.type->size;
+ type->align = type->alias.type->size;
break;
case STORAGE_FUNCTION:
type->size = SIZE_UNDEFINED;
diff --git a/src/typedef.c b/src/typedef.c
@@ -122,18 +122,25 @@ emit_const(const struct expression *expr, FILE *out)
fprintf(out, "\"");
break;
case STORAGE_ENUM:
- assert(expr->result->storage == STORAGE_ALIAS);
- char *ident = identifier_unparse(&expr->result->alias.ident);
- fprintf(out, "%s::", ident);
- free(ident);
- struct type_enum_value *ev = type_dealias(expr->result)->_enum.values;
- for (; ev; ev = ev->next) {
- if (ev->uval == val->uval) {
+ assert(expr->result->storage == STORAGE_ENUM);
+ struct scope_object *ev =
+ type_dealias(expr->result)->enum_values->objects;
+ for(; ev; ev = ev->lnext) {
+ if (ev->otype == O_SCAN) {
+ continue;
+ }
+ if (ev->type->alias.type->storage == STORAGE_CHAR
+ || !type_is_signed(ev->type->alias.type)) {
+ if (ev->value->constant.uval == val->uval) {
+ fprintf(out, "%ld", val->uval);
+ break;
+ }
+ } else if (ev->value->constant.ival == val->ival) {
+ fprintf(out, "%ld", val->ival);
break;
}
}
assert(ev);
- fprintf(out, "%s", ev->name);
break;
case STORAGE_ALIAS:
case STORAGE_ARRAY:
@@ -296,22 +303,10 @@ emit_type(const struct type *type, FILE *out)
ret &= emit_type(type->func.result, out);
break;
case STORAGE_ENUM:
- fprintf(out, "enum %s { ", type_storage_unparse(type->_enum.storage));
- for (const struct type_enum_value *ev = type->_enum.values;
- ev; ev = ev->next) {
- fprintf(out, "%s = ", ev->name);
- if (type_is_signed(type)) {
- fprintf(out, "%" PRIi64 "%s", ev->ival,
- storage_to_suffix(type->_enum.storage));
- } else {
- fprintf(out, "%" PRIu64 "%s", ev->uval,
- storage_to_suffix(type->_enum.storage));
- }
- if (ev->next) {
- fprintf(out, ", ");
- }
- }
- fprintf(out, "}");
+ ret &= type->alias.exported;
+ ident = identifier_unparse(&type->alias.ident);
+ fprintf(out, "%s", ident);
+ free(ident);
break;
case STORAGE_TUPLE:
fprintf(out, "(");
@@ -412,8 +407,25 @@ emit_decl_type(struct declaration *decl, FILE *out)
{
char *ident = identifier_unparse(&decl->ident);
fprintf(out, "export type %s = ", ident);
- assert(decl->_type->storage == STORAGE_ALIAS);
- emit_exported_type(decl->_type->alias.type, out);
+ assert(decl->_type->storage == STORAGE_ALIAS
+ || decl->_type->storage == STORAGE_ENUM);
+ if (decl->_type->storage == STORAGE_ENUM) {
+ const struct type *type = decl->_type;
+ fprintf(out, "enum %s { ",
+ type_storage_unparse(type->alias.type->storage));
+ for (const struct scope_object *ev = type->enum_values->objects;
+ ev; ev = ev->lnext) {
+ if (ev->otype == O_SCAN) {
+ continue;
+ }
+ fprintf(out, "%s = ", ev->name.name);
+ emit_const(ev->value, out);
+ fprintf(out, ", ");
+ }
+ fprintf(out, "}");
+ } else {
+ emit_exported_type(decl->_type->alias.type, out);
+ }
fprintf(out, "; // size: %zd, align: %zd, id: %u\n",
decl->_type->size, decl->_type->align, decl->_type->id);
}
diff --git a/src/types.c b/src/types.c
@@ -279,7 +279,7 @@ type_is_signed(const struct type *type)
{
enum type_storage storage = type_dealias(type)->storage;
if (storage == STORAGE_ENUM) {
- storage = type_dealias(type)->_enum.storage;
+ storage = type_dealias(type)->alias.type->storage;
}
switch (storage) {
case STORAGE_VOID:
@@ -362,6 +362,9 @@ type_hash(const struct type *type)
case STORAGE_VOID:
case STORAGE_STRING:
break; // built-ins
+ case STORAGE_ENUM:
+ hash = fnv1a(hash, type->alias.type->storage);
+ /* fallthrough */
case STORAGE_ALIAS:
for (const struct identifier *ident = &type->alias.ident; ident;
ident = ident->ns) {
@@ -383,14 +386,6 @@ type_hash(const struct type *type)
hash = fnv1a_u32(hash, type_hash(param->type));
}
break;
- case STORAGE_ENUM:
- hash = fnv1a(hash, type->_enum.storage);
- for (struct type_enum_value *value = type->_enum.values; value;
- value = value->next) {
- hash = fnv1a_s(hash, value->name);
- hash = fnv1a_u64(hash, value->uval);
- }
- break;
case STORAGE_FCONST:
case STORAGE_ICONST:
case STORAGE_RCONST:
diff --git a/tests/15-enums.ha b/tests/15-enums.ha
@@ -63,9 +63,31 @@ fn reject() void = {
assert(rt::compile("export let a: int = 0: enum{A, B}: int;") != 0);
};
+type interdependent1 = enum {
+ A = 0,
+ B = interdependent2::A + 1,
+ C = interdependent2::B + 1,
+};
+
+type interdependent2 = enum {
+ A = interdependent1::A + 1,
+ B = interdependent1::B + 1,
+ C = interdependent1::C + 1,
+};
+
+fn interdependent() void = {
+ assert(interdependent1::A == 0);
+ assert(interdependent2::A == 1);
+ assert(interdependent1::B == 2);
+ assert(interdependent2::B == 3);
+ assert(interdependent1::C == 4);
+ assert(interdependent2::C == 5);
+};
+
export fn main() void = {
implicit();
explicit();
storage();
reject();
+ interdependent();
};