commit cc42fcfe62355e614d451e9ae8e207b2658cb4ad
parent 3a0fc82e5d96c77f77db53ee8f9f1ffa3458f40a
Author: Eyal Sawady <ecs@d2evs.net>
Date: Mon, 11 Jan 2021 09:12:10 -0500
check: implement type aliases
Diffstat:
7 files changed, 104 insertions(+), 31 deletions(-)
diff --git a/include/scope.h b/include/scope.h
@@ -7,6 +7,7 @@ enum object_type {
O_BIND,
O_CONST,
O_DECL,
+ O_TYPE,
};
// XXX: This might be better as a hash map
diff --git a/include/type_store.h b/include/type_store.h
@@ -17,6 +17,8 @@ struct type_store {
struct context *check_context;
};
+const struct type *type_dealias(const struct type *type);
+
bool type_is_assignable(struct type_store *store,
const struct type *to, const struct type *from);
bool type_is_castable(const struct type *to, const struct type *from);
diff --git a/include/types.h b/include/types.h
@@ -43,6 +43,11 @@ struct type;
#define SIZE_UNDEFINED ((size_t)-1)
#define ALIGN_UNDEFINED ((size_t)-1)
+struct type_alias {
+ struct identifier ident;
+ const struct type *type;
+};
+
struct type_array {
size_t length; // SIZE_UNDEFINED for [*] or slices
const struct type *members;
@@ -106,6 +111,7 @@ struct type {
unsigned int flags;
size_t size, align;
union {
+ struct type_alias alias;
struct type_array array;
struct type_func func;
struct type_pointer pointer;
diff --git a/src/check.c b/src/check.c
@@ -72,12 +72,18 @@ check_expr_access(struct context *ctx,
char buf[1024];
identifier_unparse_static(&aexpr->access.ident, buf, sizeof(buf));
expect(&aexpr->loc, obj, "Unknown object '%s'", buf);
- if (obj->otype == O_CONST) {
+ switch (obj->otype) {
+ case O_CONST:
// Lower constants
*expr = *obj->value;
- } else {
+ break;
+ case O_BIND:
+ case O_DECL:
expr->result = obj->type;
expr->access.object = obj;
+ break;
+ case O_TYPE:
+ expect(&aexpr->loc, false, "Expected identifier, got type");
}
break;
case ACCESS_INDEX:
@@ -85,19 +91,19 @@ check_expr_access(struct context *ctx,
expr->access.index = xcalloc(1, sizeof(struct expression));
check_expression(ctx, aexpr->access.array, expr->access.array);
check_expression(ctx, aexpr->access.index, expr->access.index);
- const struct type *atype = expr->access.array->result;
- const struct type *itype = expr->access.index->result;
- while (atype->storage == TYPE_STORAGE_POINTER) {
- expect(&aexpr->access.array->loc,
- !(atype->pointer.flags & PTR_NULLABLE),
- "Cannot index nullable pointer");
- atype = atype->pointer.referent;
- }
+ const struct type *atype =
+ type_dereference(expr->access.array->result);
+ expect(&aexpr->access.array->loc, atype,
+ "Cannot dereference nullable pointer for indexing");
+ const struct type *itype =
+ type_dealias(expr->access.index->result);
expect(&aexpr->access.array->loc,
atype->storage == TYPE_STORAGE_ARRAY || atype->storage == TYPE_STORAGE_SLICE,
- "Cannot index non-array, non-slice object");
+ "Cannot index non-array, non-slice %s object",
+ type_storage_unparse(atype->storage));
expect(&aexpr->access.index->loc, type_is_integer(itype),
- "Cannot use non-integer type as slice/array index");
+ "Cannot use non-integer %s type as slice/array index",
+ type_storage_unparse(itype->storage));
expr->access.index = lower_implicit_cast(
&builtin_type_size, expr->access.index);
expr->result = type_store_lookup_with_flags(&ctx->store,
@@ -106,13 +112,10 @@ check_expr_access(struct context *ctx,
case ACCESS_FIELD:
expr->access._struct = xcalloc(1, sizeof(struct expression));
check_expression(ctx, aexpr->access._struct, expr->access._struct);
- const struct type *stype = expr->access._struct->result;
- while (stype->storage == TYPE_STORAGE_POINTER) {
- expect(&aexpr->access.array->loc,
- !(stype->pointer.flags & PTR_NULLABLE),
- "Cannot dereference nullable pointer for field selection");
- stype = stype->pointer.referent;
- }
+ const struct type *stype =
+ type_dereference(expr->access._struct->result);
+ expect(&aexpr->access._struct->loc, stype,
+ "Cannot dereference nullable pointer for field selection");
expect(&aexpr->access._struct->loc,
stype->storage == TYPE_STORAGE_STRUCT || stype->storage == TYPE_STORAGE_UNION,
"Cannot index non-struct, non-union object");
@@ -323,6 +326,8 @@ check_expr_call(struct context *ctx,
expr->call.lvalue = lvalue;
const struct type *fntype = type_dereference(lvalue->result);
+ expect(&aexpr->loc, fntype,
+ "Cannot dereference nullable pointer type for function call");
expect(&aexpr->loc,
fntype->storage == TYPE_STORAGE_FUNCTION,
"Cannot call non-function type");
@@ -939,7 +944,7 @@ check_declarations(struct context *ctx,
decl = check_function(ctx, adecl);
break;
case AST_DECL_TYPE:
- assert(0); // TODO
+ break; // Handled in scan
case AST_DECL_GLOBAL:
assert(0); // TODO
case AST_DECL_CONST:
@@ -1020,6 +1025,19 @@ scan_const(struct context *ctx, const struct ast_global_decl *decl)
}
static void
+scan_type(struct context *ctx, const struct ast_type_decl *decl)
+{
+ trenter(TR_SCAN, "type");
+ const struct type *type =
+ type_store_lookup_atype(&ctx->store, decl->type);
+
+ struct identifier ident = {0};
+ mkident(ctx, &ident, &decl->ident);
+ scope_insert(ctx->unit, O_TYPE, &ident, &decl->ident, type, NULL);
+ trleave(TR_SCAN, NULL);
+}
+
+static void
scan_declarations(struct context *ctx, const struct ast_decls *decls)
{
trenter(TR_SCAN, "declarations");
@@ -1030,7 +1048,8 @@ scan_declarations(struct context *ctx, const struct ast_decls *decls)
scan_function(ctx, &decl->function);
break;
case AST_DECL_TYPE:
- assert(0); // TODO
+ scan_type(ctx, &decl->type);
+ break;
case AST_DECL_GLOBAL:
assert(0); // TODO
case AST_DECL_CONST:
diff --git a/src/gen.c b/src/gen.c
@@ -111,6 +111,7 @@ qval_for_object(struct gen_context *ctx,
val->name = ident_to_sym(&obj->ident);
break;
case O_CONST:
+ case O_TYPE:
assert(0); // Invariant (lowered in check)
}
diff --git a/src/type_store.c b/src/type_store.c
@@ -3,6 +3,7 @@
#include <string.h>
#include "check.h"
#include "eval.h"
+#include "scope.h"
#include "type_store.h"
#include "util.h"
@@ -25,6 +26,15 @@ ast_array_len(struct type_store *store, const struct ast_type *atype)
return (size_t)out.constant.uval;
}
+const struct type *
+type_dealias(const struct type *type)
+{
+ while (type->storage == TYPE_STORAGE_ALIAS) {
+ type = type->alias.type;
+ }
+ return type;
+}
+
bool
type_is_assignable(struct type_store *store,
const struct type *to,
@@ -96,6 +106,7 @@ type_is_assignable(struct type_store *store,
}
assert(0); // Unreachable
case TYPE_STORAGE_ALIAS:
+ return type_is_assignable(store, to->alias.type, from);
case TYPE_STORAGE_ENUM:
case TYPE_STORAGE_TAGGED_UNION:
assert(0); // TODO
@@ -121,9 +132,10 @@ type_is_assignable(struct type_store *store,
}
bool
-type_is_castable(const struct type *to,
- const struct type *from)
+type_is_castable(const struct type *to, const struct type *from)
{
+ to = type_dealias(to);
+ from = type_dealias(from);
switch (from->storage) {
case TYPE_STORAGE_CHAR:
return to->storage == TYPE_STORAGE_U8;
@@ -166,7 +178,6 @@ type_is_castable(const struct type *to,
case TYPE_STORAGE_ARRAY:
return to->storage == TYPE_STORAGE_SLICE
|| to->storage == TYPE_STORAGE_ARRAY;
- case TYPE_STORAGE_ALIAS:
case TYPE_STORAGE_TAGGED_UNION:
assert(0); // TODO
case TYPE_STORAGE_STRING:
@@ -181,6 +192,8 @@ type_is_castable(const struct type *to,
case TYPE_STORAGE_STRUCT:
case TYPE_STORAGE_UNION:
return false;
+ case TYPE_STORAGE_ALIAS:
+ assert(0); // Handled above
}
assert(0); // Unreachable
@@ -273,7 +286,12 @@ type_hash(struct type_store *store, const struct type *type)
case TYPE_STORAGE_STRING:
break; // built-ins
case TYPE_STORAGE_ALIAS:
- assert(0); // TODO
+ for (const struct identifier *ident = &type->alias.ident; ident;
+ ident = ident->ns) {
+ hash = djb2_s(hash, ident->name);
+ }
+ hash = djb2(hash, type_hash(store, type->alias.type));
+ break;
case TYPE_STORAGE_ARRAY:
hash = djb2(hash, type_hash(store, type->array.members));
hash = djb2(hash, type->array.length);
@@ -358,7 +376,11 @@ type_eq_type(struct type_store *store,
case TYPE_STORAGE_STRING:
return true;
case TYPE_STORAGE_ALIAS:
- assert(0); // TODO
+ if (!identifier_eq(&a->alias.ident, &b->alias.ident)) {
+ return false;
+ }
+ assert(type_eq_type(store, a->alias.type, b->alias.type));
+ return true;
case TYPE_STORAGE_ARRAY:
return a->array.length == b->array.length
&& a->array.expandable == b->array.expandable
@@ -485,6 +507,7 @@ type_init_from_atype(struct type_store *store,
type->storage = atype->storage;
type->flags = atype->flags;
+ const struct scope_object *obj;
switch (type->storage) {
case TYPE_STORAGE_BOOL:
case TYPE_STORAGE_CHAR:
@@ -508,7 +531,14 @@ type_init_from_atype(struct type_store *store,
case TYPE_STORAGE_VOID:
assert(0); // Invariant
case TYPE_STORAGE_ALIAS:
- assert(0); // TODO
+ obj = scope_lookup(store->check_context->scope, &atype->alias);
+ // TODO: Bubble this up:
+ assert(obj && obj->otype == O_TYPE);
+ identifier_dup(&type->alias.ident, &atype->alias);
+ type->alias.type = obj->type;
+ type->size = type->alias.type->size;
+ type->align = type->alias.type->align;
+ break;
case TYPE_STORAGE_ARRAY:
type->array.length = ast_array_len(store, atype);
type->array.members = type_store_lookup_atype(
@@ -594,7 +624,12 @@ type_init_from_type(struct type_store *store,
case TYPE_STORAGE_VOID:
assert(0); // Invariant
case TYPE_STORAGE_ALIAS:
- assert(0); // TODO
+ new->size = old->size;
+ new->align = old->align;
+ identifier_dup(&new->alias.ident, &old->alias.ident);
+ new->alias.type =
+ type_store_lookup_type(store, old->alias.type);
+ break;
case TYPE_STORAGE_ARRAY:
new->array.members =
type_store_lookup_type(store, old->array.members);
diff --git a/src/types.c b/src/types.c
@@ -2,14 +2,22 @@
#include <stdbool.h>
#include <string.h>
#include "types.h"
+#include "type_store.h"
const struct type *
type_dereference(const struct type *type)
{
- if (type->storage != TYPE_STORAGE_POINTER) {
+ switch (type->storage) {
+ case TYPE_STORAGE_ALIAS:
+ return type_dereference(type_dealias(type));
+ case TYPE_STORAGE_POINTER:
+ if (type->pointer.flags & PTR_NULLABLE) {
+ return NULL;
+ }
+ return type_dereference(type->pointer.referent);
+ default:
return type;
}
- return type_dereference(type->pointer.referent);
}
const struct struct_field *
@@ -184,7 +192,6 @@ type_is_signed(const struct type *type)
{
switch (type->storage) {
case TYPE_STORAGE_VOID:
- case TYPE_STORAGE_ALIAS:
case TYPE_STORAGE_ARRAY:
case TYPE_STORAGE_FUNCTION:
case TYPE_STORAGE_POINTER:
@@ -213,6 +220,8 @@ type_is_signed(const struct type *type)
case TYPE_STORAGE_F32:
case TYPE_STORAGE_F64:
return true;
+ case TYPE_STORAGE_ALIAS:
+ return type_is_signed(type_dealias(type));
case TYPE_STORAGE_ENUM:
assert(0); // TODO
}