commit 5ea0e291896fa9cf6b67af8c5089ada284f47b8c
parent 84edc0f7c3394bf46b59e52a7ee48f9a7605b9d6
Author: Bor Grošelj Simić <bgs@turminal.net>
Date: Sat, 21 May 2022 03:03:06 +0200
implement enum aliases
Signed-off-by: Bor Grošelj Simić <bgs@turminal.net>
Diffstat:
7 files changed, 148 insertions(+), 11 deletions(-)
diff --git a/include/check.h b/include/check.h
@@ -126,6 +126,7 @@ struct incomplete_declaration {
struct {
struct ast_decl decl;
struct scope *enum_values;
+ struct identifiers *enum_aliases;
};
struct incomplete_enum_field *field;
};
diff --git a/include/types.h b/include/types.h
@@ -72,7 +72,8 @@ struct type_enum_value {
};
struct type_enum {
- struct type_enum_value *values;
+ struct scope *values;
+ struct identifiers *aliases;
};
enum variadism {
@@ -156,7 +157,7 @@ struct type {
union {
struct {
struct type_alias alias;
- struct scope *enum_values;
+ struct type_enum _enum;
};
struct type_array array;
struct type_func func;
diff --git a/src/check.c b/src/check.c
@@ -3102,12 +3102,11 @@ check_type(struct context *ctx,
decl->type = DECL_TYPE;
const struct type *type =
type_store_lookup_atype(ctx->store, adecl->type);
- if (type->storage == STORAGE_ENUM) {
+ if (adecl->type->storage == STORAGE_ENUM) {
decl->_type = type;
} else {
struct type _alias = {
- .storage = type->storage == STORAGE_ENUM ?
- STORAGE_ENUM : STORAGE_ALIAS,
+ .storage = STORAGE_ALIAS,
.alias = {
.ident = decl->ident,
.name = adecl->ident,
@@ -3462,17 +3461,111 @@ scan_enum_field(struct context *ctx, struct incomplete_declaration *idecl)
}
ctx->resolving_enum = NULL;
+ for (struct identifiers *id = type->_enum.aliases; id; id = id->next) {
+ const struct scope_object *obj = scope_lookup(ctx->scope, &id->ident);
+ if (obj->otype == O_SCAN) {
+ obj = scan_decl_finish(ctx, obj, NULL);
+ }
+ struct identifier alias_ident, alias_name = {
+ .name = name.name,
+ .ns = &id->ident,
+ };
+ mkident(ctx, &alias_ident, &alias_name);
+ scope_insert(ctx->scope, O_CONST, &alias_ident,
+ &alias_name, obj->type, value);
+ }
+
scope_insert(idecl->field->enum_scope, O_CONST, &name, &localname, type, value);
return scope_insert(ctx->scope, O_CONST, &ident, &name, type, value);
}
+void
+enum_alias_list(struct context *ctx, const struct scope_object *obj,
+ struct identifiers *ids)
+{
+ struct incomplete_declaration *idecl;
+ const struct type *type;
+ switch (obj->otype) {
+ case O_SCAN:
+ idecl = (struct incomplete_declaration *)obj;
+ switch (idecl->type) {
+ case IDECL_ENUM_FLD:
+ return;
+ case IDECL_ENUM_TYPE:
+ if (!idecl->enum_aliases) {
+ idecl->enum_aliases = ids;
+ } else {
+ struct identifiers *id;
+ for (id = idecl->enum_aliases;
+ id->next; id = id->next);
+ id->next = ids;
+ }
+ return;
+ case IDECL_DECL:
+ if (idecl->decl.decl_type != AST_DECL_TYPE) {
+ return;
+ }
+ if (idecl->decl.type.type->storage != STORAGE_ALIAS) {
+ return;
+ }
+ struct identifiers *new = xcalloc(1, sizeof(struct identifiers));
+ identifier_dup(&new->ident, &idecl->decl.type.ident); new->next = ids;
+ ids = new;
+
+ struct scope *subunit = ctx->unit->parent;
+ ctx->unit->parent = idecl->imports;
+ obj = scope_lookup(ctx->scope, &idecl->decl.type.type->alias);
+ if (!obj) {
+ struct identifier *ident =
+ &idecl->decl.type.type->alias;
+ error(ctx, idecl->decl.loc, NULL,
+ "Unknown object '%s'",
+ identifier_unparse(ident));
+ return;
+ }
+ ctx->unit->parent = subunit;
+ enum_alias_list(ctx, obj, ids);
+ break;
+ }
+ break;
+ case O_TYPE:
+ type = type_dealias(obj->type);
+ if (type->storage != STORAGE_ENUM) {
+ return;
+ }
+ for (; ids; ids = ids->next) {
+ const struct scope_object *alias =
+ scope_lookup(ctx->scope, &ids->ident);
+ if (alias->otype == O_SCAN) {
+ alias = scan_decl_finish(ctx, alias, NULL);
+ }
+ for (obj = type->_enum.values->objects;
+ obj; obj = obj->lnext) {
+ struct identifier ident, name = {
+ .name = obj->name.name,
+ .ns = &ids->ident,
+ };
+ mkident(ctx, &ident, &name);
+ scope_insert(ctx->scope, O_CONST, &ident, &name,
+ alias->type, obj->value);
+ }
+ }
+ break;
+ case O_BIND:
+ case O_CONST:
+ case O_DECL:
+ return;
+ }
+}
+
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->_enum.values = idecl->enum_values;
+ type->_enum.aliases = idecl->enum_aliases;
type->alias.exported = idecl->decl.exported;
type_store_lookup_alias(ctx->store, type);
return scope_insert(ctx->scope, O_TYPE,
@@ -3505,7 +3598,6 @@ 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;
}
@@ -3852,6 +3944,17 @@ check_internal(struct type_store *ts,
}
scope_free(def_scope);
+ // Find enum aliases and store them in incomplete enum value declarations
+ for (const struct scope_object *obj = ctx.scope->objects;
+ obj; obj = obj->lnext) {
+ struct incomplete_declaration *idecl =
+ (struct incomplete_declaration *)obj;
+ if (idecl->type == IDECL_DECL
+ && idecl->decl.decl_type == AST_DECL_TYPE) {
+ enum_alias_list(&ctx, obj, NULL);
+ }
+ }
+
// Perform actual declaration resolution
ctx.resolving_enum = NULL;
for (const struct scope_object *obj = ctx.scope->objects;
diff --git a/src/type_store.c b/src/type_store.c
@@ -668,8 +668,7 @@ type_init_from_atype(struct type_store *store,
type->storage = obj->type->storage;
if (obj->type->storage == STORAGE_ENUM) {
- assert(obj->type->enum_values);
- type->enum_values = obj->type->enum_values;
+ type->_enum = obj->type->_enum;
} else if (atype->unwrap) {
*type = *type_dealias(obj->type);
break;
diff --git a/src/typedef.c b/src/typedef.c
@@ -124,7 +124,7 @@ emit_const(const struct expression *expr, FILE *out)
case STORAGE_ENUM:
assert(expr->result->storage == STORAGE_ENUM);
struct scope_object *ev =
- type_dealias(expr->result)->enum_values->objects;
+ type_dealias(expr->result)->_enum.values->objects;
for(; ev; ev = ev->lnext) {
if (ev->otype == O_SCAN) {
continue;
@@ -413,7 +413,7 @@ emit_decl_type(struct declaration *decl, FILE *out)
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;
+ for (const struct scope_object *ev = type->_enum.values->objects;
ev; ev = ev->lnext) {
if (ev->otype == O_SCAN) {
continue;
diff --git a/testmod/testmod.ha b/testmod/testmod.ha
@@ -1,2 +1,10 @@
+export type _enum = enum {
+ ONE = 1,
+ TWO = 2,
+ THREE = 3,
+};
+
+export type enum_alias = _enum;
+
export fn testfunc1() void = void;
export fn testfunc2() void = void;
diff --git a/tests/15-enums.ha b/tests/15-enums.ha
@@ -1,4 +1,5 @@
use rt;
+use testmod;
type implicit_values = enum {
ZERO,
@@ -84,10 +85,34 @@ fn interdependent() void = {
assert(interdependent2::C == 5);
};
+type alias = with_storage;
+
+type imported_alias = testmod::_enum;
+
+type imported_double_alias = testmod::enum_alias;
+
+fn aliases() void = {
+ assert(size(alias) == size(with_storage));
+ assert(alias::CAFE == with_storage::CAFE);
+ assert(alias::BABE == with_storage::BABE);
+ assert(alias::DEAD == with_storage::DEAD);
+ assert(alias::BEEF == with_storage::BEEF);
+
+ // test with alias of imported enum
+ assert(imported_alias::ONE == testmod::_enum::ONE);
+ assert(imported_alias::TWO == testmod::_enum::TWO);
+ assert(imported_alias::THREE == testmod::_enum::THREE);
+
+ assert(imported_double_alias::ONE == testmod::_enum::ONE);
+ assert(imported_double_alias::TWO == testmod::_enum::TWO);
+ assert(imported_double_alias::THREE == testmod::_enum::THREE);
+};
+
export fn main() void = {
implicit();
explicit();
storage();
reject();
interdependent();
+ aliases();
};