harec

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 565ce1c45b783b7a97003c867d3528a055268c37
parent 2a2f1ced2e2e40f7efaa499a5d619bcfb33e2954
Author: Drew DeVault <sir@cmpwn.com>
Date:   Fri, 25 Dec 2020 11:02:41 -0500

Implement type assignability rules

Diffstat:
Minclude/type_store.h | 3+++
Minclude/types.h | 11+++++++----
Msrc/check.c | 34++++++++++++++++++++++------------
Msrc/lex.c | 1+
Msrc/qtype.c | 4++++
Msrc/type_store.c | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/types.c | 34+++++++++++++++++++++++++---------
Mtodo.txt | 2++
8 files changed, 162 insertions(+), 29 deletions(-)

diff --git a/include/type_store.h b/include/type_store.h @@ -14,6 +14,9 @@ struct type_store { struct type_bucket *buckets[TYPE_STORE_BUCKETS]; }; +bool type_is_assignable(struct type_store *store, + const struct type *to, const struct type *from); + unsigned long atype_hash(struct type_store *store, const struct ast_type *type); unsigned long type_hash(struct type_store *store, const struct type *type); diff --git a/include/types.h b/include/types.h @@ -11,17 +11,18 @@ enum type_storage { TYPE_STORAGE_ENUM, TYPE_STORAGE_F32, TYPE_STORAGE_F64, - TYPE_STORAGE_I8, TYPE_STORAGE_I16, TYPE_STORAGE_I32, TYPE_STORAGE_I64, + TYPE_STORAGE_I8, TYPE_STORAGE_INT, + TYPE_STORAGE_NULL, TYPE_STORAGE_RUNE, TYPE_STORAGE_SIZE, - TYPE_STORAGE_U8, TYPE_STORAGE_U16, TYPE_STORAGE_U32, TYPE_STORAGE_U64, + TYPE_STORAGE_U8, TYPE_STORAGE_UINT, TYPE_STORAGE_UINTPTR, TYPE_STORAGE_VOID, @@ -87,12 +88,13 @@ struct type { }; }; +const struct type *type_dereference(const struct type *type); + const char *type_storage_unparse(enum type_storage storage); bool type_is_signed(const struct type *type); bool type_is_integer(const struct type *type); bool type_is_numeric(const struct type *type); - -const struct type *type_dereference(const struct type *type); +bool type_is_float(const struct type *type); // Built-in type singletons extern const struct type @@ -112,6 +114,7 @@ extern const struct type builtin_type_u64, builtin_type_uint, builtin_type_uintptr, + builtin_type_null, builtin_type_rune, builtin_type_size, builtin_type_void, diff --git a/src/check.c b/src/check.c @@ -13,6 +13,7 @@ struct context { struct type_store store; + const struct type *current_fntype; struct scope *unit; struct scope *scope; }; @@ -77,14 +78,16 @@ check_expr_assign(struct context *ctx, "Cannot dereference non-pointer type for assignment"); expect(!(object->result->pointer.flags & PTR_NULLABLE), "Cannot dereference nullable pointer type"); - // TODO: Test assignability rules - assert(object->result->pointer.referent->storage == value->result->storage); + expect(type_is_assignable(&ctx->store, + object->result->pointer.referent, + value->result), + "Value type is not assignable to pointer type"); } else { assert(object->type == EXPR_ACCESS); // Invariant const struct scope_object *obj = object->access.object; expect(!(obj->type->flags & TYPE_CONST), "Cannot assign to const object"); - // TODO: Test assignability rules: - assert(obj->type->storage == value->result->storage); + expect(type_is_assignable(&ctx->store, obj->type, value->result), + "rvalue type is not assignable to lvalue"); } } @@ -170,8 +173,8 @@ check_expr_binding(struct context *ctx, } expect(type->size != 0 && type->size != SIZE_UNDEFINED, "Cannot create binding for type of zero or undefined size"); - - // TODO: Check assignability of initializer + expect(type_is_assignable(&ctx->store, type, initializer->result), + "Initializer is not assignable to binding type"); const struct scope_object *obj = scope_insert(ctx->scope, O_BIND, &ident, type); @@ -216,9 +219,9 @@ check_expr_call(struct context *ctx, arg->value = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aarg->value, arg->value); - // TODO: Test for assignability - expect(arg->value->result->storage == param->type->storage, - "Invalid type for parameter"); + expect(type_is_assignable(&ctx->store, + param->type, arg->value->result), + "Argument is not assignable to parameter type"); aarg = aarg->next; param = param->next; @@ -264,11 +267,12 @@ check_expr_constant(struct context *ctx, case TYPE_STORAGE_F32: case TYPE_STORAGE_F64: case TYPE_STORAGE_STRING: + case TYPE_STORAGE_NULL: + case TYPE_STORAGE_VOID: assert(0); // TODO case TYPE_STORAGE_CHAR: case TYPE_STORAGE_ENUM: case TYPE_STORAGE_UINTPTR: - case TYPE_STORAGE_VOID: case TYPE_STORAGE_ALIAS: case TYPE_STORAGE_ARRAY: case TYPE_STORAGE_FUNCTION: @@ -308,6 +312,7 @@ check_expr_list(struct context *ctx, next = &list->next; } else { expr->result = lexpr->result; + expr->terminates = lexpr->terminates; } } @@ -357,7 +362,9 @@ check_expr_return(struct context *ctx, struct expression *rval = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->_return.value, rval); expr->_return.value = rval; - // TODO: Test assignability with function's return type + expect(type_is_assignable(&ctx->store, + ctx->current_fntype->func.result, rval->result), + "Return value is not assignable to function result type"); } trleave(TR_CHECK, NULL); @@ -495,6 +502,7 @@ check_function(struct context *ctx, const struct type *fntype = type_store_lookup_atype( &ctx->store, &fn_atype); assert(fntype); // Invariant + ctx->current_fntype = fntype; struct declaration *decl = xcalloc(1, sizeof(struct declaration)); decl->type = DECL_FUNC; @@ -519,7 +527,8 @@ check_function(struct context *ctx, check_expression(ctx, afndecl->body, body); decl->func.body = body; - // TODO: Check assignability of expression result to function type + expect(body->terminates || type_is_assignable(&ctx->store, fntype->func.result, body->result), + "Result value is not assignable to function result type"); // TODO: Add function name to errors if ((decl->func.flags & FN_INIT) @@ -534,6 +543,7 @@ check_function(struct context *ctx, scope_insert(ctx->unit, O_DECL, &decl->ident, decl->func.type); scope_pop(&ctx->scope, TR_CHECK); + ctx->current_fntype = NULL; trleave(TR_CHECK, NULL); return decl; } diff --git a/src/lex.c b/src/lex.c @@ -1025,6 +1025,7 @@ token_str(const struct token *tok) case TYPE_STORAGE_ENUM: case TYPE_STORAGE_FUNCTION: case TYPE_STORAGE_POINTER: + case TYPE_STORAGE_NULL: case TYPE_STORAGE_SLICE: case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_TAGGED_UNION: diff --git a/src/qtype.c b/src/qtype.c @@ -27,6 +27,7 @@ qstype_for_type(const struct type *type) case TYPE_STORAGE_SIZE: case TYPE_STORAGE_UINTPTR: // XXX: Architecture dependent case TYPE_STORAGE_POINTER: // XXX: Architecture dependent + case TYPE_STORAGE_NULL: // XXX: Architecture dependent return Q_LONG; case TYPE_STORAGE_F32: return Q_SINGLE; @@ -71,6 +72,7 @@ qxtype_for_type(const struct type *type) case TYPE_STORAGE_SIZE: case TYPE_STORAGE_UINTPTR: // XXX: Architecture dependent case TYPE_STORAGE_POINTER: // XXX: Architecture dependent + case TYPE_STORAGE_NULL: // XXX: Architecture dependent case TYPE_STORAGE_F32: case TYPE_STORAGE_F64: case TYPE_STORAGE_VOID: @@ -113,6 +115,7 @@ qtype_for_type(struct gen_context *ctx, const struct type *type, bool extended) case TYPE_STORAGE_SIZE: case TYPE_STORAGE_UINTPTR: case TYPE_STORAGE_POINTER: + case TYPE_STORAGE_NULL: case TYPE_STORAGE_F32: case TYPE_STORAGE_F64: case TYPE_STORAGE_VOID: @@ -147,6 +150,7 @@ type_is_aggregate(const struct type *type) case TYPE_STORAGE_I8: case TYPE_STORAGE_INT: case TYPE_STORAGE_POINTER: + case TYPE_STORAGE_NULL: case TYPE_STORAGE_RUNE: case TYPE_STORAGE_SIZE: case TYPE_STORAGE_U16: diff --git a/src/type_store.c b/src/type_store.c @@ -2,6 +2,93 @@ #include <stdlib.h> #include "type_store.h" #include "util.h" +#include <stdio.h> + +bool +type_is_assignable(struct type_store *store, + const struct type *to, + const struct type *from) +{ + // const and non-const types are mutually assignable + if (to->flags & TYPE_CONST) { + to = type_store_lookup_with_flags(store, + to, to->flags & ~TYPE_CONST); + } + if (from->flags & TYPE_CONST) { + from = type_store_lookup_with_flags(store, + from, from->flags & ~TYPE_CONST); + } + + if (to == from) { + return true; + } + + switch (to->storage) { + case TYPE_STORAGE_I8: + case TYPE_STORAGE_I16: + case TYPE_STORAGE_I32: + case TYPE_STORAGE_I64: + case TYPE_STORAGE_INT: + return type_is_integer(from) + && type_is_signed(from) + && to->size >= from->size; + case TYPE_STORAGE_SIZE: + case TYPE_STORAGE_U8: + case TYPE_STORAGE_U16: + case TYPE_STORAGE_U32: + case TYPE_STORAGE_U64: + case TYPE_STORAGE_UINT: + return type_is_integer(from) + && !type_is_signed(from) + && to->size >= from->size; + case TYPE_STORAGE_UINTPTR: + return (type_is_integer(from) + && !type_is_signed(from) + && to->size >= from->size) + || from->storage == TYPE_STORAGE_POINTER; + case TYPE_STORAGE_F32: + case TYPE_STORAGE_F64: + return type_is_float(from); + case TYPE_STORAGE_POINTER: + switch (from->storage) { + case TYPE_STORAGE_UINTPTR: + return true; + case TYPE_STORAGE_NULL: + return to->pointer.flags & PTR_NULLABLE; + case TYPE_STORAGE_POINTER: + if (to->pointer.referent != from->pointer.referent) { + return false; + } + if (to->pointer.flags & PTR_NULLABLE) { + return from->pointer.flags & PTR_NULLABLE; + } + return true; + default: + return false; + } + assert(0); // Unreachable + case TYPE_STORAGE_ALIAS: + case TYPE_STORAGE_ENUM: + case TYPE_STORAGE_TAGGED_UNION: + assert(0); // TODO + // The following types are only assignable from themselves, and are + // handled above: + case TYPE_STORAGE_ARRAY: + case TYPE_STORAGE_BOOL: + case TYPE_STORAGE_CHAR: + case TYPE_STORAGE_FUNCTION: + case TYPE_STORAGE_NULL: + case TYPE_STORAGE_RUNE: + case TYPE_STORAGE_SLICE: + case TYPE_STORAGE_STRING: + case TYPE_STORAGE_STRUCT: + case TYPE_STORAGE_UNION: + case TYPE_STORAGE_VOID: + return false; + } + + assert(0); // Unreachable +} const struct type * builtin_type_for_storage(enum type_storage storage, bool is_const) @@ -42,8 +129,9 @@ builtin_type_for_storage(enum type_storage storage, bool is_const) case TYPE_STORAGE_UINTPTR: return is_const ? &builtin_type_const_uintptr : &builtin_type_uintptr; case TYPE_STORAGE_VOID: - // const void and void are the same type - return is_const ? &builtin_type_void : &builtin_type_void; + return &builtin_type_void; // const void and void are the same type + case TYPE_STORAGE_NULL: + return &builtin_type_null; // const null and null are the same type case TYPE_STORAGE_ALIAS: case TYPE_STORAGE_ARRAY: case TYPE_STORAGE_FUNCTION: @@ -76,6 +164,7 @@ atype_hash(struct type_store *store, const struct ast_type *type) case TYPE_STORAGE_I32: case TYPE_STORAGE_I64: case TYPE_STORAGE_INT: + case TYPE_STORAGE_NULL: case TYPE_STORAGE_RUNE: case TYPE_STORAGE_SIZE: case TYPE_STORAGE_U8: @@ -129,6 +218,7 @@ type_hash(struct type_store *store, const struct type *type) case TYPE_STORAGE_I32: case TYPE_STORAGE_I64: case TYPE_STORAGE_INT: + case TYPE_STORAGE_NULL: case TYPE_STORAGE_RUNE: case TYPE_STORAGE_SIZE: case TYPE_STORAGE_U8: @@ -199,6 +289,7 @@ type_eq_atype(struct type_store *store, case TYPE_STORAGE_I32: case TYPE_STORAGE_I64: case TYPE_STORAGE_INT: + case TYPE_STORAGE_NULL: case TYPE_STORAGE_RUNE: case TYPE_STORAGE_SIZE: case TYPE_STORAGE_U8: @@ -264,6 +355,7 @@ type_eq_type(struct type_store *store, case TYPE_STORAGE_I32: case TYPE_STORAGE_I64: case TYPE_STORAGE_INT: + case TYPE_STORAGE_NULL: case TYPE_STORAGE_RUNE: case TYPE_STORAGE_SIZE: case TYPE_STORAGE_U8: @@ -327,6 +419,7 @@ type_init_from_atype(struct type_store *store, case TYPE_STORAGE_I32: case TYPE_STORAGE_I64: case TYPE_STORAGE_INT: + case TYPE_STORAGE_NULL: case TYPE_STORAGE_RUNE: case TYPE_STORAGE_SIZE: case TYPE_STORAGE_U8: @@ -387,17 +480,18 @@ type_init_from_type(struct type_store *store, case TYPE_STORAGE_CHAR: case TYPE_STORAGE_F32: case TYPE_STORAGE_F64: - case TYPE_STORAGE_I8: case TYPE_STORAGE_I16: case TYPE_STORAGE_I32: case TYPE_STORAGE_I64: + case TYPE_STORAGE_I8: case TYPE_STORAGE_INT: + case TYPE_STORAGE_NULL: case TYPE_STORAGE_RUNE: case TYPE_STORAGE_SIZE: - case TYPE_STORAGE_U8: case TYPE_STORAGE_U16: case TYPE_STORAGE_U32: case TYPE_STORAGE_U64: + case TYPE_STORAGE_U8: case TYPE_STORAGE_UINT: case TYPE_STORAGE_UINTPTR: case TYPE_STORAGE_VOID: diff --git a/src/types.c b/src/types.c @@ -2,6 +2,15 @@ #include <stdbool.h> #include "types.h" +const struct type * +type_dereference(const struct type *type) +{ + if (type->storage != TYPE_STORAGE_POINTER) { + return type; + } + return type_dereference(type->pointer.referent); +} + const char * type_storage_unparse(enum type_storage storage) { @@ -34,6 +43,8 @@ type_storage_unparse(enum type_storage storage) return "int"; case TYPE_STORAGE_POINTER: return "pointer"; + case TYPE_STORAGE_NULL: + return "rune"; case TYPE_STORAGE_RUNE: return "rune"; case TYPE_STORAGE_SIZE: @@ -82,6 +93,7 @@ type_is_integer(const struct type *type) case TYPE_STORAGE_UNION: case TYPE_STORAGE_BOOL: case TYPE_STORAGE_CHAR: + case TYPE_STORAGE_NULL: case TYPE_STORAGE_RUNE: case TYPE_STORAGE_ENUM: case TYPE_STORAGE_F32: @@ -121,6 +133,7 @@ type_is_numeric(const struct type *type) case TYPE_STORAGE_BOOL: case TYPE_STORAGE_CHAR: case TYPE_STORAGE_RUNE: + case TYPE_STORAGE_NULL: case TYPE_STORAGE_ENUM: return false; case TYPE_STORAGE_I8: @@ -143,6 +156,12 @@ type_is_numeric(const struct type *type) } bool +type_is_float(const struct type *type) +{ + return type->storage == TYPE_STORAGE_F32 || type->storage == TYPE_STORAGE_F64; +} + +bool type_is_signed(const struct type *type) { switch (type->storage) { @@ -159,6 +178,7 @@ type_is_signed(const struct type *type) case TYPE_STORAGE_BOOL: case TYPE_STORAGE_CHAR: case TYPE_STORAGE_RUNE: + case TYPE_STORAGE_NULL: case TYPE_STORAGE_SIZE: case TYPE_STORAGE_U8: case TYPE_STORAGE_U16: @@ -181,15 +201,6 @@ type_is_signed(const struct type *type) assert(0); // Unreachable } -const struct type * -type_dereference(const struct type *type) -{ - if (type->storage != TYPE_STORAGE_POINTER) { - return type; - } - return type_dereference(type->pointer.referent); -} - // Built-in type singletons const struct type builtin_type_bool = { .storage = TYPE_STORAGE_BOOL, @@ -266,6 +277,11 @@ builtin_type_uintptr = { .size = 8, // XXX: ARCH .align = 8, }, +builtin_type_null = { + .storage = TYPE_STORAGE_NULL, + .size = 8, // XXX: ARCH + .align = 8, +}, builtin_type_rune = { .storage = TYPE_STORAGE_RUNE, .size = 4, diff --git a/todo.txt b/todo.txt @@ -1,5 +1,7 @@ highest priorities, unordered except where there are obvious dependencies: +- double check integers <32 bits in gen +- named constants (true, false, null, void) - aggregate types - in parameters - in variables