commit 91f8b24c598103677925cb0633f24cee5ae436ed
parent 485ad4fff22a12f9c5185ec6b738f5ee8c886758
Author: Bor Grošelj Simić <bor.groseljsimic@telemach.net>
Date: Sun, 5 Dec 2021 03:08:55 +0100
rewrite declaration resolution
See docs/declaration_solver.txt for an overview of the approach.
Signed-off-by: Bor Grošelj Simić <bor.groseljsimic@telemach.net>
Diffstat:
10 files changed, 827 insertions(+), 787 deletions(-)
diff --git a/docs/declaration_solver.txt b/docs/declaration_solver.txt
@@ -0,0 +1,69 @@
+First thing harec does after parsing the inputs is making sure all the global
+declarations are valid. A constraint unique to this compilation step is that
+unlike with sequential bindings in a compound expression, there is no explicit
+order in which the declarations should be traversed to validate them, the
+correct traversal order is given by the contents of declarations themselves. To
+make things even more complex, interdependent declarations may reside in
+different subunits, so there is quite a bit of scope juggling involved to
+achieve correct scope shadowing.
+
+Declarations come in five flavours - functions, constants, type aliases, global
+variables and enum fields.
+
+First, the constants defined upon harec invocation are checked and put into a
+dedicated scope. Then the imports for each subunit are loaded. This step requires
+the command line defines to already be in place. After the imports of a
+subunit are loaded, all of its declarations are marked incomplete and put into
+the unit scope. Duplicate declarations are caught and reported at this step.
+Special care needs to be taken with enums, by creating a scope for each of them
+and inserting all the fields into it in the correct order.
+
+At this point the defines can be copied from the dedicated scope into the unit
+scope, shadowing the declarations from the source files.
+
+With everything but the declarations in place, the core part of the algorithm
+is started:
+
+For each incomplete declaration:
+ (1) Save previous enum and subunit context, and load subunit context for
+ current declaration.
+ (2) If this declaration is marked as in-progress, error out because the
+ declaration part of a dependency cycle, otherwise mark this
+ declaration as in progress.
+ (3) Disambiguate between different declaration flavours:
+ - for functions, constants and globals and enum fields:
+ Check and evaluate as if all the dependencies are already resolved.
+ For enum fields, the relevant enum context has to be loaded and
+ implicit values need to be taken care of.
+
+ If an incomplete dependency is encountered along the way, first
+ resolve that dependency recursively and then continue with
+ resolution. When the check is done, insert the declaration into the
+ unit scope. Declaration is now considered complete.
+ - for type aliases:
+ Types have two distinct properties that both have to be computed
+ before a type is complete, their dimensions and their
+ representation. The approach taken can be summarized as follows:
+
+ (a) Compute secondary type's dimensions by traversing just enough
+ of its constituent types to be able to do so. If an incomplete
+ type alias is encountered, first compute that type's dimensions
+ recursively and then continue.
+ (b) Insert the complete alias into the type store and into the unit
+ scope.
+ (c) Compute secondary type's representation by traversing all of
+ its constituent types and repeating this entire process on
+ those that are still incomplete.
+
+ A valid type alias never references itself during (a), because such
+ a type would not be storable in memory. Types may reference
+ themselves during (c). Such types are called self-referential
+ types. Self-references during (c) require no special treatment,
+ because from step (b) onwards the type we are currently declaring
+ can be treated as a regular complete type.
+ (4) Remove the in progress mark
+ (5) Restore the saved subunit and enum context
+
+At this point all the incomplete declarations in the unit scope are shadowed by
+their complete counterparts. From here on, no special considerations regarding
+incomplete declarations apply and check can proceed accordingly.
diff --git a/include/check.h b/include/check.h
@@ -2,11 +2,11 @@
#define HARE_CHECK_H
#include <stdbool.h>
#include "identifier.h"
+#include "scope.h"
#include "types.h"
#include "type_store.h"
struct expression;
-struct scope;
#define MODCACHE_BUCKETS 256
@@ -41,6 +41,7 @@ struct context {
bool is_test;
struct scope *unit;
struct scope *scope;
+ struct scope *resolving_enum;
bool deferring;
int id;
struct errors *errors;
@@ -106,6 +107,34 @@ struct unit {
struct imports *imports;
};
+enum idecl_type {
+ IDECL_DECL,
+ IDECL_ENUM,
+};
+
+// Keeps track of enum specific context required for enum field resolution
+struct incomplete_enum_field {
+ struct ast_enum_field *field;
+ struct ast_type *type;
+ struct scope *enum_scope;
+};
+
+// Keeps track of context required to resolve a declaration or an enum field
+// Extends the scope_object struct so it can be inserted into a scope
+struct incomplete_declaration {
+ const struct scope_object obj;
+ struct scope *imports; // the scope of this declaration's subunit
+ enum idecl_type type;
+ bool in_progress;
+ union {
+ struct ast_decl decl;
+ struct incomplete_enum_field *field;
+ };
+};
+
+const struct scope_object *scan_decl_finish(struct context *ctx,
+ const struct scope_object *obj, struct dimensions *dim);
+
struct scope *check(struct type_store *ts,
bool is_test,
struct define *defines,
diff --git a/include/expr.h b/include/expr.h
@@ -271,7 +271,7 @@ struct expression_measure {
enum measure_operator op;
union {
struct expression *value;
- const struct type *type;
+ struct dimensions dimensions;
// TODO: Field selection
};
};
diff --git a/include/scope.h b/include/scope.h
@@ -9,6 +9,7 @@ enum object_type {
O_BIND,
O_CONST,
O_DECL,
+ O_SCAN,
O_TYPE,
};
@@ -52,7 +53,7 @@ struct scope {
struct yield *yields;
// Linked list in insertion order
- // Used for function parameters, where order matters
+ // Used for function parameters and enum values, where order matters
struct scope_object *objects;
struct scope_object **next;
diff --git a/include/type_store.h b/include/type_store.h
@@ -26,6 +26,9 @@ struct ast_type;
const struct type *type_store_lookup_atype(
struct type_store *store, const struct ast_type *atype);
+struct dimensions type_store_lookup_dimensions(
+ struct type_store *store, const struct ast_type *atype);
+
const struct type *builtin_type_for_storage(
enum type_storage storage, bool is_const);
@@ -42,8 +45,7 @@ const struct type *type_store_lookup_slice(struct type_store *store,
const struct type *members);
const struct type *type_store_lookup_alias(struct type_store *store,
- const struct identifier *ident, const struct type *secondary,
- bool exported);
+ const struct type *secondary, bool exported);
const struct type *type_store_lookup_tagged(struct type_store *store,
struct type_tagged_union *tags);
diff --git a/include/types.h b/include/types.h
@@ -107,6 +107,7 @@ struct struct_field {
char *name;
const struct type *type;
size_t offset;
+ size_t size;
struct struct_field *next;
};
@@ -153,6 +154,11 @@ struct type {
};
};
+struct dimensions {
+ size_t size;
+ size_t align;
+};
+
const struct type *type_dereference(const struct type *type);
const struct type *type_dealias(const struct type *type);
const struct struct_field *type_get_field(
diff --git a/src/check.c b/src/check.c
@@ -128,17 +128,28 @@ check_expr_access(struct context *ctx,
expr->type = EXPR_ACCESS;
expr->access.type = aexpr->access.type;
- const struct scope_object *obj;
+ const struct scope_object *obj = NULL;
switch (expr->access.type) {
case ACCESS_IDENTIFIER:
- obj = scope_lookup(ctx->scope, &aexpr->access.ident);
- char buf[1024];
- identifier_unparse_static(&aexpr->access.ident, buf, sizeof(buf));
+ if (ctx->resolving_enum) {
+ obj = scope_lookup(ctx->resolving_enum, &aexpr->access.ident);
+ }
+ if (!obj) {
+ obj = scope_lookup(ctx->scope, &aexpr->access.ident);
+ }
if (!obj) {
+ char buf[1024];
+ identifier_unparse_static(&aexpr->access.ident,
+ buf, sizeof(buf));
error(ctx, aexpr->loc, expr,
"Unknown object '%s'", buf);
return;
}
+ if (obj->otype == O_SCAN) {
+ // complete the declaration first
+ obj = scan_decl_finish(ctx, obj, NULL);
+ }
+
switch (obj->otype) {
case O_CONST:
// Lower constants
@@ -159,6 +170,8 @@ check_expr_access(struct context *ctx,
expr->type = EXPR_CONSTANT;
expr->result = obj->type;
break;
+ case O_SCAN:
+ assert(0); // handled above
}
break;
case ACCESS_INDEX:
@@ -1974,7 +1987,7 @@ check_expr_measure(struct context *ctx,
}
break;
case M_SIZE:
- expr->measure.type = type_store_lookup_atype(
+ expr->measure.dimensions = type_store_lookup_dimensions(
ctx->store, aexpr->measure.type);
break;
case M_OFFSET:
@@ -2338,6 +2351,10 @@ check_expr_struct(struct context *ctx,
"Unknown type alias");
return;
}
+
+ // Do we want globals without type hints?
+ assert(obj->otype != O_SCAN);
+
if (obj->otype != O_TYPE) {
error(ctx, aexpr->loc, expr,
"Name does not refer to a type");
@@ -2868,7 +2885,6 @@ check_const(struct context *ctx,
struct declaration *decl = xcalloc(1, sizeof(struct declaration));
const struct scope_object *obj = scope_lookup(
ctx->unit, &adecl->ident);
- assert(obj && obj->otype == O_CONST);
decl->type = DECL_CONST;
decl->constant.type = type;
decl->constant.value = obj->value;
@@ -2892,7 +2908,6 @@ check_function(struct context *ctx,
};
const struct type *fntype = type_store_lookup_atype(
ctx->store, &fn_atype);
- assert(fntype); // Invariant
ctx->fntype = fntype;
expect(&adecl->loc,
@@ -3017,8 +3032,19 @@ check_type(struct context *ctx,
decl->type = DECL_TYPE;
const struct type *type =
type_store_lookup_atype(ctx->store, adecl->type.type);
+ struct type _alias = {
+ .storage = STORAGE_ALIAS,
+ .alias = {
+ .ident = decl->ident,
+ .type = type,
+ .exported = adecl->exported,
+ },
+ .size = type->size,
+ .align = type->align,
+ .flags = type->flags,
+ };
const struct type *alias =
- type_store_lookup_alias(ctx->store, &decl->ident, type, decl->exported);
+ type_store_lookup_alias(ctx->store, &_alias, true);
decl->_type = alias;
return decl;
}
@@ -3080,406 +3106,124 @@ check_declarations(struct context *ctx,
return next;
}
-// Set is_static = true if you intend to evaluate this expression and
-// statically allocate the result right away.
-static bool expr_is_specified(struct context *ctx,
- const struct ast_expression *aexpr, bool is_static);
-
-static bool
-type_is_specified(const struct type *type, bool is_static) {
- assert(type);
- switch (type->storage) {
- case STORAGE_BOOL:
- case STORAGE_CHAR:
- case STORAGE_F32:
- case STORAGE_F64:
- case STORAGE_FCONST:
- case STORAGE_I16:
- case STORAGE_I32:
- case STORAGE_I64:
- case STORAGE_I8:
- case STORAGE_ICONST:
- case STORAGE_INT:
- case STORAGE_NULL:
- case STORAGE_RUNE:
- case STORAGE_SIZE:
- case STORAGE_STRING:
- case STORAGE_U16:
- case STORAGE_U32:
- case STORAGE_U64:
- case STORAGE_U8:
- case STORAGE_UINT:
- case STORAGE_UINTPTR:
- case STORAGE_VOID:
- return true;
- case STORAGE_ALIAS:
- if (type->alias.type == NULL) {
- return false;
- }
- return type_is_specified(type->alias.type, is_static);
- case STORAGE_ARRAY:
- return type_is_specified(type->array.members, is_static);
- case STORAGE_SLICE:
- if (!is_static) {
- return true;
- }
- return type_is_specified(type->array.members, is_static);
- case STORAGE_ENUM:
- return true;
- case STORAGE_FUNCTION:
- for (struct type_func_param *param = type->func.params;
- param; param = param->next) {
- if (!type_is_specified(param->type, is_static)) {
- return false;
- }
- }
- return type_is_specified(type->func.result, is_static);
- case STORAGE_POINTER:
- return true;
- case STORAGE_STRUCT:
- case STORAGE_UNION:
- for (const struct struct_field *field =
- type->struct_union.fields;
- field; field = field->next) {
- if (!type_is_specified(field->type, is_static)) {
- return false;
- }
- }
- return true;
- case STORAGE_TAGGED:
- for (const struct type_tagged_union *ttype = &type->tagged;
- ttype; ttype = ttype->next) {
- if (!type_is_specified(ttype->type, is_static)) {
- return false;
- }
- }
- return true;
- case STORAGE_TUPLE:
- for (const struct type_tuple *ttype = &type->tuple;
- ttype; ttype = ttype->next) {
- if (!type_is_specified(ttype->type, is_static)) {
- return false;
- }
- }
- return true;
- }
- abort(); // Unreachable
+static struct incomplete_declaration *
+incomplete_declaration_create(struct context *ctx, struct location loc,
+ struct scope *scope, const struct identifier *ident,
+ const struct identifier *name)
+{
+ struct incomplete_declaration *idecl =
+ (struct incomplete_declaration *)scope_lookup(scope, ident);
+ if (idecl) {
+ error(ctx, loc, NULL, "Duplicate global identifier '%s'",
+ identifier_unparse(ident));
+ return idecl;
+ }
+ idecl = xcalloc(1, sizeof(struct incomplete_declaration));
+
+ scope_object_init((struct scope_object *)idecl, O_SCAN,
+ ident, name, NULL, NULL);
+ scope_insert_from_object(scope, (struct scope_object *)idecl);
+ return idecl;
}
-static bool
-atype_is_specified(struct context *ctx,
- const struct ast_type *atype, bool is_static)
+static void
+incomplete_enum_field_create(struct context *ctx, struct scope *imports,
+ struct scope *enum_scope, struct ast_type *etype,
+ struct ast_enum_field *f)
{
- if (!atype) {
- return true;
- }
+ // We have to process the last field first
+ // This way, objects in enum_scope will have lnext pointing to
+ // the previous element, which is important for implicit enum values.
+ if (f->next) {
+ incomplete_enum_field_create(ctx, imports, enum_scope,
+ etype, f->next);
+ }
+ assert(etype->storage == STORAGE_ALIAS);
+ struct incomplete_enum_field *field = xcalloc(1, sizeof(struct ast_type));
+ *field = (struct incomplete_enum_field){
+ .field = f,
+ .type = etype,
+ .enum_scope = enum_scope,
+ };
- const struct scope_object *object;
- switch (atype->storage) {
- case STORAGE_BOOL:
- case STORAGE_CHAR:
- case STORAGE_F32:
- case STORAGE_F64:
- case STORAGE_FCONST:
- case STORAGE_I16:
- case STORAGE_I32:
- case STORAGE_I64:
- case STORAGE_I8:
- case STORAGE_ICONST:
- case STORAGE_INT:
- case STORAGE_NULL:
- case STORAGE_RUNE:
- case STORAGE_SIZE:
- case STORAGE_STRING:
- case STORAGE_U16:
- case STORAGE_U32:
- case STORAGE_U64:
- case STORAGE_U8:
- case STORAGE_UINT:
- case STORAGE_UINTPTR:
- case STORAGE_VOID:
- return true;
- case STORAGE_ALIAS:
- object = scope_lookup(ctx->scope, &atype->alias);
- if (object == NULL) {
- return false;
- }
- assert(object->otype == O_TYPE);
- const struct type *secondary = object->type;
- assert(secondary->storage == STORAGE_ALIAS);
- return type_is_specified(secondary->alias.type, is_static);
- case STORAGE_ARRAY:
- return atype_is_specified(ctx, atype->array.members, is_static)
- && expr_is_specified(ctx, atype->array.length, is_static);
- case STORAGE_SLICE:
- if (!is_static) {
- return true;
- }
- return atype_is_specified(ctx, atype->array.members, is_static);
- case STORAGE_ENUM:
- for (struct ast_enum_field *field = atype->_enum.values;
- field; field = field->next) {
- if (!expr_is_specified(ctx, field->value, is_static)) {
- return false;
- }
- }
- return true;
- case STORAGE_FUNCTION:
- for (struct ast_function_parameters *param = atype->func.params;
- param; param = param->next) {
- if (!atype_is_specified(ctx, param->type, is_static)) {
- return false;
- }
- }
- return atype_is_specified(ctx, atype->func.result, is_static);
- case STORAGE_POINTER:
- return true;
- case STORAGE_STRUCT:
- case STORAGE_UNION:
- for (const struct ast_struct_union_type *stype =
- &atype->struct_union;
- stype; stype = stype->next) {
- if (!expr_is_specified(ctx, stype->offset, is_static)) {
- return false;
- }
- if (!atype_is_specified(ctx, stype->type, is_static)) {
- return false;
- }
- }
- return true;
- case STORAGE_TAGGED:
- for (const struct ast_tagged_union_type *ttype =
- &atype->tagged_union;
- ttype; ttype = ttype->next) {
- if (!atype_is_specified(ctx, ttype->type, is_static)) {
- return false;
- }
- }
- return true;
- case STORAGE_TUPLE:
- for (const struct ast_tuple_type *ttype = &atype->tuple;
- ttype; ttype = ttype->next) {
- if (!atype_is_specified(ctx, ttype->type, is_static)) {
- return false;
- }
- }
- return true;
- }
- abort(); // Unreachable
+ struct identifier name = {
+ .name = strdup(f->name),
+ };
+ struct incomplete_declaration *fld =
+ incomplete_declaration_create(ctx, etype->loc, enum_scope,
+ &name, &name);
+ fld->type = IDECL_ENUM;
+ fld->imports = imports;
+ fld->field = field;
+
+ struct identifier name_ns = {
+ .name = etype->alias.name,
+ .ns = etype->alias.ns,
+ };
+ struct identifier ident = {
+ .name = strdup(f->name),
+ .ns = &name_ns,
+ };
+ struct identifier _ident = {0};
+ mkident(ctx, &_ident, &etype->alias);
+ struct identifier vident = {
+ .name = strdup(f->name),
+ .ns = &_ident,
+ };
+ fld = incomplete_declaration_create(ctx, etype->loc, ctx->scope,
+ &ident, &vident);
+ fld->type = IDECL_ENUM,
+ fld->imports = imports,
+ fld->field = field;
+ free(name.name);
}
-static bool
-expr_is_specified(struct context *ctx,
- const struct ast_expression *aexpr,
- bool is_static)
+static void
+incomplete_types_create(struct context *ctx, struct scope *imp, struct ast_decl *decl)
{
- if (!aexpr) {
- return true;
- }
-
- switch (aexpr->type) {
- case EXPR_ACCESS:
- switch (aexpr->access.type) {
- case ACCESS_IDENTIFIER:
- // XXX: Is this right?
- //return scope_lookup(ctx->scope, &aexpr->access.ident);
- return true;
- case ACCESS_INDEX:
- return expr_is_specified(ctx, aexpr->access.array, is_static)
- && expr_is_specified(ctx, aexpr->access.index, is_static);
- case ACCESS_FIELD:
- return expr_is_specified(ctx, aexpr->access._struct, is_static);
- case ACCESS_TUPLE:
- return expr_is_specified(ctx, aexpr->access.tuple, is_static)
- && expr_is_specified(ctx, aexpr->access.value, is_static);
- }
- assert(0);
- case EXPR_ALLOC:
- return expr_is_specified(ctx, aexpr->alloc.expr, is_static)
- && expr_is_specified(ctx, aexpr->alloc.cap, is_static);
- case EXPR_APPEND:
- for (const struct ast_append_values *value =
- aexpr->append.values;
- value; value = value->next) {
- if (!expr_is_specified(ctx, value->expr, is_static)) {
- return false;
- }
- }
- return expr_is_specified(ctx, aexpr->append.expr, is_static)
- && expr_is_specified(ctx, aexpr->append.variadic, is_static);
- case EXPR_ASSERT:
- return expr_is_specified(ctx, aexpr->assert.cond, is_static)
- && expr_is_specified(ctx, aexpr->assert.message, is_static);
- case EXPR_ASSIGN:
- return expr_is_specified(ctx, aexpr->assign.object, is_static)
- && expr_is_specified(ctx, aexpr->assign.value, is_static);
- case EXPR_BINARITHM:
- return expr_is_specified(ctx, aexpr->binarithm.lvalue, is_static)
- && expr_is_specified(ctx, aexpr->binarithm.rvalue, is_static);
- case EXPR_BINDING:
- for (const struct ast_expression_binding *binding =
- &aexpr->binding;
- binding; binding = binding->next) {
- if (!expr_is_specified(ctx, binding->initializer, is_static)) {
- return false;
- }
- if (!atype_is_specified(ctx, binding->type, is_static)) {
- return false;
- }
- }
- return true;
- case EXPR_COMPOUND:
- for (const struct ast_expression_list *list = &aexpr->compound.list;
- list; list = list->next) {
- if (!expr_is_specified(ctx, list->expr, is_static)) {
- return false;
- }
- }
- return true;
- case EXPR_BREAK:
- case EXPR_CONTINUE:
- return true;
- case EXPR_CALL:
- for (struct ast_call_argument *arg = aexpr->call.args; arg;
- arg = arg->next) {
- if (!expr_is_specified(ctx, arg->value, is_static)) {
- return false;
- }
- }
- return expr_is_specified(ctx, aexpr->call.lvalue, is_static);
- case EXPR_CAST:
- return expr_is_specified(ctx, aexpr->cast.value, is_static)
- && atype_is_specified(ctx, aexpr->cast.type, is_static);
- case EXPR_CONSTANT:
- if (aexpr->constant.storage == STORAGE_ARRAY) {
- for (struct ast_array_constant *aconst =
- aexpr->constant.array;
- aconst; aconst = aconst->next) {
- if (!expr_is_specified(ctx, aconst->value, is_static)) {
- return false;
- }
- }
- }
- return true;
- case EXPR_DEFER:
- return expr_is_specified(ctx, aexpr->defer.deferred, is_static);
- case EXPR_DELETE:
- return expr_is_specified(ctx, aexpr->delete.expr, is_static);
- case EXPR_FOR:
- return expr_is_specified(ctx, aexpr->_for.bindings, is_static)
- && expr_is_specified(ctx, aexpr->_for.cond, is_static)
- && expr_is_specified(ctx, aexpr->_for.afterthought, is_static)
- && expr_is_specified(ctx, aexpr->_for.body, is_static);
- case EXPR_FREE:
- return expr_is_specified(ctx, aexpr->free.expr, is_static);
- case EXPR_IF:
- return expr_is_specified(ctx, aexpr->_if.cond, is_static)
- && expr_is_specified(ctx, aexpr->_if.true_branch, is_static)
- && expr_is_specified(ctx, aexpr->_if.false_branch, is_static);
- case EXPR_INSERT:
- assert(0); // TODO
- case EXPR_MATCH:
- for (struct ast_match_case *mcase = aexpr->match.cases; mcase;
- mcase = mcase->next) {
- if (!atype_is_specified(ctx, mcase->type, is_static)) {
- return false;
- }
- for (const struct ast_expression_list *list = &mcase->exprs;
- list; list = list->next) {
- if (!expr_is_specified(ctx, list->expr, is_static)) {
- return false;
- }
- }
- }
- return expr_is_specified(ctx, aexpr->match.value, is_static);
- case EXPR_MEASURE:
- switch (aexpr->measure.op) {
- case M_LEN:
- return expr_is_specified(ctx, aexpr->measure.value, is_static);
- case M_SIZE:
- return atype_is_specified(ctx, aexpr->measure.type, is_static);
- case M_OFFSET:
- return expr_is_specified(ctx, aexpr->measure.value, is_static);
- }
- assert(0);
- case EXPR_PROPAGATE:
- return expr_is_specified(ctx, aexpr->propagate.value, is_static);
- case EXPR_RETURN:
- return expr_is_specified(ctx, aexpr->_return.value, is_static);
- case EXPR_SLICE:
- return expr_is_specified(ctx, aexpr->slice.object, is_static)
- && expr_is_specified(ctx, aexpr->slice.start, is_static)
- && expr_is_specified(ctx, aexpr->slice.end, is_static);
- case EXPR_STRUCT:
- for (struct ast_field_value *field = aexpr->_struct.fields;
- field; field = field->next) {
- if (field->type && !atype_is_specified(ctx, field->type, is_static)) {
- return false;
- }
- if (!expr_is_specified(ctx, field->initializer, is_static)) {
- return false;
- }
- }
- if (aexpr->_struct.type.name) {
- if (!scope_lookup(ctx->scope, &aexpr->_struct.type)) {
- return false;
- }
- }
- return true;
- case EXPR_SWITCH:
- for (struct ast_switch_case *scase = aexpr->_switch.cases;
- scase; scase =scase->next) {
- for (struct ast_case_option *opt = scase->options;
- opt; opt = opt->next) {
- if (!expr_is_specified(ctx, opt->value, is_static)) {
- return false;
- }
- }
- for (const struct ast_expression_list *list = &scase->exprs;
- list; list = list->next) {
- if (!expr_is_specified(ctx, list->expr, is_static)) {
- return false;
- }
- }
- }
- return expr_is_specified(ctx, aexpr->_switch.value, is_static);
- case EXPR_TUPLE:
- for (const struct ast_expression_tuple *tuple = &aexpr->tuple;
- tuple; tuple = tuple->next) {
- if (!expr_is_specified(ctx, tuple->expr, is_static)) {
- return false;
- }
+ for (struct ast_type_decl *t = &decl->type; t; t = t->next) {
+ struct identifier with_ns = {0};
+ mkident(ctx, &with_ns, &t->ident);
+ 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,
+ .type = *t,
+ .exported = decl->exported,
+ };
+ 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(&enum_scope, SCOPE_ENUM);
}
- return true;
- case EXPR_UNARITHM:
- return expr_is_specified(ctx, aexpr->unarithm.operand, is_static);
- case EXPR_YIELD:
- return expr_is_specified(ctx, aexpr->control.value, is_static);
}
- assert(0); // Unreachable
}
-static bool
+const struct scope_object *
scan_const(struct context *ctx, const struct ast_global_decl *decl)
{
- // TODO: Get rid of this once the type store bubbles up errors
- for (const struct ast_global_decl *d = decl; d; d = d->next) {
- if (!atype_is_specified(ctx, d->type, true)
- || !expr_is_specified(ctx, d->init, true)) {
- return false;
- }
- }
assert(!decl->symbol); // Invariant
const struct type *type = type_store_lookup_atype(
ctx->store, decl->type);
+ expect(&decl->type->loc, type != NULL, "Unable to resolve type");
// TODO: Free the initializer
- struct expression *initializer =
- xcalloc(1, sizeof(struct expression));
+ struct expression *initializer = xcalloc(1, sizeof(struct expression));
check_expression(ctx, decl->init, initializer, type);
- if (ctx->errors != NULL) {
- return false;
- }
expect(&decl->init->loc, type_is_assignable(type, initializer->result),
"Constant type is not assignable from initializer type");
@@ -3493,29 +3237,20 @@ scan_const(struct context *ctx, const struct ast_global_decl *decl)
struct identifier ident = {0};
mkident(ctx, &ident, &decl->ident);
- scope_insert(ctx->unit, O_CONST, &ident, &decl->ident, type, value);
- return true;
+ return scope_insert(ctx->unit, O_CONST, &ident,
+ &decl->ident, type, value);
}
-static bool
+const struct scope_object *
scan_function(struct context *ctx, const struct ast_function_decl *decl)
{
- if ((decl->flags & FN_TEST) && !ctx->is_test) {
- return true;
- }
const struct ast_type fn_atype = {
.storage = STORAGE_FUNCTION,
.flags = TYPE_CONST,
.func = decl->prototype,
};
- // TODO: Get rid of this once the type store bubbles up errors
- // TODO: Do we want to do something on !expr_is_specified(ctx, decl->body)?
- if (!atype_is_specified(ctx, &fn_atype, false)) {
- return false;
- }
const struct type *fntype = type_store_lookup_atype(
ctx->store, &fn_atype);
- assert(fntype);
if (!(decl->flags & FN_TEST)) {
struct identifier ident = {0};
@@ -3526,26 +3261,17 @@ scan_function(struct context *ctx, const struct ast_function_decl *decl)
} else {
ident = decl->ident;
}
- scope_insert(ctx->unit, O_DECL, &ident, &decl->ident, fntype, NULL);
+ return scope_insert(ctx->unit, O_DECL, &ident,
+ &decl->ident, fntype, NULL);
}
-
- char buf[1024];
- identifier_unparse_static(&decl->ident, buf, sizeof(buf));
- return true;
+ return NULL;
}
-static bool
+const struct scope_object *
scan_global(struct context *ctx, const struct ast_global_decl *decl)
{
- // TODO: Get rid of this once the type store bubbles up errors
- if (!atype_is_specified(ctx, decl->type, true)
- || !expr_is_specified(ctx, decl->init, true)) {
- return false;
- }
-
const struct type *type = type_store_lookup_atype(
ctx->store, decl->type);
- assert(type);
if (decl->type->storage == STORAGE_ARRAY
&& decl->type->array.contextual) {
@@ -3553,9 +3279,6 @@ scan_global(struct context *ctx, const struct ast_global_decl *decl)
struct expression *initializer =
xcalloc(1, sizeof(struct expression));
check_expression(ctx, decl->init, initializer, type);
- if (ctx->errors != NULL) {
- return false;
- }
expect(&decl->init->loc,
initializer->result->storage == STORAGE_ARRAY,
"Cannot infer array length from non-array type");
@@ -3571,140 +3294,267 @@ scan_global(struct context *ctx, const struct ast_global_decl *decl)
} else {
mkident(ctx, &ident, &decl->ident);
}
- scope_insert(ctx->unit, O_DECL, &ident, &decl->ident, type, NULL);
- return true;
+ return scope_insert(ctx->unit, O_DECL, &ident, &decl->ident, type, NULL);
}
-static bool
-scan_type(struct context *ctx, const struct ast_type_decl *decl, bool exported)
+const struct scope_object *
+scan_enum_field(struct context *ctx, struct incomplete_declaration *idecl)
{
- // TODO: Get rid of this once the type store bubbles up errors
- if (!atype_is_specified(ctx, decl->type, false)) {
- return false;
+ assert(ctx->resolving_enum == NULL);
+ assert(idecl->type == IDECL_ENUM);
+
+ struct identifier name = {
+ .name = idecl->obj.ident.name
+ };
+ struct identifier ident = idecl->obj.ident, vident = idecl->obj.name;
+
+ idecl = (struct incomplete_declaration *)scope_lookup(
+ idecl->field->enum_scope, &name);
+ if (idecl->obj.otype != O_SCAN) {
+ return &idecl->obj;
}
+
+ ctx->resolving_enum = idecl->field->enum_scope;
+ // TODO set this type's contents to the correct builtin type
const struct type *type =
- type_store_lookup_atype(ctx->store, decl->type);
+ type_store_lookup_atype(ctx->store, idecl->field->type);
- struct identifier ident = {0};
- if (!decl->ident.ns) {
- mkident(ctx, &ident, &decl->ident);
- } else {
- ident = decl->ident;
- }
+ struct expression *value =
+ xcalloc(1, sizeof(struct expression));
+ value->result = type;
+ 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);
- const struct type *alias =
- type_store_lookup_alias(ctx->store, &ident, type, exported);
- scope_insert(ctx->unit, O_TYPE, &ident, &decl->ident, alias, NULL);
-
- if (type->storage == STORAGE_ENUM) {
- for (struct type_enum_value *value = type->_enum.values; value;
- value = value->next) {
- struct ast_type atype = {
- .loc = decl->type->loc,
- .storage = STORAGE_ALIAS,
- .flags = 0,
- .unwrap = false,
- .alias = decl->ident,
- };
- const struct type *alias =
- type_store_lookup_atype(ctx->store, &atype);
+ struct expression *initializer =
+ xcalloc(1, sizeof(struct expression));
+ check_expression(ctx, idecl->field->field->value,
+ initializer, builtin);
- struct expression *expr =
- xcalloc(sizeof(struct expression), 1);
- expr->type = EXPR_CONSTANT;
- expr->result = alias;
- if (type_is_signed(alias)) {
- expr->constant.ival = value->ival;
+ handle_errors(ctx->errors);
+ expect(&idecl->field->field->value->loc,
+ type_is_assignable(builtin, initializer->result),
+ "Enum value type is not assignable from initializer type");
+
+ initializer = lower_implicit_cast(type, initializer);
+ enum eval_result r = eval_expr(ctx, initializer, value);
+ expect(&idecl->field->field->value->loc, r == EVAL_OK,
+ "Unable to evaluate constant initializer at compile time");
+ } else { // implicit value
+ const struct scope_object *obj = NULL;
+ // find previous enum value
+ if (idecl->obj.lnext && idecl->obj.lnext->otype == O_SCAN) {
+ obj = scope_lookup(idecl->field->enum_scope,
+ &idecl->obj.lnext->name);
+ if (obj->otype == O_SCAN) {
+ // complete previous value first
+ obj = scan_decl_finish(ctx, obj, NULL);
+ }
+ }
+ value->type = EXPR_CONSTANT;
+ if (type_is_signed(type_dealias(type))) {
+ if (obj == NULL) {
+ value->constant.ival = 0;
} else {
- expr->constant.uval = value->uval;
+ value->constant.ival = obj->value->constant.ival + 1;
+ }
+ } else {
+ if (obj == NULL) {
+ value->constant.ival = 0;
+ } else {
+ value->constant.uval = obj->value->constant.uval + 1;
}
-
- struct identifier name_ns = {
- .name = decl->ident.name,
- .ns = decl->ident.ns,
- };
- struct identifier name = {
- .name = value->name,
- .ns = &name_ns,
- };
- struct identifier vident = {
- .name = value->name,
- .ns = &ident,
- };
- scope_insert(ctx->unit, O_CONST, &name, &vident, alias, expr);
}
}
- return true;
+ ctx->resolving_enum = NULL;
+
+ // TODO: allow specifying values by their long name
+ scope_insert(idecl->field->enum_scope, O_CONST, &name, &name, type, value);
+ return scope_insert(ctx->scope, O_CONST, &ident, &vident, type, value);
}
-static bool
-scan_declaration(struct context *ctx, const struct ast_decl *decl)
+static const struct scope_object *
+scan_type(struct context *ctx, struct ast_type_decl *decl, bool exported,
+ struct dimensions dim)
+{
+ struct identifier ident = {0};
+ mkident(ctx, &ident, &decl->ident);
+
+ struct type _alias = {
+ .storage = STORAGE_ALIAS,
+ .alias = {
+ .ident = ident,
+ .type = NULL,
+ .exported = exported,
+ },
+ .size = dim.size,
+ .align = dim.align,
+ .flags = decl->type->flags,
+ };
+
+ const struct type *alias = type_store_lookup_alias(ctx->store, &_alias, true);
+ const struct scope_object *ret =
+ 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);
+ return ret;
+}
+
+static void
+scan_decl_start(struct context *ctx, struct scope *imports, struct ast_decl *decl)
{
switch (decl->decl_type) {
case AST_DECL_CONST:
- for (const struct ast_global_decl *c = &decl->constant; c;
- c = c->next) {
- if (!scan_const(ctx, c)) {
- return false;
- }
+ for (struct ast_global_decl *g = &decl->constant; g; g = g->next) {
+ struct identifier with_ns = {0};
+ mkident(ctx, &with_ns, &g->ident);
+ struct incomplete_declaration *idecl =
+ incomplete_declaration_create(ctx, decl->loc,
+ ctx->scope, &with_ns, &g->ident);
+ idecl->type = IDECL_DECL;
+ idecl->decl = (struct ast_decl){
+ .decl_type = AST_DECL_CONST,
+ .loc = decl->loc,
+ .constant = *g,
+ .exported = decl->exported,
+ };
+ idecl->imports = imports;
}
- return true;
- case AST_DECL_FUNC:
- return scan_function(ctx, &decl->function);
+ break;
case AST_DECL_GLOBAL:
- for (const struct ast_global_decl *g = &decl->global; g;
- g = g->next) {
- if (!scan_global(ctx, g)) {
- return false;
+ for (struct ast_global_decl *g = &decl->global; g; g = g->next) {
+ struct identifier with_ns = {0};
+ mkident(ctx, &with_ns, &g->ident);
+ struct incomplete_declaration *idecl =
+ incomplete_declaration_create(ctx, decl->loc,
+ ctx->scope, &with_ns, &g->ident);
+ idecl->type = IDECL_DECL;
+ idecl->decl = (struct ast_decl){
+ .decl_type = AST_DECL_GLOBAL,
+ .loc = decl->loc,
+ .global = *g,
+ .exported = decl->exported,
+ };
+ idecl->imports = imports;
+ }
+ break;
+ case AST_DECL_FUNC:
+ if ((decl->function.flags & FN_TEST)) {
+ if (ctx->is_test) {
+ scan_function(ctx, &decl->function);
}
+ return;
}
- return true;
+ struct ast_function_decl *func = &decl->function;
+ struct identifier with_ns = {0};
+ mkident(ctx, &with_ns, &func->ident);
+ struct incomplete_declaration *idecl =
+ incomplete_declaration_create(ctx, decl->loc,
+ ctx->scope, &with_ns, &func->ident);
+ idecl->type = IDECL_DECL;
+ idecl->decl = (struct ast_decl){
+ .decl_type = AST_DECL_FUNC,
+ .loc = decl->loc,
+ .function = *func,
+ .exported = decl->exported,
+ };
+ idecl->imports = imports;
+ break;
case AST_DECL_TYPE:
- return scan_type(ctx, &decl->type, decl->exported);
+ incomplete_types_create(ctx, imports, decl);
+ break;
}
- assert(0); // Unreachable
}
-static struct ast_decls *
-scan_declarations(struct context *ctx, const struct ast_decls *decls)
+const struct scope_object *
+scan_decl_finish(struct context *ctx, const struct scope_object *obj,
+ struct dimensions *dim)
{
- struct ast_decls *next = NULL;
- bool found = false;
- while (decls || next) {
- if (!decls) {
- if (!found) {
- return next;
- }
- decls = next;
- next = NULL;
- found = false;
- }
- if (scan_declaration(ctx, &decls->decl)) {
- found = true;
+ // save current subunit and enum context
+ struct scope *enum_scope = ctx->resolving_enum;
+ ctx->resolving_enum = NULL;
+ struct scope *subunit = ctx->unit->parent;
+ ctx->unit->parent = NULL;
+
+ // ensure this declaration wasn't already scanned
+ struct incomplete_declaration *idecl = (struct incomplete_declaration *)obj;
+ obj = scope_lookup(ctx->scope, &idecl->obj.ident);
+ if (obj && obj->otype != O_SCAN) {
+ goto exit;
+ }
+
+ // load this declaration's subunit context
+ ctx->unit->parent = idecl->imports;
+
+ // TODO handle circular enum dependencies
+ if (idecl->type == IDECL_ENUM) {
+ obj = scan_enum_field(ctx, idecl);
+ goto exit;
+ }
+
+ // resolving a declaration that is already in progress -> cycle
+ expect(&idecl->decl.loc, !idecl->in_progress,
+ "Circular dependency for '%s'\n",
+ identifier_unparse(&obj->ident));
+ idecl->in_progress = true;
+
+ switch (idecl->decl.decl_type) {
+ case AST_DECL_CONST:
+ obj = scan_const(ctx, &idecl->decl.constant);
+ break;
+ case AST_DECL_GLOBAL:
+ obj = scan_global(ctx, &idecl->decl.global);
+ break;
+ case AST_DECL_FUNC:
+ obj = scan_function(ctx, &idecl->decl.function);
+ break;
+ case AST_DECL_TYPE:
+ if (dim) {
+ // the caller is only interested in this type's dimensions
+ *dim = type_store_lookup_dimensions(ctx->store,
+ idecl->decl.type.type);
+ obj = NULL;
} else {
- // TODO: Free this
- struct ast_decls *cur =
- xcalloc(sizeof(struct ast_decls), 1);
- memcpy(cur, decls, sizeof(struct ast_decls));
- cur->next = next;
- next = cur;
+ // 1. compute type dimensions
+ struct dimensions _dim =
+ type_store_lookup_dimensions(ctx->store,
+ idecl->decl.type.type);
+
+ handle_errors(ctx->errors);
+ idecl->in_progress = false;
+
+ // 2. compute type representation and store it
+ obj = scan_type(ctx, &idecl->decl.type,
+ idecl->decl.exported, _dim);
}
- decls = decls->next;
+ break;
}
- return next;
+exit:
+ // load stored context
+ ctx->unit->parent = subunit;
+ ctx->resolving_enum = enum_scope;
+ idecl->in_progress = false;
+ return obj;
}
static void
load_import(struct context *ctx, struct ast_imports *import,
struct type_store *ts, struct scope *scope)
{
+ struct context *old_ctx = ctx->store->check_context;
struct scope *mod = module_resolve(ctx->modcache,
ctx->defines, &import->ident, ts);
+ ctx->store->check_context = old_ctx;
switch (import->mode) {
case AST_IMPORT_IDENTIFIER:
for (struct scope_object *obj = mod->objects;
obj; obj = obj->lnext) {
+ if (obj->otype == O_SCAN) {
+ continue;
+ }
scope_insert(scope, obj->otype, &obj->ident,
&obj->name, obj->type, obj->value);
if (obj->name.ns && obj->name.ns->ns) {
@@ -3733,6 +3583,9 @@ load_import(struct context *ctx, struct ast_imports *import,
case AST_IMPORT_ALIAS:
for (struct scope_object *obj = mod->objects;
obj; obj = obj->lnext) {
+ if (obj->otype == O_SCAN) {
+ continue;
+ }
struct identifier ns = {
.name = obj->name.ns->name,
.ns = import->alias,
@@ -3772,6 +3625,9 @@ load_import(struct context *ctx, struct ast_imports *import,
};
for (struct scope_object *o = mod->objects;
o; o = o->lnext) {
+ if (obj->otype == O_SCAN) {
+ continue;
+ }
if (!identifier_eq(o->name.ns, &ident)) {
continue;
};
@@ -3801,9 +3657,9 @@ check_internal(struct type_store *ts,
ctx.is_test = is_test;
ctx.store = ts;
ctx.store->check_context = &ctx;
- ctx.modcache = cache;
ctx.defines = defines;
ctx.next = &ctx.errors;
+ ctx.modcache = cache;
// Top-level scope management involves:
//
@@ -3813,9 +3669,11 @@ check_internal(struct type_store *ts,
//
// Further down the call frame, subsequent functions will create
// sub-scopes for each declaration, expression-list, etc.
- ctx.unit = scope_push(&ctx.scope, SCOPE_UNIT);
- // Install defines (-D on the command line)
+ struct scope *def_scope = NULL;
+ scope_push(&def_scope, SCOPE_UNIT);
+
+ // Put defines into a temporary scope (-D on the command line)
// XXX: This duplicates a lot of code with scan_const
for (struct define *def = defines; def; def = def->next) {
struct location loc = {
@@ -3837,29 +3695,29 @@ check_internal(struct type_store *ts,
enum eval_result r = eval_expr(&ctx, initializer, value);
expect(&loc, r == EVAL_OK,
"Unable to evaluate constant initializer at compile time");
- scope_insert(ctx.unit, O_CONST,
+ scope_insert(def_scope, O_CONST,
&def->ident, &def->ident, type, value);
}
-
- struct scopes *subunit_scopes;
+ struct scopes *subunit_scopes = NULL;
struct scopes **next = &subunit_scopes;
+ struct scope *su_scope = NULL;
struct imports **inext = &unit->imports;
- struct unresolveds {
- struct scope *scope;
- const struct ast_decls *unresolved;
- struct unresolveds *next;
- };
- struct unresolveds *cur = NULL, *unresolved = NULL;
+ ctx.scope = NULL;
+ ctx.unit = scope_push(&ctx.scope, SCOPE_UNIT);
- // First pass populates the imports
+ // Populate the imports and put declarations into a scope.
+ // Each declaration holds a reference to its subunit's imports
+ // A scope gets us:
+ // a) duplicate detection for free
+ // b) a way to find declaration's definition when it's refered to
for (const struct ast_subunit *su = &aunit->subunits;
su; su = su->next) {
- scope_push(&ctx.scope, SCOPE_SUBUNIT);
-
+ su_scope = NULL;
+ scope_push(&su_scope, SCOPE_SUBUNIT);
for (struct ast_imports *imports = su->imports;
imports; imports = imports->next) {
- load_import(&ctx, imports, ts, ctx.scope);
+ load_import(&ctx, imports, ts, su_scope);
bool found = false;
for (struct imports *uimports = unit->imports;
@@ -3877,89 +3735,50 @@ check_internal(struct type_store *ts,
}
}
- struct unresolveds *new =
- xcalloc(sizeof(struct unresolveds), 1);
- new->unresolved = su->decls;
- new->next = cur;
- cur = new;
+ for (struct ast_decls *d = su->decls; d; d = d->next) {
+ scan_decl_start(&ctx, su_scope, &d->decl);
+ };
*next = xcalloc(1, sizeof(struct scopes));
- new->scope = (*next)->scope = scope_pop(&ctx.scope);
+ (*next)->scope = su_scope;
next = &(*next)->next;
}
- // Second pass populates the type graph
- bool found = false;
- while (cur || unresolved) {
- if (!cur) {
- if (!found) {
- handle_errors(ctx.errors);
- error(&ctx, unresolved->unresolved->decl.loc, NULL,
- "Undefined identifier used in declaration");
- handle_errors(ctx.errors);
- }
- cur = unresolved;
- unresolved = NULL;
- found = false;
- }
- struct errors *error = ctx.errors;
- while (error) {
- struct errors *next = error->next;
- free(error);
- error = next;
- }
- ctx.errors = NULL;
- ctx.next = &ctx.errors;
- ctx.scope = cur->scope;
- ctx.store->check_context = &ctx;
- struct ast_decls *left =
- scan_declarations(&ctx, cur->unresolved);
- if (left) {
- struct unresolveds *new =
- xcalloc(sizeof(struct unresolveds), 1);
- new->scope = cur->scope;
- new->unresolved = left;
- new->next = unresolved;
- unresolved = new;
- }
-
- size_t old_len = 0, new_len = 0;
- for (const struct ast_decls *old = cur->unresolved; old;
- old = old->next) {
- old_len++;
- }
- for (struct ast_decls *new = left; new;
- new = new->next) {
- new_len++;
- }
- if (new_len < old_len) {
- found = true;
- }
+ // Put defines into unit scope
+ // We have to insert them *after* declarations, because this way they
+ // shadow declarations, not the other way around
+ //
+ // XXX: shadowed declarations are not checked for consistency
+ for (const struct scope_object *obj = def_scope->objects;
+ obj; obj = obj->lnext) {
+ scope_insert(ctx.unit, O_CONST, &obj->ident, &obj->name,
+ obj->type, obj->value);
+ }
+ scope_free(def_scope);
- struct unresolveds *tmp = cur;
- cur = tmp->next;
- free(tmp);
+ // Perform actual declaration resolution
+ ctx.resolving_enum = NULL;
+ for (const struct scope_object *obj = ctx.scope->objects;
+ obj && obj->otype == O_SCAN; obj = obj->lnext) {
+ scan_decl_finish(&ctx, obj, NULL);
+ assert(ctx.resolving_enum == NULL);
}
+ handle_errors(ctx.errors);
+
if (scan_only) {
+ ctx.store->check_context = NULL;
+ ctx.unit->parent = NULL;
return ctx.unit;
}
- struct errors *error = ctx.errors;
- while (error) {
- struct errors *next = error->next;
- free(error);
- error = next;
- }
- ctx.errors = NULL;
- ctx.next = &ctx.errors;
-
- // Third pass populates the expression graph
+ // Populate the expression graph
struct scopes *scope = subunit_scopes;
struct declarations **next_decl = &unit->declarations;
for (const struct ast_subunit *su = &aunit->subunits;
su; su = su->next) {
- ctx.scope = scope->scope;
+ // subunit scope has to be *behind* unit scope
+ ctx.scope->parent = scope->scope;
next_decl = check_declarations(&ctx, su->decls, next_decl);
scope = scope->next;
}
@@ -3969,6 +3788,8 @@ check_internal(struct type_store *ts,
exit(EXIT_FAILURE);
}
+ ctx.store->check_context = NULL;
+ ctx.unit->parent = NULL;
return ctx.unit;
}
diff --git a/src/eval.c b/src/eval.c
@@ -378,7 +378,7 @@ eval_cast(struct context *ctx, struct expression *in, struct expression *out)
// XXX: We should also be able to handle expressions which use
// symbols/identifiers
out->type = EXPR_CONSTANT;
- out->result = to;
+ out->result = in->result;
const struct type *subtype;
switch (to->storage) {
@@ -391,6 +391,7 @@ eval_cast(struct context *ctx, struct expression *in, struct expression *out)
|| from->storage == STORAGE_UINTPTR);
out->constant.uval = val.constant.uval;
return EVAL_OK;
+ case STORAGE_ENUM:
case STORAGE_I16:
case STORAGE_I32:
case STORAGE_I64:
@@ -445,7 +446,6 @@ eval_cast(struct context *ctx, struct expression *in, struct expression *out)
}
return EVAL_OK;
case STORAGE_CHAR:
- case STORAGE_ENUM:
case STORAGE_NULL:
case STORAGE_ALIAS:
assert(0); // Handled above
@@ -473,7 +473,7 @@ eval_measurement(struct context *ctx, struct expression *in, struct expression *
case M_LEN:
assert(0); // TODO
case M_SIZE:
- out->constant.uval = in->measure.type->size;
+ out->constant.uval = in->measure.dimensions.size;
return EVAL_OK;
case M_OFFSET:
if (in->measure.value->access.type == ACCESS_FIELD) {
diff --git a/src/gen.c b/src/gen.c
@@ -245,6 +245,7 @@ gen_access_ident(struct gen_context *ctx, const struct expression *expr)
};
case O_CONST:
case O_TYPE:
+ case O_SCAN:
abort(); // Invariant
}
abort(); // Invariant
@@ -2232,7 +2233,7 @@ gen_expr_measure(struct gen_context *ctx, const struct expression *expr)
return (struct gen_value){
.kind = GV_CONST,
.type = &builtin_type_size,
- .lval = expr->measure.type->size,
+ .lval = expr->measure.dimensions.size,
};
case M_OFFSET:
if (expr->measure.value->access.type == ACCESS_FIELD) {
diff --git a/src/type_store.c b/src/type_store.c
@@ -10,6 +10,17 @@
#include "type_store.h"
#include "util.h"
+// XXX: This needs to be updated on updates to type_flags (types.h)
+const unsigned int typeflags[] = {
+ 0,
+ TYPE_CONST,
+ TYPE_ERROR,
+ TYPE_ERROR | TYPE_CONST,
+};
+
+static struct dimensions _type_store_lookup_atype(struct type_store *store,
+ const struct type **type, const struct ast_type *atype);
+
static void
error(struct context *ctx, const struct location loc, char *fmt, ...)
{
@@ -28,17 +39,6 @@ error(struct context *ctx, const struct location loc, char *fmt, ...)
ctx->next = &next->next;
}
-static char *
-gen_typename(const struct type *type)
-{
- size_t sz = 0;
- char *ptr = NULL;
- FILE *f = open_memstream(&ptr, &sz);
- emit_type(type, f);
- fclose(f);
- return ptr;
-}
-
static size_t
ast_array_len(struct type_store *store, const struct ast_type *atype)
{
@@ -131,16 +131,6 @@ builtin_type_for_storage(enum type_storage storage, bool is_const)
}
static const struct type *
-builtin_for_atype(const struct ast_type *atype)
-{
- if (atype->flags & TYPE_ERROR) {
- return NULL;
- }
- bool is_const = (atype->flags & TYPE_CONST) != 0;
- return builtin_type_for_storage(atype->storage, is_const);
-}
-
-static const struct type *
builtin_for_type(const struct type *type)
{
if (type->flags & TYPE_ERROR) {
@@ -153,7 +143,7 @@ builtin_for_type(const struct type *type)
static struct struct_field *
struct_insert_field(struct type_store *store, struct struct_field **fields,
enum type_storage storage, size_t *size, size_t *usize, size_t *align,
- const struct ast_struct_union_type *atype, bool *ccompat)
+ const struct ast_struct_union_type *atype, bool *ccompat, bool size_only)
{
while (*fields && (!atype->name || !(*fields)->name || strcmp((*fields)->name, atype->name) < 0)) {
fields = &(*fields)->next;
@@ -164,6 +154,7 @@ struct_insert_field(struct type_store *store, struct struct_field **fields,
"Duplicate struct/union member '%s'", atype->name);
return NULL;
}
+ // XXX: leaks if size_only
*fields = xcalloc(1, sizeof(struct struct_field));
(*fields)->next = field;
field = *fields;
@@ -171,8 +162,13 @@ struct_insert_field(struct type_store *store, struct struct_field **fields,
if (atype->name) {
field->name = strdup(atype->name);
}
- field->type = type_store_lookup_atype(store, atype->type);
- if (field->type->size == 0) {
+ struct dimensions dim = {0};
+ if (size_only) {
+ dim = _type_store_lookup_atype(store, NULL, atype->type);
+ } else {
+ dim = _type_store_lookup_atype(store, &field->type, atype->type);
+ }
+ if (dim.size == 0) {
error(store->check_context, atype->type->loc,
"Struct field size cannot be zero");
return NULL;
@@ -198,21 +194,22 @@ struct_insert_field(struct type_store *store, struct struct_field **fields,
}
} else {
size_t offs = *size;
- if (offs % field->type->align) {
- offs += field->type->align - (offs % field->type->align);
+ if (offs % dim.align) {
+ offs += dim.align - (offs % dim.align);
}
field->offset = offs;
- assert(field->offset % field->type->align == 0);
+ assert(field->offset % dim.align == 0);
}
- if (field->type->size == SIZE_UNDEFINED || *size == SIZE_UNDEFINED) {
+ if (dim.size == SIZE_UNDEFINED || *size == SIZE_UNDEFINED) {
*size = SIZE_UNDEFINED;
} else if (storage == STORAGE_STRUCT) {
- *size = field->offset + field->type->size;
+ *size = field->offset + dim.size;
} else {
- *usize = field->type->size > *usize ? field->type->size : *usize;
+ *usize = dim.size > *usize ? dim.size : *usize;
}
- *align = field->type->align > *align ? field->type->align : *align;
+ *align = dim.align > *align ? dim.align : *align;
+ field->size = dim.size;
return field;
}
@@ -276,15 +273,15 @@ shift_fields(struct type_store *store, struct struct_field *parent)
static void
struct_init_from_atype(struct type_store *store, enum type_storage storage,
size_t *size, size_t *align, struct struct_field **fields,
- const struct ast_struct_union_type *atype, bool *ccompat)
+ const struct ast_struct_union_type *atype, bool *ccompat, bool size_only)
{
// TODO: fields with size SIZE_UNDEFINED
size_t usize = 0;
assert(storage == STORAGE_STRUCT || storage == STORAGE_UNION);
while (atype) {
struct struct_field *field = struct_insert_field(store, fields,
- storage, size, &usize, align, atype, ccompat);
- if (!field->name) {
+ storage, size, &usize, align, atype, ccompat, size_only);
+ if (!field->name && !size_only) {
// We need to shift the embedded struct/union's fields
// so that their offsets are from the start of the
// parent type. This is a bit of a hack, but it makes
@@ -317,16 +314,56 @@ sum_tagged_memb(struct type_store *store,
return nmemb;
}
+// get next member of an incomplete tagged union without completing it
+static void
+tagged_or_atagged_member(struct type_store *store,
+ const struct ast_type **atype, const struct type **type)
+{
+ const struct ast_type *_atype = *atype;
+ while (_atype->storage == STORAGE_ALIAS && _atype->unwrap) {
+ const struct scope_object *obj = scope_lookup(
+ store->check_context->scope, &_atype->alias);
+ if (obj->otype != O_SCAN) {
+ if (obj->otype == O_TYPE) {
+ *type = type_dealias(obj->type);
+ return;
+ } else {
+ error(store->check_context, _atype->loc,
+ "Object '%s' is not a type",
+ identifier_unparse(&obj->ident));
+ *type = &builtin_type_void;
+ return;
+ }
+ }
+ struct incomplete_declaration *idecl =
+ (struct incomplete_declaration *)obj;
+ if (idecl->type != IDECL_DECL
+ || idecl->decl.decl_type != AST_DECL_TYPE) {
+ error(store->check_context, _atype->loc,
+ "Object '%s' is not a type",
+ identifier_unparse(&obj->ident));
+ *type = &builtin_type_void;
+ return;
+ }
+ _atype = idecl->decl.type.type;
+ }
+ *type = NULL;
+ *atype = _atype;
+}
+
static size_t
sum_atagged_memb(struct type_store *store,
const struct ast_tagged_union_type *u)
{
size_t nmemb = 0;
for (; u; u = u->next) {
- const struct type *type =
- type_store_lookup_atype(store, u->type);
- if (type->storage == STORAGE_TAGGED) {
+ const struct type *type = NULL;
+ const struct ast_type *atype = u->type;
+ tagged_or_atagged_member(store, &atype, &type);
+ if (type != NULL && type->storage == STORAGE_TAGGED) {
nmemb += sum_tagged_memb(store, &type->tagged);
+ } else if (atype->storage == STORAGE_TAGGED) {
+ nmemb += sum_atagged_memb(store, &atype->tagged_union);
} else {
++nmemb;
}
@@ -400,20 +437,6 @@ tagged_init(struct type_store *store, struct type *type,
}
nmemb = nmemb_dedup;
- if (tu[0]->type->size == SIZE_UNDEFINED) {
- char *type = gen_typename(tu[0]->type);
- // TODO
- struct location loc = {
- .path = "<unknown>",
- .lineno = 0,
- .colno = 0,
- };
- error(store->check_context, loc,
- "Cannot create tagged union from type %s, which has undefined size\n",
- type);
- free(type);
- }
-
// First one free
type->tagged = *tu[0];
free(tu[0]);
@@ -423,27 +446,12 @@ tagged_init(struct type_store *store, struct type *type,
struct type_tagged_union **next = &type->tagged.next;
for (size_t i = 1; i < nmemb; ++i) {
- if (tu[i]->type->size == SIZE_UNDEFINED) {
- char *type = gen_typename(tu[i]->type);
- // TODO
- struct location loc = {
- .path = "<unknown>",
- .lineno = 0,
- .colno = 0,
- };
- error(store->check_context, loc,
- "Cannot create tagged union from type %s, which has undefined size\n",
- type);
- free(type);
- }
-
if (tu[i]->type->size > type->size) {
type->size = tu[i]->type->size;
}
if (tu[i]->type->align > type->align) {
type->align = tu[i]->type->align;
}
-
*next = tu[i];
next = &tu[i]->next;
}
@@ -453,7 +461,6 @@ tagged_init(struct type_store *store, struct type *type,
}
type->size += builtin_type_uint.size % type->align
+ builtin_type_uint.align;
-
return nmemb;
}
@@ -474,43 +481,116 @@ tagged_init_from_atype(struct type_store *store,
}
}
-static void
+static struct dimensions
+_tagged_size(struct type_store *store, const struct ast_tagged_union_type *u)
+{
+ struct dimensions dim = {0};
+ for (; u; u = u->next) {
+ struct dimensions memb = {0};
+ const struct type *type = NULL;
+ const struct ast_type *atype = u->type;
+ tagged_or_atagged_member(store, &atype, &type);
+ if (type != NULL && type->storage == STORAGE_TAGGED) {
+ for (const struct type_tagged_union *u = &type->tagged;
+ u; u = u->next) {
+ if (memb.size < u->type->size) {
+ memb.size = u->type->size;
+ }
+ if (memb.align < u->type->align) {
+ memb.align = u->type->align;
+ }
+ }
+ } else if (atype->storage == STORAGE_TAGGED) {
+ memb = _tagged_size(store, &atype->tagged_union);
+ } else {
+ memb = _type_store_lookup_atype(store, NULL, atype);
+ }
+ if (dim.size < memb.size) {
+ dim.size = memb.size;
+ }
+ if (dim.align < memb.align) {
+ dim.align = memb.align;
+ }
+ }
+ return dim;
+}
+
+// compute the dimensions of an incomplete tagged union without completing it
+static struct dimensions
+tagged_size(struct type_store *store, const struct ast_type *atype)
+{
+ struct dimensions dim = _tagged_size(store, &atype->tagged_union);
+ if (dim.align < builtin_type_uint.align) {
+ dim.align = builtin_type_uint.align;
+ }
+ dim.size += builtin_type_uint.size % dim.align
+ + builtin_type_uint.align;
+ return dim;
+}
+
+
+static struct dimensions
tuple_init_from_atype(struct type_store *store,
struct type *type, const struct ast_type *atype)
{
- type->size = 0, type->align = 0;
const struct ast_tuple_type *atuple = &atype->tuple;
- struct type_tuple *cur = &type->tuple;
+ struct type_tuple *cur = NULL;
+ if (type) {
+ type->size = 0, type->align = 0;
+ cur = &type->tuple;
+ }
+ struct dimensions dim = {0};
while (atuple) {
- cur->type = type_store_lookup_atype(store, atuple->type);
- if (cur->type->size == 0) {
+ struct dimensions memb = {0};
+ if (type) {
+ memb = _type_store_lookup_atype(store, &cur->type, atuple->type);
+ cur->offset = dim.size % memb.align + dim.size;
+ } else {
+ memb = _type_store_lookup_atype(store, NULL, atuple->type);
+ }
+ if (memb.size == 0) {
error(store->check_context, atuple->type->loc,
"Type of size zero cannot be a tuple member");
continue;
}
- cur->offset = type->size % cur->type->align + type->size;
- type->size += type->size % cur->type->align + cur->type->size;
- if (type->align < cur->type->align) {
- type->align = cur->type->align;
+ dim.size += dim.size % memb.align + memb.size;
+ if (dim.align < memb.align) {
+ dim.align = memb.align;
}
atuple = atuple->next;
- if (atuple) {
+ if (atuple && type) {
cur->next = xcalloc(1, sizeof(struct type_tuple));
cur = cur->next;
}
}
+ if (type) {
+ type->size = dim.size;
+ type->align = dim.align;
+ }
+ return dim;
}
-static void
+static const struct type *
+type_store_lookup_type(struct type_store *store, const struct type *type);
+
+static struct dimensions
type_init_from_atype(struct type_store *store,
struct type *type,
const struct ast_type *atype)
{
+ struct type tmp = {0};
+ bool size_only = false;
+ if (type == NULL) {
+ type = &tmp;
+ size_only = true;
+ }
+
type->storage = atype->storage;
type->flags = atype->flags;
- const struct scope_object *obj;
+ // TODO: Use a dedicated error type instead of void for errors
+ const struct scope_object *obj = NULL;
const struct identifier *ident;
const struct type *builtin;
struct identifier temp;
@@ -556,55 +636,66 @@ type_init_from_atype(struct type_store *store,
&atype->alias);
}
if (!obj) {
- if (atype->unwrap) {
- error(store->check_context, atype->loc,
- "Cannot unwrap unknown type alias %s",
- identifier_unparse(ident));
+ error(store->check_context, atype->loc,
+ "Unresolvable identifier '%s'",
+ identifier_unparse(ident));
+ *type = builtin_type_void;
+ return (struct dimensions){0};
+ }
+
+
+ if (obj->otype == O_SCAN) {
+ // an incomplete declaration was encountered
+ struct dimensions dim = {0};
+ if (size_only) {
+ scan_decl_finish(store->check_context, obj, &dim);
+ type->size = dim.size;
+ type->align = dim.align;
+ break;
}
- identifier_dup(&type->alias.ident, ident);
- type->alias.type = NULL;
- type->size = SIZE_UNDEFINED;
- type->align = SIZE_UNDEFINED;
- return;
+ // complete it first and then proceed normally
+ obj = scan_decl_finish(store->check_context, obj, NULL);
}
if (obj->otype != O_TYPE) {
error(store->check_context, atype->loc,
"Object '%s' is not a type",
identifier_unparse(&obj->ident));
- type->alias.type = NULL;
- type->size = SIZE_UNDEFINED;
- type->align = SIZE_UNDEFINED;
- return;
+ *type = builtin_type_void;
+ return (struct dimensions){0};
}
if (atype->unwrap) {
*type = *type_dealias(obj->type);
break;
}
-
identifier_dup(&type->alias.ident, &obj->ident);
- type->alias.type = obj->type;
- type->size = type->alias.type->size;
- type->align = type->alias.type->align;
+ type->alias.type = obj->type->alias.type;
+ type->size = obj->type->size;
+ type->align = obj->type->align;
break;
case STORAGE_ARRAY:
type->array.length = ast_array_len(store, atype);
- type->array.members = type_store_lookup_atype(
- store, atype->array.members);
- if (type->array.members->size == SIZE_UNDEFINED) {
- type->align = SIZE_UNDEFINED;
- type->size = SIZE_UNDEFINED;
+ struct dimensions memb = {0};
+ if (size_only) {
+ memb = _type_store_lookup_atype(store,
+ NULL, atype->array.members);
+ } else {
+ memb = _type_store_lookup_atype(store,
+ &type->array.members, atype->array.members);
+ }
+ if (memb.size == SIZE_UNDEFINED) {
error(store->check_context, atype->loc,
"Array member must have defined size");
- return;
+ *type = builtin_type_void;
+ return (struct dimensions){0};
}
- type->align = type->array.members->align;
+ type->align = memb.align;
if (type->array.length == SIZE_UNDEFINED) {
type->size = SIZE_UNDEFINED;
} else {
- type->size = type->array.members->size * type->array.length;
+ type->size = memb.size * type->array.length;
}
break;
case STORAGE_ENUM:
@@ -614,12 +705,14 @@ type_init_from_atype(struct type_store *store,
if (!type_is_integer(storage)) {
error(store->check_context, atype->loc,
"Enum storage must be an integer");
- // TODO: Use a dedicated error type
*type = builtin_type_void;
- return;
+ 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);
@@ -684,9 +777,12 @@ type_init_from_atype(struct type_store *store,
break;
case STORAGE_FUNCTION:
type->size = SIZE_UNDEFINED;
- type->align = SIZE_UNDEFINED;
- type->func.result =
- type_store_lookup_atype(store, atype->func.result);
+ type->align = ALIGN_UNDEFINED;
+ if (size_only) {
+ break;
+ }
+ type->func.result = type_store_lookup_atype(store,
+ atype->func.result);
type->func.variadism = atype->func.variadism;
type->func.flags = atype->func.flags;
struct type_func_param *param, **next = &type->func.params;
@@ -705,6 +801,9 @@ type_init_from_atype(struct type_store *store,
case STORAGE_POINTER:
type->size = 8; // XXX: ARCH
type->align = 8;
+ if (size_only) {
+ break;
+ }
type->pointer.flags = atype->pointer.flags;
type->pointer.referent = type_store_lookup_atype(
store, atype->pointer.referent);
@@ -712,6 +811,9 @@ type_init_from_atype(struct type_store *store,
case STORAGE_SLICE:
type->size = 24; // XXX: ARCH
type->align = 8;
+ if (size_only) {
+ break;
+ }
type->array.members = type_store_lookup_atype(
store, atype->array.members);
type->array.length = SIZE_UNDEFINED;
@@ -721,53 +823,59 @@ type_init_from_atype(struct type_store *store,
type->struct_union.c_compat = true;
struct_init_from_atype(store, type->storage, &type->size,
&type->align, &type->struct_union.fields,
- &atype->struct_union, &type->struct_union.c_compat);
+ &atype->struct_union, &type->struct_union.c_compat,
+ size_only);
if (!type->struct_union.c_compat) {
// Recompute size
type->size = 0;
for (struct struct_field *f = type->struct_union.fields;
f; f = f->next) {
- if (f->offset + f->type->size > type->size) {
- type->size = f->offset + f->type->size;
+ if (f->type) assert(f->type->size == f->size);
+ if (f->offset + f->size > type->size) {
+ type->size = f->offset + f->size;
}
}
}
break;
case STORAGE_TAGGED:
- tagged_init_from_atype(store, type, atype);
+ if (size_only) {
+ struct dimensions tagged = tagged_size(store, atype);
+ type->size = tagged.size;
+ type->align = tagged.align;
+ } else {
+ tagged_init_from_atype(store, type, atype);
+ }
break;
case STORAGE_TUPLE:
- tuple_init_from_atype(store, type, atype);
+ if (size_only) {
+ struct dimensions tup;
+ tup = tuple_init_from_atype(store, NULL, atype);
+ type->size = tup.size;
+ type->align = tup.align;
+ } else {
+ tuple_init_from_atype(store, type, atype);
+ }
break;
}
+ return (struct dimensions){ .size = type->size, .align = type->align };
+}
+
+static void add_padding(size_t *size, size_t align) {
+ if (*size != SIZE_UNDEFINED && *size != 0 && *size % align != 0) {
+ *size += align - (*size - align) % align;
+ }
}
static const struct type *
_type_store_lookup_type(
struct type_store *store,
- const struct type *type,
- bool recur)
+ const struct type *type)
{
const struct type *builtin = builtin_for_type(type);
if (builtin) {
return builtin;
}
- struct type psuedotype = *type;
- if (type->storage == STORAGE_ALIAS && !recur) {
- // References to type aliases always inherit the flags that the
- // alias was defined with
- //
- // TODO: This is likely problematic for forward references
- // TODO: Does this need a spec update?
- const struct scope_object *obj = scope_lookup(
- store->check_context->scope, &type->alias.ident);
- if (obj) {
- psuedotype.flags |= obj->type->flags;
- type = &psuedotype;
- }
- }
-
uint32_t hash = type_hash(type);
struct type_bucket **next = &store->buckets[hash % TYPE_STORE_BUCKETS],
*bucket = NULL;
@@ -775,6 +883,9 @@ _type_store_lookup_type(
while (*next) {
bucket = *next;
if (bucket->type.id == hash) {
+ if (bucket->type.storage == STORAGE_ALIAS) {
+ bucket->type.alias.type = type->alias.type;
+ }
return &bucket->type;
}
next = &bucket->next;
@@ -783,30 +894,56 @@ _type_store_lookup_type(
bucket = *next = xcalloc(1, sizeof(struct type_bucket));
bucket->type = *type;
bucket->type.id = hash;
- if (type->size != SIZE_UNDEFINED
- && type->size != 0
- && type->size % type->align != 0) {
- bucket->type.size += type->align - (type->size - type->align) % type->align;
- }
+ add_padding(&bucket->type.size, type->align);
return &bucket->type;
}
static const struct type *
type_store_lookup_type(struct type_store *store, const struct type *type)
{
- return _type_store_lookup_type(store, type, false);
+ if (type->storage != STORAGE_ALIAS) {
+ return _type_store_lookup_type(store, type);
+ }
+ // References to type aliases always inherit the flags that the
+ // alias was defined with
+ struct type psuedotype = *type;
+ const struct scope_object *obj = scope_lookup(
+ store->check_context->scope, &type->alias.ident);
+ psuedotype.flags |= obj->type->flags;
+ return type_store_lookup_alias(store, &psuedotype, true);
+}
+
+static struct dimensions
+_type_store_lookup_atype(struct type_store *store, const struct type **type, const struct ast_type *atype)
+{
+ struct type temp = {0};
+ struct dimensions dim = {0};
+ if (type) {
+ dim = type_init_from_atype(store, &temp, atype);
+ *type = type_store_lookup_type(store, &temp);
+ } else {
+ dim = type_init_from_atype(store, NULL, atype);
+ }
+ add_padding(&dim.size, dim.align);
+ return dim;
}
const struct type *
type_store_lookup_atype(struct type_store *store, const struct ast_type *atype)
{
- const struct type *builtin = builtin_for_atype(atype);
- if (builtin) {
- return builtin;
- }
- struct type type = {0};
- type_init_from_atype(store, &type, atype);
- return type_store_lookup_type(store, &type);
+ struct type temp = {0};
+ const struct type *type = &temp;
+ _type_store_lookup_atype(store, &type, atype);
+ return type;
+}
+
+// Compute dimensions of an incomplete type without completing it
+struct dimensions
+type_store_lookup_dimensions(struct type_store *store, const struct ast_type *atype)
+{
+ struct dimensions dim = type_init_from_atype(store, NULL, atype);
+ add_padding(&dim.size, dim.align);
+ return dim;
}
const struct type *
@@ -818,7 +955,7 @@ type_store_lookup_with_flags(struct type_store *store,
}
struct type new = *type;
new.flags = flags;
- return type_store_lookup_type(store, &new);
+ return _type_store_lookup_type(store, &new);
}
const struct type *
@@ -870,54 +1007,28 @@ type_store_lookup_slice(struct type_store *store, const struct type *members)
}
const struct type *
-type_store_lookup_alias(struct type_store *store,
- const struct identifier *ident, const struct type *secondary,
- bool exported)
+type_store_lookup_alias(struct type_store *store, const struct type *type, bool exported)
{
- struct type alias = {
- .storage = STORAGE_ALIAS,
- .alias = {
- .ident = *ident,
- .type = secondary,
- .exported = exported,
- },
- .size = secondary->size,
- .align = secondary->align,
- .flags = secondary->flags,
- };
- // XXX: This needs to be updated on updates to type_flags (types.h)
- unsigned int flags[] = {
- 0,
- TYPE_CONST,
- TYPE_ERROR,
- TYPE_ERROR | TYPE_CONST,
- };
- for (size_t i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
- if ((flags[i] & secondary->flags) != secondary->flags) {
+ struct type tmp = *type;
+ const struct type *ret = NULL;
+ for (size_t i = 0; i < sizeof(typeflags) / sizeof(typeflags[0]); i++) {
+ if ((typeflags[i] & type->flags) != type->flags) {
continue;
}
- const struct type *flagged = type_store_lookup_with_flags(store,
- secondary, flags[i]);
- alias.flags = flagged->flags;
- alias.alias.type = flagged;
- struct type *falias =
- (struct type *)type_store_lookup_type(store, &alias);
- if (falias->alias.type == NULL) {
- // Finish filling in forward referenced type
- falias->alias.type = type_store_lookup_with_flags(store,
- secondary, secondary->flags | flags[i]);
- falias->alias.exported = exported;
- falias->size = secondary->size;
- falias->align = secondary->align;
- falias->flags = secondary->flags | flags[i];
+ if (type->alias.type) {
+ tmp.alias.type = type_store_lookup_with_flags(
+ store, type->alias.type, typeflags[i]);
+ }
+ tmp.flags = typeflags[i];
+ const struct type *alias = _type_store_lookup_type(store, &tmp);
+ if (typeflags[i] == type->flags) {
+ ret = alias;
}
}
- alias.flags = secondary->flags;
- alias.alias.type = secondary;
- alias.alias.exported = exported;
- return type_store_lookup_type(store, &alias);
+ return ret;
}
+
const struct type *
type_store_lookup_tagged(struct type_store *store,
struct type_tagged_union *tags)