harec

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

commit c8555dd82d94cef6cb3ee87210a3e5bc8593d1be
parent 9db7edc36603cc8088e279514d07b5299f6ddd35
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sat, 19 Dec 2020 10:54:30 -0500

Type check constant expressions

Diffstat:
Minclude/ast.h | 4++--
Minclude/lex.h | 6+++---
Minclude/type_store.h | 3+++
Msrc/check.c | 84++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/lex.c | 16++++++++--------
Msrc/parse.c | 4++--
Msrc/type_store.c | 108++++++++++++++++++++++++++++++++++++++++----------------------------------------
7 files changed, 153 insertions(+), 72 deletions(-)

diff --git a/include/ast.h b/include/ast.h @@ -87,8 +87,8 @@ struct ast_type { struct ast_constant_expression { enum type_storage storage; union { - intmax_t _signed; - uintmax_t _unsigned; + intmax_t ival; + uintmax_t uval; struct { size_t len; char *value; diff --git a/include/lex.h b/include/lex.h @@ -135,15 +135,15 @@ struct token { enum lexical_token token; enum type_storage storage; union { - double _float; char *name; uint32_t rune; - intmax_t _signed; + intmax_t ival; + uintmax_t uval; + double fval; struct { size_t len; char *value; } string; - uintmax_t _unsigned; }; }; diff --git a/include/type_store.h b/include/type_store.h @@ -20,4 +20,7 @@ unsigned long type_hash(struct type_store *store, const struct type *type); const struct type *type_store_lookup_atype( struct type_store *store, const struct ast_type *atype); +const struct type *builtin_type_for_storage( + enum type_storage storage, bool is_const); + #endif diff --git a/src/check.c b/src/check.c @@ -28,12 +28,88 @@ expect(bool constraint, char *fmt, ...) } static void +check_expr_constant(struct context *ctx, + const struct ast_expression *aexpr, + struct expression *expr) +{ + trace(TR_CHECK, "constant"); + expr->type = EXPR_CONSTANT; + expr->result = builtin_type_for_storage(aexpr->constant.storage, false); + + switch (aexpr->constant.storage) { + case TYPE_STORAGE_I8: + case TYPE_STORAGE_I16: + case TYPE_STORAGE_I32: + case TYPE_STORAGE_I64: + case TYPE_STORAGE_INT: + expr->constant.ival = aexpr->constant.ival; + break; + case TYPE_STORAGE_U8: + case TYPE_STORAGE_U16: + case TYPE_STORAGE_U32: + case TYPE_STORAGE_U64: + case TYPE_STORAGE_UINT: + case TYPE_STORAGE_SIZE: + expr->constant.uval = aexpr->constant.uval; + break; + case TYPE_STORAGE_RUNE: + assert(0); // TODO + case TYPE_STORAGE_BOOL: + case TYPE_STORAGE_F32: + case TYPE_STORAGE_F64: + assert(0); // TODO + case TYPE_STORAGE_CHAR: + case TYPE_STORAGE_UINTPTR: + case TYPE_STORAGE_VOID: + case TYPE_STORAGE_ALIAS: + case TYPE_STORAGE_ARRAY: + case TYPE_STORAGE_FUNCTION: + case TYPE_STORAGE_POINTER: + case TYPE_STORAGE_SLICE: + case TYPE_STORAGE_STRING: + case TYPE_STORAGE_STRUCT: + case TYPE_STORAGE_TAGGED_UNION: + case TYPE_STORAGE_UNION: + assert(0); // Invariant + } +} + +static void check_expression(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr) { trenter(TR_CHECK, "expression"); - assert(0); // TODO + + switch (aexpr->type) { + case EXPR_ACCESS: + case EXPR_ASSERT: + case EXPR_ASSIGN: + case EXPR_BINARITHM: + case EXPR_BINDING_LIST: + case EXPR_CALL: + case EXPR_CAST: + assert(0); // TODO + case EXPR_CONSTANT: + check_expr_constant(ctx, aexpr, expr); + break; + case EXPR_CONTROL: + case EXPR_FOR: + case EXPR_FREE: + case EXPR_FUNC: + case EXPR_IF: + case EXPR_INDEX: + case EXPR_LIST: + case EXPR_MATCH: + case EXPR_MEASURE: + case EXPR_SLICE: + case EXPR_STRUCT: + case EXPR_SWITCH: + case EXPR_UNARITHM: + case EXPR_WHILE: + assert(0); // TODO + } + trleave(TR_CHECK, NULL); } @@ -43,9 +119,9 @@ check_function(struct context *ctx, struct declaration *decl) { const struct ast_function_decl *afndecl = &adecl->function; + trenter(TR_CHECK, "function"); assert(!afndecl->prototype.params); // TODO assert(!afndecl->symbol); // TODO - trenter(TR_CHECK, "function"); const struct ast_type fn_atype = { .storage = TYPE_STORAGE_FUNCTION, @@ -61,6 +137,7 @@ check_function(struct context *ctx, struct expression *body = calloc(1, sizeof(struct expression)); check_expression(ctx, &afndecl->body, body); + // TODO: Check assignability of expression result to function type // TODO: Add function name to errors @@ -76,7 +153,8 @@ check_function(struct context *ctx, if ((fntype->func.flags & FN_NORETURN)) { expect(!body->terminates, "@noreturn function must not terminate."); } else { - expect(body->terminates, "This function never terminates. Add a return statement or @noreturn."); + expect(body->type != EXPR_LIST || body->terminates, + "This function never terminates. Add a return statement or @noreturn."); } trleave(TR_CHECK, NULL); } diff --git a/src/lex.c b/src/lex.c @@ -383,9 +383,9 @@ finalize: case TYPE_STORAGE_UINT: case TYPE_STORAGE_U64: case TYPE_STORAGE_SIZE: - out->_unsigned = strtoumax(lexer->buf, NULL, strlen(base)); + out->uval = strtoumax(lexer->buf, NULL, strlen(base)); for (uintmax_t i = 0; i < exponent; i++) { - out->_unsigned *= 10; + out->uval *= 10; } break; case TYPE_STORAGE_I8: @@ -393,14 +393,14 @@ finalize: case TYPE_STORAGE_I32: case TYPE_STORAGE_INT: case TYPE_STORAGE_I64: - out->_signed = strtoimax(lexer->buf, NULL, strlen(base)); + out->ival = strtoimax(lexer->buf, NULL, strlen(base)); for (uintmax_t i = 0; i < exponent; i++) { - out->_signed *= 10; + out->ival *= 10; } break; case TYPE_STORAGE_F32: case TYPE_STORAGE_F64: - out->_float = strtod(lexer->buf, NULL); + out->fval = strtod(lexer->buf, NULL); break; default: assert(0); @@ -996,18 +996,18 @@ token_str(const struct token *tok) case TYPE_STORAGE_UINT: case TYPE_STORAGE_UINTPTR: case TYPE_STORAGE_SIZE: - snprintf(buf, sizeof(buf), "%ju", tok->_unsigned); + snprintf(buf, sizeof(buf), "%ju", tok->uval); break; case TYPE_STORAGE_I8: case TYPE_STORAGE_I16: case TYPE_STORAGE_I32: case TYPE_STORAGE_I64: case TYPE_STORAGE_INT: - snprintf(buf, sizeof(buf), "%jd", tok->_signed); + snprintf(buf, sizeof(buf), "%jd", tok->ival); break; case TYPE_STORAGE_F32: case TYPE_STORAGE_F64: - snprintf(buf, sizeof(buf), "%lf", tok->_float); + snprintf(buf, sizeof(buf), "%lf", tok->fval); break; case TYPE_STORAGE_RUNE: bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "'"); diff --git a/src/parse.c b/src/parse.c @@ -353,14 +353,14 @@ parse_simple_expression(struct parser *par, struct ast_expression *exp) case TYPE_STORAGE_UINT: case TYPE_STORAGE_UINTPTR: case TYPE_STORAGE_SIZE: - exp->constant._unsigned = (uintmax_t)tok._unsigned; + exp->constant.uval = (uintmax_t)tok.uval; break; case TYPE_STORAGE_I8: case TYPE_STORAGE_I16: case TYPE_STORAGE_I32: case TYPE_STORAGE_I64: case TYPE_STORAGE_INT: - exp->constant._signed = (intmax_t)tok._signed; + exp->constant.ival = (intmax_t)tok.ival; break; case TYPE_STORAGE_STRING: exp->constant.string.len = tok.string.len; diff --git a/src/type_store.c b/src/type_store.c @@ -3,56 +3,63 @@ #include "type_store.h" #include "util.h" -unsigned long -atype_hash(struct type_store *store, const struct ast_type *type) +const struct type * +builtin_type_for_storage(enum type_storage storage, bool is_const) { - unsigned long hash = DJB2_INIT; - hash = djb2(hash, type->storage); - hash = djb2(hash, type->flags); - switch (type->storage) { + switch (storage) { case TYPE_STORAGE_BOOL: + return is_const ? &builtin_type_bool : &builtin_type_const_bool; case TYPE_STORAGE_CHAR: + return is_const ? &builtin_type_char : &builtin_type_const_char; case TYPE_STORAGE_F32: + return is_const ? &builtin_type_f32 : &builtin_type_const_f32; case TYPE_STORAGE_F64: + return is_const ? &builtin_type_f64 : &builtin_type_const_f64; case TYPE_STORAGE_I8: + return is_const ? &builtin_type_i8 : &builtin_type_const_i8; case TYPE_STORAGE_I16: + return is_const ? &builtin_type_i16 : &builtin_type_const_i16; case TYPE_STORAGE_I32: + return is_const ? &builtin_type_i32 : &builtin_type_const_i32; case TYPE_STORAGE_I64: + return is_const ? &builtin_type_i64 : &builtin_type_const_i64; case TYPE_STORAGE_INT: + return is_const ? &builtin_type_int : &builtin_type_const_int; case TYPE_STORAGE_RUNE: + return is_const ? &builtin_type_rune : &builtin_type_const_rune; case TYPE_STORAGE_SIZE: + return is_const ? &builtin_type_size : &builtin_type_const_size; case TYPE_STORAGE_U8: + return is_const ? &builtin_type_u8 : &builtin_type_const_u8; case TYPE_STORAGE_U16: + return is_const ? &builtin_type_u16 : &builtin_type_const_u16; case TYPE_STORAGE_U32: + return is_const ? &builtin_type_u32 : &builtin_type_const_u32; case TYPE_STORAGE_U64: + return is_const ? &builtin_type_u64 : &builtin_type_const_u64; case TYPE_STORAGE_UINT: + return is_const ? &builtin_type_uint : &builtin_type_const_uint; case TYPE_STORAGE_UINTPTR: + return is_const ? &builtin_type_uintptr : &builtin_type_const_uintptr; case TYPE_STORAGE_VOID: - break; // built-ins + // const void and void are the same type + return is_const ? &builtin_type_void : &builtin_type_void; + case TYPE_STORAGE_POINTER: case TYPE_STORAGE_ALIAS: case TYPE_STORAGE_ARRAY: case TYPE_STORAGE_FUNCTION: - hash = djb2(hash, atype_hash(store, type->func.result)); - hash = djb2(hash, type->func.variadism); - hash = djb2(hash, type->func.flags); - for (struct ast_function_parameters *param = type->func.params; - param; param = param->next) { - hash = djb2(hash, atype_hash(store, param->type)); - } - break; - case TYPE_STORAGE_POINTER: case TYPE_STORAGE_SLICE: case TYPE_STORAGE_STRING: case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_TAGGED_UNION: case TYPE_STORAGE_UNION: - assert(0); // TODO + return NULL; } - return hash; + assert(0); // Unreachable } unsigned long -type_hash(struct type_store *store, const struct type *type) +atype_hash(struct type_store *store, const struct ast_type *type) { unsigned long hash = DJB2_INIT; hash = djb2(hash, type->storage); @@ -80,12 +87,12 @@ type_hash(struct type_store *store, const struct type *type) case TYPE_STORAGE_ALIAS: case TYPE_STORAGE_ARRAY: case TYPE_STORAGE_FUNCTION: - hash = djb2(hash, type_hash(store, type->func.result)); + hash = djb2(hash, atype_hash(store, type->func.result)); hash = djb2(hash, type->func.variadism); hash = djb2(hash, type->func.flags); - for (struct type_func_param *param = type->func.params; + for (struct ast_function_parameters *param = type->func.params; param; param = param->next) { - hash = djb2(hash, type_hash(store, param->type)); + hash = djb2(hash, atype_hash(store, param->type)); } break; case TYPE_STORAGE_POINTER: @@ -99,66 +106,59 @@ type_hash(struct type_store *store, const struct type *type) return hash; } -static const struct type * -builtin_for_atype(const struct ast_type *atype) +unsigned long +type_hash(struct type_store *store, const struct type *type) { - bool is_const = (atype->flags & TYPE_CONST) != 0; - switch (atype->storage) { + unsigned long hash = DJB2_INIT; + hash = djb2(hash, type->storage); + hash = djb2(hash, type->flags); + switch (type->storage) { case TYPE_STORAGE_BOOL: - return is_const ? &builtin_type_bool : &builtin_type_const_bool; case TYPE_STORAGE_CHAR: - return is_const ? &builtin_type_char : &builtin_type_const_char; case TYPE_STORAGE_F32: - return is_const ? &builtin_type_f32 : &builtin_type_const_f32; case TYPE_STORAGE_F64: - return is_const ? &builtin_type_f64 : &builtin_type_const_f64; case TYPE_STORAGE_I8: - return is_const ? &builtin_type_i8 : &builtin_type_const_i8; case TYPE_STORAGE_I16: - return is_const ? &builtin_type_i16 : &builtin_type_const_i16; case TYPE_STORAGE_I32: - return is_const ? &builtin_type_i32 : &builtin_type_const_i32; case TYPE_STORAGE_I64: - return is_const ? &builtin_type_i64 : &builtin_type_const_i64; case TYPE_STORAGE_INT: - return is_const ? &builtin_type_int : &builtin_type_const_int; case TYPE_STORAGE_RUNE: - return is_const ? &builtin_type_rune : &builtin_type_const_rune; case TYPE_STORAGE_SIZE: - return is_const ? &builtin_type_size : &builtin_type_const_size; case TYPE_STORAGE_U8: - return is_const ? &builtin_type_u8 : &builtin_type_const_u8; case TYPE_STORAGE_U16: - return is_const ? &builtin_type_u16 : &builtin_type_const_u16; case TYPE_STORAGE_U32: - return is_const ? &builtin_type_u32 : &builtin_type_const_u32; case TYPE_STORAGE_U64: - return is_const ? &builtin_type_u64 : &builtin_type_const_u64; case TYPE_STORAGE_UINT: - return is_const ? &builtin_type_uint : &builtin_type_const_uint; case TYPE_STORAGE_UINTPTR: - return is_const ? &builtin_type_uintptr : &builtin_type_const_uintptr; case TYPE_STORAGE_VOID: - // const void and void are the same type - return is_const ? &builtin_type_void : &builtin_type_void; - case TYPE_STORAGE_POINTER: - if (atype->pointer.referent->storage == TYPE_STORAGE_CHAR - && atype->pointer.referent->flags == TYPE_CONST) { - return &builtin_type_const_ptr_char; - } - return NULL; + break; // built-ins case TYPE_STORAGE_ALIAS: case TYPE_STORAGE_ARRAY: case TYPE_STORAGE_FUNCTION: + hash = djb2(hash, type_hash(store, type->func.result)); + hash = djb2(hash, type->func.variadism); + hash = djb2(hash, type->func.flags); + for (struct type_func_param *param = type->func.params; + param; param = param->next) { + hash = djb2(hash, type_hash(store, param->type)); + } + break; + case TYPE_STORAGE_POINTER: case TYPE_STORAGE_SLICE: case TYPE_STORAGE_STRING: case TYPE_STORAGE_STRUCT: case TYPE_STORAGE_TAGGED_UNION: case TYPE_STORAGE_UNION: - return NULL; - + assert(0); // TODO } - assert(0); // Unreachable + return hash; +} + +static const struct type * +builtin_for_atype(const struct ast_type *atype) +{ + bool is_const = (atype->flags & TYPE_CONST) != 0; + return builtin_type_for_storage(atype->storage, is_const); } static bool